查看原文
其他

chugee 2018-06-02



0 前言



出于个人兴趣,我决定读一读bochs的源码,一搜才发现网上的文档竟如此稀缺,除了官方稍显混乱的几本手册,能够找到的相对比较完整的资料就只有喻强的bochs项目源码分析与注释了,而且这个文档对于细节讲得不多,我初看的时候是一头雾水,断断续续摸索了好一阵,总算才理清了一些思路。


由于代码篇幅较多,加上个人原因前后经历的时间也略长,中间有很多内容不是很熟悉了,想到了写点东西,一方面将之前所看部分重新梳理,另一方面可以方便后来人。


由于个人时间有限,我仅将现阶段已看完的部分整理出来,有很多细节没有搞透。虽然已经尽我所能,无奈个人能力不足,如有错误敬请各位批评指正,也欢迎大家补充。



一、环境搭建



Bochs有众多版本,每个版本有对应不同环境的多个变体,本文分析的是2.2.1 for win32版本(因为喻强的手册就是这个版本),用到的工具有VC6.0, VM12.5.5, SI4.0和Understand5.0


简单说来:


1. VC6.0+Windows 7+VMware12.5.5 编译源码进行动态调试,bochs2.2.1 for win32 的源码中已经建立好了工程,使用VC6.0直接编译即可


2. SI4.0+Understand5.0 结合源码进行静态分析,用SI是因为习惯,用Understand是因为它那强大的图表功能


两个SI的技巧:


1. 用SI4.0分析源码时使用Conditional Parsing功能,将不必要分析的代码略过;


2. 由于代码中大量使用了SI未预定义的宏定义,需要修改c.tom文件以识别这些宏定义。



二、前置知识



bochs2.2.1使用C++编写,涉及到了类,继承,虚函数等概念,并用到了一些宏,这里仅对阅读源码时必备的知识作简单阐述,对具体概念和背后机理不作介绍。


2.1 关于宏定义中#和##的说明


在宏定义里,a##b就是把a,b连接起来:

假如定义了#define f(a,b) a##b则f(1,2)就是12,得到的是一个数12。

#a就是把a转化成字符串:

假如定义了#define g(a) #a  则g(f(1,2))得到的是f(1,2)转化成的字符串”12”。


2.2 父类与子类构造函数的说明


类的实例化过程需要调用构造函数,子类的实例化在调用自己的构造函数前需要调用父类的构造函数,对于子类没有显式调用父类构造函数的情况,可分为以下几种:


A. 父类没有声明构造函数

(1).如果子类也没有声明自己的构造函数,则父类和子类均由编译器生成默认的构造函数;

(2).如果子类中声明了构造函数(无参或者带参),在创建子类对象时,先调用父类默认的构造函数(编译器自动生成),再调用子类的构造函数。


B. 父类声明了无参构造函数

如果子类的构造函数没有显式地调用父类的构造,它将会调用父类的无参构造函数。也就是说,父类的无参构造函数将会被隐式地调用。


C. 父类只声明了带参构造函数

因为父类只有带参的构造函数,所以如果子类中的构造函数没有显示地调用父类的带参构造函数,则会报错。也就是说,如果父类只声明了带参构造函数,子类则必须显示地调用父类的构造函数。


在bochs源码中大多以初始化列表的形式调用父类的带参构造函数:

bx_param_c::bx_param_c (bx_id id, char *name, char *description) : bx_object_c (id)    (siminterface.cpp, 797L)


或者以初始化列表形式初始化某一成员变量,但该变量是某个类的实例,因此还是会调用类的构造函数:

BX_CPU_C::BX_CPU_C(): bx_cpuid(0) ,local_apic (this)    (init.cpp, 34L)


local_apic是bx_local_apic_c类型的成员变量,为了初始化这个成员变

量将会调用bx_local_apic_c的构造函数


2.3 不同模块中函数的导入与导出问题


一般而言,模块中定义了两种函数:导出函数(export function)和内部函数(internal function)。 

导出函数可以被其它模块调用,内部函数仅在定义它们的模块内部使用。

在要导出的函数、类、数据的声明前加上__declspec(dllexport)的修饰符,用来表示导出;


