加载引导加载程序的第二阶段

2023-12-02

我正在尝试为 x86 机器创建一个小型操作系统,并开始为相当小的引导加载程序编写代码。我创建的引导加载程序非常简单,它从位于主引导记录后面的扇区加载一个小的第二引导加载程序,并跳转到该代码。主引导记录中的引导加载程序代码似乎运行良好,当它尝试跳转到第二阶段引导加载程序时出现问题。第二阶段引导加载程序应该输出一个指示成功的字母(字母 S),这样我就可以知道代码正在执行。问题是屏幕上没有出现任何内容,所以我怀疑第二阶段引导加载程序从未执行过。我使用的代码如下:

主引导记录中的引导加载程序:

[BITS 16] ; 16 bit mode
[ORG 0x7C00] ; Boot loader start address

Boot:
    ; Initial, dl contains drive number
    ; Set data segment to code segment
    mov ax, cs
    mov ds, ax
    mov es, ax
    ; Set the stack segment to 0xA000
    add ax, 0xA000
    mov ss, ax
    mov sp, 0x00
    ; Reset the drive, dl contains drive number
    mov ah, 0x00
    int 0x13
    ; Read from drive, dl contains drive number
    ;     Set up output location to 0x7E00: 0x00
    mov ax, 0x7E00
    mov es, ax ; Load to 0x7E00 : 0x00
    mov bx, 0x00
ReadDrive:
    mov ah, 0x02
    mov al, 0x01 ; Read 1 sector
    mov ch, 0x00 ; Read on cylinder 0
    mov cl, 0x02 ; Read sector 2
    mov dh, 0x00 ; Head number 0
    int 0x13

    jnc Success
    ; Print error (character F)
    mov al, 0x46
    call PrintChar
    jmp ReadDrive ; Retry

PrintChar: ; Prints a single character
    pusha
    mov ah, 0x09
    mov bh, 0x00
    mov bl, 0x0F
    mov cx, 0x01
    int 0x10
    popa
    ret

Success:
    jmp 0x7E00:0x00 ; Jump to 2nd stage bootloader

TIMES 510 - ($ - $$) db 0
DW 0xAA55 ; Boot signature

第二阶段引导加载程序的代码:

[BITS 16]
[ORG 0x7E00]

Boot2:
    ; Prints the character S to the screen
    mov al, 0x53
    mov ah, 0x09
    mov bh, 0x00
    mov bl, 0x0F
    mov cx, 0x01
    int 0x10
    jmp $ ; Loop forever

TIMES 512 - ($ - $$) db 0 ; Fill rest of block

使用以下代码编译此代码并将其写入驱动器:

nasm boot.asm -o boot.bin -f bin
nasm boot2.asm -o boot2.bin -f bin
dd if=boot.bin of=/dev/sd3 bs=512
dd if=boot2.bin of=/dev/sd3 bs=512 seek=1

此代码写入的设备是 16GB USB 驱动器。我用来启动此代码的计算机支持从 USB 启动,并像任何其他硬盘驱动器一样启动它们。代码似乎没有执行的原因是什么?


您的代码中似乎存在很多问题。我将尝试识别其中的一些。一些有用的参考资料可以在我为 Stackoveflow 撰写的一些答案中找到。

  • 一般引导加载程序技巧其中给出了您不想在引导加载程序中做出的一般准则和假设
  • 信息关于不设置的陷阱DS正确地访问内存变量并获取垃圾。这在某种程度上适用于你的第二阶段
  • An answer对于与您的问题相似的问题也可以提供一些有用的信息。

Stack

您确实设置了一个堆栈,但它可能会与视频内存重叠。尽管这可能与您的问题无关,但它是一个潜在的问题。有了这个代码:

add ax, 0xA000
mov ss, ax
mov sp, 0x00

You set SS=0xa000,并且SP=0x0000。这会设置堆栈,但不幸的是,压入堆栈的第一个值将位于 0xa000:(0x0000-2)= 0xa000:0xfffe 。 0xa000:0xfffe 恰好可能落在视频内存中。也许您打算执行 ss=0x9000 ,因此堆栈上的第一个值将位于 0x9000:0xfffe 。那里也有一个障碍。这扩展 Bios 数据区(EBDA) 可以位于该区域。某些 BIOS 会错误地返回该区域的错误大小。在大多数情况下,其大小为 0k 到 4k,正好低于物理地址 0xa0000。如果考虑到最坏的情况,我会选择低于该值的堆栈。

