return dev_length;
}
+static inline size_t
+get_ivhd_header_size(const struct acpi_ivrs_hardware *ivhd_block)
+{
+ switch ( ivhd_block->header.type )
+ {
+ case ACPI_IVRS_TYPE_HARDWARE:
+ return offsetof(struct acpi_ivrs_hardware, efr_image);
+ case ACPI_IVRS_TYPE_HARDWARE_11H:
+ return sizeof(struct acpi_ivrs_hardware);
+ }
+ return 0;
+}
+
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;
+ size_t 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;
}
/* 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)) )
{
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;
}
+#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)
+
+static inline bool_t is_ivhd_block(u8 type)
+{
+ return (type == ACPI_IVRS_TYPE_HARDWARE ||
+ type == ACPI_IVRS_TYPE_HARDWARE_11H);
+}
+
+static inline bool_t is_ivmd_block(u8 type)
+{
+ return (type == ACPI_IVRS_TYPE_MEMORY_ALL ||
+ type == ACPI_IVRS_TYPE_MEMORY_ONE ||
+ type == ACPI_IVRS_TYPE_MEMORY_RANGE ||
+ type == ACPI_IVRS_TYPE_MEMORY_IOMMU);
+}
+
static int __init parse_ivrs_table(struct acpi_table_header *table)
{
const struct acpi_ivrs_header *ivrs_block;
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;
}
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;
}
{
const union acpi_ivhd_device *ivhd_device;
u16 block_length, dev_length;
+ size_t 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)) )
{
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;
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)
+{
+ size_t 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);
+}