uchan note

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

はてなブログで画像タグが展開されないバグの検証テスト記事

画像タグを張りつける条件によってはタグが展開されないことを確認するテスト.以下の画像をサンプルとして使う

f:id:uchan_nos:20171126114150p:plain:w100

スペースによるコードブロック

の後だとなぜか展開されない.

[f:id:uchan_nos:20171126114150p:plain:w100]

2枚貼っても展開されない.

[f:id:uchan_nos:20171126114150p:plain:w100]

バッククオートによるコードブロック

を書くことによって展開動作が復活する.

f:id:uchan_nos:20171126114150p:plain:w100

※この挙動が治ってしまうと記事が意味不明になるので,治る前の様子をスクショしました.

f:id:uchan_nos:20171126114558p:plain:w100

UEFI + iPXE で自作 OS をネットワーク起動する

概要

開発マシン上に置いた自作 OS のカーネルを,ネットワーク経由でターゲットマシンに読み込ませ,起動させるやり方についての記事です.

ディスクイメージをネットワークブートする - Raphine Project の記事を大いに参考にしました.Raphine Project の記事は BIOS を対象にしていますが,本記事は BIOS ではなく UEFI で起動させることが主な違いです.

UEFI + USB メモリによる起動

ネットワーク起動を説明する前に,簡単に USB メモリでの起動を説明します.

USB メモリに入れたカーネルUEFI で起動させるために,私は次の方法を用いています.

  • USB メモリを FAT でフォーマットして
  • /EFI/BOOT/BOOTX64.EFIUEFI アプリとして作成した自作ブートローダを配置し
  • カーネル本体となる ELF ファイルを適当なパスに配置し
  • ブートローダで ELF ファイルを読み取って起動する.

これは UEFI を使った起動方法の中で比較的シンプルな仕組みです.

UEFI を使う中で最もシンプルな起動方法は,大神祐真著の フルスクラッチで作る!UEFIベアメタルプログラミング で紹介された,UEFI アプリとして OS っぽいものを作る方法でしょう. この方法ではカーネル本体とブートローダを分ける必要がなくシンプルです.

ただ,個人的にはブートローダカーネル本体は分けて作りたい1ので,2 段階のブート方式を採用しました.

ネットワーク起動

ネットワーク起動とは,ネットワーク上に起動用データを置いておき,それを読み取って起動する方法です. 標準的な規格に PXE というものがあります.

標準の PXE では DHCP サーバを立て,起動用データの置き場所などを指定する必要がありますが,Raphine Project の記事では iPXE という PXE の実装を利用しています. iPXE には,DHCP を使わずに IP アドレス直指定でデータの場所を指定することができる,TFTP サーバではなく HTTP サーバから起動用データを取得できる,という機能があります.めっちゃ楽です. 本記事でもそれにならいます.

タイトルの「UEFI + iPXE」は,iPXE 自体とそこから起動されるものを UEFI アプリとする,という 2 つの意味があります. 次のような構成で起動させます.

  • USB メモリを FAT でフォーマットして
  • /EFI/BOOT/BOOTX64.EFI に iPXE のバイナリを配置し
  • 自作ブートローダカーネル本体となる ELF ファイルを HTTP サーバに配置し
  • iPXE でブートローダーと ELF ファイルを読み取って起動する.

Raphine Project の記事では UEFI の代わりに BIOS で起動させるためのディスクイメージを用意しているので,上記構成とはちょっと違いますね.

iPXE 起動用 USB メモリの準備

まずは iPXE のビルドに必要な依存パッケージをインストールします2

$ sudo apt-get install build-essential binutils-dev zlib1g-dev libiberty-dev liblzma-dev

次にダウンロード&ビルドします.

$ git clone git://git.ipxe.org/ipxe.git
$ cd ipxe/src
$ make bin-x86_64-efi/ipxe.efi

上記コマンドでは,アーキテクチャ x86_64 向けの UEFI アプリ(拡張子 .efi)を生成するように指示しています. ビルドが完了したら,USB メモリにファイルをコピーします. FAT でフォーマットしてある USB メモリが /mnt/usb にマウントされていると仮定して説明します.

$ mkdir -p /mnt/usb/EFI/BOOT
$ cp bin-x86_64-efi/ipxe.efi /mnt/usb/EFI/BOOT/BOOTX64.EFI

これで iPXE 起動用の USB メモリは完成です.

ブートローダカーネルの準備

自作ブートローダUEFI アプリ)とカーネル本体を適当なディレクトリに置いておき(仮に /path/to/files とします),そこを HTTP サーバからアクセスできるようにします.

$ cd /path/to/files
$ python3 -m http.server 8080

Python を使うと手軽に HTTP サーバを構築できるのでおすすめです.

iPXE による起動

Raphine Project の手順の kernel と initrd を次のように設定すればできます.

