这次是尝试在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
sudo su -
export LFS=/mnt/LFS
软件包和补丁
获取必要的软件包
mkdir -v $LFS/sources
chmod -v a+wt $LFS/sources
cd $LFS/sources
wget https://www.linuxfromscratch.org/~xry111/lfs/view/clfs-ng-systemd/wget-list-systemd
wget --input-file=wget-list-systemd --continue --directory-prefix=$LFS/sources
最后的准备工作
创建好目录布局和用于放交叉编译工具链的目录
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
chown -v lfs $LFS/{usr{,/*},lib*,boot,var,etc,bin,sbin,tools}
su - lfs
配置一下lfs用户的工作环境
cat > ~/.bash_profile << "EOF"
exec env -i HOME=$HOME TERM=$TERM PS1='\u:\w\$ ' /bin/bash
EOF
cat > ~/.bashrc << "EOF"
set +h
umask 022
LFS=/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_SITE
EOF
source ~/.bash_profile
编译交叉工具链
GCC
在编译GCC时,注意目录的替换
sed -i.orig 's/lib64/lib/g' gcc/config/riscv/t-withmultilib gcc/config/riscv/t-linux
Linux API Headers
这里需要调整架构至riscv
,支持的架构在arch/
目录下
make headers ARCH=riscv
Glibc
指定好动态链接器的位置
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在编译时会出现问题,所以这里下载一个修好问题的snapshot
https://git.savannah.gnu.org/gitweb/?p=grub.git;a=commit;h=b835601c7639ed1890f2d3db91900a8506011a8e
解压GRUB,然后进入目录中
./bootstrap
./autogen.sh
接下来就可以正常构建了
Linux
还是注意架构的替换
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
编译内核,注意平台架构
make ARCH=riscv CROSS_COMPILE=$LFS_TGT-
安装内核,注意内核的路径
install -vm644 arch/riscv/boot/Image $LFS/boot/vmlinuz
因为等下要用QEMU启动,所以做进虚拟硬盘里,等下以回环设备挂载,root路径就发生了改动
mkdir -pv $LFS/boot/grub
cat > $LFS/boot/grub/grub.cfg << "EOF"
# Begin /boot/grub/grub.cfg
set default=0
set timeout=5
search --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
sudo su -
export LFS=/mnt/LFS
制作一个30G的虚拟硬盘,然后制作三个分区,UEFI分区(100MB),Boot分区(500MB)和根分区
dd if=/dev/zero of=lfs_riscv64.img bs=1M count=30000
losetup -fP lfs_riscv64.img
fdisk /dev/loop0
在制作UEFI分区时,注意需要改变分区类型
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
格式化分区
mkfs.vfat -F 32 /dev/loop0p1
mkfs.ext4 /dev/loop0p2
mkfs.ext4 /dev/loop0p3
挂载分区,注意挂载路径和挂载顺序
mkdir -v /mnt/temp/
mount /dev/loop0p3 /mnt/temp
mkdir -v /mnt/temp/boot
mount /dev/loop0p2 /mnt/temp/boot
mkdir -v /mnt/temp/boot/efi
mount /dev/loop0p1 /mnt/temp/boot/efi
cp -av $LFS/* /mnt/temp
chown -R root:root /mnt/temp
备份一下临时交叉编译工具链
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
$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
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/edk2
export EDK_TOOLS_PATH=$WORKSPACE/edk2/BaseTools
source edk2/edksetup.sh
make -C edk2/BaseTools clean
make -C edk2/BaseTools
make -C edk2/BaseTools/Source/C
source edk2/edksetup.sh BaseTools
build -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
git clone git@github.com:riscv-software-src/opensbi.git
cd opensbi
make 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,使用的是直接挂载内核和根文件系统的方法
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
# qemu-system-riscv64 \
# -nographic \
# -machine virt \
# -m 2G \
# -bios /home/nakanomiku/CODES/opensbi/build/platform/generic/firmware/fw_dynamic.bin \
# -drive if=pflash,unit=0,format=raw,readonly=on,file=/usr/share/qemu-efi-riscv64/RISCV_VIRT_CODE.fd \
# -drive if=pflash,unit=1,format=raw,file=/usr/share/qemu-efi-riscv64/RISCV_VIRT_VARS.fd \
# -drive file=/dev/loop0,format=raw \
# -d cpu_reset,guest_errors
# sudo qemu-system-riscv64 -nographic -machine virt \
# -drive if=pflash,unit=0,format=raw,readonly=on,file=/usr/share/qemu-efi-riscv64/RISCV_VIRT_CODE.fd \
# -drive if=pflash,unit=1,format=raw,file=/usr/share/qemu-efi-riscv64/RISCV_VIRT_VARS.fd \
# -drive file=/home/nakanomiku/iso/lfs_riscv64.img,format=raw
# sudo qemu-system-riscv64 -nographic -machine virt \
# -drive if=pflash,unit=0,format=raw,readonly=on,file=/home/lfs/Build2/RiscVVirtQemu/RELEASE_GCC5/FV/RISCV_VIRT_CODE.fd \
# -drive if=pflash,unit=1,format=raw,file=/home/lfs/Build2/RiscVVirtQemu/RELEASE_GCC5/FV/RISCV_VIRT_VARS.fd \
# -drive file=/dev/loop0,format=raw
启动系统并构建临时工具
先准备虚拟内核文件系统
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
ln -sfv /proc/self/fd/0 /dev/stdin
ln -sfv /proc/self/fd/1 /dev/stdout
ln -sfv /proc/self/fd/2 /dev/stderr
ln -sv /proc/self/fd /dev
当前环境的shell是初始进程,如果直接退出会导致panic,所以防止不小心退出shell
enable -n exit exec
readonly IGNOREEOF=1000
一些测试需要真实的标准输入输出,所以为串口ttyS0创建一个新的shell进程
agetty -n -l /bin/bash ttyS0
创建一个控制终端并定义一些环境变量,需要注意的是现在不再使用交叉编译工具链了
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
一些测试需要主机名,创建一个临时的主机名
hostname lfs
继续创建剩下所需要的目录
mkdir -pv /{boot,home,mnt,opt,srv}
mkdir -pv /etc/{opt,sysconfig}
mkdir -pv /lib/firmware
mkdir -pv /media/{floppy,cdrom}
mkdir -pv /usr/{,local/}{include,src}
mkdir -pv /usr/lib/locale
mkdir -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/run
ln -sfv /run/lock /var/lock
install -dv -m 0750 /root
install -dv -m 1777 /tmp /var/tmp
创建一个记录文件系统的文件的软连接
ln -sv /proc/self/mounts /etc/mtab
创建一个简单的/etc/hosts
cat > /etc/hosts << EOF
127.0.0.1 localhost $(hostname)
::1 localhost
EOF
创建/etc/passwd和/etc/group
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
localedef -i C -f UTF-8 C.UTF-8
创建一个用于跑测试的用户tester
echo "tester:x:101:101::/home/tester:/bin/bash" >> /etc/passwd
echo "tester:x:101:" >> /etc/group
install -o tester -d /home/tester
重新登陆一下
exec /usr/bin/bash --login
创建一些不会自动创建的日志文件
touch /var/log/{btmp,lastlog,faillog,wtmp}
chgrp -v utmp /var/log/lastlog
chmod -v 664 /var/log/lastlog
chmod -v 600 /var/log/btmp
现在可以继续构建临时工具了
Flex
Flex的config.guess和config.sub太旧了,识别不出riscv,更新一下
cd build-aux
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
Expect
Expect的config.guess和config.sub太旧了,识别不出riscv,更新一下(可以复制之前已经下载过的)
cd tclconfig
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
GCC
在编译GCC时,注意目录的替换
sed -i.orig 's/lib64/lib/g' gcc/config/riscv/t-withmultilib gcc/config/riscv/t-linux
有几个失败的测试样例不需要移除
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,更新一下(可以复制之前已经下载过的)
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
需要安装的软件包
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
系统配置
网络
cat > /etc/systemd/network/10-ether0.link << "EOF"
[Match]
# Change the MAC address as appropriate for your network device
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/hostname
cat > /etc/hosts << "EOF"
# Begin /etc/hosts
192.168.0.2
::1 ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
# End /etc/hosts
EOF
locale
cat > /etc/locale.conf << "EOF"
LANG=en_US.ISO-8859-1
EOF