mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
IOMMU/VT-d: wire common device reserved memory API
Re-use rmrr= parameter handling code to handle common device reserved memory. Move MAX_USER_RMRR_PAGES limit enforcement to apply only to user-configured ranges, but not those from internal callers. Signed-off-by: Marek Marczykowski-Górecki <marmarek@invisiblethingslab.com> Reviewed-by: Kevin Tian <kevin.tian@intel.com> Release-acked-by: Henry Wang <Henry.Wang@arm.com>
This commit is contained in:
1 parent
0751a75e39
commit
2d9b369913
1 file changed
+130
-98
@@ -861,113 +861,136 @@ static struct user_rmrr __initdata user_rmrrs[MAX_USER_RMRR];
|
|||||||
|
|
||||||
/* Macro for RMRR inclusive range formatting. */
|
/* Macro for RMRR inclusive range formatting. */
|
||||||
#define ERMRRU_FMT "[%lx-%lx]"
|
#define ERMRRU_FMT "[%lx-%lx]"
|
||||||
#define ERMRRU_ARG(eru) eru.base_pfn, eru.end_pfn
|
#define ERMRRU_ARG base_pfn, end_pfn
|
||||||
|
|
||||||
|
/* Returns 1 on success, 0 when ignoring and < 0 on error. */
|
||||||
|
static int __init add_one_user_rmrr(unsigned long base_pfn,
|
||||||
|
unsigned long end_pfn,
|
||||||
|
unsigned int dev_count,
|
||||||
|
uint32_t *sbdf)
|
||||||
|
{
|
||||||
|
struct acpi_rmrr_unit *rmrr, *rmrru;
|
||||||
|
unsigned int idx, seg;
|
||||||
|
unsigned long base_iter;
|
||||||
|
bool overlap;
|
||||||
|
|
||||||
|
if ( iommu_verbose )
|
||||||
|
printk(XENLOG_DEBUG VTDPREFIX
|
||||||
|
"Adding RMRR for %d device ([0]: %#x) range "ERMRRU_FMT"\n",
|
||||||
|
dev_count, sbdf[0], ERMRRU_ARG);
|
||||||
|
|
||||||
|
if ( base_pfn > end_pfn )
|
||||||
|
{
|
||||||
|
printk(XENLOG_ERR VTDPREFIX
|
||||||
|
"Invalid RMRR Range "ERMRRU_FMT"\n",
|
||||||
|
ERMRRU_ARG);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
overlap = false;
|
||||||
|
list_for_each_entry(rmrru, &acpi_rmrr_units, list)
|
||||||
|
{
|
||||||
|
if ( pfn_to_paddr(base_pfn) <= rmrru->end_address &&
|
||||||
|
rmrru->base_address <= pfn_to_paddr(end_pfn) )
|
||||||
|
{
|
||||||
|
printk(XENLOG_ERR VTDPREFIX
|
||||||
|
"Overlapping RMRRs: "ERMRRU_FMT" and [%lx-%lx]\n",
|
||||||
|
ERMRRU_ARG,
|
||||||
|
paddr_to_pfn(rmrru->base_address),
|
||||||
|
paddr_to_pfn(rmrru->end_address));
|
||||||
|
overlap = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Don't add overlapping RMRR. */
|
||||||
|
if ( overlap )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
base_iter = base_pfn;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if ( !mfn_valid(_mfn(base_iter)) )
|
||||||
|
{
|
||||||
|
printk(XENLOG_ERR VTDPREFIX
|
||||||
|
"Invalid pfn in RMRR range "ERMRRU_FMT"\n",
|
||||||
|
ERMRRU_ARG);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while ( base_iter++ < end_pfn );
|
||||||
|
|
||||||
|
/* Invalid pfn in range as the loop ended before end_pfn was reached. */
|
||||||
|
if ( base_iter <= end_pfn )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
rmrr = xzalloc(struct acpi_rmrr_unit);
|
||||||
|
if ( !rmrr )
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
rmrr->scope.devices = xmalloc_array(u16, dev_count);
|
||||||
|
if ( !rmrr->scope.devices )
|
||||||
|
{
|
||||||
|
xfree(rmrr);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
seg = 0;
|
||||||
|
for ( idx = 0; idx < dev_count; idx++ )
|
||||||
|
{
|
||||||
|
rmrr->scope.devices[idx] = sbdf[idx];
|
||||||
|
seg |= PCI_SEG(sbdf[idx]);
|
||||||
|
}
|
||||||
|
if ( seg != PCI_SEG(sbdf[0]) )
|
||||||
|
{
|
||||||
|
printk(XENLOG_ERR VTDPREFIX
|
||||||
|
"Segments are not equal for RMRR range "ERMRRU_FMT"\n",
|
||||||
|
ERMRRU_ARG);
|
||||||
|
scope_devices_free(&rmrr->scope);
|
||||||
|
xfree(rmrr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
rmrr->segment = seg;
|
||||||
|
rmrr->base_address = pfn_to_paddr(base_pfn);
|
||||||
|
/* Align the end_address to the end of the page */
|
||||||
|
rmrr->end_address = pfn_to_paddr(end_pfn) | ~PAGE_MASK;
|
||||||
|
rmrr->scope.devices_cnt = dev_count;
|
||||||
|
|
||||||
|
if ( register_one_rmrr(rmrr) )
|
||||||
|
printk(XENLOG_ERR VTDPREFIX
|
||||||
|
"Could not register RMMR range "ERMRRU_FMT"\n",
|
||||||
|
ERMRRU_ARG);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static int __init add_user_rmrr(void)
|
static int __init add_user_rmrr(void)
|
||||||
{
|
{
|
||||||
struct acpi_rmrr_unit *rmrr, *rmrru;
|
unsigned int i;
|
||||||
unsigned int idx, seg, i;
|
int ret;
|
||||||
unsigned long base, end;
|
|
||||||
bool overlap;
|
|
||||||
|
|
||||||
for ( i = 0; i < nr_rmrr; i++ )
|
for ( i = 0; i < nr_rmrr; i++ )
|
||||||
{
|
{
|
||||||
base = user_rmrrs[i].base_pfn;
|
ret = add_one_user_rmrr(user_rmrrs[i].base_pfn,
|
||||||
end = user_rmrrs[i].end_pfn;
|
user_rmrrs[i].end_pfn,
|
||||||
|
user_rmrrs[i].dev_count,
|
||||||
if ( base > end )
|
user_rmrrs[i].sbdf);
|
||||||
{
|
if ( ret < 0 )
|
||||||
printk(XENLOG_ERR VTDPREFIX
|
return ret;
|
||||||
"Invalid RMRR Range "ERMRRU_FMT"\n",
|
|
||||||
ERMRRU_ARG(user_rmrrs[i]));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( (end - base) >= MAX_USER_RMRR_PAGES )
|
|
||||||
{
|
|
||||||
printk(XENLOG_ERR VTDPREFIX
|
|
||||||
"RMRR range "ERMRRU_FMT" exceeds "\
|
|
||||||
__stringify(MAX_USER_RMRR_PAGES)" pages\n",
|
|
||||||
ERMRRU_ARG(user_rmrrs[i]));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
overlap = false;
|
|
||||||
list_for_each_entry(rmrru, &acpi_rmrr_units, list)
|
|
||||||
{
|
|
||||||
if ( pfn_to_paddr(base) <= rmrru->end_address &&
|
|
||||||
rmrru->base_address <= pfn_to_paddr(end) )
|
|
||||||
{
|
|
||||||
printk(XENLOG_ERR VTDPREFIX
|
|
||||||
"Overlapping RMRRs: "ERMRRU_FMT" and [%lx-%lx]\n",
|
|
||||||
ERMRRU_ARG(user_rmrrs[i]),
|
|
||||||
paddr_to_pfn(rmrru->base_address),
|
|
||||||
paddr_to_pfn(rmrru->end_address));
|
|
||||||
overlap = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Don't add overlapping RMRR. */
|
|
||||||
if ( overlap )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if ( !mfn_valid(_mfn(base)) )
|
|
||||||
{
|
|
||||||
printk(XENLOG_ERR VTDPREFIX
|
|
||||||
"Invalid pfn in RMRR range "ERMRRU_FMT"\n",
|
|
||||||
ERMRRU_ARG(user_rmrrs[i]));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} while ( base++ < end );
|
|
||||||
|
|
||||||
/* Invalid pfn in range as the loop ended before end_pfn was reached. */
|
|
||||||
if ( base <= end )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
rmrr = xzalloc(struct acpi_rmrr_unit);
|
|
||||||
if ( !rmrr )
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
rmrr->scope.devices = xmalloc_array(u16, user_rmrrs[i].dev_count);
|
|
||||||
if ( !rmrr->scope.devices )
|
|
||||||
{
|
|
||||||
xfree(rmrr);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
seg = 0;
|
|
||||||
for ( idx = 0; idx < user_rmrrs[i].dev_count; idx++ )
|
|
||||||
{
|
|
||||||
rmrr->scope.devices[idx] = user_rmrrs[i].sbdf[idx];
|
|
||||||
seg |= PCI_SEG(user_rmrrs[i].sbdf[idx]);
|
|
||||||
}
|
|
||||||
if ( seg != PCI_SEG(user_rmrrs[i].sbdf[0]) )
|
|
||||||
{
|
|
||||||
printk(XENLOG_ERR VTDPREFIX
|
|
||||||
"Segments are not equal for RMRR range "ERMRRU_FMT"\n",
|
|
||||||
ERMRRU_ARG(user_rmrrs[i]));
|
|
||||||
scope_devices_free(&rmrr->scope);
|
|
||||||
xfree(rmrr);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
rmrr->segment = seg;
|
|
||||||
rmrr->base_address = pfn_to_paddr(user_rmrrs[i].base_pfn);
|
|
||||||
/* Align the end_address to the end of the page */
|
|
||||||
rmrr->end_address = pfn_to_paddr(user_rmrrs[i].end_pfn) | ~PAGE_MASK;
|
|
||||||
rmrr->scope.devices_cnt = user_rmrrs[i].dev_count;
|
|
||||||
|
|
||||||
if ( register_one_rmrr(rmrr) )
|
|
||||||
printk(XENLOG_ERR VTDPREFIX
|
|
||||||
"Could not register RMMR range "ERMRRU_FMT"\n",
|
|
||||||
ERMRRU_ARG(user_rmrrs[i]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int __init cf_check add_one_extra_rmrr(xen_pfn_t start, xen_ulong_t nr, u32 id, void *ctxt)
|
||||||
|
{
|
||||||
|
u32 sbdf_array[] = { id };
|
||||||
|
return add_one_user_rmrr(start, start+nr, 1, sbdf_array);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __init add_extra_rmrr(void)
|
||||||
|
{
|
||||||
|
return iommu_get_extra_reserved_device_memory(add_one_extra_rmrr, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
#include <asm/tboot.h>
|
#include <asm/tboot.h>
|
||||||
/* ACPI tables may not be DMA protected by tboot, so use DMAR copy */
|
/* ACPI tables may not be DMA protected by tboot, so use DMAR copy */
|
||||||
/* SINIT saved in SinitMleData in TXT heap (which is DMA protected) */
|
/* SINIT saved in SinitMleData in TXT heap (which is DMA protected) */
|
||||||
@@ -1010,7 +1033,7 @@ int __init acpi_dmar_init(void)
|
|||||||
{
|
{
|
||||||
iommu_init_ops = &intel_iommu_init_ops;
|
iommu_init_ops = &intel_iommu_init_ops;
|
||||||
|
|
||||||
return add_user_rmrr();
|
return add_user_rmrr() || add_extra_rmrr();
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@@ -1108,6 +1131,15 @@ static int __init cf_check parse_rmrr_param(const char *str)
|
|||||||
else
|
else
|
||||||
end = start;
|
end = start;
|
||||||
|
|
||||||
|
if ( (end - start) >= MAX_USER_RMRR_PAGES )
|
||||||
|
{
|
||||||
|
printk(XENLOG_ERR VTDPREFIX
|
||||||
|
"RMRR range "ERMRRU_FMT" exceeds "\
|
||||||
|
__stringify(MAX_USER_RMRR_PAGES)" pages\n",
|
||||||
|
start, end);
|
||||||
|
return -E2BIG;
|
||||||
|
}
|
||||||
|
|
||||||
user_rmrrs[nr_rmrr].base_pfn = start;
|
user_rmrrs[nr_rmrr].base_pfn = start;
|
||||||
user_rmrrs[nr_rmrr].end_pfn = end;
|
user_rmrrs[nr_rmrr].end_pfn = end;
|
||||||
|
|
||||||
|
|||||||