sos-code-article10/drivers/part.c

217 lines
5.6 KiB
C

/* Copyright (C) 2005 Thomas Petazzoni
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.
*/
#include <sos/kmalloc.h>
#include <sos/blkdev.h>
#include <drivers/bochs.h>
/**
* This structure defines the structure of a partition entry
* (determined by the PC architecture)
*/
typedef struct partition_entry
{
sos_ui8_t active;
sos_ui8_t start_dl;
sos_ui16_t start_cylinder;
sos_ui8_t type;
sos_ui8_t end_dl;
sos_ui16_t end_cylinder;
sos_ui32_t lba;
sos_ui32_t size;
} partition_entry_t;
/**
* Offset of the partition table inside the 512-byte sector.
*/
#define PART_TABLE_OFFSET 446
/**
* The most common partition types. For a complete list, you can for
* example refer to
* http://www.win.tue.nl/~aeb/partitions/partition_types-1.html
*/
#define PART_TYPE_EXTENDED 0x5
#define PART_TYPE_FAT16_1 0xe
#define PART_TYPE_FAT16_2 0x6
#define PART_TYPE_FAT32_1 0xb
#define PART_TYPE_FAT32_2 0xc
#define PART_TYPE_LINUX_SWAP 0x82
#define PART_TYPE_LINUX 0x83
/**
* Converts a partition type to a string
*/
static const char *
sos_part_type_str (unsigned int type)
{
switch (type)
{
case PART_TYPE_EXTENDED:
return "Extended";
case PART_TYPE_FAT16_1:
case PART_TYPE_FAT16_2:
return "FAT16";
case PART_TYPE_FAT32_1:
case PART_TYPE_FAT32_2:
return "FAT32";
case PART_TYPE_LINUX_SWAP:
return "Linux Swap";
case PART_TYPE_LINUX:
return "Linux";
default:
return "Unknown";
}
}
/**
* Detect the partitions on the given device, and registers them to
* the block device infrastructure.
*/
sos_ret_t
sos_part_detect (sos_ui32_t disk_class, sos_ui32_t disk_instance,
sos_size_t block_size, const char *name)
{
sos_vaddr_t buffer;
struct sos_blockdev_instance *blkdev;
struct partition_entry *part_entry;
unsigned int extstart = 0, extsup = 0;
sos_ret_t ret;
sos_size_t size = block_size;
unsigned int partnum;
blkdev = sos_blockdev_ref_instance (disk_class, disk_instance);
if (blkdev == NULL)
return -SOS_ENOENT;
buffer = (sos_vaddr_t) sos_kmalloc (block_size, 0);
if (buffer == 0)
{
sos_blockdev_release_instance (blkdev);
return -SOS_ENOMEM;
}
ret = sos_blockdev_kernel_read (blkdev, 0, buffer, & size);
if (ret != SOS_OK)
{
sos_blockdev_release_instance (blkdev);
sos_kfree (buffer);
return ret;
}
if (size != block_size)
{
sos_blockdev_release_instance (blkdev);
sos_kfree (buffer);
return -SOS_EIO;
}
part_entry = (struct partition_entry *) (buffer + PART_TABLE_OFFSET);
/* Handle primary partitions */
for (partnum = 0; partnum < 4; partnum++)
{
if (part_entry [partnum].size == 0)
continue;
sos_bochs_printf ("%s%d (%lu Mb, %s) ", name, partnum+1,
(part_entry[partnum].size * block_size >> 20),
sos_part_type_str(part_entry[partnum].type));
if (part_entry [partnum].type == PART_TYPE_EXTENDED)
{
if (extstart != 0)
sos_bochs_printf ("Warning: two extended partitions detected\n");
extstart = part_entry [partnum].lba;
continue;
}
ret = sos_blockdev_register_partition (disk_class,
disk_instance + partnum + 1,
blkdev,
part_entry[partnum].lba,
part_entry[partnum].size, NULL);
if (ret != SOS_OK)
{
sos_bochs_printf ("Could not register partition %d for disk %lu:%lu: error %d\n",
partnum, disk_class, disk_instance, ret);
continue;
}
}
while (extstart != 0 && partnum < 15)
{
ret = sos_blockdev_kernel_read (blkdev, (extstart + extsup) * block_size,
(sos_luoffset_t) buffer, & size);
if (ret != SOS_OK)
{
sos_blockdev_release_instance (blkdev);
sos_kfree (buffer);
return ret;
}
if (size != block_size)
{
sos_blockdev_release_instance (blkdev);
sos_kfree (buffer);
return -SOS_EIO;
}
sos_bochs_printf ("%s%d (%lu Mb, %s) ", name, partnum+1,
(part_entry[0].size * block_size >> 20),
sos_part_type_str(part_entry[0].type));
ret = sos_blockdev_register_partition (disk_class,
disk_instance + partnum + 1,
blkdev,
extstart + part_entry[0].lba,
part_entry[0].size, NULL);
if (ret != SOS_OK)
sos_bochs_printf ("Could not register partition %d for disk %lu:%lu: error %d\n",
partnum, disk_class, disk_instance, ret);
extsup = part_entry[1].lba;
partnum ++;
if (extsup == 0)
break;
}
sos_blockdev_release_instance (blkdev);
sos_kfree (buffer);
return SOS_OK;
}
/**
* Remove all detected partitions on given device
*/
sos_ret_t
sos_part_undetect (sos_ui32_t disk_class, sos_ui32_t disk_instance)
{
int partnum;
sos_ret_t ret;
for (partnum = 0; partnum < 15; partnum++)
{
ret = sos_blockdev_unregister_device (disk_class, disk_instance + partnum + 1);
if (ret != SOS_OK)
sos_bochs_printf ("Warning: could not unregister partition %d of device %lu:%lu\n",
partnum, disk_class, disk_instance);
}
return SOS_OK;
}