1410bccf9SNathan Fontenot /* 2410bccf9SNathan Fontenot * Support for Partition Mobility/Migration 3410bccf9SNathan Fontenot * 4410bccf9SNathan Fontenot * Copyright (C) 2010 Nathan Fontenot 5410bccf9SNathan Fontenot * Copyright (C) 2010 IBM Corporation 6410bccf9SNathan Fontenot * 7410bccf9SNathan Fontenot * This program is free software; you can redistribute it and/or 8410bccf9SNathan Fontenot * modify it under the terms of the GNU General Public License version 9410bccf9SNathan Fontenot * 2 as published by the Free Software Foundation. 10410bccf9SNathan Fontenot */ 11410bccf9SNathan Fontenot 12410bccf9SNathan Fontenot #include <linux/kernel.h> 13410bccf9SNathan Fontenot #include <linux/kobject.h> 14410bccf9SNathan Fontenot #include <linux/smp.h> 15b56eade5SPaul Gortmaker #include <linux/stat.h> 16410bccf9SNathan Fontenot #include <linux/completion.h> 17410bccf9SNathan Fontenot #include <linux/device.h> 18410bccf9SNathan Fontenot #include <linux/delay.h> 19410bccf9SNathan Fontenot #include <linux/slab.h> 20410bccf9SNathan Fontenot 21410bccf9SNathan Fontenot #include <asm/rtas.h> 22410bccf9SNathan Fontenot #include "pseries.h" 23410bccf9SNathan Fontenot 24410bccf9SNathan Fontenot static struct kobject *mobility_kobj; 25410bccf9SNathan Fontenot 26410bccf9SNathan Fontenot struct update_props_workarea { 27410bccf9SNathan Fontenot u32 phandle; 28410bccf9SNathan Fontenot u32 state; 29410bccf9SNathan Fontenot u64 reserved; 30410bccf9SNathan Fontenot u32 nprops; 31410bccf9SNathan Fontenot }; 32410bccf9SNathan Fontenot 33410bccf9SNathan Fontenot #define NODE_ACTION_MASK 0xff000000 34410bccf9SNathan Fontenot #define NODE_COUNT_MASK 0x00ffffff 35410bccf9SNathan Fontenot 36410bccf9SNathan Fontenot #define DELETE_DT_NODE 0x01000000 37410bccf9SNathan Fontenot #define UPDATE_DT_NODE 0x02000000 38410bccf9SNathan Fontenot #define ADD_DT_NODE 0x03000000 39410bccf9SNathan Fontenot 40762ec157SNathan Fontenot #define MIGRATION_SCOPE (1) 41762ec157SNathan Fontenot 42762ec157SNathan Fontenot static int mobility_rtas_call(int token, char *buf, s32 scope) 43410bccf9SNathan Fontenot { 44410bccf9SNathan Fontenot int rc; 45410bccf9SNathan Fontenot 46410bccf9SNathan Fontenot spin_lock(&rtas_data_buf_lock); 47410bccf9SNathan Fontenot 48410bccf9SNathan Fontenot memcpy(rtas_data_buf, buf, RTAS_DATA_BUF_SIZE); 49762ec157SNathan Fontenot rc = rtas_call(token, 2, 1, NULL, rtas_data_buf, scope); 50410bccf9SNathan Fontenot memcpy(buf, rtas_data_buf, RTAS_DATA_BUF_SIZE); 51410bccf9SNathan Fontenot 52410bccf9SNathan Fontenot spin_unlock(&rtas_data_buf_lock); 53410bccf9SNathan Fontenot return rc; 54410bccf9SNathan Fontenot } 55410bccf9SNathan Fontenot 56410bccf9SNathan Fontenot static int delete_dt_node(u32 phandle) 57410bccf9SNathan Fontenot { 58410bccf9SNathan Fontenot struct device_node *dn; 59410bccf9SNathan Fontenot 60410bccf9SNathan Fontenot dn = of_find_node_by_phandle(phandle); 61410bccf9SNathan Fontenot if (!dn) 62410bccf9SNathan Fontenot return -ENOENT; 63410bccf9SNathan Fontenot 64410bccf9SNathan Fontenot dlpar_detach_node(dn); 65410bccf9SNathan Fontenot return 0; 66410bccf9SNathan Fontenot } 67410bccf9SNathan Fontenot 68410bccf9SNathan Fontenot static int update_dt_property(struct device_node *dn, struct property **prop, 69410bccf9SNathan Fontenot const char *name, u32 vd, char *value) 70410bccf9SNathan Fontenot { 71410bccf9SNathan Fontenot struct property *new_prop = *prop; 72410bccf9SNathan Fontenot int more = 0; 73410bccf9SNathan Fontenot 74410bccf9SNathan Fontenot /* A negative 'vd' value indicates that only part of the new property 75410bccf9SNathan Fontenot * value is contained in the buffer and we need to call 76410bccf9SNathan Fontenot * ibm,update-properties again to get the rest of the value. 77410bccf9SNathan Fontenot * 78410bccf9SNathan Fontenot * A negative value is also the two's compliment of the actual value. 79410bccf9SNathan Fontenot */ 80410bccf9SNathan Fontenot if (vd & 0x80000000) { 81410bccf9SNathan Fontenot vd = ~vd + 1; 82410bccf9SNathan Fontenot more = 1; 83410bccf9SNathan Fontenot } 84410bccf9SNathan Fontenot 85410bccf9SNathan Fontenot if (new_prop) { 86410bccf9SNathan Fontenot /* partial property fixup */ 87410bccf9SNathan Fontenot char *new_data = kzalloc(new_prop->length + vd, GFP_KERNEL); 88410bccf9SNathan Fontenot if (!new_data) 89410bccf9SNathan Fontenot return -ENOMEM; 90410bccf9SNathan Fontenot 91410bccf9SNathan Fontenot memcpy(new_data, new_prop->value, new_prop->length); 92410bccf9SNathan Fontenot memcpy(new_data + new_prop->length, value, vd); 93410bccf9SNathan Fontenot 94410bccf9SNathan Fontenot kfree(new_prop->value); 95410bccf9SNathan Fontenot new_prop->value = new_data; 96410bccf9SNathan Fontenot new_prop->length += vd; 97410bccf9SNathan Fontenot } else { 98410bccf9SNathan Fontenot new_prop = kzalloc(sizeof(*new_prop), GFP_KERNEL); 99410bccf9SNathan Fontenot if (!new_prop) 100410bccf9SNathan Fontenot return -ENOMEM; 101410bccf9SNathan Fontenot 102410bccf9SNathan Fontenot new_prop->name = kstrdup(name, GFP_KERNEL); 103410bccf9SNathan Fontenot if (!new_prop->name) { 104410bccf9SNathan Fontenot kfree(new_prop); 105410bccf9SNathan Fontenot return -ENOMEM; 106410bccf9SNathan Fontenot } 107410bccf9SNathan Fontenot 108410bccf9SNathan Fontenot new_prop->length = vd; 109410bccf9SNathan Fontenot new_prop->value = kzalloc(new_prop->length, GFP_KERNEL); 110410bccf9SNathan Fontenot if (!new_prop->value) { 111410bccf9SNathan Fontenot kfree(new_prop->name); 112410bccf9SNathan Fontenot kfree(new_prop); 113410bccf9SNathan Fontenot return -ENOMEM; 114410bccf9SNathan Fontenot } 115410bccf9SNathan Fontenot 116410bccf9SNathan Fontenot memcpy(new_prop->value, value, vd); 117410bccf9SNathan Fontenot *prop = new_prop; 118410bccf9SNathan Fontenot } 119410bccf9SNathan Fontenot 120410bccf9SNathan Fontenot if (!more) { 12179d1c712SNathan Fontenot of_update_property(dn, new_prop); 122d8e533b4STyrel Datwyler *prop = NULL; 123410bccf9SNathan Fontenot } 124410bccf9SNathan Fontenot 125410bccf9SNathan Fontenot return 0; 126410bccf9SNathan Fontenot } 127410bccf9SNathan Fontenot 128762ec157SNathan Fontenot static int update_dt_node(u32 phandle, s32 scope) 129410bccf9SNathan Fontenot { 130410bccf9SNathan Fontenot struct update_props_workarea *upwa; 131410bccf9SNathan Fontenot struct device_node *dn; 132410bccf9SNathan Fontenot struct property *prop = NULL; 133*638a405fSTyrel Datwyler int i, rc, rtas_rc; 134410bccf9SNathan Fontenot char *prop_data; 135410bccf9SNathan Fontenot char *rtas_buf; 136410bccf9SNathan Fontenot int update_properties_token; 1372e9b7b02SNathan Fontenot u32 vd; 138410bccf9SNathan Fontenot 139410bccf9SNathan Fontenot update_properties_token = rtas_token("ibm,update-properties"); 140410bccf9SNathan Fontenot if (update_properties_token == RTAS_UNKNOWN_SERVICE) 141410bccf9SNathan Fontenot return -EINVAL; 142410bccf9SNathan Fontenot 143410bccf9SNathan Fontenot rtas_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL); 144410bccf9SNathan Fontenot if (!rtas_buf) 145410bccf9SNathan Fontenot return -ENOMEM; 146410bccf9SNathan Fontenot 147410bccf9SNathan Fontenot dn = of_find_node_by_phandle(phandle); 148410bccf9SNathan Fontenot if (!dn) { 149410bccf9SNathan Fontenot kfree(rtas_buf); 150410bccf9SNathan Fontenot return -ENOENT; 151410bccf9SNathan Fontenot } 152410bccf9SNathan Fontenot 153410bccf9SNathan Fontenot upwa = (struct update_props_workarea *)&rtas_buf[0]; 154410bccf9SNathan Fontenot upwa->phandle = phandle; 155410bccf9SNathan Fontenot 156410bccf9SNathan Fontenot do { 157*638a405fSTyrel Datwyler rtas_rc = mobility_rtas_call(update_properties_token, rtas_buf, 158762ec157SNathan Fontenot scope); 159*638a405fSTyrel Datwyler if (rtas_rc < 0) 160410bccf9SNathan Fontenot break; 161410bccf9SNathan Fontenot 162410bccf9SNathan Fontenot prop_data = rtas_buf + sizeof(*upwa); 163410bccf9SNathan Fontenot 1642e9b7b02SNathan Fontenot /* The first element of the buffer is the path of the node 1652e9b7b02SNathan Fontenot * being updated in the form of a 8 byte string length 1662e9b7b02SNathan Fontenot * followed by the string. Skip past this to get to the 1672e9b7b02SNathan Fontenot * properties being updated. 1682e9b7b02SNathan Fontenot */ 169410bccf9SNathan Fontenot vd = *prop_data++; 1702e9b7b02SNathan Fontenot prop_data += vd; 1712e9b7b02SNathan Fontenot 1722e9b7b02SNathan Fontenot /* The path we skipped over is counted as one of the elements 1732e9b7b02SNathan Fontenot * returned so start counting at one. 1742e9b7b02SNathan Fontenot */ 1752e9b7b02SNathan Fontenot for (i = 1; i < upwa->nprops; i++) { 1762e9b7b02SNathan Fontenot char *prop_name; 1772e9b7b02SNathan Fontenot 1782e9b7b02SNathan Fontenot prop_name = prop_data; 1792e9b7b02SNathan Fontenot prop_data += strlen(prop_name) + 1; 1802e9b7b02SNathan Fontenot vd = *(u32 *)prop_data; 1812e9b7b02SNathan Fontenot prop_data += sizeof(vd); 182410bccf9SNathan Fontenot 183410bccf9SNathan Fontenot switch (vd) { 184410bccf9SNathan Fontenot case 0x00000000: 185410bccf9SNathan Fontenot /* name only property, nothing to do */ 186410bccf9SNathan Fontenot break; 187410bccf9SNathan Fontenot 188410bccf9SNathan Fontenot case 0x80000000: 189410bccf9SNathan Fontenot prop = of_find_property(dn, prop_name, NULL); 19079d1c712SNathan Fontenot of_remove_property(dn, prop); 191410bccf9SNathan Fontenot prop = NULL; 192410bccf9SNathan Fontenot break; 193410bccf9SNathan Fontenot 194410bccf9SNathan Fontenot default: 195410bccf9SNathan Fontenot rc = update_dt_property(dn, &prop, prop_name, 196410bccf9SNathan Fontenot vd, prop_data); 197410bccf9SNathan Fontenot if (rc) { 198410bccf9SNathan Fontenot printk(KERN_ERR "Could not update %s" 199410bccf9SNathan Fontenot " property\n", prop_name); 200410bccf9SNathan Fontenot } 201410bccf9SNathan Fontenot 202410bccf9SNathan Fontenot prop_data += vd; 203410bccf9SNathan Fontenot } 204410bccf9SNathan Fontenot } 205*638a405fSTyrel Datwyler } while (rtas_rc == 1); 206410bccf9SNathan Fontenot 207410bccf9SNathan Fontenot of_node_put(dn); 208410bccf9SNathan Fontenot kfree(rtas_buf); 209410bccf9SNathan Fontenot return 0; 210410bccf9SNathan Fontenot } 211410bccf9SNathan Fontenot 212410bccf9SNathan Fontenot static int add_dt_node(u32 parent_phandle, u32 drc_index) 213410bccf9SNathan Fontenot { 214410bccf9SNathan Fontenot struct device_node *dn; 215410bccf9SNathan Fontenot struct device_node *parent_dn; 216410bccf9SNathan Fontenot int rc; 217410bccf9SNathan Fontenot 218410bccf9SNathan Fontenot dn = dlpar_configure_connector(drc_index); 219410bccf9SNathan Fontenot if (!dn) 220410bccf9SNathan Fontenot return -ENOENT; 221410bccf9SNathan Fontenot 222410bccf9SNathan Fontenot parent_dn = of_find_node_by_phandle(parent_phandle); 223410bccf9SNathan Fontenot if (!parent_dn) { 224410bccf9SNathan Fontenot dlpar_free_cc_nodes(dn); 225410bccf9SNathan Fontenot return -ENOENT; 226410bccf9SNathan Fontenot } 227410bccf9SNathan Fontenot 228410bccf9SNathan Fontenot dn->parent = parent_dn; 229410bccf9SNathan Fontenot rc = dlpar_attach_node(dn); 230410bccf9SNathan Fontenot if (rc) 231410bccf9SNathan Fontenot dlpar_free_cc_nodes(dn); 232410bccf9SNathan Fontenot 233410bccf9SNathan Fontenot of_node_put(parent_dn); 234410bccf9SNathan Fontenot return rc; 235410bccf9SNathan Fontenot } 236410bccf9SNathan Fontenot 237762ec157SNathan Fontenot int pseries_devicetree_update(s32 scope) 238410bccf9SNathan Fontenot { 239410bccf9SNathan Fontenot char *rtas_buf; 240410bccf9SNathan Fontenot u32 *data; 241410bccf9SNathan Fontenot int update_nodes_token; 242410bccf9SNathan Fontenot int rc; 243410bccf9SNathan Fontenot 244410bccf9SNathan Fontenot update_nodes_token = rtas_token("ibm,update-nodes"); 245410bccf9SNathan Fontenot if (update_nodes_token == RTAS_UNKNOWN_SERVICE) 246410bccf9SNathan Fontenot return -EINVAL; 247410bccf9SNathan Fontenot 248410bccf9SNathan Fontenot rtas_buf = kzalloc(RTAS_DATA_BUF_SIZE, GFP_KERNEL); 249410bccf9SNathan Fontenot if (!rtas_buf) 250410bccf9SNathan Fontenot return -ENOMEM; 251410bccf9SNathan Fontenot 252410bccf9SNathan Fontenot do { 253762ec157SNathan Fontenot rc = mobility_rtas_call(update_nodes_token, rtas_buf, scope); 254410bccf9SNathan Fontenot if (rc && rc != 1) 255410bccf9SNathan Fontenot break; 256410bccf9SNathan Fontenot 257410bccf9SNathan Fontenot data = (u32 *)rtas_buf + 4; 258410bccf9SNathan Fontenot while (*data & NODE_ACTION_MASK) { 259410bccf9SNathan Fontenot int i; 260410bccf9SNathan Fontenot u32 action = *data & NODE_ACTION_MASK; 261410bccf9SNathan Fontenot int node_count = *data & NODE_COUNT_MASK; 262410bccf9SNathan Fontenot 263410bccf9SNathan Fontenot data++; 264410bccf9SNathan Fontenot 265410bccf9SNathan Fontenot for (i = 0; i < node_count; i++) { 266410bccf9SNathan Fontenot u32 phandle = *data++; 267410bccf9SNathan Fontenot u32 drc_index; 268410bccf9SNathan Fontenot 269410bccf9SNathan Fontenot switch (action) { 270410bccf9SNathan Fontenot case DELETE_DT_NODE: 271410bccf9SNathan Fontenot delete_dt_node(phandle); 272410bccf9SNathan Fontenot break; 273410bccf9SNathan Fontenot case UPDATE_DT_NODE: 274762ec157SNathan Fontenot update_dt_node(phandle, scope); 275410bccf9SNathan Fontenot break; 276410bccf9SNathan Fontenot case ADD_DT_NODE: 277410bccf9SNathan Fontenot drc_index = *data++; 278410bccf9SNathan Fontenot add_dt_node(phandle, drc_index); 279410bccf9SNathan Fontenot break; 280410bccf9SNathan Fontenot } 281410bccf9SNathan Fontenot } 282410bccf9SNathan Fontenot } 283410bccf9SNathan Fontenot } while (rc == 1); 284410bccf9SNathan Fontenot 285410bccf9SNathan Fontenot kfree(rtas_buf); 286410bccf9SNathan Fontenot return rc; 287410bccf9SNathan Fontenot } 288410bccf9SNathan Fontenot 289410bccf9SNathan Fontenot void post_mobility_fixup(void) 290410bccf9SNathan Fontenot { 291410bccf9SNathan Fontenot int rc; 292410bccf9SNathan Fontenot int activate_fw_token; 293410bccf9SNathan Fontenot 294762ec157SNathan Fontenot rc = pseries_devicetree_update(MIGRATION_SCOPE); 295410bccf9SNathan Fontenot if (rc) { 296410bccf9SNathan Fontenot printk(KERN_ERR "Initial post-mobility device tree update " 297410bccf9SNathan Fontenot "failed: %d\n", rc); 298410bccf9SNathan Fontenot return; 299410bccf9SNathan Fontenot } 300410bccf9SNathan Fontenot 301410bccf9SNathan Fontenot activate_fw_token = rtas_token("ibm,activate-firmware"); 302410bccf9SNathan Fontenot if (activate_fw_token == RTAS_UNKNOWN_SERVICE) { 303410bccf9SNathan Fontenot printk(KERN_ERR "Could not make post-mobility " 304410bccf9SNathan Fontenot "activate-fw call.\n"); 305410bccf9SNathan Fontenot return; 306410bccf9SNathan Fontenot } 307410bccf9SNathan Fontenot 308410bccf9SNathan Fontenot rc = rtas_call(activate_fw_token, 0, 1, NULL); 309410bccf9SNathan Fontenot if (!rc) { 310762ec157SNathan Fontenot rc = pseries_devicetree_update(MIGRATION_SCOPE); 311410bccf9SNathan Fontenot if (rc) 312410bccf9SNathan Fontenot printk(KERN_ERR "Secondary post-mobility device tree " 313410bccf9SNathan Fontenot "update failed: %d\n", rc); 314410bccf9SNathan Fontenot } else { 315410bccf9SNathan Fontenot printk(KERN_ERR "Post-mobility activate-fw failed: %d\n", rc); 316410bccf9SNathan Fontenot return; 317410bccf9SNathan Fontenot } 318410bccf9SNathan Fontenot 319410bccf9SNathan Fontenot return; 320410bccf9SNathan Fontenot } 321410bccf9SNathan Fontenot 322410bccf9SNathan Fontenot static ssize_t migrate_store(struct class *class, struct class_attribute *attr, 323410bccf9SNathan Fontenot const char *buf, size_t count) 324410bccf9SNathan Fontenot { 325410bccf9SNathan Fontenot struct rtas_args args; 326410bccf9SNathan Fontenot u64 streamid; 327410bccf9SNathan Fontenot int rc; 328410bccf9SNathan Fontenot 329410bccf9SNathan Fontenot rc = strict_strtoull(buf, 0, &streamid); 330410bccf9SNathan Fontenot if (rc) 331410bccf9SNathan Fontenot return rc; 332410bccf9SNathan Fontenot 333410bccf9SNathan Fontenot memset(&args, 0, sizeof(args)); 334410bccf9SNathan Fontenot args.token = rtas_token("ibm,suspend-me"); 335410bccf9SNathan Fontenot args.nargs = 2; 336410bccf9SNathan Fontenot args.nret = 1; 337410bccf9SNathan Fontenot 338410bccf9SNathan Fontenot args.args[0] = streamid >> 32 ; 339410bccf9SNathan Fontenot args.args[1] = streamid & 0xffffffff; 340410bccf9SNathan Fontenot args.rets = &args.args[args.nargs]; 341410bccf9SNathan Fontenot 342410bccf9SNathan Fontenot do { 343410bccf9SNathan Fontenot args.rets[0] = 0; 344410bccf9SNathan Fontenot rc = rtas_ibm_suspend_me(&args); 345410bccf9SNathan Fontenot if (!rc && args.rets[0] == RTAS_NOT_SUSPENDABLE) 346410bccf9SNathan Fontenot ssleep(1); 347410bccf9SNathan Fontenot } while (!rc && args.rets[0] == RTAS_NOT_SUSPENDABLE); 348410bccf9SNathan Fontenot 349410bccf9SNathan Fontenot if (rc) 350410bccf9SNathan Fontenot return rc; 351410bccf9SNathan Fontenot else if (args.rets[0]) 352410bccf9SNathan Fontenot return args.rets[0]; 353410bccf9SNathan Fontenot 354410bccf9SNathan Fontenot post_mobility_fixup(); 355410bccf9SNathan Fontenot return count; 356410bccf9SNathan Fontenot } 357410bccf9SNathan Fontenot 358410bccf9SNathan Fontenot static CLASS_ATTR(migration, S_IWUSR, NULL, migrate_store); 359410bccf9SNathan Fontenot 360410bccf9SNathan Fontenot static int __init mobility_sysfs_init(void) 361410bccf9SNathan Fontenot { 362410bccf9SNathan Fontenot int rc; 363410bccf9SNathan Fontenot 364410bccf9SNathan Fontenot mobility_kobj = kobject_create_and_add("mobility", kernel_kobj); 365410bccf9SNathan Fontenot if (!mobility_kobj) 366410bccf9SNathan Fontenot return -ENOMEM; 367410bccf9SNathan Fontenot 368410bccf9SNathan Fontenot rc = sysfs_create_file(mobility_kobj, &class_attr_migration.attr); 369410bccf9SNathan Fontenot 370410bccf9SNathan Fontenot return rc; 371410bccf9SNathan Fontenot } 372410bccf9SNathan Fontenot device_initcall(mobility_sysfs_init); 373