Делаем домашний сервер из: X88mini13

в 21:04, , рубрики: arm64, минисервер

Предыдущие железяки: MXQ 4K и сетевой видеорегистратор

А вот была еще такая интересная штука: ТВ-бокс X88mini13

Делаем домашний сервер из: X88mini13 - 1

Заявлено 4Гб RAM и 32Гб emmc, процессор RK3528 - в принципе, неплохо.
Софт опять сейчас не интересует: Андроид + обычные для приставок программы, можно устанавливать новые - словом, обычный Андроид.

Делаем домашний сервер из: X88mini13 - 2

Внутри — аккуратная плата, видны отдельно микросхемы RAM, отдельно чип на 32Гб (в центре фото черный прямоугольник), в углу платы три контакта под гребенку, serial port — вот тут он понадобится.

Скорость соединения 1 500 000 бод, смотрим:

Как обычно, проходит загрузка, видны и 4Гб RAM, и 32Гб emmc — не соврали. Процессор 3528, 64-битный — тоже хорошо.

Сразу загружается Андроид, и мы попадаем в шелл, юзером. Валятся системные сообшения от ядра, мешают, но их можно игнорировать.

Юзер в Андроиде мало что может сделать, но есть интересный лайфхак:

system/xbin/su

И мы получаем рут!

Впрочем, в Андроиде и рут достаточно ограничен в правах, но многого и не требуется - нужно сначала сделать бекап.

Подключаем SD-карту, тут желательно на 64Гб, она даже определяется и монтируется, под именем типа /mnt/media_rw/XXXXXXXXXXXXXX

Смотрим, какие есть устройства:

ls /dev/block/mmcblk*

Там будет что-то типа
mmcblk1
mmcblk1p1
mmcblk2
mmcblk2p1
mmcblk2p2
mmcblk2p3
mmcblk2p4
mmcblk2p5
mmcblk2p6
mmcblk2p7
mmcblk2p8
mmcblk2p9
mmcblk2p10
mmcblk2p11
mmcblk2p12
mmcblk2p13
mmcblk2p14
mmcblk2p15

В данном случае mmcblk1 - SD-карта, mmcblk2 - встроенная память, на ней много разделов.
Таблица разделов - GPT, у каждого свое название, если потом заглянуть в эту таблицу, а пока надо скопировать.

Бекап всего целиком сделать проще всего:

cd /mnt/media_rw/XXXXXXXXXXXXXX
dd if=/dev/block/mmcblk2 of=backup.bin

В результате на SD-карте будет создан образ всей emmc устройства, он нам еще пригодится.

А теперь - как превратить это в сервер.

Конкретно с этой моделью есть одна неявная ловушка, в которую, судя по профильным форумам, многие попались:

Дело в том, что ее можно легко запустить с рядом Armbian-прошивок, буквально просто записав их на SD-карту.
Например, Armbian_community_24.11.0-trunk.273_Hinlink-h28k_bookworm_vendor_6.1.75_minimal.img прекрасно подходит: записываем образ на карту, вставляем в девайс, перезагружаемся - и вот она, работающая система.
Остается, по советам из тех же форумов, запустить программу armbian-install, и перенести ОС на встроенную память.

И всё, вы попались. Да, устройства на базе Rockchip "окирпичить" сложно, но есть нюанс.
В частности, эта конкретная модель использует память DDR3 (не LPDDR3), и ее нужно правильно инициализировать в самом начале загрузки SPL.

Штатный SPL из прошивки сначала делает это, потом загружает U-Boot из прошивки, и только потом ищет и загружает ОС, на SD-карте или из внутренней памяти. В любом случае сначала загружается SPL из встроенной emmc.

А вот такая попытка перенести ОС приводит к перезаписи SPL/U-Boot на версию из загружаемого образа, которая совсем не обязательно умеет инициализировать DDR3.

В результате загрузка с внутренней памяти начинается нормально, но рабочей RAM еще нет, и загрузка виснет на этапе запуска U-Boot. К загрузке ОС с карты переходить некому.
Конечно, у нас есть бекап, и можно вынуть из него и записать этот SPL с U-Boot на карту - но устройство не будет пытаться стартовать с карты, ведь у него есть рабочая внутренняя память и исправный загрузчик там, правда, он память подключать не умеет...