类似的,在声明前加上__declspec(dllimport),可以表示某个类、函数是从外部模块中导入的。


实际中通常会遇到以下情况:


两个模块使用一个函数或者类,一个是提供者,一个是使用者,二者之间的接口是头文件。在头文件中声明方法,在提供者那里函数或类应该被声明为__declspec(dllexport),在使用者那里,函数应该被声明为__declspec(dllimport)。


二者使用同一个头文件时可以使用条件编译:定义一个宏,针对提供者和使用者,设置不同的值。


#ifdef LABEL_VAR

#define LABEL_API __declspec(dllexport)

#else

#define LABEL_API __declspec(dllimport)

#endif

#include <string>

using std::string;

class LABEL_API LABEL_CLASS

{

STATEMENT

};


只需在提供者的头文件中定义LABEL_VAR,而使用者不定义LABEL_API,则能够很方便的实现函数形式的一致性。


在bochs中使用了宏定义BOCHSAPI实现上述功能


#if defined(WIN32) || defined(__CYGWIN__)

#  if BX_PLUGINS && defined(BX_PLUGGABLE)

//   #warning I will import DLL symbols from Bochs main program.

#    define BOCHSAPI __declspec(dllimport)

#  elif BX_PLUGINS

//   #warning I will export DLL symbols.

#    define BOCHSAPI __declspec(dllexport)

#  endif


可以认为使用BOCHSAPI宏修饰的一般都是全局使用的函数、结构或类


2.4 静态成员定义


在bochs中还会经常看到BX_XXX_THIS这种形式的宏




以dma的相关宏为例(dma.h, 32L)


#if BX_USE_DMA_SMF

#  define BX_DMA_SMF  static

#  define BX_DMA_THIS theDmaDevice->

#else

#  define BX_DMA_SMF

#  define BX_DMA_THIS this->

#endif


如果有多个实例,目标成员变量或函数将被定义为静态,静态成员指同一个类所有实例共享的成员:当某个类的实例修改了静态成员变量,其修改值为该类的其它所有实例所见,静态成员函数可以理解为在类中但跟类的实例无关的函数。


非静态成员每个类的实例都会有一个。



三、初始化完成的工作



解析命令行参数并修改相应的配置信息,读取用户配置信息,将相关的信息保存到内存中,初始化外设,初始化内存,初始化CPU,加载映像,开始模拟(顺序并非严格如此,过程存在交叉的情况)


初始化阶段最重要的工作就是根据用户输入的配置信息让CPU以不同的参数执行



四、初始化过程



4.0 全局对象


bochs中定义了很多全局变量,有些变量的类型是一个类,在mainCRTStartup的_cinit上下断点,可以看到是在_initterm(__xc_a, __xc_z)中调用了这些类的构造函数。


具体说来在main前就实例化的对象有:


(1)static logfunctions thePluginLog;  (main.cpp, 88L)

代码位于logio.cpp, 261L


(2)bx_pc_system_c bx_pc_system; (main.cpp, 99L)

代码位于pc_system.cpp, 58L


(3)bx_devices_c bx_devices; (devices.cpp, 42L)

代码位于devices.cpp, 48L


(4)bx_slowdown_timer_c bx_slowdown_timer; (slowdown_timer.cpp, 53L)

代码位于slowdown_timer.cpp, 55L


(5)bx_virt_timer_c bx_virt_timer; (virt_timer.cpp, 111L)

代码位于virt_timer.cpp, 132L


(6)bx_pit_c bx_pit; (pit_wrap.cpp, 53L)

代码位于pit_wrap.cpp, 84L


(7)class bx_ioapic_c bx_ioapic;(ioapic.cpp, 10L)

代码位于ioapic.cpp, 32L 还显式调用了父类的构造函数bx_generic_apic_c(apic.cpp, 39L)


(8)class bx_vnet_locator_c : public eth_locator_c {...} bx_vnet_match; (eth_vnet.cpp, 219L)

代码位于eth_vnet.cpp, 221L 还显式调用了父类的构造函数eth_locator_c(eth.cpp, 50L)


(9)class bx_win32_locator_c : public eth_locator_c {...} bx_win32_match; (eth_win32.cpp, 222L)

