Как разрешить MMU ARM1136JFS (ARM v6) иметь однозначное соответствие между физическим и виртуальным адресным пространством? - PullRequest
3 голосов
/ 09 августа 2010

Я хочу включить кеш данных. У меня нет большого опыта работы с ARM, так как я в основном запрограммирован на IA32. Насколько я понимаю, мне нужно включить MMU, чтобы включить кэш данных. Поскольку мне не нужна виртуальная память в других отношениях, я хочу включить MMU с однозначным отображением между физическим и виртуальным адресным пространством для всех приложений.

Любая помощь / указатели / статьи или код приветствуется.

Ответы [ 4 ]

3 голосов
/ 12 января 2011

Каталог страниц 16K, содержащий виртуальное / физическое сопоставление один к одному, может быть создан с помощью следующего (непроверенного) кода:

/* Setup types for virtual addresses, physical address, and the page table. */
typedef unsigned long vaddr_t;
typedef unsigned long paddr_t;
typedef unsigned long pde_t;

/* Reserve space for a page directory. Must be 16k aligned. */
pde_t page_directory[1 << 12] ALIGNED(1 << 12);

/* Create a 1MB mapping in the given page directory from 'virt' to 'phys'. */
void set_large_page_mapping(pde_t *pd, vaddr_t virt, paddr_t phys)
{
    pde_t entry = 0;
    entry |= phys & 0xfff00000; /* Target of the mapping. */
    entry |= 2;                 /* This is a 1MB section entry. */
    entry |= 1 << 4;            /* Enable caches (C). */
    entry |= 1 << 3;            /* Enable writeback (B). */
    entry |= 3 << 10;           /* Full read/write permission. */
    pd[virt >> 20] = entry;     /* Install the entry. */
}

/* Setup a page directory with one-to-one physical/virtual mappings. */
void setup_one_to_one_mappings(void)
{
    unsigned long i;

    /* Setup a mapping for each 1MB region in the virtual address space. */
    for (i = 0; i < (1 << 12); i++) {
       /* Map the virtual address "i << 20" to phys address "i << 20". */
       set_large_page_mapping(page_directory, i << 20, i << 20);
    }

    /* TODO: Write function to install this page directory and enable the MMU. */
    enable_mmu(page_directory);
}

Функция enable_mmu еще должна быть написана, для чего нужно:

  • Очистить кэш данных и инструкций;
  • Недействительные существующие записи TLB;
  • Очистить другие кэши (буфер предварительной выборки, целевые кэши ветвления и т. Д.);
  • Установить TTBR0, чтобы он содержал указатель на PD, сгенерированный выше; и наконец
  • Включить кэши и MMU.

Каждая из этих инструкций, как правило, относится к конкретному процессору, но примеры (надеюсь) должны быть доступны для вашего оборудования в другом месте (или, если это не удастся, при просмотре источников в других операционных системах, таких как Linux или FreeBSD). Кроме того, для начала тестирования вам, вероятно, нужно беспокоиться только о последних двух моментах.

3 голосов
/ 27 апреля 2011

Ниже приведен код, позволяющий взаимно-однозначное сопоставление физического и виртуального адресного пространства:

#define NUM_PAGE_TABLE_ENTRIES 4096 /* 1 entry per 1MB, so this covers 4G address space */
#define CACHE_DISABLED    0x12
#define SDRAM_START       0x80000000
#define SDRAM_END         0x8fffffff
#define CACHE_WRITEBACK   0x1e

static inline void enable_mmu(void)
{
    static U32 __attribute__((aligned(16384))) page_table[NUM_PAGE_TABLE_ENTRIES];
    int i;
    U32 reg;

    /* Set up an identity-mapping for all 4GB, rw for everyone */
    for (i = 0; i < NUM_PAGE_TABLE_ENTRIES; i++)
        page_table[i] = i << 20 | (3 << 10) | CACHE_DISABLED;
    /* Then, enable cacheable and bufferable for RAM only */
    for (i = SDRAM_START >> 20; i < SDRAM_END >> 20; i++)
    {
        page_table[i] = i << 20 | (3 << 10) | CACHE_WRITEBACK;
    }

    /* Copy the page table address to cp15 */
    asm volatile("mcr p15, 0, %0, c2, c0, 0"
            : : "r" (page_table) : "memory");
    /* Set the access control to all-supervisor */
    asm volatile("mcr p15, 0, %0, c3, c0, 0" : : "r" (~0));

    /* Enable the MMU */
    asm("mrc p15, 0, %0, c1, c0, 0" : "=r" (reg) : : "cc");
    reg|=0x1
    asm volatile("mcr p15, 0, %0, c1, c0, 0" : : "r" (reg) : "cc");
}
2 голосов
/ 11 августа 2010

Вы можете попробовать что-то подобное (см. Ниже).Я хотел сделать то же самое, включить кеш данных и сделать один в один.Некоторые инструменты, документы и код могут усложнить проблему.Вам нужен trm от arm для конкретного mmu в вашем ядре и / или таблица данных для core, где бы ни была задокументирована mmu.

Я использую mp11 ядро ​​arm11, которое, вероятно, похоже на то, что у вас есть, mmuдостаточно похож на arm9.У mpcore была новая запись в таблице больших блоков, которая была в 16 раз больше следующей, но это была пустая трата времени, потому что вам пришлось поместить 16 копий той же записи в таблицу mmu.

