OSDev

для всех
Текущее время: 28 мар 2024, 20:42

Часовой пояс: UTC + 3 часа




Начать новую тему Ответить на тему  [ Сообщений: 9 ] 
Автор Сообщение
СообщениеДобавлено: 31 янв 2019, 21:33 

Зарегистрирован: 31 янв 2019, 21:03
Сообщения: 5
Здравствуйте, товарищи. Столкнулся со следующей проблемой:
после записи сектора (команды ATA_CMD_WRITE_DMA_EX = 0x35 или ATA_CMD_WRITE_DMA = 0xCA) любая следующая операция (чтение/запись) не завершается (PxCI не сбрасывается в 0). Если чисто читать много раз (ATA_CMD_READ_DMA_EX = 0xC8), то операции чтения выполняются нормально. На QEMU чтение, следующее после записи, завершается нормально (PxCI = 0, видим считанные данные в буфере). На реальном же железе (чипсет ich8) после записи все "стопорится"...
Последовательность действий при записи:
1) нашли свободный слот
2) заполнили Command Header в Command List
3) заполнили PDRT в Command Table, указав буфер источника данных
4) заполнили Command FIS в Command Table
5) PxCI = 1 << slot;
6) while( PxCI != 0);
Функция записи завершается успешно... PxCI = 0, PxSERR = 0, PxSACT = 0, PxTFD = 0x50 (bit 7, BSY, = 0).
Но любая последующая операция, после запуска (PxCI = 1 << slot), повисает на последующем ожидании завершения, while( PxCI != 0).
Такое ощущение, что я какой-то принципиальный момент упустил... Если кто сталкивался с подобной ситуацией, отпишитесь пожалуйста.
(Никаких упоминаний о необходимости flush или ч-н в этом духе не нашел).

Код функции записи аналогичен "write_port",
https://github.com/ashishtanwer/SBUnix/ ... sys/ahci.c


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 01 фев 2019, 14:42 

Зарегистрирован: 26 янв 2019, 14:00
Сообщения: 34
Добрый день.

savver писал(а):
любая следующая операция (чтение/запись) не завершается (PxCI не сбрасывается в 0).

Была идентичная проблема. У меня проблемы возникли из-за неправильного выделения памяти. Если запустить ваш код на реальном оборудовании, заметите, что диск читает без остановки (и лампочка будет гореть). Как итог могу сказать, коль запустите до запуска пейджинга проблем не возникнет.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 01 фев 2019, 16:15 

Зарегистрирован: 31 янв 2019, 21:03
Сообщения: 5
Понял, спасибо, сейчас буду проверять данную гипотезу. Да, при второй операции, которая подвисает, лампочка активности диска горит постоянно.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 05 фев 2019, 18:06 

Зарегистрирован: 26 янв 2019, 14:00
Сообщения: 34
Смогли решить данную проблему?
Странным образом подобная вышла у меня. Происходит это при чтении. Примечательно то, что в QEMU все здорово, все работает на ура. А на железе нет. По идее с памятью все нормально, а что именно не так не смекну.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 05 фев 2019, 20:07 

Зарегистрирован: 31 янв 2019, 21:03
Сообщения: 5
До пейджинга пока проверить не получилось (он настраивается в бутлоадере, там асмовый код, пока не дошли руки перенести ahci туда, точнее попытка в лоб привела к вылету системы, в общем пока чуть отложил...). Вообще, виртуальные адреса напрямую отображаются на физические... В любом случае спасибо, вернусь к этой проверке чуть позже...

...Последняя отладка показала, что после завершения записи FIS_D2H нулевой, хотя при чтении он заполняется нормально (видать нет ответа от уст-ва?).

Тут сейчас поигрался с настройками ahci при ините, и получил интересную ситуацию... После записи FIS_D2H = 0 осталось, но следующее чтение уже не виснет в while(PxCI != 1), но при этом и не читает данные... Если перезагрузить комп, и прочесть сектор, в который писали... то опа, видим данные записанные в прошлом сеансе работы.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 06 фев 2019, 11:03 

