156083Skris// SPDX-License-Identifier: GPL-2.0 257511Speter/* 356083Skris * scsicam.c - SCSI CAM support functions, use for HDIO_GETGEO, etc. 456083Skris * 556083Skris * Copyright 1993, 1994 Drew Eckhardt 656083Skris * Visionary Computing 756083Skris * (Unix and Linux consulting and custom programming) 856083Skris * drew@Colorado.EDU 956083Skris * +1 (303) 786-7975 1056083Skris * 1156083Skris * For more information, please consult the SCSI-CAM draft. 1256083Skris */ 1356083Skris 1456083Skris#include <linux/module.h> 1556083Skris#include <linux/slab.h> 1656083Skris#include <linux/fs.h> 1756083Skris#include <linux/kernel.h> 1856083Skris#include <linux/blkdev.h> 1956083Skris#include <linux/pagemap.h> 2056083Skris#include <linux/msdos_partition.h> 2156083Skris#include <asm/unaligned.h> 2256083Skris 2356083Skris#include <scsi/scsicam.h> 2456083Skris 2556083Skris/** 2656083Skris * scsi_bios_ptable - Read PC partition table out of first sector of device. 2756083Skris * @dev: from this device 2856083Skris * 2956083Skris * Description: Reads the first sector from the device and returns %0x42 bytes 3056083Skris * starting at offset %0x1be. 3156083Skris * Returns: partition table in kmalloc(GFP_KERNEL) memory, or NULL on error. 3256083Skris */ 3356083Skrisunsigned char *scsi_bios_ptable(struct block_device *dev) 3456083Skris{ 3556083Skris struct address_space *mapping = bdev_whole(dev)->bd_inode->i_mapping; 3656083Skris unsigned char *res = NULL; 3756083Skris struct folio *folio; 3856083Skris 3956083Skris folio = read_mapping_folio(mapping, 0, NULL); 4056083Skris if (IS_ERR(folio)) 4156083Skris return NULL; 4256083Skris 4356083Skris res = kmemdup(folio_address(folio) + 0x1be, 66, GFP_KERNEL); 4456083Skris folio_put(folio); 4556083Skris return res; 4656083Skris} 4756083SkrisEXPORT_SYMBOL(scsi_bios_ptable); 4856083Skris 4956083Skris/** 5056083Skris * scsi_partsize - Parse cylinders/heads/sectors from PC partition table 5156083Skris * @bdev: block device to parse 5256083Skris * @capacity: size of the disk in sectors 5356083Skris * @geom: output in form of [hds, cylinders, sectors] 5456083Skris * 5556083Skris * Determine the BIOS mapping/geometry used to create the partition 5656083Skris * table, storing the results in @geom. 5756083Skris * 5856083Skris * Returns: %false on failure, %true on success. 5956083Skris */ 6056083Skrisbool scsi_partsize(struct block_device *bdev, sector_t capacity, int geom[3]) 6156083Skris{ 6256083Skris int cyl, ext_cyl, end_head, end_cyl, end_sector; 6368654Skris unsigned int logical_end, physical_end, ext_physical_end; 6468654Skris struct msdos_partition *p, *largest = NULL; 6556083Skris void *buf; 6656083Skris int ret = false; 6756083Skris 6856083Skris buf = scsi_bios_ptable(bdev); 6956083Skris if (!buf) 7056083Skris return false; 7156083Skris 7256083Skris if (*(unsigned short *) (buf + 64) == 0xAA55) { 7368654Skris int largest_cyl = -1, i; 7468654Skris 7568654Skris for (i = 0, p = buf; i < 4; i++, p++) { 7668654Skris if (!p->sys_ind) 7756083Skris continue; 7856083Skris#ifdef DEBUG 7956083Skris printk("scsicam_bios_param : partition %d has system \n", 8056083Skris i); 8156083Skris#endif 8256083Skris cyl = p->cyl + ((p->sector & 0xc0) << 2); 8356083Skris if (cyl > largest_cyl) { 8456083Skris largest_cyl = cyl; 8556083Skris largest = p; 8656083Skris } 8756083Skris } 8856083Skris } 8956083Skris if (largest) { 9056083Skris end_cyl = largest->end_cyl + ((largest->end_sector & 0xc0) << 2); 9156083Skris end_head = largest->end_head; 9256083Skris end_sector = largest->end_sector & 0x3f; 9356083Skris 9456083Skris if (end_head + 1 == 0 || end_sector == 0) 9556083Skris goto out_free_buf; 9656083Skris 9756083Skris#ifdef DEBUG 9859194Skris printk("scsicam_bios_param : end at h = %d, c = %d, s = %d\n", 9959194Skris end_head, end_cyl, end_sector); 10059194Skris#endif 10159194Skris 10259194Skris physical_end = end_cyl * (end_head + 1) * end_sector + 10359194Skris end_head * end_sector + end_sector; 10459194Skris 105100931Snectar /* This is the actual _sector_ number at the end */ 106100931Snectar logical_end = get_unaligned_le32(&largest->start_sect) 107100931Snectar + get_unaligned_le32(&largest->nr_sects); 108100931Snectar 109100931Snectar /* This is for >1023 cylinders */ 110100931Snectar ext_cyl = (logical_end - (end_head * end_sector + end_sector)) 11159194Skris / (end_head + 1) / end_sector; 112100931Snectar ext_physical_end = ext_cyl * (end_head + 1) * end_sector + 11359194Skris end_head * end_sector + end_sector; 11459194Skris 11556083Skris#ifdef DEBUG 11656083Skris printk("scsicam_bios_param : logical_end=%d physical_end=%d ext_physical_end=%d ext_cyl=%d\n" 11756083Skris ,logical_end, physical_end, ext_physical_end, ext_cyl); 11856083Skris#endif 11956083Skris 12056083Skris if (logical_end == physical_end || 12156083Skris (end_cyl == 1023 && ext_physical_end == logical_end)) { 12256083Skris geom[0] = end_head + 1; 12356083Skris geom[1] = end_sector; 12456083Skris geom[2] = (unsigned long)capacity / 12556083Skris ((end_head + 1) * end_sector); 12656083Skris ret = true; 12756083Skris goto out_free_buf; 12856083Skris } 12956083Skris#ifdef DEBUG 13056083Skris printk("scsicam_bios_param : logical (%u) != physical (%u)\n", 13156083Skris logical_end, physical_end); 13256083Skris#endif 13356083Skris } 13456083Skris 13556083Skrisout_free_buf: 13656083Skris kfree(buf); 13756083Skris return ret; 13856083Skris} 13956083SkrisEXPORT_SYMBOL(scsi_partsize); 14056083Skris 14156083Skris/* 14256083Skris * Function : static int setsize(unsigned long capacity,unsigned int *cyls, 14356083Skris * unsigned int *hds, unsigned int *secs); 14456083Skris * 14556083Skris * Purpose : to determine a near-optimal int 0x13 mapping for a 14656083Skris * SCSI disk in terms of lost space of size capacity, storing 14756083Skris * the results in *cyls, *hds, and *secs. 14856083Skris * 14956083Skris * Returns : -1 on failure, 0 on success. 15056083Skris * 15156083Skris * Extracted from 15256083Skris * 15356083Skris * WORKING X3T9.2 15456083Skris * DRAFT 792D 15556083Skris * see http://www.t10.org/ftp/t10/drafts/cam/cam-r12b.pdf 15656083Skris * 15756083Skris * Revision 6 15856083Skris * 10-MAR-94 15956083Skris * Information technology - 16056083Skris * SCSI-2 Common access method 16156083Skris * transport and SCSI interface module 16256083Skris * 16356083Skris * ANNEX A : 16459194Skris * 16559194Skris * setsize() converts a read capacity value to int 13h 16659194Skris * head-cylinder-sector requirements. It minimizes the value for 16759194Skris * number of heads and maximizes the number of cylinders. This 16856083Skris * will support rather large disks before the number of heads 16956083Skris * will not fit in 4 bits (or 6 bits). This algorithm also 17056083Skris * minimizes the number of sectors that will be unused at the end 17156083Skris * of the disk while allowing for very large disks to be 17256083Skris * accommodated. This algorithm does not use physical geometry. 17359194Skris */ 17456083Skris 17556083Skrisstatic int setsize(unsigned long capacity, unsigned int *cyls, unsigned int *hds, 17656083Skris unsigned int *secs) 17756083Skris{ 17856083Skris unsigned int rv = 0; 17956083Skris unsigned long heads, sectors, cylinders, temp; 18056083Skris 18156083Skris cylinders = 1024L; /* Set number of cylinders to max */ 18256083Skris sectors = 62L; /* Maximize sectors per track */ 18356083Skris 18456083Skris temp = cylinders * sectors; /* Compute divisor for heads */ 18556083Skris heads = capacity / temp; /* Compute value for number of heads */ 18656083Skris if (capacity % temp) { /* If no remainder, done! */ 18756083Skris heads++; /* Else, increment number of heads */ 18856083Skris temp = cylinders * heads; /* Compute divisor for sectors */ 18956083Skris sectors = capacity / temp; /* Compute value for sectors per 19056083Skris track */ 19156083Skris if (capacity % temp) { /* If no remainder, done! */ 19256083Skris sectors++; /* Else, increment number of sectors */ 19356083Skris temp = heads * sectors; /* Compute divisor for cylinders */ 19456083Skris cylinders = capacity / temp; /* Compute number of cylinders */ 19556083Skris } 19656083Skris } 19756083Skris if (cylinders == 0) 19856083Skris rv = (unsigned) -1; /* Give error if 0 cylinders */ 19956083Skris 20056083Skris *cyls = (unsigned int) cylinders; /* Stuff return values */ 20156083Skris *secs = (unsigned int) sectors; 20256083Skris *hds = (unsigned int) heads; 20368654Skris return (rv); 20468654Skris} 20568654Skris 20656083Skris/** 20768654Skris * scsicam_bios_param - Determine geometry of a disk in cylinders/heads/sectors. 20856083Skris * @bdev: which device 20959194Skris * @capacity: size of the disk in sectors 21059194Skris * @ip: return value: ip[0]=heads, ip[1]=sectors, ip[2]=cylinders 21156083Skris * 21256083Skris * Description : determine the BIOS mapping/geometry used for a drive in a 21356083Skris * SCSI-CAM system, storing the results in ip as required 21456083Skris * by the HDIO_GETGEO ioctl(). 21556083Skris * 21656083Skris * Returns : -1 on failure, 0 on success. 21756083Skris */ 21856083Skrisint scsicam_bios_param(struct block_device *bdev, sector_t capacity, int *ip) 21968654Skris{ 22056083Skris u64 capacity64 = capacity; /* Suppress gcc warning */ 22156083Skris int ret = 0; 22256083Skris 22368654Skris /* try to infer mapping from partition table */ 22468654Skris if (scsi_partsize(bdev, capacity, ip)) 22568654Skris return 0; 22668654Skris 22756083Skris if (capacity64 < (1ULL << 32)) { 22856083Skris /* 22956083Skris * Pick some standard mapping with at most 1024 cylinders, and 23056083Skris * at most 62 sectors per track - this works up to 7905 MB. 23156083Skris */ 23256083Skris ret = setsize((unsigned long)capacity, (unsigned int *)ip + 2, 23356083Skris (unsigned int *)ip + 0, (unsigned int *)ip + 1); 23456083Skris } 235100931Snectar 23656083Skris /* 237100931Snectar * If something went wrong, then apparently we have to return a geometry 23856083Skris * with more than 1024 cylinders. 23956083Skris */ 24056083Skris if (ret || ip[0] > 255 || ip[1] > 63) { 24156083Skris if ((capacity >> 11) > 65534) { 242100931Snectar ip[0] = 255; 24356083Skris ip[1] = 63; 244100931Snectar } else { 24556083Skris ip[0] = 64; 24656083Skris ip[1] = 32; 24756083Skris } 24856083Skris 24956083Skris if (capacity > 65535*63*255) 25056083Skris ip[2] = 65535; 25156083Skris else 25256083Skris ip[2] = (unsigned long)capacity / (ip[0] * ip[1]); 25356083Skris } 25456083Skris 25556083Skris return 0; 25656083Skris} 25756083SkrisEXPORT_SYMBOL(scsicam_bios_param); 25856083Skris