Создание драйвера устройства с XCode

В этом учебном руководстве Вы изучаете, как создать драйвер устройства Набора I/O для OS X. Вы создаете простой драйвер, распечатывающий текстовые сообщения, но фактически не управляющий устройством. Это учебное руководство не касается процесса для загрузки, или отладка Вашего драйвера — видят Отладку Расширения ядра с GDB после завершения этого учебного руководства для получения информации о загрузке и отладке.

Если Вы незнакомы с XCode, сначала считайте XCode Быстрый Гид.

План действий

Вот существенные шаги, которые Вы выполните:

  1. Ознакомьте Себя с Архитектурой Набора I/O

  2. Создайте новый проект

  3. Отредактируйте информационный список свойств

  4. Заполните заголовочный файл

  5. Реализуйте водительские точки входа

  6. Добавьте объявления библиотеки

  7. Подготовьте драйвер к загрузке

Это учебное руководство предполагает, что Вы зарегистрированы как администратор Вашей машины, которая необходима для использования sudo команда.

Ознакомьте Себя с Архитектурой Набора I/O

Каждый драйвер Набора I/O основывается на семье I/O Kit, наборе классов C++, реализующих функциональность, которая характерна для всех устройств определенного типа. Примеры семей I/O Kit включают устройства хранения (диски), сетевые устройства и устройства интерфейса пользователя (такие как клавиатуры).

Драйвер Набора I/O связывается с устройством, которым он управляет через объект провайдера, обычно представляющий шинное соединение для устройства. Объекты провайдера, делающие так, упоминаются как куски.

Драйвер Набора I/O загружается в ядро автоматически, когда это соответствует против устройства, представленного куском. Драйвер соответствует против устройства путем определения одного или более лиц, описаний типов устройства, которым может управлять драйвер.

После того, как драйвер Набора I/O соответствует против устройства и загрузок в ядро, он направляет I/O для устройства, а также службы продажи, связанные с устройством, такие как обеспечение микропрограммного механизма обновления.

Прежде чем Вы начнете создавать свой собственный драйвер, необходимо удостовериться, что Вы понимаете архитектуру Набора I/O путем чтения Обзора архитектуры в Основных принципах IOKit.

Создайте новый проект

Создание проекта драйвера Набора I/O в XCode так же просто как выбор надлежащего шаблона проекта и обеспечение имени.

  1. Запуск XCode.

  2. Выберите File> New> New Project. Панель New Project появляется.

  3. В панели New Project выберите System Plug-in из списка категорий проектов слева. Выберите IOKit Driver из списка шаблонов справа. Нажать Далее.

  4. На экране, появляющемся, войти MyDriver для названия продукта введите идентификатор компании и нажмите Далее.

  5. Выберите расположение для проекта и нажмите Create.

    XCode создает новый проект и выводит на экран его окно проекта. Необходимо видеть что-то вроде этого:

    ../art/New_IOKitDriver.jpg

    Новый проект содержит несколько файлов, включая исходный файл, MyDriver.cpp, который не содержит кода.

  6. Удостоверьтесь, что kext создает для корректной архитектуры.

    (Если Вы не видите экран выше, выберите MyDriver под Целями. Выберите вкладку Build Settings. Щелкните по треугольнику раскрытия, следующему за Архитектурой.)

    Затем для Создания Активной Архитектуры Только удостоверяются, что выбрали No — это особенно важно при выполнении 32-разрядного ядра на 64-разрядной машине.

Отредактируйте информационный список свойств

Как все пакеты, драйвер устройства содержит информационный список свойств, описывающий драйвер. Значение по умолчанию Info.plist файл, создаваемый XCode, содержит шаблонные значения, которые необходимо отредактировать для описания драйвера.

