gnu ассемблер и начальный загрузчик bootloader

gnu ассемблер и начальный загрузчик bootloader

Сообщение nezabudka » 28 сен 2016, 21:49

Вступительная часть здесь
Немного о ассемблерах.
Самое интересное, что в век высокоуровневых, производительных языков
программирования опускаться на самое дно каменного века и вытаскивать
на свет божий даже не счеты а счетные палочки - ассемблер, дело
довольно не продуктивное но, извиняюсь крайне захватывающее. Сколько бы
мы не занимались другими важными вещами, работа процессора для нас без
знания ассемблера всегда будет представляться популярной абстракцией в виде
черного ящика. Соглашусь что для многих вещей этого вполне достаточно,
но для интересующихся людей к которым причисляю и себя, маловато будет.
Чем мне интересно программирование в линукс, это тем что все уровни
абстракции логичны и взаимно связанны. Познакомясь с линухом случайно
я оказалась как бы в середине происходящего без стенок, потолка и пола.
Двигаться в слепую в своем интересе можно было в любую выбранную сторону,
чем я по сути успешно и занималась до недавнего времени. Мир линукса на
столько многообразен и многогранен что совсем недавно я даже не подозревала
что смогу когда нибудь нащупать его границы. И вот совсем недавно я обнаружила
для себя дно - основание в виде гнутого ассемблера который предоставляет
фундаментальные понятия работы процессора и основы работы компьютера в целом.
Согласитесь стоя на фундаменте гораздо проще ознакомится со всем архитектурным
ансамблем. А раз так я предлагаю всем вместе заняться его строительством.
Вооружившись начальными знаниями по ассемблеру и несколькими ресурсами по
осиписательству я предлагаю вместе пройти проторенной дорожкой, постараться
не заблудится, а по возможности протоптать хоть что то свое. Сперва я хотела
сделать пару вводных статей об инструментах разработки, вируальных машинах и
базовой системе и подойти так сказать основательно к процессу, но вот у меня
сильно разболелся зуб и я подумала "ну все это нафик" будем подключать все по
мере пользования, то есть сразу в бой иначе интрига зачахнет и умрет.
И так, немного о дизайне предстоящего процесса.
Дайвайте сразу определимся с точкой входа, тоесть обозначим на временном
промежутке работы компьютера начало нашей будущей программы. Многие думают,
что начальный загрузчик и называется начальным потому, что ему достается
свободный процессор в его полное, распоряжение. Но не тут то было. На метеринской
плате есть постоянное место хранения специальной программы. Вот она то при
включении компьютера и загружается в оперативную память первая и в дальнейшем
начинает свое выполнение на процессоре. В то время когда начальный загрузчик
попадает в компьютер на нем уже во всю выполняется другая программа. Микросхема
биоса скопировала свое содержимое в оперативку и работа этой программы является
ни чем иным как работой ядра стартовой операционной системы.
В нашей программе нет даже драйверов что бы взаимодействовать с клавиатурой и
монитором на прямую и поэтому мы воспользуемся услугами драйверов запущенного
биоса что бы общатся с программой. В обычном дистрибутиве, когда запускаем
пользовательский код, мы можем взаимодействовать с ядром системы по средством
системных вызовов. Записываем нужные аргументы в определенные регистры или стек
и отправляем запрос на прерывание - выполнение, определенные в ассемблере
командами "int $0x80" в 32 битной и "syscall" в 64 битной версии для
x86 совместимых процессоров. Точно так же и в загруженное в оперативку
ядро-биос мы будем давать запрос на прерывание, а биос любезно будет
предоставлять нам свои возможности для выполнения наших заданий и в
тоже время с удовольствием поделится с нашим кодом процессорным временем
для наших нужд когда мы можем обойтись и силами собственного кода.
То есть наш код будет пользоваться процессором совместно с биосом и иногда
пользоваться некоторыми уже готовыми функциями биоса по нашим запросам.
И фактически мы напишем пользовательскую программу которая будет
работать в операционной системе БИОС.
Для этого стартапа свою домашнюю федору я забраковала и установила рядышком
дебиан тестинг, так как из за гонки за новизной имеется множество хвостов
которые заносить мне за этим драконом-федорой стало напряжно.
Начальный код загрузчика со страницы
http://www.independent-software.com/wri ... -system-2/
я переписала под синтаксис AT&T мне так привычней и исправила некоторые
ошибки или опечатки. Тем кто предпочитает синтаксис от intel и захочет
воспользоваться оригинальной версисией программы я предлагаю самому найти
неточности и думаю это будет интересно. Для всех остальных я привожу
переписанный код со всеми пока не использующимися переменными. Но разъяснения
по работе с начальным загрузчиком я решила построить на другом коде написанном
мною в результате разбора некоторых аналогичных проектов. Мой код является
учебным и не имеет продолжения, но на параллельном примере будет проще
разобраться с кодом из проекта. Что бы не растянутся в вечность в этом
посте я привожу первый код и примеры компиляции и запуска его.