起動用スクリプトの記述や iPXE バイナリへの埋め込み方法は Raphine Project の記事を参考にしてください.

iPXE の高速化

何も設定をいじらずに iPXE をビルドすると余計な機能が組み込まれてしまい,iPXE の初期化時間が延びる原因となります.

iPXE initialising devices...

私の環境では上記の状態で数十秒時間がかかりました.

そんな時は ipxe/src/config/general.h でビルドオプションを調整すると改善する可能性があります. 私の環境では次に示す WiFi 関係のオプションを #undef したら非常に高速化しました.

#define CRYPTO_80211_WEP
#define CRYPTO_80211_WPA
#define CRYPTO_80211_WPA2

私が検証に使った MinnowBoard Turbot には無線 LAN インターフェースがないので,上記機能を初期化しようとしてタイムアウトしていた可能性がありますね.


  1. カーネル本体とブートローダーのビルドを分けられるのが大きな利点です.UEFI アプリとしてカーネルを作ってしまうと,コンパイラなどの選択が自由にできません.

  2. Raphine Project の記事では liblzma-dev を入れてないですが,私の場合は入れておかないとビルドが通りませんでした.

【技術書典3】システムプログラミングハンドブックを出します(ダウンロード頒布有)

大人気の技術書オンリーの同人誌即売会「技術書典3」が、10/22(日)に秋葉原 UDX で開かれます。

前回の技術書典2ではLinuxカーネルモジュール自作入門を出しました。 今回は「システムプログラミングハンドブック」を出しますので、そのお知らせです。

頒布情報

  • 日時 2017年10月22日(日)
  • 場所 アキバ・スクエア
  • ブース あ11企「サイボウズ株式会社」
  • 書名 システムプログラミングハンドブック
  • ページ数 48(表紙含む)

f:id:uchan_nos:20171010231329p:plain:w150

続きを読む

Newlibビルドメモ

Newlibをclangを使って自作OS向けにビルドしたメモ

Newlibはなぜか、host=targetでconfigureしてしまうと何もビルドが走らない。 たとえホスト環境と同じ環境で動く自作OSをビルドする際も、ちょっと違うtargetを指定する必要があるらしい。 target=x86_64-none-elfとすることでビルドが走るようになった。

(2017/07/26追記:newlib-cygwinのトップディレクトリではなく、newlib-cygwin/newlibを直接ビルドすることでhost=targetでもビルドできた。詳しくは後述)

さて、target=Xとすると、NewlibのビルドスクリプトX-cc というクロスコンパイラが PATH が通ったところに存在することを求めてくる。 gccを使ってビルドするときは、クロスコンパイルをする際にクロスコンパイラがあることが一般的なので、まあこれは妥当な仕様なのだろう。

ところがclangはクロス環境別のclangコマンドを用意しなくても、–targetオプションで好きなバイナリを出力できる。 X-cc という名前で、clang --target=X という内容のスクリプトを用意してNewlibビルドスクリプトをだます。

今回、自作OSのバイナリを位置独立実行ファイルとしたかったので CFLAGS=-fPIC を付けたい。 クロスコンパイルするので CFLAGS_FOR_TARGET という変数に設定することがミソ。CFLAGS だとlibc.aのビルドには影響を与えることができない。

git clone git://sourceware.org/git/newlib-cygwin.git

echo 'clang --target=x86_64-none-elf "$@"' > SOMEWHERE_IN_PATH/x86_64-none-elf-cc
chmod +x SOMEWHERE_IN_PATH/x86_64-none-elf-cc

mkdir build-newlib
cd build-newlib
CC=clang ../newlib-cygwin/configure CFLAGS_FOR_TARGET='-g -O2 -fPIC' --prefix=$HOME/x86_64-none-elf --target=x86_64-none-elf
make
make install

ネイティブビルド

Newlibをビルドしている環境と、Newlibの対象の環境が等しい(host=target)の場合のビルド方法。

Newlibはx86Linux環境でネイティブビルドを行うとlibtoolを用いたビルドになる。 で、おそらくその場合に --enable-multilib オプションがデフォルトで有効になっていて、 手元の実験だと -m32 付きで64ビット向けソースコードをビルドしようとしてエラーになるという間抜けなことになった。

そこで --disable-multilib でエラーを回避した。multilibは32ビット版と64ビット版を同時に生成するような機能らしいが、どうせ64ビット版ライブラリしか使わないので。

git clone git://sourceware.org/git/newlib-cygwin.git

mkdir build-newlib
cd build-newlib
CC=clang ../newlib-cygwin/newlib/configure CFLAGS='-g -O2 -fPIC' --prefix=$HOME/x86_64-pc-linux-gnu --disable-multilib
make
make install

UEFI Memory Map メモ

UEFI で得られる Memory Map の実例。

機種:qemu-system-x86_64

