Здравствуйте. Пишу драйвер под Windows XP для мастера шины PCI. Устройство пишет и читает системную память. Необходимо выделить непрерывную область памяти ядра ~ 128 Мбайт. Драйвер создаётся с помощью Driver Studio 3.0 НЕ удаётся выделить область более чем 39 Мбайт. При MAX_DMA_LENGTH = 0x4000000 и более, полученный *.sys отторгается системой и в диспетчере Устройств появляется характерный жёлтый восклицательный знак. При этом реально в системе 4 Гбайта оперативной памяти. Можно ли как-то перераспределить оперативную память для решения этой задачи? Привожу часть исходного текста: Код (Text): #define MAX_DMA_LENGTH 0x4000000 // 0x100000 is 1 MB // Initialize the device descriptor for the DMA object using the assigned resource DEVICE_DESCRIPTION dd; RtlZeroMemory(&dd, sizeof(dd)); dd.Version = DEVICE_DESCRIPTION_VERSION; dd.Master = TRUE; dd.ScatterGather = FALSE; dd.DemandMode = TRUE; dd.AutoInitialize = FALSE; dd.Dma32BitAddresses = TRUE; dd.IgnoreCount = FALSE; dd.DmaChannel = 0; dd.InterfaceType = PCIBus; dd.DmaWidth = Width32Bits; // PCI default width dd.DmaSpeed = Compatible; dd.MaximumLength = MAX_DMA_LENGTH; // Initialize the DMA adapter object m_Dma.Initialize(&dd, m_Lower.PDO()); Virtual_address_for_user = m_Dma.AllocateCommonBuffer(MAX_DMA_LENGTH,&Address_for_dma,false); if (Virtual_address_for_user == NULL) { m_Dma.Invalidate(); return STATUS_INSUFFICIENT_RESOURCES; } mdl = IoAllocateMdl(Virtual_address_for_user, MAX_DMA_LENGTH, FALSE, FALSE, NULL); if (mdl == NULL) { m_Dma.Invalidate(); return STATUS_INSUFFICIENT_RESOURCES; } MmBuildMdlForNonPagedPool(mdl); Заключайте код в соответствующие bb-теги, читать читать сплошную пелену текста просто невозможно.
Cпасибо большое. Конечно, MAX_DMA_LENGTH задаётся в шестнадцатиричном коде, так что число 0x4000000 соответствует 64 Мб (это моя ошибка) НО попытался поэкспериметировать и получилось, что драйвер успешно ставится примерно до 0x3FF0000 Если ставлю 0x3FFFFFF, то опять отторжение. ТО есть полноценный буфер размером 64 Мб получить так и не удалось.... Правильно ли я понимаю, что это принципиальное ограничение WINDOWS XP и VISTA решает эту проблему? Никаких вариатов с XP быть не может? (редактирование реестра, изменение кода драйвера или ещё что-то)? Буду благодарен за информацию.
>Если ставлю 0x3FFFFFF, то опять отторжение. Всё правильно – мы округлили. Для XP и 2k3 максимальный размер описываемого буфера в байтах – (0x1000 * (2^16 - 1 - sizeof(MDL)) / sizeof(ULONG_PTR)), для Vi – (2GB-0x1000), для W7 – (4GB-0x1000). Чтобы понять, откуда такие ограничения, можно взглянуть на код: Код (Text): 0:000> dt MDL nt!MDL +0x000 Next : Ptr32 _MDL +0x004 Size : Int2B // 2 байта на размер MDL +0x006 MdlFlags : Int2B +0x008 Process : Ptr32 _EPROCESS +0x00c MappedSystemVa : Ptr32 Void +0x010 StartVa : Ptr32 Void +0x014 ByteCount : Uint4B +0x018 ByteOffset : Uint4B XP, 2k3: IoAllocateMdl(VirtualAddress, Length, ...) { ... 00967 // 00968 // If the requested length is greater than 2Gb, then we're not going 00969 // to be able to map the memory, so fail the request. 00970 // 00971 00972 if (Length & 0x80000000) { 00973 return NULL; 00974 } ... 00977 // 00978 // Allocate an MDL from the lookaside list or pool as appropriate. 00979 // 00980 00981 mdl = NULL; 00982 fixedSize = 0; 00983 size = ADDRESS_AND_SIZE_TO_SPAN_PAGES(VirtualAddress, Length); // размер всего региона в страницах 00984 if (size > IOP_FIXED_SIZE_MDL_PFNS) { 00985 allocateSize = sizeof(MDL) + (sizeof(PFN_NUMBER) * size); // размер структуры в байтах 00986 if (allocateSize > MAXUSHORT) { 00987 return NULL; // если не уместить в 2 байта, то вернуть фейл. 00988 } На самом деле, размер структуры не важен – количество PFN может быть вычислено по другим полям (ByteCount). Теперь можно посмотреть на код Vi: Код (Text): 0:000> uf IoAllocateMdl nt!IoAllocateMdl [d:\rtm\base\ntos\io\iomgr\iosubs.c @ 980]: 980 00445dc2 mov edi,edi 980 00445dc4 push ebp 980 00445dc5 mov ebp,esp 980 00445dc7 sub esp,10h 996 00445dca test dword ptr [ebp+0Ch],80000000h // 2GB check 996 00445dd1 je nt!IoAllocateMdl+0x18 (00445dda) ... 1008 00445e06 cmp eax,11h // IOP_FIXED_SIZE_MDL_PFNS 1008 00445e09 mov dword ptr [ebp-10h],ecx 1008 00445e0c jbe nt!IoAllocateMdl+0x55 (00445e17) nt!IoAllocateMdl+0x4c [d:\rtm\base\ntos\io\iomgr\iosubs.c @ 1009]: 1009 00445e0e lea eax,[eax*4+1Ch] 1010 00445e15 jmp nt!IoAllocateMdl+0xb2 (00445e74) ... nt!IoAllocateMdl+0xb2 [d:\rtm\base\ntos\io\iomgr\iosubs.c @ 1017]: 1017 00445e74 push 206C644Dh 1017 00445e79 push eax 1017 00445e7a push 0 1017 00445e7c call nt!ExAllocatePoolWithTag (004ed3b7) // 64k check – отсутствует Конечно, в качестве исключения можно попробовать вручную аллоцировать и инициализировать MDL. Если все используемые функции будут игнорировать поле MDL.Size (оно вообще где-нибудь используется?), то, естественно, это ничем не грозит.
arevstudio Виртуальную память или физическую ? MDL описывает группу страниц произвольным образом расположенных в физической памяти, но отображены они на адресное простанство процесса линейно.
>Конечно, в качестве исключения можно попробовать вручную аллоцировать и инициализировать MDL Может быть Вы подскажете как это сделать? Не могу найти описание MDL
Физическую. В устройство при инициализации записывается начальный адрес физической памяти. Драйвер передаёт приложению виртуальный адрес соответствующий этому физическому и далее приложение работет с этой областью памяти
arevstudio Тогда забудьте про пулы и MDL. Менеджер памяти не позволяет манипулировать большими областями физической памяти. У меня был подобный вопрос http://www.wasm.ru/forum/viewtopic.php?id=36344 Единственное решение видимо - вручную искать свободный блок, но слишком уж велика вероятность того, что такого блока нет.
З.Ы. Можно выгрузить все в файл подкачки / выполнить нечто сродни "дефрагментации" но это как-то... не по людски.
>>Конечно, в качестве исключения можно попробовать вручную аллоцировать и инициализировать MDL >Может быть Вы подскажете как это сделать? Посмотреть как это делает винда. В дизассемблерном листинге (часть мы привели) или в виде сишного кода в WRK. >Не могу найти описание MDL. И не получится найти. Вообще говоря, предполагается, что MDL – мутная структура (т.е. программисту незачем знать её внутреннее устройство). Опять-таки, можно взять описание самой структуры из WRK или символов: Код (Text): lkd> * w7 x64 lkd> dt -v _MDL nt!_MDL struct _MDL, 8 elements, 0x30 bytes +0x000 Next : Ptr64 to struct _MDL, 8 elements, 0x30 bytes +0x008 Size : Int2B +0x00a MdlFlags : Int2B +0x010 Process : Ptr64 to struct _EPROCESS, 135 elements, 0x4d0 bytes +0x018 MappedSystemVa : Ptr64 to Void +0x020 StartVa : Ptr64 to Void +0x028 ByteCount : Uint4B +0x02c ByteOffset : Uint4B lkd> * 8-byte PFNs follow Но, повторимся, подобные решения считаем допустимыми только в качестве временного хака – когда других путей нет; при этом один должен чётко осознавать, что он делает. На самом деле, множество плохих вещей происходит из-за такого подхода. >DEVICE_DESCRIPTION dd; >dd.ScatterGather = FALSE; >Virtual_address_for_user = m_Dma.AllocateCommonBuffer(MAX_DMA_LENGTH,&Address_for_dma,false) Означает ли ^это^ запрос на выделение непрерывной физической памяти объёмом MAX_DMA_LENGTH? Если да, то, судя по прохождению проверки на нуль, он, как ни странно, успешен – и единственной проблемой остаётся создание MDL? >Драйвер передаёт приложению виртуальный адрес соответствующий этому физическому и далее приложение работет с этой областью памяти. Ни в коей мере не являемся экспертом по DMA, однако разве принятая в Windows модель работы с dma-устройствами, не поддерживающими scatter-gather, не подразумевает наличие некого промежуточного буфера (aka "Common Buffer"), невидимого приложению, но используемого драйвером и устройством? Лайк, приложение передаёт запрос драйверу, драйвер копирует данные из буфера приложения в Common Buffer и инициирует устройство на принятие данных; Либо драйвер инициирует получение и, затем, копирует данные из Common Buffer в предоставленный приложением буфер; В случае слишком большого пользовательского буфера драйвер делает несколько запросов (возможно, для нескольких различных Общих Буферов). >Virtual_address_for_user = m_Dma.AllocateCommonBuffer(MAX_DMA_LENGTH,&Address_for_dma,false) "Virtual_address_for_user" получает виртуальный адрес нестраничной памяти, в адресном пространстве ядра? А суффикс "for_user" здесь имеет значение "for_device_user" == "for_driver"?
Sol_Ksacap Хал использует для выделения непрерывных участков физической памяти под дма HalAllocateCommonBuffer(), которая сводится к MmAllocateContiguousMemorySpecifyCache(). Касательно MDL: проецируем страницы на адресное пространство с помощью MmMapLockedPages() и вызываем для каждой страницы(они спроецированы линейно) MmGetPhysicalAddress(). Вы увидите насколько различны будут физические адреса страниц.
1. Запрашиваю буффер 0x50000, вроде получаю, но TdiBuildReceive возвращает только 0xFAF0 байтов, вместо 0x38000, есть ли основания для ограничений (хотя читал PAGE_SIZE * (65535 - sizeof(MDL)) / sizeof(ULONG_PTR) == 0x400000, но уж очень близко к 0xFFFF) ?. 2. После TdiBuildReceive, IoCallDriver в поле Mdl->Next появляется цепочка, может там искать продолжение ( непохоже на то) Код (Text): pRBuffer = ExAllocatePoolWithTag(NonPagedPool, Size,'s0tA'); memset(pRBuffer, 0, Size); Mdl = IoAllocateMdl(pRBuffer, Size, FALSE, FALSE, NULL); /////////////////////////////////////////////////////////////// kd> dt mdl Local var @ 0xb89e8d18 Type _MDL* 0x89498600 +0x000 Next : (null) +0x004 Size : 348 +0x006 MdlFlags : 0 +0x008 Process : (null) +0x00c MappedSystemVa : (null) +0x010 StartVa : 0x893f5000 <<<<< pRBuffer +0x014 ByteCount : 0x50000 <<<< OK +0x018 ByteOffset : 0 //////////////////////////////////////////////////////////// if (!Mdl) {... } __try { MmProbeAndLockPages(Mdl, KernelMode, IoModifyAccess); } __except(EXCEPTION_EXECUTE_HANDLER) {... } Mdl->Next = NULL; Flags = TDI_RECEIVE_NORMAL; TdiBuildReceive (Irp, DeviceObject, Connection, KSocketComplete, &Ctx, Mdl, Flags, Size); Status = IoCallDriver(DeviceObject, Irp); ///////////////////////////////////////////////////////////////// kd> dt -r1 mdl Local var @ 0xb89e8d18 Type _MDL* 0x89498600 +0x000 Next : 0x897b42a8 _MDL <<<<<< ?? +0x000 Next : 0x89452768 _MDL +0x004 Size : 3 +0x006 MdlFlags : 0 +0x008 Process : 0x897b42d0 _EPROCESS +0x00c MappedSystemVa : 0x000002a7 +0x010 StartVa : 0x00330049 +0x014 ByteCount : 0x30 <<<< ??? +0x018 ByteOffset : 0x801a4 +0x004 Size : 348 +0x006 MdlFlags : 128 +0x008 Process : (null) +0x00c MappedSystemVa : 0xb857a000 +0x010 StartVa : 0x893f5000 +0x014 ByteCount : 0x50000 +0x018 ByteOffset : 0 ////////////////////////////////////////////////////////////////// if (Status == STATUS_PENDING) { Status = KeWaitForSingleObject(&Ctx.Event, Executive, KernelMode, FALSE, &SockTimeout); if (Status == STATUS_TIMEOUT) {... } else Status = Ctx.Iosb.Status; } BytesReceived = Ctx.Iosb.Information;<<<< == 0xFAF0 ??????
Вопрос не сюда, с памятью все нормально, тупо проверил, и пишется и читается Код (Text): pAAA = pRBuffer; while (k < 0x50000) { (*(pAAA + k)) = SendBfr[i++]; k++; if (j==i) i =0; } for (i = 0; i < j; i++) SendBfr[i] = (*(pAAA + 0x50000-j + i));