1
2
3
4
5
6
7
8
9
10
11
12
13
14#include <linux/module.h>
15#include <linux/slab.h>
16#include <linux/fs.h>
17#include <linux/genhd.h>
18#include <linux/kernel.h>
19#include <linux/blkdev.h>
20#include <linux/msdos_partition.h>
21#include <asm/unaligned.h>
22
23#include <scsi/scsicam.h>
24
25
26
27
28
29
30
31
32
33unsigned char *scsi_bios_ptable(struct block_device *dev)
34{
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);
46 return res;
47}
48EXPORT_SYMBOL(scsi_bios_ptable);
49
50
51
52
53
54
55
56
57
58
59
60
61bool scsi_partsize(struct block_device *bdev, sector_t capacity, int geom[3])
62{
63 int cyl, ext_cyl, end_head, end_cyl, end_sector;
64 unsigned int logical_end, physical_end, ext_physical_end;
65 struct msdos_partition *p, *largest = NULL;
66 void *buf;
67 int ret = false;
68
69 buf = scsi_bios_ptable(bdev);
70 if (!buf)
71 return false;
72
73 if (*(unsigned short *) (buf + 64) == 0xAA55) {
74 int largest_cyl = -1, i;
75
76 for (i = 0, p = buf; i < 4; i++, p++) {
77 if (!p->sys_ind)
78 continue;
79#ifdef DEBUG
80 printk("scsicam_bios_param : partition %d has system \n",
81 i);
82#endif
83 cyl = p->cyl + ((p->sector & 0xc0) << 2);
84 if (cyl > largest_cyl) {
85 largest_cyl = cyl;
86 largest = p;
87 }
88 }
89 }
90 if (largest) {
91 end_cyl = largest->end_cyl + ((largest->end_sector & 0xc0) << 2);
92 end_head = largest->end_head;
93 end_sector = largest->end_sector & 0x3f;
94
95 if (end_head + 1 == 0 || end_sector == 0)
96 goto out_free_buf;
97
98#ifdef DEBUG
99 printk("scsicam_bios_param : end at h = %d, c = %d, s = %d\n",
100 end_head, end_cyl, end_sector);
101#endif
102
103 physical_end = end_cyl * (end_head + 1) * end_sector +
104 end_head * end_sector + end_sector;
105
106
107 logical_end = get_unaligned_le32(&largest->start_sect)
108 + get_unaligned_le32(&largest->nr_sects);
109
110
111 ext_cyl = (logical_end - (end_head * end_sector + end_sector))
112 / (end_head + 1) / end_sector;
113 ext_physical_end = ext_cyl * (end_head + 1) * end_sector +
114 end_head * end_sector + end_sector;
115
116#ifdef DEBUG
117 printk("scsicam_bios_param : logical_end=%d physical_end=%d ext_physical_end=%d ext_cyl=%d\n"
118 ,logical_end, physical_end, ext_physical_end, ext_cyl);
119#endif
120
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;
129 }
130#ifdef DEBUG
131 printk("scsicam_bios_param : logical (%u) != physical (%u)\n",
132 logical_end, physical_end);
133#endif
134 }
135
136out_free_buf:
137 kfree(buf);
138 return ret;
139}
140EXPORT_SYMBOL(scsi_partsize);
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176static int setsize(unsigned long capacity, unsigned int *cyls, unsigned int *hds,
177 unsigned int *secs)
178{
179 unsigned int rv = 0;
180 unsigned long heads, sectors, cylinders, temp;
181
182 cylinders = 1024L;
183 sectors = 62L;
184
185 temp = cylinders * sectors;
186 heads = capacity / temp;
187 if (capacity % temp) {
188 heads++;
189 temp = cylinders * heads;
190 sectors = capacity / temp;
191
192 if (capacity % temp) {
193 sectors++;
194 temp = heads * sectors;
195 cylinders = capacity / temp;
196 }
197 }
198 if (cylinders == 0)
199 rv = (unsigned) -1;
200
201 *cyls = (unsigned int) cylinders;
202 *secs = (unsigned int) sectors;
203 *hds = (unsigned int) heads;
204 return (rv);
205}
206
207
208
209
210
211
212
213
214
215
216
217
218
219int scsicam_bios_param(struct block_device *bdev, sector_t capacity, int *ip)
220{
221 u64 capacity64 = capacity;
222 int ret = 0;
223
224
225 if (scsi_partsize(bdev, capacity, ip))
226 return 0;
227
228 if (capacity64 < (1ULL << 32)) {
229
230
231
232
233 ret = setsize((unsigned long)capacity, (unsigned int *)ip + 2,
234 (unsigned int *)ip + 0, (unsigned int *)ip + 1);
235 }
236
237
238
239
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}
258EXPORT_SYMBOL(scsicam_bios_param);
259