Exercise 7. Trace through the first few instructions of the boot loader again and identify the first instruction that would "break" or otherwise do the wrong thing if you were to get the boot loader's link address wrong. Then change the link address in boot/Makefrag to something wrong, run gmake clean, recompile lab1 with gmake, and trace into the boot loader again to see what happens. Don't forget to change the link address back afterwards!

My words:
        boot/Makefrag中第28行是$(V)$(LD) $(LDFLAGS) -N -e start -Ttext 0x7C00 -o $@.out $^,这里0x7c00是设定了编译时.text节的absolute address。
-Ttext 7c00参数在ld命令中等同于--section-start .text=7c00。
 以下摘于manual:
--section-start sectionname=org
Locate a section in the output file at the absolute address given by org. You may use this option as many times as necessary to locate multiple sections in the command line. org must be a single hexadecimal integer; for compatibility with other linkers, you may omit the leading 0x usually associated with hexadecimal values. Note: there should be no white space between sectionname, the equals sign ("="), and org.

        当更改7c00为7c10之后,进入bochs,然后设置断点在0x7c00,trace on,step到某步时会出现以下错误:

  1. <bochs:28>   
  2. (0).[239972216] [0x00007c2d] 0000:00007c2d (unk. ctxt): jmp far 0008:7c42         ; ea427c0800  
  3. CPU 0: Exception 0x0d - (#GP) general protection fault occured   
  4.  (error_code=0x0008)  
  5. CPU 0: Interrupt 0x0d occured (error_code=0x0008)  
  6. CPU 0: Exception 0x0d - (#GP) general protection fault occured   
  7.  (error_code=0x006a)  
  8. CPU 0: Exception 0x08 - (#DF) double fault occured (error_code=0x0000)  
  9. CPU 0: Interrupt 0x08 occured (error_code=0x0000)  
  10. CPU 0: Exception 0x0d - (#GP) general protection fault occured (error_code=0x0042)  
  11. (0).[239972216] [0x00007c2d] 0000:00007c2d (unk. ctxt): jmp far 0008:7c42         ; ea427c0800   
  12. Next at t=239972217  
  13. (0) [0xfffffff0] f000:fff0 (unk. ctxt): jmp far f000:e05b         ; ea5be000f0   

        原因就是在编译时指定了绝对地址,而JOS的BIOS总是会将boot loader载入到0x7c00的位置,一旦编译时指定的绝对地址与载入到内存时放置的地址不同,只要运行涉及到地址的命令,都会出错。注意在obj/boot/boot.asm中,几乎所有的地址都是根据编译时指定的那个绝对地址而设置的。比如当我们设置那个地址为0x7c10时,下面反汇编之后看到的地址都会比正确情况下多了10,当载入到内存时,其实会发生不一致,一些xor eax,eax的命令还好,ldgdt命令,jmp命令这些都是错的。

        在上面的输出中是在jmp far命令时发生了错误,不知为什么,没有像课件中的pdf里说的那样发生panic,不过一样可以看到出错。前面的命令没有报错,但是不代表没有错。以下是boot.S中的内容:
 
  1. #include <inc/mmu.h>  
  2.    
  3. # Start the CPU: switch to 32-bit protected mode, jump into C.  
  4. # The BIOS loads this code from the first sector of the hard disk into 
  5. # memory at physical address 0x7c00 and starts executing in real mode  
  6. with %cs=0 %ip=7c00.  
  7.    
  8. .set PROT_MODE_CSEG, 0x8         # kernel code segment selector  
  9. .set PROT_MODE_DSEG, 0x10        # kernel data segment selector  
  10. .set CR0_PE_ON,      0x1         # protected mode enable flag  
  11.    
  12. .globl start  
  13. start:  
  14.  .code16                     # Assemble for 16-bit mode  
  15.  cli                         # Disable interrupts  
  16.  cld                         # String operations increment  
  17.    
  18.  # Set up the important data segment registers (DS, ES, SS).  
  19.  xorw    %ax,%ax             # Segment number zero  
  20.  movw    %ax,%ds             # -> Data Segment  
  21.  movw    %ax,%es             # -> Extra Segment  
  22.  movw    %ax,%ss             # -> Stack Segment  
  23.    
  24.  # Enable A20:  
  25.  #   For backwards compatibility with the earliest PCs, physical  
  26.  #   address line 20 is tied low, so that addresses higher than  
  27.  #   1MB wrap around to zero by default. This code undoes this.  
  28. seta20.1:  
  29.  inb     $0x64,%al               # Wait for not busy  
  30.  testb   $0x2,%al  
  31.  jnz     seta20.1  
  32.    
  33.  movb    $0xd1,%al               # 0xd1 -> port 0x64  
  34.  outb    %al,$0x64  
  35.    
  36. seta20.2:  
  37.  inb     $0x64,%al               # Wait for not busy  
  38.  testb   $0x2,%al  
  39.  jnz     seta20.2  
  40.    
  41.  movb    $0xdf,%al               # 0xdf -> port 0x60  
  42.  outb    %al,$0x60  
  43.    
  44.     
  45.  
  46.  
  47.  # Switch from real to protected mode, using a bootstrap GDT  
  48.  # and segment translation that makes virtual addresses   
  49.  # identical to their physical addresses, so that the   
  50.  # effective memory map does not change during the switch.  
  51.  lgdt    gdtdesc  
  52.  movl    %cr0, %eax  
  53.  orl     $CR0_PE_ON, %eax  
  54.  movl    %eax, %cr0  
  55.     
  56.  # Jump to next instruction, but in 32-bit code segment.  
  57.  # Switches processor into 32-bit mode.  
  58.  ljmp    $PROT_MODE_CSEG, $protcseg   
        代码没有贴完,最后那个ljmp就是刚才出错的地方,我们可以看看前面的命令。
 
  1. jnz     seta20.1  
  2. jnz     seta20.2  
  3. lgdt    gdtdesc 
        这3条命令都是会出错的,不过在执行的过程中前两条判断等于0之后跳过了,第三条已经把一个奇怪地址放到了gdt寄存器里但还没有使用。这里会出错的原因就是seta20.1,seta20.2还有gdtdesc在ld时都已经变成了地址,以下摘自obj/boot/boot.asm:
 
  1. 00007c1a <seta20.1>:  
  1. jnz     seta20.1 
  2.     7c1e:       75 fa                   jne    7c1a <seta20.1>
   
  1. 00007c24 <seta20.2>:  
 
  1. jnz     seta20.2  
  2.     7c28:       75 fa                   jne    7c24 <seta20.2>   
 
  1. 00007c74 <gdtdesc>:  
  2.     7c74:       17                      pop    %ss  
  3.     7c75:       00 5c 7c 00             add    %bl,0x0(%esp,%edi,2)  
  4.     7c79:       00 90 90 55 ba f7       add    %dl,-0x845aa70(%eax)  
        可以看到这些地址也是基于-Ttext 7c10这个地址的,跳转时使用的地址是上述的,而BIOS载入boot loader时希望的是基于7c00的,所以出错了。实际上,在bochs中使用info gdt命令查看gdt表也是一堆奇怪的数据。