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