Skip to contentSkip to conversation

boot-directdisk: mount root by MBR disk signature for Linux 3.8+

PATCH In review Jonathan Liu 2 participants 4 messages Jul 9, 2013 – Jul 10, 2013

lore
Share & tools
lore
#0 lore raw

PATCH v3 1/1 boot-directdisk: mount root by MBR disk signature for Linux 3.8+

The root device is currently set as /dev/hda2. However, this is only correct if it's the first IDE drive. If booting off the first SATA drive instead, it would be /dev/sda2. It's not the first drive, neither /dev/hda2 or /dev/sda2 would be correct.

The solution to this has typically been to use the filesystem UUID to specify the root device but this requires extra support in the initrd. Linux 3.8 introduces the ability to specify the root device using the MBR disk signature and the partition number which is much simpler to use and avoids the extra overhead of an initrd.

This change uses the MBR disk signature to specify the root device when using Linux 3.8+ and CONFIG_BLOCK (required for root=PARTUUID=) is enabled in the kernel.

This has been tested with QEMU x86 and Intel Desktop Board D2500HN using an image recipe inheriting boot-directdisk and core-image.

---
 meta/classes/boot-directdisk.bbclass | 30 +++++++++++++++++++++++++++++-
 1 file changed, 29 insertions(+), 1 deletion(-)
meta/classes/boot-directdisk.bbclass +29 -1
index efeadab..f97060f 100644
@@ -35,7 +35,9 @@ BOOTDD_EXTRA_SPACE ?= "16384"
 # Get the build_syslinux_cfg() function from the syslinux class
 
 AUTO_SYSLINUXCFG = "1"
-SYSLINUX_ROOT ?= "root=/dev/sda2"
+SYSLINUX_FIXED_ROOT = "root=/dev/sda2"
+SYSLINUX_UUID_ROOT = "root=PARTUUID=${DISK_SIGNATURE}-02"
+SYSLINUX_ROOT ?= "${@'${SYSLINUX_UUID_ROOT}' if has_kernel_mbr_partuuid_support(d) else '${SYSLINUX_FIXED_ROOT}'}"
 SYSLINUX_TIMEOUT ?= "10"
 
 inherit syslinux
