25 Commits
0.1.0 ... main

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
Caleb Connolly
77b48f092a Merge branch 'emmc' into 'main'
Support eMMC-backed A/B

See merge request sdm845-mainline/qbootctl!1
2022-09-21 16:25:40 +00:00
Richard Acayan
17ee1b8e65 detect and handle emmc-backed xbl partitions when switching slots 2022-08-31 17:49:18 -04:00
Richard Acayan
c1f24adfb6 properly remove the partition number and separator 2022-08-31 17:10:31 -04:00
Caleb Connolly
50ef0328af minor cleanups, improve error handling
gracefully handle UFS_BSG not being enabled in the kernel, rather than
putting the device into a semi-bricked state (oops).
2022-06-10 19:18:13 +01:00
Caleb Connolly
487a96f663 gpt-utils: drop unused function
it also called strlcpy which only exists on musl and bionic, dropping it
lets this build on glibc too.
2022-06-05 22:30:34 +01:00
17 changed files with 1972 additions and 1408 deletions

View File

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

2
.gitignore vendored
View File

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

545
LICENSE

File diff suppressed because it is too large Load Diff

View File

@@ -34,8 +34,14 @@ qbootctl [-c|-m|-s|-u|-b|-n|-x] [SLOT]
-s SLOT set to active slot to SLOT -s SLOT set to active slot to SLOT
-m [SLOT] mark a boot as successful (default: current) -m [SLOT] mark a boot as successful (default: current)
-u [SLOT] mark SLOT as unbootable (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 ## Debugging
Set `DEBUG` to 1 in `utils.h` to enable debug logging. Set `DEBUG` to 1 in `utils.h` to enable debug logging.
## Documentation
A more details explanation and a list of devices where qbootctl has been
validated can be found [on the postmarketOS wiki](https://wiki.postmarketos.org/wiki/Android_AB_Slots):

View File

@@ -1,6 +1,20 @@
/* /*
* Copyright (C) 2016 The Android Open Source Project * 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 * 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 * it under the terms of the GNU General Public License as published by
@@ -19,85 +33,86 @@
#ifndef __BOOTCTRL_H__ #ifndef __BOOTCTRL_H__
#define __BOOTCTRL_H__ #define __BOOTCTRL_H__
#include <stdbool.h>
struct slot_info { struct slot_info {
bool active; bool active;
bool bootable; bool bootable;
bool successful; bool successful;
}; };
struct boot_control_module { 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 * (*markBootSuccessful)() marks the specified slot
* whether the current slot is A or B. The meaning of A and B is * as boot successful
* 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 * Returns 0 on success, -errno on error.
* without any risk of corruption. */
* The returned value is always guaranteed to be strictly less than the int (*markBootSuccessful)(unsigned slot);
* value returned by getNumberSlots. Slots start at 0 and
* finish at getNumberSlots() - 1
*/
unsigned (*getCurrentSlot)();
/* /*
* (*markBootSuccessful)() marks the specified slot * (*setActiveBootSlot)() marks the slot passed in parameter as
* as boot successful * the active boot slot (see getCurrentSlot for an explanation
* * of the "slot" parameter). This overrides any previous call to
* Returns 0 on success, -errno on error. * setSlotAsUnbootable.
*/ * Returns 0 on success, -errno on error.
int (*markBootSuccessful)(unsigned slot); */
int (*setActiveBootSlot)(unsigned slot, bool ignore_missing_bsg);
/* /*
* (*setActiveBootSlot)() marks the slot passed in parameter as * (*setSlotAsUnbootable)() marks the slot passed in parameter as
* the active boot slot (see getCurrentSlot for an explanation * an unbootable. This can be used while updating the contents of the slot's
* of the "slot" parameter). This overrides any previous call to * partitions, so that the system will not attempt to boot a known bad set up.
* setSlotAsUnbootable. * Returns 0 on success, -errno on error.
* Returns 0 on success, -errno on error. */
*/ int (*setSlotAsUnbootable)(unsigned slot);
int (*setActiveBootSlot)(unsigned slot);
/* /*
* (*setSlotAsUnbootable)() marks the slot passed in parameter as * (*isSlotBootable)() returns if the slot passed in parameter is
* an unbootable. This can be used while updating the contents of the slot's * bootable. Note that slots can be made unbootable by both the
* partitions, so that the system will not attempt to boot a known bad set up. * bootloader and by the OS using setSlotAsUnbootable.
* Returns 0 on success, -errno on error. * Returns 1 if the slot is bootable, 0 if it's not, and -errno on
*/ * error.
int (*setSlotAsUnbootable)(unsigned slot); */
int (*isSlotBootable)(unsigned slot);
/* /*
* (*isSlotBootable)() returns if the slot passed in parameter is * (*getSuffix)() returns the string suffix used by partitions that
* bootable. Note that slots can be made unbootable by both the * correspond to the slot number passed in parameter. The returned string
* bootloader and by the OS using setSlotAsUnbootable. * is expected to be statically allocated and not need to be freed.
* Returns 1 if the slot is bootable, 0 if it's not, and -errno on * Returns NULL if slot does not match an existing slot.
* error. */
*/ const char *(*getSuffix)(unsigned slot);
int (*isSlotBootable)(unsigned slot);
/* /*
* (*getSuffix)() returns the string suffix used by partitions that * (*isSlotMarkedSucessful)() returns if the slot passed in parameter has
* correspond to the slot number passed in parameter. The returned string * been marked as successful using markBootSuccessful.
* is expected to be statically allocated and not need to be freed. * Returns 1 if the slot has been marked as successful, 0 if it's
* Returns NULL if slot does not match an existing slot. * not the case, and -errno on error.
*/ */
const char* (*getSuffix)(unsigned slot); int (*isSlotMarkedSuccessful)(unsigned slot);
/* /**
* (*isSlotMarkedSucessful)() returns if the slot passed in parameter has * Returns the active slot to boot into on the next boot. If
* been marked as successful using markBootSuccessful. * setActiveBootSlot() has been called, the getter function should return
* Returns 1 if the slot has been marked as successful, 0 if it's * the same slot as the one provided in the last setActiveBootSlot() call.
* not the case, and -errno on error. */
*/ unsigned (*getActiveBootSlot)();
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)();
}; };
extern const struct boot_control_module bootctl; 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__ #ifndef __GPT_UTILS_H__
#define __GPT_UTILS_H__ #define __GPT_UTILS_H__
#include <vector>
#include <string>
#include <map>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#include <unistd.h> #include <unistd.h>
#include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h>
#include <linux/limits.h>
#define GPT_SIGNATURE "EFI PART" #define GPT_SIGNATURE "EFI PART"
#define HEADER_SIZE_OFFSET 12 #define HEADER_SIZE_OFFSET 12
@@ -60,8 +60,8 @@ extern "C" {
#define PARTITION_NAME_OFFSET 56 #define PARTITION_NAME_OFFSET 56
#define MAX_GPT_NAME_SIZE 72 #define MAX_GPT_NAME_SIZE 72
//Bit 48 onwords in the attribute field are the ones where we are allowed to // Bit 48 onwords in the attribute field are the ones where we are allowed to
//store our AB attributes. // store our AB attributes.
#define AB_FLAG_OFFSET (ATTRIBUTE_FLAG_OFFSET + 6) #define AB_FLAG_OFFSET (ATTRIBUTE_FLAG_OFFSET + 6)
#define GPT_DISK_INIT_MAGIC 0xABCD #define GPT_DISK_INIT_MAGIC 0xABCD
#define AB_PARTITION_ATTR_SLOT_ACTIVE (0x1 << 2) #define AB_PARTITION_ATTR_SLOT_ACTIVE (0x1 << 2)
@@ -74,16 +74,28 @@ extern "C" {
#define AB_SLOT_A_SUFFIX "_a" #define AB_SLOT_A_SUFFIX "_a"
#define AB_SLOT_B_SUFFIX "_b" #define AB_SLOT_B_SUFFIX "_b"
#define PTN_XBL "xbl" #define PTN_XBL "xbl"
#define PTN_SWAP_LIST \ // XBL is not included because the slot attributes are meaningless there
PTN_XBL, "abl", "aop", "apdp", "cmnlib", "cmnlib64", "devcfg", "dtbo", \ // *which* XBL partition is active is determined via the UFS bBootLunEn field
"hyp", "keymaster", "msadp", "qupfw", "storsec", "tz", \ // as it needs to be handled by PBL
"vbmeta", "vbmeta_system", "xbl_config" #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 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])) #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
enum gpt_instance { PRIMARY_GPT = 0, SECONDARY_GPT }; enum gpt_instance { PRIMARY_GPT = 0, SECONDARY_GPT };
@@ -91,78 +103,70 @@ enum gpt_instance { PRIMARY_GPT = 0, SECONDARY_GPT };
enum boot_chain { NORMAL_BOOT = 0, BACKUP_BOOT }; enum boot_chain { NORMAL_BOOT = 0, BACKUP_BOOT };
struct gpt_disk { struct gpt_disk {
//GPT primary header // GPT primary header
uint8_t *hdr; uint8_t *hdr;
//primary header crc // primary header crc
uint32_t hdr_crc; uint32_t hdr_crc;
//GPT backup header // GPT backup header
uint8_t *hdr_bak; uint8_t *hdr_bak;
//backup header crc // backup header crc
uint32_t hdr_bak_crc; uint32_t hdr_bak_crc;
//Partition entries array // Partition entries array
uint8_t *pentry_arr; uint8_t *pentry_arr;
//Partition entries array for backup table // Partition entries array for backup table
uint8_t *pentry_arr_bak; uint8_t *pentry_arr_bak;
//Size of the pentry array // Size of the pentry array
uint32_t pentry_arr_size; 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; uint32_t pentry_size;
//CRC of the partition entry array // CRC of the partition entry array
uint32_t pentry_arr_crc; 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; uint32_t pentry_arr_bak_crc;
//Path to block dev representing the disk // Path to block dev representing the disk
char devpath[PATH_MAX]; char devpath[PATH_MAX];
//Block size of disk // Block size of disk
uint32_t block_size; uint32_t block_size;
uint32_t is_initialized; uint32_t is_initialized;
}; };
//GPT disk methods // GPT disk methods
struct gpt_disk *gpt_disk_alloc(); bool gpt_disk_is_valid(struct gpt_disk *disk);
//Free previously allocated gpt_disk struct // Free previously allocated gpt_disk struct
void gpt_disk_free(struct gpt_disk *disk); void gpt_disk_free(struct gpt_disk *disk);
//Get the details of the disk holding the partition whose name // Get the details of the disk holding the partition whose name
//is passed in via dev // is passed in via dev
int gpt_disk_get_disk_info(const char *dev, struct gpt_disk *disk); 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, uint8_t *gpt_disk_get_pentry(struct gpt_disk *disk, const char *partname,
enum gpt_instance instance); enum gpt_instance instance);
//Update the crc fields of the modified disk structure // Write the contents of struct gpt_disk back to the actual disk
int gpt_disk_update_crc(struct gpt_disk *disk);
//Write the contents of struct gpt_disk back to the actual disk
int gpt_disk_commit(struct gpt_disk *disk); int gpt_disk_commit(struct gpt_disk *disk);
//Swtich betwieen using either the primary or the backup // Swtich betwieen using either the primary or the backup
//boot LUN for boot. This is required since UFS boot partitions // boot LUN for boot. This is required since UFS boot partitions
//cannot have a backup GPT which is what we use for failsafe // cannot have a backup GPT which is what we use for failsafe
//updates of the other 'critical' partitions. This function will // updates of the other 'critical' partitions. This function will
//not be invoked for emmc targets and on UFS targets is only required // not be invoked for emmc targets and on UFS targets is only required
//to be invoked for XBL. // to be invoked for XBL.
// //
//The algorithm to do this is as follows: // The algorithm to do this is as follows:
//- Find the real block device(eg: /dev/block/sdb) that corresponds // - Find the real block device(eg: /dev/block/sdb) that corresponds
// to the /dev/block/bootdevice/by-name/xbl(bak) symlink // 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 // use this node to to locate the scsi generic device that represents
// it by checking the file /sys/block/sdb/device/scsi_generic/sgY // 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 // - Once we locate sgY we call the query ioctl on /dev/sgy to switch
//the boot lun to either LUNA or LUNB // the boot lun to either LUNA or LUNB
int gpt_utils_set_xbl_boot_partition(enum boot_chain chain); 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, bool gpt_utils_is_partition_backed_by_emmc(const char *part);
//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);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -1,15 +1,17 @@
project('qbootctl', 'cpp') project('qbootctl', 'c', default_options : ['c_std=gnu11'])
deps = [ cc = meson.get_compiler('c')
dependency('zlib'),
] if not cc.has_header('linux/bsg.h')
error('linux-headers not found')
endif
src = [ src = [
'qbootctl.cpp', 'qbootctl.c',
'bootctrl_impl.cpp', 'bootctrl_impl.c',
'bootctrl_test.cpp', 'gpt-utils.c',
'gpt-utils.cpp', 'ufs-bsg.c',
'ufs-bsg.cpp', 'crc32.c',
] ]
inc = [ inc = [
@@ -18,7 +20,6 @@ inc = [
executable('qbootctl', src, executable('qbootctl', src,
include_directories: inc, include_directories: inc,
dependencies: deps,
install: true, install: true,
c_args: [], 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 * 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 * 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/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <map>
#include <list>
#include <string>
#include <vector>
#include <errno.h> #include <errno.h>
#include <regex>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <stdbool.h>
#include <stdint.h>
#include "bootctrl.h" #include "bootctrl.h"
@@ -77,6 +74,7 @@ fail:
int usage() int usage()
{ {
// clang-format off
fprintf(stderr, "qbootctl: qcom bootctrl HAL port for Linux\n"); fprintf(stderr, "qbootctl: qcom bootctrl HAL port for Linux\n");
fprintf(stderr, "-------------------------------------------\n"); fprintf(stderr, "-------------------------------------------\n");
fprintf(stderr, "qbootctl [-c|-m|-s|-u|-b|-n|-x] [SLOT]\n\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, " -h this help text\n");
fprintf(stderr, " -c get the current slot\n"); fprintf(stderr, " -c get the current slot\n");
fprintf(stderr, " -a get the active slot\n"); fprintf(stderr, " -a get the active slot\n");
fprintf(stderr, fprintf(stderr, " -b SLOT check if SLOT is marked as bootable\n");
" -b SLOT check if SLOT is marked as bootable\n"); fprintf(stderr, " -n SLOT check if SLOT is marked as successful\n");
fprintf(stderr, fprintf(stderr, " -x [SLOT] get the slot suffix for SLOT (default: current)\n");
" -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, " -s SLOT set to active slot to SLOT\n");
fprintf(stderr, fprintf(stderr, " -m [SLOT] mark a boot as successful (default: current)\n");
" -m [SLOT] mark a boot as successful (default: current)\n"); fprintf(stderr, " -u [SLOT] mark SLOT as unbootable (default: current)\n");
fprintf(stderr, fprintf(stderr, " -i still write the GPT headers even if the UFS bLun can't be changed (default: false)\n");
" -u [SLOT] mark SLOT as unbootable (default: current)\n"); // clang-format on
return 1; return 1;
} }
@@ -106,7 +101,7 @@ int get_slot_info(struct slot_info *slots)
slots[active_slot].active = true; slots[active_slot].active = true;
for (size_t i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
rc = impl->isSlotMarkedSuccessful(i); rc = impl->isSlotMarkedSuccessful(i);
if (rc < 0) if (rc < 0)
return rc; return rc;
@@ -120,16 +115,15 @@ int get_slot_info(struct slot_info *slots)
return 0; return 0;
} }
void dump_info() static void dump_info(int current_slot)
{ {
struct slot_info slots[2] = { { 0 } }; struct slot_info slots[2] = { { 0 } };
int current_slot = impl->getCurrentSlot();
get_slot_info(slots); get_slot_info(slots);
printf("Current slot: %s\n", printf("Current slot: %s\n",
current_slot >= 0 ? impl->getSuffix(current_slot) : "N/A"); 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("SLOT %s:\n", impl->getSuffix(i));
printf("\tActive : %d\n", slots[i].active); printf("\tActive : %d\n", slots[i].active);
printf("\tSuccessful : %d\n", slots[i].successful); printf("\tSuccessful : %d\n", slots[i].successful);
@@ -140,22 +134,24 @@ void dump_info()
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int optflag; int optflag;
int slot = -1; int slot = -1, current_slot;
int rc; int rc;
bool ignore_missing_bsg = false;
const char* IS_TEST = getenv("QBOOTCTL_TEST");
if (IS_TEST) {
impl = &bootctl_test;
}
if(geteuid() != 0) { if(geteuid() != 0) {
fprintf(stderr, "This program must be run as root!\n"); fprintf(stderr, "This program must be run as root!\n");
return 1; 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) { switch (argc) {
case 1: case 1:
dump_info(); dump_info(current_slot);
return 0; return 0;
case 2: case 2:
break; break;
@@ -166,14 +162,13 @@ int main(int argc, char **argv)
return usage(); return usage();
} }
if (slot < 0)
slot = impl->getCurrentSlot();
optflag = getopt(argc, argv, "hcmas:ub:n:x"); optflag = getopt(argc, argv, "hcmas:ub:n:x");
if (slot < 0 || optflag == 'c')
slot = current_slot;
switch (optflag) { switch (optflag) {
case 'c': case 'c':
slot = impl->getCurrentSlot();
printf("Current slot: %s\n", impl->getSuffix(slot)); printf("Current slot: %s\n", impl->getSuffix(slot));
return 0; return 0;
case 'a': case 'a':
@@ -193,7 +188,7 @@ int main(int argc, char **argv)
printf("%s\n", impl->getSuffix(slot)); printf("%s\n", impl->getSuffix(slot));
return 0; return 0;
case 's': case 's':
rc = impl->setActiveBootSlot(slot); rc = impl->setActiveBootSlot(slot, ignore_missing_bsg);
if (rc < 0) { if (rc < 0) {
fprintf(stderr, "SLOT %s: Failed to set active\n", fprintf(stderr, "SLOT %s: Failed to set active\n",
impl->getSuffix(slot)); impl->getSuffix(slot));
@@ -218,6 +213,9 @@ int main(int argc, char **argv)
} }
printf("SLOT %s: Set as unbootable\n", impl->getSuffix(slot)); printf("SLOT %s: Set as unbootable\n", impl->getSuffix(slot));
return 0; return 0;
case 'i':
ignore_missing_bsg = true;
break;
case 'h': case 'h':
default: default:
usage(); usage();

View File

@@ -45,52 +45,30 @@
#include "utils.h" #include "utils.h"
#include "ufs-bsg.h" #include "ufs-bsg.h"
// FIXME: replace this with something that actually works /* UFS BSG device node */
// static int get_ufs_bsg_dev(void) static char ufs_bsg_dev[FNAME_SZ] = "/dev/bsg/ufs-bsg0";
// {
// DIR *dir;
// struct dirent *ent;
// int ret = -ENODEV;
// if ((dir = opendir ("/dev")) != NULL) { static int fd_ufs_bsg = 0;
// /* read all the files and directories within directory */
// while ((ent = readdir(dir)) != NULL) {
// if (!strcmp(ent->d_name, "bsg") ||
// !strcmp(ent->d_name, "ufs-bsg0")) {
// snprintf(ufs_bsg_dev, FNAME_SZ, "/dev/%s", ent->d_name);
// ret = 0;
// break;
// }
// }
// if (ret)
// fprintf(stderr, "could not find the ufs-bsg dev\n");
// closedir (dir);
// } else {
// /* could not open directory */
// fprintf(stderr, "could not open /dev (error no: %d)\n", errno);
// ret = -EINVAL;
// }
// return ret; int ufs_bsg_dev_open()
// }
int ufs_bsg_dev_open(void)
{ {
int ret; if (fd_ufs_bsg)
if (!fd_ufs_bsg) { return 0;
fd_ufs_bsg = open(ufs_bsg_dev, O_RDWR);
ret = errno; fd_ufs_bsg = open(ufs_bsg_dev, O_RDWR);
if (fd_ufs_bsg < 0) { if (fd_ufs_bsg < 0) {
fprintf(stderr, "Unable to open %s (error no: %d)", fprintf(stderr, "Unable to open '%s': %s\n", ufs_bsg_dev,
ufs_bsg_dev, errno); strerror(errno));
fd_ufs_bsg = 0; fprintf(stderr,
return ret; "Is CONFIG_SCSI_UFS_BSG is enabled in your kernel?\n");
} fd_ufs_bsg = 0;
return -1;
} }
return 0; return 0;
} }
void ufs_bsg_dev_close(void) void ufs_bsg_dev_close()
{ {
if (fd_ufs_bsg) { if (fd_ufs_bsg) {
close(fd_ufs_bsg); close(fd_ufs_bsg);
@@ -103,16 +81,16 @@ static int ufs_bsg_ioctl(int fd, struct ufs_bsg_request *req,
enum bsg_ioctl_dir dir) enum bsg_ioctl_dir dir)
{ {
int ret; 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) { if (dir == BSG_IOCTL_DIR_FROM_DEV) {
sg_io.din_xfer_len = buf_len; sg_io.din_xfer_len = buf_len;
sg_io.din_xferp = (__u64)(buf); sg_io.din_xferp = (__u64)(buf);
@@ -159,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, static int ufs_query_attr(int fd, __u32 value, __u8 func, __u8 opcode, __u8 idn,
__u8 index, __u8 sel) __u8 index, __u8 sel)
{ {
struct ufs_bsg_request req { struct ufs_bsg_request req = { 0 };
}; struct ufs_bsg_reply rsp = { 0 };
struct ufs_bsg_reply rsp {
};
enum bsg_ioctl_dir dir = BSG_IOCTL_DIR_FROM_DEV; enum bsg_ioctl_dir dir = BSG_IOCTL_DIR_FROM_DEV;
int ret = 0; int ret = 0;
@@ -188,9 +164,6 @@ int32_t set_boot_lun(__u8 lun_id)
int32_t ret; int32_t ret;
__u32 boot_lun_id = lun_id; __u32 boot_lun_id = lun_id;
// ret = get_ufs_bsg_dev();
// if (ret)
// return ret;
LOGD("Using UFS bsg device: %s\n", ufs_bsg_dev); LOGD("Using UFS bsg device: %s\n", ufs_bsg_dev);
ret = ufs_bsg_dev_open(); ret = ufs_bsg_dev_open();
@@ -201,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, 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, QUERY_REQ_OP_WRITE_ATTR, QUERY_ATTR_IDN_BOOT_LU_EN,
0, 0); 0, 0);
if (ret) { if (ret)
fprintf(stderr, fprintf(stderr,
"Error requesting ufs attr idn %d via query ioctl (return value: %d, error no: %d)", "Error requesting ufs attr idn %d via query ioctl (return value: %d, error no: %d)",
QUERY_ATTR_IDN_BOOT_LU_EN, ret, errno); QUERY_ATTR_IDN_BOOT_LU_EN, ret, errno);
goto out;
}
out:
ufs_bsg_dev_close(); ufs_bsg_dev_close();
return ret; return ret;
} }

View File

@@ -36,11 +36,6 @@
#define DWORD(b3, b2, b1, b0) htobe32((b3 << 24) | (b2 << 16) | (b1 << 8) | b0) #define DWORD(b3, b2, b1, b0) htobe32((b3 << 24) | (b2 << 16) | (b1 << 8) | b0)
/* UFS BSG device node */
char ufs_bsg_dev[FNAME_SZ] = "/dev/bsg/ufs-bsg0";
int fd_ufs_bsg;
/* UPIU Transaction Codes */ /* UPIU Transaction Codes */
enum { enum {
UTP_UPIU_NOP_OUT = 0x00, UTP_UPIU_NOP_OUT = 0x00,
@@ -91,4 +86,6 @@ enum query_attr_idn {
QUERY_ATTR_IDN_ACTIVE_ICC_LVL = 0x03, QUERY_ATTR_IDN_ACTIVE_ICC_LVL = 0x03,
}; };
int ufs_bsg_dev_open();
#endif /* __RECOVERY_UFS_BSG_H__ */ #endif /* __RECOVERY_UFS_BSG_H__ */

View File

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