[spoiler]
Код: выделить все
.code16
.att_syntax noprefix
.section .text
.org 0x0                                       

LOAD_SEGMENT = 0x1000       # load the boot loader to segment 1000h

.global _start
_start:
   jmp start                 # jump to beginning of code
   nop

bootsector:
iOEM:          .ascii "DevOS   "    # OEM String
iSectSize:     .word  0x200         # bytes per sector
iClustSize:    .byte  1             # sectors per cluster
iResSect:      .word  1             # #of reserved sectors
iFatCnt:       .byte  2             # #of FAT copies
iRootSize:     .word  224           # size of root directory
iTotalSect:    .word  2880          # total # of sectors if over 32 MB
iMedia:        .byte  0xF0          # media Descriptor
iFatSize:      .word  9             # size of each FAT
iTrackSect:    .word  9             # sectors per track
iHeadCnt:      .word  2             # number of read-write heads
iHiddenSect:   .int   0             # number of hidden sectors
iSect32:       .int   0             # # sectors for over 32 MB
iBootDrive:    .byte  0             # holds drive that the boot sector came
from
iReserved:     .byte  0             # reserved, empty
iBootSign:     .byte  0x29          # extended boot sector signature
iVolID:        .ascii "seri"        # disk serial
acVolumeLabel: .ascii "MYVOLUME   " # volume label
acFSType:      .ascii "FAT16   "    # file system type

.func
WriteString:
   lodsb                       # load byte at ds:si into al (advancing
si)
   or   %al,   %al         # test if character is 0 (end)
   jz   WriteString_done    # jump to end if 0.
   
   mov   $0xe,   %ah         # Subfunction 0xe of int 10h (video
teletype output)
   mov   $9,   %bx         # Set bh (page nr) to 0, and bl
(attribute) to white (9)
   int   $0x10               # call BIOS interrupt.
   
   jmp   WriteString         # Repeat for next character.

WriteString_done:
   retw
.endfunc

.func
Reboot:
   lea   rebootmsg, %si      # Load address of reboot message into si
   call   WriteString         # print the string
   xor   %ax,   %ax         # subfuction 0
   int   $0x16               # call bios to wait for key

   .byte  0xEA                 # machine language to jump to FFFF:0000
(reboot)
   .word  0x0000
   .word  0xFFFF
.endfunc

start:
   # Setup segments:
   cli
   mov   %dl,   iBootDrive  # save what drive we booted from (should
be 0x0)
   mov   %cs,   %ax         # CS = 0x0, since that's where boot sector
is (0x07c00)
   mov   %ax,   %ds         # DS = CS = 0x0
   mov   %ax,   %es         # ES = CS = 0x0
   mov   %ax,   %ss         # SS = CS = 0x0
   mov   $0x7C00, %sp        # Stack grows down from offset 0x7C00
toward 0x0000.
   sti 
   
   # Display "loading" message:
   lea   loadmsg, %si
   call   WriteString
   
   # Reset disk system.
   # Jump to bootFailure on error.
   mov   iBootDrive, %dl     # drive to reset
   xor   %ax,   %ax         # subfunction 0
   int   $0x13               # call interrupt 13h
   jc   bootFailure         # display error message if carry set(error) 
   # End of loader, for now. Reboot.
   call Reboot

bootFailure:
   lea   diskerror, %si
   call   WriteString
   call   Reboot

# PROGRAM DATA
loadmsg:          .asciz "Loading OS...\r\n"
diskerror:        .asciz "Disk error.\r\n"
rebootmsg:        .asciz "Press any key to reboot..."

.fill (510 - ( . - _start )), 1, 0  # Pad with nulls up to 510 bytes
BootMagic:  .word 0xAA55
[/spoiler]

Перед сборкой надо установить компилятор:
Код: выделить все
sudo apt install binutils

Собираем код в объектный файл:
Код: выделить все
as -o boot.o boot.s

Линкуем:
Код: выделить все
ld -Ttext 0x7c00 --oformat=binary -o boot.bin boot.o

