Ветка trunk ядра KolibriOS. Путеводитель-справочник по исходному коду. Автор(ы): rgimad v0.0.2 ---------------------- / ---------------------- ==== init.inc - mem_test тестирование памяти. Если bios имеет функцию 0xE820, то тестирование не производится и происходит выход из функции. - init_mem инициализация системной таблицы страниц - init_page_map TODO - init_BIOS32 TODO - test_cpu получение информации о процессоре. - acpi_locate поиск поиск структуры RSDP (Root System Description Pointer) которая используется в ACPI. функция возвращает указатель на RSDP в регистре eax https://wiki.osdev.org/RSDP - rsdt_find в ecx принимает адрес RSDT, в edx сигнатуру таблицы, которую нужно найти (например ACPI_FADT_SIGN) в eax возвращает адрес требуемой таблицы https://wiki.osdev.org/RSDT - check_acpi TODO - init_hpet Инициализация HPET (High Perfomance Event Timer) ==== kernel32.inc этот файл не содержить реализаций и является по сути заголовочным здесь содержатся инклюды .inc файлов почти всех подсистем ядра ==== proc32.inc содержит макросы для определения и вызова процедур: - stdcall proc directly call STDCALL procedure - invoke proc indirectly call STDCALL procedure - ccall proc directly call CDECL procedure - cinvoke proc indirectly call CDECL procedure - proc ==== macros.inc содержит разные полезные макросы, в том числе для работы со связными списками - $Revision Клиент svn при работе с репозиторием после ключевого слова $Revision: подставляет текущую ревизию файла. В macros.inc из исходников ядра, подключающемся первым, определён макрос fasm'а $Revision, который вычисляет максимум из всех мест, где он встретился (то есть во всех файлах, прямо или косвенно подключаемым к kernel.asm), каковой максимум и является номером самой свежей ревизии ядра (не драйверов). В конце kernel.asm он присваивается переменной __REV__, ну а boot/bootstr.inc включает значение __REV__ в начальную выводимую строку. - diff16 TODO - diff10 TODO - dbgstr вывод строки на доску отладки. макросы для работы со связными списками: - list_init head - __list_add - внутреннее использование - list_add - list_add_tail - list_del ==== struct.inc содержит макросы для объявления структур и работы с ними - struct макрос для объявления структур ==== fdo.inc Formatted Debug Output (FDO) Это набор макросов для повышения удобства отладки приложений. Работает подобно функции printf(), выводя на доску отладки форматированные сообщения. доступные спецификаторы формата %s, %d, %u, %x (с частичной поддержкой ширины) Использовать обычно нужно только DEBUGF, хотя доступны также и DEBUGS, DEBUGD, DEBUGH. Примеры: DEBUGF 1, "%s - %d (%x)", eax, 123, ch DEBUGF 1, "%d.%d.%d.%d", [ip+0]:1, [ip+1]:1, [ip+2]:1, [ip+3]:1 DEBUGF 1, <"function ", __FNAME__, ": %s - %x - %u">, "text here", [var]:5, [esp+16] DEBUGF 1, "[%d][%d][%d][%d][%d]\n", al, ax, ebx, [eax], [eax]:1 DEBUGF 2, "[%u][%u][%u][%u][%u]\n", al, ax, ebx, [eax], [eax]:1 DEBUGF 3, "[%x][%x][%x][%x][%x]\n", al, ax, ebx, [eax], [eax]:1 DEBUGF 4, "[%s][%s][%s][%s][%s][%s][%s]\n", "string":4, eax, eax:5, [ebx]:5, eax:ecx, eax:[ecx], eax:byte[ecx] Ширина: for %s - any number, register, or in-memory variable for %d - 1, 2, 4 (only for in-memory arguments) for %u - 1, 2, 4 (only for in-memory arguments) for %x - 1 .. 8 ==== data32.inc содержит в основном строковые константы на различных языках (русский, английский и т.д). также в здесь объявлены некотрые переменные и настройки ядра включает в себя файлы data32sp.inc (испанский) и data32et.inc (эстонский) - keymap содежит раскладку клавиатуры (128 байт) - keymap_shift раскладка клавиатуры при зажатой Shift - keymap_alt раскладка клавиатуры при зажатой Alt ... - далее идут объявления констант с именами boot_* это те самые сообщения, которые пишутся белым шрифтом на черном фоне при загрузке KolibriOS ... - далее идут константы, содержащие пути к драйверам, некоторым файлам - shmem_list TODO - dll_list TODO - pcidev_list TODO ... - syslang хранит номер языка системы (1 - en, 4 - ru и тд) - gdts судя из названия метки: gdts = GDT Start GDT это Global Descriptor Table, глобальная таблица дескрипторов, в которой описываются сегменты памяти далее описаны элементы этой таблицы то есть сегменты: os_code_l, int_data_l и os_data_l, app_code_l, app_data_l, pci_code_32, pci_data_32, apm_code_32, apm_code_16, apm_data_16, graph_data_l, tss0_1, tls_data_l - gdte после описания всех сегментов стоит это метка. судя из названия: gdte = GDT End ... - pg_data TODO ... - sys_pgmap таблица физических страниц. представляет собой массив битов, который для каждой физической страницы описывает, выделена она или свободна. ==== const.inc содержит определения констант и структур ядра. ... Константы флагов страниц: PAGE_SIZE = 4096 PG_UNMAP = 0x000 PG_READ = 0x001 PG_WRITE = 0x002 PG_USER = 0x004 PG_PCD = 0x008 PG_PWT = 0x010 PG_ACCESSED = 0x020 PG_DIRTY = 0x040 PG_PAT = 0x080 PG_GLOBAL = 0x100 PG_SHARED = 0x200 PG_SWR = 0x003 ; PG_WRITE + PG_READ PG_UR = 0x005 ; PG_USER + PG_READ PG_UWR = 0x007 ; PG_USER + PG_WRITE + PG_READ PG_NOCACHE = 0x018 ; PG_PCD + PG_PWT ... TODO: написать про остальное ==== kglobals.inc содержит макросы для объявления глобальных данных (макросы iglobal ... endg и uglobal ... endg) - use "iglobal" for inserting initialized global data definitions. - use 'uglobal' for inserting uninitialized global definitions. (even when you define some data values, these variables will be stored as uninitialized data) ==== encoding.inc содержит макросы для конвертирования и объявления кодировок ==== unpacker.inc это lzma-распаковщик, содержит одну единственную функцию void unpack(void* packed_data, void* unpacked_data); TODO: вопрос - а как задается для данных которые нужно распаковать? т.е как эта функция определяет длину packed_data? ---------------------- detect/ ---------------------- ==== detect/biosdisk.inc получение информации о жестких дисках с помощью функций bios ==== detect/biosmem.inc получение memory map с помощую функции bios'а memory map это структура, описывающая физическую оперативную память компьютера ==== detect/dev_fd.inc поиск и занесение в таблицу приводов FDD (Floppy Disk Drive т.е привод гибких дисков т.е дискет) ==== detect/disks.inc в этом заголовочном файле просто 4 инклюда: include 'dev_fd.inc' include 'dev_hdcd.inc' include 'getcache.inc' include 'sear_par.inc' ==== detect/init_ata.inc инициализация ATA интерфейса, поиск, инициализация и настройка IDE контроллеров сначала идет код поиска IDE контроллера в списке pci устройств и определение его BAR регистров - set_pci_command_bus_master установить bus master бит в командный pci регистр - Init_IDE_ATA_controller инициализация ide контроллера - Init_IDE_ATA_controller_2 ==== detect/dev_hdcd.inc поиск hdd и cd дисков, чтение информации о них, функции посылки команд на них - FindHDD - FindHDD_2 - FindHDD_1 - calculate_IDE_device_values_storage - convert_Sector512_value - ReadHDD_ID Чтение идентификатора жесткого диска Входные параметры передаются через глобальные переменные: ChannelNumber - номер канала (1 или 2); DiskNumber - номер диска на канале (0 или 1). Идентификационный блок данных считывается в массив Sector512. - find_IDE_controller_done инициализирует до трех ide контроллеров настраивает ide кэш ищет разделы на диске ... если включен extended_primary_loader, то выполняется код из boot/rdload.inc ==== detect/getcache.inc настройка IDE кэша ==== detect/sear_par.inc поиск разделов на дисках ==== boot/rdload.inc загрузчик ram диска ==== detect/vortex86.inc обнаружение и получение информации о SoC Vortex86 для компьютеров на базе нее ---------------------- core/ ---------------------- ==== core/syscall.inc Содержит таблицу системных вызовов и функции для осуществления системных вызовов различными способами. TODO: чем отличаются следующие три функции? когда какая применяется? - sysenter_entry - i40 - syscall_entry - servetable2 Это таблица системных вызовов. Хранит адреса функций-обработчиков системных вызовов, начиная с 0ой и заканчивая 80ой. ==== core/apic.inc реализована работа с APIC (Advanced Programmable Interrupt Controller) и PIC APIC состоит из двух модулей - LAPIC (свой для каждого ядра процессора) и IOAPIC (контроллер на системной плате) в начале объявлены глобальные перменные и константы. Например переменная irq_mode обозначет текущий режим прерываний. Она принимает значение IRQ_PIC и IRQ_APIC TODO: описать предназначение каждой перменной и константы содержит функции: - APIC_init эта функция отвечает за APIC. она инициализирует конроллер IOAPIC и вызывает функцию LAPIC_init - LAPIC_init инициализация LAPIC - IOAPIC_read чтение регистра из контроллера IOAPIC. номер регистра передается в EAX, значение возвращеется в EAX - IOAPIC_write запись значения в регистр контроллера IOAPIC номер регистра передается в EAX, значение передается в EBX, функция ничего не возвращает - PIC_init инициализация PIC (Programmable Interrupt Controller, программируетмый контроллер прерываний) она выполняет Remap all IRQ to 0x20+ Vectors, IRQ0 to vector 0x20, IRQ1 to vector 0x21... - PIT_init инициализирует PIT (Programmable Interval Timer, программируемый интервальный таймер) устанавливает интервал срабатывания 1/100 секунды - unmask_timer TODO - IRQ_mask_all отключить все irq - irq_eoi послать сигнал EOI (End Of Interrupt, конец прерывания). В cl передается номер irq - proc enable_irq stdcall, irq_line:dword - proc disable_irq stdcall, irq_line:dword - pci_irq_fixup - get_clock_ns - acpi_get_root_ptr возращает в eax Root System Description Pointer (есть такая структура данных в ACPI) ==== core/debug.inc содержит реализазию системной функции 69 - отладка. (см. http://wiki.kolibrios.org/wiki/SysFn69/) ==== core/clipboard.inc содержит реализацию системной функции54 - работа с буфером обмена. (см. http://wiki.kolibrios.org/wiki/SysFn54/) ==== core/string.inc Реализует следующие функции для работы со строками: size_t strncat(char *s1, const char *s2, size_t n); // Append string s2 to s1. char *strchr(const char *s, int c); // int strncmp(const char *s1, const char *s2, size_t n); // char *strncpy(char *s1, const char *s2, size_t n); // Copy string s2 to s1. proc strrchr stdcall, s:dword, c:dword; Look for the last occurrence a character in a string. ==== core/conf_lib.inc загружает настройки ядра из файла /sys/sys.conf для чтения ini использует функции libini.obj (?) содержит фцнкции: - proc set_kernel_conf читает из sys.conf параметры mouse_speed, mouse_delay, midibase и применяет их - proc strtoint конвертация строки в dword, сама оределяет hex или dec, по наличию или отсутствию 0x, 0X - proc strtoint_dec - proc strtoint_hex - proc do_inet_adr stdcall,strs ковертировать строковую запись ip адреса в dword ==== core/memory.inc - alloc_page (proc alloc_page) функция выделения одной физической страницы. функция ищет первую свободную физическую страницу в таблице физических страниц (sys_pgmap ?). Найдя страницу, page_start устанавливается на следующую страницу после найденной. но есть при следующем выове свободные страницы бцдцт искаться начиная с нее. Система хранит массив битов, который для каждой физической страницы описывает, выделена она или свободна, а также вспомогательные переменные: подсказку [page_start] - нижнюю границу при поиске свободной страницы (указатель внутри битового массива, относительно которого известно, что все предшествующие данные забиты единицами, а соответствующие страницы выделены), указатель [page_end] на конец массива, число свободных страниц [pg_data.pages_free]. in: ничего out: eax = физ. адрес выделенной физической страницы, или eax = 0 если свободной страницы не нашлось. - alloc_pages (proc alloc_pages stdcall, count:dword) функция выделения нескольких физических страниц, выделяющая связный диапазон, причём кратный 8 страницам in: требуемое количество страниц out: eax = начальный физ. адрес выделенного диапазона страниц, или eax = 0 если не удалось выделить. - map_page (proc map_page stdcall,lin_addr:dword,phis_addr:dword,flags:dword) функция отображения указанной физической страницы по указанному линейному адресу (стандартное добавление элемента в таблицу страниц; работает и с user-mode пространством). способная также размаппить страницу (нулевой элемент таблицы страниц соответствует свободной линейной странице). in: lin_addr - по какому линейному адресу отобразить страницу phis_addr - физ. адрес отображаемой страницы flags - какие флаги поставить на страницу (констанды этих слагов есть в data32.inc, например PG_SWR и тд) out: ничего - map_space объявлена, но не реализована, предназначение неясно. - free_page (proc free_page) функция освобождения ранее выделенной физической страницы in: eax = физ. адрес страницы out: ничего - map_io_mem (proc map_io_mem stdcall, base:dword, size:dword, flags:dword) функция, создающая отображение заданного блока физических страниц в адресном пространстве ядра Как работает: вызывает alloc_kernel_space (см. core/heap.inc), а потом добавляет в таблицу страниц преобразование указанных физических адресов на только что выделенные линейные. in: base = начало блока size = размер блока flags = какие флаги поставить на виртуальные страницы out: eax = линейный адрес начала блока страниц - commit_pages аналогичная map_page функция, только для блока непрерывных физических адресов. отображает непрерывный блок физических адресов по указанному линейному адресу in: eax = page base + page flags ebx = linear address ecx = count out: ничего - release_pages функция, которая принимает линейный адрес и размер блока и одновременно размаппит из линейных адресов и освобождает физические страницы из этого блока. in: eax = base ecx = count out: ничего - unmap_pages функция, обратная commit_pages. Размаппит страницы принадлежащие указанному непрерывному блоку линейных адресов. - map_page_table TODO - alloc_dma24 TODO - create_trampoline_pgmap TODO - new_mem_resize TODO - get_pg_addr функция получения физического адреса по указанному линейному - page_fault_handler TODO - map_mem_ipc TODO - map_memEx TODO - safe_map_page TODO - sys_IPC реализация системной функции 60 - sys_ipc_send реализация подфункции 2 сисфункции 60 - sysfn_meminfo реализация подфункции 20 системной функции 18. кстати, таблица подфункций сисфункции 18 находится в kernel.asm, это метка sys_system_table обработчик сисфункции 18 sys_system - f68 реализует подфункции 11-29 системной функции 68 - f68call это таблица подфункций 11-29 системной функции 68 - load_pe_driver эту функцию использует реализация подфункции 21 системной функции 68 - create_ring_buffer TODO - print_mem TODO ==== core/heap.inc в начале объявляется структура MEM_BLOCK и константы MEM_BLOCK_RESERVED, MEM_BLOCK_FREE, MEM_BLOCK_USED, MEM_BLOCK_DONT_FREE также объявляется calc_index. содержит следующие функции: - md TODO - init_kernel_heap инициализация кучи ядра. - get_small_block найти первый блок >= требуемого размера. аргументы : eax = требуемый размер возвращаемое значение: edi = указатель на блок; ebx= индекс блока - free_mem_block TODO - alloc_kernel_space выделяет непрерывный диапазон в адресном пространстве ядра - free_kernel_space освобождает непрерывный диапазон в адресном пространстве ядра - kernel_alloc TODO - kernel_free TODO далее функции пользовательской кучи - init_heap TODO - user_alloc TODO - user_alloc_at TODO - user_free TODO - user_unmap TODO - user_normalize TODO - user_realloc TODO далее функции shared memory - destroy_smap TODO - shmem_open TODO - shmem_close TODO далее функции user land ring buffers - user_ring TODO ==== core/sys32.inc - функции управления задачами функции: - build_interrupt_table TODO - page_fault_exc TODO - show_error_parameters TODO - lock_application_table TODO - unlock_application_table TODO - sys_resize_app_memory Реализация единственной подфункции 1 системной функции 64 - terminate Завершение процесса, когда системный поток получает управление (главный цикл системы), одним из его действий является проход по списку процессов, поиск потоков в завершающемся состоянии и убийство таких процессов. Все нижеследующие действия происходят в контексте системного потока. Захватывает доступ на запись к таблицам потоков (lock_application_table) проходит по списку объектов ядра и вызывает деструкторы если этот поток - последний в своём процессе, уничтожает адресное пространство освобождает разные системные ресурсы, которые мог выделить этот поток и которых нет в списке объектов ядра (список горячих клавиш, список кнопок, определённых потоком) если поток отлаживается, посылает извещение отладчику освобождает память под kernel-mode стек и область сохранения FPU/SSE освобождает карту ввода/вывода (если она была изменена - есть стандартная карта ввода/вывода, которая создаётся при загрузке и разделяется между всеми потоками) если окно потока было на вершине оконного стека, активирует следующее окно если поток рухнул (или был прибит) в процессе работы с жёстким диском, освобождает мьютекс занятости жёсткого диска; то же самое для CD и дискеты освобождает выделенные потоком IRQ и порты если текущий прибиваемый процесс - отладчик, помечает как завершающиеся все отлаживаемые им процессы перерисовывает экран Освобождает доступ на запись к таблицам потоков (unlock_application_table) - destroy_thread - protect_from_terminate - unprotect_from_terminate - request_terminate ==== core/sched.inc - планировщик задач - irq0 обработчик прерывания интервального таймера (irq 0) Увеличивает текущее время (число сотых долей секунды, прошедших с загрузки системы, может быть получено в приложении функцией 26.9, много где используется внутри ядра) Вызывает процедуру обработки текущей ноты для писка, описанного в предыдущем абзаце; Каждую 100-ю итерацию (каждую секунду) обнуляет счётчик "тактов в предыдущую секунду" (поле в структуре потока) у всех потоков; Служит планировщиком, переключаясь на следующую задачу; алгоритм выбора описан в предыдущем посте, а при переключении увеличивается счётчик тактов у текущего потока (от которого управление уходит) и заполняются системные структуры - kernel-mode стек, карта разрешения ввода/вывода, page table (cr3), отладочные регистры drN (если нужно) и устанавливает бит TS в cr0. (Регистры CPU хранятся в системном стеке, так что popa после переключения стека автоматически восстановит регистры задачи, которая стала текущей.) Последнее действие нужно для "ленивой выгрузки" контекста FPU/MMX/SSE: если этот контекст переключать сразу, это займёт какое-то время, при том, что новая задача, возможно, вообще не использует ничего, кроме CPU; поэтому эти регистры остаются на своих местах, но устанавливается флаг TaskSwitch, в результате чего при следующем обращении к регистрам (именно обращении! когда нужно действительно переключать весь контекст) процессор возбудит исключение, обработчик которого молча сохранит регистры ушедшего потока, загрузит регистры нового потока и перезапустит инструкцию, сделав вид, что ничего не случилось. - change_task данная функция с помощью find_next_task находит следующий таск на котрый нужно переключиться. и переключается на него с помощью вызова do_change_task - update_counters TODO - updatecputimes TODO - do_change_task функция переключения контекста на заданный таск. параметры передаются так: ebx = address of the APPDATA for incoming task (new) warning: [CURRENT_TASK] and [TASK_BASE] must be changed before (e.g. in find_next_task) [current_slot] is the outcoming (old), and set here to a new value (ebx) - scheduler_add_thread TODO - scheduler_remove_thread TODO - find_next_task Найти новый таск для передачи ему управления принимает аргументы: bl = SCHEDULE_ANY_PRIORITY: учитывать таски со всеми приоритетами bl = SCHEDULE_HIGHER_PRIORITY: учитывать только таски с приоритетом больше приоритета текущего таска продолжаем исполнять текущий таск, если не найдется готовых тасков с приоритетом меньшим чем у текущего возвращаемое значение: ebx = address of the APPDATA for the selected task (slot-base) edi = address of the TASKDATA for the selected task ZF = 1 if the task is the same предупреждение: [CURRENT_TASK] = bh , [TASK_BASE] = edi -- as result [current_slot] is not set to new value (ebx)!!! scratched: eax,ecx - pick_task TODO - shed TODO - enqueue TODO ==== core/taskman.inc - fs_execute Создание процесса. принимает на вход имя бинарного файла для загрузки, параметры командной строки для нового процесса и флаги, сейчас только то, запускается процесс как отлаживаемый или как обычный. (edx = flags; ecx -> cmdline; ebx -> absolute file path; eax = string length ) Загружает бинарник (целиком в память ядра; если он упакован kpack'ом, то распаковывается в памяти); Проверяет заголовок исполняемого файла, вычисляются нужные параметры (есть две версии заголовка, мало отличающиеся); Захватывает доступ на запись к таблицам потоков (lock_application_table) Находит пустой слот для нового потока; если такого нет (255 потоков уже запущены) - выход с ошибкой; Заполняет имя процесса; Создаёт новое адресное пространство (это отдельная история); Вызывает функцию set_app_params, заполняющую остальные поля структуры потока (подробнее - ниже); Освобождает доступ на запись к таблицам потоков (unlock_application_table) - test_app_header TODO - alloc_thread_slot TODO - create_process TODO - destroy_page_table TODO - destroy_process TODO - pid_to_slot Поиск номера слота таска по его PID. Принимает PID в eax, возращает номер слота в eax, или 0 если таска с таким PID не существует. - check_region TODO - read_process_memory TODO - write_process_memory TODO - new_sys_threads Создание нового потока. Принимает на вход entry point нового процесса и указатель на user-mode стек. Захватывает доступ на запись к таблицам потоков (lock_application_table) Находит пустой слот для нового потока; если такого нет - выход с ошибкой; Копирует имя процесса и информацию об адресном пространстве вызывающего потока в структуру для нового; Вызывает set_app_params Освобождает доступ на запись к таблицам потоков (unlock_application_table) - map_process_image TODO - common_app_entry TODO - set_app_params Выделяет в адресном пространстве ядра буфер под стек ядра и область для сохранения состояния FPU и SSE; Инициализирует разные параметры потока значениями по умолчанию; Копирует командную строку и путь к приложению в адресное пространство процесса по адресам, записанным в заголовке бинарника (или не копирует, если эти значения в заголовке нулевые, что означает, что программа в них не нуждается); Выделяет очередной идентификатор (каждый следующий TID равен предыдущему + 1); инициализирует user-mode контекст, значения eip и esp берутся из параметров вызова для sys_new_threads и из заголовка для fs_execute; Если новый процесс загружается как отлаживаемый, то помечает его состояние как замороженное, иначе - как работающее (начиная с этого места на новый поток возможны переключения задач). - get_stack_base TODO