xref: /qemu/hw/acpi/erst.c (revision f7e26ffa590ff26b4c6a2c513ad9ba1e6156f5b8)
1*f7e26ffaSEric DeVolder /*
2*f7e26ffaSEric DeVolder  * ACPI Error Record Serialization Table, ERST, Implementation
3*f7e26ffaSEric DeVolder  *
4*f7e26ffaSEric DeVolder  * ACPI ERST introduced in ACPI 4.0, June 16, 2009.
5*f7e26ffaSEric DeVolder  * ACPI Platform Error Interfaces : Error Serialization
6*f7e26ffaSEric DeVolder  *
7*f7e26ffaSEric DeVolder  * Copyright (c) 2021 Oracle and/or its affiliates.
8*f7e26ffaSEric DeVolder  *
9*f7e26ffaSEric DeVolder  * SPDX-License-Identifier: GPL-2.0-or-later
10*f7e26ffaSEric DeVolder  */
11*f7e26ffaSEric DeVolder 
12*f7e26ffaSEric DeVolder #include "qemu/osdep.h"
13*f7e26ffaSEric DeVolder #include "qapi/error.h"
14*f7e26ffaSEric DeVolder #include "hw/qdev-core.h"
15*f7e26ffaSEric DeVolder #include "exec/memory.h"
16*f7e26ffaSEric DeVolder #include "qom/object.h"
17*f7e26ffaSEric DeVolder #include "hw/pci/pci.h"
18*f7e26ffaSEric DeVolder #include "qom/object_interfaces.h"
19*f7e26ffaSEric DeVolder #include "qemu/error-report.h"
20*f7e26ffaSEric DeVolder #include "migration/vmstate.h"
21*f7e26ffaSEric DeVolder #include "hw/qdev-properties.h"
22*f7e26ffaSEric DeVolder #include "hw/acpi/acpi.h"
23*f7e26ffaSEric DeVolder #include "hw/acpi/acpi-defs.h"
24*f7e26ffaSEric DeVolder #include "hw/acpi/aml-build.h"
25*f7e26ffaSEric DeVolder #include "hw/acpi/bios-linker-loader.h"
26*f7e26ffaSEric DeVolder #include "exec/address-spaces.h"
27*f7e26ffaSEric DeVolder #include "sysemu/hostmem.h"
28*f7e26ffaSEric DeVolder #include "hw/acpi/erst.h"
29*f7e26ffaSEric DeVolder #include "trace.h"
30*f7e26ffaSEric DeVolder 
31*f7e26ffaSEric DeVolder /* ACPI 4.0: Table 17-16 Serialization Actions */
32*f7e26ffaSEric DeVolder #define ACTION_BEGIN_WRITE_OPERATION         0x0
33*f7e26ffaSEric DeVolder #define ACTION_BEGIN_READ_OPERATION          0x1
34*f7e26ffaSEric DeVolder #define ACTION_BEGIN_CLEAR_OPERATION         0x2
35*f7e26ffaSEric DeVolder #define ACTION_END_OPERATION                 0x3
36*f7e26ffaSEric DeVolder #define ACTION_SET_RECORD_OFFSET             0x4
37*f7e26ffaSEric DeVolder #define ACTION_EXECUTE_OPERATION             0x5
38*f7e26ffaSEric DeVolder #define ACTION_CHECK_BUSY_STATUS             0x6
39*f7e26ffaSEric DeVolder #define ACTION_GET_COMMAND_STATUS            0x7
40*f7e26ffaSEric DeVolder #define ACTION_GET_RECORD_IDENTIFIER         0x8
41*f7e26ffaSEric DeVolder #define ACTION_SET_RECORD_IDENTIFIER         0x9
42*f7e26ffaSEric DeVolder #define ACTION_GET_RECORD_COUNT              0xA
43*f7e26ffaSEric DeVolder #define ACTION_BEGIN_DUMMY_WRITE_OPERATION   0xB
44*f7e26ffaSEric DeVolder #define ACTION_RESERVED                      0xC
45*f7e26ffaSEric DeVolder #define ACTION_GET_ERROR_LOG_ADDRESS_RANGE   0xD
46*f7e26ffaSEric DeVolder #define ACTION_GET_ERROR_LOG_ADDRESS_LENGTH  0xE
47*f7e26ffaSEric DeVolder #define ACTION_GET_ERROR_LOG_ADDRESS_RANGE_ATTRIBUTES 0xF
48*f7e26ffaSEric DeVolder #define ACTION_GET_EXECUTE_OPERATION_TIMINGS 0x10 /* ACPI 6.3 */
49*f7e26ffaSEric DeVolder 
50*f7e26ffaSEric DeVolder /* ACPI 4.0: Table 17-17 Command Status Definitions */
51*f7e26ffaSEric DeVolder #define STATUS_SUCCESS                0x00
52*f7e26ffaSEric DeVolder #define STATUS_NOT_ENOUGH_SPACE       0x01
53*f7e26ffaSEric DeVolder #define STATUS_HARDWARE_NOT_AVAILABLE 0x02
54*f7e26ffaSEric DeVolder #define STATUS_FAILED                 0x03
55*f7e26ffaSEric DeVolder #define STATUS_RECORD_STORE_EMPTY     0x04
56*f7e26ffaSEric DeVolder #define STATUS_RECORD_NOT_FOUND       0x05
57*f7e26ffaSEric DeVolder 
58*f7e26ffaSEric DeVolder /* UEFI 2.1: Appendix N Common Platform Error Record */
59*f7e26ffaSEric DeVolder #define UEFI_CPER_RECORD_MIN_SIZE 128U
60*f7e26ffaSEric DeVolder #define UEFI_CPER_RECORD_LENGTH_OFFSET 20U
61*f7e26ffaSEric DeVolder #define UEFI_CPER_RECORD_ID_OFFSET 96U
62*f7e26ffaSEric DeVolder #define IS_UEFI_CPER_RECORD(ptr) \
63*f7e26ffaSEric DeVolder     (((ptr)[0] == 'C') && \
64*f7e26ffaSEric DeVolder      ((ptr)[1] == 'P') && \
65*f7e26ffaSEric DeVolder      ((ptr)[2] == 'E') && \
66*f7e26ffaSEric DeVolder      ((ptr)[3] == 'R'))
67*f7e26ffaSEric DeVolder 
68*f7e26ffaSEric DeVolder /*
69*f7e26ffaSEric DeVolder  * NOTE that when accessing CPER fields within a record, memcpy()
70*f7e26ffaSEric DeVolder  * is utilized to avoid a possible misaligned access on the host.
71*f7e26ffaSEric DeVolder  */
72*f7e26ffaSEric DeVolder 
73*f7e26ffaSEric DeVolder /*
74*f7e26ffaSEric DeVolder  * This implementation is an ACTION (cmd) and VALUE (data)
75*f7e26ffaSEric DeVolder  * interface consisting of just two 64-bit registers.
76*f7e26ffaSEric DeVolder  */
77*f7e26ffaSEric DeVolder #define ERST_REG_SIZE (16UL)
78*f7e26ffaSEric DeVolder #define ERST_ACTION_OFFSET (0UL) /* action (cmd) */
79*f7e26ffaSEric DeVolder #define ERST_VALUE_OFFSET  (8UL) /* argument/value (data) */
80*f7e26ffaSEric DeVolder 
81*f7e26ffaSEric DeVolder /*
82*f7e26ffaSEric DeVolder  * ERST_RECORD_SIZE is the buffer size for exchanging ERST
83*f7e26ffaSEric DeVolder  * record contents. Thus, it defines the maximum record size.
84*f7e26ffaSEric DeVolder  * As this is mapped through a PCI BAR, it must be a power of
85*f7e26ffaSEric DeVolder  * two and larger than UEFI_CPER_RECORD_MIN_SIZE.
86*f7e26ffaSEric DeVolder  * The backing storage is divided into fixed size "slots",
87*f7e26ffaSEric DeVolder  * each ERST_RECORD_SIZE in length, and each "slot"
88*f7e26ffaSEric DeVolder  * storing a single record. No attempt at optimizing storage
89*f7e26ffaSEric DeVolder  * through compression, compaction, etc is attempted.
90*f7e26ffaSEric DeVolder  * NOTE that slot 0 is reserved for the backing storage header.
91*f7e26ffaSEric DeVolder  * Depending upon the size of the backing storage, additional
92*f7e26ffaSEric DeVolder  * slots will be part of the slot 0 header in order to account
93*f7e26ffaSEric DeVolder  * for a record_id for each available remaining slot.
94*f7e26ffaSEric DeVolder  */
95*f7e26ffaSEric DeVolder /* 8KiB records, not too small, not too big */
96*f7e26ffaSEric DeVolder #define ERST_RECORD_SIZE (8192UL)
97*f7e26ffaSEric DeVolder 
98*f7e26ffaSEric DeVolder #define ACPI_ERST_MEMDEV_PROP "memdev"
99*f7e26ffaSEric DeVolder #define ACPI_ERST_RECORD_SIZE_PROP "record_size"
100*f7e26ffaSEric DeVolder 
101*f7e26ffaSEric DeVolder /*
102*f7e26ffaSEric DeVolder  * From the ACPI ERST spec sections:
103*f7e26ffaSEric DeVolder  * A record id of all 0s is used to indicate 'unspecified' record id.
104*f7e26ffaSEric DeVolder  * A record id of all 1s is used to indicate empty or end.
105*f7e26ffaSEric DeVolder  */
106*f7e26ffaSEric DeVolder #define ERST_UNSPECIFIED_RECORD_ID (0UL)
107*f7e26ffaSEric DeVolder #define ERST_EMPTY_END_RECORD_ID (~0UL)
108*f7e26ffaSEric DeVolder 
109*f7e26ffaSEric DeVolder #define ERST_IS_VALID_RECORD_ID(rid) \
110*f7e26ffaSEric DeVolder     ((rid != ERST_UNSPECIFIED_RECORD_ID) && \
111*f7e26ffaSEric DeVolder      (rid != ERST_EMPTY_END_RECORD_ID))
112*f7e26ffaSEric DeVolder 
113*f7e26ffaSEric DeVolder /*
114*f7e26ffaSEric DeVolder  * Implementation-specific definitions and types.
115*f7e26ffaSEric DeVolder  * Values are arbitrary and chosen for this implementation.
116*f7e26ffaSEric DeVolder  * See erst.rst documentation for details.
117*f7e26ffaSEric DeVolder  */
118*f7e26ffaSEric DeVolder #define ERST_EXECUTE_OPERATION_MAGIC 0x9CUL
119*f7e26ffaSEric DeVolder #define ERST_STORE_MAGIC 0x524F545354535245UL /* ERSTSTOR */
120*f7e26ffaSEric DeVolder typedef struct {
121*f7e26ffaSEric DeVolder     uint64_t magic;
122*f7e26ffaSEric DeVolder     uint32_t record_size;
123*f7e26ffaSEric DeVolder     uint32_t storage_offset; /* offset to record storage beyond header */
124*f7e26ffaSEric DeVolder     uint16_t version;
125*f7e26ffaSEric DeVolder     uint16_t reserved;
126*f7e26ffaSEric DeVolder     uint32_t record_count;
127*f7e26ffaSEric DeVolder     uint64_t map[]; /* contains record_ids, and position indicates index */
128*f7e26ffaSEric DeVolder } __attribute__((packed)) ERSTStorageHeader;
129*f7e26ffaSEric DeVolder 
130*f7e26ffaSEric DeVolder /*
131*f7e26ffaSEric DeVolder  * Object cast macro
132*f7e26ffaSEric DeVolder  */
133*f7e26ffaSEric DeVolder #define ACPIERST(obj) \
134*f7e26ffaSEric DeVolder     OBJECT_CHECK(ERSTDeviceState, (obj), TYPE_ACPI_ERST)
135*f7e26ffaSEric DeVolder 
136*f7e26ffaSEric DeVolder /*
137*f7e26ffaSEric DeVolder  * Main ERST device state structure
138*f7e26ffaSEric DeVolder  */
139*f7e26ffaSEric DeVolder typedef struct {
140*f7e26ffaSEric DeVolder     PCIDevice parent_obj;
141*f7e26ffaSEric DeVolder 
142*f7e26ffaSEric DeVolder     /* Backend storage */
143*f7e26ffaSEric DeVolder     HostMemoryBackend *hostmem;
144*f7e26ffaSEric DeVolder     MemoryRegion *hostmem_mr;
145*f7e26ffaSEric DeVolder     uint32_t storage_size;
146*f7e26ffaSEric DeVolder     uint32_t default_record_size;
147*f7e26ffaSEric DeVolder 
148*f7e26ffaSEric DeVolder     /* Programming registers */
149*f7e26ffaSEric DeVolder     MemoryRegion iomem_mr;
150*f7e26ffaSEric DeVolder 
151*f7e26ffaSEric DeVolder     /* Exchange buffer */
152*f7e26ffaSEric DeVolder     MemoryRegion exchange_mr;
153*f7e26ffaSEric DeVolder 
154*f7e26ffaSEric DeVolder     /* Interface state */
155*f7e26ffaSEric DeVolder     uint8_t operation;
156*f7e26ffaSEric DeVolder     uint8_t busy_status;
157*f7e26ffaSEric DeVolder     uint8_t command_status;
158*f7e26ffaSEric DeVolder     uint32_t record_offset;
159*f7e26ffaSEric DeVolder     uint64_t reg_action;
160*f7e26ffaSEric DeVolder     uint64_t reg_value;
161*f7e26ffaSEric DeVolder     uint64_t record_identifier;
162*f7e26ffaSEric DeVolder     ERSTStorageHeader *header;
163*f7e26ffaSEric DeVolder     unsigned first_record_index;
164*f7e26ffaSEric DeVolder     unsigned last_record_index;
165*f7e26ffaSEric DeVolder     unsigned next_record_index;
166*f7e26ffaSEric DeVolder 
167*f7e26ffaSEric DeVolder } ERSTDeviceState;
168*f7e26ffaSEric DeVolder 
169*f7e26ffaSEric DeVolder /*******************************************************************/
170*f7e26ffaSEric DeVolder /*******************************************************************/
171*f7e26ffaSEric DeVolder static uint8_t *get_nvram_ptr_by_index(ERSTDeviceState *s, unsigned index)
172*f7e26ffaSEric DeVolder {
173*f7e26ffaSEric DeVolder     uint8_t *rc = NULL;
174*f7e26ffaSEric DeVolder     off_t offset = (index * le32_to_cpu(s->header->record_size));
175*f7e26ffaSEric DeVolder 
176*f7e26ffaSEric DeVolder     g_assert(offset < s->storage_size);
177*f7e26ffaSEric DeVolder 
178*f7e26ffaSEric DeVolder     rc = memory_region_get_ram_ptr(s->hostmem_mr);
179*f7e26ffaSEric DeVolder     rc += offset;
180*f7e26ffaSEric DeVolder 
181*f7e26ffaSEric DeVolder     return rc;
182*f7e26ffaSEric DeVolder }
183*f7e26ffaSEric DeVolder 
184*f7e26ffaSEric DeVolder static void make_erst_storage_header(ERSTDeviceState *s)
185*f7e26ffaSEric DeVolder {
186*f7e26ffaSEric DeVolder     ERSTStorageHeader *header = s->header;
187*f7e26ffaSEric DeVolder     unsigned mapsz, headersz;
188*f7e26ffaSEric DeVolder 
189*f7e26ffaSEric DeVolder     header->magic = cpu_to_le64(ERST_STORE_MAGIC);
190*f7e26ffaSEric DeVolder     header->record_size = cpu_to_le32(s->default_record_size);
191*f7e26ffaSEric DeVolder     header->version = cpu_to_le16(0x0100);
192*f7e26ffaSEric DeVolder     header->reserved = cpu_to_le16(0x0000);
193*f7e26ffaSEric DeVolder 
194*f7e26ffaSEric DeVolder     /* Compute mapsize */
195*f7e26ffaSEric DeVolder     mapsz = s->storage_size / s->default_record_size;
196*f7e26ffaSEric DeVolder     mapsz *= sizeof(uint64_t);
197*f7e26ffaSEric DeVolder     /* Compute header+map size */
198*f7e26ffaSEric DeVolder     headersz = sizeof(ERSTStorageHeader) + mapsz;
199*f7e26ffaSEric DeVolder     /* Round up to nearest integer multiple of ERST_RECORD_SIZE */
200*f7e26ffaSEric DeVolder     headersz = QEMU_ALIGN_UP(headersz, s->default_record_size);
201*f7e26ffaSEric DeVolder     header->storage_offset = cpu_to_le32(headersz);
202*f7e26ffaSEric DeVolder 
203*f7e26ffaSEric DeVolder     /*
204*f7e26ffaSEric DeVolder      * The HostMemoryBackend initializes contents to zero,
205*f7e26ffaSEric DeVolder      * so all record_ids stashed in the map are zero'd.
206*f7e26ffaSEric DeVolder      * As well the record_count is zero. Properly initialized.
207*f7e26ffaSEric DeVolder      */
208*f7e26ffaSEric DeVolder }
209*f7e26ffaSEric DeVolder 
210*f7e26ffaSEric DeVolder static void check_erst_backend_storage(ERSTDeviceState *s, Error **errp)
211*f7e26ffaSEric DeVolder {
212*f7e26ffaSEric DeVolder     ERSTStorageHeader *header;
213*f7e26ffaSEric DeVolder     uint32_t record_size;
214*f7e26ffaSEric DeVolder 
215*f7e26ffaSEric DeVolder     header = memory_region_get_ram_ptr(s->hostmem_mr);
216*f7e26ffaSEric DeVolder     s->header = header;
217*f7e26ffaSEric DeVolder 
218*f7e26ffaSEric DeVolder     /* Ensure pointer to header is 64-bit aligned */
219*f7e26ffaSEric DeVolder     g_assert(QEMU_PTR_IS_ALIGNED(header, sizeof(uint64_t)));
220*f7e26ffaSEric DeVolder 
221*f7e26ffaSEric DeVolder     /*
222*f7e26ffaSEric DeVolder      * Check if header is uninitialized; HostMemoryBackend inits to 0
223*f7e26ffaSEric DeVolder      */
224*f7e26ffaSEric DeVolder     if (le64_to_cpu(header->magic) == 0UL) {
225*f7e26ffaSEric DeVolder         make_erst_storage_header(s);
226*f7e26ffaSEric DeVolder     }
227*f7e26ffaSEric DeVolder 
228*f7e26ffaSEric DeVolder     /* Validity check record_size */
229*f7e26ffaSEric DeVolder     record_size = le32_to_cpu(header->record_size);
230*f7e26ffaSEric DeVolder     if (!(
231*f7e26ffaSEric DeVolder         (record_size) && /* non zero */
232*f7e26ffaSEric DeVolder         (record_size >= UEFI_CPER_RECORD_MIN_SIZE) &&
233*f7e26ffaSEric DeVolder         (((record_size - 1) & record_size) == 0) && /* is power of 2 */
234*f7e26ffaSEric DeVolder         (record_size >= 4096) /* PAGE_SIZE */
235*f7e26ffaSEric DeVolder         )) {
236*f7e26ffaSEric DeVolder         error_setg(errp, "ERST record_size %u is invalid", record_size);
237*f7e26ffaSEric DeVolder     }
238*f7e26ffaSEric DeVolder 
239*f7e26ffaSEric DeVolder     /* Validity check header */
240*f7e26ffaSEric DeVolder     if (!(
241*f7e26ffaSEric DeVolder         (le64_to_cpu(header->magic) == ERST_STORE_MAGIC) &&
242*f7e26ffaSEric DeVolder         ((le32_to_cpu(header->storage_offset) % record_size) == 0) &&
243*f7e26ffaSEric DeVolder         (le16_to_cpu(header->version) == 0x0100) &&
244*f7e26ffaSEric DeVolder         (le16_to_cpu(header->reserved) == 0)
245*f7e26ffaSEric DeVolder         )) {
246*f7e26ffaSEric DeVolder         error_setg(errp, "ERST backend storage header is invalid");
247*f7e26ffaSEric DeVolder     }
248*f7e26ffaSEric DeVolder 
249*f7e26ffaSEric DeVolder     /* Check storage_size against record_size */
250*f7e26ffaSEric DeVolder     if (((s->storage_size % record_size) != 0) ||
251*f7e26ffaSEric DeVolder          (record_size > s->storage_size)) {
252*f7e26ffaSEric DeVolder         error_setg(errp, "ACPI ERST requires storage size be multiple of "
253*f7e26ffaSEric DeVolder             "record size (%uKiB)", record_size);
254*f7e26ffaSEric DeVolder     }
255*f7e26ffaSEric DeVolder 
256*f7e26ffaSEric DeVolder     /* Compute offset of first and last record storage slot */
257*f7e26ffaSEric DeVolder     s->first_record_index = le32_to_cpu(header->storage_offset)
258*f7e26ffaSEric DeVolder         / record_size;
259*f7e26ffaSEric DeVolder     s->last_record_index = (s->storage_size / record_size);
260*f7e26ffaSEric DeVolder }
261*f7e26ffaSEric DeVolder 
262*f7e26ffaSEric DeVolder static void update_map_entry(ERSTDeviceState *s, unsigned index,
263*f7e26ffaSEric DeVolder     uint64_t record_id)
264*f7e26ffaSEric DeVolder {
265*f7e26ffaSEric DeVolder     if (index < s->last_record_index) {
266*f7e26ffaSEric DeVolder         s->header->map[index] = cpu_to_le64(record_id);
267*f7e26ffaSEric DeVolder     }
268*f7e26ffaSEric DeVolder }
269*f7e26ffaSEric DeVolder 
270*f7e26ffaSEric DeVolder static unsigned find_next_empty_record_index(ERSTDeviceState *s)
271*f7e26ffaSEric DeVolder {
272*f7e26ffaSEric DeVolder     unsigned rc = 0; /* 0 not a valid index */
273*f7e26ffaSEric DeVolder     unsigned index = s->first_record_index;
274*f7e26ffaSEric DeVolder 
275*f7e26ffaSEric DeVolder     for (; index < s->last_record_index; ++index) {
276*f7e26ffaSEric DeVolder         if (le64_to_cpu(s->header->map[index]) == ERST_UNSPECIFIED_RECORD_ID) {
277*f7e26ffaSEric DeVolder             rc = index;
278*f7e26ffaSEric DeVolder             break;
279*f7e26ffaSEric DeVolder         }
280*f7e26ffaSEric DeVolder     }
281*f7e26ffaSEric DeVolder 
282*f7e26ffaSEric DeVolder     return rc;
283*f7e26ffaSEric DeVolder }
284*f7e26ffaSEric DeVolder 
285*f7e26ffaSEric DeVolder static unsigned lookup_erst_record(ERSTDeviceState *s,
286*f7e26ffaSEric DeVolder     uint64_t record_identifier)
287*f7e26ffaSEric DeVolder {
288*f7e26ffaSEric DeVolder     unsigned rc = 0; /* 0 not a valid index */
289*f7e26ffaSEric DeVolder 
290*f7e26ffaSEric DeVolder     /* Find the record_identifier in the map */
291*f7e26ffaSEric DeVolder     if (record_identifier != ERST_UNSPECIFIED_RECORD_ID) {
292*f7e26ffaSEric DeVolder         /*
293*f7e26ffaSEric DeVolder          * Count number of valid records encountered, and
294*f7e26ffaSEric DeVolder          * short-circuit the loop if identifier not found
295*f7e26ffaSEric DeVolder          */
296*f7e26ffaSEric DeVolder         uint32_t record_count = le32_to_cpu(s->header->record_count);
297*f7e26ffaSEric DeVolder         unsigned count = 0;
298*f7e26ffaSEric DeVolder         unsigned index;
299*f7e26ffaSEric DeVolder         for (index = s->first_record_index; index < s->last_record_index &&
300*f7e26ffaSEric DeVolder                 count < record_count; ++index) {
301*f7e26ffaSEric DeVolder             if (le64_to_cpu(s->header->map[index]) == record_identifier) {
302*f7e26ffaSEric DeVolder                 rc = index;
303*f7e26ffaSEric DeVolder                 break;
304*f7e26ffaSEric DeVolder             }
305*f7e26ffaSEric DeVolder             if (le64_to_cpu(s->header->map[index]) !=
306*f7e26ffaSEric DeVolder                 ERST_UNSPECIFIED_RECORD_ID) {
307*f7e26ffaSEric DeVolder                 ++count;
308*f7e26ffaSEric DeVolder             }
309*f7e26ffaSEric DeVolder         }
310*f7e26ffaSEric DeVolder     }
311*f7e26ffaSEric DeVolder 
312*f7e26ffaSEric DeVolder     return rc;
313*f7e26ffaSEric DeVolder }
314*f7e26ffaSEric DeVolder 
315*f7e26ffaSEric DeVolder /*
316*f7e26ffaSEric DeVolder  * ACPI 4.0: 17.4.1.1 Serialization Actions, also see
317*f7e26ffaSEric DeVolder  * ACPI 4.0: 17.4.2.2 Operations - Reading 6.c and 2.c
318*f7e26ffaSEric DeVolder  */
319*f7e26ffaSEric DeVolder static unsigned get_next_record_identifier(ERSTDeviceState *s,
320*f7e26ffaSEric DeVolder     uint64_t *record_identifier, bool first)
321*f7e26ffaSEric DeVolder {
322*f7e26ffaSEric DeVolder     unsigned found = 0;
323*f7e26ffaSEric DeVolder     unsigned index;
324*f7e26ffaSEric DeVolder 
325*f7e26ffaSEric DeVolder     /* For operations needing to return 'first' record identifier */
326*f7e26ffaSEric DeVolder     if (first) {
327*f7e26ffaSEric DeVolder         /* Reset initial index to beginning */
328*f7e26ffaSEric DeVolder         s->next_record_index = s->first_record_index;
329*f7e26ffaSEric DeVolder     }
330*f7e26ffaSEric DeVolder     index = s->next_record_index;
331*f7e26ffaSEric DeVolder 
332*f7e26ffaSEric DeVolder     *record_identifier = ERST_EMPTY_END_RECORD_ID;
333*f7e26ffaSEric DeVolder 
334*f7e26ffaSEric DeVolder     if (le32_to_cpu(s->header->record_count)) {
335*f7e26ffaSEric DeVolder         for (; index < s->last_record_index; ++index) {
336*f7e26ffaSEric DeVolder             if (le64_to_cpu(s->header->map[index]) !=
337*f7e26ffaSEric DeVolder                     ERST_UNSPECIFIED_RECORD_ID) {
338*f7e26ffaSEric DeVolder                     /* where to start next time */
339*f7e26ffaSEric DeVolder                     s->next_record_index = index + 1;
340*f7e26ffaSEric DeVolder                     *record_identifier = le64_to_cpu(s->header->map[index]);
341*f7e26ffaSEric DeVolder                     found = 1;
342*f7e26ffaSEric DeVolder                     break;
343*f7e26ffaSEric DeVolder             }
344*f7e26ffaSEric DeVolder         }
345*f7e26ffaSEric DeVolder     }
346*f7e26ffaSEric DeVolder     if (!found) {
347*f7e26ffaSEric DeVolder         /* at end (ie scan complete), reset */
348*f7e26ffaSEric DeVolder         s->next_record_index = s->first_record_index;
349*f7e26ffaSEric DeVolder     }
350*f7e26ffaSEric DeVolder 
351*f7e26ffaSEric DeVolder     return STATUS_SUCCESS;
352*f7e26ffaSEric DeVolder }
353*f7e26ffaSEric DeVolder 
354*f7e26ffaSEric DeVolder /* ACPI 4.0: 17.4.2.3 Operations - Clearing */
355*f7e26ffaSEric DeVolder static unsigned clear_erst_record(ERSTDeviceState *s)
356*f7e26ffaSEric DeVolder {
357*f7e26ffaSEric DeVolder     unsigned rc = STATUS_RECORD_NOT_FOUND;
358*f7e26ffaSEric DeVolder     unsigned index;
359*f7e26ffaSEric DeVolder 
360*f7e26ffaSEric DeVolder     /* Check for valid record identifier */
361*f7e26ffaSEric DeVolder     if (!ERST_IS_VALID_RECORD_ID(s->record_identifier)) {
362*f7e26ffaSEric DeVolder         return STATUS_FAILED;
363*f7e26ffaSEric DeVolder     }
364*f7e26ffaSEric DeVolder 
365*f7e26ffaSEric DeVolder     index = lookup_erst_record(s, s->record_identifier);
366*f7e26ffaSEric DeVolder     if (index) {
367*f7e26ffaSEric DeVolder         /* No need to wipe record, just invalidate its map entry */
368*f7e26ffaSEric DeVolder         uint32_t record_count;
369*f7e26ffaSEric DeVolder         update_map_entry(s, index, ERST_UNSPECIFIED_RECORD_ID);
370*f7e26ffaSEric DeVolder         record_count = le32_to_cpu(s->header->record_count);
371*f7e26ffaSEric DeVolder         record_count -= 1;
372*f7e26ffaSEric DeVolder         s->header->record_count = cpu_to_le32(record_count);
373*f7e26ffaSEric DeVolder         rc = STATUS_SUCCESS;
374*f7e26ffaSEric DeVolder     }
375*f7e26ffaSEric DeVolder 
376*f7e26ffaSEric DeVolder     return rc;
377*f7e26ffaSEric DeVolder }
378*f7e26ffaSEric DeVolder 
379*f7e26ffaSEric DeVolder /* ACPI 4.0: 17.4.2.2 Operations - Reading */
380*f7e26ffaSEric DeVolder static unsigned read_erst_record(ERSTDeviceState *s)
381*f7e26ffaSEric DeVolder {
382*f7e26ffaSEric DeVolder     unsigned rc = STATUS_RECORD_NOT_FOUND;
383*f7e26ffaSEric DeVolder     unsigned exchange_length;
384*f7e26ffaSEric DeVolder     unsigned index;
385*f7e26ffaSEric DeVolder 
386*f7e26ffaSEric DeVolder     /* Check if backend storage is empty */
387*f7e26ffaSEric DeVolder     if (le32_to_cpu(s->header->record_count) == 0) {
388*f7e26ffaSEric DeVolder         return STATUS_RECORD_STORE_EMPTY;
389*f7e26ffaSEric DeVolder     }
390*f7e26ffaSEric DeVolder 
391*f7e26ffaSEric DeVolder     exchange_length = memory_region_size(&s->exchange_mr);
392*f7e26ffaSEric DeVolder 
393*f7e26ffaSEric DeVolder     /* Check for record identifier of all 0s */
394*f7e26ffaSEric DeVolder     if (s->record_identifier == ERST_UNSPECIFIED_RECORD_ID) {
395*f7e26ffaSEric DeVolder         /* Set to 'first' record in storage */
396*f7e26ffaSEric DeVolder         get_next_record_identifier(s, &s->record_identifier, true);
397*f7e26ffaSEric DeVolder         /* record_identifier is now a valid id, or all 1s */
398*f7e26ffaSEric DeVolder     }
399*f7e26ffaSEric DeVolder 
400*f7e26ffaSEric DeVolder     /* Check for record identifier of all 1s */
401*f7e26ffaSEric DeVolder     if (s->record_identifier == ERST_EMPTY_END_RECORD_ID) {
402*f7e26ffaSEric DeVolder         return STATUS_FAILED;
403*f7e26ffaSEric DeVolder     }
404*f7e26ffaSEric DeVolder 
405*f7e26ffaSEric DeVolder     /* Validate record_offset */
406*f7e26ffaSEric DeVolder     if (s->record_offset > (exchange_length - UEFI_CPER_RECORD_MIN_SIZE)) {
407*f7e26ffaSEric DeVolder         return STATUS_FAILED;
408*f7e26ffaSEric DeVolder     }
409*f7e26ffaSEric DeVolder 
410*f7e26ffaSEric DeVolder     index = lookup_erst_record(s, s->record_identifier);
411*f7e26ffaSEric DeVolder     if (index) {
412*f7e26ffaSEric DeVolder         uint8_t *nvram;
413*f7e26ffaSEric DeVolder         uint8_t *exchange;
414*f7e26ffaSEric DeVolder         uint32_t record_length;
415*f7e26ffaSEric DeVolder 
416*f7e26ffaSEric DeVolder         /* Obtain pointer to the exchange buffer */
417*f7e26ffaSEric DeVolder         exchange = memory_region_get_ram_ptr(&s->exchange_mr);
418*f7e26ffaSEric DeVolder         exchange += s->record_offset;
419*f7e26ffaSEric DeVolder         /* Obtain pointer to slot in storage */
420*f7e26ffaSEric DeVolder         nvram = get_nvram_ptr_by_index(s, index);
421*f7e26ffaSEric DeVolder         /* Validate CPER record_length */
422*f7e26ffaSEric DeVolder         memcpy((uint8_t *)&record_length,
423*f7e26ffaSEric DeVolder             &nvram[UEFI_CPER_RECORD_LENGTH_OFFSET],
424*f7e26ffaSEric DeVolder             sizeof(uint32_t));
425*f7e26ffaSEric DeVolder         record_length = le32_to_cpu(record_length);
426*f7e26ffaSEric DeVolder         if (record_length < UEFI_CPER_RECORD_MIN_SIZE) {
427*f7e26ffaSEric DeVolder             rc = STATUS_FAILED;
428*f7e26ffaSEric DeVolder         }
429*f7e26ffaSEric DeVolder         if ((s->record_offset + record_length) > exchange_length) {
430*f7e26ffaSEric DeVolder             rc = STATUS_FAILED;
431*f7e26ffaSEric DeVolder         }
432*f7e26ffaSEric DeVolder         /* If all is ok, copy the record to the exchange buffer */
433*f7e26ffaSEric DeVolder         if (rc != STATUS_FAILED) {
434*f7e26ffaSEric DeVolder             memcpy(exchange, nvram, record_length);
435*f7e26ffaSEric DeVolder             rc = STATUS_SUCCESS;
436*f7e26ffaSEric DeVolder         }
437*f7e26ffaSEric DeVolder     } else {
438*f7e26ffaSEric DeVolder         /*
439*f7e26ffaSEric DeVolder          * See "Reading : 'The steps performed by the platform ...' 2.c"
440*f7e26ffaSEric DeVolder          * Set to 'first' record in storage
441*f7e26ffaSEric DeVolder          */
442*f7e26ffaSEric DeVolder         get_next_record_identifier(s, &s->record_identifier, true);
443*f7e26ffaSEric DeVolder     }
444*f7e26ffaSEric DeVolder 
445*f7e26ffaSEric DeVolder     return rc;
446*f7e26ffaSEric DeVolder }
447*f7e26ffaSEric DeVolder 
448*f7e26ffaSEric DeVolder /* ACPI 4.0: 17.4.2.1 Operations - Writing */
449*f7e26ffaSEric DeVolder static unsigned write_erst_record(ERSTDeviceState *s)
450*f7e26ffaSEric DeVolder {
451*f7e26ffaSEric DeVolder     unsigned rc = STATUS_FAILED;
452*f7e26ffaSEric DeVolder     unsigned exchange_length;
453*f7e26ffaSEric DeVolder     unsigned index;
454*f7e26ffaSEric DeVolder     uint64_t record_identifier;
455*f7e26ffaSEric DeVolder     uint32_t record_length;
456*f7e26ffaSEric DeVolder     uint8_t *exchange;
457*f7e26ffaSEric DeVolder     uint8_t *nvram = NULL;
458*f7e26ffaSEric DeVolder     bool record_found = false;
459*f7e26ffaSEric DeVolder 
460*f7e26ffaSEric DeVolder     exchange_length = memory_region_size(&s->exchange_mr);
461*f7e26ffaSEric DeVolder 
462*f7e26ffaSEric DeVolder     /* Validate record_offset */
463*f7e26ffaSEric DeVolder     if (s->record_offset > (exchange_length - UEFI_CPER_RECORD_MIN_SIZE)) {
464*f7e26ffaSEric DeVolder         return STATUS_FAILED;
465*f7e26ffaSEric DeVolder     }
466*f7e26ffaSEric DeVolder 
467*f7e26ffaSEric DeVolder     /* Obtain pointer to record in the exchange buffer */
468*f7e26ffaSEric DeVolder     exchange = memory_region_get_ram_ptr(&s->exchange_mr);
469*f7e26ffaSEric DeVolder     exchange += s->record_offset;
470*f7e26ffaSEric DeVolder 
471*f7e26ffaSEric DeVolder     /* Validate CPER record_length */
472*f7e26ffaSEric DeVolder     memcpy((uint8_t *)&record_length, &exchange[UEFI_CPER_RECORD_LENGTH_OFFSET],
473*f7e26ffaSEric DeVolder         sizeof(uint32_t));
474*f7e26ffaSEric DeVolder     record_length = le32_to_cpu(record_length);
475*f7e26ffaSEric DeVolder     if (record_length < UEFI_CPER_RECORD_MIN_SIZE) {
476*f7e26ffaSEric DeVolder         return STATUS_FAILED;
477*f7e26ffaSEric DeVolder     }
478*f7e26ffaSEric DeVolder     if ((s->record_offset + record_length) > exchange_length) {
479*f7e26ffaSEric DeVolder         return STATUS_FAILED;
480*f7e26ffaSEric DeVolder     }
481*f7e26ffaSEric DeVolder 
482*f7e26ffaSEric DeVolder     /* Extract record identifier */
483*f7e26ffaSEric DeVolder     memcpy((uint8_t *)&record_identifier, &exchange[UEFI_CPER_RECORD_ID_OFFSET],
484*f7e26ffaSEric DeVolder         sizeof(uint64_t));
485*f7e26ffaSEric DeVolder     record_identifier = le64_to_cpu(record_identifier);
486*f7e26ffaSEric DeVolder 
487*f7e26ffaSEric DeVolder     /* Check for valid record identifier */
488*f7e26ffaSEric DeVolder     if (!ERST_IS_VALID_RECORD_ID(record_identifier)) {
489*f7e26ffaSEric DeVolder         return STATUS_FAILED;
490*f7e26ffaSEric DeVolder     }
491*f7e26ffaSEric DeVolder 
492*f7e26ffaSEric DeVolder     index = lookup_erst_record(s, record_identifier);
493*f7e26ffaSEric DeVolder     if (index) {
494*f7e26ffaSEric DeVolder         /* Record found, overwrite existing record */
495*f7e26ffaSEric DeVolder         nvram = get_nvram_ptr_by_index(s, index);
496*f7e26ffaSEric DeVolder         record_found = true;
497*f7e26ffaSEric DeVolder     } else {
498*f7e26ffaSEric DeVolder         /* Record not found, not an overwrite, allocate for write */
499*f7e26ffaSEric DeVolder         index = find_next_empty_record_index(s);
500*f7e26ffaSEric DeVolder         if (index) {
501*f7e26ffaSEric DeVolder             nvram = get_nvram_ptr_by_index(s, index);
502*f7e26ffaSEric DeVolder         } else {
503*f7e26ffaSEric DeVolder             /* All slots are occupied */
504*f7e26ffaSEric DeVolder             rc = STATUS_NOT_ENOUGH_SPACE;
505*f7e26ffaSEric DeVolder         }
506*f7e26ffaSEric DeVolder     }
507*f7e26ffaSEric DeVolder     if (nvram) {
508*f7e26ffaSEric DeVolder         /* Write the record into the slot */
509*f7e26ffaSEric DeVolder         memcpy(nvram, exchange, record_length);
510*f7e26ffaSEric DeVolder         memset(nvram + record_length, exchange_length - record_length, 0xFF);
511*f7e26ffaSEric DeVolder         /* If a new record, increment the record_count */
512*f7e26ffaSEric DeVolder         if (!record_found) {
513*f7e26ffaSEric DeVolder             uint32_t record_count;
514*f7e26ffaSEric DeVolder             record_count = le32_to_cpu(s->header->record_count);
515*f7e26ffaSEric DeVolder             record_count += 1; /* writing new record */
516*f7e26ffaSEric DeVolder             s->header->record_count = cpu_to_le32(record_count);
517*f7e26ffaSEric DeVolder         }
518*f7e26ffaSEric DeVolder         update_map_entry(s, index, record_identifier);
519*f7e26ffaSEric DeVolder         rc = STATUS_SUCCESS;
520*f7e26ffaSEric DeVolder     }
521*f7e26ffaSEric DeVolder 
522*f7e26ffaSEric DeVolder     return rc;
523*f7e26ffaSEric DeVolder }
524*f7e26ffaSEric DeVolder 
525*f7e26ffaSEric DeVolder /*******************************************************************/
526*f7e26ffaSEric DeVolder 
527*f7e26ffaSEric DeVolder static uint64_t erst_rd_reg64(hwaddr addr,
528*f7e26ffaSEric DeVolder     uint64_t reg, unsigned size)
529*f7e26ffaSEric DeVolder {
530*f7e26ffaSEric DeVolder     uint64_t rdval;
531*f7e26ffaSEric DeVolder     uint64_t mask;
532*f7e26ffaSEric DeVolder     unsigned shift;
533*f7e26ffaSEric DeVolder 
534*f7e26ffaSEric DeVolder     if (size == sizeof(uint64_t)) {
535*f7e26ffaSEric DeVolder         /* 64b access */
536*f7e26ffaSEric DeVolder         mask = 0xFFFFFFFFFFFFFFFFUL;
537*f7e26ffaSEric DeVolder         shift = 0;
538*f7e26ffaSEric DeVolder     } else {
539*f7e26ffaSEric DeVolder         /* 32b access */
540*f7e26ffaSEric DeVolder         mask = 0x00000000FFFFFFFFUL;
541*f7e26ffaSEric DeVolder         shift = ((addr & 0x4) == 0x4) ? 32 : 0;
542*f7e26ffaSEric DeVolder     }
543*f7e26ffaSEric DeVolder 
544*f7e26ffaSEric DeVolder     rdval = reg;
545*f7e26ffaSEric DeVolder     rdval >>= shift;
546*f7e26ffaSEric DeVolder     rdval &= mask;
547*f7e26ffaSEric DeVolder 
548*f7e26ffaSEric DeVolder     return rdval;
549*f7e26ffaSEric DeVolder }
550*f7e26ffaSEric DeVolder 
551*f7e26ffaSEric DeVolder static uint64_t erst_wr_reg64(hwaddr addr,
552*f7e26ffaSEric DeVolder     uint64_t reg, uint64_t val, unsigned size)
553*f7e26ffaSEric DeVolder {
554*f7e26ffaSEric DeVolder     uint64_t wrval;
555*f7e26ffaSEric DeVolder     uint64_t mask;
556*f7e26ffaSEric DeVolder     unsigned shift;
557*f7e26ffaSEric DeVolder 
558*f7e26ffaSEric DeVolder     if (size == sizeof(uint64_t)) {
559*f7e26ffaSEric DeVolder         /* 64b access */
560*f7e26ffaSEric DeVolder         mask = 0xFFFFFFFFFFFFFFFFUL;
561*f7e26ffaSEric DeVolder         shift = 0;
562*f7e26ffaSEric DeVolder     } else {
563*f7e26ffaSEric DeVolder         /* 32b access */
564*f7e26ffaSEric DeVolder         mask = 0x00000000FFFFFFFFUL;
565*f7e26ffaSEric DeVolder         shift = ((addr & 0x4) == 0x4) ? 32 : 0;
566*f7e26ffaSEric DeVolder     }
567*f7e26ffaSEric DeVolder 
568*f7e26ffaSEric DeVolder     val &= mask;
569*f7e26ffaSEric DeVolder     val <<= shift;
570*f7e26ffaSEric DeVolder     mask <<= shift;
571*f7e26ffaSEric DeVolder     wrval = reg;
572*f7e26ffaSEric DeVolder     wrval &= ~mask;
573*f7e26ffaSEric DeVolder     wrval |= val;
574*f7e26ffaSEric DeVolder 
575*f7e26ffaSEric DeVolder     return wrval;
576*f7e26ffaSEric DeVolder }
577*f7e26ffaSEric DeVolder 
578*f7e26ffaSEric DeVolder static void erst_reg_write(void *opaque, hwaddr addr,
579*f7e26ffaSEric DeVolder     uint64_t val, unsigned size)
580*f7e26ffaSEric DeVolder {
581*f7e26ffaSEric DeVolder     ERSTDeviceState *s = (ERSTDeviceState *)opaque;
582*f7e26ffaSEric DeVolder 
583*f7e26ffaSEric DeVolder     /*
584*f7e26ffaSEric DeVolder      * NOTE: All actions/operations/side effects happen on the WRITE,
585*f7e26ffaSEric DeVolder      * by this implementation's design. The READs simply return the
586*f7e26ffaSEric DeVolder      * reg_value contents.
587*f7e26ffaSEric DeVolder      */
588*f7e26ffaSEric DeVolder     trace_acpi_erst_reg_write(addr, val, size);
589*f7e26ffaSEric DeVolder 
590*f7e26ffaSEric DeVolder     switch (addr) {
591*f7e26ffaSEric DeVolder     case ERST_VALUE_OFFSET + 0:
592*f7e26ffaSEric DeVolder     case ERST_VALUE_OFFSET + 4:
593*f7e26ffaSEric DeVolder         s->reg_value = erst_wr_reg64(addr, s->reg_value, val, size);
594*f7e26ffaSEric DeVolder         break;
595*f7e26ffaSEric DeVolder     case ERST_ACTION_OFFSET + 0:
596*f7e26ffaSEric DeVolder         /*
597*f7e26ffaSEric DeVolder          * NOTE: all valid values written to this register are of the
598*f7e26ffaSEric DeVolder          * ACTION_* variety. Thus there is no need to make this a 64-bit
599*f7e26ffaSEric DeVolder          * register, 32-bits is appropriate. As such ERST_ACTION_OFFSET+4
600*f7e26ffaSEric DeVolder          * is not needed.
601*f7e26ffaSEric DeVolder          */
602*f7e26ffaSEric DeVolder         switch (val) {
603*f7e26ffaSEric DeVolder         case ACTION_BEGIN_WRITE_OPERATION:
604*f7e26ffaSEric DeVolder         case ACTION_BEGIN_READ_OPERATION:
605*f7e26ffaSEric DeVolder         case ACTION_BEGIN_CLEAR_OPERATION:
606*f7e26ffaSEric DeVolder         case ACTION_BEGIN_DUMMY_WRITE_OPERATION:
607*f7e26ffaSEric DeVolder         case ACTION_END_OPERATION:
608*f7e26ffaSEric DeVolder             s->operation = val;
609*f7e26ffaSEric DeVolder             break;
610*f7e26ffaSEric DeVolder         case ACTION_SET_RECORD_OFFSET:
611*f7e26ffaSEric DeVolder             s->record_offset = s->reg_value;
612*f7e26ffaSEric DeVolder             break;
613*f7e26ffaSEric DeVolder         case ACTION_EXECUTE_OPERATION:
614*f7e26ffaSEric DeVolder             if ((uint8_t)s->reg_value == ERST_EXECUTE_OPERATION_MAGIC) {
615*f7e26ffaSEric DeVolder                 s->busy_status = 1;
616*f7e26ffaSEric DeVolder                 switch (s->operation) {
617*f7e26ffaSEric DeVolder                 case ACTION_BEGIN_WRITE_OPERATION:
618*f7e26ffaSEric DeVolder                     s->command_status = write_erst_record(s);
619*f7e26ffaSEric DeVolder                     break;
620*f7e26ffaSEric DeVolder                 case ACTION_BEGIN_READ_OPERATION:
621*f7e26ffaSEric DeVolder                     s->command_status = read_erst_record(s);
622*f7e26ffaSEric DeVolder                     break;
623*f7e26ffaSEric DeVolder                 case ACTION_BEGIN_CLEAR_OPERATION:
624*f7e26ffaSEric DeVolder                     s->command_status = clear_erst_record(s);
625*f7e26ffaSEric DeVolder                     break;
626*f7e26ffaSEric DeVolder                 case ACTION_BEGIN_DUMMY_WRITE_OPERATION:
627*f7e26ffaSEric DeVolder                     s->command_status = STATUS_SUCCESS;
628*f7e26ffaSEric DeVolder                     break;
629*f7e26ffaSEric DeVolder                 case ACTION_END_OPERATION:
630*f7e26ffaSEric DeVolder                     s->command_status = STATUS_SUCCESS;
631*f7e26ffaSEric DeVolder                     break;
632*f7e26ffaSEric DeVolder                 default:
633*f7e26ffaSEric DeVolder                     s->command_status = STATUS_FAILED;
634*f7e26ffaSEric DeVolder                     break;
635*f7e26ffaSEric DeVolder                 }
636*f7e26ffaSEric DeVolder                 s->busy_status = 0;
637*f7e26ffaSEric DeVolder             }
638*f7e26ffaSEric DeVolder             break;
639*f7e26ffaSEric DeVolder         case ACTION_CHECK_BUSY_STATUS:
640*f7e26ffaSEric DeVolder             s->reg_value = s->busy_status;
641*f7e26ffaSEric DeVolder             break;
642*f7e26ffaSEric DeVolder         case ACTION_GET_COMMAND_STATUS:
643*f7e26ffaSEric DeVolder             s->reg_value = s->command_status;
644*f7e26ffaSEric DeVolder             break;
645*f7e26ffaSEric DeVolder         case ACTION_GET_RECORD_IDENTIFIER:
646*f7e26ffaSEric DeVolder             s->command_status = get_next_record_identifier(s,
647*f7e26ffaSEric DeVolder                                     &s->reg_value, false);
648*f7e26ffaSEric DeVolder             break;
649*f7e26ffaSEric DeVolder         case ACTION_SET_RECORD_IDENTIFIER:
650*f7e26ffaSEric DeVolder             s->record_identifier = s->reg_value;
651*f7e26ffaSEric DeVolder             break;
652*f7e26ffaSEric DeVolder         case ACTION_GET_RECORD_COUNT:
653*f7e26ffaSEric DeVolder             s->reg_value = le32_to_cpu(s->header->record_count);
654*f7e26ffaSEric DeVolder             break;
655*f7e26ffaSEric DeVolder         case ACTION_GET_ERROR_LOG_ADDRESS_RANGE:
656*f7e26ffaSEric DeVolder             s->reg_value = (hwaddr)pci_get_bar_addr(PCI_DEVICE(s), 1);
657*f7e26ffaSEric DeVolder             break;
658*f7e26ffaSEric DeVolder         case ACTION_GET_ERROR_LOG_ADDRESS_LENGTH:
659*f7e26ffaSEric DeVolder             s->reg_value = le32_to_cpu(s->header->record_size);
660*f7e26ffaSEric DeVolder             break;
661*f7e26ffaSEric DeVolder         case ACTION_GET_ERROR_LOG_ADDRESS_RANGE_ATTRIBUTES:
662*f7e26ffaSEric DeVolder             s->reg_value = 0x0; /* intentional, not NVRAM mode */
663*f7e26ffaSEric DeVolder             break;
664*f7e26ffaSEric DeVolder         case ACTION_GET_EXECUTE_OPERATION_TIMINGS:
665*f7e26ffaSEric DeVolder             s->reg_value =
666*f7e26ffaSEric DeVolder                 (100ULL << 32) | /* 100us max time */
667*f7e26ffaSEric DeVolder                 (10ULL  <<  0) ; /*  10us min time */
668*f7e26ffaSEric DeVolder             break;
669*f7e26ffaSEric DeVolder         default:
670*f7e26ffaSEric DeVolder             /* Unknown action/command, NOP */
671*f7e26ffaSEric DeVolder             break;
672*f7e26ffaSEric DeVolder         }
673*f7e26ffaSEric DeVolder         break;
674*f7e26ffaSEric DeVolder     default:
675*f7e26ffaSEric DeVolder         /* This should not happen, but if it does, NOP */
676*f7e26ffaSEric DeVolder         break;
677*f7e26ffaSEric DeVolder     }
678*f7e26ffaSEric DeVolder }
679*f7e26ffaSEric DeVolder 
680*f7e26ffaSEric DeVolder static uint64_t erst_reg_read(void *opaque, hwaddr addr,
681*f7e26ffaSEric DeVolder                                 unsigned size)
682*f7e26ffaSEric DeVolder {
683*f7e26ffaSEric DeVolder     ERSTDeviceState *s = (ERSTDeviceState *)opaque;
684*f7e26ffaSEric DeVolder     uint64_t val = 0;
685*f7e26ffaSEric DeVolder 
686*f7e26ffaSEric DeVolder     switch (addr) {
687*f7e26ffaSEric DeVolder     case ERST_ACTION_OFFSET + 0:
688*f7e26ffaSEric DeVolder     case ERST_ACTION_OFFSET + 4:
689*f7e26ffaSEric DeVolder         val = erst_rd_reg64(addr, s->reg_action, size);
690*f7e26ffaSEric DeVolder         break;
691*f7e26ffaSEric DeVolder     case ERST_VALUE_OFFSET + 0:
692*f7e26ffaSEric DeVolder     case ERST_VALUE_OFFSET + 4:
693*f7e26ffaSEric DeVolder         val = erst_rd_reg64(addr, s->reg_value, size);
694*f7e26ffaSEric DeVolder         break;
695*f7e26ffaSEric DeVolder     default:
696*f7e26ffaSEric DeVolder         break;
697*f7e26ffaSEric DeVolder     }
698*f7e26ffaSEric DeVolder     trace_acpi_erst_reg_read(addr, val, size);
699*f7e26ffaSEric DeVolder     return val;
700*f7e26ffaSEric DeVolder }
701*f7e26ffaSEric DeVolder 
702*f7e26ffaSEric DeVolder static const MemoryRegionOps erst_reg_ops = {
703*f7e26ffaSEric DeVolder     .read = erst_reg_read,
704*f7e26ffaSEric DeVolder     .write = erst_reg_write,
705*f7e26ffaSEric DeVolder     .endianness = DEVICE_NATIVE_ENDIAN,
706*f7e26ffaSEric DeVolder };
707*f7e26ffaSEric DeVolder 
708*f7e26ffaSEric DeVolder /*******************************************************************/
709*f7e26ffaSEric DeVolder /*******************************************************************/
710*f7e26ffaSEric DeVolder static int erst_post_load(void *opaque, int version_id)
711*f7e26ffaSEric DeVolder {
712*f7e26ffaSEric DeVolder     ERSTDeviceState *s = opaque;
713*f7e26ffaSEric DeVolder 
714*f7e26ffaSEric DeVolder     /* Recompute pointer to header */
715*f7e26ffaSEric DeVolder     s->header = (ERSTStorageHeader *)get_nvram_ptr_by_index(s, 0);
716*f7e26ffaSEric DeVolder     trace_acpi_erst_post_load(s->header, le32_to_cpu(s->header->record_size));
717*f7e26ffaSEric DeVolder 
718*f7e26ffaSEric DeVolder     return 0;
719*f7e26ffaSEric DeVolder }
720*f7e26ffaSEric DeVolder 
721*f7e26ffaSEric DeVolder static const VMStateDescription erst_vmstate  = {
722*f7e26ffaSEric DeVolder     .name = "acpi-erst",
723*f7e26ffaSEric DeVolder     .version_id = 1,
724*f7e26ffaSEric DeVolder     .minimum_version_id = 1,
725*f7e26ffaSEric DeVolder     .post_load = erst_post_load,
726*f7e26ffaSEric DeVolder     .fields = (VMStateField[]) {
727*f7e26ffaSEric DeVolder         VMSTATE_UINT8(operation, ERSTDeviceState),
728*f7e26ffaSEric DeVolder         VMSTATE_UINT8(busy_status, ERSTDeviceState),
729*f7e26ffaSEric DeVolder         VMSTATE_UINT8(command_status, ERSTDeviceState),
730*f7e26ffaSEric DeVolder         VMSTATE_UINT32(record_offset, ERSTDeviceState),
731*f7e26ffaSEric DeVolder         VMSTATE_UINT64(reg_action, ERSTDeviceState),
732*f7e26ffaSEric DeVolder         VMSTATE_UINT64(reg_value, ERSTDeviceState),
733*f7e26ffaSEric DeVolder         VMSTATE_UINT64(record_identifier, ERSTDeviceState),
734*f7e26ffaSEric DeVolder         VMSTATE_UINT32(next_record_index, ERSTDeviceState),
735*f7e26ffaSEric DeVolder         VMSTATE_END_OF_LIST()
736*f7e26ffaSEric DeVolder     }
737*f7e26ffaSEric DeVolder };
738*f7e26ffaSEric DeVolder 
739*f7e26ffaSEric DeVolder static void erst_realizefn(PCIDevice *pci_dev, Error **errp)
740*f7e26ffaSEric DeVolder {
741*f7e26ffaSEric DeVolder     ERSTDeviceState *s = ACPIERST(pci_dev);
742*f7e26ffaSEric DeVolder 
743*f7e26ffaSEric DeVolder     trace_acpi_erst_realizefn_in();
744*f7e26ffaSEric DeVolder 
745*f7e26ffaSEric DeVolder     if (!s->hostmem) {
746*f7e26ffaSEric DeVolder         error_setg(errp, "'" ACPI_ERST_MEMDEV_PROP "' property is not set");
747*f7e26ffaSEric DeVolder         return;
748*f7e26ffaSEric DeVolder     } else if (host_memory_backend_is_mapped(s->hostmem)) {
749*f7e26ffaSEric DeVolder         error_setg(errp, "can't use already busy memdev: %s",
750*f7e26ffaSEric DeVolder                    object_get_canonical_path_component(OBJECT(s->hostmem)));
751*f7e26ffaSEric DeVolder         return;
752*f7e26ffaSEric DeVolder     }
753*f7e26ffaSEric DeVolder 
754*f7e26ffaSEric DeVolder     s->hostmem_mr = host_memory_backend_get_memory(s->hostmem);
755*f7e26ffaSEric DeVolder 
756*f7e26ffaSEric DeVolder     /* HostMemoryBackend size will be multiple of PAGE_SIZE */
757*f7e26ffaSEric DeVolder     s->storage_size = object_property_get_int(OBJECT(s->hostmem), "size", errp);
758*f7e26ffaSEric DeVolder 
759*f7e26ffaSEric DeVolder     /* Initialize backend storage and record_count */
760*f7e26ffaSEric DeVolder     check_erst_backend_storage(s, errp);
761*f7e26ffaSEric DeVolder 
762*f7e26ffaSEric DeVolder     /* BAR 0: Programming registers */
763*f7e26ffaSEric DeVolder     memory_region_init_io(&s->iomem_mr, OBJECT(pci_dev), &erst_reg_ops, s,
764*f7e26ffaSEric DeVolder                           TYPE_ACPI_ERST, ERST_REG_SIZE);
765*f7e26ffaSEric DeVolder     pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->iomem_mr);
766*f7e26ffaSEric DeVolder 
767*f7e26ffaSEric DeVolder     /* BAR 1: Exchange buffer memory */
768*f7e26ffaSEric DeVolder     memory_region_init_ram(&s->exchange_mr, OBJECT(pci_dev),
769*f7e26ffaSEric DeVolder                             "erst.exchange",
770*f7e26ffaSEric DeVolder                             le32_to_cpu(s->header->record_size), errp);
771*f7e26ffaSEric DeVolder     pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY,
772*f7e26ffaSEric DeVolder                         &s->exchange_mr);
773*f7e26ffaSEric DeVolder 
774*f7e26ffaSEric DeVolder     /* Include the backend storage in the migration stream */
775*f7e26ffaSEric DeVolder     vmstate_register_ram_global(s->hostmem_mr);
776*f7e26ffaSEric DeVolder 
777*f7e26ffaSEric DeVolder     trace_acpi_erst_realizefn_out(s->storage_size);
778*f7e26ffaSEric DeVolder }
779*f7e26ffaSEric DeVolder 
780*f7e26ffaSEric DeVolder static void erst_reset(DeviceState *dev)
781*f7e26ffaSEric DeVolder {
782*f7e26ffaSEric DeVolder     ERSTDeviceState *s = ACPIERST(dev);
783*f7e26ffaSEric DeVolder 
784*f7e26ffaSEric DeVolder     trace_acpi_erst_reset_in(le32_to_cpu(s->header->record_count));
785*f7e26ffaSEric DeVolder     s->operation = 0;
786*f7e26ffaSEric DeVolder     s->busy_status = 0;
787*f7e26ffaSEric DeVolder     s->command_status = STATUS_SUCCESS;
788*f7e26ffaSEric DeVolder     s->record_identifier = ERST_UNSPECIFIED_RECORD_ID;
789*f7e26ffaSEric DeVolder     s->record_offset = 0;
790*f7e26ffaSEric DeVolder     s->next_record_index = s->first_record_index;
791*f7e26ffaSEric DeVolder     /* NOTE: first/last_record_index are computed only once */
792*f7e26ffaSEric DeVolder     trace_acpi_erst_reset_out(le32_to_cpu(s->header->record_count));
793*f7e26ffaSEric DeVolder }
794*f7e26ffaSEric DeVolder 
795*f7e26ffaSEric DeVolder static Property erst_properties[] = {
796*f7e26ffaSEric DeVolder     DEFINE_PROP_LINK(ACPI_ERST_MEMDEV_PROP, ERSTDeviceState, hostmem,
797*f7e26ffaSEric DeVolder                      TYPE_MEMORY_BACKEND, HostMemoryBackend *),
798*f7e26ffaSEric DeVolder     DEFINE_PROP_UINT32(ACPI_ERST_RECORD_SIZE_PROP, ERSTDeviceState,
799*f7e26ffaSEric DeVolder                      default_record_size, ERST_RECORD_SIZE),
800*f7e26ffaSEric DeVolder     DEFINE_PROP_END_OF_LIST(),
801*f7e26ffaSEric DeVolder };
802*f7e26ffaSEric DeVolder 
803*f7e26ffaSEric DeVolder static void erst_class_init(ObjectClass *klass, void *data)
804*f7e26ffaSEric DeVolder {
805*f7e26ffaSEric DeVolder     DeviceClass *dc = DEVICE_CLASS(klass);
806*f7e26ffaSEric DeVolder     PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
807*f7e26ffaSEric DeVolder 
808*f7e26ffaSEric DeVolder     trace_acpi_erst_class_init_in();
809*f7e26ffaSEric DeVolder     k->realize = erst_realizefn;
810*f7e26ffaSEric DeVolder     k->vendor_id = PCI_VENDOR_ID_REDHAT;
811*f7e26ffaSEric DeVolder     k->device_id = PCI_DEVICE_ID_REDHAT_ACPI_ERST;
812*f7e26ffaSEric DeVolder     k->revision = 0x00;
813*f7e26ffaSEric DeVolder     k->class_id = PCI_CLASS_OTHERS;
814*f7e26ffaSEric DeVolder     dc->reset = erst_reset;
815*f7e26ffaSEric DeVolder     dc->vmsd = &erst_vmstate;
816*f7e26ffaSEric DeVolder     dc->user_creatable = true;
817*f7e26ffaSEric DeVolder     dc->hotpluggable = false;
818*f7e26ffaSEric DeVolder     device_class_set_props(dc, erst_properties);
819*f7e26ffaSEric DeVolder     dc->desc = "ACPI Error Record Serialization Table (ERST) device";
820*f7e26ffaSEric DeVolder     set_bit(DEVICE_CATEGORY_MISC, dc->categories);
821*f7e26ffaSEric DeVolder     trace_acpi_erst_class_init_out();
822*f7e26ffaSEric DeVolder }
823*f7e26ffaSEric DeVolder 
824*f7e26ffaSEric DeVolder static const TypeInfo erst_type_info = {
825*f7e26ffaSEric DeVolder     .name          = TYPE_ACPI_ERST,
826*f7e26ffaSEric DeVolder     .parent        = TYPE_PCI_DEVICE,
827*f7e26ffaSEric DeVolder     .class_init    = erst_class_init,
828*f7e26ffaSEric DeVolder     .instance_size = sizeof(ERSTDeviceState),
829*f7e26ffaSEric DeVolder     .interfaces = (InterfaceInfo[]) {
830*f7e26ffaSEric DeVolder         { INTERFACE_CONVENTIONAL_PCI_DEVICE },
831*f7e26ffaSEric DeVolder         { }
832*f7e26ffaSEric DeVolder     }
833*f7e26ffaSEric DeVolder };
834*f7e26ffaSEric DeVolder 
835*f7e26ffaSEric DeVolder static void erst_register_types(void)
836*f7e26ffaSEric DeVolder {
837*f7e26ffaSEric DeVolder     type_register_static(&erst_type_info);
838*f7e26ffaSEric DeVolder }
839*f7e26ffaSEric DeVolder 
840*f7e26ffaSEric DeVolder type_init(erst_register_types)
841