程序项目代做,有需求私信(小程序、网站、爬虫、电路板设计、驱动、应用程序开发、毕设疑难问题处理等)

Rockchip RK3399 - 移植linux 5.2.8

目录

----------------------------------------------------------------------------------------------------------------------------

开发板 :NanoPC-T4开发板
eMMC16GB
LPDDR34GB
显示屏 :15.6英寸HDMI接口显示屏
u-bootRockchip官方uboot 2017.09
linux5.2.8
----------------------------------------------------------------------------------------------------------------------------

在前面的章节,我们已经介绍了在RK3399上面移植Rockchip官方提供的uboot 2017.09,这一节我们将移植linux 5.2.8RK3399上。

一、linux内核

uboot一样,linux内核通常有三种:

  • linux官方源码:《https://github.com/torvalds/linux》,linux官方源码是由linux官方维护,支持非常全面的芯片,但对具体某款开发板支持情况一般;
  • 半导体厂商瑞芯微官方源码:《https://github.com/rockchip-linux/kernel》,半导体厂商基于linux官方源码进行修改,对自家的芯片进行完善的支持,针对某款处理器支持情况较好;
  • 开发板友善之家官方源码:《https://github.com/friendlyarm/kernel-rockchip》,开发板厂商基于半导体厂商维护的linux,对自家的开发板进行板级支持,针对某款开发板支持情况较好;

由于Rockchip官方提供的内核版本较低,所以这里我们直接去linux官方去下载,然后后面参考开发板友善之家官方源码修改使其能够支持NanoPC-T4开发板。

1.1 源码下载

内核源码下载地址为:《https://www.kernel.org/》,这里我们不下载最新的6.3.2版本,我们和之前介绍的《Mini2440内核移植》一样,选择5.2.8版本:

也可以到内核镜像网址下载https://mirrors.edge.kernel.org/pub/linux/kernel/,这里下载速度更快。

如果下载速度太慢,无法下载,提供另一个链接:http://ftp.sjtu.edu.cn/sites/ftp.kernel.org/pub/linux/kernel/

我们这里下载linux-5.2.8版本,执行如下命令:

root@zhengyang:/work/sambashare/rk3399# wget http://ftp.sjtu.edu.cn/sites/ftp.kernel.org/pub/linux/kernel/v5.x/linux-5.2.8.tar.gz

这里我是下载到/work/sambashare/rk3399路径下的,这个路径是用来专门存放与rk3399相关的内容。

解压源码:

root@zhengyang:/work/sambashare/rk3399# tar -xvf linux-5.2.8.tar.gz

关于《linux内核的目录结构》我们之前已经介绍过,这里就不再重复叙述了。

root@zhengyang:/work/sambashare/rk3399# cd linux-5.2.8/
root@zhengyang:/work/sambashare/rk3399/linux-5.2.8# ll
总用量 840
drwxrwxr-x  24 root root   4096 Aug  9  2019 ./
drwxr-xr-x   8 root root   4096 May 17 04:16 ../
drwxrwxr-x  27 root root   4096 Aug  9  2019 arch/
drwxrwxr-x   3 root root   4096 Aug  9  2019 block/
drwxrwxr-x   2 root root   4096 Aug  9  2019 certs/
-rw-rw-r--   1 root root  14943 Aug  9  2019 .clang-format
-rw-rw-r--   1 root root     59 Aug  9  2019 .cocciconfig
-rw-rw-r--   1 root root    423 Aug  9  2019 COPYING
-rw-rw-r--   1 root root  99486 Aug  9  2019 CREDITS
drwxrwxr-x   4 root root   4096 Aug  9  2019 crypto/
drwxrwxr-x 122 root root  12288 Aug  9  2019 Documentation/
drwxrwxr-x 140 root root   4096 Aug  9  2019 drivers/
drwxrwxr-x  73 root root   4096 Aug  9  2019 fs/
-rw-rw-r--   1 root root     71 Aug  9  2019 .get_maintainer.ignore
-rw-rw-r--   1 root root     30 Aug  9  2019 .gitattributes
-rw-rw-r--   1 root root   1658 Aug  9  2019 .gitignore
drwxrwxr-x  27 root root   4096 Aug  9  2019 include/
drwxrwxr-x   2 root root   4096 Aug  9  2019 init/
drwxrwxr-x   2 root root   4096 Aug  9  2019 ipc/
-rw-rw-r--   1 root root   1513 Aug  9  2019 Kbuild
-rw-rw-r--   1 root root    563 Aug  9  2019 Kconfig
drwxrwxr-x  18 root root   4096 Aug  9  2019 kernel/
drwxrwxr-x  15 root root  12288 Aug  9  2019 lib/
drwxrwxr-x   6 root root   4096 Aug  9  2019 LICENSES/
-rw-rw-r--   1 root root  12316 Aug  9  2019 .mailmap
-rw-rw-r--   1 root root 512407 Aug  9  2019 MAINTAINERS
-rw-rw-r--   1 root root  60262 Aug  9  2019 Makefile
drwxrwxr-x   3 root root   4096 Aug  9  2019 mm/
drwxrwxr-x  70 root root   4096 Aug  9  2019 net/
-rw-rw-r--   1 root root    727 Aug  9  2019 README
drwxrwxr-x  29 root root   4096 Aug  9  2019 samples/
drwxrwxr-x  15 root root   4096 Aug  9  2019 scripts/
drwxrwxr-x  11 root root   4096 Aug  9  2019 security/
drwxrwxr-x  26 root root   4096 Aug  9  2019 sound/
drwxrwxr-x  36 root root   4096 Aug  9  2019 tools/
drwxrwxr-x   2 root root   4096 Aug  9  2019 usr/
drwxrwxr-x   4 root root   4096 Aug  9  2019 virt/

1.2 配置Makefile

修改顶层的Makefile,打开Makefile文件,找到下面语句:

ARCH        ?= $(SUBARCH)

修改为:

ARCH        ?= arm64
CROSS_COMPILE    ?= arm-linux-

其中,ARCH是指定目标平台为arm64CROSS_COMPILE是指定交叉编译器,这里指定的是系统默认的交叉编译器,如要使用其它的,则要把编译器的全路径在这里写出。

1.3 内核defconfig配置

接下来要做的就是内核配置、编译了。单板的默认配置文件在arch/arm64/configs目录下:

root@zhengyang:/work/sambashare/rk3399/linux-5.2.8# ll arch/arm64/configs/defconfig
-rw-rw-r-- 1 root root 18417 Aug  9  2019 arch/arm64/configs/defconfig

在这个目录下就这一个配置,我们也没有其它选择了,那只能走一步看一步了。

配置文件defconfig其定义如下:

