Два в одном
Начнем с простого, но до сих пор никем не решенного вопроса, то есть уже давно решенного, конечно, но совсем не так. Известно, что поддержка NTFS-разделов представляет собой сплошную проблему. Драйвера, научившиеся писать на NTFS-раздел, появились совсем недавно, да и то лишь затем, чтобы покрасоваться на выставках. Для реальной работы они не пригодны, потому что не очень стабильно работают и несут на своем горбу кучу ограничений. Сжатые файлы, транзакции и куча других вещей все еще не поддерживается. К тому же, NTFS не стоит на месте и хоть и медленно, но совершенствуется, в смысле видоизменяется. Можно ли, хотя бы теоретически, написать 100% совместимый драйвер, "переваривающий" новые версии NTFS без участия программиста? Вопрос совсем не так глуп, каким кажется. Для чего нам горбатиться над своим собственным драйвером, когда под рукой есть уже готовый — NTFS.SYS. Если мы сумеем заставить его заработать под LINUX'ом, все проблемы решатся сами собой.
Вот только не надо кричать, что я ни хрена не разбираюсь в программировании и что на уровне ядра LINUX/BSD отличается от Windows так же, как слонопотам от крокодила. Да, различий очень много, но что-то общее между ними все-таки есть. И Windows, и LINUX, и BSD работают на x86-процессорах в защищенном режиме, используют страничную организацию виртуальной памяти, и взаимодействуют с оборудованием в строго установленном порядке (через иерархию физических и виртуальных шин). Высокоуровневые драйвера такие, например, как NTFS.SYS вообще не касаются оборудования и содержат минимум системно-зависимого кода. Почему же тогда драйвер от одной системы не работает в другой? Главным образом, потому что интерфейс между осью и драйвером в каждом случае различен и еще потому, что драйвер использует библиотеку функций, экспортируемых системой и эти функции у каждой системы свои.
Перенести Windows-драйвер в LINUX/BSD вполне реально! Для этого даже не потребуется его исходный код. Достаточно лишь написать тонкий и несложный "переходник" между драйвером и операционной системой, принимающий запросы и транслирующий их по всем правилами "этикета", а так же перетащить библиотеку функций, необходимых драйверу для работы. О, да! — для этого необходимо уметь программировать! Для простых смертных пользователей такой рецепт совершенно не годится, но тут уже ничего не попишешь. Тем не менее, перенести готовый драйвер намного проще, чем переписать его с нуля. Как минимум не потребуется проводить кропотливую работу по дизассемблированию оригинального кода, заменяющую собой поиск технической документации (которая либо совсем отсутствует, либо отдается только под подписку о неразглашении, зачастую запрещающее открытое распространение исходных текстов). К тому же, при выходе новых версий Windows-драйвера, процедура обновления LINUX/BSD порта существенно обновляется, просто скопировал поверх старого файла и все. Однако, все это теория. Перейдем к деталям.
Ядерная модель Windows NT и всех производных от нее операционных систем (как-то Windows 2000, XP, 2003, Longhorn) достаточно проста. С "внешним" миром ядро связывает Диспетчер Системных Сервисов, "подключенный" к NTDLL.DLL, которая находится уже за "скорлупой" ядра и исполняется в пользовательском режиме. Диспетчер системных сервисов, реализованный в NTOSKRNL.EXE, опирается на Вызываемые Интерфейсы Ядра, часть которых реализована внутри самого NTOSKRNL.EXE, а часть — во внешних драйверах, к числу которых в частности принадлежит диспетчер электропитания. Определенный класс драйверов, называемый Драйверами Устройств и файловой системы, находится в своеобразной "скорлупе" и взаимодействует с диспетчером системных вызовов через диспетчер ввода-вывода реализованный опять-таки в NTOSKRNL.EXE!
Рисунок 3 Windows NT изнутри
Ядро
на котором как на фундаменте держаться все вышеупомянутые компоненты представляет собой просто совокупность низкоуровневых функций, сосредоточенных... правильно! В NTOSKRNL.EXE! Ниже находится только слой абстрагирования от оборудования или сокращенно HAL (Hardware Abstraction Level). Когда-то у Microsoft была идея разделить ядро на системно-зависимую и системно-независимую часть, чтобы упростить перенос Windows на другие платформы, но уже во времена NT 4.x все перемешалось и большая часть системно-зависимых функций попала в NTOSKRNL.EXE, а ныне HAL медленно, но неотвратимо умирает. В нем осталось небольшое количество действительно низкоуровневых функций, непосредственно взаимодействующих с оборудованием. В частности, с портами и с DMA. Но в ядре LINUX/BSD есть свои функции для работы с DMA, так что тащить за собой HAL нам совершенно необязательно, тем более что драйвера взаимодействуют с DMA не напрямую, а через Plug-a-Pray менеджер, который находится в NTOSKRNL.EXE.
Что же касается портов ввода-вывода, то это вообще смех. Вот так, например, выглядит дизассемблерный текст функции READ_PORT_UCHAR, читающий из данного порта беззнаковый байт.
.text:80015A2C
.text:80015A2C public READ_PORT_UCHAR
.text:80015A2C READ_PORT_UCHAR proc near ; CODE XREF: HalGetEnvironmentVariable+2C^p
.text:80015A2C ; HalSetEnvironmentVariable+3D^p
.text:80015A2C
.text:80015A2C arg_0 = dword ptr 4
.text:80015A2C
.text:80015A2C xor eax, eax
.text:80015A2E mov edx, [esp+arg_0]
.text:80015A32 in al, dx
.text:80015A33 retn 4
.text:80015A33 READ_PORT_UCHAR endp
Листинг 1 дизассемблерный листинг функции READ_PORT_UCHAR, выдернутой из HAL'a
Таким образом, если заставить NTOSKRNL.EXE работать в среде чужеродного ему LINUX'а (или BSD), мы получим возможность запускать любые NT-драйвера без какой-либо доработки их двоичного кода. Это не только упрощает задачу переноса, но и снимает проблему так называемых "авторских прав", от которых меня уже блевать тянет. У нас на них можно в принципе и забить, послав Била к черту, но не известно сработает ли это заклинание лет эдак через пять. Интеграция с Европой идет полным ходом, Третий Рим (известный под логотипом U.S.A) рвется в заснеженные леса России, всюду устанавливая свои порядки и законы... Любой обладатель лицензионной копии Windows (или другой программы) вправе вызывать готовый драйвер откуда угодно без каких бы то ни было разрешений и без выплаты дополнительного вознаграждения, но модифицировать двоичный код ему позволят едва ли.
Но мы ведь и не собираемся ничего модифицировать! Мы берем готовый NTOSKRNL.EXE и... Собственно, это все. Работы предстоит не так уж и много. Достаточно просто спроецировать его по адресам, указанным в заголовке PE-файла (а NTOSKRNL.EXE это обычный PE-Файл) и разобраться с таблицей экспорта, используемой драйверами. Короче говоря, мы должны реализовать свой собственный PE-загрузчик и заснуть его в загружаемый модуль ядра или в само ядро. Чтобы не мучаться, можно хлебнуть вина и содрать готовый загрузчик оттуда. Нет, это не спиртной напиток (хакеры предпочитают пиво), это эмулятор Windows'а такой. Он так и называется Wine (Windows Emulator).
Взаимодействие NTOSKRNL.EXE с LINUX/ BSD ядром будет происходить через переходной код, эмулирующий HAL. Этот код мы будем должны написать сами, однако, ничего сложного в этом нет и объем работы предстоит минимальный, поскольку HAL содержит немного функций, да и те простые как самовар. Сложнее подружить диспетчер системных вызовов в внешним миром, то есть миром LINUX/BSD. Основная проблема в том, что интерфейс Диспетчера ни хрена не документирован и к тому же подвержен постоянным изменениям. В Windows 2000 он один, в Windows XP он другой, а потом Microsoft вновь придумает новую пакость и вся наша работа полетит к черту. Поэтому, приходится хитрить и тащить за собой не только NTOSKRNL.EXE, но еще и NTDLL.DLL. Некоторые могут спросить: а на фига, собствеено? Какое отношение NTDLL.DLL имеет к драйверам и ядру? Драйвера его не вызывают, да и сам NTDLL.DLL представляет собой всего лишь набор переходников к NTOSKRNL.EXE.
Так ведь тут-то и зарыта собака (да вы ее помните, ну там самая которая съела кусок мяса)! Интерфейс NTDLL.DLL худо бедно документирован и остается практически неизменным уже на протяжении многих лет, поэтому, его смело можно брать за основу. После этого остается "всего лишь" связать NTDLL.DLL с миром LINUX/BSD, то есть написать транслятор запросов к драйверам. Это не так-то просто сделать, поскольку писать придется достаточно много и работа отнимет не один день и даже не одну неделю, а с учетом отладки потребуется как минимум месяц. Но работа стоит того!!!
По крайней мере, в LINUX/BSD наладится нормальная работа с NTFS и некоторыми другими драйверами ввода-вывода. С видео-картами, правда, все значительно сложнее, поскольку они, как и следует из рис. 3, взаимодействуют отнюдь не с Диспетчером ввода-вывода (который находятся внутри NTOSKRNL.EXE), а с подсистемой win32. В Winnows 2000 она реализована в файле win2k.sys. Как обстоят дела в других системах — не знаю, да это и не важно. Драйвер win2k.sys — лишь малая часть того, что ему нужно для работы и просто так перетащить в LINUX/BSD его не получится. За ним неизбежно потянется все его окружение и написать столько "оберток" будет практически нереально. То есть, реально конечно, но сколько это потребует времени и сил? Переписать видео-драйвер гораздо проще, не говоря уже о том, что в этом случае он будет намного более производителен. Кстати говоря, компании NVIDIA и ATI в последнее время наладили выпуск LINUX/BSD драйверов под наиболее популярные чипсеты, так что проблема снимается сама собой.
Рисунок 4 видео-драйвера под LINUX x86 и x86-64, от ATI
Рисунок 5 видео-драйвера для LINUX x86, x86-64, IA64, FreeBSD x86 и Solaris x86/x64 от NVIDIA