#+STARTUP: showall indent hidestars
# -*- org-src-preserve-indentation: t -*-

#+TITLE: Начальная загрузка ОС

* Загрузка через GRUB

SayoriOS использует GRUB2 в качестве загрузчика, его конфиг лежит в iso/boot/grub/grub.cfg

#+BEGIN_SRC conf :noweb tangle :tangle iso/boot/grub/grub.cfg :exports none
  set timeout=5

  <<grub_modules>>

  <<grub_terminal>>

  <<grub_theme>>

  # Меню загрузки

  <<grub_menuentryes>>
#+END_SRC

** Подгружаемые GRUB-модули

Для загрузки нам понадобятся необходимые модули:

#+NAME: grub_modules
#+BEGIN_SRC conf
  # Импорт модулей
  insmod vbe
  insmod vga
  insmod video_bochs
  insmod video_cirrus
  insmod gfxterm
  insmod gfxmenu
  insmod tga
#+END_SRC

** Настройка терминала

Теперь нам нужно настроить терминал, его input и output.

Допустимые настройки зависят от платформы, и могут включать:
- console - родная консоль платформы
- serial - последовательный терминал
- serial_<port> - последовательный терминал с явным выбором порта
- at_keyboard - клавиатура ПК AT
- usb_keyboard - USB-клавиатура использовующая HID Boot Protocol

По умолчанию используется "console", но тут мы указываем ее явно.

Допустимые output терминалов также зависят от платформы, и могут включать:
- console - родная консоль платформы
- serial - последовательный терминал
- serial_<port> - последовательный терминал с явным выбором порта
- gfxterm - вывод в графическом режиме
- vga_text - вывод текста VGA
- mda_text - (текстовый вывод MDA),
- morse - вывод через Beeper используя код Морзе
- spkmodem -простой протокол передачи данных с использованием системного динамика. Он полезен, когда последовательный порт недоступен. Подключите выход от отправляющей системы (где работает GRUB) к линейному входу принимающей системы (обычно машина для проявки). На принимающей машине скопмплируйте "spkmodem-recv" из запустите:

#+BEGIN_SRC sh
  parecord --channels=1 --rate=48000 --format=s16le | ./spkmodem-recv
#+END_SRC

Мы используем gfxterm - вывод в графическом режиме. Это приводит к тому, что GRUB загружает все доступные видео GRUB драйверы и использует тот, который наиболее подходит для вашего оборудования. Поэтому для минимизации размера эту опцию можно изменить.

#+NAME: grub_terminal
#+BEGIN_SRC conf
  # Настройка терминала
  terminal_input console
  terminal_output gfxterm
#+END_SRC

** Тема оформления GRUB

Мы используем тему оформления, которая лежит в iso/boot/grub/theme.txt

#+NAME: grub_theme
#+BEGIN_SRC conf
  # Тема оформления
  set theme=/boot/grub/theme.txt
  export theme
#+END_SRC

Эта тема добавляет фоновую картинку и описывает внешний вид меню

#+NAME: grub_theme
#+BEGIN_SRC conf :tangle iso/boot/grub/theme.txt
  desktop-color: "#24282c"
  desktop-image: "background.tga"

  + boot_menu {
      left = 15%
      width = 70%
      top = 30%
      height = 45%
      item_height = 30
      item_padding = 5
      icon_width = 32
      icon_height = 32
      item_icon_space = 20
      item_spacing = 5
      item_color = "#cccccc"
      selected_item_color = "#00ff00"
  }

  + hbox {
      left = 50%-250
      top = 80%
      width = 500
      + label { width = 250 height = 20 align = "center" color = "#ffffff" text = "E = Edit Boot Options" }
      + label { width = 250 height = 20 align = "center" color = "#ffffff" text = "C = GRUB Commandline" }
  }
#+END_SRC

** Пункты меню

Несколько пунктов меню описывают различные варианты загрузки SayoryOS. Все они пока используют boot/kernel.elf и bot/ramdisk

*** Обычная загрузка

#+BEGIN_SRC conf :noweb-ref grub_menuentryes
  # Обычная загрузка
  menuentry "SayoriOS Soul v0.3.4 Alpha" {
      multiboot /boot/kernel.elf
      module /boot/ramdisk initrd_tarfs;
      boot
  }
#+END_SRC

