内核位于0x1000时,如何用C与汇编获取内存数据?
保护模式下C内核物理内存映射解决方案
一、流程结构调整(彻底规避内存覆盖)
重新规划各阶段内存布局,消除冲突:
- MBR(0x7C00):仅负责加载ASM内核到
0x10000(16KB位置,远离0x0600、0x1000等敏感区域),执行完成后直接跳转至ASM内核入口 - ASM内核(0x10000):按顺序完成3项核心操作:
- 在实模式下读取E820内存映射,存储到
0x20000(32KB位置,与所有加载区无重叠) - 加载C内核到原有地址
0x1000(保留现有配置,避免崩溃) - 切换到保护模式,传递内存映射的起始地址和条目数给C内核,再调用C内核入口
- 在实模式下读取E820内存映射,存储到
- C内核(0x1000):接收内存映射参数后,执行IDT加载、ISR安装等原有逻辑,再基于映射初始化物理内存管理器
二、E820内存映射汇编实现(无覆盖风险)
以下是实模式下可靠读取E820映射的汇编代码,直接嵌入ASM内核的实模式段:
; 读取E820内存映射,存储到0x20000 ; 返回参数:ecx=条目数,edi=映射起始地址(0x20000) e820_read: xor ebx, ebx ; ebx初始化为0,标记首次调用 mov edx, 0x534D4150 ; "SMAP" magic值,必须传递给int 0x15 mov edi, 0x20000 ; 映射存储起始地址,远离所有加载区 xor ecx, ecx ; 条目计数初始为0 e820_loop: mov eax, 0xE820 ; E820功能号 mov ecx, 24 ; 每个条目的标准大小(24字节) int 0x15 ; 调用BIOS中断获取映射条目 jc e820_done ; 进位标志置位表示出错,退出循环 cmp eax, 0x534D4150 ; 检查返回的magic值是否合法 jne e820_done add edi, 24 ; 移动指针到下一个条目存储位置 inc ecx ; 条目数+1 test ebx, ebx ; ebx为0表示无更多条目 jne e820_loop e820_done: ; 切换保护模式前,将ecx(条目数)和0x20000(映射地址)存入寄存器传递给C内核 mov ebx, ecx ; ebx暂存条目数 mov ecx, 0x20000 ; ecx暂存映射起始地址
三、C内核对接实现
- 定义与汇编匹配的E820条目结构体:
#include <stdint.h> typedef struct { uint64_t base_addr; // 内存区域起始物理地址 uint64_t length; // 内存区域长度 uint32_t type; // 类型:1=可用,2=保留,3=ACPI回收区等 uint32_t acpi_ext; // ACPI扩展字段(可忽略) } e820_entry_t;
- 定义C内核入口函数,接收汇编传递的参数:
// 用cdecl调用约定匹配汇编传参规则 void __attribute__((cdecl)) kernel_main(uint32_t e820_count, e820_entry_t *e820_map) { // 执行原有逻辑:加载IDT、安装ISR、设置PIT和键盘 // ... // 基于e820_map初始化物理内存管理器 for (uint32_t i = 0; i < e820_count; i++) { if (e820_map[i].type == 1) { // 标记该区域为可用物理内存 // ... } } // 后续逻辑 while (1); }
四、关键避坑点
- 绝对禁止在
0x0600、0x1000、0x7C00区域存储持久数据,这些区域会被BIOS或内核加载过程覆盖 - E820内存映射必须在实模式下读取,切换到保护模式后无法再调用BIOS中断
- 分页功能需在C内核完成内存映射解析和物理内存管理器初始化后再启用,过早启用会导致地址映射混乱
内容的提问来源于stack exchange,提问作者Tech
相关产品推荐
相关产品推荐