Index Type PhysicalStart VirtualStart NumberOfPages Attribute
0 3 EfiBootServicesCode 00000000 00000000 1 F
1 7 EfiConventionalMemory 00001000 00000000 9F F
2 7 EfiConventionalMemory 00100000 00000000 700 F
3 A EfiACPIMemoryNVS 00800000 00000000 8 F
4 7 EfiConventionalMemory 00808000 00000000 8 F
5 A EfiACPIMemoryNVS 00810000 00000000 8 F
6 7 EfiConventionalMemory 00818000 00000000 8 F
7 A EfiACPIMemoryNVS 00820000 00000000 E0 F
8 4 EfiBootServicesData 00900000 00000000 A00 F
9 7 EfiConventionalMemory 01300000 00000000 2C36 F
10 4 EfiBootServicesData 03F36000 00000000 20 F
11 7 EfiConventionalMemory 03F56000 00000000 2C89 F
12 2 EfiLoaderData 06BDF000 00000000 100 F
13 1 EfiLoaderCode 06CDF000 00000000 3 F
14 3 EfiBootServicesCode 06CE2000 00000000 AA F
15 A EfiACPIMemoryNVS 06D8C000 00000000 C F
16 0 EfiReservedMemoryType 06D98000 00000000 17 F
17 6 EfiRuntimeServicesData 06DAF000 00000000 20 800000000000000F
18 7 EfiConventionalMemory 06DCF000 00000000 167 F
19 4 EfiBootServicesData 06F36000 00000000 106 F
20 7 EfiConventionalMemory 0703C000 00000000 3 F
21 4 EfiBootServicesData 0703F000 00000000 5FB F
22 7 EfiConventionalMemory 0763A000 00000000 C F
23 4 EfiBootServicesData 07646000 00000000 689 F
24 7 EfiConventionalMemory 07CCF000 00000000 D F
25 3 EfiBootServicesCode 07CDC000 00000000 173 F
26 5 EfiRuntimeServicesCode 07E4F000 00000000 30 800000000000000F
27 6 EfiRuntimeServicesData 07E7F000 00000000 24 800000000000000F
28 0 EfiReservedMemoryType 07EA3000 00000000 4 F
29 9 EfiACPIReclaimMemory 07EA7000 00000000 8 F
30 A EfiACPIMemoryNVS 07EAF000 00000000 4 F
31 4 EfiBootServicesData 07EB3000 00000000 63 F
32 3 EfiBootServicesCode 07F16000 00000000 18 F
33 4 EfiBootServicesData 07F2E000 00000000 9 F
34 3 EfiBootServicesCode 07F37000 00000000 11 F
35 7 EfiConventionalMemory 07F48000 00000000 8 F
36 6 EfiRuntimeServicesData 07F50000 00000000 20 800000000000000F
37 7 EfiConventionalMemory 07F70000 00000000 8 F
38 A EfiACPIMemoryNVS 07F78000 00000000 88 F
39 6 EfiRuntimeServicesData FFE00000 00000000 200 8000000000000001

x86 OS自作ゼミの進め方

セキュリティ・キャンプ2017のx86 OS自作ゼミの進め方、最大限の成果を出す方法について思うところを書きます。

3日間は短い!

x86 OS自作ゼミは3日間しかありません(それでもセキュリティ・キャンプの中では最長なのですが)。OS自作という壮大なテーマに対して3日間というのはとても短いです。しかし短い中でも人に自慢できる成果が出た方が本人も楽しいだろうし、講師も皆さんの成果を見て楽しみたいと思っていますので、3日間である程度のモノが出来上がるようにしたいです。

3日間というと、独自のOSを1からそれなりの完成度まで持っていくには時間が到底足りません。「30日でできる!OS自作入門」をノンストップで読みまくると、それだけで3日間が終わるでしょう。それくらい短い時間です。

講師を最大限活用する

この3日間は、講師が皆さんのプログラミングをお手伝いする、というのが基本スタイルです。みっちり講義をするのではなく、皆さんが自分で手を動かして、講師がデバッグや設計のお手伝いをします。逆に、講師をどれだけ活用できるかが勝負と言ってもいいかもしれません。

ということで、受講者の皆さんには3日間という時間リソースと、講師という人的リソースを最大限に活用して成果を出せるようにネタを仕込んできていただきたいです。例えば、キャンプ当日までに基本的なコードは書いておいて、3日間は講師を捕まえてデバッグを手伝わせる、なんてのはいいやり方です。デバイスドライバを作ろうと思うのであれば、キャンプに来る前にそのデバイスに関する情報(IOポートの番号、デバイスレジスタ表、サンプルコードなど)を集めておいて、3日間は調べ物に時間をかけなくて良いようにする、というのもいいですね。

関連リンク

  • seccamp2017x86 OS自作ゼミの概要とか講師紹介とか、応募課題の出題意図の説明とか