summaryrefslogtreecommitdiff
path: root/vmdebootstrap/uefi.py
blob: b2ca4108686ef6cf9e112649568353e0c2520e04 (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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
"""
  Wrapper for UEFI operations
"""
# -*- coding: utf-8 -*-
#
#  uefi.py
#
#  Copyright 2015 Neil Williams <codehelp@debian.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.


# pylint: disable=missing-docstring,duplicate-code


import os
import logging
import cliapp
from vmdebootstrap.base import (
    Base,
    runcmd,
    mount_wrapper,
    umount_wrapper,
)
from vmdebootstrap.constants import arch_table


class Uefi(Base):

    name = 'uefi'

    def __init__(self):
        super(Uefi, self).__init__()
        self.bootdir = None

    def check_settings(self, oldstable=False):
        if not self.settings['use-uefi'] and self.settings['esp-size'] != 5242880:
            raise cliapp.AppException(
                'You must specify use-uefi for esp-size to have effect')
        if self.settings['arch'] in arch_table and\
                arch_table[self.settings['arch']]['exclusive'] and\
                (not self.settings['use-uefi'] and not self.settings['squash']):
            raise cliapp.AppException(
                'Only UEFI is supported on %s' % self.settings['arch'])
        elif self.settings['use-uefi'] and self.settings['arch'] not in arch_table:
            raise cliapp.AppException(
                '%s is not a supported UEFI architecture' % self.settings['arch'])
        if self.settings['use-uefi'] and (
                self.settings['bootsize'] or
                self.settings['bootoffset']):
            raise cliapp.AppException(
                'A separate boot partition is not supported with UEFI')

        if self.settings['use-uefi'] and not self.settings['grub']:
            raise cliapp.AppException(
                'UEFI without Grub is not supported.')

        # wheezy (which became oldstable on 04/25/2015) only had amd64 uefi
        if oldstable:
            if self.settings['arch'] != 'amd64' and self.settings['use-uefi']:
                raise cliapp.AppException(
                    'Only amd64 supports UEFI in Wheezy')

    def efi_packages(self):
        packages = []
        if not self.settings['use-uefi'] or\
                self.settings['arch'] not in arch_table:
            return packages
        pkg = arch_table[self.settings['arch']]['package']
        self.message("Adding %s to debootstrap" % pkg)
        packages.append(pkg)
        extra = arch_table[self.settings['arch']]['extra']
        if extra and isinstance(extra, str):
            bin_pkg = arch_table[str(extra)]['bin_package']
            self.message("Adding support for %s using %s" % (extra, bin_pkg))
            packages.append(bin_pkg)
        return packages

    def copy_efi_binary(self, efi_removable, efi_install):
        if self.settings['arch'] not in arch_table:
            return
        logging.debug("using bootdir=%s", self.bootdir)
        if efi_removable.startswith('/'):
            efi_removable = efi_removable[1:]
        if efi_install.startswith('/'):
            efi_install = efi_install[1:]
        efi_output = os.path.join(self.bootdir, efi_removable)
        efi_input = os.path.join(self.bootdir, efi_install)
        logging.debug("moving %s to %s", efi_output, efi_input)
        if not os.path.exists(efi_input):
            logging.warning("%s does not exist (%s)", efi_input, efi_install)
            raise cliapp.AppException("Missing %s" % efi_input)
        if not os.path.exists(os.path.dirname(efi_output)):
            os.makedirs(os.path.dirname(efi_output))
        logging.debug(
            'Moving UEFI support: %s -> %s', efi_input, efi_output)
        if os.path.exists(efi_output):
            os.unlink(efi_output)
        os.rename(efi_input, efi_output)

    def configure_efi(self, rootdir):
        """
        Copy the bootloader file from the package into the location
        so needs to be after grub and kernel already installed.
        """
        if self.settings['arch'] not in arch_table:
            return
        self.message('Configuring EFI')
        mount_wrapper(rootdir)
        efi_removable = str(arch_table[self.settings['arch']]['removable'])
        efi_install = str(arch_table[self.settings['arch']]['install'])
        self.message('Installing UEFI support binary')
        logging.debug("moving %s to %s", efi_removable, efi_install)
        try:
            self.copy_efi_binary(efi_removable, efi_install)
        finally:
            umount_wrapper(rootdir)

    def configure_extra_efi(self, rootdir):
        if self.settings['arch'] not in arch_table:
            return
        extra = arch_table[self.settings['arch']]['extra']
        if extra:
            mount_wrapper(rootdir)
            efi_removable = str(arch_table[extra]['removable'])
            efi_install = str(arch_table[extra]['install'])
            self.message('Copying UEFI support binary for %s' % extra)
            try:
                self.copy_efi_binary(efi_removable, efi_install)
            finally:
                umount_wrapper(rootdir)

    def partition_esp(self):
        if not self.settings['use-uefi']:
            return
        espsize = self.settings['esp-size'] / (1024 * 1024)
        self.message("Using ESP size: %smib %s bytes" % (espsize, self.settings['esp-size']))
        runcmd(['parted', '-s', self.settings['image'],
                'mkpart', 'primary', 'fat32',
                '1', str(espsize)])
        runcmd(['parted', '-s', self.settings['image'],
                'set', '1', 'boot', 'on'])
        runcmd(['parted', '-s', self.settings['image'],
                'set', '1', 'esp', 'on'])

    def prepare_esp(self, rootdir, bootdev):
        self.bootdir = '%s/%s/%s' % (rootdir, 'boot', 'efi')
        logging.debug("bootdir:%s", self.bootdir)
        self.mkfs(bootdev, fstype='vfat')
        os.makedirs(self.bootdir)
        return self.bootdir

    def make_root(self, extent):
        bootsize = self.settings['esp-size'] / (1024 * 1024) + 1
        runcmd(['parted', '-s', self.settings['image'],
                'mkpart', 'primary', str(bootsize), extent])