View Code
CONFIG_SYSVIPC=y
CONFIG_POSIX_MQUEUE=y
CONFIG_AUDIT=y
CONFIG_NO_HZ_IDLE=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_PREEMPT=y
CONFIG_IRQ_TIME_ACCOUNTING=y
CONFIG_BSD_PROCESS_ACCT=y
CONFIG_BSD_PROCESS_ACCT_V3=y
CONFIG_TASKSTATS=y
CONFIG_TASK_DELAY_ACCT=y
CONFIG_TASK_XACCT=y
CONFIG_TASK_IO_ACCOUNTING=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_NUMA_BALANCING=y
CONFIG_MEMCG=y
CONFIG_MEMCG_SWAP=y
CONFIG_BLK_CGROUP=y
CONFIG_CGROUP_PIDS=y
CONFIG_CGROUP_HUGETLB=y
CONFIG_CPUSETS=y
CONFIG_CGROUP_DEVICE=y
CONFIG_CGROUP_CPUACCT=y
CONFIG_CGROUP_PERF=y
CONFIG_USER_NS=y
CONFIG_SCHED_AUTOGROUP=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_KALLSYMS_ALL=y
# CONFIG_COMPAT_BRK is not set
CONFIG_PROFILING=y
CONFIG_ARCH_AGILEX=y
CONFIG_ARCH_SUNXI=y
CONFIG_ARCH_ALPINE=y
CONFIG_ARCH_BCM2835=y
CONFIG_ARCH_BCM_IPROC=y
CONFIG_ARCH_BERLIN=y
CONFIG_ARCH_BRCMSTB=y
CONFIG_ARCH_EXYNOS=y
CONFIG_ARCH_K3=y
CONFIG_ARCH_LAYERSCAPE=y
CONFIG_ARCH_LG1K=y
CONFIG_ARCH_HISI=y
CONFIG_ARCH_MEDIATEK=y
CONFIG_ARCH_MESON=y
CONFIG_ARCH_MVEBU=y
CONFIG_ARCH_MXC=y
CONFIG_ARCH_QCOM=y
CONFIG_ARCH_RENESAS=y
CONFIG_ARCH_ROCKCHIP=y
CONFIG_ARCH_SEATTLE=y
CONFIG_ARCH_STRATIX10=y
CONFIG_ARCH_SYNQUACER=y
CONFIG_ARCH_TEGRA=y
CONFIG_ARCH_SPRD=y
CONFIG_ARCH_THUNDER=y
CONFIG_ARCH_THUNDER2=y
CONFIG_ARCH_UNIPHIER=y
CONFIG_ARCH_VEXPRESS=y
CONFIG_ARCH_XGENE=y
CONFIG_ARCH_ZX=y
CONFIG_ARCH_ZYNQMP=y
CONFIG_ARM64_VA_BITS_48=y
CONFIG_SCHED_MC=y
CONFIG_NUMA=y
CONFIG_SECCOMP=y
CONFIG_KEXEC=y
CONFIG_CRASH_DUMP=y
CONFIG_XEN=y
CONFIG_COMPAT=y
CONFIG_HIBERNATION=y
CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y
CONFIG_ARM_CPUIDLE=y
CONFIG_CPU_FREQ=y
CONFIG_CPU_FREQ_STAT=y
CONFIG_CPU_FREQ_GOV_POWERSAVE=m
CONFIG_CPU_FREQ_GOV_USERSPACE=y
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m
CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
CONFIG_CPUFREQ_DT=y
CONFIG_ACPI_CPPC_CPUFREQ=m
CONFIG_ARM_ARMADA_37XX_CPUFREQ=y
CONFIG_ARM_SCPI_CPUFREQ=y
CONFIG_ARM_TEGRA186_CPUFREQ=y
CONFIG_ARM_SCPI_PROTOCOL=y
CONFIG_RASPBERRYPI_FIRMWARE=y
CONFIG_INTEL_STRATIX10_SERVICE=y
CONFIG_TI_SCI_PROTOCOL=y
CONFIG_EFI_CAPSULE_LOADER=y
CONFIG_IMX_SCU=y
CONFIG_IMX_SCU_PD=y
CONFIG_ACPI=y
CONFIG_ACPI_APEI=y
CONFIG_ACPI_APEI_GHES=y
CONFIG_ACPI_APEI_MEMORY_FAILURE=y
CONFIG_ACPI_APEI_EINJ=y
CONFIG_VIRTUALIZATION=y
CONFIG_KVM=y
CONFIG_ARM64_CRYPTO=y
CONFIG_CRYPTO_SHA1_ARM64_CE=y
CONFIG_CRYPTO_SHA2_ARM64_CE=y
CONFIG_CRYPTO_SHA512_ARM64_CE=m
CONFIG_CRYPTO_SHA3_ARM64=m
CONFIG_CRYPTO_SM3_ARM64_CE=m
CONFIG_CRYPTO_GHASH_ARM64_CE=y
CONFIG_CRYPTO_CRCT10DIF_ARM64_CE=m
CONFIG_CRYPTO_AES_ARM64_CE_CCM=y
CONFIG_CRYPTO_AES_ARM64_CE_BLK=y
CONFIG_CRYPTO_CHACHA20_NEON=m
CONFIG_CRYPTO_AES_ARM64_BS=m
CONFIG_JUMP_LABEL=y
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
CONFIG_KSM=y
CONFIG_MEMORY_FAILURE=y
CONFIG_TRANSPARENT_HUGEPAGE=y
CONFIG_CMA=y
CONFIG_NET=y
CONFIG_PACKET=y
CONFIG_UNIX=y
CONFIG_INET=y
CONFIG_IP_MULTICAST=y
CONFIG_IP_PNP=y
CONFIG_IP_PNP_DHCP=y
CONFIG_IP_PNP_BOOTP=y
CONFIG_IPV6=m
CONFIG_NETFILTER=y
CONFIG_NF_CONNTRACK=m
CONFIG_NF_CONNTRACK_EVENTS=y
CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m
CONFIG_NETFILTER_XT_TARGET_LOG=m
CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m
CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m
CONFIG_IP_NF_IPTABLES=m
CONFIG_IP_NF_FILTER=m
CONFIG_IP_NF_TARGET_REJECT=m
CONFIG_IP_NF_NAT=m
CONFIG_IP_NF_TARGET_MASQUERADE=m
CONFIG_IP_NF_MANGLE=m
CONFIG_IP6_NF_IPTABLES=m
CONFIG_IP6_NF_FILTER=m
CONFIG_IP6_NF_TARGET_REJECT=m
CONFIG_IP6_NF_MANGLE=m
CONFIG_IP6_NF_NAT=m
CONFIG_IP6_NF_TARGET_MASQUERADE=m
CONFIG_BRIDGE=m
CONFIG_BRIDGE_VLAN_FILTERING=y
CONFIG_VLAN_8021Q=m
CONFIG_VLAN_8021Q_GVRP=y
CONFIG_VLAN_8021Q_MVRP=y
CONFIG_QRTR=m
CONFIG_QRTR_SMD=m
CONFIG_QRTR_TUN=m
CONFIG_BPF_JIT=y
CONFIG_BT=m
CONFIG_BT_HIDP=m
# CONFIG_BT_HS is not set
# CONFIG_BT_LE is not set
CONFIG_BT_LEDS=y
# CONFIG_BT_DEBUGFS is not set
CONFIG_BT_HCIBTUSB=m
CONFIG_BT_HCIUART=m
CONFIG_BT_HCIUART_LL=y
CONFIG_BT_HCIUART_BCM=y
CONFIG_CFG80211=m
CONFIG_MAC80211=m
CONFIG_MAC80211_LEDS=y
CONFIG_RFKILL=m
CONFIG_NET_9P=y
CONFIG_NET_9P_VIRTIO=y
CONFIG_PCI=y
CONFIG_PCIEPORTBUS=y
CONFIG_PCI_IOV=y
CONFIG_HOTPLUG_PCI=y
CONFIG_HOTPLUG_PCI_ACPI=y
CONFIG_PCI_AARDVARK=y
CONFIG_PCI_TEGRA=y
CONFIG_PCIE_RCAR=y
CONFIG_PCI_HOST_GENERIC=y
CONFIG_PCI_XGENE=y
CONFIG_PCIE_ALTERA=y
CONFIG_PCIE_ALTERA_MSI=y
CONFIG_PCI_HOST_THUNDER_PEM=y
CONFIG_PCI_HOST_THUNDER_ECAM=y
CONFIG_PCIE_ROCKCHIP_HOST=m
CONFIG_PCI_LAYERSCAPE=y
CONFIG_PCI_HISI=y
CONFIG_PCIE_QCOM=y
CONFIG_PCIE_ARMADA_8K=y
CONFIG_PCIE_KIRIN=y
CONFIG_PCIE_HISI_STB=y
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
CONFIG_HISILICON_LPC=y
CONFIG_SIMPLE_PM_BUS=y
CONFIG_MTD=y
CONFIG_MTD_BLOCK=y
CONFIG_MTD_M25P80=y
CONFIG_MTD_RAW_NAND=y
CONFIG_MTD_NAND_DENALI_DT=y
CONFIG_MTD_NAND_MARVELL=y
CONFIG_MTD_NAND_QCOM=y
CONFIG_MTD_SPI_NOR=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_NBD=m
CONFIG_VIRTIO_BLK=y
CONFIG_BLK_DEV_NVME=m
CONFIG_SRAM=y
CONFIG_EEPROM_AT25=m
# CONFIG_SCSI_PROC_FS is not set
CONFIG_BLK_DEV_SD=y
CONFIG_SCSI_SAS_ATA=y
CONFIG_SCSI_HISI_SAS=y
CONFIG_SCSI_HISI_SAS_PCI=y
CONFIG_SCSI_UFSHCD=y
CONFIG_SCSI_UFSHCD_PLATFORM=y
CONFIG_SCSI_UFS_QCOM=m
CONFIG_SCSI_UFS_HISI=y
CONFIG_ATA=y
CONFIG_SATA_AHCI=y
CONFIG_SATA_AHCI_PLATFORM=y
CONFIG_AHCI_CEVA=y
CONFIG_AHCI_MVEBU=y
CONFIG_AHCI_XGENE=y
CONFIG_AHCI_QORIQ=y
CONFIG_SATA_SIL24=y
CONFIG_SATA_RCAR=y
CONFIG_PATA_PLATFORM=y
CONFIG_PATA_OF_PLATFORM=y
CONFIG_NETDEVICES=y
CONFIG_MACVLAN=m
CONFIG_MACVTAP=m
CONFIG_TUN=y
CONFIG_VETH=m
CONFIG_VIRTIO_NET=y
CONFIG_AMD_XGBE=y
CONFIG_NET_XGENE=y
CONFIG_ATL1C=m
CONFIG_MACB=y
CONFIG_THUNDER_NIC_PF=y
CONFIG_FEC=y
CONFIG_HIX5HD2_GMAC=y
CONFIG_HNS_DSAF=y
CONFIG_HNS_ENET=y
CONFIG_HNS3=y
CONFIG_HNS3_HCLGE=y
CONFIG_HNS3_ENET=y
CONFIG_E1000E=y
CONFIG_IGB=y
CONFIG_IGBVF=y
CONFIG_MVNETA=y
CONFIG_MVPP2=y
CONFIG_SKY2=y
CONFIG_QCOM_EMAC=m
CONFIG_RAVB=y
CONFIG_SMC91X=y
CONFIG_SMSC911X=y
CONFIG_SNI_AVE=y
CONFIG_SNI_NETSEC=y
CONFIG_STMMAC_ETH=m
CONFIG_MDIO_BUS_MUX_MMIOREG=y
CONFIG_AT803X_PHY=m
CONFIG_MARVELL_PHY=m
CONFIG_MARVELL_10G_PHY=m
CONFIG_MESON_GXL_PHY=m
CONFIG_MICREL_PHY=y
CONFIG_REALTEK_PHY=m
CONFIG_ROCKCHIP_PHY=y
CONFIG_USB_PEGASUS=m
CONFIG_USB_RTL8150=m
CONFIG_USB_RTL8152=m
CONFIG_USB_LAN78XX=m
CONFIG_USB_USBNET=m
CONFIG_USB_NET_DM9601=m
CONFIG_USB_NET_SR9800=m
CONFIG_USB_NET_SMSC75XX=m
CONFIG_USB_NET_SMSC95XX=m
CONFIG_USB_NET_PLUSB=m
CONFIG_USB_NET_MCS7830=m
CONFIG_ATH10K=m
CONFIG_ATH10K_PCI=m
CONFIG_BRCMFMAC=m
CONFIG_MWIFIEX=m
CONFIG_MWIFIEX_PCIE=m
CONFIG_WL18XX=m
CONFIG_WLCORE_SDIO=m
CONFIG_INPUT_EVDEV=y
CONFIG_KEYBOARD_ADC=m
CONFIG_KEYBOARD_GPIO=y
CONFIG_KEYBOARD_CROS_EC=y
CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_TOUCHSCREEN_ATMEL_MXT=m
CONFIG_INPUT_MISC=y
CONFIG_INPUT_PM8941_PWRKEY=y
CONFIG_INPUT_HISI_POWERKEY=y
# CONFIG_SERIO_SERPORT is not set
CONFIG_SERIO_AMBAKMI=y
CONFIG_LEGACY_PTY_COUNT=16
CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_EXTENDED=y
CONFIG_SERIAL_8250_SHARE_IRQ=y
CONFIG_SERIAL_8250_BCM2835AUX=y
CONFIG_SERIAL_8250_DW=y
CONFIG_SERIAL_8250_OMAP=y
CONFIG_SERIAL_8250_MT6577=y
CONFIG_SERIAL_8250_UNIPHIER=y
CONFIG_SERIAL_OF_PLATFORM=y
CONFIG_SERIAL_AMBA_PL011=y
CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
CONFIG_SERIAL_MESON=y
CONFIG_SERIAL_MESON_CONSOLE=y
CONFIG_SERIAL_SAMSUNG=y
CONFIG_SERIAL_SAMSUNG_CONSOLE=y
CONFIG_SERIAL_TEGRA=y
CONFIG_SERIAL_TEGRA_TCU=y
CONFIG_SERIAL_IMX=y
CONFIG_SERIAL_IMX_CONSOLE=y
CONFIG_SERIAL_SH_SCI=y
CONFIG_SERIAL_MSM=y
CONFIG_SERIAL_MSM_CONSOLE=y
CONFIG_SERIAL_QCOM_GENI=y
CONFIG_SERIAL_QCOM_GENI_CONSOLE=y
CONFIG_SERIAL_XILINX_PS_UART=y
CONFIG_SERIAL_XILINX_PS_UART_CONSOLE=y
CONFIG_SERIAL_FSL_LPUART=y
CONFIG_SERIAL_FSL_LPUART_CONSOLE=y
CONFIG_SERIAL_MVEBU_UART=y
CONFIG_SERIAL_DEV_BUS=y
CONFIG_VIRTIO_CONSOLE=y
CONFIG_IPMI_HANDLER=m
CONFIG_IPMI_DEVICE_INTERFACE=m
CONFIG_IPMI_SI=m
CONFIG_TCG_TPM=y
CONFIG_TCG_TIS_I2C_INFINEON=y
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_MUX=y
CONFIG_I2C_MUX_PCA954x=y
CONFIG_I2C_BCM2835=m
CONFIG_I2C_DESIGNWARE_PLATFORM=y
CONFIG_I2C_GPIO=m
CONFIG_I2C_IMX=y
CONFIG_I2C_MESON=y
CONFIG_I2C_MV64XXX=y
CONFIG_I2C_PXA=y
CONFIG_I2C_QUP=y
CONFIG_I2C_RK3X=y
CONFIG_I2C_SH_MOBILE=y
CONFIG_I2C_TEGRA=y
CONFIG_I2C_UNIPHIER_F=y
CONFIG_I2C_RCAR=y
CONFIG_I2C_CROS_EC_TUNNEL=y
CONFIG_SPI=y
CONFIG_SPI_ARMADA_3700=y
CONFIG_SPI_BCM2835=m
CONFIG_SPI_BCM2835AUX=m
CONFIG_SPI_NXP_FLEXSPI=y
CONFIG_SPI_MESON_SPICC=m
CONFIG_SPI_MESON_SPIFC=m
CONFIG_SPI_ORION=y
CONFIG_SPI_PL022=y
CONFIG_SPI_ROCKCHIP=y
CONFIG_SPI_QUP=y
CONFIG_SPI_S3C64XX=y
CONFIG_SPI_SPIDEV=m
CONFIG_SPI_SUN6I=y
CONFIG_SPMI=y
CONFIG_PINCTRL_SINGLE=y
CONFIG_PINCTRL_MAX77620=y
CONFIG_PINCTRL_IMX8MQ=y
CONFIG_PINCTRL_IMX8QXP=y
CONFIG_PINCTRL_IPQ8074=y
CONFIG_PINCTRL_MSM8916=y
CONFIG_PINCTRL_MSM8994=y
CONFIG_PINCTRL_MSM8996=y
CONFIG_PINCTRL_MSM8998=y
CONFIG_PINCTRL_QCS404=y
CONFIG_PINCTRL_QDF2XXX=y
CONFIG_PINCTRL_QCOM_SPMI_PMIC=y
CONFIG_PINCTRL_SDM845=y
CONFIG_GPIO_DWAPB=y
CONFIG_GPIO_MB86S7X=y
CONFIG_GPIO_PL061=y
CONFIG_GPIO_RCAR=y
CONFIG_GPIO_UNIPHIER=y
CONFIG_GPIO_XGENE=y
CONFIG_GPIO_XGENE_SB=y
CONFIG_GPIO_PCA953X=y
CONFIG_GPIO_PCA953X_IRQ=y
CONFIG_GPIO_MAX77620=y
CONFIG_POWER_AVS=y
CONFIG_ROCKCHIP_IODOMAIN=y
CONFIG_POWER_RESET_MSM=y
CONFIG_POWER_RESET_XGENE=y
CONFIG_POWER_RESET_SYSCON=y
CONFIG_SYSCON_REBOOT_MODE=y
CONFIG_BATTERY_SBS=m
CONFIG_BATTERY_BQ27XXX=y
CONFIG_SENSORS_ARM_SCPI=y
CONFIG_SENSORS_LM90=m
CONFIG_SENSORS_PWM_FAN=m
CONFIG_SENSORS_RASPBERRYPI_HWMON=m
CONFIG_SENSORS_INA2XX=m
CONFIG_THERMAL_GOV_POWER_ALLOCATOR=y
CONFIG_CPU_THERMAL=y
CONFIG_THERMAL_EMULATION=y
CONFIG_ROCKCHIP_THERMAL=m
CONFIG_RCAR_THERMAL=y
CONFIG_RCAR_GEN3_THERMAL=y
CONFIG_ARMADA_THERMAL=y
CONFIG_BCM2835_THERMAL=m
CONFIG_BRCMSTB_THERMAL=m
CONFIG_EXYNOS_THERMAL=y
CONFIG_TEGRA_BPMP_THERMAL=m
CONFIG_QCOM_TSENS=y
CONFIG_UNIPHIER_THERMAL=y
CONFIG_WATCHDOG=y
CONFIG_ARM_SP805_WATCHDOG=y
CONFIG_S3C2410_WATCHDOG=y
CONFIG_IMX2_WDT=y
CONFIG_MESON_GXBB_WATCHDOG=m
CONFIG_MESON_WATCHDOG=m
CONFIG_RENESAS_WDT=y
CONFIG_UNIPHIER_WATCHDOG=y
CONFIG_BCM2835_WDT=y
CONFIG_MFD_ALTERA_SYSMGR=y
CONFIG_MFD_BD9571MWV=y
CONFIG_MFD_AXP20X_I2C=y
CONFIG_MFD_AXP20X_RSB=y
CONFIG_MFD_CROS_EC=y
CONFIG_MFD_CROS_EC_CHARDEV=m
CONFIG_MFD_EXYNOS_LPASS=m
CONFIG_MFD_HI6421_PMIC=y
CONFIG_MFD_HI655X_PMIC=y
CONFIG_MFD_MAX77620=y
CONFIG_MFD_SPMI_PMIC=y
CONFIG_MFD_RK808=y
CONFIG_MFD_SEC_CORE=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
CONFIG_REGULATOR_AXP20X=y
CONFIG_REGULATOR_BD9571MWV=y
CONFIG_REGULATOR_FAN53555=y
CONFIG_REGULATOR_GPIO=y
CONFIG_REGULATOR_HI6421V530=y
CONFIG_REGULATOR_HI655X=y
CONFIG_REGULATOR_MAX77620=y
CONFIG_REGULATOR_MAX8973=y
CONFIG_REGULATOR_PFUZE100=y
CONFIG_REGULATOR_PWM=y
CONFIG_REGULATOR_QCOM_RPMH=y
CONFIG_REGULATOR_QCOM_SMD_RPM=y
CONFIG_REGULATOR_QCOM_SPMI=y
CONFIG_REGULATOR_RK808=y
CONFIG_REGULATOR_S2MPS11=y
CONFIG_REGULATOR_VCTRL=m
CONFIG_RC_CORE=m
CONFIG_RC_DECODERS=y
CONFIG_RC_DEVICES=y
CONFIG_IR_MESON=m
CONFIG_MEDIA_SUPPORT=m
CONFIG_MEDIA_CAMERA_SUPPORT=y
CONFIG_MEDIA_ANALOG_TV_SUPPORT=y
CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y
CONFIG_MEDIA_CONTROLLER=y
CONFIG_VIDEO_V4L2_SUBDEV_API=y
# CONFIG_DVB_NET is not set
CONFIG_MEDIA_USB_SUPPORT=y
CONFIG_USB_VIDEO_CLASS=m
CONFIG_V4L_PLATFORM_DRIVERS=y
CONFIG_VIDEO_SUN6I_CSI=m
CONFIG_V4L_MEM2MEM_DRIVERS=y
CONFIG_VIDEO_SAMSUNG_S5P_JPEG=m
CONFIG_VIDEO_SAMSUNG_S5P_MFC=m
CONFIG_VIDEO_SAMSUNG_EXYNOS_GSC=m
CONFIG_VIDEO_RENESAS_FCP=m
CONFIG_VIDEO_RENESAS_VSP1=m
CONFIG_DRM=m
CONFIG_DRM_NOUVEAU=m
CONFIG_DRM_EXYNOS=m
CONFIG_DRM_EXYNOS5433_DECON=y
CONFIG_DRM_EXYNOS7_DECON=y
CONFIG_DRM_EXYNOS_DSI=y
# CONFIG_DRM_EXYNOS_DP is not set
CONFIG_DRM_EXYNOS_HDMI=y
CONFIG_DRM_EXYNOS_MIC=y
CONFIG_DRM_ROCKCHIP=m
CONFIG_ROCKCHIP_ANALOGIX_DP=y
CONFIG_ROCKCHIP_CDN_DP=y
CONFIG_ROCKCHIP_DW_HDMI=y
CONFIG_ROCKCHIP_DW_MIPI_DSI=y
CONFIG_ROCKCHIP_INNO_HDMI=y
CONFIG_DRM_RCAR_DU=m
CONFIG_DRM_SUN4I=m
CONFIG_DRM_SUN8I_DW_HDMI=m
CONFIG_DRM_SUN8I_MIXER=m
CONFIG_DRM_TEGRA=m
CONFIG_DRM_PANEL_SIMPLE=m
CONFIG_DRM_SII902X=m
CONFIG_DRM_I2C_ADV7511=m
CONFIG_DRM_VC4=m
CONFIG_DRM_HISI_HIBMC=m
CONFIG_DRM_HISI_KIRIN=m
CONFIG_DRM_MESON=m
CONFIG_DRM_PL111=m
CONFIG_FB=y
CONFIG_FB_MODE_HELPERS=y
CONFIG_BACKLIGHT_GENERIC=m
CONFIG_BACKLIGHT_PWM=m
CONFIG_BACKLIGHT_LP855X=m
CONFIG_LOGO=y
# CONFIG_LOGO_LINUX_MONO is not set
# CONFIG_LOGO_LINUX_VGA16 is not set
CONFIG_SOUND=y
CONFIG_SND=y
CONFIG_SND_HDA_TEGRA=m
CONFIG_SND_HDA_CODEC_HDMI=m
CONFIG_SND_SOC=y
CONFIG_SND_BCM2835_SOC_I2S=m
CONFIG_SND_MESON_AXG_SOUND_CARD=m
CONFIG_SND_SOC_ROCKCHIP=m
CONFIG_SND_SOC_ROCKCHIP_SPDIF=m
CONFIG_SND_SOC_ROCKCHIP_RT5645=m
CONFIG_SND_SOC_RK3399_GRU_SOUND=m
CONFIG_SND_SOC_SAMSUNG=y
CONFIG_SND_SOC_RCAR=m
CONFIG_SND_SOC_AK4613=m
CONFIG_SND_SOC_ES7134=m
CONFIG_SND_SOC_ES7241=m
CONFIG_SND_SOC_PCM3168A_I2C=m
CONFIG_SND_SOC_TAS571X=m
CONFIG_SND_SIMPLE_CARD=m
CONFIG_SND_AUDIO_GRAPH_CARD=m
CONFIG_I2C_HID=m
CONFIG_USB=y
CONFIG_USB_OTG=y
CONFIG_USB_XHCI_HCD=y
CONFIG_USB_XHCI_TEGRA=y
CONFIG_USB_EHCI_HCD=y
CONFIG_USB_EHCI_EXYNOS=y
CONFIG_USB_EHCI_HCD_PLATFORM=y
CONFIG_USB_OHCI_HCD=y
CONFIG_USB_OHCI_EXYNOS=y
CONFIG_USB_OHCI_HCD_PLATFORM=y
CONFIG_USB_RENESAS_USBHS=m
CONFIG_USB_STORAGE=y
CONFIG_USB_MUSB_HDRC=y
CONFIG_USB_MUSB_SUNXI=y
CONFIG_USB_DWC3=y
CONFIG_USB_DWC2=y
CONFIG_USB_CHIPIDEA=y
CONFIG_USB_CHIPIDEA_UDC=y
CONFIG_USB_CHIPIDEA_HOST=y
CONFIG_USB_ISP1760=y
CONFIG_USB_HSIC_USB3503=y
CONFIG_NOP_USB_XCEIV=y
CONFIG_USB_ULPI=y
CONFIG_USB_GADGET=y
CONFIG_USB_RENESAS_USBHS_UDC=m
CONFIG_USB_RENESAS_USB3=m
CONFIG_MMC=y
CONFIG_MMC_BLOCK_MINORS=32
CONFIG_MMC_ARMMMCI=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_ACPI=y
CONFIG_MMC_SDHCI_PLTFM=y
CONFIG_MMC_SDHCI_OF_ARASAN=y
CONFIG_MMC_SDHCI_OF_ESDHC=y
CONFIG_MMC_SDHCI_CADENCE=y
CONFIG_MMC_SDHCI_ESDHC_IMX=y
CONFIG_MMC_SDHCI_TEGRA=y
CONFIG_MMC_SDHCI_F_SDH30=y
CONFIG_MMC_MESON_GX=y
CONFIG_MMC_SDHCI_MSM=y
CONFIG_MMC_SPI=y
CONFIG_MMC_SDHI=y
CONFIG_MMC_UNIPHIER=y
CONFIG_MMC_DW=y
CONFIG_MMC_DW_EXYNOS=y
CONFIG_MMC_DW_HI3798CV200=y
CONFIG_MMC_DW_K3=y
CONFIG_MMC_DW_ROCKCHIP=y
CONFIG_MMC_SUNXI=y
CONFIG_MMC_BCM2835=y
CONFIG_MMC_SDHCI_XENON=y
CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y
CONFIG_LEDS_GPIO=y
CONFIG_LEDS_PWM=y
CONFIG_LEDS_SYSCON=y
CONFIG_LEDS_TRIGGER_DISK=y
CONFIG_LEDS_TRIGGER_HEARTBEAT=y
CONFIG_LEDS_TRIGGER_CPU=y
CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
CONFIG_LEDS_TRIGGER_PANIC=y
CONFIG_EDAC=y
CONFIG_EDAC_GHES=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_MAX77686=y
CONFIG_RTC_DRV_RK808=m
CONFIG_RTC_DRV_RX8581=m
CONFIG_RTC_DRV_S5M=y
CONFIG_RTC_DRV_DS3232=y
CONFIG_RTC_DRV_EFI=y
CONFIG_RTC_DRV_CROS_EC=y
CONFIG_RTC_DRV_S3C=y
CONFIG_RTC_DRV_PL031=y
CONFIG_RTC_DRV_SUN6I=y
CONFIG_RTC_DRV_ARMADA38X=y
CONFIG_RTC_DRV_TEGRA=y
CONFIG_RTC_DRV_IMX_SC=m
CONFIG_RTC_DRV_XGENE=y
CONFIG_DMADEVICES=y
CONFIG_FSL_EDMA=y
CONFIG_DMA_BCM2835=m
CONFIG_K3_DMA=y
CONFIG_MV_XOR=y
CONFIG_MV_XOR_V2=y
CONFIG_PL330_DMA=y
CONFIG_TEGRA20_APB_DMA=y
CONFIG_QCOM_BAM_DMA=y
CONFIG_QCOM_HIDMA_MGMT=y
CONFIG_QCOM_HIDMA=y
CONFIG_RCAR_DMAC=y
CONFIG_RENESAS_USB_DMAC=m
CONFIG_VFIO=y
CONFIG_VFIO_PCI=y
CONFIG_VIRTIO_PCI=y
CONFIG_VIRTIO_BALLOON=y
CONFIG_VIRTIO_MMIO=y
CONFIG_XEN_GNTDEV=y
CONFIG_XEN_GRANT_DEV_ALLOC=y
CONFIG_CROS_EC_I2C=y
CONFIG_CROS_EC_SPI=y
CONFIG_COMMON_CLK_RK808=y
CONFIG_COMMON_CLK_SCPI=y
CONFIG_COMMON_CLK_CS2000_CP=y
CONFIG_COMMON_CLK_S2MPS11=y
CONFIG_CLK_QORIQ=y
CONFIG_COMMON_CLK_PWM=y
CONFIG_CLK_IMX8MQ=y
CONFIG_CLK_IMX8QXP=y
CONFIG_TI_SCI_CLK=y
CONFIG_COMMON_CLK_QCOM=y
CONFIG_QCOM_CLK_SMD_RPM=y
CONFIG_QCOM_CLK_RPMH=y
CONFIG_IPQ_GCC_8074=y
CONFIG_MSM_GCC_8916=y
CONFIG_MSM_GCC_8994=y
CONFIG_MSM_MMCC_8996=y
CONFIG_MSM_GCC_8998=y
CONFIG_QCS_GCC_404=y
CONFIG_SDM_GCC_845=y
CONFIG_HWSPINLOCK=y
CONFIG_HWSPINLOCK_QCOM=y
CONFIG_ARM_MHU=y
CONFIG_IMX_MBOX=y
CONFIG_PLATFORM_MHU=y
CONFIG_BCM2835_MBOX=y
CONFIG_TI_MESSAGE_MANAGER=y
CONFIG_QCOM_APCS_IPC=y
CONFIG_ROCKCHIP_IOMMU=y
CONFIG_TEGRA_IOMMU_SMMU=y
CONFIG_ARM_SMMU=y
CONFIG_ARM_SMMU_V3=y
CONFIG_QCOM_IOMMU=y
CONFIG_REMOTEPROC=m
CONFIG_QCOM_Q6V5_MSS=m
CONFIG_QCOM_Q6V5_PAS=m
CONFIG_QCOM_SYSMON=m
CONFIG_RPMSG_QCOM_GLINK_RPM=y
CONFIG_RPMSG_QCOM_GLINK_SMEM=m
CONFIG_RPMSG_QCOM_SMD=y
CONFIG_RASPBERRYPI_POWER=y
CONFIG_QCOM_COMMAND_DB=y
CONFIG_QCOM_GENI_SE=y
CONFIG_QCOM_GLINK_SSR=m
CONFIG_QCOM_RPMH=y
CONFIG_QCOM_SMEM=y
CONFIG_QCOM_SMD_RPM=y
CONFIG_QCOM_SMP2P=y
CONFIG_QCOM_SMSM=y
CONFIG_ARCH_R8A774A1=y
CONFIG_ARCH_R8A774C0=y
CONFIG_ARCH_R8A7795=y
CONFIG_ARCH_R8A7796=y
CONFIG_ARCH_R8A77965=y
CONFIG_ARCH_R8A77970=y
CONFIG_ARCH_R8A77980=y
CONFIG_ARCH_R8A77990=y
CONFIG_ARCH_R8A77995=y
CONFIG_ROCKCHIP_PM_DOMAINS=y
CONFIG_ARCH_TEGRA_132_SOC=y
CONFIG_ARCH_TEGRA_210_SOC=y
CONFIG_ARCH_TEGRA_186_SOC=y
CONFIG_ARCH_TEGRA_194_SOC=y
CONFIG_ARCH_K3_AM6_SOC=y
CONFIG_SOC_TI=y
CONFIG_TI_SCI_PM_DOMAINS=y
CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y
CONFIG_EXTCON_USB_GPIO=y
CONFIG_EXTCON_USBC_CROS_EC=y
CONFIG_MEMORY=y
CONFIG_IIO=y
CONFIG_EXYNOS_ADC=y
CONFIG_ROCKCHIP_SARADC=m
CONFIG_IIO_CROS_EC_SENSORS_CORE=m
CONFIG_IIO_CROS_EC_SENSORS=m
CONFIG_IIO_CROS_EC_LIGHT_PROX=m
CONFIG_IIO_CROS_EC_BARO=m
CONFIG_PWM=y
CONFIG_PWM_BCM2835=m
CONFIG_PWM_CROS_EC=m
CONFIG_PWM_MESON=m
CONFIG_PWM_RCAR=m
CONFIG_PWM_ROCKCHIP=y
CONFIG_PWM_SAMSUNG=y
CONFIG_PWM_SUN4I=m
CONFIG_PWM_TEGRA=m
CONFIG_RESET_TI_SCI=y
CONFIG_PHY_XGENE=y
CONFIG_PHY_SUN4I_USB=y
CONFIG_PHY_HI6220_USB=y
CONFIG_PHY_HISTB_COMBPHY=y
CONFIG_PHY_HISI_INNO_USB2=y
CONFIG_PHY_MVEBU_CP110_COMPHY=y
CONFIG_PHY_QCOM_QMP=m
CONFIG_PHY_QCOM_USB_HS=y
CONFIG_PHY_RCAR_GEN3_PCIE=y
CONFIG_PHY_RCAR_GEN3_USB2=y
CONFIG_PHY_RCAR_GEN3_USB3=m
CONFIG_PHY_ROCKCHIP_EMMC=y
CONFIG_PHY_ROCKCHIP_INNO_HDMI=m
CONFIG_PHY_ROCKCHIP_INNO_USB2=y
CONFIG_PHY_ROCKCHIP_PCIE=m
CONFIG_PHY_ROCKCHIP_TYPEC=y
CONFIG_PHY_UNIPHIER_USB2=y
CONFIG_PHY_UNIPHIER_USB3=y
CONFIG_PHY_TEGRA_XUSB=y
CONFIG_HISI_PMU=y
CONFIG_QCOM_L2_PMU=y
CONFIG_QCOM_L3_PMU=y
CONFIG_QCOM_QFPROM=y
CONFIG_ROCKCHIP_EFUSE=y
CONFIG_UNIPHIER_EFUSE=y
CONFIG_MESON_EFUSE=m
CONFIG_FPGA=y
CONFIG_FPGA_MGR_STRATIX10_SOC=m
CONFIG_FPGA_BRIDGE=m
CONFIG_ALTERA_FREEZE_BRIDGE=m
CONFIG_FPGA_REGION=m
CONFIG_OF_FPGA_REGION=m
CONFIG_TEE=y
CONFIG_OPTEE=y
CONFIG_EXT2_FS=y
CONFIG_EXT3_FS=y
CONFIG_EXT4_FS_POSIX_ACL=y
CONFIG_BTRFS_FS=m
CONFIG_BTRFS_FS_POSIX_ACL=y
CONFIG_FANOTIFY=y
CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y
CONFIG_QUOTA=y
CONFIG_AUTOFS4_FS=y
CONFIG_FUSE_FS=m
CONFIG_CUSE=m
CONFIG_OVERLAY_FS=m
CONFIG_VFAT_FS=y
CONFIG_HUGETLBFS=y
CONFIG_CONFIGFS_FS=y
CONFIG_EFIVAR_FS=y
CONFIG_SQUASHFS=y
CONFIG_NFS_FS=y
CONFIG_NFS_V4=y
CONFIG_NFS_V4_1=y
CONFIG_NFS_V4_2=y
CONFIG_ROOT_NFS=y
CONFIG_9P_FS=y
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_ISO8859_1=y
CONFIG_SECURITY=y
CONFIG_CRYPTO_ECHAINIV=y
CONFIG_CRYPTO_ANSI_CPRNG=y
CONFIG_DMA_CMA=y
CONFIG_CMA_SIZE_MBYTES=32
CONFIG_PRINTK_TIME=y
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_FS=y
CONFIG_MAGIC_SYSRQ=y
CONFIG_DEBUG_KERNEL=y
# CONFIG_SCHED_DEBUG is not set
# CONFIG_DEBUG_PREEMPT is not set
# CONFIG_FTRACE is not set
CONFIG_MEMTEST=y

