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