5
*/
6
7
#include "qemu/osdep.h"
8
+#include "qemu/log.h"
9
+#include "qemu/units.h"
10
+#include "system/address-spaces.h"
11
#include "system/runstate.h"
12
#include "hw/ppc/pnv.h"
13
#include "hw/ppc/pnv_mpipl.h"
14
+#include <math.h>
15
+
16
+#define MDST_TABLE_RELOCATED \
17
+ (pnv->mpipl_state.skiboot_base + MDST_TABLE_OFF)
18
+#define MDDT_TABLE_RELOCATED \
19
+ (pnv->mpipl_state.skiboot_base + MDDT_TABLE_OFF)
20
+
21
+/*
22
+ * Preserve the memory regions as pointed by MDST table
23
+ *
24
+ * During this, the memory region pointed by entries in MDST, are 'copied'
25
+ * as it is to the memory region pointed by corresponding entry in MDDT
26
+ *
27
+ * Notes: All reads should consider data coming from skiboot as big-endian,
28
+ * and data written should also be in big-endian
29
+ */
30
+static bool pnv_mpipl_preserve_mem(PnvMachineState *pnv)
31
+{
32
+ g_autofree MdstTableEntry *mdst = g_malloc(MDST_TABLE_SIZE);
33
+ g_autofree MddtTableEntry *mddt = g_malloc(MDDT_TABLE_SIZE);
34
+ g_autofree MdrtTableEntry *mdrt = g_malloc0(MDRT_TABLE_SIZE);
35
+ AddressSpace *default_as = &address_space_memory;
36
+ MemTxResult io_result;
37
+ MemTxAttrs attrs;
38
+ uint64_t src_addr, dest_addr;
39
+ uint32_t data_len;
40
+ uint64_t num_chunks, chunk_id = 0;
41
+ int mdrt_idx = 0;
42
+
43
+ /* Mark the memory transactions as privileged memory access */
44
+ attrs.user = 0;
45
+ attrs.memory = 1;
46
+
47
+ if (pnv->mpipl_state.mdrt_table) {
48
+ /*
49
+ * MDRT table allocated from some past crash, free the memory to
50
+ * prevent memory leak
51
+ */
52
+ g_free(pnv->mpipl_state.mdrt_table);
53
+ pnv->mpipl_state.num_mdrt_entries = 0;
54
+ }
55
+
56
+ io_result = address_space_read(default_as, MDST_TABLE_RELOCATED, attrs,
57
+ mdst, MDST_TABLE_SIZE);
58
+ if (io_result != MEMTX_OK) {
59
+ qemu_log_mask(LOG_GUEST_ERROR,
60
+ "MPIPL: Failed to read MDST table at: 0x" TARGET_FMT_lx "\n",
61
+ MDST_TABLE_RELOCATED);
62
+
63
+ return false;
64
+ }
65
+
66
+ io_result = address_space_read(default_as, MDDT_TABLE_RELOCATED, attrs,
67
+ mddt, MDDT_TABLE_SIZE);
68
+ if (io_result != MEMTX_OK) {
69
+ qemu_log_mask(LOG_GUEST_ERROR,
70
+ "MPIPL: Failed to read MDDT table at: 0x" TARGET_FMT_lx "\n",
71
+ MDDT_TABLE_RELOCATED);
72
+
73
+ return false;
74
+ }
75
+
76
+ /* Try to read all entries */
77
+ for (int i = 0; i < MDST_MAX_ENTRIES; ++i) {
78
+ g_autofree uint8_t *copy_buffer = NULL;
79
+ bool is_copy_failed = false;
80
+
81
+ /* Considering entry with address and size as 0, as end of table */
82
+ if ((mdst[i].addr == 0) && (mdst[i].size == 0)) {
83
+ break;
84
+ }
85
+
86
+ if (mdst[i].size != mddt[i].size) {
87
+ qemu_log_mask(LOG_TRACE,
88
+ "Warning: Invalid entry, size mismatch in MDST & MDDT\n");
89
+ continue;
90
+ }
91
+
92
+ if (mdst[i].data_region != mddt[i].data_region) {
93
+ qemu_log_mask(LOG_TRACE,
94
+ "Warning: Invalid entry, region mismatch in MDST & MDDT\n");
95
+ continue;
96
+ }
97
+
98
+ src_addr = be64_to_cpu(mdst[i].addr) & ~HRMOR_BIT;
99
+ dest_addr = be64_to_cpu(mddt[i].addr) & ~HRMOR_BIT;
100
+ data_len = be32_to_cpu(mddt[i].size);
101
+
102
+#define COPY_CHUNK_SIZE ((size_t)(32 * MiB))
103
+ copy_buffer = g_try_malloc(COPY_CHUNK_SIZE);
104
+ if (copy_buffer == NULL) {
105
+ qemu_log_mask(LOG_GUEST_ERROR,
106
+ "MPIPL: Failed allocating memory (size: %zu) for copying"
107
+ " reserved memory regions\n", COPY_CHUNK_SIZE);
108
+ is_copy_failed = true;
109
+ continue;
110
+ }
111
+
112
+ chunk_id = 0;
113
+ num_chunks = ceil((data_len * 1.0f) / COPY_CHUNK_SIZE);
114
+ while (chunk_id < num_chunks) {
115
+ /* Take minimum of bytes left to copy, and chunk size */
116
+ uint64_t copy_len = MIN(
117
+ data_len - (chunk_id * COPY_CHUNK_SIZE),
118
+ COPY_CHUNK_SIZE
119
+ );
120
+
121
+ /* Copy the source region to destination */
122
+ io_result = address_space_read(default_as, src_addr, attrs,
123
+ copy_buffer, copy_len);
124
+ if (io_result != MEMTX_OK) {
125
+ qemu_log_mask(LOG_GUEST_ERROR,
126
+ "MPIPL: Failed to read region at: 0x%" PRIx64 "\n",
127
+ src_addr);
128
+ is_copy_failed = true;
129
+ break;
130
+ }
131
+
132
+ io_result = address_space_write(default_as, dest_addr, attrs,
133
+ copy_buffer, copy_len);
134
+ if (io_result != MEMTX_OK) {
135
+ qemu_log_mask(LOG_GUEST_ERROR,
136
+ "MPIPL: Failed to write region at: 0x%" PRIx64 "\n",
137
+ dest_addr);
138
+ is_copy_failed = true;
139
+ break;
140
+ }
141
+
142
+ src_addr += COPY_CHUNK_SIZE;
143
+ dest_addr += COPY_CHUNK_SIZE;
144
+ ++chunk_id;
145
+ }
146
+#undef COPY_CHUNK_SIZE
147
+
148
+ if (is_copy_failed) {
149
+ /*
150
+ * HDAT doesn't specify an error code in MDRT for failed copy,
151
+ * and doesn't specify how this is to be handled
152
+ * Hence just skip adding an entry in MDRT, as done for size
153
+ * mismatch or other inconsistency between MDST/MDDT
154
+ */
155
+ continue;
156
+ }
157
+
158
+ /* Populate entry in MDRT table if preserving successful */
159
+ mdrt[mdrt_idx].src_addr = cpu_to_be64(src_addr);
160
+ mdrt[mdrt_idx].dest_addr = cpu_to_be64(dest_addr);
161
+ mdrt[mdrt_idx].size = cpu_to_be32(data_len);
162
+ mdrt[mdrt_idx].data_region = mdst[i].data_region;
163
+ ++mdrt_idx;
164
+ }
165
+
166
+ pnv->mpipl_state.mdrt_table = g_steal_pointer(&mdrt);
167
+ pnv->mpipl_state.num_mdrt_entries = mdrt_idx;
168
+
169
+ return true;
170
+}
171
172
void do_mpipl_preserve(PnvMachineState *pnv)
173
{
174
+ pnv_mpipl_preserve_mem(pnv);
175
+
176
/* Mark next boot as Memory-preserving boot */
177
pnv->mpipl_state.is_next_boot_mpipl = true;
178