Merge changes If824cb70,I025d9d68,Ic5a57a89,If5d63f39,I1adb1906 into main am: f3e994b439

Original change: https://android-review.googlesource.com/c/platform/system/core/+/3318438

Change-Id: I251001b955a0ea61ec84fc5e6389b09666087146
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Doug Anderson 2024-11-07 15:59:51 +00:00 committed by Automerger Merge Worker
commit c4ce6111b4
7 changed files with 249 additions and 40 deletions

View file

@ -39,6 +39,33 @@ for the node path:
`device_id` is `uevent MINOR % 128 + 1`.
3. All other devices are created as `/dev/<basename uevent DEVPATH>`
Whether a device is considered a "boot device" is a bit complicated.
- The recommended way to specify the boot device is to provide the "partition UUID" containing the
kernel (or, really, any parition on the boot device) and then boot device is the block device
containing that partition. This is passed via `androidboot.boot_part_uuid` which can be provided
either via the kernel bootconfig or via the kernel commandline. As an example, you could set
`androidboot.boot_part_uuid=12345678-abcd-ef01-0234-6789abcdef01`.
- Though using `boot_part_uuid` is preferred, you can also specify the boot device via
`androidboot.boot_device` or `androidboot.boot_devices`. These can be passed via the kernel
bootconfig or the kernel command line. It is also possible to pass this via device tree by
creating a `boot_devices` property in the Android firmware node. In most cases the `boot_device`
is the sysfs path (without the `/sys/devices` or `/sys/devices/platform` prefix) to the closest
parent of the block device that's on the "platform" bus. As an example, if the block device is
`/sys/devices/platform/soc@0/7c4000.mmc/mmc_host/mmc1/mmc1:0001/block/mmcblk1` then the
`boot_device` is `soc@0/7c4000.mmc` since we strip off the `/sys/devices/platform` and nothing
past the `7c4000.mmc` directory represents a device on the "platform" bus. In the case that none
of the parents are on the "platform" bus there are special rules for block devices under PCI
and VBD (Virtual Block Device). NOTE: sysfs paths for block devices are not guaranteed to be
stable between kernel versions, which is one of the reasons why it is suggested to use
`boot_part_uuid` instead of `boot_devices`. ALSO NOTE: If more than one device matches (either
because multiple `boot_devices` were listed or because there was more than one block device
under the found sysfs directory) and these multiple matching devices provide some of the same
named partitions then the behavior is unspecified.
- There is a further fallback to determine "boot devices" via the vstab, but providing at least
`boot_devices` has been required since Android 12 so this further fallback will not be described
here.
The permissions can be modified using a ueventd.rc script and a line that beings with `/dev`. These
lines take the format of

View file