Драйвер устройства Info.plist файл находится в формате XML. Каждый раз, когда возможно, необходимо просмотреть и отредактировать файл из XCode или в Редакторе Списка свойств приложение. Таким образом Вы помогаете гарантировать, чтобы Вы не добавляли элементы (такие как комментарии), который не может быть проанализирован ядром во время ранней начальной загрузки.

  1. Нажмите Info.plist в окне проекта XCode.

    XCode выводит на экран Info.plist файл в области редактора. Необходимо видеть элементы файла списка свойств, как показано на рисунке 1.

    Рисунок 1  MyDriver Info.plist

    По умолчанию редактор списка свойств XCode маскирует фактические ключи и значения списка свойств. Видеть фактические ключи и значения, Щелчок управления где угодно в редакторе списка свойств и выбрать Show Raw Keys/Values из контекстного меню.

  2. Измените значение CFBundleIdentifier свойство для использования уникального префикса пространства имен.

    На строке для CFBundleIdentifier, дважды щелкните в Столбце значений для редактирования его. Выбрать com.yourcompany и измените его на com.MyCompany (или домен DNS Вашей компании наоборот). Значение должно теперь быть com.MyCompany.driver.${PRODUCT_NAME:rfc1034identifier}.

    Пакеты в OS X обычно используют соглашение о присвоении имен обратного DNS избежать конфликтов пространства имен. Это соглашение особенно важно для kexts, потому что все загрузились, kexts совместно используют единое пространство имен для идентификаторов пакета.

    Последняя часть идентификатора пакета по умолчанию, ${PRODUCT_NAME:rfc1034identifier}, когда Вы разрабатываете свой проект, заменяется установкой сборки Названия продукта для цели драйвера.

  3. Добавьте индивидуальность к своему водительскому IOKitPersonalities словарь.

    Щелкните IOKitPersonalities свойство для выбора его затем щелкните по его треугольнику раскрытия так, чтобы это указало вниз.

    Щелкните по Новому Дочернему символу по правой стороне выбранной строки. Свойство назвало New item появляется как дочерний элемент IOKitPersonalities свойство. Измените имя New item к MyDriver.

    Сделайте MyDriver элемент словарь. Щелчок управления это и выбирает Value Type> Dictionary из контекстного меню.

    Ваш драйвер устройства требует одной или более записей в IOKitPersonalities словарь его информационного списка свойств. Этот словарь определяет свойства, используемые для соответствия Вашего драйвера к устройству и загрузке его.

  4. Заполните словарь индивидуальности.

    Создайте дочерний элемент для MyDriver словарь. Переименуйте дочерний элемент от New item к CFBundleIdentifier. Скопируйте и вставьте значение от верхнего уровня списка свойств CFBundleIdentifier значение (com.MyCompany.driver.${PRODUCT_NAME:rfc1034identifier}) как значение.

    Создайте второй дочерний элемент для MyDriver словарь. Переименуйте дочерний элемент к IOClass. Войти com_MyCompany_driver_MyDriver как значение. Обратите внимание на то, что это - то же значение что касается CFBundleIdentifier, кроме него разделяет его элементы underbars вместо точек. Это значение используется в качестве имени класса для Вашего драйвера устройства.

    Создайте третий дочерний элемент для MyDriver словарь. Переименуйте дочерний элемент к IOKitDebug. Войти 65535 как значение и изменение тип значения от Строки до Числа. При указании ненулевого значения для этого свойства драйвер обеспечивает полезную отладочную информацию, когда это соответствует и загружается. При создании драйвера для общедоступного выпуска необходимо указать 0 как значение для этого свойства или удаляют его полностью.

    Создайте еще два дочерних элемента для MyDriver словарь. Присвойте их имена и значения согласно Таблице 1.

    Таблица 1  MyDriver значения словаря индивидуальности

    Имя

    Значение

    IOProviderClass

    IOResources

    IOMatchCategory

    com_MyCompany_driver_MyDriver

    Эти элементы вместе определяют успешное соответствие для Вашего драйвера, так, чтобы он мог быть загружен. Они служат следующим целям:

    • IOProviderClass указывает, что класс провайдера возражает, что Ваш драйвер может соответствовать на. Обычно драйвер устройства соответствует на куске, управляющем портом, с которым подключено Ваше устройство. Например, если Ваш драйвер соединяется с шиной PCI, необходимо указать IOPCIDevice как Ваш водительский класс провайдера. В этом учебном руководстве Вы создаете виртуальный драйвер без устройства, таким образом, это соответствует на IOResources.

    • IOMatchCategory позволяет другим драйверам соответствовать на том же устройстве как Ваш драйвер, пока отличаются значения драйверов для этого свойства. Драйвер этого учебного руководства соответствует на IOResources, специальный класс провайдера, обеспечивающий ресурсы в масштабе всей системы, таким образом, он должен включать это свойство, чтобы позволить другим драйверам соответствовать на IOResources также. При разработке драйвера Вы не должны включать это свойство, если Ваш драйвер не соответствует на устройстве, которое другой драйвер может соответствовать на, такие как последовательный порт с многократными устройствами, присоединенными к нему.

    Когда Вы закончили добавлять элементы списка свойств, список должен быть похожим на пример, показанный на рисунке 2.

    Рисунок 2  Info.plist записи после дополнений
  5. Выберите File> Save для сохранения изменений.

