Как написать ядро операционной системы
Перейти к содержимому

Как написать ядро операционной системы

  • автор:

Собственная операционная система на Rust

Этот блог посвящен написанию маленькой операционной системы на языке программирования Rust. Каждый пост — это маленькое руководство, включающее в себя весь необходимый код, — вы сможете следовать ему, если пожелаете. Исходный код также доступен в соотвестующем репозитории на Github.

Последний пост: Async/Await

Bare Bones

Независимый бинарный файл на Rust

Первый шаг в создании собственного ядра операционной системы — это создание исполняемого файла на Rust, который не будет подключать стандартную библиотеку. Именно это дает возможность запускать Rust код на голом железе без слоя операционной системы.

Минимально возможное ядро на Rust

В этом посте мы создадим минимальное 64-битное ядро на Rust для архитектуры x86_64. Мы будем отталкиваться от независимого бинарного файла из предыдущего поста для создания загрузочного образа диска, который может что-то выводить на экран.

VGA Text Mode

The VGA text mode is a simple way to print text to the screen. In this post, we create an interface that makes its usage safe and simple by encapsulating all unsafety in a separate module. We also implement support for Rust’s formatting macros.

Testing

This post explores unit and integration testing in no_std executables. We will use Rust’s support for custom test frameworks to execute test functions inside our kernel. To report the results out of QEMU, we will use different features of QEMU and the bootimage tool.

Interrupts

CPU Exceptions

CPU exceptions occur in various erroneous situations, for example, when accessing an invalid memory address or when dividing by zero. To react to them, we have to set up an interrupt descriptor table that provides handler functions. At the end of this post, our kernel will be able to catch breakpoint exceptions and resume normal execution afterward.

Double Faults

This post explores the double fault exception in detail, which occurs when the CPU fails to invoke an exception handler. By handling this exception, we avoid fatal triple faults that cause a system reset. To prevent triple faults in all cases, we also set up an Interrupt Stack Table to catch double faults on a separate kernel stack.

Hardware Interrupts

In this post, we set up the programmable interrupt controller to correctly forward hardware interrupts to the CPU. To handle these interrupts, we add new entries to our interrupt descriptor table, just like we did for our exception handlers. We will learn how to get periodic timer interrupts and how to get input from the keyboard.

Memory Management

Introduction to Paging

This post introduces paging, a very common memory management scheme that we will also use for our operating system. It explains why memory isolation is needed, how segmentation works, what virtual memory is, and how paging solves memory fragmentation issues. It also explores the layout of multilevel page tables on the x86_64 architecture.

Paging Implementation

This post shows how to implement paging support in our kernel. It first explores different techniques to make the physical page table frames accessible to the kernel and discusses their respective advantages and drawbacks. It then implements an address translation function and a function to create a new mapping.

Heap Allocation

This post adds support for heap allocation to our kernel. First, it gives an introduction to dynamic memory and shows how the borrow checker prevents common allocation errors. It then implements the basic allocation interface of Rust, creates a heap memory region, and sets up an allocator crate. At the end of this post, all the allocation and collection types of the built-in alloc crate will be available to our kernel.

Allocator Designs

This post explains how to implement heap allocators from scratch. It presents and discusses different allocator designs, including bump allocation, linked list allocation, and fixed-size block allocation. For each of the three designs, we will create a basic implementation that can be used for our kernel.

Multitasking

Async/Await

In this post, we explore cooperative multitasking and the async/await feature of Rust. We take a detailed look at how async/await works in Rust, including the design of the Future trait, the state machine transformation, and pinning. We then add basic support for async/await to our kernel by creating an asynchronous keyboard task and a basic executor.

Subscribe

Receive notifications about new posts and other major changes! You can either:

  • Subscribe to our RSS/Atom Feed,
  • Subscribe to this GitHub issue, or
  • Subscribe to our email newsletter.

Status Updates

These posts give a regular overview of the most important changes to the blog and the tools and libraries behind the scenes.

  • This Month in Rust OSDev (March 2024)
  • This Month in Rust OSDev (February 2024)
  • This Month in Rust OSDev (January 2024)
  • This Month in Rust OSDev (December 2023)
  • This Month in Rust OSDev (November 2023)
  • view all »

