uchan note

プログラミングや電子工作の話題を書きます

はりぼてOSでELFなアプリを起動する

これは 自作 OS Advent Calendar 2016 の 18 日目の記事です。

概要

『30 日でできる!OS 自作入門』 の「はりぼて OS」が対応している実行可能形式は HRB 形式です。 HRB という名の通り「はりぼて OS」独自の形式で、.text、.data、.bss セクションに相当するデータを含むことができます。

構造がシンプルなので、入門者にとっては実行可能形式を学ぶのに都合がいい形式であると思います。 しかし、独自形式ゆえ周辺のツールが整いづらく、実際に扱いやすい形式というわけでもありません。

本記事では、Linux 環境でスタンダードとなっている ELF 形式を「はりぼて OS」でも動かそうじゃないか、ということでやり方をご紹介します。 ELF 形式のアプリをロードして実行する方法(OS 側)、ELF 形式でアプリをコンパイルする方法(アプリ側)の 2 つの側面から解説します。

f:id:uchan_nos:20161218173129p:plain

画像はいらすとやから頂いたエルフです。

続きを読む

Windows で GCC 6.2.0 をビルドするメモ

f:id:uchan_nos:20161208235327j:plainこれは 自作 OS Advent Calendar 2016 の 8 日目の記事です。

概要

現時点で最新バージョンの GCC 6.2.0 を Windows 上でビルドするメモです。 このメモでは、i386-elf をターゲットにした GCC を作ります。 コンパイラ本体のみが対象で、標準ライブラリ等はビルドしません。 (将来、標準ライブラリも自作 OS 向けにビルドできるようになったら、また記事を書こうかと思います。っていつだよ!)

続きを読む

レベル変換の仕組み(オームの法則からMOS-FETを使ったレベル変換回路まで)

レベル変換

レベル変換とは、電圧の異なる信号線をつなぐ際に電圧を変換することです。

例えば Raspberry Pi という小さな Linux ボードコンピュータの電源電圧は 3.3V です。 そのため GPIO の入出力電圧も 3.3V となっており、電子工作でよくある 5V 駆動の装置を直接つなぐことができません。 著者は最近、秋月電子通商で売っている中で最安のキャラクタ液晶表示器 SC1602Raspberry Pi から制御しようと思ってレベル変換回路を作りました。

また、いわゆる「L チカ」といって LED を点滅させるだけの回路でも、LED によっては 3.5V 程度の電圧がないと十分に光らない品種があり (赤色 LED に比べ、青色 LED は高い電圧を求める傾向があります)、3.3V の出力では十分でないケースがあります。

レベル変換は低電圧側の回路から高電圧側の回路を制御するだけでなく、逆に高電圧側から低電圧側の回路を制御するのに用いる場合もあります。

ハードウェアエンジニアにとっては良く知られた回路ですが、ソフトウェア専門の人がちょっと電子工作しようとするとハマることが多いと思います。 この記事では、オームの法則を復習しつつ、レベル変換回路の基礎を見てみます。

続きを読む

osdev-jp という自作 OS のコミュニティを作りました

これは 自作 OS Advent Calendar 2016 の初日の記事です。

自作 OS Advent Calendar といいつつ、初日は技術的なことは書かずにコミュニティの紹介をします。期待してくださった方にはすみません(^^;)

自作 OS コミュニティの昔と今

私が自作 OS というものを知ったのは 2003 年ごろのことです。 そのころ、ウェブには活発な自作 OS コミュニティがいくつかありました。 私は主に OSASK コミュニティで活動していましたが、周りには dev-j、OS-Wiki などの OS 系の情報サイトや Mona OS、MEG-OS もありました。

時は流れて 2016 年。 私はしばらく自作 OS から離れていましたが、あるきっかけでまた自作 OS 関連の活動を再開させようと思いました。

久しぶりの OS 熱に侵されながら、おぼろげな過去の記憶をもとに dev-j や OS-Wiki などを再訪してみました。 しかし、そこは荒れた荒野のような状態でした。 dev-j にいたっては、トップページさえ 403 エラーでアクセスできない状態になっています。