代码位于eth_win32.cpp, 224L 还显式调用了父类的构造函数eth_locator_c


(10)class bx_null_locator_c : public eth_locator_c {...} bx_null_match; (eth_null.cpp, 67L)

代码位于eth_null.cpp, 69L 还显式调用了父类的构造函数eth_locator_c


(11)BOCHSAPI BX_CPU_C    bx_cpu; (cpu.cpp, 60L)

代码位于init.cpp, 32L 还调用了local_apic的构造函数bx_local_apic_c (apic.cpp, 390L)


(12)BOCHSAPI BX_MEM_C    bx_mem; (cpu.cpp, 61L)

代码位于misc_mem.cpp, 41L


(13)bxPageWriteStampTable pageWriteStampTable; (cpu.cpp, 70L)

代码位于icache.h, 51L&&52L


(14)bx_keymap_c bx_keymap;  (keymap.cpp, 75L)

代码位于keymap.cpp, 79L


入口函数main中将命令行参数传给了bx_startup_flags,这是一个名为bx_startup_flags_t的结构体(siminterface.h, 1525L),用于保存命令行参数。main最终调用了bxmain()函数,正如名字所暗示的那样,这个函数才是bochs的真正入口,main只是起到了一个封装的作用。




4.1 用户配置接口相关对象创建


在bx_init_siminterface中实例化了两个对象

logfunctions():siminterface_log和bx_real_sim_c():SIM


4.1.1 日志类和IO类


定义在logio.cpp中

iofunction主要提供了IO的文件句柄和文件名,以及日志类的实例

logfunction主要实现了日志功能,对不同日志级别(类型)可以有不同的处理方式


日志级别(类型)有五种DEBUG, INFO, ERROR, PANIC和PASS


处理方式有四种IGNORE, REPORT, ASK和FATAL


默认的处理方式为

int logfunctions::default_onoff[N_LOGLEV] = {

  ACT_IGNORE,  //debug

  ACT_REPORT,  //info

  ACT_REPORT,  //error

  ACT_FATAL,    //panic

  ACT_FATAL    //pass

};


main.cpp中定义了一个全局变量 static logfunctions thePluginLog; 在mainCRTStartup中执行它的构造函数时初始化了全局变量io,参数是FILE*类型的stderr,在初始化io的过程中文件句柄logfd设为stderr,文件名logfn设为”/dev/stderr”,io创建的过程中会初始化一个新的logfunction对象。


logfunction的成员函数set(io)会将当前logfuction加入到io的logfn_list数组中,也就是说一个iofunction可以对应多个logfunction;而一个logfunction只对应一个iofunction




bochs中用到的主要类都以logfunctions为基类,而logfunctions类存在无参数的构造函数,因此以上的这些类都将默认的调用logfunctions的构造函数


4.1.2 配置接口类


定义在 siminterface.cpp和siminterface.h 中

bx_real_sim_c的构造函数中创建了一个数组param_registry(siminterface.cpp, 228L),这个数组中存放了所有的用户配置信息,bx_real_sim_c类是 bx_simulator_interface_c 类的子类,对于这两个类,源码中有较为详细的解释,以下内容来源于注释:


传统的bochs界面是一个有许多VGA显示块和按钮的窗口,开发者需要在gui下实现不同的版本,x.cpp, win32.cpp, beos.cpp, macintosh.cpp 等,让gui.h和gui.cpp定义独立于平台的部分函数。很少有参数能够在运行时修改。


而现在bochs中引入了两个和用户界面相关的概念:CI(Configuration Interface)和VGAW(VGA display Window)


VGAW就是bochs的图形化界面;


CI是让用户能够编辑一系列配置和运行时选项而设计的用户接口,一些选项如内存大小,是否开启网卡应该在运行前启动,而如软盘映像,每秒种指令数等参数可以在运行时修改。


由于用户界面可以以多种形式表现,标准输入输出,图形化界面或web浏览器等等,不同的用户界面形式可能需要不同的CI实现版本,甚至可能以加载插件的形式实现用户界面,因此开发者将CI封装起来,设计了siminterface类。


用户只需使用siminterface提供的接口就可以修改CI,这样一来使得CI的界面可以很容易的改变形式,同时siminterface也成为CI和虚拟机之间的桥梁。


CI和虚拟机是完全分离的,两者都可以独立编译,事实上CI的代码中不包含bochs.h头文件(这是虚拟机的核心头文件),所有CI对虚拟机的行为都已经封装在siminterface中了。


基类 bx_simulator_interface_c 中只包含虚函数,并且定义了CI使用的接口,实现均在子类bx_real_sim_c中。


4.1.3 参数类


在siminterface的实现中有一个很重要的概念——参数类,用户配置的参数是由一些基本的类型组成,如字符串,整型,bool量等,bochs中对这些类型进行了封装,每种类型都有各自的成员变量及方法,所有的定义都在siminterface.h中




使用understand能够很方便的得到UML类图,小图看不清,附件有jpg图




在UML类图中使用三层矩形框表示类,第一层显示类的名称;第二层是字段和属性;第三层是类的方法。


图中每一个成员前面的符号:‘+’表示public,‘-’表示private,‘#’表示protected。


(1)对于相对抽象的父类型,成员变量包含变量的信息:

bx_object_c(siminterface.h, 871L)中设定了id和type

bx_param_c(siminterface.h, 883L)中设定了一些提示信息(用于提示用户输入)以及描述信息


(2)对于相对具体的子类型,成员变量包含类型的限制和具体值:

bx_list_c(siminterface.h, 1145L)为数组类型,由指向bx_param_c的指针组成


bx_param_num_c(siminterface.h, 924L)为整数类型,成员变量中包含了最大值、最小值,基数等信息


bx_param_bool_c(siminterface.h, 1043L) 是 bx_param_num_c 的特例


bx_param_enum_c(siminterface.h, 1074L) 为枚举类型,成员变量中包含了一个指针数组,每个指针指向一个参数类的实例


bx_param_string_c(siminterface.h, 1094L)为字符串类型,成员变量中包含了最大长度、分隔符等信息


bx_param_filename_c(siminterface.h, 1136L) 是 bx_param_string_c 的特例


(3)两种指针类型:


bx_shadow_num_c(siminterface.h, 986L) 和 bx_shadow_bool_c(siminterface.h, 1059L),成员变量中包含一个指针指向相应的类型实例


4.2 设置返回点


setjmp和longjmp的配合使用可以实现goto语句的功能,可以从一个位置跳转到另一个位置,但它不像goto语句那样只能在一个函数内跳转,它可以跨函数跳转,能够在栈上跳过若干栈帧,返回到当前函数调用路径上的某一函数中。


用法如下:

#include <setjmp.h>

Int setjmp(jmp_buf  env);


这个函数相当于设置goto语句要跳转的位置,env可以理解为goto语句的标签


若直接调用则返回0,若由longjmp调用返回至此则返回longjmp中的val值

void longjmp(jmp_buf env,int val);


这个函数相当于goto语句


调用此函数会返回到设置env的语句即setjmp所在位置,val 是要设置的setjmp的返回值。


一般在发生异常时使用这种跳转功能


4.3 参数初始化


在bx_init_main(main.cpp, 416L)中对环境和各参数进行简单的初始化,可以参考附件中的excel文件


(1)使用宏 SAFE_GET_IOFUNC(bochs.h, 359L) 和SAFE_GET_GENLOG(bochs.h, 361L)确保全局的IO类和日志类实例的有效性。

SAFE_GET_GENLOG会创建一个全局的日志类实例genlog


(2)在 bx_init_bx_dbg(main.cpp, 932L) 中对一个bx_debug_t结构体类型(bochs.h, 424L)的全局变量bx_dbg进行赋值;


(3)在 bx_init_options(config.cpp, 372L) 中将各种系统配置参数和外设的相关参数放到bx_real_sim_c的成员数组param_registry中,bx_options是bx_options_t结构体类型(bochs.h, 615L),它是由一系列的指针或指针构成的结构体组成,参数的实际存放位置在bx_real_sim_c::param_registry中(siminterface.cpp, 256L)


