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):

  1. GNU C++ překladač gcc a GNU assembler as

  2. Binutils (linker) ld

  3. Vývojové knihovny pro C++

  4. nástroj make pro sestavování projektů

  5. VirtualBox (emulátor PC)

  6. xorriso

  7. 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