什么是Bochs?

简单地说,Bochs是一款仿真软件,可以用软件的方式模拟硬件的工作。同类软件有Qemu,仿真软件与虚拟机(hypervisor)还不完全相同,仿真软件是完全软件模拟硬件,而虚拟机软件(比如Vmware, VirtualBox)是利用主机的硬件进行工作。

Bochs的主页地址:http://bochs.sourceforge.net/

Bochs软件的下载地址:http://sourceforge.net/projects/bochs/files/bochs/

Bochs的使用

Bochs的使用依赖配置文件,通过配置文件指定不同的硬件,以及指定存储介质的映像文件(BIOS的ROM文件、磁盘文件等)。

在Windows环境下安装过Bochs后,在配置文件上右键菜单会出现Run, Debug的菜单选项,从而启动运行或者调试。

Bochs调试器

Bochs的调试器命令与gdb命令十分相似,但是更加强大。简单介绍几条命令的使用:

   1: #流程控制

   2: c             #continue, 继续执行

   3: s [count]     #step, 单步执行count次

   4: #断点

   5: vb seg:off    #设置逻辑地址断点

   6: lb addr       #设置物理地址断点

   7: info break    #查看断点

   8: d n           #删除断点

   9: #查看内存

  10: x/n[bhwg][xduotc] #查看内存

  11: [bhwg]        #显示单元大小,分别代表byte, half, word, giant word

  12: [xduotc]      #显示格式,分别代表hex, dec, unsigned, octal, binary, char

  13: #查看寄存器

  14: r             #查看基本寄存器

  15: sreg          #查看段寄存器

更多的命令,请输入help查看。

在Windows环境下编译Bochs源代码

首先说一下编译源码的动机,当我们安装了Bochs之后就已经可以使用它来运行或者调试一个被仿真的系统了。 这种调试类似于gdb,调试目标是运行在Bochs之上的系统。

然而,我们知道,既然Bochs是一个开源的项目,以通过软件的方式仿真了硬件系统,那么我们就可以通过查看Bochs的源码来学习相关的硬件知识(比如Intel体系结构,BIOS,DMA等)了。

从上面的下载地址下载一份源代码,解压后,能看到vs2008/bochs.sln文件,从而打开Visual Studio项目进行编译。

默认配置选项中没有包含对bochsdbg的支持,因此我们需要重新运行configure程序,悲剧的是configure是Linux下面的程序,我们可以通过以下方式来达到同样的目的:

  1. 1. 安装mingw,以及msys,将msys/bin目录添加到系统PATH环境变量中;
  2. 2. 修改源码目录下的.conf.win32_vcpp文件,添加
       1: --enable-debugger --enable-disasm

  3. 3. 打开Visual Studio的Prompt命令行,cd到源码目录下,运行
       1: bash.exe .conf.win32_vcpp

完成以上步骤之后,就可以编译出具有debug功能的Bochs可执行程序了。

Bochs是怎样处理调试命令的?

我们可以在位置上设置断点:

   1: void bx_dbg_user_input_loop(void) /*dbg_main.cc*/

然后在调试窗口中输入命令

   1: r

程序会在这两个断点处中断,这个bx_dbg_user_input_loop函数就是不断接收调试命令的循环体,它会把接收到的调试命令经过lex&yacc框架进行解析,然后调用到相应的handler来处理调试请求。

这些handler都在debug.h文件中进行声明,比如处理r命令的handler定义为

   1: void bx_dbg_info_registers_command(int);

在该函数的定义处设置断点,我们就能够了解到Bochs是怎样处理r这样的调试请求的。

通过跟踪几个调试命令的实现,我们发现了三个重要的全局变量:

   1: BOCHSAPI BX_CPU_C bx_cpu;

   2: BOCHSAPI BX_MEM_C bx_mem;

   3: bx_devices_c bx_devices;

分别保存着用来描述CPU、内存和外部设备的数据结构。

指令IN和OUT是如何处理的?

由于我们希望通过Bochs来学习硬件相关的内容,所以会对IN和OUT这两条指令很感兴趣,因为CPU就是通过这两条指令与外部设备之间进行协调工作的。

