как правильно обновить таблицу перевода MMU

Я включил MMU на своей плате s3c2440 (память 3G - 4G :: атрибут ошибки), все было в порядке, когда я не читал/записывал память 3G - 4G. Итак, чтобы проверить вектор ошибки страницы, я записал 0xFF в адрес 3G, как я и ожидал, я получил правильное значение от FSR, поэтому я сделал это в _do_page_fault(), шаг был таким:

.....                 // set new page to translation table 
.....
invlidate_icache ();  // clear icache 
clr_dcache ();        // wb is used ,clear dcache 
invalidate_ttb ();    // invalidate translation table 

а затем ISR_dataabort вернулся, я прочитал адрес 3G, чтобы получить 0xFF, который я написал ранее. К сожалению, я снова получил прерывание данных (я уверен, что значение таблицы перевода, которое я установил, в порядке)

Итак, как правильно обновить таблицу перевода MMU. Любая помощь приветствуется! спасибо

вот основной код, который я использовал (просто для некоторого теста), (я немного не знаком с ARM ARCH, поэтому этот код может быть срочным)

/* MMU TTB 0 BASE ATTR */
#define TTB0_FAULT          (0|(1<<4))          /* TTB FAULT */
#define TTB0_COARSE         (1|(1<<4))          /* COARSE PAGE BASE ADDR */
#define TTB0_SEG            (2|(1<<4))          /* SEG BASE ADDR */
#define TTB0_FINE           (3|(1<<4))          /* FINE PAGE BASE ADDR */

/* MMU TTB 1 BASE ATTR */ 
#define TTB1_FAULT          (0)
#define TTB1_LPG            (1)                 /* Large page */
#define TTB1_SPG            (2)                 /* small page */
#define TTB1_TPG            (3)                 /* tiny page */

/* domain access priority level */
#define FAULT_PL            (0x0)               /* domain fault */
#define USR_PL              (0x1)               /* usr mode */
#define RSV_PL              (0x2)               /* reserved */
#define SYS_PL              (0x3)               /* sys mode */

#define DOMAIN_FAULT        (0x0<<5)            /* fault 0*/
#define DOMAIN_SYS          (0x1<<5)            /* sys 1*/
#define DOMAIN_USR          (0x2<<5)            /* usr 2*/

/* C,B bit */
#define CB                  (3<<2)              /* cache_on, write_back */
#define CNB                 (2<<2)              /* cache_on, write_through */ 
#define NCB                 (1<<2)              /* cache_off,WR_BUF on */
#define NCNB                (0<<2)              /* cache_off,WR_BUF off */

/* ap 2 bits */
#define AP_FAULT            (0<<10)             /* access deny */
#define AP_SU_ONLY          (1<<10)             /* rw su only */
#define AP_USR_RO           (2<<10)             /* sup=RW, user=RO */
#define AP_RW               (3<<10)             /* su=RW, user=RW */

/* page dir 1 ap0 */
#define AP0_SU_ONLY         (1<<4)             /* rw su only */
#define AP0_USR_RO          (2<<4)             /* sup=RW, user=RO */
#define AP0_RW              (3<<4)             /* su=RW, user=RW */

/* page dir 1 ap1 */
#define AP1_SU_ONLY         (1<<6)             /* rw su only */
#define AP1_USR_RO          (2<<6)             /* sup=RW, user=RO */
#define AP1_RW              (3<<6)             /* su=RW, user=RW */


/* page dir 1 ap2 */
#define AP2_SU_ONLY         (1<<8)             /* rw su only */
#define AP2_USR_RO          (2<<8)             /* sup=RW, user=RO */
#define AP2_RW              (3<<8)             /* su=RW, user=RW */

/* page dir 1 ap3 */
#define AP3_SU_ONLY         (1<<10)             /* rw su only */
#define AP3_USR_RO          (2<<10)             /* sup=RW, user=RO */
#define AP3_RW              (3<<10)             /* su=RW, user=RW */