Размер нашего бин файла должен составлять 512 байт
Код: выделить все
ls -l

Сделаем образ дискеты:
Код: выделить все
dd if=/dev/zero of=floppy.img bs=1024 count=1440

или
Код: выделить все
mkdosfs -C floppy.img 1440

Последняя команда в дебиан требует рут прав поэтому после создания необходимо
вернуть награбленное хозяину:
Код: выделить все
sudo chown nez:nez floppy.img

Записываем загрузочный сектор на образ дискеты:
Код: выделить все
dd if=boot.bin of=floppy.img bs=512 count=1 conv=notrunc

В гипервизоре qemu-kvm бин файл можно запускать и так но в дальнейшем
работа с образом дискеты нам пригодится. Установим гипервизор и запустим
в нем наш загрузчик:
Код: выделить все
sudo apt install qemu-system-i386
qemu-system-i386 -fda floppy.img -boot a

Этот загрузчик только и делает что выводит три строчки и перезагружает биос
по нажатию любой клавиши. В следующей части я приведу параллельный код и
детальный разбор полетов с учетом возможных обсуждений этой части. Мы научим
загрузчик понимать клавиатуру и делать выбор в программе загрузки, а так же
разберем несколько вариантов дебагера, дезассемблера, радактора объектных и
бинарных файлов и вообще помучием наш код и себя основательно.
451
"I invented the term Object-Oriented and I can tell you I did not have C++ in mind." - Alan Kay
Аватар пользователя
nezabudka
Местный говорун
Местный говорун
 
Автор темы
Сообщений: 614
Фото: 180
Стаж: 3 года 6 месяцев 26 дней
Откуда: Ростов на Дону
Благодарил (а): 286 раз.
Поблагодарили: 147 раз.

gnu ассемблер и начальный загрузчик bootloader

Спонсор

Спонсор
 

Re: gnu ассемблер и начальный загрузчик bootloader

Сообщение Olej » 28 сен 2016, 23:19

Уточняем:
Цитата:
Записываем нужные аргументы в определенные регистры или стек
и отправляем запрос на прерывание - выполнение, определенные в ассемблере
командами "int $0x80" в 32 битной и "syscall" в 64 битной версии для
x86 совместимых процессоров.

- команды int 80h и sysenter (через которые осуществляются системные вызовы и в Linux и в Windows)
- sysenter существует как в 32-бит, так и в 64-бит процессоре...
- sysутеук появляется в процессорах Pentium IV и старше
- ... как, фактически, эквивалент int когда параметры ключевые параметры перехода (CS, SP, IP) теперь загружаются не из памяти, а из специальных внутренних регистров MSR (Model Specific Registers) с предопределёнными (0х174, 0х175, 0х176) номерами (из большого их общего числа), куда предварительно эти значения записываются, опять же, специальной новой командой процессора wmsr...
- сделано это из-за значительного проигрыша (рыночного) процессоров Pentium IV
- переход на команду вызова sysenter и возврат командой sysexit - это примерно 2008 год.

И ещё...
Разные операционные системы для системного вызова вызывают разные int, не обязательно 80h:
- MS DOS - 21h
- Windows - 2Eh
- Linux - 80h
- QNX - 21h
- Minix 3 - 21h
Olej
 
Стаж: 48 лет 10 месяцев 13 дней

Re: gnu ассемблер и начальный загрузчик bootloader

Сообщение Olej » 28 сен 2016, 23:30

Цитата:
И фактически мы напишем пользовательскую программу которая будет
работать в операционной системе БИОС.

Это очень интересно...
Но!

1. Это многократно, и уже давно, описано - ещё со времён публикаций Norton-а о MS-DOS.

2. Это не так сильно актуально, потому что BIOS отмирает ... и через 2-3 года вы не найдёте BIOS ни на одной современной модели.
Так же точно, как и загрузка (дальнейшая) с диска формата MBR, и вообще сам формат MBR.
Всё это - это уже экспонаты для политехнического музея.

Куда интереснее было бы, если подробно рассмотреть загрузку с UEFI ... и проделать, может, какие-то иллюстрационные примеры кода UEFI загрузки.
В этом был бы очень большой элемент новизны!
Olej
 
Стаж: 48 лет 10 месяцев 13 дней

Re: gnu ассемблер и начальный загрузчик bootloader

Сообщение nezabudka » 29 сен 2016, 06:26

Olej писал(а):

Куда интереснее было бы, если подробно рассмотреть загрузку с UEFI ... и проделать, может, какие-то иллюстрационные примеры кода UEFI загрузки.
В этом был бы очень большой элемент новизны!

