pmbootstrap-meow/pmb/install/partition.py
Caleb Connolly e547bb7f9c
helpers: mount: drop args (MR 2252)
Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
2024-06-23 12:38:38 +02:00

199 lines
6.9 KiB
Python

# Copyright 2023 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later
from pathlib import Path
from typing import Optional
from pmb.helpers import logging
import os
import time
import pmb.chroot
import pmb.config
from pmb.core.types import PmbArgs
import pmb.install.losetup
from pmb.core import Chroot
# FIXME (#2324): this function drops disk to a string because it's easier
# to manipulate, this is probably bad.
def partitions_mount(args: PmbArgs, layout, disk: Optional[Path]):
"""
Mount blockdevices of partitions inside native chroot
:param layout: partition layout from get_partition_layout()
:param disk: path to disk block device (e.g. /dev/mmcblk0) or None
"""
if not disk:
img_path = Path("/home/pmos/rootfs") / f"{args.device}.img"
disk = pmb.install.losetup.device_by_back_file(args, img_path)
logging.info(f"Mounting partitions of {disk} inside the chroot")
tries = 20
# Devices ending with a number have a "p" before the partition number,
# /dev/sda1 has no "p", but /dev/mmcblk0p1 has. See add_partition() in
# block/partitions/core.c of linux.git.
partition_prefix = str(disk)
if str.isdigit(disk.name[-1:]):
partition_prefix = f"{disk}p"
found = False
for i in range(tries):
if os.path.exists(f"{partition_prefix}1"):
found = True
break
logging.debug(f"NOTE: ({i + 1}/{tries}) failed to find the install "
"partition. Retrying...")
time.sleep(0.1)
if not found:
raise RuntimeError(f"Unable to find the first partition of {disk}, "
f"expected it to be at {partition_prefix}1!")
partitions = [layout["boot"], layout["root"]]
if layout["kernel"]:
partitions += [layout["kernel"]]
for i in partitions:
source = Path(f"{partition_prefix}{i}")
target = Chroot.native() / "dev" / f"installp{i}"
pmb.helpers.mount.bind_file(source, target)
def partition(args: PmbArgs, layout, size_boot, size_reserve):
"""
Partition /dev/install and create /dev/install{p1,p2,p3}:
* /dev/installp1: boot
* /dev/installp2: root (or reserved space)
* /dev/installp3: (root, if reserved space > 0)
When adjusting this function, make sure to also adjust
ondev-prepare-internal-storage.sh in postmarketos-ondev.git!
:param layout: partition layout from get_partition_layout()
:param size_boot: size of the boot partition in MiB
:param size_reserve: empty partition between root and boot in MiB (pma#463)
"""
# Convert to MB and print info
mb_boot = f"{round(size_boot)}M"
mb_reserved = f"{round(size_reserve)}M"
mb_root_start = f"{round(size_boot) + round(size_reserve)}M"
logging.info(f"(native) partition /dev/install (boot: {mb_boot},"
f" reserved: {mb_reserved}, root: the rest)")
filesystem = args.deviceinfo["boot_filesystem"] or "ext2"
# Actual partitioning with 'parted'. Using check=False, because parted
# sometimes "fails to inform the kernel". In case it really failed with
# partitioning, the follow-up mounting/formatting will not work, so it
# will stop there (see #463).
boot_part_start = args.deviceinfo["boot_part_start"] or "2048"
partition_type = args.deviceinfo["partition_type"] or "msdos"
commands = [
["mktable", partition_type],
["mkpart", "primary", filesystem, boot_part_start + 's', mb_boot],
]
if size_reserve:
mb_reserved_end = f"{round(size_reserve + size_boot)}M"
commands += [["mkpart", "primary", mb_boot, mb_reserved_end]]
commands += [
["mkpart", "primary", mb_root_start, "100%"],
["set", str(layout["boot"]), "boot", "on"]
]
# Not strictly necessary if the device doesn't use EFI boot, but marking
# it as an ESP will cover all situations where the device does use EFI
# boot. Marking it as ESP is helpful for EFI fw when it's looking for EFI
# system partitions. It's assumed that setting this bit is unlikely to
# cause problems for other situations, like when using Legacy BIOS boot
# or u-boot.
if partition_type.lower() == "gpt":
commands += [["set", str(layout["boot"]), "esp", "on"]]
for command in commands:
pmb.chroot.root(args, ["parted", "-s", "/dev/install"] +
command, check=False)
def partition_cgpt(args: PmbArgs, layout, size_boot, size_reserve):
"""
This function does similar functionality to partition(), but this
one is for ChromeOS devices which use special GPT.
:param layout: partition layout from get_partition_layout()
:param size_boot: size of the boot partition in MiB
:param size_reserve: empty partition between root and boot in MiB (pma#463)
"""
pmb.chroot.apk.install(args, ["cgpt"], build=False)
cgpt = {
'kpart_start': args.deviceinfo["cgpt_kpart_start"],
'kpart_size': args.deviceinfo["cgpt_kpart_size"],
}
# Convert to MB and print info
mb_boot = f"{round(size_boot)}M"
mb_reserved = f"{round(size_reserve)}M"
logging.info(f"(native) partition /dev/install (boot: {mb_boot},"
f" reserved: {mb_reserved}, root: the rest)")
boot_part_start = str(int(cgpt['kpart_start']) + int(cgpt['kpart_size']))
# Convert to sectors
s_boot = str(int(size_boot * 1024 * 1024 / 512))
s_root_start = str(int(
int(boot_part_start) + int(s_boot) + size_reserve * 1024 * 1024 / 512
))
commands = [
["parted", "-s", "/dev/install", "mktable", "gpt"],
["cgpt", "create", "/dev/install"],
[
"cgpt", "add",
"-i", str(layout["kernel"]),
"-t", "kernel",
"-b", cgpt['kpart_start'],
"-s", cgpt['kpart_size'],
"-l", "pmOS_kernel",
"-S", "1", # Successful flag
"-T", "5", # Tries flag
"-P", "10", # Priority flag
"/dev/install"
],
[
"cgpt", "add",
# pmOS_boot is second partition, the first will be ChromeOS kernel
# partition
"-i", str(layout["boot"]), # Partition number
"-t", "efi", # Mark this partition as bootable for u-boot
"-b", boot_part_start,
"-s", s_boot,
"-l", "pmOS_boot",
"/dev/install"
],
]
dev_size = pmb.chroot.root(
args, ["blockdev", "--getsz", "/dev/install"], output_return=True)
# 33: Sec GPT table (32) + Sec GPT header (1)
root_size = str(int(dev_size) - int(s_root_start) - 33)
commands += [
[
"cgpt", "add",
"-i", str(layout["root"]),
"-t", "data",
"-b", s_root_start,
"-s", root_size,
"-l", "pmOS_root",
"/dev/install"
],
["partx", "-a", "/dev/install"]
]
for command in commands:
pmb.chroot.root(args, command, check=False)