11
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Sechack365 2019Advent Calendar 2019

Day 21

自作組込みOS "CHAOS" について

Last updated at Posted at 2019-12-21

本記事は Sechack365 2019 Advent Calendar 2019 - Qiita の 21 日目の投稿です.

1. CHAOSとは?

拡張性に優れて自由に改造できる(ハッカブルな)組込みOSです.フルスクラッチでARM64bitで動作するOSというのが特徴で,現状はRaspberry Pi 3 model bで動作します.またQEMUを使ったエミュレーションも可能です.

CHAOSを使って組込みOSを理解しましょう!

サポートしている機能

  • UART (PCと通信)
  • GPIO (LEDやモータ,センサ等のInput/Output)
  • RNG (乱数生成)

2. 実際に手を動かす

開発環境構築

  1. 下のリンクからCHAOSをクローンまたはダウンロードしてください.
    CHAOSのレポジトリ
  2. READMEを参照して開発環境を構築してください.
  3. makeしてビルドに成功すればOKです.

動かしてみよう

  1. QEMUで動かしてみましょう.
$ make run

するとQEMUが立ち上がって以下のようなコマンド入力画面が表示されます.

tsuru@rz4:~/CHAOS$ make run
qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial null -serial stdio

CHAOS started
> 

色々入力してみましょう.
対応コマンドはmain.cに書いてあります.

> echo hello
hello
> on
GPIO ON!
> off
GPIO OFF!
> reboot
rebooting...

入力した文字列はchar型のbuf配列に格納されてstrcmpで対応するコマンドを比較して処理を決めています.
ctrl+c でQEMUを終了できます.

  1. ラズパイに書き込もう
    FAT32でフォーマットされた micro SD をPCに差し込み,ディレクトリ内のファイル群をコピーします.
kernel8.img
bootcode.bin
config.txt
start.elf

SDカードスロットに差し込み,ラズパイのGPIO14ピン,15ピン(RX,TX)とGNDをUSBシリアル変換器を使い,USB経由でPCを接続します.その後以下のコマンドでminicomを立ち上げます.

make com

minicomが立ち上がった後にラズパイの電源を入れるとQEMUでエミュレーションした時と同じようにコマンド入力画面が表示されます.

ここでGPIO16ピンとGNDにLEDを接続して以下のコマンドを入力してみましょう.

on

するとどうでしょうLEDが点灯します!めっちゃ簡単ですね!
消灯する際は

off

で出来ます.
あとは,乱数を作ったり,再起動したり,シャットダウンできたりします.

CHAOS started
> rand
4EBE8815
> rand
523D3704
> reboot
rebooting...
CHAOS started
> shutdown
shutdown

という感じで自作OS CHAOS の機能を紹介しました.
次はソースコードについて説明します.

  1. CHAOSのコード読解
lib.c
#include "lib.h"
#include "uart.h"
#include "gpio.h"

void init(void) {
	uart_init();
}

int putc(unsigned char c) {
	if (c == '\n')
		uart_send('\r');
	if (c != "")
		uart_send(c);
	return 0;
}

unsigned char getc(void) {
	unsigned char c = uart_getc();
	c = (c == '\r') ? '\n' : c;
	putc(c);
	return c;
}

int puts(unsigned char *str) {
	while (*str)
		putc(*(str++));
	return 0;
}

int gets(unsigned char *buf) {
	int i = 0;
	unsigned char c;
	do {
		c = getc();
		if (c == '\n')
			c = '\0';
		buf[i++] = c;
	} while (c);
	return i - 1;
}

int strcmp(const char *s1, const char *s2) {
	while (*s1 || *s2) {
		if (*s1 != *s2)
			return (*s1 > *s2) ? 1 : -1;
		s1++;
		s2++;
	}
	return 0;
}

int strncmp(const char *s1, const char *s2, int len) {
	while ((*s1 || *s2) && (len > 0)) {
		if (*s1 != *s2)
			return (*s1 > *s2) ? 1 : -1;
		s1++;
		s2++;
		len--;
	}
	return 0;
}

void rand_init(void) {
	*RNG_STATUS = 0x40000;
	*RNG_INT_MASK |= 1;
	*RNG_CTRL |= 1;
	while(!((*RNG_STATUS) >> 24)) asm volatile("nop");
}

unsigned int rand(unsigned int min, unsigned int max) {
	return *RNG_DATA % (max - min) + min;
}

void on(unsigned int n) {
    register unsigned int r;
// ON
	*GPFSEL1 = 0x01 << 18;
	*GPSET0 = 0x01 << 16;

// UARTできるように切り替える
    r=*GPFSEL1;
    r&=~((7<<12)|(7<<15)); // gpio14, gpio15
    r|=(2<<12)|(2<<15);    // alt5
    *GPFSEL1 = r;
}

