From 1c273f96f86ea0d9058db57a5fa16eb2f3034711 Mon Sep 17 00:00:00 2001 From: Kailash Turimella Date: Mon, 6 Oct 2025 14:46:50 -0700 Subject: [PATCH 1/6] completed --- Makefile | 2 + docs/compress.md | 50 ++++++++++++ mkfs/mkfs.c | 103 +++++++++++++++++++++++-- user/compress.c | 178 +++++++++++++++++++++++++++++++++++++++++++ user/compress_test.c | 130 +++++++++++++++++++++++++++++++ 5 files changed, 455 insertions(+), 8 deletions(-) create mode 100644 docs/compress.md create mode 100644 user/compress.c create mode 100644 user/compress_test.c diff --git a/Makefile b/Makefile index cf031b0..477d25f 100644 --- a/Makefile +++ b/Makefile @@ -124,6 +124,8 @@ mkfs/mkfs: mkfs/mkfs.c $K/fs.h $K/param.h UPROGS=\ $U/_cat\ + $U/_compress\ + $U/_compress_test\ $U/_echo\ $U/_forktest\ $U/_grep\ diff --git a/docs/compress.md b/docs/compress.md new file mode 100644 index 0000000..c2cae4d --- /dev/null +++ b/docs/compress.md @@ -0,0 +1,50 @@ +# compress — Run-Length Encode / Decode files + +## Usage + +`compress` provides simple Run-Length Encoding (RLE) for any file. It can read from **stdin** and write to **stdout** if file paths are omitted. + +* `-c` : **Compress** input → RLE output +* `-d` : **Decompress** RLE input → original bytes +* `-h` : Show help/usage + +> RLE format: a stream of 2-byte pairs `[count][value]` with `count` in 1–255. Long runs are split. Random data may grow in size. + +### Examples + +```sh +# File → file +$ compress -c README.md README.rle +$ compress -d README.rle README.out + +# Stream mode (stdin/stdout + pipes/redirection) +$ cat README.md | compress -c > foo.rle +$ cat foo.rle | compress -d > out.txt +``` + +--- + +## Testing + +An automated test runner `/compress_test` is included. It creates sample files, runs compress/decompress, and checks round-trip equality. + +### To run tests + +1. Boot the OS and open the shell. +2. Run: + + ``` + /compress_test + ``` +3. You should see `PASS`/`FAIL` lines and a summary, e.g.: + + ``` + [PASS] small text + [PASS] empty file + [PASS] long run 1000x'A' + [PASS] binary 0x00..0xFF + + All tests PASSED + ``` + +If a test fails, the message indicates which stage (compress/decompress/compare) had an issue. diff --git a/mkfs/mkfs.c b/mkfs/mkfs.c index 5f080bc..5e3873e 100644 --- a/mkfs/mkfs.c +++ b/mkfs/mkfs.c @@ -4,12 +4,18 @@ #include #include #include +#include +#include +#include +#define dirent xv6_dirent #define stat xv6_stat // avoid clash with host struct stat #include "kernel/types.h" #include "kernel/fs.h" #include "kernel/stat.h" #include "kernel/param.h" +#undef dirent +#undef stat #ifndef static_assert #define static_assert(a, b) do { switch (0) case 0: case (a): ; } while (0) @@ -20,9 +26,9 @@ // Disk layout: // [ boot block | sb block | log | inode blocks | free bit map | data blocks ] -int nbitmap = FSSIZE/BPB + 1; +int nbitmap = FSSIZE/(BSIZE*8) + 1; int ninodeblocks = NINODES / IPB + 1; -int nlog = LOGBLOCKS+1; // Header followed by LOGBLOCKS data blocks. +int nlog = LOGBLOCKS; int nmeta; // Number of meta blocks (boot, sb, nlog, inode, bitmap) int nblocks; // Number of data blocks @@ -65,12 +71,88 @@ xint(uint x) return y; } +void +add_file(int parent_fd, uint parentino, char *filename) +{ + uint inum; + int fd; + char buf[BSIZE]; + struct xv6_dirent de; + + if((fd = openat(parent_fd, filename, 0)) < 0) + die(filename); + + inum = ialloc(T_FILE); + + bzero(&de, sizeof(de)); + de.inum = xshort(inum); + strncpy(de.name, filename, DIRSIZ); + iappend(parentino, &de, sizeof(de)); + + ssize_t cc; + while((cc = read(fd, buf, sizeof(buf))) > 0) + iappend(inum, buf, cc); + + close(fd); +} + +uint +add_dir(int level, int parent_fd, uint parentino, char *dirname) +{ + struct xv6_dirent de; + uint dino = ialloc(T_DIR); + bzero(&de, sizeof(de)); + de.inum = xshort(dino); + strcpy(de.name, dirname); + iappend(parentino, &de, sizeof(de)); + + bzero(&de, sizeof(de)); + de.inum = xshort(dino); + strcpy(de.name, "."); + iappend(dino, &de, sizeof(de)); + + bzero(&de, sizeof(de)); + de.inum = xshort(parentino); + strcpy(de.name, ".."); + iappend(dino, &de, sizeof(de)); + + int dir_fd = -1; + if ((dir_fd = openat(parent_fd, dirname, O_RDONLY)) == -1) { + perror("open"); + return dino; + } + + DIR *d = fdopendir(dir_fd); + if (d == NULL) { + perror("fdopendir"); + return dino; + } + + struct dirent *e; + while ((e = readdir(d)) != NULL) { + if (e->d_name[0] == '.') { + continue; + } + + if (e->d_type == DT_REG) { + printf("%*s+ %s\n", level * 2, "", e->d_name); + add_file(dir_fd, dino, e->d_name); + } else if (e->d_type == DT_DIR) { + printf("%*s+ /%s\n", level * 2, "", e->d_name); + add_dir(level + 1, dir_fd, dino, e->d_name); + } + } + close(dir_fd); + + return dino; +} + int main(int argc, char *argv[]) { int i, cc, fd; uint rootino, inum, off; - struct dirent de; + struct xv6_dirent de; char buf[BSIZE]; struct dinode din; @@ -83,7 +165,7 @@ main(int argc, char *argv[]) } assert((BSIZE % sizeof(struct dinode)) == 0); - assert((BSIZE % sizeof(struct dirent)) == 0); + assert((BSIZE % sizeof(struct xv6_dirent)) == 0); fsfd = open(argv[1], O_RDWR|O_CREAT|O_TRUNC, 0666); if(fsfd < 0) @@ -102,7 +184,7 @@ main(int argc, char *argv[]) sb.inodestart = xint(2+nlog); sb.bmapstart = xint(2+nlog+ninodeblocks); - printf("nmeta %d (boot, super, log blocks %u, inode blocks %u, bitmap blocks %u) blocks %d total %d\n", + printf("nmeta %d (boot, super, log blocks %u inode blocks %u, bitmap blocks %u) blocks %d total %d\n", nmeta, nlog, ninodeblocks, nbitmap, nblocks, FSSIZE); freeblock = nmeta; // the first free block that we can allocate @@ -128,6 +210,13 @@ main(int argc, char *argv[]) iappend(rootino, &de, sizeof(de)); for(i = 2; i < argc; i++){ + struct stat sb; + stat(argv[i], &sb); + if (S_ISDIR(sb.st_mode)) { + add_dir(0, AT_FDCWD, rootino, argv[i]); + continue; + } + // get rid of "user/" char *shortname; if(strncmp(argv[i], "user/", 5) == 0) @@ -147,8 +236,6 @@ main(int argc, char *argv[]) if(shortname[0] == '_') shortname += 1; - assert(strlen(shortname) <= DIRSIZ); - inum = ialloc(T_FILE); bzero(&de, sizeof(de)); @@ -240,7 +327,7 @@ balloc(int used) int i; printf("balloc: first %d blocks have been allocated\n", used); - assert(used < BPB); + assert(used < BSIZE*8); bzero(buf, BSIZE); for(i = 0; i < used; i++){ buf[i/8] = buf[i/8] | (0x1 << (i%8)); diff --git a/user/compress.c b/user/compress.c new file mode 100644 index 0000000..ee64323 --- /dev/null +++ b/user/compress.c @@ -0,0 +1,178 @@ +// user/compress.c — Run-Length Encoding (RLE) compressor/decompressor for FogOSv2 +// Format: stream of pairs [count:1 byte][value:1 byte], repeated to EOF. +// Runs >255 are split into multiple pairs. + +#include "kernel/types.h" +#include "kernel/stat.h" +#include "kernel/fcntl.h" +#include "user/user.h" + +#define INBUF 512 +#define OUTBUF 128 + +static void usage(void) { + printf( + "usage:\n" + " compress -c [in] [out] # compress\n" + " compress -d [in] [out] # decompress\n" + "If in/out omitted, stdin/stdout are used.\n" + ); +} + +static int open_in(const char *p) { + if (!p) return 0; // stdin + int fd = open(p, O_RDONLY); + if (fd < 0) { + printf("compress: cannot open %s\n", p); + } + return fd; +} + +static int open_out(const char *p) { + if (!p) return 1; // stdout + int fd = open(p, O_CREATE | O_WRONLY); + if (fd < 0) { + printf("compress: cannot create %s\n", p); + } + return fd; +} + +/* -------------------- RLE compress -------------------- */ +static int rle_compress_fd(int in, int out) { + uchar buf[INBUF]; + int have_prev = 0; + uchar prev = 0; + int cnt = 0; + + for (;;) { + int n = read(in, buf, sizeof(buf)); + if (n < 0) { printf("compress: read error\n"); return -1; } + if (n == 0) break; + + for (int i = 0; i < n; i++) { + uchar b = buf[i]; + if (!have_prev) { + prev = b; cnt = 1; have_prev = 1; + } else if (b == prev && cnt < 255) { + cnt++; + } else { + uchar pair[2] = { (uchar)cnt, prev }; + if (write(out, pair, 2) != 2) { printf("compress: write error\n"); return -1; } + prev = b; cnt = 1; + } + } + } + + if (have_prev) { + uchar pair[2] = { (uchar)cnt, prev }; + if (write(out, pair, 2) != 2) { printf("compress: write error\n"); return -1; } + } + return 0; +} + +/* ------------------ RLE decompress -------------------- */ +static int rle_decompress_fd(int in, int out) { + uchar buf[INBUF]; + int carry = -1; // -1 = no carry; otherwise holds a pending count byte + uchar outbuf[OUTBUF]; + + for (int k = 0; k < OUTBUF; k++) outbuf[k] = 0; // init once + + for (;;) { + int n = read(in, buf, sizeof(buf)); + if (n < 0) { printf("compress: read error\n"); return -1; } + if (n == 0) break; + + int i = 0; + + // If we had an odd byte (count) from the previous read, pair it now. + if (carry != -1) { + if (n < 1) { printf("compress: corrupt input (missing value)\n"); return -1; } + int cnt = carry; + uchar val = buf[0]; + if (cnt <= 0) { printf("compress: corrupt input (zero/neg count)\n"); return -1; } + + // emit cnt copies of val + for (int j = 0; j < OUTBUF; j++) outbuf[j] = val; + int left = cnt; + while (left > 0) { + int chunk = left < OUTBUF ? left : OUTBUF; + if (write(out, outbuf, chunk) != chunk) { printf("compress: write error\n"); return -1; } + left -= chunk; + } + i = 1; // consumed one byte (value) + carry = -1; // cleared + } + + // Process full pairs from this buffer + for (; i + 1 < n; i += 2) { + int cnt = buf[i]; + uchar val = buf[i+1]; + if (cnt <= 0) { printf("compress: corrupt input (zero/neg count)\n"); return -1; } + + for (int j = 0; j < OUTBUF; j++) outbuf[j] = val; + int left = cnt; + while (left > 0) { + int chunk = left < OUTBUF ? left : OUTBUF; + if (write(out, outbuf, chunk) != chunk) { printf("compress: write error\n"); return -1; } + left -= chunk; + } + } + + // If there's one byte left, it's a dangling count; carry it to next read + if (i < n) { + carry = buf[n-1]; + } + } + + if (carry != -1) { // ended with odd number of bytes → corrupt + printf("compress: corrupt input (odd byte count)\n"); + return -1; + } + return 0; +} + +/* ------------------------ main ------------------------ */ +int main(int argc, char *argv[]) { + if (argc < 2) { usage(); exit(1); } + + int mode = 0; // 1 = compress, 2 = decompress + const char *inpath = 0; // NULL -> stdin + const char *outpath = 0; // NULL -> stdout + + // Parse: first flag (-c/-d), then up to 2 positional args + for (int i = 1; i < argc; i++) { + char *a = argv[i]; + if (strcmp(a, "-c") == 0) { + mode = 1; + } else if (strcmp(a, "-d") == 0) { + mode = 2; + } else if (!inpath) { + inpath = a; + } else if (!outpath) { + outpath = a; + } else { + printf("compress: too many arguments\n"); + usage(); + exit(1); + } + } + + if (mode == 0) { + printf("compress: must specify -c or -d\n"); + usage(); + exit(1); + } + + int in = open_in(inpath); + if (in < 0) exit(1); + int out = open_out(outpath); + if (out < 0) { if (in > 1) close(in); exit(1); } + + int rc = (mode == 1) ? rle_compress_fd(in, out) : rle_decompress_fd(in, out); + + if (in > 1) close(in); + if (out > 1) close(out); + if (rc < 0) exit(1); + exit(0); +} diff --git a/user/compress_test.c b/user/compress_test.c new file mode 100644 index 0000000..f4b4b6f --- /dev/null +++ b/user/compress_test.c @@ -0,0 +1,130 @@ +// It creates small input files, runs `compress -c` then `compress -d` via +// fork/exec, and verifies the round-trip output matches the original. + +#include "kernel/types.h" +#include "kernel/stat.h" +#include "kernel/fcntl.h" +#include "user/user.h" + +#define IN_CHUNK 512 + +static int write_file(const char *path, const void *buf, int len) { + int fd = open(path, O_CREATE | O_WRONLY); + if (fd < 0) { printf("compress_test: open(O_CREATE) %s failed\n", path); return -1; } + const char *p = (const char*)buf; + int left = len; + while (left > 0) { + int n = write(fd, p, left); + if (n < 0) { printf("compress_test: write %s failed\n", path); close(fd); return -1; } + left -= n; p += n; + } + close(fd); + return 0; +} + +static int files_equal(const char *a, const char *b) { + int fa = open(a, O_RDONLY); + int fb = open(b, O_RDONLY); + if (fa < 0 || fb < 0) { if (fa>=0) close(fa); if (fb>=0) close(fb); return 0; } + + char ba[IN_CHUNK], bb[IN_CHUNK]; + for (;;) { + int na = read(fa, ba, sizeof(ba)); + int nb = read(fb, bb, sizeof(bb)); + if (na < 0 || nb < 0) { close(fa); close(fb); return 0; } + if (na != nb) { close(fa); close(fb); return 0; } + if (na == 0) { close(fa); close(fb); return 1; } // both EOF + for (int i = 0; i < na; i++) if (ba[i] != bb[i]) { close(fa); close(fb); return 0; } + } +} + +static int run_prog(char *prog, char *argv[]) { + int pid = fork(); + if (pid < 0) { printf("compress_test: fork failed\n"); return -1; } + if (pid == 0) { + // child + exec(prog, argv); + printf("compress_test: exec %s failed\n", prog); + exit(127); + } + int status = 0; + if (wait(&status) < 0) { + // Some FogOS variants support wait(&status); others allow wait(0). + // If your toolchain complains, change to: wait(0); + } + return status; +} + +static int do_roundtrip(const char *label, const char *infile, + const char *rlefile, const char *outfile) +{ + // compress -c infile rlefile + char *argv_c[] = { "compress", "-c", (char*)infile, (char*)rlefile, 0 }; + int st1 = run_prog("compress", argv_c); + if (st1 != 0) { printf("[FAIL] %s: compress step failed (status=%d)\n", label, st1); return -1; } + + // decompress -d rlefile outfile + char *argv_d[] = { "compress", "-d", (char*)rlefile, (char*)outfile, 0 }; + int st2 = run_prog("compress", argv_d); + if (st2 != 0) { printf("[FAIL] %s: decompress step failed (status=%d)\n", label, st2); return -1; } + + // compare + if (!files_equal(infile, outfile)) { + printf("[FAIL] %s: round-trip mismatch\n", label); + return -1; + } + + printf("[PASS] %s\n", label); + return 0; +} + +static void make_repeating(char *buf, int len, char byte) { + for (int i = 0; i < len; i++) buf[i] = byte; +} + +int +main(void) +{ + int rc = 0; + + // --- Test 1: small text --- + const char *t1_in = "t1_in.txt"; + const char *t1_rle = "t1.rle"; + const char *t1_out = "t1_out.txt"; + const char *msg = "Hello RLE!\nHello RLE!\nAAABBBCCCDDD\n"; + if (write_file(t1_in, msg, strlen(msg)) < 0) rc = -1; + if (rc == 0 && do_roundtrip("small text", t1_in, t1_rle, t1_out) < 0) rc = -1; + + // --- Test 2: empty file --- + const char *t2_in = "t2_in.bin"; + const char *t2_rle = "t2.rle"; + const char *t2_out = "t2_out.bin"; + if (rc == 0 && write_file(t2_in, "", 0) < 0) rc = -1; + if (rc == 0 && do_roundtrip("empty file", t2_in, t2_rle, t2_out) < 0) rc = -1; + + // --- Test 3: long run (>255) --- + const char *t3_in = "t3_in.bin"; + const char *t3_rle = "t3.rle"; + const char *t3_out = "t3_out.bin"; + char big[1000]; + make_repeating(big, sizeof(big), 'A'); + if (rc == 0 && write_file(t3_in, big, sizeof(big)) < 0) rc = -1; + if (rc == 0 && do_roundtrip("long run 1000x'A'", t3_in, t3_rle, t3_out) < 0) rc = -1; + + // --- Test 4: binary with zero bytes --- + const char *t4_in = "t4_in.bin"; + const char *t4_rle = "t4.rle"; + const char *t4_out = "t4_out.bin"; + char bin[256]; + for (int i = 0; i < 256; i++) bin[i] = (char)i; // 0x00..0xFF once + if (rc == 0 && write_file(t4_in, bin, sizeof(bin)) < 0) rc = -1; + if (rc == 0 && do_roundtrip("binary 0x00..0xFF", t4_in, t4_rle, t4_out) < 0) rc = -1; + + if (rc == 0) { + printf("\nAll tests PASSED\n"); + exit(0); + } else { + printf("\nSome tests FAILED\n"); + exit(1); + } +} From 7e31681dd90e93ad4d1af7d89b181658c7012472 Mon Sep 17 00:00:00 2001 From: Kailash Turimella Date: Tue, 30 Dec 2025 08:32:50 +0530 Subject: [PATCH 2/6] p --- 1.sh | 33 ++++ 2.sh | 433 +++++++++++++++++++++++++++++++++++++++++++++ 3.sh | 430 ++++++++++++++++++++++++++++++++++++++++++++ 4.sh | 432 ++++++++++++++++++++++++++++++++++++++++++++ Makefile | 34 +++- kernel/exec.c | 35 ++++ kernel/fs.c | 93 ++++++++++ kernel/memlayout.h | 6 + kernel/proc.c | 183 +++++++++++++++---- kernel/proc.h | 8 + kernel/strace.h | 121 +++++++++++++ kernel/syscall.c | 28 ++- kernel/syscall.h | 9 + kernel/sysfile.c | 28 +++ kernel/sysproc.c | 104 ++++++++++- mkfs/mkfs.c | 105 +---------- user/sh.c | 34 +++- user/user.h | 47 +++-- user/usys.pl | 9 + 19 files changed, 2016 insertions(+), 156 deletions(-) create mode 100644 1.sh create mode 100644 2.sh create mode 100644 3.sh create mode 100644 4.sh create mode 100644 kernel/strace.h diff --git a/1.sh b/1.sh new file mode 100644 index 0000000..3c124d2 --- /dev/null +++ b/1.sh @@ -0,0 +1,33 @@ +# Start of file. This line should not cause errors +echo Command 000 +echo Command 001 +echo Command 002 +ehchhho Command 003 +echo Command 004 +echo Command 005 +echo Command 006 # Look, it's command 6. This shouldn't print +echo Command 007 +echo Command 008 +# Empty commands (like this one) and blank lines should not go in history + + + + + +/ls / +mkdir a +mkdir a/b +mkdir a/b/c +mkdir a/b/c/hello +cd a +cd b/c/hello +/ls +/wc /README.md +cd / +/wc README.md +echo --- +history +echo --- +history -t +exit +echo If you see this line, exit is not working. diff --git a/2.sh b/2.sh new file mode 100644 index 0000000..d07fb12 --- /dev/null +++ b/2.sh @@ -0,0 +1,433 @@ +# Make sure empty lines (including this one) are handled +# properly to pass this test. +# +# Bad/unknown commands **do** go into the history. + +thisisacommandthatprobablydoesntexistbutletstryitanywayhuh +echo Command 000 +echo Command 001 +echo Command 002 +echo Command 003 +echo Command 004 +echo Command 005 + +echo Command 006 + +echo Command 007 + +echo Command 008 + +echo Command 009 +echo Command 010 +echo Command 011 +echo Command 012 +echo Command 013 +echo Command 014 +echo Command 015 +echo Command 016 +echo Command 017 +echo Command 018 +echo Command 019 +echo Command 020 +echo Command 021 +echo Command 022 +echo Command 023 +echo Command 024 +echo Command 025 +echo Command 026 +echo Command 027 +echo Command 028 +echo Command 029 +echo Command 030 +echo Command 031 +echo Command 032 +echo Command 033 +echo Command 034 +echo Command 035 +echo Command 036 +echo Command 037 +echo Command 038 +echo Command 039 +echo Command 040 +echo Command 041 +echo Command 042 +echo Command 043 +echo Command 044 +echo Command 045 +echo Command 046 +echo Command 047 +echo Command 048 +echo Command 049 +echo Command 050 +echo Command 051 +echo Command 052 +echo Command 053 +echo Command 054 +echo Command 055 +echo Command 056 +echo Command 057 +echo Command 058 +echo Command 059 +echo Command 060 +echo Command 061 +echo Command 062 +echo Command 063 +echo Command 064 +echo Command 065 +echo Command 066 +echo Command 067 +echo Command 068 +echo Command 069 +echo Command 070 +echo Command 071 +echo Command 072 +echo Command 073 +echo Command 074 +echo Command 075 +echo Command 076 +echo Command 077 +echo Command 078 +echo Command 079 +echo Command 080 +echo Command 081 +echo Command 082 +echo Command 083 +echo Command 084 +echo Command 085 +echo Command 086 +echo Command 087 +echo Command 088 +echo Command 089 +echo Command 090 +echo Command 091 +echo Command 092 +echo Command 093 +echo Command 094 +echo Command 095 +echo Command 096 +echo Command 097 +echo Command 098 +echo Command 099 +echo Command 100 +echo Command 101 +echo Command 102 +echo Command 103 +echo Command 104 +echo Command 105 +echo Command 106 +echo Command 107 +echo Command 108 +echo Command 109 +echo Command 110 +echo Command 111 +echo Command 112 +echo Command 113 +echo Command 114 +echo Command 115 +echo Command 116 +echo Command 117 +echo Command 118 +echo Command 119 +echo Command 120 +echo Command 121 +echo Command 122 +echo Command 123 +echo Command 124 +echo Command 125 +echo Command 126 +echo Command 127 +echo Command 128 +echo Command 129 +echo Command 130 +echo Command 131 +echo Command 132 +echo Command 133 +echo Command 134 +echo Command 135 +echo Command 136 +echo Command 137 +echo Command 138 +echo Command 139 +echo Command 140 +echo Command 141 +echo Command 142 +echo Command 143 +echo Command 144 +echo Command 145 +echo Command 146 +echo Command 147 +echo Command 148 +echo Command 149 +echo Command 150 +echo Command 151 +echo Command 152 +echo Command 153 +echo Command 154 +echo Command 155 +echo Command 156 +echo Command 157 +echo Command 158 +echo Command 159 +echo Command 160 +echo Command 161 +echo Command 162 +echo Command 163 +echo Command 164 +echo Command 165 +echo Command 166 +echo Command 167 +echo Command 168 +echo Command 169 +echo Command 170 +echo Command 171 +echo Command 172 +echo Command 173 +echo Command 174 +echo Command 175 +echo Command 176 +echo Command 177 +echo Command 178 +echo Command 179 +echo Command 180 +echo Command 181 +echo Command 182 +echo Command 183 +echo Command 184 +echo Command 185 +echo Command 186 +echo Command 187 +echo Command 188 +echo Command 189 +echo Command 190 +echo Command 191 +echo Command 192 +echo Command 193 +echo Command 194 +echo Command 195 +echo Command 196 +echo Command 197 +echo Command 198 +echo Command 199 +echo Command 200 +echo Command 201 +echo Command 202 +echo Command 203 +echo Command 204 +echo Command 205 +echo Command 206 +echo Command 207 +echo Command 208 +echo Command 209 +echo Command 210 +echo Command 211 +echo Command 212 +echo Command 213 +echo Command 214 +echo Command 215 +echo Command 216 +echo Command 217 +echo Command 218 +echo Command 219 +echo Command 220 +echo Command 221 +echo Command 222 +echo Command 223 +echo Command 224 +echo Command 225 +echo Command 226 +echo Command 227 +echo Command 228 +echo Command 229 +echo Command 230 +echo Command 231 +echo Command 232 +echo Command 233 +echo Command 234 +echo Command 235 +echo Command 236 +echo Command 237 +echo Command 238 +echo Command 239 +echo Command 240 +echo Command 241 +echo Command 242 +echo Command 243 +echo Command 244 +echo Command 245 +echo Command 246 +echo Command 247 +echo Command 248 +echo Command 249 +echo Command 250 +echo Command 251 +echo Command 252 +echo Command 253 +echo Command 254 +echo Command 255 +echo Command 256 +echo Command 257 +echo Command 258 +echo Command 259 +echo Command 260 +echo Command 261 +echo Command 262 +echo Command 263 +echo Command 264 +echo Command 265 +echo Command 266 +echo Command 267 +echo Command 268 +echo Command 269 +echo Command 270 +echo Command 271 +echo Command 272 +echo Command 273 +echo Command 274 +echo Command 275 +echo Command 276 +echo Command 277 +echo Command 278 +echo Command 279 +echo Command 280 +echo Command 281 +echo Command 282 +echo Command 283 +echo Command 284 +echo Command 285 +echo Command 286 +echo Command 287 +echo Command 288 +echo Command 289 +echo Command 290 +echo Command 291 +echo Command 292 +echo Command 293 +echo Command 294 +echo Command 295 +echo Command 296 +echo Command 297 +echo Command 298 +echo Command 299 +echo Command 300 +echo Command 301 +echo Command 302 +echo Command 303 +echo Command 304 +echo Command 305 +echo Command 306 +echo Command 307 +echo Command 308 +echo Command 309 +echo Command 310 +echo Command 311 +echo Command 312 +echo Command 313 +echo Command 314 +echo Command 315 +echo Command 316 +echo Command 317 +echo Command 318 +echo Command 319 +echo Command 320 +echo Command 321 +echo Command 322 +echo Command 323 +echo Command 324 +echo Command 325 +echo Command 326 +echo Command 327 +echo Command 328 +echo Command 329 +echo Command 330 +echo Command 331 +echo Command 332 +echo Command 333 +echo Command 334 +echo Command 335 +echo Command 336 +echo Command 337 +echo Command 338 +echo Command 339 +echo Command 340 +echo Command 341 +echo Command 342 +echo Command 343 +echo Command 344 +echo Command 345 +echo Command 346 +echo Command 347 +echo Command 348 +echo Command 349 +echo Command 350 +echo Command 351 +echo Command 352 +echo Command 353 +echo Command 354 +echo Command 355 +echo Command 356 +echo Command 357 +echo Command 358 +echo Command 359 +echo Command 360 +echo Command 361 +echo Command 362 +echo Command 363 +echo Command 364 +echo Command 365 +echo Command 366 +echo Command 367 +echo Command 368 +echo Command 369 +echo Command 370 +echo Command 371 +echo Command 372 +echo Command 373 +echo Command 374 +echo Command 375 +echo Command 376 +echo Command 377 +echo Command 378 +echo Command 379 +echo Command 380 +echo Command 381 +echo Command 382 +echo Command 383 +echo Command 384 +echo Command 385 +echo Command 386 +echo Command 387 +echo Command 388 +echo Command 389 +echo Command 390 +echo Command 391 +echo Command 392 +echo Command 393 +echo Command 394 +echo Command 395 +echo Command 396 +echo Command 397 +echo Command 398 +echo Command 399 +echo Command 400 +echo Command 401 +echo Command 402 +echo Command 403 +echo Command 404 +echo Command 405 +echo Command 406 +pwd # May or may not exist... +echo Command 407 +echo Command 408 +echo Command 409 +echo Command 410 +echo Command 411 +echo Command 412 +echo Command 413 +echo Command 414 +echo Command 415 +echo Command 416 +echo Command 417 +echo Command 418 +echo Command 419 +echo ----- +history diff --git a/3.sh b/3.sh new file mode 100644 index 0000000..6c4e8d8 --- /dev/null +++ b/3.sh @@ -0,0 +1,430 @@ +!542 # Doesn't actually exist :-) +history +echo Command 002 +echo Command 003 +echo Command 004 +echo Command 005 +echo Command 006 +echo Command 007 +echo Command 008 +echo Command 009 +echo Command 010 +echo Command 011 +echo Command 012 +echo Command 013 +echo Command 014 +echo Command 015 +echo Command 016 +echo Command 017 +echo Command 018 +echo Command 019 +echo Command 020 +echo Command 021 +echo Command 022 +echo Command 023 +echo Command 024 +echo Command 025 +echo Command 026 +echo Command 027 +echo Command 028 +echo Command 029 +echo Command 030 +echo Command 031 +echo Command 032 +echo Command 033 +echo Command 034 +echo Command 035 +echo Command 036 +echo Command 037 +echo Command 038 +echo Command 039 +echo Command 040 +echo Command 041 +echo Command 042 +echo Command 043 +echo Command 044 +echo Command 045 +echo Command 046 +echo Command 047 +echo Command 048 +echo Command 049 +echo Command 050 +echo Command 051 +echo Command 052 +echo Command 053 +echo Command 054 +echo Command 055 +echo Command 056 +echo Command 057 +echo Command 058 +echo Command 059 +echo Command 060 +echo Command 061 +echo Command 062 +echo Command 063 +echo Command 064 +echo Command 065 +echo Command 066 +echo Command 067 +echo Command 068 +echo Command 069 +echo Command 070 +echo Command 071 +echo Command 072 +echo Command 073 +echo Command 074 +echo Command 075 +echo Command 076 +echo Command 077 +echo Command 078 +echo Command 079 +echo Command 080 +echo Command 081 +echo Command 082 +echo Command 083 +echo Command 084 +echo Command 085 +echo Command 086 +echo Command 087 +echo Command 088 +echo Command 089 +echo Command 090 +echo Command 091 +echo Command 092 +echo Command 093 +echo Command 094 +echo Command 095 +echo Command 096 +echo Command 097 +echo Command 098 +echo Command 099 +echo Command 100 +echo Command 101 +echo Command 102 +echo Command 103 +echo Command 104 +echo Command 105 +echo Command 106 +echo Command 107 +echo Command 108 +echo Command 109 +echo Command 110 +echo Command 111 +echo Command 112 +echo Command 113 +echo Command 114 +echo Command 115 +echo Command 116 +echo Command 117 +echo Command 118 +echo Command 119 +echo Command 120 +echo Command 121 +echo Command 122 +echo Command 123 +echo Command 124 +echo Command 125 +echo Command 126 +echo Command 127 +echo Command 128 +echo Command 129 +echo Command 130 +echo Command 131 +echo Command 132 +echo Command 133 +echo Command 134 +echo Command 135 +echo Command 136 +echo Command 137 +echo Command 138 +echo Command 139 +echo Command 140 +echo Command 141 +echo Command 142 +echo Command 143 +echo Command 144 +echo Command 145 +echo Command 146 +echo Command 147 +echo Command 148 +echo Command 149 +echo Command 150 +echo Command 151 +echo Command 152 +echo Command 153 +echo Command 154 +echo Command 155 +echo Command 156 +echo Command 157 +echo Command 158 +echo Command 159 +echo Command 160 +echo Command 161 +echo Command 162 +echo Command 163 +echo Command 164 +echo Command 165 +echo Command 166 +echo Command 167 +echo Command 168 +echo Command 169 +echo Command 170 +echo Command 171 +echo Command 172 +echo Command 173 +echo Command 174 +echo Command 175 +echo Command 176 +echo Command 177 +echo Command 178 +echo Command 179 +echo Command 180 +echo Command 181 +echo Command 182 +echo Command 183 +echo Command 184 +echo Command 185 +echo Command 186 +echo Command 187 +echo Command 188 +echo Command 189 +echo Command 190 +echo Command 191 +echo Command 192 +echo Command 193 +echo Command 194 +echo Command 195 +echo Command 196 +echo Command 197 +echo Command 198 +echo Command 199 +echo Command 200 +echo Command 201 +echo Command 202 +echo Command 203 +echo Command 204 +echo Command 205 +echo Command 206 +echo Command 207 +echo Command 208 +echo Command 209 +echo Command 210 +echo Command 211 +echo Command 212 +echo Command 213 +echo Command 214 +echo Command 215 +echo Command 216 +echo Command 217 +echo Command 218 +echo Command 219 +echo Command 220 +echo Command 221 +echo Command 222 +echo Command 223 +echo Command 224 +echo Command 225 +echo Command 226 +echo Command 227 +echo Command 228 +echo Command 229 +echo Command 230 +echo Command 231 +echo Command 232 +echo Command 233 +echo Command 234 +echo Command 235 +echo Command 236 +echo Command 237 +echo Command 238 +echo Command 239 +echo Command 240 +echo Command 241 +echo Command 242 +echo Command 243 +echo Command 244 +echo Command 245 +echo Command 246 +echo Command 247 +echo Command 248 +echo Command 249 +echo Command 250 +echo Command 251 +echo Command 252 +echo Command 253 +echo Command 254 +echo Command 255 +echo Command 256 +echo Command 257 +echo Command 258 +echo Command 259 +echo Command 260 +echo Command 261 +echo Command 262 +echo Command 263 +echo Command 264 +echo Command 265 +echo Command 266 +echo Command 267 +echo Command 268 +echo Command 269 +echo Command 270 +echo Command 271 +echo Command 272 +echo Command 273 +echo Command 274 +echo Command 275 +echo Command 276 +echo Command 277 +echo Command 278 +echo Command 279 +echo Command 280 +echo Command 281 +echo Command 282 +echo Command 283 +echo Command 284 +echo Command 285 +echo Command 286 +echo Command 287 +echo Command 288 +echo Command 289 +echo Command 290 +echo Command 291 +echo Command 292 +echo Command 293 +echo Command 294 +echo Command 295 +echo Command 296 +echo Command 297 +echo Command 298 +echo Command 299 +echo Command 300 +echo Command 301 +echo Command 302 +echo Command 303 +echo Command 304 +echo Command 305 +echo Command 306 +echo Command 307 +echo Command 308 +echo Command 309 +echo Command 310 +echo Command 311 +echo Command 312 +echo Command 313 +echo Command 314 +echo Command 315 +echo Command 316 +echo Command 317 +echo Command 318 +echo Command 319 +echo Command 320 +echo Command 321 +echo Command 322 +echo Command 323 +echo Command 324 +echo Command 325 +echo Command 326 +echo Command 327 +echo Command 328 +echo Command 329 +echo Command 330 +echo Command 331 +echo Command 332 +echo Command 333 +echo Command 334 +echo Command 335 +echo Command 336 +echo Command 337 +echo Command 338 +echo Command 339 +echo Command 340 +echo Command 341 +echo Command 342 +echo Command 343 +echo Command 344 +echo Command 345 +echo Command 346 +echo Command 347 +echo Command 348 +echo Command 349 +echo Command 350 +echo Command 351 +echo Command 352 +echo Command 353 +echo Command 354 +echo Command 355 +echo Command 356 +echo Command 357 +echo Command 358 +echo Command 359 +echo Command 360 +echo Command 361 +echo Command 362 +echo Command 363 +echo Command 364 +echo Command 365 +echo Command 366 +echo Command 367 +echo Command 368 +echo Command 369 +echo Command 370 +echo Command 371 +echo Command 372 +echo Command 373 +echo Command 374 +echo Command 375 +echo Command 376 +echo Command 377 +echo Command 378 +echo Command 379 +echo Command 380 +echo Command 381 +echo Command 382 +echo Command 383 +echo Command 384 +echo Command 385 +echo Command 386 +echo Command 387 +echo Command 388 +echo Command 389 +echo Command 390 +echo Command 391 +echo Command 392 +echo Command 393 +echo Command 394 +echo Command 395 +echo Command 396 +echo Command 397 +echo Command 398 +echo Command 399 +echo Command 400 +echo Command 401 +echo Command 402 +echo Command 403 +echo Command 404 +echo Command 405 +echo Command 406 +echo Command 407 +echo Command 408 +echo Command 409 +echo Command 410 +echo Command 411 +echo Command 412 +echo Command 413 +echo Command 414 +echo Command 415 +echo Command 416 +echo Command 417 +echo Command 418 +echo Command 419 +echo Command 420 +echo ----- +history +echo ----- +!420 # Works! +!325 # Works! +!325 # Shouldn't work the 2nd time -- gets popped off history. +!401 # Works! +!23 # Does not exist anymore +!999 # Hasn't happened yet diff --git a/4.sh b/4.sh new file mode 100644 index 0000000..22e012c --- /dev/null +++ b/4.sh @@ -0,0 +1,432 @@ +echo Command 000 +echo Command 001 +cat /README.md +echo Command 003 +echo Command 004 +echo Command 005 +echo Command 006 +echo Command 007 +echo Command 008 +echo Command 009 +echo Command 010 +echo Command 011 +echo Command 012 +echo Command 013 +echo Command 014 +echo Command 015 +echo Command 016 +echo Command 017 +echo Command 018 +echo Command 019 +echo Command 020 +echo Command 021 +echo Command 022 +echo Command 023 +echo Command 024 +echo Command 025 +echo Command 026 +echo Command 027 +echo Command 028 +echo Command 029 +echo Command 030 +echo Command 031 +echo Command 032 +echo Command 033 +echo Command 034 +echo Command 035 +echo Command 036 +echo Command 037 +echo Command 038 +echo Command 039 +echo Command 040 +echo Command 041 +echo Command 042 +echo Command 043 +echo Command 044 +echo Command 045 +echo Command 046 +echo Command 047 +echo Command 048 +echo Command 049 +echo Command 050 +echo Command 051 +echo Command 052 +echo Command 053 +echo Command 054 +echo Command 055 +echo Command 056 +echo Command 057 +echo Command 058 +echo Command 059 +echo Command 060 +echo Command 061 +echo Command 062 +echo Command 063 +echo Command 064 +echo Command 065 +echo Command 066 +echo Command 067 +echo Command 068 +echo Command 069 +echo Command 070 +echo Command 071 +echo Command 072 +echo Command 073 +echo Command 074 +echo Command 075 +echo Command 076 +echo Command 077 +echo Command 078 +echo Command 079 +echo Command 080 +echo Command 081 +echo Command 082 +echo Command 083 +echo Command 084 +echo Command 085 +echo Command 086 +echo Command 087 +echo Command 088 +echo Command 089 +echo Command 090 +echo Command 091 +echo Command 092 +echo Command 093 +echo Command 094 +echo Command 095 +echo Command 096 +echo Command 097 +echo Command 098 +echo Command 099 +echo Command 100 +echo Command 101 +echo Command 102 +echo Command 103 +echo Command 104 +echo Command 105 +echo Command 106 +echo Command 107 +echo Command 108 +echo Command 109 +echo Command 110 +echo Command 111 +echo Command 112 +echo Command 113 +echo Command 114 +echo Command 115 +echo Command 116 +echo Command 117 +echo Command 118 +echo Command 119 +echo Command 120 +echo Command 121 +echo Command 122 +echo Command 123 +echo Command 124 +echo Command 125 +echo Command 126 +echo Command 127 +echo Command 128 +echo Command 129 +echo Command 130 +# Potato +echo Command 131 +echo Command 132 +echo Command 133 +echo Command 134 +echo Command 135 +echo Command 136 +echo Command 137 +echo Command 138 +echo Command 139 +echo Command 140 +echo Command 141 +echo Command 142 +echo Command 143 +echo Command 144 +echo Command 145 +echo Command 146 +echo Command 147 +echo Command 148 +echo Command 149 +echo Command 150 +echo Command 151 +echo Command 152 +echo Command 153 +echo Command 154 +echo Command 155 +echo Command 156 +echo Command 157 +echo Command 158 +echo Command 159 +echo Command 160 +echo Command 161 +echo Command 162 +echo Command 163 +echo Command 164 +echo Command 165 +echo Command 166 +echo Command 167 +echo Command 168 +echo Command 169 +echo Command 170 +echo Command 171 +echo Command 172 +echo Command 173 +echo Command 174 +echo Command 175 +echo Command 176 +echo Command 177 +echo Command 178 +echo Command 179 +echo Command 180 +echo Command 181 +echo Command 182 +echo Command 183 +echo Command 184 +echo Command 185 +echo Command 186 +echo Command 187 +echo Command 188 +echo Command 189 +echo Command 190 +echo Command 191 +echo Command 192 +echo Command 193 +echo Command 194 +echo Command 195 +echo Command 196 +echo Command 197 +echo Command 198 +echo Command 199 +echo Command 200 +echo Command 201 +echo Command 202 +echo Command 203 +echo Command 204 +echo Command 205 +echo Command 206 +echo Command 207 +echo Command 208 +echo Command 209 +echo Command 210 +echo Command 211 +echo Command 212 +echo Command 213 +echo Command 214 +echo Command 215 +echo Command 216 +echo Command 217 +echo Command 218 +echo Command 219 +echo Command 220 +echo Command 221 +echo Command 222 +echo Command 223 +echo Command 224 +echo Command 225 +echo Command 226 +echo Command 227 +echo Command 228 +echo Command 229 +echo Command 230 +echo Command 231 +echo Command 232 +echo Command 233 +echo Command 234 +echo Command 235 +echo Command 236 +echo Command 237 +echo Command 238 +echo Command 239 +echo Command 240 +echo Command 241 +echo Command 242 +echo Command 243 +echo Command 244 +echo Command 245 +echo Command 246 +echo Command 247 +echo Command 248 +echo Command 249 +echo Command 250 +echo Command 251 +echo Command 252 +echo Command 253 +echo Command 254 +echo Command 255 +echo Command 256 +echo Command 257 +echo Command 258 +echo Command 259 +echo Command 260 +echo Command 261 +echo Command 262 +echo Command 263 +echo Command 264 +echo Command 265 +echo Command 266 +echo Command 267 +echo Command 268 +echo Command 269 +echo Command 270 +echo Command 271 +echo Command 272 +echo Command 273 +echo Command 274 +echo Command 275 +echo Command 276 +echo Command 277 +echo Command 278 +echo Command 279 +echo Command 280 +echo Command 281 +echo Command 282 +echo Command 283 +echo Command 284 +echo Command 285 +echo Command 286 +echo Command 287 +echo Command 288 +echo Command 289 +echo Command 290 +echo Command 291 +echo Command 292 +echo Command 293 +echo Command 294 +echo Command 295 +echo Command 296 +echo Command 297 +echo Command 298 +echo Command 299 +echo Command 300 +echo Command 301 +echo Command 302 +echo Command 303 +echo Command 304 +echo Command 305 +echo Command 306 +echo Command 307 +echo Command 308 +echo Command 309 +echo Command 310 +echo Command 311 +echo Command 312 +echo Command 313 +echo Command 314 +echo Command 315 +echo Command 316 +echo Command 317 +echo Command 318 +echo Command 319 +echo Command 320 +echo Command 321 +echo Command 322 +echo Command 323 +echo Command 324 +echo Command 325 +echo Command 326 +echo Command 327 +echo Command 328 +echo Command 329 +echo Command 330 +echo Command 331 +echo Command 332 +echo Command 333 +echo Command 334 +echo Command 335 +echo Command 336 +echo Command 337 +echo Command 338 +echo Command 339 +echo Command 340 +echo Command 341 +echo Command 342 +echo Command 343 +echo Command 344 +echo Command 345 +echo Command 346 +echo Command 347 +echo Command 348 +echo Command 349 +echo Command 350 +echo Command 351 +echo Command 352 +echo Command 353 +echo Command 354 +echo Command 355 +echo Command 356 +echo Command 357 +echo Command 358 +echo Command 359 +echo Command 360 +echo Command 361 +echo Command 362 +echo Command 363 +echo Command 364 +echo Command 365 +echo Command 366 +echo Command 367 +echo Command 368 +echo Command 369 +echo Command 370 +echo Command 371 +ls / +echo Command 373 +echo Command 374 +echo Command 375 +echo Command 376 +echo Command 377 +echo Command 378 +echo Command 379 +echo Command 380 +echo Command 381 +echo Command 383 +echo Command 384 +mkdir /a +ls /a +echo Command 385 +echo Command 386 +echo Command 387 +echo Command 388 +mysterycommand +echo Command 389 +echo Command 390 +echo Command 391 +echo Command 392 +grep Potato$ /README.md +grep Potato$ /4.sh +echo Command 394 +echo Command 395 +echo Command 396 +echo Command 397 +echo Command 398 +echo Command 399 +echo Command 400 +echo Command 401 +echo Command 402 +echo Command 403 +echo Command 404 +echo Command 405 +wc /README.md +echo Command 407 +echo Command 408 +echo Command 409 +echo Command 410 +echo Command 411 +echo Command 412 +echo Command 413 +echo Command 414 +echo Command 415 +echo Command 416 +echo Command 417 +echo Command 418 +echo Command 419 +echo ----- +!ls # ls an empty dir +!m # run mysterycommand and fail since it doesn't exist +!grepfzw # no command had this prefix +!grep # find 'Potato' in this script (*not* in README.md) +!w # word count README.md +!gr # find 'Potato' again +!e # last echo command (-----) diff --git a/Makefile b/Makefile index 477d25f..9496185 100644 --- a/Makefile +++ b/Makefile @@ -83,11 +83,14 @@ endif LDFLAGS = -z max-page-size=4096 -$K/kernel: $(OBJS) $K/kernel.ld +$K/kernel: $(OBJS) $K/kernel.ld $K/strace.h $(LD) $(LDFLAGS) -T $K/kernel.ld -o $K/kernel $(OBJS) $(OBJDUMP) -S $K/kernel > $K/kernel.asm $(OBJDUMP) -t $K/kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $K/kernel.sym +$K/strace.h: generate-traces.sh $U/user.h + ./generate-traces.sh > $K/strace.h + $K/%.o: $K/%.S $(CC) -g -c -o $@ $< @@ -110,7 +113,7 @@ $U/usys.o : $U/usys.S $U/_forktest: $U/forktest.o $(ULIB) # forktest has less library code linked in - needs to be small # in order to be able to max out the proc table. - $(LD) $(LDFLAGS) -N -e main -Ttext 0 -o $U/_forktest $U/forktest.o $U/ulib.o $U/usys.o + $(LD) $(LDFLAGS) -N -e main -Ttext 0 -o $U/_forktest $U/forktest.o $U/ulib.o $U/usys.o $U/umalloc.o $U/printf.o $(OBJDUMP) -S $U/_forktest > $U/forktest.asm mkfs/mkfs: mkfs/mkfs.c $K/fs.h $K/param.h @@ -123,20 +126,43 @@ mkfs/mkfs: mkfs/mkfs.c $K/fs.h $K/param.h .PRECIOUS: %.o UPROGS=\ + $U/_about\ + $U/_barca\ + $U/_benchmark\ + $U/_broken\ $U/_cat\ + $U/_catlines\ $U/_compress\ $U/_compress_test\ + $U/_clear\ + $U/_clock\ $U/_echo\ + $U/_fnr\ $U/_forktest\ + $U/_freemem\ $U/_grep\ $U/_init\ $U/_kill\ + $U/_leetify\ $U/_ln\ $U/_ls\ $U/_mkdir\ + $U/_mmap\ + $U/_mmaptest\ + $U/_mt70\ + $U/_mt80\ + $U/_mt90\ + $U/_nice\ + $U/_opt_cat\ + $U/_pwd\ $U/_rm\ + $U/_reboot\ $U/_sh\ + $U/_shutdown\ + $U/_spinner\ + $U/_strace\ $U/_stressfs\ + $U/_tolower\ $U/_usertests\ $U/_grind\ $U/_wc\ @@ -145,8 +171,8 @@ UPROGS=\ $U/_forphan\ $U/_dorphan\ -fs.img: mkfs/mkfs README.md $(UPROGS) - mkfs/mkfs fs.img README.md $(UPROGS) +fs.img: mkfs/mkfs README.md tm.txt input.txt $(UPROGS) script.sh spin1.sh spin2.sh 1.sh 2.sh + mkfs/mkfs fs.img README.md tm.txt input.txt $(UPROGS) script.sh spin1.sh spin2.sh 1.sh 2.sh -include kernel/*.d user/*.d diff --git a/kernel/exec.c b/kernel/exec.c index 7cb6fe5..061e574 100644 --- a/kernel/exec.c +++ b/kernel/exec.c @@ -44,6 +44,37 @@ kexec(char *path, char **argv) } ilock(ip); + // check for shebang (#!) + char shebang[2]; + if(readi(ip, 0, (uint64)shebang, 0, 2) == 2 && shebang[0] == '#' && shebang[1] == '!') { + // now read the interpreter + char interpreter[MAXPATH]; + int n = readi(ip, 0, (uint64)interpreter, 2, MAXPATH); + if (n <= 0) + goto bad; + + // find \n and remove it from the interpreter + for (i = 0; i < n; i++) { + if(interpreter[i] == '\n') { + interpreter[i] = '\0'; + break; + } + } + + // build new argv array to pass to kexec + char *new_argv[MAXARG]; + new_argv[0] = interpreter; + new_argv[1] = path; + for (i = 1; argv[1] && i < MAXARG - 2; i++) + new_argv[i + 1] = argv[i]; + new_argv[i + 1] = 0; + + // unlock and end any file system log operations + iunlockput(ip); + end_op(); + return kexec(interpreter, new_argv); + } + // Read the ELF header. if(readi(ip, 0, (uint64)&elf, 0, sizeof(elf)) != sizeof(elf)) goto bad; @@ -133,6 +164,10 @@ kexec(char *path, char **argv) p->sz = sz; p->trapframe->epc = elf.entry; // initial program counter = main p->trapframe->sp = sp; // initial stack pointer + + p->mmap = TRAPFRAME - (USERSTACK * PGSIZE); + p->mmap_pages = 0; + proc_freepagetable(oldpagetable, oldsz); return argc; // this ends up in a0, the first argument to main(argc, argv) diff --git a/kernel/fs.c b/kernel/fs.c index 9274678..19ecc66 100644 --- a/kernel/fs.c +++ b/kernel/fs.c @@ -718,3 +718,96 @@ nameiparent(char *path, char *name) { return namex(path, 1, name); } + +// Get the name of a particular inode in a directory. +int +diriname(struct inode *dp, ushort inum, char name[DIRSIZ], ushort *iparent) +{ + ilock(dp); + + if (dp->type != T_DIR) + panic("diriname not DIR"); + + struct dirent de; + if (iparent) { + // Read second entry ('..', aka parent) + readi(dp, 0, (uint64) &de, sizeof(struct dirent), sizeof(struct dirent)); + *iparent = de.inum; + } + + for (uint off = 0; off < dp->size; off += sizeof(de)){ + if (readi(dp, 0, (uint64) &de, off, sizeof(de)) != sizeof(de)) + panic("diriname read"); + if (de.inum == 0) + continue; + if (de.inum == inum && name) { + // Found the inode + strncpy(name, de.name, DIRSIZ); + iunlockput(dp); + return strlen(name); + } + } + + iunlockput(dp); + return -1; +} + +int +getcwd(char *buf, uint size) +{ + struct inode *ip = myproc()->cwd; + ilock(ip); + + if(ip->type != T_DIR) + panic("getcwd not DIR"); + + if (!buf || size <= 1) { + iunlock(ip); + return -1; + } + + if (ip->inum == 1 && size > 1) { + buf[0] = '/'; + buf[1] = '\0'; + iunlock(ip); + return 0; + } + + // We copy the path to the *end* of the buffer instead of the beginning, then + // work backwards. Start by null terminating the string. + buf[size - 1] = '\0'; + uint bufp = size - 2; + + struct inode *i = iget(ip->dev, ip->inum); + + ushort iparent; + ushort last_parent = ip->inum; + ushort icurrent = ip->inum; + char name[DIRSIZ]; + iunlock(ip); + + // Starting with the CWD inode, go up one level (..), read that directory, and + // search for the CWD inode in it. Repeat the process until we reach /. + while (icurrent != 1 && diriname(i, icurrent, name, &iparent) != -1) { + if (icurrent != last_parent) { + // We found another path element. Add it to the string. + int len = strlen(name); + if (len < bufp) { + bufp -= len; + memmove(buf + bufp, name, len); + buf[--bufp] = '/'; + } else { + // No more space to store the path. + break; + } + } + i = iget(ip->dev, iparent); + icurrent = last_parent; + last_parent = iparent; + } + + // If we didn't use the entire buffer, slide the string over to the beginning. + memmove(buf, buf + bufp, size - bufp); + + return 0; +} diff --git a/kernel/memlayout.h b/kernel/memlayout.h index 9bc9424..3848ff4 100644 --- a/kernel/memlayout.h +++ b/kernel/memlayout.h @@ -25,6 +25,12 @@ #define VIRTIO0 0x10001000 #define VIRTIO0_IRQ 1 +// goldfish_rtc clock +#define GOLDFISH 0x101000 + +// qemu "test" device for signaling exit/status +#define VIRT_TEST 0x100000 + // qemu puts platform-level interrupt controller (PLIC) here. #define PLIC 0x0c000000L #define PLIC_PRIORITY (PLIC + 0x0) diff --git a/kernel/proc.c b/kernel/proc.c index 9d6cf3f..f1bf4fa 100644 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -125,6 +125,12 @@ allocproc(void) p->pid = allocpid(); p->state = USED; + p->nice = 1; + p->priority = 3 - p->nice; + + p->mmap = 0; + p->mmap_pages = 0; + // Allocate a trapframe page. if((p->trapframe = (struct trapframe *)kalloc()) == 0){ freeproc(p); @@ -158,8 +164,15 @@ freeproc(struct proc *p) if(p->trapframe) kfree((void*)p->trapframe); p->trapframe = 0; - if(p->pagetable) - proc_freepagetable(p->pagetable, p->sz); + if (p->pagetable) { + if (p->mmap_pages > 0) { + uvmunmap(p->pagetable, p->mmap, p->mmap_pages, 1); + p->mmap_pages = 0; + p->mmap = 0; + } + proc_freepagetable(p->pagetable, p->sz); + } + p->pagetable = 0; p->sz = 0; p->pid = 0; @@ -168,6 +181,7 @@ freeproc(struct proc *p) p->chan = 0; p->killed = 0; p->xstate = 0; + p->tracing = 0; p->state = UNUSED; } @@ -279,6 +293,25 @@ kfork(void) // Cause fork to return 0 in the child. np->trapframe->a0 = 0; + np->mmap = p->mmap; + np->mmap_pages = p->mmap_pages; + + for (i = 0; i < p->mmap_pages; i++) { + uint64 va = p->mmap + (uint64)i * PGSIZE; + + uint64 pa = walkaddr(p->pagetable, va); + if (pa == 0) + panic("kfork: mmap walkaddr failed"); + + + if(mappages(np->pagetable, va, PGSIZE, pa, + PTE_R | PTE_W | PTE_U) < 0) + panic("kfrok: mmap mappages failed"); + + + incref(pa); + } + // increment reference counts on open file descriptors. for(i = 0; i < NOFILE; i++) if(p->ofile[i]) @@ -287,6 +320,9 @@ kfork(void) safestrcpy(np->name, p->name, sizeof(p->name)); + np->nice = p->nice; // copy parent's nicenessœ + np->priority = p->priority; + pid = np->pid; release(&np->lock); @@ -411,6 +447,64 @@ kwait(uint64 addr) } } +// Wait for a child process to exit and return its pid. +// Return -1 if this process has no children. +int +kwait2(uint64 addr, uint64 addr2) +{ + struct proc *pp; + int havekids, pid; + struct proc *p = myproc(); + + acquire(&wait_lock); + + for(;;){ + // Scan through table looking for exited children. + havekids = 0; + for(pp = proc; pp < &proc[NPROC]; pp++){ + if(pp->parent == p){ + // make sure the child isn't still in exit() or swtch(). + acquire(&pp->lock); + + havekids = 1; + if(pp->state == ZOMBIE){ + // Found one. + pid = pp->pid; + if(addr != 0 && copyout(p->pagetable, addr, (char *)&pp->xstate, + sizeof(pp->xstate)) < 0) { + release(&pp->lock); + release(&wait_lock); + return -1; + } + + // copy syscall count to user + if(addr2 != 0 && copyout(p->pagetable, addr2, (char *)&pp->counter, + sizeof(pp->counter)) < 0) { + release(&pp->lock); + release(&wait_lock); + return -1; + } + + freeproc(pp); + release(&pp->lock); + release(&wait_lock); + return pid; + } + release(&pp->lock); + } + } + + // No point waiting if we don't have any children. + if(!havekids || killed(p)){ + release(&wait_lock); + return -1; + } + + // Wait for a child to exit. + sleep(p, &wait_lock); //DOC: wait-sleep + } +} + // Per-CPU process scheduler. // Each CPU calls scheduler() after setting itself up. // Scheduler never returns. It loops, doing: @@ -426,39 +520,64 @@ scheduler(void) c->proc = 0; for(;;){ - // The most recent process to run may have had interrupts - // turned off; enable them to avoid a deadlock if all - // processes are waiting. Then turn them back off - // to avoid a possible race between an interrupt - // and wfi. - intr_on(); - intr_off(); - - int found = 0; - for(p = proc; p < &proc[NPROC]; p++) { - acquire(&p->lock); - if(p->state == RUNNABLE) { - // Switch to chosen process. It is the process's job - // to release its lock and then reacquire it - // before jumping back to us. - p->state = RUNNING; - c->proc = p; - swtch(&c->context, &p->context); - - // Process is done running for now. - // It should have changed its p->state before coming back. - c->proc = 0; - found = 1; + // The most recent process to run may have had interrupts + // turned off; enable them to avoid a deadlock if all + // processes are waiting. Then turn them back off + // to avoid a possible race between an interrupt + // and wfi. + intr_on(); + intr_off(); + + int found = 0; + + // Find the highest priority among RUNNABLE processes + // so we can start scanning from there + int max_priority = -1; + for(p = proc; p < &proc[NPROC]; p++){ + acquire(&p->lock); + if(p->state == RUNNABLE && p->priority > max_priority) + max_priority = p->priority; + release(&p->lock); + } + + // Iterate priority "levels" from highest we observed down to 0. + // At each level, run any RUNNABLE process whose priority is >= level. + if(max_priority >= 0){ + for(int level = max_priority; level >= 0; level--){ + int found_runnable = 0; + + for(p = proc; p < &proc[NPROC]; p++){ + acquire(&p->lock); + if(p->state == RUNNABLE && p->priority >= level){ + found_runnable = 1; + + // Switch to chosen process. It is the process's job + // to release its lock and then reacquire it + // before jumping back to us. + p->state = RUNNING; + c->proc = p; + swtch(&c->context, &p->context); + + // Process is done running for now. + // It should have changed its p->state before coming back. + c->proc = 0; + found = 1; + } + release(&p->lock); } - release(&p->lock); - } - if(found == 0) { - // nothing to run; stop running on this core until an interrupt. - asm volatile("wfi"); + + // If no process qualified at this level, just drop to the next one. + if(!found_runnable) + continue; } } -} + if(found == 0){ + // nothing to run; stop running on this core until an interrupt. + asm volatile("wfi"); + } + } +} // Switch to scheduler. Must hold only p->lock // and have changed proc->state. Saves and restores // intena because intena is a property of this @@ -681,7 +800,7 @@ procdump(void) state = states[p->state]; else state = "???"; - printf("%d %s %s", p->pid, state, p->name); + printf("%d %s %s priority=%d nice=%d\n", p->pid, state, p->name, p->priority, p->nice); printf("\n"); } } diff --git a/kernel/proc.h b/kernel/proc.h index d021857..d591306 100644 --- a/kernel/proc.h +++ b/kernel/proc.h @@ -91,6 +91,10 @@ struct proc { int killed; // If non-zero, have been killed int xstate; // Exit status to be returned to parent's wait int pid; // Process ID + int tracing; // Trace Flag + int counter; // Counter for tracking SYS calls + int nice; // (0 = highest priority) + int priority; // (derived: prio = 3 - nice; higher number = higher priority) // wait_lock must be held when using this: struct proc *parent; // Parent process @@ -98,6 +102,10 @@ struct proc { // these are private to the process, so p->lock need not be held. uint64 kstack; // Virtual address of kernel stack uint64 sz; // Size of process memory (bytes) + + uint64 mmap; // lowest virtual address used by mmap region + int mmap_pages; // number of mmap pages currently mapped + pagetable_t pagetable; // User page table struct trapframe *trapframe; // data page for trampoline.S struct context context; // swtch() here to run process diff --git a/kernel/strace.h b/kernel/strace.h new file mode 100644 index 0000000..3fb96c2 --- /dev/null +++ b/kernel/strace.h @@ -0,0 +1,121 @@ +#include "defs.h" +#include "syscall.h" +#pragma GCC diagnostic ignored "-Wunused-function" + +static uint64 +read_int(int n) +{ + int i; + argint(n, &i); + return i; +} + +static void * +read_ptr(int n) +{ + // TODO: read an argument as a pointer. wait() in sysproc.c + // is a good example of doing this. + uint64 addr; + argaddr(n, &addr); + return (void *)addr; +} + +static char * +read_str(int n) +{ + // TODO: read an argument as a string. open() or mkdir() in sysfile.c + // are good examples of doing this. + static char buf[128]; + if (argstr(n, buf, sizeof(buf)) < 0) + return "(null)"; + return buf; +} + +void +strace(struct proc *p, int syscall_num, uint64 ret_val) +{ + // printf("[strace] syscall: %d\n", syscall_num); + // This will drop the raw syscall output here, so it's commented out for + // now... we need to update this function so that it prints system call + // tracing information (based on the syscall_num passed in). + switch(syscall_num){ + case SYS_fork: + printf("[%d|%s] fork() = %ld\n", p->pid, p->name, ret_val); + break; +case SYS_exit: + printf("[%d|%s] exit(int = %ld) = %ld\n", p->pid, p->name, read_int(0), ret_val); + break; +case SYS_wait: + printf("[%d|%s] wait(status = %p) = %ld\n", p->pid, p->name, read_ptr(0), ret_val); + break; +case SYS_pipe: + printf("[%d|%s] pipe(pipefd = %p) = %ld\n", p->pid, p->name, read_ptr(0), ret_val); + break; +case SYS_read: + printf("[%d|%s] read(fd = %ld, buf = %p, count = %ld) = %ld\n", p->pid, p->name, read_int(0), read_ptr(1), read_int(2), ret_val); + break; +case SYS_write: + printf("[%d|%s] write(fd = %ld, buf = %p, count = %ld) = %ld\n", p->pid, p->name, read_int(0), read_ptr(1), read_int(2), ret_val); + break; +case SYS_close: + printf("[%d|%s] close(fd = %ld) = %ld\n", p->pid, p->name, read_int(0), ret_val); + break; +case SYS_kill: + printf("[%d|%s] kill(pid = %ld) = %ld\n", p->pid, p->name, read_int(0), ret_val); + break; +case SYS_exec: + printf("[%d|%s] exec(path = %p, argv = %p) = %ld\n", p->pid, p->name, read_ptr(0), read_ptr(1), ret_val); + break; +case SYS_open: + printf("[%d|%s] open(path = %s, mode = %ld) = %ld\n", p->pid, p->name, read_str(0), read_int(1), ret_val); + break; +case SYS_mknod: + printf("[%d|%s] mknod(path = %s, major = %ld, minor = %ld) = %ld\n", p->pid, p->name, read_str(0), read_int(1), read_int(2), ret_val); + break; +case SYS_unlink: + printf("[%d|%s] unlink(path = %s) = %ld\n", p->pid, p->name, read_str(0), ret_val); + break; +case SYS_fstat: + printf("[%d|%s] fstat(fd = %ld, st = %p) = %ld\n", p->pid, p->name, read_int(0), read_ptr(1), ret_val); + break; +case SYS_link: + printf("[%d|%s] link(old_path = %s, new_path = %s) = %ld\n", p->pid, p->name, read_str(0), read_str(1), ret_val); + break; +case SYS_mkdir: + printf("[%d|%s] mkdir(path = %s) = %ld\n", p->pid, p->name, read_str(0), ret_val); + break; +case SYS_chdir: + printf("[%d|%s] chdir(path = %s) = %ld\n", p->pid, p->name, read_str(0), ret_val); + break; +case SYS_dup: + printf("[%d|%s] dup(fd = %ld) = %ld\n", p->pid, p->name, read_int(0), ret_val); + break; +case SYS_getpid: + printf("[%d|%s] getpid() = %ld\n", p->pid, p->name, ret_val); + break; +case SYS_sbrk: + printf("[%d|%s] sbrk(int = %ld) = %p\n", p->pid, p->name, read_int(0), (void *) ret_val); + break; +case SYS_pause: + printf("[%d|%s] pause(int = %ld) = %ld\n", p->pid, p->name, read_int(0), ret_val); + break; +case SYS_uptime: + printf("[%d|%s] uptime() = %ld\n", p->pid, p->name, ret_val); + break; +case SYS_shutdown: + printf("[%d|%s] shutdown() = %ld\n", p->pid, p->name, ret_val); + break; +case SYS_reboot: + printf("[%d|%s] reboot() = %ld\n", p->pid, p->name, ret_val); + break; +case SYS_clock: + printf("[%d|%s] clock() = %ld\n", p->pid, p->name, ret_val); + break; +case SYS_strace_on: + printf("[%d|%s] strace_on() = %ld\n", p->pid, p->name, ret_val); + break; +case SYS_wait2: + printf("[%d|%s] wait2(status = %p, counter = %p) = %ld\n", p->pid, p->name, read_ptr(0), read_ptr(1), ret_val); + break; + } +} diff --git a/kernel/syscall.c b/kernel/syscall.c index 076d965..fc2075c 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -6,6 +6,7 @@ #include "proc.h" #include "syscall.h" #include "defs.h" +#include "strace.h" // Fetch the uint64 at addr from the current process. int @@ -101,6 +102,15 @@ extern uint64 sys_unlink(void); extern uint64 sys_link(void); extern uint64 sys_mkdir(void); extern uint64 sys_close(void); +extern uint64 sys_shutdown(void); +extern uint64 sys_reboot(void); +extern uint64 sys_clock(void); +extern uint64 sys_strace_on(void); +extern uint64 sys_wait2(void); +extern uint64 sys_nice(void); +extern uint64 sys_getcwd(void); +extern uint64 sys_freemem(void); +extern uint64 sys_mmap(void); // An array mapping syscall numbers from syscall.h // to the function that handles the system call. @@ -126,6 +136,15 @@ static uint64 (*syscalls[])(void) = { [SYS_link] sys_link, [SYS_mkdir] sys_mkdir, [SYS_close] sys_close, +[SYS_shutdown] sys_shutdown, +[SYS_reboot] sys_reboot, +[SYS_clock] sys_clock, +[SYS_strace_on] sys_strace_on, +[SYS_wait2] sys_wait2, +[SYS_nice] sys_nice, +[SYS_getcwd] sys_getcwd, +[SYS_freemem] sys_freemem, +[SYS_mmap] sys_mmap, }; void @@ -138,7 +157,14 @@ syscall(void) if(num > 0 && num < NELEM(syscalls) && syscalls[num]) { // Use num to lookup the system call function for num, call it, // and store its return value in p->trapframe->a0 - p->trapframe->a0 = syscalls[num](); + p->counter++; // increase sys call counter + + uint64 ptr = syscalls[num](); + + if (p->tracing) { + strace(p, num, ptr); + } + p->trapframe->a0 = ptr; } else { printf("%d %s: unknown sys call %d\n", p->pid, p->name, num); diff --git a/kernel/syscall.h b/kernel/syscall.h index 3dd926d..5e11c4c 100644 --- a/kernel/syscall.h +++ b/kernel/syscall.h @@ -20,3 +20,12 @@ #define SYS_link 19 #define SYS_mkdir 20 #define SYS_close 21 +#define SYS_shutdown 22 +#define SYS_reboot 23 +#define SYS_clock 24 +#define SYS_strace_on 25 +#define SYS_wait2 26 +#define SYS_nice 27 +#define SYS_getcwd 28 +#define SYS_freemem 29 +#define SYS_mmap 30 diff --git a/kernel/sysfile.c b/kernel/sysfile.c index d8234ce..e6ca5ba 100644 --- a/kernel/sysfile.c +++ b/kernel/sysfile.c @@ -406,6 +406,34 @@ sys_mknod(void) return 0; } +uint64 +sys_getcwd(void) +{ + uint64 ubuf; + int sz; + + begin_op(); + argaddr(0, &ubuf); + argint(1, &sz); + + if (sz > PGSIZE) { + end_op(); + return -1; + } + + char *buf = kalloc(); + getcwd(buf, sz); + + if (copyout(myproc()->pagetable, ubuf, buf, sz) < 0) { + end_op(); + return -1; + } + + kfree(buf); + end_op(); + return 0; +} + uint64 sys_chdir(void) { diff --git a/kernel/sysproc.c b/kernel/sysproc.c index 3044d00..58038e7 100644 --- a/kernel/sysproc.c +++ b/kernel/sysproc.c @@ -33,7 +33,16 @@ sys_wait(void) { uint64 p; argaddr(0, &p); - return kwait(p); + return kwait2(p, 0); +} + +uint64 +sys_wait2(void) +{ + uint64 p, p2; + argaddr(0, &p); + argaddr(1, &p2); + return kwait2(p, p2); } uint64 @@ -105,3 +114,96 @@ sys_uptime(void) release(&tickslock); return xticks; } + +// shutdown +uint64 +sys_shutdown(void) +{ + volatile uint32 *test_dev = (uint32 *) VIRT_TEST; + *test_dev = 0x5555; + + return 0; +} + +// reboot +uint64 +sys_reboot(void) +{ + volatile uint32 *test_dev = (uint32 *) VIRT_TEST; + *test_dev = 0x7777; + + return 0; +} + +uint64 +sys_clock(void) +{ + volatile uint32 *base = (uint32*)GOLDFISH; + + uint32 first = base[0]; // low 32 bits + uint32 second = base[1]; // high 32 + + return ((uint64)second << 32) | first; +} + +uint64 +sys_strace_on(void) +{ + struct proc *p = myproc(); + p->tracing = 1; + return 0; +} + +uint64 +sys_nice(void) +{ + int n; + argint(0, &n); + + if (n < 0) n = 0; + if (n > 3) n = 3; + + struct proc *p = myproc(); + acquire(&p->lock); + + p->nice = n; + p->priority = 3 - n; + release(&p->lock); + + return p->priority; +} + +uint64 +sys_freemem(void) +{ + uint64 pages = kfreepages(); + return pages * (PGSIZE / 1024); +} + +uint64 +sys_mmap(void) +{ + struct proc *p = myproc(); + + void *pa = kalloc(); + if(pa == 0) + return (uint64)-1; + + memset(pa, 0, PGSIZE); + + uint64 va = p->mmap - PGSIZE; + + if(mappages(p->pagetable, va, PGSIZE, (uint64)pa, + PTE_R | PTE_W | PTE_U) < 0){ + kfree(pa); + return (uint64)-1; + } + + if(walkaddr(p->pagetable, va) != (uint64)pa) + panic("sys_mmap: mapping error"); + + p->mmap = va; + p->mmap_pages++; + + return va; +} diff --git a/mkfs/mkfs.c b/mkfs/mkfs.c index 5e3873e..67eb79d 100644 --- a/mkfs/mkfs.c +++ b/mkfs/mkfs.c @@ -4,18 +4,12 @@ #include #include #include -#include -#include -#include -#define dirent xv6_dirent #define stat xv6_stat // avoid clash with host struct stat #include "kernel/types.h" #include "kernel/fs.h" #include "kernel/stat.h" #include "kernel/param.h" -#undef dirent -#undef stat #ifndef static_assert #define static_assert(a, b) do { switch (0) case 0: case (a): ; } while (0) @@ -26,9 +20,9 @@ // Disk layout: // [ boot block | sb block | log | inode blocks | free bit map | data blocks ] -int nbitmap = FSSIZE/(BSIZE*8) + 1; +int nbitmap = FSSIZE/BPB + 1; int ninodeblocks = NINODES / IPB + 1; -int nlog = LOGBLOCKS; +int nlog = LOGBLOCKS+1; // Header followed by LOGBLOCKS data blocks. int nmeta; // Number of meta blocks (boot, sb, nlog, inode, bitmap) int nblocks; // Number of data blocks @@ -71,88 +65,12 @@ xint(uint x) return y; } -void -add_file(int parent_fd, uint parentino, char *filename) -{ - uint inum; - int fd; - char buf[BSIZE]; - struct xv6_dirent de; - - if((fd = openat(parent_fd, filename, 0)) < 0) - die(filename); - - inum = ialloc(T_FILE); - - bzero(&de, sizeof(de)); - de.inum = xshort(inum); - strncpy(de.name, filename, DIRSIZ); - iappend(parentino, &de, sizeof(de)); - - ssize_t cc; - while((cc = read(fd, buf, sizeof(buf))) > 0) - iappend(inum, buf, cc); - - close(fd); -} - -uint -add_dir(int level, int parent_fd, uint parentino, char *dirname) -{ - struct xv6_dirent de; - uint dino = ialloc(T_DIR); - bzero(&de, sizeof(de)); - de.inum = xshort(dino); - strcpy(de.name, dirname); - iappend(parentino, &de, sizeof(de)); - - bzero(&de, sizeof(de)); - de.inum = xshort(dino); - strcpy(de.name, "."); - iappend(dino, &de, sizeof(de)); - - bzero(&de, sizeof(de)); - de.inum = xshort(parentino); - strcpy(de.name, ".."); - iappend(dino, &de, sizeof(de)); - - int dir_fd = -1; - if ((dir_fd = openat(parent_fd, dirname, O_RDONLY)) == -1) { - perror("open"); - return dino; - } - - DIR *d = fdopendir(dir_fd); - if (d == NULL) { - perror("fdopendir"); - return dino; - } - - struct dirent *e; - while ((e = readdir(d)) != NULL) { - if (e->d_name[0] == '.') { - continue; - } - - if (e->d_type == DT_REG) { - printf("%*s+ %s\n", level * 2, "", e->d_name); - add_file(dir_fd, dino, e->d_name); - } else if (e->d_type == DT_DIR) { - printf("%*s+ /%s\n", level * 2, "", e->d_name); - add_dir(level + 1, dir_fd, dino, e->d_name); - } - } - close(dir_fd); - - return dino; -} - int main(int argc, char *argv[]) { int i, cc, fd; uint rootino, inum, off; - struct xv6_dirent de; + struct dirent de; char buf[BSIZE]; struct dinode din; @@ -165,9 +83,9 @@ main(int argc, char *argv[]) } assert((BSIZE % sizeof(struct dinode)) == 0); - assert((BSIZE % sizeof(struct xv6_dirent)) == 0); + assert((BSIZE % sizeof(struct dirent)) == 0); - fsfd = open(argv[1], O_RDWR|O_CREAT|O_TRUNC, 0666); + fsfd = open(argv[1], O_RDWR|O_CREATE|O_TRUNC, 0666); if(fsfd < 0) die(argv[1]); @@ -184,7 +102,7 @@ main(int argc, char *argv[]) sb.inodestart = xint(2+nlog); sb.bmapstart = xint(2+nlog+ninodeblocks); - printf("nmeta %d (boot, super, log blocks %u inode blocks %u, bitmap blocks %u) blocks %d total %d\n", + printf("nmeta %d (boot, super, log blocks %u, inode blocks %u, bitmap blocks %u) blocks %d total %d\n", nmeta, nlog, ninodeblocks, nbitmap, nblocks, FSSIZE); freeblock = nmeta; // the first free block that we can allocate @@ -210,13 +128,6 @@ main(int argc, char *argv[]) iappend(rootino, &de, sizeof(de)); for(i = 2; i < argc; i++){ - struct stat sb; - stat(argv[i], &sb); - if (S_ISDIR(sb.st_mode)) { - add_dir(0, AT_FDCWD, rootino, argv[i]); - continue; - } - // get rid of "user/" char *shortname; if(strncmp(argv[i], "user/", 5) == 0) @@ -236,6 +147,8 @@ main(int argc, char *argv[]) if(shortname[0] == '_') shortname += 1; + assert(strlen(shortname) <= DIRSIZ); + inum = ialloc(T_FILE); bzero(&de, sizeof(de)); @@ -327,7 +240,7 @@ balloc(int used) int i; printf("balloc: first %d blocks have been allocated\n", used); - assert(used < BSIZE*8); + assert(used < BPB); bzero(buf, BSIZE); for(i = 0; i < used; i++){ buf[i/8] = buf[i/8] | (0x1 << (i%8)); diff --git a/user/sh.c b/user/sh.c index 278fb37..f5454c6 100644 --- a/user/sh.c +++ b/user/sh.c @@ -77,6 +77,14 @@ runcmd(struct cmd *cmd) if(ecmd->argv[0] == 0) exit(1); exec(ecmd->argv[0], ecmd->argv); + + if (strchr(ecmd->argv[0], '/') == 0) { + char buf[128]; + buf[0] = '/'; + strcpy(buf+1, ecmd->argv[0]); + exec(buf, ecmd->argv); + } + fprintf(2, "exec %s failed\n", ecmd->argv[0]); break; @@ -132,9 +140,12 @@ runcmd(struct cmd *cmd) } int -getcmd(char *buf, int nbuf) +getcmd(char *buf, int nbuf, int is_script) { - write(2, "$ ", 2); + if (!is_script) { + write(2, "$ ", 2); + } + memset(buf, 0, nbuf); gets(buf, nbuf); if(buf[0] == 0) // EOF @@ -143,10 +154,11 @@ getcmd(char *buf, int nbuf) } int -main(void) +main(int argc, char *argv[]) { static char buf[100]; int fd; + int is_script = 0; // flag to check if we are running a script // Ensure that three file descriptors are open. while((fd = open("console", O_RDWR)) >= 0){ @@ -156,12 +168,22 @@ main(void) } } + if (argc > 1) { + is_script = 1; + close(0); // we close stdin + if (open(argv[1], O_RDONLY) < 0) { + fprintf(2, "sh: cannot open %s\n", argv[1]); + exit(1); + } + } + + // Read and run input commands. - while(getcmd(buf, sizeof(buf)) >= 0){ + while(getcmd(buf, sizeof(buf), is_script) >= 0){ char *cmd = buf; while (*cmd == ' ' || *cmd == '\t') - cmd++; - if (*cmd == '\n') // is a blank command + cmd++; + if (*cmd == '\n' || *cmd == '#') // is a blank command continue; if(cmd[0] == 'c' && cmd[1] == 'd' && cmd[2] == ' '){ // Chdir must be called by the parent, not the child. diff --git a/user/user.h b/user/user.h index ac84de9..718b656 100644 --- a/user/user.h +++ b/user/user.h @@ -5,25 +5,34 @@ struct stat; // system calls int fork(void); int exit(int) __attribute__((noreturn)); -int wait(int*); -int pipe(int*); -int write(int, const void*, int); -int read(int, void*, int); -int close(int); -int kill(int); -int exec(const char*, char**); -int open(const char*, int); -int mknod(const char*, short, short); -int unlink(const char*); -int fstat(int fd, struct stat*); -int link(const char*, const char*); -int mkdir(const char*); -int chdir(const char*); -int dup(int); +int wait(int *status); +int pipe(int *pipefd); +int write(int fd, const void *buf, int count); +int read(int fd, void *buf, int count); +int close(int fd); +int kill(int pid); +int exec(const char* path, char **argv); +int open(const char* path, int mode); +int mknod(const char* path, short major, short minor); +int unlink(const char* path); +int fstat(int fd, struct stat* st); +int link(const char* old_path, const char* new_path); +int mkdir(const char* path); +int chdir(const char* path); +int dup(int fd); int getpid(void); -char* sys_sbrk(int,int); +char* sys_sbrk(int n, int incr); int pause(int); int uptime(void); +int shutdown(void); +int reboot(void); +int clock(void); +int strace_on(void); +int wait2(int *status, int *counter); +int nice(int n); +int getcwd(char *, int); +int freemem(void); +void* mmap(void); // ulib.c int stat(const char*, struct stat*); @@ -39,6 +48,7 @@ int memcmp(const void *, const void *, uint); void *memcpy(void *, const void *, uint); char* sbrk(int); char* sbrklazy(int); +int getline(char **buf, int sizep, int fd); // printf.c void fprintf(int, const char*, ...) __attribute__ ((format (printf, 2, 3))); @@ -47,3 +57,8 @@ void printf(const char*, ...) __attribute__ ((format (printf, 1, 2))); // umalloc.c void* malloc(uint); void free(void*); +void* calloc(uint, uint); +void* realloc(void*, uint); +void malloc_name(void *ptr, const char *name); +void malloc_print(void); +void malloc_setfsm(int); diff --git a/user/usys.pl b/user/usys.pl index c5d4c3a..983c857 100755 --- a/user/usys.pl +++ b/user/usys.pl @@ -42,3 +42,12 @@ sub entry { entry("sbrk"); entry("pause"); entry("uptime"); +entry("shutdown"); +entry("reboot"); +entry("clock"); +entry("strace_on"); +entry("wait2"); +entry("nice"); +entry("getcwd"); +entry("freemem"); +entry("mmap"); From db273526ccd6c51a6142088ecc051208a7b5f11f Mon Sep 17 00:00:00 2001 From: Kailash Turimella Date: Tue, 30 Dec 2025 08:34:43 +0530 Subject: [PATCH 3/6] . --- kernel/kalloc.c | 68 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 62 insertions(+), 6 deletions(-) diff --git a/kernel/kalloc.c b/kernel/kalloc.c index 0699e7e..da58887 100644 --- a/kernel/kalloc.c +++ b/kernel/kalloc.c @@ -9,6 +9,16 @@ #include "riscv.h" #include "defs.h" +// Store a reference count for each physical page +static int ref_count[(PHYSTOP - KERNBASE) / PGSIZE]; + +// Convert physical address to index in reference count array +static inline int +pa2idx(uint64 pa) +{ + return (pa - KERNBASE) / PGSIZE; +} + void freerange(void *pa_start, void *pa_end); extern char end[]; // first address after kernel. @@ -23,6 +33,15 @@ struct { struct run *freelist; } kmem; +void +incref(uint64 pa) +{ + int idx = pa2idx(pa); + acquire(&kmem.lock); + ref_count[idx]++; + release(&kmem.lock); +} + void kinit() { @@ -34,7 +53,7 @@ void freerange(void *pa_start, void *pa_end) { char *p; - p = (char*)PGROUNDUP((uint64)pa_start); + p = (char*)PGROUNDUP((uint64)pa_start); for(; p + PGSIZE <= (char*)pa_end; p += PGSIZE) kfree(p); } @@ -47,18 +66,35 @@ void kfree(void *pa) { struct run *r; + uint64 addr = (uint64)pa; - if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP) + if((addr % PGSIZE) != 0 || addr < (uint64)end || addr >= PHYSTOP) panic("kfree"); + int idx = pa2idx(addr); + + acquire(&kmem.lock); + + if(ref_count[idx] < 0) + panic("kfree: ref_count underflow"); + + if(ref_count[idx] > 0){ + // normal case: page was allocated by kalloc + ref_count[idx]--; + if(ref_count[idx] > 0){ + // still in use somewhere else – don't free yet + release(&kmem.lock); + return; + } + } + // Fill with junk to catch dangling refs. memset(pa, 1, PGSIZE); r = (struct run*)pa; - - acquire(&kmem.lock); r->next = kmem.freelist; kmem.freelist = r; + release(&kmem.lock); } @@ -72,11 +108,31 @@ kalloc(void) acquire(&kmem.lock); r = kmem.freelist; - if(r) + if(r){ kmem.freelist = r->next; + ref_count[pa2idx((uint64)r)] = 1; // << MUST be inside lock + } release(&kmem.lock); if(r) - memset((char*)r, 5, PGSIZE); // fill with junk + memset((char*)r, 5, PGSIZE); + return (void*)r; +} + +uint64 +kfreepages(void) +{ + uint64 num_pages = 0; + struct run *r; + + acquire(&kmem.lock); + r = kmem.freelist; + while (r) { + num_pages++; + r = r->next; + } + release(&kmem.lock); + + return num_pages; } From 20c1fdc0dced0d328e2fcc95fc228c72cbb6b351 Mon Sep 17 00:00:00 2001 From: Kailash Turimella Date: Tue, 30 Dec 2025 08:39:32 +0530 Subject: [PATCH 4/6] . --- kernel/defs.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernel/defs.h b/kernel/defs.h index fdec535..81d57ba 100644 --- a/kernel/defs.h +++ b/kernel/defs.h @@ -53,12 +53,15 @@ int readi(struct inode*, int, uint64, uint, uint); void stati(struct inode*, struct stat*); int writei(struct inode*, int, uint64, uint, uint); void itrunc(struct inode*); +int getcwd(char *, uint); void ireclaim(int); // kalloc.c void* kalloc(void); void kfree(void *); void kinit(void); +uint64 kfreepages(void); +void incref(uint64 pa); // log.c void initlog(int, struct superblock*); @@ -97,6 +100,7 @@ void sched(void); void sleep(void*, struct spinlock*); void userinit(void); int kwait(uint64); +int kwait2(uint64, uint64); void wakeup(void*); void yield(void); int either_copyout(int user_dst, uint64 dst, void *src, uint64 len); From c0059dc8d7b4b5b7094af72c6dae08087cdb0728 Mon Sep 17 00:00:00 2001 From: Kailash Turimella Date: Wed, 31 Dec 2025 05:47:42 -0800 Subject: [PATCH 5/6] completed --- Makefile | 6 +- generate-traces.sh | 147 +++++++++++++ kernel/param.h | 2 +- mkfs/mkfs.c | 2 +- test.sh | 9 + tm.txt | 500 +++++++++++++++++++++++++++++++++++++++++++++ user/about.c | 13 ++ user/benchmark.c | 40 ++++ user/broken.c | 324 +++++++++++++++++++++++++++++ user/catlines.c | 40 ++++ user/clear.c | 8 + user/clock.c | 13 ++ user/fnr.c | 29 +++ user/freemem.c | 11 + user/leetify.c | 154 ++++++++++++++ user/mmap.c | 17 ++ user/mmaptest.c | 53 +++++ user/mt70.c | 49 +++++ user/mt80.c | 49 +++++ user/mt90.c | 90 ++++++++ user/nice.c | 32 +++ user/opt_cat.c | 59 ++++++ user/pwd.c | 11 + user/reboot.c | 14 ++ user/shell.c | 389 +++++++++++++++++++++++++++++++++++ user/shutdown.c | 14 ++ user/spinner.c | 28 +++ user/strace.c | 21 ++ user/tolower.c | 12 ++ user/ulib.c | 66 ++++++ user/umalloc.c | 485 ++++++++++++++++++++++++++++++++++++------- user/user.h | 12 +- user/usys.pl | 0 33 files changed, 2613 insertions(+), 86 deletions(-) create mode 100755 generate-traces.sh create mode 100644 test.sh create mode 100644 tm.txt create mode 100644 user/about.c create mode 100644 user/benchmark.c create mode 100644 user/broken.c create mode 100644 user/catlines.c create mode 100644 user/clear.c create mode 100644 user/clock.c create mode 100644 user/fnr.c create mode 100644 user/freemem.c create mode 100644 user/leetify.c create mode 100644 user/mmap.c create mode 100644 user/mmaptest.c create mode 100644 user/mt70.c create mode 100644 user/mt80.c create mode 100644 user/mt90.c create mode 100644 user/nice.c create mode 100644 user/opt_cat.c create mode 100644 user/pwd.c create mode 100644 user/reboot.c create mode 100644 user/shell.c create mode 100644 user/shutdown.c create mode 100644 user/spinner.c create mode 100644 user/strace.c create mode 100644 user/tolower.c mode change 100755 => 100644 user/usys.pl diff --git a/Makefile b/Makefile index 9496185..430d995 100644 --- a/Makefile +++ b/Makefile @@ -127,7 +127,6 @@ mkfs/mkfs: mkfs/mkfs.c $K/fs.h $K/param.h UPROGS=\ $U/_about\ - $U/_barca\ $U/_benchmark\ $U/_broken\ $U/_cat\ @@ -158,6 +157,7 @@ UPROGS=\ $U/_rm\ $U/_reboot\ $U/_sh\ + $U/_shell\ $U/_shutdown\ $U/_spinner\ $U/_strace\ @@ -171,8 +171,8 @@ UPROGS=\ $U/_forphan\ $U/_dorphan\ -fs.img: mkfs/mkfs README.md tm.txt input.txt $(UPROGS) script.sh spin1.sh spin2.sh 1.sh 2.sh - mkfs/mkfs fs.img README.md tm.txt input.txt $(UPROGS) script.sh spin1.sh spin2.sh 1.sh 2.sh +fs.img: mkfs/mkfs README.md tm.txt $(UPROGS) 1.sh 2.sh 3.sh 4.sh test.sh + mkfs/mkfs fs.img README.md tm.txt $(UPROGS) 1.sh 2.sh 3.sh 4.sh test.sh -include kernel/*.d user/*.d diff --git a/generate-traces.sh b/generate-traces.sh new file mode 100755 index 0000000..2207f29 --- /dev/null +++ b/generate-traces.sh @@ -0,0 +1,147 @@ +#!/usr/bin/env bash +# This script is designed to be run on a Linux host (i.e., not in xv6) +# You should probably run this script to see what it does first. To do that, you +# may need to make it executable: +# +# chmod +x generate-traces.sh +# +# And then run it from the *root* of your OS directory. +# +# +# For each user space system call stub, this script will print out information +# about its name, arguments, and return values. +# +# You can add information to the stubs that this script will extract. For +# example, instead of: +# +# int write(int, const void *, int); +# +# You can add argument names: +# +# int write(int fd, const void *buf, int count); +# +# And this script will extract them. By default, pointers are assumed to be +# addresses, but if you want the script to treat them like strings you can add a +# bit of metadata with a C comment: +# +# int mkdir(const char* /*str*/ path); +# +# Here's a few examples from a user/user.h file: +# int exit(int status) __attribute__((noreturn)); +# int wait(int *status); +# int pipe(int *pipefd); +# int mkdir(const char* /*str*/ path); + + +# The following code extracts function information from user/user.h based on the +# system calls found in user/usys.pl. + +# Retrieves the type of the argument provided as a printf format specifier +get_type() { + case "${1}" in + *'/*str*/'*) echo "%s" ;; + *"*"*) echo "%p" ;; + *) echo "%ld" ;; # default to int + esac +} + +# Determines which function to call for a given format specifier. +get_conversion() { + case "${1}" in + '%p') echo "read_ptr(${2})" ;; + '%s') echo "read_str(${2})" ;; + '%ld') echo "read_int(${2})" ;; + esac +} + +# Inspects the user/user.h file to extract system call information +syscall_output=$( +for i in $(sed -n 's/entry("\(.*\)");/\1/p' user/usys.pl); do + return_value=$(sed -nE \ + "s:\s*([A-Za-z_][A-Za-z0-9_\* ]+)\s+${i}.*:\1:p;" user/user.h) + args=$(sed 's/const//g; s/void//g; s/struct//g; s/((noreturn))//g' user/user.h \ + | sed -nE "s:.*\s${i}\((.*)\).*:\1:p") + + # Extract argument names and types for printing + arg_list="" + arg_convs="" + arg_count=0 + export IFS="," + for arg in $args; do + arg_name=$(echo "${arg}" | sed -nE 's:.*\b([A-Za-z0-9_]+)\s*:\1:p') + arg_type=$(get_type "${arg}") + arg_conv=$(get_conversion "${arg_type}" "${arg_count}") + arg_list="${arg_list}${arg_name} = ${arg_type}, " + arg_convs="${arg_convs}${arg_conv}, " + (( arg_count++ )) + done + + # Remove extra ', ' + [[ "${arg_count}" -gt 0 ]] && arg_list="${arg_list::-2}" + + # cast the return type, if necessary + return_type="$(get_type "${return_value}")" + cast_ret="" + if [[ "${return_type}" == "%p" ]]; then + cast_ret="(void *)" + fi + + # Print out the C code to display system call information. You will probably + # need to modify this so it can be used to conditionally print out the + # appropriate info. + echo "case SYS_${i}:" + echo " printf(\"[%d|%s] ${i}(${arg_list}) = $(get_type "${return_value}")\\n\", p->pid, p->name, ${arg_convs} ${cast_ret} ret_val);" + echo " break;" +done +) + +# The information we want has been extracted and stored in $syscall_output. +# Using this information, we'll generate C code below. When we run 'cat < [args...]\n", argv[0]); + exit(1); + } + + uint64 start = clock(); + + int pid = fork(); + if (pid < 0) { + fprintf(2, "benchmark: fork failed\n"); + exit(1); + } + if (pid == 0) { + exec(argv[1], &argv[1]); + fprintf(2, "benchmark: exec %s failed\n", argv[1]); + exit(1); + } + + int status; + int counter; + + if (wait2(&status, &counter) < 0) { + fprintf(2, "benchmark: wait2 failed\n"); + exit(1); + } + + uint64 end = clock(); + uint64 time_taken = end - start; + uint64 time_taken_ms = time_taken / 1000000ULL; + + printf("Benchmark Complete\n"); + printf("Time Elapsed: %d ms\n", (int)time_taken_ms); + printf("System Calls: %d\n", (int)counter); + exit(0); +} diff --git a/user/broken.c b/user/broken.c new file mode 100644 index 0000000..779f9ed --- /dev/null +++ b/user/broken.c @@ -0,0 +1,324 @@ +/** + * @file broken.c + * @author mmalensek + * + * This program contains a series of buggy, broken, or strange C functions for + * you to ponder. Your job is to analyze each function, fix whatever bugs the + * functions might have, and then explain what went wrong. Sometimes the + * compiler will give you a hint. + * + * ____________ + * < Good luck! > + * ------------ + * \ ^__^ + * \ (oo)\_______ + * (__)\ )\/\ + * ||----w | + * || || + */ + +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + + +static int func_counter = 1; +#define FUNC_START() printf("\n\n%d.) %s\n", func_counter++, __func__); + +#pragma GCC diagnostic ignored "-Waggressive-loop-optimizations" +#pragma GCC diagnostic ignored "-Wdangling-pointer" +#pragma GCC diagnostic ignored "-Wformat-overflow" + +/** + * This code example was taken from the book 'Mastering C Pointers,' one of the + * not so good ways to learn pointers. It was trying to demonstrate how to print + * 'BNGULAR'... with pointers...? Maybe? + * + * (1) Fix the problem. + * (2) Explain what's wrong with this code: + * String literals are stored in read only memory + * '66' in this example is 'B'. What we can do is set a[0] = 'B' to replace the first char in the string. + */ +void +angular(void) +{ + FUNC_START(); + + char a[] = "ANGULAR"; + a[0] = 'B'; + printf("%s\n", a); +} + +/** + * This function is the next step after 'Hello world' -- it takes user input and + * prints it back out! (Wow). + * + * But, unfortunately, it doesn't work. + * + * (1) Fix the problem. + * (2) Explain what's wrong with this code: + * + * The main problem for this one is that we have to allocate space + for the string. I tried to change gets to fgets but I don't think + that works because using stdio.h didn't work. + + */ +void +greeter(void) +{ + FUNC_START(); + + char name[128]; + + printf("Please enter your name: "); + gets(name, 128); + + // Remove newline character + char *p = name; + for ( ; *p != '\n' && *p != 0; p++) { } + *p = '\0'; + + printf("Hello, %s!\n", name); +} + +/** + * This code isn't so much broken, but more of an exercise in understanding how + * C data types are represented in memory. + * + * (1) Fill in your guesses below to see if you can get all 6 correct on the + * first try. + * (2) Explain *WHY* you get the sizes you do for each of the 6 entries. + * sizeof(int) and sizeof(float) are fairly straightforward. + + A char is always 1. Both ints and floats are 4. + things is 48 because it is an array of 12 ints. so 12 x 4 = 48 + 'A' is an int so thats why its 4 bytes. + "A" is a string literal so it is 2 bytes. The null terminator "\0" is included in there. + * + */ +#define SIZE_CHECK(sz, guess) (sz), sz == guess ? "Right!" : "Wrong!" +void +sizer(void) +{ + FUNC_START(); + + int guesses[] = {1, 4, 4, 48, 4, 2 }; // fill in 6 guesses here + if (sizeof(guesses) / sizeof(int) != 6) { + printf("Please enter a guess for each of the sizes below.\n"); + printf("Example: guesses[] = { 1, 4, 0, 0, 0, 0 }\n"); + printf("would mean sizeof(char) == 1, sizeof(int) == 4, and so on"); + return; + } + + int things[12] = { 0 }; + printf("sizeof(char) = %ld\t[%s]\n", SIZE_CHECK(sizeof(char), guesses[0])); + printf("sizeof(int) = %ld\t[%s]\n", SIZE_CHECK(sizeof(int), guesses[1])); + printf("sizeof(float) = %ld\t[%s]\n", SIZE_CHECK(sizeof(float), guesses[2])); + printf("sizeof(things) = %ld\t[%s]\n", SIZE_CHECK(sizeof(things), guesses[3])); + printf("sizeof('A') = %ld\t[%s]\n", SIZE_CHECK(sizeof('A'), guesses[4])); + printf("sizeof(\"A\") = %ld\t[%s]\n", SIZE_CHECK(sizeof("A"), guesses[5])); +} + +/** + * This 'useful' function prints out an array of integers with their indexes, or + * at least tries to. It even has a special feature where it adds '12' to the + * array. + * + * (1) Fix the problem. + * (2) Explain what's wrong with this code: + * + * One problem I found is that the loop is until < 1000, which exceeds the size we set for + the stuff array which is 100. + Another problem or inconvinience is the 14[stuff + 1] = 12. At first I didn't understand it, + but after searching online what it means. It basically just sets 12 to index 15. + */ +void +displayer(void) +{ + FUNC_START(); + + int stuff[100] = { 0 }; + + /* Can you guess what the following does without running the program? */ + /* Rewrite it so it's easier to read. */ + stuff[15] = 12; + + for (int i = 0; i < 100; ++i) { + printf("%d: %d\n", i, stuff[i]); + } +} + +/** + * Adds up the contents of an array and prints the total. Unfortunately the + * total is wrong! See main() for the arguments that were passed in. + * + * (1) Fix the problem. + * (2) Explain what's wrong with this code: + * + * The problem is that sizeof(arr) doesn't work as intended. It wouldn't get to the + end of the loop. To fix it I changed the function call so we can get the correct size of + array is use that to loop through. + */ +void +adder(int *arr, int n) +{ + FUNC_START(); + + int total = 0; + + for (int i = 0; i < n; ++i) { + total += arr[i]; + } + + printf("Total is: %d\n", total); +} + +/** + * This function is supposed to be somewhat like strcat, but it doesn't work. + * + * (1) Fix the problem. + * (2) Explain what's wrong with this code: + * The problem is that it returns a pointer to a local array. When the function returns, it finishes. + To fix we use malloc to allocate it on the heap. + * + */ +char * +suffixer(char *a, char *b) +{ + FUNC_START(); + + char *buf = malloc(strlen(a) + strlen(b) + 1); + if (!buf) return 0; + + char *buf_start = buf; + + strcpy(buf, a); + strcpy(buf + strlen(a), b); + return buf_start; + +} + +/** + * This is an excerpt of Elon Musk's Full Self Driving code. Unfortunately, it + * keeps telling people to take the wrong turn. Figure out how to fix it, and + * explain what's going on so Elon can get back to work on leaving Earth for + * good. + * + * (1) Fix the problem. + * (2) Explain what's wrong with this code: + * + * One problem is that in the if statement we aren't checking if its actually + true or not. So we have to add == 0 for it to work. The whole string comparison + part is also not needed. + + There is also 2 overflow issues. + */ +void +driver(void) +{ + FUNC_START(); + + char street1[10] = { "fulton" }; + char street2[10] = { "gomery" }; + char street3[10] = { "piedmont" }; + char street4[10] = { "baker" }; + char street5[10] = { "haight" }; + + if (strcmp(street1, street2) == 0) { + char *new_name = "saint agnew "; + memcpy(street4, new_name, strlen(new_name)); + } + + printf("Welcome to TeslaOS 0.1!\n"); + printf("Calculating route...\n"); + printf("Turn left at the intersection of %s and %s.\n", street5, street3); +} + +/** + * This function tokenizes a string by space, sort of like a basic strtok or + * strsep. It has two subtle memory bugs for you to find. + * + * (1) Fix the problem. + * (2) Explain what's wrong with this code: + * + * One problem was is a size bug. the malloc allocates a byte too few + so strcpy(line, str) overflows. + Once the scan hits '\0', if it advances it would go past the buffer. + + */ +void +tokenizer(void) +{ + FUNC_START(); + + char *str = "Hope was a letter I never could send"; + char *line = malloc(strlen(str) + 1); + char *base = line; + char *c = line; + + strcpy(line, str); + + while (*c != '\0') { + + for ( ; *c != ' ' && *c != '\0'; c++) { + // find the next space (or end of string) + } + + if (*c == '\0') { + printf("%s\n", line); + break; + } + + *c = '\0'; + printf("%s\n", line); + + line = c + 1; + c = line; + } + + free(base); +} + +/** +* This function should print one thing you like about C, and one thing you +* dislike about it. Assuming you don't mess up, there will be no bugs to find +* here! +*/ +void +finisher(void) +{ + FUNC_START(); + + // TODO + printf("Something I like about C is how you can control much more about how everything works.\n"); + printf("Something I don't like is that having a lot more control means more stuff can go wrong :( "); +} + +int +main(void) +{ + printf("Starting up!"); + + + angular(); + + greeter(); + + sizer(); + + displayer(); + + int nums[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512 }; + adder(nums, sizeof(nums) / sizeof(nums[0])); + + char *result = suffixer("kayak", "ing"); + printf("Suffixed: %s\n", result); + + driver(); + + tokenizer(); + + finisher(); + + return 0; +} diff --git a/user/catlines.c b/user/catlines.c new file mode 100644 index 0000000..9de4315 --- /dev/null +++ b/user/catlines.c @@ -0,0 +1,40 @@ +#include "kernel/fcntl.h" +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int +fgets7(char *buf, int max, int fd) +{ + int i, cc; + char c; + + for (i = 0; i + 1 < max; ) { + cc = read(fd, &c, 1); + if(cc < 1) + break; + buf[i++] = c; + if(c == '\n' || c == '\r') + break; + } + buf[i] = '\0'; + return i; +} + +int +main(int argc, char *argv[]) +{ + if (argc <= 1) { + fprintf(2, "Usage: %s filename\n", argv[0]); + return 1; + } + + int fd = open(argv[1], O_RDONLY); + char buf[128]; + int line_count = 0; + while (fgets7(buf, 128, fd) > 0 ) { + printf("Line %d: %s", line_count++, buf); + } + + return 0; +} diff --git a/user/clear.c b/user/clear.c new file mode 100644 index 0000000..b54b651 --- /dev/null +++ b/user/clear.c @@ -0,0 +1,8 @@ +#include "kernel/types.h" +#include "user/user.h" + +int +main(void) { + write(1, "\x1b[2J\x1b[H", 7); + exit(0); +} diff --git a/user/clock.c b/user/clock.c new file mode 100644 index 0000000..627a700 --- /dev/null +++ b/user/clock.c @@ -0,0 +1,13 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int +main(void) +{ + uint64 ns = clock(); // call the clock function + uint64 sec64 = ns / 1000000000ULL; // convert to seconds + printf("%d\n", (int)sec64); + exit(0); + +} diff --git a/user/fnr.c b/user/fnr.c new file mode 100644 index 0000000..140ecb2 --- /dev/null +++ b/user/fnr.c @@ -0,0 +1,29 @@ +#include "kernel/types.h" +#include "user/user.h" + +int main(int argc, char *argv[]) { + char *line = malloc(512); + while (getline(&line, 512, 0) > 0) { + for (int i = 1; i < argc; i += 2) { + char *find = argv[i]; + char *repl = argv[i + 1]; + uint find_len = strlen(find); + if (strlen(line) < find_len) { + continue; + } + for (int j = 0; j < strlen(line) - find_len; ++j) { + char *p = line + j; + char *q = find; + int k; + for (k = 0; *p && *p == *q && k < find_len; ++k) { + p++, q++; + } + if (k == find_len) { + memcpy(line + j, repl, find_len); + } + } + } + printf("%s", line); + } + return 0; +} diff --git a/user/freemem.c b/user/freemem.c new file mode 100644 index 0000000..0c87fb5 --- /dev/null +++ b/user/freemem.c @@ -0,0 +1,11 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int +main(void) +{ + int k = freemem(); + printf("%d KiB\n", k); + exit(0); +} diff --git a/user/leetify.c b/user/leetify.c new file mode 100644 index 0000000..b18fc2b --- /dev/null +++ b/user/leetify.c @@ -0,0 +1,154 @@ +/** + * @file leetify.c + * + * Scaffolding to create an amazing l337speak generator using only external + * commands combined with pipelines. + */ + +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" +#include "kernel/fcntl.h" + +/** + * Represents a command in a pipeline. + * Note that EITHER stdout_pipe can be true, or a stdout_file can be set. + * + * - tokens Array of strings that describe the command to run + * - stdout_pipe set to true if this command's output should be written to pipe + * - stdout_file set to a file name if this command's output should be written + * to a file + */ +struct command { + char **tokens; + int stdout_pipe; + char *stdout_file; +}; + +void +execute_pipeline(struct command *cmds, int n) +{ + + // go through all the commands + for (int i = 0; i < n - 1; i++) { + + int p[2]; // p[0] stdin, p[1] stdout + if (pipe(p) < 0) { + fprintf(2, "pipe failed\n"); + exit(1); + } + + int pid = fork(); + if (pid < 0) { + fprintf(2, "fork failed\n"); + exit(1); + } + + if (pid == 0) { + // child + + // redirect stdout to write end of pipe + close(p[0]); // close read + close(1); // redirect stdout -> pipe write end + dup(p[1]); + close(p[1]); + + // exec + exec(cmds[i].tokens[0], cmds[i].tokens); + + fprintf(2, "exec %s failed\n", cmds[i].tokens[0]); + exit(1); + } else { + // parent + + // close write end of pipe + close(p[1]); + + // redirect stdin + close(0); + dup(p[0]); + + // close copy of the read end + close(p[0]); + + } + } + struct command *final_command = &cmds[n-1]; + if (final_command->stdout_file) { + int fd = open(final_command->stdout_file, O_CREATE | O_WRONLY | O_TRUNC); + if (fd < 0) { + fprintf(2, "open failed for %s\n", final_command->tokens[0]); + exit(1); + } + close(1); + dup(fd); + close(fd); + } + + exec(final_command->tokens[0], final_command->tokens); + fprintf(2, "exec failed: %s\n", final_command->tokens[0]); + exit(1); + +} + +int +main(int argc, char *argv[]) +{ + char *input_file = (char *)0; + char *output_file = (char *) 0; + + if (argc < 2 || argc > 3) { + printf("Usage: %s file-to-leetify [output-file]\n", argv[0]); + return 1; + } + + input_file = argv[1]; + + if (argc == 3) { + output_file = argv[2]; + } + + printf("Input file: %s\n", input_file); + if (output_file) { + printf("Writing to output file: %s\n", output_file); + } + + // cat input file + char *command1[] = { "cat", input_file, (char *)0 }; + // tolower + char *command2[] = { "tolower", (char *)0 }; + // fnr + char *command3[] = { "fnr", "the", "teh", "a", "4", "e", "3", "i", "!", (char *)0 }; + // fnr 2 + char *command4[] = { "fnr", "l", "1", "o", "0", "s", "5", (char *)0 }; + + struct command cmds[4] = { 0 }; + cmds[0].tokens = command1; /* What this command runs */ + cmds[0].stdout_pipe = 1; + cmds[0].stdout_file = (char *)0; /* This command is not writing to a file. */ + + cmds[1].tokens = command2; + cmds[1].stdout_pipe = 1; + cmds[1].stdout_file = (char *)0; /* This command's output goes to a pipe. */ + + cmds[2].tokens = command3; + cmds[2].stdout_pipe = 1; + cmds[2].stdout_file = (char *)0; + + cmds[3].tokens = command4; + cmds[3].stdout_pipe = 0; /* Last command so set stdout_pipe = false */ + cmds[3].stdout_file = output_file; + + int pid = fork(); + if (pid == -1) { + fprintf(2, "Fork failed\n"); + return 1; + } else if (pid == 0) { + execute_pipeline(cmds, 4); + } else { + int status; + wait(&status); + } + + return 0; +} diff --git a/user/mmap.c b/user/mmap.c new file mode 100644 index 0000000..d3f8523 --- /dev/null +++ b/user/mmap.c @@ -0,0 +1,17 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int +main(void) +{ + char *p = (char*)mmap(); + if (!p) { + printf("mmap failed\n"); + exit(1); + } + + strcpy(p, "hello from mmap"); + printf("%s\n", p); + exit(0); +} diff --git a/user/mmaptest.c b/user/mmaptest.c new file mode 100644 index 0000000..f3477d9 --- /dev/null +++ b/user/mmaptest.c @@ -0,0 +1,53 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int +main(void) +{ + char *a = mmap(); + char *b = mmap(); + char *c = mmap(); + + strcpy(a, "a: hello"); + strcpy(b, "b: world"); + strcpy(c, "c: original"); + + printf("parent original:\n%s\n%s\n%s\n\n", a, b, c); + + + int pid = fork(); + + if(pid == 0){ + + printf("child sees after fork:\n%s\n%s\n%s\n\n", a, b, c); + + // child modifies first two pages + strcpy(a, "a: spurs"); + strcpy(b, "b: child changed world"); + + // forks and makes another child (grandchild) + int pid2 = fork(); + if(pid2 == 0){ + printf("grandchild sees:\n%s\n%s\n%s\n\n", a, b, c); + + // grandchild modifies the 3rd page + strcpy(c, "C: grandchild updated this"); + + printf("grandchild done.\n"); + exit(0); + } + + // child waits for grandchild to finish updating page C + wait(0); + + printf("child after grandchild:\n%s\n%s\n%s\n\n", a, b, c); + exit(0); + } + + wait(0); + + printf("parent final:\n%s\n%s\n%s\n\n", a, b, c); + + exit(0); +} diff --git a/user/mt70.c b/user/mt70.c new file mode 100644 index 0000000..4776483 --- /dev/null +++ b/user/mt70.c @@ -0,0 +1,49 @@ +#include "kernel/types.h" +#include "user/user.h" + +#pragma GCC diagnostic ignored "-Wunused-variable" + +int main(void) +{ +// malloc_setfsm(FSM_FIRST_FIT); + + char *a = malloc(132); + char *b = malloc(42); /* Will be deleted */ + char *c = malloc(132); + char *d = malloc(132); /* Will be deleted */ + char *e = malloc(132); + char *f = malloc(3132); + + /* These allocations total 4096 bytes after alignment is included. */ + + malloc_name(a, "A"); + malloc_name(b, "B"); + malloc_name(c, "C"); + malloc_name(d, "D"); + malloc_name(e, "E"); + malloc_name(f, "F"); + + free(b); + free(d); + + /** + * The following allocation will choose: + * - First fit: free space at the end of the page (unnamed, no variable) + * - Best fit: B + * - Worst fit: D + */ + char *g = malloc(42); + + malloc_name(g, "G"); + + malloc_print(); + + /** + * Things to check: + * - correct alignment (evenly divisible by 16) + * - free block sizes are correct (make sure not off by one) + * - end of last block's memory address - first address = 4096 + */ + + return 0; +} diff --git a/user/mt80.c b/user/mt80.c new file mode 100644 index 0000000..4776483 --- /dev/null +++ b/user/mt80.c @@ -0,0 +1,49 @@ +#include "kernel/types.h" +#include "user/user.h" + +#pragma GCC diagnostic ignored "-Wunused-variable" + +int main(void) +{ +// malloc_setfsm(FSM_FIRST_FIT); + + char *a = malloc(132); + char *b = malloc(42); /* Will be deleted */ + char *c = malloc(132); + char *d = malloc(132); /* Will be deleted */ + char *e = malloc(132); + char *f = malloc(3132); + + /* These allocations total 4096 bytes after alignment is included. */ + + malloc_name(a, "A"); + malloc_name(b, "B"); + malloc_name(c, "C"); + malloc_name(d, "D"); + malloc_name(e, "E"); + malloc_name(f, "F"); + + free(b); + free(d); + + /** + * The following allocation will choose: + * - First fit: free space at the end of the page (unnamed, no variable) + * - Best fit: B + * - Worst fit: D + */ + char *g = malloc(42); + + malloc_name(g, "G"); + + malloc_print(); + + /** + * Things to check: + * - correct alignment (evenly divisible by 16) + * - free block sizes are correct (make sure not off by one) + * - end of last block's memory address - first address = 4096 + */ + + return 0; +} diff --git a/user/mt90.c b/user/mt90.c new file mode 100644 index 0000000..c126462 --- /dev/null +++ b/user/mt90.c @@ -0,0 +1,90 @@ +#include "kernel/types.h" +#include "user/user.h" + +#pragma GCC diagnostic ignored "-Wunused-variable" + +void +merge_test(void) +{ + char *start = sbrk(0); + printf("Starting address: %p\n\n", start); + + void *a = malloc(532); malloc_name(a, "A"); + void *b = malloc(326); malloc_name(b, "B"); + void *c = malloc(282); malloc_name(c, "C"); + void *d = malloc(1032); malloc_name(d, "D"); + void *e = malloc(432); malloc_name(e, "E"); + + free(b); + free(c); + free(a); + free(d); + free(e); + + malloc_print(); + + printf("\n\n"); + + void *f = malloc(632); malloc_name(f, "F"); + void *g = malloc(182); malloc_name(f, "G"); + void *h = malloc(9928); malloc_name(f, "H"); + + free(f); + free(g); + free(h); + + malloc_print(); + + char *end = sbrk(0); + printf("\n\nEnding address: %p\n", end); + + if (start == end) { + printf("Success! start == end!\n"); + } +} + +void +realloc_test(void) +{ + char *a = malloc(532); malloc_name(a, "A"); + void *b = malloc(326); malloc_name(b, "B"); + void *c = malloc(282); malloc_name(c, "C"); + void *d = malloc(1032); malloc_name(d, "D"); + void *e = malloc(432); malloc_name(e, "E"); + + /* Does not actually need to be resized */ + a = realloc(a, 542); malloc_name(a, "R1"); + + /* No room, must free + allocate new */ + c = realloc(c, 9632); malloc_name(c, "R2"); + + /* Now that 'c' is free, we can expand 'b' into it */ + b = realloc(b, 632); malloc_name(b, "R3"); + + /* Expand into free space at the end of the region. This will produce a new + * free block out of the remaining space */ + e = realloc(e, 1312); malloc_name(e, "R4"); + + /* Shrink 'd' down to make a new free block */ + d = realloc(d, 832); malloc_name(d, "R5"); + + /* Shrink 'd' one more time to make sure that the two neighboring free + * blocks get merged back together */ + d = realloc(d, 632); malloc_name(d, "R6"); + + malloc_print(); +} + +int +main(void) +{ + printf("---- MERGE TEST ----\n"); + merge_test(); + printf("\n\n"); + + printf("---- REALLOC TEST ----\n"); + realloc_test(); + printf("\n\n"); + + return 0; +} diff --git a/user/nice.c b/user/nice.c new file mode 100644 index 0000000..fe0b3e6 --- /dev/null +++ b/user/nice.c @@ -0,0 +1,32 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int main(int argc, char *argv[]) { + if (argc < 3) { + fprintf(2, "usage: nice <0..3> cmd [args...]\n"); + exit(1); + } + int n = atoi(argv[1]); + if (n < 0) + n = 0; + if (n > 3) + n = 3; + + int pid = fork(); + + if (pid < 0) { + fprintf(2, "nice: fork failed\n"); + exit(1); + } + + if (pid == 0) { + nice(n); + exec(argv[2], &argv[2]); + fprintf(2, "nice: exec %s failed\n", argv[2]); + exit(1); + } + + wait(0); + exit(0); +} diff --git a/user/opt_cat.c b/user/opt_cat.c new file mode 100644 index 0000000..ed4b763 --- /dev/null +++ b/user/opt_cat.c @@ -0,0 +1,59 @@ +#include "kernel/fcntl.h" +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +static int +fgets3(char *out, int max, int fd) +{ + enum { CAP = 8192 }; + static char storage[CAP]; + static int r = 0, w = 0; + int outi = 0; + + for (;;) { + if (r >= w) { + r = w = 0; + int n = read(fd, storage, CAP); + if (n <= 0) { + if (outi == 0) return 0; + out[outi] = '\0'; + return outi; + } + w = n; + } + + while (r < w && outi + 1 < max) { + char c = storage[r++]; + out[outi++] = c; + if (c == '\n' || c == '\r') { + out[outi] = '\0'; + return outi; + } + } + + if (outi + 1 >= max) { + out[outi] = '\0'; + return outi; + } + } +} + + +int +main(int argc, char *argv[]) +{ + if (argc <= 1) { + fprintf(2, "Usage: %s filename\n", argv[0]); + return 1; + } + + int fd = open(argv[1], O_RDONLY); + char buf[128]; + int line_count = 0; + while (fgets3(buf, 128, fd) > 0 ) { + printf("Line %d: %s", line_count++, buf); + } + + return 0; +} diff --git a/user/pwd.c b/user/pwd.c new file mode 100644 index 0000000..165ab8b --- /dev/null +++ b/user/pwd.c @@ -0,0 +1,11 @@ +#include "kernel/types.h" +#include "user/user.h" + +int +main(void) +{ + char buf[128]; + getcwd(buf, 128); + printf("%s\n", buf); + return 0; +} diff --git a/user/reboot.c b/user/reboot.c new file mode 100644 index 0000000..a44da64 --- /dev/null +++ b/user/reboot.c @@ -0,0 +1,14 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int +main(int argc, char **argv) +{ + printf("Rebooting...\n"); + reboot(); + + // error message just in case + fprintf(2, "reboot failed!\n"); + exit(1); +} diff --git a/user/shell.c b/user/shell.c new file mode 100644 index 0000000..f315f55 --- /dev/null +++ b/user/shell.c @@ -0,0 +1,389 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" +#include "kernel/fcntl.h" + +int fgets(char *buf, unsigned int max, int fd); + +uint +strspn(const char *str, const char *chars) +{ + uint i, j; + for (i = 0; str[i] != '\0'; i++) { + for (j = 0; chars[j] != str[i]; j++) { + if (chars[j] == '\0') + return i; + } + } + return i; +} + +uint +strcspn(const char *str, const char *chars) +{ + const char *p, *sp; + char c, sc; + for (p = str;;) { + c = *p++; + sp = chars; + do { + if ((sc = *sp++) == c) { + return (p - 1 - str); + } + } while (sc != 0); + } +} + +char +*next_token(char **str_ptr, const char *delim) +{ + if (*str_ptr == 0) { + return 0; + } + + uint tok_start = strspn(*str_ptr, delim); + uint tok_end = strcspn(*str_ptr + tok_start, delim); + + /* Zero length token. We must be finished. */ + if (tok_end == 0) { + *str_ptr = 0; + return 0; + } + + /* Take note of the start of the current token. We'll return it later. */ + char *current_ptr = *str_ptr + tok_start; + + /* Shift pointer forward (to the end of the current token) */ + *str_ptr += tok_start + tok_end; + + if (**str_ptr == '\0') { + /* If the end of the current token is also the end of the string, we + * must be at the last token. */ + *str_ptr = 0; + } else { + /* Replace the matching delimiter with a NUL character to terminate the + * token string. */ + **str_ptr = '\0'; + + /* Shift forward one character over the newly-placed NUL so that + * next_pointer now points at the first character of the next token. */ + (*str_ptr)++; + } + + return current_ptr; +} + +// helper func to just print the basic prompt +static void print_prompt(int last_status, int cmd_num) { + char path[128]; + int n = getcwd(path, sizeof(path)); + if (n < 0) { + path[0] = '/'; path[1] = 0; + } + printf("[%d]-[%d]-[%s]$ ", last_status, cmd_num, path); +} + +static int is_blank(const char *s) { + for (; *s; s++) { + if (*s!= ' ' && *s!= '\t' && *s!= '\n' && *s!= '\r') return 0; + } + return 1; +} + +// helper fun to remove remove newliens and return characters +static void strip_trailing_newline(char *buf, int *len_io) { + int len = *len_io; + while (len > 0 && (buf[len-1] == '\n' || buf[len-1] == '\r')) { + buf[--len] = 0; + } + *len_io = len; +} + +// helper to check for comments and remove them +static void remove_comment(char *buf) { + for (int i = 0; buf[i]; i++) { + if (buf[i] == '#') { + buf[i] = 0; + break; + } + } +} + +#define MAX_HIST 100 +struct history_entry { + int num; + char line[256]; + int duration_ms; +}; + +static struct history_entry hist[MAX_HIST]; + +// method to add to the history +static void add_history(const char *line, int cmd_num) { + if (!line || !*line) return; + int slot = (cmd_num - 1) % MAX_HIST; + hist[slot].num = cmd_num; + + int i = 0; + for (; i < (int)sizeof(hist[slot].line) - 1 && line[i]; i++) { + hist[slot].line[i] = line[i]; + } + hist[slot].line[i] = 0; +} + +// actual history command +static void history_command(int current_cmd_num, int show_time) { + int last_done = current_cmd_num - 1; // last completed command + if (last_done < 1) return; + + int first = last_done - MAX_HIST + 1; + if (first < 1) first = 1; + + for (int n = first; n <= last_done; n++) { + int slot = (n - 1) % MAX_HIST; + if (hist[slot].num == n && hist[slot].line[0] != 0) { + if (show_time) { + printf("[%d|%dms] %s\n", hist[slot].num, hist[slot].duration_ms, hist[slot].line); + } else { + printf("%d %s\n", hist[slot].num, hist[slot].line); + } + } + } +} + +// lookup in history with number arg +static const char* hist_num(int num) { + if (num < 1) return 0; + int slot = (num - 1) % MAX_HIST; + + // make sure that everything matches and line isn't empty + if (hist[slot].num == num && hist[slot].line[0] != 0) return hist[slot].line; + + return 0; +} + +// lookup in history with prefix arg +static const char* hist_prefix(const char *prefix, int last_done) { + int prefix_len = 0; + while(prefix[prefix_len]) + prefix_len++; + + // go through all the commands + // from newest to oldest + for (int n = last_done; n >= 1; n--) { + int slot = (n - 1) % MAX_HIST; + + if (hist[slot].num != n || hist[slot].line[0] == 0) + continue; + + const char *s = hist[slot].line; + + // compare prefix against start of the command + int i = 0; + while (i < prefix_len && s[i] == prefix[i]) + i++; + + // if they match return the string + if (i == prefix_len) + return s; + } + + return 0; +} + +int +main(int argc, char *argv[]) +{ + + // -1. print a prompt + // 0. get the user's command (stdin) + // 1. fork + // 2. exec + + + int last_status = 0; // 0 = success, 1 = fail + int cmd_num = 1; + int scripting = 0; + int script_fd = -1; + + if (argc > 1) { + script_fd = open(argv[1], O_RDONLY); + if (script_fd < 0) { + fprintf(2, "shell: cannot open %s", argv[1]); + exit(1); + } + scripting = 1; + } + + + while (1) { + if (!scripting) { + print_prompt(last_status, cmd_num); + } + + char buf[256]; + + int r; + if (scripting) { + int i = 0; + while (i + 1 < sizeof(buf)) { + char c; + r = read(script_fd, &c, 1); + if (r < 1) { + break; + } + buf[i++] = c; + if (c == '\n') + break; + } + buf[i] = 0; + + if (i == 0) { + //fprintf(2, "shell: script EOF, exiting\n"); + exit(0); + } + } else { + if (gets(buf, sizeof(buf)) == 0) { + printf("\n"); + exit(0); + } + } + + + int got = strlen(buf); + + // remove "\n" and comments + strip_trailing_newline(buf, &got); + remove_comment(buf); + + // skip blank lines + if (is_blank(buf)) { + continue; + } + + /* + Extra history commands: + 1. "!" -> last command + 2. "!5" -> command number 5 + 3. "!prefix" -> most recent command with the prefix + */ + + if (buf[0] == '!') { + int last_done = cmd_num - 1; + const char *expanded = 0; + + // the prev command + if (buf[1] == '!' && buf[2] == 0) { + expanded = hist_num(last_done); + } else if (buf[1] >= '0' && buf[1] <= '9') { + int n = 0; + for (int i = 1; buf[i] >= '0' && buf[i] <= '9'; i++) { + n = n * 10 + (buf[i] - '0'); + } + expanded = hist_num(n); + } else { + // !prefix + expanded = hist_prefix(buf + 1, last_done); + } + + if (!expanded) { + fprintf(2, "history: no match for '%s'\n", buf); + last_status = 1; + cmd_num++; + continue; + } + + + int i = 0; + while (i < (int)sizeof(buf) - 1 && expanded[i]) { + buf[i] = expanded[i]; + i++; + } + buf[i] = 0; + + printf("%s\n", buf); + } + + // record this command before executing so numbers align + add_history(buf, cmd_num); + + + char *args[32]; + int tokens = 0; + char *next_tok = buf; + char *curr_tok; + /* Tokenize. */ + while ((curr_tok = next_token(&next_tok, " \n\t\r")) != 0 && tokens < 31) { + // printf("Token: '%s'\n", curr_tok); + args[tokens++] = curr_tok; + } + args[tokens] = 0; + if (tokens == 0) { + continue; + } + + + // built in commands + if (strcmp(args[0], "exit") == 0) { + exit(0); + } + + // history command + if (strcmp(args[0], "history") == 0) { + int show_time = 0; + if (tokens > 1 && strcmp(args[1], "-t") == 0) + show_time = 1; + history_command(cmd_num, show_time); + last_status = 0; + cmd_num++; + continue; + } + + + if (strcmp(args[0], "cd") == 0) { + if (tokens < 2) { + fprintf(2, "chdir: missing arg\n"); + last_status = 1; + } else if (chdir(args[1]) < 0) { + fprintf(2, "chdir: no file or directory found: %s\n", args[1]); + last_status = 1; + } else { + last_status = 0; + } + cmd_num++; + continue; + } + + uint64 start = clock(); + + int pid = fork(); + if (pid < 0 ) { + fprintf(2, "Fork failed"); + last_status = 1; + cmd_num++; + continue; + + } else if (pid == 0) { + // child + // execute the command + exec(args[0], args); + + fprintf(2, "exec failed: %s\n", args[0]); + exit(1); + + } else { + wait(0); + + uint64 end = clock(); + uint64 diff = end - start; + int diff_ms = (int)(diff / 1000000ULL); + + int slot = (cmd_num - 1) % MAX_HIST; + hist[slot].duration_ms = diff_ms; + + cmd_num++; + } + } + + return 0; +} diff --git a/user/shutdown.c b/user/shutdown.c new file mode 100644 index 0000000..418275a --- /dev/null +++ b/user/shutdown.c @@ -0,0 +1,14 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int +main(int argc, char **argv) +{ + printf("Shutting down...\n"); + shutdown(); + + // error message just in case + fprintf(2, "shutdown failed!\n"); + exit(1); +} diff --git a/user/spinner.c b/user/spinner.c new file mode 100644 index 0000000..0c70fd8 --- /dev/null +++ b/user/spinner.c @@ -0,0 +1,28 @@ +#include "kernel/types.h" +#include "user/user.h" + +int +main(int argc, char *argv[]) +{ + if (argc <= 2) { + fprintf(2, "Usage: spinner name amount\n"); + exit(1); + } + + char *name = argv[1]; + int amount = atoi(argv[2]); + + int start = uptime(); + for (int i = 0; i < amount; ++i) { + printf("%s", name); + for (int j = 0; j < 10000000; ++j) { + // wheee :-) + } + } + int end = uptime(); + + printf("\n[%s] has finished, %d work in %d ticks.\n", + name, amount, end - start); + + exit(0); +} diff --git a/user/strace.c b/user/strace.c new file mode 100644 index 0000000..f7a6cb8 --- /dev/null +++ b/user/strace.c @@ -0,0 +1,21 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int +main(int argc, char *argv[]) { + if(argc < 2) { + fprintf(2, "Usage: tracer command...\n"); + exit(1); + } + + if (fork() == 0) { + strace_on(); + exec(argv[1], argv + 1); + fprintf(2, "tracer: exec %s failed\n", argv[1]); + exit(1); + } + wait(0); + + exit(0); +} diff --git a/user/tolower.c b/user/tolower.c new file mode 100644 index 0000000..55eb0a5 --- /dev/null +++ b/user/tolower.c @@ -0,0 +1,12 @@ +#include "kernel/types.h" +#include "user/user.h" + +int main() { + for (int c = 0; read(0, &c, 1) > 0; ) { + if (c >= 'A' && c <= 'Z') { + c = c - 'A' + 'a'; + } + write(1, &c, 1); + } + return 0; +} diff --git a/user/ulib.c b/user/ulib.c index a2f5a20..82b1b3a 100644 --- a/user/ulib.c +++ b/user/ulib.c @@ -158,3 +158,69 @@ sbrklazy(int n) { return sys_sbrk(n, SBRK_LAZY); } +// Reads contents of given file ('fd') to 'buf' of size 'max'. +// Returns character's read, excluding null terminator. +int +fgets(char *buf, unsigned int max, int fd) +{ + int i, cc; + char c; + + for(i=0; i+1 < max; ){ + cc = read(fd, &c, 1); + if(cc < 1) { + return -1; + } + buf[i++] = c; + if(c == '\n' || c == '\r'){ + break; + } + } + + buf[i] = '\0'; + return i; +} + +// Like fgets, but dynamically allocates correct memory +// to store the string. +// Caller provides pointer to a malloced 'buf' and capacity ('size'). +// 'buf' points to NULL, a new buffer is allocated. +// Returns characters read, excluding null terminator. +int +getline(char **buf, int size, int fd) +{ + int chars_read = 0; + int curr_read; + + // initilize buffer if not already initilized (size = 2, malloc space for a char + /0) + if (*buf == 0 || size < 2) { + free(*buf); // in case buf was 1 (if null, does nothing) + *buf = malloc(2); + size = 2; + } + + while ((curr_read = fgets(*buf + chars_read, size - chars_read, fd))) { + + if (curr_read < 0) { // error in fgets + return - 1; + } + + chars_read += curr_read; + + //resize + char *temp = malloc(size * 2); + memcpy(temp, *buf, chars_read + 1); // include null term + free(*buf); + *buf = temp; + size = size * 2; + + // break if end of line/eof + char *last_char = *buf + (chars_read - 1); + if (strcmp(last_char, "\n") == 0 || strcmp(last_char, "\r") == 0) { + break; + } + } + + return chars_read; +} + diff --git a/user/umalloc.c b/user/umalloc.c index 2ac6894..16c4bf3 100644 --- a/user/umalloc.c +++ b/user/umalloc.c @@ -3,88 +3,423 @@ #include "user/user.h" #include "kernel/param.h" -// Memory allocator by Kernighan and Ritchie, -// The C programming Language, 2nd ed. Section 8.7. +#define NULL 0 +#define PAGE_SIZE 4096 +#define MIN_SPLIT_BLOCK_SIZE (sizeof(struct mem_block) + 16) -typedef long Align; -union header { - struct { - union header *ptr; - uint size; - } s; - Align x; +#define NAME_LEN 16 + +#define BLOCK_HEADER_SIZE sizeof(struct mem_block) + + +/* If we haven't passed -DDEBUG=1 to gcc, then this will be set to 0: */ +#ifndef DEBUG +#define DEBUG 1 +#endif + + +// Real allocator block metadata +struct mem_block { + char name[8]; + uint64 size; + struct mem_block *next; + struct mem_block *prev; }; -typedef union header Header; -static Header base; -static Header *freep; +struct mem_block *head; +struct mem_block *tail; + +// Free space management algorithms +enum { + FSM_FIRST_FIT = 0, + FSM_BEST_FIT = 1, + FSM_WORST_FIT = 2, +}; + +static int current_fsm = FSM_FIRST_FIT; + +// Our memory: +// +----------+ <--- base +// | | +// | | +// | | <--- bound +// | | +// | | +// | | +// | | +// | | +// | | +// | | +// +----------+ +// +// malloc -> ask kernel for more memory +// free -> tell kernel to shrink our memory +// +// "Hello Kernel, can I have 32 bytes of space please?" (sbrk) +// -> allocate 1 page of memory +// -> hand us 32 bytes of it +// "Hello Kernel, can I have 32 bytes of space please?" (sbrk) +// -> it would be bad to allocate another page here +// maybe the previous thing is not used anymore +// maybe we want to make use of the 4096 - 32 bytes left from the first +// page +// +// +// So, our allocator is going to allocate *pages* of memory at a time. +// One page can hold multiple blocks of memory: +// +-------------------------------------------+ +// | | | | | | | 1 page (4096 bytes) +// +-------------------------------------------+ +// ^ ^ ^ ^ ^ + +// The 'size' of a block: +// 1101 .... 0010 0000 0001 <- free +// 1101 .... 1110 0000 0000 <- used +// +// Why we can do this: +// - We are going to round everything up so that sizes are multiples of 16 +// - That means the smallest size we can have is 16 (0001 0000) +// - Note how the last 4 bits are never used... so we store the free bit there! + +// Get the block header from a data pointer +struct mem_block* get_header(void* ptr) { + return (struct mem_block*)((char*)ptr - BLOCK_HEADER_SIZE); +} + +static uint +align16(uint size) +{ + return (size + 15) & ~0x0F; +} + +// +void set_used(struct mem_block *block) +{ + // turn off the least significant bit + block->size = block->size & (~0x01); +} + +void set_free(struct mem_block *block) +{ + // turn on the least significant bit + block->size = block->size | 0x01; +} + +int is_free(struct mem_block *block) +{ + // check whether the last bit is set or not + return block->size & 0x01; +} + +uint get_size(struct mem_block *block) +{ + // ignore the free bit when retrieving the size + return block->size & (~0x01); +} + +/* Given an existing block, this function should split it in two */ +// How this works: +// Maybe we allocate 1 byte +// That gets turned into 16 (that's our minimum size) +// But then that gets turned into 4096 (our page size) +struct mem_block * +split(struct mem_block *block, uint total_sz) +{ + if (block == NULL) { + return NULL; + } + + if (!is_free(block)) { + return NULL; + } + + uint old_size = get_size(block); + + // Don't split if remainder would be too small + if (old_size <= total_sz + MIN_SPLIT_BLOCK_SIZE) { + return NULL; + } + + char *base = (char *) block; + struct mem_block *new_block = (struct mem_block *) (base + total_sz); + + uint remainder_size = old_size - total_sz; + + new_block->size = remainder_size; + set_free(new_block); + + new_block->next = block->next; + new_block->prev = block; + + if (block->next != NULL) { + block->next->prev = new_block; + } else { + // if block was tail, new_block becomes new tail + tail = new_block; + } + + block->size = total_sz; + set_used(block); + block->next = new_block; + + return new_block; +} + +// We have some space like this: +// +-----------------------------+ +// | mem_block | data | +// +-----------------------------+ +// ^ ^ +// | | +// | +--- actual data pointer (for user space programs) +// | +// +----- header +// + +// find first free whose size is big enough +static struct mem_block* +find_free_first_fit(uint total_sz) +{ + struct mem_block *curr = head; + + while (curr != NULL) { + if (is_free(curr) && get_size(curr) >= total_sz) { + return curr; + } + curr = curr->next; + } + return NULL; +} + +// find free block with smallest size >= total_sz +static struct mem_block* +find_free_best_fit(uint total_sz) +{ + struct mem_block *curr = head; + struct mem_block *best = NULL; + + while (curr != NULL) { + if (is_free(curr) && get_size(curr) >= total_sz) { + if (best == NULL || get_size(curr) < get_size(best)) { + best = curr; + } + } + curr = curr->next; + } + + return best; +} + +static struct mem_block* +find_free_worst_fit(uint total_sz) +{ + struct mem_block *curr = head; + struct mem_block *worst = NULL; + + while (curr != NULL) { + if (is_free(curr) && get_size(curr) >= total_sz) { + if (worst == NULL || get_size(curr) > get_size(worst)) { + worst = curr; + } + } + curr = curr->next; + } + return worst; +} + +static struct mem_block * +find_free_block(uint total_sz) +{ + switch (current_fsm) { + case FSM_BEST_FIT: + return find_free_best_fit(total_sz); + case FSM_WORST_FIT: + return find_free_worst_fit(total_sz); + case FSM_FIRST_FIT: + default: + return find_free_first_fit(total_sz); + } +} + + +void * +malloc(uint size) +{ + if (size == 0) { + return NULL; + } + + uint aligned = align16(size); + uint total_sz = BLOCK_HEADER_SIZE + aligned; + + // try to find a suitable free block first + struct mem_block *block = find_free_block(total_sz); + if (block != NULL) { + // take space from this free block + split(block, total_sz); + set_used(block); + return (void *)(block + 1); + } + + // no suitable free block, request a new chunk from the OS + uint request = total_sz; + if (request < PAGE_SIZE) { + request = PAGE_SIZE; + } + + block = (struct mem_block *) sbrk(request); + if ((long)block == -1) { + return NULL; + } + + // treat the whole new region as a single free block + block->size = request; + set_free(block); + block->next = NULL; + block->prev = tail; + + if (head == NULL) { + head = block; + tail = block; + } else { + tail->next = block; + tail = block; + } + + // now carve out the requested block from this new free block + struct mem_block *alloc_block = block; + split(alloc_block, total_sz); + set_used(alloc_block); + + return (void *)(alloc_block + 1); +} +// linked list helpers: +// * add a new block +// * remove a block +// * AND make sure you can handle both the (1) block list, and (2) free list void -free(void *ap) -{ - Header *bp, *p; - - bp = (Header*)ap - 1; - for(p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr) - if(p >= p->s.ptr && (bp > p || bp < p->s.ptr)) - break; - if(bp + bp->s.size == p->s.ptr){ - bp->s.size += p->s.ptr->s.size; - bp->s.ptr = p->s.ptr->s.ptr; - } else - bp->s.ptr = p->s.ptr; - if(p + p->s.size == bp){ - p->s.size += bp->s.size; - p->s.ptr = bp->s.ptr; - } else - p->s.ptr = bp; - freep = p; -} - -static Header* -morecore(uint nu) -{ - char *p; - Header *hp; - - if(nu < 4096) - nu = 4096; - p = sbrk(nu * sizeof(Header)); - if(p == SBRK_ERROR) - return 0; - hp = (Header*)p; - hp->s.size = nu; - free((void*)(hp + 1)); - return freep; -} - -void* -malloc(uint nbytes) -{ - Header *p, *prevp; - uint nunits; - - nunits = (nbytes + sizeof(Header) - 1)/sizeof(Header) + 1; - if((prevp = freep) == 0){ - base.s.ptr = freep = prevp = &base; - base.s.size = 0; +free(void *ptr) +{ + + if (ptr == NULL) { + return; } - for(p = prevp->s.ptr; ; prevp = p, p = p->s.ptr){ - if(p->s.size >= nunits){ - if(p->s.size == nunits) - prevp->s.ptr = p->s.ptr; - else { - p->s.size -= nunits; - p += p->s.size; - p->s.size = nunits; - } - freep = prevp; - return (void*)(p + 1); - } - if(p == freep) - if((p = morecore(nunits)) == 0) - return 0; + + // ptr points to the user data; header is right before that + struct mem_block *block = ((struct mem_block *)ptr) -1; + + if (is_free(block)) { + return; } + + set_free(block); + +} + +// This is great because we can allocate arrays of things easily +// And they're zeroed out +// This function is perfect and never needs to be changed +void * +calloc(uint nmemb, uint size) +{ + char *mem = malloc(nmemb * size); + if (mem == NULL) { + return NULL; + } + memset(mem, 0, nmemb * size); + return mem; +} + +// takes a pointer to a previous allocation and resizes it (shrink / grow) +void * +realloc(void *ptr, uint size) +{ + if (ptr == NULL) { + return malloc(size); + } + + if (size == 0) { + free(ptr); + return NULL; + } + + struct mem_block *old_block = ((struct mem_block *) ptr) - 1; + uint old_total_size = old_block->size; + uint old_payload_size = old_total_size - sizeof(struct mem_block); + + void *new_ptr = malloc(size); + if (new_ptr == NULL) { + return NULL; + } + + uint copy_size = old_payload_size < size ? old_payload_size : size; + memmove(new_ptr, ptr, copy_size); + + free(ptr); + + return new_ptr; +} + +void +malloc_print(void) +{ + struct mem_block *curr; + + printf("--Current Memory State --\n"); + + curr = head; + while (curr != NULL) { + char *start = (char *)curr; + char *end = start + get_size(curr); + const char *status = is_free(curr) ? "FREE" : "USED"; + + printf("[BLOCK %p-%p] %d\t[%s]\n", + start, + end, + get_size(curr), + status); + + curr = curr->next; + } + + printf("\n-- Free List --\n"); + + curr = head; + int first = 1; + while (curr != NULL) { + if (is_free(curr)) { + if (!first) { + printf(" -> "); + } + printf("[%p]", curr); + first = 0; + } + curr = curr->next; + } + if (!first) { + printf(" -> "); + } + printf("NULL\n"); +} + +void +malloc_setfsm(int algo) +{ + if (algo == FSM_FIRST_FIT || + algo == FSM_BEST_FIT || + algo == FSM_WORST_FIT) { + current_fsm = algo; + } +} + +void malloc_name(void *ptr, const char *name) { + struct mem_block *block = get_header(ptr); + int i; + for (i = 0; i < 7 && name[i] != 0; i++) { + block->name[i] = name[i]; + } + block->name[i] = 0; } diff --git a/user/user.h b/user/user.h index 718b656..2a2b379 100644 --- a/user/user.h +++ b/user/user.h @@ -12,13 +12,13 @@ int read(int fd, void *buf, int count); int close(int fd); int kill(int pid); int exec(const char* path, char **argv); -int open(const char* path, int mode); -int mknod(const char* path, short major, short minor); -int unlink(const char* path); +int open(const char*/*str*/ path, int mode); +int mknod(const char* /*str*/ path, short major, short minor); +int unlink(const char* /*str*/ path); int fstat(int fd, struct stat* st); -int link(const char* old_path, const char* new_path); -int mkdir(const char* path); -int chdir(const char* path); +int link(const char* /*str*/old_path, const char* /*str*/ new_path); +int mkdir(const char* /*str*/ path); +int chdir(const char* /*str*/ path); int dup(int fd); int getpid(void); char* sys_sbrk(int n, int incr); diff --git a/user/usys.pl b/user/usys.pl old mode 100755 new mode 100644 From efbac5e1eb1e816a2d2f844a617488e7a5578105 Mon Sep 17 00:00:00 2001 From: Kailash Turimella Date: Mon, 20 Jul 2026 18:28:49 -0500 Subject: [PATCH 6/6] readme updated --- README.md | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 147 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 85f6164..8981ad2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,152 @@ # FogOS -Fall 2025 Edition - ![FogOS](docs/fogos.gif) +A 64-bit RISC-V operating system, built on top of [MIT's xv6](https://pdos.csail.mit.edu/6.1810/). + +xv6 provides the base: a multiprocessor kernel, virtual memory, a crash-safe logging filesystem, and a small set of Unix utilities. Everything documented below is what I built on top of it — nine new system calls, a priority scheduler, physical page reference counting, anonymous memory mapping, a build-time syscall tracer, a shell written from scratch, and a rewritten heap allocator. + +It runs on bare metal under QEMU. No host OS underneath, no libc. + +--- + +## Quick start + +Requires a `riscv64` GNU toolchain and `qemu-system-riscv64`. + +```sh +make qemu # build and boot FogOS in QEMU + # (exit with Ctrl-A then X) +``` + +Once booted: + +```sh +$ shell # my shell +$ strace ls # trace every syscall a program makes +$ benchmark wc README.md # time a command, count its syscalls +$ freemem # free memory, in KB +$ mt90 # allocator stress test + heap map +$ compress_test # RLE round-trip test suite +``` + +--- + +## Kernel + +### Auto-generated syscall tracing + +`strace_on()` sets a per-process flag; every subsequent syscall prints its name, decoded arguments, and return value. + +The decoder isn't hand-written. `generate-traces.sh` parses `user/usys.pl` and `user/user.h` at build time and emits `kernel/strace.h`, deriving argument names and printf format specifiers directly from the C prototypes. A `/*str*/` comment annotation marks pointers that should render as strings rather than addresses: + +```c +int mkdir(const char* /*str*/ path); → [3|sh] mkdir(path = "a/b") = 0 +``` + +Add a syscall to `usys.pl` and it becomes traceable with no extra work. The Makefile wires this in as a real build dependency, so the tracer can't drift out of sync with the syscall table. + +### Priority scheduler + +Stock xv6 round-robins flatly over the process table. My scheduler finds the highest priority among `RUNNABLE` processes, then descends through priority levels, giving higher-priority processes proportionally more turns. Priority is exposed through a `nice` syscall (`0`–`3`, `0` highest, stored as `priority = 3 - nice`) and inherited across `fork`. Run `spinner` under different `nice` values to watch it take effect. + +### Physical page reference counting + +`kalloc.c` keeps a refcount per physical page, indexed by `(pa - KERNBASE) / PGSIZE`. `kalloc` sets it to 1 inside the freelist lock; `kfree` decrements and only returns a page to the freelist when the last reference drops, panicking on underflow. This is the substrate copy-on-write fork needs, and it's what makes shared mappings across `fork` safe from double-frees. `kfreepages()` walks the freelist to report what's left. + +### Anonymous mmap + +Each process gets a region growing *downward* from below the user stack (`TRAPFRAME - USERSTACK * PGSIZE`), tracked by `p->mmap` and `p->mmap_pages`. Each `mmap()` call allocates a page, zeroes it, and maps it `PTE_R | PTE_W | PTE_U` at the next address down. + +`fork` re-maps the child's mapped pages onto the same physical frames and bumps their refcounts, so the region is shared between parent and child rather than copied. `freeproc` unmaps it on exit. Exercised by `mmap` and `mmaptest`. + +### getcwd + +xv6 has no notion of an absolute path for the working directory — the kernel holds only an inode pointer. Mine walks *up* the tree from the cwd inode via `..`, scanning each parent's directory entries to recover the name of the child it came from, building the path backwards from the end of the buffer and then sliding it into place. `diriname()` does the per-directory reverse lookup. + +### Shebang execution + +`kexec` peeks at the first two bytes of the target; on `#!` it reads the interpreter path, rewrites `argv` to put the interpreter first and the script second, releases its inode and ends its log transaction, then re-enters `kexec`. So `#!/crash` at the top of a script works the way it does on Unix. + +### wait2 + +Extends `wait` to also copy out the exiting child's cumulative syscall count, tracked per-process in `syscall()`. This is the primitive `benchmark` is built on. + +### Wall-clock time and power control + +`clock()` reads the goldfish-RTC MMIO registers for nanosecond-resolution time. `shutdown()` and `reboot()` write magic values to QEMU's `virt` test device to actually halt or reset the machine. `freemem()` reports free memory in KB. + +--- + +## User space + +### shell + +Written from scratch, alongside the xv6 `sh`: + +- prompt showing exit status, command number, and absolute working directory: `[0]-[12]-[/a/b/c]$` +- 100-entry ring-buffer history; `history` and `history -t` (per-command wall-clock duration in ms, measured with `clock()`) +- history expansion: `!!` for the last command, `!5` by number, `!prefix` for the most recent match +- comment stripping, blank-line handling, `cd` and `exit` builtins +- script mode — `shell foo.sh` runs non-interactively with prompts suppressed +- a hand-written tokenizer (`strspn`/`strcspn`/`next_token`), since there's no libc to lean on + +`1.sh` through `4.sh` are the conformance scripts it's tested against, covering the edge cases: bad commands still enter history, empty lines don't, comments are stripped before execution. + +I also extended the original `sh` with script-file arguments, `#` comment handling, and a `/`-prefix fallback so bare command names resolve. + +### Heap allocator + +`user/umalloc.c`, a ~500-line rewrite. Doubly-linked block list over page-granular `sbrk` regions: + +- three selectable free-space-management policies — first fit, best fit, worst fit, via `malloc_setfsm` +- the free bit packed into the low bit of the size field, which is free real estate because every size is 16-byte aligned +- block splitting with a minimum-split threshold, and coalescing on free +- `calloc`, `realloc`, plus named allocations (`malloc_name`) and `malloc_print` to dump an annotated map of the heap + +`mt70`, `mt80`, and `mt90` stress it and visualize the resulting layout. + +### Compression + +`compress -c` / `-d` implements run-length encoding over file *and* stream modes, so it composes with pipes and redirection under `sh`. Format is a stream of `[count][value]` pairs with runs split at 255. See [docs/compress.md](docs/compress.md). `compress_test` round-trips empty files, long runs, and full 0x00–0xFF binary data. + +### Utilities + +`about` · `benchmark` (times a command and counts its syscalls) · `catlines` · `clear` · `clock` · `fnr` (stream find-and-replace) · `freemem` · `leetify` · `mmap` · `nice` · `opt_cat` (buffered `cat`, a direct throughput comparison against the naive one) · `pwd` · `reboot` · `shutdown` · `spinner` · `strace` · `tolower` + +Plus `getline` and `fgets` added to the user library. + +### C debugging exercises + +`broken.c` is a set of deliberately buggy C functions — read-only string literals, dangling pointers, uninitialized reads, format overflows, off-by-ones — each fixed with a written explanation of the underlying fault. + +--- + +## Layout + +``` +kernel/ kernel source; strace.h is generated at build time +user/ user programs and the user-space C library +mkfs/ builds the initial filesystem image +docs/ per-feature documentation +*.sh shell conformance scripts, baked into fs.img +test-xv6.py scripted QEMU test driver +``` + +Scripted testing, without typing at the console: + +```sh +./test-xv6.py usertests # full user-space test suite +./test-xv6.py -q usertests # quick pass +./test-xv6.py crash # crash-recovery tests +``` + +## System calls added + +`shutdown` · `reboot` · `clock` · `strace_on` · `wait2` · `nice` · `getcwd` · `freemem` · `mmap` + +--- + +## License +MIT — see [LICENSE](LICENSE). Built on xv6-riscv, © 2006–2022 Frans Kaashoek, Robert Morris, and Russ Cox; see [xv6-LICENSE](xv6-LICENSE) and [xv6-README](xv6-README).