summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan McFarlane <i.mcfarlane2002@gmail.com>2025-04-22 14:51:47 -0400
committerIan McFarlane <i.mcfarlane2002@gmail.com>2025-04-22 14:51:47 -0400
commit325e2ea9aef0723645b86bdc773f02293747c495 (patch)
tree2d844c3e30a27eaf463fed851620221f3ad7d540
parenttry to find mcfg (diff)
parentforce rebuild on header change (diff)
downloadcomus-pcie.tar.gz
comus-pcie.tar.bz2
comus-pcie.zip
Merge branch 'main' into pciepcie
-rw-r--r--Makefile5
-rw-r--r--config/gdbinit32
-rw-r--r--config/kernel.ld15
-rw-r--r--config/user.ld65
-rw-r--r--kernel/drivers/pit.c12
-rw-r--r--kernel/fs/fs.c53
-rw-r--r--kernel/include/comus/drivers/pit.h2
-rw-r--r--kernel/include/comus/fs.h4
-rw-r--r--kernel/include/comus/memory.h35
-rw-r--r--kernel/include/comus/procs.h159
-rw-r--r--kernel/include/comus/syscalls.h37
-rw-r--r--kernel/main.c4
-rw-r--r--kernel/mboot/module.c2
-rw-r--r--kernel/memory/memory.c65
-rw-r--r--kernel/memory/memory.h7
-rw-r--r--kernel/memory/paging.c147
-rw-r--r--kernel/memory/paging.h3
-rw-r--r--kernel/memory/physalloc.c3
-rw-r--r--kernel/memory/virtalloc.c12
-rw-r--r--kernel/memory/virtalloc.h5
-rw-r--r--kernel/procs.c527
-rw-r--r--kernel/user.c11
-rw-r--r--user/Makefile68
-rw-r--r--user/README25
-rw-r--r--user/compile_flags.txt8
-rw-r--r--user/hello.c7
-rw-r--r--user/idle.c52
l---------[-rw-r--r--]user/include/error.h31
-rw-r--r--user/include/stdio.h45
l---------user/include/syscalls.h1
-rw-r--r--user/include/unistd.h23
-rw-r--r--user/init.c185
-rw-r--r--user/lib/alloc.c15
-rw-r--r--user/lib/entry.S26
-rw-r--r--user/lib/fread.c65
-rw-r--r--user/lib/fwrite.c57
-rw-r--r--user/lib/printf.c40
-rw-r--r--user/lib/spawn.c32
-rw-r--r--user/lib/syscall.S111
-rw-r--r--user/lib/timetostr.c143
-rw-r--r--user/progABC.c71
-rw-r--r--user/progDE.c59
-rw-r--r--user/progFG.c59
-rw-r--r--user/progH.c68
-rw-r--r--user/progI.c107
-rw-r--r--user/progJ.c55
-rw-r--r--user/progKL.c63
-rw-r--r--user/progMN.c76
-rw-r--r--user/progP.c55
-rw-r--r--user/progQ.c45
-rw-r--r--user/progR.c102
-rw-r--r--user/progS.c53
-rw-r--r--user/progTUV.c167
-rw-r--r--user/progW.c61
-rw-r--r--user/progX.c51
-rw-r--r--user/progY.c52
-rw-r--r--user/progZ.c60
-rw-r--r--user/shell.c346
58 files changed, 774 insertions, 2915 deletions
diff --git a/Makefile b/Makefile
index b87410f..bb015aa 100644
--- a/Makefile
+++ b/Makefile
@@ -78,13 +78,13 @@ clean:
build: $(BIN)/$(ISO)
-$(A_OBJ): $(BIN)/%.S.o : %.S
+$(A_OBJ): $(BIN)/%.S.o : %.S $(H_SRC)
mkdir -p $(@D)
printf "\033[33m AS \033[0m%s\n" $<
$(CPP) $(CPPFLAGS) -o $@.cpp $<
$(AS) -o $@ $@.cpp
-$(C_OBJ): $(BIN)/%.o : %.c
+$(C_OBJ): $(BIN)/%.o : %.c $(H_SRC)
mkdir -p $(@D)
printf "\033[34m CC \033[0m%s\n" $<
$(CC) -c $(CFLAGS) -o $@ $<
@@ -101,6 +101,7 @@ $(BIN)/$(ISO): $(BIN)/$(KERNEL)
$(GRUB) -o $(BIN)/$(ISO) bin/iso 2>/dev/null
$(BIN)/$(IMAGE):
+ make -s -C user
qemu-img create $(BIN)/$(IMAGE) $(IMAGE_SIZE)
fmt:
diff --git a/config/gdbinit b/config/gdbinit
index ca2c885..a5266e5 100644
--- a/config/gdbinit
+++ b/config/gdbinit
@@ -1,31 +1,3 @@
-# adapted from the xv6 .gdbinit.tmpl file
-set $lastcs = -1
-
-define hook-stop
- # There doesn't seem to be a good way to detect if we're in 16- or
- # 32-bit mode, but we always run with CS == 8 in 32-bit mode.
- if $cs == 8 || $cs == 27
- if $lastcs != 8 && $lastcs != 27
- set architecture i386
- end
- x/i $pc
- else
- if $lastcs == -1 || $lastcs == 8 || $lastcs == 27
- set architecture i8086
- end
- # Translate the segment:offset into a physical address
- printf "[%4x:%4x] ", $cs, $eip
- x/i $cs*16+$eip
- end
- set $lastcs = $cs
-end
-
-echo + target remote localhost:1337\n
+set architecture i386:x86-64
target remote localhost:1337
-
-# If this fails, it's probably because your GDB doesn't support ELF.
-# Look at the tools page at
-# http://pdos.csail.mit.edu/6.828/2009/tools.html
-# for instructions on building GDB with ELF support.
-echo + symbol-file build/kernel/kernel\n
-symbol-file build/kernel/kernel
+symbol-file bin/kernel.bin
diff --git a/config/kernel.ld b/config/kernel.ld
index bcacf61..8ac23e8 100644
--- a/config/kernel.ld
+++ b/config/kernel.ld
@@ -1,5 +1,12 @@
ENTRY(_start)
+PHDRS
+{
+ text PT_LOAD;
+ rodata PT_LOAD;
+ data PT_LOAD;
+}
+
SECTIONS
{
. = 1M;
@@ -14,7 +21,7 @@ SECTIONS
*(.text)
*(.text.*)
text_end = .;
- }
+ } : text
. = ALIGN(0x1000);
@@ -23,7 +30,7 @@ SECTIONS
*(.rodata)
*(.rodata.*)
rodata_end = .;
- }
+ } : rodata
. = ALIGN(0x1000);
@@ -32,7 +39,7 @@ SECTIONS
*(.data)
*(.data.*)
data_end = .;
- }
+ } : data
. = ALIGN(0x1000);
@@ -42,7 +49,7 @@ SECTIONS
*(.bss)
*(.bss.*)
bss_end = .;
- }
+ } : data
kernel_end = .;
diff --git a/config/user.ld b/config/user.ld
index 9e31dff..da1f8b5 100644
--- a/config/user.ld
+++ b/config/user.ld
@@ -1,51 +1,58 @@
-/*
-** Simple linker script for user-level programs.
-*/
-
-OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
-OUTPUT_ARCH(i386)
ENTRY(_start)
+PHDRS
+{
+ text PT_LOAD;
+ rodata PT_LOAD;
+ data PT_LOAD;
+}
+
SECTIONS
{
- /* user text begins at the second page of the address space */
- . = 0x1000;
+ . = 1024M;
+
+ user_start = .;
.text : {
- KEEP(*(.text .stub .text.* .gnu.linkonce.t.*))
- }
+ text_start = .;
+ *(.text)
+ *(.text.*)
+ *(.gnu.linkonce.t.*)
+ text_end = .;
+ } : text
- /* define some standard symbols */
- PROVIDE(etext = .);
- PROVIDE(_etext = .);
+ . = ALIGN(0x1000);
- /* read-only data will go at the end of the text section */
.rodata : {
- KEEP(*(.rodata .rodata.* .gnu.linkonce.r.*))
- }
+ rodata_start = .;
+ *(.rodata)
+ *(.rodata.*)
+ *(.gnu.linkonce.r.*)
+ rodata_end = .;
+ } : rodata
- /* Align the data segment at the next page boundary */
. = ALIGN(0x1000);
.data : {
- KEEP(*(.data))
- }
+ data_start = .;
+ *(.data)
+ *(.data.*)
+ data_end = .;
+ } : data
- PROVIDE(edata = .);
- PROVIDE(_edata = .);
-
- /* Page-align the BSS segment */
. = ALIGN(0x1000);
- PROVIDE(__bss_start = .);
-
.bss : {
- KEEP(*(.bss))
- }
+ bss_start = .;
+ *(COMMON)
+ *(.bss)
+ *(.bss.*)
+ bss_end = .;
+ } : data
- PROVIDE(_end = .);
+ user_end = .;
/DISCARD/ : {
- *(.stab .stab_info .stabstr .eh_frame .note.GNU-stack .note.gnu.property .comment)
+ *(.eh_frame .note.GNU-stack .note.gnu.property .comment)
}
}
diff --git a/kernel/drivers/pit.c b/kernel/drivers/pit.c
index a8fe179..d77df08 100644
--- a/kernel/drivers/pit.c
+++ b/kernel/drivers/pit.c
@@ -6,7 +6,7 @@
#define CHAN_2 0x42
#define CMD 0x43
-uint64_t ticks = 0;
+volatile uint64_t ticks = 0;
uint16_t pit_read_divider(void)
{
@@ -21,8 +21,10 @@ uint16_t pit_read_divider(void)
void pit_set_divider(uint16_t count)
{
- cli();
- outb(CHAN_0, count & 0xFF); // low byte
- outb(CHAN_0, (count & 0xFF00) >> 8); // high byte
- sti();
+ (void)count;
+ // FIXME: broken on -O0
+ // cli();
+ // outb(CHAN_0, count & 0xFF); // low byte
+ // outb(CHAN_0, (count & 0xFF00) >> 8); // high byte
+ // sti();
}
diff --git a/kernel/fs/fs.c b/kernel/fs/fs.c
index 4b6bc5d..c8399a3 100644
--- a/kernel/fs/fs.c
+++ b/kernel/fs/fs.c
@@ -18,7 +18,7 @@ void fs_init(void)
void *rd = mboot_get_initrd(&rd_len);
if (rd != NULL) {
assert(idx < N_DISKS, "Too many disks, limit is: %d\n", N_DISKS);
- fs_disks[idx] = (struct disk) {
+ fs_disks[idx] = (struct disk){
.present = 1,
.id = idx,
.type = DISK_TYPE_RAMDISK,
@@ -32,7 +32,7 @@ void fs_init(void)
struct ide_devicelist ide_list = ide_devices_enumerate();
for (size_t i = 0; i < ide_list.num_devices; i++) {
assert(idx < N_DISKS, "Too many disks, limit is: %d\n", N_DISKS);
- fs_disks[idx] = (struct disk) {
+ fs_disks[idx] = (struct disk){
.present = 1,
.id = idx,
.type = DISK_TYPE_ATA,
@@ -92,7 +92,8 @@ int fs_find_file_rel(struct file *rel, char *rel_path, struct file *res)
panic("fs_find_file_rel NOT YET IMPLEMENTED");
}
-static int disk_read_rd(struct disk *disk, size_t offset, size_t len, uint8_t *buffer)
+static int disk_read_rd(struct disk *disk, size_t offset, size_t len,
+ uint8_t *buffer)
{
if (offset + len >= disk->rd.len) {
WARN("attempted to read past length of ramdisk");
@@ -103,7 +104,8 @@ static int disk_read_rd(struct disk *disk, size_t offset, size_t len, uint8_t *b
return 0;
}
-static int disk_read_ata(struct disk *disk, size_t offset, size_t len, uint8_t *buffer)
+static int disk_read_ata(struct disk *disk, size_t offset, size_t len,
+ uint8_t *buffer)
{
static size_t atabuf_len = 0;
static uint16_t *atabuf = NULL;
@@ -112,28 +114,28 @@ static int disk_read_ata(struct disk *disk, size_t offset, size_t len, uint8_t *
uint32_t err = offset % ATA_SECT_SIZE;
int ret = 0;
- if (atabuf == NULL || atabuf_len < numsects*ATA_SECT_SIZE) {
- if ((atabuf = krealloc(atabuf, numsects*ATA_SECT_SIZE)) == NULL)
+ if (atabuf == NULL || atabuf_len < numsects * ATA_SECT_SIZE) {
+ if ((atabuf = krealloc(atabuf, numsects * ATA_SECT_SIZE)) == NULL)
return 1;
- atabuf_len = numsects*ATA_SECT_SIZE;
+ atabuf_len = numsects * ATA_SECT_SIZE;
}
// read sectors
- if ((ret = ide_device_read_sectors(disk->ide, numsects, offset / ATA_SECT_SIZE, atabuf)))
+ if ((ret = ide_device_read_sectors(disk->ide, numsects,
+ offset / ATA_SECT_SIZE, atabuf)))
return 1;
// copy over to buffer
- memcpy(buffer, atabuf + err, len);
+ memcpy(buffer, (char *)atabuf + err, len);
return ret;
}
-
-int disk_read(struct disk *disk, size_t offset, size_t len, uint8_t *buffer)
+int disk_read(struct disk *disk, size_t offset, size_t len, void *buffer)
{
int ret = 0;
- switch(disk->type) {
+ switch (disk->type) {
case DISK_TYPE_RAMDISK:
ret = disk_read_rd(disk, offset, len, buffer);
break;
@@ -141,14 +143,16 @@ int disk_read(struct disk *disk, size_t offset, size_t len, uint8_t *buffer)
ret = disk_read_ata(disk, offset, len, buffer);
break;
default:
- ERROR("attempted to read from disk with invalid type: %d\n", disk->type);
+ ERROR("attempted to read from disk with invalid type: %d\n",
+ disk->type);
ret = 1;
}
return ret;
}
-static int disk_write_rd(struct disk *disk, size_t offset, size_t len, uint8_t *buffer)
+static int disk_write_rd(struct disk *disk, size_t offset, size_t len,
+ uint8_t *buffer)
{
if (offset + len >= disk->rd.len) {
WARN("attempted to write past length of ramdisk");
@@ -159,7 +163,8 @@ static int disk_write_rd(struct disk *disk, size_t offset, size_t len, uint8_t *
return 0;
}
-static int disk_write_ata(struct disk *disk, size_t offset, size_t len, uint8_t *buffer)
+static int disk_write_ata(struct disk *disk, size_t offset, size_t len,
+ uint8_t *buffer)
{
static size_t atabuf_len = 0;
static uint16_t *atabuf = NULL;
@@ -168,31 +173,33 @@ static int disk_write_ata(struct disk *disk, size_t offset, size_t len, uint8_t
uint32_t err = offset % ATA_SECT_SIZE;
int ret = 0;
- if (atabuf == NULL || atabuf_len < numsects*ATA_SECT_SIZE) {
- if ((atabuf = krealloc(atabuf, numsects*ATA_SECT_SIZE)) == NULL)
+ if (atabuf == NULL || atabuf_len < numsects * ATA_SECT_SIZE) {
+ if ((atabuf = krealloc(atabuf, numsects * ATA_SECT_SIZE)) == NULL)
return 1;
- atabuf_len = numsects*ATA_SECT_SIZE;
+ atabuf_len = numsects * ATA_SECT_SIZE;
}
// read sectors what will be overwritten
- if ((ret = ide_device_read_sectors(disk->ide, numsects, offset / ATA_SECT_SIZE, atabuf)))
+ if ((ret = ide_device_read_sectors(disk->ide, numsects,
+ offset / ATA_SECT_SIZE, atabuf)))
return 1;
// copy custom data over
- memcpy(atabuf + err, buffer, len);
+ memcpy((char *)atabuf + err, buffer, len);
// write back sectors
- if ((ret = ide_device_write_sectors(disk->ide, numsects, offset / ATA_SECT_SIZE, atabuf)))
+ if ((ret = ide_device_write_sectors(disk->ide, numsects,
+ offset / ATA_SECT_SIZE, atabuf)))
return 1;
return ret;
}
-int disk_write(struct disk *disk, size_t offset, size_t len, uint8_t *buffer)
+int disk_write(struct disk *disk, size_t offset, size_t len, void *buffer)
{
int ret = 0;
- switch(disk->type) {
+ switch (disk->type) {
case DISK_TYPE_RAMDISK:
ret = disk_write_rd(disk, offset, len, buffer);
break;
diff --git a/kernel/include/comus/drivers/pit.h b/kernel/include/comus/drivers/pit.h
index a7a111d..77f0a14 100644
--- a/kernel/include/comus/drivers/pit.h
+++ b/kernel/include/comus/drivers/pit.h
@@ -13,7 +13,7 @@
// how many time the pit has ticked
// not accurate time, good for spinning though
-extern uint64_t ticks;
+extern volatile uint64_t ticks;
uint16_t pit_read_divider(void);
void pit_set_divider(uint16_t count);
diff --git a/kernel/include/comus/fs.h b/kernel/include/comus/fs.h
index 048c7c5..e67b6fe 100644
--- a/kernel/include/comus/fs.h
+++ b/kernel/include/comus/fs.h
@@ -47,7 +47,7 @@ struct disk {
* @param buffer - the buffer to save data into
* @returns bytes read on success, negative fs error code in failure
*/
-int disk_read(struct disk *disk, size_t offset, size_t len, uint8_t *buffer);
+int disk_read(struct disk *disk, size_t offset, size_t len, void *buffer);
/**
* write data from a disk into a buffer
@@ -58,7 +58,7 @@ int disk_read(struct disk *disk, size_t offset, size_t len, uint8_t *buffer);
* @param buffer - the buffer to read from
* @returns bytes written on success, negative fs error code in failure
*/
-int disk_write(struct disk *disk, size_t offset, size_t len, uint8_t *buffer);
+int disk_write(struct disk *disk, size_t offset, size_t len, void *buffer);
enum file_type {
// regular file
diff --git a/kernel/include/comus/memory.h b/kernel/include/comus/memory.h
index 3b57324..588219e 100644
--- a/kernel/include/comus/memory.h
+++ b/kernel/include/comus/memory.h
@@ -97,11 +97,11 @@ mem_ctx_t mem_ctx_clone(mem_ctx_t ctx, bool cow);
void mem_ctx_free(mem_ctx_t ctx);
/**
- * Free a memory context
+ * Switch into a different memory context
*
- * @param ctx - pointer to the memory context
+ * @param ctx - the memory context
*/
-void free_mem_ctx(mem_ctx_t ctx);
+void mem_ctx_switch(mem_ctx_t ctx);
/**
* Allocates at least len bytes of memory starting at
@@ -126,17 +126,42 @@ void mem_unmapaddr(mem_ctx_t ctx, void *virt);
/**
* Allocate a single page of memory with the given paging structure
*
+ * @param ctx - the memory context
+ * @param lazy - if to lazy allocate pages (alloc on fault)
+ * @returns the vitural address aloocated or NULL on failure
+ */
+void *mem_alloc_page(mem_ctx_t ctx, bool lazy);
+
+/**
+ * Allocate a single page of memory at the given vitural address with the given paging structure
+ *
+ * @param ctx - the memory context
+ * @param virt - the vitural address to allocate at
+ * @param lazy - if to lazy allocate pages (alloc on fault)
* @returns the vitural address aloocated or NULL on failure
*/
-void *mem_alloc_page(mem_ctx_t ctx);
+void *mem_alloc_page_at(mem_ctx_t ctx, void *virt, bool lazy);
/**
* Allocate size_t amount of contiguous virtual pages with the given paging structure
*
+ * @param ctx - the memory context
+ * @param count - the number of pages to allocate
+ * @param lazy - if to lazy allocate pages (alloc on fault)
+ * @returns the address allocated or NULL on failure
+ */
+void *mem_alloc_pages(mem_ctx_t ctx, size_t count, bool lazy);
+
+/**
+ * Allocate size_t amount of contiguous virtual pages at a given virtural address with the given paging structure
+ *
+ * @param ctx - the memory context
* @param count - the number of pages to allocate
+ * @param virt - the vitural address to allocate at
+ * @param lazy - if to lazy allocate pages (alloc on fault)
* @returns the address allocated or NULL on failure
*/
-void *mem_alloc_pages(mem_ctx_t ctx, size_t count);
+void *mem_alloc_pages_at(mem_ctx_t ctx, size_t count, void *virt, bool lazy);
/**
* Free allocated pages with the given paging structure.
diff --git a/kernel/include/comus/procs.h b/kernel/include/comus/procs.h
index fe8cbee..80c4fe4 100644
--- a/kernel/include/comus/procs.h
+++ b/kernel/include/comus/procs.h
@@ -14,6 +14,16 @@
#include <comus/memory.h>
#include <lib.h>
+#define PCB_REG(pcb, x) ((pcb)->regs->x)
+#define PCB_RET(pcb) ((pcb)->regs->rax)
+#define PCB_ARG1(pcb) PCB_REG((pcb), rdi)
+#define PCB_ARG2(pcb) PCB_REG((pcb), rsi)
+#define PCB_ARG3(pcb) PCB_REG((pcb), rdx)
+#define PCB_ARG4(pcb) PCB_REG((pcb), rcx)
+
+/// process id
+typedef unsigned short pid_t;
+
/// process states
enum proc_state {
// pre-viable
@@ -33,71 +43,27 @@ enum proc_state {
N_PROC_STATES,
};
-/// process priority
-enum proc_priority {
- PROC_PRIO_HIGH,
- PROC_PRIO_STD,
- PROC_PRIO_LOW,
- PROC_PRIO_DEFERRED,
- // sentinel
- N_PROC_PRIOS,
-};
-
-/// process quantum length
-/// values are number of clock ticks
-enum proc_quantum {
- PROC_QUANTUM_SHORT = 1,
- PROC_QUANTUM_STANDARD = 3,
- PROC_QUANTUM_LONG = 5,
-};
-
-/// program section information
-struct proc_section {
- uint64_t length;
- uint64_t addr;
-};
-
-#define SECT_L1 0
-#define SECT_L2 1
-#define SECT_L3 2
-#define SECT_STACK 3
-#define N_SECTS 4
-#define N_LOADABLE 3
-
-/// pid type
-typedef unsigned short pid_t;
-
/// process control block
struct pcb {
- // process context
+ // metadata
+ pid_t pid;
+ struct pcb *parent;
+ enum proc_state state;
+ size_t priority;
+ size_t ticks;
+
+ // context
mem_ctx_t memctx;
struct cpu_regs *regs;
- // vm information
- struct proc_section sects[N_SECTS];
-
// queue linkage
struct pcb *next; // next PDB in queue
// process state information
- struct pcb *parent; // pointer to PCB of our parent process
- uint64_t wakeup; // wakeup time, for sleeping processes
- uint8_t exit_status; // termination status, for parent's use
-
- // process metadata
- pid_t pid; // pid of this process
- enum proc_state state; // process' current state
- enum proc_priority priority; // process priority level
- size_t ticks; // remaining ticks in this time slice
+ uint64_t wakeup;
+ uint8_t exit_status;
};
-#define PCB_REG(pcb, x) ((pcb)->regs->x)
-#define PCB_RET(pcb) ((pcb)->regs->rax)
-#define PCB_ARG(pcb, n) (((uint64_t *)(((pcb)->regs) + 1))[(n)])
-
-/// pcb queue structure
-typedef struct pcb_queue_s *pcb_queue_t;
-
/// ordering of pcb queues
enum pcb_queue_order {
O_PCB_FIFO,
@@ -108,6 +74,9 @@ enum pcb_queue_order {
N_PCB_ORDERINGS,
};
+/// pcb queue structure
+typedef struct pcb_queue_s *pcb_queue_t;
+
/// public facing pcb queues
extern pcb_queue_t pcb_freelist;
extern pcb_queue_t ready;
@@ -116,51 +85,40 @@ extern pcb_queue_t sleeping;
extern pcb_queue_t zombie;
/// pointer to the currently-running process
-extern struct pcb *current;
-
+extern struct pcb *current_pcb;
+/// pointer to the pcb for the 'init' process
+extern struct pcb *init_pcb;
/// the process table
extern struct pcb ptable[N_PROCS];
/// next avaliable pid
extern pid_t next_pid;
-/// pointer to the pcb for the 'init' process
-extern struct pcb *init_pcb;
-
-/// table of state name strings
-extern const char *proc_state_str[N_PROC_STATES];
-
-/// table of priority name strings
-extern const char *proc_prio_str[N_PROC_PRIOS];
-
-/// table of queue ordering name strings
-extern const char *pcb_ord_str[N_PCB_ORDERINGS];
-
/**
* Initialization for the process module
*/
void pcb_init(void);
/**
- * Allocate a PCB from the list of free PCBs
+ * allocate a PCB from the free list
+ *
+ * @returns 0 on success or non zero error code
*/
int pcb_alloc(struct pcb **pcb);
/**
- * Return a PCB to the list of free PCBs.
+ * free an allocted PCB back to the free list
*
- * @param pcb Pointer to the PCB to be deallocated.
+ * @param pcb - pointer to the PCB to be deallocated
*/
void pcb_free(struct pcb *pcb);
/**
- * 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.
+ * turn the indicated process into a zombie
*
* @param pcb - pointer to the newly-undead PCB
*/
-void pcb_zombify(register struct pcb *victim);
+void pcb_zombify(struct pcb *victim);
/**
* Reclaim a process' data structures
@@ -237,7 +195,7 @@ struct pcb *pcb_queue_peek(const pcb_queue_t queue);
* @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, struct pcb **pcb);
+int pcb_queue_pop(pcb_queue_t queue, struct pcb **pcb);
/**
* Remove the specified PCB from the indicated queue.
@@ -246,7 +204,7 @@ int pcb_queue_remove(pcb_queue_t queue, struct pcb **pcb);
* @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, struct pcb *pcb);
+int pcb_queue_remove(pcb_queue_t queue, struct pcb *pcb);
/**
* Schedule the supplied process
@@ -260,51 +218,4 @@ void schedule(struct pcb *pcb);
*/
void dispatch(void);
-/**
- * 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 struct cpu_regs *c);
-
-/**
- * dump the process context for all active processes
- *
- * @param msg[in] Optional message to print
- */
-void ctx_dump_all(const char *msg);
-
-/**
- * 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 struct pcb *p, bool all);
-
-/**
- * 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 contents);
-
-/**
- * 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 all);
-
-/**
- * Prints basic information about the process table (number of
- * entries, number with each process state, etc.).
- */
-void ptable_dump_counts(void);
-
#endif /* procs.h */
diff --git a/kernel/include/comus/syscalls.h b/kernel/include/comus/syscalls.h
new file mode 100644
index 0000000..3dc128d
--- /dev/null
+++ b/kernel/include/comus/syscalls.h
@@ -0,0 +1,37 @@
+/**
+ * @file syscalls.h
+ *
+ * @author Freya Murphy <freya@freyacat.org>
+ * @author cisi 452
+ *
+ * System call declarations
+ */
+
+#ifndef SYSCALLS_H_
+#define SYSCALLS_H_
+
+#define SYS_exit 0
+#define SYS_waitpid 1
+#define SYS_fork 2
+#define SYS_exec 3
+#define SYS_open 4
+#define SYS_close 5
+#define SYS_read 6
+#define SYS_write 7
+#define SYS_getpid 8
+#define SYS_getppid 9
+#define SYS_gettime 10
+#define SYS_getprio 11
+#define SYS_setprio 12
+#define SYS_kill 13
+#define SYS_sleep 14
+#define SYS_brk 15
+#define SYS_sbrk 16
+
+// UPDATE THIS DEFINITION IF MORE SYSCALLS ARE ADDED!
+#define N_SYSCALLS 13
+
+// interrupt vector entry for system calls
+#define VEC_SYSCALL 0x80
+
+#endif /* syscalls.h */
diff --git a/kernel/main.c b/kernel/main.c
index 4953940..4047a64 100644
--- a/kernel/main.c
+++ b/kernel/main.c
@@ -8,6 +8,7 @@
#include <comus/drivers/gpu.h>
#include <comus/drivers/ata.h>
#include <comus/fs.h>
+#include <comus/procs.h>
#include <lib.h>
void kreport(void)
@@ -40,6 +41,9 @@ void main(long magic, volatile void *mboot)
// load file systems
fs_init();
+ // initalize processes
+ pcb_init();
+
// report system state
kreport();
diff --git a/kernel/mboot/module.c b/kernel/mboot/module.c
index 7a64f2e..79d092e 100644
--- a/kernel/mboot/module.c
+++ b/kernel/mboot/module.c
@@ -20,5 +20,5 @@ void *mboot_get_initrd(size_t *len)
struct multiboot_tag_module *mod = (struct multiboot_tag_module *)tag;
*len = mod->mod_end - mod->mod_start;
- return (void*) (uintptr_t) mod->mod_start;
+ return (void *)(uintptr_t)mod->mod_start;
}
diff --git a/kernel/memory/memory.c b/kernel/memory/memory.c
index de94fe3..57aa08c 100644
--- a/kernel/memory/memory.c
+++ b/kernel/memory/memory.c
@@ -2,6 +2,7 @@
#include <comus/asm.h>
#include <comus/mboot.h>
#include <comus/efi.h>
+#include <comus/limits.h>
#include <lib.h>
#include "memory.h"
@@ -9,10 +10,16 @@
#include "virtalloc.h"
#include "physalloc.h"
+// kernel memory context
mem_ctx_t kernel_mem_ctx;
-struct mem_ctx_s _kernel_mem_ctx;
+static struct mem_ctx_s _kernel_mem_ctx;
+
+// kernel page tables
extern volatile char kernel_pml4[];
-extern struct virt_ctx kernel_virt_ctx;
+
+// user space memory contexts
+static struct mem_ctx_s user_mem_ctx[N_PROCS];
+static struct mem_ctx_s *user_mem_ctx_next = NULL;
void *kmapaddr(void *phys, void *virt, size_t len, unsigned int flags)
{
@@ -26,12 +33,12 @@ void kunmapaddr(void *virt)
void *kalloc_page(void)
{
- return mem_alloc_page(kernel_mem_ctx);
+ return mem_alloc_page(kernel_mem_ctx, false);
}
void *kalloc_pages(size_t count)
{
- return mem_alloc_pages(kernel_mem_ctx, count);
+ return mem_alloc_pages(kernel_mem_ctx, count, false);
}
void kfree_pages(void *ptr)
@@ -46,7 +53,20 @@ int kload_page(void *virt)
mem_ctx_t mem_ctx_alloc(void)
{
- panic("not yet implemented");
+ mem_ctx_t ctx = user_mem_ctx_next;
+ if (ctx == NULL)
+ return NULL;
+
+ if ((ctx->pml4 = pml4_alloc()) == NULL)
+ return NULL;
+ virtaddr_init(&ctx->virtctx);
+
+ user_mem_ctx_next = ctx->prev;
+ if (ctx->prev)
+ ctx->prev->next = NULL;
+ ctx->prev = NULL;
+
+ return ctx;
}
mem_ctx_t mem_ctx_clone(mem_ctx_t ctx, bool cow)
@@ -59,9 +79,21 @@ mem_ctx_t mem_ctx_clone(mem_ctx_t ctx, bool cow)
void mem_ctx_free(mem_ctx_t ctx)
{
- (void)ctx;
+ pml4_free(ctx->pml4);
+ virtaddr_cleanup(&ctx->virtctx);
- panic("not yet implemented");
+ if (user_mem_ctx_next == NULL) {
+ user_mem_ctx_next = ctx;
+ } else {
+ user_mem_ctx_next->next = ctx;
+ ctx->prev = user_mem_ctx_next;
+ user_mem_ctx_next = ctx;
+ }
+}
+
+void mem_ctx_switch(mem_ctx_t ctx)
+{
+ __asm__ volatile("mov %0, %%cr3" ::"r"(ctx->pml4) : "memory");
}
void memory_init(void)
@@ -73,11 +105,10 @@ void memory_init(void)
kernel_mem_ctx = &_kernel_mem_ctx;
kernel_mem_ctx->pml4 = kernel_pml4;
- kernel_mem_ctx->virtctx = &kernel_virt_ctx;
cli();
paging_init();
- virtaddr_init(kernel_mem_ctx->virtctx);
+ virtaddr_init(&kernel_mem_ctx->virtctx);
physalloc_init(&mmap);
sti();
@@ -88,4 +119,20 @@ void memory_init(void)
continue;
kmapaddr((void *)seg->addr, (void *)seg->addr, seg->len, F_WRITEABLE);
}
+
+ // setup user mem ctx linked list
+ for (size_t i = 0; i < N_PROCS; i++) {
+ struct mem_ctx_s *ctx = &user_mem_ctx[i];
+ ctx->next = NULL;
+ ctx->prev = NULL;
+
+ if (user_mem_ctx_next == NULL) {
+ user_mem_ctx_next = ctx;
+ continue;
+ }
+
+ user_mem_ctx_next->next = ctx;
+ ctx->prev = user_mem_ctx_next;
+ user_mem_ctx_next = ctx;
+ }
}
diff --git a/kernel/memory/memory.h b/kernel/memory/memory.h
index c39656d..2657fc7 100644
--- a/kernel/memory/memory.h
+++ b/kernel/memory/memory.h
@@ -3,6 +3,11 @@
#include "virtalloc.h"
struct mem_ctx_s {
- struct virt_ctx *virtctx;
+ // page tables
volatile char *pml4;
+ // virt addr allocator
+ struct virt_ctx virtctx;
+ // linked list
+ struct mem_ctx_s *next;
+ struct mem_ctx_s *prev;
};
diff --git a/kernel/memory/paging.c b/kernel/memory/paging.c
index 0c74145..ff73444 100644
--- a/kernel/memory/paging.c
+++ b/kernel/memory/paging.c
@@ -63,63 +63,72 @@ extern volatile struct pte
// paged address to read page tables
// the structures are not gurenteed to be ident mapped
// map them here with map_<type>(phys_addr) before useing structures
-void volatile *addr_mapped = (void *)(uintptr_t)0x40004000;
static volatile struct pml4e *pml4_mapped = (void *)(uintptr_t)0x40000000;
static volatile struct pdpte *pdpt_mapped = (void *)(uintptr_t)0x40001000;
static volatile struct pde *pd_mapped = (void *)(uintptr_t)0x40002000;
static volatile struct pte *pt_mapped = (void *)(uintptr_t)0x40003000;
+static volatile void *addr_mapped = (void *)(uintptr_t)0x40004000;
+
+// kernel start/end
+extern char kernel_start[];
+extern char kernel_end[];
static inline void invlpg(volatile void *addr)
{
__asm__ volatile("invlpg (%0)" ::"r"(addr) : "memory");
}
-static void load_addr(volatile void *phys_addr)
-{
- static volatile struct pte *pt = &paging_pt[4];
- pt->address = (uint64_t)phys_addr >> 12;
- pt->flags = F_PRESENT | F_WRITEABLE;
- invlpg(addr_mapped);
-}
-
-static void load_pml4(volatile void *phys)
+static volatile struct pml4e *load_pml4(volatile void *phys)
{
static volatile struct pte *pt = &paging_pt[0];
if ((uint64_t)phys >> 12 == pt->address)
- return;
+ return pml4_mapped;
pt->address = (uint64_t)phys >> 12;
pt->flags = F_PRESENT | F_WRITEABLE;
invlpg(pml4_mapped);
+ return pml4_mapped;
}
-static void load_pdpt(volatile void *phys)
+static volatile struct pdpte *load_pdpt(volatile void *phys)
{
static volatile struct pte *pt = &paging_pt[1];
if ((uint64_t)phys >> 12 == pt->address)
- return;
+ return pdpt_mapped;
pt->address = (uint64_t)phys >> 12;
pt->flags = F_PRESENT | F_WRITEABLE;
invlpg(pdpt_mapped);
+ return pdpt_mapped;
}
-static void load_pd(volatile void *phys)
+static volatile struct pde *load_pd(volatile void *phys)
{
static volatile struct pte *pt = &paging_pt[2];
if ((uint64_t)phys >> 12 == pt->address)
- return;
+ return pd_mapped;
pt->address = (uint64_t)phys >> 12;
pt->flags = F_PRESENT | F_WRITEABLE;
invlpg(pd_mapped);
+ return pd_mapped;
}
-static void load_pt(volatile void *phys)
+static volatile struct pte *load_pt(volatile void *phys)
{
static volatile struct pte *pt = &paging_pt[3];
if ((uint64_t)phys >> 12 == pt->address)
- return;
+ return pt_mapped;
pt->address = (uint64_t)phys >> 12;
pt->flags = F_PRESENT | F_WRITEABLE;
invlpg(pt_mapped);
+ return pt_mapped;
+}
+
+static volatile void *load_addr(volatile void *phys_addr)
+{
+ static volatile struct pte *pt = &paging_pt[4];
+ pt->address = (uint64_t)phys_addr >> 12;
+ pt->flags = F_PRESENT | F_WRITEABLE;
+ invlpg(addr_mapped);
+ return addr_mapped;
}
#define PAG_SUCCESS 0
@@ -488,7 +497,6 @@ static int map_pages(volatile struct pml4e *root, void *virt_start,
return 0;
failed:
-
unmap_pages(root, virt, i);
return 1;
@@ -498,18 +506,18 @@ void paging_init(void)
{
// map pdpt
kernel_pml4[0].flags = F_PRESENT | F_WRITEABLE;
- kernel_pml4[0].address = (uint64_t)(&kernel_pdpt_0) >> 12;
+ kernel_pml4[0].address = (uint64_t)(kernel_pdpt_0) >> 12;
// map pd0 & pd1
kernel_pdpt_0[0].flags = F_PRESENT | F_WRITEABLE;
- kernel_pdpt_0[0].address = (uint64_t)(&kernel_pd_0) >> 12;
+ kernel_pdpt_0[0].address = (uint64_t)(kernel_pd_0) >> 12;
kernel_pdpt_0[1].flags = F_PRESENT | F_WRITEABLE;
- kernel_pdpt_0[1].address = (uint64_t)(&kernel_pd_1) >> 12;
+ kernel_pdpt_0[1].address = (uint64_t)(kernel_pd_1) >> 12;
// map pd0 entires (length N_IDENT_PTS)
for (int i = 0; i < N_IDENT_PTS; i++) {
kernel_pd_0[i].flags = F_PRESENT | F_WRITEABLE;
- kernel_pd_0[i].address = (uint64_t)(&kernel_pd_0_ents[512 * i]) >> 12;
+ kernel_pd_0[i].address = (uint64_t)(kernel_pd_0_ents + 512 * i) >> 12;
}
// identity map kernel
@@ -520,15 +528,39 @@ void paging_init(void)
// map paging_pt
kernel_pd_1[0].flags = F_PRESENT | F_WRITEABLE;
- kernel_pd_1[0].address = (uint64_t)(&paging_pt) >> 12;
+ kernel_pd_1[0].address = (uint64_t)(paging_pt) >> 12;
- memsetv(&paging_pt, 0, 4096);
+ memsetv(paging_pt, 0, 4096);
// make sure we are using THESE pagetables
// EFI doesnt on boot
__asm__ volatile("mov %0, %%cr3" ::"r"(kernel_pml4) : "memory");
}
+volatile void *pml4_alloc(void)
+{
+ struct pml4e *pml4_phys = alloc_phys_page();
+ struct pml4e *pml4 = kmapaddr(pml4_phys, NULL, PAGE_SIZE, F_WRITEABLE);
+ memset(pml4, 0, PAGE_SIZE);
+
+ if (map_pages(pml4_phys, kernel_start, kernel_start,
+ F_PRESENT | F_WRITEABLE,
+ (kernel_end - kernel_start) / PAGE_SIZE)) {
+ free_phys_page(pml4_phys);
+ kunmapaddr(pml4);
+ return NULL;
+ }
+
+ kunmapaddr(pml4);
+ return pml4_phys;
+}
+
+void pml4_free(volatile void *pml4)
+{
+ (void)pml4;
+ // TODD: free structures
+}
+
static inline void *page_align(void *addr)
{
uintptr_t a = (uintptr_t)addr;
@@ -552,13 +584,13 @@ void *mem_mapaddr(mem_ctx_t ctx, void *phys, void *virt, size_t len,
// get page aligned (or allocate) vitural address
if (virt == NULL)
- virt = virtaddr_alloc(ctx->virtctx, pages);
+ virt = virtaddr_alloc(&ctx->virtctx, pages);
if (virt == NULL)
return NULL;
if (map_pages((volatile struct pml4e *)ctx->pml4, virt, aligned_phys,
F_PRESENT | flags, pages)) {
- virtaddr_free(ctx->virtctx, virt);
+ virtaddr_free(&ctx->virtctx, virt);
return NULL;
}
@@ -567,32 +599,72 @@ void *mem_mapaddr(mem_ctx_t ctx, void *phys, void *virt, size_t len,
void mem_unmapaddr(mem_ctx_t ctx, void *virt)
{
- long pages = virtaddr_free(ctx->virtctx, virt);
+ if (virt == NULL)
+ return;
+
+ long pages = virtaddr_free(&ctx->virtctx, virt);
if (pages < 1)
return;
unmap_pages(kernel_pml4, virt, pages);
}
-void *mem_alloc_page(mem_ctx_t ctx)
+void *mem_alloc_page(mem_ctx_t ctx, bool lazy)
{
- void *virt = virtaddr_alloc(ctx->virtctx, 1);
+ void *virt = virtaddr_alloc(&ctx->virtctx, 1);
if (virt == NULL)
return NULL;
- if (map_page((volatile struct pml4e *)ctx->pml4, virt, NULL, F_WRITEABLE)) {
- virtaddr_free(ctx->virtctx, virt);
+
+ if (mem_alloc_page_at(ctx, virt, lazy) == NULL) {
+ virtaddr_free(&ctx->virtctx, virt);
+ return NULL;
+ }
+
+ return virt;
+}
+
+void *mem_alloc_page_at(mem_ctx_t ctx, void *virt, bool lazy)
+{
+ void *phys = NULL;
+ if (!lazy) {
+ if ((phys = alloc_phys_page()) == NULL)
+ return NULL;
+ }
+
+ if (map_page((volatile struct pml4e *)ctx->pml4, virt, phys, F_WRITEABLE)) {
+ if (phys)
+ free_phys_page(phys);
return NULL;
}
+
return virt;
}
-void *mem_alloc_pages(mem_ctx_t ctx, size_t count)
+void *mem_alloc_pages(mem_ctx_t ctx, size_t count, bool lazy)
{
- void *virt = virtaddr_alloc(ctx->virtctx, count);
+ void *virt = virtaddr_alloc(&ctx->virtctx, count);
if (virt == NULL)
return NULL;
- if (map_pages((volatile struct pml4e *)ctx->pml4, virt, NULL, F_WRITEABLE,
+
+ if (mem_alloc_pages_at(ctx, count, virt, lazy) == NULL) {
+ virtaddr_free(&ctx->virtctx, virt);
+ return NULL;
+ }
+
+ return virt;
+}
+
+void *mem_alloc_pages_at(mem_ctx_t ctx, size_t count, void *virt, bool lazy)
+{
+ void *phys = NULL;
+ if (!lazy) {
+ if ((phys = alloc_phys_pages(count)) == NULL)
+ return NULL;
+ }
+
+ if (map_pages((volatile struct pml4e *)ctx->pml4, virt, phys, F_WRITEABLE,
count)) {
- virtaddr_free(ctx->virtctx, virt);
+ if (phys)
+ free_phys_pages(phys, count);
return NULL;
}
return virt;
@@ -600,7 +672,10 @@ void *mem_alloc_pages(mem_ctx_t ctx, size_t count)
void mem_free_pages(mem_ctx_t ctx, void *virt)
{
- long pages = virtaddr_free(ctx->virtctx, virt);
+ if (virt == NULL)
+ return;
+
+ long pages = virtaddr_free(&ctx->virtctx, virt);
if (pages == 1)
unmap_page((volatile struct pml4e *)ctx->pml4, virt);
else if (pages > 1)
diff --git a/kernel/memory/paging.h b/kernel/memory/paging.h
index b54d422..e2a4464 100644
--- a/kernel/memory/paging.h
+++ b/kernel/memory/paging.h
@@ -11,4 +11,7 @@
void paging_init(void);
+volatile void *pml4_alloc(void);
+void pml4_free(volatile void *pml4);
+
#endif /* paging.h */
diff --git a/kernel/memory/physalloc.c b/kernel/memory/physalloc.c
index dc8faa8..b164358 100644
--- a/kernel/memory/physalloc.c
+++ b/kernel/memory/physalloc.c
@@ -112,6 +112,9 @@ void free_phys_page(void *ptr)
void free_phys_pages(void *ptr, int pages)
{
+ if (ptr == NULL)
+ return;
+
long idx = page_idx(ptr);
if (idx == -1)
return;
diff --git a/kernel/memory/virtalloc.c b/kernel/memory/virtalloc.c
index 8a0d1ed..0f4de93 100644
--- a/kernel/memory/virtalloc.c
+++ b/kernel/memory/virtalloc.c
@@ -3,8 +3,6 @@
#include "virtalloc.h"
-struct virt_ctx kernel_virt_ctx;
-
static struct virt_addr_node *get_node_idx(struct virt_ctx *ctx, int idx)
{
if (idx < BOOTSTRAP_VIRT_ALLOC_NODES) {
@@ -88,7 +86,7 @@ void virtaddr_init(struct virt_ctx *ctx)
.is_alloc = false,
.is_used = true,
};
- memsetv(ctx->bootstrap_nodes, 0, sizeof(ctx->bootstrap_nodes));
+ memset(ctx, 0, sizeof(struct virt_ctx));
ctx->bootstrap_nodes[0] = init;
ctx->alloc_nodes = NULL;
ctx->start_node = &ctx->bootstrap_nodes[0];
@@ -169,6 +167,9 @@ void *virtaddr_alloc(struct virt_ctx *ctx, int n_pages)
long virtaddr_free(struct virt_ctx *ctx, void *virtaddr)
{
+ if (virtaddr == NULL)
+ return -1;
+
uintptr_t virt = (uintptr_t)virtaddr;
if (virt % PAGE_SIZE)
@@ -188,3 +189,8 @@ long virtaddr_free(struct virt_ctx *ctx, void *virtaddr)
return -1;
}
+
+void virtaddr_cleanup(struct virt_ctx *ctx)
+{
+ kfree(ctx->alloc_nodes);
+}
diff --git a/kernel/memory/virtalloc.h b/kernel/memory/virtalloc.h
index 9f974c5..7bf8b91 100644
--- a/kernel/memory/virtalloc.h
+++ b/kernel/memory/virtalloc.h
@@ -66,4 +66,9 @@ void *virtaddr_alloc(struct virt_ctx *ctx, int pages);
*/
long virtaddr_free(struct virt_ctx *ctx, void *virtaddr);
+/**
+ * Cleans up heap allocations and frees the virtalloc context
+ */
+void virtaddr_cleanup(struct virt_ctx *ctx);
+
#endif /* virtalloc.h */
diff --git a/kernel/procs.c b/kernel/procs.c
index a8352b0..9cde22f 100644
--- a/kernel/procs.c
+++ b/kernel/procs.c
@@ -25,74 +25,25 @@ pcb_queue_t waiting;
pcb_queue_t sleeping;
pcb_queue_t zombie;
-// pointer to the currently-running process
-struct pcb *current;
-
/// pointer to the currently-running process
-struct pcb *current;
+struct pcb *current_pcb = NULL;
+
+/// pointer to the pcb for the 'init' process
+struct pcb *init_pcb = NULL;
/// the process table
struct pcb ptable[N_PROCS];
/// next avaliable pid
-pid_t next_pid;
-
-/// pointer to the pcb for the 'init' process
-struct pcb *init_pcb;
-
-/// table of state name strings
-const char *proc_state_str[N_PROC_STATES] = {
- [PROC_STATE_UNUSED] = "Unu", [PROC_STATE_NEW] = "New",
- [PROC_STATE_READY] = "Rdy", [PROC_STATE_RUNNING] = "Run",
- [PROC_STATE_SLEEPING] = "Slp", [PROC_STATE_BLOCKED] = "Blk",
- [PROC_STATE_WAITING] = "Wat", [PROC_STATE_KILLED] = "Kil",
- [PROC_STATE_ZOMBIE] = "Zom",
-};
-
-/// table of priority name strings
-const char *proc_prio_str[N_PROC_PRIOS] = {
- [PROC_PRIO_HIGH] = "High",
- [PROC_PRIO_STD] = "User",
- [PROC_PRIO_LOW] = "Low ",
- [PROC_PRIO_DEFERRED] = "Def ",
-};
+pid_t next_pid = 0;
-/// table of queue ordering name strings
-const char *pcb_ord_str[N_PCB_ORDERINGS] = {
- [O_PCB_FIFO] = "FIFO",
- [O_PCB_PRIO] = "PRIO",
- [O_PCB_PID] = "PID ",
- [O_PCB_WAKEUP] = "WAKE",
-};
-
-/**
- * Priority search functions. These are used to traverse a supplied
- * queue looking for the queue entry that would precede the supplied
- * PCB when that PCB is inserted into the queue.
- *
- * Variations:
- * find_prev_wakeup() compares wakeup times
- * find_prev_priority() compares process priorities
- * find_prev_pid() compares PIDs
- *
- * Each assumes the queue should be in ascending order by the specified
- * comparison value.
- *
- * @param[in] queue The queue to search
- * @param[in] pcb The PCB to look for
- *
- * @return a pointer to the predecessor in the queue, or NULL if
- * this PCB would be at the beginning of the queue.
- */
static struct pcb *find_prev_wakeup(pcb_queue_t queue, struct pcb *pcb)
{
- // sanity checks!
assert(queue != NULL, "find_prev_wakeup: queue is null");
assert(pcb != NULL, "find_prev_wakeup: pcb is null");
struct pcb *prev = NULL;
struct pcb *curr = queue->head;
-
while (curr != NULL && curr->wakeup <= pcb->wakeup) {
prev = curr;
curr = curr->next;
@@ -103,13 +54,11 @@ static struct pcb *find_prev_wakeup(pcb_queue_t queue, struct pcb *pcb)
static struct pcb *find_prev_priority(pcb_queue_t queue, struct pcb *pcb)
{
- // sanity checks!
assert(queue != NULL, "find_prev_priority: queue is null");
assert(pcb != NULL, "find_prev_priority: pcb is null");
struct pcb *prev = NULL;
struct pcb *curr = queue->head;
-
while (curr != NULL && curr->priority <= pcb->priority) {
prev = curr;
curr = curr->next;
@@ -120,13 +69,11 @@ static struct pcb *find_prev_priority(pcb_queue_t queue, struct pcb *pcb)
static struct pcb *find_prev_pid(pcb_queue_t queue, struct pcb *pcb)
{
- // sanity checks!
assert(queue != NULL, "find_prev_pid: queue is null");
assert(pcb != NULL, "find_prev_pid: pcb is null");
struct pcb *prev = NULL;
struct pcb *curr = queue->head;
-
while (curr != NULL && curr->pid <= pcb->pid) {
prev = curr;
curr = curr->next;
@@ -145,7 +92,7 @@ static struct pcb *find_prev_pid(pcb_queue_t queue, struct pcb *pcb)
void pcb_init(void)
{
// there is no current process
- current = NULL;
+ current_pcb = NULL;
// set up the external links to the queues
QINIT(pcb_freelist, O_PCB_FIFO);
@@ -154,12 +101,8 @@ void pcb_init(void)
QINIT(sleeping, O_PCB_WAKEUP);
QINIT(zombie, O_PCB_PID);
- // We statically allocate our PCBs, so we need to add them
- // to the freelist before we can use them. If this changes
- // so that we dynamically allocate PCBs, this step either
- // won't be required, or could be used to pre-allocate some
- // number of PCB structures for future use.
-
+ // setup pcb linked list (free list)
+ // this can be done by calling pcb_free :)
struct pcb *ptr = ptable;
for (int i = 0; i < N_PROCS; ++i) {
pcb_free(ptr);
@@ -169,12 +112,11 @@ void pcb_init(void)
int pcb_alloc(struct pcb **pcb)
{
- // sanity check!
assert(pcb != NULL, "pcb_alloc: allocating a non free pcb pointer");
// remove the first PCB from the free list
struct pcb *tmp;
- if (pcb_queue_remove(pcb_freelist, &tmp) != SUCCESS)
+ if (pcb_queue_pop(pcb_freelist, &tmp) != SUCCESS)
return E_NO_PCBS;
*pcb = tmp;
@@ -197,208 +139,125 @@ void pcb_free(struct pcb *pcb)
}
}
-void pcb_zombify(register struct pcb *victim)
+void pcb_zombify(struct pcb *victim)
{
- // should this be an error?
if (victim == NULL)
return;
- // every process must have a parent, even if it's 'init'
+ assert(victim->pid != 1,
+ "pcb_zombify: attemped to zombie the init process");
assert(victim->parent != NULL, "pcb_zombify: process missing a parent");
- // We need to locate the parent of this process. We also need
- // to reparent any children of this process. We do these in
- // a single loop.
struct pcb *parent = victim->parent;
struct pcb *zchild = NULL;
+ struct pcb *curr = ptable;
- // two PIDs we will look for
- pid_t vicpid = victim->pid;
-
- // speed up access to the process table entries
- register struct pcb *curr = ptable;
-
+ // find all children of victim and reparent
for (int i = 0; i < N_PROCS; ++i, ++curr) {
- // make sure this is a valid entry
+ // is this a valid entry?
if (curr->state == PROC_STATE_UNUSED)
continue;
- // if this is our parent, just keep going - we continue
- // iterating to find all the children of this process.
- if (curr == parent)
+ // is this not our child?
+ if (curr->parent != victim)
continue;
- if (curr->parent == victim) {
- // found a child - reparent it
- curr->parent = init_pcb;
+ // reparent to init
+ curr->parent = init_pcb;
- // see if this child is already undead
- if (curr->state == PROC_STATE_ZOMBIE) {
- // if it's already a zombie, remember it, so we
- // can pass it on to 'init'; also, if there are
- // two or more zombie children, it doesn't matter
- // which one we pick here, as the others will be
- // collected when 'init' loops
- zchild = curr;
- }
+ // if this child is a zombie
+ if (curr->state == PROC_STATE_ZOMBIE) {
+ // if it's already a zombie, remember it, so we
+ // can pass it on to 'init'; also, if there are
+ // two or more zombie children, it doesn't matter
+ // which one we pick here, as the others will be
+ // collected when 'init' loops
+ zchild = curr;
}
}
- // If we found a child that was already terminated, we need to
- // wake up the init process if it's already waiting.
- //
- // NOTE: we only need to do this for one Zombie child process -
- // init will loop and collect the others after it finishes with
- // this one.
- //
- // Also note: it's possible that the exiting process' parent is
- // also init, which means we're letting one of zombie children
- // of the exiting process be cleaned up by init before the
- // existing process itself is cleaned up by init. This will work,
- // because after init cleans up the zombie, it will loop and
- // call waitpid() again, by which time this exiting process will
- // be marked as a zombie.
-
+ // schedule init if zombie child found
if (zchild != NULL && init_pcb->state == PROC_STATE_WAITING) {
- // dequeue the zombie
- assert(pcb_queue_remove_this(zombie, zchild) == SUCCESS,
+ assert(pcb_queue_remove(zombie, zchild) == SUCCESS,
"pcb_zombify: cannot remove zombie process from queue");
-
- assert(pcb_queue_remove_this(waiting, init_pcb) == SUCCESS,
+ assert(pcb_queue_remove(waiting, init_pcb) == SUCCESS,
"pcb_zombify: cannot remove waiting process from queue");
- // intrinsic return value is the PID
+ // send exited pid to init
PCB_RET(init_pcb) = zchild->pid;
-
- // may also want to return the exit status
- int64_t *ptr = (int64_t *)PCB_ARG(init_pcb, 2);
-
+ // set &status in init's waitpid call
+ int *ptr = (int *)PCB_ARG2(init_pcb);
if (ptr != NULL) {
- // BUG:
- // ********************************************************
- // ** Potential VM issue here! This code assigns the exit
- // ** status into a variable in the parent's address space.
- // ** This works in the baseline because we aren't using
- // ** any type of memory protection. If address space
- // ** separation is implemented, this code will very likely
- // ** STOP WORKING, and will need to be fixed.
- // ********************************************************
+ mem_ctx_switch(init_pcb->memctx);
*ptr = zchild->exit_status;
+ mem_ctx_switch(kernel_mem_ctx);
}
- // all done - schedule 'init', and clean up the zombie
+ // schedule init and cleanup child
schedule(init_pcb);
pcb_cleanup(zchild);
}
- // Now, deal with the parent of this process. If the parent is
- // already waiting, just wake it up and clean up this process.
- // Otherwise, this process becomes a zombie.
- //
- // NOTE: if the exiting process' parent is init and we just woke
- // init up to deal with a zombie child of the exiting process,
- // init's status won't be Waiting any more, so we don't have to
- // worry about it being scheduled twice.
-
+ // if the parent is waiting, wake it up and clean the victim,
+ // otherwise the victim will become a zombie
if (parent->state == PROC_STATE_WAITING) {
// verify that the parent is either waiting for this process
// or is waiting for any of its children
- uint64_t target = PCB_ARG(parent, 1);
-
- if (target == 0 || target == vicpid) {
- // the parent is waiting for this child or is waiting
- // for any of its children, so we can wake it up.
-
- // intrinsic return value is the PID
- PCB_RET(parent) = vicpid;
-
- // may also want to return the exit status
- int64_t *ptr = (int64_t *)PCB_ARG(parent, 2);
+ uint64_t target = PCB_ARG1(parent);
+ if (target != 0 || target == victim->pid) {
+ // send exited pid to parent
+ PCB_RET(parent) = victim->pid;
+ // send &status to parent
+ int *ptr = (int *)PCB_ARG2(parent);
if (ptr != NULL) {
- // ********************************************************
- // ** Potential VM issue here! This code assigns the exit
- // ** status into a variable in the parent's address space.
- // ** This works in the baseline because we aren't using
- // ** any type of memory protection. If address space
- // ** separation is implemented, this code will very likely
- // ** STOP WORKING, and will need to be fixed.
- // ********************************************************
+ mem_ctx_switch(parent->memctx);
*ptr = victim->exit_status;
+ mem_ctx_switch(kernel_mem_ctx);
}
- // all done - schedule the parent, and clean up the zombie
+ // schedule the parent, and clean up the zombie
schedule(parent);
pcb_cleanup(victim);
-
return;
}
}
- // The parent isn't waiting OR is waiting for a specific child
- // that isn't this exiting process, so we become a Zombie.
- //
- // This code assumes that Zombie processes are *not* in
- // a queue, but instead are just in the process table with
- // a state of 'Zombie'. This simplifies life immensely,
- // because we won't need to dequeue it when it is collected
- // by its parent.
-
victim->state = PROC_STATE_ZOMBIE;
assert(pcb_queue_insert(zombie, victim) == SUCCESS,
"cannot insert victim process into zombie queue");
-
- // Note: we don't call _dispatch() here - we leave that for
- // the calling routine, as it's possible we don't need to
- // choose a new current process.
}
void pcb_cleanup(struct pcb *pcb)
{
- // avoid deallocating a NULL pointer
- if (pcb == NULL) {
- // should this be an error?
+ if (pcb == NULL)
return;
- }
-
- // we need to release all the VM data structures and frames
- mem_ctx_free(pcb->memctx);
- // TODO: close open files
-
- // release the PCB itself
+ if (pcb->memctx)
+ mem_ctx_free(pcb->memctx);
pcb_free(pcb);
}
struct pcb *pcb_find_pid(pid_t pid)
{
- // must be a valid PID
- if (pid < 1) {
+ if (pid < 1)
return NULL;
- }
- // scan the process table
struct pcb *p = ptable;
-
for (int i = 0; i < N_PROCS; ++i, ++p) {
if (p->pid == pid && p->state != PROC_STATE_UNUSED) {
return p;
}
}
- // didn't find it!
return NULL;
}
struct pcb *pcb_find_ppid(pid_t pid)
{
- // must be a valid PID
- if (pid < 1) {
+ if (pid < 1)
return NULL;
- }
- // scan the process table
struct pcb *p = ptable;
-
for (int i = 0; i < N_PROCS; ++i, ++p) {
assert(p->parent != NULL,
"pcb_find_ppid: process does not have a parent");
@@ -407,50 +266,33 @@ struct pcb *pcb_find_ppid(pid_t pid)
}
}
- // didn't find it!
return NULL;
}
int pcb_queue_reset(pcb_queue_t queue, enum pcb_queue_order style)
{
- // sanity check
assert(queue != NULL, "pcb_queue_reset: queue is null");
- // make sure the style is valid
- if (style < 0 || style >= N_PCB_ORDERINGS) {
+ if (style < 0 || style >= N_PCB_ORDERINGS)
return E_BAD_PARAM;
- }
- // reset the queue
queue->head = queue->tail = NULL;
queue->order = style;
-
return SUCCESS;
}
bool pcb_queue_empty(pcb_queue_t queue)
{
- // if there is no queue, blow up
assert(queue != NULL, "pcb_queue_empty: queue is empty");
-
return PCB_QUEUE_EMPTY(queue);
}
-/**
- * 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)
- */
size_t pcb_queue_length(const pcb_queue_t queue)
{
- // sanity check
assert(queue != NULL, "pcb_queue_length: queue is null");
- // this is pretty simple
- register struct pcb *tmp = queue->head;
- register size_t num = 0;
-
+ struct pcb *tmp = queue->head;
+ size_t num = 0;
while (tmp != NULL) {
++num;
tmp = tmp->next;
@@ -461,27 +303,19 @@ size_t pcb_queue_length(const pcb_queue_t queue)
int pcb_queue_insert(pcb_queue_t queue, struct pcb *pcb)
{
- // sanity checks
assert(queue != NULL, "pcb_queue_insert: queue is null");
assert(pcb != NULL, "pcb_queue_insert: pcb is null");
- // if this PCB is already in a queue, we won't touch it
- if (pcb->next != NULL) {
- // what to do? we let the caller decide
+ if (pcb->next != NULL)
return E_BAD_PARAM;
- }
- // is the queue empty?
if (queue->head == NULL) {
queue->head = queue->tail = pcb;
return SUCCESS;
}
assert(queue->tail != NULL, "pcb_queue_insert: queue tail is null");
- // no, so we need to search it
struct pcb *prev = NULL;
-
- // find the predecessor node
switch (queue->order) {
case O_PCB_FIFO:
prev = queue->tail;
@@ -496,12 +330,10 @@ int pcb_queue_insert(pcb_queue_t queue, struct pcb *pcb)
prev = find_prev_wakeup(queue, pcb);
break;
default:
- // do we need something more specific here?
return E_BAD_PARAM;
}
- // OK, we found the predecessor node; time to do the insertion
-
+ // found the predecessor node, time to do the insertion
if (prev == NULL) {
// there is no predecessor, so we're
// inserting at the front of the queue
@@ -511,12 +343,10 @@ int pcb_queue_insert(pcb_queue_t queue, struct pcb *pcb)
queue->tail = pcb;
}
queue->head = pcb;
-
} else if (prev->next == NULL) {
// append at end
prev->next = pcb;
queue->tail = pcb;
-
} else {
// insert between prev & prev->next
pcb->next = prev->next;
@@ -526,51 +356,35 @@ int pcb_queue_insert(pcb_queue_t queue, struct pcb *pcb)
return SUCCESS;
}
-int pcb_queue_remove(pcb_queue_t queue, struct pcb **pcb)
+int pcb_queue_pop(pcb_queue_t queue, struct pcb **pcb)
{
- //sanity checks
- assert(queue != NULL, "pcb_queue_remove: queue is null");
- assert(pcb != NULL, "pcb_queue_remove: pcb is null");
+ assert(queue != NULL, "pcb_queue_pop: queue is null");
+ assert(pcb != NULL, "pcb_queue_pop: pcb is null");
- // can't get anything if there's nothing to get!
- if (PCB_QUEUE_EMPTY(queue)) {
+ if (PCB_QUEUE_EMPTY(queue))
return E_EMPTY_QUEUE;
- }
- // take the first entry from the queue
struct pcb *tmp = queue->head;
queue->head = tmp->next;
-
- // disconnect it completely
tmp->next = NULL;
-
- // was this the last thing in the queue?
if (queue->head == NULL) {
- // yes, so clear the tail pointer for consistency
queue->tail = NULL;
}
- // save the pointer
*pcb = tmp;
-
return SUCCESS;
}
-int pcb_queue_remove_this(pcb_queue_t queue, struct pcb *pcb)
+int pcb_queue_remove(pcb_queue_t queue, struct pcb *pcb)
{
- //sanity checks
- assert(queue != NULL, "pcb_queue_remove_this: queue is null");
- assert(pcb != NULL, "pcb_queue_remove_this: pcb is null");
+ assert(queue != NULL, "pcb_queue_remove: queue is null");
+ assert(pcb != NULL, "pcb_queue_remove: pcb is null");
- // can't get anything if there's nothing to get!
- if (PCB_QUEUE_EMPTY(queue)) {
+ if (PCB_QUEUE_EMPTY(queue))
return E_EMPTY_QUEUE;
- }
- // iterate through the queue until we find the desired PCB
struct pcb *prev = NULL;
struct pcb *curr = queue->head;
-
while (curr != NULL && curr != pcb) {
prev = curr;
curr = curr->next;
@@ -587,8 +401,7 @@ int pcb_queue_remove_this(pcb_queue_t queue, struct pcb *pcb)
if (curr == NULL) {
// case 1
- assert(prev != NULL,
- "pcb_queue_remove_this: prev element in queue is null");
+ assert(prev != NULL, "pcb_queue_remove: prev element in queue is null");
// case 4
return E_NOT_FOUND;
}
@@ -618,239 +431,45 @@ int pcb_queue_remove_this(pcb_queue_t queue, struct pcb *pcb)
// there's a possible consistancy problem here if somehow
// one of the queue pointers is NULL and the other one
// is not NULL
-
assert((queue->head == NULL && queue->tail == NULL) ||
(queue->head != NULL && queue->tail != NULL),
- "pcb_queue_remove_this: queue consistancy problem");
+ "pcb_queue_remove: queue consistancy problem");
return SUCCESS;
}
struct pcb *pcb_queue_peek(const pcb_queue_t queue)
{
- //sanity check
assert(queue != NULL, "pcb_queue_peek: queue is null");
- // can't get anything if there's nothing to get!
- if (PCB_QUEUE_EMPTY(queue)) {
+ if (PCB_QUEUE_EMPTY(queue))
return NULL;
- }
- // just return the first entry from the queue
return queue->head;
}
void schedule(struct pcb *pcb)
{
- // sanity check
assert(pcb != NULL, "schedule: pcb is null");
- // check for a killed process
if (pcb->state == PROC_STATE_KILLED)
panic("attempted to schedule killed process %d", pcb->pid);
- // mark it as ready
pcb->state = PROC_STATE_READY;
- // add it to the ready queue
if (pcb_queue_insert(ready, pcb) != SUCCESS)
panic("schedule insert fail");
}
void dispatch(void)
{
- // verify that there is no current process
- assert(current == NULL, "dispatch: current process is not null");
+ assert(current_pcb == NULL, "dispatch: current process is not null");
- // grab whoever is at the head of the queue
- int status = pcb_queue_remove(ready, &current);
- if (status != SUCCESS) {
+ int status = pcb_queue_pop(ready, &current_pcb);
+ if (status != SUCCESS)
panic("dispatch queue remove failed, code %d", status);
- }
// set the process up for success
- current->state = PROC_STATE_RUNNING;
- current->ticks = PROC_QUANTUM_STANDARD;
-}
-
-void ctx_dump(const char *msg, register struct cpu_regs *regs)
-{
- // first, the message (if there is one)
- if (msg)
- kputs(msg);
-
- // the pointer
- kprintf(" @ %16p: ", (void *)regs);
-
- // if it's NULL, why did you bother calling me?
- if (regs == NULL) {
- kprintf(" NULL???\n");
- return;
- }
-
- // now, the contents
- kputc('\n');
- cpu_print_regs(regs);
-}
-
-void ctx_dump_all(const char *msg)
-{
- // first, the message (if there is one)
- if (msg)
- kputs(msg);
-
- int n = 0;
- register struct pcb *pcb = ptable;
- for (int i = 0; i < N_PROCS; ++i, ++pcb) {
- if (pcb->state != PROC_STATE_UNUSED) {
- ++n;
- kprintf("%2d(%d): ", n, pcb->pid);
- ctx_dump(NULL, pcb->regs);
- }
- }
-}
-
-void pcb_dump(const char *msg, register struct pcb *pcb, bool all)
-{
- // first, the message (if there is one)
- if (msg)
- kputs(msg);
-
- // the pointer
- kprintf(" @ %16px:", (void *)pcb);
-
- // if it's NULL, why did you bother calling me?
- if (pcb == NULL) {
- kputs(" NULL???\n");
- return;
- }
-
- kprintf(" %d", pcb->pid);
- kprintf(" %s",
- pcb->state >= N_PROC_STATES ? "???" : proc_state_str[pcb->state]);
-
- if (!all) {
- // just printing IDs and states on one line
- return;
- }
-
- // now, the rest of the contents
- kprintf(" %s", pcb->priority >= N_PROC_PRIOS ?
- "???" :
- proc_prio_str[pcb->priority]);
-
- kprintf(" ticks %zu xit %d wake %16lx\n", pcb->ticks, pcb->exit_status,
- pcb->wakeup);
-
- kprintf(" parent %16p", (void *)pcb->regs);
- if (pcb->parent != NULL) {
- kprintf(" (%u)", pcb->parent->pid);
- }
-
- kprintf(" next %16p context %16p memctx %16p", (void *)pcb->next,
- (void *)pcb->regs, (void *)pcb->memctx);
-
- kputc('\n');
-}
-
-void pcb_queue_dump(const char *msg, pcb_queue_t queue, bool contents)
-{
- // report on this queue
- kprintf("%s: ", msg);
- if (queue == NULL) {
- kputs("NULL???\n");
- return;
- }
-
- // first, the basic data
- kprintf("head %16p tail %16p", (void *)queue->head, (void *)queue->tail);
-
- // next, how the queue is ordered
- kprintf(" order %s\n", queue->order >= N_PCB_ORDERINGS ?
- "????" :
- pcb_ord_str[queue->order]);
-
- // if there are members in the queue, dump the first few PIDs
- if (contents && queue->head != NULL) {
- kputs(" PIDs: ");
- struct pcb *tmp = queue->head;
- for (int i = 0; i < 5 && tmp != NULL; ++i, tmp = tmp->next) {
- kprintf(" [%u]", tmp->pid);
- }
-
- if (tmp != NULL) {
- kputs(" ...");
- }
-
- kputc('\n');
- }
-}
-
-void ptable_dump(const char *msg, bool all)
-{
- if (msg)
- kputs(msg);
- kputc(' ');
-
- int used = 0;
- int empty = 0;
-
- register struct pcb *pcb = ptable;
- for (int i = 0; i < N_PROCS; ++i) {
- if (pcb->state == PROC_STATE_UNUSED) {
- // an empty slot
- ++empty;
-
- } else {
- // a non-empty slot
- ++used;
-
- // if not dumping everything, add commas if needed
- if (!all && used) {
- kputc(',');
- }
-
- // report the table slot #
- kprintf(" #%d:", i);
-
- // and dump the contents
- pcb_dump(NULL, pcb, all);
- }
- }
-
- // only need this if we're doing one-line output
- if (!all) {
- kputc('\n');
- }
-
- // sanity check - make sure we saw the correct number of table slots
- if ((used + empty) != N_PROCS) {
- kprintf("Table size %d, used %d + empty %d = %d???\n", N_PROCS, used,
- empty, used + empty);
- }
-}
-
-void ptable_dump_counts(void)
-{
- size_t nstate[N_PROC_STATES] = { 0 };
- size_t unknown = 0;
-
- int n = 0;
- struct pcb *ptr = ptable;
- while (n < N_PROCS) {
- if (ptr->state < 0 || ptr->state >= N_PROC_STATES) {
- ++unknown;
- } else {
- ++nstate[ptr->state];
- }
- ++n;
- ++ptr;
- }
-
- kprintf("Ptable: %zu ***", unknown);
- for (n = 0; n < N_PROC_STATES; ++n) {
- kprintf(" %zu %s", nstate[n],
- proc_state_str[n] != NULL ? proc_state_str[n] : "???");
- }
- kputc('\n');
+ current_pcb->state = PROC_STATE_RUNNING;
+ current_pcb->ticks = 3; // ticks per process
}
diff --git a/kernel/user.c b/kernel/user.c
new file mode 100644
index 0000000..0a237e9
--- /dev/null
+++ b/kernel/user.c
@@ -0,0 +1,11 @@
+#include <comus/procs.h>
+#include <comus/memory.h>
+
+void user_cleanup(struct pcb *pcb)
+{
+ if (pcb == NULL)
+ return;
+
+ mem_ctx_free(pcb->memctx);
+ pcb->memctx = NULL;
+}
diff --git a/user/Makefile b/user/Makefile
new file mode 100644
index 0000000..a01d950
--- /dev/null
+++ b/user/Makefile
@@ -0,0 +1,68 @@
+### Copyright (c) 2025 Freya Murphy <freya@freyacat.org>
+
+.PHONY: build clean
+.SILENT:
+
+AS ?= as
+AR ?= ar
+CC ?= cc
+LD ?= ld
+CPP ?= cpp
+
+CPPFLAGS += -Iinclude
+
+CFLAGS += -O2
+CFLAGS += -std=c11
+CFLAGS += -Wall -Wextra -pedantic
+CFLAGS += -fno-pie -fno-stack-protector
+CFLAGS += -fno-omit-frame-pointer -ffreestanding
+CFLAGS += -fno-builtin
+CFLAGS += -D DEBUG -g
+CFLAGS += $(CPPFLAGS)
+
+LDFLAGS += -nmagic -nostdlib
+LDFLAGS += -z noexecstack
+
+USER=*
+LIB=lib
+BIN=../bin/user
+
+H_SRC = $(shell find include -type f -name "*.h")
+LIBA_SRC = $(shell find $(LIB) -type f -name "*.S")
+LIBA_OBJ = $(patsubst %.S,$(BIN)/%.S.o,$(LIBA_SRC))
+LIBC_SRC = $(shell find $(LIB) -type f -name "*.c")
+LIBC_OBJ = $(patsubst %.c,$(BIN)/%.o,$(LIBC_SRC))
+USER_SRC = $(shell find $(USER) -maxdepth 0 -type f -name "*.c")
+USER_OBJ = $(patsubst %.c,$(BIN)/%.o,$(USER_SRC))
+USER_PROJ = $(patsubst %.o,%,$(USER_OBJ))
+
+build: $(USER_PROJ)
+
+clean:
+ rm -fr $(BIN)
+
+$(LIBA_OBJ): $(BIN)/%.S.o : %.S $(H_SRC)
+ mkdir -p $(@D)
+ printf "\033[33m AS \033[0m%s\n" user/$<
+ $(CPP) $(CPPFLAGS) -o $@.cpp $<
+ $(AS) -o $@ $@.cpp
+
+$(LIBC_OBJ): $(BIN)/%.o : %.c $(H_SRC)
+ mkdir -p $(@D)
+ printf "\033[34m CC \033[0m%s\n" user/$<
+ $(CC) -c $(CFLAGS) -o $@ $<
+
+$(BIN)/libc.a: $(LIBA_OBJ) $(LIBC_OBJ)
+ @mkdir -p $(@D)
+ printf "\033[35m AR \033[0m%s\n" $(shell echo $@ | cut -c 4-)
+ $(AR) rcs $@ $(LIBA_OBJ) $(LIBC_OBJ)
+
+$(USER_OBJ): $(BIN)/%.o : %.c $(H_SRC)
+ mkdir -p $(@D)
+ printf "\033[34m CC \033[0m%s\n" user/$<
+ $(CC) -c $(CFLAGS) -o $@ $<
+
+$(USER_PROJ): % : %.o $(BIN)/libc.a
+ mkdir -p $(@D)
+ printf "\033[32m LD \033[0m%s\n" $(shell echo $@ | cut -c 4-)
+ $(LD) $(LDFLAGS) -T ../config/user.ld -o $@ $< $(BIN)/libc.a
diff --git a/user/README b/user/README
deleted file mode 100644
index 548aac4..0000000
--- a/user/README
+++ /dev/null
@@ -1,25 +0,0 @@
-This directory contains the source code for all user-level processes,
-split out by main function.
-
-Naming convention:
-
- idle() classic 'idle' process; ensures there is always a
- runnable process to dispatch (vs., for instance, having
- dispatch() pause when there is nothing to dispatch).
-
- init() classic 'init' process; starts the idle process, and
- starts (and restarts) the user shell program.
-
- shell() "user shell" process, for spawning individual tests
-
- progN() program source code for user process(es) 'N'
-
-All of these expect at least one command-line argument. All are invoked
-with command lines of this form:
-
- name x n
-
-Each of these is designed to be compiled and linked separately, with the
-resulting load modules bundled into a blob for automatic loading by the
-bootstrap. Each will typically use one or more library functions from the
-../lib directory.
diff --git a/user/compile_flags.txt b/user/compile_flags.txt
new file mode 100644
index 0000000..c519df5
--- /dev/null
+++ b/user/compile_flags.txt
@@ -0,0 +1,8 @@
+-c
+-std=c11
+-Iinclude
+-ffreestanding
+-fno-builtin
+-Wall
+-Wextra
+-pedantic
diff --git a/user/hello.c b/user/hello.c
new file mode 100644
index 0000000..5d91d03
--- /dev/null
+++ b/user/hello.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+
+int main(void)
+{
+ printf("Hello World!\n");
+ return 0;
+}
diff --git a/user/idle.c b/user/idle.c
deleted file mode 100644
index c088a3c..0000000
--- a/user/idle.c
+++ /dev/null
@@ -1,52 +0,0 @@
-#include <common.h>
-
-/**
-** Idle process: write, getpid, gettime, exit
-**
-** Reports itself, then loops forever delaying and printing a character.
-** MUST NOT SLEEP, as it must always be available in the ready queue
-** when there is no other process to dispatch.
-**
-** Invoked as: idle
-*/
-
-USERMAIN(main)
-{
- // this is the character we will repeatedly print
- char ch = '.';
-
- // ignore the command-line arguments
- (void)argc;
- (void)argv;
-
- // get some current information
- uint_t pid = getpid();
- uint32_t now = gettime();
- enum priority_e prio = getprio();
-
- char buf[128];
- sprint(buf, "Idle [%d], started @ %u\n", pid, prio, now);
- cwrites(buf);
-
- // report our presence on the console
- cwrites("Idle started\n");
-
- write(CHAN_SIO, &ch, 1);
-
- // idle() should never block - it must always be available
- // for dispatching when we need to pick a new current process
-
- for (;;) {
- DELAY(LONG);
- write(CHAN_SIO, &ch, 1);
- }
-
- // we should never reach this point!
- now = gettime();
- sprint(buf, "Idle [%d] EXITING @ %u!?!?!\n", pid, now);
- cwrites(buf);
-
- exit(1);
-
- return (42);
-}
diff --git a/user/include/error.h b/user/include/error.h
index b9265b4..e62c3b3 100644..120000
--- a/user/include/error.h
+++ b/user/include/error.h
@@ -1,30 +1 @@
-/**
- * @file errno.h
- *
- * @author Freya Murphy <freya@freyacat.org>
- *
- * Error codes.
- */
-
-// public error codes
-#define SUCCESS (0)
-#define E_SUCCESS SUCCESS
-#define E_FAILURE (-1)
-#define E_BAD_PARAM (-2)
-#define E_BAD_FD (-3)
-#define E_NO_CHILDREN (-4)
-#define E_NO_MEMORY (-5)
-#define E_NOT_FOUND (-6)
-#define E_NO_PROCS (-7)
-
-// internal error codes
-#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)
+../../kernel/include/comus/error.h \ No newline at end of file
diff --git a/user/include/stdio.h b/user/include/stdio.h
index 8a938ab..2e2abf4 100644
--- a/user/include/stdio.h
+++ b/user/include/stdio.h
@@ -27,6 +27,47 @@ extern FILE *stdout;
#define stdout stdout
/**
+ * Get a char from stdin
+ *
+ * @returns the character or EOF on failure
+ */
+extern int getchar(void);
+
+/**
+ * Get a chracter from a stream
+ *
+ * @param stream - the stream to read from
+ * @returns the character or EOF on failure
+ */
+extern int getc(FILE *stream);
+
+/**
+ * Get a chracter from a stream
+ *
+ * @param stream - the stream to read from
+ * @returns the character or EOF on failure
+ */
+extern int fgetc(FILE *stream);
+
+/**
+ * Reads a string from stdin
+ *
+ * @param s - the buffer to read into
+ * @returns s on success, NULL on error or EOF
+ */
+extern char *gets(char *s);
+
+/**
+ * Reads a string from stdin
+ *
+ * @param s - the buffer to read into
+ * @param size - reads at most 1 less then size
+ * @param stream - stream to read from from
+ * @returns s on success, NULL on error or EOF
+ */
+extern char *fgets(char *restrict str, int size, FILE *stream);
+
+/**
* Prints out a char
*
* @param c - the char
@@ -140,7 +181,7 @@ extern int vsnprintf(char *restrict s, size_t maxlen, const char *format,
* @param format - the format string
* @param ... - variable args for the format
*/
-__attribute__((format(printf, 2, 3))) extern void
+__attribute__((format(printf, 2, 3))) extern int
fprintf(FILE *stream, const char *format, ...);
/**
@@ -150,7 +191,7 @@ fprintf(FILE *stream, const char *format, ...);
* @param format - the format string
* @param args - variable arg list for the format
*/
-extern void vfprintf(FILE *stream, const char *format, va_list args);
+extern int vfprintf(FILE *stream, const char *format, va_list args);
/**
* opens a file with a given file name
diff --git a/user/include/syscalls.h b/user/include/syscalls.h
new file mode 120000
index 0000000..0537882
--- /dev/null
+++ b/user/include/syscalls.h
@@ -0,0 +1 @@
+../../kernel/include/comus/syscalls.h \ No newline at end of file
diff --git a/user/include/unistd.h b/user/include/unistd.h
index 1f83abc..a815914 100644
--- a/user/include/unistd.h
+++ b/user/include/unistd.h
@@ -147,33 +147,18 @@ extern int kill(pid_t pid);
extern int sleep(unsigned long ms);
/**
- * Wait for any child to exit
+ * Set the heap break to addr
*
- * @param status - pointer to int32_t into which the child's status is placed,
- * or NULL
- * @return The pid of the terminated child, or an error code
- *
- * Analogous to waitpid(0,status)
- */
-extern int wait(int *status);
-
-/**
- * Spawn a new process running a different program
- *
- * @param prog - program table index of the program to spawn
- * @param args - the command-line argument vector for the process
- * @return the pid of the child, or an error code
- *
- * Analogous to calling fork and exec
+ * @param addr - sets the programs break to addr
+ * @return the previos program break on success, or NULL on failure
*/
-extern int spawn(pid_t prog, char **args);
+extern void *brk(const void *addr);
/**
* Increment the program's data space by increment bytes.
*
* @param increment - the amount in bytes to increment the heap
* @return the previos program break on success, or NULL on failure
- *
*/
extern void *sbrk(intptr_t increment);
diff --git a/user/init.c b/user/init.c
deleted file mode 100644
index be8e393..0000000
--- a/user/init.c
+++ /dev/null
@@ -1,185 +0,0 @@
-#include <common.h>
-
-/**
-** Initial process; it starts the other top-level user processes.
-**
-** Prints a message at startup, '+' after each user process is spawned,
-** and '!' before transitioning to wait() mode to the SIO, and
-** startup and transition messages to the console. It also reports
-** each child process it collects via wait() to the console along
-** with that child's exit status.
-*/
-
-/*
-** "Spawn table" process entry. Similar to the one in shell.c, but
-** this version has a field to hold the PID of the spawned process
-** to allow 'init' to respawn it when it terminates.
-*/
-typedef struct proc_s {
- uint_t index; // process table index
- uint_t pid; // its PID (when spawned)
- uint8_t e_prio; // process priority
- char select[3]; // identifying character, NUL, extra
- char *args[N_ARGS]; // argument vector strings
-} proc_t;
-
-/*
-** Create a spawn table entry for a process with a string literal
-** as its argument buffer. We rely on the fact that the C standard
-** ensures our array of pointers will be filled out with NULLs
-*/
-#define PROCENT(e, p, s, ...) \
- { \
- e, 0, p, s, \
- { \
- __VA_ARGS__, NULL \
- } \
- }
-
-// sentinel value for the end of the table - must be updated
-// if you have more than 90,210 user programs in the table
-#define TBLEND 90210
-
-/*
-** This table contains one entry for each process that should be
-** started by 'init'. Typically, this includes the 'idle' process
-** and a 'shell' process.
-*/
-static proc_t spawn_table[] = {
-
- // the idle process; it runs at Deferred priority,
- // so it will only be dispatched when there is
- // nothing else available to be dispatched
- PROCENT(Idle, PRIO_DEFERRED, "!", "idle", "."),
-
- // the user shell
- PROCENT(Shell, PRIO_STD, "@", "shell"),
-
- // PROCENT( 0, 0, 0, 0 )
- { TBLEND }
-};
-
-// character to be printed by init when it spawns a process
-static char ch = '+';
-
-/**
-** process - spawn all user processes listed in the supplied table
-**
-** @param proc pointer to the spawn table entry to be used
-*/
-
-static void process(proc_t *proc)
-{
- char buf[128];
-
- // kick off the process
- int32_t p = fork();
- if (p < 0) {
- // error!
- sprint(buf, "INIT: fork for #%d failed\n", (uint32_t)(proc->index));
- cwrites(buf);
-
- } else if (p == 0) {
- // change child's priority
- (void)setprio(proc->e_prio);
-
- // now, send it on its way
- exec(proc->index, proc->args);
-
- // uh-oh - should never get here!
- sprint(buf, "INIT: exec(0x%08x) failed\n", (uint32_t)(proc->index));
- cwrites(buf);
-
- } else {
- // parent just reports that another one was started
- swritech(ch);
-
- proc->pid = p;
- }
-}
-
-/*
-** The initial user process. Should be invoked with zero or one
-** argument; if provided, the first argument should be the ASCII
-** character 'init' will print to indicate the spawning of a process.
-*/
-USERMAIN(main)
-{
- char buf[128];
-
- // check to see if we got a non-standard "spawn" character
- if (argc > 1) {
- // maybe - check it to be sure it's printable
- uint_t i = argv[1][0];
- if (i > ' ' && i < 0x7f) {
- ch = argv[1][0];
- }
- }
-
- cwrites("Init started\n");
-
- // home up, clear on a TVI 925
- swritech('\x1a');
-
- // wait a bit
- DELAY(SHORT);
-
- // a bit of Dante to set the mood :-)
- swrites("\n\nSpem relinquunt qui huc intrasti!\n\n\r");
-
- /*
- ** Start all the user processes
- */
-
- cwrites("INIT: starting user processes\n");
-
- proc_t *next;
- for (next = spawn_table; next->index != TBLEND; ++next) {
- process(next);
- }
-
- swrites(" !!!\r\n\n");
-
- /*
- ** At this point, we go into an infinite loop waiting
- ** for our children (direct, or inherited) to exit.
- */
-
- cwrites("INIT: transitioning to wait() mode\n");
-
- for (;;) {
- int32_t status;
- int whom = waitpid(0, &status);
-
- // PIDs must be positive numbers!
- if (whom <= 0) {
- sprint(buf, "INIT: waitpid() returned %d???\n", whom);
- cwrites(buf);
- } else {
- // got one; report it
- sprint(buf, "INIT: pid %d exit(%d)\n", whom, status);
- cwrites(buf);
-
- // figure out if this is one of ours
- for (next = spawn_table; next->index != TBLEND; ++next) {
- if (next->pid == whom) {
- // one of ours - reset the PID field
- // (in case the spawn attempt fails)
- next->pid = 0;
- // and restart it
- process(next);
- break;
- }
- }
- }
- }
-
- /*
- ** SHOULD NEVER REACH HERE
- */
-
- cwrites("*** INIT IS EXITING???\n");
- exit(1);
-
- return (1); // shut the compiler up
-}
diff --git a/user/lib/alloc.c b/user/lib/alloc.c
index 49c762b..3d987d4 100644
--- a/user/lib/alloc.c
+++ b/user/lib/alloc.c
@@ -21,6 +21,19 @@ static const size_t header_len = sizeof(struct page_header);
static struct page_header *start_header = NULL;
static struct page_header *end_header = NULL;
+static void *alloc_pages(size_t pages)
+{
+ (void)pages;
+ // TODO: impl
+ return NULL;
+}
+
+static void free_pages(struct page_header *header)
+{
+ (void)header;
+ // TODO: impl
+}
+
static struct page_header *get_header(void *ptr)
{
struct page_header *header =
@@ -39,7 +52,6 @@ static void *alloc_new(size_t size)
{
size_t pages = ((size + header_len) / PAGE_SIZE) + 1;
- // FIXME: use brk/sbrk
void *addr = alloc_pages(pages);
void *mem = (char *)addr + header_len;
@@ -209,7 +221,6 @@ void free(void *ptr)
header->next->prev = header->prev;
if (header->prev)
header->prev->next = header->next;
- // FIXME: use brk/sbrk
free_pages(header);
}
}
diff --git a/user/lib/entry.S b/user/lib/entry.S
index 87ad9c7..34390a0 100644
--- a/user/lib/entry.S
+++ b/user/lib/entry.S
@@ -1,25 +1,15 @@
-//
-// user-level startup routine
-//
- .text
.globl _start
- .globl main
- .globl exit
+ .extern main
+ .extern exit
-// entry point - this is where the kernel starts us running
+ .section .text
+ .code64
_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
+ subq $16, %rsp # ???
+ pushq %rax
call exit
- // if we come back from that, something bad has
- // happened, so we just lock up
-1: jmp 1b
+halt:
+ jmp halt
diff --git a/user/lib/fread.c b/user/lib/fread.c
new file mode 100644
index 0000000..f8d0ad8
--- /dev/null
+++ b/user/lib/fread.c
@@ -0,0 +1,65 @@
+#include <stdio.h>
+#include <unistd.h>
+
+FILE *stdin = (void *)0;
+
+int getchar(void)
+{
+ return fgetc(stdin);
+}
+
+int getc(FILE *stream)
+{
+ return fgetc(stream);
+}
+
+int fgetc(FILE *stream)
+{
+ int c;
+ if (fread(&c, 1, 1, stream) < 1)
+ return EOF;
+ return c;
+}
+
+char *gets(char *str)
+{
+ char *s = str;
+ while (1) {
+ char c = fgetc(stdin);
+ if (c == '\n' || c == EOF || c == '\0')
+ break;
+ *(str++) = c;
+ }
+ *str = '\0';
+ return s;
+}
+
+char *fgets(char *restrict str, int size, FILE *stream)
+{
+ if (size < 1)
+ return NULL;
+
+ char *s = str;
+ while (size > 1) {
+ char c = fgetc(stream);
+ if (c == '\n' || c == EOF || c == '\0')
+ break;
+ *(str++) = c;
+ size--;
+ }
+
+ *str = '\0';
+ return s;
+}
+
+size_t fread(void *restrict ptr, size_t size, size_t n, FILE *restrict stream)
+{
+ int fd = (uintptr_t)stream;
+ char *restrict buf = ptr;
+
+ for (size_t i = 0; i < n; i++)
+ if (read(fd, buf + i * size, size) < 1)
+ return i;
+
+ return n;
+}
diff --git a/user/lib/fwrite.c b/user/lib/fwrite.c
new file mode 100644
index 0000000..aa828e0
--- /dev/null
+++ b/user/lib/fwrite.c
@@ -0,0 +1,57 @@
+#include <stdio.h>
+#include <unistd.h>
+
+FILE *stdout = (void *)1;
+
+int putchar(int c)
+{
+ return putc(c, stdout);
+}
+
+int putc(int c, FILE *stream)
+{
+ return fputc(c, stream);
+}
+
+int fputc(int c, FILE *stream)
+{
+ if (fwrite(&c, 1, 1, stream) < 1)
+ return EOF;
+ return c;
+}
+
+int puts(const char *str)
+{
+ int res;
+ res = fputs(str, stdout);
+ if (res == EOF)
+ return res;
+ res = fputc('\n', stdout);
+ if (res == EOF)
+ return res;
+ return 0;
+}
+
+int fputs(const char *str, FILE *stream)
+{
+ int res;
+ while (*str) {
+ res = fputc(*str++, stream);
+ if (res == EOF)
+ return res;
+ }
+ return 0;
+}
+
+size_t fwrite(const void *restrict ptr, size_t size, size_t n,
+ FILE *restrict stream)
+{
+ int fd = (uintptr_t)stream;
+ const char *restrict buf = ptr;
+
+ for (size_t i = 0; i < n; i++)
+ if (write(fd, buf + i * size, size) < 1)
+ return i;
+
+ return n;
+}
diff --git a/user/lib/printf.c b/user/lib/printf.c
index 65d7f0f..e6abd09 100644
--- a/user/lib/printf.c
+++ b/user/lib/printf.c
@@ -7,6 +7,7 @@
#include <stdarg.h>
#define PRINTF_NUMERIC_BUF_LEN 50
+#define PRINTF_BUFFER_LEN 256
typedef union {
unsigned long long int u;
@@ -620,42 +621,3 @@ int vfprintf(FILE *stream, const char *format, va_list args)
do_printf(&ctx, args);
return ctx.written_len;
}
-
-int putchar(int c)
-{
- return putc(c, stdout);
-}
-
-int putc(int c, FILE *stream)
-{
- return fputc(c, stream);
-}
-
-int fputc(int c, FILE *stream)
-{
- // TODO: a
- return c;
-}
-
-int puts(const char *str)
-{
- int res;
- res = fputs(str, stdout);
- if (res == EOF)
- return res;
- res = fputc('\n', stdout);
- if (res == EOF)
- return res;
- return 0;
-}
-
-int fputs(const char *str, FILE *stream)
-{
- int res;
- while (*str) {
- res = fputc(*str++, stream);
- if (res == EOF)
- return res;
- }
- return 0;
-}
diff --git a/user/lib/spawn.c b/user/lib/spawn.c
deleted file mode 100644
index 78b1a53..0000000
--- a/user/lib/spawn.c
+++ /dev/null
@@ -1,32 +0,0 @@
-#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);
-}
diff --git a/user/lib/syscall.S b/user/lib/syscall.S
index 46fcb89..fc1ab93 100644
--- a/user/lib/syscall.S
+++ b/user/lib/syscall.S
@@ -1,93 +1,26 @@
-/**
-** @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 ; \
+.macro SYSCALL name num
+ .align 8
+ .globl \name
+\name:
+ movq $\num, %rax
+ int $VEC_SYSCALL
ret
+.endm
-/*
-** "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
+SYSCALL exit SYS_exit
+SYSCALL waitpid SYS_waitpid
+SYSCALL fork SYS_fork
+SYSCALL exec SYS_exec
+SYSCALL read SYS_read
+SYSCALL write SYS_write
+SYSCALL getpid SYS_getpid
+SYSCALL getppid SYS_getppid
+SYSCALL gettime SYS_gettime
+SYSCALL getprio SYS_getprio
+SYSCALL setprio SYS_setprio
+SYSCALL kill SYS_kill
+SYSCALL sleep SYS_sleep
+SYSCALL brk SYS_brk
+SYSCALL sbrk SYS_sbrk
diff --git a/user/lib/timetostr.c b/user/lib/timetostr.c
deleted file mode 100644
index fa77362..0000000
--- a/user/lib/timetostr.c
+++ /dev/null
@@ -1,143 +0,0 @@
-#include <lib.h>
-#include <time.h>
-
-static char *ABB_WEEKDAY[7] = {
- "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
-};
-
-static char *FULL_WEEKDAY[7] = { "Sunday", "Monday", "Tuesday", "Wednesday",
- "Thursday", "Friday", "Saturady" };
-
-static char *ABB_MONTH[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
-
-static char *FULL_MONTH[12] = {
- "January", "Feburary", "March", "April", "May", "June",
- "July", "August", "September", "October", "November", "December"
-};
-
-static char *write_num(unsigned int num, unsigned int pad, char *buf, size_t n)
-{
- size_t digits = 1;
- unsigned int x = num;
-
- while (x /= 10, x > 0)
- digits++;
- if (pad == 0)
- pad = digits;
-
- for (size_t i = 0; i < pad; i++) {
- size_t digit;
- if (i >= digits) {
- digit = 0;
- } else {
- digit = num % 10;
- num /= 10;
- }
-
- if (pad - i - 1 >= n)
- continue;
- buf[pad - i - 1] = '0' + digit;
- }
-
- if (pad > n)
- pad = n;
-
- return buf + pad;
-}
-
-void timetostr(time_t *time, char *format, char *buf, size_t n)
-{
- char *index = buf;
- char c;
- int space;
-
- while (c = *format++, space = (buf + n) - index, c != '\0' && space > 0) {
- if (c != '%') {
- *index++ = c;
- continue;
- } else {
- c = *format++;
- }
-
- switch (c) {
- case '%':
- *index++ = '%';
- break;
- case 'a':
- index = strncpy(index, ABB_WEEKDAY[time->wday], space);
- break;
- case 'A':
- index = strncpy(index, FULL_WEEKDAY[time->wday], space);
- break;
- case 'b':
- case 'h':
- index = strncpy(index, ABB_MONTH[time->mon], space);
- break;
- case 'B':
- index = strncpy(index, FULL_MONTH[time->mon], space);
- break;
- case 'C':
- index = write_num(time->cen, 0, index, space);
- break;
- case 'd':
- index = write_num(time->mday, 2, index, space);
- break;
- case 'H':
- index = write_num(time->hour, 2, index, space);
- break;
- case 'I':
- index = write_num((time->hour + 12) % 12 + 1, 2, index, space);
- break;
- case 'j':
- index = write_num(time->yday, 3, index, space);
- break;
- case 'm':
- index = write_num(time->mon + 1, 2, index, space);
- break;
- case 'M':
- index = write_num(time->min, 2, index, space);
- break;
- case 'n':
- *index++ = '\n';
- break;
- case 'p':
- index = strncpy(index, time->hour > 11 ? "PM" : "AM", space);
- break;
- case 'P':
- index = strncpy(index, time->hour > 11 ? "pm" : "am", space);
- break;
- case 'q':
- index = write_num((time->mon + 3) / 3, 0, index, space);
- break;
- case 'S':
- index = write_num(time->sec, 2, index, space);
- break;
- case 't':
- *index++ = '\t';
- break;
- case 'u':
- index = write_num(((time->wday + 1) % 7) + 1, 0, index, space);
- break;
- case 'w':
- index = write_num(time->wday, 0, index, space);
- break;
- case 'y':
- index = write_num(time->yn, 2, index, space);
- break;
- case 'Y':
- index = write_num(time->year + 1900, 0, index, space);
- break;
- default: {
- char b[3] = { '%', c, '\0' };
- index = strncpy(index, b, space);
- break;
- }
- }
- }
-
- if (space < 1)
- buf[n - 1] = '\0';
- else
- *index = '\0';
-}
diff --git a/user/progABC.c b/user/progABC.c
deleted file mode 100644
index 0a4b299..0000000
--- a/user/progABC.c
+++ /dev/null
@@ -1,71 +0,0 @@
-#include <common.h>
-
-/**
-** User function main #1: exit, write
-**
-** Prints its ID, then loops N times delaying and printing, then exits.
-** Verifies the return byte count from each call to write().
-**
-** Invoked as: main1 x n
-** where x is the ID character
-** n is the iteration count
-*/
-
-USERMAIN(main)
-{
- int count = 30; // default iteration count
- char ch = '1'; // default character to print
- char buf[128]; // local char buffer
-
- // process the command-line arguments
- switch (argc) {
- case 3:
- count = str2int(argv[2], 10);
- // FALL THROUGH
- case 2:
- ch = argv[1][0];
- break;
- default:
- sprint(buf, "%s: argc %d, args: ", argv[0], argc);
- cwrites(buf);
- for (int i = 0; i <= argc; ++i) {
- sprint(buf, " %s", argv[argc] ? argv[argc] : "(null)");
- cwrites(buf);
- }
- cwrites("\n");
- }
-
- // announce our presence
- int n = swritech(ch);
- if (n != 1) {
- sprint(buf, "== %c, write #1 returned %d\n", ch, n);
- cwrites(buf);
- }
-
- // iterate and print the required number of other characters
- for (int i = 0; i < count; ++i) {
- DELAY(STD);
- n = swritech(ch);
- if (n != 1) {
- sprint(buf, "== %c, write #2 returned %d\n", ch, n);
- cwrites(buf);
- }
- }
-
- // all done!
- exit(0);
-
- // should never reach this code; if we do, something is
- // wrong with exit(), so we'll report it
-
- char msg[] = "*1*";
- msg[1] = ch;
- n = write(CHAN_SIO, msg, 3); /* shouldn't happen! */
- if (n != 3) {
- sprint(buf, "User %c, write #3 returned %d\n", ch, n);
- cwrites(buf);
- }
-
- // this should really get us out of here
- return (42);
-}
diff --git a/user/progDE.c b/user/progDE.c
deleted file mode 100644
index be2dd1b..0000000
--- a/user/progDE.c
+++ /dev/null
@@ -1,59 +0,0 @@
-#include <common.h>
-
-/**
-** User function main #2: write
-**
-** Prints its ID, then loops N times delaying and printing, then returns
-** without calling exit(). Verifies the return byte count from each call
-** to write().
-**
-** Invoked as: main2 x n
-** where x is the ID character
-** n is the iteration count
-*/
-
-USERMAIN(main)
-{
- int n;
- int count = 30; // default iteration count
- char ch = '2'; // default character to print
- char buf[128];
-
- // process the command-line arguments
- switch (argc) {
- case 3:
- count = str2int(argv[2], 10);
- // FALL THROUGH
- case 2:
- ch = argv[1][0];
- break;
- default:
- sprint(buf, "main2: argc %d, args: ", argc);
- cwrites(buf);
- for (int i = 0; i <= argc; ++i) {
- sprint(buf, " %s", argv[argc] ? argv[argc] : "(null)");
- cwrites(buf);
- }
- cwrites("\n");
- }
-
- // announce our presence
- n = swritech(ch);
- if (n != 1) {
- sprint(buf, "== %c, write #1 returned %d\n", ch, n);
- cwrites(buf);
- }
-
- // iterate and print the required number of other characters
- for (int i = 0; i < count; ++i) {
- DELAY(STD);
- n = swritech(ch);
- if (n != 1) {
- sprint(buf, "== %c, write #2 returned %d\n", ch, n);
- cwrites(buf);
- }
- }
-
- // all done!
- return (0);
-}
diff --git a/user/progFG.c b/user/progFG.c
deleted file mode 100644
index 8dddd56..0000000
--- a/user/progFG.c
+++ /dev/null
@@ -1,59 +0,0 @@
-#include <common.h>
-
-/**
-** User function main #3: exit, sleep, write
-**
-** Prints its ID, then loops N times sleeping and printing, then exits.
-**
-** Invoked as: main3 x n s
-** where x is the ID character
-** n is the iteration count
-** s is the sleep time in seconds
-*/
-
-USERMAIN(main)
-{
- char ch = '3'; // default character to print
- int nap = 10; // default sleep time
- int count = 30; // iteration count
- char buf[128];
-
- // process the command-line arguments
- switch (argc) {
- case 4:
- nap = str2int(argv[3], 10);
- // FALL THROUGH
- case 3:
- count = str2int(argv[2], 10);
- // FALL THROUGH
- case 2:
- ch = argv[1][0];
- break;
- default:
- sprint(buf, "main3: argc %d, args: ", argc);
- cwrites(buf);
- for (int i = 0; i <= argc; ++i) {
- sprint(buf, " %s", argv[argc] ? argv[argc] : "(null)");
- cwrites(buf);
- }
- cwrites("\n");
- }
-
- // announce our presence
- int n = swritech(ch);
- if (n != 1) {
- sprint(buf, "=== %c, write #1 returned %d\n", ch, n);
- cwrites(buf);
- }
-
- write(CHAN_SIO, &ch, 1);
-
- for (int i = 0; i < count; ++i) {
- sleep(SEC_TO_MS(nap));
- write(CHAN_SIO, &ch, 1);
- }
-
- exit(0);
-
- return (42); // shut the compiler up!
-}
diff --git a/user/progH.c b/user/progH.c
deleted file mode 100644
index 0cef860..0000000
--- a/user/progH.c
+++ /dev/null
@@ -1,68 +0,0 @@
-#include <common.h>
-
-/**
-** User function H: exit, fork, exec, sleep, write
-**
-** Prints its ID, then spawns 'n' children; exits before they terminate.
-**
-** Invoked as: userH x n
-** where x is the ID character
-** n is the number of children to spawn
-*/
-
-USERMAIN(main)
-{
- int32_t ret = 0; // return value
- int count = 5; // child count
- char ch = 'h'; // default character to print
- char buf[128];
- int whom;
-
- // process the argument(s)
- switch (argc) {
- case 3:
- count = str2int(argv[2], 10);
- // FALL THROUGH
- case 2:
- ch = argv[1][0];
- break;
- default:
- sprint(buf, "userH: argc %d, args: ", argc);
- cwrites(buf);
- for (int i = 0; i <= argc; ++i) {
- sprint(buf, " %s", argv[argc] ? argv[argc] : "(null)");
- cwrites(buf);
- }
- cwrites("\n");
- }
-
- // announce our presence
- swritech(ch);
-
- // we spawn user Z and then exit before it can terminate
- // userZ 'Z' 10
-
- char *argsz[] = { "userZ", "Z", "10", NULL };
-
- for (int i = 0; i < count; ++i) {
- // spawn a child
- whom = spawn(ProgZ, argsz);
-
- // our exit status is the number of failed spawn() calls
- if (whom < 0) {
- sprint(buf, "!! %c spawn() failed, returned %d\n", ch, whom);
- cwrites(buf);
- ret += 1;
- }
- }
-
- // yield the CPU so that our child(ren) can run
- sleep(0);
-
- // announce our departure
- swritech(ch);
-
- exit(ret);
-
- return (42); // shut the compiler up!
-}
diff --git a/user/progI.c b/user/progI.c
deleted file mode 100644
index a1988e3..0000000
--- a/user/progI.c
+++ /dev/null
@@ -1,107 +0,0 @@
-#include <common.h>
-
-#ifndef MAX_CHILDREN
-#define MAX_CHILDREN 50
-#endif
-
-/**
-** User function I: exit, fork, exec, kill, sleep, waitpid, write
-**
-** Reports, then loops spawing userW, sleeps, kills two children, then
-** loops checking the status of all its children
-**
-** Invoked as: userI [ x [ n ] ]
-** where x is the ID character (defaults to 'i')
-** n is the number of children to spawn (defaults to 5)
-*/
-
-USERMAIN(main)
-{
- int count = 5; // default child count
- char ch = 'i'; // default character to print
- int nap = 5; // nap time
- char buf[128];
- char ch2[] = "*?*";
- uint_t children[MAX_CHILDREN];
- int nkids = 0;
-
- // process the command-line arguments
- switch (argc) {
- case 3:
- count = str2int(argv[2], 10);
- // FALL THROUGH
- case 2:
- ch = argv[1][0];
- break;
- case 1: // just use the defaults
- break;
- default:
- sprint(buf, "userI: argc %d, args: ", argc);
- cwrites(buf);
- for (int i = 0; i <= argc; ++i) {
- sprint(buf, " %s", argv[argc] ? argv[argc] : "(null)");
- cwrites(buf);
- }
- cwrites("\n");
- }
-
- // secondary output (for indicating errors)
- ch2[1] = ch;
-
- // announce our presence
- write(CHAN_SIO, &ch, 1);
-
- // set up the argument vector
- // we run: userW 10 5
-
- char *argsw[] = { "userW", "W", "10", "5", NULL };
-
- for (int i = 0; i < count; ++i) {
- int whom = spawn(ProgW, argsw);
- if (whom < 0) {
- swrites(ch2);
- } else {
- swritech(ch);
- children[nkids++] = whom;
- }
- }
-
- // let the children start
- sleep(SEC_TO_MS(nap));
-
- // kill two of them
- int32_t status = kill(children[1]);
- if (status) {
- sprint(buf, "!! %c: kill(%d) status %d\n", ch, children[1], status);
- cwrites(buf);
- children[1] = -42;
- }
- status = kill(children[3]);
- if (status) {
- sprint(buf, "!! %c: kill(%d) status %d\n", ch, children[3], status);
- cwrites(buf);
- children[3] = -42;
- }
-
- // collect child information
- while (1) {
- int n = waitpid(0, NULL);
- if (n == E_NO_CHILDREN) {
- // all done!
- break;
- }
- for (int i = 0; i < count; ++i) {
- if (children[i] == n) {
- sprint(buf, "== %c: child %d (%d)\n", ch, i, children[i]);
- cwrites(buf);
- }
- }
- sleep(SEC_TO_MS(nap));
- };
-
- // let init() clean up after us!
-
- exit(0);
-
- return (42); // shut the compiler up!
-}
diff --git a/user/progJ.c b/user/progJ.c
deleted file mode 100644
index a1e6310..0000000
--- a/user/progJ.c
+++ /dev/null
@@ -1,55 +0,0 @@
-#include <common.h>
-
-/**
-** User function J: exit, fork, exec, write
-**
-** Reports, tries to spawn lots of children, then exits
-**
-** Invoked as: userJ x [ n ]
-** where x is the ID character
-** n is the number of children to spawn (defaults to 2 * N_PROCS)
-*/
-
-USERMAIN(main)
-{
- int count = 2 * N_PROCS; // number of children to spawn
- char ch = 'j'; // default character to print
- char buf[128];
-
- // process the command-line arguments
- switch (argc) {
- case 3:
- count = str2int(argv[2], 10);
- // FALL THROUGH
- case 2:
- ch = argv[1][0];
- break;
- default:
- sprint(buf, "userJ: argc %d, args: ", argc);
- cwrites(buf);
- for (int i = 0; i <= argc; ++i) {
- sprint(buf, " %s", argv[argc] ? argv[argc] : "(null)");
- cwrites(buf);
- }
- cwrites("\n");
- }
-
- // announce our presence
- write(CHAN_SIO, &ch, 1);
-
- // set up the command-line arguments
- char *argsy[] = { "userY", "Y", "10", NULL };
-
- for (int i = 0; i < count; ++i) {
- int whom = spawn(ProgY, argsy);
- if (whom < 0) {
- write(CHAN_SIO, "!j!", 3);
- } else {
- write(CHAN_SIO, &ch, 1);
- }
- }
-
- exit(0);
-
- return (42); // shut the compiler up!
-}
diff --git a/user/progKL.c b/user/progKL.c
deleted file mode 100644
index aa9f039..0000000
--- a/user/progKL.c
+++ /dev/null
@@ -1,63 +0,0 @@
-#include <common.h>
-
-/**
-** User function main #4: exit, fork, exec, sleep, write
-**
-** Loops, spawning N copies of userX and sleeping between spawns.
-**
-** Invoked as: main4 x n
-** where x is the ID character
-** n is the iteration count (defaults to 5)
-*/
-
-USERMAIN(main)
-{
- int count = 5; // default iteration count
- char ch = '4'; // default character to print
- int nap = 30; // nap time
- char msg2[] = "*4*"; // "error" message to print
- char buf[32];
-
- // process the command-line arguments
- switch (argc) {
- case 3:
- count = str2int(argv[2], 10);
- // FALL THROUGH
- case 2:
- ch = argv[1][0];
- break;
- default:
- sprint(buf, "main4: argc %d, args: ", argc);
- cwrites(buf);
- for (int i = 0; i <= argc; ++i) {
- sprint(buf, " %s", argv[argc] ? argv[argc] : "(null)");
- cwrites(buf);
- }
- cwrites("\n");
- }
-
- // announce our presence
- write(CHAN_SIO, &ch, 1);
-
- // argument vector for the processes we will spawn
- char *arglist[] = { "userX", "X", buf, NULL };
-
- for (int i = 0; i < count; ++i) {
- write(CHAN_SIO, &ch, 1);
-
- // second argument to X is 100 plus the iteration number
- sprint(buf, "%d", 100 + i);
- int whom = spawn(ProgX, arglist);
- if (whom < 0) {
- swrites(msg2);
- } else {
- write(CHAN_SIO, &ch, 1);
- }
-
- sleep(SEC_TO_MS(nap));
- }
-
- exit(0);
-
- return (42); // shut the compiler up!
-}
diff --git a/user/progMN.c b/user/progMN.c
deleted file mode 100644
index d2dbe9b..0000000
--- a/user/progMN.c
+++ /dev/null
@@ -1,76 +0,0 @@
-#include <common.h>
-
-/**
-** User function main #5: exit, fork, exec, write
-**
-** Iterates spawning copies of userW (and possibly userZ), reporting
-** their PIDs as it goes.
-**
-** Invoked as: main5 x n b
-** where x is the ID character
-** n is the iteration count
-** b is the w&z boolean
-*/
-
-USERMAIN(main)
-{
- int count = 5; // default iteration count
- char ch = '5'; // default character to print
- int alsoZ = 0; // also do userZ?
- char msgw[] = "*5w*";
- char msgz[] = "*5z*";
- char buf[128];
-
- // process the command-line arguments
- switch (argc) {
- case 4:
- alsoZ = argv[3][0] == 't';
- // FALL THROUGH
- case 3:
- count = str2int(argv[2], 10);
- // FALL THROUGH
- case 2:
- ch = argv[1][0];
- break;
- default:
- sprint(buf, "main5: argc %d, args: ", argc);
- cwrites(buf);
- for (int i = 0; i <= argc; ++i) {
- sprint(buf, " %s", argv[argc] ? argv[argc] : "(null)");
- cwrites(buf);
- }
- cwrites("\n");
- }
-
- // update the extra message strings
- msgw[1] = msgz[1] = ch;
-
- // announce our presence
- write(CHAN_SIO, &ch, 1);
-
- // set up the argument vector(s)
-
- // W: 15 iterations, 5-second sleep
- char *argsw[] = { "userW", "W", "15", "5", NULL };
-
- // Z: 15 iterations
- char *argsz[] = { "userZ", "Z", "15", NULL };
-
- for (int i = 0; i < count; ++i) {
- write(CHAN_SIO, &ch, 1);
- int whom = spawn(ProgW, argsw);
- if (whom < 1) {
- swrites(msgw);
- }
- if (alsoZ) {
- whom = spawn(ProgZ, argsz);
- if (whom < 1) {
- swrites(msgz);
- }
- }
- }
-
- exit(0);
-
- return (42); // shut the compiler up!
-}
diff --git a/user/progP.c b/user/progP.c
deleted file mode 100644
index 8909599..0000000
--- a/user/progP.c
+++ /dev/null
@@ -1,55 +0,0 @@
-#include "common.h"
-
-/**
-** User function P: exit, sleep, write, gettime
-**
-** Reports itself, then loops reporting itself
-**
-** Invoked as: userP x [ n [ t ] ]
-** where x is the ID character
-** n is the iteration count (defaults to 3)
-** t is the sleep time (defaults to 2 seconds)
-*/
-
-USERMAIN(main)
-{
- int count = 3; // default iteration count
- char ch = 'p'; // default character to print
- int nap = 2; // nap time
- char buf[128];
-
- // process the command-line arguments
- switch (argc) {
- case 4:
- nap = str2int(argv[3], 10);
- // FALL THROUGH
- case 3:
- count = str2int(argv[2], 10);
- // FALL THROUGH
- case 2:
- ch = argv[1][0];
- break;
- default:
- sprint(buf, "userP: argc %d, args: ", argc);
- cwrites(buf);
- for (int i = 0; i <= argc; ++i) {
- sprint(buf, " %s", argv[argc] ? argv[argc] : "(null)");
- cwrites(buf);
- }
- cwrites("\n");
- }
-
- // announce our presence
- uint32_t now = gettime();
- sprint(buf, " P@%u", now);
- swrites(buf);
-
- for (int i = 0; i < count; ++i) {
- sleep(SEC_TO_MS(nap));
- write(CHAN_SIO, &ch, 1);
- }
-
- exit(0);
-
- return (42); // shut the compiler up!
-}
diff --git a/user/progQ.c b/user/progQ.c
deleted file mode 100644
index 660c2d6..0000000
--- a/user/progQ.c
+++ /dev/null
@@ -1,45 +0,0 @@
-#include <common.h>
-
-/**
-** User function Q: exit, write, bogus
-**
-** Reports itself, then tries to execute a bogus system call
-**
-** Invoked as: userQ x
-** where x is the ID character
-*/
-
-USERMAIN(main)
-{
- char ch = 'q'; // default character to print
- char buf[128];
-
- // process the command-line arguments
- switch (argc) {
- case 2:
- ch = argv[1][0];
- break;
- default:
- sprint(buf, "userQ: argc %d, args: ", argc);
- cwrites(buf);
- for (int i = 0; i <= argc; ++i) {
- sprint(buf, " %s", argv[argc] ? argv[argc] : "(null)");
- cwrites(buf);
- }
- cwrites("\n");
- }
-
- // announce our presence
- write(CHAN_SIO, &ch, 1);
-
- // try something weird
- bogus();
-
- // should not have come back here!
- sprint(buf, "!!!!! %c returned from bogus syscall!?!?!\n", ch);
- cwrites(buf);
-
- exit(1);
-
- return (42); // shut the compiler up!
-}
diff --git a/user/progR.c b/user/progR.c
deleted file mode 100644
index 672318b..0000000
--- a/user/progR.c
+++ /dev/null
@@ -1,102 +0,0 @@
-#include <common.h>
-
-/**
-** User function R: exit, sleep, write, fork, getpid, getppid,
-**
-** Reports itself and its sequence number, along with its PID and
-** its parent's PID. It then delays, forks, delays, reports again,
-** and exits.
-**
-** Invoked as: userR x n [ s ]
-** where x is the ID character
-** n is the sequence number of the initial incarnation
-** s is the initial delay time (defaults to 10)
-*/
-
-USERMAIN(main)
-{
- char ch = 'r'; // default character to print
- int delay = 10; // initial delay count
- int seq = 99; // my sequence number
- char buf[128];
-
- // process the command-line arguments
- switch (argc) {
- case 4:
- delay = str2int(argv[3], 10);
- // FALL THROUGH
- case 3:
- seq = str2int(argv[2], 10);
- // FALL THROUGH
- case 2:
- ch = argv[1][0];
- break;
- default:
- sprint(buf, "userR: argc %d, args: ", argc);
- cwrites(buf);
- for (int i = 0; i <= argc; ++i) {
- sprint(buf, " %s", argv[argc] ? argv[argc] : "(null)");
- cwrites(buf);
- }
- cwrites("\n");
- }
-
- /*
- ** C oddity: a label cannot immediately precede a declaration.
- **
- ** Declarations are not considered "statements" in C. Prior to
- ** C99, all declarations had to precede any statements inside a
- ** block. Labels can only appear before statements. C99 allowed
- ** the mixing of declarations and statements, but did not relax
- ** the requirement that labels precede only statements.
- **
- ** That's why the declarations of these variables occur before the
- ** label, but their initializations occur after the label.
- **
- ** As the PSA says on TV, "The more you know..." :-)
- */
-
- int32_t pid;
- int32_t ppid;
-
-restart:
-
- // announce our presence
- pid = getpid();
- ppid = getppid();
-
- sprint(buf, " %c[%d,%d,%d]", ch, seq, pid, ppid);
- swrites(buf);
-
- sleep(SEC_TO_MS(delay));
-
- // create the next child in sequence
- if (seq < 5) {
- ++seq;
- int32_t n = fork();
- switch (n) {
- case -1:
- // failure?
- sprint(buf, "** R[%d] fork code %d\n", pid, n);
- cwrites(buf);
- break;
- case 0:
- // child
- goto restart;
- default:
- // parent
- --seq;
- sleep(SEC_TO_MS(delay));
- }
- }
-
- // final report - PPID may change, but PID and seq shouldn't
- pid = getpid();
- ppid = getppid();
- sprint(buf, " %c[%d,%d,%d]", ch, seq, pid, ppid);
- swrites(buf);
-
- exit(0);
-
- return (42); // shut the compiler up!
-}
diff --git a/user/progS.c b/user/progS.c
deleted file mode 100644
index c8e0e60..0000000
--- a/user/progS.c
+++ /dev/null
@@ -1,53 +0,0 @@
-#include <common.h>
-
-/**
-** User function S: exit, sleep, write
-**
-** Reports itself, then loops forever, sleeping on each iteration
-**
-** Invoked as: userS x [ s ]
-** where x is the ID character
-** s is the sleep time (defaults to 20)
-*/
-
-USERMAIN(main)
-{
- char ch = 's'; // default character to print
- int nap = 20; // nap time
- char buf[128];
-
- // process the command-line arguments
- switch (argc) {
- case 3:
- nap = str2int(argv[2], 10);
- // FALL THROUGH
- case 2:
- ch = argv[1][0];
- break;
- default:
- sprint(buf, "userS: argc %d, args: ", argc);
- cwrites(buf);
- for (int i = 0; i <= argc; ++i) {
- sprint(buf, " %s", argv[argc] ? argv[argc] : "(null)");
- cwrites(buf);
- }
- cwrites("\n");
- }
-
- // announce our presence
- write(CHAN_SIO, &ch, 1);
-
- sprint(buf, "userS sleeping %d(%d)\n", nap, SEC_TO_MS(nap));
- cwrites(buf);
-
- for (;;) {
- sleep(SEC_TO_MS(nap));
- write(CHAN_SIO, &ch, 1);
- }
-
- sprint(buf, "!! %c exiting!?!?!?\n", ch);
- cwrites(buf);
- exit(1);
-
- return (42); // shut the compiler up!
-}
diff --git a/user/progTUV.c b/user/progTUV.c
deleted file mode 100644
index 8e69a66..0000000
--- a/user/progTUV.c
+++ /dev/null
@@ -1,167 +0,0 @@
-#include <common.h>
-
-/**
-** User function main #6: exit, fork, exec, kill, waitpid, sleep, write
-**
-** Reports, then loops spawing userW, sleeps, then waits for or kills
-** all its children.
-**
-** Invoked as: main6 x c b
-** where x is the ID character
-** c is the child count
-** b is wait/kill indicator ('w', 'W', or 'k')
-*/
-
-#ifndef MAX_CHILDREN
-#define MAX_CHILDREN 50
-#endif
-
-USERMAIN(main)
-{
- int count = 3; // default child count
- char ch = '6'; // default character to print
- int nap = 8; // nap time
- bool_t waiting = true; // default is waiting by PID
- bool_t bypid = true;
- char buf[128];
- uint_t children[MAX_CHILDREN];
- int nkids = 0;
- char ch2[] = "*?*";
-
- // process the command-line arguments
- switch (argc) {
- case 4:
- waiting = argv[3][0] != 'k'; // 'w'/'W' -> wait, else -> kill
- bypid = argv[3][0] != 'w'; // 'W'/'k' -> by PID
- // FALL THROUGH
- case 3:
- count = str2int(argv[2], 10);
- // FALL THROUGH
- case 2:
- ch = argv[1][0];
- break;
- default:
- sprint(buf, "main6: argc %d, args: ", argc);
- cwrites(buf);
- for (int i = 0; i <= argc; ++i) {
- sprint(buf, " %s", argv[argc] ? argv[argc] : "(null)");
- cwrites(buf);
- }
- cwrites("\n");
- }
-
- // fix the secondary output message (for indicating errors)
- ch2[1] = ch;
-
- // announce our presence
- write(CHAN_SIO, &ch, 1);
-
- // set up the argument vector
- char *argsw[] = { "userW", "W", "10", "5", NULL };
-
- for (int i = 0; i < count; ++i) {
- int whom = spawn(ProgW, argsw);
- if (whom < 0) {
- swrites(ch2);
- } else {
- children[nkids++] = whom;
- }
- }
-
- // let the children start
- sleep(SEC_TO_MS(nap));
-
- // collect exit status information
-
- // current child index
- int n = 0;
-
- do {
- int this;
- int32_t status;
-
- // are we waiting for or killing it?
- if (waiting) {
- this = waitpid(bypid ? children[n] : 0, &status);
- } else {
- // always by PID
- this = kill(children[n]);
- }
-
- // what was the result?
- if (this < SUCCESS) {
- // uh-oh - something went wrong
-
- // "no children" means we're all done
- if (this != E_NO_CHILDREN) {
- if (waiting) {
- sprint(buf, "!! %c: waitpid(%d) status %d\n", ch,
- bypid ? children[n] : 0, this);
- } else {
- sprint(buf, "!! %c: kill(%d) status %d\n", ch, children[n],
- this);
- }
- } else {
- sprint(buf, "!! %c: no children\n", ch);
- }
-
- // regardless, we're outta here
- break;
-
- } else {
- // locate the child
- int ix = -1;
-
- // were we looking by PID?
- if (bypid) {
- // we should have just gotten the one we were looking for
- if (this != children[n]) {
- // uh-oh
- sprint(buf, "** %c: wait/kill PID %d, got %d\n", ch,
- children[n], this);
- cwrites(buf);
- } else {
- ix = n;
- }
- }
-
- // either not looking by PID, or the lookup failed somehow
- if (ix < 0) {
- int i;
- for (i = 0; i < nkids; ++i) {
- if (children[i] == this) {
- ix = i;
- break;
- }
- }
- }
-
- // if ix == -1, the PID we received isn't in our list of children
-
- if (ix < 0) {
- // didn't find an entry for this PID???
- sprint(buf, "!! %c: child PID %d term, NOT FOUND\n", ch, this);
-
- } else {
- // found this PID in our list of children
- if (ix != n) {
- // ... but it's out of sequence
- sprint(buf, "== %c: child %d (%d,%d) status %d\n", ch, ix,
- n, this, status);
- } else {
- sprint(buf, "== %c: child %d (%d) status %d\n", ch, ix,
- this, status);
- }
- }
- }
-
- cwrites(buf);
-
- ++n;
-
- } while (n < nkids);
-
- exit(0);
-
- return (42); // shut the compiler up!
-}
diff --git a/user/progW.c b/user/progW.c
deleted file mode 100644
index b71ec23..0000000
--- a/user/progW.c
+++ /dev/null
@@ -1,61 +0,0 @@
-#include <common.h>
-
-/**
-** User function W: exit, sleep, write, getpid, gettime
-**
-** Reports its presence, then iterates 'n' times printing identifying
-** information and sleeping, before exiting.
-**
-** Invoked as: userW x [ n [ s ] ]
-** where x is the ID character
-** n is the iteration count (defaults to 20)
-** s is the sleep time (defaults to 3 seconds)
-*/
-
-USERMAIN(main)
-{
- int count = 20; // default iteration count
- char ch = 'w'; // default character to print
- int nap = 3; // nap length
- char buf[128];
-
- // process the command-line arguments
- switch (argc) {
- case 4:
- nap = str2int(argv[3], 10);
- // FALL THROUGH
- case 3:
- count = str2int(argv[2], 10);
- // FALL THROUGH
- case 2:
- ch = argv[1][0];
- break;
- default:
- sprint(buf, "userW: argc %d, args: ", argc);
- cwrites(buf);
- for (int i = 0; i <= argc; ++i) {
- sprint(buf, " %s", argv[argc] ? argv[argc] : "(null)");
- cwrites(buf);
- }
- cwrites("\n");
- }
-
- // announce our presence
- int pid = getpid();
- uint32_t now = gettime();
- sprint(buf, " %c[%d,%u]", ch, pid, now);
- swrites(buf);
-
- write(CHAN_SIO, &ch, 1);
-
- for (int i = 0; i < count; ++i) {
- now = gettime();
- sprint(buf, " %c[%d,%u] ", ch, pid, now);
- swrites(buf);
- sleep(SEC_TO_MS(nap));
- }
-
- exit(0);
-
- return (42); // shut the compiler up!
-}
diff --git a/user/progX.c b/user/progX.c
deleted file mode 100644
index 782823e..0000000
--- a/user/progX.c
+++ /dev/null
@@ -1,51 +0,0 @@
-#include <common.h>
-
-/**
-** User function X: exit, write, getpid
-**
-** Prints its PID at start and exit, iterates printing its character
-** N times, and exits with a status of 12.
-**
-** Invoked as: userX x n
-** where x is the ID character
-** n is the iteration count
-*/
-
-USERMAIN(main)
-{
- int count = 20; // iteration count
- char ch = 'x'; // default character to print
- char buf[128];
-
- // process the command-line arguments
- switch (argc) {
- case 3:
- count = str2int(argv[2], 10);
- // FALL THROUGH
- case 2:
- ch = argv[1][0];
- break;
- default:
- sprint(buf, "userX: argc %d, args: ", argc);
- cwrites(buf);
- for (int i = 0; i <= argc; ++i) {
- sprint(buf, " %s", argv[argc] ? argv[argc] : "(null)");
- cwrites(buf);
- }
- cwrites("\n");
- }
-
- // announce our presence
- int pid = getpid();
- sprint(buf, " %c[%d]", ch, pid);
- swrites(buf);
-
- for (int i = 0; i < count; ++i) {
- swrites(buf);
- DELAY(STD);
- }
-
- exit(12);
-
- return (42); // shut the compiler up!
-}
diff --git a/user/progY.c b/user/progY.c
deleted file mode 100644
index 43313bd..0000000
--- a/user/progY.c
+++ /dev/null
@@ -1,52 +0,0 @@
-#include <common.h>
-
-/**
-** User function Y: exit, sleep, write, getpid
-**
-** Reports its PID, then iterates N times printing 'Yx' and
-** sleeping for one second, then exits.
-**
-** Invoked as: userY x [ n ]
-** where x is the ID character
-** n is the iteration count (defaults to 10)
-*/
-
-USERMAIN(main)
-{
- int count = 10; // default iteration count
- char ch = 'y'; // default character to print
- char buf[128];
-
- // process the command-line arguments
- switch (argc) {
- case 3:
- count = str2int(argv[2], 10);
- // FALL THROUGH
- case 2:
- ch = argv[1][0];
- break;
- default:
- sprint(buf, "?: argc %d, args: ", argc);
- cwrites(buf);
- for (int i = 0; i <= argc; ++i) {
- sprint(buf, " %s", argv[argc] ? argv[argc] : "(null)");
- cwrites(buf);
- }
- cwrites("\n");
- }
-
- // report our presence
- int pid = getpid();
- sprint(buf, " %c[%d]", ch, pid);
- swrites(buf);
-
- for (int i = 0; i < count; ++i) {
- swrites(buf);
- DELAY(STD);
- sleep(SEC_TO_MS(1));
- }
-
- exit(0);
-
- return (42); // shut the compiler up!
-}
diff --git a/user/progZ.c b/user/progZ.c
deleted file mode 100644
index fdeb888..0000000
--- a/user/progZ.c
+++ /dev/null
@@ -1,60 +0,0 @@
-#include <common.h>
-
-/**
-** User function Z: exit, sleep, write, getpid
-**
-** Prints its ID, then records PID and PPID, loops printing its ID,
-** and finally re-gets PPID for comparison. Yields after every second
-** ID print in the loop.
-**
-** This code is used as a handy "spawn me" test routine; it is spawned
-** by several of the standard test processes.
-**
-** Invoked as: userZ x [ n ]
-** where x is the ID character
-** n is the iteration count (defaults to 10)
-*/
-
-USERMAIN(main)
-{
- int count = 10; // default iteration count
- char ch = 'z'; // default character to print
- char buf[128];
-
- // process the command-line arguments
- switch (argc) {
- case 3:
- count = str2int(argv[2], 10);
- // FALL THROUGH
- case 2:
- ch = argv[1][0];
- break;
- default:
- sprint(buf, "?: argc %d, args: ", argc);
- cwrites(buf);
- for (int i = 0; i <= argc; ++i) {
- sprint(buf, " %s", argv[argc] ? argv[argc] : "(null)");
- cwrites(buf);
- }
- cwrites("\n");
- }
-
- // announce our presence
- int pid = getpid();
- sprint(buf, " %c[%d]", ch, pid);
- swrites(buf);
-
- // iterate for a while; occasionally yield the CPU
- for (int i = 0; i < count; ++i) {
- sprint(buf, " %c[%d]", ch, i);
- swrites(buf);
- DELAY(STD);
- if (i & 1) {
- sleep(0);
- }
- }
-
- exit(0);
-
- return (42); // shut the compiler up!
-}
diff --git a/user/shell.c b/user/shell.c
deleted file mode 100644
index f8c13cd..0000000
--- a/user/shell.c
+++ /dev/null
@@ -1,346 +0,0 @@
-#include <common.h>
-
-// should we keep going?
-static bool_t time_to_stop = false;
-
-// number of spawned but uncollected children
-static int children = 0;
-
-/*
-** For the test programs in the baseline system, command-line arguments
-** follow these rules. The first two entries are as follows:
-**
-** argv[0] the name used to "invoke" this process
-** argv[1] the "character to print" (identifies the process)
-**
-** Most user programs have one or more additional arguments.
-**
-** See the comment at the beginning of each user-code source file for
-** information on the argument list that code expects.
-*/
-
-/*
-** "Spawn table" process entry. Similar to that in init.c,
-** except this one has no place to store the PID of the child.
-*/
-typedef struct proc_s {
- uint_t index; // process table index
- int8_t prio; // process priority
- char select[3]; // identifying character, NUL, extra
- char *args[N_ARGS]; // argument vector strings
-} proc_t;
-
-/*
-** Create a spawn table entry for a process with a string literal
-** as its argument buffer. We rely on the fact that the C standard
-** ensures our array of pointers will be filled out with NULLs
-*/
-#define PROCENT(e, p, s, ...) \
- { \
- e, p, s, \
- { \
- __VA_ARGS__, NULL \
- } \
- }
-
-// sentinel value for the end of the table - must be updated
-// if you have more than 90,210 user programs in the table
-#define TBLEND 90210
-
-/*
-** The spawn table contains entries for processes that are started
-** by the shell.
-*/
-static proc_t spawn_table[] = {
-
-// Users A-C each run ProgABC, which loops printing its character
-#if defined(SPAWN_A)
- PROCENT(ProgABC, PRIO_STD, "A", "userA", "A", "30"),
-#endif
-#if defined(SPAWN_B)
- PROCENT(ProgABC, PRIO_STD, "B", "userB", "B", "30"),
-#endif
-#if defined(SPAWN_C)
- PROCENT(ProgABC, PRIO_STD, "C", "userC", "C", "30"),
-#endif
-
-// Users D and E run ProgDE, which is like ProgABC but doesn't exit()
-#if defined(SPAWN_D)
- PROCENT(ProgDE, PRIO_STD, "D", "userD", "D", "20"),
-#endif
-#if defined(SPAWN_E)
- PROCENT(ProgDE, PRIO_STD, "E", "userE", "E", "20"),
-#endif
-
-// Users F and G run ProgFG, which sleeps between write() calls
-#if defined(SPAWN_F)
- PROCENT(ProgFG, PRIO_STD, "F", "userF", "F", "20"),
-#endif
-#if defined(SPAWN_G)
- PROCENT(ProgFG, PRIO_STD, "G", "userG", "G", "10"),
-#endif
-
-// User H tests reparenting of orphaned children
-#if defined(SPAWN_H)
- PROCENT(ProgH, PRIO_STD, "H", "userH", "H", "4"),
-#endif
-
-// User I spawns several children, kills one, and waits for all
-#if defined(SPAWN_I)
- PROCENT(ProgI, PRIO_STD, "I", "userI", "I"),
-#endif
-
-// User J tries to spawn 2 * N_PROCS children
-#if defined(SPAWN_J)
- PROCENT(ProgJ, PRIO_STD, "J", "userJ", "J"),
-#endif
-
-// Users K and L iterate spawning userX and sleeping
-#if defined(SPAWN_K)
- PROCENT(ProgKL, PRIO_STD, "K", "userK", "K", "8"),
-#endif
-#if defined(SPAWN_L)
- PROCENT(ProgKL, PRIO_STD, "L", "userL", "L", "5"),
-#endif
-
-// Users M and N spawn copies of userW and userZ via ProgMN
-#if defined(SPAWN_M)
- PROCENT(ProgMN, PRIO_STD, "M", "userM", "M", "5", "f"),
-#endif
-#if defined(SPAWN_N)
- PROCENT(ProgMN, PRIO_STD, "N", "userN", "N", "5", "t"),
-#endif
-
-// There is no user O
-
-// User P iterates, reporting system time and stats, and sleeping
-#if defined(SPAWN_P)
- PROCENT(ProgP, PRIO_STD, "P", "userP", "P", "3", "2"),
-#endif
-
-// User Q tries to execute a bad system call
-#if defined(SPAWN_Q)
- PROCENT(ProgQ, PRIO_STD, "Q", "userQ", "Q"),
-#endif
-
-// User R reports its PID, PPID, and sequence number; it
-// calls fork() but not exec(), with each child getting the
-// next sequence number, to a total of five copies
-#if defined(SPAWN_R)
- PROCENT(ProgR, PRIO_STD, "R", "userR", "R", "20", "1"),
-#endif
-
-// User S loops forever, sleeping 13 sec. on each iteration
-#if defined(SPAWN_S)
- PROCENT(ProgS, PRIO_STD, "S", "userS", "S", "13"),
-#endif
-
-// Users T-V run ProgTUV(); they spawn copies of userW
-// User T waits for any child
-// User U waits for each child by PID
-// User V kills each child
-#if defined(SPAWN_T)
- PROCENT(ProgTUV, PRIO_STD, "T", "userT", "T", "6", "w"),
-#endif
-#if defined(SPAWN_U)
- PROCENT(ProgTUV, PRIO_STD, "U", "userU", "U", "6", "W"),
-#endif
-#if defined(SPAWN_V)
- PROCENT(ProgTUV, PRIO_STD, "V", "userV", "V", "6", "k"),
-#endif
-
- // a dummy entry to use as a sentinel
- { TBLEND }
-
- // these processes are spawned by the ones above, and are never
- // spawned directly.
-
- // PROCENT( ProgW, PRIO_STD, "?", "userW", "W", "20", "3" ),
- // PROCENT( ProgX, PRIO_STD, "?", "userX", "X", "20" ),
- // PROCENT( ProgY, PRIO_STD, "?", "userY", "Y", "10" ),
- // PROCENT( ProgZ, PRIO_STD, "?", "userZ", "Z", "10" )
-};
-
-/*
-** usage function
-*/
-static void usage(void)
-{
- swrites("\nTests - run with '@x', where 'x' is one or more of:\n ");
- proc_t *p = spawn_table;
- while (p->index != TBLEND) {
- swritech(' ');
- swritech(p->select[0]);
- }
- swrites("\nOther commands: @* (all), @h (help), @x (exit)\n");
-}
-
-/*
-** run a program from the program table, or a builtin command
-*/
-static int run(char which)
-{
- char buf[128];
- register proc_t *p;
-
- if (which == 'h') {
- // builtin "help" command
- usage();
-
- } else if (which == 'x') {
- // builtin "exit" command
- time_to_stop = true;
-
- } else if (which == '*') {
- // torture test! run everything!
- for (p = spawn_table; p->index != TBLEND; ++p) {
- int status = spawn(p->index, p->args);
- if (status > 0) {
- ++children;
- }
- }
-
- } else {
- // must be a single test; find and run it
- for (p = spawn_table; p->index != TBLEND; ++p) {
- if (p->select[0] == which) {
- // found it!
- int status = spawn(p->index, p->args);
- if (status > 0) {
- ++children;
- }
- return status;
- }
- }
-
- // uh-oh, made it through the table without finding the program
- sprint(buf, "shell: unknown cmd '%c'\n", which);
- swrites(buf);
- usage();
- }
-
- return 0;
-}
-
-/**
-** edit - perform any command-line editing we need to do
-**
-** @param line Input line buffer
-** @param n Number of valid bytes in the buffer
-*/
-static int edit(char line[], int n)
-{
- char *ptr = line + n - 1; // last char in buffer
-
- // strip the EOLN sequence
- while (n > 0) {
- if (*ptr == '\n' || *ptr == '\r') {
- --n;
- } else {
- break;
- }
- }
-
- // add a trailing NUL byte
- if (n > 0) {
- line[n] = '\0';
- }
-
- return n;
-}
-
-/**
-** shell - extremely simple shell for spawning test programs
-**
-** Scheduled by _kshell() when the character 'u' is typed on
-** the console keyboard.
-*/
-USERMAIN(main)
-{
- // keep the compiler happy
- (void)argc;
- (void)argv;
-
- // report that we're up and running
- swrites("Shell is ready\n");
-
- // print a summary of the commands we'll accept
- usage();
-
- // loop forever
- while (!time_to_stop) {
- char line[128];
- char *ptr;
-
- // the shell reads one line from the keyboard, parses it,
- // and performs whatever command it requests.
-
- swrites("\n> ");
- int n = read(CHAN_SIO, line, sizeof(line));
-
- // shortest valid command is "@?", so must have 3+ chars here
- if (n < 3) {
- // ignore it
- continue;
- }
-
- // edit it as needed; new shortest command is 2+ chars
- if ((n = edit(line, n)) < 2) {
- continue;
- }
-
- // find the '@'
- int i = 0;
- for (ptr = line; i < n; ++i, ++ptr) {
- if (*ptr == '@') {
- break;
- }
- }
-
- // did we find an '@'?
- if (i < n) {
- // yes; process any commands that follow it
- ++ptr;
-
- for (; *ptr != '\0'; ++ptr) {
- char buf[128];
- int pid = run(*ptr);
-
- if (pid < 0) {
- // spawn() failed
- sprint(buf, "+++ Shell spawn %c failed, code %d\n", *ptr,
- pid);
- cwrites(buf);
- }
-
- // should we end it all?
- if (time_to_stop) {
- break;
- }
- } // for
-
- // now, wait for all the spawned children
- while (children > 0) {
- // wait for the child
- int32_t status;
- char buf[128];
- int whom = waitpid(0, &status);
-
- // figure out the result
- if (whom == E_NO_CHILDREN) {
- break;
- } else if (whom < 1) {
- sprint(buf, "shell: waitpid() returned %d\n", whom);
- } else {
- --children;
- sprint(buf, "shell: PID %d exit status %d\n", whom, status);
- }
- // report it
- swrites(buf);
- }
- } // if i < n
- } // while
-
- cwrites("!!! shell exited loop???\n");
- exit(1);
-}