微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

ReactOS 源码分析 (二) By:ProgrammeBoy

重头戏FreeLdr.sys

简单介绍

代码位置: D:/ReactOS/ReactOS_src/boot/freeldr/freeldr/

先说下freeldr.sys,这个小编包含了内存管理、文件系统、缓存、UI、磁盘管理等要要的操作系统的功能。再加上点进程管理就可以成一个小操作系统了。那好我们就分析下freeldr以便我们能更好的理解操作系统。

来到代码目录下,好多代码代码的入口函数在那个文件呀,怎么找呀。我啰嗦下,但不知道对不对,我们代开D:/ReactOS/ReactOS_src/boot/freeldr/freeldr.rbuild文件

像这样:

<?xml version="1.0"?>

<!DOCTYPE group SYstem "../../tools/rbuild/project.dtd">

<group xmlns:xi="http://www.w3.org/2001/XInclude">

<directory name="bootsect">

<xi:include href="bootsect/bootsect.rbuild" />

</directory>

<directory name="freeldr">

<xi:include href="freeldr/freeldr_startup.rbuild" />

<xi:include href="freeldr/freeldr_base64k.rbuild" />

<xi:include href="freeldr/freeldr_base.rbuild" />

<xi:include href="freeldr/freeldr_arch.rbuild" />

<xi:include href="freeldr/freeldr_main.rbuild" />

<xi:include href="freeldr/setupldr_main.rbuild" />

<xi:include href="freeldr/freeldr.rbuild" />

<xi:include href="freeldr/setupldr.rbuild" />

</directory>

<directory name="fdebug">

<xi:include href="fdebug/fdebug.rbuild" />

</directory>

</group>

看第一个是什么呀?对呀是: freeldr/freeldr_startup.rbuild,那就去子目录找下,找到了,内容如下:

<?xml version="1.0"?>

<!DOCTYPE module SYstem "../../../tools/rbuild/project.dtd">

<module name="freeldr_startup" type="objectlibrary">

<include base="freeldr_startup">include</include>

<include base="ntoskrnl">include</include>

<compilerflag>-fno-inline</compilerflag>

<compilerflag>-fno-zero-initialized-in-bss</compilerflag>

<directory name="arch">

<if property="ARCH" value="i386">

<directory name="i386">

<file first="true">fathelp.asm</file>

<file>arch.S</file>

</directory>

</if>

<if property="ARCH" value="amd64">

<directory name="amd64">

<file first="true">fathelp.S</file>

<file>arch.S</file>

</directory>

</if>

</directory>

</module>

前面已经假定我们的机器是i386的了,看到带颜色的那几句吗?尤其是蓝色那句,好像是第一个程序源码的第一个文件到底是不是呢?我用IDA看了下freeldr的反汇编确实是.就是那个fathelp.asm,那好我们来看下fathelp.asm..

Fathelp.asm

代码路径: D:/ReactOS/ReactOS_src/boot/freeldr/freeldr/arch/i386/fathelp.asm

看到代码

; This code will be stored in the first 512 bytes

; of freeldr.sys. The first 3 bytes will be a jmp

; instruction to skip past the FAT helper code

; that is stored in the rest of the 512 bytes.

;

; This code is loaded at 0000:8000 so we have to

; encode a jmp instruction to jump to 0000:8200

global _mainCRTStartup ; For Mingw32 builds where the linker looks for this symbol

_mainCRTStartup:

global start

start:

db 0xe9

db 0xfd

db 0x01

.以下省略

看到这我就不分析下面的啦,正如上面的注释所说,这个代码储存在freeldr.sys的前512字节中,3个字节会跳过这段代码,剩下的部分放在接下来的剩余空间内,这里为什么跳1fa?对呀,因为e9 01fd自身就3字节,加起来这好是0x200h,512字节.

arch.s

那好我们看下一个文件,一个文件是什么了?怎么看?不会这么快就忘了吧,freeldr_startup.rbuild里呀!马上看下,哦是”arch.S”,?*.s是什么文件?不知道?我也不知道.google...知道了,原来也是汇编格式呀,只不过是AT&T汇编格式,和我们的nasm差不多,就是源/目的操作数的位置颠倒了下,这里我就不多说这个汇编的语法了,毕竟我们是分析代码,大家可以去网上search,大有文章.

代码:

.text

.code16

/*支持C语言的注释和宏定义还有include,不错不错找个时间好好看下这个汇编*/

#define ASM

#include <arch.h>

#include <multiboot.h>

/*入口点*/

EXTERN(RealEntryPoint)

/*关中断*/

cli

/*寄存器初始化*/

xorw %ax,%ax

