2024年5月28日火曜日

MochiOS開発記@当面の目標

突然のフリーズ。 カーネルログを見ても解析困難。
というわけで、このバグ修正を当面の目標にしよう。
INT14なのでページフォルトなのだけど、誰かがメモリ破壊してる気がするなぁ。。。
とりあえず、カーネルログのレベル実装してもうちょい情報増やしてみよう。

2024年3月3日日曜日

CUnit@単体テストはじめ

CUnit@単体テストはじめ

はじめに

自作kernel作るにあたってDebug機能が欲しくなり。
snprintfが必要となり。
やっぱlibc欲しいなとlibcを作り始め(newlib使ってもよかったんだけど、自作楽しいので)。
snprintfを作ったのだが、これ動くのか?と。
単純に作業量2倍になるので、趣味でテスト系は避けてきたのだけど、やっぱ避けて通れないよねテスト。
というわけで、CUnit使うか・・・の備忘録。

ライブラリインストール

sudo apt-get install libcunit1-dev

テスト対象コード例(target.c)

int target_func( void )
{
	return 5;
}

テストコード例

#include <CUnit/CUnit.h>
#include <CUnit/Basic.h>

#include "target.c"

void test_func_1( void )
{
	ret = target_func();

	CU_ASSERT_EQUAL( ret, 5 );
}

int main( void )
{
	CU_pSuite suite;

	/* テストレジストリ初期化 */
	CU_initialize_registry();
	
	/* テストスイート追加 */
	suite = CU_add_suite( "TestSuite", NULL, NULL );

	/* テスト追加 */
	CU_add_test( suite, "test1", test_func_1 );

	/* テスト実行 */
	CU_basic_set_mode( CU_BRM_VERBOSE );
	CU_basic_run_tests();

	/* テストレジストリ削除 */
	CU_cleanum_registry();

	return 0;
}

static関数をテストしたい場合も考慮して、テスト対象コードをinclude。

サブ関数をstub化

サブ関数をstub化する方法はいくつかあるが、テスト対象コードをいっさい変えずにやるのは無理っぽい。
下記が最善策かなぁ。検索してもいい案が出てこないので自分で考えてみた。

#ifndef CALL
#define CALL( a ) a
#endif

#ifndef PROTO
#define PROTO( a, b ) a b
#endif

PROTO( extern int, target_sub( void ) );

int target_sub( void )
{
	return 5;
}

int target_func( void )
{
	return CALL( target_sub() );
}

テストコードは、テスト対象コードのincludeの前に下記を定義しておく

#define CALL( a ) stub_##a
#define PROTO( a, b ) a stub_##b

テスト対象コードincludeの後に下記を定義する

int stub_target_sub( void )
{
	return 5;
}

2022年11月22日火曜日

linux@VPN構築

linux@VPN構築

はじめに

在宅勤務になってから、ほぼ外出することが無くなり、外出先から自宅ネットワークに繋ぐことも無くなっていた。久しぶりに繋いでみたら、繋がらない。どうやら使っていたDDNSサービス(ieserver)が終了したらしく、他にも色々と理由があってダメだったので、見直すことにした。

ネットワーク構成は下記の通り。

My home
My Device
Rooter
VPN server
VPN Client
INTERNET
DDNS

VPN serverは、Debian GNU/Linux 9 (stretch)
VPNClientは、WindowsだったりAndroidだったり。

ドメイン名設定

私の自宅インターネット接続環境は固定IPアドレスではなく、再接続なんかの度にコロコロIPアドレスが変わるので、外出先からはvpn.mochi.cоm(仮)みたいなドメイン名を使ったアクセスをできるようにする必要がある。
これは、どこかのDDNSサービスを使ってドメイン名を取得し、自宅サーバから定期的に自分のグローバルIPアドレスを更新してあげることで実現できる。

DDNSサービスの選択

今回、適当に検索して色々出てくるなか、下記サービスを使うことにした。

DDNS Now

簡単に説明すると、

  • 無料
  • ホスト名:アカウント名.f5.si ※siはスロベニアらしい
  • Aレコード設定可
  • AAAAレコード設定可
  • TXTレコード設定可

こんな素晴らしいサービスを無料で提供して頂けることに感謝。
そして、もともと使っていたieserver並みにシンプルに使える。

crondでIPを自動更新する

