From: John Hodge Date: Mon, 21 Sep 2009 08:23:50 +0000 (+0800) Subject: Initial commit of kernel only X-Git-Tag: rel0.06~561 X-Git-Url: https://git.ucc.asn.au/?a=commitdiff_plain;h=8bc40333b1401d7616b225945fee53d972c2f418;p=tpg%2Facess2.git Initial commit of kernel only Known Bugs: - Copy-on-write causes errors - Other MM_Clone/MM_Clear bugs --- 8bc40333b1401d7616b225945fee53d972c2f418 diff --git a/Kernel/GenSyscalls.php b/Kernel/GenSyscalls.php new file mode 100644 index 00000000..7397f9de --- /dev/null +++ b/Kernel/GenSyscalls.php @@ -0,0 +1,77 @@ +$call) +{ + if($i != $num) { + $lHeader .= "\n"; + $lAsmInc .= "\n"; + } + + $lHeader .= "\t{$call[0]}"; + if($i != $num) $lHeader .= " = {$num}"; + $lHeader .= ",\t// {$num} - {$call[1]}\n"; + + $lAsmInc .= "%define {$call[0]}\t{$num}\t; {$call[1]}\n"; + + + if($i != $num) + $i = $num+1; + else + $i ++; +} +$lHeader .= "\tNUM_SYSCALLS,\n"; +$lHeader .= "\tSYS_DEBUG = 0x100 // 0x100 - Print a debug string\n"; +$lHeader .= "};\n\n"; +$lHeader .= "static const char *cSYSCALL_NAMES[] = {\n\t"; + +$j = 0; +for($i=0;$i<$lMax;$i++) +{ + $lHeader .= "\"".$lSyscalls[$i][0]."\","; + $j ++; + if($j == 6) { + $lHeader .= "\n\t"; + $j = 0; + } +} +$lHeader .= "\"\"\n};\n#endif\n"; + +//echo $lHeader; + +$fp = fopen("include/syscalls.h", "w"); fwrite($fp, $lHeader); fclose($fp); +$fp = fopen("include/syscalls.inc.asm", "w"); fwrite($fp, $lAsmInc); fclose($fp); + +?> diff --git a/Kernel/Makefile b/Kernel/Makefile new file mode 100644 index 00000000..0c900229 --- /dev/null +++ b/Kernel/Makefile @@ -0,0 +1,63 @@ +# Acess2 Kernel +# Root Makefile +# NOTE: Does some voodoo to allow differing architecture builds to co-exist +# - The built objects and dependency files are suffixed with the arch name +# - The final binary is Acess2..bin + +-include Makefile.cfg + +-include arch/$(ARCHDIR)/Makefile + +RM = rm -f + +MAKEDEP = $(CC) -M + +CPPFLAGS += -I./include -I./arch/$(ARCHDIR)/include -DARCH=$(ARCH) +CFLAGS += -Wall -Werror -O3 -fno-stack-protector -fno-builtin -fleading-underscore +ASFLAGS += -D ARCH=\"$(ARCH)\" +LDFLAGS += -T arch/$(ARCHDIR)/link.ld + +OBJ = $(addprefix arch/$(ARCHDIR)/,$(A_OBJ)) +OBJ += heap.o messages.o debug.o modules.o lib.o syscalls.o system.o +OBJ += binary.o bin/elf.o +OBJ += vfs/main.o vfs/open.o vfs/acls.o vfs/dir.o vfs/io.o vfs/mount.o vfs/memfile.o vfs/nodecache.o +OBJ += vfs/fs/root.o vfs/fs/devfs.o vfs/fs/fat.o +OBJ += drv/pci.o drv/ata_x86.o drv/vterm.o drv/vga.o drv/kb.o +OBJ := $(addsuffix .$(ARCH), $(OBJ)) +BIN = ../Acess2.$(ARCH).bin + +DEPFILES = $(filter %.o.$(ARCH),$(OBJ)) +DEPFILES := $(DEPFILES:%.o.$(ARCH)=%.d.$(ARCH)) + +SRCFILES = $(OBJ:%.o.$(ARCH)=%.c) +SRCFILES := $(SRCFILES:%.ao.$(ARCH)=%.asm) + +.PHONY: all clean + +all: $(BIN) + +clean: + @$(RM) $(BIN) $(OBJ) $(DEPFILES) + +$(BIN): $(OBJ) arch/$(ARCHDIR)/link.ld Makefile + @echo --- LD -o $(BIN) + @$(LD) $(LDFLAGS) -o $(BIN) $(OBJ) -Map ../Map.$(ARCH).txt + @objdump $(BIN) -D > $(BIN).dsm + @cp $(BIN) /mnt/AcessFDD/ + @wc -l $(SRCFILES) > LineCounts.$(ARCH).txt + @git commit + +%.ao.$(ARCH): %.asm Makefile + @echo --- NASM -o $@ + @$(AS) $(ASFLAGS) $< -o $@ + +%.o.$(ARCH): %.c Makefile + @echo --- GCC -o $@ + @$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< + @$(MAKEDEP) $(CPPFLAGS) -MT $@ -o $*.d.$(ARCH) $< + +include/syscalls.h: syscalls.lst Makefile + php GenSyscalls.php + +# Dependency Files +-include $(DEPFILES) diff --git a/Kernel/Makefile.cfg b/Kernel/Makefile.cfg new file mode 100644 index 00000000..8e186b82 --- /dev/null +++ b/Kernel/Makefile.cfg @@ -0,0 +1,6 @@ +# +# Acess2 Makefile Config +# Makefile.cfg + +ARCH = i386 +ARCHDIR = x86 diff --git a/Kernel/arch/x86/Makefile b/Kernel/arch/x86/Makefile new file mode 100644 index 00000000..335d2195 --- /dev/null +++ b/Kernel/arch/x86/Makefile @@ -0,0 +1,18 @@ +# +# Acess2 Kernel +# i386 Architecture Makefile +# arch/i386/Makefile + +# Assuming build machine is 32-bit ELF +CC = gcc +AS = nasm +LD = ld +OBJDUMP = objdump + +CPPFLAGS = +CFLAGS = +ASFLAGS = -f elf + +A_OBJ = start.ao main.o lib.o desctab.ao errors.o irq.o +A_OBJ += mm_phys.o mm_virt.o +A_OBJ += proc.o time.o diff --git a/Kernel/arch/x86/desctab.asm b/Kernel/arch/x86/desctab.asm new file mode 100644 index 00000000..a66bfa3d --- /dev/null +++ b/Kernel/arch/x86/desctab.asm @@ -0,0 +1,296 @@ +; AcessOS Microkernel Version +; +; desctab.asm +[BITS 32] + +%if ARCH == "i386" +MAX_CPUS equ 1 +%else +MAX_CPUS equ 8 +%endif +GDT_SIZE equ (1+2*2+MAX_CPUS)*8 ; 4 Permission levels + +[section .data] +; GDT +[global _gGDT] +_gGDT: + ; PL0 - Kernel + ; PL3 - User + dd 0x00000000, 0x00000000 ; 00 NULL Entry + dd 0x0000FFFF, 0x00CF9A00 ; 08 PL0 Code + dd 0x0000FFFF, 0x00CF9200 ; 10 PL0 Data + dd 0x0000FFFF, 0x00CFFA00 ; 18 PL3 Code + dd 0x0000FFFF, 0x00CFF200 ; 20 PL3 Data + times MAX_CPUS dd 0, 0 +_gGDTptr: + dw GDT_SIZE-1 + dd _gGDT +; IDT +ALIGN 8 +_gIDT: + times 256 dd 0x00080000,0x00000F00 +_gIDTPtr: + dw 256 * 16 - 1 ; Limit + dd _gIDT ; Base + +[section .text] +[global _Desctab_Install] +_Desctab_Install: + ; Set GDT + lgdt [_gGDTptr] + mov ax, 0x10 ; PL0 Data + mov ss, ax + mov ds, ax + mov es, ax + mov gs, ax + mov fs, ax + jmp 0x08:.pl0code +.pl0code: + + ; Set IDT +%macro SETISR 1 + mov eax, _Isr%1 + mov WORD [_gIDT + %1*8], ax + shr eax, 16 + mov WORD [_gIDT + %1*8+6], ax + mov ax, WORD [_gIDT + %1*8 + 4] + or ax, 0x8000 + mov WORD [_gIDT + %1*8 + 4], ax +%endmacro +%macro SETUSER 1 + mov ax, WORD [_gIDT + %1*8 + 4] + or ax, 0x6000 + mov WORD [_gIDT + %1*8 + 4], ax +%endmacro + %assign i 0 + %rep 32 + SETISR i + %assign i i+1 + %endrep + + SETISR 0xAC + SETUSER 0xAC + + %assign i 0xF0 + %rep 16 + SETISR i + %assign i i+1 + %endrep + + ; Load IDT + lidt [_gIDTPtr] + + ; Remap PIC + push edx ; Save EDX + mov dx, 0x20 + mov al, 0x11 + out dx, al ; Init Command + mov dx, 0x21 + mov al, 0xF0 + out dx, al ; Offset (Start of IDT Range) + mov al, 0x04 + out dx, al ; IRQ connected to Slave (00000100b) = IRQ2 + mov al, 0x01 + out dx, al ; Set Mode + mov al, 0x00 + out dx, al ; Set Mode + + mov dx, 0xA0 + mov al, 0x11 + out dx, al ; Init Command + mov dx, 0xA1 + mov al, 0xF8 + out dx, al ; Offset (Start of IDT Range) + mov al, 0x02 + out dx, al ; IRQ Line connected to master + mov al, 0x01 + out dx, al ; Set Mode + mov dl, 0x00 + out dx, al ; Set Mode + pop edx + + ret + + +; =============== +; = Define ISRs = +; =============== +%macro ISR_ERRNO 1 +[global _Isr%1] +_Isr%1: + ;xchg bx, bx + push %1 + jmp ErrorCommon +%endmacro +%macro ISR_NOERR 1 +[global _Isr%1] +_Isr%1: + xchg bx, bx + push 0 + push %1 + jmp ErrorCommon +%endmacro + +%macro DEF_SYSCALL 1 +[global _Isr%1] +_Isr%1: + push 0 + push %1 + jmp SyscallCommon +%endmacro + +%macro DEF_IRQ 1 +[global _Isr%1] +_Isr%1: + push 0 + push %1 + jmp IRQCommon +%endmacro + +ISR_NOERR 0; 0: Divide By Zero Exception +ISR_NOERR 1; 1: Debug Exception +ISR_NOERR 2; 2: Non Maskable Interrupt Exception +ISR_NOERR 3; 3: Int 3 Exception +ISR_NOERR 4; 4: INTO Exception +ISR_NOERR 5; 5: Out of Bounds Exception +ISR_NOERR 6; 6: Invalid Opcode Exception +ISR_NOERR 7; 7: Coprocessor Not Available Exception +ISR_ERRNO 8; 8: Double Fault Exception (With Error Code!) +ISR_NOERR 9; 9: Coprocessor Segment Overrun Exception +ISR_ERRNO 10; 10: Bad TSS Exception (With Error Code!) +ISR_ERRNO 11; 11: Segment Not Present Exception (With Error Code!) +ISR_ERRNO 12; 12: Stack Fault Exception (With Error Code!) +ISR_ERRNO 13; 13: General Protection Fault Exception (With Error Code!) +ISR_ERRNO 14; 14: Page Fault Exception (With Error Code!) +ISR_NOERR 15; 15: Reserved Exception +ISR_NOERR 16; 16: Floating Point Exception +ISR_NOERR 17; 17: Alignment Check Exception +ISR_NOERR 18; 18: Machine Check Exception +ISR_NOERR 19; 19: Reserved +ISR_NOERR 20; 20: Reserved +ISR_NOERR 21; 21: Reserved +ISR_NOERR 22; 22: Reserved +ISR_NOERR 23; 23: Reserved +ISR_NOERR 24; 24: Reserved +ISR_NOERR 25; 25: Reserved +ISR_NOERR 26; 26: Reserved +ISR_NOERR 27; 27: Reserved +ISR_NOERR 28; 28: Reserved +ISR_NOERR 29; 29: Reserved +ISR_NOERR 30; 30: Reserved +ISR_NOERR 31; 31: Reserved + +DEF_SYSCALL 0xAC ; Acess System Call + +; IRQs +; - Timer +[global _Isr240] +_Isr240: + push 0 + jmp SchedulerBase +; - Assignable +%assign i 0xF1 +%rep 16 + DEF_IRQ i +%assign i i+1 +%endrep + +; --------------------- +; Common error handling +; --------------------- +[extern _ErrorHandler] +ErrorCommon: + pusha + push ds + push es + push fs + push gs + + push esp + call _ErrorHandler + add esp, 4 + + pop gs + pop fs + pop es + pop ds + popa + add esp, 8 ; Error Code and ID + iret + +; -------------------------- +; Common System Call Handler +; -------------------------- +[extern _SyscallHandler] +SyscallCommon: + pusha + push ds + push es + push fs + push gs + + push esp + call _SyscallHandler + add esp, 4 + + pop gs + pop fs + pop es + pop ds + popa + add esp, 8 ; Error Code and ID + iret + +; ------------ +; IRQ Handling +; ------------ +[extern _IRQ_Handler] +IRQCommon: + pusha + push ds + push es + push fs + push gs + + push esp + call _IRQ_Handler + add esp, 4 + + pop gs + pop fs + pop es + pop ds + popa + add esp, 8 ; Error Code and ID + iret + +; -------------- +; Task Scheduler +; -------------- +[extern _Proc_Scheduler] +SchedulerBase: + pusha + push ds + push es + push fs + push gs + + mov eax, [esp+12*4] ; CPU Number + push eax ; Pus as argument + + call _Proc_Scheduler + + add esp, 4 ; Remove Argument + + pop gs + pop fs + pop es + pop ds + + mov dx, 0x20 + mov al, 0x20 + out dx, al ; ACK IRQ + popa + add esp, 4 ; CPU ID + ; No Error code / int num + iret diff --git a/Kernel/arch/x86/errors.c b/Kernel/arch/x86/errors.c new file mode 100644 index 00000000..a8d89ac4 --- /dev/null +++ b/Kernel/arch/x86/errors.c @@ -0,0 +1,52 @@ +/* + * Acess2 - x86 Architecture + * arch/x86/errors.c + * - CPU Error Handler + */ +#include + +// === IMPORTS === +extern void MM_PageFault(Uint Addr, Uint ErrorCode, tRegs *Regs); +extern void Proc_DumpThreads(); + +// === CODE === +/** + * \fn void ErrorHandler(tRegs *Regs) + * \brief General Error Handler + */ +void ErrorHandler(tRegs *Regs) +{ + Uint cr; + __asm__ __volatile__ ("cli"); + + if(Regs->int_num == 14) + { + __asm__ __volatile__ ("mov %%cr2, %0":"=r"(cr)); + MM_PageFault( cr, Regs->err_code, Regs ); + return ; + } + + Warning("CPU Error %i, Code: 0x%x", Regs->int_num, Regs->err_code); + Warning(" CS:EIP = 0x%04x:%08x", Regs->cs, Regs->eip); + Warning(" SS:ESP = 0x%04x:%08x", Regs->ss, Regs->esp); + Warning(" EFLAGS = 0x%08x", Regs->eflags); + Warning(" EAX %08x EBX %08x", Regs->eax, Regs->ebx); + Warning(" ECX %08x EDX %08x", Regs->ecx, Regs->edx); + Warning(" ESP %08x EBP %08x", Regs->esp, Regs->ebp); + Warning(" ESI %08x EDI %08x", Regs->esi, Regs->edi); + Warning(" DS %04x ES %04x", Regs->ds, Regs->es); + Warning(" FS %04x GS %04x", Regs->fs, Regs->gs); + + // Control Registers + __asm__ __volatile__ ("mov %%cr0, %0":"=r"(cr)); + Warning(" CR0: 0x%08x", cr); + __asm__ __volatile__ ("mov %%cr2, %0":"=r"(cr)); + Warning(" CR2: 0x%08x", cr); + __asm__ __volatile__ ("mov %%cr3, %0":"=r"(cr)); + Warning(" CR3: 0x%08x", cr); + + // Dump running threads + Proc_DumpThreads(); + + for(;;) __asm__ __volatile__ ("hlt"); +} diff --git a/Kernel/arch/x86/include/arch.h b/Kernel/arch/x86/include/arch.h new file mode 100644 index 00000000..9c16b182 --- /dev/null +++ b/Kernel/arch/x86/include/arch.h @@ -0,0 +1,115 @@ +/* + * Acess2 + * - x86 Architecture + * arch/i386/include/arch.h + */ +#ifndef _ARCH_H_ +#define _ARCH_H_ + +// - Memory Layout +#define MM_USER_MIN 0x00200000 +#define USER_STACK_SZ 0x00010000 +#define USER_STACK_TOP 0x00200000 +#define MM_USER_MAX 0xBC000000 +#define MM_PPD_MIN 0xBC000000 // Per-Process Data +#define MM_PPD_VFS 0xBC000000 // +#define MM_PPD_CFG 0xBFFFF000 // +#define MM_PPD_MAX 0xB0000000 +#define KERNEL_BASE 0xC0000000 +#define MM_KHEAP_BASE 0xC0400000 // C+4MiB +#define MM_KHEAP_MAX 0xCF000000 // +#define MM_KERNEL_VFS 0xCF000000 // +#define MM_KUSER_CODE 0xCFFF0000 // 16 Pages +#define MM_MODULE_MIN 0xD0000000 // Lowest Module Address +#define MM_MODULE_MAX 0xF0000000 // 512 MiB + +#define BITS 32 + +// - Processor/Machine Specific Features +#if ARCH == i386 +// Uses no advanced features +# define USE_MP 0 +# define USE_PAE 0 +#elif ARCH == i586 +// All Enabled +# define USE_MP 1 +# define USE_PAE 1 +#else +# error "Unknown architecture '" #ARCH "'" +#endif + +// === MACROS === +#define LOCK(lockptr) do {int v=1;\ + while(v)__asm__ __volatile__("lock xchgl %%eax, (%%edi)":"=a"(v):"a"(1),"D"(lockptr));}while(0) +#define RELEASE(lockptr) __asm__ __volatile__("lock andl $0, (%%edi)"::"D"(lockptr)); + +// === TYPES === +typedef unsigned int Uint; // Unsigned machine native integer +typedef unsigned char Uint8; +typedef unsigned short Uint16; +typedef unsigned long Uint32; +typedef unsigned long long Uint64; +typedef signed int Sint; // Signed Machine Native integer +typedef signed char Sint8; +typedef signed short Sint16; +typedef signed long Sint32; +typedef signed long long Sint64; +typedef Uint size_t; + +#if USE_PAE +typedef Uint64 tPAddr; +#else +typedef Uint32 tPAddr; +#endif +typedef Uint32 tVAddr; + +typedef void (*tThreadFunction)(void*); + +typedef struct { + Uint gs, fs, es, ds; + Uint edi, esi, ebp, kesp; + Uint ebx, edx, ecx, eax; + Uint int_num, err_code; + Uint eip, cs; + Uint eflags, esp, ss; +} tRegs; + +typedef struct { + Uint Resvd1[4]; // GS, FS, ES, DS + Uint Arg4, Arg5; // EDI, ESI + Uint Arg6; // EBP + Uint Resvd2[1]; // Kernel ESP + union { + Uint Arg1; + Uint Error; + }; // EBX + union { + Uint Arg3; + Uint RetHi; // High 32 bits of ret + }; // EDX + Uint Arg2; // ECX + union { + Uint Num; + Uint Return; + }; // EAX + Uint Resvd3[5]; // Int, Err, Eip, CS, ... + Uint StackPointer; // ESP + Uint Resvd4[1]; // SS +} tSyscallRegs; + +typedef struct { + Uint16 LimitLow; + Uint16 BaseLow; + Uint8 BaseMid; + Uint8 Access; + struct { + unsigned LimitHi: 4; + unsigned Flags: 4; + } __attribute__ ((packed)); + Uint8 BaseHi; +} __attribute__ ((packed)) tGDT; + +// --- Interface Flags & Macros +#define CLONE_VM 0x10 + +#endif // !defined(_ARCH_H_) diff --git a/Kernel/arch/x86/include/mm_phys.h b/Kernel/arch/x86/include/mm_phys.h new file mode 100644 index 00000000..65ab091b --- /dev/null +++ b/Kernel/arch/x86/include/mm_phys.h @@ -0,0 +1,13 @@ +/* + * AcessOS Microkernel Version + * mm_phys.h + */ +#ifndef _MM_PHYS_H +#define _MM_PHYS_H + +// === FUNCTIONS === +extern Uint32 MM_AllocPhys(); +extern void MM_RefPhys(Uint32 Addr); +extern void MM_DerefPhys(Uint32 Addr); + +#endif diff --git a/Kernel/arch/x86/include/mm_virt.h b/Kernel/arch/x86/include/mm_virt.h new file mode 100644 index 00000000..8b7508ca --- /dev/null +++ b/Kernel/arch/x86/include/mm_virt.h @@ -0,0 +1,16 @@ +/* + * AcessOS Microkernel Version + * mm_phys.h + */ +#ifndef _MM_PHYS_H +#define _MM_PHYS_H + +// === FUNCTIONS === +extern void MM_SetCR3(Uint32 CR3); +extern tPAddr MM_Allocate(Uint VAddr); +extern void MM_Deallocate(Uint VAddr); +extern int MM_Map(Uint VAddr, tPAddr PAddr); +extern Uint MM_Clone(); +extern Uint MM_NewKStack(); + +#endif diff --git a/Kernel/arch/x86/include/mp.h b/Kernel/arch/x86/include/mp.h new file mode 100644 index 00000000..f64cd0bf --- /dev/null +++ b/Kernel/arch/x86/include/mp.h @@ -0,0 +1,17 @@ +/* + */ +#ifndef _MP_H +#define _MP_H + +#define MPTABLE_IDENT ('_'|('M'<<8)|('P'<<16)|('_'<<24)) + +typedef struct sMPInfo { + Uint Sig; // '_MP_' + Uint MPConfig; + Uint8 Length; + Uint8 Version; + Uint8 Checksum; + Uint8 Features[5]; // 2-4 are unused +} tMPInfo; + +#endif diff --git a/Kernel/arch/x86/include/proc.h b/Kernel/arch/x86/include/proc.h new file mode 100644 index 00000000..6e559bed --- /dev/null +++ b/Kernel/arch/x86/include/proc.h @@ -0,0 +1,83 @@ +/* + * AcessOS Microkernel Version + * proc.h + */ +#ifndef _PROC_H +#define _PROC_H + +// === CONSTANTS === +#define GETMSG_IGNORE ((void*)-1) + +// === TYPES === +typedef struct sMessage { + struct sMessage *Next; + Uint Source; + Uint Length; + Uint8 Data[]; +} tMsg; // sizeof = 12+ + +typedef struct sThread { + struct sThread *Next; + int IsLocked; + int Status; //!< Thread Status + + Uint TID; //!< Thread ID + Uint TGID; //!< Thread Group (Process) + Uint UID, GID; //!< User and Group + char *ThreadName; //!< Name of thread + + Uint ESP, EBP, EIP; //!< State on switch + #if USE_PAE + Uint64 PML4[3]; //!< Address Space + #else + Uint CR3; //!< Memory Space + #endif + + Uint KernelStack; //!< Thread's Kernel Stack + + tMsg *Messages; //!< Message Queue + tMsg *LastMessage; //!< Last Message (speeds up insertion) + + int Quantum, Remaining; //!< Quantum Size and remaining timesteps + int NumTickets; //!< Priority - Chance of gaining CPU + + Uint Config[NUM_CFG_ENTRIES]; //!< Per-process configuration +} tThread; // sizeof = 68 + +enum { + THREAD_STAT_NULL, + THREAD_STAT_ACTIVE, + THREAD_STAT_SLEEPING, + THREAD_STAT_WAITING, + THREAD_STAT_DEAD +}; + +typedef struct sTSS { + Uint32 Link; + Uint32 ESP0, SS0; + Uint32 ESP1, SS1; + Uint32 ESP2, SS2; + Uint32 CR3; + Uint32 EIP; + Uint32 EFLAGS; + Uint32 EAX, ECX, EDX, EBX; + Uint32 ESP, EBP, ESI, EDI; + Uint32 ES, CS, DS, SS, FS, GS; + Uint32 LDTR; + Uint16 Resvd, IOPB; // IO Permissions Bitmap +} tTSS; + +// === GLOBALS === +extern tThread *gCurrentThread; + +// === FUNCTIONS === +extern void Proc_Start(); +extern int Proc_Clone(Uint *Err, Uint Flags); +extern void Proc_Exit(); +extern void Proc_Yield(); +extern void Proc_Sleep(); +extern void Proc_SetTickets(int Num); +extern tThread *Proc_GetThread(Uint TID); +extern void Thread_Wake(tThread *Thread); + +#endif diff --git a/Kernel/arch/x86/irq.c b/Kernel/arch/x86/irq.c new file mode 100644 index 00000000..848e7e9d --- /dev/null +++ b/Kernel/arch/x86/irq.c @@ -0,0 +1,56 @@ +/* + * AcessOS Microkernel Version + * irq.c + */ +#include + +// === CONSTANTS === +#define MAX_CALLBACKS_PER_IRQ 4 + +// === TYPES === +typedef void (*tIRQ_Callback)(void); + +// === GLOBALS === +tIRQ_Callback gIRQ_Handlers[16][MAX_CALLBACKS_PER_IRQ]; + +// === CODE === +/** + * \fn void IRQ_Handler(tRegs *Regs) + * \brief Handle an IRQ + */ +void IRQ_Handler(tRegs *Regs) +{ + int i; + + Regs->int_num -= 0xF0; // Adjust + + //Log("IRQ_Handler: (Regs={int_num:%i})", Regs->int_num); + + for( i = 0; i < MAX_CALLBACKS_PER_IRQ; i++ ) + { + //Log(" IRQ_Handler: Call %p", gIRQ_Handlers[Regs->int_num][i]); + if( gIRQ_Handlers[Regs->int_num][i] ) + gIRQ_Handlers[Regs->int_num][i](); + } + + outb(0x20, 0x20); // ACK IRQ + if(Regs->int_num >= 8) + outb(0xA0, 0x20); // ACK IRQ (Secondary PIC) +} + +/** + * \fn int IRQ_AddHandler( int Num, void (*Callback)(void) ) + */ +int IRQ_AddHandler( int Num, void (*Callback)(void) ) +{ + int i; + for( i = 0; i < MAX_CALLBACKS_PER_IRQ; i++ ) + { + if( gIRQ_Handlers[Num][i] == NULL ) { + gIRQ_Handlers[Num][i] = Callback; + return 1; + } + } + + return 0; +} diff --git a/Kernel/arch/x86/lib.c b/Kernel/arch/x86/lib.c new file mode 100644 index 00000000..2334417c --- /dev/null +++ b/Kernel/arch/x86/lib.c @@ -0,0 +1,149 @@ +/* + * AcessOS Microkernel Version + * lib.c + */ +#include + +// === CODE === +void Spinlock(int *lock) +{ + int v = 1; + while(v) __asm__ __volatile__ ("lock xchgl %%eax, (%%edi)":"=a"(v):"a"(1),"D"(lock)); +} + +void Release(int *lock) +{ + __asm__ __volatile__ ("lock andl $0, (%0)"::"r"(lock)); +} + +// === IO Commands === +void outb(Uint16 Port, Uint8 Data) +{ + __asm__ __volatile__ ("outb %%al, %%dx"::"d"(Port),"a"(Data)); +} +void outw(Uint16 Port, Uint16 Data) +{ + __asm__ __volatile__ ("outw %%ax, %%dx"::"d"(Port),"a"(Data)); +} +void outd(Uint16 Port, Uint32 Data) +{ + __asm__ __volatile__ ("outl %%eax, %%dx"::"d"(Port),"a"(Data)); +} +Uint8 inb(Uint16 Port) +{ + Uint8 ret; + __asm__ __volatile__ ("inb %%dx, %%al":"=a"(ret):"d"(Port)); + return ret; +} +Uint16 inw(Uint16 Port) +{ + Uint16 ret; + __asm__ __volatile__ ("inw %%dx, %%ax":"=a"(ret):"d"(Port)); + return ret; +} +Uint32 ind(Uint16 Port) +{ + Uint32 ret; + __asm__ __volatile__ ("inl %%dx, %%eax":"=a"(ret):"d"(Port)); + return ret; +} + +/** + * \fn void *memset(void *Dest, int Val, Uint Num) + * \brief Do a byte set of Dest + */ +void *memset(void *Dest, int Val, Uint Num) +{ + __asm__ __volatile__ ("rep stosb" :: "D" (Dest), "a" (Val), "c" (Num)); + return Dest; +} +/** + * \fn void *memsetd(void *Dest, Uint Val, Uint Num) + */ +void *memsetd(void *Dest, Uint Val, Uint Num) +{ + __asm__ __volatile__ ("rep stosl" :: "D" (Dest), "a" (Val), "c" (Num)); + return Dest; +} + + +/** + * \fn void *memcpy(void *Dest, void *Src, Uint Num) + */ +void *memcpy(void *Dest, void *Src, Uint Num) +{ + __asm__ __volatile__ ("rep movsb" :: "D" (Dest), "S" (Src), "c" (Num)); + return Dest; +} +/** + * \fn void *memcpyd(void *Dest, void *Src, Uint Num) + */ +void *memcpyd(void *Dest, void *Src, Uint Num) +{ + __asm__ __volatile__ ("rep movsl" :: "D" (Dest), "S" (Src), "c" (Num)); + return Dest; +} + +/** + * \fn Uint64 __udivdi3(Uint64 Num, Uint64 Den) + * \brief Divide two 64-bit integers + */ +Uint64 __udivdi3(Uint64 Num, Uint64 Den) +{ + Uint64 ret = 0; + + if(Den == 0) __asm__ __volatile__ ("int $0x0"); // Call Div by Zero Error + if(Den == 1) return Num; // Speed Hacks + if(Den == 2) return Num >> 1; // Speed Hacks + if(Den == 4) return Num >> 2; // Speed Hacks + if(Den == 8) return Num >> 3; // Speed Hacks + if(Den == 16) return Num >> 4; // Speed Hacks + if(Den == 32) return Num >> 5; // Speed Hacks + if(Den == 1024) return Num >> 10; // Speed Hacks + if(Den == 2048) return Num >> 11; // Speed Hacks + + if(Num >> 32 == 0 && Den >> 32 == 0) + return (Uint32)Num / (Uint32)Den; + + //Log("__udivdi3: (Num={0x%x:%x}, Den={0x%x:%x})", + // Num>>32, Num&0xFFFFFFFF, + // Den>>32, Den&0xFFFFFFFF); + + while(Num > Den) { + ret ++; + Num -= Den; + } + return ret; +} + +/** + * \fn Uint64 __umoddi3(Uint64 Num, Uint64 Den) + * \brief Get the modulus of two 64-bit integers + */ +Uint64 __umoddi3(Uint64 Num, Uint64 Den) +{ + if(Den == 0) __asm__ __volatile__ ("int $0x0"); // Call Div by Zero Error + if(Den == 1) return 0; // Speed Hacks + if(Den == 2) return Num & 1; // Speed Hacks + if(Den == 4) return Num & 3; // Speed Hacks + if(Den == 8) return Num & 7; // Speed Hacks + if(Den == 16) return Num & 15; // Speed Hacks + if(Den == 32) return Num & 31; // Speed Hacks + if(Den == 1024) return Num & 1023; // Speed Hacks + if(Den == 2048) return Num & 2047; // Speed Hacks + + if(Num >> 32 == 0 && Den >> 32 == 0) + return (Uint32)Num % (Uint32)Den; + + while(Num > Den) + Num -= Den; + return Num; +} + +// --- EXPORTS --- +EXPORT(memcpy); EXPORT(memset); +//EXPORT(memcpyw); EXPORT(memsetw); +EXPORT(memcpyd); EXPORT(memsetd); +EXPORT(inb); EXPORT(inw); EXPORT(ind); +EXPORT(outb); EXPORT(outw); EXPORT(outd); +EXPORT(__udivdi3); EXPORT(__umoddi3); diff --git a/Kernel/arch/x86/link.ld b/Kernel/arch/x86/link.ld new file mode 100644 index 00000000..93066bce --- /dev/null +++ b/Kernel/arch/x86/link.ld @@ -0,0 +1,56 @@ +/* + * AcessMicro Kernel + * Linker Script + */ + +lowStart = start - 0xC0000000; +ENTRY(lowStart) +OUTPUT_FORMAT(elf32-i386) + +SECTIONS { + . = 0x100000; + .multiboot : AT(ADDR(.multiboot)) { + *(.multiboot) + } + + . += 0xC0000000; + + .text ALIGN(0x1000): AT(ADDR(.text) - 0xC0000000) { + *(.text) + } + + .usertext ALIGN(0x1000): AT(ADDR(.usertext) - 0xC0000000) { + _UsertextBase = .; + *(.usertext) + } + + .rodata ALIGN(0x1000): AT(ADDR(.rodata) - 0xC0000000) { + *(.initpd) + *(.rodata) + *(.rdata) + _gKernelModules = .; + *(KMODULES) + _gKernelModulesEnd = .; + . = ALIGN(4); + _gKernelSymbols = .; + *(KEXPORT) + _gKernelSymbolsEnd = .; + + } + + .padata ALIGN (0x1000) : AT(ADDR(.padata) - 0xC0000000) { + *(.padata) + } + + .data ALIGN (0x1000) : AT(ADDR(.data) - 0xC0000000) { + *(.data) + } + + .bss : AT(ADDR(.bss) - 0xC0000000) { + _sbss = .; + *(COMMON) + *(.bss) + _ebss = .; + } + _gKernelEnd = (. + 0xFFF)&0xFFFFF000; +} diff --git a/Kernel/arch/x86/main.c b/Kernel/arch/x86/main.c new file mode 100644 index 00000000..710923d4 --- /dev/null +++ b/Kernel/arch/x86/main.c @@ -0,0 +1,76 @@ +/* + * Acess 2 + * x86 Kernel Main + * arch/x86/main.c + */ +#include +#include +#include +#include +#include + +#define VGA_ERRORS 0 + +// === IMPORTS === +extern void Heap_Install(); +extern void Desctab_Install(); +extern void MM_PreinitVirtual(); +extern void MM_Install(tMBoot_Info *MBoot); +extern void MM_InstallVirtual(); +extern void Proc_Start(); +extern Uint Proc_Clone(Uint *Err, Uint Flags); +extern void Proc_Sleep(); +extern void Proc_Exit(); + +// === GLOBALS === + +// === CODE === +int kmain(Uint MbMagic, tMBoot_Info *MbInfo) +{ + int i; + tMBoot_Module *mods; + + // Adjust Multiboot structure address + MbInfo = (void*)( (Uint)MbInfo + KERNEL_BASE ); + + Desctab_Install(); // Set up GDT and IDT + MM_PreinitVirtual(); // Initialise vital mappings + MM_Install( MbInfo ); // Set up physical memory manager + MM_InstallVirtual(); // Clean up virtual address space + Heap_Install(); // Create initial heap + + Log("Starting Multitasking..."); + // Start Multitasking + Proc_Start(); + + Log("Starting VFS..."); + // Load Virtual Filesystem + VFS_Init(); + + Log("Loading Modules..."); + + // Load initial modules + mods = (void*)( MbInfo->Modules + KERNEL_BASE ); + for(i=0;iModuleCount;i++) + { + // Adjust into higher half + mods[i].Start += KERNEL_BASE; + mods[i].End += KERNEL_BASE; + mods[i].String += KERNEL_BASE; + + Log("Loading '%s'", mods[i].String); + + if( !Module_LoadMem( (void *)mods[i].Start, mods[i].End-mods[i].Start, (char *)mods[i].String ) ) + { + Warning("Unable to load module\n"); + } + } + + // Pass on to Independent Loader + Log("Loading Configuration..."); + System_Init( (char*)(MbInfo->CommandLine + KERNEL_BASE) ); + + // Sleep forever (sleeping beauty) + for(;;) Proc_Sleep(); + return 0; +} diff --git a/Kernel/arch/x86/mm_phys.c b/Kernel/arch/x86/mm_phys.c new file mode 100644 index 00000000..80e92fe6 --- /dev/null +++ b/Kernel/arch/x86/mm_phys.c @@ -0,0 +1,187 @@ +/* + AcessOS Microkernel Version + mm_phys.c +*/ +#include +#include +#include + +#define REFERENCE_BASE 0xE0400000 + +// === IMPORTS === +extern void gKernelEnd; + +// === PROTOTYPES === +Uint32 MM_AllocPhys(); +void MM_RefPhys(Uint32 Addr); +void MM_DerefPhys(Uint32 Addr); + +// === GLOBALS === + int giPhysAlloc = 0; +Uint giPageCount = 0; +Uint32 gaSuperBitmap[1024]; // Blocks of 1024 Pages +Uint32 gaPageBitmap[1024*1024/32]; // Individual pages +Uint32 *gaPageReferences; + +// === CODE === +void MM_Install(tMBoot_Info *MBoot) +{ + Uint kernelPages, num; + Uint i; + tMBoot_Module *mods; + + // Initialise globals + giPageCount = (MBoot->HighMem >> 2) + 256; // HighMem is a kByte value + Log("giPageCount = %i", giPageCount); + + // Get used page count + kernelPages = (Uint)&gKernelEnd - KERNEL_BASE; + kernelPages += 0xFFF; // Page Align + kernelPages >>= 12; + + // Fill page bitmap + num = kernelPages/32; + memsetd(gaPageBitmap, -1, num); + gaPageBitmap[ num ] = (1 << (kernelPages & 31)) - 1; + + // Fill Superpage bitmap + num = kernelPages/(32*32); + memsetd(gaSuperBitmap, -1, num); + gaSuperBitmap[ num ] = (1 << ((kernelPages / 32) & 31)) - 1; + + // Mark Multiboot's pages as taken + // - Structure + MM_RefPhys( (Uint)MBoot - KERNEL_BASE ); + // - Module List + for(i = (MBoot->ModuleCount*sizeof(tMBoot_Module)+0xFFF)>12; i--; ) + MM_RefPhys( MBoot->Modules + (i << 12) ); + // - Modules + mods = (void*)(MBoot->Modules + KERNEL_BASE); + for(i = 0; i < MBoot->ModuleCount; i++) + { + num = (mods[i].End - mods[i].Start + 0xFFF) >> 12; + while(num--) + MM_RefPhys( (mods[i].Start & ~0xFFF) + (num<<12) ); + } + + // Allocate References + Log("Reference Pages %i", (giPageCount*4+0xFFF)>>12); + for(num = 0; num < (giPageCount*4+0xFFF)>>12; num++) + { + MM_Allocate( REFERENCE_BASE + (num<<12) ); + } + + // Fill references + gaPageReferences = (void*)REFERENCE_BASE; + memsetd(gaPageReferences, 1, kernelPages); + for( num = kernelPages; num < giPageCount; num++ ) + { + //if(gaPageBitmap[ num2 / 32 ] == 0) { + // memsetd(&gaPageReferences[num2], 0, 31-(num2&31)); + // num2 = (num2 + 32) & ~31; + //} else + gaPageReferences[num] = (gaPageBitmap[ num / 32 ] >> (num&31)) & 1; + } +} + +/** + * \fn Uint32 MM_AllocPhys() + * \brief Allocates a physical page + */ +tPAddr MM_AllocPhys() +{ + int num = giPageCount / 32 / 32; + int a, b, c; + Uint32 ret; + + LOCK( &giPhysAlloc ); + + // Find free page + for(a=0;gaSuperBitmap[a]==-1&&a0;c++); + + // Mark page used + if(gaPageReferences) + gaPageReferences[a*32*32+b*32+c] = 1; + gaPageBitmap[ a*32+b ] |= 1 << c; + + // Get address + ret = (a << 22) + (b << 17) + (c << 12); + + // Mark used block + if(gaPageBitmap[ a*32+b ] == -1) gaSuperBitmap[a] |= 1 << b; + + // Release Spinlock + RELEASE( &giPhysAlloc ); + //LOG("ret = %x", ret); + return ret; +} + +/** + * \fn void MM_RefPhys(tPAddr Addr) + */ +void MM_RefPhys(tPAddr Addr) +{ + // Get page number + Addr >>= 12; + + // We don't care about non-ram pages + if(Addr >= giPageCount) return; + + // Lock Structures + LOCK( &giPhysAlloc ); + + // Reference the page + if(gaPageReferences) + gaPageReferences[ Addr ] ++; + + // Mark as used + gaPageBitmap[ Addr / 32 ] |= 1 << (Addr&31); + + // Mark used block + if(gaPageBitmap[ Addr / 32 ] == -1) gaSuperBitmap[Addr/1024] |= 1 << ((Addr/32)&31); + + // Release Spinlock + RELEASE( &giPhysAlloc ); +} + +/** + * \fn void MM_DerefPhys(Uint32 Addr) + */ +void MM_DerefPhys(Uint32 Addr) +{ + // Get page number + Addr >>= 12; + + // We don't care about non-ram pages + if(Addr >= giPageCount) return; + + // Check if it is freed + if(gaPageReferences[ Addr ] == 0) { + Warning("MM_DerefPhys - Non-referenced memory dereferenced"); + return; + } + + // Lock Structures + LOCK( &giPhysAlloc ); + + // Dereference + gaPageReferences[ Addr ] --; + + // Mark as free in bitmaps + if( gaPageReferences[ Addr ] == 0 ) + { + gaPageBitmap[ Addr / 32 ] &= ~(1 << (Addr&31)); + if(gaPageReferences[ Addr ] == 0) + gaSuperBitmap[ Addr >> 10 ] &= ~(1 << ((Addr >> 5)&31)); + } + + // Release spinlock + RELEASE( &giPhysAlloc ); +} diff --git a/Kernel/arch/x86/mm_virt.c b/Kernel/arch/x86/mm_virt.c new file mode 100644 index 00000000..985dc144 --- /dev/null +++ b/Kernel/arch/x86/mm_virt.c @@ -0,0 +1,679 @@ +/* + * AcessOS Microkernel Version + * mm_virt.c + * + * Memory Map + * 0xE0 - Kernel Base + * 0xF0 - Kernel Stacks + * 0xFD - Fractals + * 0xFE - Unused + * 0xFF - System Calls / Kernel's User Code + */ +#include +#include +#include + +#define KERNEL_STACKS 0xF0000000 +#define KERNEL_STACK_SIZE 0x00002000 +#define KERNEL_STACK_END 0xFD000000 +#define PAGE_TABLE_ADDR 0xFD000000 +#define PAGE_DIR_ADDR 0xFD3F4000 +//#define PAGE_CR3_ADDR 0xFD3F47F4 +//#define TMP_CR3_ADDR 0xFD3F47F8 // Part of core instead of temp +#define PAGE_CR3_ADDR 0xFD3F4FD0 +#define TMP_CR3_ADDR 0xFD3F4FD4 // Part of core instead of temp +#define TMP_DIR_ADDR 0xFD3F5000 // Same +#define TMP_TABLE_ADDR 0xFD400000 +#define HW_MAP_ADDR 0xFD800000 +#define HW_MAP_MAX 0xFEFF0000 +#define NUM_HW_PAGES ((HW_MAP_MAX-HW_MAP_ADDR)/0x1000) +#define TEMP_MAP_ADDR 0xFEFF0000 // Allows 16 "temp" pages +#define NUM_TEMP_PAGES 16 + +#define USE_COW 1 + +#define PF_PRESENT 0x1 +#define PF_WRITE 0x2 +#define PF_USER 0x4 +#define PF_COW 0x200 +#define PF_PAGED 0x400 + +#define INVLPG(addr) __asm__ __volatile__ ("invlpg (%0)"::"r"(addr)) + +// === IMPORTS === +extern Uint32 gaInitPageDir[1024]; +extern Uint32 gaInitPageTable[1024]; + +// === PROTOTYPES === +void MM_PreinitVirtual(); +void MM_InstallVirtual(); +void MM_PageFault(Uint Addr, Uint ErrorCode, tRegs *Regs); +void MM_DumpTables(tVAddr Start, tVAddr End); +tPAddr MM_DuplicatePage(Uint VAddr); + +// === GLOBALS === +tPAddr *gaPageTable = (void*)PAGE_TABLE_ADDR; +tPAddr *gaPageDir = (void*)PAGE_DIR_ADDR; +tPAddr *gaPageCR3 = (void*)PAGE_CR3_ADDR; +tPAddr *gaTmpTable = (void*)TMP_TABLE_ADDR; +tPAddr *gaTmpDir = (void*)TMP_DIR_ADDR; +tPAddr *gTmpCR3 = (void*)TMP_CR3_ADDR; + int gilTempMappings = 0; + +// === CODE === +/** + * \fn void MM_PreinitVirtual() + */ +void MM_PreinitVirtual() +{ + gaInitPageDir[ 0 ] = 0; + gaInitPageDir[ PAGE_TABLE_ADDR >> 22 ] = ((Uint)&gaInitPageDir - KERNEL_BASE) | 3; +} +/** + * \fn void MM_InstallVirtual() + */ +void MM_InstallVirtual() +{ + int i; + + // --- Pre-Allocate kernel tables + for( i = KERNEL_BASE>>22; i < 1024; i ++ ) + { + if( gaPageDir[ i ] ) continue; + // Skip stack tables, they are process unique + if( i > KERNEL_STACKS >> 22 && i < KERNEL_STACK_END >> 22) { + gaPageDir[ i ] = 0; + continue; + } + // Preallocate table + gaPageDir[ i ] = MM_AllocPhys() | 3; + INVLPG( &gaPageTable[i*1024] ); + memset( &gaPageTable[i*1024], 0, 0x1000 ); + } +} + +/** + * \fn void MM_PageFault(Uint Addr, Uint ErrorCode, tRegs *Regs) + * \brief Called on a page fault + */ +void MM_PageFault(Uint Addr, Uint ErrorCode, tRegs *Regs) +{ + ENTER("xAddr bErrorCode", Addr, ErrorCode); + + // -- Check for COW -- + if( gaPageDir [Addr>>22] & PF_PRESENT + && gaPageTable[Addr>>12] & PF_PRESENT + && gaPageTable[Addr>>12] & PF_COW ) + { + tPAddr paddr; + paddr = MM_DuplicatePage( Addr ); + MM_DerefPhys( gaPageTable[Addr>>12] & ~0xFFF ); + gaPageTable[Addr>>12] &= PF_USER; + gaPageTable[Addr>>12] |= paddr|PF_PRESENT|PF_WRITE; + INVLPG( Addr & ~0xFFF ); + } + + // -- Check Error Code -- + if(ErrorCode & 8) + Warning("Reserved Bits Trashed!"); + else + { + Warning("%s %s %s memory%s", + (ErrorCode&4?"User":"Kernel"), + (ErrorCode&2?"write to":"read from"), + (ErrorCode&1?"bad/locked":"non-present"), + (ErrorCode&16?" (Instruction Fetch)":"") + ); + } + + Log("gaPageDir[0x%x] = 0x%x", Addr>>22, gaPageDir[Addr>>22]); + if( gaPageDir[Addr>>22] & PF_PRESENT ) + Log("gaPageTable[0x%x] = 0x%x", Addr>>12, gaPageTable[Addr>>12]); + + MM_DumpTables(0, -1); + + Panic("Page Fault at 0x%x\n", Regs->eip); + LEAVE('-'); +} + +/** + * \fn void MM_DumpTables(Uint Start, Uint End) + * \brief Dumps the layout of the page tables + */ +void MM_DumpTables(tVAddr Start, tVAddr End) +{ + tVAddr rangeStart = 0; + tPAddr expected = 0; + tVAddr curPos; + Uint page; + const tPAddr MASK = ~0xF98; + + Start >>= 12; End >>= 12; + for(page = Start, curPos = Start<<12; + page < End; + curPos += 0x1000, page++) + { + if( !(gaPageDir[curPos>>22] & PF_PRESENT) + || !(gaPageTable[page] & PF_PRESENT) + || (gaPageTable[page] & MASK) != expected) + { + if(expected) { + Log("0x%08x-0x%08x => 0x%08x-0x%08x (%s%s%s%s)", + rangeStart, curPos - 1, + gaPageTable[rangeStart>>12] & ~0xFFF, + (expected & ~0xFFF) - 1, + (expected & PF_PAGED ? "p" : "-"), + (expected & PF_COW ? "C" : "-"), + (expected & PF_USER ? "U" : "-"), + (expected & PF_WRITE ? "W" : "-") + ); + expected = 0; + } + if( !(gaPageDir[curPos>>22] & PF_PRESENT) ) continue; + if( !(gaPageTable[curPos>>12] & PF_PRESENT) ) continue; + + expected = (gaPageTable[page] & MASK); + rangeStart = curPos; + } + if(expected) expected += 0x1000; + } + + if(expected) { + Log("0x%08x-0x%08x => 0x%08x-0x%08x (%s%s%s%s)", + rangeStart, curPos - 1, + gaPageTable[rangeStart>>12] & ~0xFFF, + (expected & ~0xFFF) - 1, + (expected & PF_PAGED ? "p" : "-"), + (expected & PF_COW ? "C" : "-"), + (expected & PF_USER ? "U" : "-"), + (expected & PF_WRITE ? "W" : "-") + ); + expected = 0; + } +} + +/** + * \fn tPAddr MM_Allocate(Uint VAddr) + */ +tPAddr MM_Allocate(Uint VAddr) +{ + // Check if the directory is mapped + if( gaPageDir[ VAddr >> 22 ] == 0 ) + { + // Allocate directory + gaPageDir[ VAddr >> 22 ] = MM_AllocPhys() | 3; + // Mark as user + if(VAddr < MM_USER_MAX) gaPageDir[ VAddr >> 22 ] |= PF_USER; + + INVLPG( &gaPageDir[ VAddr >> 22 ] ); + memsetd( &gaPageTable[ (VAddr >> 12) & ~0x3FF ], 0, 1024 ); + } + // Check if the page is already allocated + else if( gaPageTable[ VAddr >> 12 ] != 0 ) { + Warning("MM_Allocate - Allocating to used address"); + return gaPageTable[ VAddr >> 12 ] & ~0xFFF; + } + + // Allocate + gaPageTable[ VAddr >> 12 ] = MM_AllocPhys() | 3; + // Mark as user + if(VAddr < MM_USER_MAX) gaPageTable[ VAddr >> 12 ] |= PF_USER; + // Invalidate Cache for address + INVLPG( VAddr & ~0xFFF ); + + return gaPageTable[ VAddr >> 12 ] & ~0xFFF; +} + +/** + * \fn void MM_Deallocate(Uint VAddr) + */ +void MM_Deallocate(Uint VAddr) +{ + if( gaPageDir[ VAddr >> 22 ] == 0 ) { + Warning("MM_Deallocate - Directory not mapped"); + return; + } + + if(gaPageTable[ VAddr >> 12 ] == 0) { + Warning("MM_Deallocate - Page is not allocated"); + return; + } + + // Dereference page + MM_DerefPhys( gaPageTable[ VAddr >> 12 ] & ~0xFFF ); + // Clear page + gaPageTable[ VAddr >> 12 ] = 0; +} + +/** + * \fn tPAddr MM_GetPhysAddr(Uint Addr) + * \brief Checks if the passed address is accesable + */ +tPAddr MM_GetPhysAddr(Uint Addr) +{ + if( !(gaPageDir[Addr >> 22] & 1) ) + return 0; + if( !(gaPageTable[Addr >> 12] & 1) ) + return 0; + return (gaPageTable[Addr >> 12] & ~0xFFF) | (Addr & 0xFFF); +} + +/** + * \fn void MM_SetCR3(Uint CR3) + * \brief Sets the current process space + */ +void MM_SetCR3(Uint CR3) +{ + __asm__ __volatile__ ("mov %0, %%cr3"::"r"(CR3)); +} + +/** + * \fn int MM_Map(Uint VAddr, tPAddr PAddr) + * \brief Map a physical page to a virtual one + */ +int MM_Map(Uint VAddr, tPAddr PAddr) +{ + //ENTER("xVAddr xPAddr", VAddr, PAddr); + // Sanity check + if( PAddr & 0xFFF || VAddr & 0xFFF ) { + Warning("MM_Map - Physical or Virtual Addresses are not aligned"); + //LEAVE('i', 0); + return 0; + } + + // Align addresses + PAddr &= ~0xFFF; VAddr &= ~0xFFF; + + // Check if the directory is mapped + if( gaPageDir[ VAddr >> 22 ] == 0 ) + { + gaPageDir[ VAddr >> 22 ] = MM_AllocPhys() | 3; + + // Mark as user + if(VAddr < MM_USER_MAX) gaPageDir[ VAddr >> 22 ] |= PF_USER; + + INVLPG( &gaPageTable[ (VAddr >> 12) & ~0x3FF ] ); + memsetd( &gaPageTable[ (VAddr >> 12) & ~0x3FF ], 0, 1024 ); + } + // Check if the page is already allocated + else if( gaPageTable[ VAddr >> 12 ] != 0 ) { + Warning("MM_Map - Allocating to used address"); + //LEAVE('i', 0); + return 0; + } + + // Map + gaPageTable[ VAddr >> 12 ] = PAddr | 3; + // Mark as user + if(VAddr < MM_USER_MAX) gaPageTable[ VAddr >> 12 ] |= PF_USER; + + //LOG("gaPageTable[ 0x%x ] = (Uint)%p = 0x%x", + // VAddr >> 12, &gaPageTable[ VAddr >> 12 ], gaPageTable[ VAddr >> 12 ]); + + // Reference + MM_RefPhys( PAddr ); + + //LOG("INVLPG( 0x%x )", VAddr); + INVLPG( VAddr ); + + //LEAVE('i', 1); + return 1; +} + +/** + * \fn Uint MM_ClearUser() + * \brief Clear user's address space + */ +Uint MM_ClearUser() +{ + Uint i, j; + + // Copy Directories + for( i = 0; i < (MM_USER_MAX>>22); i ++ ) + { + // Check if directory is not allocated + if( !(gaPageDir[i] & PF_PRESENT) ) { + gaPageDir[i] = 0; + continue; + } + + + for( j = 0; j < 1024; j ++ ) + { + if( gaPageTable[i*1024+j] & 1 ) + MM_DerefPhys( gaPageTable[i*1024+j] & ~0xFFF ); + gaPageTable[i*1024+j] = 0; + } + + MM_DerefPhys( gaPageDir[i] & ~0xFFF ); + } + + + return *gTmpCR3; +} + +/** + * \fn Uint MM_Clone() + * \brief Clone the current address space + */ +Uint MM_Clone() +{ + Uint i, j; + Uint kStackBase = gCurrentThread->KernelStack - KERNEL_STACK_SIZE; + void *tmp; + + //ENTER(""); + + // Create Directory Table + *gTmpCR3 = MM_AllocPhys() | 3; + INVLPG( gaTmpDir ); + //LOG("Allocated Directory (%x)", *gTmpCR3); + memsetd( gaTmpDir, 0, 1024 ); + + // Copy Tables + for(i=0;i<768;i++) + { + // Check if table is allocated + if( !(gaPageDir[i] & PF_PRESENT) ) { + gaTmpDir[i] = 0; + continue; + } + + // Allocate new table + gaTmpDir[i] = MM_AllocPhys() | (gaPageDir[i] & 7); + INVLPG( &gaTmpTable[i*1024] ); + // Fill + for( j = 0; j < 1024; j ++ ) + { + if( !(gaPageTable[i*1024+j] & PF_PRESENT) ) { + gaTmpTable[i*1024+j] = 0; + continue; + } + + #if USE_COW + // Refrence old page + MM_RefPhys( gaPageTable[i*1024+j] & ~0xFFF ); + // Add to new table + if(gaPageTable[i*1024+j] & PF_WRITE) { + gaTmpTable[i*1024+j] = (gaPageTable[i*1024+j] & ~PF_WRITE) | PF_COW; + gaPageTable[i*1024+j] = (gaPageTable[i*1024+j] & ~PF_WRITE) | PF_COW; + } + else + gaTmpTable[i*1024+j] = gaPageTable[i*1024+j]; + LOG("gaTmpTable[0x%x] = 0x%x", i*1024+j, gaTmpTable[i*1024+j]); + #else + gaTmpTable[i*1024+j] = MM_DuplicatePage( (i*1024+j)<<12 ) | (gaPageTable[i*1024+j]&7); + #endif + } + } + + // Map in kernel tables (and make fractal mapping) + for( i = 768; i < 1024; i ++ ) + { + // Fractal + if( i == (PAGE_TABLE_ADDR >> 22) ) { + gaTmpDir[ PAGE_TABLE_ADDR >> 22 ] = *gTmpCR3; + continue; + } + + if( gaPageDir[i] == 0 ) { + gaTmpDir[i] = 0; + continue; + } + + //LOG("gaPageDir[%x/4] = 0x%x", i*4, gaPageDir[i]); + MM_RefPhys( gaPageDir[i] & ~0xFFF ); + gaTmpDir[i] = gaPageDir[i]; + } + + // Allocate kernel stack + for(i = KERNEL_STACKS >> 22; + i < KERNEL_STACK_END >> 22; + i ++ ) + { + // Check if directory is allocated + if( (gaPageDir[i] & 1) == 0 ) { + gaTmpDir[i] = 0; + continue; + } + + // We don't care about other kernel stacks, just the current one + if( i != kStackBase >> 22 ) { + MM_DerefPhys( gaPageDir[i] & ~0xFFF ); + gaTmpDir[i] = 0; + continue; + } + + // Create a copy + gaTmpDir[i] = MM_AllocPhys() | 3; + INVLPG( &gaTmpTable[i*1024] ); + for( j = 0; j < 1024; j ++ ) + { + // Is the page allocated? If not, skip + if( !(gaPageTable[i*1024+j] & 1) ) { + gaTmpTable[i*1024+j] = 0; + continue; + } + + // We don't care about other kernel stacks + if( ((i*1024+j)*4096 & ~(KERNEL_STACK_SIZE-1)) != kStackBase ) { + gaTmpTable[i*1024+j] = 0; + continue; + } + + // Allocate page + gaTmpTable[i*1024+j] = MM_AllocPhys() | 3; + + MM_RefPhys( gaTmpTable[i*1024+j] & ~0xFFF ); + + tmp = (void *) MM_MapTemp( gaTmpTable[i*1024+j] & ~0xFFF ); + memcpy( tmp, (void *)( (i*1024+j)*0x1000 ), 0x1000 ); + MM_FreeTemp( (Uint)tmp ); + } + } + + //LEAVE('x', *gTmpCR3 & ~0xFFF); + return *gTmpCR3 & ~0xFFF; +} + +/** + * \fn Uint MM_NewKStack() + * \brief Create a new kernel stack + */ +Uint MM_NewKStack() +{ + Uint base = KERNEL_STACKS; + Uint i; + for(;base> 22] & 1) ) return ; + if( !(gaPageTable[VAddr >> 12] & 1) ) return ; + + ent = &gaPageTable[VAddr >> 12]; + + // Read-Only + if( Mask & MM_PFLAG_RO ) + { + if( Flags & MM_PFLAG_RO ) *ent &= ~PF_WRITE; + else *ent |= PF_WRITE; + } + + // Kernel + if( Mask & MM_PFLAG_KERNEL ) + { + if( Flags & MM_PFLAG_KERNEL ) *ent &= ~PF_USER; + else *ent |= PF_USER; + } +} + +/** + * \fn tPAddr MM_DuplicatePage(Uint VAddr) + * \brief Duplicates a virtual page to a physical one + */ +tPAddr MM_DuplicatePage(Uint VAddr) +{ + tPAddr ret; + Uint temp; + int wasRO = 0; + + // Check if mapped + if( !(gaPageDir [VAddr >> 22] & PF_PRESENT) ) return 0; + if( !(gaPageTable[VAddr >> 12] & PF_PRESENT) ) return 0; + + // Page Align + VAddr &= ~0xFFF; + + // Allocate new page + ret = MM_AllocPhys(); + + // Write-lock the page (to keep data constistent), saving its R/W state + wasRO = (gaPageTable[VAddr >> 12] & PF_WRITE ? 0 : 1); + gaPageTable[VAddr >> 12] &= ~PF_WRITE; + INVLPG( VAddr ); + + // Copy Data + temp = MM_MapTemp(ret); + memcpy( (void*)temp, (void*)VAddr, 0x1000 ); + MM_FreeTemp(temp); + + // Restore Writeable status + if(!wasRO) gaPageTable[VAddr >> 12] |= PF_WRITE; + INVLPG(VAddr); + + return ret; +} + +/** + * \fn Uint MM_MapTemp(tPAddr PAddr) + * \brief Create a temporary memory mapping + * \todo Show Luigi Barone (C Lecturer) and see what he thinks + */ +Uint MM_MapTemp(tPAddr PAddr) +{ + int i; + + //ENTER("XPAddr", PAddr); + + PAddr &= ~0xFFF; + + //LOG("gilTempMappings = %i", gilTempMappings); + + for(;;) + { + LOCK( &gilTempMappings ); + + for( i = 0; i < NUM_TEMP_PAGES; i ++ ) + { + // Check if page used + if(gaPageTable[ (TEMP_MAP_ADDR >> 12) + i ] & 1) continue; + // Mark as used + gaPageTable[ (TEMP_MAP_ADDR >> 12) + i ] = PAddr | 3; + INVLPG( TEMP_MAP_ADDR + (i << 12) ); + //LEAVE('p', TEMP_MAP_ADDR + (i << 12)); + RELEASE( &gilTempMappings ); + return TEMP_MAP_ADDR + (i << 12); + } + RELEASE( &gilTempMappings ); + Proc_Yield(); + } +} + +/** + * \fn void MM_FreeTemp(Uint PAddr) + * \brief Free's a temp mapping + */ +void MM_FreeTemp(Uint VAddr) +{ + int i = VAddr >> 12; + //ENTER("xVAddr", VAddr); + + if(i >= (TEMP_MAP_ADDR >> 12)) + gaPageTable[ i ] = 0; + + //LEAVE('-'); +} + +/** + * \fn Uint MM_MapHWPage(tPAddr PAddr, Uint Number) + * \brief Allocates a contigous number of pages + */ +Uint MM_MapHWPage(tPAddr PAddr, Uint Number) +{ + int i, j; + + PAddr &= ~0xFFF; + + // Scan List + for( i = 0; i < NUM_HW_PAGES; i ++ ) + { + // Check if addr used + if( gaPageTable[ (HW_MAP_ADDR >> 12) + i ] & 1 ) + continue; + + // Check possible region + for( j = 0; j < Number && i + j < NUM_HW_PAGES; j ++ ) + { + // If there is an allocated page in the region we are testing, break + if( gaPageTable[ (HW_MAP_ADDR >> 12) + i + j ] & 1 ) break; + } + // Is it all free? + if( j == Number ) + { + // Allocate + for( j = 0; j < Number; j++ ) { + MM_RefPhys( PAddr + (j<<12) ); + gaPageTable[ (HW_MAP_ADDR >> 12) + i + j ] = (PAddr + (j<<12)) | 3; + } + return HW_MAP_ADDR + (i<<12); + } + } + // If we don't find any, return NULL + return 0; +} + +/** + * \fn void MM_UnmapHWPage(Uint VAddr, Uint Number) + * \brief Unmap a hardware page + */ +void MM_UnmapHWPage(Uint VAddr, Uint Number) +{ + int i, j; + // Sanity Check + if(VAddr < HW_MAP_ADDR || VAddr-Number*0x1000 > HW_MAP_MAX) return; + + i = VAddr >> 12; + + LOCK( &gilTempMappings ); // Temp and HW share a directory, so they share a lock + + for( j = 0; j < Number; j++ ) + { + MM_DerefPhys( gaPageTable[ (HW_MAP_ADDR >> 12) + i + j ] ); + gaPageTable[ (HW_MAP_ADDR >> 12) + i + j ] = 0; + } + + RELEASE( &gilTempMappings ); +} + +// --- EXPORTS --- +EXPORT(MM_GetPhysAddr); +EXPORT(MM_Map); +//EXPORT(MM_Unmap); +EXPORT(MM_MapHWPage); +EXPORT(MM_UnmapHWPage); diff --git a/Kernel/arch/x86/proc.c b/Kernel/arch/x86/proc.c new file mode 100644 index 00000000..d7af3e3d --- /dev/null +++ b/Kernel/arch/x86/proc.c @@ -0,0 +1,836 @@ +/* + * AcessOS Microkernel Version + * proc.c + */ +#include +#include +#include +#include +#if USE_MP +# include +#endif + +// === CONSTANTS === +#define RANDOM_SEED 0xACE55052 +#define SWITCH_MAGIC 0xFFFACE55 // There is no code in this area +#define DEFAULT_QUANTUM 10 +#define DEFAULT_TICKETS 5 +#define MAX_TICKETS 10 +#define TIMER_DIVISOR 11931 //~100Hz + +// === IMPORTS === +extern tGDT gGDT[]; +extern Uint GetEIP(); // start.asm +extern Uint32 gaInitPageDir[1024]; // start.asm +extern void Kernel_Stack_Top; + +// === PROTOTYPES === +void Proc_Start(); +void Proc_ChangeStack(); + int Proc_Clone(Uint *Err, Uint Flags); +void Proc_Exit(); +void Proc_Yield(); +void Proc_Sleep(); +static tThread *Proc_int_GetPrevThread(tThread **List, tThread *Thread); +void Proc_Scheduler(); +Sint64 now(); +Uint rand(); + +// === GLOBALS === +// -- Core Thread -- +tThread gThreadZero = { + NULL, 0, // Next, Lock + THREAD_STAT_ACTIVE, // Status + 0, 0, // TID, TGID + 0, 0, // UID, GID + "ThreadZero", // Name + 0, 0, 0, // ESP, EBP, EIP (Set on switch) + #if USE_PAE + {0,0,0}, // PML4 Entries + #else + (Uint)&gaInitPageDir-KERNEL_BASE, // CR3 + #endif + (Uint)&Kernel_Stack_Top, // Kernel Stack (Unused as it is PL0) + NULL, NULL, // Messages, Last Message + DEFAULT_QUANTUM, DEFAULT_QUANTUM, // Quantum, Remaining + DEFAULT_TICKETS, + {0} // Default config to zero + }; +// -- Processes -- +// --- Locks --- +volatile int giThreadListLock = 0; ///\note NEVER use a heap function while locked +// --- Current State --- +#if USE_MP +tThread **gCurrentThread = NULL; +# define CUR_THREAD gCurrentThread[0] +#else +tThread *gCurrentThread = NULL; +# define CUR_THREAD gCurrentThread +#endif +volatile int giNumActiveThreads = 0; +volatile int giTotalTickets = 0; +volatile Uint giNextTID = 1; +// --- Thread Lists --- +tThread *gActiveThreads = NULL; // Currently Running Threads +tThread *gSleepingThreads = NULL; // Sleeping Threads +tThread *gDeleteThreads = NULL; // Threads to delete +// --- Multiprocessing --- + int giNumCPUs = 1; +#if USE_MP +tMPInfo *gMPTable = NULL; +#endif +#if USE_PAE +Uint32 *gPML4s[4] = NULL; +#endif +tTSS *gTSSs = NULL; +#if !USE_MP +tTSS gTSS0 = {0}; +#endif + +// === CODE === +/** + * \fn void Proc_Start() + * \brief Starts the process scheduler + */ +void Proc_Start() +{ + Uint pos = 0; + + #if USE_MP + // -- Initialise Multiprocessing + // Find MP Floating Table + // - EBDA + for(pos = KERNEL_BASE|0x9FC00; pos < (KERNEL_BASE|0xA0000); pos += 16) { + if( *(Uint*)(pos) == MPTABLE_IDENT ) { + if(ByteSum( (void*)pos, sizeof(tMPInfo) ) != 0) continue; + gMPTable = (void*)pos; + break; + } + } + // - Last KiB + if(!gMPTable) { + + } + // - BIOS ROM + if(!gMPTable) { + for(pos = KERNEL_BASE|0xF0000; pos < (KERNEL_BASE|0x100000); pos += 16) { + if( *(Uint*)(pos) == MPTABLE_IDENT ) { + if(ByteSum( (void*)pos, sizeof(tMPInfo) ) != 0) continue; + gMPTable = (void*)pos; + break; + } + } + } + + // If the MP Table Exists, parse it + if(gMPTable) + { + Panic("Uh oh... MP Table Parsing is unimplemented\n"); + } else { + #endif + giNumCPUs = 1; + gTSSs = &gTSS0; + #if USE_MP + } + + // Initialise TSS + for(pos=0;pos> 16; + gGDT[5+pos].BaseHi = (Uint)&gTSSs[pos] >> 24; + #if USE_MP + } + for(pos=0;pos>8)&0xFF); // High Byte + + // Create Initial Task + gActiveThreads = &gThreadZero; + gCurrentThread = &gThreadZero; + giTotalTickets = gThreadZero.NumTickets; + giNumActiveThreads = 1; + + // Create Per-Process Data Block + MM_Allocate(MM_PPD_CFG); + + // Change Stacks + Proc_ChangeStack(); + + #if 1 + // Create Idle Task + if(Proc_Clone(0, 0) == 0) + { + gCurrentThread->ThreadName = "Idle Thread"; + Proc_SetTickets(0); // Never called randomly + gCurrentThread->Quantum = 1; // 1 slice quantum + for(;;) __asm__ __volatile__ ("hlt"); // Just yeilds + } + #endif + + // Start Interrupts (and hence scheduler) + __asm__ __volatile__("sti"); +} + +/** + * \fn void Proc_ChangeStack() + * \brief Swaps the current stack for a new one (in the proper stack reigon) + */ +void Proc_ChangeStack() +{ + Uint esp, ebp; + Uint tmpEbp, oldEsp; + Uint curBase, newBase; + + __asm__ __volatile__ ("mov %%esp, %0":"=r"(esp)); + __asm__ __volatile__ ("mov %%ebp, %0":"=r"(ebp)); + + oldEsp = esp; + + // Create new KStack + newBase = MM_NewKStack(); + // Check for errors + if(newBase == 0) { + Panic("What the?? Unable to allocate space for initial kernel stack"); + return; + } + + curBase = gCurrentThread->KernelStack; + + LOG("curBase = 0x%x, newBase = 0x%x", curBase, newBase); + + // Get ESP as a used size + esp = curBase - esp; + LOG("memcpy( %p, %p, 0x%x )", (void*)(newBase - esp), (void*)(curBase - esp), esp ); + // Copy used stack + memcpy( (void*)(newBase - esp), (void*)(curBase - esp), esp ); + // Get ESP as an offset in the new stack + esp = newBase - esp; + // Adjust EBP + ebp = newBase - (curBase - ebp); + + // Repair EBPs & Stack Addresses + // Catches arguments also, but may trash stack-address-like values + for(tmpEbp = esp; tmpEbp < newBase; tmpEbp += 4) + { + if(oldEsp < *(Uint*)tmpEbp && *(Uint*)tmpEbp < curBase) + *(Uint*)tmpEbp += newBase - curBase; + } + + gCurrentThread->KernelStack = newBase; + + __asm__ __volatile__ ("mov %0, %%esp"::"r"(esp)); + __asm__ __volatile__ ("mov %0, %%ebp"::"r"(ebp)); +} + +/** + * \fn int Proc_Clone(Uint *Err, Uint Flags) + * \brief Clone the current process + */ +int Proc_Clone(Uint *Err, Uint Flags) +{ + tThread *newThread; + Uint eip, esp, ebp; + + __asm__ __volatile__ ("mov %%esp, %0": "=r"(esp)); + __asm__ __volatile__ ("mov %%ebp, %0": "=r"(ebp)); + + // Create new thread structure + newThread = malloc( sizeof(tThread) ); + if(!newThread) { + Warning("Proc_Clone - Out of memory when creating thread\n"); + *Err = -ENOMEM; + return -1; + } + // Base new thread on old + memcpy(newThread, gCurrentThread, sizeof(tThread)); + // Initialise Memory Space (New Addr space or kernel stack) + if(Flags & CLONE_VM) { + newThread->TGID = newThread->TID; + newThread->CR3 = MM_Clone(); + } else { + Uint tmpEbp, oldEsp = esp; + + // Create new KStack + newThread->KernelStack = MM_NewKStack(); + // Check for errors + if(newThread->KernelStack == 0) { + free(newThread); + return -1; + } + + // Get ESP as a used size + esp = gCurrentThread->KernelStack - esp; + // Copy used stack + memcpy( (void*)(newThread->KernelStack - esp), (void*)(gCurrentThread->KernelStack - esp), esp ); + // Get ESP as an offset in the new stack + esp = newThread->KernelStack - esp; + // Adjust EBP + ebp = newThread->KernelStack - (gCurrentThread->KernelStack - ebp); + + // Repair EBPs & Stack Addresses + // Catches arguments also, but may trash stack-address-like values + for(tmpEbp = esp; tmpEbp < newThread->KernelStack; tmpEbp += 4) + { + if(oldEsp < *(Uint*)tmpEbp && *(Uint*)tmpEbp < gCurrentThread->KernelStack) + *(Uint*)tmpEbp += newThread->KernelStack - gCurrentThread->KernelStack; + } + } + + // Set Pointer, Spinlock and TID + newThread->Next = NULL; + newThread->IsLocked = 0; + newThread->TID = giNextTID++; + + // Clear message list (messages are not inherited) + newThread->Messages = NULL; + newThread->LastMessage = NULL; + + // Set remaining (sheduler expects remaining to be correct) + newThread->Remaining = newThread->Quantum; + + // Save core machine state + newThread->ESP = esp; + newThread->EBP = ebp; + eip = GetEIP(); + if(eip == SWITCH_MAGIC) { + outb(0x20, 0x20); // ACK Timer and return as child + return 0; + } + + // Set EIP as parent + newThread->EIP = eip; + + //Log(" Proc_Clone: giTimestamp = %i.%07i", (Uint)giTimestamp, (Uint)giPartMiliseconds/214); + + // Lock list and add to active + LOCK( &giThreadListLock ); + newThread->Next = gActiveThreads; + gActiveThreads = newThread; + giNumActiveThreads ++; + giTotalTickets += newThread->NumTickets; + RELEASE( &giThreadListLock ); + + return newThread->TID; +} + +/** + * \fn void Proc_SetThreadName() + * \brief Sets the thread's name + */ +void Proc_SetThreadName(char *NewName) +{ + if( (Uint)CUR_THREAD->ThreadName > 0xC0400000 ) + free( CUR_THREAD->ThreadName ); + CUR_THREAD->ThreadName = malloc(strlen(NewName)+1); + strcpy(CUR_THREAD->ThreadName, NewName); +} + +/** + * \fn Uint Proc_MakeUserStack() + */ +Uint Proc_MakeUserStack() +{ + int i; + Uint base = USER_STACK_TOP - USER_STACK_SZ; + + // Check Prospective Space + for( i = USER_STACK_SZ >> 12; i--; ) + if( MM_GetPhysAddr( base + (i<<12) ) != 0 ) + break; + + if(i != -1) return 0; + + // Allocate Stack - Allocate incrementally to clean up MM_Dump output + for( i = 0; i < USER_STACK_SZ/4069; i++ ) + MM_Allocate( base + (i<<12) ); + + return base + USER_STACK_SZ; +} + + +/** + * \fn void Proc_StartUser(Uint Entrypoint, Uint Base, int ArgC, char **ArgV, char **EnvP, int DataSize) + * \brief Starts a user task + */ +void Proc_StartUser(Uint Entrypoint, Uint *Bases, int ArgC, char **ArgV, char **EnvP, int DataSize) +{ + Uint *stack = (void*)Proc_MakeUserStack(); + int i; + Uint delta; + Uint16 ss, cs; + + LOG("stack = 0x%x", stack); + + // Copy Arguments + stack = (void*)( (Uint)stack - DataSize ); + memcpy( stack, ArgV, DataSize ); + + // Adjust Arguments and environment + delta = (Uint)stack - (Uint)ArgV; + ArgV = (char**)stack; + for( i = 0; ArgV[i]; i++ ) ArgV[i] += delta; + i ++; + EnvP = &ArgV[i]; + for( i = 0; EnvP[i]; i++ ) EnvP[i] += delta; + + // User Mode Segments + ss = 0x23; cs = 0x1B; + + // Arguments + *--stack = (Uint)EnvP; + *--stack = (Uint)ArgV; + *--stack = (Uint)ArgC; + while(*Bases) + *--stack = *Bases++; + *--stack = 0; // Return Address + delta = (Uint)stack; // Reuse delta to save SP + + *--stack = ss; //Stack Segment + *--stack = delta; //Stack Pointer + *--stack = 0x0202; //EFLAGS (Resvd (0x2) and IF (0x20)) + *--stack = cs; //Code Segment + *--stack = Entrypoint; //EIP + //PUSHAD + *--stack = 0xAAAAAAAA; // eax + *--stack = 0xCCCCCCCC; // ecx + *--stack = 0xDDDDDDDD; // edx + *--stack = 0xBBBBBBBB; // ebx + *--stack = 0xD1D1D1D1; // edi + *--stack = 0x54545454; // esp - NOT POPED + *--stack = 0x51515151; // esi + *--stack = 0xB4B4B4B4; // ebp + //Individual PUSHs + *--stack = ss; // ds + *--stack = ss; // es + *--stack = ss; // fs + *--stack = ss; // gs + + __asm__ __volatile__ ( + "mov %%eax,%%esp;\n\t" // Set stack pointer + "pop %%gs;\n\t" + "pop %%fs;\n\t" + "pop %%es;\n\t" + "pop %%ds;\n\t" + "popa;\n\t" + "iret;\n\t" : : "a" (stack)); + for(;;); +} + +/** + * \fn void Proc_Exit() + * \brief Kill the current process + */ +void Proc_Exit() +{ + tThread *thread; + tMsg *msg; + + ///\note Double lock is needed due to overlap of locks + + // Lock thread (stop us recieving messages) + LOCK( &gCurrentThread->IsLocked ); + + // Lock thread list + LOCK( &giThreadListLock ); + + // Get previous thread on list + thread = Proc_int_GetPrevThread( &gActiveThreads, gCurrentThread ); + if(!thread) { + Warning("Proc_Exit - Current thread is not on the active queue"); + return; + } + + // Clear Message Queue + while( gCurrentThread->Messages ) + { + msg = gCurrentThread->Messages->Next; + free( gCurrentThread->Messages ); + gCurrentThread->Messages = msg; + } + + gCurrentThread->Remaining = 0; // Clear Remaining Quantum + gCurrentThread->Quantum = 0; // Clear Quantum to indicate dead thread + thread->Next = gCurrentThread->Next; // Remove from active + + // Add to delete queue + if(gDeleteThreads) { + gCurrentThread->Next = gDeleteThreads; + gDeleteThreads = gCurrentThread; + } else { + gCurrentThread->Next = NULL; + gDeleteThreads = gCurrentThread; + } + + giNumActiveThreads --; + giTotalTickets -= gCurrentThread->NumTickets; + + // Mark thread as sleeping + gCurrentThread->Status = THREAD_STAT_DEAD; + + // Release spinlocks + RELEASE( &gCurrentThread->IsLocked ); // Released first so that it IS released + RELEASE( &giThreadListLock ); + __asm__ __volatile__ ("hlt"); +} + +/** + * \fn void Proc_Yield() + * \brief Yield remainder of timeslice + */ +void Proc_Yield() +{ + gCurrentThread->Quantum = 0; + __asm__ __volatile__ ("hlt"); +} + +/** + * \fn void Proc_Sleep() + * \brief Take the current process off the run queue + */ +void Proc_Sleep() +{ + tThread *thread; + + //Log("Proc_Sleep: %i going to sleep", gCurrentThread->TID); + + // Acquire Spinlock + LOCK( &giThreadListLock ); + + // Get thread before current thread + thread = Proc_int_GetPrevThread( &gActiveThreads, gCurrentThread ); + if(!thread) { + Warning("Proc_Sleep - Current thread is not on the active queue"); + return; + } + + // Don't sleep if there is a message waiting + if( gCurrentThread->Messages ) { + RELEASE( &giThreadListLock ); + return; + } + + // Unset remaining timeslices (force a task switch on timer fire) + gCurrentThread->Remaining = 0; + + // Remove from active list + thread->Next = gCurrentThread->Next; + + // Add to Sleeping List (at the top) + gCurrentThread->Next = gSleepingThreads; + gSleepingThreads = gCurrentThread; + + // Reduce the active count & ticket count + giNumActiveThreads --; + giTotalTickets -= gCurrentThread->NumTickets; + + // Mark thread as sleeping + gCurrentThread->Status = THREAD_STAT_SLEEPING; + + // Release Spinlock + RELEASE( &giThreadListLock ); + + __asm__ __volatile__ ("hlt"); +} + +/** + * \fn void Thread_Wake( tThread *Thread ) + * \brief Wakes a sleeping/waiting thread up + */ +void Thread_Wake(tThread *Thread) +{ + tThread *prev; + switch(Thread->Status) + { + case THREAD_STAT_ACTIVE: break; + case THREAD_STAT_SLEEPING: + LOCK( &giThreadListLock ); + prev = Proc_int_GetPrevThread(&gSleepingThreads, Thread); + prev->Next = Thread->Next; // Remove from sleeping queue + Thread->Next = gActiveThreads; // Add to active queue + gActiveThreads = Thread; + Thread->Status = THREAD_STAT_ACTIVE; + RELEASE( &giThreadListLock ); + break; + case THREAD_STAT_WAITING: + Warning("Thread_Wake - Waiting threads are not currently supported"); + break; + case THREAD_STAT_DEAD: + Warning("Thread_Wake - Attempt to wake dead thread (%i)", Thread->TID); + break; + default: + Warning("Thread_Wake - Unknown process status (%i)\n", Thread->Status); + break; + } +} + +/** + * \fn int Proc_Demote(Uint *Err, int Dest, tRegs *Regs) + * \brief Demotes a process to a lower permission level + * \param Err Pointer to user's errno + */ +int Proc_Demote(Uint *Err, int Dest, tRegs *Regs) +{ + int cpl = Regs->cs & 3; + // Sanity Check + if(Dest > 3 || Dest < 0) { + *Err = -EINVAL; + return -1; + } + + // Permission Check + if(cpl > Dest) { + *Err = -EACCES; + return -1; + } + + // Change the Segment Registers + Regs->cs = (((Dest+1)<<4) | Dest) - 8; + Regs->ss = ((Dest+1)<<4) | Dest; + // Check if the GP Segs are GDT, then change them + if(!(Regs->ds & 4)) Regs->ds = ((Dest+1)<<4) | Dest; + if(!(Regs->es & 4)) Regs->es = ((Dest+1)<<4) | Dest; + if(!(Regs->fs & 4)) Regs->fs = ((Dest+1)<<4) | Dest; + if(!(Regs->gs & 4)) Regs->gs = ((Dest+1)<<4) | Dest; + + return 0; +} + +/** + * \fn void Proc_SetTickets(int Num) + * \brief Sets the 'priority' of a task + */ +void Proc_SetTickets(int Num) +{ + if(Num < 0) return; + if(Num > MAX_TICKETS) Num = MAX_TICKETS; + + LOCK( &giThreadListLock ); + giTotalTickets -= gCurrentThread->NumTickets; + gCurrentThread->NumTickets = Num; + giTotalTickets += Num; + //LOG("giTotalTickets = %i", giTotalTickets); + RELEASE( &giThreadListLock ); +} + +/** + * \fn tThread *Proc_GetThread(Uint TID) + * \brief Gets a thread given its TID + */ +tThread *Proc_GetThread(Uint TID) +{ + tThread *thread; + + // Search Active List + for(thread = gActiveThreads; + thread; + thread = thread->Next) + { + if(thread->TID == TID) + return thread; + } + + // Search Sleeping List + for(thread = gSleepingThreads; + thread; + thread = thread->Next) + { + if(thread->TID == TID) + return thread; + } + + return NULL; +} + +/** + * \fn static tThread *Proc_int_GetPrevThread(tThread *List, tThread *Thread) + * \brief Gets the previous entry in a thead linked list + */ +static tThread *Proc_int_GetPrevThread(tThread **List, tThread *Thread) +{ + tThread *ret; + // First Entry + if(*List == Thread) { + return (tThread*)List; + } else { + for(ret = *List; + ret->Next && ret->Next != Thread; + ret = ret->Next + ); + // Error if the thread is not on the list + if(!ret->Next || ret->Next != Thread) { + return NULL; + } + } + return ret; +} + +/** + * \fn void Proc_DumpThreads() + */ +void Proc_DumpThreads() +{ + tThread *thread; + + Log("Active Threads:"); + for(thread=gActiveThreads;thread;thread=thread->Next) + { + Log(" %i (%i) - %s", thread->TID, thread->TGID, thread->ThreadName); + Log(" %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum); + Log(" CR3 0x%x, KStack 0x%x", thread->CR3, thread->KernelStack); + } + Log("Sleeping Threads:"); + for(thread=gSleepingThreads;thread;thread=thread->Next) + { + Log(" %i (%i) - %s", thread->TID, thread->TGID, thread->ThreadName); + Log(" %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum); + Log(" CR3 0x%x, KStack 0x%x", thread->CR3, thread->KernelStack); + } +} + +/** + * \fn void Proc_Scheduler(int CPU) + * \brief Swap current task + */ +void Proc_Scheduler(int CPU) +{ + Uint esp, ebp, eip; + Uint number, ticket; + tThread *thread; + + // If the spinlock is set, let it complete + if(giThreadListLock) return; + + // Clear Delete Queue + while(gDeleteThreads) + { + thread = gDeleteThreads->Next; + if(gDeleteThreads->IsLocked) { // Only free if structure is unused + gDeleteThreads->Status = THREAD_STAT_NULL; + free( gDeleteThreads ); + } + gDeleteThreads = thread; + } + + // Check if there is any tasks running + if(giNumActiveThreads == 0) { + Log("No Active threads, sleeping\n"); + __asm__ __volatile__ ("hlt"); + return; + } + + // Reduce remaining quantum + if(gCurrentThread->Remaining--) return; + // Reset quantum for next call + gCurrentThread->Remaining = gCurrentThread->Quantum; + + // Get machine state + __asm__ __volatile__ ("mov %%esp, %0":"=r"(esp)); + __asm__ __volatile__ ("mov %%ebp, %0":"=r"(ebp)); + eip = GetEIP(); + if(eip == SWITCH_MAGIC) return; // Check if a switch happened + + // Save machine state + gCurrentThread->ESP = esp; + gCurrentThread->EBP = ebp; + gCurrentThread->EIP = eip; + + // Special case: 1 thread + if(giNumActiveThreads == 1) + { + // Check if a switch is needed (NumActive can be 1 after a sleep) + if(gActiveThreads == gCurrentThread) return; + // Switch processes + gCurrentThread = gActiveThreads; + goto performSwitch; + } + + // Get the ticket number + ticket = number = rand() % giTotalTickets; + + // Find the next thread + for(thread=gActiveThreads;thread;thread=thread->Next) + { + if(thread->NumTickets > number) break; + number -= thread->NumTickets; + } + + // Error Check + if(thread == NULL) + { + number = 0; + for(thread=gActiveThreads;thread;thread=thread->Next) + number += thread->NumTickets; + Panic("Bookeeping Failed - giTotalTicketCount (%i) != true count (%i)", + giTotalTickets, number); + } + + // Set current thread + gCurrentThread = thread; + + // Update Kernel Stack pointer + gTSSs[CPU].ESP0 = thread->KernelStack; + +performSwitch: + // Set address space + //MM_SetCR3( gCurrentThread->CR3 ); + __asm__ __volatile__ ("mov %0, %%cr3"::"a"(gCurrentThread->CR3)); + // Switch threads + __asm__ __volatile__ ( + "mov %1, %%esp\n\t" + "mov %2, %%ebp\n\t" + "jmp *%3" : : + "a"(SWITCH_MAGIC), "b"(gCurrentThread->ESP), + "d"(gCurrentThread->EBP), "c"(gCurrentThread->EIP)); + for(;;); // Shouldn't reach here +} + +// --- Process Structure Access Functions --- +int Proc_GetPID() +{ + return gCurrentThread->TGID; +} +int Proc_GetTID() +{ + return gCurrentThread->TID; +} +int Proc_GetUID() +{ + return gCurrentThread->UID; +} +int Proc_GetGID() +{ + return gCurrentThread->GID; +} + +/** + * \fn Uint rand() + * \brief Pseudo random number generator + * \note Unknown effectiveness (made up on the spot) + */ +Uint rand() +{ + static Uint randomState = RANDOM_SEED; + Uint ret = randomState; + int roll = randomState & 31; + randomState = (randomState << roll) | (randomState >> (32-roll)); + randomState ^= 0x9A3C5E78; + return ret; +} diff --git a/Kernel/arch/x86/start.asm b/Kernel/arch/x86/start.asm new file mode 100644 index 00000000..e1cc70ad --- /dev/null +++ b/Kernel/arch/x86/start.asm @@ -0,0 +1,105 @@ +; AcessOS Microkernel Version +; Start.asm + +[bits 32] + +KERNEL_BASE equ 0xC0000000 + +[section .multiboot] +mboot: + ; Multiboot macros to make a few lines later more readable + 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 mboot - KERNEL_BASE ;Location of Multiboot Header + +[section .text] +[extern _kmain] +[global start] +start: + ; Set up stack + mov esp, _Kernel_Stack_Top + + ; Start Paging + mov ecx, _gaInitPageDir - KERNEL_BASE + mov cr3, ecx + + mov ecx, cr0 + or ecx, 0x80000000 + mov cr0, ecx + + lea ecx, [.higherHalf] + jmp ecx +.higherHalf: + + mov DWORD [_gaInitPageDir], 0 + + ; Call the kernel + push ebx ; Multiboot Info + push eax ; Multiboot Magic Value + call _kmain + + ; Halt the Machine + cli +.hlt: + hlt + jmp .hlt + +[global _GetEIP] +_GetEIP: + mov eax, [esp] + ret + +[extern _Proc_Clone] +[extern _Proc_Exit] +[global _SpawnTask] +_SpawnTask: + ; Call Proc_Clone with Flags=0 + xor eax, eax + push eax + push eax + call _Proc_Clone + add esp, 8 ; Remove arguments from stack + + test eax, eax + jnz .parent + + ; In child, so now set up stack frame + mov ebx, [esp+4] ; Child Function + mov edx, [esp+8] ; Argument + ; Child + push edx ; Argument + call ebx ; Function + call _Proc_Exit ; Kill Thread + +.parent: + ret + +[section .initpd] +[global _gaInitPageDir] +[global _gaInitPageTable] +align 0x1000 +_gaInitPageDir: + dd _gaInitPageTable-KERNEL_BASE+3 ; 0x00 + times 1024-256-1 dd 0 + dd _gaInitPageTable-KERNEL_BASE+3 ; 0xC0 + times 256-1 dd 0 +align 0x1000 +_gaInitPageTable: + %assign i 0 + %rep 1024 + dd i*0x1000+3 + %assign i i+1 + %endrep +[global _Kernel_Stack_Top] +ALIGN 0x1000 + times 1024 dd 0 +_Kernel_Stack_Top: + diff --git a/Kernel/arch/x86/time.c b/Kernel/arch/x86/time.c new file mode 100644 index 00000000..3a13904f --- /dev/null +++ b/Kernel/arch/x86/time.c @@ -0,0 +1,69 @@ +/* + * Acess2 Kernel + * Timekeeping + * arch/x86/time.c + */ +#include + +// === MACROS === +#define TIMER_FREQ 1024 //Hz +#define MS_PER_TICK_WHOLE (1000/(TIMER_FREQ)) +#define MS_PER_TICK_FRACT ((Uint64)(1000*TIMER_FREQ-((Uint64)MS_PER_TICK_WHOLE)*0x80000000/TIMER_FREQ)) + +// === PROTOTYPES === +void Time_Interrupt(); + +// === GLOBALS === +Uint64 giTicks = 0; +Sint64 giTimestamp = 0; +Uint64 giPartMiliseconds = 0; + +// === CODE === +/** + * \fn int Time_Setup() + * \brief Sets the system time from the Realtime-Clock + */ +int Time_Setup() +{ + Uint8 val; + + outb(0x70, inb(0x70)&0x7F); // Disable NMIs + __asm__ __volatile__ ("cli"); // Disable normal interrupts + + // Enable IRQ8 + outb(0x70, 0x0B); // Set the index to register B + val = inb(0x71); // Read the current value of register B + outb(0x70, 0x0B); // Set the index again (a read will reset the index to register D) + outb(0x71, val | 0x40); // Write the previous value or'd with 0x40. This turns on bit 6 of register D + + __asm__ __volatile__ ("sti"); // Disable normal interrupts + outb(0x70, inb(0x70)|0x80); // Disable NMIs + + // Install IRQ Handler + IRQ_AddHandler(8, Time_Interrupt); + return 0; +} + +/** + * \fn void Time_Interrupt() + * \brief Called on the timekeeping IRQ + */ +void Time_Interrupt() +{ + giTicks ++; + giTimestamp += MS_PER_TICK_WHOLE; + giPartMiliseconds += MS_PER_TICK_FRACT; + if(giPartMiliseconds > 0x80000000) { + giTimestamp ++; + giPartMiliseconds -= 0x80000000; + } +} + +/** + * \fn Sint64 now() + * \brief Return the current timestamp + */ +Sint64 now() +{ + return giTimestamp; +} diff --git a/Kernel/bin/bin_elf.h b/Kernel/bin/bin_elf.h new file mode 100644 index 00000000..fbbfebc2 --- /dev/null +++ b/Kernel/bin/bin_elf.h @@ -0,0 +1,215 @@ +/** + Acess v1 + \file bin_elf.h + \brief ELF Exeutable Loader +*/ +#ifndef _BIN_ELF_H +#define _BIN_ELF_H + +/** + \struct elf_header_s + \brief ELF File Header +*/ +struct sElf32_Ehdr { + union { + char ident[16]; //!< Identifier Bytes + struct { + Uint Ident1; + Uint Ident2; + Uint HashTable; + Uint SymTable; + } misc; + }; + Uint16 filetype; //!< File Type + Uint16 machine; //!< Machine / Arch + Uint32 version; //!< Version (File?) + Uint32 entrypoint; //!< Entry Point + Uint32 phoff; //!< Program Header Offset + Uint32 shoff; //!< Section Header Offset + Uint32 flags; //!< Flags + Uint16 headersize; //!< Header Size + Uint16 phentsize; //!< Program Header Entry Size + Uint16 phentcount; //!< Program Header Entry Count + Uint16 shentsize; //!< Section Header Entry Size + Uint16 shentcount; //!< Section Header Entry Count + Uint16 shstrindex; //!< Section Header String Table Index +}; + +/** + \name Executable Types + \{ +*/ +#define ET_NONE 0 //!< NULL Type +#define ET_REL 1 //!< Relocatable (Object) +#define ET_EXEC 2 //!< Executable +#define ET_DYN 3 //!< Dynamic Library +#define ET_CORE 4 //!< Core? +#define ET_LOPROC 0xFF00 //!< Low Impl Defined +#define ET_HIPROC 0xFFFF //!< High Impl Defined +//! \} + +/** + \name Section IDs + \{ +*/ +#define SHN_UNDEF 0 //!< Undefined Section +#define SHN_LORESERVE 0xFF00 //!< Low Reserved +#define SHN_LOPROC 0xFF00 //!< Low Impl Defined +#define SHN_HIPROC 0xFF1F //!< High Impl Defined +#define SHN_ABS 0xFFF1 //!< Absolute Address (Base: 0, Size: -1) +#define SHN_COMMON 0xFFF2 //!< Common +#define SHN_HIRESERVE 0xFFFF //!< High Reserved +//! \} + +/** + \enum eElfSectionTypes + \brief ELF Section Types +*/ +enum eElfSectionTypes { + SHT_NULL, //0 + SHT_PROGBITS, //1 + SHT_SYMTAB, //2 + SHT_STRTAB, //3 + SHT_RELA, //4 + SHT_HASH, //5 + SHT_DYNAMIC, //6 + SHT_NOTE, //7 + SHT_NOBITS, //8 + SHT_REL, //9 + SHT_SHLIB, //A + SHT_DYNSYM, //B + SHT_LAST, //C + SHT_LOPROC = 0x70000000, + SHT_HIPROC = 0x7fffffff, + SHT_LOUSER = 0x80000000, + SHT_HIUSER = 0xffffffff +}; + +#define SHF_WRITE 0x1 +#define SHF_ALLOC 0x2 +#define SHF_EXECINSTR 0x4 +#define SHF_MASKPROC 0xf0000000 + +struct sElf32_Shent { + Uint32 name; + Uint32 type; + Uint32 flags; + Uint32 address; + Uint32 offset; + Uint32 size; + Uint32 link; + Uint32 info; + Uint32 addralign; + Uint32 entsize; +}; //sizeof = 40 + +struct elf_sym_s { + union { + Uint32 nameOfs; + char *name; + }; + Uint32 value; //Address + Uint32 size; + Uint8 info; + Uint8 other; + Uint16 shndx; +}; +#define STN_UNDEF 0 // Undefined Symbol + +enum { + PT_NULL, //0 + PT_LOAD, //1 + PT_DYNAMIC, //2 + PT_INTERP, //3 + PT_NOTE, //4 + PT_SHLIB, //5 + PT_PHDR, //6 + PT_LOPROC = 0x70000000, + PT_HIPROC = 0x7fffffff +}; + +struct sElf32_Phdr { + Uint32 Type; + Uint Offset; + Uint VAddr; + Uint PAddr; + Uint32 FileSize; + Uint32 MemSize; + Uint32 Flags; + Uint32 Align; +}; + +struct elf32_rel_s { + Uint32 r_offset; + Uint32 r_info; +}; + +struct elf32_rela_s { + Uint32 r_offset; + Uint32 r_info; + Sint32 r_addend; +}; + +enum { + R_386_NONE=0, // none + R_386_32, // S+A + R_386_PC32, // S+A-P + R_386_GOT32, // G+A-P + R_386_PLT32, // L+A-P + R_386_COPY, // none + R_386_GLOB_DAT, // S + R_386_JMP_SLOT, // S + R_386_RELATIVE, // B+A + R_386_GOTOFF, // S+A-GOT + R_386_GOTPC, // GOT+A-P + R_386_LAST // none +}; + +#define ELF32_R_SYM(i) ((i)>>8) // Takes an info value and returns a symbol index +#define ELF32_R_TYPE(i) ((i)&0xFF) // Takes an info value and returns a type +#define ELF32_R_INFO(s,t) (((s)<<8)+((t)&0xFF)) // Takes a type and symbol index and returns an info value + +struct elf32_dyn_s { + Uint16 d_tag; + Uint32 d_val; //Also d_ptr +}; + +enum { + DT_NULL, //!< Marks End of list + DT_NEEDED, //!< Offset in strtab to needed library + DT_PLTRELSZ, //!< Size in bytes of PLT + DT_PLTGOT, //!< Address of PLT/GOT + DT_HASH, //!< Address of symbol hash table + DT_STRTAB, //!< String Table address + DT_SYMTAB, //!< Symbol Table address + DT_RELA, //!< Relocation table address + DT_RELASZ, //!< Size of relocation table + DT_RELAENT, //!< Size of entry in relocation table + DT_STRSZ, //!< Size of string table + DT_SYMENT, //!< Size of symbol table entry + DT_INIT, //!< Address of initialisation function + DT_FINI, //!< Address of termination function + DT_SONAME, //!< String table offset of so name + DT_RPATH, //!< String table offset of library path + DT_SYMBOLIC,//!< Reverse order of symbol searching for library, search libs first then executable + DT_REL, //!< Relocation Entries (Elf32_Rel instead of Elf32_Rela) + DT_RELSZ, //!< Size of above table (bytes) + DT_RELENT, //!< Size of entry in above table + DT_PLTREL, //!< Relocation entry of PLT + DT_DEBUG, //!< Debugging Entry - Unknown contents + DT_TEXTREL, //!< Indicates that modifcations to a non-writeable segment may occur + DT_JMPREL, //!< Address of PLT only relocation entries + DT_LOPROC = 0x70000000, //!< Low Definable + DT_HIPROC = 0x7FFFFFFF //!< High Definable +}; + +typedef struct sElf32_Ehdr Elf32_Ehdr; +typedef struct sElf32_Phdr Elf32_Phdr; +typedef struct sElf32_Shent Elf32_Shent; +typedef struct elf_sym_s elf_symtab; +typedef struct elf_sym_s Elf32_Sym; +typedef struct elf32_rel_s Elf32_Rel; +typedef struct elf32_rela_s Elf32_Rela; +typedef struct elf32_dyn_s Elf32_Dyn; + +#endif // defined(_EXE_ELF_H) diff --git a/Kernel/bin/elf.c b/Kernel/bin/elf.c new file mode 100644 index 00000000..dbd38d2c --- /dev/null +++ b/Kernel/bin/elf.c @@ -0,0 +1,587 @@ +/* +Acess v0.1 +ELF Executable Loader Code +*/ +#include +#include +#include "bin_elf.h" + +#define DEBUG 1 +#define DEBUG_WARN 1 + +#if DEBUG +# define DEBUGS(v...) Log(v) +#else +# define DEBUGS(v...) +# undef ENTER +# undef LOG +# undef LEAVE +# define ENTER(...) +# define LOG(...) +# define LEAVE(...) +#endif + + +// === PROTOTYPES === +tBinary *Elf_Load(int fp); + int Elf_Relocate(void *Base); + int Elf_GetSymbol(void *Base, char *Name, Uint *ret); + int Elf_Int_DoRelocate(Uint r_info, Uint32 *ptr, Uint32 addend, Elf32_Sym *symtab, Uint base); +Uint Elf_Int_HashString(char *str); + +// === GLOBALS === +tBinaryType gELF_Info = { + NULL, + 0x464C457F, 0xFFFFFFFF, // '\x7FELF' + "ELF", + Elf_Load, Elf_Relocate, Elf_GetSymbol + }; + +// === CODE === +tBinary *Elf_Load(int fp) +{ + tBinary *ret; + Elf32_Ehdr hdr; + Elf32_Phdr *phtab; + int i, j, k; + int iPageCount; + int count; + + ENTER("ifp", fp); + + // Read ELF Header + VFS_Read(fp, sizeof(hdr), &hdr); + + // Check the file type + if(hdr.ident[0] != 0x7F || hdr.ident[1] != 'E' || hdr.ident[2] != 'L' || hdr.ident[3] != 'F') { + Warning("Non-ELF File was passed to the ELF loader\n"); + LEAVE('n'); + return NULL; + } + + // Check for a program header + if(hdr.phoff == 0) { + #if DEBUG_WARN + Warning("ELF File does not contain a program header\n"); + #endif + LEAVE('n'); + return NULL; + } + + // Read Program Header Table + phtab = malloc(sizeof(Elf32_Phdr)*hdr.phentcount); + VFS_Seek(fp, hdr.phoff, SEEK_SET); + VFS_Read(fp, sizeof(Elf32_Phdr)*hdr.phentcount, phtab); + + // Count Pages + iPageCount = 0; + LOG("hdr.phentcount = %i", hdr.phentcount); + for( i = 0; i < hdr.phentcount; i++ ) + { + // Ignore Non-LOAD types + if(phtab[i].Type != PT_LOAD) + continue; + iPageCount += ((phtab[i].VAddr&0xFFF) + phtab[i].MemSize + 0xFFF) >> 12; + LOG("phtab[%i] = {VAddr:0x%x, MemSize:0x%x}", i, phtab[i].VAddr, phtab[i].MemSize); + } + + LOG("iPageCount = %i", iPageCount); + + // Allocate Information Structure + ret = malloc( sizeof(tBinary) + 3*sizeof(Uint)*iPageCount ); + // Fill Info Struct + ret->Entry = hdr.entrypoint; + ret->Base = -1; // Set Base to maximum value + ret->NumPages = iPageCount; + ret->Interpreter = NULL; + + // Load Pages + j = 0; + for( i = 0; i < hdr.phentcount; i++ ) + { + int lastSize; + LOG("phtab[%i].Type = 0x%x", i, phtab[i].Type); + // Get Interpreter Name + if( phtab[i].Type == PT_INTERP ) + { + char *tmp; + if(ret->Interpreter) continue; + tmp = malloc(phtab[i].FileSize); + VFS_Seek(fp, phtab[i].Offset, 1); + VFS_Read(fp, phtab[i].FileSize, tmp); + ret->Interpreter = Binary_RegInterp(tmp); + LOG("Interpreter '%s'", tmp); + free(tmp); + continue; + } + // Ignore non-LOAD types + if(phtab[i].Type != PT_LOAD) continue; + + // Find Base + if(phtab[i].VAddr < ret->Base) ret->Base = phtab[i].VAddr; + + k = 0; + + LOG("phtab[%i] = {VAddr:0x%x,Offset:0x%x,FileSize:0x%x}", + i, phtab[i].VAddr, phtab[i].Offset, phtab[i].FileSize); + + if( (phtab[i].FileSize & 0xFFF) < 0x1000 - (phtab[i].VAddr & 0xFFF) ) + lastSize = phtab[i].FileSize; + else + lastSize = (phtab[i].FileSize & 0xFFF) + (phtab[i].VAddr & 0xFFF); + lastSize &= 0xFFF; + + LOG("lastSize = 0x%x", lastSize); + + // Get Pages + count = ( (phtab[i].VAddr&0xFFF) + phtab[i].FileSize + 0xFFF) >> 12; + for(;kPages[j+k].Virtual = phtab[i].VAddr + (k<<12); + ret->Pages[j+k].Physical = phtab[i].Offset + (k<<12); // Store the offset in the physical address + if(k != 0) { + ret->Pages[j+k].Physical -= ret->Pages[j+k].Virtual&0xFFF; + ret->Pages[j+k].Virtual &= ~0xFFF; + } + if(k == count-1) + ret->Pages[j+k].Size = lastSize; // Byte count in page + else if(k == 0) + ret->Pages[j+k].Size = 4096 - (phtab[i].VAddr&0xFFF); + else + ret->Pages[j+k].Size = 4096; + LOG("ret->Pages[%i].Size = 0x%x", j+k, ret->Pages[j+k].Size); + ret->Pages[j+k].Flags = 0; + } + count = (phtab[i].MemSize + 0xFFF) >> 12; + for(;kPages[j+k].Virtual = phtab[i].VAddr + (k<<12); + ret->Pages[j+k].Physical = -1; // -1 = Fill with zeros + if(k != 0) ret->Pages[j+k].Virtual &= ~0xFFF; + if(k == count-1 && (phtab[i].MemSize & 0xFFF)) + ret->Pages[j+k].Size = phtab[i].MemSize & 0xFFF; // Byte count in page + else + ret->Pages[j+k].Size = 4096; + ret->Pages[j+k].Flags = 0; + LOG("%i - 0x%x => 0x%x - 0x%x", j+k, + ret->Pages[j+k].Physical, ret->Pages[j+k].Virtual, ret->Pages[j+k].Size); + } + j += count; + } + + #if 0 + LOG("Cleaning up overlaps"); + // Clear up Overlaps + { + struct { + Uint V; + Uint P; + Uint S; + Uint F; + } *tmpRgns; + count = j; + tmpRgns = malloc(sizeof(*tmpRgns)*count); + // Copy + for(i=0;iPages[i].Virtual; + tmpRgns[i].P = ret->Pages[i].Physical; + tmpRgns[i].S = ret->Pages[i].Size; + tmpRgns[i].F = ret->Pages[i].Flags; + } + // Compact + for(i=1,j=0; i < count; i++) + { + if( tmpRgns[j].F == tmpRgns[i].F + && tmpRgns[j].V + tmpRgns[j].S == tmpRgns[i].V + && ((tmpRgns[j].P == -1 && tmpRgns[i].P == -1) + || (tmpRgns[j].P + tmpRgns[j].S == tmpRgns[i].P)) ) + { + tmpRgns[j].S += tmpRgns[i].S; + } else { + j ++; + tmpRgns[j].V = tmpRgns[i].V; + tmpRgns[j].P = tmpRgns[i].P; + tmpRgns[j].F = tmpRgns[i].F; + tmpRgns[j].S = tmpRgns[i].S; + } + } + j ++; + // Count + count = j; j = 0; + for(i=0;i 0x%x - 0x%x\n", i, tmpRgns[i].P, tmpRgns[i].V, tmpRgns[i].S); + tmpRgns[i].S += tmpRgns[i].V & 0xFFF; + if(tmpRgns[i].P != -1) tmpRgns[i].P -= tmpRgns[i].V & 0xFFF; + tmpRgns[i].V &= ~0xFFF; + j += (tmpRgns[i].S + 0xFFF) >> 12; + //LogF(" Elf_Load: %i - 0x%x => 0x%x - 0x%x\n", i, tmpRgns[i].P, tmpRgns[i].V, tmpRgns[i].S); + } + // Reallocate + ret = realloc( ret, sizeof(tBinary) + 3*sizeof(Uint)*j ); + if(!ret) { + Warning("BIN", "ElfLoad: Unable to reallocate return structure"); + return NULL; + } + ret->NumPages = j; + // Split + k = 0; + for(i=0;i> 12; j++,k++ ) { + ret->Pages[k].Flags = tmpRgns[i].F; + ret->Pages[k].Virtual = tmpRgns[i].V + (j<<12); + if(tmpRgns[i].P != -1) { + ret->Pages[k].Physical = tmpRgns[i].P + (j<<12); + } else + ret->Pages[k].Physical = -1; + ret->Pages[k].Size = tmpRgns[i].S - (j << 12); + // Clamp to page size + if(ret->Pages[k].Size > 0x1000) ret->Pages[k].Size = 0x1000; + } + } + // Free Temp + free(tmpRgns); + } + #endif + + // Clean Up + free(phtab); + // Return + LEAVE('p', ret); + return ret; +} + +// --- ELF RELOCATION --- +// Taken from 'ld-acess.so' +/** + \fn int Elf_Relocate(void *Base) + \brief Relocates a loaded ELF Executable +*/ +int Elf_Relocate(void *Base) +{ + Elf32_Ehdr *hdr = Base; + Elf32_Phdr *phtab; + int i, j; // Counters + char *libPath; + Uint iRealBase = -1; + Uint iBaseDiff; + int iSegmentCount; + int iSymCount = 0; + Elf32_Rel *rel = NULL; + Elf32_Rela *rela = NULL; + Uint32 *pltgot = NULL; + void *plt = NULL; + Uint32 *ptr; + int relSz=0, relEntSz=8; + int relaSz=0, relaEntSz=8; + int pltSz=0, pltType=0; + Elf32_Dyn *dynamicTab = NULL; // Dynamic Table Pointer + char *dynstrtab = NULL; // .dynamic String Table + Elf32_Sym *dynsymtab = NULL; + + ENTER("pBase", Base); + + // Parse Program Header to get Dynamic Table + phtab = Base + hdr->phoff; + iSegmentCount = hdr->phentcount; + for(i=0;i phtab[i].VAddr) + iRealBase = phtab[i].VAddr; + + // Find Dynamic Section + if(phtab[i].Type == PT_DYNAMIC) { + if(dynamicTab) { + Warning("ELF", "Elf_Relocate - Multiple PT_DYNAMIC segments\n"); + continue; + } + dynamicTab = (void *) phtab[i].VAddr; + j = i; // Save Dynamic Table ID + break; + } + } + + // Check if a PT_DYNAMIC segement was found + if(!dynamicTab) { + Warning("ELF", "Elf_Relocate: No PT_DYNAMIC segment in image, returning\n"); + LEAVE('x', hdr->entrypoint); + return hdr->entrypoint; + } + + // Page Align real base + iRealBase &= ~0xFFF; + + // Adjust "Real" Base + iBaseDiff = (Uint)Base - iRealBase; + // Adjust Dynamic Table + dynamicTab = (void *) ((Uint)dynamicTab + iBaseDiff); + + // === Get Symbol table and String Table === + for( j = 0; dynamicTab[j].d_tag != DT_NULL; j++) + { + switch(dynamicTab[j].d_tag) + { + // --- Symbol Table --- + case DT_SYMTAB: + dynamicTab[j].d_val += iBaseDiff; + dynsymtab = (void*)(dynamicTab[j].d_val); + hdr->misc.SymTable = dynamicTab[j].d_val; // Saved in unused bytes of ident + break; + + // --- String Table --- + case DT_STRTAB: + dynamicTab[j].d_val += iBaseDiff; + dynstrtab = (void*)(dynamicTab[j].d_val); + break; + + // --- Hash Table -- + case DT_HASH: + dynamicTab[j].d_val += iBaseDiff; + iSymCount = ((Uint*)(dynamicTab[j].d_val))[1]; + hdr->misc.HashTable = dynamicTab[j].d_val; // Saved in unused bytes of ident + break; + } + } + + + // Alter Symbols to true base + for(i=0;ientrypoint); + return hdr->entrypoint; +} + +/** + * \fn void Elf_Int_DoRelocate(Uint r_info, Uint32 *ptr, Uint32 addend, Elf32_Sym *symtab, Uint base) + * \brief Performs a relocation + * \param r_info Field from relocation entry + * \param ptr Pointer to location of relocation + * \param addend Value to add to symbol + * \param symtab Symbol Table + * \param base Base of loaded binary + */ +int Elf_Int_DoRelocate(Uint r_info, Uint32 *ptr, Uint32 addend, Elf32_Sym *symtab, Uint base) +{ + Uint val; + int type = ELF32_R_TYPE(r_info); + int sym = ELF32_R_SYM(r_info); + char *sSymName = symtab[sym].name; + + //LogF("Elf_Int_DoRelocate: (r_info=0x%x, ptr=0x%x, addend=0x%x, .., base=0x%x)\n", + // r_info, ptr, addend, base); + + switch( type ) + { + // Standard 32 Bit Relocation (S+A) + case R_386_32: + if( !Elf_GetSymbol((void*)base, sSymName, &val) ) // Search this binary first + if( !Binary_GetSymbol( sSymName, &val ) ) + return 0; + //LOG("R_386_32 *0x%x += 0x%x('%s')", ptr, val, sSymName); + *ptr = val + addend; + break; + + // 32 Bit Relocation wrt. Offset (S+A-P) + case R_386_PC32: + if( !Elf_GetSymbol( (void*)base, sSymName, &val ) ) + if( !Binary_GetSymbol( sSymName, &val ) ) + return 0; + //LOG("R_386_PC32 *0x%x = 0x%x + 0x%x('%s') - 0x%x", ptr, *ptr, val, sSymName, (Uint)ptr ); + // TODO: Check if it needs the true value of ptr or the compiled value + // NOTE: Testing using true value + *ptr = val + addend - (Uint)ptr; + break; + + // Absolute Value of a symbol (S) + case R_386_GLOB_DAT: + if( !Elf_GetSymbol( (void*)base, sSymName, &val ) ) + if( !Binary_GetSymbol( sSymName, &val ) ) + return 0; + //LOG("R_386_GLOB_DAT *0x%x = 0x%x (%s)", ptr, val, sSymName); + *ptr = val; + break; + + // Absolute Value of a symbol (S) + case R_386_JMP_SLOT: + if( !Elf_GetSymbol( (void*)base, sSymName, &val ) ) + if( !Binary_GetSymbol( sSymName, &val ) ) + return 0; + //LOG("R_386_JMP_SLOT *0x%x = 0x%x (%s)", ptr, val, sSymName); + *ptr = val; + break; + + // Base Address (B+A) + case R_386_RELATIVE: + //LOG("R_386_RELATIVE *0x%x = 0x%x + 0x%x", ptr, base, addend); + *ptr = base + addend; + break; + + default: + LOG("Rel 0x%x: 0x%x,%i", ptr, sym, type); + break; + } + return 1; +} + +/** + * \fn int Elf_GetSymbol(void *Base, char *name, Uint *ret) + * \brief Get a symbol from the loaded binary + */ +int Elf_GetSymbol(void *Base, char *Name, Uint *ret) +{ + Elf32_Ehdr *hdr = (void*)Base; + Elf32_Sym *symtab; + int nbuckets = 0; + int iSymCount = 0; + int i; + Uint *pBuckets; + Uint *pChains; + Uint iNameHash; + + if(!Base) return 0; + + pBuckets = (void *) hdr->misc.HashTable; + symtab = (void *) hdr->misc.SymTable; + + nbuckets = pBuckets[0]; + iSymCount = pBuckets[1]; + pBuckets = &pBuckets[2]; + pChains = &pBuckets[ nbuckets ]; + + // Get hash + iNameHash = Elf_Int_HashString(Name); + iNameHash %= nbuckets; + + // Check Bucket + i = pBuckets[ iNameHash ]; + if(symtab[i].shndx != SHN_UNDEF && strcmp(symtab[i].name, Name) == 0) { + *ret = symtab[ i ].value; + return 1; + } + + // Walk Chain + while(pChains[i] != STN_UNDEF) + { + i = pChains[i]; + if(symtab[i].shndx != SHN_UNDEF && strcmp(symtab[ i ].name, Name) == 0) { + *ret = symtab[ i ].value; + return 1; + } + } + return 0; +} + +/** + * \fn Uint Elf_Int_HashString(char *str) + * \brief Hash a string in the ELF format + * \param str String to hash + * \return Hash value + */ +Uint Elf_Int_HashString(char *str) +{ + Uint h = 0, g; + while(*str) + { + h = (h << 4) + *str++; + if( (g = h & 0xf0000000) ) + h ^= g >> 24; + h &= ~g; + } + return h; +} diff --git a/Kernel/binary.c b/Kernel/binary.c new file mode 100644 index 00000000..7175799b --- /dev/null +++ b/Kernel/binary.c @@ -0,0 +1,813 @@ +/* + * Acess2 + * Common Binary Loader + */ +#include +#include + +#define DEBUG 1 + +#if DEBUG +#else +# undef ENTER +# undef LOG +# undef LEAVE +# define ENTER(...) +# define LOG(...) +# define LEAVE(...) +#endif + +// === CONSTANTS === +#define BIN_LOWEST MM_USER_MIN // 1MiB +#define BIN_GRANUALITY 0x10000 // 64KiB +#define BIN_HIGHEST (0xBC000000-BIN_GRANUALITY) // Just below the kernel +#define KLIB_LOWEST MM_MODULE_MIN +#define KLIB_GRANUALITY 0x8000 // 32KiB +#define KLIB_HIGHEST (MM_MODULE_MAX-KLIB_GRANUALITY) + +// === TYPES === +typedef struct sKernelBin { + struct sKernelBin *Next; + void *Base; + tBinary *Info; +} tKernelBin; + +// === IMPORTS === +extern int Proc_Clone(Uint *Err, Uint Flags); +extern void Proc_SetThreadName(char *Name); +extern Uint MM_ClearUser(); +extern void Proc_Exit(); +extern void Proc_StartUser(Uint Entrypoint, Uint *Bases, int ArgC, char **ArgV, char **EnvP, int DataSize); +extern tKernelSymbol gKernelSymbols[]; +extern void gKernelSymbolsEnd; +extern tBinaryType gELF_Info; + +// === PROTOTYPES === + int Proc_Execve(char *File, char **ArgV, char **EnvP); +Uint Binary_Load(char *file, Uint *entryPoint); +tBinary *Binary_GetInfo(char *truePath); +Uint Binary_MapIn(tBinary *binary); +Uint Binary_IsMapped(tBinary *binary); +tBinary *Binary_DoLoad(char *truePath); +void Binary_Dereference(tBinary *Info); +Uint Binary_Relocate(void *Base); +Uint Binary_GetSymbolEx(char *Name, Uint *Value); +Uint Binary_FindSymbol(void *Base, char *Name, Uint *val); + +// === GLOBALS === + int glBinListLock = 0; +tBinary *glLoadedBinaries = NULL; +char **gsaRegInterps = NULL; + int giRegInterps = 0; + int glKBinListLock = 0; +tKernelBin *glLoadedKernelLibs; +tBinaryType *gRegBinTypes = &gELF_Info; + +// === FUNCTIONS === +/** + * \fn int Proc_Spawn(char *Path) + */ +int Proc_Spawn(char *Path) +{ + char stackPath[strlen(Path)+1]; + + strcpy(stackPath, Path); + + LOG("stackPath = '%s'\n", stackPath); + + if(Proc_Clone(NULL, CLONE_VM) == 0) + { + // CHILD + char *args[2] = {stackPath, NULL}; + LOG("stackPath = '%s'\n", stackPath); + Proc_Execve(stackPath, args, &args[1]); + for(;;); + } + return 0; +} + +/** + * \fn int Proc_Execve(char *File, char **ArgV, char **EnvP) + * \brief Replace the current user image with another + * \param File File to load as the next image + * \param ArgV Arguments to pass to user + * \param EnvP User's environment + * \note Called Proc_ for historical reasons + */ +int Proc_Execve(char *File, char **ArgV, char **EnvP) +{ + int argc, envc, i; + int argenvBytes; + char *argenvBuf, *strBuf; + char **argvSaved, **envpSaved; + char *savedFile; + Uint entry; + Uint bases[2] = {0}; + + ENTER("sFile pArgV pEnvP", File, ArgV, EnvP); + + // --- Save File, ArgV and EnvP (also get argc) + + // Count Arguments, Environment Variables and total string sizes + argenvBytes = 0; + for( argc = 0; ArgV && ArgV[argc]; argc++ ) + argenvBytes += strlen(ArgV[argc])+1; + for( envc = 0; EnvP && EnvP[envc]; envc++ ) + argenvBytes += strlen(EnvP[envc])+1; + argenvBytes = (argenvBytes + sizeof(void*)-1) & ~(sizeof(void*)-1); + argenvBytes += (argc+1)*sizeof(void*) + (envc+1)*sizeof(void*); + + // Allocate + argenvBuf = malloc(argenvBytes); + if(argenvBuf == NULL) { + Warning("Proc_Execve - What the hell? The kernel is out of heap space"); + return 0; + } + strBuf = argenvBuf + (argc+1)*sizeof(void*) + (envc+1)*sizeof(void*); + + // Populate + argvSaved = (char **) argenvBuf; + for( i = 0; i < argc; i++ ) + { + argvSaved[i] = strBuf; + strcpy(argvSaved[i], ArgV[i]); + strBuf += strlen(ArgV[i])+1; + } + argvSaved[i] = NULL; + envpSaved = &argvSaved[i+1]; + for( i = 0; i < envc; i++ ) + { + envpSaved[i] = strBuf; + strcpy(envpSaved[i], EnvP[i]); + strBuf += strlen(EnvP[i])+1; + } + + savedFile = malloc(strlen(File)+1); + strcpy(savedFile, File); + + // --- Set Process Name + Proc_SetThreadName(File); + + // --- Clear User Address space + MM_ClearUser(); + + // --- Load new binary + bases[0] = Binary_Load(savedFile, &entry); + free(savedFile); + if(bases[0] == 0) + { + Warning("Proc_Execve - Unable to load '%s'", File); + Proc_Exit(); + for(;;); + } + + LOG("entry = 0x%x, bases[0] = 0x%x", entry, bases[0]); + LEAVE('-'); + // --- And... Jump to it + Proc_StartUser(entry, bases, argc, argvSaved, envpSaved, argenvBytes); + for(;;); // Tell GCC that we never return +} + +/** + * \fn Uint Binary_Load(char *file, Uint *entryPoint) + */ +Uint Binary_Load(char *file, Uint *entryPoint) +{ + char *sTruePath; + tBinary *pBinary; + Uint base = -1; + + ENTER("sfile", file); + + // Sanity Check Argument + if(file == NULL) { + LEAVE('x', 0); + return 0; + } + + // Get True File Path + sTruePath = VFS_GetTruePath(file); + + if(sTruePath == NULL) { + Warning("[BIN ] '%s' does not exist.", file); + LEAVE('x', 0); + return 0; + } + + LOG("sTruePath = '%s'", sTruePath); + + // Check if the binary has already been loaded + if( !(pBinary = Binary_GetInfo(sTruePath)) ) + pBinary = Binary_DoLoad(sTruePath); // Else load it + + // Clean Up + free(sTruePath); + + // Error Check + if(pBinary == NULL) { + LEAVE('x', 0); + return 0; + } + + #if 0 + if( (base = Binary_IsMapped(pBinary)) ) { + LEAVE('x', base); + return base; + } + #endif + + // Map into process space + base = Binary_MapIn(pBinary); // If so then map it in + + // Check for errors + if(base == 0) { + LEAVE('x', 0); + return 0; + } + + // Interpret + if(pBinary->Interpreter) { + Uint start; + if( Binary_Load(pBinary->Interpreter, &start) == 0 ) { + LEAVE('x', 0); + return 0; + } + *entryPoint = start; + } + else + *entryPoint = pBinary->Entry - pBinary->Base + base; + + // Return + LOG("*entryPoint = 0x%x", *entryPoint); + LEAVE('x', base); + return base; // Pass the base as an argument to the user if there is an interpreter +} + +/** + \fn tBinary *Binary_GetInfo(char *truePath) + \brief Finds a matching binary entry + \param truePath File Identifier (True path name) +*/ +tBinary *Binary_GetInfo(char *truePath) +{ + tBinary *pBinary; + pBinary = glLoadedBinaries; + while(pBinary) + { + if(strcmp(pBinary->TruePath, truePath) == 0) + return pBinary; + pBinary = pBinary->Next; + } + return NULL; +} + +/** + \fn Uint Binary_MapIn(tBinary *binary) + \brief Maps an already-loaded binary into an address space. + \param binary Pointer to globally stored data. +*/ +Uint Binary_MapIn(tBinary *binary) +{ + Uint base; + Uint addr; + int i; + + // Reference Executable (Makes sure that it isn't unloaded) + binary->ReferenceCount ++; + + // Get Binary Base + base = binary->Base; + + // Check if base is free + if(base != 0) + { + for(i=0;iNumPages;i++) + { + if( MM_GetPhysAddr( binary->Pages[i].Virtual & ~0xFFF ) ) { + base = 0; + LOG("Address 0x%x is taken\n", binary->Pages[i].Virtual & ~0xFFF); + break; + } + } + } + + // Check if the executable has no base or it is not free + if(base == 0) + { + // If so, give it a base + base = BIN_HIGHEST; + while(base >= BIN_LOWEST) + { + for(i=0;iNumPages;i++) + { + addr = binary->Pages[i].Virtual & ~0xFFF; + addr -= binary->Base; + addr += base; + if( MM_GetPhysAddr( addr ) ) break; + } + // If space was found, break + if(i == binary->NumPages) break; + // Else decrement pointer and try again + base -= BIN_GRANUALITY; + } + } + + // Error Check + if(base < BIN_LOWEST) { + Warning("[BIN ] Executable '%s' cannot be loaded, no space", binary->TruePath); + return 0; + } + + // Map Executable In + for(i=0;iNumPages;i++) + { + addr = binary->Pages[i].Virtual & ~0xFFF; + addr -= binary->Base; + addr += base; + LOG("%i - 0x%x to 0x%x", i, addr, binary->Pages[i].Physical); + MM_Map( addr, (Uint) (binary->Pages[i].Physical) ); + if( binary->Pages[i].Physical & 1) // Read-Only + MM_SetFlags( addr, MM_PFLAG_RO, -1 ); + else + MM_SetFlags( addr, MM_PFLAG_COW, -1 ); + } + + //LOG("*0x%x = 0x%x\n", binary->Pages[0].Virtual, *(Uint*)binary->Pages[0].Virtual); + + return base; +} + +#if 0 +/** + * \fn Uint Binary_IsMapped(tBinary *binary) + * \brief Check if a binary is already mapped into the address space + * \param binary Binary information to check + * \return Current Base or 0 + */ +Uint Binary_IsMapped(tBinary *binary) +{ + Uint iBase; + + // Check prefered base + iBase = binary->Base; + if(MM_GetPage( iBase ) == (binary->Pages[0].Physical & ~0xFFF)) + return iBase; + + for(iBase = BIN_HIGHEST; + iBase >= BIN_LOWEST; + iBase -= BIN_GRANUALITY) + { + if(MM_GetPage( iBase ) == (binary->Pages[0].Physical & ~0xFFF)) + return iBase; + } + + return 0; +} +#endif + +/** + * \fn tBinary *Binary_DoLoad(char *truePath) + * \brief Loads a binary file into memory + * \param truePath Absolute filename of binary + */ +tBinary *Binary_DoLoad(char *truePath) +{ + tBinary *pBinary; + int fp, i; + Uint ident; + tBinaryType *bt = gRegBinTypes; + + ENTER("struePath", truePath); + + // Open File + fp = VFS_Open(truePath, VFS_OPENFLAG_READ); + if(fp == -1) { + LOG("Unable to load file, access denied"); + LEAVE('n'); + return NULL; + } + + // Read File Type + VFS_Read(fp, 4, &ident); + VFS_Seek(fp, 0, SEEK_SET); + + for(; bt; bt = bt->Next) + { + if( (ident & bt->Mask) != (Uint)bt->Ident ) + continue; + pBinary = bt->Load(fp); + break; + } + if(!bt) { + Warning("[BIN ] '%s' is an unknown file type. (0x%x 0x%x 0x%x 0x%x)", + truePath, ident&0xFF, (ident>>8)&0xFF, (ident>>16)&0xFF, (ident>>24)&0xFF); + LEAVE('n'); + return NULL; + } + + // Error Check + if(pBinary == NULL) { + LEAVE('n'); + return NULL; + } + + // Initialise Structure + pBinary->ReferenceCount = 0; + pBinary->TruePath = malloc( strlen(truePath) + 1 ); + strcpy(pBinary->TruePath, truePath); + + // Debug Information + LOG("Interpreter: '%s'", pBinary->Interpreter); + LOG("Base: 0x%x, Entry: 0x%x", pBinary->Base, pBinary->Entry); + LOG("NumPages: %i", pBinary->NumPages); + + // Read Data + for(i=0;iNumPages;i++) + { + Uint dest; + tPAddr paddr; + paddr = (Uint)MM_AllocPhys(); + MM_RefPhys( paddr ); // Make sure it is _NOT_ freed until we want it to be + dest = MM_MapTemp( paddr ); + dest += pBinary->Pages[i].Virtual & 0xFFF; + LOG("dest = 0x%x, paddr = 0x%x", dest, paddr); + LOG("Pages[%i]={Physical:0x%x,Virtual:0x%x,Size:0x%x}", + i, pBinary->Pages[i].Physical, pBinary->Pages[i].Virtual, pBinary->Pages[i].Size); + + // Pure Empty Page + if(pBinary->Pages[i].Physical == -1) { + LOG("%i - ZERO", i); + memsetd( (void*)dest, 0, 1024 ); + } + else + { + VFS_Seek( fp, pBinary->Pages[i].Physical, 1 ); + if(pBinary->Pages[i].Size != 0x1000) { + LOG("%i - 0x%x - 0x%x bytes", + i, pBinary->Pages[i].Physical, pBinary->Pages[i].Size); + memset( (void*)dest, 0, 0x1000 -(dest&0xFFF) ); + VFS_Read( fp, pBinary->Pages[i].Size, (void*)dest ); + } else { + LOG("%i - 0x%x", i, pBinary->Pages[i].Physical); + VFS_Read( fp, 0x1000, (void*)dest ); + } + } + pBinary->Pages[i].Physical = paddr; + MM_FreeTemp( dest ); + } + LOG("Page Count: %i", pBinary->NumPages); + + // Close File + VFS_Close(fp); + + // Add to the list + LOCK(&glBinListLock); + pBinary->Next = glLoadedBinaries; + glLoadedBinaries = pBinary; + RELEASE(&glBinListLock); + + // Return + LEAVE('p', pBinary); + return pBinary; +} + +/** + * \fn void Binary_Unload(void *Base) + * \brief Unload / Unmap a binary + * \param Base Loaded Base + * \note Currently used only for kernel libaries + */ +void Binary_Unload(void *Base) +{ + tKernelBin *pKBin; + tKernelBin *prev = NULL; + int i; + + if((Uint)Base < 0xC0000000) + { + // TODO: User Binaries + Warning("[BIN ] Unloading user binaries is currently unimplemented"); + return; + } + + // Kernel Libraries + for(pKBin = glLoadedKernelLibs; + pKBin; + prev = pKBin, pKBin = pKBin->Next) + { + // Check the base + if(pKBin->Base != Base) continue; + // Deallocate Memory + for(i = 0; i < pKBin->Info->NumPages; i++) { + MM_Deallocate( (Uint)Base + (i << 12) ); + } + // Dereference Binary + Binary_Dereference( pKBin->Info ); + // Remove from list + if(prev) prev->Next = pKBin->Next; + else glLoadedKernelLibs = pKBin->Next; + // Free Kernel Lib + free(pKBin); + return; + } +} + +/** + * \fn void Binary_Dereference(tBinary *Info) + * \brief Dereferences and if nessasary, deletes a binary + * \param Info Binary information structure + */ +void Binary_Dereference(tBinary *Info) +{ + // Decrement reference count + Info->ReferenceCount --; + + // Check if it is still in use + if(Info->ReferenceCount) return; + + /// \todo Implement binary freeing +} + +/** + \fn char *Binary_RegInterp(char *path) + \brief Registers an Interpreter + \param path Path to interpreter provided by executable +*/ +char *Binary_RegInterp(char *path) +{ + int i; + // NULL Check Argument + if(path == NULL) return NULL; + // NULL Check the array + if(gsaRegInterps == NULL) + { + giRegInterps = 1; + gsaRegInterps = malloc( sizeof(char*) ); + gsaRegInterps[0] = malloc( strlen(path) ); + strcpy(gsaRegInterps[0], path); + return gsaRegInterps[0]; + } + + // Scan Array + for( i = 0; i < giRegInterps; i++ ) + { + if(strcmp(gsaRegInterps[i], path) == 0) + return gsaRegInterps[i]; + } + + // Interpreter is not in list + giRegInterps ++; + gsaRegInterps = malloc( sizeof(char*)*giRegInterps ); + gsaRegInterps[i] = malloc( strlen(path) ); + strcpy(gsaRegInterps[i], path); + return gsaRegInterps[i]; +} + +// ============ +// Kernel Binary Handling +// ============ +/** + * \fn void *Binary_LoadKernel(char *path) + * \brief Load a binary into kernel space + * \note This function shares much with #Binary_Load, but does it's own mapping + */ +void *Binary_LoadKernel(char *file) +{ + char *sTruePath; + tBinary *pBinary; + tKernelBin *pKBinary; + Uint base = -1; + Uint addr; + int i; + + ENTER("sfile", file); + + // Sanity Check Argument + if(file == NULL) { + LEAVE('n'); + return 0; + } + + // Get True File Path + sTruePath = VFS_GetTruePath(file); + if(sTruePath == NULL) { + LEAVE('n'); + return 0; + } + + // Check if the binary has already been loaded + if( (pBinary = Binary_GetInfo(sTruePath)) ) + { + for(pKBinary = glLoadedKernelLibs; + pKBinary; + pKBinary = pKBinary->Next ) + { + if(pKBinary->Info == pBinary) { + LEAVE('p', pKBinary->Base); + return pKBinary->Base; + } + } + } + else + pBinary = Binary_DoLoad(sTruePath); // Else load it + + // Error Check + if(pBinary == NULL) { + LEAVE('n'); + return NULL; + } + + // -------------- + // Now pBinary is valid (either freshly loaded or only user mapped) + // So, map it into kernel space + // -------------- + + // Reference Executable (Makes sure that it isn't unloaded) + pBinary->ReferenceCount ++; + + // Check compiled base + base = pBinary->Base; + // - Sanity Check + if(base < KLIB_LOWEST || base > KLIB_HIGHEST || base + (pBinary->NumPages<<12) > KLIB_HIGHEST) { + base = 0; + } + // - Check if it is a valid base address + if(base != 0) + { + for(i=0;iNumPages;i++) + { + if( MM_GetPhysAddr( pBinary->Pages[i].Virtual & ~0xFFF ) ) { + base = 0; + LOG("Address 0x%x is taken\n", pBinary->Pages[i].Virtual & ~0xFFF); + break; + } + } + } + + // Check if the executable has no base or it is not free + if(base == 0) + { + // If so, give it a base + base = KLIB_LOWEST; + while(base < KLIB_HIGHEST) + { + for(i = 0; i < pBinary->NumPages; i++) + { + addr = pBinary->Pages[i].Virtual & ~0xFFF; + addr -= pBinary->Base; + addr += base; + if( MM_GetPhysAddr( addr ) ) break; + } + // If space was found, break + if(i == pBinary->NumPages) break; + // Else decrement pointer and try again + base += KLIB_GRANUALITY; + } + } + + // - Error Check + if(base >= KLIB_HIGHEST) { + Warning("[BIN ] Executable '%s' cannot be loaded into kernel, no space", pBinary->TruePath); + Binary_Dereference( pBinary ); + LEAVE('n'); + return 0; + } + + LOG("base = 0x%x", base); + + // - Map binary in + LOG("pBinary = {NumPages:%i, Pages=%p}", pBinary->NumPages, pBinary->Pages); + for(i = 0; i < pBinary->NumPages; i++) + { + addr = pBinary->Pages[i].Virtual & ~0xFFF; + addr -= pBinary->Base; + addr += base; + LOG("%i - 0x%x to 0x%x", i, addr, pBinary->Pages[i].Physical); + MM_Map( addr, (Uint) (pBinary->Pages[i].Physical) ); + MM_SetFlags( addr, MM_PFLAG_KERNEL, MM_PFLAG_KERNEL ); + #if 0 // Why was this here? It's the kernel + if( pBinary->Pages[i].Physical & 1) // Read-Only + MM_SetFlags( addr, MM_PFLAG_RO, MM_PFLAG_KERNEL ); + else + MM_SetFlags( addr, MM_PFLAG_COW, MM_PFLAG_KERNEL ); + //MM_SetCOW( addr ); + #endif + } + + // Relocate Library + if( !Binary_Relocate( (void*)base ) ) + { + Warning("[BIN ] Relocation of '%s' failed, unloading", sTruePath); + Binary_Unload( (void*)base ); + Binary_Dereference( pBinary ); + LEAVE('n'); + return 0; + } + + // Add to list (relocator must look at itself manually, not via Binary_GetSymbol) + pKBinary = malloc(sizeof(*pKBinary)); + pKBinary->Base = (void*)base; + pKBinary->Info = pBinary; + LOCK( &glKBinListLock ); + pKBinary->Next = glLoadedKernelLibs; + glLoadedKernelLibs = pKBinary; + RELEASE( &glKBinListLock ); + + LEAVE('p', base); + return (void*)base; +} + +/** + * \fn Uint Binary_Relocate(void *Base) + * \brief Relocates a loaded binary (used by kernel libraries) + * \param Base Loaded base address of binary + * \return Boolean Success + */ +Uint Binary_Relocate(void *Base) +{ + Uint32 ident = *(Uint32*) Base; + tBinaryType *bt = gRegBinTypes; + + for(; bt; bt = bt->Next) + { + if( (ident & bt->Mask) == (Uint)bt->Ident ) + return bt->Relocate( (void*)Base); + } + + Warning("[BIN ] 0x%x is an unknown file type. (0x%x 0x%x 0x%x 0x%x)", + Base, ident&0xFF, ident>>8, ident>>16, ident>>24); + return 0; +} + +/** + * \fn int Binary_GetSymbol(char *Name, Uint *Val) + * \brief Get a symbol value + * \return Value of symbol or -1 on error + * + * Gets the value of a symbol from either the currently loaded + * libraries or the kernel's exports. + */ +int Binary_GetSymbol(char *Name, Uint *Val) +{ + if( Binary_GetSymbolEx(Name, Val) ) return 1; + return 0; +} + +/** + * \fn Uint Binary_GetSymbolEx(char *Name, Uint *Value) + * \brief Get a symbol value + * + * Gets the value of a symbol from either the currently loaded + * libraries or the kernel's exports. + */ +Uint Binary_GetSymbolEx(char *Name, Uint *Value) +{ + int i; + tKernelBin *pKBin; + int numKSyms = ((Uint)&gKernelSymbolsEnd-(Uint)&gKernelSymbols)/sizeof(tKernelSymbol); + + // Scan Kernel + for( i = 0; i < numKSyms; i++ ) + { + if(strcmp(Name, gKernelSymbols[i].Name) == 0) { + *Value = gKernelSymbols[i].Value; + return 1; + } + } + + // Scan Loaded Libraries + for(pKBin = glLoadedKernelLibs; + pKBin; + pKBin = pKBin->Next ) + { + if( Binary_FindSymbol(pKBin->Base, Name, Value) ) { + return 1; + } + } + + Warning("[BIN ] Unable to find symbol '%s'", Name); + return 0; +} + +/** + * \fn Uint Binary_GetSymbolBin(void *Base, char *Name, Uint *val) + * \brief Get a symbol from the specified library + * \param Base Base address + * \param Name Name of symbol to find + * \param val Pointer to place final value + */ +Uint Binary_FindSymbol(void *Base, char *Name, Uint *val) +{ + Uint32 ident = *(Uint32*) Base; + tBinaryType *bt = gRegBinTypes; + + for(; bt; bt = bt->Next) + { + if( (ident & bt->Mask) == (Uint)bt->Ident ) + return bt->GetSymbol(Base, Name, val); + } + + Warning("[BIN ] 0x%x is an unknown file type. (0x%x 0x%x 0x%x 0x%x)", + Base, ident&0xFF, ident>>8, ident>>16, ident>>24); + return 0; +} diff --git a/Kernel/debug.c b/Kernel/debug.c new file mode 100644 index 00000000..8df36091 --- /dev/null +++ b/Kernel/debug.c @@ -0,0 +1,324 @@ +/* + * AcessOS Microkernel Version + * debug.c + */ +#include +#include + +// === MACROS === +#define E9(ch) __asm__ __volatile__ ("outb %%al, $0xe9"::"a"(((Uint8)ch))) + +// === IMPORTS === +extern void Proc_DumpThreads(); + +// === GLOBALS === + int gDebug_Level = 0; + +// === CODE === +static void E9_Str(char *Str) +{ + while(*Str) E9(*Str++); +} + +void E9_Fmt(const char *format, va_list *args) +{ + char c, pad = ' '; + int minSize = 0; + char tmpBuf[34]; // For Integers + char *p = NULL; + int isLongLong = 0; + Uint64 arg; + + while((c = *format++) != 0) + { + // Non control character + if( c != '%' ) { + E9(c); + continue; + } + + c = *format++; + + // Literal % + if(c == '%') { + E9('%'); + continue; + } + + // Pointer + if(c == 'p') { + Uint ptr = va_arg(*args, Uint); + E9('*'); E9('0'); E9('x'); + p = tmpBuf; + itoa(p, ptr, 16, BITS/4, '0'); + goto printString; + } + + // Get Argument + arg = va_arg(*args, Uint); + + // Padding + if(c == '0') { + pad = '0'; + c = *format++; + } else + pad = ' '; + + // Minimum length + minSize = 1; + if('1' <= c && c <= '9') + { + minSize = 0; + while('0' <= c && c <= '9') + { + minSize *= 10; + minSize += c - '0'; + c = *format++; + } + } + + // Long (default) + isLongLong = 0; + if(c == 'l') { + c = *format++; + if(c == 'l') { + #if BITS == 32 + arg |= va_arg(*args, Uint); + #endif + c = *format++; + isLongLong = 1; + } + } + + p = tmpBuf; + switch (c) { + case 'd': + case 'i': + if( (isLongLong && arg >> 63) || (!isLongLong && arg >> 31) ) { + E9('-'); + arg = -arg; + } + itoa(p, arg, 10, minSize, pad); + goto printString; + case 'u': + itoa(p, arg, 10, minSize, pad); + goto printString; + case 'x': + itoa(p, arg, 16, minSize, pad); + goto printString; + case 'o': + itoa(p, arg, 8, minSize, pad); + goto printString; + case 'b': + itoa(p, arg, 2, minSize, pad); + goto printString; + + case 'B': //Boolean + if(arg) E9_Str("True"); + else E9_Str("False"); + break; + + case 's': + p = (char*)(Uint)arg; + printString: + if(!p) p = "(null)"; + while(*p) E9(*p++); + break; + + default: E9(arg); break; + } + } +} + +/** + * \fn void LogV(char *Fmt, va_list Args) + */ +void LogV(char *Fmt, va_list Args) +{ + E9_Str("Log: "); + E9_Fmt(Fmt, &Args); + E9('\n'); +} +/** + * \fn void LogF(char *Msg, ...) + */ +void LogF(char *Fmt, ...) +{ + va_list args; + + va_start(args, Fmt); + + E9_Fmt(Fmt, &args); + + va_end(args); +} +/** + * \fn void Log(char *Msg, ...) + */ +void Log(char *Fmt, ...) +{ + va_list args; + + E9_Str("Log: "); + va_start(args, Fmt); + E9_Fmt(Fmt, &args); + va_end(args); + E9('\n'); +} +void Warning(char *Fmt, ...) +{ + va_list args; + E9_Str("Warning: "); + va_start(args, Fmt); + E9_Fmt(Fmt, &args); + va_end(args); + E9('\n'); +} +void Panic(char *Fmt, ...) +{ + va_list args; + E9_Str("Panic: "); + va_start(args, Fmt); + E9_Fmt(Fmt, &args); + va_end(args); + E9('\n'); + + Proc_DumpThreads(); + + __asm__ __volatile__ ("xchg %bx, %bx"); + __asm__ __volatile__ ("cli;\n\thlt"); + for(;;) __asm__ __volatile__ ("hlt"); +} + +void Debug_Enter(char *FuncName, char *ArgTypes, ...) +{ + va_list args; + int i = gDebug_Level ++; + int pos; + + va_start(args, ArgTypes); + + while(i--) E9(' '); + + E9_Str(FuncName); E9_Str(": ("); + + while(*ArgTypes) + { + pos = strpos(ArgTypes, ' '); + if(pos != -1) ArgTypes[pos] = '\0'; + if(pos == -1 || pos > 1) { + E9_Str(ArgTypes+1); + E9('='); + } + if(pos != -1) ArgTypes[pos] = ' '; + switch(*ArgTypes) + { + case 'p': E9_Fmt("%p", &args); break; + case 's': E9_Fmt("'%s'", &args); break; + case 'i': E9_Fmt("%i", &args); break; + case 'u': E9_Fmt("%u", &args); break; + case 'x': E9_Fmt("0x%x", &args); break; + case 'b': E9_Fmt("0b%b", &args); break; + // Extended (64-Bit) + case 'X': E9_Fmt("0x%llx", &args); break; + case 'B': E9_Fmt("0b%llb", &args); break; + } + if(pos != -1) { + E9(','); E9(' '); + } + + if(pos == -1) break; + ArgTypes = &ArgTypes[pos+1]; + } + + va_end(args); + E9(')'); E9('\n'); +} + +void Debug_Log(char *FuncName, char *Fmt, ...) +{ + va_list args; + int i = gDebug_Level; + + va_start(args, Fmt); + + while(i--) E9(' '); + + E9_Str(FuncName); E9_Str(": "); + E9_Fmt(Fmt, &args); + + va_end(args); + E9('\n'); +} + +void Debug_Leave(char *FuncName, char RetType, ...) +{ + va_list args; + int i = --gDebug_Level; + + va_start(args, RetType); + + // Indenting + while(i--) E9(' '); + + E9_Str(FuncName); E9_Str(": RETURN"); + + // No Return + if(RetType == '-') { + E9('\n'); + return; + } + + E9(' '); + switch(RetType) + { + case 'n': E9_Str("NULL"); break; + case 'p': E9_Fmt("%p", &args); break; + case 's': E9_Fmt("'%s'", &args); break; + case 'i': E9_Fmt("%i", &args); break; + case 'u': E9_Fmt("%u", &args); break; + case 'x': E9_Fmt("0x%x", &args); break; + // Extended (64-Bit) + case 'X': E9_Fmt("0x%llx", &args); break; + } + E9('\n'); + + va_end(args); +} + +void Debug_HexDump(char *Header, void *Data, Uint Length) +{ + Uint8 *cdat = Data; + Uint pos = 0; + E9_Str(Header); + LogF(" (Hexdump of %p)\n", Data); + + while(Length >= 16) + { + Log("%04x: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", + pos, + cdat[0], cdat[1], cdat[2], cdat[3], cdat[4], cdat[5], cdat[6], cdat[7], + cdat[8], cdat[9], cdat[10], cdat[11], cdat[12], cdat[13], cdat[14], cdat[15] + ); + Length -= 16; + cdat += 16; + pos += 16; + } + + LogF("Log: %04x: ", pos); + while(Length) + { + Uint byte = *cdat; + LogF("%02x ", byte); + Length--; + cdat --; + } + E9('\n'); + +} + +// --- EXPORTS --- +EXPORT(Warning); +EXPORT(Debug_Enter); +EXPORT(Debug_Log); +EXPORT(Debug_Leave); diff --git a/Kernel/drv/Makefile b/Kernel/drv/Makefile new file mode 100644 index 00000000..5a55d939 --- /dev/null +++ b/Kernel/drv/Makefile @@ -0,0 +1,22 @@ +# Acess2 Module/Driver Templater Makefile +# Makefile.tpl +ARCH = i386 + +CC = gcc +LD = ld + +CPPFLAGS = -I../include -I../arch/$(ARCH)/include -DARCH=$(ARCH) -DBUILD_MODULE +CFLAGS = -Wall -Werror $(CPPFLAGS) + +.PHONY: all clean + +all: ata_x86.kmd + +%.kmd: + $(CC) -shared -nostdlib -o $@ $< + +%.o: %.c + $(CC) $(CFLAGS) -o $@ -c $< + +ata_x86.kmd: ata_x86.o +bochsvbe.kmd: bochsvbe.o diff --git a/Kernel/drv/ata_x86.c b/Kernel/drv/ata_x86.c new file mode 100644 index 00000000..a4c53065 --- /dev/null +++ b/Kernel/drv/ata_x86.c @@ -0,0 +1,877 @@ +/* + * Acess2 IDE Harddisk Driver + * drv/ide.c + */ +#include +#include +#include +#include +#include +#include + +// === CONSTANTS === +#define MAX_ATA_DISKS 4 +#define SECTOR_SIZE 512 +#define MAX_DMA_SECTORS (0x1000 / SECTOR_SIZE) + +#define IDE_PRI_BASE 0x1F0 +#define IDE_SEC_BASE 0x170 + +#define IDE_PRDT_LAST 0x8000 +/** + \enum HddControls + \brief Commands to be sent to HDD_CMD +*/ +enum HddControls { + HDD_PIO_R28 = 0x20, + HDD_PIO_R48 = 0x24, + HDD_DMA_R48 = 0x25, + HDD_PIO_W28 = 0x30, + HDD_PIO_W48 = 0x34, + HDD_DMA_W48 = 0x35, + HDD_DMA_R28 = 0xC8, + HDD_DMA_W28 = 0xCA, +}; + +// === STRUCTURES === +typedef struct { + Uint32 PBufAddr; + Uint16 Bytes; + Uint16 Flags; +} tPRDT_Ent; +typedef struct { + Uint16 Flags; // 1 + Uint16 Usused1[9]; // 10 + char SerialNum[20]; // 20 + Uint16 Usused2[3]; // 23 + char FirmwareVer[8]; // 27 + char ModelNumber[40]; // 47 + Uint16 SectPerInt; // 48 - and with 0xFF to get true value; + Uint16 Unused3; // 49 + Uint16 Capabilities[2]; // 51 + Uint16 Unused4[2]; // 53 + Uint16 ValidExtData; // 54 + Uint16 Unused5[5]; // 59 + Uint16 SizeOfRWMultiple; // 60 + Uint32 Sectors28; // 62 + Uint16 Unused6[100-62]; + Uint64 Sectors48; + Uint16 Unused7[256-104]; +} tIdentify; +typedef struct { + Uint8 BootCode[0x1BE]; + struct { + Uint8 Boot; + Uint8 Unused1; // Also CHS Start + Uint16 StartHi; // Also CHS Start + Uint8 SystemID; + Uint8 Unused2; // Also CHS Length + Uint16 LengthHi; // Also CHS Length + Uint32 LBAStart; + Uint32 LBALength; + } __attribute__ ((packed)) Parts[4]; + Uint16 BootFlag; // = 0xAA 55 +} __attribute__ ((packed)) tMBR; + +typedef struct { + Uint64 Start; + Uint64 Length; + char Name[4]; + tVFS_Node Node; +} tATA_Partition; +typedef struct { + Uint64 Sectors; + char Name[2]; + tVFS_Node Node; + int NumPartitions; + tATA_Partition *Partitions; +} tATA_Disk; + +// === PROTOTYPES === + int ATA_Install(); + int ATA_SetupIO(); +static void ATA_SetupPartitions(); + void ATA_SetupVFS(); + int ATA_ScanDisk(int Disk); +void ATA_ParseGPT(int Disk); +void ATA_ParseMBR(int Disk); +void ATA_int_MakePartition(tATA_Partition *Part, int Disk, int Num, Uint64 Start, Uint64 Length); +Uint16 ATA_GetBasePort(int Disk); +char *ATA_ReadDir(tVFS_Node *Node, int Pos); +tVFS_Node *ATA_FindDir(tVFS_Node *Node, char *Name); +Uint64 ATA_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); + int ATA_IOCtl(tVFS_Node *Node, int Id, void *Data); + int ATA_Read(Uint8 Disk, Uint64 Address, Uint64 Count, void *Buffer); + int ATA_ReadDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer); +void ATA_IRQHandlerPri(void); +void ATA_IRQHandlerSec(void); +Uint8 ATA_int_BusMasterReadByte(int Ofs); +void ATA_int_BusMasterWriteByte(int Ofs, Uint8 Value); +void ATA_int_BusMasterWriteDWord(int Ofs, Uint32 Value); + +// === GLOBALS === +MODULE_DEFINE(0, 0x0032, i386ATA, ATA_Install, NULL); +tDevFS_Driver gATA_DriverInfo = { + NULL, "ata", + { + .NumACLs = 1, + .Flags = VFS_FFLAG_DIRECTORY, + .ACLs = &gVFS_ACL_EveryoneRX, + .ReadDir = ATA_ReadDir, + .FindDir = ATA_FindDir + } +}; +tATA_Disk gATA_Disks[MAX_ATA_DISKS]; + int giATA_NumNodes; +tVFS_Node **gATA_Nodes; +Uint16 gATA_BusMasterBase = 0; +Uint8 *gATA_BusMasterBasePtr = 0; + int gATA_IRQPri = 14; + int gATA_IRQSec = 15; + int giaATA_ControllerLock[2] = {0}; //!< Spinlocks for each controller +Uint8 gATA_Buffers[2][4096] __attribute__ ((section(".padata"))); + int gaATA_IRQs[2] = {0}; +tPRDT_Ent gATA_PRDTs[2] = { + {0, 512, IDE_PRDT_LAST}, + {0, 512, IDE_PRDT_LAST} +}; + +// === CODE === +/** + * \fn int ATA_Install() + */ +int ATA_Install() +{ + int ret; + + ret = ATA_SetupIO(); + if(ret != 1) return ret; + + ATA_SetupPartitions(); + + ATA_SetupVFS(); + + if( DevFS_AddDevice( &gATA_DriverInfo ) == 0 ) + return 0; + + return 1; +} + +/** + * \fn int ATA_SetupIO() + * \brief Sets up the ATA controller's DMA mode + */ +int ATA_SetupIO() +{ + int ent; + Uint addr; + + ENTER(""); + + // Get IDE Controller's PCI Entry + ent = PCI_GetDeviceByClass(0x0101, 0xFFFF, -1); + LOG("ent = %i\n", ent); + gATA_BusMasterBase = PCI_GetBAR4( ent ); + LOG("gATA_BusMasterBase = 0x%x\n", gATA_BusMasterBase); + if( gATA_BusMasterBase == 0 ) { + Warning("It seems that there is no Bus Master Controller on this machine, get one"); + LEAVE('i', 0); + return 0; + } + if( !(gATA_BusMasterBase & 1) ) + { + if( gATA_BusMasterBase < 0x100000 ) + gATA_BusMasterBasePtr = (void*)(0xC0000000|gATA_BusMasterBase); + else + gATA_BusMasterBasePtr = (void*)( MM_MapHWPage( gATA_BusMasterBase, 1 ) + (gATA_BusMasterBase&0xFFF) ); + } + + IRQ_AddHandler( gATA_IRQPri, ATA_IRQHandlerPri ); + IRQ_AddHandler( gATA_IRQSec, ATA_IRQHandlerSec ); + + gATA_PRDTs[0].PBufAddr = MM_GetPhysAddr( (Uint)&gATA_Buffers[0] ); + gATA_PRDTs[1].PBufAddr = MM_GetPhysAddr( (Uint)&gATA_Buffers[1] ); + + LOG("gATA_PRDTs = {0x%x, 0x%x}", gATA_PRDTs[0].PBufAddr, gATA_PRDTs[1].PBufAddr); + + addr = MM_GetPhysAddr( (Uint)&gATA_PRDTs[0] ); + ATA_int_BusMasterWriteDWord(4, addr); + addr = MM_GetPhysAddr( (Uint)&gATA_PRDTs[1] ); + ATA_int_BusMasterWriteDWord(12, addr); + + LEAVE('i', 1); + return 1; +} + +/** + * \fn static void ATA_SetupPartitions() + */ +static void ATA_SetupPartitions() +{ + int i; + for( i = 0; i < MAX_ATA_DISKS; i ++ ) + { + if( !ATA_ScanDisk(i) ) { + gATA_Disks[i].Name[0] = '\0'; // Mark as unused + continue; + } + } +} + +/** + * \fn void ATA_SetupVFS() + * \brief Sets up the ATA drivers VFS information and registers with DevFS + */ +void ATA_SetupVFS() +{ + int i, j, k; + + // Count number of nodes needed + giATA_NumNodes = 0; + for( i = 0; i < MAX_ATA_DISKS; i++ ) + { + if(gATA_Disks[i].Name[0] == '\0') continue; // Ignore + giATA_NumNodes ++; + giATA_NumNodes += gATA_Disks[i].NumPartitions; + } + + // Allocate Node space + gATA_Nodes = malloc( giATA_NumNodes * sizeof(void*) ); + + // Set nodes + k = 0; + for( i = 0; i < MAX_ATA_DISKS; i++ ) + { + if(gATA_Disks[i].Name[0] == '\0') continue; // Ignore + gATA_Nodes[ k++ ] = &gATA_Disks[i].Node; + for( j = 0; j < gATA_Disks[i].NumPartitions; j ++ ) + gATA_Nodes[ k++ ] = &gATA_Disks[i].Partitions[j].Node; + } +} + +/** + * \fn int ATA_ScanDisk(int Disk) + */ +int ATA_ScanDisk(int Disk) +{ + Uint16 buf[256]; + tIdentify *identify = (void*)buf; + tMBR *mbr = (void*)buf; + Uint16 base; + Uint8 val; + int i; + tVFS_Node *node; + + base = ATA_GetBasePort( Disk ); + + // Send Disk Selector + if(Disk == 1 || Disk == 3) + outb(base+6, 0xB0); + else + outb(base+6, 0xA0); + + // Send IDENTIFY + outb(base+7, 0xEC); + val = inb(base+7); // Read status + if(val == 0) return 0; // Disk does not exist + + // Poll until BSY clears and DRQ sets or ERR is set + while( ((val & 0x80) || !(val & 0x08)) && !(val & 1)) val = inb(base+7); + + if(val & 1) return 0; // Error occured, so return false + + // Read Data + for(i=0;i<256;i++) buf[i] = inw(base); + + // Populate Disk Structure + if(identify->Sectors48 != 0) + gATA_Disks[ Disk ].Sectors = identify->Sectors48; + else + gATA_Disks[ Disk ].Sectors = identify->Sectors28; + + + if( gATA_Disks[ Disk ].Sectors / (2048*1024) ) + Log("Disk %i: 0x%llx Sectors (%i GiB)", Disk, + gATA_Disks[ Disk ].Sectors, gATA_Disks[ Disk ].Sectors / (2048*1024)); + else if( gATA_Disks[ Disk ].Sectors / 2048 ) + Log("Disk %i: 0x%llx Sectors (%i MiB)", Disk, + gATA_Disks[ Disk ].Sectors, gATA_Disks[ Disk ].Sectors / 2048); + else + Log("Disk %i: 0x%llx Sectors (%i KiB)", Disk, + gATA_Disks[ Disk ].Sectors, gATA_Disks[ Disk ].Sectors / 2); + + // Create Name + gATA_Disks[ Disk ].Name[0] = 'A'+Disk; + gATA_Disks[ Disk ].Name[1] = '\0'; + + // Get pointer to vfs node and populate it + node = &gATA_Disks[ Disk ].Node; + node->Size = gATA_Disks[Disk].Sectors * SECTOR_SIZE; + node->NumACLs = 0; // Means Superuser only can access it + node->Inode = (Disk << 8) | 0xFF; + node->ImplPtr = gATA_Disks[ Disk ].Name; + + node->ATime = node->MTime + = node->CTime = now(); + + node->Read = ATA_ReadFS; + //node->Write = ATA_WriteFS; + node->IOCtl = ATA_IOCtl; + + + // --- Scan Partitions --- + // Read Boot Sector + ATA_ReadDMA( Disk, 0, 1, mbr ); + + // Check for a GPT table + if(mbr->Parts[0].SystemID == 0xEE) + ATA_ParseGPT(Disk); + else // No? Just parse the MBR + ATA_ParseMBR(Disk); + + return 1; +} + +/** + * \fn void ATA_ParseGPT(int Disk) + * \brief Parses the GUID Partition Table + */ +void ATA_ParseGPT(int Disk) +{ + ///\todo Support GPT Disks + Warning("GPT Disks are currently unsupported"); +} + +/** + * \fn void ATA_ParseMBR(int Disk) + */ +void ATA_ParseMBR(int Disk) +{ + int i, j = 0, k = 4; + tMBR mbr; + Uint64 extendedLBA; + + // Read Boot Sector + ATA_ReadDMA( Disk, 0, 1, &mbr ); + + // Count Partitions + gATA_Disks[Disk].NumPartitions = 0; + extendedLBA = 0; + for( i = 0; i < 4; i ++ ) + { + if( mbr.Parts[i].SystemID == 0 ) continue; + if( + mbr.Parts[i].Boot == 0x0 || mbr.Parts[i].Boot == 0x80 // LBA 28 + || mbr.Parts[i].Boot == 0x1 || mbr.Parts[i].Boot == 0x81 // LBA 48 + ) + { + if( mbr.Parts[i].SystemID == 0xF || mbr.Parts[i].SystemID == 5 ) { + if(extendedLBA != 0) { + Warning("Disk %i has multiple extended partitions, ignoring rest", Disk); + continue; + } + extendedLBA = mbr.Parts[i].LBAStart; + continue; + } + + gATA_Disks[Disk].NumPartitions ++; + continue; + } + // Invalid Partition, so don't count it + } + while(extendedLBA != 0) + { + if( ATA_ReadDMA( Disk, extendedLBA, 1, &mbr ) != 0 ) + break; // Stop on Errors + + extendedLBA = 0; + + if( mbr.Parts[0].SystemID == 0 ) continue; + if( mbr.Parts[0].Boot == 0x0 || mbr.Parts[0].Boot == 0x80 // LBA 28 + || mbr.Parts[0].Boot == 0x1 || mbr.Parts[0].Boot == 0x81 // LBA 48 + ) + { + if(mbr.Parts[0].SystemID == 0xF || mbr.Parts[0].SystemID == 0x7) + extendedLBA = mbr.Parts[0].LBAStart; + else + gATA_Disks[Disk].NumPartitions ++; + } + + if( mbr.Parts[1].SystemID == 0 ) continue; + if( mbr.Parts[1].Boot == 0x0 || mbr.Parts[1].Boot == 0x80 // LBA 28 + || mbr.Parts[1].Boot == 0x1 || mbr.Parts[1].Boot == 0x81 // LBA 48 + ) + { + if(mbr.Parts[1].SystemID == 0xF || mbr.Parts[1].SystemID == 0x7) { + if(extendedLBA == 0) { + Warning("Disk %i has twp forward link in the extended partition", + Disk); + break; + } + extendedLBA = mbr.Parts[1].LBAStart; + } + else { + if(extendedLBA != 0) { + Warning("Disk %i lacks a forward link in the extended partition", + Disk); + break; + } + gATA_Disks[Disk].NumPartitions ++; + } + } + } + + // Create patition array + gATA_Disks[Disk].Partitions = malloc( gATA_Disks[Disk].NumPartitions * sizeof(tATA_Partition) ); + + // --- Fill Partition Info --- + extendedLBA = 0; + for( i = 0; i < 4; i ++ ) + { + if( mbr.Parts[i].SystemID == 0 ) continue; + if( mbr.Parts[i].Boot == 0x0 || mbr.Parts[i].Boot == 0x80 ) // LBA 28 + { + if( mbr.Parts[1].SystemID == 0xF || mbr.Parts[1].SystemID == 5 ) { + if(extendedLBA != 0) { + Warning("Disk %i has multiple extended partitions, ignoring rest", Disk); + continue; + } + extendedLBA = mbr.Parts[1].LBAStart; + continue; + } + // Create Partition + ATA_int_MakePartition( &gATA_Disks[Disk].Partitions[j], Disk, i, + mbr.Parts[i].LBAStart, mbr.Parts[i].LBALength + ); + j ++; + continue; + } + if( mbr.Parts[i].Boot == 0x1 || mbr.Parts[i].Boot == 0x81 ) // LBA 48 + { + if( mbr.Parts[i].SystemID == 0xF || mbr.Parts[i].SystemID == 5 ) { + if(extendedLBA != 0) { + Warning("Disk %i has multiple extended partitions, ignoring rest", Disk); + continue; + } + extendedLBA = (mbr.Parts[i].StartHi << 16) | mbr.Parts[i].LBAStart; + continue; + } + ATA_int_MakePartition( &gATA_Disks[Disk].Partitions[j], Disk, i, + (mbr.Parts[i].StartHi << 16) | mbr.Parts[i].LBAStart, + (mbr.Parts[i].LengthHi << 16) | mbr.Parts[i].LBALength + ); + j ++; + } + // Invalid Partition, so don't count it + } + // Scan extended partition + while(extendedLBA != 0) + { + if( ATA_ReadDMA( Disk, extendedLBA, 1, &mbr ) != 0 ) + break; // Stop on Errors + + extendedLBA = 0; + + // Check first entry (should be partition) + if( mbr.Parts[0].SystemID != 0) + { + if( mbr.Parts[0].Boot == 0x0 || mbr.Parts[0].Boot == 0x80 ) // LBA 28 + { + // Forward Link to next Extended partition entry + if(mbr.Parts[0].SystemID == 0xF || mbr.Parts[0].SystemID == 0x7) + extendedLBA = mbr.Parts[0].LBAStart; + else { + ATA_int_MakePartition( &gATA_Disks[Disk].Partitions[j], Disk, k, + mbr.Parts[0].LBAStart, mbr.Parts[0].LBALength + ); + j ++; k ++; + } + } + else if( mbr.Parts[0].Boot == 0x1 || mbr.Parts[0].Boot == 0x81 ) // LBA 48 + { + if(mbr.Parts[0].SystemID == 0xF || mbr.Parts[0].SystemID == 0x7) + extendedLBA = (mbr.Parts[0].StartHi << 16) | mbr.Parts[0].LBAStart; + else { + ATA_int_MakePartition( &gATA_Disks[Disk].Partitions[j], Disk, k, + (mbr.Parts[0].StartHi << 16) | mbr.Parts[0].LBAStart, + (mbr.Parts[0].LengthHi << 16) | mbr.Parts[0].LBALength + ); + j ++; k ++; + } + } + } + + // Check second entry (should be forward link) + if( mbr.Parts[1].SystemID != 0) + { + if(mbr.Parts[1].Boot == 0x0 || mbr.Parts[1].Boot == 0x80 ) // LBA 28 + { + if(mbr.Parts[1].SystemID == 0xF || mbr.Parts[1].SystemID == 0x7) { + if(extendedLBA == 0) { + Warning("Disk %i has twp forward link in the extended partition", + Disk); + break; + } + extendedLBA = mbr.Parts[1].LBAStart; + } + else + { + if(extendedLBA != 0) { + Warning("Disk %i lacks a forward link in the extended partition", + Disk); + break; + } + ATA_int_MakePartition( &gATA_Disks[Disk].Partitions[j], Disk, k, + mbr.Parts[1].LBAStart, mbr.Parts[1].LBALength + ); + j ++; k ++; + } + + } + else if( mbr.Parts[1].Boot == 0x1 || mbr.Parts[1].Boot == 0x81 ) // LBA 48 + { + if(mbr.Parts[1].SystemID == 0xF || mbr.Parts[1].SystemID == 0x7) { + if(extendedLBA == 0) { + Warning("Disk %i has twp forward link in the extended partition", + Disk); + break; + } + extendedLBA = (mbr.Parts[1].StartHi << 16) | mbr.Parts[1].LBAStart; + } + else + { + if(extendedLBA != 0) { + Warning("Disk %i lacks a forward link in the extended partition", + Disk); + break; + } + ATA_int_MakePartition( &gATA_Disks[Disk].Partitions[j], Disk, k, + (mbr.Parts[1].StartHi << 16) | mbr.Parts[1].LBAStart, + (mbr.Parts[1].LengthHi << 16) | mbr.Parts[1].LBALength + ); + j ++; k ++; + } + } + } + } +} + +/** + * \fn void ATA_int_MakePartition(tATA_Partition *Part, int Disk, int Num, Uint64 Start, Uint64 Length) + * \brief Fills a parition's information structure + */ +void ATA_int_MakePartition(tATA_Partition *Part, int Disk, int Num, Uint64 Start, Uint64 Length) +{ + ENTER("pPart iDisk iNum XStart XLength", Part, Disk, Num, Start, Length); + Part->Start = Start; + Part->Length = Length; + Part->Name[0] = 'A'+Disk; + if(Num >= 10) { + Part->Name[1] = '1'+Num/10; + Part->Name[2] = '1'+Num%10; + Part->Name[3] = '\0'; + } else { + Part->Name[1] = '1'+Num; + Part->Name[2] = '\0'; + } + Part->Node.NumACLs = 0; // Only root can read/write raw block devices + Part->Node.Inode = (Disk << 8) | Num; + Part->Node.ImplPtr = Part->Name; + + Part->Node.Read = ATA_ReadFS; + Part->Node.IOCtl = ATA_IOCtl; + LOG("Made '%s' (&Node=%p)", Part->Name, &Part->Node); + LEAVE('-'); +} + +/** + * \fn Uint16 ATA_GetPortBase(int Disk) + * \brief Returns the base port for a given disk + */ +Uint16 ATA_GetBasePort(int Disk) +{ + switch(Disk) + { + case 0: case 1: return IDE_PRI_BASE; + case 2: case 3: return IDE_SEC_BASE; + } + return 0; +} + +/** + * \fn char *ATA_ReadDir(tVFS_Node *Node, int Pos) + */ +char *ATA_ReadDir(tVFS_Node *Node, int Pos) +{ + if(Pos >= giATA_NumNodes || Pos < 0) return NULL; + return gATA_Nodes[Pos]->ImplPtr; +} + +/** + * \fn tVFS_Node *ATA_FindDir(tVFS_Node *Node, char *Name) + */ +tVFS_Node *ATA_FindDir(tVFS_Node *Node, char *Name) +{ + int part; + // Check first character + if(Name[0] < 'A' || Name[0] > 'A'+MAX_ATA_DISKS) + return NULL; + // Raw Disk + if(Name[1] == '\0') return &gATA_Disks[Name[0]-'A'].Node; + + // Partitions + if(Name[1] < '0' || '9' < Name[1]) return NULL; + if(Name[2] == '\0') { // <= 9 + part = Name[1] - '0'; + part --; + return &gATA_Disks[Name[0]-'A'].Partitions[part].Node; + } + // > 9 + if('0' > Name[2] || '9' < Name[2]) return NULL; + if(Name[3] != '\0') return NULL; + + part = (Name[1] - '0') * 10; + part += Name[2] - '0'; + part --; + return &gATA_Disks[Name[0]-'A'].Partitions[part].Node; + +} + +/** + * \fn Uint64 ATA_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) + */ +Uint64 ATA_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + int ret; + int disk, part; + Uint64 sector, count; + + disk = Node->Inode >> 8; + part = Node->Inode & 0xFF; + + // Aligned Read + if(Offset % SECTOR_SIZE == 0 && Length % SECTOR_SIZE == 0) + { + sector = Offset / SECTOR_SIZE; + count = Length / SECTOR_SIZE; + // Raw Disk? + if(part == 0xFF) + { + // Bounds Check + if( sector >= gATA_Disks[disk].Sectors ) return 0; + if( sector + count > gATA_Disks[disk].Sectors ) + count = gATA_Disks[disk].Sectors - sector; + // Read Data + ret = ATA_Read(disk, sector, count, Buffer); + } + else // Or a partition + { + //Log(" ATA_ReadFS: %i:%i 0x%llx + 0x%llx\n", disk, part, + // gATA_Disks[disk].Partitions[part].Start, + // gATA_Disks[disk].Partitions[part].Length ); + + // Bounds Check + if( sector >= gATA_Disks[disk].Partitions[part].Length ) return 0; + if( sector + count > gATA_Disks[disk].Partitions[part].Length ) + count = gATA_Disks[disk].Partitions[part].Length - sector; + // Read Disk + ret = ATA_Read(disk, + gATA_Disks[disk].Partitions[part].Start + sector, + count, + Buffer); + } + // Check return value + if(ret == 1) + return count * SECTOR_SIZE; + else { + Warning("ATA_ReadFS: RETURN 0 (Read failed with ret = %i)", ret); + return 0; + } + } + Warning("ATA_ReadFS: RETURN 0 (Non-Aligned Read 0x%llx 0x%llx)", Offset, Length); + return 0; +} + +/** + * \fn Uint64 ATA_WriteFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) + */ +Uint64 ATA_WriteFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + return 0; +} + +/** + * \fn int ATA_IOCtl(tVFS_Node *Node, int Id, void *Data) + * \brief IO Control Funtion + */ +int ATA_IOCtl(tVFS_Node *Node, int Id, void *Data) +{ + switch(Id) + { + case DRV_IOCTL_TYPE: return DRV_TYPE_DISK; + } + return 0; +} + +// --- Disk Access --- +/** + * \fn int ATA_Read(Uint8 Disk, Uint64 Address, Uint64 Count, void *Buffer) + */ +int ATA_Read(Uint8 Disk, Uint64 Address, Uint64 Count, void *Buffer) +{ + int ret; + Uint offset; + + // Pass straight on to ATA_ReadDMAPage if we can + if(Count <= MAX_DMA_SECTORS) + return ATA_ReadDMA(Disk, Address, Count, Buffer); + + // Else we will have to break up the transfer + offset = 0; + while(Count > MAX_DMA_SECTORS) + { + ret = ATA_ReadDMA(Disk, Address+offset, MAX_DMA_SECTORS, Buffer+offset); + // Check for errors + if(ret != 1) return ret; + // Change Position + Count -= MAX_DMA_SECTORS; + offset += MAX_DMA_SECTORS*SECTOR_SIZE; + } + + return ATA_ReadDMA(Disk, Address+offset, Count, Buffer+offset); +} + +/** + * \fn int ATA_ReadDMAPage(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer) + */ +int ATA_ReadDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer) +{ + int cont = (Disk>>1)&1; // Controller ID + int disk = Disk & 1; + Uint16 base; + + // Check if the count is small enough + if(Count > MAX_DMA_SECTORS) return -1; + + // Get exclusive access to the disk controller + LOCK( &giaATA_ControllerLock[ cont ] ); + + // Set Size + gATA_PRDTs[ cont ].Bytes = Count * SECTOR_SIZE; + + // Get Port Base + base = ATA_GetBasePort(Disk); + + // Set up transfer + outb(base+0x01, 0x00); + if( Address > 0x0FFFFFFF ) // Use LBA48 + { + outb(base+0x6, 0x40 | (disk << 4)); + outb(base+0x2, 0 >> 8); // Upper Sector Count + outb(base+0x3, Address >> 24); // Low 2 Addr + outb(base+0x3, Address >> 28); // Mid 2 Addr + outb(base+0x3, Address >> 32); // High 2 Addr + } + else + { + outb(base+0x06, 0xE0 | (disk << 4) | ((Address >> 24) & 0x0F)); //Disk,Magic,High addr + } + + outb(base+0x02, (Uint8) Count); // Sector Count + outb(base+0x03, (Uint8) Address); // Low Addr + outb(base+0x04, (Uint8) (Address >> 8)); // Middle Addr + outb(base+0x05, (Uint8) (Address >> 16)); // High Addr + if( Address > 0x0FFFFFFF ) + outb(base+0x07, HDD_DMA_R48); // Read Command (LBA48) + else + outb(base+0x07, HDD_DMA_R28); // Read Command (LBA28) + + // Reset IRQ Flag + gaATA_IRQs[cont] = 0; + + // Start transfer + ATA_int_BusMasterWriteByte( cont << 3, 9 ); // Read and start + + // Wait for transfer to complete + while( gaATA_IRQs[cont] == 0 ) Proc_Yield(); + + // Complete Transfer + ATA_int_BusMasterWriteByte( cont << 3, 0 ); // Write and stop + + // Copy to destination buffer + memcpy( Buffer, gATA_Buffers[cont], Count*SECTOR_SIZE ); + + // Release controller lock + RELEASE( &giaATA_ControllerLock[ cont ] ); + + return 1; +} + +/** + * \fn void ATA_IRQHandlerPri(void) + */ +void ATA_IRQHandlerPri(void) +{ + Uint8 val; + + // IRQ bit set for Primary Controller + val = ATA_int_BusMasterReadByte( 0x2 ); + if(val & 4) { + //Log(" ATA_IRQHandlerPri: IRQ hit (val = 0x%x)", val); + ATA_int_BusMasterWriteByte( 0x2, 4 ); + gaATA_IRQs[0] = 1; + return ; + } +} + +/** + * \fn void ATA_IRQHandlerSec(void) + */ +void ATA_IRQHandlerSec(void) +{ + Uint8 val; + // IRQ bit set for Secondary Controller + val = ATA_int_BusMasterReadByte( 0xA ); + if(val & 4) { + //Log(" ATA_IRQHandlerSec: IRQ hit (val = 0x%x)", val); + ATA_int_BusMasterWriteByte( 0xA, 4 ); + gaATA_IRQs[1] = 1; + return ; + } +} + +/** + * \fn Uint8 ATA_int_BusMasterReadByte(int Ofs) + */ +Uint8 ATA_int_BusMasterReadByte(int Ofs) +{ + if( gATA_BusMasterBase & 1 ) + return inb( (gATA_BusMasterBase & ~1) + Ofs ); + else + return *(Uint8*)(gATA_BusMasterBasePtr + Ofs); +} + +/** + * \fn void ATA_int_BusMasterWriteByte(int Ofs, Uint8 Value) + * \brief Writes a byte to a Bus Master Register + */ +void ATA_int_BusMasterWriteByte(int Ofs, Uint8 Value) +{ + if( gATA_BusMasterBase & 1 ) + outb( (gATA_BusMasterBase & ~1) + Ofs, Value ); + else + *(Uint8*)(gATA_BusMasterBasePtr + Ofs) = Value; +} + +/** + * \fn void ATA_int_BusMasterWriteDWord(int Ofs, Uint32 Value) + * \brief Writes a dword to a Bus Master Register + */ +void ATA_int_BusMasterWriteDWord(int Ofs, Uint32 Value) +{ + + if( gATA_BusMasterBase & 1 ) + outd( (gATA_BusMasterBase & ~1) + Ofs, Value ); + else + *(Uint32*)(gATA_BusMasterBasePtr + Ofs) = Value; +} diff --git a/Kernel/drv/bochsvbe.c b/Kernel/drv/bochsvbe.c new file mode 100644 index 00000000..2aed670a --- /dev/null +++ b/Kernel/drv/bochsvbe.c @@ -0,0 +1,390 @@ +/** + * \file drv_bochsvbe.c + * \brief BGA (Bochs Graphic Adapter) Driver + * \note for AcessOS Version 1 + * \warning This driver does NOT support the Bochs PCI VGA driver +*/ +#include +#include +#include +#include +#include +#include + +#define DEBUG 0 +#if DEBUG +# define DEBUGS(v...) SysDebug(v) +#else +# define DEBUGS(v...) +#endif + +//#define INT static +#define INT + +// === TYPEDEFS === +typedef struct { + Uint16 width; + Uint16 height; + Uint16 bpp; + Uint16 flags; + Uint32 fbSize; +} t_bga_mode; + + +// === PROTOTYPES === +// Driver + int BGA_Install(char **Arguments); +void BGA_Uninstall(); +// Internal +void BGA_int_WriteRegister(Uint16 reg, Uint16 value); +Uint16 BGA_int_ReadRegister(Uint16 reg); +void BGA_int_SetBank(Uint16 bank); +void BGA_int_SetMode(Uint16 width, Uint16 height, Uint16 bpp); + int BGA_int_UpdateMode(int id); + int BGA_int_FindMode(tVideo_IOCtl_Mode *info); + int BGA_int_ModeInfo(tVideo_IOCtl_Mode *info); + int BGA_int_MapFB(void *Dest); +// Filesystem +Uint64 BGA_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer); +Uint64 BGA_Write(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer); + int BGA_Ioctl(tVFS_Node *node, int id, void *data); + +// === CONSTANTS === +const t_bga_mode BGA_MODES[] = { + {640,480,8, 0, 640*480}, + {640,480,32, 0, 640*480*4}, + {800,600,8, 0, 800*600}, + {800,600,32, 0, 800*600*4}, +}; +#define BGA_MODE_COUNT (sizeof(BGA_MODES)/sizeof(BGA_MODES[0])) +#define BGA_LFB_MAXSIZE (1024*768*4) +#define VBE_DISPI_BANK_ADDRESS 0xA0000 +#define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000 +#define VBE_DISPI_IOPORT_INDEX 0x01CE +#define VBE_DISPI_IOPORT_DATA 0x01CF +#define VBE_DISPI_DISABLED 0x00 +#define VBE_DISPI_ENABLED 0x01 +#define VBE_DISPI_LFB_ENABLED 0x40 +#define VBE_DISPI_NOCLEARMEM 0x80 +enum { + VBE_DISPI_INDEX_ID, + VBE_DISPI_INDEX_XRES, + VBE_DISPI_INDEX_YRES, + VBE_DISPI_INDEX_BPP, + VBE_DISPI_INDEX_ENABLE, + VBE_DISPI_INDEX_BANK, + VBE_DISPI_INDEX_VIRT_WIDTH, + VBE_DISPI_INDEX_VIRT_HEIGHT, + VBE_DISPI_INDEX_X_OFFSET, + VBE_DISPI_INDEX_Y_OFFSET +}; + +// GLOBALS +MODULE_DEFINE(0, 0x0032, BochsVBE, BGA_Install, NULL, NULL); +tDevFS_Driver gBGA_DriverStruct = { + NULL, "BochsGA", + { + .Read = BGA_Read, + .Write = BGA_Write, + .IOCtl = BGA_Ioctl + } +}; + int giBGA_CurrentMode = -1; + int giBGA_DriverId = -1; +Uint *gBGA_Framebuffer; + +// === CODE === +/** + * \fn int BGA_Install(char **Arguments) + */ +int BGA_Install(char **Arguments) +{ + int bga_version = 0; + + // Check BGA Version + bga_version = BGA_int_ReadRegister(VBE_DISPI_INDEX_ID); + // NOTE: This driver was written for 0xB0C4, but they seem to be backwards compatable + if(bga_version < 0xB0C4 || bga_version > 0xB0C5) { + Warning("[BGA ] Bochs Adapter Version is not 0xB0C4 or 0xB0C5, instead 0x%x", bga_version); + return 0; + } + + // Install Device + giBGA_DriverId = DevFS_AddDevice( &gBGA_DriverStruct ); + if(giBGA_DriverId == -1) { + Warning("[BGA ] Unable to register with DevFS, maybe already loaded?"); + return 0; + } + + // Map Framebuffer to hardware address + gBGA_Framebuffer = (void *) MM_MapHWPage(VBE_DISPI_LFB_PHYSICAL_ADDRESS, 768); // 768 pages (3Mb) + + return 1; +} + +/** + * \fn void BGA_Uninstall() + */ +void BGA_Uninstall() +{ + //DevFS_DelDevice( giBGA_DriverId ); + MM_UnmapHWPage( VBE_DISPI_LFB_PHYSICAL_ADDRESS, 768 ); +} + +/** + * \fn Uint64 BGA_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer) + * \brief Read from the framebuffer + */ +Uint64 BGA_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer) +{ + // Check Mode + if(giBGA_CurrentMode == -1) return -1; + + // Check Offset and Length against Framebuffer Size + if(off+len > BGA_MODES[giBGA_CurrentMode].fbSize) + return -1; + + // Copy from Framebuffer + memcpy(buffer, (void*)((Uint)gBGA_Framebuffer + (Uint)off), len); + return len; +} + +/** + * \fn Uint64 BGA_Write(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer) + * \brief Write to the framebuffer + */ +Uint64 BGA_Write(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer) +{ + Uint8 *destBuf; + + DEBUGS("BGA_Write: (off=%i, len=0x%x)\n", off, len); + + // Check Mode + if(giBGA_CurrentMode == -1) + return -1; + // Check Input against Frambuffer Size + if(off+len > BGA_MODES[giBGA_CurrentMode].fbSize) + return -1; + + destBuf = (Uint8*) ((Uint)gBGA_Framebuffer + (Uint)off); + + DEBUGS(" BGA_Write: *buffer = 0x%x\n", *(Uint*)buffer); + DEBUGS(" BGA_Write: Updating Framebuffer (0x%x - 0x%x bytes)\n", + destBuf, destBuf + (Uint)len); + + + // Copy to Frambuffer + memcpy(destBuf, buffer, len); + + DEBUGS("BGA_Write: BGA Framebuffer updated\n"); + + return len; +} + +/** + * \fn INT int BGA_Ioctl(tVFS_Node *node, int id, void *data) + * \brief Handle messages to the device + */ +INT int BGA_Ioctl(tVFS_Node *node, int id, void *data) +{ + int ret = -2; + ENTER("pNode iId pData", node, id, data); + + switch(id) + { + case DRV_IOCTL_TYPE: + ret = DRV_TYPE_VIDEO; + break; + case DRV_IOCTL_IDENT: + memcpy(data, "BGA1", 4); + ret = 1; + break; + case DRV_IOCTL_VERSION: + ret = 0x100; + break; + case DRV_IOCTL_LOOKUP: // TODO: Implement + ret = 0; + break; + + case VIDEO_IOCTL_SETMODE: + ret = BGA_int_UpdateMode(*(int*)(data)); + break; + + case VIDEO_IOCTL_GETMODE: + ret = giBGA_CurrentMode; + break; + + case VIDEO_IOCTL_FINDMODE: + ret = BGA_int_FindMode((tVideo_IOCtl_Mode*)data); + break; + + case VIDEO_IOCTL_MODEINFO: + ret = BGA_int_ModeInfo((tVideo_IOCtl_Mode*)data); + break; + + // Request Access to LFB + case VIDEO_IOCTL_REQLFB: + ret = BGA_int_MapFB( *(void**)data ); + break; + + default: + LEAVE('i', -2); + return -2; + } + + LEAVE('i', ret); + return ret; +} + +//== Internal Functions == +/** + * \fn void BGA_int_WriteRegister(Uint16 reg, Uint16 value) + * \brief Writes to a BGA register + */ +void BGA_int_WriteRegister(Uint16 reg, Uint16 value) +{ + outw(VBE_DISPI_IOPORT_INDEX, reg); + outw(VBE_DISPI_IOPORT_DATA, value); +} + +INT Uint16 BGA_int_ReadRegister(Uint16 reg) +{ + outw(VBE_DISPI_IOPORT_INDEX, reg); + return inw(VBE_DISPI_IOPORT_DATA); +} + +#if 0 +INT void BGA_int_SetBank(Uint16 bank) +{ + BGA_int_WriteRegister(VBE_DISPI_INDEX_BANK, bank); +} +#endif + +/** + * \fn void BGA_int_SetMode(Uint16 width, Uint16 height, Uint16 bpp) + * \brief Sets the video mode from the dimensions and bpp given + */ +void BGA_int_SetMode(Uint16 width, Uint16 height, Uint16 bpp) +{ + DEBUGS("BGA_int_SetMode: (width=%i, height=%i, bpp=%i)\n", width, height, bpp); + BGA_int_WriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED); + BGA_int_WriteRegister(VBE_DISPI_INDEX_XRES, width); + BGA_int_WriteRegister(VBE_DISPI_INDEX_YRES, height); + BGA_int_WriteRegister(VBE_DISPI_INDEX_BPP, bpp); + BGA_int_WriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED | VBE_DISPI_NOCLEARMEM | VBE_DISPI_LFB_ENABLED); + //BGA_int_WriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED | VBE_DISPI_NOCLEARMEM); +} + +/** + * \fn int BGA_int_UpdateMode(int id) + * \brief Set current vide mode given a mode id + */ +int BGA_int_UpdateMode(int id) +{ + if(id < 0 || id >= BGA_MODE_COUNT) return -1; + BGA_int_SetMode(BGA_MODES[id].width, BGA_MODES[id].height, BGA_MODES[id].bpp); + giBGA_CurrentMode = id; + return id; +} + +/** + * \fn int BGA_int_FindMode(tVideo_IOCtl_Mode *info) + * \brief Find a mode matching the given options + */ +int BGA_int_FindMode(tVideo_IOCtl_Mode *info) +{ + int i; + int best = -1, bestFactor = 1000; + int factor, tmp; + int rqdProduct = info->width * info->height * info->bpp; + + DEBUGS("BGA_int_FindMode: (info={width:%i,height:%i,bpp:%i})\n", info->width, info->height, info->bpp); + + for(i = 0; i < BGA_MODE_COUNT; i++) + { + #if DEBUG >= 2 + LogF("Mode %i (%ix%ix%i), ", i, BGA_MODES[i].width, BGA_MODES[i].height, BGA_MODES[i].bpp); + #endif + + if(BGA_MODES[i].width == info->width + && BGA_MODES[i].height == info->height + && BGA_MODES[i].bpp == info->bpp) + { + #if DEBUG >= 2 + LogF("Perfect!\n"); + #endif + best = i; + break; + } + + tmp = BGA_MODES[i].width * BGA_MODES[i].height * BGA_MODES[i].bpp; + tmp -= rqdProduct; + tmp = tmp < 0 ? -tmp : tmp; + factor = tmp * 100 / rqdProduct; + + #if DEBUG >= 2 + LogF("factor = %i\n", factor); + #endif + + if(factor < bestFactor) + { + bestFactor = factor; + best = i; + } + } + info->id = best; + info->width = BGA_MODES[best].width; + info->height = BGA_MODES[best].height; + info->bpp = BGA_MODES[best].bpp; + return best; +} + +/** + * \fn int BGA_int_ModeInfo(tVideo_IOCtl_Mode *info) + * \brief Get mode information + */ +int BGA_int_ModeInfo(tVideo_IOCtl_Mode *info) +{ + if(!info) return -1; + if(MM_GetPhysAddr((Uint)info) == 0) + return -1; + + if(info->id < 0 || info->id >= BGA_MODE_COUNT) return -1; + + info->width = BGA_MODES[info->id].width; + info->height = BGA_MODES[info->id].height; + info->bpp = BGA_MODES[info->id].bpp; + + return 1; +} + +/** + * \fn int BGA_int_MapFB(void *Dest) + * \brief Map the framebuffer into a process's space + * \param Dest User address to load to + */ +int BGA_int_MapFB(void *Dest) +{ + Uint i; + Uint pages; + + // Sanity Check + if((Uint)Dest > 0xC0000000) return 0; + if(BGA_MODES[giBGA_CurrentMode].bpp < 15) return 0; // Only non-pallete modes are supported + + // Count required pages + pages = (BGA_MODES[giBGA_CurrentMode].fbSize + 0xFFF) >> 12; + + // Check if there is space + for( i = 0; i < pages; i++ ) + { + if(MM_GetPhysAddr( (Uint)Dest + (i << 12) )) + return 0; + } + + // Map + for( i = 0; i < pages; i++ ) + MM_Map( (Uint)Dest + (i<<12), VBE_DISPI_LFB_PHYSICAL_ADDRESS + (i<<12) ); + + return 1; +} diff --git a/Kernel/drv/kb.c b/Kernel/drv/kb.c new file mode 100644 index 00000000..013e9536 --- /dev/null +++ b/Kernel/drv/kb.c @@ -0,0 +1,272 @@ +/* + * Acess2 + * PS2 Keyboard Driver + */ +#include +#include +#include +#include +#include +#include "kb_kbdus.h" + +// === CONSTANTS === +#define KB_BUFFER_SIZE 1024 + +// === PROTOTYPES === + int KB_Install(char **Arguments); +void KB_IRQHandler(); +void KB_AddBuffer(char ch); +Uint64 KB_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Dest); +void KB_UpdateLEDs(); + int KB_IOCtl(tVFS_Node *Node, int Id, void *Data); + +// === GLOBALS === +MODULE_DEFINE(0, 0x0100, PS2Keybard, KB_Install, NULL, NULL); +tDevFS_Driver gKB_DevInfo = { + NULL, "PS2Kb", + { + .NumACLs = 0, + .Size = -1, + .Read = KB_Read, + .IOCtl = KB_IOCtl + } +}; +tKeybardCallback gKB_Callback = NULL; +Uint8 **gpKB_Map = gpKBDUS; +Uint8 gbaKB_States[256]; + int gbKB_ShiftState = 0; + int gbKB_CapsState = 0; + int gbKB_KeyUp = 0; + int giKB_KeyLayer = 0; +//Uint64 giKB_ReadBase = 0; +Uint8 gaKB_Buffer[KB_BUFFER_SIZE]; //!< Keyboard Ring Buffer +volatile int giKB_InsertPoint = 0; //!< Writing location marker +volatile int giKB_ReadPoint = 0; //!< Reading location marker +volatile int giKB_InUse = 0; //!< Lock marker + +// === CODE === +/** + * \fn int KB_Install(char **Arguments) + */ +int KB_Install(char **Arguments) +{ + IRQ_AddHandler(1, KB_IRQHandler); + DevFS_AddDevice( &gKB_DevInfo ); + return 1; +} + +/** + * \fn void KB_IRQHandler() + * \brief Called on a keyboard IRQ + */ +void KB_IRQHandler() +{ + Uint8 scancode; + Uint32 ch; + int keyNum; + + //if( inportb(0x64) & 0x20 ) return; + + scancode = inb(0x60); // Read from the keyboard's data buffer + + // Ignore ACKs + if(scancode == 0xFA) { + //kb_lastChar = KB_ACK; + return; + } + + // Layer +1 + if(scancode == 0xE0) { + giKB_KeyLayer = 1; + return; + } + // Layer +2 + if(scancode == 0xE1) { + giKB_KeyLayer = 2; + return; + } + + #if KB_ALT_SCANCODES + if(scancode == 0xF0) + { + gbKB_KeyUp = 1; + return; + } + #else + if(scancode & 0x80) + { + scancode &= 0x7F; + gbKB_KeyUp = 1; + } + #endif + + // Translate + ch = gpKB_Map[giKB_KeyLayer][scancode]; + keyNum = giKB_KeyLayer * 256 + scancode; + // Check for unknown key + if(!ch && !gbKB_KeyUp) + Warning("UNK %i %x", giKB_KeyLayer, scancode); + + // Reset Layer + giKB_KeyLayer = 0; + + // Key Up? + if (gbKB_KeyUp) + { + gbKB_KeyUp = 0; + gbaKB_States[ keyNum ] = 0; // Unset key state flag + + if( !gbaKB_States[KEY_LSHIFT] && !gbaKB_States[KEY_RSHIFT] ) + gbKB_ShiftState = 0; + + KB_AddBuffer(KEY_KEYUP); + KB_AddBuffer(ch); + + return; + } + + // Set the bit relating to the key + gbaKB_States[keyNum] = 1; + if(ch == KEY_LSHIFT || ch == KEY_RSHIFT) + gbKB_ShiftState = 1; + + // Check for Caps Lock + if(ch == KEY_CAPSLOCK) { + gbKB_CapsState = !gbKB_CapsState; + KB_UpdateLEDs(); + } + + // Ignore Non-Printable Characters + if(ch == 0 || ch & 0x80) return; + + // Is shift pressed + if(gbKB_ShiftState ^ gbKB_CapsState) + { + switch(ch) + { + case '`': ch = '~'; break; + case '1': ch = '!'; break; + case '2': ch = '@'; break; + case '3': ch = '#'; break; + case '4': ch = '$'; break; + case '5': ch = '%'; break; + case '6': ch = '^'; break; + case '7': ch = '&'; break; + case '8': ch = '*'; break; + case '9': ch = '('; break; + case '0': ch = ')'; break; + case '-': ch = '_'; break; + case '=': ch = '+'; break; + case '[': ch = '{'; break; + case ']': ch = '}'; break; + case '\\': ch = '|'; break; + case ';': ch = ':'; break; + case '\'': ch = '"'; break; + case ',': ch = '<'; break; + case '.': ch = '>'; break; + case '/': ch = '?'; break; + default: + if('a' <= ch && ch <= 'z') + ch -= 0x20; + break; + } + } + + // --- Check for Kernel Magic Combos + if(gbaKB_States[KEY_LSHIFT] && gbaKB_States[KEY_RSHIFT]) + { + switch(ch) + { + case 'D': __asm__ __volatile__ ("xchg %bx, %bx"); break; + } + } + + if(gKB_Callback) + gKB_Callback(ch); +} + +/** + * \fn void KB_AddBuffer(char ch) + * \brief Add to the keyboard ring buffer + */ +void KB_AddBuffer(char ch) +{ + // Add to buffer + gaKB_Buffer[ giKB_InsertPoint++ ] = ch; + // - Wrap + if( giKB_InsertPoint == KB_BUFFER_SIZE ) giKB_InsertPoint = 0; + // - Force increment read pointer + if( giKB_InsertPoint == giKB_ReadPoint ) giKB_ReadPoint ++; +} + +/** + * \fn Uint64 KB_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Dest) + * \brief Read from the ring buffer + * \param Node Unused + * \param Offset Unused (Character Device) + * \param Length Number of bytes to read + * \param Dest Destination + */ +Uint64 KB_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Dest) +{ + int pos = 0; + char *dstbuf = Dest; + + if(giKB_InUse) return -1; + giKB_InUse = 1; + + while(giKB_ReadPoint != giKB_InsertPoint && pos < Length) + { + dstbuf[pos++] = gaKB_Buffer[ giKB_ReadPoint++ ]; + if( giKB_ReadPoint == KB_BUFFER_SIZE ) giKB_InsertPoint = 0; + } + + giKB_InUse = 0; + + return Length; +} + +/** + * \fn void KB_UpdateLEDs() + * \brief Updates the status of the keyboard LEDs + */ +void KB_UpdateLEDs() +{ + Uint8 leds; + + leds = (gbKB_CapsState ? 4 : 0); + + while( inb(0x64) & 2 ); // Wait for bit 2 to unset + outb(0x60, 0xED); // Send update command + + while( inb(0x64) & 2 ); // Wait for bit 2 to unset + outb(0x60, leds); +} + +/** + * \fn int KB_IOCtl(tVFS_Node *Node, int Id, void *Data) + * \brief Calls an IOCtl Command + */ +int KB_IOCtl(tVFS_Node *Node, int Id, void *Data) +{ + switch(Id) + { + case DRV_IOCTL_TYPE: return DRV_TYPE_KEYBOARD; + case DRV_IOCTL_IDENT: memcpy(Data, "KB\0\0", 4); return 1; + case DRV_IOCTL_VERSION: return 0x100; + case DRV_IOCTL_LOOKUP: return 0; + + // Sets the Keyboard Callback + case KB_IOCTL_SETCALLBACK: + // Sanity Check + if((Uint)Data < KERNEL_BASE) return 0; + // Can only be set once + if(gKB_Callback != NULL) return 0; + // Set Callback + gKB_Callback = Data; + return 1; + + default: + return 0; + } +} diff --git a/Kernel/drv/kb_kbdus.h b/Kernel/drv/kb_kbdus.h new file mode 100644 index 00000000..5f85ab1f --- /dev/null +++ b/Kernel/drv/kb_kbdus.h @@ -0,0 +1,39 @@ + +#ifndef _KBDUS_H +#define _KBDUS_H + +// - BASE (NO PREFIX) +Uint8 gpKBDUS1[256] = { + // 00 + 0, KEY_ESC, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', '\t', + // 10 + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', KEY_LCTRL, 'a', 's', + // 20 + 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';','\'', '`', KEY_LSHIFT,'\\', 'z', 'x', 'c', 'v', + // 30 + 'b','n','m',',','.','/',KEY_RSHIFT,KEY_KPSTAR,KEY_LALT,' ',KEY_CAPSLOCK,KEY_F1,KEY_F2,KEY_F3,KEY_F4, KEY_F5, + // 40 + KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_NUMLOCK, KEY_SCROLLLOCK, KEY_KPHOME, KEY_KPUP, KEY_KPPGUP, KEY_KPMINUS, KEY_KPLEFT, KEY_KP5, KEY_KPRIGHT, KEY_KPPLUS, KEY_KPEND, /* 4F9 - Keypad End key*/ + // 50 + KEY_KPDOWN, KEY_KPPGDN, KEY_KPINS, KEY_KPDEL, 0, 0, 0, KEY_F11, KEY_F12, 0 +}; +// - 0xE0 Prefixed +Uint8 gpKBDUS2[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F +/*00*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0-F +/*10*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_KPENTER, KEY_RCTRL, 0, 0, +/*20*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/*30*/ 0, 0, 0, 0, 0, KEY_KPSLASH, 0, 0, KEY_RALT, 0, 0, 0, 0, 0, 0, 0, +/*40*/ 0, 0, 0, 0, 0, 0, 0, KEY_HOME, KEY_UP, KEY_PGUP, 0, KEY_LEFT, 0, KEY_RIGHT, 0, KEY_END, +/*50*/ KEY_DOWN, KEY_PGDOWN, KEY_INS, KEY_DEL, 0, 0, 0, 0, 0, 0, KEY_WIN, 0, 0, KEY_MENU +}; +Uint8 gpKBDUS3[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F +/*00*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0-F +/*10*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_PAUSE +}; + + +Uint8 *gpKBDUS[3] = { gpKBDUS1, gpKBDUS2, gpKBDUS3 }; + +#endif diff --git a/Kernel/drv/pci.c b/Kernel/drv/pci.c new file mode 100644 index 00000000..e0a25b57 --- /dev/null +++ b/Kernel/drv/pci.c @@ -0,0 +1,556 @@ +/* +AcessOS/AcessBasic v0.1 +PCI Bus Driver +*/ +#include +#include +#include +#include + +#define DEBUG 0 +#define LIST_DEVICES 1 + +// === STRUCTURES === +typedef struct s_pciDevice { + Uint16 bus, slot, fcn; + Uint16 vendor, device; + union { + struct {Uint8 class, subclass;}; + Uint16 oc; + }; + Uint16 revision; + Uint32 ConfigCache[256/4]; + char Name[8]; + tVFS_Node Node; +} t_pciDevice; + +// === CONSTANTS === +#define SPACE_STEP 5 +#define MAX_RESERVED_PORT 0xD00 + +// === PROTOTYPES === + int PCI_Install(char **Arguments); +char *PCI_ReadDirRoot(tVFS_Node *node, int pos); +tVFS_Node *PCI_FindDirRoot(tVFS_Node *node, char *filename); +Uint64 PCI_ReadDevice(tVFS_Node *node, Uint64 pos, Uint64 length, void *buffer); + + int PCI_CountDevices(Uint16 vendor, Uint16 device, Uint16 fcn); + int PCI_GetDevice(Uint16 vendor, Uint16 device, Uint16 fcn, int idx); + int PCI_GetDeviceByClass(Uint16 class, Uint16 mask, int prev); +Uint8 PCI_GetIRQ(int id); +Uint32 PCI_GetBAR0(int id); +Uint32 PCI_GetBAR1(int id); +Uint32 PCI_GetBAR3(int id); +Uint32 PCI_GetBAR4(int id); +Uint32 PCI_GetBAR5(int id); +Uint16 PCI_AssignPort(int id, int bar, int count); + + int PCI_EnumDevice(Uint16 bus, Uint16 dev, Uint16 fcn, t_pciDevice *info); +Uint32 PCI_CfgReadDWord(Uint16 bus, Uint16 dev, Uint16 func, Uint16 offset); +void PCI_CfgWriteDWord(Uint16 bus, Uint16 dev, Uint16 func, Uint16 offset, Uint32 data); +Uint16 PCI_CfgReadWord(Uint16 bus, Uint16 dev, Uint16 func, Uint16 offset); +Uint8 PCI_CfgReadByte(Uint16 bus, Uint16 dev, Uint16 func, Uint16 offset); + +// === GLOBALS === +//MODULE_DEFINE(0, 0x0100, PCI, PCI_Install, NULL); + int giPCI_BusCount = 1; + int giPCI_InodeHandle = -1; + int giPCI_DeviceCount = 0; +t_pciDevice *gPCI_Devices = NULL; +tDevFS_Driver gPCI_DriverStruct = { + NULL, "pci", + { + .Flags = VFS_FFLAG_DIRECTORY, + .NumACLs = 1, + .ACLs = &gVFS_ACL_EveryoneRX, + .ReadDir = PCI_ReadDirRoot, + .FindDir = PCI_FindDirRoot + } +}; + Uint32 *gaPCI_PortBitmap = NULL; + +// === CODE === +/** + * \fn int PCI_Install() + * \brief Scan the PCI Bus for devices + */ +int PCI_Install(char **Arguments) +{ + int bus, dev, fcn, i; + int space = 0; + t_pciDevice devInfo; + void *tmpPtr = NULL; + + // Build Portmap + gaPCI_PortBitmap = malloc( 1 << 13 ); + memset( gaPCI_PortBitmap, 0, 1 << 13 ); + for( i = 0; i < MAX_RESERVED_PORT / 32; i ++ ) + gaPCI_PortBitmap[i] = -1; + for( i = 0; i < MAX_RESERVED_PORT % 32; i ++ ) + gaPCI_PortBitmap[MAX_RESERVED_PORT / 32] = 1 << i; + //LogF("Done.\n"); + + // Scan Busses + for( bus = 0; bus < giPCI_BusCount; bus++ ) + { + for( dev = 0; dev < 10; dev++ ) // 10 Devices per bus + { + for( fcn = 0; fcn < 8; fcn++ ) // 8 functions per device + { + // Check if the device/function exists + if(!PCI_EnumDevice(bus, dev, fcn, &devInfo)) + { + continue; + } + + if(giPCI_DeviceCount == space) + { + space += SPACE_STEP; + tmpPtr = realloc(gPCI_Devices, space*sizeof(t_pciDevice)); + if(tmpPtr == NULL) + break; + gPCI_Devices = tmpPtr; + } + if(devInfo.oc == PCI_OC_PCIBRIDGE) + { + #if LIST_DEVICES + Log("[PCI ] Bridge @ %i,%i:%i (0x%x:0x%x)", + bus, dev, fcn, devInfo.vendor, devInfo.device); + #endif + giPCI_BusCount++; + } + devInfo.Node.Inode = giPCI_DeviceCount; + memcpy(&gPCI_Devices[giPCI_DeviceCount], &devInfo, sizeof(t_pciDevice)); + giPCI_DeviceCount ++; + #if LIST_DEVICES + Log("[PCI ] Device %i,%i:%i => 0x%x:0x%x", + bus, dev, fcn, devInfo.vendor, devInfo.device); + #endif + + // WTF is this for? + if(fcn == 0) { + if( !(devInfo.ConfigCache[3] & 0x800000) ) + break; + } + } + if(tmpPtr != gPCI_Devices) + break; + } + if(tmpPtr != gPCI_Devices) + break; + } + tmpPtr = realloc(gPCI_Devices, giPCI_DeviceCount*sizeof(t_pciDevice)); + if(tmpPtr == NULL) + return 0; + gPCI_Devices = tmpPtr; + //LogF("Done.\n"); + + // Complete Driver Structure + gPCI_DriverStruct.RootNode.Size = giPCI_DeviceCount; + + // And add to DevFS + DevFS_AddDevice(&gPCI_DriverStruct); + + return 1; +} + +/** + * \fn char *PCI_ReadDirRoot(tVFS_Node *node, int pos) + * \brief Read from Root of PCI Driver +*/ +char *PCI_ReadDirRoot(tVFS_Node *node, int pos) +{ + if(pos < 0 || pos >= giPCI_DeviceCount) + return NULL; + + return gPCI_Devices[pos].Name; +} +/** + * \fn tVFS_Node *PCI_FindDirRoot(tVFS_Node *node, char *filename) + */ +tVFS_Node *PCI_FindDirRoot(tVFS_Node *node, char *filename) +{ + int bus,slot,fcn; + int i; + // Validate Filename (Pointer and length) + if(!filename || strlen(filename) != 7) + return NULL; + // Check for spacers + if(filename[2] != '.' || filename[5] != ':') + return NULL; + + // Get Information + if(filename[0] < '0' || filename[0] > '9') return NULL; + bus = (filename[0] - '0')*10; + if(filename[1] < '0' || filename[1] > '9') return NULL; + bus += filename[1] - '0'; + if(filename[3] < '0' || filename[3] > '9') return NULL; + slot = (filename[3] - '0')*10; + if(filename[4] < '0' || filename[4] > '9') return NULL; + slot += filename[4] - '0'; + if(filename[6] < '0' || filename[6] > '9') return NULL; + fcn = filename[6] - '0'; + + // Find Match + for(i=0;i 256 ) return 0; + + memcpy( + buffer, + (char*)gPCI_Devices[node->Inode].ConfigCache + pos, + length); + + return length; +} + +// --- Kernel Code Interface --- +/** + \fn int PCI_CountDevices(Uint16 vendor, Uint16 device, Uint16 fcn) + \brief Counts the devices with the specified codes + \param vendor Vendor ID + \param device Device ID + \param fcn Function ID +*/ +int PCI_CountDevices(Uint16 vendor, Uint16 device, Uint16 fcn) +{ + int i, ret=0; + for(i=0;i= giPCI_DeviceCount) + return 0; + return gPCI_Devices[id].ConfigCache[15]; + //return PCI_CfgReadByte( gPCI_Devices[id].bus, gPCI_Devices[id].slot, gPCI_Devices[id].fcn, 0x3C); +} + +/** + \fn Uint32 PCI_GetBAR0(int id) +*/ +Uint32 PCI_GetBAR0(int id) +{ + if(id < 0 || id >= giPCI_DeviceCount) + return 0; + return gPCI_Devices[id].ConfigCache[4]; +} + +/** + \fn Uint32 PCI_GetBAR1(int id) +*/ +Uint32 PCI_GetBAR1(int id) +{ + if(id < 0 || id >= giPCI_DeviceCount) + return 0; + return gPCI_Devices[id].ConfigCache[5]; +} + +/** + \fn Uint32 PCI_GetBAR2(int id) +*/ +Uint32 PCI_GetBAR2(int id) +{ + if(id < 0 || id >= giPCI_DeviceCount) + return 0; + return gPCI_Devices[id].ConfigCache[6]; +} + +/** + \fn Uint32 PCI_GetBAR3(int id) +*/ +Uint32 PCI_GetBAR3(int id) +{ + if(id < 0 || id >= giPCI_DeviceCount) + return 0; + return gPCI_Devices[id].ConfigCache[7]; +} + +/** + \fn Uint32 PCI_GetBAR4(int id) +*/ +Uint32 PCI_GetBAR4(int id) +{ + if(id < 0 || id >= giPCI_DeviceCount) + return 0; + return gPCI_Devices[id].ConfigCache[8]; +} + +/** + \fn Uint32 PCI_GetBAR5(int id) +*/ +Uint32 PCI_GetBAR5(int id) +{ + if(id < 0 || id >= giPCI_DeviceCount) + return 0; + return gPCI_Devices[id].ConfigCache[9]; +} + +Uint16 PCI_AssignPort(int id, int bar, int count) +{ + Uint16 portVals; + int gran=0; + int i, j; + t_pciDevice *dev; + + //LogF("PCI_AssignPort: (id=%i,bar=%i,count=%i)\n", id, bar, count); + + if(id < 0 || id >= giPCI_DeviceCount) return 0; + if(bar < 0 || bar > 5) return 0; + + dev = &gPCI_Devices[id]; + + PCI_CfgWriteDWord( dev->bus, dev->slot, dev->fcn, 0x10+bar*4, -1 ); + portVals = PCI_CfgReadDWord( dev->bus, dev->slot, dev->fcn, 0x10+bar*4 ); + dev->ConfigCache[4+bar] = portVals; + //LogF(" PCI_AssignPort: portVals = 0x%x\n", portVals); + + // Check for IO port + if( !(portVals & 1) ) return 0; + + // Mask out final bit + portVals &= ~1; + + // Get Granuality + __asm__ __volatile__ ("bsf %%eax, %%ecx" : "=c" (gran) : "a" (portVals) ); + gran = 1 << gran; + //LogF(" PCI_AssignPort: gran = 0x%x\n", gran); + + // Find free space + portVals = 0; + for( i = 0; i < 1<<16; i += gran ) + { + for( j = 0; j < count; j ++ ) + { + if( gaPCI_PortBitmap[ (i+j)>>5 ] & 1 << ((i+j)&0x1F) ) + break; + } + if(j == count) { + portVals = i; + break; + } + } + + if(portVals) + { + for( j = 0; j < count; j ++ ) + { + if( gaPCI_PortBitmap[ (portVals+j)>>5 ] |= 1 << ((portVals+j)&0x1F) ) + break; + } + PCI_CfgWriteDWord( dev->bus, dev->slot, dev->fcn, 0x10+bar*4, portVals|1 ); + dev->ConfigCache[4+bar] = portVals|1; + } + + // Return + //LogF("PCI_AssignPort: RETURN 0x%x\n", portVals); + return portVals; +} + +/** + * \fn int PCI_EnumDevice(Uint16 bus, Uint16 slot, Uint16 fcn, t_pciDevice *info) + */ +int PCI_EnumDevice(Uint16 bus, Uint16 slot, Uint16 fcn, t_pciDevice *info) +{ + Uint16 vendor; + int i; + Uint32 addr; + + vendor = PCI_CfgReadWord(bus, slot, fcn, 0x0|0); + if(vendor == 0xFFFF) // Invalid Device + return 0; + + info->bus = bus; + info->slot = slot; + info->fcn = fcn; + info->vendor = vendor; + info->device = PCI_CfgReadWord(bus, slot, fcn, 0x0|2); + info->revision = PCI_CfgReadWord(bus, slot, fcn, 0x8|0); + info->oc = PCI_CfgReadWord(bus, slot, fcn, 0x8|2); + + // Load Config Bytes + addr = 0x80000000 | ((Uint)bus<<16) | ((Uint)slot<<11) | ((Uint)fcn<<8); + for(i=0;i<256/4;i++) + { + #if 1 + outd(0xCF8, addr); + info->ConfigCache[i] = ind(0xCFC); + addr += 4; + #else + info->ConfigCache[i] = PCI_CfgReadDWord(bus, slot, fcn, i*4); + #endif + } + + //#if LIST_DEVICES + //Log("BAR0 0x%08x BAR1 0x%08x BAR2 0x%08x", info->ConfigCache[4], info->ConfigCache[5], info->ConfigCache[6]); + //Log("BAR3 0x%08x BAR4 0x%08x BAR5 0x%08x", info->ConfigCache[7], info->ConfigCache[8], info->ConfigCache[9]); + //Log("Class: 0x%04x", info->oc); + //#endif + + // Make node name + info->Name[0] = '0' + bus/10; + info->Name[1] = '0' + bus%10; + info->Name[2] = '.'; + info->Name[3] = '0' + slot/10; + info->Name[4] = '0' + slot%10; + info->Name[5] = '.'; + info->Name[6] = '0' + fcn; + info->Name[7] = '\0'; + + // Create VFS Node + memset( &info->Node, 0, sizeof(tVFS_Node) ); + info->Node.Size = 256; + + info->Node.NumACLs = 1; + info->Node.ACLs = &gVFS_ACL_EveryoneRO; + + info->Node.Read = PCI_ReadDevice; + + return 1; +} + +Uint32 PCI_CfgReadDWord(Uint16 bus, Uint16 dev, Uint16 func, Uint16 offset) +{ + Uint32 address; + Uint32 data; + + bus &= 0xFF; // 8 Bits + dev &= 0x1F; // 5 Bits + func &= 0x7; // 3 Bits + offset &= 0xFF; // 8 Bits + + address = 0x80000000 | ((Uint)bus<<16) | ((Uint)dev<<11) | ((Uint)func<<8) | (offset&0xFC); + outd(0xCF8, address); + + data = ind(0xCFC); + return (Uint32)data; +} +void PCI_CfgWriteDWord(Uint16 bus, Uint16 dev, Uint16 func, Uint16 offset, Uint32 data) +{ + Uint32 address; + + bus &= 0xFF; // 8 Bits + dev &= 0x1F; // 5 Bits + func &= 0x7; // 3 Bits + offset &= 0xFF; // 8 Bits + + address = 0x80000000 | ((Uint)bus<<16) | ((Uint)dev<<11) | ((Uint)func<<8) | (offset&0xFC); + outd(0xCF8, address); + outd(0xCFC, data); +} +Uint16 PCI_CfgReadWord(Uint16 bus, Uint16 dev, Uint16 func, Uint16 offset) +{ + Uint32 data; + + bus &= 0xFF; // 8 Bits + dev &= 0x1F; // 5 Bits + func &= 0x7; // 3 Bits + offset &= 0xFF; // 8 Bits + + //LogF("PCI_CfgReadWord: (bus=0x%x,dev=0x%x,func=%x,offset=0x%x)\n", bus, dev, func, offset); + + outd(0xCF8, + 0x80000000 | ((Uint)bus<<16) | ((Uint)dev<<11) | ((Uint)func<<8) | (offset&0xFC) ); + + data = ind(0xCFC); + data >>= (offset&2)*8; //Allow Access to Upper Word + //LogF("PCI_CfgReadWord: RETURN 0x%x\n", data&0xFFFF); + return (Uint16)data; +} + +Uint8 PCI_CfgReadByte(Uint16 bus, Uint16 dev, Uint16 func, Uint16 offset) +{ + Uint32 address; + Uint32 data; + + bus &= 0xFF; // 8 Bits + dev &= 0x1F; // 4 Bits + func &= 0x7; // 3 Bits + offset &= 0xFF; // 8 Bits + + address = 0x80000000 | ((Uint)bus<<16) | ((Uint)dev<<11) | ((Uint)func<<8) | (offset&0xFC); + outd(0xCF8, address); + + data = ind(0xCFC); + data >>= (offset&3)*8; //Allow Access to Upper Word + return (Uint8)data; +} + + +// === EXPORTS === +/* +EXPORT(PCI_CountDevices); +EXPORT(PCI_GetDevice); +EXPORT(PCI_AssignPort); +EXPORT(PCI_GetIRQ); +*/ diff --git a/Kernel/drv/vga.c b/Kernel/drv/vga.c new file mode 100644 index 00000000..93f44f07 --- /dev/null +++ b/Kernel/drv/vga.c @@ -0,0 +1,174 @@ +/* + * Acess2 VGA Controller Driver + */ +#include +#include +#include +#include + +// === PROTOTYPES === + int VGA_Install(char **Arguments); +Uint64 VGA_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); + int VGA_IOCtl(tVFS_Node *Node, int Id, void *Data); +Uint16 VGA_int_GetWord(tVT_Char *Char); + +// === GLOBALS === +MODULE_DEFINE(0, 0x000A, VGA, VGA_Install, NULL, NULL); +tDevFS_Driver gVGA_DevInfo = { + NULL, "VGA", + { + .NumACLs = 0, + .Size = 80*25*sizeof(tVT_Char), + //.Read = VGA_Read, + .Write = VGA_Write, + .IOCtl = VGA_IOCtl + } +}; +Uint16 *gVGA_Framebuffer = (void*)( KERNEL_BASE|0xB8000 ); + +// === CODE === +/** + * \fn int VGA_Install(char **Arguments) + */ +int VGA_Install(char **Arguments) +{ + Uint8 byte; + + // Enable Bright Backgrounds + inb(0x3DA); // Reset flipflop + outb(0x3C0, 0x30); // Index 0x10, PAS + byte = inb(0x3C1); + byte &= ~8; // Disable Blink + outb(0x3C0, byte); // Write value + + + // Install DevFS + DevFS_AddDevice( &gVGA_DevInfo ); + + return 1; +} + +/** + * \fn Uint64 VGA_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) + * \brief Writes a string of bytes to the VGA controller + */ +Uint64 VGA_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + int num = Length / sizeof(tVT_Char); + int ofs = Offset / sizeof(tVT_Char); + int i = 0; + tVT_Char *chars = Buffer; + Uint16 word; + + //ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); + + for( ; num--; i ++, ofs ++) + { + word = VGA_int_GetWord( &chars[i] ); + gVGA_Framebuffer[ ofs ] = word; + } + + //LEAVE('X', Length); + return Length; +} + +/** + * \fn int VGA_IOCtl(tVFS_Node *Node, int Id, void *Data) + * \brief IO Control Call + */ +int VGA_IOCtl(tVFS_Node *Node, int Id, void *Data) +{ + switch(Id) + { + case DRV_IOCTL_TYPE: return DRV_TYPE_VIDEO; + case DRV_IOCTL_IDENT: memcpy(Data, "VGA\0", 4); return 1; + case DRV_IOCTL_VERSION: *(int*)Data = 50; return 1; + case DRV_IOCTL_LOOKUP: return 0; + + case VIDEO_IOCTL_SETMODE: return 0; // Ignored (Text Only ATM) + case VIDEO_IOCTL_GETMODE: return 0; // Mode 0 only + case VIDEO_IOCTL_FINDMODE: return 0; // Text Only! + case VIDEO_IOCTL_MODEINFO: + if( ((tVideo_IOCtl_Mode*)Data)->id != 0) return 0; + ((tVideo_IOCtl_Mode*)Data)->width = 80; + ((tVideo_IOCtl_Mode*)Data)->height = 25; + ((tVideo_IOCtl_Mode*)Data)->bpp = 4; + return 1; + } + return 0; +} + +/** + * \fn Uint8 VGA_int_GetColourNibble(Uint16 col) + * \brief Converts a 12-bit colour into a VGA 4-bit colour + */ +Uint8 VGA_int_GetColourNibble(Uint16 col) +{ + Uint8 ret = 0; + int bright = 0; + + col = col & 0xCCC; + col = ((col>>2)&3) | ((col>>4)&0xC) | ((col>>6)&0x30); + bright = ( (col & 2 ? 1 : 0) + (col & 8 ? 1 : 0) + (col & 32 ? 1 : 0) ) / 2; + + + switch(col) + { + // Black + case 0x00: ret = 0x0; break; + // Dark Grey + case 0x15: ret = 0x8; break; + // Blues + case 0x01: + case 0x02: ret = 0x1; break; + case 0x03: ret = 0x9; break; + // Green + case 0x04: + case 0x08: ret = 0x2; break; + case 0x0C: ret = 0xA; break; + // Reds + case 0x10: + case 0x20: ret = 0x4; break; + case 0x30: ret = 0xC; break; + // Light Grey + case 0x2A: ret = 0x7; break; + // White + case 0x3F: ret = 0xF; break; + + default: + ret |= (col & 0x03 ? 1 : 0); + ret |= (col & 0x0C ? 2 : 0); + ret |= (col & 0x30 ? 4 : 0); + ret |= (bright ? 8 : 0); + break; + } + return ret; +} + +/** + * \fn Uint16 VGA_int_GetWord(tVT_Char *Char) + * \brief Convers a character structure to a VGA character word + */ +Uint16 VGA_int_GetWord(tVT_Char *Char) +{ + Uint16 ret; + Uint16 col; + + // Get Character + if(Char->Ch < 128) + ret = Char->Ch; + else { + switch(Char->Ch) + { + default: ret = 0; break; + } + } + + col = VGA_int_GetColourNibble(Char->BGCol); + ret |= col << 12; + + col = VGA_int_GetColourNibble(Char->FGCol); + ret |= col << 8; + + return ret; +} diff --git a/Kernel/drv/vterm.c b/Kernel/drv/vterm.c new file mode 100644 index 00000000..9244619e --- /dev/null +++ b/Kernel/drv/vterm.c @@ -0,0 +1,531 @@ +/* + * Acess2 Virtual Terminal Driver + */ +#include +#include +#include +#include + +// === CONSTANTS === +#define NUM_VTS 4 +#define MAX_INPUT_BYTES 64 +#define VT_SCROLLBACK 4 // 4 Screens of text +#define DEFAULT_OUTPUT "/Devices/VGA" +#define DEFAULT_INPUT "/Devices/PS2Keyboard" +#define DEFAULT_WIDTH 80 +#define DEFAULT_HEIGHT 25 +#define DEFAULT_COLOUR (VT_COL_BLACK|(VT_COL_WHITE<<16)) + +enum eVT_Modes { + VT_MODE_TEXT, + VT_MODE_8BPP, + VT_MODE_16BPP, + VT_MODE_24BPP, + VT_MODE_32BPP, + NUM_VT_MODES +}; + +// === TYPES === +typedef struct { + int Mode; + int Width, Height; + int ViewPos, WritePos; + Uint32 CurColour; + char Name[2]; + int NumInputBytes; + Uint8 InputBuffer[MAX_INPUT_BYTES]; + union { + tVT_Char *Text; + Uint32 *Buffer; + }; + tVFS_Node Node; +} tVTerm; + +// === PROTOTYPES === + int VT_Install(char **Arguments); +char *VT_ReadDir(tVFS_Node *Node, int Pos); +tVFS_Node *VT_FindDir(tVFS_Node *Node, char *Name); +Uint64 VT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); +Uint64 VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); + int VT_IOCtl(tVFS_Node *Node, int Id, void *Data); +void VT_int_PutString(tVTerm *Term, Uint8 *Buffer, Uint Count); + int VT_int_ParseEscape(tVTerm *Term, char *Buffer); +void VT_int_PutChar(tVTerm *Term, Uint32 Ch); +void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll ); + +// === CONSTANTS === +const Uint16 caVT100Colours[] = { + VT_COL_BLACK, 0, 0, 0, 0, 0, 0, VT_COL_LTGREY, + VT_COL_GREY, 0, 0, 0, 0, 0, 0, VT_COL_WHITE + }; + +// === GLOBALS === +MODULE_DEFINE(0, 0x0032, VTerm, VT_Install, NULL, NULL); +tDevFS_Driver gVT_DrvInfo = { + NULL, "VTerm", + { + .Flags = VFS_FFLAG_DIRECTORY, + .Inode = -1, + .NumACLs = 0, + .ReadDir = VT_ReadDir, + .FindDir = VT_FindDir + } +}; +tVTerm gVT_Terminals[NUM_VTS]; +char *gsVT_OutputDevice = NULL; +char *gsVT_InputDevice = NULL; + int giVT_OutputDevHandle = -2; + int giVT_InputDevHandle = -2; + int giVT_CurrentTerminal = 0; + +// === CODE === +/** + * \fn int VT_Install(char **Arguments) + */ +int VT_Install(char **Arguments) +{ + char **args = Arguments; + char *arg; + int i; + + // Scan Arguments + if(Arguments) + { + for( ; (arg = *args); args++ ) + { + if(arg[0] != '-') continue; + + switch(arg[1]) + { + // Set output device + case 'o': + if(args[1] == NULL) break; + if(gsVT_OutputDevice) free(gsVT_OutputDevice); + gsVT_OutputDevice = malloc(strlen(args[1])+1); + strcpy(gsVT_OutputDevice, args[1]); + args ++; + break; + + // Set input device + case 'i': + if(args[1] == NULL) break; + if(gsVT_InputDevice) free(gsVT_InputDevice); + gsVT_InputDevice = malloc(strlen(args[1])+1); + strcpy(gsVT_InputDevice, args[1]); + args ++; + break; + + } + } + } + + // Apply Defaults + if(!gsVT_OutputDevice) gsVT_OutputDevice = DEFAULT_OUTPUT; + if(!gsVT_InputDevice) gsVT_InputDevice = DEFAULT_INPUT; + + LOG("Using '%s' as output", gsVT_OutputDevice); + LOG("Using '%s' as input", gsVT_InputDevice); + + // Create Nodes + for( i = 0; i < NUM_VTS; i++ ) + { + gVT_Terminals[i].Mode = VT_MODE_TEXT; + gVT_Terminals[i].Width = DEFAULT_WIDTH; + gVT_Terminals[i].Height = DEFAULT_HEIGHT; + gVT_Terminals[i].CurColour = DEFAULT_COLOUR; + gVT_Terminals[i].WritePos = 0; + gVT_Terminals[i].ViewPos = 0; + + gVT_Terminals[i].Buffer = malloc( DEFAULT_WIDTH*DEFAULT_HEIGHT*sizeof(tVT_Char) ); + memset( gVT_Terminals[i].Buffer, 0, DEFAULT_WIDTH*DEFAULT_HEIGHT*sizeof(tVT_Char) ); + + gVT_Terminals[i].Name[0] = '0'+i; + gVT_Terminals[i].Name[1] = '\0'; + gVT_Terminals[i].Node.Inode = i; + gVT_Terminals[i].Node.NumACLs = 0; // Only root can open virtual terminals + + gVT_Terminals[i].Node.Read = VT_Read; + gVT_Terminals[i].Node.Write = VT_Write; + gVT_Terminals[i].Node.IOCtl = VT_IOCtl; + } + + // Add to DevFS + DevFS_AddDevice( &gVT_DrvInfo ); + + return 0; +} + +/** + * \fn void VT_InitOutput() + * \brief Initialise Video Output + */ +void VT_InitOutput() +{ + giVT_OutputDevHandle = VFS_Open(gsVT_OutputDevice, VFS_OPENFLAG_WRITE); + LOG("giVT_OutputDevHandle = %x\n", giVT_OutputDevHandle); +} + +/** + * \fn void VT_InitInput() + * \brief Initialises the input + */ +void VT_InitInput() +{ + giVT_InputDevHandle = VFS_Open(gsVT_InputDevice, VFS_OPENFLAG_READ); + LOG("giVT_InputDevHandle = %x\n", giVT_InputDevHandle); +} + +/** + * \fn char *VT_ReadDir(tVFS_Node *Node, int Pos) + * \brief Read from the VTerm Directory + */ +char *VT_ReadDir(tVFS_Node *Node, int Pos) +{ + if(Pos < 0) return NULL; + if(Pos >= NUM_VTS) return NULL; + return gVT_Terminals[Pos].Name; +} + +/** + * \fn tVFS_Node *VT_FindDir(tVFS_Node *Node, char *Name) + * \brief Find an item in the VTerm directory + */ +tVFS_Node *VT_FindDir(tVFS_Node *Node, char *Name) +{ + int num; + + //ENTER("pNode sName", Node, Name); + + // Open the input and output files if needed + if(giVT_OutputDevHandle == -2) VT_InitOutput(); + if(giVT_InputDevHandle == -2) VT_InitInput(); + + // Sanity check name + if(Name[0] < '0' || Name[0] > '9' || Name[1] != '\0') { + LEAVE('n'); + return NULL; + } + // Get index + num = Name[0] - '0'; + if(num >= NUM_VTS) { + LEAVE('n'); + return NULL; + } + // Return node + //LEAVE('p', &gVT_Terminals[num].Node); + return &gVT_Terminals[num].Node; +} + +/** + * \fn Uint64 VT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) + * \brief Read from a virtual terminal + */ +Uint64 VT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); + LEAVE('i', 0); + return 0; +} + +/** + * \fn Uint64 VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) + * \brief Write to a virtual terminal + */ +Uint64 VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + tVTerm *term = &gVT_Terminals[ Node->Inode ]; + + //ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer); + + // Write + switch( term->Mode ) + { + case VT_MODE_TEXT: + VT_int_PutString(term, Buffer, Length); + break; + } + //LEAVE('i', 0); + return 0; +} + +/** + * \fn int VT_IOCtl(tVFS_Node *Node, int Id, void *Data) + * \brief Call an IO Control on a virtual terminal + */ +int VT_IOCtl(tVFS_Node *Node, int Id, void *Data) +{ + return 0; +} + +/** + * \fn void VT_int_PutString(tVTerm *Term, Uint8 *Buffer, Uint Count) + * \brief Print a string to the Virtual Terminal + */ +void VT_int_PutString(tVTerm *Term, Uint8 *Buffer, Uint Count) +{ + Uint32 val; + int i; + for( i = 0; i < Count; i++ ) + { + if( Buffer[i] == 0x1B ) // Escape Sequence + { + i += VT_int_ParseEscape(Term, (char*)&Buffer[i]); + continue; + } + + if( Buffer[i] < 128 ) // Plain ASCII + VT_int_PutChar(Term, Buffer[i]); + else { // UTF-8 + i += ReadUTF8(&Buffer[i], &val); + VT_int_PutChar(Term, val); + } + } +} + +/** + * \fn int VT_int_ParseEscape(tVTerm *Term, char *Buffer) + * \brief Parses a VT100 Escape code + */ +int VT_int_ParseEscape(tVTerm *Term, char *Buffer) +{ + char c; + int argc = 0, j = 1; + int args[4] = {0,0,0,0}; + + switch(Buffer[0]) { + //Large Code + case '[': + // Get Arguments + c = Buffer[1]; + do { + while('0' <= c && c <= '9') { + args[argc] *= 10; + args[argc] += c-'0'; + c = Buffer[++j]; + } + argc ++; + } while(c == ';'); + + // Get string (what does this do?) + if(c == '"') { + c = Buffer[++j]; + while(c != '"') + c = Buffer[++j]; + } + + // Get Command + if( ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')) + { + switch(c) { + //Clear By Line + case 'J': + // Clear Screen + if(args[0] == 2) { + memset(Term->Text, 0, Term->Width*Term->Height*VT_SCROLLBACK*sizeof(tVT_Char)); + Term->WritePos = 0; + Term->ViewPos = 0; + } + break; + // Set Font flags + case 'm': + for( ; argc--; ) + { + // Flags + if( 0 <= args[argc] && args[argc] <= 8) + { + switch(args[argc]) + { + case 0: Term->CurColour = DEFAULT_COLOUR; break; // Reset + case 1: Term->CurColour |= 0x80000000; break; // Bright + case 2: Term->CurColour &= ~0x80000000; break; // Dim + } + } + // Foreground Colour + else if(30 <= args[argc] && args[argc] <= 37) { + Term->CurColour &= 0xF000FFFF; + Term->CurColour |= (Uint32)caVT100Colours[ args[argc]-30+(Term->CurColour>>28) ] << 16; + } + // Background Colour + else if(40 <= args[argc] && args[argc] <= 47) { + Term->CurColour &= 0xFFFF8000; + Term->CurColour |= caVT100Colours[ args[argc]-40+((Term->CurColour>>12)&15) ]; + } + } + break; + } + } + break; + + default: + break; + } + + return j + 1; +} + +/** + * \fn void VT_int_PutChar(tVTerm *Term, Uint32 Ch) + * \brief Write a single character to a VTerm + */ +void VT_int_PutChar(tVTerm *Term, Uint32 Ch) +{ + int i; + //ENTER("pTerm xCh", Term, Ch); + //LOG("Term = {WritePos:%i, ViewPos:%i}\n", Term->WritePos, Term->ViewPos); + + switch(Ch) + { + case '\n': + Term->WritePos += Term->Width; + case '\r': + Term->WritePos -= Term->WritePos % Term->Width; + break; + + case '\t': + do { + Term->Text[ Term->WritePos ].Ch = '\t'; + Term->Text[ Term->WritePos ].Colour = Term->CurColour; + Term->WritePos ++; + } while(Term->WritePos & 7); + break; + + case '\b': + // Backspace is invalid at Offset 0 + if(Term->WritePos == 0) break; + + Term->WritePos --; + // Singe Character + if(Term->Text[ Term->WritePos ].Ch != '\t') { + Term->Text[ Term->WritePos ].Ch = 0; + Term->Text[ Term->WritePos ].Colour = Term->CurColour; + break; + } + // Tab + i = 7; // Limit it to 8 + do { + Term->Text[ Term->WritePos ].Ch = 0; + Term->Text[ Term->WritePos ].Colour = Term->CurColour; + Term->WritePos --; + } while(Term->WritePos && i-- && Term->Text[ Term->WritePos ].Ch == '\t'); + break; + + default: + Term->Text[ Term->WritePos ].Ch = Ch; + Term->Text[ Term->WritePos ].Colour = Term->CurColour; + Term->WritePos ++; + break; + } + + if(Term->WritePos >= Term->Width*Term->Height*VT_SCROLLBACK) + { + int base, i; + Term->WritePos -= Term->Width; + + // Update view position + base = Term->Width*Term->Height*(VT_SCROLLBACK-1); + if(Term->ViewPos < base) Term->ViewPos += Term->Width; + if(Term->ViewPos > base) Term->ViewPos = base; + + // Scroll terminal cache + base = Term->Width*(Term->Height*VT_SCROLLBACK-1); + + // Scroll Back + memcpy( Term->Text, &Term->Text[Term->Width], base*sizeof(tVT_Char) ); + + // Clear last row + for( i = 0; i < Term->Width; i ++ ) + { + Term->Text[ base + i ].Ch = 0; + Term->Text[ base + i ].Colour = Term->CurColour; + } + + VT_int_UpdateScreen( Term, 1 ); + } + else + VT_int_UpdateScreen( Term, 0 ); + + //LEAVE('-'); +} + +/** + * \fn void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll ) + * \brief Updates the video framebuffer + */ +void VT_int_UpdateScreen( tVTerm *Term, int UpdateAll ) +{ + if(UpdateAll) { + VFS_WriteAt( + giVT_OutputDevHandle, + 0, + Term->Width*Term->Height*sizeof(tVT_Char), + &Term->Text[Term->ViewPos] + ); + } else { + VFS_WriteAt( + giVT_OutputDevHandle, + Term->ViewPos*sizeof(tVT_Char), + Term->Width*sizeof(tVT_Char), + &Term->Text[Term->ViewPos] + ); + } +} + +// --- +// Font Render +// --- +#define MONOSPACE_FONT 10816 + +#if MONOSPACE_FONT == 10808 // 8x8 +# include "vterm_font_8x8.h" +#elif MONOSPACE_FONT == 10816 // 8x16 +# include "vterm_font_8x16.h" +#endif + + +int VT_Font_GetWidth(Uint32 Codepoint) +{ + return FONT_WIDTH; +} +int VT_Font_GetHeight(Uint32 Codepoint) +{ + return FONT_HEIGHT; +} + +void VT_Font_Render(Uint32 Codepoint, void *Buffer, Uint32 BGC, Uint32 FGC) +{ +// Uint8 *font; + +} + +/** + * \fn Uint8 *VT_Font_GetChar(Uint32 Codepoint) + * \brief Gets an index into the font array given a Unicode Codepoint + * \note See http://en.wikipedia.org/wiki/CP437 + */ +Uint8 *VT_Font_GetChar(Uint32 Codepoint) +{ + int index = 0; + if(Codepoint < 128) + return &VTermFont[Codepoint*FONT_HEIGHT]; + switch(Codepoint) + { + case 0xC7: index = 128; break; // Ç + case 0xFC: index = 129; break; // ü + case 0xE9: index = 130; break; // é + case 0xE2: index = 131; break; // â + case 0xE4: index = 132; break; // ä + case 0xE0: index = 133; break; // à + case 0xE5: index = 134; break; // å + case 0xE7: index = 135; break; // ç + case 0xEA: index = 136; break; // ê + case 0xEB: index = 137; break; // ë + case 0xE8: index = 138; break; // è + case 0xEF: index = 139; break; // ï + case 0xEE: index = 140; break; // î + case 0xEC: index = 141; break; // ì + case 0xC4: index = 142; break; // Ä + case 0xC5: index = 143; break; // Å + } + + return &VTermFont[index*FONT_HEIGHT]; +} diff --git a/Kernel/drv/vterm_font_8x16.h b/Kernel/drv/vterm_font_8x16.h new file mode 100644 index 00000000..7c613325 --- /dev/null +++ b/Kernel/drv/vterm_font_8x16.h @@ -0,0 +1,265 @@ +/* + * Taken from http://cvs.savannah.gnu.org/viewvc/vgabios/vgafonts.h?root=vgabios&view=markup + * Altered for Acess2 + */ +#define FONT_WIDTH 8 +#define FONT_HEIGHT 16 +static Uint8 VTermFont[256*16]= +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, 0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0xff, 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x1e, 0x0e, 0x1a, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30, 0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfe, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0xfe, 0x3e, 0x1e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, 0x06, 0x86, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, 0x30, 0x60, 0xc6, 0x86, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc3, 0xc3, 0xdb, 0xdb, 0xc3, 0xc3, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xde, 0xde, 0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x66, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe6, 0x66, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xe7, 0xff, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, 0x0c, 0x0e, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x38, 0x0c, 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xdb, 0x99, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x3c, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xc3, 0x86, 0x0c, 0x18, 0x30, 0x60, 0xc1, 0xc3, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xcc, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, 0x00, + 0x00, 0x00, 0xcc, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xcc, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x38, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x0c, 0x06, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x6c, 0x38, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x1b, 0x7e, 0xd8, 0xdc, 0x77, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3e, 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00, + 0x00, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x7e, 0xc3, 0xc0, 0xc0, 0xc0, 0xc3, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0x66, 0x66, 0x7c, 0x62, 0x66, 0x6f, 0x66, 0x66, 0x66, 0xf3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xc0, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x60, 0xce, 0x9b, 0x06, 0x0c, 0x1f, 0x00, 0x00, + 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xce, 0x96, 0x3e, 0x06, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, + 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, + 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0xd8, 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0x6c, 0xee, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; diff --git a/Kernel/drv/vterm_font_8x8.h b/Kernel/drv/vterm_font_8x8.h new file mode 100644 index 00000000..2a5311d3 --- /dev/null +++ b/Kernel/drv/vterm_font_8x8.h @@ -0,0 +1,265 @@ +/* + * Taken from http://cvs.savannah.gnu.org/viewvc/vgabios/vgafonts.h?root=vgabios&view=markup + * Altered for Acess2 + */ +#define FONT_WIDTH 8 +#define FONT_HEIGHT 8 +static Uint8 VTermFont[256*8]= +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e, + 0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e, + 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, + 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, + 0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c, + 0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c, + 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, + 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, + 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, + 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, + 0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78, + 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, + 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0, + 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0, + 0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99, + 0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00, + 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00, + 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00, + 0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00, + 0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78, + 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00, + 0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff, + 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, + 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, + 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, + 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, + 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, + 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00, + 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, + 0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00, + 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00, + 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00, + 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00, + 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00, + 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, + 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60, + 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, + 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, + 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00, + 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00, + 0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00, + 0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00, + 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00, + 0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00, + 0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00, + 0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00, + 0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00, + 0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00, + 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00, + 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60, + 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00, + 0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00, + 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00, + 0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00, + 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00, + 0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00, + 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00, + 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00, + 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, + 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00, + 0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00, + 0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00, + 0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00, + 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00, + 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00, + 0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, + 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00, + 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00, + 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, + 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, + 0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00, + 0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00, + 0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00, + 0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00, + 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00, + 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00, + 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00, + 0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00, + 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00, + 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00, + 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00, + 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00, + 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00, + 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, + 0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, + 0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00, + 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, + 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00, + 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00, + 0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0, + 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e, + 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00, + 0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00, + 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, + 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00, + 0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00, + 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00, + 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, + 0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00, + 0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00, + 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, + 0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00, + 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00, + 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x18, 0x0c, 0x78, + 0x00, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00, + 0x1c, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, + 0x7e, 0xc3, 0x3c, 0x06, 0x3e, 0x66, 0x3f, 0x00, + 0xcc, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00, + 0xe0, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00, + 0x30, 0x30, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00, + 0x00, 0x00, 0x78, 0xc0, 0xc0, 0x78, 0x0c, 0x38, + 0x7e, 0xc3, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00, + 0xcc, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, + 0xe0, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, + 0xcc, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x7c, 0xc6, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00, + 0xe0, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0xc6, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, + 0x30, 0x30, 0x00, 0x78, 0xcc, 0xfc, 0xcc, 0x00, + 0x1c, 0x00, 0xfc, 0x60, 0x78, 0x60, 0xfc, 0x00, + 0x00, 0x00, 0x7f, 0x0c, 0x7f, 0xcc, 0x7f, 0x00, + 0x3e, 0x6c, 0xcc, 0xfe, 0xcc, 0xcc, 0xce, 0x00, + 0x78, 0xcc, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, + 0x00, 0xcc, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, + 0x00, 0xe0, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, + 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00, + 0x00, 0xe0, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00, + 0x00, 0xcc, 0x00, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, + 0xc3, 0x18, 0x3c, 0x66, 0x66, 0x3c, 0x18, 0x00, + 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, + 0x18, 0x18, 0x7e, 0xc0, 0xc0, 0x7e, 0x18, 0x18, + 0x38, 0x6c, 0x64, 0xf0, 0x60, 0xe6, 0xfc, 0x00, + 0xcc, 0xcc, 0x78, 0xfc, 0x30, 0xfc, 0x30, 0x30, + 0xf8, 0xcc, 0xcc, 0xfa, 0xc6, 0xcf, 0xc6, 0xc7, + 0x0e, 0x1b, 0x18, 0x3c, 0x18, 0x18, 0xd8, 0x70, + 0x1c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00, + 0x38, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x00, 0x1c, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, + 0x00, 0x1c, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00, + 0x00, 0xf8, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0x00, + 0xfc, 0x00, 0xcc, 0xec, 0xfc, 0xdc, 0xcc, 0x00, + 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, + 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, + 0x30, 0x00, 0x30, 0x60, 0xc0, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0x00, 0xfc, 0xc0, 0xc0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfc, 0x0c, 0x0c, 0x00, 0x00, + 0xc3, 0xc6, 0xcc, 0xde, 0x33, 0x66, 0xcc, 0x0f, + 0xc3, 0xc6, 0xcc, 0xdb, 0x37, 0x6f, 0xcf, 0x03, + 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x00, 0x33, 0x66, 0xcc, 0x66, 0x33, 0x00, 0x00, + 0x00, 0xcc, 0x66, 0x33, 0x66, 0xcc, 0x00, 0x00, + 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, + 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, + 0xdb, 0x77, 0xdb, 0xee, 0xdb, 0x77, 0xdb, 0xee, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, + 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, + 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, + 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36, + 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, + 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, + 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, + 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, + 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, + 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xdc, 0xc8, 0xdc, 0x76, 0x00, + 0x00, 0x78, 0xcc, 0xf8, 0xcc, 0xf8, 0xc0, 0xc0, + 0x00, 0xfc, 0xcc, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, + 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, + 0xfc, 0xcc, 0x60, 0x30, 0x60, 0xcc, 0xfc, 0x00, + 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0x70, 0x00, + 0x00, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0xc0, + 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x00, + 0xfc, 0x30, 0x78, 0xcc, 0xcc, 0x78, 0x30, 0xfc, + 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0x6c, 0x38, 0x00, + 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x6c, 0xee, 0x00, + 0x1c, 0x30, 0x18, 0x7c, 0xcc, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0x7e, 0x00, 0x00, + 0x06, 0x0c, 0x7e, 0xdb, 0xdb, 0x7e, 0x60, 0xc0, + 0x38, 0x60, 0xc0, 0xf8, 0xc0, 0x60, 0x38, 0x00, + 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, + 0x00, 0xfc, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0x00, + 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0xfc, 0x00, + 0x60, 0x30, 0x18, 0x30, 0x60, 0x00, 0xfc, 0x00, + 0x18, 0x30, 0x60, 0x30, 0x18, 0x00, 0xfc, 0x00, + 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0x70, + 0x30, 0x30, 0x00, 0xfc, 0x00, 0x30, 0x30, 0x00, + 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, + 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x0f, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x3c, 0x1c, + 0x78, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, + 0x70, 0x18, 0x30, 0x60, 0x78, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; diff --git a/Kernel/heap.c b/Kernel/heap.c new file mode 100644 index 00000000..fce510cd --- /dev/null +++ b/Kernel/heap.c @@ -0,0 +1,398 @@ +/* + * AcessOS Microkernel Version + * heap.c + */ +#include +#include +#include + +#define WARNINGS 1 + +// === CONSTANTS === +#define HEAP_BASE 0xE0800000 +#define HEAP_MAX 0xF0000000 // 120MiB, Plenty +#define HEAP_INIT_SIZE 0x8000 // 32 KiB +#define BLOCK_SIZE (sizeof(void*)) // 8 Machine Words +#define COMPACT_HEAP 0 // Use 4 byte header? +#define FIRST_FIT 0 + +#define MAGIC_FOOT 0x2ACE5505 +#define MAGIC_FREE 0xACE55000 +#define MAGIC_USED 0x862B0505 // MAGIC_FOOT ^ MAGIC_FREE + +// === PROTOTYPES === +void Heap_Install(); +void *Heap_Extend(int Bytes); +void *Heap_Merge(tHeapHead *Head); +void *malloc(size_t Bytes); +void free(void *Ptr); +void Heap_Dump(); + +// === GLOBALS === + int giHeapSpinlock; +void *gHeapStart; +void *gHeapEnd; + +// === CODE === +void Heap_Install() +{ + gHeapStart = (void*)MM_KHEAP_BASE; + gHeapEnd = (void*)MM_KHEAP_BASE; + Heap_Extend(HEAP_INIT_SIZE); +} + +/** + * \fn void *Heap_Extend(int Bytes) + * \brief Extend the size of the heap + */ +void *Heap_Extend(int Bytes) +{ + Uint i; + tHeapHead *head = gHeapEnd; + tHeapFoot *foot; + + // Bounds Check + if( (Uint)gHeapEnd == MM_KHEAP_MAX ) + return NULL; + + // Bounds Check + if( (Uint)gHeapEnd + ((Bytes+0xFFF)&~0xFFF) > MM_KHEAP_MAX ) { + Bytes = MM_KHEAP_MAX - (Uint)gHeapEnd; + return NULL; + } + + // Heap expands in pages + for(i=0;i<(Bytes+0xFFF)>>12;i++) + MM_Allocate( (Uint)gHeapEnd+(i<<12) ); + + // Increas heap end + gHeapEnd += i << 12; + + // Create Block + head->Size = (Bytes+0xFFF)&~0xFFF; + head->Magic = MAGIC_FREE; + foot = (void*)( (Uint)gHeapEnd - sizeof(tHeapFoot) ); + foot->Head = head; + foot->Magic = MAGIC_FOOT; + + //Log(" Heap_Extend: head = %p", head); + return Heap_Merge(head); // Merge with previous block +} + +/** + * \fn void *Heap_Merge(tHeapHead *Head) + * \brief Merges two ajacent heap blocks + */ +void *Heap_Merge(tHeapHead *Head) +{ + tHeapFoot *foot; + tHeapFoot *thisFoot; + tHeapHead *head; + + //Log("Heap_Merge: (Head=%p)", Head); + + thisFoot = (void*)( (Uint)Head + Head->Size - sizeof(tHeapFoot) ); + if((Uint)thisFoot > (Uint)gHeapEnd) return NULL; + + // Merge Left (Down) + foot = (void*)( (Uint)Head - sizeof(tHeapFoot) ); + if( ((Uint)foot < (Uint)gHeapEnd && (Uint)foot > HEAP_BASE) + && foot->Head->Magic == MAGIC_FREE) { + foot->Head->Size += Head->Size; // Increase size + thisFoot->Head = foot->Head; // Change backlink + Head->Magic = 0; // Clear old head + Head->Size = 0; + Head = foot->Head; // Save new head address + foot->Head = NULL; // Clear central footer + foot->Magic = 0; + } + + // Merge Right (Upwards) + head = (void*)( (Uint)Head + Head->Size ); + if((Uint)head < (Uint)gHeapEnd && head->Magic == MAGIC_FREE) + { + Head->Size += head->Size; + foot = (void*)( (Uint)Head + Head->Size - sizeof(tHeapFoot) ); + foot->Head = Head; // Update Backlink + thisFoot->Head = NULL; // Clear old footer + thisFoot->Magic = 0; + head->Size = 0; // Clear old header + head->Magic = 0; + } + + // Return new address + return Head; +} + +/** + * \fn void *malloc(size_t Bytes) + * \brief Allocate memory from the heap + */ +void *malloc(size_t Bytes) +{ + tHeapHead *head, *newhead; + tHeapFoot *foot, *newfoot; + tHeapHead *best = NULL; + Uint bestSize = 0; // Speed hack + + // Get required size + Bytes = (Bytes + sizeof(tHeapHead) + sizeof(tHeapFoot) + BLOCK_SIZE-1) & ~(BLOCK_SIZE-1); + + // Lock Heap + LOCK(&giHeapSpinlock); + + // Traverse Heap + for( head = gHeapStart; + (Uint)head < (Uint)gHeapEnd; + head = (void*)((Uint)head + head->Size) + ) + { + // Alignment Check + if( head->Size & (BLOCK_SIZE-1) ) { + #if WARNINGS + Warning("Size of heap address %p is invalid not aligned (0x%x)", head, head->Size); + Heap_Dump(); + #endif + return NULL; + } + + // Check if allocated + if(head->Magic == MAGIC_USED) continue; + // Error check + if(head->Magic != MAGIC_FREE) { + #if WARNINGS + Warning("Magic of heap address %p is invalid (0x%x)", head, head->Magic); + Heap_Dump(); + #endif + RELEASE(&giHeapSpinlock); // Release spinlock + return NULL; + } + + // Size check + if(head->Size < Bytes) continue; + + // Perfect fit + if(head->Size == Bytes) { + head->Magic = MAGIC_USED; + RELEASE(&giHeapSpinlock); // Release spinlock + return best->Data; + } + + // Break out of loop + #if FIRST_FIT + best = head; + bestSize = head->Size; + break; + #else + // or check if the block is the best size + if(bestSize > head->Size) { + best = head; + bestSize = head->Size; + } + #endif + } + + // If no block large enough is found, create one + if(!best) + { + best = Heap_Extend( Bytes ); + // Check for errors + if(!best) { + RELEASE(&giHeapSpinlock); // Release spinlock + return NULL; + } + // Check size + if(best->Size == Bytes) { + RELEASE(&giHeapSpinlock); // Release spinlock + return best->Data; + } + } + + // Split Block + newhead = (void*)( (Uint)best + Bytes ); + newfoot = (void*)( (Uint)best + Bytes - sizeof(tHeapFoot) ); + foot = (void*)( (Uint)best + best->Size - sizeof(tHeapFoot) ); + + newfoot->Head = best; // Create new footer + newfoot->Magic = MAGIC_FOOT; + newhead->Size = best->Size - Bytes; // Create new header + newhead->Magic = MAGIC_FREE; + foot->Head = newhead; // Update backlink in old footer + best->Size = Bytes; // Update size in old header + best->Magic = MAGIC_USED; // Mark block as used + + RELEASE(&giHeapSpinlock); // Release spinlock + return best->Data; +} + +/** + * \fn void free(void *Ptr) + * \brief Free an allocated memory block + */ +void free(void *Ptr) +{ + tHeapHead *head; + tHeapFoot *foot; + + // Alignment Check + if( (Uint)Ptr & (sizeof(Uint)-1) ) { + Warning("free - Passed a non-aligned address (%p)\n", Ptr); + return; + } + + // Sanity check + if((Uint)Ptr < (Uint)gHeapStart || (Uint)Ptr > (Uint)gHeapEnd) + { + Warning("free - Passed a non-heap address (%p)\n", Ptr); + return; + } + + // Check memory block - Header + head = (void*)( (Uint)Ptr - sizeof(tHeapHead) ); + if(head->Magic == MAGIC_FREE) { + Warning("free - Passed a freed block (%p)\n", head); + return; + } + if(head->Magic != MAGIC_USED) { + Warning("free - Magic value is invalid (%p, 0x%x)\n", head, head->Magic); + return; + } + + // Check memory block - Footer + foot = (void*)( (Uint)head + head->Size - sizeof(tHeapFoot) ); + if(foot->Head != head) { + Warning("free - Footer backlink is incorrect (%p, 0x%x)\n", head, foot->Head); + return; + } + if(foot->Magic != MAGIC_FOOT) { + Warning("free - Footer magic is invalid (%p, 0x%x)\n", head, foot->Magic); + return; + } + + // Lock + LOCK( &giHeapSpinlock ); + + // Mark as free + head->Magic = MAGIC_FREE; + // Merge blocks + Heap_Merge( head ); + + // Release + RELEASE( &giHeapSpinlock ); +} + +/** + */ +void *realloc(void *__ptr, size_t __size) +{ + tHeapHead *head = (void*)( (Uint)__ptr-8 ); + tHeapHead *nextHead; + tHeapFoot *foot; + Uint newSize = (__size + sizeof(tHeapFoot)+sizeof(tHeapHead)+BLOCK_SIZE-1)&~(BLOCK_SIZE-1); + + // Check for reallocating NULL + if(__ptr == NULL) return malloc(__size); + + // Check if resize is needed + if(newSize <= head->Size) return __ptr; + + // Check if next block is free + nextHead = (void*)( (Uint)head + head->Size ); + + // Extend into next block + if(nextHead->Magic == MAGIC_FREE && nextHead->Size+head->Size >= newSize) + { + Uint size = nextHead->Size + head->Size; + foot = (void*)( (Uint)nextHead + nextHead->Size - sizeof(tHeapFoot) ); + // Exact Fit + if(size == newSize) { + head->Size = newSize; + foot->Head = head; + nextHead->Magic = 0; + nextHead->Size = 0; + return __ptr; + } + // Create a new heap block + nextHead = (void*)( (Uint)head + newSize ); + nextHead->Size = size - newSize; + nextHead->Magic = MAGIC_FREE; + foot->Head = nextHead; // Edit 2nd footer + head->Size = newSize; // Edit first header + // Create new footer + foot = (void*)( (Uint)head + newSize - sizeof(tHeapFoot) ); + foot->Head = head; + foot->Magic = MAGIC_FOOT; + return __ptr; + } + + // Extend downwards? + foot = (void*)( (Uint)head - sizeof(tHeapFoot) ); + nextHead = foot->Head; + if(nextHead->Magic == MAGIC_FREE && nextHead->Size+head->Size >= newSize) + { + Uint size = nextHead->Size + head->Size; + // Exact fit + if(size == newSize) + { + Uint oldDataSize; + // Set 1st (new/lower) header + nextHead->Magic = MAGIC_USED; + nextHead->Size = newSize; + // Get 2nd (old) footer + foot = (void*)( (Uint)nextHead + newSize ); + foot->Head = nextHead; + // Save old data size + oldDataSize = head->Size - sizeof(tHeapFoot) - sizeof(tHeapHead); + // Clear old header + head->Size = 0; + head->Magic = 0; + memcpy(nextHead->Data, __ptr, oldDataSize); + } + } + + return NULL; +} + +#if WARNINGS +void Heap_Dump() +{ + tHeapHead *head; + tHeapFoot *foot; + + head = gHeapStart; + while( (Uint)head < (Uint)gHeapEnd ) + { + foot = (void*)( (Uint)head + head->Size - sizeof(tHeapFoot) ); + Log("%p (0x%x): 0x%08lx 0x%lx", head, MM_GetPhysAddr((Uint)head), head->Size, head->Magic); + Log("%p 0x%lx", foot->Head, foot->Magic); + Log(""); + + // Sanity Check Header + if(head->Size == 0) { + Log("HALTED - Size is zero"); + break; + } + if(head->Size & (BLOCK_SIZE-1)) { + Log("HALTED - Size is malaligned"); + break; + } + if(head->Magic != MAGIC_FREE && head->Magic != MAGIC_USED) { + Log("HALTED - Head Magic is Bad"); + break; + } + + // Check footer + if(foot->Magic != MAGIC_FOOT) { + Log("HALTED - Foot Magic is Bad"); + break; + } + if(head != foot->Head) { + Log("HALTED - Footer backlink is invalid"); + break; + } + + // All OK? Go to next + head = foot->NextHead; + } +} +#endif diff --git a/Kernel/include/binary.h b/Kernel/include/binary.h new file mode 100644 index 00000000..e02fc2dc --- /dev/null +++ b/Kernel/include/binary.h @@ -0,0 +1,41 @@ +/* + */ +#ifndef _BINARY_H +#define _BINARY_H + +// === TYPES === +/** + * \struct sBinary + * \brief Defines a binary file + */ +typedef struct sBinary { + struct sBinary *Next; + char *TruePath; + char *Interpreter; + Uint Entry; + Uint Base; + int NumPages; + int ReferenceCount; + struct { + Uint Physical; + Uint Virtual; + Uint16 Size, Flags; + } Pages[]; +} tBinary; + +/** + * \struct sBinaryType + */ +typedef struct sBinaryType { + struct sBinaryType *Next; + Uint32 Ident; + Uint32 Mask; + char *Name; + tBinary *(*Load)(int FD); + int (*Relocate)(void *Base); + int (*GetSymbol)(void *Base, char *Name, Uint *Dest); +} tBinaryType; + +extern char *Binary_RegInterp(char *Path); + +#endif diff --git a/Kernel/include/binary_ext.h b/Kernel/include/binary_ext.h new file mode 100644 index 00000000..8b8a6df2 --- /dev/null +++ b/Kernel/include/binary_ext.h @@ -0,0 +1,17 @@ +/* + * Acess 2 + * binary_ext.h + * - Exported Symbols from the binary loader + */ +#ifndef _BINARY_EXT_H +#define _BINARY_EXT_H + +// === FUNCTIONS === +extern void *Binary_LoadFile(char *Path); +extern void *Binary_LoadKernel(char *Path); +extern Uint Binary_Relocate(void *Mem); +extern void Binary_Unload(void *Base); +extern int Binary_GetSymbol(char *Name, Uint *Dest); +extern Uint Binary_FindSymbol(void *Base, char *Name, Uint *Dest); + +#endif diff --git a/Kernel/include/common.h b/Kernel/include/common.h new file mode 100644 index 00000000..f199ef59 --- /dev/null +++ b/Kernel/include/common.h @@ -0,0 +1,111 @@ +/* + * AcessOS Microkernel Version + * common.h + */ +#ifndef _COMMON_H +#define _COMMON_H + +#define NULL ((void*)0) + +#include +#include + +enum eConfigs { + CFG_VFS_CWD, + CFG_VFS_MAXFILES, + NUM_CFG_ENTRIES +}; +#define CFGINT(_idx) (*(Uint*)(MM_PPD_CFG+(_idx)*sizeof(void*))) +#define CFGPTR(_idx) (*(void**)(MM_PPD_CFG+(_idx)*sizeof(void*))) + +// === CONSTANTS === +// --- Memory Flags -- +#define MM_PFLAG_RO 0x01 // Writes disallowed +#define MM_PFLAG_EXEC 0x02 // Allow execution +#define MM_PFLAG_NOPAGE 0x04 // Prevent from being paged out +#define MM_PFLAG_COW 0x08 // Copy-On-Write +#define MM_PFLAG_KERNEL 0x10 // Kernel-Only (Ring0) + +// === Kernel Export Macros === +typedef struct sKernelSymbol { + char *Name; + unsigned int Value; +} tKernelSymbol; +#define EXPORT(_name) tKernelSymbol _kexp_##_name __attribute__((section ("KEXPORT"),unused))={#_name, (Uint)_name} +#define EXPORTAS(_sym,_name) tKernelSymbol _kexp_##_name __attribute__((section ("KEXPORT"),unused))={#_name, (Uint)_sym} + +// === FUNCTIONS === +// --- Core --- +extern void System_Init(char *ArgString); +extern int IRQ_AddHandler(int Num, void (*Callback)(void)); +// --- Debug --- +extern void Panic(char *Msg, ...); +extern void Warning(char *Msg, ...); +extern void Log(char *Fmt, ...); +extern void LogV(char *Fmt, va_list Args); +extern void Debug_Enter(char *FuncName, char *ArgTypes, ...); +extern void Debug_Log(char *FuncName, char *Fmt, ...); +extern void Debug_Leave(char *FuncName, char RetType, ...); +extern void Debug_HexDump(char *Header, void *Data, Uint Length); +#define ENTER(_types...) Debug_Enter((char*)__func__, _types) +#define LOG(_fmt...) Debug_Log((char*)__func__, _fmt) +#define LEAVE(_t...) Debug_Leave((char*)__func__, _t) +// --- IO --- +extern void outb(Uint16 Port, Uint8 Data); +extern void outw(Uint16 Port, Uint16 Data); +extern void outd(Uint16 Port, Uint32 Data); +extern void outq(Uint16 Port, Uint64 Data); +extern Uint8 inb(Uint16 Port); +extern Uint16 inw(Uint16 Port); +extern Uint32 ind(Uint16 Port); +extern Uint64 inq(Uint16 Port); +// --- Memory --- +extern tPAddr MM_Allocate(Uint VAddr); +extern void MM_Deallocate(Uint VAddr); +extern int MM_Map(Uint VAddr, tPAddr PAddr); +extern tPAddr MM_GetPhysAddr(Uint VAddr); +extern void MM_SetFlags(Uint VAddr, Uint Flags, Uint Mask); +extern Uint MM_MapTemp(tPAddr PAddr); +extern void MM_FreeTemp(Uint PAddr); +extern Uint MM_MapHWPage(tPAddr PAddr, Uint Number); +extern void MM_UnmapHWPage(Uint VAddr, Uint Number); +extern tPAddr MM_AllocPhys(); +extern void MM_RefPhys(tPAddr Addr); +extern void MM_DerefPhys(tPAddr Addr); +extern void *memcpy(void *dest, void *src, Uint count); +extern void *memcpyd(void *dest, void *src, Uint count); +extern void *memset(void *dest, int val, Uint count); +extern void *memsetd(void *dest, Uint val, Uint count); +// --- Strings --- +extern Uint strlen(char *Str); +extern char *strcpy(char *__dest, char *__src); +extern int strcmp(char *__dest, char *__src); +extern int strncmp(char *Str1, char *Str2, size_t num); +extern int strucmp(char *Str1, char *Str2); +extern int strpos(char *Str, char Ch); +extern int strpos8(char *str, Uint32 search); +extern void itoa(char *buf, Uint num, int base, int minLength, char pad); +extern int ReadUTF8(Uint8 *str, Uint32 *Val); +// --- Heap --- +extern void *malloc(size_t size); +extern void *realloc(void *ptr, size_t size); +extern void free(void *Ptr); +// --- Modules --- +extern int Module_LoadMem(void *Buffer, Uint Length, char *ArgStr); +extern int Module_LoadFile(char *Path, char *ArgStr); +// --- Timing --- +extern Sint64 timestamp(int sec, int mins, int hrs, int day, int month, int year); +extern Sint64 now(); +// --- Threads --- +extern int Proc_Spawn(char *Path); +extern void Proc_Exit(); +extern void Proc_Yield(); +extern int Proc_GetCfg(int Index); +extern int Proc_GetUID(); +extern int Proc_GetGID(); +extern int SpawnTask(tThreadFunction Function, void *Arg); + +#include +#include + +#endif diff --git a/Kernel/include/drv_pci.h b/Kernel/include/drv_pci.h new file mode 100644 index 00000000..b6a1b072 --- /dev/null +++ b/Kernel/include/drv_pci.h @@ -0,0 +1,41 @@ +/* + * Acess 2 + * PCI Bus Driver + * drv_pci.h + */ +#ifndef _DRV_PCI_H +#define _DRV_PCI_H + +enum e_PciClasses { + PCI_CLASS_PRE20 = 0x00, + PCI_CLASS_STORAGE, + PCI_CLASS_NETWORK, + PCI_CLASS_DISPLAY, + PCI_CLASS_MULTIMEDIA, + PCI_CLASS_MEMORY, + PCI_CLASS_BRIDGE, + PCI_CLASS_COMM, + PCI_CLASS_PREPH, + PCI_CLASS_INPUT, + PCI_CLASS_DOCKING, + PCI_CLASS_PROCESSORS, + PCI_CLASS_SERIALBUS, + PCI_CLASS_MISC = 0xFF +}; +enum e_PciOverClasses { + PCI_OC_PCIBRIDGE = 0x0604, + PCI_OC_SCSI = 0x0100 +}; + + +extern int PCI_CountDevices(Uint16 vendor, Uint16 device, Uint16 fcn); +extern int PCI_GetDevice(Uint16 vendor, Uint16 device, Uint16 fcn, int idx); +extern int PCI_GetDeviceByClass(Uint16 class, Uint16 mask, int prev); +extern Uint8 PCI_GetIRQ(int id); +extern Uint32 PCI_GetBAR0(int id); +extern Uint32 PCI_GetBAR1(int id); +extern Uint32 PCI_GetBAR3(int id); +extern Uint32 PCI_GetBAR4(int id); +extern Uint32 PCI_GetBAR5(int id); + +#endif diff --git a/Kernel/include/errno.h b/Kernel/include/errno.h new file mode 100644 index 00000000..8bf83d8d --- /dev/null +++ b/Kernel/include/errno.h @@ -0,0 +1,16 @@ +/* + * AcessOS Microkernel Version + * errno.h + */ +#ifndef _ERRNO_H +#define _ERRNO_H + +enum eErrorNums { + EOK, + ENOSYS, + EINVAL, + ENOMEM, + EACCES +}; + +#endif diff --git a/Kernel/include/fs_devfs.h b/Kernel/include/fs_devfs.h new file mode 100644 index 00000000..4b29cf1b --- /dev/null +++ b/Kernel/include/fs_devfs.h @@ -0,0 +1,20 @@ +/* + * Acess 2 + * Device Filesystem (DevFS) + * - vfs/fs/devfs.c + */ +#ifndef _FS_DEVFS_H +#define _FS_DEVFS_H +#include + +// === TYPES === +typedef struct sDevFS_Driver { + struct sDevFS_Driver *Next; + char *Name; + tVFS_Node RootNode; +} tDevFS_Driver; + +// === FUNCTIONS === +extern int DevFS_AddDevice(tDevFS_Driver *Dev); + +#endif diff --git a/Kernel/include/heap.h b/Kernel/include/heap.h new file mode 100644 index 00000000..c793c874 --- /dev/null +++ b/Kernel/include/heap.h @@ -0,0 +1,20 @@ +/* + * AcessOS Microkernel Version + * heap.h + */ +#ifndef _HEAP_H +#define _HEAP_H + +typedef struct { + Uint Size; + Uint Magic; + char Data[]; +} tHeapHead; + +typedef struct { + Uint Magic; + tHeapHead *Head; + tHeapHead NextHead[]; // Array to make it act like a pointer, but have no size +} tHeapFoot; + +#endif diff --git a/Kernel/include/init.h b/Kernel/include/init.h new file mode 100644 index 00000000..a16acfdc --- /dev/null +++ b/Kernel/include/init.h @@ -0,0 +1,17 @@ +/* + * AcessOS Microkernel Version + * init.h + */ +#ifndef _INIT_H +#define _INIT_H + +#define INIT_SRV_MAGIC (0xE6|('S'<<8)|('R'<<16)|('V'<<24)) + +typedef struct sInitServ { + Uint32 Magic; + Uint32 LoadBase; + Uint32 MemSize; + int (*Entrypoint)(char **Args); +} tInitServ; + +#endif diff --git a/Kernel/include/mboot.h b/Kernel/include/mboot.h new file mode 100644 index 00000000..764120f6 --- /dev/null +++ b/Kernel/include/mboot.h @@ -0,0 +1,26 @@ +/* + * AcessOS Microkernel Version + * mboot.h + */ +#ifndef _MBOOT_H +#define _MBOOT_H + +// === TYPES === +typedef struct { + Uint32 Flags; + Uint32 LowMem; + Uint32 HighMem; + Uint32 BootDevice; + Uint32 CommandLine; + Uint32 ModuleCount; + Uint32 Modules; +} tMBoot_Info; + +typedef struct { + Uint32 Start; + Uint32 End; + char *String; + Uint32 Resvd; +} tMBoot_Module; + +#endif diff --git a/Kernel/include/modules.h b/Kernel/include/modules.h new file mode 100644 index 00000000..dcf34656 --- /dev/null +++ b/Kernel/include/modules.h @@ -0,0 +1,41 @@ +/* + * AcessOS 2 + * - Module Loader + */ +#ifndef _MODULE_H +#define _MODULE_H + +#define MODULE_MAGIC ('A'|('M'<<8)|('D'<<16)|('\2'<<24)) + +// IA32 - Architecture 1 +#if ARCH == i386 || ARCH == i586 +# define MODULE_ARCH_ID 1 +// IA64 - Architecture 2 +#elif ARCH == x64 || ARCH == x86_64 +# define MODULE_ARCH_ID 2 +#else +# error "Unknown architecture when determining MODULE_ARCH_ID ('" #ARCH "')" +#endif + +#if BUILD_MODULE +# define MODULE_DEFINE(_flags,_ver,_ident,_entry,_deps...) char *_DriverDeps[]={_deps};\ + tModule DriverInfo=\ + {MODULE_MAGIC,MODULE_ARCH_ID,_flags,_ver,NULL,#_ident,_entry,_DriverDeps} +#else +# define MODULE_DEFINE(_flags,_ver,_ident,_entry,_deps...) char *_DriverDeps_##_ident[]={_deps};\ + tModule __attribute__ ((section ("KMODULES"),unused)) _DriverInfo_##_ident=\ + {MODULE_MAGIC,MODULE_ARCH_ID,_flags,_ver,NULL,#_ident,_entry,_DriverDeps_##_ident} +#endif + +typedef struct sModule { + Uint32 Magic; + Uint8 Arch; + Uint8 Flags; + Uint16 Version; + struct sModule *Next; + char *Name; + int (*Init)(char **Arguments); + char **Dependencies; // NULL Terminated List +} __attribute__((packed)) tModule; + +#endif diff --git a/Kernel/include/syscalls.h b/Kernel/include/syscalls.h new file mode 100644 index 00000000..b2c13cf7 --- /dev/null +++ b/Kernel/include/syscalls.h @@ -0,0 +1,76 @@ +/* + * AcessOS Microkernel Version + * syscalls.h + */ +#ifndef _SYSCALLS_H +#define _SYSCALLS_H + +enum eSyscalls { + SYS_EXIT, // 0 - Kill this thread + SYS_CLONE, // 1 - Create a new thread + SYS_KILL, // 2 - Send a signal + SYS_SIGNAL, // 3 - Set signal Handler + SYS_YIELD, // 4 - Yield remainder of timestamp + SYS_SLEEP, // 5 - Sleep until messaged or signaled + SYS_WAIT, // 6 - Wait for a time or a message + SYS_WAITTID, // 7 - Wait for a thread to do something + SYS_SETNAME, // 8 - Set's the name of the current thread + SYS_GETNAME, // 9 - Get's the name of a thread + SYS_GETTID, // 10 - Get current thread ID + SYS_GETPID, // 11 - Get current thread group ID + SYS_SETPRI, // 12 - Set process priority + SYS_SENDMSG, // 13 - Send an IPC message + SYS_GETMSG, // 14 - Recieve an IPC message + SYS_GETTIME, // 15 - Get the current timestamp + SYS_SPAWN, // 16 - Spawn a new process + SYS_EXECVE, // 17 - Replace the current process + SYS_LOADBIN, // 18 - Load a binary into the current address space + SYS_UNLOADBIN, // 19 - Unload a loaded binary + + SYS_GETPHYS = 32, // 32 - Get the physical address of a page + SYS_MAP, // 33 - Map a physical address + SYS_ALLOCATE, // 34 - Allocate a page + SYS_UNMAP, // 35 - Unmap a page + SYS_PREALLOC, // 36 - Preallocate a page + SYS_SETFLAGS, // 37 - Set a page's flags + SYS_SHAREWITH, // 38 - Share a page with another thread + SYS_GETUID, // 39 - Get current User ID + SYS_GETGID, // 40 - Get current Group ID + SYS_SETUID, // 41 - Set current user ID + SYS_SETGID, // 42 - Set current Group ID + + SYS_OPEN = 64, // 64 - Open a file + SYS_REOPEN, // 65 - Close a file and reuse its handle + SYS_CLOSE, // 66 - Close a file + SYS_READ, // 67 - Read from an open file + SYS_WRITE, // 68 - Write to an open file + SYS_IOCTL, // 69 - Perform an IOCtl Call + SYS_READDIR, // 70 - Read from an open directory + SYS_MKDIR, // 71 - Create a new directory + SYS_SYMLINK, // 72 - Create a symbolic link + SYS_GETACL, // 73 - Get an ACL Value + SYS_SETACL, // 74 - Set an ACL Value + SYS_FINFO, // 75 - Get file information + SYS_SEEK, // 76 - Seek to a new position in the file + SYS_TELL, // 77 - Return the current file position + NUM_SYSCALLS, + SYS_DEBUG = 0x100 // 0x100 - Print a debug string +}; + +static const char *cSYSCALL_NAMES[] = { + "SYS_EXIT","SYS_CLONE","SYS_KILL","SYS_SIGNAL","SYS_YIELD","SYS_SLEEP", + "SYS_WAIT","SYS_WAITTID","SYS_SETNAME","SYS_GETNAME","SYS_GETTID","SYS_GETPID", + "SYS_SETPRI","SYS_SENDMSG","SYS_GETMSG","SYS_GETTIME","SYS_SPAWN","SYS_EXECVE", + "SYS_LOADBIN","SYS_UNLOADBIN","","","","", + "","","","","","", + "","","SYS_GETPHYS","SYS_MAP","SYS_ALLOCATE","SYS_UNMAP", + "SYS_PREALLOC","SYS_SETFLAGS","SYS_SHAREWITH","SYS_GETUID","SYS_GETGID","SYS_SETUID", + "SYS_SETGID","","","","","", + "","","","","","", + "","","","","","", + "","","","","SYS_OPEN","SYS_REOPEN", + "SYS_CLOSE","SYS_READ","SYS_WRITE","SYS_IOCTL","SYS_READDIR","SYS_MKDIR", + "SYS_SYMLINK","SYS_GETACL","SYS_SETACL","SYS_FINFO","SYS_SEEK","SYS_TELL", + "" +}; +#endif diff --git a/Kernel/include/syscalls.inc.asm b/Kernel/include/syscalls.inc.asm new file mode 100644 index 00000000..39b81937 --- /dev/null +++ b/Kernel/include/syscalls.inc.asm @@ -0,0 +1,51 @@ +; Acess2 +; System Calls List +; + +%define SYS_EXIT 0 ; Kill this thread +%define SYS_CLONE 1 ; Create a new thread +%define SYS_KILL 2 ; Send a signal +%define SYS_SIGNAL 3 ; Set signal Handler +%define SYS_YIELD 4 ; Yield remainder of timestamp +%define SYS_SLEEP 5 ; Sleep until messaged or signaled +%define SYS_WAIT 6 ; Wait for a time or a message +%define SYS_WAITTID 7 ; Wait for a thread to do something +%define SYS_SETNAME 8 ; Set's the name of the current thread +%define SYS_GETNAME 9 ; Get's the name of a thread +%define SYS_GETTID 10 ; Get current thread ID +%define SYS_GETPID 11 ; Get current thread group ID +%define SYS_SETPRI 12 ; Set process priority +%define SYS_SENDMSG 13 ; Send an IPC message +%define SYS_GETMSG 14 ; Recieve an IPC message +%define SYS_GETTIME 15 ; Get the current timestamp +%define SYS_SPAWN 16 ; Spawn a new process +%define SYS_EXECVE 17 ; Replace the current process +%define SYS_LOADBIN 18 ; Load a binary into the current address space +%define SYS_UNLOADBIN 19 ; Unload a loaded binary + +%define SYS_GETPHYS 32 ; Get the physical address of a page +%define SYS_MAP 33 ; Map a physical address +%define SYS_ALLOCATE 34 ; Allocate a page +%define SYS_UNMAP 35 ; Unmap a page +%define SYS_PREALLOC 36 ; Preallocate a page +%define SYS_SETFLAGS 37 ; Set a page's flags +%define SYS_SHAREWITH 38 ; Share a page with another thread +%define SYS_GETUID 39 ; Get current User ID +%define SYS_GETGID 40 ; Get current Group ID +%define SYS_SETUID 41 ; Set current user ID +%define SYS_SETGID 42 ; Set current Group ID + +%define SYS_OPEN 64 ; Open a file +%define SYS_REOPEN 65 ; Close a file and reuse its handle +%define SYS_CLOSE 66 ; Close a file +%define SYS_READ 67 ; Read from an open file +%define SYS_WRITE 68 ; Write to an open file +%define SYS_IOCTL 69 ; Perform an IOCtl Call +%define SYS_READDIR 70 ; Read from an open directory +%define SYS_MKDIR 71 ; Create a new directory +%define SYS_SYMLINK 72 ; Create a symbolic link +%define SYS_GETACL 73 ; Get an ACL Value +%define SYS_SETACL 74 ; Set an ACL Value +%define SYS_FINFO 75 ; Get file information +%define SYS_SEEK 76 ; Seek to a new position in the file +%define SYS_TELL 77 ; Return the current file position diff --git a/Kernel/include/syscalls_old.h b/Kernel/include/syscalls_old.h new file mode 100644 index 00000000..6a9d0384 --- /dev/null +++ b/Kernel/include/syscalls_old.h @@ -0,0 +1,89 @@ +/* + * AcessOS Microkernel Version + * syscalls.h + */ +#ifndef _SYSCALLS_H +#define _SYSCALLS_H + +enum eSyscalls { + SYS_EXIT, // 0 - Kill Current Thread + SYS_CLONE, // 1 - Create a new thread + SYS_KILL, // 2 - Send a signal + SYS_SIGNAL, // 3 - Set a signal handler + SYS_YIELD, // 4 - Yield remainder of timestamp + SYS_SLEEP, // 5 - Sleep until messaged or signaled + SYS_WAIT, // 6 - Wait for a time or a message + SYS_WAITTID, // 7 - Wait for a thread to terminate + + SYS_SETNAME, // 8 - Set thread's name + SYS_GETNAME, // 9 - Get a thread's name + SYS_GETTID, // 10 - Get current thread ID + SYS_GETPID, // 11 - Get current thread group ID + + SYS_SETPRI, // 12 - Set Priority + + SYS_SENDMSG, // 13 - Send an IPC message + SYS_GETMSG, // 14 - Recieve an IPC message + + SYS_GETTIME, // 15 - Get the current timestamp + + SYS_SPAWN, // 16 - Fork and Execve + SYS_EXECVE, // 17 - Replace the current process image with another + SYS_LOADBIN, // 18 - Load a binary image into memory + SYS_UNLOADBIN, // 19 - Unload a loaded binary image + + // Address Space & Permissions (001x xxxx) + SYS_GETPHYS=32, // 32 - Gets the physical address of a page + SYS_MAP, // 33 - Map a physical page + SYS_ALLOCATE, // 34 - Allocate a page + SYS_UNMAP, // 35 - Unmap a page + SYS_PREALLOC, // 36 - Preallocates a page + SYS_SETFLAGS, // 37 - Sets a page's flags + SYS_SHAREWITH, // 38 - Share a page with another thread + // Permissions + SYS_GETUID, // 39 - Get current User ID + SYS_GETGID, // 40 - Get current Group ID + SYS_SETUID, // 41 - Set the current User ID + SYS_SETGID, // 42 - Set the current Group ID + + // VFS (010x xxxx) + SYS_OPEN = 64, // 64 - Open a file + SYS_REOPEN, // 65 - Close a file and reuse its handle + SYS_CLOSE, // 66 - Close and open file + SYS_READ, // 67 - Read from a file + SYS_WRITE, // 68 - Write to a file + SYS_IOCTL, // 69 - Call a file's IOCtl method + SYS_READDIR, // 70 - Read from a directory + SYS_MKDIR, // 71 - Make new directory + SYS_SYMLINK, // 72 - Create a symbolic link + SYS_GETACL, // 73 - Get an ACL + SYS_SETACL, // 74 - Set an ACL + SYS_FINFO, // 75 - Get a file's information + + NUM_SYSCALLS, + + SYS_DEBUG = 0x100 // Print a debug string +}; + +static const char *cSYSCALL_NAMES[] = { + "SYS_EXIT", "SYS_CLONE", "SYS_KILL", "SYS_SIGNAL", "SYS_YIELD", "SYS_SLEEP", "SYS_WAIT", + "SYS_WAITTID", + "SYS_SETNAME", "SYS_GETNAME", "SYS_GETTID", "SYS_GETPID", "SYS_SETPRI", + "SYS_SENDMSG", "SYS_GETMSG", + "SYS_GETTIME", + "SYS_SPAWN", "SYS_EXECVE", "SYS_LOADBIN", + "", "", "", "", "", "", "", "", "", "", "", "", + + "SYS_GETPHYS", "SYS_MAP", "SYS_ALLOCATE", "SYS_UNMAP", + "SYS_PREALLOC", "SYS_SETFLAGS", "SYS_SHAREWITH", + "SYS_GETUID", "SYS_GETGID", "SYS_SETUID", "SYS_SETGID", + + "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", + + "SYS_OPEN", "SYS_REOPEN", "SYS_CLOSE", + "SYS_READ", "SYS_WRITE", "SYS_IOCTL", + "SYS_READDIR", "SYS_MKDIR", "SYS_SYMLINK", + "SYS_GETACL", "SYS_SETACL", "SYS_FINFO" + }; + +#endif diff --git a/Kernel/include/tpl_drv_common.h b/Kernel/include/tpl_drv_common.h new file mode 100644 index 00000000..31ad97a0 --- /dev/null +++ b/Kernel/include/tpl_drv_common.h @@ -0,0 +1,44 @@ +/** + AcessOS Version 1 + \file tpl_drv_common.h + \brief Common Driver Interface Definitions +*/ +#ifndef _TPL_COMMON_H +#define _TPL_COMMON_H + +/** + * \enum eTplDrv_IOCtl + * \brief Common IOCtl Calls + */ +enum eTplDrv_IOCtl { + /// \brief Driver Type - Return an ::eTplDrv_Type value + DRV_IOCTL_TYPE, + /// \brief Get driver identifier - (char *dest[4]) + DRV_IOCTL_IDENT, + /// \brief Get driver version - (int *ver) + DRV_IOCTL_VERSION, + /// \brief Get a IOCtl from a symbolic name + DRV_IOCTL_LOOKUP, +}; + +/** + * \enum eTplDrv_Type + * \brief Driver Types returned by DRV_IOCTL_TYPE + */ +enum eTplDrv_Type { + DRV_TYPE_NULL, //!< NULL Type - Custom Interface + DRV_TYPE_MISC, //!< Miscelanious Compilant - Supports the core calls + DRV_TYPE_TERMINAL, //!< Terminal + DRV_TYPE_VIDEO, //!< Video - LFB + DRV_TYPE_SOUND, //!< Audio + DRV_TYPE_DISK, //!< Disk + DRV_TYPE_KEYBOARD, //!< Keyboard + DRV_TYPE_MOUSE, //!< Mouse + DRV_TYPE_JOYSTICK, //!< Joystick / Gamepad + DRV_TYPE_NETWORK //!< Network Device +}; + +// === FUNCTIONS === +extern int GetIOCtlId(int Class, char *Name); + +#endif diff --git a/Kernel/include/tpl_drv_keyboard.h b/Kernel/include/tpl_drv_keyboard.h new file mode 100644 index 00000000..9d3e5dca --- /dev/null +++ b/Kernel/include/tpl_drv_keyboard.h @@ -0,0 +1,50 @@ +/** + * \file tpl_drv_keyboard.h + * \brief Keyboard Driver Interface Definitions +*/ +#ifndef _TPL_KEYBOARD_H +#define _TPL_KEYBOARD_H + +#include + +/** + * \enum eTplKeyboard_IOCtl + * \brief Common Keyboard IOCtl Calls + * \extends eTplDrv_IOCtl + */ +enum eTplKeyboard_IOCtl { + //! \brief Get/Set Repeat Rate - (int Rate) + KB_IOCTL_REPEATRATE = 4, + //! \brief Get/Set Repeat Delay - (int Delay) + KB_IOCTL_REPEATDELAY, + //! \brief Sets the callback + KB_IOCTL_SETCALLBACK +}; + +typedef void (*tKeybardCallback)(Uint32); + +enum { + KEY_ESC = 0x1B, + + KEY_NP_MASK = 0x80, //End of ASCII Range + KEY_LCTRL, KEY_RCTRL, + KEY_LALT, KEY_RALT, + KEY_LSHIFT, KEY_RSHIFT, + KEY_CAPSLOCK, + KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, + KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, + KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, + KEY_NUMLOCK, KEY_SCROLLLOCK, + KEY_HOME, KEY_END, KEY_INS, KEY_DEL, + KEY_PAUSE, KEY_BREAK, + KEY_PGUP, KEY_PGDOWN, + KEY_KPENTER, KEY_KPSLASH, KEY_KPMINUS, KEY_KPPLUS, KEY_KPSTAR, + KEY_KPHOME, KEY_KPUP, KEY_KPPGUP, KEY_KPLEFT, KEY_KP5, KEY_KPRIGHT, + KEY_KPEND, KEY_KPDOWN, KEY_KPPGDN, KEY_KPINS, KEY_KPDEL, + KEY_WIN, KEY_MENU, + + KEY_KEYUP = 0xFF +}; + + +#endif diff --git a/Kernel/include/tpl_drv_video.h b/Kernel/include/tpl_drv_video.h new file mode 100644 index 00000000..23d3907a --- /dev/null +++ b/Kernel/include/tpl_drv_video.h @@ -0,0 +1,77 @@ +/** + * \file tpl_drv_video.h + * \brief Video Driver Interface Definitions + * \note For AcessOS Version 1 + * + * Video drivers extend the common driver interface tpl_drv_common.h + * and must support _at least_ the IOCtl numbers defined in this file + * to be compatable with Acess. + * + * \section IOCtls + * As said, a compatable driver must implement these calls correctly, + * but they may choose not to allow direct user access to the framebuffer. + * + * \section Screen Contents + * Reads and writes to the driver's file while in component colour modes + * must correspond to a change of the contents of the screen. The framebuffer + * must start at offset 0 in the file. + * In pallete colour modes the LFB is preceded by a 1024 byte pallete (allowing + * room for 256 entries of 32-bits each) +*/ +#ifndef _TPL_VIDEO_H +#define _TPL_VIDEO_H + +#include + +/** + * \enum eTplVideo_IOCtl + * \brief Common Video IOCtl Calls + * \extends eTplDrv_IOCtl + */ +enum eTplVideo_IOCtl { + //! \brief Set Mode - (int mode) + VIDEO_IOCTL_SETMODE = 4, + //! \brief Get Mode - (int *mode) + VIDEO_IOCTL_GETMODE, + //! \brief Find a matching mode - (tVideo_IOCtl_Mode *info) + VIDEO_IOCTL_FINDMODE, + //! \brief Get mode info - (tVideo_IOCtl_Mode *info) + VIDEO_IOCTL_MODEINFO, + //! \brief Request access to Framebuffer - (void *dest), Return Boolean Success + VIDEO_IOCTL_REQLFB +}; + +/** + \struct sVideo_IOCtl_Mode + \brief Mode Structure used in IOCtl Calls +*/ +struct sVideo_IOCtl_Mode { + short id; //!< Mide ID + Uint16 width; //!< Width + Uint16 height; //!< Height + Uint16 bpp; //!< Bits per Pixel +}; +typedef struct sVideo_IOCtl_Mode tVideo_IOCtl_Mode; //!< Mode Type + +/** + * \struct sVT_Char + * \brief Virtual Terminal Representation of a character + */ +struct sVT_Char { + Uint32 Ch; + union { + struct { + Uint16 BGCol; + Uint16 FGCol; + }; + Uint32 Colour; + }; +}; +typedef struct sVT_Char tVT_Char; + +#define VT_COL_BLACK 0x0000 +#define VT_COL_GREY 0x0888 +#define VT_COL_LTGREY 0x0CCC +#define VT_COL_WHITE 0x0FFF + +#endif diff --git a/Kernel/include/vfs.h b/Kernel/include/vfs.h new file mode 100644 index 00000000..12cc8a41 --- /dev/null +++ b/Kernel/include/vfs.h @@ -0,0 +1,106 @@ +/* + * Acess Micro - VFS Server Ver 1 + */ +#ifndef _VFS_H +#define _VFS_H + +#include + +#define VFS_PERM_READ 0x00000001 +#define VFS_PERM_WRITE 0x00000002 +#define VFS_PERM_APPEND 0x00000004 +#define VFS_PERM_EXECUTE 0x00000008 +#define VFS_PERM_ALL 0x7FFFFFFF // Mask for permissions +#define VFS_PERM_DENY 0x80000000 // Inverts permissions + +typedef struct sVFS_ACL { + struct { + unsigned Group: 1; // Group (as opposed to user) flag + unsigned ID: 31; // ID of Group/User (-1 for nobody/world) + }; + struct { + unsigned Inv: 1; // Invert Permissions + unsigned Perms: 31; // Permission Flags + }; +} tVFS_ACL; + +#define VFS_FFLAG_READONLY 0x01 +#define VFS_FFLAG_DIRECTORY 0x02 +#define VFS_FFLAG_SYMLINK 0x04 + +typedef struct sVFS_Node { + //char *Name; //!< Node's Name (UTF-8) + + Uint64 Inode; //!< Inode ID + Uint ImplInt; //!< Implementation Usable Integer + void *ImplPtr; //!< Implementation Usable Pointer + + int ReferenceCount; //!< Number of times the node is used + + Uint64 Size; //!< File Size + + Uint32 Flags; //!< File Flags + + Sint64 ATime; //!< Last Accessed Time + Sint64 MTime; //!< Last Modified Time + Sint64 CTime; //!< Creation Time + + Uint UID; //!< Owning User + Uint GID; //!< Owning Group + + int NumACLs; //!< Number of ACL entries + tVFS_ACL *ACLs; //!< ACL Entries + + //! Reference the node + void (*Reference)(struct sVFS_Node *Node); + //! Close (dereference) the node + void (*Close)(struct sVFS_Node *Node); + //! Send an IO Control + int (*IOCtl)(struct sVFS_Node *Node, int Id, void *Data); + + Uint64 (*Read)(struct sVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); + Uint64 (*Write)(struct sVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); + + //! Find an directory entry by name + struct sVFS_Node *(*FindDir)(struct sVFS_Node *Node, char *Name); + //! Read from a directory + char *(*ReadDir)(struct sVFS_Node *Node, int Pos); + //! Create a node in a directory + int (*MkNod)(struct sVFS_Node *Node, char *Name, Uint Flags); + //! Relink (Rename/Remove) a file/directory + int (*Relink)(struct sVFS_Node *Node, char *OldName, char *NewName); + + //!< \todo Complete +} tVFS_Node; + +/** + * \brief VFS Driver (Filesystem) Definition + */ +typedef struct sVFS_Driver { + char *Name; + Uint Flags; + tVFS_Node *(*InitDevice)(char *Device, char *Options); + struct sVFS_Driver *Next; +} tVFS_Driver; + +// === GLOBALS === +#define VFS_SKIP ((void*)1) +#define VFS_SKIPN(n) ((void*)(n)) +extern tVFS_Node NULLNode; +extern tVFS_ACL gVFS_ACL_EveryoneRWX; +extern tVFS_ACL gVFS_ACL_EveryoneRW; +extern tVFS_ACL gVFS_ACL_EveryoneRX; +extern tVFS_ACL gVFS_ACL_EveryoneRO; + +// === FUNCTIONS === +extern int VFS_AddDriver(tVFS_Driver *Info); +extern tVFS_Driver *VFS_GetFSByName(char *Name); + +// --- Node Cache -- +extern int Inode_GetHandle(); +extern tVFS_Node *Inode_GetCache(int Handle, Uint64 Inode); +extern tVFS_Node *Inode_CacheNode(int Handle, tVFS_Node *Node); +extern void Inode_UncacheNode(int Handle, Uint64 Inode); +extern void Inode_ClearCache(int Handle); + +#endif diff --git a/Kernel/include/vfs_ext.h b/Kernel/include/vfs_ext.h new file mode 100644 index 00000000..6390f84f --- /dev/null +++ b/Kernel/include/vfs_ext.h @@ -0,0 +1,44 @@ +/* + * Acess 2 + * vfs_ext.h + * - Exported Symbols from the VFS + */ +#ifndef _VFS_EXT_H +#define _VFS_EXT_H + +// === CONSTANTS === +#define VFS_MEMPATH_SIZE (3 + (BITS/8)*2) +#define VFS_OPENFLAG_EXEC 0x01 +#define VFS_OPENFLAG_READ 0x02 +#define VFS_OPENFLAG_WRITE 0x04 +#define VFS_OPENFLAG_NOLINK 0x40 +#define VFS_OPENFLAG_USER 0x80 +#define VFS_KERNEL_FLAG 0x40000000 + +#define SEEK_SET 1 +#define SEEK_CUR 0 +#define SEEK_END -1 + +// === FUNCTIONS === +extern int VFS_Init(); + +extern int VFS_Open(char *Path, Uint Flags); +extern void VFS_Close(int FD); + +extern int VFS_Seek(int FD, Sint64 Offset, int Direction); +extern Uint64 VFS_Tell(int FD); + +extern Uint64 VFS_Read(int FD, Uint64 Length, void *Buffer); +extern Uint64 VFS_Write(int FD, Uint64 Length, void *Buffer); + +extern Uint64 VFS_ReadAt(int FD, Uint64 Offset, Uint64 Length, void *Buffer); +extern Uint64 VFS_WriteAt(int FD, Uint64 Offset, Uint64 Length, void *Buffer); + +extern void VFS_GetMemPath(void *Base, Uint Length, char *Dest); +extern char *VFS_GetTruePath(char *Path); + +extern int VFS_Mount(char *Device, char *MountPoint, char *Filesystem, char *Options); +extern int VFS_MkDir(char *Path); +extern int VFS_Symlink(char *Link, char *Dest); + +#endif diff --git a/Kernel/include/vfs_int.h b/Kernel/include/vfs_int.h new file mode 100644 index 00000000..f227b23d --- /dev/null +++ b/Kernel/include/vfs_int.h @@ -0,0 +1,49 @@ +/* + * Acess Micro - VFS Server Ver 1 + */ +#ifndef _VFS_INT_H +#define _VFS_INT_H + +#include "vfs.h" + +// === TYPES === +typedef struct sVFS_Mount { + struct sVFS_Mount *Next; + char *MountPoint; + int MountPointLen; + char *Device; + char *Options; + tVFS_Driver *Filesystem; + tVFS_Node *RootNode; + char StrData[]; +} tVFS_Mount; + +typedef struct sVFS_Handle { + tVFS_Node *Node; + tVFS_Mount *Mount; + Uint64 Position; + Uint Mode; +} tVFS_Handle; + +typedef struct sVFS_Proc { + struct sVFS_Proc *Next; + int ID; + int CwdLen; + Uint UID, GID; + char *Cwd; + int MaxHandles; + tVFS_Handle Handles[]; +} tVFS_Proc; + +// === GLOBALS === +extern tVFS_Mount *gMounts; + +// === PROTOTYPES === +// --- OPEN.C --- +extern char *VFS_GetAbsPath(char *Path); +extern tVFS_Node *VFS_ParsePath(char *Path, char **TruePath); +extern tVFS_Handle *VFS_GetHandle(int FD); +// --- ACLS.C --- +extern int VFS_CheckACL(tVFS_Node *Node, Uint Permissions); + +#endif diff --git a/Kernel/include/vfs_ramfs.h b/Kernel/include/vfs_ramfs.h new file mode 100644 index 00000000..5bb7f319 --- /dev/null +++ b/Kernel/include/vfs_ramfs.h @@ -0,0 +1,20 @@ +/* + * AcessMicro VFS + * - RAM Filesystem Coommon Structures + */ +#ifndef _RAMFS_H +#define _RAMFS_H +#include + +typedef struct sRamFS_File { + struct sRamFS_File *Next; + struct sRamFS_File *Parent; + char *Name; + tVFS_Node Node; + union { + struct sRamFS_File *FirstChild; + char *Bytes; + } Data; +} tRamFS_File; + +#endif diff --git a/Kernel/lib.c b/Kernel/lib.c new file mode 100644 index 00000000..ee9f24e0 --- /dev/null +++ b/Kernel/lib.c @@ -0,0 +1,251 @@ +/* + * Acess2 + * Common Library Functions + */ +#include + +// === CONSTANTS === +// Jan Feb Mar Apr May Jun Jul Aug Sept Oct Nov Dec +const short DAYS_BEFORE[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; +#define UNIX_TO_2K ((30*365*3600*24) + (7*3600*24)) //Normal years + leap years + +// === PROTOTYPES === + int ReadUTF8(Uint8 *str, Uint32 *Val); + +// === CODE === +static const char cUCDIGITS[] = "0123456789ABCDEF"; +/** + * \fn static void itoa(char *buf, Uint num, int base, int minLength, char pad) + * \brief Convert an integer into a character string + */ +void itoa(char *buf, Uint num, int base, int minLength, char pad) +{ + char tmpBuf[BITS]; + int pos=0, i; + + // Sanity check + if(!buf) return; + + // Sanity Check + if(base > 16 || base < 2) { + buf[0] = 0; + return; + } + + // Convert + while(num > base-1) { + tmpBuf[pos] = cUCDIGITS[ num % base ]; + num /= (Uint)base; // Shift `num` right 1 digit + pos++; + } + tmpBuf[pos++] = cUCDIGITS[ num % base ]; // Last digit of `num` + + // Put in reverse + i = 0; + minLength -= pos; + while(minLength-- > 0) buf[i++] = pad; + while(pos-- > 0) buf[i++] = tmpBuf[pos]; // Reverse the order of characters + buf[i] = 0; +} + +/** + * \fn int tolower(int __c) + * \brief Converts a character to lower case + */ +int tolower(int c) +{ + if('A' <= c && c <= 'Z') + return c - 'A' + 'a'; + return c; +} + +/** + * \fn int strucmp(char *Str1, char *Str2) + * \brief Compare \a Str1 and \a Str2 case-insensitively + */ +int strucmp(char *Str1, char *Str2) +{ + while(*Str1 && tolower(*Str1) == tolower(*Str2)) + Str1++, Str2++; + return tolower(*Str1) - tolower(*Str2); +} + +/** + * \fn int strpos(char *Str, char Ch) + * \brief Search a string for an ascii character + */ +int strpos(char *Str, char Ch) +{ + int pos; + for(pos=0;Str[pos];pos++) + { + if(Str[pos] == Ch) return pos; + } + return -1; +} + +/** + */ +int ByteSum(void *Ptr, int Size) +{ + int sum = 0; + while(Size--) sum += *(Uint8*)Ptr++; + return sum; +} + +/** + * \fn Uint strlen(char *__str) + * \brief Get the length of string + */ +Uint strlen(char *__str) +{ + Uint ret = 0; + while(*__str++) ret++; + return ret; +} + +/** + * \fn char *strcpy(char *__str1, char *__str2) + * \brief Copy a string to a new location + */ +char *strcpy(char *__str1, char *__str2) +{ + while(*__str2) + *__str1++ = *__str2++; + *__str1 = '\0'; // Terminate String + return __str1; +} + +/** + * \fn int strcmp(char *str1, char *str2) + * \brief Compare two strings return the difference between + * the first non-matching characters. + */ +int strcmp(char *str1, char *str2) +{ + while(*str1 && *str1 == *str2) + str1++, str2++; + return *str1 - *str2; +} + +/** + * \fn int strncmp(char *Str1, char *Str2, size_t num) + * \brief Compare strings \a Str1 and \a Str2 to a maximum of \a num characters + */ +int strncmp(char *Str1, char *Str2, size_t num) +{ + while(num-- && *Str1 && *Str1 == *Str2) + Str1++, Str2++; + return *Str1-*Str2; +} + +/** + * \fn int strpos8(char *str, Uint32 search) + * \brief Search a string for a UTF-8 character + */ +int strpos8(char *str, Uint32 Search) +{ + int pos; + Uint32 val = 0; + for(pos=0;str[pos];pos++) + { + // ASCII Range + if(Search < 128) { + if(str[pos] == Search) return pos; + continue; + } + if(*(Uint8*)(str+pos) < 128) continue; + + pos += ReadUTF8( (Uint8*)&str[pos], &val ); + if(val == Search) return pos; + } + return -1; +} + +/** + * \fn int ReadUTF8(Uint8 *str, Uint32 *Val) + * \brief Read a UTF-8 character from a string + */ +int ReadUTF8(Uint8 *str, Uint32 *Val) +{ + // ASCII + if( !(*str & 0x80) ) { + *Val = *str; + return 1; + } + + // Middle of a sequence + if( (*str & 0xC0) == 0x80 ) { + *Val = -1; + return 1; + } + + // Two Byte + if( (*str & 0xE0) == 0xC0 ) { + *Val = (*str & 0x1F) << 6; // Upper 6 Bits + str ++; + if( (*str & 0xC0) != 0x80) return -1; // Validity check + *Val |= (*str & 0x3F); // Lower 6 Bits + return 2; + } + + // Three Byte + if( (*str & 0xF0) == 0xE0 ) { + *Val = (*str & 0x0F) << 12; // Upper 4 Bits + str ++; + if( (*str & 0xC0) != 0x80) return -1; // Validity check + *Val |= (*str & 0x3F) << 6; // Middle 6 Bits + str ++; + if( (*str & 0xC0) != 0x80) return -1; // Validity check + *Val |= (*str & 0x3F); // Lower 6 Bits + return 3; + } + + // Four Byte + if( (*str & 0xF1) == 0xF0 ) { + *Val = (*str & 0x07) << 18; // Upper 3 Bits + str ++; + if( (*str & 0xC0) != 0x80) return -1; // Validity check + *Val |= (*str & 0x3F) << 12; // Middle-upper 6 Bits + str ++; + if( (*str & 0xC0) != 0x80) return -1; // Validity check + *Val |= (*str & 0x3F) << 6; // Middle-lower 6 Bits + str ++; + if( (*str & 0xC0) != 0x80) return -1; // Validity check + *Val |= (*str & 0x3F); // Lower 6 Bits + return 4; + } + + // UTF-8 Doesn't support more than four bytes + *Val = -1; + return 4; +} + +/** + * \fn Uint64 timestamp(int sec, int mins, int hrs, int day, int month, int year) + * \brief Converts a date into an Acess Timestamp + */ +Sint64 timestamp(int sec, int mins, int hrs, int day, int month, int year) +{ + Sint64 stamp; + stamp = sec; + stamp += mins*60; + stamp += hrs*3600; + + stamp += day*3600*24; + stamp += month*DAYS_BEFORE[month]*3600*24; + if( ( + ((year&3) == 0 || year%100 != 0) + || (year%100 == 0 && ((year/100)&3) == 0) + ) && month > 1) // Leap year and after feb + stamp += 3600*24; + + stamp += ((365*4+1) * ((year-2000)&~3)) * 3600*24; // Foour Year Segments + stamp += ((year-2000)&3) * 365*3600*24; // Inside four year segment + stamp += UNIX_TO_2K; + + return stamp * 1000; +} + +EXPORT(timestamp); +EXPORT(ReadUTF8); diff --git a/Kernel/messages.c b/Kernel/messages.c new file mode 100644 index 00000000..31ae8f37 --- /dev/null +++ b/Kernel/messages.c @@ -0,0 +1,102 @@ +/* + * AcessOS Microkernel Version + * messages.c + */ +#include +#include +#include + +// === CODE === +/** + * \fn int Proc_SendMessage(Uint *Err, Uint Dest, int Length, void *Data) + * \brief Send an IPC message + * \param Err Pointer to the errno variable + * \param Dest Destination Thread + */ +int Proc_SendMessage(Uint *Err, Uint Dest, int Length, void *Data) +{ + tThread *thread; + tMsg *msg; + + Log("Proc_SendMessage: (Err=%p, Dest=%i, Length=%i, Data=%p)", Err, Dest, Length, Data); + + if(Length <= 0 || !Data) { + *Err = -EINVAL; + return -1; + } + + // Get thread + thread = Proc_GetThread( Dest ); + + // Error check + if(!thread) { return -1; } + + // Get Spinlock + LOCK( &thread->IsLocked ); + + // Check if thread is still alive + if(thread->Status == THREAD_STAT_DEAD) return -1; + + // Create message + msg = malloc( sizeof(tMsg)+Length ); + msg->Next = NULL; + msg->Source = gCurrentThread->TID; + msg->Length = Length; + memcpy(msg->Data, Data, Length); + + // If there are already messages + if(thread->LastMessage) { + thread->LastMessage->Next = msg; + thread->LastMessage = msg; + } else { + thread->Messages = msg; + thread->LastMessage = msg; + } + + RELEASE(&thread->IsLocked); + + Thread_Wake( thread ); + + return 0; +} + +/** + * \fn int Proc_GetMessage(Uint *Err, Uint *Source, void *Buffer) + * \brief Gets a message + */ +int Proc_GetMessage(Uint *Err, Uint *Source, void *Buffer) +{ + int ret; + void *tmp; + + // Check if queue has any items + if(!gCurrentThread->Messages) { + return 0; + } + + LOCK( &gCurrentThread->IsLocked ); + + if(Source) + *Source = gCurrentThread->Messages->Source; + + // Get message length + if( !Buffer ) { + ret = gCurrentThread->Messages->Length; + RELEASE( &gCurrentThread->IsLocked ); + return ret; + } + + // Get message + if(Buffer != GETMSG_IGNORE) + memcpy(Buffer, gCurrentThread->Messages->Data, gCurrentThread->Messages->Length); + ret = gCurrentThread->Messages->Length; + + // Remove from list + tmp = gCurrentThread->Messages->Next; + free(gCurrentThread->Messages); + gCurrentThread->Messages = tmp; + + RELEASE( &gCurrentThread->IsLocked ); + + return ret; +} diff --git a/Kernel/modules.c b/Kernel/modules.c new file mode 100644 index 00000000..91afc262 --- /dev/null +++ b/Kernel/modules.c @@ -0,0 +1,173 @@ +/* + * Acess2 + * - Module Loader + */ +#include +#include + +// === PROTOTYPES === + int Module_LoadMem(void *Buffer, Uint Length, char *ArgString); + int Module_LoadFile(char *Path, char *ArgString); + int Module_int_ResolveDeps(tModule *Info); + int Module_IsLoaded(char *Name); + +// === IMPORTS === +extern tModule gKernelModules[]; +extern void gKernelModulesEnd; + +// === GLOBALS === + int giNumBuiltinModules = 0; + int giModuleSpinlock = 0; +tModule *gLoadedModules = NULL; + +// === CODE === +int Modules_LoadBuiltins() +{ + int i; + giNumBuiltinModules = (Uint)&gKernelModulesEnd - (Uint)&gKernelModules; + giNumBuiltinModules /= sizeof(tModule); + + for( i = 0; i < giNumBuiltinModules; i++ ) + { + Log("Initialising %p '%s' v%i.%i...", + &gKernelModules[i], + gKernelModules[i].Name, + gKernelModules[i].Version>>8, gKernelModules[i].Version & 0xFF + ); + gKernelModules[i].Init(NULL); + } + + return 0; +} + +/** + * \fn int Module_LoadMem(void *Buffer, Uint Length, char *ArgString) + * \brief Load a module from a memory location + */ +int Module_LoadMem(void *Buffer, Uint Length, char *ArgString) +{ + char path[VFS_MEMPATH_SIZE]; + + VFS_GetMemPath(Buffer, Length, path); + + return Module_LoadFile( path, ArgString ); +} + +/** + * \fn int Module_LoadFile(char *Path, char *ArgString) + * \brief Load a module from a file + */ +int Module_LoadFile(char *Path, char *ArgString) +{ + void *base; + tModule *info; + + // Load Binary + base = Binary_LoadKernel(Path); + + // Error check + if(base == NULL) return 0; + + // Check for Acess Driver + if( Binary_FindSymbol(base, "DriverInfo", (Uint*)&info ) == 0 ) + { + #if USE_EDI + // Check for EDI Driver + if( Binary_FindSymbol(base, "driver_init", NULL ) == 0 ) + { + Binary_Relocate(base); // Relocate + return Module_InitEDI( base ); // And intialise + } + #endif + + // Unknown module type?, return error + Binary_Unload(base); + #if USE_EDI + Warning("Module_LoadMem: Module has neither a Module Info struct, nor an EDI entrypoint"); + #else + Warning("Module_LoadMem: Module does not have a Module Info struct"); + #endif + return 0; + } + + LOG("info = %p\n", info); + Debug_HexDump("info", info, 6*4); + + // Check magic number + if(info->Magic != MODULE_MAGIC) + { + Warning("Module_LoadMem: Module's magic value is invalid (0x%x != 0x%x)", info->Magic, MODULE_MAGIC); + return 0; + } + + // Check Architecture + if(info->Arch != MODULE_ARCH_ID) + { + Warning("Module_LoadMem: Module is for a different architecture"); + return 0; + } + + // Resolve Dependencies + if( !Module_int_ResolveDeps(info) ) { + Binary_Unload(base); + return 0; + } + + // Call Initialiser + //if( info->Init( ArgString ) != 0 ) + if( info->Init( NULL ) == 0 ) + { + Binary_Unload(base); + return 0; + } + + // Add to list + LOCK( &giModuleSpinlock ); + info->Next = gLoadedModules; + gLoadedModules = info; + RELEASE( &giModuleSpinlock ); + + return 1; +} + +/** + * \fn int Module_int_ResolveDeps(tModule *Info) + * \brief Resolves the dependencies + * \todo Implement + * \note Currently does not resolve the dependencies, just checks them + */ +int Module_int_ResolveDeps(tModule *Info) +{ + char **names = Info->Dependencies; + + // Walk dependencies array + for( ; *names; names++ ) + { + // Check if the module is loaded + if( !Module_IsLoaded(*names) ) { + Warning("Module `%s' requires `%s', which is not loaded\n", Info->Name, *names); + return 0; + } + } + return 1; +} + +/** + * \fn int Module_IsLoaded(char *Name) + * \brief Checks if a module is loaded + * \param Name Name of module to find + */ +int Module_IsLoaded(char *Name) +{ + tModule *mod = gLoadedModules; + + // Scan loaded list + for( ; mod; mod = mod->Next ) + { + // If found, return true + if(strcmp(mod->Name, Name) == 0) + return 1; + } + // not found - return false + return 0; +} diff --git a/Kernel/proc.c b/Kernel/proc.c new file mode 100644 index 00000000..e5d9f8f1 --- /dev/null +++ b/Kernel/proc.c @@ -0,0 +1,653 @@ +/* + * AcessOS Microkernel Version + * proc.c + */ +#include +#include +#include +#include +#include + +// === CONSTANTS === +#define RANDOM_SEED 0xACE55051 +#define SWITCH_MAGIC 0xFFFACE55 // There is no code in this area +#define DEFAULT_QUANTUM 10 +#define DEFAULT_TICKETS 5 +#define MAX_TICKETS 10 +#define TIMER_DIVISOR 11931 //~100Hz + +// === MACROS === +#define TIMER_BASE 1193182 //Hz +#define MS_PER_TICK_WHOLE (1000*(TIMER_DIVISOR)/(TIMER_BASE)) +#define MS_PER_TICK_FRACT ((Uint64)(1000*TIMER_DIVISOR-((Uint64)MS_PER_TICK_WHOLE)*TIMER_BASE)*(0x80000000/TIMER_BASE)) + +// === IMPORTS === +extern Uint GetEIP(); // start.asm +extern Uint32 gaInitPageDir[1024]; // start.asm +extern void Kernel_Stack_Top; + +// === PROTOTYPES === +void Proc_Start(); +static tThread *Proc_int_GetPrevThread(tThread **List, tThread *Thread); +void Proc_Scheduler(); +Uint rand(); + +// === GLOBALS === +// -- Core Thread -- +tThread gThreadZero = { + NULL, 0, // Next, Lock + THREAD_STAT_ACTIVE, // Status + 0, 0, // TID, TGID + 0, 0, // UID, GID + "ThreadZero", // Name + 0, 0, 0, // ESP, EBP, EIP (Set on switch) + (Uint)&gaInitPageDir-KERNEL_BASE, // CR3 + (Uint)&Kernel_Stack_Top, // Kernel Stack (Unused as it it PL0) + NULL, NULL, // Messages, Last Message + DEFAULT_QUANTUM, DEFAULT_QUANTUM, // Quantum, Remaining + DEFAULT_TICKETS + }; +// -- Processes -- +// --- Locks --- + int giThreadListLock = 0; ///\note NEVER use a heap function while locked +// --- Current State --- +tThread *gCurrentThread = NULL; + int giNumActiveThreads = 0; + int giTotalTickets = 0; +Uint giNextTID = 1; +// --- Thread Lists --- +tThread *gActiveThreads = NULL; // Currently Running Threads +tThread *gSleepingThreads = NULL; // Sleeping Threads +tThread *gDeleteThreads = NULL; // Threads to delete +// --- Timekeeping --- +Uint64 giTicks = 0; +Uint64 giTimestamp = 0; +Uint64 giPartMiliseconds = 0; +// --- Multiprocessing --- + int giNumCPUs = 1; +tMPInfo *gMPTable = NULL; +tTSS *gTSSs = NULL; +tTSS gTSS0 = {0}; + +// === CODE === +/** + * \fn void Proc_Start() + * \brief Starts the process scheduler + */ +void Proc_Start() +{ + Uint pos; + // -- Initialise Multiprocessing + // Find MP Floating Table + // - EBDA + for(pos = KERNEL_BASE|0x9FC00; pos < (KERNEL_BASE|0xA0000); pos += 16) { + if( *(Uint*)(pos) == MPTABLE_IDENT ) { + if(ByteSum( (void*)pos, sizeof(tMPInfo) ) != 0) continue; + gMPTable = (void*)pos; + break; + } + } + // - Last KiB + if(!gMPTable) { + + } + // - BIOS ROM + if(!gMPTable) { + for(pos = KERNEL_BASE|0xF0000; pos < (KERNEL_BASE|0x100000); pos += 16) { + if( *(Uint*)(pos) == MPTABLE_IDENT ) { + if(ByteSum( (void*)pos, sizeof(tMPInfo) ) != 0) continue; + gMPTable = (void*)pos; + break; + } + } + } + + // If the MP Table Exists, parse it + if(gMPTable) + { + Panic("Uh oh... MP Table Parsing is unimplemented\n"); + } else { + giNumCPUs = 1; + gTSSs = &gTSS0; + } + + // Initialise TSS + for(pos=0;pos> 16; + gGDT[9+pos].BaseHi = (Uint)&gTSSs[pos] >> 24; + } + for(pos=0;pos>8)&0xFF); // High Byte + + // Clear timestamp + giTimestamp = 0; + giPartMiliseconds = 0; + giTicks = 0; + + // Create Initial Task + gActiveThreads = &gThreadZero; + gCurrentThread = &gThreadZero; + giTotalTickets = gThreadZero.NumTickets; + giNumActiveThreads = 1; + + // Create Idle Task + if(Proc_Clone(0, 0) == 0) + { + gCurrentThread->ThreadName = "Idle Thread"; + Proc_SetTickets(0); // Never called randomly + gCurrentThread->Quantum = 1; // 1 slice quantum + for(;;) __asm__ __volatile__ ("hlt"); // Just yeilds + } + + // Start Interrupts (and hence scheduler) + __asm__ __volatile__("sti"); +} + +/** + * \fn int Proc_Clone(Uint *Err, Uint Flags) + * \brief Clone the current process + */ +int Proc_Clone(Uint *Err, Uint Flags) +{ + tThread *newThread; + Uint eip, esp, ebp; + + __asm__ __volatile__ ("mov %%esp, %0": "=r"(esp)); + __asm__ __volatile__ ("mov %%ebp, %0": "=r"(ebp)); + + // Create new thread structure + newThread = malloc( sizeof(tThread) ); + if(!newThread) { + Warning("Proc_Clone - Out of memory when creating thread\n"); + *Err = -ENOMEM; + return -1; + } + // Base new thread on old + memcpy(newThread, gCurrentThread, sizeof(tThread)); + // Initialise Memory Space (New Addr space or kernel stack) + if(Flags & CLONE_VM) { + newThread->TGID = newThread->TID; + newThread->CR3 = MM_Clone(); + } else { + Uint tmpEbp, oldEsp = esp; + + // Create new KStack + newThread->KernelStack = MM_NewKStack(); + // Check for errors + if(newThread->KernelStack == 0) { + free(newThread); + return -1; + } + + // Get ESP as a used size + esp = gCurrentThread->KernelStack - esp; + // Copy used stack + memcpy( (void*)(newThread->KernelStack - esp), (void*)(gCurrentThread->KernelStack - esp), esp ); + // Get ESP as an offset in the new stack + esp = newThread->KernelStack - esp; + // Adjust EBP + ebp = newThread->KernelStack - (gCurrentThread->KernelStack - ebp); + + // Repair EBPs & Stack Addresses + #if 0 + tmpEbp = ebp; + while(oldEsp < *(Uint*)tmpEbp && *(Uint*)tmpEbp < gCurrentThread->KernelStack) + { + *(Uint*)tmpEbp += newThread->KernelStack - gCurrentThread->KernelStack; + tmpEbp = *(Uint*)tmpEbp; + } + #else // Catches arguments also, but may trash stack-address-like values + for(tmpEbp = esp; tmpEbp < newThread->KernelStack; tmpEbp += 4) + { + if(oldEsp < *(Uint*)tmpEbp && *(Uint*)tmpEbp < gCurrentThread->KernelStack) + *(Uint*)tmpEbp += newThread->KernelStack - gCurrentThread->KernelStack; + } + #endif + } + + // Set Pointer, Spinlock and TID + newThread->Next = NULL; + newThread->IsLocked = 0; + newThread->TID = giNextTID++; + + // Clear message list (messages are not inherited) + newThread->Messages = NULL; + newThread->LastMessage = NULL; + + // Set remaining (sheduler expects remaining to be correct) + newThread->Remaining = newThread->Quantum; + + // Save core machine state + newThread->ESP = esp; + newThread->EBP = ebp; + eip = GetEIP(); + if(eip == SWITCH_MAGIC) { + outb(0x20, 0x20); // ACK Timer and return as child + return 0; + } + + // Set EIP as parent + newThread->EIP = eip; + + //Log(" Proc_Clone: giTimestamp = %i.%07i", (Uint)giTimestamp, (Uint)giPartMiliseconds/214); + + // Lock list and add to active + LOCK( &giThreadListLock ); + newThread->Next = gActiveThreads; + gActiveThreads = newThread; + giNumActiveThreads ++; + giTotalTickets += newThread->NumTickets; + RELEASE( &giThreadListLock ); + + return newThread->TID; +} + +/** + * \fn void Proc_Exit() + * \brief Kill the current process + */ +void Proc_Exit() +{ + tThread *thread; + tMsg *msg; + + ///\note Double lock is needed due to overlap of locks + + // Lock thread (stop us recieving messages) + LOCK( &gCurrentThread->IsLocked ); + + // Lock thread list + LOCK( &giThreadListLock ); + + // Get previous thread on list + thread = Proc_int_GetPrevThread( &gActiveThreads, gCurrentThread ); + if(!thread) { + Warning("Proc_Exit - Current thread is not on the active queue"); + return; + } + + // Clear Message Queue + while( gCurrentThread->Messages ) + { + msg = gCurrentThread->Messages->Next; + free( gCurrentThread->Messages ); + gCurrentThread->Messages = msg; + } + + gCurrentThread->Remaining = 0; // Clear Remaining Quantum + gCurrentThread->Quantum = 0; // Clear Quantum to indicate dead thread + thread->Next = gCurrentThread->Next; // Remove from active + + // Add to delete queue + if(gDeleteThreads) { + gCurrentThread->Next = gDeleteThreads; + gDeleteThreads = gCurrentThread; + } else { + gCurrentThread->Next = NULL; + gDeleteThreads = gCurrentThread; + } + + giNumActiveThreads --; + giTotalTickets -= gCurrentThread->NumTickets; + + // Mark thread as sleeping + gCurrentThread->Status = THREAD_STAT_DEAD; + + // Release spinlocks + RELEASE( &gCurrentThread->IsLocked ); // Released first so that it IS released + RELEASE( &giThreadListLock ); + __asm__ __volatile__ ("hlt"); +} + +/** + * \fn void Proc_Yield() + * \brief Yield remainder of timeslice + */ +void Proc_Yield() +{ + gCurrentThread->Quantum = 0; + __asm__ __volatile__ ("hlt"); +} + +/** + * \fn void Proc_Sleep() + * \brief Take the current process off the run queue + */ +void Proc_Sleep() +{ + tThread *thread; + + //Log("Proc_Sleep: %i going to sleep", gCurrentThread->TID); + + // Acquire Spinlock + LOCK( &giThreadListLock ); + + // Get thread before current thread + thread = Proc_int_GetPrevThread( &gActiveThreads, gCurrentThread ); + if(!thread) { + Warning("Proc_Sleep - Current thread is not on the active queue"); + return; + } + + // Don't sleep if there is a message waiting + if( gCurrentThread->Messages ) { + RELEASE( &giThreadListLock ); + return; + } + + // Unset remaining timeslices (force a task switch on timer fire) + gCurrentThread->Remaining = 0; + + // Remove from active list + thread->Next = gCurrentThread->Next; + + // Add to Sleeping List (at the top) + gCurrentThread->Next = gSleepingThreads; + gSleepingThreads = gCurrentThread; + + // Reduce the active count & ticket count + giNumActiveThreads --; + giTotalTickets -= gCurrentThread->NumTickets; + + // Mark thread as sleeping + gCurrentThread->Status = THREAD_STAT_SLEEPING; + + // Release Spinlock + RELEASE( &giThreadListLock ); + + __asm__ __volatile__ ("hlt"); +} + +/** + * \fn void Thread_Wake( tThread *Thread ) + * \brief Wakes a sleeping/waiting thread up + */ +void Thread_Wake(tThread *Thread) +{ + tThread *prev; + switch(Thread->Status) + { + case THREAD_STAT_ACTIVE: break; + case THREAD_STAT_SLEEPING: + LOCK( &giThreadListLock ); + prev = Proc_int_GetPrevThread(&gSleepingThreads, Thread); + prev->Next = Thread->Next; // Remove from sleeping queue + Thread->Next = gActiveThreads; // Add to active queue + gActiveThreads = Thread; + Thread->Status = THREAD_STAT_ACTIVE; + RELEASE( &giThreadListLock ); + break; + case THREAD_STAT_WAITING: + Warning("Thread_Wake - Waiting threads are not currently supported"); + break; + case THREAD_STAT_DEAD: + Warning("Thread_Wake - Attempt to wake dead thread (%i)", Thread->TID); + break; + default: + Warning("Thread_Wake - Unknown process status (%i)\n", Thread->Status); + break; + } +} + +/** + * \fn int Proc_Demote(Uint *Err, int Dest, tRegs *Regs) + * \brief Demotes a process to a lower permission level + * \param Err Pointer to user's errno + */ +int Proc_Demote(Uint *Err, int Dest, tRegs *Regs) +{ + int cpl = Regs->cs & 3; + // Sanity Check + if(Dest > 3 || Dest < 0) { + *Err = -EINVAL; + return -1; + } + + // Permission Check + if(cpl > Dest) { + *Err = -EACCES; + return -1; + } + + // Change the Segment Registers + Regs->cs = (((Dest+1)<<4) | Dest) - 8; + Regs->ss = ((Dest+1)<<4) | Dest; + // Check if the GP Segs are GDT, then change them + if(!(Regs->ds & 4)) Regs->ds = ((Dest+1)<<4) | Dest; + if(!(Regs->es & 4)) Regs->es = ((Dest+1)<<4) | Dest; + if(!(Regs->fs & 4)) Regs->fs = ((Dest+1)<<4) | Dest; + if(!(Regs->gs & 4)) Regs->gs = ((Dest+1)<<4) | Dest; + + return 0; +} + +/** + * \fn void Proc_SetTickets(int Num) + * \brief Sets the 'priority' of a task + */ +void Proc_SetTickets(int Num) +{ + if(Num < 0) return; + if(Num > MAX_TICKETS) Num = MAX_TICKETS; + + Log("Proc_SetTickets: (Num=%i)", Num); + Log(" Proc_SetTickets: giTotalTickets = %i", giTotalTickets); + LOCK( &giThreadListLock ); + giTotalTickets -= gCurrentThread->NumTickets; + gCurrentThread->NumTickets = Num; + giTotalTickets += Num; + RELEASE( &giThreadListLock ); + Log(" Proc_SetTickets: giTotalTickets = %i", giTotalTickets); + Log("Proc_SetTickets: RETURN", giTotalTickets); +} + +/** + * \fn tThread *Proc_GetThread(Uint TID) + * \brief Gets a thread given its TID + */ +tThread *Proc_GetThread(Uint TID) +{ + tThread *thread; + + // Search Active List + for(thread = gActiveThreads; + thread; + thread = thread->Next) + { + if(thread->TID == TID) + return thread; + } + + // Search Sleeping List + for(thread = gSleepingThreads; + thread; + thread = thread->Next) + { + if(thread->TID == TID) + return thread; + } + + return NULL; +} + +/** + * \fn static tThread *Proc_int_GetPrevThread(tThread *List, tThread *Thread) + * \brief Gets the previous entry in a thead linked list + */ +static tThread *Proc_int_GetPrevThread(tThread **List, tThread *Thread) +{ + tThread *ret; + // First Entry + if(*List == Thread) { + return (tThread*)List; + } else { + for(ret = *List; + ret->Next && ret->Next != Thread; + ret = ret->Next + ); + // Error if the thread is not on the list + if(!ret->Next || ret->Next != Thread) { + return NULL; + } + } + return ret; +} + +/** + * \fn void Proc_DumpThreads() + */ +void Proc_DumpThreads() +{ + tThread *thread; + + Log("Active Threads:"); + for(thread=gActiveThreads;thread;thread=thread->Next) + { + Log(" %i (%i) - %s", thread->TID, thread->TGID, thread->ThreadName); + Log(" %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum); + Log(" CR3 0x%x, KStack 0x%x", thread->CR3, thread->KernelStack); + } + Log("Sleeping Threads:"); + for(thread=gSleepingThreads;thread;thread=thread->Next) + { + Log(" %i (%i) - %s", thread->TID, thread->TGID, thread->ThreadName); + Log(" %i Tickets, Quantum %i", thread->NumTickets, thread->Quantum); + Log(" CR3 0x%x, KStack 0x%x", thread->CR3, thread->KernelStack); + } +} + +/** + * \fn void Proc_Scheduler(int CPU) + * \brief Swap current task + */ +void Proc_Scheduler(int CPU) +{ + Uint esp, ebp, eip; + Uint number, ticket; + tThread *thread; + + // Increment Timestamps + giTicks ++; + giTimestamp += MS_PER_TICK_WHOLE; + giPartMiliseconds += MS_PER_TICK_FRACT; + if(giPartMiliseconds > 0x80000000) { + giTimestamp ++; + giPartMiliseconds -= 0x80000000; + } + + // If the spinlock is set, let it complete + if(giThreadListLock) return; + + // Clear Delete Queue + while(gDeleteThreads) + { + thread = gDeleteThreads->Next; + if(gDeleteThreads->IsLocked) { // Only free if structure is unused + gDeleteThreads->Status = THREAD_STAT_NULL; + free( gDeleteThreads ); + } + gDeleteThreads = thread; + } + + // Check if there is any tasks running + if(giNumActiveThreads == 0) { + Log("No Active threads, sleeping\n"); + __asm__ __volatile__ ("hlt"); + return; + } + + // Reduce remaining quantum + if(gCurrentThread->Remaining--) return; + // Reset quantum for next call + gCurrentThread->Remaining = gCurrentThread->Quantum; + + // Get machine state + __asm__ __volatile__ ("mov %%esp, %0":"=r"(esp)); + __asm__ __volatile__ ("mov %%ebp, %0":"=r"(ebp)); + eip = GetEIP(); + if(eip == SWITCH_MAGIC) return; // Check if a switch happened + + // Save machine state + gCurrentThread->ESP = esp; + gCurrentThread->EBP = ebp; + gCurrentThread->EIP = eip; + + // Special case: 1 thread + if(giNumActiveThreads == 1) + { + // Check if a switch is needed (NumActive can be 1 after a sleep) + if(gActiveThreads == gCurrentThread) return; + // Switch processes + gCurrentThread = gActiveThreads; + goto performSwitch; + } + + // Get the ticket number + ticket = number = rand() % giTotalTickets; + + //Log(" Proc_Scheduler: number = 0x%x\n", number); + + // Find the next thread + for(thread=gActiveThreads;thread;thread=thread->Next) + { + if(thread->NumTickets > number) break; + number -= thread->NumTickets; + } + + // Error Check + if(thread == NULL) + { + number = 0; + for(thread=gActiveThreads;thread;thread=thread->Next) + number += thread->NumTickets; + Panic("Bookeeping Failed - giTotalTicketCount (%i) != true count (%i)", + giTotalTickets, number); + } + + // Set current thread + gCurrentThread = thread; + + // Update Kernel Stack pointer + gTSSs[CPU].ESP0 = thread->KernelStack; + +performSwitch: + // Set address space + MM_SetCR3( gCurrentThread->CR3 ); + // Switch threads + __asm__ __volatile__ ( + "mov %1, %%esp\n\t" + "mov %2, %%ebp\n\t" + "jmp *%3" : : + "a"(SWITCH_MAGIC), "b"(gCurrentThread->ESP), + "d"(gCurrentThread->EBP), "c"(gCurrentThread->EIP)); + for(;;); // Shouldn't reach here +} + +/** + * \fn Uint rand() + * \brief Pseudo random number generator + * \note Unknown effectiveness (made up on the spot) + */ +Uint rand() +{ + static Uint randomState = RANDOM_SEED; + Uint ret = randomState; + int roll = randomState & 31; + randomState = (randomState << roll) | (randomState >> (32-roll)); + randomState ^= 0x9A3C5E78; + return ret; +} diff --git a/Kernel/syscalls.c b/Kernel/syscalls.c new file mode 100644 index 00000000..d89d6e6a --- /dev/null +++ b/Kernel/syscalls.c @@ -0,0 +1,146 @@ +/* + * AcessOS Microkernel Version + * syscalls.c + */ +#define DEBUG 1 + +#include +#include +#include +#include + +// === IMPORTS === +extern int Proc_Clone(Uint *Err, Uint Flags); +extern Uint Proc_SendMessage(Uint *Err, Uint Dest, Uint Length, void *Data); +extern int Proc_GetMessage(Uint *Err, Uint *Source, void *Buffer); +extern int Proc_Execve(char *File, char **ArgV, char **EnvP); +extern Uint Binary_Load(char *file, Uint *entryPoint); + +// === CODE === +void SyscallHandler(tSyscallRegs *Regs) +{ + Uint ret=0, err=0; + #if DEBUG + ENTER("iThread iNum", gCurrentThread->TID, Regs->Num); + if(Regs->Num < NUM_SYSCALLS) + LOG("Syscall %s", cSYSCALL_NAMES[Regs->Num]); + LOG("Arg1: 0x%x, Arg2: 0x%x, Arg3: 0x%x", Regs->Arg1, Regs->Arg2, Regs->Arg3); + #endif + switch(Regs->Num) + { + // -- Exit the current thread + case SYS_EXIT: Proc_Exit(); break; + + // -- Put the current thread to sleep + case SYS_SLEEP: Proc_Sleep(); Log(" SyscallHandler: %i is alive", gCurrentThread->TID); break; + + // -- Yield current timeslice + case SYS_YIELD: Proc_Yield(); break; + + // -- Clone the current thread + case SYS_CLONE: + // Call clone system call + ret = Proc_Clone(&err, Regs->Arg1); + // Change user stack if requested + if(ret == 0 && Regs->Arg2) + Regs->StackPointer = Regs->Arg2; + break; + + // -- Send a signal + case SYS_KILL: + err = -ENOSYS; + ret = -1; + break; + + // -- Map an address + case SYS_MAP: MM_Map(Regs->Arg1, Regs->Arg2); break; + // -- Allocate an address + case SYS_ALLOCATE: ret = MM_Allocate(Regs->Arg1); break; + // -- Unmap an address + case SYS_UNMAP: MM_Deallocate(Regs->Arg1); break; + + // -- Get Thread/Process IDs + case SYS_GETTID: ret = gCurrentThread->TID; break; + case SYS_GETPID: ret = gCurrentThread->TGID; break; + // -- Get User/Group IDs + case SYS_GETUID: ret = gCurrentThread->UID; break; + case SYS_GETGID: ret = gCurrentThread->GID; break; + + // -- Send Message + case SYS_SENDMSG: + ret = Proc_SendMessage(&err, Regs->Arg1, Regs->Arg2, (void*)Regs->Arg3); + break; + // -- Check for messages + case SYS_GETMSG: + ret = Proc_GetMessage(&err, (Uint*)Regs->Arg1, (void*)Regs->Arg2); + break; + + // -- Set the thread's name + case SYS_SETNAME: + // Sanity Check + if(!Regs->Arg1) { ret = -1; err = -EINVAL; break; } + // Lock Process + LOCK( &gCurrentThread->IsLocked ); + // Free old name + if(gCurrentThread->ThreadName && (Uint)gCurrentThread->ThreadName > 0xE0800000) + free(gCurrentThread->ThreadName); + // Change name + gCurrentThread->ThreadName = malloc( strlen( (char*)Regs->Arg1 ) + 1 ); + strcpy(gCurrentThread->ThreadName, (char*)Regs->Arg1); + // Unlock + RELEASE( &gCurrentThread->IsLocked ); + break; + + // --- + // Binary Control + // --- + case SYS_EXECVE: + ret = Proc_Execve((char*)Regs->Arg1, (char**)Regs->Arg2, (char**)Regs->Arg3); + break; + case SYS_LOADBIN: + ret = Binary_Load((char*)Regs->Arg1, (Uint*)Regs->Arg2); + break; + + // --- + // Virtual Filesystem + // --- + case SYS_OPEN: + ret = VFS_Open((char*)Regs->Arg1, Regs->Arg2 | VFS_OPENFLAG_USER); + break; + + case SYS_CLOSE: + VFS_Close( Regs->Arg1 ); + break; + + case SYS_WRITE: + #if BITS < 64 + VFS_Write( Regs->Arg1, Regs->Arg2|((Uint64)Regs->Arg3<<32), (void*)Regs->Arg4 ); + #else + VFS_Write( Regs->Arg1, Regs->Arg2, (void*)Regs->Arg3 ); + #endif + break; + + + // -- Debug + case SYS_DEBUG: + Log((char*)Regs->Arg1, + Regs->Arg2, Regs->Arg3, Regs->Arg4, Regs->Arg5, Regs->Arg6); + break; + + // -- Default (Return Error) + default: + Warning("SyscallHandler: Unknown System Call %i", Regs->Num); + if(Regs->Num < NUM_SYSCALLS) + Warning(" Syscall %s", cSYSCALL_NAMES[Regs->Num]); + err = -ENOSYS; + ret = -1; + break; + } + Regs->Return = ret; + Regs->Error = err; + #if DEBUG + LOG("SyscallHandler: err = %i", err); + LEAVE('x', ret); + #endif +} + diff --git a/Kernel/syscalls.lst b/Kernel/syscalls.lst new file mode 100644 index 00000000..280656ee --- /dev/null +++ b/Kernel/syscalls.lst @@ -0,0 +1,56 @@ + +0 +SYS_EXIT Kill this thread +SYS_CLONE Create a new thread +SYS_KILL Send a signal +SYS_SIGNAL Set signal Handler +SYS_YIELD Yield remainder of timestamp +SYS_SLEEP Sleep until messaged or signaled +SYS_WAIT Wait for a time or a message +SYS_WAITTID Wait for a thread to do something + +SYS_SETNAME Set's the name of the current thread +SYS_GETNAME Get's the name of a thread +SYS_GETTID Get current thread ID +SYS_GETPID Get current thread group ID +SYS_SETPRI Set process priority + +SYS_SENDMSG Send an IPC message +SYS_GETMSG Recieve an IPC message + +SYS_GETTIME Get the current timestamp + +SYS_SPAWN Spawn a new process +SYS_EXECVE Replace the current process +SYS_LOADBIN Load a binary into the current address space +SYS_UNLOADBIN Unload a loaded binary + +32 +SYS_GETPHYS Get the physical address of a page +SYS_MAP Map a physical address +SYS_ALLOCATE Allocate a page +SYS_UNMAP Unmap a page +SYS_PREALLOC Preallocate a page +SYS_SETFLAGS Set a page's flags +SYS_SHAREWITH Share a page with another thread + +SYS_GETUID Get current User ID +SYS_GETGID Get current Group ID +SYS_SETUID Set current user ID +SYS_SETGID Set current Group ID + +64 +SYS_OPEN Open a file +SYS_REOPEN Close a file and reuse its handle +SYS_CLOSE Close a file +SYS_READ Read from an open file +SYS_WRITE Write to an open file +SYS_IOCTL Perform an IOCtl Call +SYS_READDIR Read from an open directory +SYS_MKDIR Create a new directory +SYS_SYMLINK Create a symbolic link +SYS_GETACL Get an ACL Value +SYS_SETACL Set an ACL Value +SYS_FINFO Get file information +SYS_SEEK Seek to a new position in the file +SYS_TELL Return the current file position diff --git a/Kernel/system.c b/Kernel/system.c new file mode 100644 index 00000000..a736c46c --- /dev/null +++ b/Kernel/system.c @@ -0,0 +1,343 @@ +/* + * Acess 2 + * Architecture Independent System Init + * system.c + */ +#include + +// === IMPORTS === +extern int Modules_LoadBuiltins(); +extern int PCI_Install(); + +// === PROTOTYPES === +void System_Init(char *ArgString); +void System_ParseCommandLine(char *ArgString); +void System_ParseVFS(char *Arg); +void System_ParseSetting(char *Arg); +void System_ExecuteScript(); + int System_Int_GetString(char *Str, char **Dest); + +// === GLOBALS === +char *gsInitPath = "/Acess/Bin/init"; +char *gsConfigScript = "/Acess/BootConf.cfg"; + +// === CODE === +void System_Init(char *ArgString) +{ + // - Start Builtin Drivers & Filesystems + PCI_Install(); + //ATA_Install(); + Modules_LoadBuiltins(); + + // - Parse Kernel's Command Line + System_ParseCommandLine(ArgString); + + // - Execute the Config Script + Log("Executing config script..."); + System_ExecuteScript(); +} + +/** + * \fn void System_ParseCommandLine(char *ArgString) + * \brief Parses the kernel's command line and sets the environment + */ +void System_ParseCommandLine(char *ArgString) +{ + char *argv[32]; + int argc; + int i; + char *str; + + Log("Kernel Command Line: \"%s\"", ArgString); + + // --- Get Arguments --- + str = ArgString; + for( argc = 0; argc < 32; argc++ ) + { + while(*str == ' ') str++; // Eat Whitespace + if(*str == '\0') { argc--; break;} // End of string + argv[argc] = str; + while(*str && *str != ' ') + { + /*if(*str == '"') { + while(*str && !(*str == '"' && str[-1] != '\\')) + str ++; + }*/ + str++; + } + if(*str == '\0') break; // End of string + *str = '\0'; // Cap off argument string + str ++; // and increment the string pointer + } + if(argc < 32) + argc ++; // Count last argument + + // --- Parse Arguments --- + for( i = 1; i < argc; i++ ) + { + if( argv[i][0] == '/' ) + System_ParseVFS( argv[i] ); + else + System_ParseSetting( argv[i] ); + } +} + +/** + * \fn void System_ParseVFS(char *Arg) + */ +void System_ParseVFS(char *Arg) +{ + char *value; + int fd; + + value = Arg; + // Search for the '=' token + while( *value && *value != '=' ) + value++; + + // Check if the equals was found + if( *value == '\0' ) { + Warning("Expected '=' in the string '%s'", Arg); + return ; + } + + // Edit string + *value = '\0'; value ++; + + // Check assignment type + // - Symbolic Link = + if(value[0] == '/') + { + Log("Symbolic link '%s' pointing to '%s'", Arg, value); + VFS_Symlink(Arg, value); + } + // - Mount =: + else + { + char *dev = value; + // Find colon + while(*dev && *dev != ':') dev++; + if(*dev) { + *dev = '\0'; + dev++; // Eat ':' + } + // Create Mountpoint + if( (fd = VFS_Open(Arg, 0)) == -1 ) { + Log("Creating directory '%s'", Arg, value); + VFS_MkDir( Arg ); + } else { + VFS_Close(fd); + } + // Mount + Log("Mounting '%s' to '%s' ('%s')", dev, Arg, value); + VFS_Mount(dev, Arg, value, ""); + } +} + +/** + * \fn void System_ParseSetting(char *Arg) + */ +void System_ParseSetting(char *Arg) +{ + char *value; + value = Arg; + + // Search for the '=' token + while( *value && *value != '=' ) + value++; + + // Check for boolean/flag (no '=') + if(*value == '\0') + { + if(strcmp(Arg, "") == 0) { + } else { + Warning("Kernel flag '%s' is not recognised", Arg); + } + } + else + { + *value = '\0'; // Remove '=' + value ++; // and eat it's position + + if(strcmp(Arg, "SCRIPT") == 0) { + Log("Config Script: '%s'", value); + gsConfigScript = value; + } else { + Warning("Kernel config setting '%s' is not recognised", Arg); + } + + } +} + +/** + * \fn void System_ExecuteScript() + */ +void System_ExecuteScript() +{ + int fp; + int fLen = 0; + int i = 0, lineStart; + char *sArg1, *sArg2, *sArg3; + char *fData; + + // Open Script + fp = VFS_Open(gsConfigScript, VFS_OPENFLAG_READ); + if(fp == -1) { + Warning("[CFG] Passed script '%s' does not exist", gsConfigScript); + return; + } + + // Read into memory buffer + VFS_Seek(fp, 0, SEEK_END); + fLen = VFS_Tell(fp); + VFS_Seek(fp, 0, SEEK_SET); + fData = malloc(fLen+1); + VFS_Read(fp, fLen, fData); + fData[fLen] = '\0'; + VFS_Close(fp); + + // Read Script + while(i < fLen) + { + sArg1 = sArg2 = sArg3 = NULL; + + lineStart = i; + // Clear leading whitespace and find empty lines + while(i < fLen && (fData[i] == ' ' || fData[i]=='\t')) i ++; + if(i == fLen) break; + if(fData[i] == '\n') { + i++; + continue; + } + + // Comment + if(fData[i] == ';' || fData[i] == '#') { + while(i < fLen && fData[i] != '\n') i ++; + i ++; + continue; + } + + // Commands + // - Mount + if(strncmp("mount ", fData+i, 6) == 0) { + i += 6; + i += System_Int_GetString(fData+i, &sArg1); + if(!sArg1) goto read2eol; + i += System_Int_GetString(fData+i, &sArg2); + if(!sArg2) goto read2eol; + i += System_Int_GetString(fData+i, &sArg3); + if(!sArg3) goto read2eol; + //Log("[CFG ] Mount '%s' to '%s' (%s)\n", sArg1, sArg2, sArg3); + VFS_Mount(sArg1, sArg2, sArg3, ""); + } + // - Load Module + else if(strncmp("module ", fData+i, 6) == 0) { + i += 7; + i += System_Int_GetString(fData+i, &sArg1); + if(!sArg1) goto read2eol; + //Log("[CFG ] Load Module '%s'\n", sArg1); + Module_LoadFile(sArg1, ""); //!\todo Use the rest of the line as the argument string + } + // - Load Module + else if(strncmp("edimod ", fData+i, 6) == 0) { + i += 7; + i += System_Int_GetString(fData+i, &sArg1); + if(!sArg1) goto read2eol; + Log("[CFG ] Load EDI Module '%s'\n", sArg1); + Module_LoadFile(sArg1, ""); + } + // - Symlink + else if(strncmp("symlink ", fData+i, 7) == 0) { + i += 8; + i += System_Int_GetString(fData+i, &sArg1); + if(!sArg1) goto read2eol; + i += System_Int_GetString(fData+i, &sArg2); + if(!sArg2) goto read2eol; + Log("[CFG ] Symlink '%s' pointing to '%s'\n", sArg1, sArg2); + VFS_Symlink(sArg1, sArg2); + } + // - New Directory + else if(strncmp("mkdir ", fData+i, 5) == 0) { + i += 6; + i += System_Int_GetString(fData+i, &sArg1); + if(!sArg1) goto read2eol; + Log("[CFG ] New Directory '%s'\n", sArg1); + VFS_MkDir(sArg1); + } + // - Spawn a task + else if(strncmp("spawn ", fData+i, 5) == 0) { + i += 6; + i += System_Int_GetString(fData+i, &sArg1); + if(!sArg1) goto read2eol; + Log("[CFG ] Starting '%s' as a new task\n", sArg1); + Proc_Spawn(sArg1); + } + else { + Warning("Unknown configuration command, Line: '%s'", fData+i); + goto read2eol; + } + read2eol: + if(sArg1) free(sArg1); + if(sArg2) free(sArg2); + if(sArg3) free(sArg3); + // Skip to EOL + while(i < fLen && fData[i] != '\n') i++; + i ++; // Skip \n + } + free(fData); +} + +/** + * \fn int System_Int_GetString(char *Str, char **Dest) + * \brief Gets a string from another + * \note Destructive + * \param Str Input String + * \param Dest Pointer to output pointer + * \return Characters eaten from input + */ +int System_Int_GetString(char *Str, char **Dest) +{ + int pos = 0; + int start = 0; + int len; + + //LogF("GetString: (Str='%s', Dest=0x%x)\n", Str, Dest); + + while(Str[pos] == ' ' || Str[pos] == '\t') pos++; + if(Str[pos] == '\n' || Str[pos] == '\0') { + *Dest = NULL; + return pos; + } + + // Quoted String + if(Str[pos] == '"') + { + pos ++; + start = pos; + while(Str[pos] != '"') pos++; + + len = pos - start; + *Dest = malloc( len + 1 ); + memcpy( *Dest, Str+start, len ); + (*Dest)[len] = '\0'; + + //LogF("GetString: RETURN *Dest = '%s'\n", *Dest); + + pos++; + return pos; + } + + // Non-Quoted String - Whitespace deliminated + start = pos; + while(Str[pos] != ' ' && Str[pos] != '\t' && Str[pos] != '\n') pos++; + + len = pos - start; + //LogF(" GetString: len = %i\n", len); + *Dest = malloc( len + 1 ); + memcpy( *Dest, Str+start, len ); + (*Dest)[len] = '\0'; + + //LogF("GetString: RETURN *Dest = '%s'\n", *Dest); + + return pos; +} diff --git a/Kernel/vfs.c b/Kernel/vfs.c new file mode 100644 index 00000000..c1d6eef5 --- /dev/null +++ b/Kernel/vfs.c @@ -0,0 +1,12 @@ +/* + * Acess 2 + * binary.c + * - Binary File Loader + */ +#include +#include + +// === GLOBALS === + + +// === CODE === diff --git a/Kernel/vfs/acls.c b/Kernel/vfs/acls.c new file mode 100644 index 00000000..52c802c6 --- /dev/null +++ b/Kernel/vfs/acls.c @@ -0,0 +1,58 @@ +/* + * Acess Micro VFS + */ +#include +#include "vfs.h" +#include "vfs_int.h" + +// === GLOBALS === +tVFS_ACL gVFS_ACL_EveryoneRWX = { {0,-1}, {0,VFS_PERM_ALL} }; +tVFS_ACL gVFS_ACL_EveryoneRW = { {0,-1}, {0,VFS_PERM_ALL^VFS_PERM_EXECUTE} }; +tVFS_ACL gVFS_ACL_EveryoneRX = { {0,-1}, {0,VFS_PERM_READ|VFS_PERM_EXECUTE} }; +tVFS_ACL gVFS_ACL_EveryoneRO = { {0,-1}, {0,VFS_PERM_READ} }; + +// === CODE === +/** + * \fn int VFS_CheckACL(tVFS_Node *Node, Uint Permissions) + * \brief Checks the permissions on a file + */ +int VFS_CheckACL(tVFS_Node *Node, Uint Permissions) +{ + int i; + int uid = Proc_GetUID(); + int gid = Proc_GetGID(); + + // Root can do anything + if(uid == 0) return 1; + + // Root only file?, fast return + if( Node->NumACLs == 0 ) return 0; + + // Check Deny Permissions + for(i=0;iNumACLs;i++) + { + if(!Node->ACLs[i].Inv) continue; // Ignore ALLOWs + if(Node->ACLs[i].ID != -1) + { + if(!Node->ACLs[i].Group && Node->ACLs[i].ID != uid) continue; + if(Node->ACLs[i].Group && Node->ACLs[i].ID != gid) continue; + } + + if(Node->ACLs[i].Perms & Permissions) return 0; + } + + // Check for allow permissions + for(i=0;iNumACLs;i++) + { + if(Node->ACLs[i].Inv) continue; // Ignore DENYs + if(Node->ACLs[i].ID != -1) + { + if(!Node->ACLs[i].Group && Node->ACLs[i].ID != uid) continue; + if(Node->ACLs[i].Group && Node->ACLs[i].ID != gid) continue; + } + + if((Node->ACLs[i].Perms & Permissions) == Permissions) return 1; + } + + return 0; +} diff --git a/Kernel/vfs/dir.c b/Kernel/vfs/dir.c new file mode 100644 index 00000000..aeda00ea --- /dev/null +++ b/Kernel/vfs/dir.c @@ -0,0 +1,133 @@ +/* + */ +#include "vfs.h" +#include "vfs_int.h" + +// === IMPORTS === +extern tVFS_Mount *gRootMount; + +// === PROTOTYPES === + int VFS_MkDir(char *Path); + int VFS_MkNod(char *Path, Uint Flags); + +// === CODE === +/** + * \fn int VFS_MkDir(char *Path) + * \brief Create a new node + * \param Path Path of directory to create + */ +int VFS_MkDir(char *Path) +{ + return VFS_MkNod(Path, VFS_FFLAG_DIRECTORY); +} + +/** + * \fn int VFS_MkNod(char *Path, Uint Flags) + * \brief Create a new node in a directory + * \param Path Path of new node + * \param Flags Flags to apply to the node + */ +int VFS_MkNod(char *Path, Uint Flags) +{ + char *absPath, *name; + int pos=0, oldpos = 0; + tVFS_Node *parent; + int ret; + + Debug_Enter("VFS_MkNod", "sPath xFlags", Path, Flags); + + absPath = VFS_GetAbsPath(Path); + + while( (pos = strpos8(&absPath[pos+1], '/')) != -1 ) oldpos = pos; + absPath[oldpos] = '\0'; // Mutilate path + name = &absPath[oldpos+1]; + + // Check for root + if(absPath[0] == '\0') + parent = VFS_ParsePath("/", NULL); + else + parent = VFS_ParsePath(absPath, NULL); + + if(!parent) return -1; // Error Check + + // Permissions Check + if( !VFS_CheckACL(parent, VFS_PERM_EXECUTE|VFS_PERM_WRITE) ) { + if(parent->Close) parent->Close( parent ); + free(absPath); + Debug_Leave("VFS_MkNod", 'i', -1); + return -1; + } + + Debug_Log("VFS_MkNod", "parent = %p\n", parent); + + if(parent->MkNod == NULL) { + Warning("VFS_MkNod - Directory has no MkNod method"); + Debug_Leave("VFS_MkNod", 'i', -1); + return -1; + } + + // Create node + ret = parent->MkNod(parent, name, Flags); + + // Free allocated string + free(absPath); + + // Free Parent + if(parent->Close) parent->Close( parent ); + + // Error Check + if(ret == 0) return -1; + + Debug_Leave("VFS_MkNod", 'i', 0); + return 0; +} + +/** + * \fn int VFS_Symlink(char *Name, char *Link) + * \brief Creates a symlink called \a Name to \a Link + * \param Name Name of symbolic link + * \param Link Destination of symbolic link + */ +int VFS_Symlink(char *Name, char *Link) +{ + char *realLink; + int fp; + tVFS_Node *destNode; + + //LogF("vfs_symlink: (name='%s', link='%s')\n", name, link); + + // Get absolue path name + Link = VFS_GetAbsPath( Link ); + if(!Link) { + Warning("Path '%s' is badly formed", Link); + return -1; + } + + // Get true path and node + destNode = VFS_ParsePath( Link, &realLink ); + free(Link); + + // Check if destination exists + if(!destNode) { + Warning("File '%s' does not exist, symlink not created", Link); + return -1; + } + + // Derefence the destination + if(destNode->Close) destNode->Close(destNode); + + // Make node + if( VFS_MkNod(Name, VFS_FFLAG_SYMLINK) != 0 ) { + Warning("Unable to create link node '%s'", Name); + return -2; // Make link node + } + + // Write link address + fp = VFS_Open(Name, VFS_OPENFLAG_WRITE|VFS_OPENFLAG_NOLINK); + VFS_Write(fp, strlen(realLink), realLink); + VFS_Close(fp); + + free(realLink); + + return 1; +} diff --git a/Kernel/vfs/fs/devfs.c b/Kernel/vfs/fs/devfs.c new file mode 100644 index 00000000..25be4907 --- /dev/null +++ b/Kernel/vfs/fs/devfs.c @@ -0,0 +1,96 @@ +/* + * Acess 2 + * Device Filesystem (DevFS) + * - vfs/fs/devfs.c + */ +#include +#include +#include + +// === PROTOTYPES === + int DevFS_AddDevice(tDevFS_Driver *Dev); +tVFS_Node *DevFS_InitDevice(char *Device, char *Options); +char *DevFS_ReadDir(tVFS_Node *Node, int Pos); +tVFS_Node *DevFS_FindDir(tVFS_Node *Node, char *Name); + +// === GLOBALS === +tVFS_Driver gDevFS_Info = { + "devfs", 0, DevFS_InitDevice, NULL + }; +tVFS_Node gDevFS_RootNode = { + .NumACLs = 1, + .ACLs = &gVFS_ACL_EveryoneRW, + .ReadDir = DevFS_ReadDir, + .FindDir = DevFS_FindDir + }; +tDevFS_Driver *gDevFS_Drivers = NULL; + int giDevFS_NextID = 1; + +// === CODE === +/** + * \fn int DevFS_AddDevice(tDevFS_Driver *Dev) + */ +int DevFS_AddDevice(tDevFS_Driver *Dev) +{ + Dev->Next = gDevFS_Drivers; + gDevFS_Drivers = Dev; + + return giDevFS_NextID++; +} + +/** + * \fn tVFS_Node *DevFS_InitDevice(char *Device, char *Options) + * \brief Initialise the DevFS and detect double-mounting, or just do nothing + * \stub + */ +tVFS_Node *DevFS_InitDevice(char *Device, char *Options) +{ + return &gDevFS_RootNode; +} + +/** + * \fn char *DevFS_ReadDir(tVFS_Node *Node, int Pos) + */ +char *DevFS_ReadDir(tVFS_Node *Node, int Pos) +{ + tDevFS_Driver *dev; + + if(Pos < 0) return NULL; + + for(dev = gDevFS_Drivers; + dev && Pos--; + dev = dev->Next + ); + + return dev->Name; +} + +/** + * \fn tVFS_Node *DevFS_FindDir(tVFS_Node *Node, char *Name) + * \brief Get an entry from the devices directory + */ +tVFS_Node *DevFS_FindDir(tVFS_Node *Node, char *Name) +{ + tDevFS_Driver *dev; + + //ENTER("pNode sName", Node, Name); + + for(dev = gDevFS_Drivers; + dev; + dev = dev->Next + ) + { + //LOG("dev = %p", dev); + //LOG("dev->Name = '%s'", dev->Name); + if(strcmp(dev->Name, Name) == 0) { + //LEAVE('p', &dev->RootNode); + return &dev->RootNode; + } + } + + //LEAVE('n'); + return NULL; +} + +// --- EXPORTS --- +EXPORT(DevFS_AddDevice); diff --git a/Kernel/vfs/fs/fat.c b/Kernel/vfs/fs/fat.c new file mode 100644 index 00000000..c4f66713 --- /dev/null +++ b/Kernel/vfs/fs/fat.c @@ -0,0 +1,920 @@ +/* + * Acess2 + * FAT12/16/32 Driver Version (Incl LFN) + */ +//INCLUDES +#include +#include +#include +#include "fs_fat.h" + +#define DEBUG 0 +#define VERBOSE 1 + +#if DEBUG +# define DEBUGS(v...) Log(v) +#else +# define DEBUGS(v...) +# undef ENTER +# undef LOG +# undef LEAVE +# define ENTER(...) +# define LOG(...) +# define LEAVE(...) +#endif + +#define CACHE_FAT 1 //!< Caches the FAT in memory +#define USE_LFN 1 //!< Enables the use of Long File Names + +// === TYPES === +#if USE_LFN +typedef struct s_lfncache { + Uint Inode, Impl; + int id; + char Name[256]; + struct s_lfncache *Next; +} t_lfncache; +#endif + +// === PROTOTYPES === + int FAT_Install(char **Arguments); +tVFS_Node *FAT_InitDevice(char *device, char *options); +void FAT_CloseDevice(tVFS_Node *node); +Uint64 FAT_Read(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer); +Uint64 FAT_Write(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer); +char *FAT_ReadDir(tVFS_Node *dirNode, int dirpos); +tVFS_Node *FAT_FindDir(tVFS_Node *dirNode, char *file); + int FAT_Mknod(tVFS_Node *Node, char *Name, Uint Flags); + int FAT_Relink(tVFS_Node *node, char *OldName, char *NewName); +void FAT_CloseFile(tVFS_Node *node); + +// === SEMI-GLOBALS === +MODULE_DEFINE(0, 0x5B /*v0.90*/, FAT32, FAT_Install, NULL); +tFAT_VolInfo gFAT_Disks[8]; + int giFAT_PartCount = 0; +#if CACHE_FAT +Uint32 *fat_cache[8]; +#endif +#if USE_LFN +t_lfncache *fat_lfncache; +#endif +tVFS_Driver gFAT_FSInfo = { + "fat", 0, FAT_InitDevice, NULL + }; + +// === CODE === +/** + * \fn int FAT_Install(char **Arguments) + * \brief + */ +int FAT_Install(char **Arguments) +{ + VFS_AddDriver( &gFAT_FSInfo ); + return 0; +} + +/* Reads the boot sector of a disk and prepares the structures for it + */ +tVFS_Node *FAT_InitDevice(char *Device, char *options) +{ + fat_bootsect *bs; + int i; + Uint32 FATSz, RootDirSectors, TotSec, CountofClusters; + tVFS_Node *node = NULL; + tFAT_VolInfo *diskInfo = &gFAT_Disks[giFAT_PartCount]; + + //Temporary Pointer + bs = &diskInfo->bootsect; + + //Open device and read boot sector + diskInfo->fileHandle = VFS_Open(Device, VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE); + if(diskInfo->fileHandle == -1) { + Warning("FAT_InitDisk - Unable to open device '%s'", Device); + return NULL; + } + + VFS_ReadAt(diskInfo->fileHandle, 0, 512, bs); + + if(bs->bps == 0 || bs->spc == 0) { + Warning("FAT_InitDisk - Error in FAT Boot Sector\n"); + return NULL; + } + + //FAT Type Determining + // From Microsoft FAT Specifcation + RootDirSectors = ((bs->files_in_root*32) + (bs->bps - 1)) / bs->bps; + + if(bs->fatSz16 != 0) FATSz = bs->fatSz16; + else FATSz = bs->spec.fat32.fatSz32; + + if(bs->totalSect16 != 0) TotSec = bs->totalSect16; + else TotSec = bs->totalSect32; + + CountofClusters = (TotSec - (bs->resvSectCount + (bs->fatCount * FATSz) + RootDirSectors)) / bs->spc; + + if(CountofClusters < 4085) + diskInfo->type = FAT12; + else if(CountofClusters < 65525) + diskInfo->type = FAT16; + else + diskInfo->type = FAT32; + + #if VERBOSE + { + char *sFatType, *sSize; + Uint iSize = CountofClusters * bs->spc / 2; + + switch(diskInfo->type) + { + case FAT12: sFatType = "FAT12"; break; + case FAT16: sFatType = "FAT16"; break; + case FAT32: sFatType = "FAT32"; break; + } + if(iSize <= 2*1024) { + sSize = "KiB"; + } + else if(iSize <= 2*1024*1024) { + sSize = "MiB"; + iSize >>= 10; + } + else { + sSize = "GiB"; + iSize >>= 20; + } + Log("[FAT ] '%s' %s, %i %s", Device, sFatType, iSize, sSize); + } + #endif + + //Get Name + //puts(" Name: "); + if(diskInfo->type == FAT32) { + for(i=0;i<11;i++) + diskInfo->name[i] = (bs->spec.fat32.label[i] == ' ' ? '\0' : bs->spec.fat32.label[i]); + } + else { + for(i=0;i<11;i++) + diskInfo->name[i] = (bs->spec.fat16.label[i] == ' ' ? '\0' : bs->spec.fat16.label[i]); + } + diskInfo->name[11] = '\0'; + //puts(diskInfo->name); putch('\n'); + + //Compute Root directory offset + if(diskInfo->type == FAT32) + diskInfo->rootOffset = bs->spec.fat32.rootClust; + else + diskInfo->rootOffset = (FATSz * bs->fatCount) / bs->spc; + + diskInfo->clusterCount = CountofClusters; + + diskInfo->firstDataSect = bs->resvSectCount + (bs->fatCount * FATSz) + RootDirSectors; + + //Allow for Caching the FAT + #if CACHE_FAT + { + Uint32 Ofs; + fat_cache[ giFAT_PartCount ] = (Uint32*)malloc(sizeof(Uint32)*CountofClusters); + if(fat_cache[giFAT_PartCount] == NULL) { + Warning("FAT_InitDisk - Heap Exhausted\n"); + return NULL; + } + Ofs = bs->resvSectCount*512; + if(diskInfo->type == FAT12) { + Uint32 val; + int j; + char buf[1536]; + for(i=0;ifileHandle, Ofs, 3*512, buf); + Ofs += 3*512; + } + val = *((int*)(buf+j*3)); + fat_cache[giFAT_PartCount][i*2] = val & 0xFFF; + fat_cache[giFAT_PartCount][i*2+1] = (val>>12) & 0xFFF; + } + } + if(diskInfo->type == FAT16) { + Uint16 buf[256]; + for(i=0;ifileHandle, Ofs, 512, buf); + Ofs += 512; + } + fat_cache[giFAT_PartCount][i] = buf[i&255]; + } + } + if(diskInfo->type == FAT32) { + Uint32 buf[128]; + for(i=0;ifileHandle, Ofs, 512, buf); + Ofs += 512; + } + fat_cache[giFAT_PartCount][i] = buf[i&127]; + } + } + DEBUGS(" FAT_InitDisk: FAT Fully Cached\n"); + } + #endif /*CACHE_FAT*/ + + //Initalise inode cache for FAT + gFAT_Disks[giFAT_PartCount].inodeHandle = Inode_GetHandle(); + + #if DEBUG + Log(" FAT_InitDisk: Inode Cache handle is %i\n", gFAT_Disks[giFAT_PartCount].inodeHandle); + #endif + + //== VFS Interface + node = &gFAT_Disks[giFAT_PartCount].rootNode; + //node->Name = gFAT_Disks[giFAT_PartCount].name; + node->Inode = diskInfo->rootOffset; + node->Size = bs->files_in_root; //Unknown - To be set on readdir + node->ImplInt = giFAT_PartCount; + + node->ReferenceCount = 1; + + node->UID = 0; node->GID= 0; + node->NumACLs = 1; + node->ACLs = &gVFS_ACL_EveryoneRWX; + node->Flags = VFS_FFLAG_DIRECTORY; + node->CTime = node->MTime = node->ATime = now(); + + node->Read = node->Write = NULL; + node->ReadDir = FAT_ReadDir; + node->FindDir = FAT_FindDir; + node->Relink = FAT_Relink; + node->MkNod = FAT_Mknod; + node->Close = FAT_CloseDevice; + + giFAT_PartCount ++; + return node; +} + +/** + * \fn void FAT_CloseDevice(tVFS_Node *node) + * \brief Closes a mount and marks it as free + */ +void FAT_CloseDevice(tVFS_Node *node) +{ + node->ReferenceCount --; + + if(node->ReferenceCount > 0) return; + + // Close Disk Handle + VFS_Close( gFAT_Disks[node->ImplInt].fileHandle ); + Inode_ClearCache(gFAT_Disks[node->ImplInt].inodeHandle); + gFAT_Disks[node->ImplInt].fileHandle = -2; + return; +} + +/** + * \fn static Uint32 FAT_int_GetFatValue(int handle, Uint32 cluster) + * \brief Fetches a value from the FAT + */ +static Uint32 FAT_int_GetFatValue(int handle, Uint32 cluster) +{ + Uint32 val; + ENTER("iHandle xCluster", handle, cluster); + #if CACHE_FAT + val = fat_cache[handle][cluster]; + #else + if(gFAT_Disks[handle].type == FAT12) { + VFS_ReadAt(gFAT_Disks[handle].fileHandle, 512+(cluster&~1)*3, 3, &val); + val = (cluster&1 ? val&0xFFF : val>>12); + } else if(gFAT_Disks[handle].type == FAT16) { + VFS_ReadAt(gFAT_Disks[handle].fileHandle, 512+cluster*2, 2, &val); + } else { + VFS_ReadAt(gFAT_Disks[handle].fileHandle, 512+cluster*4, 4, &val); + } + #endif /*CACHE_FAT*/ + LEAVE('x', val); + return val; +} + +/* Reads a cluster's data + */ +static void FAT_int_ReadCluster(int Handle, Uint32 Cluster, int Length, void *Buffer) +{ + #if DEBUG + ENTER("iHandle xCluster iLength pBuffer", Handle, Cluster, Length, Buffer); + #endif + VFS_ReadAt( + gFAT_Disks[Handle].fileHandle, + (gFAT_Disks[Handle].firstDataSect + (Cluster-2)*gFAT_Disks[Handle].bootsect.spc ) + * gFAT_Disks[Handle].bootsect.bps, + Length, + Buffer + ); + #if DEBUG + LEAVE('-'); + #endif +} + +/** + * \fn Uint64 FAT_Read(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer) + * \brief Reads data from a specified file + */ +Uint64 FAT_Read(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer) +{ + int preSkip, count; + int handle = node->ImplInt; + int i, cluster, pos; + int bpc; + void *tmpBuf; + Uint eocMarker; + tFAT_VolInfo *disk = &gFAT_Disks[node->ImplInt]; + + ENTER("Xoffset Xlength pbuffer", offset, length, buffer); + + // Calculate and Allocate Bytes Per Cluster + bpc = disk->bootsect.spc * disk->bootsect.bps; + tmpBuf = (void*) malloc(bpc); + LOG("malloc'd %i bytes", bpc); + + // Cluster is stored in Inode Field + cluster = node->Inode; + + // Get EOC Marker + if (disk->type == FAT12) eocMarker = EOC_FAT12; + else if(disk->type == FAT16) eocMarker = EOC_FAT16; + else if(disk->type == FAT32) eocMarker = EOC_FAT32; + else { + Log("ERROR: Unsupported FAT Variant.\n"); + free(tmpBuf); + LEAVE('i', 0); + return 0; + } + + // Single Cluster including offset + if(length + offset < bpc) + { + FAT_int_ReadCluster(handle, cluster, bpc, tmpBuf); + memcpy( buffer, (void*)( tmpBuf + offset%bpc ), length ); + free(tmpBuf); + LEAVE('i', 1); + return 1; + } + + preSkip = offset / bpc; + + //Skip previous clusters + for(i=preSkip;i--;) { + cluster = FAT_int_GetFatValue(handle, cluster); + if(cluster == eocMarker) { + Warning("FAT_Read - Offset is past end of cluster chain mark"); + } + } + + // Get Count of Clusters to read + count = ((offset%bpc+length) / bpc) + 1; + + // Get buffer Position after 1st cluster + pos = bpc - offset%bpc; + + // Read 1st Cluster + FAT_int_ReadCluster(handle, cluster, bpc, tmpBuf); + memcpy( + buffer, + (void*)( tmpBuf + (bpc-pos) ), + (pos < length ? pos : length) + ); + + if (count == 1) { + free(tmpBuf); + LEAVE('i', 1); + return 1; + } + + cluster = FAT_int_GetFatValue(handle, cluster); + + #if DEBUG + LOG("pos=%i\n", pos); + LOG("Reading the rest of the clusters\n"); + #endif + + + //Read the rest of the cluster data + for( i = 1; i < count-1; i++ ) + { + FAT_int_ReadCluster(handle, cluster, bpc, tmpBuf); + memcpy((void*)(buffer+pos), tmpBuf, bpc); + pos += bpc; + cluster = FAT_int_GetFatValue(handle, cluster); + if(cluster == eocMarker) { + Warning("FAT_Read - Read past End of Cluster Chain"); + free(tmpBuf); + LEAVE('i', 0); + return 0; + } + } + + FAT_int_ReadCluster(handle, cluster, bpc, tmpBuf); + memcpy((void*)(buffer+pos), tmpBuf, length-pos); + + #if DEBUG + LOG("Free tmpBuf(0x%x) and Return\n", tmpBuf); + #endif + + free(tmpBuf); + LEAVE('X', length); + return length; +} + +/** + * \fn Uint64 FAT_Write(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer) + */ +Uint64 FAT_Write(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer) +{ + return 0; +} + +/** + * \fn static void FAT_int_ProperFilename(char *dest, char *src) + * \brief Converts a FAT directory entry name into a proper filename + */ +static void FAT_int_ProperFilename(char *dest, char *src) +{ + int a, b; + + for( a = 0; a < 8; a++) { + if(src[a] == ' ') break; + dest[a] = src[a]; + } + b = a; + a = 8; + if(src[8] != ' ') + dest[b++] = '.'; + for( ; a < 11; a++, b++) { + if(src[a] == ' ') break; + dest[b] = src[a]; + } + dest[b] = '\0'; + #if DEBUG + //Log("FAT_int_ProperFilename: dest='%s'", dest); + #endif +} + +/** + * \fn char *FAT_int_CreateName(tVFS_Node *parent, fat_filetable *ft, char *LongFileName) + * \brief Converts either a LFN or a 8.3 Name into a proper name + */ +char *FAT_int_CreateName(tVFS_Node *parent, fat_filetable *ft, char *LongFileName) +{ + char *ret; + int len; + #if USE_LFN + if(LongFileName && LongFileName[0] != '\0') + { + len = strlen(LongFileName); + ret = malloc(len+1); + strcpy(ret, LongFileName); + } + else + { + #endif + ret = (char*) malloc(13); + memset(ret, 13, '\0'); + FAT_int_ProperFilename(ret, ft->name); + #if USE_LFN + } + #endif + return ret; +} + +/** + * \fn tVFS_Node *FAT_int_CreateNode(tVFS_Node *parent, fat_filetable *ft, char *LongFileName) + * \brief Creates a tVFS_Node structure for a given file entry + */ +tVFS_Node *FAT_int_CreateNode(tVFS_Node *parent, fat_filetable *ft, char *LongFileName) +{ + tVFS_Node node = {0}; + tVFS_Node *ret; + + ENTER("pParent pFT sLongFileName", parent, ft, LongFileName); + + // Get Name + //node.Name = FAT_int_CreateName(parent, ft, LongFileName); + // Set Other Data + node.Inode = ft->cluster | (ft->clusterHi<<16); + node.Size = ft->size; + node.ImplInt = parent->ImplInt; + node.UID = 0; node.GID = 0; + node.NumACLs = 1; + node.ACLs = &gVFS_ACL_EveryoneRWX; // RWXRWXRWX + + node.Flags = 0; + if(ft->attrib & ATTR_DIRECTORY) node.Flags |= VFS_FFLAG_DIRECTORY; + if(ft->attrib & ATTR_READONLY) node.Flags |= VFS_FFLAG_READONLY; + + node.ATime = timestamp(0,0,0, + ((ft->adate&0x1F)-1), //Days + ((ft->adate&0x1E0)-1), //Months + 1980+((ft->adate&0xFF00)>>8)); //Years + + node.CTime = ft->ctimems * 10; //Miliseconds + node.CTime += timestamp( + (ft->ctime&0x1F)<<1, //Seconds + ((ft->ctime&0x3F0)>>5), //Minutes + ((ft->ctime&0xF800)>>11), //Hours + ((ft->cdate&0x1F)-1), //Days + ((ft->cdate&0x1E0)-1), //Months + 1980+((ft->cdate&0xFF00)>>8)); //Years + + node.MTime = timestamp( + (ft->mtime&0x1F)<<1, //Seconds + ((ft->mtime&0x3F0)>>5), //Minuites + ((ft->mtime&0xF800)>>11), //Hours + ((ft->mdate&0x1F)-1), //Days + ((ft->mdate&0x1E0)-1), //Months + 1980+((ft->mdate&0xFF00)>>8)); //Years + + if(node.Flags & VFS_FFLAG_DIRECTORY) { + node.ReadDir = FAT_ReadDir; + node.FindDir = FAT_FindDir; + node.MkNod = FAT_Mknod; + } else { + node.Read = FAT_Read; + node.Write = FAT_Write; + } + node.Close = FAT_CloseFile; + node.Relink = FAT_Relink; + + ret = Inode_CacheNode(gFAT_Disks[parent->ImplInt].inodeHandle, &node); + LEAVE('p', ret); + return ret; +} + +#if USE_LFN +/** + \fn char *FAT_int_GetLFN(tVFS_Node *node) + \brief Return pointer to LFN cache entry + */ +char *FAT_int_GetLFN(tVFS_Node *node) +{ + t_lfncache *tmp; + tmp = fat_lfncache; + while(tmp) + { + if(tmp->Inode == node->Inode && tmp->Impl == node->ImplInt) + return tmp->Name; + tmp = tmp->Next; + } + tmp = malloc(sizeof(t_lfncache)); + tmp->Inode = node->Inode; + tmp->Impl = node->ImplInt; + memset(tmp->Name, 0, 256); + + tmp->Next = fat_lfncache; + fat_lfncache = tmp; + + return tmp->Name; +} + +/** + \fn void FAT_int_DelLFN(tVFS_Node *node) + \brief Delete a LFN cache entry +*/ +void FAT_int_DelLFN(tVFS_Node *node) +{ + t_lfncache *tmp; + + if(!fat_lfncache) return; + + if(!fat_lfncache->Next) + { + tmp = fat_lfncache; + fat_lfncache = tmp->Next; + free(tmp); + return; + } + tmp = fat_lfncache; + while(tmp && tmp->Next) + { + if(tmp->Inode == node->Inode && tmp->Impl == node->ImplInt) + { + free(tmp->Next); + tmp->Next = tmp->Next->Next; + return; + } + tmp = tmp->Next; + } +} +#endif + +/** + \fn char *FAT_ReadDir(tVFS_Node *dirNode, int dirPos) + \param dirNode Node structure of directory + \param dirPos Directory position +**/ +char *FAT_ReadDir(tVFS_Node *dirNode, int dirpos) +{ + fat_filetable fileinfo[16]; //Sizeof=32, 16 per sector + int a=0; + tFAT_VolInfo *disk = &gFAT_Disks[dirNode->ImplInt&7]; + Uint32 cluster, offset; + int preSkip; + char *ret; + #if USE_LFN + char *lfn = NULL; + #endif + + ENTER("pDirNode iDirPos", dirNode, dirpos); + + // Get Byte Offset and skip + offset = dirpos * sizeof(fat_filetable); + preSkip = (offset >> 9) / disk->bootsect.spc; // >>9 == /512 + cluster = dirNode->Inode; // Cluster ID + + // Do Cluster Skip + // - Pre FAT32 had a reserved area for the root. + if( !(disk->type != FAT32 && cluster == disk->rootOffset) ) + { + //Skip previous clusters + for(a=preSkip;a--;) { + cluster = FAT_int_GetFatValue(dirNode->ImplInt, cluster); + } + } + + // Check for end of cluster chain + if((disk->type == FAT12 && cluster == EOC_FAT12) + || (disk->type == FAT16 && cluster == EOC_FAT16) + || (disk->type == FAT32 && cluster == EOC_FAT32)) + return NULL; + + // Bounds Checking (Used to spot heap overflows) + if(cluster > disk->clusterCount + 2) + { + Warning("FAT_ReadDir - Cluster ID is over cluster count (0x%x>0x%x)", + cluster, disk->clusterCount+2); + LEAVE('n'); + return NULL; + } + + LOG("cluster=0x%x, dirpos=%i\n", cluster, dirpos); + + // Compute Offsets + // - Pre FAT32 cluster base (in sectors) + if( cluster == disk->rootOffset && disk->type != FAT32 ) + offset = disk->bootsect.resvSectCount + cluster*disk->bootsect.spc; + else + { // FAT32 cluster base (in sectors) + offset = disk->firstDataSect; + offset += (cluster - 2) * disk->bootsect.spc; + } + // Sector in cluster + if(disk->bootsect.spc == 1) + offset += (dirpos / 16); + else + offset += (dirpos / 16) % disk->bootsect.spc; + // Offset in sector + a = dirpos & 0xF; + + LOG("offset=%i, a=%i\n", (Uint)offset, a); + + // Read Sector + VFS_ReadAt(disk->fileHandle, offset*512, 512, fileinfo); // Read Dir Data + + LOG("name[0] = 0x%x\n", (Uint8)fileinfo[a].name[0]); + //Check if this is the last entry + if(fileinfo[a].name[0] == '\0') { + dirNode->Size = dirpos; + LOG("End of list\n"); + LEAVE('n'); + return NULL; // break + } + + // Check for empty entry + if((Uint8)fileinfo[a].name[0] == 0xE5) { + LOG("Empty Entry\n"); + LEAVE('p', VFS_SKIP); + return VFS_SKIP; // Skip + } + + #if USE_LFN + // Get Long File Name Cache + lfn = FAT_int_GetLFN(dirNode); + if(fileinfo[a].attrib == ATTR_LFN) + { + fat_longfilename *lfnInfo; + int len; + + lfnInfo = (fat_longfilename *) &fileinfo[a]; + if(lfnInfo->id & 0x40) memset(lfn, 0, 256); + // Get the current length + len = strlen(lfn); + + // Sanity Check (FAT implementations do not allow >255 bytes) + if(len + 13 > 255) return VFS_SKIP; + // Rebase all bytes + for(a=len+1;a--;) lfn[a+13] = lfn[a]; + + // Append new bytes + lfn[ 0] = lfnInfo->name1[0]; lfn[ 1] = lfnInfo->name1[1]; + lfn[ 2] = lfnInfo->name1[2]; lfn[ 3] = lfnInfo->name1[3]; + lfn[ 4] = lfnInfo->name1[4]; + lfn[ 5] = lfnInfo->name2[0]; lfn[ 6] = lfnInfo->name2[1]; + lfn[ 7] = lfnInfo->name2[2]; lfn[ 8] = lfnInfo->name2[3]; + lfn[ 9] = lfnInfo->name2[4]; lfn[10] = lfnInfo->name2[5]; + lfn[11] = lfnInfo->name3[0]; lfn[12] = lfnInfo->name3[1]; + LEAVE('p', VFS_SKIP); + return VFS_SKIP; + } + #endif + + //Check if it is a volume entry + if(fileinfo[a].attrib & 0x08) { + LEAVE('p', VFS_SKIP); + return VFS_SKIP; + } + // Ignore . and .. + if(fileinfo[a].name[0] == '.') { + LEAVE('p', VFS_SKIP); + return VFS_SKIP; + } + + LOG("name='%c%c%c%c%c%c%c%c.%c%c%c'\n", + fileinfo[a].name[0], fileinfo[a].name[1], fileinfo[a].name[2], fileinfo[a].name[3], + fileinfo[a].name[4], fileinfo[a].name[5], fileinfo[a].name[6], fileinfo[a].name[7], + fileinfo[a].name[8], fileinfo[a].name[9], fileinfo[a].name[10] ); + + #if USE_LFN + //node = FAT_int_CreateNode(dirNode, &fileinfo[a], lfn); + ret = FAT_int_CreateName(dirNode, &fileinfo[a], lfn); + lfn[0] = '\0'; + #else + //node = FAT_int_CreateNode(dirNode, &fileinfo[a], NULL); + ret = FAT_int_CreateName(dirNode, &fileinfo[a], NULL); + #endif + + LEAVE('s', ret); + return ret; +} + +/** + * \fn tVFS_Node *FAT_FindDir(tVFS_Node *node, char *name) + * \brief Finds an entry in the current directory + */ +tVFS_Node *FAT_FindDir(tVFS_Node *node, char *name) +{ + fat_filetable fileinfo[16]; + char tmpName[11]; + #if USE_LFN + fat_longfilename *lfnInfo; + char *lfn = NULL; + int lfnPos=255, lfnId = -1; + #endif + int i=0; + tVFS_Node *tmpNode; + Uint64 diskOffset; + tFAT_VolInfo *disk = &gFAT_Disks[node->ImplInt]; + Uint32 dirCluster; + Uint32 cluster; + + ENTER("pnode sname", node, name); + + // Fast Returns + if(!name) return NULL; + if(name[0] == '\0') return NULL; + + #if USE_LFN + lfn = FAT_int_GetLFN(node); + #endif + + dirCluster = node->Inode; + // Seek to Directory + if( dirCluster == disk->rootOffset && disk->type != FAT32 ) + diskOffset = (disk->bootsect.resvSectCount+dirCluster*disk->bootsect.spc) << 9; + else + diskOffset = (disk->firstDataSect+(dirCluster-2)*disk->bootsect.spc) << 9; + + for(;;i++) + { + // Load sector + if((i & 0xF) == 0) { + VFS_ReadAt(disk->fileHandle, diskOffset, 512, fileinfo); + diskOffset += 512; + } + + //Check if the files are free + if(fileinfo[i&0xF].name[0] == '\0') break; //Free and last + if(fileinfo[i&0xF].name[0] == '\xE5') goto loadCluster; //Free + + + #if USE_LFN + // Long File Name Entry + if(fileinfo[i&0xF].attrib == ATTR_LFN) + { + lfnInfo = (fat_longfilename *) &fileinfo[i&0xF]; + if(lfnInfo->id & 0x40) { + memset(lfn, 0, 256); + lfnPos = 255; + } + lfn[lfnPos--] = lfnInfo->name3[1]; lfn[lfnPos--] = lfnInfo->name3[0]; + lfn[lfnPos--] = lfnInfo->name2[5]; lfn[lfnPos--] = lfnInfo->name2[4]; + lfn[lfnPos--] = lfnInfo->name2[3]; lfn[lfnPos--] = lfnInfo->name2[2]; + lfn[lfnPos--] = lfnInfo->name2[1]; lfn[lfnPos--] = lfnInfo->name2[0]; + lfn[lfnPos--] = lfnInfo->name1[4]; lfn[lfnPos--] = lfnInfo->name1[3]; + lfn[lfnPos--] = lfnInfo->name1[2]; lfn[lfnPos--] = lfnInfo->name1[1]; + lfn[lfnPos--] = lfnInfo->name1[0]; + if((lfnInfo->id&0x3F) == 1) + { + memcpy(lfn, lfn+lfnPos+1, 256-lfnPos); + lfnId = i+1; + } + } + else + { + // Remove LFN if it does not apply + if(lfnId != i) lfn[0] = '\0'; + #endif + // Get Real Filename + FAT_int_ProperFilename(tmpName, fileinfo[i&0xF].name); + + LOG("tmpName = '%s'\n", tmpName); + + //Only Long name is case sensitive, 8.3 is not + #if USE_LFN + if(strucmp(tmpName, name) == 0 || strcmp(lfn, name) == 0) { + #else + if(strucmp(tmpName, name) == 0) { + #endif + cluster = fileinfo[i&0xF].cluster | (fileinfo[i&0xF].clusterHi << 16); + tmpNode = Inode_GetCache(disk->inodeHandle, cluster); + if(tmpNode == NULL) // Node is not cached + { + #if USE_LFN + tmpNode = FAT_int_CreateNode(node, &fileinfo[i&0xF], lfn); + #else + tmpNode = FAT_int_CreateNode(node, &fileinfo[i&0xF], NULL); + #endif + } + #if USE_LFN + lfn[0] = '\0'; + #endif + LEAVE('p', tmpNode); + return tmpNode; + } + #if USE_LFN + } + #endif + + loadCluster: + //Load Next cluster? + if( ((i+1) >> 4) % disk->bootsect.spc == 0 && ((i+1) & 0xF) == 0) + { + if( dirCluster == disk->rootOffset && disk->type != FAT32 ) + continue; + dirCluster = FAT_int_GetFatValue(node->ImplInt, dirCluster); + diskOffset = (disk->firstDataSect+(dirCluster-2)*disk->bootsect.spc)*512; + } + } + + LEAVE('n'); + return NULL; +} + +/** + * \fn int FAT_Mknod(tVFS_Node *Node, char *Name, Uint Flags) + * \brief Create a new node + */ +int FAT_Mknod(tVFS_Node *Node, char *Name, Uint Flags) +{ + return 0; +} + +/** + * \fn int FAT_Relink(tVFS_Node *Node, char *OldName, char *NewName) + * \brief Rename / Delete a file + */ +int FAT_Relink(tVFS_Node *Node, char *OldName, char *NewName) +{ + return 0; +} + +/** + * \fn void FAT_CloseFile(tVFS_Node *Node) + * \brief Close an open file + */ +void FAT_CloseFile(tVFS_Node *Node) +{ + if(Node == NULL) return ; + + Inode_UncacheNode(gFAT_Disks[Node->ImplInt].inodeHandle, Node->Inode); + #if USE_LFN + if( !Inode_GetCache(gFAT_Disks[Node->ImplInt].inodeHandle, Node->Inode) + && Node->Flags & VFS_FFLAG_DIRECTORY) + FAT_int_DelLFN(Node); + else // Get Cache references the node, so dereference it + Inode_UncacheNode(gFAT_Disks[Node->ImplInt].inodeHandle, Node->Inode); + #endif + return ; +} + +/** + * \fn void fat_install() + * \brief Add the FAT Filesystem to the VFS + */ +void fat_install() +{ + VFS_AddDriver( &gFAT_FSInfo ); +} diff --git a/Kernel/vfs/fs/fs_fat.h b/Kernel/vfs/fs/fs_fat.h new file mode 100644 index 00000000..67feb61c --- /dev/null +++ b/Kernel/vfs/fs/fs_fat.h @@ -0,0 +1,135 @@ +/* + * Acess2 + * FAT12/16/32 Driver + * vfs/fs/fs_fat.h + */ + +// === On Disk Structures === +/** + \struct fat_bootsect_s + \brief Bootsector format +*/ +struct fat_bootsect_s { + Uint8 jmp[3]; //!< Jump Instruction + char oemname[8]; //!< OEM Name. Typically MSDOS1.1 + Uint16 bps; //!< Bytes per Sector. Assumed to be 512 + Uint8 spc; //!< Sectors per Cluster + Uint16 resvSectCount; //!< Number of reserved sectors at beginning of volume + Uint8 fatCount; //!< Number of copies of the FAT + Uint16 files_in_root; //!< Count of files in the root directory + Uint16 totalSect16; //!< Total sector count (FAT12/16) + Uint8 mediaDesc; //!< Media Desctiptor + Uint16 fatSz16; //!< FAT Size (FAT12/16) + Uint16 spt; //!< Sectors per track. Ignored (Acess uses LBA) + Uint16 heads; //!< Heads. Ignored (Acess uses LBA) + Uint32 hiddenCount; //!< ??? + Uint32 totalSect32; //!< Total sector count (FAT32) + union { + struct { + Uint8 drvNum; //!< Drive Number. BIOS Drive ID (E.g. 0x80) + Uint8 resv; //!< Reserved byte + Uint8 bootSig; //!< Boot Signature. ??? + Uint32 volId; //!< Volume ID + char label[11]; //!< Disk Label + char fsType[8]; //!< FS Type. ??? + } __attribute__((packed)) fat16; //!< FAT16 Specific information + struct { + Uint32 fatSz32; //!< 32-Bit FAT Size + Uint16 extFlags; //!< Extended flags + Uint16 fsVer; //!< Filesystem Version + Uint32 rootClust; //!< Root Cluster ID + Uint16 fsInfo; //!< FS Info. ??? + Uint16 backupBS; //!< Backup Bootsector Sector Offset + char resv[12]; //!< Reserved Data + Uint8 drvNum; //!< Drive Number + char resv2; //!< Reserved Data + Uint8 bootSig; //!< Boot Signature. ??? + Uint32 volId; //!< Volume ID + char label[11]; //!< Disk Label + char fsType[8]; //!< Filesystem Type. ??? + } __attribute__((packed)) fat32; //!< FAT32 Specific Information + }__attribute__((packed)) spec; //!< Non Shared Data + char pad[512-90]; //!< Bootsector Data (Code/Boot Signature 0xAA55) +} __attribute__((packed)); + +/** + \struct fat_filetable_s + \brief Format of a 8.3 file entry on disk +*/ +struct fat_filetable_s { + char name[11]; //!< 8.3 Name + //char ext[3]; //!< Extention + Uint8 attrib; //!< File Attributes. + Uint8 ntres; //!< Reserved for NT - Set to 0 + Uint8 ctimems; //!< 10ths of a second ranging from 0-199 (2 seconds) + Uint16 ctime; //!< Creation Time + Uint16 cdate; //!< Creation Date + Uint16 adate; //!< Accessed Data. No Time feild though + Uint16 clusterHi; //!< High Cluster. 0 for FAT12 and FAT16 + Uint16 mtime; //!< Last Modified Time + Uint16 mdate; //!< Last Modified Date + Uint16 cluster; //!< Low Word of First cluster + Uint32 size; //!< Size of file +} __attribute__((packed)); + +/** + \struct fat_longfilename_s + \brief Format of a long file name entry on disk +*/ +struct fat_longfilename_s { + Uint8 id; //!< ID of entry. Bit 6 is set for last entry + Uint16 name1[5]; //!< 5 characters of name + Uint8 attrib; //!< Attributes. Must be ATTR_LFN + Uint8 type; //!< Type. ??? + Uint8 checksum; //!< Checksum + Uint16 name2[6]; //!< 6 characters of name + Uint16 firstCluster; //!< Used for non LFN compatability. Set to 0 + Uint16 name3[2]; //!< Last 2 characters of name +} __attribute__((packed)); + +#define ATTR_READONLY 0x01 +#define ATTR_HIDDEN 0x02 +#define ATTR_SYSTEM 0x04 +#define ATTR_VOLUMEID 0x08 +#define ATTR_DIRECTORY 0x10 +#define ATTR_ARCHIVE 0x20 +#define ATTR_LFN (ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUMEID) + +/** + \enum eFatType + \brief Internal Ids for FAT types +*/ +enum eFatType { +// FAT_NULL, //!< NULL Entry + FAT12, //!< FAT12 Volume + FAT16, //!< FAT16 Volume + FAT32, //!< FAT32 Volume +// FAT_LAST //!< LAST Entry. Unused +}; + +#define EOC_FAT12 0x0FFF +#define EOC_FAT16 0xFFFF +#define EOC_FAT32 0x0FFFFFF + +typedef struct fat_bootsect_s fat_bootsect; +typedef struct fat_filetable_s fat_filetable; +typedef struct fat_longfilename_s fat_longfilename; + +// === Memory Structures === +/** + \struct drv_fat_volinfo_s + \brief Representation of a volume in memory +*/ +struct drv_fat_volinfo_s { + int fileHandle; //!< File Handle + int type; //!< FAT Type. See eFatType + char name[12]; //!< Volume Name (With NULL Terminator) + Uint32 firstDataSect; //!< First data sector + Uint32 rootOffset; //!< Root Offset (clusters) + Uint32 clusterCount; //!< Total Cluster Count + fat_bootsect bootsect; //!< Boot Sector + tVFS_Node rootNode; //!< Root Node + int inodeHandle; //!< Inode Cache Handle +}; + +typedef struct drv_fat_volinfo_s tFAT_VolInfo; diff --git a/Kernel/vfs/fs/root.c b/Kernel/vfs/fs/root.c new file mode 100644 index 00000000..95e308ae --- /dev/null +++ b/Kernel/vfs/fs/root.c @@ -0,0 +1,208 @@ +/* + * AcessMicro VFS + * - Root Filesystem Driver + */ +#include +#include + +// === CONSTANTS === +#define MAX_FILES 64 + +// === PROTOTYPES === +tVFS_Node *Root_InitDevice(char *Device, char *Options); + int Root_MkNod(tVFS_Node *Node, char *Name, Uint Flags); +tVFS_Node *Root_FindDir(tVFS_Node *Node, char *Name); +char *Root_ReadDir(tVFS_Node *Node, int Pos); +Uint64 Root_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); +Uint64 Root_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); +tRamFS_File *Root_int_AllocFile(); + +// === GLOBALS === +tVFS_Driver gRootFS_Info = { + "rootfs", 0, Root_InitDevice, + NULL +}; +tRamFS_File RootFS_Files[MAX_FILES]; +tVFS_ACL RootFS_ACLs[3] = { + {{0,0}, {0,VFS_PERM_ALL}}, // Owner (Root) + {{1,0}, {0,VFS_PERM_ALL}}, // Group (Root) + {{0,-1}, {0,VFS_PERM_ALL}} // World (Nobody) +}; + +// === CODE === +/** + * \fn tVFS_Node *Root_InitDevice(char *Device, char *Options) + * \brief Initialise the root filesystem + */ +tVFS_Node *Root_InitDevice(char *Device, char *Options) +{ + tRamFS_File *root; + if(strcmp(Device, "root") != 0) { + return NULL; + } + + // Create Root Node + root = &RootFS_Files[0]; + + root->Node.ImplPtr = root; + + root->Node.CTime + = root->Node.MTime + = root->Node.ATime = now(); + root->Node.NumACLs = 3; + root->Node.ACLs = RootFS_ACLs; + + //root->Node.Close = Root_CloseFile; // Not Needed (It's a RAM Disk!) + //root->Node.Relink = Root_RelinkRoot; // Not Needed (Why relink the root of the tree) + root->Node.FindDir = Root_FindDir; + root->Node.ReadDir = Root_ReadDir; + root->Node.MkNod = Root_MkNod; + + return &root->Node; +} + +/** + * \fn int Root_MkNod(tVFS_Node *Node, char *Name, Uint Flags) + * \brief Create an entry in the root directory + */ +int Root_MkNod(tVFS_Node *Node, char *Name, Uint Flags) +{ + tRamFS_File *parent = Node->ImplPtr; + tRamFS_File *child = parent->Data.FirstChild; + tRamFS_File *prev = (tRamFS_File *) &parent->Data.FirstChild; + + Log("Root_MkNod: (Node=%p, Name='%s', Flags=0x%x)", Node, Name, Flags); + + // Find last child, while we're at it, check for duplication + for( ; child; prev = child, child = child->Next ) + { + if(strcmp(child->Name, Name) == 0) return 0; + } + + child = Root_int_AllocFile(); + memset(child, 0, sizeof(tRamFS_File)); + + child->Name = malloc(strlen(Name)+1); + strcpy(child->Name, Name); + + child->Parent = parent; + child->Next = NULL; + child->Data.FirstChild = NULL; + + child->Node.ImplPtr = child; + child->Node.Flags = Flags; + child->Node.NumACLs = 0; + child->Node.Size = 0; + + if(Flags & VFS_FFLAG_DIRECTORY) + { + child->Node.ReadDir = Root_ReadDir; + child->Node.FindDir = Root_FindDir; + child->Node.MkNod = Root_MkNod; + } else { + child->Node.Read = Root_Read; + child->Node.Write = Root_Write; + } + + prev->Next = child; + + return 1; +} + +/** + * \fn tVFS_Node *Root_FindDir(tVFS_Node *Node, char *Name) + * \brief Find an entry in the filesystem + */ +tVFS_Node *Root_FindDir(tVFS_Node *Node, char *Name) +{ + tRamFS_File *parent = Node->ImplPtr; + tRamFS_File *child = parent->Data.FirstChild; + + //Log("Root_FindDir: (Node=%p, Name='%s')", Node, Name); + + for(;child;child = child->Next) + { + //Log(" Root_FindDir: strcmp('%s', '%s')", child->Node.Name, Name); + if(strcmp(child->Name, Name) == 0) return &child->Node; + } + + return NULL; +} + +/** + * \fn char *Root_ReadDir(tVFS_Node *Node, int Pos) + * \brief Get an entry from the filesystem + */ +char *Root_ReadDir(tVFS_Node *Node, int Pos) +{ + tRamFS_File *parent = Node->ImplPtr; + tRamFS_File *child = parent->Data.FirstChild; + + for( ; child && Pos--; child = child->Next ) ; + + if(Pos) return child->Name; + + return child->Name; +} + +/** + * \fn Uint64 Root_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) + * \brief Read from a file in the root directory + */ +Uint64 Root_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + tRamFS_File *file = Node->ImplPtr; + + if(Offset > Node->Size) return 0; + if(Length > Node->Size) return 0; + + if(Offset+Length > Node->Size) + Length = Node->Size - Offset; + + memcpy(Buffer, file->Data.Bytes+Offset, Length); + + return Length; +} + +/** + * \fn Uint64 Root_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) + * \brief Write to a file in the root directory + */ +Uint64 Root_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + tRamFS_File *file = Node->ImplPtr; + + // Check if buffer needs to be expanded + if(Offset + Length > Node->Size) + { + void *tmp = realloc( file->Data.Bytes, Offset + Length ); + if(tmp == NULL) { + Warning("Root_Write - Increasing buffer size failed\n"); + return -1; + } + file->Data.Bytes = tmp; + Node->Size = Offset + Length; + Log(" Root_Write: Expanded buffer to %i bytes\n", Node->Size); + } + + memcpy(file->Data.Bytes+Offset, Buffer, Length); + + return Length; +} + +/** + * \fn tRamFS_File *Root_int_AllocFile() + * \brief Allocates a file from the pool + */ +tRamFS_File *Root_int_AllocFile() +{ + int i; + for( i = 0; i < MAX_FILES; i ++ ) + { + if( RootFS_Files[i].Name == NULL ) + { + return &RootFS_Files[i]; + } + } + return NULL; +} diff --git a/Kernel/vfs/io.c b/Kernel/vfs/io.c new file mode 100644 index 00000000..57d3f84a --- /dev/null +++ b/Kernel/vfs/io.c @@ -0,0 +1,168 @@ +/* + * AcessMicro VFS + * - File IO Passthru's + */ +#include +#include "vfs.h" +#include "vfs_int.h" + +#define DEBUG 0 + +#if DEBUG +#else +# undef ENTER +# undef LOG +# undef LEAVE +# define ENTER(...) +# define LOG(...) +# define LEAVE(...) +#endif + +// === CODE === +/** + * \fn Uint64 VFS_Read(int FD, Uint64 Length, void *Buffer) + * \brief Read data from a node (file) + */ +Uint64 VFS_Read(int FD, Uint64 Length, void *Buffer) +{ + tVFS_Handle *h; + Uint64 ret; + + ENTER("iFD XLength pBuffer", FD, Length, Buffer); + + h = VFS_GetHandle(FD); + if(!h) return -1; + + if( !(h->Mode & VFS_OPENFLAG_READ) || h->Node->Flags & VFS_FFLAG_DIRECTORY ) { + LEAVE('i', -1); + return -1; + } + + if(!h->Node->Read) { + LEAVE('i', 0); + return 0; + } + + ret = h->Node->Read(h->Node, h->Position, Length, Buffer); + if(ret == -1) { + LEAVE('i', -1); + return -1; + } + + h->Position += ret; + LEAVE('X', ret); + return ret; +} + +/** + * \fn Uint64 VFS_ReadAt(int FD, Uint64 Offset, Uint64 Length, void *Buffer) + * \brief Read data from a given offset (atomic) + */ +Uint64 VFS_ReadAt(int FD, Uint64 Offset, Uint64 Length, void *Buffer) +{ + tVFS_Handle *h; + Uint64 ret; + + h = VFS_GetHandle(FD); + if(!h) return -1; + + if( !(h->Mode & VFS_OPENFLAG_READ) ) return -1; + if( h->Node->Flags & VFS_FFLAG_DIRECTORY ) return -1; + + if(!h->Node->Read) { + Warning("VFS_ReadAt - Node %p, does not have a read method", h->Node); + return 0; + } + ret = h->Node->Read(h->Node, Offset, Length, Buffer); + if(ret == -1) return -1; + return ret; +} + +/** + * \fn Uint64 VFS_Write(int FD, Uint64 Length, void *Buffer) + * \brief Read data from a node (file) + */ +Uint64 VFS_Write(int FD, Uint64 Length, void *Buffer) +{ + tVFS_Handle *h; + Uint64 ret; + + h = VFS_GetHandle(FD); + if(!h) return -1; + + if( !(h->Mode & VFS_OPENFLAG_WRITE) ) return -1; + if( h->Node->Flags & VFS_FFLAG_DIRECTORY ) return -1; + + if(!h->Node->Write) return 0; + + ret = h->Node->Write(h->Node, h->Position, Length, Buffer); + if(ret == -1) return -1; + h->Position += ret; + return ret; +} + +/** + * \fn Uint64 VFS_WriteAt(int FD, Uint64 Offset, Uint64 Length, void *Buffer) + * \brief Write data to a file at a given offset (atomic) + */ +Uint64 VFS_WriteAt(int FD, Uint64 Offset, Uint64 Length, void *Buffer) +{ + tVFS_Handle *h; + Uint64 ret; + + h = VFS_GetHandle(FD); + if(!h) return -1; + + if( !(h->Mode & VFS_OPENFLAG_WRITE) ) return -1; + if( h->Node->Flags & VFS_FFLAG_DIRECTORY ) return -1; + + if(!h->Node->Write) return 0; + ret = h->Node->Write(h->Node, Offset, Length, Buffer); + if(ret == -1) return -1; + return ret; +} + +/** + * \fn Uint64 VFS_Tell(int FD) + * \brief Returns the current file position + */ +Uint64 VFS_Tell(int FD) +{ + tVFS_Handle *h; + + h = VFS_GetHandle(FD); + if(!h) return -1; + + return h->Position; +} + +/** + * \fn int VFS_Seek(int FD, Sint64 Distance, int Whence) + * \brief Seek to a new location + * \param FD File descriptor + * \param Distance Where to go + * \param Whence From where + */ +int VFS_Seek(int FD, Sint64 Distance, int Whence) +{ + tVFS_Handle *h; + + h = VFS_GetHandle(FD); + if(!h) return -1; + + // Set relative to current position + if(Whence == 0) { + h->Position += Distance; + return 0; + } + + // Set relative to end of file + if(Whence < 0) { + h->Position = h->Node->Size - Distance; + return 0; + } + + // Set relative to start of file + h->Position = Distance; + return 0; +} diff --git a/Kernel/vfs/main.c b/Kernel/vfs/main.c new file mode 100644 index 00000000..bfa1fc1c --- /dev/null +++ b/Kernel/vfs/main.c @@ -0,0 +1,100 @@ +/* + * Acess 2 + * Virtual File System + */ +#include +#include "vfs.h" +#include "vfs_int.h" +#include "vfs_ext.h" + +// === IMPORTS === +extern tVFS_Driver gRootFS_Info; +extern tVFS_Driver gDevFS_Info; + +// === GLOBALS === +tVFS_Node NULLNode = {0}; +static int siDriverListLock = 0; +tVFS_Driver *gVFS_Drivers = NULL; + +// === CODE === +/** + * \fn int VFS_Init() + * \brief Initialises the VFS for use by the kernel and user + */ +int VFS_Init() +{ + // Core Drivers + gVFS_Drivers = &gRootFS_Info; + gVFS_Drivers->Next = &gDevFS_Info; + + VFS_Mount("root", "/", "rootfs", ""); + VFS_MkDir("/Devices"); + VFS_MkDir("/Mount"); + VFS_Mount("dev", "/Devices", "devfs", ""); + + CFGINT(CFG_VFS_MAXFILES) = 32; + return 0; +} + +/** + * \fn char *VFS_GetTruePath(char *Path) + * \brief Gets the true path (non-symlink) of a file + */ +char *VFS_GetTruePath(char *Path) +{ + tVFS_Node *node; + char *ret; + + node = VFS_ParsePath(Path, &ret); + + if(!node) return NULL; + if(node->Close) node->Close(node); + + return ret; +} + +/** + * \fn void VFS_GetMemPath(void *Base, Uint Length, char *Dest) + * \brief Create a VFS memory pointer path + */ +void VFS_GetMemPath(void *Base, Uint Length, char *Dest) +{ + Log("VFS_GetMemPath: (Base=%p, Length=0x%x, Dest=%p)", Base, Length, Dest); + Dest[0] = '$'; + itoa( &Dest[1], (Uint)Base, 16, BITS/4, '0' ); + Dest[BITS/4+1] = ':'; + itoa( &Dest[BITS/4+2], Length, 16, BITS/4, '0' ); + + Log("VFS_GetMemPath: Dest = \"%s\"", Dest); +} + +/** + * \fn tVFS_Driver *VFS_GetFSByName(char *Name) + * \brief Gets a filesystem structure given a name + */ +tVFS_Driver *VFS_GetFSByName(char *Name) +{ + tVFS_Driver *drv = gVFS_Drivers; + + for(;drv;drv=drv->Next) + { + if(strcmp(drv->Name, Name) == 0) + return drv; + } + return NULL; +} + +/** + * \fn int VFS_AddDriver(tVFS_Driver *Info) + */ +int VFS_AddDriver(tVFS_Driver *Info) +{ + if(!Info) return -1; + + LOCK( &siDriverListLock ); + Info->Next = gVFS_Drivers; + gVFS_Drivers = Info; + RELEASE( &siDriverListLock ); + + return 0; +} diff --git a/Kernel/vfs/memfile.c b/Kernel/vfs/memfile.c new file mode 100644 index 00000000..2fc38e76 --- /dev/null +++ b/Kernel/vfs/memfile.c @@ -0,0 +1,137 @@ +/* + */ +#include +#include + +// === PROTOTYPES === +tVFS_Node *VFS_MemFile_Create(tVFS_Node *Unused, char *Path); +void VFS_MemFile_Close(tVFS_Node *Node); +Uint64 VFS_MemFile_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); +Uint64 VFS_MemFile_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); + +// === GLOBALS === +tVFS_Node gVFS_MemRoot = { + .Flags = VFS_FFLAG_DIRECTORY, + .NumACLs = 0, + .FindDir = VFS_MemFile_Create + }; + +// === CODE === +/** + * \fn tVFS_Node *VFS_MemFile_Create(tVFS_Node *Unused, char *Path) + * \note Treated as finddir by VFS_ParsePath + */ +tVFS_Node *VFS_MemFile_Create(tVFS_Node *Unused, char *Path) +{ + Uint base, size; + char *str = Path; + tVFS_Node *ret; + + str++; // Eat '$' + + // Read Base address + base = 0; + for( ; ('0' <= *str && *str <= '9') || ('A' <= *str && *str <= 'F'); str++ ) + { + base *= 16; + if('A' <= *str && *str <= 'F') + base += *str - 'A' + 10; + else + base += *str - '0'; + } + + // Check separator + if(*str++ != ':') return NULL; + + // Read buffer size + size = 0; + for( ; ('0' <= *str && *str <= '9') || ('A' <= *str && *str <= 'F'); str++ ) + { + size *= 16; + if('A' <= *str && *str <= 'F') + size += *str - 'A' + 10; + else + size += *str - '0'; + } + + // Check for NULL byte + if(*str != '\0') return NULL; + + Log(" VFS_MemFile_Create: base=0x%x, size=0x%x", base, size); + + // Allocate and fill node + ret = malloc(sizeof(tVFS_Node)); + memset(ret, 0, sizeof(tVFS_Node)); + + // State + ret->ImplPtr = (void*)base; + ret->Size = size; + + // ACLs + ret->NumACLs = 1; + ret->ACLs = &gVFS_ACL_EveryoneRWX; + + // Functions + ret->Close = VFS_MemFile_Close; + ret->Read = VFS_MemFile_Read; + ret->Write = VFS_MemFile_Write; + + return ret; +} + +/** + * \fn void VFS_MemFile_Close(tVFS_Node *Node) + * \brief Dereference and clean up a memory file + */ +void VFS_MemFile_Close(tVFS_Node *Node) +{ + Node->ReferenceCount --; + if( Node->ReferenceCount == 0 ) { + Node->ImplPtr = NULL; + free(Node); + } +} + +/** + * \fn Uint64 VFS_MemFile_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) + * \brief Read from a memory file + */ +Uint64 VFS_MemFile_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + // Check for use of free'd file + if(Node->ImplPtr == NULL) return 0; + + // Check for out of bounds read + if(Offset > Node->Size) return 0; + + // Truncate data read if needed + if(Offset + Length > Node->Size) + Length = Node->Size - Offset; + + // Copy Data + memcpy(Buffer, Node->ImplPtr+Offset, Length); + + return Length; +} + +/** + * \fn Uint64 VFS_MemFile_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) + * \brief Write to a memory file + */ +Uint64 VFS_MemFile_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + // Check for use of free'd file + if(Node->ImplPtr == NULL) return 0; + + // Check for out of bounds read + if(Offset > Node->Size) return 0; + + // Truncate data read if needed + if(Offset + Length > Node->Size) + Length = Node->Size - Offset; + + // Copy Data + memcpy(Node->ImplPtr+Offset, Buffer, Length); + + return Length; +} diff --git a/Kernel/vfs/mount.c b/Kernel/vfs/mount.c new file mode 100644 index 00000000..f49c7212 --- /dev/null +++ b/Kernel/vfs/mount.c @@ -0,0 +1,81 @@ +/* + * Acess Micro - VFS Server version 1 + */ +#include +#include +#include + +// === GLOBALS === + int glVFS_MountList = 0; +tVFS_Mount *gMounts; +tVFS_Mount *gRootMount = NULL; + +// === CODE === +/** + * \fn int VFS_Mount(char *Device, char *MountPoint, char *Filesystem, char *ArgString) + * \brief Mount a device + * \param Device Device string to mount + * \param MountPoint Destination for the mount + * \param Filesystem Filesystem to use for the mount + * \param ArgString Options to be passed to the filesystem + * \return -1 on Invalid FS, -2 on No Mem, 0 on success + */ +int VFS_Mount(char *Device, char *MountPoint, char *Filesystem, char *ArgString) +{ + tVFS_Mount *mnt; + tVFS_Driver *fs; + int deviceLen = strlen(Device); + int mountLen = strlen(MountPoint); + int argLen = strlen(ArgString); + + // Get the filesystem + fs = VFS_GetFSByName(Filesystem); + if(!fs) { + Warning("VFS_Mount - Unknown FS Type '%s'", Filesystem); + return -1; + } + + // Create mount information + mnt = malloc( sizeof(tVFS_Mount)+deviceLen+1+mountLen+1+argLen+1 ); + if(!mnt) { + return -2; + } + + // HACK: Forces VFS_ParsePath to fall back on root + if(mountLen == 1 && MountPoint[0] == '/') + mnt->MountPointLen = 0; + else + mnt->MountPointLen = mountLen; + + // Fill Structure + mnt->Filesystem = fs; + + mnt->Device = &mnt->StrData[0]; + memcpy( mnt->Device, Device, deviceLen+1 ); + + mnt->MountPoint = &mnt->StrData[deviceLen+1]; + memcpy( mnt->MountPoint, MountPoint, mountLen+1 ); + + mnt->Options = &mnt->StrData[deviceLen+1+mountLen+1]; + memcpy( mnt->Options, ArgString, argLen+1 ); + + // Initialise Volume + mnt->RootNode = fs->InitDevice(Device, ArgString); + if(!mnt->RootNode) { + free(mnt); + return -2; + } + + // Set root + if(!gRootMount) gRootMount = mnt; + + // Add to mount list + LOCK( &glVFS_MountList ); + mnt->Next = gMounts; + gMounts = mnt; + RELEASE( &glVFS_MountList ); + + Log("VFS_Mount: Mounted '%s' to '%s' ('%s')", Device, MountPoint, Filesystem); + + return 0; +} diff --git a/Kernel/vfs/nodecache.c b/Kernel/vfs/nodecache.c new file mode 100644 index 00000000..983f3f77 --- /dev/null +++ b/Kernel/vfs/nodecache.c @@ -0,0 +1,209 @@ +/* + * AcessMicro VFS + * - File IO Passthru's + */ +#include +#include "vfs.h" +#include "vfs_int.h" + +// === TYPES === +typedef struct sCachedInode { + struct sCachedInode *Next; + tVFS_Node Node; +} tCachedInode; +typedef struct sInodeCache { + struct sInodeCache *Next; + int Handle; + tCachedInode *FirstNode; // Sorted List + Uint64 MaxCached; // Speeds up Searching +} tInodeCache; + +// === PROTOTYPES === +tInodeCache *Inode_int_GetFSCache(int Handle); + +// === GLOBALS === + int gVFS_NextInodeHandle = 1; + int gilVFS_InodeCache = 0; +tInodeCache *gVFS_InodeCache = NULL; + +// === CODE === +/** + * \fn int Inode_GetHandle() + */ +int Inode_GetHandle() +{ + tInodeCache *ent; + + ent = malloc( sizeof(tInodeCache) ); + ent->MaxCached = 0; + ent->Handle = gVFS_NextInodeHandle++; + ent->Next = NULL; ent->FirstNode = NULL; + + // Add to list + LOCK( &gilVFS_InodeCache ); + ent->Next = gVFS_InodeCache; + gVFS_InodeCache = ent; + RELEASE( &gilVFS_InodeCache ); + + return gVFS_NextInodeHandle-1; +} + +/** + * \fn tVFS_Node *Inode_GetCache(int Handle, Uint64 Inode) + * \brief Gets a node from the cache + */ +tVFS_Node *Inode_GetCache(int Handle, Uint64 Inode) +{ + tInodeCache *cache; + tCachedInode *ent; + + cache = Inode_int_GetFSCache(Handle); + if(!cache) return NULL; + + if(Inode > cache->MaxCached) return NULL; + + // Search Cache + ent = cache->FirstNode; + for( ; ent; ent = ent->Next ) + { + if(ent->Node.Inode < Inode) continue; + if(ent->Node.Inode > Inode) return NULL; + ent->Node.ReferenceCount ++; + return &ent->Node; + } + + return NULL; // Should never be reached +} + +/** + * \fn tVFS_Node *Inode_CacheNode(int Handle, tVFS_Node *Node) + */ +tVFS_Node *Inode_CacheNode(int Handle, tVFS_Node *Node) +{ + tInodeCache *cache; + tCachedInode *newEnt, *ent, *prev; + + cache = Inode_int_GetFSCache(Handle); + if(!cache) return NULL; + + if(Node->Inode > cache->MaxCached) + cache->MaxCached = Node->Inode; + + // Search Cache + ent = cache->FirstNode; + prev = (tCachedInode*) &cache->FirstNode; + for( ; ent; prev = ent, ent = ent->Next ) + { + if(ent->Node.Inode < Node->Inode) continue; + if(ent->Node.Inode == Node->Inode) { + ent->Node.ReferenceCount ++; + return &ent->Node; + } + break; + } + + // Create new entity + newEnt = malloc(sizeof(tCachedInode)); + newEnt->Next = ent; + memcpy(&newEnt->Node, Node, sizeof(tVFS_Node)); + prev->Next = newEnt; + + return &newEnt->Node; +} + +/** + * \fn void Inode_UncacheNode(int Handle, Uint64 Inode) + * \brief Dereferences/Removes a cached node + */ +void Inode_UncacheNode(int Handle, Uint64 Inode) +{ + tInodeCache *cache; + tCachedInode *ent, *prev; + + cache = Inode_int_GetFSCache(Handle); + if(!cache) return; + + if(Inode > cache->MaxCached) return; + + // Search Cache + ent = cache->FirstNode; + prev = (tCachedInode*) &cache->FirstNode; // Special case removal + for( ; ent; prev = ent, ent = ent->Next ) + { + if(ent->Node.Inode < Inode) continue; + if(ent->Node.Inode > Inode) return; + ent->Node.ReferenceCount --; + // Check if node needs to be freed + if(ent->Node.ReferenceCount == 0) + { + prev->Next = ent->Next; + if(ent->Node.Inode == cache->MaxCached) + { + if(ent != cache->FirstNode) + cache->MaxCached = prev->Node.Inode; + else + cache->MaxCached = 0; + } + + free(ent); + } + return; + } +} + +/** + * \fn void Inode_ClearCache(int Handle) + * \brief Removes a cache + */ +void Inode_ClearCache(int Handle) +{ + tInodeCache *cache; + tInodeCache *prev = (tInodeCache*) &gVFS_InodeCache; + tCachedInode *ent, *next; + + cache = Inode_int_GetFSCache(Handle); + if(!cache) return; + + // Search Cache + ent = cache->FirstNode; + while( ent ) + { + ent->Node.ReferenceCount = 1; + next = ent->Next; + + if(ent->Node.Close) + ent->Node.Close( &ent->Node ); + free(ent); + + ent = next; + } + + // Free Cache + prev->Next = cache->Next; + free(cache); +} + +/** + * \fn tInodeCache *Inode_int_GetFSCache(int Handle) + * \brief Gets a cache given it's handle + */ +tInodeCache *Inode_int_GetFSCache(int Handle) +{ + tInodeCache *cache = gVFS_InodeCache; + // Find Cache + for( ; cache; cache = cache->Next ) + { + if(cache->Handle > Handle) continue; + if(cache->Handle < Handle) { + Warning("Inode_int_GetFSCache - Handle %i not in cache\n", Handle); + return NULL; + } + break; + } + if(!cache) { + Warning("Inode_int_GetFSCache - Handle %i not in cache [NULL]\n", Handle); + return NULL; + } + + return cache; +} diff --git a/Kernel/vfs/open.c b/Kernel/vfs/open.c new file mode 100644 index 00000000..f025e0de --- /dev/null +++ b/Kernel/vfs/open.c @@ -0,0 +1,502 @@ +/* + * AcessMicro VFS + * - Open, Close and ChDir + */ +#include +#include "vfs.h" +#include "vfs_int.h" +#include "vfs_ext.h" + +#define DEBUG 0 + +#if DEBUG +#else +# undef ENTER +# undef LOG +# undef LEAVE +# define ENTER(...) +# define LOG(...) +# define LEAVE(...) +#endif + +// === CONSTANTS === +#define OPEN_MOUNT_ROOT 1 +#define MAX_KERNEL_FILES 128 + +// === IMPORTS === +extern tVFS_Node gVFS_MemRoot; +extern tVFS_Mount *gRootMount; + +// === GLOBALS === +tVFS_Handle *gaUserHandles = (void*)MM_PPD_VFS; +tVFS_Handle *gaKernelHandles = (void*)MM_KERNEL_VFS; + +// === CODE === +/** + * \fn char *VFS_GetAbsPath(char *Path) + * \brief Create an absolute path from a relative one + */ +char *VFS_GetAbsPath(char *Path) +{ + char *ret; + int pathLen = strlen(Path); + int read, write; + int pos, slashNum=0, baseLen; + Uint slashOffsets[256]; + char *cwd = CFGPTR(CFG_VFS_CWD); + int cwdLen; + + ENTER("sPath", Path); + + // Memory File + if(Path[0] == '$') { + ret = malloc(strlen(Path)+1); + strcpy(ret, Path); + LEAVE('p', ret); + return ret; + } + + // Check if the path is already absolute + if(Path[0] == '/') { + ret = malloc(pathLen + 1); + strcpy(ret, Path); + baseLen = 1; + } else { + cwdLen = strlen(cwd); + // Prepend the current directory + ret = malloc(cwdLen+pathLen+1); + strcpy(ret, cwd); + strcpy(&ret[cwdLen], Path); + + // Pre-fill the slash positions + pos = 0; + while( (pos = strpos( &ret[pos+1], '/' )) != -1 ) + slashOffsets[slashNum++] = pos; + + baseLen = cwdLen; + } + + // Remove . and .. + read = write = baseLen; // Cwd has already been parsed + for(; read < baseLen+pathLen; read = pos+1) + { + pos = strpos( &ret[read], '/' ); + // If we are in the last section, force a break at the end of the itteration + if(pos == -1) pos = baseLen+pathLen; + else pos += read; // Else, Adjust to absolute + + // Check Length + if(pos - read <= 2) + { + // Current Dir "." + if(strncmp(&ret[read], ".", pos-read) == 0) continue; + // Parent ".." + if(strncmp(&ret[read], "..", pos-read) == 0) + { + // If there is no higher, silently ignore + if(!slashNum) continue; + // Reverse write pointer + write = slashOffsets[ slashNum-- ]; + continue; + } + } + + + // Only copy if the positions differ + if(read != write) { + memcpy( &ret[write], &ret[read], pos-read+1 ); + } + write = pos+1; + if(slashNum < 256) + slashOffsets[ slashNum++ ] = pos; + else { + LOG("Path '%s' has too many elements", Path); + free(ret); + LEAVE('n'); + return NULL; + } + } + + // `ret` should now be the absolute path + LEAVE('s', ret); + return ret; +} + +/** + * \fn char *VFS_ParsePath(char *Path, char **TruePath) + * \brief Parses a path, resolving sysmlinks and applying permissions + */ +tVFS_Node *VFS_ParsePath(char *Path, char **TruePath) +{ + tVFS_Mount *mnt; + tVFS_Mount *longestMount = gRootMount; // Root is first + int cmp, retLength = 0; + int ofs, nextSlash; + tVFS_Node *curNode, *tmpNode; + char *tmp; + + ENTER("sPath pTruePath", Path, TruePath); + + // Memory File + if(Path[0] == '$') { + if(TruePath) { + *TruePath = malloc(strlen(Path)+1); + strcpy(*TruePath, Path); + } + curNode = gVFS_MemRoot.FindDir(&gVFS_MemRoot, Path); + LEAVE('p', curNode); + return curNode; + } + // For root we always fast return + + if(Path[0] == '/' && Path[1] == '\0') { + if(TruePath) { + *TruePath = malloc( gRootMount->MountPointLen+1 ); + strcpy(*TruePath, gRootMount->MountPoint); + } + LEAVE('p', gRootMount->RootNode); + return gRootMount->RootNode; + } + + // Check if there is anything mounted + if(!gMounts) return NULL; + + // Find Mountpoint + for(mnt = gMounts; + mnt; + mnt = mnt->Next) + { + // Quick Check + if( Path[mnt->MountPointLen] != '/' && Path[mnt->MountPointLen] != '\0') + continue; + // Length Check - If the length is smaller than the longest match sofar + if(mnt->MountPointLen < longestMount->MountPointLen) continue; + // String Compare + cmp = strcmp(Path, mnt->MountPoint); + + #if OPEN_MOUNT_ROOT + // Fast Break - Request Mount Root + if(cmp == 0) { + if(TruePath) { + *TruePath = malloc( mnt->MountPointLen+1 ); + strcpy(*TruePath, mnt->MountPoint); + } + LEAVE('p', mnt->RootNode); + return mnt->RootNode; + } + #endif + // Not a match, continue + if(cmp != '/') continue; + longestMount = mnt; + } + + // Sanity Check + /*if(!longestMount) { + Log("VFS_GetTruePath - ERROR: No Root Node\n"); + return NULL; + }*/ + + // Save to shorter variable + mnt = longestMount; + + LOG("mnt = {MountPoint:\"%s\"}", mnt->MountPoint); + + // Initialise String + if(TruePath) + { + *TruePath = malloc( mnt->MountPointLen+1 ); + strcpy(*TruePath, mnt->MountPoint); + retLength = mnt->MountPointLen; + } + + curNode = mnt->RootNode; + curNode->ReferenceCount ++; + // Parse Path + ofs = mnt->MountPointLen+1; + for(; (nextSlash = strpos(&Path[ofs], '/')) != -1; Path[nextSlash]='/',ofs = nextSlash + 1) + { + nextSlash += ofs; + Path[nextSlash] = '\0'; + + // Check for empty string + if( Path[ofs] == '\0' ) continue; + + // Check permissions on root of filesystem + if( !VFS_CheckACL(curNode, VFS_PERM_EXECUTE) ) { + curNode->Close( curNode ); + if(TruePath) free(*TruePath); + LEAVE('n'); + return NULL; + } + + // Check if the node has a FindDir method + if(!curNode->FindDir) { + if(curNode->Close) curNode->Close(curNode); + if(TruePath) free(*TruePath); + Path[nextSlash] = '/'; + LEAVE('n'); + return NULL; + } + LOG("FindDir(%p, '%s')", curNode, &Path[ofs]); + // Get Child Node + tmpNode = curNode->FindDir(curNode, &Path[ofs]); + LOG("tmpNode = %p", tmpNode); + if(curNode->Close) + curNode->Close(curNode); + curNode = tmpNode; + + // Error Check + if(!curNode) { + LOG("Node '%s' not found in dir '%s'", &Path[ofs], Path); + if(TruePath) + free(*TruePath); + Path[nextSlash] = '/'; + LEAVE('n'); + return NULL; + } + + // Handle Symbolic Links + if(curNode->Flags & VFS_FFLAG_SYMLINK) { + if(TruePath) + free(*TruePath); + tmp = malloc( curNode->Size + 1 ); + curNode->Read( curNode, 0, curNode->Size, tmp ); + tmp[ curNode->Size ] = '\0'; + + // Parse Symlink Path + curNode = VFS_ParsePath(tmp, TruePath); + free(tmp); // Free temp string + + // Error Check + if(!curNode) { + LEAVE('n'); + return NULL; + } + + // Set Path Variable + if(TruePath) { + *TruePath = tmp; + retLength = strlen(tmp); + } + + continue; + } + + // Handle Non-Directories + if( !(curNode->Flags & VFS_FFLAG_DIRECTORY) ) + { + Warning("VFS_ParsePath - File in directory context"); + if(TruePath) free(*TruePath); + LEAVE('n'); + return NULL; + } + + // Check if path needs extending + if(!TruePath) continue; + + // Increase buffer space + tmp = realloc( *TruePath, retLength + strlen(&Path[ofs]) + 1 + 1 ); + // Check if allocation succeeded + if(!tmp) { + Warning("VFS_ParsePath - Unable to reallocate true path buffer"); + free(*TruePath); + if(curNode->Close) curNode->Close(curNode); + LEAVE('n'); + return NULL; + } + *TruePath = tmp; + // Append to path + (*TruePath)[retLength] = '/'; + strcpy(*TruePath+retLength+1, &Path[ofs]); + // - Extend Path + retLength += strlen(&Path[ofs])+1; + } + + // Get last node + LOG("VFS_ParsePath: FindDir(%p, '%s')", curNode, &Path[ofs]); + tmpNode = curNode->FindDir(curNode, &Path[ofs]); + LOG("tmpNode = %p", tmpNode); + if(curNode->Close) curNode->Close(curNode); + // Check if file was found + if(!tmpNode) { + LOG("Node '%s' not found in dir '%s'", &Path[ofs], Path); + if(TruePath) free(*TruePath); + if(curNode->Close) curNode->Close(curNode); + LEAVE('n'); + return NULL; + } + + if(TruePath) + { + // Increase buffer space + tmp = realloc(*TruePath, retLength + strlen(&Path[ofs]) + 1 + 1); + // Check if allocation succeeded + if(!tmp) { + Warning("VFS_ParsePath - Unable to reallocate true path buffer"); + free(*TruePath); + if(tmpNode->Close) tmpNode->Close(curNode); + LEAVE('n'); + return NULL; + } + *TruePath = tmp; + // Append to path + (*TruePath)[retLength] = '/'; + strcpy(*TruePath + retLength + 1, &Path[ofs]); + // - Extend Path + //retLength += strlen(tmpNode->Name) + 1; + } + + LEAVE('p', tmpNode); + return tmpNode; +} + +/** + * \fn int VFS_Open(char *Path, Uint Mode) + * \brief Open a file + */ +int VFS_Open(char *Path, Uint Mode) +{ + tVFS_Node *node; + char *absPath; + int i; + + ENTER("sPath xMode", Path, Mode); + + // Get absolute path + absPath = VFS_GetAbsPath(Path); + LOG("absPath = \"%s\"", absPath); + // Parse path and get mount point + node = VFS_ParsePath(absPath, NULL); + // Free generated path + free(absPath); + + if(!node) { + LOG("Cannot find node"); + LEAVE('i', -1); + return -1; + } + + // Check for symlinks + if( !(Mode & VFS_OPENFLAG_NOLINK) && (node->Flags & VFS_FFLAG_SYMLINK) ) + { + if( !node->Read ) { + LOG("No read method on symlink"); + LEAVE('i', -1); + return -1; + } + absPath = malloc(node->Size+1); // Allocate Buffer + node->Read( node, 0, node->Size, absPath ); // Read Path + + absPath[ node->Size ] = '\0'; // End String + if(node->Close) node->Close( node ); // Close old node + node = VFS_ParsePath(absPath, NULL); // Get new node + free( absPath ); // Free allocated path + } + + if(!node) { + LEAVE('i', -1); + return -1; + } + + i = 0; + i |= (Mode & VFS_OPENFLAG_EXEC) ? VFS_PERM_EXECUTE : 0; + i |= (Mode & VFS_OPENFLAG_READ) ? VFS_PERM_READ : 0; + i |= (Mode & VFS_OPENFLAG_WRITE) ? VFS_PERM_WRITE : 0; + + LOG("i = 0b%b", i); + + // Permissions Check + if( !VFS_CheckACL(node, i) ) { + node->Close( node ); + LEAVE('i', -1); + return -1; + } + + // Check for a user open + if(Mode & VFS_OPENFLAG_USER) + { + // Allocate Buffer + if( MM_GetPhysAddr( (Uint)gaUserHandles ) == 0 ) + { + Uint addr, size; + size = CFGINT(CFG_VFS_MAXFILES) * sizeof(tVFS_Handle); + for(addr = 0; addr < size; addr += 0x1000) + MM_Allocate( (Uint)gaUserHandles + addr ); + memset( gaUserHandles, 0, size ); + } + // Get a handle + for(i=0;iNode->Close) + h->Node->Close( h->Node ); + + h->Node = NULL; +} + +/** + * \fn tVFS_Handle *VFS_GetHandle(int FD) + * \brief Gets a pointer to the handle information structure + */ +tVFS_Handle *VFS_GetHandle(int FD) +{ + if(FD < 0) return NULL; + + if(FD & VFS_KERNEL_FLAG) { + FD &= (VFS_KERNEL_FLAG - 1); + if(FD >= MAX_KERNEL_FILES) return NULL; + return &gaKernelHandles[ FD ]; + } else { + if(FD >= CFGINT(CFG_VFS_MAXFILES)) return NULL; + return &gaUserHandles[ FD ]; + } +}