I am doing some OS experiment. Until now, all my code utilized the real mode BIOS interrupt to manipulate hard disk and floppy. But once my code enabled the Protect Mode of the
If you're writing an operating system, it really needs to have device drivers for any hardware it needs to use, including the storage devices. If you want to avoid needing drivers at such an early stage, you could consider using an existing bootloader like grub, but eventually you'll need them anyway.
If you'd like code to read the harddrive (or USB key) from 32-bit mode, you can find some from my OS project PwnOS. It doesn't support DMA or anything, but the basics work. Specifically, trunk/Core/IO/ATA Driver.asm contains the code for reading an ATA device, e.g. harddrive (without magic numbers! :D)
I decided not to write the code for writing a device, since I didn't want to risk it, but it's very similar. The specs can be found at the first google hit for "cottontail os dev" (you'll want the ATA/ATAPI-6 document), but they're a bit hard to follow.
If you have any more questions about it, feel free to ask. I've got code to get set up into 64-bit mode as well, as well as an assembly language editor that I specifically designed for OS development (search for Inventor IDE), in that it has built-in assembling and linking of 16-bit, 32-bit, and 64-bit code at defined addresses and file offsets. That way, you can focus on the part you're interested in, instead of the fluff.
V86: yes the way to go, but if setting up an OS:
try this(designed for long mode but should work.I havent tested this YET, I see no reason it wont work yet.Issue is not nasm, its ld.)
LD H8s 16-bit ELF/aout references.This is standard to load from GRUB.
I kow the 32-bit CS is off, I need to double check its location. Otherwise it looks ok.
This is hard to find code.
-- ;modify for 32bit??
;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!
It seems that your question is not how to talk to hardware (the problem device drivers would solve) because the BIOS interfaces are sufficient for you.
Rather, you need to know how to communicate between protected mode ring 0 (which has unlimited access to BIOS calls and all other privileged instructions) and protected mode ring 3 where application code usually lives. This is a system call. Pretty much all architectures run interrupt handlers in privileged mode so a software interrupt is one way of implementing system calls, x86 also provides a syscall
instruction which is optimized for this purpose.
Of course, you could just run everything in ring 0 with a flat memory model where you can access all memory directly.
Ring 0/ring 3 is the x86 terminology, but all systems with an MPU support some concept of a privileged mode which allows access to memory by physical address (and for split memory-I/O architectures, access to all of I/O space).
ATAPI devices use the same ports. You can emulate DPMI in order to get past the 1MB+64k limit, but yeah, learn Protected Mode.
The project is now an old one, but the OSKit project at Utah — which might still run on modern machines, since other late-1990s operating systems can still find the RAM and disk drives on today's PCs? — was a device-driver stack build separately from any particular operating system so that you could develop your own kernel just by writing C code.
It was kind of neat; you could compile "Hello, world." in C against OSKit and get an OS you could boot that came up and printed "Hello, world." and then halted. :-)
Anyway, if you are really doing an "OS experiment", you might want to try it out — or at least use its code as a guideline for how to get up and running with some drivers. Of course, if you're really doing less of an "OS experiment" and more of a "learn obscure facts about x86", then it might do more than you want. :-)
http://www.cs.utah.edu/flux/oskit/