我们通过尝试,找到了下面这个函数

   1: /*

   2:  * Write a byte of data to the IO memory address space.

   3:  */

   4:  

   5:   void BX_CPP_AttrRegparmN(3)

   6: bx_devices_c::outp(Bit16u addr, Bit32u value, unsigned io_len)

bx_devices会在内部维护一个外设端口对应的读和写的handler的数组

   1: struct io_handler_struct **read_port_to_handler;

   2: struct io_handler_struct **write_port_to_handler;

这是两个二维指针数组,用端口号作为下标可以找到某个端口对应的读写处理函数,默认会把每个handler都设置成io_write_handlers

   1: /* set handlers to the default one */

   2:  for (i=0; i < PORTS; i++) {

   3:    read_port_to_handler[i] = &io_read_handlers;

   4:    write_port_to_handler[i] = &io_write_handlers;

   5:  }

通过查找函数

   1: #define DEV_register_ioread_handler(b,c,d,e,f) bx_devices.register_io_read_handler(b,c,d,e,f)

   2: #define DEV_register_iowrite_handler(b,c,d,e,f) bx_devices.register_io_write_handler(b,c,d,e,f)

我们可以找到哪些设备支持了自己的IO读写功能,以及其对应的handler。

以DMA为例,我们可以找到如下的注册handler代码

   1: // 0000..000F

   2: for (i=0x0000; i<=0x000F; i++) {

   3:   DEV_register_ioread_handler(this, read_handler, i, "DMA controller", 1);

   4:   DEV_register_iowrite_handler(this, write_handler, i, "DMA controller", 3);

   5: }

   6:  

   7: // 00080..008F

   8: for (i=0x0080; i<=0x008F; i++) {

   9:   DEV_register_ioread_handler(this, read_handler, i, "DMA controller", 1);

  10:   DEV_register_iowrite_handler(this, write_handler, i, "DMA controller", 3);

  11: }

  12:  

  13: // 000C0..00DE

  14: for (i=0x00C0; i<=0x00DE; i+=2) {

  15:   DEV_register_ioread_handler(this, read_handler, i, "DMA controller", 1);

  16:   DEV_register_iowrite_handler(this, write_handler, i, "DMA controller", 3);

  17: }

只要在DMA模块的read_handler和write_handler处理设置断点,我们就可以动态地调试DMA的处理逻辑了。

经过了以上的准备工作之后,我们就可以开始调试一个具体的系统了。我是以DLX为目标进行调试的,在调试过程中,我们可以一步一步地了解到从计算机加电后执行BIOS开机自检程序,到加载MBR,通过LILO一步一步地把Linux操作系统启动起来的全过程,一个奇妙的旅程即将开始!

有人已经这样做了,并且根据Bochs的代码,出了一本书:http://www.mouseos.com/books/x86-64/index.html

