|
@@ -558,33 +558,48 @@ void apei_resources_release(struct apei_resources *resources)
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(apei_resources_release);
|
|
|
|
|
|
-static int apei_check_gar(struct acpi_generic_address *reg, u64 *paddr)
|
|
|
+static int apei_check_gar(struct acpi_generic_address *reg, u64 *paddr,
|
|
|
+ u32 *access_bit_width)
|
|
|
{
|
|
|
- u32 width, space_id;
|
|
|
+ u32 bit_width, bit_offset, access_size_code, space_id;
|
|
|
|
|
|
- width = reg->bit_width;
|
|
|
+ bit_width = reg->bit_width;
|
|
|
+ bit_offset = reg->bit_offset;
|
|
|
+ access_size_code = reg->access_width;
|
|
|
space_id = reg->space_id;
|
|
|
/* Handle possible alignment issues */
|
|
|
memcpy(paddr, ®->address, sizeof(*paddr));
|
|
|
if (!*paddr) {
|
|
|
pr_warning(FW_BUG APEI_PFX
|
|
|
- "Invalid physical address in GAR [0x%llx/%u/%u]\n",
|
|
|
- *paddr, width, space_id);
|
|
|
+ "Invalid physical address in GAR [0x%llx/%u/%u/%u/%u]\n",
|
|
|
+ *paddr, bit_width, bit_offset, access_size_code,
|
|
|
+ space_id);
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
- if ((width != 8) && (width != 16) && (width != 32) && (width != 64)) {
|
|
|
+ if (access_size_code < 1 || access_size_code > 4) {
|
|
|
pr_warning(FW_BUG APEI_PFX
|
|
|
- "Invalid bit width in GAR [0x%llx/%u/%u]\n",
|
|
|
- *paddr, width, space_id);
|
|
|
+ "Invalid access size code in GAR [0x%llx/%u/%u/%u/%u]\n",
|
|
|
+ *paddr, bit_width, bit_offset, access_size_code,
|
|
|
+ space_id);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ *access_bit_width = 1UL << (access_size_code + 2);
|
|
|
+
|
|
|
+ if ((bit_width + bit_offset) > *access_bit_width) {
|
|
|
+ pr_warning(FW_BUG APEI_PFX
|
|
|
+ "Invalid bit width + offset in GAR [0x%llx/%u/%u/%u/%u]\n",
|
|
|
+ *paddr, bit_width, bit_offset, access_size_code,
|
|
|
+ space_id);
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
if (space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY &&
|
|
|
space_id != ACPI_ADR_SPACE_SYSTEM_IO) {
|
|
|
pr_warning(FW_BUG APEI_PFX
|
|
|
- "Invalid address space type in GAR [0x%llx/%u/%u]\n",
|
|
|
- *paddr, width, space_id);
|
|
|
+ "Invalid address space type in GAR [0x%llx/%u/%u/%u/%u]\n",
|
|
|
+ *paddr, bit_width, bit_offset, access_size_code,
|
|
|
+ space_id);
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
@@ -595,23 +610,25 @@ static int apei_check_gar(struct acpi_generic_address *reg, u64 *paddr)
|
|
|
int apei_read(u64 *val, struct acpi_generic_address *reg)
|
|
|
{
|
|
|
int rc;
|
|
|
+ u32 access_bit_width;
|
|
|
u64 address;
|
|
|
acpi_status status;
|
|
|
|
|
|
- rc = apei_check_gar(reg, &address);
|
|
|
+ rc = apei_check_gar(reg, &address, &access_bit_width);
|
|
|
if (rc)
|
|
|
return rc;
|
|
|
|
|
|
*val = 0;
|
|
|
switch(reg->space_id) {
|
|
|
case ACPI_ADR_SPACE_SYSTEM_MEMORY:
|
|
|
- status = acpi_os_read_memory((acpi_physical_address)
|
|
|
- address, val, reg->bit_width);
|
|
|
+ status = acpi_os_read_memory((acpi_physical_address) address,
|
|
|
+ val, access_bit_width);
|
|
|
if (ACPI_FAILURE(status))
|
|
|
return -EIO;
|
|
|
break;
|
|
|
case ACPI_ADR_SPACE_SYSTEM_IO:
|
|
|
- status = acpi_os_read_port(address, (u32 *)val, reg->bit_width);
|
|
|
+ status = acpi_os_read_port(address, (u32 *)val,
|
|
|
+ access_bit_width);
|
|
|
if (ACPI_FAILURE(status))
|
|
|
return -EIO;
|
|
|
break;
|
|
@@ -627,22 +644,23 @@ EXPORT_SYMBOL_GPL(apei_read);
|
|
|
int apei_write(u64 val, struct acpi_generic_address *reg)
|
|
|
{
|
|
|
int rc;
|
|
|
+ u32 access_bit_width;
|
|
|
u64 address;
|
|
|
acpi_status status;
|
|
|
|
|
|
- rc = apei_check_gar(reg, &address);
|
|
|
+ rc = apei_check_gar(reg, &address, &access_bit_width);
|
|
|
if (rc)
|
|
|
return rc;
|
|
|
|
|
|
switch (reg->space_id) {
|
|
|
case ACPI_ADR_SPACE_SYSTEM_MEMORY:
|
|
|
- status = acpi_os_write_memory((acpi_physical_address)
|
|
|
- address, val, reg->bit_width);
|
|
|
+ status = acpi_os_write_memory((acpi_physical_address) address,
|
|
|
+ val, access_bit_width);
|
|
|
if (ACPI_FAILURE(status))
|
|
|
return -EIO;
|
|
|
break;
|
|
|
case ACPI_ADR_SPACE_SYSTEM_IO:
|
|
|
- status = acpi_os_write_port(address, val, reg->bit_width);
|
|
|
+ status = acpi_os_write_port(address, val, access_bit_width);
|
|
|
if (ACPI_FAILURE(status))
|
|
|
return -EIO;
|
|
|
break;
|
|
@@ -661,23 +679,24 @@ static int collect_res_callback(struct apei_exec_context *ctx,
|
|
|
struct apei_resources *resources = data;
|
|
|
struct acpi_generic_address *reg = &entry->register_region;
|
|
|
u8 ins = entry->instruction;
|
|
|
+ u32 access_bit_width;
|
|
|
u64 paddr;
|
|
|
int rc;
|
|
|
|
|
|
if (!(ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER))
|
|
|
return 0;
|
|
|
|
|
|
- rc = apei_check_gar(reg, &paddr);
|
|
|
+ rc = apei_check_gar(reg, &paddr, &access_bit_width);
|
|
|
if (rc)
|
|
|
return rc;
|
|
|
|
|
|
switch (reg->space_id) {
|
|
|
case ACPI_ADR_SPACE_SYSTEM_MEMORY:
|
|
|
return apei_res_add(&resources->iomem, paddr,
|
|
|
- reg->bit_width / 8);
|
|
|
+ access_bit_width / 8);
|
|
|
case ACPI_ADR_SPACE_SYSTEM_IO:
|
|
|
return apei_res_add(&resources->ioport, paddr,
|
|
|
- reg->bit_width / 8);
|
|
|
+ access_bit_width / 8);
|
|
|
default:
|
|
|
return -EINVAL;
|
|
|
}
|