Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

ARM AArch64のMMU

More than 1 year has passed since last update.

AArch64のMMUを使ってみました。

MMUを使ってみたといっても、論理アドレスと物理アドレスを1対1対応させただけです。

動作確認はQEMU ver 2.12 で行いました。

MMUの機能

AArch64のMMUは二つの機能があります。

  • 論理アドレスから物理アドレスに変換
  • メモリ属性の設定

アドレス変換

ARMのドキュメントにある、アドレス変換のイメージイラストです。

スクリーンショット 2018-06-26 22.01.22.png

あとで説明を書く。

キャッシュポリシーとメモリ属性

今回使うキャッシュポリシーは3種類です。

  • デバイスメモリ、キャッシュしない
  • ノーマルメモリ、キャッシュしない
  • ノーマルメモリ、リードキャッシュ有効、ライトキャッシュ有効、ライトバックキャッシュ有効

今回明示的に設定するメモリ属性は3つです。

  • AF : Accesss Flag : アクセス可能なら1
  • NS : Security bit: Non Secureなら1
  • SH : Shareable attribute : よくわかっていないデータキャッシュの共有についてのなにか。

MMU関連レジスタ

MMUの設定に関連するレジスタの概要です。とりあえずEL1用のものだけです。

  • MAIR_EL1 : Memory Attribute Indirection Register (EL1) キャッシュポリシーの設定。ここで設定した値のインデックスをページテーブルエントリで指定する。
  • TCR_EL1 : Translation Control Register (EL1) MMUの設定
  • TTBR0_EL1  : Translation Table Base Register 0 (EL1) 変換テーブルアドレス
  • TTBR1_EL1 : Translation Table Base Register 1 (EL1) 変換テーブルアドレス 使わない
  • SCTLR_EL1 : System Control Register (EL1) システムの設定、MMUの有効化はこのレジスタ

詳細は Architecture Reference Manual を見てください。

ページテーブルエントリ

ページテーブルのエントリーの長さは64ビットです。

エントリのフォーマットは3種類あります。

スクリーンショット 2018-06-26 22.01.48.png

今回は、1段目はTable descriptorを使い、2段目はTable entryを使います。

ページテーブル設定例

Raspberry Pi 3のMMUを、論理アドレスと仮想アドレスを1対1に設定してみました。

Raspberry Pi 3のメモリマップ

RAMは1024MB搭載されており、そのうち128MBをGPU(VideoCore4)に割り当て、残りを880MBをARMに割り当てる設定です。

物理アドレス 用途 属性
0000_0000 - 36FF_FFFF RAM ノーマルメモリ、リードキャッシュ有効、ライトキャッシュ有効、ライトバックキャッシュ有効
3700_0000 - 3EFF_FFFF GPU用RAM ノーマルメモリ、キャッシュしない
3F00_0000 - 3FFF_FFFF Peripheral デバイスメモリ、キャッシュしない
4000_0000 - 41FF_FFFF Mailbox デバイスメモリ、キャッシュしない
4200_0000 - FFFF_FFFF 使わない 未使用

ページテーブルの配置

ページテーブルは4KBアラインで配置する必要があるみたいです。

1段目のページテーブル

使うのは2エントリです。
1エントリ当たり1024GBの範囲を変換する。

  • 0 : 0-1GBの範囲だったら、0-511のL2 Page Tableを適用
  • 1 : 1-2GBの範囲だったら、512-1023のL2 Page Tableを適用
  • 2-511 : 未使用

2段目のページテーブル

使うのは513エントリです。
1エントリ当たり2MBの範囲を変換する。

  • 0-440 : RAM
  • 441-502 : GPU用RAM
  • 503-511 : ペリフェラル
  • 512 : Mailbox
  • 513-1023 : 未使用

MMU設定手順

  • MAIR_EL1 にキャッシュポリシーを3つ設定
  • L2 Page Table に値を設定
  • L1 Page Table に値を設定
  • TTBR0_EL1 に L1 Page Tableのアドレスを設定
  • TCR_EL1 を設定
  • SCTLR_EL1 を設定しMMUとキャッシュを有効

コード

実際のコードです。

static __attribute__((aligned(4096))) uint64_t l1_page_table[512];
static __attribute__((aligned(4096))) uint64_t l2_page_table[1024];

