blob: 7b6400fb49f3d9b7b1fe895a87b17af7088d5bd9 [file] [log] [blame]
// Copyright 2019 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "modemfwd/firmware_manifest.h"
#include <utility>
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
#include <brillo/proto_file_io.h>
#include "modemfwd/firmware_directory.h"
#include "modemfwd/proto_bindings/firmware_manifest_v2.pb.h"
namespace modemfwd {
namespace {
bool ParseDevice(const Device& device,
const base::FilePath& directory_path,
DeviceFirmwareCache* out_cache) {
// Sort main firmware entries by version. Ensure the versions are
// all separate.
std::map<std::string, std::unique_ptr<FirmwareFileInfo>> main_firmware_infos;
for (const MainFirmwareV2& main_firmware : device.main_firmware()) {
if (main_firmware.filename().empty() || main_firmware.version().empty() ||
!Compression_IsValid(main_firmware.compression())) {
LOG(ERROR) << "Found malformed main firmware manifest entry";
return false;
}
if (main_firmware_infos.count(main_firmware.version()) > 0) {
LOG(ERROR) << "Found multiple main firmware with the same version for "
<< "device " << device.device_id()
<< (device.variant().empty()
? ""
: "(variant " + device.variant() + ")");
return false;
}
auto compression =
ToFirmwareFileInfoCompression(main_firmware.compression());
if (!compression.has_value())
return false;
if (base::FilePath(main_firmware.filename()).IsAbsolute()) {
LOG(ERROR) << "Main firmware should use relative path ("
<< main_firmware.filename() << ").";
return false;
}
main_firmware_infos.emplace(
main_firmware.version(),
std::make_unique<FirmwareFileInfo>(
directory_path.Append(main_firmware.filename()),
main_firmware.version(), compression.value()));
}
// Main firmware is default for a device if:
// * It is explicitly specified as default in the Device entry.
// * It is the only main firmware.
FirmwareFileInfo* default_main_entry = nullptr;
if (!device.default_main_firmware_version().empty()) {
if (main_firmware_infos.count(device.default_main_firmware_version()) ==
0) {
LOG(ERROR) << "Firmware manifest specified invalid default main firmware "
"version";
return false;
}
default_main_entry =
main_firmware_infos[device.default_main_firmware_version()].get();
} else if (main_firmware_infos.size() == 1) {
default_main_entry = main_firmware_infos.begin()->second.get();
}
std::map<std::string, FirmwareFileInfo*> oem_firmware_infos;
for (const OemFirmwareV2& oem_firmware : device.oem_firmware()) {
if (oem_firmware.filename().empty() || oem_firmware.version().empty() ||
!Compression_IsValid(oem_firmware.compression())) {
LOG(ERROR) << "Found malformed OEM firmware manifest entry";
return false;
}
auto compression =
ToFirmwareFileInfoCompression(oem_firmware.compression());
if (!compression.has_value())
return false;
if (base::FilePath(oem_firmware.filename()).IsAbsolute()) {
LOG(ERROR) << "OEM firmware should use relative path ("
<< oem_firmware.filename() << ").";
return false;
}
auto oem_info = std::make_unique<FirmwareFileInfo>(
directory_path.Append(oem_firmware.filename()), oem_firmware.version(),
compression.value());
if (oem_firmware.main_firmware_version_size() > 0) {
for (const std::string& version : oem_firmware.main_firmware_version())
oem_firmware_infos.emplace(version, oem_info.get());
} else {
if (default_main_entry)
oem_firmware_infos.emplace(default_main_entry->version, oem_info.get());
}
out_cache->all_files.push_back(std::move(oem_info));
}
// If not, then each carrier firmware must specify a functional main firmware
// version, and there must be a generic carrier firmware supplying the main
// version if no explicitly supported carrier is found.
for (const CarrierFirmwareV2& carrier_firmware : device.carrier_firmware()) {
if (carrier_firmware.filename().empty() ||
carrier_firmware.version().empty() ||
carrier_firmware.carrier_id().empty() ||
!Compression_IsValid(carrier_firmware.compression())) {
LOG(ERROR) << "Found malformed carrier firmware manifest entry";
return false;
}
// Convert the manifest entry into a FirmwareFileInfo.
auto compression =
ToFirmwareFileInfoCompression(carrier_firmware.compression());
if (!compression.has_value())
return false;
// There must either be a default main firmware or an explicitly specified
// one here.
FirmwareFileInfo* main_firmware_for_carrier;
FirmwareFileInfo* oem_firmware_for_carrier;
if (!carrier_firmware.main_firmware_version().empty()) {
if (main_firmware_infos.count(carrier_firmware.main_firmware_version()) ==
0) {
LOG(ERROR)
<< "Manifest specified invalid default main firmware version";
return false;
}
main_firmware_for_carrier =
main_firmware_infos[carrier_firmware.main_firmware_version()].get();
oem_firmware_for_carrier =
oem_firmware_infos[carrier_firmware.main_firmware_version()];
} else if (default_main_entry) {
main_firmware_for_carrier = default_main_entry;
oem_firmware_for_carrier =
oem_firmware_infos[default_main_entry->version];
} else {
LOG(ERROR) << "No main firmware specified for carrier firmware "
<< carrier_firmware.filename();
return false;
}
if (base::FilePath(carrier_firmware.filename()).IsAbsolute()) {
LOG(ERROR) << "Carrier firmware should use relative path ("
<< carrier_firmware.filename() << ").";
return false;
}
auto carrier_info = std::make_unique<FirmwareFileInfo>(
directory_path.Append(carrier_firmware.filename()),
carrier_firmware.version(), compression.value());
// Add the firmware to the cache under the carrier ID for this entry.
for (const std::string& supported_carrier : carrier_firmware.carrier_id()) {
if (out_cache->carrier_firmware.count(supported_carrier) > 0) {
LOG(ERROR) << "Duplicate carrier firmware entry for carrier "
<< supported_carrier;
// We haven't inserted main firmware into the mapping yet. Clear all
// the indices to prevent poorly-behaved users from getting dangling
// pointers.
out_cache->main_firmware.clear();
out_cache->carrier_firmware.clear();
out_cache->oem_firmware.clear();
return false;
}
out_cache->main_firmware[supported_carrier] = main_firmware_for_carrier;
if (oem_firmware_for_carrier)
out_cache->oem_firmware[supported_carrier] = oem_firmware_for_carrier;
out_cache->carrier_firmware[supported_carrier] = carrier_info.get();
}
out_cache->all_files.push_back(std::move(carrier_info));
}
// Now it's safe to move all of the main firmware file info pointers.
for (auto& main_info : main_firmware_infos)
out_cache->all_files.push_back(std::move(main_info.second));
// If we have a default entry but didn't see any generic carrier firmware,
// we put the default main firmware in the main index under generic.
if (out_cache->main_firmware.count(FirmwareDirectory::kGenericCarrierId) ==
0) {
if (!default_main_entry) {
LOG(ERROR) << "Manifest did not supply generic main firmware";
return false;
}
out_cache->main_firmware[FirmwareDirectory::kGenericCarrierId] =
default_main_entry;
auto oem_info = oem_firmware_infos.find(default_main_entry->version);
if (oem_info != oem_firmware_infos.end() && oem_info->second)
out_cache->oem_firmware[FirmwareDirectory::kGenericCarrierId] =
oem_info->second;
}
return true;
}
} // namespace
bool ParseFirmwareManifestV2(const base::FilePath& manifest,
FirmwareIndex* index) {
FirmwareManifestV2 manifest_proto;
if (!brillo::ReadTextProtobuf(manifest, &manifest_proto))
return false;
base::FilePath directory = manifest.DirName();
for (const Device& device : manifest_proto.device()) {
if (device.device_id().empty()) {
LOG(ERROR) << "Empty device ID in device entry";
return false;
}
DeviceType type{device.device_id(), device.variant()};
if (index->count(type) > 0) {
LOG(ERROR) << "Duplicate device entry in manifest";
return false;
}
DeviceFirmwareCache cache;
if (!ParseDevice(device, directory, &cache))
return false;
(*index)[type] = std::move(cache);
}
return true;
}
} // namespace modemfwd