*** Загрузка с отключенным remote desktop

#+BEGIN_SRC conf :noweb-ref grub_menuentryes
  # Загрузка с отключенным remote desktop
  menuentry --hotkey=w "SayoriOS Soul v0.3.4 Alpha [VMWare]" {
      multiboot /boot/kernel.elf disable=rdsp
      module /boot/ramdisk initrd_tarfs;
      boot
  }
#+END_SRC

*** Загрузка с отключенным floppy

#+BEGIN_SRC conf :noweb-ref grub_menuentryes
  # Загрузка с отключенным floppy
  menuentry --hotkey=f "SayoriOS Soul v0.3.4 Alpha [No Floppy Support]" {
      multiboot /boot/kernel.elf disable=floppy
      module /boot/ramdisk initrd_tarfs;
      boot
  }
#+END_SRC

*** Загрузка без сети

#+BEGIN_SRC conf :noweb-ref grub_menuentryes
  # Загрузка без сети
  menuentry "SayoriOS Soul v0.3.x Alpha [Minimal Bootscreen]" {
      multiboot /boot/kernel.elf bootscreen=minimal disable=network
      module /boot/ramdisk initrd_tarfs;
      boot
  }
#+END_SRC

*** Быстрая загрузка

#+BEGIN_SRC conf :noweb-ref grub_menuentryes
  # Быстрая загрузка
  menuentry --hotkey=f "SayoriOS Soul v0.3.4 Alpha [FAST+]" {
      multiboot /boot/kernel.elf disable=coms disable=ac97 disable=pc-speaker disable=floppy disable=network bootscreen=minimal bootscreen=no-logs
      module /boot/ramdisk initrd_tarfs;
      boot
  }
#+END_SRC

*** NatSuki

#+BEGIN_SRC conf :noweb-ref grub_menuentryes
  # NatSuki
  menuentry --hotkey=n "SayoriOS Soul v0.3.4 Alpha [NatSuki]" {
      multiboot /boot/kernel.elf
      module /boot/ramdisk initrd_tarfs;
      boot
  }
#+END_SRC

*** Reboot

#+BEGIN_SRC conf :noweb-ref grub_menuentryes
  # Reboot
  menuentry --hotkey=r 'Reboot' {
     reboot
  }
#+END_SRC

*** Power off

#+BEGIN_SRC conf :noweb-ref grub_menuentryes
  # Power off
  menuentry --hotkey=h 'Power off' {
     halt
  }
#+END_SRC

* Сборка загрузчика

Мы можем собирать загрузчик с использованием GRUB или GRUB2:

#+NAME: tool_grub
#+BEGIN_SRC sh :tangle tools/grub.sh
  GRUB=$(ls $PREFIX/bin/grub-mkrescue 2> /dev/null)
  GRUB2=$(ls $PREFIX/bin/grub2-mkrescue 2> /dev/null)

  if [ -n "$GRUB2" ]; then
      echo $GRUB2
  elif [ -n "$GRUB" ]; then
      echo $GRUB
  fi
#+END_SRC

И это используется в таргете Makefile, который называется "geniso":

#+NAME: make_geniso
#+BEGIN_SRC makefile :padline yes
geniso: $(KERNEL)
	$(shell bash tools/grub.sh) -o "kernel.iso" iso/ -V kernel
#+END_SRC

"geniso"-цель зависит от цели "KERNEL", через которую определен файл с ядром (определяется в config.mk):

#+NAME: make_kernel_var
#+BEGIN_SRC makefile :padline yes
	KERNEL = iso/boot/kernel.elf
#+END_SRC

А в основном Makefile эта цель описана так (я временно удалил все связанное с Rust для упрощения, а также stripping):

#+NAME: make_kernel
#+BEGIN_SRC makefile :padline yes
$(KERNEL): $(KERNEL_NEED)
	@echo -e '\x1b[32mLINK \x1b[0m' $(KERNEL)
	@rm -f $(KERNEL)
	@$(LD) $(LDFLAGS) -o $(KERNEL) $(KERNEL_NEED)
	@bash tools/genmap.sh
	@bash tools/insertmap.sh
	@ls -lh $(KERNEL)
	@-rm kernel.map
#+END_SRC

KERNEL-цель зависит от:
- "KERNEL_NEED"
- "LD"
- "LDFLAGS"

