Pull to refresh

Comments 69

Вот если бы вы еще объяснили, как запустить программу без Grub :)
Ведь grub, фактически, является пускалкой нашего хелло-ворлда.
Без Груб запустить нетрудно, но первое, что надо уяснить — так делать не нужно. Не нужно бутлодеры переписывать, иначе так на них и остановитесь.
То, что свой бутлоадер писать смысла нет — и так понятно. Но, в общем случае, интересно, как пускается сам бутлоадер.

Сейчас ковыряю свою lpcxpresso по мотивам этих статей. Жуть, как интересно!
Это на кортексе М3 что-ли? Ну там попроще, мягко говоря) Без MMU-то. Кстати, туда тоже часто стоит подумать, а нужен ли свой костыль, или лучше FreeRTOS поставить — все-таки она протестирована многими тысячами, если не сотнями тысяч разработчиков. А дополнительных затрат почти не вносит, очень легкий код, без навязывания HALa.

А в общем случае это все рассматривается в подавляющем большинстве статей про программирование под x86, погуглите «hello world защищенный режим» — там рассматривается достаточно информации, чтобы понять как это вообще работает (по сути, теории-то там мало, вам БИОС отдает управление и крутитесь как хотите, настраивайте ваш протектед режим и радуйтесь), но в то же время от хеллоуворлда до нормального бутлодера очень далеко — не потому что там какие-то тайные знания, а потому что надо реализовать очень много рутинного кода по работе с ФС и периферией, чтобы лодер смог реально грузить ваше ядро на уровне современных.
Я потому и выбрал эту железку в силу своей простоты. Что касается FreeRTOS, задача-то в том и состоит, чтобы понять как оно сделано, а не «поставил-работает».
Бутлоадер пускает BIOS или EFI. А как они это делают, это уже отдельная история. Сложность в том, что пока контролёр памяти не инициализирован, использовать память нельзя Взято отсюда. Я не представляю, откуда в этот момент исполняется код. Буду благодарен, если кто-нибудь объяснит это.

З.Ы. Сам раньше игрался с бутлоадерами, даже мини-отладчик писал, который выводил содержимое регистров и немного данных из стека. Выводить умел в любой позиционной системе счисления (ну пока алфавит не кончится).
Из микросхемы биоса и исполняет. Она мапирована на адресное пространство.
Это то я знаю. Т.е. саму микросхему никак инициировать не нужно? Процессор получает из неё данные, минуя контроллер памяти?
Да. Самая первая инструкция, которую исполняет ваш процессор — это инструкция из ПЗУ БИОСа. Джамп, обычно.
Вы про x86? Там контроллер памяти — это первое, что инициализируется БИОСом (ну или UEFI, не суть важно). Процессор же при старте сразу начинает исполнять код, который в БИОСе хранится. Я ковырял старый биос от phoenix времён второго пентиума, там интересная техника использовалась для вызова функций. Памяти то нету, стека, следовательно, тоже. В листинге по этой ссылке можете посмотреть: sites.google.com/site/pinczakko/pinczakko-s-guide-to-award-bios-reverse-engineering#Relocatable_Hardware_Port. Вообще, почитайте эту статью, крайне интересно.

Вкратце, исполняется «пролог», который инициализирует память, распаковывает в память образ биоса, вычленяет из него модули и начинает их по-очереди запускать, а каждый модуль, в свою очередь, инициализирует свою часть железа. Потом уже подгружаются биосы pci карточек, а потом запускается загрузчик с диска или pxe сетевухи.
Мне интересно, как запускается BIOS на многоядерном ЦП?
Процессор же при старте сразу начинает исполнять код
а в многоядерном другие ядра выключены в момент старта или что они исполняют?
Выключены, они запускаются позже операционной системой.

На многоядерных процах читается бит из MSR регистров, и то ядро, которое является загрузочным, получит бит BSP установленным в 1, остальные ядра в ноль.

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

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


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


