Команда IT специалистов выполнит подготовку инфраструктуры для вашего бизнеса.
Внедрение самых передовых решений и технологий.
Поддержка и сопровождение ваших сервисов.
Выполнение работ под "ключ", от покупки сервера, до настройки автоматизации процессов.
8(977)608-78-62 adm@nixm.ru

Пишем свой начальный загрузчик bootloader

Ответить
Аватара пользователя
nezabudka
Местный говорун
Местный говорун
Сообщения: 618
Зарегистрирован: 18 апр 2015, 06:13
Откуда: Ростов на Дону

Пишем свой начальный загрузчик bootloader

Сообщение nezabudka »

Предыдущую часть смотрите здесь
Сегодня мы напишим свой собственный начальный загрузчик
Который помимо вывода строк на монитор будет предоставлять
нам возможность выбора из списка. Я не стала эспериментировать
со свистоперделками, цветом и другой доступной из биоса
хренью, а ограничилась тем что нам может действительно
в дальнейшем пригодится. Практически в коде у нас получился
обычный переключатель 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
[/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.

Код: Выделить всё

as -o bootloader.o bootloader.s
ld -Ttext 0x7c00 --oformat=binary -o bootloader.bin bootloader.o
qemu-kvm -fda bootloader.bin -boot a
[album]454[/album]
Начальный загрузчик у нас получился простенький, без излишеств,
но в то же время достаточно симпатичный и функциональный.
В следующей статье мы научимся отлаживать наш код и настроим для
этого подходящий инструментарий.
"I invented the term Object-Oriented and I can tell you I did not have C++ in mind." - Alan Kay
Olej

Re: Пишем свой начальный загрузчик bootloader

Сообщение Olej »

/
nezabudka писал(а): Загрузчик начинает свою работу и через команду
прерывания int $0x10 отправляет для вывода на монитор
символ за символом в интервале 0,05 секунды пока на мониторе
полностью не отобразится меню загрузчика определенное
в переменной menu.
Если вы описываете это упражнение не только для себя, а ещё для кого-то, кто будет это читать, то нужно предупреждать:
- всё это будет работать только в реальном аппаратном режиме процессора x86 (с адресацией только до 640Kb RAM) ...
- который (режим) повторяет работу процессоров 8086/8088 - времени ... ранних 90-х
- все же программы в операционных системах в Windows, Linux, FreeBSD и во всех других операционных системах, когда они пишутся и на ассемблере - используют совсем другую мнемонику (некоторых) команд и схему адресации - защищёного режима (в который процессор переключается аппаратно)
- ... поэтому ассемблерный код реального режима невыполним в защищённом и наоборот
- (современные ОС в процессе загрузки переключает процессор из реального режима в защищённый)
- но в процессорах x86 существует, кроме реального и защищённого режимов, ещё один - иногда его называют нереальный, когда в режиме подобного реальному адресуется 4Gb памяти как в защищённом.

Зачем я сделал такое дополнение?
1. для общей эрудиции, тех кто заинтересуется тем, что в x86 (по недосмотру Intel) есть не 2, а 3 режима адресации.
2. очень интересно было бы все ваши эксперименты выполнять переведя процессор в нереальный режим адресации.
Аватара пользователя
nezabudka
Местный говорун
Местный говорун
Сообщения: 618
Зарегистрирован: 18 апр 2015, 06:13
Откуда: Ростов на Дону

Re: Пишем свой начальный загрузчик bootloader

Сообщение nezabudka »

Olej Сперва повторюсь, это немного расширенный обзор по следам серии статей где и описано
все в подробных деталях. Здесь я хочу дополнить что уже опубликованно немного под другим взглядом.
Если выберите в интернете первый десяток публикаций по написанию начального загрузчика то
обнаружите копию своего текста. Думаю именно мой вариан будет неплохим дополнением к уже существующим. В любом случае большое спасибо за коментарии и может кому то будет лень ходить
в гости.
"I invented the term Object-Oriented and I can tell you I did not have C++ in mind." - Alan Kay
Ответить

Вернуться в «Другие языки»