blob: 03f57619bb0ccbe1b903c5d3082ad11b1bc29558 [file] [log] [blame]
Yong Hongcc481892019-05-16 20:26:05 +08001/* Copyright 2019 The Chromium OS Authors. All rights reserved.
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 */
5
6#include "hardware_verifier/verifier_impl.h"
7
8#include <map>
Yong Hong85a4dff2019-05-25 04:22:47 +08009#include <set>
Yong Hongcc481892019-05-16 20:26:05 +080010#include <string>
11#include <utility>
12
Qijiang Fan713061e2021-03-08 15:45:12 +090013#include <base/check.h>
Yong Hongcc481892019-05-16 20:26:05 +080014#include <base/logging.h>
15#include <base/optional.h>
16
17namespace hardware_verifier {
18
Yong Hong85a4dff2019-05-25 04:22:47 +080019namespace {
20
21constexpr auto kGenericComponentName = "generic";
Stimim Chenacdd9962020-05-11 17:00:27 +080022constexpr auto kNoMatchComponentName = "NO_MATCH";
23
24void AddFoundComponentInfo(
25 HwVerificationReport* hw_verification_report,
26 const runtime_probe::ProbeRequest_SupportCategory& component_category,
27 const std::string& comp_name,
28 const QualificationStatus status) {
29 auto* found_comp_info = hw_verification_report->add_found_component_infos();
30 found_comp_info->set_component_category(component_category);
31 found_comp_info->set_component_uuid(comp_name);
32 found_comp_info->set_qualification_status(status);
33 if (status != QualificationStatus::QUALIFIED) {
34 hw_verification_report->set_is_compliant(false);
35 }
36}
Yong Hong85a4dff2019-05-25 04:22:47 +080037
38} // namespace
39
40VerifierImpl::VerifierImpl() {
41 using CppType = google::protobuf::FieldDescriptor::CppType;
42 constexpr int kCppTypeMsg = CppType::CPPTYPE_MESSAGE;
43 constexpr int kCppTypeStr = CppType::CPPTYPE_STRING;
44
45 // Resolve |comp_category_infos_| in the constructor.
46 const auto* category_enum_desc =
47 runtime_probe::ProbeRequest_SupportCategory_descriptor();
48 comp_category_infos_.resize(category_enum_desc->value_count());
49
50 const auto* probe_result_desc = runtime_probe::ProbeResult::descriptor();
51 const auto* generic_device_info_desc =
52 HwVerificationReport_GenericDeviceInfo::descriptor();
53 for (int i = 0; i < category_enum_desc->value_count(); ++i) {
54 auto* comp_category_info = &comp_category_infos_[i];
55 const auto& comp_category_name = category_enum_desc->value(i)->name();
56
57 comp_category_info->enum_value = category_enum_desc->value(i)->number();
58 comp_category_info->enum_name = comp_category_name;
59
60 const auto* field_desc =
61 probe_result_desc->FindFieldByName(comp_category_name);
62 DCHECK(field_desc && field_desc->cpp_type() == kCppTypeMsg &&
63 field_desc->is_repeated())
64 << "Field (" << comp_category_name << ") must be a repeated field for "
65 << "the HW components in |runtime_probe::ProbeResult|.";
66 comp_category_info->probe_result_comp_field = field_desc;
67
68 const auto* probe_result_comp_desc = field_desc->message_type();
69 field_desc = probe_result_comp_desc->FindFieldByName("name");
70 DCHECK(field_desc && field_desc->cpp_type() == kCppTypeStr &&
71 field_desc->is_optional())
72 << "Field (" << comp_category_name
73 << ") should contain a string of the name of the component.";
74 comp_category_info->probe_result_comp_name_field = field_desc;
75
76 field_desc = probe_result_comp_desc->FindFieldByName("values");
77 DCHECK(field_desc && field_desc->cpp_type() == kCppTypeMsg &&
78 field_desc->is_optional())
79 << "Field (" << comp_category_name
80 << ") should contain a message field for the component values.";
81 comp_category_info->probe_result_comp_values_field = field_desc;
82
83 field_desc = generic_device_info_desc->FindFieldByName(comp_category_name);
84 if (field_desc) {
85 DCHECK(field_desc->cpp_type() == kCppTypeMsg && field_desc->is_repeated())
86 << "|hardware_verifier::HwVerificationReport_GenericDeviceInfo| "
87 << "should contain a repeated field for the generic ("
88 << comp_category_name << ") components.";
89 } else {
90 VLOG(1) << "(" << comp_category_name << ") field is not found in "
91 << "|hardware_verifier::HwVerificationReport_GenericDeviceInfo|, "
92 << "will ignore the generic component of that category.";
93 }
94 comp_category_info->report_comp_values_field = field_desc;
95 }
96}
97
Yong Hongcc481892019-05-16 20:26:05 +080098base::Optional<HwVerificationReport> VerifierImpl::Verify(
99 const runtime_probe::ProbeResult& probe_result,
100 const HwVerificationSpec& hw_verification_spec) const {
Stimim Chenacdd9962020-05-11 17:00:27 +0800101 // A dictionary of 'expected_component_category => seen'.
102 std::map<int, bool> seen_comp;
103 // Collect the categories of generic components we found.
104 std::set<int> seen_generic_comp;
105
Yong Hongcc481892019-05-16 20:26:05 +0800106 // A dictionary which maps (component_category, component_uuid) to its
107 // qualification status.
108 std::map<int, std::map<std::string, QualificationStatus>> qual_status_dict;
Yong Hong85a4dff2019-05-25 04:22:47 +0800109 for (const auto& comp_info : hw_verification_spec.component_infos()) {
110 const auto& category = comp_info.component_category();
111 const auto& uuid = comp_info.component_uuid();
112 const auto& qualification_status = comp_info.qualification_status();
Yong Hongcc481892019-05-16 20:26:05 +0800113 const auto& insert_result =
114 qual_status_dict[static_cast<int>(category)].emplace(
115 uuid, qualification_status);
116 if (!insert_result.second) {
117 LOG(ERROR)
118 << "The verification spec contains duplicated component infos.";
119 return base::nullopt;
120 }
Stimim Chenacdd9962020-05-11 17:00:27 +0800121
122 // We expect to see this component in probe result.
123 seen_comp[category] = false;
Yong Hongcc481892019-05-16 20:26:05 +0800124 }
125
Yong Hong85a4dff2019-05-25 04:22:47 +0800126 // A dictionary which maps component_category to the field names in the
Kevin Lindaa73632020-07-20 16:45:56 +0800127 // allowlist.
128 std::map<int, std::set<std::string>> generic_comp_value_allowlists;
Yong Hong85a4dff2019-05-25 04:22:47 +0800129 for (const auto& spec_info :
Kevin Lindaa73632020-07-20 16:45:56 +0800130 hw_verification_spec.generic_component_value_allowlists()) {
131 const auto& insert_result = generic_comp_value_allowlists.emplace(
Yong Hong85a4dff2019-05-25 04:22:47 +0800132 spec_info.component_category(),
133 std::set<std::string>(spec_info.field_names().cbegin(),
134 spec_info.field_names().cend()));
135 if (!insert_result.second) {
Kevin Lindaa73632020-07-20 16:45:56 +0800136 LOG(ERROR) << "Duplicated allowlist tables for category (num="
Yong Hong85a4dff2019-05-25 04:22:47 +0800137 << spec_info.component_category() << ") are detected in the "
138 << "verification spec.";
139 return base::nullopt;
140 }
141 }
142
Yong Hongcc481892019-05-16 20:26:05 +0800143 HwVerificationReport hw_verification_report;
144 hw_verification_report.set_is_compliant(true);
Yong Hong85a4dff2019-05-25 04:22:47 +0800145 auto* generic_device_info =
146 hw_verification_report.mutable_generic_device_info();
147 const auto* generic_device_info_refl = generic_device_info->GetReflection();
Yong Hongcc481892019-05-16 20:26:05 +0800148
Yong Hongcc481892019-05-16 20:26:05 +0800149 const auto* probe_result_refl = probe_result.GetReflection();
Yong Hong85a4dff2019-05-25 04:22:47 +0800150 for (const auto& comp_category_info : comp_category_infos_) {
151 const auto& comp_name_to_qual_status =
152 qual_status_dict[comp_category_info.enum_value];
Yong Hongcc481892019-05-16 20:26:05 +0800153
Kevin Lindaa73632020-07-20 16:45:56 +0800154 // the default allowlist is empty.
155 const auto& generic_comp_value_allowlist =
156 generic_comp_value_allowlists[comp_category_info.enum_value];
Yong Hongcc481892019-05-16 20:26:05 +0800157
Yong Hong85a4dff2019-05-25 04:22:47 +0800158 const auto& num_comps = probe_result_refl->FieldSize(
159 probe_result, comp_category_info.probe_result_comp_field);
160 for (int i = 0; i < num_comps; ++i) {
161 const auto& comp = probe_result_refl->GetRepeatedMessage(
162 probe_result, comp_category_info.probe_result_comp_field, i);
Yong Hongcc481892019-05-16 20:26:05 +0800163 const auto* comp_refl = comp.GetReflection();
Yong Hong85a4dff2019-05-25 04:22:47 +0800164 const auto& comp_name = comp_refl->GetString(
165 comp, comp_category_info.probe_result_comp_name_field);
Yong Hongcc481892019-05-16 20:26:05 +0800166
Yong Hong85a4dff2019-05-25 04:22:47 +0800167 // If the component name is "generic", add it to |generic_device_info|
168 // in the report.
169 if (comp_name == kGenericComponentName) {
Stimim Chenacdd9962020-05-11 17:00:27 +0800170 seen_generic_comp.insert(comp_category_info.enum_value);
Yong Hong85a4dff2019-05-25 04:22:47 +0800171 if (!comp_category_info.report_comp_values_field) {
172 VLOG(1) << "Ignore the generic component of ("
173 << comp_category_info.enum_name << ") category.";
174 } else {
175 // Duplicate the original values and filter the fields by the
Kevin Lindaa73632020-07-20 16:45:56 +0800176 // allowlist.
Yong Hong85a4dff2019-05-25 04:22:47 +0800177 auto* dup_comp_values = generic_device_info_refl->AddMessage(
178 generic_device_info, comp_category_info.report_comp_values_field);
179 dup_comp_values->CopyFrom(comp_refl->GetMessage(
180 comp, comp_category_info.probe_result_comp_values_field));
181
182 const auto* dup_comp_values_refl = dup_comp_values->GetReflection();
183 const auto* dup_comp_values_desc = dup_comp_values->GetDescriptor();
184 for (int j = 0; j < dup_comp_values_desc->field_count(); ++j) {
185 const auto* field = dup_comp_values_desc->field(j);
Kevin Lindaa73632020-07-20 16:45:56 +0800186 if (!generic_comp_value_allowlist.count(field->name())) {
Yong Hong85a4dff2019-05-25 04:22:47 +0800187 dup_comp_values_refl->ClearField(dup_comp_values, field);
188 }
189 }
190 }
191 continue;
Yong Hongcc481892019-05-16 20:26:05 +0800192 }
Yong Hong85a4dff2019-05-25 04:22:47 +0800193
194 // If the component name is not "generic", do the regular qualification
195 // status check.
Yong Hongcc481892019-05-16 20:26:05 +0800196 const auto& qual_status_it = comp_name_to_qual_status.find(comp_name);
197 if (qual_status_it == comp_name_to_qual_status.end()) {
198 LOG(ERROR) << "The probe result contains unregonizable components "
Yong Hong85a4dff2019-05-25 04:22:47 +0800199 << "(category=" << comp_category_info.enum_name
200 << ", uuid=" << comp_name << ").";
Yong Hongcc481892019-05-16 20:26:05 +0800201 return base::nullopt;
202 }
Stimim Chenacdd9962020-05-11 17:00:27 +0800203 // TODO(b147654337): How about components that are "missing", that is:
204 // - It is expected on the system (according to SKU or MODEL).
205 // - We cannot find this in generic nor non-generic components.
206 AddFoundComponentInfo(
207 &hw_verification_report,
Yong Hongcc481892019-05-16 20:26:05 +0800208 static_cast<runtime_probe::ProbeRequest_SupportCategory>(
Stimim Chenacdd9962020-05-11 17:00:27 +0800209 comp_category_info.enum_value),
210 comp_name, qual_status_it->second);
211 seen_comp[comp_category_info.enum_value] = true;
212 }
213 }
214
215 for (const auto& it : seen_comp) {
216 // We have found a generic component in this category, but this doesn't have
217 // any qualification status.
218 if (!it.second && seen_generic_comp.count(it.first)) {
219 AddFoundComponentInfo(
220 &hw_verification_report,
221 static_cast<runtime_probe::ProbeRequest_SupportCategory>(it.first),
222 kNoMatchComponentName, QualificationStatus::NO_MATCH);
Yong Hongcc481892019-05-16 20:26:05 +0800223 }
224 }
225
226 // TODO(yhong): Implement the SKU specific checks.
227 return hw_verification_report;
228}
229
230} // namespace hardware_verifier