summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-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
18 files changed, 424 insertions, 664 deletions
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;
+}