Проблема с пониманием инициализации драйвера IDE

Вопрос или проблема

При разработке драйвера ATA PIO я следовал этим руководствам: PCI IDE Controller и ATA PIO Mode. Я успешно реализовал простой минималистичный драйвер в QEMU для i386. Однако я решил рефакторить его, чтобы сделать драйвер более надежным.

Теперь у меня возникают трудности с пониманием правильного процесса инициализации драйвера. Моя предыдущая реализация жестко задавала основные порты ATA и поддерживала только основной диск. Однако в вики OSDev предлагается динамически обнаруживать адреса портов, а не полагаться на эти жестко прописанные значения, соответствующие совместимости.

Я написал аналогичную функцию, подобную той, что предоставлена на вики OSDev (см. код ниже), но моя версия не включает побитовые операции, потому что я не полностью понимаю их. Мог бы кто-то объяснить, что делает эта операция и почему она важна?

Пример кода из OSDev:

void ide_initialize(unsigned int BAR0, unsigned int BAR1, unsigned int BAR2, unsigned int BAR3, unsigned int BAR4) {

   int j, k, count = 0;

   // 1- Обнаружить I/O порты, которые управляют контроллером IDE:
   channels[ATA_PRIMARY  ].base  = (BAR0 & 0xFFFFFFFC) + 0x1F0 * (!BAR0);
   channels[ATA_PRIMARY  ].ctrl  = (BAR1 & 0xFFFFFFFC) + 0x3F6 * (!BAR1);
   channels[ATA_SECONDARY].base  = (BAR2 & 0xFFFFFFFC) + 0x170 * (!BAR2);
   channels[ATA_SECONDARY].ctrl  = (BAR3 & 0xFFFFFFFC) + 0x376 * (!BAR3);
   channels[ATA_PRIMARY  ].bmide = (BAR4 & 0xFFFFFFFC) + 0; // Bus Master IDE
   channels[ATA_SECONDARY].bmide = (BAR4 & 0xFFFFFFFC) + 8; // Bus Master IDE

Какова цель побитовой операции (& 0xFFFFFFFC) в этом коде и почему она необходима для правильной инициализации? Кроме того, как работает умножение (!BAR0) в этом контексте?

Я пытался понять, как работают процессорные порты ввода-вывода и различия между Программированным вводом-выводом (PIO) и Прямым доступом к памяти (DMA). Также я хотел бы получить объяснение того, как драйвер обнаруживает правильные порты ввода-вывода через PCI.

Вам следует ознакомиться с копией спецификации PCI. Низкие биты каждого BAR определяют, является ли это областью памяти или I/O, плюс некоторую другую информацию для областей памяти. Если BAR0 является пространством I/O на 0x1F0, регистр на самом деле будет читать 0x1F1. Необходимо удалить нижние биты, чтобы получить фактический номер порта I/O. Оператор !BAR0 — это слишком умный способ предоставить значение по умолчанию. Если BAR0 равен 0, то значение будет 0x1F0.

BIOS выделяет память и пространство ввода-вывода во время загрузки. BAR-ы, с помощью довольно хитрой схемы, описанной в спецификациях, могут использоваться BIOS для определения того, сколько памяти и пространства ввода-вывода требует устройство. Затем он делит доступное пространство и назначает диапазоны адресов.

Когда ваша система имеет операционную систему, ядро сканирует шину PCI (и таблицы BIOS, такие как DSDT), чтобы определить, какие устройства присутствуют. Затем оно понимает, какие драйверы нужны, считывает назначения BIOS из регистров BAR и сообщает драйверу, какие регистры были назначены. Если вы создаете свою систему, то вам решать, как это обрабатывать, но для устройств PCI информация, которая вам нужна, находится в этих регистрах BAR.

Порты ввода-вывода — это наследие оригинального 8086. Адресное пространство крайне ограничено, а доступ медленный, поэтому порты ввода-вывода не используются устройствами XXI века. Большинство современных устройств используют отображение памяти, где устройство отображается в виртуальную память и доступ к нему осуществляется напрямую. Для этого и предназначен ваш BAR4. В приведенном выше коде упоминается “мастер шины” — это когда само устройство использует DMA для непосредственной передачи блоков. Это необходимо настраивать драйвером, записывая регистры, но сама передача происходит без участия ЦП.

Ответ или решение

Конечно! Давайте подробно рассмотрим и объясним ключевые аспекты инициализации драйвера ATA PIO, начиная с вашего запроса о побитовых операциях и динамическом определении адресов портов.

1. Понятие адреса портов и BAR

Во-первых, стоит упомянуть, что во время инициализации устройства PCI, BIOS назначает адреса базовых регистров (BAR) для каждого устройства. Эти регистры могут указывать как на область ввода-вывода (I/O), так и на область памяти. Низкие биты каждого BAR содержат информацию о типе области, а также другие параметры, такие как размер.

2. Побитовая операция & 0xFFFFFFFC

Теперь рассмотрим побитовую операцию & 0xFFFFFFFC, которая используется в вашем примере кода. Эта операция нужна для очистки младших двух битов адреса, которые могут указывать на специфические качества адреса (например, то, что это — адрес области ввода-вывода), а также обеспечивают выравнивание адреса по 4 байта, что является требованием для многих архитектур. В случае I/O пространств, выравнивание по 4 байта означает, что адреса будут кратны 4, что позволяет избежать потенциальных проблем при обращении к регистрам устройства.

Применяя BAR0 & 0xFFFFFFFC, вы гарантируете, что адрес, который будет использоваться для доступа к регистрам устройства, корректен и безопасен для работы вашего драйвера.

3. Оператор (!BAR0)

Часть кода, которая включает (!BAR0), использует оператор логического отрицания. Если BAR0 равен 0, то !BAR0 будет равен 1, и выражение 0x1F0 * (!BAR0) вернет 0x1F0. Таким образом, если BAR0 указывает на допустимый адрес (не равен 0), к этому адресу будет добавлено значение 0. Это позволяет задать значение по умолчанию для ситуации, когда устройство не инициировано или не установлено в системе.

4. Процесс обнаружения I/O портов через PCI

Когда операционная система запускается, ядро сканирует шину PCI, чтобы узнать, какие устройства подключены. Ядро использует информацию из BAR-REGISTERS, чтобы определить назначенные адреса для каждого устройства. В вашем случае, если вы разрабатываете собственную систему, вам необходимо самостоятельно обрабатывать эту информацию, и для этого вы будете использовать адреса, указанные в регистре BAR, чтобы инициализировать драйвер.

5. Различие между PIO и DMA

Ваша ссылка на различия между Programmed I/O (PIO) и Direct Memory Access (DMA) также важна. PIO – это метод, при котором процессор управляет всеми операциями ввода-вывода, что может быть медленным. DMA позволяет устройствам обмениваться данными напрямую с оперативной памятью, минуя процессор, что значительно увеличивает производительность.

Вывод

Таким образом, правильная инициализация вашего драйвера involves использование побитовых операций для обеспечения корректного доступа к регистрам устройства, а также осознание того, как управляются адреса через PCI. Правильная обработка инициализации, включая динамическое определение адресов, позволит вашей системе работать более эффективно и совместимо с различными устройствами.

Если у вас остались дополнительные вопросы или нужна помощь с конкретными аспектами инициализации драйвера, пожалуйста, дайте знать!

Оцените материал
Добавить комментарий

Капча загружается...