0. はじめに
実機でhello worldを目標に簡単なブートプログラムを書きます. せっかく友人からもらったraspberry piを遊ばせているので いいおもちゃになりそうです. ただ,ほとんどhttp://wiki.osdev.org/ARM_RaspberryPi_Tutorial_Cを参考にしているので 詳細を知りたい方は原文を読んでいただけたらと思います. 前半はエミュレータとクロスコンパイラの準備です. 全部コピペしてくれれば動くようにはしているつもりです.
対象はraspberry pi Model Bと呼ばれるものです.(これしか持ってないです) wikipediaより
- cpu: 700 MHz / ARM1176JZF-S コア (ARM11 ファミリ)
- ボード: BCM2835
- メモリ: 512MB
- 内蔵SSDやHDは搭載しない代わりに,SDカードを記憶装置として使います.
- ブートもSDカードからです.
1. エミュレータ
まずqemuを使って,raspbianが動作するか確認します.
もともとインストールされていたqemuは qemu-system-arm -cpu ?にarm1176は含まれていますが qemu-system-arm -M ?にBCM2835ボードが含まれていないようなので, 代わりにversatilepbを使います. そのため実機とエミュレータでハードウェアのアドレス(GIOやUART)が 変わるので注意が必要です.
-cpuでcpuの種類,-mでメモリサイズ(MB),-hdaでイメージファイル, -appendでを指定します. -kernelでホスト環境にカーネルのファイルを指定します. raspbianのイメージファイルはここから. http://www.raspberrypi.org/downloads
$ wget http://xecdesign.com/downloads/linux-qemu/kernel-qemu
$ wget http://files.velocix.com/c1410/images/raspbian/2012-10-28-wheezy-raspbian/2012-10-28-wheezy-raspbian.zip
$ unzip 2012-10-28-wheezy-raspbian.zip
$ qemu-system-arm \
-kernel kernel-qemu \
-M versatilepb \
-cpu arm1176 -m 256 \
-append "root=/dev/sda2 panic=1" -hda 2012-10-28-wheezy-raspbian.img
2. クロスコンパイラ
まず,binutilsをインストールします. アセンブラ,リンカ,などをまとめたものです. 今後何か移植するかも知れないのでnewlibも含めておきます. 恐らくこのステップが一番大変です. 何度もやり直しましたorz
TARGETをarm-none-eabi, PREFIXを/usr/local/cross-piにします.
$ export LDFLAGS="-L/opt/local/lib"
$ export CFLAGS="-I/usr/local/include -O2"
$ cd /usr/local
$ mkdir cross-pi
$ cd /usr/local/src
$ wget http://ftp.gnu.org/gnu/binutils/binutils-2.23.tar.gz
$ tar xvf binutils-2.23.tar.gz
$ cd binutils-2.23
$ ./configure --target=arm-none-eabi --prefix=/usr/local/cross-pi
$ make
$ make install
$ cd /usr/local/src
$ wget http://ftp.tsukuba.wide.ad.jp/software/gcc/releases/gcc-4.8.2/gcc-4.8.2.tar.gz
$ wget ftp://sourceware.org/pub/newlib/newlib-1.20.0.tar.gz
$ tar xvf gcc-4.8.2.tar.gz
$ tar xvf newlib-1.20.0.tar.gz
$ cd gcc-4.8.2
$ ln -s ../newlib-1.20.0/newlib .
$ mkdir work
$ cd work
$ ../configure --prefix=/usr/local/cross-pi --target=arm-none-eabi --enable-multilib --with-newlib --enable-languages="c,c++" --enable-interwork
$ make
$ make install
3. カーネル
以下のファイルで構成されます.
- include/mmio.h
- include/uart.h
- link-arm-eabi.ld リンカスクリプト
- boot.S ブート
- main.c エントリポイント
- uart.c UARTの初期化と通信
- syscalls.c システムコールの中身(newlibのために)
- Makefile
3.1 include/mmio.h
外部端子は,メモリに配置されているので(Memory Mapped IO) それを操作する関数群です. inlineをつけてインライン展開するように,volatileをつけて 最適化を行わないようにしています.
#ifndef MMIO_H
#define MMIO_H
#include <stdint.h>
/* MMIOに書き込む */
static inline void mmio_write(uint32_t reg, uint32_t data) {
uint32_t *ptr = (uint32_t*)reg;
asm volatile("__mmio_write_%=: str %[data], [%[reg]]"
: : [reg]"r"(ptr), [data]"r"(data));
}
/* MMIOから読み込む */
static inline uint32_t mmio_read(uint32_t reg) {
uint32_t *ptr = (uint32_t*)reg;
uint32_t data;
asm volatile("ldr %[data], [%[reg]]"
: [data]"=r"(data) : [reg]"r"(ptr));
return data;
}
#endif
3.2 include/uart.h
MMIOの具体的なアドレスを定義しておきます. IS_EMULATEが見苦しいです.ごめんなさい. (Makefileの方に書きます.) 実機のほうは,http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf を参照すればよさそうです. 0x7Ennnnnn(バスアドレス)は,0x20nnnnnn(物理アドレス)に対応します. p5,6に書いてます. p89にGPIOのベースアドレスがあります. p175からは,UARTのベース物理アドレスや, DR,RSRECRなどのオフセットがあります.(これは実機,versatile共通化かな) エミュレータのほうは,http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0224i/Chdbeibh.htmlの4.1 Memory Mapなどに書いてあります.
#ifndef UART_H
#define UART_H
/*
* 1: エミュレータ
* 0: 実機
*/
#define IS_EMULATE 1
#include <stdint.h>
enum {
#if IS_EMULATE == 1
/* エミュレータ */
GPIO_BASE = 0x101E4000,
UART0_BASE = 0x101F1000,
#else
/* 実機 */
GPIO_BASE = 0x20200000,
UART0_BASE = 0x20201000,
#endif
/* Controls actuation of pull up/down to ALL GPIO pins. */
GPPUD = (GPIO_BASE + 0x94),
/* Controls actuation of pull up/down for specific GPIO pin. */
GPPUDCLK0 = (GPIO_BASE + 0x98),
/* UART0 */
UART0_DR = (UART0_BASE + 0x00),
UART0_RSRECR = (UART0_BASE + 0x04),
UART0_FR = (UART0_BASE + 0x18),
UART0_ILPR = (UART0_BASE + 0x20),
UART0_IBRD = (UART0_BASE + 0x24),
UART0_FBRD = (UART0_BASE + 0x28),
UART0_LCRH = (UART0_BASE + 0x2C),
UART0_CR = (UART0_BASE + 0x30),
UART0_IFLS = (UART0_BASE + 0x34),
UART0_IMSC = (UART0_BASE + 0x38),
UART0_RIS = (UART0_BASE + 0x3C),
UART0_MIS = (UART0_BASE + 0x40),
UART0_ICR = (UART0_BASE + 0x44),
UART0_DMACR = (UART0_BASE + 0x48),
UART0_ITCR = (UART0_BASE + 0x80),
UART0_ITIP = (UART0_BASE + 0x84),
UART0_ITOP = (UART0_BASE + 0x88),
UART0_TDR = (UART0_BASE + 0x8C),
};
void uart_init();
void uart_putc(uint8_t byte);
void uart_puts(const char *str);
uint8_t uart_getc();
#endif
3.3 link-arm-eabi.ld
リンカスクリプトです. メモリの配置を決めます. ENTRY(Start)はカーネルイメージのエントリポイントを表します. (シンボルStartはboot.Sで定義されています.) 一旦elfに変換してから生バイナリにするのでリンカにとってこうしたほうが良さそうです. (よくわかんない) “.“が現在のメモリアドレスです. textセグメントの先頭に.text.bootセクションを配置します. その後ろに残りのtextセグメントを配置します. _text_startや_text_endなどをわざわざ宣言しているのはソースコードから利用するためです. 実際_bss_startはboot.Sから利用されています.(0初期化の変数群) . = ALIGN(4096)で,4096バイトごとにalignmentしています. 別々のセグメントが同じページに混じるとパーミッションとかがまずいので.
ENTRY(Start)
SECTIONS
{
/*
* Starts at LOADER_ADDR.
* 0x8000 ... 実機
* 0x10000 ... qemu
* */
//. = 0x8000;
. = 0x10000;
_start = .;
_text_start = .;
.text :
{
KEEP(*(.text.boot)) /* テキストセグメントの先頭は.text.bootセクション */
*(.text) /* その後ろに他の.textセクションを配置 */
}
. = ALIGN(4096);
_text_end = .;
_rodata_start = .;
.rodata :
{
*(.rodata)
}
. = ALIGN(4096);
_rodata_end = .;
_data_start = .;
.data :
{
*(.data)
}
. = ALIGN(4096);
_data_end = .;
_bss_start = .;
.bss :
{
bss = .;
*(.bss)
}
. = ALIGN(4096);
_bss_end = .;
end = .;
_end = .;
}
3.4 boot.S
.text.bootセクションを設定し,テキストセグメントの先頭に配置するようにします. その後,spを適当に設定し,bssエリアを0クリアし,kernel_main(C言語)にジャンプします.
.section ".text.boot"
.globl Start
/*
* Entry point for the kernel.
* r15 -> プログラムカウンタ( 実機なら0x8000,エミュレータなら0x10000)
* r0 -> 0x00000000
* r1 -> 0x00000C42
* r2 -> 0x00000100 ATAGS
* kernel_mainのため,r0-r2を使わない
*/
Start:
/* スタックポイント初期化 */
mov sp, #0x8000
/* bss0クリア */
ldr r4, =_bss_start
ldr r9, =_bss_end
mov r5, #0
mov r6, #0
mov r7, #0
mov r8, #0
b 2f
1:
/* アドレスr4にr5-r8をストア,r4をインクリメント
* http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0170b/BABEFCIB.html
*/
stmia r4!, {r5-r8}
/* bss_endより小さければループ */
2:
cmp r4, r9
blo 1b
/* kernel_mainへジャンプ */
ldr r3, =kernel_main
blx r3
halt:
wfe
b halt
3.5 main.c
苦労してnewlibを入れたのでwrite()やstrlen()が問題なく使えます. write()については中身を書いてやらないといけません. (またあとで説明します.) GPUのブートローダ(?)から,レジスタr0-r2を通して カーネルにパラメータが渡され,boot.Sからkernel_mainに渡されます.
- r0: 基本に0です
- r1: ARM Linux Machine Type.raspberry piは, Broadcom BCM2835というSoC(GPU,CPU,DSP,SDRAMをまとめたチップ)を使っていて, BCM2835はBCM2708の特定のチップであるから,BCM2708に対応する3138(0xC42) が設定されます.
- r2: ATAGsの情報が格納されます.
#include <stdint.h>
#include <uart.h>
#include <stdlib.h>
const char *hello="Hello, cinderella of the tea plantation.\n";
const char *halt="** halt **\n";
void kernel_main(uint32_t r0, uint32_t r1, uint32_t atags) {
int *a;
int i;
char s[30];
(void)r0;
(void)r1;
(void)atags;
uart_init();
uart_puts(hello);
write(0,hello,strlen(hello));
// Wait a bit
for(volatile int i = 0; i < 10000000; ++i) { }
uart_puts(halt);
while(1){
uart_putc(uart_getc());
}
}
3.6 uart.c
UARTの初期化と通信の関数群です. BCM2835 ARM Peripherals (pdfの資料) のp101あたりに初期化手順があります. 150サイクル待ちましょうとか書いてます.
#include<stdint.h>
#include<mmio.h>
#include<uart.h>
/*
* countサイクル待つ
*
* ループを最適化しない
*
* 00000030 <__delay_16>:
* 30: e2533001 subs r3, r3, #1
* 34: 1afffffd bne 30 <__delay_16>
*/
static void delay(int32_t count) {
asm volatile("__delay_%=: subs %[count], %[count], #1; bne __delay_%=\n"
: : [count]"r"(count) : "cc");
}
/*
* UART0の初期化
*
* 1. GPのIXDとRXDを有効にする
* 2. ボーレートを計算
* 3. 割り込みを有効にする
*/
void uart_init() {
/* UART0を無効に Control Registerを0に */
mmio_write(UART0_CR, 0x00000000);
/* GPIO pin 14,15を初期化
* シリアル通信に使うIXDとRXDに対応 p102より
* GPIOには割り当て方が6種類ある.ここではALT0を使っているよう
* GRIO pin 14: IXD0
* GRIO pin 15: RXD0
*/
/* GP pull-up,downを無効にして150サイクル待つ */
mmio_write(GPPUD, 0x00000000);
delay(150);
/* GP pull-up,down の14,15ビット目をセットし,150サイクル待つ */
mmio_write(GPPUDCLK0, (1 << 14) | (1 << 15));
delay(150);
/* GPPUDCLK0 を0に */
mmio_write(GPPUDCLK0, 0x00000000);
/* Interrupt Clear Register 割り込みを無効にする */
mmio_write(UART0_ICR, 0x7FF);
/* ボーレート(シリアル通信に使う)の整数部・小数部の計算
*
* Divider = UART_CLOCK/(16 * Baud)
* Fraction part register = (Fractional part * 64) + 0.5
* UART_CLOCK = 3000000; Baud = 115200.
*
* Divider = 3000000/(16 * 115200) = 1.627 = ~1.
* Fractional part register = (.627 * 64) + 0.5 = 40.6 = ~40.
*
*/
mmio_write(UART0_IBRD, 1);
mmio_write(UART0_FBRD, 40);
/* FIFO,8bit通信を有効にする.
* 4bit目: FIFOを有効に
* 5,6bit目: 1フレームの通信量 11なら8 bit/フレーム
*/
mmio_write(UART0_LCRH, (1 << 4) | (1 << 5) | (1 << 6));
/* 全ての割り込みを有効に
* 1:有効 0:無効
* 1bit目: uUARTCTS modern 割り込み
* 4bit目: 受信割り込み
* 5bit目: 送信割り込み
* 6bit目: 受信タイムアウト割り込み
* 7bit目: フレームエラー割り込み
* 8bit目: パリティエラー割り込み
* 9bit目: Breakエラー割り込み
* 10bit目: オーバーランエラー割り込み
*/
mmio_write(UART0_IMSC, (1 << 1) | (1 << 4) | (1 << 5) |
(1 << 6) | (1 << 7) | (1 << 8) |
(1 << 9) | (1 << 10));
/* 0bit目: UARTを有効に
* 8bit目: 送信を有効に
* 9bit目: 受信を有効に
*/
mmio_write(UART0_CR, (1 << 0) | (1 << 8) | (1 << 9));
}
/*
* 1バイトシリアル送信
*/
void uart_putc(uint8_t byte) {
/* FR 5bit目: 送信FIFOがいっぱいなら1 */
while (1) {
if (!(mmio_read(UART0_FR) & (1 << 5))) {
break;
}
}
mmio_write(UART0_DR, byte);
}
/*
* 0終端文字列をシリアル送信
*/
void uart_puts(const char *str) {
while (*str) {
uart_putc(*str++);
}
}
/*
* 1バイトシリアル受信
*/
uint8_t uart_getc() {
/* FR 4bit目: 受信FIFOが空なら1 */
while(1) {
if (!(mmio_read(UART0_FR) & (1 << 4))) {
break;
}
}
return mmio_read(UART0_DR);
}
3.7 syscalls.c
newlibが使えるようにシステムコールを書いていきます. リンクの際に足りないものを増やしていきました. とりあえずwrite()でシリアル送信するようにしておきます.
#include <errno.h>
#include <uart.h>
#undef errno
extern int errno;
/*
* send message to uart0 (serial port)
*/
int _write(int fd, char *ptr, int len){
int i=0;
while(i<len && ptr[i]!='\0')
uart_putc(ptr[i++]);
return i;
}
int _close(int file) {
return -1;
}
int _fstat(){
return -1;
}
int _sbrk(){
return -1;
}
int _kill(){
return -1;
}
int _exit(){
return -1;
}
int _getpid(){
return -1;
}
int _gettimeofday(){
return -1;
}
int _isatty(){
return -1;
}
int _lseek(){
return -1;
}
int _read(){
return -1;
}
3.8 Makefile
ソースファイル(*.c,*.S) -> オブジェクトファイル(*.o) -> 実行可能ファイル(*.elfファイル) -> バイナリファイル(*.img)
と変換しています. CFLAGSには組み込み,カーネル向けの fpic,nostdlib,nostartfiles,ffreestanding,nodefaultlibs を指定しています. elf - 生バイナリ - アセンブラの変換はこのページが便利です. http://d.hatena.ne.jp/ken_2501jp/20121107/1352311439%5D
PREFIX = /usr/local/cross-pi
ARMGNU = $(PREFIX)/bin/arm-none-eabi
QEMU = /usr/local/bin/qemu-system-arm
# ソースファイル
SOURCES_ASM := $(wildcard *.S)
SOURCES_C := $(wildcard *.c)
# オブジェクトファイル
# $(patsubst 検索文字列,置換後文字列,対象文字列)
OBJS := $(patsubst %.S,%.o,$(SOURCES_ASM))
OBJS += $(patsubst %.c,%.o,$(SOURCES_C))
# elfフォーマット
ELF := kernel.elf
# 生バイナリ
BINARY := kernel.img
# リスト
LIST := kernel.list
# マップ
MAP := kernel.map
# 依存関係を出力
DEPENDFLAGS := -MD -MP
# インクルードパスを指定 (gccで使う)
INCLUDES := -I include -I /usr/local/src/mruby/include
# ライブラリパスを指定 (ldで使う) 順番も関係するので注意
# /usr/local/src/mruby/build/arm/lib/libmruby_core.a
LIBS := /usr/local/src/mruby/build/arm/lib/libmruby.a \
/usr/local/cross-pi/arm-none-eabi/lib/libc.a \
/usr/local/cross-pi/arm-none-eabi/lib/libm.a \
/usr/local/cross-pi/arm-none-eabi/lib/libg.a \
/usr/local/cross-pi/lib/gcc/arm-none-eabi/4.8.2/libgcc.a
# CLAGSベース
#
# 標準ライブラリや,main関数に依存しないように
# fpic: 位置独立コード
# nostartfiles, ffreestanding, nodefaultlibs: 組み込みやカーネル向け
# fno-builtin: ビルトイン関数は置換しない
# fomit-frame-pointer: 関数呼び出し時にフレームポインタは使わない
#
# pendatic: mrubyのヘッダ内でgcc拡張機能を使っているようなので外す
BASEFLAGS := -O2 -fpic -nostdlib
BASEFLAGS += -nostartfiles -ffreestanding -nodefaultlibs
BASEFLAGS += -fno-builtin -fomit-frame-pointer -mcpu=arm1176jzf-s
ASFLAGS := $(INCLUDES) $(DEPENDFLAGS) -D__ASSEMBLY__
# CFLAGS
# c99を使う
CFLAGS := $(INCLUDES) $(DEPENDFLAGS) $(BASEFLAGS) $(WARNFLAGS)
CFLAGS += -std=gnu99
# qemuオプション
CPU := arm1176
MEM := 256
#MACHINE := realview-eb
MACHINE := versatilepb
SERIAL := stdio
QEMU_OPT := -nographic -m $(MEM) -M $(MACHINE) -cpu $(CPU) -serial $(SERIAL)
# イメージファイル
all: $(BINARY) $(LIST)
# qemuで実行
run: $(ELF)
$(QEMU) $(QEMU_OPT) -kernel $(ELF)
include $(wildcard *.d)
# linkしてkernel.elf
$(ELF): $(OBJS) link-arm-eabi.ld
$(ARMGNU)-ld -o $@ $(OBJS) $(LIBS) -Tlink-arm-eabi.ld -Map $(MAP)
# kernel.elfからkernel.list
$(LIST) : $(ELF)
$(ARMGNU)-objdump -d $(ELF) > $(LIST)
# kernel.elfから生バイナリkernel.imgへ
$(BINARY): kernel.elf
$(ARMGNU)-objcopy $(ELF) -O binary $(BINARY)
clean:
$(RM) -f $(OBJS) $(BINARY) $(ELF) $(MAP) $(LIST)
# *.dも含めて削除
dist-clean: clean
$(RM) -f *.d
# *.cから*.oファイルに
%.o: %.c Makefile
$(ARMGNU)-gcc $(CFLAGS) -c $< -o $@
# .Sから*.oファイルに
%.o: %.S Makefile
$(ARMGNU)-gcc $(ASFLAGS) -c $< -o $@
4. おわりに
$ makeとするとイメージファイルができ,$ make runとすると qemu上で動作します. kernel.elfとkernel.imgがちゃんと出来ているのも確認できます. alignmentも上手くできていました.
$ arm-none-eabi-objdump -D kernel.elf | head -n 50
kernel.elf: ファイル形式 elf32-littlearm
セクション .text の逆アセンブル:
00010000 <Start>:
10000: e3a0d902 mov sp, #32768 ; 0x8000
10004: e59f4030 ldr r4, [pc, #48] ; 1003c <halt+0x8>
10008: e59f9030 ldr r9, [pc, #48] ; 10040 <halt+0xc>
$ hexdump kernel.img | head -n 10
0000000 02 d9 a0 e3 30 40 9f e5 30 90 9f e5 00 50 a0 e3
0000010 00 60 a0 e3 00 70 a0 e3 00 80 a0 e3 00 00 00 ea
Raspberry piのブートプロセスも気になったので,このページを読んでみました. http://elinux.org/RPi_Software#GPU_bootloaders
Raspberry piに電源を入れたとき,CPUはhaltされていてGPUの中の小さなRISC CPUが SoC(System on Chip)内のプログラムを実行します. そのためブートプロセスはGPUで実行されることになります.
- 1st ブートローダ: FAT32フォーマットされたSDカードのブートパーティションをマウントします. SoC内にあるので修正できません.
- 2nd ブートローダ(bootcode.bin): GPUファームウェアをSDカードから取り出すのに使います.
- GPUファームウェア(start.elf): fixup.datによりGPUとCPUで使うSDRAMのパーティションを設定します. (ここで,CPUがGPUから起こされる)
- ユーザコード(kernel.img): CPUにより,Linuxカーネル(kernel.img)や,他のブートローダ(U-Bootなど), OSを持たないアプリケーションなどが実行される. (config.txtにkernel=u-boot.binのようにすれば好きなイメージファイルが 実行される. それと,cmdline.txtにカーネルオプションがかける.)
2012/10/19までは3rdブートローダもあったみたいだが,今は必須ではないようです.
1st,2ndブートローダを作るのは大変ですが,オレオレカーネルを作る方のため?に, githubにbootconde.bin,start.elfが公開されているので大丈夫です. https://github.com/raspberrypi/firmware/tree/master/boot バイナリだけでソースコードは公開されていないみたいです. 公開されたとしてもarm cpuじゃなくてGPUのほうだから読めるのかどうか. 設定ファイルはconfig.txtで例えば, GPUのメモリを16MBにするときは,config.txtでgpu_mem=16としてfixup_cd.dat, start_cd.elf を追加すれば良さそうです.
http://wiki.gentoo.org/wiki/Raspberry_Pi を見てみると,SDカードの構成のサンプルがあります. 今回はファイルシステムは使わないのでブートパーティションだけあれば十分ですが.
さらにもう少し調べてみるとCPUがカーネルを走らせ始めた後もGPUのコードはアンロード されないということが分かりました. GPUは,VCOS (Video Core Operating System)と呼ばれる小さなOSを走らせ, カーネル側とmailboxというプロトコルや割り込みを通して,グラフィックの 操作を行います. 驚いたことにGPUはグラフィックだけでなくクロック制御やオーディオの制御 も行うようです.
せっかくnewlibを使えるようになったので何か移植してみようかと思います. 移植性のよいmrubyか,オレオレschemeか,その辺りになりそうです.
参考
- ARM RaspberryPi osdevのルート.Mailbox,割り込み,例外,シリアル通信,ブートなど簡単なまとめ
- ARM RaspberryPi Tutorial C osdevのチュートリアル.シリアル送受信でHello World
- ARM System Calls osdevの一部 システムコールの使い方
- cambridge ケンブリッジ大のチュートリアル.Alex’s OS.HDMIへの出力方法もある.
- iPhoneでインラインアセンブラ C言語からインラインアセンブラの使い方.
- Raspberry Pi Frame buffer rasp piのMailBoxレジスタのアドレスの記載あり
- raspberrypi / firmware MailBoxの詳細.ファームウェア開発者のgithub
- Category RaspberryPi eLinuxルート.
- PRI Hub eLinuxまとめ
- RPi Framebuffer eLinux.orgのFrame Bufferの記述.FrameBufferのアドレス,送受信手順の説明あり.
- Post subject: ARM framebuffer versatilepbでのFrameBufferのアドレス
- ARM926EJ-S User Guide versatilepbのほうのReal Viewのhtmlマニュアル
- ARM926EJ-S User Guide versatilepbのほうのReal Viewのpdfマニュアル
- ARM QEMUでベアメタルコードを動かす versatilepbでUARTからシリアルポートに送信(昔やってたやつ)
- Raspberry Pi Frame buffer framebufferをまとめた個人ブログ
- Step01 – Bare Metal Programming in C Pt1 GPIOのチュートリアル.まだこれしかない.個人ブログ.
- Put characters on the screen x86のようにはいかないというQ&A
- Level of Hackability of raspberry pi raspiハックの手順Q&A