commit 7c3169833fb1850f0e601734a771e81fe42b14f3 from: Aleksey Ryndin date: Tue Sep 12 11:42:02 2023 UTC Moved from https://got.any-key.press/?action=summary&path=squat.git commit - 6b7a76042d9a4ea05defa90868034259a49a3313 commit + 7c3169833fb1850f0e601734a771e81fe42b14f3 blob - 158547f4cc6099f0ec5a59cf6de05974745d686d blob + 5f253db376e12e4a91df69b8deb3cfee5032ec9f --- .gitignore +++ .gitignore @@ -1,2 +1,4 @@ **/*.swp **/*.mp3 +capsule/squat/reports/DDI0183G_uart_pl011_r1p5_trm.pdf +capsule/squat/reports/SysReg_xml_v88A-2021-12.pdf blob - 15433943e06dc9bd6ff3f46e6516971541062385 blob + 5eda73bcb9c0e534e1285a7c6de3fa60585eacf7 --- capsule/index.gmi +++ capsule/index.gmi @@ -10,6 +10,10 @@ Материалы на русском языке распосложены здесь: => vostok/ Простой Gemini сервер vostok (Восток), написанный на C++11 +## squⱯt: С++11 код, запускаемый на aarch64 железе. + +=> squat/ squⱯt: С++11 код, запускаемый на aarch64 железе + ## Архив аудио лекции В своей капсуле я храню архив интересных (по моему мнению) аудиолекции. blob - /dev/null blob + 46e2a40e8f0275303e9d5bcfdd84c3b38ff3e684 (mode 644) --- /dev/null +++ capsule/squat/index.gmi @@ -0,0 +1,11 @@ +# squⱯt: С++11 код, запускаемый на aarch64 железе. + +Git репозиторий с исходным кодом сервера (C++11): +``` +ssh://anonymous@got.any-key.press/squat +``` +=> https://got.any-key.press/?action=summary&path=squat.git Web интерфейс к git репозиторию + +## Блог разработки проекта squⱯt + +=> reports/ blob - /dev/null blob + 7a70f14793e7705200ca4149d7058c103cd0ee10 (mode 644) --- /dev/null +++ capsule/squat/reports/0.0.1.gmi @@ -0,0 +1,128 @@ +# squⱯt v0.0.1 + +С++11 код, запускаемый на aarch64 железе. + +## Структура проекта + +Код проекта расположен в следующих исходных файлах: +* `boot.S`: стартовый (загрузочный) код в виде ассемблерного листинга. Код загоняет в бесконечный цикл все процессоры, кроме _первого_ (используя для идентификации регистр MPIDR_EL1). А на первом процессоре обнуляет секцию `BSS`, настраивает стек на адрес точки входа и отдает управление в C++ функцию `kernel_entry_point`. Стек по мере наполнения растет в сторону уменьшения адреса, поэтому стартовые код (точки входа) не перетирается. +* `kernel.cc`: модуль условного ядра. Вызывает необходимую инициализацию (на текущий момент это только UART) и передает управление коду полезной нагрузки (на текущий момент это `UART echo test mode`: получение из UART символов и их обратная отсылка). +* `uart_virt.cc`: реализация работы UART(PL011) для платформы virt в QEMU. +* `linker.ld`: описание геометрии секций для компоновщика. + +Проект собирается и запускается с использованием простого `Makefile`. + +=> https://developer.arm.com/documentation/ddi0595/2021-12/AArch64-Registers/MPIDR-EL1--Multiprocessor-Affinity-Register MPIDR_EL1, Multiprocessor Affinity Register +=> https://developer.arm.com/documentation/ddi0183 PrimeCell UART (PL011) Technical Reference Manual +=> https://qemu.readthedocs.io/en/latest/system/arm/virt.html QEMU: ‘virt’ generic virtual platform + +UPD(2023-09-12): Связи с тем, что ARM блокирует запросы к своей документации для пользователей из РФ, ниже добавлены ссылки на локально сохранённые документы. +=> SysReg_xml_v88A-2021-12.pdf Arm Architecture Registers +=> DDI0183G_uart_pl011_r1p5_trm.pdf PrimeCell UART (PL011) + +## Конфигурирование сборки + +Для сборки используются утилиты из состава LLVM. Для учета локальных особенностей системы, где происходит сборка, используется файл `config.mk`. + +Сборка и запуск протестированы в двух arm64-конфигурациях: +* Alpine Linux v3.17. Из особенностей тут только то, что используется утилита `objcopy` из пакета `llvm14`. +``` +$ cat config.mk +OBJCOPY = llvm14-objcopy +``` + +* OpenBSD 7.2. Локальный конфиг нужен для явного использования инструментария LLVM из пакетов, так как встроенный в базу не умеет кросс-компилировать. +``` +$ cat config.mk +AS = /usr/local/bin/clang +CXX = /usr/local/bin/clang++ +LD = /usr/local/bin/ld.lld +OBJCOPY = /usr/local/bin/llvm-objcopy +``` + +Сборка (команда `make`) последовательно вызовет следующие утилиты: +* `${AC}` (по умолчанию `clang`): обработка ассемблерного файла (`.S`) для получения объектного (`.o`). +* `${CXX}` (по умолчанию `clang++`): обработка C++ файлов (`.cc`) для получения объектных (`.o`). +* `${LD}` (по умолчанию `ld.lld`): компоновка исполняемого (`.elf`) файла из полученных на предыдущих шагах объектных файлов. +* `${OBJCOPY}` (по умолчанию `llvm-objcopy`): дамп исполняемого файла в плоское представление, как если бы этот файл был загружен в память на исполнение. Результатом получает `.img` файл. + +=> https://llvm.org/ The LLVM Compiler Infrastructure + +## Просмотр содержимого собранных бинарных файлов + +Утилиту `llvm-objdump` можно использовать для инспектирования содержимого получившегося `.elf` файла: +``` +$ llvm-objdump -d squat.elf +squat.elf: file format elf64-littleaarch64 + +Disassembly of section .text.boot: + +0000000000000000 <_start>: + 0: a0 00 38 d5 mrs x0, MPIDR_EL1 + 4: 00 5c 40 92 and x0, x0, #0xffffff + 8: 60 00 00 b4 cbz x0, 0x14 <_first_processor> + +000000000000000c <_idle>: + c: 5f 20 03 d5 wfe + 10: ff ff ff 17 b 0xc <_idle> + +0000000000000014 <_first_processor>: + 14: 60 14 00 10 adr x0, #652 + 18: 41 14 00 10 adr x1, #648 + +000000000000001c <_zero_bss>: + 1c: 1f 84 00 f8 str xzr, [x0], #8 + 20: 1f 00 01 eb cmp x0, x1 + 24: cb ff ff 54 b.lt 0x1c <_zero_bss> + 28: c0 fe ff 10 adr x0, #-40 + 2c: 1f 00 00 91 mov sp, x0 + 30: 02 00 00 94 bl 0x38 + 34: f6 ff ff 17 b 0xc <_idle> + +Disassembly of section .text: + +0000000000000038 : + 38: fd 7b bf a9 stp x29, x30, [sp, #-16]! + 3c: fd 03 00 91 mov x29, sp + 40: 25 00 00 94 bl 0xd4 <_ZN5Board4Uart10initializeEv> + 44: 1f 20 03 d5 nop + 48: a0 10 00 10 adr x0, #532 + 4c: 04 00 00 94 bl 0x5c <_ZN12_GLOBAL__N_116uart_send_stringEPKc> + 50: 18 00 00 94 bl 0xb0 <_ZN12_GLOBAL__N_119uart_echo_test_modeEv> + 54: fd 7b c1 a8 ldp x29, x30, [sp], #16 + 58: c0 03 5f d6 ret +``` +Тут стоит запомнить, что первая инструкция, которая ожидается к исполнению `mrs x0, MPIDR_EL1` имеет байт код `a0 00 38 d5`. + +А результирующий _плоский_ `.img` файл можно смотреть с использованием классического `hexdump`: +``` +$ hexdump -C squat.img +00000000 a0 00 38 d5 00 5c 40 92 60 00 00 b4 5f 20 03 d5 |..8..\@.`..._ ..| +00000010 ff ff ff 17 60 14 00 10 41 14 00 10 1f 84 00 f8 |....`...A.......| +00000020 1f 00 01 eb cb ff ff 54 c0 fe ff 10 1f 00 00 91 |.......T........| +00000030 02 00 00 94 f6 ff ff 17 fd 7b bf a9 fd 03 00 91 |.........{......| +``` +Тут стоит снова заметить, что в начале дампа мы снова видим заветный байт код `a0 00 38 d5`, то есть нашу инструкцию `mrs x0, MPIDR_EL1`. Это минимальный признак того, что сборка прошла успешно. + +## Запуск и текущая полезная нагрузка + +Пощупать текущую полезную нагрузку можно выполнив команду `make qemu`: +``` +$ make qemu +qemu-system-aarch64 -M virt -cpu cortex-a53 -kernel squat.img -nographic -monitor none -serial stdio +Squat entry point +UART echo test mode +> +``` + +В таком режиме код ждет получения очередного символа через UART, а затем отсылает его же обратно. + +## Запуск под отладкой + +При необходимости можно запустить QEMU в режиме отладки командой `make gdb-remote`. В отличии от обычного запуска (`make qemu`) при запуске qemu добавляются аргументы `-s -S`, которые включают отладку и ждут подключения отладчика. + +В нашем случае отладчиком будем выступать `lldb`, который локально подключится к QEMU через порт `1234`: +``` +$ lldb +(lldb) gdb-remote 1234 +``` blob - /dev/null blob + e096ad7fadcef7ee97a35632dcbb6378dc48d12f (mode 644) --- /dev/null +++ capsule/squat/reports/index.gmi @@ -0,0 +1,3 @@ +# Блог проекта squⱯt + +=> 0.0.1.gmi v0.0.1 (2023-03-02)