@ -30,8 +30,8 @@
 
			
		
	
		
			
				
					  - [实验内容 ](#lab1_challenge2_content )
 
			
		
	
		
			
				
					  - [实验指导 ](#lab1_challenge2_guide )
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					< a  name = "fundamental" > < / a >  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					## 3.1 实验1的基础知识
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					本章我们将首先[获得代码](#subsec_preparecode),接下来介绍[程序的编译链接和ELF文件](#subsec_elfload)的基础知识, , 
 
			
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
				@ -607,53 +607,63 @@ $ riscv64-unknown-elf-objdump -D ./obj/riscv-pke | grep _mentry
 
			
		
	
		
			
				
					 28     call m_start
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					它的执行将机器复位( ) ( lab1_1中只考虑单个内核 ) ( ) , ( ) : 
 
			
		
	
		
			
				
					它的执行将机器复位( ) ( PKE的基础实验中只考虑单个处理器的情况 ) ( ) , ( ) : 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					```c
 
			
		
	
		
			
				
					 68 void m_start(uintptr_t hartid, uintptr_t dtb) {
 
			
		
	
		
			
				
					 69   // init the spike file interface (stdin,stdout,stderr)
 
			
		
	
		
			
				
					 70   spike_file_init();
 
			
		
	
		
			
				
					 71   sprint("In m_start, hartid:%d\n", hartid);
 
			
		
	
		
			
				
					 72
 
			
		
	
		
			
				
					 73   // init HTIF (Host-Target InterFace) and memory by using the Device Table Blob (DTB)
 
			
		
	
		
			
				
					 74   init_dtb(dtb);
 
			
		
	
		
			
				
					 75
 
			
		
	
		
			
				
					 76   // set previous privilege mode to S (Supervisor), and will enter S mode after 'mret'
 
			
		
	
		
			
				
					 77   write_csr(mstatus, ((read_csr(mstatus) &  ~MSTATUS_MPP_MASK) | MSTATUS_MPP_S));
 
			
		
	
		
			
				
					 78
 
			
		
	
		
			
				
					 79   // set M Exception Program Counter to sstart, for mret (requires gcc -mcmodel=medany)
 
			
		
	
		
			
				
					 80   write_csr(mepc, (uint64)s_start);
 
			
		
	
		
			
				
					 81
 
			
		
	
		
			
				
					 82   // delegate all interrupts and exceptions to supervisor mode.
 
			
		
	
		
			
				
					 83   delegate_traps();
 
			
		
	
		
			
				
					 84
 
			
		
	
		
			
				
					 85   // switch to supervisor mode and jump to s_start(), i.e., set pc to mepc
 
			
		
	
		
			
				
					 86   asm volatile("mret");
 
			
		
	
		
			
				
					 87 }
 
			
		
	
		
			
				
					 77 void m_start(uintptr_t hartid, uintptr_t dtb) {
 
			
		
	
		
			
				
					 78   // init the spike file interface (stdin,stdout,stderr)
 
			
		
	
		
			
				
					 79   // functions with "spike_" prefix are all defined in codes under spike_interface/,
 
			
		
	
		
			
				
					 80   // sprint is also defined in spike_interface/spike_utils.c
 
			
		
	
		
			
				
					 81   spike_file_init();
 
			
		
	
		
			
				
					 82   sprint("In m_start, hartid:%d\n", hartid);
 
			
		
	
		
			
				
					 83
 
			
		
	
		
			
				
					 84   // init HTIF (Host-Target InterFace) and memory by using the Device Table Blob (DTB)
 
			
		
	
		
			
				
					 85   // init_dtb() is defined above.
 
			
		
	
		
			
				
					 86   init_dtb(dtb);
 
			
		
	
		
			
				
					 87
 
			
		
	
		
			
				
					 88   // set previous privilege mode to S (Supervisor), and will enter S mode after 'mret'
 
			
		
	
		
			
				
					 89   // write_csr is a macro defined in kernel/riscv.h
 
			
		
	
		
			
				
					 90   write_csr(mstatus, ((read_csr(mstatus) &  ~MSTATUS_MPP_MASK) | MSTATUS_MPP_S));
 
			
		
	
		
			
				
					 91
 
			
		
	
		
			
				
					 92   // set M Exception Program Counter to sstart, for mret (requires gcc -mcmodel=medany)
 
			
		
	
		
			
				
					 93   write_csr(mepc, (uint64)s_start);
 
			
		
	
		
			
				
					 94
 
			
		
	
		
			
				
					 95   // delegate all interrupts and exceptions to supervisor mode.
 
			
		
	
		
			
				
					 96   // delegate_traps() is defined above.
 
			
		
	
		
			
				
					 97   delegate_traps();
 
			
		
	
		
			
				
					 98
 
			
		
	
		
			
				
					 99   // switch to supervisor mode (S mode) and jump to s_start(), i.e., set pc to mepc
 
			
		
	
		
			
				
					100   asm volatile("mret");
 
			
		
	
		
			
				
					101 }
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					它的作用是首先初始化spike的客户机-主机接口( , ) , ( ) ; ( , ) ( ) , ( ) ; , ( ) ; , ( ) , , 
 
			
		
	
		
			
				
					它的作用是首先初始化spike的客户机-主机接口( , ) , ( 81-86 行) ; ( , ) ( ) , ( 90--93行) ; , ( ) ; , ( ) ,  行的返回动作将“返回”S态, 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					s_start函数在kernel/kernel.c文件中定义: 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					```c
 
			
		
	
		
			
				
					 30 int s_start(void) {
 
			
		
	
		
			
				
					 31   sprint("Enter supervisor mode...\n");
 
			
		
	
		
			
				
					 32   // Note: we use direct (i.e., Bare mode) for memory mapping in lab1.
 
			
		
	
		
			
				
					 33   // which means: Virtual Address = Physical Address
 
			
		
	
		
			
				
					 34   write_csr(satp, 0);
 
			
		
	
		
			
				
					 35
 
			
		
	
		
			
				
					 36   // the application code (elf) is first loaded into memory, and then put into execution
 
			
		
	
		
			
				
					 37   load_user_program(&user_app); 
 
			
		
	
		
			
				
					 38
 
			
		
	
		
			
				
					 39   sprint("Switch to user mode...\n");
 
			
		
	
		
			
				
					 40   switch_to(&user_app); 
 
			
		
	
		
			
				
					 41
 
			
		
	
		
			
				
					 42   return 0;
 
			
		
	
		
			
				
					 43 }
 
			
		
	
		
			
				
					 34 int s_start(void) {
 
			
		
	
		
			
				
					 35   sprint("Enter supervisor mode...\n");
 
			
		
	
		
			
				
					 36   // Note: we use direct (i.e., Bare mode) for memory mapping in lab1.
 
			
		
	
		
			
				
					 37   // which means: Virtual Address = Physical Address
 
			
		
	
		
			
				
					 38   // therefore, we need to set satp to be 0 for now. we will enable paging in lab2_x.
 
			
		
	
		
			
				
					 39   //
 
			
		
	
		
			
				
					 40   // write_csr is a macro defined in kernel/riscv.h
 
			
		
	
		
			
				
					 41   write_csr(satp, 0);
 
			
		
	
		
			
				
					 42
 
			
		
	
		
			
				
					 43   // the application code (elf) is first loaded into memory, and then put into execution
 
			
		
	
		
			
				
					 44   load_user_program(&user_app); 
 
			
		
	
		
			
				
					 45
 
			
		
	
		
			
				
					 46   sprint("Switch to user mode...\n");
 
			
		
	
		
			
				
					 47   // switch_to() is defined in kernel/process.c
 
			
		
	
		
			
				
					 48   switch_to(&user_app); 
 
			
		
	
		
			
				
					 49
 
			
		
	
		
			
				
					 50   // we should never reach here.
 
			
		
	
		
			
				
					 51   return 0;
 
			
		
	
		
			
				
					 52 }
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					该函数的动作也非常简单: ( ) ( ) , ( ) ) , ( ) , 
 
			
		
	
		
			
				
					该函数的动作也非常简单: ( 1 行) ( ) , ( 44 行) ) , ( ) , 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					以上过程中, ( ) ( ) , , , ( ) : 
 
			
		
	
		
			
				
					
 
			
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
				@ -691,49 +701,54 @@ s_start函数在kernel/kernel.c文件中定义: 
 
			
		
	
		
			
				
					 29   assert(proc);
 
			
		
	
		
			
				
					 30   current = proc;
 
			
		
	
		
			
				
					 31
 
			
		
	
		
			
				
					 32   write_csr(stvec, (uint64)smode_trap_vector); 
 
			
		
	
		
			
				
					 33   // set up trapframe values that smode_trap_vector will need when 
 
			
		
	
		
			
				
					 34   // the process next re-enters the kernel .
 
			
		
	
		
			
				
					 35   proc->trapframe->kernel_sp = proc->kstack;  // process's kernel stack 
 
			
		
	
		
			
				
					 36   proc->trapframe->kernel_trap = (uint64)smode_trap_handler; 
 
			
		
	
		
			
				
					 37
 
			
		
	
		
			
				
					 38   // set up the registers that strap_vector.S's sret will use 
 
			
		
	
		
			
				
					 39   // to get to user space. 
 
			
		
	
		
			
				
					 40
 
			
		
	
		
			
				
					 41   // set S Previous Privilege mode to User. 
 
			
		
	
		
			
				
					 42   unsigned long x = read_csr(sstatus); 
 
			
		
	
		
			
				
					 43   x & = ~SSTATUS_SPP;  // clear SPP to 0 for user mode 
 
			
		
	
		
			
				
					 44   x |= SSTATUS_SPIE;  // enable interrupts in user mode 
 
			
		
	
		
			
				
					 45
 
			
		
	
		
			
				
					 46   write_csr(sstatus, x); 
 
			
		
	
		
			
				
					 32   // write the smode_trap_vector (64-bit func. address) defined in kernel/strap_vector.S 
 
			
		
	
		
			
				
					 33   // to the stvec privilege register, such that trap handler pointed by smode_trap_vector 
 
			
		
	
		
			
				
					 34   // will be triggered when an interrupt occurs in S mode .
 
			
		
	
		
			
				
					 35   write_csr(stvec, (uint64)smode_trap_vector); 
 
			
		
	
		
			
				
					 36
 
			
		
	
		
			
				
					 37   // set up trapframe values (in process structure) that smode_trap_vector will need when 
 
			
		
	
		
			
				
					 38   // the process next re-enters the kernel. 
 
			
		
	
		
			
				
					 39   proc->trapframe->kernel_sp = proc->kstack;  // process's kernel stack 
 
			
		
	
		
			
				
					 40   proc->trapframe->kernel_trap = (uint64)smode_trap_handler; 
 
			
		
	
		
			
				
					 41
 
			
		
	
		
			
				
					 42   // SSTATUS_SPP and SSTATUS_SPIE are defined in kernel/riscv.h 
 
			
		
	
		
			
				
					 43   // set S Previous Privilege mode (the SSTATUS_SPP bit in sstatus register) to User mode. 
 
			
		
	
		
			
				
					 44   unsigned long x = read_csr(sstatus); 
 
			
		
	
		
			
				
					 45   x & = ~SSTATUS_SPP;  // clear SPP to 0 for user mode 
 
			
		
	
		
			
				
					 46   x |= SSTATUS_SPIE;  // enable interrupts in user mode 
 
			
		
	
		
			
				
					 47
 
			
		
	
		
			
				
					 48   // set S Exception Program Counter to the saved user pc .
 
			
		
	
		
			
				
					 49   write_csr(sepc, proc->trapframe->epc );
 
			
		
	
		
			
				
					 48   // write x back to 'sstatus' register to enable interrupts, and sret destination mode .
 
			
		
	
		
			
				
					 49   write_csr(sstatus, x );
 
			
		
	
		
			
				
					 50
 
			
		
	
		
			
				
					 51   // switch to user mode with sret.
 
			
		
	
		
			
				
					 52   return_to_user(proc->trapframe);
 
			
		
	
		
			
				
					 53 }
 
			
		
	
		
			
				
					 51   // set S Exception Program Counter (sepc register) to the elf entry pc.
 
			
		
	
		
			
				
					 52   write_csr(sepc, proc->trapframe->epc);
 
			
		
	
		
			
				
					 53
 
			
		
	
		
			
				
					 54   // return_to_user() is defined in kernel/strap_vector.S. switch to user mode with sret.
 
			
		
	
		
			
				
					 55   return_to_user(proc->trapframe);
 
			
		
	
		
			
				
					 56 }
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					可以看到, , ( ) : 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					```assembly
 
			
		
	
		
			
				
					 45 .globl return_to_user
 
			
		
	
		
			
				
					 46 return_to_user:
 
			
		
	
		
			
				
					 47     # save a0 in sscratch, so sscratch points to a trapframe now.
 
			
		
	
		
			
				
					 48     csrw sscratch, a0
 
			
		
	
		
			
				
					 49
 
			
		
	
		
			
				
					 50     # let [t6]=[a0]
 
			
		
	
		
			
				
					 51     addi t6, a0, 0
 
			
		
	
		
			
				
					 52
 
			
		
	
		
			
				
					 53     # restore all registers from trapframe, so as to resort the execution of a process
 
			
		
	
		
			
				
					 54     restore_all_registers
 
			
		
	
		
			
				
					 55
 
			
		
	
		
			
				
					 56     # return to user mode and user pc.
 
			
		
	
		
			
				
					 57     sret
 
			
		
	
		
			
				
					 49 .globl return_to_user
 
			
		
	
		
			
				
					 50 return_to_user:
 
			
		
	
		
			
				
					 51     # [sscratch]=[a0], save a0 in sscratch, so sscratch points to a trapframe now.
 
			
		
	
		
			
				
					 52     csrw sscratch, a0
 
			
		
	
		
			
				
					 53
 
			
		
	
		
			
				
					 54     # let [t6]=[a0]
 
			
		
	
		
			
				
					 55     addi t6, a0, 0
 
			
		
	
		
			
				
					 56
 
			
		
	
		
			
				
					 57     # restore_all_registers is a assembly macro defined in util/load_store.S.
 
			
		
	
		
			
				
					 58     # the macro restores all registers from trapframe started from [t6] to all general
 
			
		
	
		
			
				
					 59     # purpose registers, so as to resort the execution of a process.
 
			
		
	
		
			
				
					 60     restore_all_registers
 
			
		
	
		
			
				
					 61
 
			
		
	
		
			
				
					 62     # return to user mode and user pc.
 
			
		
	
		
			
				
					 63     sret
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					其作用是恢复进程的上下文(54 行) , , ( ) , ( ) 
 
			
		
	
		
			
				
					其作用是恢复进程的上下文(60 行) , , ( ) , ( ) 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					
 
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -744,60 +759,65 @@ s_start函数在kernel/kernel.c文件中定义: 
 
			
		
	
		
			
				
					这里我们对load_user_program()函数进行讨论, : 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					```c
 
			
		
	
		
			
				
					 18 void load_user_program(process *proc) {
 
			
		
	
		
			
				
					 19   proc->trapframe = (trapframe *)USER_TRAP_FRAME;
 
			
		
	
		
			
				
					 20   memset(proc->trapframe, 0, sizeof(trapframe));
 
			
		
	
		
			
				
					 21   proc->kstack = USER_KSTACK;
 
			
		
	
		
			
				
					 22   proc->trapframe->regs.sp = USER_STACK;
 
			
		
	
		
			
				
					 23
 
			
		
	
		
			
				
					 24   load_bincode_from_host_elf(proc);
 
			
		
	
		
			
				
					 25 }
 
			
		
	
		
			
				
					 19 void load_user_program(process *proc) {
 
			
		
	
		
			
				
					 20   // USER_TRAP_FRAME is a physical address defined in kernel/config.h
 
			
		
	
		
			
				
					 21   proc->trapframe = (trapframe *)USER_TRAP_FRAME;
 
			
		
	
		
			
				
					 22   memset(proc->trapframe, 0, sizeof(trapframe));
 
			
		
	
		
			
				
					 23   // USER_KSTACK is also a physical address defined in kernel/config.h
 
			
		
	
		
			
				
					 24   proc->kstack = USER_KSTACK;
 
			
		
	
		
			
				
					 25   proc->trapframe->regs.sp = USER_STACK;
 
			
		
	
		
			
				
					 26
 
			
		
	
		
			
				
					 27   // load_bincode_from_host_elf() is defined in kernel/elf.c
 
			
		
	
		
			
				
					 28   load_bincode_from_host_elf(proc);
 
			
		
	
		
			
				
					 29 }
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					我们看到, , : 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					```c
 
			
		
	
		
			
				
					103 void load_bincode_from_host_elf(struct process *p) {
 
			
		
	
		
			
				
					104   arg_buf arg_bug_msg;
 
			
		
	
		
			
				
					105
 
			
		
	
		
			
				
					106   // retrieve command line arguements
 
			
		
	
		
			
				
					107   size_t argc = parse_args(&arg_bug_msg); 
 
			
		
	
		
			
				
					108   if (!argc) panic("You need to specify the application program!\n");
 
			
		
	
		
			
				
					107 void load_bincode_from_host_elf(process *p) {
 
			
		
	
		
			
				
					108   arg_buf arg_bug_msg;
 
			
		
	
		
			
				
					109
 
			
		
	
		
			
				
					110   sprint("Application: %s\n", arg_bug_msg.argv[0]); 
 
			
		
	
		
			
				
					111
 
			
		
	
		
			
				
					112   //elf loading 
 
			
		
	
		
			
				
					113   elf_ctx elfloader; 
 
			
		
	
		
			
				
					114   elf_info info ;
 
			
		
	
		
			
				
					110   // retrieve command line arguements
 
			
		
	
		
			
				
					111   size_t argc = parse_args(&arg_bug_msg); 
 
			
		
	
		
			
				
					112   if (!argc) panic("You need to specify the application program!\n");
 
			
		
	
		
			
				
					113
 
			
		
	
		
			
				
					114   sprint("Application: %s\n", arg_bug_msg.argv[0]) ;
 
			
		
	
		
			
				
					115
 
			
		
	
		
			
				
					116   info.f = spike_file_open(arg_bug_msg.argv[0], O_RDONLY, 0); 
 
			
		
	
		
			
				
					117   info.p = p ;
 
			
		
	
		
			
				
					118   if (IS_ERR_VALUE(info.f)) panic("Fail on openning the input application program.\n"); 
 
			
		
	
		
			
				
					119
 
			
		
	
		
			
				
					120   // init elfloader 
 
			
		
	
		
			
				
					121   if (elf_init(& elfloader, & info) != EL_OK) 
 
			
		
	
		
			
				
					122     panic("fail to init elfloader.\n") ;
 
			
		
	
		
			
				
					123
 
			
		
	
		
			
				
					124   // load elf 
 
			
		
	
		
			
				
					125   if (elf_load(& elfloader) != EL_OK) panic("Fail on loading elf.\n"); 
 
			
		
	
		
			
				
					126
 
			
		
	
		
			
				
					127   // entry (virtual) address 
 
			
		
	
		
			
				
					128   p->trapframe->epc = elfloader.ehdr.entry ;
 
			
		
	
		
			
				
					116   //elf loading. elf_ctx is defined in kernel/elf.h, used to track the loading process. 
 
			
		
	
		
			
				
					117   elf_ctx elfloader ;
 
			
		
	
		
			
				
					118   // elf_info is defined above, used to tie the elf file and its corresponding process. 
 
			
		
	
		
			
				
					119   elf_info info; 
 
			
		
	
		
			
				
					120
 
			
		
	
		
			
				
					121   info.f = spike_file_open(arg_bug_msg.argv[0], O_RDONLY, 0); 
 
			
		
	
		
			
				
					122   info.p = p ;
 
			
		
	
		
			
				
					123   // IS_ERR_VALUE is a macro defined in spike_interface/spike_htif.h 
 
			
		
	
		
			
				
					124   if (IS_ERR_VALUE(info.f)) panic("Fail on openning the input application program.\n"); 
 
			
		
	
		
			
				
					125
 
			
		
	
		
			
				
					126   // init elfloader context. elf_init() is defined above. 
 
			
		
	
		
			
				
					127   if (elf_init(& elfloader, & info) != EL_OK) 
 
			
		
	
		
			
				
					128     panic("fail to init elfloader.\n") ;
 
			
		
	
		
			
				
					129
 
			
		
	
		
			
				
					130   // close host file 
 
			
		
	
		
			
				
					131   spike_file_close( info.f  );
 
			
		
	
		
			
				
					130   // load elf. elf_load() is defined above. 
 
			
		
	
		
			
				
					131   if (elf_load(& elfloader) != EL_OK) panic("Fail on loading elf.\n" );
 
			
		
	
		
			
				
					132
 
			
		
	
		
			
				
					133   sprint("Application program entry point (virtual address): 0x%lx\n", p->trapframe->epc);
 
			
		
	
		
			
				
					134 }
 
			
		
	
		
			
				
					133   // entry (virtual, also physical in lab1_x) address
 
			
		
	
		
			
				
					134   p->trapframe->epc = elfloader.ehdr.entry;
 
			
		
	
		
			
				
					135
 
			
		
	
		
			
				
					136   // close the host spike file
 
			
		
	
		
			
				
					137   spike_file_close( info.f );
 
			
		
	
		
			
				
					138
 
			
		
	
		
			
				
					139   sprint("Application program entry point (virtual address): 0x%lx\n", p->trapframe->epc);
 
			
		
	
		
			
				
					140 }
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					该函数的大致过程是:
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					- ( ) , , ; 
 
			
		
	
		
			
				
					- ( ) , ; 
 
			
		
	
		
			
				
					- ( 25 行) ; 
 
			
		
	
		
			
				
					- ( 28 行) , , ; 
 
			
		
	
		
			
				
					- ( 1--133 行) 
 
			
		
	
		
			
				
					- ( 8--114 行) , , ; 
 
			
		
	
		
			
				
					- ( 7--128行) ,  初始化ELF加载数据结构, ; 
 
			
		
	
		
			
				
					- ( 31 行) ; 
 
			
		
	
		
			
				
					- ( 34 行) , , ; 
 
			
		
	
		
			
				
					- ( 7--139 行) 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					该函数用到了同文件中的诸多工具函数,这些函数的细节请读者自行阅读相关代码,这里我们只贴我们认为重要的代码:
 
			
		
	
		
			
				
					
 
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -806,28 +826,30 @@ s_start函数在kernel/kernel.c文件中定义: 
 
			
		
	
		
			
				
					- elf_load: ( ) : 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					```c
 
			
		
	
		
			
				
					 51 elf_status elf_load(elf_ctx *ctx) {
 
			
		
	
		
			
				
					 52   elf_prog_header ph_addr;
 
			
		
	
		
			
				
					 53   int i, off;
 
			
		
	
		
			
				
					 54   // traverse the elf program segment headers
 
			
		
	
		
			
				
					 55   for (i = 0, off = ctx->ehdr.phoff; i <  ctx- > ehdr.phnum; i++, off += sizeof(ph_addr)) {
 
			
		
	
		
			
				
					 56     // read segment headers
 
			
		
	
		
			
				
					 57     if (elf_fpread(ctx, (void *)& ph_addr, sizeof(ph_addr), off) != sizeof(ph_addr)) return EL_EIO;
 
			
		
	
		
			
				
					 58
 
			
		
	
		
			
				
					 59     if (ph_addr.type != ELF_PROG_LOAD) continue;
 
			
		
	
		
			
				
					 60     if (ph_addr.memsz <  ph_addr.filesz )  return  EL_ERR ; 
 
			
		
	
		
			
				
					 61     if (ph_addr.vaddr + ph_addr.memsz <  ph_addr.vaddr )  return  EL_ERR ; 
 
			
		
	
		
			
				
					 53 elf_status elf_load(elf_ctx *ctx) {
 
			
		
	
		
			
				
					 54   // elf_prog_header structure is defined in kernel/elf.h
 
			
		
	
		
			
				
					 55   elf_prog_header ph_addr;
 
			
		
	
		
			
				
					 56   int i, off;
 
			
		
	
		
			
				
					 57
 
			
		
	
		
			
				
					 58   // traverse the elf program segment headers
 
			
		
	
		
			
				
					 59   for (i = 0, off = ctx->ehdr.phoff; i <  ctx- > ehdr.phnum; i++, off += sizeof(ph_addr)) {
 
			
		
	
		
			
				
					 60     // read segment headers
 
			
		
	
		
			
				
					 61     if (elf_fpread(ctx, (void *)& ph_addr, sizeof(ph_addr), off) != sizeof(ph_addr)) return EL    _EIO;
 
			
		
	
		
			
				
					 62
 
			
		
	
		
			
				
					 63     // allocate memory before loading
 
			
		
	
		
			
				
					 64     void *dest = elf_alloc_mb(ctx, ph_addr.vaddr, ph_addr.vaddr, ph_addr.memsz);
 
			
		
	
		
			
				
					 65
 
			
		
	
		
			
				
					 66     // actual loading
 
			
		
	
		
			
				
					 67     if (elf_fpread(ctx, dest, ph_addr.memsz, ph_addr.off) != ph_addr.memsz)
 
			
		
	
		
			
				
					 68       return EL_EIO;
 
			
		
	
		
			
				
					 69   }
 
			
		
	
		
			
				
					 70
 
			
		
	
		
			
				
					 71   return EL_OK;
 
			
		
	
		
			
				
					 72 }
 
			
		
	
		
			
				
					 63     if (ph_addr.type != ELF_PROG_LOAD) continue;
 
			
		
	
		
			
				
					 64     if (ph_addr.memsz <  ph_addr.filesz )  return  EL_ERR ; 
 
			
		
	
		
			
				
					 65     if (ph_addr.vaddr + ph_addr.memsz <  ph_addr.vaddr )  return  EL_ERR ; 
 
			
		
	
		
			
				
					 66
 
			
		
	
		
			
				
					 67     // allocate memory block before elf loading
 
			
		
	
		
			
				
					 68     void *dest = elf_alloc_mb(ctx, ph_addr.vaddr, ph_addr.vaddr, ph_addr.memsz);
 
			
		
	
		
			
				
					 69
 
			
		
	
		
			
				
					 70     // actual loading
 
			
		
	
		
			
				
					 71     if (elf_fpread(ctx, dest, ph_addr.memsz, ph_addr.off) != ph_addr.memsz)
 
			
		
	
		
			
				
					 72       return EL_EIO;
 
			
		
	
		
			
				
					 73   }
 
			
		
	
		
			
				
					 74
 
			
		
	
		
			
				
					 75   return EL_OK;
 
			
		
	
		
			
				
					 76 }
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					这个函数里, , ( , : ) , , 
 
			
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
				@ -957,7 +979,7 @@ lab1_1实验需要读者了解和掌握操作系统中系统调用机制的实
 
			
		
	
		
			
				
					 27 }
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					我们发现, , , ( ) ( ) , ( ) ( ) : 
 
			
		
	
		
			
				
					我们发现, , , ( ) , ( ) , ( ) ( ) : 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					```assembly
 
			
		
	
		
			
				
					 16 .globl smode_trap_vector
 
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -968,29 +990,33 @@ lab1_1实验需要读者了解和掌握操作系统中系统调用机制的实
 
			
		
	
		
			
				
					 21
 
			
		
	
		
			
				
					 22     # save the context (user registers) of current process in its trapframe.
 
			
		
	
		
			
				
					 23     addi t6, a0 , 0
 
			
		
	
		
			
				
					 24     store_all_registers
 
			
		
	
		
			
				
					 25
 
			
		
	
		
			
				
					 26     # come back to save a0 register before entering trap handling in trapframe
 
			
		
	
		
			
				
					 27     csrr t0, sscratch
 
			
		
	
		
			
				
					 28     sd t0, 72(a0)
 
			
		
	
		
			
				
					 29
 
			
		
	
		
			
				
					 30     # use the "user kernel" stack (whose pointer stored in p->trapframe->kernel_sp)
 
			
		
	
		
			
				
					 31     ld sp, 248(a0)
 
			
		
	
		
			
				
					 32
 
			
		
	
		
			
				
					 33     # load the address of smode_trap_handler() from p->trapframe->kernel_trap
 
			
		
	
		
			
				
					 34     ld t0, 256(a0)
 
			
		
	
		
			
				
					 35
 
			
		
	
		
			
				
					 36     # jump to smode_trap_handler() that is defined in kernel/trap.c
 
			
		
	
		
			
				
					 37     jr t0
 
			
		
	
		
			
				
					 24
 
			
		
	
		
			
				
					 25     # store_all_registers is a macro defined in util/load_store.S, it stores contents
 
			
		
	
		
			
				
					 26     # of all general purpose registers into a piece of memory started from [t6].
 
			
		
	
		
			
				
					 27     store_all_registers
 
			
		
	
		
			
				
					 28
 
			
		
	
		
			
				
					 29     # come back to save a0 register before entering trap handling in trapframe
 
			
		
	
		
			
				
					 30     # [t0]=[sscratch]
 
			
		
	
		
			
				
					 31     csrr t0, sscratch
 
			
		
	
		
			
				
					 32     sd t0, 72(a0)
 
			
		
	
		
			
				
					 33
 
			
		
	
		
			
				
					 34     # use the "user kernel" stack (whose pointer stored in p->trapframe->kernel_sp)
 
			
		
	
		
			
				
					 35     ld sp, 248(a0)
 
			
		
	
		
			
				
					 36
 
			
		
	
		
			
				
					 37     # load the address of smode_trap_handler() from p->trapframe->kernel_trap
 
			
		
	
		
			
				
					 38     ld t0, 256(a0)
 
			
		
	
		
			
				
					 39
 
			
		
	
		
			
				
					 40     # jump to smode_trap_handler() that is defined in kernel/trap.c
 
			
		
	
		
			
				
					 41     jr t0
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					从以上代码我们可以看到, ( ) ( ) ; ( ) , ( ) , 
 
			
		
	
		
			
				
					从以上代码我们可以看到, ( ) ( 7行) ; (  行) , ( 5 行),**该过程实际上完成了栈的切换**。完整的切换过程为:
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					- 1) , ( ) , 
 
			
		
	
		
			
				
					- 2) , , ; 
 
			
		
	
		
			
				
					- 3) 1 行时,将栈切换到用户进程“自带”的“用户内核栈“,也就是`kernel/process.c`文件中`switch_to`函数的第35 行所引用的`proc->kstack`, , ? 
 
			
		
	
		
			
				
					- 3) 5 行时,将栈切换到用户进程“自带”的“用户内核栈“,也就是`kernel/process.c`文件中`switch_to`函数的第39 行所引用的`proc->kstack`, , ? 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					后续的执行将使用应用进程所附带的内核栈来保存执行的上下文, ; , ( 4行) , (  行) 
 
			
		
	
		
			
				
					后续的执行将使用应用进程所附带的内核栈来保存执行的上下文, ; , ( 8行) , (  行) 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					smode_trap_handler()函数的定义在kernel/strap.c文件中, : 
 
			
		
	
		
			
				
					
 
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -1004,21 +1030,22 @@ smode_trap_handler()函数的定义在kernel/strap.c文件中, 
 
			
		
	
		
			
				
					 39   // save user process counter.
 
			
		
	
		
			
				
					 40   current->trapframe->epc = read_csr(sepc);
 
			
		
	
		
			
				
					 41
 
			
		
	
		
			
				
					 42   // if the cause of trap is syscall from user application
 
			
		
	
		
			
				
					 43   if (read_csr(scause) == CAUSE_USER_ECALL) {
 
			
		
	
		
			
				
					 44     handle_syscall(current->trapframe);
 
			
		
	
		
			
				
					 45   } else {
 
			
		
	
		
			
				
					 46     sprint("smode_trap_handler(): unexpected scause %p\n", read_csr(scause));
 
			
		
	
		
			
				
					 47     sprint("            sepc=%p stval=%p\n", read_csr(sepc), read_csr(stval));
 
			
		
	
		
			
				
					 48     panic( "unexpected exception happened.\n" );
 
			
		
	
		
			
				
					 49   }
 
			
		
	
		
			
				
					 50
 
			
		
	
		
			
				
					 51   // continue the execution of current process.
 
			
		
	
		
			
				
					 52   switch_to(current);
 
			
		
	
		
			
				
					 53 }
 
			
		
	
		
			
				
					 42   // if the cause of trap is syscall from user application.
 
			
		
	
		
			
				
					 43   // read_csr() and CAUSE_USER_ECALL are macros defined in kernel/riscv.h
 
			
		
	
		
			
				
					 44   if (read_csr(scause) == CAUSE_USER_ECALL) {
 
			
		
	
		
			
				
					 45     handle_syscall(current->trapframe);
 
			
		
	
		
			
				
					 46   } else {
 
			
		
	
		
			
				
					 47     sprint("smode_trap_handler(): unexpected scause %p\n", read_csr(scause));
 
			
		
	
		
			
				
					 48     sprint("            sepc=%p stval=%p\n", read_csr(sepc), read_csr(stval));
 
			
		
	
		
			
				
					 49     panic( "unexpected exception happened.\n" );
 
			
		
	
		
			
				
					 50   }
 
			
		
	
		
			
				
					 51
 
			
		
	
		
			
				
					 52   // continue (come back to) the execution of current process.
 
			
		
	
		
			
				
					 53   switch_to(current);
 
			
		
	
		
			
				
					 54 }
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					该函数首先在第36行, ( ) , ( ) ; , ; ( 3--49 行的if...else...语句) , ( ) , ( , ) , ; , 2 行调用switch_to()函数返回当前进程。
 
			
		
	
		
			
				
					该函数首先在第36行, ( ) , ( ) ; , ; ( 4--50 行的if...else...语句) , ( ) , ( , ) , ; , 3 行调用switch_to()函数返回当前进程。
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					handle_syscall()函数的定义也在kernel/strap.c文件中: 
 
			
		
	
		
			
				
					
 
			
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
				@ -1179,63 +1206,73 @@ lab1_2实验需要读者了解和掌握操作系统中异常( ) 
 
			
		
	
		
			
				
					通过[3.1.5](#subsec_booting)节的阅读, , ? , , : 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					```c
 
			
		
	
		
			
				
					 51 static void delegate_traps() {
 
			
		
	
		
			
				
					 52   if (!supports_extension('S')) {
 
			
		
	
		
			
				
					 53     // confirm that our processor supports supervisor mode. abort if not.
 
			
		
	
		
			
				
					 54     sprint("s mode is not supported.\n");
 
			
		
	
		
			
				
					 55     return;
 
			
		
	
		
			
				
					 56   }
 
			
		
	
		
			
				
					 57
 
			
		
	
		
			
				
					 58   uintptr_t interrupts = MIP_SSIP | MIP_STIP | MIP_SEIP;
 
			
		
	
		
			
				
					 59   uintptr_t exceptions = (1U < <  CAUSE_MISALIGNED_FETCH )  |  ( 1U  < <  CAUSE_FETCH_PAGE_FAULT )  | 
 
			
		
	
		
			
				
					 60                          (1U < <  CAUSE_BREAKPOINT )  |  ( 1U  < <  CAUSE_LOAD_PAGE_FAULT )  | 
 
			
		
	
		
			
				
					 61                          (1U < <  CAUSE_STORE_PAGE_FAULT )  |  ( 1U  < <  CAUSE_USER_ECALL ) ; 
 
			
		
	
		
			
				
					 55 static void delegate_traps() {
 
			
		
	
		
			
				
					 56   // supports_extension macro is defined in kernel/riscv.h
 
			
		
	
		
			
				
					 57   if (!supports_extension('S')) {
 
			
		
	
		
			
				
					 58     // confirm that our processor supports supervisor mode. abort if it does not.
 
			
		
	
		
			
				
					 59     sprint("S mode is not supported.\n");
 
			
		
	
		
			
				
					 60     return;
 
			
		
	
		
			
				
					 61   }
 
			
		
	
		
			
				
					 62
 
			
		
	
		
			
				
					 63   write_csr(mideleg, interrupts);
 
			
		
	
		
			
				
					 64   write_csr(medeleg, exceptions);
 
			
		
	
		
			
				
					 65   assert(read_csr(mideleg) == interrupts);
 
			
		
	
		
			
				
					 66   assert(read_csr(medeleg) == exceptions);
 
			
		
	
		
			
				
					 67 }
 
			
		
	
		
			
				
					 63   // macros used in following two statements are defined in kernel/riscv.h
 
			
		
	
		
			
				
					 64   uintptr_t interrupts = MIP_SSIP | MIP_STIP | MIP_SEIP;
 
			
		
	
		
			
				
					 65   uintptr_t exceptions = (1U < <  CAUSE_MISALIGNED_FETCH )  |  ( 1U  < <  CAUSE_FETCH_PAGE_FAULT )  | 
 
			
		
	
		
			
				
					 66                          (1U < <  CAUSE_BREAKPOINT )  |  ( 1U  < <  CAUSE_LOAD_PAGE_FAULT )  | 
 
			
		
	
		
			
				
					 67                          (1U < <  CAUSE_STORE_PAGE_FAULT )  |  ( 1U  < <  CAUSE_USER_ECALL ) ; 
 
			
		
	
		
			
				
					 68
 
			
		
	
		
			
				
					 69   // writes 64-bit values (interrupts and exceptions) to 'mideleg' and 'medeleg' (two
 
			
		
	
		
			
				
					 70   // priviledged registers of RV64G machine) respectively.
 
			
		
	
		
			
				
					 71   //
 
			
		
	
		
			
				
					 72   // write_csr and read_csr are macros defined in kernel/riscv.h
 
			
		
	
		
			
				
					 73   write_csr(mideleg, interrupts);
 
			
		
	
		
			
				
					 74   write_csr(medeleg, exceptions);
 
			
		
	
		
			
				
					 75   assert(read_csr(mideleg) == interrupts);
 
			
		
	
		
			
				
					 76   assert(read_csr(medeleg) == exceptions);
 
			
		
	
		
			
				
					 77 }
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					在第58--61行的代码中, , , ( , , ) ! , , ( ( ) ) : 
 
			
		
	
		
			
				
					在第64--67 行的代码中, , , ( , , ) ! , , ( ( 04 行`write_csr(mtvec, (uint64)mtrapvec);`) ) : 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					```assembly
 
			
		
	
		
			
				
					  8 mtrapvec:
 
			
		
	
		
			
				
					  9     # swap a0 and mscratch
 
			
		
	
		
			
				
					 10     # so that a0 points to interrupt frame
 
			
		
	
		
			
				
					 11     csrrw a0, mscratch, a0
 
			
		
	
		
			
				
					 12
 
			
		
	
		
			
				
					 13     # save the registers in interrupt frame 
 
			
		
	
		
			
				
					 14     addi t6, a0, 0 
 
			
		
	
		
			
				
					 15     store_all_registers 
 
			
		
	
		
			
				
					 16     # save the user a0 in itrframe->a0 
 
			
		
	
		
			
				
					 17     csrr t0, mscratch 
 
			
		
	
		
			
				
					 18     sd t0, 72(a0) 
 
			
		
	
		
			
				
					 19
 
			
		
	
		
			
				
					 20     # use stack0 for sp 
 
			
		
	
		
			
				
					 21     la sp, stack0 
 
			
		
	
		
			
				
					 22     li a3, 4096 
 
			
		
	
		
			
				
					 23     csrr a4, mhartid 
 
			
		
	
		
			
				
					 24     addi a4, a4, 1 
 
			
		
	
		
			
				
					 25     mul a3, a3, a4 
 
			
		
	
		
			
				
					 26     add sp, sp, a3 
 
			
		
	
		
			
				
					 27
 
			
		
	
		
			
				
					 28     // save the address of interrupt frame in the csr "mscratch" 
 
			
		
	
		
			
				
					 29     csrw mscratch, a0 
 
			
		
	
		
			
				
					 30
 
			
		
	
		
			
				
					 31     call handle_mtrap 
 
			
		
	
		
			
				
					  9     # mscratch -> g_itrframe (cf. kernel/machine/minit.c line 94) 
 
			
		
	
		
			
				
					 10     # swap a0 and mscratch, s o that a0 points to interrupt frame, 
 
			
		
	
		
			
				
					 11     # i.e., [a0] = & g_itrframe 
 
			
		
	
		
			
				
					 12     csrrw a0, mscratch, a0 
 
			
		
	
		
			
				
					 13
 
			
		
	
		
			
				
					 14     # save the registers in g_itrframe 
 
			
		
	
		
			
				
					 15     addi t6, a0, 0 
 
			
		
	
		
			
				
					 16     store_all_registers 
 
			
		
	
		
			
				
					 17     # save the original content of a0 in g_itrframe 
 
			
		
	
		
			
				
					 18     csrr t0, mscratch 
 
			
		
	
		
			
				
					 19     sd t0, 72(a0) 
 
			
		
	
		
			
				
					 20
 
			
		
	
		
			
				
					 21     # switch stack (to use stack0) for the rest of machine mode 
 
			
		
	
		
			
				
					 22     # trap handling. 
 
			
		
	
		
			
				
					 23     la sp, stack0 
 
			
		
	
		
			
				
					 24     li a3, 4096 
 
			
		
	
		
			
				
					 25     csrr a4, mhartid 
 
			
		
	
		
			
				
					 26     addi a4, a4, 1 
 
			
		
	
		
			
				
					 27     mul a3, a3, a4 
 
			
		
	
		
			
				
					 28     add sp, sp, a3 
 
			
		
	
		
			
				
					 29
 
			
		
	
		
			
				
					 30     # pointing mscratch back to g_itrframe 
 
			
		
	
		
			
				
					 31     csrw mscratch, a0 
 
			
		
	
		
			
				
					 32
 
			
		
	
		
			
				
					 33     // restore all registers
 
			
		
	
		
			
				
					 34     csrr t6, mscratch
 
			
		
	
		
			
				
					 35     restore_all_registers
 
			
		
	
		
			
				
					 36
 
			
		
	
		
			
				
					 37     mret
 
			
		
	
		
			
				
					 33     # call machine mode trap handling function
 
			
		
	
		
			
				
					 34     call handle_mtrap
 
			
		
	
		
			
				
					 35
 
			
		
	
		
			
				
					 36     # restore all registers, come back to the status before entering
 
			
		
	
		
			
				
					 37     # machine mode handling.
 
			
		
	
		
			
				
					 38     csrr t6, mscratch
 
			
		
	
		
			
				
					 39     restore_all_registers
 
			
		
	
		
			
				
					 40
 
			
		
	
		
			
				
					 41     mret
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					可以看到, , ( , ) , ( ) , ( ) , , 
 
			
		
	
		
			
				
					可以看到, , ( 31行` riscv_regs g_itrframe;`, ) , ( 5 行) , ( 6 行的store_all_registers完成) , , 8-19 行是保护进入中断处理前a0寄存器的值到g_itrframe。
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					接下来, 1--26行切换栈到stack0( ) ,  行调用handle_mtrap()函数。handle_mtrap()函数在kernel/machine/mtrap.c文件中定义: 
 
			
		
	
		
			
				
					接下来, 3--28行切换栈到stack0( ) ,  行调用handle_mtrap()函数。handle_mtrap()函数在kernel/machine/mtrap.c文件中定义: 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					```c
 
			
		
	
		
			
				
					 20 void handle_mtrap() {
 
			
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
				@ -1281,6 +1318,8 @@ lab1_2实验需要读者了解和掌握操作系统中异常( ) 
 
			
		
	
		
			
				
					$ git commit -a -m "my work on lab1_2 is done."
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					< a  name = "irq" > < / a >  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					## 3.4 lab1_3 (外部)中断 
 
			
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
				@ -1411,154 +1450,163 @@ System is shutting down with exit code 0.
 
			
		
	
		
			
				
					- 在m_start函数( ) , : 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					```c
 
			
		
	
		
			
				
					 7 2 void timerinit(uintptr_t hartid) {
 
			
		
	
		
			
				
					 7 3   // fire timer irq after TIMER_INTERVAL from now.
 
			
		
	
		
			
				
					 7 4   *(uint64* )CLINT_MTIMECMP(hartid) = *(uint64* )CLINT_MTIME + TIMER_INTERVAL;
 
			
		
	
		
			
				
					 7 5
 
			
		
	
		
			
				
					 7 6   // enable machine-mode timer irq in MIE (Machine Interrupt Enable) csr.
 
			
		
	
		
			
				
					 7 7   write_csr(mie, read_csr(mie) | MIE_MTIE);
 
			
		
	
		
			
				
					 7 8 }
 
			
		
	
		
			
				
					 8 2 void timerinit(uintptr_t hartid) {
 
			
		
	
		
			
				
					 8 3   // fire timer irq after TIMER_INTERVAL from now.
 
			
		
	
		
			
				
					 8 4   *(uint64* )CLINT_MTIMECMP(hartid) = *(uint64* )CLINT_MTIME + TIMER_INTERVAL;
 
			
		
	
		
			
				
					 8 5
 
			
		
	
		
			
				
					 8 6   // enable machine-mode timer irq in MIE (Machine Interrupt Enable) csr.
 
			
		
	
		
			
				
					 8 7   write_csr(mie, read_csr(mie) | MIE_MTIE);
 
			
		
	
		
			
				
					 8 8 }
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					该函数首先在7 4行设置了下一次timer触发的时间, ( , ) , 7 7行设置了MIE( , ) , ( ) 
 
			
		
	
		
			
				
					该函数首先在8 4行设置了下一次timer触发的时间, ( , ) , 8 7行设置了MIE( , ) , ( ) 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					时钟中断触发后, : 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					```assembly
 
			
		
	
		
			
				
					  8 mtrapvec:
 
			
		
	
		
			
				
					  9     # swap a0 and mscratch 
 
			
		
	
		
			
				
					 10     # so that a0 points to interrupt frame
 
			
		
	
		
			
				
					 11     csrrw a0, mscratch, a0 
 
			
		
	
		
			
				
					 12
 
			
		
	
		
			
				
					 13     # save the registers in interrupt frame 
 
			
		
	
		
			
				
					 14     addi t6, a0, 0 
 
			
		
	
		
			
				
					 15     store_all_registers 
 
			
		
	
		
			
				
					 16     # save the user a0 in itrframe->a0 
 
			
		
	
		
			
				
					 17     csrr t0, mscratch 
 
			
		
	
		
			
				
					 18     sd t0, 72(a0) 
 
			
		
	
		
			
				
					 19
 
			
		
	
		
			
				
					 20     # use stack0 for sp 
 
			
		
	
		
			
				
					 21     la sp, stack0 
 
			
		
	
		
			
				
					 22     li a3, 4096 
 
			
		
	
		
			
				
					 23     csrr a4, mhartid 
 
			
		
	
		
			
				
					 24     addi a4, a4, 1 
 
			
		
	
		
			
				
					 25     mul a3, a3, a4 
 
			
		
	
		
			
				
					 26     add sp, sp, a3 
 
			
		
	
		
			
				
					 27
 
			
		
	
		
			
				
					 28     // save the address of interrupt frame in the csr "mscratch" 
 
			
		
	
		
			
				
					 29     csrw mscratch, a0 
 
			
		
	
		
			
				
					 30
 
			
		
	
		
			
				
					 31     call handle_mtrap 
 
			
		
	
		
			
				
					  9     # mscratch -> g_itrframe (cf. kernel/machine/minit.c line 94) 
 
			
		
	
		
			
				
					 10     # swap a0 and mscratch, s o that a0 points to interrupt frame, 
 
			
		
	
		
			
				
					 11     # i.e., [a0] = & g_itrframe 
 
			
		
	
		
			
				
					 12     csrrw a0, mscratch, a0 
 
			
		
	
		
			
				
					 13
 
			
		
	
		
			
				
					 14     # save the registers in g_itrframe 
 
			
		
	
		
			
				
					 15     addi t6, a0, 0 
 
			
		
	
		
			
				
					 16     store_all_registers 
 
			
		
	
		
			
				
					 17     # save the original content of a0 in g_itrframe 
 
			
		
	
		
			
				
					 18     csrr t0, mscratch 
 
			
		
	
		
			
				
					 19     sd t0, 72(a0) 
 
			
		
	
		
			
				
					 20
 
			
		
	
		
			
				
					 21     # switch stack (to use stack0) for the rest of machine mode 
 
			
		
	
		
			
				
					 22     # trap handling. 
 
			
		
	
		
			
				
					 23     la sp, stack0 
 
			
		
	
		
			
				
					 24     li a3, 4096 
 
			
		
	
		
			
				
					 25     csrr a4, mhartid 
 
			
		
	
		
			
				
					 26     addi a4, a4, 1 
 
			
		
	
		
			
				
					 27     mul a3, a3, a4 
 
			
		
	
		
			
				
					 28     add sp, sp, a3 
 
			
		
	
		
			
				
					 29
 
			
		
	
		
			
				
					 30     # pointing mscratch back to g_itrframe 
 
			
		
	
		
			
				
					 31     csrw mscratch, a0 
 
			
		
	
		
			
				
					 32
 
			
		
	
		
			
				
					 33     // restore all registers
 
			
		
	
		
			
				
					 34     csrr t6, mscratch
 
			
		
	
		
			
				
					 35     restore_all_registers
 
			
		
	
		
			
				
					 36
 
			
		
	
		
			
				
					 37     mret
 
			
		
	
		
			
				
					 33     # call machine mode trap handling function
 
			
		
	
		
			
				
					 34     call handle_mtrap
 
			
		
	
		
			
				
					 35
 
			
		
	
		
			
				
					 36     # restore all registers, come back to the status before entering
 
			
		
	
		
			
				
					 37     # machine mode handling.
 
			
		
	
		
			
				
					 38     csrr t6, mscratch
 
			
		
	
		
			
				
					 39     restore_all_registers
 
			
		
	
		
			
				
					 40
 
			
		
	
		
			
				
					 41     mret
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					和lab1_2一样, , ( ) , 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					```c
 
			
		
	
		
			
				
					 17  static void handle_timer() {
 
			
		
	
		
			
				
					 18    int cpuid = 0;
 
			
		
	
		
			
				
					 19    // setup the timer fired at next time (TIMER_INTERVAL from now)
 
			
		
	
		
			
				
					 20    *(uint64* )CLINT_MTIMECMP(cpuid) = *(uint64* )CLINT_MTIMECMP(cpuid) + TIMER_INTERVAL;
 
			
		
	
		
			
				
					 21 
 
			
		
	
		
			
				
					 22    // setup a soft interrupt in sip (S-mode Interrupt Pending) to be handled in S-mode
 
			
		
	
		
			
				
					 23    write_csr(sip, SIP_SSIP);
 
			
		
	
		
			
				
					 24  }
 
			
		
	
		
			
				
					 25 
 
			
		
	
		
			
				
					 26  //
 
			
		
	
		
			
				
					 27 // handle_mtrap calls cooresponding functions to handle an exception of a given type .
 
			
		
	
		
			
				
					 28  //
 
			
		
	
		
			
				
					 29  void handle_mtrap() {
 
			
		
	
		
			
				
					 30    uint64 mcause = read_csr(mcause);
 
			
		
	
		
			
				
					 31    switch (mcause) {
 
			
		
	
		
			
				
					 32      case CAUSE_MTIMER:
 
			
		
	
		
			
				
					 33        handle_timer();
 
			
		
	
		
			
				
					 34        break;
 
			
		
	
		
			
				
					 35      case CAUSE_FETCH_ACCESS:
 
			
		
	
		
			
				
					 36        handle_instruction_access_fault();
 
			
		
	
		
			
				
					 37        break;
 
			
		
	
		
			
				
					 38      case CAUSE_LOAD_ACCESS:
 
			
		
	
		
			
				
					 39        handle_load_access_fault();
 
			
		
	
		
			
				
					 40      case CAUSE_STORE_ACCESS:
 
			
		
	
		
			
				
					 41        handle_store_access_fault();
 
			
		
	
		
			
				
					 42        break;
 
			
		
	
		
			
				
					 43      case CAUSE_ILLEGAL_INSTRUCTION:
 
			
		
	
		
			
				
					 44        // TODO (lab1_2): call handle_illegal_instruction to implement illegal instruction
 
			
		
	
		
			
				
					 45        // interception, and finish lab1_2.
 
			
		
	
		
			
				
					 46        panic( "call handle_illegal_instruction to accomplish illegal instruction interception for lab1_2.\n" );
 
			
		
	
		
			
				
					 47 
 
			
		
	
		
			
				
					 48        break;
 
			
		
	
		
			
				
					 49      case CAUSE_MISALIGNED_LOAD:
 
			
		
	
		
			
				
					 50        handle_misaligned_load();
 
			
		
	
		
			
				
					 51        break;
 
			
		
	
		
			
				
					 52      case CAUSE_MISALIGNED_STORE:
 
			
		
	
		
			
				
					 53        handle_misaligned_store();
 
			
		
	
		
			
				
					 54        break;
 
			
		
	
		
			
				
					 55 
 
			
		
	
		
			
				
					 56      default:
 
			
		
	
		
			
				
					 57        sprint("machine trap(): unexpected mscause %p\n", mcause);
 
			
		
	
		
			
				
					 58        sprint("            mepc=%p mtval=%p\n", read_csr(mepc), read_csr(mtval));
 
			
		
	
		
			
				
					 59        panic( "unexpected exception happened in M-mode.\n" );
 
			
		
	
		
			
				
					 60        break;
 
			
		
	
		
			
				
					 61    }
 
			
		
	
		
			
				
					 62  }
 
			
		
	
		
			
				
					 18  static void handle_timer() {
 
			
		
	
		
			
				
					 19    int cpuid = 0;
 
			
		
	
		
			
				
					 20    // setup the timer fired at next time (TIMER_INTERVAL from now)
 
			
		
	
		
			
				
					 21    *(uint64* )CLINT_MTIMECMP(cpuid) = *(uint64* )CLINT_MTIMECMP(cpuid) + TIMER_INTERVAL;
 
			
		
	
		
			
				
					 22 
 
			
		
	
		
			
				
					 23    // setup a soft interrupt in sip (S-mode Interrupt Pending) to be handled in S-mode
 
			
		
	
		
			
				
					 24    write_csr(sip, SIP_SSIP);
 
			
		
	
		
			
				
					 25  }
 
			
		
	
		
			
				
					 26 
 
			
		
	
		
			
				
					 27  //
 
			
		
	
		
			
				
					 28 // handle_mtrap calls a handling function according to the type of a machine mode interrupt (    trap) .
 
			
		
	
		
			
				
					 29  //
 
			
		
	
		
			
				
					 30  void handle_mtrap() {
 
			
		
	
		
			
				
					 31    uint64 mcause = read_csr(mcause);
 
			
		
	
		
			
				
					 32    switch (mcause) {
 
			
		
	
		
			
				
					 33      case CAUSE_MTIMER:
 
			
		
	
		
			
				
					 34        handle_timer();
 
			
		
	
		
			
				
					 35        break;
 
			
		
	
		
			
				
					 36      case CAUSE_FETCH_ACCESS:
 
			
		
	
		
			
				
					 37        handle_instruction_access_fault();
 
			
		
	
		
			
				
					 38        break;
 
			
		
	
		
			
				
					 39      case CAUSE_LOAD_ACCESS:
 
			
		
	
		
			
				
					 40        handle_load_access_fault();
 
			
		
	
		
			
				
					 41      case CAUSE_STORE_ACCESS:
 
			
		
	
		
			
				
					 42        handle_store_access_fault();
 
			
		
	
		
			
				
					 43        break;
 
			
		
	
		
			
				
					 44      case CAUSE_ILLEGAL_INSTRUCTION:
 
			
		
	
		
			
				
					 45        // TODO (lab1_2): call handle_illegal_instruction to implement illegal instruction
 
			
		
	
		
			
				
					 46        // interception, and finish lab1_2.
 
			
		
	
		
			
				
					 47        panic( "call handle_illegal_instruction to accomplish illegal instruction interception      for lab1_2.\n" );
 
			
		
	
		
			
				
					 48 
 
			
		
	
		
			
				
					 49        break;
 
			
		
	
		
			
				
					 50      case CAUSE_MISALIGNED_LOAD:
 
			
		
	
		
			
				
					 51        handle_misaligned_load();
 
			
		
	
		
			
				
					 52        break;
 
			
		
	
		
			
				
					 53      case CAUSE_MISALIGNED_STORE:
 
			
		
	
		
			
				
					 54        handle_misaligned_store();
 
			
		
	
		
			
				
					 55        break;
 
			
		
	
		
			
				
					 56 
 
			
		
	
		
			
				
					 57      default:
 
			
		
	
		
			
				
					 58        sprint("machine trap(): unexpected mscause %p\n", mcause);
 
			
		
	
		
			
				
					 59        sprint("            mepc=%p mtval=%p\n", read_csr(mepc), read_csr(mtval));
 
			
		
	
		
			
				
					 60        panic( "unexpected exception happened in M-mode.\n" );
 
			
		
	
		
			
				
					 61        break;
 
			
		
	
		
			
				
					 62    }
 
			
		
	
		
			
				
					 63  }
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					而handle_timer()函数会( 0行) ( ) ,  行对SIP( , ) , , , , , ( ) 
 
			
		
	
		
			
				
					而handle_timer()函数会( 1行) ( ) ,  行对SIP( , ) , , , , , ( ) 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					那么为什么操作系统内核不在M态就完成对时钟中断的处理, ? , , , ( ) , ( ) , , , , , 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					接下来, , : 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					```c
 
			
		
	
		
			
				
					 45 void smode_trap_handler(void) {
 
			
		
	
		
			
				
					 46   // make sure we are in User mode before entering the trap handling.
 
			
		
	
		
			
				
					 47   // we will consider other previous case in lab1_3 (interrupt).
 
			
		
	
		
			
				
					 48   if ((read_csr(sstatus) &  SSTATUS_SPP) != 0) panic("usertrap: not from user mode");
 
			
		
	
		
			
				
					 49
 
			
		
	
		
			
				
					 50   assert(current);
 
			
		
	
		
			
				
					 51   // save user process counter.
 
			
		
	
		
			
				
					 52   current->trapframe->epc = read_csr(sepc);
 
			
		
	
		
			
				
					 53
 
			
		
	
		
			
				
					 54   // if the cause of trap is syscall from user application
 
			
		
	
		
			
				
					 55   uint64 cause = read_csr(scause);
 
			
		
	
		
			
				
					 48 void smode_trap_handler(void) {
 
			
		
	
		
			
				
					 49   // make sure we are in User mode before entering the trap handling.
 
			
		
	
		
			
				
					 50   // we will consider other previous case in lab1_3 (interrupt).
 
			
		
	
		
			
				
					 51   if ((read_csr(sstatus) &  SSTATUS_SPP) != 0) panic("usertrap: not from user mode");
 
			
		
	
		
			
				
					 52
 
			
		
	
		
			
				
					 53   assert(current);
 
			
		
	
		
			
				
					 54   // save user process counter.
 
			
		
	
		
			
				
					 55   current->trapframe->epc = read_csr(sepc);
 
			
		
	
		
			
				
					 56
 
			
		
	
		
			
				
					 57   if (cause == CAUSE_USER_ECALL) {
 
			
		
	
		
			
				
					 58     handle_syscall(current->trapframe);
 
			
		
	
		
			
				
					 59   } else if (cause == CAUSE_MTIMER_S_TRAP) {  //soft trap generated by timer interrupt in M mode
 
			
		
	
		
			
				
					 60     handle_mtimer_trap();
 
			
		
	
		
			
				
					 61   } else {
 
			
		
	
		
			
				
					 62     sprint("smode_trap_handler(): unexpected scause %p\n", read_csr(scause));
 
			
		
	
		
			
				
					 63     sprint("            sepc=%p stval=%p\n", read_csr(sepc), read_csr(stval));
 
			
		
	
		
			
				
					 64     panic( "unexpected exception happened.\n" );
 
			
		
	
		
			
				
					 65   }
 
			
		
	
		
			
				
					 66
 
			
		
	
		
			
				
					 67   // continue the execution of current process.
 
			
		
	
		
			
				
					 68   switch_to(current);
 
			
		
	
		
			
				
					 69 }
 
			
		
	
		
			
				
					 57   // if the cause of trap is syscall from user application.
 
			
		
	
		
			
				
					 58   // read_csr() and CAUSE_USER_ECALL are macros defined in kernel/riscv.h
 
			
		
	
		
			
				
					 59   uint64 cause = read_csr(scause);
 
			
		
	
		
			
				
					 60
 
			
		
	
		
			
				
					 61   // we need to handle the timer trap @lab1_3 .
 
			
		
	
		
			
				
					 62   if (cause == CAUSE_USER_ECALL) {
 
			
		
	
		
			
				
					 63     handle_syscall(current->trapframe);
 
			
		
	
		
			
				
					 64   } else if (cause == CAUSE_MTIMER_S_TRAP) {  //soft trap generated by timer interrupt in M m    ode
 
			
		
	
		
			
				
					 65     handle_mtimer_trap();
 
			
		
	
		
			
				
					 66   } else {
 
			
		
	
		
			
				
					 67     sprint("smode_trap_handler(): unexpected scause %p\n", read_csr(scause));
 
			
		
	
		
			
				
					 68     sprint("            sepc=%p stval=%p\n", read_csr(sepc), read_csr(stval));
 
			
		
	
		
			
				
					 69     panic( "unexpected exception happened.\n" );
 
			
		
	
		
			
				
					 70   }
 
			
		
	
		
			
				
					 71
 
			
		
	
		
			
				
					 72   // continue (come back to) the execution of current process.
 
			
		
	
		
			
				
					 73   switch_to(current);
 
			
		
	
		
			
				
					 74 }
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					我们看到, , , , , 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					```c
 
			
		
	
		
			
				
					 31 static uint64 g_ticks = 0;
 
			
		
	
		
			
				
					 32 void handle_mtimer_trap() {
 
			
		
	
		
			
				
					 33   sprint("Ticks %d\n", g_ticks);
 
			
		
	
		
			
				
					 34   // TODO (lab1_3): increase g_ticks to record this "tick", and then clear the "SIP"
 
			
		
	
		
			
				
					 35   // field in sip register.
 
			
		
	
		
			
				
					 36   // hint: use write_csr to disable the SIP_SSIP bit in sip.
 
			
		
	
		
			
				
					 37   panic( "lab1_3: increase g_ticks by one, and clear SIP field in sip register.\n" );
 
			
		
	
		
			
				
					 38
 
			
		
	
		
			
				
					 39 }
 
			
		
	
		
			
				
					 32 //
 
			
		
	
		
			
				
					 33 // added @lab1_3 
 
			
		
	
		
			
				
					 34 //
 
			
		
	
		
			
				
					 35 void handle_mtimer_trap() {
 
			
		
	
		
			
				
					 36   sprint("Ticks %d\n", g_ticks);
 
			
		
	
		
			
				
					 37   // TODO (lab1_3): increase g_ticks to record this "tick", and then clear the "SIP"
 
			
		
	
		
			
				
					 38   // field in sip register.
 
			
		
	
		
			
				
					 39   // hint: use write_csr to disable the SIP_SSIP bit in sip.
 
			
		
	
		
			
				
					 40   panic( "lab1_3: increase g_ticks by one, and clear SIP field in sip register.\n" );
 
			
		
	
		
			
				
					 41
 
			
		
	
		
			
				
					 42 }
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					至此,我们就知道为什么会在之前看到`lab1_3: increase g_ticks by one, and clear SIP field in sip register.`这样的输出了, 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					那么handle_mtimer_trap()需要完成哪些“后续动作”呢? , , , 3 行会输出该计数。为了确保我们的系统持续正常运行, , ; , , ( , ) ( ) , , , , 
 
			
		
	
		
			
				
					那么handle_mtimer_trap()需要完成哪些“后续动作”呢? , , , 6 行会输出该计数。为了确保我们的系统持续正常运行, , ; , , ( , ) ( ) , , , , 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					**实验完毕后,记得提交修改(命令行中-m后的字符串可自行确定) , : 
 
			
		
	
		
			
				
					
 
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -1566,6 +1614,8 @@ System is shutting down with exit code 0.
 
			
		
	
		
			
				
					$ git commit -a -m "my work on lab1_3 is done."
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					< a  name = "lab1_challenge1_backtrace" > < / a >  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					## 3.5 lab1_challenge1 打印用户程序调用栈(难度:★ ★ ★ ☆ ☆ ) 
 
			
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
				@ -1717,8 +1767,8 @@ $ riscv64-unknown-elf-objdump -d obj/app_print_backtrace
 
			
		
	
		
			
				
					在`elf_init`函数中完成了`elf header`的加载:
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					```C
 
			
		
	
		
			
				
					42   // load the elf header
 
			
		
	
		
			
				
					43   if (elf_fpread(ctx, & ctx->ehdr, sizeof(ctx->ehdr), 0) != sizeof(ctx->ehdr)) return EL_EIO;
 
			
		
	
		
			
				
					41   // load the elf header
 
			
		
	
		
			
				
					42   if (elf_fpread(ctx, & ctx->ehdr, sizeof(ctx->ehdr), 0) != sizeof(ctx->ehdr)) return EL_EIO;
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					Section Header Table可以认为是一个线性结构, , , ,