osdev-jp の紹介

そこで私は、自作 OS に再びかかわろうと思うきっかけを与えてくれた 2 人の若者 ―― @liva_jy さんと @hikalium さん ―― とともに osdev-jp というコミュニティを作ることにしました。

発足したのは 2016 年 3 月です。 発足当初、このコミュニティは(OS-Wiki のように)OS 関連の情報を集めるサイトの名前でした。 今は情報収集はあまり捗っていませんが、「自作 OS もくもく会」というオフ会を定期的に開催し、自作 OS 好きの人同士のつながりを作り出しています。

osdev-jp の主要なリソースは以下の通りです。

「自作 OS もくもく会」は今のところ 2 カ月に 1 回のペースで開催しています。 開催予告は GitHub Wikiイベントカレンダー に載せていくつもりです。

nfcpy で複数の System Code を持つ NFC タグを扱う方法

Linux (Ubuntu 16.04) で nfcpy を使ってみました。落とし穴が多い気がしたので、ここにまとめます。 特に、複数の system code を持つような NFC タグの扱いが複雑ですので、詳しく説明します。

概要

LinuxNFC タグを読み書きするには nfcpy という Python ライブラリを使うのが割とよく用いられる方法のようです。 この記事では、nfcpy で NFC タグに記録されたブロックを取得する方法を説明します。

nfcpy に関する記事はいくつもあるのですが、どれも複数の System Code を持つ NFC カードの扱い方は書いてないようでした。 また、対応する nfcpy のバージョンが古かったりするので、この記事を書こうと思いました。

この記事で対象とする nfcpy のバージョンは 0.11.1 です。 また、動作検証は Sony 製の NFC リーダー・ライター PaSoRi RC-S380 を使って行いました。

nfcpy のインストール

公式ドキュメントは http://nfcpy.readthedocs.io/en/stable-0.11/ にあります。 (ただし、このドキュメントは少し古いです。現在 nfcpy は GitHub に移行したのですが、ドキュメントは lauchpad 時代のままです。)

nfcpy は PyPI に登録されているので、pip でインストールできます。 (nfcpy は現時点でまだ Python 3 系に対応してないので、pip3 は使わないでください。)

$ sudo pip install -U nfcpy

pip でインストールするとサンプルコードが手に入らないので、別途 GitHub からダウンロードします。

$ git clone git@github.com:nfcpy/nfcpy.git
$ cd nfcpy
$ git checkout stable/0.11

ここまでできたら、サンプルコードを実行して PaSoRi でタグを読み取ることができるはずです。

$ sudo examples/tagtool.py
[nfc.clf] searching for reader on path usb
[nfc.clf] using SONY RC-S380/P NFC Port-100 v1.11 at usb:002:013
** waiting for a tag **
Type3Tag 'FeliCa Standard (RC-S915)' ID=xxxxxxxxxxxxxxxx PMM=yyyyyyyyyyyyyyyy SYS=8108

NFC タグをタッチすると検出され、製造ID(IDm)、製造パラメータ(PMm)、システムコードが表示されます。(最終行)

おまけ:sudo を使わないで作業する方法

環境によって、USB 接続の NFC リーダーに一般ユーザが接続することができない場合があります。 毎回 sudo を使えば問題ないのですが、それは嫌だという方には公式ドキュメントで説明されている通り、回避策があります。

まず、一般ユーザで NFC リーダーが扱えないことを確認します。

$ examples/tagtool.py --device usb
[nfc.clf] searching for reader on path usb
[nfc.clf] no reader available on path usb
[main] no contactless reader found on usb
[main] no contactless reader available

この時点でちゃんとリーダーを検出できていれば、何もする必要はありません。 検出できないことが確認できたら、lsusb コマンドでデバイス ID を調べます。

$ lsusb
...
Bus 002 Device 013: ID 054c:06c3 Sony Corp.
...