void mmu_init(void)
{
    uint32_t i;
    uint64_t r;

    /* clear pipeline */
    asm volatile("dsb sy");

    /* cache policy */
    r = ((0x00ul << (2 * 8)) |   /* device memory */
         (0x44ul << (1 * 8)) |   /* normal memory and no data cache */
         (0xfful << (0 * 8)));   /* normal memory and data cache */
    asm volatile ("msr mair_el1, %0" : : "r" (r));

    /* l2 page table */
    /* RAM        : AF=1, SH=3, Indx=0, EntryType=1 */
    for (i = 0; i < 441; i++) {
        l2_page_table[i] = (uintptr_t) i << (21 - 12 + 12) | 1 << 10 | 3 << 9 | 0 << 2 | 1;
    }
    /* GPU RAM    : AF=1, Indx=1, EntryType=1 */
    for (i = 441; i < 503; i++) {
        l2_page_table[i] = (uintptr_t) i << (21 - 12 + 12) | 1 << 10          | 1 << 2 | 1;
    }
    /* peripheral : AF=1, Indx=2, EntryType=1 */
    for (i = 503; i < 512; i++) {
        l2_page_table[i] = (uintptr_t) i << (21 - 12 + 12) | 1 << 10          | 2 << 2 | 1;
    }
    /* mailbox    : AF=1, Indx=2, EntryType=1 */
    l2_page_table[512] = (uintptr_t) 512 << (21 - 12 + 12) | 1 << 10          | 2 << 2 | 1;
    for (i = 513; i < 1024; i++) {
        l2_page_table[i] = 0;
    }

    /* l1 page table */
    l1_page_table[0] = ((uintptr_t) &l2_page_table[0] >> 12 ) << 12 | 1<<5 | 3;
    l1_page_table[1] = ((uintptr_t) &l2_page_table[512] >> 12 ) << 12 | 1<<5 | 3;
    for (i = 2; i < 512; i++) {
        l1_page_table[i] = 0;
    }

    /* ttbr */
    asm volatile ("msr ttbr0_el1, %0" : : "r" ((uintptr_t)&l1_page_table[0]));
    asm volatile ("isb");

    /* tcr */
    r = (0b00LL << 37) | // TBI=0, no tagging
        (0b000LL << 32)| // IPS= 32 bit ... 000 = 32bit, 001 = 36bit, 010 = 40bit
        (0b10LL << 30) | // TG1=4k ... options are 10=4KB, 01=16KB, 11=64KB ... take care differs from TG0
        (0b11LL << 28) | // SH1=3 inner ... options 00 = Non-shareable, 01 = INVALID, 10 = Outer Shareable, 11 = Inner Shareable
        (0b01LL << 26) | // ORGN1=1 write back .. options 00 = Non-cacheable, 01 = Write back cacheable, 10 = Write thru cacheable, 11 = Write Back Non-cacheable
        (0b01LL << 24) | // IRGN1=1 write back .. options 00 = Non-cacheable, 01 = Write back cacheable, 10 = Write thru cacheable, 11 = Write Back Non-cacheable
        (0b0LL  << 23) | // EPD1 ... Translation table walk disable for translations using TTBR1_EL1  0 = walk, 1 = generate fault
        (25LL   << 16) | // T1SZ=25 (512G) ... The region size is 2 POWER (64-T1SZ) bytes
        (0b00LL << 14) | // TG0=4k  ... options are 00=4KB, 01=64KB, 10=16KB,  ... take care differs from TG1
        (0b11LL << 12) | // SH0=3 inner ... .. options 00 = Non-shareable, 01 = INVALID, 10 = Outer Shareable, 11 = Inner Shareable
        (0b01LL << 10) | // ORGN0=1 write back .. options 00 = Non-cacheable, 01 = Write back cacheable, 10 = Write thru cacheable, 11 = Write Back Non-cacheable
        (0b01LL << 8)  | // IRGN0=1 write back .. options 00 = Non-cacheable, 01 = Write back cacheable, 10 = Write thru cacheable, 11 = Write Back Non-cacheable
        (0b0LL  << 7)  | // EPD0  ... Translation table walk disable for translations using TTBR0_EL1  0 = walk, 1 = generate fault
        (25LL   << 0);   // T0SZ=25 (512G)  ... The region size is 2 POWER (64-T0SZ) bytes
    asm volatile ("msr tcr_el1, %0" : : "r" (r));

    /* sctlr */
    asm volatile ("isb");
    asm volatile ("mrs %0, sctlr_el1" : "=r" (r));
    r |= 0xC00800;   // set mandatory reserved bits
    r |= (1<<12) |   // I, Instruction cache enable. 
         (1<<4)  |   // SA0, Stack Alignment Check Enable for EL0
         (1<<3)  |   // SA, Stack Alignment Check Enable
         (1<<2)  |   // C, Data cache enable.
         (1<<1)  |   // A, Alignment check enable bit
         (1<<0);     // set M, enable MMU
    asm volatile ("msr sctlr_el1, %0" : : "r" (r));
    asm volatile("isb");

Todo

  • QEMUでMMUが有効になっているか確認する。(MMUのイベントをトレースすれば良いのか?)
  • QEMUでキャッシュの有無で実行速度が変わるのか

参考にした情報

eggman
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away