You are on page 1of 14

Розробка завантажувача операційної системи

Метою даної роботи є реалізація простого завантажувача операційної


системи.
Реальний режим роботи процесору.
Реальний режим роботи процесору (англ. real mode) – це однозадачний
режим у якому кожна програма має доступ до всієї оперативної пам'яті.
Особливостями архітектури реального режиму є: 16 розрядні регістри, 16-
розрядна шина даних та 20-розрядна адресна шина. Тобто в даному режимі є
можливість звернення до пам'яті тільки з адресним простором до 1 МБ (20-
розрядна адресна шина ). Завдання 20-розрядних адресів здійснюється за
допомогою сегментних регістрів. Пам'ять розподілена на сегменти по 64 КБ.
В сегментному регістрі вказується базова адреса сегменту. Сегменти можуть
бути трьох типів: коду, даних та стеку. З ними пов’язані відповідно регістри:
CS – сегмент коду, DS – сегмент даних, SS – сегмент стеку. Сегментний регістр
- це також 16 розрядний регістр, тому для формування 20 розрядної базової
адреси його значення зсувається вліво на 4 розряди. Таким чином адреси, що
використовуються для посилання на змінні або код завжди формуються
відносно значення, що задане в сегментних регістрах. Схема адресації в
реальному режимі представлена на рисунку 1.

Рис. 1. Схема адресації в реальному режимі процесора


BIOS (Basic input output system).
Після включення комп’ютера (натисканням кнопки Power), здійснюється
перевірка наявності електричного живлення і у позитивному випадку на
материнську плату видається сигнал Power Good.
Після цього починається робота процедури POST (Power On Self Test)
BIOS, що здійснює перевірку системи на наявність та працездатність
необхідних пристроїв (пам’ять, клавіатура та інш.). Ця процедура знаходиться
в пам’яті ROM BIOS, що розташована на материнській платі. В пам’яті ROM
BIOS розробниками материнській платі записуються процедури, які
використовуються для взаємодії з периферійними пристроями. Це значно
спрощує взаємодію с периферійними пристроями з боку операційної системи:
замість використання механізму портів (команди OUT, IN), що передбачає
знання апаратних особливостей пристроїв з якими здійснюється взаємодія,
використовуються переривання BIOS (команда INT, наприклад функції
переривання int 10h призначені для роботи з виводом інформації на дисплей).
Але перед тим як розбиратися з процедурою POST, необхідно розуміти як
стартує ця процедура.
По перше, процесор при старті завжди знаходиться в реальному режимі
роботи. Далі, після підтвердження Power Good у регістр CS завантажується
число 0xF000, а у регістр IP – 0xFFF0. Таким чином формується 20 розрядна
адреса 0xFFFF0 першої команди до виконання процесором. Команда, що
розташована за даною адресою називається Reset vector. Ця команда, як ми
бачимо, займає останні 15 байтів пам’яті в 1 МБ. Важливо відзначити, що ROM
BIOS та оперативна пам'ять RAM находяться в одному адресному просторі,
при цьому процедури BIOS завжди розташовані в кінці пам’яті (0xF0000 -
0xFFFFF). До речі кажучи, пам'ять дисплею також знаходиться в одному
адресному просторі з RAM (0xA0000 – 0xBFFFF), Video BIOS (0xC0000 –
0xC7FFF), BIOS Expansions – 0xC8000 – 0xEFFFF. Команда Reset vector являє
собою звичайну команду безумовного переходу (JMP <address>), яка ініціює
перехід до першої команди процедури POST, що може бути розташована за
будь-якою адресою. Приклад завдання вектору показаний в лістингу 1
Лістинг 1
db 0fff0h-$ dup 0ffh ;at F000:FFF0
jmp far 0f000h:reset

Такий гнучкий підхід (введення до системи пам’яті BIOS та ініціювання


запуску системи за допомогою Reset vector) був запропонований Gary Arlen
Kildall (1942-1994), який заснував компанію Digital Research. Але команда JMP
– трьох байтна, чому використовується адреса 0xFFFF0. Справа в тому, що
процесор в фазі вибірки команди із пам’яті за адресою вибирає 16 байтів, тому
що 16 байтів – максимальний розмір команди архітектури X86.
Одним з кроків процедури POST є ініціалізація механізму переривань.
Дана ініціалізація включає дві фази: програмування контролера переривань
(programmable interrupt controller - 8259); копіювання векторів переривань в
оперативну пам'ять починаючи з адреси 0x0. В даному контексті вектор
означає 2-байтний адресу обробника переривань.
Приклад роботи з контролером переривань показаний у лістингу 2, а код
копіювання векторів у лістингу 3
Лістинг 2
in al,pic0+1 ;enable timer, keyboard interrupt
and al,11111100xb
out iowait,al
out pic0+1,al
out iowait,al
mov al,eoi
out pic0,al
ret