Вам нужен кусок памяти для хранения таблицы mmu, вам нужно поместить в таблицу запись для физического адреса этого блока памяти, который является первой записью, которую он может найти, посмотрите в mmuстол, чтобы узнать о таблице MMU своего рода вещь.Точно так же область таблицы векторов прерываний (адрес ноль) должна быть там.используйте самые грубые записи, которые вы можете.

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

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MMUTABLEBASE 0x00000000

#define MMUALLOC (0x8000>>2)

unsigned int mmu_table[MMUALLOC];
unsigned int mmu_table_owner[MMUALLOC];

#define TOP_LEVEL_WORDS (1<<((31-20)+1))
#define COARSE_TABLE_WORDS (1<<((19-12)+1))
#define SMALL_TABLE_WORDS (1<<((11-0)+1))

unsigned int base;
unsigned int nextfree;


unsigned int next_coarse_offset ( unsigned int x )
{
    unsigned int mask;

    mask=(~0)<<(10-2);
    mask=~mask;
    while(x&mask) x++; //lazy brute force
    return(x);
}

unsigned int add_one ( unsigned int add, unsigned int flags )
{
    unsigned int ra;
    unsigned int rb;
    unsigned int rc;

    ra=add>>20;
    if(mmu_table[ra])
    {
        printf("Address %08X already allocated\n",add);
        return(1);
    }
    add=ra<<20;

    rb=next_coarse_offset(nextfree);
    rc=rb+COARSE_TABLE_WORDS;

    if(rc>=MMUALLOC)
    {
        printf("Not enough room\n");
        return(1);
    }
    nextfree=rc;

    mmu_table[ra]=(MMUTABLEBASE+(rb<<2))|0x00000001;

    for(ra=0;ra<COARSE_TABLE_WORDS;ra++)
    {
        mmu_table[rb+ra]=(add+(ra<<12))|0x00000032|flags;
        mmu_table_owner[rb+ra]=(add+(ra<<12));
    }
    return(0);
}



int main ( void )
{

    memset(mmu_table,0xF0,sizeof(mmu_table));

    for(nextfree=0;nextfree<TOP_LEVEL_WORDS;nextfree++)
    {
        mmu_table[nextfree]=0x00000000;
        mmu_table_owner[nextfree]=nextfree<<20;
    }

    if(add_one(0xD6000000,0x0000|8|4)) return(1);
    if(add_one(0x00000000,0x0000|8|4)) return(1);
    if(add_one(0xC3F00000,0x0000)) return(1);
    if(add_one(0xCA000000,0x0000)) return(1);


    printf("    .globl _start\n");
    printf("_start:\n");

    for(base=0;base<nextfree;base++)
    {
        printf(".word 0x%08X ;@ [0x%08X] 0x%08X\n",mmu_table[base],MMUTABLEBASE+(base<<2),mmu_table_owner[base]);
    }

    for(;base<MMUALLOC;base++)
    {
        printf(".word 0x00000000 ;@ [0x%08X]\n",MMUTABLEBASE+(base<<2));
    }
    printf("   b   zreset\n");
    for(base=0;base<15;base++) printf("    b zhang\n");
    printf("zhang: b zhang\n");
    printf("zreset:\n");
    printf("  ldr pc,=reset\n");
    printf("\n");


    return(0);
}
0 голосов
/ 29 августа 2013

Для полноты, вот дополнительный код, который необходим для фактического включения кэширования данных после настройки MMU (а также кэширования инструкций, но это также работает без MMU):

    void enable_data_cache()
    {
        asm volatile( "  mrc p15, 0, r0, c1, c0, 0" );  // Read c1 into r0
        asm volatile( "  orr r0, r0, #4" );             // Set bit 2: Dcache
        asm volatile( "  mcr p15, 0, r0, c1, c0, 0" );  // Return r0 to c1
    }

    void disable_data_cache()
    {
        asm volatile( "_disable_data_cache_start_:" );
        asm volatile( "  mrc p15, 0, r15, c7, c14, 3" ); // test, clean and invalidate
        asm volatile( "  bne _disable_data_cache_start_" );

        asm volatile( "  mov r0,#0" );
        asm volatile( "  mcr p15, 0, r0, c7, c5, 0" ); //  invalidate I cache
        asm volatile( "  mcr p15, 0, r0, c7, c10, 4"); //  drain write buffer

        asm volatile( "  mrc p15, 0, r0, c1, c0, 0" );  // Read c1 into r0
        asm volatile( "  bic r0, r0, #4"     );         // Clear bit 2: disable Dcache
        asm volatile( "  mcr p15, 0, r0, c1, c0, 0"  ); // Return r0 to c1
    }

    void clean_data_cache()
    {
        asm volatile( "_clean_data_cache_start_:" );
        asm volatile( "  mrc p15, 0, r15, c7, c14, 3" ); // test, clean and invalidate
        asm volatile( "  bne _clean_data_cache_start_" );
    }

    void enable_instruction_cache()
    {
        asm volatile( "  mrc p15, 0, r0, c1, c0, 0" );  // Read c1 into r0
        asm volatile( "  orr r0, r0, #4096" );          // Set bit 12: Icache
        asm volatile( "  mcr p15, 0, r0, c1, c0, 0" );  // Return r0 to c1
    }

    void disable_instruction_cache()
    {
        asm volatile( "  mrc p15, 0, r0, c1, c0, 0" );  // Read c1 into r0
        asm volatile( "  bic r0, r0, #4096" );          // Clearr bit 12: Icache
        asm volatile( "  mcr p15, 0, r0, c1, c0, 0" );  // Return r0 to c1
    }
...