читать на ядре 0 в Kernel Force

Тема в разделе "WASM.NT.KERNEL", создана пользователем zky02, 16 янв 2024.

  1. zky02

    zky02 New Member

    Публикаций:
    0
    Регистрация:
    16 янв 2024
    Сообщения:
    20
    это правильный подход?
    Код (C++):
    1. class TSingleProcessorMode
    2. {
    3.     KDPC DpcTraps[MAXIMUM_PROCESSORS];
    4.     volatile LONGLONG Stall;
    5.     unsigned __int64 cr9;
    6.     KPRIORITY SavedPriority;
    7.     int  CpuCount;
    8.     static void DpcRoutine(KDPC* pDpc, void* pContext, void* pArg1, void* pArg2);
    9. public:
    10.     void Initialize();
    11.     void Enter();
    12.     void Exit();
    13. };
    14.  
    15. void TSingleProcessorMode::Initialize()
    16. {
    17.     RtlZeroMemory(this, sizeof(TSingleProcessorMode));
    18.     CpuCount = (int)KeQueryActiveProcessorCount(nullptr);
    19.     if (CpuCount > 1)
    20.     {
    21.         for (int i = 0; i < CpuCount; i++)
    22.         {
    23.             KeInitializeDpc(&DpcTraps[i], DpcRoutine, this);
    24.             KeSetImportanceDpc(&DpcTraps[i], LowImportance);
    25.             KeSetTargetProcessorDpc(&DpcTraps[i], i);
    26.         }
    27.     }
    28. }
    29.  
    30. void TSingleProcessorMode::DpcRoutine(KDPC* pDpc, void* pContext, void* pArg1, void* pArg2)
    31. {
    32.   TSingleProcessorMode* pThis = (TSingleProcessorMode*)pContext;
    33.   auto cr8 = __readcr8();
    34.   __writecr8(HIGH_LEVEL);
    35.   _disable();
    36.   InterlockedDecrement64(&pThis->Stall);
    37.   do
    38.   _mm_pause();
    39.   while (pThis->Stall > 0);
    40.   _enable();
    41.   __writecr8(cr8);
    42. }
    43.  
    44. void TSingleProcessorMode::Enter()
    45. {
    46.     cr9 = __readcr8();
    47.     if (CpuCount > 1)
    48.     {
    49.         SavedPriority = KeSetPriorityThread(KeGetCurrentThread(), HIGH_PRIORITY - 1);
    50.         KAFFINITY ActiveProcessors = KeQueryActiveProcessors();
    51.         __writecr8(DISPATCH_LEVEL);
    52.         ULONG CurrentProcessor = (ULONG)KeGetCurrentProcessorNumber();
    53.         Stall = 1;
    54.  
    55.         for (int i = CpuCount - 1; i >= 0; i--)
    56.         {
    57.             if (i != CurrentProcessor && (ActiveProcessors & (1ull << i)) != 0)
    58.             {
    59.                 InterlockedIncrement64(&Stall);
    60.                 KeInsertQueueDpc(&DpcTraps[i], 0, 0);
    61.             }
    62.         }
    63.  
    64.         __writecr8(cr9);
    65.  
    66.  
    67.         while (InterlockedAdd64(&Stall, -1) > 0)
    68.         {
    69.             _mm_pause();
    70.         }
    71.  
    72.         __writecr8(HIGH_LEVEL);
    73.         _disable();
    74.     }
    75.     else
    76.     {
    77.         __writecr8(HIGH_LEVEL);
    78.         _disable();
    79.     }
    80. }
    81.  
    82. void TSingleProcessorMode::Exit()
    83. {
    84.     if (CpuCount > 1)
    85.     {
    86.         InterlockedExchange64(&Stall, -1);
    87.         _enable();
    88.         __writecr8(cr9);
    89.         KeSetPriorityThread(KeGetCurrentThread(), SavedPriority);
    90.     }
    91.     else
    92.     {
    93.         _enable();
    94.         __writecr8(cr9);
    95.     }
    96. }
    Проблема в том, что чтение происходит на ядре 6, а не на ядре 0.

    [​IMG]
     
  2. comrade

    comrade Константин Ёпрст

    Публикаций:
    0
    Регистрация:
    16 сен 2002
    Сообщения:
    232
    Адрес:
    Russian Federation
    &DpcTraps[[index]] а не &DpcTraps ? линии 23-26. Иначе получается что атрибуты ставятся только на первый элемент массива
     
  3. zky02

    zky02 New Member

    Публикаций:
    0
    Регистрация:
    16 янв 2024
    Сообщения:
    20
    Да, я исправил ошибку. Теперь, как остановить все процессоры и поставить в очередь только на нулевом ядре
     
  4. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.455
    Адрес:
    Россия, Нижний Новгород
    Можешь сделать KeSetSystemAffinityThreadEx своему потоку, назначив его на нужное тебе ядро (на нулевое), после этого всем другим ядрам отправляешь DPC со спинлоком.
    В этом случае текущий поток гарантированно будет выполняться на нужном тебе ядре, а все другие потоки будут ждать.
    И насчёт прерываний, поднимать IRQL до HIGH_LEVEL или отключать прерывания не требуется, т.к. на DISPATCH_LEVEL планировщик уже не работает.
    А если надо отключить все прерывания, то cli достаточно, поднимать в cr8 не требуется, т.к. cli равнозначен поднятию до HIGH_LEVEL, и прервать его могут только NMI и SMI.
     
    comrade, zky02 и q2e74 нравится это.
  5. zky02

    zky02 New Member

    Публикаций:
    0
    Регистрация:
    16 янв 2024
    Сообщения:
    20
    :drinks: большое спасибо
     
  6. zky02

    zky02 New Member

    Публикаций:
    0
    Регистрация:
    16 янв 2024
    Сообщения:
    20
    Я нашел еще один хороший способ чтения в ядре 0
    Замените 6 на ваше максимальное количество процессоров
    Код (C++):
    1. const ULONG numberOfProcessors = KeQueryActiveProcessorCountEx(ALL_PROCESSOR_GROUPS);
    2.     PROCESSOR_NUMBER processorNumber;
    3.  
    4.     NTSTATUS Status = KeGetProcessorNumberFromIndex(numberOfProcessors - 6, &processorNumber);
    5.     if (!NT_SUCCESS(Status))
    6.     {
    7.         return;
    8.     }
    9.  
    10.     processorNumber.Group;
    11.     processorNumber.Number;
    12.  
    13.      Status = ZwSetInformationThread(ZwCurrentThread(), ThreadIdealProcessorEx, &processorNumber, sizeof(processorNumber));
    14.     if (!NT_SUCCESS(Status))
    15.         return;
    16.  
    17.     KAFFINITY ProcessAffinity = (KAFFINITY)(1ull << processorNumber.Number);
    18.      Status = ZwSetInformationProcess(ZwCurrentProcess(), ProcessAffinityMask, &ProcessAffinity, sizeof(ProcessAffinity));
    19.     if (!NT_SUCCESS(Status))
    20.         return;
    21.     KAFFINITY ThreadAffinity = (KAFFINITY)(1ull << processorNumber.Number);
    22.     Status = ZwSetInformationThread(ZwCurrentThread(), ThreadAffinityMask, &ThreadAffinity, sizeof(ThreadAffinity));
    23.     if (!NT_SUCCESS(Status))
    24.         return;
    [​IMG] :dance3:
     
  7. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.455
    Адрес:
    Россия, Нижний Новгород
    А это то же самое: внутри ZwSetInformationThread(ThreadAffinityMask) сведётся к KeSetSystemAffinityThreadEx.
     
  8. zky02

    zky02 New Member

    Публикаций:
    0
    Регистрация:
    16 янв 2024
    Сообщения:
    20
    Извините за вопрос, может ли эта рутина также работать для чтения ядра 0? Спасибо
    Код (C++):
    1. volatile LONG Barrier = 1;
    2.  
    3.  
    4. ULONG_PTR BroadcastFunction(ULONG_PTR Context)
    5. {
    6.     volatile LONG* barrier = (volatile LONG*)Context;
    7.     ULONG currentCpu = KeGetCurrentProcessorNumber();
    8.  
    9.     if (currentCpu == 0)
    10.     {
    11.         // CPU 0 выполняет свою работу
    12.         DbgPrint("CPU %u выполняет свою работу\n", currentCpu);
    13.         InterlockedExchange(barrier, 0);
    14.     }
    15.     else
    16.     {
    17.         while (InterlockedCompareExchange(barrier, 0, 0) != 0)
    18.         {
    19.             _mm_pause(); // Используйте _mm_pause для энергосберегающего ожидания
    20.         }
    21.     }
    22.  
    23.     return 0;
    24. }
    25.  
    26. VOID CallIpiForCpu0()
    27. {
    28.     InterlockedExchange(&Barrier, 1);
    29.     KeIpiGenericCall(BroadcastFunction, (ULONG_PTR)&Barrier);
    30. }
     
  9. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.455
    Адрес:
    Россия, Нижний Новгород
    Может, но учитывай, что ты будешь выполняться на очень высоком IRQL - на IPI_LEVEL, а значит, нельзя безопасно работать с Paged-памятью, поскольку она может быть выгружена в своп, а на IRQL >= DISPATCH_LEVEL подгрузка страниц из свопа не работает.
     
    zky02 нравится это.
  10. zky02

    zky02 New Member

    Публикаций:
    0
    Регистрация:
    16 янв 2024
    Сообщения:
    20
    фиксированный
    Код (C++):
    1. volatile LONG64 g_lock = 1;
    2. ULONG_PTR broadcast_function(ULONG_PTR context)
    3. {
    4.     UNREFERENCED_PARAMETER(Context);
    5.     ULONG currentProcessor = KeGetCurrentProcessorNumber();
    6.  
    7.     if (currentProcessor != 0)
    8.     {
    9.         DbgPrint("CPU %u ждет барьер\n", currentProcessor);
    10.         // Ожидание, пока барьер не будет снят
    11.         while (InterlockedCompareExchange64(&g_lock, 1, 1) == 1)
    12.         {
    13.             _mm_pause();
    14.         }
    15.  
    16.         DbgPrint("CPU %u освободился от барьера\n", currentProcessor);
    17.     }
    18.     else
    19.     {
    20.         DbgPrint("CPU %u выполняет свою работу\n", currentProcessor);
    21.  
    22.         // Выполнение необходимой операции
    23.         LARGE_INTEGER currentTime;
    24.         KeQuerySystemTime(&currentTime);
    25.  
    26.         DbgPrint("CPU %u текущее время: %lld\n", currentProcessor, currentTime.QuadPart);
    27.  
    28.         // Снятие барьера путем установки блокировки в 0
    29.         InterlockedExchange64(&g_lock, 0);
    30.         DbgPrint("CPU %u установил барьер в 0\n", currentProcessor);
    31.     }
    32.  
    33.     return 0;
    34. }
    Код (C++):
    1. VOID PerformBroadcast()
    2. {
    3.     InterlockedExchange64(&g_lock, 1);
    4.     KeIpiGenericCall(broadcast_function, 0);
    5. }
    [​IMG] :drinks::drinks:
     
  11. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.455
    Адрес:
    Россия, Нижний Новгород
    А зачем тебе это? Похоже, ты пытаешься сделать какую-то простую вещь очень странными способами.
    IPI, конечно, работает, но для чего тебе вообще понадобилось привязываться к конкретному ядру и почему не использовать KeSetSystemAffinityThreadEx или на крайний случай отправку DPC нужному ядру через KeInitializeDpc/KeSetImportanceDpc/KeSetTargetProcessorDpc/KeInsertQueueDpc?
    Останавливать все ядра, пока на одном из них выполняется какая-то работа (как и вообще делать работу на IPI_LEVEL) - очень сомнительное решение.
     
  12. zky02

    zky02 New Member

    Публикаций:
    0
    Регистрация:
    16 янв 2024
    Сообщения:
    20
    Использование KeSetSystemAffinityThreadEx можно только для установки аффинности текущего потока. Но я хочу установить аффинность произвольного потока и заблокировать большинство процессоров, чтобы оставить активным только один. Именно поэтому я использую KeIpiGenericCall.

    KeIpiGenericCall позволяет мне послать IPI (Inter-Processor Interrupt) всем процессорам и синхронизировать их, чтобы только один процессор выполнял нужную задачу, а остальные ждали. Это обеспечивает высокую точность и предотвращает контекстные переключения, которые могут повлиять на точность времени в моей задаче. Также это помогает избежать использования DPC на уровне IPI, который может быть не оптимален для моего случая.
     
  13. UbIvItS

    UbIvItS Well-Known Member

    Публикаций:
    0
    Регистрация:
    5 янв 2007
    Сообщения:
    6.243
    не легче ли использовать юникерны линукса? :)
     
  14. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.455
    Адрес:
    Россия, Нижний Новгород
    Так а что ты делаешь-то?

    Если что, планировщик отключается на DISPATCH_LEVEL. Отправки DPC уже достаточно, чтобы эксклюзивно "забрать" нужное ядро, чтобы планировщик перестал работать, и никакой IPI уже не нужен.
    А насчёт точности, отправка IPI - это точно не про точность) Потому что они и вызываются в произвольном порядке, и это довольно тяжеловесная операция - дёрнуть APIC, он пошлёт прерывания, процессорам надо сохранить контекст, и пошло-поехало...
    В принципе, отправка DPC - это тоже прерывание, но хотя бы не затрагивает другие ядра. Вряд ли тебе действительно нужно их морозить.
     
  15. UbIvItS

    UbIvItS Well-Known Member

    Публикаций:
    0
    Регистрация:
    5 янв 2007
    Сообщения:
    6.243
    проблема выни в теневых процессах - если заморозишь ядра, есть некоторая гарантия, что никакая керь не вылезет в фоне :)
     
  16. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.455
    Адрес:
    Россия, Нижний Новгород
    …что получишь синий экран с CLOCK_WATCHDOG_TIMEOUT.
     
    Mikl___ нравится это.
  17. UbIvItS

    UbIvItS Well-Known Member

    Публикаций:
    0
    Регистрация:
    5 янв 2007
    Сообщения:
    6.243
    подойти к Вопросу можно по-разному: тот же патч ядра вполне может решить проблему. но в большинстве случаев лучше юникерны использовать :)
     
  18. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.455
    Адрес:
    Россия, Нижний Новгород
    Мы даже не знаем, к какому именно вопросу нам надо подходить, а уже думаем, как патчить ядро, переходить на линукс и морозить намертво процессоры)
    А потом окажется, что ТС что-то намудрил, и ему вообще не надо никаких привязок ни к процессорам, ни к ядрам)
     
  19. zky02

    zky02 New Member

    Публикаций:
    0
    Регистрация:
    16 янв 2024
    Сообщения:
    20
    Теперь я понимаю, я использую KeIpiGenericCall, и это работает великолепно. Я добавил KeMemoryBarrier для дополнительной безопасности в упорядочении памяти. Есть другой API, который можно использовать — IoConnectInterruptEx, но я слышал, что он предназначен только для PnP устройств