# 基于qemu-riscv从0开始构建嵌入式linux系统ch11-1. 向着linux Kernel出发!——加载引导 ### kernel编译 首先,我们添加linux内核源码到我们的项目中,内核官网:https://www.kernel.org/。这里我们选择5.10.42版本,一般来讲如果没有特殊需求选择一个比较新的longterm版本是比较稳妥可靠的。说起来linux kernel的编译是非常容易的,得益于linux内核良好的工程架构,只需要向编译脚本添加如下命令,即可生成Image文件。这里我们使用了riscv架构下的默认defconfig,kernel默认运行在riscv的S模式下。 ```shell if [ ! -d "$SHELL_FOLDER/output/linux_kernel" ]; then mkdir $SHELL_FOLDER/output/linux_kernel fi cd $SHELL_FOLDER/linux-5.10.42 make ARCH=riscv CROSS_COMPILE=$CROSS_PREFIX- defconfig make ARCH=riscv CROSS_COMPILE=$CROSS_PREFIX- -j16 cp $SHELL_FOLDER/linux-5.10.42/arch/riscv/boot/Image $SHELL_FOLDER/output/linux_kernel/Image ``` ### 制作文件系统映像 有了内核Image接下来只要将设备树文件和Image打包生成文件系统映像,就可以使用uboot加载到内存里引导执行了,接下来我们来一步步制作文件系统映像。 - 首先创建工作目录./rootfs/rootfs和./rootfs/bootfs,其中rootfs是将来存放根文件系统的,本节我们不添加任何文件到该目录,bootfs目录下存放用于uboot访问加载的映像,包括三个文件。内核Image、用于内核的设备树文件quard_star.dtb、用于uboot的Distro Boot Script文件。然后使用dd命令创建1G大小的文件rootfs.img,即为文件系统原始映像。 ```shell if [ ! -d "$SHELL_FOLDER/output/rootfs" ]; then mkdir $SHELL_FOLDER/output/rootfs fi if [ ! -d "$SHELL_FOLDER/output/rootfs/rootfs" ]; then mkdir $SHELL_FOLDER/output/rootfs/rootfs fi if [ ! -d "$SHELL_FOLDER/output/rootfs/bootfs" ]; then mkdir $SHELL_FOLDER/output/rootfs/bootfs fi cd $SHELL_FOLDER/output/rootfs if [ ! -f "$SHELL_FOLDER/output/rootfs/rootfs.img" ]; then dd if=/dev/zero of=rootfs.img bs=1M count=1024 fi ``` - 拷贝Image和dtb到bootfs ```shell cp $SHELL_FOLDER/output/linux_kernel/Image $SHELL_FOLDER/output/rootfs/bootfs/Image cp $SHELL_FOLDER/output/uboot/quard_star_uboot.dtb $SHELL_FOLDER/output/rootfs/bootfs/quard_star.dtb ``` - 创建文件quard_star_uboot.cmd,编写uboot的Distro Boot Script。这个脚本是uboot的脚本,你可以任务是uboot下的shell脚本,内容就三条命令,通过load加载文件系统第一个分区下的根目录内Image文件到内存0x80000000,加载文件系统第一个分区下的根目录内quard_star.dtb文件到内存0x82000000,然后使用booti命令引导boot程序,booti用于Image文件的boot,第一个参数是Image所在内存地址,第二个参数为内存文件系统地址(如果不需要可以传入'-'),第三个参数为设备树所在内存地址。 ``` load virtio 0:1 0x80000000 /Image load virtio 0:1 0x82000000 /quard_star.dtb booti 0x80000000 - 0x82000000 ``` - 使用uboot的mkimage工具将Distro Boot Script转换为可以执行的boot.scr文件,这个文件会在uboot的启动时自动在文件系统内搜索该文件名然后执行其中的命令。 ```shell $SHELL_FOLDER/u-boot-2021.07/tools/mkimage -A riscv -O linux -T script -C none -a 0 -e 0 -n "Distro Boot Script" -d $SHELL_FOLDER/dts/quard_star_uboot.cmd $SHELL_FOLDER/output/rootfs/bootfs/boot.scr ``` - OK,用到的文件都准备充分了,下一步我们编写脚本generate_rootfs.sh用于格式化rootfs.img内容。内容如下,这里稍微解释一下,losetup用于挂载$1到本地回环设备/dev/loop70,然后使用fdisk工具对/dev/loop70进行分区表构造生成两个分区,之后重新挂载到本地回环设备上会解析出两个分区块设备/dev/loop70p1和/dev/loop70p2,使用mkfs.vfat和mkfs.ext4分别格式化分区后,解除挂载,即完成了原始映像分区创建。这里要解释一下fdisk是交互式的命令工具,我们使用了echo -e分别将要输入的命令写入到脚本,"I\n$2\nw\n"即为依次输入‘I’,‘回车’,‘$2’,‘回车’,‘w’,‘回车’,对应的操作就是加载$2所执向的sfdisk脚本文件,安装该文件格式化分区,然后w写入分区并退出fdisk。 ```shell #!/bin/bash losetup -o 0 --sizelimit 1073741824 /dev/loop70 $1 -P echo -e "I\n$2\nw\n" | fdisk /dev/loop70 losetup -d /dev/loop70 sync echo "please wait 5s" sleep 5 losetup -o 0 --sizelimit 1073741824 /dev/loop70 $1 -P mkfs.vfat /dev/loop70p1 mkfs.ext4 /dev/loop70p2 losetup -d /dev/loop70 sync ``` - 创建文件sfdisk,内容如下,即定义loop70p1和loop70p2的分区起始页和大小 ``` label: dos label-id: 0x32a47f23 device: /dev/loop70 unit: sectors /dev/loop70p1 : start= 2048, size= 196608, type=c /dev/loop70p2 : start= 198656, size= 1898496, type=83 ``` - build.sh添加generate_rootfs.sh rootfs.img sfdisk即可完成分区格式化。pkexec用来提权,因为上述losetup、fdisk命令必须使用管理员权限才能运行。 ```shell pkexec $SHELL_FOLDER/build_rootfs/generate_rootfs.sh $SHELL_FOLDER/output/rootfs/rootfs.img $SHELL_FOLDER/build_rootfs/sfdisk ``` - 编写build_rootfs/build.sh文件用于拷贝文件到文件系统映像 ``` #!/bin/bash losetup -o 0 --sizelimit 1073741824 /dev/loop70 $1/rootfs.img -P if [ -d "$1/target" ]; then rm -rf $1/target fi mkdir $1/target mkdir $1/target/bootfs mkdir $1/target/rootfs mount /dev/loop70p1 $1/target/bootfs mount /dev/loop70p2 $1/target/rootfs cp -r $1/bootfs/* $1/target/bootfs/ cp -r $1/rootfs/* $1/target/rootfs/ sync echo "please wait 5s" sleep 5 umount $1/target/bootfs umount $1/target/rootfs losetup -d /dev/loop70 sync ``` - 同样添加./build_rootfs/build.sh rootfs完成文件拷贝。 ```shell pkexec $SHELL_FOLDER/build_rootfs/build.sh $SHELL_FOLDER/output/rootfs ``` ### 运行qemu 直接执行./run.sh nographic就看到这次uboot的引导命令执行成功,内核开始boot,输出如下: ``` U-Boot 2021.07 (Aug 08 2021 - 14:35:46 +0800) CPU: rv64imafdcsu Model: riscv-quard-star,qemu DRAM: 1 GiB Loading Environment from nowhere... OK In: uart0@10000000 Out: uart0@10000000 Err: uart0@10000000 Net: No ethernet found. Hit any key to stop autoboot: 0 Device 0: QEMU VirtIO Block Device Type: Hard Disk Capacity: 1024.0 MB = 1.0 GB (2097152 x 512) ... is now current device Scanning virtio 0:1... Found U-Boot script /boot.scr 178 bytes read in 3 ms (57.6 KiB/s) ## Executing script at 88000000 17709568 bytes read in 16 ms (1 GiB/s) 5247 bytes read in 2 ms (2.5 MiB/s) Moving Image from 0x80000000 to 0x80200000, end=8133a000 ## Flattened Device Tree blob at 82000000 Booting using the fdt blob at 0x82000000 Using Device Tree in place at 0000000082000000, end 000000008200447e Starting kernel ... [ 0.000000] Linux version 5.10.42 (xiaoming@xiaoming) (riscv64-unknown-linux-gnu-gcc (GCC) 11.1.0, GNU ld (GNU Binutils) 2.36.1) #208 SMP PREEMPT Sun Aug 8 14:35:52 CST 2021 [ 0.000000] OF: fdt: Ignoring memory range 0x80000000 - 0x80200000 [ 0.000000] efi: UEFI not found. [ 0.000000] Zone ranges: [ 0.000000] DMA32 [mem 0x0000000080200000-0x00000000bfffffff] [ 0.000000] Normal empty [ 0.000000] Movable zone start for each node [ 0.000000] Early memory node ranges [ 0.000000] node 0: [mem 0x0000000080200000-0x00000000bfffffff] [ 0.000000] Initmem setup node 0 [mem 0x0000000080200000-0x00000000bfffffff] [ 0.000000] software IO TLB: mapped [mem 0x00000000ba9f7000-0x00000000be9f7000] (64MB) [ 0.000000] SBI specification v0.2 detected [ 0.000000] SBI implementation ID=0x1 Version=0x9 [ 0.000000] SBI v0.2 TIME extension detected [ 0.000000] SBI v0.2 IPI extension detected [ 0.000000] SBI v0.2 RFENCE extension detected [ 0.000000] SBI v0.2 HSM extension detected [ 0.000000] riscv: ISA extensions acdfimsu [ 0.000000] riscv: ELF capabilities acdfim [ 0.000000] percpu: Embedded 17 pages/cpu s32744 r8192 d28696 u69632 [ 0.000000] Built 1 zonelists, mobility grouping on. Total pages: 258055 [ 0.000000] Kernel command line: root=/dev/vda2 rw console=ttyS0 [ 0.000000] Dentry cache hash table entries: 131072 (order: 8, 1048576 bytes, linear) [ 0.000000] Inode-cache hash table entries: 65536 (order: 7, 524288 bytes, linear) [ 0.000000] Sorting __ex_table... [ 0.000000] mem auto-init: stack:off, heap alloc:off, heap free:off [ 0.000000] Memory: 938312K/1046528K available (7144K kernel code, 3925K rwdata, 4096K rodata, 223K init, 342K bss, 108216K reserved, 0K cma-reserved) [ 0.000000] Virtual kernel memory layout: [ 0.000000] fixmap : 0xffffffcefee00000 - 0xffffffceff000000 (2048 kB) [ 0.000000] pci io : 0xffffffceff000000 - 0xffffffcf00000000 ( 16 MB) [ 0.000000] vmemmap : 0xffffffcf00000000 - 0xffffffcfffffffff (4095 MB) [ 0.000000] vmalloc : 0xffffffd000000000 - 0xffffffdfffffffff (65535 MB) [ 0.000000] lowmem : 0xffffffe000000000 - 0xffffffe03fe00000 (1022 MB) [ 0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=8, Nodes=1 [ 0.000000] rcu: Preemptible hierarchical RCU implementation. [ 0.000000] rcu: RCU debug extended QS entry/exit. [ 0.000000] Trampoline variant of Tasks RCU enabled. [ 0.000000] Tracing variant of Tasks RCU enabled. [ 0.000000] rcu: RCU calculated value of scheduler-enlistment delay is 25 jiffies. [ 0.000000] NR_IRQS: 64, nr_irqs: 64, preallocated irqs: 0 [ 0.000000] riscv-intc: 64 local interrupts mapped [ 0.000000] plic: plic@c000000: mapped 53 interrupts with 8 handlers for 16 contexts. [ 0.000000] random: get_random_bytes called from start_kernel+0x31c/0x490 with crng_init=0 [ 0.000000] riscv_timer_init_dt: Registering clocksource cpuid [0] hartid [0] [ 0.000000] clocksource: riscv_clocksource: mask: 0xffffffffffffffff max_cycles: 0x24e6a1710, max_idle_ns: 440795202120 ns [ 0.000293] sched_clock: 64 bits at 10MHz, resolution 100ns, wraps every 4398046511100ns [ 0.005668] Console: colour dummy device 80x25 [ 0.012791] Calibrating delay loop (skipped), value calculated using timer frequency.. 20.00 BogoMIPS (lpj=40000) [ 0.013077] pid_max: default: 32768 minimum: 301 [ 0.015827] Mount-cache hash table entries: 2048 (order: 2, 16384 bytes, linear) [ 0.015953] Mountpoint-cache hash table entries: 2048 (order: 2, 16384 bytes, linear) [ 0.064327] rcu: Hierarchical SRCU implementation. [ 0.067752] EFI services will not be available. [ 0.073230] smp: Bringing up secondary CPUs ... [ 0.107641] CPU7: failed to start [ 0.110178] smp: Brought up 1 node, 7 CPUs [ 0.128800] devtmpfs: initialized [ 0.143957] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 7645041785100000 ns [ 0.144272] futex hash table entries: 2048 (order: 5, 131072 bytes, linear) [ 0.151876] NET: Registered protocol family 16 [ 0.249864] vgaarb: loaded [ 0.252192] SCSI subsystem initialized [ 0.255517] usbcore: registered new interface driver usbfs [ 0.256358] usbcore: registered new interface driver hub [ 0.256600] usbcore: registered new device driver usb [ 0.272238] clocksource: Switched to clocksource riscv_clocksource [ 0.355524] NET: Registered protocol family 2 [ 0.357939] IP idents hash table entries: 16384 (order: 5, 131072 bytes, linear) [ 0.366451] tcp_listen_portaddr_hash hash table entries: 512 (order: 2, 20480 bytes, linear) [ 0.366611] TCP established hash table entries: 8192 (order: 4, 65536 bytes, linear) [ 0.366918] TCP bind hash table entries: 8192 (order: 6, 262144 bytes, linear) [ 0.367350] TCP: Hash tables configured (established 8192 bind 8192) [ 0.369283] UDP hash table entries: 512 (order: 3, 49152 bytes, linear) [ 0.369677] UDP-Lite hash table entries: 512 (order: 3, 49152 bytes, linear) [ 0.372257] NET: Registered protocol family 1 [ 0.382006] RPC: Registered named UNIX socket transport module. [ 0.382095] RPC: Registered udp transport module. [ 0.382116] RPC: Registered tcp transport module. [ 0.382133] RPC: Registered tcp NFSv4.1 backchannel transport module. [ 0.382278] PCI: CLS 0 bytes, default 64 [ 0.391432] workingset: timestamp_bits=62 max_order=18 bucket_order=0 [ 0.418671] NFS: Registering the id_resolver key type [ 0.420658] Key type id_resolver registered [ 0.420721] Key type id_legacy registered [ 0.421317] nfs4filelayout_init: NFSv4 File Layout Driver Registering... [ 0.421447] nfs4flexfilelayout_init: NFSv4 Flexfile Layout Driver Registering... [ 0.422671] 9p: Installing v9fs 9p2000 file system support [ 0.425413] NET: Registered protocol family 38 [ 0.426299] Block layer SCSI generic (bsg) driver version 0.4 loaded (major 251) [ 0.426699] io scheduler mq-deadline registered [ 0.426880] io scheduler kyber registered [ 0.454289] input: QEMU Virtio Mouse as /devices/platform/soc/10105000.virtio_mmio/virtio2/input/input0 [ 0.456811] input: QEMU Virtio Keyboard as /devices/platform/soc/10104000.virtio_mmio/virtio3/input/input1 [ 0.467156] Serial: 8250/16550 driver, 4 ports, IRQ sharing disabled [ 0.485057] printk: console [ttyS0] disabled [ 0.487586] 10000000.uart0: ttyS0 at MMIO 0x10000000 (irq = 1, base_baud = 230400) is a 16550A [ 0.549207] printk: console [ttyS0] enabled [ 0.553339] 10001000.uart1: ttyS1 at MMIO 0x10001000 (irq = 2, base_baud = 230400) is a 16550A [ 0.563191] [drm] radeon kernel modesetting enabled. [ 0.568638] [drm] features: -virgl +edid [ 0.570309] [drm] number of scanouts: 1 [ 0.570731] [drm] number of cap sets: 0 [ 0.597484] [drm] Initialized virtio_gpu 0.1.0 0 for virtio1 on minor 0 [ 0.711552] Console: switching to colour frame buffer device 160x45 [ 0.731434] virtio_gpu virtio1: [drm] fb0: virtio_gpudrmfb frame buffer device [ 0.791282] loop: module loaded [ 0.814540] virtio_blk virtio0: [vda] 2097152 512-byte logical blocks (1.07 GB/1.00 GiB) [ 0.815582] vda: detected capacity change from 0 to 1073741824 [ 0.852595] vda: vda1 vda2 [ 0.878293] libphy: Fixed MDIO Bus: probed [ 0.911475] e1000e: Intel(R) PRO/1000 Network Driver [ 0.912730] e1000e: Copyright(c) 1999 - 2015 Intel Corporation. [ 0.914557] ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver [ 0.915923] ehci-pci: EHCI PCI platform driver [ 0.918434] ehci-platform: EHCI generic platform driver [ 0.920404] ohci_hcd: USB 1.1 'Open' Host Controller (OHCI) Driver [ 0.921664] ohci-pci: OHCI PCI platform driver [ 0.922937] ohci-platform: OHCI generic platform driver [ 0.933734] usbcore: registered new interface driver uas [ 0.935148] usbcore: registered new interface driver usb-storage [ 0.938227] mousedev: PS/2 mouse device common for all mice [ 0.948625] goldfish_rtc 10003000.rtc: registered as rtc0 [ 0.952000] goldfish_rtc 10003000.rtc: setting system clock to 2021-08-09T06:36:48 UTC (1628491008) [ 0.965331] usbcore: registered new interface driver usbhid [ 0.965928] usbhid: USB HID core driver [ 0.971386] NET: Registered protocol family 10 [ 0.995197] Segment Routing with IPv6 [ 0.996810] sit: IPv6, IPv4 and MPLS over IPv4 tunneling driver [ 1.003680] NET: Registered protocol family 17 [ 1.009301] 9pnet: Installing 9P2000 support [ 1.012361] Key type dns_resolver registered [ 1.013807] debug_vm_pgtable: [debug_vm_pgtable ]: Validating architecture page table helpers [ 1.103225] EXT4-fs (vda2): mounted filesystem with ordered data mode. Opts: (null) [ 1.104780] VFS: Mounted root (ext4 filesystem) on device 254:2. [ 1.109586] devtmpfs: error mounting -2 [ 1.142338] Freeing unused kernel memory: 220K [ 1.148368] Run /sbin/init as init process [ 1.149677] Run /etc/init as init process [ 1.150884] Run /bin/init as init process [ 1.152613] Run /bin/sh as init process [ 1.153840] Kernel panic - not syncing: No working init found. Try passing init= option to kernel. See Linux Documentation/admin-guide/init.rst for guidance. [ 1.155653] CPU: 2 PID: 1 Comm: swapper/0 Not tainted 5.10.42 #208 [ 1.156597] Call Trace: [ 1.157151] [] walk_stackframe+0x0/0xaa [ 1.157793] [] show_stack+0x32/0x3e [ 1.158385] [] dump_stack+0x74/0x8e [ 1.158931] [] panic+0x100/0x2b6 [ 1.159545] [] kernel_init+0xec/0xf8 [ 1.160115] [] ret_from_exception+0x0/0xc [ 1.161124] SMP: stopping secondary CPUs [ 1.168836] ---[ end Kernel panic - not syncing: No working init found. Try passing init= option to kernel. See Linux Documentation/admin-guide/init.rst for guidance. ]--- ``` 可以看到内核以及成功运行起来,只是在最后需要执行第一个用户态init进程时报了错误,因为找不到init,到这里本节就完成了,那么后面的init进程是什么,内核启动过程又是什么的流程,下一节我将对其做以分析解释。 > 本教程的
github仓库:https://github.com/QQxiaoming/quard_star_tutorial
gitee仓库:https://gitee.com/QQxiaoming/quard_star_tutorial
本节所在tag:ch11