# Сборка ядра Linux для Orange Pi 5 Plus (и других устройств на Rockchip RK3588) с поддержкой iSCSI

Для моего проекта (k3s на Orange Pi 5 Plus) потребовалась поддержка система управления блочным хранилищем 'longhorn'
(longhorn.io). _Longhorn_ позволяет создавать надёжные тома для контейнеров, автоматически реплицируя их между узлами
и обеспечения высокую доступности приложений (если один узел выходит из строя, данные остаются доступны на репликах
на других узлах).

Для _longhorn_ нужен `iSCSI` (**Internet Small Computer System Interface**) — сетевой протокол, который по TCP/IP 
подсоединяет удалённые диски так, будто они подключены физически. Longhorn через iSCSI обеспечивает передачу
данных между узлами по сети.

В ядре Linux поддержка _iSCSI_ реализована через модули, такие как `iscsi_tcp` и `scsi_transport_iscsi`. Однако,
в большинстве дистрибутивов для одноплатных компьютеров, включая и Orange Pi 5 Plus, эти модули отключены. Это делают
для экономии ресурсов, да и в целом, это нетипичный сценарий использования Orange Pi. В версии Ubuntu 22.04
для Orange Pi 5 Plus (в ядре от Xunlong), поддержка iSCSI тоже отключена, и это делает невозможным запуск Longhorn
из коробки.

**А значит нужно компиляция ядра (сбока)**

Мы вручную включим поддержку _iSCSI_ (модули `iscsi_tcp` и `scsi_transport_iscsi`), и cможем настроить
ядро под свои задачи. Однако компиляция и его замена требует осторожности и внимательности, ошибка в процессе может
привести к тому, что система не загрузится (но мы сделаем резервную копию текущего ядра, загрузчика, чтобы можно
было восстановить систему).

Обновления системы (например, через `apt`) с новым ядром будет проходить, как обычно: пакеты, утилиты и приложения
обновятся без проблем. Однако пересобранное ядро больше не будет автоматически обновляться через стандартные средства
дистрибутива. Если выйдет новая версия ядра, то придётся вручную повторить процесс компиляции. К счастью,
поставщик (это Xunlong) обновляет ядро не часто.

| ✎ Заметка                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Orange Pi 5 Plus использует процессор **Rockchip RK3588** (как и многие другие устройства под брендом Orange). Процесс компиляции ядра для всех RK35xx почти идентичен, и базируется на одном и том же дистрибутиве. Однако, разные периферийные устройства: например, GPIO, USB или Ethernet-контроллеры могут отличаться. Мы будем использовать `.config` от текущего, рабочего ядра, так что настройки будут актуальны. Но следует иметь в виду, что возможно, в будущем могут появиться изменения в конфигурации, дистрибутива и т.п. Следует учитывать, это и **никто ничего не гарантирует**. |

## План сборки ядра с iSCSI

* Резервное копирование текущего ядра, загрузчика, дерева устройств, /boot и т.п.
* Установить инструментарий для настройки и компиляции ядра
* Скачать исходники.
* Настроить конфиг для включения модулей iSCSI (и мониторинга NVMe).
* Скомпилировать ядро и модули.
* Установить новое ядро.
* Перезагрузиться в новое ядро.
* Проверка.

## Резервное копирование текущего ядра и /boot

Архивируем том `/boot` на нашем Orange Pi 5 Plus во временный каталог `/tmp`:
```bash
sudo tar -czf /tmp/boot-backup.tar.gz /boot
```

Проверим, что архив создался:
```bash
ls -al /tmp/boot-backup.tar.gz
```

Увидим что архив создался:
```text
-rw-r--r-- 1 root root 62116591 мар 23 20:33 /tmp/boot-backup.tar.gz
```

Теперь нам нужно скопировать его на какой-нибудь внешний хост (или носитель, но я буду копировать на хост другого
компьютера). Если что-то пойдёт не так (например, после обновления ядра система не загрузится) можно будет восстановить
файлы из этого архива. Мой Orange Pi 5 Plus, на котором я буду компилировать ядро имеет хост — `opi5plus-3` (замени
на свой хост), а имя пользователя от которого я работаю — `opi` (замени на свой). На рабочем компьютере, с которого
я захожу по SSH на Orange, у меня есть папка `~/backup/` (`mkdir -p ~/backup`). Скачиваю в неё архив:
```bash
scp opi@opi5plus-3.local:/tmp/boot-backup.tar.gz ~/backup/opi5plus-3-boot-backup.tar.gz
```

### Что в архиве?

