Coverage Report

Created: 2017-10-25 09:10

/root/src/xen/xen/drivers/acpi/apei/apei-io.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * apei-io.c - APEI IO memory pre-mapping/post-unmapping and access
3
 *
4
 * Copyright (C) 2009-2010, Intel Corp.
5
 *  Author: Huang Ying <ying.huang@intel.com>
6
 *  Ported by: Liu, Jinsong <jinsong.liu@intel.com>
7
 *
8
 * This program is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU General Public License version
10
 * 2 as published by the Free Software Foundation.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program; If not, see <http://www.gnu.org/licenses/>.
19
 */
20
21
#include <xen/kernel.h>
22
#include <xen/errno.h>
23
#include <xen/delay.h>
24
#include <xen/string.h>
25
#include <xen/xmalloc.h>
26
#include <xen/types.h>
27
#include <xen/spinlock.h>
28
#include <xen/list.h>
29
#include <xen/cper.h>
30
#include <xen/prefetch.h>
31
#include <asm/fixmap.h>
32
#include <asm/io.h>
33
#include <acpi/acpi.h>
34
#include <acpi/apei.h>
35
36
static LIST_HEAD(apei_iomaps);
37
/*
38
 * Used for mutual exclusion between writers of apei_iomaps list, for
39
 * synchronization between readers and writer.
40
 */
41
static DEFINE_SPINLOCK(apei_iomaps_lock);
42
43
struct apei_iomap {
44
  struct list_head list;
45
  void __iomem *vaddr;
46
  unsigned long size;
47
  paddr_t paddr;
48
};
49
50
static struct apei_iomap *__apei_find_iomap(paddr_t paddr,
51
              unsigned long size)
52
18
{
53
18
  struct apei_iomap *map;
54
18
55
18
  list_for_each_entry(map, &apei_iomaps, list) {
56
17
    if (map->paddr + map->size >= paddr + size &&
57
16
        map->paddr <= paddr)
58
16
      return map;
59
17
  }
60
2
  return NULL;
61
18
}
62
63
static void __iomem *__apei_ioremap_fast(paddr_t paddr,
64
           unsigned long size)
65
18
{
66
18
  struct apei_iomap *map;
67
18
68
18
  map = __apei_find_iomap(paddr, size);
69
18
  if (map)
70
16
    return map->vaddr + (paddr - map->paddr);
71
18
  else
72
2
    return NULL;
73
18
}
74
75
static int apei_range_nr;
76
77
static void __iomem *__init apei_range_map(paddr_t paddr, unsigned long size)
78
2
{
79
2
  int i, pg;
80
2
  int start_nr, cur_nr;
81
2
82
2
  pg = ((((paddr + size -1) & PAGE_MASK)
83
2
     - (paddr & PAGE_MASK)) >> PAGE_SHIFT) + 1;
84
2
  if (apei_range_nr + pg > FIX_APEI_RANGE_MAX)
85
0
    return NULL;
86
2
87
2
  start_nr = apei_range_nr + pg -1;
88
6
  for (i = 0; i < pg; i++) {
89
4
    cur_nr = start_nr - i;
90
4
    set_fixmap_nocache(FIX_APEI_RANGE_BASE + cur_nr,
91
4
          paddr + (i << PAGE_SHIFT));
92
4
    apei_range_nr++;
93
4
  }
94
2
95
2
  return (void __iomem *)fix_to_virt(FIX_APEI_RANGE_BASE + start_nr);
96
2
}
97
98
/*
99
 * Used to pre-map the specified IO memory area. First try to find
100
 * whether the area is already pre-mapped, if it is, return; otherwise,
101
 * do the real map, and add the mapping into apei_iomaps list.
102
 */