自宅のIPアドレスはインターネット再接続の度に変わってしまい、その都度手動でDDNSサービスに再登録するのは面倒な上に、外出先からはIPアドレスが判らず再登録できないので、VPNサーバを使って自動的にIPをDDNSサービスに再登録する様にする。

  1. crontabを開く
crontab -e
  1. 10分毎に実行するコマンドを追加する
0,10,20,30,40,50 * * * * wget -O DDNSNow_update.log "https://f5.si/update.php?domain=ユーザ名&password=パスワード"

パスワードはAPIトークンでもOK。というかAPIトークンがいいかな。
この設定だと、ログ(DDNSNow_update.log)はホームディレクトリに吐かれる。

PPPoE接続設定

以前の私のインターネットはPPPoE接続だったのだが、FTTHなのに通信速度が遅く、在宅勤務となった際に安定したビデオ会議が出来なかったため、IPoE・IPv4 over IPv6接続に切り替えていた。
ただし、この接続はIKEv2のVPNで使用する500番や4500番などの特定ポートが使用できないため、ここでも外出先からVPN接続できない要因となっていた。
これまでの環境は維持しながらVPN接続できるようにもしたいため、ルータでIPoE接続しつつPPPoEパススルーさせ、VPNサーバから直接PPPoE接続させることで、通常のインターネット通信は高速のまま外部からのVPN接続を可能にさせる。
(これは通信事業者・プロバイダによって出来たり出来なかったりするかも)

My home
IPoE
PPPoE
PPPoE
Rooter
-PPPoE pass through-
VPN server
VPN Client
INTERNET

基本的なファイアウォール設定

VPNサーバもグローバルIPアドレスを獲得したわけなので、直接攻撃されることができて超危険。ファイアウォールを設定する。

  1. ufw(ファイアウォール)インストール
sudo apt-get install ufw
  1. 全アクセスの拒否
sudo ufw default deny
  1. ローカルLAN(192.168.1.*)からの全アクセスを全許可
sudo ufw allow in from 192.168.1.0/24
  1. ファイアウォール有効化
sudo ufw enable
  1. ログレベルの設定
    お好みで。フラッシュメモリを温存したいので、ログを無効にする。
sudo ufw logging off

出したい場合はoffをonに、またはlowに。

pppoeconf設定

  1. pppoeconfインストール
sudo apt-get install pppoeconf
  1. 接続設定
pppoeconf

色々質問されるが、大体は「はい」を選択すれば問題ない。

PPPoE接続

pon dsl-provider

接続状況確認

ip address show

(参考)PPPoE切断

poff dsl-provider

VPNサーバ構築

認証方式の選択

VPNのサーバ/ユーザ認証方式は、相互に証明書を使った認証(EAP-TLS)にする。
下記のような鍵構成にする。

My Home
My Device
クライアント証明書事前登録
ルート証明書事前配布
ルート証明書事前配布
オレオレ認証局
・個人鍵       
・公開鍵(ルート証明書)
VPNサーバ
・ルート証明書    
・個人鍵       
・公開鍵(サーバ証明書)
・クライアント証明書 
VPNクライアント
・ルート証明書    
・個人鍵       
・公開鍵(ユーザ証明書)

ユーザ証明書とサーバ証明書は、オレオレ認証局の個人鍵によって署名し、オレオレ認証局の公開鍵(ルート証明書)によって正しさを検証することができる。
VPN接続時、VPNサーバはクライアントにサーバ証明書を送信し、クライアントは事前配布済のルート証明書を使ってサーバを認証する。同様にクライアントはVPNサーバにクライアント証明書を送信し、ルート証明書を使ってクライアントを認証する。
(なお、VPNサーバにクライアント証明書を事前登録しておく理由は、失効済証明書で認証を防ぐため)

VPNサーバ設定

StrongSwanを使ってlinux上にVPNサーバを構築する。

StrongSwanインストール

sudo apt-get install strongswan

ipsecを設定する

/etc/ipsec.confを修正し、VPN構成を設定する。

