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

#+TITLE: Начало работы ядра

* Ядро и его структура

Точка входа определяется при компиляции ядра в файле конфига линкера kernel/asm/link.ld

Рассмотрим этот конфиг подробнее.

#+BEGIN_SRC conf :noweb tangle :tangle ../kernel/asm/link.ld :exports none
  <<ld_entry_point>>

  <<ld_sections>>
#+END_SRC

** Точка входа

Точкой входа является метка __pre_init

#+NAME: ld_entry_point
#+BEGIN_SRC conf
  ENTRY (__pre_init)
#+END_SRC

Эта метка находится в файле ./kernel/asm/init.s

** Сегменты

Мы имеем несколько сегментов
- text
- data
- rodata
- bss
- debug_symbols
- user

Начальный адрес размещения сегмента text = 0x200000

#+NAME: ld_sections
#+BEGIN_SRC conf
  SECTIONS
  {
      . = 0x00200000;

      KERNEL_BASE_pos = .;

      .text ALIGN (0x1000) :
      {
          ,*(.mboot)

          CODE_start = .;

          ,*(.text*)

          CODE_end = .;
      }

      .data ALIGN (0x1000) :
      {
          DATA_start = .;

          ,*(.data*)

          DATA_end = .;
      }

      .rodata ALIGN (0x1000) :
      {
          RODATA_start = .;

          ,*(.rodata*)

          RODATA_end = .;
      }

      .bss ALIGN (0x1000) :
      {
          ,*(COMMON)

          BSS_start = .;

          ,*(.bss*)

          BSS_end = .;
      }

      .debug_symbols ALIGN(0x1000) : {
          ,*(.debug_symbols*)
      }

      .user ALIGN(0x1000) : {
          USER_start = .;
          ,*(.user*)
          USER_end = .;
      }

      KERNEL_END_pos = .;
  }
#+END_SRC

* Начальная инициализация

Это ассемблерная точка входа.

- Запрещаем прерывания
- Настраиваем FPU
- Вызываем sse_enable
- Настраиваем стек
- Очищаем EBP
- Вызываем kernel

#+BEGIN_SRC asm :noweb tangle :tangle ../kernel/asm/init.s
  /*
   ,* SayoriOS
   ,* Старт инициализации ядра
   ,*/

  .code32
  .set ALIGN,		 						1<<0			# Выравнивание загруженных модулей по границам страницы
  .set MEMINFO,	 						1<<1			# Просим предоставить карту памяти
  .set VBE_MODE,   						1<<2            # VBE mode flag. GRUB will set it for us and provide info about it.
  .set INIT_MBOOT_HEADER_MAGIC,           0x1BADB002
  .set INIT_MBOOT_HEADER_FLAGS,           ALIGN | MEMINFO | VBE_MODE
  .set INIT_MBOOT_CHECKSUM,               0x00000000 - (INIT_MBOOT_HEADER_MAGIC + INIT_MBOOT_HEADER_FLAGS)

  .set STACK_SIZE, 1024 * 64  # 64 KB

  .extern kernel

  .section .mboot, "a", @progbits

  .int INIT_MBOOT_HEADER_MAGIC
  .int INIT_MBOOT_HEADER_FLAGS
  .int INIT_MBOOT_CHECKSUM
  .long 0, 0, 0, 0, 0     # Неиспользуется
  .long 0                 # 0 - графический режим
  .long 800, 600, 32      # Ширина, длина, глубина

  .section .bss
      .align 16
      stack_bottom:
          .skip STACK_SIZE
      stack_top:

  .section	.text

  .global		__pre_init

  __pre_init:
          cli

          # init FPU
          fninit
          fldcw (conword)

          call sse_enable

          mov $stack_top, %esp

          push	%esp
          push	%ebx

          xor %ebp, %ebp

          call	kernel

          hlt

  conword:
          .word 0x37f

  loop:
          jmp	loop
#+END_SRC

** SSE-enable

sse-check вызывается из сишного кода в kernel.c

