summaryrefslogtreecommitdiff
path: root/include
AgeCommit message (Collapse)Author
2018-08-26Updated the style of the code in lib/string.c to conform to the rest ofJake Mannens
the project. Added the system call 'sys_panic' as well as the corresponding library function panic() whose prototype is defined in unistd.h. This is to allow userspace programs to deliberately crash the kernel for the purpose of debugging. Added the element 'write' as a function pointer in the tty_struct structure. The purpose of this function pointer is to provide a means for the TTY subsystem to notify the device driver of any newly added data to the write queue. Added the tty_write() function to the TTY subsystem. This function will copy data provided by the user into the specified TTY device's write queue, blocking if the queue's buffer is full. tty_write() will then call the driver's write() function to notify it of new data. Added the rsint_tx() function to the serial driver to handle transmit interrupts by re-supplying the UART's transmit FIFO.
2018-08-22Added the header file limits.h which contains one definition forJake Mannens
SSIZE_MAX which is needed to limit the number of bytes read() will transfer. Laid the foundation for a TTY subsystem. This works by taking the previously used buffer and r/w pointers concept and implements it as a 'tty_queue' struct. The struct 'tty_struct' is used to represent a TTY device. This struct currently contains three elements; a read queue for data flowing from the device to the user, a write queue for data flowing from the user to the device and a function pointer to an init function. The latter element will reduce complexity by allowing the TTY subsystem to initialize each TTY device driver (when it's ready), rather than each driver having to be initialized during bootup. Each TTY device is implemented as a pointer to a tty_struct. The structures are defined separately by each driver as well as tracked and maintained by pointers in the table 'ttys' in tty.c. Modified the RS232 serial driver to make use of the new TTY subsystem for transferring data to the user. Currently, write calls are still handled manually through the rsputs() function though this will change in future. Modified the read() system call to direct read calls to the TTY subsystem which will further direct the call to the appropriate TTY device driver. The serial driver's interrupt routine now uses the wake_up() function to wake processes blocking for serial data. This is to ensure the scheduler is notified of the wakeup. This function is the preferred method for waking processes since accessing the task state field directly may not be possible in the future and is discouraged. The reason for this is that the scheduler's behaviour may change to require being notified of task wakeup events in the future.
2018-08-20Defined macros cli() and sti() in asm/interrupt.h to disable and enableJake Mannens
interrupts respectively. Added the scheduling primatives sleep_on() and interruptible_sleep_on() to sched.c. sleep_on() adds the current task to the specified wait queue then puts it into an uninterruptible sleep. Wait queues are formed by having each waiting task holding a pointer to the waiting task that preceeded it, then placing a pointer to it's own task struct into the queue's head pointer. When the first task in the queue is awoken, it wakes the preceeding task prior to returning from sleep_on(). The function interruptible_sleep_on() works in a similar manner, however when a task is awoken from interruptible_sleep_on(), it wakes up the first task in it's wait queue, then puts itself back to sleep. The purpose of this, is to automatically dissolve and reform the wait queue. As each task in the queue is a awakened, tasks that need to handle signals will do so whilst tasks that have no pending signals and are still waiting will place themselves back into the wait queue by calling interruptible_sleep_on() again. Whilst this method isn't the most effiecient due to waking every task in a wait queue because one received a signal, it does avoid the mess and complications involved in maintaining the queue as an array or linked list. Added prototypes for the scheduling primatives wake_up(), sleep_on() and interruptible_sleep_on() to kernel/sched.h so that these functions may be used throughout the kernel. Added code to temporaily re-enable interrupts in reschedule() so that the idle loop may continue to function even if interrupts were disabled prior to calling the function.
2018-08-07Fixed a bug where the panic() function called printf() instead ofJake Mannens
printk() to notify the user of the kernel panic. This resulted in a system call being made to the kernel itself and the machine not fully halting. Fixed an issue with the serial driver in which the functions rsputs() and rsread() will still attempt a data transfer even if serial_init() failed to detect and initialize a serial port. Added the ability for tasks to be interrupted whilst reading from the serial port. This was done by putting the task into TSTATE_INTERRUPTIBLE instead of TSTATE_UNINTERRUPTIBLE when waiting for data in the serial buffer. Furthermore, a check was introduced after the task wakes up to see if any data was put in the buffer, or if the task was awoken by another source. Changed the type pid_t from an unsigned 16-bit integer to a signed 16-bit integer. This was done to make passing PID's to certain functions easier. Added the new system call sys_kill which will allow one process to send a signal to another. Added the kill_proc() function to sched.c to kill a process. Currently, this works by nullifying the PID field in the process' task structure, marking all the pages mapped to it's address space as free for use, then calling the scheduler to switch to another runnable task (or to idle). Modified the default signal handler within the kernel to now handle the SIGKILL signal by calling kill_proc().
2018-08-06Defined ssize_t in unistd.h.Jake Mannens
Added the _syscall2 macro to unistd.h to facilitate system calls that require two arguments to be passed. Modified the ATA driver to simply abort initialisation if a drive is not found, or cannot be configured. This will allow the kernel to function on a diskless system without invoking panic() unnecessarily. Added the functions irq_enable() and irq_disable() to asm/interrupt.h to make it easier for C code to mask and unmask IRQ's on each PIC. Moved the declaration for rsputs() from kernel/con.h to the new kernel/serial.h file since this is a function provided by the serial driver. Implemented a basic I/O input framework. This involves the new system call sys_read, which takes an I/O read request and directs it to the appropriate kernel handler function depending on the calling process' ctty value. This mechanism is identical to the sys_puts system call. Added the rsread() function to service sys_read calls from processes whose ctty value is equal to 1. This function will continually copy data from the serial buffer to the location specified. If there is not a sufficient amount of new data in the buffer to satisfy the request, the process is put into the TSTATE_UNINTERRUPTIBLE state and the scheduler is called to switch tasks. Prior to calling the scheduler, the function will set the waiting_task pointer to the calling process. This pointer will later be used by the interrupt handler to wake the process when new data arrives. Added an interrupt handler to service the IRQ4 (UART) interrupt. This subroutine is a stub which will save the machine's state then transfer control to rs_handler() in serial.c which will read bytes from the serial port and place them in a buffer. Before returning, rs_handler() checks the waiting_task pointer to see if a task is waiting for the newly received data and if so, it sets the task's state to TSTATE_RUNNING before resetting the pointer to NULL and returning. Ideally, the scheduler should be invoked at this point to select another task but since our basic round-robin scheduler currently has no concept of task priorities (and for the sake of simplicity), we will avoid invoking the scheduler in response to interrupts for now.
2018-08-01Defined EOF as -1 in stdio.h.Jake Mannens
Implemented the sprintf() library function in lib/stdio.c which uses the vsprintf() function. Implemented a very primative controlling TTY for each process. This is achieved by a switch in the sys_puts system call which uses the 'ctty' element of the process' task structure to determine an appropriate I/O channel. A negative ctty value doesn't equate to any I/O channel effectively disabling the process' output. Added the sys_ctty system call which allows a process to set it's own ctty value. Removed the sys_rsputs system call. Output to serial is now performed by the process first setting it's ctty value to 1, then invoking sys_puts.
2018-07-30Added a new subroutine invlpg() to asm.s which simply checks the currentJake Mannens
CPU is not an i386 before executing the invlpg instruction with the provided address. This will once again make the kernel compatible with the i386 processor as executing the invlpg instruction without these checks would have resulted in an invalid opcode exception.
2018-07-30Fixed a bug where the tick_handler() and sigret() functions did not passJake Mannens
a pointer to the saved state information to check_signals(). This bug would have only manifested itself if multiple signals were to be processed (sigret) or if a signal had been set during the handling of a timer interrupt (tick_handler) and *ONLY* if switching to the user's own handler since the state information is not needed to invoke the kernel's default signal handler. Implemented alarms for userspace processes. This required significant modification of the scheduler algorithm. When idling waiting for a process that can run, the scheduler now continually checks the alarms and signals of each process and updates their state accordingly. Implemented the sys_alarm system call to set the new 'alarm' field for the calling process. Created the sys_pause system call which changes the state of the calling process to TSTATE_INTERRUPTIBLE, effectively removing it from the scheduler's run queue, putting it to sleep until a signal arrives.
2018-07-28Added new function register_trap() which creates trap gate entries inJake Mannens
the IDT. This function takes the same parameters as register_isr() which creates interrupt gate entries in the IDT. The register_isr() function now sets the gate type to 0x0E regardless of what was already in the descriptor. This is to break reliance on the IDT already being initialized to a known state as well as avoiding conflicts with the new register_trap() function. Added declaration for the 'ticks' variable in kernel/sched.h so that it's value may be used throughout the kernel. Changed the system call gate to a trap gate. This means that interrupts will not be disabled prior to entry into the system call handler. This will allow hardware functions such as the timer to operate continuously even if the user makes a system call. Added checks to the timer interrupt handler. These checks prevent the scheduler from being called if the interrupt occurred during kernel mode execution. The idea here, is that the timer interrupt handler only services the hardware (increments the tick count and sends an EOI to the PIC's) if a system call was already running in the kernel. The system call handler has also been expanded to check if the timer fired prior to returning to userspace. If the timer did fire, the syscall handler will invoke the scheduler (as the timer handler would have), so that it can decide if it's time to switch tasks.
2018-07-25Added missing prototype for puts() in stdio.h.Jake Mannens
Implemented a basic serial interface using COM0 which can be accessed with the system call sys_puts as well as the library functions rsputs() and rsprintf(). Renamed puts() in con.c to con_puts() and made the function static to avoid interference with the library function puts().
2018-07-25Fixed a bug in switch_to() in which the value of EBX was not poppedJake Mannens
prior to return. This meant that switching to the same task did not abort properly as the incorrect return address was popped off the stack. Fixed a bug where the task register was not initialized before the scheduler. This meant that on the first task switch, the CPU would dump it's current state to a random address (0 most likely), potentially corrupting important data. This has been corrected by introducing a 'garbage TSS' (and associated descriptor in the GDT) which is selected before the scheduler is initialized as a safe place for the data to be written. Modified the scheduler so that it now waits indefinitely until a task becomes ready to run. This fixes the possible bug where the scheduler won't re-schedule the currently running task if it is the only task on the system. Add signal handling capabilities to the kernel. The bulk of this is present in the subroutine check_signals() defined in traps.s. This function is called on every timer tick and system call prior to userspace return. The subroutine operates by pushing fake state information onto the kernel's stack, then using it to return to userspace. Prior to this, the subroutine pushes the return address 0xFFFFE000 onto the user's stack. This address corresponds to the unmapped page located between the top of the user's stack (lower) and the kernel's stack page (upper). When the user's signal handler tries to return, it will cause a page fault that will be used as a notification mechanism to inform the kernel that the signal handler is done. The kernel will then switch to the originally pushed state information and use it to return the task to the original execution state. Due to it's nature, check_signals() must only be called prior to exiting the kernel since it may not return. Added the header file 'signal.h' which defines (most) of the POSIX signals as well as the prototype for the signal() function. Added the 'signal' element to the task structure. This field is a bitmap of all currently pending signals. Added the 'sig_handlers' element to the task structure. This is an array of all user-defined signal handlers. Currently, a value of 0 indicates the default handler should be used whilst any other value is considered to be the address of a userspace signal handler. The ability to ignore a signal is not yet present but will be added sometime soon. Added the sys_signal system call to register a signal. Added the stub function sighandler_default() to sched.c which handles all signals not caught by the user.
2018-07-16Included the necessary stdint.h in kernel/hd.h. That header file may nowJake Mannens
be used freely throughout the kernel.
2018-07-14con_init() is now called during the kernel's boot sequence in kboot()Jake Mannens
rather than in kmain() as some subsystems may now require early console I/O. Added 16-bit read/write I/O functions to asm/io.h. These functions are inw() and outw() respectively. Added the file kernel/fs.h which will contain definitions relating to filesystem functions. Defined the type off_t as a signed 32-bit value in sys/types.h. This type will be required for filesystem functionality. Added the directory 'kernel/fs' to the project's source tree. The kernel's makefile has been updated accordingly. This directory will contain any source files relating to filesystem functionality (both assembly and C files). Added the file 'fs/hd.c' to the kernel's source tree. This file currently contains three main functions (which are defined in kernel/hd.h). These functions are as follows; hd_init() to enumerate and initialize the hard disks, hd_read() to read sectors from the disk and hd_write() to write sectors to the disk. Currently, all transfers are done in ATA PIO mode using polling, however this will change in future. The function hd_init() is called during the kernel's boot sequence in kboot(). Added the file hd.img to the project's root directory. This is a 20MB raw image file that will be used by Qemu as a 20MB hard disk. The main makefile has been updated to tell Qemu to use this file on launch.
2018-07-13Added a state field to the task structure to hold the task's run state.Jake Mannens
Now, reschedule() requires a task to be in the TSTATE_RUNNING state for it to run. Renamed the TSS structure within the task structure from 'state' to 'tss' to avoid confusion with the task's run state. Removed the task_state structure declaration from sched.c as it is no longer needed due to context switches now being performed entirely in hardware. Removed the cstate pointer from sched.c. Interrupt handlers no longer set a pointer to saved state information on the stack since this was only needed for software task switching. NOTE: This may be re-introduced should it become necessary to access state information on the stack. Added a basic implementation of the wake_up() scheduling primative. This function is currently not used and may be completely re-written in future.
2018-07-12Task state information is no longer manually saved on context switch.Jake Mannens
Since the kernel now has it's own stack unique to each address space, we can now rely on hardware task switching to *also* save the task states. To accomplish this, most of the code in switch_to() has been elimated. This includes; the clearing of the busy flag in the old TSS on each switch, setting the TR register to null prior to each switch and calling save_state() (which has also been removed entirely), to copy the state information. Modified the for loop in reschedule() to account for the fact that the switch_to() function may now return (which it *always* does when returning to the task). For the same reason, switch_to() must also be careful to preserve registers such as EBX and actually make a return following the far jump. Added basic definitions for task states in sched.h. These are; running, interruptible, uninterruptible, zombie and stopped. These states will (possibly) be used in the future to implement blocking system calls.
2018-07-11Implemented a basic scheduler using an array of task structures in whichJake Mannens
each structure has a TSS inside, as well as several descriptor entries in the GDT pointing to each task's TSS. Task switching is performed in hardware by means of far jumping to the new task's TSS descriptor in the GDT. Task states are saved manually by copying the saved state from the interrupt handler's stack, back into the task's TSS. Added the function save_state() to sched.c which can be called from assembly to copy the state information of the suspended task from the current stack, to it's respective task structure. NOTE: this assumes that the pointer cstate has been set ahead of time to point to the state information on the stack, and that the pointer ctask has not yet been modified. Added the function reschedule() which is currently called from the timer's tick handler. Currently, this function merely alternates between the two tasks created in sched_init(), but in future, will be expanded to decided independently which task will run next. The function userspace_init() was renamed to the more appropriate sched_init(). Moved most of the code from the original userspace_init() to the new function create_proc() which sets up a virtual address space and state information, copies the specified binary into the new address space, then returns a pointer to the newly created tasks entry in the task table. Defined the type pid_t in unistd.h as a 16-bit unsigned integer. Added two new debugging system calls for checking which task is currently active. The getpid() system calls returns the caller's PID whilst the getpdir() call returns the physical location of the caller's page directory (the value of CR3). The getpdir() call can be used as a fallback to determine which task is *actually* running if the information in ctask or the getpid() call is faulty. Added two new subroutines; set_tss() and clear_tss() (whose prototypes are defined in asm/system.h) for managing the task-state descriptor entries in the GDT. set_tss() initializes a TSS descriptor and sets it's base pointer to the supplied TSS pointer whilst clear_tss() simply marks a TSS descriptor as 'not present' without clearing it.
2018-07-08Moved the 'ticks' variable from kmain.c to timer.s.Jake Mannens
Added the prototype for userspace_init() to sched.h removing the need to the extern declaration in kmain.c.
2018-07-08Added a printf() function to the library under stdio.c which uses theJake Mannens
vsprintf() function to render formatted strings and then the puts system call to output them. Moved the vsprintf() function from the kernel to the library. Furthermore, the prototype for the function has been moved from the kernel's headers, to the new header file stdio.h. Renamed the kernel's internal printf() function to printk() in order to avoid confusion with the library provided function. Renamed the sys_print system call to the more appropriate name, sys_puts. Added a new system call sys_time, which returns the system's uptime in seconds. This is mainly for testing the userspace binary and will not be permanent. Added the file time.c to the library which contains the caller for sys_time and a helper routine sleep() which delays execution for the specified number of seconds. The new header file time.h contains prototypes for both these functions as well as the definition for the type time_t. Fixed a bug in which the value of EAX was not properly passed to the system call handler, resulting in the wrong system call being executed. This was caused by the code in the SAVE macro not properly preserving the value. Fixed a bug in which the value of EAX was not preserved during a return from system call, but rather restored with the original EAX value prior to the call. As a result, system call return codes were not properly passed. This has been corrected by introducing a new macro RESTORE_SYS which carries out the same restore operations, but maintains EAX prior to the return.
2018-07-07Added '-g' flag for GCC to all makefiles to ensure debugging informationJake Mannens
is produced. This may change later. Added the new directory 'lib' to the source tree which build lib.a, an archive containing common library routines for both the kernel and userspace code to use. Added the file string.c to the lib directory (as well as the appropriate headers in /include) which provides some basic functions from the standard C string library. Added a physical memory manager which is now located in memory.c. This memory manager tracks free pages from 1MB-8MB with a simple table and allocates memory in blocks of 4KB pages. Multiple pages can be allocated in which they are returned as a linked list. Added a 'page window' in memory.c which allows the temporary mapping of a single page at a time into the current address space. Moved all paging routines that were previously located in page.s over to memory.c where they have been re-implemented as a mixture of C and inline assembly. Moved the primative userspace routines from usrspace.s over to the new sched.c. The only remaining routine, usrcall is now located in asm.s as 'switch_to' which takes two arguments, pointers to the task structure and task state structure of the new task which is being switched to. Pages for userspace are now allocated dynamically. The user binary is loaded in at 1GB upwards. The user stack is located at the end of the 4GB address space with the lower 1GB being reserved for the kernel. Updated the link.ld file for the userspace binary to include the new starting address 0x40000000 (1GB). Renamed the symbols for the user binary blob to make them shorter.
2018-06-25Added the header asm/interrupt.h which includes a prototype for theJake Mannens
assembly function register_isr making it usable within the C portions of the source. Added a new file panic.s with the function panic that will print a panic message, disable interrupts and halt the system. Created the skeleton framework for paging in the new file page.s. The new function paging_init (called in kboot) will setup a simple page directory with two tables covering all addresses 0-8MB. It will also mark pages from 0-1MB as 'supervisor-only' to protect the kernel. NOTE: The function paging_init must be called before initialising the IDT as it does not disable interrupts! Modified the page fault handler to print the offending linear address along with the supplied error code. Following that, the handler will initiate a kernel panic. This function (along with panic) assumes console I/O to be operational. Modified the userspace test code to deliberately intiate a page fault by accessing an unmapped page.
2018-06-24Modified makefiles to use the more appropriate variable $(MAKE) whenJake Mannens
invoking the tool recursively. Disabled GCC's position-independent-code generation in makefiles. Modified makefile for kernel/usrbin so that it now compiles and links C code into the userspace test. Created errno.h and populated it with standard error definitions. Replaced the va_list based system call handlers with a system call table defined in the header kernel/sys.h. NOTE: This header is included in kmain.c and should ONLY be included there! Do NOT include this header in sys.c. Rather than fetching the user's stack pointer and using it to initialize a va_list, parameters are now passed to the call handlers via the general purpose registers EAX, EBX, ECX and EDX where EAX contains the requested call number and conveys the return value. Setup macros in unistd.h to aid to making system calls from userspace. Implemented two basic system calls; sys_print and sys_dummy. The former takes a single char* argument and displays it on screen whilst the latter is used to populate otherwise empty entries of the system call table. sys_dummy returns the error ENOSYS whenever it is called.
2018-06-20Added a very basic types.h header file that defines NULL, size_t andJake Mannens
time_t. Moved the kernel's loading point down to address 0 in conventional memory and updated linker scripts accordingly. Began to experiment with loading a binary blob into extended memory (0x100000), switching to userspace mode, and executing it.
2018-06-19Added foundation code to initialize the PIT and create a 10ms jiffiesJake Mannens
counter.
2018-06-17Cleaned up console I/O functions in con.c and con.h respectively.Jake Mannens
2018-06-16Initial commitJake Mannens