在linux内核根目录下执行如下命令,执行完之后会在内核根目录下生成默认配置文件.config:

root@zhengyang:/work/sambashare/rk3399/linux-5.2.8#  make defconfig
  HOSTCC  scripts/basic/fixdep
  HOSTCC  scripts/kconfig/conf.o
  HOSTCC  scripts/kconfig/confdata.o
  HOSTCC  scripts/kconfig/expr.o
  LEX     scripts/kconfig/lexer.lex.c
  YACC    scripts/kconfig/parser.tab.h
  HOSTCC  scripts/kconfig/lexer.lex.o
  YACC    scripts/kconfig/parser.tab.c
  HOSTCC  scripts/kconfig/parser.tab.o
  HOSTCC  scripts/kconfig/preprocess.o
  HOSTCC  scripts/kconfig/symbol.o
  HOSTLD  scripts/kconfig/conf
*** Default configuration is based on 'defconfig'
#
# configuration written to .config
#

1.4 内核裁切

我们可以通过make menuconfig配置内核支持的功能:

root@zhengyang:/work/sambashare/rk3399/linux-5.2.8# make menuconfig

打开如下页面:

1.4.1 支持设备树

实际上在linux 5.2.8版本内核已经把设备树作为默认配置项了,因此也不需要我们配置了。

内核在启动时,会执行setup_arch函数,该函数用于在内核启动期间对硬件进行初始化,包括设置CPU、内存、设备树等等。该函数的实现是针对特定架构的,因此不同的架构会有不同的setup_arch实现。以ARM(非ARM64)为例:

setup_arch(&command_line);  // arch/arm/kernel/setup.c
    mdesc = setup_machine_fdt(__atags_pointer);  // arch/arm/kernel/devtree.c
        // 检验设备树头部,判断设备树有效性
        early_init_dt_verify(phys_to_virt(dt_phys)  //  drivers/of/fdt.c
                  initial_boot_params = params;
            // 找到最匹配的machine_desc
            mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach);  // drivers/of/fdt.c
            early_init_dt_scan_nodes()  // drivers/of/fdt.c
                // 从dtb文件中读取chosen节点的信息,包括命令行boot_command_line,initrd location及size
                of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line)
                // 得到根节点的 #address-cells 和 #size-cells属性信息
                of_scan_flat_dt(early_init_dt_scan_root, NULL)
                // 得到/memory节点的reg属性
                of_scan_flat_dt(early_init_dt_scan_memory, NULL)
        machine_desc = mdesc;
    ......
       

of_flat_dt_match_machine函数通过遍历所有的machine_desc,找到与设备树中compatible最匹配的一个machine_desc(每一个开发板都对应一个machine_desc,通过它定义开发板相关的一些属性及函数,比如机器类型ID、中断初始化函数、I/O映射函数、machine初始化函数等;这些函数会在内核启动阶段被回调,在较早的版本中,init_machine会包含大量的平台设备注册的代码)。

然而在ARM64中,其实现发生了改变:

setup_arch(&command_line);  // arch/arm64/kernel/setup.c
    // 参数为dtb在内存的首地址
    setup_machine_fdt(__fdt_pointer)   // arch/arm64/kernel/setup.c
        // 为fdt建立地址映射,在该函数的最后,顺便就调用memblock_reserve保留了该段内存
        void *dt_virt = fixmap_remap_fdt(dt_phys) 
        // 校验并解析dtb文件
        early_init_dt_scan(dt_virt)   // drivers/of/fdt.c  
            // 检验设备树头部,判断设备树有效性
            early_init_dt_verify() 
            early_init_dt_scan_nodes()  // drivers/of/fdt.c
                // 从dtb文件中读取chosen节点的信息,包括命令行boot_command_line,initrd location及size
                of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line)
                // 得到根节点的 #address-cells 和 #size-cells属性信息
                of_scan_flat_dt(early_init_dt_scan_root, NULL)
                // 得到/memory节点的reg属性
                of_scan_flat_dt(early_init_dt_scan_memory, NULL)
        // 获取machine名称
        name = of_flat_dt_get_machine_name();  // drivers/of/fdt.c
            // 获取设备树的根节点
            unsigned long dt_root = of_get_flat_dt_root() 
            // 这里直接获取设备树的model属性
            name = of_get_flat_dt_prop(dt_root, "model", NULL)   
    parse_early_param();
    ......
    arm64_memblock_init()
    ......
    // 解析dtb文件,转换成节点是device_node的树状结构, 其中设备节点会被转换为device_node结构
    unflatten_device_tree()  
    ......

