实模式BIOS例程和保护模式

11

我正在进行一些操作系统实验。到目前为止,我所有的代码都利用了实模式BIOS中断来操作硬盘和软盘。但是一旦我的代码启用了CPU的保护模式,所有的实模式BIOS中断服务程序将不可用。我该如何读写硬盘和软盘?现在是否需要编写硬件驱动程序?我该如何开始?这是OS开发如此困难的原因之一吗?

我知道硬件都是通过读取和写入特定的控制或数据寄存器来控制的。例如,我知道硬盘的命令块寄存器范围从0x1F0到0x1F7。但我想知道是否PC平台上许多不同硬件的寄存器地址都相同?还是必须在使用它们之前检测它们?如何检测它们?

由于我不确定如何在保护模式下读写软盘或硬盘,所以现在必须使用BIOS中断将所有必要的内核文件从软盘加载到内存中。但是如果我的内核文件超过了实模式1M空间限制,我该怎么办?

感谢任何回答!

更新

我模糊地记得有一种方法可以先切换到保护模式,然后再切换回实模式。然后我们可以在保护模式下使用BIOS例程。也许我记错了。是否有人记得正确的方法?


很多问题,只为一个问题。而且我没有看到任何与C或C++语言有关的问题。 - Jens Gustedt
嗨,Jens。我只是为了更引人注目而打上了C/C++的标签。现在我已经删除了它。很抱歉打错标签了。 - smwikipedia
9个回答

5
尽管可以在保护模式和实模式之间切换,但这几乎肯定不是你想做的。这就是在286上的做法(相当笨拙,因为它没有故意支持从保护模式切换回实模式)。然而,从386开始,它们添加了一个V86模式,可以在保护模式下作为任务运行。
如果你想从保护模式使用BIOS,这几乎肯定是处理方式。你基本上可以创建一个V86任务,切换到它来使用BIOS,然后切换回另一个任务来执行保护模式代码。
如果你想玩一下,你可能需要看一下DJGPP,它是一个DOS扩展程序(基本上像我刚才描述的程序一样,根据需要处理进出V86任务的切换以处理磁盘I/O等),以及一个相当旧的gcc版本的端口,这样你可以编写在其上运行的代码。
商用DOS扩展程序市场现在基本上已经消失,所以至少一个曾经商业化的DOS扩展程序(HX)现在可作为开源软件使用。如果你想要使用它,你可能需要与OpenWatcom编译器一起使用。
编辑:至于如何读取超过1MB(例如)的文件,它很简单但笨拙:分块读取数据,当你完成读取时,你要么重新映射内存,要么复制内容,将你读到的内容放到你真正想要的位置,然后再读取另一个块。

就与硬件交流而言:很大程度上取决于您是想要一些基本工作的东西,还是想要充分利用当前硬件。仅使用基本的IDE端口将使您能够与几乎任何不是非常古老的硬盘进行通信--但是充分利用硬件需要更多的工作。 IDE / ATAPI驱动器使用了大约半打不同的DMA模式,每种模式都需要稍微不同的设置。其中相当多的模式可能已经过时,您可能只想直接支持最新的几个,并对其他任何内容回退到基本(非DMA)传输。


2
如果您使用传统的集成开发环境,所有硬件都会以相同的方式进行通信 - 您不必担心编写自定义驱动程序(尽管如果您走得够远,您会发现即使它们都声称遵循相同的规范,它们也有其有趣的怪癖)。 http://www.t13.org/http://www.t10.org是您可以找到相关规范的地方 - 如果您感到勇敢,您还可以编写SATA驱动程序 - 您可以在英特尔网站上找到AHCI规范(http://www.intel.com/technology/serialata/ahci.htm)。

2
似乎您的问题并不是如何与硬件进行交互(设备驱动程序可以解决这个问题),因为BIOS接口已经足够满足您的需求。
相反,您需要了解如何在特权级0(具有对BIOS调用和所有其他特权指令的无限制访问权限)和通常存在应用程序代码的特权级3之间进行通信。这就是所谓的“系统调用”。几乎所有体系结构都在特权模式下运行中断处理程序,因此软件中断是实现系统调用的一种方式,x86还提供了一个优化此目的的"syscall"指令。
当然,您也可以在平坦内存模型下在特权级0中运行所有内容,以便可以直接访问所有内存。
Ring 0/Ring 3是x86的术语,但所有带有MPU的系统都支持特权模式的某些概念,允许通过物理地址(在分裂的内存-I/O架构中,访问所有I/O空间)访问内存。

3
我认为 BIOS(一个16位接口)即便在0特权级下也无法在32位模式下工作。 - Greg Hewgill
不幸的是,你不需要拥有一个“产品”就可以获得专利。并不是说康柏公司没有这样做过,只是获得专利并不一定需要拥有产品。 - paxdiablo

1

V86:是的,这是正确的方法,但如果要设置操作系统:

可以尝试这个(设计用于长模式,但应该可以工作。我还没有测试过,但我看不出为什么它不会工作。问题不在nasm上,而在ld上。)

LD H8s 16位ELF / aout引用。从GRUB加载是标准做法。

我知道32位CS有问题,我需要再检查一下位置。否则看起来还可以。

这是难以找到的代码。

- - ;修改为32位??

;this code is placed somewhere after 10000h
;-----we're in LONG MODE-----
  mov          dword [.stckptr], esp   ;first of all save stack
  sgdt         [.gdtv32]               ;save your gdt pointer
  lgdt         [.gdtv16]               ;load a new one
  sidt         [.idt32]                ;save your idt pointer
  lidt         [.idt16]                ;load real mode idt
  ;far jump in long mode is not possible, do a trick
  push         DESC_REAL
  push         @f-10000h               ;this is CS*10h, modify if needed!
  retfd
