2017/02/19

AArch64 armasm の勉強 システムレジスタの値を取得する実習

前々回前回で、システムレジスタについてと、 C 言語から armasm のサブルーチンを呼び出す方法が分かった気がする。 なので、実際にプログラムを作成してレジスタの値が取得できるかを確かめる。

前提

この辺の作業で使った環境を引き続き使用している。

作成したプログラム

main.c

#include <string.h>
#include <types.h>
#include <uart.h>

#include "SystemRegisters.h"

#define BUF_LENGTH 255

int main() {

    char cbuf[BUF_LENGTH];

    uint32 daifFlags = daif();
    ltoa(daifFlags, cbuf, BUF_LENGTH, 2);
    print("daifFlags = ");
    println(cbuf);

    uint32 nzcvFlags = nzcv();
    ltoa(nzcvFlags, cbuf, BUF_LENGTH, 2);
    print("nzcvFlags = ");
    println(cbuf);

    while (1);

    return 0;
}

SystemRegisters.h

#include <types.h>

extern uint32 daif();
extern uint32 nzcv();

SystemRegisters.S

.global daif
daif:
    MRS X0, DAIF
    RET
.global nzcv
nzcv:
    MRS X0, NZCV
    RET

とりあえずこれで確認してみる。

実行結果は下記。

daifFlags = 1111000000
nzcvFlags = 1100000000000000000000000000000

printf を実装してないから表示がちょっと残念だけど、 下記、条件フラグの記載を見る限り、 DAIF フラグが全部立ってるので、割り込みはすべて Disable であることと、 Z, C フラグが立ってることを確認できた???

とりあえず今日はここまで。 明日はこれでいろいろなレジスタの状態を確認していってみようか。

2017/02/16

AArch64 armasm の勉強 サブルーチン編

前回(親知らずの日記: AArch64 armasm の勉強 システムレジスタ編)、システムレジスタの値を MRS, MSR で読み書きできそうということが分かったので、取得した値を表示してみたい。

そのためには、 C 言語から armasm のサブルーチンを呼び出せるようにしないといけない。

そのあたりの勉強をする。

座学

ARM Information Center - ARM® コンパイラ armasm ユーザガイド を見てみたが、 AArch64 の章に、サブルーチンに関する話が載っていなかった。

仕方がないので ARM Information Center - 6.3 サブルーチン呼び出しでのレジスタの使用方法 を参照する。

これによると、

  • レジスタ 0 から 3 までを引数用に使用
  • レジスタ 0 を 戻り値用に使用
  • サブルーチン呼び出しには BL 命令を使用
  • サブルーチンからの復帰には BX LR 命令を使用

のようだ。

それぞれの命令の意味を確認しようと思ったら、 ARM Information CenterBX 命令がない...。「復帰」で検索かけたら RET 命令が見つかった。AArch64 では RET 命令でサブルーチンからの復帰を行うみたい。

ここまでわかったので、実際作ったプログラムをアセンブリプログラムに変換して確かめてみる。

実践

検証環境

ツールチェインは gcc-linaro-6.2.1-2016.11-i686-mingw32_aarch64-linux-gnu

検証用プログラムの作成

caller.c

extern int getOne(void);
extern int add(int a, int b);

int main() {

    int one = getOne();

    int number = add(1, 2);

    while (1);

    return 0;
}

callee.c

int getOne() {
    return 1;
}

int add(int a, int b) {
    return a + b;
}

上記の C コードをアセンブリコードへ変換する。

アセンブリコードの確認

aarch64-linux-gnu-gcc -S ./caller.c -o caller.S
aarch64-linux-gnu-gcc -S ./callee.c -o callee.S

で、結果が下記。

caller.S

    .arch armv8-a
    .file   "caller.c"
    .text
    .align  2
    .global main
    .type   main, %function
main:
    stp x29, x30, [sp, -32]!
    add x29, sp, 0
    bl  getOne
    str w0, [x29, 28]
    mov w1, 2
    mov w0, 1
    bl  add
    str w0, [x29, 24]
.L2:
    b   .L2
    .size   main, .-main
    .ident  "GCC: (Linaro GCC 6.2-2016.11) 6.2.1 20161016"
    .section    .note.GNU-stack,"",@progbits

callee.S

    .arch armv8-a
    .file   "callee.c"
    .text
    .align  2
    .global getOne
    .type   getOne, %function
getOne:
    mov w0, 1
    ret
    .size   getOne, .-getOne
    .align  2
    .global add
    .type   add, %function