Заполните заголовочный файл

Открытый MyDriver.h в Вашем проекте Source папка. Заголовочный файл по умолчанию не содержит кода. Рисунок 3 показывает, где найти MyDriver.h файл в окне проекта.

Рисунок 3  , Просматривающий исходный код в XCode

Отредактируйте содержание MyDriver.h соответствовать код в Перечислении 1.

Перечисление 1  MyDriver.h содержание файла

 
#include <IOKit/IOService.h>
class com_MyCompany_driver_MyDriver : public IOService
{
OSDeclareDefaultStructors(com_MyCompany_driver_MyDriver)
public:
    virtual bool init(OSDictionary *dictionary = 0);
    virtual void free(void);
    virtual IOService *probe(IOService *provider, SInt32 *score);
    virtual bool start(IOService *provider);
    virtual void stop(IOService *provider);
};
 

Заметьте что первая строка MyDriver.h включает заголовочный файл IOService.h. Этот заголовочный файл определяет многие методы и службы то использование драйверов устройств. Заголовочный файл расположен в IOKit папка Kernel.framework. Когда Вы разрабатываете свой собственный драйвер, убедиться включать только заголовочные файлы от Kernel.framework (в дополнение к заголовочным файлам Вы создаете), потому что только эти файлы имеют значение в среде ядра. При включении других заголовочных файлов драйвер мог бы скомпилировать, но ему не удается загрузиться, потому что функции и службы, определенные в тех заголовочных файлах, не доступны в ядре.

Обратите внимание на то, что при разработке собственного драйвера необходимо заменить экземпляры com_MyCompany_driver_MyDriver с именем Вашего водительского класса.

В заголовочном файле каждого класса драйвера, OSDeclareDefaultStructors макрос должен быть первой строкой в объявлении класса. Макрос берет один параметр: имя класса. Это объявляет конструкторов класса и деструкторы для Вас, таким образом который ожидает I/O Кит.