Вот тут бы подошли советы типа закорачивания пина Clk на землю: тогда устройство бы поняло, что emmc не работает и надо загружаться с карты - но пины недоступны: emmc в BGA-чипе, все дорожки под маской, а из оставшихся доступных для закорачивания контактов для этих целей не подходит ни один, проверил все.
Получаем классический "кирпич".

Однако, способ есть, хоть и варварский: радикальная хирургия. Если с помощью паяльного фена аккуратно выпаять чип emmc с платы - вот тогда загружаться устройство будет с SD-карты. Остается только правильно отформатировать карту, перенести на нее первые 32768 секторов и создав как минимум первые два раздела точно как были в прошивке, а на оставшееся место записывать разделы boot и root Линукса.
Теряем 32Гб памяти - зато получаем рабочую систему.

Делаем домашний сервер из: X88mini13 - 3

Но если до этого не дошло - лучше пойти другим путем, и заодно собрать свою сборку ОС.

Если посмотреть на сохраненный бекап через fdisk - можно увидеть разделы на нем:

 1            8192           16383   4.0 MiB     FFFF  security
 2           16384           24575   4.0 MiB     FFFF  uboot
 3           24576           32767   4.0 MiB     FFFF  trust
 4           32768           40959   4.0 MiB     FFFF  misc
 5           40960           49151   4.0 MiB     FFFF  dtbo
 6           49152           51199   1024.0 KiB  FFFF  vbmeta
 7           51200          182271   64.0 MiB    FFFF  boot
 8          182272          378879   96.0 MiB    FFFF  recovery
 9          378880         1165311   384.0 MiB   FFFF  backup
 10         1165312         1951743   384.0 MiB   FFFF  cache
 11         1951744         1984511   16.0 MiB    FFFF  metadata
 12         1984512         1985535   512.0 KiB   FFFF  frp
 13         1985536         1987583   1024.0 KiB  FFFF  baseparameter
 14         1987584         8360959   3.0 GiB     FFFF  super
 15         8360960        61071295   25.1 GiB    FFFF  userdata

Нам нужен 7, boot:

losetup -P loopX filename
ls /dev/loopX*
dd if=/dev/loopXp7 of=p7

Если посмотреть на него в HEX - в начале видим:

00000000  41 4e 44 52 4f 49 44 21  08 f8 07 02 00 80 00 10  |ANDROID!........|
00000010  c3 ee 15 00 00 00 00 11  00 c6 56 00 00 00 f0 10  |..........V.....|
00000020  00 01 00 10 00 08 00 00  02 00 00 00 78 01 00 18  |............x...|
00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000040  63 6f 6e 73 6f 6c 65 3d  74 74 79 46 49 51 30 20  |console=ttyFIQ0 |
00000050  66 69 72 6d 77 61 72 65  5f 63 6c 61 73 73 2e 70  |firmware_class.p|
00000060  61 74 68 3d 2f 76 65 6e  64 6f 72 2f 65 74 63 2f  |ath=/vendor/etc/|
00000070  66 69 72 6d 77 61 72 65  20 69 6e 69 74 3d 2f 69  |firmware init=/i|

Это загрузочный образ ядра Андроид.
Распаковываем:

mkdir dir
unpackbootimg -i p7 -o dir

В каталоге dir появляются файлы, из которых состоит этот образ. Нам нужны отсюда два:
p7-kernel
p7-dtb

Возвращаемся к TV-box: если при перезагрузке нажать Ctrl-C - попадаем в U-Boot