add:
    sub sp, sp, #16
    str w0, [sp, 12]
    str w1, [sp, 8]
    ldr w1, [sp, 12]
    ldr w0, [sp, 8]
    add w0, w1, w0
    add sp, sp, 16
    ret
    .size   add, .-add
    .ident  "GCC: (Linaro GCC 6.2-2016.11) 6.2.1 20161016"
    .section    .note.GNU-stack,"",@progbits

まとめ

ドキュメントに書いてある通り、引数と戻り値を設定しているみたい。 そして、復帰は RET 使っている。

サブルーチン側では、

  1. スタック領域確保する
  2. スタックに引数を積み上げる
  3. サブルーチンの処理結果をレジスタ 0 に格納する
  4. 確保したスタック領域を返却する

のような流れっぽい。 スタックは、アドレスの少ないほうに伸びていくっぽい。

本日はここまで。 明日、余裕があったら引数の数増やして確認してみる。

2017/02/15

AArch64 armasm の勉強 システムレジスタ編

BareMetalで遊ぶ Raspberry Pi - 達人出版会 を参照しながら Raspberry Pi 3 Model B+ でベアメタルタイマー割り込みやろうとしたのだけど、 AArch64 でやろうとするといろいろ勝手が違うらしいのでそこのところを勉強する。

元ネタ資料はこれら

レジスタ概要を把握するため、ARM Information Center - 章 4 AArch64 状態の概要 を一通り読んだ。

不正確な箇条書きで書くとこんな感じらしい。

  • レジスタ概要
    • 汎用レジスタ 30 本
      • プログラム内で好き勝手適当に使えるレジスタ群
      • X30 で 64 bit レジスタとしてアクセス
      • W30 で 32 bit レジスタとしてアクセス(上 32 bit はゼロクリアされる)
      • X30, W30 はリンクレジスタ
    • PC(プログラムカウンタ)
      • 次に実行するアドレスが格納されるレジスタ
      • 直接値を変更することはできない、特定の命令で書き換えるらしい
    • SP(スタックポインタレジスタ)
      • スタックを実現するために使うレジスタ
      • 命令一つで格納とインクリメントをしてくれるとかそういうの???
      • サブルーチンの引数なんかをスタックするのに使うらしい
      • 例外レベル 0 から 3 までにそれぞれ専用のレジスタをもつ
      • アセンブラの命令によっては X31, W31 でアクセスできるらしい
    • SPSR(セーブドプログラムステータスレジスタ)
      • 例外発生時にその時点での状態を記録してくれるレジスタ
      • 例外レベル 1 から 3 までに専用のレジスタが一つずつある
      • 例外が発生すると、現在のステータスが遷移先の SPSR に格納される
      • SPSR が格納する情報は下記の通り
        • N, Z, C, V フラグ
        • D, A, I, F 割り込み Disable ビット
        • レジスタ幅
        • 実行モード
        • IL, SS ビット
    • LR(リンクレジスタ)
      • サブルーチン呼び出し時に復帰アドレスを格納する
    • ELR(例外リンクレジスタ)
      • 例外発生時に復帰アドレスを格納する
      • 例外レベル 1 から 3 にそれぞれ専用のレジスタがある
        • 設定によっては例外レベルでレジスタを使い分けないこともできるらしい
  • 例外レベルとは
    • それぞれのレベルで下記のような感じらしい。それぞれの意味はよく分からない。
      • EL0 : アプリケーション
      • EL1 : 特権付き
      • EL2 : ハイパーバイザ
      • EL3 : セキュアモニタ
  • AArch64 でのプロセス状態書き込み・読み込み方法
    • プロセス状態としてアクセスできる情報は下記の通り
      • N,Z,C,V フラグ(NZCV)
      • 現在のレジスタ幅(nRW)
      • スタックポインタ選択ビット(SPSel)
      • 割り込み Disable フラグ(DAIF)
      • 現在の例外レベル(EL)
      • シングルステッププロセス状態ビット(SS)
      • 不正な例外復帰状態ビット(IL)
    • MSR 命令で書き込めるもの
      • NZCV
      • DAIF
      • SPSel(EL1以上)
    • MRS 命令で読み取れるもの
      • NZCV
      • DAIF
      • CurrentEL の例外レベルビット(EL1 以上)
      • SPSel(EL1 以上)

BareMetalで遊ぶ Raspberry Pi - 達人出版会 内で CPSR にアクセスしている部分は、大体 MSR, MRS で置き換えられるのかな?

ARM Information Center - ARM Cortex-A53 MPCore Processor Technical Reference Manual に書かれているレジスタなんかも、 MSR, MRS 命令で読み書きするみたい。

とりあえず今日はここまで。