#+BEGIN_SRC asm :noweb tangle :tangle ../kernel/asm/sse.s
  .section	.text
  .global	sse_check

  sse_check:
      mov $0x1, %eax

      cpuid

      test $(1 << 25), %edx
      mov $0x1, %eax

      jnz good

      xor %eax, %eax
  good:
      ret


  .global		sse_enable

  sse_enable:
      # enable SSE
      mov %cr0, %eax
      and $~0x04, %ax
      or $0x2, %ax
      mov %eax, %cr0

      mov %cr4, %eax
      or $(3 << 9), %ax
      mov %eax, %cr4

      ret
#+END_SRC

* Код ядра

лежит в kernel/src/kernel.c

#+BEGIN_SRC c :noweb tangle :tangle ../kernel/src/kernel.c :exports none
  /**
   ,* @file kernel.c
   ,* @author Пиминов Никита (nikita.piminoff@yandex.ru), NDRAEY >_ (pikachu_andrey@vk.com)
   ,* @brief Основная точка входа в ядро
   ,* @version 0.3.5
   ,* @date 2022-11-01
   ,* @copyright Copyright SayoriOS Team (c) 2022-2024
   ,*/

  #include "kernel.h"

  #include <drv/fpu.h>
  #include <lib/php/explode.h>
  #include <sys/unwind.h>

  #include "mem/pmm.h"
  #include "mem/vmm.h"
  #include "drv/audio/ac97.h"
  #include "sys/mtrr.h"
  #include "net/ipv4.h"
  #include "lib/freeada/ada.h"

  #include "fs/natfs.h"
  #include "net/stack.h"

  #include <lib/pixel.h>

  <<kglob>>

  void jse_file_getBuff(char* buf);
  <<kdecl>>

  <<kfun>>

  /**
   ,* @brief Точка входа в ядро
   ,*
   ,* @param multiboot_header_t mboot - Информация MultiBoot
   ,* @param initial_esp -  Точка входа
   ,*/

  extern size_t CODE_start;
  extern size_t CODE_end;
  extern size_t DATA_start;
  extern size_t DATA_end;
  extern size_t RODATA_start;
  extern size_t RODATA_end;
  extern size_t BSS_start;
  extern size_t BSS_end;
  extern size_t USER_start;
  extern size_t USER_end;

  /*
    Спаси да сохрани этот кусок кода
    Да на все твое кодерская воля
    Да прибудет с тобой, священный код
    Я тебя благославляю
  ,*/
  int kernel(multiboot_header_t* mboot, uint32_t initial_esp) {
      <<kernel>>
  }

  //void k() {
  //    qemu_log("Focken processez!");
  //
  //    while(1) {
  //        qemu_log("AAA");
  //        sleep_ms(250);
  //    };
  //}
#+END_SRC

** Установка значения для инициализированного ком-порта

#+BEGIN_SRC c :noweb-ref kernel :padline yes
  __com_setInit(1, 1);

#+END_SRC

** Установка параметров miltiboot

#+BEGIN_SRC c :noweb-ref kernel
  multiboot = mboot;

#+END_SRC

#+BEGIN_SRC c :noweb-ref kglob  :padline yes
  multiboot_header_t* multiboot;
#+END_SRC

** Сохраняем значение вершины стека

#+BEGIN_SRC c :noweb-ref kernel
  __asm__ volatile("movl %%esp, %0" : "=r"(init_esp));

#+END_SRC

#+BEGIN_SRC c :noweb-ref kglob
  uint32_t init_esp = 0;
#+END_SRC

** Получаем адрес фреймбуфера из данных мультибута

#+BEGIN_SRC c :noweb-ref kernel
  framebuffer_addr = (uint8_t *) (mboot->framebuffer_addr);

#+END_SRC

framebuffer_addr объявлен в kernel/src/io/screen.c

** Нарисуем что-то в фреймбуфере

Функция объявлена в этом же файле

#+BEGIN_SRC c :noweb-ref kfun
  #ifndef RELEASE
  void draw_raw_fb(multiboot_header_t* mboot, int x, int y, int w, int h, int color) {
      for(uint32_t i = y; i < y + h; i++) {
          for(uint32_t j = x; j < x + w; j++) {
              uint8_t* a = (framebuffer_addr + (j * ((mboot->framebuffer_bpp) >> 3)) + i * (mboot->framebuffer_pitch));

              a[2] = (color >> 16) & 0xff;
              a[1] = (color >> 8) & 0xff;
              a[0] = (color) & 0xff;
          }
      }
  }
  #else
  #define draw_raw_fb(a, b, c, d, e, f)
  #endif

