diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..1a1d609 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,44 @@ +name: CI + +on: + push: + pull_request: + workflow_dispatch: + release: + types: [published] + +env: + PROJECT_TYPE: KEXT + +jobs: + build: + name: Build + runs-on: macos-latest + env: + JOB_TYPE: BUILD + steps: + - uses: actions/checkout@v2 + - uses: actions/checkout@v2 + with: + repository: acidanthera/MacKernelSDK + path: MacKernelSDK + - name: CI Bootstrap + run: | + src=$(/usr/bin/curl -Lfs https://raw.githubusercontent.com/acidanthera/ocbuild/master/ci-bootstrap.sh) && eval "$src" || exit 1 + + - run: xcodebuild -jobs 1 -configuration Debug + - run: xcodebuild -jobs 1 -configuration Release + + - name: Upload to Artifacts + uses: actions/upload-artifact@v2 + with: + name: Artifacts + path: build/*/*.zip + - name: Upload to Release + if: github.event_name == 'release' + uses: svenstaro/upload-release-action@e74ff71f7d8a4c4745b560a485cc5fdb9b5b999d + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: build/*/*.zip + tag: ${{ github.ref }} + file_glob: true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ee06487 --- /dev/null +++ b/.gitignore @@ -0,0 +1,56 @@ +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## User settings +xcuserdata/ + +## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) +*.xcscmblueprint +*.xccheckout + +## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) +build/ +DerivedData/ +*.moved-aside +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 + +## Gcc Patch +/*.gcno + +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +Lilu.kext +MacKernelSDK/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..aa8f8bf --- /dev/null +++ b/README.md @@ -0,0 +1,67 @@ +# USBToolBoxᵇᵉᵗᵃ + +*Making USB mapping simple(r)* + +The USBToolBox kext is a kext intended to make common actions for USB mapping easier. + +## Features + +* Attach to the controller instance or parent device, allowing for more ways to match +* Ignore port definitions from ACPI to force macOS to enumerate all ports manually + * Bypasses borked ACPI as seen on some Ryzen motherboards and 400 series Intel motherboards + * Replaces SSDT-RHUB +* Override any built-in Apple USB maps attaching based on SMBIOS and controller name + * Removes the need for controller renames in ACPI patches +* Does not require model identifier specified in USB map (if attaching to PCI device) +* Very compatible with existing USB maps (port format is the same) +* Does not hardcode any port maps, unlike USBInjectAll + +This does **not** patch the port limit. + +## Configuration + +USBToolBox supports configuration using boot arguments, properties, or in the map. You can set the properties on either the PCI device or the `AppleUSBHostController` instance. + +Properties can be any type and only existence, not type, is checked, unless otherwise specified. + +`-utboff` (property `utb-off`): Disable USBToolBox completely + +`-utbacpioff` (property `utb-acpi-off`): Disable RHUB removal from ACPI plane (borked ACPI removal) + +`-utbappleoff` (property `utb-apple-off`): Disable existing `ports` and `port-count` removal + +`-utbmapoff` (property `utb-map-off`): Disable custom map (useful for testing) + +`utbwait=XXX` (property `utb-wait`, type number): Custom delay for `waitForMatchingService`. Integer between 1-180, inclusive. + +## Converting Existing Maps + +Converting existing maps is fairly easy. + +* For each IOKit personality, change the following: + * `CFBundleIdentifier` to `com.dhinakg.USBToolBox.kext` + * `IOClass` to `USBToolBox` + * `IOMatchCategory` to `USBToolBox` +* Add a dictionary named `OSBundleLibraries` to the root item. It should contain `com.dhinakg.USBToolBox.kext`, with value `1.0.0`. + +## Usage + +You can get the latest release from the GitHub [releases tab](https://github.com/USBToolBox/kext/releases). + +The zip contains 2 kexts: the main `USBToolBox.kext`, and `UTBDefault.kext`, a codeless kext used for attaching USBToolBox to all PCIe USB controllers. This is designed for use before you map, so that you can have all USB ports working (assuming no port limit) before you map. However, it is not needed if you choose to map from the start (ie. from Windows, using the USBToolBox [tool](https://github.com/USBToolBox/tool)). + +A basic fresh install flow would be as follows: + +1. Add `USBToolBox.kext` and `UTBDefault.kext` to your `EFI/OC/Kexts` folder, and make sure to update your `config.plist`. +2. Install macOS. +3. Map your ports with the USBToolBox [tool](https://github.com/USBToolBox/tool). +4. Remove `UTBDefault.kext` and add your newly created `UTBMap.kext` (or whatever your USB map is called) to `EFI/OC/Kexts`. +5. Reboot and you should have a USB mapped system! + +## Credits + +@RehabMan for [USBInjectAll](https://github.com/RehabMan/USBInjectAll), an inspiration for this project + +@acidanthera for [MacKernelSDK](https://github.com/acidanthera/MacKernelSDK) + +My testing team (you know who you are) for testing diff --git a/Resources/UTBDefault.kext/Contents/Info.plist b/Resources/UTBDefault.kext/Contents/Info.plist new file mode 100644 index 0000000..8a31654 --- /dev/null +++ b/Resources/UTBDefault.kext/Contents/Info.plist @@ -0,0 +1,47 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleGetInfoString + v1.0 + CFBundleIdentifier + com.dhinakg.USBToolBox.injector + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + UTBMap + CFBundlePackageType + KEXT + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + IOKitPersonalities + + XHC1 + + CFBundleIdentifier + com.dhinakg.USBToolBox.kext + IOClass + USBToolBox + IOMatchCategory + USBToolBox + IOPCIClassMatch + 0x0c030000&0xffff0000 + IOProviderClass + IOPCIDevice + + + OSBundleLibraries + + com.dhinakg.USBToolBox.kext + 1.0.0 + + OSBundleRequired + Root + + diff --git a/USBToolBox.xcodeproj/project.pbxproj b/USBToolBox.xcodeproj/project.pbxproj new file mode 100644 index 0000000..07ac49e --- /dev/null +++ b/USBToolBox.xcodeproj/project.pbxproj @@ -0,0 +1,367 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 5114BA3B263DA71E00D7132C /* libkmod.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5114BA3A263DA71E00D7132C /* libkmod.a */; }; + 5114BA55263DAE8300D7132C /* UTBDefault.kext in Embed Resources */ = {isa = PBXBuildFile; fileRef = 5114BA4D263DAE2500D7132C /* UTBDefault.kext */; }; + 5114BA5B263DAF7800D7132C /* USBToolBox.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5114BA58263DAF7800D7132C /* USBToolBox.cpp */; }; + 5114BA5C263DAF7800D7132C /* USBToolBox.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 5114BA59263DAF7800D7132C /* USBToolBox.hpp */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 5114BA3E263DA8C000D7132C /* Embed Resources */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 16; + files = ( + 5114BA55263DAE8300D7132C /* UTBDefault.kext in Embed Resources */, + ); + name = "Embed Resources"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 5114BA29263DA57C00D7132C /* USBToolBox.kext */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = USBToolBox.kext; sourceTree = BUILT_PRODUCTS_DIR; }; + 5114BA30263DA57C00D7132C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 5114BA3A263DA71E00D7132C /* libkmod.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libkmod.a; path = usr/lib/libkmod.a; sourceTree = SDKROOT; }; + 5114BA4D263DAE2500D7132C /* UTBDefault.kext */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.kernel-extension"; name = UTBDefault.kext; path = Resources/UTBDefault.kext; sourceTree = ""; }; + 5114BA58263DAF7800D7132C /* USBToolBox.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = USBToolBox.cpp; sourceTree = ""; }; + 5114BA59263DAF7800D7132C /* USBToolBox.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = USBToolBox.hpp; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 5114BA26263DA57C00D7132C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 5114BA3B263DA71E00D7132C /* libkmod.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 5114BA1F263DA57C00D7132C = { + isa = PBXGroup; + children = ( + 5114BA2B263DA57C00D7132C /* USBToolBox */, + 5114BA2A263DA57C00D7132C /* Products */, + 5114BA4C263DADFD00D7132C /* Resources */, + 5114BA39263DA71E00D7132C /* Frameworks */, + ); + sourceTree = ""; + }; + 5114BA2A263DA57C00D7132C /* Products */ = { + isa = PBXGroup; + children = ( + 5114BA29263DA57C00D7132C /* USBToolBox.kext */, + ); + name = Products; + sourceTree = ""; + }; + 5114BA2B263DA57C00D7132C /* USBToolBox */ = { + isa = PBXGroup; + children = ( + 5114BA58263DAF7800D7132C /* USBToolBox.cpp */, + 5114BA59263DAF7800D7132C /* USBToolBox.hpp */, + 5114BA30263DA57C00D7132C /* Info.plist */, + ); + path = USBToolBox; + sourceTree = ""; + }; + 5114BA39263DA71E00D7132C /* Frameworks */ = { + isa = PBXGroup; + children = ( + 5114BA3A263DA71E00D7132C /* libkmod.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + 5114BA4C263DADFD00D7132C /* Resources */ = { + isa = PBXGroup; + children = ( + 5114BA4D263DAE2500D7132C /* UTBDefault.kext */, + ); + name = Resources; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 5114BA24263DA57C00D7132C /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 5114BA5C263DAF7800D7132C /* USBToolBox.hpp in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 5114BA28263DA57C00D7132C /* USBToolBox */ = { + isa = PBXNativeTarget; + buildConfigurationList = 5114BA33263DA57C00D7132C /* Build configuration list for PBXNativeTarget "USBToolBox" */; + buildPhases = ( + 5114BA24263DA57C00D7132C /* Headers */, + 5114BA25263DA57C00D7132C /* Sources */, + 5114BA26263DA57C00D7132C /* Frameworks */, + 5114BA3E263DA8C000D7132C /* Embed Resources */, + 5114BA3F263DA8E100D7132C /* Archive */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = USBToolBox; + productName = kext; + productReference = 5114BA29263DA57C00D7132C /* USBToolBox.kext */; + productType = "com.apple.product-type.kernel-extension"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 5114BA20263DA57C00D7132C /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1240; + TargetAttributes = { + 5114BA28263DA57C00D7132C = { + CreatedOnToolsVersion = 12.4; + }; + }; + }; + buildConfigurationList = 5114BA23263DA57C00D7132C /* Build configuration list for PBXProject "USBToolBox" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 5114BA1F263DA57C00D7132C; + productRefGroup = 5114BA2A263DA57C00D7132C /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 5114BA28263DA57C00D7132C /* USBToolBox */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXShellScriptBuildPhase section */ + 5114BA3F263DA8E100D7132C /* Archive */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = Archive; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "cd \"${TARGET_BUILD_DIR}\"\n\ndist=(\"$FULL_PRODUCT_NAME\")\nif [ -d \"$DWARF_DSYM_FILE_NAME\" ]; then dist+=(\"$DWARF_DSYM_FILE_NAME\"); fi\ndist+=(\"UTBDefault.kext\")\n\narchive=\"${PRODUCT_NAME}-${MODULE_VERSION}-$(echo $CONFIGURATION | tr /a-z/ /A-Z/).zip\"\nrm -rf *.zip\nif [ \"$CONFIGURATION\" == \"Release\" ]; then strip -x -T \"${EXECUTABLE_PATH}\"; fi\nzip -qry -FS \"${archive}\" \"${dist[@]}\"\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 5114BA25263DA57C00D7132C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5114BA5B263DAF7800D7132C /* USBToolBox.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 5114BA31263DA57C00D7132C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + KERNEL_EXTENSION_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/MacKernelSDK/Headers"; + KERNEL_FRAMEWORK_HEADERS = "$(PROJECT_DIR)/MacKernelSDK/Headers"; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 5114BA32263DA57C00D7132C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + KERNEL_EXTENSION_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/MacKernelSDK/Headers"; + KERNEL_FRAMEWORK_HEADERS = "$(PROJECT_DIR)/MacKernelSDK/Headers"; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = macosx; + }; + name = Release; + }; + 5114BA34263DA57C00D7132C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = x86_64; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1.0.0; + INFOPLIST_FILE = USBToolBox/Info.plist; + MODULE_NAME = com.dhinakg.USBToolBox.kext; + MODULE_VERSION = 1.0.0; + PRODUCT_BUNDLE_IDENTIFIER = com.dhinakg.USBToolBox.kext; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = YES; + WRAPPER_EXTENSION = kext; + }; + name = Debug; + }; + 5114BA35263DA57C00D7132C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = x86_64; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 1.0.0; + INFOPLIST_FILE = USBToolBox/Info.plist; + MODULE_NAME = com.dhinakg.USBToolBox.kext; + MODULE_VERSION = 1.0.0; + PRODUCT_BUNDLE_IDENTIFIER = com.dhinakg.USBToolBox.kext; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = YES; + STRIP_STYLE = "non-global"; + WRAPPER_EXTENSION = kext; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 5114BA23263DA57C00D7132C /* Build configuration list for PBXProject "USBToolBox" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5114BA31263DA57C00D7132C /* Debug */, + 5114BA32263DA57C00D7132C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 5114BA33263DA57C00D7132C /* Build configuration list for PBXNativeTarget "USBToolBox" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5114BA34263DA57C00D7132C /* Debug */, + 5114BA35263DA57C00D7132C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 5114BA20263DA57C00D7132C /* Project object */; +} diff --git a/USBToolBox.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/USBToolBox.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..0dd98c4 --- /dev/null +++ b/USBToolBox.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/USBToolBox.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/USBToolBox.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/USBToolBox.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/USBToolBox.xcodeproj/xcuserdata/dhinak.xcuserdatad/xcschemes/xcschememanagement.plist b/USBToolBox.xcodeproj/xcuserdata/dhinak.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..3e796f2 --- /dev/null +++ b/USBToolBox.xcodeproj/xcuserdata/dhinak.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,19 @@ + + + + + SchemeUserState + + USBToolBox.xcscheme_^#shared#^_ + + orderHint + 0 + + kext.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git a/USBToolBox/Info.plist b/USBToolBox/Info.plist new file mode 100644 index 0000000..5588508 --- /dev/null +++ b/USBToolBox/Info.plist @@ -0,0 +1,49 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + ${MARKETING_VERSION} + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + IOKitPersonalities + + NSHumanReadableCopyright + Copyright © 2020-2021 Dhinak G. All rights reserved. + OSBundleCompatibleVersion + 1.0.0 + OSBundleLibraries + + com.apple.driver.AppleUSBHostMergeProperties + 1.2 + com.apple.iokit.IOPCIFamily + 2.9 + com.apple.kpi.bsd + 19.0.0 + com.apple.kpi.dsep + 19.0.0 + com.apple.kpi.iokit + 19.0.0 + com.apple.kpi.libkern + 19.0.0 + com.apple.kpi.mach + 19.0.0 + com.apple.kpi.unsupported + 19.0.0 + + OSBundleRequired + Root + + diff --git a/USBToolBox/USBToolBox.cpp b/USBToolBox/USBToolBox.cpp new file mode 100644 index 0000000..d6521aa --- /dev/null +++ b/USBToolBox/USBToolBox.cpp @@ -0,0 +1,385 @@ +// +// USBToolBox.cpp +// USBToolBox +// +// Created by Dhinak G on 6/1/20. +// Copyright © 2020-2021 Dhinak G. All rights reserved. +// + +#include +#include +#include +#include +#include +#include + +#include "USBToolBox.hpp" + +#define super IOService +#define PRODUCT_NAME USBToolBox + + +#define checkProperty(a) (this->pciDevice && this->pciDevice->getProperty(a)) || (this->controllerInstance && this->controllerInstance->getProperty(a)) || getProperty(a) + +OSDefineMetaClassAndStructors(USBToolBox, IOService) + +OSDictionary* USBToolBox::createMatchingDictionary() { + char pciDevicePath[PARENT_PATH_LENGTH] = { 0 }; + int pciDevicePathLength = sizeof(pciDevicePath); + + bool pciDevicePathStatus = pciDevice->getPath(pciDevicePath, &pciDevicePathLength, gIOServicePlane); + + if (!pciDevicePathStatus) { + SYSTEMLOGPROV("Failed to get PCI device path!"); + return NULL; + } else { + DEBUGLOGPROV("Path is %s, length %d", pciDevicePath, pciDevicePathLength); + } + + OSString* pciDevicePathString = OSString::withCString(pciDevicePath); + + if (!pciDevicePathString) { + SYSTEMLOGPROV("Failed to create OSString of PCI device path!"); + return NULL; + } + + const OSSymbol* pathMatchKey = OSSymbol::withCString("IOPathMatch"); + + if (!pathMatchKey) { + SYSTEMLOGPROV("Failed to create path match key!"); + OSSafeReleaseNULL(pciDevicePathString); + return NULL; + } + + const OSObject* parentMatchObjects[] {pciDevicePathString}; + const OSSymbol* parentMatchSymbols[] {pathMatchKey}; + + OSDictionary* parentMatch = OSDictionary::withObjects(parentMatchObjects, parentMatchSymbols, 1); + + OSSafeReleaseNULL(pathMatchKey); + OSSafeReleaseNULL(pciDevicePathString); + + if (!parentMatch) { + SYSTEMLOGPROV("Failed to create parent match dict!"); + return NULL; + } + + OSDictionary* matchingDict = serviceMatching("AppleUSBHostController"); + + if (!matchingDict) { + SYSTEMLOGPROV("Failed to create matching dict!"); + OSSafeReleaseNULL(parentMatch); + return NULL; + } + + bool setParentMatchStatus = matchingDict->setObject("IOParentMatch", parentMatch); + + OSSafeReleaseNULL(parentMatch); + + if (!setParentMatchStatus) { + SYSTEMLOGPROV("Failed to set parent match!"); + OSSafeReleaseNULL(matchingDict); + return NULL; + } + + char modelNameArray[MODEL_NAME_LENGTH] = { 0 }; + // bool modelNameStatus = PEGetProductName(modelNameArray, MODEL_NAME_LENGTH); Big Sur+ + bool modelNameStatus = PEGetModelName(modelNameArray, MODEL_NAME_LENGTH); + + if (!modelNameStatus) { + SYSTEMLOGPROV("Failed to get model name!"); + OSSafeReleaseNULL(matchingDict); + return NULL; + } else { + DEBUGLOGPROV("Name is %s", modelNameArray); + } + + OSString* modelName = OSString::withCString(modelNameArray); + + if (!modelName) { + SYSTEMLOGPROV("Could not create OSString with model name!"); + OSSafeReleaseNULL(matchingDict); + return NULL; + } + + bool setModelStatus = matchingDict->setObject("model", modelName); + + modelName->release(); + + if (!setModelStatus) { + SYSTEMLOGPROV("Failed to set model!"); + OSSafeReleaseNULL(matchingDict); + return NULL; + } + + return matchingDict; +} + +IORegistryEntry* USBToolBox::getControllerViaMatching() { + PE_parse_boot_argn(bootargMatchWait, &matchWait, sizeof(matchWait)); + + if (OSObject* matchWaitProperty = pciDevice->getProperty(propertyMatchWait)) { + if (OSNumber* matchWaitNumber = OSDynamicCast(OSNumber, matchWaitProperty)) { + matchWait = matchWaitNumber->unsigned64BitValue(); + } else { + DEBUGLOGPROV("Provider match wait property is incorrect type"); + } + } + + if (OSObject* matchWaitProperty = getProperty(propertyMatchWait)) { + if (OSNumber* matchWaitNumber = OSDynamicCast(OSNumber, matchWaitProperty)) { + matchWait = matchWaitNumber->unsigned64BitValue(); + } else { + DEBUGLOGPROV("Match wait property is incorrect type"); + } + } + + if (matchWait > MAX_WAIT) { + SYSTEMLOGPROV("Match wait is too high, ignoring"); + matchWait = DEFAULT_WAIT; + } + + OSDictionary* matchingDict = createMatchingDictionary(); + + if (!matchingDict) { + SYSTEMLOGPROV("Failed to create matching dict!"); + return NULL; + } + + DEBUGLOGPROV("Starting waitForMatchingService"); + IOService* controller = IOService::waitForMatchingService(matchingDict, matchWait * NANOSECONDS); + + OSSafeReleaseNULL(matchingDict); + + if (controller) { + DEBUGLOGPROV("waitForMatchingService successful"); + } else { + SYSTEMLOGPROV("waitForMatchingService failed or timed out"); + } + return controller; +} + +bool USBToolBox::checkClassHierarchy(IORegistryEntry* entry, const char* className) { + const OSSymbol* classSymbol = OSSymbol::withCString(className); + + if (!classSymbol) { + DEBUGLOG("Failed to create class symbol for %s!", className); + return false; + } + + const OSMetaClass* entryMetaclass = entry->getMetaClass(); + while (entryMetaclass) { + if (entryMetaclass->getClassNameSymbol() == classSymbol) { + OSSafeReleaseNULL(classSymbol); + return true; + } + entryMetaclass = entryMetaclass->getSuperClass(); + }; + OSSafeReleaseNULL(classSymbol); + return false; +} + +IORegistryEntry* USBToolBox::getControllerViaIteration() { + OSIterator* iterator = pciDevice->getChildIterator(gIOServicePlane); + if (!iterator) { + SYSTEMLOGPROV("Failed to create IOService iterator!"); + return NULL; + } + + IORegistryEntry* controller = NULL; + + DEBUGLOGPROV("Starting iteration over IOService plane"); + while (IORegistryEntry* entry = OSDynamicCast(IORegistryEntry, iterator->getNextObject())) { + DEBUGLOGPROV("Object name is %s with class %s", entry->getName(), entry->getMetaClass()->getClassName()); + if (checkClassHierarchy(entry, "AppleUSBHostController")) { + // This is it + DEBUGLOGPROV("%s is AppleUSBHostController instance", entry->getName()); + // Retain before we exit iterator to prevent any races. + entry->retain(); + controller = entry; + break; + } + } + DEBUGLOGPROV("Iteration over IOService plane finished"); + OSSafeReleaseNULL(iterator); + + return controller; +} + +void USBToolBox::deleteProperty(IORegistryEntry* entry, const char* property) { + if (entry->getProperty(property)) { + DEBUGLOGPROV("%s exists, removing", property); + entry->removeProperty(property); + DEBUGLOGPROV("removed %s", property); + } else { + DEBUGLOGPROV("%s is null, not removing", property); + } +} + +void USBToolBox::mergeProperties(IORegistryEntry* instance) { + if (instance) { + this->controllerInstance = instance; + } + DEBUGLOGPROV("Merging properties"); + + if (!(checkKernelArgument(bootargAppleOff) || checkProperty(propertyAppleOff))) { + DEBUGLOGPROV("Removing any preexisting ports"); + deleteProperty(this->controllerInstance, "ports"); + deleteProperty(this->controllerInstance, "port-count"); + } else { + SYSTEMLOGPROV("Apple off specified, not removing any preexisting ports"); + } + + if (!(checkKernelArgument(bootargMapOff) || checkProperty(propertyMapOff))) { + OSDictionary* properties = NULL; + if ((properties = OSDynamicCast(OSDictionary, getProperty("IOProviderMergeProperties")))) { + DEBUGLOGPROV("Applying map"); + } else if ((properties = OSDynamicCast(OSDictionary, this->pciDevice->getProperty("IOProviderMergeProperties")))) { + DEBUGLOGPROV("Applying map from provider"); + } + + if (properties) { + bool iterateStatus = properties->iterateObjects(^bool (const OSSymbol * key, OSObject * object) { + //DEBUGLOGPROV("Applied property %s", key->getCStringNoCopy()); + this->controllerInstance->setProperty(key, object); + return false; // The callback should return true to early terminate the iteration, false otherwise. + }); + if (!iterateStatus) { + SYSTEMLOGPROV("Map application iteration failed!"); + } else { + DEBUGLOGPROV("Successfully applied map"); + } + } else { + DEBUGLOGPROV("No properties to apply"); + } + + } else { + SYSTEMLOGPROV("Map disabled, continuing"); + } + this->controllerInstance->release(); +} + +void USBToolBox::removeACPIPorts() { + if (!(checkKernelArgument(bootargAcpiOff) || checkProperty(propertyAcpiOff))) { + const IORegistryPlane* acpiPlane = IORegistryEntry::getPlane("IOACPIPlane"); + + if (!acpiPlane) { + SYSTEMLOGPROV("Could not get ACPI plane!"); + return; + } + + IORegistryEntry* acpiEntry = OSDynamicCast(IORegistryEntry, pciDevice->getProperty("acpi-device")); + + if (acpiEntry) { + if (OSIterator* acpiIterator = acpiEntry->getChildIterator(acpiPlane)) { + DEBUGLOGPROV("Starting iteration over ACPI plane"); + while (IORegistryEntry* entry = OSDynamicCast(IORegistryEntry, acpiIterator->getNextObject())) { + DEBUGLOGPROV("Object name is %s, detatching", entry->getName()); + entry->detachAll(acpiPlane); + } + DEBUGLOGPROV("Iteration over ACPI plane finished"); + acpiEntry->setProperty("ACPI removed by USBToolBox", true); + OSSafeReleaseNULL(acpiIterator); + } else { + SYSTEMLOGPROV("Failed to create ACPI iterator!"); + } + } else { + SYSTEMLOGPROV("Failed to get ACPI entry or none available"); + } + } else { + SYSTEMLOGPROV("ACPI fixup disabled, continuing"); + } +} + +IOService* USBToolBox::probe(IOService* provider, SInt32* score) { + DEBUGLOG("%s: probe start", provider->getName()); + + if (OSDynamicCast(IOPCIDevice, provider)) { + // We are attached to the PCI device + this->pciDevice = provider; + } else if (checkClassHierarchy(provider, "AppleUSBHostController")) { + // We are already attached to the instance + this->controllerInstance = provider; + this->controllerInstance->retain(); + this->pciDevice = this->controllerInstance->getParentEntry(gIOServicePlane); // Should not be released by the caller. + if (!(this->pciDevice)) { + DEBUGLOG("%s: No parent PCI device!", provider->getName()); + return NULL; + } + } else { + DEBUGLOG("%s: Unknown provider class %s!", provider->getName(), provider->getMetaClass()->getClassName()); + return NULL; + } + + if (checkKernelArgument(bootargOff) || checkProperty(propertyOff)) { + SYSTEMLOGPROV("disable argument specified, exiting"); + return NULL; + } + + this->pciDevice->setProperty("UTB Version", versionString); + + removeACPIPorts(); + + if (!(this->controllerInstance)) { + this->controllerInstance = getControllerViaMatching(); + } + if (!(this->controllerInstance)) { + DEBUGLOGPROV("Failed to obtain via matching in probe"); + } else { + mergeProperties(); + DEBUGLOGPROV("Probe early finish"); + return NULL; + } + DEBUGLOGPROV("Probe end"); + return super::probe(provider, score); +} + +bool USBToolBox::start(IOService *provider) { + DEBUGLOGPROV("start start"); + // If we're here we couldn't get the instance in probe + DEBUGLOGPROV("Trying to obtain via matching"); + this->controllerInstance = getControllerViaMatching(); + + if (!(this->controllerInstance)) { + DEBUGLOGPROV("Failed to obtain via matching, falling back to iteration"); + this->controllerInstance = getControllerViaIteration(); + } + + if (!(this->controllerInstance)) { + DEBUGLOGPROV("Failed to obtain via iteration, falling back to notification"); + OSDictionary* matchingDict = createMatchingDictionary(); + + if (!matchingDict) { + SYSTEMLOGPROV("Failed to create matching dict!"); + return false; + } + if (!gIOPublishNotification) { + SYSTEMLOGPROV("IOPublishNotification null!"); + OSSafeReleaseNULL(matchingDict); + return false; + } + // addMatchingNotification(const OSSymbol *type, OSDictionary *matching, SInt32 priority, IOServiceMatchingNotificationHandlerBlock handler) + IONotifier* notifierStatus = addMatchingNotification(gIOMatchedNotification, matchingDict, 0, ^bool (IOService * newService, IONotifier * notifier) { + DEBUGLOGPROV("controller callback start"); + newService->retain(); + mergeProperties(newService); + // addMatchingNotification does not consume a reference on the matching dictionary when the notification is removed, unlike addNotification. + matchingDict->release(); + // These actions are synchronized with invocations of the notification handler, so removing a notification request will guarantee the handler is not being executed. + notifier->remove(); + + DEBUGLOGPROV("controller callback end"); + terminate(); + return true; + }); + DEBUGLOGPROV("Installed controller notifier status: %s", notifierStatus ? "successful" : "failed"); + } else { + mergeProperties(); + } + DEBUGLOGPROV("start exit"); + return super::start(provider); +} + +void USBToolBox::free() { + super::free(); +} diff --git a/USBToolBox/USBToolBox.hpp b/USBToolBox/USBToolBox.hpp new file mode 100644 index 0000000..5edb99e --- /dev/null +++ b/USBToolBox/USBToolBox.hpp @@ -0,0 +1,95 @@ +// +// PortPopulator.hpp +// USBToolBox +// +// Created by Dhinak G on 6/1/20. +// Copyright © 2020-2021 Dhinak G. All rights reserved. +// + +#ifndef USBToolBox_hpp +#define USBToolBox_hpp + +#include +#include +#include + +#define super IOService +#define PRODUCT_NAME USBToolBox + +#ifdef DEBUG +#define TRACELOG kprintf +#else +#define TRACELOG IOLog +#endif + +#define SYSTEMLOG(format, args...) do { TRACELOG("USBToolBox: " format "\n", ##args); } while (false) +#define SYSTEMLOGPROV(format, args...) do { TRACELOG("USBToolBox: %s: " format "\n", pciDevice->getName(), ##args); } while (false) + +// Debug log +#ifdef DEBUG +#define DEBUGLOG(format, args...) SYSTEMLOG(format, ##args) +#define DEBUGLOGPROV(format, args...) SYSTEMLOGPROV(format, ##args) +#else +#define DEBUGLOG(format, args...) +#define DEBUGLOGPROV(format, args...) +#endif + + +class USBToolBox : public IOService { + OSDeclareDefaultStructors(USBToolBox) + + static constexpr const int PARENT_PATH_LENGTH = 512; + static constexpr const int MODEL_NAME_LENGTH = 16; + + static constexpr const uint64_t MAX_WAIT = 180; + static constexpr const uint64_t DEFAULT_WAIT = 1; + static constexpr const uint64_t NANOSECONDS = 1000000000; + // Boot args + static constexpr const char *bootargOff {"-utboff"}; // Disable the kext + static constexpr const char *bootargAcpiOff {"-utbacpioff"}; // Disable RHUB removal from ACPI plane + static constexpr const char *bootargAppleOff {"-utbappleoff"}; // Disable existing `ports` and `ports-count` removal + static constexpr const char *bootargMapOff {"-utbmapoff"}; // Disable the map + static constexpr const char *bootargMatchWait {"utbwait"}; // Modify waitForMatchingService delay + + // Properties + static constexpr const char *propertyOff {"utb-off"}; + static constexpr const char *propertyAcpiOff {"utb-acpi-off"}; + static constexpr const char *propertyAppleOff {"utb-apple-off"}; + static constexpr const char *propertyMapOff {"utb-map-off"}; + static constexpr const char *propertyMatchWait {"utb-wait"}; + + static constexpr const char *versionString = +#ifdef VERBOSE + "VBS-" +#elif defined DEBUG + "DBG-" +#else + "REL-" +#endif + __DATE__ "-" __TIME__; +public: + virtual IOService* probe(IOService* provider, SInt32* score) override; + virtual bool start(IOService* provider) override; + virtual void free() override; +private: + uint64_t matchWait = DEFAULT_WAIT; // Default timeout in seconds + + IORegistryEntry* pciDevice = NULL; + IORegistryEntry* controllerInstance = NULL; + + void installMergeNotifier(const OSSymbol* type, const char* className ); + void deleteProperty(IORegistryEntry* provider, const char* property); + OSDictionary* createMatchingDictionary(); + IORegistryEntry* getControllerViaMatching(); + IORegistryEntry* getControllerViaIteration(); + void mergeProperties(IORegistryEntry* instance = NULL); + void removeACPIPorts(); + + static bool checkClassHierarchy(IORegistryEntry* provider, const char* className); + static inline bool checkKernelArgument(const char* arg) { + uint32_t value = 0; + return PE_parse_boot_argn(arg, &value, sizeof(value)); + }; +}; + +#endif /* USBToolBox_hpp */