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