NakanoMiku
文章10
标签12
分类3
基于x86_64平台构建RISC-V平台上的CLFS

基于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

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
本文作者:NakanoMiku
本文链接:http://nakanomiku39.github.io/2024/10/08/%E5%9F%BA%E4%BA%8Ex86-64%E5%B9%B3%E5%8F%B0%E6%9E%84%E5%BB%BARISC-V%E5%B9%B3%E5%8F%B0%E4%B8%8A%E7%9A%84CLFS/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可