Управление памятью ядра Windows XP

Тема в разделе "WASM.NT.KERNEL", создана пользователем arevstudio, 5 фев 2010.

  1. arevstudio

    arevstudio New Member

    Публикаций:
    0
    Регистрация:
    5 фев 2010
    Сообщения:
    5
    Здравствуйте.
    Пишу драйвер под Windows XP для мастера шины PCI.
    Устройство пишет и читает системную память.
    Необходимо выделить непрерывную область памяти ядра ~ 128 Мбайт.
    Драйвер создаётся с помощью Driver Studio 3.0
    НЕ удаётся выделить область более чем 39 Мбайт.
    При MAX_DMA_LENGTH = 0x4000000 и более, полученный *.sys отторгается системой и в диспетчере Устройств появляется характерный жёлтый восклицательный знак.
    При этом реально в системе 4 Гбайта оперативной памяти.
    Можно ли как-то перераспределить оперативную память для решения этой задачи?

    Привожу часть исходного текста:

    Код (Text):
    1. #define MAX_DMA_LENGTH  0x4000000   // 0x100000 is 1 MB
    2.  
    3.     // Initialize the device descriptor for the DMA object using the assigned resource
    4.     DEVICE_DESCRIPTION dd;
    5.     RtlZeroMemory(&dd, sizeof(dd));
    6.     dd.Version = DEVICE_DESCRIPTION_VERSION;
    7.     dd.Master = TRUE;
    8.     dd.ScatterGather = FALSE;
    9.     dd.DemandMode = TRUE;
    10.     dd.AutoInitialize = FALSE;
    11.     dd.Dma32BitAddresses = TRUE;
    12.     dd.IgnoreCount = FALSE;
    13.     dd.DmaChannel = 0;
    14.     dd.InterfaceType = PCIBus;
    15.     dd.DmaWidth = Width32Bits;  // PCI default width
    16.     dd.DmaSpeed = Compatible;
    17.     dd.MaximumLength = MAX_DMA_LENGTH;
    18.  
    19.     // Initialize the DMA adapter object
    20.     m_Dma.Initialize(&dd, m_Lower.PDO());
    21.  
    22.     Virtual_address_for_user = m_Dma.AllocateCommonBuffer(MAX_DMA_LENGTH,&Address_for_dma,false);
    23.     if (Virtual_address_for_user == NULL) {
    24.         m_Dma.Invalidate();
    25.         return STATUS_INSUFFICIENT_RESOURCES;
    26.     }
    27.     mdl = IoAllocateMdl(Virtual_address_for_user, MAX_DMA_LENGTH, FALSE, FALSE, NULL);
    28.     if (mdl == NULL) {
    29.         m_Dma.Invalidate();
    30.         return STATUS_INSUFFICIENT_RESOURCES;
    31.     }
    32.     MmBuildMdlForNonPagedPool(mdl);
    Заключайте код в соответствующие bb-теги, читать читать сплошную пелену текста просто невозможно.
     
  2. Sol_Ksacap

    Sol_Ksacap Миша

    Публикаций:
    0
    Регистрация:
    6 мар 2008
    Сообщения:
    623
    2^12 * 2^16 / 2^2 == 2^26 == 64M == 0x4000000.
     
  3. arevstudio

    arevstudio New Member

    Публикаций:
    0
    Регистрация:
    5 фев 2010
    Сообщения:
    5
    Cпасибо большое.
    Конечно, MAX_DMA_LENGTH задаётся в шестнадцатиричном коде, так что число 0x4000000 соответствует 64 Мб
    (это моя ошибка)
    НО попытался поэкспериметировать и получилось, что драйвер успешно ставится примерно до 0x3FF0000
    Если ставлю 0x3FFFFFF, то опять отторжение. ТО есть полноценный буфер размером 64 Мб получить так и не удалось....
    Правильно ли я понимаю, что это принципиальное ограничение WINDOWS XP и VISTA решает эту проблему?
    Никаких вариатов с XP быть не может? (редактирование реестра, изменение кода драйвера или ещё что-то)?
    Буду благодарен за информацию.
     
  4. Sol_Ksacap

    Sol_Ksacap Миша

    Публикаций:
    0
    Регистрация:
    6 мар 2008
    Сообщения:
    623
    >Если ставлю 0x3FFFFFF, то опять отторжение.
    Всё правильно – мы округлили. Для XP и 2k3 максимальный размер описываемого буфера в байтах – (0x1000 * (2^16 - 1 - sizeof(MDL)) / sizeof(ULONG_PTR)), для Vi – (2GB-0x1000), для W7 – (4GB-0x1000).

    Чтобы понять, откуда такие ограничения, можно взглянуть на код:
    Код (Text):
    1. 0:000> dt MDL
    2. nt!MDL
    3.    +0x000 Next             : Ptr32 _MDL
    4.    +0x004 Size             : Int2B                 // 2 байта на размер MDL
    5.    +0x006 MdlFlags         : Int2B
    6.    +0x008 Process          : Ptr32 _EPROCESS
    7.    +0x00c MappedSystemVa   : Ptr32 Void
    8.    +0x010 StartVa          : Ptr32 Void
    9.    +0x014 ByteCount        : Uint4B
    10.    +0x018 ByteOffset       : Uint4B
    11.  
    12. XP, 2k3:
    13. IoAllocateMdl(VirtualAddress, Length, ...)
    14. {
    15. ...
    16. 00967     //
    17. 00968     // If the requested length is greater than 2Gb, then we're not going
    18. 00969     // to be able to map the memory, so fail the request.
    19. 00970     //
    20. 00971
    21. 00972     if (Length & 0x80000000) {
    22. 00973         return NULL;
    23. 00974     }
    24. ...
    25. 00977     //
    26. 00978     // Allocate an MDL from the lookaside list or pool as appropriate.
    27. 00979     //
    28. 00980
    29. 00981     mdl = NULL;
    30. 00982     fixedSize = 0;
    31. 00983     size = ADDRESS_AND_SIZE_TO_SPAN_PAGES(VirtualAddress, Length);      // размер всего региона в страницах
    32. 00984     if (size > IOP_FIXED_SIZE_MDL_PFNS) {
    33. 00985         allocateSize = sizeof(MDL) + (sizeof(PFN_NUMBER) * size);       // размер структуры в байтах
    34. 00986         if (allocateSize > MAXUSHORT) {
    35. 00987             return NULL;                                                // если не уместить в 2 байта, то вернуть фейл.
    36. 00988         }
    На самом деле, размер структуры не важен – количество PFN может быть вычислено по другим полям (ByteCount).

    Теперь можно посмотреть на код Vi:
    Код (Text):
    1. 0:000> uf IoAllocateMdl
    2. nt!IoAllocateMdl [d:\rtm\base\ntos\io\iomgr\iosubs.c @ 980]:
    3.   980 00445dc2 mov     edi,edi
    4.   980 00445dc4 push    ebp
    5.   980 00445dc5 mov     ebp,esp
    6.   980 00445dc7 sub     esp,10h
    7.   996 00445dca test    dword ptr [ebp+0Ch],80000000h         // 2GB check
    8.   996 00445dd1 je      nt!IoAllocateMdl+0x18 (00445dda)
    9.  
    10. ...
    11.  
    12.  1008 00445e06 cmp     eax,11h                               // IOP_FIXED_SIZE_MDL_PFNS
    13.  1008 00445e09 mov     dword ptr [ebp-10h],ecx
    14.  1008 00445e0c jbe     nt!IoAllocateMdl+0x55 (00445e17)
    15.  
    16. nt!IoAllocateMdl+0x4c [d:\rtm\base\ntos\io\iomgr\iosubs.c @ 1009]:
    17.  1009 00445e0e lea     eax,[eax*4+1Ch]
    18.  1010 00445e15 jmp     nt!IoAllocateMdl+0xb2 (00445e74)
    19. ...
    20. nt!IoAllocateMdl+0xb2 [d:\rtm\base\ntos\io\iomgr\iosubs.c @ 1017]:
    21.  1017 00445e74 push    206C644Dh
    22.  1017 00445e79 push    eax
    23.  1017 00445e7a push    0
    24.  1017 00445e7c call    nt!ExAllocatePoolWithTag (004ed3b7)    // 64k check – отсутствует
    Конечно, в качестве исключения можно попробовать вручную аллоцировать и инициализировать MDL. Если все используемые функции будут игнорировать поле MDL.Size (оно вообще где-нибудь используется?), то, естественно, это ничем не грозит.
     
  5. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    arevstudio
    Виртуальную память или физическую ?
    MDL описывает группу страниц произвольным образом расположенных в физической памяти, но отображены они на адресное простанство процесса линейно.
     
  6. arevstudio

    arevstudio New Member

    Публикаций:
    0
    Регистрация:
    5 фев 2010
    Сообщения:
    5
    >Конечно, в качестве исключения можно попробовать вручную аллоцировать и инициализировать MDL

    Может быть Вы подскажете как это сделать?

    Не могу найти описание MDL
     
  7. arevstudio

    arevstudio New Member

    Публикаций:
    0
    Регистрация:
    5 фев 2010
    Сообщения:
    5
    Физическую.
    В устройство при инициализации записывается начальный адрес физической памяти. Драйвер передаёт приложению виртуальный адрес соответствующий этому физическому и далее приложение работет с этой областью памяти
     
  8. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    arevstudio
    Тогда забудьте про пулы и MDL. Менеджер памяти не позволяет манипулировать большими областями физической памяти. У меня был подобный вопрос http://www.wasm.ru/forum/viewtopic.php?id=36344
    Единственное решение видимо - вручную искать свободный блок, но слишком уж велика вероятность того, что такого блока нет.
     
  9. Wizard109

    Wizard109 New Member

    Публикаций:
    0
    Регистрация:
    6 ноя 2006
    Сообщения:
    346
    А если не по теме... зачем нужно ~ 128 Мбайт непрерывной памяти ?
     
  10. Wizard109

    Wizard109 New Member

    Публикаций:
    0
    Регистрация:
    6 ноя 2006
    Сообщения:
    346
    З.Ы. Можно выгрузить все в файл подкачки / выполнить нечто сродни "дефрагментации" но это как-то... не по людски.
     
  11. Sol_Ksacap

    Sol_Ksacap Миша

    Публикаций:
    0
    Регистрация:
    6 мар 2008
    Сообщения:
    623
    >>Конечно, в качестве исключения можно попробовать вручную аллоцировать и инициализировать MDL
    >Может быть Вы подскажете как это сделать?
    Посмотреть как это делает винда. В дизассемблерном листинге (часть мы привели) или в виде сишного кода в WRK.

    >Не могу найти описание MDL.
    И не получится найти. Вообще говоря, предполагается, что MDL – мутная структура (т.е. программисту незачем знать её внутреннее устройство). Опять-таки, можно взять описание самой структуры из WRK или символов:
    Код (Text):
    1. lkd> * w7 x64
    2. lkd> dt -v _MDL
    3. nt!_MDL
    4. struct _MDL, 8 elements, 0x30 bytes
    5.    +0x000 Next             : Ptr64 to struct _MDL, 8 elements, 0x30 bytes
    6.    +0x008 Size             : Int2B
    7.    +0x00a MdlFlags         : Int2B
    8.    +0x010 Process          : Ptr64 to struct _EPROCESS, 135 elements, 0x4d0 bytes
    9.    +0x018 MappedSystemVa   : Ptr64 to Void
    10.    +0x020 StartVa          : Ptr64 to Void
    11.    +0x028 ByteCount        : Uint4B
    12.    +0x02c ByteOffset       : Uint4B
    13. 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"?
     
  12. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Sol_Ksacap
    Хал использует для выделения непрерывных участков физической памяти под дма HalAllocateCommonBuffer(), которая сводится к MmAllocateContiguousMemorySpecifyCache(). Касательно MDL: проецируем страницы на адресное пространство с помощью MmMapLockedPages() и вызываем для каждой страницы(они спроецированы линейно) MmGetPhysicalAddress(). Вы увидите насколько различны будут физические адреса страниц.
     
  13. at0s

    at0s New Member

    Публикаций:
    0
    Регистрация:
    13 июл 2009
    Сообщения:
    91
    1. Запрашиваю буффер 0x50000, вроде получаю, но TdiBuildReceive возвращает только 0xFAF0 байтов, вместо 0x38000, есть ли основания для ограничений (хотя читал PAGE_SIZE * (65535 - sizeof(MDL)) / sizeof(ULONG_PTR) == 0x400000, но уж очень близко к 0xFFFF) ?.
    2. После TdiBuildReceive, IoCallDriver в поле Mdl->Next появляется цепочка, может там искать продолжение ( непохоже на то)

    Код (Text):
    1.    pRBuffer = ExAllocatePoolWithTag(NonPagedPool, Size,'s0tA');  
    2.    memset(pRBuffer, 0,  Size);
    3.  
    4.     Mdl = IoAllocateMdl(pRBuffer, Size, FALSE, FALSE, NULL);
    5. ///////////////////////////////////////////////////////////////
    6. kd> dt mdl
    7. Local var @ 0xb89e8d18 Type _MDL*
    8. 0x89498600
    9.    +0x000 Next             : (null)
    10.    +0x004 Size             : 348
    11.    +0x006 MdlFlags         : 0
    12.    +0x008 Process          : (null)
    13.    +0x00c MappedSystemVa   : (null)
    14.    +0x010 StartVa          : 0x893f5000  <<<<< pRBuffer
    15.    +0x014 ByteCount        : 0x50000  <<<<  OK
    16.    +0x018 ByteOffset       : 0
    17. ////////////////////////////////////////////////////////////
    18.     if (!Mdl)  {...    }
    19.  
    20.     __try
    21.     {
    22.            MmProbeAndLockPages(Mdl, KernelMode, IoModifyAccess);
    23.     }
    24.     __except(EXCEPTION_EXECUTE_HANDLER)
    25.            {...    }
    26.  
    27.     Mdl->Next = NULL;
    28.  
    29.     Flags = TDI_RECEIVE_NORMAL;
    30.  
    31.     TdiBuildReceive (Irp, DeviceObject, Connection, KSocketComplete, &Ctx, Mdl, Flags, Size);
    32.  
    33.     Status = IoCallDriver(DeviceObject, Irp);
    34.  /////////////////////////////////////////////////////////////////
    35. kd> dt -r1 mdl
    36. Local var @ 0xb89e8d18 Type _MDL*
    37. 0x89498600
    38.    +0x000 Next             : 0x897b42a8 _MDL <<<<<< ??
    39.       +0x000 Next             : 0x89452768 _MDL
    40.       +0x004 Size             : 3
    41.       +0x006 MdlFlags         : 0
    42.       +0x008 Process          : 0x897b42d0 _EPROCESS
    43.       +0x00c MappedSystemVa   : 0x000002a7
    44.       +0x010 StartVa          : 0x00330049
    45.       +0x014 ByteCount        : 0x30 <<<< ???
    46.       +0x018 ByteOffset       : 0x801a4
    47.    +0x004 Size             : 348
    48.    +0x006 MdlFlags         : 128
    49.    +0x008 Process          : (null)
    50.    +0x00c MappedSystemVa   : 0xb857a000
    51.    +0x010 StartVa          : 0x893f5000
    52.    +0x014 ByteCount        : 0x50000
    53.    +0x018 ByteOffset       : 0
    54. //////////////////////////////////////////////////////////////////
    55.     if (Status == STATUS_PENDING)
    56.     {
    57.         Status = KeWaitForSingleObject(&Ctx.Event, Executive, KernelMode, FALSE,
    58.                                                          &SockTimeout);
    59.         if (Status == STATUS_TIMEOUT)
    60.             {... }
    61.         else
    62.             Status = Ctx.Iosb.Status;
    63.     }
    64.     BytesReceived = Ctx.Iosb.Information;<<<<   ==  0xFAF0 ??????
     
  14. at0s

    at0s New Member

    Публикаций:
    0
    Регистрация:
    13 июл 2009
    Сообщения:
    91
    Вопрос не сюда, с памятью все нормально,
    тупо проверил, и пишется и читается
    Код (Text):
    1.    
    2.                 pAAA = pRBuffer;
    3.         while (k < 0x50000)
    4.         {
    5.                   (*(pAAA + k)) =  SendBfr[i++];
    6.                k++;
    7.               if (j==i) i =0;
    8.         }
    9.         for (i = 0; i < j; i++)
    10.                 SendBfr[i] = (*(pAAA + 0x50000-j + i));