一个操作系统的实现实验之3.2.1

一个操作系统的实现实验之3.2.1
2010年08月10日
  基于代码pmtest2.asm
  目标:测试读写打地址内存,例如:5M为基址;
  保护模式如何跳回实模式;
  首先来说说,代码3.4中引入的LABEL_DESC_NORMAL:Descriptor 0,0ffffh, DA_DRW; 何为Normal描述符呢?
  实模式下标准段属性,一般界限为0xFFFF(64K),属性为可读可写。
  前面的日志中关于段寄存器作为选择子时,我提出个问题"只有6个寄存器,是不是表项只能有6个啊?"
  代码中定义了7个表项,显然可以回答的疑问了。程序的处理方式是:用内存变量存放表项在GDT中的偏移,当需要用到表项时在将这个值装入段寄存器。CS是特殊的寄存器,不能通过mov命令装入选择子,只能通过jmp命令实现。
  现在看来,这个问题提的好像提弱智的。
  代码3.4需要使用了堆栈(用来存放从5M基址空间读取的数据),因此设置了堆栈空间。
  [SECTION .gs]
  ALGIN 32 让接下来的指令32位对其
  [BITS 32]
  LABEL_STACK:
  times 512 db 0 堆栈空间512字节
  TopOfStack equ $-LABEL_STACK-1 栈顶指针,用mov esp,TopOfStack完成赋值
  代码3.1中的mov esp, 0x100没有应该也没啥关系,因为未使用堆栈。
  $$的妙用
  作者StrTest-$$表示字符串相对于本节开始处的偏移,但我没看出它与StrTest-LABEL_DATA的区别?
  保护模式回到实模式必须作的几个工作
  1.回到实模式后,不能对段寄存器进行修改,当然也就无法改变段描述符对应的高速缓冲寄存器的内容,所以要在返回前将段寄存器设置一个合理的值。
  所以程序中用标准描述符给cs以外的段赋了新值,这样它们就符合实模式下的要求了。
  2.必须从16位代码段中返回,所以要先由32 位代码段跳入16位代码段
  作者的解释是:因为无法实现从32位代码段返回时cs高速缓冲寄存器中的属性符合实模式的要求。
  cs和高速缓冲寄存器值的改变只能通过jmp和call指令实现。所以首先需要定义一个16位的代码段,这个代码段的描述符定义为:段界限0ffffh,段属性:存在的只执行代码段,也就是程序中定义的LABEL_DESC_CODE16,用于在执行jmp命令时给cs赋值。
  语句jmp SelectorCode16:0完成了上述任务。
  实验验证情况(如何调试见日志下半部分):
  [0x000326ef] 0010:00000053 (unk. ctxt): jmp far 0018:00000000 ; ea000000001800
  jmp执行前
  cs:0x0010, dh=0x00409903, dl=0x269c00d8, valid=1
  Code segment, base=0x0003269c, limit=0x000000d8, Execute-Only, Accessed, 32-bit
  gdtr:base=0x00032348, limit=0x3f
  eip: 0x00000053
  jum执行后
  cs:0x0018, dh=0x00009903, dl=0x2778ffff, valid=1
  Code segment, base=0x00032778, limit=0x0000ffff, Execute-Only, Accessed, 16-bit
  gdtr:base=0x00032348, limit=0x3f
  eip: 0x00000000
  3.保护模式跳实模式
  LABEL_GO_BACK_TO_REAL:
  jmp 0:LABEL_REAL_ENTRY;
  jmp指令中段地址在程序开始处被设置成正确的值,即mov [LABEL_GO_BACK_TO_REAL+3], ax设置的实模式下cs的值
  实验验证情况:
  [0x0003278d] 0018:0015 (unk. ctxt): jmp far 3224:0442 ; ea42042432
  生成的二进制代码可以证明mov [LABEL_GO_BACK_TO_REAL+3], ax语句达到预期效果
  jum执行前
  cs:0x0018, dh=0x00009303, dl=0x2778ffff, valid=1
  Data segment, base=0x00032778, limit=0x0000ffff, Read/Write, Accessed
  gdtr:base=0x00032348, limit=0x3f
  eip: 0x00000015
  jum执行后
  cs:0x3224, dh=0x00009303, dl=0x2240ffff, valid=1
  Data segment, base=0x00032240, limit=0x0000ffff, Read/Write, Accessed
  eip: 0x00000442
  这里有个疑问就是:实模式时cs的的类型为什么是Data segment?保护模式后是Code segment?
  如何调试:
  本来想和3.1那样一步一步的跟踪一下程序,特别是看看jmp的前后cpu寄存器的一些变化,可没想到这个想法竟然很难是实现。
  由于3.4的代码长度超过512字节,所以需要利用freedos来运行pmtest2.com,而不能有bochs直接运行。
  安装书中的方法,可以顺利将pmtest2.com运行起来,也能看到结果,但pmtest2.com是在dos环境下运行,它已经脱离了bochs的控制,因此无法对其进行单步跟踪。
  上网查了一下,发现好多人都遇到这样的问题,包括该书的第一版读者。遗憾的是作者第二版时仍然未把此问题跟大家交代清楚,不能不说这是此书的一大不足之处,让好多读者很是郁闷,花很大力气去网上搜索解决方法。
  在网上看到2中解决此问题的方法:
  1.使用dos下的debug32工具单步跟踪pmtest2.com的运行情况。但这个方法存在问题,当跟踪到mov cr0,eax语句时freedos会错误,无法继续跟踪下去。
  2.使用bochs的magic_break,方法如下:
  在Bochs的配置文件里加上一句magic_break: enabled=1,然后在程序里加上一句xchg bx,bx,运行到这条指令时会断到Bochs调试器里(在Bochsrc_sample.txt里)。
  网上的留言的大侠使用该方法成功了,可惜我没成功,网上回了个贴,说了一下自己的操作流程,等待回复中。
  今天终于把这个方法实验成功了,前面失败的原因是没有把新编译成的pmtest.com文件拷贝到pm.img文件中。下面把整个步骤完整的列一下:
  1)修改bochsrc文件。
  在文件末尾增加"magic_break: enabled=1"
  2)pmtest.asm文件中增加语句xchg bx, bx,选了2个地方都没有成功
  org 0100h
  ;xchg bx, bx ;我选的位置
  jmp LABEL_BEGIN
  3)用nasm编译新该的asm文件,并拷贝到pm.img中
  sudo mount -o loop pm.img /mnt
  sudo cp pmtest.com /mnt
  sudo umount /mnt
  4)启动bochs
  bochs -f bochsrc
  5)选择6
  Please choose one: [6]
  让bochs运行
   c 回车
  6)在freedos中进入b盘,运行pmtest.com
  7)在bochs的调试窗口中将会停在xchg bx,bx处。提示如下:
  (0) Magic breakpoint
  Next at t=89456572
  (0) [0x00032342] 3224:0102 (unk. ctxt): jmp .+643 (0x000325c8) ; e98302
  
  这样我们又可以用前面的方法单步调试程序了。
  3.使用bochs的vb命令和 Edit options实现
  参考http://blog.csdn.net/titer1/archive/2010/04/28/554 0362.aspx,上面图文并茂讲的很清楚。
  vb命令需要seg:ofs格式来设置断点,那如何知道seg和ofs值呢?
  在网上看到一个方法:在你想下断点的位置前增加语句jmp $,当pmtest在dos下运行起来后回到bochs调试窗口,按下ctrl+c,bochs将会停在jmp $的位置,并显示出seg:ofs。我们就可以用这个值和vb来下断点了。
  费力的折腾了一周,才过了这关。希望作者再版时把这部分内容加上,好让大家能集中精力学习操作系统。

你可能感兴趣的:(技术杂绘)