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 28 const KernelVersion& KernelInfo::version() const { 29 return mVersion; 30 } 31 32 const std::map<std::string, std::string>& KernelInfo::configs() const { 33 return mConfigs; 34 } 35 36 Level KernelInfo::level() const { 37 return mLevel; 38 } 39 40 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 67 bool KernelInfo::matchKernelVersion(const KernelVersion& minLts) const { 68 return mVersion.dropMinor() == minLts.dropMinor() && minLts.minorRev <= mVersion.minorRev; 69 } 70 71 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 kernelsForLevel[matrixKernelLevel].push_back(&matrixKernel); 105 } 106 107 if (kernelsForLevel.empty()) { 108 if (error) { 109 std::stringstream ss; 110 ss << "No kernel entry found for kernel version " << mVersion.dropMinor() 111 << " at kernel FCM version " 112 << (kernelLevel == Level::UNSPECIFIED ? "unspecified" : to_string(kernelLevel)) 113 << ". The following kernel requirements are checked:"; 114 for (const MatrixKernel& matrixKernel : kernels) { 115 ss << "\n Minimum LTS: " << matrixKernel.minLts() 116 << ", kernel FCM version: " << matrixKernel.getSourceMatrixLevel() 117 << (matrixKernel.conditions().empty() ? "" : ", with conditionals"); 118 }; 119 *error = ss.str(); 120 } 121 return {}; 122 } 123 124 // At this point, kernelsForLevel contains kernel requirements for each level. 125 // For example, if the running kernel version is 4.14.y then kernelsForLevel contains 126 // 4.14-p, 4.14-q, 4.14-r. 127 // (This excludes kernels < kernel FCM version, or device FCM version if kernel FCM version is 128 // empty. For example, if device level = Q and kernel level is unspecified, this list only 129 // contains 4.14-q and 4.14-r). 130 131 // Use legacy behavior when kernel FCM version is not specified. e.g. target FCM version 3 (P) 132 // matches kernel 4.4-p, 4.9-p, 4.14-p, 4.19-q, etc., but not 4.9-q or 4.14-q. 133 // Since we already filtered |kernels| based on kernel version, we only need to check the first 134 // item in kernelsForLevel. 135 // Note that this excludes *-r and above kernels. Devices with target FCM version >= 5 (R) must 136 // state kernel FCM version explicitly in the device manifest. The value is automatically 137 // inserted for devices with target FCM version >= 5 when manifest is built with assemble_vintf. 138 if (kernelLevel == Level::UNSPECIFIED) { 139 auto [matrixKernelLevel, matrixKernels] = *kernelsForLevel.begin(); 140 141 // Do not allow *-r and above kernels. 142 if (matrixKernelLevel != Level::UNSPECIFIED && matrixKernelLevel >= Level::R) { 143 if (error) { 144 KernelInfo msg; 145 msg.mLevel = Level::R; 146 *error = "Kernel FCM version is not specified, but kernel version " + 147 to_string(mVersion) + 148 " is found. Fix by specifying kernel FCM version in device manifest. " 149 "For example, for a *-r kernel:\n" + 150 toXml(msg); 151 } 152 return {}; 153 } 154 155 auto matchedMatrixKernels = getMatchedKernelVersionAndConfigs(matrixKernels, error); 156 if (matchedMatrixKernels.empty()) { 157 return {}; 158 } 159 return matchedMatrixKernels; 160 } 161 162 // Use new behavior when kernel FCM version is specified. e.g. kernel FCM version 3 (P) 163 // 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. 164 // Note we already filtered |kernels| based on kernel version. 165 auto [firstMatrixKernelLevel, firstMatrixKernels] = *kernelsForLevel.begin(); 166 if (firstMatrixKernelLevel == Level::UNSPECIFIED || firstMatrixKernelLevel > kernelLevel) { 167 if (error) { 168 *error = "Kernel FCM Version is " + to_string(kernelLevel) + " and kernel version is " + 169 to_string(mVersion) + 170 ", but the first kernel FCM version allowed for kernel version " + 171 to_string(mVersion.dropMinor()) + ".y is " + to_string(firstMatrixKernelLevel); 172 } 173 return {}; 174 } 175 for (auto [matrixKernelLevel, matrixKernels] : kernelsForLevel) { 176 if (matrixKernelLevel == Level::UNSPECIFIED || matrixKernelLevel < kernelLevel) { 177 continue; 178 } 179 std::string errorForLevel; 180 auto matchedMatrixKernels = 181 getMatchedKernelVersionAndConfigs(matrixKernels, &errorForLevel); 182 if (matchedMatrixKernels.empty()) { 183 if (error) { 184 *error += "For kernel requirements at matrix level " + 185 to_string(matrixKernelLevel) + ", " + errorForLevel + "\n"; 186 } 187 continue; 188 } 189 return matchedMatrixKernels; 190 } 191 192 if (error) { 193 error->insert(0, "No compatible kernel requirement found (kernel FCM version = " + 194 to_string(kernelLevel) + ").\n"); 195 } 196 return {}; 197 } 198 199 std::vector<const MatrixKernel*> KernelInfo::getMatchedKernelVersionAndConfigs( 200 const std::vector<const MatrixKernel*>& kernels, std::string* error) const { 201 std::vector<const MatrixKernel*> result; 202 bool foundMatchedKernelVersion = false; 203 for (const MatrixKernel* matrixKernel : kernels) { 204 if (!matchKernelVersion(matrixKernel->minLts())) { 205 continue; 206 } 207 foundMatchedKernelVersion = true; 208 // ignore this fragment if not all conditions are met. 209 if (!matchKernelConfigs(matrixKernel->conditions(), error)) { 210 continue; 211 } 212 if (!matchKernelConfigs(matrixKernel->configs(), error)) { 213 return {}; 214 } 215 result.push_back(matrixKernel); 216 } 217 if (!foundMatchedKernelVersion) { 218 if (error != nullptr) { 219 std::stringstream ss; 220 ss << "Framework is incompatible with kernel version " << version() 221 << ", compatible kernel versions are:"; 222 for (const MatrixKernel* matrixKernel : kernels) { 223 ss << "\n Minimum LTS: " << matrixKernel->minLts() 224 << ", kernel FCM version: " << matrixKernel->getSourceMatrixLevel() 225 << (matrixKernel->conditions().empty() ? "" : ", with conditionals"); 226 }; 227 *error = ss.str(); 228 } 229 return {}; 230 } 231 if (result.empty()) { 232 // This means matchKernelVersion passes but all matchKernelConfigs(conditions) fails. 233 // This should not happen because first <conditions> for each <kernel> must be 234 // empty. Reject here for inconsistency. 235 if (error != nullptr) { 236 error->insert(0, "Framework matches kernel version with unmet conditions."); 237 } 238 return {}; 239 } 240 if (error != nullptr) { 241 error->clear(); 242 } 243 return result; 244 } 245 246 bool KernelInfo::operator==(const KernelInfo& other) const { 247 return mVersion == other.mVersion && mConfigs == other.mConfigs; 248 } 249 250 bool KernelInfo::merge(KernelInfo* other, std::string* error) { 251 if (!mergeField(&mVersion, &other->mVersion)) { 252 if (error) { 253 *error = "Conflicting kernel version: " + to_string(version()) + " vs. " + 254 to_string(other->version()); 255 } 256 return false; 257 } 258 259 // Do not allow merging configs. One of them must be empty. 260 if (!mergeField(&mConfigs, &other->mConfigs)) { 261 if (error) { 262 *error = "Found <kernel><config> items in two manifests."; 263 } 264 return false; 265 } 266 267 if (!mergeField(&mLevel, &other->mLevel, Level::UNSPECIFIED)) { 268 if (error) { 269 *error = "Conflicting kernel level: " + to_string(level()) + " vs. " + 270 to_string(other->level()); 271 } 272 return false; 273 } 274 return true; 275 } 276 277 } // namespace vintf 278 } // namespace android 279