基于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模式下。
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,即为文件系统原始映像。
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
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_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。
#!/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命令必须使用管理员权限才能运行。
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完成文件拷贝。
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] [<ffffffe000202794>] walk_stackframe+0x0/0xaa
[ 1.157793] [<ffffffe0008e8706>] show_stack+0x32/0x3e
[ 1.158385] [<ffffffe0008eb356>] dump_stack+0x74/0x8e
[ 1.158931] [<ffffffe0008e891a>] panic+0x100/0x2b6
[ 1.159545] [<ffffffe0008f2c86>] kernel_init+0xec/0xf8
[ 1.160115] [<ffffffe000201314>] 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