103
void __iomem *__init apei_pre_map(paddr_t paddr, unsigned long size)
104
15
{
105
15
  void __iomem *vaddr;
106
15
  struct apei_iomap *map;
107
15
  unsigned long flags;
108
15
109
15
  spin_lock_irqsave(&apei_iomaps_lock, flags);
110
15
  vaddr = __apei_ioremap_fast(paddr, size);
111
15
  spin_unlock_irqrestore(&apei_iomaps_lock, flags);
112
15
  if (vaddr)
113
13
    return vaddr;
114
15
115
2
  map = xmalloc(struct apei_iomap);
116
2
  if (!map)
117
0
    return NULL;
118
2
119
2
  vaddr = apei_range_map(paddr, size);
120
2
  if (!vaddr) {
121
0
    xfree(map);
122
0
    return NULL;
123
0
  }
124
2
125
2
  INIT_LIST_HEAD(&map->list);
126
2
  map->paddr = paddr & PAGE_MASK;
127
2
  map->size = (((paddr + size + PAGE_SIZE -1) & PAGE_MASK)
128
2
           - (paddr & PAGE_MASK));
129
2
  map->vaddr = vaddr;
130
2
131
2
  spin_lock_irqsave(&apei_iomaps_lock, flags);
132
2
  list_add_tail(&map->list, &apei_iomaps);
133
2
  spin_unlock_irqrestore(&apei_iomaps_lock, flags);
134
2
135
2
  return map->vaddr + (paddr - map->paddr);
136
2
}
137
138
/*
139
 * Used to post-unmap the specified IO memory area.
140
 */
141
static void __init apei_post_unmap(paddr_t paddr, unsigned long size)
142
0
{
143
0
  struct apei_iomap *map;
144
0
  unsigned long flags;
145
0
146
0
  spin_lock_irqsave(&apei_iomaps_lock, flags);
147
0
  map = __apei_find_iomap(paddr, size);
148
0
  if (map)
149
0
    list_del(&map->list);
150
0
  spin_unlock_irqrestore(&apei_iomaps_lock, flags);
151
0
152
0
  xfree(map);
153
0
}
154
155
/* In NMI handler, should set silent = 1 */
156
static int apei_check_gar(struct acpi_generic_address *reg,
157
        u64 *paddr, int silent)
158
17
{
159
17
  u32 width, space_id;
160
17
161
17
  width = reg->bit_width;
162
17
  space_id = reg->space_id;
163
17
  /* Handle possible alignment issues */
164
17
  memcpy(paddr, &reg->address, sizeof(*paddr));
165
17
  if (!*paddr) {
166
0
    if (!silent)
167
0
      printk(KERN_WARNING
168
0
      "Invalid physical address in GAR\n");
169
0
    return -EINVAL;
170
0
  }
171
17
172
17
  if ((width != 8) && (width != 16) && (width != 32) && (width != 64)) {
173
0
    if (!silent)
174
0
      printk(KERN_WARNING
175
0
      "Invalid bit width in GAR\n");
176
0
    return -EINVAL;
177
0
  }
178
17
179
17
  if (space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY &&
180
0
      space_id != ACPI_ADR_SPACE_SYSTEM_IO) {
181
0
    if (!silent)
182
0
      printk(KERN_WARNING
183
0
      "Invalid address space type in GAR\n");
184
0
    return -EINVAL;
185
0
  }
186
17
187
17
  return 0;
188
17
}
189
190
/* Pre-map, working on GAR */
191
int __init apei_pre_map_gar(struct acpi_generic_address *reg)
192
15
{
193
15
  u64 paddr;
194
15
  void __iomem *vaddr;
195
15
  int rc;
196
15
197
15
  if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY)
198
1
    return 0;
199
15
200
14
  rc = apei_check_gar(reg, &paddr, 0);
201
14
  if (rc)
202
0
    return rc;
203
14
204
14
  vaddr = apei_pre_map(paddr, reg->bit_width / 8);
205
14
  if (!vaddr)
206
0
    return -EIO;
207
14
208
14
  return 0;