а также от файлов:
- tools/genmap.sh (todo)
- tools/insertmap.sh (todo)

Таргет "KERNEL_NEED" определяется в config.mk:

#+NAME: make_kernel_need_var
#+BEGIN_SRC makefile :padline yes
  KERNEL_NEED = $(ASM) $(OBJS) $(CPP_CODE)
#+END_SRC

#+NAME: make_ld_var
#+BEGIN_SRC makefile :padline yes
  LD ?= ld.lld
#+END_SRC

#+NAME: make_ldflags_var
#+BEGIN_SRC makefile :padline yes
  LDFLAGS=-T kernel/asm/link.ld
#+END_SRC

Теперь у нас накопились цели:
- "ASM" (сборка ассемблерной части)
- "OBJS" (сборка сишной части)
- "CPP_CODE" (полностью закомментированные сорцы)

а также файл:
- kernel/asm/link.ld (todo)

** Сборка ассемблерной части

Тут мы достигаем ассеблерного исходного кода:

#+NAME: make_asm_var
#+BEGIN_SRC makefile :padline yes
  ASM=$(ASM_SRC:%.s=$(OBJ_DIRECTORY)/%.o)
#+END_SRC

что добавляет к списку целей:
- "ASM_SRC"
- "OBJ_DIRECTORY"

#+NAME: make_asm_src_var
#+BEGIN_SRC makefile :padline yes :exports none
  ASM_SRC=kernel/asm/init.s \
  kernel/asm/interrupt.s \
  kernel/asm/sys_calls.s \
  kernel/asm/usr.s \
  kernel/asm/sse.s \
  kernel/asm/gdt.s \
  kernel/asm/paging.s \
  kernel/asm/regs.s \
  kernel/src/lib/setjmp.s \
  kernel/asm/switch_task.s \
  #	kernel/src/sys/v8086.s \
#+END_SRC

"OBJ_DIRECTORY":

#+NAME: make_obj_directory_var
#+BEGIN_SRC makefile :padline yes
  OBJ_DIRECTORY = objects
#+END_SRC

#+NAME: make_obj_directory
#+BEGIN_SRC makefile :padline yes
$(OBJ_DIRECTORY)/%.o : %.s | $(OBJ_DIRECTORY)
	@echo -e '\x1b[32mASM  \x1b[0m' $@
	@$(AS) $< $(ASFLAGS) -o $@

$(OBJ_DIRECTORY)/%.o : %.c | $(OBJ_DIRECTORY)
	@echo -e '\x1b[32mC    \x1b[0m' $@
	@$(CC) $(CFLAGS) -c -o $@ $<

$(OBJ_DIRECTORY)/%.o : %.cpp | $(OBJ_DIRECTORY)
	@echo -e '\x1b[32mCPP  \x1b[0m' $@
	@$(CXX) $(CPP_FLAGS) -c -o $@ $<
#+END_SRC

** Сборка сишной части

Тут мы достигаем сишного кода:

#+NAME: make_objs_var
#+BEGIN_SRC makefile :padline yes
  OBJS = $(SOURCES:%.c=$(OBJ_DIRECTORY)/%.o)
#+END_SRC

Так как "OBJ_DIRECTORY" уже определена, остается разобраться с "SOURCES"

