gjlmp
驱动牛犊
驱动牛犊
  • 注册日期2010-10-09
  • 最后登录2014-01-09
  • 粉丝5
  • 关注3
  • 积分21分
  • 威望124点
  • 贡献值0点
  • 好评度3点
  • 原创分0分
  • 专家分35分
阅读:2918回复:1

剖析KiInitializeKernel函数

楼主#
更多 发布于:2012-03-05 22:30
    此函数位于\private\ntos\ke\i386\kernlini.c文件中。
    功能是初始化内核数据结构、idle线程进程对象、PCB,调用执行体初始化函数,返回到系统启动程序,当新的处理器上线时也会调用这个函数初始化处理器指定的结构。
    
        调用KiSetProcessorType函数设置CPU类型、步进,保存到CPU的PRCB中。
    先尝试更改EFLAGS寄存器的ID位(bit 21),检验是否支持CPUID指令,如果能更改,则支持,调用CPUID获取CPU类型;如果不能更改,则不支持CPUID指令,尝试更改EFLAGS寄存器的AC位(bit 18),如果能更改,则是386,再调用Get386Stepping函数获取步进;如果不能更改,则是486,再调用Get486Stepping函数获取步进。
    调用CPUID指令之前,先把Trap6改为临时处理无效指令的异常处理代码,当调用CPUID时,发生此异常,则先恢复Trap6的原始异常处理代码,再进行处理器判断(386或486)。
    先设置CPUID参数(EAX)为0,调用CPUID指令,如果EAX值大于3,则是586;如果小于1,则进行(386或486)处理器判断;如果是其他值,则再设置CPUID参数(EAX)为1,调用CPUID指令,EAX的bit13、bit14位则是处理器类型。
    调用Get386Stepping函数获取386步进。
        先调用386的32bit乘法指令mul,如果失败,则步进为0;如果成功,则调用xbts指令,如果失败,则步进为B0(100h);如果成功,则更改调试异常(Trap1)为临时Trap1处理代码,设置EFLAGS寄存器的TF位(bit 8),调用popfd指令,如果产生调试异常,则步进为D1(301h);如果没有,则步进为B1(101h)。
    调用Get486Stepping函数获取486步进。
        如果CRO的ET位 (bit 4)为1,且不能被清除,则步进为A(0);如果能被清除,则执行mov eax, DR4指令,如果产生Trap06异常,则步进为B(100h);否则,则调用_KiIsNpxPresent函数检验Npx是否存在,如果不存在,则步进为C(200h);如果存在,则调用FPU相关指令,如果执行失败,则步进为C(200h);如果成功,则步进为D(300h)。

    调用_KiSetCR0Bits函数设置CRO位,如果CPU类型高于386,则设置CR0的WP位(bit16)。
    
        调用KiIsNpxPresent函数检验数字附处理器是否存在,且根据NPX是否存在、CPU类型设置CRO寄存器值。
       先调用fninit指定初始化NPX,再调用fnstsw指令获取状态,如果状态是0,则NPX存在,否则不存在。
       先清理CRO的ET、MP、TS、EM位,如果不存在NPX,则只EM、TS置位,如果存在NPX,则ET也置位,如果CPU类型高于386,则NE也置位。

    调用KeGetPcr函数获取PCR地址,即PCR的PcSelfPcr(01CH)值。
    当定义NT_UP时,PCR等于0FFDFF000H,否则PCR等于FS寄存器的值。
ifdef NT_UP
    P0PCRADDRESS equ 0FFDFF000H
    PCR equ ds:[0FFDFF000H]
else
    PCR equ fs:
