Whew, I just wrote a ton. Feel free to skim over and answer parts of this post (thanks again) - I certainly took a few breaks while writing it. I'll divide it up again.
A lot of my questions this time are just out of curiosity...
In response to AJ
Memory
AJ wrote:When you boot, there is no IDT, as this is a protected mode structure that you need to create. You have the real mode IVT which is located from 0x0000 - 0x0500. If you have switched to protected mode this can be overwritten, but only if you do not want to later use real-mode interrupts in v86 mode.
So if I want my OS to be able to emulate DOS to run old DOS programs, for example, I shouldn't touch that once I get into protected mode. BTW, I think I meant IVT, not IDT
Out of curiosity, if I disable interrupts (like, for enabling A20), supposing I have already loaded all the necessary code to do drive/FS access, since the drive accesses use PIO, I don't need to re-enable interrupts, right? (Well, even if they were calls to BIOS, those interrupts still work since they're non-maskable, I think).
If that's the case, these drive operations will load the kernel, and then I just have to switch to protected mode (at which point, you say, it's safe to mess with the IVT), which involves disabling interrupts anyway.
So I'm saying that it seems possible to engineer my code so that once I disable interrupts, I no longer need to re-enable them until protected mode. Can I mess with the IVT at that point (i.e, in real mode with no maskable interrupts - supposing I no longer need any non-maskable interrupts)? Or will that cause problems still? Just curious
AJ wrote:Then, from 0x0500 - 0x90000 (? - this varies from machine to machine), you have free RAM. From this point to 0x100000 (1MB) is the Bios Data Area (BDA) which you must not overwrite, even if you change to PMode.
At 0xB8000 is 80*25 text-mode video RAM. Each word contains a character code and a colour code. (There are other video areas at 0xB0000 and 0xA0000, I believe, but I have never used them).
What is the function of the BDA? What happens if I overwrite it?
So video area is in the middle of the BIOS area? Hmm, I wonder what those other memory areas do... I guess I shouldn't worry about that until I start working on the kernel.
AJ wrote:Sometimes, there will be data such as VESA interfaces loaded higher in RAM.
In theory, when you boot, you should not assume anything about memory. Your boot loader is, of course, loaded at 0x7C00 - 0x7DFF. Have a look at bios function 15
http://www.ctyme.com/intr/rb-1741.htm for more information about this.
I'm a little confused about this, two things:
1) If I cannot assume anything about memory, then how do I know, for example, that I can load a larger, second stage to that area you said is usually free RAM? (also, an aside, does anyone know the historical/backwards compatibility reasons for loading the bootsector to 0x7C00? Why not 0x0500, the start of the free RAM?)
2) The Ralph Brown page you linked, it says to set EAX, EBX, EDX, ECX etc... but I'm not in 32-bit protected mode yet! And I thought you could no longer use the BIOS interrupts from protected mode? Aren't these 32-bit registers??
Disks
AJ wrote:Although I know nothing about EXT2, if you need to execute more code than you have available in your own boot loader, you have 3 options:
1. Directly load the second sector of the disk, having formatted the disk so that the BPB shows more than one reserved sector.
2. Make your first stage boot loader simply read the file system and execute your second stage loader - nothing else (this is certainly possible in 510 bytes with FAT).
3. Use GRUB - you can still write a pre-kernel secondary bootloader which takes over from GRUB and does some system initialisation before your kernel starts up.
Method #1: Sorry, BPB? Wikipedia gives me an idea of what this is, but I haven't yet seen this used, can you point me somewhere?
From context it seems like you're saying I can use the BPB to say "I'm going to reserve these first few blocks of the hard drive for boot code, so don't use it for filesystem data."
however... it seems like (from wikipedia), that the BPB is a filesystem feature of FAT file systems and NTFS (and not EXT2), which makes sense because all the documentation of EXT2 says that the superblock starts at offset 1024 always.
That means I could safely use the second 512 bytes of the drive,
but what if I want to support more filesystems? Is it a safe assumption that all filesystems will have some mechanism (i.e, a BPB or simply a specification) where I can reserve the first 1024 bytes? (ok maybe not
all filesystems - surely older ones never thought we'd need more than 512 bytes for booting - but at least newer filesystems?)
Method #2: That's what I was originally thinking, but I highly doubt I can fit an EXT2 read implementation in 510 bytes (though I haven't tried yet, I'd hate to find out that I really can't the hard way).
Method #3: I think I will end up using Grub in the long run, but I'd like to take this challenge on for my own learning purposes
AJ wrote:Hope some of this rambling helps!
Definitely! Thank you
so much for taking the time to respond!
In response to mathematician
Disk
mathematician wrote:The BIOS passes the drive number to the program loaded onto the boot sector of a floppy in the dl register. On a hard disk the program loaded from the master boot record similarly gets the drive number passed to it in the dl register. The BIOS knows nothing about partitions, therefore the drive number in dl always refers to a physical drive. Whether on a floppy or hard disk, the BIOS always reads the first sector of track 0 on the physical disk.
As you say, there is precious little room for anything very much in a boot sector. If you disassemble an MBR you will find it using the classic CHS addressing scheme of int 13h fn 2. One solution to the LBA problem is to reserve the whole of track 0, head 0, for a program which will implement LBA extensions to the BIOS.
I was pretty sure about the first part of what you said, and now I'm certain (thanks).
Sorry, I haven't been paying much attention to CHS as I've been reading that most modern hardware should support LBA in some form... but from what I (think I) know, wouldn't that mean, I'd be asking for 512*(63 SectersPerTrack) / (1024 bytes/killobyte) = 31.5 killobytes?? That seems like plenty but what about the filesystem? The EXT2 superblock starts at offset 1024!
mathematician wrote:It is the MBR's job to scan the partition table for the entry marked as active, and then load the boot sector for that partition; passing in DS:SI a pointer to the relevant partition table entry.
Okay. I knew that about what the MBR is supposed to do, so what I'm focusing on right now is the second part of that (the bootsector of the partition - so I don't have to worry about the partition table and limiting myself farther to 446 bytes!)
DS:SI is a pointer to memory, right? so they point to the 16-byte data structure in memory, correct? And I should have my bootloader copy necessary information from that data structure before accessing more memory (so that I don't overwrite). Am I understanding that right?
Partition Tables
mathematician wrote:Hard drives always have a partition table, even if there is only one partition on the disk.
I'm not very familiar with the format of a partition table, so I'm going to pause right here to go educate myself...
All right, so in a system with a partitioned drive where my code is located on the first sector of a bootable partition, I don't need to worry - but you're saying that in a system with one disk with only one partition, the partition table still must exist... so that means either:
1) The partition table indicates that the first partition takes up the entire drive (starting at the very first sector, ending at the very last and thus the MBR of the drive = the bootsector of the partition) or
2) The partition table indicates that the first partition takes place somewhere after the first sector and ends at the last.
Which brings me to a few questions about partition tables:
Is method number 1, listed above, even possible? can the partition table include the sector that it's in, in a partition?
Method 2 makes more sense to me, because then there's a seperate place for the bootcode in the MBR and the bootloader in the first sector of the partition. Is that the idea?
I tried to come up with the values for the fields in the partition table for a 1GB hard drive with only one partition, and I got this far:
Code: Select all
partition 1:
0x80 // bootable
0x00 // first head
0x0000 // first cylinder and sector (C=0, S=0)
(arbitrary(?) byte) perhaps 0x83 // linux native partition system ID
0x8B // ending head (decimal 139)
0x2083 // ending cylinder and sector (C=131, S=8)
0x???????? // "relative sector to start of partition" question... (see below)
0x00200000 // 1 GB = 1073741824 bytes / 512(bytes/sector)=2097152 sectors
Ok, first of all, what exactly does the "relative sector" field do? I can find plenty of examples of partition tables but this field seems obvious to everyone else (heh..). Maybe it's the wording that's throwing me off, but why would partition 1 need to define it's relative sector
to the partition (itself???). At first when I saw "relative sector" I thought "offset", which is why I set the first CHS values to 0, thinking that putting an offset later would cause the partition not to overlay the MBR...
Which brings me to my next question: supposing, from the word "relative", that this field offsets the CHS values in any way - what happens as you approach the limit of the CHS values? Aren't they also limited by the hardware or does it convert to LBA?
How does it happen that I can partition a 100 GB hard drive into 50 GB partitions when the limit for CHS if 8.4 GB's?
Anyway, if it is the case that I can offset partitions, then I can see how the MBR can request all of head 0, track 0 without disturbing the filesystem...
Last question about partition tables: suppose, as it were, partition 1 takes up the entire drive. Do most OS's notice this and ignore the rest of the partition table (since, logically, there is no room for any more partitions)? I suppose this isn't very important since the MBR can't know anything about the partition table beforehand and therefore can't use it's space anyway... But I'm just curious, what would the partition table entries look like for partitions 2, 3, and 4?
mathematician wrote:On a floppy DS:SI points to nothing in particular. The partition's boot loader must either load the second stage of a chain loader, or else load the kernel. In a multi-boot system it has to be the former.
The only real option is to reserve a fixed location on the partition for the second stage of a chain loader, or for a kernel file. In the available 512 bytes of the first stage loader there is no possibility of being able to parse the file system.
Obviously, it's possible with a pretty simple file system, but I'm fairly certain it's not going to be possible with EXT2 (or if it is, it's going to be really difficult, especially as a first real bootloader). That being said, up until now I've been afraid of just loading a fixed location because I thought it'd interfere with the filesystem, but the answer to my partition questions might be the answer here as well.
Booting from different types of drives
mathematician wrote:Since the BIOS only knows about physical drives, 80h always refers to the master on IDE cnannel 1, 81h to the slave on IDE channel 1, 82h to the master on IDE channel 2 and 83h to the slave on IDE channel 2. The programming interfaces for PATA an SATA drives are thankfully identical.
Exactly what I was looking for, Thank you!
mathematician wrote:Grub is a chain loader which will load your kernel for you. Therefore if you use grub there is no need to worry about having to write your own loader. But if you do, there is nothing to say that the loaders on a floppy or hard disks have to be identical. When at some stage, probably well down the line, you write your disk formatting program it could determine which loader was the more appropriate.
I was toying around with that idea too, I thought it'd be really convenient to have one bootloader, however, I suppose even grub's setup probably detects the type of drive and writes accordingly. This makes a lot of sense - I think for now I'll just worry about the hard drive, and perhaps later the CD-ROM or USB drives (though, I'm not sure if qemu's BIOS will boot from an emulated USB drive? I haven't tried).
Memory
mathematician wrote:In real mode the first kilobyte of memory is reserved for the interrupt vector table. Memory from 0x400 up to somewhere beyond 0x500 is reserved by the BIOS.
Now I feel kind of bad asking this (I always thought it sounded kinda rude in debates), but does anyone have a source? AJ said something similar but seemed to indicate that 0x500 was the start of free RAM.
mathematician wrote:After being loaded at 7C00, the MBR usually relocates itself to 0x600, so it should be safe for your code to reuse the same memory.
Now this question is just out of pure interest, and I actually asked it above, but: Why is 0x7C00
the place to load every piece of bootcode (both the MBR
and the bootsector of the partition)? Why isn't it standard to load the MBR to 0x600? and the bootsector to 0x800 or something? Surely that convention would save some code since the MBR wouldn't have to relocate itself?
mathematician wrote:Memory from 0xA0000 up to the end of the first megabyte is reserved first for the video buffers and, after that, for BIOS extensions.
Also out of interest (and also asked above, heh..), what exactly are the video buffers for that don't affect what you see on the screen?
mathematician wrote:The reserved area of memory mentioned above are well known, so they are not reported by int 15h, fn 0xE820, but that function can be used to get a map of memory used by any other memory mapped devices that may be lurking. The protected mode IDT is anywhere you want to put it.
Okay, this is another question I asked above - the int15h fn 0xE820 seems to use 32-bit registers, but I'm not in 32-bit mode? how's that?
Also, a memory map is obviously going to be dynamic and differ from system to system. Does this mean I must make the second stage position-independent code? Since I'm not sure I'll be able to load it to the same position every time? Anyone care to point me in the direction of a good tutorial on how to write position-independent code (or, which opcodes can I no longer use? or is that not the case?)
THANK YOU!
Thank you guys so much for your time, you've been a great help so far. I think the two biggest things keeping me from starting are my questions regarding partitions, and how I can use the BIOS call to get a memory map from real mode.
Oh, also:
AJ wrote:Have a look at
http://bos.asmhackers.net/ and download version 0.4. This contains a 512b boot loader which parses the FAT12 file system and loads the kernel. I am sure I have also seen this with FAT32. As for ext2 - I have no idea...
I think I can give some info here. I haven't looked into FAT very much. If I remember correctly, the design behind FAT file systems is kinda based on linked lists, right? It's probably a little more complicated than a simple linked list, I know, but that is the method of assigning parts of the drive to a file when it needs more space and it's a huge cause of fragmentation.
EXT2, on the other hand, is a surprisingly well designed file system, yet it's not
too complicated. It's built up of "groups", which contain "blocks" (not in the LBA sense), which may contain several different structures. As such, to implement EXT2, it takes more code to locate data on a disk, but it takes less disk access (= faster, which doesn't really matter in the context of a bootloader, but it's a great choice for a default file system).
I get the impression that fitting a FAT32 read implementation is possible, but it's not extremely easy or trivial - it sounds like there are some tricks (albeit tricks that one could probably figure out on their own) to it. If that's the case, then I cannot fathom how an EXT2 read implementation would fit in the same space.