可以看到这里没有了设备树和machine_desc的匹配过程了,也就是说ARM64已经完全抛弃了machine_desc,全部采用设备树的实现方式,关于《为什么要采用设备树》我在设备树章节已经具体介绍过。

1.4.2 支持NFS文件系统

使用NFS作为根文件系统,因为文件系统在宿主机中,这样在修改文件系统就非常方便,主要用于开发阶段使用。

File systems --->
    [*] Network File Systems --->
          <*> NFS client support
    	      <*> NFS client support for NFS version 2
    	      <*> NFS client support for NFS version 3
              <*> NFS client support for NFS version 4
          [*]   Root file system on NFS

勾选NFS client support for NFS version 4

1.4.3 配置uevent helper

配置Support for uevent helper

Device Drivers --->
     Generic Driver Options --->
            [*] Support for uevent helper 
            (/sbin/mdev)    path to uevent helper (NEW) 

该选项的作用是启用uevent helper程序的支持。uevent是内核与用户空间之间通信的一种方式,当内核检测到新的设备时,会生成一个uevent来通知用户空间,使得用户空间能够及时响应设备插拔事件,并做出相应的处理。其中, uevent helper程序就是在接收到uevent后执行的用户空间程序,用来完成设备的热插拔处理。

在内核中,CONFIG_UEVENT_HELPER=y的设定可以确保uevent helper程序能够被编译到内核中,从而能够正常地接收并响应uevent事件。

path to uevent helper配置为/sbin/mdev;即指定uevent helper程序为/sbin/mdev

具体参考:《dev下无法生成节点的分析思路和解决方法及原理》。

1.4.4 支持RAM块设备

如果我们需要使用ramdisk根文件系统,就需要配置RAM块设备驱动,否者不用配置这个。

ram disk顾名思义,内存磁盘。我们平常接触的一些存储介质,如:Nor FlashNand FlasheMMC、ufs、以及机械硬盘固态硬盘等,都是用来存储数据的,同理内存也是可以当成磁盘来存储数据的,唯一不同的就是ram是掉电不保存的,而前面提到的那些存储介质掉电都是保存数据的。

我们都知道,在linux中,上面介绍的Flash这些存储介质,都是需要有对应的驱动,注册成块设备。并向上层提供接口。如NorNand等都抽象成mtdblock块设备。

同理,linux中的ram disk意思就是拿出ram中的一部分大小,用对应的驱动注册层块设备,提供给上层调用。一般的ram disk注册成的块设备,在文件系统中设备节点体现为/dev/ramx

我们需要配置内核支持ramdisk

General setup  ---> 
    [*] Initial RAM filesystem and RAM disk (initramfs/initrd) support

这里的意思是配置内核初始化时,去寻找initramfsinitrdinitrd就是我们的ramdisk文件系统,至于initramfs,是一个可以编译进内核的根文件映像。

Device Drivers  ---> 
     [*] Block devices  --->
          <*>   RAM block device support
          (1)     Default number of RAM disks
        (327680) Default RAM disk size (kbytes)    

勾选第一项RAM block device support,才会出来第二项和第三项的选项;

  • 第一项的意思是内核支持ram disk块设备驱动;
  • 第二项是注册的块设备数量,内核默认是16,我这里就写了1,内核启动后会有/dev/ram0块设备节点,配置16则会有/dev/ram0~ram15
  • 第三项的意思是ram disk占得最大内存,这里我写的是32M,一般来说可以写的再小点。实际在使用/dev/ramx设备时,并不是一下子就占系统32M内存,而是根据实际使用情况而分配的;
1.4.5 支持ext4文件系统

由于后面我们会制作ext4类型的根文件系统,因此需要勾选对ext4文件系统格式的支持,内核默认已经勾选了。

File systems --->
     -*- The Extended 4 (ext4) filesystem     
     [*]  Ext4 POSIX Access Control Lists
     [  ]  Ext4 Security Labels                                                                         
     [  ]  Ext4 debugging support   
1.4.6 配置DRM驱动

DRM,全称Direct Rending Manger。是目前Linux主流的图形显示框架。配置这个的目的是为了在移植了带有桌面的ubuntu根文件系统后,通过显示器来显示ubuntu桌面;

Device Drivers  ---> 
    Graphics support  --->     
        [*] Direct Rendering Manager (XFree86 4.1.0 and higher DRI support)  ---> 
        [*] DRM Support for Rockchip
1.4.7 配置有线网卡驱动

RK3399内置了以太网控制器,所以只要搭配一颗以太网PHY芯片就可以实现以太网功能。这里我使用的NanoPC-T4开发板搭载的以太网PHY芯片型号为RTL8211E-VB-CG,是Realtek瑞昱推出的一款高集成的网络接收PHY芯片,它符合10Base-T100Base-TX1000Base-T IEEE802.3标准,该芯片在网络通信中属于物理层,用于MACPHY之间的数据通信。

10/100M以太网PHYMAC之间的接口主要有MIIRMII,而10/100/1000M以太网PHYMAC之间的接口主要有RGMII。而RK3399RMIIRGMII接口均支持,也就是说RK3399支持100M网卡、1000M网卡。

因此我们需要配置:

Device Drivers  ---> 
  [*] Network device support  --->
     [*] Ethernet driver support  --->    
        [*] STMicroelectronics 10/100/1000/EQOS Ethernet driver     
            [*] STMMAC Platform bus support
                [*] Rockchip dwmac support

需要注意RTL8211和我们之前接触的DM9000有很大的差异:

  • 功能不同:RTL8211E是一款物理层网络接口芯片,主要负责物理层的处理和传输;而DM9000是一款以太网控制器芯片,主要负责数据链路层的处理和管理;
  • 高度不同:RTL8211E是一个高度集成化的单芯片方案,可直接与处理器连接,不需要外部存储器;DM9000则需要和存储器结合使用,并且需要外部时钟电路的支持;
  • 传输速率不同:RTL8211E支持千兆以太网(1000Mbps)和百兆以太网(100Mbps)的数据传输,而DM9000只支持百兆以太网(100Mbps);
  • 接口不同:RTL8211E采用RGMII接口标准,而DM9000采用MII接口标准;
  • 芯片封装不同:RTL8211E常见的封装形式是QFN,很容易焊接到PCB上;而DM9000常见的封装形式是QFP,需要进行插件式安装;
1.4.8 配置无线网卡驱动

我使用的NanoPC-T4开发板搭载的WiFi模组为AP6356,符合IEEE802.11a/b/g/n/ac 2x2 MIMO标准,并可在802.11n中通过双流达到867Mbps的速度以连接无线局域网。该集成模块提供SDIO/PCIe接口用于WiFiUART/PCM接口用于蓝牙。

由于linux 5.2.8版本并没有直接支持AP6356,因此需要对源码和设备树进行大量调整,就不在这里介绍了。如果需要支持无线网卡的话具体参考:《Rockchip RK3399 - WiFi AP6356驱动》。

屏蔽内核启动开机logo

Device Drivers  --->
    Graphics support  --->
        [] Bootup logo --->
1.4.10 保存配置

保存配置:

存档:

root@zhengyang:/work/sambashare/rk3399/linux-5.2.8# mv rk3399_defconfig ./arch/arm64/configs/

重新配置内核:

root@zhengyang:/work/sambashare/rk3399/linux-5.2.8# make rk3399_defconfig

1.5 修改设备树

1.5.1 配置display-subsystem设备节点

执行如下命令:

root@zhengyang:/work/sambashare/rk3399/linux-5.2.8# vim arch/arm64/boot/dts/rockchip/rk3399.dtsi

将以下节点:

display-subsystem {
        compatible = "rockchip,display-subsystem";
        ports = <&vopl_out>, <&vopb_out>;
};

修改为:

display_subsystem: display-subsystem {
        compatible = "rockchip,display-subsystem";
        ports = <&vopl_out>, <&vopb_out>;
};
1.5.2 配置gmac设备节点

arch/arm64/boot/dts/rockchip/rk3399-evb.dts中为以下节点新增属性:

&gmac {
	assigned-clock-parents = <&clkin_gmac>;
	assigned-clocks = <&cru SCLK_RMII_SRC>;
	clock_in_out = "input";
	pinctrl-names = "default";
	pinctrl-0 = <&rgmii_pins>, <&phy_intb>, <&phy_rstb>;
	phy-handle = <&rtl8211e>;
	phy-mode = "rgmii";
	phy-supply = <&vcc3v3_s3>;
	tx_delay = <0x28>;
	rx_delay = <0x11>;
	status = "okay";

	mdio {
		compatible = "snps,dwmac-mdio";
		#address-cells = <1>;
		#size-cells = <0>;

		rtl8211e: ethernet-phy@1 {
			reg = <1>;
			interrupt-parent = <&gpio3>;
			interrupts = <RK_PB2 IRQ_TYPE_LEVEL_LOW>;
			reset-assert-us = <10000>;
			reset-deassert-us = <30000>;
			reset-gpios = <&gpio3 RK_PB7 GPIO_ACTIVE_LOW>;
		};
	};
};

同时配置引脚配置节点:

&pinctrl {
	......

	gmac {
		phy_intb: phy-intb {
			rockchip,pins = <3 RK_PB2 RK_FUNC_GPIO &pcfg_pull_up>;
		};

		phy_rstb: phy-rstb {
			rockchip,pins = <3 RK_PB7 RK_FUNC_GPIO &pcfg_pull_none>;
		};
	};
	.....
}
1.5.2 新增子节点属性

同时在arch/arm64/boot/dts/rockchip/rk3399-evb.dts中为以下节点新增属性:

&i2c7 {
        status = "okay";
};

&display_subsystem {
         status = "okay";
};

&vopl {
        status = "okay";
};

&vopl_mmu {
        status = "okay";
};
     
&vopb {
        status = "okay";
};

&vopb_mmu {
        status = "okay";
};

&hdmi {
        ddc-i2c-bus = <&i2c7>;
        pinctrl-names = "default";
        pinctrl-0 = <&hdmi_cec>;
        status = "okay";
};

1.6 编译内核

1.6.1 编译内核

linux内核根目录下执行如下命令:

root@zhengyang:/work/sambashare/rk3399/linux-5.2.8# make -j8
  HOSTCC  scripts/extract-cert
scripts/extract-cert.c:21:10: fatal error: openssl/bio.h: 没有那个文件或目录
   21 | #include <openssl/bio.h>
      |          ^~~~~~~~~~~~~~~
compilation terminated.
make[1]: *** [scripts/Makefile.host:95:scripts/extract-cert] 错误 1
make: *** [Makefile:1204:scripts] 错误 2
make: *** 正在等待未完成的任务....

如果出现如上错误,安装libssl-dev即可:

root@zhengyang:/work/sambashare/rk3399/linux-5.2.8# sudo apt install libssl-dev

这个编译属实有点久,如果可以的话,尽量裁切掉一些没用的驱动。

编译完成后会在linux根目录下生成vmlinux文件,elf格式:这个文件属实有点大214M,真是惊呆了;

root@zhengyang:/work/sambashare/rk3399/linux-5.2.8# ll vmlinux
-rwxr-xr-x 1 root root 223823704 May 23 21:00 vmlinux*
root@zhengyang:/work/sambashare/rk3399/linux-5.2.8# du -sh vmlinux
214M    vmlinux

此外,在arch/arm64/boot/文件夹下生成Image镜像文件,以及设备树dst/rockchip/xxx.dtb文件;

root@zhengyang:/work/sambashare/rk3399/linux-5.2.8# ll arch/arm64/boot
总用量 44032
drwxr-xr-x  3 root root     4096 May 16 11:28 ./
drwxr-xr-x 14 root root     4096 May 16 11:15 ../
drwxr-xr-x 33 root root     4096 May 16 10:27 dts/
-rw-r--r--  1 root root       55 May 16 10:27 .gitignore
-rw-r--r--  1 root root 33616384 May 16 11:27 Image
-rw-r--r--  1 root root      134 May 16 11:27 .Image.cmd
-rw-r--r--  1 root root 11617499 May 16 11:28 Image.gz
-rw-r--r--  1 root root      101 May 16 11:28 .Image.gz.cmd
-rw-r--r--  1 root root     1562 May 16 10:27 install.sh
-rw-r--r--  1 root root      960 May 16 10:27 Makefile
root@zhengyang:/work/sambashare/rk3399/linux-5.2.8/arch/arm64# cd arch/arm64/boot
root@zhengyang:/work/sambashare/rk3399/linux-5.2.8/arch/arm64/boot# ls dts/rockchip/*rk3399*.dtb
dts/rockchip/rk3399-evb.dtb              dts/rockchip/rk3399-gru-scarlet-kd.dtb  dts/rockchip/rk3399-rock960.dtb
dts/rockchip/rk3399-ficus.dtb            dts/rockchip/rk3399-nanopc-t4.dtb       dts/rockchip/rk3399-rock-pi-4.dtb
dts/rockchip/rk3399-firefly.dtb          dts/rockchip/rk3399-nanopi-m4.dtb       dts/rockchip/rk3399-rockpro64.dtb
dts/rockchip/rk3399-gru-bob.dtb          dts/rockchip/rk3399-nanopi-neo4.dtb     dts/rockchip/rk3399-roc-pc.dtb
dts/rockchip/rk3399-gru-kevin.dtb        dts/rockchip/rk3399-orangepi.dtb        dts/rockchip/rk3399-sapphire.dtb
dts/rockchip/rk3399-gru-scarlet-inx.dtb  dts/rockchip/rk3399-puma-haikou.dtb     dts/rockchip/rk3399-sapphire-excavator.dtb

除此之外在arch/arm64/boot/路径下也生成了Image.gz文件,需要注意的是该文件和zImage文件并不一样,该文件并没有内嵌gzip解压缩代码,就是单纯的gzip压缩后的文件。

1.6.2 编译dts

如果需要单独编译设备树,在linux内核根目录执行如下命令:

root@zhengyang:/work/sambashare/rk3399/linux-5.2.8# make  arch/arm64/boot/dts/rockchip/rk3399-evb.dts  dtbs V=1

即可把arch/arm64/boot/dts/rockchip里的dts文件编译成dtb文件。

二、内核镜像文件介绍

linux内核常用的镜像文件格式有以下几种:

  • vmlinuxlinux内核编译出来的原始的内核文件,elf格式。该文件包含了符号表、重定位等信息,可以用来调试,但不能直接引导linux系统启动;可以使用arm-linx-readelf -h vmlinux查看文件信息;
  • Imagelinux内核编译时,使用arm-linux-objcopy处理vmlinux后生成的二进制文件。该文件是raw binary二进制文件,bin文件是将elf文件中的代码段,数据段,还有一些自定义的段抽取出来做成的一个内存的镜像,可直接引导linux系统启动;
  • Image.gz:使用gzip压缩Image后得到的压缩文件;
  • zImage:首先将Image.gz文件开头嵌入gzip解压缩代码,然后编译得到elf格式文件arch/arm/boot/compressed/vmlinux,最后使用arm-linux-objcopy命令对其处理生成的linux内核镜像;
  • uImageuImage又分为两种Legacy uImageFIT uImage。以Legacy uImage为例,其在zImage前面增加一个64字节的头,描述镜像文件类型,加载位置,大小等信息,比如我们熟悉的Mini2440使用的就是这种;

下图展示了不同类型的linux镜像生成过程:

2.1 vmlinux

