mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
Enable HVM guest VT-d device hotplug via a simple ACPI hotplug device model.
** Currently only 2 virtual hotplug pci slots(6~7) are created so more than 2 vtd dev can't be hotplugged, but we can easily extend it in future. Three new commands are added: "xm pci-list domid" show the current assigned vtd device, like: VSlt domain bus slot func 0x6 0x0 0x02 0x00 0x0 "xm pci-detach" hot remove the specified vtd device by the virtual slot, like: xm pci-detach EdwinHVMDomainVtd 6 "xm pci-attach DomainID dom bus dev func [vslot]" hot add a new vtd device in the vslot. If no vslot specified, a free slot will be picked up. e.g. to insert '0000:03:00.0': xm pci-attach EdwinHVMDomainVtd 0 3 0 0 ** guest pci hotplug linux: pls. use 2.6.X and enable ACPI PCI hotplug ( Bus options=> PCI hotplug => ACPI PCI hotplug driver ) windows: 2000/xp/2003/vista are all okay Signed-off-by: Zhai Edwin <edwin.zhai@intel.com>
This commit is contained in:
1 parent
1546fd3f23
commit
3bbf83843c
26 files changed
+1545
-46
No files matched your search
@@ -86,7 +86,7 @@ DefinitionBlock ("DSDT.aml", "DSDT", 2, "Xen", "HVM", 0)
|
||||
Name (_UID, 0x00)
|
||||
Name (_ADR, 0x00)
|
||||
Name (_BBN, 0x00)
|
||||
|
||||
|
||||
Method (_CRS, 0, NotSerialized)
|
||||
{
|
||||
Name (PRT0, ResourceTemplate ()
|
||||
@@ -720,6 +720,121 @@ DefinitionBlock ("DSDT.aml", "DSDT", 2, "Xen", "HVM", 0)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************
|
||||
* Each PCI hotplug slot needs at least two methods to handle
|
||||
* the ACPI event:
|
||||
* _EJ0: eject a device
|
||||
* _STA: return a device's status, e.g. enabled or removed
|
||||
* Other methods are optional:
|
||||
* _PS0/3: put them here for debug purpose
|
||||
*
|
||||
* Eject button would generate a general-purpose event, then the
|
||||
* control method for this event uses Notify() to inform OSPM which
|
||||
* action happened and on which device.
|
||||
*
|
||||
* Pls. refer "6.3 Device Insertion, Removal, and Status Objects"
|
||||
* in ACPI spec 3.0b for details.
|
||||
*
|
||||
* QEMU provides a simple hotplug controller with some I/O to
|
||||
* handle the hotplug action and status, which is beyond the ACPI
|
||||
* scope.
|
||||
*/
|
||||
|
||||
Device (S1F0)
|
||||
{
|
||||
Name (_ADR, 0x00060000) /* Dev 6, Func 0 */
|
||||
Name (_SUN, 0x00000001)
|
||||
|
||||
Method (_PS0, 0)
|
||||
{
|
||||
Store (0x80, \_GPE.DPT2)
|
||||
}
|
||||
|
||||
Method (_PS3, 0)
|
||||
{
|
||||
Store (0x83, \_GPE.DPT2)
|
||||
}
|
||||
|
||||
Method (_EJ0, 1)
|
||||
{
|
||||
Store (0x88, \_GPE.DPT2)
|
||||
Store (0x1, \_GPE.PHP1) /* eject php slot 1*/
|
||||
}
|
||||
|
||||
Method (_STA, 0)
|
||||
{
|
||||
Store (0x89, \_GPE.DPT2)
|
||||
Return ( \_GPE.PHP1 ) /* IN status as the _STA */
|
||||
}
|
||||
}
|
||||
|
||||
Device (S2F0)
|
||||
{
|
||||
Name (_ADR, 0x00070000) /* Dev 7, Func 0 */
|
||||
Name (_SUN, 0x00000002)
|
||||
|
||||
Method (_PS0, 0)
|
||||
{
|
||||
Store (0x90, \_GPE.DPT2)
|
||||
}
|
||||
|
||||
Method (_PS3, 0)
|
||||
{
|
||||
Store (0x93, \_GPE.DPT2)
|
||||
}
|
||||
|
||||
Method (_EJ0, 1)
|
||||
{
|
||||
Store (0x98, \_GPE.DPT2)
|
||||
Store (0x1, \_GPE.PHP2) /* eject php slot 1*/
|
||||
}
|
||||
|
||||
Method (_STA, 0)
|
||||
{
|
||||
Store (0x99, \_GPE.DPT2)
|
||||
Return ( \_GPE.PHP2 ) /* IN status as the _STA */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Scope (\_GPE)
|
||||
{
|
||||
OperationRegion (PHP, SystemIO, 0x10c0, 0x03)
|
||||
Field (PHP, ByteAcc, NoLock, Preserve)
|
||||
{
|
||||
PSTA, 8, /* hotplug controller status reg */
|
||||
PHP1, 8, /* hotplug slot 1 control reg */
|
||||
PHP2, 8 /* hotplug slot 2 control reg */
|
||||
}
|
||||
OperationRegion (DG1, SystemIO, 0xb044, 0x04)
|
||||
Field (DG1, ByteAcc, NoLock, Preserve)
|
||||
{
|
||||
DPT1, 8,
|
||||
DPT2, 8
|
||||
}
|
||||
Method (_L03, 0, NotSerialized)
|
||||
{
|
||||
/* detect slot and event(remove/add) */
|
||||
Name (SLT, 0x0)
|
||||
Name (EVT, 0x0)
|
||||
Store (PSTA, Local1)
|
||||
ShiftRight (Local1, 0x4, SLT)
|
||||
And (Local1, 0xf, EVT)
|
||||
|
||||
/* debug */
|
||||
Store (SLT, DPT1)
|
||||
Store (EVT, DPT2)
|
||||
|
||||
If ( LEqual(SLT, 0x1) )
|
||||
{
|
||||
Notify (\_SB.PCI0.S1F0, EVT)
|
||||
}
|
||||
ElseIf ( LEqual(SLT, 0x2) )
|
||||
{
|
||||
Notify (\_SB.PCI0.S2F0, EVT)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,15 +5,15 @@
|
||||
* Copyright (C) 2000 - 2006 Intel Corporation
|
||||
* Supports ACPI Specification Revision 3.0a
|
||||
*
|
||||
* Compilation of "dsdt.asl" - Fri Feb 15 12:48:58 2008
|
||||
* Compilation of "dsdt.asl" - Fri Feb 15 14:07:57 2008
|
||||
*
|
||||
* C source code output
|
||||
*
|
||||
*/
|
||||
unsigned char AmlCode[] =
|
||||
{
|
||||
0x44,0x53,0x44,0x54,0x9C,0x0E,0x00,0x00, /* 00000000 "DSDT...." */
|
||||
0x02,0xD5,0x58,0x65,0x6E,0x00,0x00,0x00, /* 00000008 "..Xen..." */
|
||||
0x44,0x53,0x44,0x54,0x5A,0x10,0x00,0x00, /* 00000000 "DSDTZ..." */
|
||||
0x02,0xCC,0x58,0x65,0x6E,0x00,0x00,0x00, /* 00000008 "..Xen..." */
|
||||
0x48,0x56,0x4D,0x00,0x00,0x00,0x00,0x00, /* 00000010 "HVM....." */
|
||||
0x00,0x00,0x00,0x00,0x49,0x4E,0x54,0x4C, /* 00000018 "....INTL" */
|
||||
0x07,0x07,0x06,0x20,0x08,0x50,0x4D,0x42, /* 00000020 "... .PMB" */
|
||||
@@ -29,7 +29,7 @@ unsigned char AmlCode[] =
|
||||
0x07,0x0A,0x07,0x00,0x00,0x08,0x50,0x49, /* 00000070 "......PI" */
|
||||
0x43,0x44,0x00,0x14,0x0C,0x5F,0x50,0x49, /* 00000078 "CD..._PI" */
|
||||
0x43,0x01,0x70,0x68,0x50,0x49,0x43,0x44, /* 00000080 "C.phPICD" */
|
||||
0x10,0x43,0xE1,0x5F,0x53,0x42,0x5F,0x5B, /* 00000088 ".C._SB_[" */
|
||||
0x10,0x42,0xF1,0x5F,0x53,0x42,0x5F,0x5B, /* 00000088 ".B._SB_[" */
|
||||
0x80,0x42,0x49,0x4F,0x53,0x00,0x0C,0x00, /* 00000090 ".BIOS..." */
|
||||
0xA0,0x0E,0x00,0x0A,0x10,0x5B,0x81,0x21, /* 00000098 ".....[.!" */
|
||||
0x42,0x49,0x4F,0x53,0x01,0x55,0x41,0x52, /* 000000A0 "BIOS.UAR" */
|
||||
@@ -45,7 +45,7 @@ unsigned char AmlCode[] =
|
||||
0x00,0xFF,0xFF,0x09,0x00,0x00,0x00,0x00, /* 000000F0 "........" */
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 000000F8 "........" */
|
||||
0x00,0x00,0x00,0x0A,0x00,0x00,0x00,0x00, /* 00000100 "........" */
|
||||
0x00,0x79,0x00,0x5B,0x82,0x4F,0xD8,0x50, /* 00000108 ".y.[.O.P" */
|
||||
0x00,0x79,0x00,0x5B,0x82,0x4E,0xE8,0x50, /* 00000108 ".y.[.N.P" */
|
||||
0x43,0x49,0x30,0x08,0x5F,0x48,0x49,0x44, /* 00000110 "CI0._HID" */
|
||||
0x0C,0x41,0xD0,0x0A,0x03,0x08,0x5F,0x55, /* 00000118 ".A...._U" */
|
||||
0x49,0x44,0x00,0x08,0x5F,0x41,0x44,0x52, /* 00000120 "ID.._ADR" */
|
||||
@@ -479,6 +479,62 @@ unsigned char AmlCode[] =
|
||||
0x54,0x41,0x00,0xA4,0x0A,0x0F,0x08,0x5F, /* 00000E80 "TA....._" */
|
||||
0x43,0x52,0x53,0x11,0x10,0x0A,0x0D,0x47, /* 00000E88 "CRS....G" */
|
||||
0x01,0x78,0x03,0x78,0x03,0x08,0x08,0x22, /* 00000E90 ".x.x..."" */
|
||||
0x80,0x00,0x79,0x00,
|
||||
0x80,0x00,0x79,0x00,0x5B,0x82,0x4D,0x07, /* 00000E98 "..y.[.M." */
|
||||
0x53,0x31,0x46,0x30,0x08,0x5F,0x41,0x44, /* 00000EA0 "S1F0._AD" */
|
||||
0x52,0x0C,0x00,0x00,0x06,0x00,0x08,0x5F, /* 00000EA8 "R......_" */
|
||||
0x53,0x55,0x4E,0x01,0x14,0x13,0x5F,0x50, /* 00000EB0 "SUN..._P" */
|
||||
0x53,0x30,0x00,0x70,0x0A,0x80,0x5C,0x2E, /* 00000EB8 "S0.p..\." */
|
||||
0x5F,0x47,0x50,0x45,0x44,0x50,0x54,0x32, /* 00000EC0 "_GPEDPT2" */
|
||||
0x14,0x13,0x5F,0x50,0x53,0x33,0x00,0x70, /* 00000EC8 ".._PS3.p" */
|
||||
0x0A,0x83,0x5C,0x2E,0x5F,0x47,0x50,0x45, /* 00000ED0 "..\._GPE" */
|
||||
0x44,0x50,0x54,0x32,0x14,0x1F,0x5F,0x45, /* 00000ED8 "DPT2.._E" */
|
||||
0x4A,0x30,0x01,0x70,0x0A,0x88,0x5C,0x2E, /* 00000EE0 "J0.p..\." */
|
||||
0x5F,0x47,0x50,0x45,0x44,0x50,0x54,0x32, /* 00000EE8 "_GPEDPT2" */
|
||||
0x70,0x01,0x5C,0x2E,0x5F,0x47,0x50,0x45, /* 00000EF0 "p.\._GPE" */
|
||||
0x50,0x48,0x50,0x31,0x14,0x1E,0x5F,0x53, /* 00000EF8 "PHP1.._S" */
|
||||
0x54,0x41,0x00,0x70,0x0A,0x89,0x5C,0x2E, /* 00000F00 "TA.p..\." */
|
||||
0x5F,0x47,0x50,0x45,0x44,0x50,0x54,0x32, /* 00000F08 "_GPEDPT2" */
|
||||
0xA4,0x5C,0x2E,0x5F,0x47,0x50,0x45,0x50, /* 00000F10 ".\._GPEP" */
|
||||
0x48,0x50,0x31,0x5B,0x82,0x4E,0x07,0x53, /* 00000F18 "HP1[.N.S" */
|
||||
0x32,0x46,0x30,0x08,0x5F,0x41,0x44,0x52, /* 00000F20 "2F0._ADR" */
|
||||
0x0C,0x00,0x00,0x07,0x00,0x08,0x5F,0x53, /* 00000F28 "......_S" */
|
||||
0x55,0x4E,0x0A,0x02,0x14,0x13,0x5F,0x50, /* 00000F30 "UN...._P" */
|
||||
0x53,0x30,0x00,0x70,0x0A,0x90,0x5C,0x2E, /* 00000F38 "S0.p..\." */
|
||||
0x5F,0x47,0x50,0x45,0x44,0x50,0x54,0x32, /* 00000F40 "_GPEDPT2" */
|
||||
0x14,0x13,0x5F,0x50,0x53,0x33,0x00,0x70, /* 00000F48 ".._PS3.p" */
|
||||
0x0A,0x93,0x5C,0x2E,0x5F,0x47,0x50,0x45, /* 00000F50 "..\._GPE" */
|
||||
0x44,0x50,0x54,0x32,0x14,0x1F,0x5F,0x45, /* 00000F58 "DPT2.._E" */
|
||||
0x4A,0x30,0x01,0x70,0x0A,0x98,0x5C,0x2E, /* 00000F60 "J0.p..\." */
|
||||
0x5F,0x47,0x50,0x45,0x44,0x50,0x54,0x32, /* 00000F68 "_GPEDPT2" */
|
||||
0x70,0x01,0x5C,0x2E,0x5F,0x47,0x50,0x45, /* 00000F70 "p.\._GPE" */
|
||||
0x50,0x48,0x50,0x32,0x14,0x1E,0x5F,0x53, /* 00000F78 "PHP2.._S" */
|
||||
0x54,0x41,0x00,0x70,0x0A,0x99,0x5C,0x2E, /* 00000F80 "TA.p..\." */
|
||||
0x5F,0x47,0x50,0x45,0x44,0x50,0x54,0x32, /* 00000F88 "_GPEDPT2" */
|
||||
0xA4,0x5C,0x2E,0x5F,0x47,0x50,0x45,0x50, /* 00000F90 ".\._GPEP" */
|
||||
0x48,0x50,0x32,0x10,0x4E,0x0B,0x5F,0x47, /* 00000F98 "HP2.N._G" */
|
||||
0x50,0x45,0x5B,0x80,0x50,0x48,0x50,0x5F, /* 00000FA0 "PE[.PHP_" */
|
||||
0x01,0x0B,0xC0,0x10,0x0A,0x03,0x5B,0x81, /* 00000FA8 "......[." */
|
||||
0x15,0x50,0x48,0x50,0x5F,0x01,0x50,0x53, /* 00000FB0 ".PHP_.PS" */
|
||||
0x54,0x41,0x08,0x50,0x48,0x50,0x31,0x08, /* 00000FB8 "TA.PHP1." */
|
||||
0x50,0x48,0x50,0x32,0x08,0x5B,0x80,0x44, /* 00000FC0 "PHP2.[.D" */
|
||||
0x47,0x31,0x5F,0x01,0x0B,0x44,0xB0,0x0A, /* 00000FC8 "G1_..D.." */
|
||||
0x04,0x5B,0x81,0x10,0x44,0x47,0x31,0x5F, /* 00000FD0 ".[..DG1_" */
|
||||
0x01,0x44,0x50,0x54,0x31,0x08,0x44,0x50, /* 00000FD8 ".DPT1.DP" */
|
||||
0x54,0x32,0x08,0x14,0x46,0x07,0x5F,0x4C, /* 00000FE0 "T2..F._L" */
|
||||
0x30,0x33,0x00,0x08,0x53,0x4C,0x54,0x5F, /* 00000FE8 "03..SLT_" */
|
||||
0x00,0x08,0x45,0x56,0x54,0x5F,0x00,0x70, /* 00000FF0 "..EVT_.p" */
|
||||
0x50,0x53,0x54,0x41,0x61,0x7A,0x61,0x0A, /* 00000FF8 "PSTAaza." */
|
||||
0x04,0x53,0x4C,0x54,0x5F,0x7B,0x61,0x0A, /* 00001000 ".SLT_{a." */
|
||||
0x0F,0x45,0x56,0x54,0x5F,0x70,0x53,0x4C, /* 00001008 ".EVT_pSL" */
|
||||
0x54,0x5F,0x44,0x50,0x54,0x31,0x70,0x45, /* 00001010 "T_DPT1pE" */
|
||||
0x56,0x54,0x5F,0x44,0x50,0x54,0x32,0xA0, /* 00001018 "VT_DPT2." */
|
||||
0x1B,0x93,0x53,0x4C,0x54,0x5F,0x01,0x86, /* 00001020 "..SLT_.." */
|
||||
0x5C,0x2F,0x03,0x5F,0x53,0x42,0x5F,0x50, /* 00001028 "\/._SB_P" */
|
||||
0x43,0x49,0x30,0x53,0x31,0x46,0x30,0x45, /* 00001030 "CI0S1F0E" */
|
||||
0x56,0x54,0x5F,0xA1,0x1E,0xA0,0x1C,0x93, /* 00001038 "VT_....." */
|
||||
0x53,0x4C,0x54,0x5F,0x0A,0x02,0x86,0x5C, /* 00001040 "SLT_...\" */
|
||||
0x2F,0x03,0x5F,0x53,0x42,0x5F,0x50,0x43, /* 00001048 "/._SB_PC" */
|
||||
0x49,0x30,0x53,0x32,0x46,0x30,0x45,0x56, /* 00001050 "I0S2F0EV" */
|
||||
0x54,0x5F,
|
||||
};
|
||||
int DsdtLen=sizeof(AmlCode);
|
||||
@@ -59,9 +59,11 @@ struct acpi_20_fadt Fadt = {
|
||||
.pm1a_evt_blk = ACPI_PM1A_EVT_BLK_ADDRESS,
|
||||
.pm1a_cnt_blk = ACPI_PM1A_CNT_BLK_ADDRESS,
|
||||
.pm_tmr_blk = ACPI_PM_TMR_BLK_ADDRESS,
|
||||
.gpe0_blk = ACPI_GPE0_BLK_ADDRESS,
|
||||
.pm1_evt_len = ACPI_PM1A_EVT_BLK_BIT_WIDTH / 8,
|
||||
.pm1_cnt_len = ACPI_PM1A_CNT_BLK_BIT_WIDTH / 8,
|
||||
.pm_tmr_len = ACPI_PM_TMR_BLK_BIT_WIDTH / 8,
|
||||
.gpe0_blk_len = ACPI_GPE0_BLK_LEN,
|
||||
|
||||
.p_lvl2_lat = 0x0fff, /* >100, means we do not support C2 state */
|
||||
.p_lvl3_lat = 0x0fff, /* >1000, means we do not support C3 state */
|
||||
|
||||
+329
-12
@@ -29,33 +29,161 @@
|
||||
|
||||
extern FILE *logfile;
|
||||
|
||||
struct php_dev {
|
||||
struct pt_dev *pt_dev;
|
||||
uint8_t valid;
|
||||
uint8_t r_bus;
|
||||
uint8_t r_dev;
|
||||
uint8_t r_func;
|
||||
};
|
||||
struct dpci_infos {
|
||||
|
||||
struct php_dev php_devs[PHP_SLOT_LEN];
|
||||
|
||||
PCIBus *e_bus;
|
||||
struct pci_access *pci_access;
|
||||
|
||||
} dpci_infos;
|
||||
|
||||
static int token_value(char *token)
|
||||
{
|
||||
token = strchr(token, 'x') + 1;
|
||||
return strtol(token, NULL, 16);
|
||||
}
|
||||
|
||||
static int next_bdf(char **str, int *seg, int *bus, int *dev, int *func)
|
||||
{
|
||||
char *token;
|
||||
char *token, *delim = ":.-";
|
||||
|
||||
if ( !(*str) || !strchr(*str, ',') )
|
||||
if ( !(*str) ||
|
||||
( !strchr(*str, ':') && !strchr(*str, '.')) )
|
||||
return 0;
|
||||
|
||||
token = *str;
|
||||
*seg = token_value(token);
|
||||
token = strchr(token, ',') + 1;
|
||||
token = strsep(str, delim);
|
||||
*seg = token_value(token);
|
||||
|
||||
token = strsep(str, delim);
|
||||
*bus = token_value(token);
|
||||
token = strchr(token, ',') + 1;
|
||||
|
||||
token = strsep(str, delim);
|
||||
*dev = token_value(token);
|
||||
token = strchr(token, ',') + 1;
|
||||
|
||||
token = strsep(str, delim);
|
||||
*func = token_value(token);
|
||||
token = strchr(token, ',');
|
||||
*str = token ? token + 1 : NULL;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Insert a new pass-through device into a specific pci slot.
|
||||
* input dom:bus:dev.func@slot, chose free one if slot == 0
|
||||
* return -1: required slot not available
|
||||
* 0: no free hotplug slots, but normal slot should okay
|
||||
* >0: the new hotplug slot
|
||||
*/
|
||||
static int __insert_to_pci_slot(int bus, int dev, int func, int slot)
|
||||
{
|
||||
int i, php_slot;
|
||||
|
||||
/* preferred virt pci slot */
|
||||
if ( slot >= PHP_SLOT_START && slot < PHP_SLOT_END )
|
||||
{
|
||||
php_slot = PCI_TO_PHP_SLOT(slot);
|
||||
if ( !dpci_infos.php_devs[php_slot].valid )
|
||||
{
|
||||
goto found;
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( slot != 0 )
|
||||
return -1;
|
||||
|
||||
/* slot == 0, pick up a free one */
|
||||
for ( i = 0; i < PHP_SLOT_LEN; i++ )
|
||||
{
|
||||
if ( !dpci_infos.php_devs[i].valid )
|
||||
{
|
||||
php_slot = i;
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return 0;
|
||||
|
||||
found:
|
||||
dpci_infos.php_devs[php_slot].valid = 1;
|
||||
dpci_infos.php_devs[php_slot].r_bus = bus;
|
||||
dpci_infos.php_devs[php_slot].r_dev = dev;
|
||||
dpci_infos.php_devs[php_slot].r_func = func;
|
||||
return PHP_TO_PCI_SLOT(php_slot);
|
||||
}
|
||||
|
||||
/* Insert a new pass-through device into a specific pci slot.
|
||||
* input dom:bus:dev.func@slot
|
||||
*/
|
||||
int insert_to_pci_slot(char *bdf_slt)
|
||||
{
|
||||
int seg, bus, dev, func, slot;
|
||||
char *bdf_str, *slt_str, *delim="@";
|
||||
|
||||
bdf_str = strsep(&bdf_slt, delim);
|
||||
slt_str = bdf_slt;
|
||||
slot = token_value(slt_str);
|
||||
|
||||
if ( !next_bdf(&bdf_str, &seg, &bus, &dev, &func))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return __insert_to_pci_slot(bus, dev, func, slot);
|
||||
|
||||
}
|
||||
|
||||
/* Test if a pci slot has a device
|
||||
* 1: present
|
||||
* 0: not present
|
||||
* -1: invalide pci slot input
|
||||
*/
|
||||
int test_pci_slot(int slot)
|
||||
{
|
||||
int php_slot;
|
||||
|
||||
if ( slot < PHP_SLOT_START || slot >= PHP_SLOT_END )
|
||||
return -1;
|
||||
|
||||
php_slot = PCI_TO_PHP_SLOT(slot);
|
||||
if ( dpci_infos.php_devs[php_slot].valid )
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* find the pci slot for pass-through dev with specified BDF */
|
||||
int bdf_to_slot(char *bdf_str)
|
||||
{
|
||||
int seg, bus, dev, func, i;
|
||||
|
||||
if ( !next_bdf(&bdf_str, &seg, &bus, &dev, &func))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* locate the virtual pci slot for this VTd device */
|
||||
for ( i = 0; i < PHP_SLOT_LEN; i++ )
|
||||
{
|
||||
if ( dpci_infos.php_devs[i].valid &&
|
||||
dpci_infos.php_devs[i].r_bus == bus &&
|
||||
dpci_infos.php_devs[i].r_dev == dev &&
|
||||
dpci_infos.php_devs[i].r_func == func )
|
||||
{
|
||||
return PHP_TO_PCI_SLOT(i);
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Being called each time a mmio region has been updated */
|
||||
void pt_iomem_map(PCIDevice *d, int i, uint32_t e_phys, uint32_t e_size,
|
||||
int type)
|
||||
@@ -269,15 +397,64 @@ static int pt_register_regions(struct pt_dev *assigned_device)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pt_unregister_regions(struct pt_dev *assigned_device)
|
||||
{
|
||||
int i, type, ret;
|
||||
uint32_t e_size;
|
||||
PCIDevice *d = (PCIDevice*)assigned_device;
|
||||
|
||||
for ( i = 0; i < PCI_NUM_REGIONS; i++ )
|
||||
{
|
||||
e_size = assigned_device->bases[i].e_size;
|
||||
if ( e_size == 0 )
|
||||
continue;
|
||||
|
||||
type = d->io_regions[i].type;
|
||||
|
||||
if ( type == PCI_ADDRESS_SPACE_MEM ||
|
||||
type == PCI_ADDRESS_SPACE_MEM_PREFETCH )
|
||||
{
|
||||
ret = xc_domain_memory_mapping(xc_handle, domid,
|
||||
assigned_device->bases[i].e_physbase >> XC_PAGE_SHIFT,
|
||||
assigned_device->bases[i].access.maddr >> XC_PAGE_SHIFT,
|
||||
(e_size+XC_PAGE_SIZE-1) >> XC_PAGE_SHIFT,
|
||||
DPCI_REMOVE_MAPPING);
|
||||
if ( ret != 0 )
|
||||
{
|
||||
PT_LOG("Error: remove old mem mapping failed!\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
else if ( type == PCI_ADDRESS_SPACE_IO )
|
||||
{
|
||||
ret = xc_domain_ioport_mapping(xc_handle, domid,
|
||||
assigned_device->bases[i].e_physbase,
|
||||
assigned_device->bases[i].access.pio_base,
|
||||
e_size,
|
||||
DPCI_REMOVE_MAPPING);
|
||||
if ( ret != 0 )
|
||||
{
|
||||
PT_LOG("Error: remove old io mapping failed!\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct pt_dev * register_real_device(PCIBus *e_bus,
|
||||
const char *e_dev_name, int e_devfn, uint8_t r_bus, uint8_t r_dev,
|
||||
uint8_t r_func, uint32_t machine_irq, struct pci_access *pci_access)
|
||||
{
|
||||
int rc, i;
|
||||
int rc = -1, i;
|
||||
struct pt_dev *assigned_device = NULL;
|
||||
struct pci_dev *pci_dev;
|
||||
uint8_t e_device, e_intx;
|
||||
struct pci_config_cf8 machine_bdf;
|
||||
int free_pci_slot = -1;
|
||||
|
||||
PT_LOG("Assigning real physical device %02x:%02x.%x ...\n",
|
||||
r_bus, r_dev, r_func);
|
||||
@@ -296,6 +473,15 @@ struct pt_dev * register_real_device(PCIBus *e_bus,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( e_devfn == PT_VIRT_DEVFN_AUTO ) {
|
||||
/*indicate a static assignment(not hotplug), so find a free PCI hot plug slot */
|
||||
free_pci_slot = __insert_to_pci_slot(r_bus, r_dev, r_func, 0);
|
||||
if ( free_pci_slot > 0 )
|
||||
e_devfn = free_pci_slot << 3;
|
||||
else
|
||||
PT_LOG("Error: no free virtual PCI hot plug slot, thus no live migration.\n");
|
||||
}
|
||||
|
||||
/* Register device */
|
||||
assigned_device = (struct pt_dev *) pci_register_device(e_bus, e_dev_name,
|
||||
sizeof(struct pt_dev), e_devfn,
|
||||
@@ -306,8 +492,12 @@ struct pt_dev * register_real_device(PCIBus *e_bus,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( free_pci_slot > 0 )
|
||||
dpci_infos.php_devs[PCI_TO_PHP_SLOT(free_pci_slot)].pt_dev = assigned_device;
|
||||
|
||||
assigned_device->pci_dev = pci_dev;
|
||||
|
||||
|
||||
/* Assign device */
|
||||
machine_bdf.reg = 0;
|
||||
machine_bdf.bus = r_bus;
|
||||
@@ -355,11 +545,96 @@ struct pt_dev * register_real_device(PCIBus *e_bus,
|
||||
return assigned_device;
|
||||
}
|
||||
|
||||
int unregister_real_device(int php_slot)
|
||||
{
|
||||
struct php_dev *php_dev;
|
||||
struct pci_dev *pci_dev;
|
||||
uint8_t e_device, e_intx;
|
||||
struct pt_dev *assigned_device = NULL;
|
||||
uint32_t machine_irq;
|
||||
uint32_t bdf = 0;
|
||||
int rc = -1;
|
||||
|
||||
if ( php_slot < 0 || php_slot >= PHP_SLOT_LEN )
|
||||
return -1;
|
||||
|
||||
php_dev = &dpci_infos.php_devs[php_slot];
|
||||
assigned_device = php_dev->pt_dev;
|
||||
|
||||
if ( !assigned_device || !php_dev->valid )
|
||||
return -1;
|
||||
|
||||
pci_dev = assigned_device->pci_dev;
|
||||
|
||||
/* hide pci dev from qemu */
|
||||
pci_hide_device((PCIDevice*)assigned_device);
|
||||
|
||||
/* Unbind interrupt */
|
||||
e_device = (assigned_device->dev.devfn >> 3) & 0x1f;
|
||||
e_intx = assigned_device->dev.config[0x3d]-1;
|
||||
machine_irq = pci_dev->irq;
|
||||
|
||||
if ( machine_irq != 0 ) {
|
||||
rc = xc_domain_unbind_pt_irq(xc_handle, domid, machine_irq, PT_IRQ_TYPE_PCI, 0,
|
||||
e_device, e_intx, 0);
|
||||
if ( rc < 0 )
|
||||
{
|
||||
/* TBD: unregister device in case of an error */
|
||||
PT_LOG("Error: Unbinding of interrupt failed! rc=%d\n", rc);
|
||||
}
|
||||
}
|
||||
|
||||
/* unregister real device's MMIO/PIO BARs */
|
||||
pt_unregister_regions(assigned_device);
|
||||
|
||||
/* deassign the dev to dom0 */
|
||||
bdf |= (pci_dev->bus & 0xff) << 16;
|
||||
bdf |= (pci_dev->dev & 0x1f) << 11;
|
||||
bdf |= (pci_dev->func & 0x1f) << 8;
|
||||
if ( (rc = xc_deassign_device(xc_handle, domid, bdf)) != 0)
|
||||
PT_LOG("Error: Revoking the device failed! rc=%d\n", rc);
|
||||
|
||||
/* mark this slot as free */
|
||||
php_dev->valid = 0;
|
||||
php_dev->pt_dev = NULL;
|
||||
qemu_free(assigned_device);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int power_on_php_slot(int php_slot)
|
||||
{
|
||||
struct php_dev *php_dev = &dpci_infos.php_devs[php_slot];
|
||||
int pci_slot = php_slot + PHP_SLOT_START;
|
||||
struct pt_dev *pt_dev;
|
||||
pt_dev =
|
||||
register_real_device(dpci_infos.e_bus,
|
||||
"DIRECT PCI",
|
||||
pci_slot << 3,
|
||||
php_dev->r_bus,
|
||||
php_dev->r_dev,
|
||||
php_dev->r_func,
|
||||
PT_MACHINE_IRQ_AUTO,
|
||||
dpci_infos.pci_access);
|
||||
|
||||
php_dev->pt_dev = pt_dev;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int power_off_php_slot(int php_slot)
|
||||
{
|
||||
return unregister_real_device(php_slot);
|
||||
}
|
||||
|
||||
int pt_init(PCIBus *e_bus, char *direct_pci)
|
||||
{
|
||||
int seg, b, d, f;
|
||||
int seg, b, d, f, php_slot = 0;
|
||||
struct pt_dev *pt_dev;
|
||||
struct pci_access *pci_access;
|
||||
char *vslots;
|
||||
char slot_str[8];
|
||||
|
||||
/* Initialize libpci */
|
||||
pci_access = pci_alloc();
|
||||
@@ -371,6 +646,19 @@ int pt_init(PCIBus *e_bus, char *direct_pci)
|
||||
pci_init(pci_access);
|
||||
pci_scan_bus(pci_access);
|
||||
|
||||
memset(&dpci_infos, 0, sizeof(struct dpci_infos));
|
||||
dpci_infos.pci_access = pci_access;
|
||||
dpci_infos.e_bus = e_bus;
|
||||
|
||||
if ( strlen(direct_pci) == 0 ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* the virtual pci slots of all pass-through devs
|
||||
* with hex format: xx;xx...;
|
||||
*/
|
||||
vslots = qemu_mallocz ( strlen(direct_pci) / 3 );
|
||||
|
||||
/* Assign given devices to guest */
|
||||
while ( next_bdf(&direct_pci, &seg, &b, &d, &f) )
|
||||
{
|
||||
@@ -382,8 +670,37 @@ int pt_init(PCIBus *e_bus, char *direct_pci)
|
||||
PT_LOG("Error: Registration failed (%02x:%02x.%x)\n", b, d, f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Record the virtual slot info */
|
||||
if ( php_slot < PHP_SLOT_LEN &&
|
||||
dpci_infos.php_devs[php_slot].pt_dev == pt_dev )
|
||||
{
|
||||
sprintf(slot_str, "0x%x;", PHP_TO_PCI_SLOT(php_slot));
|
||||
}
|
||||
else
|
||||
sprintf(slot_str, "0x%x;", 0);
|
||||
|
||||
strcat(vslots, slot_str);
|
||||
php_slot++;
|
||||
}
|
||||
|
||||
/* Write virtual slots info to xenstore for Control panel use */
|
||||
xenstore_write_vslots(vslots);
|
||||
|
||||
qemu_free(vslots);
|
||||
|
||||
/* Success */
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pt_uninit(void)
|
||||
{
|
||||
struct pci_access *access;
|
||||
|
||||
/* clean up the libpci */
|
||||
access = dpci_infos.pci_access;
|
||||
if ( access ) {
|
||||
pci_cleanup(access);
|
||||
}
|
||||
|
||||
}
|
||||
+4
-2
@@ -945,8 +945,10 @@ static void pc_init1(uint64_t ram_size, int vga_ram_size, char *boot_device,
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PASSTHROUGH
|
||||
/* Pass-through Initialization */
|
||||
if ( pci_enabled && direct_pci )
|
||||
/* Pass-through Initialization
|
||||
* init libpci even direct_pci is null, as can hotplug a dev runtime
|
||||
*/
|
||||
if ( pci_enabled )
|
||||
{
|
||||
rc = pt_init(pci_bus, direct_pci);
|
||||
if ( rc < 0 )
|
||||
|
||||
@@ -107,7 +107,8 @@ PCIDevice *pci_register_device(PCIBus *bus, const char *name,
|
||||
|
||||
if (devfn < 0) {
|
||||
for(devfn = bus->devfn_min ; devfn < 256; devfn += 8) {
|
||||
if (!bus->devices[devfn])
|
||||
if ( !bus->devices[devfn] &&
|
||||
!( devfn >= PHP_DEVFN_START && devfn < PHP_DEVFN_END ) )
|
||||
goto found;
|
||||
}
|
||||
return NULL;
|
||||
@@ -132,6 +133,12 @@ PCIDevice *pci_register_device(PCIBus *bus, const char *name,
|
||||
return pci_dev;
|
||||
}
|
||||
|
||||
void pci_hide_device(PCIDevice *pci_dev)
|
||||
{
|
||||
PCIBus *bus = pci_dev->bus;
|
||||
bus->devices[pci_dev->devfn] = NULL;
|
||||
}
|
||||
|
||||
void pci_register_io_region(PCIDevice *pci_dev, int region_num,
|
||||
uint32_t size, int type,
|
||||
PCIMapIORegionFunc *map_func)
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
*/
|
||||
|
||||
#include "vl.h"
|
||||
#include <xen/hvm/ioreq.h>
|
||||
|
||||
/* PM1a_CNT bits, as defined in the ACPI specification. */
|
||||
#define SCI_EN (1 << 0)
|
||||
@@ -36,6 +37,19 @@
|
||||
#define SLP_TYP_S4 (6 << 10)
|
||||
#define SLP_TYP_S5 (7 << 10)
|
||||
|
||||
#define ACPI_DBG_IO_ADDR 0xb044
|
||||
#define ACPI_PHP_IO_ADDR 0x10c0
|
||||
|
||||
#define PHP_EVT_ADD 0x0
|
||||
#define PHP_EVT_REMOVE 0x3
|
||||
|
||||
#define ACPI_SCI_IRQ 9
|
||||
|
||||
/* The bit in GPE0_STS/EN to notify the pci hotplug event */
|
||||
#define ACPI_PHP_GPE_BIT 3
|
||||
|
||||
#define ACPI_PHP_SLOT_NUM PHP_SLOT_LEN
|
||||
|
||||
typedef struct AcpiDeviceState AcpiDeviceState;
|
||||
AcpiDeviceState *acpi_device_table;
|
||||
|
||||
@@ -44,6 +58,27 @@ typedef struct PCIAcpiState {
|
||||
uint16_t pm1_control; /* pm1a_ECNT_BLK */
|
||||
} PCIAcpiState;
|
||||
|
||||
typedef struct GPEState {
|
||||
/* GPE0 block */
|
||||
uint8_t gpe0_sts[ACPI_GPE0_BLK_LEN / 2];
|
||||
uint8_t gpe0_en[ACPI_GPE0_BLK_LEN / 2];
|
||||
|
||||
/* SCI IRQ level */
|
||||
uint8_t sci_asserted;
|
||||
|
||||
} GPEState;
|
||||
|
||||
GPEState gpe_state;
|
||||
|
||||
typedef struct PHPSlots {
|
||||
struct {
|
||||
uint8_t status; /* Apaptor stats */
|
||||
} slot[ACPI_PHP_SLOT_NUM];
|
||||
uint8_t plug_evt; /* slot|event slot:0-no event;1-1st. event:0-remove;1-add */
|
||||
} PHPSlots;
|
||||
|
||||
PHPSlots php_slots;
|
||||
|
||||
static void piix4acpi_save(QEMUFile *f, void *opaque)
|
||||
{
|
||||
PCIAcpiState *s = opaque;
|
||||
@@ -142,6 +177,318 @@ static void acpi_map(PCIDevice *pci_dev, int region_num,
|
||||
register_ioport_read(addr + 4, 2, 2, acpiPm1Control_readw, d);
|
||||
}
|
||||
|
||||
static inline int test_bit(uint8_t *map, int bit)
|
||||
{
|
||||
return ( map[bit / 8] & (1 << (bit % 8)) );
|
||||
}
|
||||
|
||||
static inline void set_bit(uint8_t *map, int bit)
|
||||
{
|
||||
map[bit / 8] |= (1 << (bit % 8));
|
||||
}
|
||||
|
||||
static inline void clear_bit(uint8_t *map, int bit)
|
||||
{
|
||||
map[bit / 8] &= ~(1 << (bit % 8));
|
||||
}
|
||||
|
||||
extern FILE *logfile;
|
||||
static void acpi_dbg_writel(void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
#if defined(DEBUG)
|
||||
printf("ACPI: DBG: 0x%08x\n", val);
|
||||
#endif
|
||||
fprintf(logfile, "ACPI:debug: write addr=0x%x, val=0x%x.\n", addr, val);
|
||||
}
|
||||
|
||||
/*
|
||||
* simple PCI hotplug controller IO
|
||||
* ACPI_PHP_IO_ADDR + :
|
||||
* 0 - the hotplug description: slot(|event(remove/add);
|
||||
* 1 - 1st php slot ctr/sts reg
|
||||
* 2 - 2nd php slot ctr/sts reg
|
||||
* ......
|
||||
*/
|
||||
static uint32_t acpi_php_readb(void *opaque, uint32_t addr)
|
||||
{
|
||||
PHPSlots *hotplug_slots = opaque;
|
||||
int num;
|
||||
uint32_t val;
|
||||
|
||||
switch (addr)
|
||||
{
|
||||
case ACPI_PHP_IO_ADDR:
|
||||
val = hotplug_slots->plug_evt;
|
||||
break;
|
||||
default:
|
||||
num = addr - ACPI_PHP_IO_ADDR - 1;
|
||||
val = hotplug_slots->slot[num].status;
|
||||
}
|
||||
|
||||
fprintf(logfile, "ACPI PCI hotplug: read addr=0x%x, val=0x%x.\n", addr, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
static void acpi_php_writeb(void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
PHPSlots *hotplug_slots = opaque;
|
||||
int php_slot;
|
||||
fprintf(logfile, "ACPI PCI hotplug: write addr=0x%x, val=0x%x.\n", addr, val);
|
||||
|
||||
switch (addr)
|
||||
{
|
||||
case ACPI_PHP_IO_ADDR:
|
||||
break;
|
||||
default:
|
||||
php_slot = addr - ACPI_PHP_IO_ADDR - 1;
|
||||
if ( val == 0x1 ) { /* Eject command */
|
||||
/* make _STA of the slot 0 */
|
||||
hotplug_slots->slot[php_slot].status = 0;
|
||||
|
||||
/* clear the hotplug event */
|
||||
hotplug_slots->plug_evt = 0;
|
||||
|
||||
/* power off the slot */
|
||||
power_off_php_slot(php_slot);
|
||||
|
||||
/* signal the CP ACPI hot remove done. */
|
||||
xenstore_record_dm_state("pci-removed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void pcislots_save(QEMUFile* f, void* opaque)
|
||||
{
|
||||
PHPSlots *s = (PHPSlots*)opaque;
|
||||
int i;
|
||||
for ( i = 0; i < ACPI_PHP_SLOT_NUM; i++ ) {
|
||||
qemu_put_8s( f, &s->slot[i].status);
|
||||
}
|
||||
qemu_put_8s(f, &s->plug_evt);
|
||||
}
|
||||
|
||||
static int pcislots_load(QEMUFile* f, void* opaque, int version_id)
|
||||
{
|
||||
PHPSlots *s = (PHPSlots*)opaque;
|
||||
int i;
|
||||
if (version_id != 1)
|
||||
return -EINVAL;
|
||||
for ( i = 0; i < ACPI_PHP_SLOT_NUM; i++ ) {
|
||||
qemu_get_8s( f, &s->slot[i].status);
|
||||
}
|
||||
qemu_get_8s(f, &s->plug_evt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void php_slots_init(void)
|
||||
{
|
||||
PHPSlots *slots = &php_slots;
|
||||
int i;
|
||||
memset(slots, 0, sizeof(PHPSlots));
|
||||
|
||||
/* update the pci slot status */
|
||||
for ( i = 0; i < PHP_SLOT_LEN; i++ ) {
|
||||
if ( test_pci_slot( PHP_TO_PCI_SLOT(i) ) == 1 )
|
||||
slots->slot[i].status = 0xf;
|
||||
}
|
||||
|
||||
|
||||
/* ACPI PCI hotplug controller */
|
||||
register_ioport_read(ACPI_PHP_IO_ADDR, ACPI_PHP_SLOT_NUM + 1, 1, acpi_php_readb, slots);
|
||||
register_ioport_write(ACPI_PHP_IO_ADDR, ACPI_PHP_SLOT_NUM + 1, 1, acpi_php_writeb, slots);
|
||||
register_savevm("pcislots", 0, 1, pcislots_save, pcislots_load, slots);
|
||||
}
|
||||
|
||||
/* GPEx_STS occupy 1st half of the block, while GPEx_EN 2nd half */
|
||||
static uint32_t gpe_sts_read(void *opaque, uint32_t addr)
|
||||
{
|
||||
GPEState *s = opaque;
|
||||
|
||||
return s->gpe0_sts[addr - ACPI_GPE0_BLK_ADDRESS];
|
||||
}
|
||||
|
||||
/* write 1 to clear specific GPE bits */
|
||||
static void gpe_sts_write(void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
GPEState *s = opaque;
|
||||
int hotplugged = 0;
|
||||
|
||||
fprintf(logfile, "gpe_sts_write: addr=0x%x, val=0x%x.\n", addr, val);
|
||||
|
||||
hotplugged = test_bit(&s->gpe0_sts[0], ACPI_PHP_GPE_BIT);
|
||||
s->gpe0_sts[addr - ACPI_GPE0_BLK_ADDRESS] &= ~val;
|
||||
if ( s->sci_asserted &&
|
||||
hotplugged &&
|
||||
!test_bit(&s->gpe0_sts[0], ACPI_PHP_GPE_BIT)) {
|
||||
fprintf(logfile, "Clear the GPE0_STS bit for ACPI hotplug & deassert the IRQ.\n");
|
||||
pic_set_irq(ACPI_SCI_IRQ, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static uint32_t gpe_en_read(void *opaque, uint32_t addr)
|
||||
{
|
||||
GPEState *s = opaque;
|
||||
|
||||
return s->gpe0_en[addr - (ACPI_GPE0_BLK_ADDRESS + ACPI_GPE0_BLK_LEN / 2)];
|
||||
}
|
||||
|
||||
/* write 0 to clear en bit */
|
||||
static void gpe_en_write(void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
GPEState *s = opaque;
|
||||
int reg_count;
|
||||
|
||||
fprintf(logfile, "gpe_en_write: addr=0x%x, val=0x%x.\n", addr, val);
|
||||
reg_count = addr - (ACPI_GPE0_BLK_ADDRESS + ACPI_GPE0_BLK_LEN / 2);
|
||||
s->gpe0_en[reg_count] = val;
|
||||
/* If disable GPE bit right after generating SCI on it,
|
||||
* need deassert the intr to avoid redundant intrs
|
||||
*/
|
||||
if ( s->sci_asserted &&
|
||||
reg_count == (ACPI_PHP_GPE_BIT / 8) &&
|
||||
!(val & (1 << (ACPI_PHP_GPE_BIT % 8))) ) {
|
||||
fprintf(logfile, "deassert due to disable GPE bit.\n");
|
||||
s->sci_asserted = 0;
|
||||
pic_set_irq(ACPI_SCI_IRQ, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void gpe_save(QEMUFile* f, void* opaque)
|
||||
{
|
||||
GPEState *s = (GPEState*)opaque;
|
||||
int i;
|
||||
|
||||
for ( i = 0; i < ACPI_GPE0_BLK_LEN / 2; i++ ) {
|
||||
qemu_put_8s(f, &s->gpe0_sts[i]);
|
||||
qemu_put_8s(f, &s->gpe0_en[i]);
|
||||
}
|
||||
|
||||
qemu_put_8s(f, &s->sci_asserted);
|
||||
if ( s->sci_asserted ) {
|
||||
fprintf(logfile, "gpe_save with sci asserted!\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int gpe_load(QEMUFile* f, void* opaque, int version_id)
|
||||
{
|
||||
GPEState *s = (GPEState*)opaque;
|
||||
int i;
|
||||
if (version_id != 1)
|
||||
return -EINVAL;
|
||||
|
||||
for ( i = 0; i < ACPI_GPE0_BLK_LEN / 2; i++ ) {
|
||||
qemu_get_8s(f, &s->gpe0_sts[i]);
|
||||
qemu_get_8s(f, &s->gpe0_en[i]);
|
||||
}
|
||||
|
||||
qemu_get_8s(f, &s->sci_asserted);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gpe_acpi_init(void)
|
||||
{
|
||||
GPEState *s = &gpe_state;
|
||||
memset(s, 0, sizeof(GPEState));
|
||||
|
||||
register_ioport_read(ACPI_GPE0_BLK_ADDRESS,
|
||||
ACPI_GPE0_BLK_LEN / 2,
|
||||
1,
|
||||
gpe_sts_read,
|
||||
s);
|
||||
register_ioport_read(ACPI_GPE0_BLK_ADDRESS + ACPI_GPE0_BLK_LEN / 2,
|
||||
ACPI_GPE0_BLK_LEN / 2,
|
||||
1,
|
||||
gpe_en_read,
|
||||
s);
|
||||
|
||||
register_ioport_write(ACPI_GPE0_BLK_ADDRESS,
|
||||
ACPI_GPE0_BLK_LEN / 2,
|
||||
1,
|
||||
gpe_sts_write,
|
||||
s);
|
||||
register_ioport_write(ACPI_GPE0_BLK_ADDRESS + ACPI_GPE0_BLK_LEN / 2,
|
||||
ACPI_GPE0_BLK_LEN / 2,
|
||||
1,
|
||||
gpe_en_write,
|
||||
s);
|
||||
|
||||
register_savevm("gpe", 0, 1, gpe_save, gpe_load, s);
|
||||
}
|
||||
|
||||
static void acpi_sci_intr(GPEState *s)
|
||||
{
|
||||
if ( !test_bit(&s->gpe0_sts[0], ACPI_PHP_GPE_BIT) &&
|
||||
test_bit(&s->gpe0_en[0], ACPI_PHP_GPE_BIT) ) {
|
||||
|
||||
set_bit(&s->gpe0_sts[0], ACPI_PHP_GPE_BIT);
|
||||
s->sci_asserted = 1;
|
||||
pic_set_irq(ACPI_SCI_IRQ, 1);
|
||||
fprintf(logfile, "generate a sci for PHP.\n");
|
||||
}
|
||||
}
|
||||
|
||||
void acpi_php_del(int pci_slot)
|
||||
{
|
||||
GPEState *s = &gpe_state;
|
||||
PHPSlots *hotplug_slots = &php_slots;
|
||||
int php_slot = PCI_TO_PHP_SLOT(pci_slot);
|
||||
|
||||
if ( pci_slot < PHP_SLOT_START || pci_slot >= PHP_SLOT_END ) {
|
||||
fprintf(logfile, "not find the pci slot %d when hot remove.\n", pci_slot);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* update the php controller status */
|
||||
hotplug_slots->plug_evt = (((php_slot+1) << 4) | PHP_EVT_REMOVE);
|
||||
|
||||
/* generate a SCI interrupt */
|
||||
acpi_sci_intr(s);
|
||||
}
|
||||
|
||||
void acpi_php_add(int pci_slot)
|
||||
{
|
||||
GPEState *s = &gpe_state;
|
||||
PHPSlots *hotplug_slots = &php_slots;
|
||||
int php_slot = PCI_TO_PHP_SLOT(pci_slot);
|
||||
char ret_str[30];
|
||||
|
||||
if ( pci_slot < PHP_SLOT_START || pci_slot >= PHP_SLOT_END ) {
|
||||
fprintf(logfile, "hot add pci slot %d exceed.\n", pci_slot);
|
||||
|
||||
if ( pci_slot == 0 )
|
||||
sprintf(ret_str, "no free hotplug slots");
|
||||
else if ( pci_slot == -1 )
|
||||
sprintf(ret_str, "wrong bdf or vslot");
|
||||
|
||||
if ( strlen(ret_str) > 0 )
|
||||
xenstore_record_dm("parameter", ret_str);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* update the php controller status */
|
||||
hotplug_slots->plug_evt = (((php_slot+1) << 4) | PHP_EVT_ADD);
|
||||
|
||||
/* update the slot status as present */
|
||||
hotplug_slots->slot[php_slot].status = 0xf;
|
||||
|
||||
/* power on the slot */
|
||||
power_on_php_slot(php_slot);
|
||||
|
||||
/* tell Control panel which slot for the new pass-throgh dev */
|
||||
sprintf(ret_str, "0x%x", pci_slot);
|
||||
xenstore_record_dm("parameter", ret_str);
|
||||
|
||||
/* signal the CP ACPI hot insert done */
|
||||
xenstore_record_dm_state("pci-inserted");
|
||||
|
||||
/* generate a SCI interrupt */
|
||||
acpi_sci_intr(s);
|
||||
}
|
||||
|
||||
/* PIIX4 acpi pci configuration space, func 2 */
|
||||
void pci_piix4_acpi_init(PCIBus *bus, int devfn)
|
||||
{
|
||||
@@ -181,5 +528,12 @@ void pci_piix4_acpi_init(PCIBus *bus, int devfn)
|
||||
|
||||
acpi_map((PCIDevice *)d, 0, 0x1f40, 0x10, PCI_ADDRESS_SPACE_IO);
|
||||
|
||||
gpe_acpi_init();
|
||||
|
||||
php_slots_init();
|
||||
|
||||
/* for ACPI debug */
|
||||
register_ioport_write(ACPI_DBG_IO_ADDR, 4, 4, acpi_dbg_writel, d);
|
||||
|
||||
register_savevm("piix4acpi", 0, 1, piix4acpi_save, piix4acpi_load, d);
|
||||
}
|
||||
@@ -1280,6 +1280,12 @@ static term_cmd_t term_cmds[] = {
|
||||
"device", "add USB device (e.g. 'host:bus.addr' or 'host:vendor_id:product_id')" },
|
||||
{ "usb_del", "s", do_usb_del,
|
||||
"device", "remove USB device 'bus.addr'" },
|
||||
#ifdef CONFIG_PHP_DEBUG
|
||||
{ "pci_add", "s", do_pci_add,
|
||||
"device", "insert PCI pass-through device by BDF,e.g. (dom, bus, dev, func) by hex '0x0, 0x3, 0x0, 0x0'" },
|
||||
{ "pci_del", "s", do_pci_del,
|
||||
"device", "remove PCI pass-through device by BDF,e.g. (dom, bus, dev, func) by hex '0x0, 0x3, 0x0, 0x0'" },
|
||||
#endif
|
||||
#ifndef CONFIG_DM
|
||||
{ "cpu", "i", do_cpu_set,
|
||||
"index", "set the default CPU" },
|
||||
|
||||
+20
-4
@@ -4382,6 +4382,24 @@ void usb_info(void)
|
||||
}
|
||||
}
|
||||
|
||||
void do_pci_del(char *devname)
|
||||
{
|
||||
int pci_slot;
|
||||
pci_slot = bdf_to_slot(devname);
|
||||
|
||||
acpi_php_del(pci_slot);
|
||||
}
|
||||
|
||||
void do_pci_add(char *devname)
|
||||
{
|
||||
int pci_slot;
|
||||
|
||||
pci_slot = insert_to_pci_slot(devname);
|
||||
|
||||
acpi_php_add(pci_slot);
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************/
|
||||
/* pid file */
|
||||
|
||||
@@ -7067,7 +7085,7 @@ int main(int argc, char **argv)
|
||||
#endif
|
||||
sigset_t set;
|
||||
char qemu_dm_logfilename[128];
|
||||
const char *direct_pci = NULL;
|
||||
const char *direct_pci = direct_pci_str;
|
||||
|
||||
#if !defined(__sun__) && !defined(CONFIG_STUBDOM)
|
||||
/* Maximise rlimits. Needed where default constraints are tight (*BSD). */
|
||||
@@ -7590,9 +7608,6 @@ int main(int argc, char **argv)
|
||||
case QEMU_OPTION_vncunused:
|
||||
vncunused++;
|
||||
break;
|
||||
case QEMU_OPTION_pci:
|
||||
direct_pci = optarg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7970,5 +7985,6 @@ int main(int argc, char **argv)
|
||||
|
||||
main_loop();
|
||||
quit_timers();
|
||||
pt_uninit();
|
||||
return 0;
|
||||
}
|
||||
@@ -817,11 +817,15 @@ struct PCIDevice {
|
||||
int irq_state[4];
|
||||
};
|
||||
|
||||
extern char direct_pci_str[];
|
||||
|
||||
PCIDevice *pci_register_device(PCIBus *bus, const char *name,
|
||||
int instance_size, int devfn,
|
||||
PCIConfigReadFunc *config_read,
|
||||
PCIConfigWriteFunc *config_write);
|
||||
|
||||
void pci_hide_device(PCIDevice *pci_dev);
|
||||
|
||||
void pci_register_io_region(PCIDevice *pci_dev, int region_num,
|
||||
uint32_t size, int type,
|
||||
PCIMapIORegionFunc *map_func);
|
||||
@@ -850,6 +854,22 @@ void pci_info(void);
|
||||
PCIBus *pci_bridge_init(PCIBus *bus, int devfn, uint32_t id,
|
||||
pci_map_irq_fn map_irq, const char *name);
|
||||
|
||||
/* PCI slot 6~7 support ACPI PCI hot plug */
|
||||
#define PHP_SLOT_START (6)
|
||||
#define PHP_SLOT_END (8)
|
||||
#define PHP_SLOT_LEN (PHP_SLOT_END - PHP_SLOT_START)
|
||||
#define PHP_TO_PCI_SLOT(x) (x + PHP_SLOT_START)
|
||||
#define PCI_TO_PHP_SLOT(x) (x - PHP_SLOT_START)
|
||||
#define PHP_DEVFN_START (PHP_SLOT_START << 3)
|
||||
#define PHP_DEVFN_END (PHP_SLOT_END << 3)
|
||||
|
||||
int insert_to_pci_slot(char*);
|
||||
int test_pci_slot(int);
|
||||
int bdf_to_slot(char*);
|
||||
int power_on_php_slot(int);
|
||||
int power_off_php_slot(int);
|
||||
void pt_uninit(void);
|
||||
|
||||
/* prep_pci.c */
|
||||
PCIBus *pci_prep_init(void);
|
||||
|
||||
@@ -1120,6 +1140,9 @@ void tpm_tis_init(SetIRQFunc *set_irq, void *irq_opaque, int irq);
|
||||
|
||||
/* piix4acpi.c */
|
||||
extern void pci_piix4_acpi_init(PCIBus *bus, int devfn);
|
||||
void acpi_php_add(int);
|
||||
void acpi_php_del(int);
|
||||
|
||||
|
||||
/* pc.c */
|
||||
extern QEMUMachine pc_machine;
|
||||
@@ -1320,6 +1343,9 @@ void do_usb_add(const char *devname);
|
||||
void do_usb_del(const char *devname);
|
||||
void usb_info(void);
|
||||
|
||||
void do_pci_add(char *devname);
|
||||
void do_pci_del(char *devname);
|
||||
|
||||
/* scsi-disk.c */
|
||||
enum scsi_reason {
|
||||
SCSI_REASON_DONE, /* Command complete. */
|
||||
@@ -1466,10 +1492,12 @@ void readline_start(const char *prompt, int is_password,
|
||||
void xenstore_parse_domain_config(int domid);
|
||||
int xenstore_fd(void);
|
||||
void xenstore_process_event(void *opaque);
|
||||
void xenstore_record_dm(char *subpath, char *state);
|
||||
void xenstore_record_dm_state(char *state);
|
||||
void xenstore_check_new_media_present(int timeout);
|
||||
void xenstore_write_vncport(int vnc_display);
|
||||
void xenstore_read_vncpasswd(int domid, char *pwbuf, size_t pwbuflen);
|
||||
void xenstore_write_vslots(char *vslots);
|
||||
|
||||
int xenstore_domain_has_devtype(struct xs_handle *handle,
|
||||
const char *devtype);
|
||||
|
||||
+90
-6
@@ -79,6 +79,8 @@ static void waitForDevice(char *fn)
|
||||
return;
|
||||
}
|
||||
|
||||
#define DIRECT_PCI_STR_LEN 160
|
||||
char direct_pci_str[DIRECT_PCI_STR_LEN];
|
||||
void xenstore_parse_domain_config(int domid)
|
||||
{
|
||||
char **e = NULL;
|
||||
@@ -86,7 +88,7 @@ void xenstore_parse_domain_config(int domid)
|
||||
char *fpath = NULL, *bpath = NULL,
|
||||
*dev = NULL, *params = NULL, *type = NULL, *drv = NULL;
|
||||
int i, is_scsi, is_hdN = 0;
|
||||
unsigned int len, num, hd_index;
|
||||
unsigned int len, num, hd_index, pci_devid = 0;
|
||||
BlockDriverState *bs;
|
||||
|
||||
for(i = 0; i < MAX_DISKS + MAX_SCSI_DISKS; i++)
|
||||
@@ -250,6 +252,38 @@ void xenstore_parse_domain_config(int domid)
|
||||
fprintf(logfile, "Watching %s\n", buf);
|
||||
}
|
||||
|
||||
/* get the pci pass-through parameter */
|
||||
if (pasprintf(&buf, "/local/domain/0/backend/pci/%u/%u/num_devs",
|
||||
domid, pci_devid) == -1)
|
||||
goto out;
|
||||
|
||||
free(params);
|
||||
params = xs_read(xsh, XBT_NULL, buf, &len);
|
||||
if (params == NULL)
|
||||
goto out;
|
||||
num = atoi(params);
|
||||
|
||||
for ( i = 0; i < num; i++ ) {
|
||||
if (pasprintf(&buf, "/local/domain/0/backend/pci/%u/%u/dev-%d",
|
||||
domid, pci_devid, i) != -1) {
|
||||
free(dev);
|
||||
dev = xs_read(xsh, XBT_NULL, buf, &len);
|
||||
|
||||
if ( strlen(dev) + strlen(direct_pci_str) > DIRECT_PCI_STR_LEN ) {
|
||||
fprintf(stderr, "qemu: too many pci pass-through devices\n");
|
||||
memset(direct_pci_str, 0, DIRECT_PCI_STR_LEN);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* append to direct_pci_str */
|
||||
if ( dev ) {
|
||||
strcat(direct_pci_str, dev);
|
||||
strcat(direct_pci_str, "-");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
out:
|
||||
free(type);
|
||||
free(params);
|
||||
@@ -388,7 +422,7 @@ void xenstore_process_logdirty_event(void)
|
||||
/* Accept state change commands from the control tools */
|
||||
static void xenstore_process_dm_command_event(void)
|
||||
{
|
||||
char *path = NULL, *command = NULL;
|
||||
char *path = NULL, *command = NULL, *par = NULL;
|
||||
unsigned int len;
|
||||
extern int suspend_requested;
|
||||
|
||||
@@ -407,6 +441,34 @@ static void xenstore_process_dm_command_event(void)
|
||||
} else if (!strncmp(command, "continue", len)) {
|
||||
fprintf(logfile, "dm-command: continue after state save\n");
|
||||
suspend_requested = 0;
|
||||
} else if (!strncmp(command, "pci-rem", len)) {
|
||||
fprintf(logfile, "dm-command: hot remove pass-through pci dev \n");
|
||||
|
||||
if (pasprintf(&path,
|
||||
"/local/domain/0/device-model/%u/parameter", domid) == -1) {
|
||||
fprintf(logfile, "out of memory reading dm command parameter\n");
|
||||
goto out;
|
||||
}
|
||||
par = xs_read(xsh, XBT_NULL, path, &len);
|
||||
if (!par)
|
||||
goto out;
|
||||
|
||||
do_pci_del(par);
|
||||
free(par);
|
||||
} else if (!strncmp(command, "pci-ins", len)) {
|
||||
fprintf(logfile, "dm-command: hot insert pass-through pci dev \n");
|
||||
|
||||
if (pasprintf(&path,
|
||||
"/local/domain/0/device-model/%u/parameter", domid) == -1) {
|
||||
fprintf(logfile, "out of memory reading dm command parameter\n");
|
||||
goto out;
|
||||
}
|
||||
par = xs_read(xsh, XBT_NULL, path, &len);
|
||||
if (!par)
|
||||
goto out;
|
||||
|
||||
do_pci_add(par);
|
||||
free(par);
|
||||
} else {
|
||||
fprintf(logfile, "dm-command: unknown command\"%*s\"\n", len, command);
|
||||
}
|
||||
@@ -416,22 +478,27 @@ static void xenstore_process_dm_command_event(void)
|
||||
free(command);
|
||||
}
|
||||
|
||||
void xenstore_record_dm_state(char *state)
|
||||
void xenstore_record_dm(char *subpath, char *state)
|
||||
{
|
||||
char *path = NULL;
|
||||
|
||||
if (pasprintf(&path,
|
||||
"/local/domain/0/device-model/%u/state", domid) == -1) {
|
||||
fprintf(logfile, "out of memory recording dm state\n");
|
||||
"/local/domain/0/device-model/%u/%s", domid, subpath) == -1) {
|
||||
fprintf(logfile, "out of memory recording dm \n");
|
||||
goto out;
|
||||
}
|
||||
if (!xs_write(xsh, XBT_NULL, path, state, strlen(state)))
|
||||
fprintf(logfile, "error recording dm state\n");
|
||||
fprintf(logfile, "error recording dm \n");
|
||||
|
||||
out:
|
||||
free(path);
|
||||
}
|
||||
|
||||
void xenstore_record_dm_state(char *state)
|
||||
{
|
||||
xenstore_record_dm("state", state);
|
||||
}
|
||||
|
||||
void xenstore_process_event(void *opaque)
|
||||
{
|
||||
char **vec, *offset, *bpath = NULL, *buf = NULL, *drv = NULL, *image = NULL;
|
||||
@@ -522,6 +589,23 @@ void xenstore_write_vncport(int display)
|
||||
free(buf);
|
||||
}
|
||||
|
||||
void xenstore_write_vslots(char *vslots)
|
||||
{
|
||||
char *path = NULL;
|
||||
int pci_devid = 0;
|
||||
|
||||
if (pasprintf(&path,
|
||||
"/local/domain/0/backend/pci/%u/%u/vslots", domid, pci_devid) == -1) {
|
||||
fprintf(logfile, "out of memory when updating vslots.\n");
|
||||
goto out;
|
||||
}
|
||||
if (!xs_write(xsh, XBT_NULL, path, vslots, strlen(vslots)))
|
||||
fprintf(logfile, "error updating vslots \n");
|
||||
|
||||
out:
|
||||
free(path);
|
||||
}
|
||||
|
||||
void xenstore_read_vncpasswd(int domid, char *pwbuf, size_t pwbuflen)
|
||||
{
|
||||
char *buf = NULL, *path, *uuid = NULL, *passwd = NULL;
|
||||
|
||||
@@ -762,6 +762,20 @@ int xc_test_assign_device(
|
||||
return do_domctl(xc_handle, &domctl);
|
||||
}
|
||||
|
||||
int xc_deassign_device(
|
||||
int xc_handle,
|
||||
uint32_t domid,
|
||||
uint32_t machine_bdf)
|
||||
{
|
||||
DECLARE_DOMCTL;
|
||||
|
||||
domctl.cmd = XEN_DOMCTL_deassign_device;
|
||||
domctl.domain = domid;
|
||||
domctl.u.assign_device.machine_bdf = machine_bdf;
|
||||
|
||||
return do_domctl(xc_handle, &domctl);
|
||||
}
|
||||
|
||||
/* Pass-through: binds machine irq to guests irq */
|
||||
int xc_domain_bind_pt_irq(
|
||||
int xc_handle,
|
||||
@@ -797,6 +811,36 @@ int xc_domain_bind_pt_irq(
|
||||
return rc;
|
||||
}
|
||||
|
||||
int xc_domain_unbind_pt_irq(
|
||||
int xc_handle,
|
||||
uint32_t domid,
|
||||
uint8_t machine_irq,
|
||||
uint8_t irq_type,
|
||||
uint8_t bus,
|
||||
uint8_t device,
|
||||
uint8_t intx,
|
||||
uint8_t isa_irq)
|
||||
{
|
||||
int rc;
|
||||
xen_domctl_bind_pt_irq_t * bind;
|
||||
DECLARE_DOMCTL;
|
||||
|
||||
domctl.cmd = XEN_DOMCTL_unbind_pt_irq;
|
||||
domctl.domain = (domid_t)domid;
|
||||
|
||||
bind = &(domctl.u.bind_pt_irq);
|
||||
bind->hvm_domid = domid;
|
||||
bind->irq_type = irq_type;
|
||||
bind->machine_irq = machine_irq;
|
||||
bind->u.pci.bus = bus;
|
||||
bind->u.pci.device = device;
|
||||
bind->u.pci.intx = intx;
|
||||
bind->u.isa.isa_irq = isa_irq;
|
||||
|
||||
rc = do_domctl(xc_handle, &domctl);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int xc_domain_bind_pt_pci_irq(
|
||||
int xc_handle,
|
||||
uint32_t domid,
|
||||
|
||||
@@ -914,6 +914,10 @@ int xc_test_assign_device(int xc_handle,
|
||||
uint32_t domid,
|
||||
uint32_t machine_bdf);
|
||||
|
||||
int xc_deassign_device(int xc_handle,
|
||||
uint32_t domid,
|
||||
uint32_t machine_bdf);
|
||||
|
||||
int xc_domain_memory_mapping(int xc_handle,
|
||||
uint32_t domid,
|
||||
unsigned long first_gfn,
|
||||
@@ -937,6 +941,15 @@ int xc_domain_bind_pt_irq(int xc_handle,
|
||||
uint8_t intx,
|
||||
uint8_t isa_irq);
|
||||
|
||||
int xc_domain_unbind_pt_irq(int xc_handle,
|
||||
uint32_t domid,
|
||||
uint8_t machine_irq,
|
||||
uint8_t irq_type,
|
||||
uint8_t bus,
|
||||
uint8_t device,
|
||||
uint8_t intx,
|
||||
uint8_t isa_irq);
|
||||
|
||||
int xc_domain_bind_pt_pci_irq(int xc_handle,
|
||||
uint32_t domid,
|
||||
uint8_t machine_irq,
|
||||
|
||||
@@ -516,6 +516,131 @@ class XendDomainInfo:
|
||||
asserts.isCharConvertible(key)
|
||||
self.storeDom("control/sysrq", '%c' % key)
|
||||
|
||||
def sync_pcidev_info(self):
|
||||
|
||||
if not self.info.is_hvm():
|
||||
return
|
||||
|
||||
devid = '0'
|
||||
dev_info = self._getDeviceInfo_pci(devid)
|
||||
if dev_info is None:
|
||||
return
|
||||
|
||||
# get the virtual slot info from xenstore
|
||||
dev_uuid = sxp.child_value(dev_info, 'uuid')
|
||||
pci_conf = self.info['devices'][dev_uuid][1]
|
||||
pci_devs = pci_conf['devs']
|
||||
|
||||
count = 0
|
||||
vslots = None
|
||||
while vslots is None and count < 20:
|
||||
vslots = xstransact.Read("/local/domain/0/backend/pci/%u/%s/vslots"
|
||||
% (self.getDomid(), devid))
|
||||
time.sleep(0.1)
|
||||
count += 1
|
||||
if vslots is None:
|
||||
log.error("Device model didn't tell the vslots for PCI device")
|
||||
return
|
||||
|
||||
#delete last delim
|
||||
if vslots[-1] == ";":
|
||||
vslots = vslots[:-1]
|
||||
|
||||
slot_list = vslots.split(';')
|
||||
if len(slot_list) != len(pci_devs):
|
||||
log.error("Device model's pci dev num dismatch")
|
||||
return
|
||||
|
||||
#update the vslot info
|
||||
count = 0;
|
||||
for x in pci_devs:
|
||||
x['vslt'] = slot_list[count]
|
||||
count += 1
|
||||
|
||||
|
||||
def pci_device_create(self, dev_config):
|
||||
log.debug("XendDomainInfo.pci_device_create: %s" % scrub_password(dev_config))
|
||||
|
||||
if not self.info.is_hvm():
|
||||
raise VmError("only HVM guest support pci attach")
|
||||
|
||||
#all the PCI devs share one conf node
|
||||
devid = '0'
|
||||
|
||||
dev_type = sxp.name(dev_config)
|
||||
new_devs = sxp.child_value(dev_config, 'devs')
|
||||
new_dev = new_devs[0]
|
||||
dev_info = self._getDeviceInfo_pci(devid)#from self.info['devices']
|
||||
|
||||
#check conflict before trigger hotplug event
|
||||
if dev_info is not None:
|
||||
dev_uuid = sxp.child_value(dev_info, 'uuid')
|
||||
pci_conf = self.info['devices'][dev_uuid][1]
|
||||
pci_devs = pci_conf['devs']
|
||||
for x in pci_devs:
|
||||
if (int(x['vslt'], 16) == int(new_dev['vslt'], 16) and
|
||||
int(x['vslt'], 16) != 0 ):
|
||||
raise VmError("vslot %s already have a device." % (new_dev['vslt']))
|
||||
|
||||
if (int(x['domain'], 16) == int(new_dev['domain'], 16) and
|
||||
int(x['bus'], 16) == int(new_dev['bus'], 16) and
|
||||
int(x['slot'], 16) == int(new_dev['slot'], 16) and
|
||||
int(x['func'], 16) == int(new_dev['func'], 16) ):
|
||||
raise VmError("device is already inserted")
|
||||
|
||||
# Test whether the devices can be assigned with VT-d
|
||||
pci_str = "%s, %s, %s, %s" % (new_dev['domain'],
|
||||
new_dev['bus'],
|
||||
new_dev['slot'],
|
||||
new_dev['func'])
|
||||
bdf = xc.test_assign_device(self.domid, pci_str)
|
||||
if bdf != 0:
|
||||
bus = (bdf >> 16) & 0xff
|
||||
devfn = (bdf >> 8) & 0xff
|
||||
dev = (devfn >> 3) & 0x1f
|
||||
func = devfn & 0x7
|
||||
raise VmError("Fail to hot insert device(%x:%x.%x): maybe VT-d is "
|
||||
"not enabled, or the device is not exist, or it "
|
||||
"has already been assigned to other domain"
|
||||
% (bus, dev, func))
|
||||
|
||||
bdf_str = "%s:%s:%s.%s@%s" % (new_dev['domain'],
|
||||
new_dev['bus'],
|
||||
new_dev['slot'],
|
||||
new_dev['func'],
|
||||
new_dev['vslt'])
|
||||
self.image.signalDeviceModel('pci-ins', 'pci-inserted', bdf_str)
|
||||
|
||||
# update the virtual pci slot
|
||||
vslt = xstransact.Read("/local/domain/0/device-model/%i/parameter"
|
||||
% self.getDomid())
|
||||
new_dev['vslt'] = vslt
|
||||
|
||||
if dev_info is None:
|
||||
# create a new one from scrach
|
||||
dev_cfg_sxp = [dev_type,
|
||||
['dev',
|
||||
['domain', new_dev['domain']],
|
||||
['bus', new_dev['bus']],
|
||||
['slot', new_dev['slot']],
|
||||
['func', new_dev['func']],
|
||||
['vslt', new_dev['vslt']]
|
||||
]]
|
||||
dev_uuid = self.info.device_add(dev_type, cfg_sxp = dev_cfg_sxp)
|
||||
dev_config_dict = self.info['devices'][dev_uuid][1]
|
||||
try:
|
||||
dev_config_dict['devid'] = devid = \
|
||||
self._createDevice(dev_type, dev_config_dict)
|
||||
self._waitForDevice(dev_type, devid)
|
||||
except VmError, ex:
|
||||
raise ex
|
||||
else:
|
||||
# update the pci config to add the new dev
|
||||
pci_devs.extend(new_devs)
|
||||
self._reconfigureDevice('pci', devid, pci_conf)
|
||||
|
||||
return self.getDeviceController('pci').sxpr(devid)
|
||||
|
||||
def device_create(self, dev_config):
|
||||
"""Create a new device.
|
||||
|
||||
@@ -524,6 +649,11 @@ class XendDomainInfo:
|
||||
"""
|
||||
log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config))
|
||||
dev_type = sxp.name(dev_config)
|
||||
|
||||
if dev_type == 'pci':
|
||||
rc = self.pci_device_create(dev_config)
|
||||
return rc
|
||||
|
||||
dev_uuid = self.info.device_add(dev_type, cfg_sxp = dev_config)
|
||||
dev_config_dict = self.info['devices'][dev_uuid][1]
|
||||
log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config_dict))
|
||||
@@ -584,10 +714,65 @@ class XendDomainInfo:
|
||||
for devclass in XendDevices.valid_devices():
|
||||
self.getDeviceController(devclass).waitForDevices()
|
||||
|
||||
def destroyPCIDevice(self, vslot):
|
||||
log.debug("destroyPCIDevice called %s", vslot)
|
||||
|
||||
if not self.info.is_hvm():
|
||||
raise VmError("only HVM guest support pci detach")
|
||||
|
||||
#all the PCI devs share one conf node
|
||||
devid = '0'
|
||||
vslot = int(vslot)
|
||||
dev_info = self._getDeviceInfo_pci('0')#from self.info['devices']
|
||||
dev_uuid = sxp.child_value(dev_info, 'uuid')
|
||||
|
||||
#delete the pci bdf config under the pci device
|
||||
pci_conf = self.info['devices'][dev_uuid][1]
|
||||
pci_len = len(pci_conf['devs'])
|
||||
|
||||
#find the pass-through device with the virtual slot
|
||||
devnum = 0
|
||||
for x in pci_conf['devs']:
|
||||
if int(x['vslt'], 16) == vslot:
|
||||
break
|
||||
devnum += 1
|
||||
|
||||
if devnum >= pci_len:
|
||||
raise VmError("Device @ vslot 0x%x doesn't exist." % (vslot))
|
||||
|
||||
if vslot == 0:
|
||||
raise VmError("Device @ vslot 0x%x do not support hotplug." % (vslot))
|
||||
|
||||
bdf_str = "%s:%s:%s.%s" % (x['domain'], x['bus'], x['slot'], x['func'])
|
||||
log.info("destroyPCIDevice:%s:%s!", x, bdf_str)
|
||||
|
||||
self.image.signalDeviceModel('pci-rem', 'pci-removed', bdf_str)
|
||||
|
||||
if pci_len > 1:
|
||||
del pci_conf['devs'][devnum]
|
||||
self._reconfigureDevice('pci', devid, pci_conf)
|
||||
else:
|
||||
self.getDeviceController('pci').destroyDevice(devid, True)
|
||||
del self.info['devices'][dev_uuid]
|
||||
platform = self.info['platform']
|
||||
orig_dev_num = len(platform['pci'])
|
||||
|
||||
#need remove the pci config
|
||||
#TODO:can use this to keep some info to ask high level management tools to hot insert a new passthrough dev after migration
|
||||
if orig_dev_num != 0:
|
||||
# platform['pci'] = ["%dDEVs" % orig_dev_num]
|
||||
platform['pci'] = []
|
||||
|
||||
return 0
|
||||
|
||||
def destroyDevice(self, deviceClass, devid, force = False, rm_cfg = False):
|
||||
log.debug("XendDomainInfo.destroyDevice: deviceClass = %s, device = %s",
|
||||
deviceClass, devid)
|
||||
|
||||
if deviceClass == 'dpci':
|
||||
rc = self.destroyPCIDevice(devid)
|
||||
return rc
|
||||
|
||||
if rm_cfg:
|
||||
# Convert devid to device number. A device number is
|
||||
# needed to remove its configuration.
|
||||
@@ -647,6 +832,14 @@ class XendDomainInfo:
|
||||
return rc
|
||||
|
||||
def getDeviceSxprs(self, deviceClass):
|
||||
if deviceClass == 'pci':
|
||||
dev_info = self._getDeviceInfo_pci('0')#from self.info['devices']
|
||||
if dev_info is None:
|
||||
return []
|
||||
dev_uuid = sxp.child_value(dev_info, 'uuid')
|
||||
pci_devs = self.info['devices'][dev_uuid][1]['devs']
|
||||
pci_len = len(pci_devs)
|
||||
return pci_devs
|
||||
if self._stateGet() in (DOM_STATE_RUNNING, DOM_STATE_PAUSED, DOM_STATE_CRASHED):
|
||||
return self.getDeviceController(deviceClass).sxprs()
|
||||
else:
|
||||
@@ -683,6 +876,12 @@ class XendDomainInfo:
|
||||
if devid == dev:
|
||||
return dev_info
|
||||
|
||||
def _getDeviceInfo_pci(self, devid):
|
||||
for dev_type, dev_info in self.info.all_devices_sxpr():
|
||||
if dev_type != 'pci':
|
||||
continue
|
||||
return dev_info
|
||||
return None
|
||||
|
||||
def setMemoryTarget(self, target):
|
||||
"""Set the memory target of this domain.
|
||||
@@ -1543,6 +1742,9 @@ class XendDomainInfo:
|
||||
if self.image:
|
||||
self.image.createDeviceModel()
|
||||
|
||||
#if have pass-through devs, need the virtual pci slots info from qemu
|
||||
self.sync_pcidev_info()
|
||||
|
||||
def _releaseDevices(self, suspend = False):
|
||||
"""Release all domain's devices. Nothrow guarantee."""
|
||||
if self.image:
|
||||
|
||||
@@ -300,23 +300,42 @@ class ImageHandler:
|
||||
self.vm.storeDom("image/device-model-pid", self.pid)
|
||||
log.info("device model pid: %d", self.pid)
|
||||
|
||||
def saveDeviceModel(self):
|
||||
def signalDeviceModel(self, cmd, ret, par = None):
|
||||
if self.device_model is None:
|
||||
return
|
||||
# Signal the device model to pause itself and save its state
|
||||
# Signal the device model to for action
|
||||
if cmd is '' or ret is '':
|
||||
raise VmError('need valid command and result when signal device model')
|
||||
|
||||
orig_state = xstransact.Read("/local/domain/0/device-model/%i/state"
|
||||
% self.vm.getDomid())
|
||||
|
||||
if par is not None:
|
||||
xstransact.Store("/local/domain/0/device-model/%i"
|
||||
% self.vm.getDomid(), ('parameter', par))
|
||||
|
||||
xstransact.Store("/local/domain/0/device-model/%i"
|
||||
% self.vm.getDomid(), ('command', 'save'))
|
||||
% self.vm.getDomid(), ('command', cmd))
|
||||
# Wait for confirmation. Could do this with a watch but we'd
|
||||
# still end up spinning here waiting for the watch to fire.
|
||||
state = ''
|
||||
count = 0
|
||||
while state != 'paused':
|
||||
while state != ret:
|
||||
state = xstransact.Read("/local/domain/0/device-model/%i/state"
|
||||
% self.vm.getDomid())
|
||||
time.sleep(0.1)
|
||||
count += 1
|
||||
if count > 100:
|
||||
raise VmError('Timed out waiting for device model to save')
|
||||
raise VmError('Timed out waiting for device model action')
|
||||
|
||||
#resotre orig state
|
||||
xstransact.Store("/local/domain/0/device-model/%i"
|
||||
% self.vm.getDomid(), ('state', orig_state))
|
||||
log.info("signalDeviceModel:restore dm state to %s", orig_state)
|
||||
|
||||
def saveDeviceModel(self):
|
||||
# Signal the device model to pause itself and save its state
|
||||
self.signalDeviceModel('save', 'paused')
|
||||
|
||||
def resumeDeviceModel(self):
|
||||
if self.device_model is None:
|
||||
@@ -479,7 +498,7 @@ class HVMImageHandler(ImageHandler):
|
||||
|
||||
dmargs = [ 'boot', 'fda', 'fdb', 'soundhw',
|
||||
'localtime', 'serial', 'stdvga', 'isa',
|
||||
'acpi', 'usb', 'usbdevice', 'pci' ]
|
||||
'acpi', 'usb', 'usbdevice' ]
|
||||
|
||||
for a in dmargs:
|
||||
v = vmConfig['platform'].get(a)
|
||||
|
||||
@@ -412,6 +412,14 @@ class DevController:
|
||||
return result
|
||||
|
||||
|
||||
def removeBackend(self, devid, *args):
|
||||
frontpath = self.frontendPath(devid)
|
||||
backpath = xstransact.Read(frontpath, "backend")
|
||||
if backpath:
|
||||
return xstransact.Remove(backpath, *args)
|
||||
else:
|
||||
raise VmError("Device %s not connected" % devid)
|
||||
|
||||
def readBackend(self, devid, *args):
|
||||
frontpath = self.frontendPath(devid)
|
||||
backpath = xstransact.Read(frontpath, "backend")
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
|
||||
import types
|
||||
import time
|
||||
|
||||
from xen.xend import sxp
|
||||
from xen.xend.XendError import VmError
|
||||
@@ -62,25 +63,62 @@ class PciController(DevController):
|
||||
|
||||
back = {}
|
||||
pcidevid = 0
|
||||
vslots = ""
|
||||
for pci_config in config.get('devs', []):
|
||||
domain = parse_hex(pci_config.get('domain', 0))
|
||||
bus = parse_hex(pci_config.get('bus', 0))
|
||||
slot = parse_hex(pci_config.get('slot', 0))
|
||||
func = parse_hex(pci_config.get('func', 0))
|
||||
|
||||
vslt = pci_config.get('vslt')
|
||||
if vslt is not None:
|
||||
vslots = vslots + vslt + ";"
|
||||
|
||||
self.setupDevice(domain, bus, slot, func)
|
||||
back['dev-%i' % pcidevid] = "%04x:%02x:%02x.%02x" % \
|
||||
(domain, bus, slot, func)
|
||||
pcidevid += 1
|
||||
|
||||
if vslots != "":
|
||||
back['vslots'] = vslots
|
||||
|
||||
back['num_devs']=str(pcidevid)
|
||||
back['uuid'] = config.get('uuid','')
|
||||
return (0, back, {})
|
||||
|
||||
def reconfigureDevice(self, _, config):
|
||||
"""@see DevController.reconfigureDevice"""
|
||||
#currently only support config changes by hot insert/remove pass-through dev
|
||||
#delete all the devices in xenstore
|
||||
(devid, new_back, new_front) = self.getDeviceDetails(config)
|
||||
num_devs = self.readBackend(devid, 'num_devs')
|
||||
for i in range(int(num_devs)):
|
||||
self.removeBackend(devid, 'dev-%d' % i)
|
||||
self.removeBackend(devid, 'num_devs')
|
||||
|
||||
#create new devices config
|
||||
num_devs = new_back['num_devs']
|
||||
for i in range(int(num_devs)):
|
||||
dev_no = 'dev-%d' % i
|
||||
self.writeBackend(devid, dev_no, new_back[dev_no])
|
||||
self.writeBackend(devid, 'num_devs', num_devs)
|
||||
|
||||
if new_back['vslots'] is not None:
|
||||
self.writeBackend(devid, 'vslots', new_back['vslots'])
|
||||
|
||||
return new_back.get('uuid')
|
||||
|
||||
def getDeviceConfiguration(self, devid, transaction = None):
|
||||
result = DevController.getDeviceConfiguration(self, devid, transaction)
|
||||
num_devs = self.readBackend(devid, 'num_devs')
|
||||
pci_devs = []
|
||||
|
||||
vslots = self.readBackend(devid, 'vslots')
|
||||
if vslots is not None:
|
||||
if vslots[-1] == ";":
|
||||
vslots = vslots[:-1]
|
||||
slot_list = vslots.split(';')
|
||||
|
||||
for i in range(int(num_devs)):
|
||||
dev_config = self.readBackend(devid, 'dev-%d' % i)
|
||||
|
||||
@@ -91,10 +129,16 @@ class PciController(DevController):
|
||||
|
||||
if pci_match!=None:
|
||||
pci_dev_info = pci_match.groupdict()
|
||||
pci_devs.append({'domain': '0x%(domain)s' % pci_dev_info,
|
||||
dev_dict = {'domain': '0x%(domain)s' % pci_dev_info,
|
||||
'bus': '0x%(bus)s' % pci_dev_info,
|
||||
'slot': '0x%(slot)s' % pci_dev_info,
|
||||
'func': '0x%(func)s' % pci_dev_info})
|
||||
'func': '0x%(func)s' % pci_dev_info}
|
||||
|
||||
#append vslot info
|
||||
if vslots is not None:
|
||||
dev_dict['vslt'] = slot_list[i]
|
||||
|
||||
pci_devs.append(dev_dict)
|
||||
|
||||
result['devs'] = pci_devs
|
||||
result['uuid'] = self.readBackend(devid, 'uuid')
|
||||
|
||||
@@ -175,6 +175,12 @@ SUBCOMMAND_HELP = {
|
||||
'vnet-delete' : ('<VnetId>', 'Delete a Vnet.'),
|
||||
'vnet-list' : ('[-l|--long]', 'List Vnets.'),
|
||||
'vtpm-list' : ('<Domain> [--long]', 'List virtual TPM devices.'),
|
||||
'pci-attach ' : ('<Domain> <dom> <bus> <slot> <func> [virtual slot]',
|
||||
'Insert a new pass-through pci device.'),
|
||||
'pci-detach ' : ('<Domain> <virtual slot>',
|
||||
'Remove a domain\'s pass-through pci device.'),
|
||||
'pci-list' : ('<Domain>',
|
||||
'List pass-through pci devices for a domain.'),
|
||||
|
||||
# security
|
||||
|
||||
@@ -335,6 +341,9 @@ device_commands = [
|
||||
"network-detach",
|
||||
"network-list",
|
||||
"vtpm-list",
|
||||
"pci-attach",
|
||||
"pci-detach",
|
||||
"pci-list",
|
||||
]
|
||||
|
||||
vnet_commands = [
|
||||
@@ -2051,6 +2060,31 @@ def xm_vtpm_list(args):
|
||||
% ni)
|
||||
|
||||
|
||||
def xm_pci_list(args):
|
||||
(use_long, params) = arg_check_for_resource_list(args, "pci-list")
|
||||
|
||||
dom = params[0]
|
||||
|
||||
devs = server.xend.domain.getDeviceSxprs(dom, 'pci')
|
||||
|
||||
if len(devs) == 0:
|
||||
return
|
||||
|
||||
has_vslt = devs[0].has_key('vslt')
|
||||
if has_vslt:
|
||||
hdr_str = 'VSlt domain bus slot func'
|
||||
fmt_str = "%(vslt)-3s %(domain)-3s %(bus)-3s %(slot)-3s %(func)-3s "
|
||||
else:
|
||||
hdr_str = 'domain bus slot func'
|
||||
fmt_str = "%(domain)-3s %(bus)-3s %(slot)-3s %(func)-3s "
|
||||
hdr = 0
|
||||
|
||||
for x in devs:
|
||||
if hdr == 0:
|
||||
print (hdr_str)
|
||||
hdr = 1
|
||||
print ( fmt_str % x )
|
||||
|
||||
def parse_block_configuration(args):
|
||||
dom = args[0]
|
||||
|
||||
@@ -2198,6 +2232,29 @@ def xm_network_attach(args):
|
||||
vif.append(vif_param)
|
||||
server.xend.domain.device_create(dom, vif)
|
||||
|
||||
def parse_pci_configuration(args):
|
||||
dom = args[0]
|
||||
|
||||
if len(args) == 6:
|
||||
vslt = args[5]
|
||||
else:
|
||||
vslt = '0x0' #chose a free virtual PCI slot
|
||||
|
||||
pci = ['pci',
|
||||
['devs',
|
||||
[{'domain': "0x%x" % int(args[1], 16),
|
||||
'bus': "0x%x" % int(args[2], 16),
|
||||
'slot': "0x%x" % int(args[3], 16),
|
||||
'func': "0x%x" % int(args[4], 16),
|
||||
'vslt': "0x%x" % int(vslt, 16)}]
|
||||
]]
|
||||
|
||||
return (dom, pci)
|
||||
|
||||
def xm_pci_attach(args):
|
||||
arg_check(args, 'xm_pci_attach', 5, 6)
|
||||
(dom, pci) = parse_pci_configuration(args)
|
||||
server.xend.domain.device_create(dom, pci)
|
||||
|
||||
def detach(args, deviceClass):
|
||||
rm_cfg = True
|
||||
@@ -2263,6 +2320,12 @@ def xm_network_detach(args):
|
||||
detach(args, 'vif')
|
||||
|
||||
|
||||
def xm_pci_detach(args):
|
||||
arg_check(args, 'xm_pci_detach', 2, 2)
|
||||
dom = args[0]
|
||||
dev = args[1]
|
||||
server.xend.domain.destroyDevice(dom, 'dpci', dev)
|
||||
|
||||
def xm_vnet_list(args):
|
||||
xenapi_unsupported()
|
||||
try:
|
||||
@@ -2452,6 +2515,10 @@ commands = {
|
||||
"vnet-delete": xm_vnet_delete,
|
||||
# vtpm
|
||||
"vtpm-list": xm_vtpm_list,
|
||||
#pci
|
||||
"pci-attach": xm_pci_attach,
|
||||
"pci-detach": xm_pci_detach,
|
||||
"pci-list": xm_pci_list,
|
||||
}
|
||||
|
||||
## The commands supported by a separate argument parser in xend.xm.
|
||||
|
||||
@@ -580,6 +580,34 @@ long arch_do_domctl(
|
||||
}
|
||||
break;
|
||||
|
||||
case XEN_DOMCTL_deassign_device:
|
||||
{
|
||||
struct domain *d;
|
||||
u8 bus, devfn;
|
||||
|
||||
ret = -EINVAL;
|
||||
if ( !iommu_enabled )
|
||||
break;
|
||||
|
||||
if ( unlikely((d = get_domain_by_id(domctl->domain)) == NULL) )
|
||||
{
|
||||
gdprintk(XENLOG_ERR,
|
||||
"XEN_DOMCTL_deassign_device: get_domain_by_id() failed\n");
|
||||
break;
|
||||
}
|
||||
bus = (domctl->u.assign_device.machine_bdf >> 16) & 0xff;
|
||||
devfn = (domctl->u.assign_device.machine_bdf >> 8) & 0xff;
|
||||
|
||||
if ( !device_assigned(bus, devfn) )
|
||||
break;
|
||||
|
||||
reassign_device_ownership(d, dom0, bus, devfn);
|
||||
gdprintk(XENLOG_INFO, "XEN_DOMCTL_deassign_device: bdf = %x:%x:%x\n",
|
||||
bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
|
||||
put_domain(d);
|
||||
}
|
||||
break;
|
||||
|
||||
case XEN_DOMCTL_bind_pt_irq:
|
||||
{
|
||||
struct domain * d;
|
||||
@@ -595,6 +623,23 @@ long arch_do_domctl(
|
||||
gdprintk(XENLOG_ERR, "pt_irq_create_bind failed!\n");
|
||||
rcu_unlock_domain(d);
|
||||
}
|
||||
break;
|
||||
|
||||
case XEN_DOMCTL_unbind_pt_irq:
|
||||
{
|
||||
struct domain * d;
|
||||
xen_domctl_bind_pt_irq_t * bind;
|
||||
|
||||
ret = -ESRCH;
|
||||
if ( (d = rcu_lock_domain_by_id(domctl->domain)) == NULL )
|
||||
break;
|
||||
bind = &(domctl->u.bind_pt_irq);
|
||||
if ( iommu_enabled )
|
||||
ret = pt_irq_destroy_bind_vtd(d, bind);
|
||||
if ( ret < 0 )
|
||||
gdprintk(XENLOG_ERR, "pt_irq_destroy_bind failed!\n");
|
||||
rcu_unlock_domain(d);
|
||||
}
|
||||
break;
|
||||
|
||||
case XEN_DOMCTL_memory_mapping:
|
||||
|
||||
@@ -211,8 +211,7 @@ void hvm_set_pci_link_route(struct domain *d, u8 link, u8 isa_irq)
|
||||
clear_bit(old_isa_irq, &hvm_irq->dpci->isairq_map);
|
||||
|
||||
for ( i = 0; i < NR_LINK; i++ )
|
||||
if ( test_bit(i, &hvm_irq->dpci->link_map) &&
|
||||
hvm_irq->pci_link.route[i] )
|
||||
if ( hvm_irq->dpci->link_cnt[i] && hvm_irq->pci_link.route[i] )
|
||||
set_bit(hvm_irq->pci_link.route[i],
|
||||
&hvm_irq->dpci->isairq_map);
|
||||
}
|
||||
|
||||
@@ -1441,6 +1441,8 @@ void reassign_device_ownership(
|
||||
bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
|
||||
source->domain_id, target->domain_id);
|
||||
|
||||
pdev_flr(bus, devfn);
|
||||
|
||||
for_each_pdev( source, pdev )
|
||||
{
|
||||
if ( (pdev->bus != bus) || (pdev->devfn != devfn) )
|
||||
@@ -1476,7 +1478,6 @@ void return_devices_to_dom0(struct domain *d)
|
||||
dprintk(XENLOG_INFO VTDPREFIX,
|
||||
"return_devices_to_dom0: bdf = %x:%x:%x\n",
|
||||
pdev->bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
|
||||
pdev_flr(pdev->bus, pdev->devfn);
|
||||
reassign_device_ownership(d, dom0, pdev->bus, pdev->devfn);
|
||||
}
|
||||
|
||||
@@ -1941,7 +1942,6 @@ int intel_iommu_assign_device(struct domain *d, u8 bus, u8 devfn)
|
||||
"assign_device: bus = %x dev = %x func = %x\n",
|
||||
bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
|
||||
|
||||
pdev_flr(bus, devfn);
|
||||
reassign_device_ownership(dom0, d, bus, devfn);
|
||||
|
||||
/* Setup rmrr identify mapping */
|
||||
|
||||
@@ -101,7 +101,7 @@ int pt_irq_create_bind_vtd(
|
||||
intx = pt_irq_bind->u.pci.intx;
|
||||
guest_gsi = hvm_pci_intx_gsi(device, intx);
|
||||
link = hvm_pci_intx_link(device, intx);
|
||||
set_bit(link, hvm_irq_dpci->link_map);
|
||||
hvm_irq_dpci->link_cnt[link]++;
|
||||
|
||||
digl = xmalloc(struct dev_intx_gsi_link);
|
||||
if ( !digl )
|
||||
@@ -137,6 +137,65 @@ int pt_irq_create_bind_vtd(
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pt_irq_destroy_bind_vtd(
|
||||
struct domain *d, xen_domctl_bind_pt_irq_t *pt_irq_bind)
|
||||
{
|
||||
struct hvm_irq_dpci *hvm_irq_dpci = d->arch.hvm_domain.irq.dpci;
|
||||
uint32_t machine_gsi, guest_gsi;
|
||||
uint32_t device, intx, link;
|
||||
struct list_head *digl_list, *tmp;
|
||||
struct dev_intx_gsi_link *digl;
|
||||
|
||||
if ( hvm_irq_dpci == NULL )
|
||||
return 0;
|
||||
|
||||
machine_gsi = pt_irq_bind->machine_irq;
|
||||
device = pt_irq_bind->u.pci.device;
|
||||
intx = pt_irq_bind->u.pci.intx;
|
||||
guest_gsi = hvm_pci_intx_gsi(device, intx);
|
||||
link = hvm_pci_intx_link(device, intx);
|
||||
hvm_irq_dpci->link_cnt[link]--;
|
||||
|
||||
gdprintk(XENLOG_INFO,
|
||||
"pt_irq_destroy_bind_vtd: machine_gsi=%d, guest_gsi=%d, device=%d, intx=%d.\n",
|
||||
machine_gsi, guest_gsi, device, intx);
|
||||
memset(&hvm_irq_dpci->girq[guest_gsi], 0, sizeof(struct hvm_girq_dpci_mapping));
|
||||
|
||||
/* clear the mirq info */
|
||||
if ( hvm_irq_dpci->mirq[machine_gsi].valid )
|
||||
{
|
||||
|
||||
list_for_each_safe ( digl_list, tmp,
|
||||
&hvm_irq_dpci->mirq[machine_gsi].digl_list )
|
||||
{
|
||||
digl = list_entry(digl_list,
|
||||
struct dev_intx_gsi_link, list);
|
||||
if ( digl->device == device &&
|
||||
digl->intx == intx &&
|
||||
digl->link == link &&
|
||||
digl->gsi == guest_gsi )
|
||||
{
|
||||
list_del(&digl->list);
|
||||
xfree(digl);
|
||||
}
|
||||
}
|
||||
|
||||
if ( list_empty(&hvm_irq_dpci->mirq[machine_gsi].digl_list) )
|
||||
{
|
||||
pirq_guest_unbind(d, machine_gsi);
|
||||
kill_timer(&hvm_irq_dpci->hvm_timer[irq_to_vector(machine_gsi)]);
|
||||
hvm_irq_dpci->mirq[machine_gsi].dom = NULL;
|
||||
hvm_irq_dpci->mirq[machine_gsi].valid = 0;
|
||||
}
|
||||
}
|
||||
|
||||
gdprintk(XENLOG_INFO,
|
||||
"XEN_DOMCTL_irq_unmapping: m_irq = %x device = %x intx = %x\n",
|
||||
machine_gsi, device, intx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hvm_do_IRQ_dpci(struct domain *d, unsigned int mirq)
|
||||
{
|
||||
struct hvm_irq *hvm_irq = &d->arch.hvm_domain.irq;
|
||||
|
||||
@@ -64,7 +64,7 @@ struct hvm_irq_dpci {
|
||||
/* Record of mapped ISA IRQs */
|
||||
DECLARE_BITMAP(isairq_map, NR_ISAIRQS);
|
||||
/* Record of mapped Links */
|
||||
DECLARE_BITMAP(link_map, NR_LINK);
|
||||
uint8_t link_cnt[NR_LINK];
|
||||
struct timer hvm_timer[NR_IRQS];
|
||||
};
|
||||
|
||||
|
||||
@@ -74,6 +74,9 @@ int iommu_domain_init(struct domain *d);
|
||||
void iommu_domain_destroy(struct domain *d);
|
||||
int device_assigned(u8 bus, u8 devfn);
|
||||
int assign_device(struct domain *d, u8 bus, u8 devfn);
|
||||
void reassign_device_ownership(struct domain *source,
|
||||
struct domain *target,
|
||||
u8 bus, u8 devfn);
|
||||
int iommu_map_page(struct domain *d, unsigned long gfn, unsigned long mfn);
|
||||
int iommu_unmap_page(struct domain *d, unsigned long gfn);
|
||||
void iommu_flush(struct domain *d, unsigned long gfn, u64 *p2m_entry);
|
||||
@@ -83,6 +86,8 @@ int hvm_do_IRQ_dpci(struct domain *d, unsigned int irq);
|
||||
int dpci_ioport_intercept(ioreq_t *p);
|
||||
int pt_irq_create_bind_vtd(struct domain *d,
|
||||
xen_domctl_bind_pt_irq_t *pt_irq_bind);
|
||||
int pt_irq_destroy_bind_vtd(struct domain *d,
|
||||
xen_domctl_bind_pt_irq_t *pt_irq_bind);
|
||||
unsigned int io_apic_read_remap_rte(
|
||||
unsigned int apic, unsigned int reg);
|
||||
void io_apic_write_remap_rte(unsigned int apic,
|
||||
|
||||
@@ -439,6 +439,7 @@ DEFINE_XEN_GUEST_HANDLE(xen_domctl_sendtrigger_t);
|
||||
/* Assign PCI device to HVM guest. Sets up IOMMU structures. */
|
||||
#define XEN_DOMCTL_assign_device 37
|
||||
#define XEN_DOMCTL_test_assign_device 45
|
||||
#define XEN_DOMCTL_deassign_device 47
|
||||
struct xen_domctl_assign_device {
|
||||
uint32_t machine_bdf; /* machine PCI ID of assigned device */
|
||||
};
|
||||
@@ -448,6 +449,7 @@ DEFINE_XEN_GUEST_HANDLE(xen_domctl_assign_device_t);
|
||||
|
||||
/* Pass-through interrupts: bind real irq -> hvm devfn. */
|
||||
#define XEN_DOMCTL_bind_pt_irq 38
|
||||
#define XEN_DOMCTL_unbind_pt_irq 48
|
||||
typedef enum pt_irq_type_e {
|
||||
PT_IRQ_TYPE_PCI,
|
||||
PT_IRQ_TYPE_ISA
|
||||
|
||||
@@ -118,6 +118,11 @@ struct buffered_piopage {
|
||||
#define ACPI_PM1A_EVT_BLK_ADDRESS 0x0000000000001f40
|
||||
#define ACPI_PM1A_CNT_BLK_ADDRESS (ACPI_PM1A_EVT_BLK_ADDRESS + 0x04)
|
||||
#define ACPI_PM_TMR_BLK_ADDRESS (ACPI_PM1A_EVT_BLK_ADDRESS + 0x08)
|
||||
|
||||
#define ACPI_GPE0_BLK_ADDRESS (ACPI_PM_TMR_BLK_ADDRESS + 0x20)
|
||||
|
||||
#define ACPI_GPE0_BLK_LEN 0x08
|
||||
|
||||
#endif /* defined(__i386__) || defined(__x86_64__) */
|
||||
|
||||
#endif /* _IOREQ_H_ */
|
||||
|
||||