0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014 #include <linux/module.h>
0015 #include <linux/slab.h>
0016 #include <linux/fs.h>
0017 #include <linux/kernel.h>
0018 #include <linux/blkdev.h>
0019 #include <linux/pagemap.h>
0020 #include <linux/msdos_partition.h>
0021 #include <asm/unaligned.h>
0022
0023 #include <scsi/scsicam.h>
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033 unsigned char *scsi_bios_ptable(struct block_device *dev)
0034 {
0035 struct address_space *mapping = bdev_whole(dev)->bd_inode->i_mapping;
0036 unsigned char *res = NULL;
0037 struct folio *folio;
0038
0039 folio = read_mapping_folio(mapping, 0, NULL);
0040 if (IS_ERR(folio))
0041 return NULL;
0042
0043 res = kmemdup(folio_address(folio) + 0x1be, 66, GFP_KERNEL);
0044 folio_put(folio);
0045 return res;
0046 }
0047 EXPORT_SYMBOL(scsi_bios_ptable);
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060 bool scsi_partsize(struct block_device *bdev, sector_t capacity, int geom[3])
0061 {
0062 int cyl, ext_cyl, end_head, end_cyl, end_sector;
0063 unsigned int logical_end, physical_end, ext_physical_end;
0064 struct msdos_partition *p, *largest = NULL;
0065 void *buf;
0066 int ret = false;
0067
0068 buf = scsi_bios_ptable(bdev);
0069 if (!buf)
0070 return false;
0071
0072 if (*(unsigned short *) (buf + 64) == 0xAA55) {
0073 int largest_cyl = -1, i;
0074
0075 for (i = 0, p = buf; i < 4; i++, p++) {
0076 if (!p->sys_ind)
0077 continue;
0078 #ifdef DEBUG
0079 printk("scsicam_bios_param : partition %d has system \n",
0080 i);
0081 #endif
0082 cyl = p->cyl + ((p->sector & 0xc0) << 2);
0083 if (cyl > largest_cyl) {
0084 largest_cyl = cyl;
0085 largest = p;
0086 }
0087 }
0088 }
0089 if (largest) {
0090 end_cyl = largest->end_cyl + ((largest->end_sector & 0xc0) << 2);
0091 end_head = largest->end_head;
0092 end_sector = largest->end_sector & 0x3f;
0093
0094 if (end_head + 1 == 0 || end_sector == 0)
0095 goto out_free_buf;
0096
0097 #ifdef DEBUG
0098 printk("scsicam_bios_param : end at h = %d, c = %d, s = %d\n",
0099 end_head, end_cyl, end_sector);
0100 #endif
0101
0102 physical_end = end_cyl * (end_head + 1) * end_sector +
0103 end_head * end_sector + end_sector;
0104
0105
0106 logical_end = get_unaligned_le32(&largest->start_sect)
0107 + get_unaligned_le32(&largest->nr_sects);
0108
0109
0110 ext_cyl = (logical_end - (end_head * end_sector + end_sector))
0111 / (end_head + 1) / end_sector;
0112 ext_physical_end = ext_cyl * (end_head + 1) * end_sector +
0113 end_head * end_sector + end_sector;
0114
0115 #ifdef DEBUG
0116 printk("scsicam_bios_param : logical_end=%d physical_end=%d ext_physical_end=%d ext_cyl=%d\n"
0117 ,logical_end, physical_end, ext_physical_end, ext_cyl);
0118 #endif
0119
0120 if (logical_end == physical_end ||
0121 (end_cyl == 1023 && ext_physical_end == logical_end)) {
0122 geom[0] = end_head + 1;
0123 geom[1] = end_sector;
0124 geom[2] = (unsigned long)capacity /
0125 ((end_head + 1) * end_sector);
0126 ret = true;
0127 goto out_free_buf;
0128 }
0129 #ifdef DEBUG
0130 printk("scsicam_bios_param : logical (%u) != physical (%u)\n",
0131 logical_end, physical_end);
0132 #endif
0133 }
0134
0135 out_free_buf:
0136 kfree(buf);
0137 return ret;
0138 }
0139 EXPORT_SYMBOL(scsi_partsize);
0140
0141
0142
0143
0144
0145
0146
0147
0148
0149
0150
0151
0152
0153
0154
0155
0156
0157
0158
0159
0160
0161
0162
0163
0164
0165
0166
0167
0168
0169
0170
0171
0172
0173
0174
0175 static int setsize(unsigned long capacity, unsigned int *cyls, unsigned int *hds,
0176 unsigned int *secs)
0177 {
0178 unsigned int rv = 0;
0179 unsigned long heads, sectors, cylinders, temp;
0180
0181 cylinders = 1024L;
0182 sectors = 62L;
0183
0184 temp = cylinders * sectors;
0185 heads = capacity / temp;
0186 if (capacity % temp) {
0187 heads++;
0188 temp = cylinders * heads;
0189 sectors = capacity / temp;
0190
0191 if (capacity % temp) {
0192 sectors++;
0193 temp = heads * sectors;
0194 cylinders = capacity / temp;
0195 }
0196 }
0197 if (cylinders == 0)
0198 rv = (unsigned) -1;
0199
0200 *cyls = (unsigned int) cylinders;
0201 *secs = (unsigned int) sectors;
0202 *hds = (unsigned int) heads;
0203 return (rv);
0204 }
0205
0206
0207
0208
0209
0210
0211
0212
0213
0214
0215
0216
0217
0218 int scsicam_bios_param(struct block_device *bdev, sector_t capacity, int *ip)
0219 {
0220 u64 capacity64 = capacity;
0221 int ret = 0;
0222
0223
0224 if (scsi_partsize(bdev, capacity, ip))
0225 return 0;
0226
0227 if (capacity64 < (1ULL << 32)) {
0228
0229
0230
0231
0232 ret = setsize((unsigned long)capacity, (unsigned int *)ip + 2,
0233 (unsigned int *)ip + 0, (unsigned int *)ip + 1);
0234 }
0235
0236
0237
0238
0239
0240 if (ret || ip[0] > 255 || ip[1] > 63) {
0241 if ((capacity >> 11) > 65534) {
0242 ip[0] = 255;
0243 ip[1] = 63;
0244 } else {
0245 ip[0] = 64;
0246 ip[1] = 32;
0247 }
0248
0249 if (capacity > 65535*63*255)
0250 ip[2] = 65535;
0251 else
0252 ip[2] = (unsigned long)capacity / (ip[0] * ip[1]);
0253 }
0254
0255 return 0;
0256 }
0257 EXPORT_SYMBOL(scsicam_bios_param);