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 "parse_xml_internal.h"
21 #include "utils.h"
22 
23 namespace android {
24 namespace vintf {
25 
26 using details::mergeField;
27 
version() const28 const KernelVersion& KernelInfo::version() const {
29     return mVersion;
30 }
31 
configs() const32 const std::map<std::string, std::string>& KernelInfo::configs() const {
33     return mConfigs;
34 }
35 
level() const36 Level KernelInfo::level() const {
37     return mLevel;
38 }
39 
matchKernelConfigs(const std::vector<KernelConfig> & matrixConfigs,std::string * error) const40 bool KernelInfo::matchKernelConfigs(const std::vector<KernelConfig>& matrixConfigs,
41                                     std::string* error) const {
42     std::stringstream ss;
43     ss << "Kernel config errors:";
44     bool configMatches = true;
45     for (const KernelConfig& matrixConfig : matrixConfigs) {
46         const std::string& key = matrixConfig.first;
47         auto it = this->mConfigs.find(key);
48         if (it == this->mConfigs.end()) {
49             // special case: <value type="tristate">n</value> matches if the config doesn't exist.
50             if (matrixConfig.second == KernelConfigTypedValue::gMissingConfig) {
51                 continue;
52             }
53             ss << "\n    Missing config " << key;
54             configMatches = false;
55             continue;
56         }
57         const std::string& kernelValue = it->second;
58         if (!matrixConfig.second.matchValue(kernelValue)) {
59             ss << "\n    For config " << key << ", value = " << kernelValue << " but required "
60                << to_string(matrixConfig.second);
61             configMatches = false;
62             continue;
63         }
64     }
65     if (!configMatches && error != nullptr) {
66         *error = ss.str();
67     }
68     return configMatches;
69 }
70 
matchKernelVersion(const KernelVersion & minLts) const71 bool KernelInfo::matchKernelVersion(const KernelVersion& minLts) const {
72     return mVersion.dropMinor() == minLts.dropMinor() && minLts.minorRev <= mVersion.minorRev;
73 }
74 
getMatchedKernelRequirements(const std::vector<MatrixKernel> & kernels,Level kernelLevel,std::string * error) const75 std::vector<const MatrixKernel*> KernelInfo::getMatchedKernelRequirements(
76     const std::vector<MatrixKernel>& kernels, Level kernelLevel, std::string* error) const {
77     std::map<Level, std::vector<const MatrixKernel*>> kernelsForLevel;
78     for (const MatrixKernel& matrixKernel : kernels) {
79         const auto& minLts = matrixKernel.minLts();
80         auto matrixKernelLevel = matrixKernel.getSourceMatrixLevel();
81 
82         // Filter out kernels with different x.y.
83         if (mVersion.dropMinor() != minLts.dropMinor()) {
84             continue;
85         }
86 
87         // Check matrix kernel level
88 
89         // Use legacy behavior when kernel FCM version is not specified. Blindly add all of them
90         // here. The correct one (with smallest matrixKernelLevel) will be picked later.
91         if (kernelLevel == Level::UNSPECIFIED) {
92             kernelsForLevel[matrixKernelLevel].push_back(&matrixKernel);
93             continue;
94         }
95 
96         if (matrixKernelLevel == Level::UNSPECIFIED) {
97             if (error) {
98                 *error = "Seen unspecified source matrix level; this should not happen.";
99             }
100             return {};
101         }
102 
103         if (matrixKernelLevel < kernelLevel) {
104             continue;
105         }
106 
107         // matrix level >= kernel level
108 
109         // for kernel level >= S, do not allow matrix level > kernel level; i.e. only check
110         // matching KMI.
111         if (kernelLevel >= Level::S && matrixKernelLevel > kernelLevel) {
112             continue;
113         }
114 
115         kernelsForLevel[matrixKernelLevel].push_back(&matrixKernel);
116     }
117 
118     if (kernelsForLevel.empty()) {
119         if (error) {
120             std::stringstream ss;
121             ss << "No kernel entry found for kernel version " << mVersion.dropMinor()
122                << " at kernel FCM version "
123                << (kernelLevel == Level::UNSPECIFIED ? "unspecified" : to_string(kernelLevel))
124                << ". The following kernel requirements are checked:";
125             for (const MatrixKernel& matrixKernel : kernels) {
126                 ss << "\n  Minimum LTS: " << matrixKernel.minLts()
127                    << ", kernel FCM version: " << matrixKernel.getSourceMatrixLevel()
128                    << (matrixKernel.conditions().empty() ? "" : ", with conditionals");
129             };
130             *error = ss.str();
131         }
132         return {};
133     }
134 
135     // At this point, kernelsForLevel contains kernel requirements for each level.
136     // For example, if the running kernel version is 4.14.y then kernelsForLevel contains
137     // 4.14-p, 4.14-q, 4.14-r.
138     // (This excludes kernels < kernel FCM version, or device FCM version if kernel FCM version is
139     // empty. For example, if device level = Q and kernel level is unspecified, this list only
140     // contains 4.14-q and 4.14-r).
141 
142     // Use legacy behavior when kernel FCM version is not specified. e.g. target FCM version 3 (P)
143     // matches kernel 4.4-p, 4.9-p, 4.14-p, 4.19-q, etc., but not 4.9-q or 4.14-q.
144     // Since we already filtered |kernels| based on kernel version, we only need to check the first
145     // item in kernelsForLevel.
146     // Note that this excludes *-r and above kernels. Devices with target FCM version >= 5 (R) must
147     // state kernel FCM version explicitly in the device manifest. The value is automatically
148     // inserted for devices with target FCM version >= 5 when manifest is built with assemble_vintf.
149     if (kernelLevel == Level::UNSPECIFIED) {
150         auto [matrixKernelLevel, matrixKernels] = *kernelsForLevel.begin();
151 
152         // Do not allow *-r and above kernels.
153         if (matrixKernelLevel != Level::UNSPECIFIED && matrixKernelLevel >= Level::R) {
154             if (error) {
155                 KernelInfo msg;
156                 msg.mLevel = Level::R;
157                 *error = "Kernel FCM version is not specified, but kernel version " +
158                          to_string(mVersion) +
159                          " is found. Fix by specifying kernel FCM version in device manifest. "
160                          "For example, for a *-r kernel:\n" +
161                          toXml(msg);
162             }
163             return {};
164         }
165 
166         auto matchedMatrixKernels = getMatchedKernelVersionAndConfigs(matrixKernels, error);
167         if (matchedMatrixKernels.empty()) {
168             return {};
169         }
170         return matchedMatrixKernels;
171     }
172 
173     // Use new behavior when kernel FCM version is specified. e.g. kernel FCM version 3 (P)
174     // 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.
175     // For kernel FCM version >= S, only matching KMI is accepted. e.g. kernel FCM version 6 (S)
176     // matches 4.19-stable, 5.10-android12, 5.4-android12, not x.y-android13.
177     // Note we already filtered |kernels| based on kernel version.
178     auto [firstMatrixKernelLevel, firstMatrixKernels] = *kernelsForLevel.begin();
179     if (firstMatrixKernelLevel == Level::UNSPECIFIED || firstMatrixKernelLevel > kernelLevel) {
180         if (error) {
181             *error = "Kernel FCM Version is " + to_string(kernelLevel) + " and kernel version is " +
182                      to_string(mVersion) +
183                      ", but the first kernel FCM version allowed for kernel version " +
184                      to_string(mVersion.dropMinor()) + ".y is " + to_string(firstMatrixKernelLevel);
185         }
186         return {};
187     }
188     for (auto [matrixKernelLevel, matrixKernels] : kernelsForLevel) {
189         if (matrixKernelLevel == Level::UNSPECIFIED || matrixKernelLevel < kernelLevel) {
190             continue;
191         }
192         std::string errorForLevel;
193         auto matchedMatrixKernels =
194             getMatchedKernelVersionAndConfigs(matrixKernels, &errorForLevel);
195         if (matchedMatrixKernels.empty()) {
196             if (error) {
197                 *error += "For kernel requirements at matrix level " +
198                           to_string(matrixKernelLevel) + ", " + errorForLevel + "\n";
199             }
200             continue;
201         }
202         return matchedMatrixKernels;
203     }
204 
205     if (error) {
206         error->insert(0, "No compatible kernel requirement found (kernel FCM version = " +
207                              to_string(kernelLevel) + ").\n");
208     }
209     return {};
210 }
211 
getMatchedKernelVersionAndConfigs(const std::vector<const MatrixKernel * > & kernels,std::string * error) const212 std::vector<const MatrixKernel*> KernelInfo::getMatchedKernelVersionAndConfigs(
213     const std::vector<const MatrixKernel*>& kernels, std::string* error) const {
214     std::vector<const MatrixKernel*> result;
215     bool foundMatchedKernelVersion = false;
216     for (const MatrixKernel* matrixKernel : kernels) {
217         if (!matchKernelVersion(matrixKernel->minLts())) {
218             continue;
219         }
220         foundMatchedKernelVersion = true;
221         // ignore this fragment if not all conditions are met.
222         if (!matchKernelConfigs(matrixKernel->conditions(), nullptr)) {
223             continue;
224         }
225         if (!matchKernelConfigs(matrixKernel->configs(), error)) {
226             return {};
227         }
228         result.push_back(matrixKernel);
229     }
230     if (!foundMatchedKernelVersion) {
231         if (error != nullptr) {
232             std::stringstream ss;
233             ss << "Framework is incompatible with kernel version " << version()
234                << ", compatible kernel versions are:";
235             for (const MatrixKernel* matrixKernel : kernels) {
236                 ss << "\n  Minimum LTS: " << matrixKernel->minLts()
237                    << ", kernel FCM version: " << matrixKernel->getSourceMatrixLevel()
238                    << (matrixKernel->conditions().empty() ? "" : ", with conditionals");
239             };
240             *error = ss.str();
241         }
242         return {};
243     }
244     if (result.empty()) {
245         // This means matchKernelVersion passes but all matchKernelConfigs(conditions) fails.
246         // This should not happen because first <conditions> for each <kernel> must be
247         // empty. Reject here for inconsistency.
248         if (error != nullptr) {
249             error->insert(0, "Framework matches kernel version with unmet conditions.");
250         }
251         return {};
252     }
253     if (error != nullptr) {
254         error->clear();
255     }
256     return result;
257 }
258 
operator ==(const KernelInfo & other) const259 bool KernelInfo::operator==(const KernelInfo& other) const {
260     return mVersion == other.mVersion && mConfigs == other.mConfigs;
261 }
262 
merge(KernelInfo * other,std::string * error)263 bool KernelInfo::merge(KernelInfo* other, std::string* error) {
264     if (!mergeField(&mVersion, &other->mVersion)) {
265         if (error) {
266             *error = "Conflicting kernel version: " + to_string(version()) + " vs. " +
267                      to_string(other->version());
268         }
269         return false;
270     }
271 
272     // Do not allow merging configs. One of them must be empty.
273     if (!mergeField(&mConfigs, &other->mConfigs)) {
274         if (error) {
275             *error = "Found <kernel><config> items in two manifests.";
276         }
277         return false;
278     }
279 
280     if (!mergeField(&mLevel, &other->mLevel, Level::UNSPECIFIED)) {
281         if (error) {
282             *error = "Conflicting kernel level: " + to_string(level()) + " vs. " +
283                      to_string(other->level());
284         }
285         return false;
286     }
287     return true;
288 }
289 
290 }  // namespace vintf
291 }  // namespace android
292