Управление обработкой прерываний (irq.h)

API определен в заголовочном файле sysroot-*-kos/include/coresrv/io/irq.h из состава KasperskyOS SDK.

API позволяет управлять обработкой аппаратных прерываний. Аппаратное прерывание – это сигнал процессору от устройства о необходимости немедленно переключиться с исполнения текущей программы на обработку события, связанного с этим устройством. Например, нажатие клавиши на клавиатуре вызывает аппаратное прерывание, которое обеспечивает требуемую реакцию на это нажатие (к примеру, ввод символа).

Аппаратное прерывание возникает при обращении устройства к контроллеру прерываний. Это обращение может осуществляться через линию аппаратного прерывания между устройством и контроллером прерываний или через память MMIO. Во втором случае устройство выполняет запись в память MMIO, вызывая прерывание MSI (англ. Message Signaled Interrupt, MSI).

Каждой линии аппаратного прерывания соответствует одно прерывание с уникальным номером.

Поддерживаются прерывания MSI двух типов: MSI и MSI-X (расширенная версия).

Сведения о функциях API приведены в таблице ниже.

Использование API

Чтобы связать прерывание с его обработчиком, нужно выполнить следующие шаги:

  1. Регистрация прерывания.

    Чтобы зарегистрировать прерывание, возникающее при обращении устройства к контроллеру прерываний через линию аппаратного прерывания, нужно вызвать функцию KnRegisterIrq(). Если такое прерывание требуется зарегистрировать несколько раз, то при каждой регистрации в параметре flags нужно указать флаг MMIO_U_SHARED. Если регистрируемое прерывание используется кодом ядра, то нужно указать флаг MMIO_K_SHARED. Если эти флаги не будут указаны, то доступ к прерыванию получит только тот код, который запросит этот доступ первым (это может быть как код ядра, так и код, исполняющийся в пользовательском режиме). Все последующие попытки регистрации вызовом функции KnRegisterIrq() завершатся с ошибкой rcAlreadyExists, и в диагностический вывод будут добавлены сообщения о проблеме совместного доступа к прерыванию. Если ядру не удастся получиться доступ к прерыванию из-за того, что это прерывание уже зарегистрировано кодом, исполняющимся в пользовательском режиме, то также в диагностический вывод будет добавлено сообщение о проблеме совместного доступа к прерыванию.

    Чтобы зарегистрировать прерывание MSI, нужно вызвать функцию KnIoRegisterMsiIrq(), предварительно создав объект прерывания MSI вызовом функции KnIoAllocMsi(). Дескриптор объекта прерывания MSI можно передать другому процессу через IPC. В один момент времени один объект может ассоциироваться только с одним прерыванием MSI. Повторно использовать объект для регистрации прерывания MSI можно только после закрытия дескриптора ранее зарегистрированного прерывания. После регистрации прерывания MSI дескриптор объекта можно закрыть.

    Данные, полученные через выходные параметры addr и data функции KnIoAllocMsi(), нужно передать контроллеру шины PCIe для настройки прерываний MSI (типа MSI или MSI-X) для заданного устройства. При этом для одного устройства можно настроить одно прерывание типа MSI либо одно или несколько прерываний типа MSI-X. (Функция KnIoAllocMsi() не позволяет выделить последовательность векторов прерываний, что не дает возможности поддерживать несколько прерываний типа MSI для одного устройства.) Если для устройства нужно настроить одно прерывание типа MSI или MSI-X, нужно создать один объект прерывания MSI единственным вызовом функции KnIoAllocMsi(). Если для устройства нужно настроить несколько прерываний типа MSI-X, нужно создать столько же объектов прерываний MSI несколькими вызовами функции KnIoAllocMsi(). Если в системе создано максимально возможное число объектов прерываний MSI, функция KnIoAllocMsi() возвращает rcResourceNotFound. Если в системе создано максимально возможное число объектов прерываний MSI с одинаковым приоритетом прерывания, функция KnIoAllocMsi() возвращает rcFail. Если для устройства создано максимальное число объектов прерываний MSI, функция KnIoAllocMsi() возвращает rcFail.

    Дескриптор прерывания, полученный вызовом функции KnRegisterIrq() или KnIoRegisterMsiIrq(), можно передать другому процессу через IPC.

  2. Привязка потока исполнения к прерыванию вызовом функции KnIoAttachIrq().

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

    Используя дескриптор прерывания, полученный вызовом функции KnRegisterIrq() или KnIoRegisterMsiIrq(), можно привязать к прерыванию только один поток исполнения. В отличие от прерывания MSI к прерыванию, возникающему при обращении устройства к контроллеру прерываний через линию аппаратного прерывания, можно привязать несколько потоков исполнения в одном или нескольких процессах. Чтобы сделать это, нужно использовать разные дескрипторы одного прерывания, полученные при отдельных вызовах функции KnRegisterIrq() с одинаковыми флагами в параметре flags.

    Дескриптор, полученный вызовом функции KnIoAttachIrq(), нельзя передать другому процессу через IPC.

