20 Commits
0.1.2 ... 0.2.2

Author SHA1 Message Date
Caleb Connolly
39a6e6daaf commit disk before free
When auto-freeing a disk, be sure to commit pending changes first.

fixes setActiveBootslot() since the GPT refactor

Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
2025-03-24 23:31:41 +01:00
Caleb Connolly
e1d7c370bc properly error if run on a non-A/B device
Detect the case where there are no A/B partitions and print an error
about it rather than erroneously continuing execution.

Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
2025-03-18 14:10:52 +00:00
Arnaud Ferraris
413ee35eda bootctrl: fall back to active slot if current slot not found in cmdline
While postmarketOS makes use of a kernel cmdline argument, this isn't
the case for all distros. Furthermore, this argument isn't present in
those added automatically by the Android bootloader.

In order to still be able to use `qbootctl` on other distros, this
change ensures we use the currently active slot as the current slot when
the cmdline argument is missing. This ensure `qbootctl -m` picks up the
correct slot more often than not, instead of always defaulting to `_a`.
2025-03-18 13:40:57 +00:00
Guido Günther
d30d9dfc03 gpt-utils: Open block device read only when obtaining info
Otherwise reading information like `qbootctl -a` triggers removal and
readdition of all partitions like:

```
KERNEL[73264.679695] remove   /devices/platform/soc@0/1d84000.ufshc/host0/target0:0:0/0:0:0:4/block/sde/sde11 (block)
KERNEL[73264.748249] add      /devices/platform/soc@0/1d84000.ufshc/host0/target0:0:0/0:0:0:4/block/sde/sde11 (block)
KERNEL[73264.863286] remove   /devices/platform/soc@0/1d84000.ufshc/host0/target0:0:0/0:0:0:4/block/sde/sde11 (block)
KERNEL[73264.917664] add      /devices/platform/soc@0/1d84000.ufshc/host0/target0:0:0/0:0:0:4/block/sde/sde11 (block)
UDEV  [73264.980859] remove   /devices/platform/soc@0/1d84000.ufshc/host0/target0:0:0/0:0:0:4/block/sde/sde11 (block)
UDEV  [73265.150742] add      /devices/platform/soc@0/1d84000.ufshc/host0/target0:0:0/0:0:0:4/block/sde/sde11 (block)
UDEV  [73265.276134] remove   /devices/platform/soc@0/1d84000.ufshc/host0/target0:0:0/0:0:0:4/block/sde/sde11 (block)
UDEV  [73265.492442] add      /devices/platform/soc@0/1d84000.ufshc/host0/target0:0:0/0:0:0:4/block/sde/sde11 (block)
…[the same for all other partitions on sde]…
```

leading to wasted CPU cycles (and battery) but also making programms
that run right after that command and accessing one of the partitions
fail sporadically. This can be bad if those commands are e.g. doing a

```
cat /dev/disk/by-partlabel/boot_a
```

to backup the current boot partition.
2025-02-11 23:41:55 +00:00
Caleb Connolly
b8d2248914 add a flag to continue when ufs bsg device is missing
For some usecases it is desired to still update the GPT headers even if
the UFS BSG device is missing. Add a new flag which implements this
behaviour.

It isn't made the default because on some platforms this could result in
an unbootable device, there it's preferable to defer to the user by just
bailing out if the UFS BSG device is unavailable.

Signed-off-by: Caleb Connolly <caleb@connolly.tech>
2023-09-27 18:56:43 +01:00
Caleb Connolly
3390f642e3 meson.build: use gnu11 std instead of c11
This fixes a few compilation issues on different platforms (yay C
standards)

Signed-off-by: Caleb Connolly <caleb@connolly.tech>
2023-09-27 18:43:46 +01:00
Caleb Connolly
74fcde7a8e header fixes 2023-06-26 14:44:24 +01:00
Caleb Connolly
ea34c3f0b9 drop test stub, fix licenses 2023-06-24 01:38:14 +01:00
Caleb Connolly
e9e86efb96 even even more rework, drop zlib dep
Also fix the mark_boot_successful feature which I broke earlier
2023-06-24 01:04:15 +01:00
Caleb Connolly
bca9aa5dd7 more rework, cleanup, port to pure C
The only C++ was for handling discovering and iterating over the
partitions PER block device, this was implemented in a really
overcomplicated way.

