summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorToomas Soome <tsoome@me.com>2016-11-21 00:29:06 +0200
committerDan McDonald <danmcd@omniti.com>2017-01-23 13:59:29 -0500
commit252244c3410eebc24658f285a920786b16c56862 (patch)
tree37d098d32a59029be6cb1a1d8b4b40b5da9adebb /usr/src
parent1ab55c5b11c6901ca810d93fdd657fb57ca800f1 (diff)
downloadillumos-joyent-252244c3410eebc24658f285a920786b16c56862.tar.gz
7598 loader: Implement disk_ioctl() to support DIOCGSECTORSIZE and DIOCGMEDIASIZE.
Reviewed by: Robert Mustacchi <rm@joyent.com> Reviewed by: Juraj Lutter <juraj@lutter.sk> Approved by: Dan McDonald <danmcd@omniti.com>
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/boot/sys/boot/common/disk.c45
-rw-r--r--usr/src/boot/sys/boot/common/part.c33
-rw-r--r--usr/src/boot/sys/boot/common/part.h1
3 files changed, 71 insertions, 8 deletions
diff --git a/usr/src/boot/sys/boot/common/disk.c b/usr/src/boot/sys/boot/common/disk.c
index bad0a700c4..088013b0c6 100644
--- a/usr/src/boot/sys/boot/common/disk.c
+++ b/usr/src/boot/sys/boot/common/disk.c
@@ -45,7 +45,8 @@ __FBSDID("$FreeBSD$");
struct open_disk {
struct ptable *table;
- off_t mediasize;
+ uint64_t mediasize;
+ uint64_t entrysize;
u_int sectorsize;
u_int flags;
int rcnt;
@@ -64,7 +65,7 @@ struct dentry {
int d_partition;
struct open_disk *od;
- off_t d_offset;
+ uint64_t d_offset;
STAILQ_ENTRY(dentry) entry;
#ifdef DISK_DEBUG
uint32_t count;
@@ -263,13 +264,28 @@ disk_write(struct disk_devdesc *dev, void *buf, uint64_t offset, u_int blocks)
}
int
-disk_ioctl(struct disk_devdesc *dev, u_long cmd, void *buf)
+disk_ioctl(struct disk_devdesc *dev, u_long cmd, void *data)
{
+ struct open_disk *od = dev->d_opendata;
+
+ if (od == NULL)
+ return (ENOTTY);
+
+ switch (cmd) {
+ case DIOCGSECTORSIZE:
+ *(u_int *)data = od->sectorsize;
+ break;
+ case DIOCGMEDIASIZE:
+ if (dev->d_offset == 0)
+ *(uint64_t *)data = od->mediasize;
+ else
+ *(uint64_t *)data = od->entrysize * od->sectorsize;
+ break;
+ default:
+ return (ENOTTY);
+ }
- if (dev->d_dev->dv_ioctl)
- return ((*dev->d_dev->dv_ioctl)(dev->d_opendata, cmd, buf));
-
- return (ENXIO);
+ return (0);
}
int
@@ -314,6 +330,7 @@ disk_open(struct disk_devdesc *dev, uint64_t mediasize, u_int sectorsize,
}
dev->d_opendata = od;
od->rcnt = 0;
+ od->entrysize = 0;
}
od->mediasize = mediasize;
od->sectorsize = sectorsize;
@@ -329,14 +346,24 @@ disk_open(struct disk_devdesc *dev, uint64_t mediasize, u_int sectorsize,
rc = ENXIO;
goto out;
}
+
+ if (ptable_getsize(od->table, &mediasize) != 0) {
+ rc = ENXIO;
+ goto out;
+ }
+ if (mediasize > od->mediasize) {
+ od->mediasize = mediasize;
+ }
opened:
rc = 0;
if (ptable_gettype(od->table) == PTABLE_BSD &&
partition >= 0) {
/* It doesn't matter what value has d_slice */
rc = ptable_getpart(od->table, &part, partition);
- if (rc == 0)
+ if (rc == 0) {
dev->d_offset = part.start;
+ od->entrysize = part.end - part.start + 1;
+ }
} else if (slice >= 0) {
/* Try to get information about partition */
if (slice == 0)
@@ -346,6 +373,7 @@ opened:
if (rc != 0) /* Partition doesn't exist */
goto out;
dev->d_offset = part.start;
+ od->entrysize = part.end - part.start + 1;
slice = part.index;
if (ptable_gettype(od->table) == PTABLE_GPT) {
partition = 255;
@@ -389,6 +417,7 @@ opened:
if (rc != 0)
goto out;
dev->d_offset += part.start;
+ od->entrysize = part.end - part.start + 1;
}
out:
if (table != NULL)
diff --git a/usr/src/boot/sys/boot/common/part.c b/usr/src/boot/sys/boot/common/part.c
index 800641bd86..4539c9c4b7 100644
--- a/usr/src/boot/sys/boot/common/part.c
+++ b/usr/src/boot/sys/boot/common/part.c
@@ -330,10 +330,30 @@ ptable_gptread(struct ptable *table, void *dev, diskread_t dread)
DEBUG("GPT detected");
size = MIN(hdr.hdr_entries * hdr.hdr_entsz,
MAXTBLSZ * table->sectorsize);
+
+ /*
+ * If the disk's sector count is smaller than the sector count recorded
+ * in the disk's GPT table header, set the table->sectors to the value
+ * recorded in GPT tables. This is done to work around buggy firmware
+ * that returns truncated disk sizes.
+ *
+ * Note, this is still not a foolproof way to get disk's size. For
+ * example, an image file can be truncated when copied to smaller media.
+ */
+ if (hdr.hdr_lba_alt + 1 > table->sectors)
+ table->sectors = hdr.hdr_lba_alt + 1;
+
for (i = 0; i < size / hdr.hdr_entsz; i++) {
ent = (struct gpt_ent *)(tbl + i * hdr.hdr_entsz);
if (uuid_equal(&ent->ent_type, &gpt_uuid_unused, NULL))
continue;
+
+ /* Simple sanity checks. */
+ if (ent->ent_lba_start < hdr.hdr_lba_start ||
+ ent->ent_lba_end > hdr.hdr_lba_end ||
+ ent->ent_lba_start > ent->ent_lba_end)
+ continue;
+
entry = malloc(sizeof(*entry));
if (entry == NULL)
break;
@@ -846,6 +866,19 @@ ptable_gettype(const struct ptable *table)
}
int
+ptable_getsize(const struct ptable *table, uint64_t *sizep)
+{
+ uint64_t tmp = table->sectors * table->sectorsize;
+
+ if (tmp < table->sectors)
+ return (EOVERFLOW);
+
+ if (sizep != NULL)
+ *sizep = tmp;
+ return (0);
+}
+
+int
ptable_getpart(const struct ptable *table, struct ptable_entry *part, int idx)
{
struct pentry *entry;
diff --git a/usr/src/boot/sys/boot/common/part.h b/usr/src/boot/sys/boot/common/part.h
index 1dc83815b9..a554016d92 100644
--- a/usr/src/boot/sys/boot/common/part.h
+++ b/usr/src/boot/sys/boot/common/part.h
@@ -83,6 +83,7 @@ struct ptable *ptable_open(void *dev, uint64_t sectors, uint16_t sectorsize,
diskread_t *dread);
void ptable_close(struct ptable *table);
enum ptable_type ptable_gettype(const struct ptable *table);
+int ptable_getsize(const struct ptable *table, uint64_t *sizep);
int ptable_getpart(const struct ptable *table, struct ptable_entry *part,
int index);