Если что-то пойдет не так, нам надо знать какие файлы в `/boot` важны и для чего предназначены. Вот список файлов
('ls -al /boot'):
```text
drwxr-xr-x  4 root root     4096 янв  1  1970 ./
drwxr-xr-x 19 root root     4096 фев 16 19:59 ../
-rwxr-xr-x  1 root root   230456 ноя 21 09:56 boot.bmp*
-rwxr-xr-x  1 root root     3341 ноя 21 09:55 boot.cmd*
-rwxr-xr-x  1 root root     3413 ноя 21 10:04 boot.scr*
-rwxr-xr-x  1 root root   230393 ноя 21 07:08 config-6.1.43-rockchip-rk3588*
drwxr-xr-x  3 root root     4096 ноя 21 09:55 dtb/
drwxr-xr-x  3 root root     4096 ноя 21 09:55 dtb-6.1.43-rockchip-rk3588/
-rwxr-xr-x  1 root root 40579584 ноя 21 07:08 Image*
-rwxr-xr-x  1 root root 15462140 мар 21 16:36 initrd.img-6.1.43-rockchip-rk3588*
-rwxr-xr-x  1 root root  1152056 ноя 21 09:56 logo.bmp*
-rwxr-xr-x  1 root root        0 ноя 21 09:55 .next*
-rwxr-xr-x  1 root root      178 фев 16 19:59 orangepiEnv.txt*
-rwxr-xr-x  1 root root     1542 ноя 21 09:56 orangepi_first_run.txt.template*
-rwxr-xr-x  1 root root  6209932 ноя 21 07:08 System.map-6.1.43-rockchip-rk3588*
-rwxr-xr-x  1 root root 15462204 мар 21 16:36 uInitrd*
-rwxr-xr-x  1 root root 40579584 ноя 21 07:08 vmlinuz-6.1.43-rockchip-rk3588*
```

Ключевые файлы:
* `Image` — собственно ядро Linux (в данном случае версия 6.1.43 для Rockchip RK3588). При старте компьютера загрузчик 
  U-Boot загрузит его в память и передаст ему управление. Без этого файла система не запустится.
* `vmlinuz-6.1.43-rockchip-rk3588` — копия ядра (в системе уже есть резервная копия).
* `dtb/` — Каталог файлами **Device Tree Blob** (`DTB`). Это бинарные файлы, описывающие аппаратное обеспечение устройства.
  Для Orange Pi 5 Plus используется файл вроде `rk3588-orangepi-5-plus.dtb` (находится в подкаталоге `dtb/rockchip/`).
  DTB передаётся ядру, чтобы оно знало, как работать с процессором (количество ядер, частоты), памятью (RAM, её объём
  и адреса), периферией (UART, I2C, SPI, Ethernet, USB, GPIO, прерывания и тому подобное). На ARM-устройствах
  (в отличие от x86, где есть ACPI) нет стандартного способа обнаружения оборудования. DTB решает эту проблему,
  предоставляя ядру "карту" железа. U-Boot загружает DTB из `/boot/dtb/ `и передаёт его ядру при старте. Кстати,
  в подкаталоге dtb/rockchip/ есть `overlay/` — это дополнительные файлы, которые могут использоваться для добавления 
  и/или изменения функциональности устройства. Например, можно добавить поддержку новых периферийных устройств (камеру,
  дисплей и т.п.).
* `uInitrd` — Начальный RAM-диск (initrd), адаптированный для U-Boot. Содержит модули и скрипты, необходимые для
  пред-загрузки (выбор накопителя, монтирование корневой файловой системы и т.п.). Если он повреждён или несовместим
  с ядром, загрузка может упасть на этапе инициализации.
* `orangepiEnv.txt` — Конфигурационный файл для U-Boot. Здесь задаются параметры загрузки, такие как путь расположение
  дерева DTB, UUID корневой файловой системы, тип файловой системы… Без правильных настроек в этом файле U-Boot не
  найдёт нужные для загрузки файлы.
* `boot.scr` — Скрипт загрузки для U-Boot. Содержит команды для загрузки ядра, initrd и DTB. U-Boot выполняет его
  при старте системы. Этот файл создаётся из `boot.cmd` с помощью утилиты `mkimage`. Если он повреждён или отсутствует,
  U-Boot не сможет загрузить систему.