linux根目录下的.vmlinux.cmd文件可以看到vmlinux的生成命令:

root@zhengyang:/work/sambashare/rk3399/linux-5.2.8# cat .vmlinux.cmd
cmd_vmlinux := /bin/bash scripts/link-vmlinux.sh arm-linux-ld  -EL  -maarch64elf --no-undefined -X --fix-cortex-a53-843419  --build-id ;  true

2.2 Image

linux根目录下的arch/arm64/boot/.Image.cmd文件可以看到vmlinux的生成命令:

cmd_arch/arm64/boot/Image := arm-linux-objcopy  -O binary -R .note -R .note.gnu.build-id -R .comment -S vmlinux arch/arm64/boot/Image

这里利用arm-linux-objcopy命令将elf格式的vmlinux文件转换为二进制文件Image。其中 :

  • -O binary:指定输出的文件格式为二进制文件;
  • -R note:从输出文件中删除掉所有名为.note的段;
  • -R .note.gnu.build-id:从输出文件中删除掉所有名为.not.build-id的段;
  • -R .comment:从输出文件中删除掉所有名为.comment的段;
  • -S:不从源文件中复制重定位信息和符号信息到目标文件中;

2.3 Image.gz

linux根目录下的arch/arm64/boot/.Image.gz.cmd文件可以看到vmlinux的生成命令:

root@zhengyang:/work/sambashare/rk3399/linux-5.2.8# cat arch/arm64/boot/.Image.gz.cmd
cmd_arch/arm64/boot/Image.gz := cat arch/arm64/boot/Image | gzip -n -f -9 > arch/arm64/boot/Image.gz

这里使用gzip文件对Image文件进行压缩,处理得到Image.gz文件;其中:

  • -n:压缩文件时,不保存原来的文件名称及时间戳记;
  • -f:强行压缩文件。不理会文件名称或硬连接是否存在以及该文件是否为符号连接;
  • -9:表示高压缩比,多在创建压缩包时使用;

2.4 zImage

arm64arch/arm64/boot路径下并没有生成zImage文件,但是我们在为Mini2440开发板编译内核时,在arch/arm/boot/路径下是生成了zImage文件的。

其编译命令位于arch/arm/boot/.zImage.cmd文件:

root@zhengyang:/work/sambashare/linux-5.2.8-dt# cat arch/arm/boot/.zImage.cmd
cmd_arch/arm/boot/zImage := arm-linux-objcopy -O binary -R .comment -S  arch/arm/boot/compressed/vmlinux arch/arm/boot/zImage

可以看到这里利用arm-linux-objcopy命令将arch/arm/boot/compressed/vmlinux转换为二进制位zImage。那问题来了arch/arm/boot/compressed/vmlinux是哪里来的呢?

arch/arm/boot/compressed/vmlinux的生成包括两步:

(1) 使用gzip命令将Image进行压缩得到piggy_data文件;

root@zhengyang:/work/sambashare/linux-5.2.8-dt# cat arch/arm/boot/compressed/.piggy_data.cmd
cmd_arch/arm/boot/compressed/piggy_data := cat arch/arm/boot/compressed/../Image | gzip -n -f -9 > arch/arm/boot/compressed/piggy_data

(2) 将piggy_data文件开头嵌入gzip解压缩代码编译得到elf格式文件arch/arm/boot/compressed/vmlinux

root@zhengyang:/work/sambashare/linux-5.2.8-dt# cat arch/arm/boot/compressed/.vmlinux.cmd
cmd_arch/arm/boot/compressed/vmlinux := arm-linux-ld  -EL  \
 --defsym _kernel_bss_size=202320 \
 --defsym zreladdr=0x30008000 \
 --no-undefined  \
 -X  \
 -T arch/arm/boot/compressed/vmlinux.lds \
 arch/arm/boot/compressed/head.o  \
 arch/arm/boot/compressed/piggy.o  \
 arch/arm/boot/compressed/misc.o  \
 arch/arm/boot/compressed/decompress.o  \
 arch/arm/boot/compressed/string.o  \
 arch/arm/boot/compressed/lib1funcs.o \ 
 arch/arm/boot/compressed/ashldi3.o  \
 arch/arm/boot/compressed/bswapsdi2.o  \
 -o arch/arm/boot/compressed/vmlinux

这里是将压缩的内核镜像数据(arch/arm/boot/compressed/piggy_data.piggydata段中,并定义符号 input_datainput_data_end引用该段的开始和结束地址,具体实现在arch/arm/boot/compressed/piggy.S文件,该文件会被编译成arch/arm/boot/compressed/piggy.o

/* SPDX-License-Identifier: GPL-2.0 */
        .section .piggydata,#alloc
        .globl  input_data
input_data:
        .incbin "arch/arm/boot/compressed/piggy_data"
        .globl  input_data_end
input_data_end:

其中,.section指令用于切换当前段至.piggydata段,#alloc表示告知编译器该段需要分配内存,input_datainput_data_end为符号名,用于访问所在段的起始地址和结束地址。最关键的是.incbin "arch/arm/boot/compressed/piggy_data" 指令,它用于将arch/arm/boot/compressed/piggy_data中的二进制数据按字节复制到.piggydata段中,从而实现内核镜像数据的加载。

内核镜像数据的解压是在arch/arm/boot/compressed/misc.c文件实现的:

extern char input_data[];
extern char input_data_end[];
extern int do_decompress(u8 *input, int len, u8 *output, void (*error)(char *x));

void
decompress_kernel(unsigned long output_start, unsigned long free_mem_ptr_p,
                unsigned long free_mem_ptr_end_p,
                int arch_id)
{
        int ret;

        output_data             = (unsigned char *)output_start;
        free_mem_ptr            = free_mem_ptr_p;
        free_mem_end_ptr        = free_mem_ptr_end_p;
        __machine_arch_type     = arch_id;

        arch_decomp_setup();

        putstr("Uncompressing Linux...");
        ret = do_decompress(input_data, input_data_end - input_data,
                            output_data, error);
        if (ret)
                error("decompressor returned an error");
        else
                putstr(" done, booting the kernel.\n");
}

在链接arch/arm/boot/compressed/vmlinux.lds中将段.piggydata链接在后面:

OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
  /DISCARD/ : {
    *(.ARM.exidx*)
    *(.ARM.extab*)
    *(.data)
  }
  . = 0;
  _text = .;
  .text : {
    _start = .;
    *(.start)
    *(.text)
    *(.text.*)
    *(.fixup)
    *(.gnu.warning)
    *(.glue_7t)
    *(.glue_7)
  }
  .table : ALIGN(4) {
    _table_start = .;
    LONG((2))
    LONG((0x5a534c4b))
    LONG((__piggy_size_addr - _start))
    LONG((_kernel_bss_size))
    LONG(0)
    _table_end = .;
  }
  .rodata : {
    *(.rodata)
    *(.rodata.*)
    *(.data.rel.ro)
  }
  .piggydata : {
    *(.piggydata)
    __piggy_size_addr = . - 4;
  }
  . = ALIGN(4);
  _etext = .;
  .got.plt : { *(.got.plt) }
  _got_start = .;
  .got : { *(.got) }
  _got_end = .;
  .pad : { BYTE(0); . = ALIGN(8); }
  _edata = .;
  .image_end (NOLOAD) : {
    _edata_real = .;
  }
  _magic_sig = (0x016f2818);
  _magic_start = (_start);
  _magic_end = (_edata);
  _magic_table = (_table_start - _start);
  . = ALIGN(8);
  __bss_start = .;
  .bss : { *(.bss) }
  _end = .;
  . = ALIGN(8);
  .stack : { *(.stack) }
  PROVIDE(__pecoff_data_size = ALIGN(512) - ADDR(.data));
  PROVIDE(__pecoff_end = ALIGN(512));
  .stab 0 : { *(.stab) }
  .stabstr 0 : { *(.stabstr) }
  .stab.excl 0 : { *(.stab.excl) }
  .stab.exclstr 0 : { *(.stab.exclstr) }
  .stab.index 0 : { *(.stab.index) }
  .stab.indexstr 0 : { *(.stab.indexstr) }
  .comment 0 : { *(.comment) }
}
ASSERT(_edata_real == _edata, "error: zImage file size is incorrect");

2.5 uImage

arm64arch/arm64/boot路径下并没有生成uImage文件。但是我们在为Mini2440开发板编译内核时,在arch/arm/boot/路径下是生成了uImage文件的,更具体的说是Legacy uImage

其编译命令位于arch/arm/boot/.uImage.cmd文件:

root@zhengyang:/work/sambashare/linux-5.2.8-dt# cat arch/arm/boot/.uImage.cmd
cmd_arch/arm/boot/uImage := /bin/bash ./scripts/mkuboot.sh    \
-A arm \           // 架构
-O linux \         // 操作系统
-C none  \         // 压缩类型为none,这样uboot就不会对镜像文件进行解压操作了;解压工作交由内核镜像头部的gzip解压代码完成
-T kernel \        // 镜像类型
-a 0x30008000 \    // 加载地址
-e 0x30008000 \    // 入口地址
-n 'Linux-5.2.8' \  // 镜像名称
-d arch/arm/boot/zImage arch/arm/boot/uImage // 输入文件

这里使用使用./scripts/mkuboot.sh脚本来制作uImage,最终调用mkimage工具来制作uImage

#!/bin/bash
# SPDX-License-Identifier: GPL-2.0

#
# Build U-Boot image when `mkimage' tool is available.
#

MKIMAGE=$(type -path "${CROSS_COMPILE}mkimage")

if [ -z "${MKIMAGE}" ]; then
        MKIMAGE=$(type -path mkimage)
        if [ -z "${MKIMAGE}" ]; then
                # Doesn't exist
                echo '"mkimage" command not found - U-Boot images will not be built' >&2
                exit 1;
        fi
fi

# Call "mkimage" to create U-Boot image
${MKIMAGE} "$@"
2.5.1 加载/入口地址等

在内核移植中我们经常提到load addressentry pointbootm addresskernel运行地址,那这些地址有什么关系;

  • load address:加载地址load_addr ,由mkimage -a 指定;
  • entry point: 入口地址entry_point,由mkimage -e指定;
  • bootm addressbootm命令后面紧跟的地址,也就是加载地址addr
  • kernel运行地址:zImage自解压后将kernel解压到实际运行的物理地址;

所谓加载地址是指bootm将内核镜像文件拷贝到内存空间的位置,入口地址是加载地址确定后bootm从内核镜像文件中开始执行的地址。

理论上zImage转化为uImage需要添加0x40长度的header头, 所以entry_point == load_addr + 0x40

2.5.2 bootmuImage处理

(1) addr != load_addr

addr所在地址提取取出内核镜像头部信息中进行处理, 并将去掉头部的内核直接加载到-a指定的地址load_addr

然后进入load_addr进行内核引导(entry_point)。所以此时地址:addr != load_addr == entry_point

(2) addr == load_addr

也就是说bootm后的地址等于加载地址,即已经将内核镜像(带头部)加载到-a指定的内存地址load_addr中。

此时:addr == load_addr == entry_point - 0x40;

2.5.3 制作镜像

Mini2440开发板为例,制作镜像头以及下载地址有以下两种情况:

(1) 情景一(内核linux-5.2.8使用)

mkimage -n 'Linux-5.2.8'  -A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008000 -d zImage uImage

加载地址和入口地址相同:load_addr == entry_point,烧录和启动命令;

tftp 0x31000000 uImage
bootm 0x31000000
# Starting kernel...之前有内核搬移(从0x31000040->0x0x30008000),并从0x30008000启动

下载地址可以任意放,也可以指定为0x30008000,此时也进行了内核搬移,从0x30008040->0x0x30008000

(2) 情景二

mkimage -n  'Linux-5.2.8' -A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008040 -d zImage uImage

入口地址在加载地址后面64个字节:entry_point == load_addr + 0x40;烧录和启动命令;

tftp 0x30008000 uImage
bootm 0x30008000
# 没有内核搬移,从从0x30008040启动

下载地址一定要在指定的加载地址上。

三、boot启动命令解析

当我们在uboot命令行执行了boot命令时,uboot会获取bootcmd环境变量的内容,然后执行bootcmd中保存的启动命令。接下来我们来分析一下bootcmd默认配置。

3.1 bootcmd

uboot include/configs/evb_rk3399.h文件中配置了默认的启动命令:

#ifndef CONFIG_SPL_BUILD
#undef CONFIG_BOOTCOMMAND
#define CONFIG_BOOTCOMMAND RKIMG_BOOTCOMMAND
#endif

RKIMG_BOOTCOMMAND定义在include/configs/rockchip-common.h文件中:

#if defined(CONFIG_AVB_VBMETA_PUBLIC_KEY_VALIDATE)
#define RKIMG_BOOTCOMMAND                       \
        "boot_android ${devtype} ${devnum};"
#elif defined(CONFIG_FIT_SIGNATURE)
#define RKIMG_BOOTCOMMAND                       \
        "boot_fit;"
#else
#define RKIMG_BOOTCOMMAND                       \        #  这里这个会生效
        "boot_android ${devtype} ${devnum};"    \
        "boot_fit;"                             \
        "bootrkp;"                              \
        "run distro_bootcmd;"
#endif

可以看到:

  • 如果配置了CONFIG_AVB_VBMETA_PUBLIC_KEY_VALIDATE,则执行命令boot_android \${devtype} \${devnum}
  • 如果配置了CONFIG_FIT_SIGNATURE,则执行命令boot_fitboot_fit命令是用来执行FIT uImage的;
  • 由于前两个我们都没有配置,因此执行的命令是最后一个;

我们在uboot命令行执行print也可以看到cmdline环境变量的值:

bootcmd=boot_android ${devtype} ${devnum};boot_fit;bootrkp;run distro_bootcmd;

如果后面实验环境我们烧录的内核镜像是boot.img,那么其内核镜像格式既不是Android,也不是FIT uImage,因此前两种启动方式都会失败,我们从内核启动日志可以看到:

=> boot
Could not find misc partition
ANDROID: reboot reason: "(none)"
No valid android hdr
Android image load failed
Android boot failed, error -1.
## Booting FIT Image FIT: No fit blob
do_boot_fit 1
FIT: No FIT image

3.2 boot_fit启动方式

boot_fit启动方式使用的内核镜像为FIT uImage。由于Rockchip官方提供的uboot在启动方式上做了大量魔改,尤其是bootm命令:

do_bootm  // cmd/bootm.c
    // 支持
    //   - Android: bootm [aosp addr]
    //     - FIT:     bootm [fit addr]
    //   - uImage:  bootm [uimage addr]
    board_do_bootm(argc,argv)    // arch/arm/mach-rockchip/board.c   Rockchip自己扩充的,用于从不同位置加载内核镜像
        // 获取内核镜像格式  支持三种Android、FIT uImage、以及Legacy uImage;不同的内核镜像格式,处理逻辑不一样
        format = (genimg_get_format(img))  //common/image.c
        // 当镜像格式为FIT uImage时,会走下面的代码
        bootm_image_populate_dtb(img)     
            rockchip_ram_read_dtb_file(img, (void *)gd->fdt_blob)   // arch/arm/mach-rockchip/boot_rkimg.c
                // 如果配置了CONFIG_ROCKCHIP_RESOURCE_IMAGE,这节点名字就是resource,否者就是fdt
                // 关于resource节点指向的文件,应该是Rockchip自己定义的资源文件格式,相关代码位于arch/arm/mach-rockchip/resource_img.c
                // 我们以fdt节点为例介绍,获取/images/fdt节点偏移
                noffset = fdt_path_offset(img,"/images/fdt)     // lib/libfdt/fdt_ro.c 207
                //获取fdt节点data属性以及指定的dtb文件的大小             
                fit_image_get_data(img, noffset, &data, &size) // common/image-fit.c:900
                    // 获取"data"属性(返回的是指针)以及dtb文件的长度 即/incbin/("rk3399-evb.dtb")
                    *data = fdt_getprop(fit, noffset, FIT_DATA_PROP, &len);
                    // 获取"data-size"属性,从而得dtb文件
                    fit_image_get_data_size(fit, noffset, &len)
                    // 获取"data-offset"属性,从而得到dtb文件的偏移
                    fit_image_get_data_offset(fit, noffset, (int *)&data_off)
                    // 获取"data-position"属性,该属性并不存在
                    fit_image_get_data_position(fit, noffset, (int *)&data_pos)
        
                fdt_check_header(data)
                memcpy(fdt, data, size)
                sysmem_alloc_base(MEM_FDT, (phys_addr_t)fdt,ALIGN(fdt_totalsize(fdt), RK_BLK_SIZE) + CONFIG_SYS_FDT_PAD)
        run_command(boot_cmd, 0)    // boot_cmd设置为"boot_fit <addr>" 执行bootcmd中的命令,相当于执行boot_fit  addr命令
                                   
    do_bootm_states(....)   

这里为了适配各种镜像格式,Rockchip自己实现了一个board_do_bootm的逻辑,尤其是bootm_image_populate_dtb里面又搞了一些幺蛾子,而且我对这段逻辑也没啥兴趣,所以干脆我就放弃使用bootm命令去启动内核了,直接采用boot_fit命令启动FIT uImage镜像,其启动命令格式如下

=> help boot_fit
boot_fit - Boot FIT Image from memory or boot/recovery partition

Usage:
boot_fit boot_fit [addr]

其实现位于cmd/bootfit.c以及arch/arm/mach-rockchip/fit.c等文件,阅读代码我发现几处比较有意思的地方。

当我们把FIT uImage下载到内存某个地址,比如0x10000000,运行boot_fit 0x10000000启动内核,该命令会执行do_boot_fit函数,函数位于cmd/bootfit.c

static int do_boot_fit(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
{
        char *bootm_args[1];
        char fit_addr[12];
        ulong size;
        void *fit;
        int ret;

        if (argc > 2)
                return CMD_RET_USAGE;

        printf("## Booting FIT Image ");

        if (argc == 1)
                fit = do_boot_fit_storage(&size);
        else
                fit = do_boot_fit_ram(argv, &size);

        if (!fit) {
                FIT_I("No FIT image\n");
                goto out;
        }

        if (fdt_check_header(fit)) {
                FIT_I("Invalid FIT format\n");
                goto out;
        }

        /* fixup entry/load and alloc sysmem */
        if (fit_image_pre_process(fit)){
                goto out;
        }

        env_set("bootm-no-reloc", "y");
        snprintf(fit_addr, sizeof(fit_addr), "0x%lx", (ulong)fit);
        bootm_args[0] = fit_addr;

        printf("at %s with size 0x%08lx\n", fit_addr, size);

        ret = do_bootm_states(NULL, 0, ARRAY_SIZE(bootm_args), bootm_args,
                BOOTM_STATE_START |
                BOOTM_STATE_FINDOS | BOOTM_STATE_FINDOTHER |
                BOOTM_STATE_LOADOS |
#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH
                BOOTM_STATE_RAMDISK |
#endif
                BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO |
                BOOTM_STATE_OS_GO, &images, 1);

        if (ret && argc != 1) {
                fit_image_fail_process(fit);
                goto out;
        }

        return CMD_RET_SUCCESS;
out:
        return CMD_RET_FAILURE;
}

如果boot_fit命令只有1个参数:比如boot_fit ,则从eMMC存储设备加载FIT uImage并启动 。

如果boot_fit命令有2个参数: 比如boot_fit <load_addr> ,则从RAM启动;采用boot_fit <load_addr>需要先将FIT-uImage加载到内存,然后根据传入的加载地址启动内核。

3.2.1 支持fit启动

支持从fit启动,需要进行如下配置:

(1) 取消CONFIG_ROCKCHIP_RESOURCE_IMAGE配置,不使用Rockchip resource镜像; 配置CONFIG_ROCKCHIP_FIT_IMAGE,即使用fit镜像;

ARM architecture   --->
	[ ] Enable support for rockchip resource image
	[*] Enable support for FIT image 

(2) 配置CONFIG_CMD_BOOT_FITCONFIG_CMD_BOOT_FIT依赖CONFIG_ROCKCHIP_FIT_IMAGE

Command line interface  --->
	Device access commands   --->
		 -*-  boot_fit        

cmd/Makefile中定义有:

obj-$(CONFIG_CMD_BOOT_FIT) += bootfit.o

include/image.h定义有:

#define IMAGE_ENABLE_FIT     CONFIG_IS_ENABLED(FIT)   // 配置了CONFIG_FIT
3.2.2 do_boot_fit_storage

这里我们看一下do_boot_fit_storage函数是如何从eMMC存储设备加载FIT uImage镜像到内存的 。

fit = do_boot_fit_storage(&size);  //  cmd/bootfit.c
    fit_image_load_bootables(size)    // arch/arm/mach-rockchip/fit.c
        // 获取启动设备
        dev_desc = rockchip_get_bootdev();
        fit = fit_get_blob(dev_desc, &part, false)
             // 根据分区名获取分区信息 part_name = PART_BOOT 即"boot"  
             // 分区名称是写在gpt分区表中的,开始扇区0x8000,占用扇区数量0x38000 扇区大小112MB
              part_get_info_by_name(dev_desc, part_name, &part)   // disk/part.c
              // 除法运算,向上取整 应该为 1 
              blk_num = DIV_ROUND_UP(sizeof(struct fdt_header), dev_desc->blksz);
			  // 分配一个512字节大小,地址是ARCH_DMA_MINALIGN的倍数的内存块
              fdt = memalign(ARCH_DMA_MINALIGN, blk_num * dev_desc->blksz);
			  // 读取第一个扇区数据到fdt,地址范围0x00000000~0x00000027表示的是fdt_header结构体的成员信息
			  blk_dread(dev_desc, part.start, blk_num, fdt);
              // 首先获取totalsize->totalsize,即计算kernel.its文件打包后在kernel.itb中所占的大小,由于我们编译指定了-E属性,因此这里计算的是不包含image data file文件的大小,更准确的说应该是fdt blob的大小
              blk_num = DIV_ROUND_UP(fdt_totalsize(fdt), dev_desc->blksz);
			  // 分配一个blk_num*dev_desc->blksz大小,地址是ARCH_DMA_MINALIGN的倍数的内存块
              fit = memalign(ARCH_DMA_MINALIGN, blk_num * dev_desc->blksz);
         	  // 将fdt blob(不包含image)加载到内存地址fit
	          blk_dread(dev_desc, part.start, blk_num, fit);   

经过分析我们可以推断出: 采用boot_fit命令启动内核,必须要设置GPT分区, 默认boot_fit命令从eMMCboot分区加载FIT-uImage实际使用到的镜像文件到内存,然后启动内核 。

3.2.3 fit_image_pre_process

函数fit_image_pre_process用于对FIT uImage进行预处理:包括检查其格式是否合法、修正镜像元素的入口地址和加载地址等信息,并且在必要时分配系统内存。

fit_image_pre_process(fit)   // arch/arm/mach-rockchip/fit.c
    fit_image_fixup_alloc(fit, FIT_FDT_PROP,"fdt_addr_r", MEM_FDT)  // 检查fdt节点
        // 从环境变量获取fdt_addr_r地址  环境变量默认配置的fdt_addr_r=0x08300000
        addr = env_get_ulong(addr_name, 16, 0); // addr_name="fdt_addr_r"
        // 获取fdt节点data-offset属性值保存到offset变量 
        // 获取fdt节点data-size属性值保存到size变量 
        // 获取fdt镜像加载到内存的地址,保存到load变量 
        fit_image_get_param(fit, prop_name, &load, &offset, &size)  // prop_name="fdt"
            fdt_image_get_offset_size(fit, prop_name, offset, size)
                // 获取fdt节点
                noffset = fit_default_conf_get_node(fit, prop_name);
                // 获取"data-size"属性,保存到sz
                fit_image_get_data_size(fit, noffset, &sz)
                // 获取"data-position"属性 没有该属性
                ret = fit_image_get_data_position(fit, noffset, &offs);
                if (!ret)
                    offs -= fdt_totalsize(fit);
                else
                    // 获取"data-offset"属性,保存到offs
                    ret = fit_image_get_data_offset(fit, noffset, &offs);
            // 获取fdt镜像在内存中的地址,保存到load
            fdt_image_get_load(fit, prop_name, load)            
        // 修正fdt的入口地址和加载地址
        fix_image_set_addr(fit, prop_name, load, addr);  // 入参:old=load, new=addr(环境变量中的地址)            
            /* do not fix if verified-boot */
            if (!fit_image_addr_is_placeholder(old) ||  // 默认不使用环境变量中的地址修正,就使用its文件文件中配置的load、entry地址
                fit_sig_require_conf(fit, gd_fdt_blob()))  
                return 0;

            // 设置fit节点entry属性值为new
            fit_image_set_entry(fit, noffset, new);  // common/image-fit.c
            // 设置load节点entry属性值为new
            fit_image_set_load(fit, noffset, new);

        sysmem_alloc_base(mem, (phys_addr_t)addr,ALIGN(size, RK_BLK_SIZE))                
    fit_image_fixup_alloc(fit, FIT_KERNEL_PROP,"kernel_addr_r", MEM_KERNEL) // 检查kernel节点
    fit_image_fixup_alloc(fit, FIT_RAMDISK_PROP,"ramdisk_addr_r", MEM_RAMDISK) // 检查ramdisk节点
    
do_bootm_states(....);

从这段代码中不难看出FIT uImage必须满足以下条件:

  • 必须要有ramdiskkernelfdt节点;(这里就很烦人,ramdisk你限制个屁,为此我还去做了ramdisk镜像);
  • 校验kernelramdiskftddata-offsetdata-size属性,这个就导致在使用tools/mkimage工具生成FIT uImage镜像的时候必须要指定-E参数,不然生成的FIT uImage是不会有这两个属性的;

上面有一块内容我们需要单独提一下,就是啥时候我们会使用环境变量中的fdt_addr_rkernel_addr_rramdisk_addr_r值去修正its文件中配置的loadentry地址。具体需要看一下fit_image_addr_is_placeholder函数实现;

#define FIT_PLACEHOLDER_ADDR            0xffffff00

static inline int fit_image_addr_is_placeholder(ulong addr)
{
        return (addr & 0xffffff00) == FIT_PLACEHOLDER_ADDR;
}

可以看到如果addr满足(addr & 0xffffff00) == 0xffffff00时该函数就会返回true,这样就会执行fit_image_set_entryfit_image_set_load函数进行修正its文件中配置的loadentry地址。

3.3 distro boot启动方式

有关distro boot启动方式的内容就比较多了,但这不是我们这一节的重点,我们大概看一下distro_bootcmd命令的内容:

distro_bootcmd=for target in ${boot_targets}; do run bootcmd_${target}; done 

有关bistro boot启动相关的环境变量定义在include/config_distro_bootcmd.h文件中,有兴趣可以自行研究。

View Code
cat include/config_distro_bootcmd.h
/*
 * (C) Copyright 2014
 * NVIDIA Corporation <www.nvidia.com>
 *
 * Copyright 2014 Red Hat, Inc.
 *
 * SPDX-License-Identifier:     GPL-2.0+
 */

#ifndef _CONFIG_CMD_DISTRO_BOOTCMD_H
#define _CONFIG_CMD_DISTRO_BOOTCMD_H

/*
 * A note on error handling: It is possible for BOOT_TARGET_DEVICES to
 * reference a device that is not enabled in the U-Boot configuration, e.g.
 * it may include MMC in the list without CONFIG_CMD_MMC being enabled. Given
 * that BOOT_TARGET_DEVICES is a macro that's expanded by the C pre-processor
 * at compile time, it's not  possible to detect and report such problems via
 * a simple #ifdef/#error combination. Still, the code needs to report errors.
 * The best way I've found to do this is to make BOOT_TARGET_DEVICES expand to
 * reference a non-existent symbol, and have the name of that symbol encode
 * the error message. Consequently, this file contains references to e.g.
 * BOOT_TARGET_DEVICES_references_MMC_without_CONFIG_CMD_MMC. Given the
 * prevalence of capitals here, this looks like a pre-processor macro and
 * hence seems like it should be all capitals, but it's really an error
 * message that includes some other pre-processor symbols in the text.
 */

#define BOOTENV_SHARED_BLKDEV_BODY(devtypel) \
                "if " #devtypel " dev ${devnum}; then " \
                        "setenv devtype " #devtypel "; " \
                        "run scan_dev_for_boot_part; " \
                "fi\0"

#define BOOTENV_SHARED_BLKDEV(devtypel) \
        #devtypel "_boot=" \
        BOOTENV_SHARED_BLKDEV_BODY(devtypel)

#define BOOTENV_DEV_BLKDEV(devtypeu, devtypel, instance) \
        "bootcmd_" #devtypel #instance "=" \
                "setenv devnum " #instance "; " \
                "run " #devtypel "_boot\0"

#define BOOTENV_DEV_NAME_BLKDEV(devtypeu, devtypel, instance) \
        #devtypel #instance " "

#ifdef CONFIG_SANDBOX
#define BOOTENV_SHARED_HOST     BOOTENV_SHARED_BLKDEV(host)
#define BOOTENV_DEV_HOST        BOOTENV_DEV_BLKDEV
#define BOOTENV_DEV_NAME_HOST   BOOTENV_DEV_NAME_BLKDEV
#else
#define BOOTENV_SHARED_HOST
#define BOOTENV_DEV_HOST \
        BOOT_TARGET_DEVICES_references_HOST_without_CONFIG_SANDBOX
#define BOOTENV_DEV_NAME_HOST \
        BOOT_TARGET_DEVICES_references_HOST_without_CONFIG_SANDBOX
#endif

#ifdef CONFIG_CMD_MMC
#define BOOTENV_SHARED_MMC      BOOTENV_SHARED_BLKDEV(mmc)
#define BOOTENV_DEV_MMC         BOOTENV_DEV_BLKDEV
#define BOOTENV_DEV_NAME_MMC    BOOTENV_DEV_NAME_BLKDEV
#else
#define BOOTENV_SHARED_MMC
#define BOOTENV_DEV_MMC \
        BOOT_TARGET_DEVICES_references_MMC_without_CONFIG_CMD_MMC
#define BOOTENV_DEV_NAME_MMC \
        BOOT_TARGET_DEVICES_references_MMC_without_CONFIG_CMD_MMC
#endif

#ifdef CONFIG_CMD_UBIFS
#define BOOTENV_SHARED_UBIFS \
        "ubifs_boot=" \
                "if ubi part UBI && ubifsmount ubi${devnum}:boot; then "  \
                        "setenv devtype ubi; "                            \
                        "setenv bootpart 0; "                             \
                        "run scan_dev_for_boot; "                         \
                "fi\0"
#define BOOTENV_DEV_UBIFS       BOOTENV_DEV_BLKDEV
#define BOOTENV_DEV_NAME_UBIFS  BOOTENV_DEV_NAME_BLKDEV
#else
#define BOOTENV_SHARED_UBIFS
#define BOOTENV_DEV_UBIFS \
        BOOT_TARGET_DEVICES_references_UBIFS_without_CONFIG_CMD_UBIFS
#define BOOTENV_DEV_NAME_UBIFS \
        BOOT_TARGET_DEVICES_references_UBIFS_without_CONFIG_CMD_UBIFS
#endif

#ifdef CONFIG_EFI_LOADER
#if defined(CONFIG_ARM64)
#define BOOTEFI_NAME "bootaa64.efi"
#elif defined(CONFIG_ARM)
#define BOOTEFI_NAME "bootarm.efi"
#endif
#endif

#ifdef BOOTEFI_NAME
#if defined(CONFIG_ARM) && !defined(CONFIG_ARM64)
/*
 * On 32bit ARM systems there is a reasonable number of systems that follow
 * the $soc-$board$boardver.dtb name scheme for their device trees. Use that
 * scheme if we don't have an explicit fdtfile variable.
 */
#define BOOTENV_EFI_SET_FDTFILE_FALLBACK                                  \
        "if test -z \"${fdtfile}\" -a -n \"${soc}\"; then "               \
          "setenv efi_fdtfile ${soc}-${board}${boardver}.dtb; "           \
        "fi; "
#else
#define BOOTENV_EFI_SET_FDTFILE_FALLBACK
#endif

#define BOOTENV_SHARED_EFI                                                \
        "boot_efi_binary="                                                \
                "load ${devtype} ${devnum}:${distro_bootpart} "           \
                        "${kernel_addr_r} efi/boot/"BOOTEFI_NAME"; "      \
                "if fdt addr ${fdt_addr_r}; then "                        \
                        "bootefi ${kernel_addr_r} ${fdt_addr_r};"         \
                "else "                                                    \
                        "bootefi ${kernel_addr_r} ${fdtcontroladdr};"     \
                "fi\0"                                                    \
        \
        "load_efi_dtb="                                                   \
                "load ${devtype} ${devnum}:${distro_bootpart} "           \
                        "${fdt_addr_r} ${prefix}${efi_fdtfile}\0"         \
        \
        "efi_dtb_prefixes=/ /dtb/ /dtb/current/\0"                        \
        "scan_dev_for_efi="                                               \
                "setenv efi_fdtfile ${fdtfile}; "                         \
                BOOTENV_EFI_SET_FDTFILE_FALLBACK                          \
                "for prefix in ${efi_dtb_prefixes}; do "                  \
                        "if test -e ${devtype} "                          \
                                        "${devnum}:${distro_bootpart} "   \
                                        "${prefix}${efi_fdtfile}; then "  \
                                "run load_efi_dtb; "                      \
                        "fi;"                                             \
                "done;"                                                   \
                "if test -e ${devtype} ${devnum}:${distro_bootpart} "     \
                                        "efi/boot/"BOOTEFI_NAME"; then "  \
                                "echo Found EFI removable media binary "  \
                                        "efi/boot/"BOOTEFI_NAME"; "       \
                                "run boot_efi_binary; "                   \
                                "echo EFI LOAD FAILED: continuing...; "   \
                "fi; "                                                    \
                "setenv efi_fdtfile\0"
#define SCAN_DEV_FOR_EFI "run scan_dev_for_efi;"
#else
#define BOOTENV_SHARED_EFI
#define SCAN_DEV_FOR_EFI
#endif

#ifdef CONFIG_SATA
#define BOOTENV_SHARED_SATA     BOOTENV_SHARED_BLKDEV(sata)
#define BOOTENV_DEV_SATA        BOOTENV_DEV_BLKDEV
#define BOOTENV_DEV_NAME_SATA   BOOTENV_DEV_NAME_BLKDEV
#else
#define BOOTENV_SHARED_SATA
#define BOOTENV_DEV_SATA \
        BOOT_TARGET_DEVICES_references_SATA_without_CONFIG_SATA
#define BOOTENV_DEV_NAME_SATA \
        BOOT_TARGET_DEVICES_references_SATA_without_CONFIG_SATA
#endif

#ifdef CONFIG_SCSI
#define BOOTENV_RUN_SCSI_INIT "run scsi_init; "
#define BOOTENV_SET_SCSI_NEED_INIT "setenv scsi_need_init; "
#define BOOTENV_SHARED_SCSI \
        "scsi_init=" \
                "if ${scsi_need_init}; then " \
                        "setenv scsi_need_init false; " \
                        "scsi scan; " \
                "fi\0" \
        \
        "scsi_boot=" \
                BOOTENV_RUN_SCSI_INIT \
                BOOTENV_SHARED_BLKDEV_BODY(scsi)
#define BOOTENV_DEV_SCSI        BOOTENV_DEV_BLKDEV
#define BOOTENV_DEV_NAME_SCSI   BOOTENV_DEV_NAME_BLKDEV
#else
#define BOOTENV_RUN_SCSI_INIT
#define BOOTENV_SET_SCSI_NEED_INIT
#define BOOTENV_SHARED_SCSI
#define BOOTENV_DEV_SCSI \
        BOOT_TARGET_DEVICES_references_SCSI_without_CONFIG_SCSI
#define BOOTENV_DEV_NAME_SCSI \
        BOOT_TARGET_DEVICES_references_SCSI_without_CONFIG_SCSI
#endif

#ifdef CONFIG_IDE
#define BOOTENV_SHARED_IDE      BOOTENV_SHARED_BLKDEV(ide)
#define BOOTENV_DEV_IDE         BOOTENV_DEV_BLKDEV
#define BOOTENV_DEV_NAME_IDE    BOOTENV_DEV_NAME_BLKDEV
#else
#define BOOTENV_SHARED_IDE
#define BOOTENV_DEV_IDE \
        BOOT_TARGET_DEVICES_references_IDE_without_CONFIG_IDE
#define BOOTENV_DEV_NAME_IDE \
        BOOT_TARGET_DEVICES_references_IDE_without_CONFIG_IDE
#endif

#if defined(CONFIG_DM_PCI)
#define BOOTENV_RUN_NET_PCI_ENUM "run boot_net_pci_enum; "
#define BOOTENV_SHARED_PCI \
        "boot_net_pci_enum=pci enum\0"
#else
#define BOOTENV_RUN_NET_PCI_ENUM
#define BOOTENV_SHARED_PCI
#endif

#ifdef CONFIG_CMD_USB
#define BOOTENV_RUN_NET_USB_START "run boot_net_usb_start; "
#define BOOTENV_SHARED_USB \
        "boot_net_usb_start=usb start\0" \
        "usb_boot=" \
                "usb start; " \
                BOOTENV_SHARED_BLKDEV_BODY(usb)
#define BOOTENV_DEV_USB         BOOTENV_DEV_BLKDEV
#define BOOTENV_DEV_NAME_USB    BOOTENV_DEV_NAME_BLKDEV
#else
#define BOOTENV_RUN_NET_USB_START
#define BOOTENV_SHARED_USB
#define BOOTENV_DEV_USB \
        BOOT_TARGET_DEVICES_references_USB_without_CONFIG_CMD_USB
#define BOOTENV_DEV_NAME_USB \
        BOOT_TARGET_DEVICES_references_USB_without_CONFIG_CMD_USB
#endif

#if defined(CONFIG_CMD_DHCP)
#if defined(CONFIG_EFI_LOADER)
#if defined(CONFIG_ARM64)
#define BOOTENV_EFI_PXE_ARCH "0xb"
#define BOOTENV_EFI_PXE_VCI "PXEClient:Arch:00011:UNDI:003000"
#elif defined(CONFIG_ARM)
#define BOOTENV_EFI_PXE_ARCH "0xa"
#define BOOTENV_EFI_PXE_VCI "PXEClient:Arch:00010:UNDI:003000"
#elif defined(CONFIG_X86)
/* Always assume we're running 64bit */
#define BOOTENV_EFI_PXE_ARCH "0x7"
#define BOOTENV_EFI_PXE_VCI "PXEClient:Arch:00007:UNDI:003000"
#else
#error Please specify an EFI client identifier
#endif

/*
 * Ask the dhcp server for an EFI binary. If we get one, check for a
 * device tree in the same folder. Then boot everything. If the file was
 * not an EFI binary, we just return from the bootefi command and continue.
 */
#define BOOTENV_EFI_RUN_DHCP \
        "setenv efi_fdtfile ${fdtfile}; "                                 \
        BOOTENV_EFI_SET_FDTFILE_FALLBACK                                  \
        "setenv efi_old_vci ${bootp_vci};"                                \
        "setenv efi_old_arch ${bootp_arch};"                              \
        "setenv bootp_vci " BOOTENV_EFI_PXE_VCI ";"                       \
        "setenv bootp_arch " BOOTENV_EFI_PXE_ARCH ";"                     \
        "if dhcp ${kernel_addr_r}; then "                                 \
                "tftpboot ${fdt_addr_r} dtb/${efi_fdtfile};"              \
                "if fdt addr ${fdt_addr_r}; then "                        \
                        "bootefi ${kernel_addr_r} ${fdt_addr_r}; "        \
                "else "                                                   \
                        "bootefi ${kernel_addr_r} ${fdtcontroladdr};"     \
                "fi;"                                                     \
        "fi;"                                                             \
        "setenv bootp_vci ${efi_old_vci};"                                \
        "setenv bootp_arch ${efi_old_arch};"                              \
        "setenv efi_fdtfile;"                                             \
        "setenv efi_old_arch;"                                            \
        "setenv efi_old_vci;"
#else
#define BOOTENV_EFI_RUN_DHCP
#endif
#define BOOTENV_DEV_DHCP(devtypeu, devtypel, instance) \
        "bootcmd_dhcp=" \
                BOOTENV_RUN_NET_USB_START \
                BOOTENV_RUN_NET_PCI_ENUM \
                "if dhcp ${scriptaddr} ${boot_script_dhcp}; then " \
                        "source ${scriptaddr}; " \
                "fi;" \
                BOOTENV_EFI_RUN_DHCP \
                "\0"
#define BOOTENV_DEV_NAME_DHCP(devtypeu, devtypel, instance) \
        "dhcp "
#else
#define BOOTENV_DEV_DHCP \
        BOOT_TARGET_DEVICES_references_DHCP_without_CONFIG_CMD_DHCP
#define BOOTENV_DEV_NAME_DHCP \
        BOOT_TARGET_DEVICES_references_DHCP_without_CONFIG_CMD_DHCP
#endif

#if defined(CONFIG_CMD_DHCP) && defined(CONFIG_CMD_PXE)
#define BOOTENV_DEV_PXE(devtypeu, devtypel, instance) \
        "bootcmd_pxe=" \
                BOOTENV_RUN_NET_USB_START \
                BOOTENV_RUN_NET_PCI_ENUM \
                "dhcp; " \
                "if pxe get; then " \
                        "pxe boot; " \
                "fi\0"
#define BOOTENV_DEV_NAME_PXE(devtypeu, devtypel, instance) \
        "pxe "
#else
#define BOOTENV_DEV_PXE \
        BOOT_TARGET_DEVICES_references_PXE_without_CONFIG_CMD_DHCP_or_PXE
#define BOOTENV_DEV_NAME_PXE \
        BOOT_TARGET_DEVICES_references_PXE_without_CONFIG_CMD_DHCP_or_PXE
#endif

#define BOOTENV_DEV_NAME(devtypeu, devtypel, instance) \
        BOOTENV_DEV_NAME_##devtypeu(devtypeu, devtypel, instance)
#define BOOTENV_BOOT_TARGETS \
        "boot_targets=" BOOT_TARGET_DEVICES(BOOTENV_DEV_NAME) "\0"

#define BOOTENV_DEV(devtypeu, devtypel, instance) \
        BOOTENV_DEV_##devtypeu(devtypeu, devtypel, instance)
#define BOOTENV \
        BOOTENV_SHARED_HOST \
        BOOTENV_SHARED_MMC \
        BOOTENV_SHARED_PCI \
        BOOTENV_SHARED_USB \
        BOOTENV_SHARED_SATA \
        BOOTENV_SHARED_SCSI \
        BOOTENV_SHARED_IDE \
        BOOTENV_SHARED_UBIFS \
        BOOTENV_SHARED_EFI \
        "boot_prefixes=/ /boot/\0" \
        "boot_scripts=boot.scr.uimg boot.scr\0" \
        "boot_script_dhcp=boot.scr.uimg\0" \
        BOOTENV_BOOT_TARGETS \
        \
        "boot_extlinux="                                                  \
                "sysboot ${devtype} ${devnum}:${distro_bootpart} any "    \
                        "${scriptaddr} ${prefix}extlinux/extlinux.conf\0" \
        \
        "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\0"                                                    \
        \
        "boot_a_script="                                                  \
                "load ${devtype} ${devnum}:${distro_bootpart} "           \
                        "${scriptaddr} ${prefix}${script}; "              \
                "source ${scriptaddr}\0"                                  \
        \
        "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\0"                                                  \
        \
        "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_EFI                                          \
                "\0"                                                      \
        \
        "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\0"                                                  \
        \
        BOOT_TARGET_DEVICES(BOOTENV_DEV)                                  \
        \
        "distro_bootcmd=" BOOTENV_SET_SCSI_NEED_INIT                      \
                "for target in ${boot_targets}; do "                      \
                        "run bootcmd_${target}; "                         \
                "done\0"

#ifndef CONFIG_BOOTCOMMAND
#define CONFIG_BOOTCOMMAND "run distro_bootcmd"
#endif

#endif  /* _CONFIG_CMD_DISTRO_BOOTCMD_H */

最终distro boot启动机制会从我们的eMMCboot分区加载内核镜像,然后启动内核,因此需要确保GPT分区表没有问题,不然uboot是无法找到boot分区的内核镜像的。

而根路径的挂载是由内核完成的,内核通过启动命令中配置的root=PARTUUID=B921B045-1D找到根文件系统所在的分区(块设备号为179:5、即/dev/mmcblk0p5),然后将根路径挂载到/dev/mmcblk0p5设备节点。

[    1.856037] EXT4-fs (mmcblk0p5): mounted filesystem with ordered data mode. Opts: (null)
[    1.865144] VFS: Mounted root (ext4 filesystem) on device 179:5.
[    1.873579] devtmpfs: mounted

四、distro boot启动内核镜像制作

Rockchp官方uboot给出的使用教程就是使用distro boot方式启动内核,其需要我们制作一个包含内核镜像和设备树的启动分区。

4.1 目录结构

创建一个名为boot的文件夹:

root@zhengyang:/work/sambashare/rk3399/linux-5.2.8# mkdir boot

然后,将设备树二进制文件复制到该文件夹中,这里我们和uboot移植一样,选择官方的评估板对应的设备树文件rk3399-evb.dtb

root@zhengyang:/work/sambashare/rk3399/linux-5.2.8# cp arch/arm64/boot/dts/rockchip/rk3399-evb.dtb boot/rk3399.dtb

接着,将内核镜像复制到该文件夹中:

root@zhengyang:/work/sambashare/rk3399/linux-5.2.8# cp arch/arm64/boot/Image boot/

为了实现distro引导,我们需要在启动分区中添加extlinux引导程序以及相应的配置文件。

我们需要创建一个名为extlinux的文件夹,在其中创建一个名为extlinux.conf的文件:

root@zhengyang:/work/sambashare/rk3399/linux-5.2.8# mkdir boot/extlinux
root@zhengyang:/work/sambashare/rk3399/linux-5.2.8# touch boot/extlinux/extlinux.conf

在所有这些文件都准备好之后,boot文件夹结构如下:

boot
├── extlinux
│   └── extlinux.conf
├── Image
└── rk3399-evb.dtb

4.2 extlinux.conf

extlinux.conf文件中编写配置信息,指定内核镜像和设备树的路径,以及其它启动参数。配置文件内容如下:

label rockchip-kernel-5.2.8
    kernel /Image
    fdt /rk3399.dtb
    append earlycon=uart8250,mmio32,0xff1a0000 console=ttyS2,1500000n8 root=PARTUUID=B921B045-1D rw rootwait rootfstype=ext4 init=/sbin/init
4.2.1 earlycon

这里最后一行实际上就是配置ubootbootargs参数,这里首先设置了:

earlycon=uart8250,mmio32,0xff1a0000 

ARM64kernel未建立console之前,使用earlycon,实现日志信息的打印,因此首先配置了earlycon:其中:

  • uart8250表示针对uart8250这个串口设备;
  • mio32表示内存I/O资源32位,0xff1a0000RK3399 UART2寄存器基地址,这里没有配置波特率,默认还是使用uboot中配置的波特率。
4.2.2 console

接着设置了控制台console,当RK3399 UART2控制台驱动注册之后,将会注销earlycon驱动,将内核输出的日志通过指定的console打印出来。

console=ttyS2,1500000n8

如果我们没有设置console,那么console将会使用tty0这个终端(tty0表示当前所使用虚拟终端的一个别名,系统所产生的信息会发送到该终端上。有关虚拟终端的信息,可以参考《linux驱动移植-tty架构)》,这将导致内核启动日志输出到如下这个位置将会停止(即无法通过串口输出):

[    0.015084] printk: console [tty0] enabled
[    0.019585] printk: bootconsole [uart8250] disabled

注意:我们需要根据自己的开发板配置串口。

4.2.3 root

指定根文件系统以及根文件系统类型;通过root属性指定根文件系统所在位置,通常这是一个必须明确设置的选项,格式如下:

root=字符串 rw rootwait rootfstype=ext4

rw表示可以对根文件系统进行读写,默认配置是只读;

rootwait表示在根文件系统就绪之前无限等待。主要用于等待那些反应速度较慢的异步检测的设备就绪(例如USB/MMC/FireWire)

rootfstype为根文件系统类型;

“字符串”可以采用如下几种形式:

  • XXxx:一个16进制数,其中XX是主设备号,xx是次设备号。例如/dev/sdc15(主设备号是8,次设备号是47),可以表示成082F;
  • /dev/nfs:表示使用nfsroot选项指定的NFS磁盘,仅在根文件系统位于NFS文件系统上的时候才可以使用,比如配置成root=/dev/nfs rw rootwait nfsroot=192.168.0.200:/work/nfs_root/ubuntu,vers=4 ip=192.168.0.105:192.168.0.200:192.168.0.1:255.255.255.0::eth0:off
  • /dev/disk:表示一块完整的无分区块设备。比如/dev/mmcblk0/dev/sdb
  • /dev/diskN:表示disk磁盘的第N个分区,这是最常见的写法,比如:/dev/mtdblock3/dev/mmcblk0p5/dev/sda2
  • PARTUUID=b921b045-1df0-41c3-af44-4c6f280d3fae:仅用于EFI/GPT格式的磁盘,表示分区表中UUID值为b921b045-1df0-41c3-af44-4c6f280d3fae的分区;
  • PARTUUID=SSSSSSSS-PP :仅用于传统的MSDOS分区表,SSSSSSSS是用16进制表示的32NT disk signaturePP是用16进制表示的分区号。比如:PARTUUID=97531ACF-02 可能相当于/dev/sda2
  • major:minor由一对十进制数组成,其中major是主设备号,minor是次设备号。例如/dev/sdc15(主设备号是8,次设备号是47),可以表示成8:47

uboot命令行下我们使用mmc part查看分区UUID

=> mmc part
Partition Map for MMC device 0  --   Partition Type: EFI
Part    Start LBA       End LBA         Name
        Attributes
        Type GUID
        Partition GUID
  1     0x00000040      0x00001f7f      "loader1"
        attrs:  0x0000000000000000
        type:   ebd0a0a2-b9e5-4433-87c0-68b6b72699c7
        guid:   8ef917d1-5bd0-2c4f-9a33-b225b21cf919
  2     0x00004000      0x00005fff      "loader2"
        attrs:  0x0000000000000000
        type:   ebd0a0a2-b9e5-4433-87c0-68b6b72699c7
        guid:   77877125-2374-ad40-8b02-609e37971c59
  3     0x00006000      0x00007fff      "trust"
        attrs:  0x0000000000000000
        type:   ebd0a0a2-b9e5-4433-87c0-68b6b72699c7
        guid:   b4b84b8a-f8ae-0443-b5af-3605b195c4c9
  4     0x00008000      0x0003ffff      "boot"
        attrs:  0x0000000000000004
        type:   ebd0a0a2-b9e5-4433-87c0-68b6b72699c7
        guid:   35219908-db08-c643-9133-2213191e57ff
  5     0x00040000      0x01d1efde      "rootfs"
        attrs:  0x0000000000000000
        type:   ebd0a0a2-b9e5-4433-87c0-68b6b72699c7
        guid:   b921b045-1df0-41c3-af44-4c6f280d3fae

我们发现rootfs分区对应的UUIDb921b045-1df0-41c3-af44-4c6f280d3fae,而我们上面配置的root=PARTUUID=B921B045-1D并不是上面我们介绍的任意一种方式,但是这个是Rockchip官方给的示例,运行也是没问题的。如果采用PARTUUID的方式。按照我们上面说的应该配置为:

root=PARTUUID=b921b045-1df0-41c3-af44-4c6f280d3fae

4.3 ext2镜像

使用genext2fs工具可以生成一个ext2文件系统镜像文件。在使用genext2fs命令时,需要指定生成文件系统的大小、块大小、inode大小等参数,以及输出的镜像文件名称和所在路径。

执行以下命令,将boot文件夹中的文件和目录打包成一个ext2文件系统镜像:

root@zhengyang:/work/sambashare/rk3399/linux-5.2.8# sudo apt-get install genext2fs
root@zhengyang:/work/sambashare/rk3399/linux-5.2.8# genext2fs -b 32768 -B $((64*1024*1024/32768)) -d boot/ -i 8192 -U boot.img
root@zhengyang:/work/sambashare/rk3399/linux-5.2.8# ll boot.img
-rw-r--r-- 1 root root 33554432 May 17 05:17 boot.img

其中:

  • -b :表示文件系统块的数量;
  • -B : 表示文件系统块的大小,单位为字节;
  • -d :从指定目录开始创建文件系统
  • -i 8192;每个inode使用的字节数;
  • -U:选项表示创建一个唯一的文件系统UUID

这里我们设置了32768个块,每个块大小为64*1024*1024/32768,所以文件系统总大小为64MB,足够放下boot文件夹内的所有文件。

五、FIT uImage镜像制作

我们在《Mini2440uboot移植流程之linux内核启动分析》中介绍过FIT uImage的制作流程,其通过一定语法和格式将一些需要使用到的镜像(例如kerneldtb以及文件系统)组合到一起生成一个image file

5.1 Image.gz

linux内核5.2.8版本的编译完成后会在arch/arm64/boot/文件夹下生成内核镜像文件Image,其中Image是内核镜像原生二进制文件,可以直接在芯片上运行的,由于Image文件较大,我们将Image压缩转换为Image.gz(内核编译默认也会生成这个文件):

cat arch/arm64/boot/Image | gzip -n -f -9 > arch/arm64/boot/Image.gz

5.2 创建its文件

因为mkimage是根据its文件中的描述来打包镜像生成itb文件(FIT uImage),所以首先需要制作一个its文件,在its文件中描述需要被打包的镜像,主要是kernel镜像,dtb文件,ramdisk镜像。

我们创建一个kernel.its文件,内容如下:

/*
 * Simple U-Boot uImage source file containing a single kernel and FDT blob
 */
/dts-v1/;

/ {
        description = "Simple image with single Linux kernel and FDT blob";
        #address-cells = <1>;

        images {

                kernel {
                        description = "Vanilla Linux kernel";
                        data = /incbin/("arch/arm64/boot/Image.gz");
                        type = "kernel";
                        arch = "arm64";
                        os = "linux";
                        compression = "gzip";
                        load = <0x280000>;
                        entry = <0x280000>;
                        hash-1 {
                                algo = "crc32";
                        };
                        hash-2 {
                                algo = "sha1";
                        };
                };

               fdt {
                        description = "Flattened Device Tree blob";
                        data = /incbin/("arch/arm64/boot/dts/rockchip/rk3399-evb.dtb");
                        type = "flat_dt";
                        arch = "arm64";
                        compression = "none";
                        load = <0x8300000>;
                        entry = <0x8300000>;
                        hash-1 {
                                algo = "crc32";
                        };
                        hash-2 {
                                algo = "sha1";
                        };
                };

                ramdisk {
                       description = "Ramdisk for project-x";
                       data = /incbin/("ramdisk.gz");
                       type = "ramdisk";
                       arch = "arm64";
                       os = "linux";
                       load = <00000000>;
                       entry = <00000000>;
                       compression = "gzip";
                       hash-1 {
                               algo = "crc32";
                       };
                };

        };

        configurations {
                default = "conf-1";
                conf-1 {
                        description = "Boot Linux kernel with FDT blob";
                        kernel = "kernel";
                        fdt = "fdt";
                        ramdisk = "ramdisk";
                };
        };
};

这里我们内核镜像使用的Image.gz文件,该文件是通过gzipImage进行压缩得到的,因此需要在kernel属性中指定compression,这样uboot才知道如何解压。

5.2.1 内核加载地址0x280000

这里有一点一定要注意:kernelloadentry地址一定要指定为0x280000;我最初将loadentry都指定为0x200000000x20080000,后来又尝试了0x200000uboot启动内核时都会卡在Starting kernel ...,就这个小小的问题我排查了好多天,说多了这都是泪啊;那我后来是怎么发现loadentry加载地址有问题的呢?

因为distro boot启动内核的方式(Rockchip官方推荐的)是可以成功启动内核的。所以我修改了arch/arm/lib/bootm.c文件,将images->epImage镜像的加载地址)、images->fd_addrfdt的加载地址)信息打印出来。