Чтобы запретить (маскировать) прерывание, нужно вызвать функцию KnIoDisableIrq() или KnIoDisableIrqNoSync(). Функция KnIoDisableIrq() возвращает управление после завершения текущей итерации в обработчике маскируемого прерывания за исключением случая, когда вызвана из этого обработчика. В этом случае функция KnIoDisableIrq() ведет себя, как функция KnIoDisableIrqNoSync(), которая всегда возвращает управление, не дожидаясь завершения текущей итерации в обработчике маскируемого прерывания. То есть текущая итерация в обработчике прерывания может быть завершена уже после того, как функция KnIoDisableIrqNoSync() вернула управление. Функцию KnIoDisableIrq() нельзя вызывать из критической секции, которая синхронизируется с критической секцией в обработчике маскируемого или другого прерывания.

Чтобы разрешить (демаскировать) прерывание, нужно вызвать функцию KnIoEnableIrq(). (После регистрации прерывания и привязки к нему потока исполнения это прерывание не требуется демаскировать.)

Хотя функции KnIoDisableIrq(), KnIoDisableIrqNoSync() и KnIoEnableIrq() принимают дескриптор прерывания, через который к прерыванию привязан только один поток исполнения, их действие распространяется на все потоки исполнения, привязанные к этому прерыванию. (Это не относится к прерываниям MSI, к которым может быть привязан только один поток исполнения.)

Чтобы инициировать отвязывание потока исполнения от прерывания, нужно вызвать функцию KnIoDetachIrq() вне потока исполнения, привязанного к прерыванию. Отвязывание выполняет поток исполнения, привязанный к прерыванию, вызовом функции KnThreadDetachIrq() из API thread_api.h.

Обработка прерывания

После выполнения привязки к прерыванию поток исполнения вызывает функцию Call() из API syscalls.h. В результате этого вызова поток исполнения блокируется. При возникновении прерывания или вызове функции KnIoDetachIrq() ядро KasperskyOS отправляет IPC-сообщение процессу, содержащему этот поток. Это IPC-сообщение содержит запрос на обработку прерывания или запрос на отвязывание потока исполнения от прерывания. Когда процесс получает IPC-сообщение, функция Call() в потоке исполнения, привязанном к прерыванию, возвращает управление и предоставляет потоку содержимое IPC-сообщения. Поток исполнения извлекает из IPC-сообщения запрос и обрабатывает прерывание либо выполняет отвязывание от прерывания. Если выполняется обработка прерывания, то по ее завершении сведения об успехе или неуспехе обработки добавляются в ответное IPC-сообщение, которое отправляется ядру следующим вызовом функции Call() в цикле.

При обработке прерывания нужно использовать функции IoGetIrqRequest() и IoSetIrqAnswer(), которые объявлены в заголовочном файле sysroot-*-kos/include/io/io_irq.h из состава KasperskyOS SDK. Эти функции позволяют извлекать из IPC-сообщений и добавлять в IPC-сообщения данные для информационного обмена между ядром и потоком исполнения, привязанным к прерыванию.

Типовой цикл обработки прерывания включает следующие шаги:

  1. Добавление в IPC-сообщение, которое будет отправлено ядру, сведений об успехе или неуспехе обработки прерывания вызовом функции IoSetIrqAnswer().
  2. Отправка IPC-сообщения ядру и получение IPC-сообщения от ядра.

    Чтобы выполнить этот шаг, нужно вызвать функции Call(). В параметре handle требуется указать дескриптор, полученный при вызове функции KnIoAttachIrq(). Через параметр msgOut необходимо задать IPC-сообщение, которое будет отправлено ядру, а через параметр msgIn необходимо задать IPC-сообщение, которое будет получено от ядра.

  3. Извлечение запроса из IPC-сообщения, полученного от ядра, вызовом функции IoGetIrqRequest().
  4. Обработка прерывания или отвязывание от прерывания в зависимости от запроса.

    Если запрос требует выполнить отвязывание от прерывания, то нужно выйти из цикла обработки прерывания и вызвать функцию KnThreadDetachIrq() из API thread_api.h.

Дерегистрация прерывания

Чтобы дерегистрировать прерывание, нужно выполнить следующие шаги:

  1. Выполнить отвязывание потоков исполнения от прерывания.

    Чтобы выполнить этот шаг, нужно использовать функцию KnThreadDetachIrq() из API thread_api.h.

  2. Закрыть дескрипторы, полученные при вызовах функции KnIoAttachIrq().
  3. Закрыть или отозвать дескрипторы прерывания во всех процессах, которые владеют этими дескрипторами.

    В случае с прерыванием MSI дополнительно нужно закрыть или отозвать дескрипторы объекта прерывания MSI во всех процессах, которые владеют этими дескрипторами.

Сведения о функциях API

Функции irq.h

Функция

Сведения о функции

