[openEuler RISC-V SIG]在openEuler RISC-V上通过源码编译安装GRUB2
本文介绍了在openEuler RISC-V上通过源码编译安装GRUB2的方法。
目标
在openEuler RISC-V上基于OE grub2源码完成grub2编译替换,能够正常拉起系统。
环境
OS: openEuler 24.03 SP2 RISC-V
Hardware: RISC-V QEMU 9.1.3
oerv的grub2命令行通过按下c进入,用户名是root,默认密码openEuler#12
openEuler与Fedora都属于redhat系列,fedora官方提供了rv-fedora上编译grub2的文档,可以参考。
获取源码
虽然理论上可以直接基于grub2上游源码编译,但是每个Linux发行版会针对自己的特点为grub2添加补丁,为了保持一致,建议通过openEuler的包管理器dnf直接拉取src package
### 该命令会将grub2的源码包下载到当前目录下
dnf download --source grub2 --downloaddir="./"
然后通过rpm -i命令解包源码到当前目录(缺少的依赖手动安装即可)。
### 获取rpmbuild使用的源码
rpm -i ${GRUB2_SRC_RPM_NAME}
可以在当前目录下看到rpmbuild目录,结构如下,SPECS存放了编译命令,SOURCES存放了grub2源码。
rpmbuild/
├── SOURCES
└── SPECS
注意,这里解压出来的源码是源码tar包+patches(补丁)的格式,给rpmbuild用的,不适合修改源码。因此继续使用rpmbuild -bp命令完成源码解压和打patch。
### 获取编译使用的源码
rpmbuild -bp rpmbuild/SPECS/grub2.spec
### 可能需要的依赖
dnf install -y gcc make rpm-build autoconf automake bison bzip2-devel device-mapper-devel flex freetype-devel fuse-devel gettext-devel git help2man libtasn1-devel libusb-devel ncurses-devel "pesign >= 0.99-8" rpm-devel texinfo xz-devel
有兴趣的可以关注rpmbuld prepare过程中打印的log,基本是在初始化代码环境,包括解压,初始化git仓库(因为补丁是基于git diff生成的),打补丁,以及生成配置文件。此时rpmbuild下生成了新目录,我们主要使用存放完成准备源码的BUILD目录。
rpmbuild/
├── BUILD
├── BUILDROOT
├── RPMS
├── SOURCES
├── SPECS
└── SRPMS
EFI分区和BOOT分区
从使用上理解,可以将EFI分区视为grub镜像的查找目录,而BOOT目录是GRUB启动时的根文件系统/目录,用于查找Linux内核镜像和rootfs/initrd。
为了确保GRUB镜像能够正确被EDK2识别,在通过grub/grub2-mkimage时制作的grub镜像时,需要确保镜像在EFI分区下路径 = 制作时指定的路径,否则启动时会出现下图找不到grub镜像的问题。
大部分系统以及教程都建议boot分区和efi分区分离,编译和安装时grub基本默认/boot/efi下为efi分区,/boot为boot分区。
但是,openEuler RISC-V目前EFI分区和BOOT分区是同一个分区,都是直接挂载在/boot目录下,但是EFI镜像制作时指定的目录(即图中的EFI/openeuler/grubriscv64.efi)则是相对/boot/efi目录的路径,导致oerv找不到默认启动项使用的grub镜像(即图中报错),实际使用的是/boot/efi/BOOT下grub镜像,只修改/boot/efi/EFI/openEuler下面的内容不会生效。
感兴趣的读者可以尝试直接将完整的/boot/efi/EFI/openEuler目录拷贝到/boot/efi/openeuler下,按照我们之前的分析,这样就能够实现grub镜像构建时路径和加载时路径一致,确保openEuler使用的是预期的grub镜像(检查启动时日志可以发现已经没有报错了)。
源码修改
下面修改源码,为了方便验证,我们修改以下两个部分:
- grub2版本号:在configure.ac的AC_INIT中定义(注意修改configure.ac后必须运行autogen.sh才能生效,详见下一节)。
- hello mod:修改hello模块(在grub-core/hellp/hello.c中),自定义打印信息。
编译安装
编译有两条路线:
-
直接使用rpmbuild:oerv所有软件包提供的spec都不支持交叉编译。因为openEuler OBS出RISC-V包是基于docker + qemu_user在x86上模拟RISC-V原生编译出软件包,可以通过以下方式使用rpmbuild出包:
-
模仿OBS的行为,在qemu-user驱动的RISC-V的容器镜像中编译。
-
在硬件环境如qemu上直接rpmbuild,性能也还可以接受。
同时,直接使用rpmbuild不方便快速验证代码,因为rpmbuild完全基于patch应用代码修改,每次修改之后需要按照生成patch->添加patch并修改spec->重新构建的流程操作,效率低下。
-
-
手动进行编译:对于需要快速验证的同学推荐手动进行编译。手动编译可以通过在configure时修改host/target选项支持交叉编译,因为grub2的编译比较简单,我们这里直接使用qemu原生编译。
0. 清理环境
清理下配置,有时候configure会因为环境被污染失败
### 定义grub2代码根目录 GRUB2_ROOT= ### 清理环境 make -C ${GRUB2_ROOT} distclean1. 生成configure
这一步必须在代码根目录下执行。因为autogen会在工作目录下查找gnulib依赖,oe-grub2直接将依赖打包在源码中,不需要额外执行bootstrap获取依赖。
注意任何对configure.ac的修改都需要运行autogen才能生效。### 自动生成配置文件 cd ${GRUB2_ROOT} && bash ./autogen.sh3. 配置
强烈建议仅在QEMU等测试环境中使用这段配置,为了方便它是直接从rpmbuild -bc日志中摘下来的,使用这个配置直接编译安装会覆盖当前系统grub工具(其它oe版本同样可以使用rpmbuild -bc命令查看日志输出来获取configure命令)。
注意:
- oe在grub源码下创建了grub-riscv64-efi-2.12目录作为output目录,进入该目录后执行configure脚本完成配置,也可以直接在代码根目录下执行,编译和output目录不同而已。
- 在bootstrap时OBS会使用riscv64-openEuler-linux-gnu编译grub,但是在使用环境上是没有的(标准的三元组,因为OBS是原生编译,所以build/host/target是一样的)。我们直接使用默认gcc,移除三元组的配置。
cd ${GRUB2_ROOT}/grub-riscv64-efi-2.12 && ../configure \ --program-prefix= \ --disable-dependency-tracking \ --prefix=/usr \ --exec-prefix=/usr \ --bindir=/usr/bin \ --sbindir=/usr/sbin \ --sysconfdir=/etc \ --datadir=/usr/share \ --includedir=/usr/include \ --libdir=/usr/lib \ --libexecdir=/usr/libexec \ --localstatedir=/var \ --sharedstatedir=/var/lib \ --mandir=/usr/share/man \ --infodir=/usr/share/info \ "CC=gcc -fPIE -Wl,-z,noexecstack" \ "HOST_CFLAGS=-fno-strict-aliasing -g3 -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-D_GLIBCXX_ASSERTIONS -fstack-clash-protection -specs=/usr/lib/rpm/generic-hardened-cc1 -I$(pwd) -fno-stack-protector" \ "HOST_CPPFLAGS= -I$(pwd) -fPIC" \ "HOST_LDFLAGS=-Wl,-z,relro -Wl,-z,now -specs=/usr/lib/rpm/generic-hardened-ld" \ "TARGET_CFLAGS=-fno-strict-aliasing -g3 -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-D_GLIBCXX_ASSERTIONS -fstack-clash-protection -I$(pwd) -fno-stack-protector" \ "TARGET_CPPFLAGS= -I$(pwd)" \ --with-platform=efi \ --with-grubdir=grub2 \ --program-transform-name=s,grub,grub2, \ --disable-werror4. 编译grub
在配置目录下进行编译,编译完成后安装grub2工具到configure时配置的目录。注意这里并不是安装镜像。
make -j$(nproc) && make install5. 安装grub
5.1 自动安装grub
可以直接使用grub2-install命令安装grub2 mod和镜像。同时该命令还会生成对应的grub.cfg和grubenv。前者用于指导grub加载linux,后者用于启用grub环境变量(如debug)。无特殊要求时,可以直接使用以下命令安装
EFI_DIR="/boot" BL_ID="openEuler" ./grub-install \ --target=riscv64-efi \ --efi-directory=${EFI_DIR} \ --bootloader-id=${BL_ID}注意efi-directory参数和bootloader-id:
- 前者指定了EFI分区的挂载目录,这里OERV自己的处理明显有问题,OERV的EFI分区挂载在/boot目录下,但是仍然按照/boot/efi目录进行操作。
- 后者指定了在EDK2启动时当前grub对应的启动项名称,我们这里直接替换了第一个启动项,如果不希望覆盖可以换成不一样的。
- 同时,我们这里没有指定安装目录,grub的mods和env,cfg等会缺省安装到${EFI_DIR}/grub2目录下(因为是安装grub2),即每次安装都会替换mods和cfg等。
完成自动安装后,我们能够在${EFI_DIR}/${BL_ID} 下找到安装的grub2镜像,默认名字grubriscv64.efi。并在${EFI_DIR}/grub2下找到cfg和mods等。
5.2 生成grub配置
grub install时生成的cfg不一定能够正常识别,重新使用grub2-mkconfig命令生成。
注意目标cfg目录需要和install目录保持一致。EFI_DIR="/boot" BL_ID="openEuler" sudo grub2-mkconfig -o ${EFI_DIR}/grub2/grub.cfg5.3 (非必须)构建grub镜像
制作镜像非必须,直接使用grub2-install安装的镜像已经可以正常使用。但是如果需要打包mods到镜像中,需要使用grub2-mkimage工具单独制作镜像,下面是OE grub的mkimage命令。
EFI_DIR="/boot" BL_ID="openEuler" ./grub-mkimage \ -O riscv64-efi \ -o grubriscv64.efi.orig \ -p /EFI/${BL_ID}\ -d grub-core \ all_video boot blscfg btrfs cat configfile cryptodisk echo efifwsetup efinet ext2 f2fs fat font gcry_rijndael gcry_rsa gcry_serpent gcry_sha256 gcry_twofish gcry_whirlpool gfxmenu gfxterm gfxterm_background gzio halt http iso9660 jpeg loadenv loopback linux lvm lsefi lsefimmap luks luks2 mdraid09 mdraid1x minicmd net normal part_apple part_msdos part_gpt password_pbkdf2 png reboot search search_fs_uuid search_fs_file search_label serial sleep test tftp video xfs tpm tpcm_kunpeng \ --sbat ././sbat.csv \ -c ${EFI_DIR}/grub.cfg注意**-p和-c参数**:
注:在整个编译和配置过程中没有看到报错,命令应当无误。
验证
完成grub2编译和镜像/mods安装后,首先尝试执行任一grub2工具(如grub2-install)的version命令。检查版本是否与自定义版本一致,(如果不一致检查是否修改了configure.ac后未运行autogen生成配置文件)
grub2-install --version
然后重启系统,在系统启动时按下c进入grub,接下来输入用户名(root),默认密码(openEuler#12),进入命令行界面。
- 查看grub版本是否与自定义版本一致。
- 执行hello命令,检查输出是否与编译时一致。
鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。
更多推荐

所有评论(0)