Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions src/gpt.js
Original file line number Diff line number Diff line change
Expand Up @@ -274,8 +274,17 @@ export class GPT {
if (partEntry.type === TYPE_EFI_UNUSED) continue;
const partSlot = partEntry.name.slice(-2);
if (partSlot !== "_a" && partSlot !== "_b") continue;
const bootable = partEntry.name === `boot${partSlot}`;
partEntry.attributes = updateABFlags(partEntry.attributes, partSlot === `_${slot}`, bootable, !bootable);
const isTargetSlot = partSlot === `_${slot}`;
const isBootPartition = partEntry.name === `boot${partSlot}`;
// Active slot: bootable, successful, not unbootable, 7 tries
// Inactive slot: not active, not successful, unbootable, 0 tries
partEntry.attributes = updateABFlags(
partEntry.attributes,
isTargetSlot, // active
isTargetSlot && isBootPartition, // successful (only boot partition)
!isTargetSlot, // unbootable (only inactive slot)
isTargetSlot && isBootPartition ? 7 : 0 // tries remaining
);
}
}
}
Expand Down Expand Up @@ -307,7 +316,7 @@ function parseABFlags(attributes) {
function updateABFlags(attributes, active, successful, unbootable, triesRemaining = 0) {
let ret = attributes;

ret &= ~(AB_PARTITION_ATTR_SLOT_ACTIVE | AB_PARTITION_ATTR_BOOT_SUCCESSFUL | AB_PARTITION_ATTR_UNBOOTABLE | AB_PARTITION_ATTR_TRIES_MASK) << AB_FLAG_OFFSET;
ret &= ~((AB_PARTITION_ATTR_SLOT_ACTIVE | AB_PARTITION_ATTR_BOOT_SUCCESSFUL | AB_PARTITION_ATTR_UNBOOTABLE | AB_PARTITION_ATTR_TRIES_MASK) << AB_FLAG_OFFSET);

if (active) ret |= AB_PARTITION_ATTR_SLOT_ACTIVE << AB_FLAG_OFFSET;
if (successful) ret |= AB_PARTITION_ATTR_BOOT_SUCCESSFUL << AB_FLAG_OFFSET;
Expand Down
32 changes: 26 additions & 6 deletions src/qdl.js
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ export class qdlDevice {
const chunkSectors = Math.min(Number(range.end - sector + 1n), maxSectors);
const result = await this.firehose.cmdErase(lun, sector, chunkSectors);
if (!result) {
logger.error(`Failed to erase sectors chunk ${sectors}-${sectors + BigInt(chunkSectors - 1)}`);
logger.error(`Failed to erase sectors chunk ${sector}-${sector + BigInt(chunkSectors - 1)}`);
return false;
}
sector = sector + BigInt(chunkSectors);
Expand Down Expand Up @@ -298,7 +298,9 @@ export class qdlDevice {
const [found, lun, partition] = await this.detectPartition(name);
if (!found) throw new Error(`Partition ${name} not found`);
logger.info(`Erasing ${name}...`);
await this.firehose.cmdErase(lun, partition.start, partition.sectors);
if (!await this.firehose.cmdErase(lun, partition.start, partition.sectors)) {
throw new Error(`Failed to erase ${name}`);
}
logger.debug(`Erased ${name} ${partition.start}-${partition.end} (${partition.sectors} sectors)`);
return true;
}
Expand Down Expand Up @@ -360,18 +362,26 @@ export class qdlDevice {
primaryGpt.setActiveSlot(slot);

const primaryPartEntries = primaryGpt.buildPartEntries();
await this.firehose.cmdProgram(lun, primaryGpt.partEntriesStartLba, new Blob([primaryPartEntries]));
if (!await this.firehose.cmdProgram(lun, primaryGpt.partEntriesStartLba, new Blob([primaryPartEntries]))) {
throw new Error(`Failed to write primary partition entries for LUN ${lun}`);
}
const primaryHeader = primaryGpt.buildHeader(primaryPartEntries);
await this.firehose.cmdProgram(lun, primaryGpt.currentLba, new Blob([primaryHeader]));
if (!await this.firehose.cmdProgram(lun, primaryGpt.currentLba, new Blob([primaryHeader]))) {
throw new Error(`Failed to write primary GPT header for LUN ${lun}`);
}

// Update backup GPT
const backupGpt = await this.getGpt(lun, primaryGpt.alternateLba);
backupGpt.setActiveSlot(slot);

const backupPartEntries = backupGpt.buildPartEntries();
await this.firehose.cmdProgram(lun, backupGpt.partEntriesStartLba, new Blob([backupPartEntries]));
if (!await this.firehose.cmdProgram(lun, backupGpt.partEntriesStartLba, new Blob([backupPartEntries]))) {
throw new Error(`Failed to write backup partition entries for LUN ${lun}`);
}
const backupHeader = backupGpt.buildHeader(backupPartEntries);
await this.firehose.cmdProgram(lun, backupGpt.currentLba, new Blob([backupHeader]));
if (!await this.firehose.cmdProgram(lun, backupGpt.currentLba, new Blob([backupHeader]))) {
throw new Error(`Failed to write backup GPT header for LUN ${lun}`);
}
}

const activeBootLunId = (slot === "a") ? 1 : 2;
Expand All @@ -380,6 +390,16 @@ export class qdlDevice {
return true;
}

/**
* Set the bootable storage drive LUN (1 for slot A, 2 for slot B)
* @param {number} lun
* @returns {Promise<void>}
*/
async setBootableLun(lun) {
await this.firehose.cmdSetBootLunId(lun);
logger.info(`Successfully set bootable LUN to ${lun}`);
}

async reset() {
await this.firehose.cmdReset();
return true;
Expand Down