Как запустить программу без операционной системы?

Как запустить программу самостоятельно без запуска операционной системы? Можете ли вы создать программы сборки, которые компьютер может загружать и запускать при запуске, например, загружать компьютер с флеш-накопителя и запускать программу, которая находится на процессоре?

Как запустить программу самостоятельно без запуска операционной системы?

Вы помещаете свой двоичный код в место, где процессор ищет после перезагрузки (например, адрес 0 на ARM).

Можете ли вы создать программы сборки, которые компьютер может загружать и запускать при запуске (например, загружать компьютер с флеш-накопителя и запускать программу, находящуюся на диске)?

Общий ответ на вопрос: это можно сделать. Его часто называют «голым программированием». Чтобы читать с флеш-накопителя, вы хотите знать, что такое USB, и вы хотите, чтобы какой-то драйвер работал с этим USB. Программа на этом диске также должна быть в определенном формате. В какой-то конкретной файловой системе … Это то, что обычно выполняют загрузчики. Многие платы ARM позволяют делать некоторые из этих вещей. У некоторых есть загрузчик, который поможет вам с базовой настройкой.

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

Редактировать: эта статья, и весь wiki.osdev.org отведут большинство ваших вопросов. http://wiki.osdev.org/Introduction

Кроме того, если вы не хотите экспериментировать напрямую с оборудованием, вы можете запустить его как виртуальную машину с помощью гипервизоров типа qemu. Посмотрите, как запустить «hello world» прямо на виртуализированное оборудование ARM здесь .

Управляемые примеры

Технически программа, которая работает без ОС, является ОС. Итак, давайте посмотрим, как создать и запустить некоторые мини-приложения hello world.

В этом репозитории GitHub присутствует код всех приведенных ниже примеров. Все было протестировано на Ubuntu 14.04 AMD64 QEMU и реальном оборудовании ThinkPad T430.

Загрузочный сектор

На x86 самый простой и самый низкий уровень, который вы можете сделать, это создать основной загрузочный сектор (MBR) , который является типом загрузочного сектора , а затем установить его на диск.

Здесь мы создаем один с одним вызовом printf :

 printf '\364%509s\125\252' > main.img sudo apt-get install qemu-system-x86 qemu-system-x86_64 -hda main.img 

main.img содержит следующее:

  • \364 в восьмеричном == 0xf4 в шестнадцатеричном 0xf4 : кодировка для инструкции hlt , которая говорит CPU прекратить работу.

    Поэтому наша программа ничего не сделает: только начните и остановитесь.

    Мы используем восьмеричную, потому что \x шестнадцатеричные числа не заданы POSIX.

    Мы можем легко получить эту кодировку с помощью:

     echo hlt > a.asm nasm -f bin a.asm hd a 

    но кодировка 0xf4 также документирована в руководстве Intel, конечно.

  • %509s производят 509 пробелов. Нужно заполнить файл до байта 510.

  • \125\252 в восьмеричном == 0x55 за которым следует 0xaa : магические байты, требуемые аппаратным обеспечением. Они должны быть байтами 511 и 512.

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

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

Работа на реальном оборудовании

Эмуляторы – это весело, но аппаратное обеспечение – настоящая сделка.

  • Запишите изображение на USB-накопитель (уничтожат ваши данные!):

     sudo dd if=main.img of=/dev/sdX 
  • подключите USB к компьютеру

  • включить его

  • скажите, чтобы он загрузился с USB.

    Это означает, что прошивка выбирает USB перед жестким диском.

    Если это не стандартное поведение вашего устройства, продолжайте нажимать Enter, F12, ESC или другие такие странные клавиши после включения питания, пока не получите меню загрузки, в котором вы можете выбрать загрузку с USB.

    Часто можно настроить порядок поиска в этих меню.

Привет мир

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

Очевидный вопрос: как сделать IO? Несколько вариантов:

  • попросите прошивку, например BIOS или UEFI, сделать, если для нас
  • VGA: специальная область памяти, которая печатается на экране, если она записана. Может использоваться в защищенном режиме.
  • написать драйвер и поговорить непосредственно с оборудованием дисплея. Это «правильный» способ сделать это: более мощный, но более сложный.
  • используйте функции отладки фишек. Например, ARM называет их полупозицией . На реальном оборудовании требуется некоторая дополнительная аппаратная и программная поддержка, но на эмуляторах это может быть удобный удобный вариант. Пример .

Здесь мы будем делать пример BIOS, поскольку он проще. Но учтите, что это не самый надежный метод.

Вот код GAS:

 .code16 .global _start _start: cli mov $msg, %si mov $0x0e, %ah loop: lodsb or %al, %al jz halt int $0x10 jmp loop halt: hlt msg: .asciz "hello world" .org 510 .word 0xaa55 

Помимо стандартных инструкций по сборке пользовательского интерфейса, мы имеем:

  • .code16 : указывает GAS на вывод 16-битного кода

  • cli : отключить программные прерывания. Это может привести к тому, что процессор начнет работать снова после hlt

  • int $0x10 : вызов BIOS. Это то, что печатает символы один за другим.

  • .org 510 и .word 0xaa55 : поместите магические байты в конец

Быстрый и грязный способ скомпилировать это с помощью:

 as -o main.o main.S ld --oformat binary -o main.ing -Ttext 0x7C00 main.o 

и запустите main.img как и раньше.

Здесь есть два важных флага:

  • --oformat binary : выводить код исходной двоичной ассемблера, не деформировать его внутри файла ELF, как это имеет место для обычных исполняемых файлов userland.

  • -Ttext 0x7C00 : нам нужно сообщить компоновщику ld где будет размещен код, чтобы он мог получить доступ к памяти.

    В частности, это используется во время фазы перемещения. Подробнее об этом читайте здесь .

