标签: osdev

为什么启用/禁用A20线路

我对A20门有疑问.我读了一篇关于它的文章,说这个机制存在解决地址"环绕"的问题,当新的CPU获得32位地址总线而不是旧的20位总线时出现.

在我看来,处理环绕的正确方法是关闭所有位A20-A31,而不仅仅是A20.

为什么只关闭A20位才能解决问题呢?

memory x86 osdev

6
推荐指数
1
解决办法
1260
查看次数

为什么在从实模式切换到保护模式之前需要禁用中断?

我在许多ose(和一些引导程序)中看到,它们cli在从实模式切换到保护模式之前都禁用了interrupt().为什么我们需要这样做?

x86 operating-system real-mode osdev bootloader

6
推荐指数
1
解决办法
1049
查看次数

Write :: write_fmt在裸机上无法正常工作

在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_64aarch64.

我的代码使用相同的代码生成选项构建.libcore版本对应于我的编译器.有问题的代码在这里.你能说出问题的可能原因吗?

重要更新:

是更精简的代码.libcore是在项目中构建的,因此一切都在掌控之中.上面的循环来自机器重启.代码完全适用于aarch64,但奇怪的是在x86_64上的Write :: write_fmt内崩溃.仔细检查我的启动程序集 - 似乎没有错误.

osdev rust

6
推荐指数
1
解决办法
312
查看次数

创建一个用grub2加载的简单多引导内核

我试图按照这里的说明构建一个简单的操作系统内核: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)

x86 assembly kernel grub osdev

6
推荐指数
1
解决办法
2365
查看次数

将完整操作系统编写为UEFI应用程序的现实性

从技术上讲,UEFI提供了许多功能,这些功能是操作系统(如文件系统)的基础工作的一部分.在某种程度上,遗憾的是,在启动过程中,操作系统会丢弃并重新实现所有内容.

我想知道将多少功能操作系统作为简单的UEFI应用程序实现它.我在考虑图形用户界面和多任务处理.使用FAT32作为文件系统,UEFI应该可以用于很多目的,因此应该标识映射内存.它将构成一个非常精简和简单的操作系统,甚至可能无需安装.

问题是在完全成熟的操作系统的功能方面提供什么是可行的限制?有什么建议?

c osdev uefi

6
推荐指数
1
解决办法
311
查看次数

对_GLOBAL_OFFSET_TABLE_的未定义引用(仅在生成二进制文件时)

这就是问题:
当我用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脚本:
获取二进制文件: …

linker gcc linker-errors ld osdev

6
推荐指数
1
解决办法
3262
查看次数

BIOS 和地址 0x07C00

来自维基百科

在 IBM PC 兼容机器上,BIOS 选择一个引导设备,然后将设备的第一个扇区(可能是 MBR、VBR 或任何可执行代码)复制到内存地址 0x7C00 处的物理内存中

我正在阅读操作系统中的引导过程,尤其是英特尔 x86:所以我发现将引导加载程序BIOS的第一个512 bytes加载到内存位置

(segment, offset) = (0x0000,0x7C00) = 0x07C00
Run Code Online (Sandbox Code Playgroud)

并跳转到那里执行引导加载程序

我的问题是为什么BIOS总是加载

引导程序到0x07C00

x86 operating-system intel bios osdev

6
推荐指数
1
解决办法
701
查看次数

Linux内存分段

在研究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开始,但是那些标志分别是0xc09b0xa09b等等?我倾向于认为它们是段选择器,如果不是,那么如果它们的寻址空间都从0开始,我将如何从内核段访问userland段?

不使用细分。仅使用分页。段的seg_base地址设置为0,将其空间扩展到0xFFFFF,从而提供完整的线性地址空间。这意味着逻辑地址与线性地址没有区别。

另外,由于所有段彼此重叠,提供内存保护(即内存分离)的是分页单元吗?

分页提供保护,而不是分段。内核将检查线性地址空间,并根据边界(通常称为 …

linux x86 osdev linux-kernel memory-segmentation

6
推荐指数
1
解决办法
242
查看次数

无法修改数据段寄存器。尝试时抛出一般保护错误

我一直在尝试按照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)

x86 assembly gcc osdev isr

6
推荐指数
1
解决办法
264
查看次数

为什么 BIOS 需要在第二条指令中将(看似)随机地址中的值与零进行比较?

我试图通过深入了解操作系统的低级细节来学习它。我现在上的课程是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到特定的地方?我在网上搜索了一段时间,但仍然找不到解释。更棘手的是,我发现不同的人会得到不同的地址来进行比较,比如0x6ac80x65a4或者0x6c48这里。

x86 assembly qemu bios osdev

6
推荐指数
1
解决办法
187
查看次数