8
This commit is contained in:
28
AGENTS.md
28
AGENTS.md
@@ -110,11 +110,29 @@ options root=/dev/mapper/cl-root ro crashkernel=auto rd.lvm.lv=cl/root rhgb quie
|
|||||||
检测到缺少 EFI 模块时,自动安装相应的包:
|
检测到缺少 EFI 模块时,自动安装相应的包:
|
||||||
| 发行版 | 包名 |
|
| 发行版 | 包名 |
|
||||||
|--------|------|
|
|--------|------|
|
||||||
| CentOS/RHEL/Rocky/Alma/Fedora | `grub2-efi-x64`, `grub2-efi-x64-modules` |
|
| CentOS/RHEL/Rocky/Alma/Fedora | `grub2-efi-x64`, `grub2-efi-x64-modules`, `shim-x64` |
|
||||||
| Debian/Ubuntu | `grub-efi-amd64` |
|
| Debian/Ubuntu | `grub-efi-amd64`, `shim-signed` |
|
||||||
| Arch/Manjaro | `grub` (已包含 EFI 支持) |
|
| Arch/Manjaro | `grub`, `efibootmgr` |
|
||||||
|
|
||||||
### 7. GRUB 修复 (`backend.py:chroot_and_repair_grub`)
|
### 7. 手动 EFI 安装 (`backend.py:_manual_install_efi_files`)
|
||||||
|
|
||||||
|
**NEW v2.4**: 当 `grub2-install` 因 Secure Boot 不支持时,使用手动方式安装 EFI 文件
|
||||||
|
|
||||||
|
#### 问题场景
|
||||||
|
某些系统(特别是 CentOS/RHEL 8)的 `grub2-install` 会报错:
|
||||||
|
```
|
||||||
|
this utility cannot be used for EFI platforms because it does not support UEFI Secure Boot.
|
||||||
|
```
|
||||||
|
|
||||||
|
这是因为标准 `grub2-install` 不支持 Secure Boot,需要使用 `shim` 链式加载。
|
||||||
|
|
||||||
|
#### 手动安装方法
|
||||||
|
当 `grub2-install` 失败时,自动尝试:
|
||||||
|
1. **查找现有 EFI 文件** - 从 `/boot/efi/EFI/<distro>/` 复制已有的 `grubx64.efi` 或 `shimx64.efi`
|
||||||
|
2. **使用 shim 链式加载** - 复制 `shimx64.efi` 并配置它加载 `grubx64.efi`
|
||||||
|
3. **使用 grub-mkimage 生成** - 从模块生成新的 EFI 文件
|
||||||
|
|
||||||
|
### 8. GRUB 修复 (`backend.py:chroot_and_repair_grub`)
|
||||||
|
|
||||||
#### BIOS 模式
|
#### BIOS 模式
|
||||||
- `grub-install --target=i386-pc --recheck --force /dev/sdX`
|
- `grub-install --target=i386-pc --recheck --force /dev/sdX`
|
||||||
@@ -123,11 +141,13 @@ options root=/dev/mapper/cl-root ro crashkernel=auto rd.lvm.lv=cl/root rhgb quie
|
|||||||
#### UEFI 模式 (参考 Calamares 实现)
|
#### UEFI 模式 (参考 Calamares 实现)
|
||||||
- 自动检测系统架构 (x86_64/i386/arm64/loongarch64)
|
- 自动检测系统架构 (x86_64/i386/arm64/loongarch64)
|
||||||
- **NEW**: 自动检测并安装 UEFI GRUB 模块
|
- **NEW**: 自动检测并安装 UEFI GRUB 模块
|
||||||
|
- **NEW**: 当 grub2-install 因 Secure Boot 失败时,自动使用手动 EFI 安装
|
||||||
- 获取正确的 EFI 参数 (target, grub_file, boot_file)
|
- 获取正确的 EFI 参数 (target, grub_file, boot_file)
|
||||||
- **多重安装策略**(自动回退):
|
- **多重安装策略**(自动回退):
|
||||||
1. 标准安装(带 NVRAM)
|
1. 标准安装(带 NVRAM)
|
||||||
2. Live 环境/可移动模式(`--removable`)
|
2. Live 环境/可移动模式(`--removable`)
|
||||||
3. 无 NVRAM 模式(`--no-nvram`)
|
3. 无 NVRAM 模式(`--no-nvram`)
|
||||||
|
4. **NEW**: 手动 EFI 文件复制(针对 Secure Boot 限制)
|
||||||
- **NEW**: EFI Fallback 安装(复制到 `/EFI/Boot/bootx64.efi`)
|
- **NEW**: EFI Fallback 安装(复制到 `/EFI/Boot/bootx64.efi`)
|
||||||
- **NEW**: 支持自定义 EFI 启动项名称
|
- **NEW**: 支持自定义 EFI 启动项名称
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
138
backend.py
138
backend.py
@@ -1176,16 +1176,17 @@ def check_and_install_efi_modules(mount_point: str, distro_type: str, is_uefi: b
|
|||||||
chroot_cmd_prefix = ["sudo", "chroot", mount_point]
|
chroot_cmd_prefix = ["sudo", "chroot", mount_point]
|
||||||
|
|
||||||
# 根据发行版选择正确的 EFI 包
|
# 根据发行版选择正确的 EFI 包
|
||||||
|
# 注意:CentOS/RHEL 8+ 需要 shim 包来支持 UEFI Secure Boot
|
||||||
efi_packages_map = {
|
efi_packages_map = {
|
||||||
"centos": ["grub2-efi-x64", "grub2-efi-x64-modules"],
|
"centos": ["grub2-efi-x64", "grub2-efi-x64-modules", "shim-x64"],
|
||||||
"rhel": ["grub2-efi-x64", "grub2-efi-x64-modules"],
|
"rhel": ["grub2-efi-x64", "grub2-efi-x64-modules", "shim-x64"],
|
||||||
"fedora": ["grub2-efi-x64", "grub2-efi-x64-modules"],
|
"fedora": ["grub2-efi-x64", "grub2-efi-x64-modules", "shim-x64"],
|
||||||
"rocky": ["grub2-efi-x64", "grub2-efi-x64-modules"],
|
"rocky": ["grub2-efi-x64", "grub2-efi-x64-modules", "shim-x64"],
|
||||||
"almalinux": ["grub2-efi-x64", "grub2-efi-x64-modules"],
|
"almalinux": ["grub2-efi-x64", "grub2-efi-x64-modules", "shim-x64"],
|
||||||
"debian": ["grub-efi-amd64"],
|
"debian": ["grub-efi-amd64", "shim-signed"],
|
||||||
"ubuntu": ["grub-efi-amd64"],
|
"ubuntu": ["grub-efi-amd64", "shim-signed"],
|
||||||
"arch": ["grub"], # Arch 的 grub 包包含 EFI 支持
|
"arch": ["grub", "efibootmgr"], # Arch 的 grub 包包含 EFI 支持
|
||||||
"manjaro": ["grub"],
|
"manjaro": ["grub", "efibootmgr"],
|
||||||
}
|
}
|
||||||
|
|
||||||
efi_packages = efi_packages_map.get(distro_type, [])
|
efi_packages = efi_packages_map.get(distro_type, [])
|
||||||
@@ -1194,6 +1195,8 @@ def check_and_install_efi_modules(mount_point: str, distro_type: str, is_uefi: b
|
|||||||
log_warning(f"未知的发行版 '{distro_type}',无法自动安装 EFI 模块")
|
log_warning(f"未知的发行版 '{distro_type}',无法自动安装 EFI 模块")
|
||||||
return False, f"无法确定 {distro_type} 的 EFI 包名"
|
return False, f"无法确定 {distro_type} 的 EFI 包名"
|
||||||
|
|
||||||
|
log_info(f"将安装以下 EFI 包: {', '.join(efi_packages)}")
|
||||||
|
|
||||||
# 安装 EFI 包前,先确保网络连接(复制 DNS 配置)
|
# 安装 EFI 包前,先确保网络连接(复制 DNS 配置)
|
||||||
resolv_source = "/etc/resolv.conf"
|
resolv_source = "/etc/resolv.conf"
|
||||||
resolv_target = os.path.join(mount_point, "etc/resolv.conf")
|
resolv_target = os.path.join(mount_point, "etc/resolv.conf")
|
||||||
@@ -1244,6 +1247,113 @@ def check_and_install_efi_modules(mount_point: str, distro_type: str, is_uefi: b
|
|||||||
return False, "UEFI 模块安装失败"
|
return False, "UEFI 模块安装失败"
|
||||||
|
|
||||||
|
|
||||||
|
def _manual_install_efi_files(mount_point: str, efi_target: str, efi_grub_file: str,
|
||||||
|
efi_boot_file: str, bootloader_id: str = "GRUB") -> bool:
|
||||||
|
"""
|
||||||
|
手动安装 EFI 文件(当 grub2-install 因为 Secure Boot 不支持时使用)。
|
||||||
|
直接从 /boot/efi/EFI/<distro>/ 复制已有的 EFI 文件,或使用 shim 链式加载。
|
||||||
|
|
||||||
|
返回: 是否成功
|
||||||
|
"""
|
||||||
|
log_step("手动安装 EFI 文件", f"目标: {efi_grub_file}")
|
||||||
|
|
||||||
|
efi_firmware_dir = os.path.join(mount_point, "boot/efi/EFI")
|
||||||
|
|
||||||
|
# 创建目标目录
|
||||||
|
target_dir = os.path.join(efi_firmware_dir, bootloader_id)
|
||||||
|
try:
|
||||||
|
os.makedirs(target_dir, exist_ok=True)
|
||||||
|
log_info(f"创建/确认 EFI 目录: {target_dir}")
|
||||||
|
except Exception as e:
|
||||||
|
log_error(f"创建 EFI 目录失败: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 查找源 EFI 文件
|
||||||
|
# 可能的源路径:
|
||||||
|
source_paths = []
|
||||||
|
|
||||||
|
# 1. 从 /boot/efi/EFI/centos 或 /boot/efi/EFI/redhat 复制
|
||||||
|
for subdir in ["centos", "redhat", "fedora", "rocky", "almalinux", "boot"]:
|
||||||
|
source_paths.append(os.path.join(efi_firmware_dir, subdir, efi_grub_file))
|
||||||
|
source_paths.append(os.path.join(efi_firmware_dir, subdir, "shimx64.efi"))
|
||||||
|
source_paths.append(os.path.join(efi_firmware_dir, subdir, "shim.efi"))
|
||||||
|
|
||||||
|
# 2. 从 /boot/efi/ 直接查找
|
||||||
|
source_paths.append(os.path.join(mount_point, "boot/efi", efi_grub_file))
|
||||||
|
source_paths.append(os.path.join(mount_point, "boot/efi", "shimx64.efi"))
|
||||||
|
|
||||||
|
# 3. 从 /usr/lib/grub/x86_64-efi/ 复制 grubx64.efi
|
||||||
|
grub_modules_dir = os.path.join(mount_point, f"usr/lib/grub/{efi_target}")
|
||||||
|
source_paths.append(os.path.join(grub_modules_dir, "grubx64.efi"))
|
||||||
|
|
||||||
|
# 查找 shim 和 grub
|
||||||
|
shim_source = None
|
||||||
|
grub_source = None
|
||||||
|
|
||||||
|
for path in source_paths:
|
||||||
|
if os.path.exists(path):
|
||||||
|
log_info(f"找到 EFI 文件: {path}")
|
||||||
|
if "shim" in path.lower():
|
||||||
|
shim_source = path
|
||||||
|
elif "grub" in path.lower():
|
||||||
|
grub_source = path
|
||||||
|
|
||||||
|
# 复制文件
|
||||||
|
try:
|
||||||
|
if grub_source:
|
||||||
|
grub_target = os.path.join(target_dir, efi_grub_file)
|
||||||
|
shutil.copy2(grub_source, grub_target)
|
||||||
|
log_success(f"✓ 复制 GRUB: {grub_source} -> {grub_target}")
|
||||||
|
|
||||||
|
if shim_source:
|
||||||
|
# 如果使用 shim,复制 shim 并重命名为 grubx64.efi
|
||||||
|
# 或者保留 shim 文件名并在 NVRAM 中注册
|
||||||
|
shim_target = os.path.join(target_dir, "shimx64.efi")
|
||||||
|
shutil.copy2(shim_source, shim_target)
|
||||||
|
log_success(f"✓ 复制 Shim: {shim_source} -> {shim_target}")
|
||||||
|
|
||||||
|
# 同时复制为 bootloader 名称,便于启动
|
||||||
|
if efi_grub_file != "shimx64.efi":
|
||||||
|
shutil.copy2(shim_source, os.path.join(target_dir, efi_grub_file))
|
||||||
|
|
||||||
|
# 如果没有找到任何文件,尝试从 grub 模块生成
|
||||||
|
if not grub_source and not shim_source:
|
||||||
|
log_warning("未找到现有 EFI 文件,尝试使用 grub-mkimage 生成...")
|
||||||
|
|
||||||
|
# 构建 grub.efi 路径
|
||||||
|
grub_efi_output = os.path.join(target_dir, efi_grub_file)
|
||||||
|
|
||||||
|
# 获取模块列表(必需模块)
|
||||||
|
modules = "part_gpt part_msdos fat ext2 xfs btrfs normal boot linux configfile search search_fs_uuid search_fs_file"
|
||||||
|
|
||||||
|
# 尝试使用 grub-mkimage 生成 EFI 文件
|
||||||
|
chroot_cmd_prefix = ["sudo", "chroot", mount_point]
|
||||||
|
mkimage_cmd = chroot_cmd_prefix + [
|
||||||
|
"grub2-mkimage",
|
||||||
|
"-o", grub_efi_output,
|
||||||
|
"-O", efi_target,
|
||||||
|
"-p", "/boot/grub2",
|
||||||
|
] + modules.split()
|
||||||
|
|
||||||
|
success, _, stderr = run_command(
|
||||||
|
mkimage_cmd,
|
||||||
|
"使用 grub-mkimage 生成 EFI 文件",
|
||||||
|
timeout=60
|
||||||
|
)
|
||||||
|
|
||||||
|
if success and os.path.exists(grub_efi_output):
|
||||||
|
log_success(f"✓ 成功生成 EFI 文件: {grub_efi_output}")
|
||||||
|
else:
|
||||||
|
log_error(f"生成 EFI 文件失败: {stderr}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
log_error(f"手动安装 EFI 文件失败: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def install_efi_fallback(mount_point: str, efi_target: str, efi_grub_file: str,
|
def install_efi_fallback(mount_point: str, efi_target: str, efi_grub_file: str,
|
||||||
efi_boot_file: str, bootloader_id: str = "GRUB") -> bool:
|
efi_boot_file: str, bootloader_id: str = "GRUB") -> bool:
|
||||||
"""
|
"""
|
||||||
@@ -1920,6 +2030,16 @@ def chroot_and_repair_grub(mount_point: str, target_disk: str,
|
|||||||
else:
|
else:
|
||||||
install_errors.append(f"no-nvram 模式: {stderr}")
|
install_errors.append(f"no-nvram 模式: {stderr}")
|
||||||
|
|
||||||
|
# 如果所有 grub2-install 方法都失败,尝试手动复制 EFI 文件(针对 Secure Boot 问题)
|
||||||
|
if not grub_install_success and "Secure Boot" in str(install_errors):
|
||||||
|
log_info("grub2-install 不支持 Secure Boot,尝试手动安装 EFI 文件...")
|
||||||
|
success = _manual_install_efi_files(mount_point, efi_target, efi_grub_file, efi_boot_file, bootloader_id)
|
||||||
|
if success:
|
||||||
|
grub_install_success = True
|
||||||
|
log_success("✓ 手动 EFI 文件安装成功")
|
||||||
|
else:
|
||||||
|
log_error("手动 EFI 文件安装失败")
|
||||||
|
|
||||||
# 安装 EFI fallback(如果启用)
|
# 安装 EFI fallback(如果启用)
|
||||||
if grub_install_success and use_fallback:
|
if grub_install_success and use_fallback:
|
||||||
log_info("安装 EFI fallback 文件...")
|
log_info("安装 EFI fallback 文件...")
|
||||||
|
|||||||
Reference in New Issue
Block a user