Реализуйте водительские точки входа

  1. Открытый MyDriver.cpp в Вашем проекте Source папка. Файл по умолчанию не содержит кода.

  2. Редактирование MyDriver.cpp соответствовать код в Перечислении 2.



    Перечисление 2  MyDriver.cpp содержание файла

     
    #include <IOKit/IOLib.h>
    #include "MyDriver.h"
     
    // This required macro defines the class's constructors, destructors,
    // and several other methods I/O Kit requires.
    OSDefineMetaClassAndStructors(com_MyCompany_driver_MyDriver, IOService)
     
    // Define the driver's superclass.
    #define super IOService
     
    bool com_MyCompany_driver_MyDriver::init(OSDictionary *dict)
    {
        bool result = super::init(dict);
        IOLog("Initializing\n");
        return result;
    }
     
    void com_MyCompany_driver_MyDriver::free(void)
    {
        IOLog("Freeing\n");
        super::free();
    }
     
    IOService *com_MyCompany_driver_MyDriver::probe(IOService *provider,
        SInt32 *score)
    {
        IOService *result = super::probe(provider, score);
        IOLog("Probing\n");
        return result;
    }
     
    bool com_MyCompany_driver_MyDriver::start(IOService *provider)
    {
        bool result = super::start(provider);
        IOLog("Starting\n");
        return result;
    }
     
    void com_MyCompany_driver_MyDriver::stop(IOService *provider)
    {
        IOLog("Stopping\n");
        super::stop(provider);
    }
     

    OSDefineMetaClassAndStructors макрос должен появиться перед определением любого из методов класса. Этот макрос берет два параметра: имя Вашего класса и имя суперкласса Вашего класса. Макрос определяет конструкторов класса, деструкторы и несколько других методов, требуемых Набором I/O.

    Это перечисление включает методы точки входа что использование Набора I/O для доступа к драйверу. Эти точки входа служат следующим целям:

    • init метод является методом первой инстанции, обратился к каждому экземпляру Вашего класса драйвера. Это вызывают только один раз на каждом экземпляре. free метод является последним методом, обратился к любому объекту. В любых выдающихся ресурсах, выделенных драйвером, нужно избавиться free. Обратите внимание на то, что init метод воздействует на объекты в общем; это должно использоваться только для подготовки объектов получить вызовы. Фактическая функциональность драйвера должна быть установлена в start метод.

    • probe метод вызывают, если Ваш драйвер должен связаться с аппаратными средствами, чтобы определить, существует ли соответствие. Этот метод должен оставить аппаратные средства в хорошем состоянии, когда это возвращается, потому что другие драйверы могут зондировать аппаратные средства также.

    • start метод говорит драйверу начинать управлять аппаратными средствами. После start вызывается, драйвер может начать направлять I/O, публикуя куски, и продав службы. stop метод является первым методом, который вызовут, прежде чем будет разгружен Ваш драйвер. Когда stop вызывается, Ваш драйвер должен очистить любое состояние, которое он создал в start метод. start и stop методы говорят с аппаратными средствами через Ваш водительский класс провайдера.

    IOLog функция является ядром, эквивалентным из printf для драйвера Набора I/O.

  3. Сохраните свои изменения путем выбора File> Save.

  4. Разработайте свой проект путем выбора Build> Build. Фиксируйте любые ошибки компилятора перед продолжением.

Добавьте объявления библиотеки

Поскольку kexts соединяются во время загрузки, kext должен перечислить свои библиотеки в его информационном списке свойств с OSBundleLibraries свойство. На этом этапе создания Вашего драйвера необходимо узнать, каковы те библиотеки. Лучший способ сделать так состоит в том, чтобы работать kextlibs инструмент на Вашем созданном kext и копии его вывод в Ваш kext’s Info.plist файл.

Выполненный kextlibs на Драйвере

kextlibs программа командной строки, которую Вы выполняете с Терминальным приложением. Его цель состоит в том, чтобы идентифицировать библиотеки, против которых должен соединиться Ваш kext.

  1. Запустите Терминальное приложение, расположенное в /Applications/Utilities.

  2. В Окне терминала переместитесь в каталог, содержащий Ваш драйвер.

    XCode хранит Ваш драйвер в Debug папка build папка Вашего проекта (если Вы не выбрали различную конфигурацию сборки или установили различное расположение для продуктов сборки с помощью Предпочтительного диалогового окна XCode):

    $ cd MyDriver/build/Debug

    Этот каталог содержит Ваш драйвер. Это должно иметь имя MyDriver.kext. Это имя формируется из Названия продукта, как установлено в настройках сборки Вашей цели и суффиксе, в этом случае .kext.

  3. Выполненный kextlibs на Вашем драйвере с -xml флаг командной строки.

    Эта команда ищет все неразрешенные символы в исполнимой программе Вашего расширения ядра среди установленных расширений библиотеки (в /System/Library/Extensions/) и распечатывает фрагмент XML, подходящий для вставки в Info.plist файл. Например:

    $ kextlibs -xml MyDriver.kext
            <key>OSBundleLibraries</key>
            <dict>
                    <key>com.apple.kpi.iokit</key>
                    <string>10.2</string>
                    <key>com.apple.kpi.libkern</key>
                    <string>10.2</string>
            </dict>
  4. Удостовериться kextlibs вышедший с успешным состоянием путем проверки переменной оболочки $?.

    $ echo $?
    0

    Если kextlibs печать любые ошибки или выходы с ненулевым состоянием, это, возможно, было неспособно определить местоположение некоторых символов. Для этого учебного руководства известны библиотеки, но в общем использовании необходимо использовать kextfind инструмент для нахождения библиотек для любых символов этим kextlibs не может расположиться. Посмотрите Определяют местоположение Kexts.

  5. Выберите вывод XML kextlibs и выберите Edit> Copy.

