162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* ds.c: Domain Services driver for Logical Domains 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2007, 2008 David S. Miller <davem@davemloft.net> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/kernel.h> 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/types.h> 1062306a36Sopenharmony_ci#include <linux/string.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/sched.h> 1362306a36Sopenharmony_ci#include <linux/sched/clock.h> 1462306a36Sopenharmony_ci#include <linux/delay.h> 1562306a36Sopenharmony_ci#include <linux/mutex.h> 1662306a36Sopenharmony_ci#include <linux/kthread.h> 1762306a36Sopenharmony_ci#include <linux/reboot.h> 1862306a36Sopenharmony_ci#include <linux/cpu.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <asm/hypervisor.h> 2162306a36Sopenharmony_ci#include <asm/ldc.h> 2262306a36Sopenharmony_ci#include <asm/vio.h> 2362306a36Sopenharmony_ci#include <asm/mdesc.h> 2462306a36Sopenharmony_ci#include <asm/head.h> 2562306a36Sopenharmony_ci#include <asm/irq.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include "kernel.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define DRV_MODULE_NAME "ds" 3062306a36Sopenharmony_ci#define PFX DRV_MODULE_NAME ": " 3162306a36Sopenharmony_ci#define DRV_MODULE_VERSION "1.0" 3262306a36Sopenharmony_ci#define DRV_MODULE_RELDATE "Jul 11, 2007" 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic char version[] = 3562306a36Sopenharmony_ci DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; 3662306a36Sopenharmony_ciMODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); 3762306a36Sopenharmony_ciMODULE_DESCRIPTION("Sun LDOM domain services driver"); 3862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3962306a36Sopenharmony_ciMODULE_VERSION(DRV_MODULE_VERSION); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistruct ds_msg_tag { 4262306a36Sopenharmony_ci __u32 type; 4362306a36Sopenharmony_ci#define DS_INIT_REQ 0x00 4462306a36Sopenharmony_ci#define DS_INIT_ACK 0x01 4562306a36Sopenharmony_ci#define DS_INIT_NACK 0x02 4662306a36Sopenharmony_ci#define DS_REG_REQ 0x03 4762306a36Sopenharmony_ci#define DS_REG_ACK 0x04 4862306a36Sopenharmony_ci#define DS_REG_NACK 0x05 4962306a36Sopenharmony_ci#define DS_UNREG_REQ 0x06 5062306a36Sopenharmony_ci#define DS_UNREG_ACK 0x07 5162306a36Sopenharmony_ci#define DS_UNREG_NACK 0x08 5262306a36Sopenharmony_ci#define DS_DATA 0x09 5362306a36Sopenharmony_ci#define DS_NACK 0x0a 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci __u32 len; 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* Result codes */ 5962306a36Sopenharmony_ci#define DS_OK 0x00 6062306a36Sopenharmony_ci#define DS_REG_VER_NACK 0x01 6162306a36Sopenharmony_ci#define DS_REG_DUP 0x02 6262306a36Sopenharmony_ci#define DS_INV_HDL 0x03 6362306a36Sopenharmony_ci#define DS_TYPE_UNKNOWN 0x04 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistruct ds_version { 6662306a36Sopenharmony_ci __u16 major; 6762306a36Sopenharmony_ci __u16 minor; 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistruct ds_ver_req { 7162306a36Sopenharmony_ci struct ds_msg_tag tag; 7262306a36Sopenharmony_ci struct ds_version ver; 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistruct ds_ver_ack { 7662306a36Sopenharmony_ci struct ds_msg_tag tag; 7762306a36Sopenharmony_ci __u16 minor; 7862306a36Sopenharmony_ci}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistruct ds_ver_nack { 8162306a36Sopenharmony_ci struct ds_msg_tag tag; 8262306a36Sopenharmony_ci __u16 major; 8362306a36Sopenharmony_ci}; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistruct ds_reg_req { 8662306a36Sopenharmony_ci struct ds_msg_tag tag; 8762306a36Sopenharmony_ci __u64 handle; 8862306a36Sopenharmony_ci __u16 major; 8962306a36Sopenharmony_ci __u16 minor; 9062306a36Sopenharmony_ci char svc_id[]; 9162306a36Sopenharmony_ci}; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistruct ds_reg_ack { 9462306a36Sopenharmony_ci struct ds_msg_tag tag; 9562306a36Sopenharmony_ci __u64 handle; 9662306a36Sopenharmony_ci __u16 minor; 9762306a36Sopenharmony_ci}; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistruct ds_reg_nack { 10062306a36Sopenharmony_ci struct ds_msg_tag tag; 10162306a36Sopenharmony_ci __u64 handle; 10262306a36Sopenharmony_ci __u16 major; 10362306a36Sopenharmony_ci}; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistruct ds_unreg_req { 10662306a36Sopenharmony_ci struct ds_msg_tag tag; 10762306a36Sopenharmony_ci __u64 handle; 10862306a36Sopenharmony_ci}; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistruct ds_unreg_ack { 11162306a36Sopenharmony_ci struct ds_msg_tag tag; 11262306a36Sopenharmony_ci __u64 handle; 11362306a36Sopenharmony_ci}; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistruct ds_unreg_nack { 11662306a36Sopenharmony_ci struct ds_msg_tag tag; 11762306a36Sopenharmony_ci __u64 handle; 11862306a36Sopenharmony_ci}; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistruct ds_data { 12162306a36Sopenharmony_ci struct ds_msg_tag tag; 12262306a36Sopenharmony_ci __u64 handle; 12362306a36Sopenharmony_ci}; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistruct ds_data_nack { 12662306a36Sopenharmony_ci struct ds_msg_tag tag; 12762306a36Sopenharmony_ci __u64 handle; 12862306a36Sopenharmony_ci __u64 result; 12962306a36Sopenharmony_ci}; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistruct ds_info; 13262306a36Sopenharmony_cistruct ds_cap_state { 13362306a36Sopenharmony_ci __u64 handle; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci void (*data)(struct ds_info *dp, 13662306a36Sopenharmony_ci struct ds_cap_state *cp, 13762306a36Sopenharmony_ci void *buf, int len); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci const char *service_id; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci u8 state; 14262306a36Sopenharmony_ci#define CAP_STATE_UNKNOWN 0x00 14362306a36Sopenharmony_ci#define CAP_STATE_REG_SENT 0x01 14462306a36Sopenharmony_ci#define CAP_STATE_REGISTERED 0x02 14562306a36Sopenharmony_ci}; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic void md_update_data(struct ds_info *dp, struct ds_cap_state *cp, 14862306a36Sopenharmony_ci void *buf, int len); 14962306a36Sopenharmony_cistatic void domain_shutdown_data(struct ds_info *dp, 15062306a36Sopenharmony_ci struct ds_cap_state *cp, 15162306a36Sopenharmony_ci void *buf, int len); 15262306a36Sopenharmony_cistatic void domain_panic_data(struct ds_info *dp, 15362306a36Sopenharmony_ci struct ds_cap_state *cp, 15462306a36Sopenharmony_ci void *buf, int len); 15562306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 15662306a36Sopenharmony_cistatic void dr_cpu_data(struct ds_info *dp, 15762306a36Sopenharmony_ci struct ds_cap_state *cp, 15862306a36Sopenharmony_ci void *buf, int len); 15962306a36Sopenharmony_ci#endif 16062306a36Sopenharmony_cistatic void ds_pri_data(struct ds_info *dp, 16162306a36Sopenharmony_ci struct ds_cap_state *cp, 16262306a36Sopenharmony_ci void *buf, int len); 16362306a36Sopenharmony_cistatic void ds_var_data(struct ds_info *dp, 16462306a36Sopenharmony_ci struct ds_cap_state *cp, 16562306a36Sopenharmony_ci void *buf, int len); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic struct ds_cap_state ds_states_template[] = { 16862306a36Sopenharmony_ci { 16962306a36Sopenharmony_ci .service_id = "md-update", 17062306a36Sopenharmony_ci .data = md_update_data, 17162306a36Sopenharmony_ci }, 17262306a36Sopenharmony_ci { 17362306a36Sopenharmony_ci .service_id = "domain-shutdown", 17462306a36Sopenharmony_ci .data = domain_shutdown_data, 17562306a36Sopenharmony_ci }, 17662306a36Sopenharmony_ci { 17762306a36Sopenharmony_ci .service_id = "domain-panic", 17862306a36Sopenharmony_ci .data = domain_panic_data, 17962306a36Sopenharmony_ci }, 18062306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 18162306a36Sopenharmony_ci { 18262306a36Sopenharmony_ci .service_id = "dr-cpu", 18362306a36Sopenharmony_ci .data = dr_cpu_data, 18462306a36Sopenharmony_ci }, 18562306a36Sopenharmony_ci#endif 18662306a36Sopenharmony_ci { 18762306a36Sopenharmony_ci .service_id = "pri", 18862306a36Sopenharmony_ci .data = ds_pri_data, 18962306a36Sopenharmony_ci }, 19062306a36Sopenharmony_ci { 19162306a36Sopenharmony_ci .service_id = "var-config", 19262306a36Sopenharmony_ci .data = ds_var_data, 19362306a36Sopenharmony_ci }, 19462306a36Sopenharmony_ci { 19562306a36Sopenharmony_ci .service_id = "var-config-backup", 19662306a36Sopenharmony_ci .data = ds_var_data, 19762306a36Sopenharmony_ci }, 19862306a36Sopenharmony_ci}; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic DEFINE_SPINLOCK(ds_lock); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistruct ds_info { 20362306a36Sopenharmony_ci struct ldc_channel *lp; 20462306a36Sopenharmony_ci u8 hs_state; 20562306a36Sopenharmony_ci#define DS_HS_START 0x01 20662306a36Sopenharmony_ci#define DS_HS_DONE 0x02 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci u64 id; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci void *rcv_buf; 21162306a36Sopenharmony_ci int rcv_buf_len; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci struct ds_cap_state *ds_states; 21462306a36Sopenharmony_ci int num_ds_states; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci struct ds_info *next; 21762306a36Sopenharmony_ci}; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic struct ds_info *ds_info_list; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic struct ds_cap_state *find_cap(struct ds_info *dp, u64 handle) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci unsigned int index = handle >> 32; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (index >= dp->num_ds_states) 22662306a36Sopenharmony_ci return NULL; 22762306a36Sopenharmony_ci return &dp->ds_states[index]; 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic struct ds_cap_state *find_cap_by_string(struct ds_info *dp, 23162306a36Sopenharmony_ci const char *name) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci int i; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci for (i = 0; i < dp->num_ds_states; i++) { 23662306a36Sopenharmony_ci if (strcmp(dp->ds_states[i].service_id, name)) 23762306a36Sopenharmony_ci continue; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci return &dp->ds_states[i]; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci return NULL; 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic int __ds_send(struct ldc_channel *lp, void *data, int len) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci int err, limit = 1000; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci err = -EINVAL; 24962306a36Sopenharmony_ci while (limit-- > 0) { 25062306a36Sopenharmony_ci err = ldc_write(lp, data, len); 25162306a36Sopenharmony_ci if (!err || (err != -EAGAIN)) 25262306a36Sopenharmony_ci break; 25362306a36Sopenharmony_ci udelay(1); 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci return err; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic int ds_send(struct ldc_channel *lp, void *data, int len) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci unsigned long flags; 26262306a36Sopenharmony_ci int err; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci spin_lock_irqsave(&ds_lock, flags); 26562306a36Sopenharmony_ci err = __ds_send(lp, data, len); 26662306a36Sopenharmony_ci spin_unlock_irqrestore(&ds_lock, flags); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci return err; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistruct ds_md_update_req { 27262306a36Sopenharmony_ci __u64 req_num; 27362306a36Sopenharmony_ci}; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistruct ds_md_update_res { 27662306a36Sopenharmony_ci __u64 req_num; 27762306a36Sopenharmony_ci __u32 result; 27862306a36Sopenharmony_ci}; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic void md_update_data(struct ds_info *dp, 28162306a36Sopenharmony_ci struct ds_cap_state *cp, 28262306a36Sopenharmony_ci void *buf, int len) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci struct ldc_channel *lp = dp->lp; 28562306a36Sopenharmony_ci struct ds_data *dpkt = buf; 28662306a36Sopenharmony_ci struct ds_md_update_req *rp; 28762306a36Sopenharmony_ci struct { 28862306a36Sopenharmony_ci struct ds_data data; 28962306a36Sopenharmony_ci struct ds_md_update_res res; 29062306a36Sopenharmony_ci } pkt; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci rp = (struct ds_md_update_req *) (dpkt + 1); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci printk(KERN_INFO "ds-%llu: Machine description update.\n", dp->id); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci mdesc_update(); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci memset(&pkt, 0, sizeof(pkt)); 29962306a36Sopenharmony_ci pkt.data.tag.type = DS_DATA; 30062306a36Sopenharmony_ci pkt.data.tag.len = sizeof(pkt) - sizeof(struct ds_msg_tag); 30162306a36Sopenharmony_ci pkt.data.handle = cp->handle; 30262306a36Sopenharmony_ci pkt.res.req_num = rp->req_num; 30362306a36Sopenharmony_ci pkt.res.result = DS_OK; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci ds_send(lp, &pkt, sizeof(pkt)); 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistruct ds_shutdown_req { 30962306a36Sopenharmony_ci __u64 req_num; 31062306a36Sopenharmony_ci __u32 ms_delay; 31162306a36Sopenharmony_ci}; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistruct ds_shutdown_res { 31462306a36Sopenharmony_ci __u64 req_num; 31562306a36Sopenharmony_ci __u32 result; 31662306a36Sopenharmony_ci char reason[1]; 31762306a36Sopenharmony_ci}; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic void domain_shutdown_data(struct ds_info *dp, 32062306a36Sopenharmony_ci struct ds_cap_state *cp, 32162306a36Sopenharmony_ci void *buf, int len) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci struct ldc_channel *lp = dp->lp; 32462306a36Sopenharmony_ci struct ds_data *dpkt = buf; 32562306a36Sopenharmony_ci struct ds_shutdown_req *rp; 32662306a36Sopenharmony_ci struct { 32762306a36Sopenharmony_ci struct ds_data data; 32862306a36Sopenharmony_ci struct ds_shutdown_res res; 32962306a36Sopenharmony_ci } pkt; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci rp = (struct ds_shutdown_req *) (dpkt + 1); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci printk(KERN_ALERT "ds-%llu: Shutdown request from " 33462306a36Sopenharmony_ci "LDOM manager received.\n", dp->id); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci memset(&pkt, 0, sizeof(pkt)); 33762306a36Sopenharmony_ci pkt.data.tag.type = DS_DATA; 33862306a36Sopenharmony_ci pkt.data.tag.len = sizeof(pkt) - sizeof(struct ds_msg_tag); 33962306a36Sopenharmony_ci pkt.data.handle = cp->handle; 34062306a36Sopenharmony_ci pkt.res.req_num = rp->req_num; 34162306a36Sopenharmony_ci pkt.res.result = DS_OK; 34262306a36Sopenharmony_ci pkt.res.reason[0] = 0; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci ds_send(lp, &pkt, sizeof(pkt)); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci orderly_poweroff(true); 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistruct ds_panic_req { 35062306a36Sopenharmony_ci __u64 req_num; 35162306a36Sopenharmony_ci}; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cistruct ds_panic_res { 35462306a36Sopenharmony_ci __u64 req_num; 35562306a36Sopenharmony_ci __u32 result; 35662306a36Sopenharmony_ci char reason[1]; 35762306a36Sopenharmony_ci}; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic void domain_panic_data(struct ds_info *dp, 36062306a36Sopenharmony_ci struct ds_cap_state *cp, 36162306a36Sopenharmony_ci void *buf, int len) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci struct ldc_channel *lp = dp->lp; 36462306a36Sopenharmony_ci struct ds_data *dpkt = buf; 36562306a36Sopenharmony_ci struct ds_panic_req *rp; 36662306a36Sopenharmony_ci struct { 36762306a36Sopenharmony_ci struct ds_data data; 36862306a36Sopenharmony_ci struct ds_panic_res res; 36962306a36Sopenharmony_ci } pkt; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci rp = (struct ds_panic_req *) (dpkt + 1); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci printk(KERN_ALERT "ds-%llu: Panic request from " 37462306a36Sopenharmony_ci "LDOM manager received.\n", dp->id); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci memset(&pkt, 0, sizeof(pkt)); 37762306a36Sopenharmony_ci pkt.data.tag.type = DS_DATA; 37862306a36Sopenharmony_ci pkt.data.tag.len = sizeof(pkt) - sizeof(struct ds_msg_tag); 37962306a36Sopenharmony_ci pkt.data.handle = cp->handle; 38062306a36Sopenharmony_ci pkt.res.req_num = rp->req_num; 38162306a36Sopenharmony_ci pkt.res.result = DS_OK; 38262306a36Sopenharmony_ci pkt.res.reason[0] = 0; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci ds_send(lp, &pkt, sizeof(pkt)); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci panic("PANIC requested by LDOM manager."); 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 39062306a36Sopenharmony_cistruct dr_cpu_tag { 39162306a36Sopenharmony_ci __u64 req_num; 39262306a36Sopenharmony_ci __u32 type; 39362306a36Sopenharmony_ci#define DR_CPU_CONFIGURE 0x43 39462306a36Sopenharmony_ci#define DR_CPU_UNCONFIGURE 0x55 39562306a36Sopenharmony_ci#define DR_CPU_FORCE_UNCONFIGURE 0x46 39662306a36Sopenharmony_ci#define DR_CPU_STATUS 0x53 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci/* Responses */ 39962306a36Sopenharmony_ci#define DR_CPU_OK 0x6f 40062306a36Sopenharmony_ci#define DR_CPU_ERROR 0x65 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci __u32 num_records; 40362306a36Sopenharmony_ci}; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistruct dr_cpu_resp_entry { 40662306a36Sopenharmony_ci __u32 cpu; 40762306a36Sopenharmony_ci __u32 result; 40862306a36Sopenharmony_ci#define DR_CPU_RES_OK 0x00 40962306a36Sopenharmony_ci#define DR_CPU_RES_FAILURE 0x01 41062306a36Sopenharmony_ci#define DR_CPU_RES_BLOCKED 0x02 41162306a36Sopenharmony_ci#define DR_CPU_RES_CPU_NOT_RESPONDING 0x03 41262306a36Sopenharmony_ci#define DR_CPU_RES_NOT_IN_MD 0x04 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci __u32 stat; 41562306a36Sopenharmony_ci#define DR_CPU_STAT_NOT_PRESENT 0x00 41662306a36Sopenharmony_ci#define DR_CPU_STAT_UNCONFIGURED 0x01 41762306a36Sopenharmony_ci#define DR_CPU_STAT_CONFIGURED 0x02 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci __u32 str_off; 42062306a36Sopenharmony_ci}; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cistatic void __dr_cpu_send_error(struct ds_info *dp, 42362306a36Sopenharmony_ci struct ds_cap_state *cp, 42462306a36Sopenharmony_ci struct ds_data *data) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci struct dr_cpu_tag *tag = (struct dr_cpu_tag *) (data + 1); 42762306a36Sopenharmony_ci struct { 42862306a36Sopenharmony_ci struct ds_data data; 42962306a36Sopenharmony_ci struct dr_cpu_tag tag; 43062306a36Sopenharmony_ci } pkt; 43162306a36Sopenharmony_ci int msg_len; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci memset(&pkt, 0, sizeof(pkt)); 43462306a36Sopenharmony_ci pkt.data.tag.type = DS_DATA; 43562306a36Sopenharmony_ci pkt.data.handle = cp->handle; 43662306a36Sopenharmony_ci pkt.tag.req_num = tag->req_num; 43762306a36Sopenharmony_ci pkt.tag.type = DR_CPU_ERROR; 43862306a36Sopenharmony_ci pkt.tag.num_records = 0; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci msg_len = (sizeof(struct ds_data) + 44162306a36Sopenharmony_ci sizeof(struct dr_cpu_tag)); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci pkt.data.tag.len = msg_len - sizeof(struct ds_msg_tag); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci __ds_send(dp->lp, &pkt, msg_len); 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic void dr_cpu_send_error(struct ds_info *dp, 44962306a36Sopenharmony_ci struct ds_cap_state *cp, 45062306a36Sopenharmony_ci struct ds_data *data) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci unsigned long flags; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci spin_lock_irqsave(&ds_lock, flags); 45562306a36Sopenharmony_ci __dr_cpu_send_error(dp, cp, data); 45662306a36Sopenharmony_ci spin_unlock_irqrestore(&ds_lock, flags); 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci#define CPU_SENTINEL 0xffffffff 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cistatic void purge_dups(u32 *list, u32 num_ents) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci unsigned int i; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci for (i = 0; i < num_ents; i++) { 46662306a36Sopenharmony_ci u32 cpu = list[i]; 46762306a36Sopenharmony_ci unsigned int j; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci if (cpu == CPU_SENTINEL) 47062306a36Sopenharmony_ci continue; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci for (j = i + 1; j < num_ents; j++) { 47362306a36Sopenharmony_ci if (list[j] == cpu) 47462306a36Sopenharmony_ci list[j] = CPU_SENTINEL; 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic int dr_cpu_size_response(int ncpus) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci return (sizeof(struct ds_data) + 48262306a36Sopenharmony_ci sizeof(struct dr_cpu_tag) + 48362306a36Sopenharmony_ci (sizeof(struct dr_cpu_resp_entry) * ncpus)); 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_cistatic void dr_cpu_init_response(struct ds_data *resp, u64 req_num, 48762306a36Sopenharmony_ci u64 handle, int resp_len, int ncpus, 48862306a36Sopenharmony_ci cpumask_t *mask, u32 default_stat) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci struct dr_cpu_resp_entry *ent; 49162306a36Sopenharmony_ci struct dr_cpu_tag *tag; 49262306a36Sopenharmony_ci int i, cpu; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci tag = (struct dr_cpu_tag *) (resp + 1); 49562306a36Sopenharmony_ci ent = (struct dr_cpu_resp_entry *) (tag + 1); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci resp->tag.type = DS_DATA; 49862306a36Sopenharmony_ci resp->tag.len = resp_len - sizeof(struct ds_msg_tag); 49962306a36Sopenharmony_ci resp->handle = handle; 50062306a36Sopenharmony_ci tag->req_num = req_num; 50162306a36Sopenharmony_ci tag->type = DR_CPU_OK; 50262306a36Sopenharmony_ci tag->num_records = ncpus; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci i = 0; 50562306a36Sopenharmony_ci for_each_cpu(cpu, mask) { 50662306a36Sopenharmony_ci ent[i].cpu = cpu; 50762306a36Sopenharmony_ci ent[i].result = DR_CPU_RES_OK; 50862306a36Sopenharmony_ci ent[i].stat = default_stat; 50962306a36Sopenharmony_ci i++; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci BUG_ON(i != ncpus); 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic void dr_cpu_mark(struct ds_data *resp, int cpu, int ncpus, 51562306a36Sopenharmony_ci u32 res, u32 stat) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci struct dr_cpu_resp_entry *ent; 51862306a36Sopenharmony_ci struct dr_cpu_tag *tag; 51962306a36Sopenharmony_ci int i; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci tag = (struct dr_cpu_tag *) (resp + 1); 52262306a36Sopenharmony_ci ent = (struct dr_cpu_resp_entry *) (tag + 1); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci for (i = 0; i < ncpus; i++) { 52562306a36Sopenharmony_ci if (ent[i].cpu != cpu) 52662306a36Sopenharmony_ci continue; 52762306a36Sopenharmony_ci ent[i].result = res; 52862306a36Sopenharmony_ci ent[i].stat = stat; 52962306a36Sopenharmony_ci break; 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic int dr_cpu_configure(struct ds_info *dp, struct ds_cap_state *cp, 53462306a36Sopenharmony_ci u64 req_num, cpumask_t *mask) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci struct ds_data *resp; 53762306a36Sopenharmony_ci int resp_len, ncpus, cpu; 53862306a36Sopenharmony_ci unsigned long flags; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci ncpus = cpumask_weight(mask); 54162306a36Sopenharmony_ci resp_len = dr_cpu_size_response(ncpus); 54262306a36Sopenharmony_ci resp = kzalloc(resp_len, GFP_KERNEL); 54362306a36Sopenharmony_ci if (!resp) 54462306a36Sopenharmony_ci return -ENOMEM; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci dr_cpu_init_response(resp, req_num, cp->handle, 54762306a36Sopenharmony_ci resp_len, ncpus, mask, 54862306a36Sopenharmony_ci DR_CPU_STAT_CONFIGURED); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci mdesc_populate_present_mask(mask); 55162306a36Sopenharmony_ci mdesc_fill_in_cpu_data(mask); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci for_each_cpu(cpu, mask) { 55462306a36Sopenharmony_ci int err; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci printk(KERN_INFO "ds-%llu: Starting cpu %d...\n", 55762306a36Sopenharmony_ci dp->id, cpu); 55862306a36Sopenharmony_ci err = add_cpu(cpu); 55962306a36Sopenharmony_ci if (err) { 56062306a36Sopenharmony_ci __u32 res = DR_CPU_RES_FAILURE; 56162306a36Sopenharmony_ci __u32 stat = DR_CPU_STAT_UNCONFIGURED; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci if (!cpu_present(cpu)) { 56462306a36Sopenharmony_ci /* CPU not present in MD */ 56562306a36Sopenharmony_ci res = DR_CPU_RES_NOT_IN_MD; 56662306a36Sopenharmony_ci stat = DR_CPU_STAT_NOT_PRESENT; 56762306a36Sopenharmony_ci } else if (err == -ENODEV) { 56862306a36Sopenharmony_ci /* CPU did not call in successfully */ 56962306a36Sopenharmony_ci res = DR_CPU_RES_CPU_NOT_RESPONDING; 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci printk(KERN_INFO "ds-%llu: CPU startup failed err=%d\n", 57362306a36Sopenharmony_ci dp->id, err); 57462306a36Sopenharmony_ci dr_cpu_mark(resp, cpu, ncpus, res, stat); 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci spin_lock_irqsave(&ds_lock, flags); 57962306a36Sopenharmony_ci __ds_send(dp->lp, resp, resp_len); 58062306a36Sopenharmony_ci spin_unlock_irqrestore(&ds_lock, flags); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci kfree(resp); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci /* Redistribute IRQs, taking into account the new cpus. */ 58562306a36Sopenharmony_ci fixup_irqs(); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci return 0; 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cistatic int dr_cpu_unconfigure(struct ds_info *dp, 59162306a36Sopenharmony_ci struct ds_cap_state *cp, 59262306a36Sopenharmony_ci u64 req_num, 59362306a36Sopenharmony_ci cpumask_t *mask) 59462306a36Sopenharmony_ci{ 59562306a36Sopenharmony_ci struct ds_data *resp; 59662306a36Sopenharmony_ci int resp_len, ncpus, cpu; 59762306a36Sopenharmony_ci unsigned long flags; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci ncpus = cpumask_weight(mask); 60062306a36Sopenharmony_ci resp_len = dr_cpu_size_response(ncpus); 60162306a36Sopenharmony_ci resp = kzalloc(resp_len, GFP_KERNEL); 60262306a36Sopenharmony_ci if (!resp) 60362306a36Sopenharmony_ci return -ENOMEM; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci dr_cpu_init_response(resp, req_num, cp->handle, 60662306a36Sopenharmony_ci resp_len, ncpus, mask, 60762306a36Sopenharmony_ci DR_CPU_STAT_UNCONFIGURED); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci for_each_cpu(cpu, mask) { 61062306a36Sopenharmony_ci int err; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci printk(KERN_INFO "ds-%llu: Shutting down cpu %d...\n", 61362306a36Sopenharmony_ci dp->id, cpu); 61462306a36Sopenharmony_ci err = remove_cpu(cpu); 61562306a36Sopenharmony_ci if (err) 61662306a36Sopenharmony_ci dr_cpu_mark(resp, cpu, ncpus, 61762306a36Sopenharmony_ci DR_CPU_RES_FAILURE, 61862306a36Sopenharmony_ci DR_CPU_STAT_CONFIGURED); 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci spin_lock_irqsave(&ds_lock, flags); 62262306a36Sopenharmony_ci __ds_send(dp->lp, resp, resp_len); 62362306a36Sopenharmony_ci spin_unlock_irqrestore(&ds_lock, flags); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci kfree(resp); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci return 0; 62862306a36Sopenharmony_ci} 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_cistatic void dr_cpu_data(struct ds_info *dp, struct ds_cap_state *cp, void *buf, 63162306a36Sopenharmony_ci int len) 63262306a36Sopenharmony_ci{ 63362306a36Sopenharmony_ci struct ds_data *data = buf; 63462306a36Sopenharmony_ci struct dr_cpu_tag *tag = (struct dr_cpu_tag *) (data + 1); 63562306a36Sopenharmony_ci u32 *cpu_list = (u32 *) (tag + 1); 63662306a36Sopenharmony_ci u64 req_num = tag->req_num; 63762306a36Sopenharmony_ci cpumask_t mask; 63862306a36Sopenharmony_ci unsigned int i; 63962306a36Sopenharmony_ci int err; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci switch (tag->type) { 64262306a36Sopenharmony_ci case DR_CPU_CONFIGURE: 64362306a36Sopenharmony_ci case DR_CPU_UNCONFIGURE: 64462306a36Sopenharmony_ci case DR_CPU_FORCE_UNCONFIGURE: 64562306a36Sopenharmony_ci break; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci default: 64862306a36Sopenharmony_ci dr_cpu_send_error(dp, cp, data); 64962306a36Sopenharmony_ci return; 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci purge_dups(cpu_list, tag->num_records); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci cpumask_clear(&mask); 65562306a36Sopenharmony_ci for (i = 0; i < tag->num_records; i++) { 65662306a36Sopenharmony_ci if (cpu_list[i] == CPU_SENTINEL) 65762306a36Sopenharmony_ci continue; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci if (cpu_list[i] < nr_cpu_ids) 66062306a36Sopenharmony_ci cpumask_set_cpu(cpu_list[i], &mask); 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if (tag->type == DR_CPU_CONFIGURE) 66462306a36Sopenharmony_ci err = dr_cpu_configure(dp, cp, req_num, &mask); 66562306a36Sopenharmony_ci else 66662306a36Sopenharmony_ci err = dr_cpu_unconfigure(dp, cp, req_num, &mask); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci if (err) 66962306a36Sopenharmony_ci dr_cpu_send_error(dp, cp, data); 67062306a36Sopenharmony_ci} 67162306a36Sopenharmony_ci#endif /* CONFIG_HOTPLUG_CPU */ 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_cistruct ds_pri_msg { 67462306a36Sopenharmony_ci __u64 req_num; 67562306a36Sopenharmony_ci __u64 type; 67662306a36Sopenharmony_ci#define DS_PRI_REQUEST 0x00 67762306a36Sopenharmony_ci#define DS_PRI_DATA 0x01 67862306a36Sopenharmony_ci#define DS_PRI_UPDATE 0x02 67962306a36Sopenharmony_ci}; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_cistatic void ds_pri_data(struct ds_info *dp, 68262306a36Sopenharmony_ci struct ds_cap_state *cp, 68362306a36Sopenharmony_ci void *buf, int len) 68462306a36Sopenharmony_ci{ 68562306a36Sopenharmony_ci struct ds_data *dpkt = buf; 68662306a36Sopenharmony_ci struct ds_pri_msg *rp; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci rp = (struct ds_pri_msg *) (dpkt + 1); 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci printk(KERN_INFO "ds-%llu: PRI REQ [%llx:%llx], len=%d\n", 69162306a36Sopenharmony_ci dp->id, rp->req_num, rp->type, len); 69262306a36Sopenharmony_ci} 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_cistruct ds_var_hdr { 69562306a36Sopenharmony_ci __u32 type; 69662306a36Sopenharmony_ci#define DS_VAR_SET_REQ 0x00 69762306a36Sopenharmony_ci#define DS_VAR_DELETE_REQ 0x01 69862306a36Sopenharmony_ci#define DS_VAR_SET_RESP 0x02 69962306a36Sopenharmony_ci#define DS_VAR_DELETE_RESP 0x03 70062306a36Sopenharmony_ci}; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_cistruct ds_var_set_msg { 70362306a36Sopenharmony_ci struct ds_var_hdr hdr; 70462306a36Sopenharmony_ci char name_and_value[]; 70562306a36Sopenharmony_ci}; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_cistruct ds_var_delete_msg { 70862306a36Sopenharmony_ci struct ds_var_hdr hdr; 70962306a36Sopenharmony_ci char name[]; 71062306a36Sopenharmony_ci}; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_cistruct ds_var_resp { 71362306a36Sopenharmony_ci struct ds_var_hdr hdr; 71462306a36Sopenharmony_ci __u32 result; 71562306a36Sopenharmony_ci#define DS_VAR_SUCCESS 0x00 71662306a36Sopenharmony_ci#define DS_VAR_NO_SPACE 0x01 71762306a36Sopenharmony_ci#define DS_VAR_INVALID_VAR 0x02 71862306a36Sopenharmony_ci#define DS_VAR_INVALID_VAL 0x03 71962306a36Sopenharmony_ci#define DS_VAR_NOT_PRESENT 0x04 72062306a36Sopenharmony_ci}; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_cistatic DEFINE_MUTEX(ds_var_mutex); 72362306a36Sopenharmony_cistatic int ds_var_doorbell; 72462306a36Sopenharmony_cistatic int ds_var_response; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_cistatic void ds_var_data(struct ds_info *dp, 72762306a36Sopenharmony_ci struct ds_cap_state *cp, 72862306a36Sopenharmony_ci void *buf, int len) 72962306a36Sopenharmony_ci{ 73062306a36Sopenharmony_ci struct ds_data *dpkt = buf; 73162306a36Sopenharmony_ci struct ds_var_resp *rp; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci rp = (struct ds_var_resp *) (dpkt + 1); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci if (rp->hdr.type != DS_VAR_SET_RESP && 73662306a36Sopenharmony_ci rp->hdr.type != DS_VAR_DELETE_RESP) 73762306a36Sopenharmony_ci return; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci ds_var_response = rp->result; 74062306a36Sopenharmony_ci wmb(); 74162306a36Sopenharmony_ci ds_var_doorbell = 1; 74262306a36Sopenharmony_ci} 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_civoid ldom_set_var(const char *var, const char *value) 74562306a36Sopenharmony_ci{ 74662306a36Sopenharmony_ci struct ds_cap_state *cp; 74762306a36Sopenharmony_ci struct ds_info *dp; 74862306a36Sopenharmony_ci unsigned long flags; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci spin_lock_irqsave(&ds_lock, flags); 75162306a36Sopenharmony_ci cp = NULL; 75262306a36Sopenharmony_ci for (dp = ds_info_list; dp; dp = dp->next) { 75362306a36Sopenharmony_ci struct ds_cap_state *tmp; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci tmp = find_cap_by_string(dp, "var-config"); 75662306a36Sopenharmony_ci if (tmp && tmp->state == CAP_STATE_REGISTERED) { 75762306a36Sopenharmony_ci cp = tmp; 75862306a36Sopenharmony_ci break; 75962306a36Sopenharmony_ci } 76062306a36Sopenharmony_ci } 76162306a36Sopenharmony_ci if (!cp) { 76262306a36Sopenharmony_ci for (dp = ds_info_list; dp; dp = dp->next) { 76362306a36Sopenharmony_ci struct ds_cap_state *tmp; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci tmp = find_cap_by_string(dp, "var-config-backup"); 76662306a36Sopenharmony_ci if (tmp && tmp->state == CAP_STATE_REGISTERED) { 76762306a36Sopenharmony_ci cp = tmp; 76862306a36Sopenharmony_ci break; 76962306a36Sopenharmony_ci } 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci } 77262306a36Sopenharmony_ci spin_unlock_irqrestore(&ds_lock, flags); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci if (cp) { 77562306a36Sopenharmony_ci union { 77662306a36Sopenharmony_ci struct { 77762306a36Sopenharmony_ci struct ds_data data; 77862306a36Sopenharmony_ci struct ds_var_set_msg msg; 77962306a36Sopenharmony_ci } header; 78062306a36Sopenharmony_ci char all[512]; 78162306a36Sopenharmony_ci } pkt; 78262306a36Sopenharmony_ci char *base, *p; 78362306a36Sopenharmony_ci int msg_len, loops; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci if (strlen(var) + strlen(value) + 2 > 78662306a36Sopenharmony_ci sizeof(pkt) - sizeof(pkt.header)) { 78762306a36Sopenharmony_ci printk(KERN_ERR PFX 78862306a36Sopenharmony_ci "contents length: %zu, which more than max: %lu," 78962306a36Sopenharmony_ci "so could not set (%s) variable to (%s).\n", 79062306a36Sopenharmony_ci strlen(var) + strlen(value) + 2, 79162306a36Sopenharmony_ci sizeof(pkt) - sizeof(pkt.header), var, value); 79262306a36Sopenharmony_ci return; 79362306a36Sopenharmony_ci } 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci memset(&pkt, 0, sizeof(pkt)); 79662306a36Sopenharmony_ci pkt.header.data.tag.type = DS_DATA; 79762306a36Sopenharmony_ci pkt.header.data.handle = cp->handle; 79862306a36Sopenharmony_ci pkt.header.msg.hdr.type = DS_VAR_SET_REQ; 79962306a36Sopenharmony_ci base = p = &pkt.header.msg.name_and_value[0]; 80062306a36Sopenharmony_ci strcpy(p, var); 80162306a36Sopenharmony_ci p += strlen(var) + 1; 80262306a36Sopenharmony_ci strcpy(p, value); 80362306a36Sopenharmony_ci p += strlen(value) + 1; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci msg_len = (sizeof(struct ds_data) + 80662306a36Sopenharmony_ci sizeof(struct ds_var_set_msg) + 80762306a36Sopenharmony_ci (p - base)); 80862306a36Sopenharmony_ci msg_len = (msg_len + 3) & ~3; 80962306a36Sopenharmony_ci pkt.header.data.tag.len = msg_len - sizeof(struct ds_msg_tag); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci mutex_lock(&ds_var_mutex); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci spin_lock_irqsave(&ds_lock, flags); 81462306a36Sopenharmony_ci ds_var_doorbell = 0; 81562306a36Sopenharmony_ci ds_var_response = -1; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci __ds_send(dp->lp, &pkt, msg_len); 81862306a36Sopenharmony_ci spin_unlock_irqrestore(&ds_lock, flags); 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci loops = 1000; 82162306a36Sopenharmony_ci while (ds_var_doorbell == 0) { 82262306a36Sopenharmony_ci if (loops-- < 0) 82362306a36Sopenharmony_ci break; 82462306a36Sopenharmony_ci barrier(); 82562306a36Sopenharmony_ci udelay(100); 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci mutex_unlock(&ds_var_mutex); 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci if (ds_var_doorbell == 0 || 83162306a36Sopenharmony_ci ds_var_response != DS_VAR_SUCCESS) 83262306a36Sopenharmony_ci printk(KERN_ERR "ds-%llu: var-config [%s:%s] " 83362306a36Sopenharmony_ci "failed, response(%d).\n", 83462306a36Sopenharmony_ci dp->id, var, value, 83562306a36Sopenharmony_ci ds_var_response); 83662306a36Sopenharmony_ci } else { 83762306a36Sopenharmony_ci printk(KERN_ERR PFX "var-config not registered so " 83862306a36Sopenharmony_ci "could not set (%s) variable to (%s).\n", 83962306a36Sopenharmony_ci var, value); 84062306a36Sopenharmony_ci } 84162306a36Sopenharmony_ci} 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_cistatic char full_boot_str[256] __attribute__((aligned(32))); 84462306a36Sopenharmony_cistatic int reboot_data_supported; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_civoid ldom_reboot(const char *boot_command) 84762306a36Sopenharmony_ci{ 84862306a36Sopenharmony_ci /* Don't bother with any of this if the boot_command 84962306a36Sopenharmony_ci * is empty. 85062306a36Sopenharmony_ci */ 85162306a36Sopenharmony_ci if (boot_command && strlen(boot_command)) { 85262306a36Sopenharmony_ci unsigned long len; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci snprintf(full_boot_str, sizeof(full_boot_str), "boot %s", 85562306a36Sopenharmony_ci boot_command); 85662306a36Sopenharmony_ci len = strlen(full_boot_str); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci if (reboot_data_supported) { 85962306a36Sopenharmony_ci unsigned long ra = kimage_addr_to_ra(full_boot_str); 86062306a36Sopenharmony_ci unsigned long hv_ret; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci hv_ret = sun4v_reboot_data_set(ra, len); 86362306a36Sopenharmony_ci if (hv_ret != HV_EOK) 86462306a36Sopenharmony_ci pr_err("SUN4V: Unable to set reboot data " 86562306a36Sopenharmony_ci "hv_ret=%lu\n", hv_ret); 86662306a36Sopenharmony_ci } else { 86762306a36Sopenharmony_ci ldom_set_var("reboot-command", full_boot_str); 86862306a36Sopenharmony_ci } 86962306a36Sopenharmony_ci } 87062306a36Sopenharmony_ci sun4v_mach_sir(); 87162306a36Sopenharmony_ci} 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_civoid ldom_power_off(void) 87462306a36Sopenharmony_ci{ 87562306a36Sopenharmony_ci sun4v_mach_exit(0); 87662306a36Sopenharmony_ci} 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_cistatic void ds_conn_reset(struct ds_info *dp) 87962306a36Sopenharmony_ci{ 88062306a36Sopenharmony_ci printk(KERN_ERR "ds-%llu: ds_conn_reset() from %ps\n", 88162306a36Sopenharmony_ci dp->id, __builtin_return_address(0)); 88262306a36Sopenharmony_ci} 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_cistatic int register_services(struct ds_info *dp) 88562306a36Sopenharmony_ci{ 88662306a36Sopenharmony_ci struct ldc_channel *lp = dp->lp; 88762306a36Sopenharmony_ci int i; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci for (i = 0; i < dp->num_ds_states; i++) { 89062306a36Sopenharmony_ci struct { 89162306a36Sopenharmony_ci struct ds_reg_req req; 89262306a36Sopenharmony_ci u8 id_buf[256]; 89362306a36Sopenharmony_ci } pbuf; 89462306a36Sopenharmony_ci struct ds_cap_state *cp = &dp->ds_states[i]; 89562306a36Sopenharmony_ci int err, msg_len; 89662306a36Sopenharmony_ci u64 new_count; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci if (cp->state == CAP_STATE_REGISTERED) 89962306a36Sopenharmony_ci continue; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci new_count = sched_clock() & 0xffffffff; 90262306a36Sopenharmony_ci cp->handle = ((u64) i << 32) | new_count; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci msg_len = (sizeof(struct ds_reg_req) + 90562306a36Sopenharmony_ci strlen(cp->service_id)); 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci memset(&pbuf, 0, sizeof(pbuf)); 90862306a36Sopenharmony_ci pbuf.req.tag.type = DS_REG_REQ; 90962306a36Sopenharmony_ci pbuf.req.tag.len = (msg_len - sizeof(struct ds_msg_tag)); 91062306a36Sopenharmony_ci pbuf.req.handle = cp->handle; 91162306a36Sopenharmony_ci pbuf.req.major = 1; 91262306a36Sopenharmony_ci pbuf.req.minor = 0; 91362306a36Sopenharmony_ci strcpy(pbuf.id_buf, cp->service_id); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci err = __ds_send(lp, &pbuf, msg_len); 91662306a36Sopenharmony_ci if (err > 0) 91762306a36Sopenharmony_ci cp->state = CAP_STATE_REG_SENT; 91862306a36Sopenharmony_ci } 91962306a36Sopenharmony_ci return 0; 92062306a36Sopenharmony_ci} 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_cistatic int ds_handshake(struct ds_info *dp, struct ds_msg_tag *pkt) 92362306a36Sopenharmony_ci{ 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci if (dp->hs_state == DS_HS_START) { 92662306a36Sopenharmony_ci if (pkt->type != DS_INIT_ACK) 92762306a36Sopenharmony_ci goto conn_reset; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci dp->hs_state = DS_HS_DONE; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci return register_services(dp); 93262306a36Sopenharmony_ci } 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci if (dp->hs_state != DS_HS_DONE) 93562306a36Sopenharmony_ci goto conn_reset; 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci if (pkt->type == DS_REG_ACK) { 93862306a36Sopenharmony_ci struct ds_reg_ack *ap = (struct ds_reg_ack *) pkt; 93962306a36Sopenharmony_ci struct ds_cap_state *cp = find_cap(dp, ap->handle); 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci if (!cp) { 94262306a36Sopenharmony_ci printk(KERN_ERR "ds-%llu: REG ACK for unknown " 94362306a36Sopenharmony_ci "handle %llx\n", dp->id, ap->handle); 94462306a36Sopenharmony_ci return 0; 94562306a36Sopenharmony_ci } 94662306a36Sopenharmony_ci printk(KERN_INFO "ds-%llu: Registered %s service.\n", 94762306a36Sopenharmony_ci dp->id, cp->service_id); 94862306a36Sopenharmony_ci cp->state = CAP_STATE_REGISTERED; 94962306a36Sopenharmony_ci } else if (pkt->type == DS_REG_NACK) { 95062306a36Sopenharmony_ci struct ds_reg_nack *np = (struct ds_reg_nack *) pkt; 95162306a36Sopenharmony_ci struct ds_cap_state *cp = find_cap(dp, np->handle); 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci if (!cp) { 95462306a36Sopenharmony_ci printk(KERN_ERR "ds-%llu: REG NACK for " 95562306a36Sopenharmony_ci "unknown handle %llx\n", 95662306a36Sopenharmony_ci dp->id, np->handle); 95762306a36Sopenharmony_ci return 0; 95862306a36Sopenharmony_ci } 95962306a36Sopenharmony_ci cp->state = CAP_STATE_UNKNOWN; 96062306a36Sopenharmony_ci } 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci return 0; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ciconn_reset: 96562306a36Sopenharmony_ci ds_conn_reset(dp); 96662306a36Sopenharmony_ci return -ECONNRESET; 96762306a36Sopenharmony_ci} 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_cistatic void __send_ds_nack(struct ds_info *dp, u64 handle) 97062306a36Sopenharmony_ci{ 97162306a36Sopenharmony_ci struct ds_data_nack nack = { 97262306a36Sopenharmony_ci .tag = { 97362306a36Sopenharmony_ci .type = DS_NACK, 97462306a36Sopenharmony_ci .len = (sizeof(struct ds_data_nack) - 97562306a36Sopenharmony_ci sizeof(struct ds_msg_tag)), 97662306a36Sopenharmony_ci }, 97762306a36Sopenharmony_ci .handle = handle, 97862306a36Sopenharmony_ci .result = DS_INV_HDL, 97962306a36Sopenharmony_ci }; 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci __ds_send(dp->lp, &nack, sizeof(nack)); 98262306a36Sopenharmony_ci} 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_cistatic LIST_HEAD(ds_work_list); 98562306a36Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(ds_wait); 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_cistruct ds_queue_entry { 98862306a36Sopenharmony_ci struct list_head list; 98962306a36Sopenharmony_ci struct ds_info *dp; 99062306a36Sopenharmony_ci int req_len; 99162306a36Sopenharmony_ci int __pad; 99262306a36Sopenharmony_ci u64 req[]; 99362306a36Sopenharmony_ci}; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_cistatic void process_ds_work(void) 99662306a36Sopenharmony_ci{ 99762306a36Sopenharmony_ci struct ds_queue_entry *qp, *tmp; 99862306a36Sopenharmony_ci unsigned long flags; 99962306a36Sopenharmony_ci LIST_HEAD(todo); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci spin_lock_irqsave(&ds_lock, flags); 100262306a36Sopenharmony_ci list_splice_init(&ds_work_list, &todo); 100362306a36Sopenharmony_ci spin_unlock_irqrestore(&ds_lock, flags); 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci list_for_each_entry_safe(qp, tmp, &todo, list) { 100662306a36Sopenharmony_ci struct ds_data *dpkt = (struct ds_data *) qp->req; 100762306a36Sopenharmony_ci struct ds_info *dp = qp->dp; 100862306a36Sopenharmony_ci struct ds_cap_state *cp = find_cap(dp, dpkt->handle); 100962306a36Sopenharmony_ci int req_len = qp->req_len; 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci if (!cp) { 101262306a36Sopenharmony_ci printk(KERN_ERR "ds-%llu: Data for unknown " 101362306a36Sopenharmony_ci "handle %llu\n", 101462306a36Sopenharmony_ci dp->id, dpkt->handle); 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci spin_lock_irqsave(&ds_lock, flags); 101762306a36Sopenharmony_ci __send_ds_nack(dp, dpkt->handle); 101862306a36Sopenharmony_ci spin_unlock_irqrestore(&ds_lock, flags); 101962306a36Sopenharmony_ci } else { 102062306a36Sopenharmony_ci cp->data(dp, cp, dpkt, req_len); 102162306a36Sopenharmony_ci } 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci list_del(&qp->list); 102462306a36Sopenharmony_ci kfree(qp); 102562306a36Sopenharmony_ci } 102662306a36Sopenharmony_ci} 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_cistatic int ds_thread(void *__unused) 102962306a36Sopenharmony_ci{ 103062306a36Sopenharmony_ci DEFINE_WAIT(wait); 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci while (1) { 103362306a36Sopenharmony_ci prepare_to_wait(&ds_wait, &wait, TASK_INTERRUPTIBLE); 103462306a36Sopenharmony_ci if (list_empty(&ds_work_list)) 103562306a36Sopenharmony_ci schedule(); 103662306a36Sopenharmony_ci finish_wait(&ds_wait, &wait); 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci if (kthread_should_stop()) 103962306a36Sopenharmony_ci break; 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci process_ds_work(); 104262306a36Sopenharmony_ci } 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci return 0; 104562306a36Sopenharmony_ci} 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_cistatic int ds_data(struct ds_info *dp, struct ds_msg_tag *pkt, int len) 104862306a36Sopenharmony_ci{ 104962306a36Sopenharmony_ci struct ds_data *dpkt = (struct ds_data *) pkt; 105062306a36Sopenharmony_ci struct ds_queue_entry *qp; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci qp = kmalloc(sizeof(struct ds_queue_entry) + len, GFP_ATOMIC); 105362306a36Sopenharmony_ci if (!qp) { 105462306a36Sopenharmony_ci __send_ds_nack(dp, dpkt->handle); 105562306a36Sopenharmony_ci } else { 105662306a36Sopenharmony_ci qp->dp = dp; 105762306a36Sopenharmony_ci memcpy(&qp->req, pkt, len); 105862306a36Sopenharmony_ci list_add_tail(&qp->list, &ds_work_list); 105962306a36Sopenharmony_ci wake_up(&ds_wait); 106062306a36Sopenharmony_ci } 106162306a36Sopenharmony_ci return 0; 106262306a36Sopenharmony_ci} 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_cistatic void ds_up(struct ds_info *dp) 106562306a36Sopenharmony_ci{ 106662306a36Sopenharmony_ci struct ldc_channel *lp = dp->lp; 106762306a36Sopenharmony_ci struct ds_ver_req req; 106862306a36Sopenharmony_ci int err; 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci req.tag.type = DS_INIT_REQ; 107162306a36Sopenharmony_ci req.tag.len = sizeof(req) - sizeof(struct ds_msg_tag); 107262306a36Sopenharmony_ci req.ver.major = 1; 107362306a36Sopenharmony_ci req.ver.minor = 0; 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci err = __ds_send(lp, &req, sizeof(req)); 107662306a36Sopenharmony_ci if (err > 0) 107762306a36Sopenharmony_ci dp->hs_state = DS_HS_START; 107862306a36Sopenharmony_ci} 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_cistatic void ds_reset(struct ds_info *dp) 108162306a36Sopenharmony_ci{ 108262306a36Sopenharmony_ci int i; 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci dp->hs_state = 0; 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci for (i = 0; i < dp->num_ds_states; i++) { 108762306a36Sopenharmony_ci struct ds_cap_state *cp = &dp->ds_states[i]; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci cp->state = CAP_STATE_UNKNOWN; 109062306a36Sopenharmony_ci } 109162306a36Sopenharmony_ci} 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_cistatic void ds_event(void *arg, int event) 109462306a36Sopenharmony_ci{ 109562306a36Sopenharmony_ci struct ds_info *dp = arg; 109662306a36Sopenharmony_ci struct ldc_channel *lp = dp->lp; 109762306a36Sopenharmony_ci unsigned long flags; 109862306a36Sopenharmony_ci int err; 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci spin_lock_irqsave(&ds_lock, flags); 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci if (event == LDC_EVENT_UP) { 110362306a36Sopenharmony_ci ds_up(dp); 110462306a36Sopenharmony_ci spin_unlock_irqrestore(&ds_lock, flags); 110562306a36Sopenharmony_ci return; 110662306a36Sopenharmony_ci } 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci if (event == LDC_EVENT_RESET) { 110962306a36Sopenharmony_ci ds_reset(dp); 111062306a36Sopenharmony_ci spin_unlock_irqrestore(&ds_lock, flags); 111162306a36Sopenharmony_ci return; 111262306a36Sopenharmony_ci } 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci if (event != LDC_EVENT_DATA_READY) { 111562306a36Sopenharmony_ci printk(KERN_WARNING "ds-%llu: Unexpected LDC event %d\n", 111662306a36Sopenharmony_ci dp->id, event); 111762306a36Sopenharmony_ci spin_unlock_irqrestore(&ds_lock, flags); 111862306a36Sopenharmony_ci return; 111962306a36Sopenharmony_ci } 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci err = 0; 112262306a36Sopenharmony_ci while (1) { 112362306a36Sopenharmony_ci struct ds_msg_tag *tag; 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci err = ldc_read(lp, dp->rcv_buf, sizeof(*tag)); 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci if (unlikely(err < 0)) { 112862306a36Sopenharmony_ci if (err == -ECONNRESET) 112962306a36Sopenharmony_ci ds_conn_reset(dp); 113062306a36Sopenharmony_ci break; 113162306a36Sopenharmony_ci } 113262306a36Sopenharmony_ci if (err == 0) 113362306a36Sopenharmony_ci break; 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci tag = dp->rcv_buf; 113662306a36Sopenharmony_ci err = ldc_read(lp, tag + 1, tag->len); 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci if (unlikely(err < 0)) { 113962306a36Sopenharmony_ci if (err == -ECONNRESET) 114062306a36Sopenharmony_ci ds_conn_reset(dp); 114162306a36Sopenharmony_ci break; 114262306a36Sopenharmony_ci } 114362306a36Sopenharmony_ci if (err < tag->len) 114462306a36Sopenharmony_ci break; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci if (tag->type < DS_DATA) 114762306a36Sopenharmony_ci err = ds_handshake(dp, dp->rcv_buf); 114862306a36Sopenharmony_ci else 114962306a36Sopenharmony_ci err = ds_data(dp, dp->rcv_buf, 115062306a36Sopenharmony_ci sizeof(*tag) + err); 115162306a36Sopenharmony_ci if (err == -ECONNRESET) 115262306a36Sopenharmony_ci break; 115362306a36Sopenharmony_ci } 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci spin_unlock_irqrestore(&ds_lock, flags); 115662306a36Sopenharmony_ci} 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_cistatic int ds_probe(struct vio_dev *vdev, const struct vio_device_id *id) 115962306a36Sopenharmony_ci{ 116062306a36Sopenharmony_ci static int ds_version_printed; 116162306a36Sopenharmony_ci struct ldc_channel_config ds_cfg = { 116262306a36Sopenharmony_ci .event = ds_event, 116362306a36Sopenharmony_ci .mtu = 4096, 116462306a36Sopenharmony_ci .mode = LDC_MODE_STREAM, 116562306a36Sopenharmony_ci }; 116662306a36Sopenharmony_ci struct mdesc_handle *hp; 116762306a36Sopenharmony_ci struct ldc_channel *lp; 116862306a36Sopenharmony_ci struct ds_info *dp; 116962306a36Sopenharmony_ci const u64 *val; 117062306a36Sopenharmony_ci int err, i; 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci if (ds_version_printed++ == 0) 117362306a36Sopenharmony_ci printk(KERN_INFO "%s", version); 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci dp = kzalloc(sizeof(*dp), GFP_KERNEL); 117662306a36Sopenharmony_ci err = -ENOMEM; 117762306a36Sopenharmony_ci if (!dp) 117862306a36Sopenharmony_ci goto out_err; 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci hp = mdesc_grab(); 118162306a36Sopenharmony_ci val = mdesc_get_property(hp, vdev->mp, "id", NULL); 118262306a36Sopenharmony_ci if (val) 118362306a36Sopenharmony_ci dp->id = *val; 118462306a36Sopenharmony_ci mdesc_release(hp); 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci dp->rcv_buf = kzalloc(4096, GFP_KERNEL); 118762306a36Sopenharmony_ci if (!dp->rcv_buf) 118862306a36Sopenharmony_ci goto out_free_dp; 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci dp->rcv_buf_len = 4096; 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci dp->ds_states = kmemdup(ds_states_template, 119362306a36Sopenharmony_ci sizeof(ds_states_template), GFP_KERNEL); 119462306a36Sopenharmony_ci if (!dp->ds_states) 119562306a36Sopenharmony_ci goto out_free_rcv_buf; 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci dp->num_ds_states = ARRAY_SIZE(ds_states_template); 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci for (i = 0; i < dp->num_ds_states; i++) 120062306a36Sopenharmony_ci dp->ds_states[i].handle = ((u64)i << 32); 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci ds_cfg.tx_irq = vdev->tx_irq; 120362306a36Sopenharmony_ci ds_cfg.rx_irq = vdev->rx_irq; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci lp = ldc_alloc(vdev->channel_id, &ds_cfg, dp, "DS"); 120662306a36Sopenharmony_ci if (IS_ERR(lp)) { 120762306a36Sopenharmony_ci err = PTR_ERR(lp); 120862306a36Sopenharmony_ci goto out_free_ds_states; 120962306a36Sopenharmony_ci } 121062306a36Sopenharmony_ci dp->lp = lp; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci err = ldc_bind(lp); 121362306a36Sopenharmony_ci if (err) 121462306a36Sopenharmony_ci goto out_free_ldc; 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci spin_lock_irq(&ds_lock); 121762306a36Sopenharmony_ci dp->next = ds_info_list; 121862306a36Sopenharmony_ci ds_info_list = dp; 121962306a36Sopenharmony_ci spin_unlock_irq(&ds_lock); 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci return err; 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ciout_free_ldc: 122462306a36Sopenharmony_ci ldc_free(dp->lp); 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ciout_free_ds_states: 122762306a36Sopenharmony_ci kfree(dp->ds_states); 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ciout_free_rcv_buf: 123062306a36Sopenharmony_ci kfree(dp->rcv_buf); 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ciout_free_dp: 123362306a36Sopenharmony_ci kfree(dp); 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ciout_err: 123662306a36Sopenharmony_ci return err; 123762306a36Sopenharmony_ci} 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_cistatic const struct vio_device_id ds_match[] = { 124062306a36Sopenharmony_ci { 124162306a36Sopenharmony_ci .type = "domain-services-port", 124262306a36Sopenharmony_ci }, 124362306a36Sopenharmony_ci {}, 124462306a36Sopenharmony_ci}; 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_cistatic struct vio_driver ds_driver = { 124762306a36Sopenharmony_ci .id_table = ds_match, 124862306a36Sopenharmony_ci .probe = ds_probe, 124962306a36Sopenharmony_ci .name = "ds", 125062306a36Sopenharmony_ci}; 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_cistatic int __init ds_init(void) 125362306a36Sopenharmony_ci{ 125462306a36Sopenharmony_ci unsigned long hv_ret, major, minor; 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci if (tlb_type == hypervisor) { 125762306a36Sopenharmony_ci hv_ret = sun4v_get_version(HV_GRP_REBOOT_DATA, &major, &minor); 125862306a36Sopenharmony_ci if (hv_ret == HV_EOK) { 125962306a36Sopenharmony_ci pr_info("SUN4V: Reboot data supported (maj=%lu,min=%lu).\n", 126062306a36Sopenharmony_ci major, minor); 126162306a36Sopenharmony_ci reboot_data_supported = 1; 126262306a36Sopenharmony_ci } 126362306a36Sopenharmony_ci } 126462306a36Sopenharmony_ci kthread_run(ds_thread, NULL, "kldomd"); 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci return vio_register_driver(&ds_driver); 126762306a36Sopenharmony_ci} 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_cifs_initcall(ds_init); 1270