add ax, 0x9000
mov ss, ax
mov sp, 0xF000  ; Bottom of stack at 0x9000:0xF000

内存地址0x7e00

这里有两个问题。在您的问题中,您建议您尝试将第二阶段读入引导加载程序上方的区域。那将位于物理地址 0x7e00。您的代码执行以下操作:

; Read from drive, dl contains drive number
;     Set up output location to 0x7E00: 0x00
mov ax, 0x7E00
mov es, ax ; Load to 0x7E00 : 0x00
mov bx, 0x00

16-bit 段:偏移对使用此计算映射到物理内存地址:(segment

mov ax, 0x07E0
mov es, ax ; Load to 0x07E0:0x00
mov bx, 0x00

0x07E0:0x00 是物理内存地址 (0x07E0FAR JMP使用以下代码进入第二阶段:

jmp 0x7E00:0x00 ; Jump to 2nd stage bootloader

应该:

jmp 0x07E0:0x00 ; Jump to 2nd stage bootloader  

第二阶段的潜在问题

如果您进行建议的更改(jmp 0x07E0:0x00)前面提到过然后FAR JMP会改变CS:IP to CS=0x07E0(段),IP= 0x0000(offset) 并在那里继续执行。你需要你的ORG指令来匹配偏移量(IP)您从第一阶段跳转到的位置。由于偏移量 (IP) 是 0x0000 你的 ORG 指令应该匹配:

[ORG 0x0000]

您还需要确保当第二阶段开始加载时DS也被设置为匹配。实现此目的的一种方法是显式复制代码段CS到数据段DS。这可以通过第二阶段顶部的代码来完成,如下所示:

mov ax, cs 
mov ds, ax

没有正确设置数据段DS对变量的所有引用都将使用错误的段,并且可能不会指向它们在内存中的实际位置。您的代码目前没有变量,因此您没有注意到该问题。


不要假设 BIOS 通过 CS:IP=0x0000:0x7c00 调用第一阶段

在这个答案的序言中提到的一般引导加载程序提示中,提示 #1 非常重要:

  • 当 BIOS 跳转到您的代码时,您不能依赖 CS、DS、ES、SS、SP 寄存器具有有效或预期值。当引导加载程序启动时,应正确设置它们。您只能保证引导加载程序将从物理地址 0x00007c00 加载并运行,并且引导驱动器号加载到 DL 寄存器中。

在您的代码中,您的引导加载程序具有以下内容:

[BITS 16] ; 16 bit mode
[ORG 0x7C00] ; Boot loader start address

Boot:
    ; Initial, dl contains drive number
    ; Set data segment to code segment
    mov ax, cs
    mov ds, ax
    mov es, ax

[ORG 0x7C00]很好,但有一个假设是CS当它到达我们的引导加载程序时,该段被设置为 0x0000。然后我们设置DS=CS。对于简单的引导加载程序来说,传统观点是 BIOS 跳转到 0x0000:0x7c00 (CS:IP). ORG应与偏移量匹配(在本例中IP)。问题是,实际上 BIOS 跳转到物理地址 0x00007c00,但它可以通过多种方式来实现CS:IP pairs.

BIOS 可以对我们的代码进行 FAR JMP(或等效操作):jmp 0x07c0:0x0000,一些模拟器和真实硬件就是这样做的。 0x07c0:0x0000 是 (0x07c0IP= 0x0000。我们已经设定了[ORG 0x7c00]。那将是不匹配的!如果我们实际上不知道什么,我们如何解决这个问题CS:IP配对 BIOS 调用我们?很简单——不要复制CS to DS在引导加载程序的第一阶段。由于我们需要 0x7c00 的偏移量,DS需要为 0x0000 才能工作。我们应该明确地将 0x0000 放在我们的数据段中(DS)。代码可能如下所示:

[BITS 16] ; 16 bit mode
[ORG 0x7C00] ; Boot loader start address

Boot:
    ; Initial, dl contains drive number
    ; Set data segment to code segment
    xor ax, ax   ; AX=0
    mov ds, ax   ; DS=0  
    mov es, ax   ; ES=0
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

加载引导加载程序的第二阶段 的相关文章

  • 32 位到 64 位内联汇编移植

    我有一段 C 代码 在 GNU Linux 环境下用 g 编译 它加载一个函数指针 它如何执行并不重要 使用一些内联汇编将一些参数推送到堆栈上 然后调用该函数 代码如下 unsigned long stack 1 23 33 43 save
  • Grub 和进入实模式(低级汇编语言编程)

    我一直在开发一个玩具操作系统 并一直使用 grub 作为我的引导加载程序 最近尝试使用 VGA 时 我发现无法使用硬件中断 我发现这是因为我被 grub 置于保护模式 有人知道如何在不删除 grub 的情况下回到实模式吗 如果您使用 GRU
  • 将字段中的位扩展到掩码中所有(重叠+相邻)集位的最快方法?

    假设我有 2 个名为 IN 和 MASK 的二进制输入 实际字段大小可能是 32 到 256 位 具体取决于用于完成任务的指令集 每次调用时两个输入都会改变 Inputs IN 1100010010010100 MASK 000111101
  • PAE(物理地址扩展)如何实现大于4GB的地址空间?

    维基百科文章的摘录物理地址扩展 http en wikipedia org wiki Physical Address Extension x86 处理器硬件架构通过用于选择附加内存的附加地址线进行了增强 因此物理地址大小从 32 位增加到
  • 将 C 代码转换为 x86-64 汇编

    我正在尝试将 C 代码转换为 x86 64 我的目标是反转链表 传入的两个参数是 head ptr 和 offset to 以获取指针字段的地址 即指向列表中下一个节点的指针 据我了解 head ptr是通过rdi寄存器传入的 offset
  • IDA pro asm 指令更改

    我只是想知道我怎样才能 更改IDA视图A中的asm指令 如何编辑指令 对于 实例 jnz 到 jmp 如何插入新指令 call func1 调用 func2 插入到现有的 代码 我知道如何制作 diff 文件 我知道如何在我的 DLL 上应
  • 在 Intel x86 架构上使用非 AVX 指令移动 xmm 整数寄存器值

    我有以下问题 需要使用 AVX2 以外的任何工具来解决 我有 3 个值存储在 m128i 变量中 不需要第四个值 需要将这些值移动 4 3 5 我需要两个功能 一个用于按这些值进行右逻辑移位 另一个用于左逻辑移位 有谁知道使用 SSE AV
  • Polygot 包含 nasm/yasm 和 C 的文件

    我有一堆幻数 我想将它们包含在由 nasm 或 yasm 编译的 C 程序和汇编文件中 在纯 C 语言中 该文件看起来像是一系列定义 例如 define BLESS 55378008 define ANSWER 42 在 nasm 或 ya
  • 在 x86 程序集中打印寄存器值的简单方法

    我需要在 8086 Assembly 中编写一个程序 接收来自用户的数据 进行一些数学计算并在屏幕上打印答案 我已经编写了程序的所有部分并且一切正常 但我不知道如何打印号码显示到屏幕上 在我所有计算结束时 答案是 AX 它被视为无符号 16
  • 是否有适用于双打 (__m128d) 的 Move (_mm_move_ss) 和 Set (_mm_set_ss) 内在函数?

    多年来 我有几次看到 in 中的内在函数float参数被转换为 m128使用以下代码 m128 b mm move ss m mm set ss a 例如 void MyFunction float y m128 a mm move ss
  • 68000 汇编语言 - CMPI.B

    What are the contents of the CCR and D3 after the following instructions sequence executes Perform the calculation by ha
  • 如何在 MacOS 上使用 nasm 进行编译

    我正在尝试在汇编器上编译并链接我的第一个程序 我尝试编译以下代码 include stud io inc global main section text main xor eax eax again PRINT Hello PUTCHAR
  • 将 XMM 寄存器压入堆栈

    有没有办法将打包双字整数从 XMM 寄存器推送到堆栈 然后在需要时将其弹出 理想情况下 我正在寻找通用寄存器的 PUSH 或 POP 之类的东西 我已经检查了英特尔手册 但我要么错过了命令 要么没有 或者我是否必须将值解压到通用寄存器然后推
  • 嵌入式系统:使用汇编语言时的内存布局

    根据我的理解 嵌入式系统运行机器代码 有多种方法可以生成此代码 一种是用 C 等高级语言编写程序 然后使用编译器获得这样的代码 另一种方法是用汇编语言为该嵌入式系统编写指令 并使用汇编器将其转换为机器代码 现在我们得到了加载到系统并执行的机
  • x86 程序集 Pushl/popl 不适用于“错误:后缀或操作数无效”

    我是汇编编程的新手 正在努力解决编程基础 http savannah nongnu org projects pgubook 在带有 GNU 汇编器 v2 20 1 的 Ubuntu x86 64 桌面上 我已经能够汇编 链接执行我的代码
  • 汇编语言程序中连续两次相乘

    我正在使用 8086 模拟器以及 DOSBOX 和 MASM 我知道当我们将 8 位与 8 位相乘时 答案将是 16 位 al 8 bit ax 当我们将 16 位与 16 位相乘时 答案将是 32 位 ax 16 bit dx ax 但如
  • 将十进制转换为十六进制

    首先 这是家庭作业 我正在尝试将 5 位数字读入寄存器 bx 假定该数字不大于 65535 16 位 以下是我尝试这样做的方法 但是 当我尝试打印该号码时 我仅打印输入的最后一位数字 这让我猜测 当我向 bx 添加另一个数字时 它会覆盖以前
  • 如何将 asm 着色器编译为 fxo 文件?

    我有一个已编译的 fxo 着色器 我正在尝试对其进行稍微编辑 仅调整一些常量 使用 fxdis https code google com archive p fxdis d3d1x https code google com archiv
  • 如何恢复 x86-64 寄存器保存约定

    fibonacci cmpq 1 rdi ja recursive movl 1 eax ret recursive push rbp push r10 movq rdi r10 leaq 2 rdi rdi call fibonacci
  • 如何从程序内部获取指向程序的特定可执行文件部分的指针? (也许是诽谤)

    我在 Linux 环境中 需要编写一个程序来检索放置在其可执行文件的某个部分中的一些数据 那么 如何从程序内部获取指向程序某个部分 通过其名称 的指针呢 我知道可以使用elf getdata 将节的索引作为参数传递给 get 和Elf Da

随机推荐

  • 使用鼻子插件将布尔值传递给我的包

    使用鼻子测试时 可以将变量从 cmd 移动到我的模块吗 场景 我正在使用 selenium 运行测试 需要针对网站的生产版本和沙箱版本 www sandbox myurl com 和 www myurl com 运行 我编写了一个自定义的鼻
  • R : 当刻度数据丢失时刻度数据增加值

    我正在处理刻度数据 并希望将我的 xts 不规则间隔的系列聚合成 1 秒均匀的系列 因此 我使用 xts 包函数 to period price 1m lt to period price period seconds k 1 OHLC F
  • 从 Dom 元素获取 CSS 路径

    我得到这个函数来获取 cssPath var cssPath function el var path while el nodeName toLowerCase html el el parentNode path unshift el
  • 数据错误不是来自 Vue 3 浏览器刷新

    我正在使用 Axios 从数据库中提取数据 随着onMounted 数据来了 但是如果不对浏览器进行任何操作 数据是不可见的 我正在使用 v for 当有变化时 数据就进来 例如 当我滚动时 数据就进来 我无法解决 我该怎么办 我哪里做错了
  • 用于密码验证的正则表达式 C#

    我想在 C 中使用正则表达式验证密码 这些是条件 不应以数字或特殊字符开头 不应以特殊字符结尾 必须至少包含这三个字符中的任何一个 并且不允许使用其他特殊字符 必须包含至少一个字母 必须包含至少一位数字 长度至少应为 8 个字符 这是我的尝
  • 插件照亮 0.7 与 jQuery 1.9.1 或 jQuery-UI 1.10.3 不兼容 -> TypeError: $.css(...) 未定义

    jQuery 或 jQuery UI 中的更改与插件不兼容jquery Illuminate 0 7 插件jquery Illuminate 0 7 works 在 Chrome 30 Firefox 22 和 IE 10 中 它用 jqu
  • SSRS 报告中的嵌入图像未显示 - 权限问题

    我的所有报告的顶部都有一个徽标 作为嵌入图像 这些报告通过 SSRS Web 服务接口显示在 ASP Net Web 应用程序中 所有这些都是非常标准的内容 但图像不会呈现 我只是得到了一个损坏的链接 对于此问题有许多可能的解决方案 我尝试
  • Xcode:缺少自动调整预览窗口大小

    I am learning iOS and using Apress Beggining iOS 5 development book The book says there is a window to preview UI elemen
  • 有人可以用最简单的术语解释什么是“for”循环吗?

    我正在尝试学习编码 但我无法理解 for 循环中发生的情况 if else if else 语句很简单 if this is true do this else otherwise but if this is true do that e
  • 在 Swift 中使用正则表达式

    我正在尝试替换 Swift 中字符串中出现的所有空格和特殊字符 我不知道 Swift 中的正则表达式是什么 我尝试使用内置函数 replacingOccurences of 并将我的 RegEx 作为字符串协议传递 在代码编译时 不进行任何
  • AngularJS 与 Ajax 表单提交需要点击两次

    我需要从 HTML 页面执行以下活动 用户输入电子邮件和密码进行注册 当用户点击时表单被发送到控制器Submit Control 使用 AJAX 创建对 RESTful Server 的 JSON 请求 服务器做出相应响应 根据服务器的响应
  • 我什么时候应该使用 hstack/vstack、append、concatenate、column_stack?

    简单的问题 这些方法各自的优点是什么 看起来 只要给定正确的参数 和 ndarray 形状 它们的工作效果似乎都是相同的 做一些工作到位吗 有更好的表现吗 我什么时候应该使用哪些功能 如果你有两个矩阵 那么你就可以使用hstack and
  • 如何验证人名? -Python/Django

    我希望为我的其中一张表单的全名创建一个验证器 不幸的是 我不确定继续下去的最佳方法是什么 因为它并不像以下那样微不足道 if not char in string letters raise ValidationError 认为正则表达式会
  • 如何在R中使用两种不同类型的日期格式将字符转换为日期?

    我有一个包含超过 200 万个 obs 的庞大数据集 并且所有列的类都是字符类型 我需要将其中之一转换为日期格式 dd mm yyyy 但日期是这样写的 dates lt c 2022 04 08 26 01 2021 14 07 2021
  • WAGU(表视图中的数据)库中的修改

    参考post 其中 Clough 先生给出的答案是 他编写了代码来获得这样的输出 PLATINUM COMPUTERS PVT LTD NO 20 B Main Street Kandy Sri Lanka Land 812254630 M
  • minGW gcc 对 _imp____glew* 函数的未定义引用

    我正在尝试在 Win 7 x64 系统上使用 MinGW 编译一个相对简单的 OpenGL 程序 并且我不断收到对几个 GLEW 函数的未定义引用 我已将库设置为链接到程序 并一直在寻找列表中可能缺少的任何库 但链接器的输出仍然如下所示 1
  • 使用量角器和硒将文件上传到隐藏输入

    我有一个隐藏的文件输入字段 如下所示
  • SQSlistener 未接收消息

    我能够从 springboot 向 SQS 队列发送消息 但无法使用 sqslistener 注释接收消息 有人可以帮忙吗 public void send String message queueMessagingTemplate con
  • Ruby:使用其中一个数组的值对 2 个数组进行排序

    我正在用 ruby 创建一个简单的游戏 并且有两个存储高分的数组 HS Points and HS Names 我将高分保存在两个文件中 并且我想对点进行一些编码 点转换为 to s 23 我想按降序对姓名和分数进行排序 并将它们限制为 1
  • 加载引导加载程序的第二阶段

    我正在尝试为 x86 机器创建一个小型操作系统 并开始为相当小的引导加载程序编写代码 我创建的引导加载程序非常简单 它从位于主引导记录后面的扇区加载一个小的第二引导加载程序 并跳转到该代码 主引导记录中的引导加载程序代码似乎运行良好 当它尝