1 /*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include "KernelInfo.h"
17
18 #include "parse_string.h"
19 #include "parse_xml.h"
20 #include "utils.h"
21
22 namespace android {
23 namespace vintf {
24
25 extern XmlConverter<KernelInfo>& gKernelInfoConverter;
26
27 using details::mergeField;
28
version() const29 const KernelVersion& KernelInfo::version() const {
30 return mVersion;
31 }
32
configs() const33 const std::map<std::string, std::string>& KernelInfo::configs() const {
34 return mConfigs;
35 }
36
level() const37 Level KernelInfo::level() const {
38 return mLevel;
39 }
40
matchKernelConfigs(const std::vector<KernelConfig> & matrixConfigs,std::string * error) const41 bool KernelInfo::matchKernelConfigs(const std::vector<KernelConfig>& matrixConfigs,
42 std::string* error) const {
43 for (const KernelConfig& matrixConfig : matrixConfigs) {
44 const std::string& key = matrixConfig.first;
45 auto it = this->mConfigs.find(key);
46 if (it == this->mConfigs.end()) {
47 // special case: <value type="tristate">n</value> matches if the config doesn't exist.
48 if (matrixConfig.second == KernelConfigTypedValue::gMissingConfig) {
49 continue;
50 }
51 if (error != nullptr) {
52 *error = "Missing config " + key;
53 }
54 return false;
55 }
56 const std::string& kernelValue = it->second;
57 if (!matrixConfig.second.matchValue(kernelValue)) {
58 if (error != nullptr) {
59 *error = "For config " + key + ", value = " + kernelValue + " but required " +
60 to_string(matrixConfig.second);
61 }
62 return false;
63 }
64 }
65 return true;
66 }
67
matchKernelVersion(const KernelVersion & minLts) const68 bool KernelInfo::matchKernelVersion(const KernelVersion& minLts) const {
69 return mVersion.dropMinor() == minLts.dropMinor() && minLts.minorRev <= mVersion.minorRev;
70 }
71
getMatchedKernelRequirements(const std::vector<MatrixKernel> & kernels,Level kernelLevel,std::string * error) const72 std::vector<const MatrixKernel*> KernelInfo::getMatchedKernelRequirements(
73 const std::vector<MatrixKernel>& kernels, Level kernelLevel, std::string* error) const {
74 std::map<Level, std::vector<const MatrixKernel*>> kernelsForLevel;
75 for (const MatrixKernel& matrixKernel : kernels) {
76 const auto& minLts = matrixKernel.minLts();
77 auto matrixKernelLevel = matrixKernel.getSourceMatrixLevel();
78
79 // Filter out kernels with different x.y.
80 if (mVersion.dropMinor() != minLts.dropMinor()) {
81 continue;
82 }
83
84 // Check matrix kernel level
85
86 // Use legacy behavior when kernel FCM version is not specified. Blindly add all of them
87 // here. The correct one (with smallest matrixKernelLevel) will be picked later.
88 if (kernelLevel == Level::UNSPECIFIED) {
89 kernelsForLevel[matrixKernelLevel].push_back(&matrixKernel);
90 continue;
91 }
92
93 if (matrixKernelLevel == Level::UNSPECIFIED) {
94 if (error) {
95 *error = "Seen unspecified source matrix level; this should not happen.";
96 }
97 return {};
98 }
99
100 if (matrixKernelLevel < kernelLevel) {
101 continue;
102 }
103
104 // matrix level >= kernel level
105 kernelsForLevel[matrixKernelLevel].push_back(&matrixKernel);
106 }
107
108 if (kernelsForLevel.empty()) {
109 if (error) {
110 std::stringstream ss;
111 ss << "No kernel entry found for kernel version " << mVersion.dropMinor()
112 << " at kernel FCM version "
113 << (kernelLevel == Level::UNSPECIFIED ? "unspecified" : to_string(kernelLevel))
114 << ". The following kernel requirements are checked:";
115 for (const MatrixKernel& matrixKernel : kernels) {
116 ss << "\n Minimum LTS: " << matrixKernel.minLts()
117 << ", kernel FCM version: " << matrixKernel.getSourceMatrixLevel()
118 << (matrixKernel.conditions().empty() ? "" : ", with conditionals");
119 };
120 *error = ss.str();
121 }
122 return {};
123 }
124
125 // At this point, kernelsForLevel contains kernel requirements for each level.
126 // For example, if the running kernel version is 4.14.y then kernelsForLevel contains
127 // 4.14-p, 4.14-q, 4.14-r.
128 // (This excludes kernels < kernel FCM version, or device FCM version if kernel FCM version is
129 // empty. For example, if device level = Q and kernel level is unspecified, this list only
130 // contains 4.14-q and 4.14-r).
131
132 // Use legacy behavior when kernel FCM version is not specified. e.g. target FCM version 3 (P)
133 // matches kernel 4.4-p, 4.9-p, 4.14-p, 4.19-q, etc., but not 4.9-q or 4.14-q.
134 // Since we already filtered |kernels| based on kernel version, we only need to check the first
135 // item in kernelsForLevel.
136 // Note that this excludes *-r and above kernels. Devices with target FCM version >= 5 (R) must
137 // state kernel FCM version explicitly in the device manifest. The value is automatically
138 // inserted for devices with target FCM version >= 5 when manifest is built with assemble_vintf.
139 if (kernelLevel == Level::UNSPECIFIED) {
140 auto [matrixKernelLevel, matrixKernels] = *kernelsForLevel.begin();
141
142 // Do not allow *-r and above kernels.
143 if (matrixKernelLevel != Level::UNSPECIFIED && matrixKernelLevel >= Level::R) {
144 if (error) {
145 KernelInfo msg;
146 msg.mLevel = Level::R;
147 *error = "Kernel FCM version is not specified, but kernel version " +
148 to_string(mVersion) +
149 " is found. Fix by specifying kernel FCM version in device manifest. "
150 "For example, for a *-r kernel:\n" +
151 gKernelInfoConverter(msg);
152 }
153 return {};
154 }
155
156 auto matchedMatrixKernels = getMatchedKernelVersionAndConfigs(matrixKernels, error);
157 if (matchedMatrixKernels.empty()) {
158 return {};
159 }
160 return matchedMatrixKernels;
161 }
162
163 // Use new behavior when kernel FCM version is specified. e.g. kernel FCM version 3 (P)
164 // matches kernel 4.4-p, 4.9-p, 4.14-p, 4.9-q, 4.14-q, 4.14-r etc., but not 5.4-r.
165 // Note we already filtered |kernels| based on kernel version.
166 auto [firstMatrixKernelLevel, firstMatrixKernels] = *kernelsForLevel.begin();
167 if (firstMatrixKernelLevel == Level::UNSPECIFIED || firstMatrixKernelLevel > kernelLevel) {
168 if (error) {
169 *error = "Kernel FCM Version is " + to_string(kernelLevel) + " and kernel version is " +
170 to_string(mVersion) +
171 ", but the first kernel FCM version allowed for kernel version " +
172 to_string(mVersion.dropMinor()) + ".y is " + to_string(firstMatrixKernelLevel);
173 }
174 return {};
175 }
176 for (auto [matrixKernelLevel, matrixKernels] : kernelsForLevel) {
177 if (matrixKernelLevel == Level::UNSPECIFIED || matrixKernelLevel < kernelLevel) {
178 continue;
179 }
180 std::string errorForLevel;
181 auto matchedMatrixKernels =
182 getMatchedKernelVersionAndConfigs(matrixKernels, &errorForLevel);
183 if (matchedMatrixKernels.empty()) {
184 if (error) {
185 *error += "For kernel requirements at matrix level " +
186 to_string(matrixKernelLevel) + ", " + errorForLevel + "\n";
187 }
188 continue;
189 }
190 return matchedMatrixKernels;
191 }
192
193 if (error) {
194 error->insert(0, "No compatible kernel requirement found (kernel FCM version = " +
195 to_string(kernelLevel) + ").\n");
196 }
197 return {};
198 }
199
getMatchedKernelVersionAndConfigs(const std::vector<const MatrixKernel * > & kernels,std::string * error) const200 std::vector<const MatrixKernel*> KernelInfo::getMatchedKernelVersionAndConfigs(
201 const std::vector<const MatrixKernel*>& kernels, std::string* error) const {
202 std::vector<const MatrixKernel*> result;
203 bool foundMatchedKernelVersion = false;
204 for (const MatrixKernel* matrixKernel : kernels) {
205 if (!matchKernelVersion(matrixKernel->minLts())) {
206 continue;
207 }
208 foundMatchedKernelVersion = true;
209 // ignore this fragment if not all conditions are met.
210 if (!matchKernelConfigs(matrixKernel->conditions(), error)) {
211 continue;
212 }
213 if (!matchKernelConfigs(matrixKernel->configs(), error)) {
214 return {};
215 }
216 result.push_back(matrixKernel);
217 }
218 if (!foundMatchedKernelVersion) {
219 if (error != nullptr) {
220 std::stringstream ss;
221 ss << "Framework is incompatible with kernel version " << version()
222 << ", compatible kernel versions are:";
223 for (const MatrixKernel* matrixKernel : kernels) {
224 ss << "\n Minimum LTS: " << matrixKernel->minLts()
225 << ", kernel FCM version: " << matrixKernel->getSourceMatrixLevel()
226 << (matrixKernel->conditions().empty() ? "" : ", with conditionals");
227 };
228 *error = ss.str();
229 }
230 return {};
231 }
232 if (result.empty()) {
233 // This means matchKernelVersion passes but all matchKernelConfigs(conditions) fails.
234 // This should not happen because first <conditions> for each <kernel> must be
235 // empty. Reject here for inconsistency.
236 if (error != nullptr) {
237 error->insert(0, "Framework matches kernel version with unmet conditions.");
238 }
239 return {};
240 }
241 if (error != nullptr) {
242 error->clear();
243 }
244 return result;
245 }
246
operator ==(const KernelInfo & other) const247 bool KernelInfo::operator==(const KernelInfo& other) const {
248 return mVersion == other.mVersion && mConfigs == other.mConfigs;
249 }
250
merge(KernelInfo * other,std::string * error)251 bool KernelInfo::merge(KernelInfo* other, std::string* error) {
252 if (!mergeField(&mVersion, &other->mVersion)) {
253 if (error) {
254 *error = "Conflicting kernel version: " + to_string(version()) + " vs. " +
255 to_string(other->version());
256 }
257 return false;
258 }
259
260 // Do not allow merging configs. One of them must be empty.
261 if (!mergeField(&mConfigs, &other->mConfigs)) {
262 if (error) {
263 *error = "Found <kernel><config> items in two manifests.";
264 }
265 return false;
266 }
267
268 if (!mergeField(&mLevel, &other->mLevel, Level::UNSPECIFIED)) {
269 if (error) {
270 *error = "Conflicting kernel level: " + to_string(level()) + " vs. " +
271 to_string(other->level());
272 }
273 return false;
274 }
275 return true;
276 }
277
278 } // namespace vintf
279 } // namespace android
280