First Edition

You are currently viewing the second edition of “Writing an OS in Rust”. The first edition is very different in many aspects, for example it builds upon the GRUB bootloader instead of using the `bootloader` crate. In case you’re interested in it, it is still available. Note that the first edition is no longer updated and might contain outdated information. read the first edition »

Support Me

Creating and maintaining this blog and the associated libraries is a lot of work, but I really enjoy doing it. By supporting me, you allow me to invest more time in new content, new features, and continuous maintenance. The best way to support me is to sponsor me on GitHub. Thank you!

Написание собственной работоспособной ОС за полгода

image

Здравствуйте! Всех категорически приветствую, сегодня хотел бы рассказать Вам о своём опыте написание работоспособной ОС под архитектуру x86.

Как-то весенней ночью у меня родилась гениальная идея — попробовать себя в написании собственной ОС, которая может позволить запускать программы, работать с устройствами, да и в общем выжимать всю мощь из Intel’овской архитектуры в своих нуждах: к примеру, для своей фабрики или чего-либо иного. Моей целью было и есть написание такой ОС, которая могла бы позволить максимальную производительность для каких-то конкретных задач, не тратя процессорное время на всяческие излишества. В основном я преследую лишь спортивный интерес, получение опыта для себя в системном программировании и написания драйверов для устройств, которые используются повсеместно. Что из этого вышло — решать вам, сразу говорю, что не надо писать комментарии про создание собственного дистрибутива линукса, и преследовал интерес написать всё «From scratch» — с нуля, дабы хорошо погрузиться в тему ОСдева. Сразу хочу выразить огромную благодарность Бенджамину Лунту и форуму OSDev, так же как их Вики. Бен помог мне разобраться с EHCI, что несомненно внесло огромный вклад в мою ОС — USB устройства, они везде! Так же передо мной стояла задача создать собственную архитектуру, удобную мне, не исключая использование стандартов ELF-файлов. Что же, давайте перейдем к сути.
UPD: всю инфу можно найти в группе тык, так же там есть пост с доками и образом(старым, сейчас дописываю доки для stable-версии)

Что сделано?

Сейчас моя ОС умеет работать с USB-флешками, мышками, клавиатурами, AHCI дисками, PCI IDE контроллером, APIC’ом и ACPI, реализована вытесняющая многозадачность, реализован запуск программ, поточная работа с файлами, SVGA драйвер, который работает на 0x118 режиме VBE, работают DNS, DHCP, TCP, UPD, IPv4, HTTP, полный драйвер FAT32, драйвер RTL8139(69) и Intel Gigabit Ethernet.

Оконная система вместе с моей реализацией SVGA позволяет выдать аж 120 FPS при полной перерисовке экрана. Давайте перейдем к тому, как это всё реализовано и вообще может работать.

Как это работает?

Для начала, я написал бутлоадер, который с FAT32 читает вторичный загрузчик с ядром. Второй загрузчик переходит в защищенный режим и прыгает в ядро, там я загружаю и настраиваю IDT, после чего инициализирую PCI-устройства, запускаю ядра и запускаю CMD.

Кто-то спросит, как же ты добился такого перфоманса с SVGA? Ответ прост: ассемблер, ассемблер и еще раз ассемблер. Не обошлось без SSE инструкций, которые очень ускоряют копирование памяти. К примеру, вот код для копирования видеобуфера в LFB(Linear Frame Buffer):

.byte 0x60#Save registers in stack mov %2,%%ecx #Repeat count to ecx mov %0,%%edi #Video memory start to edi mov %1,%%esi #Video buffer start to esi ww1sse2: movaps (%%esi),%%xmm0 #Copy 16 bytes to xmm0 from buffer movaps %%xmm0,(%%edi) #Copy from xmm0 to video memory movaps 16(%%esi),%%xmm0 #16 again, but + 16 from current movaps %%xmm0,16(%%edi) #16 again, but + 16 from current movaps 32(%%esi),%%xmm0 #16 again, but + 32 from current movaps %%xmm0,32(%%edi) #16 again, but + 32 from current movaps 48(%%esi),%%xmm0 #16 again, but + 48 from current movaps %%xmm0,48(%%edi) #16 again, but + 48 from current add $64,%%edi #Add 64 bytes to edi add $64,%%esi #Add 64 bytes to esi dec%%ecx#Decrement count #test %%ecx,%%ecx #Compare ecx with zero jnz ww1sse2 #If not zero, repeat again .byte 0x61 #Restore registers from stack 

