DIY 完全可用的 Alpine Linux,适用于 Allwinner 和其他 ARM SOC

来自 Alpine Linux
此材料需要 Wiki 语法或样式改进...

请随时帮助我们清理它。

此页面建议移动...

它应该重命名为 ARM SoCs。 (讨论)

此材料已过时...

这篇文章需要更新以涵盖支持的 Alpine 版本。 看起来它是为比 3.10.9 更旧的版本编写的 (讨论)

这个方法可行 - 已测试

请随时通过 atlury@gmail.com 或 oneinsect@gmail.com 联系我。

主线内核与主线 U-Boot

序言

  • 请注意,以下大部分内容是针对 NanoPi M1 的,但通过理解可以扩展到任何 ARM Soc
  • NanoPi M1 和 Orange Pi PC 非常相似,除了 Orange Pi PC 有多 512MB 的 RAM。 它们具有相似的规格(USB、以太网、I/O 等)。

有时这些 SOC 始终以恒定的 1.3V 供电,例如 NanoPi M1 或 Orange PI One。 但是,Orange Pi PC 具有可编程电压发生器,可以降低电压。

一个原始的稳压器始终以 1.3V 为 H3 供电在某些板子上。 在这种模式下,节流相当低效,因为仅当降低时钟速度时,温度不会降低太多。 因此,除非您使用大型散热片和风扇,否则预计会出现严重的性能问题。

有时,如果您的板子具有固定电压调节器,您必须修改您的 dts 文件以限制温度并防止在您不使用散热器时烧毁板子。 但是,我建议您为 H3 板使用某种被动/主动冷却。

在撰写本文时,NanoPi M1 仍然没有自己的 .dtb 文件,因此我们使用了类似板子的 .dtb 文件(Orange Pi PC)。

您可以随意尝试旧内核。 该过程可能会略有不同,请参阅 linux-sunxi.org 获取更多信息和链接。

您的 SOC 可能不受主线内核支持。 但是,您仍然可以使用 Alpine Linux。

很多人都在编写 .dtb 文件。 例如,以下是基于 H3 的具有固定电压调节器的板卡被动散热的主线内核 dts 配置

https://github.com/megous/linux/wiki/Fixed-voltage-regulator-test

为 Orange Pi One 编写。 将其视为具有固定电压调节器,并在压力测试期间将温度限制在 ~75°C。 您可以随意尝试。 互联网是巨大的。 在 IRC 频道闲逛。

对于有时使用 DVI 转 HDMI 转换器等时显示不出现的问题,有一个解决方法。 建议您使用 VGA 转 HDMI 或调整 boot.scr 和其他文件的设置。

我们预计随着 Alpine Linux 内核版本的进一步发展,您将能够直接使用 Alpine Linux,而无需重新编译您自己的内核、initramfs 文件等

综合介绍

当有这么多 xyz 发行版可用时,我们为什么需要 Alpine Linux? 好吧,它是最轻量级的平台之一,具有 SD 卡和 USB 设备的热插拔支持。 可以选择分配 SD 卡可用空间用于应用程序存储。

有可能完全升级远程运行的设备,并且停机时间最短。 可以使用 USB 空调制解调器进行控制台登录,以便进行现场维护。 最重要的是,您可以放心,它可以经受住断电和重启。 对于没有板载 mmc 的设备,尤其是在 sd 卡上工作的设备,这一点更为重要。

仅在需要时提交的理念,使整个操作系统保持只读和内存中状态,而根本不触及存储。 因此,设备可以长时间运行而不会崩溃。

与 x86 不同,Arm 设备通常不带 bios。 x86 PC 中的 BIOS 通常是一个固件,用于配置硬件并将硬件连接到操作系统,它为各种操作系统提供支持,并支持新的操作系统版本。

ARM 使用不同的方法,涉及引导加载程序进行硬件配置和操作系统启动。 引导加载程序是专门为应用程序开发的,适用于一个明确定义的硬件 SOC 配置、一个操作系统且只有一个版本,这意味着您可能无法将其用于其他 SOC,除非进行重大更改。

我们在这里通常讨论 Allwinner,尤其是 H3 SOC,但您也可以将这种理念应用于其他 SOC。 nanopi m1、Orange pi pc 和 Orange pi lite 仅需约 10 美元,使其成为性价比最高的家庭服务器、防火墙。

Linux 操作系统不能像在 ARM 设备上那样直接启动,而无需少量机器特定的代码来初始化该系统。

ARM 设备启动过程通常涉及 4 个阶段

  • (阶段 1)ROM - 从初始化的持久存储器(由启动模式选择)读取,将 SPL 加载到内部 Ram
  • (阶段 2)SPL 加载器 - SPL 加载后会进行额外的设置,并将持久存储器引导加载程序 (u-boot) 加载到 DDR RAM 中
  • (阶段 3)U-boot - U-boot 加载后继续处理器设置,并将 Linux 内核读取到 DDR RAM 中
  • (阶段 4)内核 - 内核加载后,它会启动 Linux 并初始化用户运行时环境

启动过程的第一阶段是二级程序加载器 (SPL)。 这段初步代码负责板级初始化、加载 u-boot 二进制文件(“二级程序”)并将控制流移交给 u-boot 主程序。 它是设备特定的,通常由 SoC 供应商以闭源二进制 blob 的形式提供。