#+NAME: make_sources_var
#+BEGIN_SRC makefile :padline yes :exports none
  SOURCES=\
  kernel/src/sys/cpuinfo.c \
  kernel/src/sys/cpu_isr.c \
  kernel/src/sys/gdt.c \
  kernel/src/sys/tss.c \
  kernel/src/sys/idt.c \
  kernel/src/sys/isr.c \
  kernel/src/io/ports.c \
  kernel/src/io/serial_port.c \
  kernel/src/lib/string.c \
  kernel/src/drv/fpu.c \
  kernel/src/sys/timer.c \
  kernel/src/sys/logo.c \
  kernel/src/lib/math/math.c \
  kernel/src/mem/pmm.c	\
  kernel/src/mem/vmm.c	\
  $(wildcard kernel/src/lib/libvector/src/*.c) \
  $(wildcard kernel/src/lib/libstring/src/*.c) \
  kernel/src/lib/stdio.c \
  kernel/src/lib/split.c \
  kernel/src/io/screen.c \
  kernel/src/io/tty.c \
  kernel/src/fs/tarfs.c \
  kernel/src/fs/fsm.c \
  kernel/src/fs/nvfs.c \
  kernel/src/fs/natfs.c \
  kernel/src/fs/iso9660.c \
  kernel/src/lib/php/str_replace.c \
  kernel/src/sys/scheduler.c \
  kernel/src/lib/php/explode.c \
  kernel/src/lib/php/pathinfo.c \
  kernel/src/lib/elk/elk.c \
  kernel/src/lib/elk/elk_engine.c \
  kernel/src/lib/elk/elk_libs.c \
  kernel/src/lib/elk/jse_func.c \
  $(wildcard kernel/src/lib/elk/ext/*.c) \
  kernel/src/lib/freeada/ada.c \
  kernel/src/drv/psf.c \
  kernel/src/sys/unwind.c \
  kernel/src/fs/NatSuki.c \
  kernel/src/drv/disk/initrd.c \
  kernel/src/drv/disk/dpm.c \
  kernel/src/lib/list.c \
  kernel/src/sys/sync.c \
  kernel/src/gui/basics.c \
  kernel/src/lib/pixel.c \
  kernel/src/sys/bootscreen.c \
  kernel/src/debug/hexview.c \
  kernel/src/drv/video/vbe.c \
  kernel/src/drv/input/keyboard.c \
  kernel/src/drv/input/mouse.c \
  kernel/src/sys/syscalls.c \
  kernel/src/sys/testing.c \
  kernel/src/sys/trigger.c \
  kernel/src/lib/rand.c \
  kernel/src/drv/cmos.c \
  kernel/src/drv/beeper.c \
  kernel/src/user/env.c \
  kernel/src/drv/pci.c \
  kernel/src/gui/pointutils.c \
  kernel/src/gui/line.c \
  kernel/src/gui/circle.c \
  kernel/src/lib/math/exp.c \
  kernel/src/lib/math/log.c \
  kernel/src/lib/math/pow.c \
  kernel/src/lib/math/acos.c \
  kernel/src/lib/math/asin.c \
  kernel/src/lib/math/atan.c \
  kernel/src/lib/math/modf.c \
  kernel/src/lib/math/integral.c \
  kernel/src/lib/math/sin.c \
  kernel/src/lib/math/cos.c \
  kernel/src/lib/math/tan.c \
  kernel/src/lib/math/sqrt.c \
  kernel/src/io/rgb_image.c \
  $(wildcard kernel/src/lib/libstring/*.c) \
  kernel/src/lib/math/cbrt.c \
  kernel/src/sys/cpuid.c	\
  kernel/src/drv/disk/ata.c \
  kernel/src/drv/disk/atapi.c \
  kernel/src/sys/cputemp.c	\
  kernel/src/net/endianess.c \
  kernel/src/net/cards.c \
  kernel/src/net/ethernet.c \
  kernel/src/net/arp.c \
  kernel/src/net/ipv4.c \
  kernel/src/net/udp.c \
  kernel/src/net/dhcp.c \
  kernel/src/net/icmp.c \
  kernel/src/sys/system.c \
  kernel/src/io/status_sounds.c \
  kernel/src/io/status_loggers.c \
  kernel/src/extra/cli.c \
  kernel/src/sys/variable.c	\
  kernel/src/fs/fat32.c \
  kernel/src/sys/fxsave_region.c \
  kernel/src/toys/gfxbench.c \
  kernel/src/toys/miniplay.c \
  kernel/src/drv/rtl8139.c \
  kernel/src/drv/network/virtio_network.c \
  kernel/src/fmt/tga.c \
  kernel/src/lib/sprintf.c \
  kernel/src/debug/ubsan.c \
  kernel/src/drv/disk/floppy.c \
  kernel/src/drv/disk/ata_dma.c \
  kernel/src/drv/audio/ac97.c \
  kernel/src/sys/elf.c \
  kernel/src/sys/acpi.c \
  kernel/src/sys/pixfmt.c \
  kernel/src/desktop/render.c \
  kernel/src/desktop/window.c \
  kernel/src/desktop/widget.c \
  kernel/src/desktop/widget_button.c \
  kernel/src/desktop/widget_progress.c \
  kernel/src/desktop/widget_image.c \
  kernel/src/desktop/widget_label.c \
  kernel/src/desktop/eki.c \
  kernel/src/desktop/parallel_desktop.c \
  kernel/src/sys/mtrr.c \
  kernel/src/net/net_info_cli.c \
  kernel/src/toys/mala.c \
  kernel/src/debug/memmeter.c \
  kernel/src/drv/disk/ahci.c \
  kernel/src/drv/disk/ata_pio.c \
  kernel/src/toys/minesweeper.c \
  kernel/src/toys/calendar.c \
  kernel/src/toys/diskctl.c \
  kernel/src/lib/utf_conversion.c \
  kernel/src/lib/base64.c \
  kernel/src/sys/file_descriptors.c \
  kernel/src/net/stack.c \
  kernel/src/toys/pavi.c \
  $(GAMEBOY) \
  kernel/src/lib/kernel.c \
  #	kernel/src/toys/ducktape.c \
  kernel/src/toys/piano.c \
  kernel/src/toys/dino.c \
  kernel/src/extra/texplorer.c \
  kernel/src/drv/disk/mbr.c \
  kernel/src/fs/fat12.c \
  kernel/src/fs/smfs.c \
  kernel/src/toys/pavi.c \
#+END_SRC

Теперь нам нужен еще "GAMEBOY" (я не знаю зачем нужна его поддежка в ядре):

#+NAME: make_gameboy_var
#+BEGIN_SRC makefile :padline yes
  GAMEBOY = $(wildcard kernel/src/ports/gameboy/*.c)
#+END_SRC

В файле config.mk определена еще переменная "GAMEBOY_OBJS" которая нигде не используется

#+BEGIN_SRC makefile
  GAMEBOY_OBJS = $(GAMEBOY:.c=.o)
#+END_SRC

И надо определить DIRECTORIES:

#+NAME: make_dirs_var
#+BEGIN_SRC makefile :padline yes
  DIRECTORIES = objects/kernel/src \
                  objects/kernel/asm \
                  objects/kernel/src/lib \
                  objects/kernel/src/lib/libstring/src \
                  objects/kernel/src/lib/libvector/src \
                  objects/kernel/src/lib/php \
                  objects/kernel/src/lib/elk \
                  objects/kernel/src/lib/elk/ext \
                  objects/kernel/src/lib/freeada \
                  objects/kernel/src/extra \
                  objects/kernel/src/sys \
                  objects/kernel/src/io \
                  objects/kernel/src/net \
                  objects/kernel/src/drv \
                  objects/kernel/src/drv/disk \
                  objects/kernel/src/debug \
                  objects/kernel/src/fmt \
                  objects/kernel/src/drv/audio \
                  objects/kernel/src/drv/video \
                  objects/kernel/src/drv/input \
                  objects/kernel/src/drv/network \
                  objects/kernel/src/fs \
                  objects/kernel/src/lib/math \
                  objects/kernel/src/toys \
                  objects/kernel/src/gui \
                  objects/kernel/src/desktop \
                  objects/kernel/src/user \
                  objects/kernel/src/ports/gameboy \
                  objects/kernel/src/mem \
                  objects/kernel/src/arch/x86 \
                  # objects/kernel/src/compress/zlib \
#+END_SRC

* Сборка Makefile                                                  :noexport:

#+BEGIN_SRC makefile :noweb tangle :tangle MyMakefile :padline yes
all:
	@-mkdir -p $(OBJ_DIRECTORY) $(DIRECTORIES)
	@$(MAKE) $(KERNEL)

<<make_dirs_var>>

<<make_gameboy_var>>

<<make_sources_var>>

<<make_objs_var>>

<<make_obj_directory_var>>

<<make_obj_directory>>

<<make_asm_src_var>>

<<make_asm_var>>

<<make_ldflags_var>>

<<make_ld_var>>

<<make_kernel_need_var>>

<<make_kernel>>

<<make_geniso>>

clean:
	-rm -f $(KERNEL)
	-rm -f $(KERNEL_NEED)
	-rm -f $(DEPS)
	-rm -f iso/boot/ramdisk
#+END_SRC

* Граф зависимостей в Makefile

Есть способ получить его в виде svg-файла с помощью ключей самого make и graphviz:

#+NAME: make_graph
#+BEGIN_SRC sh
  make -Bnd | make2graph > out.dot && dot -Tsvg  out.dot > out.svg && firefox --new-window out.svg
#+END_SRC