Simplify things, port everything over to c11 and drop the libstdc++
requirement entirely.
2023-06-24 00:06:02 +01:00
Caleb Connolly
04f4ac81ea rework, fixup, cleanup
Allocate gpt_disk on stack, allocate when needed, rather than multiple
times for every partition.

Huge code cleanup, rerun clang-format, etc

Many changes here inspired by Eric's earlier work.
2023-06-23 21:53:30 +01:00
Caleb Connolly
7f852512b6 write out the backup header too
Now that we actually have the real backup header, we can safely write it
out without having the wrong LBA pointers and other such issues that
would come from writing the primary header to the backup header (ask me
how I know)
2023-06-23 17:23:56 +01:00
Caleb Connolly
7a5d5ee40e make disk->hdr_bak actually be the GPT backup header
I do not know who thought this should be a "backup" copy of the primary
GPT header...
2023-06-23 17:22:39 +01:00
Caleb Connolly
75e30f8d09 Revert "Use scope-based memory management for malloc, free, open, close, etc."
This commit introduced a lot of changes and seems to have caused a few
issues, including breaking crc32 generation. Revert it for now with the
intention to reimplement some of the improvements.

This reverts commit 9d7600df51.
2023-06-23 13:30:08 +01:00
Eric Curtin
20572c1417 Fix segfault originating in get_suffix function
Some callers try to print the return value from here, printf %s NULL,
ends up being a segfault.
2023-05-29 01:27:55 +00:00
Eric Curtin
9d7600df51 Use scope-based memory management for malloc, free, open, close, etc.
If we are using C++, we might as well use scope-based memory management
for these things, cleaner and less error prone. Also removed
unnecessary gotos.
2023-05-29 01:26:29 +00:00
Eric Curtin
843aa92266 Change hdr_offset to signed value, so lseek64 return can be checked
The lines:

	if (instance == PRIMARY_GPT)
		hdr_offset = block_size;
	else {
		hdr_offset = lseek64(fd, 0, SEEK_END) - block_size;
	}
	if (hdr_offset < 0) {
		fprintf(stderr, "%s: Failed to get gpt header offset\n",
			__func__);
		goto error;
	}

are error checked. But previously hdr_offset could never be less than
zero because it was unsigned.
2023-05-15 20:28:54 +01:00
Caleb Connolly
e705c38010 add missing linux/limits.h
Needed for PATH_MAX
2023-05-15 20:26:28 +01:00
Caleb Connolly
1752506e75 gitignore: compile_commands.json 2023-05-15 20:25:59 +01:00
alefnode
df63d7c21c Fix to << uint32_t has not been declared >> 2023-05-04 09:15:29 +02:00
16 changed files with 1928 additions and 1337 deletions

View File

@@ -54,7 +54,7 @@ BreakConstructorInitializersBeforeComma: false
#BreakConstructorInitializers: BeforeComma # Unknown to clang-format-4.0
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: false
ColumnLimit: 80
ColumnLimit: 100
CommentPragmas: '^ IWYU pragma:'
#CompactNamespaces: false # Unknown to clang-format-4.0
ConstructorInitializerAllOnOneLineOrOnePerLine: false
@@ -89,16 +89,16 @@ ObjCSpaceBeforeProtocolList: true
# Taken from git's rules
#PenaltyBreakAssignment: 10 # Unknown to clang-format-4.0
PenaltyBreakBeforeFirstCallParameter: 30
PenaltyBreakBeforeFirstCallParameter: 50
PenaltyBreakComment: 10
PenaltyBreakFirstLessLess: 0
PenaltyBreakString: 10
PenaltyExcessCharacter: 100
PenaltyExcessCharacter: 30
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
ReflowComments: false
SortIncludes: false
SortIncludes: true
#SortUsingDeclarations: false # Unknown to clang-format-4.0
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
@@ -114,7 +114,7 @@ SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp03
Standard: Cpp11
TabWidth: 8
UseTab: Always
...

2
.gitignore vendored
View File

@@ -1,3 +1,5 @@
.vscode/
.push.settings.jsonc
build/
compile_commands.json

545
LICENSE

File diff suppressed because it is too large Load Diff

View File