void off(unsigned int n) {
    register unsigned int r;
// OFF
	*GPFSEL1 = 0x01 << 18;
	*GPCLR0 = 0x01 << 16;

// UARTできるように切り替える
    r=*GPFSEL1;
    r&=~((7<<12)|(7<<15)); // gpio14, gpio15
    r|=(2<<12)|(2<<15);    // alt5
    *GPFSEL1 = r;
}

main.cで呼び出している関数がlib.cで定義してあります.注目するべきはGPFSEL1とGPSET0と*GPCLR0です.これらはgpio.hで定義されたレジスタです.

gpio.h
// RaspberryPi3 Memory Mapped I/O Base Address
#define MMIO_BASE       0x3F000000

// GPIO
#define GPFSEL0         ((volatile unsigned int*)(MMIO_BASE+0x00200000))
#define GPFSEL1         ((volatile unsigned int*)(MMIO_BASE+0x00200004))
#define GPFSEL2         ((volatile unsigned int*)(MMIO_BASE+0x00200008))
#define GPFSEL3         ((volatile unsigned int*)(MMIO_BASE+0x0020000C))
#define GPFSEL4         ((volatile unsigned int*)(MMIO_BASE+0x00200010))
#define GPFSEL5         ((volatile unsigned int*)(MMIO_BASE+0x00200014))
#define GPSET0          ((volatile unsigned int*)(MMIO_BASE+0x0020001C))
#define GPSET1          ((volatile unsigned int*)(MMIO_BASE+0x00200020))
#define GPCLR0          ((volatile unsigned int*)(MMIO_BASE+0x00200028))
#define GPLEV0          ((volatile unsigned int*)(MMIO_BASE+0x00200034))
#define GPLEV1          ((volatile unsigned int*)(MMIO_BASE+0x00200038))
#define GPEDS0          ((volatile unsigned int*)(MMIO_BASE+0x00200040))
#define GPEDS1          ((volatile unsigned int*)(MMIO_BASE+0x00200044))
#define GPHEN0          ((volatile unsigned int*)(MMIO_BASE+0x00200064))
#define GPHEN1          ((volatile unsigned int*)(MMIO_BASE+0x00200068))
#define GPPUD           ((volatile unsigned int*)(MMIO_BASE+0x00200094))
#define GPPUDCLK0       ((volatile unsigned int*)(MMIO_BASE+0x00200098))
#define GPPUDCLK1       ((volatile unsigned int*)(MMIO_BASE+0x0020009C))

// RNG
#define RNG_CTRL        ((volatile unsigned int*)(MMIO_BASE+0x00104000))
#define RNG_STATUS      ((volatile unsigned int*)(MMIO_BASE+0x00104004))
#define RNG_DATA        ((volatile unsigned int*)(MMIO_BASE+0x00104008))
#define RNG_INT_MASK    ((volatile unsigned int*)(MMIO_BASE+0x00104010))

レジスタのアドレスをgpio.hで定義されています.
詳細はラズパイ3に搭載されているBMC2837のデータシートを確認してください.

※実はデータシートのベースアドレスは0x7F000000なんですが現状のラズパイ3では0x3F000000であることに注意してください.

GPFSEL...GPIOの入出力設定するレジスタ
GPSET ...対応するGPIOピンをHIGHにする
GPCLR ...対応するGPIOピンをLOWにする

ここでlib.cのon()を見てみましょう.

lib.c
void on(unsigned int n) {
    register unsigned int r;
// ON
	*GPFSEL1 = 0x01 << 18;
	*GPSET0 = 0x01 << 16;

// UARTできるように切り替える
    r=*GPFSEL1;
    r&=~((7<<12)|(7<<15)); // gpio14, gpio15
    r|=(2<<12)|(2<<15);    // alt5
    *GPFSEL1 = r;
}
  1. *GPFSEL1 = 0x01 << 18; はデータシートP92を参照すると"GPIO16ピンを出力に設定"
  2. *GPSET0 = 0x01 << 16; はOUTしたいピン番号分(16)左シフトすることで"GPIO16をOUT"
    この2つのレジスタの演算だけでラズパイのGPIO操作ができます.

ただ,UARTする際にGPFSEL1を操作するため,UARTできるように書き換えるための演算を次の行から行っています.

3. 終わりに

駆け足になりましたがザックリとCHAOSの概要とGPIOの設定,操作を見ていきました.
レジスタのアドレスを変えたりリンカ・スクリプトを改造することで他のSoCでも動くと思います.ガンガン移植していって楽しむのも通かと思います.
CHAOSはKL-01というライセンスなので自由に改変,再配布可能です.またこんな機能追加したよ!というのがあればどんどんプルリク送ってほしいです!まだまだCHAOSには機能が充分に揃ってないですが,そのぶん改造の幅やいろんな可能性を秘めていると言えます.一緒にCHAOSを盛り上げていきましょう!!

11
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?