Оконная система построена на ООП и, думаю, не нуждается в комментариях, всё почти как в шинде.

Менеджер памяти простейший — «Watermark Allocator». Распределение ресурсов осуществляется за счет того, что в ядре нет функций, которые могли бы нагадить друг другу, всё запросы сделаны через очереди и т.п.

Пока что нет никаких потоков ввода-вывода, но в ближайшее время они будут реализованы.
Система логических дисков аналогична MS-DOSу: одна буква — один диск. Поддерживаются как MBR разделы, так и GPT разделы.

Разработка драйверов устройств

Не обошлось тут и без этого — брать чужой код как-то некультурно и не практично, если раскапывать репозитории линукса — так можно и заблудиться.

Если честно, то я сторонник того, что главное в программе — функционал, но в тоже время считаю, что за графику можно и функционалом немного пожертвовать: к примеру, VIM.

Конечно, это стало самой интересной стадией разработки: прочитать тонны документации, а потом понять, что ты поставил return для отладки, из-за чего часть структуры не заполнялась вовсе. Думаю, что это достаточно хорошо заставляет тебя напрячь мозг, ибо даже после десятого прочтения доков какие-то аспекты всё равно остаются тебе непонятны, и приходиться заниматься долгой отладкой на реальном железе.

Отладка

Отладка ОС занимает огромную кучу времени: скомпилить сорсы, скопировать на виртуальный диск, сохранить образ диска, проверить на эмуляторе, после чего скопировать файлы на флешку и проверить уже на реальном железе. А главное — никаких отладчиков, выводи в текстовый режим, либо в окошечки — повисло — ставь return’ы.

Итоги

Да, ОС всё еще в стадии разработки, хочется под новый год получить стабильную бета-версию, а может в будущем даже продолжить разработку на более серьезном уровне.

Что хочу сказать напоследок — если тема разработки ОС кого-то заинтересовала, или кто-то хочет узнать аспекты работы с какими-либо устройствами и интерфейсами, хитрости реализации, или просто помочь проекту(не деньгами, а кодом) — можете отписать об этом в комментариях, все пожелания будут учтены, и в деталях и красках будут описаны интересующее вас аспекты.

Пишем макет 16-битного ядра на C/C++

В первой и второй статьях я лишь коротко представил процесс написания загрузчика на ассемблере и C. Для меня это было хоть и непросто, но в то же время интересно, так что я остался доволен. Однако создания загрузчика мне показалось мало, и я увлекся идеей его расширения дополнительной функциональностью. Но так как в итоге размер готовой программы превысил 512 байт, то при попытке запуска системы с несущего ее загрузочного диска я столкнулся с проблемой “This is not a bootable disk”.

О чем эта статья?

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

Нужен ли для этого опыт?

Статья окажется намного полезнее тем, у кого уже есть опыт программирования. Несмотря на ее чисто ознакомительный, казалось бы, характер, написание программ на ассемблере и C может оказаться достаточно сложным. Поэтому новичкам я рекомендую для начала ознакомиться с вводными принципами программирования и уже потом возвращаться к этому материалу.

Как и ранее, процесс изложения построен по принципу вопрос-ответ, что должно упростить восприятие информации.

План статьи

  • Ограничения загрузчика
  • Вызов из загрузчика других файлов диска
  • Файловая система FAT
  • Принцип работы FAT
  • Среда разработки
  • Написание загрузчика для FAT
  • Мини-проект: написание 16-битного ядра
  • Тестирование ядра

Ограничения загрузчика

В предыдущих статьях я написал загрузчик и, реализовав с его помощью вывод на экран цветных прямоугольников, захотел добавить дополнительную функциональность. Однако при этом я столкнулся с ограничением размера сектора в 512 байт, что не позволило добиться желаемого стандартным путем.

В итоге передо мной стоит две задачи:

  • Расширить загрузчик кодом, реализующим дополнительную функциональность.
  • Сохранить при этом размер загрузчика в 512 байт.