#define RAM_START           (0x30000000)
#define KERNEL_ENTRY        (0x30300000)        /* BANK 6 (3M) */
#define KERNEL_STACK        (0x3001A000)        /* BANK 6 (16K + 64K + 16K + 8K) (8k kernel stack) */
#define IRQ_STACK           (0x3001B000)        /* 4K IRQ STACK */
#define KERNEL_IMG_SIZE     (0x20000)            

#define IRQ_STACK           (0x3001B000)

/* 16K aignment */
#define TTB_BASE            (0x30000000)        
#define PAGE_DIR0           (TTB_BASE)
#define TTB_FULL_SIZE       (0x4000)        
#define PAGE_DIR1           (TTB_BASE+TTB_FULL_SIZE)                 

#define PAGE_DIR0_SIZE      (0x4000)            /* 16k */


    void _do_page_fault (void)
    {
        //
        ...........
        // 
        // read the FSR && get the vaddr && type here 
    volatile unsigned *page_dir = (volatile unsigned*)(TTB_BASE);  
    unsigned index = vaddr >> 20,i = 0, j = 0;
    unsigned page = 0;

        if (!(page_dir[index] & ~(0x3FF) && (type == 0x0B))) {          /* page_dir empty */
            i = index & ~0x03;                                          
            if ( (page_dir[i+0] & ~(0x3FF)) || (page_dir [i+1] & ~(0x3FF))         
              || (page_dir[i+2] & ~(0x3FF)) || (page_dir [i+3] & ~(0x3FF)) )
            {
                panic ( "page dir is bad !\n" );                        /* 4 continuous page_dir must be 0 */
            }

            if (!(page = find_free_page ())) 
                panic ( "no more free page !\n" );                      /* alloc a page page dir*/

            page_dir[i+0] = (page + 0x000) | DOMAIN_USR | TTB0_COARSE ; /* small page 1st 1KB */
            page_dir[i+1] = (page + 0x400) | DOMAIN_USR | TTB0_COARSE ; /* small page 2nd 1KB */
            page_dir[i+2] = (page + 0x800) | DOMAIN_USR | TTB0_COARSE ; /* small page 3rd 1KB */
            page_dir[i+3] = (page + 0xC00) | DOMAIN_USR | TTB0_COARSE ; /* small page 4th 1KB */

            if (!(page = find_free_page ())) 
                panic ( "no more free page !\n" );                      /* alloc a page page table*/

            volatile unsigned *page_tbl = (volatile unsigned*) (page_dir[index] & ~(0x3FF));

            *page_tbl = page|AP0_RW|AP1_RW|AP2_RW|AP3_RW| NCNB|TTB1_SPG;/* small page is used */


            invalidate_icache ();

            for (i = 0; i < 64; i++)
            {
                for (j = 0;j < 8;j ++)
                    clr_invalidate_dcache ( (i<<26)|(j<<5) );
            }


            invalidate_tlb ();
        }
        ........
        //

    }

    /* here is the macros */

    #define invalidate_tlb()    \
    {\
         __asm__  __volatile__ (\
            "mov    r0,#0\n"\
            "mcr    p15,0,r0,c8,c7,0\n"\
            :::"r0" \
        );\
    }

    #define clr_invalidate_dcache(index)    \
    {\
        __asm__  __volatile__ (\
            "mcr    p15,0,%[i],c7,c14,2\n"\
            :: [i]"r"(index)\
        );\
    }


    #define invalidate_icache() \
    {\
        __asm__  __volatile__ (\
            "mov    r0,#0\n"\
            "mcr    p15,0,r0,c7,c5,0\n"\
            ::: "r0"\
        );\
    }

    #define invalidate_dcache() \
    {\
        __asm__  __volatile__ (\
            "mov    r0,#0\n"\
            "mcr    p15,0,r0,c7,c6,0\n"\
            ::: "r0"\
        );\
    }



    #define invalidate_idcache()    \
    {\
        __asm__  __volatile__ (\
            "mov    r0,#0\n"\
            "mcr    p15,0,r0,c7,c7,0\n"\
            :::"r0"\
        );\
    }\

