Пишем свой начальный загрузчик bootloader
Добавлено: 08 окт 2016, 13:34
Предыдущую часть смотрите здесь
Сегодня мы напишим свой собственный начальный загрузчик
Который помимо вывода строк на монитор будет предоставлять
нам возможность выбора из списка. Я не стала эспериментировать
со свистоперделками, цветом и другой доступной из биоса
хренью, а ограничилась тем что нам может действительно
в дальнейшем пригодится. Практически в коде у нас получился
обычный переключатель switch(). Если вы знакомы с меню
загрузчика freebsd то наш результат будет выглядеть похоже.
Основными кирпичиками для работы с биосом мы определили функции
вывода символа на монитор и задержки, аналогичной тому что нам
предоставляет команда sleep в баше. Алгоритм работы нашего
загрузчика следующий:
Загрузчик начинает свою работу и через команду
прерывания int $0x10 отправляет для вывода на монитор
символ за символом в интервале 0,05 секунды пока на мониторе
полностью не отобразится меню загрузчика определенное
в переменной menu.
Интервал между выводами знаков мы так же передаем
биосу посредством прерывания int $0x15. Далее мы отправляем
биосу сигнал ожидания ввода символа с клавиатуры int $0x16.
При нажатии любой клавиши наш код определит и преключит
выполнение по одному из трех сценариев. Первый сценарий
осуществится если мы нажмем единицу, второй - двойку и
третий - дефолтный если мы нажмем любую другую отличную
от первых двух клавиш. Вот код нашего загрузчика:
[spoiler][/spoiler]
Функцию вывода на монитор единичного символа мы обернули
в цикл и при помощи команды lods организовали конвеерную
итерацию по перебору всего массива строки. Выход из цикла
осуществляется автоматически когда закончится строка и в
регистре %al появится занчение '\0'
Так как функции в asm вызываются без параметров
а нам просто необходимо задавать ей разные адреса
строк в регистр %si то вместо того что бы перед каждым
вызовом этой функции мы заносили адрес переменной
в регистр (lea var, %si),
мы скомбинируем функцию с макросом в котором мы можем
задавать параметры. Тоесть мы вызываем определенный макрос
с параметром а он в свою очередь воспользуется услугами
функции. В результате мы уложимся всего одной командой.
В нашей программе определены два макроса. Один вполне
самодостаточный, строки 14-18, посылает биосу сигнал
на выполнение задержки определенный параметром. У нас имеются
задержки между выводом строк в 0,5 секунды и в 3 секунды
перед перезагрузкой. Все хорошо видно в коде программы.
Так как я посчитала что интервал между выводом знаков
строчного массива определенного в макросе минимально
возможной величино в 0,1 секунды это слишком много то
в функции вывода строки я не использовала макрос задержки
а написала самостоятельный код и определила в нем
интервал в 0,05 секунды. Строки 34-37. На самом деле при
вызове задержки интервал определяется в двух регистрах,
значения старших разрядов числа заносятся в регистр %cx,
а младшие разряды в регистр %dx. Одно общее значение из этих
двух регистров %cx:%dx и определяет интевал в микросекундах.
Тоесть поместив в регистр %cx ноль а в регистр %dx 5000
мы определили задержку как раз в 0,05 секунды. В макросе
мы ограничились только регисром %cx у которого нижний предел
начинается с 0,1 секунды, нас это устраивает, а %dx в этом
случае мы обнуляем что бы при вызове макроса не приходилось
указывать два параметра. Собираем наш код как описано в
предыдущей статье и запускаем в гипервизоре qemu-kvm.
[album]454[/album]
Начальный загрузчик у нас получился простенький, без излишеств,
но в то же время достаточно симпатичный и функциональный.
В следующей статье мы научимся отлаживать наш код и настроим для
этого подходящий инструментарий.
Сегодня мы напишим свой собственный начальный загрузчик
Который помимо вывода строк на монитор будет предоставлять
нам возможность выбора из списка. Я не стала эспериментировать
со свистоперделками, цветом и другой доступной из биоса
хренью, а ограничилась тем что нам может действительно
в дальнейшем пригодится. Практически в коде у нас получился
обычный переключатель switch(). Если вы знакомы с меню
загрузчика freebsd то наш результат будет выглядеть похоже.
Основными кирпичиками для работы с биосом мы определили функции
вывода символа на монитор и задержки, аналогичной тому что нам
предоставляет команда sleep в баше. Алгоритм работы нашего
загрузчика следующий:
Загрузчик начинает свою работу и через команду
прерывания int $0x10 отправляет для вывода на монитор
символ за символом в интервале 0,05 секунды пока на мониторе
полностью не отобразится меню загрузчика определенное
в переменной menu.
Интервал между выводами знаков мы так же передаем
биосу посредством прерывания int $0x15. Далее мы отправляем
биосу сигнал ожидания ввода символа с клавиатуры int $0x16.
При нажатии любой клавиши наш код определит и преключит
выполнение по одному из трех сценариев. Первый сценарий
осуществится если мы нажмем единицу, второй - двойку и
третий - дефолтный если мы нажмем любую другую отличную
от первых двух клавиш. Вот код нашего загрузчика:
[spoiler]
Код: Выделить всё
.code16 #шеснадцати битный режим
.section .text #только одна секция
.globl _start;
#точка входа в программу
_start:
jmp _boot #прыгаем на метку _boot: в код выполнения
welcome: .asciz "Hello, World\n\r" #here we define the string
goodby: .asciz "This is normal level\r\nreboot will over 3 seconds\r\n"
menu: .asciz "choose level download:\r\n1 - nomal boot\r\n2 - fast reboot\r\n"
mistake: .asciz "Mistake, try again\r\nreboot now"
.macro mWait str
mov $0x86, %ah
mov \str, %cx
int $0x15
.endm
.macro mWritestr str
lea \str, %si
call Writestr
.endm
.func
Writestr:
1: lodsb
orb %al, %al #проверяем на ноль, определяем конец строки.
jz 1f
movb $0xe, %ah
int $0x10
# mWait $0x1
mov $0x86, %ah
mov $0x0, %cx
mov $0x5000, %dx
int $0x15
jmp 1b
1: ret
.endfunc
_boot:
mWritestr welcome
mWait $0x5
mWritestr menu
mov $0x00, %ah
int $0x16
cmp $0x02, %ah
je Exit
cmp $0x03, %ah
je reboot
mWritestr mistake
mWait $0x10
jmp reboot
Exit:
mWritestr goodby
mWait $0x30
reboot:
ljmp $0xffff, $0x0000
#Заполняем оставшееся место до сигнатуры нулями
.fill (510 - (. - _start)), 1, 0
.word 0xaa55
Функцию вывода на монитор единичного символа мы обернули
в цикл и при помощи команды lods организовали конвеерную
итерацию по перебору всего массива строки. Выход из цикла
осуществляется автоматически когда закончится строка и в
регистре %al появится занчение '\0'
Так как функции в asm вызываются без параметров
а нам просто необходимо задавать ей разные адреса
строк в регистр %si то вместо того что бы перед каждым
вызовом этой функции мы заносили адрес переменной
в регистр (lea var, %si),
мы скомбинируем функцию с макросом в котором мы можем
задавать параметры. Тоесть мы вызываем определенный макрос
с параметром а он в свою очередь воспользуется услугами
функции. В результате мы уложимся всего одной командой.
В нашей программе определены два макроса. Один вполне
самодостаточный, строки 14-18, посылает биосу сигнал
на выполнение задержки определенный параметром. У нас имеются
задержки между выводом строк в 0,5 секунды и в 3 секунды
перед перезагрузкой. Все хорошо видно в коде программы.
Так как я посчитала что интервал между выводом знаков
строчного массива определенного в макросе минимально
возможной величино в 0,1 секунды это слишком много то
в функции вывода строки я не использовала макрос задержки
а написала самостоятельный код и определила в нем
интервал в 0,05 секунды. Строки 34-37. На самом деле при
вызове задержки интервал определяется в двух регистрах,
значения старших разрядов числа заносятся в регистр %cx,
а младшие разряды в регистр %dx. Одно общее значение из этих
двух регистров %cx:%dx и определяет интевал в микросекундах.
Тоесть поместив в регистр %cx ноль а в регистр %dx 5000
мы определили задержку как раз в 0,05 секунды. В макросе
мы ограничились только регисром %cx у которого нижний предел
начинается с 0,1 секунды, нас это устраивает, а %dx в этом
случае мы обнуляем что бы при вызове макроса не приходилось
указывать два параметра. Собираем наш код как описано в
предыдущей статье и запускаем в гипервизоре qemu-kvm.
Код: Выделить всё
as -o bootloader.o bootloader.s
ld -Ttext 0x7c00 --oformat=binary -o bootloader.bin bootloader.o
qemu-kvm -fda bootloader.bin -boot a
Начальный загрузчик у нас получился простенький, без излишеств,
но в то же время достаточно симпатичный и функциональный.
В следующей статье мы научимся отлаживать наш код и настроим для
этого подходящий инструментарий.