それっぽいデバイスを見つけたら 054c:06c3 のところの番号を使って tagtool.py を実行しなおします。

$ examples/tagtool.py --device usb:054c:06c3
[nfc.clf] searching for reader on path usb:054c:06c3
[main] access denied for device with path usb:054c:06c3
[main] first match for path usb:054c:06c3 is usb:002:013
[main] usb:002:013 is owned by root but you are xxxxx
[main] members of the root group may use usb:002:013
[main] you may want to add a udev rule to access this device
[main] sudo sh -c 'echo SUBSYSTEM==\"usb\", ACTION==\"add\", ATTRS{idVendor}==\"054c\", ATTRS{idProduct}==\"06c3\", GROUP=\"plugdev\" >> /etc/udev/rules.d/nfcdev.rules'
[main] no contactless reader available

「指定した ID のデバイスは root の所有なのでアクセスできないよ」てなことが書いてあります。 下から 2 行目に解決策が提示されていますので、そのコマンドを実行します。

$ sudo sh -c 'echo SUBSYSTEM==\"usb\", ACTION==\"add\", ATTRS{idVendor}==\"054c\", ATTRS{idProduct}==\"06c3\", GROUP=\"plugdev\" >> /etc/udev/rules.d/nfcdev.rules'

マシンを再起動すれば、一般ユーザで使えるようになっているはずです。

NFC タグの中身を見てみる

nfcpy をインストールしたことですし、NFC タグの中身を表示するプログラムを作りましょう。 以下、NFC といいつつ FeliCa Standard を対象としたプログラムになっています。 他の規格のカードではうまく動かない可能性があります。

dump_simple.py は NFC タグ(の先頭 System)が持つサービスをすべて列挙するプログラムです。

import nfc


def check_services(tag, start, n):
    services = [nfc.tag.tt3.ServiceCode(i >> 6, i & 0x3f)
                for i in xrange(start, start+n)]
    versions = tag.request_service(services)
    for i in xrange(n):
        if versions[i] == 0xffff: continue
        print services[i], versions[i]


def on_connect(tag):
    print tag
    n = 32
    for i in xrange(0, 0x10000, n):
        check_services(tag, i, n)


def main():
    with nfc.ContactlessFrontend('usb') as clf:
        clf.connect(rdwr={'on-connect': on_connect})


if __name__ == '__main__':
    main()

取りあえず実行してみます。サービスの全列挙には実行には時間がかかりますのでしばらく待ちましょう。

$ python dump_simple.py
Type3Tag 'FeliCa Standard (RC-S915)' ID=xxxxxxxxxxxxxxxx PMM=yyyyyyyyyyyyyyyy SYS=8108
Service Code 0000h (Service 0 Type 000000b) 4097
Service Code 0100h (Service 4 Type 000000b) 4097
...

プログラムを説明します。

nfc.ContactlessFrontend('usb') で USB 接続の NFC リーダーを開きます。

clf.connectNFC リーダーを起動し、NFC タグのタッチ検出を開始します。 connect の引数で、NFC タグがタッチされたときに呼び出すコールバック関数 on_connect を指定します。

ドキュメントにはコールバック関数を設定しない(clf.connect(rdwr={}))場合、NFC タグをタッチするまでブロックするような記述がありますが、実験したところブロックしませんでした。 きちんと on-connect コールバックを設定する必要があるようです。

NFC タグがタッチされると on_connect にはそのタグを表すオブジェクト tag が渡ってきます。 以降、tag を使って NFC タグと通信し、様々なデータを読み取っていきます。

dump_simple.py プログラムは、タッチした NFC タグが持つすべてのサービスを列挙します。

tag.request_service にサービスコード(ServiceCode)を渡すと、タグがそのサービスを持つかどうかを調べられます。 タグが指定したサービスを持つなら 0xffff 以外の値が返ってきます。

サービスコードは 16 ビットの値で、上位 10 ビットがサービス番号、下位 6 ビットが属性値です。 16 ビット値ですから、0 から 0x10000 までを調べればすべてのサービスを網羅できます。 for i in xrange(0, 0x10000, n) はそういう意味です。