/* Subcommand: GO */
static void boot_jump_linux(bootm_headers_t *images, int flag)
{
#ifdef CONFIG_ARM64
        void (*kernel_entry)(void *fdt_addr, void *res0, void *res1,
                        void *res2);
        int fake = (flag & BOOTM_STATE_OS_FAKE_GO);
        int es_flag = 0;

#if defined(CONFIG_AMP)
        es_flag = arm64_switch_amp_pe(images);
#elif defined(CONFIG_ARM64_SWITCH_TO_AARCH32)
        es_flag = arm64_switch_aarch32(images);
#endif
        kernel_entry = (void (*)(void *fdt_addr, void *res0, void *res1,
                                void *res2))images->ep;

        printf("## Transferring control to Linux (at address %lx)...\n",
                (ulong) kernel_entry);
        printf("%s 0x%x=0x%x \n",__func__,images->ep,*((unsigned int *)images->ep));
        printf("%s 0x%x=0x%x \n",__func__,images->ep + 4,*((unsigned int *)(images->ep + 4) ));
                printf("%s 0x%x=0x%x \n",__func__,images->ep + 20,*((unsigned int *)(images->ep + 20) ));
        printf("%s 0x%x=0x%x \n",__func__,images->ft_addr,*((unsigned int *)images->ft_addr));
        printf("%s 0x%x=0x%x \n",__func__,((unsigned int *)images->ft_addr)+1,*((unsigned int *)images->ft_addr+1));
                printf("%s 0x%x=0x%x \n",__func__,((unsigned int *)images->ft_addr)+5,*((unsigned int *)images->ft_addr+5));

        bootstage_mark(BOOTSTAGE_ID_RUN_OS);

        announce_and_cleanup(images, fake);  // 这里面会输出Starting kernel

        if (!fake) {
#ifdef CONFIG_ARMV8_PSCI         // 不会走
                armv8_setup_psci();#endif
                do_nonsec_virt_switch();

                update_os_arch_secondary_cores(images->os.arch);

#ifdef CONFIG_ARMV8_SWITCH_TO_EL1      // 不会走
                armv8_switch_to_el2((u64)images->ft_addr, 0, 0, 0,
                                    (u64)switch_to_el1, ES_TO_AARCH64);
#else
                if ((IH_ARCH_DEFAULT == IH_ARCH_ARM64) &&
                    (images->os.arch == IH_ARCH_ARM)){           
                        armv8_switch_to_el2(0, (u64)gd->bd->bi_arch_number,
                                            (u64)images->ft_addr, es_flag,
                                            (u64)images->ep,
                                            ES_TO_AARCH32);
                }else {                // 走这里
                        armv8_switch_to_el2((u64)images->ft_addr, 0, 0, es_flag,
                                            images->ep,
                                            ES_TO_AARCH64);
                }
#endif
        }
 ......    // 后面是非CONFIG_ARM64成立时,才会走的代码
}

