blob: e7ec758eeb7e6def110917a290a15cc0141cfd61 [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
Kevin Lin8799fd62021-03-25 17:16:38 +080060 if (comp_category_info->enum_value ==
61 runtime_probe::ProbeRequest_SupportCategory_UNKNOWN)
62 continue;
63
Yong Hong85a4dff2019-05-25 04:22:47 +080064 const auto* field_desc =
65 probe_result_desc->FindFieldByName(comp_category_name);
66 DCHECK(field_desc && field_desc->cpp_type() == kCppTypeMsg &&
67 field_desc->is_repeated())
68 << "Field (" << comp_category_name << ") must be a repeated field for "
69 << "the HW components in |runtime_probe::ProbeResult|.";
70 comp_category_info->probe_result_comp_field = field_desc;
71
72 const auto* probe_result_comp_desc = field_desc->message_type();
73 field_desc = probe_result_comp_desc->FindFieldByName("name");
74 DCHECK(field_desc && field_desc->cpp_type() == kCppTypeStr &&
75 field_desc->is_optional())
76 << "Field (" << comp_category_name
77 << ") should contain a string of the name of the component.";
78 comp_category_info->probe_result_comp_name_field = field_desc;
79
80 field_desc = probe_result_comp_desc->FindFieldByName("values");
81 DCHECK(field_desc && field_desc->cpp_type() == kCppTypeMsg &&
82 field_desc->is_optional())
83 << "Field (" << comp_category_name
84 << ") should contain a message field for the component values.";
85 comp_category_info->probe_result_comp_values_field = field_desc;
86
87 field_desc = generic_device_info_desc->FindFieldByName(comp_category_name);
88 if (field_desc) {
89 DCHECK(field_desc->cpp_type() == kCppTypeMsg && field_desc->is_repeated())
90 << "|hardware_verifier::HwVerificationReport_GenericDeviceInfo| "
91 << "should contain a repeated field for the generic ("
92 << comp_category_name << ") components.";
93 } else {
94 VLOG(1) << "(" << comp_category_name << ") field is not found in "
95 << "|hardware_verifier::HwVerificationReport_GenericDeviceInfo|, "
96 << "will ignore the generic component of that category.";
97 }
98 comp_category_info->report_comp_values_field = field_desc;
99 }
100}
101
Yong Hongcc481892019-05-16 20:26:05 +0800102base::Optional<HwVerificationReport> VerifierImpl::Verify(
103 const runtime_probe::ProbeResult& probe_result,
104 const HwVerificationSpec& hw_verification_spec) const {
Stimim Chenacdd9962020-05-11 17:00:27 +0800105 // A dictionary of 'expected_component_category => seen'.
106 std::map<int, bool> seen_comp;
107 // Collect the categories of generic components we found.
108 std::set<int> seen_generic_comp;
109
Yong Hongcc481892019-05-16 20:26:05 +0800110 // A dictionary which maps (component_category, component_uuid) to its
111 // qualification status.
112 std::map<int, std::map<std::string, QualificationStatus>> qual_status_dict;
Yong Hong85a4dff2019-05-25 04:22:47 +0800113 for (const auto& comp_info : hw_verification_spec.component_infos()) {
114 const auto& category = comp_info.component_category();
115 const auto& uuid = comp_info.component_uuid();
116 const auto& qualification_status = comp_info.qualification_status();
Yong Hongcc481892019-05-16 20:26:05 +0800117 const auto& insert_result =
118 qual_status_dict[static_cast<int>(category)].emplace(
119 uuid, qualification_status);
120 if (!insert_result.second) {
121 LOG(ERROR)
122 << "The verification spec contains duplicated component infos.";
123 return base::nullopt;
124 }
Stimim Chenacdd9962020-05-11 17:00:27 +0800125
126 // We expect to see this component in probe result.
127 seen_comp[category] = false;
Yong Hongcc481892019-05-16 20:26:05 +0800128 }
129
Yong Hong85a4dff2019-05-25 04:22:47 +0800130 // A dictionary which maps component_category to the field names in the
Kevin Lindaa73632020-07-20 16:45:56 +0800131 // allowlist.
132 std::map<int, std::set<std::string>> generic_comp_value_allowlists;
Yong Hong85a4dff2019-05-25 04:22:47 +0800133 for (const auto& spec_info :
Kevin Lindaa73632020-07-20 16:45:56 +0800134 hw_verification_spec.generic_component_value_allowlists()) {
135 const auto& insert_result = generic_comp_value_allowlists.emplace(
Yong Hong85a4dff2019-05-25 04:22:47 +0800136 spec_info.component_category(),
137 std::set<std::string>(spec_info.field_names().cbegin(),
138 spec_info.field_names().cend()));
139 if (!insert_result.second) {
Kevin Lindaa73632020-07-20 16:45:56 +0800140 LOG(ERROR) << "Duplicated allowlist tables for category (num="
Yong Hong85a4dff2019-05-25 04:22:47 +0800141 << spec_info.component_category() << ") are detected in the "
142 << "verification spec.";
143 return base::nullopt;
144 }
145 }
146
Yong Hongcc481892019-05-16 20:26:05 +0800147 HwVerificationReport hw_verification_report;
148 hw_verification_report.set_is_compliant(true);
Yong Hong85a4dff2019-05-25 04:22:47 +0800149 auto* generic_device_info =
150 hw_verification_report.mutable_generic_device_info();
151 const auto* generic_device_info_refl = generic_device_info->GetReflection();
Yong Hongcc481892019-05-16 20:26:05 +0800152
Yong Hongcc481892019-05-16 20:26:05 +0800153 const auto* probe_result_refl = probe_result.GetReflection();
Yong Hong85a4dff2019-05-25 04:22:47 +0800154 for (const auto& comp_category_info : comp_category_infos_) {
Kevin Lin8799fd62021-03-25 17:16:38 +0800155 if (comp_category_info.enum_value ==
156 runtime_probe::ProbeRequest_SupportCategory_UNKNOWN)
157 continue;
Yong Hong85a4dff2019-05-25 04:22:47 +0800158 const auto& comp_name_to_qual_status =
159 qual_status_dict[comp_category_info.enum_value];
Yong Hongcc481892019-05-16 20:26:05 +0800160
Kevin Lindaa73632020-07-20 16:45:56 +0800161 // the default allowlist is empty.
162 const auto& generic_comp_value_allowlist =
163 generic_comp_value_allowlists[comp_category_info.enum_value];
Yong Hongcc481892019-05-16 20:26:05 +0800164
Yong Hong85a4dff2019-05-25 04:22:47 +0800165 const auto& num_comps = probe_result_refl->FieldSize(
166 probe_result, comp_category_info.probe_result_comp_field);
167 for (int i = 0; i < num_comps; ++i) {
168 const auto& comp = probe_result_refl->GetRepeatedMessage(
169 probe_result, comp_category_info.probe_result_comp_field, i);
Yong Hongcc481892019-05-16 20:26:05 +0800170 const auto* comp_refl = comp.GetReflection();
Yong Hong85a4dff2019-05-25 04:22:47 +0800171 const auto& comp_name = comp_refl->GetString(
172 comp, comp_category_info.probe_result_comp_name_field);
Yong Hongcc481892019-05-16 20:26:05 +0800173
Yong Hong85a4dff2019-05-25 04:22:47 +0800174 // If the component name is "generic", add it to |generic_device_info|
175 // in the report.
176 if (comp_name == kGenericComponentName) {
Stimim Chenacdd9962020-05-11 17:00:27 +0800177 seen_generic_comp.insert(comp_category_info.enum_value);
Yong Hong85a4dff2019-05-25 04:22:47 +0800178 if (!comp_category_info.report_comp_values_field) {
179 VLOG(1) << "Ignore the generic component of ("
180 << comp_category_info.enum_name << ") category.";
181 } else {
182 // Duplicate the original values and filter the fields by the
Kevin Lindaa73632020-07-20 16:45:56 +0800183 // allowlist.
Yong Hong85a4dff2019-05-25 04:22:47 +0800184 auto* dup_comp_values = generic_device_info_refl->AddMessage(
185 generic_device_info, comp_category_info.report_comp_values_field);
186 dup_comp_values->CopyFrom(comp_refl->GetMessage(
187 comp, comp_category_info.probe_result_comp_values_field));
188
189 const auto* dup_comp_values_refl = dup_comp_values->GetReflection();
190 const auto* dup_comp_values_desc = dup_comp_values->GetDescriptor();
191 for (int j = 0; j < dup_comp_values_desc->field_count(); ++j) {
192 const auto* field = dup_comp_values_desc->field(j);
Kevin Lindaa73632020-07-20 16:45:56 +0800193 if (!generic_comp_value_allowlist.count(field->name())) {
Yong Hong85a4dff2019-05-25 04:22:47 +0800194 dup_comp_values_refl->ClearField(dup_comp_values, field);
195 }
196 }
197 }
198 continue;
Yong Hongcc481892019-05-16 20:26:05 +0800199 }
Yong Hong85a4dff2019-05-25 04:22:47 +0800200
201 // If the component name is not "generic", do the regular qualification
202 // status check.
Yong Hongcc481892019-05-16 20:26:05 +0800203 const auto& qual_status_it = comp_name_to_qual_status.find(comp_name);
204 if (qual_status_it == comp_name_to_qual_status.end()) {
205 LOG(ERROR) << "The probe result contains unregonizable components "
Yong Hong85a4dff2019-05-25 04:22:47 +0800206 << "(category=" << comp_category_info.enum_name
207 << ", uuid=" << comp_name << ").";
Yong Hongcc481892019-05-16 20:26:05 +0800208 return base::nullopt;
209 }
Stimim Chenacdd9962020-05-11 17:00:27 +0800210 // TODO(b147654337): How about components that are "missing", that is:
211 // - It is expected on the system (according to SKU or MODEL).
212 // - We cannot find this in generic nor non-generic components.
213 AddFoundComponentInfo(
214 &hw_verification_report,
Yong Hongcc481892019-05-16 20:26:05 +0800215 static_cast<runtime_probe::ProbeRequest_SupportCategory>(
Stimim Chenacdd9962020-05-11 17:00:27 +0800216 comp_category_info.enum_value),
217 comp_name, qual_status_it->second);
218 seen_comp[comp_category_info.enum_value] = true;
219 }
220 }
221
222 for (const auto& it : seen_comp) {
223 // We have found a generic component in this category, but this doesn't have
224 // any qualification status.
225 if (!it.second && seen_generic_comp.count(it.first)) {
226 AddFoundComponentInfo(
227 &hw_verification_report,
228 static_cast<runtime_probe::ProbeRequest_SupportCategory>(it.first),
229 kNoMatchComponentName, QualificationStatus::NO_MATCH);
Yong Hongcc481892019-05-16 20:26:05 +0800230 }
231 }
232
233 // TODO(yhong): Implement the SKU specific checks.
234 return hw_verification_report;
235}
236
237} // namespace hardware_verifier