Зарегистрирован: 26 янв 2019, 14:00
Сообщения: 34
savver писал(а):
До пейджинга пока проверить не получилось (он настраивается в бутлоадере, там асмовый код, пока не дошли руки перенести ahci туда, точнее попытка в лоб привела к вылету системы, в общем пока чуть отложил...). Вообще, виртуальные адреса напрямую отображаются на физические... В любом случае спасибо, вернусь к этой проверке чуть позже...

Выяснил опытным путем, что дело не в страничной адресации и не в выделении памяти. А следовательно ошибка кроется именно в драйвере AHCI. Странно, но после 2 первых действий (чтение/запись) "виснут" и чтение, и запись (на QEMU не "виснет"). Я пытался повторить одно и то же действие 3+ раз, и конкретно на 3 конец.

Следовательно разбор кода пошел у AHCI. Всю информацию о нем брал здесь. Единственное что привлекло внимание на cmd table. Как я понял при операции AHCI полагается на него, и каждое действие должен быть нулевым. Но результатов никаких не получил. В остальном код не сильно отличен, да и влиять на ход работы не должен, имею ввиду именно повторяющихся действий.
Код:
HBA_CMD_TBL *cmdtbl = (HBA_CMD_TBL*)(cmdheader->ctba);
   memset(cmdtbl, 0, sizeof((cmdheader->prdtl-1)*sizeof(HBA_PRDT_ENTRY));


ОБНОВЛЕНИЕ: Я очень сильно сглупил, как оказалось выше описанное помогло мне. Но, помогло мне не потому, что так правильно, а потому что я по глупости залез в этот участок памяти. Т.е. как факт драйвер AHCI у меня рабочий, и данная ошибка была лишь из-за невнимательности. Надеюсь, что и вы найдете ошибку.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 08 фев 2019, 20:32 

Зарегистрирован: 31 янв 2019, 21:03
Сообщения: 5
Sebt писал(а):
Надеюсь, что и вы найдете ошибку.

Спасибо, также веду ветку на англоязычном форуме (https://forum.osdev.org/viewtopic.php?f ... 17#p288417 ).
Пока есть вариант, в котором можно циклически вызывать функцию записи... она работает, но если между записями добавить паузу порядка 3х секунд (кеш?).


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 08 фев 2019, 21:45 

Зарегистрирован: 31 янв 2019, 21:03
Сообщения: 5
savver писал(а):
... также веду ветку на англоязычном форуме (https://forum.osdev.org/viewtopic.php?f ... 17#p288417 )

Дублирую и сюда код.
В main():
Код:
// 1. HBA reset & rebase
ahci_probe( devs, dev_cnt, (ahci_dev_t*)&ahci_devs, AHCI_PROBE_DEVS, &ports_map);
ahci_hba_reset(&ahci_devs[0]);
ahci_port_rebase_mix(&ahci_devs[0], ahci_devs[0].port, 0, (uint32_t)0xffdf800);

// 2. ATA Identify - work ok
ahci_identify(ahci_devs[0].port, dbuf_stat);

// 3. Test Reading - work ok
memset(dbuf_stat, 0, sizeof(dbuf_stat));
ahci_res = ahci_read(ahci_devs[0].port, WR_SECTOR_ADDR, 0, DATA_SEC_CNT, dbuf_stat);
assert (ahci_res == 0, "ahci_read_res = %d", ahci_res);

// 4. Cycle of writing
ahci_res = 0;
for(int j = 0; j < 5; j++)
{
   ahci_res |= ahci_write( ahci_devs[0].port, WR_SECTOR_ADDR + j, 0, DATA_SEC_CNT, dbufw_stat );
   
      //__ahci_flush_cache(ahci_devs[0].port); - not shure, that it works correct
      ahci_fis_dump( ahci_devs[0].port );
      ahci_port_info(ahci_devs[0].port);
      printf("ahci_write_res = %d", ahci_res);
     
      delay_ms(3*1000);
     
      dbufw_stat[0] *= 2;
}


Функция записи (с последними изменениями, уже чуть-чуть отличается от варианта SBUnix за счет вызова start/stop_engine и вариаций флагов w,c,p,control):
Код:
int ahci_write(HBA_PORT *port, uint32_t startl, uint32_t starth, uint32_t count, uint16_t * buf)
{
    printf("\n=== write (%08x) ===\n", startl);

    uintptr_t p;
    port->is = 0xffff;        // Clear pending interrupt bits
    int spin = 0;             // Spin lock timeout counter
    int slot = find_cmdslot(port);
    printf("W0:slot=%d,clf=%d ", slot, sizeof(FIS_REG_H2D)/sizeof(uint32_t));
    if (slot == -1)
        return EINVAL;

    p  = port->clb | (port->clbu << 32);
    HBA_CMD_HEADER *   cmdheader = (HBA_CMD_HEADER *)(p);

    cmdheader += slot;
    cmdheader->cfl = sizeof(FIS_REG_H2D)/sizeof(uint32_t);   // Command FIS size
    cmdheader->w = 1;               //
    cmdheader->c = 1;               /** mom_1, must be == 1 (!!!) **/
    cmdheader->p = 1;               /** mom_2, if==0 hang in 'while(ci != 0)s' (!!!) **/
    // 8K bytes (16 sectors) per PRDT
    cmdheader->prdtl = (uint16_t)((count-1)>>4) + 1;    // PRDT entries count

    p = cmdheader->ctba | (cmdheader->ctbau << 32);
    HBA_CMD_TBL *   cmdtbl = (HBA_CMD_TBL *)(p);

    memset(cmdtbl, 0, sizeof(HBA_CMD_TBL) + (cmdheader->prdtl-1)*sizeof(HBA_PRDT_ENTRY));
    int i = 0;
    // 8K bytes (16 sectors) per PRDT
    for (i=0; i<cmdheader->prdtl-1; i++)
    {
        cmdtbl->prdt_entry[i].dba  = (uint32_t)((uint64_t)buf & 0xFFFFFFFF);
        cmdtbl->prdt_entry[i].dbau = (uint32_t)(((uint64_t)buf >> 32) & 0xFFFFFFFF);
        cmdtbl->prdt_entry[i].dbc = 8*1024-1;     // 8K bytes
        cmdtbl->prdt_entry[i].i = 0;
        buf += 4*1024;  // 4K words
        count -= 16;    // 16 sectors
   }
    /**If the final Data FIS transfer in a command is for an odd number of 16-bit words, the transmitter�s
Transport layer is responsible for padding the final Dword of a FIS with zeros. If the HBA receives one
more word than is indicated in the PRD table due to this padding requirement, the HBA shall not signal
this as an overflow condition. In addition, if the HBA inserts padding as required in a FIS it is transmitting,
an overflow error shall not be indicated. The PRD Byte Count field shall be updated based on the
number of words specified in the PRD table, ignoring any additional padding.**/

    // Last entry
    cmdtbl->prdt_entry[i].dba   = (uint32_t)((uint64_t)buf & 0xFFFFFFFF);
    cmdtbl->prdt_entry[i].dbau  = (uint32_t)(((uint64_t)buf >> 32) & 0xFFFFFFFF);
    cmdtbl->prdt_entry[i].dbc   = count << 9 ;   // 512 bytes per sector
    cmdtbl->prdt_entry[i].i     = 1; // interrupt gen

    // Setup command
    FIS_REG_H2D *   cmdfis      = (FIS_REG_H2D *)(&cmdtbl->cfis);
    memset(cmdfis, 0, sizeof(FIS_REG_H2D));

    cmdfis->fis_type    = FIS_TYPE_REG_H2D;
    cmdfis->c           = 1;  // Command
    cmdfis->command     = ATA_CMD_WRITE_DMA_EX;

    cmdfis->lba0        = (uint8_t)startl;
    cmdfis->lba1        = (uint8_t)(startl >> 8);
    cmdfis->lba2        = (uint8_t)(startl >> 16);
    cmdfis->device      =  1 << 6 ;  // LBA mode //0xE0;

    cmdfis->lba3        = (uint8_t)(startl >> 24);
    cmdfis->lba4        = (uint8_t)starth;
    cmdfis->lba5        = (uint8_t)(starth >> 8);

    cmdfis->countl      = count & 0xff;
    cmdfis->counth      = count >> 8;

    cmdfis->control      = 8;  /** mom_3. also try write 0x08, 0x00 (!!!) **/

    // The below loop waits until the port is no longer busy before issuing
    // a new command
    while((port->tfd & (ATA_DEV_BUSY | ATA_DEV_DRQ)) && spin < 10000000)
    {
        spin++;
    }
    if(spin == 10000000)
    {
        printf("Port is hung %d\n", EDEADLK);
        return EDEADLK;
    }

    port->ci = 1 << slot;       // Issue command
    _ahci_engine_start(port);   /** mom_4 **/
    printf("W1:port->cmd=%08x,ci=%08x,tfd=%08x,sact=%08x ", port->cmd,port->ci, port->tfd, port->sact);
    delay_ms(1);   //debug only

    // Wait for completion
    uint32_t    cntr = 0;
    while (1)
    {
        // In some longer duration reads, it may be helpful to spin on the DPS bit
        // in the PxIS port field as well (1 << 5)
        if ((port->ci & (1<<slot)) == 0)
                break;
        if (port->is & HBA_PxIS_TFES)   // Task file error
        {
            printf("Read disk error\n");
            return EIO;
        }
        if(cntr++ > 1000*1000)
        {
            printf("WE:port->ci=%08x,is=%08x,tfd=%08x,sact=%08x ", port->ci, port->is, port->tfd, port->sact);
            _ahci_engine_stop(port);
            while(1);
        }
    }
    printf("W2:port->ci=%08x,tfd=%08x,sact=%08x ", port->ci, port->tfd, port->sact);
    // Check again
    if (port->is & HBA_PxIS_TFES)
    {
        printf("Read disk error\n");
        _ahci_engine_stop(port);
        return EIO;
    }
    while(port->ci != 0)
    {
   //     print("[%d]", k++);
    }
    printf("W:ok (ci=%d,sig=%08x)\n", port->ci,port->sig);

    _ahci_engine_stop(port);  /** mom_4 **/
    return 0;
}


Код:
void  _ahci_engine_stop(HBA_PORT* port)
{
    port->cmd &= ~(1 << 0);
    port->cmd &= ~(1 << 4); //FRE
    while(1)
    {
        if(port->cmd & AHCI_PORT_CMD_FIS_ON)  //FR
            continue;
        if(port->cmd & AHCI_PORT_CMD_LIST_ON) //CR
            continue;

        break;
    }
}
//---------------------------------------------
void   _ahci_engine_start(HBA_PORT* port)
{
    port->cmd |= (1 << 4);
    port->cmd |= (1 << 0);
    port->is = 0;
}


Несколько замечаний/наблюдений по коду (в коде помечены /** mom.x **/ )...
mom.1, mom.2
(_ahci_start_engine/stop_engine вызываются, между вызовами функции записи пауза = 3 sec)
1) Если "cmdheader->c = 0, cmdheader->p->0", функция зависает на проверке "while(port->ci != 0)" (распечатка где "WE": port->ci = 1, port->is = 0x01000020, port->tfd = 0xD0, port->sact = 0)
2) If "cmdheader->c = 1, cmdheader->p->0", 1й вызов функции завершается успешно (port->ci = 0, port->tfd = 0x150, port->sact = 0), но при 2м вызове функции зависаем в проверке окончания операции "while(port->ci != 0)" (port->ci = 1, port->is = 0, port->tfd = 0xD0).
3) If "cmdheader->c = 1, cmdheader->p->1" все вызовы функции записи завершаются успешно!!! Последующий цикл чтения показывает, что во все 5 секторов данные записались верно, но... если уменьшим паузу между вызовами функции записи (например, до 2х сек), то 2й вызов функции завершается ошибкой (port->ci = 0, port->is=0x41000001, port->tfd = 0x8451; fis_d2h: sts=0x51,err=0x84)... кеш? в списке ATA команд есть FLUSH CACHE, но видел в примерах осей (маленьких), чтобы его использовали.
4) mom.3, "cmdfis->control = 0" or "= 8"не влияет
5) mom4... why call "_ahci_start_engine"/"_ahci_stop_engine", т.е. при каждой операции port_engine запускается, после завершения - останавливается... я увидел этот момент в одной из baremetal реализации... С таким подходом и большой паузой :) я получил вариант, который пишет в цикле, причем данные записываются верно... Хотя во многих других реализациях я не видел, чтобы engine так часто дергали.
Если закоментировать вызовы stop/start_engine, 1й вызов функции записи завершается без ошибок, 2й с ошибкой (port->ci = 0, port->is = 0x41000021, port->tfd = 0x8451).


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 09 фев 2019, 21:00 

