BootControlAndroid: Implement using libhardware.
This simply calls into the a boot_control HAL implementation loaded with
libhardware. I've tested it with the implementation located in
system/extras/boot_control_copy.
The only non-trivial routine here is GetPartitionDevice() which I've
hand-tested on a device using the following snippet of code:
string names[] = {"boot", "system", "nope_enoent"};
for (string name : names) {
for (int slot = 0; slot <= 2; slot++) {
string device;
if (!GetPartitionDevice(name, slot, &device))
device = "NOTHERE";
LOG(INFO) << "slot:" << slot << " part:" << name << " -> " << device;
}
}
Change-Id: I1280325bd7cd4cf1cc9a33ef13c2f46f215da6e6
diff --git a/boot_control_android.cc b/boot_control_android.cc
index fa9defd..4bb3b92 100644
--- a/boot_control_android.cc
+++ b/boot_control_android.cc
@@ -16,45 +16,162 @@
#include "update_engine/boot_control_android.h"
+#include <base/logging.h>
+#include <base/files/file_util.h>
+#include <base/strings/string_util.h>
#include <chromeos/make_unique_ptr.h>
+#include <cutils/properties.h>
+#include <fs_mgr.h>
-#include "update_engine/boot_control.h"
+#include "update_engine/utils.h"
using std::string;
+namespace {
+
+// Open the appropriate fstab file and fallback to /fstab.device if
+// that's what's being used.
+static struct fstab* OpenFSTab() {
+ char propbuf[PROPERTY_VALUE_MAX];
+ struct fstab* fstab;
+
+ property_get("ro.hardware", propbuf, "");
+ string fstab_name = string("/fstab.") + propbuf;
+ fstab = fs_mgr_read_fstab(fstab_name.c_str());
+ if (fstab != nullptr)
+ return fstab;
+
+ fstab = fs_mgr_read_fstab("/fstab.device");
+ return fstab;
+}
+
+} // namespace
+
+
namespace chromeos_update_engine {
namespace boot_control {
// Factory defined in boot_control.h.
std::unique_ptr<BootControlInterface> CreateBootControl() {
- return chromeos::make_unique_ptr(new BootControlAndroid());
+ std::unique_ptr<BootControlAndroid> boot_control(new BootControlAndroid());
+ if (!boot_control->Init()) {
+ return nullptr;
+ }
+ return chromeos::make_unique_ptr(boot_control.release());
}
} // namespace boot_control
-// TODO(deymo): Read the values from the libhardware HAL.
+bool BootControlAndroid::Init() {
+ const hw_module_t* hw_module;
+ int ret;
+
+ ret = hw_get_module(BOOT_CONTROL_HARDWARE_MODULE_ID, &hw_module);
+ if (ret != 0) {
+ LOG(ERROR) << "Error loading boot_control HAL implementation.";
+ return false;
+ }
+
+ module_ = reinterpret_cast<boot_control_module_t*>(const_cast<hw_module_t*>(hw_module));
+ module_->init(module_);
+
+ LOG(INFO) << "Loaded boot_control HAL "
+ << "'" << hw_module->name << "' "
+ << "version " << (hw_module->module_api_version>>8) << "."
+ << (hw_module->module_api_version&0xff) << " "
+ << "authored by '" << hw_module->author << "'.";
+ return true;
+}
unsigned int BootControlAndroid::GetNumSlots() const {
- return 2;
+ return module_->getNumberSlots(module_);
}
BootControlInterface::Slot BootControlAndroid::GetCurrentSlot() const {
- return 0;
+ return module_->getCurrentSlot(module_);
}
bool BootControlAndroid::GetPartitionDevice(const string& partition_name,
- unsigned int slot,
+ Slot slot,
string* device) const {
- return false;
+ struct fstab* fstab;
+ struct fstab_rec* record;
+
+ // We can't use fs_mgr to look up |partition_name| because fstab
+ // doesn't list every slot partition (it uses the slotselect option
+ // to mask the suffix).
+ //
+ // We can however assume that there's an entry for the /misc mount
+ // point and use that to get the device file for the misc
+ // partition. This helps us locate the disk that |partition_name|
+ // resides on. From there we'll assume that a by-name scheme is used
+ // so we can just replace the trailing "misc" by the given
+ // |partition_name| and suffix corresponding to |slot|, e.g.
+ //
+ // /dev/block/platform/soc.0/7824900.sdhci/by-name/misc ->
+ // /dev/block/platform/soc.0/7824900.sdhci/by-name/boot_a
+ //
+ // If needed, it's possible to relax the by-name assumption in the
+ // future by trawling /sys/block looking for the appropriate sibling
+ // of misc and then finding an entry in /dev matching the sysfs
+ // entry.
+
+ fstab = OpenFSTab();
+ if (fstab == nullptr) {
+ LOG(ERROR) << "Error opening fstab file.";
+ return false;
+ }
+ record = fs_mgr_get_entry_for_mount_point(fstab, "/misc");
+ if (record == nullptr) {
+ LOG(ERROR) << "Error finding /misc entry in fstab file.";
+ fs_mgr_free_fstab(fstab);
+ return false;
+ }
+
+ base::FilePath misc_device = base::FilePath(record->blk_device);
+ fs_mgr_free_fstab(fstab);
+
+ if (misc_device.BaseName() != base::FilePath("misc")) {
+ LOG(ERROR) << "Device file " << misc_device.value() << " for /misc "
+ << "is not in the expected format.";
+ return false;
+ }
+
+ const char* suffix = module_->getSuffix(module_, slot);
+ if (suffix == nullptr) {
+ LOG(ERROR) << "boot_control impl returned no suffix for slot " << slot;
+ return false;
+ }
+
+ base::FilePath path = misc_device.DirName().Append(partition_name + suffix);
+ if (!base::PathExists(path)) {
+ LOG(ERROR) << "Device file " << path.value() << " does not exist.";
+ return false;
+ }
+
+ *device = path.value();
+ return true;
}
bool BootControlAndroid::IsSlotBootable(Slot slot) const {
- return false;
+ int ret = module_->isSlotBootable(module_, slot);
+ if (ret < 0) {
+ LOG(ERROR) << "Unable to determine if slot " << slot
+ << " is bootable: " << strerror(-ret);
+ return false;
+ }
+ return ret == 1;
}
bool BootControlAndroid::MarkSlotUnbootable(Slot slot) {
- return true;
+ int ret = module_->setSlotAsUnbootable(module_, slot);
+ if (ret < 0) {
+ LOG(ERROR) << "Unable to mark slot " << slot
+ << " as bootable: " << strerror(-ret);
+ return false;
+ }
+ return ret == 0;
}
} // namespace chromeos_update_engine