diff --git a/init/README.ueventd.md b/init/README.ueventd.md index 7d00195eb..aac4acb6a 100644 --- a/init/README.ueventd.md +++ b/init/README.ueventd.md @@ -39,6 +39,33 @@ for the node path: `device_id` is `uevent MINOR % 128 + 1`. 3. All other devices are created as `/dev/` +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 diff --git a/init/block_dev_initializer.cpp b/init/block_dev_initializer.cpp index 8f5215856..4ca5b8f0e 100644 --- a/init/block_dev_initializer.cpp +++ b/init/block_dev_initializer.cpp @@ -33,7 +33,49 @@ BlockDevInitializer::BlockDevInitializer() : uevent_listener_(16 * 1024 * 1024) auto boot_devices = android::fs_mgr::GetBootDevices(); device_handler_ = std::make_unique( std::vector{}, std::vector{}, std::vector{}, - 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() { diff --git a/init/block_dev_initializer.h b/init/block_dev_initializer.h index cb1d36555..25107c97f 100644 --- a/init/block_dev_initializer.h +++ b/init/block_dev_initializer.h @@ -29,6 +29,7 @@ class BlockDevInitializer final { public: BlockDevInitializer(); + bool InitBootDevicesFromPartUuid(); bool InitDeviceMapper(); bool InitDmUser(const std::string& name); bool InitDevices(std::set devices); diff --git a/init/devices.cpp b/init/devices.cpp index 501657ac6..0b1e13dba 100644 --- a/init/devices.cpp +++ b/init/devices.cpp @@ -192,7 +192,22 @@ void SysfsPermissions::SetPermissions(const std::string& path) const { BlockDeviceInfo DeviceHandler::GetBlockDeviceInfo(const std::string& uevent_path) const { BlockDeviceInfo info; - if (FindPlatformDevice(uevent_path, &info.str)) { + 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"; @@ -290,6 +305,22 @@ bool DeviceHandler::FindPlatformDevice(std::string path, std::string* platform_d return FindSubsystemDevice(path, platform_device_path, subsystem_paths); } +bool DeviceHandler::FindMmcDevice(std::string path, std::string* mmc_device_path) const { + const std::set 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 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 @@ -567,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") { @@ -629,17 +702,25 @@ void DeviceHandler::ColdbootDone() { DeviceHandler::DeviceHandler(std::vector dev_permissions, std::vector sysfs_permissions, std::vector subsystems, std::set 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{}, std::vector{}, - std::vector{}, std::set{}, false) {} + std::vector{}, std::set{}, "", false) {} } // namespace init } // namespace android diff --git a/init/devices.h b/init/devices.h index 7eee87a12..cac52bc17 100644 --- a/init/devices.h +++ b/init/devices.h @@ -128,10 +128,12 @@ class DeviceHandler : public UeventHandler { DeviceHandler(); DeviceHandler(std::vector dev_permissions, - std::vector sysfs_permissions, std::vector subsystems, - std::set boot_devices, bool skip_restorecon); + std::vector sysfs_permissions, + std::vector subsystems, std::set 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 @@ -146,6 +148,8 @@ class DeviceHandler : public UeventHandler { bool FindSubsystemDevice(std::string path, std::string* device_path, const std::set& 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 GetDevicePermissions( const std::string& path, const std::vector& links) const; void MakeDevice(const std::string& path, bool block, int major, int minor, @@ -160,6 +164,8 @@ class DeviceHandler : public UeventHandler { std::vector sysfs_permissions_; std::vector subsystems_; std::set boot_devices_; + std::string boot_part_uuid_; + bool found_boot_part_uuid_; bool skip_restorecon_; std::string sysfs_mount_point_; }; diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp index 4f1af3038..aa6b55166 100644 --- a/init/first_stage_mount.cpp +++ b/init/first_stage_mount.cpp @@ -288,6 +288,10 @@ static bool IsMicrodroidStrictBoot() { } bool FirstStageMountVBootV2::InitDevices() { + if (!block_dev_init_.InitBootDevicesFromPartUuid()) { + return false; + } + std::set devices; GetSuperDeviceName(&devices); diff --git a/init/ueventd.cpp b/init/ueventd.cpp index 3f0d0e95b..286e47266 100644 --- a/init/ueventd.cpp +++ b/init/ueventd.cpp @@ -353,10 +353,25 @@ int ueventd_main(int argc, char** argv) { auto ueventd_configuration = GetConfiguration(); - uevent_handlers.emplace_back(std::make_unique( + 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 device_handler = std::make_unique( 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( 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 base_paths = {"/odm/lib/modules", "/vendor/lib/modules"}; uevent_handlers.emplace_back(std::make_unique(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,