powerpc: Fix hardware IRQs with MMU on exceptions when HV=0
[deliverable/linux.git] / arch / powerpc / platforms / pseries / mobility.c
CommitLineData
410bccf9
NF
1/*
2 * Support for Partition Mobility/Migration
3 *
4 * Copyright (C) 2010 Nathan Fontenot
5 * Copyright (C) 2010 IBM Corporation
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License version
9 * 2 as published by the Free Software Foundation.
10 */
11
12#include <linux/kernel.h>
13#include <linux/kobject.h>
14#include <linux/smp.h>
b56eade5 15#include <linux/stat.h>
410bccf9
NF
16#include <linux/completion.h>
17#include <linux/device.h>
18#include <linux/delay.h>
19#include <linux/slab.h>
20
21#include <asm/rtas.h>
22#include "pseries.h"
23
24static struct kobject *mobility_kobj;
25
26struct update_props_workarea {
27 u32 phandle;
28 u32 state;
29 u64 reserved;
30 u32 nprops;
31};
32
33#define NODE_ACTION_MASK 0xff000000
34#define NODE_COUNT_MASK 0x00ffffff
35
36#define DELETE_DT_NODE 0x01000000
37#define UPDATE_DT_NODE 0x02000000
38#define ADD_DT_NODE 0x03000000
39
40static int mobility_rtas_call(int token, char *buf)
41{
42 int rc;
43
44 spin_lock(&rtas_data_buf_lock);
45
46 memcpy(rtas_data_buf, buf, RTAS_DATA_BUF_SIZE);
47 rc = rtas_call(token, 2, 1, NULL, rtas_data_buf, 1);
48 memcpy(buf, rtas_data_buf, RTAS_DATA_BUF_SIZE);
49
50 spin_unlock(&rtas_data_buf_lock);
51 return rc;
52}
53
54static int delete_dt_node(u32 phandle)
55{
56 struct device_node *dn;
57
58 dn = of_find_node_by_phandle(phandle);
59 if (!dn)
60 return -ENOENT;
61
62 dlpar_detach_node(dn);
63 return 0;
64}
65
66static int update_dt_property(struct device_node *dn, struct property **prop,
67 const char *name, u32 vd, char *value)
68{
69 struct property *new_prop = *prop;
410bccf9
NF
70 int more = 0;
71
72 /* A negative 'vd' value indicates that only part of the new property
73 * value is contained in the buffer and we need to call
74 * ibm,update-properties again to get the rest of the value.
75 *
76 * A negative value is also the two's compliment of the actual value.
77 */
78 if (vd & 0x80000000) {
79 vd = ~vd + 1;
80 more = 1;
81 }
82
83 if (new_prop) {
84 /* partial property fixup */
85 char *new_data = kzalloc(new_prop->length + vd, GFP_KERNEL);
86 if (!new_data)
87 return -ENOMEM;
88
89 memcpy(new_data, new_prop->value, new_prop->length);
90 memcpy(new_data + new_prop->length, value, vd);
91
92 kfree(new_prop->value);
93 new_prop->value = new_data;
94 new_prop->length += vd;
95 } else {
96 new_prop = kzalloc(sizeof(*new_prop), GFP_KERNEL);
97 if (!new_prop)
98 return -ENOMEM;
99
100 new_prop->name = kstrdup(name, GFP_KERNEL);
101 if (!new_prop->name) {
102 kfree(new_prop);
103 return -ENOMEM;
104 }
105
106 new_prop->length = vd;
107 new_prop->value = kzalloc(new_prop->length, GFP_KERNEL);
108 if (!new_prop->value) {
109 kfree(new_prop->name);
110 kfree(new_prop);
111 return -ENOMEM;
112 }
113
114 memcpy(new_prop->value, value, vd);
115 *prop = new_prop;
116 }
117
118 if (!more) {
79d1c712 119 of_update_property(dn, new_prop);
410bccf9
NF
120 new_prop = NULL;
121 }
122
123 return 0;
124}
125
126static int update_dt_node(u32 phandle)
127{
128 struct update_props_workarea *upwa;
129 struct device_node *dn;
130 struct property *prop = NULL;
131 int i, rc;
132 char *prop_data;
133 char *rtas_buf;
134 int update_properties_token;
135
136 update_properties_token = rtas_token("ibm,update-properties");
137 if (update_properties_token == RTAS_UNKNOWN_SERVICE)
138 return -EINVAL;
139
140 rtas_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL);
141 if (!rtas_buf)
142 return -ENOMEM;
143
144 dn = of_find_node_by_phandle(phandle);
145 if (!dn) {
146 kfree(rtas_buf);
147 return -ENOENT;
148 }
149
150 upwa = (struct update_props_workarea *)&rtas_buf[0];
151 upwa->phandle = phandle;
152
153 do {
154 rc = mobility_rtas_call(update_properties_token, rtas_buf);
155 if (rc < 0)
156 break;
157
158 prop_data = rtas_buf + sizeof(*upwa);
159
160 for (i = 0; i < upwa->nprops; i++) {
161 char *prop_name;
162 u32 vd;
163
164 prop_name = prop_data + 1;
165 prop_data += strlen(prop_name) + 1;
166 vd = *prop_data++;
167
168 switch (vd) {
169 case 0x00000000:
170 /* name only property, nothing to do */
171 break;
172
173 case 0x80000000:
174 prop = of_find_property(dn, prop_name, NULL);
79d1c712 175 of_remove_property(dn, prop);
410bccf9
NF
176 prop = NULL;
177 break;
178
179 default:
180 rc = update_dt_property(dn, &prop, prop_name,
181 vd, prop_data);
182 if (rc) {
183 printk(KERN_ERR "Could not update %s"
184 " property\n", prop_name);
185 }
186
187 prop_data += vd;
188 }
189 }
190 } while (rc == 1);
191
192 of_node_put(dn);
193 kfree(rtas_buf);
194 return 0;
195}
196
197static int add_dt_node(u32 parent_phandle, u32 drc_index)
198{
199 struct device_node *dn;
200 struct device_node *parent_dn;
201 int rc;
202
203 dn = dlpar_configure_connector(drc_index);
204 if (!dn)
205 return -ENOENT;
206
207 parent_dn = of_find_node_by_phandle(parent_phandle);
208 if (!parent_dn) {
209 dlpar_free_cc_nodes(dn);
210 return -ENOENT;
211 }
212
213 dn->parent = parent_dn;
214 rc = dlpar_attach_node(dn);
215 if (rc)
216 dlpar_free_cc_nodes(dn);
217
218 of_node_put(parent_dn);
219 return rc;
220}
221
222static int pseries_devicetree_update(void)
223{
224 char *rtas_buf;
225 u32 *data;
226 int update_nodes_token;
227 int rc;
228
229 update_nodes_token = rtas_token("ibm,update-nodes");
230 if (update_nodes_token == RTAS_UNKNOWN_SERVICE)
231 return -EINVAL;
232
233 rtas_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL);
234 if (!rtas_buf)
235 return -ENOMEM;
236
237 do {
238 rc = mobility_rtas_call(update_nodes_token, rtas_buf);
239 if (rc && rc != 1)
240 break;
241
242 data = (u32 *)rtas_buf + 4;
243 while (*data & NODE_ACTION_MASK) {
244 int i;
245 u32 action = *data & NODE_ACTION_MASK;
246 int node_count = *data & NODE_COUNT_MASK;
247
248 data++;
249
250 for (i = 0; i < node_count; i++) {
251 u32 phandle = *data++;
252 u32 drc_index;
253
254 switch (action) {
255 case DELETE_DT_NODE:
256 delete_dt_node(phandle);
257 break;
258 case UPDATE_DT_NODE:
259 update_dt_node(phandle);
260 break;
261 case ADD_DT_NODE:
262 drc_index = *data++;
263 add_dt_node(phandle, drc_index);
264 break;
265 }
266 }
267 }
268 } while (rc == 1);
269
270 kfree(rtas_buf);
271 return rc;
272}
273
274void post_mobility_fixup(void)
275{
276 int rc;
277 int activate_fw_token;
278
279 rc = pseries_devicetree_update();
280 if (rc) {
281 printk(KERN_ERR "Initial post-mobility device tree update "
282 "failed: %d\n", rc);
283 return;
284 }
285
286 activate_fw_token = rtas_token("ibm,activate-firmware");
287 if (activate_fw_token == RTAS_UNKNOWN_SERVICE) {
288 printk(KERN_ERR "Could not make post-mobility "
289 "activate-fw call.\n");
290 return;
291 }
292
293 rc = rtas_call(activate_fw_token, 0, 1, NULL);
294 if (!rc) {
295 rc = pseries_devicetree_update();
296 if (rc)
297 printk(KERN_ERR "Secondary post-mobility device tree "
298 "update failed: %d\n", rc);
299 } else {
300 printk(KERN_ERR "Post-mobility activate-fw failed: %d\n", rc);
301 return;
302 }
303
304 return;
305}
306
307static ssize_t migrate_store(struct class *class, struct class_attribute *attr,
308 const char *buf, size_t count)
309{
310 struct rtas_args args;
311 u64 streamid;
312 int rc;
313
314 rc = strict_strtoull(buf, 0, &streamid);
315 if (rc)
316 return rc;
317
318 memset(&args, 0, sizeof(args));
319 args.token = rtas_token("ibm,suspend-me");
320 args.nargs = 2;
321 args.nret = 1;
322
323 args.args[0] = streamid >> 32 ;
324 args.args[1] = streamid & 0xffffffff;
325 args.rets = &args.args[args.nargs];
326
327 do {
328 args.rets[0] = 0;
329 rc = rtas_ibm_suspend_me(&args);
330 if (!rc && args.rets[0] == RTAS_NOT_SUSPENDABLE)
331 ssleep(1);
332 } while (!rc && args.rets[0] == RTAS_NOT_SUSPENDABLE);
333
334 if (rc)
335 return rc;
336 else if (args.rets[0])
337 return args.rets[0];
338
339 post_mobility_fixup();
340 return count;
341}
342
343static CLASS_ATTR(migration, S_IWUSR, NULL, migrate_store);
344
345static int __init mobility_sysfs_init(void)
346{
347 int rc;
348
349 mobility_kobj = kobject_create_and_add("mobility", kernel_kobj);
350 if (!mobility_kobj)
351 return -ENOMEM;
352
353 rc = sysfs_create_file(mobility_kobj, &class_attr_migration.attr);
354
355 return rc;
356}
357device_initcall(mobility_sysfs_init);
This page took 0.21269 seconds and 5 git commands to generate.