#+END_SRC

#+BEGIN_SRC c :noweb-ref kernel
  draw_raw_fb(mboot, 0, 0, 200, 16, 0x444444);

#+END_SRC

** Выведем лого

#+BEGIN_SRC c :noweb-ref kernel
  drawASCIILogo(0);

#+END_SRC

объявлена kernel/src/sys/logo.c

** Вывести в лог версию ядра

#+BEGIN_SRC c :noweb-ref kernel
  qemu_log("SayoriOS v%d.%d.%d\nBuilt: %s",
           VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH,    // Версия ядра
           __TIMESTAMP__                                   // Время окончания компиляции ядра
      );

#+END_SRC

** Вывести адрес загрузки бутлоадера

#+BEGIN_SRC c :noweb-ref kernel
  qemu_log("Bootloader header at: %x", mboot);

#+END_SRC

** Вывести информацию о поддержке SSE

#+BEGIN_SRC c :noweb-ref kernel
  qemu_log("SSE: %s", sse_check() ? "Supported" : "Not supported");

#+END_SRC

** Сохранить текущее состояние FPU

если sse доступен

#+BEGIN_SRC c :noweb-ref kernel
  if (sse_check()) {
      __wtf_fxsave();
  }

#+END_SRC

** Инициализация прерываний

#+BEGIN_SRC c :noweb-ref kernel
  qemu_log("Setting `Interrupt Descriptor Table`...");
  init_descriptor_tables();
  qemu_log("Setting `RIH`...");
  isr_init();

#+END_SRC

init_descriptor_tables объявлена в kernel/src/sys/gdt.c

isr_init объявлена в kernel/src/sys/isr.c

** Иницализация FPU

#+BEGIN_SRC c :noweb-ref kernel
  qemu_log("Initializing FPU...");
  fpu_init();

#+END_SRC

fpu_init объявлена в kernel/src/drv/fpu.c

** Еще разок нарисуем блок в фреймбуфере

#+BEGIN_SRC c :noweb-ref kernel
  draw_raw_fb(mboot, 0, 0, 400, 16, 0x888888);

#+END_SRC

** Инициализация таймера

#+BEGIN_SRC c :noweb-ref kernel
  init_timer(CLOCK_FREQ);

#+END_SRC

объявлена в kernel/src/sys/timer.c

** Разрешение прерываний

#+BEGIN_SRC c :noweb-ref kernel
  __asm__ volatile("sti");

#+END_SRC

** Рисуем на экран

#+BEGIN_SRC c :noweb-ref kernel
  draw_raw_fb(mboot, 0, 0, 800, 16, 0xffffff);

#+END_SRC

** Отображаем информацию о занимаемой памяти

#+BEGIN_SRC c :noweb-ref kernel
  qemu_log("Checking RAM...");
  check_memory_map((memory_map_entry_t *) mboot->mmap_addr, mboot->mmap_length);
  qemu_log("Memory summary:");
  qemu_log("    Code: %x - %x", &CODE_start, &CODE_end);
  qemu_log("    Data: %x - %x", &DATA_start, &DATA_end);
  qemu_log("    Read-only data: %x - %x", &RODATA_start, &RODATA_end);
  qemu_log("    BSS: %x - %x", &BSS_start, &BSS_end);
  qemu_log("Memory manager initialization...");

#+END_SRC

check_memory_map объявлена в kernel/src/mem/pmm.c

** Сканируем модули ядра

#+BEGIN_SRC c :noweb-ref kfun
  /**
   * @brief Инициализирует модули подключенные к ОС
   *
   */

  size_t last_module_end = 0;

  void scan_kmodules() {
      uint32_t	mods_count = multiboot->mods_count;

      for (size_t i = 0; i < mods_count; i++){
          multiboot_module_t *mod = (multiboot_module_t *) (uint32_t*)(multiboot->mods_addr + 8*i);

          last_module_end = mod->mod_end;
      }

      qemu_log("Last module ends at: %x", last_module_end);
  }

