aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMyron Stowe <mstowe@redhat.com>2011-11-07 18:23:41 -0500
committerLen Brown <len.brown@intel.com>2012-01-17 04:36:40 -0500
commit700130b41f4ee54520ac2ef2f7f1d072789711a4 (patch)
tree8fe06ea2fdce1afab67db16ca9ecf8b25e150ff5
parent6f68c91c55ea3576d366797fa8d45e31c4aa79f8 (diff)
ACPI APEI: Convert atomicio routines
APEI needs memory access in interrupt context. The obvious choice is acpi_read(), but originally it couldn't be used in interrupt context because it makes temporary mappings with ioremap(). Therefore, we added drivers/acpi/atomicio.c, which provides: acpi_pre_map_gar() -- ioremap in process context acpi_atomic_read() -- memory access in interrupt context acpi_post_unmap_gar() -- iounmap Later we added acpi_os_map_generic_address() (2971852) and enhanced acpi_read() so it works in interrupt context as long as the address has been previously mapped (620242a). Now this sequence: acpi_os_map_generic_address() -- ioremap in process context acpi_read()/apei_read() -- now OK in interrupt context acpi_os_unmap_generic_address() is equivalent to what atomicio.c provides. This patch introduces apei_read() and apei_write(), which currently are functional equivalents of acpi_read() and acpi_write(). This is mainly proactive, to prevent APEI breakages if acpi_read() and acpi_write() are ever augmented to support the 'bit_offset' field of GAS, as APEI's __apei_exec_write_register() precludes splitting up functionality related to 'bit_offset' and APEI's 'mask' (see its APEI_EXEC_PRESERVE_REGISTER block). With apei_read() and apei_write() in place, usages of atomicio routines are converted to apei_read()/apei_write() and existing calls within osl.c and the CA, based on the re-factoring that was done in an earlier patch series - http://marc.info/?l=linux-acpi&m=128769263327206&w=2: acpi_pre_map_gar() --> acpi_os_map_generic_address() acpi_post_unmap_gar() --> acpi_os_unmap_generic_address() acpi_atomic_read() --> apei_read() acpi_atomic_write() --> apei_write() Note that acpi_read() and acpi_write() currently use 'bit_width' for accessing GARs which seems incorrect. 'bit_width' is the size of the register, while 'access_width' is the size of the access the processor must generate on the bus. The 'access_width' may be larger, for example, if the hardware only supports 32-bit or 64-bit reads. I wanted to minimize any possible impacts with this patch series so I did *not* change this behavior. Signed-off-by: Myron Stowe <myron.stowe@redhat.com> Signed-off-by: Len Brown <len.brown@intel.com>
-rw-r--r--drivers/acpi/apei/apei-base.c102
-rw-r--r--drivers/acpi/apei/apei-internal.h3
-rw-r--r--drivers/acpi/apei/ghes.c10
3 files changed, 104 insertions, 11 deletions
diff --git a/drivers/acpi/apei/apei-base.c b/drivers/acpi/apei/apei-base.c
index 4abb6c74a93..e45350cb6ac 100644
--- a/drivers/acpi/apei/apei-base.c
+++ b/drivers/acpi/apei/apei-base.c
@@ -34,13 +34,13 @@
34#include <linux/module.h> 34#include <linux/module.h>
35#include <linux/init.h> 35#include <linux/init.h>
36#include <linux/acpi.h> 36#include <linux/acpi.h>
37#include <linux/acpi_io.h>
37#include <linux/slab.h> 38#include <linux/slab.h>
38#include <linux/io.h> 39#include <linux/io.h>
39#include <linux/kref.h> 40#include <linux/kref.h>
40#include <linux/rculist.h> 41#include <linux/rculist.h>
41#include <linux/interrupt.h> 42#include <linux/interrupt.h>
42#include <linux/debugfs.h> 43#include <linux/debugfs.h>
43#include <acpi/atomicio.h>
44 44
45#include "apei-internal.h" 45#include "apei-internal.h"
46 46
@@ -70,7 +70,7 @@ int __apei_exec_read_register(struct acpi_whea_header *entry, u64 *val)
70{ 70{
71 int rc; 71 int rc;
72 72
73 rc = acpi_atomic_read(val, &entry->register_region); 73 rc = apei_read(val, &entry->register_region);
74 if (rc) 74 if (rc)
75 return rc; 75 return rc;
76 *val >>= entry->register_region.bit_offset; 76 *val >>= entry->register_region.bit_offset;
@@ -116,13 +116,13 @@ int __apei_exec_write_register(struct acpi_whea_header *entry, u64 val)
116 val <<= entry->register_region.bit_offset; 116 val <<= entry->register_region.bit_offset;
117 if (entry->flags & APEI_EXEC_PRESERVE_REGISTER) { 117 if (entry->flags & APEI_EXEC_PRESERVE_REGISTER) {
118 u64 valr = 0; 118 u64 valr = 0;
119 rc = acpi_atomic_read(&valr, &entry->register_region); 119 rc = apei_read(&valr, &entry->register_region);
120 if (rc) 120 if (rc)
121 return rc; 121 return rc;
122 valr &= ~(entry->mask << entry->register_region.bit_offset); 122 valr &= ~(entry->mask << entry->register_region.bit_offset);
123 val |= valr; 123 val |= valr;
124 } 124 }
125 rc = acpi_atomic_write(val, &entry->register_region); 125 rc = apei_write(val, &entry->register_region);
126 126
127 return rc; 127 return rc;
128} 128}
@@ -243,7 +243,7 @@ static int pre_map_gar_callback(struct apei_exec_context *ctx,
243 u8 ins = entry->instruction; 243 u8 ins = entry->instruction;
244 244
245 if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER) 245 if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER)
246 return acpi_pre_map_gar(&entry->register_region); 246 return acpi_os_map_generic_address(&entry->register_region);
247 247
248 return 0; 248 return 0;
249} 249}
@@ -276,7 +276,7 @@ static int post_unmap_gar_callback(struct apei_exec_context *ctx,
276 u8 ins = entry->instruction; 276 u8 ins = entry->instruction;
277 277
278 if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER) 278 if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER)
279 acpi_post_unmap_gar(&entry->register_region); 279 acpi_os_unmap_generic_address(&entry->register_region);
280 280
281 return 0; 281 return 0;
282} 282}
@@ -591,6 +591,96 @@ static int apei_check_gar(struct acpi_generic_address *reg, u64 *paddr)
591 return 0; 591 return 0;
592} 592}
593 593
594/* read GAR in interrupt (including NMI) or process context */
595int apei_read(u64 *val, struct acpi_generic_address *reg)
596{
597 int rc;
598 u64 address;
599 u32 tmp, width = reg->bit_width;
600 acpi_status status;
601
602 rc = apei_check_gar(reg, &address);
603 if (rc)
604 return rc;
605
606 if (width == 64)
607 width = 32; /* Break into two 32-bit transfers */
608
609 *val = 0;
610 switch(reg->space_id) {
611 case ACPI_ADR_SPACE_SYSTEM_MEMORY:
612 status = acpi_os_read_memory((acpi_physical_address)
613 address, &tmp, width);
614 if (ACPI_FAILURE(status))
615 return -EIO;
616 *val = tmp;
617
618 if (reg->bit_width == 64) {
619 /* Read the top 32 bits */
620 status = acpi_os_read_memory((acpi_physical_address)
621 (address + 4), &tmp, 32);
622 if (ACPI_FAILURE(status))
623 return -EIO;
624 *val |= ((u64)tmp << 32);
625 }
626 break;
627 case ACPI_ADR_SPACE_SYSTEM_IO:
628 status = acpi_os_read_port(address, (u32 *)val, reg->bit_width);
629 if (ACPI_FAILURE(status))
630 return -EIO;
631 break;
632 default:
633 return -EINVAL;
634 }
635
636 return 0;
637}
638EXPORT_SYMBOL_GPL(apei_read);
639
640/* write GAR in interrupt (including NMI) or process context */
641int apei_write(u64 val, struct acpi_generic_address *reg)
642{
643 int rc;
644 u64 address;
645 u32 width = reg->bit_width;
646 acpi_status status;
647
648 rc = apei_check_gar(reg, &address);
649 if (rc)
650 return rc;
651
652 if (width == 64)
653 width = 32; /* Break into two 32-bit transfers */
654
655 switch (reg->space_id) {
656 case ACPI_ADR_SPACE_SYSTEM_MEMORY:
657 status = acpi_os_write_memory((acpi_physical_address)
658 address, ACPI_LODWORD(val),
659 width);
660 if (ACPI_FAILURE(status))
661 return -EIO;
662
663 if (reg->bit_width == 64) {
664 status = acpi_os_write_memory((acpi_physical_address)
665 (address + 4),
666 ACPI_HIDWORD(val), 32);
667 if (ACPI_FAILURE(status))
668 return -EIO;
669 }
670 break;
671 case ACPI_ADR_SPACE_SYSTEM_IO:
672 status = acpi_os_write_port(address, val, reg->bit_width);
673 if (ACPI_FAILURE(status))
674 return -EIO;
675 break;
676 default:
677 return -EINVAL;
678 }
679
680 return 0;
681}
682EXPORT_SYMBOL_GPL(apei_write);
683
594static int collect_res_callback(struct apei_exec_context *ctx, 684static int collect_res_callback(struct apei_exec_context *ctx,
595 struct acpi_whea_header *entry, 685 struct acpi_whea_header *entry,
596 void *data) 686 void *data)
diff --git a/drivers/acpi/apei/apei-internal.h b/drivers/acpi/apei/apei-internal.h
index d778edd34fb..cca240a3303 100644
--- a/drivers/acpi/apei/apei-internal.h
+++ b/drivers/acpi/apei/apei-internal.h
@@ -68,6 +68,9 @@ static inline int apei_exec_run_optional(struct apei_exec_context *ctx, u8 actio
68/* IP has been set in instruction function */ 68/* IP has been set in instruction function */
69#define APEI_EXEC_SET_IP 1 69#define APEI_EXEC_SET_IP 1
70 70
71int apei_read(u64 *val, struct acpi_generic_address *reg);
72int apei_write(u64 val, struct acpi_generic_address *reg);
73
71int __apei_exec_read_register(struct acpi_whea_header *entry, u64 *val); 74int __apei_exec_read_register(struct acpi_whea_header *entry, u64 *val);
72int __apei_exec_write_register(struct acpi_whea_header *entry, u64 val); 75int __apei_exec_write_register(struct acpi_whea_header *entry, u64 val);
73int apei_exec_read_register(struct apei_exec_context *ctx, 76int apei_exec_read_register(struct apei_exec_context *ctx,
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index aaf36090de1..b3207e16670 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -33,6 +33,7 @@
33#include <linux/module.h> 33#include <linux/module.h>
34#include <linux/init.h> 34#include <linux/init.h>
35#include <linux/acpi.h> 35#include <linux/acpi.h>
36#include <linux/acpi_io.h>
36#include <linux/io.h> 37#include <linux/io.h>
37#include <linux/interrupt.h> 38#include <linux/interrupt.h>
38#include <linux/timer.h> 39#include <linux/timer.h>
@@ -48,7 +49,6 @@
48#include <linux/pci.h> 49#include <linux/pci.h>
49#include <linux/aer.h> 50#include <linux/aer.h>
50#include <acpi/apei.h> 51#include <acpi/apei.h>
51#include <acpi/atomicio.h>
52#include <acpi/hed.h> 52#include <acpi/hed.h>
53#include <asm/mce.h> 53#include <asm/mce.h>
54#include <asm/tlbflush.h> 54#include <asm/tlbflush.h>
@@ -301,7 +301,7 @@ static struct ghes *ghes_new(struct acpi_hest_generic *generic)
301 if (!ghes) 301 if (!ghes)
302 return ERR_PTR(-ENOMEM); 302 return ERR_PTR(-ENOMEM);
303 ghes->generic = generic; 303 ghes->generic = generic;
304 rc = acpi_pre_map_gar(&generic->error_status_address); 304 rc = acpi_os_map_generic_address(&generic->error_status_address);
305 if (rc) 305 if (rc)
306 goto err_free; 306 goto err_free;
307 error_block_length = generic->error_block_length; 307 error_block_length = generic->error_block_length;
@@ -321,7 +321,7 @@ static struct ghes *ghes_new(struct acpi_hest_generic *generic)
321 return ghes; 321 return ghes;
322 322
323err_unmap: 323err_unmap:
324 acpi_post_unmap_gar(&generic->error_status_address); 324 acpi_os_unmap_generic_address(&generic->error_status_address);
325err_free: 325err_free:
326 kfree(ghes); 326 kfree(ghes);
327 return ERR_PTR(rc); 327 return ERR_PTR(rc);
@@ -330,7 +330,7 @@ err_free:
330static void ghes_fini(struct ghes *ghes) 330static void ghes_fini(struct ghes *ghes)
331{ 331{
332 kfree(ghes->estatus); 332 kfree(ghes->estatus);
333 acpi_post_unmap_gar(&ghes->generic->error_status_address); 333 acpi_os_unmap_generic_address(&ghes->generic->error_status_address);
334} 334}
335 335
336enum { 336enum {
@@ -401,7 +401,7 @@ static int ghes_read_estatus(struct ghes *ghes, int silent)
401 u32 len; 401 u32 len;
402 int rc; 402 int rc;
403 403
404 rc = acpi_atomic_read(&buf_paddr, &g->error_status_address); 404 rc = apei_read(&buf_paddr, &g->error_status_address);
405 if (rc) { 405 if (rc) {
406 if (!silent && printk_ratelimit()) 406 if (!silent && printk_ratelimit())
407 pr_warning(FW_WARN GHES_PFX 407 pr_warning(FW_WARN GHES_PFX