├── homework ├── readme.txt ├── primes.c ├── barrier.c ├── 0001-some-comments-of-the-exercises.patch ├── 0001-xv6-pages-for-the-kernel-are-shared-among-processes.patch ├── 0001-exercise-1-2-for-chapter-3-Traps-interrupts-and-driv.patch ├── ph.c ├── 0001-chapter-2-Page-Tables-Exercise-1-3.patch ├── 0001-uthreads.patch ├── 0001-xv6-lazy-page-allocation.patch ├── 0001-HW-Big-files-finished.patch ├── 0001-add-CPU-alarm-for-xv6.patch ├── homework1-shell.c ├── 0001-support-threading-in-process.patch └── 0001-homework-syscall-part1-part2.patch ├── Dockerfile ├── Dockerfile.aarch64 ├── labs ├── 0001-fix-out-of-memory-issue-due-to-b-binary.patch ├── 0001-lab2-PTE_PS-challenge.patch ├── 0001-LAB1-finished.patch ├── 0001-challenge-IRQ-driven-transmit-receive.patch ├── 0001-LAB-5-challenge-1-IRQ-driven-disk-access.patch ├── 0001-trying-with-fine-grained-locks.patch ├── 0001-ready-to-submit-my-lab2.patch ├── 0001-start-with-lab5.patch ├── 0001-lab3-user-env-finished.patch └── 0001-start-with-lab-6-without-challenges.patch └── README.md /homework/readme.txt: -------------------------------------------------------------------------------- 1 | steps for homework syscall: 2 | 3 | 1. register syscall declaration in user.h 4 | 2. define syscall number in syscall.h 5 | 3. add sys_xxx wrapper in sysproc.c or somewhere else appropriate, where finally the syscall will be "trapped" in according to syscall number in step 2 :p 6 | 4. expose wrapper in step 3 in syscall.c and register it also with syscall number(step 2) in the table 7 | 5. add syscall definition of step 1 in usys.S so linker will find memory reference for declaration in user.h 8 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:latest 2 | 3 | ENV LD_LIBRARY_PATH "usr/local/lib":$LD_LIBRARY_PATH 4 | ENV SHELL /bin/bash 5 | 6 | RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y \ 7 | --no-install-recommends build-essential gdb git vim ca-certificates gcc-multilib tcpdump \ 8 | python libsdl1.2-dev libtool-bin libglib2.0-dev libz-dev libpixman-1-dev locales && \ 9 | sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \ 10 | dpkg-reconfigure --frontend=noninteractive locales && update-locale LANG=en_US.UTF-8 && \ 11 | git clone https://github.com/k0Iry/6.828-qemu.git qemu && \ 12 | cd qemu && ./configure --disable-kvm --disable-werror --target-list="i386-softmmu x86_64-softmmu" \ 13 | && cpus=$(nproc) && make -j $cpus && make install && cd .. && rm -rf qemu && sed -i '9,12 s/^#//' /root/.bashrc 14 | 15 | VOLUME /usr/src/app 16 | WORKDIR /usr/src/app 17 | 18 | CMD ["/bin/bash"] 19 | -------------------------------------------------------------------------------- /Dockerfile.aarch64: -------------------------------------------------------------------------------- 1 | FROM debian:latest 2 | 3 | WORKDIR /workdir 4 | 5 | RUN apt-get update && \ 6 | apt install -y wget ca-certificates libgmp3-dev libmpfr-dev libmpc-dev xz-utils xz-utils build-essential qemu-system-x86 texinfo --no-install-recommends 7 | 8 | RUN wget https://ftp.gnu.org/gnu/binutils/binutils-2.35.tar.xz && unxz binutils-2.35.tar.xz && tar xfv binutils-2.35.tar && rm binutils-2.35.tar && cd binutils-2.35 && ./configure --prefix=/usr/local --target=i386-jos-elf --disable-werror && make -j 4 && make install && cd .. && rm -rf binutils-2.35 9 | RUN wget https://ftp.gnu.org/gnu/gcc/gcc-9.5.0/gcc-9.5.0.tar.xz && unxz gcc-9.5.0.tar.xz && tar xvf gcc-9.5.0.tar && rm gcc-9.5.0.tar && cd gcc-9.5.0 && mkdir build && cd build && ../configure --prefix=/usr/local --target=i386-jos-elf --disable-werror --disable-libssp --disable-libmudflap --with-newlib --without-headers --enable-languages=c MAKEINFO=missing && make all-gcc -j 4 && make install-gcc && make all-target-libgcc && make install-target-libgcc && cd ../.. && rm -rf gcc-9.5.0 10 | RUN wget https://ftp.gnu.org/gnu/gdb/gdb-10.2.tar.xz && unxz gdb-10.2.tar.xz && tar xfv gdb-10.2.tar && rm gdb-10.2.tar && cd gdb-10.2 && ./configure --prefix=/usr/local --target=i386-jos-elf --program-prefix=i386-jos-elf- --disable-werror && make all -j 4 && make install && cd .. && rm -rf gdb-10.2 11 | 12 | 13 | CMD ["/bin/bash"] 14 | -------------------------------------------------------------------------------- /homework/primes.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | void feed(int fd, int prime) 8 | { 9 | int p[2]; 10 | pipe(p); 11 | if (fork() == 0) 12 | { 13 | close(p[1]); 14 | int number = 0; 15 | read(p[0], &number, sizeof(int)); 16 | if (number) 17 | feed(p[0], number); 18 | close(p[0]); 19 | } 20 | else 21 | { 22 | printf("number: %d\n", prime); 23 | close(p[0]); 24 | int number = 0; 25 | while (read(fd, &number, sizeof(int))) 26 | { 27 | if (number % prime) 28 | { 29 | write(p[1], &number, sizeof(int)); 30 | } 31 | } 32 | close(p[1]); 33 | } 34 | } 35 | 36 | int main(int argc, char *argv[]) 37 | { 38 | if (argc != 2) 39 | { 40 | printf("usage: ./primes limit\n"); 41 | exit(EXIT_FAILURE); 42 | } 43 | 44 | int p[2]; 45 | pipe(p); 46 | pid_t pid = fork(); 47 | if (pid == 0) 48 | { 49 | close(p[1]); 50 | feed(p[0], 2); 51 | close(p[0]); 52 | } 53 | else 54 | { 55 | close(p[0]); 56 | for (int i = 2; i <= atoi(argv[1]); i++) 57 | { 58 | write(p[1], &i, sizeof(int)); 59 | } 60 | close(p[1]); 61 | } 62 | 63 | exit(EXIT_SUCCESS); 64 | } -------------------------------------------------------------------------------- /labs/0001-fix-out-of-memory-issue-due-to-b-binary.patch: -------------------------------------------------------------------------------- 1 | From 040935f41d27411b54b7a0c2842d3e6475fdf32c Mon Sep 17 00:00:00 2001 2 | From: Aaron 3 | Date: Sun, 20 Sep 2020 11:06:39 +0000 4 | Subject: [PATCH] fix out of memory issue due to '-b binary' 5 | 6 | expand 4M to 8M for kernel initialization 7 | --- 8 | kern/entrypgdir.c | 4 +++- 9 | lib/exit.c | 2 +- 10 | 2 files changed, 4 insertions(+), 2 deletions(-) 11 | 12 | diff --git a/kern/entrypgdir.c b/kern/entrypgdir.c 13 | index 6aa2fdb..f4edc99 100644 14 | --- a/kern/entrypgdir.c 15 | +++ b/kern/entrypgdir.c 16 | @@ -24,7 +24,9 @@ pde_t entry_pgdir[NPDENTRIES] = { 17 | = (0) | PTE_P | PTE_PS, //((uintptr_t)entry_pgtable - KERNBASE) + PTE_P, 18 | // Map VA's [KERNBASE, KERNBASE+4MB) to PA's [0, 4MB) 19 | [KERNBASE>>PDXSHIFT] 20 | - = (0) | PTE_P | PTE_W | PTE_PS //((uintptr_t)entry_pgtable - KERNBASE) + PTE_P + PTE_W 21 | + = (0) | PTE_P | PTE_W | PTE_PS, //((uintptr_t)entry_pgtable - KERNBASE) + PTE_P + PTE_W 22 | + [(KERNBASE>>PDXSHIFT) + 1] 23 | + = (0x400000) | PTE_P | PTE_W | PTE_PS 24 | }; 25 | 26 | // Below 4KB allocation for page table page is not needed anymore 27 | diff --git a/lib/exit.c b/lib/exit.c 28 | index 438fb5a..cee3336 100644 29 | --- a/lib/exit.c 30 | +++ b/lib/exit.c 31 | @@ -4,7 +4,7 @@ 32 | void 33 | exit(void) 34 | { 35 | - // close_all(); 36 | + close_all(); 37 | sys_env_destroy(0); 38 | } 39 | 40 | -- 41 | 2.20.1 42 | 43 | -------------------------------------------------------------------------------- /homework/barrier.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | // #define SOL 8 | 9 | static int nthread = 1; 10 | static int round = 0; 11 | 12 | struct barrier 13 | { 14 | pthread_mutex_t barrier_mutex; 15 | pthread_cond_t barrier_cond; 16 | int nthread; // Number of threads that have reached this round of the barrier 17 | int round; // Barrier round 18 | } bstate; 19 | 20 | static void 21 | barrier_init(void) 22 | { 23 | assert(pthread_mutex_init(&bstate.barrier_mutex, NULL) == 0); 24 | assert(pthread_cond_init(&bstate.barrier_cond, NULL) == 0); 25 | bstate.nthread = 0; 26 | } 27 | 28 | static void 29 | barrier() 30 | { 31 | // add the lock before updating any global stuff 32 | pthread_mutex_lock(&bstate.barrier_mutex); 33 | // we actually make the changes with all threads come in 34 | bstate.nthread++; 35 | if (bstate.nthread < nthread) 36 | { 37 | // there are still other threads haven't called in, so wait 38 | pthread_cond_wait(&bstate.barrier_cond, &bstate.barrier_mutex); 39 | } 40 | else 41 | { 42 | bstate.nthread = 0; // reset number of threads which hit the barrier 'bstate.round' 43 | bstate.round++; // only inc round after all threads coming in 44 | pthread_cond_broadcast(&bstate.barrier_cond); 45 | } 46 | // bstate.round++; cannot be here, otherwise, the round will be overflow... 47 | pthread_mutex_unlock(&bstate.barrier_mutex); 48 | } 49 | 50 | static void * 51 | thread(void *xa) 52 | { 53 | long n = (long)xa; 54 | long delay; 55 | int i; 56 | 57 | for (i = 0; i < 20000; i++) 58 | { 59 | int t = bstate.round; 60 | assert(i == t); 61 | barrier(); 62 | usleep(random() % 100); 63 | } 64 | } 65 | 66 | int main(int argc, char *argv[]) 67 | { 68 | pthread_t *tha; 69 | void *value; 70 | long i; 71 | double t1, t0; 72 | 73 | if (argc < 2) 74 | { 75 | fprintf(stderr, "%s: %s nthread\n", argv[0], argv[0]); 76 | exit(-1); 77 | } 78 | nthread = atoi(argv[1]); 79 | tha = malloc(sizeof(pthread_t) * nthread); 80 | srandom(0); 81 | 82 | barrier_init(); 83 | 84 | for (i = 0; i < nthread; i++) 85 | { 86 | assert(pthread_create(&tha[i], NULL, thread, (void *)i) == 0); 87 | } 88 | for (i = 0; i < nthread; i++) 89 | { 90 | assert(pthread_join(tha[i], &value) == 0); 91 | } 92 | printf("OK; passed\n"); 93 | } 94 | -------------------------------------------------------------------------------- /homework/0001-some-comments-of-the-exercises.patch: -------------------------------------------------------------------------------- 1 | From f486da8c959dc058a26b0d27e114e032b8d5ebda Mon Sep 17 00:00:00 2001 2 | From: Aaron 3 | Date: Sun, 16 Aug 2020 08:44:20 +0000 4 | Subject: [PATCH] some comments of the exercises 5 | 6 | add "hlt" instruction to avoid 100% cpus usage 7 | note this needs interrupts enabled, otherwise cpus 8 | will not be woken up at all 9 | --- 10 | pipe.c | 8 ++++++++ 11 | proc.c | 8 ++++++++ 12 | 2 files changed, 16 insertions(+) 13 | 14 | diff --git a/pipe.c b/pipe.c 15 | index fdc819e..0d9e9b8 100644 16 | --- a/pipe.c 17 | +++ b/pipe.c 18 | @@ -76,6 +76,14 @@ int pipewrite(struct pipe *p, char *addr, int n) { 19 | 20 | acquire(&p->lock); 21 | for (i = 0; i < n; i++) { 22 | + // needs to be "while" statement, consider this case: 23 | + // there are two processes p1, p2, trying to write at the same time, 24 | + // p1 is hit the full pipe condition and is sleeping and release lock 25 | + // p2 accidentally acquire the lock before reader and also sleep 26 | + // reader read m bytes out of pipe, and release the lock, then 27 | + // p1 & p2 are all wakeup, since now the pipe is not full, let's say 28 | + // p1 write full of pipe and sleep, and p2 get the lock before reader 29 | + // and continue with line 94, override some data written by p1 30 | while (p->nwrite == p->nread + PIPESIZE) { // DOC: pipewrite-full 31 | if (p->readopen == 0 || myproc()->killed) { 32 | release(&p->lock); 33 | diff --git a/proc.c b/proc.c 34 | index 691f884..3272d7d 100644 35 | --- a/proc.c 36 | +++ b/proc.c 37 | @@ -266,6 +266,11 @@ void exit(void) { 38 | acquire(&ptable.lock); 39 | 40 | // Parent might be sleeping in wait(). 41 | + // it is ok to wake up earlier, since parents 42 | + // need to acquire the lock to reap the child 43 | + // so until child is set to ZOMBIE and release 44 | + // the lock, parent won't actually do the job 45 | + // so no missed wake-up 46 | wakeup1(curproc->parent); 47 | 48 | // Pass abandoned children to init. 49 | @@ -342,6 +347,9 @@ void scheduler(void) { 50 | for (;;) { 51 | // Enable interrupts on this processor. 52 | sti(); 53 | + // must hlt with interrupts enabled, otherwise 54 | + // all cpus will not run at all 55 | + asm volatile("hlt"); 56 | 57 | // Loop over process table looking for process to run. 58 | acquire(&ptable.lock); 59 | -- 60 | 2.20.1 61 | 62 | -------------------------------------------------------------------------------- /homework/0001-xv6-pages-for-the-kernel-are-shared-among-processes.patch: -------------------------------------------------------------------------------- 1 | From a5e3b69a5fe68097d95f431c71e345bc93ec9d6f Mon Sep 17 00:00:00 2001 2 | From: Aaron 3 | Date: Sun, 14 Jun 2020 23:27:42 +0200 4 | Subject: [PATCH] xv6 pages for the kernel are shared among processes 5 | 6 | --- 7 | vm.c | 43 +++++++++++++++++++++++++++++++------------ 8 | 1 file changed, 31 insertions(+), 12 deletions(-) 9 | 10 | diff --git a/vm.c b/vm.c 11 | index 1e821ed..047c873 100644 12 | --- a/vm.c 13 | +++ b/vm.c 14 | @@ -118,20 +118,27 @@ static struct kmap { 15 | pde_t* 16 | setupkvm(void) 17 | { 18 | + if (!kpgdir) 19 | + panic("where is kernel pgdir?\n"); 20 | pde_t *pgdir; 21 | - struct kmap *k; 22 | - 23 | - if((pgdir = (pde_t*)kalloc()) == 0) 24 | + if ((pgdir = (pde_t *)kalloc()) == 0) 25 | return 0; 26 | memset(pgdir, 0, PGSIZE); 27 | - if (P2V(PHYSTOP) > (void*)DEVSPACE) 28 | - panic("PHYSTOP too high"); 29 | - for(k = kmap; k < &kmap[NELEM(kmap)]; k++) 30 | - if(mappages(pgdir, k->virt, k->phys_end - k->phys_start, 31 | - (uint)k->phys_start, k->perm) < 0) { 32 | - freevm(pgdir); 33 | - return 0; 34 | + // copy pdes from kernel page directory 35 | + // only copy needed ones, we don't do memmove 36 | + struct kmap *k; 37 | + for (k = kmap; k < &kmap[NELEM(kmap)]; k++) 38 | + { 39 | + uint sz = k->phys_end - k->phys_start, i; 40 | + for (i = 0; i <= (sz >> PDXSHIFT); i++) 41 | + { 42 | + int idx = PDX(k->virt) + i; 43 | + if (idx < NPDENTRIES) 44 | + pgdir[idx] = kpgdir[idx]; 45 | + // cprintf("index is %d\n", PDX(k->virt) + i); 46 | } 47 | + } 48 | + 49 | return pgdir; 50 | } 51 | 52 | @@ -140,7 +147,17 @@ setupkvm(void) 53 | void 54 | kvmalloc(void) 55 | { 56 | - kpgdir = setupkvm(); 57 | + struct kmap *k; 58 | + if((kpgdir = (pde_t*)kalloc()) == 0) 59 | + panic("kvmalloc"); 60 | + memset(kpgdir, 0, PGSIZE); 61 | + if (P2V(PHYSTOP) > (void*)DEVSPACE) 62 | + panic("PHYSTOP too high"); 63 | + for(k = kmap; k < &kmap[NELEM(kmap)]; k++) 64 | + if(mappages(kpgdir, k->virt, k->phys_end - k->phys_start, 65 | + (uint)k->phys_start, k->perm) < 0) { 66 | + freevm(kpgdir); 67 | + } 68 | switchkvm(); 69 | } 70 | 71 | @@ -288,7 +305,9 @@ freevm(pde_t *pgdir) 72 | if(pgdir == 0) 73 | panic("freevm: no pgdir"); 74 | deallocuvm(pgdir, KERNBASE, 0); 75 | - for(i = 0; i < NPDENTRIES; i++){ 76 | + // 0 - NPDENTRYIES/2 will map user space 77 | + // we only free user 78 | + for(i = 0; i < NPDENTRIES/2; i++){ 79 | if(pgdir[i] & PTE_P){ 80 | char * v = P2V(PTE_ADDR(pgdir[i])); 81 | kfree(v); 82 | -- 83 | 2.24.3 (Apple Git-128) 84 | 85 | -------------------------------------------------------------------------------- /labs/0001-lab2-PTE_PS-challenge.patch: -------------------------------------------------------------------------------- 1 | From 216188a5dbf834c86ebb41063fcb706d3c10f86e Mon Sep 17 00:00:00 2001 2 | From: Aaron 3 | Date: Sun, 7 Jun 2020 18:15:43 +0200 4 | Subject: [PATCH] lab2 PTE_PS challenge 5 | 6 | --- 7 | kern/entry.S | 5 +++++ 8 | kern/entrypgdir.c | 12 ++++++++---- 9 | kern/kernel.ld | 2 +- 10 | kern/pmap.c | 2 ++ 11 | 4 files changed, 16 insertions(+), 5 deletions(-) 12 | 13 | diff --git a/kern/entry.S b/kern/entry.S 14 | index 9550bbb..55e4848 100644 15 | --- a/kern/entry.S 16 | +++ b/kern/entry.S 17 | @@ -52,6 +52,11 @@ entry: 18 | # sufficient until we set up our real page table in mem_init 19 | # in lab 2. 20 | 21 | + # enable Page size extensions bit to support superpages 22 | + # then we set PTE_PS bit in page directory entries 23 | + movl %cr4, %eax 24 | + orl $(CR4_PSE), %eax # enable superpages (4MB) 25 | + movl %eax, %cr4 26 | # Load the physical address of entry_pgdir into cr3. entry_pgdir 27 | # is defined in entrypgdir.c. 28 | movl $(RELOC(entry_pgdir)), %eax 29 | diff --git a/kern/entrypgdir.c b/kern/entrypgdir.c 30 | index 4f324d1..6aa2fdb 100644 31 | --- a/kern/entrypgdir.c 32 | +++ b/kern/entrypgdir.c 33 | @@ -21,15 +21,19 @@ __attribute__((__aligned__(PGSIZE))) 34 | pde_t entry_pgdir[NPDENTRIES] = { 35 | // Map VA's [0, 4MB) to PA's [0, 4MB) 36 | [0] 37 | - = ((uintptr_t)entry_pgtable - KERNBASE) + PTE_P, 38 | + = (0) | PTE_P | PTE_PS, //((uintptr_t)entry_pgtable - KERNBASE) + PTE_P, 39 | // Map VA's [KERNBASE, KERNBASE+4MB) to PA's [0, 4MB) 40 | [KERNBASE>>PDXSHIFT] 41 | - = ((uintptr_t)entry_pgtable - KERNBASE) + PTE_P + PTE_W 42 | + = (0) | PTE_P | PTE_W | PTE_PS //((uintptr_t)entry_pgtable - KERNBASE) + PTE_P + PTE_W 43 | }; 44 | 45 | +// Below 4KB allocation for page table page is not needed anymore 46 | +// because we enabled superpages, so processor can directly address 47 | +// 4MB physical address span 48 | +// 49 | // Entry 0 of the page table maps to physical page 0, entry 1 to 50 | // physical page 1, etc. 51 | -__attribute__((__aligned__(PGSIZE))) 52 | +/* __attribute__((__aligned__(PGSIZE))) 53 | pte_t entry_pgtable[NPTENTRIES] = { 54 | 0x000000 | PTE_P | PTE_W, 55 | 0x001000 | PTE_P | PTE_W, 56 | @@ -1055,5 +1059,5 @@ pte_t entry_pgtable[NPTENTRIES] = { 57 | 0x3fd000 | PTE_P | PTE_W, 58 | 0x3fe000 | PTE_P | PTE_W, 59 | 0x3ff000 | PTE_P | PTE_W, 60 | -}; 61 | +};*/ 62 | 63 | diff --git a/kern/kernel.ld b/kern/kernel.ld 64 | index a219d1d..b5d69e6 100644 65 | --- a/kern/kernel.ld 66 | +++ b/kern/kernel.ld 67 | @@ -50,9 +50,9 @@ SECTIONS 68 | .bss : { 69 | PROVIDE(edata = .); 70 | *(.bss) 71 | - PROVIDE(end = .); 72 | BYTE(0) 73 | } 74 | + PROVIDE(end = .); 75 | 76 | 77 | /DISCARD/ : { 78 | diff --git a/kern/pmap.c b/kern/pmap.c 79 | index 49dbd5d..dfe1471 100644 80 | --- a/kern/pmap.c 81 | +++ b/kern/pmap.c 82 | @@ -118,6 +118,8 @@ boot_alloc(uint32_t n) 83 | result = nextfree; 84 | nextfree += ROUNDUP(n, PGSIZE); 85 | cprintf("nextfree now is %p\n", nextfree); 86 | + cprintf("result is %p\n", result); 87 | + cprintf("kern_pgdir is here: %p\n", &kern_pgdir); 88 | 89 | 90 | return result; 91 | -- 92 | 2.24.3 (Apple Git-128) 93 | 94 | -------------------------------------------------------------------------------- /labs/0001-LAB1-finished.patch: -------------------------------------------------------------------------------- 1 | From 66fecd6b894ededfc9a8fb01c324e395712b55b8 Mon Sep 17 00:00:00 2001 2 | From: Aaron 3 | Date: Sun, 26 Apr 2020 15:38:24 +0200 4 | Subject: [PATCH] LAB1 finished! 5 | 6 | --- 7 | kern/kdebug.c | 9 +++++++++ 8 | kern/monitor.c | 29 ++++++++++++++++++++++++++++- 9 | lib/printfmt.c | 7 +++---- 10 | 3 files changed, 40 insertions(+), 5 deletions(-) 11 | 12 | diff --git a/kern/kdebug.c b/kern/kdebug.c 13 | index 9547143..e287098 100644 14 | --- a/kern/kdebug.c 15 | +++ b/kern/kdebug.c 16 | @@ -179,6 +179,15 @@ debuginfo_eip(uintptr_t addr, struct Eipdebuginfo *info) 17 | // Look at the STABS documentation and to find 18 | // which one. 19 | // Your code here. 20 | + stab_binsearch(stabs, &lline, &rline, N_SLINE, addr); 21 | + if (lline <= rline) 22 | + { 23 | + info->eip_line = stabs[lline].n_desc; 24 | + } 25 | + else 26 | + { 27 | + return -1; 28 | + } 29 | 30 | 31 | // Search backwards from the line number for the relevant filename 32 | diff --git a/kern/monitor.c b/kern/monitor.c 33 | index e137e92..db8dffa 100644 34 | --- a/kern/monitor.c 35 | +++ b/kern/monitor.c 36 | @@ -24,6 +24,7 @@ struct Command { 37 | static struct Command commands[] = { 38 | { "help", "Display this list of commands", mon_help }, 39 | { "kerninfo", "Display information about the kernel", mon_kerninfo }, 40 | + { "backtrace", "Display backtrace to current function call", mon_backtrace} 41 | }; 42 | 43 | /***** Implementations of basic kernel monitor commands *****/ 44 | @@ -57,7 +58,33 @@ mon_kerninfo(int argc, char **argv, struct Trapframe *tf) 45 | int 46 | mon_backtrace(int argc, char **argv, struct Trapframe *tf) 47 | { 48 | - // Your code here. 49 | + cprintf("Stack backtrace\n"); 50 | + 51 | + uint32_t ebp = 0; 52 | + uintptr_t eip = 0; 53 | + asm volatile("movl %%ebp,%0" : "=r" (ebp)); // now should be after prologue 54 | + 55 | + while(ebp != 0) // in entry.S, ebp got initialized to 0, where we should stop 56 | + { 57 | + eip = (uintptr_t)*((uint32_t *)ebp + 1); 58 | + struct Eipdebuginfo debuginfo = {NULL, 0, NULL, 0, 0, 0}; 59 | + if (debuginfo_eip(eip, &debuginfo) == -1) 60 | + return -1; 61 | + 62 | + int fn_name_len = debuginfo.eip_fn_namelen; 63 | + char fn_name[fn_name_len + 1]; 64 | + const char *eip_fn_name = debuginfo.eip_fn_name; 65 | + for (int i = 0; i < fn_name_len; i ++) 66 | + { 67 | + fn_name[i] = eip_fn_name[i]; 68 | + } 69 | + fn_name[fn_name_len] = '\0'; 70 | + cprintf("ebp %08x eip %08x args %08x %08x %08x %08x %08x\n\n \t\t%s:%d: %s+%d\n", 71 | + ebp, eip, *((uint32_t *)ebp + 2), *((uint32_t *)ebp + 3), *((uint32_t *)ebp + 4), *((uint32_t *)ebp + 5), *((uint32_t *)ebp + 6), 72 | + debuginfo.eip_file, debuginfo.eip_line, fn_name, debuginfo.eip_fn_narg); 73 | + ebp = *(uint32_t *)ebp; 74 | + } 75 | + 76 | return 0; 77 | } 78 | 79 | diff --git a/lib/printfmt.c b/lib/printfmt.c 80 | index 28e01c9..0dca3e4 100644 81 | --- a/lib/printfmt.c 82 | +++ b/lib/printfmt.c 83 | @@ -205,10 +205,9 @@ vprintfmt(void (*putch)(int, void*), void *putdat, const char *fmt, va_list ap) 84 | 85 | // (unsigned) octal 86 | case 'o': 87 | - // Replace this with your code. 88 | - putch('X', putdat); 89 | - putch('X', putdat); 90 | - putch('X', putdat); 91 | + num = getuint(&ap, lflag); 92 | + base = 8; 93 | + goto number; 94 | break; 95 | 96 | // pointer 97 | -- 98 | 2.24.3 (Apple Git-128) 99 | 100 | -------------------------------------------------------------------------------- /homework/0001-exercise-1-2-for-chapter-3-Traps-interrupts-and-driv.patch: -------------------------------------------------------------------------------- 1 | From 8aec43bcf8c32ca0b4a720e6b4b4d9df2e3bdc13 Mon Sep 17 00:00:00 2001 2 | From: Aaron 3 | Date: Sat, 27 Jun 2020 21:22:07 +0200 4 | Subject: [PATCH] exercise 1 & 2 for chapter 3 Traps, interrupts, and drivers 5 | 6 | --- 7 | date.c | 5 +++-- 8 | syscall.c | 22 ++++++++++++++++++++++ 9 | sysproc.c | 4 +--- 10 | trap.c | 4 +++- 11 | 4 files changed, 29 insertions(+), 6 deletions(-) 12 | 13 | diff --git a/date.c b/date.c 14 | index e89784b..f22f052 100644 15 | --- a/date.c 16 | +++ b/date.c 17 | @@ -11,8 +11,9 @@ int main(int argc, char *argv[]) { 18 | } 19 | 20 | // your code to print the time in any format you like... 21 | - // printf(1, "hour: %d, minute: %d, second: %d\n", r.hour, r.minute, 22 | - // r.second); 23 | + printf(1, "UTC time:\n"); 24 | + printf(1, "%d-%d-%d %dh:%dm:%ds\n", r.year, r.month, r.day, r.hour, 25 | + r.minute, r.second); 26 | 27 | exit(); 28 | } 29 | \ No newline at end of file 30 | diff --git a/syscall.c b/syscall.c 31 | index f9d43c0..032695d 100644 32 | --- a/syscall.c 33 | +++ b/syscall.c 34 | @@ -122,6 +122,28 @@ static char *syscallnames[] = { 35 | [SYS_date] "date", [SYS_dup2] "dup2", [SYS_alarm] "alarm", 36 | }; 37 | 38 | +// breakpoint here, x/37x 39 | +// (gdb) x/37x $esp 40 | +/* 41 | +0x8dffff7c: 0x801054cd 0x0000003a 0x00000000 0x00000000 42 | +0x8dffff8c: 0x8dffffac 0x8010327e 0x00000001 0x01010101 43 | +0x8dffff9c: 0x00000000 0x00000000 0x00000000 0x00000000 44 | +0x8dffffac: 0x801051d1 0x8dffffb4 0x00000000 0x00000000 45 | +0x8dffffbc: 0x00000000 0x8dffffd4 0x00000000 0x00000000 46 | +0x8dffffcc: 0x00000000 0x00000007 0x00000000 0x00000000 47 | +0x8dffffdc: 0x00000023 0x00000023 0x00000040 0x00000000 48 | +0x8dffffec: 0x00000013 0x0000001b 0x00000202 0x00000ff4 49 | +0x8dfffffc: 0x00000023 50 | + 51 | +0x8dffffb4: trapframe passed in trap.c 52 | +0x801051d1: return address at trapasm.S:21 53 | +0x8dffffac: stack before calling into trap(struct trapframe *tf) 54 | +0x8dffff9c: 0x00000000 0x00000000 0x00000000 0x00000000 (%ebx, %esi, %edi, %ebp) 55 | + 56 | +0x8dffff7c: 0x801054cd(return address) 0x0000003a 0x00000000 0x00000000 57 | +0x8dffff8c: 0x8dffffac 0x8010327e 0x00000001 0x01010101 : junk while allocating stack space 58 | +*/ 59 | + 60 | void syscall(void) { 61 | int num; 62 | struct proc *curproc = myproc(); 63 | diff --git a/sysproc.c b/sysproc.c 64 | index 20ca9f5..3d35ace 100644 65 | --- a/sysproc.c 66 | +++ b/sysproc.c 67 | @@ -86,9 +86,7 @@ int sys_date(void) { 68 | struct rtcdate *rtc = (struct rtcdate *)date; 69 | // fill in rtcdate 70 | cmostime(rtc); 71 | - cprintf("UTC time:\n"); 72 | - cprintf("%d-%d-%d %dh:%dm:%ds\n", rtc->year, rtc->month, rtc->day, rtc->hour, 73 | - rtc->minute, rtc->second); 74 | + 75 | return 0; 76 | } 77 | 78 | diff --git a/trap.c b/trap.c 79 | index 06c2a15..106e95b 100644 80 | --- a/trap.c 81 | +++ b/trap.c 82 | @@ -85,6 +85,8 @@ void trap(struct trapframe *tf) { 83 | 84 | // inject shell code on user stack (restore caller-saved registers) 85 | // shell code (add esp,8;pop edx;pop ecx;pop eax;ret;), little endian 86 | + // rasm2 -a x86 -b 32 "add esp,8;pop edx;pop ecx;pop eax;ret" 87 | + // 83c4085a5958c3 88 | tf->esp -= 8; 89 | *((uint *)tf->esp) = 0x5a08c483; 90 | *((uint *)tf->esp + 1) = 0xc35859; 91 | @@ -137,7 +139,7 @@ void trap(struct trapframe *tf) { 92 | panic("Out of memory\n"); 93 | } 94 | memset(new_mem, 0, PGSIZE); 95 | - cprintf("page fault at address 0x%x, will be corrected.\n", rcr2()); 96 | + cprintf("page fault at address 0x%x, will be corrected, current stopped at eip: 0x%x\n", rcr2(), tf->eip); 97 | // lazy map to the allocated physical memory page with user R/W 98 | mappages(myproc()->pgdir, (void *)PGROUNDDOWN(rcr2()), PGSIZE, V2P(new_mem), 99 | PTE_W | PTE_U); 100 | -- 101 | 2.24.3 (Apple Git-128) 102 | 103 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # xv6-jos-i386-lab 2 | My lab implementation of 6.828 **2018** OS course of MIT. 3 | 4 | All labs are finished. *(Lab 4 & Lab6 without challenge questions)*. 5 | 6 | Check complete source code here: https://github.com/k0Iry/6.828_2018_mit_jos 7 | 8 | # Get Started 9 | Build a docker image for this course: 10 | 11 | `docker build --tag 6.828 .` 12 | 13 | `docker run -d --name=6.828 -v /local/path/to/6.828/:/usr/src/app --rm -it kljsandjb/6.828:latest` 14 | 15 | Recommend to use **remote container** of Visual Studio Code. 16 | 17 | ## Please note! 18 | **On macOS or Windows, the performance of IO in docker container is really bad, so the compile & run time could be much longer than on the host.** 19 | 20 | On macOS, I built the tool chain, to use: 21 | 22 | ``` 23 | brew tap k0Iry/i386-jos-toolchains 24 | brew install i386-jos-elf-gcc i386-jos-elf-gdb 25 | ``` 26 | 27 | But still you need to compile the 6.828 QEMU here: https://pdos.csail.mit.edu/6.828/2018/tools.html 28 | 29 | # Results (ALL LABS finished): 30 | JOS: ![CI](https://github.com/k0Iry/6.828_2018_mit_jos/workflows/CI/badge.svg?branch=lab6) 31 | 32 | ## LAB 2: 33 | 34 | irytu@Irys-MBP ~/D/6/lab (lab2)> cat ../../grade-lab2.txt 35 | /Applications/Xcode.app/Contents/Developer/usr/bin/make clean 36 | ``` 37 | + mk obj/kern/kernel.img 38 | running JOS: (1.2s) 39 | Physical page allocator: OK 40 | Page management: OK 41 | Kernel page directory: OK 42 | Page management 2: OK 43 | Score: 70/70 44 | ``` 45 | ## LAB 3: 46 | 47 | ``` 48 | make[1]: Leaving directory '/usr/src/app/lab' 49 | divzero: OK (13.4s) 50 | softint: OK (9.8s) 51 | badsegment: OK (10.8s) 52 | Part A score: 30/30 53 | 54 | faultread: OK (14.2s) 55 | faultreadkernel: OK (9.8s) 56 | faultwrite: OK (8.4s) 57 | faultwritekernel: OK (11.0s) 58 | breakpoint: OK (11.5s) 59 | testbss: OK (11.6s) 60 | hello: OK (9.9s) 61 | buggyhello: OK (10.7s) 62 | buggyhello2: OK (10.3s) 63 | evilhello: OK (9.4s) 64 | Part B score: 50/50 65 | 66 | Score: 80/80 67 | ``` 68 | 69 | ## LAB 4: 70 | 71 | ``` 72 | make[1]: Leaving directory '/usr/src/app/lab' 73 | dumbfork: OK (13.3s) 74 | Part A score: 5/5 75 | 76 | faultread: OK (12.8s) 77 | faultwrite: OK (11.9s) 78 | faultdie: OK (10.6s) 79 | faultregs: OK (10.8s) 80 | faultalloc: OK (10.7s) 81 | faultallocbad: OK (12.9s) 82 | faultnostack: OK (11.7s) 83 | faultbadhandler: OK (11.9s) 84 | faultevilhandler: OK (10.6s) 85 | forktree: OK (13.2s) 86 | Part B score: 50/50 87 | 88 | spin: OK (13.1s) 89 | stresssched: OK (12.3s) 90 | sendpage: OK (11.2s) 91 | pingpong: OK (9.7s) 92 | primes: OK (13.3s) 93 | Part C score: 25/25 94 | 95 | Score: 80/80 96 | ``` 97 | 98 | ## LAB 5: 99 | 100 | ``` 101 | + mk obj/fs/clean-fs.img 102 | + cp obj/fs/clean-fs.img obj/fs/fs.img 103 | make[1]: Leaving directory '/usr/src/app/lab' 104 | internal FS tests [fs/test.c]: OK (18.3s) 105 | fs i/o: OK 106 | check_bc: OK 107 | check_super: OK 108 | check_bitmap: OK 109 | alloc_block: OK 110 | file_open: OK 111 | file_get_block: OK 112 | file_flush/file_truncate/file rewrite: OK 113 | testfile: OK (18.0s) 114 | serve_open/file_stat/file_close: OK 115 | file_read: OK 116 | file_write: OK 117 | file_read after file_write: OK 118 | open: OK 119 | large file: OK 120 | spawn via spawnhello: OK (14.5s) 121 | Protection I/O space: OK (15.3s) 122 | PTE_SHARE [testpteshare]: OK (15.3s) 123 | PTE_SHARE [testfdsharing]: OK (18.5s) 124 | start the shell [icode]: Timeout! OK (44.0s) 125 | testshell: OK (15.9s) 126 | primespipe: OK (22.6s) 127 | Score: 150/150 128 | ``` 129 | 130 | ## LAB 6: 131 | 132 | ``` 133 | make[1]: Leaving directory '/home/runner/work/6.828_2018_mit_jos/6.828_2018_mit_jos' 134 | testtime: OK (7.6s) 135 | pci attach: OK (0.7s) 136 | testoutput [5 packets]: OK (1.5s) 137 | testoutput [100 packets]: OK (1.1s) 138 | Part A score: 35/35 139 | 140 | testinput [5 packets]: OK (1.1s) 141 | testinput [100 packets]: OK (1.1s) 142 | tcp echo server [echosrv]: OK (1.8s) 143 | web server [httpd]: 144 | http://localhost:26003/: OK (1.0s) 145 | http://localhost:26003/index.html: OK (1.8s) 146 | http://localhost:26003/random_file.txt: OK (1.1s) 147 | Part B score: 70/70 148 | 149 | Score: 105/105 150 | ``` 151 | 152 | XV6: 153 | 154 | see homework folder 155 | -------------------------------------------------------------------------------- /homework/ph.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define SOL 9 | #define NBUCKET 5 10 | #define NKEYS 100000 11 | 12 | struct entry 13 | { 14 | int key; 15 | int value; 16 | struct entry *next; 17 | }; 18 | struct entry *table[NBUCKET]; 19 | int keys[NKEYS]; 20 | int nthread = 1; 21 | volatile int done; 22 | pthread_mutex_t locks[NBUCKET]; 23 | 24 | double 25 | now() 26 | { 27 | struct timeval tv; 28 | gettimeofday(&tv, 0); 29 | return tv.tv_sec + tv.tv_usec / 1000000.0; 30 | } 31 | 32 | static void 33 | print(void) 34 | { 35 | int i; 36 | struct entry *e; 37 | for (i = 0; i < NBUCKET; i++) 38 | { 39 | printf("%d: ", i); 40 | for (e = table[i]; e != 0; e = e->next) 41 | { 42 | printf("%d ", e->key); 43 | } 44 | printf("\n"); 45 | } 46 | } 47 | 48 | static void 49 | insert(int key, int value, struct entry **p, struct entry *n) 50 | { 51 | struct entry *e = malloc(sizeof(struct entry)); 52 | e->key = key; 53 | e->value = value; 54 | e->next = n; 55 | *p = e; 56 | } 57 | 58 | static void put(int key, int value) 59 | { 60 | int i = key % NBUCKET; 61 | // [i] could be the same for multi threads, if so 62 | // memory referred by &table[i] will be override. 63 | // we lost key-value pair potentially 64 | 65 | // let's think about this case: for the first bucket 66 | // where [i == 0]. 67 | // 68 | // p -> entry_0 and we are about to insert new entries 69 | // from thread 0 and thread 1 70 | // 71 | // thread 0 and thread 1 both take entry_0 as next one 72 | // either: p->entry_thread0->entry_0 73 | // or: p->entry_thread1->entry_0 74 | // but what we expect is: 75 | // p->entry_thread0/1->entry_thread1/0->entry_0 76 | pthread_mutex_lock(locks + i); 77 | insert(key, value, &table[i], table[i]); 78 | pthread_mutex_unlock(locks + i); 79 | } 80 | 81 | static struct entry * 82 | get(int key) 83 | { 84 | // [get] in this case doesn't need a lock 85 | // since [get] happens after ALL [put], otherwise 86 | // [get] probably needs to acquire the lock released 87 | // by e.g. the [put] 88 | struct entry *e = 0; 89 | for (e = table[key % NBUCKET]; e != 0; e = e->next) 90 | { 91 | if (e->key == key) 92 | break; 93 | } 94 | return e; 95 | } 96 | 97 | static void * 98 | thread(void *xa) 99 | { 100 | long n = (long)xa; 101 | int i; 102 | int b = NKEYS / nthread; 103 | int k = 0; 104 | double t1, t0; 105 | 106 | // printf("b = %d\n", b); 107 | t0 = now(); 108 | for (i = 0; i < b; i++) 109 | { 110 | // printf("%d: put %d\n", n, b*n+i); 111 | put(keys[b * n + i], n); 112 | } 113 | t1 = now(); 114 | printf("%ld: put time = %f\n", n, t1 - t0); 115 | 116 | // Should use pthread_barrier, but MacOS doesn't support it ... 117 | __sync_fetch_and_add(&done, 1); 118 | while (done < nthread) 119 | ; 120 | 121 | t0 = now(); 122 | for (i = 0; i < NKEYS; i++) 123 | { 124 | struct entry *e = get(keys[i]); 125 | if (e == 0) 126 | k++; 127 | } 128 | t1 = now(); 129 | printf("%ld: get time = %f\n", n, t1 - t0); 130 | printf("%ld: %d keys missing\n", n, k); 131 | return NULL; 132 | } 133 | 134 | int main(int argc, char *argv[]) 135 | { 136 | pthread_t *tha; 137 | void *value; 138 | long i; 139 | double t1, t0; 140 | 141 | if (argc < 2) 142 | { 143 | fprintf(stderr, "%s: %s nthread\n", argv[0], argv[0]); 144 | exit(-1); 145 | } 146 | nthread = atoi(argv[1]); 147 | tha = malloc(sizeof(pthread_t) * nthread); 148 | srandom(0); 149 | assert(NKEYS % nthread == 0); 150 | for (i = 0; i < NKEYS; i++) 151 | { 152 | keys[i] = random(); 153 | } 154 | for (i = 0; i < NBUCKET; i++) 155 | { 156 | pthread_mutex_init(locks + i, NULL); 157 | } 158 | t0 = now(); 159 | for (i = 0; i < nthread; i++) 160 | { 161 | assert(pthread_create(&tha[i], NULL, thread, (void *)i) == 0); 162 | } 163 | for (i = 0; i < nthread; i++) 164 | { 165 | assert(pthread_join(tha[i], &value) == 0); 166 | } 167 | t1 = now(); 168 | printf("completion time = %f\n", t1 - t0); 169 | } 170 | -------------------------------------------------------------------------------- /labs/0001-challenge-IRQ-driven-transmit-receive.patch: -------------------------------------------------------------------------------- 1 | From d6973cfd19884a80f86a0768e3ed6a14d1058be5 Mon Sep 17 00:00:00 2001 2 | From: Aaron 3 | Date: Mon, 12 Oct 2020 19:37:42 +0200 4 | Subject: [PATCH] challenge: IRQ driven transmit & receive 5 | 6 | enable the interrupts for net card by 7 | writing to 8259A chip, enable transmit 8 | write-back interrupt for full queue 9 | --- 10 | inc/env.h | 3 ++- 11 | kern/e1000.c | 27 ++++++++++++++++++++++++--- 12 | kern/e1000.h | 1 + 13 | kern/pci.c | 3 +++ 14 | kern/sched.c | 3 ++- 15 | kern/trap.c | 6 ++++++ 16 | 6 files changed, 38 insertions(+), 5 deletions(-) 17 | 18 | diff --git a/inc/env.h b/inc/env.h 19 | index 43b1f30..9595284 100644 20 | --- a/inc/env.h 21 | +++ b/inc/env.h 22 | @@ -36,7 +36,8 @@ enum { 23 | ENV_RUNNABLE, 24 | ENV_RUNNING, 25 | ENV_NOT_RUNNABLE, 26 | - ENV_IDE_SLEEPING 27 | + ENV_IDE_SLEEPING, 28 | + ENV_NS_WAITING 29 | }; 30 | 31 | // Special environment types 32 | diff --git a/kern/e1000.c b/kern/e1000.c 33 | index 46cb455..962fe9f 100644 34 | --- a/kern/e1000.c 35 | +++ b/kern/e1000.c 36 | @@ -2,6 +2,7 @@ 37 | #include 38 | #include 39 | #include 40 | +#include 41 | 42 | // LAB 6: Your driver code here 43 | 44 | @@ -26,6 +27,15 @@ 45 | #define E1000_TXD_CMD_EOP 0x01000000 /* End of Packet */ 46 | #define E1000_TXD_STAT_DD 0x00000001 /* Descriptor Done */ 47 | 48 | +// interrupt-driven 49 | +#define E1000_ICR (0x000C0/4) /* Interrupt Cause Read - R/clr */ 50 | + #define E1000_ICR_TXDW 0x00000001 /* Transmit desc written back */ 51 | + 52 | +#define E1000_IMS (0x000D0/4) /* Interrupt Mask Set - RW */ 53 | + #define E1000_IMS_TXDW E1000_ICR_TXDW /* Transmit desc written back */ 54 | + 55 | +#define E1000_IMC 0x000D8 /* Interrupt Mask Clear - WO */ 56 | + 57 | // RX 58 | #define E1000_RDBAL (0x02800/4) /* RX Descriptor Base Address Low - RW */ 59 | #define E1000_RDBAH (0x02804/4) /* RX Descriptor Base Address High - RW */ 60 | @@ -37,7 +47,6 @@ 61 | #define E1000_RAL (0x05400/4) /* Receive Address Low - RW Array */ 62 | #define E1000_RAH (0x05404/4) /* Receive Address High - RW Array */ 63 | #define E1000_RAH_AV 0x80000000 /* Receive descriptor valid */ 64 | -#define E1000_IMS (0x000D0/4) /* Interrupt Mask Set - RW */ 65 | 66 | #define E1000_RXD_STAT_DD 0x01 /* Descriptor Done */ 67 | #define E1000_RXD_STAT_EOP 0x02 /* End of Packet */ 68 | @@ -135,8 +144,6 @@ static void init_rx() 69 | e1000_bar0[E1000_MTA + 1] = 0; 70 | e1000_bar0[E1000_MTA + 2] = 0; 71 | e1000_bar0[E1000_MTA + 3] = 0; 72 | - // not enable IRQ for now 73 | - e1000_bar0[E1000_IMS] = 0; 74 | e1000_bar0[E1000_RCTL] = E1000_RCTL_EN | E1000_RCTL_SECRC | E1000_RCTL_SZ_2048 | E1000_RCTL_BAM; 75 | } 76 | 77 | @@ -175,6 +182,10 @@ size_t e1000_transmit(const void *buffer, size_t size) 78 | } 79 | // require for re-transmission 80 | cprintf("lost packet 0x%x\n", buffer); 81 | + e1000_bar0[E1000_IMS] |= E1000_IMS_TXDW; 82 | + curenv->env_status = ENV_NS_WAITING; 83 | + extern void sched_yield(); 84 | + sched_yield(); 85 | return 0; 86 | } 87 | 88 | @@ -205,4 +216,14 @@ size_t e1000_receive(void *buffer, size_t size) 89 | e1000_bar0[E1000_RDT] = current; 90 | 91 | return length; 92 | +} 93 | + 94 | +// interrupt handler for transmit full queue or receive empty queue 95 | +void e1000_intr() 96 | +{ 97 | + if (e1000_bar0[E1000_ICR] & E1000_ICR_TXDW) 98 | + { 99 | + cprintf("E1000_ICR_TXDW\n"); 100 | + } 101 | + e1000_bar0[E1000_IMC] = ~0b0; 102 | } 103 | \ No newline at end of file 104 | diff --git a/kern/e1000.h b/kern/e1000.h 105 | index f82e492..ed05ea5 100644 106 | --- a/kern/e1000.h 107 | +++ b/kern/e1000.h 108 | @@ -12,6 +12,7 @@ volatile uint32_t *e1000_bar0; // memory mapped E1000 device registers 109 | 110 | size_t e1000_transmit(const void *buffer, size_t size); 111 | size_t e1000_receive(void *buffer, size_t size); 112 | +void e1000_intr(); 113 | 114 | 115 | #endif // SOL >= 6 116 | diff --git a/kern/pci.c b/kern/pci.c 117 | index 5ad5ae7..5cc6f2a 100644 118 | --- a/kern/pci.c 119 | +++ b/kern/pci.c 120 | @@ -5,6 +5,7 @@ 121 | #include 122 | #include 123 | #include 124 | +#include 125 | 126 | // Flag to do "lspci" at bootup 127 | static int pci_show_devs = 1; 128 | @@ -253,6 +254,8 @@ pci_func_enable(struct pci_func *f) 129 | PCI_VENDOR(f->dev_id), PCI_PRODUCT(f->dev_id)); 130 | cprintf("Device status for E1000 BAR 0 is 0x%x\n", 131 | e1000_bar0[E1000_STATUS]); 132 | + // enable pci interrupts 133 | + irq_setmask_8259A(irq_mask_8259A & ~(1<irq_line)); 134 | } 135 | 136 | int 137 | diff --git a/kern/sched.c b/kern/sched.c 138 | index dab7b3b..e7e4400 100644 139 | --- a/kern/sched.c 140 | +++ b/kern/sched.c 141 | @@ -65,7 +65,8 @@ sched_halt(void) 142 | if ((envs[i].env_status == ENV_RUNNABLE || 143 | envs[i].env_status == ENV_RUNNING || 144 | envs[i].env_status == ENV_DYING || 145 | - envs[i].env_status == ENV_IDE_SLEEPING)) 146 | + envs[i].env_status == ENV_IDE_SLEEPING) || 147 | + envs[i].env_status == ENV_NS_WAITING) 148 | break; 149 | } 150 | if (i == NENV) { 151 | diff --git a/kern/trap.c b/kern/trap.c 152 | index ce81dd4..02323bd 100644 153 | --- a/kern/trap.c 154 | +++ b/kern/trap.c 155 | @@ -14,6 +14,7 @@ 156 | #include 157 | #include 158 | #include 159 | +#include 160 | 161 | static struct Taskstate ts; 162 | 163 | @@ -287,6 +288,11 @@ trap_dispatch(struct Trapframe *tf) 164 | } 165 | } 166 | 167 | + case IRQ_OFFSET + 11: 168 | + e1000_intr(); 169 | + lapic_eoi(); 170 | + return; 171 | + 172 | default: 173 | break; 174 | } 175 | -- 176 | 2.24.3 (Apple Git-128) 177 | 178 | -------------------------------------------------------------------------------- /homework/0001-chapter-2-Page-Tables-Exercise-1-3.patch: -------------------------------------------------------------------------------- 1 | From 35ca2719a13f6c139849f451081c7d121f1c5cd3 Mon Sep 17 00:00:00 2001 2 | From: Aaron 3 | Date: Sat, 6 Jun 2020 20:01:21 +0200 4 | Subject: [PATCH] chapter 2 Page Tables, Exercise 1 - 3 5 | 6 | --- 7 | Makefile | 1 + 8 | exec.c | 18 ++++++++++++++++-- 9 | kalloc.c | 2 +- 10 | main.c | 7 ++++++- 11 | sbrktest.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 12 | vm.c | 2 +- 13 | 6 files changed, 71 insertions(+), 5 deletions(-) 14 | create mode 100644 sbrktest.c 15 | 16 | diff --git a/Makefile b/Makefile 17 | index e26ed07..60cef2d 100644 18 | --- a/Makefile 19 | +++ b/Makefile 20 | @@ -182,6 +182,7 @@ UPROGS=\ 21 | _wc\ 22 | _zombie\ 23 | _date\ 24 | + _sbrktest\ 25 | 26 | fs.img: mkfs README $(UPROGS) 27 | ./mkfs fs.img README $(UPROGS) 28 | diff --git a/exec.c b/exec.c 29 | index b40134f..0833f06 100644 30 | --- a/exec.c 31 | +++ b/exec.c 32 | @@ -72,13 +72,25 @@ exec(char *path, char **argv) 33 | for(argc = 0; argv[argc]; argc++) { 34 | if(argc >= MAXARG) 35 | goto bad; 36 | - sp = (sp - (strlen(argv[argc]) + 1)) & ~3; 37 | + sp = (sp - (strlen(argv[argc]) + 1)) & ~3; // if argv is too large, might hit inaccessible area 38 | if(copyout(pgdir, sp, argv[argc], strlen(argv[argc]) + 1) < 0) 39 | goto bad; 40 | - ustack[3+argc] = sp; 41 | + ustack[3+argc] = sp; // save the address of each argument 42 | } 43 | ustack[3+argc] = 0; 44 | 45 | + // ustack structure (will be copied to user runtime stack): 46 | + /*| 0 |*/ /*high*/ 47 | + /*|------------|*/ 48 | + /*| &argv[0] |*/ 49 | + /*| &argv[1] |*/ 50 | + /*| ...... |*/ 51 | + /*| &argv[N] |*/ 52 | + /*|------------|*/ 53 | + /*| argv ptr |*/ 54 | + /*| argc |*/ 55 | + /*| 0xffffffff |*/ /*low*/ 56 | + 57 | ustack[0] = 0xffffffff; // fake return PC 58 | ustack[1] = argc; 59 | ustack[2] = sp - (argc+1)*4; // argv pointer 60 | @@ -102,6 +114,8 @@ exec(char *path, char **argv) 61 | switchuvm(curproc); 62 | freevm(oldpgdir); 63 | return 0; 64 | + // drop into trapret, restore new process context with new image installed, 65 | + // the curproc's kernel stack contains new information needed (eip, esp, pgdir...) 66 | 67 | bad: 68 | if(pgdir) 69 | diff --git a/kalloc.c b/kalloc.c 70 | index 14cd4f4..3f6e10b 100644 71 | --- a/kalloc.c 72 | +++ b/kalloc.c 73 | @@ -70,7 +70,7 @@ kfree(char *v) 74 | if(kmem.use_lock) 75 | acquire(&kmem.lock); 76 | r = (struct run*)v; 77 | - r->next = kmem.freelist; 78 | + r->next = kmem.freelist; // store struct in page itself 79 | kmem.freelist = r; 80 | if(kmem.use_lock) 81 | release(&kmem.lock); 82 | diff --git a/main.c b/main.c 83 | index 9924e64..20f2c2f 100644 84 | --- a/main.c 85 | +++ b/main.c 86 | @@ -18,7 +18,7 @@ int 87 | main(void) 88 | { 89 | kinit1(end, P2V(4*1024*1024)); // phys page allocator 90 | - kvmalloc(); // kernel page table 91 | + kvmalloc(); // kernel page table, after this, entrypgdir will expire 92 | mpinit(); // detect other processors 93 | lapicinit(); // interrupt controller 94 | seginit(); // segment descriptors 95 | @@ -99,6 +99,11 @@ startothers(void) 96 | // hence the __aligned__ attribute. 97 | // PTE_PS in a page directory entry enables 4Mbyte pages. 98 | 99 | +// When PTE_PS is enabled, the pgdir entries below (entrypgdir[0] and entrypgdir[512]) 100 | +// directly point to a physical page(4MB), only 1 level addressing 101 | + 102 | +// if PTE_PS is clear, these entries point to a page table page (4K), in this case we 103 | +// also need to setup page table separately, to have 2 levels addressing 104 | __attribute__((__aligned__(PGSIZE))) 105 | pde_t entrypgdir[NPDENTRIES] = { 106 | // Map VA's [0, 4MB) to PA's [0, 4MB) 107 | diff --git a/sbrktest.c b/sbrktest.c 108 | new file mode 100644 109 | index 0000000..d8c7186 110 | --- /dev/null 111 | +++ b/sbrktest.c 112 | @@ -0,0 +1,46 @@ 113 | +#include "types.h" 114 | +#include "user.h" 115 | + 116 | +// In qemu console: 117 | +// 1. use 'info cpus' to see if you are in user space 118 | +// (qemu) cpu 1 119 | +// (qemu) info cpus 120 | +// CPU #0: pc=0x0000000080103d20 thread_id=17473 121 | +//* CPU #1: pc=0x0000000000000001 thread_id=17473 122 | +// 123 | +// 2. if you are not, use 'cpu 1' to set default CPU (thread) 124 | +// 3. 'info pg' and we now can see user space page mappings: 125 | +// 126 | +// breakpoint set: sys_sbrk(line 58 to avoid race condition)->trapret 127 | +// 128 | +// before sbrk 129 | +// (qemu) info pg 130 | +// VPN range Entry Flags Physical page 131 | +// [00000-003ff] PDE[000] ----A--UWP 132 | +// [00000-00000] PTE[000] ----A--UWP 0dee2 133 | +// [00001-00001] PTE[001] --------WP 0dee0 134 | +// [00002-00002] PTE[002] ---DA--UWP 0dedf 135 | + 136 | +// after sbrk 137 | +// (qemu) info pg 138 | +// VPN range Entry Flags Physical page 139 | +// [00000-003ff] PDE[000] ----A--UWP 140 | +// [00000-00000] PTE[000] ----A--UWP 0dee2 141 | +// [00001-00001] PTE[001] --------WP 0dee0 142 | +// [00002-00002] PTE[002] ---DA--UWP 0dedf 143 | +// [00003-00003] PTE[003] -------UWP 0dfbc (newly allocated page after sbrk(1)) 144 | + 145 | +// a new physical page will be allocted and page table also will be updated 146 | + 147 | +int main(int argc, char *argv[]) 148 | +{ 149 | + if (sbrk(1) == (char *)-1) 150 | + { 151 | + printf(2, "sbrk failed\n"); 152 | + exit(); 153 | + } 154 | + 155 | + printf(1, "sbrk success\n"); 156 | + 157 | + exit(); // must be exit(), if we return, fake PC 0xffffffff will take over, then got killed... 158 | +} 159 | \ No newline at end of file 160 | diff --git a/vm.c b/vm.c 161 | index 7134cff..7ca8f7a 100644 162 | --- a/vm.c 163 | +++ b/vm.c 164 | @@ -237,7 +237,7 @@ allocuvm(pde_t *pgdir, uint oldsz, uint newsz) 165 | deallocuvm(pgdir, newsz, oldsz); 166 | return 0; 167 | } 168 | - memset(mem, 0, PGSIZE); 169 | + memset(mem, 0, PGSIZE); // when load new binary, if there is any gap (memsz - filesz), fill with 0 170 | if(mappages(pgdir, (char*)a, PGSIZE, V2P(mem), PTE_W|PTE_U) < 0){ 171 | cprintf("allocuvm out of memory (2)\n"); 172 | deallocuvm(pgdir, newsz, oldsz); 173 | -- 174 | 2.24.3 (Apple Git-128) 175 | 176 | -------------------------------------------------------------------------------- /homework/0001-uthreads.patch: -------------------------------------------------------------------------------- 1 | From 99ed23108c896f36e011202b48514cc157e27159 Mon Sep 17 00:00:00 2001 2 | From: Aaron 3 | Date: Sat, 1 Aug 2020 20:29:38 +0000 4 | Subject: [PATCH] uthreads 5 | 6 | --- 7 | Makefile | 5 +++ 8 | proc.c | 3 ++ 9 | trap.c | 2 +- 10 | uthread.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++ 11 | uthread_switch.S | 36 +++++++++++++++ 12 | 5 files changed, 159 insertions(+), 1 deletion(-) 13 | create mode 100644 uthread.c 14 | create mode 100644 uthread_switch.S 15 | 16 | diff --git a/Makefile b/Makefile 17 | index f026203..e84f397 100644 18 | --- a/Makefile 19 | +++ b/Makefile 20 | @@ -156,6 +156,10 @@ _forktest: forktest.o $(ULIB) 21 | $(LD) $(LDFLAGS) -N -e main -Ttext 0 -o _forktest forktest.o ulib.o usys.o 22 | $(OBJDUMP) -S _forktest > forktest.asm 23 | 24 | +_uthread: uthread.o uthread_switch.o 25 | + $(LD) $(LDFLAGS) -N -e main -Ttext 0 -o _uthread uthread.o uthread_switch.o $(ULIB) 26 | + $(OBJDUMP) -S _uthread > uthread.asm 27 | + 28 | mkfs: mkfs.c fs.h 29 | gcc -Werror -Wno-nullability-completeness -Wall -o mkfs mkfs.c 30 | 31 | @@ -184,6 +188,7 @@ UPROGS=\ 32 | _date\ 33 | _sbrktest\ 34 | _alarmtest\ 35 | + _uthread\ 36 | 37 | fs.img: mkfs README $(UPROGS) 38 | ./mkfs fs.img README $(UPROGS) 39 | diff --git a/proc.c b/proc.c 40 | index 54f2d9d..bf1fec1 100644 41 | --- a/proc.c 42 | +++ b/proc.c 43 | @@ -367,6 +367,9 @@ void sched(void) { 44 | // Give up the CPU for one scheduling round. 45 | void yield(void) { 46 | acquire(&ptable.lock); // DOC: yieldlock 47 | + // must hold ptable.lock, otherwise as soon as process's 48 | + // state changes to RUNNABLE, the scheduler() may 49 | + // immediately pick this again and run it on another cpu 50 | myproc()->state = RUNNABLE; 51 | sched(); 52 | release(&ptable.lock); 53 | diff --git a/trap.c b/trap.c 54 | index 106e95b..0df84cc 100644 55 | --- a/trap.c 56 | +++ b/trap.c 57 | @@ -123,7 +123,7 @@ void trap(struct trapframe *tf) { 58 | // we don't allocate new memory for exceeding process size 59 | // if we implement malloc then I guess we do not need this condition 60 | // anymore? 61 | - cprintf("Invalid address 0x%x, will be panicked.\n", rcr2()); 62 | + cprintf("at eip 0x%x, trying to access invalid address 0x%x, will be panicked.\n", tf->eip, rcr2()); 63 | panic("Invalid address!\n"); 64 | } 65 | // what if we use system call write to the guard page, 66 | diff --git a/uthread.c b/uthread.c 67 | new file mode 100644 68 | index 0000000..66fc1f8 69 | --- /dev/null 70 | +++ b/uthread.c 71 | @@ -0,0 +1,114 @@ 72 | +#include "types.h" 73 | +#include "stat.h" 74 | +#include "user.h" 75 | + 76 | +/* Possible states of a thread; */ 77 | +#define FREE 0x0 78 | +#define RUNNING 0x1 79 | +#define RUNNABLE 0x2 80 | + 81 | +#define STACK_SIZE 8192 82 | +#define MAX_THREAD 4 83 | + 84 | +typedef struct thread thread_t, *thread_p; 85 | +typedef struct mutex mutex_t, *mutex_p; 86 | + 87 | +struct thread { 88 | + int sp; /* saved stack pointer */ 89 | + char stack[STACK_SIZE]; /* the thread's stack */ 90 | + int state; /* FREE, RUNNING, RUNNABLE */ 91 | +}; 92 | +static thread_t all_thread[MAX_THREAD]; 93 | +thread_p current_thread; 94 | +thread_p next_thread; 95 | +extern void thread_switch(void); 96 | + 97 | +void 98 | +thread_init(void) 99 | +{ 100 | + // main() is thread 0, which will make the first invocation to 101 | + // thread_schedule(). it needs a stack so that the first thread_switch() can 102 | + // save thread 0's state. thread_schedule() won't run the main thread ever 103 | + // again, because its state is set to RUNNING, and thread_schedule() selects 104 | + // a RUNNABLE thread. 105 | + current_thread = &all_thread[0]; 106 | + current_thread->state = RUNNING; 107 | +} 108 | + 109 | +static void 110 | +thread_schedule(void) 111 | +{ 112 | + thread_p t; 113 | + 114 | + /* Find another runnable thread. */ 115 | + next_thread = 0; 116 | + for (t = all_thread; t < all_thread + MAX_THREAD; t++) { 117 | + if (t->state == RUNNABLE && t != current_thread) { 118 | + next_thread = t; 119 | + break; 120 | + } 121 | + } 122 | + 123 | + if (t >= all_thread + MAX_THREAD && current_thread->state == RUNNABLE) { 124 | + /* The current thread is the only runnable thread; run it. */ 125 | + next_thread = current_thread; 126 | + } 127 | + 128 | + if (next_thread == 0) { 129 | + printf(2, "thread_schedule: no runnable threads\n"); 130 | + exit(); 131 | + } 132 | + 133 | + if (current_thread != next_thread) { /* switch threads? */ 134 | + next_thread->state = RUNNING; 135 | + thread_switch(); 136 | + } else 137 | + next_thread = 0; 138 | +} 139 | + 140 | +void 141 | +thread_create(void (*func)()) 142 | +{ 143 | + thread_p t; 144 | + 145 | + for (t = all_thread; t < all_thread + MAX_THREAD; t++) { 146 | + if (t->state == FREE) break; 147 | + } 148 | + t->sp = (int) (t->stack + STACK_SIZE); // set sp to the top of the stack 149 | + t->sp -= 4; // space for return address 150 | + * (int *) (t->sp) = (int)func; // push return address on stack 151 | + t->sp -= 32; // space for registers that thread_switch expects 152 | + t->state = RUNNABLE; 153 | +} 154 | + 155 | +void 156 | +thread_yield(void) 157 | +{ 158 | + current_thread->state = RUNNABLE; 159 | + thread_schedule(); 160 | +} 161 | + 162 | +static void 163 | +mythread(void) 164 | +{ 165 | + int i; 166 | + printf(1, "my thread running\n"); 167 | + for (i = 0; i < 100; i++) { 168 | + printf(1, "my thread 0x%x\n", (int) current_thread); 169 | + thread_yield(); 170 | + } 171 | + printf(1, "my thread: exit\n"); 172 | + current_thread->state = FREE; 173 | + thread_schedule(); 174 | +} 175 | + 176 | + 177 | +int 178 | +main(int argc, char *argv[]) 179 | +{ 180 | + thread_init(); 181 | + thread_create(mythread); 182 | + thread_create(mythread); 183 | + thread_schedule(); 184 | + return 0; 185 | +} 186 | diff --git a/uthread_switch.S b/uthread_switch.S 187 | new file mode 100644 188 | index 0000000..58148d3 189 | --- /dev/null 190 | +++ b/uthread_switch.S 191 | @@ -0,0 +1,36 @@ 192 | + .text 193 | + 194 | +/* Switch from current_thread to next_thread. Make next_thread 195 | + * the current_thread, and set next_thread to 0. 196 | + * Use eax as a temporary register; it is caller saved. 197 | + */ 198 | + .globl thread_switch 199 | +thread_switch: 200 | + /* YOUR CODE HERE */ 201 | + pushal # save registers 202 | + movl current_thread, %eax 203 | + movl %esp, (%eax) # save current state in current_thread->sp 204 | + 205 | + movl next_thread, %eax 206 | + movl (%eax), %esp # update %esp with next_thread->sp 207 | + 208 | + # update the pointers 209 | + movl %eax, current_thread 210 | + movl $0, next_thread 211 | + 212 | + popal # restore registers 213 | + ret /* pop return address from stack */ 214 | + 215 | +/* 216 | +(gdb) x/9x next_thread->sp 217 | +0x6cf4 : 0x00000000 0x00000000 0x00006d30 0x00006d14 218 | +0x6d04 : 0x0000005a 0x00004d30 0x0000000a 0x00002d28 219 | +0x6d14 : 0x00000179 220 | + 221 | +0x00000179 is the return address of caller of thread_switch (thread_schedule), during thread's 222 | +running, when it calls into thread_schedule(), it will push 0x00000179 on the stack, then it 223 | +drops into thread_switch(), push the 8 general registers (%eax, %ecx, %edx, %ebx, %esp, %ebp, %esi, %edi) 224 | + 225 | + 174: e8 27 ff ff ff call a0 226 | + 179: 83 c4 10 add $0x10,%esp 227 | +*/ 228 | \ No newline at end of file 229 | -- 230 | 2.20.1 231 | 232 | -------------------------------------------------------------------------------- /homework/0001-xv6-lazy-page-allocation.patch: -------------------------------------------------------------------------------- 1 | From 099e5c33d55aeb9dcd4cbf3fb8c55bdd07d309fd Mon Sep 17 00:00:00 2001 2 | From: Aaron 3 | Date: Wed, 10 Jun 2020 18:33:28 +0200 4 | Subject: [PATCH] xv6 lazy page allocation 5 | 6 | --- 7 | defs.h | 2 +- 8 | exec.c | 7 +++++-- 9 | proc.c | 36 ++++++++++++++++++------------------ 10 | sbrktest.c | 12 +++++++++++- 11 | sysproc.c | 15 +++++++++++---- 12 | trap.c | 31 +++++++++++++++++++++++++++++++ 13 | vm.c | 18 +++++++++++++++++- 14 | 7 files changed, 94 insertions(+), 27 deletions(-) 15 | 16 | diff --git a/defs.h b/defs.h 17 | index 82fb982..8237829 100644 18 | --- a/defs.h 19 | +++ b/defs.h 20 | @@ -106,7 +106,7 @@ int pipewrite(struct pipe*, char*, int); 21 | int cpuid(void); 22 | void exit(void); 23 | int fork(void); 24 | -int growproc(int); 25 | +// int growproc(int); 26 | int kill(int); 27 | struct cpu* mycpu(void); 28 | struct proc* myproc(); 29 | diff --git a/exec.c b/exec.c 30 | index 0833f06..d8d6738 100644 31 | --- a/exec.c 32 | +++ b/exec.c 33 | @@ -47,7 +47,7 @@ exec(char *path, char **argv) 34 | continue; 35 | if(ph.memsz < ph.filesz) 36 | goto bad; 37 | - if(ph.vaddr + ph.memsz < ph.vaddr) 38 | + if(ph.vaddr + ph.memsz < ph.vaddr) // check overflow to avoid attack, loaduvm will otherwise load code in kernel space 39 | goto bad; 40 | if((sz = allocuvm(pgdir, sz, ph.vaddr + ph.memsz)) == 0) 41 | goto bad; 42 | @@ -65,7 +65,7 @@ exec(char *path, char **argv) 43 | sz = PGROUNDUP(sz); 44 | if((sz = allocuvm(pgdir, sz, sz + 2*PGSIZE)) == 0) 45 | goto bad; 46 | - clearpteu(pgdir, (char*)(sz - 2*PGSIZE)); 47 | + clearpteu(pgdir, (char*)(sz - 2*PGSIZE)); // guarding a user stack with an unmapped page 48 | sp = sz; 49 | 50 | // Push argument strings, prepare rest of stack in ustack. 51 | @@ -105,6 +105,9 @@ exec(char *path, char **argv) 52 | last = s+1; 53 | safestrcpy(curproc->name, last, sizeof(curproc->name)); 54 | 55 | + // A process’s most important pieces of kernel state are its page table, its kernel stack, and its run state 56 | + // below we change the page table, its kernel stack, leaving run state as the same 57 | + 58 | // Commit to the user image. 59 | oldpgdir = curproc->pgdir; 60 | curproc->pgdir = pgdir; 61 | diff --git a/proc.c b/proc.c 62 | index 4fb202e..f0eada5 100644 63 | --- a/proc.c 64 | +++ b/proc.c 65 | @@ -155,24 +155,24 @@ userinit(void) 66 | 67 | // Grow current process's memory by n bytes. 68 | // Return 0 on success, -1 on failure. 69 | -int 70 | -growproc(int n) 71 | -{ 72 | - uint sz; 73 | - struct proc *curproc = myproc(); 74 | - 75 | - sz = curproc->sz; 76 | - if(n > 0){ 77 | - if((sz = allocuvm(curproc->pgdir, sz, sz + n)) == 0) 78 | - return -1; 79 | - } else if(n < 0){ 80 | - if((sz = deallocuvm(curproc->pgdir, sz, sz + n)) == 0) 81 | - return -1; 82 | - } 83 | - curproc->sz = sz; 84 | - switchuvm(curproc); 85 | - return 0; 86 | -} 87 | +// int 88 | +// growproc(int n) 89 | +// { 90 | +// uint sz; 91 | +// struct proc *curproc = myproc(); 92 | + 93 | +// sz = curproc->sz; 94 | +// if(n > 0){ 95 | +// if((sz = allocuvm(curproc->pgdir, sz, sz + n)) == 0) 96 | +// return -1; 97 | +// } else if(n < 0){ 98 | +// if((sz = deallocuvm(curproc->pgdir, sz, sz + n)) == 0) 99 | +// return -1; 100 | +// } 101 | +// curproc->sz = sz; 102 | +// switchuvm(curproc); 103 | +// return 0; 104 | +// } 105 | 106 | // Create a new process copying p as the parent. 107 | // Sets up stack to return as if from system call. 108 | diff --git a/sbrktest.c b/sbrktest.c 109 | index d8c7186..bd969ff 100644 110 | --- a/sbrktest.c 111 | +++ b/sbrktest.c 112 | @@ -34,11 +34,21 @@ 113 | 114 | int main(int argc, char *argv[]) 115 | { 116 | - if (sbrk(1) == (char *)-1) 117 | + char *addr = sbrk(1); 118 | + if (addr == (char *)-1) 119 | { 120 | printf(2, "sbrk failed\n"); 121 | exit(); 122 | } 123 | + *addr = 'a'; // write above stack (heap) to generate a page fault and self-correction 124 | + // when use lazy allocation, the mapping changes above would become: 125 | + // 1. before *addr = 'a', [00002-00002] PTE[002] ---DA--UWP 0dee5 126 | + // 2. after *addr = 'a', [00002-00003] PTE[002-003] ---DA--UWP 0dee5 0dfbc (new page has been allocated on the fly) 127 | + 128 | + // write just 1 byte will trigger the page fault, cause one physical page got mapped, so next time, we access 129 | + // within one page like: (addr + 1), (addr + 2), ... , (addr + 4095), we kind of got a cache-hit 130 | + 131 | + // *(addr + 4096) = 'b'; //this should cause a kernel panic, because we did not increase process size that much 132 | 133 | printf(1, "sbrk success\n"); 134 | 135 | diff --git a/sysproc.c b/sysproc.c 136 | index 2a38eb9..e192bb6 100644 137 | --- a/sysproc.c 138 | +++ b/sysproc.c 139 | @@ -51,10 +51,17 @@ sys_sbrk(void) 140 | 141 | if(argint(0, &n) < 0) 142 | return -1; 143 | - cprintf("sbrk increments data size by %d bytes\n",n); 144 | - addr = myproc()->sz; 145 | - if(growproc(n) < 0) 146 | - return -1; 147 | + struct proc *curproc = myproc(); 148 | + addr = curproc->sz; 149 | + cprintf("sbrk increments data size from 0x%x to 0x%x\n", addr, addr + n); 150 | + 151 | + if (n < 0) 152 | + { 153 | + // shrink process address space 154 | + deallocuvm(curproc->pgdir, addr, addr + n); 155 | + } 156 | + curproc->sz = addr + n; 157 | + 158 | return addr; 159 | } 160 | 161 | diff --git a/trap.c b/trap.c 162 | index b76cfae..352f0b0 100644 163 | --- a/trap.c 164 | +++ b/trap.c 165 | @@ -13,6 +13,8 @@ struct gatedesc idt[256]; 166 | extern uint vectors[]; // in vectors.S: array of 256 entry pointers 167 | struct spinlock tickslock; 168 | uint ticks; 169 | +extern int mappages(pde_t *pgdir, void *va, uint size, uint pa, int perm); 170 | +extern int hitguardpage(pte_t *pgdir, char *uva); 171 | 172 | void 173 | tvinit(void) 174 | @@ -77,6 +79,35 @@ trap(struct trapframe *tf) 175 | cpuid(), tf->cs, tf->eip); 176 | lapiceoi(); 177 | break; 178 | + case T_PGFLT: 179 | + { 180 | + if (rcr2() >= myproc()->sz) 181 | + { 182 | + // we don't allocate new memory for exceeding process size 183 | + // if we implement malloc then I guess we do not need this condition anymore? 184 | + cprintf("Invalid address 0x%x, will be panicked.\n", rcr2()); 185 | + panic("Invalid address!\n"); 186 | + } 187 | + // what if we use system call write to the guard page, 188 | + // which we should not write to? we can check if PTE_U is enabled 189 | + // we do not allocate memory for this area 190 | + if (hitguardpage(myproc()->pgdir, (char *)rcr2())) 191 | + { 192 | + panic("hitguardpage\n"); 193 | + } 194 | + 195 | + // find which page contains page fault virtual address with PGROUNDDOWN 196 | + char *new_mem = kalloc(); 197 | + if (new_mem == 0) 198 | + { 199 | + panic("Out of memory\n"); 200 | + } 201 | + memset(new_mem, 0, PGSIZE); 202 | + cprintf("page fault at address 0x%x, will be corrected.\n", rcr2()); 203 | + // lazy map to the allocated physical memory page with user R/W 204 | + mappages(myproc()->pgdir, (void *)PGROUNDDOWN(rcr2()), PGSIZE, V2P(new_mem), PTE_W | PTE_U); 205 | + break; 206 | + } 207 | 208 | //PAGEBREAK: 13 209 | default: 210 | diff --git a/vm.c b/vm.c 211 | index 7ca8f7a..1e821ed 100644 212 | --- a/vm.c 213 | +++ b/vm.c 214 | @@ -57,7 +57,7 @@ walkpgdir(pde_t *pgdir, const void *va, int alloc) 215 | // Create PTEs for virtual addresses starting at va that refer to 216 | // physical addresses starting at pa. va and size might not 217 | // be page-aligned. 218 | -static int 219 | +int 220 | mappages(pde_t *pgdir, void *va, uint size, uint pa, int perm) 221 | { 222 | char *a, *last; 223 | @@ -310,6 +310,22 @@ clearpteu(pde_t *pgdir, char *uva) 224 | *pte &= ~PTE_U; 225 | } 226 | 227 | +int hitguardpage(pte_t *pgdir, char *uva) 228 | +{ 229 | + pte_t *pte; 230 | + 231 | + pte = walkpgdir(pgdir, uva, 0); 232 | + if (pte == 0) 233 | + panic("hitguardpage"); 234 | + if (*pte && !(*pte & PTE_U)) 235 | + { 236 | + cprintf("hitguardpage on phy address: 0x%x\n", PTE_ADDR(*pte)); 237 | + return 1; 238 | + } 239 | + 240 | + return 0; 241 | +} 242 | + 243 | // Given a parent process's page table, create a copy 244 | // of it for a child. 245 | pde_t* 246 | -- 247 | 2.24.3 (Apple Git-128) 248 | 249 | -------------------------------------------------------------------------------- /homework/0001-HW-Big-files-finished.patch: -------------------------------------------------------------------------------- 1 | From 23904d031374caa3f3d3d16de45ce6cc5049b15b Mon Sep 17 00:00:00 2001 2 | From: Aaron 3 | Date: Thu, 15 Oct 2020 17:33:03 +0200 4 | Subject: [PATCH] HW: Big files finished 5 | 6 | --- 7 | Makefile | 4 +++- 8 | big.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 9 | file.h | 2 +- 10 | fs.c | 27 +++++++++++++++++++++++++++ 11 | fs.h | 6 +++--- 12 | param.h | 2 +- 13 | pipe.c | 4 ++++ 14 | syscall.c | 26 +++++++++++++------------- 15 | sysfile.c | 4 ++-- 16 | 9 files changed, 107 insertions(+), 21 deletions(-) 17 | create mode 100644 big.c 18 | 19 | diff --git a/Makefile b/Makefile 20 | index e84f397..25bbca6 100644 21 | --- a/Makefile 22 | +++ b/Makefile 23 | @@ -189,6 +189,7 @@ UPROGS=\ 24 | _sbrktest\ 25 | _alarmtest\ 26 | _uthread\ 27 | + _big\ 28 | 29 | fs.img: mkfs README $(UPROGS) 30 | ./mkfs fs.img README $(UPROGS) 31 | @@ -225,8 +226,9 @@ QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \ 32 | then echo "-gdb tcp::$(GDBPORT)"; \ 33 | else echo "-s -p $(GDBPORT)"; fi) 34 | ifndef CPUS 35 | -CPUS := 2 36 | +CPUS := 1 37 | endif 38 | +QEMUEXTRA = -snapshot 39 | QEMUOPTS = -drive file=fs.img,index=1,media=disk,format=raw -drive file=xv6.img,index=0,media=disk,format=raw -smp $(CPUS) -m 512 $(QEMUEXTRA) 40 | 41 | qemu: fs.img xv6.img 42 | diff --git a/big.c b/big.c 43 | new file mode 100644 44 | index 0000000..cecf1a0 45 | --- /dev/null 46 | +++ b/big.c 47 | @@ -0,0 +1,53 @@ 48 | +#include "types.h" 49 | +#include "stat.h" 50 | +#include "user.h" 51 | +#include "fcntl.h" 52 | + 53 | +int 54 | +main() 55 | +{ 56 | + char buf[512]; 57 | + int fd, i, sectors; 58 | + 59 | + fd = open("big.file", O_CREATE | O_WRONLY); 60 | + if(fd < 0){ 61 | + printf(2, "big: cannot open big.file for writing\n"); 62 | + exit(); 63 | + } 64 | + 65 | + sectors = 0; 66 | + while(1){ 67 | + *(int*)buf = sectors; 68 | + int cc = write(fd, buf, sizeof(buf)); 69 | + if(cc <= 0) 70 | + break; 71 | + sectors++; 72 | + if (sectors % 100 == 0) 73 | + printf(2, "."); 74 | + } 75 | + 76 | + printf(1, "\nwrote %d sectors\n", sectors); 77 | + 78 | + close(fd); 79 | + fd = open("big.file", O_RDONLY); 80 | + if(fd < 0){ 81 | + printf(2, "big: cannot re-open big.file for reading\n"); 82 | + exit(); 83 | + } 84 | + for(i = 0; i < sectors; i++){ 85 | + int cc = read(fd, buf, sizeof(buf)); 86 | + if(cc <= 0){ 87 | + printf(2, "big: read error at sector %d\n", i); 88 | + exit(); 89 | + } 90 | + if(*(int*)buf != i){ 91 | + printf(2, "big: read the wrong data (%d) for sector %d\n", 92 | + *(int*)buf, i); 93 | + exit(); 94 | + } 95 | + } 96 | + 97 | + printf(1, "done; ok\n"); 98 | + 99 | + exit(); 100 | +} 101 | diff --git a/file.h b/file.h 102 | index 4e613d7..5fb5420 100644 103 | --- a/file.h 104 | +++ b/file.h 105 | @@ -21,7 +21,7 @@ struct inode { 106 | short minor; 107 | short nlink; 108 | uint size; 109 | - uint addrs[NDIRECT + 1]; 110 | + uint addrs[NDIRECT + 2]; 111 | }; 112 | 113 | // table mapping major device number to 114 | diff --git a/fs.c b/fs.c 115 | index ea08c95..e238fcc 100644 116 | --- a/fs.c 117 | +++ b/fs.c 118 | @@ -367,6 +367,33 @@ static uint bmap(struct inode *ip, uint bn) { 119 | brelse(bp); 120 | return addr; 121 | } 122 | + bn -= NINDIRECT; 123 | + 124 | + if (bn < NINDIRECT * NINDIRECT) 125 | + { 126 | + if ((addr = ip->addrs[NDIRECT + 1]) == 0) 127 | + ip->addrs[NDIRECT + 1] = addr = balloc(ip->dev); 128 | + bp = bread(ip->dev, addr); 129 | + a = (uint *)bp->data; 130 | + if ((addr = a[bn / NINDIRECT]) == 0) 131 | + { 132 | + // allocate singly-indirect block as needed 133 | + a[bn / NINDIRECT] = addr = balloc(ip->dev); 134 | + log_write(bp); 135 | + } 136 | + brelse(bp); 137 | + // read 1 of 128 singly-indirect blocks 138 | + bp = bread(ip->dev, addr); 139 | + a = (uint *)bp->data; 140 | + if ((addr = a[bn % NINDIRECT]) == 0) 141 | + { 142 | + a[bn % NINDIRECT] = addr = balloc(ip->dev); 143 | + log_write(bp); 144 | + } 145 | + brelse(bp); 146 | + 147 | + return addr; 148 | + } 149 | 150 | panic("bmap: out of range"); 151 | } 152 | diff --git a/fs.h b/fs.h 153 | index ff03b95..8547ea7 100644 154 | --- a/fs.h 155 | +++ b/fs.h 156 | @@ -20,9 +20,9 @@ struct superblock { 157 | uint bmapstart; // Block number of first free map block 158 | }; 159 | 160 | -#define NDIRECT 12 161 | +#define NDIRECT 11 162 | #define NINDIRECT (BSIZE / sizeof(uint)) 163 | -#define MAXFILE (NDIRECT + NINDIRECT) 164 | +#define MAXFILE (NDIRECT + NINDIRECT + (NINDIRECT * NINDIRECT)) 165 | 166 | // On-disk inode structure 167 | struct dinode { 168 | @@ -31,7 +31,7 @@ struct dinode { 169 | short minor; // Minor device number (T_DEV only) 170 | short nlink; // Number of links to inode in file system 171 | uint size; // Size of file (bytes) 172 | - uint addrs[NDIRECT + 1]; // Data block addresses 173 | + uint addrs[NDIRECT + 2]; // Data block addresses 174 | }; 175 | 176 | // Inodes per block. 177 | diff --git a/param.h b/param.h 178 | index 5cade8f..35b52af 100644 179 | --- a/param.h 180 | +++ b/param.h 181 | @@ -10,4 +10,4 @@ 182 | #define MAXOPBLOCKS 10 // max # of blocks any FS op writes 183 | #define LOGSIZE (MAXOPBLOCKS * 3) // max data blocks in on-disk log 184 | #define NBUF (MAXOPBLOCKS * 3) // size of disk block cache 185 | -#define FSSIZE 1000 // size of file system in blocks 186 | +#define FSSIZE 20000 // size of file system in blocks 187 | diff --git a/pipe.c b/pipe.c 188 | index 0d9e9b8..634a7b4 100644 189 | --- a/pipe.c 190 | +++ b/pipe.c 191 | @@ -90,6 +90,10 @@ int pipewrite(struct pipe *p, char *addr, int n) { 192 | return -1; 193 | } 194 | wakeup(&p->nread); 195 | + // there is a race between sleep and kill 196 | + // if a p1 is killing p2 after p2's 'myproc()->killed' check, 197 | + // and before p2 goes to sleep, p1 lost wake up for the p2 198 | + // p2 will have to wait for another round to be killed 199 | sleep(&p->nwrite, &p->lock); // DOC: pipewrite-sleep 200 | } 201 | p->data[p->nwrite++ % PIPESIZE] = addr[i]; 202 | diff --git a/syscall.c b/syscall.c 203 | index 3f4edde..bd04eea 100644 204 | --- a/syscall.c 205 | +++ b/syscall.c 206 | @@ -114,17 +114,17 @@ static int (*syscalls[])(void) = { 207 | 208 | // use a string table for the name, any better idea? 209 | // use STAB to get the name from debug information, will do it later 210 | -static char *syscallnames[] = { 211 | - [SYS_fork] "fork", [SYS_exit] "exit", [SYS_wait] "wait", 212 | - [SYS_pipe] "pipe", [SYS_read] "read", [SYS_kill] "kill", 213 | - [SYS_exec] "exec", [SYS_fstat] "fstat", [SYS_chdir] "chdir", 214 | - [SYS_dup] "dup", [SYS_getpid] "getpid", [SYS_sbrk] "sbrk", 215 | - [SYS_sleep] "sleep", [SYS_uptime] "uptime", [SYS_open] "open", 216 | - [SYS_write] "write", [SYS_mknod] "mknod", [SYS_unlink] "unlink", 217 | - [SYS_link] "link", [SYS_mkdir] "mkdir", [SYS_close] "close", 218 | - [SYS_date] "date", [SYS_dup2] "dup2", [SYS_alarm] "alarm", 219 | - [SYS_clone] "clone", [SYS_sched_yield] "yield", 220 | -}; 221 | +// static char *syscallnames[] = { 222 | +// [SYS_fork] "fork", [SYS_exit] "exit", [SYS_wait] "wait", 223 | +// [SYS_pipe] "pipe", [SYS_read] "read", [SYS_kill] "kill", 224 | +// [SYS_exec] "exec", [SYS_fstat] "fstat", [SYS_chdir] "chdir", 225 | +// [SYS_dup] "dup", [SYS_getpid] "getpid", [SYS_sbrk] "sbrk", 226 | +// [SYS_sleep] "sleep", [SYS_uptime] "uptime", [SYS_open] "open", 227 | +// [SYS_write] "write", [SYS_mknod] "mknod", [SYS_unlink] "unlink", 228 | +// [SYS_link] "link", [SYS_mkdir] "mkdir", [SYS_close] "close", 229 | +// [SYS_date] "date", [SYS_dup2] "dup2", [SYS_alarm] "alarm", 230 | +// [SYS_clone] "clone", [SYS_sched_yield] "yield", 231 | +// }; 232 | 233 | // breakpoint here, x/37x 234 | // (gdb) x/37x $esp 235 | @@ -168,8 +168,8 @@ void syscall(void) { 236 | // cprintf("first argument: %s\n", argument); 237 | 238 | curproc->tf->eax = syscalls[num](); 239 | - if (num != SYS_write) 240 | - cprintf("%s -> %d\n\n", syscallnames[num], curproc->tf->eax); 241 | + // if (num != SYS_write) 242 | + // cprintf("%s -> %d\n\n", syscallnames[num], curproc->tf->eax); 243 | } else { 244 | cprintf("%d %s: unknown sys call %d\n", curproc->pid, curproc->name, num); 245 | curproc->tf->eax = -1; 246 | diff --git a/sysfile.c b/sysfile.c 247 | index 5db20fc..d019fa4 100644 248 | --- a/sysfile.c 249 | +++ b/sysfile.c 250 | @@ -88,8 +88,8 @@ int sys_read(void) { 251 | 252 | if (argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argptr(1, &p, n) < 0) 253 | return -1; 254 | - cprintf("read on file object %p, file buffer address: %p, size %d\n", f, p, 255 | - n); 256 | + // cprintf("read on file object %p, file buffer address: %p, size %d\n", f, p, 257 | + // n); 258 | return fileread(f, p, n); 259 | } 260 | 261 | -- 262 | 2.24.3 (Apple Git-128) 263 | 264 | -------------------------------------------------------------------------------- /homework/0001-add-CPU-alarm-for-xv6.patch: -------------------------------------------------------------------------------- 1 | From da93687d8fddcb92c5c4a7628353e5ef9fbfeb88 Mon Sep 17 00:00:00 2001 2 | From: Aaron 3 | Date: Mon, 15 Jun 2020 18:26:01 +0200 4 | Subject: [PATCH] add CPU alarm for xv6 5 | 6 | implemented CPU alarm for each process for xv6 7 | also disabled write syscall's print for better 8 | console output. 9 | 10 | The CPU alarm needs us to insert signal handler's 11 | address into the user stack, and then drop into 12 | trapret to go back the user space, so that: 13 | 1. signal handler got called 14 | 2. afer signal handler returned, immediately pick 15 | where user program being interrupted and continue 16 | 17 | now can save and restore caller-saved registers 18 | around calling of the handler 19 | --- 20 | Makefile | 1 + 21 | alarmtest.c | 18 ++++++++++++++++++ 22 | proc.c | 3 +++ 23 | proc.h | 3 +++ 24 | syscall.c | 10 +++++++--- 25 | syscall.h | 1 + 26 | sysfile.c | 4 ++-- 27 | sysproc.c | 14 ++++++++++++++ 28 | trap.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 29 | user.h | 1 + 30 | usys.S | 1 + 31 | 11 files changed, 97 insertions(+), 5 deletions(-) 32 | create mode 100644 alarmtest.c 33 | 34 | diff --git a/Makefile b/Makefile 35 | index 60cef2d..f026203 100644 36 | --- a/Makefile 37 | +++ b/Makefile 38 | @@ -183,6 +183,7 @@ UPROGS=\ 39 | _zombie\ 40 | _date\ 41 | _sbrktest\ 42 | + _alarmtest\ 43 | 44 | fs.img: mkfs README $(UPROGS) 45 | ./mkfs fs.img README $(UPROGS) 46 | diff --git a/alarmtest.c b/alarmtest.c 47 | new file mode 100644 48 | index 0000000..4968c3f 49 | --- /dev/null 50 | +++ b/alarmtest.c 51 | @@ -0,0 +1,18 @@ 52 | +#include "types.h" 53 | +#include "stat.h" 54 | +#include "user.h" 55 | + 56 | +void periodic(); 57 | + 58 | +int main(int argc, char *argv[]) { 59 | + int i; 60 | + printf(1, "alarmtest starting\n"); 61 | + alarm(10, periodic); 62 | + for (i = 0; i < 25 * 5000000; i++) { 63 | + if ((i % 250000) == 0) 64 | + write(2, ".", 1); 65 | + } 66 | + exit(); 67 | +} 68 | + 69 | +void periodic() { printf(1, "alarm!\n"); } 70 | \ No newline at end of file 71 | diff --git a/proc.c b/proc.c 72 | index 3ac1eb2..54f2d9d 100644 73 | --- a/proc.c 74 | +++ b/proc.c 75 | @@ -99,6 +99,9 @@ found: 76 | p->context = (struct context *)sp; 77 | memset(p->context, 0, sizeof *p->context); 78 | p->context->eip = (uint)forkret; 79 | + p->alarmhandler = 0; 80 | + p->alarmticks = 0; 81 | + p->ticksleft = 0; 82 | 83 | return p; 84 | } 85 | diff --git a/proc.h b/proc.h 86 | index 62a0ed0..654230b 100644 87 | --- a/proc.h 88 | +++ b/proc.h 89 | @@ -49,6 +49,9 @@ struct proc { 90 | struct file *ofile[NOFILE]; // Open files 91 | struct inode *cwd; // Current directory 92 | char name[16]; // Process name (debugging) 93 | + uint alarmticks; // the interval per process alarm action 94 | + uint ticksleft; // ticks left for another action, range: [0, alarmticks] 95 | + void (*alarmhandler)(); // the alarm handler 96 | }; 97 | 98 | // Process memory is laid out contiguously, low addresses first: 99 | diff --git a/syscall.c b/syscall.c 100 | index 453f9ae..f9d43c0 100644 101 | --- a/syscall.c 102 | +++ b/syscall.c 103 | @@ -95,7 +95,9 @@ extern int sys_write(void); 104 | extern int sys_uptime(void); 105 | extern int sys_date(void); 106 | extern int sys_dup2(void); 107 | +extern int sys_alarm(void); 108 | 109 | +// we register syscall interfaces here 110 | static int (*syscalls[])(void) = { 111 | [SYS_fork] sys_fork, [SYS_exit] sys_exit, [SYS_wait] sys_wait, 112 | [SYS_pipe] sys_pipe, [SYS_read] sys_read, [SYS_kill] sys_kill, 113 | @@ -104,10 +106,11 @@ static int (*syscalls[])(void) = { 114 | [SYS_sleep] sys_sleep, [SYS_uptime] sys_uptime, [SYS_open] sys_open, 115 | [SYS_write] sys_write, [SYS_mknod] sys_mknod, [SYS_unlink] sys_unlink, 116 | [SYS_link] sys_link, [SYS_mkdir] sys_mkdir, [SYS_close] sys_close, 117 | - [SYS_date] sys_date, [SYS_dup2] sys_dup2, 118 | + [SYS_date] sys_date, [SYS_dup2] sys_dup2, [SYS_alarm] sys_alarm, 119 | }; 120 | 121 | // use a string table for the name, any better idea? 122 | +// use STAB to get the name from debug information, will do it later 123 | static char *syscallnames[] = { 124 | [SYS_fork] "fork", [SYS_exit] "exit", [SYS_wait] "wait", 125 | [SYS_pipe] "pipe", [SYS_read] "read", [SYS_kill] "kill", 126 | @@ -116,7 +119,7 @@ static char *syscallnames[] = { 127 | [SYS_sleep] "sleep", [SYS_uptime] "uptime", [SYS_open] "open", 128 | [SYS_write] "write", [SYS_mknod] "mknod", [SYS_unlink] "unlink", 129 | [SYS_link] "link", [SYS_mkdir] "mkdir", [SYS_close] "close", 130 | - [SYS_date] "date", [SYS_dup2] "dup2", 131 | + [SYS_date] "date", [SYS_dup2] "dup2", [SYS_alarm] "alarm", 132 | }; 133 | 134 | void syscall(void) { 135 | @@ -139,7 +142,8 @@ void syscall(void) { 136 | // cprintf("first argument: %s\n", argument); 137 | 138 | curproc->tf->eax = syscalls[num](); 139 | - cprintf("%s -> %d\n\n", syscallnames[num], curproc->tf->eax); 140 | + if (num != 16) 141 | + cprintf("%s -> %d\n\n", syscallnames[num], curproc->tf->eax); 142 | } else { 143 | cprintf("%d %s: unknown sys call %d\n", curproc->pid, curproc->name, num); 144 | curproc->tf->eax = -1; 145 | diff --git a/syscall.h b/syscall.h 146 | index edb15c1..f1bcf02 100644 147 | --- a/syscall.h 148 | +++ b/syscall.h 149 | @@ -22,3 +22,4 @@ 150 | #define SYS_close 21 151 | #define SYS_date 22 152 | #define SYS_dup2 23 153 | +#define SYS_alarm 24 154 | \ No newline at end of file 155 | diff --git a/sysfile.c b/sysfile.c 156 | index cabeadb..5db20fc 100644 157 | --- a/sysfile.c 158 | +++ b/sysfile.c 159 | @@ -100,8 +100,8 @@ int sys_write(void) { 160 | 161 | if (argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argptr(1, &p, n) < 0) 162 | return -1; 163 | - cprintf("write to file object %p, file buffer address: %p, size %d\n", f, p, 164 | - n); 165 | + // cprintf("write to file object %p, file buffer address: %p, size %d\n", f, p, 166 | + // n); 167 | return filewrite(f, p, n); 168 | } 169 | 170 | diff --git a/sysproc.c b/sysproc.c 171 | index 1820a7a..20ca9f5 100644 172 | --- a/sysproc.c 173 | +++ b/sysproc.c 174 | @@ -91,3 +91,17 @@ int sys_date(void) { 175 | rtc->minute, rtc->second); 176 | return 0; 177 | } 178 | + 179 | +int sys_alarm(void) { 180 | + int ticks; 181 | + void (*handler)(); 182 | + 183 | + if (argint(0, &ticks) < 0) 184 | + return -1; 185 | + if (argptr(1, (char **)&handler, 1) < 0) 186 | + return -1; 187 | + myproc()->alarmticks = ticks; 188 | + myproc()->alarmhandler = handler; 189 | + myproc()->ticksleft = ticks; 190 | + return 0; 191 | +} 192 | diff --git a/trap.c b/trap.c 193 | index 26af406..06c2a15 100644 194 | --- a/trap.c 195 | +++ b/trap.c 196 | @@ -49,6 +49,52 @@ void trap(struct trapframe *tf) { 197 | release(&tickslock); 198 | } 199 | lapiceoi(); 200 | + if (myproc() != 0 && (tf->cs & 0x3) == 0x3) // come from user space, cs is 3 201 | + { 202 | + if (myproc()->ticksleft > 0) { 203 | + --(myproc()->ticksleft); 204 | + } else { 205 | + // the handler must be run in user space. 206 | + 207 | + // save caller-saved user registers: %eax, %ecx, %edx 208 | + // when handler got called, it might change these registers during the 209 | + // procedure so after the handler, we need to restore those registers 210 | + // from somewhere (the stack) 211 | + // |------------| 212 | + // | old eip | 213 | + // |------------| 214 | + // |retore regs | 215 | + // |------------| <- esp 216 | + 217 | + // when handler returns, it should be returned to 218 | + // the point user program being interrupted 219 | + if (myproc()->alarmhandler != 0) { 220 | + // reset 221 | + myproc()->ticksleft = myproc()->alarmticks; 222 | + 223 | + tf->esp -= 4; 224 | + *((uint *)tf->esp) = tf->eip; // save %eip 225 | + tf->eip = 226 | + (uint)myproc()->alarmhandler; // replace with handler's address 227 | + 228 | + // save caller-saved registers: %eax, %ecx, %edx 229 | + tf->esp -= 12; 230 | + *((uint *)tf->esp + 2) = tf->eax; 231 | + *((uint *)tf->esp + 1) = tf->ecx; 232 | + *((uint *)tf->esp) = tf->edx; 233 | + 234 | + // inject shell code on user stack (restore caller-saved registers) 235 | + // shell code (add esp,8;pop edx;pop ecx;pop eax;ret;), little endian 236 | + tf->esp -= 8; 237 | + *((uint *)tf->esp) = 0x5a08c483; 238 | + *((uint *)tf->esp + 1) = 0xc35859; 239 | + 240 | + // shell code address, will be popped after the handler 241 | + tf->esp -= 4; 242 | + *((uint *)tf->esp) = tf->esp + 4; 243 | + } 244 | + } 245 | + } 246 | break; 247 | case T_IRQ0 + IRQ_IDE: 248 | ideintr(); 249 | diff --git a/user.h b/user.h 250 | index aa406ed..859f982 100644 251 | --- a/user.h 252 | +++ b/user.h 253 | @@ -25,6 +25,7 @@ char *sbrk(int); 254 | int sleep(int); 255 | int uptime(void); 256 | int date(struct rtcdate *); 257 | +int alarm(uint, void (*)()); 258 | 259 | // ulib.c 260 | int stat(const char *, struct stat *); 261 | diff --git a/usys.S b/usys.S 262 | index 07f432d..14f284b 100644 263 | --- a/usys.S 264 | +++ b/usys.S 265 | @@ -32,3 +32,4 @@ SYSCALL(sleep) 266 | SYSCALL(uptime) 267 | SYSCALL(date) 268 | SYSCALL(dup2) 269 | +SYSCALL(alarm) 270 | -- 271 | 2.24.3 (Apple Git-128) 272 | 273 | -------------------------------------------------------------------------------- /homework/homework1-shell.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | // Simplifed xv6 shell. 12 | #define MAXARGS 10 13 | 14 | // All commands have at least a type. Have looked at the type, the code 15 | // typically casts the *cmd to some specific cmd type. 16 | struct cmd { 17 | int type; // ' ' (exec), | (pipe), '<' or '>' for redirection 18 | }; 19 | 20 | struct execcmd { 21 | int type; // ' ' 22 | char *argv[MAXARGS]; // arguments to the command to be exec-ed 23 | }; 24 | 25 | struct redircmd { 26 | int type; // < or > 27 | struct cmd *cmd; // the command to be run (e.g., an execcmd) 28 | char *file; // the input/output file 29 | int flags; // flags for open() indicating read or write 30 | int fd; // the file descriptor number to use for the file 31 | }; 32 | 33 | struct pipecmd { 34 | int type; // | 35 | struct cmd *left; // left side of pipe 36 | struct cmd *right; // right side of pipe 37 | }; 38 | 39 | struct listcmd { 40 | int type; // ; 41 | struct cmd *left; // the first on within each recursion 42 | struct cmd *right; // the next one 43 | }; 44 | 45 | int fork1(void); // Fork but exits on failure. 46 | struct cmd *parsecmd(char*); 47 | 48 | // Execute cmd. Never returns. 49 | void 50 | runcmd(struct cmd *cmd) 51 | { 52 | int p[2], r; 53 | struct execcmd *ecmd; 54 | struct pipecmd *pcmd; 55 | struct redircmd *rcmd; 56 | struct listcmd *lcmd; 57 | 58 | if(cmd == 0) 59 | _exit(0); 60 | 61 | switch(cmd->type){ 62 | default: 63 | fprintf(stderr, "unknown runcmd\n"); 64 | _exit(-1); 65 | 66 | case ' ': 67 | ecmd = (struct execcmd*)cmd; 68 | if(ecmd->argv[0] == 0) 69 | _exit(0); 70 | // fprintf(stderr, "exec not implemented\n"); 71 | // Your code here ... int execv(const char *path, char *const argv[]); 72 | if (execvp(ecmd->argv[0], ecmd->argv) == -1) // use execvp to access $PATH env 73 | { 74 | // never returns, otherwise print errno to indicate 75 | perror("Command not found"); 76 | _exit(1); 77 | } 78 | break; 79 | 80 | case '>': 81 | case '<': 82 | rcmd = (struct redircmd*)cmd; 83 | // fprintf(stderr, "redir not implemented\n"); 84 | // Your code here ... 85 | close(rcmd->fd); 86 | // close default fd and open new one 87 | // e.g. ls > ls.txt, close default kernel object 88 | // for standard output, re-assign fd 1 to new file 89 | // so ls's output would be put into that file 90 | if (open(rcmd->file, rcmd->flags, 0644) == -1) 91 | { 92 | perror("Open file failed"); 93 | _exit(1); 94 | } 95 | runcmd(rcmd->cmd); 96 | break; 97 | 98 | case '|': 99 | pcmd = (struct pipecmd*)cmd; 100 | // fprintf(stderr, "pipe not implemented\n"); 101 | // Your code here ... 102 | if (pipe(p) == -1) 103 | { 104 | perror("Pipe created failed"); 105 | _exit(1); 106 | } 107 | if (fork1() == 0) 108 | { 109 | close(0); 110 | if (dup(p[0]) == -1) // now fd 0 is assigned to pipe's read end 111 | { 112 | perror("duplicate failed"); 113 | _exit(1); 114 | } 115 | close(p[0]); 116 | close(p[1]); 117 | // don't worry child would exit 118 | // because it will be blocked if 119 | // there is no msg from parent 120 | runcmd(pcmd->right); // child reads from pipe's read end 121 | } 122 | else 123 | { 124 | close(1); 125 | if (dup(p[1]) == -1) // now fd 1 is assigned to pipe's write end 126 | { 127 | perror("duplicate failed"); 128 | _exit(1); 129 | } 130 | close(p[0]); 131 | close(p[1]); 132 | runcmd(pcmd->left); // parent writes into pipe's write end 133 | } 134 | 135 | break; 136 | 137 | case ';': 138 | lcmd = (struct listcmd *)cmd; 139 | // by sequence 140 | runcmd(pcmd->left); 141 | runcmd(pcmd->right); 142 | 143 | break; 144 | 145 | } 146 | _exit(0); 147 | } 148 | 149 | int 150 | getcmd(char *buf, int nbuf) 151 | { 152 | if (isatty(fileno(stdin))) 153 | fprintf(stdout, "6.828$ "); 154 | memset(buf, 0, nbuf); 155 | if(fgets(buf, nbuf, stdin) == 0) 156 | return -1; // EOF 157 | return 0; 158 | } 159 | 160 | int 161 | main(void) 162 | { 163 | static char buf[100]; 164 | int fd, r; 165 | 166 | // Read and run input commands. 167 | while(getcmd(buf, sizeof(buf)) >= 0){ 168 | if(buf[0] == 'c' && buf[1] == 'd' && buf[2] == ' '){ 169 | // Clumsy but will have to do for now. 170 | // Chdir has no effect on the parent if run in the child. 171 | buf[strlen(buf)-1] = 0; // chop \n 172 | if(chdir(buf+3) < 0) 173 | fprintf(stderr, "cannot cd %s\n", buf+3); 174 | continue; 175 | } 176 | if(fork1() == 0) 177 | runcmd(parsecmd(buf)); 178 | wait(&r); 179 | } 180 | exit(0); 181 | } 182 | 183 | int 184 | fork1(void) 185 | { 186 | int pid; 187 | 188 | pid = fork(); 189 | if(pid == -1) 190 | perror("fork"); 191 | return pid; 192 | } 193 | 194 | struct cmd* 195 | execcmd(void) 196 | { 197 | struct execcmd *cmd; 198 | 199 | cmd = malloc(sizeof(*cmd)); 200 | memset(cmd, 0, sizeof(*cmd)); 201 | cmd->type = ' '; 202 | return (struct cmd*)cmd; 203 | } 204 | 205 | struct cmd* 206 | redircmd(struct cmd *subcmd, char *file, int type) 207 | { 208 | struct redircmd *cmd; 209 | 210 | cmd = malloc(sizeof(*cmd)); 211 | memset(cmd, 0, sizeof(*cmd)); 212 | cmd->type = type; 213 | cmd->cmd = subcmd; 214 | cmd->file = file; 215 | cmd->flags = (type == '<') ? O_RDONLY : O_WRONLY|O_CREAT|O_TRUNC; 216 | cmd->fd = (type == '<') ? 0 : 1; 217 | return (struct cmd*)cmd; 218 | } 219 | 220 | struct cmd* 221 | pipecmd(struct cmd *left, struct cmd *right) 222 | { 223 | struct pipecmd *cmd; 224 | 225 | cmd = malloc(sizeof(*cmd)); 226 | memset(cmd, 0, sizeof(*cmd)); 227 | cmd->type = '|'; 228 | cmd->left = left; 229 | cmd->right = right; 230 | return (struct cmd*)cmd; 231 | } 232 | 233 | // Parsing 234 | 235 | char whitespace[] = " \t\r\n\v"; 236 | char symbols[] = "<|>"; 237 | 238 | int 239 | gettoken(char **ps, char *es, char **q, char **eq) 240 | { 241 | char *s; 242 | int ret; 243 | 244 | s = *ps; 245 | while(s < es && strchr(whitespace, *s)) 246 | s++; 247 | if(q) 248 | *q = s; 249 | ret = *s; 250 | switch(*s){ 251 | case 0: 252 | break; 253 | case '|': 254 | case '<': 255 | s++; 256 | break; 257 | case '>': 258 | s++; 259 | break; 260 | default: 261 | ret = 'a'; 262 | while(s < es && !strchr(whitespace, *s) && !strchr(symbols, *s)) 263 | s++; 264 | break; 265 | } 266 | if(eq) 267 | *eq = s; 268 | 269 | while(s < es && strchr(whitespace, *s)) 270 | s++; 271 | *ps = s; 272 | return ret; 273 | } 274 | 275 | int 276 | peek(char **ps, char *es, char *toks) 277 | { 278 | char *s; 279 | 280 | s = *ps; 281 | while(s < es && strchr(whitespace, *s)) 282 | s++; 283 | *ps = s; 284 | return *s && strchr(toks, *s); 285 | } 286 | 287 | struct cmd *parseline(char**, char*); 288 | struct cmd *parsepipe(char**, char*); 289 | struct cmd *parseexec(char**, char*); 290 | 291 | // make a copy of the characters in the input buffer, starting from s through es. 292 | // null-terminate the copy to make it a string. 293 | char 294 | *mkcopy(char *s, char *es) 295 | { 296 | int n = es - s; 297 | char *c = malloc(n+1); 298 | assert(c); 299 | strncpy(c, s, n); 300 | c[n] = 0; 301 | return c; 302 | } 303 | 304 | struct cmd* 305 | parsecmd(char *s) 306 | { 307 | char *es; 308 | struct cmd *cmd; 309 | 310 | es = s + strlen(s); 311 | cmd = parseline(&s, es); 312 | peek(&s, es, ""); 313 | if(s != es){ 314 | fprintf(stderr, "leftovers: %s\n", s); 315 | exit(-1); 316 | } 317 | return cmd; 318 | } 319 | 320 | struct cmd* 321 | parseline(char **ps, char *es) 322 | { 323 | struct cmd *cmd; 324 | cmd = parsepipe(ps, es); 325 | return cmd; 326 | } 327 | 328 | struct cmd* 329 | parsepipe(char **ps, char *es) 330 | { 331 | struct cmd *cmd; 332 | 333 | cmd = parseexec(ps, es); 334 | if(peek(ps, es, "|")){ 335 | gettoken(ps, es, 0, 0); 336 | cmd = pipecmd(cmd, parsepipe(ps, es)); 337 | } 338 | return cmd; 339 | } 340 | 341 | struct cmd* 342 | parseredirs(struct cmd *cmd, char **ps, char *es) 343 | { 344 | int tok; 345 | char *q, *eq; 346 | 347 | while(peek(ps, es, "<>")){ 348 | tok = gettoken(ps, es, 0, 0); 349 | if(gettoken(ps, es, &q, &eq) != 'a') { 350 | fprintf(stderr, "missing file for redirection\n"); 351 | exit(-1); 352 | } 353 | switch(tok){ 354 | case '<': 355 | cmd = redircmd(cmd, mkcopy(q, eq), '<'); 356 | break; 357 | case '>': 358 | cmd = redircmd(cmd, mkcopy(q, eq), '>'); 359 | break; 360 | } 361 | } 362 | return cmd; 363 | } 364 | 365 | struct cmd* 366 | parseexec(char **ps, char *es) 367 | { 368 | char *q, *eq; 369 | int tok, argc; 370 | struct execcmd *cmd; 371 | struct cmd *ret; 372 | 373 | ret = execcmd(); 374 | cmd = (struct execcmd*)ret; 375 | 376 | argc = 0; 377 | ret = parseredirs(ret, ps, es); 378 | while(!peek(ps, es, "|")){ 379 | if((tok=gettoken(ps, es, &q, &eq)) == 0) 380 | break; 381 | if(tok != 'a') { 382 | fprintf(stderr, "syntax error\n"); 383 | exit(-1); 384 | } 385 | cmd->argv[argc] = mkcopy(q, eq); 386 | argc++; 387 | if(argc >= MAXARGS) { 388 | fprintf(stderr, "too many args\n"); 389 | exit(-1); 390 | } 391 | ret = parseredirs(ret, ps, es); 392 | } 393 | cmd->argv[argc] = 0; 394 | return ret; 395 | } 396 | -------------------------------------------------------------------------------- /labs/0001-LAB-5-challenge-1-IRQ-driven-disk-access.patch: -------------------------------------------------------------------------------- 1 | From ff3e70643cc806a669e1fb3290e4255c0c2de822 Mon Sep 17 00:00:00 2001 2 | From: Aaron 3 | Date: Fri, 11 Sep 2020 13:25:55 +0000 4 | Subject: [PATCH] LAB 5 challenge 1, IRQ driven disk access 5 | 6 | finished challenge 1 7 | 8 | make[1]: Leaving directory '/usr/src/app/lab' 9 | internal FS tests [fs/test.c]: OK (29.2s) 10 | fs i/o: OK 11 | check_bc: OK 12 | check_super: OK 13 | check_bitmap: OK 14 | alloc_block: OK 15 | file_open: OK 16 | file_get_block: OK 17 | file_flush/file_truncate/file rewrite: OK 18 | testfile: OK (25.7s) 19 | serve_open/file_stat/file_close: OK 20 | file_read: OK 21 | file_write: OK 22 | file_read after file_write: OK 23 | open: OK 24 | large file: OK 25 | spawn via spawnhello: OK (25.0s) 26 | Protection I/O space: OK (25.4s) 27 | (Old jos.out.faultio failure log removed) 28 | PTE_SHARE [testpteshare]: OK (24.9s) 29 | PTE_SHARE [testfdsharing]: OK (25.0s) 30 | start the shell [icode]: Timeout! OK (51.3s) 31 | testshell: OK (29.1s) 32 | (Old jos.out.testshell failure log removed) 33 | primespipe: OK (34.3s) 34 | Score: 150/150 35 | root@c9982579a1dc:/usr/src/app/lab# 36 | --- 37 | fs/ide.c | 31 ++++++++++++++++++------------- 38 | inc/env.h | 7 ++++++- 39 | inc/lib.h | 1 + 40 | inc/syscall.h | 1 + 41 | kern/picirq.c | 10 ++++++++-- 42 | kern/sched.c | 3 ++- 43 | kern/syscall.c | 21 +++++++++++++++++++++ 44 | kern/trap.c | 35 ++++++++++++++++++++++++++++++++++- 45 | lib/fork.c | 6 +++++- 46 | lib/syscall.c | 6 ++++++ 47 | 10 files changed, 102 insertions(+), 19 deletions(-) 48 | 49 | diff --git a/fs/ide.c b/fs/ide.c 50 | index 2d8b4bf..c218d5f 100644 51 | --- a/fs/ide.c 52 | +++ b/fs/ide.c 53 | @@ -59,7 +59,6 @@ ide_set_disk(int d) 54 | diskno = d; 55 | } 56 | 57 | - 58 | int 59 | ide_read(uint32_t secno, void *dst, size_t nsecs) 60 | { 61 | @@ -74,13 +73,11 @@ ide_read(uint32_t secno, void *dst, size_t nsecs) 62 | outb(0x1F4, (secno >> 8) & 0xFF); 63 | outb(0x1F5, (secno >> 16) & 0xFF); 64 | outb(0x1F6, 0xE0 | ((diskno&1)<<4) | ((secno>>24)&0x0F)); 65 | - outb(0x1F7, 0x20); // CMD 0x20 means read sector 66 | 67 | - for (; nsecs > 0; nsecs--, dst += SECTSIZE) { 68 | - if ((r = ide_wait_ready(1)) < 0) 69 | - return r; 70 | - insl(0x1F0, dst, SECTSIZE/4); 71 | - } 72 | + // between issuing disk cmd and set to sleep, there might be a timer IRQ 73 | + // comes in, and fs -> RUNNABLE, then disk IRQ comes, and we handle it 74 | + // then fs goes to sleep, with CPU halted, we missed wake up 75 | + sys_ide_sleep(dst, nsecs, 0); 76 | 77 | return 0; 78 | } 79 | @@ -99,14 +96,22 @@ ide_write(uint32_t secno, const void *src, size_t nsecs) 80 | outb(0x1F4, (secno >> 8) & 0xFF); 81 | outb(0x1F5, (secno >> 16) & 0xFF); 82 | outb(0x1F6, 0xE0 | ((diskno&1)<<4) | ((secno>>24)&0x0F)); 83 | - outb(0x1F7, 0x30); // CMD 0x30 means write sector 84 | 85 | - for (; nsecs > 0; nsecs--, src += SECTSIZE) { 86 | - if ((r = ide_wait_ready(1)) < 0) 87 | - return r; 88 | - outsl(0x1F0, src, SECTSIZE/4); 89 | - } 90 | + sys_ide_sleep((void *)src, nsecs, 1); 91 | 92 | return 0; 93 | } 94 | 95 | +/* 96 | + 97 | +0. non-fs env raises a fs request and sleep 98 | +1. fs might issue read/write command 99 | +2. fs goes to sleep and yields the CPU 100 | +3. disk irq comes, fs wakes up 101 | +4. fs in turn wakes up env that waits 102 | +5. fs serv goes for another run 103 | + 104 | +So basically we can only handle one IRQ each 105 | +time (since fs env will sleep after each disk command), 106 | +we can support concurrency by implementing 'thread' in JOS 107 | +*/ 108 | \ No newline at end of file 109 | diff --git a/inc/env.h b/inc/env.h 110 | index ab392db..c8be428 100644 111 | --- a/inc/env.h 112 | +++ b/inc/env.h 113 | @@ -35,7 +35,8 @@ enum { 114 | ENV_DYING, 115 | ENV_RUNNABLE, 116 | ENV_RUNNING, 117 | - ENV_NOT_RUNNABLE 118 | + ENV_NOT_RUNNABLE, 119 | + ENV_IDE_SLEEPING 120 | }; 121 | 122 | // Special environment types 123 | @@ -66,6 +67,10 @@ struct Env { 124 | uint32_t env_ipc_value; // Data value sent to us 125 | envid_t env_ipc_from; // envid of the sender 126 | int env_ipc_perm; // Perm of page mapping received 127 | + 128 | + // Lab 5 FS 129 | + void *chan; // sleep on channel (0 means write, otherwise read) 130 | + int op; // read 0, write 1 131 | }; 132 | 133 | #endif // !JOS_INC_ENV_H 134 | diff --git a/inc/lib.h b/inc/lib.h 135 | index 88f7102..e3f56b4 100644 136 | --- a/inc/lib.h 137 | +++ b/inc/lib.h 138 | @@ -57,6 +57,7 @@ int sys_page_map(envid_t src_env, void *src_pg, 139 | int sys_page_unmap(envid_t env, void *pg); 140 | int sys_ipc_try_send(envid_t to_env, uint32_t value, void *pg, int perm); 141 | int sys_ipc_recv(void *rcv_pg); 142 | +void sys_ide_sleep(void *chan, size_t nsecs, int op); 143 | 144 | // This must be inlined. Exercise for reader: why? 145 | static inline envid_t __attribute__((always_inline)) 146 | diff --git a/inc/syscall.h b/inc/syscall.h 147 | index 20c6433..cc21b42 100644 148 | --- a/inc/syscall.h 149 | +++ b/inc/syscall.h 150 | @@ -17,6 +17,7 @@ enum { 151 | SYS_yield, 152 | SYS_ipc_try_send, 153 | SYS_ipc_recv, 154 | + SYS_ide_sleep, 155 | NSYSCALLS 156 | }; 157 | 158 | diff --git a/kern/picirq.c b/kern/picirq.c 159 | index 8cb3e62..adc9080 100644 160 | --- a/kern/picirq.c 161 | +++ b/kern/picirq.c 162 | @@ -11,10 +11,16 @@ 163 | uint16_t irq_mask_8259A = 0xFFFF & ~(1< 1 ? 0xc4 : 0x20); // CMD 0x20 means read sector 218 | + } 219 | + else 220 | + { 221 | + outb(0x1F7, nsecs > 1 ? 0xc5 : 0x30); // CMD 0x30 means write sector 222 | + outsl(0x1F0, chan, PGSIZE / 4); 223 | + } 224 | + curenv->chan = chan; 225 | + curenv->env_status = ENV_IDE_SLEEPING; 226 | + curenv->op = op; 227 | + sched_yield(); 228 | +} 229 | + 230 | // Dispatches to the correct kernel function, passing the arguments. 231 | int32_t 232 | syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5) 233 | @@ -425,6 +444,8 @@ syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, 234 | return sys_ipc_try_send(a1, a2, (void *)a3, a4); 235 | case SYS_ipc_recv: 236 | return sys_ipc_recv((void *)a1); 237 | + case SYS_ide_sleep: 238 | + sys_ide_sleep((void *)a1, a2, (int)a3); 239 | case NSYSCALLS: 240 | default: 241 | return -E_INVAL; 242 | diff --git a/kern/trap.c b/kern/trap.c 243 | index ccf82f7..ab83a75 100644 244 | --- a/kern/trap.c 245 | +++ b/kern/trap.c 246 | @@ -238,20 +238,52 @@ trap_dispatch(struct Trapframe *tf) 247 | // LAB 4: Your code here. 248 | if (tf->tf_trapno >= IRQ_OFFSET && tf->tf_trapno < IRQ_OFFSET + 16) 249 | { 250 | - lapic_eoi(); 251 | switch (tf->tf_trapno) 252 | { 253 | case IRQ_OFFSET + IRQ_TIMER: 254 | + lapic_eoi(); 255 | sched_yield(); 256 | break; 257 | 258 | case IRQ_OFFSET + IRQ_KBD: 259 | kbd_intr(); 260 | + lapic_eoi(); 261 | return; 262 | 263 | case IRQ_OFFSET + IRQ_SERIAL: 264 | serial_intr(); 265 | + lapic_eoi(); 266 | return; 267 | + 268 | + case IRQ_OFFSET + IRQ_IDE: 269 | + for (int i = 0; i < NENV; i++) 270 | + { 271 | + if (envs[i].env_type == ENV_TYPE_FS) 272 | + { 273 | + // read a BLKSIZE from disk if needed then acknowledge the interrupt 274 | + if (envs[i].op == 0) 275 | + { 276 | + lcr3(PADDR(envs[i].env_pgdir)); 277 | + insl(0x1F0, envs[i].chan, PGSIZE / 4); 278 | + envs[i].chan = 0; 279 | + lcr3(PADDR(kern_pgdir)); 280 | + } 281 | + // OCW2: send non-specific EOI command to give driver an ACK 282 | + // otherwise we won't receive the rest IDE interrupts followed 283 | + outb(IO_PIC1, 0x20); 284 | + outb(IO_PIC2, 0x20); 285 | + // finally, make fs runnable 286 | + if (envs[i].env_status == ENV_IDE_SLEEPING) 287 | + envs[i].env_status = ENV_RUNNABLE; 288 | + else 289 | + { 290 | + // shouldn't be here 291 | + cprintf("status: %u\n", envs[i].env_status); 292 | + print_trapframe(tf); 293 | + } 294 | + return; 295 | + } 296 | + } 297 | 298 | default: 299 | break; 300 | @@ -427,6 +459,7 @@ page_fault_handler(struct Trapframe *tf) 301 | utf->utf_eflags = tf->tf_eflags; 302 | utf->utf_esp = tf->tf_esp; 303 | 304 | + // page fault exception handler thread 305 | tf->tf_esp = (uintptr_t)&utf->utf_fault_va; 306 | tf->tf_eip = (uintptr_t)curenv->env_pgfault_upcall; 307 | env_run(curenv); 308 | diff --git a/lib/fork.c b/lib/fork.c 309 | index ce606d6..4cdf73c 100644 310 | --- a/lib/fork.c 311 | +++ b/lib/fork.c 312 | @@ -63,6 +63,11 @@ pgfault(struct UTrapframe *utf) 313 | // copy-on-write again if it was already copy-on-write at the beginning of 314 | // this function?) 315 | // 316 | +// Answer of above exercise question: 317 | +// *If the beginning it is CoW, and then it faults, marked as PTE_W, then 318 | +// we'll corrupt the child's copy when this page is being written by parent 319 | +// after this, and the 'snapshot' misfunction* 320 | +// 321 | // Returns: 0 on success, < 0 on error. 322 | // It is also OK to panic on error. 323 | // 324 | @@ -73,7 +78,6 @@ duppage(envid_t envid, unsigned pn) 325 | 326 | // LAB 4: Your code here. 327 | int perm = PTE_P | PTE_U; // at least PTE_P and PTE_U 328 | - // envid_t curenvid = sys_getenvid(); 329 | 330 | int is_wr = (uvpt[pn] & PTE_W) == PTE_W; 331 | int is_cow = (uvpt[pn] & PTE_COW) == PTE_COW; 332 | diff --git a/lib/syscall.c b/lib/syscall.c 333 | index f6d1136..8668705 100644 334 | --- a/lib/syscall.c 335 | +++ b/lib/syscall.c 336 | @@ -158,3 +158,9 @@ sys_ipc_recv(void *dstva) 337 | return syscall(SYS_ipc_recv, 1, (uint32_t)dstva, 0, 0, 0, 0); 338 | } 339 | 340 | +void 341 | +sys_ide_sleep(void *chan, size_t nsecs, int op) 342 | +{ 343 | + // can't use sysenter, since we need to restore our flags from trapframe 344 | + syscall(SYS_ide_sleep, 0, (uint32_t)chan, nsecs, (uint32_t)op, 0, 0); 345 | +} 346 | \ No newline at end of file 347 | -- 348 | 2.20.1 349 | 350 | -------------------------------------------------------------------------------- /homework/0001-support-threading-in-process.patch: -------------------------------------------------------------------------------- 1 | From b5c52ad2bb7b50b43e4d43c1051c0f2cfb495c5c Mon Sep 17 00:00:00 2001 2 | From: Aaron 3 | Date: Mon, 3 Aug 2020 14:43:13 +0000 4 | Subject: [PATCH] support threading in process 5 | 6 | to the kernel, it only cares about task unit, in linux it is process 7 | 8 | user space threads within a process share code segment, 9 | static memory and heap (dynamic memory), 10 | but have separate processor registers sets and stacks. 11 | --- 12 | defs.h | 1 + 13 | proc.c | 72 ++++++++++++++++++++++++++++++++++++++----------------- 14 | syscall.c | 6 ++++- 15 | syscall.h | 4 +++- 16 | sysproc.c | 20 ++++++++++++++++ 17 | trap.c | 2 ++ 18 | user.h | 2 ++ 19 | usys.S | 2 ++ 20 | uthread.c | 65 ++++++++++++++++++++++--------------------------- 21 | vm.c | 1 - 22 | 10 files changed, 113 insertions(+), 62 deletions(-) 23 | 24 | diff --git a/defs.h b/defs.h 25 | index 8627d58..6ca46df 100644 26 | --- a/defs.h 27 | +++ b/defs.h 28 | @@ -123,6 +123,7 @@ void yield(void); 29 | 30 | // swtch.S 31 | void swtch(struct context **, struct context *); 32 | +int clone(uint, uint); 33 | 34 | // spinlock.c 35 | void acquire(struct spinlock *); 36 | diff --git a/proc.c b/proc.c 37 | index bf1fec1..691f884 100644 38 | --- a/proc.c 39 | +++ b/proc.c 40 | @@ -106,6 +106,50 @@ found: 41 | return p; 42 | } 43 | 44 | +// create a new execute thread for a process 45 | +// we clone parent process and tweak the kernel 46 | +// stack for the child, assign the same pid 47 | +int clone(uint eip, uint esp) { 48 | + struct proc *curproc = myproc(); 49 | + struct proc *child = allocproc(); 50 | + if (!child) 51 | + return 0; 52 | + 53 | + // clone the child, share the address space 54 | + child->pgdir = curproc->pgdir; 55 | + child->sz = curproc->sz; 56 | + child->parent = curproc; 57 | + 58 | + // copy necessary segment registers and eflags 59 | + child->tf->cs = curproc->tf->cs; 60 | + child->tf->ds = curproc->tf->ds; 61 | + child->tf->es = curproc->tf->es; 62 | + child->tf->ss = curproc->tf->ss; 63 | + child->tf->fs = curproc->tf->fs; 64 | + child->tf->gs = curproc->tf->gs; 65 | + child->tf->eflags = curproc->tf->eflags; 66 | + 67 | + // inherit file descriptors as well 68 | + int i; 69 | + for (i = 0; i < NOFILE; i++) 70 | + if (curproc->ofile[i]) 71 | + child->ofile[i] = filedup(curproc->ofile[i]); 72 | + child->cwd = idup(curproc->cwd); 73 | + 74 | + // let new process (thread of execution) start running 75 | + // from new %eip and with new stack %esp 76 | + child->tf->eip = eip; 77 | + child->tf->esp = esp; 78 | + 79 | + acquire(&ptable.lock); 80 | + 81 | + child->state = RUNNABLE; 82 | + 83 | + release(&ptable.lock); 84 | + 85 | + return child->pid; 86 | +} 87 | + 88 | // PAGEBREAK: 32 89 | // Set up first user process. 90 | void userinit(void) { 91 | @@ -123,6 +167,10 @@ void userinit(void) { 92 | (int)_binary_initcode_size); // now mapping self's content 93 | p->sz = PGSIZE; 94 | memset(p->tf, 0, sizeof(*p->tf)); 95 | + // we need to set low 2 bits in CS, otherwise 96 | + // user program would instead use kernel stack 97 | + // when trapped into kernel, we will fail to 98 | + // fetch the syscall arguments (%esp & %cs keep same) 99 | p->tf->cs = (SEG_UCODE << 3) | DPL_USER; 100 | p->tf->ds = (SEG_UDATA << 3) | DPL_USER; 101 | p->tf->es = p->tf->ds; 102 | @@ -146,27 +194,6 @@ void userinit(void) { 103 | release(&ptable.lock); 104 | } 105 | 106 | -// Grow current process's memory by n bytes. 107 | -// Return 0 on success, -1 on failure. 108 | -// int 109 | -// growproc(int n) 110 | -// { 111 | -// uint sz; 112 | -// struct proc *curproc = myproc(); 113 | - 114 | -// sz = curproc->sz; 115 | -// if(n > 0){ 116 | -// if((sz = allocuvm(curproc->pgdir, sz, sz + n)) == 0) 117 | -// return -1; 118 | -// } else if(n < 0){ 119 | -// if((sz = deallocuvm(curproc->pgdir, sz, sz + n)) == 0) 120 | -// return -1; 121 | -// } 122 | -// curproc->sz = sz; 123 | -// switchuvm(curproc); 124 | -// return 0; 125 | -// } 126 | - 127 | // Create a new process copying p as the parent. 128 | // Sets up stack to return as if from system call. 129 | // Caller must set state of returned proc to RUNNABLE. 130 | @@ -276,7 +303,8 @@ int wait(void) { 131 | pid = p->pid; 132 | kfree(p->kstack); 133 | p->kstack = 0; 134 | - freevm(p->pgdir); 135 | + if (p->pgdir != p->parent->pgdir) 136 | + freevm(p->pgdir); 137 | p->pid = 0; 138 | p->parent = 0; 139 | p->name[0] = 0; 140 | diff --git a/syscall.c b/syscall.c 141 | index 032695d..3f4edde 100644 142 | --- a/syscall.c 143 | +++ b/syscall.c 144 | @@ -96,6 +96,8 @@ extern int sys_uptime(void); 145 | extern int sys_date(void); 146 | extern int sys_dup2(void); 147 | extern int sys_alarm(void); 148 | +extern int sys_clone(void); 149 | +extern int sys_yield(void); 150 | 151 | // we register syscall interfaces here 152 | static int (*syscalls[])(void) = { 153 | @@ -107,6 +109,7 @@ static int (*syscalls[])(void) = { 154 | [SYS_write] sys_write, [SYS_mknod] sys_mknod, [SYS_unlink] sys_unlink, 155 | [SYS_link] sys_link, [SYS_mkdir] sys_mkdir, [SYS_close] sys_close, 156 | [SYS_date] sys_date, [SYS_dup2] sys_dup2, [SYS_alarm] sys_alarm, 157 | + [SYS_clone] sys_clone, [SYS_sched_yield] sys_yield, 158 | }; 159 | 160 | // use a string table for the name, any better idea? 161 | @@ -120,6 +123,7 @@ static char *syscallnames[] = { 162 | [SYS_write] "write", [SYS_mknod] "mknod", [SYS_unlink] "unlink", 163 | [SYS_link] "link", [SYS_mkdir] "mkdir", [SYS_close] "close", 164 | [SYS_date] "date", [SYS_dup2] "dup2", [SYS_alarm] "alarm", 165 | + [SYS_clone] "clone", [SYS_sched_yield] "yield", 166 | }; 167 | 168 | // breakpoint here, x/37x 169 | @@ -164,7 +168,7 @@ void syscall(void) { 170 | // cprintf("first argument: %s\n", argument); 171 | 172 | curproc->tf->eax = syscalls[num](); 173 | - if (num != 16) 174 | + if (num != SYS_write) 175 | cprintf("%s -> %d\n\n", syscallnames[num], curproc->tf->eax); 176 | } else { 177 | cprintf("%d %s: unknown sys call %d\n", curproc->pid, curproc->name, num); 178 | diff --git a/syscall.h b/syscall.h 179 | index f1bcf02..8cee3ea 100644 180 | --- a/syscall.h 181 | +++ b/syscall.h 182 | @@ -22,4 +22,6 @@ 183 | #define SYS_close 21 184 | #define SYS_date 22 185 | #define SYS_dup2 23 186 | -#define SYS_alarm 24 187 | \ No newline at end of file 188 | +#define SYS_alarm 24 189 | +#define SYS_clone 25 190 | +#define SYS_sched_yield 26 191 | \ No newline at end of file 192 | diff --git a/sysproc.c b/sysproc.c 193 | index 3d35ace..52fb0fb 100644 194 | --- a/sysproc.c 195 | +++ b/sysproc.c 196 | @@ -103,3 +103,23 @@ int sys_alarm(void) { 197 | myproc()->ticksleft = ticks; 198 | return 0; 199 | } 200 | + 201 | +int sys_clone(void) { 202 | + void (*entry)(); 203 | + int esp; 204 | + if (argptr(0, (char **)&entry, 4) < 0) 205 | + return -1; 206 | + if (argint(1, &esp) < 0) 207 | + return -1; 208 | + 209 | + int t; 210 | + if ((t= clone((uint)entry, esp)) == 0) 211 | + return -1; 212 | + 213 | + return 0; 214 | +} 215 | + 216 | +int sys_yield(void) { 217 | + yield(); 218 | + return 0; 219 | +} 220 | \ No newline at end of file 221 | diff --git a/trap.c b/trap.c 222 | index 0df84cc..585e988 100644 223 | --- a/trap.c 224 | +++ b/trap.c 225 | @@ -119,6 +119,8 @@ void trap(struct trapframe *tf) { 226 | lapiceoi(); 227 | break; 228 | case T_PGFLT: { 229 | + if (rcr2() == 0xffffffff) 230 | + exit(); 231 | if (rcr2() >= myproc()->sz) { 232 | // we don't allocate new memory for exceeding process size 233 | // if we implement malloc then I guess we do not need this condition 234 | diff --git a/user.h b/user.h 235 | index 859f982..2300a38 100644 236 | --- a/user.h 237 | +++ b/user.h 238 | @@ -26,6 +26,8 @@ int sleep(int); 239 | int uptime(void); 240 | int date(struct rtcdate *); 241 | int alarm(uint, void (*)()); 242 | +int clone(uint, uint); 243 | +int sched_yield(); 244 | 245 | // ulib.c 246 | int stat(const char *, struct stat *); 247 | diff --git a/usys.S b/usys.S 248 | index 14f284b..da8ccab 100644 249 | --- a/usys.S 250 | +++ b/usys.S 251 | @@ -33,3 +33,5 @@ SYSCALL(uptime) 252 | SYSCALL(date) 253 | SYSCALL(dup2) 254 | SYSCALL(alarm) 255 | +SYSCALL(clone) 256 | +SYSCALL(sched_yield) 257 | \ No newline at end of file 258 | diff --git a/uthread.c b/uthread.c 259 | index 66fc1f8..5fad39a 100644 260 | --- a/uthread.c 261 | +++ b/uthread.c 262 | @@ -35,37 +35,22 @@ thread_init(void) 263 | current_thread->state = RUNNING; 264 | } 265 | 266 | -static void 267 | -thread_schedule(void) 268 | +void 269 | +thread_exit() 270 | { 271 | - thread_p t; 272 | - 273 | - /* Find another runnable thread. */ 274 | - next_thread = 0; 275 | - for (t = all_thread; t < all_thread + MAX_THREAD; t++) { 276 | - if (t->state == RUNNABLE && t != current_thread) { 277 | - next_thread = t; 278 | - break; 279 | - } 280 | - } 281 | - 282 | - if (t >= all_thread + MAX_THREAD && current_thread->state == RUNNABLE) { 283 | - /* The current thread is the only runnable thread; run it. */ 284 | - next_thread = current_thread; 285 | - } 286 | - 287 | - if (next_thread == 0) { 288 | - printf(2, "thread_schedule: no runnable threads\n"); 289 | - exit(); 290 | - } 291 | - 292 | - if (current_thread != next_thread) { /* switch threads? */ 293 | - next_thread->state = RUNNING; 294 | - thread_switch(); 295 | - } else 296 | - next_thread = 0; 297 | + exit(); 298 | } 299 | 300 | +/* 301 | + once we create a thread, maybe we need to add it 302 | + to the group in the kernel, so the kernel could 303 | + assign idle CPUs to it (via system call) 304 | + 305 | + but this group should be per process owned, maybe 306 | + we can add this in struct proc (kernel code), 307 | + could be a linked-list so that we can dynamically 308 | + update it 309 | +*/ 310 | void 311 | thread_create(void (*func)()) 312 | { 313 | @@ -75,17 +60,24 @@ thread_create(void (*func)()) 314 | if (t->state == FREE) break; 315 | } 316 | t->sp = (int) (t->stack + STACK_SIZE); // set sp to the top of the stack 317 | - t->sp -= 4; // space for return address 318 | - * (int *) (t->sp) = (int)func; // push return address on stack 319 | - t->sp -= 32; // space for registers that thread_switch expects 320 | + 321 | + t->sp -= 4; 322 | + *(int *)t->sp = (int)thread_exit; 323 | t->state = RUNNABLE; 324 | + 325 | + // make a system call, pass the func() to the kernel, 326 | + // let idle CPUs run upon it 327 | + if (clone((uint)func, (uint)t->sp) != 0) 328 | + { 329 | + printf(2, "uthread_create syscall failed!\n"); 330 | + exit(); 331 | + } 332 | } 333 | 334 | void 335 | thread_yield(void) 336 | { 337 | - current_thread->state = RUNNABLE; 338 | - thread_schedule(); 339 | + sched_yield(); 340 | } 341 | 342 | static void 343 | @@ -94,12 +86,10 @@ mythread(void) 344 | int i; 345 | printf(1, "my thread running\n"); 346 | for (i = 0; i < 100; i++) { 347 | - printf(1, "my thread 0x%x\n", (int) current_thread); 348 | + printf(1, "my thread 0x%x\n", getpid()); 349 | thread_yield(); 350 | } 351 | printf(1, "my thread: exit\n"); 352 | - current_thread->state = FREE; 353 | - thread_schedule(); 354 | } 355 | 356 | 357 | @@ -109,6 +99,7 @@ main(int argc, char *argv[]) 358 | thread_init(); 359 | thread_create(mythread); 360 | thread_create(mythread); 361 | - thread_schedule(); 362 | + while (wait() != -1); 363 | + printf(1, "all threads reaped.\n"); 364 | return 0; 365 | } 366 | diff --git a/vm.c b/vm.c 367 | index f2c46d7..cb9bbc1 100644 368 | --- a/vm.c 369 | +++ b/vm.c 370 | @@ -126,7 +126,6 @@ pde_t *setupkvm(void) { 371 | int idx = PDX(k->virt) + i; 372 | if (idx < NPDENTRIES) 373 | pgdir[idx] = kpgdir[idx]; 374 | - // cprintf("index is %d\n", PDX(k->virt) + i); 375 | } 376 | } 377 | 378 | -- 379 | 2.20.1 380 | 381 | -------------------------------------------------------------------------------- /labs/0001-trying-with-fine-grained-locks.patch: -------------------------------------------------------------------------------- 1 | From 2f50f4b77cd140d636e3274b482faf732a360fa9 Mon Sep 17 00:00:00 2001 2 | From: Aaron 3 | Date: Mon, 20 Jul 2020 14:09:37 +0000 4 | Subject: [PATCH] trying with fine-grained locks... 5 | 6 | 1. page free list lock 7 | 2. scheduler lock (similar to env list lock) 8 | 9 | added some comments 10 | --- 11 | kern/env.c | 13 +++++++++---- 12 | kern/init.c | 14 ++++---------- 13 | kern/lapic.c | 8 ++++++-- 14 | kern/pmap.c | 6 ++++++ 15 | kern/sched.c | 10 +++++++--- 16 | kern/spinlock.c | 21 +++++++++++++++++++++ 17 | kern/spinlock.h | 42 ++++++++++++++++++++++++++++++++++++++++++ 18 | kern/syscall.c | 3 +++ 19 | kern/trap.c | 11 +++++------ 20 | lib/ipc.c | 3 ++- 21 | 10 files changed, 105 insertions(+), 26 deletions(-) 22 | 23 | diff --git a/kern/env.c b/kern/env.c 24 | index e1019da..5668d1b 100644 25 | --- a/kern/env.c 26 | +++ b/kern/env.c 27 | @@ -90,6 +90,7 @@ envid2env(envid_t envid, struct Env **env_store, bool checkperm) 28 | // that used the same slot in the envs[] array). 29 | e = &envs[ENVX(envid)]; 30 | if (e->env_status == ENV_FREE || e->env_id != envid) { 31 | + cprintf("e->env_id 0x%x, envid: 0x%x\n", e->env_id, envid); 32 | *env_store = 0; 33 | return -E_BAD_ENV; 34 | } 35 | @@ -236,7 +237,7 @@ env_alloc(struct Env **newenv_store, envid_t parent_id) 36 | // Set the basic status variables. 37 | e->env_parent_id = parent_id; 38 | e->env_type = ENV_TYPE_USER; 39 | - e->env_status = ENV_RUNNABLE; 40 | + e->env_status = ENV_NOT_RUNNABLE; 41 | e->env_runs = 0; 42 | 43 | // Clear out all the saved register state, 44 | @@ -417,6 +418,7 @@ env_create(uint8_t *binary, enum EnvType type) 45 | panic("env_alloc"); 46 | load_icode(new_env, binary); 47 | new_env->env_type = type; 48 | + new_env->env_status = ENV_RUNNABLE; 49 | } 50 | 51 | // 52 | @@ -510,7 +512,7 @@ env_pop_tf(struct Trapframe *tf) 53 | curenv->env_cpunum = cpunum(); 54 | 55 | asm volatile( 56 | - "\tmovl %0,%%esp\n" 57 | + "\tmovl %0,%%esp\n" /* switch kernel stack if necessary */ 58 | "\tpopal\n" 59 | "\tpopl %%es\n" 60 | "\tpopl %%ds\n" 61 | @@ -547,7 +549,7 @@ env_run(struct Env *e) 62 | // e->env_tf to sensible values. 63 | 64 | // LAB 3: Your code here. 65 | - if (curenv != NULL && curenv != e) 66 | + if (curenv && curenv != e) 67 | { 68 | if (curenv->env_status == ENV_RUNNING) 69 | curenv->env_status = ENV_RUNNABLE; 70 | @@ -557,7 +559,10 @@ env_run(struct Env *e) 71 | curenv->env_runs ++; 72 | 73 | lcr3(PADDR(curenv->env_pgdir)); 74 | - unlock_kernel(); 75 | + if (scheduler_lock.locked && scheduler_lock.cpu == thiscpu) 76 | + unlock_scheduler(); 77 | + if (kernel_lock.locked && kernel_lock.cpu == thiscpu) 78 | + unlock_kernel(); 79 | env_pop_tf(&(curenv->env_tf)); 80 | } 81 | 82 | diff --git a/kern/init.c b/kern/init.c 83 | index 0a0faaa..508d31c 100644 84 | --- a/kern/init.c 85 | +++ b/kern/init.c 86 | @@ -13,7 +13,6 @@ 87 | #include 88 | #include 89 | #include 90 | -#include 91 | 92 | static void boot_aps(void); 93 | 94 | @@ -41,13 +40,6 @@ i386_init(void) 95 | // Lab 4 multitasking initialization functions 96 | pic_init(); 97 | 98 | - // Acquire the big kernel lock before waking up APs 99 | - // Your code here: 100 | - lock_kernel(); 101 | - 102 | - // Starting non-boot CPUs 103 | - boot_aps(); 104 | - 105 | #if defined(TEST) 106 | // Don't touch -- used by grading script! 107 | ENV_CREATE(TEST, ENV_TYPE_USER); 108 | @@ -62,7 +54,8 @@ i386_init(void) 109 | ENV_CREATE(user_yield, ENV_TYPE_USER); 110 | ENV_CREATE(user_yield, ENV_TYPE_USER); 111 | #endif // TEST* 112 | - 113 | + // Starting non-boot CPUs 114 | + boot_aps(); 115 | // Schedule and run the first user environment! 116 | sched_yield(); 117 | } 118 | @@ -105,6 +98,8 @@ mp_main(void) 119 | { 120 | // We are in high EIP now, safe to switch to kern_pgdir 121 | lcr3(PADDR(kern_pgdir)); 122 | + // now the memory-mapped device content has been 123 | + // updated with current processor's local apic 124 | cprintf("SMP: CPU %d starting\n", cpunum()); 125 | 126 | lapic_init(); 127 | @@ -117,7 +112,6 @@ mp_main(void) 128 | // only one CPU can enter the scheduler at a time! 129 | // 130 | // Your code here: 131 | - lock_kernel(); 132 | sched_yield(); 133 | 134 | // Remove this after you finish Exercise 6 135 | diff --git a/kern/lapic.c b/kern/lapic.c 136 | index dc05777..d60aa1e 100644 137 | --- a/kern/lapic.c 138 | +++ b/kern/lapic.c 139 | @@ -21,7 +21,7 @@ 140 | #define ICRLO (0x0300/4) // Interrupt Command 141 | #define INIT 0x00000500 // INIT/RESET 142 | #define STARTUP 0x00000600 // Startup IPI 143 | - #define DELIVS 0x00001000 // Delivery status 144 | + #define DELIVS 0x00001000 // Delivery status (read-only, 0: Idle, 1: Send Pending) 145 | #define ASSERT 0x00004000 // Assert interrupt (vs deassert) 146 | #define DEASSERT 0x00000000 147 | #define LEVEL 0x00008000 // Level triggered 148 | @@ -39,7 +39,7 @@ 149 | #define ERROR (0x0370/4) // Local Vector Table 3 (ERROR) 150 | #define MASKED 0x00010000 // Interrupt masked 151 | #define TICR (0x0380/4) // Timer Initial Count 152 | -#define TCCR (0x0390/4) // Timer Current Count 153 | +#define TCCR (0x0390/4) // Timer Current Count (read only) 154 | #define TDCR (0x03E0/4) // Timer Divide Configuration 155 | 156 | physaddr_t lapicaddr; // Initialized in mpconfig.c 157 | @@ -63,6 +63,8 @@ lapic_init(void) 158 | lapic = mmio_map_region(lapicaddr, 4096); 159 | 160 | // Enable local APIC; set spurious interrupt vector. 161 | + // The spurious-interrupt vector register is initialized to 000000FFH. 162 | + // By setting bit 8 to 0, software disables the local APIC. 163 | lapicw(SVR, ENABLE | (IRQ_OFFSET + IRQ_SPURIOUS)); 164 | 165 | // The timer repeatedly counts down at bus frequency 166 | @@ -160,12 +162,14 @@ lapic_startap(uint8_t apicid, uint32_t addr) 167 | microdelay(200); 168 | lapicw(ICRLO, INIT | LEVEL); 169 | microdelay(100); // should be 10ms, but too slow in Bochs! 170 | + // wait-for-SIPI state 171 | 172 | // Send startup IPI (twice!) to enter code. 173 | // Regular hardware is supposed to only accept a STARTUP 174 | // when it is in the halted state due to an INIT. So the second 175 | // should be ignored, but it is part of the official Intel algorithm. 176 | // Bochs complains about the second one. Too bad for Bochs. 177 | + // INIT-SIPI-SIPI sequence 178 | for (i = 0; i < 2; i++) { 179 | lapicw(ICRHI, apicid << 24); 180 | lapicw(ICRLO, STARTUP | (addr >> 12)); 181 | diff --git a/kern/pmap.c b/kern/pmap.c 182 | index c2e22a7..a598f78 100644 183 | --- a/kern/pmap.c 184 | +++ b/kern/pmap.c 185 | @@ -10,6 +10,7 @@ 186 | #include 187 | #include 188 | #include 189 | +#include 190 | 191 | // These variables are set by i386_detect_memory() 192 | size_t npages; // Amount of physical memory (in pages) 193 | @@ -387,14 +388,17 @@ struct PageInfo * 194 | page_alloc(int alloc_flags) 195 | { 196 | // Fill this function in 197 | + lock_pagelist(); 198 | if (page_free_list == NULL) 199 | { 200 | + unlock_pagelist(); 201 | return NULL; 202 | } 203 | 204 | struct PageInfo *alloc_page; 205 | alloc_page = page_free_list; 206 | page_free_list = page_free_list->pp_link; 207 | + unlock_pagelist(); 208 | alloc_page->pp_link = NULL; 209 | 210 | if (alloc_flags & ALLOC_ZERO) 211 | @@ -423,8 +427,10 @@ page_free(struct PageInfo *pp) 212 | { 213 | panic("Page is still in use! Cannot be freed"); 214 | } 215 | + lock_pagelist(); 216 | pp->pp_link = page_free_list; 217 | page_free_list = pp; 218 | + unlock_pagelist(); 219 | } 220 | 221 | // 222 | diff --git a/kern/sched.c b/kern/sched.c 223 | index 9e464d7..cbcafec 100644 224 | --- a/kern/sched.c 225 | +++ b/kern/sched.c 226 | @@ -30,9 +30,10 @@ sched_yield(void) 227 | // below to halt the cpu. 228 | 229 | // LAB 4: Your code here. 230 | - int i = 0, curpos = 0, k = 0; 231 | + int i = 1, curpos = -1, k = 0; 232 | if (curenv) 233 | curpos = ENVX(curenv->env_id); 234 | + lock_scheduler(); 235 | for (; i < NENV; i++) 236 | { 237 | k = (i + curpos) % NENV; // in a circular way 238 | @@ -41,7 +42,7 @@ sched_yield(void) 239 | env_run(&envs[k]); 240 | } 241 | } 242 | - if (curenv != NULL && curenv->env_status == ENV_RUNNING) 243 | + if (curenv && curenv->env_status == ENV_RUNNING && curenv->env_cpunum == cpunum()) 244 | { 245 | env_run(curenv); 246 | } 247 | @@ -82,7 +83,10 @@ sched_halt(void) 248 | xchg(&thiscpu->cpu_status, CPU_HALTED); 249 | 250 | // Release the big kernel lock as if we were "leaving" the kernel 251 | - unlock_kernel(); 252 | + if (kernel_lock.locked && kernel_lock.cpu == thiscpu) 253 | + unlock_kernel(); 254 | + if (scheduler_lock.locked && scheduler_lock.cpu == thiscpu) 255 | + unlock_scheduler(); 256 | 257 | // Reset stack pointer, enable interrupts and then halt. 258 | asm volatile ( 259 | diff --git a/kern/spinlock.c b/kern/spinlock.c 260 | index bf4d2d2..9a6eea0 100644 261 | --- a/kern/spinlock.c 262 | +++ b/kern/spinlock.c 263 | @@ -16,6 +16,27 @@ struct spinlock kernel_lock = { 264 | #endif 265 | }; 266 | 267 | +// the page free list lock 268 | +struct spinlock pagelist_lock = { 269 | +#ifdef DEBUG_SPINLOCK 270 | + .name = "pagelist_lock" 271 | +#endif 272 | +}; 273 | + 274 | +// scheduler lock 275 | +struct spinlock scheduler_lock = { 276 | +#ifdef DEBUG_SPINLOCK 277 | + .name = "scheduler_lock" 278 | +#endif 279 | +}; 280 | + 281 | +// inter-process communication lock 282 | +struct spinlock ipc_lock = { 283 | +#ifdef DEBUG_SPINLOCK 284 | + .name = "ipc_lock" 285 | +#endif 286 | +}; 287 | + 288 | #ifdef DEBUG_SPINLOCK 289 | // Record the current call stack in pcs[] by following the %ebp chain. 290 | static void 291 | diff --git a/kern/spinlock.h b/kern/spinlock.h 292 | index 52d20b4..7aa3a3c 100644 293 | --- a/kern/spinlock.h 294 | +++ b/kern/spinlock.h 295 | @@ -26,6 +26,48 @@ void spin_unlock(struct spinlock *lk); 296 | #define spin_initlock(lock) __spin_initlock(lock, #lock) 297 | 298 | extern struct spinlock kernel_lock; 299 | +extern struct spinlock pagelist_lock; 300 | +extern struct spinlock scheduler_lock; 301 | +extern struct spinlock ipc_lock; 302 | + 303 | +static inline void 304 | +lock_pagelist(void) 305 | +{ 306 | + spin_lock(&pagelist_lock); 307 | +} 308 | + 309 | +static inline void 310 | +unlock_pagelist(void) 311 | +{ 312 | + spin_unlock(&pagelist_lock); 313 | + asm volatile("pause"); 314 | +} 315 | + 316 | +static inline void 317 | +lock_scheduler(void) 318 | +{ 319 | + spin_lock(&scheduler_lock); 320 | +} 321 | + 322 | +static inline void 323 | +unlock_scheduler(void) 324 | +{ 325 | + spin_unlock(&scheduler_lock); 326 | + asm volatile("pause"); 327 | +} 328 | + 329 | +static inline void 330 | +lock_ipc(void) 331 | +{ 332 | + spin_lock(&ipc_lock); 333 | +} 334 | + 335 | +static inline void 336 | +unlock_ipc(void) 337 | +{ 338 | + spin_unlock(&ipc_lock); 339 | + asm volatile("pause"); 340 | +} 341 | 342 | static inline void 343 | lock_kernel(void) 344 | diff --git a/kern/syscall.c b/kern/syscall.c 345 | index f19a51f..230ecf2 100644 346 | --- a/kern/syscall.c 347 | +++ b/kern/syscall.c 348 | @@ -11,6 +11,7 @@ 349 | #include 350 | #include 351 | #include 352 | +#include 353 | 354 | // Print a string to the system console. 355 | // The string is exactly 'len' characters long. 356 | @@ -68,6 +69,7 @@ sys_env_destroy(envid_t envid) 357 | static void 358 | sys_yield(void) 359 | { 360 | + unlock_kernel(); 361 | sched_yield(); 362 | } 363 | 364 | @@ -352,6 +354,7 @@ sys_ipc_recv(void *dstva) 365 | 366 | // now we give up CPU 367 | curenv->env_status = ENV_NOT_RUNNABLE; 368 | + unlock_kernel(); 369 | sched_yield(); 370 | 371 | return 0; 372 | diff --git a/kern/trap.c b/kern/trap.c 373 | index 59027a8..d826510 100644 374 | --- a/kern/trap.c 375 | +++ b/kern/trap.c 376 | @@ -275,19 +275,18 @@ trap(struct Trapframe *tf) 377 | 378 | // Re-acqurie the big kernel lock if we were halted in 379 | // sched_yield() 380 | - if (xchg(&thiscpu->cpu_status, CPU_STARTED) == CPU_HALTED) 381 | - lock_kernel(); 382 | + xchg(&thiscpu->cpu_status, CPU_STARTED); 383 | + // lock_scheduler(); 384 | // Check that interrupts are disabled. If this assertion 385 | // fails, DO NOT be tempted to fix it by inserting a "cli" in 386 | // the interrupt path. 387 | assert(!(read_eflags() & FL_IF)); 388 | + // Acquire the big kernel lock before doing any 389 | + // serious kernel work. only apply in trap 390 | + lock_kernel(); 391 | 392 | if ((tf->tf_cs & 3) == 3) { 393 | // Trapped from user mode. 394 | - // Acquire the big kernel lock before doing any 395 | - // serious kernel work. 396 | - // LAB 4: Your code here. 397 | - lock_kernel(); 398 | 399 | // Question 2: Why do we still need separate kernel stacks for each CPU? 400 | // Answer: 401 | diff --git a/lib/ipc.c b/lib/ipc.c 402 | index e05a1e3..c1b6f32 100644 403 | --- a/lib/ipc.c 404 | +++ b/lib/ipc.c 405 | @@ -60,7 +60,8 @@ ipc_send(envid_t to_env, uint32_t val, void *pg, int perm) 406 | while ((r = sys_ipc_try_send(to_env, val, pg, perm)) != 0) 407 | { 408 | if (r != -E_IPC_NOT_RECV) 409 | - panic("sys_ipc_try_send, %e", r); 410 | + panic("sys_ipc_try_send %u on cpu %d, %e", val, thisenv->env_cpunum, r); 411 | + // yield the CPU, current status: ENV_RUNNING 412 | sys_yield(); 413 | } 414 | } 415 | -- 416 | 2.20.1 417 | 418 | -------------------------------------------------------------------------------- /labs/0001-ready-to-submit-my-lab2.patch: -------------------------------------------------------------------------------- 1 | From f62e1bebe9f05810ae7f4689901b19845e75340a Mon Sep 17 00:00:00 2001 2 | From: Aaron 3 | Date: Thu, 21 May 2020 17:42:23 +0200 4 | Subject: [PATCH] ready to submit my lab2 5 | 6 | --- 7 | inc/memlayout.h | 4 ++ 8 | inc/mmu.h | 4 +- 9 | kern/monitor.c | 38 ++++++++++- 10 | kern/monitor.h | 1 + 11 | kern/pmap.c | 176 ++++++++++++++++++++++++++++++++++++++++++++++-- 12 | 5 files changed, 214 insertions(+), 9 deletions(-) 13 | 14 | diff --git a/inc/memlayout.h b/inc/memlayout.h 15 | index a537b15..6baeb18 100644 16 | --- a/inc/memlayout.h 17 | +++ b/inc/memlayout.h 18 | @@ -157,6 +157,9 @@ typedef uint32_t pde_t; 19 | * A second consequence is that the contents of the current page directory 20 | * will always be available at virtual address (UVPT + (UVPT >> PGSHIFT)), to 21 | * which uvpd is set in lib/entry.S. 22 | + * 23 | + * One of the PDEs points to the PD itself, other (2^10 - 1) PDEs point to (2^10 - 1) pages 24 | + * 25 | */ 26 | extern volatile pte_t uvpt[]; // VA of "virtual page table" 27 | extern volatile pde_t uvpd[]; // VA of current page directory 28 | @@ -186,3 +189,4 @@ struct PageInfo { 29 | 30 | #endif /* !__ASSEMBLER__ */ 31 | #endif /* !JOS_INC_MEMLAYOUT_H */ 32 | + 33 | diff --git a/inc/mmu.h b/inc/mmu.h 34 | index 093c8a6..368e79c 100644 35 | --- a/inc/mmu.h 36 | +++ b/inc/mmu.h 37 | @@ -55,7 +55,7 @@ 38 | #define PDXSHIFT 22 // offset of PDX in a linear address 39 | 40 | // Page table/directory entry flags. 41 | -#define PTE_P 0x001 // Present 42 | +#define PTE_P 0x001 // Present Indicates whether the page or page table being pointed to by the entry is currently loaded in physical memory. 43 | #define PTE_W 0x002 // Writeable 44 | #define PTE_U 0x004 // User 45 | #define PTE_PWT 0x008 // Write-Through 46 | @@ -73,7 +73,7 @@ 47 | #define PTE_SYSCALL (PTE_AVAIL | PTE_P | PTE_W | PTE_U) 48 | 49 | // Address in page table or page directory entry 50 | -#define PTE_ADDR(pte) ((physaddr_t) (pte) & ~0xFFF) 51 | +#define PTE_ADDR(pte) ((physaddr_t) (pte) & ~0xFFF) // purge tags 52 | 53 | // Control Register flags 54 | #define CR0_PE 0x00000001 // Protection Enable 55 | diff --git a/kern/monitor.c b/kern/monitor.c 56 | index db8dffa..d16c514 100644 57 | --- a/kern/monitor.c 58 | +++ b/kern/monitor.c 59 | @@ -10,6 +10,7 @@ 60 | #include 61 | #include 62 | #include 63 | +#include 64 | 65 | #define CMDBUF_SIZE 80 // enough for one VGA text line 66 | 67 | @@ -24,7 +25,8 @@ struct Command { 68 | static struct Command commands[] = { 69 | { "help", "Display this list of commands", mon_help }, 70 | { "kerninfo", "Display information about the kernel", mon_kerninfo }, 71 | - { "backtrace", "Display backtrace to current function call", mon_backtrace} 72 | + { "backtrace", "Display backtrace to current function call", mon_backtrace}, 73 | + { "showmappings", "Display memory mappings in current active address space", mon_showmappings} 74 | }; 75 | 76 | /***** Implementations of basic kernel monitor commands *****/ 77 | @@ -88,7 +90,41 @@ mon_backtrace(int argc, char **argv, struct Trapframe *tf) 78 | return 0; 79 | } 80 | 81 | +int mon_showmappings(int argc, char **argv, struct Trapframe *tf) 82 | +{ 83 | + if (argc != 3) 84 | + { 85 | + cprintf("usage: showmappings , addresses are virtual\n"); 86 | + return 1; 87 | + } 88 | + 89 | + uintptr_t low_addr = (uintptr_t)strtol(argv[1], NULL, 16); 90 | + uintptr_t high_addr = (uintptr_t)strtol(argv[2], NULL, 16); 91 | + if (low_addr > high_addr) 92 | + return 1; 93 | 94 | + extern pde_t entry_pgdir[]; 95 | + cprintf("Show mappings between 0x%08x and 0x%08x\n", low_addr, high_addr); 96 | + 97 | + size_t range = (high_addr - low_addr) / PGSIZE; 98 | + 99 | + for (int i = 0; i <= range; i++) 100 | + { 101 | + pte_t *pgtbl_entry = NULL; 102 | + const void *vir_addr = (const void *)(low_addr + i * PGSIZE); 103 | + if (!(pgtbl_entry = pgdir_walk(entry_pgdir, vir_addr, 0))) 104 | + { 105 | + if (!(pgtbl_entry = pgdir_walk(kern_pgdir, vir_addr, 0))) 106 | + { 107 | + cprintf("Invalid mappings, perhaps accessing USER level, not supported yet\n"); 108 | + return 1; 109 | + } 110 | + } 111 | + cprintf("\tVirtual address 0x%08x mapped to physical address 0x%08x\n", vir_addr, PTE_ADDR(*pgtbl_entry) + PGOFF(vir_addr)); 112 | + } 113 | + 114 | + return 0; 115 | +} 116 | 117 | /***** Kernel monitor command interpreter *****/ 118 | 119 | diff --git a/kern/monitor.h b/kern/monitor.h 120 | index 0aa0f26..35d2793 100644 121 | --- a/kern/monitor.h 122 | +++ b/kern/monitor.h 123 | @@ -15,5 +15,6 @@ void monitor(struct Trapframe *tf); 124 | int mon_help(int argc, char **argv, struct Trapframe *tf); 125 | int mon_kerninfo(int argc, char **argv, struct Trapframe *tf); 126 | int mon_backtrace(int argc, char **argv, struct Trapframe *tf); 127 | +int mon_showmappings(int argc, char **argv, struct Trapframe *tf); 128 | 129 | #endif // !JOS_KERN_MONITOR_H 130 | diff --git a/kern/pmap.c b/kern/pmap.c 131 | index 8c809f1..49dbd5d 100644 132 | --- a/kern/pmap.c 133 | +++ b/kern/pmap.c 134 | @@ -95,6 +95,8 @@ boot_alloc(uint32_t n) 135 | if (!nextfree) { 136 | extern char end[]; 137 | nextfree = ROUNDUP((char *) end, PGSIZE); 138 | + cprintf(".bss end is %p\n", (char *)end); 139 | + cprintf("nextfree initialized as %p\n", nextfree); 140 | } 141 | 142 | // Allocate a chunk large enough to hold 'n' bytes, then update 143 | @@ -102,8 +104,23 @@ boot_alloc(uint32_t n) 144 | // to a multiple of PGSIZE. 145 | // 146 | // LAB 2: Your code here. 147 | + if (n == 0) 148 | + { 149 | + result = nextfree; 150 | + return result; 151 | + } 152 | 153 | - return NULL; 154 | + if (n / PGSIZE > npages - 1) 155 | + { 156 | + panic("boot_alloc: There is no enough memory for allocation\n"); 157 | + } 158 | + 159 | + result = nextfree; 160 | + nextfree += ROUNDUP(n, PGSIZE); 161 | + cprintf("nextfree now is %p\n", nextfree); 162 | + 163 | + 164 | + return result; 165 | } 166 | 167 | // Set up a two-level page table: 168 | @@ -125,7 +142,7 @@ mem_init(void) 169 | i386_detect_memory(); 170 | 171 | // Remove this line when you're ready to test this function. 172 | - panic("mem_init: This function is not finished\n"); 173 | + // panic("mem_init: This function is not finished\n"); 174 | 175 | ////////////////////////////////////////////////////////////////////// 176 | // create initial page directory. 177 | @@ -148,6 +165,9 @@ mem_init(void) 178 | // array. 'npages' is the number of physical pages in memory. Use memset 179 | // to initialize all fields of each struct PageInfo to 0. 180 | // Your code goes here: 181 | + pages = (struct PageInfo *) boot_alloc(npages * sizeof(struct PageInfo)); 182 | + memset(pages, 0, npages * sizeof(struct PageInfo)); 183 | + cprintf("pages are located starting from 0x%08x\n", PADDR(pages)); 184 | 185 | 186 | ////////////////////////////////////////////////////////////////////// 187 | @@ -172,6 +192,7 @@ mem_init(void) 188 | // (ie. perm = PTE_U | PTE_P) 189 | // - pages itself -- kernel RW, user NONE 190 | // Your code goes here: 191 | + boot_map_region(kern_pgdir, UPAGES, ROUNDUP(npages * sizeof(struct PageInfo), PGSIZE), PADDR(pages), PTE_U | PTE_W); 192 | 193 | ////////////////////////////////////////////////////////////////////// 194 | // Use the physical memory that 'bootstack' refers to as the kernel 195 | @@ -184,6 +205,8 @@ mem_init(void) 196 | // overwrite memory. Known as a "guard page". 197 | // Permissions: kernel RW, user NONE 198 | // Your code goes here: 199 | + cprintf("Kernel stack starts from: %08x\n", bootstacktop); 200 | + boot_map_region(kern_pgdir, (KSTACKTOP - KSTKSIZE), KSTKSIZE, PADDR((void *)bootstacktop - KSTKSIZE), PTE_W); 201 | 202 | ////////////////////////////////////////////////////////////////////// 203 | // Map all of physical memory at KERNBASE. 204 | @@ -193,6 +216,10 @@ mem_init(void) 205 | // we just set up the mapping anyway. 206 | // Permissions: kernel RW, user NONE 207 | // Your code goes here: 208 | + { 209 | + uint64_t limit = (uint64_t)1 << 32; 210 | + boot_map_region(kern_pgdir, KERNBASE, (limit - KERNBASE), 0, PTE_W); 211 | + } 212 | 213 | // Check that the initial page directory has been set up correctly. 214 | check_kern_pgdir(); 215 | @@ -251,8 +278,28 @@ page_init(void) 216 | // Change the code to reflect this. 217 | // NB: DO NOT actually touch the physical memory corresponding to 218 | // free pages! 219 | + pages[0].pp_ref = 1; 220 | + pages[0].pp_link = NULL; 221 | size_t i; 222 | - for (i = 0; i < npages; i++) { 223 | + for (i = 1; i < npages_basemem; i++) 224 | + { 225 | + pages[i].pp_ref = 0; 226 | + pages[i].pp_link = page_free_list; 227 | + page_free_list = &pages[i]; 228 | + } 229 | + 230 | + uint32_t pgdirend = PADDR(boot_alloc(0)) / PGSIZE; // boot_alloc returns VIRTUAL address! 231 | + 232 | + for (i = npages_basemem; i < pgdirend; i++) 233 | + { 234 | + // reserved for IO hole and kernel's segments(code, data, rodata...) 235 | + // page directory and PageInfo array's end 236 | + pages[i].pp_ref = 1; 237 | + pages[i].pp_link = NULL; 238 | + } 239 | + 240 | + for (i = pgdirend; i < npages; i ++) 241 | + { 242 | pages[i].pp_ref = 0; 243 | pages[i].pp_link = page_free_list; 244 | page_free_list = &pages[i]; 245 | @@ -275,7 +322,22 @@ struct PageInfo * 246 | page_alloc(int alloc_flags) 247 | { 248 | // Fill this function in 249 | - return 0; 250 | + if (page_free_list == NULL) 251 | + { 252 | + return NULL; 253 | + } 254 | + 255 | + struct PageInfo *alloc_page; 256 | + alloc_page = page_free_list; 257 | + page_free_list = page_free_list->pp_link; 258 | + alloc_page->pp_link = NULL; 259 | + 260 | + if (alloc_flags & ALLOC_ZERO) 261 | + { 262 | + memset(page2kva(alloc_page), 0, PGSIZE); 263 | + } 264 | + 265 | + return alloc_page; 266 | } 267 | 268 | // 269 | @@ -288,6 +350,16 @@ page_free(struct PageInfo *pp) 270 | // Fill this function in 271 | // Hint: You may want to panic if pp->pp_ref is nonzero or 272 | // pp->pp_link is not NULL. 273 | + if (pp->pp_link) 274 | + { 275 | + panic("double free or corruption"); 276 | + } 277 | + if (pp->pp_ref) 278 | + { 279 | + panic("Page is still in use! Cannot be freed"); 280 | + } 281 | + pp->pp_link = page_free_list; 282 | + page_free_list = pp; 283 | } 284 | 285 | // 286 | @@ -327,6 +399,36 @@ pte_t * 287 | pgdir_walk(pde_t *pgdir, const void *va, int create) 288 | { 289 | // Fill this function in 290 | + pde_t pg_dir_entry = pgdir[PDX(va)]; 291 | + 292 | + if (pg_dir_entry == 0) 293 | + { 294 | + if (create == 0) 295 | + { 296 | + return NULL; 297 | + } 298 | + 299 | + // alloc a page for holding one page table 300 | + struct PageInfo *pginfo = page_alloc(ALLOC_ZERO); 301 | + 302 | + if (pginfo == NULL) 303 | + { 304 | + return NULL; 305 | + } 306 | + 307 | + pginfo->pp_ref++; 308 | + physaddr_t pa = page2pa(pginfo); // page-table page's physical address 309 | + 310 | + pgdir[PDX(va)] = pa | PTE_P | PTE_W | PTE_U; // add writable, when get, needs to purge all flags 311 | + 312 | + return (pte_t *)KADDR(pa) + PTX(va); 313 | + } 314 | + 315 | + if (pg_dir_entry & PTE_P) 316 | + { 317 | + return (pte_t *)KADDR(PTE_ADDR(pg_dir_entry)) + PTX(va); // check_va2pa could fail if not cast to pte_t* type... the calculation went wrong 318 | + } 319 | + 320 | return NULL; 321 | } 322 | 323 | @@ -345,6 +447,22 @@ static void 324 | boot_map_region(pde_t *pgdir, uintptr_t va, size_t size, physaddr_t pa, int perm) 325 | { 326 | // Fill this function in 327 | + // don't directly use (va + size) as bound in for loop! 328 | + // it will exceed uint32 max value(2 ^ 32 - 1) when calculating KERNBASE mapping 329 | + for (int i = 0; i < size / PGSIZE; i++) 330 | + { 331 | + pte_t *pte_entry = pgdir_walk(pgdir, (void *)va, 1); 332 | + if (pte_entry != NULL) 333 | + { 334 | + *pte_entry = pa | perm | PTE_P; 335 | + } 336 | + else 337 | + { 338 | + panic("No more space for boot_map_region"); 339 | + } 340 | + va += PGSIZE; 341 | + pa += PGSIZE; 342 | + } 343 | } 344 | 345 | // 346 | @@ -355,7 +473,7 @@ boot_map_region(pde_t *pgdir, uintptr_t va, size_t size, physaddr_t pa, int perm 347 | // Requirements 348 | // - If there is already a page mapped at 'va', it should be page_remove()d. 349 | // - If necessary, on demand, a page table should be allocated and inserted 350 | -// into 'pgdir'. 351 | +// into 'pgdir'. create == 1 352 | // - pp->pp_ref should be incremented if the insertion succeeds. 353 | // - The TLB must be invalidated if a page was formerly present at 'va'. 354 | // 355 | @@ -363,7 +481,7 @@ boot_map_region(pde_t *pgdir, uintptr_t va, size_t size, physaddr_t pa, int perm 356 | // pp is re-inserted at the same virtual address in the same pgdir. 357 | // However, try not to distinguish this case in your code, as this 358 | // frequently leads to subtle bugs; there's an elegant way to handle 359 | -// everything in one code path. 360 | +// everything in one code path. ref will be 2? 361 | // 362 | // RETURNS: 363 | // 0 on success 364 | @@ -376,6 +494,29 @@ int 365 | page_insert(pde_t *pgdir, struct PageInfo *pp, void *va, int perm) 366 | { 367 | // Fill this function in 368 | + pte_t *pg_tbl_entry = pgdir_walk(pgdir, va, 1); 369 | + if (pg_tbl_entry != NULL) 370 | + { 371 | + physaddr_t pa = page2pa(pp); 372 | + if (PTE_ADDR(*pg_tbl_entry) == pa) 373 | + { 374 | + // do not free the page 375 | + pp->pp_ref--; 376 | + tlb_invalidate(pgdir, va); 377 | + } 378 | + else if (*pg_tbl_entry & PTE_P) 379 | + { 380 | + page_remove(pgdir, va); 381 | + } 382 | + 383 | + *pg_tbl_entry = pa | perm | PTE_P; 384 | + pp->pp_ref++; 385 | + } 386 | + else 387 | + { 388 | + return -E_NO_MEM; 389 | + } 390 | + 391 | return 0; 392 | } 393 | 394 | @@ -394,6 +535,19 @@ struct PageInfo * 395 | page_lookup(pde_t *pgdir, void *va, pte_t **pte_store) 396 | { 397 | // Fill this function in 398 | + pte_t *page_tbl_entry = pgdir_walk(pgdir, va, 0); 399 | + 400 | + if (page_tbl_entry != NULL && (*page_tbl_entry & PTE_P)) 401 | + { 402 | + physaddr_t pa = PTE_ADDR(*page_tbl_entry); 403 | + if (pte_store != NULL) 404 | + { 405 | + *pte_store = page_tbl_entry; 406 | + } 407 | + 408 | + return pa2page(pa); 409 | + } 410 | + 411 | return NULL; 412 | } 413 | 414 | @@ -416,6 +570,15 @@ void 415 | page_remove(pde_t *pgdir, void *va) 416 | { 417 | // Fill this function in 418 | + pte_t *pte_store = NULL; 419 | + struct PageInfo *pginfo = page_lookup(pgdir, va, &pte_store); 420 | + 421 | + if (pginfo != NULL) 422 | + { 423 | + page_decref(pginfo); 424 | + *pte_store = 0; 425 | + tlb_invalidate(pgdir, va); 426 | + } 427 | } 428 | 429 | // 430 | @@ -839,3 +1002,4 @@ check_page_installed_pgdir(void) 431 | 432 | cprintf("check_page_installed_pgdir() succeeded!\n"); 433 | } 434 | + 435 | -- 436 | 2.24.3 (Apple Git-128) 437 | 438 | -------------------------------------------------------------------------------- /homework/0001-homework-syscall-part1-part2.patch: -------------------------------------------------------------------------------- 1 | From d32fc8e42e3b95d2725cd551d30f8d6e7f787a20 Mon Sep 17 00:00:00 2001 2 | From: Aaron 3 | Date: Thu, 28 May 2020 18:15:31 +0200 4 | Subject: [PATCH] homework syscall part1 & part2 and challenge 5 | 6 | https://pdos.csail.mit.edu/6.828/2018/homework/xv6-syscall.html 7 | --- 8 | Makefile | 3 ++- 9 | date.c | 19 +++++++++++++++++++ 10 | proc.c | 11 ++++++----- 11 | sh.c | 4 ++-- 12 | syscall.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 13 | syscall.h | 2 ++ 14 | sysfile.c | 46 +++++++++++++++++++++++++++++++++++++++++++++- 15 | sysproc.c | 21 +++++++++++++++++++++ 16 | trap.c | 2 +- 17 | user.h | 2 ++ 18 | usys.S | 3 +++ 19 | 11 files changed, 148 insertions(+), 10 deletions(-) 20 | create mode 100644 date.c 21 | 22 | diff --git a/Makefile b/Makefile 23 | index 09d790c..e26ed07 100644 24 | --- a/Makefile 25 | +++ b/Makefile 26 | @@ -157,7 +157,7 @@ _forktest: forktest.o $(ULIB) 27 | $(OBJDUMP) -S _forktest > forktest.asm 28 | 29 | mkfs: mkfs.c fs.h 30 | - gcc -Werror -Wall -o mkfs mkfs.c 31 | + gcc -Werror -Wno-nullability-completeness -Wall -o mkfs mkfs.c 32 | 33 | # Prevent deletion of intermediate files, e.g. cat.o, after first build, so 34 | # that disk image changes after first build are persistent until clean. More 35 | @@ -181,6 +181,7 @@ UPROGS=\ 36 | _usertests\ 37 | _wc\ 38 | _zombie\ 39 | + _date\ 40 | 41 | fs.img: mkfs README $(UPROGS) 42 | ./mkfs fs.img README $(UPROGS) 43 | diff --git a/date.c b/date.c 44 | new file mode 100644 45 | index 0000000..64cd3bc 46 | --- /dev/null 47 | +++ b/date.c 48 | @@ -0,0 +1,19 @@ 49 | +#include "types.h" 50 | +#include "user.h" 51 | +#include "date.h" 52 | + 53 | +int 54 | +main(int argc, char *argv[]) 55 | +{ 56 | + struct rtcdate r; 57 | + 58 | + if (date(&r)) { 59 | + printf(2, "date failed\n"); 60 | + exit(); 61 | + } 62 | + 63 | + // your code to print the time in any format you like... 64 | + // printf(1, "hour: %d, minute: %d, second: %d\n", r.hour, r.minute, r.second); 65 | + 66 | + exit(); 67 | +} 68 | \ No newline at end of file 69 | diff --git a/proc.c b/proc.c 70 | index 806b1b1..4fb202e 100644 71 | --- a/proc.c 72 | +++ b/proc.c 73 | @@ -126,9 +126,9 @@ userinit(void) 74 | p = allocproc(); 75 | 76 | initproc = p; 77 | - if((p->pgdir = setupkvm()) == 0) 78 | + if((p->pgdir = setupkvm()) == 0) // setup process's page table pages for context switch, mapping kernel content to address space 79 | panic("userinit: out of memory?"); 80 | - inituvm(p->pgdir, _binary_initcode_start, (int)_binary_initcode_size); 81 | + inituvm(p->pgdir, _binary_initcode_start, (int)_binary_initcode_size); // now mapping self's content 82 | p->sz = PGSIZE; 83 | memset(p->tf, 0, sizeof(*p->tf)); 84 | p->tf->cs = (SEG_UCODE << 3) | DPL_USER; 85 | @@ -136,8 +136,8 @@ userinit(void) 86 | p->tf->es = p->tf->ds; 87 | p->tf->ss = p->tf->ds; 88 | p->tf->eflags = FL_IF; 89 | - p->tf->esp = PGSIZE; 90 | - p->tf->eip = 0; // beginning of initcode.S 91 | + p->tf->esp = PGSIZE; // User stack 92 | + p->tf->eip = 0; // beginning of initcode.S, now virtual address (0 - 4095) mapped to physical address loaded of executable file 93 | 94 | safestrcpy(p->name, "initcode", sizeof(p->name)); 95 | p->cwd = namei("/"); 96 | @@ -340,7 +340,7 @@ scheduler(void) 97 | // to release ptable.lock and then reacquire it 98 | // before jumping back to us. 99 | c->proc = p; 100 | - switchuvm(p); 101 | + switchuvm(p); // enter user space by setting %cr3 register to user space pgdir 102 | p->state = RUNNING; 103 | 104 | swtch(&(c->scheduler), p->context); 105 | @@ -396,6 +396,7 @@ yield(void) 106 | void 107 | forkret(void) 108 | { 109 | + // after prologue: push %ebp; mov %esp, %ebp... hit return in GDB 110 | static int first = 1; 111 | // Still holding ptable.lock from scheduler. 112 | release(&ptable.lock); 113 | diff --git a/sh.c b/sh.c 114 | index 054bab9..f56d895 100644 115 | --- a/sh.c 116 | +++ b/sh.c 117 | @@ -103,14 +103,14 @@ runcmd(struct cmd *cmd) 118 | panic("pipe"); 119 | if(fork1() == 0){ 120 | close(1); 121 | - dup(p[1]); 122 | + dup2(p[1], 1); 123 | close(p[0]); 124 | close(p[1]); 125 | runcmd(pcmd->left); 126 | } 127 | if(fork1() == 0){ 128 | close(0); 129 | - dup(p[0]); 130 | + dup2(p[0], 0); 131 | close(p[0]); 132 | close(p[1]); 133 | runcmd(pcmd->right); 134 | diff --git a/syscall.c b/syscall.c 135 | index ee85261..85b7765 100644 136 | --- a/syscall.c 137 | +++ b/syscall.c 138 | @@ -103,6 +103,8 @@ extern int sys_unlink(void); 139 | extern int sys_wait(void); 140 | extern int sys_write(void); 141 | extern int sys_uptime(void); 142 | +extern int sys_date(void); 143 | +extern int sys_dup2(void); 144 | 145 | static int (*syscalls[])(void) = { 146 | [SYS_fork] sys_fork, 147 | @@ -126,6 +128,35 @@ static int (*syscalls[])(void) = { 148 | [SYS_link] sys_link, 149 | [SYS_mkdir] sys_mkdir, 150 | [SYS_close] sys_close, 151 | +[SYS_date] sys_date, 152 | +[SYS_dup2] sys_dup2, 153 | +}; 154 | + 155 | +// use a string table for the name, any better idea? 156 | +static char *syscallnames[] = { 157 | +[SYS_fork] "fork", 158 | +[SYS_exit] "exit", 159 | +[SYS_wait] "wait", 160 | +[SYS_pipe] "pipe", 161 | +[SYS_read] "read", 162 | +[SYS_kill] "kill", 163 | +[SYS_exec] "exec", 164 | +[SYS_fstat] "fstat", 165 | +[SYS_chdir] "chdir", 166 | +[SYS_dup] "dup", 167 | +[SYS_getpid] "getpid", 168 | +[SYS_sbrk] "sbrk", 169 | +[SYS_sleep] "sleep", 170 | +[SYS_uptime] "uptime", 171 | +[SYS_open] "open", 172 | +[SYS_write] "write", 173 | +[SYS_mknod] "mknod", 174 | +[SYS_unlink] "unlink", 175 | +[SYS_link] "link", 176 | +[SYS_mkdir] "mkdir", 177 | +[SYS_close] "close", 178 | +[SYS_date] "date", 179 | +[SYS_dup2] "dup2", 180 | }; 181 | 182 | void 183 | @@ -136,7 +167,21 @@ syscall(void) 184 | 185 | num = curproc->tf->eax; 186 | if(num > 0 && num < NELEM(syscalls) && syscalls[num]) { 187 | + // should put before the real syscall, e.g. what if exec uses a 188 | + // new image replace the old one, the arguments would be nowhere to track 189 | + // 190 | + // not all sys calls got argument, it should be case by case 191 | + // keep user stack less than curproc->sz when tracing arguments, 192 | + // but how to tell arguments' number, type? using a jump table (switch)? 193 | + // 194 | + // or go into each syscall's wrapper to print them out? 195 | + // uint first_addr = curproc->tf->esp + 4; 196 | + // char *argument; 197 | + // if ((argstr(0, &argument)) != -1 && first_addr < curproc->sz) 198 | + // cprintf("first argument: %s\n", argument); 199 | + 200 | curproc->tf->eax = syscalls[num](); 201 | + cprintf("%s -> %d\n\n", syscallnames[num], curproc->tf->eax); 202 | } else { 203 | cprintf("%d %s: unknown sys call %d\n", 204 | curproc->pid, curproc->name, num); 205 | diff --git a/syscall.h b/syscall.h 206 | index bc5f356..60d4196 100644 207 | --- a/syscall.h 208 | +++ b/syscall.h 209 | @@ -20,3 +20,5 @@ 210 | #define SYS_link 19 211 | #define SYS_mkdir 20 212 | #define SYS_close 21 213 | +#define SYS_date 22 214 | +#define SYS_dup2 23 215 | diff --git a/sysfile.c b/sysfile.c 216 | index bfe61b7..b426dfd 100644 217 | --- a/sysfile.c 218 | +++ b/sysfile.c 219 | @@ -44,6 +44,7 @@ fdalloc(struct file *f) 220 | struct proc *curproc = myproc(); 221 | 222 | for(fd = 0; fd < NOFILE; fd++){ 223 | + // linear find to reach the smallest fd in current process 224 | if(curproc->ofile[fd] == 0){ 225 | curproc->ofile[fd] = f; 226 | return fd; 227 | @@ -60,12 +61,33 @@ sys_dup(void) 228 | 229 | if(argfd(0, 0, &f) < 0) 230 | return -1; 231 | + cprintf("dup will make a duplication of file object: %p\n", f); 232 | if((fd=fdalloc(f)) < 0) 233 | return -1; 234 | filedup(f); 235 | return fd; 236 | } 237 | 238 | +int 239 | +sys_dup2(void) 240 | +{ 241 | + struct file *f; 242 | + int newfd; 243 | + if (argfd(0, 0, &f) || argint(1, &newfd) < 0) 244 | + return -1; 245 | + cprintf("dup2 will make a duplication of file object %p for given file descriptor %d\n", f, newfd); 246 | + // check this given newfd is not taken already 247 | + struct proc *curproc = myproc(); 248 | + if (curproc->ofile[newfd] == 0) 249 | + { 250 | + curproc->ofile[newfd] = f; 251 | + filedup(f); 252 | + return newfd; 253 | + } 254 | + 255 | + return -1; 256 | +} 257 | + 258 | int 259 | sys_read(void) 260 | { 261 | @@ -75,6 +97,7 @@ sys_read(void) 262 | 263 | if(argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argptr(1, &p, n) < 0) 264 | return -1; 265 | + cprintf("read on file object %p, file buffer address: %p, size %d\n", f, p, n); 266 | return fileread(f, p, n); 267 | } 268 | 269 | @@ -87,6 +110,7 @@ sys_write(void) 270 | 271 | if(argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argptr(1, &p, n) < 0) 272 | return -1; 273 | + cprintf("write to file object %p, file buffer address: %p, size %d\n",f, p, n); 274 | return filewrite(f, p, n); 275 | } 276 | 277 | @@ -98,6 +122,7 @@ sys_close(void) 278 | 279 | if(argfd(0, &fd, &f) < 0) 280 | return -1; 281 | + cprintf("close file descriptor %d previously pointed to file object %p\n", fd, f); 282 | myproc()->ofile[fd] = 0; 283 | fileclose(f); 284 | return 0; 285 | @@ -111,6 +136,7 @@ sys_fstat(void) 286 | 287 | if(argfd(0, 0, &f) < 0 || argptr(1, (void*)&st, sizeof(*st)) < 0) 288 | return -1; 289 | + cprintf("fstat check file object %p, store file status on %p\n", f, st); 290 | return filestat(f, st); 291 | } 292 | 293 | @@ -123,6 +149,7 @@ sys_link(void) 294 | 295 | if(argstr(0, &old) < 0 || argstr(1, &new) < 0) 296 | return -1; 297 | + cprintf("link create new link %s to existing file %s\n", new, old); 298 | 299 | begin_op(); 300 | if((ip = namei(old)) == 0){ 301 | @@ -191,7 +218,7 @@ sys_unlink(void) 302 | 303 | if(argstr(0, &path) < 0) 304 | return -1; 305 | - 306 | + cprintf("unlink delete %s from file system\n", path); 307 | begin_op(); 308 | if((dp = nameiparent(path, name)) == 0){ 309 | end_op(); 310 | @@ -292,6 +319,16 @@ sys_open(void) 311 | 312 | if(argstr(0, &path) < 0 || argint(1, &omode) < 0) 313 | return -1; 314 | + cprintf("open file %s with mode ", path); 315 | + if (omode & O_CREATE) 316 | + cprintf("O_CREATE "); 317 | + if (omode & O_RDONLY) 318 | + cprintf("O_RDONLY "); 319 | + if (omode & O_RDWR) 320 | + cprintf("O_RDWR "); 321 | + if (omode & O_WRONLY) 322 | + cprintf("O_WRONLY "); 323 | + cprintf("\n"); 324 | 325 | begin_op(); 326 | 327 | @@ -343,6 +380,7 @@ sys_mkdir(void) 328 | end_op(); 329 | return -1; 330 | } 331 | + cprintf("mkdir create directory %s\n", path); 332 | iunlockput(ip); 333 | end_op(); 334 | return 0; 335 | @@ -363,6 +401,7 @@ sys_mknod(void) 336 | end_op(); 337 | return -1; 338 | } 339 | + cprintf("mknod creates file node %p on file path %s, major %d, minor %d\n", ip, path, major, minor); 340 | iunlockput(ip); 341 | end_op(); 342 | return 0; 343 | @@ -380,6 +419,7 @@ sys_chdir(void) 344 | end_op(); 345 | return -1; 346 | } 347 | + cprintf("chdir change to %s\n", path); 348 | ilock(ip); 349 | if(ip->type != T_DIR){ 350 | iunlockput(ip); 351 | @@ -403,6 +443,7 @@ sys_exec(void) 352 | if(argstr(0, &path) < 0 || argint(1, (int*)&uargv) < 0){ 353 | return -1; 354 | } 355 | + cprintf("exec will load %s with arguments:", path); 356 | memset(argv, 0, sizeof(argv)); 357 | for(i=0;; i++){ 358 | if(i >= NELEM(argv)) 359 | @@ -415,7 +456,9 @@ sys_exec(void) 360 | } 361 | if(fetchstr(uarg, &argv[i]) < 0) 362 | return -1; 363 | + cprintf(" %s\n", argv[i]); 364 | } 365 | + 366 | return exec(path, argv); 367 | } 368 | 369 | @@ -428,6 +471,7 @@ sys_pipe(void) 370 | 371 | if(argptr(0, (void*)&fd, 2*sizeof(fd[0])) < 0) 372 | return -1; 373 | + cprintf("pipe read end here: %p, write end here: %p\n", fd, fd + 1); 374 | if(pipealloc(&rf, &wf) < 0) 375 | return -1; 376 | fd0 = -1; 377 | diff --git a/sysproc.c b/sysproc.c 378 | index 0686d29..2a38eb9 100644 379 | --- a/sysproc.c 380 | +++ b/sysproc.c 381 | @@ -33,6 +33,7 @@ sys_kill(void) 382 | 383 | if(argint(0, &pid) < 0) 384 | return -1; 385 | + cprintf("kill pid: %d\n", pid); 386 | return kill(pid); 387 | } 388 | 389 | @@ -50,6 +51,7 @@ sys_sbrk(void) 390 | 391 | if(argint(0, &n) < 0) 392 | return -1; 393 | + cprintf("sbrk increments data size by %d bytes\n",n); 394 | addr = myproc()->sz; 395 | if(growproc(n) < 0) 396 | return -1; 397 | @@ -64,6 +66,7 @@ sys_sleep(void) 398 | 399 | if(argint(0, &n) < 0) 400 | return -1; 401 | + cprintf("sleep %d secs\n", n); 402 | acquire(&tickslock); 403 | ticks0 = ticks; 404 | while(ticks - ticks0 < n){ 405 | @@ -89,3 +92,21 @@ sys_uptime(void) 406 | release(&tickslock); 407 | return xticks; 408 | } 409 | + 410 | +int 411 | +sys_date(void) 412 | +{ 413 | + char *date; 414 | + if (argptr(0, &date, sizeof(struct rtcdate)) < 0) 415 | + { 416 | + return -1; 417 | + } 418 | + cprintf("syscall date got called with rtc struct on address %p\n", date); 419 | + struct rtcdate *rtc = (struct rtcdate *)date; 420 | + // fill in rtcdate 421 | + cmostime(rtc); 422 | + cprintf("UTC time:\n"); 423 | + cprintf("%d-%d-%d %dh:%dm:%ds\n", rtc->year, rtc->month, rtc->day, 424 | + rtc->hour, rtc->minute, rtc->second); 425 | + return 0; 426 | +} 427 | diff --git a/trap.c b/trap.c 428 | index 41c66eb..b76cfae 100644 429 | --- a/trap.c 430 | +++ b/trap.c 431 | @@ -39,7 +39,7 @@ trap(struct trapframe *tf) 432 | if(tf->trapno == T_SYSCALL){ 433 | if(myproc()->killed) 434 | exit(); 435 | - myproc()->tf = tf; 436 | + myproc()->tf = tf; // store user process current state 437 | syscall(); 438 | if(myproc()->killed) 439 | exit(); 440 | diff --git a/user.h b/user.h 441 | index 4f99c52..eb2a141 100644 442 | --- a/user.h 443 | +++ b/user.h 444 | @@ -19,10 +19,12 @@ int link(const char*, const char*); 445 | int mkdir(const char*); 446 | int chdir(const char*); 447 | int dup(int); 448 | +int dup2(int, int); 449 | int getpid(void); 450 | char* sbrk(int); 451 | int sleep(int); 452 | int uptime(void); 453 | +int date(struct rtcdate*); 454 | 455 | // ulib.c 456 | int stat(const char*, struct stat*); 457 | diff --git a/usys.S b/usys.S 458 | index 8bfd8a1..07f432d 100644 459 | --- a/usys.S 460 | +++ b/usys.S 461 | @@ -1,6 +1,7 @@ 462 | #include "syscall.h" 463 | #include "traps.h" 464 | 465 | +# linker will find memory reference here 466 | #define SYSCALL(name) \ 467 | .globl name; \ 468 | name: \ 469 | @@ -29,3 +30,5 @@ SYSCALL(getpid) 470 | SYSCALL(sbrk) 471 | SYSCALL(sleep) 472 | SYSCALL(uptime) 473 | +SYSCALL(date) 474 | +SYSCALL(dup2) 475 | -- 476 | 2.24.3 (Apple Git-128) 477 | 478 | -------------------------------------------------------------------------------- /labs/0001-start-with-lab5.patch: -------------------------------------------------------------------------------- 1 | From 2ddc45cfde6e6de2c7ef4420d027d9542135869f Mon Sep 17 00:00:00 2001 2 | From: Aaron 3 | Date: Sun, 23 Aug 2020 14:22:13 +0000 4 | Subject: [PATCH] start with lab5 5 | 6 | finished with test.c 7 | finished testfile.c 8 | adding some comments 9 | finished exercise 7 10 | finished exercise 8 (be careful with PTE_AVAIL, it will enable the page 11 | sharing) 12 | finished exercise 9 (be careful with sys_page_alloc, need zero out page) 13 | finished exercise 10 (with file IO redirection) 14 | 15 | later update with some challenges 16 | --- 17 | fs/bc.c | 13 ++++++++- 18 | fs/fs.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++---- 19 | fs/serv.c | 24 ++++++++++++++-- 20 | inc/mmu.h | 1 + 21 | kern/env.c | 3 ++ 22 | kern/init.c | 3 +- 23 | kern/syscall.c | 17 ++++++++++-- 24 | kern/trap.c | 19 +++++++++++++ 25 | lib/exit.c | 2 +- 26 | lib/file.c | 12 +++++++- 27 | lib/fork.c | 26 +++++++++++------- 28 | lib/pgfault.c | 4 +-- 29 | lib/spawn.c | 29 +++++++++++++++++++- 30 | user/sh.c | 13 ++++++++- 31 | 14 files changed, 213 insertions(+), 27 deletions(-) 32 | 33 | diff --git a/fs/bc.c b/fs/bc.c 34 | index e3922c4..c9ab106 100644 35 | --- a/fs/bc.c 36 | +++ b/fs/bc.c 37 | @@ -48,6 +48,10 @@ bc_pgfault(struct UTrapframe *utf) 38 | // the disk. 39 | // 40 | // LAB 5: you code here: 41 | + addr = ROUNDDOWN(addr, BLKSIZE); 42 | + if ((r = sys_page_alloc(0, addr, PTE_SYSCALL)) < 0) 43 | + panic("in bc_pgfault, sys_page_alloc: %e", r); 44 | + ide_read(blockno * BLKSIZE / SECTSIZE, addr, BLKSIZE / SECTSIZE); 45 | 46 | // Clear the dirty bit for the disk block page since we just read the 47 | // block from disk 48 | @@ -77,7 +81,14 @@ flush_block(void *addr) 49 | panic("flush_block of bad va %08x", addr); 50 | 51 | // LAB 5: Your code here. 52 | - panic("flush_block not implemented"); 53 | + if (va_is_mapped(addr) && va_is_dirty(addr)) 54 | + { 55 | + addr = ROUNDDOWN(addr, BLKSIZE); 56 | + ide_write(blockno * BLKSIZE / SECTSIZE, addr, BLKSIZE / SECTSIZE); 57 | + int r; 58 | + if ((r = sys_page_map(0, addr, 0, addr, uvpt[PGNUM(addr)] & PTE_SYSCALL)) < 0) 59 | + panic("in flush_block, sys_page_map: %e", r); 60 | + } 61 | } 62 | 63 | // Test that the block cache works, by smashing the superblock and 64 | diff --git a/fs/fs.c b/fs/fs.c 65 | index 45ecaf8..fc85af4 100644 66 | --- a/fs/fs.c 67 | +++ b/fs/fs.c 68 | @@ -62,7 +62,16 @@ alloc_block(void) 69 | // super->s_nblocks blocks in the disk altogether. 70 | 71 | // LAB 5: Your code here. 72 | - panic("alloc_block not implemented"); 73 | + // panic("alloc_block not implemented"); 74 | + for (uint32_t i = 3; i < super->s_nblocks; i++) 75 | + { 76 | + if (block_is_free(i)) 77 | + { 78 | + bitmap[i / 32] &= ~(1 << (i % 32)); 79 | + flush_block(bitmap); 80 | + return i; 81 | + } 82 | + } 83 | return -E_NO_DISK; 84 | } 85 | 86 | @@ -134,8 +143,39 @@ fs_init(void) 87 | static int 88 | file_block_walk(struct File *f, uint32_t filebno, uint32_t **ppdiskbno, bool alloc) 89 | { 90 | - // LAB 5: Your code here. 91 | - panic("file_block_walk not implemented"); 92 | + // LAB 5: Your code here. 93 | + if (filebno >= NDIRECT + NINDIRECT) 94 | + return -E_INVAL; 95 | + if (filebno < NDIRECT) 96 | + { 97 | + if (ppdiskbno) 98 | + *ppdiskbno = f->f_direct + filebno; 99 | + return 0; 100 | + } 101 | + // haven't got result from direct blocks, so query indirect ones... 102 | + uint32_t *indirect_block; 103 | + if (f->f_indirect == 0) 104 | + { 105 | + if (alloc == 0) 106 | + return -E_NOT_FOUND; 107 | + 108 | + // alloc a block for indirect block 109 | + f->f_indirect = alloc_block(); 110 | + if (f->f_indirect < 0) 111 | + return f->f_indirect; 112 | + indirect_block = (uint32_t *)diskaddr(f->f_indirect); 113 | + // this should trigger a page fault which reads some data from disk 114 | + // and then we manage to clear the block 115 | + memset(indirect_block, 0, BLKSIZE); 116 | + } 117 | + else 118 | + { 119 | + indirect_block = (uint32_t *)diskaddr(f->f_indirect); 120 | + } 121 | + if (ppdiskbno) 122 | + *ppdiskbno = indirect_block + filebno - NDIRECT; 123 | + 124 | + return 0; 125 | } 126 | 127 | // Set *blk to the address in memory where the filebno'th 128 | @@ -149,8 +189,30 @@ file_block_walk(struct File *f, uint32_t filebno, uint32_t **ppdiskbno, bool all 129 | int 130 | file_get_block(struct File *f, uint32_t filebno, char **blk) 131 | { 132 | - // LAB 5: Your code here. 133 | - panic("file_get_block not implemented"); 134 | + // LAB 5: Your code here. 135 | + if (filebno >= NDIRECT + NINDIRECT) 136 | + return -E_INVAL; 137 | + int r = 0; 138 | + uint32_t *ppdiskbno; 139 | + if ((r = file_block_walk(f, filebno, &ppdiskbno, true)) < 0) 140 | + return r; 141 | + if (blk) 142 | + { 143 | + if (*ppdiskbno != 0) 144 | + { 145 | + *blk = diskaddr(*ppdiskbno); 146 | + return 0; 147 | + } 148 | + // demand allocation 149 | + int blkno = alloc_block(); 150 | + if (blkno < 0) 151 | + return blkno; 152 | + // update the entry 153 | + *ppdiskbno = (uint32_t)blkno; 154 | + *blk = diskaddr(*ppdiskbno); 155 | + } 156 | + 157 | + return 0; 158 | } 159 | 160 | // Try to find a file named "name" in dir. If so, set *file to it. 161 | @@ -173,6 +235,8 @@ dir_lookup(struct File *dir, const char *name, struct File **file) 162 | for (i = 0; i < nblock; i++) { 163 | if ((r = file_get_block(dir, i, &blk)) < 0) 164 | return r; 165 | + // file system interprets the contents of a directory-file as a series of File structures, 166 | + // describing the files and subdirectories within the directory. 167 | f = (struct File*) blk; 168 | for (j = 0; j < BLKFILES; j++) 169 | if (strcmp(f[j].f_name, name) == 0) { 170 | diff --git a/fs/serv.c b/fs/serv.c 171 | index 76c1d99..55269c9 100644 172 | --- a/fs/serv.c 173 | +++ b/fs/serv.c 174 | @@ -70,6 +70,8 @@ openfile_alloc(struct OpenFile **o) 175 | for (i = 0; i < MAXOPEN; i++) { 176 | switch (pageref(opentab[i].o_fd)) { 177 | case 0: 178 | + // fd page will be actually copy-on-write once after fork 179 | + // we need to correct this in our fork 180 | if ((r = sys_page_alloc(0, opentab[i].o_fd, PTE_P|PTE_U|PTE_W)) < 0) 181 | return r; 182 | /* fall through */ 183 | @@ -214,7 +216,16 @@ serve_read(envid_t envid, union Fsipc *ipc) 184 | cprintf("serve_read %08x %08x %08x\n", envid, req->req_fileid, req->req_n); 185 | 186 | // Lab 5: Your code here: 187 | - return 0; 188 | + int r; 189 | + struct OpenFile *o; 190 | + if ((r = openfile_lookup(envid, req->req_fileid, &o)) < 0) 191 | + return r; 192 | + 193 | + if ((r = file_read(o->o_file, ret->ret_buf, req->req_n, o->o_fd->fd_offset)) < 0) 194 | + return r; 195 | + 196 | + o->o_fd->fd_offset += r; 197 | + return r; 198 | } 199 | 200 | 201 | @@ -229,7 +240,16 @@ serve_write(envid_t envid, struct Fsreq_write *req) 202 | cprintf("serve_write %08x %08x %08x\n", envid, req->req_fileid, req->req_n); 203 | 204 | // LAB 5: Your code here. 205 | - panic("serve_write not implemented"); 206 | + int r; 207 | + struct OpenFile *o; 208 | + if ((r = openfile_lookup(envid, req->req_fileid, &o)) < 0) 209 | + return r; 210 | + 211 | + if ((r = file_write(o->o_file, req->req_buf, req->req_n, o->o_fd->fd_offset)) < 0) 212 | + return r; 213 | + 214 | + o->o_fd->fd_offset += r; 215 | + return r; 216 | } 217 | 218 | // Stat ipc->stat.req_fileid. Return the file's struct Stat to the 219 | diff --git a/inc/mmu.h b/inc/mmu.h 220 | index a9c7241..10207bd 100644 221 | --- a/inc/mmu.h 222 | +++ b/inc/mmu.h 223 | @@ -67,6 +67,7 @@ 224 | 225 | // The PTE_AVAIL bits aren't used by the kernel or interpreted by the 226 | // hardware, so user processes are allowed to set them arbitrarily. 227 | +// including PTE_SHARE 0x400 228 | #define PTE_AVAIL 0xE00 // Available for software use 229 | 230 | // Flags in PTE_SYSCALL may be used in system calls. (Others may not.) 231 | diff --git a/kern/env.c b/kern/env.c 232 | index 1ac611c..d7f90bd 100644 233 | --- a/kern/env.c 234 | +++ b/kern/env.c 235 | @@ -274,6 +274,7 @@ env_alloc(struct Env **newenv_store, envid_t parent_id) 236 | env_free_list = e->env_link; 237 | *newenv_store = e; 238 | 239 | + // slient this line 240 | // cprintf("[%08x] new env %08x\n", curenv ? curenv->env_id : 0, e->env_id); 241 | return 0; 242 | } 243 | @@ -420,6 +421,8 @@ env_create(uint8_t *binary, enum EnvType type) 244 | if (env_alloc(&new_env, 0) != 0) 245 | panic("env_alloc"); 246 | load_icode(new_env, binary); 247 | + if (type == ENV_TYPE_FS) 248 | + new_env->env_tf.tf_eflags |= FL_IOPL_3; 249 | new_env->env_type = type; 250 | } 251 | 252 | diff --git a/kern/init.c b/kern/init.c 253 | index 91ae9e1..1f4c4ad 100644 254 | --- a/kern/init.c 255 | +++ b/kern/init.c 256 | @@ -56,7 +56,8 @@ i386_init(void) 257 | ENV_CREATE(TEST, ENV_TYPE_USER); 258 | #else 259 | // Touch all you want. 260 | - ENV_CREATE(user_icode, ENV_TYPE_USER); 261 | + // ENV_CREATE(user_icode, ENV_TYPE_USER); 262 | + ENV_CREATE(user_spawnhello, ENV_TYPE_USER); 263 | #endif // TEST* 264 | 265 | // Should not be necessary - drains keyboard because interrupt has given up. 266 | diff --git a/kern/syscall.c b/kern/syscall.c 267 | index a9e6d38..315f404 100644 268 | --- a/kern/syscall.c 269 | +++ b/kern/syscall.c 270 | @@ -134,7 +134,18 @@ sys_env_set_trapframe(envid_t envid, struct Trapframe *tf) 271 | // LAB 5: Your code here. 272 | // Remember to check whether the user has supplied us with a good 273 | // address! 274 | - panic("sys_env_set_trapframe not implemented"); 275 | + int ret = 0; 276 | + struct Env *env = NULL; 277 | + if ((ret = envid2env(envid, &env, 1)) != 0) 278 | + return ret; 279 | + 280 | + // copy the trapframe 281 | + user_mem_assert(env, tf, sizeof(struct Trapframe), PTE_U); 282 | + env->env_tf = *tf; 283 | + env->env_tf.tf_cs = GD_UT | 3; 284 | + env->env_tf.tf_eflags |= FL_IF; 285 | + env->env_tf.tf_eflags &= ~FL_IOPL_MASK; 286 | + return 0; 287 | } 288 | 289 | // Set the page fault upcall for 'envid' by modifying the corresponding struct 290 | @@ -191,7 +202,7 @@ sys_page_alloc(envid_t envid, void *va, int perm) 291 | if ((perm & PTE_P) != PTE_P || (perm & PTE_U) != PTE_U || (perm & ~PTE_SYSCALL) != 0) 292 | return -E_INVAL; 293 | 294 | - struct PageInfo *page = page_alloc(0); 295 | + struct PageInfo *page = page_alloc(ALLOC_ZERO); 296 | if (page == NULL) 297 | return -E_NO_MEM; 298 | if (page_insert(env->env_pgdir, page, va, perm) != 0) 299 | @@ -404,6 +415,8 @@ syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, 300 | return sys_exofork(); 301 | case SYS_env_set_status: 302 | return sys_env_set_status(a1, a2); 303 | + case SYS_env_set_trapframe: 304 | + return sys_env_set_trapframe(a1, (struct Trapframe *)a2); 305 | case SYS_env_set_pgfault_upcall: 306 | return sys_env_set_pgfault_upcall(a1, (void *)a2); 307 | case SYS_yield: 308 | diff --git a/kern/trap.c b/kern/trap.c 309 | index 5f110a7..ccf82f7 100644 310 | --- a/kern/trap.c 311 | +++ b/kern/trap.c 312 | @@ -245,6 +245,14 @@ trap_dispatch(struct Trapframe *tf) 313 | sched_yield(); 314 | break; 315 | 316 | + case IRQ_OFFSET + IRQ_KBD: 317 | + kbd_intr(); 318 | + return; 319 | + 320 | + case IRQ_OFFSET + IRQ_SERIAL: 321 | + serial_intr(); 322 | + return; 323 | + 324 | default: 325 | break; 326 | } 327 | @@ -310,6 +318,17 @@ trap(struct Trapframe *tf) 328 | // Copy trap frame (which is currently on the stack) 329 | // into 'curenv->env_tf', so that running the environment 330 | // will restart at the trap point. 331 | + 332 | + // The reason for this copy: difference from xv6 333 | + // in xv6, each process(env) has its own kernel stack, so each process(env) can 334 | + // always rely on its own stack even during task switch, since task switch always switch kernel stack. 335 | + 336 | + // But in JOS here, each CPU got its own kernel stack instead of each process(env), process(env) only keeps a 337 | + // *snapshot* of trapframe when it gets trapped into the kernel, so that kernel 338 | + // can freely do task switch without worrying about the stack switch (we do switch always in env_run()). 339 | + // If we don't do this copy, only keep the pointer, when a timer interrupt comes in, the kernel stack would be 340 | + // replaced with another process's state, once we re-run the previous one, it will use the state from another 341 | + // process(env), which definitely is wrong! 342 | curenv->env_tf = *tf; 343 | // The trapframe on the stack should be ignored from here on. 344 | tf = &curenv->env_tf; 345 | diff --git a/lib/exit.c b/lib/exit.c 346 | index cee3336..438fb5a 100644 347 | --- a/lib/exit.c 348 | +++ b/lib/exit.c 349 | @@ -4,7 +4,7 @@ 350 | void 351 | exit(void) 352 | { 353 | - close_all(); 354 | + // close_all(); 355 | sys_env_destroy(0); 356 | } 357 | 358 | diff --git a/lib/file.c b/lib/file.c 359 | index 39025b2..7697693 100644 360 | --- a/lib/file.c 361 | +++ b/lib/file.c 362 | @@ -141,7 +141,17 @@ devfile_write(struct Fd *fd, const void *buf, size_t n) 363 | // remember that write is always allowed to write *fewer* 364 | // bytes than requested. 365 | // LAB 5: Your code here 366 | - panic("devfile_write not implemented"); 367 | + int r; 368 | + 369 | + fsipcbuf.write.req_fileid = fd->fd_file.id; 370 | + fsipcbuf.write.req_n = n; 371 | + memmove(fsipcbuf.write.req_buf, buf, n); 372 | + if ((r = fsipc(FSREQ_WRITE, NULL)) < 0) 373 | + return r; 374 | + 375 | + assert(r <= n); 376 | + assert(r <= sizeof(fsipcbuf.write.req_buf)); 377 | + return r; 378 | } 379 | 380 | static int 381 | diff --git a/lib/fork.c b/lib/fork.c 382 | index 3f19405..ce606d6 100644 383 | --- a/lib/fork.c 384 | +++ b/lib/fork.c 385 | @@ -27,11 +27,11 @@ pgfault(struct UTrapframe *utf) 386 | // LAB 4: Your code here. 387 | if ((err & FEC_WR) != FEC_WR) 388 | { 389 | - panic("Access is not writing, err code %d\n", err); 390 | + panic("Access to addr 0x%x is not writing, err code %d\n", addr, err); 391 | } 392 | if ((uvpt[PGNUM(addr)] & PTE_COW) != PTE_COW) 393 | { 394 | - panic("Fault address not marked as Copy-on-Write"); 395 | + panic("Fault address 0x%x not marked as Copy-on-Write", addr); 396 | } 397 | 398 | // Allocate a new page, map it at a temporary location (PFTEMP), 399 | @@ -41,16 +41,18 @@ pgfault(struct UTrapframe *utf) 400 | // You should make three system calls. 401 | 402 | // LAB 4: Your code here. 403 | - envid_t envid = sys_getenvid(); 404 | + // envid_t envid = sys_getenvid(); 405 | // cprintf("fault addr 0x%x, envid %x\n", addr, envid); 406 | addr = ROUNDDOWN(addr, PGSIZE); // page-size aligned 407 | 408 | - if ((r = sys_page_alloc(envid, PFTEMP, PTE_P | PTE_U | PTE_W)) != 0) 409 | + if ((r = sys_page_alloc(0, PFTEMP, PTE_P | PTE_U | PTE_W)) != 0) 410 | panic("sys_page_alloc, %e", r); 411 | memmove((void *)PFTEMP, addr, PGSIZE); 412 | // remap the addr with newly allocated writable page 413 | - if ((r = sys_page_map(envid, PFTEMP, envid, addr, PTE_P | PTE_U | PTE_W)) != 0) 414 | + if ((r = sys_page_map(0, PFTEMP, 0, addr, PTE_P | PTE_U | PTE_W)) != 0) 415 | panic("sys_page_map, %e, fault addr 0x%x", r, addr); 416 | + if ((r = sys_page_unmap(0, PFTEMP)) != 0) 417 | + panic("sys_page_unmap, %e", r); 418 | } 419 | 420 | // 421 | @@ -71,23 +73,27 @@ duppage(envid_t envid, unsigned pn) 422 | 423 | // LAB 4: Your code here. 424 | int perm = PTE_P | PTE_U; // at least PTE_P and PTE_U 425 | - envid_t curenvid = sys_getenvid(); 426 | + // envid_t curenvid = sys_getenvid(); 427 | 428 | int is_wr = (uvpt[pn] & PTE_W) == PTE_W; 429 | int is_cow = (uvpt[pn] & PTE_COW) == PTE_COW; 430 | + int is_shared = (uvpt[pn] & PTE_SHARE); 431 | + 432 | void *addr = (void *)(pn * PGSIZE); 433 | - if (is_wr || is_cow) 434 | + if ((is_wr || is_cow) && !is_shared) 435 | { 436 | // create new mapping 437 | - if ((r = sys_page_map(curenvid, addr, envid, addr, perm | PTE_COW)) != 0) 438 | + if ((r = sys_page_map(0, addr, envid, addr, perm | PTE_COW)) != 0) 439 | panic("sys_page_map, %e", r); 440 | - if ((r = sys_page_map(curenvid, addr, curenvid, addr, perm | PTE_COW)) != 0) 441 | + if ((r = sys_page_map(0, addr, 0, addr, perm | PTE_COW)) != 0) 442 | panic("sys_page_map, %e", r); 443 | } 444 | else 445 | { 446 | + if (is_shared) 447 | + perm = PTE_SYSCALL & uvpt[pn]; 448 | // only remap child without PTE_COW 449 | - if ((r = sys_page_map(curenvid, addr, envid, addr, perm)) != 0) 450 | + if ((r = sys_page_map(0, addr, envid, addr, perm)) != 0) 451 | panic("sys_page_map, %e", r); 452 | } 453 | return 0; 454 | diff --git a/lib/pgfault.c b/lib/pgfault.c 455 | index 0cb1b33..ab0138d 100644 456 | --- a/lib/pgfault.c 457 | +++ b/lib/pgfault.c 458 | @@ -29,9 +29,9 @@ set_pgfault_handler(void (*handler)(struct UTrapframe *utf)) 459 | if (_pgfault_handler == 0) { 460 | // First time through! 461 | // LAB 4: Your code here. 462 | - if ((r = sys_page_alloc(sys_getenvid(), (void *)(UXSTACKTOP - PGSIZE), PTE_P | PTE_U | PTE_W)) != 0) 463 | + if ((r = sys_page_alloc(0, (void *)(UXSTACKTOP - PGSIZE), PTE_P | PTE_U | PTE_W)) != 0) 464 | panic("sys_page_alloc, %e", r); 465 | - if ((r = sys_env_set_pgfault_upcall(sys_getenvid(), _pgfault_upcall)) != 0) 466 | + if ((r = sys_env_set_pgfault_upcall(0, _pgfault_upcall)) != 0) 467 | panic("sys_env_set_pgfault_upcall, %e", r); 468 | } 469 | 470 | diff --git a/lib/spawn.c b/lib/spawn.c 471 | index 9d0eb07..1e27ded 100644 472 | --- a/lib/spawn.c 473 | +++ b/lib/spawn.c 474 | @@ -11,6 +11,19 @@ static int map_segment(envid_t child, uintptr_t va, size_t memsz, 475 | int fd, size_t filesz, off_t fileoffset, int perm); 476 | static int copy_shared_pages(envid_t child); 477 | 478 | +/**** 479 | +Think about what you would have to do in order to implement exec in user space, 480 | +and be sure you understand why it is harder. 481 | + 482 | +Well, if we're going to implement some routine like 'exec', one big prerequsite 483 | +is that we need to make sure we run under current process's context. Then it needs 484 | +to find someway to replace itself with new binary image file, which means we have 485 | +to be in the kernel mode to do that, otherwise how to maintain the correctness 486 | +during the procedure of replacing? Then we need to of course update our page tables 487 | +with newly added physical memory pages for new image, and also be sure to free the 488 | +old image's physical memory occupation 489 | +*****/ 490 | + 491 | // Spawn a child process from a program image loaded from the file system. 492 | // prog: the pathname of the program to run. 493 | // argv: pointer to null-terminated array of pointers to strings, 494 | @@ -270,7 +283,7 @@ map_segment(envid_t child, uintptr_t va, size_t memsz, 495 | //cprintf("map_segment %x+%x\n", va, memsz); 496 | 497 | if ((i = PGOFF(va))) { 498 | - va -= i; 499 | + va -= i; // ROUNDDOWN 500 | memsz += i; 501 | filesz += i; 502 | fileoffset -= i; 503 | @@ -302,6 +315,20 @@ static int 504 | copy_shared_pages(envid_t child) 505 | { 506 | // LAB 5: Your code here. 507 | + int r; 508 | + void *addr = 0; 509 | + for (uint32_t i = 0; i < UTOP / PGSIZE; i ++) 510 | + { 511 | + addr = (void *)(i * PGSIZE); 512 | + if ((uvpd[PDX(addr)] & PTE_P) && (uvpt[i] & PTE_P)) 513 | + { 514 | + if (uvpt[i] & PTE_SHARE) 515 | + { 516 | + if ((r = sys_page_map(0, addr, child, addr, PTE_SYSCALL & uvpt[i])) != 0) 517 | + panic("copy_shared_pages: sys_page_map data: %e", r); 518 | + } 519 | + } 520 | + } 521 | return 0; 522 | } 523 | 524 | diff --git a/user/sh.c b/user/sh.c 525 | index 26f501a..db46d34 100644 526 | --- a/user/sh.c 527 | +++ b/user/sh.c 528 | @@ -55,7 +55,18 @@ again: 529 | // then close the original 'fd'. 530 | 531 | // LAB 5: Your code here. 532 | - panic("< redirection not implemented"); 533 | + // panic("< redirection not implemented"); 534 | + if ((fd = open(t, O_RDONLY)) < 0) 535 | + { 536 | + cprintf("open %s for read: %e", t, fd); 537 | + exit(); 538 | + } 539 | + 540 | + if (fd != 0) 541 | + { 542 | + dup(fd, 0); 543 | + close(fd); 544 | + } 545 | break; 546 | 547 | case '>': // Output redirection 548 | -- 549 | 2.20.1 550 | 551 | -------------------------------------------------------------------------------- /labs/0001-lab3-user-env-finished.patch: -------------------------------------------------------------------------------- 1 | From 3a2d75dc497203e64f67e7561df99c73be7a9cfd Mon Sep 17 00:00:00 2001 2 | From: Aaron 3 | Date: Thu, 18 Jun 2020 18:47:15 +0200 4 | Subject: [PATCH] lab3 user env finished 5 | 6 | challenge finished: 7 | 1. breakpoint single-stepping debugging 8 | 2. implement sysenter for faster system call 9 | 3. use GAS assembly altmacro for tidy-up of trapentry.S and trap.c 10 | --- 11 | inc/x86.h | 7 ++++ 12 | kern/env.c | 75 +++++++++++++++++++++++++++++++++-- 13 | kern/kdebug.c | 6 +++ 14 | kern/monitor.c | 46 +++++++++++++++++++--- 15 | kern/monitor.h | 1 + 16 | kern/pmap.c | 21 ++++++++++ 17 | kern/syscall.c | 18 ++++++++- 18 | kern/trap.c | 47 ++++++++++++++++++++++ 19 | kern/trapentry.S | 100 ++++++++++++++++++++++++++++++++--------------- 20 | lib/libmain.c | 3 +- 21 | lib/syscall.c | 38 ++++++++++++++++-- 22 | user/testbss.c | 2 +- 23 | 12 files changed, 316 insertions(+), 48 deletions(-) 24 | 25 | diff --git a/inc/x86.h b/inc/x86.h 26 | index cc15ff4..0e679c5 100644 27 | --- a/inc/x86.h 28 | +++ b/inc/x86.h 29 | @@ -248,6 +248,13 @@ read_tsc(void) 30 | return tsc; 31 | } 32 | 33 | +static inline void 34 | +wrmsr(uint32_t msr, uint32_t eax, uint32_t edx) 35 | +{ 36 | + asm volatile("wrmsr" 37 | + : :"c" (msr), "a" (eax), "d" (edx)); 38 | +} 39 | + 40 | static inline uint32_t 41 | xchg(volatile uint32_t *addr, uint32_t newval) 42 | { 43 | diff --git a/kern/env.c b/kern/env.c 44 | index db2fda9..08e9523 100644 45 | --- a/kern/env.c 46 | +++ b/kern/env.c 47 | @@ -116,6 +116,12 @@ env_init(void) 48 | { 49 | // Set up envs array 50 | // LAB 3: Your code here. 51 | + for (int i = NENV - 1; i >= 0; i--) 52 | + { 53 | + envs[i].env_id = 0; 54 | + envs[i].env_link = env_free_list; 55 | + env_free_list = envs + i; 56 | + } 57 | 58 | // Per-CPU part of the initialization 59 | env_init_percpu(); 60 | @@ -179,9 +185,18 @@ env_setup_vm(struct Env *e) 61 | // - The functions in kern/pmap.h are handy. 62 | 63 | // LAB 3: Your code here. 64 | - 65 | + p->pp_ref++; 66 | + e->env_pgdir = (pte_t *)page2kva(p); 67 | + // we can copy kern part of PDEs from kern_pgdir 68 | + // no need to allocate more physical memory and map again 69 | + for (int start = PDX(UTOP); start < 1024; start ++) 70 | + { 71 | + e->env_pgdir[start] = kern_pgdir[start]; 72 | + } 73 | // UVPT maps the env's own page table read-only. 74 | // Permissions: kernel R, user R 75 | + // need to update, if we keep the copy from kern_pgdir, 76 | + // it will point to kern_pgdir itself, not env_pgdir 77 | e->env_pgdir[PDX(UVPT)] = PADDR(e->env_pgdir) | PTE_P | PTE_U; 78 | 79 | return 0; 80 | @@ -267,6 +282,21 @@ region_alloc(struct Env *e, void *va, size_t len) 81 | // 'va' and 'len' values that are not page-aligned. 82 | // You should round va down, and round (va + len) up. 83 | // (Watch out for corner-cases!) 84 | + 85 | + // page that contains va & va + len 86 | + uint32_t start = (uint32_t)ROUNDDOWN(va, PGSIZE); 87 | + uint32_t end = (uint32_t)ROUNDUP(va + len, PGSIZE); 88 | + 89 | + for (uint32_t va = start; va < end; va += PGSIZE) 90 | + { 91 | + pte_t *pgtbl = pgdir_walk(e->env_pgdir, (const void *)va, 1); 92 | + struct PageInfo *pg_info = NULL; 93 | + if (!(*pgtbl & PTE_P)) // for already-mapped page, we don't init it in any way 94 | + pg_info = page_alloc(ALLOC_ZERO); 95 | + if (pg_info == NULL) 96 | + panic("page_alloc: %e", -E_NO_MEM); 97 | + *pgtbl = page2pa(pg_info) | PTE_P | PTE_U | PTE_W; 98 | + } 99 | } 100 | 101 | // 102 | @@ -323,11 +353,34 @@ load_icode(struct Env *e, uint8_t *binary) 103 | // What? (See env_run() and env_pop_tf() below.) 104 | 105 | // LAB 3: Your code here. 106 | - 107 | + struct Elf *elf_header = (struct Elf *)binary; 108 | + if (elf_header->e_magic != ELF_MAGIC) 109 | + panic("invalid elf file"); 110 | + 111 | + struct Proghdr *ph, *eph; 112 | + ph = (struct Proghdr *)((uint8_t *)elf_header + elf_header->e_phoff); 113 | + eph = ph + elf_header->e_phnum; 114 | + 115 | + for (; ph < eph; ph++) 116 | + { 117 | + if (ph->p_type == ELF_PROG_LOAD && ph->p_filesz <= ph->p_memsz) 118 | + { 119 | + region_alloc(e, (void *)ph->p_va, ph->p_memsz); 120 | + lcr3(PADDR(e->env_pgdir)); 121 | + memmove((void *)ph->p_va, (void*)(binary + ph->p_offset), ph->p_filesz); 122 | + lcr3(PADDR(kern_pgdir)); 123 | + } 124 | + } 125 | + e->env_tf.tf_eip = elf_header->e_entry; 126 | // Now map one page for the program's initial stack 127 | // at virtual address USTACKTOP - PGSIZE. 128 | 129 | // LAB 3: Your code here. 130 | + struct PageInfo *stack_page = page_alloc(ALLOC_ZERO); 131 | + if (page_insert(e->env_pgdir, stack_page, (void *)(USTACKTOP - PGSIZE), PTE_U | PTE_W) != 0) 132 | + { 133 | + panic("alloc user stack"); 134 | + } 135 | } 136 | 137 | // 138 | @@ -341,6 +394,11 @@ void 139 | env_create(uint8_t *binary, enum EnvType type) 140 | { 141 | // LAB 3: Your code here. 142 | + struct Env *new_env; 143 | + if (env_alloc(&new_env, 0) != 0) 144 | + panic("env_alloc"); 145 | + load_icode(new_env, binary); 146 | + new_env->env_type = type; 147 | } 148 | 149 | // 150 | @@ -457,7 +515,18 @@ env_run(struct Env *e) 151 | // e->env_tf to sensible values. 152 | 153 | // LAB 3: Your code here. 154 | + if (curenv != NULL && curenv != e) 155 | + { 156 | + if (curenv->env_status == ENV_RUNNING) 157 | + curenv->env_status = ENV_RUNNABLE; 158 | + } 159 | + curenv = e; 160 | + curenv->env_status = ENV_RUNNING; 161 | + curenv->env_runs ++; 162 | + 163 | + lcr3(PADDR(curenv->env_pgdir)); 164 | + env_pop_tf(&(curenv->env_tf)); 165 | 166 | - panic("env_run not yet implemented"); 167 | + // panic("env_run not yet implemented"); 168 | } 169 | 170 | diff --git a/kern/kdebug.c b/kern/kdebug.c 171 | index 31177c8..526656f 100644 172 | --- a/kern/kdebug.c 173 | +++ b/kern/kdebug.c 174 | @@ -142,6 +142,8 @@ debuginfo_eip(uintptr_t addr, struct Eipdebuginfo *info) 175 | // Make sure this memory is valid. 176 | // Return -1 if it is not. Hint: Call user_mem_check. 177 | // LAB 3: Your code here. 178 | + if (user_mem_check(curenv, usd, sizeof(struct UserStabData), PTE_P | PTE_U) < 0) 179 | + return -1; 180 | 181 | stabs = usd->stabs; 182 | stab_end = usd->stab_end; 183 | @@ -150,6 +152,10 @@ debuginfo_eip(uintptr_t addr, struct Eipdebuginfo *info) 184 | 185 | // Make sure the STABS and string table memory is valid. 186 | // LAB 3: Your code here. 187 | + if (user_mem_check(curenv, stabs, sizeof(struct Stab) * (stab_end - stabs), PTE_P | PTE_U) < 0) 188 | + return -1; 189 | + if (user_mem_check(curenv, stabstr, stabstr_end - stabstr, PTE_P | PTE_U) < 0) 190 | + return -1; 191 | } 192 | 193 | // String table validity checks 194 | diff --git a/kern/monitor.c b/kern/monitor.c 195 | index 4959e59..ac6a91e 100644 196 | --- a/kern/monitor.c 197 | +++ b/kern/monitor.c 198 | @@ -27,7 +27,8 @@ static struct Command commands[] = { 199 | { "help", "Display this list of commands", mon_help }, 200 | { "kerninfo", "Display information about the kernel", mon_kerninfo }, 201 | { "backtrace", "Display backtrace to current function call", mon_backtrace}, 202 | - { "showmappings", "Display memory mappings in current active address space", mon_showmappings} 203 | + { "showmappings", "Display memory mappings in current active address space", mon_showmappings}, 204 | + { "debug", "Debug purpose", mon_debug} 205 | }; 206 | 207 | /***** Implementations of basic kernel monitor commands *****/ 208 | @@ -109,16 +110,16 @@ int mon_showmappings(int argc, char **argv, struct Trapframe *tf) 209 | 210 | size_t range = (high_addr - low_addr) / PGSIZE; 211 | 212 | - for (int i = 0; i <= range; i++) 213 | + for (int i = 0; i < range; i++) 214 | { 215 | pte_t *pgtbl_entry = NULL; 216 | const void *vir_addr = (const void *)(low_addr + i * PGSIZE); 217 | - if (!(pgtbl_entry = pgdir_walk(entry_pgdir, vir_addr, 0))) 218 | + if (!(pgtbl_entry = pgdir_walk(entry_pgdir, vir_addr, 0)) || !(*pgtbl_entry & PTE_P)) 219 | { 220 | - if (!(pgtbl_entry = pgdir_walk(kern_pgdir, vir_addr, 0))) 221 | + if (!(pgtbl_entry = pgdir_walk(kern_pgdir, vir_addr, 0)) || !(*pgtbl_entry & PTE_P)) 222 | { 223 | - cprintf("Invalid mappings, perhaps accessing USER level, not supported yet\n"); 224 | - return 1; 225 | + cprintf("Invalid mappings at 0x%08x, perhaps accessing USER level, not supported yet\n", vir_addr); 226 | + continue; 227 | } 228 | } 229 | cprintf("\tVirtual address 0x%08x mapped to physical address 0x%08x\n", vir_addr, PTE_ADDR(*pgtbl_entry) + PGOFF(vir_addr)); 230 | @@ -127,6 +128,39 @@ int mon_showmappings(int argc, char **argv, struct Trapframe *tf) 231 | return 0; 232 | } 233 | 234 | +int mon_debug(int argc, char **argv, struct Trapframe *tf) 235 | +{ 236 | + if (tf == NULL) 237 | + { 238 | + cprintf("No user process is running!\n"); 239 | + return 0; 240 | + } 241 | + 242 | + const char *helpmsg = "usage debug, subcommand 'si' for single-step debug, 'continue' for continue till end or next breakpoint\n"; 243 | + 244 | + if (argc != 2) 245 | + { 246 | + cprintf("%s", helpmsg); 247 | + return 0; 248 | + } 249 | + 250 | + extern void env_pop_tf(struct Trapframe *tf); 251 | + if (strncmp("si", argv[1], 10) == 0) 252 | + { 253 | + env_pop_tf(tf); 254 | + } 255 | + if (strncmp("continue", argv[1], 10) == 0) 256 | + { 257 | + // clear TF flag to disable IRQ1 258 | + tf->tf_eflags &= ~0x100; 259 | + env_pop_tf(tf); 260 | + } 261 | + 262 | + cprintf("%s", helpmsg); 263 | + 264 | + return 0; 265 | +} 266 | + 267 | /***** Kernel monitor command interpreter *****/ 268 | 269 | #define WHITESPACE "\t\r\n " 270 | diff --git a/kern/monitor.h b/kern/monitor.h 271 | index 35d2793..303cbdc 100644 272 | --- a/kern/monitor.h 273 | +++ b/kern/monitor.h 274 | @@ -16,5 +16,6 @@ int mon_help(int argc, char **argv, struct Trapframe *tf); 275 | int mon_kerninfo(int argc, char **argv, struct Trapframe *tf); 276 | int mon_backtrace(int argc, char **argv, struct Trapframe *tf); 277 | int mon_showmappings(int argc, char **argv, struct Trapframe *tf); 278 | +int mon_debug(int argc, char **argv, struct Trapframe *tf); 279 | 280 | #endif // !JOS_KERN_MONITOR_H 281 | diff --git a/kern/pmap.c b/kern/pmap.c 282 | index 9b6334f..5d47498 100644 283 | --- a/kern/pmap.c 284 | +++ b/kern/pmap.c 285 | @@ -176,6 +176,9 @@ mem_init(void) 286 | ////////////////////////////////////////////////////////////////////// 287 | // Make 'envs' point to an array of size 'NENV' of 'struct Env'. 288 | // LAB 3: Your code here. 289 | + envs = (struct Env *) boot_alloc(NENV * sizeof(struct Env)); 290 | + memset(envs, 0, NENV * sizeof(struct Env)); 291 | + cprintf("envs are located starting from 0x%08x\n", PADDR(envs)); 292 | 293 | ////////////////////////////////////////////////////////////////////// 294 | // Now that we've allocated the initial kernel data structures, we set 295 | @@ -208,6 +211,7 @@ mem_init(void) 296 | // - the new image at UENVS -- kernel R, user R 297 | // - envs itself -- kernel RW, user NONE 298 | // LAB 3: Your code here. 299 | + boot_map_region(kern_pgdir, UENVS, ROUNDUP(NENV * sizeof(struct Env), PGSIZE), PADDR(envs), PTE_U | PTE_W); 300 | 301 | ////////////////////////////////////////////////////////////////////// 302 | // Use the physical memory that 'bootstack' refers to as the kernel 303 | @@ -632,6 +636,23 @@ int 304 | user_mem_check(struct Env *env, const void *va, size_t len, int perm) 305 | { 306 | // LAB 3: Your code here. 307 | + uint32_t start = (uint32_t)ROUNDDOWN(va, PGSIZE); 308 | + uint32_t end = ROUNDUP(((uint32_t)va + len), PGSIZE); 309 | + for (uint32_t pg = start; pg < end; pg += PGSIZE) 310 | + { 311 | + if (pg >= ULIM) 312 | + { 313 | + user_mem_check_addr = pg == start ? (uintptr_t)va : pg; 314 | + return -E_FAULT; 315 | + } 316 | + 317 | + pte_t *pgtbl_entry = pgdir_walk(env->env_pgdir, (const void *)pg, 0); 318 | + if (!pgtbl_entry || !(*pgtbl_entry & perm)) 319 | + { 320 | + user_mem_check_addr = pg == start ? (uintptr_t)va : pg; 321 | + return -E_FAULT; 322 | + } 323 | + } 324 | 325 | return 0; 326 | } 327 | diff --git a/kern/syscall.c b/kern/syscall.c 328 | index 414d489..96486a1 100644 329 | --- a/kern/syscall.c 330 | +++ b/kern/syscall.c 331 | @@ -21,6 +21,12 @@ sys_cputs(const char *s, size_t len) 332 | // Destroy the environment if not. 333 | 334 | // LAB 3: Your code here. 335 | + user_mem_assert(curenv, s, len, PTE_P); 336 | + cprintf("Will write from address: 0x%08x to address: 0x%08x\n", s, s + len); 337 | + if ((uint32_t)ROUNDUP(s, PGSIZE) != USTACKTOP || (uint32_t)ROUNDUP(s + len, PGSIZE) != USTACKTOP) 338 | + { 339 | + env_destroy(curenv); 340 | + } 341 | 342 | // Print the string supplied by the user. 343 | cprintf("%.*s", len, s); 344 | @@ -70,9 +76,19 @@ syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, 345 | // Return any appropriate return value. 346 | // LAB 3: Your code here. 347 | 348 | - panic("syscall not implemented"); 349 | + // panic("syscall not implemented"); 350 | 351 | switch (syscallno) { 352 | + case SYS_cputs: 353 | + sys_cputs((const char *)a1, a2); 354 | + return 0; 355 | + case SYS_cgetc: 356 | + return sys_cgetc(); 357 | + case SYS_getenvid: 358 | + return sys_getenvid(); 359 | + case SYS_env_destroy: 360 | + return sys_env_destroy(a1); 361 | + case NSYSCALLS: 362 | default: 363 | return -E_INVAL; 364 | } 365 | diff --git a/kern/trap.c b/kern/trap.c 366 | index e27b556..4b7f6c9 100644 367 | --- a/kern/trap.c 368 | +++ b/kern/trap.c 369 | @@ -25,6 +25,9 @@ struct Pseudodesc idt_pd = { 370 | sizeof(idt) - 1, (uint32_t) idt 371 | }; 372 | 373 | +typedef void (*trap_handler)(void); 374 | + 375 | +extern trap_handler default_handlers[]; 376 | 377 | static const char *trapname(int trapno) 378 | { 379 | @@ -63,8 +66,29 @@ void 380 | trap_init(void) 381 | { 382 | extern struct Segdesc gdt[]; 383 | + extern void sysenter_handler(); 384 | 385 | // LAB 3: Your code here. 386 | + wrmsr(0x174, GD_KT, 0); // set (CPL = 0) CS & SS 387 | + wrmsr(0x176, (uint32_t)sysenter_handler, 0); // the sysenter handler address 388 | + wrmsr(0x175, KSTACKTOP, 0); // the stack where we drop in when trapped into kernel 389 | + 390 | + int vector = 0; 391 | + for (; vector < 32; vector++) 392 | + { 393 | + // 0-31 for TRAPs 394 | + if (vector == T_BRKPT) 395 | + { 396 | + SETGATE(idt[vector], 1, GD_KT, default_handlers[vector], gdt[GD_UT >> 3].sd_dpl); 397 | + continue; 398 | + } 399 | + SETGATE(idt[vector], 1, GD_KT, default_handlers[vector], gdt[GD_KT >> 3].sd_dpl); 400 | + } 401 | + for (; vector < 256; vector++) 402 | + { 403 | + // 32 - 255 for IRQs (user defined) 404 | + SETGATE(idt[vector], 0, GD_KT, default_handlers[vector], gdt[GD_UT >> 3].sd_dpl); 405 | + } 406 | 407 | // Per-CPU setup 408 | trap_init_percpu(); 409 | @@ -144,6 +168,24 @@ trap_dispatch(struct Trapframe *tf) 410 | { 411 | // Handle processor exceptions. 412 | // LAB 3: Your code here. 413 | + if (tf->tf_trapno == T_PGFLT) 414 | + page_fault_handler(tf); 415 | + if (tf->tf_trapno == T_BRKPT) 416 | + { 417 | + // enable single-step mode for debugging, 418 | + // a debug exception will be generated 419 | + // after each instruction 420 | + tf->tf_eflags |= 0x100; 421 | + monitor(tf); 422 | + } 423 | + if (tf->tf_trapno == T_DEBUG) 424 | + monitor(tf); 425 | + if (tf->tf_trapno == T_SYSCALL) 426 | + { 427 | + tf->tf_regs.reg_eax = syscall(tf->tf_regs.reg_eax, tf->tf_regs.reg_edx, tf->tf_regs.reg_ecx, 428 | + tf->tf_regs.reg_ebx, tf->tf_regs.reg_edi, tf->tf_regs.reg_esi); 429 | + return; 430 | + } 431 | 432 | // Unexpected trap: The user process or the kernel has a bug. 433 | print_trapframe(tf); 434 | @@ -205,6 +247,11 @@ page_fault_handler(struct Trapframe *tf) 435 | // Handle kernel-mode page faults. 436 | 437 | // LAB 3: Your code here. 438 | + if (tf->tf_cs == GD_KT) 439 | + { 440 | + print_trapframe(tf); 441 | + panic("page fault happens in kernel mode"); 442 | + } 443 | 444 | // We've already handled kernel-mode exceptions, so if we get here, 445 | // the page fault happened in user mode. 446 | diff --git a/kern/trapentry.S b/kern/trapentry.S 447 | index 22fc640..c6b7997 100644 448 | --- a/kern/trapentry.S 449 | +++ b/kern/trapentry.S 450 | @@ -10,46 +10,82 @@ 451 | # exceptions/interrupts 452 | ################################################################### 453 | 454 | -/* TRAPHANDLER defines a globally-visible function for handling a trap. 455 | - * It pushes a trap number onto the stack, then jumps to _alltraps. 456 | - * Use TRAPHANDLER for traps where the CPU automatically pushes an error code. 457 | - * 458 | - * You shouldn't call a TRAPHANDLER function from C, but you may 459 | - * need to _declare_ one in C (for instance, to get a function pointer 460 | - * during IDT setup). You can declare the function with 461 | - * void NAME(); 462 | - * where NAME is the argument passed to TRAPHANDLER. 463 | - */ 464 | -#define TRAPHANDLER(name, num) \ 465 | - .globl name; /* define global symbol for 'name' */ \ 466 | - .type name, @function; /* symbol type is function */ \ 467 | - .align 2; /* align function definition */ \ 468 | - name: /* function starts here */ \ 469 | - pushl $(num); \ 470 | - jmp _alltraps 471 | - 472 | -/* Use TRAPHANDLER_NOEC for traps where the CPU doesn't push an error code. 473 | - * It pushes a 0 in place of the error code, so the trap frame has the same 474 | - * format in either case. 475 | - */ 476 | -#define TRAPHANDLER_NOEC(name, num) \ 477 | - .globl name; \ 478 | - .type name, @function; \ 479 | - .align 2; \ 480 | - name: \ 481 | - pushl $0; \ 482 | - pushl $(num); \ 483 | - jmp _alltraps 484 | +# https://stackoverflow.com/questions/48159005/for-loop-macro-preprocessor-for-assembly-files-in-gcc/48170044#48170044 485 | +.altmacro 486 | 487 | -.text 488 | +.macro irq_stubX trapno, noec 489 | + irq\trapno: 490 | +.if noec == $1 491 | + pushl $0 492 | +.endif 493 | + pushl $\trapno 494 | + jmp _alltraps 495 | +.endm 496 | + 497 | +.macro irq_insertX trapno, noec 498 | + .section .text 499 | + .align 2; 500 | + irq_stubX \trapno, \noec 501 | + .section .data # construct a function poiner array 502 | + .long irq\trapno 503 | +.endm 504 | 505 | /* 506 | * Lab 3: Your code here for generating entry points for the different traps. 507 | */ 508 | +.section .data 509 | +.globl default_handlers; 510 | +default_handlers: 511 | +.set i,0 512 | +.rept 256 513 | +.if i >= 18 || i <= 7 || i == 15 || i == 16 514 | + irq_insertX %i, $1 515 | +.else 516 | + irq_insertX %i, $0 517 | +.endif 518 | + .set i, i+1 519 | +.endr 520 | 521 | - 522 | +.text 523 | +.globl sysenter_handler 524 | +.type sysenter_handler, @function 525 | +.align 2 526 | +# bypass trap to reach syscall directly 527 | +sysenter_handler: 528 | +pushl %edi 529 | +pushl %ebx 530 | +pushl %ecx 531 | +pushl %edx 532 | +pushl %eax 533 | +# only support 4 syscall arguments 534 | +call syscall 535 | +# movl $0x174, %ecx 536 | +# movl $0, %edx 537 | +# movl $(GD_UT), %eax /* no need, because of continuity, GD_UT will be found by adding 16(0x10) to GD_KT */ 538 | +# wrmsr 539 | +movl %esi, %edx 540 | +movl %ebp, %ecx 541 | +sysexit 542 | 543 | /* 544 | * Lab 3: Your code here for _alltraps 545 | */ 546 | +.globl _alltraps; 547 | +_alltraps: 548 | +# push all rest registers on stack 549 | +pushl %ds 550 | +pushl %es 551 | +pushal 552 | + 553 | +# Set up data segments. 554 | +movw $(GD_KD), %ax 555 | +movw %ax, %ds 556 | +movw %ax, %es 557 | + 558 | +# Call trap(tf), where tf=%esp 559 | +pushl %esp 560 | +call trap 561 | + 562 | +# trap needs return back to user space 563 | +# pop registers and return from interrupt (iret) 564 | 565 | diff --git a/lib/libmain.c b/lib/libmain.c 566 | index 8a14b29..a9ed5a8 100644 567 | --- a/lib/libmain.c 568 | +++ b/lib/libmain.c 569 | @@ -13,7 +13,8 @@ libmain(int argc, char **argv) 570 | { 571 | // set thisenv to point at our Env structure in envs[]. 572 | // LAB 3: Your code here. 573 | - thisenv = 0; 574 | + envid_t envid = sys_getenvid(); 575 | + thisenv = &envs[ENVX(envid)]; 576 | 577 | // save the name of the program so that panic() can use it 578 | if (argc > 0) 579 | diff --git a/lib/syscall.c b/lib/syscall.c 580 | index 8d28dda..b810220 100644 581 | --- a/lib/syscall.c 582 | +++ b/lib/syscall.c 583 | @@ -37,27 +37,57 @@ syscall(int num, int check, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, 584 | return ret; 585 | } 586 | 587 | +static inline int32_t 588 | +sysenter(int num, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4) 589 | +{ 590 | + // fast system call: pass system call number in AX, 591 | + // up to 4 parameters in DX, CX, BX, DI 592 | + // 593 | + // Interrupt kernel with MSR (CPL = 0). 594 | + // 595 | + // https://reverseengineering.stackexchange.com/questions/2869/how-to-use-sysenter-under-linux 596 | + 597 | + int32_t ret; 598 | + asm volatile( 599 | + "pushl %%ebp\n\t" 600 | + "movl %%esp, %%ebp\n\t" 601 | + "leal sysenter_ret%=, %%esi\n\t" 602 | + "sysenter\n\t" 603 | + "sysenter_ret%=:" 604 | + "popl %%ebp\n\t" 605 | + : "=a" (ret) : 606 | + "a" (num), 607 | + "d" (a1), 608 | + "c" (a2), 609 | + "b" (a3), 610 | + "D" (a4) 611 | + : "%esi", "memory", "cc"); 612 | + 613 | + return ret; 614 | +} 615 | + 616 | void 617 | sys_cputs(const char *s, size_t len) 618 | { 619 | - syscall(SYS_cputs, 0, (uint32_t)s, len, 0, 0, 0); 620 | + sysenter(SYS_cputs, (uint32_t)s, len, 0, 0); 621 | } 622 | 623 | int 624 | sys_cgetc(void) 625 | { 626 | - return syscall(SYS_cgetc, 0, 0, 0, 0, 0, 0); 627 | + return sysenter(SYS_cgetc, 0, 0, 0, 0); 628 | } 629 | 630 | int 631 | sys_env_destroy(envid_t envid) 632 | { 633 | - return syscall(SYS_env_destroy, 1, envid, 0, 0, 0, 0); 634 | + return sysenter(SYS_env_destroy, envid, 0, 0, 0); 635 | } 636 | 637 | envid_t 638 | sys_getenvid(void) 639 | { 640 | - return syscall(SYS_getenvid, 0, 0, 0, 0, 0, 0); 641 | + return sysenter(SYS_getenvid, 0, 0, 0, 0); 642 | + // return syscall(SYS_getenvid, 0, 0, 0, 0, 0, 0); 643 | } 644 | 645 | diff --git a/user/testbss.c b/user/testbss.c 646 | index 2f8ee8e..b81e21f 100644 647 | --- a/user/testbss.c 648 | +++ b/user/testbss.c 649 | @@ -14,7 +14,7 @@ umain(int argc, char **argv) 650 | cprintf("Making sure bss works right...\n"); 651 | for (i = 0; i < ARRAYSIZE; i++) 652 | if (bigarray[i] != 0) 653 | - panic("bigarray[%d] isn't cleared!\n", i); 654 | + panic("bigarray[%d] isn't cleared! 0x%08x\n", i, bigarray + i); 655 | for (i = 0; i < ARRAYSIZE; i++) 656 | bigarray[i] = i; 657 | for (i = 0; i < ARRAYSIZE; i++) 658 | -- 659 | 2.24.3 (Apple Git-128) 660 | 661 | -------------------------------------------------------------------------------- /labs/0001-start-with-lab-6-without-challenges.patch: -------------------------------------------------------------------------------- 1 | From f8fdf75679dcc3d5bb033e1fd580890d5dbbb5dc Mon Sep 17 00:00:00 2001 2 | From: Aaron 3 | Date: Thu, 1 Oct 2020 13:06:11 +0000 4 | Subject: [PATCH] start with lab 6(without challenges) 5 | 6 | Part A: 7 | Initialization and transmitting packets finished. 8 | 9 | Part B: 10 | Initlialized receiving(RX) 11 | Ready to receive 12 | Finished 13 | 14 | TX: 15 | root@9dfe7b025747:/usr/src/app/lab# tcpdump -XXnr qemu.pcap 16 | reading from file qemu.pcap, link-type EN10MB (Ethernet) 17 | 20:44:41.795394 [|ether] 18 | 0x0000: 6865 6c6c 6f2c 2077 6f72 6c64 0a hello,.world. 19 | 20:44:41.797814 [|ether] 20 | 0x0000: 6865 6c6c 6f2c 2077 6f72 6c64 0a hello,.world. 21 | 20:44:41.801232 [|ether] 22 | 0x0000: 6865 6c6c 6f2c 2077 6f72 6c64 0a hello,.world. 23 | 20:44:41.806917 [|ether] 24 | 0x0000: 6865 6c6c 6f2c 2077 6f72 6c64 0a hello,.world. 25 | 20:44:41.812211 [|ether] 26 | 0x0000: 6865 6c6c 6f2c 2077 6f72 6c64 0a hello,.world. 27 | 28 | RX: 29 | e1000: tx disabled 30 | e1000: RCTL: 127, mac_reg[RCTL] = 0x4008002 31 | FS is running 32 | FS can do I/O 33 | Device 1 presence: 1 34 | Sending ARP announcement... 35 | e1000: index 0: 0x5b000 : 900002a 0 36 | e1000: unicast match[0]: 52:54:00:12:34:56 37 | --- 38 | .github/workflows/c-cpp.yml | 29 +++++ 39 | inc/lib.h | 2 + 40 | inc/syscall.h | 2 + 41 | kern/e1000.c | 207 ++++++++++++++++++++++++++++++++++++ 42 | kern/e1000.h | 14 +++ 43 | kern/pci.c | 8 ++ 44 | kern/syscall.c | 23 +++- 45 | kern/trap.c | 5 +- 46 | lib/syscall.c | 12 +++ 47 | net/input.c | 16 +++ 48 | net/output.c | 23 ++++ 49 | user/httpd.c | 34 +++++- 50 | user/testtime.c | 4 +- 51 | 13 files changed, 370 insertions(+), 9 deletions(-) 52 | create mode 100644 .github/workflows/c-cpp.yml 53 | 54 | diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml 55 | new file mode 100644 56 | index 0000000..a28d492 57 | --- /dev/null 58 | +++ b/.github/workflows/c-cpp.yml 59 | @@ -0,0 +1,29 @@ 60 | +name: CI 61 | + 62 | +# Controls when the action will run. Triggers the workflow on push or pull request 63 | +# events but only for the lab4 branch 64 | +on: 65 | + push: 66 | + branches: [ lab6 ] 67 | + pull_request: 68 | + branches: [ lab6 ] 69 | + 70 | +# A workflow run is made up of one or more jobs that can run sequentially or in parallel 71 | +jobs: 72 | + # This workflow contains a single job called "build" 73 | + build: 74 | + # The type of runner that the job will run on 75 | + runs-on: ubuntu-latest 76 | + 77 | + # Steps represent a sequence of tasks that will be executed as part of the job 78 | + steps: 79 | + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 80 | + - uses: actions/checkout@v2 81 | + 82 | + # Runs a single command using the runners shell 83 | + - name: Install qemu 84 | + run: sudo apt-get update && sudo apt-get install -y --no-install-recommends qemu build-essential gcc-multilib 85 | + 86 | + # Runs a set of commands using the runners shell 87 | + - name: Run make grade 88 | + run: make grade 89 | \ No newline at end of file 90 | diff --git a/inc/lib.h b/inc/lib.h 91 | index 0850acb..9a67a8e 100644 92 | --- a/inc/lib.h 93 | +++ b/inc/lib.h 94 | @@ -61,6 +61,8 @@ int sys_ipc_try_send(envid_t to_env, uint32_t value, void *pg, int perm); 95 | int sys_ipc_recv(void *rcv_pg); 96 | unsigned int sys_time_msec(void); 97 | void sys_ide_sleep(void *chan, size_t nsecs, int op); 98 | +int sys_send(const void *buffer, size_t length); 99 | +int sys_recv(void *buffer, size_t length); 100 | 101 | // This must be inlined. Exercise for reader: why? 102 | static inline envid_t __attribute__((always_inline)) 103 | diff --git a/inc/syscall.h b/inc/syscall.h 104 | index 23c5278..fff0d91 100644 105 | --- a/inc/syscall.h 106 | +++ b/inc/syscall.h 107 | @@ -19,6 +19,8 @@ enum { 108 | SYS_ipc_recv, 109 | SYS_time_msec, 110 | SYS_ide_sleep, 111 | + SYS_send, 112 | + SYS_recv, 113 | NSYSCALLS 114 | }; 115 | 116 | diff --git a/kern/e1000.c b/kern/e1000.c 117 | index 192f317..2ee1867 100644 118 | --- a/kern/e1000.c 119 | +++ b/kern/e1000.c 120 | @@ -1,4 +1,211 @@ 121 | #include 122 | #include 123 | +#include 124 | +#include 125 | 126 | // LAB 6: Your driver code here 127 | + 128 | +// divided by 4 for use as uint32_t[] indices. 129 | + 130 | +// TX 131 | +#define E1000_TDBAL (0x03800/4) /* TX Descriptor Base Address Low - RW */ 132 | +#define E1000_TDBAH (0x03804/4) /* TX Descriptor Base Address High - RW */ 133 | +#define E1000_TDLEN (0x03808/4) /* TX Descriptor Length - RW */ 134 | +#define E1000_TDH (0x03810/4) /* TX Descriptor Head - RW */ 135 | +#define E1000_TDT (0x03818/4) /* TX Descripotr Tail - RW */ 136 | + 137 | +/* Transmit Control */ 138 | +#define E1000_TCTL (0x00400/4) /* TX Control - RW */ 139 | + #define E1000_TCTL_EN 0x00000002 /* enable tx */ 140 | + #define E1000_TCTL_PSP 0x00000008 /* pad short packets */ 141 | + #define E1000_TCTL_COLD 0x003ff000 /* collision distance */ 142 | + 143 | +#define E1000_TIPG (0x00410/4) /* TX Inter-packet gap -RW */ 144 | + 145 | +#define E1000_TXD_CMD_RS 0x08000000 /* Report Status */ 146 | +#define E1000_TXD_CMD_EOP 0x01000000 /* End of Packet */ 147 | +#define E1000_TXD_STAT_DD 0x00000001 /* Descriptor Done */ 148 | + 149 | +// RX 150 | +#define E1000_RDBAL (0x02800/4) /* RX Descriptor Base Address Low - RW */ 151 | +#define E1000_RDBAH (0x02804/4) /* RX Descriptor Base Address High - RW */ 152 | +#define E1000_RDLEN (0x02808/4) /* RX Descriptor Length - RW */ 153 | +#define E1000_RDH (0x02810/4) /* RX Descriptor Head - RW */ 154 | +#define E1000_RDT (0x02818/4) /* RX Descriptor Tail - RW */ 155 | + 156 | +#define E1000_MTA (0x05200/4) /* Multicast Table Array - RW Array */ 157 | +#define E1000_RAL (0x05400/4) /* Receive Address Low - RW Array */ 158 | +#define E1000_RAH (0x05404/4) /* Receive Address High - RW Array */ 159 | +#define E1000_RAH_AV 0x80000000 /* Receive descriptor valid */ 160 | +#define E1000_IMS (0x000D0/4) /* Interrupt Mask Set - RW */ 161 | + 162 | +#define E1000_RXD_STAT_DD 0x01 /* Descriptor Done */ 163 | +#define E1000_RXD_STAT_EOP 0x02 /* End of Packet */ 164 | + 165 | +/* Receive Control */ 166 | +#define E1000_RCTL (0x00100/4) /* RX Control - RW */ 167 | + #define E1000_RCTL_EN 0x00000002 /* enable */ 168 | + #define E1000_RCTL_SZ_2048 0x00000000 /* rx buffer size 2048 */ 169 | + // #define E1000_RCTL_LPE 0x00000020 /* long packet enable */ 170 | + #define E1000_RCTL_LBM_NO 0x00000000 /* no loopback mode */ 171 | + #define E1000_RCTL_SECRC 0x04000000 /* Strip Ethernet CRC */ 172 | + #define E1000_RCTL_BAM 0x00008000 /* broadcast enable */ 173 | + 174 | +#define MTU 1518 175 | + 176 | +// register descriptor layout, 64 descriptors, each with 16 byte 177 | +// 8 descriptors within a group, total 8 groups, 1KB ring buffer. 178 | +// needs to be 16-byte aligned 179 | +struct tx_desc 180 | +{ 181 | + uint64_t addr; // only fill low 32 bits 182 | + uint16_t length; 183 | + uint8_t cso; 184 | + uint8_t cmd; 185 | + uint8_t status; 186 | + uint8_t css; 187 | + uint16_t special; 188 | +} __attribute__ ((aligned (16))); 189 | + 190 | +/* Receive Descriptor */ 191 | +struct rx_desc { 192 | + uint64_t addr; /* Address of the descriptor's data buffer */ 193 | + uint16_t length; /* Length of data DMAed into data buffer */ 194 | + uint16_t csum; /* Packet checksum */ 195 | + uint8_t status; /* Descriptor status */ 196 | + uint8_t errors; /* Descriptor Errors */ 197 | + uint16_t special; 198 | +} __attribute__ ((aligned (16))); 199 | + 200 | +struct tx_desc tdesc[64]; // TDESC ring buffer, max 64 201 | +struct rx_desc rdesc[128]; // RDESC ring buffer, min 128 202 | + 203 | +static void init_tx() 204 | +{ 205 | + // allocating buffer space for each TDESC, a PAGE can hold 2 MTU 206 | + size_t tdesc_length = sizeof(tdesc) / sizeof(struct tx_desc); 207 | + for (int i = 0; i < tdesc_length; i+=2) 208 | + { 209 | + tdesc[i].addr = page2pa(page_alloc(ALLOC_ZERO)); 210 | + tdesc[i].cmd |= (E1000_TXD_CMD_RS >> 24); // set RS bit to report status of each descriptor 211 | + tdesc[i].status |= E1000_TXD_STAT_DD; // enable DD bit by default, clear when transmitting 212 | + 213 | + // assign another half to the second one 214 | + tdesc[i + 1].addr = tdesc[i].addr + PGSIZE / 2; 215 | + tdesc[i + 1].cmd |= (E1000_TXD_CMD_RS >> 24); 216 | + tdesc[i + 1].status |= E1000_TXD_STAT_DD; 217 | + } 218 | + 219 | + // perform initialization in Chapter 14.5, for TX 220 | + e1000_bar0[E1000_TDBAH] = 0; // high 32 bit is cleared 221 | + e1000_bar0[E1000_TDBAL] = PADDR(tdesc); // base address as descriptor array's physical address 222 | + e1000_bar0[E1000_TDLEN ] = sizeof(tdesc); // total 1024 bytes 223 | + e1000_bar0[E1000_TDH] = 0x0; // initialized as 0, Hardware is responsible to update this 224 | + e1000_bar0[E1000_TDT] = 0x0; // initialized as 0, Software is responsible to update this 225 | + e1000_bar0[E1000_TCTL] = ((0x40 << 12) & E1000_TCTL_COLD) | E1000_TCTL_PSP | E1000_TCTL_EN; // enable TX, full-duplex operation 226 | + e1000_bar0[E1000_TIPG] = 10; // IEEE 802.3 standard IPG 227 | + 228 | + /* self test with overflow and transmission*/ 229 | + // char *sample = "hello, world, let's transmit something\n"; 230 | + // for (int i = 0; i < 142; i++) 231 | + // e1000_transmit(sample, strlen(sample)); 232 | +} 233 | + 234 | +static void init_rx() 235 | +{ 236 | + size_t rdesc_length = sizeof(rdesc) / sizeof(struct rx_desc); 237 | + for (int i = 0; i < rdesc_length; i+=2) 238 | + { 239 | + rdesc[i].addr = page2pa(page_alloc(ALLOC_ZERO)); 240 | + rdesc[i + 1].addr = rdesc[i].addr + PGSIZE / 2; 241 | + } 242 | + 243 | + e1000_bar0[E1000_RDBAH] = 0; 244 | + e1000_bar0[E1000_RDBAL] = PADDR(rdesc); 245 | + e1000_bar0[E1000_RDLEN] = sizeof(rdesc); 246 | + e1000_bar0[E1000_RDH] = 0; 247 | + // To prevent the index registers to wrap around, the OS always leaves one RX descriptor unused 248 | + e1000_bar0[E1000_RDT] = rdesc_length - 1; 249 | + // MAC address of QEMU's ethernet card 250 | + e1000_bar0[E1000_RAL] = 0x12005452; 251 | + *(uint16_t *)(e1000_bar0 + E1000_RAH) = 0x5634; 252 | + e1000_bar0[E1000_RAH] |= E1000_RAH_AV; 253 | + // 128 bit of MTA init to 0b 254 | + e1000_bar0[E1000_MTA] = 0; 255 | + e1000_bar0[E1000_MTA + 1] = 0; 256 | + e1000_bar0[E1000_MTA + 2] = 0; 257 | + e1000_bar0[E1000_MTA + 3] = 0; 258 | + // not enable IRQ for now 259 | + e1000_bar0[E1000_IMS] = 0; 260 | + e1000_bar0[E1000_RCTL] = E1000_RCTL_EN | E1000_RCTL_SECRC | E1000_RCTL_SZ_2048 | E1000_RCTL_BAM; 261 | +} 262 | + 263 | +// wrap pci_func_enable function 264 | +int pci_func_attach(struct pci_func *pcif) 265 | +{ 266 | + pci_func_enable(pcif); 267 | + 268 | + init_tx(); 269 | + init_rx(); 270 | + 271 | + return 0; 272 | +} 273 | + 274 | +/* 275 | + * transmit a packet 276 | + * 277 | + * return number of bytes transmitted 278 | + * return 0 if this packet needs re-transmission 279 | +*/ 280 | +size_t e1000_transmit(const void *buffer, size_t size) 281 | +{ 282 | + uint32_t current = e1000_bar0[E1000_TDT]; 283 | + if (tdesc[current].status & E1000_TXD_STAT_DD) 284 | + { 285 | + tdesc[current].status &= ~E1000_TXD_STAT_DD; 286 | + void *addr = (void *)KADDR((uint32_t)tdesc[current].addr); 287 | + size_t length = MIN(size, MTU); 288 | + memcpy(addr, buffer, length); 289 | + tdesc[current].cmd |= (E1000_TXD_CMD_EOP >> 24); // End of Packet 290 | + tdesc[current].length = length; 291 | + // update tail pointer, inform network card 292 | + uint32_t next = current + 1; 293 | + e1000_bar0[E1000_TDT] = next % (sizeof(tdesc) / sizeof(struct tx_desc)); 294 | + return length; 295 | + } 296 | + else 297 | + { 298 | + // require for re-transmission 299 | + cprintf("lost packet 0x%x\n", buffer); 300 | + return 0; 301 | + } 302 | +} 303 | + 304 | +/* 305 | + * receive a packet 306 | + * 307 | + * return number of bytes from oldest buffer 308 | + * return 0 if this packet needs to wait 309 | + * return -1 if this packet is too long (more space) 310 | +*/ 311 | +size_t e1000_receive(void *buffer, size_t size) 312 | +{ 313 | + // to receive packet, it should start beyond RDT 314 | + uint32_t current = (e1000_bar0[E1000_RDT] + 1) % (sizeof(rdesc) / sizeof(struct rx_desc)); 315 | + if (!(rdesc[current].status & E1000_RXD_STAT_DD)) 316 | + { 317 | + // RTH == RTD: buffer is empty, stop receiving 318 | + return 0; 319 | + } 320 | + 321 | + uint32_t length = rdesc[current].length; 322 | + if (size < length) 323 | + return -1; 324 | + memcpy(buffer, (const void*)(KADDR((uint32_t)rdesc[current].addr)), length); 325 | + // zero the status byte in the descriptor to make it ready for reuse by hardware 326 | + rdesc[current].status &= ~E1000_RXD_STAT_DD; 327 | + // update tail, letting card know there is one more RDESC allocated for use 328 | + e1000_bar0[E1000_RDT] = current; 329 | + 330 | + return length; 331 | +} 332 | \ No newline at end of file 333 | diff --git a/kern/e1000.h b/kern/e1000.h 334 | index 8b5a513..f82e492 100644 335 | --- a/kern/e1000.h 336 | +++ b/kern/e1000.h 337 | @@ -1,3 +1,17 @@ 338 | #ifndef JOS_KERN_E1000_H 339 | #define JOS_KERN_E1000_H 340 | + 341 | +#include 342 | + 343 | +#define E1000_VENDOR_ID 0x8086 344 | +#define E1000_DEVICE_ID 0x100E 345 | + 346 | +#define E1000_STATUS (0x00008/4) /* Device Status - RO */ 347 | + 348 | +volatile uint32_t *e1000_bar0; // memory mapped E1000 device registers 349 | + 350 | +size_t e1000_transmit(const void *buffer, size_t size); 351 | +size_t e1000_receive(void *buffer, size_t size); 352 | + 353 | + 354 | #endif // SOL >= 6 355 | diff --git a/kern/pci.c b/kern/pci.c 356 | index 784e072..5ad5ae7 100644 357 | --- a/kern/pci.c 358 | +++ b/kern/pci.c 359 | @@ -4,6 +4,7 @@ 360 | #include 361 | #include 362 | #include 363 | +#include 364 | 365 | // Flag to do "lspci" at bootup 366 | static int pci_show_devs = 1; 367 | @@ -15,6 +16,7 @@ static uint32_t pci_conf1_data_ioport = 0x0cfc; 368 | 369 | // Forward declarations 370 | static int pci_bridge_attach(struct pci_func *pcif); 371 | +extern int pci_func_attach(struct pci_func *pcif); 372 | 373 | // PCI driver table 374 | struct pci_driver { 375 | @@ -31,6 +33,7 @@ struct pci_driver pci_attach_class[] = { 376 | // pci_attach_vendor matches the vendor ID and device ID of a PCI device. key1 377 | // and key2 should be the vendor ID and device ID respectively 378 | struct pci_driver pci_attach_vendor[] = { 379 | + { E1000_VENDOR_ID, E1000_DEVICE_ID, &pci_func_attach }, 380 | { 0, 0, 0 }, 381 | }; 382 | 383 | @@ -233,6 +236,9 @@ pci_func_enable(struct pci_func *f) 384 | f->reg_base[regnum] = base; 385 | f->reg_size[regnum] = size; 386 | 387 | + if (regnum == 0) 388 | + e1000_bar0 = mmio_map_region(base, size); 389 | + 390 | if (size && !base) 391 | cprintf("PCI device %02x:%02x.%d (%04x:%04x) " 392 | "may be misconfigured: " 393 | @@ -245,6 +251,8 @@ pci_func_enable(struct pci_func *f) 394 | cprintf("PCI function %02x:%02x.%d (%04x:%04x) enabled\n", 395 | f->bus->busno, f->dev, f->func, 396 | PCI_VENDOR(f->dev_id), PCI_PRODUCT(f->dev_id)); 397 | + cprintf("Device status for E1000 BAR 0 is 0x%x\n", 398 | + e1000_bar0[E1000_STATUS]); 399 | } 400 | 401 | int 402 | diff --git a/kern/syscall.c b/kern/syscall.c 403 | index e794918..3faf2b0 100644 404 | --- a/kern/syscall.c 405 | +++ b/kern/syscall.c 406 | @@ -12,6 +12,7 @@ 407 | #include 408 | #include 409 | #include 410 | +#include 411 | 412 | // Print a string to the system console. 413 | // The string is exactly 'len' characters long. 414 | @@ -391,7 +392,7 @@ static int 415 | sys_time_msec(void) 416 | { 417 | // LAB 6: Your code here. 418 | - panic("sys_time_msec not implemented"); 419 | + return time_msec(); 420 | } 421 | 422 | static void 423 | @@ -413,6 +414,20 @@ sys_ide_sleep(void *chan, size_t nsecs, int op) 424 | sched_yield(); 425 | } 426 | 427 | +static int 428 | +sys_send(const void *buffer, size_t length) 429 | +{ 430 | + user_mem_assert(curenv, buffer, length, PTE_U); 431 | + return (int)e1000_transmit(buffer, length); 432 | +} 433 | + 434 | +static int 435 | +sys_recv(void *buffer, size_t length) 436 | +{ 437 | + user_mem_assert(curenv, buffer, length, PTE_U); 438 | + return (int)e1000_receive(buffer, length); 439 | +} 440 | + 441 | // Dispatches to the correct kernel function, passing the arguments. 442 | int32_t 443 | syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5) 444 | @@ -453,8 +468,14 @@ syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, 445 | return sys_ipc_try_send(a1, a2, (void *)a3, a4); 446 | case SYS_ipc_recv: 447 | return sys_ipc_recv((void *)a1); 448 | + case SYS_time_msec: 449 | + return sys_time_msec(); 450 | case SYS_ide_sleep: 451 | sys_ide_sleep((void *)a1, a2, (int)a3); 452 | + case SYS_send: 453 | + return sys_send((const void*)a1, a2); 454 | + case SYS_recv: 455 | + return sys_recv((void *)a1, a2); 456 | case NSYSCALLS: 457 | default: 458 | return -E_INVAL; 459 | diff --git a/kern/trap.c b/kern/trap.c 460 | index 37014bf..ce81dd4 100644 461 | --- a/kern/trap.c 462 | +++ b/kern/trap.c 463 | @@ -242,6 +242,7 @@ trap_dispatch(struct Trapframe *tf) 464 | switch (tf->tf_trapno) 465 | { 466 | case IRQ_OFFSET + IRQ_TIMER: 467 | + time_tick(); 468 | lapic_eoi(); 469 | sched_yield(); 470 | break; 471 | @@ -296,10 +297,6 @@ trap_dispatch(struct Trapframe *tf) 472 | // triggered on every CPU. 473 | // LAB 6: Your code here. 474 | 475 | - 476 | - // Handle keyboard and serial interrupts. 477 | - // LAB 5: Your code here. 478 | - 479 | // Unexpected trap: The user process or the kernel has a bug. 480 | print_trapframe(tf); 481 | if (tf->tf_cs == GD_KT) 482 | diff --git a/lib/syscall.c b/lib/syscall.c 483 | index 8663cfd..1438f02 100644 484 | --- a/lib/syscall.c 485 | +++ b/lib/syscall.c 486 | @@ -170,3 +170,15 @@ sys_ide_sleep(void *chan, size_t nsecs, int op) 487 | // can't use sysenter, since we need to restore our flags from trapframe 488 | syscall(SYS_ide_sleep, 0, (uint32_t)chan, nsecs, (uint32_t)op, 0, 0); 489 | } 490 | + 491 | +int 492 | +sys_send(const void *buffer, size_t length) 493 | +{ 494 | + return sysenter(SYS_send, (uint32_t)buffer, length, 0, 0); 495 | +} 496 | + 497 | +int 498 | +sys_recv(void *buffer, size_t length) 499 | +{ 500 | + return sysenter(SYS_recv, (uint32_t)buffer, length, 0, 0); 501 | +} 502 | diff --git a/net/input.c b/net/input.c 503 | index 4e08f0f..8a11233 100644 504 | --- a/net/input.c 505 | +++ b/net/input.c 506 | @@ -13,4 +13,20 @@ input(envid_t ns_envid) 507 | // Hint: When you IPC a page to the network server, it will be 508 | // reading from it for a while, so don't immediately receive 509 | // another packet in to the same physical page. 510 | + int r; 511 | + // trigger a page fault in user space, otherwise we will fault in kernel mode.... 512 | + nsipcbuf.pkt.jp_data[0] = '\0'; 513 | + 514 | + while (1) 515 | + { 516 | + while ((r = sys_recv(nsipcbuf.pkt.jp_data, 1518)) == 0) sys_yield(); 517 | + if (r > 0) 518 | + { 519 | + nsipcbuf.pkt.jp_len = r; 520 | + ipc_send(ns_envid, NSREQ_INPUT, (void *)&nsipcbuf, PTE_P|PTE_W|PTE_U); 521 | + // we just enabled our parent env, yield a bit waiting for it to run 522 | + for (int i = 0; i < 10; i++) 523 | + sys_yield(); 524 | + } 525 | + } 526 | } 527 | diff --git a/net/output.c b/net/output.c 528 | index f577c4e..9d483fb 100644 529 | --- a/net/output.c 530 | +++ b/net/output.c 531 | @@ -10,4 +10,27 @@ output(envid_t ns_envid) 532 | // LAB 6: Your code here: 533 | // - read a packet from the network server 534 | // - send the packet to the device driver 535 | + int32_t reqno; 536 | + uint32_t whom; 537 | + int perm; 538 | + int r; 539 | + 540 | + while (1) 541 | + { 542 | + reqno = ipc_recv((int32_t *) &whom, (void *)&nsipcbuf, &perm); 543 | + char *ptr = nsipcbuf.pkt.jp_data; 544 | + size_t total = (size_t)nsipcbuf.pkt.jp_len; 545 | + if (reqno == NSREQ_OUTPUT) 546 | + { 547 | + retry: 548 | + while ((r = sys_send((const void*)ptr, total)) == 0) sys_yield(); 549 | + if (r < total) 550 | + { 551 | + ptr += r; 552 | + total -= r; 553 | + cprintf("Sent %d bytes, remaining %d bytes to send\n", r, total); 554 | + goto retry; 555 | + } 556 | + } 557 | + } 558 | } 559 | diff --git a/user/httpd.c b/user/httpd.c 560 | index ede43bf..e94700c 100644 561 | --- a/user/httpd.c 562 | +++ b/user/httpd.c 563 | @@ -77,7 +77,29 @@ static int 564 | send_data(struct http_request *req, int fd) 565 | { 566 | // LAB 6: Your code here. 567 | - panic("send_data not implemented"); 568 | + // panic("send_data not implemented"); 569 | + struct Stat stat; 570 | + int r; 571 | + if ((r = fstat(fd, &stat)) < 0) 572 | + panic("FSTAT: %e", r); 573 | + int length = stat.st_size; 574 | + char buffer[BUFFSIZE]; 575 | + int offset; 576 | + for (offset = 0; offset < length; offset += BUFFSIZE) 577 | + { 578 | + memset(buffer, 0, BUFFSIZE); 579 | + if ((r = readn(fd, buffer, BUFFSIZE)) < 0) 580 | + panic("readn, %e", r); 581 | + write(req->sock, buffer, BUFFSIZE); 582 | + } 583 | + 584 | + int remain = length - (offset - BUFFSIZE); 585 | + memset(buffer, 0, BUFFSIZE); 586 | + if ((r = readn(fd, buffer, remain)) < 0) 587 | + panic("readn, %e", r); 588 | + write(req->sock, buffer, remain); 589 | + 590 | + return r; 591 | } 592 | 593 | static int 594 | @@ -223,12 +245,18 @@ send_file(struct http_request *req) 595 | // set file_size to the size of the file 596 | 597 | // LAB 6: Your code here. 598 | - panic("send_file not implemented"); 599 | + // panic("send_file not implemented"); 600 | + struct Stat statbuf; 601 | + if ((r = stat(req->url, &statbuf)) < 0 || statbuf.st_isdir == 1) 602 | + return send_error(req, 404); 603 | + 604 | + if ((fd = open(req->url, O_RDONLY)) < 0) 605 | + goto end; 606 | 607 | if ((r = send_header(req, 200)) < 0) 608 | goto end; 609 | 610 | - if ((r = send_size(req, file_size)) < 0) 611 | + if ((r = send_size(req, statbuf.st_size)) < 0) 612 | goto end; 613 | 614 | if ((r = send_content_type(req)) < 0) 615 | diff --git a/user/testtime.c b/user/testtime.c 616 | index 68e350c..435e3d7 100644 617 | --- a/user/testtime.c 618 | +++ b/user/testtime.c 619 | @@ -22,7 +22,9 @@ umain(int argc, char **argv) 620 | int i; 621 | 622 | // Wait for the console to calm down 623 | - for (i = 0; i < 50; i++) 624 | + // since I enabled disk interrupt, so count down will not be 625 | + // continous sometimes, so increase the yielding times 626 | + for (i = 0; i < 1000; i++) 627 | sys_yield(); 628 | 629 | cprintf("starting count down: "); 630 | -- 631 | 2.24.3 (Apple Git-128) 632 | 633 | --------------------------------------------------------------------------------