#rakulang: let’s dump the partition table

I have been playing around with microcontrollers and bare-metal on Raspberry Pi 3. I am interested in reading and writing SD cards. FAT32 can be pretty complicated, so I was looking for simpler solutions. I recently discovered, for example, that Pis can’t boot on partitions greater than 32G. The firmware doesn’t understand them.

Perhaps my own solution would take the form of just raw data, a la Forth, or perhaps it would take the form of a filesystem that was much shorter and easier to program.

Now I have a problem: if I want to boot off a Pi I need a FAT32 partition, and I can’t just write into that partition if I don’t understand the filesystem. So I need two partitions. The second one is to store the data using whatever format I choose. That way, as long as I can read and write SD cards, I don’t need to bother myself with FAT32.

I could just write to blocks that I know (or assume) are beyond the FAT32 system, but I decided to see if I can decipher the MBR partition table to see where I can safely store data.

I am going to concentrate on MBR, ignore GPT, and assume that there are no extended partitions. According to osdev, partition information is stored as follows:

Partition numberOffset
Partition 10x01BE (446)
Partition 20x01CE (462)
Partition 30x01DE (478)
Partition 40x01EE (494)

The meanings of each entry is as follows:

Element (offset)SizeDescription
0byteBoot indicator bit flag: 0 = no, 0x80 = bootable (or “active”)
1byteStarting head
26 bitsStarting sector (Bits 6-7 are the upper two bits for the Starting Cylinder field.)
310 bitsStarting Cylinder
4byteSystem ID
5byteEnding Head
66 bitsEnding Sector (Bits 6-7 are the upper two bits for the ending cylinder field)
710 bitsEnding Cylinder
8uint32_tRelative Sector (to start of partition — also equals the partition’s starting LBA value)
12uint32_tTotal Sectors in partition

Thankfully, it turns out we don’t need all that Cylinder, Head, Sector stuff. The last two elements tell us all we need to know in terms of wherabouts the blocks are for the partitions. Each block is of 512 bytes.

Knowing if the partition is bootable (element 0) is not particularly interesting from my perspective, but it is easy to deal with. Knowing the SID (aka Partition type aka System Id, element 4) is useful, because I can set my own partition type for my data.

An implementation for the partition is dump is only 34 lines of code:

#!/usr/bin/env perl6

# Reference:
# https://wiki.osdev.org/Partition_Table

my %ids = 0x00 => "Empty", 0x0b => "W95 FAT32", 0x0c => "W95 FAT32 (LBA)",
	0x82 => "Linux swap", 0x83 => "Linux", 0x93 => "Amoeba";

sub MAIN($filename) {
	say "Disk: $filename";
	say "NB: addesses given in (decimal) blocks of 512 bytes each";
	say "Part  Boot    Start      End     Size   Id Type";
	my $fh = IO::Handle.new(:path($filename.IO.path)).open;
	$fh.seek(0x01BE);

	my $entry;
	sub eat() { return $entry.shift; }
	for 1..4 {
		$entry = $fh.read(16);
		my $boo = eat;
		$boo = $boo == 0x80 ?? "*" !! " ";
		for 1..3 { eat; }
		my $sid = eat;
		for 1..3 { eat; }
		my $start = $entry.read-uint32(0, LittleEndian);
		last if $start == 0;
		my $size = $entry.read-uint32(4, LittleEndian);
		my $end = $start + $size -1 ;
		my $name = %ids{$sid} || "Unknown";
		printf "%d %8.8s %8d %8d %8d 0x%.2X %s\n", $_, $boo, $start,$end, $size,  $sid, $name;

	}
	$fh.close;

It is available as a gist. (Rant: WordPress seems to be getting worse by the day, and is particularly recalcitrant to block test. I seriously need to consider moving on to something else).

The code is pretty self-explanatory, I think.

Here’s a sample output:

Disk: fall.dat
 NB: addesses given in (decimal) blocks of 512 bytes each
 Part  Boot    Start      End     Size   Id Type
 1        *        1      200      200 0x93 Amoeba
 2               201      446      246 0x9F Unknown
 3               447      646      200 0x00 Empty

What’s with the Amoeba? Well, Amoeba is a distributed OS invented by Andy Tanenbaum. It was last modified in July 1996. I figured that I would repurpose that file ID.

The Raku code doesn’t work with raw devices, it works with actual files. To create such a file, e.g. from your hard drive, issue a command like:

sudo dd if=/dev/sda of=mbr.dat bs=512 count=1

That dumps your MBR to the file mbr.dat. You only need the fist block. Then run:

perl6 pd.pl6 mbr.dat

to print it out.

Alternatively, you can make your own “disk”. This may prove useful to me if I want to experiment with reading and writing. Creating a “disk” is easy enough:

fallocate -l 20M disk.dat

This preallocated space for a file. In this case, I chose 20M, although you can set it to whatever you want.

Now edit that file:

cfdisk disk.dat

You can pretend it is a hard drive, and create partitions for it. You can take it one step further and use mkfs to format the partitions you created. I did not do that, though.

Once you’ve created partitions, you can verify our raku utility:

perl6 pd.pl6 disk.dat

Pretty neat, huh.

Rant

Whilst looking through the partition table documentation of osdev (emphasis mine):

GPT is an updated Partition Table standard … It also contains enhancements to the concept of partition tables, in general, and is significantly more complex than the MBR scheme.

As someone who likes to play around with bare metal programming on microcontrollers and Raspberry Pis, I lament the fact just how inaccessible hardware is becoming. Huge software stacks are becoming increasingly necessary to get basic peripherals working. Take keyboards, for example. Formerly, they worked through dedicated ports with fairly easy to understand interfacing. Today we have USB keyboards. The USB standard is a large, complex beast, so getting a keyboard to work is a significant undertaking.

And don’t get me started on the hideous monstrosity that is the internet.

Spleen vented.

About mcturra2000

Computer programmer living in Scotland.
This entry was posted in Uncategorized. Bookmark the permalink.

3 Responses to #rakulang: let’s dump the partition table

  1. liztormato says:

    Most recent versions if Raku can actually be called with `raku` rather than `perl6` 🙂

  2. p6steve says:

    Great code example … I like the way you do pythonista indenting 🙂

  3. Pingback: 2020.48 DevRoom on FOSDEM – Rakudo Weekly News

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s