Как я буду это делать?

  • Напишу программу kernel.c на C, внедрив в нее всю необходимую функциональность.
  • Скомпилирую и сохраню исполняемый файл как kernel.bin.
  • Скопирую этот файл во второй сектор загрузочного диска.

В загрузчике мы можем просто загрузить второй сектор, содержащий kernel.bin, в RAM по адресу, к примеру, 0x1000 , а затем перейти к этому адресу из 0x7с00 и запустить kernel.bin.

Вот схема для лучшего понимания идеи:

Запуск из загрузчика других файлов диска

Как мы теперь знаем, у нас есть возможность передачи управления от загрузчика ( 0x7c00 ) в другую область памяти, где размещается, например, наш kernel.bin, после чего продолжить выполнение. Но здесь у меня я хочу кое-что уточнить.

Как узнать сколько секторов kernel.bin займет на диске?

Ну это простой вопрос. Для ответа на него нам достаточно выполнить несложную арифметику, а именно разделить размер kernel.bin на размер сектора, который составляет 512 байт. Например, если kernel.bin будет равен 1024 байта, то и займет он 2 сектора.

В связи с этим вам потребуется захардкодить в загрузчике необходимое для ядра количество секторов, и при возможном изменении его размера в дальнейшем не забывать о внесении соответствующих правок. В противном случае при несоответствии прописанного и фактического размеров загрузка ядра будет проваливаться.

Можно ли добавить помимо kernel.bin другие файлы, например office.bin, entertainment.bin, drivers.bin?

В принципе да. Для этого нужно будет поочередно добавить требуемые файлы на дискету, не забывая при этом обновлять загрузчик необходимой информацией об их расположении, занимаемом количестве секторов и прочем. Но тут я отдельно скажу, что в итоге сложность системы сильно увеличивается, и в конечном счете мне такой вариант не очень понравился.

Откуда мы знаем, что после загрузочного сектора выполняются именно желаемые файлы?

Хороший вопрос, так как мы, действительно, только загружаем соответствующий сектор в память и начинаем его выполнение. Это не идеальный вариант, и в нем кое-чего не хватает.

Чего не хватает?

Загрузчик вслепую загружает сектора каждого файла один за другим и затем начинает выполнение этих файлов. Но даже до того, как он попробует загрузить файлы в память, должен присутствовать способ проверить, существуют ли они вообще на диске.

Что произойдет, если по ошибке загрузить во второй сектор не тот файл, обновить загрузчик и начать выполнение?

В этом случае система просто дает сбой. Поэтому на диске должна присутствовать фиксированная область, где подобно индексу в книге будут прописаны все имена файлов.
Тогда загрузчик будет запрашивать индекс этого файла на диске, и в случае обнаружения таковой будет обнаружен в списке – продолжать загрузку этого файла в память.

Мне такой вариант очень нравится, так как он избавляет от лишних действий.

Здесь мы избегаем нескольких проблем. До этого загрузчик вслепую загружал жестко закодированные сектора. Но зачем загружать файл, не будучи уверенным в том, что это именно нужный файл, и что он вообще существует?

Как это решается?

Для этого нужно просто организовать информацию на диске, после чего перепрограммировать загрузчик, повысив тем самым его эффективность при загрузке файлов.

Такой вид организации информации называется файловой системой. Существует много типов файловых систем, среди которых есть как коммерческие, так и бесплатные. Вот некоторые из них:

FAT

Для лучшего понимания этой файловой системы вам потребуется знать некоторые технические нюансы.

Минимальной единицей измерения пространства FAT является кластер, который занимает 1 сектор накопителя. Иначе говоря, на дискете, отформатированной в FAT, 1 кластер эквивалентен 1 сектору и, соответственно, равен 512 байт.

Для удобства использования файловая система дополнительно разделяется на четыре основные области:

  • загрузочный сектор (boot sector);
  • таблицу размещения файлов (file allocation table);
  • корневой каталог (root directory);
  • область данных (data area).

Рассмотрим каждую часть подробнее.

Загрузочный сектор

Загрузочный сектор содержит служебную информацию, на основе которой ОС распознает тип файловой системы диска, после чего уже переходит к чтению его содержимого.

