Raspberry Pi システム制御コプロセッサのMIDR情報について

はじめに

2017年3月、Raspberry Pi搭載のARMシステム制御コプロセッサ情報について調べてみました。

コプロセッサ情報のアクセス例として、Main ID Register情報を読み出してみました。この命令の実行には、特権レベルが必要でしたのでカーネルモジュールで作成しました。

Raspberry Pi2はCortex-A7を搭載しているので、ARMサイトの「Cortex-A7 MPCore Technical Reference Manual」を参考にしました。

Cortex-A7 Manual

Cortex-A7 Manual

ARMのシステム制御コプロセッサ(CP15)とは

ARMプロセッサの機能拡張を行うために CP0 から CP15 の16個のコプロセッサが定義されています。コプロセッサ CP15は、プロセッサのコンフィグションを行うもので、プロセッサが実装している機能のステータス参照や制御を行うレジスタで構成しており、システム制御コプロセッサと呼びます。

コプロセッサ情報

コプロセッサ情報

コプロセッサは、メモリ空間に配置されていないため、専用命令で読み込みおよび書き込みを行います。

  • MRC命令は、コプロセッサレジスタからプロセッサレジスタに読み込みを行います。
  • MCR命令は、プロセッサレジスタからコプロセッサレジスタに書き込みを行います。

システム制御コプロセッサは、32ビット長の複数のコプロセッサ・レジスタから構成します。
各コプロセッサ・レジスタは、2つのレジスタと2つのOPコードで指定します。

MRC{ cond } coproc ,#Op1 ,Rd ,CRn ,CRm { ,#Op2 }
MCR{ cond } coproc ,#Op1 ,Rd ,CRn ,CRm { ,#Op2 }
  • CRn : プライマリ・レジスタ (C0~C15)
  • Op1 : オペコード1 (0~7)
  • CRm : セカンダリ・レジスタ (C0~C15)
  • Op2 : オペコード2 (0~7)
  • Rd : ARMプロセッサのレジスタ

C0~C15までの16個のプライマリ・レジスタは、システム制御コプロセッサの種類を決定します。

Main ID Registerのアクセス

システム制御コプロセッサのMIDR(Main ID Register)は、プロセッサのデバイスの実装コードとパーツID番号を含む識別情報を提供します。

MIDRは、コプロセッサ CP15の CRn=C0 ,Op1=0 ,CRm=C0 ,Op2=0 に配置されています。

ARMサイトの「Cortex-A7 MPCore Technical Reference Manual」から、MIDRの説明を抜粋します。

MIDR説明

MIDR説明

Raspberry Pi2に搭載しているCortex-A7プロセッサは、どんな識別情報として定義されているでしょうか?

C言語でインラインアセンブラを使用してMIDRを参照してみます。

MRC命令でMIDRを変数regにロードして、printf()で16進数で表示します。

ソースコード getreg.c を下記に示します。

#include <stdio.h>  

unsigned int get_reg()
{
    unsigned int reg;
    asm volatile("mrc p15 ,0 ,%0 ,c0 ,c0 ,0" : "=r" (reg));
    return(reg);
}

void main()
{
    unsigned int reg;

    printf("Start\n");
    reg = get_reg();
    printf("%08x",reg);
    printf("End\n");
}

プログラム getreg.c をビルドします。

$ gcc -o getreg getreg.c

実行します。

$ ./getreg
Start
Illegal instruction

なにやら違法な命令というエラーが発生しました。

逆アセンブルして、オブジェクトコードを確認します。

$ objdump -S getreg

getreg:     ファイル形式 elf32-littlearm

(省略)

