Rockchip RK3399 - 移植linux 5.2.8
目录
----------------------------------------------------------------------------------------------------------------------------
开发板 :NanoPC-T4
开发板
eMMC
:16GB
LPDDR3
:4GB
显示屏 :15.6
英寸HDMI
接口显示屏
u-boot
:Rockchip
官方uboot 2017.09
linux
:5.2.8
----------------------------------------------------------------------------------------------------------------------------
在前面的章节,我们已经介绍了在RK3399
上面移植Rockchip
官方提供的uboot 2017.09
,这一节我们将移植linux 5.2.8
到RK3399
上。
一、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
是指定目标平台为arm64
,CROSS_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 Flash
、Nand Flash
、eMMC
、ufs、以及机械硬盘固态硬盘等,都是用来存储数据的,同理内存也是可以当成磁盘来存储数据的,唯一不同的就是ram
是掉电不保存的,而前面提到的那些存储介质掉电都是保存数据的。
我们都知道,在linux
中,上面介绍的Flash
这些存储介质,都是需要有对应的驱动,注册成块设备。并向上层提供接口。如Nor
、Nand
等都抽象成mtdblock
块设备。
同理,linux
中的ram disk
意思就是拿出ram
中的一部分大小,用对应的驱动注册层块设备,提供给上层调用。一般的ram disk
注册成的块设备,在文件系统中设备节点体现为/dev/ramx
。
我们需要配置内核支持ramdisk
:
General setup --->
[*] Initial RAM filesystem and RAM disk (initramfs/initrd) support
这里的意思是配置内核初始化时,去寻找initramfs
和initrd
,initrd
就是我们的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-T
,100Base-TX
和1000Base-T IEEE802.3
标准,该芯片在网络通信中属于物理层,用于MAC
与PHY
之间的数据通信。
10/100M
以太网PHY
和MAC
之间的接口主要有MII
和RMII
,而10/100/1000M
以太网PHY
和MAC
之间的接口主要有RGMII
。而RK3399
对RMII
、RGMII
接口均支持,也就是说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
接口用于WiFi
,UART/PCM
接口用于蓝牙。
由于linux 5.2.8
版本并没有直接支持AP6356
,因此需要对源码和设备树进行大量调整,就不在这里介绍了。如果需要支持无线网卡的话具体参考:《Rockchip RK3399 - WiFi AP6356
驱动》。
1.4.9 屏蔽开机logo
屏蔽内核启动开机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
内核常用的镜像文件格式有以下几种:
vmlinux
:linux
内核编译出来的原始的内核文件,elf
格式。该文件包含了符号表、重定位等信息,可以用来调试,但不能直接引导linux
系统启动;可以使用arm-linx-readelf -h vmlinux
查看文件信息;Image
:linux
内核编译时,使用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
内核镜像;uImage
:uImage
又分为两种Legacy uImage
和FIT 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
在arm64
的arch/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_data
和input_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_data
和input_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
在arm64
的arch/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 address
、entry point
、bootm address
、kernel
运行地址,那这些地址有什么关系;
load address
:加载地址load_addr
,由mkimage -a
指定;entry point
: 入口地址entry_point
,由mkimage -e
指定;bootm address
:bootm
命令后面紧跟的地址,也就是加载地址addr
;kernel
运行地址:zImage
自解压后将kernel
解压到实际运行的物理地址;
所谓加载地址是指bootm
将内核镜像文件拷贝到内存空间的位置,入口地址是加载地址确定后bootm
从内核镜像文件中开始执行的地址。
理论上zImage
转化为uImage
需要添加0x40
长度的header
头, 所以entry_point
== load_addr
+ 0x40
。
2.5.2 bootm
对uImage
处理
(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_fit
,boot_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_FIT
,CONFIG_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
命令从eMMC
中boot
分区加载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
必须满足以下条件:
- 必须要有
ramdisk
、kernel
、fdt
节点;(这里就很烦人,ramdisk
你限制个屁,为此我还去做了ramdisk
镜像); - 校验
kernel
、ramdisk
、ftd
的data-offset
、data-size
属性,这个就导致在使用tools/mkimage
工具生成FIT uImage
镜像的时候必须要指定-E
参数,不然生成的FIT uImage
是不会有这两个属性的;
上面有一块内容我们需要单独提一下,就是啥时候我们会使用环境变量中的fdt_addr_r
、kernel_addr_r
、ramdisk_addr_r
值去修正its
文件中配置的load
、entry
地址。具体需要看一下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_entry
、fit_image_set_load
函数进行修正its
文件中配置的load
、entry
地址。
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
启动机制会从我们的eMMC
中boot
分区加载内核镜像,然后启动内核,因此需要确保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
这里最后一行实际上就是配置uboot
的bootargs
参数,这里首先设置了:
earlycon=uart8250,mmio32,0xff1a0000
ARM64
在kernel
未建立console
之前,使用earlycon
,实现日志信息的打印,因此首先配置了earlycon
:其中:
uart8250
表示针对uart8250
这个串口设备;mio32
表示内存I/O
资源32
位,0xff1a0000
是RK3399 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
进制表示的32
位NT disk signature
,PP
是用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
分区对应的UUID
为b921b045-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
镜像制作
我们在《Mini2440
之uboot
移植流程之linux
内核启动分析》中介绍过FIT uImage
的制作流程,其通过一定语法和格式将一些需要使用到的镜像(例如kernel
、dtb
以及文件系统)组合到一起生成一个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
文件,该文件是通过gzip
对Image
进行压缩得到的,因此需要在kernel
属性中指定compression
,这样uboot
才知道如何解压。
5.2.1 内核加载地址0x280000
这里有一点一定要注意:kernel
的load
、entry
地址一定要指定为0x280000
;我最初将load
、entry
都指定为0x20000000
、0x20080000
,后来又尝试了0x200000
,uboot
启动内核时都会卡在Starting kernel
...,就这个小小的问题我排查了好多天,说多了这都是泪啊;那我后来是怎么发现load
、entry
加载地址有问题的呢?
因为distro boot
启动内核的方式(Rockchip
官方推荐的)是可以成功启动内核的。所以我修改了arch/arm/lib/bootm.c
文件,将images->ep
(Image
镜像的加载地址)、images->fd_addr
(fdt
的加载地址)信息打印出来。
/* 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
节点load
、entry
为0x20000000
时,打印出来的内容:
## 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
同时比对这两次启动输出的ep
、fd_addr
内容差异:我发现kernel
、fdt
都成功加载到内存了,除了加载的地址不一样,但是内存的数据是完全一样的。
最开始我也没有往加载地址这方面去想,然后就开始百度,各种尝试。然而网上相关资料少之又少;后来我尝试了很多办法:
(1) 最初我想的是会不会是内核启动时卡住了,所以我就去定位内核日志缓冲区log_buf
在内存的地址,然后去查看该内存的内容,但是该缓冲区并没有任何有用的数据;我就开始怀疑人生了,难道是我排查的方法有问题,在文章结尾我会记录这种方法;实际上现在回想起来,内核代码应该都没有执行到,直接卡在了armv8_switch_to_el2
函数;该函数等后面我们有时间再研究;
(2) 然后又在想是不是启动参数bootargs
配置的有问题,导致内核日志没有通过串口打印出来,专文为此我还去研究了一下earlycon
控制台驱动和uart8250
串口驱动;但是同样的bootargs
配置,采用distro boot
方式启动内核内核时时有日志输出的,所以就排除了这个原因;
(3) 后来我发现distro boot
方式启动内核会从环境变量中获取kernel
以及fdt
的加载地址,我就想着要不按照这个改一下,后来尝试发现内核竟然真的可以启动了,撒花啊......,至于为啥一定要配置kernel
的load
地址为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-30 | I*B | 1 |
2024-01-28 | *兴 | 20 |
2024-02-01 | QYing | 20 |
2024-02-11 | *督 | 6 |
2024-02-18 | 一*x | 1 |
2024-02-20 | c*l | 18.88 |
2024-01-01 | *I | 5 |
2024-04-08 | *程 | 150 |
2024-04-18 | *超 | 20 |
2024-04-26 | .*V | 30 |
2024-05-08 | D*W | 5 |
2024-05-29 | *辉 | 20 |
2024-05-30 | *雄 | 10 |
2024-06-08 | *: | 10 |
2024-06-23 | 小狮子 | 666 |
2024-06-28 | *s | 6.66 |
2024-06-29 | *炼 | 1 |
2024-06-30 | *! | 1 |
2024-07-08 | *方 | 20 |
2024-07-18 | A*1 | 6.66 |
2024-07-31 | *北 | 12 |
2024-08-13 | *基 | 1 |
2024-08-23 | n*s | 2 |
2024-09-02 | *源 | 50 |
2024-09-04 | *J | 2 |
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-26 | B*h | 10 |
2024-09-30 | 岁 | 10 |
2024-10-02 | M*i | 1 |
2024-10-14 | *朋 | 10 |
2024-10-22 | *海 | 10 |
2024-10-23 | *南 | 10 |
2024-10-26 | *节 | 6.66 |
2024-10-27 | *o | 5 |
2024-10-28 | W*F | 6.66 |
2024-10-29 | R*n | 6.66 |
2024-11-02 | *球 | 6 |
2024-11-021 | *鑫 | 6.66 |
2024-11-25 | *沙 | 5 |
2024-11-29 | C*n | 2.88 |

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
2022-05-16 linux驱动移植-USB主机控制器驱动
2019-05-16 Spring MVC -- 国际化