Добавьте объявления библиотеки к информационному списку свойств

Ранее Вы отредактировали информационный список свойств с XCode графический редактор списка свойств. Для этой работы, однако, необходимо отредактировать информационный список свойств как текст.

  1. Щелчок управления Info.plist в окне проекта XCode, затем выберите Open As> Source Code File из контекстного меню.

    XCode выводит на экран Info.plist файл в области редактора. Необходимо видеть содержания XML файла списка свойств, как показано на рисунке 3. Обратите внимание на то, что ключи словаря и значения перечислены последовательно.

    Рисунок 4  MyKext Info.plist файл как текст
  2. Выберите все строки, определяющие пустой словарь OSBundleLibraries.

            <key>OSBundleLibraries</key>
            <dict/>
  3. Текст вставки в информационный словарь.

    Если kextlibs работал успешно, выберите Edit> Paste для вставки текста, который Вы скопировали с Терминала.

    Если kextlibs не работал успешно, вводят или вставляют этот текст в информационный словарь:

            <key>OSBundleLibraries</key>
            <dict>
                    <key>com.apple.kpi.iokit</key>
                    <string>10.2</string>
                    <key>com.apple.kpi.libkern</key>
                    <string>10.2</string>
            </dict>
  4. Сохраните свои изменения путем выбора File> Save.

  5. Восстановите свой драйвер (выберите Build> Build) с новым информационным списком свойств. Фиксируйте любые ошибки компилятора перед продолжением.

Подготовьте драйвер к загрузке

Теперь Вы готовы подготовить свой драйвер к загрузке. Вы делаете это с kextutil инструмент, который может исследовать kext и определить, в состоянии ли он быть загруженным. kextutil может также загрузить kext в целях разработки, но та функциональность не охвачена в этом учебном руководстве.

Установите водительские полномочия

Kexts имеют строгие требования полномочий (см., что Расширения ядра Имеют Строгие Требования к защите для подробных данных). Самый простой способ установить эти полномочия состоит в том, чтобы создать копию Вашего драйвера как root пользователь. Введите следующее в Терминал из надлежащего каталога и обеспечьте Ваш пароль, когда запрошено:

$ sudo cp -R MyDriver.kext /tmp

Теперь, когда полномочия водительской временной копии корректны, Вы готовы работать kextutil.

Выполненный kextutil

Введите следующее в Терминал:

$ kextutil -n -t /tmp/MyDriver.kext

-n (или -no-load) опция говорит kextutil не загрузить драйвер, и -t (или -print-diagnostics) опция говорит kextutil распечатать результаты его анализа к Терминалу. Если Вы выполнили предыдущие шаги в этом учебном руководстве правильно, kextutil указывает, что kext является загружаемым и должным образом соединен.

No kernel file specified; using running kernel for linking.
Notice: /tmp/MyDriver.kext has debug properties set.
MyDriver.kext appears to be loadable (including linkage for on-disk libraries).

Уведомление свойства отладки вследствие ненулевого значения IOKitDebug свойство в информационном списке свойств. Удостоверьтесь, что Вы устанавливаете это свойство в 0 или удалите его при создании драйвера для выпуска.

Куда пойти затем

Поздравления! Вы теперь записали, создали и подготовили свой собственный драйвер к загрузке. В следующем учебном руководстве в этом ряду, Отлаживая Расширение ядра с GDB, Вы изучите, как загрузить Ваш kext, отладить его и разгрузить его с установкой с двумя машинами.