ARM
cortex-A

rt-threadのラズパイ2MMU設定

cortex-AのMMU勉強中ということでrt-threadのMMU設定を見てみる。


rt_hw_mmu_init()

rt-threadにおけるMMU設定の全体。


bsp/raspi2/cpu/mmu.c

void rt_hw_mmu_init(void)

{
rt_hw_cpu_dcache_disable();
rt_hw_cpu_icache_disable();
rt_cpu_mmu_disable();

/* set page table */
/* 4G 1:1 memory */
rt_hw_mmu_setmtt(0, 0xffffffff-1, 0, RW_CB);
/* IO memory region */
rt_hw_mmu_setmtt(0x44000000, 0x80000000-1, 0x44000000, RW_NCNBXN);

/*rt_hw_cpu_dump_page_table(MMUTable);*/
rt_hw_set_domain_register(0x55555555);

rt_cpu_tlb_set(MMUTable);

rt_cpu_mmu_enable();

rt_hw_cpu_icache_enable();
rt_hw_cpu_dcache_enable();
}


以降で各関数について説明していく。


rt_hw_mmu_setmtt()


bsp/raspi2/cpu/mmu.c

/* level1 page table, each entry for 1MB memory. */

volatile static unsigned long MMUTable[4*1024] __attribute__((aligned(16*1024)));
void rt_hw_mmu_setmtt(rt_uint32_t vaddrStart,
rt_uint32_t vaddrEnd,
rt_uint32_t paddrStart,
rt_uint32_t attr)
{
volatile rt_uint32_t *pTT;
volatile int i, nSec;
pTT = (rt_uint32_t *)MMUTable + (vaddrStart >> 20);
nSec = (vaddrEnd >> 20) - (vaddrStart >> 20);
for(i = 0; i <= nSec; i++)
{
*pTT = attr | (((paddrStart >> 20) + i) << 20);
pTT++;
}
}

メモリ変換テーブルの設定を行っている。

short-descriptionでの1段めのテーブルは立てたビットにより名称と使用方法が変わるが、rt-threadではSectionと呼ばれる1段目テーブルのみを使用する1MB単位でのメモリ変換を定義している。

1st-table-section.png

なお1段めテーブルのBit[1]が1かつBit[18]が0でSectionとなる。

このコードでは仮想アドレスvaddrStartからvaddrEndまでのアドレスを物理アドレスpaddrStartとリニアに対応付けている。


属性について

引数に指定している属性は以下の通り。


bsp/raspi2/cpu.mmu.c

#define DESC_SEC       (0x2)

#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
#define AP_RW (3<<10) //supervisor=RW, user=RW
#define AP_RO (2<<10) //supervisor=RW, user=RO
#define XN (1<<4) // eXecute Never

#define DOMAIN0 (0x0<<5)
#define DOMAIN1 (0x1<<5)

/* Read/Write, cache, write back */
#define RW_CB (AP_RW|DOMAIN0|CB|DESC_SEC)
/* Read/Write, cache, write through */
#define RW_CNB (AP_RW|DOMAIN0|CNB|DESC_SEC)
/* Read/Write without cache and write buffer */
#define RW_NCNB (AP_RW|DOMAIN0|NCNB|DESC_SEC)
/* Read/Write without cache and write buffer, no execute */
#define RW_NCNBXN (AP_RW|DOMAIN0|NCNB|DESC_SEC|XN)
/* Read/Write without cache and write buffer */
#define RW_FAULT (AP_RW|DOMAIN1|NCNB|DESC_SEC)

rt-threadではIOメモリである0x4400_0000から0x8000_0000-1までをRW_NCNBXNつまり、「実行不可、RW可、ドメイン0,キャッシュ/バッファOFF」とし(AP_ROじゃなくて良いのか?とも思うがrt-threadってそもそもユーザーモード使ってないし…)、それ以外をRW_CBつまり「実行可、RW可、ドメイン0、キャッシュON」としている。


ドメインについて

B3.7.3参照

short-descriptionでは16種類のドメインが設定でき、それぞれについて「No access」、「Clients」、「Managers」の設定が可能。

rt-threadでは全てのドメインが「Clients」に設定されている。

「Clients」ではメモリアクセスの権限がチェックされ、権限がない場合はパーミッションフォルトが発生する。


rt_cpu_tlb_set()


bsp/raspi2/cpu/cp15_gcc.S

.globl rt_cpu_tlb_set

rt_cpu_tlb_set:
mcr p15, #0, r0, c2, c0, #0
dmb
bx lr

TTBR0に対してメモリ変換テーブルの先頭アドレスの設定。

TTBR0のフォーマットは以下の通り

32-bit-TTBR0.png

xは14-TTBCR.N(後述)。ここではTTBCR.N=0なのでx=14。

[31:x]はメモリ変換テーブルのアドレスをxビットシフトしたものになっている。

その他のビットは主にメモリ共有関連の設定になっているがあまり良くわかってないので要追記。

x=14なのでメモリ変換テーブルは1MB境界になっている必要がある。rt_cpu_tlb_set(MMUTable)という形で呼び出されるが,MMUTablealigned(16*1024 /*0x4000*/)なので1MB境界となっている。


TTBCRについて

rt-threadではTTBCRの設定は行っていないため、デフォルト値の0になっていると思われる。

最上位ビットEAEが0の場合はTTBCRは以下のshort-descriptor形式をとる。

short-descriptor_TTBCR.png

Nが0にになっているので、rt-threadではTTBR0しか使わないことになる。


rt_hw_set_domain_regiter()


bsp/raspi2/cpu/mmu.c

unsigned long rt_hw_set_domain_register(unsigned long domain_val)

{
unsigned long old_domain;

asm volatile ("mrc p15, 0, %0, c3, c0\n" : "=r" (old_domain));
asm volatile ("mcr p15, 0, %0, c3, c0\n" : :"r" (domain_val) : "memory");

return old_domain;
}


rt-threadでは16全てのドメインにClients(=0x01)を設定するため、

0x5555_5555を設定している(0x5=0b0101)


rt_cpu_mmu_enable()


bsp/raspi2/cpu/cp15_gcc.S

.globl rt_cpu_mmu_enable

rt_cpu_mmu_enable:
mrc p15, #0, r0, c1, c0, #0
orr r0, r0, #0x001
mcr p15, #0, r0, c1, c0, #0 @ set mmu enable bit
dsb
bx lr

SCTLR.M[0]を有効にするだけ。


参考・引用URL