Минимальное ядро на Embedded Swift запустили в QEMU: Hello без ОС

Автор блога описывает хобби-эксперимент: написать максимально простое ядро на Swift и запустить его в эмуляторе QEMU. Цель, по его словам, не заменить Linux или другое популярное ядро, а получить удовольствие и понять, что нужно, чтобы программа работала без операционной системы под ней. Похожий эксперимент автор уже делал около 10 лет назад с проектом arOS. Пока ядро делает одно: печатает сообщение через QEMU и ждёт бесконечно.

Основа проекта, Embedded Swift, подмножество Swift для сред, где нет операционной системы и стандартной библиотеки в привычном смысле. Первая попытка собрать обычный проект с print не удалась: установленный компилятор не содержал стандартную библиотеку Embedded Swift. В документации сказано: «Поскольку Embedded Swift пока экспериментален и не поддерживается в публичных релизах Swift, вам понадобится инструментальная сборка для разработчиков». Автор поставил main-snapshot через swiftly, и простая программа заработала на хосте.

Дальше цель, машина без ОС. Тестирование идёт на виртуальной машине virt эмулятора QEMU на компьютере с Apple Silicon, целевая тройка, aarch64-none-none-elf (64-битный ARM без ОС и без среды выполнения C). В Package.swift включён экспериментальный режим Embedded, строгая модель безопасности памяти и превращение всех предупреждений в ошибки. Отдельный файл toolset.json передаёт компилятору флаги: Embedded и Volatile, -no-allocations, -function-sections, отключение защиты стека, -nostdlib и компоновщик lld; сам компоновщик запускается со --gc-sections, --orphan-handling=error и собственным скриптом linker.ld.

Первая сборка упала: компоновщик жаловался на несовпадение типов секций .strtab и .shstrtab и на неопределённые символы putchar и memmove. Символы пропали потому, что на bare-metal цели нет стандартной библиотеки C. Ошибки секций объясняются обработкой «осиротевших» секций: при включённой обработке компоновщик не гадает, куда класть служебные ELF-данные, поэтому скрипт должен разместить их явно.

Запуск устроен так. Небольшой ассемблерный файл boot.S определяет символ _start, читает номер ядра процессора, паркует все вторичные ядра в цикле wfe, на ядре 0 задаёт указатель стека и вызывает Swift-функцию kernel_main. Эта функция помечена атрибутом @c, чтобы ассемблер мог вызвать её по C-совместимому имени. Атрибут @c, по обновлению от 28 сентября 2026 года, советовал Max Desiatov вместо @_cdecl: он помечает глобальную функцию как реализованную на Swift C-функцию и формализует @_cdecl; пост и код автор обновил.

Недостающие функции автор написал сам. putchar пишет байт прямо в регистр PL011 UART по адресу 0x09000000, это драйвер последовательного вывода, не экранный; QEMU в режиме -nographic показывает полученные байты в терминале. memmove, простая реализация, копирующая вперёд или назад в зависимости от перекрытия областей. Автор отмечает: здесь проект перестаёт быть обычным прикладным программированием, потому что реализуются низкоуровневые функции, нужные самой среде выполнения языка.

Скрипт linker.ld загружает ядро по адресу 0x40080000, последовательно размещает загрузочный код, константы, данные, неинициализированные данные и стек, а ELF-метаданные (.symtab.strtab.shstrtab) размещает явно, отбрасывая .comment, отладочные секции и .eh_frame. Стек сейчас 128 кБ (0x20000), по словам автора, неоправданно много для ядра, печатающего одну фразу, но с запасом для вызовов функций.

Запуск: swift build с тройкой aarch64-none-none-elf, затем qemu-system-aarch64 с машиной virt и процессором cortex-a57. На экране появляется «Hello, Embedded Swift», после чего ядро остаётся в бесконечном цикле. Автор честно признаёт: это не полезное ядро, но настоящий исполняемый файл AArch64 ELF, который стартует с ассемблерной точки входа, линкуется без стандартной библиотеки и вызывает Swift-код на вручную настроенном стеке.