@ -33,7 +33,49 @@ BlockDevInitializer::BlockDevInitializer() : uevent_listener_(16 * 1024 * 1024)
auto boot_devices = android::fs_mgr::GetBootDevices();
device_handler_ = std::make_unique<DeviceHandler>(
std::vector<Permissions>{}, std::vector<SysfsPermissions>{}, std::vector<Subsystem>{},
std::move(boot_devices), false);
std::move(boot_devices), android::fs_mgr::GetBootPartUuid(), false);
}
// If boot_part_uuid is specified, use it to set boot_devices
//
// When `androidboot.boot_part_uuid` is specified then that's the partition UUID
// of the kernel. Look for that partition and then set `boot_devices` to be
// exactly one item: the block device containing that partition.
//
// NOTE that `boot_part_uuid` is only specified on newer devices. Older devices
// specified `boot_devices` directly.
bool BlockDevInitializer::InitBootDevicesFromPartUuid() {
bool uuid_check_done = false;
auto boot_part_callback = [&, this](const Uevent& uevent) -> ListenerAction {
uuid_check_done = device_handler_->CheckUeventForBootPartUuid(uevent);
return uuid_check_done ? ListenerAction::kStop : ListenerAction::kContinue;
};
// Re-run already arrived uevents looking for the boot partition UUID.
//
// NOTE: If we're not using the boot partition UUID to find the boot
// device then the first uevent we analyze will cause us to stop looking
// and set `uuid_check_done`. This will shortcut all of the UUID logic.
// Replaying one uevent is not expected to be slow.
uevent_listener_.RegenerateUevents(boot_part_callback);
// If we're not done looking, poll for uevents for longer
if (!uuid_check_done) {
Timer t;
uevent_listener_.Poll(boot_part_callback, 10s);
LOG(INFO) << "Wait for boot partition returned after " << t;
}
// Give a nicer error message if we were expecting to find the kernel boot
// partition but didn't. Later code would fail too but the message there
// is a bit further from the root cause of the problem.
if (!uuid_check_done) {
LOG(ERROR) << __PRETTY_FUNCTION__ << ": boot partition not found after polling timeout.";
return false;
}
return true;
}
bool BlockDevInitializer::InitDeviceMapper() {

View file

@ -29,6 +29,7 @@ class BlockDevInitializer final {
public:
BlockDevInitializer();
bool InitBootDevicesFromPartUuid();
bool InitDeviceMapper();
bool InitDmUser(const std::string& name);
bool InitDevices(std::set<std::string> devices);

View file

@ -45,6 +45,7 @@
using namespace std::chrono_literals;
using android::base::Basename;
using android::base::ConsumePrefix;
using android::base::Dirname;
using android::base::ReadFileToString;
using android::base::Readlink;
@ -188,6 +189,41 @@ void SysfsPermissions::SetPermissions(const std::string& path) const {
}
}
BlockDeviceInfo DeviceHandler::GetBlockDeviceInfo(const std::string& uevent_path) const {
BlockDeviceInfo info;
if (!boot_part_uuid_.empty()) {
// Only use the more specific "MMC" or "SCSI" match if a partition UUID
// was passed. Old bootloaders that aren't passing the partition UUID
// instead pass the path to the closest "platform" device. It would
// break them if we chose this deeper (more specific) path.
//
// When we have a UUID we _want_ the more specific path since it can
// handle, for instance, differentiating two USB disks that are on
// the same USB controller. Using the closest platform device would
// classify them both the same by using the path to the USB controller.
if (FindMmcDevice(uevent_path, &info.str)) {
info.type = "mmc";
} else if (FindScsiDevice(uevent_path, &info.str)) {
info.type = "scsi";
}
} else if (FindPlatformDevice(uevent_path, &info.str)) {
info.type = "platform";
} else if (FindPciDevicePrefix(uevent_path, &info.str)) {
info.type = "pci";
} else if (FindVbdDevicePrefix(uevent_path, &info.str)) {
info.type = "vbd";
} else {
// Re-clear device to be extra certain in case one of the FindXXX()
// functions returned false but still modified it.
info.str = "";
}
info.is_boot_device = boot_devices_.find(info.str) != boot_devices_.end();
return info;
}
std::string DeviceHandler::GetPartitionNameForDevice(const std::string& query_device) {
static const auto partition_map = [] {
std::vector<std::pair<std::string, std::string>> partition_map;
@ -218,11 +254,12 @@ std::string DeviceHandler::GetPartitionNameForDevice(const std::string& query_de
return {};
}
// Given a path that may start with a platform device, find the parent platform device by finding a
// parent directory with a 'subsystem' symlink that points to the platform bus.
// If it doesn't start with a platform device, return false
bool DeviceHandler::FindPlatformDevice(std::string path, std::string* platform_device_path) const {
platform_device_path->clear();
// Given a path to a device that may have a parent in the passed set of
// subsystems, find the parent device that's in the passed set of subsystems.
// If we don't find a parent in the passed set of subsystems, return false.
bool DeviceHandler::FindSubsystemDevice(std::string path, std::string* device_path,
const std::set<std::string>& subsystem_paths) const {
device_path->clear();
// Uevents don't contain the mount point, so we need to add it here.
path.insert(0, sysfs_mount_point_);
@ -232,11 +269,20 @@ bool DeviceHandler::FindPlatformDevice(std::string path, std::string* platform_d
while (directory != "/" && directory != ".") {
std::string subsystem_link_path;
if (Realpath(directory + "/subsystem", &subsystem_link_path) &&
(subsystem_link_path == sysfs_mount_point_ + "/bus/platform" ||
subsystem_link_path == sysfs_mount_point_ + "/bus/amba")) {
subsystem_paths.find(subsystem_link_path) != subsystem_paths.end()) {
// We need to remove the mount point that we added above before returning.
directory.erase(0, sysfs_mount_point_.size());
*platform_device_path = directory;
// Skip /devices/platform or /devices/ if present
static constexpr std::string_view devices_platform_prefix = "/devices/platform/";
static constexpr std::string_view devices_prefix = "/devices/";
std::string_view sv = directory;
if (!ConsumePrefix(&sv, devices_platform_prefix)) {
ConsumePrefix(&sv, devices_prefix);
}
*device_path = sv;
return true;
}
@ -250,6 +296,31 @@ bool DeviceHandler::FindPlatformDevice(std::string path, std::string* platform_d
return false;
}
bool DeviceHandler::FindPlatformDevice(std::string path, std::string* platform_device_path) const {
const std::set<std::string> subsystem_paths = {
sysfs_mount_point_ + "/bus/platform",
sysfs_mount_point_ + "/bus/amba",
};
return FindSubsystemDevice(path, platform_device_path, subsystem_paths);
}
bool DeviceHandler::FindMmcDevice(std::string path, std::string* mmc_device_path) const {
const std::set<std::string> subsystem_paths = {
sysfs_mount_point_ + "/bus/mmc",
};
return FindSubsystemDevice(path, mmc_device_path, subsystem_paths);
}
bool DeviceHandler::FindScsiDevice(std::string path, std::string* scsi_device_path) const {
const std::set<std::string> subsystem_paths = {
sysfs_mount_point_ + "/bus/scsi",
};
return FindSubsystemDevice(path, scsi_device_path, subsystem_paths);
}
void DeviceHandler::FixupSysPermissions(const std::string& upath,
const std::string& subsystem) const {
// upaths omit the "/sys" that paths in this list
@ -371,8 +442,7 @@ void SanitizePartitionName(std::string* string) {
}
std::vector<std::string> DeviceHandler::GetBlockDeviceSymlinks(const Uevent& uevent) const {
std::string device;
std::string type;
BlockDeviceInfo info;
std::string partition;
std::string uuid;
@ -382,33 +452,20 @@ std::vector<std::string> DeviceHandler::GetBlockDeviceSymlinks(const Uevent& uev
symlinks.emplace_back("/dev/block/mapper/by-uuid/" + uuid);
}
return symlinks;
} else if (FindPlatformDevice(uevent.path, &device)) {
// Skip /devices/platform or /devices/ if present
static constexpr std::string_view devices_platform_prefix = "/devices/platform/";
static constexpr std::string_view devices_prefix = "/devices/";
}
if (StartsWith(device, devices_platform_prefix)) {
device = device.substr(devices_platform_prefix.length());
} else if (StartsWith(device, devices_prefix)) {
device = device.substr(devices_prefix.length());
}
info = GetBlockDeviceInfo(uevent.path);
type = "platform";
} else if (FindPciDevicePrefix(uevent.path, &device)) {
type = "pci";
} else if (FindVbdDevicePrefix(uevent.path, &device)) {
type = "vbd";
} else {
if (info.type.empty()) {
return {};
}
std::vector<std::string> links;
LOG(VERBOSE) << "found " << type << " device " << device;
LOG(VERBOSE) << "found " << info.type << " device " << info.str;
auto link_path = "/dev/block/" + type + "/" + device;
auto link_path = "/dev/block/" + info.type + "/" + info.str;
bool is_boot_device = boot_devices_.find(device) != boot_devices_.end();
if (!uevent.partition_name.empty()) {
std::string partition_name_sanitized(uevent.partition_name);
SanitizePartitionName(&partition_name_sanitized);
@ -418,10 +475,10 @@ std::vector<std::string> DeviceHandler::GetBlockDeviceSymlinks(const Uevent& uev
}
links.emplace_back(link_path + "/by-name/" + partition_name_sanitized);
// Adds symlink: /dev/block/by-name/<partition_name>.
if (is_boot_device) {
if (info.is_boot_device) {
links.emplace_back("/dev/block/by-name/" + partition_name_sanitized);
}
} else if (is_boot_device) {
} else if (info.is_boot_device) {
// If we don't have a partition name but we are a partition on a boot device, create a
// symlink of /dev/block/by-name/<device_name> for symmetry.
links.emplace_back("/dev/block/by-name/" + uevent.device_name);
@ -541,6 +598,48 @@ void DeviceHandler::HandleAshmemUevent(const Uevent& uevent) {
}
}
// Check Uevents looking for the kernel's boot partition UUID
//
// When we can stop checking uevents (either because we're done or because
// we weren't looking for the kernel's boot partition UUID) then return
// true. Return false if we're not done yet.
bool DeviceHandler::CheckUeventForBootPartUuid(const Uevent& uevent) {
// If we aren't using boot_part_uuid then we're done.
if (boot_part_uuid_.empty()) {
return true;
}
// Finding the boot partition is a one-time thing that we do at init
// time, not steady state. This is because the boot partition isn't
// allowed to go away or change. Once we found the boot partition we don't
// expect to run again.
if (found_boot_part_uuid_) {
LOG(WARNING) << __PRETTY_FUNCTION__
<< " shouldn't run after kernel boot partition is found";
return true;
}
// We only need to look at newly-added block devices. Note that if someone
// is replaying events all existing devices will get "add"ed.
if (uevent.subsystem != "block" || uevent.action != "add") {
return false;
}
// If it's not the partition we care about then move on.
if (uevent.partition_uuid != boot_part_uuid_) {
return false;
}
auto device = GetBlockDeviceInfo(uevent.path);
LOG(INFO) << "Boot device " << device.str << " found via partition UUID";
found_boot_part_uuid_ = true;
boot_devices_.clear();
boot_devices_.insert(device.str);
return true;
}
void DeviceHandler::HandleUevent(const Uevent& uevent) {
if (uevent.action == "add" || uevent.action == "change" || uevent.action == "bind" ||
uevent.action == "online") {
@ -603,17 +702,25 @@ void DeviceHandler::ColdbootDone() {
DeviceHandler::DeviceHandler(std::vector<Permissions> dev_permissions,
std::vector<SysfsPermissions> sysfs_permissions,
std::vector<Subsystem> subsystems, std::set<std::string> boot_devices,
bool skip_restorecon)
std::string boot_part_uuid, bool skip_restorecon)
: dev_permissions_(std::move(dev_permissions)),
sysfs_permissions_(std::move(sysfs_permissions)),
subsystems_(std::move(subsystems)),
boot_devices_(std::move(boot_devices)),
boot_part_uuid_(boot_part_uuid),
skip_restorecon_(skip_restorecon),
sysfs_mount_point_("/sys") {}
sysfs_mount_point_("/sys") {
// If both a boot partition UUID and a list of boot devices are
// specified then we ignore the boot_devices in favor of boot_part_uuid.
if (boot_devices_.size() && !boot_part_uuid.empty()) {
LOG(WARNING) << "Both boot_devices and boot_part_uuid provided; ignoring bootdevices";
boot_devices_.clear();
}
}
DeviceHandler::DeviceHandler()
: DeviceHandler(std::vector<Permissions>{}, std::vector<SysfsPermissions>{},
std::vector<Subsystem>{}, std::set<std::string>{}, false) {}
std::vector<Subsystem>{}, std::set<std::string>{}, "", false) {}
} // namespace init
} // namespace android

View file

@ -116,16 +116,24 @@ class Subsystem {
std::string dir_name_ = "/dev";
};
struct BlockDeviceInfo {
std::string str;
std::string type;
bool is_boot_device;
};
class DeviceHandler : public UeventHandler {
public:
friend class DeviceHandlerTester;
DeviceHandler();
DeviceHandler(std::vector<Permissions> dev_permissions,
std::vector<SysfsPermissions> sysfs_permissions, std::vector<Subsystem> subsystems,
std::set<std::string> boot_devices, bool skip_restorecon);
std::vector<SysfsPermissions> sysfs_permissions,
std::vector<Subsystem> subsystems, std::set<std::string> boot_devices,
std::string boot_part_uuid, bool skip_restorecon);
virtual ~DeviceHandler() = default;
bool CheckUeventForBootPartUuid(const Uevent& uevent);
void HandleUevent(const Uevent& uevent) override;
// `androidboot.partition_map` allows associating a partition name for a raw block device
@ -136,7 +144,12 @@ class DeviceHandler : public UeventHandler {
private:
void ColdbootDone() override;
BlockDeviceInfo GetBlockDeviceInfo(const std::string& uevent_path) const;
bool FindSubsystemDevice(std::string path, std::string* device_path,
const std::set<std::string>& subsystem_paths) const;
bool FindPlatformDevice(std::string path, std::string* platform_device_path) const;
bool FindMmcDevice(std::string path, std::string* mmc_device_path) const;
bool FindScsiDevice(std::string path, std::string* scsi_device_path) const;
std::tuple<mode_t, uid_t, gid_t> GetDevicePermissions(
const std::string& path, const std::vector<std::string>& links) const;
void MakeDevice(const std::string& path, bool block, int major, int minor,
@ -151,6 +164,8 @@ class DeviceHandler : public UeventHandler {
std::vector<SysfsPermissions> sysfs_permissions_;
std::vector<Subsystem> subsystems_;
std::set<std::string> boot_devices_;
std::string boot_part_uuid_;
bool found_boot_part_uuid_;
bool skip_restorecon_;
std::string sysfs_mount_point_;
};

View file

@ -288,6 +288,10 @@ static bool IsMicrodroidStrictBoot() {
}
bool FirstStageMountVBootV2::InitDevices() {
if (!block_dev_init_.InitBootDevicesFromPartUuid()) {
return false;
}
std::set<std::string> devices;
GetSuperDeviceName(&devices);

View file

@ -353,10 +353,25 @@ int ueventd_main(int argc, char** argv) {
auto ueventd_configuration = GetConfiguration();
uevent_handlers.emplace_back(std::make_unique<DeviceHandler>(
UeventListener uevent_listener(ueventd_configuration.uevent_socket_rcvbuf_size);
// Right after making DeviceHandler, replay all events looking for which
// block device has the boot partition. This lets us make symlinks
// for all of the other partitions on the same disk. Note that by the time
// we get here we know that the boot partition has already shown up (if
// we're looking for it) so just regenerating events is enough to know
// we'll see it.
std::unique_ptr<DeviceHandler> device_handler = std::make_unique<DeviceHandler>(
std::move(ueventd_configuration.dev_permissions),
std::move(ueventd_configuration.sysfs_permissions),
std::move(ueventd_configuration.subsystems), android::fs_mgr::GetBootDevices(), true));
std::move(ueventd_configuration.subsystems), android::fs_mgr::GetBootDevices(),
android::fs_mgr::GetBootPartUuid(), true);
uevent_listener.RegenerateUevents([&](const Uevent& uevent) -> ListenerAction {
bool uuid_check_done = device_handler->CheckUeventForBootPartUuid(uevent);
return uuid_check_done ? ListenerAction::kStop : ListenerAction::kContinue;
});
uevent_handlers.emplace_back(std::move(device_handler));
uevent_handlers.emplace_back(std::make_unique<FirmwareHandler>(
std::move(ueventd_configuration.firmware_directories),
std::move(ueventd_configuration.external_firmware_handlers)));
@ -365,8 +380,6 @@ int ueventd_main(int argc, char** argv) {
std::vector<std::string> base_paths = {"/odm/lib/modules", "/vendor/lib/modules"};
uevent_handlers.emplace_back(std::make_unique<ModaliasHandler>(base_paths));
}
UeventListener uevent_listener(ueventd_configuration.uevent_socket_rcvbuf_size);
if (!android::base::GetBoolProperty(kColdBootDoneProp, false)) {
ColdBoot cold_boot(uevent_listener, uevent_handlers,
ueventd_configuration.enable_parallel_restorecon,