Это не реальная система операций. Это просто простая система операций, созданная в образовательных целях.
Основная цель, которой я следую - узнать, как ОС работает с нуля. Начиная с собственного загрузочного сектора, прерывания аппаратного и программного обеспечения, собственные драйверы.
Репозиторий суффикс с GCC, потому что я планирую написать еще одну простую ОС с ржавчиной. Итак, я надеюсь, что будут Ghaiklor-OS-GCC и Ghaiklor-Os-Rustc .
| Привет, мир |
|---|
Ghaiklor-OS-GCC состоит из двух файлов в необработанном двоичном формате: boot.bin и kernel.bin . Они расположены в загрузке/boot.bin и ядра/ядра. Стоит соответственно после компиляции.
Boot.bin скомпилируется через NASM . Makefile принимает загрузку/загрузку и вызовы nasm boot/boot.asm -f bin -o boot/boot.bin . Ручки NASM включает в себя от самих субсолдеров, поэтому все файлы сборки будут скомпилированы в двоичный файл. Ничего больше, просто.
kernel.bin скомпилируется через GCC и LD , которые должны установить кросс-компилятор. Посмотрите на раздел среды развития. После того, как кросс-компилятор установлен, мы можем рекурсивно взять источники из процессора , драйверов , включить папки ядра и LIBC . Все файлы .c скомпилированы через gcc . Скомпилированные объектные файлы используются для компиляции kernel.bin ld -o kernel/kernel.bin -Ttext 0x1000 <OBJ_FILES> --oformat binary
OS-IMAGE.BIN IS составлено Concatenate of Boot.bin и Kernel.bin . Легко достигнуто с помощью cat boot/boot.bin kernel/kernel.bin > os-image.bin .
Я написал сценарий bootstrap.sh , который вы можете запустить. Он установит все необходимые зависимости для вашей хост -машины.
bash bootstrap.shКогда компьютер включен или сброшен, он проходит через серию диагностики, называемую Post-Power-Self-Test. Эта последовательность завершается поиском загрузочного устройства, такого как гибкий, CDROM или жесткий диск.
Устройство загружается, если оно несет загрузочный сектор с последовательности байтов 0x55 , 0xAA в байтах 511 и 512 соответственно. Когда BIOS находит такой загрузочный сектор, он загружается в память в 0x0000:0x7C00 .
Простая реализация загрузочного устройства:
jmp $
times 510 - ($ - $$) db 0
dw 0xAA55 $ - $$ Результаты в CURRENT_POINTER - START_POINTER . Таким образом, мы рассчитываем, как долго наша загрузочная запись. После этого мы находимся в субстрате 510 и заполняем нули, получая загрузочную запись 512 байт с подписью загрузочного сектора.
Например, у нас есть $ - $$ , равное 100. Итак, у нас есть 510 - 100 = 410 бесплатных байтов. Мы заполняем эти 410 байтов нулями. И последние два байта 511 и 512 являются загрузочной подписью, которую мы заполняем dw 0xAA55 .
Сделанный! У нас есть наше загрузочное устройство, и мы можем заменить наш jmp $ на любой код, который вам нравится.
Реализация загрузочного сектора
В начале наш код работает в реальном режиме.
Real Mode-это упрощенный 16-битный режим, который присутствует на всех процессорах x86. Real Mode был первым дизайном режима X86 и использовался многими ранними операционными системами. Для целей совместимости все процессоры x86 начинают выполнять в реальном режиме.
Что плохо и хорошо в реальном режиме?
Минусы
Плюс
Из -за многих ограничений и проблем, которые есть в реальном режиме, нам нужно переключиться на защищенный режим.
Защищенный режим является основным режимом работы современных процессоров Intel с 80286. Он позволяет работать с несколькими виртуальными адресами, каждый из которых имеет максимум 4 ГБ адресной памяти.
Поскольку ЦП инициализируется BIOS в реальном режиме, переход в защищенный режим не позволяет вам использовать большую часть прерываний BIOS. Перед переходом в защищенный режим вы должны отключить прерывания, включая NMI, включить линию A20 и загрузить таблицу глобальных дескрипторов.
Алгоритм переключения в защищенный режим:
cli
lgdt [ gdt_descriptor ]
mov eax , cr0
or eax , 0x1
mov cr0 , eax
jmp CODE_SEG:init_pmРеализация для перехода на PM
Глобальная таблица дескрипторов
Но мы можем пойти дальше ...
Что такое длинный режим и зачем его настроить?
С момента введения процессоров X86-64 был также введен новый режим, который называется Long Mode. Длинный режим в основном состоит из двух подзадачных режимов, которые являются фактическим 64-битным режимом и режимом совместимости (32-битный).
Нас интересует просто 64-битный режим, так как этот режим предоставляет много новых функций, таких как:
Перед переходом в длинный режим мы должны проверить, поддерживает ли ЦП -процессор. В случае, если ЦП не поддерживает длинный режим, нам нужно запасение в защищенный режим.
Обнаружение, поддерживает ли длинный режим
Если это так, переключитесь на длинный режим
Все эти режимы великолепны, но мы не можем написать операционную систему в 512 байтах. Таким образом, наш сектор загрузки должен знать, как загрузить наше скомпилированное ядро с жесткого диска.
Когда мы находимся в реальном режиме, мы можем использовать прерывания BIOS для чтения с диска. В нашем случае, INT 13,2 - Read Disk Sectors .
Как его использовать?
;; al = number of sectors to read (1 - 128)
;; ch = track/cylinder number
;; cl = sector number
;; dh = head number
;; dl = drive number
;; bx = pointer to buffer
mov ah , 0x02
mov al , 15
mov ch , 0x00
mov cl , 0x02
mov dh , 0x00
mov dl , 0
mov bx , KERNEL_OFFSET_IN_MEMORY
int 0x13 Этот код приводит к чтению с жесткого диска в адрес адреса KERNEL_OFFSET_IN_MEMORY . Он читает 15 секторов, начиная со второго, и хранит его по адресу KERNEL_OFFSET_IN_MEMORY .
Поскольку наше скомпилированное изображение ОС является объединением загрузочного сектора и ядра, и мы знаем, что наш загрузочный сектор составляет 512 байта, мы можем быть уверены, что наше ядро начинается во втором секторе.
Когда чтение успешно завершено, мы можем позвонить по инструкции на нашем KERNEL_OFFSET_IN_MEMORY и дать исполнение ядру.
call KERNEL_OFFSET_IN_MEMORY
jmp $Реализация для чтения диска
Мы можем провести здесь линию о нашем загрузочном секторе. Поток прост:
call ;На этом этапе наш загрузочный сектор закончил свою работу и начинает работать с ядром.
Вы можете перемещаться по источникам загрузки и попытаться получить, как это работает.
Когда мы звоним по инструкции по адресу, мы можем получить несколько проблем. Мы не можем уверены, что инструкция по адресу - kernel_main() . Решение просто.
Мы можем написать подкчазу, которая прикреплена к началу кода ядра. Эта подкзадача вызов внешней функции нашего ядра - kernel_main() . Когда объектные файлы будут связаны вместе, этот вызов будет переведен в Call of нашего kernel_main() .
global _start
[bits 32]
[extern kernel_main]
_start:
call kernel_main
jmp $Реализация записи ядра
На этом этапе у нас есть точка входа в наш метод kernel_main() . И это наша точка входа для всего ядра.
Я думаю, скучно объяснить, как работает #include и что происходит в нашем kernel_main() . Вы легко можете следовать методам, которые я призываю из него.
Запись ядра в c
Это самая простая часть.
Нам нужно создать изображение boot/boot.bin в необработанном двоичном формате. Для этого мы называем nasm Assembler с специальными флагами.
nasm boot/boot.asm -f bin -o boot/boot.binЭто приводит к необработанному двоичному формату, который вы можете запустить через QEMU.
На этом этапе мы работали с компилированным загрузочным сектором.
Нам нужно рекурсивно создавать все источники из всех папок, кроме папки boot .
Все файлы C скомпилированы в файлы объектов через файлы gcc и сборки через nasm :
gcc -g -ffreestanding -Wall -Wextra -fno-exceptions -m32 -std=c11 -c < SOURCE > -o < OBJ_FILE >
nasm < SOURCE > -f elf -o < OBJ_FILE > Это приводит к всем необходимым объектным файлам для ссылки на необработанное двоичное формат. Все, что осталось сделать, это связать их вместе через ld :
ld -o kernel/kernel.bin -Ttext 0x1000 kernel/kernel_entry.o < OBJ_FILES > --oformat binary Обратите внимание, что kernel/kernel_entry.o на первом месте, так как у нас есть проблема с вызовом kernel_main() . Таким образом, мы гарантируем, что первая инструкция будет вызвана из нашей boot/kernel_entry.asm .
В конце концов, мы скомпилировали изображение ядра в необработанном двоичном формате.
Поскольку наш сектор загрузки и ядра - это необработанные бинарные форматы, мы можем просто объединить их.
cat boot/boot.bin kernel/kernel.bin > os-image.bin Теперь мы можем запустить os-image.bin через qemu-system-i386 . BIOS пытается найти загрузочный сектор, узнайте наш boot/boot.bin и видит подпись. Начинает выполнение кода нашего сборки по адресу boot/boot.bin , который загружает наше kernel/kernel.bin .
Вот как все это работает вместе. Не стесняйтесь ориентироваться в проекте, спасибо?
Лицензия MIT (MIT)
Авторские права (с) 2016 Евгений Оброзков
Настоящим дается разрешение, бесплатно, любому лицу, получающему копию этого программного обеспечения и связанные с ними файлы документации («Программное обеспечение»), чтобы иметь дело в программном обеспечении без ограничений, включая, без ограничения, права на использование, копирование, изменение, объединение, публикацию, распределение, сублиценность и/или продавать копии программного обеспечения и разрешения лиц, на которые программное обеспечение подходит для того, чтобы поступить так, чтобы поступить на следующие условия: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: к следующим условиям: на следующие условия: к следующим условиям: на следующие условия: на следующие условия.
Вышеуказанное уведомление об авторском праве и это уведомление о разрешении должно быть включено во все копии или существенные части программного обеспечения.
Программное обеспечение предоставляется «как есть», без гарантии любого рода, явного или подразумеваемого, включая, помимо прочего, гарантии товарной пригодности, пригодности для определенной цели и несоответствия. Ни в коем случае авторы или владельцы авторских прав не будут нести ответственность за любые претензии, убытки или другую ответственность, будь то в действии контракта, деликт или иным образом, возникающие из или в связи с программным обеспечением или использованием или другими сделками в программном обеспечении.