import argparse
import atexit
+import errno
import logging
import os
import re
File = ""
class Partition:
- def __init__(self, name, size, start=None, ptype="0FC63DAF-8483-4772-8E79-3D69D8477DE4", fstype="raw"):
+ def __init__(self, name, size, start=None, ptype=None, fstype="raw", bootable=False):
self.name = name
self.size = size
self.start = start
self.ptype = ptype
+ self.bootable = bootable
def __str__(self):
- output = f"start={self.start}MiB, " if self.start else ""
- output += f"size={self.size}MiB"
- output += f", name={self.name}"
- output += f", type={self.ptype}"
- return output + "\n"
+ output = []
+ if self.start:
+ output.append(f"start={self.start}MiB")
+ if self.size:
+ output.append(f"size={self.size}MiB")
+ if self.name:
+ output.append(f"name={self.name}")
+ output.append(f"type={self.ptype}")
+ if self.bootable:
+ output.append("bootable")
+ return ", ".join(output) + "\n"
class Label:
def __init__(self, part_table, ltype):
self.ltype = ltype
+ if ltype == 'gpt':
+ ptype = "0FC63DAF-8483-4772-8E79-3D69D8477DE4"
+ elif ltype == 'dos':
+ ptype = '83'
self.part_table = []
for part in part_table:
- name = part["name"]
- size = part["size"]
- ptype = part.get("type", None)
- ftype = part["fstype"]
- start = part.get("start", None)
- self.part_table.append(Partition(**part)) #name, size, start, ptype, ftype))
+ part["ptype"] = part.get("ptype", ptype)
+ self.part_table.append(Partition(**part))
def __str__(self):
output = f"label: {self.ltype}\n"
for part in self.part_table:
output += str(part)
return output
-class Rpi3:
+class SdFusingTarget:
+ def __init__(self, device, ltype):
+ self.device = device
+ total_size = device_size(device)
+ self.user_size = total_size - self.reserved_space - \
+ reduce(lambda x, y: x + (y["size"] or 0), self.part_table, 0)
+ if self.user_size < 100:
+ logging.error("Not enough space for user data. Use larger storage.")
+ raise OSError(errno.ENOSPC, os.strerror(errno.ENOSPC), device)
+ self.part_table[self.user_partition]["size"] = self.user_size
+ self.label = Label(self.part_table, ltype)
+
+ def get_partition_index(self, binary):
+ return self.binaries.get(binary, None)
+
+class Rpi3(SdFusingTarget):
long_name = "Raspberry Pi 3"
- pass
+ part_table = [
+ {"size": 64, "fstype": "vfat", "name": "boot", "start": 4, "ptype": "0xe", "bootable": True},
+ {"size": 3072, "fstype": "ext4", "name": "rootfs"},
+ {"size": 1344, "fstype": "ext4", "name": "system-data"},
+ {"size": None, "ptype": "5", "name": "extended", "start": 4484},
+ {"size": None, "fstype": "ext4", "name": "user"},
+ {"size": 32, "fstype": "ext4", "name": "modules"},
+ {"size": 32, "fstype": "ext4", "name": "ramdisk"},
+ {"size": 32, "fstype": "ext4", "name": "ramdisk-recovery"},
+ {"size": 8, "fstype": "raw", "name": "inform"},
+ {"size": 256, "fstype": "ext4", "name": "hal"},
+ {"size": 125, "fstype": "raw", "name": "reserved2"},
+ ]
+ binaries = {
+ "boot.img": 1,
+ "rootfs.img": 2,
+ "system-data.img": 3,
+ "user.img": 5,
+ "modules.img": 6,
+ "ramdisk.img": 7,
+ "ramdisk-recovery.img": 8,
+ "hal.img": 10,
+ }
-class Rpi4:
+ def __init__(self, device, args):
+ self.reserved_space = 12
+ self.user_partition = 4
+ super().__init__(device, "dos")
+
+class Rpi4(SdFusingTarget):
long_name = "Raspberry Pi 4"
- pass
+ part_table = [
+ {"size": 64, "fstype": "vfat", "name": "boot_a", "start": 4,
+ "ptype": "C12A7328-F81F-11D2-BA4B-00A0C93EC93B"},
+ {"size": 3072, "fstype": "ext4", "name": "rootfs_a"},
+ {"size": 1344, "fstype": "ext4", "name": "system-data"},
+ {"size": 36, "fstype": "raw", "name": "none"},
+ {"size": None, "fstype": "ext4", "name": "user"},
+ {"size": 32, "fstype": "ext4", "name": "module_a"},
+ {"size": 32, "fstype": "ext4", "name": "ramdisk_a"},
+ {"size": 32, "fstype": "ext4", "name": "ramdisk-recovery_a"},
+ {"size": 8, "fstype": "ext4", "name": "inform"},
+ {"size": 256, "fstype": "ext4", "name": "hal_a"},
+ {"size": 64, "fstype": "vfat", "name": "boot_b",
+ "ptype": "C12A7328-F81F-11D2-BA4B-00A0C93EC93B"},
+ {"size": 3072, "fstype": "ext4", "name": "rootfs_b"},
+ {"size": 32, "fstype": "ext4", "name": "module_b"},
+ {"size": 32, "fstype": "ext4", "name": "ramdisk_b"},
+ {"size": 32, "fstype": "ext4", "name": "ramdisk-recovery_b"},
+ {"size": 256, "fstype": "ext4", "name": "hal_b"},
+ {"size": 4, "fstype": "ext4", "name": "param"},
+ {"size": 64, "fstype": "ext4", "name": "reserved1"},
+ {"size": 125, "fstype": "ext4", "name": "reserved2"},
+ ]
+ binaries = {
+ "boot.img": 1,
+ "rootfs.img": 2,
+ "system-data.img": 3,
+ "user.img": 5,
+ "modules.img": 6,
+ "ramdisk.img": 7,
+ "ramdisk-recovery.img": 8,
+ "hal.img": 10,
+ }
+ binaries_b = {
+ "boot.img": 11,
+ "rootfs.img": 12,
+ "modules.img": 13,
+ "ramdisk.img": 14,
+ "ramdisk-recovery.img": 15,
+ "hal.img": 16,
+ }
-class RV64:
+ def __init__(self, device, args):
+ self.reserved_space = 5
+ self.user_partition = 4
+ self.update = args.update
+ super().__init__(device, "gpt")
+
+ def get_partition_index(self, binary):
+ logging.debug(f"RPi4 get_partition_index: {binary} {repr(self.update)}")
+ if self.update == 'b':
+ self.binaries_b.get(binary, None)
+ return self.binaries.get(binary, None)
+
+class RV64(SdFusingTarget):
long_name = "QEMU RISC-V 64-bit"
part_table = [
{"size": 2, "fstype": "raw", "name": "SPL", "start": 4,
"ramdisk-recovery.img": 10,
"hal.img": 12,
}
- def __init__(self, device):
- self.device = device
- total_size = device_size(device)
- user_size = total_size - reduce(lambda x, y: x + (y["size"] or 0), self.part_table, 0)
- self.part_table[6]["size"] = user_size
- self.label = Label(self.part_table, "gpt")
- def get_partition_index(self, binary):
- return self.binaries.get(binary, None)
+ def __init__(self, device, args):
+ self.user_partition = 6
+ self.reserved_space = 5
+ super().__init__(device, 'gpt')
class VF2:
long_name = "VisionFive2"
if not new:
sys.exit(1)
- target = TARGETS[args.target](Device)
+ target = TARGETS[args.target](Device, args)
#TODO: unmount target devices
if support_delete:
if args.binaries is None or len(args.binaries) == 0:
return
- target = TARGETS[args.target](Device)
+ target = TARGETS[args.target](Device, args)
for b in args.binaries:
if re.search('\.(tar|tar\.gz|tgz)$', b):
do_fuse_image_tarball(b, target)
parser.add_argument("--log-level", dest="log_level", default="warning",
help="Verbosity, possible values: debug, info, warning, "
"error, critical (default: warning)")
- parser.add_argument("-s", "--size", type=int, default=8192,
- help="size of the backing file to create")
+ parser.add_argument("--size", type=int, default=8192,
+ help="size of the backing file to create (in MiB)")
parser.add_argument("-t", "--target", required=True,
help="Target device model. Use `--target list`"
" to show supported devices.")
+ parser.add_argument("--update", choices=['a', 'b'], default=None,
+ help="Choose partition set to update: A or B.")
parser.add_argument("--version", action="version",
version=f"%(prog)s {__version__}")
args = parser.parse_args()