Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: 1 [2]  Все   Вниз
  Печать  
Автор Тема: Синий экран при обращении к драйверу PCI устройства  (Прочитано 37867 раз)
0 Пользователей и 1 Гость смотрят эту тему.
tuiui
Участник

ru
Offline Offline

« Ответ #30 : 27-04-2010 10:27 » 

Спасибо. После долгих стараний наконец что-то родилось:
Код:
NTSTATUS MultiplierCWriteDispatch(
    IN  PDEVICE_OBJECT  DeviceObject,
    IN  PIRP            Irp
    )
{
    PMULTIPLIERC_DEVICE_EXTENSION   deviceExtension;
    NTSTATUS                        status;
    PIO_STACK_LOCATION              irpStack;
    PVOID                           writeBuffer;
    ULONG                           writeLength;    
    ULONG                           byteOffset;
    PVOID                           Address;
    
deviceExtension = (PMULTIPLIERC_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
    
status = MultiplierCCheckIoLock(&deviceExtension->IoLock, Irp);
    if (!NT_SUCCESS(status) || (status == STATUS_PENDING))
    {
        return status;
    }    

irpStack = IoGetCurrentIrpStackLocation(Irp);    
    
//Получаем количество данных для записи
    writeLength = irpStack->Parameters.Write.Length;
    if (writeLength == 0)
    {        
        status = STATUS_SUCCESS;
        Irp->IoStatus.Information = 0;
        Irp->IoStatus.Status = status;
        IoCompleteRequest(Irp, IO_NO_INCREMENT);
        MultiplierCDecrementIoCount(&deviceExtension->IoLock);
        return status;
    }

//Получаем адрес буфера для чтения  
writeBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);    

//Получаем смещение байта
byteOffset = irpStack->Parameters.Write.ByteOffset.LowPart;

//Получаем адрес для записи
    Address = deviceExtension->DataMemoryBase+byteOffset*4;

//Запись
memcpy(Address, writeBuffer, writeLength*4);

    //Возвращаем количество записанных слов
status = STATUS_SUCCESS;
    Irp->IoStatus.Information = writeLength;
    Irp->IoStatus.Status = status;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    MultiplierCDecrementIoCount(&deviceExtension->IoLock);
return status;  
}

И вот опять пришли к тому, с чего все началось. Только теперь имеем чистый C. Драйвер установился, прекрасно пишет небольшие объемы данных. Но при попытке записать в устройство более 150 двойных слов - синий экран (((

В синем экране:

0x50 PAGE_FAULT_IN_NONPAGED_AREA

Ошибочное обращение к области памяти в системном адресном пространстве

Параметры Описание
1 Адрес, обращение по которому вызвало сбой (некоторое значение)
2 Код доступа при возникновении ошибки 0 — чтение, 1 — запись (у меня 0)
3 Адрес инструкции, из которой был осуществлен ошибочный доступ (некоторое значение)
4 Зарезервировано (0)

Значит ошибка при ЧТЕНИИ. При чтении буфера, очевидно ) Наверняка я с синтаксисом накосячил.
« Последнее редактирование: 27-04-2010 12:46 от tuiui » Записан
resource
Молодой специалист

ru
Offline Offline
Пол: Мужской

« Ответ #31 : 27-04-2010 13:42 » 

Цитата
memcpy(Address, writeBuffer, writeLength*4);

Я постов предыдущих много пропустил, и возможно поэтому не пойму, откуда тут взялся memcpy.
Ошибка не из-за этого, но всё-равно надо писать RtlCopyMemory.

По поводу самой ошибки. Вопрос - зачем умножать на 4 ?
Записан
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #32 : 27-04-2010 16:34 » 

   //Получаем смещение байта
   byteOffset = irpStack->Parameters.Write.ByteOffset.LowPart;
-> это что? и зачем?)

и почему действительно *4?

при доступе обязательно проверяйте выход за пределы выделенной вам памяти.  в драйвере ВСЕГДА надо проверять все что приходит от приложения.  в не зависимости от того что приложение тоже вами написано)

RtlCopyMemory - справедливо. действительно лучше пользовать ее.
Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
resource
Молодой специалист

ru
Offline Offline
Пол: Мужской

« Ответ #33 : 27-04-2010 20:44 » 