conn ikev2-vpn
	# 基本設定
	auto=add
	keyingtries=1
	keyexchange=ikev2

	# VPNサーバ設定
	left=%any
	leftsubnet=0.0.0.0/0
	leftcert=server-cert.pem
	leftid=vpn.mochi.com
	leftsendcert=always
	
	# VPNクライアント設定
	right=%any
	rightauth=eap-tls
	rightcert=mochi-cert.pem
	rightca="CN=vpn.mochi.com"
	rightid=mochi
	rightsourceip=192.168.1.192/26
	rightdns=8.8.8.8,8.8.4.4
	
	# VPNクライアント生存確認設定
	dpddelay=60s
	dpdtimeout=20s
	dpdaction=clear
項目 説明
auto connのロード方法と接続タイミング。「add」は、ロードだけで接続しない(サーバ設定なので接続も何もない)設定
keyingtries 接続試行回数
keyexchange 鍵交換方法
left VPNサーバのローカルIPアドレス
leftcert VPNサーバ証明書のパス(絶対パスまたは/etc/ipsec.d/certsからの相対パス)
leftid VPNサーバ証明書のsubject(公開鍵の発行者)の識別名
leftsendcert VPNサーバ証明書をVPNクライアントに渡す方法
right VPNクライアント側のエンドポイントIPアドレス
rightauth VPNクライアントの認証方法
rightcert VPNクライアント証明書のパス(絶対パスまたは/etc/ipsec.d/certsからの相対パス)
rightca VPNクライアント証明書の検証に使用する認証局のDN(Distingished Name)。(/etc/ipsec.d/cacertsの中から該当するDNのルート証明書を使って検証される)
rightsourceip VPNクライアントに割り当てるIPアドレスの範囲
rightdns VPNクライアントに伝えるDNSアドレス
dpddelay 通信の生存確認間隔(デフォルトは30s)
dpdtimeout 生存確認応答待ちタイムアウト(デフォルトは150s)
dpdaction 生存確認できなかった場合のアクション。clearは接続終了を意味する。

ユーザ認証情報を設定する

サーバの秘密鍵もここに設定しておく。
/etc/ipsec.secretsを修正する

: RSA "server-key.pem"

鍵・証明書作成

作業ディレクトリ作成

下記の通り作業ディレクトリを作成して、そのディレクトリ内で作成する。

mkdir ~/pki
mkdir ~/pki/cacerts
mkdir ~/pki/certs
mkdir ~/pki/private
chmod go-rwx ~/pki/private
ディレクトリ 概要
cacerts ルート証明書を格納
certs 証明書を格納
private 個人鍵(秘密鍵)を格納

オレオレ認証局の個人鍵・証明書作成

  1. オレオレ認証局の個人鍵を作成する
pki --gen --type rsa --size 4096 --outform pem > ~/pki/private/ca-key.pem
  1. オレオレ認証局の公開鍵証明書(ルート証明書)を作成する
オレオレ認証局個人鍵
ca-key.pem
ルート証明書
ca-cert.pem
pki --self --ca --lifetime 3650 --in ~/pki/private/ca-key.pem --type rsa --dn "CN=ca.mochi.com" --outform pem > ~/pki/cacerts/ca-cert.pem

※CNの値は適切に変更すること。

VPNサーバの個人鍵・証明書作成

  1. サーバの個人鍵を作成する
pki --gen --type rsa --size 4096 --outform pem > ~/pki/private/server-key.pem
  1. サーバの公開鍵証明書(サーバ証明書)を作る
VPNサーバ個人鍵
server-key.pem
VPNサーバ公開鍵
VPNサーバ証明書
server-cert.pem
オレオレ認証局個人鍵
ca-key.pem
pki --pub --in ~/pki/private/server-key.pem --type rsa | pki --issue --lifetime 1825 --cacert ~/pki/cacerts/ca-cert.pem --cakey ~/pki/private/ca-key.pem --dn "CN=vpn.mochi.com" --san vpn.mochi.com --flag serverAuth --flag ikeIntermediate --outform pem > ~/pki/certs/server-cert.pem

※CNと-sanの値はDDNSサービスで取得したドメイン名に変更すること。

クライアントの個人鍵・証明書作成

  1. クライアントの個人鍵を作成する
pki --gen --type rsa --size 4096 --outform pem > ~/pki/private/mochi-key.pem
  1. クライアントの公開鍵証明書(クライアント証明書)を作る
クライアント個人鍵
mochi-key.pem
クライアント公開鍵
クライアント証明書
mochi-cert.pem
オレオレ認証局個人鍵
ca-key.pem
pki --pub --in ~/pki/private/mochi-key.pem --type rsa | pki --issue --lifetime 1825 --cacert ~/pki/cacerts/ca-cert.pem --cakey ~/pki/private/ca-key.pem --dn "CN=mochi" --san mochi --outform pem > ~/pki/certs/mochi-cert.pem