* `dtb-6.1.43-rockchip-rk3588/` — Копия каталога `dtb/`, обычно появляется, когда ядро устанавливается или обновляется
  через пакетный менеджер (например, `apt`). Она привязана к конкретной версии ядра — в данном случае
  `6.1.43-rockchip-rk3588`, для того, чтобы: Хранить DTB-файлы, соответствующие этой версии ядра и избегать конфликты
  между DTB от разных версий ядра (если используется несколько ядер на одной системе).

Менее критичные, но полезные файлы:
* `config-6.1.43-rockchip-rk3588` — Конфигурация ядра, использованная при его сборке (он нам понадобится, чтобы
  пересобрать ядро с iSCSI).
* `System.map-6.1.43-rockchip-rk3588` — Карта меток (символов) ядра, полезна для отладки.
* `initrd.img-6.1.43-rockchip-rk3588` — Обычный initrd, из которого генерируется uInitrd.
* `boot.bmp` и `logo.bmp` — Изображения для экрана загрузки (не влияют на работу системы).

## Устанавливать инструменты для сборки ядра

Обновим репозитории и установим минимальный инструментарий для сборки ядра:
```bash
sudo apt update
sudo apt install -y build-essential bc flex bison libssl-dev libncurses-dev git
```

Нам понадобятся следующие пакеты:

