summaryrefslogtreecommitdiffstats
path: root/src/utils/boot.py
blob: 6ee7450de9a019cb3890f3d675f0cd4382ee966a (plain)
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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#
# Copyright (C) 2023 Soleta Networks <info@soleta.eu>
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the
# Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.

import json
import logging
import os
import shlex
import subprocess

from src.utils.probe import OSFamily, get_os_family, get_linux_distro_id, os_probe
from src.utils.disk import get_partition_device, get_efi_partition
from src.utils.bios import *
from src.utils.uefi import *
from src.utils.fs import *
from src.log import OgError


def _boot_bios_linux(disk, part, mountpoint):
    logging.info(f'Booting Linux system')

    if not get_linux_distro_id(mountpoint) == 'ubuntu':
        raise OgError(f'{os_probe(mountpoint)} detected, only Ubuntu is supported for legacy BIOS boot')

    kernel_path = get_vmlinuz_path(mountpoint)
    initrd_path = get_initrd_path(mountpoint)

    device = get_partition_device(disk, part)
    grub_boot_params = get_grub_boot_params(mountpoint, device)

    kexec_cmd = f'kexec -l {kernel_path} --append="{grub_boot_params}" --initrd="{initrd_path}"'
    kexec_reboot_cmd = 'kexec -e'

    logging.info(f'Booting with: {kexec_cmd}')
    try:
        subprocess.run(shlex.split(kexec_cmd), check=True, text=True)
        subprocess.run(shlex.split(kexec_reboot_cmd), check=True, text=True)
    except OSError as e:
        raise OgError(f'Error processing kexec: {e}') from e

def _boot_bios_windows(disk, part, mountpoint):
    logging.info(f'Booting Windows system')
    try:
        with open(f'{mountpoint}/ogboot.me', 'w') as f:
            f.write('\0' * (3072))
        with open(f'{mountpoint}/ogboot.firstboot', 'w') as f:
            f.write('iniciado')
            f.write('\0' * (3072 - 8))
        with open(f'{mountpoint}/ogboot.secondboot', 'w') as f:
            f.write('\0' * (3072))
    except OSError as e:
        raise OgError(f'Could not create ogboot files in Windows partition: {e}') from e

def _boot_uefi_windows(disk, part, mountpoint):
    logging.info(f'Booting windows system')
    bootlabel = f'Part-{disk:02d}-{part:02d}'
    esp, esp_disk, esp_part_number = get_efi_partition(disk, enforce_gpt=True)
    esp_mountpoint = esp.replace('dev', 'mnt')
    if not mount_mkdir(esp, esp_mountpoint):
        raise OgError(f'Unable to mount detected EFI System Partition at {esp} into {esp_mountpoint}')

    loader_paths = [f'{esp_mountpoint}/EFI/{bootlabel}/Boot/bootmgfw.efi',
                    f'{esp_mountpoint}/EFI/Microsoft/Boot/bootmgfw.efi']
    try:
        for efi_app in loader_paths:
            if os.path.exists(efi_app):
                loader = efi_app[len(esp_mountpoint):]
                logging.info(f'Found bootloader at ESP partition: {loader}')
                break
        else:
            raise OgError(f'Unable to locate Windows EFI bootloader bootmgfw.efi')

        efibootmgr_delete_bootentry(bootlabel)
        efibootmgr_create_bootentry(esp_disk, esp_part_number, loader, bootlabel)
        efibootmgr_bootnext(bootlabel)
    finally:
        umount(esp_mountpoint)

def _boot_uefi_linux(disk, part, mountpoint):
    logging.info(f'Booting Linux system')
    bootlabel = f'Part-{disk:02d}-{part:02d}'
    esp, esp_disk, esp_part_number = get_efi_partition(disk, enforce_gpt=False)
    esp_mountpoint = esp.replace('dev', 'mnt')
    if not mount_mkdir(esp, esp_mountpoint):
        raise OgError(f'Unable to mount detected EFI System Partition at {esp} into {esp_mountpoint}')

    loader_paths = [f'{esp_mountpoint}/EFI/{bootlabel}/Boot/shimx64.efi',
                    f'{esp_mountpoint}/EFI/ubuntu/shimx64.efi']
    try:
        for efi_app in loader_paths:
            if os.path.exists(efi_app):
                loader = efi_app[len(esp_mountpoint):]
                logging.info(f'Found bootloader at ESP partition: {loader}')
                break
        else:
            raise OgError(f'Unable to locate Linux EFI bootloader shimx64.efi')

        efibootmgr_delete_bootentry(bootlabel)
        efibootmgr_create_bootentry(esp_disk, esp_part_number, loader, bootlabel)
        efibootmgr_bootnext(bootlabel)
    finally:
        umount(esp_mountpoint)

def boot_os_at(disk, part):
    logging.info(f'Booting disk={disk} partition={part}')
    device = get_partition_device(disk, part)
    mountpoint = device.replace('dev', 'mnt')
    if not mount_mkdir(device, mountpoint):
        raise OgError(f'Cannot probe OS family. Unable to mount {device} into {mountpoint}')

    is_uefi = is_uefi_supported()
    if is_uefi:
        logging.info('UEFI support detected')
    else:
        logging.info('UEFI support not detected')

    os_family = get_os_family(mountpoint)
    logging.info(f'{os_family} detected at {device}.')

    try:
        if is_uefi and os_family == OSFamily.WINDOWS:
            _boot_uefi_windows(disk, part, mountpoint)
        elif is_uefi and os_family == OSFamily.LINUX:
            _boot_uefi_linux(disk, part, mountpoint)
        elif not is_uefi and os_family == OSFamily.WINDOWS:
            _boot_bios_windows(disk, part, mountpoint)
        elif not is_uefi and os_family == OSFamily.LINUX:
            _boot_bios_linux(disk, part, mountpoint)
        else:
            raise OgError(f'Unknown OS family {os_family}')
    finally:
        umount(mountpoint)