@@ -98,6 +100,10 @@ build_boot_dd() {
 	parted $IMAGE set 1 boot on 
 	parted $IMAGE print
 
+	# Disk signature generated by parted isn't really random, so use our own generated signature
+	echo -ne "$(echo ${DISK_SIGNATURE} | fold -w 2 | tac | paste -sd '' | sed 's/\(..\)/\\x&/g')" | \
+		dd of=$IMAGE bs=1 seek=440 conv=notrunc
+
 	OFFSET=`expr $END2 / 512`
 	dd if=${STAGING_DATADIR}/syslinux/mbr.bin of=$IMAGE conv=notrunc
 	dd if=$HDDIMG of=$IMAGE conv=notrunc seek=1 bs=512
@@ -113,4 +119,26 @@ python do_bootdirectdisk() {
     bb.build.exec_func('build_boot_dd', d)
 }
 
+def generate_disk_signature():
+    import uuid
+    return str(uuid.uuid4())[:8]
+
+def get_kernel_version(d):
+    import subprocess
+    version_cmd = r"grep '^VERSION\s*=' '%s/Makefile' | grep -o '[0-9]*$'" % (d.getVar("STAGING_KERNEL_DIR", True))
+    version = int(subprocess.Popen(version_cmd, shell=True, stdout=subprocess.PIPE).communicate()[0])
+    patchlevel_cmd = r"grep '^PATCHLEVEL\s*=' '%s/Makefile' | grep -o '[0-9]*$'" % (d.getVar("STAGING_KERNEL_DIR", True))
+    patchlevel = int(subprocess.Popen(patchlevel_cmd, shell=True, stdout=subprocess.PIPE).communicate()[0])
+    return (version, patchlevel)
+
+def has_kernel_config_option(option, d):
+    import subprocess
+    grep_cmd = r"grep '^CONFIG_%s=y$' '%s/.config'" % (option, d.getVar("STAGING_KERNEL_DIR", True))
+    return subprocess.call(grep_cmd, shell=True, stdout=subprocess.PIPE) == 0
+
+def has_kernel_mbr_partuuid_support(d):
+    return get_kernel_version(d) >= (3, 8) and has_kernel_config_option("BLOCK", d)
+
+DISK_SIGNATURE := "${@generate_disk_signature()}"
+
 addtask bootdirectdisk before do_build
Signature · 1 line
1.8.3.2
#1 lore raw
Quoted 28 lines · Jonathan Liu

The root device is currently set as /dev/hda2. However, this is only correct if it's the first IDE drive. If booting off the first SATA drive instead, it would be /dev/sda2. It's not the first drive, neither /dev/hda2 or /dev/sda2 would be correct.

The solution to this has typically been to use the filesystem UUID to specify the root device but this requires extra support in the initrd. Linux 3.8 introduces the ability to specify the root device using the MBR disk signature and the partition number which is much simpler to use and avoids the extra overhead of an initrd.

This change uses the MBR disk signature to specify the root device when using Linux 3.8+ and CONFIG_BLOCK (required for root=PARTUUID=) is enabled in the kernel.

This has been tested with QEMU x86 and Intel Desktop Board D2500HN using an image recipe inheriting boot-directdisk and core-image.

---
 meta/classes/boot-directdisk.bbclass | 30 +++++++++++++++++++++++++++++-
 1 file changed, 29 insertions(+), 1 deletion(-)
meta/classes/boot-directdisk.bbclass +0 -0
index efeadab..f97060f 100644

meta/classes/boot-directdisk.bbclass BOOTDD_EXTRA_SPACE ?= "16384" · line 39

Quoted 6 lines
meta/classes/boot-directdisk.bbclass +2 -1
@@ -35,7 +35,9 @@ BOOTDD_EXTRA_SPACE ?= "16384"
 # Get the build_syslinux_cfg() function from the syslinux class
 
 AUTO_SYSLINUXCFG = "1"
-SYSLINUX_ROOT ?= "root=/dev/sda2"
+SYSLINUX_FIXED_ROOT = "root=/dev/sda2"
+SYSLINUX_UUID_ROOT = "root=PARTUUID=${DISK_SIGNATURE}-02"

Why '=' and not '?=' for the above? Shouldn't the user be able to override these?

meta/classes/boot-directdisk.bbclass build_boot_dd() { · line 105

Quoted 11 lines
meta/classes/boot-directdisk.bbclass +4 -0
+SYSLINUX_ROOT ?= "${@'${SYSLINUX_UUID_ROOT}' if has_kernel_mbr_partuuid_support(d) else '${SYSLINUX_FIXED_ROOT}'}"
 SYSLINUX_TIMEOUT ?= "10"
 
 inherit syslinux
@@ -98,6 +100,10 @@ build_boot_dd() {
 	parted $IMAGE set 1 boot on 
 	parted $IMAGE print
 
+	# Disk signature generated by parted isn't really random, so use our own generated signature
+	echo -ne "$(echo ${DISK_SIGNATURE} | fold -w 2 | tac | paste -sd '' | sed 's/\(..\)/\\x&/g')" | \
+		dd of=$IMAGE bs=1 seek=440 conv=notrunc

Oi! So, can "fold -w 2 | tac | paste -sd '' be replaced with "rev" from the standard util-linux package?

And the sed command... just insert "\x" every two characters? That doesn't seem to add any more randomness.... maybe I'm missing the point here?

Quoted 14 lines
meta/classes/boot-directdisk.bbclass +7 -0
+
 	OFFSET=`expr $END2 / 512`
 	dd if=${STAGING_DATADIR}/syslinux/mbr.bin of=$IMAGE conv=notrunc
 	dd if=$HDDIMG of=$IMAGE conv=notrunc seek=1 bs=512
@@ -113,4 +119,26 @@ python do_bootdirectdisk() {
     bb.build.exec_func('build_boot_dd', d)
 }
 
+def generate_disk_signature():
+    import uuid
+    return str(uuid.uuid4())[:8]
+
+def get_kernel_version(d):
+    import subprocess

meta/classes/boot-directdisk.bbclass python do_bootdirectdisk() { · line 137

Quoted 10 lines
meta/classes/boot-directdisk.bbclass +10 -0
+    version_cmd = r"grep '^VERSION\s*=' '%s/Makefile' | grep -o '[0-9]*$'" % (d.getVar("STAGING_KERNEL_DIR", True))
+    version = int(subprocess.Popen(version_cmd, shell=True, stdout=subprocess.PIPE).communicate()[0])
+    patchlevel_cmd = r"grep '^PATCHLEVEL\s*=' '%s/Makefile' | grep -o '[0-9]*$'" % (d.getVar("STAGING_KERNEL_DIR", True))
+    patchlevel = int(subprocess.Popen(patchlevel_cmd, shell=True, stdout=subprocess.PIPE).communicate()[0])
+    return (version, patchlevel)
+
+def has_kernel_config_option(option, d):
+    import subprocess
+    grep_cmd = r"grep '^CONFIG_%s=y$' '%s/.config'" % (option, d.getVar("STAGING_KERNEL_DIR", True))
+    return subprocess.call(grep_cmd, shell=True, stdout=subprocess.PIPE) == 0

Is there a specific kernel option to test for the MBR disk signature support or is that inherent in the block drivers now? It would be nice if we could skip the kernel version check, that code looks dangerously fragile (no fault of yours).

Quoted 7 lines
meta/classes/boot-directdisk.bbclass +6 -0
+
+def has_kernel_mbr_partuuid_support(d):
+    return get_kernel_version(d) >= (3, 8) and has_kernel_config_option("BLOCK", d)
+
+DISK_SIGNATURE := "${@generate_disk_signature()}"
+
 addtask bootdirectdisk before do_build
Signature · 3 lines
Darren Hart
Intel Open Source Technology Center
Yocto Project - Technical Lead - Linux Kernel
#2 lore raw
Quoted 31 lines · Darren Hart
Quoted 30 lines · Jonathan Liu

The root device is currently set as /dev/hda2. However, this is only correct if it's the first IDE drive. If booting off the first SATA drive instead, it would be /dev/sda2. It's not the first drive, neither /dev/hda2 or /dev/sda2 would be correct.

The solution to this has typically been to use the filesystem UUID to specify the root device but this requires extra support in the initrd. Linux 3.8 introduces the ability to specify the root device using the MBR disk signature and the partition number which is much simpler to use and avoids the extra overhead of an initrd.

This change uses the MBR disk signature to specify the root device when using Linux 3.8+ and CONFIG_BLOCK (required for root=PARTUUID=) is enabled in the kernel.

This has been tested with QEMU x86 and Intel Desktop Board D2500HN using an image recipe inheriting boot-directdisk and core-image.

---
  meta/classes/boot-directdisk.bbclass | 30 +++++++++++++++++++++++++++++-
  1 file changed, 29 insertions(+), 1 deletion(-)
meta/classes/boot-directdisk.bbclass +0 -0
index efeadab..f97060f 100644
@@ -35,7 +35,9 @@ BOOTDD_EXTRA_SPACE ?= "16384"
  # Get the build_syslinux_cfg() function from the syslinux class
 
Quoted 6 lines

meta/classes/boot-directdisk.bbclass BOOTDD_EXTRA_SPACE ?= "16384" · line 39

Quoted 4 lines
meta/classes/boot-directdisk.bbclass +2 -1
  AUTO_SYSLINUXCFG = "1"
-SYSLINUX_ROOT ?= "root=/dev/sda2"
+SYSLINUX_FIXED_ROOT = "root=/dev/sda2"
+SYSLINUX_UUID_ROOT = "root=PARTUUID=${DISK_SIGNATURE}-02"

Why '=' and not '?=' for the above? Shouldn't the user be able to override these?

Ok.

Quoted 15 lines

meta/classes/boot-directdisk.bbclass build_boot_dd() { · line 105

Quoted 12 lines
meta/classes/boot-directdisk.bbclass +4 -0
+SYSLINUX_ROOT ?= "${@'${SYSLINUX_UUID_ROOT}' if has_kernel_mbr_partuuid_support(d) else '${SYSLINUX_FIXED_ROOT}'}"
  SYSLINUX_TIMEOUT ?= "10"
 
  inherit syslinux
@@ -98,6 +100,10 @@ build_boot_dd() {
  	parted $IMAGE set 1 boot on
  	parted $IMAGE print
 
+	# Disk signature generated by parted isn't really random, so use our own generated signature
+	echo -ne "$(echo ${DISK_SIGNATURE} | fold -w 2 | tac | paste -sd '' | sed 's/\(..\)/\\x&/g')" | \
+		dd of=$IMAGE bs=1 seek=440 conv=notrunc

Oi! So, can "fold -w 2 | tac | paste -sd '' be replaced with "rev" from the standard util-linux package?

Quoted 3 lines

And the sed command... just insert "\x" every two characters? That doesn't seem to add any more randomness.... maybe I'm missing the point here?

fold -w 2 | tac | paste -sd '' is to reverse the hex bytes. the sed command is to convert the hex bytes aabbccdd to \xaa\xbb\xcc\xdd and pass to echo -ne to convert the hex bytes to binary. I can remove fold -w 2 | tac | paste -sd '' and add rev after converting to binary.

Quoted 22 lines
Quoted 22 lines
meta/classes/boot-directdisk.bbclass +13 -0
+
  	OFFSET=`expr $END2 / 512`
  	dd if=${STAGING_DATADIR}/syslinux/mbr.bin of=$IMAGE conv=notrunc
  	dd if=$HDDIMG of=$IMAGE conv=notrunc seek=1 bs=512
@@ -113,4 +119,26 @@ python do_bootdirectdisk() {
      bb.build.exec_func('build_boot_dd', d)
  }
 
+def generate_disk_signature():
+    import uuid
+    return str(uuid.uuid4())[:8]
+
+def get_kernel_version(d):
+    import subprocess
+    version_cmd = r"grep '^VERSION\s*=' '%s/Makefile' | grep -o '[0-9]*$'" % (d.getVar("STAGING_KERNEL_DIR", True))
+    version = int(subprocess.Popen(version_cmd, shell=True, stdout=subprocess.PIPE).communicate()[0])
+    patchlevel_cmd = r"grep '^PATCHLEVEL\s*=' '%s/Makefile' | grep -o '[0-9]*$'" % (d.getVar("STAGING_KERNEL_DIR", True))
+    patchlevel = int(subprocess.Popen(patchlevel_cmd, shell=True, stdout=subprocess.PIPE).communicate()[0])
+    return (version, patchlevel)
+
Quoted 8 lines

meta/classes/boot-directdisk.bbclass python do_bootdirectdisk() { · line 137

Quoted 4 lines
meta/classes/boot-directdisk.bbclass +4 -0
+def has_kernel_config_option(option, d):
+    import subprocess
+    grep_cmd = r"grep '^CONFIG_%s=y$' '%s/.config'" % (option, d.getVar("STAGING_KERNEL_DIR", True))
+    return subprocess.call(grep_cmd, shell=True, stdout=subprocess.PIPE) == 0

Is there a specific kernel option to test for the MBR disk signature support or is that inherent in the block drivers now? It would be nice if we could skip the kernel version check, that code looks dangerously fragile (no fault of yours).

The commit which adds the support is https://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/commit/?id=d33b98fc82b0908e91fb05ae081acaed7323f9d2. https://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/tree/init/do_mounts.c?id=refs/tags/v3.8.13 indicates PARTUUID= support is only enabled with CONFIG_BLOCK. Alternatively, instead of checking kernel version I could just add an option to enable the PARTUUID support and add a comment in the class that you need Linux 3.8+ with CONFIG_BLOCK. It would be disabled by default in that case and if they are using an older kernel version with the feature manually backported, they can explicitly enable it.

Quoted 8 lines

meta/classes/boot-directdisk.bbclass python do_bootdirectdisk() { · line 144

Quoted 8 lines
meta/classes/boot-directdisk.bbclass +6 -0
+
+def has_kernel_mbr_partuuid_support(d):
+    return get_kernel_version(d) >= (3, 8) and has_kernel_config_option("BLOCK", d)
+
+DISK_SIGNATURE := "${@generate_disk_signature()}"
+
  addtask bootdirectdisk before do_build

Regards,
Jonathan

#3 lore raw
In reply to Darren Hart
Quoted 47 lines · Darren Hart
Quoted 28 lines · Jonathan Liu

The root device is currently set as /dev/hda2. However, this is only correct if it's the first IDE drive. If booting off the first SATA drive instead, it would be /dev/sda2. It's not the first drive, neither /dev/hda2 or /dev/sda2 would be correct.

The solution to this has typically been to use the filesystem UUID to specify the root device but this requires extra support in the initrd. Linux 3.8 introduces the ability to specify the root device using the MBR disk signature and the partition number which is much simpler to use and avoids the extra overhead of an initrd.

This change uses the MBR disk signature to specify the root device when using Linux 3.8+ and CONFIG_BLOCK (required for root=PARTUUID=) is enabled in the kernel.

This has been tested with QEMU x86 and Intel Desktop Board D2500HN using an image recipe inheriting boot-directdisk and core-image.

---
 meta/classes/boot-directdisk.bbclass | 30 +++++++++++++++++++++++++++++-
 1 file changed, 29 insertions(+), 1 deletion(-)
meta/classes/boot-directdisk.bbclass +0 -0
index efeadab..f97060f 100644

meta/classes/boot-directdisk.bbclass BOOTDD_EXTRA_SPACE ?= "16384" · line 39

Quoted 6 lines
meta/classes/boot-directdisk.bbclass +2 -1
@@ -35,7 +35,9 @@ BOOTDD_EXTRA_SPACE ?= "16384"
 # Get the build_syslinux_cfg() function from the syslinux class
 
 AUTO_SYSLINUXCFG = "1"
-SYSLINUX_ROOT ?= "root=/dev/sda2"
+SYSLINUX_FIXED_ROOT = "root=/dev/sda2"
+SYSLINUX_UUID_ROOT = "root=PARTUUID=${DISK_SIGNATURE}-02"

Why '=' and not '?=' for the above? Shouldn't the user be able to override these?

Quoted 8 lines
meta/classes/boot-directdisk.bbclass +1 -0
+SYSLINUX_ROOT ?= "${@'${SYSLINUX_UUID_ROOT}' if has_kernel_mbr_partuuid_support(d) else '${SYSLINUX_FIXED_ROOT}'}"
 SYSLINUX_TIMEOUT ?= "10"
 
 inherit syslinux
@@ -98,6 +100,10 @@ build_boot_dd() {
      parted $IMAGE set 1 boot on
      parted $IMAGE print
 
Quoted 6 lines

meta/classes/boot-directdisk.bbclass build_boot_dd() { · line 105

Quoted 3 lines
meta/classes/boot-directdisk.bbclass +3 -0
+     # Disk signature generated by parted isn't really random, so use our own generated signature
+     echo -ne "$(echo ${DISK_SIGNATURE} | fold -w 2 | tac | paste -sd '' | sed 's/\(..\)/\\x&/g')" | \
+             dd of=$IMAGE bs=1 seek=440 conv=notrunc

Oi! So, can "fold -w 2 | tac | paste -sd '' be replaced with "rev" from the standard util-linux package?

The rev command doesn't handle binary data correctly:

$ echo -ne '\xb8\xe1\x0e\x5d' | rev
rev: stdin: Invalid or incomplete multibyte or wide character
Quoted 4 lines

And the sed command... just insert "\x" every two characters? That doesn't seem to add any more randomness.... maybe I'm missing the point here?

That's to convert hex string to binary data by adding \x before each pair of hex digits and passing to echo -ne.

Quoted 23 lines
Quoted 23 lines
meta/classes/boot-directdisk.bbclass +14 -0
+
      OFFSET=`expr $END2 / 512`
      dd if=${STAGING_DATADIR}/syslinux/mbr.bin of=$IMAGE conv=notrunc
      dd if=$HDDIMG of=$IMAGE conv=notrunc seek=1 bs=512
@@ -113,4 +119,26 @@ python do_bootdirectdisk() {
     bb.build.exec_func('build_boot_dd', d)
 }
 
+def generate_disk_signature():
+    import uuid
+    return str(uuid.uuid4())[:8]
+
+def get_kernel_version(d):
+    import subprocess
+    version_cmd = r"grep '^VERSION\s*=' '%s/Makefile' | grep -o '[0-9]*$'" % (d.getVar("STAGING_KERNEL_DIR", True))
+    version = int(subprocess.Popen(version_cmd, shell=True, stdout=subprocess.PIPE).communicate()[0])
+    patchlevel_cmd = r"grep '^PATCHLEVEL\s*=' '%s/Makefile' | grep -o '[0-9]*$'" % (d.getVar("STAGING_KERNEL_DIR", True))
+    patchlevel = int(subprocess.Popen(patchlevel_cmd, shell=True, stdout=subprocess.PIPE).communicate()[0])
+    return (version, patchlevel)
+
+def has_kernel_config_option(option, d):
Quoted 8 lines

meta/classes/boot-directdisk.bbclass python do_bootdirectdisk() { · line 137

Quoted 3 lines
meta/classes/boot-directdisk.bbclass +3 -0
+    import subprocess
+    grep_cmd = r"grep '^CONFIG_%s=y$' '%s/.config'" % (option, d.getVar("STAGING_KERNEL_DIR", True))
+    return subprocess.call(grep_cmd, shell=True, stdout=subprocess.PIPE) == 0

Is there a specific kernel option to test for the MBR disk signature support or is that inherent in the block drivers now? It would be nice if we could skip the kernel version check, that code looks dangerously fragile (no fault of yours).

I have submitted a new patch that is simpler and more flexible. It does not rely on kernel checks.
This patch is no longer relevant now.

Quoted 14 lines

meta/classes/boot-directdisk.bbclass python do_bootdirectdisk() { · line 144

Quoted 8 lines
meta/classes/boot-directdisk.bbclass +6 -0
+
+def has_kernel_mbr_partuuid_support(d):
+    return get_kernel_version(d) >= (3, 8) and has_kernel_config_option("BLOCK", d)
+
+DISK_SIGNATURE := "${@generate_disk_signature()}"
+
 addtask bootdirectdisk before do_build

--
Darren Hart
Intel Open Source Technology Center
Yocto Project - Technical Lead - Linux Kernel

Regards,
Jonathan