Обращение к совместно используемой памяти ввода/вывода
В зависимости от устройства и типа шины, совместно используемая память ввода/вывода в архитектуре компьютера может быть отображена в различные диапазоны физических адресов. Как правило, имеют место следующие ситуации.
Для большинства устройств, подключенных к шине ISA, совместно используемая память ввода/вывода отображается в 16-битовые физические адреса в диапазоне от ОхаОООО до Oxffff. Таким образом, образуется дыра” от 640 Кбайт до 1 Мбайт Для устройств, подключенных к шине PCI, совместно используемая память ввода/вывода отображается в 32-битовые физические адреса вблизи границы 4 Гбайт. Управлять такими устройствами намного проще.
Несколько лет назад компания Intel представила стандарт AGP (Accelerated Graphics Port, Ускоренный графический порт), который является развитием PCI для высокопроизводительных графических карт. В дополнение к собственной совместно используемой памяти ввода/вывода, такая карта способна напрямую обращаться к участкам оперативной памяти на материнской плате при помощи специальной электронной схемы GART (Graphics Address Remapping Table, Таблица преобразования графических адресов). Схема GART позволяет AGP-картам достигать намного большей скорости передачи данных, чем у старых PCI-карт. Впрочем, ядру, на самом деле, безразлично, где расположена физическая память, и память, отображенная согласно GART, для него не отличается от других типов совместно используемой памяти ввода/вывода.
Как драйвер устройства обращается к тому или иному участку совместно используемой памяти ввода/вывода? Начнем с архитектуры PC, с которой разобраться проще, а затем включим в обсуждение и другие архитектуры.
Вспомните, что программы ядра пользуются линейными адресами, и адреса в совместно используемой памяти ввода/вывода можно выразить числами, превышающими page offset. Далее мы будем предполагать, что значение page offset равно ОхсООООООО, т. е. линейные адреса ядра расположены в четвертом гигабайте.
Драйверы устройств должны перевести физические адреса совместно используемой памяти ввода/вывода в линейные адреса в пространстве ядра. В архитектуре PC этого можно достичь, выполнив логическую операцию ИЛИ над 32-битовым физическим адресом и константой ОхсООООООО. Предположим, например, что ядру нужно сохранить в переменной ti значение, расположенное в памяти ввода/вывода по физическому адресу 0xc000b0fe4, а в перемен
ной t2 — значение, расположенное по адресу OxfcOOOOOO. Казалось бы, следующие два оператора справятся с этой задачей:
tl = ((unsigned char )(0xc00b0fe4)); t2 = ((unsigned char )(OxfcOOOOOO));
На этапе инициализации ядро отображает доступные физические адреса оперативной памяти в начальный участок четвертого гигабайта линейного адресного пространства. Следовательно, блок управления страницами отобразит линейный адрес 0xc00b0fe4, указанный в первом операторе, обратно в оригинальный физический адрес памяти ввода/вывода 0xc000b0fe4, который попадает в дыру ISA” между 640 Кбайт до 1 Мбайт (. Все работает как нельзя лучше.
А вот со вторым оператором возникают проблемы, потому что физический адрес ввода/вывода больше максимального физического адреса оперативной памяти системы. Следовательно, линейный адрес OxfcOOOOOO не соответствует физическому адресу OxfcOOOOOO. В таких случаях Таблицы Страниц ядра должны быть модифицированы так, чтобы они включали в себя линейный адрес, который отображается на физический адрес ввода/вывода. Это можно сделать, вызвав функцию ioremap или ioremap nocache . Первая, во многом аналогичная функции vmallocO, вызывает get vm area о, чтобы создать новый дескриптор vm struct для интервала линейных адресов, имеющего размер, равный размеру требуемой области совместно используемой памяти ввода/вывода. Затем функции обновляют соответствующие записи в канонических Таблицах Страниц ядра. Функция ioremap nocache отличается от ioremap тем, что она, кроме прочего, отключает аппаратный кэш, когда ссылается на переотображенные линейные адреса.
Итак, корректный вид второго оператора должен быть примерно таким:
io_mem = ioremap(OxfbOOOOOO, 0x200000);
t2 = ((unsigned char )(io_mem + 0x100000));
Здесь первый оператор создает двухмегабайтовый интервал линейных адресов, отображающий физические адреса, начиная с OxfbOOOOOO, а второй читает содержимое памяти по адресу OxfcOOOOOO. Чтобы впоследствии снять отображение, драйвер устройства должен вызвать функцию iounmap .
В некоторых других архитектурах, отличных от архитектуры PC, к совместно используемой памяти ввода/вывода нельзя обратиться путем простого разыменования линейного адреса, указывающего на место в физической памяти. Поэтому в Linux определены следующие архитектурно-независимые функции, которые следует вызывать при обращении к совместно используемой памяти ввода/вывода:
- readb , readw , readi — читают, соответственно один, два или четыре байта из совместно используемой памяти ввода/вывода;
- writeb , writew , writei — записывают, соответственно, один, два или четыре байта в совместно используемую память ввода/вывода;
- memcpy_f romio , memcpy_toio — копируют блок данных ИЗ СОВМеСТНО используемой памяти ввода/вывода в динамическую память и, соответственно, наоборот.
- memset ioO — заполняет область совместно используемой памяти ввода/вывода фиксированным значением.
Рекомендуется следующий способ обращения к участку памяти ввода/вывода с адресом oxfcoooooo:
io_mem = ioremap(OxfbOOOOOO, 0x200000); t2 = readb(io_mem + 0x100000));
Благодаря этим функциям удается скрыть специфику платформеннозависимых способов обращения к совместно используемой памяти ввода/вывода.
Предыдущая страница | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | Следующая страница