二级程序加载器 (SPL) 和 u-boot 二进制文件驻留在特殊的板载闪存区域或 uSD/eMMC 卡的第一个扇区。 Arm 设备使用 microSD 或 eMMC 模块来存储 SPL 和 u-boot 二进制文件

在启动过程的第二阶段,执行 u-boot 主程序。 U-boot 首先在 microSD 或 eMMC 模块上的保留空间中查找自定义环境,如果需要,则回退到编译时默认环境。 此时,您可以通过在串行控制台上按键来中断自动启动过程,这将启动一个交互式 u-boot shell。 称为 bootdelay 的 u-boot 变量指定在继续自动启动之前等待按键的秒数。

自动启动过程执行一个特殊的 u-boot 宏,称为 bootcmd,它加载并执行以下过程

1. (opt.) a custom u-boot environment: uEnv.txt
2. (opt.) a precompiled u-boot macro: boot.scr
3. the kernel image, e.g. vmlinuz
4. (opt.) the device tree binary, e. g. .dtb
5. (opt.) the initial ramdisk, e. g. initramfs

第三阶段是加载 Linux 内核。 但是,在 Linux 内核接管控制之前,u-boot 会将包含基本参数的命令行传递给内核。

例如,在 u-boot 中,您可以设置 bootargs 以将其传递给内核一些参数

  • setenv bootargs /boot/vmlinuz-4.6.0-rc1-sunxi earlyprintk modules=loop,squashfs,sd-mod,usb-storage modloop=/boot/modloop-sunxi console=${console} rootwait panic=10

参数可以在操作系统启动后通过在终端窗口中键入以下内容来查看(仅示例)

  • cat /proc/cmdline root=/dev/mmcblk0p2 rootwait rw console=ttyS0,115200n8 console=tty0 no_console_suspend vdaccfg=0xa00 logo=osd1,loaded,0x7900000,720p,full dmfc=3 cvbsmode=576cvbs hdmimode=1080p m_bpp=32 vout=hdmi disablehpd=true

内核初始化硬件,挂载根文件系统(根据 root=.. 内核参数),并将控制流传递给 /sbin/init。