;
; System board I/O ports
;
dma0 equ 00h ;Primary 8237 DMA controller
pic0 equ 20h ;primary 8259 interrupt controller
timer equ 40h ;8254 timer
eoi equ 20h ;PIC: End of Interrupt
iowait equ 0ebh ;I/O wait register

Лістинг 3
;
; init interrupt vectors
;
post_vec: mov si,offset inttab ;init interrupt vectors
xor di,di ;vec00
mov ax,cs ;BIOS segment -> eax bit 31..16
shl eax,16

mov cx,1fh
post_vec1: cs:
lodsw ;copy vectors 00.1e
;load word at address DS:(E)SI into AX.
stosd ;vector + segment
;store EAX at address ES:(E)DI

loop post_vec1

add di,4 ;skip vector 1F

mov cl,60h-20h
mov ax,offset intdummy ;dummy vectors 20..5F
rep stosd

add di,8*4 ;skip 60..67


mov cl,8
rep stosd ;fill 68..6F

mov cl,8 ;copy 8 more vectors


post_vec2: cs: lodsw ;copy vectors 70..77
stosd
loop post_vec2
ret

Приклад таблиці переривань (interrupt vector table) показаний в лістингу 4


Лістинг 4
inttab: dw int00 ;divide by zero
dw int01 ;single step
dw nmi ;NMI
dw int03 ;breakpoint
dw int04 ;overflow
dw int05 ;print screen
dw int06 ;invalid opcode
dw int07 ;coprocessor not available
dw irq0 ;IRQ0 system timer
dw irq1 ;IRQ1 keyboard
dw inteoi ;reserved - cascade
dw inteoi ;IRQ3 reserved
dw inteoi ;IRQ4 reserved
dw inteoi ;IRQ5 reserved
dw irq6 ;IRQ6 floppy
dw inteoi ;IRQ7 reserved
dw int10 ;video
dw int11 ;equipment determination
dw int12 ;memory size
dw int13 ;disk
dw int14 ;serial
dw int15 ;system services
dw int16 ;keyboard
dw int17 ;printer
dw int18 ;expansion ROM
dw int19 ;bootstrap
dw int1a ;timer / RTC
dw int1b ;keyboard break
dw int1c ;user timer tick
dw v_parm ;video parameters
dw fd_ptab ;diskette parameters

dw irq8 ;IRQ8: RTC


dw inteoi2 ;IRQ9: cascade
dw inteoi2 ;IRQ10: spare
dw inteoi2 ;IRQ11: spare

Слід відзначити, що для ряду переривань в ролі оброблювачів можуть бути


встановлені заглушки. Такі заглушки називаються Dummy interrupts. Приклад
наведений у лістингу 5
Лістинг 5
;
; Dummy interrupts -> IRET
;
intdummy:
int00: ;divide by zero
int01: ;single step
int03: ;breakpoint
int04: ;overflow
int06: ;invalid opcode
int07: ;coprocessor not available
int1b: ;keyboard break
int1c: ;user timer tick
iret

Після здійснення всіх перевірок процедура BIOS передає управління


завантажувачу операційної системи.
За код завантаження відповідає переривання 19h. У спрощеному варіанті
(розглядаємо завантаження тільки з диска C :) дане переривання має вигляд
наведений у лістингу 6.
Лістинг 6
;
; INT19 entry: boot operating system
;
int19:mov dl,80h ;drive C:
mov cx,3 ;retry count
call bootdrv ;try to boot
;
; Try to boot operating system from drive DL, CX retries
;
bootdrv: push cx
xor ax,ax ;$0000:$7c00 = destination address
mov es,ax
mov bx,7c00h
mov ax,0201h ;read, 1 sector
mov cx,0001 ;cylinder 0, sector 1
mov dh,0 ;head 0
int 13h ;try to read boot sector
pop cx
jnb bootdrv2 ;:ok
push ax
mov ah,0 ;reset disk system
int 13h
pop ax ;restore status
loop bootdrv ;try 3 times
bootdrv9: ret ;return, didn't work

; check boot sector signature


