xref: /OK3568_Linux_fs/kernel/drivers/scsi/scsicam.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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