#+END_SRC

#+BEGIN_SRC c :noweb-ref kernel
  scan_kmodules();

#+END_SRC

** Инициализация pagings

#+BEGIN_SRC c :noweb-ref kernel
  init_paging();

#+END_SRC

объявлено в kernel/src/mem/pmm.c

** Помечаем используемую память

#+BEGIN_SRC c :noweb-ref kernel
  mark_reserved_memory_as_used((memory_map_entry_t *) mboot->mmap_addr, mboot->mmap_length);

#+END_SRC

объявлено в kernel/src/mem/pmm.c

** Вывод сообщения о PMM

#+BEGIN_SRC c :noweb-ref kernel
  qemu_ok("PMM Ok!");

#+END_SRC

** Инициализация VMM

#+BEGIN_SRC c :noweb-ref kernel
  vmm_init();
  qemu_ok("VMM OK!");

#+END_SRC

объявлено в kernel/src/mem/vmm.c

** Переключаем qemu логгер

#+BEGIN_SRC c :noweb-ref kernel
  switch_qemu_logging();

#+END_SRC

объявлено в kernel/src/io/ports.c

** Обработка команд указаных ядру при загрузке

#+BEGIN_SRC c :noweb-ref kernel
  kHandlerCMD((char *) mboot->cmdline);

#+END_SRC

#+BEGIN_SRC c :noweb-ref kdecl
  void kHandlerCMD(char*);
#+END_SRC


#+BEGIN_SRC c :noweb-ref kfun
  /**
   ,* @brief Обработка команд указаных ядру при загрузке
   ,*
   ,* @param cmd - Команды
   ,*/

  void kHandlerCMD(char* cmd){
      qemu_log("Kernel command line at address %x and contains: '%s'", cmd, cmd);

      if(strlen(cmd) == 0)
          return;

      uint32_t kCMDc = str_cdsp(cmd," ");
      uint32_t kCMDc_c = 0;
      char* out[128] = {0};
      str_split(cmd,out," ");
      for(int i = 0; kCMDc >= i; i++){
          kCMDc_c = str_cdsp(out[i],"=");
          char* out_data[128] = {0};
          if (kCMDc_c != 1){
              qemu_log("[kCMD] [%d] %s is ignore.",i,out[i]);
              continue;
          }
          str_split(out[i],out_data,"=");
          if (strcmpn(out_data[0],"bootscreen")){
              // Config BOOTSCREEN
              if (strcmpn(out_data[1],"minimal")){
                  bootScreenChangeMode(1);
              } else if (strcmpn(out_data[1],"light")){
                  bootScreenChangeTheme(1);
              } else if (strcmpn(out_data[1],"dark")) {
                  bootScreenChangeTheme(0);
              } else {
                  qemu_log("\t Sorry, no support bootscreen mode!");
              }
          }

          if (strcmpn(out_data[0],"NatSuki-Login")){
              __milla_setLogin(out_data[1]);
          }
          if (strcmpn(out_data[0],"NatSuki-Password")){
              __milla_setPasswd(out_data[1]);
          }
          if (strcmpn(out_data[0],"disable")){
              if (strcmpn(out_data[1],"coms")){
                  // FIXME: If uncomment following line of code, it willn't boot
                  __com_setInit(1, 0);
                  __com_setInit(2, 0);
                  __com_setInit(3, 0);
                  __com_setInit(4, 0);
                  qemu_log("\t COM-OUT DISABLED");
              } else if (strcmpn(out_data[1],"floppy")){
                  test_floppy = false;
                  qemu_log("\t FLOPPY DISABLED");
              } else if (strcmpn(out_data[1],"network")){
                  test_network = false;
                  qemu_log("\t NETWORK DISABLED");
              } else if (strcmpn(out_data[1],"pc-speaker")){
                  test_pcs = false;
                  qemu_log("\t PC-Speaker DISABLED");
              } else if (strcmpn(out_data[1],"rdsp")){
                  is_rsdp = false;
                  qemu_log("\t RDSP DISABLED");
              }  else {
                  qemu_log("\t Sorry, no support!");
              }
          }
          //qemu_log("[kCMD] [%d] %s >\n\tKey: %s\n\tValue:%s",i,out[i],out_data[0],out_data[1]);
      }
  }