bootdrv2: cmp word [es:7dfeh],0aa55h
jnz bootdrv9 ;:no
jmp far 0:7c00h ;jump to boot sector
Як ми бачимо, використовуючи переривання 13h з диска зчитується
перший сектор в пам'ять за адресою 7C00h.
Після цього командою cmp word [es:7dfeh],0aa55h здійснюється
перевірка маркеру, тобто чи є завантажений код дійсно завантажувачем.
Таким маркером являються два останніх байти, які повинні бути встановлені
в 0xAA55.

Завантажувач.
Завантажувач представляє собою невелику програму, що розміщена в
стартовому 0-му секторі диска. Інформація на диску фізично зберігається у
секторах по 512 байт, тобто при звертанні до диску зчитується сектор. Розмір
коду завантажувача має бути не більш ніж 510 байт. Для роботи з диском та
іншими периферійними пристроями використовується система переривань
(програм, що зберігаються в ROM-BIOS).
В лістингу 7 представлений код простого завантажувача операційної
системи.
Лістинг 7
bits 16 ; 16-ти бітнИЙ режим Real Mode
org 0x7c00 ; BIOS завантажується за адресою 0x7C00
start: jmp loader ; перехід до погрузчика
msg db "Welcome to My Operating System!", 0; виводиться
строка
Print:
lodsb ; завантажити наступний байт до AL
or al, al ; нульовий символ означає кінець строки
jz PrintDone
mov ah, 0eh ; функція запису символу
int 10h ; перевірка місцезнаходження
курсору
jmp Print ; нескінченний цикл
PrintDone:
ret
loader:
xor ax, ax ; обнуління ax
mov ds, ax ; встановлення сегменту даних
mov es, ax ; встановлення es=ds, адреса від
0x7c00:0
mov si, msg ; повідомлення для печаті
call Print ; виклик функції печаті
cli ; очистити всі переривання
hlt ; зупинити систему
times 510 - ($-$$) db 0 ; доповнення бінарного коду нулями до
510 байт
dw 0xAA55 ; закінчення бінарного коду системного
завантажувача

Після компіляції отримаємо наступний код (рис.2).

Рис.2. Коди операцій

Розглянемо отриманий бінарний код. По перше, код заданий у явному вигляді


