arch-image-builder/builder/disk/layout/gpt/uefi.py

119 lines
2.9 KiB
Python
Raw Normal View History

import ctypes
from binascii import crc32
from logging import getLogger
from builder.lib.serializable import Serializable, SerializableDict
from uuid import UUID, uuid4
log = getLogger(__name__)
class EfiTableHeader(ctypes.Structure, SerializableDict):
_fields_ = [
("signature", ctypes.c_uint64),
("revision", ctypes.c_uint32),
("header_size", ctypes.c_uint32),
("crc32", ctypes.c_uint32),
("reserved", ctypes.c_uint32),
]
def set_signature(self, value: str | int | bytes):
vt = type(value)
if vt is str: r = int.from_bytes(value.encode(), "little")
elif vt is bytes: r = int.from_bytes(value, "little")
elif vt is int: r = value
else: raise TypeError("bad value type")
self.signature = r
def get_signature(self) -> bytes:
return ctypes.string_at(ctypes.byref(self), 8)
def get_revision(self) -> tuple[int, int]:
return (
self.revision >> 0x10 & 0xFFFF,
self.revision & 0xFFFF,
)
def calc_crc32(self, data: bytes = None) -> int:
orig = self.crc32
self.crc32 = 0
if data is None: data = ctypes.string_at(
ctypes.byref(self), self.header_size
)
value = crc32(data, 0)
self.crc32 = orig
return value
def update_crc32(self, data: bytes = None):
self.crc32 = self.calc_crc32(data)
def check_signature(self, value: str | int | bytes) -> bool:
vt = type(value)
if vt is int: return self.signature == value
b = self.get_signature()
if vt is bytes: return b == value
if vt is str: return b == value.encode()
raise TypeError("bad value type")
def check_revision(self, major: int, minor: int) -> bool:
rev = self.get_revision()
return rev[0] == major and rev[1] == minor
def check_crc32(self) -> bool:
return self.calc_crc32() == self.crc32
def to_dict(self) -> dict:
return {
"signature": self.get_signature().decode(),
"revision": ".".join(map(str, self.get_revision())),
"header_size": self.header_size,
"crc32": self.crc32,
}
class EfiGUID(ctypes.Structure, Serializable):
_fields_ = [
("d1", ctypes.c_uint32),
("d2", ctypes.c_uint16),
("d3", ctypes.c_uint16),
("d4", ctypes.c_uint8 * 8),
]
def to_uuid(self) -> UUID:
u = bytes()
u += int.to_bytes(self.d1, 4)
u += int.to_bytes(self.d2, 2)
u += int.to_bytes(self.d3, 2)
u += bytes().join(int.to_bytes(i) for i in self.d4)
return UUID(bytes=u)
def set_uuid(self, u: UUID):
u = u.bytes
self.d1 = int.from_bytes(u[0:4])
self.d2 = int.from_bytes(u[4:6])
self.d3 = int.from_bytes(u[6:8])
for i in range(8):
self.d4[i] = int.from_bytes(u[i+8:i+9])
@staticmethod
def from_uuid(u: UUID):
if u is None: return None
g = EfiGUID()
g.set_uuid(u)
return g
@staticmethod
def generate():
return EfiGUID.from_uuid(uuid4())
def serialize(self) -> str:
return str(self.to_uuid())
def unserialize(self, o: str):
self.from_uuid(UUID(o))
def __str__(self) -> str:
return self.serialize()
assert(ctypes.sizeof(EfiTableHeader()) == 24)
assert(ctypes.sizeof(EfiGUID()) == 16)