movw %ax,%ds

movw %ax,%es

movw %ax,%fs

movw %ax,%gs

movw %ax,%ss

/* Setup a stack */

movw stack16,%sp

sti

/* 进入保护模式,这是个重点,,一起去看这个函数的具体实现过程*/

call switch_to_prot

/*现在我们在保护模式下*/

.code32

/* 设置全局变量以后在freeldr.sys的其他地方会用着,先记着 */

xorl %eax,%eax

movl %eax,(_BootDrive)

movl %eax,(_BootPartition)

/* Store the boot drive */

movb %dl,(_BootDrive)

/* Store the boot partition */

movb %dh,(_BootPartition)

/* GO! */

/* 这里知道这个_BootMain是什么函数?freeldr.sys一个C写的函数

*D:/ReactOS/ReactOS_src/boot/freeldr/freeldr/freeldr.c

*,这也说明我们的汇编要over

* 还有呀大家知道函数调用一个字都不能错,但是但freeldr.c里定义的是BootMain,这里怎么多了个_(下横杠),

* 不会出错?原来这是使用C语言的__cdecl调用方式,MSDN中说 Underscore character (_) is prefixed to names

* 就是说在编译的时候会用_修改函数名称,所以这里用了_BootMain切记,其他方式大家查MSDN

pushl %eax

call _BootMain

/* 再次转换到实模式*/

call switch_to_real

.code16

/* int 0x19是干嘛用的呀*/

/* INT19H是怎么处理启动的?
INT
19会将MBR512字节装载到内存0x7c00中,然后JUMP0x7c00处,开始执行MBR的可执行程序(masterbooter),Masterbooter最起码需要做这些事情:
*/

int $0x19

/* We should never get here */

stop:

jmp stop

nop

nop

/*

* Switches the processor to protected mode

* 转换处理器到保护模式

* it destroys eax

* 这里注意下,会改变eax中的内容

*/

EXTERN(switch_to_prot)

.code16

cli /* None of these */

/* We don't kNow what values are currently */

/*我不并不知道下面这些寄存器现在的值是多少*/

/* in the segment registers. So we are */

/* 所以现在我们重新加载这些值**/

/* going to reload them with sane values. */

/* Of course CS has to already be valid. */

/* 当然CS已经是被设置好的了*/

/* We are currently in real-mode so we */

/* 我们现在还在实模式下所以我们呢需要实模式的段值*/

/* need real-mode segment values. */

xorw %ax,%ss

/* Get the return address off the stack */

/*保存要返回的地址*/

popw (code32ret)

/* Save 16-bit stack pointer */

movw %sp,stack16

/* 这里必须懂得保护模式和实模式*/

/* 加载GDT */

lgdt gdtptr

/* 加载 IDT */

lidt i386idtptr

/*修改cr0寄存器,来开启保护模式状态 */

mov %cr0,%eax

orl $CR0_PE_SET,%eax

mov %eax,%cr0

/* Clear prefetch queue & correct CS */

ljmp $PMODE_CS,$inpmode

/* 进入32位程序段*/

.code32

inpmode:

/* Setup segment selectors */

movw $PMODE_DS,%ss

movl stack32,%esp

/* Put the return address back onto the stack */

/*将返回地址压入堆栈*/

pushl (code32ret)

/* Now return in p-mode! */

/* 返回主函数,我们现在是在保护模式下了*/

ret

/*

* Switches the processor back to real mode

* it destroys eax

*/

EXTERN(switch_to_real)

.code32

/* We don't kNow what values are currently */

/* in the segment registers. So we are */

/* going to reload them with sane values. */

/* Of course CS has to already be valid. */

/* We are currently in protected-mode so we */

/* need protected-mode segment values. */

movw $PMODE_DS,%ss

/* Get the return address off the stack */

popl (code16ret)

/* Save 32-bit stack pointer */

movl %esp,stack32

/* jmp to 16-bit segment to set the limit correctly */

ljmp $RMODE_CS,$switch_to_real16

switch_to_real16:

.code16

/* Restore segment registers to correct limit */

movw $RMODE_DS,%ss

/* 关闭保护模式 */

mov %cr0,%eax

andl $CR0_PE_CLR,%cr0

/* Clear prefetch queue & correct CS */

ljmp $0,$inrmode

inrmode:

movw %cs,%ss

/* Clear out the high 16-bits of ESP */

/* This is needed because I have one */

/* machine that hangs when booted to dos if */

/* anything other than 0x0000 is in the high */

/* 16-bits of ESP. Even though real-mode */

/* code should only use SP and not ESP. */

xorl %esp,%esp

movw stack16,%sp

/* Put the return address back onto the stack */

