162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci/****************************************************************************** 462306a36Sopenharmony_ci * platform-pci-unplug.c 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Xen platform PCI device driver 762306a36Sopenharmony_ci * Copyright (c) 2010, Citrix 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/io.h> 1462306a36Sopenharmony_ci#include <linux/export.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <xen/xen.h> 1762306a36Sopenharmony_ci#include <xen/platform_pci.h> 1862306a36Sopenharmony_ci#include "xen-ops.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define XEN_PLATFORM_ERR_MAGIC -1 2162306a36Sopenharmony_ci#define XEN_PLATFORM_ERR_PROTOCOL -2 2262306a36Sopenharmony_ci#define XEN_PLATFORM_ERR_BLACKLIST -3 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* store the value of xen_emul_unplug after the unplug is done */ 2562306a36Sopenharmony_cistatic int xen_platform_pci_unplug; 2662306a36Sopenharmony_cistatic int xen_emul_unplug; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic int check_platform_magic(void) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci short magic; 3162306a36Sopenharmony_ci char protocol; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci magic = inw(XEN_IOPORT_MAGIC); 3462306a36Sopenharmony_ci if (magic != XEN_IOPORT_MAGIC_VAL) { 3562306a36Sopenharmony_ci pr_err("Xen Platform PCI: unrecognised magic value\n"); 3662306a36Sopenharmony_ci return XEN_PLATFORM_ERR_MAGIC; 3762306a36Sopenharmony_ci } 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci protocol = inb(XEN_IOPORT_PROTOVER); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci pr_debug("Xen Platform PCI: I/O protocol version %d\n", 4262306a36Sopenharmony_ci protocol); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci switch (protocol) { 4562306a36Sopenharmony_ci case 1: 4662306a36Sopenharmony_ci outw(XEN_IOPORT_LINUX_PRODNUM, XEN_IOPORT_PRODNUM); 4762306a36Sopenharmony_ci outl(XEN_IOPORT_LINUX_DRVVER, XEN_IOPORT_DRVVER); 4862306a36Sopenharmony_ci if (inw(XEN_IOPORT_MAGIC) != XEN_IOPORT_MAGIC_VAL) { 4962306a36Sopenharmony_ci pr_err("Xen Platform: blacklisted by host\n"); 5062306a36Sopenharmony_ci return XEN_PLATFORM_ERR_BLACKLIST; 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci break; 5362306a36Sopenharmony_ci default: 5462306a36Sopenharmony_ci pr_warn("Xen Platform PCI: unknown I/O protocol version\n"); 5562306a36Sopenharmony_ci return XEN_PLATFORM_ERR_PROTOCOL; 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci return 0; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cibool xen_has_pv_devices(void) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci if (!xen_domain()) 6462306a36Sopenharmony_ci return false; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci /* PV and PVH domains always have them. */ 6762306a36Sopenharmony_ci if (xen_pv_domain() || xen_pvh_domain()) 6862306a36Sopenharmony_ci return true; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci /* And user has xen_platform_pci=0 set in guest config as 7162306a36Sopenharmony_ci * driver did not modify the value. */ 7262306a36Sopenharmony_ci if (xen_platform_pci_unplug == 0) 7362306a36Sopenharmony_ci return false; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (xen_platform_pci_unplug & XEN_UNPLUG_NEVER) 7662306a36Sopenharmony_ci return false; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (xen_platform_pci_unplug & XEN_UNPLUG_ALL) 7962306a36Sopenharmony_ci return true; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci /* This is an odd one - we are going to run legacy 8262306a36Sopenharmony_ci * and PV drivers at the same time. */ 8362306a36Sopenharmony_ci if (xen_platform_pci_unplug & XEN_UNPLUG_UNNECESSARY) 8462306a36Sopenharmony_ci return true; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci /* And the caller has to follow with xen_pv_{disk,nic}_devices 8762306a36Sopenharmony_ci * to be certain which driver can load. */ 8862306a36Sopenharmony_ci return false; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xen_has_pv_devices); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic bool __xen_has_pv_device(int state) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci /* HVM domains might or might not */ 9562306a36Sopenharmony_ci if (xen_hvm_domain() && (xen_platform_pci_unplug & state)) 9662306a36Sopenharmony_ci return true; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci return xen_has_pv_devices(); 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cibool xen_has_pv_nic_devices(void) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci return __xen_has_pv_device(XEN_UNPLUG_ALL_NICS | XEN_UNPLUG_ALL); 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xen_has_pv_nic_devices); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cibool xen_has_pv_disk_devices(void) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci return __xen_has_pv_device(XEN_UNPLUG_ALL_IDE_DISKS | 11062306a36Sopenharmony_ci XEN_UNPLUG_AUX_IDE_DISKS | XEN_UNPLUG_ALL); 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xen_has_pv_disk_devices); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* 11562306a36Sopenharmony_ci * This one is odd - it determines whether you want to run PV _and_ 11662306a36Sopenharmony_ci * legacy (IDE) drivers together. This combination is only possible 11762306a36Sopenharmony_ci * under HVM. 11862306a36Sopenharmony_ci */ 11962306a36Sopenharmony_cibool xen_has_pv_and_legacy_disk_devices(void) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci if (!xen_domain()) 12262306a36Sopenharmony_ci return false; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* N.B. This is only ever used in HVM mode */ 12562306a36Sopenharmony_ci if (xen_pv_domain()) 12662306a36Sopenharmony_ci return false; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (xen_platform_pci_unplug & XEN_UNPLUG_UNNECESSARY) 12962306a36Sopenharmony_ci return true; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci return false; 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xen_has_pv_and_legacy_disk_devices); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_civoid xen_unplug_emulated_devices(void) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci int r; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* PVH guests don't have emulated devices. */ 14062306a36Sopenharmony_ci if (xen_pvh_domain()) 14162306a36Sopenharmony_ci return; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci /* user explicitly requested no unplug */ 14462306a36Sopenharmony_ci if (xen_emul_unplug & XEN_UNPLUG_NEVER) 14562306a36Sopenharmony_ci return; 14662306a36Sopenharmony_ci /* check the version of the xen platform PCI device */ 14762306a36Sopenharmony_ci r = check_platform_magic(); 14862306a36Sopenharmony_ci /* If the version matches enable the Xen platform PCI driver. 14962306a36Sopenharmony_ci * Also enable the Xen platform PCI driver if the host does 15062306a36Sopenharmony_ci * not support the unplug protocol (XEN_PLATFORM_ERR_MAGIC) 15162306a36Sopenharmony_ci * but the user told us that unplugging is unnecessary. */ 15262306a36Sopenharmony_ci if (r && !(r == XEN_PLATFORM_ERR_MAGIC && 15362306a36Sopenharmony_ci (xen_emul_unplug & XEN_UNPLUG_UNNECESSARY))) 15462306a36Sopenharmony_ci return; 15562306a36Sopenharmony_ci /* Set the default value of xen_emul_unplug depending on whether or 15662306a36Sopenharmony_ci * not the Xen PV frontends and the Xen platform PCI driver have 15762306a36Sopenharmony_ci * been compiled for this kernel (modules or built-in are both OK). */ 15862306a36Sopenharmony_ci if (!xen_emul_unplug) { 15962306a36Sopenharmony_ci if (xen_must_unplug_nics()) { 16062306a36Sopenharmony_ci pr_info("Netfront and the Xen platform PCI driver have " 16162306a36Sopenharmony_ci "been compiled for this kernel: unplug emulated NICs.\n"); 16262306a36Sopenharmony_ci xen_emul_unplug |= XEN_UNPLUG_ALL_NICS; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci if (xen_must_unplug_disks()) { 16562306a36Sopenharmony_ci pr_info("Blkfront and the Xen platform PCI driver have " 16662306a36Sopenharmony_ci "been compiled for this kernel: unplug emulated disks.\n" 16762306a36Sopenharmony_ci "You might have to change the root device\n" 16862306a36Sopenharmony_ci "from /dev/hd[a-d] to /dev/xvd[a-d]\n" 16962306a36Sopenharmony_ci "in your root= kernel command line option\n"); 17062306a36Sopenharmony_ci xen_emul_unplug |= XEN_UNPLUG_ALL_IDE_DISKS; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci /* Now unplug the emulated devices */ 17462306a36Sopenharmony_ci if (!(xen_emul_unplug & XEN_UNPLUG_UNNECESSARY)) 17562306a36Sopenharmony_ci outw(xen_emul_unplug, XEN_IOPORT_UNPLUG); 17662306a36Sopenharmony_ci xen_platform_pci_unplug = xen_emul_unplug; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic int __init parse_xen_emul_unplug(char *arg) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci char *p, *q; 18262306a36Sopenharmony_ci int l; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci for (p = arg; p; p = q) { 18562306a36Sopenharmony_ci q = strchr(p, ','); 18662306a36Sopenharmony_ci if (q) { 18762306a36Sopenharmony_ci l = q - p; 18862306a36Sopenharmony_ci q++; 18962306a36Sopenharmony_ci } else { 19062306a36Sopenharmony_ci l = strlen(p); 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci if (!strncmp(p, "all", l)) 19362306a36Sopenharmony_ci xen_emul_unplug |= XEN_UNPLUG_ALL; 19462306a36Sopenharmony_ci else if (!strncmp(p, "ide-disks", l)) 19562306a36Sopenharmony_ci xen_emul_unplug |= XEN_UNPLUG_ALL_IDE_DISKS; 19662306a36Sopenharmony_ci else if (!strncmp(p, "aux-ide-disks", l)) 19762306a36Sopenharmony_ci xen_emul_unplug |= XEN_UNPLUG_AUX_IDE_DISKS; 19862306a36Sopenharmony_ci else if (!strncmp(p, "nics", l)) 19962306a36Sopenharmony_ci xen_emul_unplug |= XEN_UNPLUG_ALL_NICS; 20062306a36Sopenharmony_ci else if (!strncmp(p, "unnecessary", l)) 20162306a36Sopenharmony_ci xen_emul_unplug |= XEN_UNPLUG_UNNECESSARY; 20262306a36Sopenharmony_ci else if (!strncmp(p, "never", l)) 20362306a36Sopenharmony_ci xen_emul_unplug |= XEN_UNPLUG_NEVER; 20462306a36Sopenharmony_ci else 20562306a36Sopenharmony_ci pr_warn("unrecognised option '%s' " 20662306a36Sopenharmony_ci "in parameter 'xen_emul_unplug'\n", p); 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci return 0; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ciearly_param("xen_emul_unplug", parse_xen_emul_unplug); 211