一般一个参数会包含初始值,限制值(整数的最大最小值,字符串的长度),名称和描述等信息,ask_format和label用于编辑模式中显示提示信息(请求输入参数)和标签(在有些GUI的实现中,输入一些特殊参数时会产生对话框,这时标签将作为对话框的标题)


设置某些参数时还会注册handler和enable_handler


handler仅在下述成员函数中使用:

bx_param_string_c类型的get和set

bx_param_num_c类型的get64和set

bx_shadow_num_c类型的get64和set

bx_shadow_bool_c类型的get64和set


也就是只在读取和修改变量时使用,因此不难理解:handler主要用于增加一个对数据进行处理的接口,以便在取得值前可以对数据进行一次处理,也就是相当于一个“钩子”。


同样的,enable_handler仅在下述成员函数中使用:

bx_param_string_c类型的set_enabled

bx_param_num_c类型的set_enabled


也就是将变量设为可用时使用


注意bx_param_bool_c, bx_param_enum_c, bx_shadow_bool_c和bx_shadow_num_c都是bx_param_num_c的子类,它们都可能被handler和enable_handler“挂钩”


(4)在bx_print_header ()中输出bochs的版本信息(main.cpp, 106L)


(5)对命令行参数进行解析(main.cpp, 439L),如果设置了配置文件(main.cpp, 584L),将会在bx_read_configuration(config.cpp, 1884L)中解析配置文件,并在bx_real_sim_c::param_registry中作出修改(通过bx_options结构中的指针)


(6)在bx_parse_cmdline(config.cpp, 1901L)中重新对命令行参数进行了解析,因为之前读取配置文件的参数可能和命令行参数有所冲突,要以命令行参数为准,因此重新解析了一次,以覆盖相同的参数设置


(7)在plugin_startup(plugin.cpp, 452L)中对几个全局的函数指针进行了初始化,它们会在插件的初始化函数init或者一些功能函数中被调用。


4.4 确定并注册CI


1.从之前得到的参数表中找到 BXP_SEL_CONFIG_INTERFACE 参数,这个参数决定了选择哪种CI,预设有两种CI:textconfig和wx,这里选择textconfig


2.在 init_text_config_interface(textconfig.cpp, 1019L) 中注册了textconfig的回调函数,回调函数为 ci_callback(textconfig.cpp, 992L),回调函数参数为空,并设置了 bx_real_sim_c::registered_ci_name


4.5 启动CI


启动CI的代码被封装在bx_real_sim_c::configuration_interface(siminterface.cpp, 256L)中


(1)对注册的CI进行进一步检查,包括检查ci_callback是否为空(siminterface.cpp, 731L),重新读取参数表的BXP_SEL_CONFIG_INTERFACE 参数(siminterface.cpp, 729L),并与registered_ci_name 比较看是否一致 (siminterface.cpp, 735L),


(2)调用 set_display_mode (DISP_MODE_CONFIG) 时(siminterface.cpp, 744L),bx_gui 还未初始化,因此实际上什么也不执行


(3)调用回调函数ci_callback(siminterface.cpp, 745L),command参数设为CI_START



五、CI



ci_callback(textconfig.cpp, 992L)

根据不同的command参数执行不同的函数,但核心函数只有两个:


(1)bx_config_interface_init(textconfig.cpp, 731L)

实际调用的是 bx_real_sim_c::set_notify_callback,与bx_real_sim_c::registered_ci_name 类似,它设置了一个用于通知事件的回调函数,回调函数的参数设为空


(2)bx_config_interface(textconfig.cpp, 378L)

调用时传入的参数取决于参数表中BXP_BOCHS_START参数的值

该参数为 BX_QUICK_START 时传入 BX_CI_START_SIMULATION,否则传入 BX_CI_START_MENU


程序在运行过程中有可能传入 BX_CI_RUNTIME(svga.cpp, 364L && gui.cpp, 474L),BX_CI_START_OPTS(textconfig.cpp, 420L)


5.1 BX_CI_START_SIMULATION


直接开始进行模拟,调用bx_real_sim_c::begin_simulation(siminterface.cpp, 751L)

这个成员函数封装了 bx_begin_simulation(main.cpp, 717L) 函数


5.1.1 GUI的初始化及其他