Информация о файловой системе FAT, содержащаяся в загрузочном секторе, называется блоком параметров BIOS.

Блок параметров BIOS

Ниже я привел пример значений из этого блока:

Таблица размещения файлов

Эта таблица представляет своего рода связанный список, в котором каждая ячейка, соответствующая кластеру файла, содержит значение следующего кластера, формируя таким образом связанную цепочку всех кластеров этого файла.

Это значение кластера служит для:

  • определения окончания файла. Если оно находится между 0x0ff8 и 0x0fff , значит файл не содержит данных в других секторах, т.е. достигнут его конец.
  • определения следующего кластера с данными этого файла.

Корневой каталог

Корневой каталог выступает в роли индекса всех находящихся на диске файлов. Именно здесь загрузчик ищет имя нужного файла и в случае обнаружения обращается к его первому кластеру для начала загрузки.

После обнаружения и начала считывания первого кластера загрузчик с помощью FAT находит все последующие занимаемые файлом кластеры.

Область данных

Здесь содержаться фактические данные файлов.
Как только программа обнаруживает искомый сектор файла, именно из этой области извлекаются соответствующие данные.

Принцип работы FAT

Продолжим наш пример с kernel.bin, который загрузчик помещает в память для выполнения. Теперь в этом сценарии нужно прописать для загрузчика следующую функциональность:

  • Сравнить первые 11 байт данных с kernel.bin, начиная со смещения 0 в таблице корневого каталога.
  • В случае совпадения этой строки – извлечь первый кластер kernel.bin из смещения 26 корневого каталога.
  • Далее преобразовать этот кластер в соответствующий сектор и загрузить его данные в память.
  • После загрузки первого сектора в память перейти к поиску в FAT следующего кластера файла и определить, является он последним, или есть еще данные в других кластерах.

Среда разработки

Для успешной реализации этой задачи нам нужно иметь представление о следующем. Более подробную информацию по этим пунктам можете найти в двух предыдущих статьях.

  • Операционная система (GNU Linux).
  • Ассемблер (GNU Assembler).
  • Набор инструкций (x86).
  • Написание инструкций для микропроцессора x86 на GNU Assembler.
  • Компилятор (GNU C компилятор GCC).
  • Компоновщик (GNU linker ld)
  • Эмулятор, например bochs, используемый для тестирования.

Написание загрузчика FAT

Ниже я привожу фрагмент кода для выполнения файла kernel.bin на FAT-диске.

Файл: stage0.S

/********************************************************************************* * * * * * Name : stage0.S * * Date : 23-Feb-2014 * * Version : 0.0.1 * * Source : assembly language * * Author : Ashakiran Bhatter * * * * Описание: основная логика подразумевает сканирование файла kernel.bin * * на дискете fat12 и передачу этому файлу права * * выполнения. * * Использование: подробности в файле readme.txt * * * * * *********************************************************************************/ .code16 .text .globl _start; _start: jmp _boot nop /*блок параметров BIOS описание каждой сущности */ /*-------------------- -------------------------- */ .byte 0x6b,0x69,0x72,0x55,0x58,0x30,0x2e,0x31 /* метка OEM */ .byte 0x00,0x02 /* байтов в секторе */ .byte 0x01 /* секторов в кластере */ .byte 0x01,0x00 /* зарезервированных секторов */ .byte 0x02 /* таблиц fat */ .byte 0xe0,0x00 /* записей в каталоге */ .byte 0x40,0x0b /* всего секторов */ .byte 0xf0 /* описание среды передачи */ .byte 0x09,0x00 /* размер в каждой таблице fat */ .byte 0x02,0x01 /* секторов в дорожке */ .byte 0x02,0x00 /* головок на цилиндр */ .byte 0x00,0x00, 0x00, 0x00 /* скрытых секторов */ .byte 0x00,0x00, 0x00, 0x00 /* больших секторов */ .byte 0x00 /* идентификатор загрузочного диска*/ .byte 0x00 /* неиспользуемых секторов */ .byte 0x29 /* внешняя сигнатура загрузки */ .byte 0x22,0x62,0x79,0x20 /* серийный номер */ .byte 0x41,0x53,0x48,0x41,0x4b,0x49 /* метка тома 6 байт из 11 */ .byte 0x52,0x41,0x4e,0x20,0x42 /* метка тома 5 байт из 11 */ .byte 0x48,0x41,0x54,0x54,0x45,0x52,0x22 /* тип файловой системы */ /* включение макросов */ #include "macros.S" /* начало основного кода */ _boot: /* инициализация среды */ initEnvironment /* загрузка stage2 */ loadFile $fileStage2 /* бесконечный цикл */ _freeze: jmp _freeze /* непредвиденное завершение программы */ _abort: writeString $msgAbort jmp _freeze /* включение функций */ #include "routines.S" /* пользовательские переменные */ bootDrive : .byte 0x0000 msgAbort : .asciz "* * * F A T A L E R R O R * * *" #fileStage2: .ascii "STAGE2 BIN" fileStage2: .ascii "KERNEL BIN" clusterID : .word 0x0000 /* перемещение от начала к 510-му байту */ . = _start + 0x01fe /* добавление сигнатуры загрузки */ .word BOOT_SIGNATURE

