Napíšeme si vlastní operační systém od základů. K tomu budeme potřebovat znalosti assembleru a C++ a následující programovací nástroje (pracovat budeme na Linuxu):
-
GNU C++ překladač gcc a GNU assembler as
-
Binutils (linker) ld
-
Vývojové knihovny pro C++
-
nástroj make pro sestavování projektů
-
VirtualBox (emulátor PC)
-
xorriso
-
grub
Na Debianu a klonech stačí nainstalovat
sudo apt install g++ binutils libc6-dev-i386
sudo apt install VirtualBox xorriso grub-legacy
V této části vylepšíme naše pracovní prostředí a budeme spouštět náš OS ve VirtualBoxu.
Upravíme si Makefile, přidáme cíl mykernel.iso a run
Makefile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# sudo apt-get install g++ binutils libc6-dev-i386
GCCPARAMS = -m32 -fno-use-cxa-atexit -nostdlib -fno-builtin -fno-rtti -fno-exceptions -fno-leading-underscore (1)
ASPARAMS = --32 (2)
LDPARAMS = -melf_i386 (3)
objects = loader.o kernel.o
%.o: %.cpp (4)
gcc $(GCCPARAMS) -c -o $@ $< (5)
%.o: %.s (6)
as $(ASPARAMS) -o $@ $< (7)
mykernel.bin: linker.ld $(objects) (8)
ld $(LDPARAMS) -T $< -o $@ $(objects) (9)
install: mykernel.bin (10)
sudo cp $< /boot/mykernel.bin (11)
mykernel.iso: mykernel.bin
mkdir iso
mkdir iso/boot
mkdir iso/boot/grub
cp mykernel.bin iso/boot/mykernel.bin
echo 'set timeout=0' > iso/boot/grub/grub.cfg
echo 'set default=0' >> iso/boot/grub/grub.cfg
echo '' >> iso/boot/grub/grub.cfg
echo 'menuentry "My Operating System" {' >> iso/boot/grub/grub.cfg
echo ' multiboot /boot/mykernel.bin' >> iso/boot/grub/grub.cfg
echo ' boot' >> iso/boot/grub/grub.cfg
echo '}' >> iso/boot/grub/grub.cfg
grub-mkrescue --output=mykernel.iso iso
rm -rf iso
run: mykernel.iso
(killall VirtualBoxVM && sleep 1) || true
VirtualBoxVM --startvm 'MujOS' &
| 1 | Parametry kompilátoru gcc: kompilujeme pro 32bit, nebudeme používat ukazatel na vyjimky (exceptions), nemáme stdlib, nebudeme generovat informace o každé třídě s virtuálními funkcemi, nemáme vyjimky, nedělej úvodní podrtžítko v názvu funkce |
| 2 | Parametry assembleru: kompilujeme pro 32bit |
| 3 | Parametry linkeru: linkuj pro architekturu i386 a udělej formát spustitelného souboru elf |
| 4 | Pravidlo pro překlad z C++ do objektového kódu |
| 5 | Jak se překládají zdrojové soubory C++ do objektového kódu. Zavolá se gcc s parametry GCCPARAMS, $@ značí cílový soubor a $< zdrojový soubor |
| 6 | Pravidlo pro překlad z assembleru do objektového kódu |
| 7 | Zavolá se assembler as s parametry ASPARAMS |
| 8 | Pravislo pro sestavení výsledného jádra OS |
| 9 | Zavolá se linker ld s parametry LDPARAMS |
| 10 | Pravidlo pro instalaci jádra OS na lokální počítač |
| 11 | Potřebujeme být root, abychom mohli instalovat jádro OS |
Úpravy Makefile
loader.s
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
.set MAGIC, 0x1badb002
.set FLAGS, (1<<0 | 1<<1)
.set CHECKSUM, -(MAGIC + FLAGS)
.section .multiboot
.long MAGIC
.long FLAGS
.long CHECKSUM
.section .text
.extern kernelMain
.extern callConstructors
.global loader
loader:
mov $kernel_stack, %esp
call callConstructors
push %eax
push %ebx
call kernelMain
_stop:
cli
hlt
jmp _stop
.section .bss
.space 2*1024*1024; # 2 MiB
kernel_stack:
kernel.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void printf(char* str)
{
static unsigned short* VideoMemory = (unsigned short*)0xb8000;
for(int i = 0; str[i] != '\0'; ++i)
VideoMemory[i] = (VideoMemory[i] & 0xFF00) | str[i];
}
typedef void (*constructor)();
extern "C" constructor start_ctors;
extern "C" constructor end_ctors;
extern "C" void callConstructors()
{
for(constructor* i = &start_ctors; i != &end_ctors; i++)
(*i)();
}
extern "C" void kernelMain(const void* multiboot_structure, unsigned int /*multiboot_magic*/)
{
printf("Ahoj Jirko! --- https://sspvc.lixis.cz");
while(1);
}
linker.ld
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
ENTRY(loader)
OUTPUT_FORMAT(elf32-i386)
OUTPUT_ARCH(i386:i386)
SECTIONS
{
. = 0x0100000;
.text :
{
*(.multiboot)
*(.text*)
*(.rodata)
}
.data :
{
start_ctors = .;
KEEP(*( .init_array ));
KEEP(*(SORT_BY_INIT_PRIORITY( .init_array.* )));
end_ctors = .;
*(.data)
}
.bss :
{
*(.bss)
}
/DISCARD/ : { *(.fini_array*) *(.comment) }
}
Aby se náš OS mohl spustit musíme malinko upravit /boot/grub-grub.cfg
/boot/grub/grub.cfg
1
2
3
4
5
6
7
8
9
### BEGIN /etc/grub.d/40_custom ### (1)
# This file provides an easy way to add custom menu entries. Simply type the
# menu entries you want to add after this comment. Be careful not to change
# the 'exec tail' line above.
menuentry 'Muj operacni system 1' { (2)
multiboot /boot/mykernel.bin (3)
boot (4)
}
### END /etc/grub.d/40_custom ### (5)
| 1 | Najdeme sekci 40_custom (uvnitř si můžeme psát, co chceme) |
| 2 | menuentry označuje řádek v menu grubu, pojmenujeme si ho 'Muj operacni system 1' |
| 3 | Řekneme grubu, kde má hledat kernel |
| 4 | Nabootuj to |
| 5 | Tento řádek nesmíme smazat |
Zdroje
Viktor Engelman
poštovní adresa: Zimmerstrasse 16 03044 Chotěbuz Kontakt: Telefon: +49 170 500 5810 E-mail: viktor@wyoos.org