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/pagemap.h>
21#include <linux/msdos_partition.h>
22#include <asm/unaligned.h>
23
24#include <scsi/scsicam.h>
25
26
27
28
29
30
31
32
33
34unsigned char *scsi_bios_ptable(struct block_device *dev)
35{
36 struct address_space *mapping = bdev_whole(dev)->bd_inode->i_mapping;
37 unsigned char *res = NULL;
38 struct page *page;
39
40 page = read_mapping_page(mapping, 0, NULL);
41 if (IS_ERR(page))
42 return NULL;
43
44 if (!PageError(page))
45 res = kmemdup(page_address(page) + 0x1be, 66, GFP_KERNEL);
46 put_page(page);
47 return res;
48}
49EXPORT_SYMBOL(scsi_bios_ptable);
50
51
52
53
54
55
56
57
58
59
60
61
62bool scsi_partsize(struct block_device *bdev, sector_t capacity, int geom[3])
63{
64 int cyl, ext_cyl, end_head, end_cyl, end_sector;
65 unsigned int logical_end, physical_end, ext_physical_end;
66 struct msdos_partition *p, *largest = NULL;
67 void *buf;
68 int ret = false;
69
70 buf = scsi_bios_ptable(bdev);
71 if (!buf)
72 return false;
73
74 if (*(unsigned short *) (buf + 64) == 0xAA55) {
75 int largest_cyl = -1, i;
76
77 for (i = 0, p = buf; i < 4; i++, p++) {
78 if (!p->sys_ind)
79 continue;
80#ifdef DEBUG
81 printk("scsicam_bios_param : partition %d has system \n",
82 i);
83#endif
84 cyl = p->cyl + ((p->sector & 0xc0) << 2);
85 if (cyl > largest_cyl) {
86 largest_cyl = cyl;
87 largest = p;
88 }
89 }
90 }
91 if (largest) {
92 end_cyl = largest->end_cyl + ((largest->end_sector & 0xc0) << 2);
93 end_head = largest->end_head;
94 end_sector = largest->end_sector & 0x3f;
95
96 if (end_head + 1 == 0 || end_sector == 0)
97 goto out_free_buf;
98
99#ifdef DEBUG
100 printk("scsicam_bios_param : end at h = %d, c = %d, s = %d\n",
101 end_head, end_cyl, end_sector);
102#endif
103
104 physical_end = end_cyl * (end_head + 1) * end_sector +
105 end_head * end_sector + end_sector;
106
107
108 logical_end = get_unaligned_le32(&largest->start_sect)
109 + get_unaligned_le32(&largest->nr_sects);
110
111
112 ext_cyl = (logical_end - (end_head * end_sector + end_sector))
113 / (end_head + 1) / end_sector;
114 ext_physical_end = ext_cyl * (end_head + 1) * end_sector +
115 end_head * end_sector + end_sector;
116
117#ifdef DEBUG
118 printk("scsicam_bios_param : logical_end=%d physical_end=%d ext_physical_end=%d ext_cyl=%d\n"
119 ,logical_end, physical_end, ext_physical_end, ext_cyl);
120#endif
121
122 if (logical_end == physical_end ||
123 (end_cyl == 1023 && ext_physical_end == logical_end)) {
124 geom[0] = end_head + 1;
125 geom[1] = end_sector;
126 geom[2] = (unsigned long)capacity /
127 ((end_head + 1) * end_sector);
128 ret = true;
129 goto out_free_buf;
130 }
131#ifdef DEBUG
132 printk("scsicam_bios_param : logical (%u) != physical (%u)\n",
133 logical_end, physical_end);
134#endif
135 }
136
137out_free_buf:
138 kfree(buf);
139 return ret;
140}
141EXPORT_SYMBOL(scsi_partsize);
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
176
177static int setsize(unsigned long capacity, unsigned int *cyls, unsigned int *hds,
178 unsigned int *secs)
179{
180 unsigned int rv = 0;
181 unsigned long heads, sectors, cylinders, temp;
182
183 cylinders = 1024L;
184 sectors = 62L;
185
186 temp = cylinders * sectors;
187 heads = capacity / temp;
188 if (capacity % temp) {
189 heads++;
190 temp = cylinders * heads;
191 sectors = capacity / temp;
192
193 if (capacity % temp) {
194 sectors++;
195 temp = heads * sectors;
196 cylinders = capacity / temp;
197 }
198 }
199 if (cylinders == 0)
200 rv = (unsigned) -1;
201
202 *cyls = (unsigned int) cylinders;
203 *secs = (unsigned int) sectors;
204 *hds = (unsigned int) heads;
205 return (rv);
206}
207
208
209
210
211
212
213
214
215
216
217
218
219
220int scsicam_bios_param(struct block_device *bdev, sector_t capacity, int *ip)
221{
222 u64 capacity64 = capacity;
223 int ret = 0;
224
225
226 if (scsi_partsize(bdev, capacity, ip))
227 return 0;
228
229 if (capacity64 < (1ULL << 32)) {
230
231
232
233
234 ret = setsize((unsigned long)capacity, (unsigned int *)ip + 2,
235 (unsigned int *)ip + 0, (unsigned int *)ip + 1);
236 }
237
238
239
240
241
242 if (ret || ip[0] > 255 || ip[1] > 63) {
243 if ((capacity >> 11) > 65534) {
244 ip[0] = 255;
245 ip[1] = 63;
246 } else {
247 ip[0] = 64;
248 ip[1] = 32;
249 }
250
251 if (capacity > 65535*63*255)
252 ip[2] = 65535;
253 else
254 ip[2] = (unsigned long)capacity / (ip[0] * ip[1]);
255 }
256
257 return 0;
258}
259EXPORT_SYMBOL(scsicam_bios_param);
260