Зарегистрирован: 26 янв 2019, 14:00
Сообщения: 34
savver писал(а):
1) Если "cmdheader->c = 0, cmdheader->p->0", функция зависает на проверке "while(port->ci != 0)" (распечатка где "WE": port->ci = 1, port->is = 0x01000020, port->tfd = 0xD0, port->sact = 0)
2) If "cmdheader->c = 1, cmdheader->p->0", 1й вызов функции завершается успешно (port->ci = 0, port->tfd = 0x150, port->sact = 0), но при 2м вызове функции зависаем в проверке окончания операции "while(port->ci != 0)" (port->ci = 1, port->is = 0, port->tfd = 0xD0).
3) If "cmdheader->c = 1, cmdheader->p->1" все вызовы функции записи завершаются успешно!!! Последующий цикл чтения показывает, что во все 5 секторов данные записались верно, но... если уменьшим паузу между вызовами функции записи (например, до 2х сек), то 2й вызов функции завершается ошибкой (port->ci = 0, port->is=0x41000001, port->tfd = 0x8451; fis_d2h: sts=0x51,err=0x84)... кеш? в списке ATA команд есть FLUSH CACHE, но видел в примерах осей (маленьких), чтобы его использовали.
4) mom.3, "cmdfis->control = 0" or "= 8"не влияет
5) mom4... why call "_ahci_start_engine"/"_ahci_stop_engine", т.е. при каждой операции port_engine запускается, после завершения - останавливается... я увидел этот момент в одной из baremetal реализации... С таким подходом и большой паузой :) я получил вариант, который пишет в цикле, причем данные записываются верно... Хотя во многих других реализациях я не видел, чтобы engine так часто дергали.
Если закоментировать вызовы stop/start_engine, 1й вызов функции записи завершается без ошибок, 2й с ошибкой (port->ci = 0, port->is = 0x41000021, port->tfd = 0x8451).

Здравствуйте!
Довольно специфичная на мой взгляд ситуация. Я тестировал на разных устройствах ничего подобного не выявил. Вы присылали в пример SBUnix. Вы испольлозовали анолигичный метод адресации? Важным в выделении памяти под AHCI является выравнивание адреса под 1кб, иначе правильной работы ждать не стоит. Вы соблюли это? И почему вы не ожидаете очистку в коде ниже?
Код:
void   _ahci_engine_start(HBA_PORT* port)
{
    port->cmd |= (1 << 4);
    port->cmd |= (1 << 0);
    port->is = 0;
}


Вернуться к началу
 Профиль  
 
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 9 ] 

Часовой пояс: UTC + 3 часа


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  
cron
Создано на основе phpBB® Forum Software © phpBB Group
Русская поддержка phpBB