From eb1cb64b1ed18a90d3734b8fe90b378c5f3ef3ff Mon Sep 17 00:00:00 2001 From: hailin cai Date: Thu, 28 Jan 2016 16:26:21 -0500 Subject: [PATCH 001/153] Create Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Booting/linux-bootstrap-2md.md diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md new file mode 100644 index 0000000..0fbf8c6 --- /dev/null +++ b/Booting/linux-bootstrap-2md.md @@ -0,0 +1,2 @@ +# 在内核安装代码的第一步 + From 4497ec8e94f55a28f019ca3563fde700e15a4d31 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Thu, 28 Jan 2016 16:26:22 -0500 Subject: [PATCH 002/153] Update SUMMARY.md --- SUMMARY.md | 84 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 43 insertions(+), 41 deletions(-) diff --git a/SUMMARY.md b/SUMMARY.md index 4d368b8..fb9bc0e 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -1,52 +1,54 @@ -### Summary +# Summary +* [Introduction](README.md) * [引导](Booting/README.md) - * [从引导加载程序内核](Booting/linux-bootstrap-1.md) - * [在内核安装代码的第一步](Booting/linux-bootstrap-2.md) - * [视频模式初始化和转换到保护模式](Booting/linux-bootstrap-3.md) - * [过渡到64位模式](Booting/linux-bootstrap-4.md) - * [内核解压缩](Booting/linux-bootstrap-5.md) + * [从引导加载程序内核](Booting/linux-bootstrap-1.md) + * [在内核安装代码的第一步](Booting/linux-bootstrap-2md.md) + * [视频模式初始化和转换到保护模式](Booting/linux-bootstrap-3.md) + * [过渡到64位模式](Booting/linux-bootstrap-4.md) + * [内核解压缩](Booting/linux-bootstrap-5.md) * [Initialization](Initialization/README.md) - * [First steps in the kernel](Initialization/linux-initialization-1.md) - * [Early interrupts handler](Initialization/linux-initialization-2.md) - * [Last preparations before the kernel entry point](Initialization/linux-initialization-3.md) - * [Kernel entry point](Initialization/linux-initialization-4.md) - * [Continue architecture-specific boot-time initializations](Initialization/linux-initialization-5.md) - * [Architecture-specific initializations, again...](Initialization/linux-initialization-6.md) - * [End of the architecture-specific initializations, almost...](Initialization/linux-initialization-7.md) - * [Scheduler initialization](Initialization/linux-initialization-8.md) - * [RCU initialization](Initialization/linux-initialization-9.md) - * [End of initialization](Initialization/linux-initialization-10.md) + * [First steps in the kernel](Initialization/linux-initialization-1.md) + * [Early interrupts handler](Initialization/linux-initialization-2.md) + * [Last preparations before the kernel entry point](Initialization/linux-initialization-3.md) + * [Kernel entry point](Initialization/linux-initialization-4.md) + * [Continue architecture-specific boot-time initializations](Initialization/linux-initialization-5.md) + * [Architecture-specific initializations, again...](Initialization/linux-initialization-6.md) + * [End of the architecture-specific initializations, almost...](Initialization/linux-initialization-7.md) + * [Scheduler initialization](Initialization/linux-initialization-8.md) + * [RCU initialization](Initialization/linux-initialization-9.md) + * [End of initialization](Initialization/linux-initialization-10.md) * [Interrupts](interrupts/README.md) - * [Introduction](interrupts/interrupts-1.md) - * [Start to dive into interrupts](interrupts/interrupts-2.md) - * [Interrupt handlers](interrupts/interrupts-3.md) - * [Initialization of non-early interrupt gates](interrupts/interrupts-4.md) - * [Implementation of some exception handlers](interrupts/interrupts-5.md) - * [Handling Non-Maskable interrupts](interrupts/interrupts-6.md) - * [Dive into external hardware interrupts](interrupts/interrupts-7.md) - * [Initialization of external hardware interrupts structures](interrupts/interrupts-8.md) + * [Introduction](interrupts/interrupts-1.md) + * [Start to dive into interrupts](interrupts/interrupts-2.md) + * [Interrupt handlers](interrupts/interrupts-3.md) + * [Initialization of non-early interrupt gates](interrupts/interrupts-4.md) + * [Implementation of some exception handlers](interrupts/interrupts-5.md) + * [Handling Non-Maskable interrupts](interrupts/interrupts-6.md) + * [Dive into external hardware interrupts](interrupts/interrupts-7.md) + * [Initialization of external hardware interrupts structures](interrupts/interrupts-8.md) * [Memory management](mm/README.md) - * [Memblock](mm/linux-mm-1.md) - * [Fixmaps and ioremap](mm/linux-mm-2.md) -* [vsyscalls and vdso]() -* [SMP]() + * [Memblock](mm/linux-mm-1.md) + * [Fixmaps and ioremap](mm/linux-mm-2.md) +* vsyscalls and vdso +* SMP * [Concepts](Concepts/README.md) - * [Per-CPU variables](Concepts/per-cpu.md) - * [Cpumasks](Concepts/cpumask.md) + * [Per-CPU variables](Concepts/per-cpu.md) + * [Cpumasks](Concepts/cpumask.md) * [Data Structures in the Linux Kernel](DataStructures/README.md) - * [Doubly linked list](DataStructures/dlist.md) - * [Radix tree](DataStructures/radix-tree.md) + * [Doubly linked list](DataStructures/dlist.md) + * [Radix tree](DataStructures/radix-tree.md) * [理论](Theory/README.md) - * [分页](Theory/Paging.md) - * [Elf64 格式](Theory/ELF.md) - * [CPUID]() - * [MSR]() -* [Initial ram disk]() - * [initrd]() + * [分页](Theory/Paging.md) + * [Elf64 格式](Theory/ELF.md) + * CPUID + * MSR +* Initial ram disk + * initrd * [Misc](Misc/README.md) - * [How kernel compiled]() - * [Write and Submit your first Linux kernel Patch]() - * [Data types in the kernel]() + * How kernel compiled + * Write and Submit your first Linux kernel Patch + * Data types in the kernel * [Useful links](LINKS.md) * [Contributors](contributors.md) + From 643b70c29b0812b6edb135289bb6bedbff4bb4fe Mon Sep 17 00:00:00 2001 From: hailin cai Date: Thu, 28 Jan 2016 16:58:46 -0500 Subject: [PATCH 003/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index 0fbf8c6..0c1e289 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -1,2 +1,21 @@ # 在内核安装代码的第一步 +内核启动的第一步 +-------------------------------------------------------------------------------- + +在[上一节中](https://0xax.gitbooks.io/linux-insides/content/Booting/linux-bootstrap-1.html)我们开始接触到内核启动代码,并且分析了初始化部分,最后我们停在了对`main`函数(`main`函数是第一个用C写的函数)的调用(`main`函数位于[arch/x86/boot/main.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c)。 + +在这一节中我们将继续对内核启动过程的研究,我们将 +* 认识`保护模式` +* 如何从实模式进入保护模式 +* 堆和字符界面初始化 +* 内存检测,cpu验证,键盘初始化 +* 还有更多 + +现在让我们开始我们的旅程 + +保护模式 +-------------------------------------------------------------------------------- +在操作系统可以使用Intel 64位CPU的[长模式](http://en.wikipedia.org/wiki/Long_mode)之前,内核必须首先将CPU切换到保护模式运行。 + +什么是[保护模式](https://en.wikipedia.org/wiki/Protected_mode)?保护模式于1982年被引入到Intel CPU家族,并且从那之后,知道Intel 64出现,保护模式都是Intel CPU的主要运行模式。 \ No newline at end of file From 2249879c5646965d60f542ae9e73af8cf79c204e Mon Sep 17 00:00:00 2001 From: hailin cai Date: Thu, 28 Jan 2016 16:59:01 -0500 Subject: [PATCH 004/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index 0c1e289..e5ca461 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -1,4 +1,5 @@ # 在内核安装代码的第一步 +#https://0xax.gitbooks.io/linux-insides/content/Booting/linux-bootstrap-2.html 内核启动的第一步 -------------------------------------------------------------------------------- From 90c39f07cc4f81f7a04e223c3a446fcba4374828 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 29 Jan 2016 09:58:26 -0500 Subject: [PATCH 005/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index e5ca461..f03b798 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -19,4 +19,9 @@ -------------------------------------------------------------------------------- 在操作系统可以使用Intel 64位CPU的[长模式](http://en.wikipedia.org/wiki/Long_mode)之前,内核必须首先将CPU切换到保护模式运行。 -什么是[保护模式](https://en.wikipedia.org/wiki/Protected_mode)?保护模式于1982年被引入到Intel CPU家族,并且从那之后,知道Intel 64出现,保护模式都是Intel CPU的主要运行模式。 \ No newline at end of file +什么是[保护模式](https://en.wikipedia.org/wiki/Protected_mode)?保护模式于1982年被引入到Intel CPU家族,并且从那之后,知道Intel 64出现,保护模式都是Intel CPU的主要运行模式。 + +淘汰[实模式](http://wiki.osdev.org/Real_Mode)的主要原因是因为在实模式下,系统能够访问的内存非常有限。如果你还记得我们在上一节说的,在实模式下,系统最多只能访问1M内存,而且在很多时候,实际能够访问的内存只有640K。 + +保护模式带来了很多的改变,不过只要的改变都集中在内存管理方法。在保护模式中,实模式的20位地址线被替换成32位地址线,因此系统可以访问多大4GB的地址空间。另外,在保护模式中引入了[内存分页](http://en.wikipedia.org/wiki/Paging)功能,在后面我们将介绍这个功能。 + From 4963a05b4c9e981aa80a41ffcfa282c7b78ed4d1 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 29 Jan 2016 10:02:42 -0500 Subject: [PATCH 006/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index f03b798..8ec6b09 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -25,3 +25,16 @@ 保护模式带来了很多的改变,不过只要的改变都集中在内存管理方法。在保护模式中,实模式的20位地址线被替换成32位地址线,因此系统可以访问多大4GB的地址空间。另外,在保护模式中引入了[内存分页](http://en.wikipedia.org/wiki/Paging)功能,在后面我们将介绍这个功能。 +保护模式提供了2种完全不同的内存关机机制: + +* 段式内存管理 +* 内存分页 + +在这一节中,我们只介绍段式内存管理,内存分页我们将在后面的章节进行介绍。 + +在上一节中我们说过,在实模式下,一个物理地址是由2个部分组成的: + +* 内存段的基地址 +* 从基地址开始的偏移 + + From 9a8006e40719ae650303035d2eb9813da3a74101 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 29 Jan 2016 10:04:12 -0500 Subject: [PATCH 007/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index 8ec6b09..c2b27f2 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -37,4 +37,10 @@ * 内存段的基地址 * 从基地址开始的偏移 +通过这2个信息,我们可以通过下面的公式计算出对应的物理地址 + +``` +PhysicalAddress = Segment * 16 + Offset +``` + From 6e675d99c3137273ed7d76ff3175ad6bd1a7c5a8 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 29 Jan 2016 10:04:21 -0500 Subject: [PATCH 008/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index c2b27f2..b220b6d 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -39,7 +39,7 @@ 通过这2个信息,我们可以通过下面的公式计算出对应的物理地址 -``` +```c PhysicalAddress = Segment * 16 + Offset ``` From 92cfba7275299b70cef5f84c8f682967b5bd8e9b Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 29 Jan 2016 10:05:46 -0500 Subject: [PATCH 009/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index b220b6d..c5cbb03 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -39,8 +39,8 @@ 通过这2个信息,我们可以通过下面的公式计算出对应的物理地址 -```c +``` PhysicalAddress = Segment * 16 + Offset ``` - +在保护模式中,内存段的定义Memory segmentation was completely redone in protected mode. There are no 64 Kilobyte fixed-size segments. Instead, the size and location of each segment is described by an associated data structure called Segment Descriptor. The segment descriptors are stored in a data structure called Global Descriptor Table (GDT). From d2c7b9ec94d9c92837569d3dfc8dfea51c794219 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Tue, 2 Feb 2016 14:54:57 -0500 Subject: [PATCH 010/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index c5cbb03..744e0ef 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -43,4 +43,11 @@ PhysicalAddress = Segment * 16 + Offset ``` -在保护模式中,内存段的定义Memory segmentation was completely redone in protected mode. There are no 64 Kilobyte fixed-size segments. Instead, the size and location of each segment is described by an associated data structure called Segment Descriptor. The segment descriptors are stored in a data structure called Global Descriptor Table (GDT). +在保护模式中,内存段的定义和实模式完全不同。在保护模式中,每个内存段不再是64K大小,段的大小和起始位置是通过一个叫做`段描述符`的数据结构进行描述的。所有内存段的段描述符存储在一个叫做`全局描述符表`(GDT)的内存结构中。 + +全局描述符表示一个内存数据结构,但是它在内存中的位置并不是固定的,它的地址保存在一个特殊寄存器`GDTR`中。在后面的章节中,我们将在Linux内核代码中看到全局描述符表的地址是如何被保存到`GDTR`中的。具体的汇编代码看起来是这样的: + +```assembly +lgdt gdt +``` + From fe37a0555804a6d047933d449dd2b64ed2e28108 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Tue, 2 Feb 2016 14:57:02 -0500 Subject: [PATCH 011/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index 744e0ef..e9093cb 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -51,3 +51,4 @@ PhysicalAddress = Segment * 16 + Offset lgdt gdt ``` +`lgdt`汇编代码将把全局描述符表的基地址和大小保存到`GDTR`寄存器中。`GRTD`是一个48位的寄存器,where the `lgdt` instruction loads the base address and limit(size) of global descriptor table to the `GDTR` register. `GDTR` is a 48-bit register and consists of two parts: \ No newline at end of file From 7a02d63cc3df4371e19c2ec2877daf5c7ec7cccb Mon Sep 17 00:00:00 2001 From: hailin cai Date: Tue, 2 Feb 2016 15:28:53 -0500 Subject: [PATCH 012/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 35 +++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index e9093cb..b21dbe3 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -51,4 +51,37 @@ PhysicalAddress = Segment * 16 + Offset lgdt gdt ``` -`lgdt`汇编代码将把全局描述符表的基地址和大小保存到`GDTR`寄存器中。`GRTD`是一个48位的寄存器,where the `lgdt` instruction loads the base address and limit(size) of global descriptor table to the `GDTR` register. `GDTR` is a 48-bit register and consists of two parts: \ No newline at end of file +`lgdt`汇编代码将把全局描述符表的基地址和大小保存到`GDTR`寄存器中。`GRTD`是一个48位的寄存器,这个寄存器中的保存了2部分的内容: + +* 全局描述符表的大小 (16位) +* 全局描述符表的基址 (32位) + +就像前面的段落说的,全局描述符表包含了所有内存段的`段描述符`。每个段描述符长度是64位,结构如下图描述: + +``` +31 24 19 16 7 0 +------------------------------------------------------------ +| | |B| |A| | | | |0|E|W|A| | +| BASE 31:24 |G|/|L|V| LIMIT |P|DPL|S| TYPE | BASE 23:16 | 4 +| | |D| |L| 19:16 | | | |1|C|R|A| | +------------------------------------------------------------ +| | | +| BASE 15:0 | LIMIT 15:0 | 0 +| | | +------------------------------------------------------------ +``` + +粗粗一看,上面的结构非常吓人,不过实际上这个结构是非常容易理解的。比如在上图中的LIMIT 15:0表示这个数据结构的0到15位保存的内存段的大小的0到15位。相似的LIMITE 19:16表示上述数据结构的16到19位保存的是内存段大小的16到19位。从这个分析中,我们可以看出每个内存段的大小是通过20位进行描述的。下面我们将对这个数据结构进行仔细分析: + +1. 内存段长度[20位]被保存在上述内存结构的0-15和16-19位。根据上述内存结构中`G`位的设置,这20位内存定义的内存长度是不一样的。下面是一些具体的例子: + + * 如果`G`= 0, 并且Limit = 0, 那么表示段长度是1 byte + * 如果`G` = 1, 并且Limit = 0, 那么表示段长度是4K bytes + * 如果`G` = 0,并且Limit = 0xfffff,那么表示段长度是1M bytes + * 如果`G` = 1,并且Limit = 0xfffff,那么表示段长度是4G bytes + + 从上面的例子我们可以看出: + + * 如果G = 0, 那么内存段的长度是按照1 byte进行增长的 ( Limit每增加1,段长度增加1 byte ),最大的内存段长度将是1M bytes; + * 如果G = 1, 那么内存段的长度是按照4K bytes ( Limit每增加1,段长度增加4K bytes )进行增长的,最大的内存段长度将是4G bytes; + * 段长度的计算公司是 base_seg_length * ( LIMIT + 1)。 \ No newline at end of file From 7bd80975f16d682d90508002c4b2e47617ff143c Mon Sep 17 00:00:00 2001 From: hailin cai Date: Tue, 2 Feb 2016 15:47:13 -0500 Subject: [PATCH 013/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 37 ++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index b21dbe3..19abe28 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -73,7 +73,7 @@ lgdt gdt 粗粗一看,上面的结构非常吓人,不过实际上这个结构是非常容易理解的。比如在上图中的LIMIT 15:0表示这个数据结构的0到15位保存的内存段的大小的0到15位。相似的LIMITE 19:16表示上述数据结构的16到19位保存的是内存段大小的16到19位。从这个分析中,我们可以看出每个内存段的大小是通过20位进行描述的。下面我们将对这个数据结构进行仔细分析: -1. 内存段长度[20位]被保存在上述内存结构的0-15和16-19位。根据上述内存结构中`G`位的设置,这20位内存定义的内存长度是不一样的。下面是一些具体的例子: +1. Limit[20位]被保存在上述内存结构的0-15和16-19位。根据上述内存结构中`G`位的设置,这20位内存定义的内存长度是不一样的。下面是一些具体的例子: * 如果`G`= 0, 并且Limit = 0, 那么表示段长度是1 byte * 如果`G` = 1, 并且Limit = 0, 那么表示段长度是4K bytes @@ -84,4 +84,37 @@ lgdt gdt * 如果G = 0, 那么内存段的长度是按照1 byte进行增长的 ( Limit每增加1,段长度增加1 byte ),最大的内存段长度将是1M bytes; * 如果G = 1, 那么内存段的长度是按照4K bytes ( Limit每增加1,段长度增加4K bytes )进行增长的,最大的内存段长度将是4G bytes; - * 段长度的计算公司是 base_seg_length * ( LIMIT + 1)。 \ No newline at end of file + * 段长度的计算公司是 base_seg_length * ( LIMIT + 1)。 + +2. Base[32-bits]被保存在上述地址结构的0-15, 32-39以及56-63位。Base定义了段基址。 + +3. Type/Attribute (40-47 bits) 定义了内存段的类型以及支持的操作。 + * `S` 标记( 第44位 )定义了段的类型,`S` = 0说明这个内存段是一个系统段;`S` = 1说明这个内存段是一个代码段或者是数据段( 堆栈段是一种特使类型的数据段,堆栈段必须是可以进行读写的段 )。 + + 在`S` = 1的情况下,上述内存结构的第43位决定了内存段是数据段还是代码段。如果43位 = 0,拿说明是一个数据段,否则就是一个代码段。 + +对于数据段和代码段,下面的表格给出了段类型定义 + +``` +| Type Field | Descriptor Type | Description +|-----------------------------|-----------------|------------------ +| Decimal | | +| 0 E W A | | +| 0 0 0 0 0 | Data | Read-Only +| 1 0 0 0 1 | Data | Read-Only, accessed +| 2 0 0 1 0 | Data | Read/Write +| 3 0 0 1 1 | Data | Read/Write, accessed +| 4 0 1 0 0 | Data | Read-Only, expand-down +| 5 0 1 0 1 | Data | Read-Only, expand-down, accessed +| 6 0 1 1 0 | Data | Read/Write, expand-down +| 7 0 1 1 1 | Data | Read/Write, expand-down, accessed +| C R A | | +| 8 1 0 0 0 | Code | Execute-Only +| 9 1 0 0 1 | Code | Execute-Only, accessed +| 10 1 0 1 0 | Code | Execute/Read +| 11 1 0 1 1 | Code | Execute/Read, accessed +| 12 1 1 0 0 | Code | Execute-Only, conforming +| 14 1 1 0 1 | Code | Execute-Only, conforming, accessed +| 13 1 1 1 0 | Code | Execute/Read, conforming +| 15 1 1 1 1 | Code | Execute/Read, conforming, accessed +``` From 5509710f0c077598b267b53502b90a00aa21780d Mon Sep 17 00:00:00 2001 From: hailin cai Date: Thu, 4 Feb 2016 13:04:46 -0500 Subject: [PATCH 014/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index 19abe28..b4fd911 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -118,3 +118,4 @@ lgdt gdt | 13 1 1 1 0 | Code | Execute/Read, conforming | 15 1 1 1 1 | Code | Execute/Read, conforming, accessed ``` + From 34411dbbbb68adf9460bc277ed24644ddd3d7ec4 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Thu, 4 Feb 2016 13:25:12 -0500 Subject: [PATCH 015/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index b4fd911..9b2aa34 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -91,7 +91,7 @@ lgdt gdt 3. Type/Attribute (40-47 bits) 定义了内存段的类型以及支持的操作。 * `S` 标记( 第44位 )定义了段的类型,`S` = 0说明这个内存段是一个系统段;`S` = 1说明这个内存段是一个代码段或者是数据段( 堆栈段是一种特使类型的数据段,堆栈段必须是可以进行读写的段 )。 - 在`S` = 1的情况下,上述内存结构的第43位决定了内存段是数据段还是代码段。如果43位 = 0,拿说明是一个数据段,否则就是一个代码段。 +在`S` = 1的情况下,上述内存结构的第43位决定了内存段是数据段还是代码段。如果43位 = 0,拿说明是一个数据段,否则就是一个代码段。 对于数据段和代码段,下面的表格给出了段类型定义 @@ -119,3 +119,9 @@ lgdt gdt | 15 1 1 1 1 | Code | Execute/Read, conforming, accessed ``` +从上面的表格我们可以看出,当第43位是`0`的时候,这个段描述符对应的是一个数据段,如果该位是`1`,那么表示这个段描述符对应的是一个代码段。对于数据段,第42,41,40位表示的是(*E*扩展,*W*可写,*A*可访问);对于代码段,第42,41,40位表示的是(*C*一致,*R*可读,*A*可访问)。 *A*ccessible) or CRA(*C*onforming *R*eadable *A*ccessible)。 + * if E(bit 42) is 0, expand up other wise expand down. Read more [here](http://www.sudleyplace.com/dpmione/expanddown.html). + * if W(bit 41)(for Data Segments) is 1, write access is allowed otherwise not. Note that read access is always allowed on data segments. + * A(bit 40) - Whether the segment is accessed by processor or not. + * C(bit 43) is conforming bit(for code selectors). If C is 1, the segment code can be executed from a lower level privilege e.g. user level. If C is 0, it can only be executed from the same privilege level. + * R(bit 41)(for code segments). If 1 read access to segment is allowed otherwise not. Write access is never allowed to code segments. \ No newline at end of file From 4b6827722d545ad49c9fbbdcd1aee46b6578f5c4 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 5 Feb 2016 11:52:18 -0500 Subject: [PATCH 016/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index 9b2aa34..94029d7 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -120,8 +120,9 @@ lgdt gdt ``` 从上面的表格我们可以看出,当第43位是`0`的时候,这个段描述符对应的是一个数据段,如果该位是`1`,那么表示这个段描述符对应的是一个代码段。对于数据段,第42,41,40位表示的是(*E*扩展,*W*可写,*A*可访问);对于代码段,第42,41,40位表示的是(*C*一致,*R*可读,*A*可访问)。 *A*ccessible) or CRA(*C*onforming *R*eadable *A*ccessible)。 - * if E(bit 42) is 0, expand up other wise expand down. Read more [here](http://www.sudleyplace.com/dpmione/expanddown.html). - * if W(bit 41)(for Data Segments) is 1, write access is allowed otherwise not. Note that read access is always allowed on data segments. - * A(bit 40) - Whether the segment is accessed by processor or not. - * C(bit 43) is conforming bit(for code selectors). If C is 1, the segment code can be executed from a lower level privilege e.g. user level. If C is 0, it can only be executed from the same privilege level. - * R(bit 41)(for code segments). If 1 read access to segment is allowed otherwise not. Write access is never allowed to code segments. \ No newline at end of file + * 如果`E` = 0,数据段是向上扩展数据段,反之为向下扩展数据段。关于向上扩展和向下扩展数据段,可以参考下面的[链接](http://www.sudleyplace.com/dpmione/expanddown.html). + * 如果`W` = 1,说明这个数据段是可写的,否则不可写。所有数据段都是可读的。 + * A位表示该内存段是否已经被CPU访问。 + * 如果`C` = 1,说明这个代码段可以被第优先级的代码访问,比如可以被用户态代码访问。反之如果`C` = 0,说明只能同优先级的代码段可以访问。 + * 如果`R` = 1,说明该代码段可读。代码段是永远没有写权限的。 + From 19e4b1cd04749e37d2b5461e1f9cb7585a5b8321 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 5 Feb 2016 11:59:37 -0500 Subject: [PATCH 017/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index 94029d7..47be86b 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -126,3 +126,16 @@ lgdt gdt * 如果`C` = 1,说明这个代码段可以被第优先级的代码访问,比如可以被用户态代码访问。反之如果`C` = 0,说明只能同优先级的代码段可以访问。 * 如果`R` = 1,说明该代码段可读。代码段是永远没有写权限的。 +4. DPL[2-bits, bit 45 和 46] (描述符优先级) 定义了该段的优先级。具体数值是0-3。 + +5. P 标志(bit 47) - 说明该内存段是否已经存在于内存中。如果`P` = 0,那么在访问这个内存段的时候将报错。 + +6. AVL 标志(bit 52) - 这个位在Linux内核中没有被使用。 + +7. L 标志(bit 53) - 只对代码段有意义,如果`L` = 1,说明该代码段需要运行在64位模式下。 + +8. D/B flag(bit 54) - 根据段描述符描述的是一个可执行代码段、下扩数据段还是一个堆栈段,这个标志具有不同的功能。(对于32位代码和数据段,这个标志应该总是设置为1;对于16位代码和数据段,这个标志被设置为0。) + + * 可执行代码段。此时这个标志称为D标志并用于指出该段中的指令引用有效地址和操作数的默认长度。如果该标志置位,则默认值是32位地址和32位或8位的操作数;如果该标志为0,则默认值是16位地址和16位或8位的操作数。指令前缀0x66可以用来选择非默认值的操作数大小;前缀0x67可用来选择非默认值的地址大小。 + * 栈段(由SS寄存器指向的数据段)。此时该标志称为B(Big)标志,用于指明隐含堆栈操作(如PUSH、POP或CALL)时的栈指针大小。如果该标志置位,则使用32位栈指针并存放在ESP寄存器中;如果该标志为0,则使用16位栈指针并存放在SP寄存器中。如果堆栈段被设置成一个下扩数据段,这个B标志也同时指定了堆栈段的上界限。 + * 下扩数据段。此时该标志称为B标志,用于指明堆栈段的上界限。如果设置了该标志,则堆栈段的上界限是0xFFFFFFFF(4GB);如果没有设置该标志,则堆栈段的上界限是0xFFFF(64KB)。 From 989afa0c08b2de16fff325bd77671285e28cd231 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 5 Feb 2016 12:00:32 -0500 Subject: [PATCH 018/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index 47be86b..e8a7847 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -120,7 +120,7 @@ lgdt gdt ``` 从上面的表格我们可以看出,当第43位是`0`的时候,这个段描述符对应的是一个数据段,如果该位是`1`,那么表示这个段描述符对应的是一个代码段。对于数据段,第42,41,40位表示的是(*E*扩展,*W*可写,*A*可访问);对于代码段,第42,41,40位表示的是(*C*一致,*R*可读,*A*可访问)。 *A*ccessible) or CRA(*C*onforming *R*eadable *A*ccessible)。 - * 如果`E` = 0,数据段是向上扩展数据段,反之为向下扩展数据段。关于向上扩展和向下扩展数据段,可以参考下面的[链接](http://www.sudleyplace.com/dpmione/expanddown.html). + * 如果`E` = 0,数据段是向上扩展数据段,反之为向下扩展数据段。关于向上扩展和向下扩展数据段,可以参考下面的[链接](http://www.sudleyplace.com/dpmione/expanddown.html)。在一般情况下,应该是不会使用向下扩展数据段的。 * 如果`W` = 1,说明这个数据段是可写的,否则不可写。所有数据段都是可读的。 * A位表示该内存段是否已经被CPU访问。 * 如果`C` = 1,说明这个代码段可以被第优先级的代码访问,比如可以被用户态代码访问。反之如果`C` = 0,说明只能同优先级的代码段可以访问。 @@ -134,8 +134,10 @@ lgdt gdt 7. L 标志(bit 53) - 只对代码段有意义,如果`L` = 1,说明该代码段需要运行在64位模式下。 -8. D/B flag(bit 54) - 根据段描述符描述的是一个可执行代码段、下扩数据段还是一个堆栈段,这个标志具有不同的功能。(对于32位代码和数据段,这个标志应该总是设置为1;对于16位代码和数据段,这个标志被设置为0。) +8. D/B flag(bit 54) - 根据段描述符描述的是一个可执行代码段、下扩数据段还是一个堆栈段,这个标志具有不同的功能。(对于32位代码和数据段,这个标志应该总是设置为1;对于16位代码和数据段,这个标志被设置为0。)。 * 可执行代码段。此时这个标志称为D标志并用于指出该段中的指令引用有效地址和操作数的默认长度。如果该标志置位,则默认值是32位地址和32位或8位的操作数;如果该标志为0,则默认值是16位地址和16位或8位的操作数。指令前缀0x66可以用来选择非默认值的操作数大小;前缀0x67可用来选择非默认值的地址大小。 * 栈段(由SS寄存器指向的数据段)。此时该标志称为B(Big)标志,用于指明隐含堆栈操作(如PUSH、POP或CALL)时的栈指针大小。如果该标志置位,则使用32位栈指针并存放在ESP寄存器中;如果该标志为0,则使用16位栈指针并存放在SP寄存器中。如果堆栈段被设置成一个下扩数据段,这个B标志也同时指定了堆栈段的上界限。 * 下扩数据段。此时该标志称为B标志,用于指明堆栈段的上界限。如果设置了该标志,则堆栈段的上界限是0xFFFFFFFF(4GB);如果没有设置该标志,则堆栈段的上界限是0xFFFF(64KB)。 + + From 37a76fe9d8d31790ee6d1d382765c671fc766d4a Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 5 Feb 2016 12:03:40 -0500 Subject: [PATCH 019/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index e8a7847..71a25fd 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -140,4 +140,15 @@ lgdt gdt * 栈段(由SS寄存器指向的数据段)。此时该标志称为B(Big)标志,用于指明隐含堆栈操作(如PUSH、POP或CALL)时的栈指针大小。如果该标志置位,则使用32位栈指针并存放在ESP寄存器中;如果该标志为0,则使用16位栈指针并存放在SP寄存器中。如果堆栈段被设置成一个下扩数据段,这个B标志也同时指定了堆栈段的上界限。 * 下扩数据段。此时该标志称为B标志,用于指明堆栈段的上界限。如果设置了该标志,则堆栈段的上界限是0xFFFFFFFF(4GB);如果没有设置该标志,则堆栈段的上界限是0xFFFF(64KB)。 +在保护模式下,段寄存器保存的不再是一个内存段的基地址,而是一个称为`段选择器`的结构。每个段描述符都对应一个`段选择器`。`段选择器`是一个16位的数据结构,下图显示了这个数据结构的内容: +``` +----------------------------- +| Index | TI | RPL | +----------------------------- +``` + +Where, +* **Index** shows the index number of the descriptor in the GDT. +* **TI**(Table Indicator) shows where to search for the descriptor. If it is 0 then search in the Global Descriptor Table(GDT) otherwise it will look in Local Descriptor Table(LDT). +* And **RPL** is Requester's Privilege Level. \ No newline at end of file From 84a4565641934fbec38876bc29e7d691c047af50 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 5 Feb 2016 12:06:47 -0500 Subject: [PATCH 020/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index 71a25fd..9f22a5b 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -148,7 +148,8 @@ lgdt gdt ----------------------------- ``` -Where, -* **Index** shows the index number of the descriptor in the GDT. -* **TI**(Table Indicator) shows where to search for the descriptor. If it is 0 then search in the Global Descriptor Table(GDT) otherwise it will look in Local Descriptor Table(LDT). -* And **RPL** is Requester's Privilege Level. \ No newline at end of file +其中, +* **Index** 表示在GDT中,对应段描述符的索引号。 +* **TI** 表示要在GDT还是LDT中查找对应的段描述符 +* **RPL** 表示请求者优先级。这个优先级将和段描述符中的优先级协同工作,共同确定访问是否合法。 + From 95bce96d7a60523a4169cbed9426b8498a31c4b6 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 5 Feb 2016 12:21:21 -0500 Subject: [PATCH 021/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index 9f22a5b..363945b 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -140,7 +140,7 @@ lgdt gdt * 栈段(由SS寄存器指向的数据段)。此时该标志称为B(Big)标志,用于指明隐含堆栈操作(如PUSH、POP或CALL)时的栈指针大小。如果该标志置位,则使用32位栈指针并存放在ESP寄存器中;如果该标志为0,则使用16位栈指针并存放在SP寄存器中。如果堆栈段被设置成一个下扩数据段,这个B标志也同时指定了堆栈段的上界限。 * 下扩数据段。此时该标志称为B标志,用于指明堆栈段的上界限。如果设置了该标志,则堆栈段的上界限是0xFFFFFFFF(4GB);如果没有设置该标志,则堆栈段的上界限是0xFFFF(64KB)。 -在保护模式下,段寄存器保存的不再是一个内存段的基地址,而是一个称为`段选择器`的结构。每个段描述符都对应一个`段选择器`。`段选择器`是一个16位的数据结构,下图显示了这个数据结构的内容: +在保护模式下,段寄存器保存的不再是一个内存段的基地址,而是一个称为`段选择子`的结构。每个段描述符都对应一个`段选择子`。`段选择子`是一个16位的数据结构,下图显示了这个数据结构的内容: ``` ----------------------------- @@ -152,4 +152,14 @@ lgdt gdt * **Index** 表示在GDT中,对应段描述符的索引号。 * **TI** 表示要在GDT还是LDT中查找对应的段描述符 * **RPL** 表示请求者优先级。这个优先级将和段描述符中的优先级协同工作,共同确定访问是否合法。 - + +在保护模式下,每个段寄存器实际上包含下面2部分内容: +* 可见部分 - 段选择子 +* 隐藏部分 - 段描述符 + +在保护模式中,cpu是通过下面的步骤来找到一个具体的物理地址的: + +* 代码必须将相应的`段选择子`装入某个段寄存器 +* CPU根据`段选择子`从GDT中找到一个匹配的段描述符,然后将段描述符放入段寄存器的隐藏部分 +* 在没有开启分页机制的情况下,并且没有使用向下扩展段,那么内存段的基地址就是`段描述符中的基地址`,段描述符的`limit + 1`就是内存段的长度。 + From 2ce839e0d09fc96553c8552c1df3664969a4ff26 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 5 Feb 2016 12:23:51 -0500 Subject: [PATCH 022/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index 363945b..27c2a86 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -161,5 +161,6 @@ lgdt gdt * 代码必须将相应的`段选择子`装入某个段寄存器 * CPU根据`段选择子`从GDT中找到一个匹配的段描述符,然后将段描述符放入段寄存器的隐藏部分 -* 在没有开启分页机制的情况下,并且没有使用向下扩展段,那么内存段的基地址就是`段描述符中的基地址`,段描述符的`limit + 1`就是内存段的长度。 +* 在没有使用向下扩展段的时候,那么内存段的基地址就是`段描述符中的基地址`,段描述符的`limit + 1`就是内存段的长度。如果你知道一个内存地址的`偏移`,那么在没有开启分页机制的情况下,这个内存的物理地址就是`基地址+偏移` +![linear address](http://oi62.tinypic.com/2yo369v.jpg) \ No newline at end of file From 42653b366b54ea270689105d575ed0089792897f Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 5 Feb 2016 12:31:54 -0500 Subject: [PATCH 023/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index 27c2a86..b3ac411 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -163,4 +163,18 @@ lgdt gdt * CPU根据`段选择子`从GDT中找到一个匹配的段描述符,然后将段描述符放入段寄存器的隐藏部分 * 在没有使用向下扩展段的时候,那么内存段的基地址就是`段描述符中的基地址`,段描述符的`limit + 1`就是内存段的长度。如果你知道一个内存地址的`偏移`,那么在没有开启分页机制的情况下,这个内存的物理地址就是`基地址+偏移` -![linear address](http://oi62.tinypic.com/2yo369v.jpg) \ No newline at end of file +![linear address](http://oi62.tinypic.com/2yo369v.jpg) + +当代码要从实模式进入保护模式的时候,需要执行下面的操作: is: + +* 禁止中断发生 +* 使用命令`lgdt`将GDT表装入内存 +* 设置CR0寄存器的PE位为1,是CPU进入保护模式 +* 跳转开始执行保护模式代码 + +在后面的章节中,我们将看到Linux 内核中完整的转换代码。不过在系统进入保护模式之前,还有很多准备工作需要完成。 + +让我们代开C文件 [arch/x86/boot/main.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c)。这个文件包含了很多的函数,这些函数分别会执行键盘初始化,内存堆初始化等等操作...,下面让我们来具体看一些重要的函数。 + +将启动参数拷贝到"zeropage" +-------------------------------------------------------------------------------- \ No newline at end of file From 2e27b813a4e1ddf8a95ed0385d2d1ec2294875a6 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 5 Feb 2016 12:31:59 -0500 Subject: [PATCH 024/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index b3ac411..9b76b89 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -177,4 +177,5 @@ lgdt gdt 让我们代开C文件 [arch/x86/boot/main.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c)。这个文件包含了很多的函数,这些函数分别会执行键盘初始化,内存堆初始化等等操作...,下面让我们来具体看一些重要的函数。 将启动参数拷贝到"zeropage" --------------------------------------------------------------------------------- \ No newline at end of file +-------------------------------------------------------------------------------- + From af8d4b24f617ec03211fc9ef17e7c5f7214c6d2b Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 5 Feb 2016 12:32:47 -0500 Subject: [PATCH 025/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index 9b76b89..8469a27 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -179,3 +179,4 @@ lgdt gdt 将启动参数拷贝到"zeropage" -------------------------------------------------------------------------------- +We will start from the `main` routine in "main.c". First function which is called in `main` is [`copy_boot_params(void)`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L30). It copies the kernel setup header into the field of the `boot_params` structure which is defined in the [arch/x86/include/uapi/asm/bootparam.h](https://github.com/torvalds/linux/blob/master/arch/x86/include/uapi/asm/bootparam.h#L113). From 5b0a6c7d9a9aafb01453b36c07628b48d5688e00 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 5 Feb 2016 12:38:25 -0500 Subject: [PATCH 026/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index 8469a27..447d4e0 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -179,4 +179,4 @@ lgdt gdt 将启动参数拷贝到"zeropage" -------------------------------------------------------------------------------- -We will start from the `main` routine in "main.c". First function which is called in `main` is [`copy_boot_params(void)`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L30). It copies the kernel setup header into the field of the `boot_params` structure which is defined in the [arch/x86/include/uapi/asm/bootparam.h](https://github.com/torvalds/linux/blob/master/arch/x86/include/uapi/asm/bootparam.h#L113). +让我们从`main`函数开始看起,这个函数中,首先调用了[`copy_boot_params(void)`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L30)。 这个函数将内存设置信息拷贝到`boot_params`结构的相应字段。大家可以在[arch/x86/include/uapi/asm/bootparam.h](https://github.com/torvalds/linux/blob/master/arch/x86/include/uapi/asm/bootparam.h#L113)找到`boot_params`结构的定义。 From 5a4d3a82bcbf7403c6be2aa61bcd9d7501ecb06b Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 5 Feb 2016 12:44:17 -0500 Subject: [PATCH 027/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index 447d4e0..58ad6ee 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -179,4 +179,30 @@ lgdt gdt 将启动参数拷贝到"zeropage" -------------------------------------------------------------------------------- -让我们从`main`函数开始看起,这个函数中,首先调用了[`copy_boot_params(void)`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L30)。 这个函数将内存设置信息拷贝到`boot_params`结构的相应字段。大家可以在[arch/x86/include/uapi/asm/bootparam.h](https://github.com/torvalds/linux/blob/master/arch/x86/include/uapi/asm/bootparam.h#L113)找到`boot_params`结构的定义。 +让我们从`main`函数开始看起,这个函数中,首先调用了[`copy_boot_params(void)`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L30)。 + +这个函数将内存设置信息拷贝到`boot_params`结构的相应字段。大家可以在[arch/x86/include/uapi/asm/bootparam.h](https://github.com/torvalds/linux/blob/master/arch/x86/include/uapi/asm/bootparam.h#L113)找到`boot_params`结构的定义。 + +1. 将[header.S](https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S#L281)中定义的`hdr`结构中的内容拷贝到`boot_params`结构的字段`struct setup_header hdr`中。 + +2. 如果内核是通过老的命令行协议运行起来的,那么就更新内核的命令行指针。 + +这里需要注意的是拷贝`hdr`数据结构的`memcpy`函数不是C语言中的函数,而是定义在 [copy.S](https://github.com/torvalds/linux/blob/master/arch/x86/boot/copy.S)。让我们来具体分析一下这段代码: + +```assembly +GLOBAL(memcpy) + pushw %si + pushw %di + movw %ax, %di + movw %dx, %si + pushw %cx + shrw $2, %cx + rep; movsl + popw %cx + andw $3, %cx + rep; movsb + popw %di + popw %si + retl +ENDPROC(memcpy) +``` \ No newline at end of file From 52f605627e0a2126dd555e13d2a0102e7fa7a080 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 5 Feb 2016 12:45:38 -0500 Subject: [PATCH 028/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index 58ad6ee..66058ce 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -205,4 +205,8 @@ GLOBAL(memcpy) popw %si retl ENDPROC(memcpy) -``` \ No newline at end of file +``` + +Yeah, we just moved to C code and now assembly again :) First of all we can see that `memcpy` and other routines which are defined here, start and end with the two macros: `GLOBAL` and `ENDPROC`. `GLOBAL` is described in [arch/x86/include/asm/linkage.h](https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/linkage.h) which defines `globl` directive and the label for it. `ENDPROC` is described in [include/linux/linkage.h](https://github.com/torvalds/linux/blob/master/include/linux/linkage.h) which marks the `name` symbol as a function name and ends with the size of the `name` symbol. + +Implementation of `memcpy` is easy. At first, it pushes values from the `si` and `di` registers to the stack to preserve their values because they will change during the `memcpy`. `memcpy` (and other functions in copy.S) use `fastcall` calling conventions. So it gets its incoming parameters from the `ax`, `dx` and `cx` registers. Calling `memcpy` looks like this: \ No newline at end of file From 9b07bfc5982087d0be1ba480c38071311db0bdf0 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 5 Feb 2016 12:56:17 -0500 Subject: [PATCH 029/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index 66058ce..5091453 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -207,6 +207,22 @@ GLOBAL(memcpy) ENDPROC(memcpy) ``` -Yeah, we just moved to C code and now assembly again :) First of all we can see that `memcpy` and other routines which are defined here, start and end with the two macros: `GLOBAL` and `ENDPROC`. `GLOBAL` is described in [arch/x86/include/asm/linkage.h](https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/linkage.h) which defines `globl` directive and the label for it. `ENDPROC` is described in [include/linux/linkage.h](https://github.com/torvalds/linux/blob/master/include/linux/linkage.h) which marks the `name` symbol as a function name and ends with the size of the `name` symbol. +在`copy.S`文件中,你可以看到所有的方法都开始于`GLOBAL`宏定义,而结束于`ENDPROC`宏定义。 + +你可以在 [arch/x86/include/asm/linkage.h](https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/linkage.h)中找到`GLOBAL`宏定义。这个宏给代码段分配了一个名字标签,并且让这个名字全局可用。 + +```assembly +#define GLOBAL(name) \ + .globl name; \ + name: +``` + +你可以在[include/linux/linkage.h](https://github.com/torvalds/linux/blob/master/include/linux/linkage.h)中找到`ENDPROC`宏的定义。 这个宏通过`END(name)`代码标识了汇编函数的结束,同时将函数名输出,从而静态分析工具可以找到这个函数。 + +```assembly +#define ENDPROC(name) \ + .type name, @function ASM_NL \ + END(name) +``` Implementation of `memcpy` is easy. At first, it pushes values from the `si` and `di` registers to the stack to preserve their values because they will change during the `memcpy`. `memcpy` (and other functions in copy.S) use `fastcall` calling conventions. So it gets its incoming parameters from the `ax`, `dx` and `cx` registers. Calling `memcpy` looks like this: \ No newline at end of file From 1bc809376f0025f870dfcd53531ba376ba30821f Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 19 Feb 2016 11:14:33 -0500 Subject: [PATCH 030/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 35 +++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index 5091453..52f976f 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -191,16 +191,16 @@ lgdt gdt ```assembly GLOBAL(memcpy) - pushw %si - pushw %di - movw %ax, %di - movw %dx, %si - pushw %cx - shrw $2, %cx - rep; movsl - popw %cx - andw $3, %cx - rep; movsb + pushw %si ;push si to stack + pushw %di ;push di to stack + movw %ax, %di ;move &boot_param.hdr to di + movw %dx, %si ;move &hdr to si + pushw %cx ;push cx to stack ( sizeof(hdr) ) + shrw $2, %cx + rep; movsl ;copy based on 4 bytes + popw %cx ;pop cx + andw $3, %cx ;cx = cx % 4 + rep; movsb ;copy based on one byte popw %di popw %si retl @@ -225,4 +225,17 @@ ENDPROC(memcpy) END(name) ``` -Implementation of `memcpy` is easy. At first, it pushes values from the `si` and `di` registers to the stack to preserve their values because they will change during the `memcpy`. `memcpy` (and other functions in copy.S) use `fastcall` calling conventions. So it gets its incoming parameters from the `ax`, `dx` and `cx` registers. Calling `memcpy` looks like this: \ No newline at end of file +`memcpy`的实现代码是很容易理解的。首先,代码将`si`和`di`寄存器的值压入堆栈进行保存,这么做的原因是因为后续的代码将修改`si`和`di`寄存器的值。`memcpy`函数(也包括其他定义在copy.s中的其他函数)使用了`fastcall`调用规则,意味着所有的函数调用参数是通过`ax`, `dx`, `cx`寄存器传入的,而不是传统的通过堆栈传入。因此在使用下面的代码调用`memcpy`函数的时候 + +```c +memcpy(&boot_params.hdr, &hdr, sizeof hdr); +``` + +函数的参数是这样传递的 + +* `ax` 寄存器指向`boot_param.hdr`的内存地址 +* `dx` 寄存器指向`hdr`的内存地址 +* `cx` 寄存器包含`hdr`结构的大小 + +`memcpy`函数在将`si`和`di`寄存器压栈之后,将`boot_param.hdr`的地址放入`di`寄存器,将`hdr`的地址放入`si`寄存器,并且将`hdr`数据结构的大小压栈。 接下来代码首先以4个字节为单位,将`si`寄存器指向的内存内容拷贝`di`寄存器指向的内存。当剩下的字节数不足4字节的时候,代码将原始的`hdr`数据结构大小出栈放入`cx`,然后对`cx`的值对4求模,接下来就是根据`cx`的值,以字节为单位将`si`寄存器指向的内存内容拷贝到`di`寄存器指向的内存。当拷贝操作完成之后,将保留的`si`以及`di`寄存器值出栈,函数返回。 + From 9d140da0c13b163e313d19c70ad6f419eb8a8f7f Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 19 Feb 2016 11:41:27 -0500 Subject: [PATCH 031/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 82 ++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index 52f976f..557fbb9 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -239,3 +239,85 @@ memcpy(&boot_params.hdr, &hdr, sizeof hdr); `memcpy`函数在将`si`和`di`寄存器压栈之后,将`boot_param.hdr`的地址放入`di`寄存器,将`hdr`的地址放入`si`寄存器,并且将`hdr`数据结构的大小压栈。 接下来代码首先以4个字节为单位,将`si`寄存器指向的内存内容拷贝`di`寄存器指向的内存。当剩下的字节数不足4字节的时候,代码将原始的`hdr`数据结构大小出栈放入`cx`,然后对`cx`的值对4求模,接下来就是根据`cx`的值,以字节为单位将`si`寄存器指向的内存内容拷贝到`di`寄存器指向的内存。当拷贝操作完成之后,将保留的`si`以及`di`寄存器值出栈,函数返回。 +控制台初始化 +-------------------------------------------------------------------------------- + +在`hdr`结构体被拷贝到`boot_params.hdr`成员之后,系统接下来将进行控制台的初始化。控制台初始化时通过调用[arch/x86/boot/early_serial_console.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/early_serial_console.c)中定义的`console_init`函数实现的。 + +这个函数首先查看命令行参数是否包含`earlyprintk`选项。如果命令行参数包含该选项,那么函数将分析这个选项的内容。得到控制台将使用的串口信息,然后进行串口的初始化。以下是`earlyprintk`选项可能的取值: + +* serial,0x3f8,115200 +* serial,ttyS0,115200 +* ttyS0,115200 + +当串口初始化成功之后,我们将看到如下的输出如果命令行参数包含`debug`选项。 + +```C +if (cmdline_find_option_bool("debug")) + puts("early console in setup code\n"); +``` + +`puts`函数定义在[tty.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/tty.c)。这个函数只是简单的调用`putchar`函数将输入字符串中的内容按字节输出。下面让我们来看看`putchar`函数的实现: + +```C +void __attribute__((section(".inittext"))) putchar(int ch) +{ + if (ch == '\n') + putchar('\r'); + + bios_putchar(ch); + + if (early_serial_base != 0) + serial_putchar(ch); +} +``` + +`__attribute__((section(".inittext")))` 说明这段代码将被放入`.inittext`代码段。关于`.inittext`代码段的定义你可以在 [setup.ld](https://github.com/torvalds/linux/blob/master/arch/x86/boot/setup.ld#L19)中找到。 + +首先如果需要输出的字符是`\n`,那么`putchar`函数将调用自己首先输出一个字符`\r`。接下来,就调用`bios_putchar`函数将字符输出到显示器(使用bios int10中断): + +```C +static void __attribute__((section(".inittext"))) bios_putchar(int ch) +{ + struct biosregs ireg; + + initregs(&ireg); + ireg.bx = 0x0007; + ireg.cx = 0x0001; + ireg.ah = 0x0e; + ireg.al = ch; + intcall(0x10, &ireg, NULL); +} +``` + +在上面的代码中`initreg`函数接受一个`biosregs`结构的地址作为输入参数,该函数首先调用`memset`函数将`biosregs`结构体所有成员清0。 + +```C + memset(reg, 0, sizeof *reg); + reg->eflags |= X86_EFLAGS_CF; + reg->ds = ds(); + reg->es = ds(); + reg->fs = fs(); + reg->gs = gs(); +``` + +Let's look at the [memset](https://github.com/torvalds/linux/blob/master/arch/x86/boot/copy.S#L36) implementation: + +```assembly +GLOBAL(memset) + pushw %di + movw %ax, %di + movzbl %dl, %eax + imull $0x01010101,%eax + pushw %cx + shrw $2, %cx + rep; stosl + popw %cx + andw $3, %cx + rep; stosb + popw %di + retl +ENDPROC(memset) +``` + +As you can read above, it uses the `fastcall` calling conventions like the `memcpy` function, which means that the function gets parameters from `ax`, `dx` and `cx` registers. \ No newline at end of file From 6b8836182339767007ec4e5c61c8e70ca8994b3c Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 19 Feb 2016 11:42:21 -0500 Subject: [PATCH 032/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index 557fbb9..8bc43b0 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -301,7 +301,7 @@ static void __attribute__((section(".inittext"))) bios_putchar(int ch) reg->gs = gs(); ``` -Let's look at the [memset](https://github.com/torvalds/linux/blob/master/arch/x86/boot/copy.S#L36) implementation: +下面让我们来看看[memset](https://github.com/torvalds/linux/blob/master/arch/x86/boot/copy.S#L36)函数的实现 : ```assembly GLOBAL(memset) From e0b47cc83d77c9ea73461085420ef0a5e94fa3e1 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 19 Feb 2016 11:58:11 -0500 Subject: [PATCH 033/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index 8bc43b0..4e4328d 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -320,4 +320,8 @@ GLOBAL(memset) ENDPROC(memset) ``` -As you can read above, it uses the `fastcall` calling conventions like the `memcpy` function, which means that the function gets parameters from `ax`, `dx` and `cx` registers. \ No newline at end of file +首先你会发现,`memset`函数和`memcpy`函数一样使用了`fastcall`调用规则,因此函数的参数是通过`ax`,`dx`以及`cx`寄存器传入函数内部的。 + +就像memcpy函数一样,`memset`函数一开始将`di`寄存器入栈,然后将`biosregs`结构的地址从`ax`寄存器拷贝到`di`寄存器。记下来,使用`movzbl`指令将`dl`寄存器的内容拷贝到`ax`寄存器的滴字节,到这里`ax`寄存器就包含了需要拷贝到`di`寄存器所指向的内存的值。 + +接下来的`imull`指令将`eax`寄存器的值乘上`0x01010101`。这么做的原因是代码每次将尝试4个字节内存的内容。下面让我们来看一个具体的例子,假设我们需要将`0x7`这个数值放到内存中,在执行`imull`指令之前,`eax`寄存器的值是`0x7`,在`imull`指令被执行之后,`eax`寄存器的内容变成了`0x07070707`。The next instruction multiplies `eax` with `0x01010101`. It needs to because `memset` will copy 4 bytes at the same time. For example, we need to fill a structure with `0x7` with memset. `eax` will contain `0x00000007` value in this case. So if we multiply `eax` with `0x01010101`, we will get `0x07070707` and now we can copy these 4 bytes into the structure. `memset` uses `rep; stosl` instructions for copying `eax` into `es:di`. \ No newline at end of file From 2950f4c9295e6f7ef7f339abc865034e0423d223 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 19 Feb 2016 12:00:05 -0500 Subject: [PATCH 034/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index 4e4328d..2e36514 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -324,4 +324,4 @@ ENDPROC(memset) 就像memcpy函数一样,`memset`函数一开始将`di`寄存器入栈,然后将`biosregs`结构的地址从`ax`寄存器拷贝到`di`寄存器。记下来,使用`movzbl`指令将`dl`寄存器的内容拷贝到`ax`寄存器的滴字节,到这里`ax`寄存器就包含了需要拷贝到`di`寄存器所指向的内存的值。 -接下来的`imull`指令将`eax`寄存器的值乘上`0x01010101`。这么做的原因是代码每次将尝试4个字节内存的内容。下面让我们来看一个具体的例子,假设我们需要将`0x7`这个数值放到内存中,在执行`imull`指令之前,`eax`寄存器的值是`0x7`,在`imull`指令被执行之后,`eax`寄存器的内容变成了`0x07070707`。The next instruction multiplies `eax` with `0x01010101`. It needs to because `memset` will copy 4 bytes at the same time. For example, we need to fill a structure with `0x7` with memset. `eax` will contain `0x00000007` value in this case. So if we multiply `eax` with `0x01010101`, we will get `0x07070707` and now we can copy these 4 bytes into the structure. `memset` uses `rep; stosl` instructions for copying `eax` into `es:di`. \ No newline at end of file +接下来的`imull`指令将`eax`寄存器的值乘上`0x01010101`。这么做的原因是代码每次将尝试拷贝4个字节内存的内容。下面让我们来看一个具体的例子,假设我们需要将`0x7`这个数值放到内存中,在执行`imull`指令之前,`eax`寄存器的值是`0x7`,在`imull`指令被执行之后,`eax`寄存器的内容变成了`0x07070707`(4个字节的`0x7`)。在`imull`指令之后,代码使用`rep; stosl`指令将`eax`寄存器的内容拷贝到`es:di`指向的内存。 \ No newline at end of file From 011b2c5ec3b54f22728386bb048f906ad57e5cb3 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 19 Feb 2016 12:02:19 -0500 Subject: [PATCH 035/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index 2e36514..8de4b2c 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -324,4 +324,6 @@ ENDPROC(memset) 就像memcpy函数一样,`memset`函数一开始将`di`寄存器入栈,然后将`biosregs`结构的地址从`ax`寄存器拷贝到`di`寄存器。记下来,使用`movzbl`指令将`dl`寄存器的内容拷贝到`ax`寄存器的滴字节,到这里`ax`寄存器就包含了需要拷贝到`di`寄存器所指向的内存的值。 -接下来的`imull`指令将`eax`寄存器的值乘上`0x01010101`。这么做的原因是代码每次将尝试拷贝4个字节内存的内容。下面让我们来看一个具体的例子,假设我们需要将`0x7`这个数值放到内存中,在执行`imull`指令之前,`eax`寄存器的值是`0x7`,在`imull`指令被执行之后,`eax`寄存器的内容变成了`0x07070707`(4个字节的`0x7`)。在`imull`指令之后,代码使用`rep; stosl`指令将`eax`寄存器的内容拷贝到`es:di`指向的内存。 \ No newline at end of file +接下来的`imull`指令将`eax`寄存器的值乘上`0x01010101`。这么做的原因是代码每次将尝试拷贝4个字节内存的内容。下面让我们来看一个具体的例子,假设我们需要将`0x7`这个数值放到内存中,在执行`imull`指令之前,`eax`寄存器的值是`0x7`,在`imull`指令被执行之后,`eax`寄存器的内容变成了`0x07070707`(4个字节的`0x7`)。在`imull`指令之后,代码使用`rep; stosl`指令将`eax`寄存器的内容拷贝到`es:di`指向的内存。 + +在`bisoregs`结构体被`initregs`函数正确填充之后,`bios_putchar` 调用中断 [0x10](http://www.ctyme.com/intr/rb-0106.htm) interrupt which prints a character. Afterwards it checks if the serial port was initialized or not and writes a character there with [serial_putchar](https://github.com/torvalds/linux/blob/master/arch/x86/boot/tty.c#L30) and `inb/outb` instructions if it was set. \ No newline at end of file From 16c718b3bed2c689a69a7d7abb42342cb4c731c9 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 19 Feb 2016 12:04:31 -0500 Subject: [PATCH 036/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index 8de4b2c..dc623ce 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -326,4 +326,4 @@ ENDPROC(memset) 接下来的`imull`指令将`eax`寄存器的值乘上`0x01010101`。这么做的原因是代码每次将尝试拷贝4个字节内存的内容。下面让我们来看一个具体的例子,假设我们需要将`0x7`这个数值放到内存中,在执行`imull`指令之前,`eax`寄存器的值是`0x7`,在`imull`指令被执行之后,`eax`寄存器的内容变成了`0x07070707`(4个字节的`0x7`)。在`imull`指令之后,代码使用`rep; stosl`指令将`eax`寄存器的内容拷贝到`es:di`指向的内存。 -在`bisoregs`结构体被`initregs`函数正确填充之后,`bios_putchar` 调用中断 [0x10](http://www.ctyme.com/intr/rb-0106.htm) interrupt which prints a character. Afterwards it checks if the serial port was initialized or not and writes a character there with [serial_putchar](https://github.com/torvalds/linux/blob/master/arch/x86/boot/tty.c#L30) and `inb/outb` instructions if it was set. \ No newline at end of file +在`bisoregs`结构体被`initregs`函数正确填充之后,`bios_putchar` 调用中断 [0x10](http://www.ctyme.com/intr/rb-0106.htm) 在显示器上输出一个字符。接下来`putchar`函数检查是否初始化了串口,如果串口被初始化了,那么将调用[serial_putchar](https://github.com/torvalds/linux/blob/master/arch/x86/boot/tty.c#L30)将字符输出到串口。 \ No newline at end of file From cd6d03d44c10247ac97b36ae5f03883a2137c1ff Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 19 Feb 2016 12:04:58 -0500 Subject: [PATCH 037/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index dc623ce..e0b859c 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -326,4 +326,29 @@ ENDPROC(memset) 接下来的`imull`指令将`eax`寄存器的值乘上`0x01010101`。这么做的原因是代码每次将尝试拷贝4个字节内存的内容。下面让我们来看一个具体的例子,假设我们需要将`0x7`这个数值放到内存中,在执行`imull`指令之前,`eax`寄存器的值是`0x7`,在`imull`指令被执行之后,`eax`寄存器的内容变成了`0x07070707`(4个字节的`0x7`)。在`imull`指令之后,代码使用`rep; stosl`指令将`eax`寄存器的内容拷贝到`es:di`指向的内存。 -在`bisoregs`结构体被`initregs`函数正确填充之后,`bios_putchar` 调用中断 [0x10](http://www.ctyme.com/intr/rb-0106.htm) 在显示器上输出一个字符。接下来`putchar`函数检查是否初始化了串口,如果串口被初始化了,那么将调用[serial_putchar](https://github.com/torvalds/linux/blob/master/arch/x86/boot/tty.c#L30)将字符输出到串口。 \ No newline at end of file +在`bisoregs`结构体被`initregs`函数正确填充之后,`bios_putchar` 调用中断 [0x10](http://www.ctyme.com/intr/rb-0106.htm) 在显示器上输出一个字符。接下来`putchar`函数检查是否初始化了串口,如果串口被初始化了,那么将调用[serial_putchar](https://github.com/torvalds/linux/blob/master/arch/x86/boot/tty.c#L30)将字符输出到串口。 + +堆初始化 +-------------------------------------------------------------------------------- + +After the stack and bss section were prepared in [header.S](https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S) (see previous [part](linux-bootstrap-1.md)), the kernel needs to initialize the [heap](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L116) with the [`init_heap`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L116) function. + +First of all `init_heap` checks the [`CAN_USE_HEAP`](https://github.com/torvalds/linux/blob/master/arch/x86/include/uapi/asm/bootparam.h#L21) flag from the [`loadflags`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S#L321) in the kernel setup header and calculates the end of the stack if this flag was set: + +```C + char *stack_end; + + if (boot_params.hdr.loadflags & CAN_USE_HEAP) { + asm("leal %P1(%%esp),%0" + : "=r" (stack_end) : "i" (-STACK_SIZE)); +``` + +or in other words `stack_end = esp - STACK_SIZE`. + +Then there is the `heap_end` calculation: +```c + heap_end = (char *)((size_t)boot_params.hdr.heap_end_ptr + 0x200); +``` +which means `heap_end_ptr` or `_end` + `512`(`0x200h`). The last check is whether `heap_end` is greater than `stack_end`. If it is then `stack_end` is assigned to `heap_end` to make them equal. + +Now the heap is initialized and we can use it using the `GET_HEAP` method. We will see how it is used, how to use it and how the it is implemented in the next posts. \ No newline at end of file From 64b594d3ea2c2931f9392a3db40441e880921c0e Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 19 Feb 2016 12:12:08 -0500 Subject: [PATCH 038/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index e0b859c..54df7f7 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -331,7 +331,7 @@ ENDPROC(memset) 堆初始化 -------------------------------------------------------------------------------- -After the stack and bss section were prepared in [header.S](https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S) (see previous [part](linux-bootstrap-1.md)), the kernel needs to initialize the [heap](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L116) with the [`init_heap`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L116) function. +当堆栈和bss段在[header.S](https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S)中被初始化之后 (细节请参考上一篇[part](linux-bootstrap-1.md)), 内核需要初始化全局堆,全局堆[heap](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L116) 的初始化是通过 [`init_heap`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L116) function. First of all `init_heap` checks the [`CAN_USE_HEAP`](https://github.com/torvalds/linux/blob/master/arch/x86/include/uapi/asm/bootparam.h#L21) flag from the [`loadflags`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S#L321) in the kernel setup header and calculates the end of the stack if this flag was set: From 75f04f44364e918e0c18a18a1f99018929033f75 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 19 Feb 2016 12:12:25 -0500 Subject: [PATCH 039/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index 54df7f7..c65d8c4 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -331,7 +331,7 @@ ENDPROC(memset) 堆初始化 -------------------------------------------------------------------------------- -当堆栈和bss段在[header.S](https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S)中被初始化之后 (细节请参考上一篇[part](linux-bootstrap-1.md)), 内核需要初始化全局堆,全局堆[heap](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L116) 的初始化是通过 [`init_heap`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L116) function. +当堆栈和bss段在[header.S](https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S)中被初始化之后 (细节请参考上一篇[part](linux-bootstrap-1.md)), 内核需要初始化全局堆,全局堆[heap](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L116) 的初始化是通过 [`init_heap`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L116) 函数实现的。 First of all `init_heap` checks the [`CAN_USE_HEAP`](https://github.com/torvalds/linux/blob/master/arch/x86/include/uapi/asm/bootparam.h#L21) flag from the [`loadflags`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S#L321) in the kernel setup header and calculates the end of the stack if this flag was set: From c8a72397b8f2e8d15fe34e8d72b2960c169d042f Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 19 Feb 2016 12:12:55 -0500 Subject: [PATCH 040/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index c65d8c4..c03c67d 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -331,7 +331,7 @@ ENDPROC(memset) 堆初始化 -------------------------------------------------------------------------------- -当堆栈和bss段在[header.S](https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S)中被初始化之后 (细节请参考上一篇[part](linux-bootstrap-1.md)), 内核需要初始化全局堆,全局堆[heap](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L116) 的初始化是通过 [`init_heap`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L116) 函数实现的。 +当堆栈和bss段在[header.S](https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S)中被初始化之后 (细节请参考上一篇[part](linux-bootstrap-1.md)), 内核需要初始化全局堆,全局堆的初始化是通过 [`init_heap`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L116) 函数实现的。 First of all `init_heap` checks the [`CAN_USE_HEAP`](https://github.com/torvalds/linux/blob/master/arch/x86/include/uapi/asm/bootparam.h#L21) flag from the [`loadflags`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S#L321) in the kernel setup header and calculates the end of the stack if this flag was set: From 19f2a6f415027e38e441d510f3f44261065e2a44 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 19 Feb 2016 12:14:46 -0500 Subject: [PATCH 041/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 213 ++++++++++++++++++++++++++++++++- 1 file changed, 212 insertions(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index c03c67d..b436d67 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -351,4 +351,215 @@ Then there is the `heap_end` calculation: ``` which means `heap_end_ptr` or `_end` + `512`(`0x200h`). The last check is whether `heap_end` is greater than `stack_end`. If it is then `stack_end` is assigned to `heap_end` to make them equal. -Now the heap is initialized and we can use it using the `GET_HEAP` method. We will see how it is used, how to use it and how the it is implemented in the next posts. \ No newline at end of file +Now the heap is initialized and we can use it using the `GET_HEAP` method. We will see how it is used, how to use it and how the it is implemented in the next posts. + +CPU validation +-------------------------------------------------------------------------------- + +The next step as we can see is cpu validation by `validate_cpu` from [arch/x86/boot/cpu.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/cpu.c). + +It calls the [`check_cpu`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/cpucheck.c#L102) function and passes cpu level and required cpu level to it and checks that the kernel launches on the right cpu level. +```c +check_cpu(&cpu_level, &req_level, &err_flags); +if (cpu_level < req_level) { + ... + return -1; +} +``` +`check_cpu` checks the cpu's flags, presence of [long mode](http://en.wikipedia.org/wiki/Long_mode) in case of x86_64(64-bit) CPU, checks the processor's vendor and makes preparation for certain vendors like turning off SSE+SSE2 for AMD if they are missing, etc. + +Memory detection +-------------------------------------------------------------------------------- + +The next step is memory detection by the `detect_memory` function. `detect_memory` basically provides a map of available RAM to the cpu. It uses different programming interfaces for memory detection like `0xe820`, `0xe801` and `0x88`. We will see only the implementation of **0xE820** here. + +Let's look into the `detect_memory_e820` implementation from the [arch/x86/boot/memory.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/memory.c) source file. First of all, the `detect_memory_e820` function initializes the `biosregs` structure as we saw above and fills registers with special values for the `0xe820` call: + +```assembly + initregs(&ireg); + ireg.ax = 0xe820; + ireg.cx = sizeof buf; + ireg.edx = SMAP; + ireg.di = (size_t)&buf; +``` + +* `ax` contains the number of the function (0xe820 in our case) +* `cx` register contains size of the buffer which will contain data about memory +* `edx` must contain the `SMAP` magic number +* `es:di` must contain the address of the buffer which will contain memory data +* `ebx` has to be zero. + +Next is a loop where data about the memory will be collected. It starts from the call of the `0x15` BIOS interrupt, which writes one line from the address allocation table. For getting the next line we need to call this interrupt again (which we do in the loop). Before the next call `ebx` must contain the value returned previously: + +```C + intcall(0x15, &ireg, &oreg); + ireg.ebx = oreg.ebx; +``` + +Ultimately, it does iterations in the loop to collect data from the address allocation table and writes this data into the `e820entry` array: + +* start of memory segment +* size of memory segment +* type of memory segment (which can be reserved, usable and etc...). + +You can see the result of this in the `dmesg` output, something like: + +``` +[ 0.000000] e820: BIOS-provided physical RAM map: +[ 0.000000] BIOS-e820: [mem 0x0000000000000000-0x000000000009fbff] usable +[ 0.000000] BIOS-e820: [mem 0x000000000009fc00-0x000000000009ffff] reserved +[ 0.000000] BIOS-e820: [mem 0x00000000000f0000-0x00000000000fffff] reserved +[ 0.000000] BIOS-e820: [mem 0x0000000000100000-0x000000003ffdffff] usable +[ 0.000000] BIOS-e820: [mem 0x000000003ffe0000-0x000000003fffffff] reserved +[ 0.000000] BIOS-e820: [mem 0x00000000fffc0000-0x00000000ffffffff] reserved +``` + +Keyboard initialization +-------------------------------------------------------------------------------- + +The next step is the initialization of the keyboard with the call of the [`keyboard_init()`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L65) function. At first `keyboard_init` initializes registers using the `initregs` function and calling the [0x16](http://www.ctyme.com/intr/rb-1756.htm) interrupt for getting the keyboard status. +```c + initregs(&ireg); + ireg.ah = 0x02; /* Get keyboard status */ + intcall(0x16, &ireg, &oreg); + boot_params.kbd_status = oreg.al; +``` +After this it calls [0x16](http://www.ctyme.com/intr/rb-1757.htm) again to set repeat rate and delay. +```c + ireg.ax = 0x0305; /* Set keyboard repeat rate */ + intcall(0x16, &ireg, NULL); +``` + +Querying +-------------------------------------------------------------------------------- + +The next couple of steps are queries for different parameters. We will not dive into details about these queries, but will get back to it in later parts. Let's take a short look at these functions: + +The [query_mca](https://github.com/torvalds/linux/blob/master/arch/x86/boot/mca.c#L18) routine calls the [0x15](http://www.ctyme.com/intr/rb-1594.htm) BIOS interrupt to get the machine model number, sub-model number, BIOS revision level, and other hardware-specific attributes: + +```c +int query_mca(void) +{ + struct biosregs ireg, oreg; + u16 len; + + initregs(&ireg); + ireg.ah = 0xc0; + intcall(0x15, &ireg, &oreg); + + if (oreg.eflags & X86_EFLAGS_CF) + return -1; /* No MCA present */ + + set_fs(oreg.es); + len = rdfs16(oreg.bx); + + if (len > sizeof(boot_params.sys_desc_table)) + len = sizeof(boot_params.sys_desc_table); + + copy_from_fs(&boot_params.sys_desc_table, oreg.bx, len); + return 0; +} +``` + +It fills the `ah` register with `0xc0` and calls the `0x15` BIOS interruption. After the interrupt execution it checks the [carry flag](http://en.wikipedia.org/wiki/Carry_flag) and if it is set to 1, the BIOS doesn't support (**MCA**)[https://en.wikipedia.org/wiki/Micro_Channel_architecture]. If carry flag is set to 0, `ES:BX` will contain a pointer to the system information table, which looks like this: + +``` +Offset Size Description + 00h WORD number of bytes following + 02h BYTE model (see #00515) + 03h BYTE submodel (see #00515) + 04h BYTE BIOS revision: 0 for first release, 1 for 2nd, etc. + 05h BYTE feature byte 1 (see #00510) + 06h BYTE feature byte 2 (see #00511) + 07h BYTE feature byte 3 (see #00512) + 08h BYTE feature byte 4 (see #00513) + 09h BYTE feature byte 5 (see #00514) +---AWARD BIOS--- + 0Ah N BYTEs AWARD copyright notice +---Phoenix BIOS--- + 0Ah BYTE ??? (00h) + 0Bh BYTE major version + 0Ch BYTE minor version (BCD) + 0Dh 4 BYTEs ASCIZ string "PTL" (Phoenix Technologies Ltd) +---Quadram Quad386--- + 0Ah 17 BYTEs ASCII signature string "Quadram Quad386XT" +---Toshiba (Satellite Pro 435CDS at least)--- + 0Ah 7 BYTEs signature "TOSHIBA" + 11h BYTE ??? (8h) + 12h BYTE ??? (E7h) product ID??? (guess) + 13h 3 BYTEs "JPN" + ``` + +Next we call the `set_fs` routine and pass the value of the `es` register to it. The implementation of `set_fs` is pretty simple: + +```c +static inline void set_fs(u16 seg) +{ + asm volatile("movw %0,%%fs" : : "rm" (seg)); +} +``` + +This function contains inline assembly which gets the value of the `seg` parameter and puts it into the `fs` register. There are many functions in [boot.h](https://github.com/torvalds/linux/blob/master/arch/x86/boot/boot.h) like `set_fs`, for example `set_gs`, `fs`, `gs` for reading a value in it etc... + +At the end of `query_mca` it just copies the table pointed to by `es:bx` to the `boot_params.sys_desc_table`. + +The next step is getting [Intel SpeedStep](http://en.wikipedia.org/wiki/SpeedStep) information by calling the `query_ist` function. First of all it checks the CPU level and if it is correct, calls `0x15` for getting info and saves the result to `boot_params`. + +The following [query_apm_bios](https://github.com/torvalds/linux/blob/master/arch/x86/boot/apm.c#L21) function gets [Advanced Power Management](http://en.wikipedia.org/wiki/Advanced_Power_Management) information from the BIOS. `query_apm_bios` calls the `0x15` BIOS interruption too, but with `ah` = `0x53` to check `APM` installation. After the `0x15` execution, `query_apm_bios` functions check the `PM` signature (it must be `0x504d`), carry flag (it must be 0 if `APM` supported) and value of the `cx` register (if it's 0x02, protected mode interface is supported). + +Next it calls `0x15` again, but with `ax = 0x5304` for disconnecting the `APM` interface and connecting the 32-bit protected mode interface. In the end it fills `boot_params.apm_bios_info` with values obtained from the BIOS. + +Note that `query_apm_bios` will be executed only if `CONFIG_APM` or `CONFIG_APM_MODULE` was set in the configuration file: + +```C +#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE) + query_apm_bios(); +#endif +``` + +The last is the [`query_edd`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/edd.c#L122) function, which queries `Enhanced Disk Drive` information from the BIOS. Let's look into the `query_edd` implementation. + +First of all it reads the [edd](https://github.com/torvalds/linux/blob/master/Documentation/kernel-parameters.txt#L1023) option from the kernel's command line and if it was set to `off` then `query_edd` just returns. + +If EDD is enabled, `query_edd` goes over BIOS-supported hard disks and queries EDD information in the following loop: + +```C +for (devno = 0x80; devno < 0x80+EDD_MBR_SIG_MAX; devno++) { + if (!get_edd_info(devno, &ei) && boot_params.eddbuf_entries < EDDMAXNR) { + memcpy(edp, &ei, sizeof ei); + edp++; + boot_params.eddbuf_entries++; + } + ... + ... + ... +``` + +where `0x80` is the first hard drive and the value of `EDD_MBR_SIG_MAX` macro is 16. It collects data into the array of [edd_info](https://github.com/torvalds/linux/blob/master/include/uapi/linux/edd.h#L172) structures. `get_edd_info` checks that EDD is present by invoking the `0x13` interrupt with `ah` as `0x41` and if EDD is present, `get_edd_info` again calls the `0x13` interrupt, but with `ah` as `0x48` and `si` containing the address of the buffer where EDD information will be stored. + +Conclusion +-------------------------------------------------------------------------------- + +This is the end of the second part about Linux kernel insides. In the next part we will see video mode setting and the rest of preparations before transition to protected mode and directly transitioning into it. + +If you have any questions or suggestions write me a comment or ping me at [twitter](https://twitter.com/0xAX). + +**Please note that English is not my first language, And I am really sorry for any inconvenience. If you find any mistakes please send me a PR to [linux-insides](https://github.com/0xAX/linux-internals).** + +Links +-------------------------------------------------------------------------------- + +* [Protected mode](http://en.wikipedia.org/wiki/Protected_mode) +* [Protected mode](http://wiki.osdev.org/Protected_Mode) +* [Long mode](http://en.wikipedia.org/wiki/Long_mode) +* [Nice explanation of CPU Modes with code](http://www.codeproject.com/Articles/45788/The-Real-Protected-Long-mode-assembly-tutorial-for) +* [How to Use Expand Down Segments on Intel 386 and Later CPUs](http://www.sudleyplace.com/dpmione/expanddown.html) +* [earlyprintk documentation](http://lxr.free-electrons.com/source/Documentation/x86/earlyprintk.txt) +* [Kernel Parameters](https://github.com/torvalds/linux/blob/master/Documentation/kernel-parameters.txt) +* [Serial console](https://github.com/torvalds/linux/blob/master/Documentation/serial-console.txt) +* [Intel SpeedStep](http://en.wikipedia.org/wiki/SpeedStep) +* [APM](https://en.wikipedia.org/wiki/Advanced_Power_Management) +* [EDD specification](http://www.t13.org/documents/UploadedDocuments/docs2004/d1572r3-EDD3.pdf) +* [TLDP documentation for Linux Boot Process](http://www.tldp.org/HOWTO/Linux-i386-Boot-Code-HOWTO/setup.html) (old) +* [Previous Part](linux-bootstrap-1.md) + From 98796cafb981937cd566da2016bd49fb4704bd4d Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 19 Feb 2016 12:14:47 -0500 Subject: [PATCH 042/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index b436d67..7d1e849 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -561,5 +561,4 @@ Links * [APM](https://en.wikipedia.org/wiki/Advanced_Power_Management) * [EDD specification](http://www.t13.org/documents/UploadedDocuments/docs2004/d1572r3-EDD3.pdf) * [TLDP documentation for Linux Boot Process](http://www.tldp.org/HOWTO/Linux-i386-Boot-Code-HOWTO/setup.html) (old) -* [Previous Part](linux-bootstrap-1.md) - +* [Previous Part](linux-bootstrap-1.md) \ No newline at end of file From 3909c1ab366d8f3cd15f0bf4faaddc3cb71d3f0e Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 19 Feb 2016 12:20:17 -0500 Subject: [PATCH 043/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index 7d1e849..fcedf19 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -333,7 +333,7 @@ ENDPROC(memset) 当堆栈和bss段在[header.S](https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S)中被初始化之后 (细节请参考上一篇[part](linux-bootstrap-1.md)), 内核需要初始化全局堆,全局堆的初始化是通过 [`init_heap`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L116) 函数实现的。 -First of all `init_heap` checks the [`CAN_USE_HEAP`](https://github.com/torvalds/linux/blob/master/arch/x86/include/uapi/asm/bootparam.h#L21) flag from the [`loadflags`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S#L321) in the kernel setup header and calculates the end of the stack if this flag was set: +代码首先检查内核设置头中的[`loadflags`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S#L321)是否设置了 [`CAN_USE_HEAP`](https://github.com/torvalds/linux/blob/master/arch/x86/include/uapi/asm/bootparam.h#L21)标志。 如果该标记被设置了,那么代码将计算堆栈的结束地址:: ```C char *stack_end; @@ -343,7 +343,7 @@ First of all `init_heap` checks the [`CAN_USE_HEAP`](https://github.com/torvalds : "=r" (stack_end) : "i" (-STACK_SIZE)); ``` -or in other words `stack_end = esp - STACK_SIZE`. +`stack_end = esp - STACK_SIZE`. Then there is the `heap_end` calculation: ```c From 68612146c4003082aa8ed61030df075ab1cee6fb Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 19 Feb 2016 12:22:02 -0500 Subject: [PATCH 044/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index fcedf19..53824b0 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -337,13 +337,14 @@ ENDPROC(memset) ```C char *stack_end; - + + //%P1 is (-STACK_SIZE) if (boot_params.hdr.loadflags & CAN_USE_HEAP) { asm("leal %P1(%%esp),%0" : "=r" (stack_end) : "i" (-STACK_SIZE)); ``` -`stack_end = esp - STACK_SIZE`. +换言之`stack_end = esp - STACK_SIZE`. Then there is the `heap_end` calculation: ```c From b6412147cd160c8fbf31334a2c86cfaab06cebc2 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 19 Feb 2016 12:22:36 -0500 Subject: [PATCH 045/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index 53824b0..6ce8b95 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -346,7 +346,8 @@ ENDPROC(memset) 换言之`stack_end = esp - STACK_SIZE`. -Then there is the `heap_end` calculation: +有了堆栈结束地址之后,堆的结束地址就被计算出来了: + ```c heap_end = (char *)((size_t)boot_params.hdr.heap_end_ptr + 0x200); ``` From 0d46633885256d1d87049b0d0c1d4eb902fa7156 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 19 Feb 2016 12:25:10 -0500 Subject: [PATCH 046/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index 6ce8b95..ced42e9 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -346,9 +346,10 @@ ENDPROC(memset) 换言之`stack_end = esp - STACK_SIZE`. -有了堆栈结束地址之后,堆的结束地址就被计算出来了: +在计算了堆栈结束地址之后,代码计算了堆的结束地址: ```c + //heap_end = heap_end_ptr + 512 heap_end = (char *)((size_t)boot_params.hdr.heap_end_ptr + 0x200); ``` which means `heap_end_ptr` or `_end` + `512`(`0x200h`). The last check is whether `heap_end` is greater than `stack_end`. If it is then `stack_end` is assigned to `heap_end` to make them equal. From 3f5d9ed210c7e41f7ed92a38ebcfef27e6e9d05e Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 19 Feb 2016 12:25:33 -0500 Subject: [PATCH 047/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index ced42e9..c67ee55 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -349,6 +349,7 @@ ENDPROC(memset) 在计算了堆栈结束地址之后,代码计算了堆的结束地址: ```c + //heap_end = heap_end_ptr + 512 heap_end = (char *)((size_t)boot_params.hdr.heap_end_ptr + 0x200); ``` From f9079f8ed187b4e748994bd3ab2e0804f6c103bb Mon Sep 17 00:00:00 2001 From: hailin cai Date: Fri, 19 Feb 2016 12:28:47 -0500 Subject: [PATCH 048/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index c67ee55..658426c 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -353,9 +353,10 @@ ENDPROC(memset) //heap_end = heap_end_ptr + 512 heap_end = (char *)((size_t)boot_params.hdr.heap_end_ptr + 0x200); ``` -which means `heap_end_ptr` or `_end` + `512`(`0x200h`). The last check is whether `heap_end` is greater than `stack_end`. If it is then `stack_end` is assigned to `heap_end` to make them equal. -Now the heap is initialized and we can use it using the `GET_HEAP` method. We will see how it is used, how to use it and how the it is implemented in the next posts. +接下来代码判断`heap_end`是否大于`stack_end`,如果条件成立,将`stack_end`设置成`heap_end`(这么做是因为在大部分系统中全局堆和堆栈是相邻的)。 + +到这里位置,全局堆就被正确初始化了。在全局堆被初始化之后,我们就可以使用`GET_HEAP`方法。至于这个函数的实现和使用,我们将在后续的章节中看到。 CPU validation -------------------------------------------------------------------------------- From f70fea08a839459798a59aa1c89a43613b2e0caa Mon Sep 17 00:00:00 2001 From: hailin Date: Fri, 19 Feb 2016 12:31:13 -0500 Subject: [PATCH 049/153] add more translation --- Booting/linux-bootstrap-2md.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 Booting/linux-bootstrap-2md.md diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md old mode 100644 new mode 100755 From fa9907d20fc650b35ccc1cde9d62d3ef3cf1e145 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Tue, 23 Feb 2016 15:20:31 -0500 Subject: [PATCH 050/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) mode change 100755 => 100644 Booting/linux-bootstrap-2md.md diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md old mode 100755 new mode 100644 index 658426c..af38404 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -358,19 +358,23 @@ ENDPROC(memset) 到这里位置,全局堆就被正确初始化了。在全局堆被初始化之后,我们就可以使用`GET_HEAP`方法。至于这个函数的实现和使用,我们将在后续的章节中看到。 -CPU validation +检查CPU类型 -------------------------------------------------------------------------------- -The next step as we can see is cpu validation by `validate_cpu` from [arch/x86/boot/cpu.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/cpu.c). +在堆栈初始化之后,内核代码通过调用[arch/x86/boot/cpu.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/cpu.c)提供的`validate_cpu`方法检查CPU类型以确定系统是否能够在当前的CPU上运行。 + +`validate_cpu`调用了[`check_cpu`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/cpucheck.c#L102)方法得到当前系统的CPU级别,并且和系统预设的最低CPU级别进行比较。如果不满足条件,则不允许系统运行。function and passes cpu level and required cpu level to it and checks that the kernel launches on the right cpu level. -It calls the [`check_cpu`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/cpucheck.c#L102) function and passes cpu level and required cpu level to it and checks that the kernel launches on the right cpu level. ```c +/*from cpu.c*/ check_cpu(&cpu_level, &req_level, &err_flags); if (cpu_level < req_level) { - ... + printf("This kernel requires an %s CPU, ", cpu_name(req_level)); + printf("but only detected an %s CPU.\n", cpu_name(cpu_level)); return -1; } ``` + `check_cpu` checks the cpu's flags, presence of [long mode](http://en.wikipedia.org/wiki/Long_mode) in case of x86_64(64-bit) CPU, checks the processor's vendor and makes preparation for certain vendors like turning off SSE+SSE2 for AMD if they are missing, etc. Memory detection From 18dd9dd74f094ead27d593fff55464648a486977 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Tue, 23 Feb 2016 15:21:07 -0500 Subject: [PATCH 051/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index af38404..1defd5f 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -368,6 +368,7 @@ ENDPROC(memset) ```c /*from cpu.c*/ check_cpu(&cpu_level, &req_level, &err_flags); +/*after check_cpu call, req_level = req_level defined in cpucheck.c*/ if (cpu_level < req_level) { printf("This kernel requires an %s CPU, ", cpu_name(req_level)); printf("but only detected an %s CPU.\n", cpu_name(cpu_level)); From 6c712df893cc1da52904a8d7458ba5f238006daa Mon Sep 17 00:00:00 2001 From: hailin cai Date: Tue, 23 Feb 2016 15:40:20 -0500 Subject: [PATCH 052/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index 1defd5f..59d2a2f 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -376,7 +376,7 @@ if (cpu_level < req_level) { } ``` -`check_cpu` checks the cpu's flags, presence of [long mode](http://en.wikipedia.org/wiki/Long_mode) in case of x86_64(64-bit) CPU, checks the processor's vendor and makes preparation for certain vendors like turning off SSE+SSE2 for AMD if they are missing, etc. +`check_cpu`方法做了大量的检测和设置工作,下面就简单介绍 checks the cpu's flags, presence of [long mode](http://en.wikipedia.org/wiki/Long_mode) in case of x86_64(64-bit) CPU, checks the processor's vendor and makes preparation for certain vendors like turning off SSE+SSE2 for AMD if they are missing, etc. Memory detection -------------------------------------------------------------------------------- From b8c8eff7e7fe7f5fda2c2f9bc1eb6b6587932a7a Mon Sep 17 00:00:00 2001 From: hailin cai Date: Tue, 23 Feb 2016 15:41:17 -0500 Subject: [PATCH 053/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index 59d2a2f..ba1d14f 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -376,7 +376,7 @@ if (cpu_level < req_level) { } ``` -`check_cpu`方法做了大量的检测和设置工作,下面就简单介绍 checks the cpu's flags, presence of [long mode](http://en.wikipedia.org/wiki/Long_mode) in case of x86_64(64-bit) CPU, checks the processor's vendor and makes preparation for certain vendors like turning off SSE+SSE2 for AMD if they are missing, etc. +`check_cpu`方法做了大量的检测和设置工作,下面就简单介绍一些:1)检查cpu标志,如果cpu是64位cpu,那么就设置[long mode](http://en.wikipedia.org/wiki/Long_mode), 2) 检查CPU的制造商checks the processor's vendor and makes preparation for certain vendors like turning off SSE+SSE2 for AMD if they are missing, etc. Memory detection -------------------------------------------------------------------------------- From 0c597baa0102fecc0587572a432d47c05a974ec0 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Tue, 23 Feb 2016 15:42:05 -0500 Subject: [PATCH 054/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index ba1d14f..f21a289 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -376,7 +376,7 @@ if (cpu_level < req_level) { } ``` -`check_cpu`方法做了大量的检测和设置工作,下面就简单介绍一些:1)检查cpu标志,如果cpu是64位cpu,那么就设置[long mode](http://en.wikipedia.org/wiki/Long_mode), 2) 检查CPU的制造商checks the processor's vendor and makes preparation for certain vendors like turning off SSE+SSE2 for AMD if they are missing, etc. +`check_cpu`方法做了大量的检测和设置工作,下面就简单介绍一些:1)检查cpu标志,如果cpu是64位cpu,那么就设置[long mode](http://en.wikipedia.org/wiki/Long_mode), 2) 检查CPU的制造商,根据制造商的不同,设置不同的CPU选项。比如对于AMD出厂的cpu,如果不支持SSE+SSE2,那么就禁止这些选项。 Memory detection -------------------------------------------------------------------------------- From 899cca1b1e764d92910d9df7f57012b4e91d6ba4 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Tue, 23 Feb 2016 15:42:54 -0500 Subject: [PATCH 055/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index f21a289..27286e3 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -378,10 +378,10 @@ if (cpu_level < req_level) { `check_cpu`方法做了大量的检测和设置工作,下面就简单介绍一些:1)检查cpu标志,如果cpu是64位cpu,那么就设置[long mode](http://en.wikipedia.org/wiki/Long_mode), 2) 检查CPU的制造商,根据制造商的不同,设置不同的CPU选项。比如对于AMD出厂的cpu,如果不支持SSE+SSE2,那么就禁止这些选项。 -Memory detection +内存侦测 -------------------------------------------------------------------------------- -The next step is memory detection by the `detect_memory` function. `detect_memory` basically provides a map of available RAM to the cpu. It uses different programming interfaces for memory detection like `0xe820`, `0xe801` and `0x88`. We will see only the implementation of **0xE820** here. +接下来,内核调用`detect_memory`方法进行内存侦测,以得到系统当前内存的使用分布。It uses different programming interfaces for memory detection like `0xe820`, `0xe801` and `0x88`. We will see only the implementation of **0xE820** here. Let's look into the `detect_memory_e820` implementation from the [arch/x86/boot/memory.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/memory.c) source file. First of all, the `detect_memory_e820` function initializes the `biosregs` structure as we saw above and fills registers with special values for the `0xe820` call: From 3591042266b2e4a00165591ad13143d02f18a90d Mon Sep 17 00:00:00 2001 From: hailin cai Date: Tue, 23 Feb 2016 15:44:21 -0500 Subject: [PATCH 056/153] Update Booting/linux-bootstrap-2md.md --- Booting/linux-bootstrap-2md.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md index 27286e3..ff12c1b 100644 --- a/Booting/linux-bootstrap-2md.md +++ b/Booting/linux-bootstrap-2md.md @@ -363,7 +363,7 @@ ENDPROC(memset) 在堆栈初始化之后,内核代码通过调用[arch/x86/boot/cpu.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/cpu.c)提供的`validate_cpu`方法检查CPU类型以确定系统是否能够在当前的CPU上运行。 -`validate_cpu`调用了[`check_cpu`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/cpucheck.c#L102)方法得到当前系统的CPU级别,并且和系统预设的最低CPU级别进行比较。如果不满足条件,则不允许系统运行。function and passes cpu level and required cpu level to it and checks that the kernel launches on the right cpu level. +`validate_cpu`调用了[`check_cpu`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/cpucheck.c#L102)方法得到当前系统的CPU级别,并且和系统预设的最低CPU级别进行比较。如果不满足条件,则不允许系统运行。 ```c /*from cpu.c*/ @@ -381,7 +381,7 @@ if (cpu_level < req_level) { 内存侦测 -------------------------------------------------------------------------------- -接下来,内核调用`detect_memory`方法进行内存侦测,以得到系统当前内存的使用分布。It uses different programming interfaces for memory detection like `0xe820`, `0xe801` and `0x88`. We will see only the implementation of **0xE820** here. +接下来,内核调用`detect_memory`方法进行内存侦测,以得到系统当前内存的使用分布。该方法是用多种编程接口,包括`0xe820`,`0xe801`和`0x88`,进行内存侦测。在这里我们只介绍[arch/x86/boot/memory.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/memory.c)中提供的`detect_memory_e820`方法。 Let's look into the `detect_memory_e820` implementation from the [arch/x86/boot/memory.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/memory.c) source file. First of all, the `detect_memory_e820` function initializes the `biosregs` structure as we saw above and fills registers with special values for the `0xe820` call: From c9bc5a00657a4be21a3cabf4085569b0f186d1b6 Mon Sep 17 00:00:00 2001 From: "hailin.cai" Date: Tue, 23 Feb 2016 17:36:03 -0500 Subject: [PATCH 057/153] finish the chapter --- Booting/linux-bootstrap-2md.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 Booting/linux-bootstrap-2md.md diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md old mode 100644 new mode 100755 From 19a764379b5a5c54ad49eb2d9102a57b113a62e1 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Tue, 23 Feb 2016 17:37:30 -0500 Subject: [PATCH 058/153] Update SUMMARY.md --- SUMMARY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SUMMARY.md b/SUMMARY.md index fb9bc0e..c09f05e 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -3,7 +3,7 @@ * [Introduction](README.md) * [引导](Booting/README.md) * [从引导加载程序内核](Booting/linux-bootstrap-1.md) - * [在内核安装代码的第一步](Booting/linux-bootstrap-2md.md) + * [在内核安装代码的第一步](Booting/linux-bootstrap-2.md) * [视频模式初始化和转换到保护模式](Booting/linux-bootstrap-3.md) * [过渡到64位模式](Booting/linux-bootstrap-4.md) * [内核解压缩](Booting/linux-bootstrap-5.md) From 2fdfb01fdd8e9b7d7ed2c931c9d4b4567b7b3479 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Tue, 23 Feb 2016 17:38:43 -0500 Subject: [PATCH 059/153] Create Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Booting/linux-bootstrap-2.md diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md new file mode 100644 index 0000000..0fbf8c6 --- /dev/null +++ b/Booting/linux-bootstrap-2.md @@ -0,0 +1,2 @@ +# 在内核安装代码的第一步 + From 1bd140a4529e9fcd6e3babab0a8f291363091ed8 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Tue, 23 Feb 2016 17:39:14 -0500 Subject: [PATCH 060/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 579 ++++++++++++++++++++++++++++++++++- 1 file changed, 577 insertions(+), 2 deletions(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 0fbf8c6..e7e862c 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -1,2 +1,577 @@ -# 在内核安装代码的第一步 - +# 在内核安装代码的第一步 +#https://0xax.gitbooks.io/linux-insides/content/Booting/linux-bootstrap-2.html + +内核启动的第一步 +-------------------------------------------------------------------------------- + +在[上一节中](https://0xax.gitbooks.io/linux-insides/content/Booting/linux-bootstrap-1.html)我们开始接触到内核启动代码,并且分析了初始化部分,最后我们停在了对`main`函数(`main`函数是第一个用C写的函数)的调用(`main`函数位于[arch/x86/boot/main.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c)。 + +在这一节中我们将继续对内核启动过程的研究,我们将 +* 认识`保护模式` +* 如何从实模式进入保护模式 +* 堆和字符界面初始化 +* 内存检测,cpu验证,键盘初始化 +* 还有更多 + +现在让我们开始我们的旅程 + +保护模式 +-------------------------------------------------------------------------------- +在操作系统可以使用Intel 64位CPU的[长模式](http://en.wikipedia.org/wiki/Long_mode)之前,内核必须首先将CPU切换到保护模式运行。 + +什么是[保护模式](https://en.wikipedia.org/wiki/Protected_mode)?保护模式于1982年被引入到Intel CPU家族,并且从那之后,知道Intel 64出现,保护模式都是Intel CPU的主要运行模式。 + +淘汰[实模式](http://wiki.osdev.org/Real_Mode)的主要原因是因为在实模式下,系统能够访问的内存非常有限。如果你还记得我们在上一节说的,在实模式下,系统最多只能访问1M内存,而且在很多时候,实际能够访问的内存只有640K。 + +保护模式带来了很多的改变,不过只要的改变都集中在内存管理方法。在保护模式中,实模式的20位地址线被替换成32位地址线,因此系统可以访问多大4GB的地址空间。另外,在保护模式中引入了[内存分页](http://en.wikipedia.org/wiki/Paging)功能,在后面我们将介绍这个功能。 + +保护模式提供了2种完全不同的内存关机机制: + +* 段式内存管理 +* 内存分页 + +在这一节中,我们只介绍段式内存管理,内存分页我们将在后面的章节进行介绍。 + +在上一节中我们说过,在实模式下,一个物理地址是由2个部分组成的: + +* 内存段的基地址 +* 从基地址开始的偏移 + +通过这2个信息,我们可以通过下面的公式计算出对应的物理地址 + +``` +PhysicalAddress = Segment * 16 + Offset +``` + +在保护模式中,内存段的定义和实模式完全不同。在保护模式中,每个内存段不再是64K大小,段的大小和起始位置是通过一个叫做`段描述符`的数据结构进行描述的。所有内存段的段描述符存储在一个叫做`全局描述符表`(GDT)的内存结构中。 + +全局描述符表示一个内存数据结构,但是它在内存中的位置并不是固定的,它的地址保存在一个特殊寄存器`GDTR`中。在后面的章节中,我们将在Linux内核代码中看到全局描述符表的地址是如何被保存到`GDTR`中的。具体的汇编代码看起来是这样的: + +```assembly +lgdt gdt +``` + +`lgdt`汇编代码将把全局描述符表的基地址和大小保存到`GDTR`寄存器中。`GRTD`是一个48位的寄存器,这个寄存器中的保存了2部分的内容: + +* 全局描述符表的大小 (16位) +* 全局描述符表的基址 (32位) + +就像前面的段落说的,全局描述符表包含了所有内存段的`段描述符`。每个段描述符长度是64位,结构如下图描述: + +``` +31 24 19 16 7 0 +------------------------------------------------------------ +| | |B| |A| | | | |0|E|W|A| | +| BASE 31:24 |G|/|L|V| LIMIT |P|DPL|S| TYPE | BASE 23:16 | 4 +| | |D| |L| 19:16 | | | |1|C|R|A| | +------------------------------------------------------------ +| | | +| BASE 15:0 | LIMIT 15:0 | 0 +| | | +------------------------------------------------------------ +``` + +粗粗一看,上面的结构非常吓人,不过实际上这个结构是非常容易理解的。比如在上图中的LIMIT 15:0表示这个数据结构的0到15位保存的内存段的大小的0到15位。相似的LIMITE 19:16表示上述数据结构的16到19位保存的是内存段大小的16到19位。从这个分析中,我们可以看出每个内存段的大小是通过20位进行描述的。下面我们将对这个数据结构进行仔细分析: + +1. Limit[20位]被保存在上述内存结构的0-15和16-19位。根据上述内存结构中`G`位的设置,这20位内存定义的内存长度是不一样的。下面是一些具体的例子: + + * 如果`G`= 0, 并且Limit = 0, 那么表示段长度是1 byte + * 如果`G` = 1, 并且Limit = 0, 那么表示段长度是4K bytes + * 如果`G` = 0,并且Limit = 0xfffff,那么表示段长度是1M bytes + * 如果`G` = 1,并且Limit = 0xfffff,那么表示段长度是4G bytes + + 从上面的例子我们可以看出: + + * 如果G = 0, 那么内存段的长度是按照1 byte进行增长的 ( Limit每增加1,段长度增加1 byte ),最大的内存段长度将是1M bytes; + * 如果G = 1, 那么内存段的长度是按照4K bytes ( Limit每增加1,段长度增加4K bytes )进行增长的,最大的内存段长度将是4G bytes; + * 段长度的计算公司是 base_seg_length * ( LIMIT + 1)。 + +2. Base[32-bits]被保存在上述地址结构的0-15, 32-39以及56-63位。Base定义了段基址。 + +3. Type/Attribute (40-47 bits) 定义了内存段的类型以及支持的操作。 + * `S` 标记( 第44位 )定义了段的类型,`S` = 0说明这个内存段是一个系统段;`S` = 1说明这个内存段是一个代码段或者是数据段( 堆栈段是一种特使类型的数据段,堆栈段必须是可以进行读写的段 )。 + +在`S` = 1的情况下,上述内存结构的第43位决定了内存段是数据段还是代码段。如果43位 = 0,拿说明是一个数据段,否则就是一个代码段。 + +对于数据段和代码段,下面的表格给出了段类型定义 + +``` +| Type Field | Descriptor Type | Description +|-----------------------------|-----------------|------------------ +| Decimal | | +| 0 E W A | | +| 0 0 0 0 0 | Data | Read-Only +| 1 0 0 0 1 | Data | Read-Only, accessed +| 2 0 0 1 0 | Data | Read/Write +| 3 0 0 1 1 | Data | Read/Write, accessed +| 4 0 1 0 0 | Data | Read-Only, expand-down +| 5 0 1 0 1 | Data | Read-Only, expand-down, accessed +| 6 0 1 1 0 | Data | Read/Write, expand-down +| 7 0 1 1 1 | Data | Read/Write, expand-down, accessed +| C R A | | +| 8 1 0 0 0 | Code | Execute-Only +| 9 1 0 0 1 | Code | Execute-Only, accessed +| 10 1 0 1 0 | Code | Execute/Read +| 11 1 0 1 1 | Code | Execute/Read, accessed +| 12 1 1 0 0 | Code | Execute-Only, conforming +| 14 1 1 0 1 | Code | Execute-Only, conforming, accessed +| 13 1 1 1 0 | Code | Execute/Read, conforming +| 15 1 1 1 1 | Code | Execute/Read, conforming, accessed +``` + +从上面的表格我们可以看出,当第43位是`0`的时候,这个段描述符对应的是一个数据段,如果该位是`1`,那么表示这个段描述符对应的是一个代码段。对于数据段,第42,41,40位表示的是(*E*扩展,*W*可写,*A*可访问);对于代码段,第42,41,40位表示的是(*C*一致,*R*可读,*A*可访问)。 *A*ccessible) or CRA(*C*onforming *R*eadable *A*ccessible)。 + * 如果`E` = 0,数据段是向上扩展数据段,反之为向下扩展数据段。关于向上扩展和向下扩展数据段,可以参考下面的[链接](http://www.sudleyplace.com/dpmione/expanddown.html)。在一般情况下,应该是不会使用向下扩展数据段的。 + * 如果`W` = 1,说明这个数据段是可写的,否则不可写。所有数据段都是可读的。 + * A位表示该内存段是否已经被CPU访问。 + * 如果`C` = 1,说明这个代码段可以被第优先级的代码访问,比如可以被用户态代码访问。反之如果`C` = 0,说明只能同优先级的代码段可以访问。 + * 如果`R` = 1,说明该代码段可读。代码段是永远没有写权限的。 + +4. DPL[2-bits, bit 45 和 46] (描述符优先级) 定义了该段的优先级。具体数值是0-3。 + +5. P 标志(bit 47) - 说明该内存段是否已经存在于内存中。如果`P` = 0,那么在访问这个内存段的时候将报错。 + +6. AVL 标志(bit 52) - 这个位在Linux内核中没有被使用。 + +7. L 标志(bit 53) - 只对代码段有意义,如果`L` = 1,说明该代码段需要运行在64位模式下。 + +8. D/B flag(bit 54) - 根据段描述符描述的是一个可执行代码段、下扩数据段还是一个堆栈段,这个标志具有不同的功能。(对于32位代码和数据段,这个标志应该总是设置为1;对于16位代码和数据段,这个标志被设置为0。)。 + + * 可执行代码段。此时这个标志称为D标志并用于指出该段中的指令引用有效地址和操作数的默认长度。如果该标志置位,则默认值是32位地址和32位或8位的操作数;如果该标志为0,则默认值是16位地址和16位或8位的操作数。指令前缀0x66可以用来选择非默认值的操作数大小;前缀0x67可用来选择非默认值的地址大小。 + * 栈段(由SS寄存器指向的数据段)。此时该标志称为B(Big)标志,用于指明隐含堆栈操作(如PUSH、POP或CALL)时的栈指针大小。如果该标志置位,则使用32位栈指针并存放在ESP寄存器中;如果该标志为0,则使用16位栈指针并存放在SP寄存器中。如果堆栈段被设置成一个下扩数据段,这个B标志也同时指定了堆栈段的上界限。 + * 下扩数据段。此时该标志称为B标志,用于指明堆栈段的上界限。如果设置了该标志,则堆栈段的上界限是0xFFFFFFFF(4GB);如果没有设置该标志,则堆栈段的上界限是0xFFFF(64KB)。 + +在保护模式下,段寄存器保存的不再是一个内存段的基地址,而是一个称为`段选择子`的结构。每个段描述符都对应一个`段选择子`。`段选择子`是一个16位的数据结构,下图显示了这个数据结构的内容: + +``` +----------------------------- +| Index | TI | RPL | +----------------------------- +``` + +其中, +* **Index** 表示在GDT中,对应段描述符的索引号。 +* **TI** 表示要在GDT还是LDT中查找对应的段描述符 +* **RPL** 表示请求者优先级。这个优先级将和段描述符中的优先级协同工作,共同确定访问是否合法。 + +在保护模式下,每个段寄存器实际上包含下面2部分内容: +* 可见部分 - 段选择子 +* 隐藏部分 - 段描述符 + +在保护模式中,cpu是通过下面的步骤来找到一个具体的物理地址的: + +* 代码必须将相应的`段选择子`装入某个段寄存器 +* CPU根据`段选择子`从GDT中找到一个匹配的段描述符,然后将段描述符放入段寄存器的隐藏部分 +* 在没有使用向下扩展段的时候,那么内存段的基地址就是`段描述符中的基地址`,段描述符的`limit + 1`就是内存段的长度。如果你知道一个内存地址的`偏移`,那么在没有开启分页机制的情况下,这个内存的物理地址就是`基地址+偏移` + +![linear address](http://oi62.tinypic.com/2yo369v.jpg) + +当代码要从实模式进入保护模式的时候,需要执行下面的操作: is: + +* 禁止中断发生 +* 使用命令`lgdt`将GDT表装入内存 +* 设置CR0寄存器的PE位为1,是CPU进入保护模式 +* 跳转开始执行保护模式代码 + +在后面的章节中,我们将看到Linux 内核中完整的转换代码。不过在系统进入保护模式之前,还有很多准备工作需要完成。 + +让我们代开C文件 [arch/x86/boot/main.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c)。这个文件包含了很多的函数,这些函数分别会执行键盘初始化,内存堆初始化等等操作...,下面让我们来具体看一些重要的函数。 + +将启动参数拷贝到"zeropage" +-------------------------------------------------------------------------------- + +让我们从`main`函数开始看起,这个函数中,首先调用了[`copy_boot_params(void)`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L30)。 + +这个函数将内存设置信息拷贝到`boot_params`结构的相应字段。大家可以在[arch/x86/include/uapi/asm/bootparam.h](https://github.com/torvalds/linux/blob/master/arch/x86/include/uapi/asm/bootparam.h#L113)找到`boot_params`结构的定义。 + +1. 将[header.S](https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S#L281)中定义的`hdr`结构中的内容拷贝到`boot_params`结构的字段`struct setup_header hdr`中。 + +2. 如果内核是通过老的命令行协议运行起来的,那么就更新内核的命令行指针。 + +这里需要注意的是拷贝`hdr`数据结构的`memcpy`函数不是C语言中的函数,而是定义在 [copy.S](https://github.com/torvalds/linux/blob/master/arch/x86/boot/copy.S)。让我们来具体分析一下这段代码: + +```assembly +GLOBAL(memcpy) + pushw %si ;push si to stack + pushw %di ;push di to stack + movw %ax, %di ;move &boot_param.hdr to di + movw %dx, %si ;move &hdr to si + pushw %cx ;push cx to stack ( sizeof(hdr) ) + shrw $2, %cx + rep; movsl ;copy based on 4 bytes + popw %cx ;pop cx + andw $3, %cx ;cx = cx % 4 + rep; movsb ;copy based on one byte + popw %di + popw %si + retl +ENDPROC(memcpy) +``` + +在`copy.S`文件中,你可以看到所有的方法都开始于`GLOBAL`宏定义,而结束于`ENDPROC`宏定义。 + +你可以在 [arch/x86/include/asm/linkage.h](https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/linkage.h)中找到`GLOBAL`宏定义。这个宏给代码段分配了一个名字标签,并且让这个名字全局可用。 + +```assembly +#define GLOBAL(name) \ + .globl name; \ + name: +``` + +你可以在[include/linux/linkage.h](https://github.com/torvalds/linux/blob/master/include/linux/linkage.h)中找到`ENDPROC`宏的定义。 这个宏通过`END(name)`代码标识了汇编函数的结束,同时将函数名输出,从而静态分析工具可以找到这个函数。 + +```assembly +#define ENDPROC(name) \ + .type name, @function ASM_NL \ + END(name) +``` + +`memcpy`的实现代码是很容易理解的。首先,代码将`si`和`di`寄存器的值压入堆栈进行保存,这么做的原因是因为后续的代码将修改`si`和`di`寄存器的值。`memcpy`函数(也包括其他定义在copy.s中的其他函数)使用了`fastcall`调用规则,意味着所有的函数调用参数是通过`ax`, `dx`, `cx`寄存器传入的,而不是传统的通过堆栈传入。因此在使用下面的代码调用`memcpy`函数的时候 + +```c +memcpy(&boot_params.hdr, &hdr, sizeof hdr); +``` + +函数的参数是这样传递的 + +* `ax` 寄存器指向`boot_param.hdr`的内存地址 +* `dx` 寄存器指向`hdr`的内存地址 +* `cx` 寄存器包含`hdr`结构的大小 + +`memcpy`函数在将`si`和`di`寄存器压栈之后,将`boot_param.hdr`的地址放入`di`寄存器,将`hdr`的地址放入`si`寄存器,并且将`hdr`数据结构的大小压栈。 接下来代码首先以4个字节为单位,将`si`寄存器指向的内存内容拷贝`di`寄存器指向的内存。当剩下的字节数不足4字节的时候,代码将原始的`hdr`数据结构大小出栈放入`cx`,然后对`cx`的值对4求模,接下来就是根据`cx`的值,以字节为单位将`si`寄存器指向的内存内容拷贝到`di`寄存器指向的内存。当拷贝操作完成之后,将保留的`si`以及`di`寄存器值出栈,函数返回。 + +控制台初始化 +-------------------------------------------------------------------------------- + +在`hdr`结构体被拷贝到`boot_params.hdr`成员之后,系统接下来将进行控制台的初始化。控制台初始化时通过调用[arch/x86/boot/early_serial_console.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/early_serial_console.c)中定义的`console_init`函数实现的。 + +这个函数首先查看命令行参数是否包含`earlyprintk`选项。如果命令行参数包含该选项,那么函数将分析这个选项的内容。得到控制台将使用的串口信息,然后进行串口的初始化。以下是`earlyprintk`选项可能的取值: + +* serial,0x3f8,115200 +* serial,ttyS0,115200 +* ttyS0,115200 + +当串口初始化成功之后,我们将看到如下的输出如果命令行参数包含`debug`选项。 + +```C +if (cmdline_find_option_bool("debug")) + puts("early console in setup code\n"); +``` + +`puts`函数定义在[tty.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/tty.c)。这个函数只是简单的调用`putchar`函数将输入字符串中的内容按字节输出。下面让我们来看看`putchar`函数的实现: + +```C +void __attribute__((section(".inittext"))) putchar(int ch) +{ + if (ch == '\n') + putchar('\r'); + + bios_putchar(ch); + + if (early_serial_base != 0) + serial_putchar(ch); +} +``` + +`__attribute__((section(".inittext")))` 说明这段代码将被放入`.inittext`代码段。关于`.inittext`代码段的定义你可以在 [setup.ld](https://github.com/torvalds/linux/blob/master/arch/x86/boot/setup.ld#L19)中找到。 + +首先如果需要输出的字符是`\n`,那么`putchar`函数将调用自己首先输出一个字符`\r`。接下来,就调用`bios_putchar`函数将字符输出到显示器(使用bios int10中断): + +```C +static void __attribute__((section(".inittext"))) bios_putchar(int ch) +{ + struct biosregs ireg; + + initregs(&ireg); + ireg.bx = 0x0007; + ireg.cx = 0x0001; + ireg.ah = 0x0e; + ireg.al = ch; + intcall(0x10, &ireg, NULL); +} +``` + +在上面的代码中`initreg`函数接受一个`biosregs`结构的地址作为输入参数,该函数首先调用`memset`函数将`biosregs`结构体所有成员清0。 + +```C + memset(reg, 0, sizeof *reg); + reg->eflags |= X86_EFLAGS_CF; + reg->ds = ds(); + reg->es = ds(); + reg->fs = fs(); + reg->gs = gs(); +``` + +下面让我们来看看[memset](https://github.com/torvalds/linux/blob/master/arch/x86/boot/copy.S#L36)函数的实现 : + +```assembly +GLOBAL(memset) + pushw %di + movw %ax, %di + movzbl %dl, %eax + imull $0x01010101,%eax + pushw %cx + shrw $2, %cx + rep; stosl + popw %cx + andw $3, %cx + rep; stosb + popw %di + retl +ENDPROC(memset) +``` + +首先你会发现,`memset`函数和`memcpy`函数一样使用了`fastcall`调用规则,因此函数的参数是通过`ax`,`dx`以及`cx`寄存器传入函数内部的。 + +就像memcpy函数一样,`memset`函数一开始将`di`寄存器入栈,然后将`biosregs`结构的地址从`ax`寄存器拷贝到`di`寄存器。记下来,使用`movzbl`指令将`dl`寄存器的内容拷贝到`ax`寄存器的滴字节,到这里`ax`寄存器就包含了需要拷贝到`di`寄存器所指向的内存的值。 + +接下来的`imull`指令将`eax`寄存器的值乘上`0x01010101`。这么做的原因是代码每次将尝试拷贝4个字节内存的内容。下面让我们来看一个具体的例子,假设我们需要将`0x7`这个数值放到内存中,在执行`imull`指令之前,`eax`寄存器的值是`0x7`,在`imull`指令被执行之后,`eax`寄存器的内容变成了`0x07070707`(4个字节的`0x7`)。在`imull`指令之后,代码使用`rep; stosl`指令将`eax`寄存器的内容拷贝到`es:di`指向的内存。 + +在`bisoregs`结构体被`initregs`函数正确填充之后,`bios_putchar` 调用中断 [0x10](http://www.ctyme.com/intr/rb-0106.htm) 在显示器上输出一个字符。接下来`putchar`函数检查是否初始化了串口,如果串口被初始化了,那么将调用[serial_putchar](https://github.com/torvalds/linux/blob/master/arch/x86/boot/tty.c#L30)将字符输出到串口。 + +堆初始化 +-------------------------------------------------------------------------------- + +当堆栈和bss段在[header.S](https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S)中被初始化之后 (细节请参考上一篇[part](linux-bootstrap-1.md)), 内核需要初始化全局堆,全局堆的初始化是通过 [`init_heap`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L116) 函数实现的。 + +代码首先检查内核设置头中的[`loadflags`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S#L321)是否设置了 [`CAN_USE_HEAP`](https://github.com/torvalds/linux/blob/master/arch/x86/include/uapi/asm/bootparam.h#L21)标志。 如果该标记被设置了,那么代码将计算堆栈的结束地址:: + +```C + char *stack_end; + + //%P1 is (-STACK_SIZE) + if (boot_params.hdr.loadflags & CAN_USE_HEAP) { + asm("leal %P1(%%esp),%0" + : "=r" (stack_end) : "i" (-STACK_SIZE)); +``` + +换言之`stack_end = esp - STACK_SIZE`. + +在计算了堆栈结束地址之后,代码计算了堆的结束地址: + +```c + + //heap_end = heap_end_ptr + 512 + heap_end = (char *)((size_t)boot_params.hdr.heap_end_ptr + 0x200); +``` + +接下来代码判断`heap_end`是否大于`stack_end`,如果条件成立,将`stack_end`设置成`heap_end`(这么做是因为在大部分系统中全局堆和堆栈是相邻的)。 + +到这里位置,全局堆就被正确初始化了。在全局堆被初始化之后,我们就可以使用`GET_HEAP`方法。至于这个函数的实现和使用,我们将在后续的章节中看到。 + +检查CPU类型 +-------------------------------------------------------------------------------- + +在堆栈初始化之后,内核代码通过调用[arch/x86/boot/cpu.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/cpu.c)提供的`validate_cpu`方法检查CPU类型以确定系统是否能够在当前的CPU上运行。 + +`validate_cpu`调用了[`check_cpu`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/cpucheck.c#L102)方法得到当前系统的CPU级别,并且和系统预设的最低CPU级别进行比较。如果不满足条件,则不允许系统运行。 + +```c +/*from cpu.c*/ +check_cpu(&cpu_level, &req_level, &err_flags); +/*after check_cpu call, req_level = req_level defined in cpucheck.c*/ +if (cpu_level < req_level) { + printf("This kernel requires an %s CPU, ", cpu_name(req_level)); + printf("but only detected an %s CPU.\n", cpu_name(cpu_level)); + return -1; +} +``` + +`check_cpu`方法做了大量的检测和设置工作,下面就简单介绍一些:1)检查cpu标志,如果cpu是64位cpu,那么就设置[long mode](http://en.wikipedia.org/wiki/Long_mode), 2) 检查CPU的制造商,根据制造商的不同,设置不同的CPU选项。比如对于AMD出厂的cpu,如果不支持SSE+SSE2,那么就禁止这些选项。 + +内存侦测 +-------------------------------------------------------------------------------- + +接下来,内核调用`detect_memory`方法进行内存侦测,以得到系统当前内存的使用分布。该方法是用多种编程接口,包括`0xe820`,`0xe801`和`0x88`,进行内存侦测。在这里我们只介绍[arch/x86/boot/memory.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/memory.c)中提供的`detect_memory_e820`方法。 + +该方法首先调用`initregs`方法初始化`biosregs`数据结构,然后向该数据结构填入`0xe820`编程接口所要求的参数: + +```assembly + initregs(&ireg); + ireg.ax = 0xe820; + ireg.cx = sizeof buf; + ireg.edx = SMAP; + ireg.di = (size_t)&buf; +``` + +* `ax` 固定为0xe820 +* `cx` 包含数据缓冲区的大小,该缓冲区将包含系统内存的信息数据 +* `edx` 必须是SMAP这个魔术数字,就是0x534d4150 +* `es:di` 包含数据缓冲区的地址 +* `ebx` 必须为0. + +接下来就是通过一个循环来收集内存信息了。每个循环都开始于一个`0x15`中断调用,这个中断调用返回地址分配表中的一项,接着程序将返回的`ebx`设置`biosregs`数据结构中进行下一次的`0x15`中断调用。那么循环什么时候结束呢?直到`0x15`调用返回的eflags包含标志`X86_EFLAGS_CF`: + +```C + intcall(0x15, &ireg, &oreg); + ireg.ebx = oreg.ebx; +``` + +在循环结束之后,整个内存非陪标中的数据将被写入到`e820entry`数组中,这个数组的每个元素包含下面3个信息: + +* 内存段的起始地址 +* 内存段的大小 +* 内存段的类型(类型可以是reserved, usable等等)。 + +你可以在`dmesg`输出中看到这个数组的内容: + +``` +[ 0.000000] e820: BIOS-provided physical RAM map: +[ 0.000000] BIOS-e820: [mem 0x0000000000000000-0x000000000009fbff] usable +[ 0.000000] BIOS-e820: [mem 0x000000000009fc00-0x000000000009ffff] reserved +[ 0.000000] BIOS-e820: [mem 0x00000000000f0000-0x00000000000fffff] reserved +[ 0.000000] BIOS-e820: [mem 0x0000000000100000-0x000000003ffdffff] usable +[ 0.000000] BIOS-e820: [mem 0x000000003ffe0000-0x000000003fffffff] reserved +[ 0.000000] BIOS-e820: [mem 0x00000000fffc0000-0x00000000ffffffff] reserved +``` + +键盘初始化 +-------------------------------------------------------------------------------- + +接下来内核调用[`keyboard_init()`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L65) 方法进行键盘初始化操作。 首先,方法调用`initregs`初始化寄存器结构,然后调用`0x16`中断[0x16](http://www.ctyme.com/intr/rb-1756.htm) 来获取键盘状态。 + +```c + initregs(&ireg); + ireg.ah = 0x02; /* Get keyboard status */ + intcall(0x16, &ireg, &oreg); + boot_params.kbd_status = oreg.al; +``` + +在获取了键盘状态之后,代码再次调用`0x16`中断[0x16](http://www.ctyme.com/intr/rb-1757.htm) 来设置键盘的按键检测频率。 + +```c + ireg.ax = 0x0305; /* Set keyboard repeat rate */ + intcall(0x16, &ireg, NULL); +``` + +系统参数查询 +-------------------------------------------------------------------------------- + +接下来内核将进行一系列的参数查询。我们在这里将不深入介绍所有这些查询,我们将在后续章节中在进行详细介绍。在这里我们将简单介绍一下这些函数: + +[query_mca](https://github.com/torvalds/linux/blob/master/arch/x86/boot/mca.c#L18) 方法调用`0x15`中断[0x15](http://www.ctyme.com/intr/rb-1594.htm)来获取机器的型号信息,BIOS版本以及其他一些硬件相关的属性: + +```c +int query_mca(void) +{ + struct biosregs ireg, oreg; + u16 len; + + initregs(&ireg); + ireg.ah = 0xc0; + intcall(0x15, &ireg, &oreg); + + if (oreg.eflags & X86_EFLAGS_CF) + return -1; /* No MCA present */ + + set_fs(oreg.es); + len = rdfs16(oreg.bx); + + if (len > sizeof(boot_params.sys_desc_table)) + len = sizeof(boot_params.sys_desc_table); + + copy_from_fs(&boot_params.sys_desc_table, oreg.bx, len); + return 0; +} +``` + +这个方法设置`ah`寄存器的值为`0xc0`,然后调用`0x15` BIOS中断。中断返回之后代码检查 [carry flag](http://en.wikipedia.org/wiki/Carry_flag)。如果它被置位,说明BIOS不支持(**MCA**)[https://en.wikipedia.org/wiki/Micro_Channel_architecture]。如果CF被设置成0,那么`ES:BX`指向系统信息表。这个表的内容如下所示: + +``` +Offset Size Description + 00h WORD number of bytes following + 02h BYTE model (see #00515) + 03h BYTE submodel (see #00515) + 04h BYTE BIOS revision: 0 for first release, 1 for 2nd, etc. + 05h BYTE feature byte 1 (see #00510) + 06h BYTE feature byte 2 (see #00511) + 07h BYTE feature byte 3 (see #00512) + 08h BYTE feature byte 4 (see #00513) + 09h BYTE feature byte 5 (see #00514) +---AWARD BIOS--- + 0Ah N BYTEs AWARD copyright notice +---Phoenix BIOS--- + 0Ah BYTE ??? (00h) + 0Bh BYTE major version + 0Ch BYTE minor version (BCD) + 0Dh 4 BYTEs ASCIZ string "PTL" (Phoenix Technologies Ltd) +---Quadram Quad386--- + 0Ah 17 BYTEs ASCII signature string "Quadram Quad386XT" +---Toshiba (Satellite Pro 435CDS at least)--- + 0Ah 7 BYTEs signature "TOSHIBA" + 11h BYTE ??? (8h) + 12h BYTE ??? (E7h) product ID??? (guess) + 13h 3 BYTEs "JPN" + ``` + +接下来代码调用`set_fs`方法,将`es`寄存器的值写入`fs`寄存器: + +```c +static inline void set_fs(u16 seg) +{ + asm volatile("movw %0,%%fs" : : "rm" (seg)); +} +``` + +在[boot.h](https://github.com/torvalds/linux/blob/master/arch/x86/boot/boot.h) 存在很多类似于`set_fs`的方法, 比如 `set_gs`。 + +在`query_mca`的最后,代码将`es:bx`只想的内存地址拷贝到`boot_params.sys_desc_table`。 + +接下来,调用`query_ist`方法获取[Intel SpeedStep](http://en.wikipedia.org/wiki/SpeedStep)信息。这个方法首先检查CPU类型,然后调用`0x15`中断获得这个信息并放入`boot_params`中。 + +接下来,内核会调用[query_apm_bios](https://github.com/torvalds/linux/blob/master/arch/x86/boot/apm.c#L21) 方法从BIOS获得 [高级电源管理](http://en.wikipedia.org/wiki/Advanced_Power_Management) 信息。`query_apm_bios`也是调用`0x15`中断,只不过将`ah`设置成0x53以获得APM设置。中断调用返回之后,代码将检查`bx`和`cx`的值,如果`bx`不是`0x504d` ( PM 标记 ),或者`cx`不是0x02 (0x02,表示支持保护模式),那么代码直接返回错误。否则,将进行下面的步骤。 + +接下来,代码使用`ax = 0x5304`来调用`0x15`中断,以断开`APM`接口;然后使用`ax = 0x5303`调用`0x15`中断,以连接到保护模式接口;最后使用`ax = 0x5300`调用`0x15`中断再次获取APM设置,然后将信息写入`boot_params.apm_bios_info`。 + +只有在`CONFIG_APM`或者`CONFIG_APM_MODULE`被设置的情况下,`query_apm_bios`方法才会被调用: + +```C +#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE) + query_apm_bios(); +#endif +``` + +最后是[`query_edd`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/edd.c#L122) 方法调用, 这个方法从BIOS中查询`Enhanced Disk Drive`信息。下面让我们看看`query_edd`方法的实现。 + +首先,代码检查内核命令行参数是否设置了[edd](https://github.com/torvalds/linux/blob/master/Documentation/kernel-parameters.txt#L1023) 选项,如果edd选项设置成`off`,`query_edd`不做任何操作,直接返回。 + +如果EDD被激活了,`query_edd`遍历所有BIOS支持的硬盘,并获取相应硬盘的EDD信息: + +```C +for (devno = 0x80; devno < 0x80+EDD_MBR_SIG_MAX; devno++) { + if (!get_edd_info(devno, &ei) && boot_params.eddbuf_entries < EDDMAXNR) { + memcpy(edp, &ei, sizeof ei); + edp++; + boot_params.eddbuf_entries++; + } + ... + ... + ... +``` + +在代码中 `0x80`是第一块硬盘,`EDD_MBR_SIG_MAX`是一个宏,值为16。代码把获得的信息放入数组[edd_info](https://github.com/torvalds/linux/blob/master/include/uapi/linux/edd.h#L172)中。`get_edd_info`方法通过调用`0x13`中断调用(设置`ah = 0x41` ) 来检查EDD是否被硬盘支持。如果EDD被支持,代码将再次调用`0x13`中断,在这次调用中`ah = 0x48`,并且`si`只想缓冲区地址。EDD信息将被保存到`si`指向的缓冲区。 + +结束语 +-------------------------------------------------------------------------------- + +本章到此就结束了,在下一章我们将讲解显示模式设置,以及在进入保护模式之前的其他准备工作,最后我们将成功进入保护模式。 + +如果你有任何的问题或者建议,你可以留言,也可以直接发消息给我[twitter](https://twitter.com/0xAX). + +**英文不是我的母语。如果你发现我的英文描述有任何问题,请提交一个PR到[linux-insides](https://github.com/0xAX/linux-internals).** + +相关链接 +-------------------------------------------------------------------------------- + +* [Protected mode](http://en.wikipedia.org/wiki/Protected_mode) +* [Protected mode](http://wiki.osdev.org/Protected_Mode) +* [Long mode](http://en.wikipedia.org/wiki/Long_mode) +* [Nice explanation of CPU Modes with code](http://www.codeproject.com/Articles/45788/The-Real-Protected-Long-mode-assembly-tutorial-for) +* [How to Use Expand Down Segments on Intel 386 and Later CPUs](http://www.sudleyplace.com/dpmione/expanddown.html) +* [earlyprintk documentation](http://lxr.free-electrons.com/source/Documentation/x86/earlyprintk.txt) +* [Kernel Parameters](https://github.com/torvalds/linux/blob/master/Documentation/kernel-parameters.txt) +* [Serial console](https://github.com/torvalds/linux/blob/master/Documentation/serial-console.txt) +* [Intel SpeedStep](http://en.wikipedia.org/wiki/SpeedStep) +* [APM](https://en.wikipedia.org/wiki/Advanced_Power_Management) +* [EDD specification](http://www.t13.org/documents/UploadedDocuments/docs2004/d1572r3-EDD3.pdf) +* [TLDP documentation for Linux Boot Process](http://www.tldp.org/HOWTO/Linux-i386-Boot-Code-HOWTO/setup.html) (old) +* [Previous Part](linux-bootstrap-1.md) \ No newline at end of file From 2c346eff668fc2631f2ba668963744052555c97e Mon Sep 17 00:00:00 2001 From: "hailin.cai" Date: Tue, 23 Feb 2016 17:39:54 -0500 Subject: [PATCH 061/153] change the chapter filename --- Booting/linux-bootstrap-2.md | 0 Booting/linux-bootstrap-2md.md | 574 --------------------------------- 2 files changed, 574 deletions(-) mode change 100644 => 100755 Booting/linux-bootstrap-2.md delete mode 100755 Booting/linux-bootstrap-2md.md diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md old mode 100644 new mode 100755 diff --git a/Booting/linux-bootstrap-2md.md b/Booting/linux-bootstrap-2md.md deleted file mode 100755 index ff12c1b..0000000 --- a/Booting/linux-bootstrap-2md.md +++ /dev/null @@ -1,574 +0,0 @@ -# 在内核安装代码的第一步 -#https://0xax.gitbooks.io/linux-insides/content/Booting/linux-bootstrap-2.html - -内核启动的第一步 --------------------------------------------------------------------------------- - -在[上一节中](https://0xax.gitbooks.io/linux-insides/content/Booting/linux-bootstrap-1.html)我们开始接触到内核启动代码,并且分析了初始化部分,最后我们停在了对`main`函数(`main`函数是第一个用C写的函数)的调用(`main`函数位于[arch/x86/boot/main.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c)。 - -在这一节中我们将继续对内核启动过程的研究,我们将 -* 认识`保护模式` -* 如何从实模式进入保护模式 -* 堆和字符界面初始化 -* 内存检测,cpu验证,键盘初始化 -* 还有更多 - -现在让我们开始我们的旅程 - -保护模式 --------------------------------------------------------------------------------- -在操作系统可以使用Intel 64位CPU的[长模式](http://en.wikipedia.org/wiki/Long_mode)之前,内核必须首先将CPU切换到保护模式运行。 - -什么是[保护模式](https://en.wikipedia.org/wiki/Protected_mode)?保护模式于1982年被引入到Intel CPU家族,并且从那之后,知道Intel 64出现,保护模式都是Intel CPU的主要运行模式。 - -淘汰[实模式](http://wiki.osdev.org/Real_Mode)的主要原因是因为在实模式下,系统能够访问的内存非常有限。如果你还记得我们在上一节说的,在实模式下,系统最多只能访问1M内存,而且在很多时候,实际能够访问的内存只有640K。 - -保护模式带来了很多的改变,不过只要的改变都集中在内存管理方法。在保护模式中,实模式的20位地址线被替换成32位地址线,因此系统可以访问多大4GB的地址空间。另外,在保护模式中引入了[内存分页](http://en.wikipedia.org/wiki/Paging)功能,在后面我们将介绍这个功能。 - -保护模式提供了2种完全不同的内存关机机制: - -* 段式内存管理 -* 内存分页 - -在这一节中,我们只介绍段式内存管理,内存分页我们将在后面的章节进行介绍。 - -在上一节中我们说过,在实模式下,一个物理地址是由2个部分组成的: - -* 内存段的基地址 -* 从基地址开始的偏移 - -通过这2个信息,我们可以通过下面的公式计算出对应的物理地址 - -``` -PhysicalAddress = Segment * 16 + Offset -``` - -在保护模式中,内存段的定义和实模式完全不同。在保护模式中,每个内存段不再是64K大小,段的大小和起始位置是通过一个叫做`段描述符`的数据结构进行描述的。所有内存段的段描述符存储在一个叫做`全局描述符表`(GDT)的内存结构中。 - -全局描述符表示一个内存数据结构,但是它在内存中的位置并不是固定的,它的地址保存在一个特殊寄存器`GDTR`中。在后面的章节中,我们将在Linux内核代码中看到全局描述符表的地址是如何被保存到`GDTR`中的。具体的汇编代码看起来是这样的: - -```assembly -lgdt gdt -``` - -`lgdt`汇编代码将把全局描述符表的基地址和大小保存到`GDTR`寄存器中。`GRTD`是一个48位的寄存器,这个寄存器中的保存了2部分的内容: - -* 全局描述符表的大小 (16位) -* 全局描述符表的基址 (32位) - -就像前面的段落说的,全局描述符表包含了所有内存段的`段描述符`。每个段描述符长度是64位,结构如下图描述: - -``` -31 24 19 16 7 0 ------------------------------------------------------------- -| | |B| |A| | | | |0|E|W|A| | -| BASE 31:24 |G|/|L|V| LIMIT |P|DPL|S| TYPE | BASE 23:16 | 4 -| | |D| |L| 19:16 | | | |1|C|R|A| | ------------------------------------------------------------- -| | | -| BASE 15:0 | LIMIT 15:0 | 0 -| | | ------------------------------------------------------------- -``` - -粗粗一看,上面的结构非常吓人,不过实际上这个结构是非常容易理解的。比如在上图中的LIMIT 15:0表示这个数据结构的0到15位保存的内存段的大小的0到15位。相似的LIMITE 19:16表示上述数据结构的16到19位保存的是内存段大小的16到19位。从这个分析中,我们可以看出每个内存段的大小是通过20位进行描述的。下面我们将对这个数据结构进行仔细分析: - -1. Limit[20位]被保存在上述内存结构的0-15和16-19位。根据上述内存结构中`G`位的设置,这20位内存定义的内存长度是不一样的。下面是一些具体的例子: - - * 如果`G`= 0, 并且Limit = 0, 那么表示段长度是1 byte - * 如果`G` = 1, 并且Limit = 0, 那么表示段长度是4K bytes - * 如果`G` = 0,并且Limit = 0xfffff,那么表示段长度是1M bytes - * 如果`G` = 1,并且Limit = 0xfffff,那么表示段长度是4G bytes - - 从上面的例子我们可以看出: - - * 如果G = 0, 那么内存段的长度是按照1 byte进行增长的 ( Limit每增加1,段长度增加1 byte ),最大的内存段长度将是1M bytes; - * 如果G = 1, 那么内存段的长度是按照4K bytes ( Limit每增加1,段长度增加4K bytes )进行增长的,最大的内存段长度将是4G bytes; - * 段长度的计算公司是 base_seg_length * ( LIMIT + 1)。 - -2. Base[32-bits]被保存在上述地址结构的0-15, 32-39以及56-63位。Base定义了段基址。 - -3. Type/Attribute (40-47 bits) 定义了内存段的类型以及支持的操作。 - * `S` 标记( 第44位 )定义了段的类型,`S` = 0说明这个内存段是一个系统段;`S` = 1说明这个内存段是一个代码段或者是数据段( 堆栈段是一种特使类型的数据段,堆栈段必须是可以进行读写的段 )。 - -在`S` = 1的情况下,上述内存结构的第43位决定了内存段是数据段还是代码段。如果43位 = 0,拿说明是一个数据段,否则就是一个代码段。 - -对于数据段和代码段,下面的表格给出了段类型定义 - -``` -| Type Field | Descriptor Type | Description -|-----------------------------|-----------------|------------------ -| Decimal | | -| 0 E W A | | -| 0 0 0 0 0 | Data | Read-Only -| 1 0 0 0 1 | Data | Read-Only, accessed -| 2 0 0 1 0 | Data | Read/Write -| 3 0 0 1 1 | Data | Read/Write, accessed -| 4 0 1 0 0 | Data | Read-Only, expand-down -| 5 0 1 0 1 | Data | Read-Only, expand-down, accessed -| 6 0 1 1 0 | Data | Read/Write, expand-down -| 7 0 1 1 1 | Data | Read/Write, expand-down, accessed -| C R A | | -| 8 1 0 0 0 | Code | Execute-Only -| 9 1 0 0 1 | Code | Execute-Only, accessed -| 10 1 0 1 0 | Code | Execute/Read -| 11 1 0 1 1 | Code | Execute/Read, accessed -| 12 1 1 0 0 | Code | Execute-Only, conforming -| 14 1 1 0 1 | Code | Execute-Only, conforming, accessed -| 13 1 1 1 0 | Code | Execute/Read, conforming -| 15 1 1 1 1 | Code | Execute/Read, conforming, accessed -``` - -从上面的表格我们可以看出,当第43位是`0`的时候,这个段描述符对应的是一个数据段,如果该位是`1`,那么表示这个段描述符对应的是一个代码段。对于数据段,第42,41,40位表示的是(*E*扩展,*W*可写,*A*可访问);对于代码段,第42,41,40位表示的是(*C*一致,*R*可读,*A*可访问)。 *A*ccessible) or CRA(*C*onforming *R*eadable *A*ccessible)。 - * 如果`E` = 0,数据段是向上扩展数据段,反之为向下扩展数据段。关于向上扩展和向下扩展数据段,可以参考下面的[链接](http://www.sudleyplace.com/dpmione/expanddown.html)。在一般情况下,应该是不会使用向下扩展数据段的。 - * 如果`W` = 1,说明这个数据段是可写的,否则不可写。所有数据段都是可读的。 - * A位表示该内存段是否已经被CPU访问。 - * 如果`C` = 1,说明这个代码段可以被第优先级的代码访问,比如可以被用户态代码访问。反之如果`C` = 0,说明只能同优先级的代码段可以访问。 - * 如果`R` = 1,说明该代码段可读。代码段是永远没有写权限的。 - -4. DPL[2-bits, bit 45 和 46] (描述符优先级) 定义了该段的优先级。具体数值是0-3。 - -5. P 标志(bit 47) - 说明该内存段是否已经存在于内存中。如果`P` = 0,那么在访问这个内存段的时候将报错。 - -6. AVL 标志(bit 52) - 这个位在Linux内核中没有被使用。 - -7. L 标志(bit 53) - 只对代码段有意义,如果`L` = 1,说明该代码段需要运行在64位模式下。 - -8. D/B flag(bit 54) - 根据段描述符描述的是一个可执行代码段、下扩数据段还是一个堆栈段,这个标志具有不同的功能。(对于32位代码和数据段,这个标志应该总是设置为1;对于16位代码和数据段,这个标志被设置为0。)。 - - * 可执行代码段。此时这个标志称为D标志并用于指出该段中的指令引用有效地址和操作数的默认长度。如果该标志置位,则默认值是32位地址和32位或8位的操作数;如果该标志为0,则默认值是16位地址和16位或8位的操作数。指令前缀0x66可以用来选择非默认值的操作数大小;前缀0x67可用来选择非默认值的地址大小。 - * 栈段(由SS寄存器指向的数据段)。此时该标志称为B(Big)标志,用于指明隐含堆栈操作(如PUSH、POP或CALL)时的栈指针大小。如果该标志置位,则使用32位栈指针并存放在ESP寄存器中;如果该标志为0,则使用16位栈指针并存放在SP寄存器中。如果堆栈段被设置成一个下扩数据段,这个B标志也同时指定了堆栈段的上界限。 - * 下扩数据段。此时该标志称为B标志,用于指明堆栈段的上界限。如果设置了该标志,则堆栈段的上界限是0xFFFFFFFF(4GB);如果没有设置该标志,则堆栈段的上界限是0xFFFF(64KB)。 - -在保护模式下,段寄存器保存的不再是一个内存段的基地址,而是一个称为`段选择子`的结构。每个段描述符都对应一个`段选择子`。`段选择子`是一个16位的数据结构,下图显示了这个数据结构的内容: - -``` ------------------------------ -| Index | TI | RPL | ------------------------------ -``` - -其中, -* **Index** 表示在GDT中,对应段描述符的索引号。 -* **TI** 表示要在GDT还是LDT中查找对应的段描述符 -* **RPL** 表示请求者优先级。这个优先级将和段描述符中的优先级协同工作,共同确定访问是否合法。 - -在保护模式下,每个段寄存器实际上包含下面2部分内容: -* 可见部分 - 段选择子 -* 隐藏部分 - 段描述符 - -在保护模式中,cpu是通过下面的步骤来找到一个具体的物理地址的: - -* 代码必须将相应的`段选择子`装入某个段寄存器 -* CPU根据`段选择子`从GDT中找到一个匹配的段描述符,然后将段描述符放入段寄存器的隐藏部分 -* 在没有使用向下扩展段的时候,那么内存段的基地址就是`段描述符中的基地址`,段描述符的`limit + 1`就是内存段的长度。如果你知道一个内存地址的`偏移`,那么在没有开启分页机制的情况下,这个内存的物理地址就是`基地址+偏移` - -![linear address](http://oi62.tinypic.com/2yo369v.jpg) - -当代码要从实模式进入保护模式的时候,需要执行下面的操作: is: - -* 禁止中断发生 -* 使用命令`lgdt`将GDT表装入内存 -* 设置CR0寄存器的PE位为1,是CPU进入保护模式 -* 跳转开始执行保护模式代码 - -在后面的章节中,我们将看到Linux 内核中完整的转换代码。不过在系统进入保护模式之前,还有很多准备工作需要完成。 - -让我们代开C文件 [arch/x86/boot/main.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c)。这个文件包含了很多的函数,这些函数分别会执行键盘初始化,内存堆初始化等等操作...,下面让我们来具体看一些重要的函数。 - -将启动参数拷贝到"zeropage" --------------------------------------------------------------------------------- - -让我们从`main`函数开始看起,这个函数中,首先调用了[`copy_boot_params(void)`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L30)。 - -这个函数将内存设置信息拷贝到`boot_params`结构的相应字段。大家可以在[arch/x86/include/uapi/asm/bootparam.h](https://github.com/torvalds/linux/blob/master/arch/x86/include/uapi/asm/bootparam.h#L113)找到`boot_params`结构的定义。 - -1. 将[header.S](https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S#L281)中定义的`hdr`结构中的内容拷贝到`boot_params`结构的字段`struct setup_header hdr`中。 - -2. 如果内核是通过老的命令行协议运行起来的,那么就更新内核的命令行指针。 - -这里需要注意的是拷贝`hdr`数据结构的`memcpy`函数不是C语言中的函数,而是定义在 [copy.S](https://github.com/torvalds/linux/blob/master/arch/x86/boot/copy.S)。让我们来具体分析一下这段代码: - -```assembly -GLOBAL(memcpy) - pushw %si ;push si to stack - pushw %di ;push di to stack - movw %ax, %di ;move &boot_param.hdr to di - movw %dx, %si ;move &hdr to si - pushw %cx ;push cx to stack ( sizeof(hdr) ) - shrw $2, %cx - rep; movsl ;copy based on 4 bytes - popw %cx ;pop cx - andw $3, %cx ;cx = cx % 4 - rep; movsb ;copy based on one byte - popw %di - popw %si - retl -ENDPROC(memcpy) -``` - -在`copy.S`文件中,你可以看到所有的方法都开始于`GLOBAL`宏定义,而结束于`ENDPROC`宏定义。 - -你可以在 [arch/x86/include/asm/linkage.h](https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/linkage.h)中找到`GLOBAL`宏定义。这个宏给代码段分配了一个名字标签,并且让这个名字全局可用。 - -```assembly -#define GLOBAL(name) \ - .globl name; \ - name: -``` - -你可以在[include/linux/linkage.h](https://github.com/torvalds/linux/blob/master/include/linux/linkage.h)中找到`ENDPROC`宏的定义。 这个宏通过`END(name)`代码标识了汇编函数的结束,同时将函数名输出,从而静态分析工具可以找到这个函数。 - -```assembly -#define ENDPROC(name) \ - .type name, @function ASM_NL \ - END(name) -``` - -`memcpy`的实现代码是很容易理解的。首先,代码将`si`和`di`寄存器的值压入堆栈进行保存,这么做的原因是因为后续的代码将修改`si`和`di`寄存器的值。`memcpy`函数(也包括其他定义在copy.s中的其他函数)使用了`fastcall`调用规则,意味着所有的函数调用参数是通过`ax`, `dx`, `cx`寄存器传入的,而不是传统的通过堆栈传入。因此在使用下面的代码调用`memcpy`函数的时候 - -```c -memcpy(&boot_params.hdr, &hdr, sizeof hdr); -``` - -函数的参数是这样传递的 - -* `ax` 寄存器指向`boot_param.hdr`的内存地址 -* `dx` 寄存器指向`hdr`的内存地址 -* `cx` 寄存器包含`hdr`结构的大小 - -`memcpy`函数在将`si`和`di`寄存器压栈之后,将`boot_param.hdr`的地址放入`di`寄存器,将`hdr`的地址放入`si`寄存器,并且将`hdr`数据结构的大小压栈。 接下来代码首先以4个字节为单位,将`si`寄存器指向的内存内容拷贝`di`寄存器指向的内存。当剩下的字节数不足4字节的时候,代码将原始的`hdr`数据结构大小出栈放入`cx`,然后对`cx`的值对4求模,接下来就是根据`cx`的值,以字节为单位将`si`寄存器指向的内存内容拷贝到`di`寄存器指向的内存。当拷贝操作完成之后,将保留的`si`以及`di`寄存器值出栈,函数返回。 - -控制台初始化 --------------------------------------------------------------------------------- - -在`hdr`结构体被拷贝到`boot_params.hdr`成员之后,系统接下来将进行控制台的初始化。控制台初始化时通过调用[arch/x86/boot/early_serial_console.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/early_serial_console.c)中定义的`console_init`函数实现的。 - -这个函数首先查看命令行参数是否包含`earlyprintk`选项。如果命令行参数包含该选项,那么函数将分析这个选项的内容。得到控制台将使用的串口信息,然后进行串口的初始化。以下是`earlyprintk`选项可能的取值: - -* serial,0x3f8,115200 -* serial,ttyS0,115200 -* ttyS0,115200 - -当串口初始化成功之后,我们将看到如下的输出如果命令行参数包含`debug`选项。 - -```C -if (cmdline_find_option_bool("debug")) - puts("early console in setup code\n"); -``` - -`puts`函数定义在[tty.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/tty.c)。这个函数只是简单的调用`putchar`函数将输入字符串中的内容按字节输出。下面让我们来看看`putchar`函数的实现: - -```C -void __attribute__((section(".inittext"))) putchar(int ch) -{ - if (ch == '\n') - putchar('\r'); - - bios_putchar(ch); - - if (early_serial_base != 0) - serial_putchar(ch); -} -``` - -`__attribute__((section(".inittext")))` 说明这段代码将被放入`.inittext`代码段。关于`.inittext`代码段的定义你可以在 [setup.ld](https://github.com/torvalds/linux/blob/master/arch/x86/boot/setup.ld#L19)中找到。 - -首先如果需要输出的字符是`\n`,那么`putchar`函数将调用自己首先输出一个字符`\r`。接下来,就调用`bios_putchar`函数将字符输出到显示器(使用bios int10中断): - -```C -static void __attribute__((section(".inittext"))) bios_putchar(int ch) -{ - struct biosregs ireg; - - initregs(&ireg); - ireg.bx = 0x0007; - ireg.cx = 0x0001; - ireg.ah = 0x0e; - ireg.al = ch; - intcall(0x10, &ireg, NULL); -} -``` - -在上面的代码中`initreg`函数接受一个`biosregs`结构的地址作为输入参数,该函数首先调用`memset`函数将`biosregs`结构体所有成员清0。 - -```C - memset(reg, 0, sizeof *reg); - reg->eflags |= X86_EFLAGS_CF; - reg->ds = ds(); - reg->es = ds(); - reg->fs = fs(); - reg->gs = gs(); -``` - -下面让我们来看看[memset](https://github.com/torvalds/linux/blob/master/arch/x86/boot/copy.S#L36)函数的实现 : - -```assembly -GLOBAL(memset) - pushw %di - movw %ax, %di - movzbl %dl, %eax - imull $0x01010101,%eax - pushw %cx - shrw $2, %cx - rep; stosl - popw %cx - andw $3, %cx - rep; stosb - popw %di - retl -ENDPROC(memset) -``` - -首先你会发现,`memset`函数和`memcpy`函数一样使用了`fastcall`调用规则,因此函数的参数是通过`ax`,`dx`以及`cx`寄存器传入函数内部的。 - -就像memcpy函数一样,`memset`函数一开始将`di`寄存器入栈,然后将`biosregs`结构的地址从`ax`寄存器拷贝到`di`寄存器。记下来,使用`movzbl`指令将`dl`寄存器的内容拷贝到`ax`寄存器的滴字节,到这里`ax`寄存器就包含了需要拷贝到`di`寄存器所指向的内存的值。 - -接下来的`imull`指令将`eax`寄存器的值乘上`0x01010101`。这么做的原因是代码每次将尝试拷贝4个字节内存的内容。下面让我们来看一个具体的例子,假设我们需要将`0x7`这个数值放到内存中,在执行`imull`指令之前,`eax`寄存器的值是`0x7`,在`imull`指令被执行之后,`eax`寄存器的内容变成了`0x07070707`(4个字节的`0x7`)。在`imull`指令之后,代码使用`rep; stosl`指令将`eax`寄存器的内容拷贝到`es:di`指向的内存。 - -在`bisoregs`结构体被`initregs`函数正确填充之后,`bios_putchar` 调用中断 [0x10](http://www.ctyme.com/intr/rb-0106.htm) 在显示器上输出一个字符。接下来`putchar`函数检查是否初始化了串口,如果串口被初始化了,那么将调用[serial_putchar](https://github.com/torvalds/linux/blob/master/arch/x86/boot/tty.c#L30)将字符输出到串口。 - -堆初始化 --------------------------------------------------------------------------------- - -当堆栈和bss段在[header.S](https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S)中被初始化之后 (细节请参考上一篇[part](linux-bootstrap-1.md)), 内核需要初始化全局堆,全局堆的初始化是通过 [`init_heap`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L116) 函数实现的。 - -代码首先检查内核设置头中的[`loadflags`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S#L321)是否设置了 [`CAN_USE_HEAP`](https://github.com/torvalds/linux/blob/master/arch/x86/include/uapi/asm/bootparam.h#L21)标志。 如果该标记被设置了,那么代码将计算堆栈的结束地址:: - -```C - char *stack_end; - - //%P1 is (-STACK_SIZE) - if (boot_params.hdr.loadflags & CAN_USE_HEAP) { - asm("leal %P1(%%esp),%0" - : "=r" (stack_end) : "i" (-STACK_SIZE)); -``` - -换言之`stack_end = esp - STACK_SIZE`. - -在计算了堆栈结束地址之后,代码计算了堆的结束地址: - -```c - - //heap_end = heap_end_ptr + 512 - heap_end = (char *)((size_t)boot_params.hdr.heap_end_ptr + 0x200); -``` - -接下来代码判断`heap_end`是否大于`stack_end`,如果条件成立,将`stack_end`设置成`heap_end`(这么做是因为在大部分系统中全局堆和堆栈是相邻的)。 - -到这里位置,全局堆就被正确初始化了。在全局堆被初始化之后,我们就可以使用`GET_HEAP`方法。至于这个函数的实现和使用,我们将在后续的章节中看到。 - -检查CPU类型 --------------------------------------------------------------------------------- - -在堆栈初始化之后,内核代码通过调用[arch/x86/boot/cpu.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/cpu.c)提供的`validate_cpu`方法检查CPU类型以确定系统是否能够在当前的CPU上运行。 - -`validate_cpu`调用了[`check_cpu`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/cpucheck.c#L102)方法得到当前系统的CPU级别,并且和系统预设的最低CPU级别进行比较。如果不满足条件,则不允许系统运行。 - -```c -/*from cpu.c*/ -check_cpu(&cpu_level, &req_level, &err_flags); -/*after check_cpu call, req_level = req_level defined in cpucheck.c*/ -if (cpu_level < req_level) { - printf("This kernel requires an %s CPU, ", cpu_name(req_level)); - printf("but only detected an %s CPU.\n", cpu_name(cpu_level)); - return -1; -} -``` - -`check_cpu`方法做了大量的检测和设置工作,下面就简单介绍一些:1)检查cpu标志,如果cpu是64位cpu,那么就设置[long mode](http://en.wikipedia.org/wiki/Long_mode), 2) 检查CPU的制造商,根据制造商的不同,设置不同的CPU选项。比如对于AMD出厂的cpu,如果不支持SSE+SSE2,那么就禁止这些选项。 - -内存侦测 --------------------------------------------------------------------------------- - -接下来,内核调用`detect_memory`方法进行内存侦测,以得到系统当前内存的使用分布。该方法是用多种编程接口,包括`0xe820`,`0xe801`和`0x88`,进行内存侦测。在这里我们只介绍[arch/x86/boot/memory.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/memory.c)中提供的`detect_memory_e820`方法。 - -Let's look into the `detect_memory_e820` implementation from the [arch/x86/boot/memory.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/memory.c) source file. First of all, the `detect_memory_e820` function initializes the `biosregs` structure as we saw above and fills registers with special values for the `0xe820` call: - -```assembly - initregs(&ireg); - ireg.ax = 0xe820; - ireg.cx = sizeof buf; - ireg.edx = SMAP; - ireg.di = (size_t)&buf; -``` - -* `ax` contains the number of the function (0xe820 in our case) -* `cx` register contains size of the buffer which will contain data about memory -* `edx` must contain the `SMAP` magic number -* `es:di` must contain the address of the buffer which will contain memory data -* `ebx` has to be zero. - -Next is a loop where data about the memory will be collected. It starts from the call of the `0x15` BIOS interrupt, which writes one line from the address allocation table. For getting the next line we need to call this interrupt again (which we do in the loop). Before the next call `ebx` must contain the value returned previously: - -```C - intcall(0x15, &ireg, &oreg); - ireg.ebx = oreg.ebx; -``` - -Ultimately, it does iterations in the loop to collect data from the address allocation table and writes this data into the `e820entry` array: - -* start of memory segment -* size of memory segment -* type of memory segment (which can be reserved, usable and etc...). - -You can see the result of this in the `dmesg` output, something like: - -``` -[ 0.000000] e820: BIOS-provided physical RAM map: -[ 0.000000] BIOS-e820: [mem 0x0000000000000000-0x000000000009fbff] usable -[ 0.000000] BIOS-e820: [mem 0x000000000009fc00-0x000000000009ffff] reserved -[ 0.000000] BIOS-e820: [mem 0x00000000000f0000-0x00000000000fffff] reserved -[ 0.000000] BIOS-e820: [mem 0x0000000000100000-0x000000003ffdffff] usable -[ 0.000000] BIOS-e820: [mem 0x000000003ffe0000-0x000000003fffffff] reserved -[ 0.000000] BIOS-e820: [mem 0x00000000fffc0000-0x00000000ffffffff] reserved -``` - -Keyboard initialization --------------------------------------------------------------------------------- - -The next step is the initialization of the keyboard with the call of the [`keyboard_init()`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L65) function. At first `keyboard_init` initializes registers using the `initregs` function and calling the [0x16](http://www.ctyme.com/intr/rb-1756.htm) interrupt for getting the keyboard status. -```c - initregs(&ireg); - ireg.ah = 0x02; /* Get keyboard status */ - intcall(0x16, &ireg, &oreg); - boot_params.kbd_status = oreg.al; -``` -After this it calls [0x16](http://www.ctyme.com/intr/rb-1757.htm) again to set repeat rate and delay. -```c - ireg.ax = 0x0305; /* Set keyboard repeat rate */ - intcall(0x16, &ireg, NULL); -``` - -Querying --------------------------------------------------------------------------------- - -The next couple of steps are queries for different parameters. We will not dive into details about these queries, but will get back to it in later parts. Let's take a short look at these functions: - -The [query_mca](https://github.com/torvalds/linux/blob/master/arch/x86/boot/mca.c#L18) routine calls the [0x15](http://www.ctyme.com/intr/rb-1594.htm) BIOS interrupt to get the machine model number, sub-model number, BIOS revision level, and other hardware-specific attributes: - -```c -int query_mca(void) -{ - struct biosregs ireg, oreg; - u16 len; - - initregs(&ireg); - ireg.ah = 0xc0; - intcall(0x15, &ireg, &oreg); - - if (oreg.eflags & X86_EFLAGS_CF) - return -1; /* No MCA present */ - - set_fs(oreg.es); - len = rdfs16(oreg.bx); - - if (len > sizeof(boot_params.sys_desc_table)) - len = sizeof(boot_params.sys_desc_table); - - copy_from_fs(&boot_params.sys_desc_table, oreg.bx, len); - return 0; -} -``` - -It fills the `ah` register with `0xc0` and calls the `0x15` BIOS interruption. After the interrupt execution it checks the [carry flag](http://en.wikipedia.org/wiki/Carry_flag) and if it is set to 1, the BIOS doesn't support (**MCA**)[https://en.wikipedia.org/wiki/Micro_Channel_architecture]. If carry flag is set to 0, `ES:BX` will contain a pointer to the system information table, which looks like this: - -``` -Offset Size Description - 00h WORD number of bytes following - 02h BYTE model (see #00515) - 03h BYTE submodel (see #00515) - 04h BYTE BIOS revision: 0 for first release, 1 for 2nd, etc. - 05h BYTE feature byte 1 (see #00510) - 06h BYTE feature byte 2 (see #00511) - 07h BYTE feature byte 3 (see #00512) - 08h BYTE feature byte 4 (see #00513) - 09h BYTE feature byte 5 (see #00514) ----AWARD BIOS--- - 0Ah N BYTEs AWARD copyright notice ----Phoenix BIOS--- - 0Ah BYTE ??? (00h) - 0Bh BYTE major version - 0Ch BYTE minor version (BCD) - 0Dh 4 BYTEs ASCIZ string "PTL" (Phoenix Technologies Ltd) ----Quadram Quad386--- - 0Ah 17 BYTEs ASCII signature string "Quadram Quad386XT" ----Toshiba (Satellite Pro 435CDS at least)--- - 0Ah 7 BYTEs signature "TOSHIBA" - 11h BYTE ??? (8h) - 12h BYTE ??? (E7h) product ID??? (guess) - 13h 3 BYTEs "JPN" - ``` - -Next we call the `set_fs` routine and pass the value of the `es` register to it. The implementation of `set_fs` is pretty simple: - -```c -static inline void set_fs(u16 seg) -{ - asm volatile("movw %0,%%fs" : : "rm" (seg)); -} -``` - -This function contains inline assembly which gets the value of the `seg` parameter and puts it into the `fs` register. There are many functions in [boot.h](https://github.com/torvalds/linux/blob/master/arch/x86/boot/boot.h) like `set_fs`, for example `set_gs`, `fs`, `gs` for reading a value in it etc... - -At the end of `query_mca` it just copies the table pointed to by `es:bx` to the `boot_params.sys_desc_table`. - -The next step is getting [Intel SpeedStep](http://en.wikipedia.org/wiki/SpeedStep) information by calling the `query_ist` function. First of all it checks the CPU level and if it is correct, calls `0x15` for getting info and saves the result to `boot_params`. - -The following [query_apm_bios](https://github.com/torvalds/linux/blob/master/arch/x86/boot/apm.c#L21) function gets [Advanced Power Management](http://en.wikipedia.org/wiki/Advanced_Power_Management) information from the BIOS. `query_apm_bios` calls the `0x15` BIOS interruption too, but with `ah` = `0x53` to check `APM` installation. After the `0x15` execution, `query_apm_bios` functions check the `PM` signature (it must be `0x504d`), carry flag (it must be 0 if `APM` supported) and value of the `cx` register (if it's 0x02, protected mode interface is supported). - -Next it calls `0x15` again, but with `ax = 0x5304` for disconnecting the `APM` interface and connecting the 32-bit protected mode interface. In the end it fills `boot_params.apm_bios_info` with values obtained from the BIOS. - -Note that `query_apm_bios` will be executed only if `CONFIG_APM` or `CONFIG_APM_MODULE` was set in the configuration file: - -```C -#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE) - query_apm_bios(); -#endif -``` - -The last is the [`query_edd`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/edd.c#L122) function, which queries `Enhanced Disk Drive` information from the BIOS. Let's look into the `query_edd` implementation. - -First of all it reads the [edd](https://github.com/torvalds/linux/blob/master/Documentation/kernel-parameters.txt#L1023) option from the kernel's command line and if it was set to `off` then `query_edd` just returns. - -If EDD is enabled, `query_edd` goes over BIOS-supported hard disks and queries EDD information in the following loop: - -```C -for (devno = 0x80; devno < 0x80+EDD_MBR_SIG_MAX; devno++) { - if (!get_edd_info(devno, &ei) && boot_params.eddbuf_entries < EDDMAXNR) { - memcpy(edp, &ei, sizeof ei); - edp++; - boot_params.eddbuf_entries++; - } - ... - ... - ... -``` - -where `0x80` is the first hard drive and the value of `EDD_MBR_SIG_MAX` macro is 16. It collects data into the array of [edd_info](https://github.com/torvalds/linux/blob/master/include/uapi/linux/edd.h#L172) structures. `get_edd_info` checks that EDD is present by invoking the `0x13` interrupt with `ah` as `0x41` and if EDD is present, `get_edd_info` again calls the `0x13` interrupt, but with `ah` as `0x48` and `si` containing the address of the buffer where EDD information will be stored. - -Conclusion --------------------------------------------------------------------------------- - -This is the end of the second part about Linux kernel insides. In the next part we will see video mode setting and the rest of preparations before transition to protected mode and directly transitioning into it. - -If you have any questions or suggestions write me a comment or ping me at [twitter](https://twitter.com/0xAX). - -**Please note that English is not my first language, And I am really sorry for any inconvenience. If you find any mistakes please send me a PR to [linux-insides](https://github.com/0xAX/linux-internals).** - -Links --------------------------------------------------------------------------------- - -* [Protected mode](http://en.wikipedia.org/wiki/Protected_mode) -* [Protected mode](http://wiki.osdev.org/Protected_Mode) -* [Long mode](http://en.wikipedia.org/wiki/Long_mode) -* [Nice explanation of CPU Modes with code](http://www.codeproject.com/Articles/45788/The-Real-Protected-Long-mode-assembly-tutorial-for) -* [How to Use Expand Down Segments on Intel 386 and Later CPUs](http://www.sudleyplace.com/dpmione/expanddown.html) -* [earlyprintk documentation](http://lxr.free-electrons.com/source/Documentation/x86/earlyprintk.txt) -* [Kernel Parameters](https://github.com/torvalds/linux/blob/master/Documentation/kernel-parameters.txt) -* [Serial console](https://github.com/torvalds/linux/blob/master/Documentation/serial-console.txt) -* [Intel SpeedStep](http://en.wikipedia.org/wiki/SpeedStep) -* [APM](https://en.wikipedia.org/wiki/Advanced_Power_Management) -* [EDD specification](http://www.t13.org/documents/UploadedDocuments/docs2004/d1572r3-EDD3.pdf) -* [TLDP documentation for Linux Boot Process](http://www.tldp.org/HOWTO/Linux-i386-Boot-Code-HOWTO/setup.html) (old) -* [Previous Part](linux-bootstrap-1.md) \ No newline at end of file From a99f21f8d7fd5a9076da5a6128f94d9888bcfb79 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 10:36:35 -0500 Subject: [PATCH 062/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) mode change 100755 => 100644 Booting/linux-bootstrap-2.md diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md old mode 100755 new mode 100644 index e7e862c..afa2f96 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -4,7 +4,7 @@ 内核启动的第一步 -------------------------------------------------------------------------------- -在[上一节中](https://0xax.gitbooks.io/linux-insides/content/Booting/linux-bootstrap-1.html)我们开始接触到内核启动代码,并且分析了初始化部分,最后我们停在了对`main`函数(`main`函数是第一个用C写的函数)的调用(`main`函数位于[arch/x86/boot/main.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c)。 +在[上一节中](https://0xax.gitbooks.io/linux-insides/content/Booting/linux-bootstrap-1.html)我们开始接触到内核启动代码,并且分析了初始化部分,最后我们停在了对`main`函数(`main`函数是第一个用C写的函数)的调用(`main`函数位于[arch/x86/boot/main.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c))。 在这一节中我们将继续对内核启动过程的研究,我们将 * 认识`保护模式` From 5f9940bcf869c40632d8ae7153e47d3386d2eff2 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 10:37:00 -0500 Subject: [PATCH 063/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index afa2f96..69ca046 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -9,7 +9,7 @@ 在这一节中我们将继续对内核启动过程的研究,我们将 * 认识`保护模式` * 如何从实模式进入保护模式 -* 堆和字符界面初始化 +* 堆和控制台初始化 * 内存检测,cpu验证,键盘初始化 * 还有更多 From 92fb2bdbd9988decd99ea6905ef86f8feb6b41a0 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 10:37:28 -0500 Subject: [PATCH 064/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 69ca046..517996e 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -19,7 +19,7 @@ -------------------------------------------------------------------------------- 在操作系统可以使用Intel 64位CPU的[长模式](http://en.wikipedia.org/wiki/Long_mode)之前,内核必须首先将CPU切换到保护模式运行。 -什么是[保护模式](https://en.wikipedia.org/wiki/Protected_mode)?保护模式于1982年被引入到Intel CPU家族,并且从那之后,知道Intel 64出现,保护模式都是Intel CPU的主要运行模式。 +什么是[保护模式](https://en.wikipedia.org/wiki/Protected_mode)?保护模式于1982年被引入到Intel CPU家族,并且从那之后,直到Intel 64出现,保护模式都是Intel CPU的主要运行模式。 淘汰[实模式](http://wiki.osdev.org/Real_Mode)的主要原因是因为在实模式下,系统能够访问的内存非常有限。如果你还记得我们在上一节说的,在实模式下,系统最多只能访问1M内存,而且在很多时候,实际能够访问的内存只有640K。 From f54872341c8fed930d10205916f00df78f0a7302 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 10:37:52 -0500 Subject: [PATCH 065/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 517996e..688d373 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -23,7 +23,7 @@ 淘汰[实模式](http://wiki.osdev.org/Real_Mode)的主要原因是因为在实模式下,系统能够访问的内存非常有限。如果你还记得我们在上一节说的,在实模式下,系统最多只能访问1M内存,而且在很多时候,实际能够访问的内存只有640K。 -保护模式带来了很多的改变,不过只要的改变都集中在内存管理方法。在保护模式中,实模式的20位地址线被替换成32位地址线,因此系统可以访问多大4GB的地址空间。另外,在保护模式中引入了[内存分页](http://en.wikipedia.org/wiki/Paging)功能,在后面我们将介绍这个功能。 +保护模式带来了很多的改变,不过主要的改变都集中在内存管理方法。在保护模式中,实模式的20位地址线被替换成32位地址线,因此系统可以访问多大4GB的地址空间。另外,在保护模式中引入了[内存分页](http://en.wikipedia.org/wiki/Paging)功能,在后面我们将介绍这个功能。 保护模式提供了2种完全不同的内存关机机制: From 684f12f27a1dace47bb51df9736d897dc8f75d01 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 10:38:13 -0500 Subject: [PATCH 066/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 688d373..818b796 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -23,7 +23,7 @@ 淘汰[实模式](http://wiki.osdev.org/Real_Mode)的主要原因是因为在实模式下,系统能够访问的内存非常有限。如果你还记得我们在上一节说的,在实模式下,系统最多只能访问1M内存,而且在很多时候,实际能够访问的内存只有640K。 -保护模式带来了很多的改变,不过主要的改变都集中在内存管理方法。在保护模式中,实模式的20位地址线被替换成32位地址线,因此系统可以访问多大4GB的地址空间。另外,在保护模式中引入了[内存分页](http://en.wikipedia.org/wiki/Paging)功能,在后面我们将介绍这个功能。 +保护模式带来了很多的改变,不过主要的改变都集中在内存管理方法。在保护模式中,实模式的20位地址线被替换成32位地址线,因此系统可以访问多大4GB的地址空间。另外,在保护模式中引入了[内存分页](http://en.wikipedia.org/wiki/Paging)功能,在后面的章节中我们将介绍这个功能。 保护模式提供了2种完全不同的内存关机机制: From 4b9d394d006d13d841edce63d41e432529033504 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 10:38:22 -0500 Subject: [PATCH 067/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 818b796..03146ad 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -25,7 +25,7 @@ 保护模式带来了很多的改变,不过主要的改变都集中在内存管理方法。在保护模式中,实模式的20位地址线被替换成32位地址线,因此系统可以访问多大4GB的地址空间。另外,在保护模式中引入了[内存分页](http://en.wikipedia.org/wiki/Paging)功能,在后面的章节中我们将介绍这个功能。 -保护模式提供了2种完全不同的内存关机机制: +保护模式提供了2种完全不同的内存机制: * 段式内存管理 * 内存分页 From fc5795e05232b8da70531edf4aa7d6e69778dcc4 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 10:38:46 -0500 Subject: [PATCH 068/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 03146ad..0d1f951 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -25,7 +25,7 @@ 保护模式带来了很多的改变,不过主要的改变都集中在内存管理方法。在保护模式中,实模式的20位地址线被替换成32位地址线,因此系统可以访问多大4GB的地址空间。另外,在保护模式中引入了[内存分页](http://en.wikipedia.org/wiki/Paging)功能,在后面的章节中我们将介绍这个功能。 -保护模式提供了2种完全不同的内存机制: +保护模式提供了2种完全不同的内存管理机制: * 段式内存管理 * 内存分页 @@ -37,7 +37,7 @@ * 内存段的基地址 * 从基地址开始的偏移 -通过这2个信息,我们可以通过下面的公式计算出对应的物理地址 +使用这2个信息,我们可以通过下面的公式计算出对应的物理地址 ``` PhysicalAddress = Segment * 16 + Offset From 8acfcc3dd4618baebb8d3e3e71c46af285fa7fc5 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 10:39:12 -0500 Subject: [PATCH 069/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 0d1f951..ae17457 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -43,7 +43,7 @@ PhysicalAddress = Segment * 16 + Offset ``` -在保护模式中,内存段的定义和实模式完全不同。在保护模式中,每个内存段不再是64K大小,段的大小和起始位置是通过一个叫做`段描述符`的数据结构进行描述的。所有内存段的段描述符存储在一个叫做`全局描述符表`(GDT)的内存结构中。 +在保护模式中,内存段的定义和实模式完全不同。在保护模式中,每个内存段不再是64K大小,段的大小和起始位置是通过一个叫做`段描述符`的数据结构进行描述。所有内存段的段描述符存储在一个叫做`全局描述符表`(GDT)的内存结构中。 全局描述符表示一个内存数据结构,但是它在内存中的位置并不是固定的,它的地址保存在一个特殊寄存器`GDTR`中。在后面的章节中,我们将在Linux内核代码中看到全局描述符表的地址是如何被保存到`GDTR`中的。具体的汇编代码看起来是这样的: From 3773d2eccddb30cbbde471eff4e0d7127899ad44 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 10:39:35 -0500 Subject: [PATCH 070/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index ae17457..fce60c8 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -45,7 +45,7 @@ PhysicalAddress = Segment * 16 + Offset 在保护模式中,内存段的定义和实模式完全不同。在保护模式中,每个内存段不再是64K大小,段的大小和起始位置是通过一个叫做`段描述符`的数据结构进行描述。所有内存段的段描述符存储在一个叫做`全局描述符表`(GDT)的内存结构中。 -全局描述符表示一个内存数据结构,但是它在内存中的位置并不是固定的,它的地址保存在一个特殊寄存器`GDTR`中。在后面的章节中,我们将在Linux内核代码中看到全局描述符表的地址是如何被保存到`GDTR`中的。具体的汇编代码看起来是这样的: +`全局描述符表`是一个内存数据结构,但是它在内存中的位置并不是固定的,它的地址保存在一个特殊寄存器`GDTR`中。在后面的章节中,我们将在Linux内核代码中看到全局描述符表的地址是如何被保存到`GDTR`中的。具体的汇编代码看起来是这样的: ```assembly lgdt gdt From 6a12deb61cac68edbd507d1d87a9d5ff99fd2139 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 10:39:48 -0500 Subject: [PATCH 071/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index fce60c8..ff27555 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -45,7 +45,7 @@ PhysicalAddress = Segment * 16 + Offset 在保护模式中,内存段的定义和实模式完全不同。在保护模式中,每个内存段不再是64K大小,段的大小和起始位置是通过一个叫做`段描述符`的数据结构进行描述。所有内存段的段描述符存储在一个叫做`全局描述符表`(GDT)的内存结构中。 -`全局描述符表`是一个内存数据结构,但是它在内存中的位置并不是固定的,它的地址保存在一个特殊寄存器`GDTR`中。在后面的章节中,我们将在Linux内核代码中看到全局描述符表的地址是如何被保存到`GDTR`中的。具体的汇编代码看起来是这样的: +`全局描述符表`这个内存数据结构在内存中的位置并不是固定的,它的地址保存在一个特殊寄存器`GDTR`中。在后面的章节中,我们将在Linux内核代码中看到全局描述符表的地址是如何被保存到`GDTR`中的。具体的汇编代码看起来是这样的: ```assembly lgdt gdt From 2a9fc993c6884289969e3b094b4706901fe76193 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 10:40:36 -0500 Subject: [PATCH 072/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index ff27555..34ad840 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -71,7 +71,7 @@ lgdt gdt ------------------------------------------------------------ ``` -粗粗一看,上面的结构非常吓人,不过实际上这个结构是非常容易理解的。比如在上图中的LIMIT 15:0表示这个数据结构的0到15位保存的内存段的大小的0到15位。相似的LIMITE 19:16表示上述数据结构的16到19位保存的是内存段大小的16到19位。从这个分析中,我们可以看出每个内存段的大小是通过20位进行描述的。下面我们将对这个数据结构进行仔细分析: +粗粗一看,上面的结构非常吓人,不过实际上这个结构是非常容易理解的。比如在上图中的LIMIT 15:0表示这个数据结构的0到15位保存的是内存段的大小的0到15位。相似的LIMITE 19:16表示上述数据结构的16到19位保存的是内存段大小的16到19位。从这个分析中,我们可以看出每个内存段的大小是通过20位进行描述的。下面我们将对这个数据结构进行仔细分析: 1. Limit[20位]被保存在上述内存结构的0-15和16-19位。根据上述内存结构中`G`位的设置,这20位内存定义的内存长度是不一样的。下面是一些具体的例子: From 115ee9bb8f30965fed44e93107e62d06a0807ac2 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 10:46:07 -0500 Subject: [PATCH 073/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 34ad840..3b334b8 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -83,7 +83,7 @@ lgdt gdt 从上面的例子我们可以看出: * 如果G = 0, 那么内存段的长度是按照1 byte进行增长的 ( Limit每增加1,段长度增加1 byte ),最大的内存段长度将是1M bytes; - * 如果G = 1, 那么内存段的长度是按照4K bytes ( Limit每增加1,段长度增加4K bytes )进行增长的,最大的内存段长度将是4G bytes; + * 如果G = 1, 那么内存段的长度是按照4K bytes进行增长的 ( Limit每增加1,段长度增加4K bytes ),最大的内存段长度将是4G bytes; * 段长度的计算公司是 base_seg_length * ( LIMIT + 1)。 2. Base[32-bits]被保存在上述地址结构的0-15, 32-39以及56-63位。Base定义了段基址。 From 2168c31190a4940d098d10e6856b890d9a1b11a3 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 10:47:09 -0500 Subject: [PATCH 074/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 3b334b8..638c9e2 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -91,7 +91,7 @@ lgdt gdt 3. Type/Attribute (40-47 bits) 定义了内存段的类型以及支持的操作。 * `S` 标记( 第44位 )定义了段的类型,`S` = 0说明这个内存段是一个系统段;`S` = 1说明这个内存段是一个代码段或者是数据段( 堆栈段是一种特使类型的数据段,堆栈段必须是可以进行读写的段 )。 -在`S` = 1的情况下,上述内存结构的第43位决定了内存段是数据段还是代码段。如果43位 = 0,拿说明是一个数据段,否则就是一个代码段。 +在`S` = 1的情况下,上述内存结构的第43位决定了内存段是数据段还是代码段。如果43位 = 0,那说明是一个数据段,否则就是一个代码段。 对于数据段和代码段,下面的表格给出了段类型定义 From f9b5f8a7306b88f67721d70f1e5a1964ada769a2 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 10:47:11 -0500 Subject: [PATCH 075/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 638c9e2..d19a878 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -91,7 +91,7 @@ lgdt gdt 3. Type/Attribute (40-47 bits) 定义了内存段的类型以及支持的操作。 * `S` 标记( 第44位 )定义了段的类型,`S` = 0说明这个内存段是一个系统段;`S` = 1说明这个内存段是一个代码段或者是数据段( 堆栈段是一种特使类型的数据段,堆栈段必须是可以进行读写的段 )。 -在`S` = 1的情况下,上述内存结构的第43位决定了内存段是数据段还是代码段。如果43位 = 0,那说明是一个数据段,否则就是一个代码段。 +在`S` = 1的情况下,上述内存结构的第43位决定了内存段是数据段还是代码段。如果43位 = 0,说明是一个数据段,否则就是一个代码段。 对于数据段和代码段,下面的表格给出了段类型定义 From 2bd0c1030c21ca90d97c6933c8440596cfa9a8f8 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 10:47:55 -0500 Subject: [PATCH 076/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index d19a878..c67dd6a 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -119,7 +119,8 @@ lgdt gdt | 15 1 1 1 1 | Code | Execute/Read, conforming, accessed ``` -从上面的表格我们可以看出,当第43位是`0`的时候,这个段描述符对应的是一个数据段,如果该位是`1`,那么表示这个段描述符对应的是一个代码段。对于数据段,第42,41,40位表示的是(*E*扩展,*W*可写,*A*可访问);对于代码段,第42,41,40位表示的是(*C*一致,*R*可读,*A*可访问)。 *A*ccessible) or CRA(*C*onforming *R*eadable *A*ccessible)。 +从上面的表格我们可以看出,当第43位是`0`的时候,这个段描述符对应的是一个数据段,如果该位是`1`,那么表示这个段描述符对应的是一个代码段。对于数据段,第42,41,40位表示的是(*E*扩展,*W*可写,*A*可访问);对于代码段,第42,41,40位表示的是(*C*一致,*R*可读,*A*可访问)。 + * 如果`E` = 0,数据段是向上扩展数据段,反之为向下扩展数据段。关于向上扩展和向下扩展数据段,可以参考下面的[链接](http://www.sudleyplace.com/dpmione/expanddown.html)。在一般情况下,应该是不会使用向下扩展数据段的。 * 如果`W` = 1,说明这个数据段是可写的,否则不可写。所有数据段都是可读的。 * A位表示该内存段是否已经被CPU访问。 From cd30b20e7345ca44db01b2cf8df99f9f3612d2e2 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 10:48:24 -0500 Subject: [PATCH 077/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index c67dd6a..e1f5ffb 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -124,7 +124,7 @@ lgdt gdt * 如果`E` = 0,数据段是向上扩展数据段,反之为向下扩展数据段。关于向上扩展和向下扩展数据段,可以参考下面的[链接](http://www.sudleyplace.com/dpmione/expanddown.html)。在一般情况下,应该是不会使用向下扩展数据段的。 * 如果`W` = 1,说明这个数据段是可写的,否则不可写。所有数据段都是可读的。 * A位表示该内存段是否已经被CPU访问。 - * 如果`C` = 1,说明这个代码段可以被第优先级的代码访问,比如可以被用户态代码访问。反之如果`C` = 0,说明只能同优先级的代码段可以访问。 + * 如果`C` = 1,说明这个代码段可以被低优先级的代码访问,比如可以被用户态代码访问。反之如果`C` = 0,说明只能同优先级的代码段可以访问。 * 如果`R` = 1,说明该代码段可读。代码段是永远没有写权限的。 4. DPL[2-bits, bit 45 和 46] (描述符优先级) 定义了该段的优先级。具体数值是0-3。 From 967d5f91cdadace993ba876f8bca6454985ed1f4 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 10:51:35 -0500 Subject: [PATCH 078/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index e1f5ffb..d8c406c 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -166,7 +166,7 @@ lgdt gdt ![linear address](http://oi62.tinypic.com/2yo369v.jpg) -当代码要从实模式进入保护模式的时候,需要执行下面的操作: is: +当代码要从实模式进入保护模式的时候,需要执行下面的操作: * 禁止中断发生 * 使用命令`lgdt`将GDT表装入内存 From 4830d2fe5567e4144a3dd25ea41650dc5dbe375b Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 10:52:19 -0500 Subject: [PATCH 079/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index d8c406c..92c5948 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -169,7 +169,7 @@ lgdt gdt 当代码要从实模式进入保护模式的时候,需要执行下面的操作: * 禁止中断发生 -* 使用命令`lgdt`将GDT表装入内存 +* 使用命令`lgdt`将GDT表装入`GRTD`寄存器 * 设置CR0寄存器的PE位为1,是CPU进入保护模式 * 跳转开始执行保护模式代码 From 4d871cb7a9a20beeaf0ef2c7ce9d3a839fbe3372 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 10:52:44 -0500 Subject: [PATCH 080/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 92c5948..3d887aa 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -173,7 +173,7 @@ lgdt gdt * 设置CR0寄存器的PE位为1,是CPU进入保护模式 * 跳转开始执行保护模式代码 -在后面的章节中,我们将看到Linux 内核中完整的转换代码。不过在系统进入保护模式之前,还有很多准备工作需要完成。 +在后面的章节中,我们将看到Linux 内核中完整的转换代码。不过在系统进入保护模式之前,内核有很多的准备工作需要进行。 让我们代开C文件 [arch/x86/boot/main.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c)。这个文件包含了很多的函数,这些函数分别会执行键盘初始化,内存堆初始化等等操作...,下面让我们来具体看一些重要的函数。 From 7bdb21c64ed7c7e0301b63fd689e3da46c49e839 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 10:53:55 -0500 Subject: [PATCH 081/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 3d887aa..21e225a 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -182,7 +182,7 @@ lgdt gdt 让我们从`main`函数开始看起,这个函数中,首先调用了[`copy_boot_params(void)`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L30)。 -这个函数将内存设置信息拷贝到`boot_params`结构的相应字段。大家可以在[arch/x86/include/uapi/asm/bootparam.h](https://github.com/torvalds/linux/blob/master/arch/x86/include/uapi/asm/bootparam.h#L113)找到`boot_params`结构的定义。 +这个函数将内核设置信息拷贝到`boot_params`结构的相应字段。大家可以在[arch/x86/include/uapi/asm/bootparam.h](https://github.com/torvalds/linux/blob/master/arch/x86/include/uapi/asm/bootparam.h#L113)找到`boot_params`结构的定义。 1. 将[header.S](https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S#L281)中定义的`hdr`结构中的内容拷贝到`boot_params`结构的字段`struct setup_header hdr`中。 From 6dfe9091885243328446acc081df8e60e9fd018d Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 10:55:26 -0500 Subject: [PATCH 082/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 21e225a..783f42a 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -238,7 +238,7 @@ memcpy(&boot_params.hdr, &hdr, sizeof hdr); * `dx` 寄存器指向`hdr`的内存地址 * `cx` 寄存器包含`hdr`结构的大小 -`memcpy`函数在将`si`和`di`寄存器压栈之后,将`boot_param.hdr`的地址放入`di`寄存器,将`hdr`的地址放入`si`寄存器,并且将`hdr`数据结构的大小压栈。 接下来代码首先以4个字节为单位,将`si`寄存器指向的内存内容拷贝`di`寄存器指向的内存。当剩下的字节数不足4字节的时候,代码将原始的`hdr`数据结构大小出栈放入`cx`,然后对`cx`的值对4求模,接下来就是根据`cx`的值,以字节为单位将`si`寄存器指向的内存内容拷贝到`di`寄存器指向的内存。当拷贝操作完成之后,将保留的`si`以及`di`寄存器值出栈,函数返回。 +`memcpy`函数在将`si`和`di`寄存器压栈之后,将`boot_param.hdr`的地址放入`di`寄存器,将`hdr`的地址放入`si`寄存器,并且将`hdr`数据结构的大小压栈。 接下来代码首先以4个字节为单位,将`si`寄存器指向的内存内容拷贝到`di`寄存器指向的内存。当剩下的字节数不足4字节的时候,代码将原始的`hdr`数据结构大小出栈放入`cx`,然后对`cx`的值对4求模,接下来就是根据`cx`的值,以字节为单位将`si`寄存器指向的内存内容拷贝到`di`寄存器指向的内存。当拷贝操作完成之后,将保留的`si`以及`di`寄存器值出栈,函数返回。 控制台初始化 -------------------------------------------------------------------------------- From a29fb8027dc53c48cb911cbe13f72df0ac125973 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 10:56:21 -0500 Subject: [PATCH 083/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 783f42a..235986f 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -251,7 +251,7 @@ memcpy(&boot_params.hdr, &hdr, sizeof hdr); * serial,ttyS0,115200 * ttyS0,115200 -当串口初始化成功之后,我们将看到如下的输出如果命令行参数包含`debug`选项。 +当串口初始化成功之后,如果命令行参数包含`debug`选项,我们将看到如下的输出。 ```C if (cmdline_find_option_bool("debug")) From 473cf0f848a8e1cd69ef2a0abdf8e77ec7f78ee2 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 10:56:55 -0500 Subject: [PATCH 084/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 235986f..66e8995 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -275,7 +275,7 @@ void __attribute__((section(".inittext"))) putchar(int ch) `__attribute__((section(".inittext")))` 说明这段代码将被放入`.inittext`代码段。关于`.inittext`代码段的定义你可以在 [setup.ld](https://github.com/torvalds/linux/blob/master/arch/x86/boot/setup.ld#L19)中找到。 -首先如果需要输出的字符是`\n`,那么`putchar`函数将调用自己首先输出一个字符`\r`。接下来,就调用`bios_putchar`函数将字符输出到显示器(使用bios int10中断): +如果需要输出的字符是`\n`,那么`putchar`函数将调用自己首先输出一个字符`\r`。接下来,就调用`bios_putchar`函数将字符输出到显示器(使用bios int10中断): ```C static void __attribute__((section(".inittext"))) bios_putchar(int ch) From 828817384364759b0d979f8663f681ec407258c3 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 10:57:52 -0500 Subject: [PATCH 085/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 66e8995..5d7e833 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -323,7 +323,7 @@ ENDPROC(memset) 首先你会发现,`memset`函数和`memcpy`函数一样使用了`fastcall`调用规则,因此函数的参数是通过`ax`,`dx`以及`cx`寄存器传入函数内部的。 -就像memcpy函数一样,`memset`函数一开始将`di`寄存器入栈,然后将`biosregs`结构的地址从`ax`寄存器拷贝到`di`寄存器。记下来,使用`movzbl`指令将`dl`寄存器的内容拷贝到`ax`寄存器的滴字节,到这里`ax`寄存器就包含了需要拷贝到`di`寄存器所指向的内存的值。 +就像memcpy函数一样,`memset`函数一开始将`di`寄存器入栈,然后将`biosregs`结构的地址从`ax`寄存器拷贝到`di`寄存器。接下来,使用`movzbl`指令将`dl`寄存器的内容拷贝到`ax`寄存器的滴字节,到这里`ax`寄存器就包含了需要拷贝到`di`寄存器所指向的内存的值。 接下来的`imull`指令将`eax`寄存器的值乘上`0x01010101`。这么做的原因是代码每次将尝试拷贝4个字节内存的内容。下面让我们来看一个具体的例子,假设我们需要将`0x7`这个数值放到内存中,在执行`imull`指令之前,`eax`寄存器的值是`0x7`,在`imull`指令被执行之后,`eax`寄存器的内容变成了`0x07070707`(4个字节的`0x7`)。在`imull`指令之后,代码使用`rep; stosl`指令将`eax`寄存器的内容拷贝到`es:di`指向的内存。 From eacd3f4b5b2b2bfd4e114d2b726bc10449cd09f2 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 10:58:01 -0500 Subject: [PATCH 086/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 5d7e833..c96858f 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -323,7 +323,7 @@ ENDPROC(memset) 首先你会发现,`memset`函数和`memcpy`函数一样使用了`fastcall`调用规则,因此函数的参数是通过`ax`,`dx`以及`cx`寄存器传入函数内部的。 -就像memcpy函数一样,`memset`函数一开始将`di`寄存器入栈,然后将`biosregs`结构的地址从`ax`寄存器拷贝到`di`寄存器。接下来,使用`movzbl`指令将`dl`寄存器的内容拷贝到`ax`寄存器的滴字节,到这里`ax`寄存器就包含了需要拷贝到`di`寄存器所指向的内存的值。 +就像memcpy函数一样,`memset`函数一开始将`di`寄存器入栈,然后将`biosregs`结构的地址从`ax`寄存器拷贝到`di`寄存器。接下来,使用`movzbl`指令将`dl`寄存器的内容拷贝到`ax`寄存器的字节,到这里`ax`寄存器就包含了需要拷贝到`di`寄存器所指向的内存的值。 接下来的`imull`指令将`eax`寄存器的值乘上`0x01010101`。这么做的原因是代码每次将尝试拷贝4个字节内存的内容。下面让我们来看一个具体的例子,假设我们需要将`0x7`这个数值放到内存中,在执行`imull`指令之前,`eax`寄存器的值是`0x7`,在`imull`指令被执行之后,`eax`寄存器的内容变成了`0x07070707`(4个字节的`0x7`)。在`imull`指令之后,代码使用`rep; stosl`指令将`eax`寄存器的内容拷贝到`es:di`指向的内存。 From 217f0aeaf98e20cc2f2f7c36414885e3d64e8926 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 10:59:11 -0500 Subject: [PATCH 087/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index c96858f..e55604d 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -323,7 +323,7 @@ ENDPROC(memset) 首先你会发现,`memset`函数和`memcpy`函数一样使用了`fastcall`调用规则,因此函数的参数是通过`ax`,`dx`以及`cx`寄存器传入函数内部的。 -就像memcpy函数一样,`memset`函数一开始将`di`寄存器入栈,然后将`biosregs`结构的地址从`ax`寄存器拷贝到`di`寄存器。接下来,使用`movzbl`指令将`dl`寄存器的内容拷贝到`ax`寄存器的字节,到这里`ax`寄存器就包含了需要拷贝到`di`寄存器所指向的内存的值。 +就像memcpy函数一样,`memset`函数一开始将`di`寄存器入栈,然后将`biosregs`结构的地址从`ax`寄存器拷贝到`di`寄存器。接下来,使用`movzbl`指令将`dl`寄存器的内容拷贝到`ax`寄存器的低字节,到这里`ax`寄存器就包含了需要拷贝到`di`寄存器所指向的内存的值。 接下来的`imull`指令将`eax`寄存器的值乘上`0x01010101`。这么做的原因是代码每次将尝试拷贝4个字节内存的内容。下面让我们来看一个具体的例子,假设我们需要将`0x7`这个数值放到内存中,在执行`imull`指令之前,`eax`寄存器的值是`0x7`,在`imull`指令被执行之后,`eax`寄存器的内容变成了`0x07070707`(4个字节的`0x7`)。在`imull`指令之后,代码使用`rep; stosl`指令将`eax`寄存器的内容拷贝到`es:di`指向的内存。 @@ -332,7 +332,7 @@ ENDPROC(memset) 堆初始化 -------------------------------------------------------------------------------- -当堆栈和bss段在[header.S](https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S)中被初始化之后 (细节请参考上一篇[part](linux-bootstrap-1.md)), 内核需要初始化全局堆,全局堆的初始化是通过 [`init_heap`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L116) 函数实现的。 +当堆栈和bss段在[header.S](https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S)中被初始化之后 (细节请参考上一篇[part](linux-bootstrap-1.md)), 内核需要初始化全局堆,全局堆的初始化是通过 [`init_heap`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L116) 函数实现的。 代码首先检查内核设置头中的[`loadflags`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S#L321)是否设置了 [`CAN_USE_HEAP`](https://github.com/torvalds/linux/blob/master/arch/x86/include/uapi/asm/bootparam.h#L21)标志。 如果该标记被设置了,那么代码将计算堆栈的结束地址:: From 46fc388968fa84fd083cc73cfc4afdf49494cabc Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 11:00:05 -0500 Subject: [PATCH 088/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index e55604d..10c4cf2 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -355,7 +355,7 @@ ENDPROC(memset) heap_end = (char *)((size_t)boot_params.hdr.heap_end_ptr + 0x200); ``` -接下来代码判断`heap_end`是否大于`stack_end`,如果条件成立,将`stack_end`设置成`heap_end`(这么做是因为在大部分系统中全局堆和堆栈是相邻的)。 +接下来代码判断`heap_end`是否大于`stack_end`,如果条件成立,将`stack_end`设置成`heap_end`(这么做是因为在大部分系统中全局堆和堆栈是相邻的,但是增长方向是相反的)。 到这里位置,全局堆就被正确初始化了。在全局堆被初始化之后,我们就可以使用`GET_HEAP`方法。至于这个函数的实现和使用,我们将在后续的章节中看到。 From 03846a5e87381090c97a2482917ed970a37089fc Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 11:00:17 -0500 Subject: [PATCH 089/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 10c4cf2..4ea7049 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -357,7 +357,7 @@ ENDPROC(memset) 接下来代码判断`heap_end`是否大于`stack_end`,如果条件成立,将`stack_end`设置成`heap_end`(这么做是因为在大部分系统中全局堆和堆栈是相邻的,但是增长方向是相反的)。 -到这里位置,全局堆就被正确初始化了。在全局堆被初始化之后,我们就可以使用`GET_HEAP`方法。至于这个函数的实现和使用,我们将在后续的章节中看到。 +到这里为止,全局堆就被正确初始化了。在全局堆被初始化之后,我们就可以使用`GET_HEAP`方法。至于这个函数的实现和使用,我们将在后续的章节中看到。 检查CPU类型 -------------------------------------------------------------------------------- From 6eee3cc510739b47e20752dbd77298309faec7a0 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 11:00:52 -0500 Subject: [PATCH 090/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 4ea7049..84713ab 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -362,7 +362,7 @@ ENDPROC(memset) 检查CPU类型 -------------------------------------------------------------------------------- -在堆栈初始化之后,内核代码通过调用[arch/x86/boot/cpu.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/cpu.c)提供的`validate_cpu`方法检查CPU类型以确定系统是否能够在当前的CPU上运行。 +在堆栈初始化之后,内核代码通过调用[arch/x86/boot/cpu.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/cpu.c)提供的`validate_cpu`方法检查CPU以确定系统是否能够在当前的CPU上运行。 `validate_cpu`调用了[`check_cpu`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/cpucheck.c#L102)方法得到当前系统的CPU级别,并且和系统预设的最低CPU级别进行比较。如果不满足条件,则不允许系统运行。 From b56bea605fa10031e49d74a4d12ea5278062e0e1 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 11:01:33 -0500 Subject: [PATCH 091/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 84713ab..3dcbe6e 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -362,7 +362,7 @@ ENDPROC(memset) 检查CPU类型 -------------------------------------------------------------------------------- -在堆栈初始化之后,内核代码通过调用[arch/x86/boot/cpu.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/cpu.c)提供的`validate_cpu`方法检查CPU以确定系统是否能够在当前的CPU上运行。 +在堆栈初始化之后,内核代码通过调用[arch/x86/boot/cpu.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/cpu.c)提供的`validate_cpu`方法检查CPU级别以确定系统是否能够在当前的CPU上运行。 `validate_cpu`调用了[`check_cpu`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/cpucheck.c#L102)方法得到当前系统的CPU级别,并且和系统预设的最低CPU级别进行比较。如果不满足条件,则不允许系统运行。 @@ -377,7 +377,7 @@ if (cpu_level < req_level) { } ``` -`check_cpu`方法做了大量的检测和设置工作,下面就简单介绍一些:1)检查cpu标志,如果cpu是64位cpu,那么就设置[long mode](http://en.wikipedia.org/wiki/Long_mode), 2) 检查CPU的制造商,根据制造商的不同,设置不同的CPU选项。比如对于AMD出厂的cpu,如果不支持SSE+SSE2,那么就禁止这些选项。 +除此之外,`check_cpu`方法还做了大量的其他检测和设置工作,下面就简单介绍一些:1)检查cpu标志,如果cpu是64位cpu,那么就设置[long mode](http://en.wikipedia.org/wiki/Long_mode), 2) 检查CPU的制造商,根据制造商的不同,设置不同的CPU选项。比如对于AMD出厂的cpu,如果不支持SSE+SSE2,那么就禁止这些选项。 内存侦测 -------------------------------------------------------------------------------- From a3888d5dd2ceb6dd43b4c2527739ad2a3564bc7e Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 11:02:11 -0500 Subject: [PATCH 092/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 3dcbe6e..8980a3b 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -382,7 +382,7 @@ if (cpu_level < req_level) { 内存侦测 -------------------------------------------------------------------------------- -接下来,内核调用`detect_memory`方法进行内存侦测,以得到系统当前内存的使用分布。该方法是用多种编程接口,包括`0xe820`,`0xe801`和`0x88`,进行内存侦测。在这里我们只介绍[arch/x86/boot/memory.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/memory.c)中提供的`detect_memory_e820`方法。 +接下来,内核调用`detect_memory`方法进行内存侦测,以得到系统当前内存的使用分布。该方法使用多种编程接口,包括`0xe820`,`0xe801`和`0x88`,进行内存侦测。在这里我们只介绍[arch/x86/boot/memory.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/memory.c)中提供的`detect_memory_e820`方法。 该方法首先调用`initregs`方法初始化`biosregs`数据结构,然后向该数据结构填入`0xe820`编程接口所要求的参数: From f211f6571a11dec0f5ec601daa6e7a957bf91a3b Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 11:03:32 -0500 Subject: [PATCH 093/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 8980a3b..d52db9d 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -400,7 +400,7 @@ if (cpu_level < req_level) { * `es:di` 包含数据缓冲区的地址 * `ebx` 必须为0. -接下来就是通过一个循环来收集内存信息了。每个循环都开始于一个`0x15`中断调用,这个中断调用返回地址分配表中的一项,接着程序将返回的`ebx`设置`biosregs`数据结构中进行下一次的`0x15`中断调用。那么循环什么时候结束呢?直到`0x15`调用返回的eflags包含标志`X86_EFLAGS_CF`: +接下来就是通过一个循环来收集内存信息了。每个循环都开始于一个`0x15`中断调用,这个中断调用返回地址分配表中的一项,接着程序将返回的`ebx`设置到`biosregs`数据结构中,然后进行下一次的`0x15`中断调用。那么循环什么时候结束呢?直到`0x15`调用返回的eflags包含标志`X86_EFLAGS_CF`: ```C intcall(0x15, &ireg, &oreg); From 9dce88088220ede4b85bc815879bf17849d3636d Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 11:04:12 -0500 Subject: [PATCH 094/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index d52db9d..9ffd210 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -407,7 +407,7 @@ if (cpu_level < req_level) { ireg.ebx = oreg.ebx; ``` -在循环结束之后,整个内存非陪标中的数据将被写入到`e820entry`数组中,这个数组的每个元素包含下面3个信息: +在循环结束之后,整个内存分配信息将被写入到`e820entry`数组中,这个数组的每个元素包含下面3个信息: * 内存段的起始地址 * 内存段的大小 From d00db14b129caff17a4d541a9c59d98fb6c20519 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 11:05:00 -0500 Subject: [PATCH 095/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 9ffd210..28217c8 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -428,7 +428,7 @@ if (cpu_level < req_level) { 键盘初始化 -------------------------------------------------------------------------------- -接下来内核调用[`keyboard_init()`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L65) 方法进行键盘初始化操作。 首先,方法调用`initregs`初始化寄存器结构,然后调用`0x16`中断[0x16](http://www.ctyme.com/intr/rb-1756.htm) 来获取键盘状态。 +接下来内核调用[`keyboard_init()`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L65) 方法进行键盘初始化操作。 首先,方法调用`initregs`初始化寄存器结构,然后调用[0x16](http://www.ctyme.com/intr/rb-1756.htm)中断 来获取键盘状态。 ```c initregs(&ireg); From ab393a51ce1ab31804f4e6eb4a0b7274d3f786af Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 11:05:07 -0500 Subject: [PATCH 096/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 28217c8..2a05078 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -428,7 +428,7 @@ if (cpu_level < req_level) { 键盘初始化 -------------------------------------------------------------------------------- -接下来内核调用[`keyboard_init()`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L65) 方法进行键盘初始化操作。 首先,方法调用`initregs`初始化寄存器结构,然后调用[0x16](http://www.ctyme.com/intr/rb-1756.htm)中断 来获取键盘状态。 +接下来内核调用[`keyboard_init()`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L65) 方法进行键盘初始化操作。 首先,方法调用`initregs`初始化寄存器结构,然后调用[0x16](http://www.ctyme.com/intr/rb-1756.htm)中断来获取键盘状态。 ```c initregs(&ireg); From 266427b618a88d4a5731cb96b75fbc8752403445 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 11:05:29 -0500 Subject: [PATCH 097/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 2a05078..77be78a 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -437,7 +437,7 @@ if (cpu_level < req_level) { boot_params.kbd_status = oreg.al; ``` -在获取了键盘状态之后,代码再次调用`0x16`中断[0x16](http://www.ctyme.com/intr/rb-1757.htm) 来设置键盘的按键检测频率。 +在获取了键盘状态之后,代码再次调用[0x16](http://www.ctyme.com/intr/rb-1757.htm)中断来设置键盘的按键检测频率。 ```c ireg.ax = 0x0305; /* Set keyboard repeat rate */ From a9e1122d7916947b0f7c5d2b54f3cd3c737d6a86 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 11:06:03 -0500 Subject: [PATCH 098/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 77be78a..347673e 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -447,7 +447,7 @@ if (cpu_level < req_level) { 系统参数查询 -------------------------------------------------------------------------------- -接下来内核将进行一系列的参数查询。我们在这里将不深入介绍所有这些查询,我们将在后续章节中在进行详细介绍。在这里我们将简单介绍一下这些函数: +接下来内核将进行一系列的参数查询。我们在这里将不深入介绍所有这些查询,我们将在后续章节中在进行详细介绍。在这里我们将简单介绍一些系统参数查询: [query_mca](https://github.com/torvalds/linux/blob/master/arch/x86/boot/mca.c#L18) 方法调用`0x15`中断[0x15](http://www.ctyme.com/intr/rb-1594.htm)来获取机器的型号信息,BIOS版本以及其他一些硬件相关的属性: From 05f1680b163b631e188e760d6dd2726888cf28a3 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 11:06:17 -0500 Subject: [PATCH 099/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 347673e..304c6d2 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -449,7 +449,7 @@ if (cpu_level < req_level) { 接下来内核将进行一系列的参数查询。我们在这里将不深入介绍所有这些查询,我们将在后续章节中在进行详细介绍。在这里我们将简单介绍一些系统参数查询: -[query_mca](https://github.com/torvalds/linux/blob/master/arch/x86/boot/mca.c#L18) 方法调用`0x15`中断[0x15](http://www.ctyme.com/intr/rb-1594.htm)来获取机器的型号信息,BIOS版本以及其他一些硬件相关的属性: +[query_mca](https://github.com/torvalds/linux/blob/master/arch/x86/boot/mca.c#L18) 方法调用[0x15](http://www.ctyme.com/intr/rb-1594.htm)中断来获取机器的型号信息,BIOS版本以及其他一些硬件相关的属性: ```c int query_mca(void) From 71a1f4893b1277da759cf70753478fea89ca9231 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 11:07:15 -0500 Subject: [PATCH 100/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 304c6d2..de77c3e 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -475,7 +475,7 @@ int query_mca(void) } ``` -这个方法设置`ah`寄存器的值为`0xc0`,然后调用`0x15` BIOS中断。中断返回之后代码检查 [carry flag](http://en.wikipedia.org/wiki/Carry_flag)。如果它被置位,说明BIOS不支持(**MCA**)[https://en.wikipedia.org/wiki/Micro_Channel_architecture]。如果CF被设置成0,那么`ES:BX`指向系统信息表。这个表的内容如下所示: +这个方法设置`ah`寄存器的值为`0xc0`,然后调用`0x15` BIOS中断。中断返回之后代码检查 [carry flag](http://en.wikipedia.org/wiki/Carry_flag)。如果它被置位,说明BIOS不支持[**MCA**](https://en.wikipedia.org/wiki/Micro_Channel_architecture)。如果CF被设置成0,那么`ES:BX`指向系统信息表。这个表的内容如下所示: ``` Offset Size Description From 5304907b487a34f9077d11ba498791bef17a3763 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 11:07:53 -0500 Subject: [PATCH 101/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index de77c3e..d5dfce8 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -515,7 +515,7 @@ static inline void set_fs(u16 seg) 在[boot.h](https://github.com/torvalds/linux/blob/master/arch/x86/boot/boot.h) 存在很多类似于`set_fs`的方法, 比如 `set_gs`。 -在`query_mca`的最后,代码将`es:bx`只想的内存地址拷贝到`boot_params.sys_desc_table`。 +在`query_mca`的最后,代码将`es:bx`指向的内存地址拷贝到`boot_params.sys_desc_table`。 接下来,调用`query_ist`方法获取[Intel SpeedStep](http://en.wikipedia.org/wiki/SpeedStep)信息。这个方法首先检查CPU类型,然后调用`0x15`中断获得这个信息并放入`boot_params`中。 From 5db0f4bea604286b4f297b279f1ba8d98a9753d1 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 11:08:03 -0500 Subject: [PATCH 102/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index d5dfce8..6041106 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -515,7 +515,7 @@ static inline void set_fs(u16 seg) 在[boot.h](https://github.com/torvalds/linux/blob/master/arch/x86/boot/boot.h) 存在很多类似于`set_fs`的方法, 比如 `set_gs`。 -在`query_mca`的最后,代码将`es:bx`指向的内存地址拷贝到`boot_params.sys_desc_table`。 +在`query_mca`的最后,代码将`es:bx`指向的内存地址的拷贝到`boot_params.sys_desc_table`。 接下来,调用`query_ist`方法获取[Intel SpeedStep](http://en.wikipedia.org/wiki/SpeedStep)信息。这个方法首先检查CPU类型,然后调用`0x15`中断获得这个信息并放入`boot_params`中。 From 488a44000d5f290a404847208a5017eb8b681a8d Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 11:08:12 -0500 Subject: [PATCH 103/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 6041106..d5dfce8 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -515,7 +515,7 @@ static inline void set_fs(u16 seg) 在[boot.h](https://github.com/torvalds/linux/blob/master/arch/x86/boot/boot.h) 存在很多类似于`set_fs`的方法, 比如 `set_gs`。 -在`query_mca`的最后,代码将`es:bx`指向的内存地址的拷贝到`boot_params.sys_desc_table`。 +在`query_mca`的最后,代码将`es:bx`指向的内存地址拷贝到`boot_params.sys_desc_table`。 接下来,调用`query_ist`方法获取[Intel SpeedStep](http://en.wikipedia.org/wiki/SpeedStep)信息。这个方法首先检查CPU类型,然后调用`0x15`中断获得这个信息并放入`boot_params`中。 From f11a2d7325f88c40f1642e2c0fa1c6fd33890703 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 11:08:35 -0500 Subject: [PATCH 104/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index d5dfce8..026d29b 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -515,7 +515,7 @@ static inline void set_fs(u16 seg) 在[boot.h](https://github.com/torvalds/linux/blob/master/arch/x86/boot/boot.h) 存在很多类似于`set_fs`的方法, 比如 `set_gs`。 -在`query_mca`的最后,代码将`es:bx`指向的内存地址拷贝到`boot_params.sys_desc_table`。 +在`query_mca`的最后,代码将`es:bx`指向的内存地址的内容拷贝到`boot_params.sys_desc_table`。 接下来,调用`query_ist`方法获取[Intel SpeedStep](http://en.wikipedia.org/wiki/SpeedStep)信息。这个方法首先检查CPU类型,然后调用`0x15`中断获得这个信息并放入`boot_params`中。 From 38d3723b6a147cb2a55442778095c91ce562c994 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 11:08:36 -0500 Subject: [PATCH 105/153] Update Booting/linux-bootstrap-2.md From e4dce9005cdf5b43ae133cb8173ad19e8586c3b5 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 11:10:32 -0500 Subject: [PATCH 106/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 026d29b..daeb8b5 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -517,13 +517,13 @@ static inline void set_fs(u16 seg) 在`query_mca`的最后,代码将`es:bx`指向的内存地址的内容拷贝到`boot_params.sys_desc_table`。 -接下来,调用`query_ist`方法获取[Intel SpeedStep](http://en.wikipedia.org/wiki/SpeedStep)信息。这个方法首先检查CPU类型,然后调用`0x15`中断获得这个信息并放入`boot_params`中。 +接下来,内核调用`query_ist`方法获取[Intel SpeedStep](http://en.wikipedia.org/wiki/SpeedStep)信息。这个方法首先检查CPU类型,然后调用`0x15`中断获得这个信息并放入`boot_params`中。 接下来,内核会调用[query_apm_bios](https://github.com/torvalds/linux/blob/master/arch/x86/boot/apm.c#L21) 方法从BIOS获得 [高级电源管理](http://en.wikipedia.org/wiki/Advanced_Power_Management) 信息。`query_apm_bios`也是调用`0x15`中断,只不过将`ah`设置成0x53以获得APM设置。中断调用返回之后,代码将检查`bx`和`cx`的值,如果`bx`不是`0x504d` ( PM 标记 ),或者`cx`不是0x02 (0x02,表示支持保护模式),那么代码直接返回错误。否则,将进行下面的步骤。 接下来,代码使用`ax = 0x5304`来调用`0x15`中断,以断开`APM`接口;然后使用`ax = 0x5303`调用`0x15`中断,以连接到保护模式接口;最后使用`ax = 0x5300`调用`0x15`中断再次获取APM设置,然后将信息写入`boot_params.apm_bios_info`。 -只有在`CONFIG_APM`或者`CONFIG_APM_MODULE`被设置的情况下,`query_apm_bios`方法才会被调用: +需要注意的是,只有在`CONFIG_APM`或者`CONFIG_APM_MODULE`被设置的情况下,`query_apm_bios`方法才会被调用: ```C #if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE) From 383f6b00357aca6f2de7da96e44b9b31dfc90b3f Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 11:11:30 -0500 Subject: [PATCH 107/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index daeb8b5..1da4029 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -549,7 +549,7 @@ for (devno = 0x80; devno < 0x80+EDD_MBR_SIG_MAX; devno++) { ... ``` -在代码中 `0x80`是第一块硬盘,`EDD_MBR_SIG_MAX`是一个宏,值为16。代码把获得的信息放入数组[edd_info](https://github.com/torvalds/linux/blob/master/include/uapi/linux/edd.h#L172)中。`get_edd_info`方法通过调用`0x13`中断调用(设置`ah = 0x41` ) 来检查EDD是否被硬盘支持。如果EDD被支持,代码将再次调用`0x13`中断,在这次调用中`ah = 0x48`,并且`si`只想缓冲区地址。EDD信息将被保存到`si`指向的缓冲区。 +在代码中 `0x80`是第一块硬盘,`EDD_MBR_SIG_MAX`是一个宏,值为16。代码把获得的信息放入数组[edd_info](https://github.com/torvalds/linux/blob/master/include/uapi/linux/edd.h#L172)中。`get_edd_info`方法通过调用`0x13`中断调用(设置`ah = 0x41` ) 来检查EDD是否被硬盘支持。如果EDD被支持,代码将再次调用`0x13`中断,在这次调用中`ah = 0x48`,并且`si`指向一个数据缓冲区地址。中断调用之后,EDD信息将被保存到`si`指向的缓冲区。 结束语 -------------------------------------------------------------------------------- From ecc6b6f860a00bb663490af5afe0bcde76825b26 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 11:11:50 -0500 Subject: [PATCH 108/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 1da4029..1f7c63c 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -554,7 +554,7 @@ for (devno = 0x80; devno < 0x80+EDD_MBR_SIG_MAX; devno++) { 结束语 -------------------------------------------------------------------------------- -本章到此就结束了,在下一章我们将讲解显示模式设置,以及在进入保护模式之前的其他准备工作,最后我们将成功进入保护模式。 +本章到此就结束了,在下一章我们将讲解显示模式设置,以及在进入保护模式之前的其他准备工作,在下一章的最后我们将成功进入保护模式。 如果你有任何的问题或者建议,你可以留言,也可以直接发消息给我[twitter](https://twitter.com/0xAX). From 59893a4477eca9fd162eac495c4cf459318db1ad Mon Sep 17 00:00:00 2001 From: hailin Date: Wed, 24 Feb 2016 11:14:09 -0500 Subject: [PATCH 109/153] review the word, fix typo --- Booting/linux-bootstrap-2.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 Booting/linux-bootstrap-2.md diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md old mode 100644 new mode 100755 From b1b28246771afeac0292642138e58e797342577c Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 11:31:26 -0500 Subject: [PATCH 110/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) mode change 100755 => 100644 Booting/linux-bootstrap-2.md diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md old mode 100755 new mode 100644 index 1f7c63c..ae0a5c3 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -519,7 +519,7 @@ static inline void set_fs(u16 seg) 接下来,内核调用`query_ist`方法获取[Intel SpeedStep](http://en.wikipedia.org/wiki/SpeedStep)信息。这个方法首先检查CPU类型,然后调用`0x15`中断获得这个信息并放入`boot_params`中。 -接下来,内核会调用[query_apm_bios](https://github.com/torvalds/linux/blob/master/arch/x86/boot/apm.c#L21) 方法从BIOS获得 [高级电源管理](http://en.wikipedia.org/wiki/Advanced_Power_Management) 信息。`query_apm_bios`也是调用`0x15`中断,只不过将`ah`设置成0x53以获得APM设置。中断调用返回之后,代码将检查`bx`和`cx`的值,如果`bx`不是`0x504d` ( PM 标记 ),或者`cx`不是0x02 (0x02,表示支持保护模式),那么代码直接返回错误。否则,将进行下面的步骤。 +接下来,内核会调用[query_apm_bios](https://github.com/torvalds/linux/blob/master/arch/x86/boot/apm.c#L21) 方法从BIOS获得 [高级电源管理](http://en.wikipedia.org/wiki/Advanced_Power_Management) 信息。`query_apm_bios`也是调用`0x15`中断,只不过将`ah`设置成0x53以得APM设置。中断调用返回之后,代码将检查`bx`和`cx`的值,如果`bx`不是`0x504d` ( PM 标记 ),或者`cx`不是0x02 (0x02,表示支持保护模式),那么代码直接返回错误。否则,将进行下面的步骤。 接下来,代码使用`ax = 0x5304`来调用`0x15`中断,以断开`APM`接口;然后使用`ax = 0x5303`调用`0x15`中断,以连接到保护模式接口;最后使用`ax = 0x5300`调用`0x15`中断再次获取APM设置,然后将信息写入`boot_params.apm_bios_info`。 From 74ad3380a7a06912c902e8becf2a5f9c2aa8ba47 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 11:33:29 -0500 Subject: [PATCH 111/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index ae0a5c3..a23d7ae 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -382,7 +382,7 @@ if (cpu_level < req_level) { 内存侦测 -------------------------------------------------------------------------------- -接下来,内核调用`detect_memory`方法进行内存侦测,以得到系统当前内存的使用分布。该方法使用多种编程接口,包括`0xe820`,`0xe801`和`0x88`,进行内存侦测。在这里我们只介绍[arch/x86/boot/memory.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/memory.c)中提供的`detect_memory_e820`方法。 +接下来,内核调用`detect_memory`方法进行内存侦测,以得到系统当前内存的使用分布。该方法使用多种编程接口,包括`0xe820`(获取全部内存分配),`0xe801`和`0x88`(获取临近内存大小),进行内存侦测。在这里我们只介绍[arch/x86/boot/memory.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/memory.c)中提供的`detect_memory_e820`方法。 该方法首先调用`initregs`方法初始化`biosregs`数据结构,然后向该数据结构填入`0xe820`编程接口所要求的参数: From 6bd7ff8cd7e668c0c73d9c274169df547a8a0a3f Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 11:33:53 -0500 Subject: [PATCH 112/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index a23d7ae..c1c27c5 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -379,10 +379,10 @@ if (cpu_level < req_level) { 除此之外,`check_cpu`方法还做了大量的其他检测和设置工作,下面就简单介绍一些:1)检查cpu标志,如果cpu是64位cpu,那么就设置[long mode](http://en.wikipedia.org/wiki/Long_mode), 2) 检查CPU的制造商,根据制造商的不同,设置不同的CPU选项。比如对于AMD出厂的cpu,如果不支持SSE+SSE2,那么就禁止这些选项。 -内存侦测 +内存分布侦测 -------------------------------------------------------------------------------- -接下来,内核调用`detect_memory`方法进行内存侦测,以得到系统当前内存的使用分布。该方法使用多种编程接口,包括`0xe820`(获取全部内存分配),`0xe801`和`0x88`(获取临近内存大小),进行内存侦测。在这里我们只介绍[arch/x86/boot/memory.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/memory.c)中提供的`detect_memory_e820`方法。 +接下来,内核调用`detect_memory`方法进行内存侦测,以得到系统当前内存的使用分布。该方法使用多种编程接口,包括`0xe820`(获取全部内存分配),`0xe801`和`0x88`(获取临近内存大小),进行内存分布侦测。在这里我们只介绍[arch/x86/boot/memory.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/memory.c)中提供的`detect_memory_e820`方法。 该方法首先调用`initregs`方法初始化`biosregs`数据结构,然后向该数据结构填入`0xe820`编程接口所要求的参数: From d9b00ebbe6728372a545b4b213070a7129a4e487 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 11:43:09 -0500 Subject: [PATCH 113/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index c1c27c5..7071d1f 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -575,4 +575,5 @@ for (devno = 0x80; devno < 0x80+EDD_MBR_SIG_MAX; devno++) { * [APM](https://en.wikipedia.org/wiki/Advanced_Power_Management) * [EDD specification](http://www.t13.org/documents/UploadedDocuments/docs2004/d1572r3-EDD3.pdf) * [TLDP documentation for Linux Boot Process](http://www.tldp.org/HOWTO/Linux-i386-Boot-Code-HOWTO/setup.html) (old) -* [Previous Part](linux-bootstrap-1.md) \ No newline at end of file +* [Previous Part](linux-bootstrap-1.md) +* [BIOS Interrupt](http://wiki.osdev.org/BIOS) \ No newline at end of file From 83ba78a7dd1d0c86fc9f22443870fa6bd23fead9 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 11:43:42 -0500 Subject: [PATCH 114/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 7071d1f..dba4856 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -519,7 +519,7 @@ static inline void set_fs(u16 seg) 接下来,内核调用`query_ist`方法获取[Intel SpeedStep](http://en.wikipedia.org/wiki/SpeedStep)信息。这个方法首先检查CPU类型,然后调用`0x15`中断获得这个信息并放入`boot_params`中。 -接下来,内核会调用[query_apm_bios](https://github.com/torvalds/linux/blob/master/arch/x86/boot/apm.c#L21) 方法从BIOS获得 [高级电源管理](http://en.wikipedia.org/wiki/Advanced_Power_Management) 信息。`query_apm_bios`也是调用`0x15`中断,只不过将`ah`设置成0x53以得APM设置。中断调用返回之后,代码将检查`bx`和`cx`的值,如果`bx`不是`0x504d` ( PM 标记 ),或者`cx`不是0x02 (0x02,表示支持保护模式),那么代码直接返回错误。否则,将进行下面的步骤。 +接下来,内核会调用[query_apm_bios](https://github.com/torvalds/linux/blob/master/arch/x86/boot/apm.c#L21) 方法从BIOS获得 [高级电源管理](http://en.wikipedia.org/wiki/Advanced_Power_Management) 信息。`query_apm_bios`也是调用`0x15`中断,只不过将`ax`设置成0x5300以得APM设置信息。中断调用返回之后,代码将检查`bx`和`cx`的值,如果`bx`不是`0x504d` ( PM 标记 ),或者`cx`不是0x02 (0x02,表示支持保护模式),那么代码直接返回错误。否则,将进行下面的步骤。 接下来,代码使用`ax = 0x5304`来调用`0x15`中断,以断开`APM`接口;然后使用`ax = 0x5303`调用`0x15`中断,以连接到保护模式接口;最后使用`ax = 0x5300`调用`0x15`中断再次获取APM设置,然后将信息写入`boot_params.apm_bios_info`。 From f473b96e1ca0725837853375501af39de08b93ee Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 11:44:23 -0500 Subject: [PATCH 115/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index dba4856..1447c32 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -521,7 +521,7 @@ static inline void set_fs(u16 seg) 接下来,内核会调用[query_apm_bios](https://github.com/torvalds/linux/blob/master/arch/x86/boot/apm.c#L21) 方法从BIOS获得 [高级电源管理](http://en.wikipedia.org/wiki/Advanced_Power_Management) 信息。`query_apm_bios`也是调用`0x15`中断,只不过将`ax`设置成0x5300以得APM设置信息。中断调用返回之后,代码将检查`bx`和`cx`的值,如果`bx`不是`0x504d` ( PM 标记 ),或者`cx`不是0x02 (0x02,表示支持保护模式),那么代码直接返回错误。否则,将进行下面的步骤。 -接下来,代码使用`ax = 0x5304`来调用`0x15`中断,以断开`APM`接口;然后使用`ax = 0x5303`调用`0x15`中断,以连接到保护模式接口;最后使用`ax = 0x5300`调用`0x15`中断再次获取APM设置,然后将信息写入`boot_params.apm_bios_info`。 +接下来,代码使用`ax = 0x5304`来调用`0x15`中断,以断开`APM`接口;然后使用`ax = 0x5303`调用`0x15`中断,使用32位接口重新连接`APM`;最后使用`ax = 0x5300`调用`0x15`中断再次获取APM设置,然后将信息写入`boot_params.apm_bios_info`。 需要注意的是,只有在`CONFIG_APM`或者`CONFIG_APM_MODULE`被设置的情况下,`query_apm_bios`方法才会被调用: From 30b2dc880fc6a522d664cce17fab47ea37e21ee5 Mon Sep 17 00:00:00 2001 From: hailin Date: Wed, 24 Feb 2016 11:44:49 -0500 Subject: [PATCH 116/153] 1) add BIOS inte link; 2) update APM part --- Booting/linux-bootstrap-2.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 Booting/linux-bootstrap-2.md diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md old mode 100644 new mode 100755 From 972604850cba9f777525ae68273cdffeb05a6866 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 11:47:03 -0500 Subject: [PATCH 117/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) mode change 100755 => 100644 Booting/linux-bootstrap-2.md diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md old mode 100755 new mode 100644 index 1447c32..87b26a0 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -519,7 +519,7 @@ static inline void set_fs(u16 seg) 接下来,内核调用`query_ist`方法获取[Intel SpeedStep](http://en.wikipedia.org/wiki/SpeedStep)信息。这个方法首先检查CPU类型,然后调用`0x15`中断获得这个信息并放入`boot_params`中。 -接下来,内核会调用[query_apm_bios](https://github.com/torvalds/linux/blob/master/arch/x86/boot/apm.c#L21) 方法从BIOS获得 [高级电源管理](http://en.wikipedia.org/wiki/Advanced_Power_Management) 信息。`query_apm_bios`也是调用`0x15`中断,只不过将`ax`设置成0x5300以得APM设置信息。中断调用返回之后,代码将检查`bx`和`cx`的值,如果`bx`不是`0x504d` ( PM 标记 ),或者`cx`不是0x02 (0x02,表示支持保护模式),那么代码直接返回错误。否则,将进行下面的步骤。 +接下来,内核会调用[query_apm_bios](https://github.com/torvalds/linux/blob/master/arch/x86/boot/apm.c#L21) 方法从BIOS获得 [高级电源管理](http://en.wikipedia.org/wiki/Advanced_Power_Management) 信息。`query_apm_bios`也是调用`0x15`中断,只不过将`ax`设置成`0x5300`以得APM设置信息。中断调用返回之后,代码将检查`bx`和`cx`的值,如果`bx`不是`0x504d` ( PM 标记 ),或者`cx`不是0x02 (0x02,表示支持保护模式),那么代码直接返回错误。否则,将进行下面的步骤。 接下来,代码使用`ax = 0x5304`来调用`0x15`中断,以断开`APM`接口;然后使用`ax = 0x5303`调用`0x15`中断,使用32位接口重新连接`APM`;最后使用`ax = 0x5300`调用`0x15`中断再次获取APM设置,然后将信息写入`boot_params.apm_bios_info`。 From ec64110375b7f1f74b9808325b5d50d3e9d4f789 Mon Sep 17 00:00:00 2001 From: hailin Date: Wed, 24 Feb 2016 11:47:20 -0500 Subject: [PATCH 118/153] update some text presentation --- Booting/linux-bootstrap-2.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 Booting/linux-bootstrap-2.md diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md old mode 100644 new mode 100755 From 4c10a2c2d22cd5ca224b628f2f9c71f5d51d83f0 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 11:53:31 -0500 Subject: [PATCH 119/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) mode change 100755 => 100644 Booting/linux-bootstrap-2.md diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md old mode 100755 new mode 100644 index 87b26a0..fffc7a6 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -75,7 +75,7 @@ lgdt gdt 1. Limit[20位]被保存在上述内存结构的0-15和16-19位。根据上述内存结构中`G`位的设置,这20位内存定义的内存长度是不一样的。下面是一些具体的例子: - * 如果`G`= 0, 并且Limit = 0, 那么表示段长度是1 byte + * 如果`G` = 0, 并且Limit = 0, 那么表示段长度是1 byte * 如果`G` = 1, 并且Limit = 0, 那么表示段长度是4K bytes * 如果`G` = 0,并且Limit = 0xfffff,那么表示段长度是1M bytes * 如果`G` = 1,并且Limit = 0xfffff,那么表示段长度是4G bytes From a143e3f217626aa435f1fd7baa2b58b1c4e441ee Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 11:54:02 -0500 Subject: [PATCH 120/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index fffc7a6..f87ddff 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -549,7 +549,7 @@ for (devno = 0x80; devno < 0x80+EDD_MBR_SIG_MAX; devno++) { ... ``` -在代码中 `0x80`是第一块硬盘,`EDD_MBR_SIG_MAX`是一个宏,值为16。代码把获得的信息放入数组[edd_info](https://github.com/torvalds/linux/blob/master/include/uapi/linux/edd.h#L172)中。`get_edd_info`方法通过调用`0x13`中断调用(设置`ah = 0x41` ) 来检查EDD是否被硬盘支持。如果EDD被支持,代码将再次调用`0x13`中断,在这次调用中`ah = 0x48`,并且`si`指向一个数据缓冲区地址。中断调用之后,EDD信息将被保存到`si`指向的缓冲区。 +在代码中 `0x80`是第一块硬盘,`EDD_MBR_SIG_MAX`是一个宏,值为16。代码把获得的信息放入数组[edd_info](https://github.com/torvalds/linux/blob/master/include/uapi/linux/edd.h#L172)中。`get_edd_info`方法通过调用`0x13`中断调用(设置`ah = 0x41` ) 来检查EDD是否被硬盘支持。如果EDD被支持,代码将再次调用`0x13`中断,在这次调用中`ah = 0x48`,并且`si`指向一个数据缓冲区地址。中断调用之后,EDD信息将被保存到`si`指向的缓冲区地址。 结束语 -------------------------------------------------------------------------------- From c32c13db76a4c1614378e4b4213b2e4794909d33 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 11:55:03 -0500 Subject: [PATCH 121/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index f87ddff..3806fa3 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -45,13 +45,13 @@ PhysicalAddress = Segment * 16 + Offset 在保护模式中,内存段的定义和实模式完全不同。在保护模式中,每个内存段不再是64K大小,段的大小和起始位置是通过一个叫做`段描述符`的数据结构进行描述。所有内存段的段描述符存储在一个叫做`全局描述符表`(GDT)的内存结构中。 -`全局描述符表`这个内存数据结构在内存中的位置并不是固定的,它的地址保存在一个特殊寄存器`GDTR`中。在后面的章节中,我们将在Linux内核代码中看到全局描述符表的地址是如何被保存到`GDTR`中的。具体的汇编代码看起来是这样的: +`全局描述符表`这个内存数据结构在内存中的位置并不是固定的,它的地址保存在一个特殊寄存器 `GDTR` 中。在后面的章节中,我们将在Linux内核代码中看到全局描述符表的地址是如何被保存到 `GDTR` 中的。具体的汇编代码看起来是这样的: ```assembly lgdt gdt ``` -`lgdt`汇编代码将把全局描述符表的基地址和大小保存到`GDTR`寄存器中。`GRTD`是一个48位的寄存器,这个寄存器中的保存了2部分的内容: +`lgdt` 汇编代码将把全局描述符表的基地址和大小保存到`GDTR`寄存器中。`GRTD`是一个48位的寄存器,这个寄存器中的保存了2部分的内容: * 全局描述符表的大小 (16位) * 全局描述符表的基址 (32位) From e30a774f2f0d97397660375fad9e6eadfc31f834 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 11:55:10 -0500 Subject: [PATCH 122/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 3806fa3..2b66bd4 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -51,7 +51,7 @@ PhysicalAddress = Segment * 16 + Offset lgdt gdt ``` -`lgdt` 汇编代码将把全局描述符表的基地址和大小保存到`GDTR`寄存器中。`GRTD`是一个48位的寄存器,这个寄存器中的保存了2部分的内容: +`lgdt` 汇编代码将把全局描述符表的基地址和大小保存到 `GDTR` 寄存器中。`GRTD` 是一个48位的寄存器,这个寄存器中的保存了2部分的内容: * 全局描述符表的大小 (16位) * 全局描述符表的基址 (32位) From 7b9acb48549d94693d4124b4d45e0bda567f65a8 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 11:57:37 -0500 Subject: [PATCH 123/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 2b66bd4..a165fea 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -51,7 +51,7 @@ PhysicalAddress = Segment * 16 + Offset lgdt gdt ``` -`lgdt` 汇编代码将把全局描述符表的基地址和大小保存到 `GDTR` 寄存器中。`GRTD` 是一个48位的寄存器,这个寄存器中的保存了2部分的内容: +`lgdt` 汇编代码将把全局描述符表的基地址和大小保存到 `GDTR` 寄存器中。`GDTR` 是一个48位的寄存器,这个寄存器中的保存了2部分的内容: * 全局描述符表的大小 (16位) * 全局描述符表的基址 (32位) @@ -169,7 +169,7 @@ lgdt gdt 当代码要从实模式进入保护模式的时候,需要执行下面的操作: * 禁止中断发生 -* 使用命令`lgdt`将GDT表装入`GRTD`寄存器 +* 使用命令 `lgdt` 将GDT表装入 `GDTR` 寄存器 * 设置CR0寄存器的PE位为1,是CPU进入保护模式 * 跳转开始执行保护模式代码 From 6282b4033c48b7976e23e999b3f507cd9841c9d1 Mon Sep 17 00:00:00 2001 From: hailin Date: Wed, 24 Feb 2016 11:58:45 -0500 Subject: [PATCH 124/153] update GDTR space --- Booting/linux-bootstrap-2.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 Booting/linux-bootstrap-2.md diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md old mode 100644 new mode 100755 From 7b3dd4f2f4682d283d6cbd53a99378b8c7e8027d Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 12:00:33 -0500 Subject: [PATCH 125/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) mode change 100755 => 100644 Booting/linux-bootstrap-2.md diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md old mode 100755 new mode 100644 index a165fea..a614776 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -184,11 +184,11 @@ lgdt gdt 这个函数将内核设置信息拷贝到`boot_params`结构的相应字段。大家可以在[arch/x86/include/uapi/asm/bootparam.h](https://github.com/torvalds/linux/blob/master/arch/x86/include/uapi/asm/bootparam.h#L113)找到`boot_params`结构的定义。 -1. 将[header.S](https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S#L281)中定义的`hdr`结构中的内容拷贝到`boot_params`结构的字段`struct setup_header hdr`中。 +1. 将[header.S](https://github.com/torvalds/linux/blob/master/arch/x86/boot/header.S#L281)中定义的 `hdr` 结构中的内容拷贝到 `boot_params` 结构的字段 `struct setup_header hdr` 中。 2. 如果内核是通过老的命令行协议运行起来的,那么就更新内核的命令行指针。 -这里需要注意的是拷贝`hdr`数据结构的`memcpy`函数不是C语言中的函数,而是定义在 [copy.S](https://github.com/torvalds/linux/blob/master/arch/x86/boot/copy.S)。让我们来具体分析一下这段代码: +这里需要注意的是拷贝 `hdr` 数据结构的 `memcpy` 函数不是C语言中的函数,而是定义在 [copy.S](https://github.com/torvalds/linux/blob/master/arch/x86/boot/copy.S)。让我们来具体分析一下这段代码: ```assembly GLOBAL(memcpy) From e00393d9c15d309b5c584194a993dde2187ffabf Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 12:02:07 -0500 Subject: [PATCH 126/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index a614776..b7716e1 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -208,7 +208,7 @@ GLOBAL(memcpy) ENDPROC(memcpy) ``` -在`copy.S`文件中,你可以看到所有的方法都开始于`GLOBAL`宏定义,而结束于`ENDPROC`宏定义。 +在`copy.S`文件中,你可以看到所有的方法都开始于 `GLOBAL` 宏定义,而结束于 `ENDPROC` 宏定义。 你可以在 [arch/x86/include/asm/linkage.h](https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/linkage.h)中找到`GLOBAL`宏定义。这个宏给代码段分配了一个名字标签,并且让这个名字全局可用。 From 0dd0d8e2d532d42cf119da3b9239a9148046a3eb Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 12:02:37 -0500 Subject: [PATCH 127/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index b7716e1..9684404 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -210,7 +210,7 @@ ENDPROC(memcpy) 在`copy.S`文件中,你可以看到所有的方法都开始于 `GLOBAL` 宏定义,而结束于 `ENDPROC` 宏定义。 -你可以在 [arch/x86/include/asm/linkage.h](https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/linkage.h)中找到`GLOBAL`宏定义。这个宏给代码段分配了一个名字标签,并且让这个名字全局可用。 +你可以在 [arch/x86/include/asm/linkage.h](https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/linkage.h)中找到 `GLOBAL` 宏定义。这个宏给代码段分配了一个名字标签,并且让这个名字全局可用。 ```assembly #define GLOBAL(name) \ @@ -218,7 +218,7 @@ ENDPROC(memcpy) name: ``` -你可以在[include/linux/linkage.h](https://github.com/torvalds/linux/blob/master/include/linux/linkage.h)中找到`ENDPROC`宏的定义。 这个宏通过`END(name)`代码标识了汇编函数的结束,同时将函数名输出,从而静态分析工具可以找到这个函数。 +你可以在[include/linux/linkage.h](https://github.com/torvalds/linux/blob/master/include/linux/linkage.h)中找到 `ENDPROC` 宏的定义。 这个宏通过`END(name)`代码标识了汇编函数的结束,同时将函数名输出,从而静态分析工具可以找到这个函数。 ```assembly #define ENDPROC(name) \ From 39fcbc9467599ef734e34cf59376c5b26884d462 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 12:03:09 -0500 Subject: [PATCH 128/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 9684404..f78162e 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -218,7 +218,7 @@ ENDPROC(memcpy) name: ``` -你可以在[include/linux/linkage.h](https://github.com/torvalds/linux/blob/master/include/linux/linkage.h)中找到 `ENDPROC` 宏的定义。 这个宏通过`END(name)`代码标识了汇编函数的结束,同时将函数名输出,从而静态分析工具可以找到这个函数。 +你可以在[include/linux/linkage.h](https://github.com/torvalds/linux/blob/master/include/linux/linkage.h)中找到 `ENDPROC` 宏的定义。 这个宏通过 `END(name)` 代码标识了汇编函数的结束,同时将函数名输出,从而静态分析工具可以找到这个函数。 ```assembly #define ENDPROC(name) \ @@ -226,7 +226,7 @@ ENDPROC(memcpy) END(name) ``` -`memcpy`的实现代码是很容易理解的。首先,代码将`si`和`di`寄存器的值压入堆栈进行保存,这么做的原因是因为后续的代码将修改`si`和`di`寄存器的值。`memcpy`函数(也包括其他定义在copy.s中的其他函数)使用了`fastcall`调用规则,意味着所有的函数调用参数是通过`ax`, `dx`, `cx`寄存器传入的,而不是传统的通过堆栈传入。因此在使用下面的代码调用`memcpy`函数的时候 +`memcpy` 的实现代码是很容易理解的。首先,代码将 `si` 和 `di` 寄存器的值压入堆栈进行保存,这么做的原因是因为后续的代码将修改`si`和`di`寄存器的值。`memcpy`函数(也包括其他定义在copy.s中的其他函数)使用了`fastcall`调用规则,意味着所有的函数调用参数是通过`ax`, `dx`, `cx`寄存器传入的,而不是传统的通过堆栈传入。因此在使用下面的代码调用`memcpy`函数的时候 ```c memcpy(&boot_params.hdr, &hdr, sizeof hdr); From fb2dfef16d4c6f3396b5c6f2e8a21e0674bbc130 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 12:03:42 -0500 Subject: [PATCH 129/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index f78162e..3905014 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -226,7 +226,7 @@ ENDPROC(memcpy) END(name) ``` -`memcpy` 的实现代码是很容易理解的。首先,代码将 `si` 和 `di` 寄存器的值压入堆栈进行保存,这么做的原因是因为后续的代码将修改`si`和`di`寄存器的值。`memcpy`函数(也包括其他定义在copy.s中的其他函数)使用了`fastcall`调用规则,意味着所有的函数调用参数是通过`ax`, `dx`, `cx`寄存器传入的,而不是传统的通过堆栈传入。因此在使用下面的代码调用`memcpy`函数的时候 +`memcpy` 的实现代码是很容易理解的。首先,代码将 `si` 和 `di` 寄存器的值压入堆栈进行保存,这么做的原因是因为后续的代码将修改 `si` 和 `di` 寄存器的值。`memcpy` 函数(也包括其他定义在copy.s中的其他函数)使用了 `fastcall` 调用规则,意味着所有的函数调用参数是通过 `ax`, `dx`, `cx`寄存器传入的,而不是传统的通过堆栈传入。因此在使用下面的代码调用`memcpy`函数的时候 ```c memcpy(&boot_params.hdr, &hdr, sizeof hdr); From c5dfbfc2ba93e417e50bf9ce935b93f5d3607392 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 12:03:49 -0500 Subject: [PATCH 130/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 3905014..b1d5425 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -226,7 +226,7 @@ ENDPROC(memcpy) END(name) ``` -`memcpy` 的实现代码是很容易理解的。首先,代码将 `si` 和 `di` 寄存器的值压入堆栈进行保存,这么做的原因是因为后续的代码将修改 `si` 和 `di` 寄存器的值。`memcpy` 函数(也包括其他定义在copy.s中的其他函数)使用了 `fastcall` 调用规则,意味着所有的函数调用参数是通过 `ax`, `dx`, `cx`寄存器传入的,而不是传统的通过堆栈传入。因此在使用下面的代码调用`memcpy`函数的时候 +`memcpy` 的实现代码是很容易理解的。首先,代码将 `si` 和 `di` 寄存器的值压入堆栈进行保存,这么做的原因是因为后续的代码将修改 `si` 和 `di` 寄存器的值。`memcpy` 函数(也包括其他定义在copy.s中的其他函数)使用了 `fastcall` 调用规则,意味着所有的函数调用参数是通过 `ax`, `dx`, `cx`寄存器传入的,而不是传统的通过堆栈传入。因此在使用下面的代码调用 `memcpy` 函数的时候 ```c memcpy(&boot_params.hdr, &hdr, sizeof hdr); From e642cfd2a396ba9259989f63b7078051efa67a3f Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 12:05:13 -0500 Subject: [PATCH 131/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index b1d5425..404c056 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -234,11 +234,11 @@ memcpy(&boot_params.hdr, &hdr, sizeof hdr); 函数的参数是这样传递的 -* `ax` 寄存器指向`boot_param.hdr`的内存地址 -* `dx` 寄存器指向`hdr`的内存地址 -* `cx` 寄存器包含`hdr`结构的大小 +* `ax` 寄存器指向 `boot_param.hdr` 的内存地址 +* `dx` 寄存器指向 `hdr` 的内存地址 +* `cx` 寄存器包含 `hdr` 结构的大小 -`memcpy`函数在将`si`和`di`寄存器压栈之后,将`boot_param.hdr`的地址放入`di`寄存器,将`hdr`的地址放入`si`寄存器,并且将`hdr`数据结构的大小压栈。 接下来代码首先以4个字节为单位,将`si`寄存器指向的内存内容拷贝到`di`寄存器指向的内存。当剩下的字节数不足4字节的时候,代码将原始的`hdr`数据结构大小出栈放入`cx`,然后对`cx`的值对4求模,接下来就是根据`cx`的值,以字节为单位将`si`寄存器指向的内存内容拷贝到`di`寄存器指向的内存。当拷贝操作完成之后,将保留的`si`以及`di`寄存器值出栈,函数返回。 +`memcpy` 函数在将 `si` 和 `di` 寄存器压栈之后,将`boot_param.hdr`的地址放入`di`寄存器,将`hdr`的地址放入`si`寄存器,并且将`hdr`数据结构的大小压栈。 接下来代码首先以4个字节为单位,将`si`寄存器指向的内存内容拷贝到`di`寄存器指向的内存。当剩下的字节数不足4字节的时候,代码将原始的`hdr`数据结构大小出栈放入`cx`,然后对`cx`的值对4求模,接下来就是根据`cx`的值,以字节为单位将`si`寄存器指向的内存内容拷贝到`di`寄存器指向的内存。当拷贝操作完成之后,将保留的`si`以及`di`寄存器值出栈,函数返回。 控制台初始化 -------------------------------------------------------------------------------- From ad5b58d2e7697d7393750a26e4f2ef26a91a6c59 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 12:07:49 -0500 Subject: [PATCH 132/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 404c056..e91c3a3 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -238,7 +238,7 @@ memcpy(&boot_params.hdr, &hdr, sizeof hdr); * `dx` 寄存器指向 `hdr` 的内存地址 * `cx` 寄存器包含 `hdr` 结构的大小 -`memcpy` 函数在将 `si` 和 `di` 寄存器压栈之后,将`boot_param.hdr`的地址放入`di`寄存器,将`hdr`的地址放入`si`寄存器,并且将`hdr`数据结构的大小压栈。 接下来代码首先以4个字节为单位,将`si`寄存器指向的内存内容拷贝到`di`寄存器指向的内存。当剩下的字节数不足4字节的时候,代码将原始的`hdr`数据结构大小出栈放入`cx`,然后对`cx`的值对4求模,接下来就是根据`cx`的值,以字节为单位将`si`寄存器指向的内存内容拷贝到`di`寄存器指向的内存。当拷贝操作完成之后,将保留的`si`以及`di`寄存器值出栈,函数返回。 +`memcpy` 函数在将 `si` 和 `di` 寄存器压栈之后,将 `boot_param.hdr` 的地址放入 `di` 寄存器,将 `hdr` 的地址放入 `si` 寄存器,并且将 `hdr` 数据结构的大小压栈。 接下来代码首先以4个字节为单位,将 `si` 寄存器指向的内存内容拷贝到 `di` 寄存器指向的内存。当剩下的字节数不足4字节的时候,代码将原始的 `hdr` 数据结构大小出栈放入 `cx` ,然后对 `cx` 的值对4求模,接下来就是根据 `cx` 的值,以字节为单位将 `si` 寄存器指向的内存内容拷贝到 `di` 寄存器指向的内存。当拷贝操作完成之后,将保留的 `si` 以及 `di` 寄存器值出栈,函数返回。 控制台初始化 -------------------------------------------------------------------------------- From 5b4f5611d951190b45213bf58dfe44c56425e882 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 12:08:42 -0500 Subject: [PATCH 133/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index e91c3a3..94e1853 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -243,15 +243,15 @@ memcpy(&boot_params.hdr, &hdr, sizeof hdr); 控制台初始化 -------------------------------------------------------------------------------- -在`hdr`结构体被拷贝到`boot_params.hdr`成员之后,系统接下来将进行控制台的初始化。控制台初始化时通过调用[arch/x86/boot/early_serial_console.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/early_serial_console.c)中定义的`console_init`函数实现的。 +在 `hdr` 结构体被拷贝到 `boot_params.hdr` 成员之后,系统接下来将进行控制台的初始化。控制台初始化时通过调用[arch/x86/boot/early_serial_console.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/early_serial_console.c)中定义的 `console_init` 函数实现的。 -这个函数首先查看命令行参数是否包含`earlyprintk`选项。如果命令行参数包含该选项,那么函数将分析这个选项的内容。得到控制台将使用的串口信息,然后进行串口的初始化。以下是`earlyprintk`选项可能的取值: +这个函数首先查看命令行参数是否包含 `earlyprintk` 选项。如果命令行参数包含该选项,那么函数将分析这个选项的内容。得到控制台将使用的串口信息,然后进行串口的初始化。以下是 `earlyprintk` 选项可能的取值: * serial,0x3f8,115200 * serial,ttyS0,115200 * ttyS0,115200 -当串口初始化成功之后,如果命令行参数包含`debug`选项,我们将看到如下的输出。 +当串口初始化成功之后,如果命令行参数包含 `debug` 选项,我们将看到如下的输出。 ```C if (cmdline_find_option_bool("debug")) From 7e533f52145403e34dac75b0ca6e26640292ebc2 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 12:08:51 -0500 Subject: [PATCH 134/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 94e1853..93305c3 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -258,7 +258,7 @@ if (cmdline_find_option_bool("debug")) puts("early console in setup code\n"); ``` -`puts`函数定义在[tty.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/tty.c)。这个函数只是简单的调用`putchar`函数将输入字符串中的内容按字节输出。下面让我们来看看`putchar`函数的实现: +`puts` 函数定义在[tty.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/tty.c)。这个函数只是简单的调用`putchar`函数将输入字符串中的内容按字节输出。下面让我们来看看`putchar`函数的实现: ```C void __attribute__((section(".inittext"))) putchar(int ch) From 3e52d83285d07ce7d4ef44765a41a29bf3ac180d Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 12:11:12 -0500 Subject: [PATCH 135/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 93305c3..6fb4a1f 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -258,7 +258,7 @@ if (cmdline_find_option_bool("debug")) puts("early console in setup code\n"); ``` -`puts` 函数定义在[tty.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/tty.c)。这个函数只是简单的调用`putchar`函数将输入字符串中的内容按字节输出。下面让我们来看看`putchar`函数的实现: +`puts` 函数定义在[tty.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/tty.c)。这个函数只是简单的调用 `putchar` 函数将输入字符串中的内容按字节输出。下面让我们来看看 `putchar`函数的实现: ```C void __attribute__((section(".inittext"))) putchar(int ch) @@ -273,9 +273,9 @@ void __attribute__((section(".inittext"))) putchar(int ch) } ``` -`__attribute__((section(".inittext")))` 说明这段代码将被放入`.inittext`代码段。关于`.inittext`代码段的定义你可以在 [setup.ld](https://github.com/torvalds/linux/blob/master/arch/x86/boot/setup.ld#L19)中找到。 +`__attribute__((section(".inittext")))` 说明这段代码将被放入 `.inittext` 代码段。关于 `.inittext` 代码段的定义你可以在 [setup.ld](https://github.com/torvalds/linux/blob/master/arch/x86/boot/setup.ld#L19)中找到。 -如果需要输出的字符是`\n`,那么`putchar`函数将调用自己首先输出一个字符`\r`。接下来,就调用`bios_putchar`函数将字符输出到显示器(使用bios int10中断): +如果需要输出的字符是 `\n` ,那么 `putchar` 函数将调用自己首先输出一个字符 `\r`。接下来,就调用 `bios_putchar` 函数将字符输出到显示器(使用bios int10中断): ```C static void __attribute__((section(".inittext"))) bios_putchar(int ch) From 537f26dccdf08e6cdef273b5b1c888a315106077 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 12:11:49 -0500 Subject: [PATCH 136/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 6fb4a1f..9c45769 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -291,7 +291,7 @@ static void __attribute__((section(".inittext"))) bios_putchar(int ch) } ``` -在上面的代码中`initreg`函数接受一个`biosregs`结构的地址作为输入参数,该函数首先调用`memset`函数将`biosregs`结构体所有成员清0。 +在上面的代码中 `initreg` 函数接受一个 `biosregs` 结构的地址作为输入参数,该函数首先调用 `memset` 函数将 `biosregs` 结构体所有成员清0。 ```C memset(reg, 0, sizeof *reg); @@ -321,7 +321,7 @@ GLOBAL(memset) ENDPROC(memset) ``` -首先你会发现,`memset`函数和`memcpy`函数一样使用了`fastcall`调用规则,因此函数的参数是通过`ax`,`dx`以及`cx`寄存器传入函数内部的。 +首先你会发现,`memset` 函数和 `memcpy` 函数一样使用了 `fastcall` 调用规则,因此函数的参数是通过`ax`,`dx`以及`cx`寄存器传入函数内部的。 就像memcpy函数一样,`memset`函数一开始将`di`寄存器入栈,然后将`biosregs`结构的地址从`ax`寄存器拷贝到`di`寄存器。接下来,使用`movzbl`指令将`dl`寄存器的内容拷贝到`ax`寄存器的低字节,到这里`ax`寄存器就包含了需要拷贝到`di`寄存器所指向的内存的值。 From 875bbcaafbc8de65cb2460470cb9e461f7c9415c Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 12:12:00 -0500 Subject: [PATCH 137/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 9c45769..742a5fd 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -321,7 +321,7 @@ GLOBAL(memset) ENDPROC(memset) ``` -首先你会发现,`memset` 函数和 `memcpy` 函数一样使用了 `fastcall` 调用规则,因此函数的参数是通过`ax`,`dx`以及`cx`寄存器传入函数内部的。 +首先你会发现,`memset` 函数和 `memcpy` 函数一样使用了 `fastcall` 调用规则,因此函数的参数是通过`ax`,`dx` 以及 `cx` 寄存器传入函数内部的。 就像memcpy函数一样,`memset`函数一开始将`di`寄存器入栈,然后将`biosregs`结构的地址从`ax`寄存器拷贝到`di`寄存器。接下来,使用`movzbl`指令将`dl`寄存器的内容拷贝到`ax`寄存器的低字节,到这里`ax`寄存器就包含了需要拷贝到`di`寄存器所指向的内存的值。 From 37a894eea90be7f75a8b101d9c24a483016bb925 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 12:12:13 -0500 Subject: [PATCH 138/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 742a5fd..6bbe95b 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -321,9 +321,9 @@ GLOBAL(memset) ENDPROC(memset) ``` -首先你会发现,`memset` 函数和 `memcpy` 函数一样使用了 `fastcall` 调用规则,因此函数的参数是通过`ax`,`dx` 以及 `cx` 寄存器传入函数内部的。 +首先你会发现,`memset` 函数和 `memcpy` 函数一样使用了 `fastcall` 调用规则,因此函数的参数是通过 `ax`,`dx` 以及 `cx` 寄存器传入函数内部的。 -就像memcpy函数一样,`memset`函数一开始将`di`寄存器入栈,然后将`biosregs`结构的地址从`ax`寄存器拷贝到`di`寄存器。接下来,使用`movzbl`指令将`dl`寄存器的内容拷贝到`ax`寄存器的低字节,到这里`ax`寄存器就包含了需要拷贝到`di`寄存器所指向的内存的值。 +就像memcpy函数一样,`memset` 函数一开始将 `di` 寄存器入栈,然后将`biosregs`结构的地址从`ax`寄存器拷贝到`di`寄存器。接下来,使用`movzbl`指令将`dl`寄存器的内容拷贝到`ax`寄存器的低字节,到这里`ax`寄存器就包含了需要拷贝到`di`寄存器所指向的内存的值。 接下来的`imull`指令将`eax`寄存器的值乘上`0x01010101`。这么做的原因是代码每次将尝试拷贝4个字节内存的内容。下面让我们来看一个具体的例子,假设我们需要将`0x7`这个数值放到内存中,在执行`imull`指令之前,`eax`寄存器的值是`0x7`,在`imull`指令被执行之后,`eax`寄存器的内容变成了`0x07070707`(4个字节的`0x7`)。在`imull`指令之后,代码使用`rep; stosl`指令将`eax`寄存器的内容拷贝到`es:di`指向的内存。 From a1a2970c682b2fff6f03c6f45f2d85d1b16133b7 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 12:14:00 -0500 Subject: [PATCH 139/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 6bbe95b..0a10815 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -323,9 +323,9 @@ ENDPROC(memset) 首先你会发现,`memset` 函数和 `memcpy` 函数一样使用了 `fastcall` 调用规则,因此函数的参数是通过 `ax`,`dx` 以及 `cx` 寄存器传入函数内部的。 -就像memcpy函数一样,`memset` 函数一开始将 `di` 寄存器入栈,然后将`biosregs`结构的地址从`ax`寄存器拷贝到`di`寄存器。接下来,使用`movzbl`指令将`dl`寄存器的内容拷贝到`ax`寄存器的低字节,到这里`ax`寄存器就包含了需要拷贝到`di`寄存器所指向的内存的值。 +就像memcpy函数一样,`memset` 函数一开始将 `di` 寄存器入栈,然后将 `biosregs` 结构的地址从 `ax` 寄存器拷贝到`di`寄存器。接下来,使用 `movzbl` 指令将 `dl` 寄存器的内容拷贝到 `ax` 寄存器的低字节,到这里 `ax` 寄存器就包含了需要拷贝到 `di` 寄存器所指向的内存的值。 -接下来的`imull`指令将`eax`寄存器的值乘上`0x01010101`。这么做的原因是代码每次将尝试拷贝4个字节内存的内容。下面让我们来看一个具体的例子,假设我们需要将`0x7`这个数值放到内存中,在执行`imull`指令之前,`eax`寄存器的值是`0x7`,在`imull`指令被执行之后,`eax`寄存器的内容变成了`0x07070707`(4个字节的`0x7`)。在`imull`指令之后,代码使用`rep; stosl`指令将`eax`寄存器的内容拷贝到`es:di`指向的内存。 +接下来的 `imull` 指令将 `eax` 寄存器的值乘上 `0x01010101`。这么做的原因是代码每次将尝试拷贝4个字节内存的内容。下面让我们来看一个具体的例子,假设我们需要将 `0x7` 这个数值放到内存中,在执行 `imull` 指令之前,`eax` 寄存器的值是 `0x7`,在 `imull` 指令被执行之后,`eax` 寄存器的内容变成了 `0x07070707`(4个字节的 `0x7`)。在 `imull` 指令之后,代码使用 `rep; stosl` 指令将 `eax` 寄存器的内容拷贝到 `es:di` 指向的内存。 在`bisoregs`结构体被`initregs`函数正确填充之后,`bios_putchar` 调用中断 [0x10](http://www.ctyme.com/intr/rb-0106.htm) 在显示器上输出一个字符。接下来`putchar`函数检查是否初始化了串口,如果串口被初始化了,那么将调用[serial_putchar](https://github.com/torvalds/linux/blob/master/arch/x86/boot/tty.c#L30)将字符输出到串口。 From 0b7855b9da561a81773bb35343597e801649569e Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 12:15:25 -0500 Subject: [PATCH 140/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 0a10815..15ca05d 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -327,7 +327,7 @@ ENDPROC(memset) 接下来的 `imull` 指令将 `eax` 寄存器的值乘上 `0x01010101`。这么做的原因是代码每次将尝试拷贝4个字节内存的内容。下面让我们来看一个具体的例子,假设我们需要将 `0x7` 这个数值放到内存中,在执行 `imull` 指令之前,`eax` 寄存器的值是 `0x7`,在 `imull` 指令被执行之后,`eax` 寄存器的内容变成了 `0x07070707`(4个字节的 `0x7`)。在 `imull` 指令之后,代码使用 `rep; stosl` 指令将 `eax` 寄存器的内容拷贝到 `es:di` 指向的内存。 -在`bisoregs`结构体被`initregs`函数正确填充之后,`bios_putchar` 调用中断 [0x10](http://www.ctyme.com/intr/rb-0106.htm) 在显示器上输出一个字符。接下来`putchar`函数检查是否初始化了串口,如果串口被初始化了,那么将调用[serial_putchar](https://github.com/torvalds/linux/blob/master/arch/x86/boot/tty.c#L30)将字符输出到串口。 +在 `bisoregs` 结构体被 `initregs` 函数正确填充之后,`bios_putchar` 调用中断 [0x10](http://www.ctyme.com/intr/rb-0106.htm) 在显示器上输出一个字符。接下来 `putchar` 函数检查是否初始化了串口,如果串口被初始化了,那么将调用[serial_putchar](https://github.com/torvalds/linux/blob/master/arch/x86/boot/tty.c#L30)将字符输出到串口。 堆初始化 -------------------------------------------------------------------------------- @@ -355,16 +355,16 @@ ENDPROC(memset) heap_end = (char *)((size_t)boot_params.hdr.heap_end_ptr + 0x200); ``` -接下来代码判断`heap_end`是否大于`stack_end`,如果条件成立,将`stack_end`设置成`heap_end`(这么做是因为在大部分系统中全局堆和堆栈是相邻的,但是增长方向是相反的)。 +接下来代码判断 `heap_end` 是否大于 `stack_end`,如果条件成立,将 `stack_end` 设置成 `heap_end`(这么做是因为在大部分系统中全局堆和堆栈是相邻的,但是增长方向是相反的)。 -到这里为止,全局堆就被正确初始化了。在全局堆被初始化之后,我们就可以使用`GET_HEAP`方法。至于这个函数的实现和使用,我们将在后续的章节中看到。 +到这里为止,全局堆就被正确初始化了。在全局堆被初始化之后,我们就可以使用 `GET_HEAP` 方法。至于这个函数的实现和使用,我们将在后续的章节中看到。 检查CPU类型 -------------------------------------------------------------------------------- -在堆栈初始化之后,内核代码通过调用[arch/x86/boot/cpu.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/cpu.c)提供的`validate_cpu`方法检查CPU级别以确定系统是否能够在当前的CPU上运行。 +在堆栈初始化之后,内核代码通过调用[arch/x86/boot/cpu.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/cpu.c)提供的 `validate_cpu` 方法检查CPU级别以确定系统是否能够在当前的CPU上运行。 -`validate_cpu`调用了[`check_cpu`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/cpucheck.c#L102)方法得到当前系统的CPU级别,并且和系统预设的最低CPU级别进行比较。如果不满足条件,则不允许系统运行。 +`validate_cpu` 调用了[`check_cpu`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/cpucheck.c#L102)方法得到当前系统的CPU级别,并且和系统预设的最低CPU级别进行比较。如果不满足条件,则不允许系统运行。 ```c /*from cpu.c*/ @@ -377,7 +377,7 @@ if (cpu_level < req_level) { } ``` -除此之外,`check_cpu`方法还做了大量的其他检测和设置工作,下面就简单介绍一些:1)检查cpu标志,如果cpu是64位cpu,那么就设置[long mode](http://en.wikipedia.org/wiki/Long_mode), 2) 检查CPU的制造商,根据制造商的不同,设置不同的CPU选项。比如对于AMD出厂的cpu,如果不支持SSE+SSE2,那么就禁止这些选项。 +除此之外,`check_cpu` 方法还做了大量的其他检测和设置工作,下面就简单介绍一些:1)检查cpu标志,如果cpu是64位cpu,那么就设置[long mode](http://en.wikipedia.org/wiki/Long_mode), 2) 检查CPU的制造商,根据制造商的不同,设置不同的CPU选项。比如对于AMD出厂的cpu,如果不支持 `SSE+SSE2`,那么就禁止这些选项。 内存分布侦测 -------------------------------------------------------------------------------- From 9657cbbfad2a36e97a01026695502fa25e1df77a Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 12:16:05 -0500 Subject: [PATCH 141/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 15ca05d..6b09de9 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -382,9 +382,9 @@ if (cpu_level < req_level) { 内存分布侦测 -------------------------------------------------------------------------------- -接下来,内核调用`detect_memory`方法进行内存侦测,以得到系统当前内存的使用分布。该方法使用多种编程接口,包括`0xe820`(获取全部内存分配),`0xe801`和`0x88`(获取临近内存大小),进行内存分布侦测。在这里我们只介绍[arch/x86/boot/memory.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/memory.c)中提供的`detect_memory_e820`方法。 +接下来,内核调用 `detect_memory` 方法进行内存侦测,以得到系统当前内存的使用分布。该方法使用多种编程接口,包括 `0xe820`(获取全部内存分配),`0xe801` 和 `0x88`(获取临近内存大小),进行内存分布侦测。在这里我们只介绍[arch/x86/boot/memory.c](https://github.com/torvalds/linux/blob/master/arch/x86/boot/memory.c)中提供的 `detect_memory_e820` 方法。 -该方法首先调用`initregs`方法初始化`biosregs`数据结构,然后向该数据结构填入`0xe820`编程接口所要求的参数: +该方法首先调用 `initregs` 方法初始化 `biosregs` 数据结构,然后向该数据结构填入 `0xe820` 编程接口所要求的参数: ```assembly initregs(&ireg); From b2e157fe75a351890f52dedda8c9cc845d8e619a Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 12:17:17 -0500 Subject: [PATCH 142/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 6b09de9..72d0e94 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -400,20 +400,20 @@ if (cpu_level < req_level) { * `es:di` 包含数据缓冲区的地址 * `ebx` 必须为0. -接下来就是通过一个循环来收集内存信息了。每个循环都开始于一个`0x15`中断调用,这个中断调用返回地址分配表中的一项,接着程序将返回的`ebx`设置到`biosregs`数据结构中,然后进行下一次的`0x15`中断调用。那么循环什么时候结束呢?直到`0x15`调用返回的eflags包含标志`X86_EFLAGS_CF`: +接下来就是通过一个循环来收集内存信息了。每个循环都开始于一个 `0x15` 中断调用,这个中断调用返回地址分配表中的一项,接着程序将返回的 `ebx` 设置到 `biosregs` 数据结构中,然后进行下一次的 `0x15` 中断调用。那么循环什么时候结束呢?直到 `0x15` 调用返回的eflags包含标志 `X86_EFLAGS_CF`: ```C intcall(0x15, &ireg, &oreg); ireg.ebx = oreg.ebx; ``` -在循环结束之后,整个内存分配信息将被写入到`e820entry`数组中,这个数组的每个元素包含下面3个信息: +在循环结束之后,整个内存分配信息将被写入到 `e820entry` 数组中,这个数组的每个元素包含下面3个信息: * 内存段的起始地址 * 内存段的大小 * 内存段的类型(类型可以是reserved, usable等等)。 -你可以在`dmesg`输出中看到这个数组的内容: +你可以在 `dmesg` 输出中看到这个数组的内容: ``` [ 0.000000] e820: BIOS-provided physical RAM map: @@ -428,7 +428,7 @@ if (cpu_level < req_level) { 键盘初始化 -------------------------------------------------------------------------------- -接下来内核调用[`keyboard_init()`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L65) 方法进行键盘初始化操作。 首先,方法调用`initregs`初始化寄存器结构,然后调用[0x16](http://www.ctyme.com/intr/rb-1756.htm)中断来获取键盘状态。 +接下来内核调用[`keyboard_init()`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L65) 方法进行键盘初始化操作。 首先,方法调用`initregs`初始化寄存器结构,然后调用 [0x16](http://www.ctyme.com/intr/rb-1756.htm)中断来获取键盘状态。 ```c initregs(&ireg); From d5437297a49590fa2649a2f0cb4cd1364e277e17 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 12:17:29 -0500 Subject: [PATCH 143/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 72d0e94..b8a5a7b 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -428,7 +428,7 @@ if (cpu_level < req_level) { 键盘初始化 -------------------------------------------------------------------------------- -接下来内核调用[`keyboard_init()`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L65) 方法进行键盘初始化操作。 首先,方法调用`initregs`初始化寄存器结构,然后调用 [0x16](http://www.ctyme.com/intr/rb-1756.htm)中断来获取键盘状态。 +接下来内核调用[`keyboard_init()`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/main.c#L65) 方法进行键盘初始化操作。 首先,方法调用`initregs`初始化寄存器结构,然后调用[0x16](http://www.ctyme.com/intr/rb-1756.htm)中断来获取键盘状态。 ```c initregs(&ireg); From 07e04c8e0586111ec3815f7574359eea4b9adc8e Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 12:20:34 -0500 Subject: [PATCH 144/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index b8a5a7b..3c44572 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -475,7 +475,7 @@ int query_mca(void) } ``` -这个方法设置`ah`寄存器的值为`0xc0`,然后调用`0x15` BIOS中断。中断返回之后代码检查 [carry flag](http://en.wikipedia.org/wiki/Carry_flag)。如果它被置位,说明BIOS不支持[**MCA**](https://en.wikipedia.org/wiki/Micro_Channel_architecture)。如果CF被设置成0,那么`ES:BX`指向系统信息表。这个表的内容如下所示: +这个方法设置 `ah` 寄存器的值为 `0xc0`,然后调用 `0x15` BIOS中断。中断返回之后代码检查 [carry flag](http://en.wikipedia.org/wiki/Carry_flag)。如果它被置位,说明BIOS不支持[**MCA**](https://en.wikipedia.org/wiki/Micro_Channel_architecture)。如果CF被设置成0,那么 `ES:BX` 指向系统信息表。这个表的内容如下所示: ``` Offset Size Description @@ -504,7 +504,7 @@ Offset Size Description 13h 3 BYTEs "JPN" ``` -接下来代码调用`set_fs`方法,将`es`寄存器的值写入`fs`寄存器: +接下来代码调用 `set_fs` 方法,将 `es` 寄存器的值写入 `fs` 寄存器: ```c static inline void set_fs(u16 seg) @@ -513,15 +513,15 @@ static inline void set_fs(u16 seg) } ``` -在[boot.h](https://github.com/torvalds/linux/blob/master/arch/x86/boot/boot.h) 存在很多类似于`set_fs`的方法, 比如 `set_gs`。 +在[boot.h](https://github.com/torvalds/linux/blob/master/arch/x86/boot/boot.h) 存在很多类似于 `set_fs` 的方法, 比如 `set_gs`。 -在`query_mca`的最后,代码将`es:bx`指向的内存地址的内容拷贝到`boot_params.sys_desc_table`。 +在 `query_mca` 的最后,代码将 `es:bx` 指向的内存地址的内容拷贝到 `boot_params.sys_desc_table`。 -接下来,内核调用`query_ist`方法获取[Intel SpeedStep](http://en.wikipedia.org/wiki/SpeedStep)信息。这个方法首先检查CPU类型,然后调用`0x15`中断获得这个信息并放入`boot_params`中。 +接下来,内核调用 `query_ist` 方法获取[Intel SpeedStep](http://en.wikipedia.org/wiki/SpeedStep)信息。这个方法首先检查CPU类型,然后调用 `0x15` 中断获得这个信息并放入 `boot_params` 中。 -接下来,内核会调用[query_apm_bios](https://github.com/torvalds/linux/blob/master/arch/x86/boot/apm.c#L21) 方法从BIOS获得 [高级电源管理](http://en.wikipedia.org/wiki/Advanced_Power_Management) 信息。`query_apm_bios`也是调用`0x15`中断,只不过将`ax`设置成`0x5300`以得APM设置信息。中断调用返回之后,代码将检查`bx`和`cx`的值,如果`bx`不是`0x504d` ( PM 标记 ),或者`cx`不是0x02 (0x02,表示支持保护模式),那么代码直接返回错误。否则,将进行下面的步骤。 +接下来,内核会调用[query_apm_bios](https://github.com/torvalds/linux/blob/master/arch/x86/boot/apm.c#L21) 方法从BIOS获得 [高级电源管理](http://en.wikipedia.org/wiki/Advanced_Power_Management) 信息。`query_apm_bios` 也是调用 `0x15` 中断,只不过将 `ax` 设置成 `0x5300` 以得到APM设置信息。中断调用返回之后,代码将检查 `bx` 和 `cx` 的值,如果 `bx` 不是 `0x504d` ( PM 标记 ),或者 `cx` 不是 `0x02` (0x02,表示支持32位模式),那么代码直接返回错误。否则,将进行下面的步骤。 -接下来,代码使用`ax = 0x5304`来调用`0x15`中断,以断开`APM`接口;然后使用`ax = 0x5303`调用`0x15`中断,使用32位接口重新连接`APM`;最后使用`ax = 0x5300`调用`0x15`中断再次获取APM设置,然后将信息写入`boot_params.apm_bios_info`。 +接下来,代码使用 `ax = 0x5304` 来调用 `0x15` 中断,以断开 `APM` 接口;然后使用 `ax = 0x5303` 调用 `0x15` 中断,使用32位接口重新连接 `APM`;最后使用 `ax = 0x5300` 调用 `0x15` 中断再次获取APM设置,然后将信息写入 `boot_params.apm_bios_info`。 需要注意的是,只有在`CONFIG_APM`或者`CONFIG_APM_MODULE`被设置的情况下,`query_apm_bios`方法才会被调用: From d19f013ca9bf6ca7be3baaa11d1bab16dbf82fd8 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 12:20:49 -0500 Subject: [PATCH 145/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 3c44572..8432bac 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -523,7 +523,7 @@ static inline void set_fs(u16 seg) 接下来,代码使用 `ax = 0x5304` 来调用 `0x15` 中断,以断开 `APM` 接口;然后使用 `ax = 0x5303` 调用 `0x15` 中断,使用32位接口重新连接 `APM`;最后使用 `ax = 0x5300` 调用 `0x15` 中断再次获取APM设置,然后将信息写入 `boot_params.apm_bios_info`。 -需要注意的是,只有在`CONFIG_APM`或者`CONFIG_APM_MODULE`被设置的情况下,`query_apm_bios`方法才会被调用: +需要注意的是,只有在 `CONFIG_APM` 或者 `CONFIG_APM_MODULE` 被设置的情况下,`query_apm_bios` 方法才会被调用: ```C #if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE) From e6ddd2a43ace17c45203f4ffda8b8978aae6ab14 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 12:22:01 -0500 Subject: [PATCH 146/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 8432bac..2c609c9 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -531,11 +531,11 @@ static inline void set_fs(u16 seg) #endif ``` -最后是[`query_edd`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/edd.c#L122) 方法调用, 这个方法从BIOS中查询`Enhanced Disk Drive`信息。下面让我们看看`query_edd`方法的实现。 +最后是[`query_edd`](https://github.com/torvalds/linux/blob/master/arch/x86/boot/edd.c#L122) 方法调用, 这个方法从BIOS中查询 `Enhanced Disk Drive` 信息。下面让我们看看 `query_edd` 方法的实现。 -首先,代码检查内核命令行参数是否设置了[edd](https://github.com/torvalds/linux/blob/master/Documentation/kernel-parameters.txt#L1023) 选项,如果edd选项设置成`off`,`query_edd`不做任何操作,直接返回。 +首先,代码检查内核命令行参数是否设置了[edd](https://github.com/torvalds/linux/blob/master/Documentation/kernel-parameters.txt#L1023) 选项,如果edd选项设置成 `off`,`query_edd` 不做任何操作,直接返回。 -如果EDD被激活了,`query_edd`遍历所有BIOS支持的硬盘,并获取相应硬盘的EDD信息: +如果EDD被激活了,`query_edd` 遍历所有BIOS支持的硬盘,并获取相应硬盘的EDD信息: ```C for (devno = 0x80; devno < 0x80+EDD_MBR_SIG_MAX; devno++) { @@ -549,7 +549,7 @@ for (devno = 0x80; devno < 0x80+EDD_MBR_SIG_MAX; devno++) { ... ``` -在代码中 `0x80`是第一块硬盘,`EDD_MBR_SIG_MAX`是一个宏,值为16。代码把获得的信息放入数组[edd_info](https://github.com/torvalds/linux/blob/master/include/uapi/linux/edd.h#L172)中。`get_edd_info`方法通过调用`0x13`中断调用(设置`ah = 0x41` ) 来检查EDD是否被硬盘支持。如果EDD被支持,代码将再次调用`0x13`中断,在这次调用中`ah = 0x48`,并且`si`指向一个数据缓冲区地址。中断调用之后,EDD信息将被保存到`si`指向的缓冲区地址。 +在代码中 `0x80` 是第一块硬盘,`EDD_MBR_SIG_MAX` 是一个宏,值为16。代码把获得的信息放入数组[edd_info](https://github.com/torvalds/linux/blob/master/include/uapi/linux/edd.h#L172)中。`get_edd_info` 方法通过调用 `0x13` 中断调用(设置 `ah = 0x41` ) 来检查EDD是否被硬盘支持。如果EDD被支持,代码将再次调用 `0x13` 中断,在这次调用中 `ah = 0x48`,并且 `si` 指向一个数据缓冲区地址。中断调用之后,EDD信息将被保存到 `si` 指向的缓冲区地址。 结束语 -------------------------------------------------------------------------------- From 5a33c54c727a70a6100af86f48ca60931c46d42e Mon Sep 17 00:00:00 2001 From: hailin Date: Wed, 24 Feb 2016 12:22:15 -0500 Subject: [PATCH 147/153] add spacing --- Booting/linux-bootstrap-2.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 Booting/linux-bootstrap-2.md diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md old mode 100644 new mode 100755 From ab6907664ed54cb65518e62997f3d8c51819cb73 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 12:25:14 -0500 Subject: [PATCH 148/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) mode change 100755 => 100644 Booting/linux-bootstrap-2.md diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md old mode 100755 new mode 100644 index 2c609c9..5207164 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -71,7 +71,7 @@ lgdt gdt ------------------------------------------------------------ ``` -粗粗一看,上面的结构非常吓人,不过实际上这个结构是非常容易理解的。比如在上图中的LIMIT 15:0表示这个数据结构的0到15位保存的是内存段的大小的0到15位。相似的LIMITE 19:16表示上述数据结构的16到19位保存的是内存段大小的16到19位。从这个分析中,我们可以看出每个内存段的大小是通过20位进行描述的。下面我们将对这个数据结构进行仔细分析: +粗粗一看,上面的结构非常吓人,不过实际上这个结构是非常容易理解的。比如在上图中的 LIMIT 15:0 表示这个数据结构的0到15位保存的是内存段的大小的0到15位。相似的 LIMITE 19:16 表示上述数据结构的16到19位保存的是内存段大小的16到19位。从这个分析中,我们可以看出每个内存段的大小是通过20位进行描述的。下面我们将对这个数据结构进行仔细分析: 1. Limit[20位]被保存在上述内存结构的0-15和16-19位。根据上述内存结构中`G`位的设置,这20位内存定义的内存长度是不一样的。下面是一些具体的例子: From c6a2a5486a8ee1b8c2bf3d8811cac987ae0f5cbd Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 12:25:57 -0500 Subject: [PATCH 149/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index 5207164..efc0787 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -73,7 +73,7 @@ lgdt gdt 粗粗一看,上面的结构非常吓人,不过实际上这个结构是非常容易理解的。比如在上图中的 LIMIT 15:0 表示这个数据结构的0到15位保存的是内存段的大小的0到15位。相似的 LIMITE 19:16 表示上述数据结构的16到19位保存的是内存段大小的16到19位。从这个分析中,我们可以看出每个内存段的大小是通过20位进行描述的。下面我们将对这个数据结构进行仔细分析: -1. Limit[20位]被保存在上述内存结构的0-15和16-19位。根据上述内存结构中`G`位的设置,这20位内存定义的内存长度是不一样的。下面是一些具体的例子: +1. Limit[20位] 被保存在上述内存结构的0-15和16-19位。根据上述内存结构中`G`位的设置,这20位内存定义的内存长度是不一样的。下面是一些具体的例子: * 如果`G` = 0, 并且Limit = 0, 那么表示段长度是1 byte * 如果`G` = 1, 并且Limit = 0, 那么表示段长度是4K bytes @@ -86,7 +86,7 @@ lgdt gdt * 如果G = 1, 那么内存段的长度是按照4K bytes进行增长的 ( Limit每增加1,段长度增加4K bytes ),最大的内存段长度将是4G bytes; * 段长度的计算公司是 base_seg_length * ( LIMIT + 1)。 -2. Base[32-bits]被保存在上述地址结构的0-15, 32-39以及56-63位。Base定义了段基址。 +2. Base[32-bits] 被保存在上述地址结构的0-15, 32-39以及56-63位。Base定义了段基址。 3. Type/Attribute (40-47 bits) 定义了内存段的类型以及支持的操作。 * `S` 标记( 第44位 )定义了段的类型,`S` = 0说明这个内存段是一个系统段;`S` = 1说明这个内存段是一个代码段或者是数据段( 堆栈段是一种特使类型的数据段,堆栈段必须是可以进行读写的段 )。 @@ -127,7 +127,7 @@ lgdt gdt * 如果`C` = 1,说明这个代码段可以被低优先级的代码访问,比如可以被用户态代码访问。反之如果`C` = 0,说明只能同优先级的代码段可以访问。 * 如果`R` = 1,说明该代码段可读。代码段是永远没有写权限的。 -4. DPL[2-bits, bit 45 和 46] (描述符优先级) 定义了该段的优先级。具体数值是0-3。 +4. DPL(2-bits, bit 45 和 46) (描述符优先级) 定义了该段的优先级。具体数值是0-3。 5. P 标志(bit 47) - 说明该内存段是否已经存在于内存中。如果`P` = 0,那么在访问这个内存段的时候将报错。 From ddb6266d5de20b6a708ccd280b3e3da8a8c778e3 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 12:26:16 -0500 Subject: [PATCH 150/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md index efc0787..95b8744 100644 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -127,7 +127,7 @@ lgdt gdt * 如果`C` = 1,说明这个代码段可以被低优先级的代码访问,比如可以被用户态代码访问。反之如果`C` = 0,说明只能同优先级的代码段可以访问。 * 如果`R` = 1,说明该代码段可读。代码段是永远没有写权限的。 -4. DPL(2-bits, bit 45 和 46) (描述符优先级) 定义了该段的优先级。具体数值是0-3。 +4. DPL(2-bits, bit 45 和 46)定义了该段的优先级。具体数值是0-3。 5. P 标志(bit 47) - 说明该内存段是否已经存在于内存中。如果`P` = 0,那么在访问这个内存段的时候将报错。 From 5e6c5fac40d1f6aefcbac02864524356903f9d9e Mon Sep 17 00:00:00 2001 From: hailin Date: Wed, 24 Feb 2016 12:26:42 -0500 Subject: [PATCH 151/153] add more format change --- Booting/linux-bootstrap-2.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 Booting/linux-bootstrap-2.md diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md old mode 100644 new mode 100755 From dd7b754d3ba38feb2576e2cb6ada3a30ca06b538 Mon Sep 17 00:00:00 2001 From: hailin cai Date: Wed, 24 Feb 2016 12:33:35 -0500 Subject: [PATCH 152/153] Update Booting/linux-bootstrap-2.md --- Booting/linux-bootstrap-2.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) mode change 100755 => 100644 Booting/linux-bootstrap-2.md diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md old mode 100755 new mode 100644 index 95b8744..cdde467 --- a/Booting/linux-bootstrap-2.md +++ b/Booting/linux-bootstrap-2.md @@ -394,9 +394,9 @@ if (cpu_level < req_level) { ireg.di = (size_t)&buf; ``` -* `ax` 固定为0xe820 +* `ax` 固定为 `0xe820` * `cx` 包含数据缓冲区的大小,该缓冲区将包含系统内存的信息数据 -* `edx` 必须是SMAP这个魔术数字,就是0x534d4150 +* `edx` 必须是 `SMAP` 这个魔术数字,就是 `0x534d4150` * `es:di` 包含数据缓冲区的地址 * `ebx` 必须为0. From f9e44b90253fdd00e8b975737d51851ab2c38e12 Mon Sep 17 00:00:00 2001 From: hailin Date: Wed, 24 Feb 2016 12:33:47 -0500 Subject: [PATCH 153/153] add spacing --- Booting/linux-bootstrap-2.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 Booting/linux-bootstrap-2.md diff --git a/Booting/linux-bootstrap-2.md b/Booting/linux-bootstrap-2.md old mode 100644 new mode 100755