Memory 汇编中的引导加载程序问题

Memory 汇编中的引导加载程序问题,memory,assembly,kernel,bootloader,Memory,Assembly,Kernel,Bootloader,我有一个有趣的问题,我试图制作一个简单的引导加载程序,可以引导和显示文本。当我进一步研究时,我发现引导加载程序需要为某些东西保留内存。以下是我的问题: 如何为引导加载程序保留内存 为什么需要保留内存 引导加载程序还需要执行哪些其他功能 如何将控制传递给内核 你真的不需要“保留”内存,只需要使用它 实际上,您需要这样做,因为您的内核希望位于可寻址内存的开头。因此,您应该做的第一件事是将引导加载程序(此时可能会从MBR运行)复制到高内存中(远远超过内核所在的位置),然后跳转到它。然后,您需要将内核加

我有一个有趣的问题,我试图制作一个简单的引导加载程序,可以引导和显示文本。当我进一步研究时,我发现引导加载程序需要为某些东西保留内存。以下是我的问题:

  • 如何为引导加载程序保留内存
  • 为什么需要保留内存
  • 引导加载程序还需要执行哪些其他功能
  • 如何将控制传递给内核
  • 你真的不需要“保留”内存,只需要使用它

    实际上,您需要这样做,因为您的内核希望位于可寻址内存的开头。因此,您应该做的第一件事是将引导加载程序(此时可能会从MBR运行)复制到高内存中(远远超过内核所在的位置),然后跳转到它。然后,您需要将内核加载到内存的开头,为内核设置环境的其余部分(同样,它希望某些东西,比如Linux内核命令行,位于内存中的特定位置),然后跳转到其入口点

    当你来到这里,你不再关心引导程序,它的人生目标已经完成。控件已传递给内核,它可能会在某个时候覆盖用于加载程序的RAM部分


    这也可能有助于您了解启动过程的早期阶段:

    请记住,每台现代PC仍以16位(称为实模式)启动。因此,引导加载程序必须使用16位代码。在加载了内核的第一阶段之后,引导加载程序要做的第一件事就是切换到32位模式(称为保护模式)

    首先,您应该熟悉16位PC架构,尤其是其细分模型。您需要了解如何以16位模式编写引导加载程序的第一部分。 接下来,您需要了解i386体系结构(32位模式),以便切换到保护模式并配置段寄存器、分页等

    请记住,BIOS代码仅在CPU通过软件中断处于16位模式(实模式)时才可用。一旦切换到受保护模式,就不能再使用它(除非切换回16位模式,这很麻烦)。 有关BIOS中断的一个很好的参考是Ralf Brown的中断列表:

    就内核开发而言,James Molloy的内核开发教程是从零开始为x86编写操作系统的一个很好的教程:

    他使用GRUB加载内核,这可能是最好的做法,因为GRUB已经将CPU切换到保护模式并为您设置GDT。然后在C中加载一个简单的内核就变得简单多了

    如果您决定不使用GRUB,而是自己编写所有内容,那么您必须使用16位代码,使用BIOS中的软件中断来加载内核(请参阅上面的参考资料)。此外,如果希望内核采用ELF格式,则必须编写ELF加载程序。 请记住,BIOS加载的引导扇区的长度正好为512字节。这是非常少的代码(实际上是510字节,因为最后2个字节用于引导签名)。这就是为什么引导扇区会加载没有此大小限制的第二阶段引导器


    不管怎样,祝你好运

    关于问题1的答案

    1) 只要转到项目的链接器文件,就可以执行如下操作

    2) 确保将重置向量与向量表一起移动

        MEMORY
        {
        /*  ------------------- BOOT LOADER CODE --------------------------------------*/
            BOOT_cached(RX) : ORIGIN = 0x08000000, LENGTH = 32K-0x40
            BOOT_uncached(RX) : ORIGIN = 0x0C000000, LENGTH = 32K-0x40
    
        /*  ------------------- USB INTERFACE -----------------------------------------*/
            USB_INTERFACE_cached(RX) : ORIGIN = 0x08007FC0, LENGTH = 0x40
            USB_INTERFACE_uncached(RX) : ORIGIN = 0x0C007FC0, LENGTH = 0x40
    
        /*  ------------------- APPLICATION CODE ------------------------------------- */
        /*  APP_cached(RX) : ORIGIN = 0x08008000, LENGTH = 256K-32K */
        /*  APP_uncached(RX) : ORIGIN = 0x0C008000, LENGTH = 256K-32K */
    
        /*  PSRAM_1(!RX) : ORIGIN = 0x1FFFC000, LENGTH = 0x4000 */
        /*  DSRAM_1_system(!RX) : ORIGIN = 0x20000000, LENGTH = 0x8000 */
        /*  DSRAM_2_comm(!RX) : ORIGIN = 0x20008000, LENGTH = 0x8000 */
        /*  SRAM_combined(!RX) : ORIGIN = 0x1FFFC000, LENGTH = 0x14000 */
    
            DSRAM_1_system(!RX) : ORIGIN = 0x2000D000, LENGTH = 0x3000
            SRAM_combined(!RX) : ORIGIN = 0x2000D000, LENGTH = 0x3000
        }
    
        stack_size = DEFINED(stack_size) ? stack_size : 4096;
        no_init_size = 64;
    
    3) 此外,您还可以通过如下检查project output.lst文件来确认向量表是否位于正确的地址

    Sections:
    Idx Name          Size      VMA       LMA       File off  Algn
      0 .usb_interface 00000024  08007fc0  0c007fc0  0000ffc0  2**2
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
      1 .text         000050f0  08000000  0c000000  00008000  2**2
                      CONTENTS, ALLOC, LOAD, READONLY, CODE
      2 Stack         00001000  2000d000  2000d000  00015000  2**0
                      ALLOC
      3 .data         00000050  2000e000  0c0050f0  0000e000  2**2
                      CONTENTS, ALLOC, LOAD, DATA
      4 .bss          000006a8  2000e050  0c005140  0000e050  2**2
                      ALLOC
      5 USB_RAM       00000e00  2000e6f8  2000e6f8  00015000  2**2
    

    感谢您的回答,但我一直遇到的一个问题是,我不知道如何跳转到内存中的某个位置(或复制到内存中的某个位置)。我并不完全精通x86汇编程序,但
    jmp xyz
    将允许您跳转到地址xyz。要复制内存,您需要逐字将一个字加载到寄存器中,然后将其写入另一个内存地址。似乎还详细介绍了在汇编程序中快速memcpy的实现。事实上,别理我,我刚刚意识到你将用C来做这件事。使用jmp可以跳转到内存中的任意位置,与以前一样,您只需将其包装在
    \uuu asm\uu
    块中即可。内存复制可以用memcpy完成,只需传入内存地址而不是指针,因为它们基本上是一样的。您可能不得不在某个时候开始编写汇编程序,以获得所需的结果。@JAW1025:学习用x86汇编语言编程。在进入引导加载程序/内核编程之前做好这一点。没有这些知识,写这样低级的东西将是难以忍受的。从简单的事情开始。谢谢你的提示,我会更好地学习x86汇编,谢谢你在c语言中的帮助。问题是GRUB规范非常清楚地表明你应该建立自己的GDT。:GDTR'即使段寄存器如上所述设置,“GDTR”可能无效,因此OS映像在设置自己的“GDT”之前不得加载任何段寄存器(即使只是重新加载相同的值!)。同意。但是,您可以在以后的C代码中设置GDT,这将更加方便和可读(前提是您编写了一些调用lgdt指令的asm函数)。请记住,每台现代PC仍然以16位(称为实模式)启动,但并不完全如此。现代PC使用UEFI固件,在启动后立即切换到32或64位模式。(而且开机状态并不是完全真实的模式,它是一种不真实的模式,具有很高的CS基数)。如果配置为使用16位实模式运行传统BIOS引导加载程序,则PC固件可以选择使用16位实模式运行传统BIOS引导加载程序。以及模拟一堆实际上并不存在的硬件。