查看“Rolling Your Own Bootloader”的源代码
←
Rolling Your Own Bootloader
跳到导航
跳到搜索
因为以下原因,您没有权限编辑本页:
您请求的操作仅限属于该用户组的用户执行:
用户
您可以查看和复制此页面的源代码。
{{rating|2}} Some people prefer to use their own software for everything, or wish to try their hand at coding a bootloader. This page attempts to describe what steps to take when you write your own bootloader. Before you start writing one, it is best that you know the background [[Bootloader|theory]]. == What and Why == === Disclaimer === Okay. You are here because you don't want to use a mainstream boot loader. You may also want to code your own bootloader as a learning experience to better understand how they function. We also have pages on many [[Bootloaders]] developed by this community, there's the [[Bare bones]] but still people complain we don't have a page explaining how the bootloader can be coded. I won't try to give you full code that works because if that was what you were looking for, you'd be using one of the [[:Category:Bootloaders|premade bootloaders]] instead. This page plans to tell you what is needed and what could be wished in a bootloader, and optionally points at parts of the FAQ that can help you achieving the goals. Whether or not you'll use your own bootloader or reuse an existing tool is completely up to you. If you get the feeling you don't understand a thing, make sure you read our page about the [[Boot Sequence]] first.<br /> A good reason to have a custom bootloader would be a custom filesystem. === What you need to do === The bootloader ultimately has to bring the kernel (and all the kernel needs to bootstrap) in memory, switch to an environment that the kernel will like and then transfer control to the kernel. As the scope of this article is protected mode C kernels, I'll assume that "an environment the kernel will like" means [[Protected Mode]], with kernel and additional components being stored at their 'favorite', compile-time known locations, with a wide-enough stack ready and BSS section cleared. === What you could wish to add === Since the bootloader runs in [[Real Mode]], it has easier access to BIOS resources and functions. Therefore it's a good place to perform [[How Do I Determine The Amount Of RAM|memory map detection]], [[Getting VBE Mode Info|detecting available video modes]], loading additional files etc. The bootloader will collect this information and present it in a way the kernel will be able to understand == Loading... Please wait... == === Where will you load your kernel? === You will have to decide where in memory you are going to load your kernel. Your kernel generally depends on it. In Real Mode, the easiest is to stay below the 1MB barrier, which means you practically have 512KB of memory to load things. You may wish the kernel to be loaded at a well-known position, say 0x10000 physical (es=0x1000, bx=0 when calling INT13h). If your kernel is bigger (or is expected to grow bigger) than this, you'll probably prefer to have the kernel above the 1MB barrier, which means you need to activate [[A20 Line|A20 gate]] and switch to [[Unreal Mode]] to load the kernel (with A20 alone, you cannot have more than 64K above 1MB). Note that BIOS will still be unable to write to memory above 1MB, so you need to read stuff in a buffer below 1MB and then perform a rep movsd to place the data where they ultimately should go. === How will you find your kernel? === The bits of your kernel are somewhere on some disk (presumably the [[Bootable Disk|booting disk]], but this is not mandatory). Question is: where on the disk? Is it a regular file on a [[FAT|FAT-formatted floppy]]? is it a collection of consecutive sectors in the "reserved area" of the FAT12 floppy (in which case you may need a dedicated tool to format the disk and install the kernel on it)? Or is the floppy simply left unformatted and kernel pasted directly with a [[Disk Images|disk image tool]]. All the above options are possible. Maybe the one I'd choose myself would be to reserve enough space on a FAT12 floppy to store the ''list of sectors'' used by the kernel file. The "advantage" of being fully-FAT12 is that you don't need to re-write the bootsector every time you rewrite the kernel. === What else could you need to load? === That mainly depends on what's in your kernel. Linux, for instance, requires an additional 'initrd' file that will contain the 'initialization process' (as user level). If your kernel is modular and if [[File Systems]] are understood by some modules, you need to load the modules along with the kernel. Same goes for 'microkernel services' like disk/files/memory services, etc. === What if I get beyond the 512 bytes of the boot sector? === Make sure the first 512 bytes are able to load the rest of your loader and you're safe. Some do this with a separate "second stage" loader, others by really inserting a '512-bytes' break in their ASM code, making sure the rest of the loader is put after the bootsector (that is, starting at 0x7e00). === What if I wish to offer the user the option to boot several OSes? === The easiest way to boot another OS is a mechanism called ''chainloading''. Windows stores something akin to a second-stage bootloader in the boot sector of the ''partition'' it was installed in. When installing Linux, writing e.g. LILO or GRUB to the ''partition'' boot sector instead of the MBR is also an option. Now, the thing your MBR bootsector can do is to ''relocate'' itself (copying from 0x0000:0x7c00 to, traditionally, 0x0060:0x0000), parse the partition table, display some kind of menu and let the user chose which partition to boot from. Then, your (relocated) MBR bootsector would load that ''partition'' boot sector to 0x0000:0x7c00, and jump there. The partition boot sector would be none the wiser that there already was a bootsector loaded ''before'', and could actually load yet ''another'' bootsector - which is why it's called ''chainloading''. It doesn't really matter where you decide to relocate the boot sector as long as you don't overwrite the [[IVT]] (if IF in EFLAGS is set), the [[Memory Map (x86)#BIOS Data Area (BDA)|BDA]] or the [[Memory Map (x86)#Extended BIOS Data Area (EBDA)|EBDA]]. You see that with displaying a menu in some intelligible way and accepting keystrokes, such a multi-option bootloader can get quite complex rather quickly. We didn't even touch the subject of booting from extended partitions, which would require sequentially reading and parsing multiple extended partition tables before printing the menu. Taken to the extreme, boot managers like that can become as complex as a simple OS (much like GRUB is, which offers reading from various filesystems, booting [[Multiboot]] kernels, chainloading, loading initrd ramdisks etc. etc. - such internals will not be addressed here. === How do I actually load bytes === [[BIOS]] interrupt 13h. Get info about it at [[Ralf Brown's Interrupt List]], make sure you know (the now outdated) floppies may fail one or two times, that you cannot read more than a track at once, you have to use CHS addressing and you're done. To read from the hard drive (which is the preferred way these days, also used by CDROMs and USB-sticks), you probably want int 13h, ah=0x42, drive number 0x80 that uses simple LBA addressing. Details in the interrupt list. If you need guidance, feel free to check [http://clicker.cvs.sourceforge.net/clicker/c32-lxsdk/kernel/src/sosflppy/lowlevel.asm?view=log lowlevel.asm] Note also that most [[File Systems]] involve some conversion between allocation units (blocks/clusters) and physical "Cylinder:Head:Sector" values. Those conversions are simple once you know the ''sectors-per-track'' and ''heads'' counts. Check out [http://www.nondot.org/sabre/os/articles OSRC] for additional info. This is only relevant for outdated floppies; everything else, like hard drives, CDROMs, USB-sticks all use the simple LBA addressing scheme. <pre> > Does anyone have a formula for converting DOS Sectors to > Physical Sectors (Head, Cylinder, Sector) such as used in > INT 13h? DOS_sector_num = BIOS_sector_num - 1 + Head_num*Sectors_per_track + Track_num*Sectors_per_track*Total_heads BIOS_sector_num = 1 + (DOS_sector_num MOD Sectors_per_track) BIOS_Head_num = (DOS_sector_num DIV Sectors_per_track) MOD Total_heads BIOS_Track_num = (DOS_sector_num DIV Sectors_per_track) DIV Total_heads </pre> If you're loading above 1MB, you should proceed in 2 steps: first using BIOS to load in the "conventional" area, and then performing a <tt>rep movsd</tt> to place the data where they ultimately should go. == Loaded. Gathering Information == The next step consist of collecting as much information as you can/need: [[How Do I Determine The Amount Of RAM|amount of installed RAM]], available [[Getting VBE Mode Info|video modes]] and things alike are easier to do in real mode, so better do them while in [[Real Mode]] than trying to come back to real mode for a trip later. Of course the exact requirements depend on your kernel. A very simple solution here is to organize your information as a flat table (ala [[BIOS|BIOS data area]]). An alternative could be to add those information as a structured flow: you keep an index at a well-known address (or at some address you'll pass to the kernel when loaded) and that index gives for each "key" the address of the corresponding data structure. E.g. <pre> organization lookup code (eax == signature) +------+------+ mov esi, well_known_index_address | RAM. | 1234 | .loop: | VBE. | 5678 | cmp [esi],'END.' | MODS | 9ABC | je .notfound | DISK | DEF0 | add esi,8 | END. | ---- | cmp [esi-4],eax +------+------+ jne .loop mov eax,[esi] ret </pre> == Ready. Entering [[Protected Mode]] ... == To enter protected mode you should first disable interrupts and set global descriptor table. After it set PE bit of CR0: <source lang="asm"> mov eax,cr0 or eax,1 mov cr0,eax </source> After it set registers and do a far jump to kernel. If data selector is 10h, code selector is 8 and kernel offset is 10000h do: <source lang="asm"> mov ax,10h mov ds,ax mov es,ax mov fs,ax mov gs,ax mov ss,ax jmp 8:10000h </source> Notes: * that in this case, the GDT will be ''temporary''. Indeed, the loader has no idea of what the kernel wants to do with the GDT, so all it can do is providing a minimal and let the kernel reload GDTR with an appropriate GDT later. * it's common for the loader to keep interrupts disabled (the kernel will enable them later when an IDT is properly set up) * give yourself the time about thinking whether you'll enable paging now or not. Keep in mind that debugging paging initialization code without the help of exception handlers may quickly become a nightmare! * it is possible to perform more initialization once protected mode is enabled and before kernel is loaded. This will, however, require that you mix 16 bits and 32bits code in a single object file, which can quickly become a nightmare too... * it is very likely that your kernel does not start with an executable code, rather it has an ELF or PE header at 10000h. You'll have to parse that to get the entry point to jump to. == You have long way to continue... == Now, you are very distant from using extern and call function for C Code. You will need to enable [[A20]], make something which reads images (so you can actually boot any .bin or .sys file), and so on. == Help I'm Stuck! == The only way to figure out what's wrong with your boot loader is to debug it in a VM. You could probably print out variables, but the limited space makes this uneccesairly hard. Also reading [[My_Bootloader_Does_Not_Work|common mistakes and gotchas]] may give you ideas about your issue. [[Category:OS Development]] [[Category:Tutorials]] [[Category:Bootloaders]] [[de:Eigener Bootloader]] == A list of things you might want to do == * Setup 16-bit segment registers and stack * Print startup message * Check presence of PCI, CPUID, MSRs * Enable and confirm enabled A20 line * Load GDTR * Inform BIOS of target processor mode * Get memory map from BIOS * Locate kernel in filesystem * Allocate memory to load kernel image * Load kernel image into buffer * Enable graphics mode * Check kernel image ELF headers * Enable long mode, if 64-bit * Allocate and map memory for kernel segments * Setup stack * Setup COM serial output port * Setup IDT * Disable PIC * Check presence of CPU features (NX, SMEP, x87, PCID, global pages, TCE, WP, MMX, SSE, SYSCALL), and enable them * Assign a PAT to write combining * Setup FS/GS base * Load IDTR * Enable APIC and setup using information in ACPI tables * Setup GDT and TSS
本页使用的模板:
模板:Eq
(
查看源代码
)
模板:Eq1
(
查看源代码
)
模板:If
(
查看源代码
)
模板:Rating
(
查看源代码
)
模板:Show1
(
查看源代码
)
返回至“
Rolling Your Own Bootloader
”。
导航菜单
个人工具
登录
命名空间
页面
讨论
变体
已展开
已折叠
查看
阅读
查看源代码
查看历史
更多
已展开
已折叠
搜索
导航
首页
最近更改
随机页面
MediaWiki帮助
工具
链入页面
相关更改
特殊页面
页面信息