nfc.tag.tt3.ServiceCode(i >> 6, i & 0x3f) は、16 ビットの整数 i から ServiceCode オブジェクトを生成するやり方です。 i >> 6 で上位 10 ビットを取り出します。i & 0x3f で下位 6 ビットを取り出します。

tag.request_service は複数のサービスを一度に指定することができます。 実際に check_services 関数では n 個の ServiceCode のリストを作り、request_service 関数に渡しています。 1 つずつ調べるよりまとめて調べる方が速いので、32 個ずつ調べることにしたのです。

複数の System を扱う

dump_simple.py では System を切り替える処理を入れていなかったため、暗黙的に NFC タグの先頭の System を扱うことになります。 複数の System を持つタグの場合、明示的に扱う System を指定する処理が必要です。

今度は、すべての System を網羅的に調べるように改造した dump_all_systems.py を示します。

import nfc


def check_services(tag, start, n):
    services = [nfc.tag.tt3.ServiceCode(i >> 6, i & 0x3f)
                for i in xrange(start, start+n)]
    versions = tag.request_service(services)
    for i in xrange(n):
        if versions[i] == 0xffff: continue
        print services[i], versions[i]


def check_system(tag, system_code):
    idm, pmm = tag.polling(system_code=system_code)
    tag.idm, tag.pmm, tag.sys = idm, pmm, system_code
    print tag
    n = 32
    for i in xrange(0, 0x10000, n):
        check_services(tag, i, n)


def on_connect(tag):
    system_codes = tag.request_system_code()
    for s in system_codes:
        check_system(tag, s)


def main():
    with nfc.ContactlessFrontend('usb') as clf:
        clf.connect(rdwr={'on-connect': on_connect})


if __name__ == '__main__':
    main()

実行するとこんな感じになります。複数の SYS= を調べているのが分かりますね。

$ python dump_all_systems.py
Type3Tag 'FeliCa Standard (RC-S915)' ID=xxxxxxxxxxxxxxxx PMM=yyyyyyyyyyyyyyyy SYS=8108
Service Code 0000h (Service 0 Type 000000b) 4097
Service Code 0100h (Service 4 Type 000000b) 4097
...
Type3Tag 'FeliCa Standard (RC-S915)' ID=xxxxxxxxxxxxxxxx PMM=yyyyyyyyyyyyyyyy SYS=FE00
Service Code 1748h (Service 93 Random RW with key) 4097
Service Code 174Bh (Service 93 Random RO w/o key) 5963
...

プログラムの変更のポイントはこんな感じです。

  • tag.request_system_code 関数で NFC タグが持っている System の一覧を得る
  • 今まで on_connect でやっていた処理を check_system 関数に移して、polling 処理を加えた

polling の処理がミソとなる部分ですので、少し詳しく説明します。

ポーリングは、NFC リーダーが NFC タグを探す処理のことです。 ポーリング時に System Code を指定することで衝突防止を実現しています。 指定したコードと同じコードを持つ NFC タグだけがポーリングに反応することで、複数の NFC タグが探索範囲に存在しても衝突しないようになっています。

さて、tag.polling 関数に System Code を渡すと、そのコードを持つ NFC タグの IDm, PMm を取得できます。 取得した IDm と PMm、それからポーリングに使った System Code を tag.idm, tag.pmm, tag.sys に設定することで、以降のデータ通信をその System に対して行えるようになります。

データブロックの読み込み

NFC タグがもつすべての System とすべてのサービスが分かったので、次はいよいよ任意のサービスが持つデータブロックを読み込んでみます。

dump_block.py

import nfc
from binascii import hexlify


def on_connect(tag):
    idm, pmm = tag.polling(system_code=0xfe00)
    tag.idm, tag.pmm, tag.sys = idm, pmm, 0xfe00

    sc = nfc.tag.tt3.ServiceCode(93, 0x0b)  # 174B
    bc = nfc.tag.tt3.BlockCode(1, service=0)
    data = tag.read_without_encryption([sc], [bc])
    print 'str:', data
    print 'hex:', hexlify(data)