Ну не дожидаясь ответа от ТС, я сам себе так понимаю, что writeLength это размер write-буфера. Умножаем на 4 (зачем? а зачем тигру полоски? он же не зебра) и оказываемся за пределами буфера
« Последнее редактирование: 27-04-2010 20:55 от resource » Записан
tuiui
Участник

ru
Offline Offline

« Ответ #34 : 28-04-2010 05:12 » 

1. ByteOffset - это смещение относительно начала памяти устройства. Другими словами, это адрес в памяти устройства, с которого начинается запись. Передается этот параметр через структуру overlapped при вызове WriteFile (последний параметр). Мне же надо как-то из приложения передавать этот адрес.

2. Умножение на 4. Дело в том, что плата обменивается с памятью 4-байтными словами. А смещение поступает в байтах. Я хочу, чтобы смещение тоже задавалось двойными словами. То есть если напишем WriteFile(п1,п2,п3,8), то запись должна начинаться с 8-го двойного слова. Поэтому умножаю на 4. С размером буфера - то же самое. Если пишу 20, то он записывает 20 байт. А мне надо 20 двойных слов.

3. RtlCopyMemory - пробовал. Тот же результат.

Может указатель на буфер пользователя делать такого типа, чтобы RtlCopyMemory понимала, что речь идет о буфере а двойными словами

ЗЫ Скорее всего, как раз в этом *4 ошибка и есть. Но не должно же быть, вроде... Думаю, надо разобраться подробно, что происходит при вызове WriteFile, кто и как наращивает счетчики адресов. Может, если в качестве источника в WriteFile указывать массив двойных слов, то не надо умножать на 4.
« Последнее редактирование: 28-04-2010 05:32 от tuiui » Записан
resource
Молодой специалист

ru
Offline Offline
Пол: Мужской

« Ответ #35 : 28-04-2010 05:59 » 

Про слова, двойные слова, тройные или еще какие - это вариант гиблый, т.к. менеджеру ввода/вывода глубоко наплевать на все эти слова (это к вопросу о том, что происходит при вызове WriteFile). Честно говоря, такой фокус вижу впервые. На будущее, если в документации написано "размер в байтах", значит надо передавать размер в байтах. Ну можно же написать себе макрос и спокойно им пользоваться
Записан
tuiui
Участник

ru
Offline Offline

« Ответ #36 : 28-04-2010 07:50 » 

Благодарю за помощь. Сделал все в байтах. Теперь при записи по адресу d1048575 все ок. А при записи по адресу d1048576 синий экран )) На борту платы 1 Мб памяти. Отсюда вывод: надо в драйвере сделать защиту от дурака. То бишь как-то отлавливать возможные выходы за диапазон, отведенный плате в памяти.
Записан
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #37 : 28-04-2010 08:46 » 

d1048575 -шо за адрес? невыровненный... как-то подозрительно.
Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #38 : 28-04-2010 09:33 » 

вы по одному байту записываете? а плата это поддерживает?)

PS пардон. случайно ваше предыдущее сообщение удалил(((

PPS
аааа! дошло.... я привык что адреса обычно в шеснадцатиричном формате выкладывают) "d" - не заметил)
а проверять естественно. проверять надо все что приходит от приложения. ОБЯЗАТЕЛЬНО
« Последнее редактирование: 28-04-2010 09:37 от Ochkarik » Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #39 : 28-04-2010 09:36 » 

PS пардон. случайно ваше предыдущее сообщение удалил(((
инстинкт сработал Отлично
Записан

tuiui
Участник

ru
Offline Offline

« Ответ #40 : 28-04-2010 09:38 » 

))) пришлось переделать проект под плис, чтобы поддерживала. Можно вас попросить код набросать, как бы мне отлавливать эти ошибки дурака.
Записан
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #41 : 28-04-2010 09:39 » 

а чего там набрасывать...
это как проверка выхода за пределы массива. все исходные данные у вас уже есть...
Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
tuiui
Участник

ru
Offline Offline

« Ответ #42 : 28-04-2010 09:49 » 

все хорошо. Ошибки, связанные с выходом за границу адресов, отлавливаются, возвращается 0. Но вот когда пишем данные в количестве, равном 0, синий экран ) В драйвере есть такая вещь:

Код:
//Получаем количество данных для записи
writeLength = irpStack->Parameters.Write.Length;    
if (writeLength == 0)
    {        
        status = STATUS_SUCCESS;
        Irp->IoStatus.Information = 0;
        Irp->IoStatus.Status = status;
        IoCompleteRequest(Irp, IO_NO_INCREMENT);
        MultiplierCDecrementIoCount(&deviceExtension->IoLock);
        return status;
    }

Почему она не отлавливает запись нулевого количества???  Здесь была моя ладья...

А может отлавливает, но сама вызывает ошибку!
« Последнее редактирование: 28-04-2010 11:38 от tuiui » Записан
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #43 : 28-04-2010 11:59 » 

попробуйте
        Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
// Irp->IoStatus.Information не присваивать.
..
        IoCompleteRequest(Irp, IO_NO_INCREMENT);
        MultiplierCDecrementIoCount(&deviceExtension->IoLock);
 return STATUS_SUCCESS;
Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
resource
Молодой специалист

ru
Offline Offline
Пол: Мужской

« Ответ #44 : 28-04-2010 12:08 » 

хммм, интересно... Information то в 0 выставляется. Странно. Не знаю почему такая. Точно не должно такого быть. Тут надо смотреть отладчиком. Там всё сразу видно будет.

 Но тут просто больше не из за чего падать кроме как.......... IoLock'и я бы закомментарил.

Уверен, что дело именно в этом. Что там за функции такие? Покажите код.
« Последнее редактирование: 28-04-2010 12:13 от resource » Записан
tuiui
Участник

ru
Offline Offline

« Ответ #45 : 28-04-2010 12:38 » 

Тот код, что я привел, генерирует студия. В комментариях написано "просто завершить запрос нулевой длины". Что интересно, точно такие же конструкции используются для завершения запроса при выходе за диапазон адресов. Прям скопировал тупо эти 6 строчек везде, где нужно "аварийно" завершить работу.

resource, какой код вы просите показать?
« Последнее редактирование: 28-04-2010 12:42 от tuiui » Записан
resource
Молодой специалист

ru
Offline Offline
Пол: Мужской

« Ответ #46 : 28-04-2010 13:03 » 

MultiplierCCheckIoLock и MultiplierCDecrementIoCount
Записан
tuiui
Участник

ru
Offline Offline

« Ответ #47 : 28-04-2010 13:11 » 

Код:
///////////////////////////////////////////////////////////////////////////////////////////////////
//  MultiplierCCheckIoLock
//      checks if IRP is allowed to proceed.
//
//  Arguments:
//      IN  IoLock
//              io lock for our device
//
//      IN  Irp
//              new IRP
//
//  Return Value:
//      Status
//
NTSTATUS MultiplierCCheckIoLock(
    IN  PMULTIPLIERC_IO_LOCK    IoLock,
    IN  PIRP                        Irp
    )
{
    NTSTATUS            status;
    KIRQL               oldIrql;
    PMULTIPLIERC_DEVICE_EXTENSION   deviceExtension;

    deviceExtension = (PMULTIPLIERC_DEVICE_EXTENSION)IoLock->DeviceObject->DeviceExtension;

    KeAcquireSpinLock(&IoLock->IoLock, &oldIrql);

    // check if device has been removed
    if (IoLock->ErrorStatus != STATUS_SUCCESS)
    {
        status = IoLock->ErrorStatus;
        KeReleaseSpinLock(&IoLock->IoLock, oldIrql);

        Irp->IoStatus.Status = status;
        IoCompleteRequest(Irp, IO_NO_INCREMENT);

        return status;
    }

    // check if io is stalled
    if (IoLock->StallCount > 0)
    {
        // check if Irp is not the one sent by MultiplierCUnlockIo
        if (IoLock->CurrentIrp != Irp)
        {
            // save device extension into the IRP
            Irp->Tail.Overlay.DriverContext[0] = IoLock;

            // stall the IRP
            InsertTailList(&IoLock->StallIrpList, &Irp->Tail.Overlay.ListEntry);

            // insert our queue cancel routine into the IRP
            IoSetCancelRoutine(Irp, MultiplierCPendingIoCancelRoutine);

            // see if IRP was canceled
            if (Irp->Cancel && (IoSetCancelRoutine(Irp, NULL) != NULL))
            {
                // IRP was canceled
                RemoveEntryList(&Irp->Tail.Overlay.ListEntry);

                // drop the lock
                KeReleaseSpinLock(&IoLock->IoLock, oldIrql);

                // cancel the IRP
                status = STATUS_CANCELLED;
                Irp->IoStatus.Status = status;
                IoCompleteRequest(Irp, IO_NO_INCREMENT);

                return status;
            }

            // mark irp pending since STATUS_PENDING is returned
            IoMarkIrpPending(Irp);
            status = STATUS_PENDING;

            // drop the lock
            KeReleaseSpinLock(&IoLock->IoLock, oldIrql);

            // io is stalled. Make sure it is not because of
            // device is powered down.

            if (deviceExtension->DevicePowerState > PowerDeviceD0)
            {
                POWER_STATE powerState;

                powerState.DeviceState = PowerDeviceD0;
                status = PoRequestPowerIrp(
                            deviceExtension->PhysicalDeviceObject,
                            IRP_MN_SET_POWER,
                            powerState,
                            NULL,
                            NULL,
                            NULL
                            );
            }

            return status;
        }
        else
        {
            IoLock->CurrentIrp = NULL;
        }
    }

    // increment active io count
    ++IoLock->ActiveIrpCount;

    // drop the lock
    KeReleaseSpinLock(&IoLock->IoLock, oldIrql);

    status = STATUS_SUCCESS;
    return status;
}