.stckptr:
  dd           0
  align        16
.gdtv32:
  dw           0
  dd           0
  align        16
.gdtv16:
  dw           .gdtend-.gdt-1
  dd           .gdt,0
  align        16
.gdt:
  dd           0,0                      ;null descriptor
DESC_DATA=8                                 ;descriptor in YOUR GDT (modify)
DESC_LONG=$-.gdt
  dd           00000000h,00209800h      ;32 bit  mode cs -MOD ME
DESC_REAL=$-.gdt
  dd           0000FFFFh,00009801h      ;16 bit real mode cs (modify base if needed!)
.gdtend:
  align        16
.idt32:
  dw           0
  dd           0
  align        16
.idt16:
  dw           3FFh
  dd           0
  USE16

;-----we're in COMPATIBLITY MODE-----
  ;disable paging and protmode at once
@@:   mov          eax, cr0
  and          eax, 7FFFFFFEh   
  mov          cr0, eax

  ;set up real mode segment registers and stack
  mov          esp, realmode_stack_top          ;modify it to your needs!
  xor          ax, ax
  mov          ds, ax
  mov          es, ax
  mov          fs, ax
  mov          gs, ax
  mov          ss, ax
  ;convert long mode rip to real mode cs:ip
  ;jmp CS:(pmode address)-CS*10h

  jmp          1000h:@f-10000h                  ;modify if needed!
;-----we're in REAL MODE-----
@@:   ;***********call some BIOS interrupt here**********
  mov          ax, 3
  int          10h


  ;switch back to long mode
  mov          eax, cr0
  or           eax, 80000001h
  mov          cr0, eax                         ;enable protmode and paging

  ;jmp         DESC_LONG:@f
  db           66h
  db           0EAh
  dd           @f
  dw           DESC_LONG
  USE32
;-----we're in protected MODE-----
@@:   lgdt         [cs:.gdtv32]                    ;restore gdt
  mov          ax, DESC_DATA                   ;read YOUR DATA descriptor to selectors
  mov          ds, ax
  mov          es, ax
  mov          fs, ax
  mov          gs, ax
  mov          ss, ax
  lidt         [.idt32]                        ;restore idt
  mov          rsp, qword [.stckptr]           ;restore stack
  ;must be a non rip-relative jump
  mov          eax, @f
  jmp          eax
@@:

  ;AS WE WERE!

1
如果您想要在32位模式下读取硬盘(或USB键)的代码,可以从我的操作系统项目 PwnOS 找到一些。它不支持DMA或其他任何东西,但是基本功能可用。具体而言,trunk/Core/IO/ATA Driver.asm 包含了读取ATA设备(例如硬盘)的代码,没有魔法数字!:D
我决定不编写写设备的代码,因为我不想冒险,但是它们非常相似。规格可以在“cottontail os dev”的第一个谷歌搜索结果中找到(您将需要ATA/ATAPI-6文档),但是它们有点难以理解。
如果您还有任何问题,请随时问我。我也有代码可以设置为64位模式,并且专门为操作系统开发设计的汇编语言编辑器(搜索Inventor IDE),它具有在定义的地址和文件偏移处内置16位、32位和64位代码的组装和链接。这样,您可以专注于您感兴趣的部分,而不必关注其余的内容。

1

这里有一组关于保护模式的教程在这里。Tut15和tut16能够在v86模式下有效地运行DOS和BIOS,所有的中断都能够正常工作。


0
如果你在编写一个操作系统,那么这个系统需要为它需要使用的所有硬件都编写设备驱动程序,包括存储设备。如果你想要避免在这个早期阶段就需要驱动程序,那么你可以考虑使用像grub这样的现有引导程序,但最终你还是需要它们的。

0

这个项目现在已经很老了,但犹他大学的OSKit项目——可能仍然可以在现代机器上运行,因为其他晚于1990年代的操作系统仍然可以找到今天PC上的RAM和磁盘驱动器?——它是一个设备驱动程序堆栈单独构建,不依赖于任何特定的操作系统,因此您可以通过编写C代码来开发自己的内核。

它非常不错;您可以将C中的“Hello,world”编译成OSKit,并获得一个可以启动并打印“Hello,world”的操作系统,然后停止。 :-)

无论如何,如果您真的在进行“操作系统实验”,您可能希望尝试它- - 或者至少使用它的代码作为如何启动和运行某些驱动程序的指南。当然,如果您真的正在进行更少的“操作系统实验”而更多地了解x86的“鲜为人知的事实”,那么它可能会做得比您想要的更多。 :-)

http://www.cs.utah.edu/flux/oskit/


嗨,Brandon。你给了我一个很好的线索!我会去看看那个网站。也许它可以帮我减轻大量不同设备驱动程序的负担。我的重点是操作系统框架和一些主要部分,如进程、调度、内存管理、文件格式、文件系统等。至少现在不涉及设备驱动程序。 - smwikipedia
希望你能发现这个项目仍然可以编译!我希望它能继续得到维护,因为在裸机上运行C程序的能力是非常酷的,如果你想要获取一台旧机器并将其编程以快速执行某些单一任务而不需要操作系统“干扰”的话。 - Brandon Rhodes
1
这有点不错;你可以使用C语言编译器针对OSKit编译“Hello, world.”,然后得到一个可以启动的操作系统,它会打印“Hello, world.”并停止运行。 :-)当时Linus的妹妹站在那里,只是说了声“好”,因为Linus无法向她解释,真正的成就不在于显示的内容,而在于他完成任务的方式。 - Windows programmer

-1

ATAPI设备使用相同的端口。您可以模拟DPMI以克服1MB + 64k的限制,但是,学习保护模式是更好的选择。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接