Как перевести адрес виртуальной памяти в физический адрес? - PullRequest
19 голосов
/ 14 декабря 2008

В моей программе на C ++ (в Windows) я выделяю блок памяти и могу убедиться, что он остается заблокированным (неразделенным и непрерывным) в физической памяти (то есть с помощью VirtualAllocEx (), MapUserPhysicalPages () и т. Д.).

В контексте моего процесса, я могу получить адрес виртуальной памяти этого блока, но мне нужно выяснить ФИЗИЧЕСКИЙ адрес памяти этого , чтобы передать его на какое-то внешнее устройство.


1. Можно ли как-нибудь перевести виртуальный адрес в физический в моей программе в режиме USER?

2. Если нет, я могу обнаружить это виртуальное физическое отображение только в режиме KERNEL. Я думаю, это означает, что я должен написать драйвер, чтобы сделать это ...? Знаете ли вы какой-либо легкодоступный драйвер / DLL / API, который я могу использовать, с которым мое приложение (программа) будет взаимодействовать для выполнения перевода?

3. Если мне придется самому написать драйвер, как мне сделать этот перевод? какие функции я использую? mmGetPhysicalAddress () ? Как мне это использовать?

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

Будем весьма благодарны за любые ответы, советы и выдержки из кода !!

Спасибо

Ответы [ 6 ]

11 голосов
/ 14 декабря 2008

В моей программе на C ++ (в Windows) я выделяю блок памяти и могу убедиться, что он остается заблокированным (неразделенным и непрерывным) в физической памяти (т.е. используя VirtualAllocEx (), MapUserPhysicalPages () и т. Д.) .

Нет, вы не можете гарантировать, что он останется заблокированным. Что если ваш процесс завершится сбоем или завершится раньше? Что если пользователь убьет это? Эта память будет использоваться повторно для чего-то другого, и если ваше устройство все еще выполняет DMA, это в конечном итоге приведет к потере / повреждению данных или проверке ошибок (BSOD).

Кроме того, MapUserPhysicalPages является частью Windows AWE (Address Windowing Extensions), которая предназначена для обработки более 4 ГБ ОЗУ в 32-разрядных версиях Windows Server. Я не думаю, что он был предназначен для взлома DMA в пользовательском режиме.

1. Можно ли как-нибудь перевести виртуальный адрес в физический адрес моей программы в режиме USER?

Существуют драйверы, которые позволяют вам это делать, но вы не можете запрограммировать DMA из пользовательского режима в Windows и при этом иметь стабильную и безопасную систему. Разрешение процессу, выполняемому как физическая память с возможностью чтения / записи с ограниченной учетной записью, позволяет этому процессу владеть системой. Если это для одноразовой системы или прототипа, это, вероятно, приемлемо, но если вы ожидаете, что другие люди (особенно платящие клиенты) будут использовать ваше программное обеспечение и ваше устройство, вам следует написать драйвер.

2. Если нет, я могу обнаружить это виртуальное физическое отображение только в режиме KERNEL. Я думаю, это означает, что мне нужно написать драйвер для этого ...?

Это рекомендуемый способ решения этой проблемы.

Знаете ли вы о каком-либо легкодоступном драйвере / DLL / API, который я могу использовать, с которым мое приложение (программа) будет взаимодействовать для выполнения перевода?

Вы можете использовать MDL (список дескрипторов памяти) , чтобы заблокировать произвольную память, включая буферы памяти, принадлежащие процессу пользовательского режима, и преобразовать его виртуальные адреса в физические адреса. Windows также может временно создать MDL для буфера, переданного в вызов DeviceIoControl, используя METHOD_IN_DIRECT или METHOD_OUT_DIRECT.

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

3. Если мне придется написать драйвер самостоятельно, как мне сделать этот перевод? какие функции я использую? Это mmGetPhysicalAddress ()? Как мне его использовать?

Для написания драйвера гораздо больше, чем просто вызов нескольких API. Если вы собираетесь написать драйвер, я бы порекомендовал прочитать как можно больше соответствующих материалов из MSDN и OSR . Также посмотрите на примеры из Windows Driver Kit .

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

Драйверы не являются процессами. Драйвер может выполняться в контексте любого процесса, а также в различных контекстах с повышенными правами (обработчики прерываний и DPC).

5 голосов
/ 19 марта 2011

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

Если вы находитесь в ядре, вы можете просто проверить значение CR3, чтобы найти адрес таблицы базовой страницы, а затем начать свое разрешение.

Эта серия блогов содержит замечательное объяснение того, как это сделать. Вам не нужно никаких средств ОС / API для разрешения виртуальных <-> физических адресов.

Виртуальный адрес: f9a10054

1: kd> .formats 0xf9a10054
Binary:  11111001 10100001 00000000 01010100

Page Directory Pointer Index(PDPI)       11                        Index into