person true_casey    schedule 05.05.2013    source источник
comment
Укажите фактический используемый код. Короткий фрагмент псевдокода с ошибками без какой-либо информации о том, какую операционную систему вы используете, не дает ни малейшего намека на то, в чем может быть ваша проблема. Кроме того, тот факт, что вы уверены в своей записи в таблице переводов, не означает, что она верна, поэтому, пожалуйста, включите и это.   -  person unixsmurf    schedule 05.05.2013
comment
Код, который я использовал, опубликован выше, я не знаю, почему мой s3c2440 останавливается при вызове invalidate_tbl (), вы можете мне помочь, спасибо!   -  person true_casey    schedule 05.05.2013
comment
Если вы не хотите показывать свои определения, вы можете показать hex дампы записей таблицы L1 и L2 непосредственно перед прерыванием. Обязательно укажите смещения относительно баз TTB_BASE и L2. Если вы сделаете это, вы можете ответить на свой вопрос.   -  person artless noise    schedule 05.05.2013
comment
все #defines уже опубликованы. спасибо за помощь ^_^   -  person true_casey    schedule 05.05.2013


Ответы (1)


Примечание. Я предполагаю, что TTB_BASE является основной таблицей страниц ARM L1. Если это какая-то тень, вам нужно показать больше кода в соответствии с unixsmurf. Вот мое лучшее предположение...

Ваш page_dir функционирует как первичные записи L1 и как таблица страниц L2. TTB_BASE должен содержать только разделы, суперразделы или указатели к таблицам подстраниц. Вам необходимо выделить больше физической памяти для таблиц страниц L2.

Я предполагаю, что ваши page_dir[i+0], page_dir[i+1] и т. д. перезаписывают другие записи раздела L1, и ваш invalidate_tlb() делает это конкретным для ЦП. Вы должны использовать указатель L2 page_tbl для установки/индексации маленьких/тонких страниц. Возможно, код вашего ядра находится в области 3G-4G, и вы перезаписываете туда некоторые важные сопоставления L1; может произойти любое количество странных вещей. Текущее использование page_tbl неясно.

Вы не можете использовать дважды первичные таблицы L1, поскольку они имеют значение для оборудования. Я предполагаю, что это просто ошибка?

В основных таблицах страниц L1 есть только три типа записей:

  1. Разделы - 1 МБ памяти, отображаемой прямо в физ, без таблицы 2nd страниц.
  2. Супер-разделы — отображение 4 МБ памяти в соответствии с разделами, но минимизирует нагрузку на TLB.
  3. Таблица на 2й странице. Записи могут быть 1k, 4k и 64k. Таблицы Fine Page имеют размер 4 КБ и не используются в современных проектах ARM. Каждая запись представляет собой 1 КБ адреса в прекрасной таблице страниц.

Таблица основных страниц должна находиться на физическом адресе, который выровнен по 16 КБ, что также соответствует ее размеру. 16k/(4bytes/entry)*1MB указывает диапазон адресов 4 ГБ для таблиц L1. Таким образом, каждая запись в основной таблице страниц L1 всегда относится к записи 1 МБ. Приблизительные таблицы страниц – это единственный вариант для новых ARM, и они ссылаются на таблицу L2 объемом 1 КБ.

1K_L2_size * 4K_entry / (4bytes_per_entry) gives 1MB address space.
1K_L2_size * 64K_entry / (16bytes_per_entry) gives 1MB address space.

В основном разделе L1 есть четыре записи по 1 МБ каждая для надраздела. Каждая из четырех записей соответствует большой странице размером 64 КБ в таблице L2. Если вы используете суперразделы, у вас нет записей L2.

Я думаю, вы могли перепутать суперразделы и большие страницы? Наверняка неправильно отформатированные таблицы страниц будут проявляться только при аннулировании TLB, так что сопоставления MMU повторно извлекаются из таблиц посредством обхода.

Наконец, вы должны очистить кэш D и слить буфер записи, чтобы обеспечить согласованность с памятью. Вы также можете установить барьер памяти.

