@@ -821,13 +821,33 @@ static u16 __init parse_ivhd_device_special(
return dev_length;
}
+static inline unsigned int
+get_ivhd_header_size(const struct acpi_ivrs_hardware *ivhd_block)
+{
+ int ret = 0;
+
+ switch ( ivhd_block->header.type )
+ {
+ case ACPI_IVRS_TYPE_HARDWARE:
+ ret = offsetof(struct acpi_ivrs_hardware, efr_image);
+ break;
+ case ACPI_IVRS_TYPE_HARDWARE_11H:
+ ret = sizeof(struct acpi_ivrs_hardware);
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
static int __init parse_ivhd_block(const struct acpi_ivrs_hardware *ivhd_block)
{
const union acpi_ivhd_device *ivhd_device;
u16 block_length, dev_length;
+ unsigned int hdr_size = get_ivhd_header_size(ivhd_block) ;
struct amd_iommu *iommu;
- if ( ivhd_block->header.length < sizeof(*ivhd_block) )
+ if ( ivhd_block->header.length < hdr_size )
{
AMD_IOMMU_DEBUG("IVHD Error: Invalid Block Length!\n");
return -ENODEV;
@@ -845,7 +865,7 @@ static int __init parse_ivhd_block(const struct acpi_ivrs_hardware *ivhd_block)
}
/* parse Device Entries */
- block_length = sizeof(*ivhd_block);
+ block_length = hdr_size;
while ( ivhd_block->header.length >=
(block_length + sizeof(struct acpi_ivrs_de_header)) )
{
@@ -914,34 +934,6 @@ static int __init parse_ivhd_block(const struct acpi_ivrs_hardware *ivhd_block)
return 0;
}
-static int __init parse_ivrs_block(const struct acpi_ivrs_header *ivrs_block)
-{
- const struct acpi_ivrs_hardware *ivhd_block;
- const struct acpi_ivrs_memory *ivmd_block;
-
- switch ( ivrs_block->type )
- {
- case ACPI_IVRS_TYPE_HARDWARE:
- ivhd_block = container_of(ivrs_block, const struct acpi_ivrs_hardware,
- header);
- return parse_ivhd_block(ivhd_block);
-
- case ACPI_IVRS_TYPE_MEMORY_ALL:
- case ACPI_IVRS_TYPE_MEMORY_ONE:
- case ACPI_IVRS_TYPE_MEMORY_RANGE:
- case ACPI_IVRS_TYPE_MEMORY_IOMMU:
- ivmd_block = container_of(ivrs_block, const struct acpi_ivrs_memory,
- header);
- return parse_ivmd_block(ivmd_block);
-
- default:
- AMD_IOMMU_DEBUG("IVRS Error: Invalid Block Type!\n");
- return -ENODEV;
- }
-
- return 0;
-}
-
static void __init dump_acpi_table_header(struct acpi_table_header *table)
{
int i;
@@ -978,6 +970,22 @@ static void __init dump_acpi_table_header(struct acpi_table_header *table)
}
+#define to_ivhd_block(hdr) \
+ container_of(hdr, const struct acpi_ivrs_hardware, header)
+#define to_ivmd_block(hdr) \
+ container_of(hdr, const struct acpi_ivrs_memory, header)
+
+#define is_ivhd_block(x) \
+ (( x <= IVHD_HIGHEST_SUPPORT_TYPE ) && \
+ (( x == ACPI_IVRS_TYPE_HARDWARE ) || \
+ ( x == ACPI_IVRS_TYPE_HARDWARE_11H )))
+
+#define is_ivmd_block(x) \
+ (( x == ACPI_IVRS_TYPE_MEMORY_ALL ) || \
+ ( x == ACPI_IVRS_TYPE_MEMORY_ONE ) || \
+ ( x == ACPI_IVRS_TYPE_MEMORY_RANGE ) || \
+ ( x == ACPI_IVRS_TYPE_MEMORY_IOMMU ))
+
static int __init parse_ivrs_table(struct acpi_table_header *table)
{
const struct acpi_ivrs_header *ivrs_block;
@@ -1010,7 +1018,10 @@ static int __init parse_ivrs_table(struct acpi_table_header *table)
return -ENODEV;
}
- error = parse_ivrs_block(ivrs_block);
+ if ( ivrs_block->type == ivhd_type )
+ error = parse_ivhd_block(to_ivhd_block(ivrs_block));
+ else if ( is_ivmd_block (ivrs_block->type) )
+ error = parse_ivmd_block(to_ivmd_block(ivrs_block));
length += ivrs_block->length;
}
@@ -1083,9 +1094,7 @@ static int __init detect_iommu_acpi(struct acpi_table_header *table)
if ( table->length < (length + ivrs_block->length) )
return -ENODEV;
if ( ivrs_block->type == ACPI_IVRS_TYPE_HARDWARE &&
- amd_iommu_detect_one_acpi(
- container_of(ivrs_block, const struct acpi_ivrs_hardware,
- header)) != 0 )
+ amd_iommu_detect_one_acpi(to_ivhd_block(ivrs_block)) != 0 )
return -ENODEV;
length += ivrs_block->length;
}
@@ -1102,15 +1111,16 @@ static int __init get_last_bdf_ivhd(
{
const union acpi_ivhd_device *ivhd_device;
u16 block_length, dev_length;
+ unsigned int hdr_size = get_ivhd_header_size(ivhd_block) ;
int last_bdf = 0;
- if ( ivhd_block->header.length < sizeof(*ivhd_block) )
+ if ( ivhd_block->header.length < hdr_size )
{
AMD_IOMMU_DEBUG("IVHD Error: Invalid Block Length!\n");
return -ENODEV;
}
- block_length = sizeof(*ivhd_block);
+ block_length = hdr_size;
while ( ivhd_block->header.length >=
(block_length + sizeof(struct acpi_ivrs_de_header)) )
{
@@ -1177,11 +1187,9 @@ static int __init get_last_bdf_acpi(struct acpi_table_header *table)
ivrs_block = (struct acpi_ivrs_header *)((u8 *)table + length);
if ( table->length < (length + ivrs_block->length) )
return -ENODEV;
- if ( ivrs_block->type == ACPI_IVRS_TYPE_HARDWARE )
+ if ( ivrs_block->type == ivhd_type )
{
- int ret = get_last_bdf_ivhd(
- container_of(ivrs_block, const struct acpi_ivrs_hardware,
- header));
+ int ret = get_last_bdf_ivhd(to_ivhd_block(ivrs_block));
if ( ret < 0 )
return ret;
@@ -1207,8 +1215,51 @@ int __init amd_iommu_get_ivrs_dev_entries(void)
int __init amd_iommu_update_ivrs_mapping_acpi(void)
{
- if ( unlikely(acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_MSI) )
- return -EPERM;
-
return acpi_table_parse(ACPI_SIG_IVRS, parse_ivrs_table);
}
+
+static int __init
+get_supported_ivhd_type(struct acpi_table_header *table)
+{
+ unsigned long length = sizeof(struct acpi_table_ivrs);
+ const struct acpi_ivrs_header *ivrs_block, *blk = NULL;
+
+ while ( table->length > (length + sizeof(*ivrs_block)) )
+ {
+ ivrs_block = (struct acpi_ivrs_header *)((u8 *)table + length);
+
+ if ( table->length < (length + ivrs_block->length) )
+ {
+ AMD_IOMMU_DEBUG("IVRS Error: "
+ "Table Length Exceeded: %#x -> %#lx\n",
+ table->length,
+ (length + ivrs_block->length));
+ return -ENODEV;
+ }
+
+ if ( is_ivhd_block(ivrs_block->type) &&
+ (!blk || blk->type < ivrs_block->type) )
+ {
+ AMD_IOMMU_DEBUG("IVRS Block: Found type %#x flags %#x len %#x id %#x\n",
+ ivrs_block->type, ivrs_block->flags,
+ ivrs_block->length, ivrs_block->device_id);
+ blk = ivrs_block;
+ }
+ length += ivrs_block->length;
+ }
+
+ if ( !blk )
+ {
+ printk(XENLOG_ERR "Cannot find supported IVHD type.\n");
+ return -ENODEV;
+ }
+
+ AMD_IOMMU_DEBUG("Using IVHD type %#x\n", blk->type);
+
+ return blk->type;
+}
+
+int __init amd_iommu_get_supported_ivhd_type(void)
+{
+ return acpi_table_parse(ACPI_SIG_IVRS, get_supported_ivhd_type);
+}
@@ -35,6 +35,7 @@ static int __initdata nr_amd_iommus;
static struct tasklet amd_iommu_irq_tasklet;
unsigned int __read_mostly ivrs_bdf_entries;
+int __read_mostly ivhd_type;
static struct radix_tree_root ivrs_maps;
struct list_head amd_iommu_head;
struct table_struct device_table;
@@ -1232,8 +1233,15 @@ int __init amd_iommu_init(void)
amd_sp5100_erratum28() )
goto error_out;
- ivrs_bdf_entries = amd_iommu_get_ivrs_dev_entries();
+ /* We implies no IOMMU if ACPI indicates no MSI. */
+ if ( unlikely(acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_MSI) )
+ goto error_out;
+ ivhd_type = amd_iommu_get_supported_ivhd_type();
+ if ( ivhd_type < 0 )
+ goto error_out;
+
+ ivrs_bdf_entries = amd_iommu_get_ivrs_dev_entries();
if ( !ivrs_bdf_entries )
goto error_out;
@@ -589,12 +589,15 @@ struct acpi_ivrs_header {
enum acpi_ivrs_type {
ACPI_IVRS_TYPE_HARDWARE = 0x10,
+ ACPI_IVRS_TYPE_HARDWARE_11H = 0x11,
ACPI_IVRS_TYPE_MEMORY_ALL /* _MEMORY1 */ = 0x20,
ACPI_IVRS_TYPE_MEMORY_ONE /* _MEMORY2 */ = 0x21,
ACPI_IVRS_TYPE_MEMORY_RANGE /* _MEMORY3 */ = 0x22,
ACPI_IVRS_TYPE_MEMORY_IOMMU = 0x23
};
+#define IVHD_HIGHEST_SUPPORT_TYPE ACPI_IVRS_TYPE_HARDWARE_11H
+
/* Masks for Flags field above for IVHD subtable */
#define ACPI_IVHD_TT_ENABLE (1)
@@ -622,7 +625,9 @@ struct acpi_ivrs_hardware {
u64 base_address; /* IOMMU control registers */
u16 pci_segment_group;
u16 info; /* MSI number and unit ID */
- u32 reserved;
+ u32 iommu_attr;
+ u64 efr_image; /* Extd feature register */
+ u64 reserved;
};
/* Masks for Info field above */
@@ -126,6 +126,7 @@ struct ivrs_mappings {
};
extern unsigned int ivrs_bdf_entries;
+extern int ivhd_type;
struct ivrs_mappings *get_ivrs_mappings(u16 seg);
int iterate_ivrs_mappings(int (*)(u16 seg, struct ivrs_mappings *));
@@ -42,6 +42,7 @@ struct acpi_ivrs_hardware;
/* amd-iommu-detect functions */
int amd_iommu_get_ivrs_dev_entries(void);
+int amd_iommu_get_supported_ivhd_type(void);
int amd_iommu_detect_one_acpi(const struct acpi_ivrs_hardware *);
int amd_iommu_detect_acpi(void);
void get_iommu_features(struct amd_iommu *iommu);