cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ibm.c (9931B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
      4 *                  Volker Sameske <sameske@de.ibm.com>
      5 * Bugreports.to..: <Linux390@de.ibm.com>
      6 * Copyright IBM Corp. 1999, 2012
      7 */
      8
      9#include <linux/buffer_head.h>
     10#include <linux/hdreg.h>
     11#include <linux/slab.h>
     12#include <asm/dasd.h>
     13#include <asm/ebcdic.h>
     14#include <linux/uaccess.h>
     15#include <asm/vtoc.h>
     16#include <linux/module.h>
     17#include <linux/dasd_mod.h>
     18
     19#include "check.h"
     20
     21union label_t {
     22	struct vtoc_volume_label_cdl vol;
     23	struct vtoc_volume_label_ldl lnx;
     24	struct vtoc_cms_label cms;
     25};
     26
     27/*
     28 * compute the block number from a
     29 * cyl-cyl-head-head structure
     30 */
     31static sector_t cchh2blk(struct vtoc_cchh *ptr, struct hd_geometry *geo)
     32{
     33	sector_t cyl;
     34	__u16 head;
     35
     36	/* decode cylinder and heads for large volumes */
     37	cyl = ptr->hh & 0xFFF0;
     38	cyl <<= 12;
     39	cyl |= ptr->cc;
     40	head = ptr->hh & 0x000F;
     41	return cyl * geo->heads * geo->sectors +
     42	       head * geo->sectors;
     43}
     44
     45/*
     46 * compute the block number from a
     47 * cyl-cyl-head-head-block structure
     48 */
     49static sector_t cchhb2blk(struct vtoc_cchhb *ptr, struct hd_geometry *geo)
     50{
     51	sector_t cyl;
     52	__u16 head;
     53
     54	/* decode cylinder and heads for large volumes */
     55	cyl = ptr->hh & 0xFFF0;
     56	cyl <<= 12;
     57	cyl |= ptr->cc;
     58	head = ptr->hh & 0x000F;
     59	return	cyl * geo->heads * geo->sectors +
     60		head * geo->sectors +
     61		ptr->b;
     62}
     63
     64static int find_label(struct parsed_partitions *state,
     65		      dasd_information2_t *info,
     66		      struct hd_geometry *geo,
     67		      int blocksize,
     68		      sector_t *labelsect,
     69		      char name[],
     70		      char type[],
     71		      union label_t *label)
     72{
     73	Sector sect;
     74	unsigned char *data;
     75	sector_t testsect[3];
     76	unsigned char temp[5];
     77	int found = 0;
     78	int i, testcount;
     79
     80	/* There a three places where we may find a valid label:
     81	 * - on an ECKD disk it's block 2
     82	 * - on an FBA disk it's block 1
     83	 * - on an CMS formatted FBA disk it is sector 1, even if the block size
     84	 *   is larger than 512 bytes (possible if the DIAG discipline is used)
     85	 * If we have a valid info structure, then we know exactly which case we
     86	 * have, otherwise we just search through all possebilities.
     87	 */
     88	if (info) {
     89		if ((info->cu_type == 0x6310 && info->dev_type == 0x9336) ||
     90		    (info->cu_type == 0x3880 && info->dev_type == 0x3370))
     91			testsect[0] = info->label_block;
     92		else
     93			testsect[0] = info->label_block * (blocksize >> 9);
     94		testcount = 1;
     95	} else {
     96		testsect[0] = 1;
     97		testsect[1] = (blocksize >> 9);
     98		testsect[2] = 2 * (blocksize >> 9);
     99		testcount = 3;
    100	}
    101	for (i = 0; i < testcount; ++i) {
    102		data = read_part_sector(state, testsect[i], &sect);
    103		if (data == NULL)
    104			continue;
    105		memcpy(label, data, sizeof(*label));
    106		memcpy(temp, data, 4);
    107		temp[4] = 0;
    108		EBCASC(temp, 4);
    109		put_dev_sector(sect);
    110		if (!strcmp(temp, "VOL1") ||
    111		    !strcmp(temp, "LNX1") ||
    112		    !strcmp(temp, "CMS1")) {
    113			if (!strcmp(temp, "VOL1")) {
    114				strncpy(type, label->vol.vollbl, 4);
    115				strncpy(name, label->vol.volid, 6);
    116			} else {
    117				strncpy(type, label->lnx.vollbl, 4);
    118				strncpy(name, label->lnx.volid, 6);
    119			}
    120			EBCASC(type, 4);
    121			EBCASC(name, 6);
    122			*labelsect = testsect[i];
    123			found = 1;
    124			break;
    125		}
    126	}
    127	if (!found)
    128		memset(label, 0, sizeof(*label));
    129
    130	return found;
    131}
    132
    133static int find_vol1_partitions(struct parsed_partitions *state,
    134				struct hd_geometry *geo,
    135				int blocksize,
    136				char name[],
    137				union label_t *label)
    138{
    139	sector_t blk;
    140	int counter;
    141	char tmp[64];
    142	Sector sect;
    143	unsigned char *data;
    144	loff_t offset, size;
    145	struct vtoc_format1_label f1;
    146	int secperblk;
    147
    148	snprintf(tmp, sizeof(tmp), "VOL1/%8s:", name);
    149	strlcat(state->pp_buf, tmp, PAGE_SIZE);
    150	/*
    151	 * get start of VTOC from the disk label and then search for format1
    152	 * and format8 labels
    153	 */
    154	secperblk = blocksize >> 9;
    155	blk = cchhb2blk(&label->vol.vtoc, geo) + 1;
    156	counter = 0;
    157	data = read_part_sector(state, blk * secperblk, &sect);
    158	while (data != NULL) {
    159		memcpy(&f1, data, sizeof(struct vtoc_format1_label));
    160		put_dev_sector(sect);
    161		/* skip FMT4 / FMT5 / FMT7 labels */
    162		if (f1.DS1FMTID == _ascebc['4']
    163		    || f1.DS1FMTID == _ascebc['5']
    164		    || f1.DS1FMTID == _ascebc['7']
    165		    || f1.DS1FMTID == _ascebc['9']) {
    166			blk++;
    167			data = read_part_sector(state, blk * secperblk, &sect);
    168			continue;
    169		}
    170		/* only FMT1 and 8 labels valid at this point */
    171		if (f1.DS1FMTID != _ascebc['1'] &&
    172		    f1.DS1FMTID != _ascebc['8'])
    173			break;
    174		/* OK, we got valid partition data */
    175		offset = cchh2blk(&f1.DS1EXT1.llimit, geo);
    176		size  = cchh2blk(&f1.DS1EXT1.ulimit, geo) -
    177			offset + geo->sectors;
    178		offset *= secperblk;
    179		size *= secperblk;
    180		if (counter >= state->limit)
    181			break;
    182		put_partition(state, counter + 1, offset, size);
    183		counter++;
    184		blk++;
    185		data = read_part_sector(state, blk * secperblk, &sect);
    186	}
    187	strlcat(state->pp_buf, "\n", PAGE_SIZE);
    188
    189	if (!data)
    190		return -1;
    191
    192	return 1;
    193}
    194
    195static int find_lnx1_partitions(struct parsed_partitions *state,
    196				struct hd_geometry *geo,
    197				int blocksize,
    198				char name[],
    199				union label_t *label,
    200				sector_t labelsect,
    201				sector_t nr_sectors,
    202				dasd_information2_t *info)
    203{
    204	loff_t offset, geo_size, size;
    205	char tmp[64];
    206	int secperblk;
    207
    208	snprintf(tmp, sizeof(tmp), "LNX1/%8s:", name);
    209	strlcat(state->pp_buf, tmp, PAGE_SIZE);
    210	secperblk = blocksize >> 9;
    211	if (label->lnx.ldl_version == 0xf2) {
    212		size = label->lnx.formatted_blocks * secperblk;
    213	} else {
    214		/*
    215		 * Formated w/o large volume support. If the sanity check
    216		 * 'size based on geo == size based on nr_sectors' is true, then
    217		 * we can safely assume that we know the formatted size of
    218		 * the disk, otherwise we need additional information
    219		 * that we can only get from a real DASD device.
    220		 */
    221		geo_size = geo->cylinders * geo->heads
    222			* geo->sectors * secperblk;
    223		size = nr_sectors;
    224		if (size != geo_size) {
    225			if (!info) {
    226				strlcat(state->pp_buf, "\n", PAGE_SIZE);
    227				return 1;
    228			}
    229			if (!strcmp(info->type, "ECKD"))
    230				if (geo_size < size)
    231					size = geo_size;
    232			/* else keep size based on nr_sectors */
    233		}
    234	}
    235	/* first and only partition starts in the first block after the label */
    236	offset = labelsect + secperblk;
    237	put_partition(state, 1, offset, size - offset);
    238	strlcat(state->pp_buf, "\n", PAGE_SIZE);
    239	return 1;
    240}
    241
    242static int find_cms1_partitions(struct parsed_partitions *state,
    243				struct hd_geometry *geo,
    244				int blocksize,
    245				char name[],
    246				union label_t *label,
    247				sector_t labelsect)
    248{
    249	loff_t offset, size;
    250	char tmp[64];
    251	int secperblk;
    252
    253	/*
    254	 * VM style CMS1 labeled disk
    255	 */
    256	blocksize = label->cms.block_size;
    257	secperblk = blocksize >> 9;
    258	if (label->cms.disk_offset != 0) {
    259		snprintf(tmp, sizeof(tmp), "CMS1/%8s(MDSK):", name);
    260		strlcat(state->pp_buf, tmp, PAGE_SIZE);
    261		/* disk is reserved minidisk */
    262		offset = label->cms.disk_offset * secperblk;
    263		size = (label->cms.block_count - 1) * secperblk;
    264	} else {
    265		snprintf(tmp, sizeof(tmp), "CMS1/%8s:", name);
    266		strlcat(state->pp_buf, tmp, PAGE_SIZE);
    267		/*
    268		 * Special case for FBA devices:
    269		 * If an FBA device is CMS formatted with blocksize > 512 byte
    270		 * and the DIAG discipline is used, then the CMS label is found
    271		 * in sector 1 instead of block 1. However, the partition is
    272		 * still supposed to start in block 2.
    273		 */
    274		if (labelsect == 1)
    275			offset = 2 * secperblk;
    276		else
    277			offset = labelsect + secperblk;
    278		size = label->cms.block_count * secperblk;
    279	}
    280
    281	put_partition(state, 1, offset, size-offset);
    282	strlcat(state->pp_buf, "\n", PAGE_SIZE);
    283	return 1;
    284}
    285
    286
    287/*
    288 * This is the main function, called by check.c
    289 */
    290int ibm_partition(struct parsed_partitions *state)
    291{
    292	int (*fn)(struct gendisk *disk, dasd_information2_t *info);
    293	struct gendisk *disk = state->disk;
    294	struct block_device *bdev = disk->part0;
    295	int blocksize, res;
    296	loff_t offset, size;
    297	sector_t nr_sectors;
    298	dasd_information2_t *info;
    299	struct hd_geometry *geo;
    300	char type[5] = {0,};
    301	char name[7] = {0,};
    302	sector_t labelsect;
    303	union label_t *label;
    304
    305	res = 0;
    306	if (!disk->fops->getgeo)
    307		goto out_exit;
    308	fn = symbol_get(dasd_biodasdinfo);
    309	blocksize = bdev_logical_block_size(bdev);
    310	if (blocksize <= 0)
    311		goto out_symbol;
    312	nr_sectors = bdev_nr_sectors(bdev);
    313	if (nr_sectors == 0)
    314		goto out_symbol;
    315	info = kmalloc(sizeof(dasd_information2_t), GFP_KERNEL);
    316	if (info == NULL)
    317		goto out_symbol;
    318	geo = kmalloc(sizeof(struct hd_geometry), GFP_KERNEL);
    319	if (geo == NULL)
    320		goto out_nogeo;
    321	label = kmalloc(sizeof(union label_t), GFP_KERNEL);
    322	if (label == NULL)
    323		goto out_nolab;
    324	/* set start if not filled by getgeo function e.g. virtblk */
    325	geo->start = get_start_sect(bdev);
    326	if (disk->fops->getgeo(bdev, geo))
    327		goto out_freeall;
    328	if (!fn || fn(disk, info)) {
    329		kfree(info);
    330		info = NULL;
    331	}
    332
    333	if (find_label(state, info, geo, blocksize, &labelsect, name, type,
    334		       label)) {
    335		if (!strncmp(type, "VOL1", 4)) {
    336			res = find_vol1_partitions(state, geo, blocksize, name,
    337						   label);
    338		} else if (!strncmp(type, "LNX1", 4)) {
    339			res = find_lnx1_partitions(state, geo, blocksize, name,
    340						   label, labelsect, nr_sectors,
    341						   info);
    342		} else if (!strncmp(type, "CMS1", 4)) {
    343			res = find_cms1_partitions(state, geo, blocksize, name,
    344						   label, labelsect);
    345		}
    346	} else if (info) {
    347		/*
    348		 * ugly but needed for backward compatibility:
    349		 * If the block device is a DASD (i.e. BIODASDINFO2 works),
    350		 * then we claim it in any case, even though it has no valid
    351		 * label. If it has the LDL format, then we simply define a
    352		 * partition as if it had an LNX1 label.
    353		 */
    354		res = 1;
    355		if (info->format == DASD_FORMAT_LDL) {
    356			strlcat(state->pp_buf, "(nonl)", PAGE_SIZE);
    357			size = nr_sectors;
    358			offset = (info->label_block + 1) * (blocksize >> 9);
    359			put_partition(state, 1, offset, size-offset);
    360			strlcat(state->pp_buf, "\n", PAGE_SIZE);
    361		}
    362	} else
    363		res = 0;
    364
    365out_freeall:
    366	kfree(label);
    367out_nolab:
    368	kfree(geo);
    369out_nogeo:
    370	kfree(info);
    371out_symbol:
    372	if (fn)
    373		symbol_put(dasd_biodasdinfo);
    374out_exit:
    375	return res;
    376}