1 /* 2 * Copyright (c) 2021 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29 #define IOKIT_ENABLE_SHARED_PTR 30 31 #include <IOKit/IOLib.h> 32 #include <IOKit/IOReturn.h> 33 34 #include <libkern/c++/OSArray.h> 35 #include <libkern/c++/OSDictionary.h> 36 #include <libkern/c++/OSNumber.h> 37 #include <libkern/c++/OSString.h> 38 #include <libkern/c++/OSSymbol.h> 39 #include <libkern/c++/OSUnserialize.h> 40 #include <libkern/c++/OSSharedPtr.h> 41 #include <libkern/c++/OSSerialize.h> 42 43 #include <sys/work_interval.h> 44 #include <sys/param.h> 45 46 #include <kern/thread_group.h> 47 #include <kern/work_interval.h> 48 #include <kern/workload_config.h> 49 50 #if DEVELOPMENT || DEBUG 51 #define WLC_LOG(fmt, args...) IOLog("WorkloadConfig: " fmt, ##args) 52 #else 53 #define WLC_LOG(fmt, args...) 54 #endif 55 56 /* Limit criticality offsets. */ 57 #define MAX_CRITICALITY_OFFSET 16 58 59 60 /* Plist keys/values. */ 61 #define kWorkloadIDTableKey "WorkloadIDTable" 62 #define kRootKey "Root" 63 #define kPhasesKey "Phases" 64 #define kWorkIntervalTypeKey "WorkIntervalType" 65 #define kWorkloadClassKey "WorkloadClass" 66 #define kCriticalityOffsetKey "CriticalityOffset" 67 #define kDefaultPhaseKey "DefaultPhase" 68 #define kFlagsKey "Flags" 69 #define kWorkloadIDConfigurationFlagsKey "WorkloadIDConfigurationFlags" 70 71 #define kDisableWorkloadClassThreadPolicyValue "DisableWorkloadClassThreadPolicy" 72 #define kWIComplexityAllowedValue "ComplexityAllowed" 73 #if defined(XNU_TARGET_OS_XR) 74 #if defined(CONFIG_THREAD_GROUPS) 75 #define kUXMValue "UXM" 76 #define kStrictTimersValue "StrictTimers" 77 #endif /* CONFIG_THREAD_GROUPS */ 78 #endif /* XNU_TARGET_OS_XR */ 79 80 #define ARRAY_LEN(x) (sizeof (x) / sizeof (x[0])) 81 82 #if !CONFIG_THREAD_GROUPS 83 #define THREAD_GROUP_FLAGS_EFFICIENT 0 84 #define THREAD_GROUP_FLAGS_APPLICATION 0 85 #define THREAD_GROUP_FLAGS_CRITICAL 0 86 #define THREAD_GROUP_FLAGS_BEST_EFFORT 0 87 #define THREAD_GROUP_FLAGS_ABSENT 0 88 #endif /* CONFIG_THREAD_GROUPS */ 89 90 /* BEGIN IGNORE CODESTYLE */ 91 static const struct WorkloadClassData { 92 const char *name; 93 UInt32 workIntervalFlags; 94 UInt32 threadGroupFlags; 95 } wlClassData[] = { 96 [WI_CLASS_NONE] = 97 { 98 .name = "NONE", 99 .workIntervalFlags = WORK_INTERVAL_WORKLOAD_ID_HAS_ID, 100 .threadGroupFlags = THREAD_GROUP_FLAGS_ABSENT, 101 }, 102 [WI_CLASS_DISCRETIONARY] = 103 { 104 .name = "DISCRETIONARY", 105 .workIntervalFlags = WORK_INTERVAL_WORKLOAD_ID_HAS_ID, 106 .threadGroupFlags = THREAD_GROUP_FLAGS_EFFICIENT, 107 }, 108 [WI_CLASS_BEST_EFFORT] = 109 { 110 .name = "BEST_EFFORT", 111 .workIntervalFlags = WORK_INTERVAL_WORKLOAD_ID_HAS_ID, 112 .threadGroupFlags = THREAD_GROUP_FLAGS_BEST_EFFORT, 113 }, 114 [WI_CLASS_APP_SUPPORT] = 115 { 116 .name = "APPLICATION_SUPPORT", 117 .workIntervalFlags = WORK_INTERVAL_WORKLOAD_ID_HAS_ID, 118 .threadGroupFlags = 0, 119 }, 120 [WI_CLASS_APPLICATION] = 121 { 122 .name = "APPLICATION", 123 .workIntervalFlags = WORK_INTERVAL_WORKLOAD_ID_HAS_ID, 124 .threadGroupFlags = THREAD_GROUP_FLAGS_APPLICATION, 125 }, 126 [WI_CLASS_SYSTEM] = 127 { 128 .name = "SYSTEM", 129 .workIntervalFlags = WORK_INTERVAL_WORKLOAD_ID_HAS_ID, 130 .threadGroupFlags = 0, 131 }, 132 [WI_CLASS_SYSTEM_CRITICAL] = 133 { 134 .name = "SYSTEM_CRITICAL", 135 .workIntervalFlags = WORK_INTERVAL_WORKLOAD_ID_HAS_ID, 136 .threadGroupFlags = THREAD_GROUP_FLAGS_CRITICAL, 137 }, 138 [WI_CLASS_REALTIME] = 139 { 140 .name = "REALTIME", 141 .workIntervalFlags = WORK_INTERVAL_WORKLOAD_ID_HAS_ID | 142 WORK_INTERVAL_WORKLOAD_ID_RT_ALLOWED, 143 .threadGroupFlags = 0, 144 }, 145 [WI_CLASS_REALTIME_CRITICAL] = 146 { 147 .name = "REALTIME_CRITICAL", 148 .workIntervalFlags = WORK_INTERVAL_WORKLOAD_ID_HAS_ID | 149 WORK_INTERVAL_WORKLOAD_ID_RT_ALLOWED | 150 WORK_INTERVAL_WORKLOAD_ID_RT_CRITICAL, 151 .threadGroupFlags = THREAD_GROUP_FLAGS_CRITICAL, 152 }, 153 }; 154 /* END IGNORE CODESTYLE */ 155 156 struct FlagMap { 157 const char *str; 158 UInt32 flags; 159 }; 160 161 static inline IOReturn 162 stringToFlags(const OSString &str, UInt32 &flags, const struct FlagMap *map, 163 size_t mapLen) 164 { 165 for (size_t i = 0; i < mapLen; i++) { 166 if (str.isEqualTo(map[i].str)) { 167 flags = map[i].flags; 168 return kIOReturnSuccess; 169 } 170 } 171 172 return kIOReturnNotFound; 173 } 174 175 static inline IOReturn 176 flagsToString(const UInt32 flags, OSSharedPtr<OSString> &str, const struct FlagMap *map, 177 size_t mapLen) 178 { 179 for (size_t i = 0; i < mapLen; i++) { 180 if (flags == map[i].flags) { 181 str = OSString::withCStringNoCopy(map[i].str); 182 return kIOReturnSuccess; 183 } 184 } 185 186 return kIOReturnNotFound; 187 } 188 189 /* BEGIN IGNORE CODESTYLE */ 190 static const struct FlagMap typeMap[] = { 191 { 192 .str = "DEFAULT", 193 .flags = WORK_INTERVAL_TYPE_DEFAULT | 194 WORK_INTERVAL_FLAG_UNRESTRICTED, 195 }, 196 { 197 .str = "COREAUDIO", 198 .flags = WORK_INTERVAL_TYPE_COREAUDIO | 199 WORK_INTERVAL_FLAG_ENABLE_AUTO_JOIN | 200 WORK_INTERVAL_FLAG_ENABLE_DEFERRED_FINISH, 201 }, 202 { 203 .str = "COREANIMATION", 204 .flags = WORK_INTERVAL_TYPE_COREANIMATION, 205 }, 206 { 207 .str = "CA_RENDER_SERVER", 208 .flags = WORK_INTERVAL_TYPE_CA_RENDER_SERVER, 209 }, 210 { 211 .str = "FRAME_COMPOSITOR", 212 .flags = WORK_INTERVAL_TYPE_FRAME_COMPOSITOR, 213 }, 214 { 215 .str = "CA_CLIENT", 216 .flags = WORK_INTERVAL_TYPE_CA_CLIENT | 217 WORK_INTERVAL_FLAG_UNRESTRICTED, 218 }, 219 { 220 .str = "HID_DELIVERY", 221 .flags = WORK_INTERVAL_TYPE_HID_DELIVERY, 222 }, 223 { 224 .str = "COREMEDIA", 225 .flags = WORK_INTERVAL_TYPE_COREMEDIA, 226 }, 227 { 228 .str = "ARKIT", 229 .flags = WORK_INTERVAL_TYPE_ARKIT | 230 WORK_INTERVAL_FLAG_FINISH_AT_DEADLINE, 231 }, 232 { 233 .str = "AUDIO_CLIENT", 234 .flags = WORK_INTERVAL_TYPE_COREAUDIO | 235 WORK_INTERVAL_FLAG_UNRESTRICTED | 236 WORK_INTERVAL_FLAG_ENABLE_AUTO_JOIN | 237 WORK_INTERVAL_FLAG_ENABLE_DEFERRED_FINISH 238 }, 239 }; 240 /* END IGNORE CODESTYLE */ 241 242 static IOReturn 243 unparseWorkIntervalType(const UInt32 createFlags, OSSharedPtr<OSString> &typeStr) 244 { 245 IOReturn ret = flagsToString(createFlags, typeStr, typeMap, 246 ARRAY_LEN(typeMap)); 247 if (ret != kIOReturnSuccess) { 248 WLC_LOG("unrecognised create flags: 0x%x\n", createFlags); 249 } 250 251 return ret; 252 } 253 254 static IOReturn 255 parseWorkIntervalType(const OSSymbol &id, const OSObject *typeObj, UInt32 &createFlags) 256 { 257 OSSharedPtr<OSString> defaultIntervalType = OSString::withCString("DEFAULT"); 258 259 const OSString *typeStr = OSDynamicCast(OSString, typeObj); 260 if (typeStr == nullptr) { 261 typeStr = defaultIntervalType.get(); 262 } 263 264 IOReturn ret = stringToFlags(*typeStr, createFlags, typeMap, 265 ARRAY_LEN(typeMap)); 266 if (ret != kIOReturnSuccess) { 267 WLC_LOG("unrecognised \"" kWorkIntervalTypeKey "\": \"%s\"\n", 268 typeStr->getCStringNoCopy()); 269 } 270 271 return ret; 272 } 273 274 static IOReturn 275 parseWorkloadClass(const OSSymbol &id, const OSObject *wlClassObj, wi_class_t &wiClass) 276 { 277 const OSString *wlClass = OSDynamicCast(OSString, wlClassObj); 278 if (wlClass == nullptr) { 279 wiClass = WI_CLASS_NONE; 280 return kIOReturnSuccess; 281 } 282 283 for (size_t i = 0; i < ARRAY_LEN(wlClassData); i++) { 284 if (wlClassData[i].name != nullptr && 285 wlClass->isEqualTo(wlClassData[i].name)) { 286 wiClass = (wi_class_t)i; 287 return kIOReturnSuccess; 288 } 289 } 290 291 WLC_LOG("%s: unknown %s: \"%s\"\n", id.getCStringNoCopy(), 292 kWorkloadClassKey, wlClass->getCStringNoCopy()); 293 return kIOReturnError; 294 } 295 296 static IOReturn 297 parseCriticalityOffset(const OSSymbol &id, const wi_class_t wiClass, 298 const OSObject *cOffsetObj, uint8_t &criticalityOffset) 299 { 300 if (wiClass != WI_CLASS_SYSTEM_CRITICAL && 301 wiClass != WI_CLASS_REALTIME_CRITICAL && 302 wiClass != WI_CLASS_BEST_EFFORT && 303 wiClass != WI_CLASS_APP_SUPPORT && 304 wiClass != WI_CLASS_SYSTEM) { 305 criticalityOffset = 0; 306 return kIOReturnSuccess; 307 } 308 309 const OSNumber *cOffset = OSDynamicCast(OSNumber, cOffsetObj); 310 if (cOffset == nullptr) { 311 criticalityOffset = 0; 312 return kIOReturnSuccess; 313 } 314 315 UInt64 criticalityOffset64 = cOffset->unsigned64BitValue(); 316 const int nBytes = cOffset->numberOfBytes(); 317 if (nBytes <= sizeof(criticalityOffset64) && 318 criticalityOffset64 < MAX_CRITICALITY_OFFSET) { 319 criticalityOffset = (uint8_t)criticalityOffset64; 320 return kIOReturnSuccess; 321 } 322 323 WLC_LOG("%s: criticality offset too large\n", id.getCStringNoCopy()); 324 return kIOReturnError; 325 } 326 327 static IOReturn 328 parseFlags(const OSSymbol &id, const OSObject *flagsObj, UInt32 &threadGroupFlags, 329 UInt32 &workIntervalFlags) 330 { 331 /* Optional, so just carry on if not found. */ 332 if (flagsObj == nullptr) { 333 return kIOReturnSuccess; 334 } 335 336 OSArray *flags = OSDynamicCast(OSArray, flagsObj); 337 if (flags == nullptr) { 338 WLC_LOG("failed to parse \"" kFlagsKey "\"\n"); 339 return kIOReturnError; 340 } 341 342 /* BEGIN IGNORE CODESTYLE */ 343 __block IOReturn ret = kIOReturnSuccess; 344 flags->iterateObjects(^bool (OSObject *object) { 345 const OSString *flag = OSDynamicCast(OSString, object); 346 if (flag == nullptr) { 347 WLC_LOG("%s: non-string flag found\n", id.getCStringNoCopy()); 348 ret = kIOReturnError; 349 return true; 350 351 } 352 353 /* Ignore unknown flags. */ 354 #if defined(XNU_TARGET_OS_XR) 355 #if CONFIG_THREAD_GROUPS 356 if (flag->isEqualTo(kUXMValue)) { 357 threadGroupFlags |= THREAD_GROUP_FLAGS_MANAGED; 358 } 359 if (flag->isEqualTo(kStrictTimersValue)) { 360 threadGroupFlags |= THREAD_GROUP_FLAGS_STRICT_TIMERS; 361 } 362 #endif /* CONFIG_THREAD_GROUPS */ 363 #endif /* XNU_TARGET_OS_XR */ 364 if (flag->isEqualTo(kWIComplexityAllowedValue)) { 365 workIntervalFlags |= WORK_INTERVAL_WORKLOAD_ID_COMPLEXITY_ALLOWED; 366 } 367 368 return false; 369 }); 370 /* END IGNORE CODESTYLE */ 371 372 return ret; 373 } 374 375 static 376 IOReturn 377 parsePhases(workload_config_ctx_t *ctx, const OSSymbol &id, OSObject *phasesObj) 378 { 379 __block IOReturn ret = kIOReturnError; 380 381 OSDictionary *phases = OSDynamicCast(OSDictionary, phasesObj); 382 if (phases == nullptr) { 383 WLC_LOG("%s: failed to find dictionary for \"" kPhasesKey "\"\n", 384 id.getCStringNoCopy()); 385 return kIOReturnError; 386 } 387 388 /* There should be at least one phase described. */ 389 ret = kIOReturnError; 390 391 /* BEGIN IGNORE CODESTYLE */ 392 phases->iterateObjects(^bool (const OSSymbol *phase, OSObject *value) { 393 const OSDictionary *dict = OSDynamicCast(OSDictionary, value); 394 if (dict == nullptr) { 395 WLC_LOG("%s: failed to find dictionary for \"%s\" phase\n", 396 id.getCStringNoCopy(), phase->getCStringNoCopy()); 397 ret = kIOReturnError; 398 return true; 399 } 400 401 UInt32 createFlags = 0; 402 ret = parseWorkIntervalType(id, dict->getObject(kWorkIntervalTypeKey), 403 createFlags); 404 if (ret != kIOReturnSuccess) { 405 return true; 406 } 407 408 wi_class_t wiClass = WI_CLASS_NONE; 409 ret = parseWorkloadClass(id, dict->getObject(kWorkloadClassKey), wiClass); 410 if (ret != kIOReturnSuccess) { 411 return true; 412 } 413 const struct WorkloadClassData classData = wlClassData[wiClass]; 414 415 uint8_t criticalityOffset = 0; 416 ret = parseCriticalityOffset(id, wiClass, 417 dict->getObject(kCriticalityOffsetKey), criticalityOffset); 418 if (ret != kIOReturnSuccess) { 419 return true; 420 } 421 422 UInt32 threadGroupFlags = classData.threadGroupFlags; 423 UInt32 workIntervalFlags = classData.workIntervalFlags; 424 ret = parseFlags(id, dict->getObject(kFlagsKey), threadGroupFlags, workIntervalFlags); 425 if (ret != kIOReturnSuccess) { 426 return true; 427 } 428 429 const workload_config_t config = { 430 .wc_thread_group_flags = threadGroupFlags, 431 .wc_flags = workIntervalFlags, 432 .wc_create_flags = createFlags, 433 .wc_class_offset = (uint8_t)criticalityOffset, 434 .wc_class = wiClass, 435 }; 436 ret = workload_config_insert(ctx, id.getCStringNoCopy(), phase->getCStringNoCopy(), &config); 437 if (ret != kIOReturnSuccess) { 438 WLC_LOG("%s: failed to add \"%s\" phase\n", 439 id.getCStringNoCopy(), phase->getCStringNoCopy()); 440 return true; 441 } 442 443 return false; 444 }); 445 /* END IGNORE CODESTYLE */ 446 447 return ret; 448 } 449 450 static IOReturn 451 parseRoot(const OSSymbol &id, const OSObject *rootDict, OSString *&defaultPhase) 452 { 453 const OSDictionary *root = OSDynamicCast(OSDictionary, rootDict); 454 if (root == nullptr) { 455 WLC_LOG("%s: failed to find dictionary for \"" kRootKey "\"\n", 456 id.getCStringNoCopy()); 457 return kIOReturnError; 458 } 459 460 defaultPhase = OSDynamicCast(OSString, root->getObject(kDefaultPhaseKey)); 461 if (defaultPhase == nullptr) { 462 WLC_LOG("%s: failed to find \"" kDefaultPhaseKey"\" in \"" kRootKey "\" dictionary\n", 463 id.getCStringNoCopy()); 464 return kIOReturnError; 465 } 466 467 if (defaultPhase->getLength() == 0) { 468 WLC_LOG("%s: \"" kDefaultPhaseKey" \" is empty in \"" kRootKey "\" dictionary\n", 469 id.getCStringNoCopy()); 470 return kIOReturnError; 471 } 472 473 return kIOReturnSuccess; 474 } 475 476 static IOReturn 477 parseWorkloadIDTable(workload_config_ctx_t *ctx, OSDictionary *IDTable) 478 { 479 /* 480 * At least one valid entry is expected, so start off with error to 481 * catch an empty table or one with no valid entries. 482 */ 483 __block IOReturn ret = kIOReturnError; 484 485 /* BEGIN IGNORE CODESTYLE */ 486 IDTable->iterateObjects(^bool (const OSSymbol *id, OSObject *value) { 487 /* Validate the workload ID. */ 488 if (id->getLength() == 0) { 489 WLC_LOG("zero length ID in \"" kWorkloadIDTableKey "\"\n"); 490 ret = kIOReturnError; 491 return true; 492 } 493 494 /* Parse its properties. */ 495 OSDictionary *idConfig = OSDynamicCast(OSDictionary, value); 496 if (idConfig == nullptr) { 497 WLC_LOG("failed to find dictionary for \"%s\"\n", 498 id->getCStringNoCopy()); 499 ret = kIOReturnError; 500 return true; 501 } 502 503 ret = parsePhases(ctx, *id, idConfig->getObject(kPhasesKey)); 504 if (ret != kIOReturnSuccess) { 505 return true; 506 } 507 508 OSString *defaultPhase = nullptr; 509 ret = parseRoot(*id, idConfig->getObject(kRootKey), defaultPhase); 510 if (ret != kIOReturnSuccess) { 511 return true; 512 } 513 514 /* Fails if the specified phase doesn't exist.. */ 515 ret = workload_config_set_default(ctx, id->getCStringNoCopy(), 516 defaultPhase->getCStringNoCopy()); 517 if (ret != kIOReturnSuccess) { 518 WLC_LOG("failed to set default phase (%s) for \"%s\"\n", 519 defaultPhase->getCStringNoCopy(), id->getCStringNoCopy()); 520 return true; 521 } 522 523 return false; 524 }); 525 /* END IGNORE CODESTYLE */ 526 527 return ret; 528 } 529 530 static IOReturn 531 parseWorkloadIDConfigurationFlags(workload_config_ctx_t *ctx, const OSObject *idTableFlagsObj) 532 { 533 /* Optional, so just carry on if not found. */ 534 if (idTableFlagsObj == nullptr) { 535 return kIOReturnSuccess; 536 } 537 538 OSArray *idTableFlags = OSDynamicCast(OSArray, idTableFlagsObj); 539 if (idTableFlags == nullptr) { 540 WLC_LOG("failed to parse \"" 541 kWorkloadIDConfigurationFlagsKey "\"\n"); 542 return kIOReturnError; 543 } 544 545 /* BEGIN IGNORE CODESTYLE */ 546 __block IOReturn ret = kIOReturnSuccess; 547 idTableFlags->iterateObjects(^bool (OSObject *object) { 548 const OSString *flag = OSDynamicCast(OSString, object); 549 if (flag == nullptr) { 550 WLC_LOG("non-string Workload ID Table flag found\n"); 551 ret = kIOReturnError; 552 return true; 553 } 554 555 if (flag->isEqualTo(kDisableWorkloadClassThreadPolicyValue)) { 556 workload_config_clear_flag(ctx, WLC_F_THREAD_POLICY); 557 } 558 559 return false; 560 }); 561 /* END IGNORE CODESTYLE */ 562 563 return ret; 564 } 565 566 static IOReturn 567 unparseWorkloadIDConfigurationFlags(OSSharedPtr<OSDictionary> &plist) 568 { 569 workload_config_flags_t flags = WLC_F_NONE; 570 571 /* There may be no config at all. That's ok. */ 572 if (workload_config_get_flags(&flags) != KERN_SUCCESS) { 573 return kIOReturnSuccess; 574 } 575 576 /* Workload config can change thread policy scheduling - the default. */ 577 if ((flags & WLC_F_THREAD_POLICY) != 0) { 578 return kIOReturnSuccess; 579 } 580 581 OSSharedPtr<OSArray> idTableFlags = OSArray::withCapacity(1); 582 OSSharedPtr<OSString> flag = OSString::withCString(kDisableWorkloadClassThreadPolicyValue); 583 if (!idTableFlags->setObject(flag) || 584 !plist->setObject(kWorkloadIDConfigurationFlagsKey, idTableFlags)) { 585 return kIOReturnError; 586 } 587 588 return kIOReturnSuccess; 589 } 590 591 extern "C" { 592 extern IOReturn IOParseWorkloadConfig(workload_config_ctx_t *, const char *, size_t); 593 extern IOReturn IOUnparseWorkloadConfig(char *, size_t *); 594 } 595 596 /* Called locked. */ 597 IOReturn 598 IOParseWorkloadConfig(workload_config_ctx_t *ctx, const char *buffer, size_t size) 599 { 600 IOReturn ret = kIOReturnError; 601 602 OSSharedPtr<OSString> unserializeErrorString = nullptr; 603 OSSharedPtr<OSObject> obj = nullptr; 604 OSDictionary *idTable = nullptr; 605 OSDictionary *dict = nullptr; 606 607 ret = workload_config_init(ctx); 608 if (ret != kIOReturnSuccess) { 609 WLC_LOG("failed to initialize workload configuration\n"); 610 goto out; 611 } 612 613 obj = OSUnserializeXML(buffer, unserializeErrorString); 614 dict = OSDynamicCast(OSDictionary, obj.get()); 615 if (dict == nullptr) { 616 WLC_LOG("failed to unserialize plist\n"); 617 ret = kIOReturnError; 618 goto out; 619 } 620 621 idTable = OSDynamicCast(OSDictionary, dict->getObject(kWorkloadIDTableKey)); 622 if (idTable == nullptr) { 623 WLC_LOG("failed to find " kWorkloadIDTableKey "\n"); 624 ret = kIOReturnError; 625 goto out; 626 } 627 628 ret = parseWorkloadIDTable(ctx, idTable); 629 if (ret != kIOReturnSuccess) { 630 goto out; 631 } 632 633 ret = parseWorkloadIDConfigurationFlags(ctx, dict->getObject(kWorkloadIDConfigurationFlagsKey)); 634 if (ret != kIOReturnSuccess) { 635 goto out; 636 } 637 638 ret = kIOReturnSuccess; 639 640 out: 641 if (ret != kIOReturnSuccess) { 642 workload_config_free(ctx); 643 } 644 645 return ret; 646 } 647 648 /* 649 * Does the reverse of IOParseWorkloadConfig() - i.e. serializes the internal 650 * workload configuration. 651 * The serialized workload config is copied to 'buffer' (if non-NULL). 652 * size is in/out - it describes the size of buffer and on return the length of 653 * the serialized config. 654 */ 655 IOReturn 656 IOUnparseWorkloadConfig(char *buffer, size_t *size) 657 { 658 assert(size != nullptr); 659 660 OSSharedPtr<OSDictionary> dict = nullptr;; 661 OSSharedPtr<OSDictionary> idTable = nullptr; 662 OSSharedPtr<OSSerialize> serialize = nullptr; 663 664 serialize = OSSerialize::withCapacity(1); 665 if (serialize == nullptr) { 666 return kIOReturnNoMemory; 667 } 668 669 dict = OSDictionary::withCapacity(1); 670 if (dict == nullptr) { 671 return kIOReturnNoMemory; 672 } 673 674 idTable = OSDictionary::withCapacity(1); 675 if (idTable == nullptr) { 676 return kIOReturnNoMemory; 677 } 678 679 __block IOReturn ret = kIOReturnSuccess; 680 /* BEGIN IGNORE CODESTYLE */ 681 workload_config_iterate(^(const char *id_str, const void *config) { 682 OSSharedPtr<OSDictionary> idDict = OSDictionary::withCapacity(1); 683 if (idDict == nullptr) { 684 ret = kIOReturnNoMemory; 685 return true; 686 } 687 688 OSSharedPtr<OSDictionary> phase = OSDictionary::withCapacity(1); 689 if (phase == nullptr) { 690 ret = kIOReturnNoMemory; 691 return true; 692 } 693 694 workload_config_phases_iterate(config, ^(const char *phase_str, 695 const bool is_default, const workload_config_t *wc) { 696 OSSharedPtr<OSDictionary> phaseData = OSDictionary::withCapacity(1); 697 if (phaseData == nullptr) { 698 ret = kIOReturnNoMemory; 699 return true; 700 } 701 702 if (wc->wc_class != WI_CLASS_NONE) { 703 assert3u(wc->wc_class, <, WI_CLASS_COUNT); 704 OSSharedPtr<OSString> wClass = OSString::withCString(wlClassData[wc->wc_class].name); 705 if (wClass == nullptr || !phaseData->setObject(kWorkloadClassKey, wClass)) { 706 ret = kIOReturnError; 707 return true; 708 } 709 } 710 711 if (wc->wc_class_offset > 0) { 712 OSSharedPtr<OSNumber> criticalityOffset = OSNumber::withNumber(wc->wc_class_offset, 8); 713 if (criticalityOffset == nullptr || 714 !phaseData->setObject(kCriticalityOffsetKey, criticalityOffset)) { 715 ret = kIOReturnError; 716 return true; 717 } 718 } 719 720 OSSharedPtr<OSString> type = nullptr; 721 if (unparseWorkIntervalType(wc->wc_create_flags, type) != kIOReturnSuccess || 722 !phaseData->setObject(kWorkIntervalTypeKey, type)) { 723 ret = kIOReturnError; 724 return true; 725 } 726 727 728 OSSharedPtr<OSArray> flags = OSArray::withCapacity(2); 729 if (flags == nullptr) { 730 ret = kIOReturnError; 731 return true; 732 } 733 #if defined(XNU_TARGET_OS_XR) 734 #if CONFIG_THREAD_GROUPS 735 if ((wc->wc_thread_group_flags & THREAD_GROUP_FLAGS_MANAGED) != 0) { 736 OSSharedPtr<OSString> UXMStr = OSString::withCString(kUXMValue); 737 if (UXMStr == nullptr || !flags->setObject(UXMStr)) { 738 ret = kIOReturnError; 739 return true; 740 } 741 } 742 if ((wc->wc_thread_group_flags & THREAD_GROUP_FLAGS_STRICT_TIMERS) != 0) { 743 OSSharedPtr<OSString> StrictTimersStr = OSString::withCString(kStrictTimersValue); 744 if (StrictTimersStr == nullptr || !flags->setObject(StrictTimersStr)) { 745 ret = kIOReturnError; 746 return true; 747 } 748 } 749 #endif /* CONFIG_THREAD_GROUPS */ 750 #endif /* XNU_TARGET_OS_XR */ 751 if ((wc->wc_flags & WORK_INTERVAL_WORKLOAD_ID_COMPLEXITY_ALLOWED) != 0) { 752 OSSharedPtr<OSString> WIComplexityAllowedStr = 753 OSString::withCString(kWIComplexityAllowedValue); 754 if (WIComplexityAllowedStr == nullptr || !flags->setObject(WIComplexityAllowedStr)) { 755 ret = kIOReturnError; 756 return true; 757 } 758 } 759 if (flags->getCount() && !phaseData->setObject(kFlagsKey, flags)) { 760 ret = kIOReturnError; 761 return true; 762 } 763 764 if (!phase->setObject(phase_str, phaseData)) { 765 ret = kIOReturnError; 766 return true; 767 } 768 769 if (is_default) { 770 OSSharedPtr<OSDictionary> root = OSDictionary::withCapacity(1); 771 OSSharedPtr<OSString> phaseStr = OSString::withCString(phase_str); 772 773 if (root == nullptr || phaseStr == nullptr || 774 !root->setObject(kDefaultPhaseKey, phaseStr)) { 775 ret = kIOReturnError; 776 return true; 777 } 778 779 if (!idDict->setObject(kRootKey, root)) { 780 ret = kIOReturnError; 781 return true; 782 } 783 } 784 785 return false; 786 787 }); 788 789 if (ret != kIOReturnSuccess) { 790 return true; 791 } 792 793 if (!idDict->setObject(kPhasesKey, phase)) { 794 ret = kIOReturnError; 795 return true; 796 } 797 798 if (!idTable->setObject(id_str, idDict)) { 799 ret = kIOReturnError; 800 return true; 801 } 802 803 return false; 804 }); 805 /* END IGNORE CODESTYLE */ 806 807 if (ret != kIOReturnSuccess) { 808 return ret; 809 } 810 811 OSSharedPtr<OSDictionary> plist = OSDictionary::withCapacity(1); 812 if (plist == nullptr) { 813 return kIOReturnError; 814 } 815 816 if (idTable->getCount() > 0 && 817 !plist->setObject(kWorkloadIDTableKey, idTable)) { 818 return kIOReturnError; 819 } 820 821 if (unparseWorkloadIDConfigurationFlags(plist) != kIOReturnSuccess) { 822 return kIOReturnError; 823 } 824 825 if (!plist->serialize(serialize.get())) { 826 return kIOReturnError; 827 } 828 829 if (buffer != nullptr) { 830 (void) strlcpy(buffer, serialize->text(), *size); 831 } 832 *size = serialize->getLength(); 833 834 return kIOReturnSuccess; 835 } 836