4k Block device support

Adds support for the block device size to be asserted for the purpose of
writing out new images, which may be critical for operators with hardware
which requires logical blocks which are 4096 bytes long.

Change-Id: I5c16a042eacfbb94a905b93a0eb9fbc73de0a890
This commit is contained in:
Julia Kreger 2024-08-29 17:17:20 -07:00
parent e89f59393a
commit 2df6d9b914
9 changed files with 109 additions and 6 deletions

View File

@ -38,10 +38,16 @@ def image_delete(filename):
os.remove(filename)
def loopdev_attach(filename):
def loopdev_attach(filename, block_size):
if str(block_size) not in ['512', '4096']:
logger.warning("Block device size is set to %s, only 512 and "
"4096 has been tested.", block_size)
logger.info("loopdev attach")
logger.debug("Calling [sudo losetup --show -f %s]", filename)
block_device = exec_sudo(["losetup", "--show", "-f", filename])
log_msg = ("Calling [sudo losetup --sector-size %s --show -f %s]"
% (str(block_size), filename))
logger.debug(log_msg)
block_device = exec_sudo(["losetup", "--sector-size", str(block_size),
"--show", "-f", filename])
# [:-1]: Cut of the newline
block_device = block_device[:-1]
logger.info("New block device [%s]", block_device)
@ -85,6 +91,12 @@ class LocalLoopNode(NodeBase):
self.image_dir = config['directory']
else:
self.image_dir = default_config['image-dir']
if 'DIB_BLOCK_SIZE' in os.environ:
self.block_size = os.environ['DIB_BLOCK_SIZE']
elif 'block_size' in config:
self.block_size = config['block_size']
else:
self.block_size = 512
self.filename = os.path.join(self.image_dir, self.name + ".raw")
def get_edges(self):
@ -98,7 +110,7 @@ class LocalLoopNode(NodeBase):
self.add_rollback(image_delete, self.filename)
image_create(self.filename, self.size)
block_device = loopdev_attach(self.filename)
block_device = loopdev_attach(self.filename, self.block_size)
self.add_rollback(loopdev_detach, block_device)
if 'blockdev' not in self.state:

View File

@ -95,6 +95,8 @@ class Partitioning(PluginBase):
def _create_mbr(self):
"""Create partitions with MBR"""
# NOTE(TheJulia): This is funcitonally incompatible with block/sector
# sizing other than 512 bytes.
with MBR(self.image_path, self.disk_size, self.align) as part_impl:
for part_cfg in self.partitions:
part_name = part_cfg.get_name()
@ -127,7 +129,10 @@ class Partitioning(PluginBase):
def _create_gpt(self):
"""Create partitions with GPT"""
cmd = ['sgdisk', self.image_path]
# Use the loopback via device_path, as using the file means the
# partitioning is exposed to sector sizing of the OS, not of the
# underlying "device" provided by the loopback.
cmd = ['sgdisk', self.device_path]
# This padding gives us a little room for rounding so we don't
# go over the end of the disk

View File

@ -63,7 +63,7 @@ class TestGPT(tc.TestGraphGeneration):
node.create()
# check the parted call looks right
parted_cmd = ['sgdisk', self.image_path,
parted_cmd = ['sgdisk', '/dev/loopX',
'-n', '1:0:+8M', '-t', '1:EF00', '-c', '1:ESP',
'-n', '2:0:+8M', '-t', '2:EF02', '-c', '2:BSP',
'-n', '3:0:+1006M', '-t', '3:8300', '-c', '3:Root Part']

View File

@ -0,0 +1,19 @@
================
Block Device EFI
================
This provides a block-device configuration for the ``vm`` element to
get a single-partition disk suitable for EFI booting on a block device
which uses a native 4KiB sector size. This is important because GPT
partitioning relies on sector boundry placement and the GPT disk partition
table always starts on the second sector of the disk.
Note on x86 this provides the extra `BIOS boot partition
<https://en.wikipedia.org/wiki/BIOS_boot_partition>`__ and a EFI boot
partition for maximum compatability.
This element requires ``mkfs.vfat`` command to be available on the build
system, usually included in the dosfstools OS package.
Furthermore, the sector size created by this element will not be compatible
with devices using 512 byte sectors.

View File

@ -0,0 +1,30 @@
- local_loop:
name: image0
block_size: 4096
- partitioning:
base: image0
label: gpt
partitions:
- name: ESP
type: 'EF00'
size: 500MiB
mkfs:
type: vfat
mount:
mount_point: /boot/efi
fstab:
options: "defaults"
fsck-passno: 2
- name: BSP
type: 'EF02'
size: 8MiB
- name: root
type: '8300'
size: 100%
mkfs:
type: ext4
mount:
mount_point: /
fstab:
options: "defaults"
fsck-passno: 1

View File

@ -0,0 +1 @@
block-device

View File

@ -0,0 +1,10 @@
#
# Arch gate
#
if [[ "ppc64 ppc64le ppc64el" =~ "$ARCH" ]]; then
echo "block-device-efi is not supported on Power; use block-device-gpt or block-device-mbr"
exit 1
fi
export DIB_BLOCK_DEVICE=efi

View File

@ -216,6 +216,22 @@ size
directory
(optional) The directory where the image is created.
block_size
(optional) Defaults to 512 bytes. Usable to set a different logical block
size, or in loopback context sector size, which will govern how partitioning
and filesystem utilities will interact with the device and ultimately the
block layout on disk.
Examples: 512, 4096.
This option is critical if you have block devices which natively operate
with 4KiB block sizes and need to craft an image to use boot from those
devices using a GPT partition table. This setting can also be asserted
using a DIB_BLOCK_SIZE environment variable which may be useful for
users who need to craft similar, but different block size images without
the need to separately maintain different block device YAML documents.
Please keep in mind, with larger block sizes, total disk image sizes
*and* partition sizes on the disk image will need to be perfectly
divisible by the block size being asserted.
Example:
.. code-block:: yaml

View File

@ -0,0 +1,10 @@
---
features:
- |
Adds the ability for diskimage-builder to create images with different
block sizes. By default, this remains at the default of 512 bytes,
but some newer devices require 4096 bytes to be used, which impacts
the overall layout rendering 512 byte images incompatible. This setting
can also be asserted and overridden using the ``DIB_BLOCK_SIZE``
environment variable, but alternatively exists as a new ``block_size``
parameter for ``local_loop`` section in block device YAML documents.