В этом основном файле загрузки происходит:

  • Инициализация всех регистров и настройка стека вызовом макроса initEnvironment .
  • Вызов макроса loadFile для загрузки kernel.bin в память по адресу 0x1000:0000 и последующей передачи ему права выполнения.

Файл: macros.S

Этот файл содержит все предопределенные макросы и функции.

/********************************************************************************* * * * * * Name : macros.S * * Date : 23-Feb-2014 * * Version : 0.0.1 * * Source : assembly language * * Author : Ashakiran Bhatter * * * * * *********************************************************************************/ /* предопределенный макрос: загрузчик */ #define BOOT_LOADER_CODE_AREA_ADDRESS 0x7c00 #define BOOT_LOADER_CODE_AREA_ADDRESS_OFFSET 0x0000 /* предопределенный макрос: сегмент стека */ #define BOOT_LOADER_STACK_SEGMENT 0x7c00 #define BOOT_LOADER_ROOT_OFFSET 0x0200 #define BOOT_LOADER_FAT_OFFSET 0x0200 #define BOOT_LOADER_STAGE2_ADDRESS 0x1000 #define BOOT_LOADER_STAGE2_OFFSET 0x0000 /* предопределенный макрос: разметка дискеты */ #define BOOT_DISK_SECTORS_PER_TRACK 0x0012 #define BOOT_DISK_HEADS_PER_CYLINDER 0x0002 #define BOOT_DISK_BYTES_PER_SECTOR 0x0200 #define BOOT_DISK_SECTORS_PER_CLUSTER 0x0001 /* предопределенный макрос: разметка файловой системы */ #define FAT12_FAT_POSITION 0x0001 #define FAT12_FAT_SIZE 0x0009 #define FAT12_ROOT_POSITION 0x0013 #define FAT12_ROOT_SIZE 0x000e #define FAT12_ROOT_ENTRIES 0x00e0 #define FAT12_END_OF_FILE 0x0ff8 /* предопределенный макрос: загрузчик */ #define BOOT_SIGNATURE 0xaa55 /* пользовательские макросы */ /* макрос для установки среды */ .macro initEnvironment call _initEnvironment .endm /* макрос для отображения строки на экране. */ /* Для выполнения этой операции он вызывает функцию _writeString */ /* параметр: вводная строка */ .macro writeString message pushw \message call _writeString .endm /* макрос для считывания сектора в памяти */ /* Вызывает функцию _readSector со следующими параметрами */ /* параметры: номер сектора */ /* адрес загрузки */ /* смещение адреса */ /* количество считываемых секторов */ .macro readSector sectorno, address, offset, totalsectors pushw \sectorno pushw \address pushw \offset pushw \totalsectors call _readSector addw $0x0008, %sp .endm /* макрос для поиска файла на FAT-диске. */ /* Для этого он вызывает макрос readSector */ /* параметры: адрес корневого каталога */ /* целевой адрес */ /* целевое смещение */ /* размер корневого каталога */ .macro findFile file /* считывание таблицы FAT в память */ readSector $FAT12_ROOT_POSITION, $BOOT_LOADER_CODE_AREA_ADDRESS, $BOOT_LOADER_ROOT_OFFSET, $FAT12_ROOT_SIZE pushw \file call _findFile addw $0x0002, %sp .endm /* макрос для преобразования заданного кластера в номер сектора */ /* Для этого он вызывает _clusterToLinearBlockAddress */ /* параметр: номер кластера */ .macro clusterToLinearBlockAddress cluster pushw \cluster call _clusterToLinearBlockAddress addw $0x0002, %sp .endm /* макрос для загрузки целевого файла в память. */ /* Он вызывает findFile и загружает данные соответствующего файла в память */ /* по адресу 0x1000:0x0000 */ /* параметр: имя целевого файла */ .macro loadFile file /* проверка наличия файла */ findFile \file pushw %ax /* считывание таблицы FAT в память */ readSector $FAT12_FAT_POSITION, $BOOT_LOADER_CODE_AREA_ADDRESS, $BOOT_LOADER_FAT_OFFSET, $FAT12_FAT_SIZE popw %ax movw $BOOT_LOADER_STAGE2_OFFSET, %bx _loadCluster: pushw %bx pushw %ax clusterToLinearBlockAddress %ax readSector %ax, $BOOT_LOADER_STAGE2_ADDRESS, %bx, $BOOT_DISK_SECTORS_PER_CLUSTER popw %ax xorw %dx, %dx movw $0x0003, %bx mulw %bx movw $0x0002, %bx divw %bx movw $BOOT_LOADER_FAT_OFFSET, %bx addw %ax, %bx movw $BOOT_LOADER_CODE_AREA_ADDRESS, %ax movw %ax, %es movw %es:(%bx), %ax orw %dx, %dx jz _even_cluster _odd_cluster: shrw $0x0004, %ax jmp _done _even_cluster: and $0x0fff, %ax _done: popw %bx addw $BOOT_DISK_BYTES_PER_SECTOR, %bx cmpw $FAT12_END_OF_FILE, %ax jl _loadCluster /* выполнение ядра */ initKernel .endm /* параметры: имя целевого файла */ /* макрос для передачи права выполнения файлу, загруженному */ /* в память по адресу 0x1000:0x0000 */ /* параметры: none */ .macro initKernel /* инициализация ядра */ movw $(BOOT_LOADER_STAGE2_ADDRESS), %ax movw $(BOOT_LOADER_STAGE2_OFFSET) , %bx movw %ax, %es movw %ax, %ds jmp $(BOOT_LOADER_STAGE2_ADDRESS), $(BOOT_LOADER_STAGE2_OFFSET) .endm 