#+END_SRC

kHandlerCMD использует глобальный флаги
- test_pcs
- test_floppy
- test_network
- is_rsdp

#+BEGIN_SRC c :noweb-ref kglob
  bool test_pcs = true;
  bool test_floppy = true;
  bool test_network = true;
  bool is_rsdp = true;
#+END_SRC

** Временно не работаем с VESA

#+BEGIN_SRC c :noweb-ref kernel
  drv_vbe_init(mboot);

#+END_SRC

объявлено в kernel/src/drv/video/vbe.c

** Работа с rodata-сегментом (закомментировано)

#+BEGIN_SRC c :noweb-ref kernel
  // TODO: Read-only memory for .rodata segment
  //	size_t rostart = &RODATA_start;
  //	size_t roend = &RODATA_end;
  //
  //	map_pages(
  //		get_kernel_dir(),
  //		rostart,
  //		rostart,
  //		(ALIGN(roend, PAGE_SIZE) - rostart) / PAGE_SIZE,
  //		PAGE_PRESENT
  //	);

#+END_SRC

** Регистрация драйверов

#+BEGIN_SRC c :noweb-ref kernel
  qemu_log("Registration of file system drivers...");
  fsm_reg("TARFS", 1, &fs_tarfs_read, &fs_tarfs_write, &fs_tarfs_info, &fs_tarfs_create, &fs_tarfs_delete,
          &fs_tarfs_dir, &fs_tarfs_label, &fs_tarfs_detect);
  fsm_reg("FAT32", 1, &fs_fat32_read, &fs_fat32_write, &fs_fat32_info, &fs_fat32_create, &fs_fat32_delete,
          &fs_fat32_dir, &fs_fat32_label, &fs_fat32_detect);
  fsm_reg("NatFS", 1, &fs_natfs_read, &fs_natfs_write, &fs_natfs_info, &fs_natfs_create, &fs_natfs_delete,
          &fs_natfs_dir, &fs_natfs_label, &fs_natfs_detect);
  fsm_reg("ISO9660", 1, &fs_iso9660_read, &fs_iso9660_write, &fs_iso9660_info, &fs_iso9660_create, &fs_iso9660_delete,
          &fs_iso9660_dir, &fs_iso9660_label, &fs_iso9660_detect);
  fs_natfs_init();
  kModules_Init();
  mtrr_init();
  text_init("R:\\Sayori\\Fonts\\UniCyrX-ibm-8x16.psf");
  // /Sayori/Fonts/UniCyrX-ibm-8x16.psf

#+END_SRC

fs_natfs_init определена в kernel/src/fs/natfs.c

mtrr_init определена в kernel/src/sys/mtrr.c

text_init определена в kernel/src/drv/psf.c

kModules_Init загружает модули ОС:

#+BEGIN_SRC c :noweb-ref kdecl
  void kModules_Init();
#+END_SRC

#+BEGIN_SRC c :noweb-ref kfun
  void kModules_Init(){
      qemu_log("[kModules] Loading operating system modules...");
      uint32_t	mod_start[32] = {0};
      uint32_t	mod_end[32] = {0};
      uint32_t	mod_size[32] = {0};
      uint32_t	mods_count = multiboot->mods_count;

      char mod_cmd[32][64] = {0};

      if (mods_count > 0){
          qemu_log("[kModules] Found %d modules",mods_count);

          for (size_t i = 0; i < mods_count; i++){
              mod_start[i] = *(uint32_t*)(multiboot->mods_addr + 8*i);
              mod_end[i] = *(uint32_t*)(multiboot->mods_addr + 8*i + 4);

              mod_size[i] = mod_end[i] - mod_start[i];

              multiboot_module_t *mod = (multiboot_module_t *) (uint32_t*)(multiboot->mods_addr + 8*i);

              strcpy(mod_cmd[i], (char*)mod->cmdline);

              qemu_log("[kModules] Found module number `%d`. (Start: %x | End: %x | Size: %d) CMD: %s (%s)",
                       i,
                       mod_start[i],
                       mod_end[i],
                       mod_size[i],
                       mod_cmd[i],
                       (char*)mod->cmdline
                  );

              if (strcmpn(mod_cmd[i],"initrd_sefs")){
                  initrd_sefs(mod_start[i], mod_end[i]);
                  continue;
              }
              if (strcmpn(mod_cmd[i],"initrd_tarfs")){
                  initrd_tarfs(mod_start[i], mod_end[i]);
              }
          }

          qemu_log("Memory manager need to be feed with this information: Last module ends at: %x", last_module_end);
      } else {
          qemu_log("[kModules] No modules were connected to this operating system.");
      }
  }