@@ -34,6 +34,7 @@ qbootctl [-c|-m|-s|-u|-b|-n|-x] [SLOT]
-s SLOT set to active slot to SLOT
-m [SLOT] mark a boot as successful (default: current)
-u [SLOT] mark SLOT as unbootable (default: current)
-i still write the GPT headers even if the UFS bLun can't be changed (default: false)
```
## Debugging

View File

@@ -1,6 +1,20 @@
/*
* Copyright (C) 2016 The Android Open Source Project
* Copyright (C) 2022 Caleb Connolly <caleb@connolly.tech>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Copyright (C) 2023 Caleb Connolly <caleb@connolly.tech>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -19,85 +33,86 @@
#ifndef __BOOTCTRL_H__
#define __BOOTCTRL_H__
#include <stdbool.h>
struct slot_info {
bool active;
bool bootable;
bool successful;
bool successful;
};
struct boot_control_module {
/*
* (*getCurrentSlot)() returns the value letting the system know
* whether the current slot is A or B. The meaning of A and B is
* left up to the implementer. It is assumed that if the current slot
* is A, then the block devices underlying B can be accessed directly
* without any risk of corruption.
* The returned value is always guaranteed to be strictly less than the
* value returned by getNumberSlots. Slots start at 0 and
* finish at getNumberSlots() - 1
* Returns -ENOENT on devices with no slots.
*/
int (*getCurrentSlot)();
/*
* (*getCurrentSlot)() returns the value letting the system know
* whether the current slot is A or B. The meaning of A and B is
* left up to the implementer. It is assumed that if the current slot
* is A, then the block devices underlying B can be accessed directly
* without any risk of corruption.
* The returned value is always guaranteed to be strictly less than the
* value returned by getNumberSlots. Slots start at 0 and
* finish at getNumberSlots() - 1
*/
unsigned (*getCurrentSlot)();
/*
* (*markBootSuccessful)() marks the specified slot
* as boot successful
*
* Returns 0 on success, -errno on error.
*/
int (*markBootSuccessful)(unsigned slot);
/*
* (*markBootSuccessful)() marks the specified slot
* as boot successful
*
* Returns 0 on success, -errno on error.
*/
int (*markBootSuccessful)(unsigned slot);
/*
* (*setActiveBootSlot)() marks the slot passed in parameter as
* the active boot slot (see getCurrentSlot for an explanation
* of the "slot" parameter). This overrides any previous call to
* setSlotAsUnbootable.
* Returns 0 on success, -errno on error.
*/
int (*setActiveBootSlot)(unsigned slot, bool ignore_missing_bsg);
/*
* (*setActiveBootSlot)() marks the slot passed in parameter as
* the active boot slot (see getCurrentSlot for an explanation
* of the "slot" parameter). This overrides any previous call to
* setSlotAsUnbootable.
* Returns 0 on success, -errno on error.
*/
int (*setActiveBootSlot)(unsigned slot);
/*
* (*setSlotAsUnbootable)() marks the slot passed in parameter as
* an unbootable. This can be used while updating the contents of the slot's
* partitions, so that the system will not attempt to boot a known bad set up.
* Returns 0 on success, -errno on error.
*/
int (*setSlotAsUnbootable)(unsigned slot);
/*
* (*setSlotAsUnbootable)() marks the slot passed in parameter as
* an unbootable. This can be used while updating the contents of the slot's
* partitions, so that the system will not attempt to boot a known bad set up.
* Returns 0 on success, -errno on error.
*/
int (*setSlotAsUnbootable)(unsigned slot);
/*
* (*isSlotBootable)() returns if the slot passed in parameter is
* bootable. Note that slots can be made unbootable by both the
* bootloader and by the OS using setSlotAsUnbootable.
* Returns 1 if the slot is bootable, 0 if it's not, and -errno on
* error.
*/
int (*isSlotBootable)(unsigned slot);
/*
* (*isSlotBootable)() returns if the slot passed in parameter is
* bootable. Note that slots can be made unbootable by both the
* bootloader and by the OS using setSlotAsUnbootable.
* Returns 1 if the slot is bootable, 0 if it's not, and -errno on
* error.
*/
int (*isSlotBootable)(unsigned slot);
/*
* (*getSuffix)() returns the string suffix used by partitions that
* correspond to the slot number passed in parameter. The returned string
* is expected to be statically allocated and not need to be freed.
* Returns NULL if slot does not match an existing slot.
*/
const char *(*getSuffix)(unsigned slot);
/*
* (*getSuffix)() returns the string suffix used by partitions that
* correspond to the slot number passed in parameter. The returned string
* is expected to be statically allocated and not need to be freed.
* Returns NULL if slot does not match an existing slot.
*/
const char* (*getSuffix)(unsigned slot);
/*
* (*isSlotMarkedSucessful)() returns if the slot passed in parameter has
* been marked as successful using markBootSuccessful.
* Returns 1 if the slot has been marked as successful, 0 if it's
* not the case, and -errno on error.
*/
int (*isSlotMarkedSuccessful)(unsigned slot);
/*
* (*isSlotMarkedSucessful)() returns if the slot passed in parameter has
* been marked as successful using markBootSuccessful.
* Returns 1 if the slot has been marked as successful, 0 if it's
* not the case, and -errno on error.
*/
int (*isSlotMarkedSuccessful)(unsigned slot);
/**
* Returns the active slot to boot into on the next boot. If
* setActiveBootSlot() has been called, the getter function should return
* the same slot as the one provided in the last setActiveBootSlot() call.
*/
unsigned (*getActiveBootSlot)();
/**
* Returns the active slot to boot into on the next boot. If
* setActiveBootSlot() has been called, the getter function should return
* the same slot as the one provided in the last setActiveBootSlot() call.
*/
unsigned (*getActiveBootSlot)();
};
extern const struct boot_control_module bootctl;
extern const struct boot_control_module bootctl_test;
#endif // __BOOTCTRL_H__
#endif // __BOOTCTRL_H__

656
bootctrl_impl.c Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,96 +0,0 @@
/*
* Copyright (C) 2016 The Android Open Source Project
* Copyright (C) 2022 Caleb Connolly <caleb@connolly.tech>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "bootctrl.h"
struct test_state {
struct slot_info slots[2];
};
static struct test_state state = {
.slots = {
[0] = {
.active = true,
.bootable = true,
.successful = false,
},
[1] = {
.active = false,
.bootable = true,
.successful = false,
},
}
};
unsigned test_get_current_slot()
{
return (unsigned int)state.slots[1].active;
}
int test_mark_boot_successful(unsigned slot)
{
return 0;
}
int test_set_active_boot_slot(unsigned slot)
{
return 0;
}
int test_set_slot_as_unbootable(unsigned slot)
{
return 0;
}
int test_is_slot_bootable(unsigned slot)
{
return 1;
}
const char *test_get_suffix(unsigned slot)
{
switch (slot) {
case 0:
return "_x";
case 1:
return "_z";
default:
return "??";
}
}
int test_is_slot_marked_successful(unsigned slot)
{
return 1;
}
unsigned test_get_active_boot_slot()
{
return 0;
}
const struct boot_control_module bootctl_test = {
.getCurrentSlot = test_get_current_slot,
.markBootSuccessful = test_mark_boot_successful,
.setActiveBootSlot = test_set_active_boot_slot,
.setSlotAsUnbootable = test_set_slot_as_unbootable,
.isSlotBootable = test_is_slot_bootable,
.getSuffix = test_get_suffix,
.isSlotMarkedSuccessful = test_is_slot_marked_successful,
.getActiveBootSlot = test_get_active_boot_slot,
};

144
crc32.c Normal file
View File

@@ -0,0 +1,144 @@
/*
* Dec 5, 2000 Matt Domsch <Matt_Domsch@dell.com>
* - Copied crc32.c from the linux/drivers/net/cipe directory.
* - Now pass seed as an arg
* - changed len to be an unsigned long
* - changed crc32val to be a register
* - License remains unchanged! It's still GPL-compatable!
*/
/*
GPL-2 License
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Copied from efibootmgr 2023-06-23
*/
/* ============================================================= */
/* COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or */
/* code or tables extracted from it, as desired without restriction. */
/* */
/* First, the polynomial itself and its table of feedback terms. The */
/* polynomial is */
/* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */
/* */
/* Note that we take it "backwards" and put the highest-order term in */
/* the lowest-order bit. The X^32 term is "implied"; the LSB is the */
/* X^31 term, etc. The X^0 term (usually shown as "+1") results in */
/* the MSB being 1. */
/* */
/* Note that the usual hardware shift register implementation, which */
/* is what we're using (we're merely optimizing it by doing eight-bit */
/* chunks at a time) shifts bits into the lowest-order term. In our */
/* implementation, that means shifting towards the right. Why do we */
/* do it this way? Because the calculated CRC must be transmitted in */
/* order from highest-order term to lowest-order term. UARTs transmit */
/* characters in order from LSB to MSB. By storing the CRC this way, */
/* we hand it to the UART in the order low-byte to high-byte; the UART */
/* sends each low-bit to hight-bit; and the result is transmission bit */
/* by bit from highest- to lowest-order term without requiring any bit */
/* shuffling on our part. Reception works similarly. */
/* */
/* The feedback terms table consists of 256, 32-bit entries. Notes: */
/* */
/* The table can be generated at runtime if desired; code to do so */
/* is shown later. It might not be obvious, but the feedback */
/* terms simply represent the results of eight shift/xor opera- */
/* tions for all combinations of data and CRC register values. */
/* */
/* The values must be right-shifted by eight bits by the "updcrc" */
/* logic; the shift must be unsigned (bring in zeroes). On some */
/* hardware you could probably optimize the shift in assembler by */
/* using byte-swap instructions. */
/* polynomial $edb88320 */
/* */
/* -------------------------------------------------------------------- */
#include <stdint.h>
static uint32_t crc32_tab[] = {
0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
0x2d02ef8dL
};
/* Return a 32-bit CRC of the contents of the buffer. */
uint32_t
efi_crc32(const void *buf, unsigned long len)
{
unsigned long i;
register uint32_t crc32val;
const unsigned char *s = buf;
crc32val = ~0U;
for (i = 0; i < len; i ++)
{
crc32val =
crc32_tab[(crc32val ^ s[i]) & 0xff] ^
(crc32val >> 8);
}
return crc32val ^ ~0U;
}

39
crc32.h Normal file
View File

@@ -0,0 +1,39 @@
/*
libparted - a library for manipulating disk partitions
Copyright (C) 1998-2000 Free Software Foundation, Inc.
crc32.h
GPL-2 License
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Copied from efibootmgr 2023-06-23
*/
#ifndef _CRC32_H
#define _CRC32_H
#include <stdint.h>
/*
* This computes a 32 bit CRC of the data in the buffer, and returns the CRC.
* The polynomial used is 0xedb88320.
*/
extern uint32_t efi_crc32 (const void *buf, unsigned long len);
#endif /* _CRC32_H */

File diff suppressed because it is too large Load Diff

View File

@@ -29,14 +29,14 @@
#ifndef __GPT_UTILS_H__
#define __GPT_UTILS_H__
#include <vector>
#include <string>
#include <map>
#ifdef __cplusplus
extern "C" {
#endif
#include <unistd.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include <linux/limits.h>
#define GPT_SIGNATURE "EFI PART"
#define HEADER_SIZE_OFFSET 12
@@ -60,8 +60,8 @@ extern "C" {
#define PARTITION_NAME_OFFSET 56
#define MAX_GPT_NAME_SIZE 72
//Bit 48 onwords in the attribute field are the ones where we are allowed to
//store our AB attributes.
// Bit 48 onwords in the attribute field are the ones where we are allowed to
// store our AB attributes.
#define AB_FLAG_OFFSET (ATTRIBUTE_FLAG_OFFSET + 6)
#define GPT_DISK_INIT_MAGIC 0xABCD
#define AB_PARTITION_ATTR_SLOT_ACTIVE (0x1 << 2)
@@ -74,16 +74,26 @@ extern "C" {
#define AB_SLOT_A_SUFFIX "_a"
#define AB_SLOT_B_SUFFIX "_b"
#define PTN_XBL "xbl"
#define PTN_SWAP_LIST \
PTN_XBL, "abl", "aop", "apdp", "cmnlib", "cmnlib64", "devcfg", "dtbo", \
"hyp", "keymaster", "msadp", "qupfw", "storsec", "tz", \
"vbmeta", "vbmeta_system", "xbl_config"
// XBL is not included because the slot attributes are meaningless there
// *which* XBL partition is active is determined via the UFS bBootLunEn field
// as it needs to be handled by PBL
#define PTN_SWAP_LIST \
"abl_a", "aop_a", "apdp_a", "cmnlib_a", "cmnlib64_a", "devcfg_a", "dtbo_a", \
"hyp_a", "keymaster_a", "msadp_a", "qupfw_a", "storsec_a", "tz_a", \
"vbmeta_a", "vbmeta_system_a"
static const char g_all_ptns[][MAX_GPT_NAME_SIZE + 1] = {
PTN_SWAP_LIST, "boot_a", "system_a",
"vendor_a", "modem_a", "system_ext_a", "product_a"
};
// No more than /dev/sdk
#define MAX_BLOCK_DEVICES 10
#define AB_PTN_LIST \
PTN_SWAP_LIST, "boot", "system", "vendor", "modem", "system_ext", \
"product"
#define BOOT_DEV_DIR "/dev/disk/by-partlabel"
#define GPT_PTN_PATH_MAX sizeof(BOOT_DEV_DIR) + MAX_GPT_NAME_SIZE + 2
#define EMMC_DEVICE "/dev/mmcblk0"
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
@@ -93,79 +103,69 @@ enum gpt_instance { PRIMARY_GPT = 0, SECONDARY_GPT };
enum boot_chain { NORMAL_BOOT = 0, BACKUP_BOOT };
struct gpt_disk {
//GPT primary header
// GPT primary header
uint8_t *hdr;
//primary header crc
// primary header crc
uint32_t hdr_crc;
//GPT backup header
// GPT backup header
uint8_t *hdr_bak;
//backup header crc
// backup header crc
uint32_t hdr_bak_crc;
//Partition entries array
// Partition entries array
uint8_t *pentry_arr;
//Partition entries array for backup table
// Partition entries array for backup table
uint8_t *pentry_arr_bak;
//Size of the pentry array
// Size of the pentry array
uint32_t pentry_arr_size;
//Size of each element in the pentry array
// Size of each element in the pentry array
uint32_t pentry_size;
//CRC of the partition entry array
// CRC of the partition entry array
uint32_t pentry_arr_crc;
//CRC of the backup partition entry array
// CRC of the backup partition entry array
uint32_t pentry_arr_bak_crc;
//Path to block dev representing the disk
// Path to block dev representing the disk
char devpath[PATH_MAX];
//Block size of disk
// Block size of disk
uint32_t block_size;
uint32_t is_initialized;
};
//GPT disk methods
struct gpt_disk *gpt_disk_alloc();
//Free previously allocated gpt_disk struct
// GPT disk methods
bool gpt_disk_is_valid(struct gpt_disk *disk);
// Free previously allocated gpt_disk struct
void gpt_disk_free(struct gpt_disk *disk);
//Get the details of the disk holding the partition whose name
//is passed in via dev
// Get the details of the disk holding the partition whose name
// is passed in via dev
int gpt_disk_get_disk_info(const char *dev, struct gpt_disk *disk);
//Get pointer to partition entry from a allocated gpt_disk structure
int partition_is_for_disk(const struct gpt_disk *disk, const char *part, char *blockdev, int blockdev_len);
// Get pointer to partition entry from a allocated gpt_disk structure
uint8_t *gpt_disk_get_pentry(struct gpt_disk *disk, const char *partname,
enum gpt_instance instance);
//Update the crc fields of the modified disk structure
int gpt_disk_update_crc(struct gpt_disk *disk);
//Write the contents of struct gpt_disk back to the actual disk
// Write the contents of struct gpt_disk back to the actual disk
int gpt_disk_commit(struct gpt_disk *disk);
//Swtich betwieen using either the primary or the backup
//boot LUN for boot. This is required since UFS boot partitions
//cannot have a backup GPT which is what we use for failsafe
//updates of the other 'critical' partitions. This function will
//not be invoked for emmc targets and on UFS targets is only required
//to be invoked for XBL.
// Swtich betwieen using either the primary or the backup
// boot LUN for boot. This is required since UFS boot partitions
// cannot have a backup GPT which is what we use for failsafe
// updates of the other 'critical' partitions. This function will
// not be invoked for emmc targets and on UFS targets is only required
// to be invoked for XBL.
//
//The algorithm to do this is as follows:
//- Find the real block device(eg: /dev/block/sdb) that corresponds
// The algorithm to do this is as follows:
// - Find the real block device(eg: /dev/block/sdb) that corresponds
// to the /dev/block/bootdevice/by-name/xbl(bak) symlink
//
//- Once we have the block device 'node' name(sdb in the above example)
// - Once we have the block device 'node' name(sdb in the above example)
// use this node to to locate the scsi generic device that represents
// it by checking the file /sys/block/sdb/device/scsi_generic/sgY
//
//- Once we locate sgY we call the query ioctl on /dev/sgy to switch
//the boot lun to either LUNA or LUNB
// - Once we locate sgY we call the query ioctl on /dev/sgy to switch
// the boot lun to either LUNA or LUNB
int gpt_utils_set_xbl_boot_partition(enum boot_chain chain);
//Given a vector of partition names as a input and a reference to a map,
//populate the map to indicate which physical disk each of the partitions
//sits on. The key in the map is the path to the block device where the
//partition lies and the value is a vector of strings indicating which of
//the passed in partition names sits on that device.
int gpt_utils_get_partition_map(
std::vector<std::string> &partition_list,
std::map<std::string, std::vector<std::string> > &partition_map);
bool gpt_utils_is_partition_backed_by_emmc(const char *part);
#ifdef __cplusplus
}

View File

@@ -1,15 +1,17 @@
project('qbootctl', 'cpp')
project('qbootctl', 'c', default_options : ['c_std=gnu11'])
deps = [
dependency('zlib'),
]
cc = meson.get_compiler('c')
if not cc.has_header('linux/bsg.h')
error('linux-headers not found')
endif
src = [
'qbootctl.cpp',
'bootctrl_impl.cpp',
'bootctrl_test.cpp',
'gpt-utils.cpp',
'ufs-bsg.cpp',
'qbootctl.c',
'bootctrl_impl.c',
'gpt-utils.c',
'ufs-bsg.c',
'crc32.c',
]
inc = [
@@ -18,7 +20,6 @@ inc = [
executable('qbootctl', src,
include_directories: inc,
dependencies: deps,
install: true,
c_args: [],
)

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 Caleb Connolly <caleb@connolly.tech>
* Copyright (C) 2023 Caleb Connolly <caleb@connolly.tech>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -15,16 +15,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <map>
#include <list>
#include <string>
#include <vector>
#include <errno.h>
#include <regex>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdint.h>
#include "bootctrl.h"
@@ -77,6 +74,7 @@ fail:
int usage()
{
// clang-format off
fprintf(stderr, "qbootctl: qcom bootctrl HAL port for Linux\n");
fprintf(stderr, "-------------------------------------------\n");
fprintf(stderr, "qbootctl [-c|-m|-s|-u|-b|-n|-x] [SLOT]\n\n");
@@ -84,17 +82,14 @@ int usage()
fprintf(stderr, " -h this help text\n");
fprintf(stderr, " -c get the current slot\n");
fprintf(stderr, " -a get the active slot\n");
fprintf(stderr,
" -b SLOT check if SLOT is marked as bootable\n");
fprintf(stderr,
" -n SLOT check if SLOT is marked as successful\n");
fprintf(stderr,
" -x [SLOT] get the slot suffix for SLOT (default: current)\n");
fprintf(stderr, " -b SLOT check if SLOT is marked as bootable\n");
fprintf(stderr, " -n SLOT check if SLOT is marked as successful\n");
fprintf(stderr, " -x [SLOT] get the slot suffix for SLOT (default: current)\n");
fprintf(stderr, " -s SLOT set to active slot to SLOT\n");
fprintf(stderr,
" -m [SLOT] mark a boot as successful (default: current)\n");
fprintf(stderr,
" -u [SLOT] mark SLOT as unbootable (default: current)\n");
fprintf(stderr, " -m [SLOT] mark a boot as successful (default: current)\n");
fprintf(stderr, " -u [SLOT] mark SLOT as unbootable (default: current)\n");
fprintf(stderr, " -i still write the GPT headers even if the UFS bLun can't be changed (default: false)\n");
// clang-format on
return 1;
}
@@ -106,7 +101,7 @@ int get_slot_info(struct slot_info *slots)
slots[active_slot].active = true;
for (size_t i = 0; i < 2; i++) {
for (int i = 0; i < 2; i++) {
rc = impl->isSlotMarkedSuccessful(i);
if (rc < 0)
return rc;
@@ -120,16 +115,15 @@ int get_slot_info(struct slot_info *slots)
return 0;
}
void dump_info()
static void dump_info(int current_slot)
{
struct slot_info slots[2] = { { 0 } };
int current_slot = impl->getCurrentSlot();
get_slot_info(slots);
printf("Current slot: %s\n",
current_slot >= 0 ? impl->getSuffix(current_slot) : "N/A");
for (size_t i = 0; i < 2; i++) {
for (int i = 0; i < 2; i++) {
printf("SLOT %s:\n", impl->getSuffix(i));
printf("\tActive : %d\n", slots[i].active);
printf("\tSuccessful : %d\n", slots[i].successful);
@@ -140,22 +134,24 @@ void dump_info()
int main(int argc, char **argv)
{
int optflag;
int slot = -1;
int slot = -1, current_slot;
int rc;
const char* IS_TEST = getenv("QBOOTCTL_TEST");
if (IS_TEST) {
impl = &bootctl_test;
}
bool ignore_missing_bsg = false;
if(geteuid() != 0) {
fprintf(stderr, "This program must be run as root!\n");
return 1;
}
current_slot = impl->getCurrentSlot();
if (current_slot < 0) {
fprintf(stderr, "No slots found, is this an A/B device?\n");
return 1;
}
switch (argc) {
case 1:
dump_info();
dump_info(current_slot);
return 0;
case 2:
break;
@@ -166,14 +162,13 @@ int main(int argc, char **argv)
return usage();
}
if (slot < 0)
slot = impl->getCurrentSlot();
optflag = getopt(argc, argv, "hcmas:ub:n:x");
if (slot < 0 || optflag == 'c')
slot = current_slot;
switch (optflag) {
case 'c':
slot = impl->getCurrentSlot();
printf("Current slot: %s\n", impl->getSuffix(slot));
return 0;
case 'a':
@@ -193,7 +188,7 @@ int main(int argc, char **argv)
printf("%s\n", impl->getSuffix(slot));
return 0;
case 's':
rc = impl->setActiveBootSlot(slot);
rc = impl->setActiveBootSlot(slot, ignore_missing_bsg);
if (rc < 0) {
fprintf(stderr, "SLOT %s: Failed to set active\n",
impl->getSuffix(slot));
@@ -218,6 +213,9 @@ int main(int argc, char **argv)
}
printf("SLOT %s: Set as unbootable\n", impl->getSuffix(slot));
return 0;
case 'i':
ignore_missing_bsg = true;
break;
case 'h':
default:
usage();

View File

@@ -81,16 +81,16 @@ static int ufs_bsg_ioctl(int fd, struct ufs_bsg_request *req,
enum bsg_ioctl_dir dir)
{
int ret;
struct sg_io_v4 sg_io {
struct sg_io_v4 sg_io = {
.guard = 'Q',
.protocol = BSG_PROTOCOL_SCSI,
.subprotocol = BSG_SUB_PROTOCOL_SCSI_TRANSPORT,
.request_len = sizeof(*req),
.request = (__u64)req,
.response = (__u64)rsp,
.max_response_len = sizeof(*rsp),
};
sg_io.guard = 'Q';
sg_io.protocol = BSG_PROTOCOL_SCSI;
sg_io.subprotocol = BSG_SUB_PROTOCOL_SCSI_TRANSPORT;
sg_io.request_len = sizeof(*req);
sg_io.request = (__u64)req;
sg_io.response = (__u64)rsp;
sg_io.max_response_len = sizeof(*rsp);
if (dir == BSG_IOCTL_DIR_FROM_DEV) {
sg_io.din_xfer_len = buf_len;
sg_io.din_xferp = (__u64)(buf);
@@ -137,10 +137,8 @@ static void compose_ufs_bsg_query_req(struct ufs_bsg_request *req, __u8 func,
static int ufs_query_attr(int fd, __u32 value, __u8 func, __u8 opcode, __u8 idn,
__u8 index, __u8 sel)
{
struct ufs_bsg_request req {
};
struct ufs_bsg_reply rsp {
};
struct ufs_bsg_request req = { 0 };
struct ufs_bsg_reply rsp = { 0 };
enum bsg_ioctl_dir dir = BSG_IOCTL_DIR_FROM_DEV;
int ret = 0;
@@ -176,13 +174,12 @@ int32_t set_boot_lun(__u8 lun_id)
ret = ufs_query_attr(fd_ufs_bsg, boot_lun_id, QUERY_REQ_FUNC_STD_WRITE,
QUERY_REQ_OP_WRITE_ATTR, QUERY_ATTR_IDN_BOOT_LU_EN,
0, 0);
if (ret) {
if (ret)
fprintf(stderr,
"Error requesting ufs attr idn %d via query ioctl (return value: %d, error no: %d)",
QUERY_ATTR_IDN_BOOT_LU_EN, ret, errno);
goto out;
}
out:
ufs_bsg_dev_close();
return ret;
}

View File

@@ -2,7 +2,7 @@
#define __UTILS_H__
// Enable debug logging
//#define DEBUG 1
// #define DEBUG 1
#ifdef DEBUG
#define LOGD(fmt, ...) printf(fmt, ##__VA_ARGS__)