1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * scsicam.c - SCSI CAM support functions, use for HDIO_GETGEO, etc.
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright 1993, 1994 Drew Eckhardt
6*4882a593Smuzhiyun * Visionary Computing
7*4882a593Smuzhiyun * (Unix and Linux consulting and custom programming)
8*4882a593Smuzhiyun * drew@Colorado.EDU
9*4882a593Smuzhiyun * +1 (303) 786-7975
10*4882a593Smuzhiyun *
11*4882a593Smuzhiyun * For more information, please consult the SCSI-CAM draft.
12*4882a593Smuzhiyun */
13*4882a593Smuzhiyun
14*4882a593Smuzhiyun #include <linux/module.h>
15*4882a593Smuzhiyun #include <linux/slab.h>
16*4882a593Smuzhiyun #include <linux/fs.h>
17*4882a593Smuzhiyun #include <linux/genhd.h>
18*4882a593Smuzhiyun #include <linux/kernel.h>
19*4882a593Smuzhiyun #include <linux/blkdev.h>
20*4882a593Smuzhiyun #include <linux/msdos_partition.h>
21*4882a593Smuzhiyun #include <asm/unaligned.h>
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun #include <scsi/scsicam.h>
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun /**
26*4882a593Smuzhiyun * scsi_bios_ptable - Read PC partition table out of first sector of device.
27*4882a593Smuzhiyun * @dev: from this device
28*4882a593Smuzhiyun *
29*4882a593Smuzhiyun * Description: Reads the first sector from the device and returns %0x42 bytes
30*4882a593Smuzhiyun * starting at offset %0x1be.
31*4882a593Smuzhiyun * Returns: partition table in kmalloc(GFP_KERNEL) memory, or NULL on error.
32*4882a593Smuzhiyun */
scsi_bios_ptable(struct block_device * dev)33*4882a593Smuzhiyun unsigned char *scsi_bios_ptable(struct block_device *dev)
34*4882a593Smuzhiyun {
35*4882a593Smuzhiyun struct address_space *mapping = dev->bd_contains->bd_inode->i_mapping;
36*4882a593Smuzhiyun unsigned char *res = NULL;
37*4882a593Smuzhiyun struct page *page;
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun page = read_mapping_page(mapping, 0, NULL);
40*4882a593Smuzhiyun if (IS_ERR(page))
41*4882a593Smuzhiyun return NULL;
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun if (!PageError(page))
44*4882a593Smuzhiyun res = kmemdup(page_address(page) + 0x1be, 66, GFP_KERNEL);
45*4882a593Smuzhiyun put_page(page);
46*4882a593Smuzhiyun return res;
47*4882a593Smuzhiyun }
48*4882a593Smuzhiyun EXPORT_SYMBOL(scsi_bios_ptable);
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun /**
51*4882a593Smuzhiyun * scsi_partsize - Parse cylinders/heads/sectors from PC partition table
52*4882a593Smuzhiyun * @bdev: block device to parse
53*4882a593Smuzhiyun * @capacity: size of the disk in sectors
54*4882a593Smuzhiyun * @geom: output in form of [hds, cylinders, sectors]
55*4882a593Smuzhiyun *
56*4882a593Smuzhiyun * Determine the BIOS mapping/geometry used to create the partition
57*4882a593Smuzhiyun * table, storing the results in @geom.
58*4882a593Smuzhiyun *
59*4882a593Smuzhiyun * Returns: %false on failure, %true on success.
60*4882a593Smuzhiyun */
scsi_partsize(struct block_device * bdev,sector_t capacity,int geom[3])61*4882a593Smuzhiyun bool scsi_partsize(struct block_device *bdev, sector_t capacity, int geom[3])
62*4882a593Smuzhiyun {
63*4882a593Smuzhiyun int cyl, ext_cyl, end_head, end_cyl, end_sector;
64*4882a593Smuzhiyun unsigned int logical_end, physical_end, ext_physical_end;
65*4882a593Smuzhiyun struct msdos_partition *p, *largest = NULL;
66*4882a593Smuzhiyun void *buf;
67*4882a593Smuzhiyun int ret = false;
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun buf = scsi_bios_ptable(bdev);
70*4882a593Smuzhiyun if (!buf)
71*4882a593Smuzhiyun return false;
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun if (*(unsigned short *) (buf + 64) == 0xAA55) {
74*4882a593Smuzhiyun int largest_cyl = -1, i;
75*4882a593Smuzhiyun
76*4882a593Smuzhiyun for (i = 0, p = buf; i < 4; i++, p++) {
77*4882a593Smuzhiyun if (!p->sys_ind)
78*4882a593Smuzhiyun continue;
79*4882a593Smuzhiyun #ifdef DEBUG
80*4882a593Smuzhiyun printk("scsicam_bios_param : partition %d has system \n",
81*4882a593Smuzhiyun i);
82*4882a593Smuzhiyun #endif
83*4882a593Smuzhiyun cyl = p->cyl + ((p->sector & 0xc0) << 2);
84*4882a593Smuzhiyun if (cyl > largest_cyl) {
85*4882a593Smuzhiyun largest_cyl = cyl;
86*4882a593Smuzhiyun largest = p;
87*4882a593Smuzhiyun }
88*4882a593Smuzhiyun }
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun if (largest) {
91*4882a593Smuzhiyun end_cyl = largest->end_cyl + ((largest->end_sector & 0xc0) << 2);
92*4882a593Smuzhiyun end_head = largest->end_head;
93*4882a593Smuzhiyun end_sector = largest->end_sector & 0x3f;
94*4882a593Smuzhiyun
95*4882a593Smuzhiyun if (end_head + 1 == 0 || end_sector == 0)
96*4882a593Smuzhiyun goto out_free_buf;
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun #ifdef DEBUG
99*4882a593Smuzhiyun printk("scsicam_bios_param : end at h = %d, c = %d, s = %d\n",
100*4882a593Smuzhiyun end_head, end_cyl, end_sector);
101*4882a593Smuzhiyun #endif
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun physical_end = end_cyl * (end_head + 1) * end_sector +
104*4882a593Smuzhiyun end_head * end_sector + end_sector;
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun /* This is the actual _sector_ number at the end */
107*4882a593Smuzhiyun logical_end = get_unaligned_le32(&largest->start_sect)
108*4882a593Smuzhiyun + get_unaligned_le32(&largest->nr_sects);
109*4882a593Smuzhiyun
110*4882a593Smuzhiyun /* This is for >1023 cylinders */
111*4882a593Smuzhiyun ext_cyl = (logical_end - (end_head * end_sector + end_sector))
112*4882a593Smuzhiyun / (end_head + 1) / end_sector;
113*4882a593Smuzhiyun ext_physical_end = ext_cyl * (end_head + 1) * end_sector +
114*4882a593Smuzhiyun end_head * end_sector + end_sector;
115*4882a593Smuzhiyun
116*4882a593Smuzhiyun #ifdef DEBUG
117*4882a593Smuzhiyun printk("scsicam_bios_param : logical_end=%d physical_end=%d ext_physical_end=%d ext_cyl=%d\n"
118*4882a593Smuzhiyun ,logical_end, physical_end, ext_physical_end, ext_cyl);
119*4882a593Smuzhiyun #endif
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun if (logical_end == physical_end ||
122*4882a593Smuzhiyun (end_cyl == 1023 && ext_physical_end == logical_end)) {
123*4882a593Smuzhiyun geom[0] = end_head + 1;
124*4882a593Smuzhiyun geom[1] = end_sector;
125*4882a593Smuzhiyun geom[2] = (unsigned long)capacity /
126*4882a593Smuzhiyun ((end_head + 1) * end_sector);
127*4882a593Smuzhiyun ret = true;
128*4882a593Smuzhiyun goto out_free_buf;
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun #ifdef DEBUG
131*4882a593Smuzhiyun printk("scsicam_bios_param : logical (%u) != physical (%u)\n",
132*4882a593Smuzhiyun logical_end, physical_end);
133*4882a593Smuzhiyun #endif
134*4882a593Smuzhiyun }
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun out_free_buf:
137*4882a593Smuzhiyun kfree(buf);
138*4882a593Smuzhiyun return ret;
139*4882a593Smuzhiyun }
140*4882a593Smuzhiyun EXPORT_SYMBOL(scsi_partsize);
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun /*
143*4882a593Smuzhiyun * Function : static int setsize(unsigned long capacity,unsigned int *cyls,
144*4882a593Smuzhiyun * unsigned int *hds, unsigned int *secs);
145*4882a593Smuzhiyun *
146*4882a593Smuzhiyun * Purpose : to determine a near-optimal int 0x13 mapping for a
147*4882a593Smuzhiyun * SCSI disk in terms of lost space of size capacity, storing
148*4882a593Smuzhiyun * the results in *cyls, *hds, and *secs.
149*4882a593Smuzhiyun *
150*4882a593Smuzhiyun * Returns : -1 on failure, 0 on success.
151*4882a593Smuzhiyun *
152*4882a593Smuzhiyun * Extracted from
153*4882a593Smuzhiyun *
154*4882a593Smuzhiyun * WORKING X3T9.2
155*4882a593Smuzhiyun * DRAFT 792D
156*4882a593Smuzhiyun * see http://www.t10.org/ftp/t10/drafts/cam/cam-r12b.pdf
157*4882a593Smuzhiyun *
158*4882a593Smuzhiyun * Revision 6
159*4882a593Smuzhiyun * 10-MAR-94
160*4882a593Smuzhiyun * Information technology -
161*4882a593Smuzhiyun * SCSI-2 Common access method
162*4882a593Smuzhiyun * transport and SCSI interface module
163*4882a593Smuzhiyun *
164*4882a593Smuzhiyun * ANNEX A :
165*4882a593Smuzhiyun *
166*4882a593Smuzhiyun * setsize() converts a read capacity value to int 13h
167*4882a593Smuzhiyun * head-cylinder-sector requirements. It minimizes the value for
168*4882a593Smuzhiyun * number of heads and maximizes the number of cylinders. This
169*4882a593Smuzhiyun * will support rather large disks before the number of heads
170*4882a593Smuzhiyun * will not fit in 4 bits (or 6 bits). This algorithm also
171*4882a593Smuzhiyun * minimizes the number of sectors that will be unused at the end
172*4882a593Smuzhiyun * of the disk while allowing for very large disks to be
173*4882a593Smuzhiyun * accommodated. This algorithm does not use physical geometry.
174*4882a593Smuzhiyun */
175*4882a593Smuzhiyun
setsize(unsigned long capacity,unsigned int * cyls,unsigned int * hds,unsigned int * secs)176*4882a593Smuzhiyun static int setsize(unsigned long capacity, unsigned int *cyls, unsigned int *hds,
177*4882a593Smuzhiyun unsigned int *secs)
178*4882a593Smuzhiyun {
179*4882a593Smuzhiyun unsigned int rv = 0;
180*4882a593Smuzhiyun unsigned long heads, sectors, cylinders, temp;
181*4882a593Smuzhiyun
182*4882a593Smuzhiyun cylinders = 1024L; /* Set number of cylinders to max */
183*4882a593Smuzhiyun sectors = 62L; /* Maximize sectors per track */
184*4882a593Smuzhiyun
185*4882a593Smuzhiyun temp = cylinders * sectors; /* Compute divisor for heads */
186*4882a593Smuzhiyun heads = capacity / temp; /* Compute value for number of heads */
187*4882a593Smuzhiyun if (capacity % temp) { /* If no remainder, done! */
188*4882a593Smuzhiyun heads++; /* Else, increment number of heads */
189*4882a593Smuzhiyun temp = cylinders * heads; /* Compute divisor for sectors */
190*4882a593Smuzhiyun sectors = capacity / temp; /* Compute value for sectors per
191*4882a593Smuzhiyun track */
192*4882a593Smuzhiyun if (capacity % temp) { /* If no remainder, done! */
193*4882a593Smuzhiyun sectors++; /* Else, increment number of sectors */
194*4882a593Smuzhiyun temp = heads * sectors; /* Compute divisor for cylinders */
195*4882a593Smuzhiyun cylinders = capacity / temp; /* Compute number of cylinders */
196*4882a593Smuzhiyun }
197*4882a593Smuzhiyun }
198*4882a593Smuzhiyun if (cylinders == 0)
199*4882a593Smuzhiyun rv = (unsigned) -1; /* Give error if 0 cylinders */
200*4882a593Smuzhiyun
201*4882a593Smuzhiyun *cyls = (unsigned int) cylinders; /* Stuff return values */
202*4882a593Smuzhiyun *secs = (unsigned int) sectors;
203*4882a593Smuzhiyun *hds = (unsigned int) heads;
204*4882a593Smuzhiyun return (rv);
205*4882a593Smuzhiyun }
206*4882a593Smuzhiyun
207*4882a593Smuzhiyun /**
208*4882a593Smuzhiyun * scsicam_bios_param - Determine geometry of a disk in cylinders/heads/sectors.
209*4882a593Smuzhiyun * @bdev: which device
210*4882a593Smuzhiyun * @capacity: size of the disk in sectors
211*4882a593Smuzhiyun * @ip: return value: ip[0]=heads, ip[1]=sectors, ip[2]=cylinders
212*4882a593Smuzhiyun *
213*4882a593Smuzhiyun * Description : determine the BIOS mapping/geometry used for a drive in a
214*4882a593Smuzhiyun * SCSI-CAM system, storing the results in ip as required
215*4882a593Smuzhiyun * by the HDIO_GETGEO ioctl().
216*4882a593Smuzhiyun *
217*4882a593Smuzhiyun * Returns : -1 on failure, 0 on success.
218*4882a593Smuzhiyun */
scsicam_bios_param(struct block_device * bdev,sector_t capacity,int * ip)219*4882a593Smuzhiyun int scsicam_bios_param(struct block_device *bdev, sector_t capacity, int *ip)
220*4882a593Smuzhiyun {
221*4882a593Smuzhiyun u64 capacity64 = capacity; /* Suppress gcc warning */
222*4882a593Smuzhiyun int ret = 0;
223*4882a593Smuzhiyun
224*4882a593Smuzhiyun /* try to infer mapping from partition table */
225*4882a593Smuzhiyun if (scsi_partsize(bdev, capacity, ip))
226*4882a593Smuzhiyun return 0;
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun if (capacity64 < (1ULL << 32)) {
229*4882a593Smuzhiyun /*
230*4882a593Smuzhiyun * Pick some standard mapping with at most 1024 cylinders, and
231*4882a593Smuzhiyun * at most 62 sectors per track - this works up to 7905 MB.
232*4882a593Smuzhiyun */
233*4882a593Smuzhiyun ret = setsize((unsigned long)capacity, (unsigned int *)ip + 2,
234*4882a593Smuzhiyun (unsigned int *)ip + 0, (unsigned int *)ip + 1);
235*4882a593Smuzhiyun }
236*4882a593Smuzhiyun
237*4882a593Smuzhiyun /*
238*4882a593Smuzhiyun * If something went wrong, then apparently we have to return a geometry
239*4882a593Smuzhiyun * with more than 1024 cylinders.
240*4882a593Smuzhiyun */
241*4882a593Smuzhiyun if (ret || ip[0] > 255 || ip[1] > 63) {
242*4882a593Smuzhiyun if ((capacity >> 11) > 65534) {
243*4882a593Smuzhiyun ip[0] = 255;
244*4882a593Smuzhiyun ip[1] = 63;
245*4882a593Smuzhiyun } else {
246*4882a593Smuzhiyun ip[0] = 64;
247*4882a593Smuzhiyun ip[1] = 32;
248*4882a593Smuzhiyun }
249*4882a593Smuzhiyun
250*4882a593Smuzhiyun if (capacity > 65535*63*255)
251*4882a593Smuzhiyun ip[2] = 65535;
252*4882a593Smuzhiyun else
253*4882a593Smuzhiyun ip[2] = (unsigned long)capacity / (ip[0] * ip[1]);
254*4882a593Smuzhiyun }
255*4882a593Smuzhiyun
256*4882a593Smuzhiyun return 0;
257*4882a593Smuzhiyun }
258*4882a593Smuzhiyun EXPORT_SYMBOL(scsicam_bios_param);
259