#+END_SRC

kModules_Init использует initrd_sefs чтобы смонтировать виртуальный диск:

#+BEGIN_SRC c :noweb-ref kdecl
  void initrd_sefs(size_t, size_t);
#+END_SRC

#+BEGIN_SRC c :noweb-ref kfun
  /**
   ,* @brief Монтирует виртуальный диск с файловой системой Sayori Easy File System
   ,*
   ,* @param irdst - Точка монтирования
   ,* @param irded - Конец точки монтирования
   ,*/

  void initrd_sefs(size_t irdst, size_t irded){
      if (initRD){
          return;
      }

      qemu_log("[InitRD] [SEFS] Initialization of the virtual disk. The SEFS virtual file system is used.");
      qemu_log("[InitRD] [SEFS] The virtual disk space is located at address %x.", irdst);
      qemu_log("[InitRD] [SEFS] The virtual disk space is ends at %x.", irded);
  }

#+END_SRC

initrd_sefs использует глобальный флаг initRD (зачем?)

#+BEGIN_SRC c :noweb-ref kglob
  bool initRD = false;
#+END_SRC

** Инициализация Virtual Video Manager

#+BEGIN_SRC c :noweb-ref kernel
  qemu_log("Initializing the virtual video memory manager...");
  init_vbe(mboot);

#+END_SRC

объявлено в kernel/src/io/screen.c

** Инициализация Task Manager

#+BEGIN_SRC c :noweb-ref kernel
  qemu_log("Initializing Task Manager...");
  init_task_manager();

#+END_SRC

see kernel/src/sys/scheduler.c

** Очистка экрана

#+BEGIN_SRC c :noweb-ref kernel
  clean_screen();

#+END_SRC

** Инициализация шрифтов

#+BEGIN_SRC c :noweb-ref kernel
  qemu_log("Initalizing fonts...");
  tty_fontConfigurate();

#+END_SRC

** Инициализация устройств

#+BEGIN_SRC c :noweb-ref kernel
  draw_vga_str("Initializing devices...", 23, 0, 0, 0xffffff);
  punch();
  keyboardInit();
  mouse_install();
  ata_init();

#+END_SRC

** Калибрация температуры процессора

#+BEGIN_SRC c :noweb-ref kernel
  cputemp_calibrate();

#+END_SRC

** Инициализация файловых дескрипторов

#+BEGIN_SRC c :noweb-ref kernel
  file_descriptors_init();

#+END_SRC

** Инициализация загрузочного экрана

#+BEGIN_SRC c :noweb-ref kernel
  bootScreenInit(9);
  bootScreenLazy(true);

#+END_SRC

** Настройка системных вызовов

#+BEGIN_SRC c :noweb-ref kernel
  bootScreenPaint("Настройка системных вызовов...");
  qemu_log("Registering System Calls...");
  init_syscalls();

#+END_SRC

** Запоминаем время старта ядра

#+BEGIN_SRC c :noweb-ref kernel
  kernel_start_time = getTicks();

#+END_SRC

#+BEGIN_SRC c :noweb-ref kglob
  size_t kernel_start_time = 0;
#+END_SRC

** Настройка окружения

#+BEGIN_SRC c :noweb-ref kernel
  bootScreenPaint("Настройка ENV...");
  qemu_log("Registering ENV...");
  configure_env();

#+END_SRC

** Опредение процессора

#+BEGIN_SRC c :noweb-ref kernel
  bootScreenPaint("Определение процессора...");
  detect_cpu(1);

#+END_SRC

** Конфигурация триггеров

#+BEGIN_SRC c :noweb-ref kernel
  bootScreenPaint("Конфигурация триггеров...");
  triggersConfig();