printenv
arch=arm
autoload=no
baudrate=1500000
board=evb_rk3528
board_name=evb_rk3528
boot_a_script=load ${devtype} ${devnum}:${distro_bootpart} ${scriptaddr} ${prefix}${script}; source ${scriptaddr}
boot_extlinux=sysboot ${devtype} ${devnum}:${distro_bootpart} any ${scriptaddr} ${prefix}extlinux/extlinux.conf
boot_net_usb_start=usb start
boot_prefixes=/ /boot/
boot_script_dhcp=boot.scr.uimg
boot_scripts=boot.scr.uimg boot.scr
boot_targets=mmc1 mmc0 mtd2 mtd1 mtd0 usb0 pxe dhcp
bootargs=storagemedia=emmc androidboot.storagemedia=emmc androidboot.mode=normal  androidboot.dtb_idx=0 androidboot.dtbo_idx=0
bootcmd=boot_android ${devtype} ${devnum};boot_fit;bootrkp;run distro_bootcmd;
bootcmd_dhcp=run boot_net_usb_start; if dhcp ${scriptaddr} ${boot_script_dhcp}; then source ${scriptaddr}; fi;
bootcmd_mmc0=setenv devnum 0; run mmc_boot
bootcmd_mmc1=setenv devnum 1; run mmc_boot
bootcmd_mtd0=setenv devnum 0; run mtd_boot
bootcmd_mtd1=setenv devnum 1; run mtd_boot
bootcmd_mtd2=setenv devnum 2; run mtd_boot
bootcmd_pxe=run boot_net_usb_start; dhcp; if pxe get; then pxe boot; fi
bootcmd_usb0=setenv devnum 0; run usb_boot
bootdelay=0
cpu=armv8
devnum=0
devtype=mmc
distro_bootcmd=for target in ${boot_targets}; do run bootcmd_${target}; done
eth1addr=22:fa:77:25:50:71
ethaddr=30:a6:12:0f:2c:da
fdt_addr_r=0x08300000
kernel_addr_c=0x04080000
kernel_addr_r=0x00280000
mmc_boot=if mmc dev ${devnum}; then setenv devtype mmc; run scan_dev_for_boot_part; fi
partitions=uuid_disk=${uuid_gpt_disk};name=uboot,start=8MB,size=4MB,uuid=${uuid_gpt_loader2};name=trust,size=4M,uuid=${uuid_gpt_atf};name=misc,size=4MB,uuid=${uuid_gpt_misc};name=resource,size=16MB,uuid=${uuid_gpt_resource};name=kernel,size=32M,uuid=${uuid_gpt_kernel};name=boot,size=32M,bootable,uuid=${uuid_gpt_boot};name=recovery,size=32M,uuid=${uuid_gpt_recovery};name=backup,size=112M,uuid=${uuid_gpt_backup};name=cache,size=512M,uuid=${uuid_gpt_cache};name=system,size=2048M,uuid=${uuid_gpt_system};name=metadata,size=16M,uuid=${uuid_gpt_metadata};name=vendor,size=32M,uuid=${uuid_gpt_vendor};name=oem,size=32M,uuid=${uuid_gpt_oem};name=frp,size=512K,uuid=${uuid_gpt_frp};name=security,size=2M,uuid=${uuid_gpt_security};name=userdata,size=-,uuid=${uuid_gpt_userdata};
pxefile_addr_r=0x00e00000
ramdisk_addr_r=0x0a200000
rkimg_bootdev=if mmc dev 1 && rkimgtest mmc 1; then setenv devtype mmc; setenv devnum 1; echo Boot from SDcard;elif mmc dev 0; then setenv devtype mmc; setenv devnum 0;elif mtd_blk dev 0; then setenv devtype mtd; setenv devnum 0;elif mtd_blk dev 1; then setenv devtype mtd; setenv devnum 1;elif mtd_blk dev 2; then setenv devtype mtd; setenv devnum 2;elif rknand dev 0; then setenv devtype rknand; setenv devnum 0;elif rksfc dev 0; then setenv devtype spinand; setenv devnum 0;elif rksfc dev 1; then setenv devtype spinor; setenv devnum 1;else;setenv devtype ramdisk; setenv devnum 0;fi;
scan_dev_for_boot=echo Scanning ${devtype} ${devnum}:${distro_bootpart}...; for prefix in ${boot_prefixes}; do run scan_dev_for_extlinux; run scan_dev_for_scripts; done;
scan_dev_for_boot_part=part list ${devtype} ${devnum} -bootable devplist; env exists devplist || setenv devplist 1; for distro_bootpart in ${devplist}; do if fstype ${devtype} ${devnum}:${distro_bootpart} bootfstype; then run scan_dev_for_boot; fi; done
scan_dev_for_extlinux=if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}extlinux/extlinux.conf; then echo Found ${prefix}extlinux/extlinux.conf; run boot_extlinux; echo SCRIPT FAILED: continuing...; fi
scan_dev_for_scripts=for script in ${boot_scripts}; do if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}${script}; then echo Found U-Boot script ${prefix}${script}; run boot_a_script; echo SCRIPT FAILED: continuing...; fi; done
scriptaddr=0x00c00000
serial#=MN2404W1050264
soc=rockchip
stderr=vidconsole
stdin=serial
stdout=vidconsole
usb_boot=usb start; if usb dev ${devnum}; then setenv devtype usb; run scan_dev_for_boot_part; fi
vendor=rockchip

