.. | .. |
---|
17 | 17 | #include <linux/genhd.h> |
---|
18 | 18 | #include <linux/kernel.h> |
---|
19 | 19 | #include <linux/blkdev.h> |
---|
| 20 | +#include <linux/msdos_partition.h> |
---|
20 | 21 | #include <asm/unaligned.h> |
---|
21 | 22 | |
---|
22 | 23 | #include <scsi/scsicam.h> |
---|
23 | | - |
---|
24 | | - |
---|
25 | | -static int setsize(unsigned long capacity, unsigned int *cyls, unsigned int *hds, |
---|
26 | | - unsigned int *secs); |
---|
27 | 24 | |
---|
28 | 25 | /** |
---|
29 | 26 | * scsi_bios_ptable - Read PC partition table out of first sector of device. |
---|
.. | .. |
---|
35 | 32 | */ |
---|
36 | 33 | unsigned char *scsi_bios_ptable(struct block_device *dev) |
---|
37 | 34 | { |
---|
38 | | - unsigned char *res = kmalloc(66, GFP_KERNEL); |
---|
39 | | - if (res) { |
---|
40 | | - struct block_device *bdev = dev->bd_contains; |
---|
41 | | - Sector sect; |
---|
42 | | - void *data = read_dev_sector(bdev, 0, §); |
---|
43 | | - if (data) { |
---|
44 | | - memcpy(res, data + 0x1be, 66); |
---|
45 | | - put_dev_sector(sect); |
---|
46 | | - } else { |
---|
47 | | - kfree(res); |
---|
48 | | - res = NULL; |
---|
49 | | - } |
---|
50 | | - } |
---|
| 35 | + struct address_space *mapping = dev->bd_contains->bd_inode->i_mapping; |
---|
| 36 | + unsigned char *res = NULL; |
---|
| 37 | + struct page *page; |
---|
| 38 | + |
---|
| 39 | + page = read_mapping_page(mapping, 0, NULL); |
---|
| 40 | + if (IS_ERR(page)) |
---|
| 41 | + return NULL; |
---|
| 42 | + |
---|
| 43 | + if (!PageError(page)) |
---|
| 44 | + res = kmemdup(page_address(page) + 0x1be, 66, GFP_KERNEL); |
---|
| 45 | + put_page(page); |
---|
51 | 46 | return res; |
---|
52 | 47 | } |
---|
53 | 48 | EXPORT_SYMBOL(scsi_bios_ptable); |
---|
54 | 49 | |
---|
55 | 50 | /** |
---|
56 | | - * scsicam_bios_param - Determine geometry of a disk in cylinders/heads/sectors. |
---|
57 | | - * @bdev: which device |
---|
58 | | - * @capacity: size of the disk in sectors |
---|
59 | | - * @ip: return value: ip[0]=heads, ip[1]=sectors, ip[2]=cylinders |
---|
60 | | - * |
---|
61 | | - * Description : determine the BIOS mapping/geometry used for a drive in a |
---|
62 | | - * SCSI-CAM system, storing the results in ip as required |
---|
63 | | - * by the HDIO_GETGEO ioctl(). |
---|
64 | | - * |
---|
65 | | - * Returns : -1 on failure, 0 on success. |
---|
66 | | - */ |
---|
67 | | - |
---|
68 | | -int scsicam_bios_param(struct block_device *bdev, sector_t capacity, int *ip) |
---|
69 | | -{ |
---|
70 | | - unsigned char *p; |
---|
71 | | - u64 capacity64 = capacity; /* Suppress gcc warning */ |
---|
72 | | - int ret; |
---|
73 | | - |
---|
74 | | - p = scsi_bios_ptable(bdev); |
---|
75 | | - if (!p) |
---|
76 | | - return -1; |
---|
77 | | - |
---|
78 | | - /* try to infer mapping from partition table */ |
---|
79 | | - ret = scsi_partsize(p, (unsigned long)capacity, (unsigned int *)ip + 2, |
---|
80 | | - (unsigned int *)ip + 0, (unsigned int *)ip + 1); |
---|
81 | | - kfree(p); |
---|
82 | | - |
---|
83 | | - if (ret == -1 && capacity64 < (1ULL << 32)) { |
---|
84 | | - /* pick some standard mapping with at most 1024 cylinders, |
---|
85 | | - and at most 62 sectors per track - this works up to |
---|
86 | | - 7905 MB */ |
---|
87 | | - ret = setsize((unsigned long)capacity, (unsigned int *)ip + 2, |
---|
88 | | - (unsigned int *)ip + 0, (unsigned int *)ip + 1); |
---|
89 | | - } |
---|
90 | | - |
---|
91 | | - /* if something went wrong, then apparently we have to return |
---|
92 | | - a geometry with more than 1024 cylinders */ |
---|
93 | | - if (ret || ip[0] > 255 || ip[1] > 63) { |
---|
94 | | - if ((capacity >> 11) > 65534) { |
---|
95 | | - ip[0] = 255; |
---|
96 | | - ip[1] = 63; |
---|
97 | | - } else { |
---|
98 | | - ip[0] = 64; |
---|
99 | | - ip[1] = 32; |
---|
100 | | - } |
---|
101 | | - |
---|
102 | | - if (capacity > 65535*63*255) |
---|
103 | | - ip[2] = 65535; |
---|
104 | | - else |
---|
105 | | - ip[2] = (unsigned long)capacity / (ip[0] * ip[1]); |
---|
106 | | - } |
---|
107 | | - |
---|
108 | | - return 0; |
---|
109 | | -} |
---|
110 | | -EXPORT_SYMBOL(scsicam_bios_param); |
---|
111 | | - |
---|
112 | | -/** |
---|
113 | 51 | * scsi_partsize - Parse cylinders/heads/sectors from PC partition table |
---|
114 | | - * @buf: partition table, see scsi_bios_ptable() |
---|
| 52 | + * @bdev: block device to parse |
---|
115 | 53 | * @capacity: size of the disk in sectors |
---|
116 | | - * @cyls: put cylinders here |
---|
117 | | - * @hds: put heads here |
---|
118 | | - * @secs: put sectors here |
---|
| 54 | + * @geom: output in form of [hds, cylinders, sectors] |
---|
119 | 55 | * |
---|
120 | 56 | * Determine the BIOS mapping/geometry used to create the partition |
---|
121 | | - * table, storing the results in @cyls, @hds, and @secs |
---|
| 57 | + * table, storing the results in @geom. |
---|
122 | 58 | * |
---|
123 | | - * Returns: -1 on failure, 0 on success. |
---|
| 59 | + * Returns: %false on failure, %true on success. |
---|
124 | 60 | */ |
---|
125 | | - |
---|
126 | | -int scsi_partsize(unsigned char *buf, unsigned long capacity, |
---|
127 | | - unsigned int *cyls, unsigned int *hds, unsigned int *secs) |
---|
| 61 | +bool scsi_partsize(struct block_device *bdev, sector_t capacity, int geom[3]) |
---|
128 | 62 | { |
---|
129 | | - struct partition *p = (struct partition *)buf, *largest = NULL; |
---|
130 | | - int i, largest_cyl; |
---|
131 | 63 | int cyl, ext_cyl, end_head, end_cyl, end_sector; |
---|
132 | 64 | unsigned int logical_end, physical_end, ext_physical_end; |
---|
| 65 | + struct msdos_partition *p, *largest = NULL; |
---|
| 66 | + void *buf; |
---|
| 67 | + int ret = false; |
---|
133 | 68 | |
---|
| 69 | + buf = scsi_bios_ptable(bdev); |
---|
| 70 | + if (!buf) |
---|
| 71 | + return false; |
---|
134 | 72 | |
---|
135 | 73 | if (*(unsigned short *) (buf + 64) == 0xAA55) { |
---|
136 | | - for (largest_cyl = -1, i = 0; i < 4; ++i, ++p) { |
---|
| 74 | + int largest_cyl = -1, i; |
---|
| 75 | + |
---|
| 76 | + for (i = 0, p = buf; i < 4; i++, p++) { |
---|
137 | 77 | if (!p->sys_ind) |
---|
138 | 78 | continue; |
---|
139 | 79 | #ifdef DEBUG |
---|
.. | .. |
---|
153 | 93 | end_sector = largest->end_sector & 0x3f; |
---|
154 | 94 | |
---|
155 | 95 | if (end_head + 1 == 0 || end_sector == 0) |
---|
156 | | - return -1; |
---|
| 96 | + goto out_free_buf; |
---|
157 | 97 | |
---|
158 | 98 | #ifdef DEBUG |
---|
159 | 99 | printk("scsicam_bios_param : end at h = %d, c = %d, s = %d\n", |
---|
.. | .. |
---|
178 | 118 | ,logical_end, physical_end, ext_physical_end, ext_cyl); |
---|
179 | 119 | #endif |
---|
180 | 120 | |
---|
181 | | - if ((logical_end == physical_end) || |
---|
182 | | - (end_cyl == 1023 && ext_physical_end == logical_end)) { |
---|
183 | | - *secs = end_sector; |
---|
184 | | - *hds = end_head + 1; |
---|
185 | | - *cyls = capacity / ((end_head + 1) * end_sector); |
---|
186 | | - return 0; |
---|
| 121 | + if (logical_end == physical_end || |
---|
| 122 | + (end_cyl == 1023 && ext_physical_end == logical_end)) { |
---|
| 123 | + geom[0] = end_head + 1; |
---|
| 124 | + geom[1] = end_sector; |
---|
| 125 | + geom[2] = (unsigned long)capacity / |
---|
| 126 | + ((end_head + 1) * end_sector); |
---|
| 127 | + ret = true; |
---|
| 128 | + goto out_free_buf; |
---|
187 | 129 | } |
---|
188 | 130 | #ifdef DEBUG |
---|
189 | 131 | printk("scsicam_bios_param : logical (%u) != physical (%u)\n", |
---|
190 | 132 | logical_end, physical_end); |
---|
191 | 133 | #endif |
---|
192 | 134 | } |
---|
193 | | - return -1; |
---|
| 135 | + |
---|
| 136 | +out_free_buf: |
---|
| 137 | + kfree(buf); |
---|
| 138 | + return ret; |
---|
194 | 139 | } |
---|
195 | 140 | EXPORT_SYMBOL(scsi_partsize); |
---|
196 | 141 | |
---|
.. | .. |
---|
258 | 203 | *hds = (unsigned int) heads; |
---|
259 | 204 | return (rv); |
---|
260 | 205 | } |
---|
| 206 | + |
---|
| 207 | +/** |
---|
| 208 | + * scsicam_bios_param - Determine geometry of a disk in cylinders/heads/sectors. |
---|
| 209 | + * @bdev: which device |
---|
| 210 | + * @capacity: size of the disk in sectors |
---|
| 211 | + * @ip: return value: ip[0]=heads, ip[1]=sectors, ip[2]=cylinders |
---|
| 212 | + * |
---|
| 213 | + * Description : determine the BIOS mapping/geometry used for a drive in a |
---|
| 214 | + * SCSI-CAM system, storing the results in ip as required |
---|
| 215 | + * by the HDIO_GETGEO ioctl(). |
---|
| 216 | + * |
---|
| 217 | + * Returns : -1 on failure, 0 on success. |
---|
| 218 | + */ |
---|
| 219 | +int scsicam_bios_param(struct block_device *bdev, sector_t capacity, int *ip) |
---|
| 220 | +{ |
---|
| 221 | + u64 capacity64 = capacity; /* Suppress gcc warning */ |
---|
| 222 | + int ret = 0; |
---|
| 223 | + |
---|
| 224 | + /* try to infer mapping from partition table */ |
---|
| 225 | + if (scsi_partsize(bdev, capacity, ip)) |
---|
| 226 | + return 0; |
---|
| 227 | + |
---|
| 228 | + if (capacity64 < (1ULL << 32)) { |
---|
| 229 | + /* |
---|
| 230 | + * Pick some standard mapping with at most 1024 cylinders, and |
---|
| 231 | + * at most 62 sectors per track - this works up to 7905 MB. |
---|
| 232 | + */ |
---|
| 233 | + ret = setsize((unsigned long)capacity, (unsigned int *)ip + 2, |
---|
| 234 | + (unsigned int *)ip + 0, (unsigned int *)ip + 1); |
---|
| 235 | + } |
---|
| 236 | + |
---|
| 237 | + /* |
---|
| 238 | + * If something went wrong, then apparently we have to return a geometry |
---|
| 239 | + * with more than 1024 cylinders. |
---|
| 240 | + */ |
---|
| 241 | + if (ret || ip[0] > 255 || ip[1] > 63) { |
---|
| 242 | + if ((capacity >> 11) > 65534) { |
---|
| 243 | + ip[0] = 255; |
---|
| 244 | + ip[1] = 63; |
---|
| 245 | + } else { |
---|
| 246 | + ip[0] = 64; |
---|
| 247 | + ip[1] = 32; |
---|
| 248 | + } |
---|
| 249 | + |
---|
| 250 | + if (capacity > 65535*63*255) |
---|
| 251 | + ip[2] = 65535; |
---|
| 252 | + else |
---|
| 253 | + ip[2] = (unsigned long)capacity / (ip[0] * ip[1]); |
---|
| 254 | + } |
---|
| 255 | + |
---|
| 256 | + return 0; |
---|
| 257 | +} |
---|
| 258 | +EXPORT_SYMBOL(scsicam_bios_param); |
---|