※ファイル名は適切に変更すること。
※CNと-sanの値は適切に変更すること。

  1. pkcs12(pfx)形式に証明書を変換
クライアント証明書
mochi-cert.pem
クライアント証明書
mochi.p12
クライアント個人鍵
mochi-key.pem
openssl pkcs12 -export -inkey ~/pki/private/mochi-key.pem -in ~/pki/certs/mochi-cert.pem -certfile ~/pki/cacerts/ca-cert.pem -caname "ca.mochi.com" -name "mochi" -out ~/mochi.p12

これは、クライアント側で証明書をインストールする際に使用する。

作業ディレクトリコピー

/etc/ipsec.dに作業ディレクトリ内容をそのままコピーする

sudo cp -r ~/pki/* /etc/ipsec.d/

IPsec再起動

sudo ipsec restart

VPN用ファイアウォール設定

IKEv2の鍵交換でUDP500番と4500番を使用するため、下記を用いてそのポートを解放する。

sudo ufw allow 500,4500/udp

2021年11月23日火曜日

github@パスワード認証でpushできなくなった

github@パスワード認証でpushできなくなった

はじめに

最近、漸くコードをイジれて、久しぶりにgithubにpushしようとしたのだが、

$ git push origin develop
Username for 'https://github.com': MasterMochi
Password for 'https://MasterMochi@github.com':
remote: Support for password authentication was removed on August 13, 2021. 
Please use a personal access token instead.
remote: Please see https://github.blog/2020-12-15-token-authentication-requirements-for-git-operations/ for more information.
fatal: Authentication failed for 'https://github.com/MasterMochi/mvfs.git/'

あれ、pushできな~い。なになに・・・

パスワード認証のサポートは2021年8月13日に削除されました~。
代わりにパーソナルアクセストークンを使ってくれ~

そういえば、以前pushした時にそんな警告が出ていた気がする。承認方法変わったのか。
8月ってダイブ前だな・・・

誘導されたページを見てみる

https://github.blog/2020-12-15-token-authentication-requirements-for-git-operations/
前置きはすっ飛ばして、"What you need to today"をテキトーに読むと、

今日すべきこと

  • 開発者さんのために、もしGitHub.comでGit操作を承認するためにパスワード使ってんなら、HTTPS (推奨)かSSH鍵を介した パーソナルアクセストークン を使わなきゃダメよ。

うん、で?と思ったら、「パーソナルアクセストークン」にリンクがついてて、そこに飛ぶと生成方法が書いてあった。

パーソナルアクセストークン取得

  • githubページにアクセス
  • 自分のアイコンから「Settings」に飛ぶ
  • 左のメニューから「Developer settings」に飛ぶ
  • 左のメニューから「Personal access tokens」に飛ぶ
  • 「Generate new token」をクリック
  • 「Note」に適当にトークンの説明を書く
  • 「Expiration」を適当に設定(No expirationにしちゃった…無期限)
  • 「Select scopes」で「repo」にチェックいれる
  • 「Generate token」をクリックしてトークンを取得

トークン使ってpush

passwordに取得したトークン入れるだけでOKだった。

2019年9月17日火曜日

MochiOS@仮想ファイルシステムの作成part10

MochiOS@仮想ファイルシステムの作成part10

前提知識無しで作るなんちゃって仮想ファイルシステムpart10。

開発日記です。独り言です。

これまでのあらすじ

part 内容
0 仮想ファイルシステムを作りたい宣言をする。
1 ざっくり仮想ファイルシステムの機能を決める。
が、機能呼出し先の仮想ファイルシステムのタスクIDをどう取得するかという問題に気付く。
2 カーネルにタスク名登録機能を実装した。
これでタスク名からタスクIDを取得する事が出来る様になるハズ、なので試したいが試す方法が無い事に気付き、ログ出力タスクの開発を宣言する。
3 ログ出力の為に使うvsnprintf()の仕様を調べる。
4 ログ出力タスクを開発してタスク名からタスクIDの取得機能が正しく動いている事が確認できた。
5 mount機能を実装した。たぶんできてる。
6 FDどうやって管理しようかー。メッセージのやり取り制御を状態遷移で制御しようかー。とか考えて、open機能を実装した。たぶんできてる。
7 openと同じようなシーケンスなので、メッセージとちょっとやる事変えて簡単にwrite機能を実装できちゃった。
8 writeと同じようなシーケンスなので、メッセージとちょっとやる事変えて簡単にread機能を実装できちゃった。
9 readと同じようなシーケンスなので、メッセージとちょっとやる事変えて簡単にclose機能を実装できちゃった。

応用

これまでmount、open~closeまでさくっと何も考えずに実装してきた。(そういえばunmount実装してないな。まいいか)
全てはドライバとアプリ間のインタフェースを作ることがひとまずの目的だったので、ドライバを作ってみなければ。

というわけでns16550ドライバを作った。
ns16550は所謂、RS-232C、COMポート、シリアルポート、UARTなどと呼ばれているハードウェアで、割りと制御が簡単。

それぞれ16byteの転送バッファと受信バッファがあって、書き込む(VfsWrite)時は転送バッファがフルでなければ1byteづつ書き込んでフルの時は転送バッファ空割り込みが来るまで待つ。読み込む時は受信割り込みが来たときに1byteづつ読み込んでいたデータを返すだけ。

mvfsメインスレッド受信スレッド転送スレッドNS16550VfsReadReqVfsReadRespVfsWriteReq転送バッファ書込転送要求VfsWriteResp転送バッファ空割込み割込み要求転送バッファ読込書込転送バッファ読込書込受信割込み読込受信バッファ書込み読込受信バッファ書込みVfsReadReq受信バッファ読込VfsReadRespmvfsメインスレッド受信スレッド転送スレッドNS16550

MochiKernelのメッセージパッシングはキューがなく、即座にメッセージ受信をしないと仮想ファイルサーバがブロックされてシステムの性能が悪くなってしまうので、メインと受信と転送用に1プロセス3スレッド構成に。
(MochiKernelにスレッド機能なんて無かったので、作った。)

とまぁ、これだけでかなりのネタになるんだけれど、割愛。
⇂がコミットログ。
https://github.com/MasterMochi/drv-ns16550/commit/a09994d07188ec84b663b902f6dbc07509cc44de

本題はこれから。

ttyサーバを作りたいが…

tty「サーバ」って書くと???になるのだけれど、要はttyドライバです。MochiOSではサーバープロセスとしてttyファイルを提供するので「サーバ」にしているだけです。

そんで考えたのだけれど、次のシーケンスで悩みが出てくる。

  1. ユーザアプリがttyサーバに行読込み(cockedモード)
  2. ttyサーバがドライバからデータ読込み
  3. 受信データ無しのため、読込サイズ0で応答。
NS16550ドライバttyサーバユーザアプリ1. VfsRead...Read2. VfsRead...Read3. 受信データ無しの為、サイズ0応答どうやって待ち合わせる…?NS16550ドライバttyサーバユーザアプリ

(シーケンス図では、仮想ファイルサーバは省略。それぞれのインタフェースは仮想ファイルサーバ上で行われる。そのためVfsRead…Readのような形式で記載。上位からはReadを起動し、それを受け取って仮想ファイルサーバから下位へVfsReadが起動される。)

ttyサーバは行データが揃うまでユーザアプリへRead要求を返さない。行データが揃うまで、ドライバから文字を読み続けなければいけないわけだけど、ドライバは受信データが無い(EOF)ので直ぐに読込サイズ0でReadを返す。
(ところが上のコミットではドライバがブロックする設計ミス。。。修正する。)

今、実装済みの機能でこれを実現するには、ttyサーバはドライバへのReadを繰り返すしかない。

NS16550ドライバttyサーバユーザアプリVfsRead...ReadVfsRead...ReadVfsRead...ReadVfsRead...ReadVfsRead...ReadVfsRead...ReadVfsRead...ReadNS16550ドライバttyサーバユーザアプリ

これは、もう、CPU100%一直線。

Select/VfsReady機能を追加する

説明しなくても大体わかると思うけど、selectです。指定したファイルディスクリプタの読み込みまたは書き込みが出来るまで待ち合わせます。
VfsReadyはファイルの読み込みまたは書き込み準備が出来た事を意味する通知。

NS16550NS16550ドライバ仮想ファイルサーバttyサーバSelectレディチェック。読込・書込ともレディでないので待ち合わせ受信割り込み読み込みVfsReadyレディ設定ReadVfsReadデータ & レディ状態読込レディ設定データWriteVfsWriteレディ状態書込レディ設定NS16550NS16550ドライバ仮想ファイルサーバttyサーバ

いちいちSelectで指定した全てのFDに対応するMountプロセスにレディ状態を問い合わせると、メッセージのやり取りが増えて待ち合わせ状態の管理や時間がかかるので、レディ状態は仮想ファイルシステムでも保持する仕様にした。その為、VfsRead応答に最新のレディ状態を含める様に修正する。

以下、メッセージ仕様。

■ Select要求メッセージ

offset size 内容 意味
0x00 4 機能ID 0x0000_0009 Select
0x04 4 タイプ(要求/応答) 0x0000_0000 要求
0x08 4 読込監視グローバルFD数 0~1024 -
0x0C 4 書込監視グローバルFD数 0~1024 -
0x10 4 読込監視グローバルFD[0] 任意 -
読込監視グローバルFD[…]
可変 4 書込監視グローバルFD[0] 任意 -
書込監視グローバルFD[…]

■ Select応答メッセージ

offset size 内容 意味
0x00 4 機能ID 0x0000_0009 Select
0x04 4 タイプ(要求/応答) 0x0000_0001 応答
0x08 4 処理結果 0x0000_0000
0x0000_0001
成功
失敗
0x0C 4 読込レディグローバルFD数 0~1024 -
0x10 4 書込レディグローバルFD数 0~1024 -
0x14 4 読込レディグローバルFD[0] 任意 -
読込レディグローバルFD[…]
可変 4 書込レディグローバルFD[0] 任意 -
書込レディグローバルFD[…]

■ VfsReady通知メッセージ

offset size 内容 意味
0x00 4 機能ID 0x0000_0008 vfsClose
0x04 4 タイプ(要求/応答) 0x0000_0002 通知
0x08 4 グローバルFD 任意 -
0x0C 4 Ready対象 0bit目: read
1bit目: write
読込みレディ
書込みレディ

■ vfsRead応答メッセージ(改造)

offset size 内容 意味
0x00 4 機能ID 0x0000_0006 vfsRead
0x04 4 タイプ(要求/応答) 0x0000_0001 応答
0x08 4 グローバルFD 任意 -
0x0C 4 処理結果 0x0000_0000
0x0000_0001
成功
失敗
0x10 4 読込レディ状態 0x0000_0000
0x0000_0001
非レディ
レディ
0x14 4 読込み実施サイズ 0~24,064 -
0x18 0~24,064 データ 任意 -

■ vfsWrite応答メッセージ(改造)

offset size 内容 意味
0x00 4 機能ID 0x0000_0004 vfsWrite
0x04 4 タイプ(要求/応答) 0x0000_0001 応答
0x08 4 グローバルFD 任意 -
0x0C 4 処理結果 0x0000_0000
0x0000_0001
成功
失敗
0x10 4 書込レディ状態 0x0000_0000
0x0000_0002
非レディ
レディ
0x10 4 書込み実施サイズ 0~24,064 -

実装してみた

ttyプロセスが、select->read->writeを無限に繰り返す様にお試し。COM1で「1234567890Select!」を入力してみた。上のウィンドウは画面でデバッグログを、下のウィンドウはCOM1。端っこ切れてる。。
enter image description here
深く説明はしないけど、まぁCPU100%にならずに読込み待ちできたって感じです。

ここで問題発生

これまで、突貫で適当に作りすぎてて、機能毎に複数の要求を扱えない。
なので、ttyファイルをReadするとその先のドライバファイルのReadが行えない状況に。。。

driver仮想ファイルサーバttyサーバuser「/com1」をMount「/ttyS0」をMount「/ttyS0」に対してReadVfsRead「/com1」に対してReaduserからのReadを実行中なので無理VfsReaddriver仮想ファイルサーバttyサーバuser

直さねば。全体的に大改造なので、次回の更新はいつになるかな。

Rust@naked関数

Rust@naked関数 multiboot仕様でブートローダを自作せずGRUBから自作カーネル(もどき)を起動できたので、起動情報も色々受け取りたい。multiboot準拠していれば、起動直前にブートローダがレジスタEAXにマジック値を...