Общая сводка

initEnvironment:

  • Макрос для установки сегментных регистров.
  • Аргументов не требует.

writeString:

  • Макрос для отображения на экране строки с завершающим нулем.
  • В качестве аргумента передается строковая переменная с завершающим нулем.
  • Макрос для чтения с диска заданного сектора и его загрузки в целевой адрес памяти.
  • Количество аргументов: 4.
  • Макрос для проверки наличия файла.
  • Количество аргументов: 1.

clusterToLinearBlockAddress:

  • Макрос для преобразования заданного кластера в номер сектора.
  • Количество аргументов: 1.

Как написать ядро операционной системы

МЕРОПРИЯТИЯ

Usetech Mobile MeetUp (UMM) #1

МТС True Tech Day

Комментарии

Популярные По порядку
Не удалось загрузить комментарии.

ВАКАНСИИ

Продуктовый аналитик
Екатеринбург, по итогам собеседования

PHP Developer
от 200000 RUB до 270000 RUB

Senior Frontend разработчик
от 4000 USD до 8000 USD

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ

Лучший хакерский курс с Kali Linux на русском языке

Если нужно получить доступ к серверу, поправить работоспособность чужой сети или побаловаться с чьим-то сайтом – Kali Linux вам в помощь.

51 задание для оттачивания навыков системного администрирования

Разбираемся с тем, как стать системным администратором, и выполняя реальные задачи, получаем основные знания для работы и развития.

10 лучших ресурсов для изучения хакинга с помощью Kali Linux

Подборка 10 отличных ресурсов для изучения хакинга с помощью Kali Linux. Изучение данных материалов поможет вам раскрыть свои хакерские способности.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *