Compare commits

..

No commits in common. "el8-zipl" and "el8" have entirely different histories.

22 changed files with 21 additions and 1263 deletions

3
.gitignore vendored
View File

@ -90,6 +90,3 @@ target/
# Rust
Cargo.lock
vendor/
*.rpm
kiwi-descriptions

View File

@ -49,8 +49,7 @@ class BootLoaderConfig(metaclass=ABCMeta):
'grub2': {'grub2': 'BootLoaderConfigGrub2'},
'grub2_s390x_emu': {'grub2': 'BootLoaderConfigGrub2'},
'isolinux': {'isolinux': 'BootLoaderConfigIsoLinux'},
'systemd_boot': {'systemd_boot': 'BootLoaderSystemdBoot'},
'zipl': {'zipl': 'BootLoaderZipl'}
'systemd_boot': {'systemd_boot': 'BootLoaderSystemdBoot'}
}
try:
(bootloader_namespace, bootloader_name) = \

View File

@ -46,7 +46,6 @@ class BootLoaderConfigBase:
self.root_dir = root_dir
self.boot_dir = boot_dir or root_dir
self.xml_state = xml_state
self.bootloader = xml_state.get_build_type_bootloader_name()
self.arch = Defaults.get_platform_name()
self.volumes_mount = []
@ -381,13 +380,10 @@ class BootLoaderConfigBase:
disk_setup = DiskSetup(self.xml_state, self.boot_dir)
need_boot_partition = disk_setup.need_boot_partition()
if need_boot_partition:
if self.bootloader != 'zipl':
# if an extra boot partition is used we will find the
# data directly in the root of this partition and not
# below the boot/ directory. An exception to this case
# is the zipl bootloader which finds its target
# according to the mount path.
bootpath = '/'
# if an extra boot partition is used we will find the
# data directly in the root of this partition and not
# below the boot/ directory
bootpath = '/'
if target == 'disk':
if not need_boot_partition:
@ -511,7 +507,7 @@ class BootLoaderConfigBase:
self.root_mount = MountManager(
device=root_device
)
if 's390' in self.arch and self.bootloader == 'grub2_s390x_emu':
if 's390' in self.arch:
self.boot_mount = MountManager(
device=boot_device,
mountpoint=self.root_mount.mountpoint + '/boot/zipl'

View File

@ -15,18 +15,12 @@
# You should have received a copy of the GNU General Public License
# along with kiwi. If not, see <http://www.gnu.org/licenses/>
#
import os
import glob
from typing import (
Dict, NamedTuple
)
# project
from kiwi.firmware import FirmWare
from kiwi.bootloader.config.base import BootLoaderConfigBase
from kiwi.defaults import Defaults
from kiwi.utils.os_release import OsRelease
from kiwi.exceptions import KiwiKernelLookupError
target_type = NamedTuple(
'target_type', [
@ -62,15 +56,11 @@ class BootLoaderSpecBase(BootLoaderConfigBase):
Store custom arguments in an instance dict and initialize
target identifiers
"""
self.entries_dir = Defaults.get_bls_loader_entries_dir()
self.target = target_type(
disk='disk', install='install(iso)', live='live(iso)'
)
self.custom_args = custom_args
self.timeout = self.get_boot_timeout_seconds()
self.disk_type = self.xml_state.get_build_type_bootloader_targettype()
self.disk_blocksize = self.xml_state.build_type.get_target_blocksize()
self.firmware = FirmWare(self.xml_state)
self.cmdline = ''
def write(self) -> None:
@ -87,19 +77,6 @@ class BootLoaderSpecBase(BootLoaderConfigBase):
"""
pass
def write_meta_data(
self, root_device=None, write_device=None, boot_options=''
):
"""
For bootloaders following the bootloader spec take over
the cmdline options and store them for later use
:param string root_device: unused
:param string write_device: unused
:param string boot_options: kernel options as string
"""
self.cmdline = f'{self.cmdline} {boot_options}'.strip()
def setup_disk_image_config(
self, boot_uuid: str = '', root_uuid: str = '', hypervisor: str = '',
kernel: str = '', initrd: str = '', boot_options: Dict = {}
@ -122,7 +99,7 @@ class BootLoaderSpecBase(BootLoaderConfigBase):
self.custom_args['kernel'] = kernel
self.custom_args['initrd'] = initrd
self.custom_args['boot_options'] = boot_options
plus_cmdline = ' '.join(
self.cmdline = ' '.join(
[
self.get_boot_cmdline(
boot_options.get('root_device'),
@ -130,7 +107,6 @@ class BootLoaderSpecBase(BootLoaderConfigBase):
)
]
)
self.cmdline = f'{self.cmdline} {plus_cmdline}'.strip()
self.setup_loader(self.target.disk)
def setup_install_image_config(
@ -166,13 +142,12 @@ class BootLoaderSpecBase(BootLoaderConfigBase):
pass
def setup_disk_boot_images(
self, boot_uuid: str, efi_uuid: str = '', lookup_path: str = ''
self, boot_uuid: str, lookup_path: str = ''
) -> None:
"""
Create bootloader image(s) for disk boot
:param string boot_uuid: unused
:param string efi_uuid: unused
:param string mbrid: unused
:param str lookup_path: unused
Targeted to bootloader spec interface
@ -243,30 +218,3 @@ class BootLoaderSpecBase(BootLoaderConfigBase):
Implementation in specialized loader class
"""
raise NotImplementedError
def get_entry_name(self) -> str:
"""
Construct entry filename of the form
[OS_release_ID]-[kernel-version].conf or use the
naming policy of an existing entries file
:return: file basename
:rtype: str
"""
os_release = OsRelease(self.root_dir)
try:
kernel_name = os.path.basename(
list(glob.iglob(f'{self.root_dir}/lib/modules/*'))[0]
)
except Exception as issue:
raise KiwiKernelLookupError(
'Kernel lookup in {0}/lib/modules failed with: {1}'.format(
self.root_dir, issue
)
)
entry_filename = f'{os_release.get("ID")}-{kernel_name}.conf'
for dist_entry in glob.iglob(f'{self.root_dir}/{self.entries_dir}/*'):
entry_filename = os.path.basename(dist_entry)
break
return entry_filename

View File

@ -1,359 +0,0 @@
# Copyright (c) 2024 SUSE Software Solutions Germany GmbH
#
# This file is part of kiwi.
#
# kiwi 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.
#
# kiwi 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 kiwi. If not, see <http://www.gnu.org/licenses/>
#
import os
import copy
import logging
import shutil
from string import Template
from typing import Dict
# project
from kiwi.path import Path
from kiwi.boot.image.base import BootImageBase
from kiwi.bootloader.template.zipl import BootLoaderTemplateZipl
from kiwi.bootloader.config.bootloader_spec_base import BootLoaderSpecBase
from kiwi.command import Command
from kiwi.utils.temporary import Temporary
from kiwi.exceptions import (
KiwiTemplateError,
KiwiBootLoaderTargetError,
KiwiDiskGeometryError
)
log = logging.getLogger('kiwi')
class BootLoaderZipl(BootLoaderSpecBase):
def create_loader_image(self, target: str) -> None:
"""
Nothing to be done here for zipl
:param str target: unused
"""
pass # pragma: nocover
def setup_loader(self, target: str) -> None:
"""
Setup temporary zipl config and install zipl for supported targets.
Please note we are not touching the main zipl.conf file provided
by the distributors
:param str target:
target identifier, one of disk, live(iso) or install(iso)
Currently only the disk identifier is supported
"""
if target != self.target.disk:
raise KiwiBootLoaderTargetError(
'zipl is only supported with the disk image target'
)
boot_path = self.get_boot_path()
boot_options = self.custom_args['boot_options']
self._mount_system(
boot_options.get('root_device'),
boot_options.get('boot_device'),
boot_options.get('efi_device'),
boot_options.get('system_volumes'),
boot_options.get('system_root_volume')
)
root_dir = self.root_mount.mountpoint
kernel_info = BootImageBase(
self.xml_state, root_dir, root_dir
).get_boot_names()
self.custom_args['secure_linux'] = False
self.custom_args['kernel'] = \
f'{boot_path}/{os.path.basename(kernel_info.kernel_filename)}'
self.custom_args['initrd'] = \
f'{boot_path}/{kernel_info.initrd_name}'
host_key_certificates = self.xml_state.get_host_key_certificates()
cc_boot_image = f'{self.custom_args["kernel"]}.cc'
if host_key_certificates:
with Temporary(
path=self.root_mount.mountpoint, prefix='kiwi_zipl_parmfile_'
).new_file() as hkd_parm_file:
with open(hkd_parm_file.name, 'w') as parm_file:
parm_file.write(f'{self.cmdline}{os.linesep}')
genprotimg_init = [
'chroot', root_dir, 'genprotimg',
'--offline', '--verbose',
'-o', cc_boot_image,
'-i', self.custom_args['kernel'],
'-r', self.custom_args['initrd'],
'-p', hkd_parm_file.name.replace(root_dir, '')
]
for host_key_certificate in host_key_certificates:
genprotimg_host_key_check = copy.deepcopy(genprotimg_init)
genprotimg_host_key_check.append('--cert')
genprotimg_host_key_check.append(
host_key_certificates[0]['hkd_ca_cert']
)
genprotimg_host_key_check.append('--cert')
genprotimg_host_key_check.append(
host_key_certificate['hkd_sign_cert']
)
for host_key in host_key_certificate.get('hkd_cert'):
genprotimg_host_key_check.append('-k')
genprotimg_host_key_check.append(host_key)
for host_crl in host_key_certificate.get('hkd_revocation_list'):
genprotimg_host_key_check.append('--crl')
genprotimg_host_key_check.append(host_crl)
log.info(
'Checking HKDs for signing cert: {}'.format(
host_key_certificate['hkd_sign_cert']
)
)
Command.run(genprotimg_host_key_check)
os.unlink(f'{root_dir}/{cc_boot_image}')
genprotimg = genprotimg_init
genprotimg.append('--no-verify')
for host_key_certificate in host_key_certificates:
for host_key in host_key_certificate.get('hkd_cert'):
genprotimg.append('-k')
genprotimg.append(host_key)
for host_crl in host_key_certificate.get('hkd_revocation_list'):
genprotimg.append('--crl')
genprotimg.append(host_crl)
Command.run(genprotimg)
secure_execution_header = 'secure_execution_header.bin'
Command.run(
[
'chroot', root_dir, 'pvextract-hdr',
'-o', secure_execution_header,
cc_boot_image
]
)
shutil.move(
os.sep.join([root_dir, secure_execution_header]),
os.sep.join(
[
self.custom_args['target_dir'],
'{0}.{1}-{2}.{3}'.format(
self.xml_state.xml_data.get_name(),
self.arch,
self.xml_state.get_image_version(),
secure_execution_header
)
]
)
)
attestation_request = 'attestation_request.bin'
attestation_response_key = 'attestation_response.key'
pvattest = [
'chroot', root_dir, 'pvattest', 'create',
'--no-verify', '--verbose',
'-o', attestation_request,
'--arpk', attestation_response_key
]
for host_key_certificate in host_key_certificates:
for host_key in host_key_certificate.get('hkd_cert'):
pvattest.append('-k')
pvattest.append(host_key)
Command.run(pvattest)
shutil.move(
os.sep.join([root_dir, attestation_request]),
os.sep.join(
[
self.custom_args['target_dir'],
'{0}.{1}-{2}.{3}'.format(
self.xml_state.xml_data.get_name(),
self.arch,
self.xml_state.get_image_version(),
attestation_request
)
]
)
)
shutil.move(
os.sep.join([root_dir, attestation_response_key]),
os.sep.join(
[
self.custom_args['target_dir'],
'{0}.{1}-{2}.{3}'.format(
self.xml_state.xml_data.get_name(),
self.arch,
self.xml_state.get_image_version(),
attestation_response_key
)
]
)
)
self.custom_args['secure_linux'] = True
self.custom_args['secure_image_file'] = cc_boot_image
os.unlink(f'{root_dir}/{self.custom_args["kernel"]}')
os.unlink(f'{root_dir}/{self.custom_args["initrd"]}')
self.custom_args['kernel'] = ''
self.custom_args['initrd'] = ''
with Temporary(
path=self.root_mount.mountpoint, prefix='kiwi_zipl.conf_'
).new_file() as runtime_zipl_config_file:
BootLoaderZipl._write_config_file(
BootLoaderTemplateZipl().get_loader_template(),
runtime_zipl_config_file.name,
self._get_template_parameters()
)
self.set_loader_entry(
self.root_mount.mountpoint, self.target.disk
)
self._install_zipl(root_dir, runtime_zipl_config_file.name)
def set_loader_entry(self, root_dir: str, target: str) -> None:
"""
Setup/update loader entries of the form
{boot_path}/loader/entries/{get_entry_name}
:param str target:
target identifier, one of disk, live(iso) or install(iso)
"""
entry_name = self.get_entry_name()
BootLoaderZipl._write_config_file(
BootLoaderTemplateZipl().get_entry_template(
self.custom_args['secure_linux']
),
root_dir + f'{self.entries_dir}/{entry_name}',
self._get_template_parameters(entry_name)
)
def _install_zipl(self, root_dir: str, zipl_config: str) -> None:
"""
Install zipl on target
"""
zipl = [
'chroot', root_dir, 'zipl',
'--noninteractive',
'--config', zipl_config.replace(root_dir, ''),
'--blsdir', self.entries_dir,
'--verbose'
]
self.sys_mount.umount()
Command.run(zipl)
def _get_template_parameters(
self, default_entry: str = ''
) -> Dict[str, str]:
disk_type = self.disk_type or 'SCSI'
disk_type = disk_type if disk_type != 'GPT' else 'SCSI'
blocksize = self.disk_blocksize or 512
unsupported_for_target_geometry = ['FBA', 'SCSI']
targetbase = f'targetbase={self.custom_args.get("targetbase")}'
geometry = ''
if disk_type not in unsupported_for_target_geometry:
geometry = f'targetgeometry={self._get_disk_geometry()}'
return {
'secure_image_file': self.custom_args.get('secure_image_file') or '',
'kernel_file': self.custom_args['kernel'] or 'vmlinuz',
'initrd_file': self.custom_args['initrd'] or 'initrd',
'boot_options': self.cmdline,
'boot_timeout': self.timeout,
'bootpath': self.get_boot_path(),
'targetbase': targetbase,
'targettype': disk_type,
'targetblocksize': format(blocksize),
'targetoffset': self._get_partition_start(),
'targetgeometry': geometry,
'title': self.get_menu_entry_title(),
'default_entry': default_entry
}
def _get_disk_geometry(self) -> str:
target_table_type = self.firmware.get_partition_table_type()
disk_device = self.custom_args['targetbase']
disk_geometry = ''
if target_table_type == 'dasd':
disk_geometry = '{0},{1},{2}'.format(
self._get_dasd_disk_geometry_element(
disk_device, 'cylinders'
),
self._get_dasd_disk_geometry_element(
disk_device, 'tracks per cylinder'
),
self._get_dasd_disk_geometry_element(
disk_device, 'blocks per track'
)
)
return disk_geometry
def _get_partition_start(self) -> str:
target_table_type = self.firmware.get_partition_table_type()
disk_device = self.custom_args['targetbase']
if target_table_type == 'dasd':
blocks = self._get_dasd_disk_geometry_element(
disk_device, 'blocks per track'
)
fdasd_command = [
'fdasd', '-f', '-s', '-p', disk_device,
'|', 'grep', '"^ "',
'|', 'head', '-n', '1',
'|', 'tr', '-s', '" "'
]
fdasd_call = Command.run(
['bash', '-c', ' '.join(fdasd_command)]
)
fdasd_output = fdasd_call.output
try:
start_track = int(fdasd_output.split(' ')[2].lstrip())
except Exception:
raise KiwiDiskGeometryError(
f'unknown partition format: {fdasd_output}'
)
return '{0}'.format(start_track * blocks)
else:
sfdisk_command = ' '.join(
[
'sfdisk', '--dump', disk_device,
'|', 'grep', '"1 :"',
'|', 'cut', '-f1', '-d,',
'|', 'cut', '-f2', '-d='
]
)
return Command.run(
['bash', '-c', sfdisk_command]
).output.strip()
def _get_dasd_disk_geometry_element(self, disk_device, search) -> int:
fdasd = ['fdasd', '-f', '-p', disk_device]
bash_command = fdasd + ['|', 'grep', '"' + search + '"']
fdasd_call = Command.run(
['bash', '-c', ' '.join(bash_command)]
)
fdasd_output = fdasd_call.output
try:
return int(fdasd_output.split(':')[1].lstrip())
except Exception:
raise KiwiDiskGeometryError(
f'unknown format for disk geometry: {fdasd_output}'
)
@staticmethod
def _write_config_file(
template: Template, filename: str, parameters: Dict[str, str]
) -> None:
try:
config_data = template.substitute(parameters)
Path.create(os.path.dirname(filename))
with open(filename, 'w') as config:
config.write(config_data)
except Exception as issue:
raise KiwiTemplateError(
'{0}: {1}'.format(type(issue).__name__, issue)
)

View File

@ -48,8 +48,7 @@ class BootLoaderInstall(metaclass=ABCMeta):
name_map = {
'grub2': {'grub2': 'BootLoaderInstallGrub2'},
'grub2_s390x_emu': {'grub2': 'BootLoaderInstallGrub2'},
'systemd_boot': {'systemd_boot': 'BootLoaderInstallSystemdBoot'},
'zipl': {'zipl': 'BootLoaderInstallZipl'}
'systemd_boot': {'systemd_boot': 'BootLoaderInstallSystemdBoot'}
}
try:
(bootloader_namespace, bootloader_name) = \

View File

@ -1,66 +0,0 @@
# Copyright (c) 2024 SUSE Software Solutions Germany GmbH. All rights reserved.
#
# This file is part of kiwi.
#
# kiwi 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.
#
# kiwi 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 kiwi. If not, see <http://www.gnu.org/licenses/>
#
import logging
from typing import Dict
# project
from kiwi.bootloader.install.base import BootLoaderInstallBase
log = logging.getLogger('kiwi')
class BootLoaderInstallZipl(BootLoaderInstallBase):
"""
**zipl bootloader installation**
"""
def post_init(self, custom_args: Dict):
"""
zipl post initialization method
:param dict custom_args: unused
"""
self.custom_args = custom_args
def install_required(self) -> bool:
"""
Check if zipl needs to install boot code
zipl requires boot code installation, but it is done as
part of the BLS implementation in bootloader/config/zipl.py
Thus this method always returns: False
:return: False
:rtype: bool
"""
return False
def install(self):
"""
Not required for zipl, handled in config stage
"""
pass
def secure_boot_install(self):
"""
Run shim installation for secure boot setup
For zipl this is skipped since details for
secure boot are not yet clear.
"""
pass

View File

@ -1,79 +0,0 @@
# Copyright (c) 2024 SUSE Software Solutions Germany GmbH. All rights reserved.
#
# This file is part of kiwi.
#
# kiwi 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.
#
# kiwi 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 kiwi. If not, see <http://www.gnu.org/licenses/>
#
from string import Template
from textwrap import dedent
class BootLoaderTemplateZipl:
"""
**zipl configuraton file templates**
"""
def __init__(self):
self.cr = '\n'
self.main_conf = dedent('''
[defaultboot]
defaultauto
prompt=1
target=${bootpath}
${targetbase}
targettype=${targettype}
targetblocksize=${targetblocksize}
targetoffset=${targetoffset}
${targetgeometry}
timeout=${boot_timeout}
secure=auto
''').strip() + self.cr
self.entry_secure = dedent('''
title ${title}
options ${boot_options}
linux ${secure_image_file}
''').strip() + self.cr
self.entry_standard = dedent('''
title ${title}
options ${boot_options}
linux ${kernel_file}
initrd ${initrd_file}
''').strip() + self.cr
def get_loader_template(self) -> Template:
"""
Bootloader main configuration template
:return: instance of :class:`Template`
:rtype: Template
"""
template_data = self.main_conf
return Template(template_data)
def get_entry_template(self, secure: bool = False) -> Template:
"""
Bootloader entry configuration template
:return: instance of :class:`Template`
:rtype: Template
"""
if secure:
template_data = self.entry_secure
else:
template_data = self.entry_standard
return Template(template_data)

View File

@ -731,8 +731,7 @@ class DiskBuilder:
exclude_list.append(
'{0}/.*'.format(self.spare_part_mountpoint.lstrip(os.sep))
)
if 'boot' in device_map \
and 's390' in self.arch and self.bootloader == 'grub2_s390x_emu':
if 'boot' in device_map and 's390' in self.arch:
exclude_list.append('boot/zipl/*')
exclude_list.append('boot/zipl/.*')
elif 'boot' in device_map:
@ -847,7 +846,7 @@ class DiskBuilder:
if not boot_filesystem:
boot_filesystem = self.requested_filesystem
boot_directory = self.root_dir + '/boot/'
if 's390' in self.arch and self.bootloader == 'grub2_s390x_emu':
if 's390' in self.arch:
boot_directory = self.root_dir + '/boot/zipl/'
log.info(
'Creating boot(%s) filesystem on %s',
@ -1139,7 +1138,7 @@ class DiskBuilder:
custom_root_mount_args, fs_check_interval
)
if device_map.get('boot'):
if 's390' in self.arch and self.bootloader == 'grub2_s390x_emu':
if 's390' in self.arch:
boot_mount_point = '/boot/zipl'
else:
boot_mount_point = '/boot'

View File

@ -570,17 +570,6 @@ class Defaults:
"""
return 'grub2'
@staticmethod
def get_bls_loader_entries_dir() -> str:
"""
Provide default loader entries directory for BLS loaders
:return: directory name
:rtype: str
"""
return '/boot/loader/entries'
@staticmethod
def get_grub_boot_directory_name(lookup_path):
"""

View File

@ -379,12 +379,6 @@ class KiwiKernelLookupError(KiwiError):
"""
class KiwiOSReleaseImportError(KiwiError):
"""
Exception raised if the os-release file could not be imported
"""
class KiwiLiveBootImageError(KiwiError):
"""
Exception raised if an attempt was made to use an

View File

@ -65,8 +65,6 @@ class FirmWare:
if 's390' in self.arch:
if self.zipl_target_type and 'CDL' in self.zipl_target_type:
return 'dasd'
elif self.zipl_target_type and 'GPT' in self.zipl_target_type:
return 'gpt'
else:
return 'msdos'
elif 'ppc64' in self.arch:

View File

@ -2741,7 +2741,7 @@ div {
## user which can be done by using the editbootinstall and
## editbootconfig custom scripts
attribute name {
"grub2" | "isolinux" | "grub2_s390x_emu" | "systemd_boot" | "custom" | "zipl"
"grub2" | "isolinux" | "grub2_s390x_emu" | "systemd_boot" | "custom"
}
>> sch:pattern [ id = "loader_name" is-a = "bootloader_image_type"
sch:param [ name = "attr" value = "name" ]
@ -2785,7 +2785,7 @@ div {
attribute timeout { xsd:nonNegativeInteger }
>> sch:pattern [ id = "timeout" is-a = "bootloader_name_type"
sch:param [ name = "attr" value = "timeout" ]
sch:param [ name = "types" value = "grub2 isolinux grub2_s390x_emu systemd_boot zipl" ]
sch:param [ name = "types" value = "grub2 isolinux grub2_s390x_emu systemd_boot" ]
]
k.bootloader.timeout_style.attribute =
## Specifies the boot timeout style to control the way in which
@ -2805,11 +2805,11 @@ div {
## devices use SCSI, for emulated DASD devices use FBA,
## for 4k DASD devices use CDL
attribute targettype {
"FBA" | "SCSI" | "CDL" | "GPT"
"FBA" | "SCSI" | "CDL"
}
>> sch:pattern [ id = "targettype" is-a = "bootloader_name_type"
sch:param [ name = "attr" value = "targettype" ]
sch:param [ name = "types" value = "grub2_s390x_emu zipl" ]
sch:param [ name = "types" value = "grub2_s390x_emu" ]
]
k.bootloader.use_disk_password.attribute =
## When /boot is encrypted, make the boot loader store the
@ -2831,8 +2831,7 @@ div {
## and to provide configuration parameters for it
element bootloader {
k.bootloader.attlist &
k.bootloadersettings? &
k.securelinux*
k.bootloadersettings?
}
}
@ -3094,74 +3093,6 @@ div {
}
}
#==========================================
# main block: <securelinux>
#
div {
sch:pattern [
abstract = "true"
id = "securelinux_loader_requirement"
sch:rule [
context = "securelinux"
sch:assert [
test = "../@name='zipl' or ../@name='grub2-s390x-emu'"
"<securelinux> section only available for the "
"bootloader types: zipl and grub2-s390x-emu"
]
]
]
k.securelinux.hkd_ca_cert.attribute =
attribute hkd_ca_cert { text }
k.securelinux.hkd_sign_cert.attribute =
attribute hkd_sign_cert { text }
k.securelinux.attlist =
k.securelinux.hkd_ca_cert.attribute &
k.securelinux.hkd_sign_cert.attribute
k.securelinux =
## securelinux contains all elements to describe
## data required to setup a secure linux execution
## process for the individual architecture in the scope
## of the bootloader process
element securelinux {
k.securelinux.attlist &
k.hkd_cert+ &
k.hkd_revocation_list*
}
>> sch:pattern [
id = "bootloader_matches" is-a = "securelinux_loader_requirement"
]
}
#==========================================
# common element <hkd_cert>
#
div {
k.hkd_cert.name.attribute =
attribute name { text }
k.hkd_cert.attlist =
k.hkd_cert.name.attribute
k.hkd_cert =
element hkd_cert {
k.hkd_cert.attlist,
empty
}
}
#==========================================
# common element <hkd_revocation_list>
#
div {
k.hkd_revocation_list.name.attribute =
attribute name { text }
k.hkd_revocation_list.attlist =
k.hkd_revocation_list.name.attribute
k.hkd_revocation_list =
element hkd_revocation_list {
k.hkd_revocation_list.attlist,
empty
}
}
#==========================================
# main block: <environment>
#

View File

@ -4111,7 +4111,6 @@ editbootconfig custom scripts</a:documentation>
<value>grub2_s390x_emu</value>
<value>systemd_boot</value>
<value>custom</value>
<value>zipl</value>
</choice>
</attribute>
<sch:pattern id="loader_name" is-a="bootloader_image_type">
@ -4166,7 +4165,7 @@ to 10sec</a:documentation>
</attribute>
<sch:pattern id="timeout" is-a="bootloader_name_type">
<sch:param name="attr" value="timeout"/>
<sch:param name="types" value="grub2 isolinux grub2_s390x_emu systemd_boot zipl"/>
<sch:param name="types" value="grub2 isolinux grub2_s390x_emu systemd_boot"/>
</sch:pattern>
</define>
<define name="k.bootloader.timeout_style.attribute">
@ -4196,12 +4195,11 @@ for 4k DASD devices use CDL</a:documentation>
<value>FBA</value>
<value>SCSI</value>
<value>CDL</value>
<value>GPT</value>
</choice>
</attribute>
<sch:pattern id="targettype" is-a="bootloader_name_type">
<sch:param name="attr" value="targettype"/>
<sch:param name="types" value="grub2_s390x_emu zipl"/>
<sch:param name="types" value="grub2_s390x_emu"/>
</sch:pattern>
</define>
<define name="k.bootloader.use_disk_password.attribute">
@ -4247,9 +4245,6 @@ and to provide configuration parameters for it</a:documentation>
<optional>
<ref name="k.bootloadersettings"/>
</optional>
<zeroOrMore>
<ref name="k.securelinux"/>
</zeroOrMore>
</interleave>
</element>
</define>
@ -4617,74 +4612,6 @@ of the storage device</a:documentation>
</element>
</define>
</div>
<!--
==========================================
main block: <securelinux>
-->
<div>
<sch:pattern abstract="true" id="securelinux_loader_requirement">
<sch:rule context="securelinux">
<sch:assert test="../@name='zipl' or ../@name='grub2-s390x-emu'">&lt;securelinux&gt; section only available for the bootloader types: zipl and grub2-s390x-emu</sch:assert>
</sch:rule>
</sch:pattern>
<define name="k.securelinux.attlist">
<interleave>
<attribute name="hkd_ca_cert"/>
<attribute name="hkd_sign_cert"/>
</interleave>
</define>
<define name="k.securelinux">
<element name="securelinux">
<a:documentation>securelinux contains all elements to describe
data required to setup a secure linux execution
process for the individual architecture in the scope
of the bootloader process</a:documentation>
<interleave>
<ref name="k.securelinux.attlist"/>
<oneOrMore>
<ref name="k.hkd_cert"/>
</oneOrMore>
<zeroOrMore>
<ref name="k.hkd_revocation_list"/>
</zeroOrMore>
</interleave>
</element>
<sch:pattern id="bootloader_matches" is-a="securelinux_loader_requirement"/>
</define>
</div>
<!--
==========================================
common element <hkd_cert>
-->
<div>
<define name="k.hkd_cert.attlist">
<attribute name="name"/>
</define>
<define name="k.hkd_cert">
<element name="hkd_cert">
<ref name="k.hkd_cert.attlist"/>
<empty/>
</element>
</define>
</div>
<!--
==========================================
common element <hkd_revocation_list>
-->
<div>
<define name="k.hkd_revocation_list.attlist">
<attribute name="name"/>
</define>
<define name="k.hkd_revocation_list">
<element name="hkd_revocation_list">
<ref name="k.hkd_revocation_list.attlist"/>
<empty/>
</element>
</define>
</div>
<!--
==========================================
main block: <environment>

View File

@ -1,63 +0,0 @@
# Copyright (c) 2024 SUSE Software Solutions Germany GmbH. All rights reserved.
#
# This file is part of kiwi.
#
# kiwi 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.
#
# kiwi 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 kiwi. If not, see <http://www.gnu.org/licenses/>
#
import csv
from io import TextIOWrapper
from typing import Iterable
# project
from kiwi.exceptions import KiwiOSReleaseImportError
class OsRelease:
"""
**Read os-release information**
"""
def __init__(self, root_dir: str):
self.data = {}
os_release = root_dir + '/etc/os-release'
try:
with open(os_release) as osdata:
reader = csv.reader(OsRelease._rip(osdata), delimiter='=')
self.data = dict(reader)
except Exception as issue:
raise KiwiOSReleaseImportError(
f'Import of {os_release} failed with {issue}'
)
@staticmethod
def _is_comment(line: str) -> bool:
return line.startswith('#')
@staticmethod
def _is_whitespace(line: str) -> bool:
return line.isspace()
@staticmethod
def _rip(csvfile: TextIOWrapper) -> Iterable[str]:
for row in csvfile:
if not OsRelease._is_comment(row) \
and not OsRelease._is_whitespace(row):
yield row
def get(self, key: str) -> str:
"""
Return value for key or an empty string if not present
:param string key: key name from os-release
"""
return self.data.get(key) or ''

View File

@ -17,7 +17,7 @@
#
import os
from typing import (
List, Optional, Any, Dict, NamedTuple, Union
List, Optional, Any, Dict, NamedTuple
)
import re
import logging
@ -1123,47 +1123,6 @@ class XMLState:
return bootloader.get_use_disk_password()
return False
def get_build_type_bootloader_securelinux_section(self) -> List[Any]:
"""
First securelinux section from the build
type bootloader section
:return: <securelinux> section reference
:rtype: xml_parse::securelinux
"""
bootloader_section = self.get_build_type_bootloader_section()
bootloader_securelinux_section = []
get_securelinux = getattr(
bootloader_section, 'get_securelinux', None
)
if get_securelinux and get_securelinux():
bootloader_securelinux_section = get_securelinux()
return bootloader_securelinux_section
def get_host_key_certificates(
self
) -> Union[List[Dict[str, List[str]]], List[Dict[str, str]]]:
cc_result = []
cc_certificates: Dict[str, List[str]] = {}
securelinux_list = \
self.get_build_type_bootloader_securelinux_section()
for securelinux in securelinux_list:
cc_certificates = {
'hkd_cert': [],
'hkd_revocation_list': [],
'hkd_ca_cert': securelinux.get_hkd_ca_cert(),
'hkd_sign_cert': securelinux.get_hkd_sign_cert()
}
for hkd_cert in securelinux.get_hkd_cert():
cc_certificates['hkd_cert'].append(hkd_cert.get_name())
for hkd_revocation_list in securelinux.get_hkd_revocation_list():
cc_certificates['hkd_revocation_list'].append(
hkd_revocation_list.get_name()
)
cc_result.append(cc_certificates)
return cc_result
def get_build_type_oemconfig_section(self) -> Any:
"""
First oemconfig section from the build type section

View File

@ -353,7 +353,6 @@ class TestBootLoaderConfigBase:
return mount_managers.pop()
self.bootloader.arch = 's390x'
self.bootloader.bootloader = 'grub2_s390x_emu'
mock_MountManager.side_effect = mount_managers_effect
self.bootloader._mount_system(

View File

@ -17,12 +17,6 @@ class TestBootLoaderSystemdBoot:
self.state.xml_data.get_name.return_value = 'image-name'
self.state.get_image_version.return_value = 'image-version'
self.state.build_type.get_efifatimagesize.return_value = None
self.state.build_type.get_firmware.return_value = None
self.state.build_type.get_efipartsize.return_value = None
self.state.build_type.get_efiparttable.return_value = None
self.state.build_type.get_target_blocksize.return_value = None
self.state.get_build_type_bootloader_targettype.return_value = None
self.state.get_build_type_bootloader_name.return_value = 'systemd_boot'
self.bootloader = BootLoaderSystemdBoot(self.state, 'root_dir')
self.bootloader.custom_args['kernel'] = None
self.bootloader.custom_args['initrd'] = None

View File

@ -1,344 +0,0 @@
import io
from pytest import raises
from unittest.mock import (
Mock, patch, call, MagicMock
)
from kiwi.bootloader.config.zipl import BootLoaderZipl
from kiwi.bootloader.config.bootloader_spec_base import BootLoaderSpecBase
from kiwi.command import command_type
from kiwi.exceptions import (
KiwiTemplateError,
KiwiBootLoaderTargetError,
KiwiDiskGeometryError
)
class TestBootLoaderZipl:
@patch('kiwi.bootloader.config.bootloader_spec_base.FirmWare')
def setup(self, mock_FirmWare):
self.firmware = Mock()
mock_FirmWare.return_value = self.firmware
self.state = Mock()
self.state.get_host_key_certificates.return_value = [
{
'hkd_cert': ['/path/to/host.crt'],
'hkd_revocation_list': ['/path/to/revocation-list.crl'],
'hkd_ca_cert': '/path/to/DigiCertCA.crt',
'hkd_sign_cert': '/path/to/ibm-z-host-key-signing.crt'
}
]
self.state.get_build_type_bootloader_targettype.return_value = 'CDL'
self.state.build_type.get_target_blocksize.return_value = 4096
self.state.xml_data.get_name.return_value = 'image-name'
self.state.get_image_version.return_value = 'image-version'
self.state.build_type.get_efifatimagesize.return_value = None
self.bootloader = BootLoaderZipl(self.state, 'root_dir')
self.bootloader.cmdline = 'options'
self.bootloader.custom_args['kernel'] = None
self.bootloader.custom_args['initrd'] = None
self.bootloader.custom_args['boot_options'] = {}
self.bootloader.custom_args['targetbase'] = '/dev/disk'
self.bootloader.custom_args['target_dir'] = 'target_dir'
self.bootloader.sys_mount = Mock(
mountpoint='sys_mount'
)
self.bootloader.root_mount = Mock(
mountpoint='system_root_mount'
)
self.bootloader._mount_system = Mock()
self.bootloader.create_efi_path = Mock()
self.bootloader.get_boot_path = Mock(
return_value='bootpath'
)
self.bootloader.get_menu_entry_title = Mock(
return_value='title'
)
self.bootloader.arch = 's390x'
@patch('kiwi.bootloader.config.bootloader_spec_base.FirmWare')
def setup_method(self, cls, mock_FirmWare):
self.setup()
@patch('os.unlink')
def test_setup_loader_raises_invalid_target(self, mock_os_unlink):
with raises(KiwiBootLoaderTargetError):
self.bootloader.setup_loader('iso')
@patch('shutil.move')
@patch('os.unlink')
@patch('os.path.exists')
@patch('kiwi.bootloader.config.zipl.BootImageBase.get_boot_names')
@patch('kiwi.bootloader.config.zipl.Path.create')
@patch('kiwi.bootloader.config.zipl.Command.run')
@patch('kiwi.bootloader.config.zipl.BootLoaderTemplateZipl')
@patch('kiwi.bootloader.config.zipl.Temporary.new_file')
@patch.object(BootLoaderZipl, '_write_config_file')
@patch.object(BootLoaderZipl, '_get_template_parameters')
@patch.object(BootLoaderSpecBase, 'get_entry_name')
def test_setup_loader(
self, mock_get_entry_name, mock_get_template_parameters,
mock_write_config_file, mock_Temporary_new_file,
mock_BootLoaderTemplateZipl, mock_Command_run,
mock_Path_create, mock_BootImageBase_get_boot_names,
mock_os_path_exists, mock_os_unlink, mock_shutil_move
):
temporary = Mock()
temporary.name = 'system_root_mount/kiwi_zipl.conf_'
mock_Temporary_new_file.return_value.__enter__.return_value = temporary
mock_get_entry_name.return_value = \
'opensuse-leap-5.3.18-59.10-default.conf'
mock_get_template_parameters.return_value = {
'targetbase': '/dev/loop'
}
kernel_info = Mock()
kernel_info.kernel_version = 'kernel-version'
kernel_info.kernel_filename = 'kernel-filename'
kernel_info.initrd_name = 'initrd-name'
mock_os_path_exists.return_value = True
mock_BootImageBase_get_boot_names.return_value = kernel_info
with patch('builtins.open', create=True) as mock_open:
mock_open.return_value = MagicMock(spec=io.IOBase)
file_handle = mock_open.return_value.__enter__.return_value
self.bootloader.setup_loader('disk')
file_handle.write.assert_called_once_with('options\n')
assert mock_write_config_file.call_args_list == [
call(
mock_BootLoaderTemplateZipl.
return_value.get_loader_template.return_value,
'system_root_mount/kiwi_zipl.conf_',
mock_get_template_parameters.return_value
),
call(
mock_BootLoaderTemplateZipl.
return_value.get_entry_template.return_value,
'system_root_mount/boot/loader/entries/'
'opensuse-leap-5.3.18-59.10-default.conf',
mock_get_template_parameters.return_value
)
]
assert self.bootloader._mount_system.called
assert self.bootloader.sys_mount.umount.called
assert mock_Command_run.call_args_list == [
call(
[
'chroot', 'system_root_mount', 'genprotimg',
'--offline', '--verbose',
'-o', 'bootpath/kernel-filename.cc',
'-i', 'bootpath/kernel-filename',
'-r', 'bootpath/initrd-name',
'-p', temporary.name.replace('system_root_mount', ''),
'--cert', '/path/to/DigiCertCA.crt',
'--cert', '/path/to/ibm-z-host-key-signing.crt',
'-k', '/path/to/host.crt',
'--crl', '/path/to/revocation-list.crl'
]
),
call(
[
'chroot', 'system_root_mount', 'genprotimg',
'--offline', '--verbose',
'-o', 'bootpath/kernel-filename.cc',
'-i', 'bootpath/kernel-filename',
'-r', 'bootpath/initrd-name',
'-p', temporary.name.replace('system_root_mount', ''),
'--no-verify',
'-k', '/path/to/host.crt',
'--crl', '/path/to/revocation-list.crl',
]
),
call(
[
'chroot', 'system_root_mount', 'pvextract-hdr',
'-o', 'secure_execution_header.bin',
'bootpath/kernel-filename.cc'
]
),
call(
[
'chroot', 'system_root_mount', 'pvattest', 'create',
'--no-verify', '--verbose',
'-o', 'attestation_request.bin',
'--arpk', 'attestation_response.key',
'-k', '/path/to/host.crt'
]
),
call(
[
'chroot', 'system_root_mount', 'zipl',
'--noninteractive',
'--config', '/kiwi_zipl.conf_',
'--blsdir', '/boot/loader/entries',
'--verbose'
]
)
]
assert mock_shutil_move.call_args_list == [
call(
'system_root_mount/secure_execution_header.bin',
'target_dir/image-name.s390x-image-version.'
'secure_execution_header.bin'
),
call(
'system_root_mount/attestation_request.bin',
'target_dir/image-name.s390x-image-version.'
'attestation_request.bin'
),
call(
'system_root_mount/attestation_response.key',
'target_dir/image-name.s390x-image-version.'
'attestation_response.key'
)
]
assert mock_os_unlink.call_args_list == [
call('system_root_mount/bootpath/kernel-filename.cc'),
call('system_root_mount/bootpath/kernel-filename'),
call('system_root_mount/bootpath/initrd-name')
]
@patch.object(BootLoaderZipl, '_get_disk_geometry')
@patch.object(BootLoaderZipl, '_get_partition_start')
def test_get_template_parameters(
self, mock_get_partition_start, mock_get_disk_geometry
):
mock_get_partition_start.return_value = '42'
mock_get_disk_geometry.return_value = '123,53,9'
self.bootloader.timeout = 0
self.bootloader.disk_type = 'CDL'
self.bootloader.disk_blocksize = 4096
assert self.bootloader._get_template_parameters() == {
'secure_image_file': 'bootpath/kernel-filename.cc',
'kernel_file': 'vmlinuz',
'initrd_file': 'initrd',
'boot_options': 'options',
'boot_timeout': 0,
'bootpath': 'bootpath',
'targetbase': 'targetbase=/dev/disk',
'targettype': 'CDL',
'targetblocksize': '4096',
'targetoffset': '42',
'targetgeometry': 'targetgeometry=123,53,9',
'title': 'title',
'default_entry': ''
}
@patch('kiwi.bootloader.config.zipl.Path.create')
@patch.object(BootLoaderZipl, '_get_template_parameters')
def test_write_config_file(
self, mock_get_template_parameters, mock_Path_create
):
template = Mock()
template.substitute.return_value = 'data'
with patch('builtins.open', create=True) as mock_open:
mock_open.return_value = MagicMock(spec=io.IOBase)
file_handle = mock_open.return_value.__enter__.return_value
self.bootloader._write_config_file(template, 'path/some-file', {})
mock_Path_create.assert_called_once_with('path')
mock_open.assert_called_once_with('path/some-file', 'w')
file_handle.write.assert_called_once_with('data')
def test_write_config_file_raises(self):
template = Mock()
template.substitute.side_effect = Exception
with raises(KiwiTemplateError):
self.bootloader._write_config_file(template, 'some-file', {})
@patch('kiwi.bootloader.config.zipl.Command.run')
def test_get_disk_geometry(self, mock_Command_run):
command_return_values = [
command_type(
output=' cylinders ............: 10017\n',
error='', returncode=0
),
command_type(
output=' tracks per cylinder ..: 15\n',
error='', returncode=0
),
command_type(
output=' blocks per track .....: 12\n',
error='', returncode=0
)
]
def command_run(arg):
return command_return_values.pop(0)
self.firmware.get_partition_table_type.return_value = 'dasd'
mock_Command_run.side_effect = command_run
assert self.bootloader._get_disk_geometry() == '10017,15,12'
@patch('kiwi.bootloader.config.zipl.Command.run')
def test_get_partition_start_dasd(self, mock_Command_run):
self.firmware.get_partition_table_type.return_value = 'dasd'
command_return_values = [
command_type(
output=' blocks per track .....: 12\n',
error='', returncode=0
),
command_type(
output=' /dev/loop01 2 6401 6400 1 Linux native\n',
error='', returncode=0
)
]
def command_run(arg):
return command_return_values.pop(0)
mock_Command_run.side_effect = command_run
assert self.bootloader._get_partition_start() == '24'
assert mock_Command_run.call_args_list == [
call(
[
'bash', '-c',
'fdasd -f -p /dev/disk | grep "blocks per track"'
]
),
call(
[
'bash', '-c',
'fdasd -f -s -p /dev/disk | grep "^ " |'
' head -n 1 | tr -s " "'
]
)
]
@patch('kiwi.bootloader.config.zipl.Command.run')
def test_get_partition_start_not_dasd(self, mock_Command_run):
self.firmware.get_partition_table_type.return_value = 'msdos'
command = Mock()
command.output = ' 2048'
mock_Command_run.return_value = command
assert self.bootloader._get_partition_start() == '2048'
mock_Command_run.assert_called_once_with(
[
'bash', '-c',
'sfdisk --dump /dev/disk | grep "1 :" |'
' cut -f1 -d, | cut -f2 -d='
]
)
@patch('kiwi.bootloader.config.zipl.Command.run')
@patch.object(BootLoaderZipl, '_get_dasd_disk_geometry_element')
def test_get_partition_start_raises(
self, mock_get_dasd_disk_geometry_element, mock_Command_run
):
self.firmware.get_partition_table_type.return_value = 'dasd'
mock_Command_run.return_value = command_type(
output='bogus data', error='', returncode=0
)
with raises(KiwiDiskGeometryError):
self.bootloader._get_partition_start()
@patch('kiwi.bootloader.config.zipl.Command.run')
def test_get_dasd_disk_geometry_element_raises(
self, mock_Command_run
):
self.bootloader.target_table_type = 'dasd'
mock_Command_run.return_value = command_type(
output='bogus data', error='', returncode=0
)
with raises(KiwiDiskGeometryError):
self.bootloader._get_dasd_disk_geometry_element(
'/dev/disk', 'tracks per cylinder'
)

View File

@ -1,22 +0,0 @@
from unittest.mock import Mock
from kiwi.bootloader.install.zipl import BootLoaderInstallZipl
class TestBootLoaderInstallZipl:
def setup(self):
self.bootloader = BootLoaderInstallZipl(
'root_dir', Mock()
)
def setup_method(self, cls):
self.setup()
def test_install_required(self):
assert self.bootloader.install_required() is False
def test_install(self):
self.bootloader.install()
def test_secure_boot_install(self):
self.bootloader.secure_boot_install()

View File

@ -1,35 +0,0 @@
from kiwi.bootloader.template.zipl import BootLoaderTemplateZipl
class TestBootLoaderTemplateZipl:
def setup(self):
self.zipl = BootLoaderTemplateZipl()
def setup_method(self, cls):
self.setup()
def test_get_loader_template(self):
assert self.zipl.get_loader_template().substitute(
boot_timeout='10',
bootpath='/boot',
targetbase='',
targettype='SCSI',
targetblocksize='512',
targetoffset='2048',
targetgeometry=''
)
def test_get_entry_template_standard(self):
assert self.zipl.get_entry_template().substitute(
title='title',
boot_options='',
kernel_file='linux',
initrd_file='initrd'
)
def test_get_entry_template_secure(self):
assert self.zipl.get_entry_template(secure=True).substitute(
title='title',
boot_options='',
secure_image_file='linux'
)

View File

@ -24,9 +24,6 @@ class TestCloneDevice:
self.storage_device, 'root_dir'
)
def setup_method(self, cls):
self.setup()
@patch('kiwi.storage.clone_device.Command.run')
@patch('kiwi.storage.clone_device.BlockID')
@patch('kiwi.storage.clone_device.FileSystem.new')