基于x86_64平台构建RISC-V平台上的CLFS
这次是尝试在x86_64平台上交叉编译出RISC-V的LFS,其中的过程会有所不同 我没有成功通过GRUB启动这个CLFS,这里只能通过挂载内核和根文件系统的方式启动 CLFS手册链接:https://www.linuxfromscratch.org/~xry111/lfs/view/clfs-ng-systemd/index.html
之前在x86_64平台上构建同是x86_64的LFS并不是真正的交叉编译,且由于平台相同,可以借助chroot轻松完成软件的安装,而这次为真正的交叉编译,无法采取像之前一样的chroot方法进行安装,而是需要进行一些调整。 这次使用的手册并没有直接使用交叉编译 ,而是使用了交叉编译应该使用的方法,但我会真正的使用交叉编译的方法构建CLFS,并且这次会使用systemd引导内核。 这次的构建流程并不会很详细,只会挑重点说明
准备宿主系统 因为上次已经检查过了,所以这次跳过检查,先设置好环境变量LFS
1 2 sudo su -export LFS=/mnt/LFS
软件包和补丁 获取必要的软件包
1 2 3 4 5 mkdir -v $LFS /sourceschmod -v a+wt $LFS /sourcescd $LFS /sourceswget https://www.linuxfromscratch.org/~xry111/lfs/view/clfs-ng-systemd/wget-list-systemd wget --input-file=wget-list-systemd --continue --directory-prefix=$LFS /sources
最后的准备工作 创建好目录布局和用于放交叉编译工具链的目录
1 2 3 4 5 6 7 mkdir -pv $LFS /{boot,etc,var} $LFS /usr/{bin,lib,sbin}for i in bin lib sbin; do ln -sv usr/$i $LFS /$i done mkdir -pv $LFS /tools
新增lfs用户,之前已经创建过一次了,所以直接改变目录所有者,然后切换到lfs用户 这里需要注意的是,因为目标平台是RISC-V,所以环境LFS_TGT需要设置成riscv64-lfs-linux-gnu
1 2 chown -v lfs $LFS /{usr{,/*},lib*,boot,var,etc,bin,sbin,tools}su - lfs
配置一下lfs用户的工作环境
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 cat > ~/.bash_profile << "EOF" exec env -i HOME=$HOME TERM=$TERM PS1='\u:\w\$ ' /bin/bashEOF cat > ~/.bashrc << "EOF" set +humask 022LFS=/mnt/LFS LC_ALL=POSIX LFS_TGT=riscv64-lfs-linux-gnu PATH=/usr/bin if [ ! -L /bin ]; then PATH=/bin:$PATH ; fi PATH=$LFS /tools/bin:$PATH CONFIG_SITE=$LFS /usr/share/config.site export MAKEFLAGS=-j$(nproc )export LFS LC_ALL LFS_TGT PATH CONFIG_SITEEOF source ~/.bash_profile
编译交叉工具链 GCC 在编译GCC时,注意目录的替换
1 sed -i.orig 's/lib64/lib/g' gcc/config/riscv/t-withmultilib gcc/config/riscv/t-linux
这里需要调整架构至riscv,支持的架构在arch/目录下
Glibc 指定好动态链接器的位置
1 2 3 4 5 6 7 8 cat > temp.sh << "EOF" case $LFS_TGT in riscv64*) ln -sfv ../lib/ld-linux-riscv64-lp64d.so.1 $LFS /lib ln -sfv ../lib/ld-linux-riscv64-lp64d.so.1 $LFS /lib/ld-lsb-riscv64.so.1 ;; esac EOF
GRUB 因为当前下载的GRUB在编译时会出现问题,所以这里下载一个修好问题的snapshothttps://git.savannah.gnu.org/gitweb/?p=grub.git;a=commit;h=b835601c7639ed1890f2d3db91900a8506011a8e
解压GRUB,然后进入目录中
1 2 ./bootstrap ./autogen.sh
接下来就可以正常构建了
Linux 还是注意架构的替换
1 2 3 make ARCH=riscv \ CROSS_COMPILE=$LFS_TGT - \ menuconfig
CONFIG_MODULES=n CONFIG_EXTRA_FIRMWARE=”a.bin b.bin” CONFIG_EXTRA_FIRMWARE_DIR=”$LFS/lib/firmware” CONFIG_EXT2=y CONFIG_EXT4=y CONFIG_VFAT_FS=y CONFIG_MODULE_SIG=n CONFIG_SECURITY=n CONFIG_STACK_VALIDATION=n CONFIG_SYSTEM_TRUSTED_KEYRING=n CONFIG_SYSTEM_REVOCATION_LIST=n
编译内核,注意平台架构
1 make ARCH=riscv CROSS_COMPILE=$LFS_TGT -
安装内核,注意内核的路径
1 install -vm644 arch /riscv/boot/Image $LFS /boot/vmlinuz
因为等下要用QEMU启动,所以做进虚拟硬盘里,等下以回环设备挂载,root路径就发生了改动
1 2 3 4 5 6 7 8 9 10 11 12 mkdir -pv $LFS /boot/grubcat > $LFS /boot/grub/grub.cfg << "EOF" set default=0set timeout =5search --set =root --fs-uuid ED6D-9BB1 menuentry "LFS Temporary System" { linux /vmlinuz root=PARTUUID=59a73735-b7e7-451c-b351-16f64454b497 rw init=/bin/bash boot } EOF
接下来的操作都由root完成,记得先设置环境变量LFS
1 2 sudo su -export LFS=/mnt/LFS
制作一个30G的虚拟硬盘,然后制作三个分区,UEFI分区(100MB),Boot分区(500MB)和根分区
1 2 3 dd if =/dev/zero of=lfs_riscv64.img bs=1M count=30000losetup -fP lfs_riscv64.img fdisk /dev/loop0
在制作UEFI分区时,注意需要改变分区类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Command (m for help ): t Partition number (1-3, default 3): 1 Partition type or alias (type L to list all): 1 Changed type of partition 'BIOS boot' to 'EFI System' . Command (m for help ): p Disk /dev/loop0: 29.3 GiB, 31457280000 bytes, 61440000 sectors Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disklabel type : gpt Disk identifier: 1EF6AB2D-E1AA-4DE6-843C-F5C64261A675 Device Start End Sectors Size Type /dev/loop0p1 2048 206847 204800 100M EFI System /dev/loop0p2 206848 1230847 1024000 500M Linux filesystem /dev/loop0p3 1230848 61437951 60207104 28.7G Linux filesystem
格式化分区
1 2 3 mkfs.vfat -F 32 /dev/loop0p1 mkfs.ext4 /dev/loop0p2 mkfs.ext4 /dev/loop0p3
挂载分区,注意挂载路径和挂载顺序
1 2 3 4 5 6 mkdir -v /mnt/temp/mount /dev/loop0p3 /mnt/temp mkdir -v /mnt/temp/bootmount /dev/loop0p2 /mnt/temp/boot mkdir -v /mnt/temp/boot/efimount /dev/loop0p1 /mnt/temp/boot/efi
cp -av $LFS/* /mnt/temp chown -R root:root /mnt/temp
备份一下临时交叉编译工具链
1 2 3 4 tar -cJpf /root/lfs-temp-tools-cross-r12.0-499-systemd.tar.xz . \ --exclude="dev/*" --exclude="proc/*" --exclude="sys/*" --exclude="run/*" --exclude="mnt/*"
安装GRUB,注意target和--efi-directory
1 2 3 4 5 6 7 $LFS /tools/sbin/lfs-grub-install \ --target=riscv64-efi \ --efi-directory=/mnt/temp/boot/efi \ --boot-directory=/mnt/temp/boot \ --bootloader-id=GRUB \ --removable \ /dev/loop0
install -v -dm755 /mnt/temp/dev umount -Rv /mnt/lfs-target
edk2 检查qemu-system-riscv64是否正确配置,有没有riscv的uefi固件edk2
1 2 3 4 5 6 7 8 9 10 11 12 git clone --recurse-submodule https://github.com/tianocore/edk2.git export WORKSPACE=`pwd `export GCC5_RISCV64_PREFIX=/usr/bin/riscv64-linux-gnu-export PACKAGES_PATH=$WORKSPACE /edk2export EDK_TOOLS_PATH=$WORKSPACE /edk2/BaseToolssource edk2/edksetup.shmake -C edk2/BaseTools clean make -C edk2/BaseTools make -C edk2/BaseTools/Source/C source edk2/edksetup.sh BaseToolsbuild -a RISCV64 --buildtarget RELEASE -p OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc -t GCC5 truncate -s 32M Build/RiscVVirtQemu/RELEASE_GCC5/FV/RISCV_VIRT.fd
apt install qemu-efi-riscv64
opensbi 1 2 3 git clone git@github.com:riscv-software-src/opensbi.git cd opensbimake PLATFORM=generic FW_PAYLOAD_PATH=/mnt/LFS/sources/linux-6.10.5/arch/riscv/boot/Image CC=/mnt/LFS/tools/bin/riscv64-lfs-linux-gnu-gcc LD=/mnt/LFS/tools/bin/riscv64-lfs-linux-gnu-ld OBJCOPY=/mnt/LFS/tools/bin/riscv64-lfs-linux-gnu-objcopy
现在可以启动系统了,这里没有使用GRUB,使用的是直接挂载内核和根文件系统的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 qemu-system-riscv64 -nographic -m 16G -smp $(nproc ) -machine virt -kernel /mnt/temp/boot/vmlinuz -append "root=/dev/vda rw console=ttyS0 init=/bin/bash" -drive file=/dev/loop0p3,format=raw
启动系统并构建临时工具 先准备虚拟内核文件系统
1 2 3 4 5 6 7 mkdir -pv /{proc,sys,run}mkdir -pv /dev/{pts,shm}mount -vt devpts devpts -o gid=5,mode=0620 /dev/pts mount -vt proc proc /proc mount -vt sysfs sysfs /sys mount -vt tmpfs tmpfs /run mount -vt tmpfs tmpfs /dev/shm -o nosuid,nodev
调整devtmpfs
1 2 3 4 ln -sfv /proc/self/fd/0 /dev/stdinln -sfv /proc/self/fd/1 /dev/stdoutln -sfv /proc/self/fd/2 /dev/stderrln -sv /proc/self/fd /dev
当前环境的shell是初始进程,如果直接退出会导致panic,所以防止不小心退出shell
1 2 enable -n exit exec readonly IGNOREEOF=1000
一些测试需要真实的标准输入输出,所以为串口ttyS0创建一个新的shell进程
1 agetty -n -l /bin/bash ttyS0
创建一个控制终端并定义一些环境变量,需要注意的是现在不再使用交叉编译工具链了
1 2 3 4 5 6 7 8 exec setsid -c /usr/bin/env -i \ HOME=/root \ TERM="$TERM " \ PS1='(lfs) \u:\w\$ ' \ PATH=/usr/bin:/usr/sbin \ MAKEFLAGS="-j$(nproc) " \ TESTSUITEFLAGS="-j$(nproc) " \ /bin/bash --login
一些测试需要主机名,创建一个临时的主机名
继续创建剩下所需要的目录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 mkdir -pv /{boot,home,mnt,opt,srv}mkdir -pv /etc/{opt,sysconfig}mkdir -pv /lib/firmwaremkdir -pv /media/{floppy,cdrom}mkdir -pv /usr/{,local /}{include,src}mkdir -pv /usr/lib/localemkdir -pv /usr/local/{bin,lib,sbin}mkdir -pv /usr/{,local /}share/{color,dict,doc,info,locale,man}mkdir -pv /usr/{,local /}share/{misc,terminfo,zoneinfo}mkdir -pv /usr/{,local /}share/man/man{1..8}mkdir -pv /var/{cache,local ,log ,mail,opt,spool}mkdir -pv /var/lib/{color,misc,locate}ln -sfv /run /var/runln -sfv /run/lock /var/lockinstall -dv -m 0750 /root install -dv -m 1777 /tmp /var/tmp
创建一个记录文件系统的文件的软连接
1 ln -sv /proc/self/mounts /etc/mtab
创建一个简单的/etc/hosts
1 2 3 4 cat > /etc/hosts << EOF 127.0.0.1 localhost $(hostname) ::1 localhost EOF
创建/etc/passwd和/etc/group
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 cat > /etc/passwd << "EOF" root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/dev/null:/usr/bin/false daemon:x:6:6:Daemon User:/dev/null:/usr/bin/false messagebus:x:18:18:D-Bus Message Daemon User:/run/dbus:/usr/bin/false systemd-journal-gateway:x:73:73:systemd Journal Gateway:/:/usr/bin/false systemd-journal-remote:x:74:74:systemd Journal Remote:/:/usr/bin/false systemd-journal-upload:x:75:75:systemd Journal Upload:/:/usr/bin/false systemd-network:x:76:76:systemd Network Management:/:/usr/bin/false systemd-resolve:x:77:77:systemd Resolver:/:/usr/bin/false systemd-timesync:x:78:78:systemd Time Synchronization:/:/usr/bin/false systemd-coredump:x:79:79:systemd Core Dumper:/:/usr/bin/false uuidd:x:80:80:UUID Generation Daemon User:/dev/null:/usr/bin/false systemd-oom:x:81:81:systemd Out Of Memory Daemon:/:/usr/bin/false nobody:x:65534:65534:Unprivileged User:/dev/null:/usr/bin/false EOF cat > /etc/group << "EOF" root:x:0: bin:x:1:daemon sys:x:2: kmem:x:3: tape:x:4: tty :x:5:daemon:x:6: floppy:x:7: disk:x:8: lp:x:9: dialout:x:10: audio:x:11: video:x:12: utmp:x:13: cdrom:x:15: adm:x:16: messagebus:x:18: systemd-journal:x:23: input:x:24: mail:x:34: kvm:x:61: systemd-journal-gateway:x:73: systemd-journal-remote:x:74: systemd-journal-upload:x:75: systemd-network:x:76: systemd-resolve:x:77: systemd-timesync:x:78: systemd-coredump:x:79: uuidd:x:80: systemd-oom:x:81: wheel:x:97: users :x:999:nogroup:x:65534: EOF
设置locale
1 localedef -i C -f UTF-8 C.UTF-8
创建一个用于跑测试的用户tester
1 2 3 echo "tester:x:101:101::/home/tester:/bin/bash" >> /etc/passwdecho "tester:x:101:" >> /etc/groupinstall -o tester -d /home/tester
重新登陆一下
1 exec /usr/bin/bash --login
创建一些不会自动创建的日志文件
1 2 3 4 touch /var/log/{btmp,lastlog,faillog,wtmp}chgrp -v utmp /var/log/lastlogchmod -v 664 /var/log/lastlogchmod -v 600 /var/log/btmp
现在可以继续构建临时工具了
Flex Flex的config.guess和config.sub太旧了,识别不出riscv,更新一下
1 2 3 cd build-auxcurl 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD' > config.guess curl 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD' > config.sub
Expect Expect的config.guess和config.sub太旧了,识别不出riscv,更新一下(可以复制之前已经下载过的)
1 2 3 cd tclconfigcurl 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD' > config.guess curl 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD' > config.sub
GCC 在编译GCC时,注意目录的替换
1 sed -i.orig 's/lib64/lib/g' gcc/config/riscv/t-withmultilib gcc/config/riscv/t-linux
有几个失败的测试样例不需要移除
1 2 sed -e '/cpython/d' -i ../gcc/testsuite/gcc.dg/plugin/plugin.exp sed -e 's/300000/(1|300000)/' -i ../libgomp/testsuite/libgomp.c-c++-common/pr109062.c
Check Expect的config.guess和config.sub太旧了,识别不出riscv,更新一下(可以复制之前已经下载过的)
1 2 curl 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD' > config.guess curl 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD' > config.sub
GRUB 跳过GRUB 需要安装的软件包
1 2 3 4 5 wget https://ftp.gnu.org/gnu/grub/grub-2.12.tar.xz wget https://unifoundry.com/pub/unifont/unifont-16.0.01/font-builds/unifont-16.0.01.pcf.gz wget https://github.com/rhboot/efibootmgr/archive/18/efibootmgr-18.tar.gz wget https://github.com/rhboot/efivar/archive/39/efivar-39.tar.gz wget http://ftp.rpm.org/popt/releases/popt-1.x/popt-1.19.tar.gz
Popt Efivar Efibootmgr GRUB 系统配置 网络
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 cat > /etc/systemd/network/10-ether0.link << "EOF" [Match] MACAddress=12:34:45:78:90:AB [Link] Name=ether0 EOF cat > /etc/systemd/network/10-eth-static.network << "EOF" [Match] Name=eth0 [Network] Address=192.168.0.2/24 Gateway=192.168.0.1 DNS=192.168.0.1 EOF cat > /etc/systemd/network/10-eth-dhcp.network << "EOF" [Match] Name=eth0 [Network] DHCP=ipv4 [DHCPv4] UseDomains=true EOF echo "lfs" > /etc/hostnamecat > /etc/hosts << "EOF" 192.168.0.2 ::1 ip6-localhost ip6-loopback ff02::1 ip6-allnodes ff02::2 ip6-allrouters EOF
locale
1 2 3 cat > /etc/locale.conf << "EOF" LANG=en_US.ISO-8859-1 EOF