* `build-essential` — Включает `gcc` (для ARM64, make и другие базовые инструменты компиляции.
* `bc` — Утилита для точных математических вычислений в командной строке. Используется для вычислений в скриптах сборки ядра.
* `flex` и `bison` — генератор лексических анализаторов и парсер. Нужны для обработки конфигурационных файлов ядра.
* `libssl-dev` — Для поддержки криптографии в ядре и `OpenSSL`.
* `libncurses-dev` — Библиотека для создания текстовых интерфейсов в терминале. Необходимо для работы интерфейса
  `menuconfig` при настройке параметров ядра.
* `git` — Для клонирования исходного кода ядра из репозитория Xunlong.

> **Примечание:** Если вы хотите собрать ядро на x86-системе, установите кросс-компилятор `gcc-aarch64-linux-gnu` (`sudo apt install gcc-aarch64-linux-gnu`) и используйте его вместо обычного gcc в командах сборки. Он позволит собрать ядро для ARM64-архитектуры на x86-системе.


## Скачивание и подготавливаем исходники ядра
 
Клонируем репозиторий Orange Pi для ядра 6.1 из репозитория Xunlong. Мы будем брать ветку (branch)
`orange-pi-6.1-rk35xx`. Это ветка с исходниками для всех устройств на базе Rockchip RK35xx (включая Orange Pi 5 Plus):
```bash
git clone --depth 1 https://github.com/orangepi-xunlong/linux-orangepi -b orange-pi-6.1-rk35xx ~/linux-orangepi
```

После клонирования репозитория, переходим в каталог с ним:
```bash
cd ~/linux-orangepi
```

И первым делом в `Makefile` установим `EXTRAVERSION` (модификатор версии ядра). Узнаем текущую версию ядра:
```bash
uname -r
```

Я на своем Orange Pi 5 Plus вижу (у вас может быть другая версия):
```text
6.1.43-rockchip-rk3588
```

Открываем `Makefile` в текстовом редакторе:
```bash
nano Makefile
```

И устанавливаем модификатор версии ядра, строку `XTRAVERSION =` (у меня это пятая строчка):
```text
EXTRAVERSION = -rockchip-rk3588
```


Затем копируем конфиг текущего ядра в конфиг для сборки.
 ```bash
zcat /proc/config.gz > .config
```

`.config` содержит все текущие настройки ядра. Это очень удобно, так как нужно добавить только поддержку нужных модулей
(в нашем случае `iSCSI`), а не настраивать всё с нуля.

## Настройка конфига с iSCSI

Теперь нужно отредактировать, `.config` чтобы включить `iSCSI`. Заходим в интерфейс конфигуратора ядра `menuconfig`:
```bash
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- menuconfig
```

Используя стрелки на клавиатуре, чтобы двигаться по пунктам меню.
1. Перейди в `Device Drivers` → `SCSI device support`  → `SCSI Transports`.
2. Найти внутри `iSCSI Transport Attributes`, с помощью стрелок выбрать его.
3. Нажимает `M` (для модуля) — появится `<M>` рядом с пунктом. Это означает, что модуль будет собираться как модуль, 
   а не встраиваться в ядро. Модуль (`iscsi_tcp.ko`) проще протестировать без перезагрузки.
4. В нижнем меню выбрать `<Save>` и подтвердим сохранение в `.config`.
6. Вернуться в меню выше `SCSI device support` (через нижнее меню `<Exit>`).
7. Зайти в раздел `SCSI low-level drivers`. 
8. Выбрать `iSCSI Initiator over TCP/IP` и нажать `M` (для модуля).
9. Снова сохраняем конфиг в `.config` (через нижнее меню `<Save>`).
10. Выйти из `menuconfig` (через нижнее меню `<Exit>`, три раза).

| ✎ Может быть полезно:                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| На моём Orange Pi 5 Plus загрузка идет с NVMe диска ([см. как настроить](opi5plus-move-system-to-nvme-or-emmc.md)), и мне было полезно так же включить ещё и систему мониторинга NVMe: `Device Drivers` → `NVME Support`, найти пункт `SNVMe hardware monitoring` и включить его. В нижнем меню выбрать `<Save>` и подтвердим сохранение в `.config`. Выйти из `menuconfig` (через нижнее меню `<Exit>`, три раза). Теперь у меня будет возможность отслеживать состояние NVMe-диска (температуру, износ и т.д.) через инструменты вроде `nvme-cli`. |

Проверим, что iSCSI включён:
```bash
grep -E "ISCSI_TCP|SCSI_ISCSI_ATTRS|ISCSI_BOOT" .config
```

Должны увидеть что-то типа:
```text
CONFIG_SCSI_ISCSI_ATTRS=m
CONFIG_ISCSI_TCP=m
# CONFIG_ISCSI_BOOT_SYSFS is not set
```

| ✎ Проверка, что включен мониторинг NVMe (если включали):      |
|---------------------------------------------------------------|
| `grep -E "NVME_HWMON" .config` и увидим `CONFIG_NVME_HWMON=y` |

## Компиляция ядра и модулей

Запускаем компиляцию ядра и модулей:
```bash
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j8
```

`-j8` — это количество потоков, используем столько, сколько у нас ядер. На Orange Pi 5 Plus восемь ядер. Но, например,
если у вас четыре ядра, то `-j4`.

Компиляция займёт 20-40 минут (Orange Pi 5 Plus).

Установим модули ядра:
```bash
sudo make modules_install
```

После завершения ядро будет находиться в каталоге `~/linux-orangepi/arch/arm64/boot/` (файл `Image`), а дерево описания
устройства в `~/linux-orangepi/arch/arm64/boot/dts/rockchip/` (наш файл для Orange Pi 5 Plus --
`rk3588-orangepi-5-plus.dtb`, дле версий 5, 5 Max, AI, Ultra соответствующие файлы там же).

Проверим, что ядро скомпилировалось:
```bash
ls -l arch/arm64/boot/Image
```

Ожидаемый размер: около 40 МБ:
```text
-rw-rw-r-- 1 opi opi 40579584 Mar 22 19:09 arch/arm64/boot/Image
```

Размер должен почти совпасть (или полностью совпадать) с размером текущего ядра:
```bash
ls -l /boot/Image
```
```text
-rwxr-xr-x 1 root root 40579584 Nov 21 07:08 /boot/Image
```

И это не ошибка. Мы добавили только `CONFIG_ISCSI_TCP=m` и `CONFIG_SCSI_ISCSI_ATTRS=m`. Это подключит модули (`iscsi_tcp.ko`),
но само ядро (`Image`) почти не увеличивается, ведь `.ko`-модуль iSCSI не встраивается в ядро (мы выбрали `<M>`,
а не `<*>`). Кроме того, Linux выравнивает размеры файлов по блокам (например, 4 Кб), и добавление
пары небольших модулей могут не повлиять на итоговый размер. 

## Устанавливаем новое ядро рядом со старым

Мы должны скопировать два файла в `/boot`. Первый — это `Image`. Это само ядро, и оно скомпилировалось в каталог
`~/linux-orangepi/arch/arm64/boot/`. Второй файл — это `rk3588-orangepi-5-plus.dtb`. Он находится
в `~/linux-orangepi/arch/arm64/boot/dts/rockchip/`. Как было сказано выше, это дерево описания оборудования
(Device Tree Blob), и нужно при загрузке ядра. Кстати, при компиляции мы получили `.dtb` для всех моделей Orange Pi 5
(5, 5 Plus, 5 Max, AI, Ultra). Для моей модели (Orange Pi 5 Plus) нужен `rk3588-orangepi-5-plus.dtb`, а вы можете
взять другой, для своей модели.

Копируем новые файлы в `/boot` с уникальными именами (с суффиксом `-iscsi`):
```bash
sudo cp ~/linux-orangepi/arch/arm64/boot/Image /boot/Image-iscsi
sudo cp ~/linux-orangepi/arch/arm64/boot/dts/rockchip/rk3588-orangepi-5-plus.dtb /boot/dtb/rockchip/rk3588-orangepi-5-plus.dtb-iscsi
```

Старое ядро и дерево описания переименуем (это будет резервная копия, с суффиксом `-original`):
```bash
sudo mv /boot/Image /boot/Image-original
sudo mv /boot/dtb/rockchip/rk3588-orangepi-5-plus.dtb /boot/dtb/rockchip/rk3588-orangepi-5-plus.dtb-original
```

Устанавливаем новые ядро и дерево:
```bash
sudo cp /boot/Image-iscsi /boot/Image
sudo cp /boot/dtb/rockchip/rk3588-orangepi-5-plus.dtb-iscsi /boot/dtb/rockchip/rk3588-orangepi-5-plus.dtb
```

> **Важно**: Не обновляйте `uInitrd`, если текущий работает, — его замена может сломать загрузку системы.

Все готово.

## Перезагрузка в новое ядро

Тут все просто:
```bash
sudo reboot
```

#### Если что-то пошло не так и система не загрузилась

Если вы все делали правильно, то такого быть не должно. Но, тем не менее, если загрузка не произошла, то это может
выглядеть двумя способами:
* Система зависает на этапе загрузки: синенький огонек на Orange Pi не загорается и Ethernet тоже не мигает —
  _вы неправильно собрали ядро или испортили загрузчик.
* Система, вроде как, грузится, но все никак… огоньки весело мигают, но не получается подключиться ни по SSH, ни
  другим способом, пинги на IP-адрес не проходят — вы забыли подключить модули, накосячили с конфигом или с `.dtb`_.

Чтобы починить, загружайтесь с MicroSD-карты (не забудьте отключить питание перед тем как вставить MicroSD-карту).
Затем смонтируйте, в зависимости где у вас система, eMMC:
```bash
mkdir -p /mnt/emmc
mount /dev/mmcblk2p1 /mnt/emmc
cd /mnt/emmc
```

…или NVMe:
```bash
mkdir -p /mnt/nvme
mount /dev/nvme0n1p1 /mnt/nvme
cd /mnt/nvme
```

И восстанавливаем старое ядро и дерево описания:
```bash
sudo cp Image-original Image
sudo cp dtb/rockchip/rk3588-orangepi-5-plus.dtb-original dtb/rockchip/rk3588-orangepi-5-plus.dtb
```

Выключаем систему:
```bash
sudo poweroff
```

Вынимаем MicroSD-карту, включаем, и загрузиться старая система.

## Проверка нового ядра

Если же иситма поле обновления ядра загрузилась, проверим текущую версию ядра:
```bash
uname -r
```

Она должна остаться той же:
```text
6.1.43-rockchip-rk3588
```

Проверим, работает ли iSCSI: 
```bash
sudo systemctl status iscsid
```

Увидим что-то вроде:
```text
● iscsid.service - iSCSI initiator daemon (iscsid)
     Loaded: loaded (/lib/systemd/system/iscsid.service; enabled; vendor preset: enabled)
     Active: active (running) since Mon 2025-03-24 17:51:59 MSK; 28min ago
TriggeredBy: ● iscsid.socket
       Docs: man:iscsid(8)
   Main PID: 1070 (iscsid)
      Tasks: 2 (limit: 18977)
     Memory: 4.6M
        CPU: 94ms
     CGroup: /system.slice/iscsid.service
             ├─1069 /sbin/iscsid
             └─1070 /sbin/iscsid

мар 24 17:51:58 opi5plus-3 systemd[1]: Starting iSCSI initiator daemon (iscsid)...
мар 24 17:51:59 opi5plus-3 iscsid[1061]: iSCSI logger with pid=1069 started!
мар 24 17:51:59 opi5plus-3 iscsid[1069]: iSCSI daemon with pid=1070 started!
мар 24 17:51:59 opi5plus-3 systemd[1]: Started iSCSI initiator daemon (iscsid).
```

Как видим, `active (running)`, значит iSCSI работает.

| ✎ Заметка                                                                                                                                                                        |
|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Если вы включили мониторинг NVMe, то проверим и его. Установим nvme-cli:  `sudo apt install nvme-cli` и проверим состояние нашего накопителя `sudo nvme smart-log /dev/nvme0n1`. |

**ВСË!**

А, да, не забудьте удалить репозиторий с исходниками ядра, если он вам больше не нужен:
```bash
rm -rf ~/linux-orangepi
```