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