uchan note

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

mdadm の write-intent bitmap と bad block log を競合させてみる

本記事は Linux Advent Calendar 2017 の 4 日目の記事です.

概要

Linux のソフトウェア RAID の機能を管理するための mdadm コマンドにまつわるバグの再現実験を行います. そのバグとは,次の記事で紹介されている「バグ 1」のことです.

blog.cybozu.io

続きを読む

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

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

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