diff options
author | Frans Kaashoek <[email protected]> | 2020-08-10 13:05:17 -0400 |
---|---|---|
committer | GitHub <[email protected]> | 2020-08-10 13:05:17 -0400 |
commit | c31d35d8031c88b7e1ea8657cc9806dfdd4c3ef9 (patch) | |
tree | a6c903e1c61c08f4cb87700c320752a737081dcb /kernel | |
parent | 90eb90b5e203299427c3fde8c996a48835fc93cf (diff) | |
parent | d8fe1773b26758c7c7b8f36724cd822555b33612 (diff) | |
download | xv6-labs-c31d35d8031c88b7e1ea8657cc9806dfdd4c3ef9.tar.gz xv6-labs-c31d35d8031c88b7e1ea8657cc9806dfdd4c3ef9.tar.bz2 xv6-labs-c31d35d8031c88b7e1ea8657cc9806dfdd4c3ef9.zip |
Merge branch 'riscv' into riscv
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/bio.c | 8 | ||||
-rw-r--r-- | kernel/buf.h | 1 | ||||
-rw-r--r-- | kernel/console.c | 10 | ||||
-rw-r--r-- | kernel/defs.h | 3 | ||||
-rw-r--r-- | kernel/exec.c | 7 | ||||
-rw-r--r-- | kernel/fcntl.h | 1 | ||||
-rw-r--r-- | kernel/fs.c | 12 | ||||
-rw-r--r-- | kernel/kernel.ld | 36 | ||||
-rw-r--r-- | kernel/memlayout.h | 2 | ||||
-rw-r--r-- | kernel/pipe.c | 6 | ||||
-rw-r--r-- | kernel/plic.c | 16 | ||||
-rw-r--r-- | kernel/proc.c | 32 | ||||
-rw-r--r-- | kernel/proc.h | 8 | ||||
-rw-r--r-- | kernel/riscv.h | 1 | ||||
-rw-r--r-- | kernel/spinlock.c | 16 | ||||
-rw-r--r-- | kernel/start.c | 1 | ||||
-rw-r--r-- | kernel/syscall.c | 18 | ||||
-rw-r--r-- | kernel/sysfile.c | 4 | ||||
-rw-r--r-- | kernel/trampoline.S | 12 | ||||
-rw-r--r-- | kernel/trap.c | 29 | ||||
-rw-r--r-- | kernel/uart.c | 121 | ||||
-rw-r--r-- | kernel/vm.c | 11 |
22 files changed, 227 insertions, 128 deletions
diff --git a/kernel/bio.c b/kernel/bio.c index a1074f2..60d91a6 100644 --- a/kernel/bio.c +++ b/kernel/bio.c @@ -28,7 +28,8 @@ struct { struct buf buf[NBUF]; // Linked list of all buffers, through prev/next. - // head.next is most recently used. + // Sorted by how recently the buffer was used. + // head.next is most recent, head.prev is least. struct buf head; } bcache; @@ -71,7 +72,8 @@ bget(uint dev, uint blockno) } } - // Not cached; recycle an unused buffer. + // Not cached. + // Recycle the least recently used (LRU) unused buffer. for(b = bcache.head.prev; b != &bcache.head; b = b->prev){ if(b->refcnt == 0) { b->dev = dev; @@ -110,7 +112,7 @@ bwrite(struct buf *b) } // Release a locked buffer. -// Move to the head of the MRU list. +// Move to the head of the most-recently-used list. void brelse(struct buf *b) { diff --git a/kernel/buf.h b/kernel/buf.h index 4a3a39d..4616e9e 100644 --- a/kernel/buf.h +++ b/kernel/buf.h @@ -7,7 +7,6 @@ struct buf { uint refcnt; struct buf *prev; // LRU cache list struct buf *next; - struct buf *qnext; // disk queue uchar data[BSIZE]; }; diff --git a/kernel/console.c b/kernel/console.c index 87a83ff..2b1ed3c 100644 --- a/kernel/console.c +++ b/kernel/console.c @@ -27,6 +27,8 @@ // // send one character to the uart. +// called by printf, and to echo input characters, +// but not from write(). // void consputc(int c) @@ -40,9 +42,9 @@ consputc(int c) if(c == BACKSPACE){ // if the user typed backspace, overwrite with a space. - uartputc('\b'); uartputc(' '); uartputc('\b'); + uartputc_sync('\b'); uartputc_sync(' '); uartputc_sync('\b'); } else { - uartputc(c); + uartputc_sync(c); } } @@ -70,11 +72,11 @@ consolewrite(int user_src, uint64 src, int n) char c; if(either_copyin(&c, user_src, src+i, 1) == -1) break; - consputc(c); + uartputc(c); } release(&cons.lock); - return n; + return i; } // diff --git a/kernel/defs.h b/kernel/defs.h index f893d28..4eedd89 100644 --- a/kernel/defs.h +++ b/kernel/defs.h @@ -52,6 +52,7 @@ struct inode* nameiparent(char*, char*); int readi(struct inode*, int, uint64, uint, uint); void stati(struct inode*, struct stat*); int writei(struct inode*, int, uint64, uint, uint); +void itrunc(struct inode*); // ramdisk.c void ramdiskinit(void); @@ -149,6 +150,7 @@ void usertrapret(void); void uartinit(void); void uartintr(void); void uartputc(int); +void uartputc_sync(int); int uartgetc(void); // vm.c @@ -173,7 +175,6 @@ int copyinstr(pagetable_t, char *, uint64, uint64); // plic.c void plicinit(void); void plicinithart(void); -uint64 plic_pending(void); int plic_claim(void); void plic_complete(int); diff --git a/kernel/exec.c b/kernel/exec.c index 74ef654..1077ac0 100644 --- a/kernel/exec.c +++ b/kernel/exec.c @@ -97,7 +97,7 @@ exec(char *path, char **argv) // arguments to user main(argc, argv) // argc is returned via the system call return // value, which goes in a0. - p->tf->a1 = sp; + p->trapframe->a1 = sp; // Save program name for debugging. for(last=s=path; *s; s++) @@ -109,9 +109,10 @@ exec(char *path, char **argv) oldpagetable = p->pagetable; p->pagetable = pagetable; p->sz = sz; - p->tf->epc = elf.entry; // initial program counter = main - p->tf->sp = sp; // initial stack pointer + p->trapframe->epc = elf.entry; // initial program counter = main + p->trapframe->sp = sp; // initial stack pointer proc_freepagetable(oldpagetable, oldsz); + return argc; // this ends up in a0, the first argument to main(argc, argv) bad: diff --git a/kernel/fcntl.h b/kernel/fcntl.h index d565483..44861b9 100644 --- a/kernel/fcntl.h +++ b/kernel/fcntl.h @@ -2,3 +2,4 @@ #define O_WRONLY 0x001 #define O_RDWR 0x002 #define O_CREATE 0x200 +#define O_TRUNC 0x400 diff --git a/kernel/fs.c b/kernel/fs.c index 53586d5..ec68cd7 100644 --- a/kernel/fs.c +++ b/kernel/fs.c @@ -22,7 +22,6 @@ #include "file.h" #define min(a, b) ((a) < (b) ? (a) : (b)) -static void itrunc(struct inode*); // there should be one superblock per disk device, but we run with // only one device struct superblock sb; @@ -406,11 +405,8 @@ bmap(struct inode *ip, uint bn) } // Truncate inode (discard contents). -// Only called when the inode has no links -// to it (no directory entries referring to it) -// and has no in-memory reference to it (is -// not an open file or current directory). -static void +// Caller must hold ip->lock. +void itrunc(struct inode *ip) { int i, j; @@ -463,7 +459,7 @@ readi(struct inode *ip, int user_dst, uint64 dst, uint off, uint n) struct buf *bp; if(off > ip->size || off + n < off) - return -1; + return 0; if(off + n > ip->size) n = ip->size - off; @@ -476,7 +472,7 @@ readi(struct inode *ip, int user_dst, uint64 dst, uint off, uint n) } brelse(bp); } - return n; + return tot; } // Write data to inode. diff --git a/kernel/kernel.ld b/kernel/kernel.ld index acc3c8e..ee04f22 100644 --- a/kernel/kernel.ld +++ b/kernel/kernel.ld @@ -8,25 +8,37 @@ SECTIONS * where qemu's -kernel jumps. */ . = 0x80000000; - .text : - { - *(.text) + + .text : { + *(.text .text.*) . = ALIGN(0x1000); + _trampoline = .; *(trampsec) + . = ALIGN(0x1000); + ASSERT(. - _trampoline == 0x1000, "error: trampoline larger than one page"); + PROVIDE(etext = .); } - . = ALIGN(0x1000); - PROVIDE(etext = .); + .rodata : { + . = ALIGN(16); + *(.srodata .srodata.*) /* do not need to distinguish this from .rodata */ + . = ALIGN(16); + *(.rodata .rodata.*) + } - /* - * make sure end is after data and bss. - */ .data : { - *(.data) + . = ALIGN(16); + *(.sdata .sdata.*) /* do not need to distinguish this from .data */ + . = ALIGN(16); + *(.data .data.*) } + .bss : { - *(.bss) - *(.sbss*) - PROVIDE(end = .); + . = ALIGN(16); + *(.sbss .sbss.*) /* do not need to distinguish this from .bss */ + . = ALIGN(16); + *(.bss .bss.*) } + + PROVIDE(end = .); } diff --git a/kernel/memlayout.h b/kernel/memlayout.h index 8ffd538..b6e595d 100644 --- a/kernel/memlayout.h +++ b/kernel/memlayout.h @@ -62,6 +62,6 @@ // fixed-size stack // expandable heap // ... -// TRAPFRAME (p->tf, used by the trampoline) +// TRAPFRAME (p->trapframe, used by the trampoline) // TRAMPOLINE (the same page as in the kernel) #define TRAPFRAME (TRAMPOLINE - PGSIZE) diff --git a/kernel/pipe.c b/kernel/pipe.c index e358283..7ed402d 100644 --- a/kernel/pipe.c +++ b/kernel/pipe.c @@ -83,7 +83,7 @@ pipewrite(struct pipe *pi, uint64 addr, int n) acquire(&pi->lock); for(i = 0; i < n; i++){ while(pi->nwrite == pi->nread + PIPESIZE){ //DOC: pipewrite-full - if(pi->readopen == 0 || myproc()->killed){ + if(pi->readopen == 0 || pr->killed){ release(&pi->lock); return -1; } @@ -96,7 +96,7 @@ pipewrite(struct pipe *pi, uint64 addr, int n) } wakeup(&pi->nread); release(&pi->lock); - return n; + return i; } int @@ -108,7 +108,7 @@ piperead(struct pipe *pi, uint64 addr, int n) acquire(&pi->lock); while(pi->nread == pi->nwrite && pi->writeopen){ //DOC: pipe-empty - if(myproc()->killed){ + if(pr->killed){ release(&pi->lock); return -1; } diff --git a/kernel/plic.c b/kernel/plic.c index b569492..5acba39 100644 --- a/kernel/plic.c +++ b/kernel/plic.c @@ -28,26 +28,11 @@ plicinithart(void) *(uint32*)PLIC_SPRIORITY(hart) = 0; } -// return a bitmap of which IRQs are waiting -// to be served. -uint64 -plic_pending(void) -{ - uint64 mask; - - //mask = *(uint32*)(PLIC + 0x1000); - //mask |= (uint64)*(uint32*)(PLIC + 0x1004) << 32; - mask = *(uint64*)PLIC_PENDING; - - return mask; -} - // ask the PLIC what interrupt we should serve. int plic_claim(void) { int hart = cpuid(); - //int irq = *(uint32*)(PLIC + 0x201004); int irq = *(uint32*)PLIC_SCLAIM(hart); return irq; } @@ -57,6 +42,5 @@ void plic_complete(int irq) { int hart = cpuid(); - //*(uint32*)(PLIC + 0x201004) = irq; *(uint32*)PLIC_SCLAIM(hart) = irq; } diff --git a/kernel/proc.c b/kernel/proc.c index bbf3af0..cf7f260 100644 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -20,6 +20,7 @@ static void wakeup1(struct proc *chan); extern char trampoline[]; // trampoline.S +// initialize the proc table at boot time. void procinit(void) { @@ -106,7 +107,7 @@ found: p->pid = allocpid(); // Allocate a trapframe page. - if((p->tf = (struct trapframe *)kalloc()) == 0){ + if((p->trapframe = (struct trapframe *)kalloc()) == 0){ release(&p->lock); return 0; } @@ -116,7 +117,7 @@ found: // Set up new context to start executing at forkret, // which returns to user space. - memset(&p->context, 0, sizeof p->context); + memset(&p->context, 0, sizeof(p->context)); p->context.ra = (uint64)forkret; p->context.sp = p->kstack + PGSIZE; @@ -129,9 +130,9 @@ found: static void freeproc(struct proc *p) { - if(p->tf) - kfree((void*)p->tf); - p->tf = 0; + if(p->trapframe) + kfree((void*)p->trapframe); + p->trapframe = 0; if(p->pagetable) proc_freepagetable(p->pagetable, p->sz); p->pagetable = 0; @@ -145,8 +146,8 @@ freeproc(struct proc *p) p->state = UNUSED; } -// Create a page table for a given process, -// with no user pages, but with trampoline pages. +// Create a user page table for a given process, +// with no user memory, but with trampoline pages. pagetable_t proc_pagetable(struct proc *p) { @@ -164,7 +165,7 @@ proc_pagetable(struct proc *p) // map the trapframe just below TRAMPOLINE, for trampoline.S. mappages(pagetable, TRAPFRAME, PGSIZE, - (uint64)(p->tf), PTE_R | PTE_W); + (uint64)(p->trapframe), PTE_R | PTE_W); return pagetable; } @@ -176,8 +177,7 @@ proc_freepagetable(pagetable_t pagetable, uint64 sz) { uvmunmap(pagetable, TRAMPOLINE, PGSIZE, 0); uvmunmap(pagetable, TRAPFRAME, PGSIZE, 0); - if(sz > 0) - uvmfree(pagetable, sz); + uvmfree(pagetable, sz); } // a user program that calls exec("/init") @@ -207,8 +207,8 @@ userinit(void) p->sz = PGSIZE; // prepare for the very first "return" from kernel to user. - p->tf->epc = 0; // user program counter - p->tf->sp = PGSIZE; // user stack pointer + p->trapframe->epc = 0; // user program counter + p->trapframe->sp = PGSIZE; // user stack pointer safestrcpy(p->name, "initcode", sizeof(p->name)); p->cwd = namei("/"); @@ -263,10 +263,10 @@ fork(void) np->parent = p; // copy saved user registers. - *(np->tf) = *(p->tf); + *(np->trapframe) = *(p->trapframe); // Cause fork to return 0 in the child. - np->tf->a0 = 0; + np->trapframe->a0 = 0; // increment reference counts on open file descriptors. for(i = 0; i < NOFILE; i++) @@ -457,7 +457,7 @@ scheduler(void) // before jumping back to us. p->state = RUNNING; c->proc = p; - swtch(&c->scheduler, &p->context); + swtch(&c->context, &p->context); // Process is done running for now. // It should have changed its p->state before coming back. @@ -491,7 +491,7 @@ sched(void) panic("sched interruptible"); intena = mycpu()->intena; - swtch(&p->context, &mycpu()->scheduler); + swtch(&p->context, &mycpu()->context); mycpu()->intena = intena; } diff --git a/kernel/proc.h b/kernel/proc.h index 538b48a..9c16ea7 100644 --- a/kernel/proc.h +++ b/kernel/proc.h @@ -21,7 +21,7 @@ struct context { // Per-CPU state. struct cpu { struct proc *proc; // The process running on this cpu, or null. - struct context scheduler; // swtch() here to enter scheduler(). + struct context context; // swtch() here to enter scheduler(). int noff; // Depth of push_off() nesting. int intena; // Were interrupts enabled before push_off()? }; @@ -95,10 +95,10 @@ struct proc { int pid; // Process ID // these are private to the process, so p->lock need not be held. - uint64 kstack; // Bottom of kernel stack for this process + uint64 kstack; // Virtual address of kernel stack uint64 sz; // Size of process memory (bytes) - pagetable_t pagetable; // Page table - struct trapframe *tf; // data page for trampoline.S + pagetable_t pagetable; // User page table + struct trapframe *trapframe; // data page for trampoline.S struct context context; // swtch() here to run process struct file *ofile[NOFILE]; // Open files struct inode *cwd; // Current directory diff --git a/kernel/riscv.h b/kernel/riscv.h index f46ba59..0aec003 100644 --- a/kernel/riscv.h +++ b/kernel/riscv.h @@ -261,7 +261,6 @@ r_time() static inline void intr_on() { - w_sie(r_sie() | SIE_SEIE | SIE_STIE | SIE_SSIE); w_sstatus(r_sstatus() | SSTATUS_SIE); } diff --git a/kernel/spinlock.c b/kernel/spinlock.c index 563532e..9840302 100644 --- a/kernel/spinlock.c +++ b/kernel/spinlock.c @@ -34,7 +34,8 @@ acquire(struct spinlock *lk) // Tell the C compiler and the processor to not move loads or stores // past this point, to ensure that the critical section's memory - // references happen after the lock is acquired. + // references happen strictly after the lock is acquired. + // On RISC-V, this emits a fence instruction. __sync_synchronize(); // Record info about lock acquisition for holding() and debugging. @@ -52,8 +53,10 @@ release(struct spinlock *lk) // Tell the C compiler and the CPU to not move loads or stores // past this point, to ensure that all the stores in the critical - // section are visible to other CPUs before the lock is released. - // On RISC-V, this turns into a fence instruction. + // section are visible to other CPUs before the lock is released, + // and that loads in the critical section occur strictly before + // the lock is released. + // On RISC-V, this emits a fence instruction. __sync_synchronize(); // Release the lock, equivalent to lk->locked = 0. @@ -69,13 +72,12 @@ release(struct spinlock *lk) } // Check whether this cpu is holding the lock. +// Interrupts must be off. int holding(struct spinlock *lk) { int r; - push_off(); r = (lk->locked && lk->cpu == mycpu()); - pop_off(); return r; } @@ -100,9 +102,9 @@ pop_off(void) struct cpu *c = mycpu(); if(intr_get()) panic("pop_off - interruptible"); - c->noff -= 1; - if(c->noff < 0) + if(c->noff < 1) panic("pop_off"); + c->noff -= 1; if(c->noff == 0 && c->intena) intr_on(); } diff --git a/kernel/start.c b/kernel/start.c index 203c5e6..4eb6c2d 100644 --- a/kernel/start.c +++ b/kernel/start.c @@ -36,6 +36,7 @@ start() // delegate all interrupts and exceptions to supervisor mode. w_medeleg(0xffff); w_mideleg(0xffff); + w_sie(r_sie() | SIE_SEIE | SIE_STIE | SIE_SSIE); // ask for clock interrupts. timerinit(); diff --git a/kernel/syscall.c b/kernel/syscall.c index 2817e44..c1b3670 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -37,17 +37,17 @@ argraw(int n) struct proc *p = myproc(); switch (n) { case 0: - return p->tf->a0; + return p->trapframe->a0; case 1: - return p->tf->a1; + return p->trapframe->a1; case 2: - return p->tf->a2; + return p->trapframe->a2; case 3: - return p->tf->a3; + return p->trapframe->a3; case 4: - return p->tf->a4; + return p->trapframe->a4; case 5: - return p->tf->a5; + return p->trapframe->a5; } panic("argraw"); return -1; @@ -135,12 +135,12 @@ syscall(void) int num; struct proc *p = myproc(); - num = p->tf->a7; + num = p->trapframe->a7; if(num > 0 && num < NELEM(syscalls) && syscalls[num]) { - p->tf->a0 = syscalls[num](); + p->trapframe->a0 = syscalls[num](); } else { printf("%d %s: unknown sys call %d\n", p->pid, p->name, num); - p->tf->a0 = -1; + p->trapframe->a0 = -1; } } diff --git a/kernel/sysfile.c b/kernel/sysfile.c index 7768d20..015c942 100644 --- a/kernel/sysfile.c +++ b/kernel/sysfile.c @@ -341,6 +341,10 @@ sys_open(void) f->readable = !(omode & O_WRONLY); f->writable = (omode & O_WRONLY) || (omode & O_RDWR); + if((omode & O_TRUNC) && ip->type == T_FILE){ + itrunc(ip); + } + iunlock(ip); end_op(); diff --git a/kernel/trampoline.S b/kernel/trampoline.S index b113bf6..fabaaf9 100644 --- a/kernel/trampoline.S +++ b/kernel/trampoline.S @@ -20,7 +20,7 @@ uservec: # in supervisor mode, but with a # user page table. # - # sscratch points to where the process's p->tf is + # sscratch points to where the process's p->trapframe is # mapped into user space, at TRAPFRAME. # @@ -60,20 +60,20 @@ uservec: sd t5, 272(a0) sd t6, 280(a0) - # save the user a0 in p->tf->a0 + # save the user a0 in p->trapframe->a0 csrr t0, sscratch sd t0, 112(a0) - # restore kernel stack pointer from p->tf->kernel_sp + # restore kernel stack pointer from p->trapframe->kernel_sp ld sp, 8(a0) - # make tp hold the current hartid, from p->tf->kernel_hartid + # make tp hold the current hartid, from p->trapframe->kernel_hartid ld tp, 32(a0) - # load the address of usertrap(), p->tf->kernel_trap + # load the address of usertrap(), p->trapframe->kernel_trap ld t0, 16(a0) - # restore kernel page table from p->tf->kernel_satp + # restore kernel page table from p->trapframe->kernel_satp ld t1, 0(a0) csrw satp, t1 sfence.vma zero, zero diff --git a/kernel/trap.c b/kernel/trap.c index ca732f2..a63249e 100644 --- a/kernel/trap.c +++ b/kernel/trap.c @@ -48,7 +48,7 @@ usertrap(void) struct proc *p = myproc(); // save user program counter. - p->tf->epc = r_sepc(); + p->trapframe->epc = r_sepc(); if(r_scause() == 8){ // system call @@ -58,7 +58,7 @@ usertrap(void) // sepc points to the ecall instruction, // but we want to return to the next instruction. - p->tf->epc += 4; + p->trapframe->epc += 4; // an interrupt will change sstatus &c registers, // so don't enable until done with those registers. @@ -91,8 +91,9 @@ usertrapret(void) { struct proc *p = myproc(); - // turn off interrupts, since we're switching - // now from kerneltrap() to usertrap(). + // we're about to switch the destination of traps from + // kerneltrap() to usertrap(), so turn off interrupts until + // we're back in user space, where usertrap() is correct. intr_off(); // send syscalls, interrupts, and exceptions to trampoline.S @@ -100,10 +101,10 @@ usertrapret(void) // set up trapframe values that uservec will need when // the process next re-enters the kernel. - p->tf->kernel_satp = r_satp(); // kernel page table - p->tf->kernel_sp = p->kstack + PGSIZE; // process's kernel stack - p->tf->kernel_trap = (uint64)usertrap; - p->tf->kernel_hartid = r_tp(); // hartid for cpuid() + p->trapframe->kernel_satp = r_satp(); // kernel page table + p->trapframe->kernel_sp = p->kstack + PGSIZE; // process's kernel stack + p->trapframe->kernel_trap = (uint64)usertrap; + p->trapframe->kernel_hartid = r_tp(); // hartid for cpuid() // set up the registers that trampoline.S's sret will use // to get to user space. @@ -115,7 +116,7 @@ usertrapret(void) w_sstatus(x); // set S Exception Program Counter to the saved user pc. - w_sepc(p->tf->epc); + w_sepc(p->trapframe->epc); // tell trampoline.S the user page table to switch to. uint64 satp = MAKE_SATP(p->pagetable); @@ -129,7 +130,6 @@ usertrapret(void) // interrupts and exceptions from kernel code go here via kernelvec, // on whatever the current kernel stack is. -// must be 4-byte aligned to fit in stvec. void kerneltrap() { @@ -189,9 +189,16 @@ devintr() uartintr(); } else if(irq == VIRTIO0_IRQ){ virtio_disk_intr(); + } else if(irq){ + printf("unexpected interrupt irq=%d\n", irq); } - plic_complete(irq); + // the PLIC allows each device to raise at most one + // interrupt at a time; tell the PLIC the device is + // now allowed to interrupt again. + if(irq) + plic_complete(irq); + return 1; } else if(scause == 0x8000000000000001L){ // software interrupt from a machine-mode timer interrupt, diff --git a/kernel/uart.c b/kernel/uart.c index 3a5cdc4..daf9f04 100644 --- a/kernel/uart.c +++ b/kernel/uart.c @@ -18,18 +18,35 @@ // the UART control registers. // some have different meanings for // read vs write. -// http://byterunner.com/16550.html -#define RHR 0 // receive holding register (for input bytes) -#define THR 0 // transmit holding register (for output bytes) -#define IER 1 // interrupt enable register -#define FCR 2 // FIFO control register -#define ISR 2 // interrupt status register -#define LCR 3 // line control register -#define LSR 5 // line status register +// see http://byterunner.com/16550.html +#define RHR 0 // receive holding register (for input bytes) +#define THR 0 // transmit holding register (for output bytes) +#define IER 1 // interrupt enable register +#define IER_TX_ENABLE (1<<0) +#define IER_RX_ENABLE (1<<1) +#define FCR 2 // FIFO control register +#define FCR_FIFO_ENABLE (1<<0) +#define FCR_FIFO_CLEAR (3<<1) // clear the content of the two FIFOs +#define ISR 2 // interrupt status register +#define LCR 3 // line control register +#define LCR_EIGHT_BITS (3<<0) +#define LCR_BAUD_LATCH (1<<7) // special mode to set baud rate +#define LSR 5 // line status register +#define LSR_RX_READY (1<<0) // input is waiting to be read from RHR +#define LSR_TX_IDLE (1<<5) // THR can accept another character to send #define ReadReg(reg) (*(Reg(reg))) #define WriteReg(reg, v) (*(Reg(reg)) = (v)) +// the transmit output buffer. +struct spinlock uart_tx_lock; +#define UART_TX_BUF_SIZE 32 +char uart_tx_buf[UART_TX_BUF_SIZE]; +int uart_tx_w; // write next to uart_tx_buf[uart_tx_w++] +int uart_tx_r; // read next from uart_tx_buf[uar_tx_r++] + +void uartstart(); + void uartinit(void) { @@ -37,7 +54,7 @@ uartinit(void) WriteReg(IER, 0x00); // special mode to set baud rate. - WriteReg(LCR, 0x80); + WriteReg(LCR, LCR_BAUD_LATCH); // LSB for baud rate of 38.4K. WriteReg(0, 0x03); @@ -47,23 +64,87 @@ uartinit(void) // leave set-baud mode, // and set word length to 8 bits, no parity. - WriteReg(LCR, 0x03); + WriteReg(LCR, LCR_EIGHT_BITS); // reset and enable FIFOs. - WriteReg(FCR, 0x07); + WriteReg(FCR, FCR_FIFO_ENABLE | FCR_FIFO_CLEAR); + + // enable transmit and receive interrupts. + WriteReg(IER, IER_TX_ENABLE | IER_RX_ENABLE); - // enable receive interrupts. - WriteReg(IER, 0x01); + initlock(&uart_tx_lock, "uart"); } -// write one output character to the UART. +// add a character to the output buffer and tell the +// UART to start sending if it isn't already. +// blocks if the output buffer is full. +// because it may block, it can't be called +// from interrupts; it's only suitable for use +// by write(). void uartputc(int c) { + acquire(&uart_tx_lock); + while(1){ + if(((uart_tx_w + 1) % UART_TX_BUF_SIZE) == uart_tx_r){ + // buffer is full. + // wait for uartstart() to open up space in the buffer. + sleep(&uart_tx_r, &uart_tx_lock); + } else { + uart_tx_buf[uart_tx_w] = c; + uart_tx_w = (uart_tx_w + 1) % UART_TX_BUF_SIZE; + uartstart(); + release(&uart_tx_lock); + return; + } + } +} + +// alternate version of uartputc() that doesn't +// use interrupts, for use by kernel printf() and +// to echo characters. it spins waiting for the uart's +// output register to be empty. +void +uartputc_sync(int c) +{ + push_off(); + // wait for Transmit Holding Empty to be set in LSR. - while((ReadReg(LSR) & (1 << 5)) == 0) + while((ReadReg(LSR) & LSR_TX_IDLE) == 0) ; WriteReg(THR, c); + + pop_off(); +} + +// if the UART is idle, and a character is waiting +// in the transmit buffer, send it. +// caller must hold uart_tx_lock. +// called from both the top- and bottom-half. +void +uartstart() +{ + while(1){ + if(uart_tx_w == uart_tx_r){ + // transmit buffer is empty. + return; + } + + if((ReadReg(LSR) & LSR_TX_IDLE) == 0){ + // the UART transmit holding register is full, + // so we cannot give it another byte. + // it will interrupt when it's ready for a new byte. + return; + } + + int c = uart_tx_buf[uart_tx_r]; + uart_tx_r = (uart_tx_r + 1) % UART_TX_BUF_SIZE; + + // maybe uartputc() is waiting for space in the buffer. + wakeup(&uart_tx_r); + + WriteReg(THR, c); + } } // read one input character from the UART. @@ -79,14 +160,22 @@ uartgetc(void) } } -// trap.c calls here when the uart interrupts. +// handle a uart interrupt, raised because input has +// arrived, or the uart is ready for more output, or +// both. called from trap.c. void uartintr(void) { + // read and process incoming characters. while(1){ int c = uartgetc(); if(c == -1) break; consoleintr(c); } + + // send buffered characters. + acquire(&uart_tx_lock); + uartstart(); + release(&uart_tx_lock); } diff --git a/kernel/vm.c b/kernel/vm.c index 5e0b2aa..636f11a 100644 --- a/kernel/vm.c +++ b/kernel/vm.c @@ -16,9 +16,7 @@ extern char etext[]; // kernel.ld sets this to end of kernel code. extern char trampoline[]; // trampoline.S /* - * create a direct-map page table for the kernel and - * turn on paging. called early, in supervisor mode. - * the page allocator is already initialized. + * create a direct-map page table for the kernel. */ void kvminit() @@ -70,7 +68,7 @@ kvminithart() // 21..29 -- 9 bits of level-1 index. // 12..20 -- 9 bits of level-0 index. // 0..11 -- 12 bits of byte offset within the page. -static pte_t * +pte_t * walk(pagetable_t pagetable, uint64 va, int alloc) { if(va >= MAXVA) @@ -278,7 +276,7 @@ uvmdealloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz) // Recursively free page-table pages. // All leaf mappings must already have been removed. -static void +void freewalk(pagetable_t pagetable) { // there are 2^9 = 512 PTEs in a page table. @@ -301,7 +299,8 @@ freewalk(pagetable_t pagetable) void uvmfree(pagetable_t pagetable, uint64 sz) { - uvmunmap(pagetable, 0, sz, 1); + if(sz > 0) + uvmunmap(pagetable, 0, sz, 1); freewalk(pagetable); } |