(1)在 load_and_init_display_lib(main.cpp, 628L) 函数中初始化了bx_gui,以win32为例,调用 PLUG_load_plugin (win32, PLUGTYPE_OPTIONAL)(main.cpp, 696L),也就是 libwin32_LTX_plugin_init,这个函数是由IMPLEMENT_GUI_PLUGIN_CODE宏(gui.h, 384L) 展开得到的(win32.cpp, 57L),实际上是调用了bx_win32_gui_c的构造函数(这个构造函数为空)


(2)在bx_init_hardware(main.cpp, 737L)函数中,根据siminterface的默认日志记录行为来设置io的日志记录行为,将CPU的配置信息输出到日志文件中


(3)检查是否设置了rom映像(main.cpp, 843L)


5.1.2内存的初始化


在 bxPageWriteStampTable::alloc(icache.h, 55L) 中根据设置的内存大小分配cache空间,初始值为0x1fffffff


在BX_MEM_C::init_memory(misc_mem.cpp, 94L)中分配内存空间

alloc_vector_aligned (memsize + (1 << 18) + 4096, BX_MEM_VECTOR_ALIGN);


实际分配的比设定值要大 (1 << 18) + 4096 256K+4K=260K

起始偏移

大小

说明

0

memsize

系统内存

memsize

1<<18=256K

ROM

memsize+256K

4096=4K

bogus


5.1.3 加载ROM


BX_MEM_C::load_ROM(misc_mem.cpp, 136L) 具体执行了以下过程:

1.使用open打开rom文件

2.使用fstat取得状态信息


ROM类型有:


0 : System Bios

1 : VGA Bios

2 : Optional ROM Bios

3.检测rom文件大小是否合适,要载入的位置是否已被占用(rom文件大小要2K对齐因此只需每2K内存检测一次即可,所以代码中出现了右移11位的操作);如果是系统BIOS,应该结束在0xfffff,大小最大为20000=128K;


其他类型ROM的起始位置为 0xc0000-0xe0000 之间的值,大小最大为10000=64K,最多到0xc0000+66*2K的位置


4.检验校验和


初始阶段CI调用此函数实现了:

1.载入ROM(系统BIOS)放到0xf0000处

2.载入VGAROM放到0xc0000处

3.载入其他的ROM并放到预置的位置(介于c0000与0xe0000之间)


5.1.4 CPU的初始化


在 BX_CPU_C::init(init.cpp, 161L) 中


(1)在 BX_CPU_C::set_INTR(init.cpp, 924L) 中对 bx_cpu 的两个成员变量进行了赋值

  volatile bx_bool async_event; 1

  volatile bx_bool INTR; 0


(2)在 bx_local_apic_c::init(apic.cpp, 410L) 中对本地APIC进行了初始化,主要是对一些成员变量进行赋值,并且注册了一个bx_pc_system的定时器,这个定时器并未激活,设置的倒计时的ticks是0,注册函数详解见7.2


(3)对_16bit_xx_reg数组进行初始化,这个数组用于以mod-rm形式编码,采用16位地址模式的指令中指明基地址(BX或BP)和索引(SI或DI)

4.对sreg_xx数组进行初始化,这个数组用于指明指令中数据所属的段(DS或SS)

在 BX_CPU_C::sanity_checks(init.cpp, 847L) 中检查数据长度是否合法、一致


5.1.5 CPU重置


在BX_CPU_C::reset(init.cpp, 427L)中对寄存器进行了重置


5.1.6 外设的初始化


大致分成三个阶段,第一个阶段是调用相应外设类的构造函数创建对象,将对象加入到bx_devices中;第二个阶段是调用相应外设类对象的init成员函数,对状态信息等成员变量进行初始化;第三个阶段是调用相应外设类对象的reset成员函数,依旧是对一些成员变量进行赋值。这部分涉及到具体设备的编程,需要结合相关的参考手册,后面只在涉及到相关内容时再去研究细节。


值得一提的是,在vga对象的init成员函数pluginVgaDevice->init(devices.cpp, 252L)中创建了图形化界面。


5.1.7 刷新图形化界面


注释讲的比较清楚了,直接贴出来了

// update headerbar buttons since drive status can change during init

    bx_gui->update_drive_status_buttons ();

