move old kernel code (for now) into kernel/old, trying to get long mode

This commit is contained in:
Murphy 2025-04-03 12:31:21 -04:00
parent ec3c37d1d4
commit a524eb3846
Signed by: freya
GPG key ID: 9FBC6FFD6D2DBF17
51 changed files with 5132 additions and 847 deletions

View file

@ -5,7 +5,7 @@
UNAME := $(shell uname)
QEMU = qemu-system-i386
QEMU = qemu-system-x86_64
QEMUOPTS = -drive file=bin/disk.img,index=0,media=disk,format=raw \
-no-reboot \
-serial mon:stdio \
@ -44,8 +44,8 @@ bin/user.img: build
cd bin && \
./mkblob init idle prog* shell
bin/disk.img: bin/kernel.bin bin/boot.bin bin/user.img
bin/disk.img: bin/kernel.bin bin/boot.bin
cd bin && \
./BuildImage -d usb -o disk.img -b boot.bin \
kernel.bin 0x10000 user.img 0x30000
kernel.bin 0x10000

213
build.zig
View file

@ -20,59 +20,38 @@ const c_flags = &[_][]const u8{
"-ggdb",
};
const ld_flags = &[_][]const u8{
"-nmagic",
"-nostdlib",
"--no-warn-rwx-segments",
};
const boot_src = &[_][]const u8{"boot/boot.S"};
const kernel_src = &[_][]const u8{
"kernel/startup.S", // must be first
"kernel/cio.c",
"kernel/clock.c",
"kernel/isrs.S",
"kernel/entry.S", // must be first
"kernel/kernel.c",
"kernel/kmem.c",
"kernel/list.c",
"kernel/procs.c",
"kernel/sio.c",
"kernel/support.c",
"kernel/syscalls.c",
"kernel/user.c",
"kernel/vm.c",
"kernel/vmtables.c",
"lib/klibc.c",
};
const lib_src = &[_][]const u8{
"lib/blkmov.c",
"lib/atox.c",
"lib/bound.c",
"lib/cvtdec0.c",
"lib/cvtdec.c",
"lib/cvthex.c",
"lib/cvtoct.c",
"lib/cvtuns0.c",
"lib/cvtuns.c",
"lib/memclr.c",
"lib/btoa.c",
"lib/ctoi.c",
"lib/delay.c",
"lib/isdigit.c",
"lib/isspace.c",
"lib/itoc.c",
"lib/memcmp.c",
"lib/memcpy.c",
"lib/memmove.c",
"lib/memset.c",
"lib/pad.c",
"lib/padstr.c",
"lib/sprint.c",
"lib/str2int.c",
"lib/printf.c",
"lib/stpcpy.c",
"lib/stpncpy.c",
"lib/strcat.c",
"lib/strcmp.c",
"lib/strcpy.c",
"lib/strlen.c",
};
const ulib_src = &[_][]const u8{
"lib/entry.S",
"lib/ulibc.c",
"lib/ulibs.S",
"lib/strncmp.c",
"lib/strncpy.c",
"lib/strtoux.c",
"lib/strtox.c",
"lib/uxtoa.c",
"lib/xtoa.c",
};
const Prog = struct {
@ -98,114 +77,6 @@ const util_progs = &[_]Prog{
},
};
const user_progs = &[_]Prog{
// idle
Prog{
.name = "idle",
.source = &[_][]const u8{"user/idle.c"},
},
// init
Prog{
.name = "init",
.source = &[_][]const u8{"user/init.c"},
},
// progABC
Prog{
.name = "progABC",
.source = &[_][]const u8{"user/progABC.c"},
},
// progDE
Prog{
.name = "progDE",
.source = &[_][]const u8{"user/progDE.c"},
},
// progFG
Prog{
.name = "progFG",
.source = &[_][]const u8{"user/progFG.c"},
},
// progH
Prog{
.name = "progH",
.source = &[_][]const u8{"user/progH.c"},
},
// progI
Prog{
.name = "progI",
.source = &[_][]const u8{"user/progI.c"},
},
// progJ
Prog{
.name = "progJ",
.source = &[_][]const u8{"user/progJ.c"},
},
// progKL
Prog{
.name = "progKL",
.source = &[_][]const u8{"user/progKL.c"},
},
// progKL
Prog{
.name = "progKL",
.source = &[_][]const u8{"user/progKL.c"},
},
// progMN
Prog{
.name = "progMN",
.source = &[_][]const u8{"user/progMN.c"},
},
// progP
Prog{
.name = "progP",
.source = &[_][]const u8{"user/progP.c"},
},
// progQ
Prog{
.name = "progQ",
.source = &[_][]const u8{"user/progQ.c"},
},
// progR
Prog{
.name = "progR",
.source = &[_][]const u8{"user/progR.c"},
},
// progS
Prog{
.name = "progS",
.source = &[_][]const u8{"user/progS.c"},
},
// progTUV
Prog{
.name = "progTUV",
.source = &[_][]const u8{"user/progTUV.c"},
},
// progW
Prog{
.name = "progW",
.source = &[_][]const u8{"user/progW.c"},
},
// progX
Prog{
.name = "progX",
.source = &[_][]const u8{"user/progX.c"},
},
// progY
Prog{
.name = "progY",
.source = &[_][]const u8{"user/progY.c"},
},
// progZ
Prog{
.name = "progZ",
.source = &[_][]const u8{"user/progZ.c"},
},
// shell
Prog{
.name = "shell",
.source = &[_][]const u8{"user/shell.c"},
},
};
const AddSourcesOpts = struct { exe: *std.Build.Step.Compile, sources: []const []const []const u8, c_flags: []const []const u8 };
fn add_sources(b: *std.Build, opts: AddSourcesOpts) void {
@ -231,6 +102,7 @@ const BuildKernBinaryOpts = struct {
linker: ?[]const u8 = null,
entry: []const u8 = "_start",
strip: bool = false,
include: ?[]const u8 = null,
};
fn build_kern_binary(b: *std.Build, opts: BuildKernBinaryOpts) void {
@ -242,8 +114,13 @@ fn build_kern_binary(b: *std.Build, opts: BuildKernBinaryOpts) void {
.strip = opts.strip,
});
// add include path
// add include paths
exe.addIncludePath(b.path("include/"));
if (opts.include != null) {
exe.addIncludePath(b.path(opts.include.?));
}
// set entry
exe.entry = .{ .symbol_name = opts.entry };
// add asm and c source files
@ -297,56 +174,46 @@ fn build_native_binary(b: *std.Build, opts: BuildNativeBinaryOpts) void {
pub fn build(b: *std.Build) !void {
// context
const target = b.standardTargetOptions(.{
.default_target = .{
.cpu_arch = std.Target.Cpu.Arch.x86,
.os_tag = std.Target.Os.Tag.freestanding,
.abi = std.Target.Abi.gnu,
.ofmt = std.Target.ObjectFormat.elf,
},
const target32 = std.Build.resolveTargetQuery(b, .{
.cpu_arch = std.Target.Cpu.Arch.x86,
.os_tag = std.Target.Os.Tag.freestanding,
.abi = std.Target.Abi.gnu,
.ofmt = std.Target.ObjectFormat.elf,
});
const target64 = std.Build.resolveTargetQuery(b, .{
.cpu_arch = std.Target.Cpu.Arch.x86_64,
.os_tag = std.Target.Os.Tag.freestanding,
.abi = std.Target.Abi.gnu,
.ofmt = std.Target.ObjectFormat.elf,
});
const optimize = std.builtin.OptimizeMode.ReleaseFast;
// boot
build_kern_binary(b, .{
.name = "boot",
.target = target,
.target = target32,
.optimize = optimize,
.sources = &.{
boot_src,
},
.linker = "boot/boot.ld",
.entry = "bootentry",
.include = "boot/include",
});
// kernel
build_kern_binary(b, .{
.name = "kernel",
.target = target,
.target = target64,
.optimize = optimize,
.sources = &.{
kernel_src,
lib_src,
},
.linker = "kernel/kernel.ld",
.include = "kernel/include",
});
// user_progs
for (user_progs) |prog| {
build_kern_binary(b, .{
.name = prog.name,
.target = target,
.optimize = optimize,
.sources = &.{
prog.source,
lib_src,
ulib_src,
},
.linker = "user/user.ld",
.strip = true,
});
}
// util_progs
for (util_progs) |prog| {
build_native_binary(b, .{

View file

@ -1,6 +1,7 @@
-c
-std=c99
-Iinclude
-Ikernel/include
-ffreestanding
-fno-builtin
-Wall

158
kernel/entry.S Normal file
View file

@ -0,0 +1,158 @@
.globl _start
.globl kernel_pml4
.globl kernel_pdpt_0
.globl kernel_pd_0
.globl kernel_pt_0
.globl paging_pt
.globl bootstrap_pt
.extern main
.extern GDT
.section .bss
# kernel page tables
.align 4096
kernel_pml4: # reserve memory for initial 512 pml4 entires
.skip 4096
kernel_pdpt_0: # reserve memory for initial 512 pdpt entires
.skip 4096
kernel_pd_0: # reserve memory for initial 512 pd entries
.skip 4096
kernel_pt_0: # reserve memory for initial 512 pt entries
.skip 4096
paging_pt: # reserve pages for pager mappings
.skip 4096
bootstrap_pt: # reserve pages to bootstrap pager
.skip 4096
# kernel stack
.align 16
kern_stack_start:
.skip 8192
kern_stack_end:
.section .rodata
.align 16
# access bits
.set PRESENT, 1 << 7
.set NOT_SYS, 1 << 4
.set EXEC, 1 << 3
.set DC, 1 << 2
.set RW, 1 << 1
.set ACCESSED, 1 << 0
# flag bits
.set GRAN_4K, 1 << 7
.set SZ_32, 1 << 6
.set LONG_MODE, 1 << 5
# kernel gdt (long mode)
GDT:
GDT.Null:
.quad 0
GDT.Code:
.byte 0
.byte PRESENT | NOT_SYS | EXEC | RW
.byte GRAN_4K | LONG_MODE | 0xF
.byte 0
GDT.Data:
.long 0xFFFF
.byte 0
.byte PRESENT | NOT_SYS | RW
.byte GRAN_4K | SZ_32 | 0xF
.byte 0
GDT.TSS:
.long 0x00000068
.long 0x00CF8900
GDT.Pointer:
.short . - GDT - 1
.quad GDT - .
.section .text
.code32
_start:
# enable interrupts
cli
# setup stack
movl $kern_stack_end, %esp
movl $kern_stack_end, %ebp
# save multiboot (if using multiboot)
pushl $0
push %ebx
pushl $0
push %eax
# zero out kernel page table
movl $0x1000, %edi # kernel is loaded at 0x1000
movl %edi, %cr3
xorl %eax, %eax
movl $4096, %ecx # zero 4096 pages
rep stosl
movl %cr3, %edi
# identity map kernel
movl $kernel_pdpt_0 + 3, (%edi) # Set the uint32_t at the desination index to 0x2003.
movl $kernel_pdpt_0, %edi # Add 0x1000 to the desination index.
movl $kernel_pd_0 + 3, (%edi) # Set the uint32_t at the desination index to 0x3003.
movl $kernel_pd_0, %edi # Add 0x1000 to the desination index.
movl $kernel_pt_0 + 3, (%edi) # Set the uint32_t at the desination index to 0x4003.
movl $kernel_pt_0, %edi # Add 0x1000 to the desination index.
movl $0x00000003, %ebx # Entry value to set
movl $512, %ecx # Loop count
set_entry:
# set entires in mapping
movl %ebx, (%edi) # Set the uint32_t at the desination index to the B-register
addl $0x1000, %ebx # Add 0x1000 to the B-register
addl $8, %edi # Add eight to the desination index
loop set_entry
# enable page address extension
movl %cr4, %eax
orl $(1 << 5), %eax
movl %eax, %cr4
# enable long mode
movl $0xC0000080, %eax
rdmsr
orl $(1 << 8), %eax
wrmsr
# enable paging
movl %cr0, %eax
orl $(1 << 31), %eax
movl %eax, %cr0
# load gdt
lgdt GDT.Pointer
ljmp $16, $code64
.code64
code64:
movw $16, %dx # set segment registers
movw %dx, %ds
movw %dx, %ss
xorq %rbp, %rbp # set ebp to 0 so we know where to end stack traces
pop %rdi # pop possible multiboot header
pop %rsi
sti
call main
cli
halt:
hlt
jmp halt

View file

@ -0,0 +1 @@
#include <memory/mapping.h>

View file

@ -0,0 +1,20 @@
/**
* @file memory.h
*
* @author Freya Murphy
*
* Kernel memory declarations
*/
#ifndef _MEMORY_MAPPING_H
#define _MEMORY_MAPPING_H
// paging
#define PAGE_SIZE 4096
#define PAGE_PRESENT 0x1
#define PAGE_WRITE 0x2
#define PAGE_USER 0x4
#define PAGE_HUGE 0x80
#define PAGE_GLOBAL 0x100
#endif

View file

@ -1,400 +1,5 @@
/**
** @file kernel.c
**
** @author CSCI-452 class of 20245
**
** @brief Kernel support routines
*/
#define KERNEL_SRC
#include <common.h>
#include <cio.h>
#include <clock.h>
#include <kmem.h>
#include <procs.h>
#include <sio.h>
#include <syscalls.h>
#include <user.h>
#include <userids.h>
#include <vm.h>
/*
** PRIVATE DEFINITIONS
*/
/*
** PRIVATE DATA TYPES
*/
/*
** PRIVATE GLOBAL VARIABLES
*/
/*
** PUBLIC GLOBAL VARIABLES
*/
// character buffers, usable throughout the OS
// nto guaranteed to retain their contents across an exception return
char b256[256]; // primarily used for message creation
char b512[512]; // used by PANIC macro
/*
** PRIVATE FUNCTIONS
*/
/*
** PRIVATE FUNCTIONS
*/
/**
** report - report the system configuration
**
** Prints configuration information about the OS on the console monitor.
**
** @param dtrace Decode the TRACE options
*/
static void kreport(bool_t dtrace)
{
cio_puts("\n-------------------------------\n");
cio_printf("Config: N_PROCS = %d", N_PROCS);
cio_printf(" N_PRIOS = %d", N_PRIOS);
cio_printf(" N_STATES = %d", N_STATES);
cio_printf(" CLOCK = %dHz\n", CLOCK_FREQ);
// This code is ugly, but it's the simplest way to
// print out the values of compile-time options
// without spending a lot of execution time at it.
cio_puts("Options: "
#ifdef RPT_INT_UNEXP
" R-uint"
#endif
#ifdef RPT_INT_MYSTERY
" R-mint"
#endif
#ifdef TRACE_CX
" CX"
#endif
#ifdef CONSOLE_STATS
" Cstats"
#endif
); // end of cio_puts() call
#ifdef SANITY
cio_printf(" SANITY = %d", SANITY);
#endif
#ifdef STATUS
cio_printf(" STATUS = %d", STATUS);
#endif
#if TRACE > 0
cio_printf(" TRACE = 0x%04x\n", TRACE);
// decode the trace settings if that was requested
if (TRACING_SOMETHING && dtrace) {
// this one is simpler - we rely on string literal
// concatenation in the C compiler to create one
// long string to print out
cio_puts("Tracing:"
#if TRACING_PCB
" PCB"
#endif
#if TRACING_VM
" VM"
#endif
#if TRACING_QUEUE
" QUE"
#endif
#if TRACING_SCHED
" SCHED"
#endif
#if TRACING_DISPATCH
" DISPATCH"
#endif
#if TRACING_SYSCALLS
" SCALL"
#endif
#if TRACING_SYSRETS
" SRET"
#endif
#if TRACING_EXIT
" EXIT"
#endif
#if TRACING_INIT
" INIT"
#endif
#if TRACING_KMEM
" KM"
#endif
#if TRACING_KMEM_FREELIST
" KMFL"
#endif
#if TRACING_KMEM_INIT
" KMIN"
#endif
#if TRACING_FORK
" FORK"
#endif
#if TRACING_EXEC
" EXEC"
#endif
#if TRACING_SIO_STAT
" S_STAT"
#endif
#if TRACING_SIO_ISR
" S_ISR"
#endif
#if TRACING_SIO_RD
" S_RD"
#endif
#if TRACING_SIO_WR
" S_WR"
#endif
#if TRACING_USER
" USER"
#endif
#if TRACING_ELF
" ELF"
#endif
); // end of cio_puts() call
}
#endif /* TRACE > 0 */
cio_putchar('\n');
}
#if defined(CONSOLE_STATS)
/**
** stats - callback routine for console statistics
**
** Called by the CIO module when a key is pressed on the
** console keyboard. Depending on the key, it will print
** statistics on the console display, or will cause the
** user shell process to be dispatched.
**
** This code runs as part of the CIO ISR.
*/
static void stats(int code)
{
switch (code) {
case 'a': // dump the active table
ptable_dump("\nActive processes", false);
break;
case 'c': // dump context info for all active PCBs
ctx_dump_all("\nContext dump");
break;
case 'p': // dump the active table and all PCBs
ptable_dump("\nActive processes", true);
break;
case 'q': // dump the queues
// code to dump out any/all queues
pcb_queue_dump("R", ready, true);
pcb_queue_dump("W", waiting, true);
pcb_queue_dump("S", sleeping, true);
pcb_queue_dump("Z", zombie, true);
pcb_queue_dump("I", sioread, true);
break;
case 'r': // print system configuration information
report(true);
break;
// ignore CR and LF
case '\r': // FALL THROUGH
case '\n':
break;
default:
cio_printf("console: unknown request '0x%02x'\n", code);
// FALL THROUGH
case 'h': // help message
cio_puts("\nCommands:\n"
" a -- dump the active table\n"
" c -- dump contexts for active processes\n"
" h -- this message\n"
" p -- dump the active table and all PCBs\n"
" q -- dump the queues\n"
" r -- print system configuration\n");
break;
}
}
#endif
/*
** PUBLIC FUNCTIONS
*/
/**
** main - system initialization routine
**
** Called by the startup code immediately before returning into the
** first user process.
**
** Making this type 'int' keeps the compiler happy.
*/
int main(void)
{
/*
** BOILERPLATE CODE - taken from basic framework
**
** Initialize interrupt stuff.
*/
init_interrupts(); // IDT and PIC initialization
/*
** Console I/O system.
**
** Does not depend on the other kernel modules, so we can
** initialize it before we initialize the kernel memory
** and queue modules.
*/
#if defined(CONSOLE_STATS)
cio_init(stats);
#else
cio_init(NULL); // no console callback routine
#endif
cio_clearscreen(); // wipe out whatever is there
/*
** TERM-SPECIFIC CODE STARTS HERE
*/
/*
** Initialize various OS modules
**
** Other modules (clock, SIO, syscall, etc.) are expected to
** install their own ISRs in their initialization routines.
*/
cio_puts("System initialization starting.\n");
cio_puts("-------------------------------\n");
cio_puts("Modules:");
// call the module initialization functions, being
// careful to follow any module precedence requirements
km_init(); // MUST BE FIRST
#if TRACING_KMEM || TRACING_KMEM_FREE
delay(DELAY_2_SEC); // approximately
#endif
// other module initialization calls here
clk_init(); // clock
pcb_init(); // process (PCBs, queues, scheduler)
#if TRACING_PCB
delay(DELAY_2_SEC);
#endif
sio_init(); // serial i/o
sys_init(); // system call
#if TRACING_SYSCALLS || TRACING_SYSRETS
delay(DELAY_2_SEC);
#endif
vm_init(); // virtual memory
user_init(); // user code handling
cio_puts("\nModule initialization complete.\n");
// report our configuration options
kreport(true);
cio_puts("-------------------------------\n");
delay(DELAY_2_SEC);
/*
** Other tasks typically performed here:
**
** Enabling any I/O devices (e.g., SIO xmit/rcv)
*/
/*
** Create the initial user process
**
** This code is largely stolen from the fork() and exec()
** implementations in syscalls.c; if those change, this must
** also change.
*/
// if we can't get a PCB, there's no use continuing!
assert(pcb_alloc(&init_pcb) == SUCCESS);
// fill in the necessary details
init_pcb->pid = PID_INIT;
init_pcb->state = STATE_NEW;
init_pcb->priority = PRIO_HIGH;
// find the 'init' program
prog_t *prog = user_locate(Init);
assert(prog != NULL);
// command-line arguments for 'init'
const char *args[2] = { "init", NULL };
// load it
assert(user_load(prog, init_pcb, args, true) == SUCCESS);
// send it on its merry way
schedule(init_pcb);
dispatch();
#ifdef TRACE_CX
// if we're using a scrolling region, wait a bit more and then set it up
delay(DELAY_7_SEC);
// define a scrolling region in the top 7 lines of the screen
cio_setscroll(0, 7, 99, 99);
// clear it
cio_clearscroll();
// clear the top line
cio_puts_at(
0, 0,
"* ");
// separator
cio_puts_at(
0, 6,
"================================================================================");
#endif
/*
** END OF TERM-SPECIFIC CODE
**
** Finally, report that we're all done.
*/
cio_puts("System initialization complete.\n");
cio_puts("-------------------------------\n");
sio_enable(SIO_RX);
#if 0
// produce a "system state" report
cio_puts( "System status: Queues " );
pcb_queue_dump( "R", ready, true );
pcb_queue_dump( "W", waiting, true );
pcb_queue_dump( "S", sleeping, true );
pcb_queue_dump( "Z", zombie, true );
pcb_queue_dump( "I", sioread, true );
ptable_dump_counts();
pcb_dump( "Current: ", current, true );
delay( DELAY_3_SEC );
vm_print( current->pdir, true, TwoLevel );
delay( DELAY_3_SEC );
#endif
return 0;
void main(void) {
while (1) ;
}

View file

@ -1,71 +1,30 @@
/*
** Simple linker script for the 20245 kernel.
*/
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
OUTPUT_ARCH(i386)
ENTRY(_start)
SECTIONS
{
/* Link the kernel at this address. */
/* Must match what is defined in vm.h! */
. = 0x80010000;
. = 1M;
.text : AT(0x10000) {
.text : {
*(.text .stub .text.* .gnu.linkonce.t.*)
}
/* standard symbols */
PROVIDE(etext = .);
PROVIDE(_etext = .);
/* put read-only data next */
.rodata : {
*(.rodata .rodata.* .gnu.linkonce.r.*)
}
/* Could put STABs here */
/*
.stab : {
PROVIDE(__STAB_BEGIN__ = .);
*(.stab);
PROVIDE(__STAB_END__ = .);
}
.stabstr : {
PROVIDE(__STABSTR_BEGIN__ = .);
*(.stabstr);
PROVIDE(__STABSTR_END__ = .);
}
*/
/* Align the data segment at the next page boundary */
. = ALIGN(0x1000);
PROVIDE(data = .);
PROVIDE(_data = .);
/* The data segment */
.data : {
*(.data .data.*)
}
PROVIDE(edata = .);
PROVIDE(_edata = .);
/* page-align the BSS */
. = ALIGN(0x1000);
PROVIDE(__bss_start = .);
.bss : {
*(.bss .bss.*)
*(COMMON)
*(.bss .bss.*)
}
PROVIDE(end = .);
PROVIDE(_end = .);
/DISCARD/ : {
*(.stab .stab_info .stabstr)
*(.eh_frame .eh_frame_hdr)

View file

@ -1,11 +1,3 @@
/**
** @file clock.c
**
** @author CSCI-452 class of 20245
**
** @brief Clock module implementation
*/
#define KERNEL_SRC
#include <common.h>

View file

@ -1,64 +1,3 @@
/**
** @file sio.c
**
** @author Warren R. Carithers
**
** @brief SIO module
**
** For maximum compatibility from semester to semester, this code uses
** several "stand-in" type names and macros which should be defined
** in the accompanying "compat.h" header file if they're not part of
** the baseline system:
**
** standard-sized integer types: intN_t, uintN_t
** other types: PCBTYPE, QTYPE
** scheduler functions: SCHED, DISPATCH
** queue functions: QCREATE, QLENGTH, QDEQUE
** other functions: SLENGTH
** sio read queue: QNAME
**
** Our SIO scheme is very simple:
**
** Input: We maintain a buffer of incoming characters that haven't
** yet been read by processes. When a character comes in, if
** there is no process waiting for it, it goes in the buffer;
** otherwise, the first waiting process is awakeneda and it
** gets the character.
**
** When a process invokes readch(), if there is a character in
** the input buffer, the process gets it; otherwise, it is
** blocked until input appears
**
** Communication with system calls is via two routines.
** sio_readc() returns the first available character (if
** there is one), resetting the input variables if this was
** the last character in the buffer. If there are no
** characters in the buffer, sio_read() returns a -1
** (presumably so the requesting process can be blocked).
**
** sio_read() copies the contents of the input buffer into
** a user-supplied buffer. It returns the number of characters
** copied. If there are no characters available, return a -1.
**
** Output: We maintain a buffer of outgoing characters that haven't
** yet been sent to the device, and an indication of whether
** or not we are in the middle of a transmit sequence. When
** an interrupt comes in, if there is another character to
** send we copy it to the transmitter buffer; otherwise, we
** end the transmit sequence.
**
** Communication with user processes is via three functions.
** sio_writec() writes a single character; sio_write()
** writes a sized buffer full of characters; sio_puts()
** prints a NUL-terminated string. If we are in the middle
** of a transmit sequence, all characters will be added
** to the output buffer (from where they will be sent
** automatically); otherwise, we send the first character
** directly, add the rest of the characters (if there are
** any) to the output buffer, and set the "sending" flag
** to indicate that we're expecting a transmitter interrupt.
*/
#define KERNEL_SRC
// this should do all includes required for this OS

View file

@ -0,0 +1,73 @@
/**
* @file bindings.h
* @author Freya Murphy <freya@freyacat.org>
*
* C-style function definitions binded to their named assembly instruction.
*/
#ifndef _BINDINGS_H
#define _BINDINGS_H
#include <types.h>
static inline uint8_t inb(uint16_t port)
{
uint8_t ret;
__asm__ volatile("inb %1, %0" : "=a"(ret) : "Nd"(port));
return ret;
}
static inline void outb(uint16_t port, uint8_t val)
{
__asm__ volatile("outb %0, %1" : : "a"(val), "Nd"(port));
}
static inline uint16_t inw(uint16_t port)
{
uint16_t ret;
__asm__ volatile("inw %1, %0" : "=a"(ret) : "Nd"(port));
return ret;
}
static inline void outw(uint16_t port, uint16_t val)
{
__asm__ volatile("outw %0, %1" : : "a"(val), "Nd"(port));
}
static inline uint32_t inl(uint16_t port)
{
uint32_t ret;
__asm__ volatile("inl %1, %0" : "=a"(ret) : "Nd"(port));
return ret;
}
static inline void outl(uint16_t port, uint32_t val)
{
__asm__ volatile("outl %0, %1" : : "a"(val), "Nd"(port));
}
static inline void io_wait(void)
{
outb(0x80, 0);
}
static inline void sti(void)
{
__asm__ volatile("sti");
}
static inline void cli(void)
{
__asm__ volatile("cli");
}
static inline void int_wait(void)
{
__asm__ volatile("sti; hlt");
}
static inline void halt(void)
{
__asm__ volatile("cli; hlt");
}
#endif /* bindings.h */

View file

@ -0,0 +1,119 @@
/*
** SCCS ID: @(#)bootstrap.h 2.4 1/22/25
**
** @file bootstrap.h
**
** @author K. Reek
** @author Warren R. Carithers, Garrett C. Smith
**
** Addresses where various stuff goes in memory.
*/
#ifndef BOOTSTRAP_H_
#define BOOTSTRAP_H_
/*
** The boot device
*/
#define BDEV_FLOPPY 0x00
#define BDEV_USB 0x80 /* hard drive */
#define BDEV BDEV_USB /* default */
/*
** Bootstrap definitions
*/
#define BOOT_SEG 0x07c0 /* 07c0:0000 */
#define BOOT_DISP 0x0000
#define BOOT_ADDR ((BOOT_SEG << 4) + BOOT_DISP)
#define PART2_DISP 0x0200 /* 07c0:0200 */
#define PART2_ADDR ((BOOT_SEG << 4) + PART2_DISP)
#define SECTOR_SIZE 0x200 /* 512 bytes */
/* Note: this assumes the bootstrap is two sectors long! */
#define BOOT_SIZE (SECTOR_SIZE + SECTOR_SIZE)
#define OFFSET_LIMIT (0x10000 - SECTOR_SIZE)
#define BOOT_SP_DISP 0x4000 /* stack pointer 07c0:4000, or 0xbc00 */
#define BOOT_SP_ADDR ((BOOT_SEG << 4) + BOOT_SP_DISP)
#define SECTOR1_END (BOOT_ADDR + SECTOR_SIZE)
#define SECTOR2_END (BOOT_ADDR + BOOT_SIZE)
// location of the user blob data
// (three halfwords of data)
#define USER_BLOB_DATA (SECTOR2_END - 12)
/*
** The target program itself
*/
#define TARGET_SEG 0x00001000 /* 1000:0000 */
#define TARGET_ADDR 0x00010000 /* and upward */
#define TARGET_STACK 0x00010000 /* and downward */
/*
** The Global Descriptor Table (0000:0500 - 0000:2500)
*/
#define GDT_SEG 0x00000050
#define GDT_ADDR 0x00000500
/* segment register values */
#define GDT_LINEAR 0x0008 /* All of memory, R/W */
#define GDT_CODE 0x0010 /* All of memory, R/E */
#define GDT_DATA 0x0018 /* All of memory, R/W */
#define GDT_STACK 0x0020 /* All of memory, R/W */
/*
** The Interrupt Descriptor Table (0000:2500 - 0000:2D00)
*/
#define IDT_SEG 0x00000250
#define IDT_ADDR 0x00002500
/*
** Additional I/O ports used by the bootstrap code
*/
// keyboard controller
#define KBD_DATA 0x60
#define KBD_CMD 0x64
#define KBD_STAT 0x64
// status register bits
#define KBD_OBSTAT 0x01
#define KBD_IBSTAT 0x02
#define KBD_SYSFLAG 0x04
#define KBD_CMDDAT 0x08
// commands
#define KBD_P1_DISABLE 0xad
#define KBD_P1_ENABLE 0xae
#define KBD_RD_OPORT 0xd0
#define KBD_WT_OPORT 0xd1
#ifdef ASM_SRC
// segment descriptor macros for use in assembly source files
// layout:
// .word lower 16 bits of limit
// .word lower 16 bits of base
// .byte middle 8 bits of base
// .byte type byte
// .byte granularity byte
// .byte upper 8 bits of base
// we use 4K units, so we ignore the lower 12 bits of the limit
#define SEGNULL .word 0, 0, 0, 0
#define SEGMENT(base, limit, dpl, type) \
.word(((limit) >> 12) & 0xffff); \
.word((base) & 0xffff); \
.byte(((base) >> 16) & 0xff); \
.byte(SEG_PRESENT | (dpl) | SEG_NON_SYSTEM | (type)); \
.byte(SEG_GRAN_4KBYTE | SEG_DB_32BIT | (((limit) >> 28) & 0xf)); \
.byte(((base) >> 24) & 0xff)
#endif /* ASM_SRC */
#endif

287
kernel/old/include/cio.h Normal file
View file

@ -0,0 +1,287 @@
/**
** SCCS ID: @(#)cio.h 2.7 1/22/25
**
** @file cio.h
**
** @author Warren R. Carithers
** @authors K. Reek, Jon Coles
**
** Based on: c_io.c 1.13 (Ken Reek, Jon Coles, Warren R. Carithers)
**
** Declarations and descriptions of console I/O routines
**
** These routines provide a rudimentary capability for printing to
** the screen and reading from the keyboard.
**
** Screen output:
** There are two families of functions. The first provides a window
** that behaves in the usual manner: writes extending beyond the right
** edge of the window wrap around to the next line, the top line
** scrolls off the window to make room for new lines at the bottom.
** However, you may choose what part of the screen contains this
** scrolling window. This allows you to print some text at fixed
** locations on the screen while the rest of the screen scrolls.
**
** The second family allows for printing at fixed locations on the
** screen. No scrolling or line wrapping are done for these functions.
** It is not intended that these functions be used to write in the
** scrolling area of the screen.
**
** In both sets of functions, the (x,y) coordinates are interpreted
** as (column,row), with the upper left corner of the screen being
** (0,0) and the lower right corner being (79,24).
**
** The printf provided in both sets of functions has the same
** conversion capabilities. Format codes are of the form:
**
** %-0WC
**
** where "-", "0", and "W" are all optional:
** "-" is the left-adjust flag (default is right-adjust)
** "0" is the zero-fill flag (default is space-fill)
** "W" is a number specifying the minimum field width (default: 1 )
** and "C" is the conversion type, which must be one of these:
** "c" print a single character
** "s" print a null-terminated string
** "d" print an integer as a decimal value
** "x" print an integer as a hexadecimal value
** "o" print an integer as a octal value
**
** Keyboard input:
** Two functions are provided: getting a single character and getting
** a newline-terminated line. A third function returns a count of
** the number of characters available for immediate reading.
** No conversions are provided (yet).
*/
#ifndef CIO_H_
#define CIO_H_
#ifndef ASM_SRC
// EOT indicator (control-D)
#define EOT '\04'
/*****************************************************************************
**
** INITIALIZATION ROUTINES
**
** Initializes the I/O system.
*/
/**
** cio_init
**
** Initializes the I/O routines. Must be called before
** any CIO functions can be used.
**
** @param notify pointer to an input notification function, or NULL
*/
void cio_init(void (*notify)(int));
/*****************************************************************************
**
** SCROLLING OUTPUT ROUTINES
**
** Each operation begins at the current cursor position and advances
** it. If a newline is output, the reminder of that line is cleared.
** Output extending past the end of the line is wrapped. If the
** cursor is moved below the scrolling region's bottom edge, scrolling
** is delayed until the next output is produced.
*/
/**
** cio_setscroll
**
** This sets the scrolling region to be the area defined by the arguments.
** The remainder of the screen does not scroll and may be used to display
** data you do not want to move. By default, the scrolling region is the
** entire screen. X and Y coordinates begin at 0 in the upper left corner
** of the screen.
**
** @param min_x,min_y upper-left corner of the region
** @param max_x,max_y lower-right corner of the region
*/
void cio_setscroll(unsigned int min_x, unsigned int min_y, unsigned int max_x,
unsigned int max_y);
/**
** cio_moveto
**
** Moves the cursor to the specified position. (0,0) indicates
** the upper left corner of the scrolling region. Subsequent
** output will begin at the cursor position.
**
** @param x,y desired coordinate position
*/
void cio_moveto(unsigned int x, unsigned int y);
/**
** cio_putchar
**
** Prints a single character.
**
** @param c the character to be printed
*/
void cio_putchar(unsigned int c);
/**
** cio_puts
**
** Prints the characters in the string up to but not including
** the terminating null byte.
**
** @param str pointer to a NUL-terminated string to be printed
*/
void cio_puts(const char *str);
/**
** cio_write
**
** Prints "length" characters from the buffer.
**
** @param buf the buffer of characters
** @param length the number of characters to print
*/
void cio_write(const char *buf, int length);
/**
** cio_printf
**
** Limited form of printf (see the beginning of this file for
** a list of what is implemented).
**
** @param fmt a printf-style format control string
** @param ... optional additional values to be printed
*/
void cio_printf(char *fmt, ...);
/**
** cio_scroll
**
** Scroll the scrolling region up by the given number of lines.
** The output routines scroll automatically so normally you
** do not need to call this routine yourself.
**
** @param lines the number of lines to scroll
*/
void cio_scroll(unsigned int lines);
/**
** cio_clearscroll
**
** Clears the entire scrolling region to blank spaces, and
** moves the cursor to (0,0).
*/
void cio_clearscroll(void);
/*****************************************************************************
**
** NON-SCROLLING OUTPUT ROUTINES
**
** Coordinates are relative to the entire screen: (0,0) is the upper
** left corner. There is no line wrap or scrolling.
*/
/**
** cio_putchar_at
**
** Prints the given character. If a newline is printed,
** the rest of the line is cleared. If this happens to the
** left of the scrolling region, the clearing stops when the
** region is reached. If this happens inside the scrolling
** region, the clearing stops when the edge of the region
** is reached.
**
** @param x,y desired coordinate position
** @param c the character to be printed
*/
void cio_putchar_at(unsigned int x, unsigned int y, unsigned int c);
/**
** cio_puts_at
**
** Prints the given string. cio_putchar_at is used to print
** the individual characters; see that description for details.
**
** @param x,y desired coordinate position
** @param str pointer to a NUL-terminated string to be printed
*/
void cio_puts_at(unsigned int x, unsigned int y, const char *str);
/**
** cio_printf_at
**
** Limited form of printf (see the beginning of this file for
** a list of what is implemented).
**
** @param x,y desired coordinate position
** @param fmt a printf-style format control string
** @param ... optional additional values to be printed
*/
void cio_printf_at(unsigned int x, unsigned int y, char *fmt, ...);
/**
** cio_clearscreen
**
** This function clears the entire screen, including the scrolling region.
*/
void cio_clearscreen(void);
/*****************************************************************************
**
** INPUT ROUTINES
**
** When interrupts are enabled, a keyboard ISR collects keystrokes
** and saves them until the program calls for them. If the input
** queue fills, additional characters are silently discarded.
** When interrupts are not enabled, keystrokes made when no input
** routines have been ** called are lost. This can cause errors in
** the input translation because the states of the Shift and Ctrl keys
** may not be tracked accurately. If interrupts are disabled, the user
** is advised to refrain from typing anything except when the program is
** waiting for input.
*/
/**
** cio_getchar
**
** If the character is not immediately available, the function
** waits until the character arrives.
**
** @return the next character from the keyboard input buffer
*/
int cio_getchar(void);
/**
** cio_gets
**
** This function reads a newline-terminated line from the
** keyboard. cio_getchar is used to obtain the characters;
** see that description for more details. The function
** returns when:
** a newline is entered (this is stored in the buffer)
** ctrl-D is entered (not stored in the buffer)
** the buffer becomes full.
** The buffer is null-terminated in all cases.
**
** @param buffer destination buffer for the input character sequence
** @param size the buffer length
**
** @return count of the number of characters read into the buffer
*/
int cio_gets(char *buffer, unsigned int size);
/**
** cio_input_queue
**
** This function lets the program determine whether there is input
** available. This indicates whether or not a call to cio_getchar()
** would block.
**
** @return number of characters in the input queue
*/
int cio_input_queue(void);
#endif /* !ASM_SRC */
#endif

View file

@ -0,0 +1,55 @@
/**
** @file clock.h
**
** @author CSCI-452 class of 20245
**
** @brief Clock module declarations
*/
#ifndef CLOCK_H_
#define CLOCK_H_
#include <common.h>
/*
** General (C and/or assembly) definitions
*/
// conversion functions for seconds, ms, and ticks
// (SEC_TO_MS is defined in defs.h)
#define MS_TO_TICKS(n) ((n))
#define SEC_TO_TICKS(n) (MS_TO_TICKS(SEC_TO_MS(n)))
#define TICKS_TO_SEC(n) ((n) / CLOCK_FREQ)
#define TICKS_TO_SEC_ROUNDED(n) (((n) + (CLOCK_FREQ - 1)) / CLOCK_FREQ)
#ifndef ASM_SRC
/*
** Start of C-only definitions
*/
/*
** Types
*/
/*
** Globals
*/
// current system time
extern uint32_t system_time;
/*
** Prototypes
*/
/**
** Name: clk_init
**
** Clock module initialization
*/
void clk_init(void);
#endif /* !ASM_SRC */
#endif

132
kernel/old/include/compat.h Normal file
View file

@ -0,0 +1,132 @@
/**
** @file compat.h
**
** @author Warren R. Carithers
**
** @brief Compatibility definitions for standard modules.
**
** These definitions are here to simplify the integration
** of some pre-written modules into the 452 baseline system.
** This is used primarily for the 'kmem' and 'sio' modules.
**
** We use CPP symbols and not actual data types for things here,
** as this means we don't need to include any other header files
** into this file. This helps get around "include loops" (e.g.,
** a.h includes b.h, which includes c.h, which includes a.h) when
** there are many interdependencies between source files.
*/
#ifndef COMPAT_H_
#define COMPAT_H_
#include <common.h>
#include <procs.h>
/*
** Section 1: sized integer types
**
** Internally, we use standard names for "sized" integer types for
** simplicity. If those disagree with the names used in the rest of
** the system, we take the opportunity to define our names here.
**
** To enable these, uncomment them, and place the apropriate
** existing type names in place of the '?' characters.
*/
// standard "sized integer" types
// #define int8_t ?
// #define uint8_t ?
// #define int16_t ?
// #define uint16_t ?
// #define int32_t ?
// #define uint32_t ?
// #define int64_t ?
// #define uint64_t ?
// #define bool_t ?
/*
** Section 2: other types
**
** Add type definitions here as needed.
**
** Note: we do not include the PCB and Queue declarations
** here because we don't actually need them in this header
** file - we're only defining CPP macros. Whatever file
** uses these macros, however, must include the appropriate
** headers if it uses these macros.
**
** To enable these, uncomment them, and place the apropriate
** existing type names in place of the '?' characters.
*/
// type name for the PCB
#define PCBTYPE pcb_t
// type name for our queue
#define QTYPE pcb_queue_t
/*
** Section 3: interface and behavior
**
** Include #define statements here as needed to define
** the names of functions and globals used in these modules
** in terms of the names used in the rest of the baseline.
**
** To enable these, uncomment them, and place the apropriate
** existing variable or function names in place of the '?' characters.
*/
// string functions
#define SLENGTH strlen
// scheduler
#define SCHED schedule
// dispatcher
#define DISPATCH dispatch
/*
** blocked queue for reading processes
**
** Define this if we are blocking processes which try to
** read from the SIO when no characters are available.
** Its value should be the name of the globally-visible
** queue to be used.
*/
#define QNAME sioread
#ifdef QNAME
// Only define these macros if we need to be able to create and
// manage a queue of things. It is expected that these will need
// to be customized based on the names and calling sequences of
// the appropriate functions.
// invoke the queue creation function
// examples:
//
// #define QCREATE(q) do {
// _que_create( &(q), NULL );
// } while(0)
//
// #define QCREATE(q) // do nothing
#define QCREATE(q) // handled elsewhere for us
// check to see if the queue is empty
// examples:
//
// #define QEMPTY(q) queue_is_empty(q)
// #define QEMPTY(q) (quene_length(q) > 0)
#define QEMPTY(q) pcb_queue_empty(q)
// this macro expands into code that removes a value from
// 'q' and places it into 'd'
#define QDEQUE(q, d) \
do { \
assert(pcb_queue_remove((q), (pcb_t **)&(d)) == SUCCESS); \
} while (0)
#endif /* QNAME */
#endif

356
kernel/old/include/debug.h Normal file
View file

@ -0,0 +1,356 @@
/**
** @file debug.h
**
** @author Numerous CSCI-452 classes
**
** Debugging macros and constants.
**
*/
#ifndef DEBUG_H_
#define DEBUG_H_
// Standard system headers
#include <cio.h>
#include <support.h>
// Kernel library
#include <lib.h>
#ifndef ASM_SRC
/*
** Start of C-only definitions
*/
/*
** General function entry/exit announcements
*/
#ifdef ANNOUNCE_ENTRY
// Announce that we have entered a kernel function
// usage: ENTERING( "name" ), EXITING( "name" )
// currently, these do not use the macro parameter, but could be
// modified to do so; instead, we use the __func__ CPP pseudo-macro
// to get the function name
#define ENTERING(n) \
do { \
cio_puts(" enter " __func__); \
} while (0)
#define EXITING(n) \
do { \
cio_puts(" exit " __func__); \
} while (0)
#else
#define ENTERING(m) // do nothing
#define EXITING(m) // do nothing
#endif
/*
** Console messages when error conditions are noted.
*/
// Warning messages to the console
// m: message (condition, etc.)
#define WARNING(m) \
do { \
cio_printf("\n** %s (%s @ %d): ", __func__, __FILE__, __LINE__); \
cio_puts(m); \
cio_putchar('\n'); \
} while (0)
// Panic messages to the console
// n: severity level
// m: message (condition, etc.)
#define PANIC(n, m) \
do { \
sprint(b512, "%s (%s @ %d), %d: %s\n", __func__, __FILE__, __LINE__, \
n, #m); \
kpanic(b512); \
} while (0)
/*
** Assertions are categorized by the "sanity level" being used in this
** compilation; each only triggers a fault if the sanity level is at or
** above a specific value. This allows selective enabling/disabling of
** debugging checks.
**
** The sanity level is set during compilation with the CPP macro
** "SANITY". A sanity level of 0 disables conditional assertions,
** but not the basic assert() version.
*/
#ifndef SANITY
// default sanity check level: check everything!
#define SANITY 9999
#endif
// Always-active assertions
#define assert(x) \
if (!(x)) { \
PANIC(0, x); \
}
// only provide these macros if the sanity check level is positive
#if SANITY > 0
#define assert1(x) \
if (SANITY >= 1 && !(x)) { \
PANIC(1, x); \
}
#define assert2(x) \
if (SANITY >= 2 && !(x)) { \
PANIC(2, x); \
}
#define assert3(x) \
if (SANITY >= 3 && !(x)) { \
PANIC(3, x); \
}
#define assert4(x) \
if (SANITY >= 4 && !(x)) { \
PANIC(4, x); \
}
// arbitrary sanity level
#define assertN(n, x) \
if (SANITY >= (n) && !(x)) { \
PANIC(n, x); \
}
#else
#define assert1(x) // do nothing
#define assert2(x) // do nothing
#define assert3(x) // do nothing
#define assert4(x) // do nothing
#define assertN(n, x) // do nothing
#endif /* SANITY > 0 */
/*
** Tracing options are enabled by defining one or more of the T_
** macros described in the Makefile.
**
** To add a tracing option:
**
** 1) Pick a short name for it (e.g., "PCB", "VM", ...)
** 2) At the end of this list, add code like this, with "name"
** replaced by your short name, and "nnnnnnnn" replaced by a
** unique bit that will designate this tracing option:
**
** #ifdef T_name
** #define TRname 0xnnnnnnnn
** #else
** #define TRname 0
** #endif
**
** Use the next bit position following the one in last list entry.
** 3) Add this to the end of the "TRACE" macro definition:
**
** | TRname
**
** 4) In the list of "TRACING_*" macros, add one for your option
** (using a name that might be more descriptive) in the 'then' clause:
**
** #define TRACING_bettername ((TRACE & TRname) != 0)
**
** 5) Also add a "null" version in the 'else' clause:
**
** #define TRACING_bettername 0
**
** 6) Maybe add your T_name choice to the Makefile with an explanation
** on the off chance you want anyone else to be able to understand
** what it's used for. :-)
**
** We're making CPP work for its pay with this file.
*/
// 2^0 bit
#ifdef T_PCB
#define TRPCB 0x00000001
#else
#define TRPCB 0
#endif
#ifdef T_VM
#define TRVM 0x00000002
#else
#define TRVM 0
#endif
#ifdef T_QUE
#define TRQUEUE 0x00000004
#else
#define TRQUEUE 0
#endif
#ifdef T_SCH
#define TRSCHED 0x00000008
#else
#define TRSCHED 0
#endif
// 2^4 bit
#ifdef T_DSP
#define TRDISP 0x00000010
#else
#define TRDISP 0
#endif
#ifdef T_SCALL
#define TRSYSCALLS 0x00000020
#else
#define TRSYSCALLS 0
#endif
#ifdef T_SRET
#define TRSYSRETS 0x00000040
#else
#define TRSYSRETS 0
#endif
#ifdef T_EXIT
#define TREXIT 0x00000080
#else
#define TREXIT 0
#endif
// 2^8 bit
#ifdef T_INIT
#define TRINIT 0x00000100
#else
#define TRINIT 0
#endif
#ifdef T_KM
#define TRKMEM 0x00000200
#else
#define TRKMEM 0
#endif
#ifdef T_KMFR
#define TRKMEM_F 0x00000400
#else
#define TRKMEM_F 0
#endif
#ifdef T_KMIN
#define TRKMEM_I 0x00000800
#else
#define TRKMEM_I 0
#endif
// 2^12 bit
#ifdef T_FORK
#define TRFORK 0x00001000
#else
#define TRFORK 0
#endif
#ifdef T_EXEC
#define TREXEC 0x00002000
#else
#define TREXEC 0
#endif
#ifdef T_SIO
#define TRSIO_STAT 0x00004000
#else
#define TRSIO_STAT 0
#endif
#ifdef T_SIOR
#define TRSIO_RD 0x00008000
#else
#define TRSIO_RD 0
#endif
// 2^16 bit
#ifdef T_SIOW
#define TRSIO_WR 0x00010000
#else
#define TRSIO_WR 0
#endif
#ifdef T_USER
#define TRUSER 0x00020000
#else
#define TRUSER 0
#endif
#ifdef T_ELF
#define TRELF 0x00040000
#else
#define TRELF 0
#endif
// 13 bits remaining for tracing options
// next available bit: 0x00080000
#define TRACE \
(TRDISP | TREXIT | TRINIT | TRKMEM | TRKMEM_F | TRKMEM_I | TRPCB | \
TRQUEUE | TRSCHED | TREXEC | TRSIO_RD | TRSIO_STAT | TRSIO_WR | TRFORK | \
TRVM | TRSYSCALLS | TRSYSRETS | TRUSER | TRELF)
#if TRACE > 0
// compile-time expressions for testing trace options
// usage: #if TRACING_thing
#define TRACING_PCB ((TRACE & TRPCB) != 0)
#define TRACING_VM ((TRACE & TRVM) != 0)
#define TRACING_QUEUE ((TRACE & TRQUEUE) != 0)
#define TRACING_SCHED ((TRACE & TRSCHED) != 0)
#define TRACING_DISPATCH ((TRACE & TRDISPATCH) != 0)
#define TRACING_SYSCALLS ((TRACE & TRSYSCALLS) != 0)
#define TRACING_SYSRETS ((TRACE & TRSYSRETS) != 0)
#define TRACING_EXIT ((TRACE & TREXIT) != 0)
#define TRACING_INIT ((TRACE & TRINIT) != 0)
#define TRACING_KMEM ((TRACE & TRKMEM) != 0)
#define TRACING_KMEM_FREELIST ((TRACE & TRKMEM_F) != 0)
#define TRACING_KMEM_INIT ((TRACE & TRKMEM_I) != 0)
#define TRACING_FORK ((TRACE & TRFORK) != 0)
#define TRACING_EXEC ((TRACE & TREXEC) != 0)
#define TRACING_SIO_STAT ((TRACE & TRSIO_STAT) != 0)
#define TRACING_SIO_ISR ((TRACE & TRSIO_ISR) != 0)
#define TRACING_SIO_RD ((TRACE & TRSIO_RD) != 0)
#define TRACING_SIO_WR ((TRACE & TRSIO_WR) != 0)
#define TRACING_USER ((TRACE & TRUSER) != 0)
#define TRACING_ELF ((TRACE & TRELF) != 0)
// more generic tests
#define TRACING_SOMETHING (TRACE != 0)
#else
// TRACE == 0, so just define these all as "false"
#define TRACING_PCB 0
#define TRACING_STACK 0
#define TRACING_QUEUE 0
#define TRACING_SCHED 0
#define TRACING_DISPATCH 0
#define TRACING_SYSCALLS 0
#define TRACING_SYSRET 0
#define TRACING_EXIT 0
#define TRACING_INIT 0
#define TRACING_KMEM 0
#define TRACING_KMEM_FREELIST 0
#define TRACING_KMEM_INIT 0
#define TRACING_FORK 0
#define TRACING_EXEC 0
#define TRACING_SI_STAT 0
#define TRACING_SIO_ISR 0
#define TRACING_SIO_RD 0
#define TRACING_SIO_WR 0
#define TRACING_USER 0
#define TRACING_ELF 0
#define TRACING_SOMETHING 0
#endif /* TRACE */
#endif /* !ASM_SRC */
#endif

130
kernel/old/include/defs.h Normal file
View file

@ -0,0 +1,130 @@
/**
** @file defs.h
**
** @author Warren R. Carithers
**
** @brief Common definitions.
**
** This header file defines things which are neede by all
** parts of the system (OS and user levels).
**
** Things which are kernel-specific go in the kdefs.h file;
** things which are user-specific go in the udefs.h file.
** The correct one of these will be automatically included
** at the end of this file.
*/
#ifndef DEFS_H_
#define DEFS_H_
/*
** General (C and/or assembly) definitions
**
** This section of the header file contains definitions that can be
** used in either C or assembly-language source code.
*/
// NULL pointer value
//
// we define this the traditional way so that
// it's usable from both C and assembly
#define NULL 0
// predefined i/o channels
#define CHAN_CIO 0
#define CHAN_SIO 1
// sizes of various things
#define NUM_1KB 0x00000400 // 2^10
#define NUM_4KB 0x00001000 // 2^12
#define NUM_1MB 0x00100000 // 2^20
#define NUM_4MB 0x00400000 // 2^22
#define NUM_1GB 0x40000000 // 2^30
#define NUM_2GB 0x80000000 // 2^31
#define NUM_3GB 0xc0000000 // 1GB + 2GB
#ifndef ASM_SRC
/*
** Start of C-only definitions
**
** Anything that should not be visible to something other than
** the C compiler should be put here.
*/
/*
** System error codes
**
** These can be returned to both system functions
** and to user system calls.
*/
// success!
#define SUCCESS (0)
#define E_SUCCESS SUCCESS
// generic "something went wrong"
#define E_FAILURE (-1)
// specific failure reasons
#define E_BAD_PARAM (-2)
#define E_BAD_CHAN (-3)
#define E_NO_CHILDREN (-4)
#define E_NO_MEMORY (-5)
#define E_NOT_FOUND (-6)
#define E_NO_PROCS (-7)
/*
** These error codes are internal to the OS.
*/
#define E_EMPTY_QUEUE (-100)
#define E_NO_PCBS (-101)
#define E_NO_PTE (-102)
#define E_LOAD_LIMIT (-103)
// exit status values
#define EXIT_SUCCESS (0)
#define EXIT_FAILURE (-1)
#define EXIT_KILLED (-101)
#define EXIT_BAD_SYSCALL (-102)
/*
** Process priority values
*/
enum priority_e {
PRIO_HIGH,
PRIO_STD,
PRIO_LOW,
PRIO_DEFERRED
// sentinel
,
N_PRIOS
};
// halves of various data sizes
#define UI16_UPPER 0xff00
#define UI16_LOWER 0x00ff
#define UI32_UPPER 0xffff0000
#define UI32_LOWER 0x0000ffff
#define UI64_UPPER 0xffffffff00000000LL
#define UI64_LOWER 0x00000000ffffffffLL
// Simple conversion pseudo-functions usable by everyone
// convert seconds to ms
#define SEC_TO_MS(n) ((n) * 1000)
#endif /* !ASM_SRC */
/*
** Level-specific definitions
*/
#ifdef KERNEL_SRC
#include <kdefs.h>
#else
#include <udefs.h>
#endif /* KERNEL_SRC */
#endif

157
kernel/old/include/kdefs.h Normal file
View file

@ -0,0 +1,157 @@
/**
** @file kdefs.h
**
** @author CSCI-452 class of 20245
**
** @brief Kernel-only declarations.
*/
#ifndef KDEFS_H_
#define KDEFS_H_
// debugging macros
#include <debug.h>
/*
** General (C and/or assembly) definitions
*/
// page sizes
#define SZ_PAGE NUM_4KB
#define SZ_BIGPAGE NUM_4MB
// kernel stack size (bytes)
#define N_KSTKPAGES 1
#define SZ_KSTACK (N_KSTKPAGES * SZ_PAGE)
// user stack size
#define N_USTKPAGES 2
#define SZ_USTACK (N_USTKPAGES * SZ_PAGE)
// declarations for modulus checking of (e.g.) sizes and addresses
#define LOW_9_BITS 0x00000fff
#define LOW_22_BITS 0x003fffff
#define HIGH_20_BITS 0xfffff000
#define HIGH_10_BITS 0xffc00000
#define MOD4_BITS 0x00000003
#define MOD4_MASK 0xfffffffc
#define MOD4_INC 0x00000004
#define MOD4_SHIFT 2
#define MOD16_BITS 0x0000000f
#define MOD16_MASK 0xfffffff0
#define MOD16_INC 0x00000010
#define MOD16_SHIFT 4
#define MOD1K_BITS 0x000003ff
#define MOD1K_MASK 0xfffffc00
#define MOD1K_INC 0x00000400
#define MOD1K_SHIFT 10
#define MOD4K_BITS 0x00000fff
#define MOD4K_MASK 0xfffff000
#define MOD4K_INC 0x00001000
#define MOD4K_SHIFT 12
#define MOD1M_BITS 0x000fffff
#define MOD1M_MASK 0xfff00000
#define MOD1M_INC 0x00100000
#define MOD1M_SHIFT 20
#define MOD4M_BITS 0x003fffff
#define MOD4M_MASK 0xffc00000
#define MOD4M_INC 0x00400000
#define MOD4M_SHIFT 22
#define MOD1G_BITS 0x3fffffff
#define MOD1G_MASK 0xc0000000
#define MOD1G_INC 0x40000000
#define MOD1G_SHIFT 30
#ifndef ASM_SRC
/*
** Start of C-only definitions
*/
// unit conversion macros
#define B_TO_KB(x) (((uint_t)(x)) >> 10)
#define B_TO_MB(x) (((uint_t)(x)) >> 20)
#define B_TO_GB(x) (((uint_t)(x)) >> 30)
#define KB_TO_B(x) (((uint_t)(x)) << 10)
#define KB_TO_MB(x) (((uint_t)(x)) >> 10)
#define KB_TO_GB(x) (((uint_t)(x)) >> 20)
#define MB_TO_B(x) (((uint_t)(x)) << 20)
#define MB_TO_KB(x) (((uint_t)(x)) << 10)
#define MB_TO_GB(x) (((uint_t)(x)) >> 10)
#define GB_TO_B(x) (((uint_t)(x)) << 30)
#define GB_TO_KB(x) (((uint_t)(x)) << 20)
#define GB_TO_MB(x) (((uint_t)(x)) << 10)
// potetially useful compiler attributes
#define ATTR_ALIGNED(x) __attribute__((__aligned__(x)))
#define ATTR_PACKED __attribute__((__packed__))
#define ATTR_UNUSED __attribute__((__unused__))
/*
** Utility macros
*/
//
// macros to clear data structures
//
// these are usable for clearing single-valued data items (e.g.,
// a PCB, etc.)
#define CLEAR(v) memclr(&v, sizeof(v))
#define CLEAR_PTR(p) memclr(p, sizeof(*p))
//
// macros for access registers and system call arguments
//
// REG(pcb,x) -- access a specific register in a process context
#define REG(pcb, x) ((pcb)->context->x)
// RET(pcb) -- access return value register in a process context
#define RET(pcb) ((pcb)->context->eax)
// ARG(pcb,n) -- access argument #n from the indicated process
//
// ARG(pcb,0) --> return address
// ARG(pcb,1) --> first parameter
// ARG(pcb,2) --> second parameter
// etc.
//
// ASSUMES THE STANDARD 32-BIT ABI, WITH PARAMETERS PUSHED ONTO THE
// STACK. IF THE PARAMETER PASSING MECHANISM CHANGES, SO MUST THIS!
#define ARG(pcb, n) (((uint32_t *)(((pcb)->context) + 1))[(n)])
/*
** Types
*/
/*
** Globals
*/
// general-purpose character buffer
extern char b256[256];
// buffer for use by PANIC() macro
extern char b512[512];
// kernel stack
extern uint8_t kstack[SZ_KSTACK];
/*
** Prototypes
*/
#endif /* !ASM_SRC */
#endif

57
kernel/old/include/klib.h Normal file
View file

@ -0,0 +1,57 @@
/*
** @file klib.h
**
** @author Warren R. Carithers
**
** Additional support functions for the kernel.
*/
#ifndef KLIB_H_
#define KLIB_H_
#include <common.h>
#ifndef ASM_SRC
#include <x86/ops.h>
/**
** Name: put_char_or_code( ch )
**
** Description: Prints a character on the console, unless it
** is a non-printing character, in which case its hex code
** is printed
**
** @param ch The character to be printed
*/
void put_char_or_code(int ch);
/**
** Name: backtrace
**
** Perform a simple stack backtrace. Could be augmented to use the
** symbol table to print function/variable names, etc., if so desired.
**
** @param ebp Initial EBP to use
** @param args Number of function argument values to print
*/
void backtrace(uint32_t *ebp, uint_t args);
/**
** Name: kpanic
**
** Kernel-level panic routine
**
** usage: kpanic( msg )
**
** Prefix routine for panic() - can be expanded to do other things
** (e.g., printing a stack traceback)
**
** @param msg[in] String containing a relevant message to be printed,
** or NULL
*/
void kpanic(const char *msg);
#endif /* !ASM_SRC */
#endif

138
kernel/old/include/kmem.h Normal file
View file

@ -0,0 +1,138 @@
/**
** @file kmem.h
**
** @author Warren R. Carithers
** @author Kenneth Reek
** @author 4003-506 class of 20013
**
** @brief Support for dynamic memory allocation within the OS.
**
** This is a basic page allocator. Each allocation request returns
** a pointer to a single 4096-byte page of memory.
**
** The module also supports subddivision of pages into "slices",
** each of which is 1KB (i.e., 1/4 of a page).
*/
#ifndef KMEM_H_
#define KMEM_H_
#define KERNEL_SRC
// standard types etc.
#include <common.h>
/*
** General (C and/or assembly) definitions
*/
// Slab and slice sizes, in bytes
#define SZ_SLAB SZ_PAGE
#define SZ_SLICE (SZ_SLAB >> 2)
// memory limits
//
// these determine the range of memory addresses the kmem
// module will manage
//
// we won't map any memory below 1MB or above 1GB
#define KM_LOW_CUTOFF NUM_1MB
#define KM_HIGH_CUTOFF NUM_1GB
#ifndef ASM_SRC
/*
** Start of C-only definitions
*/
/*
** Types
*/
/*
** Globals
*/
/*
** Prototypes
*/
/**
** Name: km_init
**
** Find what memory is present on the system and
** construct the list of free memory blocks.
**
** Dependencies:
** Must be called before any other init routine that uses
** dynamic storage is called.
*/
void km_init(void);
/**
** Name: km_dump
**
** Dump information about the free lists to the console. By default,
** prints only the list sizes; if 'addrs' is true, also dumps the list
** of page addresses; if 'all' is also true, dumps page addresses and
** slice addresses.
**
** @param addrs Also dump page addresses
** @param both Also dump slice addresses
*/
void km_dump(bool_t addrs, bool_t both);
/*
** Functions that manipulate free memory blocks.
*/
/**
** Name: km_page_alloc
**
** Allocate a page of memory from the free list.
**
** @return a pointer to the beginning of the allocated page,
** or NULL if no memory is available
*/
void *km_page_alloc(void);
/**
** Name: km_page_free
**
** Returns a memory block to the list of available blocks,
** combining it with adjacent blocks if they're present.
**
** CRITICAL ASSUMPTION: multi-page blocks will be freed one page
** at a time!
**
** @param[in] block Pointer to the page to be returned to the free list
*/
void km_page_free(void *block);
/**
** Name: km_slice_alloc
**
** Dynamically allocates a slice (1/4 of a page). If no
** memory is available, we return NULL (unless ALLOC_FAIL_PANIC
** was defined, in which case we panic).
**
** @return a pointer to the allocated slice
*/
void *km_slice_alloc(void);
/**
** Name: km_slice_free
**
** Returns a slice to the list of available slices.
**
** We make no attempt to merge slices, as they are independent
** blocks of memory (unlike pages).
**
** @param[in] block Pointer to the slice (1/4 page) to be freed
*/
void km_slice_free(void *block);
#endif /* !ASM_SRC */
#endif

68
kernel/old/include/list.h Normal file
View file

@ -0,0 +1,68 @@
/**
** @file list.h
**
** @author Warren R. Carithers
**
** @brief Support for a basic linked list data type.
**
** This module provides a very basic linked list data structure.
** A list can contain anything that has a pointer field in the first
** four bytes; these routines assume those bytes contain a pointer to
** the following entry in the list, whatever that may be.
*/
#ifndef LIST_H_
#define LIST_H_
#define KERNEL_SRC
// standard types etc.
#include <common.h>
/*
** General (C and/or assembly) definitions
*/
#ifndef ASM_SRC
/*
** Start of C-only definitions
*/
/*
** Data types
*/
// The list structure
typedef struct list_s {
struct list_s *next; // link to the successor
} list_t;
/*
** Prototypes
*/
/**
** Name: list_add
**
** Add the supplied data to the beginning of the specified list.
**
** @param[in,out] list The address of a list_t variable
** @param[in] data The data to prepend to the list
*/
void list_add(list_t *list, void *data);
/**
** Name: list_remove
**
** Remove the first entry from a linked list.
**
** @param[in,out] list The address of a list_t variable
**
** @return a pointer to the removed data, or NULL if the list was empty
*/
void *list_remove(list_t *list);
#endif /* !ASM_SRC */
#endif

View file

@ -0,0 +1,33 @@
/**
** @file params.h
**
** @author CSCI-452 class of 20245
**
** @brief System configuration settings
**
** This header file contains many of the "easily tunable" system
** settings, such as clock rate, number of simultaneous user
** processes, etc. This provides a sort of "one-stop shop" for
** things that might be tweaked frequently.
*/
#ifndef PARAMS_H_
#define PARAMS_H_
/*
** General (C and/or assembly) definitions
*/
// Upper bound on the number of simultaneous user-level
// processes in the system (completely arbitrary)
#define N_PROCS 25
// Limit on the number of entries in argv[], INCLUDING
// the trailing NULL pointer (also completely arbitrary)
#define N_ARGS 10
// Clock frequency (Hz)
#define CLOCK_FREQ 1000
#define TICKS_PER_MS 1
#endif

457
kernel/old/include/procs.h Normal file
View file

@ -0,0 +1,457 @@
/*
** @file procs.h
**
** @author CSCI-452 class of 20245
**
** @brief Process-related declarations
*/
#ifndef PROCS_H_
#define PROCS_H_
#include <common.h>
/*
** General (C and/or assembly) definitions
*/
#ifndef ASM_SRC
/*
** Start of C-only definitions
*/
/*
** Types
*/
/*
** Process states
*/
enum state_e {
// pre-viable
STATE_UNUSED = 0,
STATE_NEW,
// runnable
STATE_READY,
STATE_RUNNING,
// runnable, but waiting for some event
STATE_SLEEPING,
STATE_BLOCKED,
STATE_WAITING,
// no longer runnable
STATE_KILLED,
STATE_ZOMBIE
// sentinel value
,
N_STATES
};
// these may be handy for checking general conditions of processes
// they depend on the order of the state names in the enum!
#define FIRST_VIABLE STATE_READY
#define FIRST_BLOCKED STATE_SLEEPING
#define LAST_VIABLE STATE_WAITING
/*
** Process priorities are defined in <defs.h>
*/
/*
** Quantum lengths - values are number of clock ticks
*/
enum quantum_e { QUANTUM_SHORT = 1, QUANTUM_STANDARD = 3, QUANTUM_LONG = 5 };
/*
** PID-related definitions
*/
#define PID_INIT 1
#define FIRST_USER_PID 2
/*
** Process context structure
**
** NOTE: the order of data members here depends on the
** register save code in isr_stubs.S!!!!
**
** This will be at the top of the user stack when we enter
** an ISR. In the case of a system call, it will be followed
** by the return address and the system call parameters.
*/
typedef struct context_s {
uint32_t ss; // pushed by isr_save
uint32_t gs;
uint32_t fs;
uint32_t es;
uint32_t ds;
uint32_t edi;
uint32_t esi;
uint32_t ebp;
uint32_t esp;
uint32_t ebx;
uint32_t edx;
uint32_t ecx;
uint32_t eax;
uint32_t vector;
uint32_t code; // pushed by isr_save or the hardware
uint32_t eip; // pushed by the hardware
uint32_t cs;
uint32_t eflags;
} context_t;
#define SZ_CONTEXT sizeof(context_t)
/*
** program section information for user processes
*/
typedef struct section_s {
uint_t length; // length, in some units
uint_t addr; // location, in some units
} section_t;
// note: these correspond to the PT_LOAD sections found in
// an ELF file, not necessarily to text/data/bss
#define SECT_L1 0
#define SECT_L2 1
#define SECT_L3 2
#define SECT_STACK 3
// total number of section table entries in our PCB
#define N_SECTS 4
// number of those that can be loaded from an ELF module
#define N_LOADABLE 3
/*
** The process control block
**
** Fields are ordered by size to avoid padding
**
** Currently, this is 72 bytes long. It could be reduced to 64 (2^6)
** bytes by making the last four fields uint16_t types; that would
** divide nicely into 1024 bytes, giving 16 PCBs per 1/4 page of memory.
*/
typedef struct pcb_s {
// four-byte fields
// start with these four bytes, for easy access in assembly
context_t *context; // pointer to context save area on stack
// VM information
pde_t *pdir; // page directory for this process
section_t sects[N_SECTS]; // per-section memory information
// queue linkage
struct pcb_s *next; // next PCB in queue
// process state information
struct pcb_s *parent; // pointer to PCB of our parent process
uint32_t wakeup; // wakeup time, for sleeping processes
int32_t exit_status; // termination status, for parent's use
// these things may not need to be four bytes
uint_t pid; // PID of this process
enum state_e state; // process' current state
enum priority_e priority; // process priority level
uint_t ticks; // remaining ticks in this time slice
} pcb_t;
#define SZ_PCB sizeof(pcb_t)
/*
** PCB queue structure (opaque to the rest of the kernel)
*/
typedef struct pcb_queue_s *pcb_queue_t;
/*
** Queue ordering methods
*/
enum pcb_queue_order_e {
O_FIFO,
O_PRIO,
O_PID,
O_WAKEUP
// sentinel
,
N_ORDERINGS
};
#define O_FIRST_STYLE O_FIFO
#define O_LAST_STYLE O_WAKEUP
/*
** Globals
*/
// public-facing queue handles
extern pcb_queue_t pcb_freelist;
extern pcb_queue_t ready;
extern pcb_queue_t waiting;
extern pcb_queue_t sleeping;
extern pcb_queue_t zombie;
extern pcb_queue_t sioread;
// pointer to the currently-running process
extern pcb_t *current;
// the process table
extern pcb_t ptable[N_PROCS];
// next available PID
extern uint_t next_pid;
// pointer to the PCB for the 'init' process
extern pcb_t *init_pcb;
// table of state name strings
extern const char state_str[N_STATES][4];
// table of priority name strings
extern const char prio_str[N_PRIOS][5];
// table of queue ordering name strings
extern const char ord_str[N_ORDERINGS][5];
/*
** Prototypes
*/
/**
** Name: pcb_init
**
** Initialization for the Process module.
*/
void pcb_init(void);
/**
** Name: pcb_alloc
**
** Allocate a PCB from the list of free PCBs.
**
** @param pcb Pointer to a pcb_t * where the PCB pointer will be returned.
**
** @return status of the allocation attempt
*/
int pcb_alloc(pcb_t **pcb);
/**
** Name: pcb_free
**
** Return a PCB to the list of free PCBs.
**
** @param pcb Pointer to the PCB to be deallocated.
*/
void pcb_free(pcb_t *pcb);
/**
** Name: pcb_zombify
**
** Turn the indicated process into a Zombie. This function
** does most of the real work for exit() and kill() calls.
** Is also called from the scheduler and dispatcher.
**
** @param pcb Pointer to the newly-undead PCB
*/
void pcb_zombify(register pcb_t *victim);
/**
** Name: pcb_cleanup
**
** Reclaim a process' data structures
**
** @param pcb The PCB to reclaim
*/
void pcb_cleanup(pcb_t *pcb);
/**
** Name: pcb_find_pid
**
** Locate the PCB for the process with the specified PID
**
** @param pid The PID to be located
**
** @return Pointer to the PCB, or NULL
*/
pcb_t *pcb_find_pid(uint_t pid);
/**
** Name: pcb_find_ppid
**
** Locate the PCB for the process with the specified parent
**
** @param pid The PID to be located
**
** @return Pointer to the PCB, or NULL
*/
pcb_t *pcb_find_ppid(uint_t pid);
/**
** Name: pcb_queue_reset
**
** Initialize a PCB queue.
**
** @param queue[out] The queue to be initialized
** @param order[in] The desired ordering for the queue
**
** @return status of the init request
*/
int pcb_queue_reset(pcb_queue_t queue, enum pcb_queue_order_e style);
/**
** Name: pcb_queue_empty
**
** Determine whether a queue is empty. Essentially just a wrapper
** for the PCB_QUEUE_EMPTY() macro, for use outside this module.
**
** @param[in] queue The queue to check
**
** @return true if the queue is empty, else false
*/
bool_t pcb_queue_empty(pcb_queue_t queue);
/**
** Name: pcb_queue_length
**
** Return the count of elements in the specified queue.
**
** @param[in] queue The queue to check
**
** @return the count (0 if the queue is empty)
*/
uint_t pcb_queue_length(const pcb_queue_t queue);
/**
** Name: pcb_queue_insert
**
** Inserts a PCB into the indicated queue.
**
** @param queue[in,out] The queue to be used
** @param pcb[in] The PCB to be inserted
**
** @return status of the insertion request
*/
int pcb_queue_insert(pcb_queue_t queue, pcb_t *pcb);
/**
** Name: pcb_queue_peek
**
** Return the first PCB from the indicated queue, but don't
** remove it from the queue
**
** @param queue[in] The queue to be used
**
** @return the PCB pointer, or NULL if the queue is empty
*/
pcb_t *pcb_queue_peek(const pcb_queue_t queue);
/**
** Name: pcb_queue_remove
**
** Remove the first PCB from the indicated queue.
**
** @param queue[in,out] The queue to be used
** @param pcb[out] Pointer to where the PCB pointer will be saved
**
** @return status of the removal request
*/
int pcb_queue_remove(pcb_queue_t queue, pcb_t **pcb);
/**
** Name: pcb_queue_remove_this
**
** Remove the specified PCB from the indicated queue.
**
** @param queue[in,out] The queue to be used
** @param pcb[in] Pointer to the PCB to be removed
**
** @return status of the removal request
*/
int pcb_queue_remove_this(pcb_queue_t queue, pcb_t *pcb);
/*
** Scheduler routines
*/
/**
** schedule(pcb)
**
** Schedule the supplied process
**
** @param pcb Pointer to the PCB of the process to be scheduled
*/
void schedule(pcb_t *pcb);
/**
** dispatch()
**
** Select the next process to receive the CPU
*/
void dispatch(void);
/*
** Debugging/tracing routines
*/
/**
** Name: ctx_dump
**
** Dumps the contents of this process context to the console
**
** @param msg[in] An optional message to print before the dump
** @param c[in] The context to dump out
*/
void ctx_dump(const char *msg, register context_t *c);
/**
** Name: ctx_dump_all
**
** dump the process context for all active processes
**
** @param msg[in] Optional message to print
*/
void ctx_dump_all(const char *msg);
/**
** Name: pcb_dump
**
** Dumps the contents of this PCB to the console
**
** @param msg[in] An optional message to print before the dump
** @param p[in] The PCB to dump
** @param all[in] Dump all the contents?
*/
void pcb_dump(const char *msg, register pcb_t *p, bool_t all);
/**
** Name: pcb_queue_dump
**
** Dump the contents of the specified queue to the console
**
** @param msg[in] An optional message to print before the dump
** @param queue[in] The queue to dump
** @param contents[in] Also dump (some) contents?
*/
void pcb_queue_dump(const char *msg, pcb_queue_t queue, bool_t contents);
/**
** Name: ptable_dump
**
** dump the contents of the "active processes" table
**
** @param msg[in] Optional message to print
** @param all[in] Dump all or only part of the relevant data
*/
void ptable_dump(const char *msg, bool_t all);
/**
** Name: ptable_dump_counts
**
** Prints basic information about the process table (number of
** entries, number with each process state, etc.).
*/
void ptable_dump_counts(void);
#endif /* !ASM_SRC */
#endif

View file

@ -0,0 +1,3 @@
#pragma once
#include <stdint.h>

168
kernel/old/include/sio.h Normal file
View file

@ -0,0 +1,168 @@
/**
** @file sio.h
**
** @author Warren R. Carithers
**
** @brief SIO definitions
*/
#ifndef SIO_H_
#define SIO_H_
// compatibility definitions
#include <compat.h>
/*
** General (C and/or assembly) definitions
*/
// sio interrupt settings
#define SIO_TX 0x01
#define SIO_RX 0x02
#define SIO_BOTH (SIO_TX | SIO_RX)
#ifndef ASM_SRC
/*
** Start of C-only definitions
*/
#include <common.h>
#include <procs.h>
/*
** PUBLIC GLOBAL VARIABLES
*/
// queue for read-blocked processes
extern QTYPE QNAME;
/*
** PUBLIC FUNCTIONS
*/
/**
** sio_init()
**
** Initialize the UART chip.
*/
void sio_init(void);
/**
** sio_enable()
**
** Enable SIO interrupts
**
** usage: uint8_t old = sio_enable( uint8_t which )
**
** @param which Bit mask indicating which interrupt(s) to enable
**
** @return the prior IER setting
*/
uint8_t sio_enable(uint8_t which);
/**
** sio_disable()
**
** Disable SIO interrupts
**
** usage: uint8_t old = sio_disable( uint8_t which )
**
** @param which Bit mask indicating which interrupt(s) to disable
**
** @return the prior IER setting
*/
uint8_t sio_disable(uint8_t which);
/**
** sio_inq_length()
**
** Get the input queue length
**
** usage: int num = sio_inq_length()
**
** @return the count of characters still in the input queue
*/
int sio_inq_length(void);
/**
** sio_readc()
**
** Get the next input character
**
** usage: int ch = sio_readc()
**
** @return the next character, or -1 if no character is available
*/
int sio_readc(void);
/**
** sio_read()
**
** Read the entire input buffer into a user buffer of a specified size
**
** usage: int num = sio_read( char *buffer, int length )
**
** @param buf The destination buffer
** @param length Length of the buffer
**
** @return the number of bytes copied, or 0 if no characters were available
*/
int sio_read(char *buffer, int length);
/**
** sio_writec( ch )
**
** Write a character to the serial output
**
** usage: sio_writec( int ch )
**
** @param ch Character to be written (in the low-order 8 bits)
*/
void sio_writec(int ch);
/**
** sio_write( ch )
**
** Write a buffer of characters to the serial output
**
** usage: int num = sio_write( const char *buffer, int length )
**
** @param buffer Buffer containing characters to write
** @param length Number of characters to write
**
** @return the number of characters copied into the SIO output buffer
*/
int sio_write(const char *buffer, int length);
/**
** sio_puts( buf )
**
** Write a NUL-terminated buffer of characters to the serial output
**
** usage: n = sio_puts( const char *buffer );
**
** @param buffer The buffer containing a NUL-terminated string
**
** @return the count of bytes transferred
*/
int sio_puts(const char *buffer);
/**
** sio_dump( full )
**
** Dump the contents of the SIO buffers to the console
**
** usage: sio_dump(true) or sio_dump(false)
**
** @param full Boolean indicating whether or not a "full" dump
** is being requested (which includes the contents
** of the queues)
*/
void sio_dump(bool_t full);
#endif /* !ASM_SRC */
#endif

View file

@ -0,0 +1,86 @@
/**
** SCCS ID: @(#)support.h 2.3 1/22/25
**
** @file support.h
**
** @author K. Reek
** @author Warren R. Carithers
**
** Declarations for functions provided in support.c, and
** some hardware characteristics needed in the initialization.
**
*/
#ifndef SUPPORT_H
#define SUPPORT_H
/*
** Delay values
**
** Notes: The parameter to the delay() function is ambiguous; it
** purports to indicate a delay length, but that isn't really tied
** to any real-world time measurement.
**
** On the original systems we used (dual 500MHz Intel P3 CPUs), each
** "unit" was approximately one tenth of a second, so delay(10) would
** delay for about one second.
**
** On the current machines (Intel Core i5-7500), delay(100) is about
** 2.5 seconds, so each "unit" is roughly 0.025 seconds.
**
** Ultimately, just remember that THESE VALUES ARE APPROXIMATE AT BEST.
*/
#define DELAY_1_SEC 40
#define DELAY_1_25_SEC 50
#define DELAY_2_SEC 80
#define DELAY_2_5_SEC 100
#define DELAY_3_SEC 120
#define DELAY_5_SEC 200
#define DELAY_7_SEC 280
#define DELAY_10_SEC 400
#ifndef ASM_SRC
/**
** panic
**
** Called when we find an unrecoverable error, this routine disables
** interrupts, prints a description of the error and then goes into a
** hard loop to prevent any further processing.
**
** @param reason NUL-terminated message to be printed.
*/
void panic(char *reason);
/**
** init_interrupts
**
** (Re)initilizes the interrupt system. This includes initializing the
** IDT and the PIC. It is up to the user to enable processor interrupts
** when they're ready.
*/
void init_interrupts(void);
/*
** install_isr
**
** Installs a second-level handler for a specific interrupt. Returns the
** previously-installed handler for reinstallation (if desired).
**
** @param vector the interrupt vector number
** @param handler the second-stage ISR function to be called by the stub
**
** @return a pointer to the previously-registered ISR
*/
void (*install_isr(int vector, void (*handler)(int, int)))(int, int);
/*
** Name: delay
**
** See the comment above about the relative accuracy of the 'length'
** parameter.
*/
void delay(int length);
#endif /* !ASM_SRC */
#endif

View file

@ -0,0 +1,80 @@
/**
** @file syscalls.h
**
** @author CSCI-452 class of 20245
**
** @brief System call declarations
*/
#ifndef SYSCALLS_H_
#define SYSCALLS_H_
#include <common.h>
/*
** General (C and/or assembly) definitions
*/
/*
** system call codes
**
** these are used in the user-level C library stub functions,
** and are defined here as CPP macros instead of as an enum
** so that they can be used from assembly
*/
#define SYS_exit 0
#define SYS_waitpid 1
#define SYS_fork 2
#define SYS_exec 3
#define SYS_read 4
#define SYS_write 5
#define SYS_getpid 6
#define SYS_getppid 7
#define SYS_gettime 8
#define SYS_getprio 9
#define SYS_setprio 10
#define SYS_kill 11
#define SYS_sleep 12
// UPDATE THIS DEFINITION IF MORE SYSCALLS ARE ADDED!
#define N_SYSCALLS 13
// dummy system call code for testing our ISR
#define SYS_bogus 0xbad
// interrupt vector entry for system calls
#define VEC_SYSCALL 0x80
#ifndef ASM_SRC
/*
** Start of C-only definitions
*/
/*
** Types
*/
/*
** Globals
*/
/*
** Prototypes
*/
#ifdef KERNEL_SRC
/**
** Name: sys_init
**
** Syscall module initialization routine
*/
void sys_init(void);
#endif /* KERNEL_SRC */
#endif /* !ASM_SRC */
#endif

View file

@ -0,0 +1,13 @@
#ifndef TYPES_H_
#define TYPES_H_
#ifndef ASM_SRC
#ifdef KERNEL_SRC
// we define these here instead of in vm.h in order to get around a
// nasty chick/egg dependency between procs.h and vm.h
typedef uint32_t pde_t; // page directory entry
typedef uint32_t pte_t; // page table entry
#endif /* KERNEL_SRC */
#endif
#endif

139
kernel/old/include/user.h Normal file
View file

@ -0,0 +1,139 @@
/**
** @file user.h
**
** @author CSCI-452 class of 20245
**
** @brief Declarations of user-level code management routines
*/
#ifndef USER_H_
#define USER_H_
#include <common.h>
#include <procs.h>
#include <x86/arch.h>
// default value for EFLAGS in new processes
#define DEFAULT_EFLAGS (EFL_MB1 | EFL_IF)
/*
** General (C and/or assembly) definitions
*/
#ifndef ASM_SRC
/*
** Start of C-only definitions
*/
/*
** Types
*/
/*
** Blob file organization
**
** The file begins with a four-byte magic number and a four-byte integer
** indicating the number of ELF files contained in the blob. This is
** followed by an array of 32-byte file table entries, and then the contents
** of the ELF files in the order they appear in the program file table.
**
** Bytes Contents
** ----- ----------------------------
** 0 - 3 File magic number ("BLB\0")
** 4 - 7 Number of ELF files in blob ("n")
** 8 - n*32+8 Program file table
** n*32+9 - ? ELF file contents
**
** Each program file table entry contains the following information:
**
** name File name (up to 19 characters long)
** offset Byte offset to the ELF header for this file
** size Size of this ELF file, in bytes
** flags Flags related to this file
*/
// user program blob header
typedef struct header_s {
char magic[4];
uint32_t num;
} header_t;
// length of the file name field
#define NAMELEN 20
// program descriptor
typedef struct prog_s {
char name[NAMELEN]; // truncated name (15 chars)
uint32_t offset; // offset from the beginning of the blob
uint32_t size; // size of this ELF module
uint32_t flags; // miscellaneous flags
} prog_t;
/*
** Globals
*/
/*
** Prototypes
*/
/**
** Name: user_init
**
** Initializes the user support module.
*/
void user_init(void);
/**
** Name: user_locate
**
** Locates a user program in the user code archive.
**
** @param what The ID of the user program to find
**
** @return pointer to the program table entry in the code archive, or NULL
*/
prog_t *user_locate(uint_t what);
/**
** Name: user_duplicate
**
** Duplicates the memory setup for an existing process.
**
** @param new The PCB for the new copy of the program
** @param old The PCB for the existing the program
**
** @return the status of the duplicate attempt
*/
int user_duplicate(pcb_t *new, pcb_t *old);
/**
** Name: user_load
**
** Loads a user program from the user code archive into memory.
** Allocates all needed frames and sets up the VM tables.
**
** @param prog A pointer to the program table entry to be loaded
** @param pcb The PCB for the program being loaded
** @param args The argument vector for the program
** @param sys Is the argument vector from kernel code?
**
** @return the status of the load attempt
*/
int user_load(prog_t *prog, pcb_t *pcb, const char **args, bool_t sys);
/**
** Name: user_cleanup
**
** "Unloads" a user program. Deallocates all memory frames and
** cleans up the VM structures.
**
** @param pcb The PCB of the program to be cleaned up
*/
void user_cleanup(pcb_t *pcb);
#endif /* !ASM_SRC */
#endif

501
kernel/old/include/vm.h Normal file
View file

@ -0,0 +1,501 @@
/**
** @file vm.h
**
** @author CSCI-452 class of 20245
**
** @brief Virtual memory-related declarations.
*/
#ifndef VM_H_
#define VM_H_
#include <defs.h>
#include <types.h>
#include <procs.h>
/*
** VM layout of the system
**
** User processes use the first 4MB of the 32-bit address space; see the
** next comment for details.
**
** Kernel virtual addresses are in the "higher half" range, beginning
** at 0x80000000. We define our mapping such that virtual address
** 0x8nnnnnnn maps to physical address 0x0nnnnnnn, so converting between
** the two is trivial.
*/
/*
** VM layout of process' address space
**
** Processes are limited to the first 4MB of the 32-bit address space:
**
** Address Range Contents
** ======================= ================================
** 0x00000000 - 0x00000fff page 0 is inaccessible
** 0x00001000 - 0x000..fff text occupies pages 1 - N
** 0x000..000 - 0x000..fff data occupies pages N+1 - N+d
** 0x000..000 - 0x000..fff bss occupies pages N+d+1 - N+d+b
** 0x000..000 - 0x003fdfff unusable
** 0x003fe000 - 0x003fffff stack occupies last two pages
**
** This gives us the following page table structure:
**
** Page directory:
** Entries Contents
** ======== ==============================
** 0 point to PMT for address space
** 1 - 1023 invalid
**
** Page map table:
** Entries Contents
** ======== ==============================
** 0 invalid
** 1 - N text frames
** N+1 - N+d data frames
** N+d+1 - N+d+b bss frames
** N+d+b+1 - 1021 invalid
** 1022 - 1023 stack frames
*/
/*
** General (C and/or assembly) definitions
*/
// user virtual addresses
#define USER_BASE 0x00000000
#define USER_MAX 0x003fffff
#define USER_TEXT 0x00001000
#define USER_STACK 0x003fe000
#define USER_STACK_P1 USER_STACK
#define USER_STACK_P2 0x003ff000
#define USER_STK_END 0x00400000
// how to find the addresses of the stack pages in the VM hierarchy
// user address space is the first 4MB of virtual memory
#define USER_PDE 0
// the stack occupies this range of pages in the user address space
#define USER_STK_FIRST_PTE 1022
#define USER_STK_LAST_PTE 1023
// some important memory addresses
#define KERN_BASE 0x80000000 // start of "kernel" memory
#define EXT_BASE 0x00100000 // start of "extended" memory (1MB)
#define DEV_BASE 0xfe000000 // "device" memory
#define PHYS_TOP 0x3fffffff // last usable physical address (1GB - 1)
// where the kernel actually lives
#define KERN_PLINK 0x00010000
#define KERN_VLINK (KERN_BASE + KERN_PLINK)
// number of entries in a page directory or page table
#define N_PDE 1024
#define N_PTE 1024
// index field shift counts and masks
#define PDIX_SHIFT 22
#define PTIX_SHIFT 12
#define PIX2I_MASK 0x3ff
// physical/virtual converters that don't use casting
// (usable from anywhere)
#define V2PNC(a) ((a) - KERN_BASE)
#define P2VNC(a) ((a) + KERN_BASE)
// page-size address rounding macros
#define SZ_PG_M1 MOD4K_BITS
#define SZ_PG_MASK MOD4K_MASK
#define PGUP(a) (((a) + SZ_PG_M1) & SZ_PG_MASK)
#define PGDOWN(a) ((a) & SZ_PG_MASK)
// page directory entry bit fields
#define PDE_P 0x00000001 // 1 = present
#define PDE_RW 0x00000002 // 1 = writable
#define PDE_US 0x00000004 // 1 = user and system usable
#define PDE_PWT 0x00000008 // cache: 1 = write-through
#define PDE_PCD 0x00000010 // cache: 1 = disabled
#define PDE_A 0x00000020 // accessed
#define PDE_D 0x00000040 // dirty (4MB pages)
#define PDE_AVL1 0x00000040 // ignored (4KB pages)
#define PDE_PS 0x00000080 // 1 = 4MB page size
#define PDE_G 0x00000100 // global
#define PDE_AVL2 0x00000e00 // ignored
#define PDE_PAT 0x00001000 // (4MB pages) use page attribute table
#define PDE_PTA 0xfffff000 // page table address field (4KB pages)
#define PDE_FA 0xffc00000 // frame address field (4MB pages)
// page table entry bit fields
#define PTE_P 0x00000001 // present
#define PTE_RW 0x00000002 // 1 = writable
#define PTE_US 0x00000004 // 1 = user and system usable
#define PTE_PWT 0x00000008 // cache: 1 = write-through
#define PTE_PCD 0x00000010 // cache: 1 = disabled
#define PTE_A 0x00000020 // accessed
#define PTE_D 0x00000040 // dirty
#define PTE_PAT 0x00000080 // use page attribute table
#define PTE_G 0x00000100 // global
#define PTE_AVL2 0x00000e00 // ignored
#define PTE_FA 0xfffff000 // frame address field
// error code bit assignments for page faults
#define PFLT_P 0x00000001
#define PFLT_W 0x00000002
#define PFLT_US 0x00000004
#define PFLT_RSVD 0x00000008
#define PFLT_ID 0x00000010
#define PFLT_PK 0x00000020
#define PFLT_SS 0x00000040
#define PFLT_HLAT 0x00000080
#define PFLT_SGX 0x00008000
#define PFLT_UNUSED 0xffff7f00
#ifndef ASM_SRC
/*
** Start of C-only definitions
*/
// physical/virtual converters that do use casting
// (not usable from assembly)
#define V2P(a) (((uint32_t)(a)) - KERN_BASE)
#define P2V(a) (((uint32_t)(a)) + KERN_BASE)
// create a pde/pte from an integer frame number and permission bits
#define MKPDE(f, p) ((pde_t)(TO_FRAME((f)) | (p)))
#define MKPTE(f, p) ((pte_t)(TO_FRAME((f)) | (p)))
// is a PDE/PTE present?
// (P bit is in the same place in both)
#define IS_PRESENT(entry) (((entry) & PDE_P) != 0)
// is a PDE a 4MB page entry?
#define IS_LARGE(pde) (((pde) & PDE_PS) != 0)
// is this entry "system only" or "system and user"?
#define IS_SYSTEM(entry) (((entry) & PDE_US) == 0)
#define IS_USER(entry) (((entry) & PDE_US) != 0)
// low-order nine bits of PDEs and PTEs hold "permission" flag bits
#define PERMS_MASK MOD4K_BITS
// 4KB frame numbers are 20 bits wide
#define FRAME_4K_SHIFT 12
#define FRAME2I_4K_MASK 0x000fffff
#define TO_4KFRAME(n) (((n) & FRAME2I_4K_MASK) << FRAME_4K_SHIFT)
#define GET_4KFRAME(n) (((n) >> FRAME_4K_SHIFT) & FRAME2I_4K_MASK)
#define PDE_4K_ADDR(n) ((n) & MOD4K_MASK)
#define PTE_4K_ADDR(n) ((n) & MOD4K_MASK)
// 4MB frame numbers are 10 bits wide
#define FRAME_4M_SHIFT 22
#define FRAME2I_4M_MASK 0x000003ff
#define TO_4MFRAME(n) (((n) & FRAME2I_4M_MASK) << FRAME_4M_SHIFT)
#define GET_4MFRAME(n) (((n) >> FRAME_4M_SHIFT) & FRAME2I_4M_MASK)
#define PDE_4M_ADDR(n) ((n) & MOD4M_MASK)
#define PTE_4M_ADDR(n) ((n) & MOD4M_MASK)
// extract the PMT address or frame address from a table entry
// PDEs could point to 4MB pages, or 4KB PMTs
#define PDE_ADDR(p) \
(IS_LARGE(p) ? (((uint32_t)p) & PDE_FA) : (((uint32_t)p) & PDE_PTA))
// PTEs always point to 4KB pages
#define PTE_ADDR(p) (((uint32_t)(p)) & PTE_FA)
// everything has nine bits of permission flags
#define PERMS(p) (((uint32_t)(p)) & PERMS_MASK)
// extract the table indices from a 32-bit VA
#define PDIX(v) ((((uint32_t)(v)) >> PDIX_SHIFT) & PIX2I_MASK)
#define PTIX(v) ((((uint32_t)(v)) >> PTIX_SHIFT) & PIX2I_MASK)
// extract the byte offset from a 32-bit VA
#define OFFSET_4K(v) (((uint32_t)(v)) & MOD4K_BITS)
#define OFFSET_4M(v) (((uint32_t)(v)) & MOD4M_BITS)
/*
** Types
*/
// page directory entries
// as a 32-bit word, in types.h
// typedef uint32_t pde_t;
// PDE for 4KB pages
typedef struct pdek_s {
uint_t p : 1; // 0: present
uint_t rw : 1; // 1: writable
uint_t us : 1; // 2: user/supervisor
uint_t pwt : 1; // 3: cache write-through
uint_t pcd : 1; // 4: cache disable
uint_t a : 1; // 5: accessed
uint_t avl1 : 1; // 6: ignored (available)
uint_t ps : 1; // 7: page size (must be 0)
uint_t avl2 : 4; // 11-8: ignored (available)
uint_t fa : 20; // 31-12: frame address
} pdek_f_t;
// PDE for 4MB pages
typedef struct pdem_s {
uint_t p : 1; // 0: present
uint_t rw : 1; // 1: writable
uint_t us : 1; // 2: user/supervisor
uint_t pwt : 1; // 3: cache write-through
uint_t pcd : 1; // 4: cache disable
uint_t a : 1; // 5: accessed
uint_t d : 1; // 6: dirty
uint_t ps : 1; // 7: page size (must be 1)
uint_t g : 1; // 8: global
uint_t avl : 3; // 11-9: ignored (available)
uint_t pat : 1; // 12: page attribute table in use
uint_t fa2 : 4; // 16-13: bits 35-32 of frame address (36-bit addrs)
uint_t rsv : 5; // 21-17: reserved - must be zero
uint_t fa : 10; // 31-22: bits 31-22 of frame address
} pdem_f_t;
// page table entries
// as a 32-bit word, in types.h
// typedef uint32_t pte_t;
// broken out into fields
typedef struct pte_s {
uint_t p : 1; // 0: present
uint_t rw : 1; // 1: writable
uint_t us : 1; // 2: user/supervisor
uint_t pwt : 1; // 3: cache write-through
uint_t pcd : 1; // 4: cache disable
uint_t a : 1; // 5: accessed
uint_t d : 1; // 6: dirty
uint_t pat : 1; // 7: page attribute table in use
uint_t g : 1; // 8: global
uint_t avl : 3; // 11-9: ignored (available)
uint_t fa : 20; // 31-12: frame address
} ptef_t;
// page fault error code bits
// comment: meaning when 1 / meaning when 0
struct pfec_s {
uint_t p : 1; // page-level protection violation / !present
uint_t w : 1; // write / read
uint_t us : 1; // user-mode access / supervisor-mode access
uint_t rsvd : 1; // reserved bit violation / not
uint_t id : 1; // instruction fetch / data fetch
uint_t pk : 1; // protection-key violation / !pk
uint_t ss : 1; // shadow stack access / !ss
uint_t hlat : 1; // HLAT paging / ordinary paging or access rights
uint_t xtr1 : 7; // unused
uint_t sgz : 1; // SGX-specific access control violation / !SGX
uint_t xtr2 : 16; // more unused
};
typedef union pfec_u {
uint32_t u;
struct pfec_s s;
} pfec_t;
// Mapping descriptor for VA::PA mappings
typedef struct mapping_t {
uint32_t va_start; // starting virtual address for this range
uint32_t pa_start; // first physical address in the range
uint32_t pa_end; // last physical address in the range
uint32_t perm; // access control
} mapping_t;
// Modes for dumping out page hierarchies
enum vmmode_e {
Simple = 0, // just count 'present' entries at each level
OneLevel, // top-level only: count entries, decode 'present'
TwoLevel, // count entries & decode at each level
Full // ??? in case we need more?
// sentinel
,
N_VMMODES
};
/*
** Globals
*/
// created page directory for the kernel
extern pde_t *kpdir;
/*
** Prototypes
*/
/**
** Name: vm_init
**
** Initialize the VM module
**
** Note: should not be called until after the memory free list has
** been set up.
*/
void vm_init(void);
/**
** Name: vm_uva2kva
**
** Convert a user VA into a kernel address. Works for all addresses -
** if the address is a page address, the low-order nine bits will be
** zeroes; otherwise, they is the offset into the page, which is
** unchanged within the address spaces.
**
** @param pdir Pointer to the page directory to examine
** @param va Virtual address to check
*/
void *vm_uva2kva(pde_t *pdir, void *va);
/**
** Name: vm_pagedup
**
** Duplicate a page of memory
**
** @param old Pointer to the first byte of a page
**
** @return a pointer to the new, duplicate page, or NULL
*/
void *vm_pagedup(void *old);
/**
** Name: vm_pdedup
**
** Duplicate a page directory entry
**
** @param entry The entry to be duplicated
**
** @return the new entry, or -1 on error.
*/
pde_t vm_pdedup(pde_t entry);
/**
** Name: vm_ptdup
**
** Duplicate a page directory entry
**
** @param dst Pointer to where the duplicate should go
** @param curr Pointer to the entry to be duplicated
**
** @return true on success, else false
*/
bool_t vm_ptdup(pde_t *dst, pde_t *curr);
/**
** Name: vm_getpte
**
** Return the address of the PTE corresponding to the virtual address
** 'va' within the address space controlled by 'pgdir'. If there is no
** page table for that VA and 'alloc' is true, create the necessary
** page table entries.
**
** @param pdir Pointer to the page directory to be searched
** @param va The virtual address we're looking for
** @param alloc Should we allocate a page table if there isn't one?
**
** @return A pointer to the page table entry for this VA, or NULL
*/
pte_t *vm_getpte(pde_t *pdir, const void *va, bool_t alloc);
/**
** Name: vm_mkkvm
**
** Create the kernel's page table hierarchy
*/
pde_t *vm_mkkvm(void);
/**
** Name: vm_mkuvm
**
** Create the page table hierarchy for a user process
*/
pde_t *vm_mkuvm(void);
/**
** Name: vm_set_kvm
**
** Switch the page table register to the kernel's page directory
*/
void vm_set_kvm(void);
/**
** Name: vm_set_uvm
**
** Switch the page table register to the page directory for a user process.
**
** @param p The PCB of the user process
*/
void vm_set_uvm(pcb_t *p);
/**
** Name: vm_add
**
** Add pages to the page hierarchy for a process, copying data into
** them if necessary.
**
** @param pdir Pointer to the page directory to modify
** @param wr "Writable" flag for the PTE
** @param sys "System" flag for the PTE
** @param va Starting VA of the range
** @param size Amount of physical memory to allocate
** @param data Pointer to data to copy, or NULL
** @param bytes Number of bytes to copy
**
** @return status of the allocation attempt
*/
int vm_add(pde_t *pdir, bool_t wr, bool_t sys, void *va, uint32_t size,
char *data, uint32_t bytes);
/**
** Name: vm_free
**
** Deallocate a page table hierarchy and all physical memory frames
** in the user portion.
**
** @param pdir Pointer to the page directory
*/
void vm_free(pde_t *pdir);
/*
** Name: vm_map
**
** Create PTEs for virtual addresses starting at 'va' that refer to
** physical addresses in the range [pa, pa+size-1]. We aren't guaranteed
** that va is page-aligned.
**
** @param pdir Page directory for this address space
** @param va The starting virtual address
** @param pa The starting physical address
** @param size Length of the range to be mapped
** @param perm Permission bits for the PTEs
*/
int vm_map(pde_t *pdir, void *va, uint32_t pa, uint32_t size, int perm);
/**
** Name: vm_uvmdup
**
** Create a duplicate of the user portio of an existing page table
** hierarchy. We assume that the "new" page directory exists and
** the system portions of it should not be touched.
**
** @param new New page directory
** @param old Existing page directory
**
** @return status of the duplication attempt
*/
int vm_uvmdup(pde_t *new, pde_t *old);
/**
** Name: vm_print
**
** Print out a paging hierarchy.
**
** @param pt Page table to display
** @param dir Is it a page directory (vs. a page table)?
** @param mode How to display the entries
*/
void vm_print(void *pt, bool_t dir, enum vmmode_e mode);
#endif /* !ASM_SRC */
#endif

View file

@ -0,0 +1,43 @@
/**
** @file vmtables.h
**
** @author CSCI-452 class of 20245
**
** @brief Predefined VM tables
*/
#ifndef VMTABLES_H_
#define VMTABLES_H_
#include <defs.h>
#include <types.h>
#include <vm.h>
#ifndef ASM_SRC
/*
** Initial page directory, for when the kernel is starting up
**
** we use large (4MB) pages here to allow us to use a one-level
** paging hierarchy; the kernel will create a new page table
** hierarchy once memory is initialized
*/
extern pde_t firstpdir[];
/*
** "Identity" page map table.
**
** This just maps the first 4MB of physical memory. It is initialized
** in vm_init().
*/
extern pte_t id_map[];
/*
** Kernel address mappings, present in every page table
*/
extern mapping_t kmap[];
extern const uint32_t n_kmap;
#endif /* !ASM_SRC */
#endif

View file

@ -0,0 +1,299 @@
/*
** @file arch.h
**
** @author Warren R. Carithers
** @author K. Reek
**
** Definitions of constants and macros for use
** with the x86 architecture and registers.
**
*/
#ifndef X86ARCH_H_
#define X86ARCH_H_
/*
** Video stuff
*/
#define VID_BASE_ADDR 0xB8000
/*
** Memory management
*/
#define SEG_PRESENT 0x80
#define SEG_PL_0 0x00
#define SEG_PL_1 0x20
#define SEG_PL_2 0x40
#define SEG_PL_3 0x50
#define SEG_SYSTEM 0x00
#define SEG_NON_SYSTEM 0x10
#define SEG_32BIT 0x04
#define DESC_IGATE 0x06
/*
** Exceptions
*/
#define N_EXCEPTIONS 256
/*
** Bit definitions in registers
**
** See IA-32 Intel Architecture SW Dev. Manual, Volume 3: System
** Programming Guide, page 2-8.
*/
/*
** EFLAGS
*/
#define EFL_RSVD 0xffc00000 /* reserved */
#define EFL_MB0 0x00008020 /* must be zero */
#define EFL_MB1 0x00000002 /* must be 1 */
#define EFL_ID 0x00200000
#define EFL_VIP 0x00100000
#define EFL_VIF 0x00080000
#define EFL_AC 0x00040000
#define EFL_VM 0x00020000
#define EFL_RF 0x00010000
#define EFL_NT 0x00004000
#define EFL_IOPL 0x00003000
#define EFL_OF 0x00000800
#define EFL_DF 0x00000400
#define EFL_IF 0x00000200
#define EFL_TF 0x00000100
#define EFL_SF 0x00000080
#define EFL_ZF 0x00000040
#define EFL_AF 0x00000010
#define EFL_PF 0x00000004
#define EFL_CF 0x00000001
/*
** CR0, CR1, CR2, CR3, CR4
**
** IA-32 V3, page 2-12.
*/
#define CR0_RSVD 0x1ffaffc0
#define CR0_PG 0x80000000
#define CR0_CD 0x40000000
#define CR0_NW 0x20000000
#define CR0_AM 0x00040000
#define CR0_WP 0x00010000
#define CR0_NE 0x00000020
#define CR0_ET 0x00000010
#define CR0_TS 0x00000008
#define CR0_EM 0x00000004
#define CR0_MP 0x00000002
#define CR0_PE 0x00000001
#define CR1_RSVD 0xffffffff
#define CR2_RSVD 0x00000000
#define CR2_PF_LIN_ADDR 0xffffffff
#define CR3_RSVD 0x00000fe7
#define CR3_PD_BASE 0xfffff000
#define CR3_PCD 0x00000010
#define CR3_PWT 0x00000008
#define CR4_RSVD 0xfd001000
#define CR4_UINT 0x02000000
#define CR4_PKS 0x01000000
#define CR4_CET 0x00800000
#define CR4_PKE 0x00400000
#define CR4_SMAP 0x00200000
#define CR4_SMEP 0x00100000
#define CR4_KL 0x00080000
#define CR4_OSXS 0x00040000
#define CR4_PCID 0x00020000
#define CR4_FSGS 0x00010000
#define CR4_SMXE 0x00004000
#define CR4_VMXE 0x00002000
#define CR4_LA57 0x00001000
#define CR4_UMIP 0x00000800
#define CR4_OSXMMEXCPT 0x00000400
#define CR4_OSFXSR 0x00000200
#define CR4_PCE 0x00000100
#define CR4_PGE 0x00000080
#define CR4_MCE 0x00000040
#define CR4_PAE 0x00000020
#define CR4_PSE 0x00000010
#define CR4_DE 0x00000008
#define CR4_TSD 0x00000004
#define CR4_PVI 0x00000002
#define CR4_VME 0x00000001
/*
** PMode segment selector field masks
**
** IA-32 V3, page 3-8.
*/
#define SEG_SEL_IX_MASK 0xfff8
#define SEG_SEL_TI_MASK 0x0004
#define SEG_SEL_RPL_MASK 0x0003
/*
** Segment descriptor bytes
**
** IA-32 V3, page 3-10.
**
** Bytes:
** 0, 1: segment limit 15:0
** 2, 3: base address 15:0
** 4: base address 23:16
** 7: base address 31:24
*/
/*
** Byte 5: access control bits
** 7: present
** 6-5: DPL
** 4: system/user
** 3-0: type
*/
#define SEG_ACCESS_P_MASK 0x80
#define SEG_PRESENT 0x80
#define SEG_NOT_PRESENT 0x00
#define SEG_ACCESS_DPL_MASK 0x60
#define SEG_DPL_0 0x00
#define SEG_DPL_1 0x20
#define SEG_DPL_2 0x40
#define SEG_DPL_3 0x60
#define SEG_ACCESS_S_MASK 0x10
#define SEG_SYSTEM 0x00
#define SEG_NON_SYSTEM 0x10
#define SEG_TYPE_MASK 0x0f
#define SEG_DATA_A_BIT 0x1
#define SEG_DATA_W_BIT 0x2
#define SEG_DATA_E_BIT 0x4
#define SEG_CODE_A_BIT 0x1
#define SEG_CODE_R_BIT 0x2
#define SEG_CODE_C_BIT 0x4
#define SEG_DATA_RO 0x0
#define SEG_DATA_ROA 0x1
#define SEG_DATA_RW 0x2
#define SEG_DATA_RWA 0x3
#define SEG_DATA_RO_XD 0x4
#define SEG_DATA_RO_XDA 0x5
#define SEG_DATA_RW_XW 0x6
#define SEG_DATA_RW_XWA 0x7
#define SEG_CODE_XO 0x8
#define SEG_CODE_XOA 0x9
#define SEG_CODE_XR 0xa
#define SEG_CODE_XRA 0xb
#define SEG_CODE_XO_C 0xc
#define SEG_CODE_XO_CA 0xd
#define SEG_CODE_XR_C 0xe
#define SEG_CODE_XR_CA 0xf
/*
** Byte 6: sizes
** 7: granularity
** 6: d/b
** 5: long mode
** 4: available!
** 3-0: upper 4 bits of limit
** 7: base address 31:24
*/
#define SEG_SIZE_G_MASK 0x80
#define SEG_GRAN_BYTE 0x00
#define SEG_GRAN_4KBYTE 0x80
#define SEG_SIZE_D_B_MASK 0x40
#define SEG_DB_16BIT 0x00
#define SEG_DB_32BIT 0x40
#define SEG_SIZE_L_MASK 0x20
#define SEG_L_64BIT 0x20
#define SEG_L_32BIT 0x00
#define SEG_SIZE_AVL_MASK 0x10
#define SEG_SIZE_LIM_19_16_MASK 0x0f
/*
** System-segment and gate-descriptor types
**
** IA-32 V3, page 3-15.
*/
// type 0: reserved
#define SEG_SYS_16BIT_TSS_AVAIL 0x1
#define SEG_SYS_LDT 0x2
#define SEG_SYS_16BIT_TSS_BUSY 0x3
#define SEG_SYS_16BIT_CALL_GATE 0x4
#define SEG_SYS_TASK_GATE 0x5
#define SEG_SYS_16BIT_INT_GATE 0x6
#define SEG_SYS_16BIT_TRAP_GATE 0x7
// type 8: reserved
#define SEG_SYS_32BIT_TSS_AVAIL 0x9
// type A: reserved
#define SEG_SYS_32BIT_TSS_BUSY 0xb
#define SEG_SYS_32BIT_CALL_GATE 0xc
// type D: reserved
#define SEG_SYS_32BIT_INT_GATE 0xe
#define SEG_SYS_32BIT_TRAP_GATE 0xf
/*
** IDT Descriptors
**
** IA-32 V3, page 5-13.
**
** All have a segment selector in bytes 2 and 3; Task Gate descriptors
** have bytes 0, 1, 4, 6, and 7 reserved; others have bytes 0, 1, 6,
** and 7 devoted to the 16 bits of the Offset, with the low nybble of
** byte 4 reserved.
*/
#define IDT_PRESENT 0x8000
#define IDT_DPL_MASK 0x6000
#define IDT_DPL_0 0x0000
#define IDT_DPL_1 0x2000
#define IDT_DPL_2 0x4000
#define IDT_DPL_3 0x6000
#define IDT_GATE_TYPE 0x0f00
#define IDT_TASK_GATE 0x0500
#define IDT_INT16_GATE 0x0600
#define IDT_INT32_GATE 0x0e00
#define IDT_TRAP16_GATE 0x0700
#define IDT_TRAP32_GATE 0x0f00
/*
** Interrupt vectors
*/
// predefined by the architecture
#define VEC_DIVIDE_ERROR 0x00
#define VEC_DEBUG_EXCEPTION 0x01
#define VEC_NMI_INTERRUPT 0x02
#define VEC_BREAKPOINT 0x03
#define VEC_OVERFLOW 0x04
#define VEC_BOUND_RANGE_EXCEEDED 0x05
#define VEC_INVALID_OPCODE 0x06
#define VEC_DEVICE_NOT_AVAILABLE 0x07
#define VEC_DOUBLE_FAULT 0x08
#define VEC_COPROCESSOR_OVERRUN 0x09
#define VEC_INVALID_TSS 0x0a
#define VEC_SEGMENT_NOT_PRESENT 0x0b
#define VEC_STACK_FAULT 0x0c
#define VEC_GENERAL_PROTECTION 0x0d
#define VEC_PAGE_FAULT 0x0e
// 0x0f is reserved - unused
#define VEC_FPU_ERROR 0x10
#define VEC_ALIGNMENT_CHECK 0x11
#define VEC_MACHINE_CHECK 0x12
#define VEC_SIMD_FP_EXCEPTION 0x13
#define VEC_VIRT_EXCEPTION 0x14
#define VEC_CTRL_PROT_EXCEPTION 0x15
// 0x16 through 0x1f are reserved
// 0x20 through 0xff are user-defined, non-reserved
// IRQ0 through IRQ15 will use vectors 0x20 through 0x2f
#define VEC_TIMER 0x20
#define VEC_KBD 0x21
#define VEC_COM2 0x23
#define VEC_COM1 0x24
#define VEC_PARALLEL 0x25
#define VEC_FLOPPY 0x26
#define VEC_MYSTERY 0x27
#define VEC_MOUSE 0x2c
#endif

View file

@ -0,0 +1,395 @@
/**
** @file ops.h
**
** @author Warren R. Carithers
**
** @brief Inline escapes to assembly for efficiency
**
** Inspiration from:
** Martins Mozeiko, https://gist.github.com/mmozeiko/f68ad2546bd6ab953315
** MIT's xv6, https://github.com/mit-pdos/xv6-public
**
** Note: normally, GCC doesn't inline unless the optimization level is
** over 1. This can be forced by adding
**
** __attribute__((always_inline))
**
** after the parameter list on each declaration. This is enabled by
** defining the compile-time CPP symbol FORCE_INLINING.
*/
#ifndef OPS_H_
#define OPS_H_
#include <common.h>
#ifndef ASM_SRC
// control "forced" inlining
#ifdef FORCE_INLINING
#define OPSINLINED __attribute__((always_inline))
#else
#define OPSINLINED /* no-op */
#endif /* FORCE_INLINING */
/****************************
** Data movement
****************************/
/**
** Block move functions
**
** Variations: movsb(), movsl(), movsq()
**
** Description: Copy from source buffer to destination buffer
**
** @param dst Destination buffer
** @param src Source buffer
** @param len Byte count
*/
static inline void movsb(void *dst, const void *src, uint32_t len) OPSINLINED
{
__asm__ __volatile__("cld; rep movsb"
: "+D"(dst), "+S"(src), "+c"(len)
:
: "memory");
}
static inline void movsw(void *dst, const void *src, uint32_t len) OPSINLINED
{
__asm__ __volatile__("cld; rep movsw"
: "+D"(dst), "+S"(src), "+c"(len)
:
: "memory");
}
static inline void movsl(void *dst, const void *src, uint32_t len) OPSINLINED
{
__asm__ __volatile__("cld; rep movsl"
: "+D"(dst), "+S"(src), "+c"(len)
:
: "memory");
}
static inline void movsq(void *dst, const void *src, uint32_t len) OPSINLINED
{
__asm__ __volatile__("cld; rep movsq"
: "+D"(dst), "+S"(src), "+c"(len)
:
: "memory");
}
/**
** Block store functions
**
** Variations: stosb(), stosw(), stosl()
**
** Description: Store a specific value into destination buffer
**
** @param dst Destination buffer
** @param val Data to copy
** @param len Byte count
*/
static inline void stosb(void *dst, uint8_t val, uint32_t len) OPSINLINED
{
__asm__ __volatile__("cld; rep stosb"
: "=D"(dst), "=c"(len)
: "0"(dst), "1"(len), "a"(val)
: "memory", "cc");
}
static inline void stosw(void *dst, uint16_t val, uint32_t len) OPSINLINED
{
__asm__ __volatile__("cld; rep stos2"
: "=D"(dst), "=c"(len)
: "0"(dst), "1"(len), "a"(val)
: "memory", "cc");
}
static inline void stosl(void *dst, uint32_t val, uint32_t len) OPSINLINED
{
__asm__ __volatile__("cld; rep stosl"
: "=D"(dst), "=c"(len)
: "0"(dst), "1"(len), "a"(val)
: "memory", "cc");
}
/****************************
** Special register access
****************************/
/**
** Register read functions
**
** Variations: r_cr0(), r_cr2(), r_cr3(), r_cr4(), r_eflags(),
** r_ebp(), r_esp()
**
** Description: Reads the register indicated by its name
**
** @return Contents of the register
*/
static inline uint32_t r_cr0(void) OPSINLINED
{
uint32_t val;
__asm__ __volatile__("movl %%cr0,%0" : "=r"(val));
return val;
}
static inline uint32_t r_cr2(void) OPSINLINED
{
uint32_t val;
__asm__ __volatile__("movl %%cr2,%0" : "=r"(val));
return val;
}
static inline uint32_t r_cr3(void) OPSINLINED
{
uint32_t val;
__asm__ __volatile__("movl %%cr3,%0" : "=r"(val));
return val;
}
static inline uint32_t r_cr4(void) OPSINLINED
{
uint32_t val;
__asm__ __volatile__("movl %%cr4,%0" : "=r"(val));
return val;
}
static inline uint32_t r_eflags(void) OPSINLINED
{
uint32_t val;
__asm__ __volatile__("pushfl; popl %0" : "=r"(val));
return val;
}
static inline uint32_t r_ebp(void) OPSINLINED
{
uint32_t val;
__asm__ __volatile__("movl %%ebp,%0" : "=r"(val));
return val;
}
static inline uint32_t r_esp(void) OPSINLINED
{
uint32_t val;
__asm__ __volatile__("movl %%esp,%0" : "=r"(val));
return val;
}
/**
** Register write functions
**
** Variations: w_cr0(), w_cr2(), w_cr3(), w_cr4(), w_eflags()
**
** Description: Writes a value into the CR indicated by its name
*/
static inline void w_cr0(uint32_t val) OPSINLINED
{
__asm__ __volatile__("movl %0,%%cr0" : : "r"(val));
}
static inline void w_cr2(uint32_t val) OPSINLINED
{
__asm__ __volatile__("movl %0,%%cr2" : : "r"(val));
}
static inline void w_cr3(uint32_t val) OPSINLINED
{
__asm__ __volatile__("movl %0,%%cr3" : : "r"(val));
}
static inline void w_cr4(uint32_t val) OPSINLINED
{
__asm__ __volatile__("movl %0,%%cr4" : : "r"(val));
}
static inline void w_eflags(uint32_t eflags) OPSINLINED
{
__asm__ __volatile__("pushl %0; popfl" : : "r"(eflags));
}
/**
** Descriptor table load functions
**
** Variations: w_gdt(), w_idt()
**
** Description: Load an address into the specified processor register
**
** @param addr The value to be loaded into the register
*/
static inline void w_gdt(void *addr) OPSINLINED
{
__asm__ __volatile__("lgdt (%0)" : : "r"(addr));
}
static inline void w_idt(void *addr) OPSINLINED
{
__asm__ __volatile__("lidt (%0)" : : "r"(addr));
}
/**
** CPU ID access
**
** Description: Retrieve CPUID information
**
** @param op Value to be placed into %eax for the operation
** @param ap Pointer to where %eax contents should be saved, or NULL
** @param bp Pointer to where %ebx contents should be saved, or NULL
** @param cp Pointer to where %ecx contents should be saved, or NULL
** @param dp Pointer to where %edx contents should be saved, or NULL
*/
static inline void cpuid(uint32_t op, uint32_t *ap, uint32_t *bp, uint32_t *cp,
uint32_t *dp) OPSINLINED
{
uint32_t eax, ebx, ecx, edx;
__asm__ __volatile__("cpuid"
: "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx)
: "a"(op));
if (ap)
*ap = eax;
if (bp)
*bp = ebx;
if (cp)
*cp = ecx;
if (dp)
*dp = edx;
}
/****************************
** TLB management
****************************/
/**
** TLB invalidation for one page
**
** Description: Invalidate the TLB entry for an address
**
** @param addr An address within the page to be flushed
*/
static inline void invlpg(uint32_t addr) OPSINLINED
{
__asm__ __volatile__("invlpg (%0)" : : "r"(addr) : "memory");
}
/**
** TLB invalidation for all pages
**
** Description: Flush all entries from the TLB
**
** We do this by changing CR3.
*/
static inline void flushtlb(void) OPSINLINED
{
uint32_t cr3;
__asm__ __volatile__("movl %%cr3,%0" : "=r"(cr3));
__asm__ __volatile__("movl %0,%%cr2" : : "r"(cr3));
}
/****************************
** I/O instructions
****************************/
/**
** Name: inN
**
** Variations: inb(), inw(), inl()
**
** Description: Read some amount of data from the supplied I/O port
**
** @param port The i/o port to read from
**
** @return The data read from the specified port
*/
static inline uint8_t inb(int port) OPSINLINED
{
uint8_t data;
__asm__ __volatile__("inb %w1,%0" : "=a"(data) : "d"(port));
return data;
}
static inline uint16_t inw(int port) OPSINLINED
{
uint16_t data;
__asm__ __volatile__("inw %w1,%0" : "=a"(data) : "d"(port));
return data;
}
static inline uint32_t inl(int port) OPSINLINED
{
uint32_t data;
__asm__ __volatile__("inl %w1,%0" : "=a"(data) : "d"(port));
return data;
}
/**
** Name: outN
**
** Variations: outb(), outw(), outl()
**
** Description: Write some data to the specified I/O port
**
** @param port The i/o port to write to
** @param data The data to be written to the port
**
** @return The data read from the specified port
*/
static inline void outb(int port, uint8_t data) OPSINLINED
{
__asm__ __volatile__("outb %0,%w1" : : "a"(data), "d"(port));
}
static inline void outw(int port, uint16_t data) OPSINLINED
{
__asm__ __volatile__("outw %0,%w1" : : "a"(data), "d"(port));
}
static inline void outl(int port, uint32_t data) OPSINLINED
{
__asm__ __volatile__("outl %0,%w1" : : "a"(data), "d"(port));
}
/****************************
** Miscellaneous instructions
****************************/
/**
** Name: breakpoint
**
** Description: Cause a breakpoint interrupt for debugging purposes
*/
static inline void breakpoint(void) OPSINLINED
{
__asm__ __volatile__("int3");
}
/**
** Name: get_ra
**
** Description: Get the return address for the calling function
** (i.e., where whoever called us will go back to)
**
** @return The address the calling routine will return to as a uint32_t
*/
static inline uint32_t get_ra(void) OPSINLINED
{
uint32_t val;
__asm__ __volatile__("movl 4(%%ebp),%0" : "=r"(val));
return val;
}
/**
** Name: ev_wait
**
** Description: Pause until something happens
*/
static inline void ev_wait(void) OPSINLINED
{
__asm__ __volatile__("sti ; hlt");
}
/**
** Name: xchgl
**
** Description: Perform an atomic exchange with memory
**
** @param addr Memory location to be modified
** @param data Data to exchange
**
** @return The old contents of the memory location
*/
static inline uint32_t xchgl(volatile uint32_t *addr, uint32_t data) OPSINLINED
{
uint32_t old;
// + indicates a read-modify-write operand
__asm__ __volatile__("lock; xchgl %0, %1"
: "+m"(*addr), "=a"(old)
: "1"(data)
: "cc");
return old;
}
#endif /* !ASM_SRC */
#endif

View file

@ -0,0 +1,136 @@
/**
** @file pic.h
**
** @author Warren R. Carithers
** @author K. Reek
**
** Definitions of constants and macros for the Intel 8259 Programmable
** Interrupt Controller.
**
*/
#ifndef X86PIC_H_
#define X86PIC_H_
/*
** Our expected configuration is two PICs, with the secondary connected
** through the IRQ2 pin of the primary.
*/
/*
** Port addresses for the command port and interrupt mask register port
** for both the primary and secondary PICs.
*/
#define PIC1_CMD 0x20 // primary command
#define PIC1_DATA (PIC1_CMD + 1) // primary data / int mask register
#define PIC2_CMD 0xA0 // secondary command
#define PIC2_DATA (PIC2_CMD + 1) // secondary data / int mask register
/*
** Initialization Command Word (ICW) definitions
**
** Initialization sequence:
** ICW1 Init command is sent to each command port.
** ICW2 vector commands are sent to the data ports.
** If "cascade mode" was selected, send ICW3 commands to the data ports.
** If "need ICW4" was selected, send ICW4 commands to the data ports.
**
** Following that sequence, the PIC is ready to accept interrupts;
** it will also accept Output Command Words (OCWs) to the data ports.
**
** PIC1_* defines are intended for the primary PIC
** PIC2_* defines are intended for the secondary PIC
** PIC_* defines are sent to both PICs
*/
/*
** ICW1: initialization, send to command port
*/
#define PIC_CW1_INIT 0x10 // start initialization sequence
#define PIC_CW1_NEED4 0x01 // ICW4 will also be set
#define PIC_CW1_SINGLE 0x02 // select single (vs. cascade) mode
#define PIC_CW1_INTVAL 0x04 // set call interval to 4 (vs. 8)
#define PIC_CW1_LEVEL 0x08 // use level-triggered mode (vs. edge)
/*
** ICW2: interrupt vector base offsets, send to data port
*/
#define PIC1_CW2_VECBASE 0x20 // IRQ0 int vector number
#define PIC2_CW2_VECBASE 0x28 // IRQ8 int vector number
/*
** ICW3: secondary::primary attachment, send to data port
*/
#define PIC1_CW3_SEC_IRQ2 0x04 // bit mask: secondary is on pin 2
#define PIC2_CW3_SEC_ID 0x02 // integer: secondary id
/*
** ICW4: operating mode, send to data port
*/
#define PIC_CW4_PM86 0x01 // 8086 mode (vs. 8080/8085)
#define PIC_CW4_AUTOEOI 0x02 // do auto eoi's
#define PIC_CW4_UNBUF 0x00 // unbuffered mode
#define PIC_CW4_SEC_BUF 0x08 // put secondary in buffered mode
#define PIC_CW4_PRI_BUF 0x0C // put primary in buffered mode
#define PIC_CW4_SFNMODE 0x10 // "special fully nested" mode
/*
** Operation Control Words (OCWs)
**
** After the init sequence, can send these
*/
/*
** OCW1: interrupt mask; send to data port
*/
#define PIC_MASK_NONE 0x00 // allow all interrupts
#define PIC_MASK_NO_IRQ0 0x01 // prevent IRQ0 interrupts
#define PIC_MASK_NO_IRQ1 0x02 // prevent IRQ1 interrupts
#define PIC_MASK_NO_IRQ2 0x04 // prevent IRQ2 interrupts
#define PIC_MASK_NO_IRQ3 0x08 // prevent IRQ3 interrupts
#define PIC_MASK_NO_IRQ4 0x10 // prevent IRQ4 interrupts
#define PIC_MASK_NO_IRQ5 0x20 // prevent IRQ5 interrupts
#define PIC_MASK_NO_IRQ6 0x40 // prevent IRQ6 interrupts
#define PIC_MASK_NO_IRQ7 0x80 // prevent IRQ7 interrupts
#define PIC_MASK_ALL 0xff // prevent all interrupts
/*
** OCW2: EOI control, interrupt level; send to command port
*/
#define PIC_LVL_0 0x00 // act on IRQ level 0
#define PIC_LVL_1 0x01 // act on IRQ level 1
#define PIC_LVL_2 0x02 // act on IRQ level 2
#define PIC_LVL_3 0x03 // act on IRQ level 3
#define PIC_LVL_4 0x04 // act on IRQ level 4
#define PIC_LVL_5 0x05 // act on IRQ level 5
#define PIC_LVL_6 0x06 // act on IRQ level 6
#define PIC_LVL_7 0x07 // act on IRQ level 7
#define PIC_EOI_NON_SPEC 0x20 // non-specific EOI command
#define PIC_EOI PIC_EOI_NON_SPEC
#define PIC_EOI_SPEC 0x60 // specific EOI command
#define PIC_SEOI PIC_EOI_SPEC
#define PIC_SEOI_LVL0 (PIC_EOI_SPEC | PIC_LVL_0)
#define PIC_SEOI_LVL1 (PIC_EOI_SPEC | PIC_LVL_1)
#define PIC_SEOI_LVL2 (PIC_EOI_SPEC | PIC_LVL_2)
#define PIC_SEOI_LVL3 (PIC_EOI_SPEC | PIC_LVL_3)
#define PIC_SEOI_LVL4 (PIC_EOI_SPEC | PIC_LVL_4)
#define PIC_SEOI_LVL5 (PIC_EOI_SPEC | PIC_LVL_5)
#define PIC_SEOI_LVL6 (PIC_EOI_SPEC | PIC_LVL_6)
#define PIC_SEOI_LVL7 (PIC_EOI_SPEC | PIC_LVL_7)
#define PIC_EOI_ROT_NONSP 0xa0 // rotate on non-spec EOI cmd
#define PIC_EOI_SET_ROT_AUTO 0x80 // set "rotate in auto EOI mode"
#define PIC_EOI_CLR_ROT_AUTO 0x00 // clear "rotate in auto EOI mode"
#define PIC_EOI_ROT_SPEC 0xe0 // rotate on spec EOI cmd (+ level)
#define PIC_EOI_SET_PRIO 0xc0 // set priority (+ level)
#define PIC_EOI_NOP 0x40 // no operation
/*
** OCW3: read requests, special mask mode; send to command port
*/
#define PIC_READIRR 0x0a // read the IR register
#define PIC_READISR 0x0b // read the IS register
#define PIC_POLL 0x0c // poll
#define PIC_MASK_RESET 0x48 // reset special mask mode
#define PIC_MASK_SET 0x68 // set special mask mode
#endif

View file

@ -0,0 +1,81 @@
/*
** @file pit.h
**
** @author Warren R. Carithers
** @author K. Reek
**
** Definitions of constants and macros for the
** Intel 8254 Programmable Interval Timer
**
*/
#ifndef X86PIT_H_
#define X86PIT_H_
/*
** Hardware timer (Intel 8254 Programmable Interval Timer)
**
** Control word layout:
**
** Bit 7 6 | 5 4 | 3 2 1 | 0
** Field SC1 SC0|RW1 RW0|M2 M1 M0 |BCD
**
** SC - select counter
** RW - read/write
** M - mode
** BCD - binary or BCD counter
*/
/* Frequency settings */
#define PIT_DEFAULT_TICKS_PER_SECOND 18 // actually 18.2065Hz
#define PIT_DEFAULT_MS_PER_TICK (1000 / PIT_DEFAULT_TICKS_PER_SECOND)
#define PIT_FREQ 1193182 // clock cycles/sec
/* Port assignments */
#define PIT_BASE_PORT 0x40 // I/O port for the timer
#define PIT_0_PORT (PIT_BASE_PORT)
#define PIT_1_PORT (PIT_BASE_PORT + 1)
#define PIT_2_PORT (PIT_BASE_PORT + 2)
#define PIT_CONTROL_PORT (PIT_BASE_PORT + 3)
/* BCD field */
#define PIT_USE_DECIMAL 0x00 // 16-bit binary counter (default)
#define PIT_USE_BCD 0x01 // BCD counter
/* Timer modes */
#define PIT_MODE_0 0x00 // int on terminal count
#define PIT_MODE_1 0x02 // one-shot
#define PIT_MODE_2 0x04 // divide-by-N
#define PIT_MODE_3 0x06 // square-wave
#define PIT_MODE_4 0x08 // software strobe
#define PIT_MODE_5 0x0a // hardware strobe
/* Timer 0 settings */
#define PIT_0_SELECT 0x00 // select timer 0
#define PIT_0_LOAD 0x30 // load LSB, then MSB
#define PIT_0_NDIV PIT_MODE_2 // divide-by-N counter
#define PIT_0_SQUARE PIT_MODE_3 // square-wave mode
#define PIT_0_ENDSIGNAL 0x00 // assert OUT at end of count
/* Timer 1 settings */
#define PIT_1_SELECT 0x40 // select timer 1
#define PIT_1_READ 0x30 // read/load LSB then MSB
#define PIT_1_RATE 0x06 // square-wave, for USART
/* Timer 2 settings */
#define PIT_2_SELECT 0x80 // select timer 1
#define PIT_2_READ 0x30 // read/load LSB then MSB
#define PIT_2_RATE 0x06 // square-wave, for USART
/* Timer read-back */
#define PIT_READBACK 0xc0 // perform a read-back
#define PIT_RB_NOT_COUNT 0x20 // don't latch the count
#define PIT_RB_NOT_STATUS 0x10 // don't latch the status
#define PIT_RB_CHAN_2 0x08 // read back channel 2
#define PIT_RB_CHAN_1 0x04 // read back channel 1
#define PIT_RB_CHAN_0 0x02 // read back channel 0
#define PIT_RB_ACCESS_MASK 0x30 // access mode field
#define PIT_RB_OP_MASK 0x0e // oper mode field
#define PIT_RB_BCD_MASK 0x01 // BCD mode field
#endif

View file

@ -0,0 +1,348 @@
/*
** @file uart.h
**
** @author M. Reek
** @authors K. Reek, Warren R. Carithers
**
** Definitions for a 16540/16550 compatible UART. Definitions are taken
** from datasheets for the National Semiconductor INS8250, NS16450, and
** NS16550 UART chips, and the PC87309 Super I/O legacy peripheral chip.
**
** The naming convention is UAx_yyy_zzzzz. "x" is either 4 or 5 (see below),
** "yyy" is the name of the register to which this value applies, and
** "zzzzz" is the name of the value or field.
**
** The UA4 prefix denotes 16540 compatible functions, available in both
** chips. The UA5 prefix denotes 16550-only functions (primarily the FIFOs).
**
** For many items there are two names: one short one that matches the name
** in the chip manual, and another that is more readable.
*/
#ifndef UART_H
#define UART_H
/*********************************************************************
***************************** I/O PORTS ******************************
*********************************************************************/
/*
** Base port number assigned to the device
*/
#define UA4_COM1_PORT 0x3f8
#define UA4_COM2_PORT 0x2f8
#define UA4_COM3_PORT 0x3e8
#define UA4_COM4_PORT 0x2e8
// short name for the one we'll use
#define UA4_PORT UA4_COM1_PORT
#define UA5_PORT UA4_COM1_PORT
/*
** Registers
**
** The 164x0 chips have the following registers. The (RO) and (WO)
** suffixes indicate read-only and write-only access.
**
** Index Register(s)
** ===== =========================================
** 0 Receiver Data (RO), Transmitter Data (WO)
** 1 Interrupt Enable
** 2 Interrupt ID (RO), FIFO Control (WO)
** 3 Line Control, Divisor Latch
** 4 Modem Control
** 5 Line Status
** 6 Modem Status
** 7 Scratch
**
** Registers indices are relative to the base I/O port for the
** specific UART port being used (e.g., for COM1, the port addresses
** are 0x3f8 through 0x3ff). When two registers share a port and have
** different access methods (RO vs. WO), a read from the port accesses
** the RO register and a write to the port access the WO register.
**
** The Line Control and Divisor Latch registers are accessed by writing
** a byte to the port; the high-order bit determines which register is
** accessed (0 selects Line Control, 1 selects Divisor Latch), with the
** remaining bits selecting fields within the indicated register.
*/
/*
** Receiver Data Register (read-only)
*/
#define UA4_RXD (UA4_PORT + 0)
#define UA4_RX_DATA UA4_RXD
/*
** Transmitter Data Register (write-only)
*/
#define UA4_TXD (UA4_PORT + 0)
#define UA4_TX_DATA UA4_TXD
/*
** Interrupt Enable Register
*/
#define UA4_IER (UA4_PORT + 1)
#define UA4_INT_ENABLE_REG UA4_IER
// fields
#define UA4_IER_RX_IE 0x01 // Rcvr High-Data-Level Int Enable
#define UA4_IER_TX_IE 0x02 // Xmitter Low-data-level Int Enable
#define UA4_IER_LS_IE 0x04 // Line Status Int Enable
#define UA4_IER_MS_IE 0x08 // Modem Status Int Enable
// aliases
#define UA4_IER_RX_INT_ENABLE UA4_IER_RX_IE
#define UA4_IER_TX_INT_ENABLE UA4_IER_TX_IE
#define UA4_IER_LINE_STATUS_INT_ENABLE UA4_IER_LS_IE
#define UA4_IER_MODEM_STATUS_INT_ENABLE UA4_IER_MS_IE
/*
** Interrupt Identification Register (read-only)
**
** a.k.a. Event Identification Register
*/
#define UA4_IIR (UA4_PORT + 2)
#define UA4_EVENT_ID UA4_IIR
// fields
#define UA4_IIR_IPF 0x01 // Interrupt Pending flag
#define UA4_IIR_IPR_MASK 0x06 // Interrupt Priority mask
#define UA4_IIR_IPR0_MASK 0x02 // IPR bit 0 mask
#define UA4_IIR_IPR1_MASK 0x04 // IPR bit 1 mask
#define UA5_IIR_RXFT 0x08 // RX_FIFO Timeout
#define UA5_IIR_FEN0 0x40 // FIFOs Enabled
#define UA5_IIR_FEN1 0x80 // FIFOs Enabled
// aliases
#define UA4_IIR_INT_PENDING UA4_IIR_IPF
#define UA4_IIR_INT_PRIORITY UA4_IIR_IPR
#define UA5_IIR_RX_FIFO_TIMEOUT UA5_IIR_RXFT
#define UA5_IIR_FIFO_ENABLED_0 UA5_IIR_FEN0
#define UA5_IIR_FIFO_ENABLED_1 UA5_IIR_FEN1
// IIR interrupt priorities (four-bit values)
#define UA4_IIR_INT_PRI_MASK 0x0f // Mask for extracting int priority
#define UA4_IIR_NO_INT 0x01 // no interrupt
#define UA4_IIR_LINE_STATUS 0x06 // line status interrupt
#define UA4_IIR_RX 0x04 // Receiver High Data Level
#define UA5_IIR_RX_FIFO 0x0c // Receiver FIFO timeout (16550)
#define UA4_IIR_TX 0x02 // Transmitter Low Data level
#define UA4_IIR_MODEM_STATUS 0x00 // Modem Status
// aliases
#define UA4_IIR_NO_INT_PENDING UA4_IIR_NO_INT
#define UA4_IIR_LINE_STATUS_INT_PENDING UA4_IIR_LINE_STATUS
#define UA4_IIR_RX_INT_PENDING UA4_IIR_RX
#define UA5_IIR_RX_FIFO_TIMEOUT_INT_PENDING UA5_IIR_RX_FIFO
#define UA4_IIR_TX_INT_PENDING UA4_IIR_TX
#define UA4_IIR_MODEM_STATUS_INT_PENDING UA4_IIR_MODEM_STATUS
/*
** FIFO Control Register (16550 only, write-only)
*/
#define UA5_FCR (UA5_PORT + 2)
#define UA5_FIFO_CTL UA5_FCR
#define UA5_FCR_FIFO_RESET 0x00 // Reset the FIFO
#define UA5_FCR_FIFO_EN 0x01 // FIFO Enable
#define UA5_FCR_RXSR 0x02 // Receiver Soft Reset
#define UA5_FCR_TXSR 0x04 // Transmitter Soft Reset
#define UA5_FCR_TXFT_MASK 0x30 // TX_FIFO threshold level mask
#define UA5_FCR_TXFT0_MASK 0x10 // TXFT bit 0 mask
#define UA5_FCR_TXFT1_MASK 0x20 // TXFT bit 1 mask
#define UA5_FCR_TX_FIFO_1 0x00 // 1 char
#define UA5_FCR_TX_FIFO_3 0x10 // 3 char
#define UA5_FCR_TX_FIFO_9 0x20 // 9 char
#define UA5_FCR_TX_FIFO_13 0x30 // 13 char
#define UA5_FCR_RXFT_MASK 0xc0 // RX_FIFO threshold level mask
#define UA5_FCR_RXFT0_MASK 0x40 // RXFT bit 0 mask
#define UA5_FCR_RXFT1_MASK 0x80 // RXFT bit 1 mask
#define UA5_FCR_RX_FIFO_1 0x00 // 1 char
#define UA5_FCR_RX_FIFO_4 0x40 // 4 char
#define UA5_FCR_RX_FIFO_8 0x80 // 8 char
#define UA5_FCR_RX_FIFO_14 0xc0 // 14 char
// aliases
#define UA5_FCR_FIFO_ENABLED UA5_FCR_FIFO_EN
#define UA5_FCR_RX_SOFT_RESET UA5_FCR_RXSR
#define UA5_FCR_TX_SOFT_RESET UA5_FCR_TXSR
#define UA5_FCR_TX_FIFO_1_CHAR UA5_FCR_TX_FIFO_1
#define UA5_FCR_TX_FIFO_3_CHAR UA5_FCR_TX_FIFO_3
#define UA5_FCR_TX_FIFO_9_CHAR UA5_FCR_TX_FIFO_9
#define UA5_FCR_TX_FIFO_13_CHAR UA5_FCR_TX_FIFO_13
#define UA5_FCR_RX_FIFO_1_CHAR UA5_FCR_RX_FIFO_1
#define UA5_FCR_RX_FIFO_4_CHAR UA5_FCR_RX_FIFO_4
#define UA5_FCR_RX_FIFO_8_CHAR UA5_FCR_RX_FIFO_8
#define UA5_FCR_RX_FIFO_14_CHAR UA5_FCR_RX_FIFO_14
/*
** Line Control Register (available in all banks)
**
** Selected when bit 7 of the value written to the port is a 0.
*/
#define UA4_LCR (UA4_PORT + 3)
#define UA4_LINE_CTL UA4_LCR
#define UA4_LCR_WLS_MASK 0x03 // Word Length Select mask
#define UA4_LCR_WLS0_MASK 0x01 // WLS bit 0 mask
#define UA4_LCR_WLS1_MASK 0x02 // WLS bit 1 mask
#define UA4_LCR_WLS_5 0x00 // 5 bits per char
#define UA4_LCR_WLS_6 0x01 // 6 bits per char
#define UA4_LCR_WLS_7 0x02 // 7 bits per char
#define UA4_LCR_WLS_8 0x03 // 8 bits per char
#define UA4_LCR_STB 0x04 // Stop Bits
#define UA4_LCR_1_STOP_BIT 0x00
#define UA4_LCR_2_STOP_BIT 0x04
#define UA4_LCR_PEN 0x08 // Parity Enable
#define UA4_LCR_EPS 0x10 // Even Parity Select
#define UA4_LCR_STKP 0x20 // Sticky Parity
#define UA4_LCR_NO_PARITY 0x00
#define UA4_LCR_ODD_PARITY UA4_LCR_PEN
#define UA4_LCR_EVEN_PARITY (UA4_LCR_PEN | UA4_LCR_EPS)
#define UA4_LCR_PARITY_LOGIC_1 (UA4_LCR_PEN | UA4_LCR_STKP)
#define UA4_LCR_PARITY_LOGIC_0 (UA4_LCR_PEN | UA4_LCR_EPS | UA4_LCR_STKP)
#define UA4_LCR_SBRK 0x40 // Set Break
#define UA4_LCR_DLAB 0x80 // Divisor Latch select bit
// aliases
#define UA4_LCR_STOP_BITS UA4_LCR_STB
#define UA4_LCR_PARITY_ENABLE UA4_LCR_PEN
#define UA4_LCR_SET_BREAK UA4_LCR_SBRK
#define UA4_LCR_BANK_SELECT_ENABLE UA4_LCR_BKSE
/*
** Divisor Latch Registers
** Divisor Latch Least Significant (DLL)
** Divisor Latch Most Significant (DLM)
**
** These contain the lower and upper halves of the 16-bit divisor for
** baud rate generation.
**
** Accessing them requires sending a command to LCR with the most
** significant bit (0x80, the DLAB field) set. This "unlocks" the
** Divisor Latch registers, which are accessed at UA4_PORT+0 and
** UA4_PORT+1 (i.e., in place of the RXD/TXD and IE registers). To
** "re-lock" the Divisor Latch registers, write a command byte to
** LCR with 0 in the DLAB bit.
*/
#define UA4_DLL (UA4_PORT + 0) // Divisor Latch (least sig.)
#define UA4_DLM (UA4_PORT + 1) // Divisor Latch (most sig.)
// aliases
#define UA4_DIVISOR_LATCH_LS UA4_DLL
#define UA4_DIVISOR_LATCH_MS UA4_DLM
// Baud rate divisor high and low bytes
#define BAUD_HIGH_BYTE(x) (((x) >> 8) & 0xff)
#define BAUD_LOW_BYTE(x) ((x) & 0xff)
// Baud rate divisors
#define DL_BAUD_50 2304
#define DL_BAUD_75 1536
#define DL_BAUD_110 1047
#define DL_BAUD_150 768
#define DL_BAUD_300 384
#define DL_BAUD_600 192
#define DL_BAUD_1200 96
#define DL_BAUD_1800 64
#define DL_BAUD_2000 58
#define DL_BAUD_2400 48
#define DL_BAUD_3600 32
#define DL_BAUD_4800 24
#define DL_BAUD_7200 16
#define DL_BAUD_9600 12
#define DL_BAUD_14400 8
#define DL_BAUD_19200 6
#define DL_BAUD_28800 4
#define DL_BAUD_38400 3
#define DL_BAUD_57600 2
#define DL_BAUD_115200 1
/*
** Modem Control Register
*/
#define UA4_MCR (UA4_PORT + 4)
#define UA4_MODEM_CTL UA4_MCR
#define UA4_MCR_DTR 0x01 // Data Terminal Ready
#define UA4_MCR_RTS 0x02 // Ready to Send
#define UA4_MCR_RILP 0x04 // Loopback Interrupt Request
#define UA4_MCR_ISEN 0x08 // Interrupt Signal Enable
#define UA4_MCR_DCDLP 0x08 // DCD Loopback
#define UA4_MCR_LOOP 0x10 // Loopback Enable
// aliases
#define UA4_MCR_DATA_TERMINAL_READY UA4_MCR_DTR
#define UA4_MCR_READY_TO_SEND UA4_MCR_RTS
#define UA4_MCR_LOOPBACK_INT_REQ UA4_MCR_RILP
#define UA4_MCR_INT_SIGNAL_ENABLE UA4_MCR_ISEN
#define UA4_MCR_LOOPBACK_DCD UA4_MCR_DCDLP
#define UA4_MCR_LOOPBACK_ENABLE UA4_MCR_LOOP
/*
** Line Status Register
*/
#define UA4_LSR (UA4_PORT + 5)
#define UA4_LINE_STATUS UA4_LSR
#define UA4_LSR_RXDA 0x01 // Receiver Data Available
#define UA4_LSR_OE 0x02 // Overrun Error
#define UA4_LSR_PE 0x04 // Parity Error
#define UA4_LSR_FE 0x08 // Framing Error
#define UA4_LSR_BRK 0x10 // Break Event Detected
#define UA4_LSR_TXRDY 0x20 // Transmitter Ready
#define UA4_LSR_TXEMP 0x40 // Transmitter Empty
#define UA4_LSR_ER_INF 0x80 // Error in RX_FIFO
// aliases
#define UA4_LSR_RX_DATA_AVAILABLE UA4_LSR_RXDA
#define UA4_LSR_OVERRUN_ERROR UA4_LSR_OE
#define UA4_LSR_PARITY_ERROR UA4_LSR_PE
#define UA4_LSR_FRAMING_ERROR UA4_LSR_FE
#define UA4_LSR_BREAK_DETECTED UA4_LSR_BRK
#define UA4_LSR_TX_READY UA4_LSR_TXRDY
#define UA4_LSR_TX_EMPTY UA4_LSR_TXEMP
#define UA4_LSR_RX_FIFO_ERROR UA4_LSR_ER_INF
/*
** Modem Status Register
*/
#define UA4_MSR (UA4_PORT + 6)
#define UA4_MODEM_STATUS UA4_MSR
#define UA4_MSR_DCTS 0x01 // Delta Clear to Send
#define UA4_MSR_DDSR 0x02 // Delta Data Set Ready
#define UA4_MSR_TERI 0x04 // Trailing Edge Ring Indicate
#define UA4_MSR_DDCD 0x08 // Delta Data Carrier Detect
#define UA4_MSR_CTS 0x10 // Clear to Send
#define UA4_MSR_DSR 0x20 // Data Set Ready
#define UA4_MSR_RI 0x40 // Ring Indicate
#define UA4_MSR_DCD 0x80 // Data Carrier Detect
// aliases
#define UA4_MSR_DELTA_CLEAR_TO_SEND UA4_MSR_DCTS
#define UA4_MSR_DELTA_DATA_SET_READY UA4_MSR_DDSR
#define UA4_MSR_TRAILING_EDGE_RING UA4_MSR_TERI
#define UA4_MSR_DELTA_DATA_CARRIER_DETECT UA4_MSR_DDCD
#define UA4_MSR_CLEAR_TO_SEND UA4_MSR_CTS
#define UA4_MSR_DATA_SET_READY UA4_MSR_DSR
#define UA4_MSR_RING_INDICATE UA4_MSR_RI
#define UA4_MSR_DATA_CARRIER_DETECT UA4_MSR_DCD
/*
** Scratch Register
**
** Not used by the UART; usable as a "scratchpad" register for
** temporary storage.
*/
#define UA4_SCR (UA4_PORT + 7)
#define UA4_SCRATCH UA4_UA5_SCR
#endif /* uart.h */

400
kernel/old/kernel.c Normal file
View file

@ -0,0 +1,400 @@
/**
** @file kernel.c
**
** @author CSCI-452 class of 20245
**
** @brief Kernel support routines
*/
#define KERNEL_SRC
#include <common.h>
#include <cio.h>
#include <clock.h>
#include <kmem.h>
#include <procs.h>
#include <sio.h>
#include <syscalls.h>
#include <user.h>
#include <userids.h>
#include <vm.h>
/*
** PRIVATE DEFINITIONS
*/
/*
** PRIVATE DATA TYPES
*/
/*
** PRIVATE GLOBAL VARIABLES
*/
/*
** PUBLIC GLOBAL VARIABLES
*/
// character buffers, usable throughout the OS
// nto guaranteed to retain their contents across an exception return
char b256[256]; // primarily used for message creation
char b512[512]; // used by PANIC macro
/*
** PRIVATE FUNCTIONS
*/
/*
** PRIVATE FUNCTIONS
*/
/**
** report - report the system configuration
**
** Prints configuration information about the OS on the console monitor.
**
** @param dtrace Decode the TRACE options
*/
static void kreport(bool_t dtrace)
{
cio_puts("\n-------------------------------\n");
cio_printf("Config: N_PROCS = %d", N_PROCS);
cio_printf(" N_PRIOS = %d", N_PRIOS);
cio_printf(" N_STATES = %d", N_STATES);
cio_printf(" CLOCK = %dHz\n", CLOCK_FREQ);
// This code is ugly, but it's the simplest way to
// print out the values of compile-time options
// without spending a lot of execution time at it.
cio_puts("Options: "
#ifdef RPT_INT_UNEXP
" R-uint"
#endif
#ifdef RPT_INT_MYSTERY
" R-mint"
#endif
#ifdef TRACE_CX
" CX"
#endif
#ifdef CONSOLE_STATS
" Cstats"
#endif
); // end of cio_puts() call
#ifdef SANITY
cio_printf(" SANITY = %d", SANITY);
#endif
#ifdef STATUS
cio_printf(" STATUS = %d", STATUS);
#endif
#if TRACE > 0
cio_printf(" TRACE = 0x%04x\n", TRACE);
// decode the trace settings if that was requested
if (TRACING_SOMETHING && dtrace) {
// this one is simpler - we rely on string literal
// concatenation in the C compiler to create one
// long string to print out
cio_puts("Tracing:"
#if TRACING_PCB
" PCB"
#endif
#if TRACING_VM
" VM"
#endif
#if TRACING_QUEUE
" QUE"
#endif
#if TRACING_SCHED
" SCHED"
#endif
#if TRACING_DISPATCH
" DISPATCH"
#endif
#if TRACING_SYSCALLS
" SCALL"
#endif
#if TRACING_SYSRETS
" SRET"
#endif
#if TRACING_EXIT
" EXIT"
#endif
#if TRACING_INIT
" INIT"
#endif
#if TRACING_KMEM
" KM"
#endif
#if TRACING_KMEM_FREELIST
" KMFL"
#endif
#if TRACING_KMEM_INIT
" KMIN"
#endif
#if TRACING_FORK
" FORK"
#endif
#if TRACING_EXEC
" EXEC"
#endif
#if TRACING_SIO_STAT
" S_STAT"
#endif
#if TRACING_SIO_ISR
" S_ISR"
#endif
#if TRACING_SIO_RD
" S_RD"
#endif
#if TRACING_SIO_WR
" S_WR"
#endif
#if TRACING_USER
" USER"
#endif
#if TRACING_ELF
" ELF"
#endif
); // end of cio_puts() call
}
#endif /* TRACE > 0 */
cio_putchar('\n');
}
#if defined(CONSOLE_STATS)
/**
** stats - callback routine for console statistics
**
** Called by the CIO module when a key is pressed on the
** console keyboard. Depending on the key, it will print
** statistics on the console display, or will cause the
** user shell process to be dispatched.
**
** This code runs as part of the CIO ISR.
*/
static void stats(int code)
{
switch (code) {
case 'a': // dump the active table
ptable_dump("\nActive processes", false);
break;
case 'c': // dump context info for all active PCBs
ctx_dump_all("\nContext dump");
break;
case 'p': // dump the active table and all PCBs
ptable_dump("\nActive processes", true);
break;
case 'q': // dump the queues
// code to dump out any/all queues
pcb_queue_dump("R", ready, true);
pcb_queue_dump("W", waiting, true);
pcb_queue_dump("S", sleeping, true);
pcb_queue_dump("Z", zombie, true);
pcb_queue_dump("I", sioread, true);
break;
case 'r': // print system configuration information
report(true);
break;
// ignore CR and LF
case '\r': // FALL THROUGH
case '\n':
break;
default:
cio_printf("console: unknown request '0x%02x'\n", code);
// FALL THROUGH
case 'h': // help message
cio_puts("\nCommands:\n"
" a -- dump the active table\n"
" c -- dump contexts for active processes\n"
" h -- this message\n"
" p -- dump the active table and all PCBs\n"
" q -- dump the queues\n"
" r -- print system configuration\n");
break;
}
}
#endif
/*
** PUBLIC FUNCTIONS
*/
/**
** main - system initialization routine
**
** Called by the startup code immediately before returning into the
** first user process.
**
** Making this type 'int' keeps the compiler happy.
*/
int main(void)
{
/*
** BOILERPLATE CODE - taken from basic framework
**
** Initialize interrupt stuff.
*/
init_interrupts(); // IDT and PIC initialization
/*
** Console I/O system.
**
** Does not depend on the other kernel modules, so we can
** initialize it before we initialize the kernel memory
** and queue modules.
*/
#if defined(CONSOLE_STATS)
cio_init(stats);
#else
cio_init(NULL); // no console callback routine
#endif
cio_clearscreen(); // wipe out whatever is there
/*
** TERM-SPECIFIC CODE STARTS HERE
*/
/*
** Initialize various OS modules
**
** Other modules (clock, SIO, syscall, etc.) are expected to
** install their own ISRs in their initialization routines.
*/
cio_puts("System initialization starting.\n");
cio_puts("-------------------------------\n");
cio_puts("Modules:");
// call the module initialization functions, being
// careful to follow any module precedence requirements
km_init(); // MUST BE FIRST
#if TRACING_KMEM || TRACING_KMEM_FREE
delay(DELAY_2_SEC); // approximately
#endif
// other module initialization calls here
clk_init(); // clock
pcb_init(); // process (PCBs, queues, scheduler)
#if TRACING_PCB
delay(DELAY_2_SEC);
#endif
sio_init(); // serial i/o
sys_init(); // system call
#if TRACING_SYSCALLS || TRACING_SYSRETS
delay(DELAY_2_SEC);
#endif
vm_init(); // virtual memory
user_init(); // user code handling
cio_puts("\nModule initialization complete.\n");
// report our configuration options
kreport(true);
cio_puts("-------------------------------\n");
delay(DELAY_2_SEC);
/*
** Other tasks typically performed here:
**
** Enabling any I/O devices (e.g., SIO xmit/rcv)
*/
/*
** Create the initial user process
**
** This code is largely stolen from the fork() and exec()
** implementations in syscalls.c; if those change, this must
** also change.
*/
// if we can't get a PCB, there's no use continuing!
assert(pcb_alloc(&init_pcb) == SUCCESS);
// fill in the necessary details
init_pcb->pid = PID_INIT;
init_pcb->state = STATE_NEW;
init_pcb->priority = PRIO_HIGH;
// find the 'init' program
prog_t *prog = user_locate(Init);
assert(prog != NULL);
// command-line arguments for 'init'
const char *args[2] = { "init", NULL };
// load it
assert(user_load(prog, init_pcb, args, true) == SUCCESS);
// send it on its merry way
schedule(init_pcb);
dispatch();
#ifdef TRACE_CX
// if we're using a scrolling region, wait a bit more and then set it up
delay(DELAY_7_SEC);
// define a scrolling region in the top 7 lines of the screen
cio_setscroll(0, 7, 99, 99);
// clear it
cio_clearscroll();
// clear the top line
cio_puts_at(
0, 0,
"* ");
// separator
cio_puts_at(
0, 6,
"================================================================================");
#endif
/*
** END OF TERM-SPECIFIC CODE
**
** Finally, report that we're all done.
*/
cio_puts("System initialization complete.\n");
cio_puts("-------------------------------\n");
sio_enable(SIO_RX);
#if 0
// produce a "system state" report
cio_puts( "System status: Queues " );
pcb_queue_dump( "R", ready, true );
pcb_queue_dump( "W", waiting, true );
pcb_queue_dump( "S", sleeping, true );
pcb_queue_dump( "Z", zombie, true );
pcb_queue_dump( "I", sioread, true );
ptable_dump_counts();
pcb_dump( "Current: ", current, true );
delay( DELAY_3_SEC );
vm_print( current->pdir, true, TwoLevel );
delay( DELAY_3_SEC );
#endif
return 0;
}

View file

@ -1,161 +0,0 @@
/*
** @file startup.S
**
** @author Jon Coles
** @authors Warren R. Carithers, K. Reek
**
** SP startup code.
**
** This code prepares the various registers for execution of
** the program. It sets up all the segment registers and the
** runtime stack. By the time this code is running, we're in
** protected mode already.
*/
#define KERNEL_SRC
#define ASM_SRC
# .arch i386
#include <common.h>
#include <bootstrap.h>
#include <x86/arch.h>
#include <x86/bios.h>
#include <vm.h>
/*
** Configuration options - define in Makefile
**
** CLEAR_BSS include code to clear all BSS space
** OS_CONFIG OS-related (vs. just standalone) variations
*/
/*
** A symbol for locating the beginning of the code.
*/
.text
.globl begtext
.globl _start
_start = V2PNC(begtext)
/*
** The entry point. When we get here, we have just entered protected
** mode, so all the segment registers are incorrect except for CS.
*/
begtext:
cli /* seems to be reset on entry to p. mode */
movb $NMI_ENABLE, %al /* re-enable NMIs (bootstrap */
outb $CMOS_ADDR /* turned them off) */
/*
** Set the data and stack segment registers (code segment register
** was set by the long jump that switched us into protected mode).
*/
xorl %eax, %eax /* clear EAX */
movw $GDT_DATA, %ax /* GDT entry #3 - data segment */
movw %ax, %ds /* for all four data segment registers */
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
movw $GDT_STACK, %ax /* entry #4 is the stack segment */
movw %ax, %ss
movl $TARGET_STACK, %esp /* set up the system stack pointer */
#ifdef CLEAR_BSS
/*
** Zero the BSS segment
**
** These symbols are defined automatically by the linker, but they're
** defined at their virtual addresses rather than their physical addresses,
** and we haven't enabled paging yet.
*/
.globl __bss_start, _end
movl $V2PNC(__bss_start), %edi
clearbss:
movl $0, (%edi)
addl $4, %edi
cmpl $V2PNC(_end), %edi
jb clearbss
#endif /* CLEAR_BSS */
/*
** Enable paging. We use "large" pages for the initial page directory
** so that a one-level hierarchy will work for us. Once we have set
** up our memory freelist, we'll create a two-level hierarchy using
** "normal" 4KB pages.
*/
# enable large pages
movl %cr4, %eax
orl $(CR4_PSE), %eax
movl %eax, %cr4
# set the page directory
.globl firstpdir
movl $(V2PNC(firstpdir)), %eax
movl %eax, %cr3
# turn on paging
movl %cr0, %eax
orl $(CR0_PG), %eax
movl %eax, %cr0
# reset our stack pointer
movl $(kstack + SZ_KSTACK), %esp
# set the initial frame pointer
xorl %ebp, %ebp
# now, jump and switch into using high addresses
# we use an indirect jump here because the assembler
# would ordinarily generate a PC-relative target
# address for the jump, which would not have the
# desired effect
movl $onward, %eax
jmp *%eax
onward:
/*
** Call the system initialization routine.
**
** Alternate idea: push the address of isr_restore
** and just do an indirect jump?
*/
.globl main
movl $main, %eax
call *%eax
/*
** At this point, main() must have created the first user
** process, and we're ready to shift into user mode. The user
** stack for that process must have the initial context in it;
** we treat this as a "return from interrupt" event, and just
** transfer to the code that restores the user context.
*/
.globl isr_restore
jmp isr_restore
.data
/*
** Define the kernel stack here, at a multiple-of-16 address
*/
.p2align 4
.globl kstack
kstack: .space SZ_KSTACK, 0
/*
** Define the initial kernel ESP here, as well. It should point
** to the first byte after the stack.
*/
.globl kernel_esp
kernel_esp:
.long kstack + SZ_KSTACK

25
ulib/entry.S Normal file
View file

@ -0,0 +1,25 @@
//
// user-level startup routine
//
.text
.globl _start
.globl main
.globl exit
// entry point - this is where the kernel starts us running
_start:
// we immediately call main()
call main
// if we come back from that, it means the user
// program didn't call exit(), in which case the
// value returned from main() is the exit status
// push that value onto the stack and call exit()
subl $12, %esp
pushl %eax
call exit
// if we come back from that, something bad has
// happened, so we just lock up
1: jmp 1b

32
ulib/spawn.c Normal file
View file

@ -0,0 +1,32 @@
#include <stdio.h>
#include <error.h>
#include <unistd.h>
int wait(int32_t *status)
{
return (waitpid(0, status));
}
int spawn(uint_t prog, char **args)
{
int32_t pid;
pid = fork();
if (pid != 0) {
// failure, or we are the parent
return (pid);
}
// we are the child
pid = getpid();
// child inherits parent's priority level
exec(prog, args);
// uh-oh....
fprintf(stderr, "Child %d exec() #%u failed\n", pid, prog);
exit(EXIT_FAILURE);
}

93
ulib/syscall.S Normal file
View file

@ -0,0 +1,93 @@
/**
** @file ulibs.S
**
** @author CSCI-452 class of 20245
**
** @brief assembly-language user-level library functions
*/
#define ASM_SRC
// get the system call codes
#include <syscalls.h>
/**
** System call stubs
**
** All have the same structure:
**
** move a code into EAX
** generate the interrupt
** return to the caller
**
** As these are simple "leaf" routines, we don't use
** the standard enter/leave method to set up a stack
** frame - that takes time, and we don't really need it.
**
** Could be modified to use the UNIX/Linux convention of
** having the syscall code set the 'C' flag to indicate that
** the value being returned in %EAX is an error code:
**
** ...
** int $VEC_SYSCALL
** jc set_errno
** ret
** ...
**
** .globl errno
** set_errno:
** movl %eax, errno
** movl $-1, %eax
** ret
*/
#define SYSCALL(name) \
.globl name ; \
name: ; \
movl $SYS_##name, %eax ; \
int $VEC_SYSCALL ; \
ret
/*
** "real" system calls
*/
SYSCALL(exit)
SYSCALL(waitpid)
SYSCALL(fork)
SYSCALL(exec)
SYSCALL(read)
SYSCALL(write)
SYSCALL(getpid)
SYSCALL(getppid)
SYSCALL(gettime)
SYSCALL(getprio)
SYSCALL(setprio)
SYSCALL(kill)
SYSCALL(sleep)
/*
** This is a bogus system call; it's here so that we can test
** our handling of out-of-range syscall codes in the syscall ISR.
*/
SYSCALL(bogus)
/*
** Other library functions
*/
/**
** fake_exit()
**
** Dummy "startup" function
**
** calls exit(%eax) - serves as the "return to" code for
** main() functions, in case they don't call exit() themselves
*/
.globl fake_exit
fake_exit:
// alternate: could push a "fake exit" status
pushl %eax // termination status returned by main()
call exit // terminate this process