209
14
}
210
211
/* Post-unmap, working on GAR */
212
int __init apei_post_unmap_gar(struct acpi_generic_address *reg)
213
0
{
214
0
  u64 paddr;
215
0
  int rc;
216
0
217
0
  if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY)
218
0
    return 0;
219
0
220
0
  rc = apei_check_gar(reg, &paddr, 0);
221
0
  if (rc)
222
0
    return rc;
223
0
224
0
  apei_post_unmap(paddr, reg->bit_width / 8);
225
0
226
0
  return 0;
227
0
}
228
229
static int apei_read_mem(u64 paddr, u64 *val, u32 width)
230
3
{
231
3
  void __iomem *addr;
232
3
  u64 tmpval;
233
3
234
3
  addr = __apei_ioremap_fast(paddr, width);
235
3
  switch (width) {
236
1
  case 8:
237
1
    *val = readb(addr);
238
1
    break;
239
0
  case 16:
240
0
    *val = readw(addr);
241
0
    break;
242
1
  case 32:
243
1
    *val = readl(addr);
244
1
    break;
245
1
  case 64:
246
1
    tmpval = (u64)readl(addr);
247
1
    tmpval |= ((u64)readl(addr+4)) << 32;
248
1
    *val = tmpval;
249
1
    break;
250
0
  default:
251
0
    return -EINVAL;
252
3
  }
253
3
254
3
  return 0;
255
3
}
256
257
static int apei_write_mem(u64 paddr, u64 val, u32 width)
258
0
{
259
0
  void __iomem *addr;
260
0
  u32 tmpval;
261
0
262
0
  addr = __apei_ioremap_fast(paddr, width);
263
0
  switch (width) {
264
0
  case 8:
265
0
    writeb(val, addr);
266
0
    break;
267
0
  case 16:
268
0
    writew(val, addr);
269
0
    break;
270
0
  case 32:
271
0
    writel(val, addr);
272
0
    break;
273
0
  case 64:
274
0
    tmpval = (u32)val;
275
0
    writel(tmpval, addr);
276
0
    tmpval = (u32)(val >> 32);
277
0
    writel(tmpval, addr+4);
278
0
    break;
279
0
  default:
280
0
    return -EINVAL;
281
0
  }
282
0
283
0
  return 0;
284
0
}
285
286
int apei_read(u64 *val, struct acpi_generic_address *reg)
287
3
{
288
3
  u64 paddr;
289
3
  int rc;
290
3
291
3
  rc = apei_check_gar(reg, &paddr, 1);
292
3
  if (rc)
293
0
    return rc;
294
3
295
3
  *val = 0;
296
3
297
3
  /* currently all erst implementation take bit_width as real range */
298
3
  switch (reg->space_id) {
299
3
  case ACPI_ADR_SPACE_SYSTEM_MEMORY:
300
3
    return apei_read_mem(paddr, val, reg->bit_width);
301
0
  case ACPI_ADR_SPACE_SYSTEM_IO:
302
0
    return acpi_os_read_port(paddr, (u32 *)val, reg->bit_width);
303
0
  default:
304
0
    return -EINVAL;
305
3
  }
306
3
}
307
308
int apei_write(u64 val, struct acpi_generic_address *reg)
309
0
{
310
0
  u64 paddr;
311
0
  int rc;
312
0
313
0
  rc = apei_check_gar(reg, &paddr, 1);
314
0
  if (rc)
315
0
    return rc;
316
0
317
0
  switch (reg->space_id) {
318
0
  case ACPI_ADR_SPACE_SYSTEM_MEMORY:
319
0
    return apei_write_mem(paddr, val, reg->bit_width);
320
0
  case ACPI_ADR_SPACE_SYSTEM_IO:
321
0
    return acpi_os_write_port(paddr, val, reg->bit_width);
322
0
  default:
323
0
    return -EINVAL;
324
0
  }
325
0
}