Код:
///////////////////////////////////////////////////////////////////////////////////////////////////
//  MultiplierCDecrementIoCount
//      decrements active io count.
//
//  Arguments:
//      IN  IoLock
//              io lock for our device
//
//  Return Value:
//      None
//
VOID MultiplierCDecrementIoCount(
    IN  PMULTIPLIERC_IO_LOCK    IoLock
    )
{
    KIRQL       oldIrql;

    KeAcquireSpinLock(&IoLock->IoLock, &oldIrql);

    if (--IoLock->ActiveIrpCount == 0)
    {
        KeSetEvent(&IoLock->StallCompleteEvent, IO_NO_INCREMENT, FALSE);
    }

    KeReleaseSpinLock(&IoLock->IoLock, oldIrql);

    return;
}
« Последнее редактирование: 28-04-2010 13:14 от tuiui » Записан
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #48 : 28-04-2010 14:02 » 

код приложения еще можно посмотреть? тот, что  вызывает эту функцию драйвера
Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
resource
Молодой специалист

ru
Offline Offline
Пол: Мужской

« Ответ #49 : 28-04-2010 19:30 » 

Ну это уже гадание на кофейной гуще. Есть бсод - есть дамп. Глянуть отладчиком и всё сразу ясно станет
Записан
tuiui
Участник

ru
Offline Offline

« Ответ #50 : 29-04-2010 05:05 » 

