如何在 NASM 汇编中进入 32 位保护模式?

2023-11-22

我正在学习 x86 汇编,并且正在尝试在 NASM 中制作一个玩具操作系统,但我不明白一些东西。

我制作了一个成功启动内核的引导加载程序:

  1. 从包含内核文件的软盘中加载 14 个扇区;
  2. 在这些标记为的扇区中搜索文件kernel.feo;
  3. 将该文件加载到内存中的偏移量处0x2000;
  4. 使用远跳转执行内核jmp 0x2000:0x0000.

所以我的内核代码位于0x2000:0记忆中。CS由于使用了远跳,可能会被正确设置。在此内核代码中,我想进入 32 位保护模式,但我不确定 GDT 是如何工作的。当我在虚拟机上运行下面的代码时(QEMU),就是什么也不做。

我想请你帮助我进入 32 位保护模式!

也就是说,您有以下问题:

  1. 您假设代码加载于0x7c00:0因为org 0,但情况可能并非如此。唯一能保证的是物理地址。您应该使用远跳到您的入口点,以便CS已正确设置。
  2. 您出于某种原因设置DS to 0x2000所以你的代码根本找不到任何数据。你应该设置DS匹配CS,或使用CS覆盖所有地方(不推荐)。
  3. 保护模式代码假定从零开始的段,这又意味着它期望org 0x7c00这当然与您的设置冲突。你应该切换到org 0x7c00和段0.
  4. VGA 文本模式段位于0xb8000 not 0xb80000(一减零)。
  5. 您没有启动签名字节0x55 0xaa在引导扇区的末尾。

我已经在代码中更正了这些内容:

  1. [org 0x0]被修正为[org 0x2000]和段设置为0;
  2. DS被修正为0代替0x2000,所以现在它匹配CS;
  3. VGA 文本模式段更正为0xb8000;

但代码无法处理这些更正,它应该打印两个字符串,但它不执行任何操作!

请注意,此内核代码不应以启动签名结尾0x55 0xAA,因为它不是引导扇区。

这是更正后的内核代码(不起作用):

[bits 16]
[org 0x2000]

    jmp 0:kernel_start

gdt_start:

gdt_null:
    dd 0x0
    dd 0x0

gdt_code:
    dw 0xffff
    dw 0x0
    db 0x0
    db 10011010b
    db 11001111b
    db 0x0

gdt_data:
    dw 0xffff
    dw 0x0
    db 0x0
    db 10010010b
    db 11001111b
    db 0x0

gdt_end:

gdt_descriptor:
    dw gdt_end - gdt_start
    dd gdt_start

CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start

print:
    mov ah, 14
    mov bh, 0
    lodsb
    cmp al, 0
    je .done
    int 0x10
    jmp print
.done:
    ret

uzenet_real db 'uzenet16', 0
uzenet_prot db 'uzenet32', 0

kernel_start:
    mov ax, 0
    mov ss, ax
    mov sp, 0xFFFC

    mov ax, 0
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    mov si, uzenet_real
    call print

    cli
    lgdt[gdt_descriptor]
    mov eax, cr0
    or eax, 0x1
    mov cr0, eax
    jmp CODE_SEG:b32

[bits 32]

VIDEO_MEMORY equ 0xb8000
WHITE_ON_BLACK equ 0x0f

print32:
    pusha
    mov edx, VIDEO_MEMORY
.loop:
    mov al, [ebx]
    mov ah, WHITE_ON_BLACK
    cmp al, 0
    je .done
    mov [edx], ax
    add ebx, 1
    add edx, 2
    jmp .loop
.done:
    popa
    ret

b32:
    mov ax, DATA_SEG
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax

    mov ebp, 0x90000
    mov esp, ebp

    mov ebx, uzenet_prot
    call print32

    jmp $

操作系统编程是一项高级任务。您至少应该能够使用调试器来发现自己的错误并理解基本的东西。您可能需要重新考虑您是否具备此努力的所有先决条件。