Linux 内核由以下组件组成

  • 内核镜像 32 位平台: <boot-partition>/zImage 或 <boot-partition>/uImage,取决于您的 u-boot 的功能和配置
  • 内核镜像 64 位平台: <boot-partition>/Image
  • 设备树二进制文件,一个低级设备描述,特定于您的设备 (<boot-partition>/<board>.dtb)
  • 内核模块 (/lib/modules/<kernel-version>/*)
  • 设备固件 (/lib/firmware/*)
  • 内核 C 头文件 (/usr/include/linux)

这些组件是借助 make 实用程序从内核源代码构建出来的。 通常,内核镜像和设备树二进制文件是从一个小的 vfat 启动分区(挂载为 /boot 或 /media/boot)加载的,而其余组件则驻留在根文件系统中。

但是对于我们这里的情况,我们将有一个原始的 u-boot 扇区,后跟一个大的 fat 分区

每个阶段都增加了功能。 重要的是要注意,SPL、U-Boot 和 Linux 内核都静态链接到从 CPU 定义的内存映射中的特定位置开始运行。 该内存映射只不过是一个布局,它定义了内部存储器和 DDR 的映射位置。

在这里,我们关注 SD 卡,并且根据 SD 卡的连接方式,写入数据的位置可能会有所不同。

好的,等等!!! 写入数据???

不要感到困惑,是的,您将需要从源代码编译 SPL 加载器并以特定方式将其写入 SD 卡,然后从源代码编译 u-boot 并以特定方式写入它,其余部分也是如此。 否则,您将如何启动我们最喜欢的 Alpine Linux?

但是您从哪里获得源代码? 通常,如果 Linux 支持 SOC(称为主线),您可以直接使用最新的内核,否则您需要从 SOC 制造商那里获取源代码。

以下是典型 Allwinner 的 SD 卡布局,它通常将 U-boot 与从块 8 开始的 SPL 加载器结合在一起。 然后加载 initramfs 等。

“start” 在这里是 1k 块号。 (将其乘以 2 以获得相应的扇区号 - 假设 512 字节扇区)。 此布局通常适用于 Linux 主线内核 4.x 及更高版本

起始 大小 用途
0 8KB 未使用,可用于分区表
8 1024KB 初始 SPL 加载器 + u-boot(只需使用 dd 烧录)
1024 直到结尾 vmlinuz + initramfs + modloop + dtb + Alpine apks 文件夹(格式 vfat + 启用启动标志,首选从 2048 开始)

请注意,通常第三个 fat 分区是从 2048 到结尾

请注意,在上面讨论时,我告诉过通常有四个阶段,实际上我已将 SPL 与 U-boot 结合并显示为一个阶段; 实际上,现在可以生成一个单独的文件,例如编译时的 “u-boot-sunxi-with-spl.bin”

是的,您没有看错,保存 vmlinuz eta al 的分区实际上是 fat 分区,我们不会有任何符号链接等问题。 是的,这不是您典型的 rootfs。

其思想是 Alpinelinux 完美地加载并将整个操作系统压缩到内存中并在那里维护它,因此符号链接问题等方面永远不会出现,因为 fat 分区永远不会被触及,并且每当发生任何更改时,并且仅当它们被提交时,它们才保存在预压缩的 tar.gz 存档中,保存然后在下次启动期间动态加载回来

引导加载程序,又名 U-Boot

让我们开始吧。 首先要理解 U-boot。

像 Alpine Linux 这样的发行版,就此而言,任何 Linux 都不需要操作任何类型的引导加载程序特定配置数据来指示系统应从哪个存储设备启动。

发行版只需要将启动配置文件复制到 ext2/3/4 或 FAT 分区中,将该分区标记为可启动(通过 MBR 可启动标志或 GPT legacy_bios_bootable 属性),并且 U-Boot(或任何其他引导加载程序)将找到这些启动文件并执行它们。 这在概念上与在台式 PC 上创建 grub2 配置文件相同。

请注意,在没有任何显式标记为可启动的分区的情况下,U-Boot 会回退到搜索磁盘的第一个有效分区以查找启动配置文件。 建议其他引导加载程序也这样做,因为我认为分区表可启动标志在 x86 PC 领域之外并不常用。

U-Boot 还可以从 TFTP 服务器搜索启动配置文件。 启动配置文件的标准格式是 extlinux.conf 的格式,由 U-Boot 的 “syslinux”(磁盘)或 “pxe boot”(网络)处理。

U-Boot 在磁盘上搜索 /extlinux/extlinux.conf,然后搜索 /boot/extlinux/extlinux.conf,或通过网络搜索 pxelinux.cfg/default。

Alpine Linux 安装程序生成的一个 extlinux.conf 示例是

 LABEL grsec
 MENU DEFAULT
 MENU LABEL Linux grsec
 LINUX /boot/vmlinuz-grsec
 INITRD /boot/initramfs-grsec
 DEVICETREEDIR /boot/dtbs
 APPEND BOOT_IMAGE=/boot/vmlinuz-grsec modules=loop,squashfs,sd-mod,usb-storage modloop=/boot/modloop-grsec console=${console}

另一个 extlinux.conf 示例是

 LABEL Linux Mailine 4.6RC2
 LINUX /boot/vmlinuz-4.6.0-rc1-sunxi
 INITRD /boot/initramfs-new.uImage
 FDT /boot/sun8i-h3-orangepi-pc.dtb
 APPEND BOOT_IMAGE=/boot/vmlinuz-4.6.0-rc1-sunxi modules=loop,squashfs,sd-mod,usb-storage modloop=/boot/modloop-sunxi console=${console}

要编译什么

您基本上在寻找什么? 您需要编译哪些文件,如何以及从哪里编译? 以及您需要将它们放在哪里? 您需要如何放置它们? 别担心,我们将在本文中介绍它们。

您将需要以下文件(在主线或 4.x 最新内核的情况下,如果您的 SOC 受支持 - 在我们的例子中,是的,Allwinner H3)

  • U-boot 引导加载程序(又名 u-boot-sunxi-with-spl.bin 文件,例如 Orange pi pc Allwinner H3) - 单独编译
  • 设备特定的 .dtb 文件(此处称为 sun8i-h3-orangepi-pc.dtb),作为您的主线内核编译的一部分生成
  • Linux 内核镜像 uImage/zImage(在我们的例子中,zImage 称为 vmlinuz-4.6-rc1-sunxi)
  • Initramfs 文件(它是什么?您将在我们进行过程中学习)
  • modloop 文件(它又是什么?您将在我们进行过程中学习)
  • boot.scr 或 extlinux.conf 文件,用于告诉 U-boot 从哪里以及如何查找和加载所有上述文件到内存中

请注意,我们将使用 boot.scr 而不是 extlinux.conf,u-boot 通常搜索 /boot.scr 或 /boot/boot.scr

在旧内核 (3.x 等) 的情况下,您将需要以下内容,我们在此不作介绍

  • uImage (linux 内核)
  • script.bin(从 fex 格式转换而来,请注意,如果我们有 fex,我们这里不需要 dtb 文件)
  • ext4/ext3 rootfs 文件系统
  • U-boot 引导加载程序等

编译 U-boot

您可以按照此过程进行操作,也可以在下一节中与 Armbian 内核一起编译。 假设您在 x64 位 ubuntu 14.04 LTS 上构建

确定您的构建目标,又名您的板子。 转到您的 u-boot 树并在目录 configs/ 中搜索您的板子,文件名看起来像 <board_name>_defconfig。 因此,如果您的设备是 orange pi pc,则您的构建目标是 orangepi_pc_defconfig

  • make CROSS_COMPILE=arm-linux-gnueabihf- <board_name>_defconfig
  • make CROSS_COMPILE=arm-linux-gnueabihf- menuconfig (可选,如果您想使用 menuconfig)
  • make CROSS_COMPILE=arm-linux-gnueabihf-

在 ARM 板上本地编译时,省略 CROSS_COMPILE=…

构建完成后,您的 u-boot 树中将提供 u-boot-sunxi-with-spl.bin

创建一个 boot.cmd 文件并将其配置为如上所述。

编译带有 .dtb 文件的内核

编译内核的首要任务是验证您的 SOC 是否受主线 Linux 内核支持。 内核还必须启用并编译这些选项,而不是模块(更好)

CONFIG_SQUASHFS=y The squashfs 4.0 is in "misc" under "filesystems"
CONFIG_BLK_DEV_LOOP=y This is under "Device drivers" -> "Block devices" -> "Loopback device support"
  • [*]启用可加载模块支持 --->
  • 设备驱动程序 --->图形支持 --->帧缓冲设备 --->[*]支持帧缓冲设备 --->[*]简单帧缓冲支持
  • 设备驱动程序 --->图形支持 --->控制台显示驱动程序支持 --->[*]帧缓冲控制台支持

simplefb 是一种在 HDMI 监视器上启动显示器的快速简便方法是使用 sunxi cfb 控制台支持构建 U-Boot,并且不要忘记在您的 boot.cmd/boot.scr 中将您的控制台更改为 console=tty1 以启用简单帧缓冲驱动程序。

选择稳定版本

或者选择 sunxi-next 分支,该分支维护了所有已被接受、合并并将包含在下一个稳定版本中的内容

  • make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- sunxi_defconfig
  • make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig (您希望使用 menuconfig 包含上述 squashfs 和 blk_dev_loop)

要构建,请运行以下命令,编译结束后,您应该已经在 arch/arm/boot 中生成了 zImage,并在 arch/arm/boot/dts 中生成了设备树 blob (.dtb)。

  • ARCH=arm CROSS_COMPILE=<toolchain-prefix> make zImage dtbs

此设备树 blob(或简称为 dtb)为内核提供了它当前运行的硬件的描述。

在设备树 blob (dtb) 目标中,它与 Allwinner 使用的 FEX 脚本非常相似,但更加通用。 别担心,记住我们展示了 fex 文件,以防旧内核 (3.x 等) 转换为 script.bin。

Dtb 允许编译单个内核镜像,该镜像将在多个平台上运行。

要识别将在您的板子上使用的 dtb 文件,请首先查看 arch/arm/boot/dts。 您应该看到很多文件,其中大多数与我们无关,因为它们针对基于其他 ARM SOC 的板卡。 所有 sunxi dtb 都遵循 <family>-<soc>-<board>.dtb 模式

如果您已将某些驱动程序配置为模块,则还需要构建和安装这些模块

  • ARCH=arm CROSS_COMPILE=<toolchain-prefix> INSTALL_MOD_PATH=<any-path-you-like> make modules modules_install

构建成功后,您可以在提供的 INSTALL_MOD_PATH 目录中找到模块。

Armbian 也有一种编译内核的简单方法(以防万一),所以请看下面: - 直到我们用 Alpine 的编译方法填写此部分

  • 创建一个具有 40GB 磁盘空间的 VM Ubuntu 14.04 LTS x64 服务器镜像(不需要安装任何编译工具,脚本将下载合适的工具)
  • apt-get -y -qq install git
  • git clone --depth 1 https://github.com/igorpecovnik/lib
  • cp lib/compile.sh .
  • vim compile.sh

请确保您具有以下选项,如下所示

KERNEL_ONLY="yes" //compile only kernel, u-boot and other packages and not buid complete OS image
KERNEL_CONFIGURE="yes"  //to include your modules
CLEAN_LEVEL="make,images,debs"
KERNEL_KEEP_CONFIG="yes"

请注意,您可以将补丁添加到 patches 文件夹中。 所有补丁必须具有文件扩展名 .patch。

或使用

  • ./compile.sh BRANCH=dev BOARD=orangepih3 KERNEL_ONLY=yes PROGRESS_DISPLAY=plain COMPRESS_OUTPUTIMAGE=yes RELEASE=jessie

让我们使用第一个选项。 系统将提示您选择板子。

选择 orangepih3,然后选择 dev 分支

选择您的内核配置选项,如上所述(squashfs 模块等)。

编译完成后,在 output 文件夹中,您将注意到以下文件

DTB 文件

linux-dtb-dev-sunxi_5.07_armhf.deb -- untar it and it will contain boot/dtb-4.6.0-rc1-sunxi/sun8i-h3-orangepi-pc.dtb

UBOOT+SPL 文件

linux-u-boot-dev-orangepih3_5.07_armhf.deb -- untar it and it will contain usr/lib/linux-u-boot-dev-orangepih3_5.07_armf/u-boot-sunxi-with-spl.bin

VMLINUZ 内核

linux-image-dev-sunxi_5.07_armhf.deb -- untar it and it will contain boot/vmlinuz-4.6.0-rc1-sunxi

MODLOOP 文件

linux-image-dev-sunxi_5.07_armhf.deb -- untar it (it should ultimately be in lib/modules/4.6.0-rc1-sunxi)
linux-firmware-image-dev-sunxi_5.07_armhf.deb -- untar it (it should ultimately be in /lib/modules/firmware)

INITRAMFS 文件

linux-image-dev-sunxi_5.07_armhf.deb -- untar it (it should ultimately be in lib/modules/4.6.0-rc1-sunxi)
linux-firmware-image-dev-sunxi_5.07_armhf.deb -- untar it (it should ultimately be in /lib/firmware)

确保您还从 https://alpinelinux.cn/downloads/ 获取通用 ARM 镜像

解压 tar.gz 文件,您将需要 initramfs-grsecapk 文件夹

接下来继续下面的章节。

预计 Alpine Linux 将很快推出前沿版本选项以及 LTS 版本。

创建 Initramfs 文件

initrd 和 initramfs(也统称为 “initial RAM disk”,即 “初始 RAM 磁盘”)的目的是相似的:它们包含一个最小的根文件系统,以及一些脚本。 当内核启动时,如果指示它使用 initrd 或 initramfs(通过引导加载程序配置完成),它会将文件解压缩到 RAM 中,chroot() 到其中,并运行一些预定义的脚本。 最后,挂载 “真正的” 根文件系统,内核切换到它,并且正常的 init 进程可以开始。

如果您习惯于构建自己的内核,并将硬件所需的模块内置到内核中,您可能会想知道为什么我们首先需要初始 RAM 磁盘。 在这种情况下,您几乎永远不需要初始 RAM 磁盘。

但是,许多发行版必须发布一个二进制内核,该内核应适用于大多数甚至所有不同的硬件、存储、文件系统等的组合。 显然,他们无法将所有可能的模块构建到内核中。 因此,他们发布了一个最小的通用内核,并使用一个初始 RAM 磁盘,其中包含大多数硬件模块和一些逻辑,以检测需要加载哪些模块才能继续正常启动系统(即从硬盘驱动器启动)。

initramfs 至少包含一个名为 /init 的文件。 此文件由内核作为主 init 进程(PID 1)执行。 它必须完成所有工作。 此外,可以有任意数量的 /init 所需的其他文件和目录。 它们通常是您在任何其他根文件系统上也会找到的文件,例如用于设备节点的 /dev、用于内核信息的 /proc、用于二进制文件的 /bin 等。 initramfs 的结构可以很简单,也可以很复杂,具体取决于您计划做什么。

当内核挂载 initramfs 时,您的目标根分区尚未挂载,因此您无法访问任何文件。 这意味着除了 initramfs 之外什么都没有。 因此,您需要的一切,您想要的一切,都必须将其包含在您的 initramfs 中。 如果您想要一个 shell,则必须将其包含在您的 initramfs 中。 如果您要挂载某些东西,则需要一个挂载实用程序。 如果您需要加载模块,则您的 initramfs 必须提供模块和加载它的实用程序。 如果该实用程序依赖于库才能工作,则您还必须包含这些库。 这看起来很复杂,而且确实如此,因为 initramfs 必须独立运行。

这意味着可以启动内核,加载一些模块并运行一些任务(从 RAM 磁盘),最后启动正常的启动过程(/sbin/init 等)。

初始 RAM 磁盘必须包含一个功能齐全的文件系统,包括一些必要的程序和库。

出于空间原因,通常使用小 footprint 的二进制文件(例如 ash、busybox 或 klibc)。

一旦初始 RAM 磁盘的工作结束并且内核已切换到真正的根文件系统,则释放 RAM 磁盘使用的内存。

如前所述,初始 RAM 磁盘有两种类型:initrd 和 initramfs。 主要区别在于 initrd 包含原始文件系统镜像(例如 ext2),而 initramfs 包含 cpio 存档(展开后会生成目录层次结构)。 请注意,还有很多其他差异,此处未提及。

在这两种情况下,镜像文件都被压缩(通常使用 gzip),内核会展开并挂载它。 所有使用初始 RAM 磁盘的最新 Linux 发行版都使用 initramfs 镜像。 initrd 的使用越来越少。 要检查您感兴趣的文件是 initrd 还是 initramfs,您可以解压缩它并将其提供给 file

  • gunzip -c /boot/initrd-2.6 | file -

输出如下,该文件是真正的 initrd

/dev/stdin: Linux rev 1.0 ext2 filesystem data

这是您使用 initramfs 会看到的内容

  • gunzip -c /root/initrams-grsec | file -

请注意输出,它说 “SVR4 with no CRC” 格式,这在以后会很有用。

/dev/stdin: ASCII cpio archive (SVR4 with no CRC)

修改从 Alpine Linux 的通用 ARM 镜像中获取的 initramfs-grsec


如前所述,initramfs 镜像是一个压缩的 cpio 存档,尽管文件名有时可能会产生误导; 有时它仍然在名称中带有 “initrd”,并且很多时候它没有以 .gz 或其他指示压缩的后缀结尾

  • file /root/initrams-grsec
/boot/root: gzip compressed data, from Unix, last modified:..., max compression

现在 cpio 是一种古老的(并且有点奇怪的)存档格式。 解压缩时,它需要从标准输入读取存档(GNU cpio 也有一个 --file 选项来在命令行上指定文件)。 目录层次结构是在当前工作目录中创建的。 所以

  • mkdir temp
  • cd temp
  • gunzip -c /boot/root/initrams-grsec | cpio -i

28293 块

  • ls -l
total 64....
drwxr-xr-x  2 root root 4096 2016-04-28 14:16 bin
drwxr-xr-x  3 root root 4096 2016-04-28 14:16 dev
drwxr-xr-x  6 root root 4096 2016-04-28 14:16 etc
-rwxr-xr-x  1 root root 3355 2016-04-28 14:16 init
drwxr-xr-x  5 root root 4096 2016-04-28 14:16 lib/modules/4.6.0-rc1-sunxi
drwxr-xr-x  5 root root 4096 2016-04-28 14:16 lib/modules/firmware
drwxr-xr-x  2 root root 4096 2016-04-28 14:16 media
drwxr-xr-x  2 root root 4096 2016-04-28 14:16 newroot
drwxr-xr-x 12 root root 4096 2016-04-28 14:16 proc
drwxr-xr-x  3 root root 4096 2016-04-28 14:16 run

现在您终于可以随意编辑和更改内容了(没有空间限制,与 initrd 不同)。 完成后,您必须重新创建压缩的 cpio 存档。 同样,cpio 在存档时有点奇怪,因为它希望从标准输入读取要存档的文件名(并将存档写入标准输出)。

  • 从 linux-image-dev-sunxi_5.07_armhf.deb 中提取 /lib/modules/4.6.0-rc1-sunxi 文件夹,并将其复制到上面提取的 lib/modules 目录中。
  • 请注意,它可能已经包含一个名为 “4.1.20-0-grsec” 的文件夹,这不是必需的,您可以删除它
  • 从 linux-firmware-image-dev-sunxi_5.07_armhf.deb 中提取 /lib/firmware 文件夹,并将其复制到上面提取的 lib/modules 目录中

接下来您可以执行以下操作

  • pwd
/root/temp
  • find . | cpio -H newc -o | gzip -9 > /root/initramfs-sunxi
34207 blocks

唯一的诀窍是必须指定 -H newc 选项,因为 initramfs 中包含的 cpio 存档必须采用(新的)“SVR4 with no CRC” 可移植格式,正如我们在开始时看到的那样。 如果您想知道,cpio 可以创建其他格式的存档:bin、odc、crc、tar、ustar、hpbin、hpodc 是 GNU cpio 信息页面中列出的格式。

请注意,构造

  • find . | something

通常被认为是不安全的,应避免在 shell 脚本中使用,因为文件可能在其包含换行符和控制字符,这些字符可能会欺骗这些文件名的使用者。 对于 initramfs 镜像中包含的文件名,通常不是这种情况,因此在此处使用 “足够安全”。

  • 更改回 /root
  • mkimage -n initramfs-sunxi -A arm -O linux -T ramdisk -C none -d initramfs-sunxi initramfs-sunxi-new

这将创建一个带有其标头的 u-boot 兼容 ramdisk。

所以您的 initramfs 文件是 initramfs-sunxi-new

创建 modloop 文件

modloop 文件包含如下文件夹...

  • /modules/4.6.0-rc1-sunxi
  • /modules/4.6.0-rc1-sunxi/kernel
  • /modules/4.6.0-rc1-sunxi/arch
  • /modules/firmware

如果您在 modloop-grsec 或从 Alpine Linux 的通用 ARM 镜像中获得的任何其他类似 modloop 文件上运行以下命令

  • file modloop-grsec

您会意识到,它输出为

Squashfs filesystem, little endian, version 4.0, xzy bytes, xyz nodes, blocksize: xzy bytes, created...

您可以创建一个 “squashfs-temp” 目录并创建一个子目录 “modules”,然后从上面提取的 Armbian 源代码中复制上面关于您的特定内核的所需文件,您就可以开始了。 然后只需运行下面的命令。

  • mksquashfs [源文件夹] [SquashFS 目标文件] -b 1048576 -comp xz -Xdict-size 100%
  • mksquashfs squashfs-temp/ modloop-sunxi -b 1048576 -comp xz -Xdict-size 100%

生成的文件 modloop-sunxi 已准备好使用

请理解,mksquashfs 默认使用 gzip (deflate) 压缩,但如果压缩时间不那么重要,您应该使用更新版本的 SquashFS 中的 xz (LZMA2) 选项。是的,请使用 xz。

上述命令使用尽可能高的压缩选项和尽可能大的块大小(1 MiB 而不是默认的 128 kiB)激活 xz。因此,创建 SquashFS 文件的过程比使用默认选项要慢,但生成的文件要小得多,并且可能(取决于磁盘 IO 时间等)速度稍快。LZMA2 是一种高度非对称的压缩算法,因此解压缩比压缩快得多。

此命令将整个源文件夹压缩到 SquashFS 目标文件中。

确保在使用此命令之前 SquashFS 目标文件不存在。如果它已经存在,mksquashfs 会尝试更新它,但这可能会产生不良结果(我没有检查过,它可能只会产生大量错误消息,但最终生成有效的文件)。

您可以使用以下命令挂载生成的文件(假设目标文件夹存在且为空)。

  • mount [SquashFS 文件] [您要挂载到的文件夹]

如果此命令失败,您可能需要显式指定一些选项。

  • mount -o loop -t squashfs [SquashFS 文件] [您要挂载到的文件夹]

如果您想自动挂载 SquashFS,您还可以添加一个类似于以下的 _/etc/fstab_ 条目。

  • [SquashFS 文件] [您要挂载到的文件夹] squashfs auto,defaults 0 0

如何烧录到 SD 卡

让我们写入 SD 卡。我在此假设您已经编译了内核和 u-boot,并希望继续进行下一步。

当我们需要真正将内容写入 SD 卡时,请小心使用 dd 命令,如果操作失误,它可能会擦除您的硬盘。在我的情况下,我有一个 SD 卡读卡器,所以我通过使用 udev 的 by-id 链接将操作指向该插槽。这些 by-id 链接包含序列号,这有助于避免错误。以下命令假设您已执行相同的操作并正在使用 by-id 链接。

在 Linux 中,使用以下命令清空 SD 卡的前 1MB(我的 SD 卡是 /dev/sda)。

  • dd if=/dev/zero of=/dev/sda bs=1M count=1

但是,当在特殊文件(即设备)上使用 dd 命令时,您需要小心。

例如,以下命令将写入一个 1024 字节的磁带块。

  • dd if=(任何输入) of=(磁带设备) bs=1024 count=1

然而,以下命令将写入 1024 个每个字节的小块。

  • dd if=(任何输入) of=(磁带设备) bs=1 count=1024

这两者是不同的;由于记录间隙,1024 个小块将比一个大块占用更多的磁带空间,并且可能导致读取磁带时出现问题。

现在假设您已经获得了文件 u-boot-sunxi-with-spl.bin,只需使用以下命令将其烧录到 SD 卡。

  • dd if=u-boot-sunxi-with-spl.bin of=/dev/sda bs=1024 seek=8

有时,根据您的编译方式,您可以分别生成 spl 和 u-boot(我不推荐这样做),您可以使用以下命令分别安装组件。

  • dd if=spl/sunxi-spl.bin of=/dev/sda bs=1024 seek=8
  • dd if=u-boot.img of=/dev/sda bs=1024 seek=40

请注意,如果您使用的是旧版本源代码(v2013.07 或更早版本),则步骤略有不同。

  • dd if=spl/sunxi-spl.bin of=/dev/sdX bs=1024 seek=8
  • dd if=u-boot.bin of=/dev/sdX bs=1024 seek=32

通常为了便于理解,以下是上述情况下 SD 卡的布局。

起始 大小 用途
0 8KB 未使用,可用于分区表等。
8 32KB 初始 SPL 加载器(2013.07 及更早版本为 8 到 24KB)。
40 504KB U-Boot(2013.07 及更早版本为 32KB 到 512KB)。
544 128KB 环境
672 352KB 保留
1024 - 可用于分区

记住在对 SD 卡进行分区时,要为所有 u-boot 文件留出足够的空间。u-boot 不会定义任何分区类型。建议将第一个 fat 分区起始扇区设置为 2048 (1MB)。

  • 接下来,使用 fdisk 在 SD 卡上创建一个新的主分区(到目前为止还没有分区,只有 uboot 被烧录到卡上)。
  • 将 SD 卡的起始扇区设置为 2048。
  • 现在通过运行 mkfs.fat /dev/sda1 在新创建的分区上安装 fat 文件系统。
  • 使用 fdisk,设置分区的可引导标志。

当设置分区的可引导标志时,要创建 FAT32 分区,如果您使用 fdisk,FAT32 分区类型称为 W95 FAT32 (LBA),其 ID 为 0xc。

您可以将 SD 卡重命名为 rootfs...

请注意,对于最新的 U-Boot,可以使用 ext2/ext3/ext4 作为启动分区,或者像我们这里的情况一样,在任何分区中使用 fat 文件系统。


有一个调试串口,您可以使用它通过 Putty 连接到 PC,波特率设置为 115200 kbps。您应该能够看到以下内容。

U-Boot SPL 2016.03-armbian (Apr 08 2016 - 01:09:18)
DRAM: 512 MiB
Failed to set core voltage! Can't set CPU frequency
Trying to boot from MMC1
U-Boot 2016.03-armbian (Apr 08 2016 - 01:09:18 +0530) Allwinner Technology
CPU:   Allwinner H3 (SUN8I 1680)
Model: Xunlong Orange Pi PC
I2C:   ready
DRAM:  512 MiB
MMC:   SUNXI SD/MMC: 0
*** Warning - bad CRC, using default environment
In:    serial
Out:   serial
Err:   serial
Net:   No ethernet found.
starting USB...
USB0:   USB EHCI 1.00
USB1:   USB OHCI 1.0
USB2:   USB EHCI 1.00
USB3:   USB OHCI 1.0
USB4:   USB EHCI 1.00
USB5:   USB OHCI 1.0
scanning bus 0 for devices... 1 USB Device(s) found
scanning bus 2 for devices... 1 USB Device(s) found
scanning bus 4 for devices... 1 USB Device(s) found
Hit any key to stop autoboot:  0
switch to partitions #0, OK
mmc0 is current device
Scanning mmc 0:1...
Found /boot/extlinux/extlinux.conf
Retrieving file: /boot/extlinux/extlinux.conf
reading /boot/extlinux/extlinux.conf
232 bytes read in 29 ms (7.8 KiB/s)
1:      Linux Mailine 4.6RC2
Retrieving file: /boot/uinitrd
reading /boot/uinitrd
 **Unable to read file /boot/uinitrd
 for failure retrieving initrd
SCRIPT FAILED: continuing...
USB device 0: unknown device
No ethernet found.
missing environment variable: pxeuuid
No ethernet found.
Retrieving file: /boot/extlinux/pxelinux.cfg/0
No ethernet found.
Retrieving file: /boot/extlinux/pxelinux.cfg/default-arm-sunxi
No ethernet found.
Retrieving file: /boot/extlinux/pxelinux.cfg/default-arm
No ethernet found.
Retrieving file: /boot/extlinux/pxelinux.cfg/default
No ethernet found.
Config file not found
No ethernet found.
=>

您可以使用以下命令浏览 SD 卡。

  • fatls mmc 0:1 boot

下一步将需要简单地将所需的文件(如启动配置文件、内核等)复制到上述 FAT 分区中(请注意,只有一个 FAT 分区,并且标记为可引导),U-Boot(或任何其他引导加载程序)将找到这些启动文件并执行它们。

  • 假设您已经编译了 vmlinuz-4.6.0-rc1-sunxi,您可以在 micro-sd fat 分区的根目录下创建一个名为 /boot 的目录,并将内核复制到那里。

  • 类似地,在 micro-sd 卡中创建另一个名为 /boot/dtbs 的文件夹,并将相应的 sun8i-h3-orangepi-pc.dtb 文件复制到那里。
  • 类似地,创建 boot.scr 并将其复制到 /boot 文件夹。

基本上,在启动时,U-boot 将在第一个 FAT 分区中查找 uEnv.txt 或 boot.scr,或者查找 extlinux.conf。在这里我们将定义 boot.scr。

boot.scr 包含加载内核、initrd、设置内核参数和启动所需的 uboot 命令。

要创建 boot.scr,首先创建一个 u-boot 脚本 boot.cmd,其中包含启动系统所需的 u-boot 命令。

  • setenv fdt_high ffffffff
  • setenv machid 1029
  • setenv bootargs earlyprintk /boot/vmlinuz-4.6.0-rc1-sunxi modules=loop,squashfs,sd-mod,usb-storage modloop=/boot/modloop-sunxi console=${console}
  • load mmc 0:1 0x43000000 boot/dtbs/sun8i-h3-orangepi-pc.dtb
  • load mmc 0:1 0x41000000 boot/vmlinuz-4.6.0-rc1-sunxi
  • load mmc 0:1 0x45000000 boot/initramfs-sunxi-new
  • bootz 0x41000000 0x45000000 0x43000000

然后使用 mkimage 命令将其转换为 boot.scr。

mkimage -C none -A arm -T script -d boot.cmd boot.scr

如果您使用的是旧版本的 U-Boot(您不应该使用旧版本 - 哪个版本?),您可能需要以下行 fdt_high ffffffff 以防止提取的内核覆盖设备树配置,但通常情况下您不需要它。

setenv 用于在 u-boot 环境中设置标志。

bootm/bootz 命令用于启动操作系统镜像。它从镜像头中获取有关操作系统类型、使用的文件压缩方法(如果有)、加载地址和入口点地址等信息。然后,该命令会将镜像加载到所需的内存地址,并在必要时动态解压缩。根据操作系统,它将传递所需的启动参数并在其入口点启动操作系统。

bootm 的第一个参数是镜像存储的内存地址(在 RAM、ROM 或闪存中),后跟取决于操作系统的可选参数。“Load”仅仅是将文件加载到 RAM 位置。

Linux 要求在启动时传递扁平设备树 blob,而 bootm 期望其第三个参数是 blob 在内存中的地址。

bootm 的第二个参数取决于是否使用 initrd 初始 ramdisk 镜像。如果内核应该在没有初始 ramdisk 的情况下启动,则第二个参数应指定为“-”,否则它将被解释为 initrd 的起始地址(在 RAM、ROM 或闪存中)。

要在没有 initrd ramdisk 镜像的情况下启动 Linux 内核镜像,可以使用以下命令。

=> bootm ${kernel_addr} - ${fdt_addr} 如果要使用 ramdisk 镜像,您可以键入

=> bootm ${kernel_addr} ${ramdisk_addr} ${fdt_addr} 这两个示例当然都意味着所使用的变量已设置为内核、fdt blob 和 initrd ramdisk 镜像的正确地址。

bootm <Linux uImage address> <mkimage wrapped ramdisk address> <device tree (dtb) address>

使用 bootm/bootz 命令,U-Boot 在启动 Linux 之前会重新定位镜像,因此上面的地址可能不是内核看到的地址。U-Boot 还会修改设备树,以告知内核 ramdisk 镜像在内存中的位置(initrd-start 和 initrd-end)。该命令将 r2 寄存器设置为设备树在内存中的地址,而 go 命令不会这样做。

  • 记住 console=${console} 对于您在 Alpinelinux 启动后与其交互非常重要。
  • 使用 bootz 而不是 bootm。
  • bootz 基本上启动加载到 RAM 中给定地址的内核镜像以及其他镜像。
  • 您可以为您的 vmlinuz 使用 0x42000000。
  • setenv machid 命令要求 nanopi m1 将自身报告为 orange pi pc,因为我们正在加载 orange pi pc dtb 和内核。
  • 上述所有命令也可以通过 u-boot 提示符运行,并通过串口调试端口进行检查,而无需编译 boot.cmd。

人们通常会如下告诫。

  • 新内核默认启用了 CMA。CMA 尝试在物理地址 0x43000000 和 0x50000000 之间保留 192MB 的物理连续内存。专门使用此地址范围非常重要,因为 CedarX 硬件只能与前 256MB 的物理 DRAM 一起工作(DRAM 的基地址为 0x40000000)。现在,如果 initrd 由 u-boot 放置在此内存区域的中间位置(将其分成两半),那么 CMA 将无法再在那里保留物理连续的 192MB 内存块。因此,CMA 内存区域的保留在启动时失败。
  • 解决此问题的方法是不将 initrd 放置在 0x43000000 和 0x50000000 之间的任何位置。
  • 确保您还创建并将文件 modloop-sunxi 和 initramfs-sunxi-new 复制到 /boot 文件夹。
  • 最后,将 apks 文件夹复制到 SD 卡的根目录。

apks 文件夹可以通过从 https://alpinelinux.cn/downloads/ 下载通用 ARM 镜像来提取。

您的 SD 卡将如下所示。

  • /boot
  • /apks

以及 /boot 目录下,包含以下内容。

  • /boot/dtbs/sun8i-h3-orangepi-pc.dtb
  • /boot/boot.scr
  • /boot/initramfs-sunxi-new
  • /boot/modloop-sunxi
  • /boot/vmlinuz-4.6.0-rc1-sunxi

结论

实践、阅读、提问、在论坛和频道中交流,这就是您学习的方式。不要担心别人不回应,他们并非无礼,有些人可能很忙,那些不忙的人会回应。最后,不要害羞,给邮件列表发送邮件。

仅用于调试目的

需要粘贴已测试的场景