--- /dev/null
+<?php
+$gLines = file("syscalls.lst");
+
+$lSyscalls = array();
+$i = 0;
+foreach($gLines as $line)
+{
+ $line = trim($line);
+ if(empty($line)) continue;
+
+ //echo $line,"\n";
+ //echo intVal($line),"\n";
+ if( intVal($line) != 0)
+ $i = $line;
+ else
+ $lSyscalls[$i++] = explode("\t", $line, 2);
+ //echo $i,"\n";
+}
+$lMax = $i;
+
+$lAsmInc = "; Acess2
+; System Calls List
+;
+
+";
+$lHeader = "/*
+ * AcessOS Microkernel Version
+ * syscalls.h
+ */
+#ifndef _SYSCALLS_H
+#define _SYSCALLS_H
+
+enum eSyscalls {
+";
+$i = 0;
+foreach($lSyscalls as $num=>$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);
+
+?>
--- /dev/null
+# 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.<ARCH>.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)
--- /dev/null
+#
+# Acess2 Makefile Config
+# Makefile.cfg
+
+ARCH = i386
+ARCHDIR = x86
--- /dev/null
+#
+# 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
--- /dev/null
+; 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
--- /dev/null
+/*
+ * Acess2 - x86 Architecture
+ * arch/x86/errors.c
+ * - CPU Error Handler
+ */
+#include <common.h>
+
+// === 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");
+}
--- /dev/null
+/*
+ * 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_)
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ */
+#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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * AcessOS Microkernel Version
+ * irq.c
+ */
+#include <common.h>
+
+// === 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;
+}
--- /dev/null
+/*
+ * AcessOS Microkernel Version
+ * lib.c
+ */
+#include <common.h>
+
+// === 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);
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * Acess 2
+ * x86 Kernel Main
+ * arch/x86/main.c
+ */
+#include <common.h>
+#include <mboot.h>
+#include <init.h>
+#include <mm_virt.h>
+#include <mp.h>
+
+#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;i<MbInfo->ModuleCount;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;
+}
--- /dev/null
+/*
+ AcessOS Microkernel Version
+ mm_phys.c
+*/
+#include <common.h>
+#include <mboot.h>
+#include <mm_virt.h>
+
+#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&&a<num;a++);
+ if(a == num) {
+ RELEASE( &giPhysAlloc );
+ return 0;
+ }
+ for(b=0;gaSuperBitmap[a]&(1<<b);b++);
+ for(c=0;gaPageBitmap[a*32+b]&(1<<c);c++);
+ //for(c=0;gaPageReferences[a*32*32+b*32+c]>0;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 );
+}
--- /dev/null
+/*
+ * 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 <common.h>
+#include <mm_phys.h>
+#include <proc.h>
+
+#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<KERNEL_STACK_END;base+=KERNEL_STACK_SIZE)
+ {
+ if(MM_GetPhysAddr(base) != 0) continue;
+ for(i=0;i<KERNEL_STACK_SIZE;i+=0x1000) {
+ MM_Allocate(base+i);
+ }
+ return base+KERNEL_STACK_SIZE;
+ }
+ Warning("MM_NewKStack - No address space left\n");
+ return 0;
+}
+
+/**
+ * \fn void MM_SetFlags(Uint VAddr, Uint Flags, Uint Mask)
+ * \brief Sets the flags on a page
+ */
+void MM_SetFlags(Uint VAddr, Uint Flags, Uint Mask)
+{
+ tPAddr *ent;
+ if( !(gaPageDir[VAddr >> 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);
--- /dev/null
+/*
+ * AcessOS Microkernel Version
+ * proc.c
+ */
+#include <common.h>
+#include <proc.h>
+#include <mm_virt.h>
+#include <errno.h>
+#if USE_MP
+# include <mp.h>
+#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<giNumCPUs;pos++)
+ {
+ #else
+ pos = 0;
+ #endif
+ gTSSs[pos].SS0 = 0x10;
+ gTSSs[pos].ESP0 = 0; // Set properly by scheduler
+ gGDT[5+pos].LimitLow = sizeof(tTSS);
+ gGDT[5+pos].LimitHi = 0;
+ gGDT[5+pos].Access = 0x89; // Type
+ gGDT[5+pos].Flags = 0x4;
+ gGDT[5+pos].BaseLow = (Uint)&gTSSs[pos] & 0xFFFF;
+ gGDT[5+pos].BaseMid = (Uint)&gTSSs[pos] >> 16;
+ gGDT[5+pos].BaseHi = (Uint)&gTSSs[pos] >> 24;
+ #if USE_MP
+ }
+ for(pos=0;pos<giNumCPUs;pos++) {
+ #endif
+ __asm__ __volatile__ ("ltr %%ax"::"a"(0x28+pos*8));
+ #if USE_MP
+ }
+ #endif
+
+ // Set timer frequency
+ outb(0x43, 0x34); // Set Channel 0, Low/High, Rate Generator
+ outb(0x40, TIMER_DIVISOR&0xFF); // Low Byte of Divisor
+ outb(0x40, (TIMER_DIVISOR>>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;
+}
--- /dev/null
+; AcessOS Microkernel Version\r
+; Start.asm\r
+\r
+[bits 32]\r
+\r
+KERNEL_BASE equ 0xC0000000\r
+\r
+[section .multiboot]\r
+mboot:\r
+ ; Multiboot macros to make a few lines later more readable\r
+ MULTIBOOT_PAGE_ALIGN equ 1<<0\r
+ MULTIBOOT_MEMORY_INFO equ 1<<1\r
+ MULTIBOOT_HEADER_MAGIC equ 0x1BADB002\r
+ MULTIBOOT_HEADER_FLAGS equ MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO\r
+ MULTIBOOT_CHECKSUM equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)\r
+ \r
+ ; This is the GRUB Multiboot header. A boot signature\r
+ dd MULTIBOOT_HEADER_MAGIC\r
+ dd MULTIBOOT_HEADER_FLAGS\r
+ dd MULTIBOOT_CHECKSUM\r
+ dd mboot - KERNEL_BASE ;Location of Multiboot Header\r
+ \r
+[section .text]\r
+[extern _kmain]\r
+[global start]\r
+start:\r
+ ; Set up stack\r
+ mov esp, _Kernel_Stack_Top\r
+ \r
+ ; Start Paging\r
+ mov ecx, _gaInitPageDir - KERNEL_BASE\r
+ mov cr3, ecx\r
+ \r
+ mov ecx, cr0\r
+ or ecx, 0x80000000\r
+ mov cr0, ecx\r
+ \r
+ lea ecx, [.higherHalf]\r
+ jmp ecx\r
+.higherHalf:\r
+\r
+ mov DWORD [_gaInitPageDir], 0\r
+\r
+ ; Call the kernel\r
+ push ebx ; Multiboot Info\r
+ push eax ; Multiboot Magic Value\r
+ call _kmain
+
+ ; Halt the Machine\r
+ cli\r
+.hlt:\r
+ hlt\r
+ jmp .hlt\r
+\r
+[global _GetEIP]\r
+_GetEIP:\r
+ mov eax, [esp]\r
+ ret\r
+\r
+[extern _Proc_Clone]\r
+[extern _Proc_Exit]\r
+[global _SpawnTask]\r
+_SpawnTask:\r
+ ; Call Proc_Clone with Flags=0\r
+ xor eax, eax\r
+ push eax
+ push eax\r
+ call _Proc_Clone\r
+ add esp, 8 ; Remove arguments from stack\r
+ \r
+ test eax, eax\r
+ jnz .parent\r
+ \r
+ ; In child, so now set up stack frame\r
+ mov ebx, [esp+4] ; Child Function\r
+ mov edx, [esp+8] ; Argument\r
+ ; Child\r
+ push edx ; Argument\r
+ call ebx ; Function\r
+ call _Proc_Exit ; Kill Thread\r
+ \r
+.parent:\r
+ ret\r
+\r
+[section .initpd]\r
+[global _gaInitPageDir]\r
+[global _gaInitPageTable]\r
+align 0x1000\r
+_gaInitPageDir:\r
+ dd _gaInitPageTable-KERNEL_BASE+3 ; 0x00\r
+ times 1024-256-1 dd 0\r
+ dd _gaInitPageTable-KERNEL_BASE+3 ; 0xC0\r
+ times 256-1 dd 0\r
+align 0x1000\r
+_gaInitPageTable:\r
+ %assign i 0\r
+ %rep 1024\r
+ dd i*0x1000+3\r
+ %assign i i+1\r
+ %endrep\r
+[global _Kernel_Stack_Top]\r
+ALIGN 0x1000\r
+ times 1024 dd 0\r
+_Kernel_Stack_Top:\r
+
--- /dev/null
+/*
+ * Acess2 Kernel
+ * Timekeeping
+ * arch/x86/time.c
+ */
+#include <common.h>
+
+// === 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;
+}
--- /dev/null
+/**\r
+ Acess v1\r
+ \file bin_elf.h\r
+ \brief ELF Exeutable Loader\r
+*/\r
+#ifndef _BIN_ELF_H\r
+#define _BIN_ELF_H\r
+\r
+/**\r
+ \struct elf_header_s\r
+ \brief ELF File Header\r
+*/\r
+struct sElf32_Ehdr {\r
+ union {\r
+ char ident[16]; //!< Identifier Bytes\r
+ struct {\r
+ Uint Ident1;\r
+ Uint Ident2;\r
+ Uint HashTable;\r
+ Uint SymTable;\r
+ } misc;\r
+ };\r
+ Uint16 filetype; //!< File Type\r
+ Uint16 machine; //!< Machine / Arch\r
+ Uint32 version; //!< Version (File?)\r
+ Uint32 entrypoint; //!< Entry Point\r
+ Uint32 phoff; //!< Program Header Offset\r
+ Uint32 shoff; //!< Section Header Offset\r
+ Uint32 flags; //!< Flags\r
+ Uint16 headersize; //!< Header Size\r
+ Uint16 phentsize; //!< Program Header Entry Size\r
+ Uint16 phentcount; //!< Program Header Entry Count\r
+ Uint16 shentsize; //!< Section Header Entry Size\r
+ Uint16 shentcount; //!< Section Header Entry Count\r
+ Uint16 shstrindex; //!< Section Header String Table Index\r
+};\r
+\r
+/**\r
+ \name Executable Types\r
+ \{\r
+*/\r
+#define ET_NONE 0 //!< NULL Type\r
+#define ET_REL 1 //!< Relocatable (Object)\r
+#define ET_EXEC 2 //!< Executable\r
+#define ET_DYN 3 //!< Dynamic Library\r
+#define ET_CORE 4 //!< Core?\r
+#define ET_LOPROC 0xFF00 //!< Low Impl Defined\r
+#define ET_HIPROC 0xFFFF //!< High Impl Defined\r
+//! \}\r
+\r
+/**\r
+ \name Section IDs\r
+ \{\r
+*/\r
+#define SHN_UNDEF 0 //!< Undefined Section\r
+#define SHN_LORESERVE 0xFF00 //!< Low Reserved\r
+#define SHN_LOPROC 0xFF00 //!< Low Impl Defined\r
+#define SHN_HIPROC 0xFF1F //!< High Impl Defined\r
+#define SHN_ABS 0xFFF1 //!< Absolute Address (Base: 0, Size: -1)\r
+#define SHN_COMMON 0xFFF2 //!< Common\r
+#define SHN_HIRESERVE 0xFFFF //!< High Reserved\r
+//! \}\r
+\r
+/**\r
+ \enum eElfSectionTypes\r
+ \brief ELF Section Types\r
+*/\r
+enum eElfSectionTypes {\r
+ SHT_NULL, //0\r
+ SHT_PROGBITS, //1\r
+ SHT_SYMTAB, //2\r
+ SHT_STRTAB, //3\r
+ SHT_RELA, //4\r
+ SHT_HASH, //5\r
+ SHT_DYNAMIC, //6\r
+ SHT_NOTE, //7\r
+ SHT_NOBITS, //8\r
+ SHT_REL, //9\r
+ SHT_SHLIB, //A\r
+ SHT_DYNSYM, //B\r
+ SHT_LAST, //C\r
+ SHT_LOPROC = 0x70000000,\r
+ SHT_HIPROC = 0x7fffffff,\r
+ SHT_LOUSER = 0x80000000,\r
+ SHT_HIUSER = 0xffffffff\r
+};\r
+\r
+#define SHF_WRITE 0x1\r
+#define SHF_ALLOC 0x2\r
+#define SHF_EXECINSTR 0x4\r
+#define SHF_MASKPROC 0xf0000000\r
+\r
+struct sElf32_Shent {\r
+ Uint32 name;\r
+ Uint32 type;\r
+ Uint32 flags;\r
+ Uint32 address;\r
+ Uint32 offset;\r
+ Uint32 size;\r
+ Uint32 link;\r
+ Uint32 info;\r
+ Uint32 addralign;\r
+ Uint32 entsize;\r
+}; //sizeof = 40\r
+\r
+struct elf_sym_s {\r
+ union {\r
+ Uint32 nameOfs;\r
+ char *name;\r
+ };\r
+ Uint32 value; //Address\r
+ Uint32 size;\r
+ Uint8 info;\r
+ Uint8 other;\r
+ Uint16 shndx;\r
+};\r
+#define STN_UNDEF 0 // Undefined Symbol\r
+\r
+enum {\r
+ PT_NULL, //0\r
+ PT_LOAD, //1\r
+ PT_DYNAMIC, //2\r
+ PT_INTERP, //3\r
+ PT_NOTE, //4\r
+ PT_SHLIB, //5\r
+ PT_PHDR, //6\r
+ PT_LOPROC = 0x70000000,\r
+ PT_HIPROC = 0x7fffffff\r
+};\r
+\r
+struct sElf32_Phdr {\r
+ Uint32 Type;\r
+ Uint Offset;\r
+ Uint VAddr;\r
+ Uint PAddr;\r
+ Uint32 FileSize;\r
+ Uint32 MemSize;\r
+ Uint32 Flags;\r
+ Uint32 Align;\r
+};\r
+\r
+struct elf32_rel_s {\r
+ Uint32 r_offset;\r
+ Uint32 r_info;\r
+};\r
+\r
+struct elf32_rela_s {\r
+ Uint32 r_offset;\r
+ Uint32 r_info;\r
+ Sint32 r_addend;\r
+};\r
+\r
+enum {\r
+ R_386_NONE=0, // none\r
+ R_386_32, // S+A\r
+ R_386_PC32, // S+A-P\r
+ R_386_GOT32, // G+A-P\r
+ R_386_PLT32, // L+A-P\r
+ R_386_COPY, // none\r
+ R_386_GLOB_DAT, // S\r
+ R_386_JMP_SLOT, // S\r
+ R_386_RELATIVE, // B+A\r
+ R_386_GOTOFF, // S+A-GOT\r
+ R_386_GOTPC, // GOT+A-P\r
+ R_386_LAST // none\r
+};\r
+\r
+#define ELF32_R_SYM(i) ((i)>>8) // Takes an info value and returns a symbol index\r
+#define ELF32_R_TYPE(i) ((i)&0xFF) // Takes an info value and returns a type\r
+#define ELF32_R_INFO(s,t) (((s)<<8)+((t)&0xFF)) // Takes a type and symbol index and returns an info value\r
+\r
+struct elf32_dyn_s {\r
+ Uint16 d_tag;\r
+ Uint32 d_val; //Also d_ptr\r
+};\r
+\r
+enum {\r
+ DT_NULL, //!< Marks End of list\r
+ DT_NEEDED, //!< Offset in strtab to needed library\r
+ DT_PLTRELSZ, //!< Size in bytes of PLT\r
+ DT_PLTGOT, //!< Address of PLT/GOT\r
+ DT_HASH, //!< Address of symbol hash table\r
+ DT_STRTAB, //!< String Table address\r
+ DT_SYMTAB, //!< Symbol Table address\r
+ DT_RELA, //!< Relocation table address\r
+ DT_RELASZ, //!< Size of relocation table\r
+ DT_RELAENT, //!< Size of entry in relocation table\r
+ DT_STRSZ, //!< Size of string table\r
+ DT_SYMENT, //!< Size of symbol table entry\r
+ DT_INIT, //!< Address of initialisation function\r
+ DT_FINI, //!< Address of termination function\r
+ DT_SONAME, //!< String table offset of so name\r
+ DT_RPATH, //!< String table offset of library path\r
+ DT_SYMBOLIC,//!< Reverse order of symbol searching for library, search libs first then executable\r
+ DT_REL, //!< Relocation Entries (Elf32_Rel instead of Elf32_Rela)\r
+ DT_RELSZ, //!< Size of above table (bytes)\r
+ DT_RELENT, //!< Size of entry in above table\r
+ DT_PLTREL, //!< Relocation entry of PLT\r
+ DT_DEBUG, //!< Debugging Entry - Unknown contents\r
+ DT_TEXTREL, //!< Indicates that modifcations to a non-writeable segment may occur\r
+ DT_JMPREL, //!< Address of PLT only relocation entries\r
+ DT_LOPROC = 0x70000000, //!< Low Definable\r
+ DT_HIPROC = 0x7FFFFFFF //!< High Definable\r
+};\r
+\r
+typedef struct sElf32_Ehdr Elf32_Ehdr;\r
+typedef struct sElf32_Phdr Elf32_Phdr;\r
+typedef struct sElf32_Shent Elf32_Shent;\r
+typedef struct elf_sym_s elf_symtab;\r
+typedef struct elf_sym_s Elf32_Sym;\r
+typedef struct elf32_rel_s Elf32_Rel;\r
+typedef struct elf32_rela_s Elf32_Rela;\r
+typedef struct elf32_dyn_s Elf32_Dyn;\r
+\r
+#endif // defined(_EXE_ELF_H)\r
--- /dev/null
+/*\r
+Acess v0.1\r
+ELF Executable Loader Code\r
+*/\r
+#include <common.h>\r
+#include <binary.h>\r
+#include "bin_elf.h"\r
+\r
+#define DEBUG 1\r
+#define DEBUG_WARN 1\r
+\r
+#if DEBUG\r
+# define DEBUGS(v...) Log(v)\r
+#else\r
+# define DEBUGS(v...)\r
+# undef ENTER\r
+# undef LOG\r
+# undef LEAVE\r
+# define ENTER(...)\r
+# define LOG(...)\r
+# define LEAVE(...)\r
+#endif\r
+\r
+\r
+// === PROTOTYPES ===\r
+tBinary *Elf_Load(int fp);\r
+ int Elf_Relocate(void *Base);\r
+ int Elf_GetSymbol(void *Base, char *Name, Uint *ret);\r
+ int Elf_Int_DoRelocate(Uint r_info, Uint32 *ptr, Uint32 addend, Elf32_Sym *symtab, Uint base);\r
+Uint Elf_Int_HashString(char *str);\r
+\r
+// === GLOBALS ===\r
+tBinaryType gELF_Info = {\r
+ NULL,\r
+ 0x464C457F, 0xFFFFFFFF, // '\x7FELF'\r
+ "ELF",\r
+ Elf_Load, Elf_Relocate, Elf_GetSymbol\r
+ };\r
+\r
+// === CODE ===\r
+tBinary *Elf_Load(int fp)\r
+{\r
+ tBinary *ret;\r
+ Elf32_Ehdr hdr;\r
+ Elf32_Phdr *phtab;\r
+ int i, j, k;\r
+ int iPageCount;\r
+ int count;\r
+ \r
+ ENTER("ifp", fp);\r
+ \r
+ // Read ELF Header\r
+ VFS_Read(fp, sizeof(hdr), &hdr);\r
+ \r
+ // Check the file type\r
+ if(hdr.ident[0] != 0x7F || hdr.ident[1] != 'E' || hdr.ident[2] != 'L' || hdr.ident[3] != 'F') {\r
+ Warning("Non-ELF File was passed to the ELF loader\n");\r
+ LEAVE('n');\r
+ return NULL;\r
+ }\r
+ \r
+ // Check for a program header\r
+ if(hdr.phoff == 0) {\r
+ #if DEBUG_WARN\r
+ Warning("ELF File does not contain a program header\n");\r
+ #endif\r
+ LEAVE('n');\r
+ return NULL;\r
+ }\r
+ \r
+ // Read Program Header Table\r
+ phtab = malloc(sizeof(Elf32_Phdr)*hdr.phentcount);\r
+ VFS_Seek(fp, hdr.phoff, SEEK_SET);\r
+ VFS_Read(fp, sizeof(Elf32_Phdr)*hdr.phentcount, phtab);\r
+ \r
+ // Count Pages\r
+ iPageCount = 0;\r
+ LOG("hdr.phentcount = %i", hdr.phentcount);\r
+ for( i = 0; i < hdr.phentcount; i++ )\r
+ {\r
+ // Ignore Non-LOAD types\r
+ if(phtab[i].Type != PT_LOAD)\r
+ continue;\r
+ iPageCount += ((phtab[i].VAddr&0xFFF) + phtab[i].MemSize + 0xFFF) >> 12;\r
+ LOG("phtab[%i] = {VAddr:0x%x, MemSize:0x%x}", i, phtab[i].VAddr, phtab[i].MemSize);\r
+ }\r
+ \r
+ LOG("iPageCount = %i", iPageCount);\r
+ \r
+ // Allocate Information Structure\r
+ ret = malloc( sizeof(tBinary) + 3*sizeof(Uint)*iPageCount );\r
+ // Fill Info Struct\r
+ ret->Entry = hdr.entrypoint;\r
+ ret->Base = -1; // Set Base to maximum value\r
+ ret->NumPages = iPageCount;\r
+ ret->Interpreter = NULL;\r
+ \r
+ // Load Pages\r
+ j = 0;\r
+ for( i = 0; i < hdr.phentcount; i++ )\r
+ {\r
+ int lastSize;\r
+ LOG("phtab[%i].Type = 0x%x", i, phtab[i].Type);\r
+ // Get Interpreter Name\r
+ if( phtab[i].Type == PT_INTERP )\r
+ {\r
+ char *tmp;\r
+ if(ret->Interpreter) continue;\r
+ tmp = malloc(phtab[i].FileSize);\r
+ VFS_Seek(fp, phtab[i].Offset, 1);\r
+ VFS_Read(fp, phtab[i].FileSize, tmp);\r
+ ret->Interpreter = Binary_RegInterp(tmp);\r
+ LOG("Interpreter '%s'", tmp);\r
+ free(tmp);\r
+ continue;\r
+ }\r
+ // Ignore non-LOAD types\r
+ if(phtab[i].Type != PT_LOAD) continue;\r
+ \r
+ // Find Base\r
+ if(phtab[i].VAddr < ret->Base) ret->Base = phtab[i].VAddr;
+\r
+ k = 0;\r
+ \r
+ LOG("phtab[%i] = {VAddr:0x%x,Offset:0x%x,FileSize:0x%x}",\r
+ i, phtab[i].VAddr, phtab[i].Offset, phtab[i].FileSize);\r
+ \r
+ if( (phtab[i].FileSize & 0xFFF) < 0x1000 - (phtab[i].VAddr & 0xFFF) )\r
+ lastSize = phtab[i].FileSize;\r
+ else\r
+ lastSize = (phtab[i].FileSize & 0xFFF) + (phtab[i].VAddr & 0xFFF);\r
+ lastSize &= 0xFFF;\r
+ \r
+ LOG("lastSize = 0x%x", lastSize);\r
+ \r
+ // Get Pages\r
+ count = ( (phtab[i].VAddr&0xFFF) + phtab[i].FileSize + 0xFFF) >> 12;\r
+ for(;k<count;k++)\r
+ {\r
+ ret->Pages[j+k].Virtual = phtab[i].VAddr + (k<<12);\r
+ ret->Pages[j+k].Physical = phtab[i].Offset + (k<<12); // Store the offset in the physical address\r
+ if(k != 0) {\r
+ ret->Pages[j+k].Physical -= ret->Pages[j+k].Virtual&0xFFF;\r
+ ret->Pages[j+k].Virtual &= ~0xFFF;\r
+ }\r
+ if(k == count-1)\r
+ ret->Pages[j+k].Size = lastSize; // Byte count in page\r
+ else if(k == 0)\r
+ ret->Pages[j+k].Size = 4096 - (phtab[i].VAddr&0xFFF);\r
+ else\r
+ ret->Pages[j+k].Size = 4096;\r
+ LOG("ret->Pages[%i].Size = 0x%x", j+k, ret->Pages[j+k].Size);\r
+ ret->Pages[j+k].Flags = 0;\r
+ }\r
+ count = (phtab[i].MemSize + 0xFFF) >> 12;\r
+ for(;k<count;k++)\r
+ {\r
+ ret->Pages[j+k].Virtual = phtab[i].VAddr + (k<<12);\r
+ ret->Pages[j+k].Physical = -1; // -1 = Fill with zeros\r
+ if(k != 0) ret->Pages[j+k].Virtual &= ~0xFFF;\r
+ if(k == count-1 && (phtab[i].MemSize & 0xFFF))\r
+ ret->Pages[j+k].Size = phtab[i].MemSize & 0xFFF; // Byte count in page\r
+ else\r
+ ret->Pages[j+k].Size = 4096;\r
+ ret->Pages[j+k].Flags = 0;\r
+ LOG("%i - 0x%x => 0x%x - 0x%x", j+k,\r
+ ret->Pages[j+k].Physical, ret->Pages[j+k].Virtual, ret->Pages[j+k].Size);\r
+ }\r
+ j += count;\r
+ }\r
+ \r
+ #if 0\r
+ LOG("Cleaning up overlaps");\r
+ // Clear up Overlaps\r
+ {\r
+ struct {\r
+ Uint V;\r
+ Uint P;\r
+ Uint S;\r
+ Uint F;\r
+ } *tmpRgns;\r
+ count = j;\r
+ tmpRgns = malloc(sizeof(*tmpRgns)*count);\r
+ // Copy\r
+ for(i=0;i<count;i++) {\r
+ tmpRgns[i].V = ret->Pages[i].Virtual;\r
+ tmpRgns[i].P = ret->Pages[i].Physical;\r
+ tmpRgns[i].S = ret->Pages[i].Size;\r
+ tmpRgns[i].F = ret->Pages[i].Flags;\r
+ }\r
+ // Compact\r
+ for(i=1,j=0; i < count; i++)\r
+ { \r
+ if( tmpRgns[j].F == tmpRgns[i].F\r
+ && tmpRgns[j].V + tmpRgns[j].S == tmpRgns[i].V\r
+ && ((tmpRgns[j].P == -1 && tmpRgns[i].P == -1)\r
+ || (tmpRgns[j].P + tmpRgns[j].S == tmpRgns[i].P)) )\r
+ {\r
+ tmpRgns[j].S += tmpRgns[i].S;\r
+ } else {\r
+ j ++;\r
+ tmpRgns[j].V = tmpRgns[i].V;\r
+ tmpRgns[j].P = tmpRgns[i].P;\r
+ tmpRgns[j].F = tmpRgns[i].F;\r
+ tmpRgns[j].S = tmpRgns[i].S;\r
+ }\r
+ }\r
+ j ++;\r
+ // Count\r
+ count = j; j = 0;\r
+ for(i=0;i<count;i++) {\r
+ //LogF(" Elf_Load: %i - 0x%x => 0x%x - 0x%x\n", i, tmpRgns[i].P, tmpRgns[i].V, tmpRgns[i].S);\r
+ tmpRgns[i].S += tmpRgns[i].V & 0xFFF;\r
+ if(tmpRgns[i].P != -1) tmpRgns[i].P -= tmpRgns[i].V & 0xFFF;\r
+ tmpRgns[i].V &= ~0xFFF;\r
+ j += (tmpRgns[i].S + 0xFFF) >> 12;\r
+ //LogF(" Elf_Load: %i - 0x%x => 0x%x - 0x%x\n", i, tmpRgns[i].P, tmpRgns[i].V, tmpRgns[i].S);\r
+ }\r
+ // Reallocate\r
+ ret = realloc( ret, sizeof(tBinary) + 3*sizeof(Uint)*j );\r
+ if(!ret) {\r
+ Warning("BIN", "ElfLoad: Unable to reallocate return structure");\r
+ return NULL;\r
+ }\r
+ ret->NumPages = j;\r
+ // Split\r
+ k = 0;\r
+ for(i=0;i<count;i++) {\r
+ for( j = 0; j < (tmpRgns[i].S + 0xFFF) >> 12; j++,k++ ) {\r
+ ret->Pages[k].Flags = tmpRgns[i].F;\r
+ ret->Pages[k].Virtual = tmpRgns[i].V + (j<<12);\r
+ if(tmpRgns[i].P != -1) {\r
+ ret->Pages[k].Physical = tmpRgns[i].P + (j<<12);\r
+ } else\r
+ ret->Pages[k].Physical = -1;\r
+ ret->Pages[k].Size = tmpRgns[i].S - (j << 12);\r
+ // Clamp to page size\r
+ if(ret->Pages[k].Size > 0x1000) ret->Pages[k].Size = 0x1000;\r
+ }\r
+ }\r
+ // Free Temp\r
+ free(tmpRgns);\r
+ }\r
+ #endif\r
+ \r
+ // Clean Up\r
+ free(phtab);\r
+ // Return\r
+ LEAVE('p', ret);\r
+ return ret;\r
+}\r
+\r
+// --- ELF RELOCATION ---\r
+// Taken from 'ld-acess.so'\r
+/**\r
+ \fn int Elf_Relocate(void *Base)\r
+ \brief Relocates a loaded ELF Executable\r
+*/\r
+int Elf_Relocate(void *Base)\r
+{\r
+ Elf32_Ehdr *hdr = Base;\r
+ Elf32_Phdr *phtab;\r
+ int i, j; // Counters\r
+ char *libPath;\r
+ Uint iRealBase = -1;\r
+ Uint iBaseDiff;\r
+ int iSegmentCount;\r
+ int iSymCount = 0;\r
+ Elf32_Rel *rel = NULL;\r
+ Elf32_Rela *rela = NULL;\r
+ Uint32 *pltgot = NULL;\r
+ void *plt = NULL;\r
+ Uint32 *ptr;\r
+ int relSz=0, relEntSz=8;\r
+ int relaSz=0, relaEntSz=8;\r
+ int pltSz=0, pltType=0;\r
+ Elf32_Dyn *dynamicTab = NULL; // Dynamic Table Pointer\r
+ char *dynstrtab = NULL; // .dynamic String Table\r
+ Elf32_Sym *dynsymtab = NULL;\r
+ \r
+ ENTER("pBase", Base);\r
+ \r
+ // Parse Program Header to get Dynamic Table\r
+ phtab = Base + hdr->phoff;\r
+ iSegmentCount = hdr->phentcount;\r
+ for(i=0;i<iSegmentCount;i++)\r
+ {\r
+ // Determine linked base address\r
+ if(phtab[i].Type == PT_LOAD && iRealBase > phtab[i].VAddr)\r
+ iRealBase = phtab[i].VAddr;\r
+ \r
+ // Find Dynamic Section\r
+ if(phtab[i].Type == PT_DYNAMIC) {\r
+ if(dynamicTab) {\r
+ Warning("ELF", "Elf_Relocate - Multiple PT_DYNAMIC segments\n");\r
+ continue;\r
+ }\r
+ dynamicTab = (void *) phtab[i].VAddr;\r
+ j = i; // Save Dynamic Table ID\r
+ break;\r
+ }\r
+ }\r
+ \r
+ // Check if a PT_DYNAMIC segement was found\r
+ if(!dynamicTab) {\r
+ Warning("ELF", "Elf_Relocate: No PT_DYNAMIC segment in image, returning\n");\r
+ LEAVE('x', hdr->entrypoint);\r
+ return hdr->entrypoint;\r
+ }\r
+ \r
+ // Page Align real base\r
+ iRealBase &= ~0xFFF;\r
+ \r
+ // Adjust "Real" Base\r
+ iBaseDiff = (Uint)Base - iRealBase;\r
+ // Adjust Dynamic Table\r
+ dynamicTab = (void *) ((Uint)dynamicTab + iBaseDiff);\r
+ \r
+ // === Get Symbol table and String Table ===\r
+ for( j = 0; dynamicTab[j].d_tag != DT_NULL; j++)\r
+ {\r
+ switch(dynamicTab[j].d_tag)\r
+ {\r
+ // --- Symbol Table ---\r
+ case DT_SYMTAB:\r
+ dynamicTab[j].d_val += iBaseDiff;\r
+ dynsymtab = (void*)(dynamicTab[j].d_val);\r
+ hdr->misc.SymTable = dynamicTab[j].d_val; // Saved in unused bytes of ident\r
+ break;\r
+ \r
+ // --- String Table ---\r
+ case DT_STRTAB:\r
+ dynamicTab[j].d_val += iBaseDiff;\r
+ dynstrtab = (void*)(dynamicTab[j].d_val);\r
+ break;\r
+ \r
+ // --- Hash Table --\r
+ case DT_HASH:\r
+ dynamicTab[j].d_val += iBaseDiff;\r
+ iSymCount = ((Uint*)(dynamicTab[j].d_val))[1];\r
+ hdr->misc.HashTable = dynamicTab[j].d_val; // Saved in unused bytes of ident\r
+ break;\r
+ }\r
+ }\r
+\r
+\r
+ // Alter Symbols to true base\r
+ for(i=0;i<iSymCount;i++)\r
+ {\r
+ dynsymtab[i].value += iBaseDiff;\r
+ dynsymtab[i].nameOfs += (Uint)dynstrtab;\r
+ //LOG("Sym '%s' = 0x%x (relocated)\n", dynsymtab[i].name, dynsymtab[i].value);\r
+ }\r
+ \r
+ // === Add to loaded list (can be imported now) ===\r
+ //Binary_AddLoaded( (Uint)Base );\r
+\r
+ // === Parse Relocation Data ===\r
+ for( j = 0; dynamicTab[j].d_tag != DT_NULL; j++)\r
+ {\r
+ switch(dynamicTab[j].d_tag)\r
+ {\r
+ // --- Shared Library Name ---\r
+ case DT_SONAME:\r
+ LOG(".so Name '%s'\n", dynstrtab+dynamicTab[j].d_val);\r
+ break;\r
+ // --- Needed Library ---\r
+ case DT_NEEDED:\r
+ libPath = dynstrtab + dynamicTab[j].d_val;\r
+ LOG("Required Library '%s' (IGNORED in kernel mode)\n", libPath);\r
+ break;\r
+ // --- PLT/GOT ---\r
+ case DT_PLTGOT: pltgot = (void*)iBaseDiff+(dynamicTab[j].d_val); break;\r
+ case DT_JMPREL: plt = (void*)(iBaseDiff+dynamicTab[j].d_val); break;\r
+ case DT_PLTREL: pltType = dynamicTab[j].d_val; break;\r
+ case DT_PLTRELSZ: pltSz = dynamicTab[j].d_val; break;\r
+ \r
+ // --- Relocation ---\r
+ case DT_REL: rel = (void*)(iBaseDiff + dynamicTab[j].d_val); break;\r
+ case DT_RELSZ: relSz = dynamicTab[j].d_val; break;\r
+ case DT_RELENT: relEntSz = dynamicTab[j].d_val; break;\r
+ case DT_RELA: rela = (void*)(iBaseDiff + dynamicTab[j].d_val); break;\r
+ case DT_RELASZ: relaSz = dynamicTab[j].d_val; break;\r
+ case DT_RELAENT: relaEntSz = dynamicTab[j].d_val; break;\r
+ }\r
+ }\r
+ \r
+ // Parse Relocation Entries\r
+ if(rel && relSz)\r
+ {\r
+ j = relSz / relEntSz;\r
+ for( i = 0; i < j; i++ )\r
+ {\r
+ ptr = (void*)(iBaseDiff + rel[i].r_offset);\r
+ if( !Elf_Int_DoRelocate(rel[i].r_info, ptr, *ptr, dynsymtab, (Uint)Base) ) {\r
+ LEAVE('x', 0);\r
+ return 0;\r
+ }\r
+ }\r
+ }\r
+ // Parse Relocation Entries\r
+ if(rela && relaSz)\r
+ {\r
+ j = relaSz / relaEntSz;\r
+ for( i = 0; i < j; i++ )\r
+ {\r
+ ptr = (void*)(iBaseDiff + rela[i].r_offset);\r
+ if( !Elf_Int_DoRelocate(rel[i].r_info, ptr, rela[i].r_addend, dynsymtab, (Uint)Base) ) {\r
+ LEAVE('x', 0);\r
+ return 0;\r
+ }\r
+ }\r
+ }\r
+ \r
+ // === Process PLT (Procedure Linkage Table) ===\r
+ if(plt && pltSz)\r
+ {\r
+ if(pltType == DT_REL)\r
+ {\r
+ Elf32_Rel *pltRel = plt;\r
+ j = pltSz / sizeof(Elf32_Rel);\r
+ for(i = 0; i < j; i++)\r
+ {\r
+ ptr = (void*)(iBaseDiff + pltRel[i].r_offset);\r
+ if( !Elf_Int_DoRelocate(pltRel[i].r_info, ptr, *ptr, dynsymtab, (Uint)Base) ) {\r
+ LEAVE('x', 0);\r
+ return 0;\r
+ }\r
+ }\r
+ }\r
+ else\r
+ {\r
+ Elf32_Rela *pltRela = plt;\r
+ j = pltSz / sizeof(Elf32_Rela);\r
+ for(i=0;i<j;i++)\r
+ {\r
+ ptr = (void*)((Uint)Base + pltRela[i].r_offset);\r
+ if( !Elf_Int_DoRelocate(pltRela[i].r_info, ptr, pltRela[i].r_addend, dynsymtab, (Uint)Base) ) {\r
+ LEAVE('x', 0);\r
+ return 0;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ \r
+ LEAVE('x', hdr->entrypoint);\r
+ return hdr->entrypoint;\r
+}\r
+\r
+/**\r
+ * \fn void Elf_Int_DoRelocate(Uint r_info, Uint32 *ptr, Uint32 addend, Elf32_Sym *symtab, Uint base)\r
+ * \brief Performs a relocation\r
+ * \param r_info Field from relocation entry\r
+ * \param ptr Pointer to location of relocation\r
+ * \param addend Value to add to symbol\r
+ * \param symtab Symbol Table\r
+ * \param base Base of loaded binary\r
+ */\r
+int Elf_Int_DoRelocate(Uint r_info, Uint32 *ptr, Uint32 addend, Elf32_Sym *symtab, Uint base)\r
+{\r
+ Uint val;\r
+ int type = ELF32_R_TYPE(r_info);\r
+ int sym = ELF32_R_SYM(r_info);\r
+ char *sSymName = symtab[sym].name;\r
+ \r
+ //LogF("Elf_Int_DoRelocate: (r_info=0x%x, ptr=0x%x, addend=0x%x, .., base=0x%x)\n",\r
+ // r_info, ptr, addend, base);\r
+ \r
+ switch( type )\r
+ {\r
+ // Standard 32 Bit Relocation (S+A)\r
+ case R_386_32:\r
+ if( !Elf_GetSymbol((void*)base, sSymName, &val) ) // Search this binary first\r
+ if( !Binary_GetSymbol( sSymName, &val ) )\r
+ return 0;\r
+ //LOG("R_386_32 *0x%x += 0x%x('%s')", ptr, val, sSymName);\r
+ *ptr = val + addend;\r
+ break;\r
+ \r
+ // 32 Bit Relocation wrt. Offset (S+A-P)\r
+ case R_386_PC32:\r
+ if( !Elf_GetSymbol( (void*)base, sSymName, &val ) )\r
+ if( !Binary_GetSymbol( sSymName, &val ) )\r
+ return 0;\r
+ //LOG("R_386_PC32 *0x%x = 0x%x + 0x%x('%s') - 0x%x", ptr, *ptr, val, sSymName, (Uint)ptr );\r
+ // TODO: Check if it needs the true value of ptr or the compiled value\r
+ // NOTE: Testing using true value\r
+ *ptr = val + addend - (Uint)ptr;\r
+ break;\r
+\r
+ // Absolute Value of a symbol (S)\r
+ case R_386_GLOB_DAT:\r
+ if( !Elf_GetSymbol( (void*)base, sSymName, &val ) )\r
+ if( !Binary_GetSymbol( sSymName, &val ) )\r
+ return 0;\r
+ //LOG("R_386_GLOB_DAT *0x%x = 0x%x (%s)", ptr, val, sSymName);\r
+ *ptr = val;\r
+ break;\r
+ \r
+ // Absolute Value of a symbol (S)\r
+ case R_386_JMP_SLOT:\r
+ if( !Elf_GetSymbol( (void*)base, sSymName, &val ) )\r
+ if( !Binary_GetSymbol( sSymName, &val ) )\r
+ return 0;\r
+ //LOG("R_386_JMP_SLOT *0x%x = 0x%x (%s)", ptr, val, sSymName);\r
+ *ptr = val;\r
+ break;\r
+\r
+ // Base Address (B+A)\r
+ case R_386_RELATIVE:\r
+ //LOG("R_386_RELATIVE *0x%x = 0x%x + 0x%x", ptr, base, addend);\r
+ *ptr = base + addend;\r
+ break;\r
+ \r
+ default:\r
+ LOG("Rel 0x%x: 0x%x,%i", ptr, sym, type);\r
+ break;\r
+ }\r
+ return 1;\r
+}\r
+\r
+/**\r
+ * \fn int Elf_GetSymbol(void *Base, char *name, Uint *ret)\r
+ * \brief Get a symbol from the loaded binary\r
+ */\r
+int Elf_GetSymbol(void *Base, char *Name, Uint *ret)\r
+{\r
+ Elf32_Ehdr *hdr = (void*)Base;\r
+ Elf32_Sym *symtab;\r
+ int nbuckets = 0;\r
+ int iSymCount = 0;\r
+ int i;\r
+ Uint *pBuckets;\r
+ Uint *pChains;\r
+ Uint iNameHash;\r
+\r
+ if(!Base) return 0;\r
+\r
+ pBuckets = (void *) hdr->misc.HashTable;\r
+ symtab = (void *) hdr->misc.SymTable;\r
+ \r
+ nbuckets = pBuckets[0];\r
+ iSymCount = pBuckets[1];\r
+ pBuckets = &pBuckets[2];\r
+ pChains = &pBuckets[ nbuckets ];\r
+ \r
+ // Get hash\r
+ iNameHash = Elf_Int_HashString(Name);\r
+ iNameHash %= nbuckets;\r
+\r
+ // Check Bucket\r
+ i = pBuckets[ iNameHash ];\r
+ if(symtab[i].shndx != SHN_UNDEF && strcmp(symtab[i].name, Name) == 0) {\r
+ *ret = symtab[ i ].value;\r
+ return 1;\r
+ }\r
+ \r
+ // Walk Chain\r
+ while(pChains[i] != STN_UNDEF)\r
+ {\r
+ i = pChains[i];\r
+ if(symtab[i].shndx != SHN_UNDEF && strcmp(symtab[ i ].name, Name) == 0) {\r
+ *ret = symtab[ i ].value;\r
+ return 1;\r
+ }\r
+ }\r
+ return 0;\r
+}\r
+\r
+/**\r
+ * \fn Uint Elf_Int_HashString(char *str)\r
+ * \brief Hash a string in the ELF format\r
+ * \param str String to hash\r
+ * \return Hash value\r
+ */\r
+Uint Elf_Int_HashString(char *str)\r
+{\r
+ Uint h = 0, g;\r
+ while(*str)\r
+ {\r
+ h = (h << 4) + *str++;\r
+ if( (g = h & 0xf0000000) )\r
+ h ^= g >> 24;\r
+ h &= ~g;\r
+ }\r
+ return h;\r
+}\r
--- /dev/null
+/*\r
+ * Acess2\r
+ * Common Binary Loader\r
+ */\r
+#include <common.h>\r
+#include <binary.h>\r
+\r
+#define DEBUG 1\r
+\r
+#if DEBUG\r
+#else\r
+# undef ENTER\r
+# undef LOG\r
+# undef LEAVE\r
+# define ENTER(...)\r
+# define LOG(...)\r
+# define LEAVE(...)\r
+#endif\r
+\r
+// === CONSTANTS ===\r
+#define BIN_LOWEST MM_USER_MIN // 1MiB\r
+#define BIN_GRANUALITY 0x10000 // 64KiB\r
+#define BIN_HIGHEST (0xBC000000-BIN_GRANUALITY) // Just below the kernel\r
+#define KLIB_LOWEST MM_MODULE_MIN\r
+#define KLIB_GRANUALITY 0x8000 // 32KiB\r
+#define KLIB_HIGHEST (MM_MODULE_MAX-KLIB_GRANUALITY)\r
+\r
+// === TYPES ===\r
+typedef struct sKernelBin {\r
+ struct sKernelBin *Next;\r
+ void *Base;\r
+ tBinary *Info;\r
+} tKernelBin;\r
+\r
+// === IMPORTS ===\r
+extern int Proc_Clone(Uint *Err, Uint Flags);\r
+extern void Proc_SetThreadName(char *Name);\r
+extern Uint MM_ClearUser();\r
+extern void Proc_Exit();\r
+extern void Proc_StartUser(Uint Entrypoint, Uint *Bases, int ArgC, char **ArgV, char **EnvP, int DataSize);\r
+extern tKernelSymbol gKernelSymbols[];\r
+extern void gKernelSymbolsEnd;\r
+extern tBinaryType gELF_Info;\r
+\r
+// === PROTOTYPES ===\r
+ int Proc_Execve(char *File, char **ArgV, char **EnvP);\r
+Uint Binary_Load(char *file, Uint *entryPoint);\r
+tBinary *Binary_GetInfo(char *truePath);\r
+Uint Binary_MapIn(tBinary *binary);\r
+Uint Binary_IsMapped(tBinary *binary);\r
+tBinary *Binary_DoLoad(char *truePath);\r
+void Binary_Dereference(tBinary *Info);\r
+Uint Binary_Relocate(void *Base);\r
+Uint Binary_GetSymbolEx(char *Name, Uint *Value);\r
+Uint Binary_FindSymbol(void *Base, char *Name, Uint *val);\r
+\r
+// === GLOBALS ===\r
+ int glBinListLock = 0;\r
+tBinary *glLoadedBinaries = NULL;\r
+char **gsaRegInterps = NULL;\r
+ int giRegInterps = 0;\r
+ int glKBinListLock = 0;\r
+tKernelBin *glLoadedKernelLibs;\r
+tBinaryType *gRegBinTypes = &gELF_Info;\r
+ \r
+// === FUNCTIONS ===\r
+/**\r
+ * \fn int Proc_Spawn(char *Path)\r
+ */\r
+int Proc_Spawn(char *Path)\r
+{\r
+ char stackPath[strlen(Path)+1];\r
+ \r
+ strcpy(stackPath, Path);\r
+ \r
+ LOG("stackPath = '%s'\n", stackPath);\r
+ \r
+ if(Proc_Clone(NULL, CLONE_VM) == 0)\r
+ {\r
+ // CHILD\r
+ char *args[2] = {stackPath, NULL};\r
+ LOG("stackPath = '%s'\n", stackPath);\r
+ Proc_Execve(stackPath, args, &args[1]);\r
+ for(;;);\r
+ }\r
+ return 0;\r
+}\r
+\r
+/**\r
+ * \fn int Proc_Execve(char *File, char **ArgV, char **EnvP)\r
+ * \brief Replace the current user image with another\r
+ * \param File File to load as the next image\r
+ * \param ArgV Arguments to pass to user\r
+ * \param EnvP User's environment\r
+ * \note Called Proc_ for historical reasons\r
+ */\r
+int Proc_Execve(char *File, char **ArgV, char **EnvP)\r
+{\r
+ int argc, envc, i;\r
+ int argenvBytes;\r
+ char *argenvBuf, *strBuf;\r
+ char **argvSaved, **envpSaved;\r
+ char *savedFile;\r
+ Uint entry;\r
+ Uint bases[2] = {0};\r
+ \r
+ ENTER("sFile pArgV pEnvP", File, ArgV, EnvP);\r
+ \r
+ // --- Save File, ArgV and EnvP (also get argc)\r
+ \r
+ // Count Arguments, Environment Variables and total string sizes\r
+ argenvBytes = 0;\r
+ for( argc = 0; ArgV && ArgV[argc]; argc++ )\r
+ argenvBytes += strlen(ArgV[argc])+1;\r
+ for( envc = 0; EnvP && EnvP[envc]; envc++ )\r
+ argenvBytes += strlen(EnvP[envc])+1;\r
+ argenvBytes = (argenvBytes + sizeof(void*)-1) & ~(sizeof(void*)-1);\r
+ argenvBytes += (argc+1)*sizeof(void*) + (envc+1)*sizeof(void*);\r
+ \r
+ // Allocate\r
+ argenvBuf = malloc(argenvBytes);\r
+ if(argenvBuf == NULL) {\r
+ Warning("Proc_Execve - What the hell? The kernel is out of heap space");\r
+ return 0;\r
+ }\r
+ strBuf = argenvBuf + (argc+1)*sizeof(void*) + (envc+1)*sizeof(void*);\r
+ \r
+ // Populate\r
+ argvSaved = (char **) argenvBuf;\r
+ for( i = 0; i < argc; i++ )\r
+ {\r
+ argvSaved[i] = strBuf;\r
+ strcpy(argvSaved[i], ArgV[i]);\r
+ strBuf += strlen(ArgV[i])+1;\r
+ }\r
+ argvSaved[i] = NULL;\r
+ envpSaved = &argvSaved[i+1];\r
+ for( i = 0; i < envc; i++ )\r
+ {\r
+ envpSaved[i] = strBuf;\r
+ strcpy(envpSaved[i], EnvP[i]);\r
+ strBuf += strlen(EnvP[i])+1;\r
+ }\r
+ \r
+ savedFile = malloc(strlen(File)+1);\r
+ strcpy(savedFile, File);\r
+ \r
+ // --- Set Process Name\r
+ Proc_SetThreadName(File);\r
+ \r
+ // --- Clear User Address space\r
+ MM_ClearUser();\r
+ \r
+ // --- Load new binary\r
+ bases[0] = Binary_Load(savedFile, &entry);\r
+ free(savedFile);\r
+ if(bases[0] == 0)\r
+ {\r
+ Warning("Proc_Execve - Unable to load '%s'", File);\r
+ Proc_Exit();\r
+ for(;;);\r
+ }\r
+ \r
+ LOG("entry = 0x%x, bases[0] = 0x%x", entry, bases[0]);\r
+ LEAVE('-');\r
+ // --- And... Jump to it\r
+ Proc_StartUser(entry, bases, argc, argvSaved, envpSaved, argenvBytes);\r
+ for(;;); // Tell GCC that we never return\r
+}\r
+\r
+/**\r
+ * \fn Uint Binary_Load(char *file, Uint *entryPoint)\r
+ */\r
+Uint Binary_Load(char *file, Uint *entryPoint)\r
+{\r
+ char *sTruePath;\r
+ tBinary *pBinary;\r
+ Uint base = -1;\r
+\r
+ ENTER("sfile", file);\r
+ \r
+ // Sanity Check Argument\r
+ if(file == NULL) {\r
+ LEAVE('x', 0);\r
+ return 0;\r
+ }\r
+\r
+ // Get True File Path\r
+ sTruePath = VFS_GetTruePath(file);\r
+ \r
+ if(sTruePath == NULL) {\r
+ Warning("[BIN ] '%s' does not exist.", file);\r
+ LEAVE('x', 0);\r
+ return 0;\r
+ }\r
+ \r
+ LOG("sTruePath = '%s'", sTruePath);\r
+\r
+ // Check if the binary has already been loaded\r
+ if( !(pBinary = Binary_GetInfo(sTruePath)) )\r
+ pBinary = Binary_DoLoad(sTruePath); // Else load it\r
+ \r
+ // Clean Up\r
+ free(sTruePath);\r
+ \r
+ // Error Check\r
+ if(pBinary == NULL) {\r
+ LEAVE('x', 0);\r
+ return 0;\r
+ }\r
+ \r
+ #if 0\r
+ if( (base = Binary_IsMapped(pBinary)) ) {\r
+ LEAVE('x', base);\r
+ return base;\r
+ }\r
+ #endif\r
+ \r
+ // Map into process space\r
+ base = Binary_MapIn(pBinary); // If so then map it in\r
+ \r
+ // Check for errors\r
+ if(base == 0) {\r
+ LEAVE('x', 0);\r
+ return 0;\r
+ }\r
+ \r
+ // Interpret\r
+ if(pBinary->Interpreter) {\r
+ Uint start;\r
+ if( Binary_Load(pBinary->Interpreter, &start) == 0 ) {\r
+ LEAVE('x', 0);\r
+ return 0;\r
+ }\r
+ *entryPoint = start;\r
+ }\r
+ else\r
+ *entryPoint = pBinary->Entry - pBinary->Base + base;\r
+ \r
+ // Return\r
+ LOG("*entryPoint = 0x%x", *entryPoint);\r
+ LEAVE('x', base);\r
+ return base; // Pass the base as an argument to the user if there is an interpreter\r
+}\r
+\r
+/**\r
+ \fn tBinary *Binary_GetInfo(char *truePath)\r
+ \brief Finds a matching binary entry\r
+ \param truePath File Identifier (True path name)\r
+*/\r
+tBinary *Binary_GetInfo(char *truePath)\r
+{\r
+ tBinary *pBinary;\r
+ pBinary = glLoadedBinaries;\r
+ while(pBinary)\r
+ {\r
+ if(strcmp(pBinary->TruePath, truePath) == 0)\r
+ return pBinary;\r
+ pBinary = pBinary->Next;\r
+ }\r
+ return NULL;\r
+}\r
+\r
+/**\r
+ \fn Uint Binary_MapIn(tBinary *binary)\r
+ \brief Maps an already-loaded binary into an address space.\r
+ \param binary Pointer to globally stored data.\r
+*/\r
+Uint Binary_MapIn(tBinary *binary)\r
+{\r
+ Uint base;\r
+ Uint addr;\r
+ int i;\r
+ \r
+ // Reference Executable (Makes sure that it isn't unloaded)\r
+ binary->ReferenceCount ++;\r
+ \r
+ // Get Binary Base\r
+ base = binary->Base;\r
+ \r
+ // Check if base is free\r
+ if(base != 0)\r
+ {\r
+ for(i=0;i<binary->NumPages;i++)\r
+ {\r
+ if( MM_GetPhysAddr( binary->Pages[i].Virtual & ~0xFFF ) ) {\r
+ base = 0;\r
+ LOG("Address 0x%x is taken\n", binary->Pages[i].Virtual & ~0xFFF);\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ \r
+ // Check if the executable has no base or it is not free\r
+ if(base == 0)\r
+ {\r
+ // If so, give it a base\r
+ base = BIN_HIGHEST;\r
+ while(base >= BIN_LOWEST)\r
+ {\r
+ for(i=0;i<binary->NumPages;i++)\r
+ {\r
+ addr = binary->Pages[i].Virtual & ~0xFFF;\r
+ addr -= binary->Base;\r
+ addr += base;\r
+ if( MM_GetPhysAddr( addr ) ) break;\r
+ }\r
+ // If space was found, break\r
+ if(i == binary->NumPages) break;\r
+ // Else decrement pointer and try again\r
+ base -= BIN_GRANUALITY;\r
+ }\r
+ }\r
+ \r
+ // Error Check\r
+ if(base < BIN_LOWEST) {\r
+ Warning("[BIN ] Executable '%s' cannot be loaded, no space", binary->TruePath);\r
+ return 0;\r
+ }\r
+ \r
+ // Map Executable In\r
+ for(i=0;i<binary->NumPages;i++)\r
+ {\r
+ addr = binary->Pages[i].Virtual & ~0xFFF;\r
+ addr -= binary->Base;\r
+ addr += base;\r
+ LOG("%i - 0x%x to 0x%x", i, addr, binary->Pages[i].Physical);\r
+ MM_Map( addr, (Uint) (binary->Pages[i].Physical) );\r
+ if( binary->Pages[i].Physical & 1) // Read-Only\r
+ MM_SetFlags( addr, MM_PFLAG_RO, -1 );\r
+ else\r
+ MM_SetFlags( addr, MM_PFLAG_COW, -1 );\r
+ }\r
+ \r
+ //LOG("*0x%x = 0x%x\n", binary->Pages[0].Virtual, *(Uint*)binary->Pages[0].Virtual);\r
+ \r
+ return base;\r
+}\r
+\r
+#if 0\r
+/**\r
+ * \fn Uint Binary_IsMapped(tBinary *binary)\r
+ * \brief Check if a binary is already mapped into the address space\r
+ * \param binary Binary information to check\r
+ * \return Current Base or 0\r
+ */\r
+Uint Binary_IsMapped(tBinary *binary)\r
+{\r
+ Uint iBase;\r
+ \r
+ // Check prefered base\r
+ iBase = binary->Base;\r
+ if(MM_GetPage( iBase ) == (binary->Pages[0].Physical & ~0xFFF))\r
+ return iBase;\r
+ \r
+ for(iBase = BIN_HIGHEST;\r
+ iBase >= BIN_LOWEST;\r
+ iBase -= BIN_GRANUALITY)\r
+ {\r
+ if(MM_GetPage( iBase ) == (binary->Pages[0].Physical & ~0xFFF))\r
+ return iBase;\r
+ }\r
+ \r
+ return 0;\r
+}\r
+#endif\r
+\r
+/**\r
+ * \fn tBinary *Binary_DoLoad(char *truePath)\r
+ * \brief Loads a binary file into memory\r
+ * \param truePath Absolute filename of binary\r
+ */\r
+tBinary *Binary_DoLoad(char *truePath)\r
+{\r
+ tBinary *pBinary;\r
+ int fp, i;\r
+ Uint ident;\r
+ tBinaryType *bt = gRegBinTypes;\r
+ \r
+ ENTER("struePath", truePath);\r
+ \r
+ // Open File\r
+ fp = VFS_Open(truePath, VFS_OPENFLAG_READ);\r
+ if(fp == -1) {\r
+ LOG("Unable to load file, access denied");\r
+ LEAVE('n');\r
+ return NULL;\r
+ }\r
+ \r
+ // Read File Type\r
+ VFS_Read(fp, 4, &ident);\r
+ VFS_Seek(fp, 0, SEEK_SET);\r
+ \r
+ for(; bt; bt = bt->Next)\r
+ {\r
+ if( (ident & bt->Mask) != (Uint)bt->Ident )\r
+ continue;\r
+ pBinary = bt->Load(fp);\r
+ break;\r
+ }\r
+ if(!bt) {\r
+ Warning("[BIN ] '%s' is an unknown file type. (0x%x 0x%x 0x%x 0x%x)",\r
+ truePath, ident&0xFF, (ident>>8)&0xFF, (ident>>16)&0xFF, (ident>>24)&0xFF);\r
+ LEAVE('n');\r
+ return NULL;\r
+ }\r
+ \r
+ // Error Check\r
+ if(pBinary == NULL) {\r
+ LEAVE('n');\r
+ return NULL;\r
+ }\r
+ \r
+ // Initialise Structure\r
+ pBinary->ReferenceCount = 0;\r
+ pBinary->TruePath = malloc( strlen(truePath) + 1 );\r
+ strcpy(pBinary->TruePath, truePath);\r
+ \r
+ // Debug Information\r
+ LOG("Interpreter: '%s'", pBinary->Interpreter);\r
+ LOG("Base: 0x%x, Entry: 0x%x", pBinary->Base, pBinary->Entry);\r
+ LOG("NumPages: %i", pBinary->NumPages);\r
+ \r
+ // Read Data\r
+ for(i=0;i<pBinary->NumPages;i++)\r
+ {\r
+ Uint dest;\r
+ tPAddr paddr;\r
+ paddr = (Uint)MM_AllocPhys();\r
+ MM_RefPhys( paddr ); // Make sure it is _NOT_ freed until we want it to be\r
+ dest = MM_MapTemp( paddr );\r
+ dest += pBinary->Pages[i].Virtual & 0xFFF;\r
+ LOG("dest = 0x%x, paddr = 0x%x", dest, paddr);\r
+ LOG("Pages[%i]={Physical:0x%x,Virtual:0x%x,Size:0x%x}",\r
+ i, pBinary->Pages[i].Physical, pBinary->Pages[i].Virtual, pBinary->Pages[i].Size);\r
+ \r
+ // Pure Empty Page\r
+ if(pBinary->Pages[i].Physical == -1) {\r
+ LOG("%i - ZERO", i);\r
+ memsetd( (void*)dest, 0, 1024 );\r
+ }\r
+ else\r
+ {\r
+ VFS_Seek( fp, pBinary->Pages[i].Physical, 1 );\r
+ if(pBinary->Pages[i].Size != 0x1000) {\r
+ LOG("%i - 0x%x - 0x%x bytes",\r
+ i, pBinary->Pages[i].Physical, pBinary->Pages[i].Size);\r
+ memset( (void*)dest, 0, 0x1000 -(dest&0xFFF) );\r
+ VFS_Read( fp, pBinary->Pages[i].Size, (void*)dest );\r
+ } else {\r
+ LOG("%i - 0x%x", i, pBinary->Pages[i].Physical);\r
+ VFS_Read( fp, 0x1000, (void*)dest );\r
+ }\r
+ }\r
+ pBinary->Pages[i].Physical = paddr;\r
+ MM_FreeTemp( dest );\r
+ }\r
+ LOG("Page Count: %i", pBinary->NumPages);\r
+ \r
+ // Close File\r
+ VFS_Close(fp);\r
+ \r
+ // Add to the list\r
+ LOCK(&glBinListLock);\r
+ pBinary->Next = glLoadedBinaries;\r
+ glLoadedBinaries = pBinary;\r
+ RELEASE(&glBinListLock);\r
+ \r
+ // Return\r
+ LEAVE('p', pBinary);\r
+ return pBinary;\r
+}\r
+\r
+/**\r
+ * \fn void Binary_Unload(void *Base)\r
+ * \brief Unload / Unmap a binary\r
+ * \param Base Loaded Base\r
+ * \note Currently used only for kernel libaries\r
+ */\r
+void Binary_Unload(void *Base)\r
+{\r
+ tKernelBin *pKBin;\r
+ tKernelBin *prev = NULL;\r
+ int i;\r
+ \r
+ if((Uint)Base < 0xC0000000)\r
+ {\r
+ // TODO: User Binaries\r
+ Warning("[BIN ] Unloading user binaries is currently unimplemented");\r
+ return;\r
+ }\r
+ \r
+ // Kernel Libraries\r
+ for(pKBin = glLoadedKernelLibs;\r
+ pKBin;\r
+ prev = pKBin, pKBin = pKBin->Next)\r
+ {\r
+ // Check the base\r
+ if(pKBin->Base != Base) continue;\r
+ // Deallocate Memory\r
+ for(i = 0; i < pKBin->Info->NumPages; i++) {\r
+ MM_Deallocate( (Uint)Base + (i << 12) );\r
+ }\r
+ // Dereference Binary\r
+ Binary_Dereference( pKBin->Info );\r
+ // Remove from list\r
+ if(prev) prev->Next = pKBin->Next;\r
+ else glLoadedKernelLibs = pKBin->Next;\r
+ // Free Kernel Lib\r
+ free(pKBin);\r
+ return;\r
+ }\r
+}\r
+\r
+/**\r
+ * \fn void Binary_Dereference(tBinary *Info)\r
+ * \brief Dereferences and if nessasary, deletes a binary\r
+ * \param Info Binary information structure\r
+ */\r
+void Binary_Dereference(tBinary *Info)\r
+{\r
+ // Decrement reference count\r
+ Info->ReferenceCount --;\r
+ \r
+ // Check if it is still in use\r
+ if(Info->ReferenceCount) return;\r
+ \r
+ /// \todo Implement binary freeing\r
+}\r
+\r
+/**\r
+ \fn char *Binary_RegInterp(char *path)\r
+ \brief Registers an Interpreter\r
+ \param path Path to interpreter provided by executable\r
+*/\r
+char *Binary_RegInterp(char *path)\r
+{\r
+ int i;\r
+ // NULL Check Argument\r
+ if(path == NULL) return NULL;\r
+ // NULL Check the array\r
+ if(gsaRegInterps == NULL)\r
+ {\r
+ giRegInterps = 1;\r
+ gsaRegInterps = malloc( sizeof(char*) );\r
+ gsaRegInterps[0] = malloc( strlen(path) );\r
+ strcpy(gsaRegInterps[0], path);\r
+ return gsaRegInterps[0];\r
+ }\r
+ \r
+ // Scan Array\r
+ for( i = 0; i < giRegInterps; i++ )\r
+ {\r
+ if(strcmp(gsaRegInterps[i], path) == 0)\r
+ return gsaRegInterps[i];\r
+ }\r
+ \r
+ // Interpreter is not in list\r
+ giRegInterps ++;\r
+ gsaRegInterps = malloc( sizeof(char*)*giRegInterps );\r
+ gsaRegInterps[i] = malloc( strlen(path) );\r
+ strcpy(gsaRegInterps[i], path);\r
+ return gsaRegInterps[i];\r
+}\r
+\r
+// ============\r
+// Kernel Binary Handling\r
+// ============\r
+/**\r
+ * \fn void *Binary_LoadKernel(char *path)\r
+ * \brief Load a binary into kernel space\r
+ * \note This function shares much with #Binary_Load, but does it's own mapping\r
+ */\r
+void *Binary_LoadKernel(char *file)\r
+{\r
+ char *sTruePath;\r
+ tBinary *pBinary;\r
+ tKernelBin *pKBinary;\r
+ Uint base = -1;\r
+ Uint addr;\r
+ int i;\r
+\r
+ ENTER("sfile", file);\r
+ \r
+ // Sanity Check Argument\r
+ if(file == NULL) {\r
+ LEAVE('n');\r
+ return 0;\r
+ }\r
+\r
+ // Get True File Path\r
+ sTruePath = VFS_GetTruePath(file);\r
+ if(sTruePath == NULL) {\r
+ LEAVE('n');\r
+ return 0;\r
+ }\r
+ \r
+ // Check if the binary has already been loaded\r
+ if( (pBinary = Binary_GetInfo(sTruePath)) )\r
+ {\r
+ for(pKBinary = glLoadedKernelLibs;\r
+ pKBinary;\r
+ pKBinary = pKBinary->Next )\r
+ {\r
+ if(pKBinary->Info == pBinary) {\r
+ LEAVE('p', pKBinary->Base);\r
+ return pKBinary->Base;\r
+ }\r
+ }\r
+ }\r
+ else\r
+ pBinary = Binary_DoLoad(sTruePath); // Else load it\r
+ \r
+ // Error Check\r
+ if(pBinary == NULL) {\r
+ LEAVE('n');\r
+ return NULL;\r
+ }\r
+ \r
+ // --------------\r
+ // Now pBinary is valid (either freshly loaded or only user mapped)\r
+ // So, map it into kernel space\r
+ // --------------\r
+ \r
+ // Reference Executable (Makes sure that it isn't unloaded)\r
+ pBinary->ReferenceCount ++;\r
+ \r
+ // Check compiled base\r
+ base = pBinary->Base;\r
+ // - Sanity Check\r
+ if(base < KLIB_LOWEST || base > KLIB_HIGHEST || base + (pBinary->NumPages<<12) > KLIB_HIGHEST) {\r
+ base = 0;\r
+ }\r
+ // - Check if it is a valid base address\r
+ if(base != 0)\r
+ {\r
+ for(i=0;i<pBinary->NumPages;i++)\r
+ {\r
+ if( MM_GetPhysAddr( pBinary->Pages[i].Virtual & ~0xFFF ) ) {\r
+ base = 0;\r
+ LOG("Address 0x%x is taken\n", pBinary->Pages[i].Virtual & ~0xFFF);\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ \r
+ // Check if the executable has no base or it is not free\r
+ if(base == 0)\r
+ {\r
+ // If so, give it a base\r
+ base = KLIB_LOWEST;\r
+ while(base < KLIB_HIGHEST)\r
+ {\r
+ for(i = 0; i < pBinary->NumPages; i++)\r
+ {\r
+ addr = pBinary->Pages[i].Virtual & ~0xFFF;\r
+ addr -= pBinary->Base;\r
+ addr += base;\r
+ if( MM_GetPhysAddr( addr ) ) break;\r
+ }\r
+ // If space was found, break\r
+ if(i == pBinary->NumPages) break;\r
+ // Else decrement pointer and try again\r
+ base += KLIB_GRANUALITY;\r
+ }\r
+ }\r
+ \r
+ // - Error Check\r
+ if(base >= KLIB_HIGHEST) {\r
+ Warning("[BIN ] Executable '%s' cannot be loaded into kernel, no space", pBinary->TruePath);\r
+ Binary_Dereference( pBinary );\r
+ LEAVE('n');\r
+ return 0;\r
+ }\r
+ \r
+ LOG("base = 0x%x", base);\r
+ \r
+ // - Map binary in\r
+ LOG("pBinary = {NumPages:%i, Pages=%p}", pBinary->NumPages, pBinary->Pages);\r
+ for(i = 0; i < pBinary->NumPages; i++)\r
+ {\r
+ addr = pBinary->Pages[i].Virtual & ~0xFFF;\r
+ addr -= pBinary->Base;\r
+ addr += base;\r
+ LOG("%i - 0x%x to 0x%x", i, addr, pBinary->Pages[i].Physical);\r
+ MM_Map( addr, (Uint) (pBinary->Pages[i].Physical) );\r
+ MM_SetFlags( addr, MM_PFLAG_KERNEL, MM_PFLAG_KERNEL );\r
+ #if 0 // Why was this here? It's the kernel\r
+ if( pBinary->Pages[i].Physical & 1) // Read-Only\r
+ MM_SetFlags( addr, MM_PFLAG_RO, MM_PFLAG_KERNEL );\r
+ else\r
+ MM_SetFlags( addr, MM_PFLAG_COW, MM_PFLAG_KERNEL );\r
+ //MM_SetCOW( addr );\r
+ #endif\r
+ }
+\r
+ // Relocate Library\r
+ if( !Binary_Relocate( (void*)base ) )\r
+ {\r
+ Warning("[BIN ] Relocation of '%s' failed, unloading", sTruePath);\r
+ Binary_Unload( (void*)base );\r
+ Binary_Dereference( pBinary );\r
+ LEAVE('n');\r
+ return 0;\r
+ }\r
+ \r
+ // Add to list (relocator must look at itself manually, not via Binary_GetSymbol)\r
+ pKBinary = malloc(sizeof(*pKBinary));\r
+ pKBinary->Base = (void*)base;\r
+ pKBinary->Info = pBinary;\r
+ LOCK( &glKBinListLock );\r
+ pKBinary->Next = glLoadedKernelLibs;\r
+ glLoadedKernelLibs = pKBinary;\r
+ RELEASE( &glKBinListLock );\r
+ \r
+ LEAVE('p', base);\r
+ return (void*)base;\r
+}\r
+\r
+/**\r
+ * \fn Uint Binary_Relocate(void *Base)\r
+ * \brief Relocates a loaded binary (used by kernel libraries)\r
+ * \param Base Loaded base address of binary\r
+ * \return Boolean Success\r
+ */\r
+Uint Binary_Relocate(void *Base)\r
+{\r
+ Uint32 ident = *(Uint32*) Base;\r
+ tBinaryType *bt = gRegBinTypes;\r
+ \r
+ for(; bt; bt = bt->Next)\r
+ {\r
+ if( (ident & bt->Mask) == (Uint)bt->Ident )\r
+ return bt->Relocate( (void*)Base);\r
+ }\r
+ \r
+ Warning("[BIN ] 0x%x is an unknown file type. (0x%x 0x%x 0x%x 0x%x)",\r
+ Base, ident&0xFF, ident>>8, ident>>16, ident>>24);\r
+ return 0;\r
+}\r
+\r
+/**\r
+ * \fn int Binary_GetSymbol(char *Name, Uint *Val)\r
+ * \brief Get a symbol value\r
+ * \return Value of symbol or -1 on error\r
+ * \r
+ * Gets the value of a symbol from either the currently loaded\r
+ * libraries or the kernel's exports.\r
+ */\r
+int Binary_GetSymbol(char *Name, Uint *Val)\r
+{\r
+ if( Binary_GetSymbolEx(Name, Val) ) return 1;\r
+ return 0;\r
+}\r
+\r
+/**\r
+ * \fn Uint Binary_GetSymbolEx(char *Name, Uint *Value)\r
+ * \brief Get a symbol value\r
+ * \r
+ * Gets the value of a symbol from either the currently loaded\r
+ * libraries or the kernel's exports.\r
+ */\r
+Uint Binary_GetSymbolEx(char *Name, Uint *Value)\r
+{\r
+ int i;\r
+ tKernelBin *pKBin;\r
+ int numKSyms = ((Uint)&gKernelSymbolsEnd-(Uint)&gKernelSymbols)/sizeof(tKernelSymbol);\r
+ \r
+ // Scan Kernel\r
+ for( i = 0; i < numKSyms; i++ )\r
+ {\r
+ if(strcmp(Name, gKernelSymbols[i].Name) == 0) {\r
+ *Value = gKernelSymbols[i].Value;\r
+ return 1;\r
+ }\r
+ }\r
+ \r
+ // Scan Loaded Libraries\r
+ for(pKBin = glLoadedKernelLibs;\r
+ pKBin;\r
+ pKBin = pKBin->Next )\r
+ {\r
+ if( Binary_FindSymbol(pKBin->Base, Name, Value) ) {\r
+ return 1;\r
+ }\r
+ }\r
+
+ Warning("[BIN ] Unable to find symbol '%s'", Name);\r
+ return 0;\r
+}\r
+\r
+/**\r
+ * \fn Uint Binary_GetSymbolBin(void *Base, char *Name, Uint *val)\r
+ * \brief Get a symbol from the specified library\r
+ * \param Base Base address\r
+ * \param Name Name of symbol to find\r
+ * \param val Pointer to place final value\r
+ */\r
+Uint Binary_FindSymbol(void *Base, char *Name, Uint *val)\r
+{\r
+ Uint32 ident = *(Uint32*) Base;\r
+ tBinaryType *bt = gRegBinTypes;\r
+ \r
+ for(; bt; bt = bt->Next)\r
+ {\r
+ if( (ident & bt->Mask) == (Uint)bt->Ident )\r
+ return bt->GetSymbol(Base, Name, val);\r
+ }\r
+ \r
+ Warning("[BIN ] 0x%x is an unknown file type. (0x%x 0x%x 0x%x 0x%x)",\r
+ Base, ident&0xFF, ident>>8, ident>>16, ident>>24);\r
+ return 0;\r
+}\r
--- /dev/null
+/*
+ * AcessOS Microkernel Version
+ * debug.c
+ */
+#include <common.h>
+#include <stdarg.h>
+
+// === 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);
--- /dev/null
+# 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
--- /dev/null
+/*
+ * Acess2 IDE Harddisk Driver
+ * drv/ide.c
+ */
+#include <common.h>
+#include <modules.h>
+#include <vfs.h>
+#include <fs_devfs.h>
+#include <drv_pci.h>
+#include <tpl_drv_common.h>
+
+// === 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;
+}
--- /dev/null
+/**\r
+ * \file drv_bochsvbe.c\r
+ * \brief BGA (Bochs Graphic Adapter) Driver\r
+ * \note for AcessOS Version 1\r
+ * \warning This driver does NOT support the Bochs PCI VGA driver\r
+*/\r
+#include <common.h>\r
+#include <modules.h>\r
+#include <vfs.h>\r
+#include <fs_devfs.h>\r
+#include <drv_pci.h>\r
+#include <tpl_drv_video.h>\r
+\r
+#define DEBUG 0\r
+#if DEBUG\r
+# define DEBUGS(v...) SysDebug(v)\r
+#else\r
+# define DEBUGS(v...)\r
+#endif\r
+\r
+//#define INT static\r
+#define INT\r
+\r
+// === TYPEDEFS ===\r
+typedef struct {\r
+ Uint16 width;\r
+ Uint16 height;\r
+ Uint16 bpp;\r
+ Uint16 flags;\r
+ Uint32 fbSize;\r
+} t_bga_mode;\r
+\r
+\r
+// === PROTOTYPES ===\r
+// Driver\r
+ int BGA_Install(char **Arguments);\r
+void BGA_Uninstall();\r
+// Internal\r
+void BGA_int_WriteRegister(Uint16 reg, Uint16 value);\r
+Uint16 BGA_int_ReadRegister(Uint16 reg);\r
+void BGA_int_SetBank(Uint16 bank);\r
+void BGA_int_SetMode(Uint16 width, Uint16 height, Uint16 bpp);\r
+ int BGA_int_UpdateMode(int id);\r
+ int BGA_int_FindMode(tVideo_IOCtl_Mode *info);\r
+ int BGA_int_ModeInfo(tVideo_IOCtl_Mode *info);\r
+ int BGA_int_MapFB(void *Dest);\r
+// Filesystem\r
+Uint64 BGA_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer);\r
+Uint64 BGA_Write(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer);\r
+ int BGA_Ioctl(tVFS_Node *node, int id, void *data);\r
+\r
+// === CONSTANTS ===\r
+const t_bga_mode BGA_MODES[] = {\r
+ {640,480,8, 0, 640*480},\r
+ {640,480,32, 0, 640*480*4},\r
+ {800,600,8, 0, 800*600},\r
+ {800,600,32, 0, 800*600*4},\r
+};\r
+#define BGA_MODE_COUNT (sizeof(BGA_MODES)/sizeof(BGA_MODES[0]))\r
+#define BGA_LFB_MAXSIZE (1024*768*4)\r
+#define VBE_DISPI_BANK_ADDRESS 0xA0000\r
+#define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000\r
+#define VBE_DISPI_IOPORT_INDEX 0x01CE\r
+#define VBE_DISPI_IOPORT_DATA 0x01CF\r
+#define VBE_DISPI_DISABLED 0x00\r
+#define VBE_DISPI_ENABLED 0x01\r
+#define VBE_DISPI_LFB_ENABLED 0x40\r
+#define VBE_DISPI_NOCLEARMEM 0x80\r
+enum {\r
+ VBE_DISPI_INDEX_ID,\r
+ VBE_DISPI_INDEX_XRES,\r
+ VBE_DISPI_INDEX_YRES,\r
+ VBE_DISPI_INDEX_BPP,\r
+ VBE_DISPI_INDEX_ENABLE,\r
+ VBE_DISPI_INDEX_BANK,\r
+ VBE_DISPI_INDEX_VIRT_WIDTH,\r
+ VBE_DISPI_INDEX_VIRT_HEIGHT,\r
+ VBE_DISPI_INDEX_X_OFFSET,\r
+ VBE_DISPI_INDEX_Y_OFFSET\r
+};\r
+\r
+// GLOBALS\r
+MODULE_DEFINE(0, 0x0032, BochsVBE, BGA_Install, NULL, NULL);\r
+tDevFS_Driver gBGA_DriverStruct = {\r
+ NULL, "BochsGA",\r
+ {\r
+ .Read = BGA_Read,\r
+ .Write = BGA_Write,\r
+ .IOCtl = BGA_Ioctl\r
+ }\r
+};\r
+ int giBGA_CurrentMode = -1;\r
+ int giBGA_DriverId = -1;\r
+Uint *gBGA_Framebuffer;\r
+\r
+// === CODE ===\r
+/**\r
+ * \fn int BGA_Install(char **Arguments)\r
+ */\r
+int BGA_Install(char **Arguments)\r
+{\r
+ int bga_version = 0;\r
+ \r
+ // Check BGA Version\r
+ bga_version = BGA_int_ReadRegister(VBE_DISPI_INDEX_ID);\r
+ // NOTE: This driver was written for 0xB0C4, but they seem to be backwards compatable\r
+ if(bga_version < 0xB0C4 || bga_version > 0xB0C5) {\r
+ Warning("[BGA ] Bochs Adapter Version is not 0xB0C4 or 0xB0C5, instead 0x%x", bga_version);\r
+ return 0;\r
+ }\r
+ \r
+ // Install Device\r
+ giBGA_DriverId = DevFS_AddDevice( &gBGA_DriverStruct );\r
+ if(giBGA_DriverId == -1) {\r
+ Warning("[BGA ] Unable to register with DevFS, maybe already loaded?");\r
+ return 0;\r
+ }\r
+ \r
+ // Map Framebuffer to hardware address\r
+ gBGA_Framebuffer = (void *) MM_MapHWPage(VBE_DISPI_LFB_PHYSICAL_ADDRESS, 768); // 768 pages (3Mb)\r
+ \r
+ return 1;\r
+}\r
+\r
+/**\r
+ * \fn void BGA_Uninstall()\r
+ */\r
+void BGA_Uninstall()\r
+{\r
+ //DevFS_DelDevice( giBGA_DriverId );\r
+ MM_UnmapHWPage( VBE_DISPI_LFB_PHYSICAL_ADDRESS, 768 );\r
+}\r
+\r
+/**\r
+ * \fn Uint64 BGA_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer)\r
+ * \brief Read from the framebuffer\r
+ */\r
+Uint64 BGA_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer)\r
+{\r
+ // Check Mode\r
+ if(giBGA_CurrentMode == -1) return -1;\r
+ \r
+ // Check Offset and Length against Framebuffer Size\r
+ if(off+len > BGA_MODES[giBGA_CurrentMode].fbSize)\r
+ return -1;\r
+ \r
+ // Copy from Framebuffer\r
+ memcpy(buffer, (void*)((Uint)gBGA_Framebuffer + (Uint)off), len);\r
+ return len;\r
+}\r
+\r
+/**\r
+ * \fn Uint64 BGA_Write(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer)\r
+ * \brief Write to the framebuffer\r
+ */\r
+Uint64 BGA_Write(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer)\r
+{\r
+ Uint8 *destBuf;\r
+ \r
+ DEBUGS("BGA_Write: (off=%i, len=0x%x)\n", off, len);\r
+ \r
+ // Check Mode\r
+ if(giBGA_CurrentMode == -1)\r
+ return -1;\r
+ // Check Input against Frambuffer Size\r
+ if(off+len > BGA_MODES[giBGA_CurrentMode].fbSize)\r
+ return -1;\r
+ \r
+ destBuf = (Uint8*) ((Uint)gBGA_Framebuffer + (Uint)off);\r
+ \r
+ DEBUGS(" BGA_Write: *buffer = 0x%x\n", *(Uint*)buffer);\r
+ DEBUGS(" BGA_Write: Updating Framebuffer (0x%x - 0x%x bytes)\n", \r
+ destBuf, destBuf + (Uint)len);\r
+ \r
+ \r
+ // Copy to Frambuffer\r
+ memcpy(destBuf, buffer, len);\r
+ \r
+ DEBUGS("BGA_Write: BGA Framebuffer updated\n");\r
+ \r
+ return len;\r
+}\r
+\r
+/**\r
+ * \fn INT int BGA_Ioctl(tVFS_Node *node, int id, void *data)\r
+ * \brief Handle messages to the device\r
+ */\r
+INT int BGA_Ioctl(tVFS_Node *node, int id, void *data)\r
+{\r
+ int ret = -2;\r
+ ENTER("pNode iId pData", node, id, data);\r
+ \r
+ switch(id)\r
+ {\r
+ case DRV_IOCTL_TYPE:\r
+ ret = DRV_TYPE_VIDEO;\r
+ break;\r
+ case DRV_IOCTL_IDENT:\r
+ memcpy(data, "BGA1", 4);\r
+ ret = 1;\r
+ break;\r
+ case DRV_IOCTL_VERSION:\r
+ ret = 0x100;\r
+ break;\r
+ case DRV_IOCTL_LOOKUP: // TODO: Implement\r
+ ret = 0;\r
+ break;\r
+ \r
+ case VIDEO_IOCTL_SETMODE:\r
+ ret = BGA_int_UpdateMode(*(int*)(data));\r
+ break;\r
+ \r
+ case VIDEO_IOCTL_GETMODE:\r
+ ret = giBGA_CurrentMode;\r
+ break;\r
+ \r
+ case VIDEO_IOCTL_FINDMODE:\r
+ ret = BGA_int_FindMode((tVideo_IOCtl_Mode*)data);\r
+ break;\r
+ \r
+ case VIDEO_IOCTL_MODEINFO:\r
+ ret = BGA_int_ModeInfo((tVideo_IOCtl_Mode*)data);\r
+ break;\r
+ \r
+ // Request Access to LFB\r
+ case VIDEO_IOCTL_REQLFB:\r
+ ret = BGA_int_MapFB( *(void**)data );\r
+ break;\r
+ \r
+ default:\r
+ LEAVE('i', -2);\r
+ return -2;\r
+ }\r
+ \r
+ LEAVE('i', ret);\r
+ return ret;\r
+}\r
+\r
+//== Internal Functions ==\r
+/**\r
+ * \fn void BGA_int_WriteRegister(Uint16 reg, Uint16 value)\r
+ * \brief Writes to a BGA register\r
+ */\r
+void BGA_int_WriteRegister(Uint16 reg, Uint16 value)\r
+{\r
+ outw(VBE_DISPI_IOPORT_INDEX, reg);\r
+ outw(VBE_DISPI_IOPORT_DATA, value);\r
+}\r
+\r
+INT Uint16 BGA_int_ReadRegister(Uint16 reg)\r
+{\r
+ outw(VBE_DISPI_IOPORT_INDEX, reg);\r
+ return inw(VBE_DISPI_IOPORT_DATA);\r
+}\r
+\r
+#if 0\r
+INT void BGA_int_SetBank(Uint16 bank)\r
+{\r
+ BGA_int_WriteRegister(VBE_DISPI_INDEX_BANK, bank);\r
+}\r
+#endif\r
+\r
+/**\r
+ * \fn void BGA_int_SetMode(Uint16 width, Uint16 height, Uint16 bpp)\r
+ * \brief Sets the video mode from the dimensions and bpp given\r
+ */\r
+void BGA_int_SetMode(Uint16 width, Uint16 height, Uint16 bpp)\r
+{\r
+ DEBUGS("BGA_int_SetMode: (width=%i, height=%i, bpp=%i)\n", width, height, bpp);\r
+ BGA_int_WriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED);\r
+ BGA_int_WriteRegister(VBE_DISPI_INDEX_XRES, width);\r
+ BGA_int_WriteRegister(VBE_DISPI_INDEX_YRES, height);\r
+ BGA_int_WriteRegister(VBE_DISPI_INDEX_BPP, bpp);\r
+ BGA_int_WriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED | VBE_DISPI_NOCLEARMEM | VBE_DISPI_LFB_ENABLED);\r
+ //BGA_int_WriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED | VBE_DISPI_NOCLEARMEM);\r
+}\r
+\r
+/**\r
+ * \fn int BGA_int_UpdateMode(int id)\r
+ * \brief Set current vide mode given a mode id\r
+ */\r
+int BGA_int_UpdateMode(int id)\r
+{\r
+ if(id < 0 || id >= BGA_MODE_COUNT) return -1;\r
+ BGA_int_SetMode(BGA_MODES[id].width, BGA_MODES[id].height, BGA_MODES[id].bpp);\r
+ giBGA_CurrentMode = id;\r
+ return id;\r
+}\r
+\r
+/**\r
+ * \fn int BGA_int_FindMode(tVideo_IOCtl_Mode *info)\r
+ * \brief Find a mode matching the given options\r
+ */\r
+int BGA_int_FindMode(tVideo_IOCtl_Mode *info)\r
+{\r
+ int i;\r
+ int best = -1, bestFactor = 1000;\r
+ int factor, tmp;\r
+ int rqdProduct = info->width * info->height * info->bpp;\r
+ \r
+ DEBUGS("BGA_int_FindMode: (info={width:%i,height:%i,bpp:%i})\n", info->width, info->height, info->bpp);\r
+ \r
+ for(i = 0; i < BGA_MODE_COUNT; i++)\r
+ {\r
+ #if DEBUG >= 2\r
+ LogF("Mode %i (%ix%ix%i), ", i, BGA_MODES[i].width, BGA_MODES[i].height, BGA_MODES[i].bpp);\r
+ #endif\r
+ \r
+ if(BGA_MODES[i].width == info->width\r
+ && BGA_MODES[i].height == info->height\r
+ && BGA_MODES[i].bpp == info->bpp)\r
+ {\r
+ #if DEBUG >= 2\r
+ LogF("Perfect!\n");\r
+ #endif\r
+ best = i;\r
+ break;\r
+ }\r
+ \r
+ tmp = BGA_MODES[i].width * BGA_MODES[i].height * BGA_MODES[i].bpp;\r
+ tmp -= rqdProduct;\r
+ tmp = tmp < 0 ? -tmp : tmp;\r
+ factor = tmp * 100 / rqdProduct;\r
+ \r
+ #if DEBUG >= 2\r
+ LogF("factor = %i\n", factor);\r
+ #endif\r
+ \r
+ if(factor < bestFactor)\r
+ {\r
+ bestFactor = factor;\r
+ best = i;\r
+ }\r
+ }\r
+ info->id = best;\r
+ info->width = BGA_MODES[best].width;\r
+ info->height = BGA_MODES[best].height;\r
+ info->bpp = BGA_MODES[best].bpp;\r
+ return best;\r
+}\r
+\r
+/**\r
+ * \fn int BGA_int_ModeInfo(tVideo_IOCtl_Mode *info)\r
+ * \brief Get mode information\r
+ */\r
+int BGA_int_ModeInfo(tVideo_IOCtl_Mode *info)\r
+{\r
+ if(!info) return -1;\r
+ if(MM_GetPhysAddr((Uint)info) == 0)\r
+ return -1;\r
+ \r
+ if(info->id < 0 || info->id >= BGA_MODE_COUNT) return -1;\r
+ \r
+ info->width = BGA_MODES[info->id].width;\r
+ info->height = BGA_MODES[info->id].height;\r
+ info->bpp = BGA_MODES[info->id].bpp;\r
+ \r
+ return 1;\r
+}\r
+\r
+/**\r
+ * \fn int BGA_int_MapFB(void *Dest)\r
+ * \brief Map the framebuffer into a process's space\r
+ * \param Dest User address to load to\r
+ */\r
+int BGA_int_MapFB(void *Dest)\r
+{\r
+ Uint i;\r
+ Uint pages;\r
+ \r
+ // Sanity Check\r
+ if((Uint)Dest > 0xC0000000) return 0;\r
+ if(BGA_MODES[giBGA_CurrentMode].bpp < 15) return 0; // Only non-pallete modes are supported\r
+ \r
+ // Count required pages\r
+ pages = (BGA_MODES[giBGA_CurrentMode].fbSize + 0xFFF) >> 12;\r
+ \r
+ // Check if there is space\r
+ for( i = 0; i < pages; i++ )\r
+ {\r
+ if(MM_GetPhysAddr( (Uint)Dest + (i << 12) ))\r
+ return 0;\r
+ }\r
+ \r
+ // Map\r
+ for( i = 0; i < pages; i++ )\r
+ MM_Map( (Uint)Dest + (i<<12), VBE_DISPI_LFB_PHYSICAL_ADDRESS + (i<<12) );\r
+ \r
+ return 1;\r
+}\r
--- /dev/null
+/*
+ * Acess2
+ * PS2 Keyboard Driver
+ */
+#include <common.h>
+#include <modules.h>
+#include <fs_devfs.h>
+#include <tpl_drv_common.h>
+#include <tpl_drv_keyboard.h>
+#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;
+ }
+}
--- /dev/null
+\r
+#ifndef _KBDUS_H\r
+#define _KBDUS_H\r
+\r
+// - BASE (NO PREFIX)\r
+Uint8 gpKBDUS1[256] = {\r
+ // 00\r
+ 0, KEY_ESC, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', '\t',\r
+ // 10\r
+ 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', KEY_LCTRL, 'a', 's',\r
+ // 20\r
+ 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';','\'', '`', KEY_LSHIFT,'\\', 'z', 'x', 'c', 'v',\r
+ // 30\r
+ 'b','n','m',',','.','/',KEY_RSHIFT,KEY_KPSTAR,KEY_LALT,' ',KEY_CAPSLOCK,KEY_F1,KEY_F2,KEY_F3,KEY_F4, KEY_F5,\r
+ // 40\r
+ 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*/\r
+ // 50\r
+ KEY_KPDOWN, KEY_KPPGDN, KEY_KPINS, KEY_KPDEL, 0, 0, 0, KEY_F11, KEY_F12, 0\r
+};\r
+// - 0xE0 Prefixed\r
+Uint8 gpKBDUS2[256] = {\r
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F\r
+/*00*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0-F\r
+/*10*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_KPENTER, KEY_RCTRL, 0, 0,\r
+/*20*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\r
+/*30*/ 0, 0, 0, 0, 0, KEY_KPSLASH, 0, 0, KEY_RALT, 0, 0, 0, 0, 0, 0, 0,\r
+/*40*/ 0, 0, 0, 0, 0, 0, 0, KEY_HOME, KEY_UP, KEY_PGUP, 0, KEY_LEFT, 0, KEY_RIGHT, 0, KEY_END,\r
+/*50*/ KEY_DOWN, KEY_PGDOWN, KEY_INS, KEY_DEL, 0, 0, 0, 0, 0, 0, KEY_WIN, 0, 0, KEY_MENU\r
+};\r
+Uint8 gpKBDUS3[256] = {\r
+// 0 1 2 3 4 5 6 7 8 9 A B C D E F\r
+/*00*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0-F\r
+/*10*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_PAUSE\r
+};\r
+\r
+\r
+Uint8 *gpKBDUS[3] = { gpKBDUS1, gpKBDUS2, gpKBDUS3 };\r
+\r
+#endif\r
--- /dev/null
+/*\r
+AcessOS/AcessBasic v0.1\r
+PCI Bus Driver\r
+*/\r
+#include <common.h>\r
+#include <vfs.h>\r
+#include <fs_devfs.h>\r
+#include <drv_pci.h>\r
+\r
+#define DEBUG 0\r
+#define LIST_DEVICES 1\r
+\r
+// === STRUCTURES ===\r
+typedef struct s_pciDevice {\r
+ Uint16 bus, slot, fcn;\r
+ Uint16 vendor, device;\r
+ union {\r
+ struct {Uint8 class, subclass;};\r
+ Uint16 oc;\r
+ };\r
+ Uint16 revision;\r
+ Uint32 ConfigCache[256/4];\r
+ char Name[8];\r
+ tVFS_Node Node;\r
+} t_pciDevice;\r
+\r
+// === CONSTANTS ===\r
+#define SPACE_STEP 5\r
+#define MAX_RESERVED_PORT 0xD00\r
+\r
+// === PROTOTYPES ===\r
+ int PCI_Install(char **Arguments);\r
+char *PCI_ReadDirRoot(tVFS_Node *node, int pos);\r
+tVFS_Node *PCI_FindDirRoot(tVFS_Node *node, char *filename);\r
+Uint64 PCI_ReadDevice(tVFS_Node *node, Uint64 pos, Uint64 length, void *buffer);\r
+ \r
+ int PCI_CountDevices(Uint16 vendor, Uint16 device, Uint16 fcn);\r
+ int PCI_GetDevice(Uint16 vendor, Uint16 device, Uint16 fcn, int idx);\r
+ int PCI_GetDeviceByClass(Uint16 class, Uint16 mask, int prev);\r
+Uint8 PCI_GetIRQ(int id);\r
+Uint32 PCI_GetBAR0(int id);\r
+Uint32 PCI_GetBAR1(int id);\r
+Uint32 PCI_GetBAR3(int id);\r
+Uint32 PCI_GetBAR4(int id);\r
+Uint32 PCI_GetBAR5(int id);\r
+Uint16 PCI_AssignPort(int id, int bar, int count);\r
+\r
+ int PCI_EnumDevice(Uint16 bus, Uint16 dev, Uint16 fcn, t_pciDevice *info);\r
+Uint32 PCI_CfgReadDWord(Uint16 bus, Uint16 dev, Uint16 func, Uint16 offset);\r
+void PCI_CfgWriteDWord(Uint16 bus, Uint16 dev, Uint16 func, Uint16 offset, Uint32 data);\r
+Uint16 PCI_CfgReadWord(Uint16 bus, Uint16 dev, Uint16 func, Uint16 offset);\r
+Uint8 PCI_CfgReadByte(Uint16 bus, Uint16 dev, Uint16 func, Uint16 offset);\r
+\r
+// === GLOBALS ===\r
+//MODULE_DEFINE(0, 0x0100, PCI, PCI_Install, NULL);\r
+ int giPCI_BusCount = 1;\r
+ int giPCI_InodeHandle = -1;\r
+ int giPCI_DeviceCount = 0;\r
+t_pciDevice *gPCI_Devices = NULL;\r
+tDevFS_Driver gPCI_DriverStruct = {\r
+ NULL, "pci",\r
+ {\r
+ .Flags = VFS_FFLAG_DIRECTORY,\r
+ .NumACLs = 1,\r
+ .ACLs = &gVFS_ACL_EveryoneRX,\r
+ .ReadDir = PCI_ReadDirRoot,\r
+ .FindDir = PCI_FindDirRoot\r
+ }\r
+};\r
+ Uint32 *gaPCI_PortBitmap = NULL;\r
+ \r
+// === CODE ===\r
+/**\r
+ * \fn int PCI_Install()\r
+ * \brief Scan the PCI Bus for devices\r
+ */\r
+int PCI_Install(char **Arguments)\r
+{\r
+ int bus, dev, fcn, i;\r
+ int space = 0;\r
+ t_pciDevice devInfo;\r
+ void *tmpPtr = NULL;\r
+ \r
+ // Build Portmap\r
+ gaPCI_PortBitmap = malloc( 1 << 13 );\r
+ memset( gaPCI_PortBitmap, 0, 1 << 13 );\r
+ for( i = 0; i < MAX_RESERVED_PORT / 32; i ++ )\r
+ gaPCI_PortBitmap[i] = -1;\r
+ for( i = 0; i < MAX_RESERVED_PORT % 32; i ++ )\r
+ gaPCI_PortBitmap[MAX_RESERVED_PORT / 32] = 1 << i;\r
+ //LogF("Done.\n");\r
+ \r
+ // Scan Busses\r
+ for( bus = 0; bus < giPCI_BusCount; bus++ )\r
+ {\r
+ for( dev = 0; dev < 10; dev++ ) // 10 Devices per bus\r
+ {\r
+ for( fcn = 0; fcn < 8; fcn++ ) // 8 functions per device\r
+ {\r
+ // Check if the device/function exists\r
+ if(!PCI_EnumDevice(bus, dev, fcn, &devInfo))\r
+ {\r
+ continue;\r
+ }\r
+ \r
+ if(giPCI_DeviceCount == space)\r
+ {\r
+ space += SPACE_STEP;\r
+ tmpPtr = realloc(gPCI_Devices, space*sizeof(t_pciDevice));\r
+ if(tmpPtr == NULL)\r
+ break;\r
+ gPCI_Devices = tmpPtr;\r
+ }\r
+ if(devInfo.oc == PCI_OC_PCIBRIDGE)\r
+ {\r
+ #if LIST_DEVICES\r
+ Log("[PCI ] Bridge @ %i,%i:%i (0x%x:0x%x)",\r
+ bus, dev, fcn, devInfo.vendor, devInfo.device);\r
+ #endif\r
+ giPCI_BusCount++;\r
+ }\r
+ devInfo.Node.Inode = giPCI_DeviceCount;\r
+ memcpy(&gPCI_Devices[giPCI_DeviceCount], &devInfo, sizeof(t_pciDevice));\r
+ giPCI_DeviceCount ++;\r
+ #if LIST_DEVICES\r
+ Log("[PCI ] Device %i,%i:%i => 0x%x:0x%x",\r
+ bus, dev, fcn, devInfo.vendor, devInfo.device);\r
+ #endif\r
+ \r
+ // WTF is this for?\r
+ if(fcn == 0) {\r
+ if( !(devInfo.ConfigCache[3] & 0x800000) )\r
+ break;\r
+ }\r
+ }\r
+ if(tmpPtr != gPCI_Devices)\r
+ break;\r
+ }\r
+ if(tmpPtr != gPCI_Devices)\r
+ break;\r
+ }\r
+ tmpPtr = realloc(gPCI_Devices, giPCI_DeviceCount*sizeof(t_pciDevice));\r
+ if(tmpPtr == NULL)\r
+ return 0;\r
+ gPCI_Devices = tmpPtr;\r
+ //LogF("Done.\n");\r
+ \r
+ // Complete Driver Structure \r
+ gPCI_DriverStruct.RootNode.Size = giPCI_DeviceCount;\r
+ \r
+ // And add to DevFS\r
+ DevFS_AddDevice(&gPCI_DriverStruct);\r
+ \r
+ return 1;\r
+}\r
+\r
+/**\r
+ * \fn char *PCI_ReadDirRoot(tVFS_Node *node, int pos)\r
+ * \brief Read from Root of PCI Driver\r
+*/\r
+char *PCI_ReadDirRoot(tVFS_Node *node, int pos)\r
+{ \r
+ if(pos < 0 || pos >= giPCI_DeviceCount)\r
+ return NULL;\r
+ \r
+ return gPCI_Devices[pos].Name;\r
+}\r
+/**\r
+ * \fn tVFS_Node *PCI_FindDirRoot(tVFS_Node *node, char *filename)\r
+ */\r
+tVFS_Node *PCI_FindDirRoot(tVFS_Node *node, char *filename)\r
+{\r
+ int bus,slot,fcn;\r
+ int i;\r
+ // Validate Filename (Pointer and length)\r
+ if(!filename || strlen(filename) != 7)\r
+ return NULL;\r
+ // Check for spacers\r
+ if(filename[2] != '.' || filename[5] != ':')\r
+ return NULL;\r
+ \r
+ // Get Information\r
+ if(filename[0] < '0' || filename[0] > '9') return NULL;\r
+ bus = (filename[0] - '0')*10;\r
+ if(filename[1] < '0' || filename[1] > '9') return NULL;\r
+ bus += filename[1] - '0';\r
+ if(filename[3] < '0' || filename[3] > '9') return NULL;\r
+ slot = (filename[3] - '0')*10;\r
+ if(filename[4] < '0' || filename[4] > '9') return NULL;\r
+ slot += filename[4] - '0';\r
+ if(filename[6] < '0' || filename[6] > '9') return NULL;\r
+ fcn = filename[6] - '0';\r
+ \r
+ // Find Match\r
+ for(i=0;i<giPCI_DeviceCount;i++)\r
+ {\r
+ if(gPCI_Devices[i].bus != bus) continue;\r
+ if(gPCI_Devices[i].slot != slot) continue;\r
+ if(gPCI_Devices[i].fcn != fcn) continue;\r
+ \r
+ return &gPCI_Devices[i].Node;\r
+ }\r
+ \r
+ // Error Return\r
+ return NULL;\r
+}\r
+\r
+/**\r
+ * \fn Uint64 PCI_ReadDevice(tVFS_Node *node, Uint64 pos, Uint64 length, void *buffer)\r
+ */\r
+Uint64 PCI_ReadDevice(tVFS_Node *node, Uint64 pos, Uint64 length, void *buffer)\r
+{ \r
+ if( pos + length > 256 ) return 0;\r
+ \r
+ memcpy(\r
+ buffer,\r
+ (char*)gPCI_Devices[node->Inode].ConfigCache + pos,\r
+ length);\r
+ \r
+ return length;\r
+}\r
+\r
+// --- Kernel Code Interface ---\r
+/**\r
+ \fn int PCI_CountDevices(Uint16 vendor, Uint16 device, Uint16 fcn)\r
+ \brief Counts the devices with the specified codes\r
+ \param vendor Vendor ID\r
+ \param device Device ID\r
+ \param fcn Function ID\r
+*/\r
+int PCI_CountDevices(Uint16 vendor, Uint16 device, Uint16 fcn)\r
+{\r
+ int i, ret=0;\r
+ for(i=0;i<giPCI_DeviceCount;i++)\r
+ {\r
+ if(gPCI_Devices[i].vendor != vendor) continue;\r
+ if(gPCI_Devices[i].device != device) continue;\r
+ if(gPCI_Devices[i].fcn != fcn) continue;\r
+ ret ++;\r
+ }\r
+ return ret;\r
+}\r
+\r
+/**\r
+ \fn int PCI_GetDevice(Uint16 vendor, Uint16 device, Uint16 fcn, int idx)\r
+ \brief Gets the ID of the specified PCI device\r
+ \param vendor Vendor ID\r
+ \param device Device ID\r
+ \param fcn Function IDs\r
+ \param idx Number of matching entry wanted\r
+*/\r
+int PCI_GetDevice(Uint16 vendor, Uint16 device, Uint16 fcn, int idx)\r
+{\r
+ int i, j=0;\r
+ for(i=0;i<giPCI_DeviceCount;i++)\r
+ {\r
+ if(gPCI_Devices[i].vendor != vendor) continue;\r
+ if(gPCI_Devices[i].device != device) continue;\r
+ if(gPCI_Devices[i].fcn != fcn) continue;\r
+ if(j == idx) return i;\r
+ j ++;\r
+ }\r
+ return -1;\r
+}\r
+\r
+/**\r
+ * \fn int PCI_GetDeviceByClass(Uint16 class, Uint16 mask, int prev)\r
+ * \brief Gets the ID of a device by it's class code\r
+ * \param class Class Code\r
+ * \param mask Mask for class comparison\r
+ * \param prev ID of previous device (-1 for no previous)\r
+ */\r
+int PCI_GetDeviceByClass(Uint16 class, Uint16 mask, int prev)\r
+{\r
+ int i;\r
+ // Check if prev is negative (meaning get first)\r
+ if(prev < 0) i = 0;\r
+ else i = prev+1;\r
+ \r
+ for( ; i < giPCI_DeviceCount; i++ )\r
+ {\r
+ if((gPCI_Devices[i].oc & mask) != class) continue;\r
+ return i;\r
+ }\r
+ return -1;\r
+}\r
+\r
+/**\r
+ \fn Uint8 PCI_GetIRQ(int id)\r
+*/\r
+Uint8 PCI_GetIRQ(int id)\r
+{\r
+ if(id < 0 || id >= giPCI_DeviceCount)\r
+ return 0;\r
+ return gPCI_Devices[id].ConfigCache[15];\r
+ //return PCI_CfgReadByte( gPCI_Devices[id].bus, gPCI_Devices[id].slot, gPCI_Devices[id].fcn, 0x3C);\r
+}\r
+\r
+/**\r
+ \fn Uint32 PCI_GetBAR0(int id)\r
+*/\r
+Uint32 PCI_GetBAR0(int id)\r
+{\r
+ if(id < 0 || id >= giPCI_DeviceCount)\r
+ return 0;\r
+ return gPCI_Devices[id].ConfigCache[4];\r
+}\r
+\r
+/**\r
+ \fn Uint32 PCI_GetBAR1(int id)\r
+*/\r
+Uint32 PCI_GetBAR1(int id)\r
+{\r
+ if(id < 0 || id >= giPCI_DeviceCount)\r
+ return 0;\r
+ return gPCI_Devices[id].ConfigCache[5];\r
+}\r
+\r
+/**\r
+ \fn Uint32 PCI_GetBAR2(int id)\r
+*/\r
+Uint32 PCI_GetBAR2(int id)\r
+{\r
+ if(id < 0 || id >= giPCI_DeviceCount)\r
+ return 0;\r
+ return gPCI_Devices[id].ConfigCache[6];\r
+}\r
+\r
+/**\r
+ \fn Uint32 PCI_GetBAR3(int id)\r
+*/\r
+Uint32 PCI_GetBAR3(int id)\r
+{\r
+ if(id < 0 || id >= giPCI_DeviceCount)\r
+ return 0;\r
+ return gPCI_Devices[id].ConfigCache[7];\r
+}\r
+\r
+/**\r
+ \fn Uint32 PCI_GetBAR4(int id)\r
+*/\r
+Uint32 PCI_GetBAR4(int id)\r
+{\r
+ if(id < 0 || id >= giPCI_DeviceCount)\r
+ return 0;\r
+ return gPCI_Devices[id].ConfigCache[8];\r
+}\r
+\r
+/**\r
+ \fn Uint32 PCI_GetBAR5(int id)\r
+*/\r
+Uint32 PCI_GetBAR5(int id)\r
+{\r
+ if(id < 0 || id >= giPCI_DeviceCount)\r
+ return 0;\r
+ return gPCI_Devices[id].ConfigCache[9];\r
+}\r
+\r
+Uint16 PCI_AssignPort(int id, int bar, int count)\r
+{\r
+ Uint16 portVals;\r
+ int gran=0;\r
+ int i, j;\r
+ t_pciDevice *dev;\r
+ \r
+ //LogF("PCI_AssignPort: (id=%i,bar=%i,count=%i)\n", id, bar, count);\r
+ \r
+ if(id < 0 || id >= giPCI_DeviceCount) return 0;\r
+ if(bar < 0 || bar > 5) return 0;\r
+ \r
+ dev = &gPCI_Devices[id];\r
+ \r
+ PCI_CfgWriteDWord( dev->bus, dev->slot, dev->fcn, 0x10+bar*4, -1 );\r
+ portVals = PCI_CfgReadDWord( dev->bus, dev->slot, dev->fcn, 0x10+bar*4 );\r
+ dev->ConfigCache[4+bar] = portVals;\r
+ //LogF(" PCI_AssignPort: portVals = 0x%x\n", portVals);\r
+ \r
+ // Check for IO port\r
+ if( !(portVals & 1) ) return 0;\r
+ \r
+ // Mask out final bit\r
+ portVals &= ~1;\r
+ \r
+ // Get Granuality\r
+ __asm__ __volatile__ ("bsf %%eax, %%ecx" : "=c" (gran) : "a" (portVals) );\r
+ gran = 1 << gran;\r
+ //LogF(" PCI_AssignPort: gran = 0x%x\n", gran);\r
+ \r
+ // Find free space\r
+ portVals = 0;\r
+ for( i = 0; i < 1<<16; i += gran )\r
+ {\r
+ for( j = 0; j < count; j ++ )\r
+ {\r
+ if( gaPCI_PortBitmap[ (i+j)>>5 ] & 1 << ((i+j)&0x1F) )\r
+ break;\r
+ }\r
+ if(j == count) {\r
+ portVals = i;\r
+ break;\r
+ }\r
+ }\r
+ \r
+ if(portVals)\r
+ {\r
+ for( j = 0; j < count; j ++ )\r
+ {\r
+ if( gaPCI_PortBitmap[ (portVals+j)>>5 ] |= 1 << ((portVals+j)&0x1F) )\r
+ break;\r
+ }\r
+ PCI_CfgWriteDWord( dev->bus, dev->slot, dev->fcn, 0x10+bar*4, portVals|1 );\r
+ dev->ConfigCache[4+bar] = portVals|1;\r
+ }\r
+ \r
+ // Return\r
+ //LogF("PCI_AssignPort: RETURN 0x%x\n", portVals);\r
+ return portVals;\r
+}\r
+\r
+/**\r
+ * \fn int PCI_EnumDevice(Uint16 bus, Uint16 slot, Uint16 fcn, t_pciDevice *info)\r
+ */\r
+int PCI_EnumDevice(Uint16 bus, Uint16 slot, Uint16 fcn, t_pciDevice *info)\r
+{\r
+ Uint16 vendor;\r
+ int i;\r
+ Uint32 addr;\r
+ \r
+ vendor = PCI_CfgReadWord(bus, slot, fcn, 0x0|0);\r
+ if(vendor == 0xFFFF) // Invalid Device\r
+ return 0;\r
+ \r
+ info->bus = bus;\r
+ info->slot = slot;\r
+ info->fcn = fcn;\r
+ info->vendor = vendor;\r
+ info->device = PCI_CfgReadWord(bus, slot, fcn, 0x0|2);\r
+ info->revision = PCI_CfgReadWord(bus, slot, fcn, 0x8|0);\r
+ info->oc = PCI_CfgReadWord(bus, slot, fcn, 0x8|2);\r
+ \r
+ // Load Config Bytes\r
+ addr = 0x80000000 | ((Uint)bus<<16) | ((Uint)slot<<11) | ((Uint)fcn<<8);\r
+ for(i=0;i<256/4;i++)\r
+ {\r
+ #if 1\r
+ outd(0xCF8, addr);\r
+ info->ConfigCache[i] = ind(0xCFC);\r
+ addr += 4;\r
+ #else\r
+ info->ConfigCache[i] = PCI_CfgReadDWord(bus, slot, fcn, i*4);\r
+ #endif\r
+ }\r
+ \r
+ //#if LIST_DEVICES\r
+ //Log("BAR0 0x%08x BAR1 0x%08x BAR2 0x%08x", info->ConfigCache[4], info->ConfigCache[5], info->ConfigCache[6]);\r
+ //Log("BAR3 0x%08x BAR4 0x%08x BAR5 0x%08x", info->ConfigCache[7], info->ConfigCache[8], info->ConfigCache[9]);\r
+ //Log("Class: 0x%04x", info->oc);\r
+ //#endif\r
+ \r
+ // Make node name\r
+ info->Name[0] = '0' + bus/10;\r
+ info->Name[1] = '0' + bus%10;\r
+ info->Name[2] = '.';\r
+ info->Name[3] = '0' + slot/10;\r
+ info->Name[4] = '0' + slot%10;\r
+ info->Name[5] = '.';\r
+ info->Name[6] = '0' + fcn;\r
+ info->Name[7] = '\0';\r
+ \r
+ // Create VFS Node\r
+ memset( &info->Node, 0, sizeof(tVFS_Node) );\r
+ info->Node.Size = 256;\r
+ \r
+ info->Node.NumACLs = 1;\r
+ info->Node.ACLs = &gVFS_ACL_EveryoneRO;\r
+ \r
+ info->Node.Read = PCI_ReadDevice;\r
+ \r
+ return 1;\r
+}\r
+\r
+Uint32 PCI_CfgReadDWord(Uint16 bus, Uint16 dev, Uint16 func, Uint16 offset)\r
+{\r
+ Uint32 address;\r
+ Uint32 data;\r
+ \r
+ bus &= 0xFF; // 8 Bits\r
+ dev &= 0x1F; // 5 Bits\r
+ func &= 0x7; // 3 Bits\r
+ offset &= 0xFF; // 8 Bits\r
+ \r
+ address = 0x80000000 | ((Uint)bus<<16) | ((Uint)dev<<11) | ((Uint)func<<8) | (offset&0xFC);\r
+ outd(0xCF8, address);\r
+ \r
+ data = ind(0xCFC);\r
+ return (Uint32)data;\r
+}\r
+void PCI_CfgWriteDWord(Uint16 bus, Uint16 dev, Uint16 func, Uint16 offset, Uint32 data)\r
+{\r
+ Uint32 address;\r
+ \r
+ bus &= 0xFF; // 8 Bits\r
+ dev &= 0x1F; // 5 Bits\r
+ func &= 0x7; // 3 Bits\r
+ offset &= 0xFF; // 8 Bits\r
+ \r
+ address = 0x80000000 | ((Uint)bus<<16) | ((Uint)dev<<11) | ((Uint)func<<8) | (offset&0xFC);\r
+ outd(0xCF8, address);\r
+ outd(0xCFC, data);\r
+}\r
+Uint16 PCI_CfgReadWord(Uint16 bus, Uint16 dev, Uint16 func, Uint16 offset)\r
+{\r
+ Uint32 data;\r
+ \r
+ bus &= 0xFF; // 8 Bits\r
+ dev &= 0x1F; // 5 Bits\r
+ func &= 0x7; // 3 Bits\r
+ offset &= 0xFF; // 8 Bits\r
+ \r
+ //LogF("PCI_CfgReadWord: (bus=0x%x,dev=0x%x,func=%x,offset=0x%x)\n", bus, dev, func, offset);\r
+ \r
+ outd(0xCF8,\r
+ 0x80000000 | ((Uint)bus<<16) | ((Uint)dev<<11) | ((Uint)func<<8) | (offset&0xFC) );\r
+ \r
+ data = ind(0xCFC);\r
+ data >>= (offset&2)*8; //Allow Access to Upper Word\r
+ //LogF("PCI_CfgReadWord: RETURN 0x%x\n", data&0xFFFF);\r
+ return (Uint16)data;\r
+}\r
+\r
+Uint8 PCI_CfgReadByte(Uint16 bus, Uint16 dev, Uint16 func, Uint16 offset)\r
+{\r
+ Uint32 address;\r
+ Uint32 data;\r
+ \r
+ bus &= 0xFF; // 8 Bits\r
+ dev &= 0x1F; // 4 Bits\r
+ func &= 0x7; // 3 Bits\r
+ offset &= 0xFF; // 8 Bits\r
+ \r
+ address = 0x80000000 | ((Uint)bus<<16) | ((Uint)dev<<11) | ((Uint)func<<8) | (offset&0xFC);\r
+ outd(0xCF8, address);\r
+ \r
+ data = ind(0xCFC);\r
+ data >>= (offset&3)*8; //Allow Access to Upper Word\r
+ return (Uint8)data;\r
+}\r
+\r
+\r
+// === EXPORTS ===\r
+/*\r
+EXPORT(PCI_CountDevices);\r
+EXPORT(PCI_GetDevice);\r
+EXPORT(PCI_AssignPort);\r
+EXPORT(PCI_GetIRQ);\r
+*/\r
--- /dev/null
+/*
+ * Acess2 VGA Controller Driver
+ */
+#include <common.h>
+#include <fs_devfs.h>
+#include <tpl_drv_video.h>
+#include <modules.h>
+
+// === 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;
+}
--- /dev/null
+/*
+ * Acess2 Virtual Terminal Driver
+ */
+#include <common.h>
+#include <fs_devfs.h>
+#include <modules.h>
+#include <tpl_drv_video.h>
+
+// === 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];
+}
--- /dev/null
+/*
+ * 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
+};
--- /dev/null
+/*
+ * 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
+};
--- /dev/null
+/*
+ * AcessOS Microkernel Version
+ * heap.c
+ */
+#include <common.h>
+#include <mm_virt.h>
+#include <heap.h>
+
+#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
--- /dev/null
+/*
+ */
+#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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * AcessOS Microkernel Version
+ * common.h
+ */
+#ifndef _COMMON_H
+#define _COMMON_H
+
+#define NULL ((void*)0)
+
+#include <arch.h>
+#include <stdarg.h>
+
+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 <binary_ext.h>
+#include <vfs_ext.h>
+
+#endif
--- /dev/null
+/*\r
+ * Acess 2\r
+ * PCI Bus Driver\r
+ * drv_pci.h\r
+ */\r
+#ifndef _DRV_PCI_H\r
+#define _DRV_PCI_H\r
+\r
+enum e_PciClasses {\r
+ PCI_CLASS_PRE20 = 0x00,\r
+ PCI_CLASS_STORAGE,\r
+ PCI_CLASS_NETWORK,\r
+ PCI_CLASS_DISPLAY,\r
+ PCI_CLASS_MULTIMEDIA,\r
+ PCI_CLASS_MEMORY,\r
+ PCI_CLASS_BRIDGE,\r
+ PCI_CLASS_COMM,\r
+ PCI_CLASS_PREPH,\r
+ PCI_CLASS_INPUT,\r
+ PCI_CLASS_DOCKING,\r
+ PCI_CLASS_PROCESSORS,\r
+ PCI_CLASS_SERIALBUS,\r
+ PCI_CLASS_MISC = 0xFF\r
+};\r
+enum e_PciOverClasses {\r
+ PCI_OC_PCIBRIDGE = 0x0604,\r
+ PCI_OC_SCSI = 0x0100\r
+};\r
+\r
+\r
+extern int PCI_CountDevices(Uint16 vendor, Uint16 device, Uint16 fcn);\r
+extern int PCI_GetDevice(Uint16 vendor, Uint16 device, Uint16 fcn, int idx);\r
+extern int PCI_GetDeviceByClass(Uint16 class, Uint16 mask, int prev);\r
+extern Uint8 PCI_GetIRQ(int id);\r
+extern Uint32 PCI_GetBAR0(int id);\r
+extern Uint32 PCI_GetBAR1(int id);\r
+extern Uint32 PCI_GetBAR3(int id);\r
+extern Uint32 PCI_GetBAR4(int id);\r
+extern Uint32 PCI_GetBAR5(int id);\r
+\r
+#endif\r
--- /dev/null
+/*
+ * AcessOS Microkernel Version
+ * errno.h
+ */
+#ifndef _ERRNO_H
+#define _ERRNO_H
+
+enum eErrorNums {
+ EOK,
+ ENOSYS,
+ EINVAL,
+ ENOMEM,
+ EACCES
+};
+
+#endif
--- /dev/null
+/*
+ * Acess 2
+ * Device Filesystem (DevFS)
+ * - vfs/fs/devfs.c
+ */
+#ifndef _FS_DEVFS_H
+#define _FS_DEVFS_H
+#include <vfs.h>
+
+// === 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+; 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/**
+ 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
--- /dev/null
+/**\r
+ * \file tpl_drv_keyboard.h\r
+ * \brief Keyboard Driver Interface Definitions\r
+*/\r
+#ifndef _TPL_KEYBOARD_H\r
+#define _TPL_KEYBOARD_H\r
+\r
+#include <tpl_drv_common.h>\r
+\r
+/**\r
+ * \enum eTplKeyboard_IOCtl\r
+ * \brief Common Keyboard IOCtl Calls\r
+ * \extends eTplDrv_IOCtl\r
+ */\r
+enum eTplKeyboard_IOCtl {\r
+ //! \brief Get/Set Repeat Rate - (int Rate)\r
+ KB_IOCTL_REPEATRATE = 4,\r
+ //! \brief Get/Set Repeat Delay - (int Delay)\r
+ KB_IOCTL_REPEATDELAY,\r
+ //! \brief Sets the callback\r
+ KB_IOCTL_SETCALLBACK\r
+};\r
+\r
+typedef void (*tKeybardCallback)(Uint32);\r
+\r
+enum {\r
+ KEY_ESC = 0x1B,\r
+ \r
+ KEY_NP_MASK = 0x80, //End of ASCII Range\r
+ KEY_LCTRL, KEY_RCTRL,\r
+ KEY_LALT, KEY_RALT,\r
+ KEY_LSHIFT, KEY_RSHIFT,\r
+ KEY_CAPSLOCK,\r
+ KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT,\r
+ KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, \r
+ KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12,\r
+ KEY_NUMLOCK, KEY_SCROLLLOCK,\r
+ KEY_HOME, KEY_END, KEY_INS, KEY_DEL,\r
+ KEY_PAUSE, KEY_BREAK,\r
+ KEY_PGUP, KEY_PGDOWN,\r
+ KEY_KPENTER, KEY_KPSLASH, KEY_KPMINUS, KEY_KPPLUS, KEY_KPSTAR,\r
+ KEY_KPHOME, KEY_KPUP, KEY_KPPGUP, KEY_KPLEFT, KEY_KP5, KEY_KPRIGHT,\r
+ KEY_KPEND, KEY_KPDOWN, KEY_KPPGDN, KEY_KPINS, KEY_KPDEL,\r
+ KEY_WIN, KEY_MENU,\r
+ \r
+ KEY_KEYUP = 0xFF\r
+};\r
+\r
+\r
+#endif\r
--- /dev/null
+/**\r
+ * \file tpl_drv_video.h\r
+ * \brief Video Driver Interface Definitions\r
+ * \note For AcessOS Version 1\r
+ * \r
+ * Video drivers extend the common driver interface tpl_drv_common.h\r
+ * and must support _at least_ the IOCtl numbers defined in this file\r
+ * to be compatable with Acess.\r
+ * \r
+ * \section IOCtls\r
+ * As said, a compatable driver must implement these calls correctly,\r
+ * but they may choose not to allow direct user access to the framebuffer.\r
+ * \r
+ * \section Screen Contents\r
+ * Reads and writes to the driver's file while in component colour modes\r
+ * must correspond to a change of the contents of the screen. The framebuffer\r
+ * must start at offset 0 in the file.\r
+ * In pallete colour modes the LFB is preceded by a 1024 byte pallete (allowing\r
+ * room for 256 entries of 32-bits each)\r
+*/\r
+#ifndef _TPL_VIDEO_H\r
+#define _TPL_VIDEO_H\r
+\r
+#include <tpl_drv_common.h>\r
+\r
+/**\r
+ * \enum eTplVideo_IOCtl\r
+ * \brief Common Video IOCtl Calls\r
+ * \extends eTplDrv_IOCtl\r
+ */\r
+enum eTplVideo_IOCtl {\r
+ //! \brief Set Mode - (int mode)\r
+ VIDEO_IOCTL_SETMODE = 4,\r
+ //! \brief Get Mode - (int *mode)\r
+ VIDEO_IOCTL_GETMODE,\r
+ //! \brief Find a matching mode - (tVideo_IOCtl_Mode *info)\r
+ VIDEO_IOCTL_FINDMODE,\r
+ //! \brief Get mode info - (tVideo_IOCtl_Mode *info)\r
+ VIDEO_IOCTL_MODEINFO,\r
+ //! \brief Request access to Framebuffer - (void *dest), Return Boolean Success\r
+ VIDEO_IOCTL_REQLFB\r
+};\r
+\r
+/**\r
+ \struct sVideo_IOCtl_Mode\r
+ \brief Mode Structure used in IOCtl Calls\r
+*/\r
+struct sVideo_IOCtl_Mode {\r
+ short id; //!< Mide ID\r
+ Uint16 width; //!< Width\r
+ Uint16 height; //!< Height\r
+ Uint16 bpp; //!< Bits per Pixel\r
+};\r
+typedef struct sVideo_IOCtl_Mode tVideo_IOCtl_Mode; //!< Mode Type\r
+\r
+/**\r
+ * \struct sVT_Char\r
+ * \brief Virtual Terminal Representation of a character\r
+ */\r
+struct sVT_Char {\r
+ Uint32 Ch;\r
+ union {\r
+ struct {\r
+ Uint16 BGCol;\r
+ Uint16 FGCol;\r
+ };\r
+ Uint32 Colour;\r
+ };\r
+};\r
+typedef struct sVT_Char tVT_Char;\r
+\r
+#define VT_COL_BLACK 0x0000\r
+#define VT_COL_GREY 0x0888\r
+#define VT_COL_LTGREY 0x0CCC\r
+#define VT_COL_WHITE 0x0FFF\r
+\r
+#endif\r
--- /dev/null
+/*
+ * Acess Micro - VFS Server Ver 1
+ */
+#ifndef _VFS_H
+#define _VFS_H
+
+#include <common.h>
+
+#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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * AcessMicro VFS
+ * - RAM Filesystem Coommon Structures
+ */
+#ifndef _RAMFS_H
+#define _RAMFS_H
+#include <vfs.h>
+
+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
--- /dev/null
+/*
+ * Acess2
+ * Common Library Functions
+ */
+#include <common.h>
+
+// === 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);
--- /dev/null
+/*
+ * AcessOS Microkernel Version
+ * messages.c
+ */
+#include <common.h>
+#include <proc.h>
+#include <errno.h>
+
+// === 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;
+}
--- /dev/null
+/*
+ * Acess2
+ * - Module Loader
+ */
+#include <common.h>
+#include <modules.h>
+
+// === 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;
+}
--- /dev/null
+/*
+ * AcessOS Microkernel Version
+ * proc.c
+ */
+#include <common.h>
+#include <proc.h>
+#include <mm_virt.h>
+#include <errno.h>
+#include <mp.h>
+
+// === 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<giNumCPUs;pos++)
+ {
+ gTSSs[pos].SS0 = 0x10;
+ gTSSs[pos].ESP0 = 0; // Set properly by scheduler
+ gGDT[9+pos].LimitLow = sizeof(tTSS);
+ gGDT[9+pos].LimitHi = 0;
+ gGDT[9+pos].Access = 0x89; // Type
+ gGDT[9+pos].Flags = 0x4;
+ gGDT[9+pos].BaseLow = (Uint)&gTSSs[pos] & 0xFFFF;
+ gGDT[9+pos].BaseMid = (Uint)&gTSSs[pos] >> 16;
+ gGDT[9+pos].BaseHi = (Uint)&gTSSs[pos] >> 24;
+ }
+ for(pos=0;pos<giNumCPUs;pos++) {
+ __asm__ __volatile__ ("ltr %%ax"::"a"(0x48+pos*8));
+ }
+
+ // Set timer frequency
+ outb(0x43, 0x34); // Set Channel 0, Low/High, Rate Generator
+ outb(0x40, TIMER_DIVISOR&0xFF); // Low Byte of Divisor
+ outb(0x40, (TIMER_DIVISOR>>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;
+}
--- /dev/null
+/*
+ * AcessOS Microkernel Version
+ * syscalls.c
+ */
+#define DEBUG 1
+
+#include <common.h>
+#include <syscalls.h>
+#include <proc.h>
+#include <errno.h>
+
+// === 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
+}
+
--- /dev/null
+
+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
--- /dev/null
+/*
+ * Acess 2
+ * Architecture Independent System Init
+ * system.c
+ */
+#include <common.h>
+
+// === 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 <link>=<destination>
+ if(value[0] == '/')
+ {
+ Log("Symbolic link '%s' pointing to '%s'", Arg, value);
+ VFS_Symlink(Arg, value);
+ }
+ // - Mount <mountpoint>=<fs>:<device>
+ 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;
+}
--- /dev/null
+/*
+ * Acess 2
+ * binary.c
+ * - Binary File Loader
+ */
+#include <common.h>
+#include <vfs.h>
+
+// === GLOBALS ===
+
+
+// === CODE ===
--- /dev/null
+/*
+ * Acess Micro VFS
+ */
+#include <common.h>
+#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;i<Node->NumACLs;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;i<Node->NumACLs;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;
+}
--- /dev/null
+/*
+ */
+#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;
+}
--- /dev/null
+/*
+ * Acess 2
+ * Device Filesystem (DevFS)
+ * - vfs/fs/devfs.c
+ */
+#include <common.h>
+#include <vfs.h>
+#include <fs_devfs.h>
+
+// === 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);
--- /dev/null
+/*\r
+ * Acess2\r
+ * FAT12/16/32 Driver Version (Incl LFN)\r
+ */\r
+//INCLUDES\r
+#include <common.h>\r
+#include <modules.h>\r
+#include <vfs.h>\r
+#include "fs_fat.h"\r
+\r
+#define DEBUG 0\r
+#define VERBOSE 1\r
+\r
+#if DEBUG\r
+# define DEBUGS(v...) Log(v)\r
+#else\r
+# define DEBUGS(v...)\r
+# undef ENTER\r
+# undef LOG\r
+# undef LEAVE\r
+# define ENTER(...)\r
+# define LOG(...)\r
+# define LEAVE(...)\r
+#endif\r
+\r
+#define CACHE_FAT 1 //!< Caches the FAT in memory\r
+#define USE_LFN 1 //!< Enables the use of Long File Names\r
+\r
+// === TYPES ===\r
+#if USE_LFN\r
+typedef struct s_lfncache {\r
+ Uint Inode, Impl;\r
+ int id;\r
+ char Name[256];\r
+ struct s_lfncache *Next;\r
+} t_lfncache;\r
+#endif\r
+\r
+// === PROTOTYPES ===\r
+ int FAT_Install(char **Arguments);\r
+tVFS_Node *FAT_InitDevice(char *device, char *options);\r
+void FAT_CloseDevice(tVFS_Node *node);\r
+Uint64 FAT_Read(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer);\r
+Uint64 FAT_Write(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer);\r
+char *FAT_ReadDir(tVFS_Node *dirNode, int dirpos);\r
+tVFS_Node *FAT_FindDir(tVFS_Node *dirNode, char *file);\r
+ int FAT_Mknod(tVFS_Node *Node, char *Name, Uint Flags);\r
+ int FAT_Relink(tVFS_Node *node, char *OldName, char *NewName);\r
+void FAT_CloseFile(tVFS_Node *node);\r
+\r
+// === SEMI-GLOBALS ===\r
+MODULE_DEFINE(0, 0x5B /*v0.90*/, FAT32, FAT_Install, NULL);\r
+tFAT_VolInfo gFAT_Disks[8];\r
+ int giFAT_PartCount = 0;\r
+#if CACHE_FAT\r
+Uint32 *fat_cache[8];\r
+#endif\r
+#if USE_LFN\r
+t_lfncache *fat_lfncache;\r
+#endif\r
+tVFS_Driver gFAT_FSInfo = {\r
+ "fat", 0, FAT_InitDevice, NULL\r
+ };\r
+\r
+// === CODE ===\r
+/**\r
+ * \fn int FAT_Install(char **Arguments)\r
+ * \brief \r
+ */\r
+int FAT_Install(char **Arguments)\r
+{\r
+ VFS_AddDriver( &gFAT_FSInfo );\r
+ return 0;\r
+}\r
+\r
+/* Reads the boot sector of a disk and prepares the structures for it\r
+ */\r
+tVFS_Node *FAT_InitDevice(char *Device, char *options)\r
+{\r
+ fat_bootsect *bs;\r
+ int i;\r
+ Uint32 FATSz, RootDirSectors, TotSec, CountofClusters;\r
+ tVFS_Node *node = NULL;\r
+ tFAT_VolInfo *diskInfo = &gFAT_Disks[giFAT_PartCount];\r
+ \r
+ //Temporary Pointer\r
+ bs = &diskInfo->bootsect;\r
+ \r
+ //Open device and read boot sector\r
+ diskInfo->fileHandle = VFS_Open(Device, VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE);\r
+ if(diskInfo->fileHandle == -1) {\r
+ Warning("FAT_InitDisk - Unable to open device '%s'", Device);\r
+ return NULL;\r
+ }\r
+ \r
+ VFS_ReadAt(diskInfo->fileHandle, 0, 512, bs);\r
+ \r
+ if(bs->bps == 0 || bs->spc == 0) {\r
+ Warning("FAT_InitDisk - Error in FAT Boot Sector\n");\r
+ return NULL;\r
+ }\r
+ \r
+ //FAT Type Determining\r
+ // From Microsoft FAT Specifcation\r
+ RootDirSectors = ((bs->files_in_root*32) + (bs->bps - 1)) / bs->bps;\r
+ \r
+ if(bs->fatSz16 != 0) FATSz = bs->fatSz16;\r
+ else FATSz = bs->spec.fat32.fatSz32;\r
+ \r
+ if(bs->totalSect16 != 0) TotSec = bs->totalSect16;\r
+ else TotSec = bs->totalSect32;\r
+ \r
+ CountofClusters = (TotSec - (bs->resvSectCount + (bs->fatCount * FATSz) + RootDirSectors)) / bs->spc;\r
+ \r
+ if(CountofClusters < 4085)\r
+ diskInfo->type = FAT12;\r
+ else if(CountofClusters < 65525)\r
+ diskInfo->type = FAT16;\r
+ else\r
+ diskInfo->type = FAT32;\r
+ \r
+ #if VERBOSE\r
+ {\r
+ char *sFatType, *sSize;\r
+ Uint iSize = CountofClusters * bs->spc / 2;\r
+ \r
+ switch(diskInfo->type)\r
+ {\r
+ case FAT12: sFatType = "FAT12"; break;\r
+ case FAT16: sFatType = "FAT16"; break;\r
+ case FAT32: sFatType = "FAT32"; break;\r
+ }\r
+ if(iSize <= 2*1024) {\r
+ sSize = "KiB";\r
+ }\r
+ else if(iSize <= 2*1024*1024) {\r
+ sSize = "MiB";\r
+ iSize >>= 10;\r
+ }\r
+ else {\r
+ sSize = "GiB";\r
+ iSize >>= 20;\r
+ }\r
+ Log("[FAT ] '%s' %s, %i %s", Device, sFatType, iSize, sSize);\r
+ }\r
+ #endif\r
+ \r
+ //Get Name\r
+ //puts(" Name: ");\r
+ if(diskInfo->type == FAT32) {\r
+ for(i=0;i<11;i++)\r
+ diskInfo->name[i] = (bs->spec.fat32.label[i] == ' ' ? '\0' : bs->spec.fat32.label[i]);\r
+ }\r
+ else {\r
+ for(i=0;i<11;i++)\r
+ diskInfo->name[i] = (bs->spec.fat16.label[i] == ' ' ? '\0' : bs->spec.fat16.label[i]);\r
+ }\r
+ diskInfo->name[11] = '\0';\r
+ //puts(diskInfo->name); putch('\n');\r
+ \r
+ //Compute Root directory offset\r
+ if(diskInfo->type == FAT32)\r
+ diskInfo->rootOffset = bs->spec.fat32.rootClust;\r
+ else\r
+ diskInfo->rootOffset = (FATSz * bs->fatCount) / bs->spc;\r
+ \r
+ diskInfo->clusterCount = CountofClusters;\r
+ \r
+ diskInfo->firstDataSect = bs->resvSectCount + (bs->fatCount * FATSz) + RootDirSectors;\r
+ \r
+ //Allow for Caching the FAT\r
+ #if CACHE_FAT\r
+ {\r
+ Uint32 Ofs;\r
+ fat_cache[ giFAT_PartCount ] = (Uint32*)malloc(sizeof(Uint32)*CountofClusters);\r
+ if(fat_cache[giFAT_PartCount] == NULL) {\r
+ Warning("FAT_InitDisk - Heap Exhausted\n");\r
+ return NULL;\r
+ }\r
+ Ofs = bs->resvSectCount*512;\r
+ if(diskInfo->type == FAT12) {\r
+ Uint32 val;\r
+ int j;\r
+ char buf[1536];\r
+ for(i=0;i<CountofClusters/2;i++) {\r
+ j = i & 511; //%512\r
+ if( j == 0 ) {\r
+ VFS_ReadAt(diskInfo->fileHandle, Ofs, 3*512, buf);\r
+ Ofs += 3*512;\r
+ }\r
+ val = *((int*)(buf+j*3));\r
+ fat_cache[giFAT_PartCount][i*2] = val & 0xFFF;\r
+ fat_cache[giFAT_PartCount][i*2+1] = (val>>12) & 0xFFF;\r
+ }\r
+ }\r
+ if(diskInfo->type == FAT16) {\r
+ Uint16 buf[256];\r
+ for(i=0;i<CountofClusters;i++) {\r
+ if( (i & 255) == 0 ) {\r
+ VFS_ReadAt(diskInfo->fileHandle, Ofs, 512, buf);\r
+ Ofs += 512;\r
+ }\r
+ fat_cache[giFAT_PartCount][i] = buf[i&255];\r
+ }\r
+ }\r
+ if(diskInfo->type == FAT32) {\r
+ Uint32 buf[128];\r
+ for(i=0;i<CountofClusters;i++) {\r
+ if( (i & 127) == 0 ) {\r
+ VFS_ReadAt(diskInfo->fileHandle, Ofs, 512, buf);\r
+ Ofs += 512;\r
+ }\r
+ fat_cache[giFAT_PartCount][i] = buf[i&127];\r
+ }\r
+ }\r
+ DEBUGS(" FAT_InitDisk: FAT Fully Cached\n");\r
+ }\r
+ #endif /*CACHE_FAT*/\r
+ \r
+ //Initalise inode cache for FAT\r
+ gFAT_Disks[giFAT_PartCount].inodeHandle = Inode_GetHandle();\r
+ \r
+ #if DEBUG\r
+ Log(" FAT_InitDisk: Inode Cache handle is %i\n", gFAT_Disks[giFAT_PartCount].inodeHandle);\r
+ #endif\r
+ \r
+ //== VFS Interface\r
+ node = &gFAT_Disks[giFAT_PartCount].rootNode;\r
+ //node->Name = gFAT_Disks[giFAT_PartCount].name;\r
+ node->Inode = diskInfo->rootOffset;\r
+ node->Size = bs->files_in_root; //Unknown - To be set on readdir\r
+ node->ImplInt = giFAT_PartCount;\r
+ \r
+ node->ReferenceCount = 1;\r
+ \r
+ node->UID = 0; node->GID= 0;\r
+ node->NumACLs = 1;\r
+ node->ACLs = &gVFS_ACL_EveryoneRWX;\r
+ node->Flags = VFS_FFLAG_DIRECTORY;\r
+ node->CTime = node->MTime = node->ATime = now();\r
+ \r
+ node->Read = node->Write = NULL;\r
+ node->ReadDir = FAT_ReadDir;\r
+ node->FindDir = FAT_FindDir;\r
+ node->Relink = FAT_Relink;\r
+ node->MkNod = FAT_Mknod;\r
+ node->Close = FAT_CloseDevice;\r
+ \r
+ giFAT_PartCount ++;\r
+ return node;\r
+}\r
+\r
+/**\r
+ * \fn void FAT_CloseDevice(tVFS_Node *node)\r
+ * \brief Closes a mount and marks it as free\r
+ */\r
+void FAT_CloseDevice(tVFS_Node *node)\r
+{\r
+ node->ReferenceCount --;\r
+ \r
+ if(node->ReferenceCount > 0) return;\r
+ \r
+ // Close Disk Handle\r
+ VFS_Close( gFAT_Disks[node->ImplInt].fileHandle );\r
+ Inode_ClearCache(gFAT_Disks[node->ImplInt].inodeHandle);\r
+ gFAT_Disks[node->ImplInt].fileHandle = -2;\r
+ return;\r
+}\r
+\r
+/**\r
+ * \fn static Uint32 FAT_int_GetFatValue(int handle, Uint32 cluster)\r
+ * \brief Fetches a value from the FAT\r
+ */\r
+static Uint32 FAT_int_GetFatValue(int handle, Uint32 cluster)\r
+{\r
+ Uint32 val;\r
+ ENTER("iHandle xCluster", handle, cluster);\r
+ #if CACHE_FAT\r
+ val = fat_cache[handle][cluster];\r
+ #else\r
+ if(gFAT_Disks[handle].type == FAT12) {\r
+ VFS_ReadAt(gFAT_Disks[handle].fileHandle, 512+(cluster&~1)*3, 3, &val);\r
+ val = (cluster&1 ? val&0xFFF : val>>12);\r
+ } else if(gFAT_Disks[handle].type == FAT16) {\r
+ VFS_ReadAt(gFAT_Disks[handle].fileHandle, 512+cluster*2, 2, &val);\r
+ } else {\r
+ VFS_ReadAt(gFAT_Disks[handle].fileHandle, 512+cluster*4, 4, &val);\r
+ }\r
+ #endif /*CACHE_FAT*/\r
+ LEAVE('x', val);\r
+ return val;\r
+}\r
+\r
+/* Reads a cluster's data\r
+ */\r
+static void FAT_int_ReadCluster(int Handle, Uint32 Cluster, int Length, void *Buffer)\r
+{\r
+ #if DEBUG\r
+ ENTER("iHandle xCluster iLength pBuffer", Handle, Cluster, Length, Buffer);\r
+ #endif\r
+ VFS_ReadAt(\r
+ gFAT_Disks[Handle].fileHandle,\r
+ (gFAT_Disks[Handle].firstDataSect + (Cluster-2)*gFAT_Disks[Handle].bootsect.spc )\r
+ * gFAT_Disks[Handle].bootsect.bps,\r
+ Length,\r
+ Buffer\r
+ );\r
+ #if DEBUG\r
+ LEAVE('-');\r
+ #endif\r
+}\r
+\r
+/**\r
+ * \fn Uint64 FAT_Read(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer)\r
+ * \brief Reads data from a specified file\r
+ */\r
+Uint64 FAT_Read(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer)\r
+{\r
+ int preSkip, count;\r
+ int handle = node->ImplInt;\r
+ int i, cluster, pos;\r
+ int bpc;\r
+ void *tmpBuf;\r
+ Uint eocMarker;\r
+ tFAT_VolInfo *disk = &gFAT_Disks[node->ImplInt];\r
+ \r
+ ENTER("Xoffset Xlength pbuffer", offset, length, buffer);\r
+ \r
+ // Calculate and Allocate Bytes Per Cluster\r
+ bpc = disk->bootsect.spc * disk->bootsect.bps;\r
+ tmpBuf = (void*) malloc(bpc);\r
+ LOG("malloc'd %i bytes", bpc);\r
+ \r
+ // Cluster is stored in Inode Field\r
+ cluster = node->Inode;\r
+ \r
+ // Get EOC Marker\r
+ if (disk->type == FAT12) eocMarker = EOC_FAT12;\r
+ else if(disk->type == FAT16) eocMarker = EOC_FAT16;\r
+ else if(disk->type == FAT32) eocMarker = EOC_FAT32;\r
+ else {\r
+ Log("ERROR: Unsupported FAT Variant.\n");\r
+ free(tmpBuf);\r
+ LEAVE('i', 0);\r
+ return 0;\r
+ }\r
+ \r
+ // Single Cluster including offset\r
+ if(length + offset < bpc)\r
+ {\r
+ FAT_int_ReadCluster(handle, cluster, bpc, tmpBuf);\r
+ memcpy( buffer, (void*)( tmpBuf + offset%bpc ), length );\r
+ free(tmpBuf);\r
+ LEAVE('i', 1);\r
+ return 1;\r
+ }\r
+ \r
+ preSkip = offset / bpc;\r
+ \r
+ //Skip previous clusters\r
+ for(i=preSkip;i--;) {\r
+ cluster = FAT_int_GetFatValue(handle, cluster);\r
+ if(cluster == eocMarker) {\r
+ Warning("FAT_Read - Offset is past end of cluster chain mark");\r
+ }\r
+ }\r
+ \r
+ // Get Count of Clusters to read\r
+ count = ((offset%bpc+length) / bpc) + 1;\r
+ \r
+ // Get buffer Position after 1st cluster\r
+ pos = bpc - offset%bpc;\r
+ \r
+ // Read 1st Cluster\r
+ FAT_int_ReadCluster(handle, cluster, bpc, tmpBuf);\r
+ memcpy(\r
+ buffer,\r
+ (void*)( tmpBuf + (bpc-pos) ),\r
+ (pos < length ? pos : length)\r
+ );\r
+ \r
+ if (count == 1) {\r
+ free(tmpBuf);\r
+ LEAVE('i', 1);\r
+ return 1;\r
+ }\r
+ \r
+ cluster = FAT_int_GetFatValue(handle, cluster);\r
+ \r
+ #if DEBUG\r
+ LOG("pos=%i\n", pos);\r
+ LOG("Reading the rest of the clusters\n");\r
+ #endif\r
+ \r
+ \r
+ //Read the rest of the cluster data\r
+ for( i = 1; i < count-1; i++ )\r
+ {\r
+ FAT_int_ReadCluster(handle, cluster, bpc, tmpBuf);\r
+ memcpy((void*)(buffer+pos), tmpBuf, bpc);\r
+ pos += bpc;\r
+ cluster = FAT_int_GetFatValue(handle, cluster);\r
+ if(cluster == eocMarker) {\r
+ Warning("FAT_Read - Read past End of Cluster Chain");\r
+ free(tmpBuf);\r
+ LEAVE('i', 0);\r
+ return 0;\r
+ }\r
+ }\r
+ \r
+ FAT_int_ReadCluster(handle, cluster, bpc, tmpBuf);\r
+ memcpy((void*)(buffer+pos), tmpBuf, length-pos);\r
+ \r
+ #if DEBUG\r
+ LOG("Free tmpBuf(0x%x) and Return\n", tmpBuf);\r
+ #endif\r
+ \r
+ free(tmpBuf);\r
+ LEAVE('X', length);\r
+ return length;\r
+}\r
+\r
+/**\r
+ * \fn Uint64 FAT_Write(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer)\r
+ */\r
+Uint64 FAT_Write(tVFS_Node *node, Uint64 offset, Uint64 length, void *buffer)\r
+{\r
+ return 0;\r
+}\r
+\r
+/**\r
+ * \fn static void FAT_int_ProperFilename(char *dest, char *src)\r
+ * \brief Converts a FAT directory entry name into a proper filename\r
+ */\r
+static void FAT_int_ProperFilename(char *dest, char *src)\r
+{\r
+ int a, b;\r
+ \r
+ for( a = 0; a < 8; a++) {\r
+ if(src[a] == ' ') break;\r
+ dest[a] = src[a];\r
+ }\r
+ b = a;\r
+ a = 8;\r
+ if(src[8] != ' ')\r
+ dest[b++] = '.';\r
+ for( ; a < 11; a++, b++) {\r
+ if(src[a] == ' ') break;\r
+ dest[b] = src[a];\r
+ }\r
+ dest[b] = '\0';\r
+ #if DEBUG\r
+ //Log("FAT_int_ProperFilename: dest='%s'", dest);\r
+ #endif\r
+}\r
+\r
+/**\r
+ * \fn char *FAT_int_CreateName(tVFS_Node *parent, fat_filetable *ft, char *LongFileName)\r
+ * \brief Converts either a LFN or a 8.3 Name into a proper name\r
+ */\r
+char *FAT_int_CreateName(tVFS_Node *parent, fat_filetable *ft, char *LongFileName)\r
+{\r
+ char *ret;\r
+ int len;\r
+ #if USE_LFN\r
+ if(LongFileName && LongFileName[0] != '\0')\r
+ { \r
+ len = strlen(LongFileName);\r
+ ret = malloc(len+1);\r
+ strcpy(ret, LongFileName);\r
+ }\r
+ else\r
+ {\r
+ #endif\r
+ ret = (char*) malloc(13);\r
+ memset(ret, 13, '\0');\r
+ FAT_int_ProperFilename(ret, ft->name);\r
+ #if USE_LFN\r
+ }\r
+ #endif\r
+ return ret;\r
+}\r
+\r
+/**\r
+ * \fn tVFS_Node *FAT_int_CreateNode(tVFS_Node *parent, fat_filetable *ft, char *LongFileName)\r
+ * \brief Creates a tVFS_Node structure for a given file entry\r
+ */\r
+tVFS_Node *FAT_int_CreateNode(tVFS_Node *parent, fat_filetable *ft, char *LongFileName)\r
+{\r
+ tVFS_Node node = {0};\r
+ tVFS_Node *ret;\r
+ \r
+ ENTER("pParent pFT sLongFileName", parent, ft, LongFileName);\r
+ \r
+ // Get Name\r
+ //node.Name = FAT_int_CreateName(parent, ft, LongFileName);\r
+ // Set Other Data\r
+ node.Inode = ft->cluster | (ft->clusterHi<<16);\r
+ node.Size = ft->size;\r
+ node.ImplInt = parent->ImplInt;\r
+ node.UID = 0; node.GID = 0;\r
+ node.NumACLs = 1;\r
+ node.ACLs = &gVFS_ACL_EveryoneRWX; // RWXRWXRWX\r
+ \r
+ node.Flags = 0;\r
+ if(ft->attrib & ATTR_DIRECTORY) node.Flags |= VFS_FFLAG_DIRECTORY;\r
+ if(ft->attrib & ATTR_READONLY) node.Flags |= VFS_FFLAG_READONLY;\r
+ \r
+ node.ATime = timestamp(0,0,0,\r
+ ((ft->adate&0x1F)-1), //Days\r
+ ((ft->adate&0x1E0)-1), //Months\r
+ 1980+((ft->adate&0xFF00)>>8)); //Years\r
+ \r
+ node.CTime = ft->ctimems * 10; //Miliseconds\r
+ node.CTime += timestamp(\r
+ (ft->ctime&0x1F)<<1, //Seconds\r
+ ((ft->ctime&0x3F0)>>5), //Minutes\r
+ ((ft->ctime&0xF800)>>11), //Hours\r
+ ((ft->cdate&0x1F)-1), //Days\r
+ ((ft->cdate&0x1E0)-1), //Months\r
+ 1980+((ft->cdate&0xFF00)>>8)); //Years\r
+ \r
+ node.MTime = timestamp(\r
+ (ft->mtime&0x1F)<<1, //Seconds\r
+ ((ft->mtime&0x3F0)>>5), //Minuites\r
+ ((ft->mtime&0xF800)>>11), //Hours\r
+ ((ft->mdate&0x1F)-1), //Days\r
+ ((ft->mdate&0x1E0)-1), //Months\r
+ 1980+((ft->mdate&0xFF00)>>8)); //Years\r
+ \r
+ if(node.Flags & VFS_FFLAG_DIRECTORY) {\r
+ node.ReadDir = FAT_ReadDir;\r
+ node.FindDir = FAT_FindDir;\r
+ node.MkNod = FAT_Mknod;\r
+ } else {\r
+ node.Read = FAT_Read;\r
+ node.Write = FAT_Write;\r
+ }\r
+ node.Close = FAT_CloseFile;\r
+ node.Relink = FAT_Relink;\r
+ \r
+ ret = Inode_CacheNode(gFAT_Disks[parent->ImplInt].inodeHandle, &node);\r
+ LEAVE('p', ret);\r
+ return ret;\r
+}\r
+\r
+#if USE_LFN\r
+/**\r
+ \fn char *FAT_int_GetLFN(tVFS_Node *node)\r
+ \brief Return pointer to LFN cache entry\r
+ */\r
+char *FAT_int_GetLFN(tVFS_Node *node)\r
+{\r
+ t_lfncache *tmp;\r
+ tmp = fat_lfncache;\r
+ while(tmp)\r
+ {\r
+ if(tmp->Inode == node->Inode && tmp->Impl == node->ImplInt)\r
+ return tmp->Name;\r
+ tmp = tmp->Next;\r
+ }\r
+ tmp = malloc(sizeof(t_lfncache));\r
+ tmp->Inode = node->Inode;\r
+ tmp->Impl = node->ImplInt;\r
+ memset(tmp->Name, 0, 256);\r
+ \r
+ tmp->Next = fat_lfncache;\r
+ fat_lfncache = tmp;\r
+ \r
+ return tmp->Name;\r
+}\r
+\r
+/**\r
+ \fn void FAT_int_DelLFN(tVFS_Node *node)\r
+ \brief Delete a LFN cache entry\r
+*/\r
+void FAT_int_DelLFN(tVFS_Node *node)\r
+{\r
+ t_lfncache *tmp;\r
+ \r
+ if(!fat_lfncache) return;\r
+ \r
+ if(!fat_lfncache->Next)\r
+ {\r
+ tmp = fat_lfncache;\r
+ fat_lfncache = tmp->Next;\r
+ free(tmp);\r
+ return;\r
+ }\r
+ tmp = fat_lfncache;\r
+ while(tmp && tmp->Next)\r
+ {\r
+ if(tmp->Inode == node->Inode && tmp->Impl == node->ImplInt)\r
+ {\r
+ free(tmp->Next);\r
+ tmp->Next = tmp->Next->Next;\r
+ return;\r
+ }\r
+ tmp = tmp->Next;\r
+ }\r
+}\r
+#endif\r
+\r
+/**\r
+ \fn char *FAT_ReadDir(tVFS_Node *dirNode, int dirPos)\r
+ \param dirNode Node structure of directory\r
+ \param dirPos Directory position\r
+**/\r
+char *FAT_ReadDir(tVFS_Node *dirNode, int dirpos)\r
+{\r
+ fat_filetable fileinfo[16]; //Sizeof=32, 16 per sector\r
+ int a=0;\r
+ tFAT_VolInfo *disk = &gFAT_Disks[dirNode->ImplInt&7];\r
+ Uint32 cluster, offset;\r
+ int preSkip;\r
+ char *ret;\r
+ #if USE_LFN\r
+ char *lfn = NULL;\r
+ #endif\r
+ \r
+ ENTER("pDirNode iDirPos", dirNode, dirpos);\r
+ \r
+ // Get Byte Offset and skip\r
+ offset = dirpos * sizeof(fat_filetable);\r
+ preSkip = (offset >> 9) / disk->bootsect.spc; // >>9 == /512\r
+ cluster = dirNode->Inode; // Cluster ID\r
+ \r
+ // Do Cluster Skip\r
+ // - Pre FAT32 had a reserved area for the root.\r
+ if( !(disk->type != FAT32 && cluster == disk->rootOffset) )\r
+ {\r
+ //Skip previous clusters\r
+ for(a=preSkip;a--;) {\r
+ cluster = FAT_int_GetFatValue(dirNode->ImplInt, cluster);\r
+ }\r
+ }\r
+ \r
+ // Check for end of cluster chain\r
+ if((disk->type == FAT12 && cluster == EOC_FAT12)\r
+ || (disk->type == FAT16 && cluster == EOC_FAT16)\r
+ || (disk->type == FAT32 && cluster == EOC_FAT32))\r
+ return NULL;\r
+ \r
+ // Bounds Checking (Used to spot heap overflows)\r
+ if(cluster > disk->clusterCount + 2)\r
+ {\r
+ Warning("FAT_ReadDir - Cluster ID is over cluster count (0x%x>0x%x)",\r
+ cluster, disk->clusterCount+2);\r
+ LEAVE('n');\r
+ return NULL;\r
+ }\r
+ \r
+ LOG("cluster=0x%x, dirpos=%i\n", cluster, dirpos);\r
+ \r
+ // Compute Offsets\r
+ // - Pre FAT32 cluster base (in sectors)\r
+ if( cluster == disk->rootOffset && disk->type != FAT32 )\r
+ offset = disk->bootsect.resvSectCount + cluster*disk->bootsect.spc;\r
+ else\r
+ { // FAT32 cluster base (in sectors)\r
+ offset = disk->firstDataSect;\r
+ offset += (cluster - 2) * disk->bootsect.spc;\r
+ }\r
+ // Sector in cluster\r
+ if(disk->bootsect.spc == 1)\r
+ offset += (dirpos / 16);\r
+ else\r
+ offset += (dirpos / 16) % disk->bootsect.spc;\r
+ // Offset in sector\r
+ a = dirpos & 0xF;\r
+\r
+ LOG("offset=%i, a=%i\n", (Uint)offset, a);\r
+ \r
+ // Read Sector\r
+ VFS_ReadAt(disk->fileHandle, offset*512, 512, fileinfo); // Read Dir Data\r
+ \r
+ LOG("name[0] = 0x%x\n", (Uint8)fileinfo[a].name[0]);\r
+ //Check if this is the last entry\r
+ if(fileinfo[a].name[0] == '\0') {\r
+ dirNode->Size = dirpos;\r
+ LOG("End of list\n");\r
+ LEAVE('n');\r
+ return NULL; // break\r
+ }\r
+ \r
+ // Check for empty entry\r
+ if((Uint8)fileinfo[a].name[0] == 0xE5) {\r
+ LOG("Empty Entry\n");\r
+ LEAVE('p', VFS_SKIP);\r
+ return VFS_SKIP; // Skip\r
+ }\r
+ \r
+ #if USE_LFN\r
+ // Get Long File Name Cache\r
+ lfn = FAT_int_GetLFN(dirNode);\r
+ if(fileinfo[a].attrib == ATTR_LFN)\r
+ {\r
+ fat_longfilename *lfnInfo;\r
+ int len;\r
+ \r
+ lfnInfo = (fat_longfilename *) &fileinfo[a];\r
+ if(lfnInfo->id & 0x40) memset(lfn, 0, 256);\r
+ // Get the current length\r
+ len = strlen(lfn);\r
+ \r
+ // Sanity Check (FAT implementations do not allow >255 bytes)\r
+ if(len + 13 > 255) return VFS_SKIP;\r
+ // Rebase all bytes\r
+ for(a=len+1;a--;) lfn[a+13] = lfn[a];\r
+ \r
+ // Append new bytes\r
+ lfn[ 0] = lfnInfo->name1[0]; lfn[ 1] = lfnInfo->name1[1];\r
+ lfn[ 2] = lfnInfo->name1[2]; lfn[ 3] = lfnInfo->name1[3];\r
+ lfn[ 4] = lfnInfo->name1[4]; \r
+ lfn[ 5] = lfnInfo->name2[0]; lfn[ 6] = lfnInfo->name2[1];\r
+ lfn[ 7] = lfnInfo->name2[2]; lfn[ 8] = lfnInfo->name2[3];\r
+ lfn[ 9] = lfnInfo->name2[4]; lfn[10] = lfnInfo->name2[5];\r
+ lfn[11] = lfnInfo->name3[0]; lfn[12] = lfnInfo->name3[1];\r
+ LEAVE('p', VFS_SKIP);\r
+ return VFS_SKIP;\r
+ }\r
+ #endif\r
+ \r
+ //Check if it is a volume entry\r
+ if(fileinfo[a].attrib & 0x08) {\r
+ LEAVE('p', VFS_SKIP);\r
+ return VFS_SKIP;\r
+ }\r
+ // Ignore . and ..\r
+ if(fileinfo[a].name[0] == '.') {\r
+ LEAVE('p', VFS_SKIP);\r
+ return VFS_SKIP;\r
+ } \r
+ \r
+ LOG("name='%c%c%c%c%c%c%c%c.%c%c%c'\n",\r
+ fileinfo[a].name[0], fileinfo[a].name[1], fileinfo[a].name[2], fileinfo[a].name[3],\r
+ fileinfo[a].name[4], fileinfo[a].name[5], fileinfo[a].name[6], fileinfo[a].name[7],\r
+ fileinfo[a].name[8], fileinfo[a].name[9], fileinfo[a].name[10] );\r
+ \r
+ #if USE_LFN\r
+ //node = FAT_int_CreateNode(dirNode, &fileinfo[a], lfn);\r
+ ret = FAT_int_CreateName(dirNode, &fileinfo[a], lfn);\r
+ lfn[0] = '\0';\r
+ #else\r
+ //node = FAT_int_CreateNode(dirNode, &fileinfo[a], NULL);\r
+ ret = FAT_int_CreateName(dirNode, &fileinfo[a], NULL);\r
+ #endif\r
+ \r
+ LEAVE('s', ret);\r
+ return ret;\r
+}\r
+\r
+/**\r
+ * \fn tVFS_Node *FAT_FindDir(tVFS_Node *node, char *name)\r
+ * \brief Finds an entry in the current directory\r
+ */\r
+tVFS_Node *FAT_FindDir(tVFS_Node *node, char *name)\r
+{\r
+ fat_filetable fileinfo[16];\r
+ char tmpName[11];\r
+ #if USE_LFN\r
+ fat_longfilename *lfnInfo;\r
+ char *lfn = NULL;\r
+ int lfnPos=255, lfnId = -1;\r
+ #endif\r
+ int i=0;\r
+ tVFS_Node *tmpNode;\r
+ Uint64 diskOffset;\r
+ tFAT_VolInfo *disk = &gFAT_Disks[node->ImplInt];\r
+ Uint32 dirCluster;\r
+ Uint32 cluster;\r
+ \r
+ ENTER("pnode sname", node, name);\r
+ \r
+ // Fast Returns\r
+ if(!name) return NULL;\r
+ if(name[0] == '\0') return NULL;\r
+ \r
+ #if USE_LFN\r
+ lfn = FAT_int_GetLFN(node);\r
+ #endif\r
+ \r
+ dirCluster = node->Inode;\r
+ // Seek to Directory\r
+ if( dirCluster == disk->rootOffset && disk->type != FAT32 )\r
+ diskOffset = (disk->bootsect.resvSectCount+dirCluster*disk->bootsect.spc) << 9;\r
+ else\r
+ diskOffset = (disk->firstDataSect+(dirCluster-2)*disk->bootsect.spc) << 9;\r
+ \r
+ for(;;i++)\r
+ {\r
+ // Load sector\r
+ if((i & 0xF) == 0) {\r
+ VFS_ReadAt(disk->fileHandle, diskOffset, 512, fileinfo);\r
+ diskOffset += 512;\r
+ }\r
+ \r
+ //Check if the files are free\r
+ if(fileinfo[i&0xF].name[0] == '\0') break; //Free and last\r
+ if(fileinfo[i&0xF].name[0] == '\xE5') goto loadCluster; //Free\r
+ \r
+ \r
+ #if USE_LFN\r
+ // Long File Name Entry\r
+ if(fileinfo[i&0xF].attrib == ATTR_LFN)\r
+ {\r
+ lfnInfo = (fat_longfilename *) &fileinfo[i&0xF];\r
+ if(lfnInfo->id & 0x40) {\r
+ memset(lfn, 0, 256);\r
+ lfnPos = 255;\r
+ }\r
+ lfn[lfnPos--] = lfnInfo->name3[1]; lfn[lfnPos--] = lfnInfo->name3[0];\r
+ lfn[lfnPos--] = lfnInfo->name2[5]; lfn[lfnPos--] = lfnInfo->name2[4];\r
+ lfn[lfnPos--] = lfnInfo->name2[3]; lfn[lfnPos--] = lfnInfo->name2[2];\r
+ lfn[lfnPos--] = lfnInfo->name2[1]; lfn[lfnPos--] = lfnInfo->name2[0];\r
+ lfn[lfnPos--] = lfnInfo->name1[4]; lfn[lfnPos--] = lfnInfo->name1[3];\r
+ lfn[lfnPos--] = lfnInfo->name1[2]; lfn[lfnPos--] = lfnInfo->name1[1];\r
+ lfn[lfnPos--] = lfnInfo->name1[0];\r
+ if((lfnInfo->id&0x3F) == 1)\r
+ {\r
+ memcpy(lfn, lfn+lfnPos+1, 256-lfnPos);\r
+ lfnId = i+1;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ // Remove LFN if it does not apply\r
+ if(lfnId != i) lfn[0] = '\0';\r
+ #endif\r
+ // Get Real Filename\r
+ FAT_int_ProperFilename(tmpName, fileinfo[i&0xF].name);\r
+ \r
+ LOG("tmpName = '%s'\n", tmpName);\r
+ \r
+ //Only Long name is case sensitive, 8.3 is not\r
+ #if USE_LFN\r
+ if(strucmp(tmpName, name) == 0 || strcmp(lfn, name) == 0) {\r
+ #else\r
+ if(strucmp(tmpName, name) == 0) {\r
+ #endif\r
+ cluster = fileinfo[i&0xF].cluster | (fileinfo[i&0xF].clusterHi << 16);\r
+ tmpNode = Inode_GetCache(disk->inodeHandle, cluster);\r
+ if(tmpNode == NULL) // Node is not cached\r
+ {\r
+ #if USE_LFN\r
+ tmpNode = FAT_int_CreateNode(node, &fileinfo[i&0xF], lfn);\r
+ #else\r
+ tmpNode = FAT_int_CreateNode(node, &fileinfo[i&0xF], NULL);\r
+ #endif\r
+ }\r
+ #if USE_LFN\r
+ lfn[0] = '\0';\r
+ #endif\r
+ LEAVE('p', tmpNode);\r
+ return tmpNode;\r
+ }\r
+ #if USE_LFN\r
+ }\r
+ #endif\r
+ \r
+ loadCluster:\r
+ //Load Next cluster?\r
+ if( ((i+1) >> 4) % disk->bootsect.spc == 0 && ((i+1) & 0xF) == 0)\r
+ {\r
+ if( dirCluster == disk->rootOffset && disk->type != FAT32 )\r
+ continue;\r
+ dirCluster = FAT_int_GetFatValue(node->ImplInt, dirCluster);\r
+ diskOffset = (disk->firstDataSect+(dirCluster-2)*disk->bootsect.spc)*512;\r
+ }\r
+ }\r
+ \r
+ LEAVE('n');\r
+ return NULL;\r
+}\r
+\r
+/**\r
+ * \fn int FAT_Mknod(tVFS_Node *Node, char *Name, Uint Flags)\r
+ * \brief Create a new node\r
+ */\r
+int FAT_Mknod(tVFS_Node *Node, char *Name, Uint Flags)\r
+{\r
+ return 0;\r
+}\r
+\r
+/**\r
+ * \fn int FAT_Relink(tVFS_Node *Node, char *OldName, char *NewName)\r
+ * \brief Rename / Delete a file\r
+ */\r
+int FAT_Relink(tVFS_Node *Node, char *OldName, char *NewName)\r
+{\r
+ return 0;\r
+}\r
+\r
+/**\r
+ * \fn void FAT_CloseFile(tVFS_Node *Node)\r
+ * \brief Close an open file\r
+ */\r
+void FAT_CloseFile(tVFS_Node *Node)\r
+{\r
+ if(Node == NULL) return ;\r
+ \r
+ Inode_UncacheNode(gFAT_Disks[Node->ImplInt].inodeHandle, Node->Inode);\r
+ #if USE_LFN\r
+ if( !Inode_GetCache(gFAT_Disks[Node->ImplInt].inodeHandle, Node->Inode)\r
+ && Node->Flags & VFS_FFLAG_DIRECTORY)\r
+ FAT_int_DelLFN(Node);\r
+ else // Get Cache references the node, so dereference it\r
+ Inode_UncacheNode(gFAT_Disks[Node->ImplInt].inodeHandle, Node->Inode);\r
+ #endif\r
+ return ;\r
+}\r
+\r
+/**\r
+ * \fn void fat_install()\r
+ * \brief Add the FAT Filesystem to the VFS\r
+ */\r
+void fat_install()\r
+{\r
+ VFS_AddDriver( &gFAT_FSInfo );\r
+}\r
--- /dev/null
+/*\r
+ * Acess2\r
+ * FAT12/16/32 Driver\r
+ * vfs/fs/fs_fat.h\r
+ */\r
+\r
+// === On Disk Structures ===\r
+/**\r
+ \struct fat_bootsect_s\r
+ \brief Bootsector format\r
+*/\r
+struct fat_bootsect_s {\r
+ Uint8 jmp[3]; //!< Jump Instruction\r
+ char oemname[8]; //!< OEM Name. Typically MSDOS1.1\r
+ Uint16 bps; //!< Bytes per Sector. Assumed to be 512\r
+ Uint8 spc; //!< Sectors per Cluster\r
+ Uint16 resvSectCount; //!< Number of reserved sectors at beginning of volume\r
+ Uint8 fatCount; //!< Number of copies of the FAT\r
+ Uint16 files_in_root; //!< Count of files in the root directory\r
+ Uint16 totalSect16; //!< Total sector count (FAT12/16)\r
+ Uint8 mediaDesc; //!< Media Desctiptor\r
+ Uint16 fatSz16; //!< FAT Size (FAT12/16)\r
+ Uint16 spt; //!< Sectors per track. Ignored (Acess uses LBA)\r
+ Uint16 heads; //!< Heads. Ignored (Acess uses LBA)\r
+ Uint32 hiddenCount; //!< ???\r
+ Uint32 totalSect32; //!< Total sector count (FAT32)\r
+ union {\r
+ struct {\r
+ Uint8 drvNum; //!< Drive Number. BIOS Drive ID (E.g. 0x80)\r
+ Uint8 resv; //!< Reserved byte\r
+ Uint8 bootSig; //!< Boot Signature. ???\r
+ Uint32 volId; //!< Volume ID\r
+ char label[11]; //!< Disk Label\r
+ char fsType[8]; //!< FS Type. ???\r
+ } __attribute__((packed)) fat16; //!< FAT16 Specific information\r
+ struct {\r
+ Uint32 fatSz32; //!< 32-Bit FAT Size\r
+ Uint16 extFlags; //!< Extended flags\r
+ Uint16 fsVer; //!< Filesystem Version\r
+ Uint32 rootClust; //!< Root Cluster ID\r
+ Uint16 fsInfo; //!< FS Info. ???\r
+ Uint16 backupBS; //!< Backup Bootsector Sector Offset\r
+ char resv[12]; //!< Reserved Data\r
+ Uint8 drvNum; //!< Drive Number\r
+ char resv2; //!< Reserved Data\r
+ Uint8 bootSig; //!< Boot Signature. ???\r
+ Uint32 volId; //!< Volume ID\r
+ char label[11]; //!< Disk Label\r
+ char fsType[8]; //!< Filesystem Type. ???\r
+ } __attribute__((packed)) fat32; //!< FAT32 Specific Information\r
+ }__attribute__((packed)) spec; //!< Non Shared Data\r
+ char pad[512-90]; //!< Bootsector Data (Code/Boot Signature 0xAA55)\r
+} __attribute__((packed));\r
+\r
+/**\r
+ \struct fat_filetable_s\r
+ \brief Format of a 8.3 file entry on disk\r
+*/\r
+struct fat_filetable_s {\r
+ char name[11]; //!< 8.3 Name\r
+ //char ext[3]; //!< Extention\r
+ Uint8 attrib; //!< File Attributes.\r
+ Uint8 ntres; //!< Reserved for NT - Set to 0\r
+ Uint8 ctimems; //!< 10ths of a second ranging from 0-199 (2 seconds)\r
+ Uint16 ctime; //!< Creation Time\r
+ Uint16 cdate; //!< Creation Date\r
+ Uint16 adate; //!< Accessed Data. No Time feild though\r
+ Uint16 clusterHi; //!< High Cluster. 0 for FAT12 and FAT16\r
+ Uint16 mtime; //!< Last Modified Time\r
+ Uint16 mdate; //!< Last Modified Date\r
+ Uint16 cluster; //!< Low Word of First cluster\r
+ Uint32 size; //!< Size of file\r
+} __attribute__((packed));\r
+\r
+/**\r
+ \struct fat_longfilename_s\r
+ \brief Format of a long file name entry on disk\r
+*/\r
+struct fat_longfilename_s {\r
+ Uint8 id; //!< ID of entry. Bit 6 is set for last entry\r
+ Uint16 name1[5]; //!< 5 characters of name\r
+ Uint8 attrib; //!< Attributes. Must be ATTR_LFN\r
+ Uint8 type; //!< Type. ???\r
+ Uint8 checksum; //!< Checksum\r
+ Uint16 name2[6]; //!< 6 characters of name\r
+ Uint16 firstCluster; //!< Used for non LFN compatability. Set to 0\r
+ Uint16 name3[2]; //!< Last 2 characters of name\r
+} __attribute__((packed));\r
+\r
+#define ATTR_READONLY 0x01\r
+#define ATTR_HIDDEN 0x02\r
+#define ATTR_SYSTEM 0x04\r
+#define ATTR_VOLUMEID 0x08\r
+#define ATTR_DIRECTORY 0x10\r
+#define ATTR_ARCHIVE 0x20\r
+#define ATTR_LFN (ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUMEID)\r
+\r
+/**\r
+ \enum eFatType\r
+ \brief Internal Ids for FAT types\r
+*/\r
+enum eFatType {\r
+// FAT_NULL, //!< NULL Entry\r
+ FAT12, //!< FAT12 Volume\r
+ FAT16, //!< FAT16 Volume\r
+ FAT32, //!< FAT32 Volume\r
+// FAT_LAST //!< LAST Entry. Unused\r
+};\r
+\r
+#define EOC_FAT12 0x0FFF\r
+#define EOC_FAT16 0xFFFF\r
+#define EOC_FAT32 0x0FFFFFF\r
+\r
+typedef struct fat_bootsect_s fat_bootsect;\r
+typedef struct fat_filetable_s fat_filetable;\r
+typedef struct fat_longfilename_s fat_longfilename;\r
+\r
+// === Memory Structures ===\r
+/**\r
+ \struct drv_fat_volinfo_s\r
+ \brief Representation of a volume in memory\r
+*/\r
+struct drv_fat_volinfo_s {\r
+ int fileHandle; //!< File Handle\r
+ int type; //!< FAT Type. See eFatType\r
+ char name[12]; //!< Volume Name (With NULL Terminator)\r
+ Uint32 firstDataSect; //!< First data sector\r
+ Uint32 rootOffset; //!< Root Offset (clusters)\r
+ Uint32 clusterCount; //!< Total Cluster Count\r
+ fat_bootsect bootsect; //!< Boot Sector\r
+ tVFS_Node rootNode; //!< Root Node\r
+ int inodeHandle; //!< Inode Cache Handle\r
+};\r
+\r
+typedef struct drv_fat_volinfo_s tFAT_VolInfo;\r
--- /dev/null
+/*
+ * AcessMicro VFS
+ * - Root Filesystem Driver
+ */
+#include <vfs.h>
+#include <vfs_ramfs.h>
+
+// === 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;
+}
--- /dev/null
+/*
+ * AcessMicro VFS
+ * - File IO Passthru's
+ */
+#include <common.h>
+#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;
+}
--- /dev/null
+/*
+ * Acess 2
+ * Virtual File System
+ */
+#include <common.h>
+#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;
+}
--- /dev/null
+/*
+ */
+#include <common.h>
+#include <vfs.h>
+
+// === 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;
+}
--- /dev/null
+/*
+ * Acess Micro - VFS Server version 1
+ */
+#include <common.h>
+#include <vfs.h>
+#include <vfs_int.h>
+
+// === 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;
+}
--- /dev/null
+/*
+ * AcessMicro VFS
+ * - File IO Passthru's
+ */
+#include <common.h>
+#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;
+}
--- /dev/null
+/*
+ * AcessMicro VFS
+ * - Open, Close and ChDir
+ */
+#include <common.h>
+#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;i<CFGINT(CFG_VFS_MAXFILES);i++)
+ {
+ if(gaUserHandles[i].Node) continue;
+ gaUserHandles[i].Node = node;
+ gaUserHandles[i].Position = 0;
+ gaUserHandles[i].Mode = Mode;
+ LEAVE('i', i);
+ return i;
+ }
+ }
+ else
+ {
+ // Allocate space if not already
+ if( MM_GetPhysAddr( (Uint)gaKernelHandles ) == 0 )
+ {
+ Uint addr, size;
+ size = MAX_KERNEL_FILES * sizeof(tVFS_Handle);
+ for(addr = 0; addr < size; addr += 0x1000)
+ MM_Allocate( (Uint)gaKernelHandles + addr );
+ memset( gaKernelHandles, 0, size );
+ }
+ // Get a handle
+ for(i=0;i<MAX_KERNEL_FILES;i++)
+ {
+ if(gaKernelHandles[i].Node) continue;
+ gaKernelHandles[i].Node = node;
+ gaKernelHandles[i].Position = 0;
+ gaKernelHandles[i].Mode = Mode;
+ LEAVE('x', i|VFS_KERNEL_FLAG);
+ return i|VFS_KERNEL_FLAG;
+ }
+ }
+
+ LEAVE('i', -1);
+ return -1;
+}
+
+/**
+ * \fn void VFS_Close(int FD)
+ * \brief Closes an open file handle
+ */
+void VFS_Close(int FD)
+{
+ tVFS_Handle *h;
+
+ // Get handle
+ h = VFS_GetHandle(FD);
+ if(h == NULL) {
+ Warning("Invalid file handle passed to VFS_Close, 0x%x\n", FD);
+ return;
+ }
+
+ if(h->Node->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 ];
+ }
+}