MBRもGPTも無い世界もあるんですよ(blkdevpartsの紹介）

この記事は[Linuxその2 Advent Calendar 2020](https://qiita.com/advent-calendar/2020/linux2)の6日目です。

# TL;DR

- linux kernelのcommand lineにblkdevparts=... という記載をすると、パーティション情報をloaderから直接渡せるよ！

```
bootargs::
  blkdevparts=mmcblk0:1G(data0),1G(data1),-;mmcblk0boot0:1m(boot),-(kernel)
```


# はじめに:どうやってパーティションは管理されているのか？

**「ありがたいことに私の狂気（パーティション）は君達の神(MBR/GPT)が保証してくれるというわけだ（パン）　」**

## 現代コンピュータにおけるパーティション管理

そもそも、パーティションとは、ハードディスクの記憶領域を論理的に分割する事を意味していた。現在主流の技術を中心に見ていく。

- 過去には、SASIのハードディスクのように40MBと20MB x 2に切り替えられるようなものもありましたけどね！！
- UNIXはVTOC(Volume Table of Contents)ですね…… 

### MBR (Master Boot Record )

IBM機のBIOS制限を継承したもの。

- ハードディスクの先頭セクタに、基本パーティションもしくは拡張パーティションを4つ記憶。
    - 拡張パーティションの中に、論理パーティションを作る事ができる。
    - CHS方式で管理するが、容量制限厳しい（後述のLBA方式に移行済み）。（C : Cylinder, シリンダ ( 0～1023)／H : Head, ヘッド (0～254)／S : Sector ( 1～ 63)）

### GPT (GUID Partition Table)

パーティション管理方式をMBRから置き換える技術。

- 後方互換のためにMBR情報を含む。
    - 基本的には「１パーティション」という情報を入れる。
    - だが、ハイブリッドMBRとして、GPT/MBRで同じ情報を入れる場合も（やっかいな）

| LBA | 情報 | 備考 |
|:-:|:-:|:-:|:-:|
| 0  | MBR | ＊ここがややこしい|
| 1 |  GPTパーティションテーブル・ヘッダー | 128 エントリまで |
| 2～33 |  GPTパーティションエントリ  |
| 34～ | パーティション  |
| ---- | 第２GPTパーティションエントリ  |
| ---- | 第２GPTパーティションテーブル・ヘッダー |  |
| ---- | 第２MBR | |

### LVM(Logical Volume Manager, おまけ)

LVM(Logical Volume Manager)も一応言及しておく。

- 物理パーティションを、LVMに登録しておく。
- LVMはパーティションを登録済み物理パーティションから割り当てる。
- これであとからディスクを追加するとかも、サイズを大きくするとか、やりたい放題！

という夢のような技術ですが……　今回の話では見なかったことに。

# 本題:MBR/GPTも無しにパーティションをマウント

**「よろしい　ならば私も問おう。君らの神(MBR/GPT)の正気（Integrity)は一体どこの誰が保障してくれるのだね?」**

MBR/GPTの正当性確認をするのは、いわゆるLoaderですね。

だが、ちょっと待ってほしい……　組み込み技術において、本当にMBR/GPTは必要なのだろうか？　例えば、こういう構成がある。

- 内蔵ストレージ容量は固定。128 GBとか決まっている。
- ストレージはボード上にEmbeddedされており、交換不可能。
- 組み込み技術では「可用性（問題なく利用できること）が重要視される。

極端なことを言えば、 **交換できないストレージの管理情報だったら、むしろROM上のloaderが固定保持する方がセキュアである**

## loaderからkernelへパーティション情報を引き渡すblkdevparts

[Embedded device command line partition parsing](https://www.kernel.org/doc/html/latest/block/cmdline-partition.html) に、loaderからkernelへパーティション情報を引き渡す手段についての記載がある。

少し、邦訳しつつ説明をする。

> The “blkdevparts” command line option adds support for reading the block device partition table from the kernel command line.
> It is typically used for fixed block (eMMC) embedded devices. It has no MBR, so saves storage space. Bootloader can be easily accessed by absolute address of data on the block device. Users can easily change the partition.

"blkdevparts" command line optionは、kernel command lineから、block device partition tableを読み出すことを実現する。
これは一般的にはdeviceに組み込まれる固定ディスク(eMMC)に用いられる。これらはMBRを持たず、ストレージ空間しか保持しない。Bootloaderはblock device上のデータの絶対的なアドレスに簡単にアクセスできる。Userは簡単にパーティションを変更できる。

つまり、「いきなりMBRなしで、パーティションを配置してもよい」ということですね！

## 使い方

もうちょっと使い方についても補足をする。

```
Example:

    eMMC disk names are "mmcblk0" and "mmcblk0boot0".

  bootargs::

    'blkdevparts=mmcblk0:1G(data0),1G(data1),-;mmcblk0boot0:1m(boot),-(kernel)'

  dmesg::

    mmcblk0: p1(data0) p2(data1) p3()
    mmcblk0boot0: p1(boot) p2(kernel)
```

ここで、1Gと書いているのは容量1G、"-"と書いているのは残り全部の容量など。よって以下のようなパーティションが指定されたことになる。

```
◯ディスクブロックデバイス(mmcblk0)
　├ 1G(data0) -> mmbclk0p1
　├ 1G(data1) -> mmbclk0p2
　└ 残り全部 -> mmbclk0p3() 

◯ディスクブロックデバイス(mmcblk0boot)
　├ 1m(boot) -> mmbclk0boot0p1
　└ 残り全部 -> mmbclk0boot0p2
```


## ソースコード拝見

### blkdevparts= で指定されたオプションはファイルローカルな変数cmdlineに保持。

```c:block/partitions/cmdline.c

static int __init cmdline_parts_setup(char *s)
{
	cmdline = s;
	return 1;
}
__setup("blkdevparts=", cmdline_parts_setup);
```

### パーティション認識時に、コマンドライン文字列解析も呼ばれる。

このあたりのコードで、他のディスクパーティション管理方式を横並びに、コマンドライン解析も行われる。

```c:block/partitions/core.c

static int (*check_part[])(struct parsed_partitions *) = {
	/*
	 * Probe partition formats with tables at disk address 0
	 * that also have an ADFS boot block at 0xdc0.
	 */

#ifdef CONFIG_ACORN_PARTITION_ADFS
	adfspart_check_ADFS,
#endif

#ifdef CONFIG_CMDLINE_PARTITION
	cmdline_partition, ★これが今回
#endif
#ifdef CONFIG_EFI_PARTITION
	efi_partition,		/* this must come before msdos */
#endif
#ifdef CONFIG_SGI_PARTITION
	sgi_partition,
#endif
#ifdef CONFIG_LDM_PARTITION
	ldm_partition,		/* this must come before msdos */
#endif
#ifdef CONFIG_MSDOS_PARTITION
	msdos_partition, ★これがMBR
#endif
#ifdef CONFIG_OSF_PARTITION
	osf_partition,
#endif

```

### コマンドライン文字列解析して、parse成功したらこれで追加。

```c:block/partitions/cmdline.c
/*
 * Purpose: allocate cmdline partitions.
 * Returns:
 * -1 if unable to read the partition table
 *  0 if this isn't our partition table
 *  1 if successful
 */
int cmdline_partition(struct parsed_partitions *state)
{
	sector_t disk_size;
	char bdev[BDEVNAME_SIZE];
	struct cmdline_parts *parts;

	if (cmdline) { ★これは一回しか実行されない
		if (bdev_parts)
			cmdline_parts_free(&bdev_parts);

		if (cmdline_parts_parse(&bdev_parts, cmdline)) {
			cmdline = NULL;
			return -1;
		}
		cmdline = NULL;
	}

	if (!bdev_parts)
		return 0;

	bdevname(state->bdev, bdev);
	parts = cmdline_parts_find(bdev_parts, bdev);
	if (!parts)
		return 0;

	disk_size = get_capacity(state->bdev->bd_disk) << 9;

	cmdline_parts_set(parts, disk_size, 1, add_part, (void *)state);
	cmdline_parts_verifier(1, state);

	strlcat(state->pp_buf, "\n", PAGE_SIZE);

	return 1;
}
```


## blkdevpartsによってできる事。

 (MBRを使っていても) パーティション情報が破損していても、マウントできる、かなあ…？

例えば、外付けハードディスクだった場合を考える。この場合ならば、別のPCに接続してディスクコピーして、更にMBR情報を書き直せば、もしかすると中のデータを読み出せる「かも」しれない。

では、オンボードのeMMCだったら？　まず、ディスクコピーができない。起動もしないのだから。そして、MBR情報を書き直せるのか？なかなか難しい課題に直面する。

そこで、blkdevpartsですよ！これらならば、起動時にloaderまで動けば、うまくするとMBR情報が壊れていても起動できる"かも"しれません。


## この話の例外（外付けストレージ）

例１）PS4のように、内蔵ストレージを交換できるものは、当然交換するストレージ側にパーティション情報があった方がよい場合もある。ただし、どうせ暗号化などをしていて中身を見せるつもりが無いのであれば、最初からパーティション情報は入れないことも選択できる。

例２）USBストレージ(USBメモリやUSB HDD、USB SSD)に関しても、交換された先でも使えるように、ストレージ側にパーティション情報があった方がよい。ただこれも良かったり、悪かったり…。

このあたりは種々の条件にもよるので、まあ、そういう世界もありますよー、ぐらいの軽い気持ちで。

# まとめ(再掲)

お忙しい中、ご精読ありがとうございました！

- linux kernelのcommand lineにblkdevparts=... という記載をすると、パーティション情報をloaderから直接渡せるよ！

```
bootargs::
  blkdevparts=mmcblk0:1G(data0),1G(data1),-;mmcblk0boot0:1m(boot),-(kernel)
```

以上となります。