Ну а потом проключается RAM, делается её POST и дальше все из RAM.
Это очень кратко, на самом деле все куда сложнее)

Ну 64к для низкоуровневого кода это очень много. Можно всю инициализацию туда и уместить.

у winbond на чипсет AMD760g боис 2 мегабайта идет.

В 64К только ранняя. Самое сложное из RAM stage проворачивается, инициализация, нумерация, назначение ресурсов всех деавайсов, + ROM всякие запускаются, например для видюхи и драйвера для Ethernet, чтобы WAN обеспечивать, т.е. кода там очень даже прилично)

На ЦИТ форуме лет 10 назад валялась статья «The real hello world» где это делали. Если займетесь некромантией, может найдете. Там делали загрузочкую дискету в которой инициализировали А20 и переходили в защищенный режим. А потом из него уже писали строку hello world. Без каких либо костылей. Было довольно интересно написано, рекомендую.
Хорошая статья. Главное донести мысль до начинающих, что те, кто начинает ОС писать с бутлодера, скорее всего, на этом бутлодере и закончат.
В вашей статье отлично смещен фокус на разработку именно полезного кода, это радует.
Зачитался просто, огромное спасибо. Расширил свой кругозор и уменьшил в размерах своё черное пятно в знаниях (хоть оно так и осталось безграничным :-) ).
скорее увеличили свое белое пятно в черной вселенной знаний ))
А вообще статья интересная и увлекателная, но непонятная )) Никогда не задумывался, создавая очередную загрузочную флешку, как оно все устроено… А оно вот оно как…
UFO just landed and posted this here
В этой аналогии лишь одно хорошо — площадь (объем знаний) увеличивается квадратично, а длина окружности (граница с непознанным) — линейно. :)
Спасибо, было интересно.
В общем-то, в 8-разрядных микроконтроллерах это обычное дело, запускать код без ОС. И дисплея там зачастую нет, вывод только на какой-нибудь UART.
Настоящие процессоры это, в каком-то смысле, «микроконтроллеры на стероидах». Поэтому меня всегда интересовало, а как там обстоит дело с запуском кода без ОС. Из статьи я понял, что весьма и весьма сложно.
На самом деле ничего сложного. В реальном режиме (сразу после передачи управления бутлоадеру) вы можете делать что хотите: выодить текст на экран, писать\читать из портов, общаться с жёстким диском. Вот только не будет у вас страничной адресации, которая доступна в защищенном режиме, и память доступна будет не вся.
UFO just landed and posted this here
Классно. Спаисбо. Если хватит желания, попобую реализовать. Еще интересно было бы почитать как сделать очень простой дистрибутив линукс. Без всяких дополнений.
У меня плохое детство было, вспоминается больше 0x7C00.
все ушли писать собственные ОС ;-) Если серьезно, то мне раньше это казалось одной из высших ступеней знания компьютера и программирования. Хотя оно так и осталось, но для сознания стало более осязаемо) Вряд ли применю как то на практике, но почитать было интересно.
Класс, кто-то писал про Python OS, вот он шанс поднять онное :) главное перелинковать все правильно.
Угу. Только сначала написать менеджер памяти как минимум. А еще неплохо бы управление процессами/потоками и драйвер ФС.
Ага, и ещё один мааааленький кусочек — TCP/IP стек :)
Вот именно его отсутствие интерпретатор вполне переживет. =)
Да и планировщик, наверное, можно отрезать.
Нуу, тогда сразу возникает вопрос — а как код попадает на целевую машину. Набирать с клавиатуры вряд ли захотите, писать каждый раз на cd — запаришься, а дисководы нынче не найти. Остаются сеть и флешки, но tcp/ip в базовом варианте всяко проще, чем usb стек.
Так не, как вы будете доставлять код на машину извне?
Что мешает запихать grub, ядро и модуль на флешку? Grub с них вполне успешно грузится.
Если научиться на ту же флешку писать через BIOS, как это делает grub — то вполне рабочий прототип получится, да.
Ну, это уже выходит за «минимальную работоспособность». И VM86, по моим воспоминаниям, тот еще гемор.
Не обязательно настраивать VM86. Можно прыгнуть в RM, выполнить код, и вернуться в PM.
Я, честно говоря, уже многое забыл, но у меня есть подозрение, что переход в RM создаст кучу интересных проблем в плане адресации — нужно будет перенастраивать сегменты так, чтобы не потерять данные, которые планируется записать.
Не следует брать откуда-то файлы stdarg.h и types.h (который на самом деле stddef.h). Эти файлы — это не часть libc, это builtin заголовки вашего компилятора, они всегда поставляются с компилятором Си, они могут отличаться между версиями компилятора, и конечно же, реализация для, например, gcc и clang, различается.
На практике это означает что:

> Не следует брать откуда-то файлы stdarg.h и types.h (который на самом деле stddef.h).

Используйте эти заголовки только такими, какие они идут с вашим компилятором.
Интересно, а как MemTest86 загружается (можно с дискетки без всяких Грубов)?

А ещё, вроде (точно не знаю, не судите строго), досовские com-файлы голый машинный код содержали. Нельзя, скажем, взять какую-нибудь первобытную com-игруху и сразу ей управление передать, не загружая ОС?
У memtest'а собственный загрузчик.

.com файл — это обычный выполняемый файл который просто не содержит заголовка для ОС (ms-dos в вашем случае). А по факту его выполнение требовало подготовки, а также практически любая более-менее приличная программа как и игры зачастую полагаются на функции предоставляемые ms-dos и без оной конечно работать не будут.
Можно. Только загвоздка в самих .COM файлах. Их писали, полагая, что вы всё же загрузили ОС. А потому их писали, используя Int 21h направо и налево. И мало кто этим не пользовался.
UFO just landed and posted this here
losetup /dev/loop2 ./hdd.img \
--offset `echo \`fdisk -lu /dev/loop1 | sed -n 10p | awk '{print $$3}'\`*512 | bc` \
--sizelimit `echo \`fdisk -lu /dev/loop1 | sed -n 10p | awk '{print $$4}'\`*512 | bc`
losetup -d /dev/loop1

Первая команда монтирует ранее созданный раздел к устройству /dev/loop2

А вторая-то зачем?
А ещё, специально для ленивых, есть kpartx. Попробуйте kpartx -a -v hdd.img
Спасибо за статью, очень познавательно, есть теперь чем заняться на досуге)
UFO just landed and posted this here
Ммм, захотелось наконец заняться LFS.

— Хм, а нет ли на Курсере чего-нибудь такого…

;)
Если хочется без OS, и чего-то практичного — build.erlangonxen.org/ Правда придется писать на Erlang'e. Кстати, сам erlangonxen.org уже давно самохостящийся и запущен поверх голого гипервизора. Нажав на странице Esc или кликнув в верхнем правом углу можно даже посмотреть как оно там внутри тикает.
Описанный способ старта, понятное дело, не работает для GRUB2.
Достаточно в файл /etc/grub.d/40_custom (на ArchLinux, в других аналогично должно быть) прописать:
menuentry "mini_os" {
  set root='hd0,msdos1'
  multiboot /kernel.bin
  boot
}

Естественно, что 'hd0,msdos1' и путь к вашему kernel.bin могут отличаться.
Если не трудно, пожалуйста, добавьте в статьи ссылку на следующую, чтобы было удобно читать цикл последователььно. Спасибо!
Добавили в каждую статью ссылки на следующие. Приятного прочтения!
А как тут реализовать sleep? Вызывать asm(«NOP») в цикле?
Можно попробовать воспользоваться системным таймером, но я не настоящий сварщик, потому не расскажу, как.
Об этом мы подробно расскажем в следующей статье цикла, реализация sleep достаточно нетривиальна.
Я уже нагуглил про int 15 (+ ваша статья № 5) — проблема только в том, что я понимаю C но не понимаю ассемблер — не могу переправить int в регистры
Sign up to leave a comment.