Полностью согласна и с удовольствием в свое время с такой статьей бы познакомилась.
"I invented the term Object-Oriented and I can tell you I did not have C++ in mind." - Alan Kay
Аватар пользователя
nezabudka
Местный говорун
Местный говорун
 
Автор темы
Сообщений: 614
Фото: 180
Стаж: 3 года 6 месяцев 26 дней
Откуда: Ростов на Дону
Благодарил (а): 286 раз.
Поблагодарили: 147 раз.

Re: gnu ассемблер и начальный загрузчик bootloader

Сообщение nezabudka » 29 сен 2016, 07:27

Olej. Не будучи экспертом в осеписательстве и даже в программировании
Я счиатю выбранный путь знакомство с ассемблером чрез биос
по следам выбранных мной публикаций правильным. По гнутому ассемблеру
не так много материала и он весь крайне разрознен, что бы одновременно
идти не по проторенной дорожке. Запуск систем с uefi на виртуальных машинах
тема для меня совершенно новая и сложная и тем более на bochs думаю даже не
возможная. Вобщем я описала все свои посылы и побудительные мотивы в первой
части. Все эти замечания совершенно правильные но мне кажется в данном случае
оторванные от реальности и все это только усложнит процесс. С другой стороны
я совсем не против если кто то возьмет на себя труд и захочет в этом проекте
по путям которого я задумала пройти переписать загрузчик на продвинутый и
органично это все встроить и я буду даже совсем не против совместно поломать
голову и по мере своих знаний помогу чем смогу. Опишите как это должно выглядеть
я попробую вникнуть и подстроится. Но имейте ввиду локомотивом будете именно вы,
потому, что я точно знаю сегодняшний предел своим имеющимся у меня знаниям.
"I invented the term Object-Oriented and I can tell you I did not have C++ in mind." - Alan Kay
Аватар пользователя
nezabudka
Местный говорун
Местный говорун
 
Автор темы
Сообщений: 614
Фото: 180
Стаж: 3 года 6 месяцев 26 дней
Откуда: Ростов на Дону
Благодарил (а): 286 раз.
Поблагодарили: 147 раз.

Re: gnu ассемблер и начальный загрузчик bootloader

Сообщение Olej » 29 сен 2016, 16:21

nezabudka писал(а):

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

Я бы тоже с такой статьей с интересом познакомился, с готовой. :)
Но на заметку, для введения в предмет:
UEFI - проблемы и решения
BIOS & UEFI
GPT диски
Здесь можно последовательно проследить от начала: кто - кому - и как передаёт активность при загрузке.
Конечно, с какими-то вещами пришлось бы экспериментировать, выяснять экспериментальным путём.

А всё, что и как относится к BIOS - это всё настолько устаревшее, что такое занятие не будет иметь никакого продолжения.
Olej
 
Стаж: 48 лет 10 месяцев 13 дней

Re: gnu ассемблер и начальный загрузчик bootloader

Сообщение Olej » 29 сен 2016, 16:49

nezabudka писал(а):

По гнутому ассемблеру
не так много материала и он весь крайне разрознен, что бы одновременно
идти не по проторенной дорожке.

Я надеюсь, что вы понимаете, обратили внимание на то, что весь первичные загрузчик, с вызовами BIOS (int 13h и т.д.), а также и вторичный загрузчик (сильно предполагаю) LILO, GRUB, ... - всё выполняется в реальном режиме процессора x86. И начальная фаза загрузки Linux также происходит в реальном режиме: подготовка таблиц LDT, GDT, IDT, ... таблицы страниц MMU и др.
И только после этого происходит переключение в защищённый режим.
Это я напоминаю для того, что ассемблерный код реального и защищённого режима будут различаться (по виду).

А по поводу скудности материалов по GASM "гнутому ассемблеру", то обратите внимание:
- даже в коде ядра Linux из всего объёма ассемблерного кода не более 5-10% написано как отдельные файлы, компилирующиеся GASM
- а остальные 90-95% ассемблерного кода ядра написаны как инлайновые ассемблерные вставки (макрорасширения) языка C и компилятора GCC
- по инлайновому ассемблеру GCC тоже не так много материалов, но попадаются... вот здесь есть ссылки: inline assembler gcc

Возможно, эти дополнения вам чем-то помогут...
Olej
 
Стаж: 48 лет 10 месяцев 13 дней


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

Кто сейчас на форуме

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1

cron