1-я таблица (указатель на страницу каталога Таблица) Индекс каталога страниц (PDI)
111001 101 Индекс в 2-й таблица (Страница каталога таблицы) Страница Индекс таблицы (PTI)
00001 0000 Указатель на 3-й таблица (таблица страниц) Байтный индекс
0000 01010100 0x054, смещение на страницу физической памяти

В его примере они используют windbg,! Dq - чтение физической памяти.

enter image description here

5 голосов
/ 11 июня 2009

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

Вы можете получить описание буфера, называемого MDL или списком дескрипторов памяти, отправив IOCTL (с помощью функции DeviceControl) на ваш драйвер с помощью METHOD_IN_DIRECT или METHOD_OUT_DIRECT. См. Следующую страницу для обсуждения определения IOCTL.

http://msdn.microsoft.com/en-us/library/ms795909.aspx

Теперь, когда у вас есть описание буфера в драйвере для вашего устройства, вы можете заблокировать его, чтобы буфер оставался в памяти в течение всего периода, в течение которого ваше устройство может воздействовать на него. Найдите MmProbeAndLockPages на MSDN.

Ваше устройство может или не может читать или записывать всю память в буфере. Устройство может поддерживать только 32-битный DMA, а на машине может быть более 4 ГБ ОЗУ. Или вы можете иметь дело с машиной, у которой есть IOMMU, GART или какая-то другая технология трансляции адресов. Для этого используйте различные API-интерфейсы DMA, чтобы получить набор логических адресов, подходящих для вашего устройства. Во многих случаях эти логические адреса будут эквивалентны физическим адресам, о которых первоначально задавался ваш вопрос, но не всегда.

Какой API DMA вы используете, зависит от того, может ли ваше устройство обрабатывать списки разброса / сбора и тому подобное. Ваш драйвер в своем коде установки вызовет IoGetDmaAdapter и будет использовать некоторые из возвращаемых им функций.

Обычно вас заинтересуют GetScatterGatherList и PutScatterGatherList. Вы предоставляете функцию (ExecutionRoutine), которая фактически программирует ваше оборудование для выполнения передачи.

В нем много деталей. Удачи.

5 голосов
/ 14 декабря 2008

1) Нет

2) Да, вы должны написать драйвер. Лучше всего будет либо виртуальный драйвер, либо смена драйвера для специального внешнего устройства.

3) Это очень запутанно. MmGetPhysicalAddress должен быть метод, который вы ищете, но я действительно не знаю, как физический адрес отображается на банк / чип / и т.д. на физической памяти.

4) Вы не можете использовать выгружаемую память, потому что она перемещается. Вы можете заблокировать выгружаемую память с помощью MmProbeAndLockPages на MDL, который вы можете построить на памяти, переданной из контекста вызова пользовательского режима. Но лучше выделить не страничную память и передать ее вашему приложению в режиме пользователя.

PVOID p = ExAllocatePoolWithTag( NonPagedPool, POOL_TAG );
PHYSICAL_ADDRESS realAddr = MmGetPhysicalAddress( p );

// use realAddr
4 голосов
/ 14 декабря 2008

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

Но если вызывающий процесс является драйвером, и я использую свое приложение для вызова драйвера для этой функции, я меняю контексты и больше не нахожусь в контексте приложения, когда вызывается подпрограмма mmGetPhysicalAddress

Драйверы не имеют контекста, как в приложениях пользовательского режима; если вы вызываете драйвер через IOCTL или что-то еще, вы обычно (но не гарантированы!) находитесь в контексте вызывающего пользовательского потока. Но на самом деле, это не имеет значения для того, что вы спрашиваете, потому что память в режиме ядра (все, что больше 0x80000000) - это одно и то же отображение независимо от того, где вы находитесь, и вы в конечном итоге выделите память на стороне ядра. Но опять же напишите правильный драйвер . Используйте WDF (http://www.microsoft.com/whdc/driver/wdf/default.mspx),, и это значительно облегчит написание правильного драйвера (хотя все еще довольно сложно, написание драйвера для Windows не так просто)

РЕДАКТИРОВАТЬ: Просто подумал, что я бы выкинул несколько ссылок на книги, чтобы помочь вам, вы должны (даже если вы не занимаетесь написанием драйвера) читать Windows Internals Руссиновича и Соломона (http://www.amazon.com/Microsoft-Windows-Internals-4th-Server/dp/0735619174/ref=pd_bbs_sr_2?ie=UTF8&s=books&qid=1229284688&sr=8-2);) Программирование модели драйвера Microsoft Windows тоже хорошо (http://www.amazon.com/Programming-Microsoft-Windows-Driver-Second/dp/0735618038/ref=sr_1_1?ie=UTF8&s=books&qid=1229284726&sr=1-1)

0 голосов
/ 14 декабря 2008

Подождите, есть еще. Чтобы получить привилегию запуска на 64-битной Vista вашего клиента, вы потратите больше времени и денег на то, чтобы ваш драйвер режима Kernal отказался от моего Microsoft,

...