#+END_SRC

** Инициализация списка сетевых карт

#+BEGIN_SRC c :noweb-ref kernel
  bootScreenPaint("Инициализация списка сетевых карт...");
  netcards_list_init();

#+END_SRC

** Инициализация сетевого стека

#+BEGIN_SRC c :noweb-ref kernel
  bootScreenPaint("Инициализация сетевого стека...");
  netstack_init();
  bootScreenPaint("Инициализация ARP...");
  arp_init();
  bootScreenPaint("Инициализация RTL8139...");
  rtl8139_init();
  bootScreenPaint("Инициализация DHCP...");
  dhcp_init_all_cards();
  bootScreenPaint("Готово...");
  bootScreenClose(0x000000, 0xFFFFFF);
  tty_set_bgcolor(COLOR_BG);

#+END_SRC

** Дата компиляции

#+BEGIN_SRC c :noweb-ref kernel
  tty_printf("SayoriOS v%d.%d.%d\nДата компиляции: %s\n",
             VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH,    // Версия ядра
             __TIMESTAMP__                                   // Время окончания компиляции ядра
      );
  tty_printf("\nВлюбиться можно в красоту, но полюбить - лишь только душу.\n(c) Уильям Шекспир\n");

#+END_SRC

** NatSuki

#+BEGIN_SRC c :noweb-ref kernel
  if (__milla_getCode() != 0) {
      tty_error("[ОШИБКА] [NatSuki] Не удалось выполнить инициализацию. Код ошибки: %d", __milla_getCode());
  }

#+END_SRC

** Текущее время

#+BEGIN_SRC c :noweb-ref kernel
  sayori_time_t time = get_time();
  tty_printf("\nВремя: %d:%d:%d\n", time.hours, time.minutes, time.seconds);

#+END_SRC

** Listing ATA disks

#+BEGIN_SRC c :noweb-ref kernel
  _tty_printf("Listing ATA disks:\n");
  ata_list();

#+END_SRC

** Иницилизация тасков

#+BEGIN_SRC c :noweb-ref kernel
  tty_taskInit();

#+END_SRC

** RSDP

#+BEGIN_SRC c :noweb-ref kernel
  if (is_rsdp){
      RSDPDescriptor* rsdp = rsdp_find();
      find_facp(rsdp->RSDTaddress);
      find_apic(rsdp->RSDTaddress);
  }

#+END_SRC

** Processors

#+BEGIN_SRC c :noweb-ref kernel
  tty_printf("Processors: %d\n", system_processors_found);

#+END_SRC

** FIXME write to null

#+BEGIN_SRC c :noweb-ref kernel
  // FIXME: WOW! We can write into 0!
  //	*((volatile int*)0) = 0x12345678;
  //	qemu_log("Data: %x", *((volatile int*)0));

#+END_SRC

** Test network

#+BEGIN_SRC c :noweb-ref kernel
  if (test_network) {
      _tty_printf("Listing network cards:\n");

      uint8_t mac_buffer[6] = {0};

      for (int i = 0; i < netcards_get_count(); i++) {
          netcard_entry_t *entry = netcard_get(i);

          _tty_printf("\tName: %s\n", entry->name);
          entry->get_mac_addr(mac_buffer);

          _tty_printf("\tMAC address: %v:%v:%v:%v:%v:%v\n",
                      mac_buffer[0],
                      mac_buffer[1],
                      mac_buffer[2],
                      mac_buffer[3],
                      mac_buffer[4],
                      mac_buffer[5]
              );
      }
  }
  qemu_log("Kernel bootup time: %f seconds.", (double) (getTicks() - kernel_start_time) / getFrequency());

#+END_SRC

** Test floppy

#+BEGIN_SRC c :noweb-ref kernel
  //	if (test_floppy){
  //		initFloppy();
  //		fatTest();
  //		_smfs_init();
  //	}

#+END_SRC

** System processors found

#+BEGIN_SRC c :noweb-ref kernel
  // tty_printf("Processors found: %d\n", system_processors_found);

#+END_SRC

** MBR Info

#+BEGIN_SRC c :noweb-ref kernel
  // _mbr_info();