当采用distro boot方式启动内核时,这是当时打印出来的内容:

## Transferring control to Linux (at address 280000)...
boot_jump_linux 0x280000=0x91005a4d
boot_jump_linux 0x280004=0x14433fff
boot_jump_linux 0x280014=0x0
boot_jump_linux 0x8300000=0xedfe0dd0
boot_jump_linux 0x8300004=0xc00000
boot_jump_linux 0x8300014=0x11000000
Total: 5818.548 ms

Starting kernel ...

而采用boot_fit启动方式时,我配置的kernel.its文件kernel节点loadentry0x20000000时,打印出来的内容:

## Transferring control to Linux (at address 20000000)...
boot_jump_linux 0x20000000=0x91005a4d
boot_jump_linux 0x20000004=0x14433fff
boot_jump_linux 0x20000014=0x0
boot_jump_linux 0xf4606000=0xedfe0dd0
boot_jump_linux 0xf4606004=0xc00000
boot_jump_linux 0xf4606014=0x11000000

同时比对这两次启动输出的epfd_addr内容差异:我发现kernelfdt都成功加载到内存了,除了加载的地址不一样,但是内存的数据是完全一样的。

最开始我也没有往加载地址这方面去想,然后就开始百度,各种尝试。然而网上相关资料少之又少;后来我尝试了很多办法:

(1) 最初我想的是会不会是内核启动时卡住了,所以我就去定位内核日志缓冲区log_buf在内存的地址,然后去查看该内存的内容,但是该缓冲区并没有任何有用的数据;我就开始怀疑人生了,难道是我排查的方法有问题,在文章结尾我会记录这种方法;实际上现在回想起来,内核代码应该都没有执行到,直接卡在了armv8_switch_to_el2函数;该函数等后面我们有时间再研究;

(2) 然后又在想是不是启动参数bootargs配置的有问题,导致内核日志没有通过串口打印出来,专文为此我还去研究了一下earlycon控制台驱动和uart8250串口驱动;但是同样的bootargs配置,采用distro boot方式启动内核内核时时有日志输出的,所以就排除了这个原因;

(3) 后来我发现distro boot方式启动内核会从环境变量中获取kernel以及fdt的加载地址,我就想着要不按照这个改一下,后来尝试发现内核竟然真的可以启动了,撒花啊......,至于为啥一定要配置kernelload地址为0x280000,目前我还不知道原因,等后面有时间研究一下。

亲爱的读者和支持者们,自动博客加入了打赏功能,陆陆续续收到了各位老铁的打赏。在此,我想由衷地感谢每一位对我们博客的支持和打赏。你们的慷慨与支持,是我们前行的动力与源泉。

日期姓名金额
2023-09-06*源19
2023-09-11*朝科88
2023-09-21*号5
2023-09-16*真60
2023-10-26*通9.9
2023-11-04*慎0.66
2023-11-24*恩0.01
2023-12-30I*B1
2024-01-28*兴20
2024-02-01QYing20
2024-02-11*督6
2024-02-18一*x1
2024-02-20c*l18.88
2024-01-01*I5
2024-04-08*程150
2024-04-18*超20
2024-04-26.*V30
2024-05-08D*W5
2024-05-29*辉20
2024-05-30*雄10
2024-06-08*:10
2024-06-23小狮子666
2024-06-28*s6.66
2024-06-29*炼1
2024-06-30*!1
2024-07-08*方20
2024-07-18A*16.66
2024-07-31*北12
2024-08-13*基1
2024-08-23n*s2
2024-09-02*源50
2024-09-04*J2
2024-09-06*强8.8
2024-09-09*波1
2024-09-10*口1
2024-09-10*波1
2024-09-12*波10
2024-09-18*明1.68
2024-09-26B*h10
2024-09-3010
2024-10-02M*i1
2024-10-14*朋10
2024-10-22*海10
2024-10-23*南10
2024-10-26*节6.66
2024-10-27*o5
2024-10-28W*F6.66
2024-10-29R*n6.66
2024-11-02*球6
2024-11-021*鑫6.66
2024-11-25*沙5
2024-11-29C*n2.88
posted @   大奥特曼打小怪兽  阅读(6713)  评论(4编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
历史上的今天:
2022-05-16 linux驱动移植-USB主机控制器驱动
2019-05-16 Spring MVC -- 国际化
如果有任何技术小问题,欢迎大家交流沟通,共同进步

公告 & 打赏

>>

欢迎打赏支持我 ^_^

最新公告

程序项目代做,有需求私信(小程序、网站、爬虫、电路板设计、驱动、应用程序开发、毕设疑难问题处理等)。

了解更多

点击右上角即可分享
微信分享提示