без заголовків тощо. Розмір отриманого коду дорівнює 512 байтам. Коди
операцій надані в [http://ref.x86asm.net/coder32.html#x88].

Покажемо зв'язок кодів операцій з командами.


EB 2C – JMP rel8 – перейти на адресу відносно даної команди, тобто адреса
останнього байту даної команди плюс 2C. Виходить адреса 0+2C + 2 = 2E, за
якою розташована команда xor ax, ax (31 С0).
57…00 - стрічка символів “Welcome to My Operating System!”, що
закінчуються байтом 0x0.

AC – код команди lodsb


08 C0 – or al,al
74 06 – JZ r8 – команда умовного переходу на відносну адресу 0x25+6+2=0x2D,
де 0x25 – адреса команди JZ, 6 – зміщення, 2 – довжина команди JZ. За адресою
0x2D ми маємо команду ret (код C3).
B4 0E – mov ah, 0Eh
CD 10 – int 10h
EB F5 – безумовний перехід на 10 байтів назад (число у додатковому коді F5
дорівнює -10).
С3 – команда ret.

31 С0 – xor ax, ax
8E D8 8E C0 – занесення до сегментних регістрів DS та ES значення з регістру
AX.
BE 02 7C – mov si, msg, замість msg виконується підстановка адреси відносно
7С00.
E8 E8 FF – CALL - виклик процедури, що знаходиться на 24 байт вище.
Розрахунок адреси виконується наступним чином: починаючи з останнього
байту команди CALL відмірюється задана константа (в даному випадку це
від’ємне значення, що задане в додатковому коді). Тобто від адреси 0x3A треба
відняти значення 0x18. У результаті отримуємо значення 0x22, тобто адресу
0xAC команди lodsb.
FA – cli
F4 - hlt
В даній програмі можна побачити, що BIOS завантажується за адресою
0x7C00 (org 0x7C00). Це адреса пам’яті, в яку BIOS завантажує MBR (англ.
Master Boot Record, перший сектор в hdd / fdd). Тобто всі адреси будуть
прив’язані до 0x7C00. Це означає, що перша інструкція буде записана за
адресою 0x7C00. Якщо дана адреса відсутня і не може бути зчитана, то BIOS
вважає пристрій незавантажуваним і переходить до перевірки наступного
пристрою.
Команда mov si, msg завантажує у регістр si адресу першого байту
повідомлення "Welcome to My Operating System!". Команда lodsb завантажує в
регістр AL байт, слово або подвійне слово з комірки пам’яті, що вказується за
допомогою індексного регістру-джерела (регістру si), яке після завантаження
автоматично збільшується на одиницю.
Коли завантажувач операційної системи запущений, процесор знаходиться
в реальному режимі, як було сказано раніше. Це означає, що можуть
використовуватися переривання BIOS, що включають в себе будь-які
переривання, що відображаються безпосередньо з апаратного забезпечення.
В коді програми переривання можна розглянути на прикладі функції
виведення символу на екран INT 10h. В регістр AL поміщений символ, що
виводиться на екран, а в AH – номер функції виводу символу.
Команда cli, що використовується в програмі скидає флаг IF. Коли цей флаг
скинутий, процесор ігнорує всі переривання від зовнішніх пристроїв, що
маскуються, але немасковані переривання (такі як Reset) залишаються
активними .
Команда HLT зупиняє роботу процесора до приходу чергового переривання
(може бути маскуємим або не маскуємим). Використовується для випадків
коли програма виконалася та чекає переривання від зовнішнього пристрою,
щоб продовжити свою роботу.
Команда times доповнює бінарний код нулями до 510 байт. Оператор
долара ($) представляє собою адресу поточного рядка. $$ представляє собою
адресу першої інструкції (org 0x7C00). Таким чином, $-$$ повертає кількість
байтів від поточного рядка до початку (в даному випадку це розмір програми).
Як ми вже вказали раніше, BIOS INT 0x19 шукає завантажувальний
диск. Якщо 511-й байт встановлений в 0xAA, а 512-й байт - в 0x55, INT 0x19
передає управління на першу команду завантаженого сектору. Оскільки макер
завантаження повинен бути останніми двома байтами в завантажувальному
секторі, в програмі використовується ключове слово times, щоб обчислити
інший розмір для заповнення до 510-го, а не 512-го байта.
Нижче представлені блок-схеми програми задля розуміння алгоритму
рішення задачі.
Рис.3. Блок-схема алгоритму рішення задачі та виведення рядка на друк

Рис.4. Загальна блок-схема алгоритму рішення задачі


Алгоритм до загальної блок-схеми представлений у лістингу коду 8.
Лістинг 8
C:\os\nasm\nasm.exe -f bin boot1.asm -o boot1.bin
dd bs=512 count=1 if=boot1.bin of=image.flp

Рис. 5. Скріншот виконання програми

Контрольні питання
1. Что такое Real mode чем он отличается от других режимов. Какая
разрядность шин данных-команд, шины адреса. Однозадачный ли?
Работает с реальной или виртуальной памятью?
2. Каким образом производится адресация в реальном режиме – как
высчитывается адрес? Показать на примере.
3. Как стартует и работает процедура POST? Какой адрес находится в IP
после включения компьютера? Как происходит взаимодействие с
устройствами на низком уровне (порты, прерывания)? Какую роль
играют команды out, in? Как находится устройство с которого
загружается ОС, как считывается загрузочный сектор (прерывание,
функция), какого он размера и по какому адресу БИОС записывает
загрузочный сектор диска? Как проверяет? Как запускает на
выполнение?
4. На что влияет org 0x7C00? Что будет если ее убрать? Какой код следует
добавить в случае ее отсутствия? Оказывает ли влияние на команды
переходов, пересылки адресов. Зачем в сегментные регистры данных в
случаях ее (org 0x7C00) присутствия, записываются нули?
5. В команде move si, msg – msg означает адрес или символ строки?
6. Как работает команда lodsb, какое влияние оказывает на si?
7. Зачем используется команды or al, al; jz PrintDone?
8. Объяснить механизм работы прерываний БИОС. Зачем они нужны? Что
из себя представляют прерывания БИОС? Как передаются параметры?
Как задается функция? Показать на примере функции вывода символа
на экран.
9. Объяснить какие существуют прерывания в системе (аппаратные и
программные), приведите примеры? Какие прерывания маскируются
командой cli? Как эта маскировка физически реализуется – какие
компоненты в этом участвуют? Как аппаратные прерывания
обрабатываются в реальном режиме (таблица IVT, вектор прерывания и
пр.)?
10.Объяснить зачем нужна команда hlt?
11.Объяснить зачем нужна команда times… и что означают $-$$?
12.Объяснить зачем нужно писать в конце файла AA55. Что будет если
этого не сделать?

You might also like