作業一

項目一:
截圖瞬間+暫存器 ax cx bx 文字敘述

截圖瞬間如下:

kernel執行到0x7c00,當下暫存器:


項目二:
重要概念總結

Chapter 0

Chapter 1


項目三:
Chapter 3 Traps, interrupts, and drivers
總結

1.trapasm.S
IDT加載和entry建立完成后,xv6通過硬體建立一個task segment descriptor,給暫存器%esp賦值并加載一個target segment selector,switchuvm() 將kernel中的user process stack的頂部的stack address儲存在task segment descriptor中,若processor處於user模式,則從task segment descriptor中加載%ss和%esp,若在kernel模式,則不進行任何處理。接著,processor push %eflags、%cs、%eip,在某些trap中還會push error code。最後,processor從IDT入口加載%eip和%cs,call vectors.pl文件打開IDT入口并跳轉到該文件。

· Build trap frame
當processor push ss,esp,eflags,cs,eip 后,alltraps push ds,ed,fs,gs 和剩餘32個通用暫存器

alltraps: 
pushl %ds 
pushl %es 
pushl %fs 
pushl %gs 
pushal  #push剩餘32通用暫存器

· Set up data segments
通過 alltraps ,暫存器 %ds, %es 含有指向data segment的指針值。

movw $(SEG_KDATA<<3), %ax
movw %ax, %ds
movw %ax, %es

· Call trap(tf)
此時暫存器已建立,可通過暫存器獲取指向segment的指針,暫存器esp存有指向trap frame的指針值。將該指針值傳入 trap 中,并call trap ,執行完 trap 后,將esp值+4,彈出棧頂元素后,esp指向trapret。

 pushl %esp  # tf=%esp,將指針傳入trap 
 call trap 
 addl $4, %esp  #此時esp指向trapret

· 通過trapret恢復用戶進程現場
這段代碼在將32位通用暫存器及其它暫存器pop后,將esp值增加8,這樣將會pop trapno 和 errcode,全部出棧后,執行 iret ,跳轉到用戶程序

.globl trapret 
trapret: 
popal 
popl %gs 
popl %fs 
popl %es 
popl %ds 
addl $0x8, %esp  #彈出trapno 和 errcode 
iret 

2. trap.c

· tvinit()
用於建立IDT的256個入口,最後的 initlock init一個互斥鎖。

tvinit(void) 
{ 
int i; 
for(i = 0; i < 256; i++) 
// idt[i] :要建立的interrupt gate,
//0 :istrap 位,0表示是一個interrupt gate
//SEG_KCODE<<3 :是一個code segment selector的值,操作后為8
//vectors[i] :offset的值,這裡為interrupt 程序的地址
//0 :descriptor privilege level
SETGATE(idt[i], 0, SEG_KCODE<<3, vectors[i], 0);   
SETGATE(idt[T_SYSCALL], 1, SEG_KCODE<<3, vectors[T_SYSCALL], DPL_USER); 

initlock(&tickslock, "time"); 
} 

· idtinit()
用於call x86.h中的lidt(),加載IDT。

 void idtinit(void) 
 { 
 lidt(idt, sizeof(idt)); 
 } 

·trap(struct trapframe tf)

 if(tf−>trapno == T_SYSCALL){ 
     if(myproc()−>killed) 
       exit(); 
     myproc()−>tf = tf; 
     syscall(); 
     if(myproc()−>killed) 
       exit(); 
     return; 
     } 
case T_IRQ0 + IRQ_TIMER: 
    if(cpuid() == 0){           
     acquire(&tickslock);    //互斥操作
     ticks++;    //如果cpu值為0 ,ticks值+1
     wakeup(&ticks);    //call wakeup,喚醒處於sleeping狀態的進程,并設置為RUNNABLE
     release(&tickslock);    //互斥操作
     } 
    lapiceoi(); 
    break; 

b. IDE, keyboard,uarl interrupt

   case T_IRQ0 + IRQ_IDE: 
     ideintr(); 
     lapiceoi(); 
     break; 
   case T_IRQ0 + IRQ_IDE+1: 
   break; 
   case T_IRQ0 + IRQ_KBD:   //keyboard
     kbdintr(); 
     lapiceoi(); 
     break; 
   case T_IRQ0 + IRQ_COM1:   //uarl devices
     uartintr(); 
     lapiceoi(); 
     break; 
   case T_IRQ0 + 7:

c. Spurious interrupt

 case T_IRQ0 + IRQ_SPURIOUS: 
    cprintf("cpu%d: spurious interrupt at %x:%x\n", 
           cpuid(), tf−>cs, tf−>eip); 
    lapiceoi(); 
    break; 

d. Others
其他情況下,如果是在kernel產生的,則print相關信息;如果是在user space產生的,則kill該進程

 default: 
     if(myproc() == 0 || (tf−>cs&3) == 0){ 
        cprintf("unexpected trap %d from cpu %d eip %x (cr2=0x%x)\n", 
                tf−>trapno, cpuid(), tf−>eip, rcr2()); 
        panic("trap"); 
    }  
     cprintf("pid %d %s: trap %d err %d on cpu %d " 
         "eip 0x%x addr 0x%x−−kill proc\n", 
         myproc()−>pid, myproc()−>name, tf−>trapno, 
         tf−>err, cpuid(), tf−>eip, rcr2()); 
     myproc()−>killed = 1;    //等於1,kill進程
     

最後檢查當前進程的myproc()−>killed

if(myproc() && myproc()−>killed && (tf−>cs&3) == DPL_USER) 
   exit(); 
   
// Force process to give up CPU on clock tick. 
// If interrupts were on while locks held, would need to check nlock.
if(myproc() && myproc()−>state == RUNNING &&
   tf−>trapno == T_IRQ0+IRQ_TIMER) 
   yield(); 
   
// Check if the process has been killed since we yielded
if(myproc() && myproc()−>killed && (tf−>cs&3) == DPL_USER) 
   exit(); 

3. syscall.c

· syscall()
syscall()將system call分配到不同的function中,處理后return結果儲存在暫存器eax中。

 void 3701 syscall(void) 
 { 
    int num; 
    struct proc *curproc = myproc(); 
    num = curproc−>tf−>eax;   //加載system call number
    if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {    //判斷是否在範圍內并有效,
       curproc−>tf−>eax = syscalls[num]();    //如果是,則return至eax
    } else {   //否,則print錯誤信息并return -1
       cprintf("%d %s: unknown sys call %d\n", 
              curproc−>pid, curproc−>name, num); 
       curproc−>tf−>eax = −1; 
    } 
} 

尋找system call argument
此過程分為三個部分:獲取int類型argument、檢驗argument和將argument轉換為指針,由三個function組成。
· argint
該function取得system call的第n個int類型argument,將其保存在ip中,通過fetchint 檢驗地址是否有效,若有效則將地址值轉換為指針。

argint(int n, int *ip) 
{ 
    return fetchint((myproc()−>tf−>esp) + 4 + 4*n, ip);  // return第n個int類型的argument
}

由於system call argument在proc->tf->esp的後一位,即 proc->tf->esp + 4,則第n個argument的地址為 proc->tf->esp + 4 + 4 * n。
fetchint 檢驗地址值是否有效并在process address space內,若不在,則return -1;并引導一個segmentation trap并kill 該進程。
· argptr
該function通過 argint 返回值對argument進行檢驗,取得第n個argument后對其進行檢驗,若有效,則將其轉換為指針。

argptr(int n, char **pp, int size) 
{ 
 int i; 3614   struct proc *curproc = myproc(); 
 if(argint(n, &i) < 0) 
    return −1;   //不在user space
 if(size < 0 || (uint)i >= curproc−>sz || (uint)i+size > curproc−>sz) 
 //check argument
    return −1; 
 *pp = (char*)i; 
 return 0; 
} 

· argstr
argstr()獲取第n個argument,并通過fetchstr將其轉換為string pointer并return。

 int 
 argstr(int n, char **pp) 
 { 
  int addr; 
  if(argint(n, &addr) < 0) //檢查是否有效
     return −1;    //無效返回-1
  return fetchstr(addr, pp);  //有效通過fethstr轉換為string pointer
 } 

4. usys.S
將 SYS_name的數據移到暫存器eax中,調用int指令處理eax中的指令,并通過ret指令返回user process’s space。

 #define SYSCALL(name) 
  .globl name;   //將name定義為global variable
  name: 
    movl $SYS_ ## name, %eax; //將name放入%eax
    int $T_SYSCALL; 
    ret 

4. vectors.pl & vectors.S
vectors.pl為一個Perl scrip,用於啟動IDT入口的entry points,vectors.S由vectors.pl生成,其中define了每個interrupt的入口程式和地址。vectors.S push一個error code(對於沒有error code的,push 0),push結束后,interrupt跳轉到alltraps。

 print "# generated by vectors.pl − do not edit\n"; 
 print "# handlers\n"; 3259 print ".globl alltraps\n"; 
 for(my $i = 0; $i < 256; $i++){ 
    print ".globl vector$i\n"; 
    print "vector$i:\n";     //global variable  vertors[i] 
    if(!($i == 8 || ($i >= 10 && $i <= 14) || $i == 17)){     
       print "  pushl \$0\n";    
 } 
 print "  pushl \$$i\n"; 
 print "  jmp alltraps\n"; 
 }