// initialize statusbar and set all items inactive

bx_gui->statusbar_setitem(-1, 0);

// The set handler for mouse_enabled does not actually update the gui until init_done is set.  This forces the set handler to be called, which sets up the mouse enabled GUI-specific stuff correctly. Not a great solution but it works. BBD


5.1.8 CPU模拟执行


刷新界面后将进入 BX_CPU(0)->cpu_loop(1);(cpu.cpp, 135L)

在cpu_loop中开始模拟执行CPU指令,具体是怎样执行的,本文不作分析。


5.2 BX_CI_START_MENU


输出主菜单的提示信息后,由用户选择输入菜单序号以实现对应的功能

根据参数表中BXP_BOCHS_START参数的值确定默认启动方式QUICK, LOAD和EDIT


5.2.1 Restore factory default configuration


通过调用bx_real_sim_c::reset_all_param(siminterface.cpp, 260L)来实现


这个函数实际上是调用了参数表中每一个参数的reset方法(bx_object_c的每一子类都定义了reset方法,虽然只有bx_param_num_c和bx_param_string_c的reset不为空)


最后将参数表中BXP_BOCHS_START参数设为EDIT(siminterface.cpp, 410L)


5.2.2 Read options from...


调用 bx_real_sim_c::reset_all_param(siminterface.cpp, 260L) 重置所有参数


调用 bx_read_rc(textconfig.cpp, 590L) 读取配置文件


最后将参数表中BXP_BOCHS_START参数设为RUN(siminterface.cpp, 417L)


5.2.3 Edit options


调用 bx_config_interface (BX_CI_START_OPTS),详见5.4

最后将参数表中BXP_BOCHS_START参数设为RUN(siminterface.cpp, 417L)


5.2.4 Save options to...


调用 bx_write_rc(textconfig.cpp, 605L) 保存配置文件


5.2.5 Begin simulation


调用 bx_config_interface (BX_CI_START_SIMULATION),详见5.1


5.2.6 Quit now


调用 bx_real_sim_c::quit_sim(siminterface.cpp, 319L)


5.3 BX_CI_RUNTIME


提供了运行时修改参数的功能,点击工具栏的CONFIG按钮将会进入这里,在windows下会调用RuntimeOptionsDialog创建一个对话框。修改参数实际调用的函数没有什么不同。


5.4 BX_CI_START_OPTS


进入编辑模式后,bochs将以菜单形式输出修改的选项,由用户选择输入菜单序号以修改相应的参数。


关键函数有:


(1)askparam(textconfig.cpp, 372L)

这个函数封装了各参数类的text_ask成员函数,前面说过在参数类bx_param_c中定义了ask_format和text_format成员变量,而这两个成员变量在初始化参数时就已经赋值了,这个函数就是完善并输出ask_format信息,以提示用户修改参数。


对于已经设置了ask_format信息的参数,将会按以下流程执行:

调用get_ask_format取得ask_format参数->根据不同类型得到用户的输入,如num和list型使用ask_uint,bool型使用ask_yn,string型使用ask_string,enum型使用ask_menu->根据得到的输入完善ask_format参数


对于没有设置ask_format信息的参数:

首先调用对应参数类的text_print完善并输出text_format信息,然后得到用户输入,设置ask_format参数


(2)bx_log_options(textconfig.cpp, 550L)


设置日志记录处理方式,bochs中日志级别(类型)有五种DEBUG, INFO, ERROR, PANIC和PASS


处理方式有四种IGNORE, REPORT, ASK和FATAL




参数为1时将对各设备的日志记录进行单独处理(选择ID,按级别依次修改,一次修改一个单元),为0时将集中处理(按级别依次修改,一次修改一列)


(3)do_menu(textconfig.cpp, 348L)


进入下一级菜单


这些参数都是list类型的,调用 bx_list_c::text_ask(textconfig.cpp, 948L)时会输出每个元素的text_format,显示时就有了菜单的效果。


5.5 BX_CI_INIT


调用bx_config_interface_init(textconfig.cpp, 731L)





本文由看雪论坛 chugee  原创

转载请注明来自看雪社区



往期热门阅读:



扫描二维码关注我们,更多干货等你来拿!

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存