Ochkarik, если я правильно понимаю, от приложения не должна зависеть стабильность драйвера. Он ведь должен быть защищен от любого дурака.
А с отладчиком не умею работать (
Записан
resource
Молодой специалист

ru
Offline Offline
Пол: Мужской

« Ответ #51 : 29-04-2010 05:14 » 

Про приложение это совершенно верно подмечено. Но думаю, что Ochkarik хотел на него глянуть, так, на всякий случай.

С отладчиком дружить надо. По-другому никак. Собственно для анализа дампа даже не надо второй машины. Я бы даже не обломился и посмотрел бы ваш дамп. Мне канал позволяет сливать такие объемы. Но бесплатные файлообменники вряд ли позволять забирать его с нормальной скоростью. Да и у вас канал, возможно не такой широкий, чтоб заливать столько. Так что, без отладчика никуда.
Записан
tuiui
Участник

ru
Offline Offline

« Ответ #52 : 29-04-2010 05:32 » 

Я просто не совсем даже и понимаю принцип его работы и что он позволяет сделать...

Простите мне мое дилетантство, но можно ли в код драйвера встроить некие контрольные точки? Чтоб, например, выводилась в файл информация о том, куда зашли, какую ошибку отловили?

А пока вот что я могу сказать.
В драйвере последовательность проверок такая:

1. Проверка на слишком большой (маленький) адрес

2. Проверка на нулевую длину

Вызываю WriteFile с нормальным адресом и нулевым количеством - BSOD

Вызываю WriteFile с некорректным адресом и нормальным количеством. Все нормально. Возвращает ноль, ничего не вылетает.

Вызываю WriteFile с некорректным адресом и нулевым количеством - BSOD !!

Отсюда вывод, что он даже не доходит до проверки на нулевую длину. Он даже не доходит до проверки на адрес. Иначе бы на первой проверке обработка завершилась. Куда же тогда смотреть?   Не понял
« Последнее редактирование: 29-04-2010 06:13 от tuiui » Записан
resource
Молодой специалист

ru
Offline Offline
Пол: Мужской

« Ответ #53 : 29-04-2010 06:28 » 

Контрольные точки то можно, но нужны они как раз при работе под отладчиком. И это логично, иначе как еще остановить драйвер, этож надо всю систему останавливать. И оно так и происходит, поэтому нужны 2 машины. Но чтоб дамп анализировать, 2 не надо.

В вашем случае, можно воспользоваться функцией DbgPrint. Такая софтина как, например, DebugView покажет всё, что выводится при помощи DbgPrint. Арсенал не богатый, но без отладчика не приходится расчитывать на что-то серьезное.
Записан
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #54 : 29-04-2010 10:41 » 

offtopic:
"Ochkarik, если я правильно понимаю, от приложения не должна зависеть стабильность драйвера. Он ведь должен быть защищен от любого дурака. "
- да! НО! если при написании драйвера осуществлены все возможные проверки и отслежены все не корректные запросы. в противном случае...)
не думайте что ЛЮБОЙ драйвер защищен. вся его защита от падения - это работа программиста. ОС за этим не следит.
Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
tuiui
Участник

ru
Offline Offline

« Ответ #55 : 29-04-2010 12:58 » 

Нашел, в чем была проблема. Оказывается, ошибка возникала при получении ссылки на буфер с данными ) Почему - объяснить не могу. Видимо, когда нулевая длина, система не передает ссылку на буфер. Так что сначала надо проверять длину буфера данных. И только если она больше нуля, пытаться получить ссылку на этот буфер:
Код:
//Получаем стек IRP
irpStack = IoGetCurrentIrpStackLocation(Irp);    
    
//Получаем количество данных для записи
writeLength = irpStack->Parameters.Write.Length;

//Завершение запроса при нулевом количестве данных
if (writeLength == 0)
    {                  
        Irp->IoStatus.Status = STATUS_SUCCESS;
        Irp->IoStatus.Information = 0;
        IoCompleteRequest(Irp, IO_NO_INCREMENT);
        return STATUS_SUCCESS;      
    }
 
//Получаем адрес буфера для чтения  
writeBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);  

Так что пока все нормально. Буду гонять еще драйвер. Если что-то всплывет - сообщу ))
Записан
resource
Молодой специалист

ru
Offline Offline
Пол: Мужской

« Ответ #56 : 29-04-2010 19:43 » 

Что-то вы уж совсем какие-то сказки выдумываете про буфер. В посте #30 точно такая же обработка идет. Разница (как видно) в том, что вы убрали IoLock'и (что собственно и было рекомендовано в #44)
Записан
tuiui
Участник

ru
Offline Offline

« Ответ #57 : 30-04-2010 05:36 » 

В посте #30 не было проверки на адреса. Там вылетало из-за этого )) А тут черт теперь разберешь. Но помню точно, что комментил IoLock, как вы рекомендовали. Не помогло тогда. Но я его все же убрал ) Наверно, не зря. Но даже без него вылетало, если пытаться буфер получать раньше обработки его длины.

В любом случае, большое всем вам спасибо. Вы мне очень помогли!
Записан
resource
Молодой специалист

ru
Offline Offline
Пол: Мужской

« Ответ #58 : 30-04-2010 06:13 » 

Да действительно, в #30 не было проверки. Ну так и в коде, приведенном в #55 ее тоже нет. Проверять, что возвращает MmGetSystemAddressForMdlSafe полюбому надо. Но если дело было в этом, то интересно почему она возвращала NULL для буфера ненулевой длины. Или чего она возвращала вообще. Ну да ладно...
Записан
Страниц: 1 [2]  Все   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines