diff options
author | Ian McFarlane <i.mcfarlane2002@gmail.com> | 2025-04-22 14:51:47 -0400 |
---|---|---|
committer | Ian McFarlane <i.mcfarlane2002@gmail.com> | 2025-04-22 14:51:47 -0400 |
commit | 325e2ea9aef0723645b86bdc773f02293747c495 (patch) | |
tree | 2d844c3e30a27eaf463fed851620221f3ad7d540 | |
parent | try to find mcfg (diff) | |
parent | force rebuild on header change (diff) | |
download | comus-pcie.tar.gz comus-pcie.tar.bz2 comus-pcie.zip |
Merge branch 'main' into pciepcie
58 files changed, 774 insertions, 2915 deletions
@@ -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, ¤t); - if (status != SUCCESS) { + int status = pcb_queue_pop(ready, ¤t_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); -} |