217 lines
5.6 KiB
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;
|
||
|
}
|