162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * sd_dif.c - SCSI Data Integrity Field
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2007, 2008 Oracle Corporation
662306a36Sopenharmony_ci * Written by: Martin K. Petersen <martin.petersen@oracle.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/blk-integrity.h>
1062306a36Sopenharmony_ci#include <linux/t10-pi.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <scsi/scsi.h>
1362306a36Sopenharmony_ci#include <scsi/scsi_cmnd.h>
1462306a36Sopenharmony_ci#include <scsi/scsi_dbg.h>
1562306a36Sopenharmony_ci#include <scsi/scsi_device.h>
1662306a36Sopenharmony_ci#include <scsi/scsi_driver.h>
1762306a36Sopenharmony_ci#include <scsi/scsi_eh.h>
1862306a36Sopenharmony_ci#include <scsi/scsi_host.h>
1962306a36Sopenharmony_ci#include <scsi/scsi_ioctl.h>
2062306a36Sopenharmony_ci#include <scsi/scsicam.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include "sd.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/*
2562306a36Sopenharmony_ci * Configure exchange of protection information between OS and HBA.
2662306a36Sopenharmony_ci */
2762306a36Sopenharmony_civoid sd_dif_config_host(struct scsi_disk *sdkp)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	struct scsi_device *sdp = sdkp->device;
3062306a36Sopenharmony_ci	struct gendisk *disk = sdkp->disk;
3162306a36Sopenharmony_ci	u8 type = sdkp->protection_type;
3262306a36Sopenharmony_ci	struct blk_integrity bi;
3362306a36Sopenharmony_ci	int dif, dix;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	dif = scsi_host_dif_capable(sdp->host, type);
3662306a36Sopenharmony_ci	dix = scsi_host_dix_capable(sdp->host, type);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	if (!dix && scsi_host_dix_capable(sdp->host, 0)) {
3962306a36Sopenharmony_ci		dif = 0; dix = 1;
4062306a36Sopenharmony_ci	}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	if (!dix) {
4362306a36Sopenharmony_ci		blk_integrity_unregister(disk);
4462306a36Sopenharmony_ci		return;
4562306a36Sopenharmony_ci	}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	memset(&bi, 0, sizeof(bi));
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	/* Enable DMA of protection information */
5062306a36Sopenharmony_ci	if (scsi_host_get_guard(sdkp->device->host) & SHOST_DIX_GUARD_IP) {
5162306a36Sopenharmony_ci		if (type == T10_PI_TYPE3_PROTECTION)
5262306a36Sopenharmony_ci			bi.profile = &t10_pi_type3_ip;
5362306a36Sopenharmony_ci		else
5462306a36Sopenharmony_ci			bi.profile = &t10_pi_type1_ip;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci		bi.flags |= BLK_INTEGRITY_IP_CHECKSUM;
5762306a36Sopenharmony_ci	} else
5862306a36Sopenharmony_ci		if (type == T10_PI_TYPE3_PROTECTION)
5962306a36Sopenharmony_ci			bi.profile = &t10_pi_type3_crc;
6062306a36Sopenharmony_ci		else
6162306a36Sopenharmony_ci			bi.profile = &t10_pi_type1_crc;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	bi.tuple_size = sizeof(struct t10_pi_tuple);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	if (dif && type) {
6662306a36Sopenharmony_ci		bi.flags |= BLK_INTEGRITY_DEVICE_CAPABLE;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci		if (!sdkp->ATO)
6962306a36Sopenharmony_ci			goto out;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci		if (type == T10_PI_TYPE3_PROTECTION)
7262306a36Sopenharmony_ci			bi.tag_size = sizeof(u16) + sizeof(u32);
7362306a36Sopenharmony_ci		else
7462306a36Sopenharmony_ci			bi.tag_size = sizeof(u16);
7562306a36Sopenharmony_ci	}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	sd_first_printk(KERN_NOTICE, sdkp,
7862306a36Sopenharmony_ci			"Enabling DIX %s, application tag size %u bytes\n",
7962306a36Sopenharmony_ci			bi.profile->name, bi.tag_size);
8062306a36Sopenharmony_ciout:
8162306a36Sopenharmony_ci	blk_integrity_register(disk, &bi);
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
84