131 lines
3.9 KiB
Markdown
131 lines
3.9 KiB
Markdown
layout: post
|
|
title: "DITo - Drives"
|
|
subtitle: "Exploring the MBR"
|
|
tags: [osdev, filesystems]
|
|
|
|
Let's write a few drive drivers.
|
|
|
|
### Pure image
|
|
|
|
The simplest image we'd have to handle is a plain image file, such as
|
|
a floppy disk. There's only a single partition and a one-to-one mapping
|
|
from the image to the drive.
|
|
|
|
First of all, let's think of what data we'd need to store. The filename
|
|
might be nice to have, and probably a pointer to the opened file. Would
|
|
hate to lose that...
|
|
|
|
:::c
|
|
struct im_data
|
|
{
|
|
char *filename;
|
|
FILE *file;
|
|
};
|
|
|
|
The functions required for the driver would then make use of the
|
|
c standard library:
|
|
|
|
:::c
|
|
int im_open(drive_t *d, int flags)
|
|
{
|
|
struct im_data *data = d->data;
|
|
data->file = fopen(data->filename, flags);
|
|
return 1;
|
|
}
|
|
int im_close(drive_t *d, int flags)
|
|
{
|
|
struct im_data *data = d->data;
|
|
fclose(data->file);
|
|
return 1;
|
|
}
|
|
int im_read(drive_t *d, void *buffer, size_t length, off_t offset)
|
|
{
|
|
struct im_data *data = d->data;
|
|
fseek(data->file, offset, SEEK_SET);
|
|
return fread(buffer, length, 1, data->file);
|
|
}
|
|
int im_write(drive_t *d, void *buffer, size_t length, off_t offset)
|
|
{
|
|
struct im_data *data = d->data;
|
|
fseek(data->file, offset, SEEK_SET);
|
|
return fwrite(buffer, length, 1, data->file);
|
|
}
|
|
|
|
The function for setting up the drive is equally simple:
|
|
|
|
:::c
|
|
drive_driver_t im_driver =
|
|
{
|
|
im_open,
|
|
im_close,
|
|
im_read,
|
|
im_write,
|
|
};
|
|
|
|
drive_t *image_drive(const char *filename)
|
|
{
|
|
drive_t *d = calloc(1, sizeof(drive_t));
|
|
struct im_data *data = calloc(1, sizeof(struct im_data));
|
|
|
|
d->driver = &im_driver;
|
|
d->data = data;
|
|
data->filename = strndup(filename, FILENAME_MAX_LENGTH);
|
|
|
|
return d;
|
|
}
|
|
|
|
As always, I've stripped away all sanity checking and error handling.
|
|
|
|
###MBR formated disks
|
|
|
|
In order to use a harddrive for different things (like file storage or
|
|
swap space), it can be split into several partitions. One standard for
|
|
doing this is through a table in the MBR or Master Boot Record of the
|
|
disk.
|
|
|
|
The MBR starts with 436 bytes of space for a bootloader, then there's a
|
|
10 byte disk ID followed by 4 partition table entries.
|
|
|
|
The entries of the table has the following structure:
|
|
|
|
:::c
|
|
struct MBR
|
|
{
|
|
uint8_t boot_indicator;
|
|
uint8_t start_chs[3];
|
|
uint8_t system_id;
|
|
uint8_t end_chs[3];
|
|
uint32_t start_lba;
|
|
uint32_t num_sectors;
|
|
}__attribute__((packed));
|
|
|
|
_CHS_ and _LBA_ are different ways of addressing sectors of the disk.
|
|
|
|
CHS - or Cylinder Sector Head - addresses the sectors by their physical
|
|
location on the disk. The format in this table is first 8 bytes for
|
|
the wanted head (two heads per harddrive platter), then 6 bits for the
|
|
sector (angle on the platter) and finally 10 bits for the cylinder
|
|
(distance from platter center). This method requires knowledge of the
|
|
actual physical structure of the drive and maxes out at around 8 GB with
|
|
512 byte sectors.
|
|
|
|
LBA - or Logical Block Addressing - leaves all that to the drive and
|
|
addresses the sectors by a single number. With 512 byte sectors, this is
|
|
useful up to about 2 GB.
|
|
|
|
For our purposes, the fields `start_lba` and `num_sectors` are all we
|
|
need (and `system_id` to check if the partition table entry is used).
|
|
|
|
###MBR driver
|
|
|
|
The driver for reading from a partition is pretty alike the plain image
|
|
driver. Principally, the only difference is that 512 times `start_lba`
|
|
has to be added to the offset for reading and writing, and that care
|
|
must be taken not to allow reading or writing outside of the partition
|
|
boundaries.
|
|
|
|
I even wrote the driver to piggyback on a different driver. I.e. the
|
|
function for creating the drive object requires a drive object as
|
|
argument (as in the [example in the previous
|
|
post](/blog/2014/04/Dito-Framework/)).
|