def main():
    with nfc.ContactlessFrontend('usb') as clf:
        clf.connect(rdwr={'on-connect': on_connect})


if __name__ == '__main__':
    main()

このプログラムはシステム 0xfe00 内のサービス 0x174B が持つブロック 1 のデータを読み込みます。 システムコードとサービスコードは、先ほどの探索で得た値を使っています。

このあたりは read_without_encryption のドキュメント に載っているサンプルを参考にしています。 ドキュメントにもありますが、BlockCode の生成時に渡す service=0 はサービスコードそのものではなく、read_without_encryption の引数 service_list 内でのインデックスです。

read_without_encryption([sc1, sc2, ..., scN], [...]) などと N 個のサービスを指定したとき、BlockCodeservice は 0 から N-1 までの値を取れます。

1 つのサービスは 16 バイトの大きさのブロックを複数個持ち、サービスの中で 0 から連番が付いています。

| Service X   |
|    ---------|
|   | Block 0 |
|    ---------|
|   | Block 1 |
|    ---------|
|   |  ...    |

サービスコードの下位 6 ビットは属性値で、次のようになっています。 この中で、パスワードなしで読み書きできるのは "w/o key" (without key = キーなし)となっているサービスだけです。 例えば 2 つのサービス "1748h" と "174Bh" のうち、読み取れるのは "174Bh" の方だけです。

ビット 意味
001000 Random RW with key
001001 Random RW w/o key
001010 Random RO with key
001011 Random RO w/o key
001100 Cyclic RW with key
001101 Cyclic RW w/o key
001110 Cyclic RO with key
001111 Cyclic RO w/o key
010000 Purse Direct with key
010001 Purse Direct w/o key
010010 Purse Cashback with key
010011 Purse Cashback w/o key
010100 Purse Decrement with key
010101 Purse Decrement w/o key
010110 Purse Read Only with key
010111 Purse Read Only w/o key

Python で Markdown 文書を HTML へ変換する

python-markdown2, py-gfm, pygments というライブラリを使って Markdown 文書を HTML へ変換するメモです。

GitHub Flavored Markdown (GFM) の特徴である Fenced Code Block にも対応しています。

インストール

$ pip3 install --user python-markdown2 py-gfm pygments pygments-style-github

スクリプト

md2html.py

#!/usr/bin/python3

import sys
from textwrap import dedent

from markdown2 import markdown


CHARSET = 'utf-8'


def main():
    if len(sys.argv) < 2:
        print('Usage: md2html path/to/file', file=sys.stderr)
        sys.exit(1)

    with open(sys.argv[1], encoding=CHARSET) as f:
        source = f.read()

    body = markdown(source, extras=['fenced-code-blocks'])
    html = dedent('''\
        <html>
        <head>
            <meta charset="{charset}">
            <link rel="stylesheet" type="text/css" href="github_pygments.css">
        </head>
        <body>{body}</body>
        </html>
        ''').format(charset=CHARSET, body=body)
    print(html)


if __name__ == '__main__':
    main()

CSS の準備

$ ~/.local/bin/pygmentize -S github -f html > github_pygments.css

変換

$ ./md2html.py hoge.md > hoge.html

おまけ:ブラウザから確認

手軽にブラウザから確認するには Python で HTTP サーバを立てると便利です。 この程度の静的 HTML なら直接ブラウザで開いてもいいのですが、HTTP サーバを立てればリモート開発している時も便利です。

$ python3 -m http.server 8000

8000 番ポートで起動します。

フリースタンディングな GCC 5.3 をビルド on Ubuntu 15.10

自作 OS 製作などでお世話になるフリースタンディングな GCC をビルドする手順のメモ。完全にきれいな環境でビルドしたわけではなく、依存関係をすべてメモに記載できていない。手順通りにビルドしてもツールが不足していたりしてエラーになることがあるが、悪しからず。

続きを読む