text: #include "types.h"
  #include "param.h"
  #include "memlayout.h"
  #include "riscv.h"
  #include "spinlock.h"
  #include "proc.h"
  #include "defs.h"
  
  struct spinlock tickslock;
  uint ticks;
  
  extern char trampoline[], uservec[], userret[];
  
  // in kernelvec.S, calls kerneltrap().
  void kernelvec();
  
  extern int devintr();
  
  void
  trapinit(void)
  {
    initlock(&tickslock, "time");
  }
  
  // set up to take exceptions and traps while in the kernel.
  void
  trapinithart(void)
  {
    w_stvec((uint64)kernelvec);
  }
  
  //栈自增处理
  int stackGrow(pagetable_t old,uint64 fault_address){
    //printf("trap: check grow\n");
    fault_address=PGROUNDDOWN(fault_address);
    if(fault_address>=MAXVA) return -1;
    //printf("trap: fault_address is %d\n",fault_address);
    //char *mem;   
    struct proc *p = myproc();
    uint heapBorder = p->sz + PGSIZE;     //堆和栈始终相差一个页面，保证堆中数据安全
    uint stackBorder = p->stackSize;
  
    //堆和栈始终相差一个页面，保证堆中数据安全
    int isLackOfStackCapacity = fault_address >= heapBorder && fault_address < stackBorder;
   // printf("fault_address: %x\n",fault_address);
   // printf("heapborder: %x\n",heapBorder);
   // printf("stackBorder: %x\n",stackBorder);
    //每次分配一个页面
    if(!isLackOfStackCapacity) {
      //printf("trap: Begin to grow\n");
      /*mem = kalloc();
      if(mem == 0) {//物理内存不足
        printf("trap: Out of memory\n");
        return -1;
      }
      uint64 newStackBorder = stackBorder - PGSIZE;
      //修改页表，增加映射关系
      if(mappages(old, newStackBorder, PGSIZE, (uint64)mem, PTE_W|PTE_U) != 0) {
        printf("trap: Mapping error\n");
        uvmdealloc(old, newStackBorder, stackBorder);
        kfree(mem);
        return -1;
      }
      memset(mem, 0, PGSIZE);*/
      int k=(PGROUNDUP(p->stackSize)-PGROUNDDOWN(fault_address))/PGSIZE;
      if(uvmalloc(old,p->stackSize-k*PGSIZE,p->stackSize)==0){
          printf("trap: uvmalloc error!\n");
          return -1;
      }
      p->stackSize -= PGSIZE;
    }else{
        printf("trap: Cannot grow\n");
        return -1;
    }
    //printf("trap: Growed!\n");
    //printf("trap: Current stackSize is %d!\n",p->stackSize);
    return 0;
  }
  
  //
  // handle an interrupt, exception, or system call from user space.
  // called from trampoline.S
  //
  void
  usertrap(void)
  {
    int which_dev = 0;
  
    if((r_sstatus() & SSTATUS_SPP) != 0)
      panic("usertrap: not from user mode");
  
    // send interrupts and exceptions to kerneltrap(),
    // since we're now in the kernel.
    w_stvec((uint64)kernelvec);
  
    struct proc *p = myproc();
    
    // save user program counter.
    p->trapframe->epc = r_sepc();
    
    if(r_scause() == 8){
      // system call
  
      if(p->killed)
        exit(-1);
  
      // sepc points to the ecall instruction,
      // but we want to return to the next instruction.
      p->trapframe->epc += 4;
  
      // an interrupt will change sstatus &c registers,
      // so don't enable until done with those registers.
      intr_on();
  
      syscall();
    }else if((which_dev = devintr()) != 0){
      // ok
    }else if(r_scause()==15){
      //printf("usertrap: page fault\n");
      // printf("usertrap():in page fault\n");
      //出错的page地址
      pagetable_t old=p->pagetable;   //原始页表
      uint64 fault_address=r_stval();   //触发page fault的虚拟地址
      if(fault_address>=MAXVA) p->killed=1;  //超过给进程分配的内存
      //将虚拟地址转化到物理地址
      else if(stackGrow(old,fault_address)!=0){
        p->killed=1;
        printf("usertrap(): unexpected scause %p pid=%d\n", r_scause(), p->pid);
        printf("            sepc=%p stval=%p\n", r_sepc(), r_stval());
        // exit(-1);
      }
    }else {
      printf("usertrap(): unexpected scause %p pid=%d\n", r_scause(), p->pid);
      printf("            sepc=%p stval=%p\n", r_sepc(), r_stval());
      p->killed = 1;
    }
  
    if(p->killed)
      exit(-1);
  
    // give up the CPU if this is a timer interrupt.
    if(which_dev == 2)
      yield();
  
    usertrapret();
  }
  
  //
  // return to user space
  //
  void
  usertrapret(void)
  {
    struct proc *p = myproc();
  
    // 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
    w_stvec(TRAMPOLINE + (uservec - trampoline));
  
    // set up trapframe values that uservec will need when
    // the process next re-enters the kernel.
    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.
    
    // set S Previous Privilege mode to User.
    unsigned long x = r_sstatus();
    x &= ~SSTATUS_SPP; // clear SPP to 0 for user mode
    x |= SSTATUS_SPIE; // enable interrupts in user mode
    w_sstatus(x);
  
    // set S Exception Program Counter to the saved user pc.
    w_sepc(p->trapframe->epc);
  
    // tell trampoline.S the user page table to switch to.
    uint64 satp = MAKE_SATP(p->pagetable);
  
    // jump to trampoline.S at the top of memory, which 
    // switches to the user page table, restores user registers,
    // and switches to user mode with sret.
    uint64 fn = TRAMPOLINE + (userret - trampoline);
    ((void (*)(uint64,uint64))fn)(TRAPFRAME, satp);
  }
  
  // interrupts and exceptions from kernel code go here via kernelvec,
  // on whatever the current kernel stack is.
  void 
  kerneltrap()
  {
    int which_dev = 0;
    uint64 sepc = r_sepc();
    uint64 sstatus = r_sstatus();
    uint64 scause = r_scause();
    
    if((sstatus & SSTATUS_SPP) == 0)
      panic("kerneltrap: not from supervisor mode");
    if(intr_get() != 0)
      panic("kerneltrap: interrupts enabled");
  
    if((which_dev = devintr()) == 0){
      printf("scause %p\n", scause);
      printf("sepc=%p stval=%p\n", r_sepc(), r_stval());
      panic("kerneltrap");
    }
  
    // give up the CPU if this is a timer interrupt.
    if(which_dev == 2 && myproc() != 0 && myproc()->state == RUNNING)
      yield();
  
    // the yield() may have caused some traps to occur,
    // so restore trap registers for use by kernelvec.S's sepc instruction.
    w_sepc(sepc);
    w_sstatus(sstatus);
  }
  
  void
  clockintr()
  {
    acquire(&tickslock);
    ticks++;
    wakeup(&ticks);
    release(&tickslock);
  }
  
  // check if it's an external interrupt or software interrupt,
  // and handle it.
  // returns 2 if timer interrupt,
  // 1 if other device,
  // 0 if not recognized.
  int
  devintr()
  {
    uint64 scause = r_scause();
  
    if((scause & 0x8000000000000000L) &&
       (scause & 0xff) == 9){
      // this is a supervisor external interrupt, via PLIC.
  
      // irq indicates which device interrupted.
      int irq = plic_claim();
  
      if(irq == UART0_IRQ){
        uartintr();
      } else if(irq == VIRTIO0_IRQ){
        virtio_disk_intr();
      } else if(irq){
        printf("unexpected interrupt irq=%d\n", 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,
      // forwarded by timervec in kernelvec.S.
  
      if(cpuid() == 0){
        clockintr();
      }
      
      // acknowledge the software interrupt by clearing
      // the SSIP bit in sip.
      w_sip(r_sip() & ~2);
  
      return 2;
    } else {
      return 0;
    }
  }
  
  