0001044c :
   1044c:       e52db004        push    {fp}            ; (str fp, [sp, #-4]!)
   10450:       e28db000        add     fp, sp, #0
   10454:       e24dd00c        sub     sp, sp, #12
   10458:       ee103f10        mrc     15, 0, r3, cr0, cr0, {0}
   1045c:       e50b3008        str     r3, [fp, #-8]
   10460:       e51b3008        ldr     r3, [fp, #-8]
   10464:       e1a00003        mov     r0, r3
   10468:       e24bd000        sub     sp, fp, #0
   1046c:       e49db004        pop     {fp}            ; (ldr fp, [sp], #4)
   10470:       e12fff1e        bx      lr

想定した mrc命令が生成されています。

さて、何が原因なのでしょうか?

マニュアルをもう一度確認したら、気になる記述があります。

Usage constraints The MIDR is:
• A read-only register.
• Common to the Secure and Non-secure states.
• Only accessible from PL1 or higher.

 

使用制限として、PL1以上でのみアクセス可能と書いてあります。

PL1以上でアクセス可能 とは

PL1とは、何を意味しているのでしょうか?

「ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition」マニュアルでその意味がわかりました。

A3.6.1 Processor privilege levels, execution privilege, and access privilege

the ARMv7 architecture defines different levels of execution privilege:
• in Secure state, the privilege levels are PL1 and PL0
• in Non-secure state, the privilege levels are PL2, PL1, and PL0.

PL0 :
The privilege level of application software, that executes in User mode. Therefore, software
executed in User mode is described as unprivileged software. This software cannot access some
features of the architecture. In particular, it cannot change many of the configuration settings.
Software executing at PL0 makes only unprivileged memory accesses.

PL1 :
Software execution in all modes other than User mode and Hyp mode is at PL1. Normally, operating
system software executes at PL1. Software executing at PL1 can access all features of the
architecture, and can change the configuration settings for those features, except for some features
added by the Virtualization Extensions that are only accessible at PL2.

PL2 :
Software executing in Hyp mode executes at PL2.
Software executing at PL2 can perform all of the operations accessible at PL1, and can access some additional functionality.


【要約】
A3.6.1 プロセッサ特権レベル、実行特権、アクセス特権

ARMv7アーキテクチャでは、さまざまなレベルの実行特権が定義されています。
•セキュア状態では、特権レベルはPL1、PL0です
•非セキュア状態では、特権レベルはPL2、PL1、PL0です。

PL0 :
ユーザーモードで実行されるアプリケーションソフトウェアの特権レベルです。したがって、ユーザーモードで実行されるソフトウェアは特権のないソフトウェアとして記述されています。このソフトウェアはアーキテクチャの機能の一部にアクセスできません。特に、多くの構成設定を変更することはできません。PL0で実行されるソフトウェアは、特権のないメモリアクセスのみを行います。

PL1 :
ユーザーモードとHypモード以外のすべてのモードでのソフトウェア実行はPL1です。通常、オペレーティングシステムソフトウェアは、PL1で動作します。PL1で実行されているソフトウェアは、アーキテクチャのすべての機能にアクセスでき、これらの機能の設定を変更することができます。ただし、仮想化拡張機能によって追加されたPL2でのみアクセス可能ないくつかの機能は除きます。

PL2 :
Hypモードで実行されるソフトウェアはPL2で実行されます。
PL2で実行されるソフトウェアは、PL1でアクセス可能なすべての操作を実行することができ、いくつかの追加機能にアクセスすることができます。


PL0がユーザーモード、PL1が特権モードというわけです。

Main ID Registerは、PL1以上でアクセス可能なので、Linuxのユーザーモードでは実行できなかったわけです。Linuxで特権モードということは、カーネルかデバイスドライバでアクセス可能ということです。

カーネルモジュールでMIDRを参照

カーネルモジュールでMIDRを参照します。

カーネルモジュールの作成方法については、「Raspberry Piでカーネルモジュールを作成」を参照してください。

MIDR参照プログラムコード

MIDR(Main ID Register)を参照する getmainid.c プログラムコードを下記に示します。

$ cat getmainid.c 
#include <linux/module.h>
#include <linux/init.h>


MODULE_LICENSE("Dual BSD/GPL");

static int getmidr_init(void)
{
     unsigned int reg;

     printk(KERN_ALERT "Start Get_MainID \n");	
	
     asm volatile("mrc p15 ,0 ,%0 ,c0 ,c0 ,0" : "=r" (reg));
     printk(KERN_ALERT "MIDR =%08x\n",reg);

     return 0;
}

static void getmidr_exit(void)
{
     printk(KERN_ALERT "End.. Get_MainID \n");	
}

module_init(getmidr_init);
module_exit(getmidr_exit);

プログラムコードの説明

カーネルモジュールに関する基本的なことは除いて、MIDRの参照に関することを説明します。

     unsigned int reg;
     asm volatile("mrc p15 ,0 ,%0 ,c0 ,c0 ,0" : "=r" (reg));
     printk(KERN_ALERT "MIDR =%08x\n",reg);

MIDRは、コプロセッサ CP15の CRn=C0 ,Op1=0 ,CRm=C0 ,Op2=0 に配置されています。

インラインアセンブラを使用して、MRC命令でコプロセッサCP15の該当レジスタをARMレジスタに読み込みます。その内容をprintk()を使用して、カーネルバッファに出力します。

ビルド

Makefileを作成します。

$ cat Makefile 
KERNEL_DIR = /lib/modules/$(shell uname -r)/build 

obj-m := mainidmod.o

mainidmod-objs := getmainid.o

all:
	make -C $(KERNEL_DIR) M=$(PWD) modules

clean:
	make -C $(KERNEL_DIR) M=$(PWD) clean

カーネルモジュールをビルドします。ルート権限が必要なのでスーパーユーザーでmakeします。

$ sudo su
# make
make -C /lib/modules/4.9.14-v7+/build  M=/home/pi/myHome/mainid modules
make[1]: Entering directory '/root/linux-a599f69212b051db4cd00a02f9312dc897beba70'
  CC [M]  /home/pi/myHome/mainid/getmainid.o
  LD [M]  /home/pi/myHome/mainid/mainidmod.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/pi/myHome/mainid/mainidmod.mod.o
  LD [M]  /home/pi/myHome/mainid/mainidmod.ko
make[1]: Leaving directory '/root/linux-a599f69212b051db4cd00a02f9312dc897beba70'
$ ls -l

-rw-r--r-- 1 pi   pi    197  3月 16 13:24 Makefile
-rw-r--r-- 1 root root    0  3月 16 13:25 Module.symvers
-rw-r--r-- 1 pi   pi    454  3月 16 13:22 getmainid.c
-rw-r--r-- 1 root root 2748  3月 16 13:25 getmainid.o
-rw-r--r-- 1 root root 3260  3月 16 13:25 mainidmod.ko
-rw-r--r-- 1 root root  612  3月 16 13:25 mainidmod.mod.c
-rw-r--r-- 1 root root 1956  3月 16 13:25 mainidmod.mod.o
-rw-r--r-- 1 root root 2016  3月 16 13:25 mainidmod.o
-rw-r--r-- 1 root root   43  3月 16 13:25 modules.order
drwxr-xr-x 2 pi   pi   4096  2月 11 11:51 user

カーネルモジュール mainidmod.ko を生成しました。

カーネルモジュールの実行

スーパーユーザーで実行します。

$ sudo su

カーネルモジュールをロードします。

# insmod mainidmod.ko

カーネルバッファにメッセージを記録したことを確認します。

# dmesfg | tail -2
[  535.781733] Start Get_MainID 
[  535.784902] MIDR =410fc075

カーネルモジュールをアンロードします。

# rmmod mainidmod

カーネルバッファにメッセージを記録したことを確認します。

# dmesg | tail -3
[  535.781733] Start Get_MainID 
[  535.784902] MIDR =410fc075
[  573.422462] End.. Get_MainID 

MIDRの参照値の確認

カーネルモジュールのロードで、MIDRの値は下記のようになりました。

MIDR =410FC075

Cortex-A7 MIDR

Cortex-A7 MIDR


Implementer 0x41 ARM Limited
Variant 0x0 Major revision number
Architecture 0xF ARMv7
Primary part number 0xC07 Cortex-A7 MPCore part number
Revision 0x5 Minor revision number

Raspberry Pi2はCortex-A7を搭載しているので、「ARMv7 Cortex-A7 MPCore」となりました。

最後に

システム制御コプロセッサ(CP15)のアクセス例として、Main ID Register情報を読み出すことができました。
システム制御コプロセッサには、キャッシュ情報などの情報が格納されているので調べていきたいと考えています。