Проект намеренно не завершён. Следующие шаги пока не реализованы: очистка секции .bss при загрузке, абстракция над регистрами UART (сейчас адреса QEMU вписаны жёстко), выяснение, какие возможности выделения памяти поддерживает Embedded Swift, прерывания и таймеры, и в итоге замена бесконечного цикла чем-то, что планирует реальную работу. Позже автор, возможно, попробует фреймбуфер, настоящий драйвер клавиатуры и минимальную интерактивную среду.

В последней части ядро учат отвечать человеку. Приёмный регистр PL011 читается после проверки регистра флагов по адресу 0x09000018: бит 4 (RXFE) показывает, пуст ли приёмный буфер FIFO, и пока он пуст, ядро ждёт. Автор называет такой опрос (polling) очень неэффективным: настоящее ядро со временем перейдёт на прерывания, но для первой реализации так проще. С этим ядро может возвращать введённые символы в терминал: печатает «Swift kernel ready.», приглашение «> » и обрабатывает перевод строки.

Ключевые факты

  • Автор блога написал минимальное bare-metal ядро на Embedded Swift для aarch64-none-none-elf; оно запускается на машине virt эмулятора QEMU и печатает «Hello, Embedded Swift» через PL011 UART.
  • Для работы понадобилась development-сборка Swift (main-snapshot через swiftly), так как Embedded Swift пока экспериментален и отсутствует в публичных релизах; автор написал собственные putchar и memmove, а скрипт линковки загружает ядро по адресу 0x40080000 и выделяет стек 128 кБ.
  • Загрузка устроена просто: ассемблерный _start паркует вторичные ядра, на ядре 0 настраивает стек и вызывает Swift-функцию kernel_main, помеченную атрибутом @c (по совету Max Desiatov вместо @_cdecl).
  • Позже добавлен ввод символов с эхом через опрос регистра флагов UART (бит RXFE); автор называет опрос очень неэффективным и допускает переход на прерывания.
  • Проект намеренно не завершён: очистка .bss, абстракция над UART, выделение памяти, прерывания, таймеры и планирование пока не реализованы.

Почему это важно

Сама по себе новость скромная: это личный учебный эксперимент, который печатает одно сообщение, а не готовая система. Интерес в другом, он показывает на практике, что Swift в экспериментальном режиме Embedded можно собрать для машины вообще без операционной системы и без привычной стандартной библиотеки. Автор подчёркивает, что цель, не заменять Linux, а понять, что нужно для работы программы без ОС. Для читателя это наглядный разбор минимального набора частей: точка входа на ассемблере, стек, скрипт компоновки и пара функций, которых на голом железе нет.

Кому это важно

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

Как это применить

Из статьи можно повторить цепочку шагов: поставить development-сборку Swift через swiftly (main-snapshot), собрать проект командой swift build с тройкой aarch64-none-none-elf, файлом toolset.json и нативной системой сборки, затем запустить результат в qemu-system-aarch64 с машиной virt, процессором cortex-a57 и опцией -nographic. Нужны ассемблерный файл boot.S, скрипт linker.ld и собственные putchar и memmove. Рабочий пример автор приводит прямо в тексте; ссылка на репозиторий, как и сведения о лицензии, в пересказываемой части не фигурируют, так что код придётся переписывать по статье.

Можно ли доверять

Материал, личный блог, где автор сам описывает свои шаги, показывает конфигурации и текст ошибок линковщика и при этом не раздувает значимость результата («это не полезное ядро»). Проверить описанное можно, воспроизведя сборку. Имя автора в доступном тексте не указано. Проверка только в эмуляторе QEMU на машине с Apple Silicon; запуск на реальном железе не упоминается.

Риски и подводные камни

Embedded Swift экспериментален и требует development-сборки, поэтому флаги и поведение могут меняться между снапшотами. Многое в ядре жёстко прописано под QEMU: адреса UART и точка загрузки. Ввод реализован опросом, который автор считает очень неэффективным. Код использует небезопасные операции с указателями, а секция .bss пока не очищается при загрузке. Проект самим автором назван незавершённым, поэтому как основу для чего-то серьёзного брать его рано.

«Поскольку Embedded Swift пока экспериментален и не поддерживается в публичных релизах Swift, вам понадобится инструментальная сборка для разработчиков (development toolchain).»

— документация Embedded Swift, цитата в статье