endif
    NT_UP是当在单处理器上创建OS时才定义的,打开各种各样的在单CPU时使用的已知的short-cuts。(NT_UP is defined when the uniprocessor build of the o/s is created, and turns on various short-cuts that can be used when it is known that only a single CPU is present.)

    初始化PRCB的DPC列表、DPC锁、其他成员。
    检验PRCB的CPU类型,仅仅支持NONE、INTEL、AMD三种类型,如果是CYRIX处理器,则报(UNSUPPORTED_PROCESSOR,0x386,0,0,0)蓝屏。

    如果是P0处理器,初始化设置CPU类型、步进、型号、级别、版本等。调用KiGetFeatureBits函数获取CPU特征。如果BOOT时支持cmpxchg8b指令,检验获取到的CPU特征是否支持cmpxchg8b指令,如果不支持,则报(MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTED, KF_CMPXCHG8B, 0, 0, 0)蓝屏。降低IRQL到APC_LEVEL级别。初始化内核内部锁KiContextSwapLock、KiDispatcherLock、KiFreezeExecutionLock。调用KiInitSystem函数初始化CPU独有的结构。    调用KeInitializeProcess函数初始化idle线程进程对象,基本优先级(BasePriority)为0,亲和性(Affinity)为0xffffffff,页目录表(DirectoryTableBase)为0,是否自动对齐(AutoAlignment)为FALSE,线程时限(ThreadQuantum)为MAXCHAR(0x7f)。
    如果是其他处理器,调用KiGetFeatureBits函数获取CPU特征。系统中的所有CPU对NPX的有效性必须一致,要么全支持,要么全不支持,如果不一致,则报(MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTED, 0x387, 0, 0, 0)蓝屏。检验CPU类型、步进,获取所有CPU的最低类型、步进。检验所有CPU对KF_CMPXCHG8B指令的有效性是否一致,如果不一致,则报(MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTED, KF_CMPXCHG8B, 0, 0, 0)蓝屏。检验所有CPU对KF_GLOBAL_PAGE的有效性是否一致,如果不一致,则报(MULTIPROCESSOR_CONFIGURATION_NOT_SUPPORTED, KF_GLOBAL_PAGE, 0, 0, 0)蓝屏。获取所有CPU的最小的步进。获取所有的CPU特征位的交集。降低IRQL到DISPATCH_LEVEL。

    系统分配了64K的共享数据区,内核、用户共享,地址范围是0xffdf0000~0xffdfffff。在osloader中(BlSetupForNt函数)已经设置好相关的页目录表、页表等。共享数据区是KUSER_SHARED_DATA结构。
    这里仅仅更新此结构的ProcessorFeatures(处理器特征位),ProcessorFeatures是大小为64的BOOLEAN类型数组,每个数组成员表示处理器的某个特征,目前定义了4个特征,分别是:
#define PF_FLOATING_POINT_PRECISION_ERRATA  0   // winnt
#define PF_FLOATING_POINT_EMULATED          1   // winnt
#define PF_COMPARE_EXCHANGE_DOUBLE          2   // winnt
#define PF_MMX_INSTRUCTIONS_AVAILABLE       3   // winnt
    这里根据变量KeFeatureBits设置此数组。KeFeatureBits的bit8位是MMX指令有效位,如果是1,则PF_MMX_INSTRUCTIONS_AVAILABLE为TRUE,否则,为FALSE; KeFeatureBits的bit7位是CMPXCHG8B指令有效位,如果是1,则PF_COMPARE_EXCHANGE_DOUBLE为TRUE,否则,为FALSE。

    调用KeInitializeThread函数初始化idle线程对象,设置线程对象的初始化内核堆栈(InitialStack)、堆栈基地址(StackBase)为(((ULONG)IdleStack) &0xfffffff0),堆栈最低地址(StackLimit)为((ULONG)Thread->InitialStack - KERNEL_STACK_SIZE),KERNEL_STACK_SIZE在AMD上为24K(0x6000),在i386上为12K(12288)。下一个处理器号(NextProcessor)为参数中的Number。优先级(Priority)为HIGH_PRIORITY。状态(State)为Running。亲和性(Affinity)为(KAFFINITY)(1<<Number)。等待IRQL(WaitIrql)为DISPATCH_LEVEL。
设置进程的激活处理器号(ActiveProcessors)为(ActiveProcessors | (1 << (Member)))。
    设置PRCB的当前线程、下一个线程、空闲线程。

    调用ExpInitializeExecutive函数初始化执行体,此处用try-except捕获异常,异常处理是报PHASE0_EXCEPTION蓝屏。

    如果是P0,调用KiComputeReciprocal函数计算时间增量倒数,且设置PRCB的MaximumDpcQueueDepth、MinimumDpcRate、AdjustDpcThreshold。

    如果是P0,调用ExAllocatePool函数申请BiosCall切换用的8K IOPM位图,如果申请失败,则报NO_PAGES_AVAILABLE蓝屏。

    提升IRQL为DISPATCH_LEVEL,设置线程属性为0。
    如果当前处理器的PRCB的下一个线程为空,则设置空闲处理器集(KiIdleSummary),期间要占用释放KiDispatcherLock锁。
    提升IRQL为HIGH_LEVEL。
    设置LoaderBlock的Prcb为空,标识此处理器初始化完毕。

最新喜欢:

liwentao321liwent...
ren970122
驱动牛犊
驱动牛犊
  • 注册日期2005-09-09
  • 最后登录2013-08-26
  • 粉丝0
  • 关注0
  • 积分21分
  • 威望233点
  • 贡献值0点
  • 好评度40点
  • 原创分0分
  • 专家分0分
沙发#
发布于:2012-03-06 19:50
和NT4的源码刚好可以对应起来。很强大。
兴趣所至,技术所在
游客

返回顶部