也就是说,您有以下问题:

  1. 您假设代码加载于0x7c00:0因为org 0,但情况可能并非如此。唯一能保证的是物理地址。您应该使用远跳到您的入口点,以便CS已正确设置。
  2. 您出于某种原因设置DS to 0x2000所以你的代码根本找不到任何数据。你应该设置DS匹配CS,或使用CS覆盖所有地方(不推荐)。
  3. 保护模式代码假定从零开始的段,这又意味着它期望org 0x7c00这当然与您的设置冲突。你应该切换到org 0x7c00和段0.
  4. VGA 文本模式段位于0xb8000 not 0xb80000(一减零)。
  5. 您没有启动签名字节0x55 0xaa在引导扇区的末尾。

固定代码:

[bits 16]
[org 0x7c00]

    jmp 0:kernel_start

gdt_start:

gdt_null:
    dd 0x0
    dd 0x0

gdt_code:
    dw 0xffff
    dw 0x0
    db 0x0
    db 10011010b
    db 11001111b
    db 0x0

gdt_data:
    dw 0xffff
    dw 0x0
    db 0x0
    db 10010010b
    db 11001111b
    db 0x0

gdt_end:

gdt_descriptor:
    dw gdt_end - gdt_start
    dd gdt_start

CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start

print:
    pusha
    mov ah, 14
    mov bh, 0
.loop:
    lodsb
    cmp al, 0
    je .done
    int 0x10
    jmp .loop
.done:
    popa
    ret

uzenet16 db 'uzenet16', 0
uzenet32 db 'uzenet32', 0

kernel_start:
    mov ax, 0
    mov ss, ax
    mov sp, 0xFFFC

    mov ax, 0
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    mov si, uzenet16
    call print

    cli
    lgdt[gdt_descriptor]
    mov eax, cr0
    or eax, 0x1
    mov cr0, eax
    jmp CODE_SEG:b32

[bits 32]

VIDEO_MEMORY equ 0xb8000
WHITE_ON_BLACK equ 0x0f

print32:
    pusha
    mov edx, VIDEO_MEMORY
.loop:
    mov al, [ebx]
    mov ah, WHITE_ON_BLACK
    cmp al, 0
    je .done
    mov [edx], ax
    add ebx, 1
    add edx, 2
    jmp .loop
.done:
    popa
    ret

b32:
    mov ax, DATA_SEG
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax

    mov ebp, 0x2000
    mov esp, ebp

    mov ebx, uzenet32
    call print32

    jmp $

[SECTION signature start=0x7dfe]
dw 0AA55h

您更新的问题似乎仍然对代码的加载位置感到困惑:您说offset 0x2000但接下来谈谈Executes the kernel using a far jump jmp 0x2000:0x0000这当然是错误的,因为它在段中多了一个零,并且无论如何应该是零段远跳:jmp 0:0x2000。除此之外,验证您的代码确实已加载到内存中的正确位置。学习使用调试器。

这是一个小的引导扇区,它将上述代码从第二个扇区加载到地址0x2000。它工作正常,问题不在于 GDT 的东西,特别是如果你甚至没有打印实模式消息(你也不清楚这一点)。

[bits 16]
[org 0x7c00]
mov ax, 0201h
mov cx, 0002h
mov dh, 0
mov bx, 0
mov es, bx
mov bx, 2000h
int 13h
jmp 0:2000h

