mirror of
https://gitlab.postmarketos.org/postmarketOS/pmbootstrap.git
synced 2025-07-12 19:09:56 +03:00
When using an msdos partition table and parted is called to enable the esp flag on the boot partition, it will disable a previously existing lba flag on that partition. However, some devices (at least the Raspberry Pi 3) will not boot without that flag. Only set that flag if the partition type is gpt. Part-of: https://gitlab.postmarketos.org/postmarketOS/pmbootstrap/-/merge_requests/2632
229 lines
7.5 KiB
Python
229 lines
7.5 KiB
Python
# Copyright 2023 Oliver Smith
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
from pathlib import Path
|
|
from pmb.helpers import logging
|
|
import os
|
|
import time
|
|
import pmb.chroot
|
|
import pmb.config
|
|
import pmb.install.losetup
|
|
from pmb.core import Chroot
|
|
from pmb.types import PartitionLayout
|
|
import pmb.core.dps
|
|
|
|
|
|
# FIXME (#2324): this function drops disk to a string because it's easier
|
|
# to manipulate, this is probably bad.
|
|
def partitions_mount(device: str, layout: PartitionLayout, disk: Path | None) -> None:
|
|
"""
|
|
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"{device}.img"
|
|
disk = pmb.install.losetup.device_by_back_file(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(layout: PartitionLayout, size_boot: int, size_reserve: int) -> None:
|
|
"""
|
|
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"{size_boot}M"
|
|
mb_reserved = f"{size_reserve}M"
|
|
mb_root_start = f"{size_boot + size_reserve}M"
|
|
logging.info(
|
|
f"(native) partition /dev/install (boot: {mb_boot},"
|
|
f" reserved: {mb_reserved}, root: the rest)"
|
|
)
|
|
|
|
filesystem = pmb.parse.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 = pmb.parse.deviceinfo().boot_part_start or "2048"
|
|
|
|
partition_type = pmb.parse.deviceinfo().partition_type or "gpt"
|
|
|
|
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]]
|
|
|
|
arch = str(pmb.parse.deviceinfo().arch)
|
|
|
|
commands += [["mkpart", "primary", mb_root_start, "100%"]]
|
|
if partition_type.lower() == "gpt":
|
|
commands += [
|
|
["type", str(layout["root"]), pmb.core.dps.root[arch][1]],
|
|
# esp is an alias for boot on GPT
|
|
["set", str(layout["boot"]), "esp", "on"],
|
|
["type", str(layout["boot"]), pmb.core.dps.boot["esp"][1]],
|
|
]
|
|
# Some devices still use MBR and will not work with only esp set
|
|
elif partition_type.lower() == "msdos":
|
|
commands += [["set", str(layout["boot"]), "boot", "on"]]
|
|
|
|
for command in commands:
|
|
pmb.chroot.root(["parted", "-s", "/dev/install", *command], check=False)
|
|
|
|
|
|
def partition_cgpt(layout: PartitionLayout, size_boot: int, size_reserve: int) -> None:
|
|
"""
|
|
This function does similar functionality to partition(), but this
|
|
one is for ChromeOS devices which use special GPT. We don't follow
|
|
the Discoverable Partitions Specification here for that exact reason.
|
|
|
|
: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(["cgpt"], Chroot.native(), build=False)
|
|
|
|
deviceinfo = pmb.parse.deviceinfo()
|
|
|
|
if deviceinfo.cgpt_kpart_start is None or deviceinfo.cgpt_kpart_size is None:
|
|
raise RuntimeError("cgpt_kpart_start or cgpt_kpart_size not found in deviceinfo")
|
|
|
|
cgpt = {
|
|
# or 0 shouldn't be needed since we None check just above, but mypy isn't that smart
|
|
# so we add it to make it happy
|
|
"kpart_start": pmb.parse.deviceinfo().cgpt_kpart_start or "0",
|
|
"kpart_size": pmb.parse.deviceinfo().cgpt_kpart_size or "0",
|
|
}
|
|
|
|
# Convert to MB and print info
|
|
mb_boot = f"{size_boot}M"
|
|
mb_reserved = f"{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(["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(command, check=False)
|