Environment size: 4064/32764 bytes

Тут целая система скриптов, определяющая порядок загрузки ОС. Если присмотреться - U-Boot ищет на доступных устройствах загрузочные разделы, а потом может запускать из них ОС, если находит extlinux/extlinux.conf.

Структура extlinux/extlinux.conf очень простая:

default x

LABEL x
      linux /XX-kernel
      initrd /XX-initrd.img.gz
      fdt /XX.dtb
      append root=/dev/XXXXX rw rootwait rootfstype=ext4

То есть нужен kernel, dtb и rootfs. Первые два есть, третий можно сделать на основе busybox.

wget -P downloads https://busybox.net/downloads/busybox-1.35.0.tar.bz2

После распаковки:

make defconfig
make LDFLAGS=-static
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- LDFLAGS=-static

Формируем ФС:

mkdir memroot
cd memroot
echo "#!/bin/sh" > init
chmod 777 init
mkdir -p bin dev proc sys
cd bin
cp ..XXX/build/busybox ./
for prog in $(./busybox --list); do ln -s /bin/busybox $prog; done

Создаем простейший init: (можно и по другому, но так проще шелл получить)

#! /bin/sh

mount -n -t devtmpfs udev /dev
mkdir -p /dev/pts
mount -n -t devpts devpts /dev/pts
mount -n -t sysfs sysfs /sys
mount -n -t proc proc /proc

/bin/sh

Упаковка:

find . | cpio -o -H newc --owner root:root > ../initrd.img
cd ../
gzip initrd.img

Теперь есть файл initrd.img.gz, можно собирать ФС для загрузки.

На SD с помощью fdisk размечаем разделы.
Первый раздел - 300Мб, второй - всё остальное.

Ищем опции и проставляем признак LegacyBoot (A) для первого раздела.
Сохраняем (w), форматируем:

mkfs.ext4 /dev/sdb1
mkfs.ext4 /dev/sdb2

mount /dev/sdb1 /mnt

Копируем в первый раздел kernel, dtb, initrd.img.gz
Создаем каталог и файл extlinux/extlinux.conf
Поправляем в нем имена, чтобы указывали на наши файлы
Строка "append ..." пока не важна, хотя можно предположить что она будет выглядеть так:

  append root=/dev/mmcblk1p2 rw rootwait rootfstype=ext4

указывая на второй раздел устройства mmcblk1 (SD-карты), который пока пустой.

В принципе этого уже почти достаточно. Если вставить карту в устройство и попробовать перезагрузиться - должно загрузиться ядро с карты, и терминал вывалится в шелл busybox.Можно проверить, под какими именами появятся устройства в /dev.
Должно быть примерно так:

ls /dev/mmcblk*

mmcblk1
mmcblk1p1
mmcblk1p2
mmcblk2
mmcblk2p1
mmcblk2p2
mmcblk2p3
mmcblk2p4
mmcblk2p5
mmcblk2p6
mmcblk2p7
mmcblk2p8
mmcblk2p9
mmcblk2p10
mmcblk2p11
mmcblk2p12
mmcblk2p13
mmcblk2p14
mmcblk2p15

Тут надо убедиться, что файл /dev/mmcblk1p2 соответствует строке root=/dev/mmcblk1p2 в extlinux.conf, и при необходимости поправить:

mkdir -p /tmp/boot
mount /dev/mmcblk1p1 /tmp/boot
vi /tmp/boot/extlinux/extlinux.conf
umount /tmp/boot

Теперь нужно собрать образ ОС, переразбить встроенную emmc и записать всё туда.
Общий принцип такой: с помощью debootstrap формируем ФС Дебиано-подобной ОС (debian, ubuntu, kali - man debootstrap):

mkdir rootfs
debootstrap --foreign --include=qemu-user-static --arch arm64 bookworm ./rootfs/
chroot ./rootfs/ /debootstrap/debootstrap --second-stage
chroot ./rootfs/ /bin/bash
passwd
apt update
apt install XXX1 XXX2 etc.
exit

Сохраняем для переноса:

cd rootfs
tar czf ../rootfs.tgz .

Теперь полученный rootfs.tgz копируем на 2 раздел SD-карты, и там же на месте распаковываем, архив не удаляем:

tar xzf rootfs.tgz