#+END_SRC

** ATA DMA Init

#+BEGIN_SRC c :noweb-ref kernel
  ata_dma_init();
#+END_SRC


** AC'97 Init

#+BEGIN_SRC c :noweb-ref kernel
  ac97_init();
  //ac97_test();

#+END_SRC

** Various commented strings

#+BEGIN_SRC c :noweb-ref kernel
  //	string_t* str = string_from_charptr("There's Pikachu, Eevee, Charmander and even Scyther is there!");
  //	vector_t* vec = string_split(str, " ");
  //
  //	qemu_log("Original string is: '%s'", str->data);
  //
  //	for(int i = 0; i < vec->size; i++) {
  //		char* string = ADDR2STRING(vec->data[i])->data;
  //
  //		qemu_log("%s", string);
  //	}
  //
  //	string_split_free(vec);
  //	string_destroy(str);
  //	qemu_log("%x", is_long_mode_supported());
  //	char* args[] = {"hello"};
  //	run_elf_file("R:\\Applications\\hello", 1, args);
  //	netcard_entry_t* card = netcard_get(0);
  //
  //	uint8_t ip[4] = {192, 168, 2, 2};
  //
  //	udp_send_packet(card, ip, 8888, 9999, "EEVEE\n", 6);

#+END_SRC

** AHCI

#+BEGIN_SRC c :noweb-ref kernel
  ahci_init();
  // ahci_test();

#+END_SRC

** Закругленные квадраты

#+BEGIN_SRC c :noweb-ref kernel
  //    *(int*)(0xAB000ACD) = 3456789;
  /// Пример закругленных квадратов
  // drawRoundedSquare(32,32, 128, 2, 0xFFFF0000, 0xFF0000FF);
  // drawRoundedRectangle(32,32,128,16,4,0xFFFF0000, 0xFF0000FF);
  // punch();
  // while (1){}
  /// КОНЕЦ ПРИМЕРА

#+END_SRC

** Ada Test

#+BEGIN_SRC c :noweb-ref kernel
  //	ada_t *ada = ada_file("R:\\ada\\test.ada");
  // void* buf = kcalloc(1, 512);
  //
  //    int buf_read = dpm_read('C',0,512,buf);
  //
  //    qemu_log("Buff (%d|%d) %s",buf_read, strlen(buf),buf);
  //
  //    buf_read = dpm_read('E',0,512,buf);
  //    qemu_log("Buff (%d|%d) %s",buf_read, strlen(buf),buf);
  //
  // kfree(buf);

#+END_SRC

** DPM Update

#+BEGIN_SRC c :noweb-ref kernel
  /// Обновим данные обо всех дисках
  fsm_dpm_update(-1);

#+END_SRC

** Seek test

#+BEGIN_SRC c :noweb-ref kernel
  // SEEK TEST
  //    char buffer[11] = {0};
  //
  //    FILE* myfile = fopen("C:\\finnish_numerals.txt", "r");
  //
  //    for(int i = 0; i < 15; i++) {
  //        memset(buffer, 0, 10);
  //
  //        fread(myfile, sizeof(char), 10, buffer);
  //
  //        tty_printf("%s", buffer);
  //    }

#+END_SRC

** JS test

#+BEGIN_SRC c :noweb-ref kernel
  //    elk_file("R:\\jse\\libs.js");
  //    void k();
  //    create_process(k, "Test process", false, true);

#+END_SRC

** Rust test

#+BEGIN_SRC c :noweb-ref kernel
  //    extern void rust_main();
  //    rust_main();

#+END_SRC

** Инициализация Virtio Network

#+BEGIN_SRC c :noweb-ref kernel
  // vio_ntw_init();

#+END_SRC

** Intel HDA Init

#+BEGIN_SRC c :noweb-ref kernel
  extern void intel_hda_init();
  intel_hda_init();

#+END_SRC

** Вызов командной строки

#+BEGIN_SRC c :noweb-ref kernel
  cli();

#+END_SRC

** Финал

Выход из ядра никогда не случится, так как в cli() стоит while(1), но возвращаемое значение нужно компилятору

#+BEGIN_SRC c :noweb-ref kernel
  return 0;
#+END_SRC