[SECTION signature start=0x7dfe]
dw 0AA55h
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 NASM 汇编中进入 32 位保护模式? 的相关文章

  • 英特尔的最后分支记录功能是英特尔处理器独有的吗?

    最后分支记录是指存储与最近执行的分支相关的源地址和目标地址的寄存器对 MSR 的集合 它们受英特尔酷睿 2 英特尔至强和英特尔凌动处理器系列的支持 http css csail mit edu 6 858 2012 readings ia3
  • Grub 和进入实模式(低级汇编语言编程)

    我一直在开发一个玩具操作系统 并一直使用 grub 作为我的引导加载程序 最近尝试使用 VGA 时 我发现无法使用硬件中断 我发现这是因为我被 grub 置于保护模式 有人知道如何在不删除 grub 的情况下回到实模式吗 如果您使用 GRU
  • Intel:序列化指令和分支预测

    英特尔架构开发人员手册 http www intel com content www us en architecture and technology 64 ia 32 architectures software developer v
  • 使用 Gas 生成与位置无关的代码 (-fPIC)

    我尝试在 x86 64 上创建共享库但失败 问题归结为以下代码 请不要介意 它没有多大意义 section data newline ascii n section text globl write newline type write n
  • 调用可以是 cdecl 或 stdcall 的函数

    我需要编写调用外部函数的代码 该函数可以是 32 位 Windows 应用程序中的 stdcall 调用或 cdecl 我的代码 调用者 无法提前知道其中的哪一个 现在 如果我尝试从定义为 stdcall 的调用站点调用 cdecl 函数
  • 各种中断的区别:SCI、SMI、NMI、普通中断

    我正在学习英特尔架构 到目前为止我遇到过几种类型的中断 SCI 系统控制中断 硬件使用的系统中断 用于向操作系统通知 ACPI 事件 SCI 是一个有效 低电平 可共享的电平中断 SMI 系统管理中断 由遗留系统上的中断事件生成的操作系统透
  • 在 Intel x86 架构上使用非 AVX 指令移动 xmm 整数寄存器值

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

    我在市场上有一个 NDK 应用程序 并获得了有关以下内容的本机崩溃报告 SIGILL信号 我使用 Google Breakpad 生成本机崩溃报告 以下是详细信息 我的应用程序是为armeabi v7a with霓虹灯支持 它在 NVIDI
  • 无法在 64 位 Linux 上从汇编 (yasm) 代码调用 C 标准库函数

    我有一个函数foo以汇编语言编写 并在 Linux Ubuntu 64 位上使用 yasm 和 GCC 编译 它只是使用以下命令将消息打印到标准输出puts 如下所示 bits 64 extern puts global foo secti
  • 在 x86 程序集中打印寄存器值的简单方法

    我需要在 8086 Assembly 中编写一个程序 接收来自用户的数据 进行一些数学计算并在屏幕上打印答案 我已经编写了程序的所有部分并且一切正常 但我不知道如何打印号码显示到屏幕上 在我所有计算结束时 答案是 AX 它被视为无符号 16
  • ICC 中的 -O3 会扰乱内在函数,使用 -O1 或 -O2 或相应的手动汇编即可

    这是后续这个问题 http stackoverflow com questions 49791664 o2 in icc messes up assembler fine with o1 in icc and all optimizatio
  • 从c调用汇编函数

    我试图从 c 调用汇编函数 但我不断收到错误 text globl integrate type integrate function integrate push ebp mov esp ebp mov 0 edi start loop
  • 难以理解汇编命令“加载有效地址”[重复]

    这个问题在这里已经有答案了 可能的重复 LEA 指令的目的是什么 https stackoverflow com questions 1658294 whats the purpose of the lea instruction LEA指
  • 比“add esp, 4”更小的指令

    又是我 我的程序中有很多 add esp 4 我正在尝试减小它的大小 是否有任何更小的指令可以替代 add esp 4 pop edx 或者您不介意破坏的任何其他整数寄存器 这就是现代编译器实际上所做的 https stackoverflo
  • 使用 ACPI 在 MS-DOS 中关闭计算机

    我在基于 Pentium 的计算机上运行 MS DOS 6 22 主板支持 ACPI 并且想知道是否有一个可以用来关闭计算机的汇编语言例程 或者它是否比那个更难 即主板 具体的 基本上 我想创建一个小程序来从命令行关闭计算机 这是专门为此编
  • 嵌入式系统:使用汇编语言时的内存布局

    根据我的理解 嵌入式系统运行机器代码 有多种方法可以生成此代码 一种是用 C 等高级语言编写程序 然后使用编译器获得这样的代码 另一种方法是用汇编语言为该嵌入式系统编写指令 并使用汇编器将其转换为机器代码 现在我们得到了加载到系统并执行的机
  • 如何将 asm 着色器编译为 fxo 文件?

    我有一个已编译的 fxo 着色器 我正在尝试对其进行稍微编辑 仅调整一些常量 使用 fxdis https code google com archive p fxdis d3d1x https code google com archiv
  • 错误:无法识别的指令 [ORG]

    我试图编写一个引导加载程序以在 dos box 中使用 我写了下面的代码 BITS 16 tell the assembler that its a 16 bit code ORG 0x7C00 Origin tell the assemb
  • 在 qemu 中将扇区加载到 RAM

    我编写了一个简单的程序 将扇区 扇区编号 2 加载到 RAM 但什么也没打印 首先 我尝试了以下引导扇区代码 org 0x7c00 mov ax 0x1000 ES BX 1000 0000 mov es ax mov bx 0x00 Lo
  • 如果默认禁用 A20 线,如何在 0xFFFFFFF0 处访问 BIOS ROM?

    我正在阅读有关 A20 线的信息http wiki osdev org A20 Line http wiki osdev org A20 Line 这似乎表明 A20 线默认被禁用 在Pentium上 如果硬复位后立即输出的地址为0xFFF