KnRegisterIrq()

Назначение

Регистрирует прерывание, возникающее при обращении устройства к контроллеру прерываний через линию аппаратного прерывания.

Параметры

  • [in] irq – номер прерывания.
  • [in] flags – флаги, задающие параметры прерывания. Флаги определены в заголовочных файлах sysroot-*-kos/include/io/io_irq.h и sysroot-*-kos/include/hal/irqmode.h из состава KasperskyOS SDK.
  • [out] outRid – указатель на дескриптор прерывания.

Возвращаемые значения

В случае успеха возвращает rcOk, иначе возвращает код ошибки.

Дополнительные сведения

В параметре flags можно указать следующие флаги:

  • IRQ_LEVEL_LOW – прерывание возникает при низком уровне сигнала.
  • IRQ_LEVEL_HIGH – прерывание возникает при высоком уровне сигнала.
  • IRQ_EDGE_RAISE – прерывание возникает при повышении уровня сигнала.
  • IRQ_EDGE_FALL – прерывание возникает при снижении уровня сигнала.
  • IRQ_PRIO_LOW – прерывание имеет низкий приоритет.
  • IRQ_PRIO_NORMAL – прерывание имеет средний приоритет.
  • IRQ_PRIO_HIGH – прерывание имеет высокий приоритет.
  • IRQ_PRIO_RT – прерывание имеет наивысший приоритет.
  • IRQ_K_SHARED – совместный доступ с ядром.
  • IRQ_U_SHARED – совместный доступ при множественных регистрациях.

KnIoAttachIrq()

Назначение

Привязывает вызывающий поток исполнения к прерыванию.

Параметры

  • [in] rid – дескриптор прерывания.
  • [out] handle – указатель на клиентский IPC-дескриптор, который используется обработчиком прерывания.

Возвращаемые значения

В случае успеха возвращает rcOk, иначе возвращает код ошибки.

KnIoDetachIrq()

Назначение

Отправляет потоку исполнения запрос, в результате выполнения которого поток должен выполнить отвязывание от прерывания.

Параметры

  • [in] rid – дескриптор прерывания.

Возвращаемые значения

В случае успеха возвращает rcOk, иначе возвращает код ошибки.

KnIoEnableIrq()

Назначение

Разрешает (демаскирует) прерывание.

Параметры

  • [in] rid – дескриптор прерывания.

Возвращаемые значения

В случае успеха возвращает rcOk, иначе возвращает код ошибки.

KnIoDisableIrq()

Назначение

Запрещает (маскирует) прерывание, опционально ожидая завершения текущей итерации в обработчике маскируемого прерывания.

Параметры

  • [in] rid – дескриптор прерывания.

Возвращаемые значения

В случае успеха возвращает rcOk, иначе возвращает код ошибки.

Дополнительные сведения

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

KnIoDisableIrqNoSync()

Назначение

Запрещает (маскирует) прерывание, не ожидая завершения текущей итерации в обработчике маскируемого прерывания.

Параметры

  • [in] rid – дескриптор прерывания.

Возвращаемые значения

В случае успеха возвращает rcOk, иначе возвращает код ошибки.

KnIoAllocMsi()

Назначение

Создает объект прерывания MSI.

Параметры

  • [in] order – фиктивный параметр (зарезервирован для возможного использования в будущем). Нужно указать 0.
  • [in] deviceId – адрес устройства на шине PCIe в формате BDF.
  • [in] priority – приоритет прерывания. Константы с возможными значениями приоритета определены в заголовочном файле sysroot-*-kos/include/io/io_irq.h из состава KasperskyOS SDK.
  • [out] addr – указатель на адрес памяти MMIO, куда устройству необходимо выполнить запись, чтобы вызвать прерывание (Message Address по спецификации PCIe).
  • [out] data – указатель на данные, которые устройству необходимо записать в память MMIO по адресу, полученному через параметр addr, чтобы вызвать прерывание (Message Data по спецификации PCIe).
  • [out] msi – указатель на дескриптор объекта прерывания MSI.

Возвращаемые значения

В случае успеха возвращает rcOk, иначе возвращает код ошибки.

Дополнительные сведения

В параметре priority можно указать следующие флаги:

  • IRQ_PRIO_LOW – прерывание имеет низкий приоритет.
  • IRQ_PRIO_NORMAL – прерывание имеет средний приоритет.
  • IRQ_PRIO_HIGH – прерывание имеет высокий приоритет.
  • IRQ_PRIO_RT – прерывание имеет наивысший приоритет.

KnIoRegisterMsiIrq()

Назначение

Регистрирует прерывание MSI.

Параметры

  • [in] msi – дескриптор объекта прерывания MSI.
  • [in] index – фиктивный параметр (зарезервирован для возможного использования в будущем). Нужно указать 0.
  • [out] irq – указатель на дескриптор прерывания.

Возвращаемые значения

В случае успеха возвращает rcOk, иначе возвращает код ошибки.

В начало