static inline void dcache_clean(void)
{
    const int zero = 0;
    asm volatile ("" ::: "memory"); /* barrier */
    /* clean entire D cache -> push to external memory. */
    asm volatile ("1: mrc p15, 0, r15, c7, c10, 3\n"
                    " bne 1b\n" ::: "cc");
    /* drain the write buffer */
    asm volatile ("mcr 15, 0, %0, c7, c10, 4"::"r" (zero));
}

Существуют также команды сопроцессора для аннулирования отдельных записей TLB, поскольку вы изменяете только часть сопоставлений vaddr/paddr в ошибке данных.

См. также: руководство по ARM MMU, Структуры виртуальной памяти и ваше Справочное руководство по архитектуре ARM.

person artless noise    schedule 05.05.2013
comment
да, TTB_BASE - это page_table уровня 1, которая стоит физический адрес 16 КБ. Ситуация такова, что я хочу использовать карту разделов 0x0000 - 0x30000000 && 0x40000000 - 0xA0000000 и использовать грубую карту страниц (стоимость 1 КБ), 0x30000000 - 0x34000000 (SDRAM = 64M). по той причине, что L2 (грубая таблица страниц стоит 1 КБ, я позволяю 4 непрерывным 4 таблицам страниц L2 использовать одну и ту же страницу. Вот почему я использую «i = index & ~ 0x03». - person true_casey; 05.05.2013
comment
в таблице L1 i = index & ~ 3, чтобы получить индекс выравнивания 4M, в L1 стоимость 4 * 4 байта, когда один из каталогов страниц 4M пуст, все каталоги страниц L1 в индексе и ~ 3 объектах должны указывать на один и тот же page *page_tbl означает, что я использовал page_tbl[0], может быть, это неправильно, когда vaddr не соответствует выравниванию 4M, но адрес 3G в порядке. - person true_casey; 05.05.2013
comment
одна таблица L1 page_table занимает 4 байта, ее ptr равен 1 МБ независимо от типа. той же странице, и если vaddr равно 3M, то запись L1 3M (страница 4K) будет такой же, как 0M, 1M, 2M. i = index & ~3 получить запись выравнивания 4M. - person true_casey; 05.05.2013
comment
Я имею в виду, что запись L1 3M vaddr занимает ту же физическую страницу, что и 0M, 1M, 2M ... но их смещение отличается, 0M имеет 0 - (0x3FF), 1M имеет 0X400 - 7ff) ...... - person true_casey; 05.05.2013
comment
Теперь все в порядке, после того как я отключил mmu и icache, dcache тоже. А затем сделал недействительным i & d cache, в конце я снова включил mmu i d cache ... Это правильный способ обновить таблицу перевода MMU ??? хотя это работает, ЭТО СТОИТ СЛИШКОМ МНОГО ЦП ???? КТО-НИБУДЬ ЗНАЕТ??? - person true_casey; 06.05.2013
comment
Нет, это не нужно, но это хорошая информация для добавления. Я добавил в свой ответ дополнительный код, который может помочь. - person artless noise; 06.05.2013
comment
Большое спасибо ^_^. Теперь я прочитал ARM_ARM, у меня есть правильный способ обновить таблицу MMU: 1. очистка кеша данных, если это кеш с обратной записью 2. аннулирование кеша данных 3. аннулирование кеша инструкций 4 .опорожнение буфера записи. - person true_casey; 06.05.2013
comment
Странно аннулировать icache. Я предполагаю, что это для устаревшего кеша, который находится в новой записи TLB. Поскольку это была ошибка данных, вы могли бы предположить, что это не было в icache, но лучше перестраховаться, чем потом сожалеть. - person artless noise; 06.05.2013
comment
Теперь мне не нужно отключать mmu, я просто ввожу код: clr_all_dcache (); //dcache_clean (); invalidate_dcache (); __asm__ __volatile__ ("mcr p15,0,%0,c8,c6,1"::"r"(vaddr)); - person true_casey; 07.05.2013