Лучший способ компиляции – использовать чистый сценарий компоновщика, как этот . Сценарий компоновщика также может помещать магические байты для нас.

Прошивка

По правде говоря, ваш загрузочный сектор – это не первое программное обеспечение, которое работает на CPU системы.

На самом деле сначала выполняется так называемая прошивка , которая является программным обеспечением:

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

Известные прошивки include:

  • BIOS : старая всеохватная прошивка x86. SeaBIOS – это стандартная реализация с открытым исходным кодом, используемая QEMU.
  • UEFI : преемник BIOS, более стандартизированный, но более способный и невероятно раздутый.
  • Coreboot : попытка благородного креста с открытым исходным кодом

Прошивка делает такие вещи, как:

  • цикл на каждый жесткий диск, USB, сеть и т. д., пока вы не найдете что-то загрузочное.

    Когда мы запускаем QEMU, -hda говорит, что main.img – это жесткий диск, подключенный к оборудованию, и

    hda – первый, который нужно попробовать, и он используется.

  • загрузите первые 512 байт на адрес RAM 0x7c00 , поместите туда RIP CPU и дайте ему запустить

  • показать такие вещи, как меню загрузки или вызовы печати BIOS на дисплее

Прошивка предлагает OS-подобные функции, от которых зависит большинство ОС. Например, подмножество Python было перенесено на BIOS / UEFI: https://www.youtube.com/watch?v=bYQ_lq5dcvM

Можно утверждать, что прошивки неотличимы от ОС, и эта прошивка – это единственное «истинное» программирование с открытым металлом, которое можно сделать.

Как это делает CoreOS-разработчик :

Трудная часть

Когда вы включаете ПК, чипы, составляющие чипсет (северный мост, южный мост и SuperIO), еще не инициализированы должным образом. Несмотря на то, что BIOS ROM так же удален от CPU, как это было возможно, это доступно CPU, потому что это должно быть, в противном случае у CPU не было бы никаких инструкций для выполнения. Это не означает, что BIOS ROM полностью отображается, как правило, нет. Но достаточно отобразить, чтобы процесс загрузки происходил. Любые другие устройства, просто забудьте об этом.

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

Начальное состояние исходного BIOS

Как и многие другие аппаратные средства, стандартизация слабая, и одним из факторов, на которые вы не должны полагаться, является начальное состояние регистров, когда ваш код запускается после BIOS.

Поэтому сделайте себе одолжение и используйте код инициализации следующим образом: https://stackoverflow.com/a/32509555/895245

Регистры, такие как %ds и %es имеют важные побочные эффекты, поэтому вы должны их обнулить, даже если вы не используете их явно.

Обратите внимание, что некоторые эмуляторы лучше, чем реальное оборудование, и дают вам хорошее начальное состояние. Затем, когда вы запускаете реальное оборудование, все ломается.

GRUB Multiboot

Загрузочные сектора просты, но они не очень удобны:

  • вы можете иметь только одну ОС на диск
  • код нагрузки должен быть очень маленьким и вписываться в 512 байт. Это можно было бы решить с помощью вызова int 0x13 BIOS .
  • вы должны сделать много запуска самостоятельно, например, переходить в защищенный режим

Именно по этим причинам GRUB создал более удобный формат файлов, называемый multiboot.

Минимальный рабочий пример: https://github.com/cirosantilli/x86-bare-metal-examples/tree/d217b180be4220a0b4a453f31275d38e697a99e0/multiboot/hello-world

Если вы подготовите свою ОС как многозадачный файл, GRUB сможет найти ее в обычной файловой системе.

Это то, что делают большинство дистрибутивов, помещая изображения ОС под /boot .

Файлы Multiboot в основном представляют собой файл ELF со специальным заголовком. Они указаны GRUB по адресу: https://www.gnu.org/software/grub/manual/multiboot/multiboot.html.

Вы можете включить многозадачный файл на загрузочный диск с grub-mkrescue .

Эль-Торито

Формат, который можно записать на компакт-диски: https://en.wikipedia.org/wiki/El_Torito_%28CD-ROM_standard%29

Также возможно создать гибридное изображение, которое работает на ISO или USB. Это можно сделать с помощью grub-mkrescue ( пример ), а также сделать kernel ​​Linux на make isoimage с помощью isohybrid .

РУКА

ARM-land имеет свои собственные соглашения (или больше отсутствие соглашений, поскольку ARM лицензирует IP поставщикам, которые его изменяют), но общие идеи одинаковы:

  • вы пишете код в магический адрес в памяти
  • вы делаете IO с магическими адресами

Некоторые отличия:

  • IO выполняется путем записи на магические адреса напрямую, нет инструкций и инструкций
  • вам нужно добавить волшебные скомпилированные закрытые исходные капли, предоставленные поставщиками для вашего изображения. Это похоже на BIOS, и это хорошо, поскольку это делает обновление прошивки более прозрачным.

Вот некоторые примеры:

  • Как сделать голые программы ARM и запустить их на QEMU? QEMU дружественное взаимодействие UART
  • Как запустить программу C без ОС на малиновой пи? Малиновый Pi аппаратный дружественный светодиодный интерфейс

Ресурсы

  • доступ к файлам с пробелами в имени файла из java
  • Ресурсы для разработки операционной системы
  • Существуют ли какие-либо macros, чтобы определить, скомпилирован ли мой код для Windows?
  • Давайте будем гением компьютера.