我对A20门有疑问.我读了一篇关于它的文章,说这个机制存在解决地址"环绕"的问题,当新的CPU获得32位地址总线而不是旧的20位总线时出现.
在我看来,处理环绕的正确方法是关闭所有位A20-A31,而不仅仅是A20.
为什么只关闭A20位才能解决问题呢?
我在许多ose(和一些引导程序)中看到,它们cli在从实模式切换到保护模式之前都禁用了interrupt().为什么我们需要这样做?
在x86_64体系结构上,write!宏使用字符串参数按预期工作,但不使用整数.当使用整数参数时,我得到一个奇怪的循环(例如write!(writer, "Hello {}!", 123)产生无限的"Hello Hello Hello ...").在aarch64上,write!宏根本不起作用.
我使用以下命令构建libcore:
rustc -C opt-level=3 -Z no-landing-pads -C no-stack-check \
--crate-type rlib --target {arch}-unknown-linux-gnu lib.rs
Run Code Online (Sandbox Code Playgroud)
{arch}分别在哪里x86_64或aarch64.
我的代码使用相同的代码生成选项构建.libcore版本对应于我的编译器.有问题的代码在这里.你能说出问题的可能原因吗?
重要更新:
这是更精简的代码.libcore是在项目中构建的,因此一切都在掌控之中.上面的循环来自机器重启.代码完全适用于aarch64,但奇怪的是在x86_64上的Write :: write_fmt内崩溃.仔细检查我的启动程序集 - 似乎没有错误.
我试图按照这里的说明构建一个简单的操作系统内核:http://mikeos.sourceforge.net/write-your-own-os.html
除了从软盘启动,我想创建一个基于grub的ISO映像并在模拟器中启动多重启动CD.对于多引导头,我已将以下内容添加到该页面上列出的源:
MBALIGN equ 1<<0 ; align loaded modules on page boundaries
MEMINFO equ 1<<1 ; provide memory map
FLAGS equ MBALIGN | MEMINFO ; this is the Multiboot 'flag' field
MAGIC equ 0x1BADB002 ; 'magic number' lets bootloader find the header
CHECKSUM equ -(MAGIC + FLAGS) ; checksum of above, to prove we are multiboot
section .multiboot
align 4
dd MAGIC
dd FLAGS
dd CHECKSUM
Run Code Online (Sandbox Code Playgroud)
我正在做以下事情来创建图像:
nasm -felf32 -o init.bin init.s
cp init.bin target/boot/init.bin
grub2-mkrescue -o …Run Code Online (Sandbox Code Playgroud) 从技术上讲,UEFI提供了许多功能,这些功能是操作系统(如文件系统)的基础工作的一部分.在某种程度上,遗憾的是,在启动过程中,操作系统会丢弃并重新实现所有内容.
我想知道将多少功能操作系统作为简单的UEFI应用程序实现它.我在考虑图形用户界面和多任务处理.使用FAT32作为文件系统,UEFI应该可以用于很多目的,因此应该标识映射内存.它将构成一个非常精简和简单的操作系统,甚至可能无需安装.
问题是在完全成熟的操作系统的功能方面提供什么是可行的限制?有什么建议?
这就是问题:
当我用C语言链接我的脚本时,使用ld,当我在ld中生成elf32-i386文件作为输出格式,将它作为OUTPUT_FORMAT()放在ld脚本中时,我没有任何错误,但如果我尝试放入最后一个OUTPUT_FORMAT()"二进制"或尝试输出扩展名为.bin的文件,我得到的错误如下:
kernel.o: In function `k_main':
kernel.c:(.text+0xe): undefined reference to `_GLOBAL_OFFSET_TABLE_'
kernelutils.o: In function `k_clear_screen':
kernelutils.c:(.text+0xc): undefined reference to `_GLOBAL_OFFSET_TABLE_'
kernelutils.o: In function `k_clear_screen_front':
kernelutils.c:(.text+0x56): undefined reference to `_GLOBAL_OFFSET_TABLE_'
kernelutils.o: In function `k_printf':
kernelutils.c:(.text+0xa0): undefined reference to `_GLOBAL_OFFSET_TABLE_'
kernelutils.o: In function `k_sleep_3sec':
kernelutils.c:(.text+0x152): undefined reference to `_GLOBAL_OFFSET_TABLE_'
kernelmalloc.o:kernelmalloc.c:(.text+0xc): more undefined references to `_GLOBAL_OFFSET_TABLE_' follow
Run Code Online (Sandbox Code Playgroud)
这不仅发生在编译特定脚本时,所有尝试使用ld链接的脚本,或gcc,因为这会调用ld,在尝试获取带扩展名为.bin的二进制文件时死掉.
当显示其中一个可执行文件的符号(上面输出中的kernel.o)时,我看到符号_GLOBAL_OFFSET_TABLE_未定义,最可怕的部分,上面错误输出中返回错误的所有函数都删除了它们的符号,这是nm输出:
cristian@mymethodman:~/Desktop/kernel/0.0.3/Archivos$ nm kernel.o
U _GLOBAL_OFFSET_TABLE_
U k_clear_screen
U k_clear_screen_front
00000000 T k_main
U k_malloc
U k_printf
U k_sleep_3sec
00000000 T __x86.get_pc_thunk.bx
Run Code Online (Sandbox Code Playgroud)
我怎么能解决这个问题?我将保留下面的链接器脚本以确保它不是.ld文件的问题,同时具有"获取elf"和"获取二进制"版本.提前致谢!
Ld脚本:
获取二进制文件: …
来自维基百科
在 IBM PC 兼容机器上,BIOS 选择一个引导设备,然后将设备的第一个扇区(可能是 MBR、VBR 或任何可执行代码)复制到内存地址 0x7C00 处的物理内存中
我正在阅读操作系统中的引导过程,尤其是英特尔 x86:所以我发现将引导加载程序BIOS的第一个512 bytes加载到内存位置
(segment, offset) = (0x0000,0x7C00) = 0x07C00
Run Code Online (Sandbox Code Playgroud)
并跳转到那里执行引导加载程序
我的问题是为什么BIOS总是加载
引导程序到0x07C00?
在研究Linux内部和内存管理时,我偶然发现Linux使用的分段分页模型。
如果我错了,请纠正我,但是Linux(保护模式)确实使用分页将线性虚拟地址空间映射到物理地址空间。由页面组成的线性地址空间,对于进程平面内存模型分为四个部分,即:
__KERNEL_CS);__KERNEL_DS);__USER_CS);__USER_DS);存在第五个内存段,称为Null段,但未使用。
这些段的CPL(当前特权级别)为0(主管)或3(用户区域)。
为简单起见,我将集中讨论32位内存映射,其中4GiB可寻址空间,3GiB用于用户空间进程空间(以绿色显示),1GiB用于主管内核空间(以红色显示):
因此红色的部分由两个部分__KERNEL_CS和组成,__KERNEL_DS绿色的部分由两个部分__USER_CS和组成__USER_DS。
这些段彼此重叠。分页将用于用户空间和内核隔离。
但是,从Wikipedia 此处提取:
daccess-ods.un.org daccess-ods.un.org许多32位操作系统都通过将所有段的基数都设置为0来模拟平面存储器模型,以使分段对程序无关。
在这里查看GDT的linux内核代码:
[GDT_ENTRY_KERNEL32_CS] = GDT_ENTRY_INIT(0xc09b, 0, 0xfffff),
[GDT_ENTRY_KERNEL_CS] = GDT_ENTRY_INIT(0xa09b, 0, 0xfffff),
[GDT_ENTRY_KERNEL_DS] = GDT_ENTRY_INIT(0xc093, 0, 0xfffff),
[GDT_ENTRY_DEFAULT_USER32_CS] = GDT_ENTRY_INIT(0xc0fb, 0, 0xfffff),
[GDT_ENTRY_DEFAULT_USER_DS] = GDT_ENTRY_INIT(0xc0f3, 0, 0xfffff),
[GDT_ENTRY_DEFAULT_USER_CS] = GDT_ENTRY_INIT(0xa0fb, 0, 0xfffff),
Run Code Online (Sandbox Code Playgroud)
正如Peter所指出的,每个段都从0开始,但是那些标志分别是0xc09b,0xa09b等等?我倾向于认为它们是段选择器,如果不是,那么如果它们的寻址空间都从0开始,我将如何从内核段访问userland段?
不使用细分。仅使用分页。段的seg_base地址设置为0,将其空间扩展到0xFFFFF,从而提供完整的线性地址空间。这意味着逻辑地址与线性地址没有区别。
另外,由于所有段彼此重叠,提供内存保护(即内存分离)的是分页单元吗?
分页提供保护,而不是分段。内核将检查线性地址空间,并根据边界(通常称为 …
我一直在尝试按照James Molloy 的本教程创建一个 ISR 处理程序
,但我被卡住了。每当我抛出软件中断时,通用寄存器和数据段寄存器都会被推送到堆栈中,而 CPU 会自动推送变量。然后将数据段更改为 0x10(内核数据段描述符)的值,从而更改权限级别。然后在处理程序返回后,这些值被pop编辑。但是,无论何时ds更改中的值,都会抛出错误代码为 0x2544 的 GPE,几秒钟后 VM 会重新启动。(链接器和编译器 i386-elf-gcc ,汇编器 nasm)
我尝试在hlt指令之间放置指令以定位哪个指令正在抛出 GPE。在那之后,我能够找到“mov ds,ax”指令。我尝试了各种方法,例如删除由引导程序代码初始化的堆栈以删除代码的权限更改部分。我可以从公共存根返回的唯一方法是删除更改权限级别的代码部分,但是当我想转向用户模式时,我仍然希望它们保留。
这是我常用的存根:
isr_common_stub:
pusha ; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax
xor eax,eax
mov ax, ds ; Lower 16-bits of eax = ds.
push eax ; save the data segment descriptor
mov ax, 0x10 ; load the kernel data segment descriptor
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
call isr_handler
xor eax,eax …Run Code Online (Sandbox Code Playgroud) 我试图通过深入了解操作系统的低级细节来学习它。我现在上的课程是MIT 6.828 操作系统工程。实验室要求学生进入 BIOS 以获取一些说明。BIOS 的前三个汇编指令如下:
0xffff0: ljmp $0xf000,$0xe05b
0xfe05b: cmpl $0x0,%cs:0x6c48
0xfe062: jne 0xfd2e1
Run Code Online (Sandbox Code Playgroud)
我想不通的是第二个:cmpl $0x0,%cs:0x6c48. 它有什么作用?为什么需要这个条件才能jmp到特定的地方?我在网上搜索了一段时间,但仍然找不到解释。更棘手的是,我发现不同的人会得到不同的地址来进行比较,比如0x6ac8,0x65a4或者0x6c48这里。