Update Booting/linux-bootstrap-3.md

This commit is contained in:
hailin cai
2016-03-09 10:48:44 -05:00
parent 79618d9488
commit 76b27f891f

74
Booting/linux-bootstrap-3.md Executable file → Normal file
View File

@@ -118,39 +118,40 @@ static inline bool heap_free(size_t n)
设置显示模式
--------------------------------------------------------------------------------
Now we can move directly to video mode initialization. We stopped at the `RESET_HEAP()` call in the `set_video` function. Next is the call to `store_mode_params` which stores video mode parameters in the `boot_params.screen_info` structure which is defined in [include/uapi/linux/screen_info.h](https://github.com/0xAX/linux/blob/master/include/uapi/linux/screen_info.h).
在我们分析了内核数据类型以及和 HEAP 相关的操作之后,让我们回来继续分析显示模式的初始化。在 `RESET_HEAP()` 函数被调用之后,`set_video` 函数接着调用 `store_mode_params` 函数将对应显示模式的相关参数写入 `boot_params.screen_info` 字段。这个字段的结构定义可以在 [include/uapi/linux/screen_info.h](https://github.com/0xAX/linux/blob/master/include/uapi/linux/screen_info.h) 中找到。
If we look at the `store_mode_params` function, we can see that it starts with the call to the `store_cursor_position` function. As you can understand from the function name, it gets information about cursor and stores it.
`store_mode_params` 函数将调用 `store_cursor_position` 函数将当前屏幕上光标的位置保存起来。下面让我们来看 `store_cursor_poistion` 函数是如何实现的。
First of all `store_cursor_position` initializes two variables which have type `biosregs` with `AH = 0x3`, and calls `0x10` BIOS interruption. After the interruption is successfully executed, it returns row and column in the `DL` and `DH` registers. Row and column will be stored in the `orig_x` and `orig_y` fields from the the `boot_params.screen_info` structure.
首先函数初始化一个类型为 `biosregs` 的变量,将其中的 `AH` 寄存器内容设置成 `0x3`,然后调用 `0x10` bios 中断。当中断调用返回之后,`DL` `DH` 寄存器分别包含了当前按光标的行和列信息。接着这2个信息将被保存如 `boot_params.screen_info` 字段的 `orig_x` `orig_y`字段。
After `store_cursor_position` is executed, the `store_video_mode` function will be called. It just gets the current video mode and stores it in `boot_params.screen_info.orig_video_mode`.
`store_cursor_position` 函数执行完毕之后,`store_mode_params` 函数将调用 `store_vide_mode` 函数将当前使用的现实模式保存到 `boot_params.screen_info.orig_video_mode`
After this, it checks the current video mode and sets the `video_segment`. After the BIOS transfers control to the boot sector, the following addresses are for video memory:
接下來 `store_mode_params` 函数将根据当前显示模式的设定,给 `video_segment` 变量设置正确的值(实际上就是设置显示内存的起始地址)。在 BIOS 将控制权转移到引导扇区的时候,显示内存地址和显示模式的对应关系如下表所示:
```
0xB000:0x0000 32 Kb Monochrome Text Video Memory
0xB800:0x0000 32 Kb Color Text Video Memory
```
So we set the `video_segment` variable to `0xB000` if the current video mode is MDA, HGC, or VGA in monochrome mode and to `0xB800` if the current video mode is in color mode. After setting up the address of the video segment, font size needs to be stored in `boot_params.screen_info.orig_video_points` with:
根据上表,如果当前显示模式是 MDA, HGC 或者单色 VGA 模式,那么 `video_sgement` 的值将被设置成 `0xB000`;如果当前显示模式是彩色模式,那么 `video_segment` 的值将被设置成 `0xB800`。在这之后,`store_mode_params` 函数将保存字体大小信息到 `boot_params.screen_info.orig_video_points`
```C
//保存字体大小信息
set_fs(0);
font_size = rdfs16(0x485);
boot_params.screen_info.orig_video_points = font_size;
```
First of all we put 0 in the `FS` register with the `set_fs` function. We already saw functions like `set_fs` in the previous part. They are all defined in [boot.h](https://github.com/0xAX/linux/blob/master/arch/x86/boot/boot.h). Next we read the value which is located at address `0x485` (this memory location is used to get the font size) and save the font size in `boot_params.screen_info.orig_video_points`.
这段代码首先调用 `set_fs` 函数(在 [boot.h](https://github.com/0xAX/linux/blob/master/arch/x86/boot/boot.h) 中定义了许多类似的函数进行寄存器操作)将数字 `0` 放入 `FS` 寄存器。接着从内存地址 `0x485` 处获取字体大小信息并保存到 `boot_params.screen_info.orig_video_points`
```
x = rdfs16(0x44a);
y = (adapter == ADAPTER_CGA) ? 25 : rdfs8(0x484)+1;
```
Next we get the amount of columns by address `0x44a` and rows by address `0x484` and store them in `boot_params.screen_info.orig_video_cols` and `boot_params.screen_info.orig_video_lines`. After this, execution of `store_mode_params` is finished.
接下来代码将从地址 `0x44a` 处获得屏幕列信息,从地址 `0x484` 处获得屏幕行信息,并将它们保存到 `boot_params.screen_info.orig_video_cols` `boot_params.screen_info.orig_video_lines`。到这里,`store_mode_params` 的执行就结束了。
Next we can see the `save_screen` function which just saves screen content to the heap. This function collects all data which we got in the previous functions like rows and columns amount etc. and stores it in the `saved_screen` structure, which is defined as:
接下来,`set_video` 函数将条用 `save_screen` 函数将当前屏幕上的所有信息保存到 HEAP 中。这个函数首先获得当前屏幕的所有信息(包括屏幕大小,当前光标位置,屏幕上的字符信息),并且保存到 `saved_screen` 结构体中。这个结构体的定义如下所示:
```C
static struct saved_screen {
@@ -160,24 +161,37 @@ static struct saved_screen {
} saved;
```
It then checks whether the heap has free space for it with:
接下来函数将检查 HEAP 中是否有足够的空间保存这个结构体的数据:
```C
if (!heap_free(saved.x*saved.y*sizeof(u16)+512))
return;
```
and allocates space in the heap if it is enough and stores `saved_screen` in it.
如果 HEAP 有足够的空间,代码将在 HEAP 中分配相应的空间并且将 `saved_screen` 保存到 HEAP。
The next call is `probe_cards(0)` from [arch/x86/boot/video-mode.c](https://github.com/0xAX/linux/blob/master/arch/x86/boot/video-mode.c#L33). It goes over all video_cards and collects the number of modes provided by the cards. Here is the interesting moment, we can see the loop:
接下来 `set_video` 函数将调用 `probe_cards(0)`(这个函数定义在 [arch/x86/boot/video-mode.c](https://github.com/0xAX/linux/blob/master/arch/x86/boot/video-mode.c#L33))。 这个函数简单遍历所有的显卡,并通过调用驱动程序设置显卡所支持的显示模式:
```C
for (card = video_cards; card < video_cards_end; card++) {
/* collecting number of modes here */
if (card->unsafe == unsafe) {
if (card->probe)
card->nmodes = card->probe();
else
card->nmodes = 0;
}
}
```
but `video_cards` is not declared anywhere. Answer is simple: Every video mode presented in the x86 kernel setup code has definition like this:
如果你仔细看上面的代码,你会发现 `video_cards` 这个变量并没有被声明,那么程序怎么能够正常编译执行呢?实际上很简单,它指向了一个在 [arch/x86/boot/setup.ld](https://github.com/0xAX/linux/blob/master/arch/x86/boot/setup.ld) 中定义的叫做 `.videocards` 的内存段:
```
.videocards : {
video_cards = .;
*(.videocards)
video_cards_end = .;
}
```
那么这段内存里面存放的数据是什么呢,下面我们就来详细分析。在内核初始化代码中,对于每个支持的现实模式都是使用下面的代码进行定义的:
```C
static __videocard video_vga = {
@@ -187,13 +201,13 @@ static __videocard video_vga = {
};
```
where `__videocard` is a macro:
`__videocard` 是一个宏定义,如下所示:
```C
#define __videocard struct card_info __attribute__((used,section(".videocards")))
```
which means that `card_info` structure:
因此 `__videocard` 是一个 `card_info` 结构,这个结构定义如下:
```C
struct card_info {
@@ -208,21 +222,24 @@ struct card_info {
};
```
is in the `.videocards` segment. Let's look in the [arch/x86/boot/setup.ld](https://github.com/0xAX/linux/blob/master/arch/x86/boot/setup.ld) linker file, we can see there:
`.videocards` 内存段实际上存放的就是所有被内核初始化代码定义的 `card_info` 结构(可以看成是一个数组),所以 `probe_cards` 函数可以使用 `video_cards`,通过循环遍历所有的 `card_info`
```
.videocards : {
video_cards = .;
*(.videocards)
video_cards_end = .;
}
`probe_cards` 执行完成之后,我们终于进入 `set_video` 函数的主循环了。在这个循环中,如果 `vid_mode=ask`,那么将显示一个菜单让用户选择想要的显示模式,然后代码将根据用户的选择或者 `vid_mod` ,通过调用 `set_mode` 函数来设置正确的现实模式。如果设置成功,循环结束,否则显示菜单让用户选择显示模式,继续进行设置显示模式的尝试。
```c
for (;;) {
if (mode == ASK_VGA)
mode = mode_menu();
if (!set_mode(mode))
break;
printf("Undefined video mode number: %x\n", mode);
mode = ASK_VGA;
}
```
It means that `video_cards` is just a memory address and all `card_info` structures are placed in this segment. It means that all `card_info` structures are placed between `video_cards` and `video_cards_end`, so we can use it in a loop to go over all of it. After `probe_cards` executes we have all structures like `static __videocard video_vga` with filled `nmodes` (number of video modes).
After `probe_cards` execution is finished, we move to the main loop in the `set_video` function. There is an infinite loop which tries to set up video mode with the `set_mode` function or prints a menu if we passed `vid_mode=ask` to the kernel command line or video mode is undefined.
The `set_mode` function is defined in [video-mode.c](https://github.com/0xAX/linux/blob/master/arch/x86/boot/video-mode.c#L147) and gets only one parameter, `mode`, which is the number of video modes (we got it from the menu or in the start of `setup_video`, from the kernel setup header).
The `set_mode` function is defined in [video-mode.c](https://github.com/0xAX/linux/blob/master/arch/x86/boot/video-mode.c#L147) and gets only one parameter, `mode`, which is the number of video modes (we got it from the menu or in the start of `setup_video`, from the kernel setup header).
The `set_mode` function checks the `mode` and calls the `raw_set_mode` function. The `raw_set_mode` calls the `set_mode` function for the selected card i.e. `card->set_mode(struct mode_info*)`. We can get access to this function from the `card_info` structure. Every video mode defines this structure with values filled depending upon the video mode (for example for `vga` it is the `video_vga.set_mode` function. See above example of `card_info` structure for `vga`). `video_vga.set_mode` is `vga_set_mode`, which checks the vga mode and calls the respective function:
@@ -579,3 +596,4 @@ Links
* [GCC designated inits](https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Designated-Inits.html)
* [GCC type attributes](https://gcc.gnu.org/onlinedocs/gcc/Type-Attributes.html)
* [Previous part](linux-bootstrap-2.md)