Linux Mobile або “emerge world” для PinePhone. Частина 1
Привіт, мене звуть В’ячеслав, і у мене є мрія.
Мрія про те, що колись світ мобільних платформ стане бодай трошки вільнішим і демократичнішим. На сьогоднішній день світ настільних ПК виглядає доволі строкато, але непогано. Тут, поряд з корпоративною автократією від Microsoft і теократією від Apple завжди існував демократичний світ Linux в якому завжди є вибір, який саме софт використовувати, а який не використовувати, і в який саме спосіб. Тут ти завжди можеш запропонувати свою власну альтернативу, якщо існуючи рішення тебе не влаштовують.
Історія про Linux — це завжди історія про вибір і, відповідно, про відповідальність за свій вибір, а також це історія не про пасивне споглядання і «страждання», коли тебе щось не влаштовує, а про активну участь в підтримці і розвитку того, що тебе влаштовує і подобається. Це історія про власний внесок.
Так от, зі світом мобільних платформ (мова тут йде передусім про смартфони) нам пощастило значно менше. Корпорації зробили правильні висновки і зуміли «захопити владу» доволі ефективно. Тут існує два виключно «диктаторських режими» при яких ніхто не питає і не зважає на твою думку, тут не існує «виборів», тут твій «верховний лідер» в особі тієї чи іншої корпорації сам вирішує що є добре для тебе, а що ні. Тільки подумайте, і Google і Apple вважають всіх своїх користувачів настільки неповносправними і безвідповідальними, що не надають їм прав адміністратора на смартфоні, а будь яка спроба отримати більше «влади» користувачем (отримання root прав, або доступ до bootloader-а) нещадно карається позбавленням гарантії, блокуванням багатьох додатків тощо.
Цього ніколи не було в світі настільник ПК. Ми завжди мали доступ до BIOS/UEFI, мали права адміністратора. А тут, раптом, виявляється, що архітектура і Android і iOS настільки погана, що щойно користувач отримує повні права на власний телефон (вважайте стає його повноправним власником, а не співвласником) цей телефон стає страшезно вразливим, незахищеним від «злого зовнішнього світу», і кожен «безвідповідальний» користувач неодмінно заподіє собі шкоду. А відтак, корпорації, «дбаючи» про нас з вами, і діючи виключно в наших інтересах, обмежують наші права і можливості.
Ну а під шумок цього «піклування», шпигують за нами, досліджують наші інтереси, звички, смаки з тим щоб в кінці кінців перетворити нас на такий собі цифровий гумус на якому можна буде далі успішно «вирощувати» свої доларові «врожаї».
До слова займаються цим не тільки вендори ОС як Google та Apple, а і вендори заліза. Важко знайти на ринку телефон на якому б не було попередньо встановлено купу bloatware програм, які вам не потрібні, які ви не збираєтесь використовувати, але OEM вважає інакше і тому він забороняє вам їх видаляти. Багато років тому, Річард Сталман казав щось, на кшталт, «якщо ви не контролюєте свій комп’ютер, то це означає, що ваш комп’ютер контролює вас». І це, як ніколи, актуально для світу сучасних смартфонів.
І що ж цим робити? Які у нас є варіанти?
Ну по-перше існують вільні дистрибутиви Android. Наприклад LineageOS (про нього ми ще згадаємо далі) — хороший варіант бодай часткового «звільнення» від диктатури корпорацій, але так чи інакше це Android зі своїми обмеженнями і зав`язкою на екосистему.
З іншого боку, є Linux, який успішно працює багато років на десктопах, серверах, прасках і чайниках. І під капотом Android-а теж Linux. Виникає питання — то чому він не може так само успішно працювати на телефоні? Давайте розбиратись.
Кілька років тому, коли я вперше придбав Linux телефон, у мене було deja vu. Мені пригадався початок
Hardware
Сьогодні нашим піддослідним буде PinePhone. Телефон вартістю $150 від Pine64.

PinePhone
Телефон має наступну специфікацію:
- Allwinner A64 Quad-Core ARM Cortex-A53 CPU;
- 3GB LPDDR3 SDRAM;
- 32GB eMMC;
- Display 5.95 inches, 1440×720;
- Back Camera: Single 5MP, 1/4″, LED Flash;
- Front Camera: Single 2MP, f/2.8, 1/5«;
- Modem: Quectel EG25-G;
- WLAN: Wi-Fi 802.11 b/g/n, single-band, hotspot;
- Bluetooth: 4.0, A2DP;
- GNSS: GPS/GLONASS/BeiDou/Galileo/QZSS, with A-GPS.
Також нам знадобиться microSD розміром 32GB або більше.
В цій статті ми спробуємо розібратись як встановити на нього Gentoo, які є можливості тюнінгу, оптимізації продуктивності тощо.
Хтось зараз думає: «Чекай, Gentoo? Це той дистрибутив в якому компілюють все що рухається, а те що не рухається — пересувають і компілюють? Він що збирається компілювати Linux на телефоні? Певно, автор збожеволів». Без паніки, компілювати звісно будемо, але не обов’язково на телефоні. «Чому Gentoo якщо є повно дистрибутивів з готовими bootable images?» Справа в тому, що Gentoo це дистрибутив від розробників і для розробників. На ньому максимально просто робити речі, які до тебе ще ніхто не робив, збирати те що потрібно саме тобі, а не залежати від того, чи хтось вже зерелізив свіжу версію ядра чи якоїсь утиліти яка тобі потрібна. На етапі коли ти працюєш з якоюсь поки не мейнстрімною річчю (а Linux на телефоні це поки не мейнстрім, на жаль) дуже часто стикаєшся з тим, що готові оновлення виходять не часто, нерідко щось виходить поламаним і його потрібно одразу патчити, і Gentoo це дуже зручний інструмент для таких кейсів.
До слова, yocto проєкт для побудови embedded Linux дистрибутивів, використовує BitBake як білд систему, яка, в свою чергу, використовує багато ідей взятих саме від білд системи Gentoo — Portage. А як щодо контріб’юшена в спільну справу? В Gentoo це робиться в кліки команди. До того ж, Gentoo це не страшно, коли є під рукою інший Gentoo. Саме так — ці пінгвіни вміють працювати в команді і допомагати один одному.
Загалом PinePhone це цікавий девайс ще й тим, що в нього є вбудований порт розширення, а відтак, він дозволяє відносно легко підключати зовнішню периферію, як от зовнішня фізична клавіатура, модуль LoRa, і т.п. Тобто, на нього можна дивитись не як на класичний телефон, а як на звичайний одноплатник (наприклад RaspberryPi) тільки з вбудованою батареєю, GSM/GPS модулем, екраном, звуком і т.п. Тобто він може бути цікавий для більш вузьких задач, а не тільки як телефон для широкого вжитку. Так, залізо на ньому вже не дуже свіже, але подивимось, що можна з цього витиснути.
Пару слів про команду пінгвінів, які нам будуть допомагати:
Gentoo-старший:
- Arch: amd64;
- CPU: i5-1245U (12 threads);
- RAM: 32GB.
Gentoo-молодший:
- Arch: aarch64;
- CPU: BCM2711 (4 threads);
- RAM: 8GB.
Старшого звуть oster, молодшого — oak. Мені подобається ця старомодна традиція давати імена комп’ютерам. Є в ній щось таке особливе, ставлення до комп’ютера як до живої істоти, чи що? Зараз, на жаль, мало хто заморочується над такими речами. Зараз частіше зустрічаєш щось типу «ГалінМак» чи «ДімінДелл», або «db-server-1» чи «app-server-2». Здається, раніше IT-шники більш креативно ставились до своєї професії. Кожен був трошки «Котляревським» який писав свою «Енеїду». Пригадую старі часи, коли інтернет ще тільки народжувався на українських просторах, в чернігівському політесі, адміни налаштовуючи шлюз для виходу в Світ добряче намучились з установкою драйверів для мережевої карти, ну і назвали ту машину «bitch». І нам, студентам, було весело бачити «bitch» одним із хопів на трейсрауті в Світ. В цьому сенсі, мене зараз радують «цікаві» назви українських дронів перехоплювачів. Творчий підхід. Молодці. Але, повернімось до справи...
Oster — це звичайний laptop який ми будемо використовувати для більшості задач по підготовці, кросс-компіляції пакетів, завантажувача тощо.
Oak — це RaspberryPi 4 одноплатник, тієї ж архітектури що і PinePhone, а тому його можна буде використовувати для нативної компіляції деяких пакетів, де це буде виправдано. Він має потужніший процесор і значно більше пам’яті ніж наш PinePhone.
Також нам може знадобитись USB-to-TTL конвертор і розпаяний 3.5мм audio jack. В PinePhone є можливість перетворити аудіо гніздо на послідовний порт шляхом перемикання одного із діп-свічів. Це може бути дуже помічним при відлагодженні ранніх етапів завантаження системи.

USB-to-TTL конвертер для підключення до послідовного порту PinePhone
Software
Давайте спершу розберемось які існують варіанти графічних середовищ в Linux для телефонів. Загалом їх існує доволі багато, на різних стадіях розробки, підтримки і наявного функціоналу. Мені пощастило «погратись» з наступними з них:
- Phosh (PhoneShell). Це GTK-based оболонка. Дуже подібна на GNOME. Оригінально розроблялась для Librem5 від Purism.
- Plasma Mobile. Це QT-based оболонка від KDE розробників. По факту, це KDE «запиляний» для телефонів.
- SXMO (SimpleXMobile). Мінімалістичний UI для телефонів написаний переважно на bash!
Phosh і Plasma Mobile дуже подібні до GNOME та KDE відповідно. З усіма своїми плюсами і мінусами. Phosh прагне бути строгим, мінімалістичним, але так щоб все працювало. Plasma Mobile — це KDE з купою налаштувань, різними «красотами», але не завжди все працює... ну як завжди. Практика використання їх на PinePhone показує, що найбільша проблема тут стабільність роботи. Обидва середовища жеруть пам’ять так, що аж гай шумить. А у нас всього 3GB. Як результат — за
Це, до речі, цікавий парадокс, який я зустрічав не раз в житті: розпочати з чистого листа ефективніше, ніж намагатись «налаштувати Франкенштейна». KDE (як власне і GNOME) маючи величезну спільноту розробників, величезний досвід, не здатні «викроїти» свій продукт таким чином, щоб він стабільно працював на залізі з обмеженими ресурсами, а от «півтора розробники» SXMO за пару років написали річ яка just works.
SXMO це доволі цікавий «звір» зі специфічним UX як для користувача який звик до сучасних смартфонів на Android чи iOS. Він використовує tiling window manager, підтримує два протоколи: старий Xorg і більш сучасний wayland. Повсюдно використовується меню, по якому, в тому числі, можна переміщатись за допомогою кнопок гучності (як виявляється, це дуже зручно, але треба звикнути). Активно використовується і підтримується в postmarketos.
Тож в нашому експерименті ми будемо використовувати саме SXMO з wayland під капотом.
Також потрібно визначитись з init системою, яку будем використовувати. В Gentoo є підтримка:
- OpenRC. init система від Gentoo. Проста, зрозуміла, надійна. Схожа на мікросервісну архітектуру. Дефолтна система для Gentoo.
- systemd. Широковідома, безальтернативна для багатьох дистрибутивів Linux, але при цьому складніша, більше схожа на «моноліт» який як «чорна діра» намагається затягнути в себе все що лежить навколо.
Ми будем використовувати OpenRC.
Підготовка build машин
Gentoo це source-based дистрибутив, а це значить що всі (майже всі) пакети, включаючи ядро, компілюються з вихідного коду. Серцем Gentoo є Portage — менеджер пакетів, який, власне, і забезпечує весь процес, від завантаження вихідного коду пакета, його компіляції і інсталювання. Цей процес може бути ресурсоємним і займати багато часу, а надто коли мова йде про системи з обмеженими ресурсами.
Але на щастя, існує ряд інструментів і підходів які дозволяють оптимізувати і значно пришвидшити цей процес. Ось деякі з них:
- Binary packages. Існує опція сконфігурувати portage так, щоб під час збирання пакету також створювалась бінарна версія цього пакету, яка може бути пізніше інстальована на цьому ж, або іншому Gentoo комп’ютері. Але тут є важливий нюанс, інший Gentoo комп’ютер повинен мати таку ж саму архітектуру і, по-можливості, той самий набір USE-флагів (опцій конкретного пакета) для успішної інсталяції.
- Crossdev — набір утиліт для portage які забезпечують кросс-компіляцію пакетів. Кросс-компіляція — це процес компіляції пакета чи програми для певної архітектури процесора відмінної від тієї на якій робиться сама компіляція. В Gentoo є поняття host і target системи. Host — система на якій відбувається компіляція. Target — система для якої компілюється пакет. Відповідно, якщо
host != targetце і є кросс-компіляція. - QEMU — емулятор різного «заліза» в тому числі aarch64. Далеко не всі пакети можуть бути кросс-компільовані. Тому для таких випадків будемо використовувати «план Б» і збирати їх через емулятор. З точку зору використання ресурсів це не дуже ефективний варіант, компіляція в емуляторі доволі повільна, але інколи без цього не обійтись.
- Distcc — розподілене компілювання пакетів. Існує можливість розподілити навантаження по різних комп’ютерах в мережі і тим самим пришвидшити процес. Але, оскільки, у нас немає великої кількості комп’ютерів, до того ж, маємо справу з різними архітектурами, тому цей підхід використовувати не будемо.
Отже, розберемось з архітектурою:
- pinephone — aarch64, cortex-A53;
- oak — aarch64, cortex-A72;
- oster — x86_64 (amd64 в Gentoo).
Підготовка oak (aarch64)
Для oak у нас немає необхідності використовувати кросс-компіляцію, лише потрібно переконатись, що CFALGS є такими, що задовільнять обидва типи процесорів. В нашому випадку для GCC ми будемо використовувати наступні флаги: -O2 -pipe -fomit-frame-pointer -march=armv8-a+crc -mtune=cortex-a53. Також включимо збірку бінарних пакетів в portgate. Результуючий make.conf буде виглядати якось так:
ROOT=/
PORTDIR=${ROOT}var/db/repos/gentoo
PKGDIR=${ROOT}var/cache/binpkgs
DISTDIR=${ROOT}var/cache/distfiles
PORTAGE_TMPDIR=/var/tmp/
ACCEPT_KEYWORDS="~arm64"
COMMON_FLAGS="-O2 -pipe -fomit-frame-pointer -march=armv8-a+crc -mtune=cortex-a53"
CFLAGS="${COMMON_FLAGS}"
CXXFLAGS="${COMMON_FLAGS}"
FCFLAGS="${COMMON_FLAGS}"
FFLAGS="${COMMON_FLAGS}"
CXXFLAGS="${CFLAGS}"
MAKEOPTS="-j5 -l4"
EMERGE_DEFAULT_OPTS="--jobs=1 --load-average=4 --autounmask-write"
VIDEO_CARDS="fbdev vc4 v3d"
FEATURES="${FEATURES} buildpkg"
BINPKG_FORMAT="gpkg"
FEATURES="${FEATURES} getbinpkg"
PORTAGE_BINHOST="http://oster/aarch64-packages"
Для того щоб oak міг виступати «донором» бінарних пакетів для телефона, необхідно налаштувати доступ до них, наприклад, по http. Встановимо http сервер і розшаримо бінарні пакети:
(oak)# emerge www-servers/lighttpd
Додамо в /etc/lighttpd/lighttpd.conf
server.modules += ( "mod_alias" ) alias.url = ( "/aarch64-packages" => "/var/cache/binpkgs" ) dir-listing.activate = "enable"
і запустимо сервер:
(oak)# /etc/init.d/lighttpd start
Підготовка oster (amd64)
Oster має іншу архітектуру, тому нам необхідно спочатку ініціалізувати середовище для кросс-компіляції під aarch64 архітектуру:
(oster)# emerge sys-devel/crossdev (oster)# crossdev --target aarch64-unknown-linux-gnu
(додатково необхідно створити crossdev overlay. Див. док.)
В результаті ми маємо отримати копію rootfs для aarch64 в /usr/aarch64-unknown-linux-gnu/, а також GNU toolchain (binutils + gcc + glibc) для крос-компіляції. Також, нам може знадобитись clang/LLVM для збірки деяких пакетів, тому корисно буде перезібрати (за необхідності) llvm-core/clang, llvm-core/llvm з USE=llvm_targets_AArch64.
Додатково, збираємо qemu, це дозволить нам робити chroot в наш target environment і працювати там через емулятор, коли буде така потреба.
Як і у випадку з oak, налаштуємо збірку бінарних пакетів для нашого кросс-середовища. Це, власне, і є головна мета цього сетапу — продукувати бінарні пакети для відповідної архітектури, які буде використовувати PinePhone.
В результаті, /usr/aarch64-unknown-linux-gnu/etc/portage/make.conf буде мати наступний вигляд:
CBUILD=x86_64-pc-linux-gnu
ROOT=/usr/aarch64-unknown-linux-gnu/
PKGDIR=/usr/aarch64-unknown-linux-gnu/var/cache/binpkgs/
PORTAGE_TMPDIR=/var/tmp/
ACCEPT_KEYWORDS="~arm64"
COMMON_FLAGS="-O2 -pipe -fomit-frame-pointer -march=armv8-a+crc -mtune=cortex-a53"
CFLAGS="${COMMON_FLAGS}"
CXXFLAGS="${COMMON_FLAGS}"
FCFLAGS="${COMMON_FLAGS}"
FFLAGS="${COMMON_FLAGS}"
CXXFLAGS="${CFLAGS}"
MAKEOPTS="-j12"
EMERGE_DEFAULT_OPTS="--jobs=1 --load-average=4 --autounmask-write"
CPU_FLAGS_ARM="edsp neon thumb vfp vfpv3 vfpv4 vfp-d32 aes sha1 sha2 crc32 v4 v5 v6 v7 v8 thumb2"
VIDEO_CARDS="lima"
USE="${ARCH} wayland gles2 gles2-only -vulkan -gnome -kde -jumbo-build -introspection -vala"
LLVM_TARGETS="AArch64"
FEATURES="-collision-protect -pid-sandbox -network-sandbox sandbox"
FEATURES="${FEATURES} buildpkg"
BINPKG_FORMAT="gpkg"
FEATURES="${FEATURES} getbinpkg"
PORTAGE_BINHOST=""
PORTAGE_BINHOST="${PORTAGE_BINHOST} http://oak/aarch64-packages"
Налаштування lighttpd для oster буде мати вигляд:
server.modules += ( "mod_alias" ) alias.url = ( "/aarch64-packages" => "/usr/aarch64-unknown-linux-gnu/var/cache/binpkgs" ) dir-listing.activate = "enable"
Як згадувалось вище, ми будем працювати над доволі свіжими речами, а тому деяких пакетів може поки не бути в офіційному репозиторії Gentoo, тому нам знадобляться додаткові overlay-ї, а також власний overlay на випадок, коли потрібного пакета поки нема в наявних overlay-ах і його потрібно буде створити самостійно.
Отже oster буде мати наступний список overlay-їв, сконфігурований в /etc/portage/repos.conf/eselect-repo.conf:
[oster] location = /var/db/repos/oster [crossdev] location = /var/db/repos/crossdev priority = 10 masters = gentoo auto-sync = no [guru] location = /var/db/repos/guru sync-type = git sync-uri = https://anongit.gentoo.org/git/repo/proj/guru.git [robertgzr] location = /var/db/repos/robertgzr sync-type = git sync-uri = https://git.sr.ht/~robertgzr/portage [vhahara] location = /var/db/repos/vhahara sync-type = git sync-uri = https://codeberg.org/vhahara/gentooverlay.git auto-sync = true
Аналогічний сетап має бути і для target середовища в /usr/aarch64-unknown-linux-gnu/etc/portage/repos.conf/eselect-repo.conf
Oak має теж саме, але без локального
osterі, звісно, безcrossdev. PinePhone буде мати цей самий конфіг, але про це трохи згодом.
Тепер у нас є aarch64-unknown-linux-gnu-emerge який ми можемо використовувати для компіляції пакетів для нашого target environment. Тож, можемо спробувати зібрати базовий набір пакетів:
(oster)# aarch64-unknown-linux-gnu-emerge -uva --keep-going @system
Тут ми можемо бачити, що деякі пакети не збираються. Причини можуть бути різні. Від помилок/недоопрацювань в ebuild файлах і до того, що деякі пакети не можуть бути зібрані в cross-environment в принципі. В першому випадку ми можемо спробувати полагодити ebuild самостійно, або відкрити bug report. В деяких випадках пакет може не збиратись через наявність «проблемної опції». Наприклад gobject-introspection. Для деяких пакетів вона не є обов’язковою, тому її можна відключити через USE=-introspection. За умовчанням ця опція відключена в make.conf і ми будем її включати лише для тих пакетів, де вона дійсно потрібна. Це відома проблема, над нею працюють в Gentoo.
Ну і для всіх вищенаведених випадків існує silver bullet у вигляді qemu емулятора. Працює це наступним чином:
- В host середовищі має бути запущений
qemu-binfmtсервіс
(oster)# /etc/init.d/qemu-binfmt start
- target середовище повинно мати відповідний
qemu-aarch64бінарник
(oster)# cp /usr/bin/qemu-aarch64 /usr/aarch64-unknown-linux-gnu/usr/bin
- Наша задача — зробити chroot в target середовище, але щоб мати повноцінну live систему після chroot потрібно:
- Змонтувати необхідні файлові системи, portage repository, etc:
(oster)# cd /usr/aarch64-unknown-linux-gnu (oster)# mount -t proc none proc (oster)# mount -o bind /dev dev (oster)# mount -o bind /sys sys (oster)# mount -o bind /var/db/repos var/db/repos (oster)# mount -o bind /lib/modules lib/modules (oster)# mount -o bind /var/tmp/portage var/tmp/portage (oster)# mount -o bind /var/cache/distfiles var/cache/distfiles
- Скопіювати актуальний
/etc/resolv.conf, щоб target мав доступ до інтернету:
(oster)# cp /etc/resolv.conf etc/resolv.conf
- Логінимось в target:
(oster)# chroot . /bin/bash --login
- Тепер, знаходячись в target середовищі, нам необхідно доконфігурувати
emerge. Це можна зробити двома способами:
- Поредагувати
/etc/portage/make.confвказавши правильні значення для chroot. Але слід пам’ятати, що цей файл в chroot середовищі, це той самий файл що і/usr/aarch64-unknown-linux-gnu/etc/portage/make.confдля target середовища при кросс-компіляції. Відповідно, одночасно кросс-компілювати і компілювати через qemu в chroot використовуючи один і той самийmake.confне вийде. Тому будем використовувати наступний варіант. - Створити alias для команди
emergeв якому перевизначити потрібні змінні:
(oster-chroot)# alias chroot-emerge="ROOT=/ PKGDIR=/var/cache/binpkgs/ CBUILD=aarch64-unknown-linux-gnu HOSTCC=aarch64-unknown-linux-gnu-gcc HOSTCXX=aarch64-unknown-linux-gnu-g++ emerge"
І тепер ми можемо збирати пакети в target chroot середовищі нативно через qemu. Наприклад:
(oster-chroot)# chroot-emerge -uva @system
Розмітка диску для PinePhone
В PinePhone завантажити операційну систему можна або із вбудованої eMMC або з окремої microSD. В нашому експерименті ми будемо встановлювати систему на microSD. Пізніше, можна буде частково, або повністю перенести її на eMMC.
Тут у нас може бути кілька підходів:
- Розмічати одразу microSD підключену до комп’ютера.
- Спочатку робити імідж в файлі, а потім, коли у нас все буде готово до першого запуску скопіювати його на microSD.
Другий варіант трохи складніший, але більш універсальний, дозволить легко робити бекап і в разі, якщо щось зламається десь на середині шляху, ми зможемо відновитись з бекапу і не починати все з самого початку.
Створимо файл в якому у нас буде повний імідж майбутньої ОС з усіма розділами і завантажувачем:
(oster)# fallocate -l 16GiB gentoo.img
Для початку нам буде цілком достатньо 16GB. Пізніше, коли будемо ставити основну масу пакетів — розширимось, але це вже буде на live системі встановленій на microSD більшого розміру.
Підключаємо його до loopback пристрою:
(oster)# losetup -f gentoo.img
Перевіряємо який саме девайс нам виділили:
(oster)# losetup NAME SIZELIMIT OFFSET AUTOCLEAR RO BACK-FILE DIO LOG-SEC /dev/loop0 0 0 0 0 /path/to/our/image/gentoo.img 0 512
Тепер все готово для розмітки диску. Використовувати будем MBR таблицю.
Загалом існує багато варіантів як саме розбивати диск, скільки саме створювати розділів в залежності від того на що саме буде орієнтована інсталяція, чи це сервер чи десктоп і т.п. Ми зупинимось на найбільш простому варіанті. У нас будуть наступні розділи:
| Розділ | Файлова система | Деталі |
|---|---|---|
| /boot | ext4 | 256MB |
| / | btrfs | Все вільне місце. Стиснення |
Btrfs це відносно нова файлова система з цікавим набором функціоналу який ми будем використовувати. А саме, вона підтримує компресію на рівні всього тому (volume) або окремих директорій і навіть файлів. Нам це може бути цікаво з тієї точки зору, що швидкість microSD невелика, а тому, теоретично, стиснувши контент на диску ми можемо «пришвидшити» читання і запис за рахунок того, що доведеться оперувати меншим об’ємом даних. Тут важливо не перестаратись з рівнем стиснення. Зависокий рівень буде створювати надлишковий тиск на процесор і це з’їсть весь профіт.
Також btrfs це COW (copy-on-write) система, і вона дозволяє доволі «дешево» робити snapshots всього тому. Це може бути корисним, якщо якийсь з експериментів піде не так, можна відновитись з бекапу.
Ще одна корисна штука btrfs це вже згадані volumes/sub-volumes. Зараз ми починаємо з простої конфігурації коли все лежить в єдиному томі для /. Пізніше, за необхідності, можна буде створити додаткові томи і змонтувати на них якісь частини файлової системи. Кожен sub-volume може мати свою квоту, а також, вже згаданий, snapshot. Наприклад, якщо у нас є якась база даних, або docker, які зазвичай пишуть в /var/lib, то можна створити sub-volume виключно на /var/lib і робити бекапи лише для нього. Для цього нам не потрібно буде заново робити розмітку диску.
На сьогодні (за умови використання свіжого ядра) btrfs підтримує 3 види компресії:
- lzo. Швидкий, але рівень стиснення невисокий.
- zlib. Повільніший ніж lzo, але має вищий рівень стиснення.
- zstd. Швидший ніж zlib і має непоганий рівень стиснення.
Ми будем використовувати zstd як найбільш оптимальний.
Отже, розмічаємо диск:
(oster)# echo -e "1M,256M,L,*\n,+,L\n" | sfdisk /dev/loop0
Зверніть увагу на те, що перший розділ починається зі зміщенням в 1М від початку. В цьому нерозміченому шматку буде розміщений завантажувач.
Диск буде виглядати якось так:
Disk /dev/loop0: 16 GiB, 17179869184 bytes, 33554432 sectors Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disklabel type: dos Disk identifier: 0x1c6f846b Device Boot Start End Sectors Size Id Type /dev/loop0p1 * 2048 526335 524288 256M 83 Linux /dev/loop0p2 526336 33554431 33028096 15.7G 83 Linux
Тепер можна створити файлові системи. Але перед цим потрібно повідомити ядру про зміну таблиці розділів на /dev/loop0:
(oster)# partprobe /dev/loop0 (oster)# ls /dev/loop0* /dev/loop0 /dev/loop0p1 /dev/loop0p2
як бачимо, з’явились окремі пристрої на кожен розділ. Далі:
(oster)# mkfs.ext4 -L boot /dev/loop0p1 (oster)# mkfs.btrfs -L rootfs /dev/loop0p2
Створюємо sub-volume для нашого / розділу:
(oster)# mkdir -p /mnt/gentoo (oster)# mount /dev/loop0p2 /mnt/gentoo (oster)# btrfs subvolume create /mnt/gentoo/rootfs-btrfs (oster)# umount /mnt/gentoo
І тепер монтуємо обидва розділи у відповідній ієрархії:
(oster)# mount -t btrfs -o subvol=rootfs-btrfs,compress=zstd:3 /dev/loop0p2 /mnt/gentoo (oster)# mkdir -p /mnt/gentoo/boot (oster)# mount /dev/loop0p1 /mnt/gentoo/boot
Зверніть увагу, що тут ми вже вказуємо алгоритм стиснення для / розділу а також рівень стиснення 3. Загалом це дефолтне значення і його можна не вказувати. zstd зі свіжим ядром підтримує рівні —15..15. Де —15..-1 — realtime з поганим рівнем стиснення; 1..3 — near-realtime з хорошим рівнем стиснення; 4..8 — повільно, але ще кращий рівень стиснення; 9..15 — спроба максимального стиснення, але вимагає додаткового процесорного часу і пам’яті.
Початкова ініціалізація системи
Керуючись стандартним Gentoo installation handbook нам необхідно завантажити stage3 tarball для нашої архітектури — aarch64 (вона ж arm64) і init системи (openrc в нашому випадку) і розпакувати його на щойно змонтовану файлову систему:
(oster)# tar -C /mnt/gentoo -xpvf stage3-*.tar.xz --xattrs-include='*.*' --numeric-owner
Тепер у нас є rootfs з мінімальним набором утиліт, бібліотек і конфігураційних файлів Gentoo на нашому іміджі, який пізніше буде скопійований на microSD картку телефона. Звідси ми можемо починати конфігурувати Portage на іміджі і робити встановлення нових пакетів.
Встановлення базових пакетів
Загалом, тут може бути багато різних підходів і «трюків» як робити це максимально ефективно. У випадку коли у нас вже є робоча система на телефоні, оптимальним підходом до її оновлення і підтримки буде варіант, коли ми налаштовуємо
FEATURES="${FEATURES} getbinpkg"
PORTAGE_BINHOST="${PORTAGE_BINHOST} http://oster/aarch64-packages http://oak/aarch64-packages"
і телефон, під час оновлення, завантажує готові пакети з наших «допоміжних» машин, де вони були попередньо зібрані. Це доволі швидкий процес і не вимагає багато ресурсів на телефоні.
Але у випадку, коли у нас ще нема робочої системи на телефоні, можна використати інший підхід. Шляхом нехитрих маніпуляцій зі змінними середовища можна сконфігурувати наш aarch64-unknown-linux-gnu-emerge і кросс-компілювати пакети безпосередньо на наш щойно змонтований імідж. Так само легко можна зробити qemu-chroot одразу на імідж, а не на /usr/aarch64-unknown-linux-gnu як ми це робили вище.
В такий спосіб, ми можемо максимально просто зібрати базовий набір пакетів на іміджі, ядро, завантажувач, залити це все на microSD і робити перші пробні запуски.
Але спочатку підготуємо наш імідж до першого запуску emerge.
Сконфігуруємо /mnt/gentoo/etc/portage/make.conf:
# CHOST=aarch64-unknown-linux-gnu
CBUILD=aarch64-unknown-linux-gnu
ROOT=/
PORTDIR=${ROOT}var/db/repos/gentoo
PKGDIR=${ROOT}var/cache/binpkgs
DISTDIR=${ROOT}var/cache/distfiles
PORTAGE_TMPDIR=/var/tmp/
ACCEPT_KEYWORDS="~arm64"
COMMON_FLAGS="-O2 -march=armv8-a+crc -mtune=cortex-a53 -pipe"
CFLAGS="${COMMON_FLAGS}"
CXXFLAGS="${COMMON_FLAGS}"
FCFLAGS="${COMMON_FLAGS}"
FFLAGS="${COMMON_FLAGS}"
# MAKEOPTS="-j4 -l4"
MAKEOPTS="-j12 -l4"
EMERGE_DEFAULT_OPTS="--jobs=1 --load-average=4 --autounmask-write"
CPU_FLAGS_ARM="edsp neon thumb vfp vfpv3 vfpv4 vfp-d32 aes sha1 sha2 crc32 v4 v5 v6 v7 v8 thumb2"
VIDEO_CARDS="lima"
USE="${ARCH} wayland gles2 gles2-only -vulkan -gnome -kde -jumbo-build -introspection -vala"
LLVM_TARGETS="AArch64"
FEATURES="-collision-protect -pid-sandbox -network-sandbox sandbox"
FEATURES="${FEATURES} buildpkg"
BINPKG_FORMAT="gpkg"
FEATURES="${FEATURES} getbinpkg"
PORTAGE_BINHOST="http://oster/aarch64-packages"
PORTAGE_BINHOST="${PORTAGE_BINHOST} http://oak/aarch64-packages"
ACCEPT_LICENSE="curl linux-fw-redistributable"
GENTOO_MIRRORS="http://gentoo.osuosl.org/ http://trumpetti.atm.tut.fi/gentoo/ http://distfiles.gentoo.org"
тут CHOST поки тримаємо закоментованим, оскільки поки ми будемо кросс-компілювати пакети з amd64 машини. Також в MAKEOPTS на даний момент використовуємо -j12 для задіювання всіх ядер/потоків amd64 машини.
Конфігуруємо репозиторії в /mnt/gentoo/etc/portage/repos.conf/eselect-repo.conf:
[vhahara] location = /mnt/gentoo/var/db/repos/vhahara sync-type = git sync-uri = https://codeberg.org/vhahara/gentooverlay.git auto-sync = true [robertgzr] location = /mnt/gentoo/var/db/repos/robertgzr sync-type = git sync-uri = https://git.sr.ht/~robertgzr/portage [guru] location = /mnt/gentoo/var/db/repos/guru sync-type = git sync-uri = https://anongit.gentoo.org/git/repo/proj/guru.git
Зверніть увагу, зараз ми вказуємо шлях до overlay-їв з префіксом
/mnt/gentooце потрібно лише для початкової ініціалізації репозиторіїв. Пізніше ми видалимо цей префікс.
Конфігуруємо таймзону:
(oster)# ln -f -s ../usr/share/zoneinfo/Europe/Kyiv /mnt/gentoo/etc/localtime
Визначимо alias який будемо використовувати для emerge-a безпосередньо в наш імідж:
(oster)# export CE_ROOT=/mnt/gentoo/
(oster)# alias crossenv-emerge="ROOT=${CE_ROOT} SYSROOT=${CE_ROOT} PORTDIR=${CE_ROOT}var/db/repos/gentoo PKGDIR=${CE_ROOT}var/cache/binpkgs DISTDIR=${CE_ROOT}var/cache/distfiles MAKEOPTS='-j12' aarch64-unknown-linux-gnu-emerge"
Синхронізуємо репозиторій та всі overlay-ї:
(oster)# crossenv-emerge --sync
Змінимо portage profile вручну:
(oster)# ln -fT -s ../../var/db/repos/gentoo/profiles/default/linux/arm64/23.0/desktop /mnt/gentoo/etc/portage/make.profile
Як згадувалось вище, ми використовуємо USE=-introspection за умовчанням, але деяким пакетам він дійсно потрібен, тому створимо /mnt/gentoo/etc/portage/package.use/introspection файл:
dev-libs/glib introspection x11-libs/gtk+ introspection dev-libs/json-glib introspection net-misc/networkmanager introspection
Запускаємо emerge базових пакетів:
(oster)# crossenv-emerge -uva --keep-going @system
Тут ми можемо бачити, що велика кількість пакетів інсталюється з готових binpkg, оскільки опція buildpkg на oak включена у мене давно, а відповідно багато бінарних пакетів вже наявна там.
Для збірки пакетів в qemu-chroot використовуємо підхід описаний вище, але chroot робимо в
/mnt/gentoo
На додачу до базових пакетів, одразу встановимо додатково:
(oster)# crossenv-emerge -va net-misc/eg25-manager net-misc/networkmanager net-misc/openssh (oster)# ln -s /etc/init.d/NetworkManager /mnt/gentoo/etc/runlevels/default (oster)# ln -s /etc/init.d/sshd /mnt/gentoo/etc/runlevels/default (oster)# ln -s /etc/init.d/eg25-manager /mnt/gentoo/etc/runlevels/default
Але перед цим виконаємо
echo "net-misc/networkmanager iwd" > /mnt/gentoo/etc/portage/package.use/networkmanager. Будемо використовувати iwd бекенд для WiFi автентифікації.
це дозволить нам одразу після першого успішного завантаження запустити sshd, налаштувати WiFi підключення, а також GSM підключення. Після цього ми зможемо підключатись до телефона по ssh через WiFi з’єднання і працювати на ньому як на звичайному Linux комп’ютері.
Відповідно, одразу створимо конфіг для нашого WiFi з’єднання в /etc/NetworkManager/system-connections/wifi.nmconnection:
[connection] id=wifi uuid=5447e887-b4fd-4994-9883-2043c14c0d92 type=wifi interface-name=wlan0 autoconnect=true [wifi] mode=infrastructure ssid=<your-ssid> [wifi-security] key-mgmt=wpa-psk psk=<your-pwd> [ipv4] method=auto [ipv6] addr-gen-mode=default method=auto [proxy]
а також GSM з’єднання в /etc/NetworkManager/system-connections/gsm.nmconnection:
[connection] id=gsm uuid=82b27a13-7aff-4768-92d1-fd14678da0ea type=gsm interface-name=cdc-wdm0 autoconnect=true [gsm] #apn=<APN> [ipv4] method=auto [ipv6] addr-gen-mode=default method=auto [proxy]
Встановимо необхідні права доступу:
(oster)# chmod 600 /mnt/gentoo/etc/NetworkManager/system-connections/{gsm,wifi}.nmconnection
Ядро
PinePhone підтримує mainline kernel, але потребує деяких патчів. Більшість з них можна знайти на codeberg.org/megi/linux. Встановлюємо вихідний код ядра в наш імідж:
(oster)# crossenv-emerge -va pinephone-sources
Вихідний код постачається з готовим базовим конфігом для PinePhone. Копіюємо базовий конфіг:
(oster)# cp /mnt/gentoo/usr/src/linux-6.19.3-pinephone/arch/arm64/configs/pinephone_defconfig /mnt/gentoo/usr/src/linux-6.19.3-pinephone/.config
Ядро Linux чудово підтримує кросс-компіляцію, тому ми можемо сміливо його збирати на oster машині, все має чудово працювати. Але для цього нам потрібно правильно задати ряд параметрів для make. Використаємо попередній підхід і створимо окремий alias який будемо використовувати для кросс-компіляції:
(oster)# export CE_ROOT=/mnt/gentoo/
(oster)# alias crossenv-aarch64-make="make ARCH=\"arm64\" CROSS_COMPILE=\"aarch64-unknown-linux-gnu-\" INSTALL_PATH=\"${CE_ROOT}boot\" INSTALL_MOD_PATH=\"${CE_ROOT}\" INSTALL_DTBS_PATH=\"${CE_ROOT}boot/dtbs\" DTC_FLAGS=\"-@\""
Запускаємо конфігурацію ядра:
(oster)# crossenv-aarch64-make menuconfig
Загалом, для першого старту, дефолтної конфігурації нам має бути достатньо. Але, оскільки ми будемо використовувати iwd в якості бекенда для NetworkManager, потрібно переконатись, що
CONFIG_CRYPTO_MD5=m CONFIG_CRYPTO_AES_TI=m
Також, для підтримки компресії в zram (про це буде далі) необхідно вибрати:
ZRAM_BACKEND_LZ4=y ZRAM_BACKEND_ZSTD=y ZRAM_DEF_COMP="lz4" ZRAM_DEF_COMP_LZ4=y
Пізніше, нам доведеться допрацьовувати конфігурацію ядра під різні задачі і, можливо, не один раз. Збирання кастомного ядра — це завжди процес ітеративний, який вимагає терпіння і уважності.
Ядро у нас буде без initramfs, а тому зараз потрібно переконатись, що драйвери для root filesystem (в нашому випадку це btrfs) будуть скомпільовані в ядро (а не як модулі).
CONFIG_BTRFS_FS=y
Запускаємо компіляцію ядра, модулів, а також device tree файлів:
(oster)# crossenv-aarch64-make -j12 Image modules dtbs
Інсталюємо все в середину нашого змонтованого іміджу:
(oster)# crossenv-aarch64-make install modules_install dtbs_install
В підсумку, ми повинні мати:
vmlinuz-<version>,config-<version>,System.map-<version>встановленими в/bootдиректорій на нашому іміджіtdbs/allwinner/*.dtbфайли так само, встановлені в/boot- модулі ядра встановлені в
/lib/modules/<version>директорій на іміджі
Завантажувач
Для PinePhone існує кілька варіантів завантажувачів:
- p-boot — завантажувач розроблений спеціально для PinePhone. Має графічний інтерфейс, меню.
- Das U-Boot — загальновідомий завантажувач, який широко використовується в багатьох embedded пристроях. Доволі простий, має гарну документацію. Будем використовувати цей варіант.
Керуючись офіційною документацією нам необхідно завантажити вихідний код u-boot, а також Arm Trusted Firmware:
git clone https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git git clone https://github.com/u-boot/u-boot
Компілюємо завантажувач:
(oster)# cd trusted-firmware-a/ (oster)# make CROSS_COMPILE=aarch64-unknown-linux-gnu- PLAT=sun50i_a64 DEBUG=1 (oster)# cd ../u-boot/ (oster)# git checkout v2026.04 (oster)# make CROSS_COMPILE=aarch64-unknown-linux-gnu- BL31=../trusted-firmware-a/build/sun50i_a64/debug/bl31.bin pinephone_defconfig (oster)# make CROSS_COMPILE=aarch64-unknown-linux-gnu- BL31=../trusted-firmware-a/build/sun50i_a64/debug/bl31.bin
Прошиваємо завантажувач в наш імідж зі зміщенням 8KB від початку диска:
(oster)# dd if=u-boot-sunxi-with-spl.bin of=/dev/loop0 bs=1024 seek=8
Тепер необхідно сконфігурувати u-boot так, щоб він знайшов наше ядро і завантажив його в пам’ять. Тут існує кілька варіантів і всі вони описані тут.
Ми будем використовувати завантаження за допомогою boot.cmd. Для цього створимо файл /mnt/gentoo/boot/boot.cmd наступного вмісту:
setenv bootargs console=ttyS0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=btrfs rootflags=subvol=rootfs-btrfs net.ifnames=0 rootwait panic=10
load mmc 0:1 0x42000000 ${bootdir}/Image
load mmc 0:1 0x44000000 ${bootdir}/dtbs/${fdtfile}
booti 0x42000000 - 0x44000000
Тут є кілька важливих моментів:
- В PinePhone microSD слот нумерується першим, а вбудована eMMC другим пристроєм. Саме тому використовуємо
mmcblk0 ${bootdir}та${fdtfile}мають бути проініціалізованіu-boot-ом- Тут ми використовуємо назву ядра
Imageхоча, після інсталяції, воно називається у насvmlinuz-<version>. Тому, створимо симлінк:
(oster)# ln -f -s vmlinuz-<version> /mnt/gentoo/boot/Image
Пізніше, коли будемо інсталювати іншу версію ядра, будемо просто редагувати це посилання, не міняючи конфіг boot.cmd
- Адреса, за якою ми завантажуємо device tree файл не повинна перекривати ядро, завантажене перед цим. Іншими словами, для нашого випадку, має виконуватись умова
0x44000000 - 0x42000000 >= <kernel_size>(в моєму випаду розмір ядра 20973576 байт) інакше device tree «перепише» ядро.
boot.cmd не використовується u-boot безпосередньо. Натомість необхідно створити u-boot image на його основі. Для цього, спочатку встановимо:
(oster)# emerge -va dev-embedded/u-boot-tools
і тепер виконаємо:
(oster)# cd /mnt/gentoo/boot (oster)# mkimage -C none -A arm -T script -d boot.cmd boot.scr
це має створити boot.scr файл, який і буде використовуватись завантажувачем.
Фінальні приготування
Створимо користувача. Для цього потрібно зробити qemu-chroot, як описувалось вище, і виконати:
(oster-chroot)# useradd -m genpp (oster-chroot)# passwd genpp (oster-chroot)# usermod -a -G wheel,uucp,audio,cdrom,video,usb,input,users,portage,plugdev genpp
Також встановимо пароль для root:
(oster-chroot)# passwd
Сконфігуруємо /mnt/gentoo/etc/fstab:
LABEL=boot /boot ext4 defaults 1 2 LABEL=rootfs / btrfs defaults,subvol=rootfs-btrfs,compress=zstd:3 0 1
Щоб мати можливість логінитись через послідовний порт під час завантаження необхідно додати в /mnt/gentoo/etc/inittab:
s0:12345:respawn:/sbin/agetty -L 115200 ttyS0 vt100
Не забуваємо поредагувати /etc/portage/repos.conf/eselect-repo.conf прописавши дійсний шлях до репозиторіїв /var/db/repos.
Відмонтовуємо імідж і заливаємо його на microSD:
(oster)# umount -R /mnt/gentoo (oster)# losetup -d /dev/loop0 (oster)# dd if=/path/to/gentoo.img of=/dev/mmcblk0 bs=1024 status=progress
Пам’ятаємо, що наш імідж розміром всього 16GB. Якщо ми хочемо мати live систему, то нам потрібна microSD розміром 32GB мінімум. Отже, заливши імідж на microSD розміром 32GB+ нам необхідно розширити / розділ, так, щоб він зайняв все вільне місце. Для цього необхідно:
- Розширити 2й розділ:
(oster)# echo ",+" | sfdisk -N 2 /dev/mmcblk0
- Збільшити розмір файлової системи:
(oster)# mount -t btrfs -o subvol=rootfs-btrfs,compress=zstd:3 /dev/mmcblk0p2 /mnt/gentoo (oster)# btrfs filesystem resize max /mnt/gentoo/ (oster)# umount /mnt/gentoo
Отже, на даному етапі у нас все готово для першого запуску системи безпосередньо та телефоні.
У наступній частині ми будем завантажувати Gentoo на PinePhone-і, підключати мережу, встановимо графічну оболонку SXMO і ряд додаткових корисних програм. Розглянемо деякі техніки тюнінгу ресурсів, ну і на завершення зробимо повноцінний bootable image із нашої системи.
4 коментарі
Додати коментар Підписатись на коментаріВідписатись від коментарівЗараз би адекватний вендор мобільної ОС не завадив би... От тільки як вирішити проблему наявності додатків...
Чекаю продовження 🔥
Погоджуюсь, писав про це у темі: «Нав’язливе шпигунське ПЗ у телефонах Samsung без можливості видалення, або як Samsung спаскудилися».
🔥🔥🔥