使用Bochs学习硬件原理的更多相关文章

  1. arm-linux内存管理学习笔记(1)-内存页表的硬件原理

    linux kernel集中了世界顶尖程序猿们的编程智慧,犹记操作系统课上老师讲操作系统的四大功能:进程调度 内存管理 设备驱动 网络.从事嵌入式软件开发工作,对设备驱动和网络接触的比較多. 而进程调 ...

  2. 读Flask源代码学习Python--config原理

    读Flask源代码学习Python--config原理 个人学习笔记,水平有限.如果理解错误的地方,请大家指出来,谢谢!第一次写文章,发现好累--!. 起因   莫名其妙在第一份工作中使用了从来没有接 ...

  3. Linux Framebuffer 驱动框架之一概念介绍及LCD硬件原理【转】

    本文转载自:http://blog.csdn.net/liuxd3000/article/details/17464779 一.基本概念 帧缓冲(Framebuffer)是Linux系统为显示设备提供 ...

  4. 深入浅出深度学习:原理剖析与python实践_黄安埠(著) pdf

    深入浅出深度学习:原理剖析与python实践 目录: 第1 部分 概要 1 1 绪论 2 1.1 人工智能.机器学习与深度学习的关系 3 1.1.1 人工智能——机器推理 4 1.1.2 机器学习—— ...

  5. 深入学习Composer原理(四)

    本系列第四篇文章,也是最后一篇 首先,我们先看看Composer的源码从哪里看起.当然,请您先准备好源码. composer init或者直接install之后,自动生成了一个vendor目录,这时您 ...

  6. 深入学习ThreadLocal原理

    上文我们学习了ThreadLocal的基本用法以及基本原理,ThreadLocal中的方法并不多,基本用到的也就get.set.remove等方法,但是其核心逻辑还是在定义在ThreadLocal内部 ...

  7. 深度学习硬件:CPU、GPU、FPGA、ASIC

    人工智能包括三个要素:算法,计算和数据.人工智能算法目前最主流的是深度学习.计算所对应的硬件平台有:CPU.GPU.FPGA.ASIC.由于移动互联网的到来,用户每天产生大量的数据被入口应用收集:搜索 ...

  8. Flash硬件原理

    1.2.1. 什么是Flash Flash全名叫做Flash Memory,从名字就能看出,是种数据存储设备,存储设备有很多类,Flash属于非易失性存储设备(Non-volatile Memory ...

  9. TensorFlow+Keras 02 深度学习的原理

    1 神经传递的原理 人类的神经元传递及其作用: 这里有几个关键概念: 树突 - 接受信息 轴突 - 输出信息 突触 - 传递信息 将其延伸到神经元中,示意图如下: 将上图整理成数学公式,则有 y = ...

随机推荐

  1. dp(最长公共子序列)

    A subsequence of a given sequence is the given sequence with some elements (possible none) left out. ...

  2. 【Linux】php7.2.8 + xdebug + composer + php代码覆盖率 + jenkins配置 (实操记录,亲测可用)

        [一.linux安装php 7.2.8] 1.wget http://nginx.org/download/nginx-1.9.9.tar.gz              # nginx可不安 ...

  3. LayaBox 常用技巧

    1.修改IDE的菜单 找到安装路径的LayaAirIDE\resources\app\out\vs\layaEditor\renders\laya.editorUI.xml 注意事项: 1.mask的 ...

  4. ASE Alpha Sprint - backend scrum 7

    本次scrum于2019.11.12在sky garden进行,持续30分钟. 参与人: Zhikai Chen, Jia Ning, Hao Wang 请假: Xin Kang, Lihao Ran ...

  5. (转载)python判断一个字符串是否是小数

    转载自:牛牛杂货铺 最近在写代码的时候,发现一个问题,想判断一个字符串是不是一个合法的小数,发现字符串没有内置判断小数的方法,然后就写了一个判断字符串是否是小数,可以判断正负小数,代码如下: def ...

  6. Android】Retrofit网络请求参数注解,@Path、@Query、@QueryMap...(转)

    对Retrofit已经使用了一点时间了,是时候归纳一下各种网络请求的service了. 下面分为GET.POST.DELETE还有PUT的请求,说明@Path.@Query.@QueryMap.@Bo ...

  7. Linux ct6.5安装rabbitmq

    yum install gcc glibc-devel make ncurses-devel openssl-devel xmlto 1.Erlang安装配置 下载安装包,地址http://www.e ...

  8. vue新建项目之饿了么组件标准配置

    main.js import Vue from 'vue' import App from './App.vue' import ElementUI from 'element-ui'; import ...

  9. 动态规划之数字三角形(POJ1163)

    在下面的数字三角形中寻找一条从顶部到底边的路径,使得路径上所经过的数字之和最大.路径上的每一步都只能往左下或 右下走.只需要求出这个最大和即可,不必给出具体路径. 既然求目标问题是根据查表得来的,自然 ...

  10. Java 实现文件复制的不同方法

    用不同的方法实现文件的复制 1. 通道 Channel,它是一个对象,可以通过它读取和写入数据.拿NIO与原来的I/O比较,通道就像是流.是对接操作系统底层和缓冲区的桥梁. 2. 性能比较 内存映射最 ...