Теперь можно снова попытаться загрузиться с карты. Снова попадаем в шелл busybox, но теперь нужно перейти в созданную ОС:

mkdir -p /mnt/boot
mount /dev/mmcblk1p2 /mnt
mount /dev/mmcblk1p1 /mnt/boot

mkdir -p /mnt/dev/pts
mount -n --move /sys /mnt/sys
mount -n --move /proc /mnt/proc
mount -n --move /dev/pts /mnt/dev/pts
mount -n --move /dev /mnt/dev

chroot /mnt /bin/bash
pwd
/

Теперь остается переразбивка встроенной emmc:

fdisk /dev/mmcblk2

Убираем все разделы кроме 1 и 2, создаем 3 300Мб, 4 - всё оставшееся.
Через (x) ставим признак загружабельности (A) на 3 раздел.
Сохраняем, форматируем разделы:

mkfs.ext4 /dev/mmcblk2p3
mkfs.ext4 /dev/mmcblk2p4

Копируем содержиое 1 раздела карты в 3 emmc, поправляем строку в /extlinux/extlinux.conf

mount /dev/mmcblk2p3 /mnt
cd /mnt
tar cf - -C /boot . | tar xf -
vi extlinux/extlinux.conf

  append root=/dev/mmcblk2p4 rw rootwait rootfstype=ext4

cd /
umount /mnt

Копируем содержимое раздела 2 карты в 4 emmc (оно есть в архиве - скопируем и распакуем):

mount /dev/mmcblk2p4 /mnt
cp rootfs.tgz /mnt/
cd /mnt
tar xzf rootfs.tgz
cd /
umount /mnt

Если всё сделано правильно - после перезагрузки без SD-карты загружаемся уже с внутренней emmc и опять попадаем в шелл busybox.
Остается настроить init-скрипт. В принципе, это можно сделать на компьютере, а можно и прямо на устройстве:

Примонтировать разделы:
mkdir -p /mnt/boot
mount /dev/mmcblk2p4 /mnt
mount /dev/mmcblk2p3 /mnt/boot

Перейти в Дебиан:
mkdir -p /mnt/dev/pts
mount -n --move /sys /mnt/sys
mount -n --move /proc /mnt/proc
mount -n --move /dev/pts /mnt/dev/pts
mount -n --move /dev /mnt/dev

chroot /mnt /bin/bash

Убедиться, что установлена cpio, или доустановить ее

cd /boot
mkdir dir
gunzup initrd.img.gz
cd dir
cpio -iv < ../initrd.img

Поправить init скрипт:

#! /bin/sh

mount -n -t devtmpfs udev /dev
mkdir -p /dev/pts
mount -n -t devpts devpts /dev/pts
mount -n -t sysfs sysfs /sys
mount -n -t proc proc /proc

ROOT_DEV=/dev/mmcblk2p4
BOOT_DEV=/dev/mmcblk2p3

while [ ! -b $ROOT_DEV ] ; do
  sleep 1
done

mkdir -p /mnt/boot
mount $ROOT_DEV /mnt
mount $BOOT_DEV /mnt/boot

sleep 1

cd /mnt
exec switch_root . "/sbin/init" "$@"

Запаковываем обратно:

rm ../initrd.img
find . | cpio -o -H newc --owner root:root > ../initrd.img
cd ../
gzip initrd.img

Ах да, и не забыть проверить, что установлен пароль root, а лучше добавить пользователя и настроить sudo.

После очередной перезагрузки в терминале будет уже не шелл, а запрос логина и пароля.
В принципе, ОС уже установлена, остались нюансы типа настройки dhcp на сетевом интерфейсе, установки даты, и желательно — сервера ntp, хотя можно обойтись и запросами ntpdate.

С неправильной датой система пакетов apt не работает, так что возможно первоначально время придется поставить вручную.

Конечно, можно спросить, зачем вообще заморачиваться с вытаскиванием родного ядра и сборкой ОС, если есть работающие сборки Armbian?

Ну хотя бы для того чтобы попробовать собрать ОС, не используя сборки, чтобы повторить это потом при необходимости в других случаях, и не убить при этом загрузчик.
А родное ядро, помимо всего прочего, имеет и родной интерфейс к аппаратному криптомодулю чипа, устройство /dev/crypto, которое тоже может на что‑то сгодиться.

На выходе — работающий мини‑сервер 4/32 Гб, 64-битный Дебиан.

Автор: JBFW

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js