pushw (code16ret)

/* Load IDTR with real mode value */

lidt rmode_idtptr

sti /* These are ok Now */

/* Now return in r-mode! */

ret

/*

* Needed for enabling the a20 address line

*/

.code16

empty_8042:

.word 0x00eb,0x00eb // jmp $+2,jmp $+2

inb $0x64,%al

cmp $0xff,%al // legacy-free machine without keyboard

jz empty_8042_ret // controllers on Intel Macs read back 0xFF

testb $0x02,%al

jnz empty_8042

empty_8042_ret:

ret

/*下面的两个函数根本就没用到过*/

/*

* Enable the A20 address line (to allow access to over 1mb)

*/

EXTERN(_EnableA20)

.code32

pushal

call switch_to_real

.code16

call empty_8042

movb $0xD1,%al // command write

outb %al,$0x64

call empty_8042

mov $0xDF,%al // A20 on

out %al,$0x60

call empty_8042

call switch_to_prot

.code32

popal

ret

/*

* disable the A20 address line

*/

EXTERN(_disableA20)

.code32

pushal

call switch_to_real

.code16

call empty_8042

movb $0xD1,$0x64

call empty_8042

mov $0xDD,%al // A20 off

out %al,$0x60

call empty_8042

call switch_to_prot

.code32

popal

ret

..

.code32

/* 16-bit stack pointer */

stack16:

.word STACK16ADDR

/* 32-bit stack pointer */

stack32:

.long STACK32ADDR

/* 16-bit return address */

code16ret:

.long 0

/* 32-bit return address */

code32ret:

.long 0

.p2align 2 /* force 4-byte alignment */

gdt:

/* NULL Descriptor */

.word 0x0000

.word 0x0000

.word 0x0000

.word 0x0000

/* 32-bit flat CS */

.word 0xFFFF

.word 0x0000

.word 0x9A00

.word 0x00CF

/* 32-bit flat DS */

.word 0xFFFF

.word 0x0000

.word 0x9200

.word 0x00CF

/* 16-bit real mode CS */

.word 0xFFFF

.word 0x0000

.word 0x9E00

.word 0x0000

/* 16-bit real mode DS */

.word 0xFFFF

.word 0x0000

.word 0x9200

.word 0x0000

/* GDT table pointer */

gdtptr:

.word 0x27 /* Limit */

.long gdt /* Base Address */

/* Initial GDT table pointer for multiboot */

gdtptrhigh:

.word 0x27 /* Limit */

.long gdt + INITIAL_BASE - FREELDR_BASE /* Base Address */

/* real-mode IDT pointer */

rmode_idtptr:

.word 0x3ff /* Limit */

.long 0 /* Base Address */

mb_info:

.fill MB_INFO_SIZE,1,0

cmdline:

.fill CMDLINE_SIZE,0

EXTERN(_BootDrive)

.long 0

EXTERN(_BootPartition)

.long 0

,看完了,总结下

这个文件函数调用基本步骤:

1,数据初始化

2,加载GDT

3,加载LDT

4,修改CR0PE位进入保护模式

5,跳转到保护模式下

6,保存全局变量

7,调用_BootMain调用FreeLdr

8,修改CR0PE位进入实模式

9,加载实模式的LDT

10,重新启动.

虽然下面的EnableA20disableA20没用到吧,这里我也说下他们是干嘛的,其实代码作者也注释上了”to allow access to over 1mb”就是为访问大于1M的内存地址,参考《自己动手写操作系统》上的一段内容:

什么是A20?这是一个历史问题,8086是采用SEG:OFFSET的模式分段的,所以他的最大内存是FFFF:FFFF10FFEFh,但是8086只有20根地址总线,只能寻址到1Mb,如果试图访问超过1MB的地址时会怎样呢?实际上系统并不会发生异常.而是回卷回去,重新从地址0开始寻址,可是到了80286系列时,真的可以访问到1MB以上的内存地址了,如果遇到同样的情况系统不会再回卷寻址,这就造成了向上不兼容,为了保证百分之百兼容,IBM想出一个办法使用键盘来控制第20个地址位,这就是A20地址线,如果不开A20地址线总会是0.为了访问所有内存,我们需要打开A20地址线,认是关闭,但是怎么打开A20地址线呢?我们可以通过92h端口来达到目的..

知识点:

1,AT&T汇编

2,实模式和保护模式和其相互转换(具体参见《自己动手写操作系统》一书)

3,A20地址的打开方法.

4,几种C语言函数调用方式.

5,Int 19h是干什么用的?(代码里的红字介绍了)

原文地址:https://www.jb51.cc/react/308505.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