随机推荐

  • 将 Arc 克隆为 Arc,其中 T 实现 U

    我觉得很奇怪 use std sync Arc trait Fruit struct Pear impl Fruit for Pear fn main let pear Arc new Pear let cloned Arc clone p
  • 在 Neo4j 中实现 Dijkstra 算法

    我对 Neo4j 很陌生 有人可以向我解释 请逐步 如何实现 Dijkstra 算法来找到两个节点之间的最短路径 是否可以简单地使用 Cypher 来完成它 我已经尝试过最短路径算法 但它很慢 MATCH from Location Loc
  • 为什么我不能反转 str::split 的结果?

    根据文档Split 有一个rev方法对结果的影响split在字符串上 fn main let mut length 0 let mut mult 1 for part in 1 30 split rev length mult part p
  • Eclipse p2:category.xml 和 site.xml 之间的区别

    p2 存储库创建 ant 任务 例如 eclipse publish featuresAndBundles 似乎采用指定类别信息的 site xml 或category xml 文件 我发现 eclipse 生成的 site xml 和ca
  • 圆形ViewPager。第一轮后碎片无法正常工作

    好的 所以我需要圆形 ViewPager 我真的很难实施它 现在我已经实现了它 并且就圆形滚动而言它运行良好 但我注意到一个问题 那就是 第一轮滚动片段不起作用后 我有三个片段 里面有一个按钮 按钮在第一轮有效 但当我回到第一页时按钮不起作
  • 有没有办法在 Eclipse 中自动生成 getter 和 setter?

    我正在研究一个新的Android项目 Java 并创建了一个带有大量变量的对象 由于我计划为所有这些添加 getter 和 setter 所以我想知道 是否有捷径Eclipse自动生成给定类中的 getter 和 setter 在所需类的源
  • 使用 JSONB 列内的值连接表

    有两个表 授权联系人 auth contacts userid varchar contacts jsonb contacts包含具有属性的联系人数组 contact id type discussion contact id varcha
  • 在 cmake 构建中使用 LLVM

    我正在尝试构建自己的使用 LLVM 的项目 我在官网上下载了源码和预编译包 最新版本 http releases llvm org download html 我下载了 LLVM source code Clang for Windows
  • 角度材料将 angularjs 1.5 组件加载到 $mdDialog 中

    目标 使用组件而不是使用 scope 来设置数据 没有可共享的错误 问题是对话框加载组件时未设置数据元素 屏幕截图显示了对话框的当前状态 选项卡 2 信息 中应该有一个绑定的对象 我可以使用 onComplete 事件验证对话框加载后对象
  • 如何通过两个键对 JSON 对象进行排序?

    我有一个 JSON 对象 我想先按一个键排序 然后按第二个键排序 类似于 SQL 中按两列排序 以下是我想要的 JSON 示例 GROUPID 3169675 LASTNAME Chantry 我想按 GROUPID 然后按 LASTNAM
  • 每个连接请求都被视为直接连接请求+ android ble

    我们正在编写一个 ble 应用程序 希望与我们想要连接的外设建立持久连接 出于同样的目的 每当我们失去现有连接时 我们都希望与外围设备重新连接 因此 我们作为中心的 Android 应用程序只是尝试通过调用 bluetoothdevice
  • Tensorflow 因 CUDNN_STATUS_ALLOC_FAILED 崩溃

    在网上搜索了几个小时没有结果 所以我想在这里问一下 我正在尝试按照 Sentdex 的教程制作一辆自动驾驶汽车 但是在运行模型时 我遇到了一堆致命错误 我在网上搜索了解决方案 很多人似乎都有同样的问题 但是 我没有找到任何解决方案 包括这个
  • KMM 与公共(共享)模块中的 Java 源

    由于平台限制 我们无法将 Java 源代码与 Kotlin Multiplatform Mobile 一起使用 但如果 Kotlin 与 Java 100 兼容 为什么我们不能将 Java 与 Kotlin Multiplatform Mo
  • OpenSSL 连接:警报内部错误

    我使用 SNI 在单个服务器上运行 100 个 HTTPS 服务 实际上 我无权访问它们 这是一项任务 我只知道他们的域名N xxx yy其中 N 的范围是 00 到 99 分配的目标是评估与每个服务器的每个连接的安全性 因此 某些服务器包
  • Bash 需要测试字母数字字符串

    尝试验证字符串中是否仅包含小写 大写或数字 if TITLE a zA Z0 9 then echo INVALID fi 想法 更新 变量 TITLE 目前只有大写文本 因此它应该通过并且不应该输出任何内容 但是 如果我向 TITLE 添
  • “find -exec”或“find |”哪个更快xargs -0'?

    在我的 Web 应用程序中 我使用 PHP 脚本渲染页面 然后从中生成静态 HTML 文件 静态 HTML 提供给用户以提高性能 HTML 文件最终会变得陈旧 需要删除 我正在讨论两种编写驱逐脚本的方法 第一个是使用单个 find 命令 例
  • 使用 ServiceWorker 缓存 iframe 请求

    我正在尝试使用 ServiceWorker 缓存 iframe 的请求 使用 sw toolbox js 但无论我如何尝试 正如 Chrome Network Tab 告诉我的那样 ServiceWorker 永远不会提供文件 这是我的 s
  • ElasticSearch 5:带有 multi_field 的 MapperParserException

    此映射已在 ES 2 X 中运行 现在在 ES 5 中出现异常 type1 properties name type multi field fields name type string index analyzer standard i
  • Soap 错误,编码:对象没有“RecordId”属性

    我正在在线制作一个连接到肥皂网络服务的注册表单 并且应该能够通过它保存付款信息 PHP是这样的 function create member fee wsdl WSDL LOCATION client new mySoap wsdl arr
  • 如何在 NASM 汇编中进入 32 位保护模式?

    我正在学习 x86 汇编 并且正在尝试在 NASM 中制作一个玩具操作系统 但我不明白一些东西 我制作了一个成功启动内核的引导加载程序 从包含内核文件的软盘中加载 14 个扇区 在这些标记为的扇区中搜索文件kernel feo 将该文件加载