1 /*
2  * Copyright (c) 2009 Travis Geiselbrecht
3  *
4  * Use of this source code is governed by a MIT-style
5  * license that can be found in the LICENSE file or at
6  * https://opensource.org/licenses/MIT
7  */
8 #include <lk/debug.h>
9 #include <stdio.h>
10 #include <string.h>
11 #include <lk/compiler.h>
12 #include <stdlib.h>
13 #include <arch.h>
14 #include <lib/bio.h>
15 #include <lib/partition.h>
16 
17 struct chs {
18     uint8_t c;
19     uint8_t h;
20     uint8_t s;
21 } __PACKED;
22 
23 struct mbr_part {
24     uint8_t status;
25     struct chs start;
26     uint8_t type;
27     struct chs end;
28     uint32_t lba_start;
29     uint32_t lba_length;
30 } __PACKED;
31 
validate_mbr_partition(bdev_t * dev,const struct mbr_part * part)32 static status_t validate_mbr_partition(bdev_t *dev, const struct mbr_part *part) {
33     /* check for invalid types */
34     if (part->type == 0)
35         return -1;
36     /* check for invalid status */
37     if (part->status != 0x80 && part->status != 0x00)
38         return -1;
39 
40     /* make sure the range fits within the device */
41     if (part->lba_start >= dev->block_count)
42         return -1;
43     if ((part->lba_start + part->lba_length) > dev->block_count)
44         return -1;
45 
46     /* that's about all we can do, MBR has no other good way to see if it's valid */
47 
48     return 0;
49 }
50 
partition_publish(const char * device,off_t offset)51 int partition_publish(const char *device, off_t offset) {
52     int err = 0;
53     int count = 0;
54 
55     // clear any partitions that may have already existed
56     partition_unpublish(device);
57 
58     bdev_t *dev = bio_open(device);
59     if (!dev) {
60         printf("partition_publish: unable to open device\n");
61         return -1;
62     }
63 
64     // get a dma aligned and padded block to read info
65     STACKBUF_DMA_ALIGN(buf, dev->block_size);
66 
67     /* sniff for MBR partition types */
68     do {
69         int i;
70 
71         err = bio_read(dev, buf, offset, 512);
72         if (err < 0)
73             goto err;
74 
75         /* look for the aa55 tag */
76         if (buf[510] != 0x55 || buf[511] != 0xaa)
77             break;
78 
79         /* see if a partition table makes sense here */
80         struct mbr_part part[4];
81         memcpy(part, buf + 446, sizeof(part));
82 
83 #if LK_DEBUGLEVEL >= INFO
84         dprintf(INFO, "mbr partition table dump:\n");
85         for (i=0; i < 4; i++) {
86             dprintf(INFO, "\t%i: status 0x%hhx, type 0x%hhx, start 0x%x, len 0x%x\n", i, part[i].status, part[i].type, part[i].lba_start, part[i].lba_length);
87         }
88 #endif
89 
90         /* validate each of the partition entries */
91         for (i=0; i < 4; i++) {
92             if (validate_mbr_partition(dev, &part[i]) >= 0) {
93                 // publish it
94                 char subdevice[128];
95 
96                 sprintf(subdevice, "%sp%d", device, i);
97 
98                 err = bio_publish_subdevice(device, subdevice, part[i].lba_start, part[i].lba_length);
99                 if (err < 0) {
100                     dprintf(INFO, "error publishing subdevice '%s'\n", subdevice);
101                     continue;
102                 }
103                 count++;
104             }
105         }
106     } while (0);
107 
108     bio_close(dev);
109 
110 err:
111     return (err < 0) ? err : count;
112 }
113 
partition_unpublish(const char * device)114 int partition_unpublish(const char *device) {
115     int i;
116     int count;
117     bdev_t *dev;
118     char devname[512];
119 
120     count = 0;
121     for (i=0; i < 16; i++) {
122         sprintf(devname, "%sp%d", device, i);
123 
124         dev = bio_open(devname);
125         if (!dev)
126             continue;
127 
128         bio_unregister_device(dev);
129         bio_close(dev);
130         count++;
131     }
132 
133     return count;
134 }
135 
136