18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* ds.c: Domain Services driver for Logical Domains 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2007, 2008 David S. Miller <davem@davemloft.net> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/kernel.h> 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/types.h> 108c2ecf20Sopenharmony_ci#include <linux/string.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/sched.h> 138c2ecf20Sopenharmony_ci#include <linux/sched/clock.h> 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/mutex.h> 168c2ecf20Sopenharmony_ci#include <linux/kthread.h> 178c2ecf20Sopenharmony_ci#include <linux/reboot.h> 188c2ecf20Sopenharmony_ci#include <linux/cpu.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <asm/hypervisor.h> 218c2ecf20Sopenharmony_ci#include <asm/ldc.h> 228c2ecf20Sopenharmony_ci#include <asm/vio.h> 238c2ecf20Sopenharmony_ci#include <asm/mdesc.h> 248c2ecf20Sopenharmony_ci#include <asm/head.h> 258c2ecf20Sopenharmony_ci#include <asm/irq.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include "kernel.h" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define DRV_MODULE_NAME "ds" 308c2ecf20Sopenharmony_ci#define PFX DRV_MODULE_NAME ": " 318c2ecf20Sopenharmony_ci#define DRV_MODULE_VERSION "1.0" 328c2ecf20Sopenharmony_ci#define DRV_MODULE_RELDATE "Jul 11, 2007" 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic char version[] = 358c2ecf20Sopenharmony_ci DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; 368c2ecf20Sopenharmony_ciMODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); 378c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Sun LDOM domain services driver"); 388c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 398c2ecf20Sopenharmony_ciMODULE_VERSION(DRV_MODULE_VERSION); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistruct ds_msg_tag { 428c2ecf20Sopenharmony_ci __u32 type; 438c2ecf20Sopenharmony_ci#define DS_INIT_REQ 0x00 448c2ecf20Sopenharmony_ci#define DS_INIT_ACK 0x01 458c2ecf20Sopenharmony_ci#define DS_INIT_NACK 0x02 468c2ecf20Sopenharmony_ci#define DS_REG_REQ 0x03 478c2ecf20Sopenharmony_ci#define DS_REG_ACK 0x04 488c2ecf20Sopenharmony_ci#define DS_REG_NACK 0x05 498c2ecf20Sopenharmony_ci#define DS_UNREG_REQ 0x06 508c2ecf20Sopenharmony_ci#define DS_UNREG_ACK 0x07 518c2ecf20Sopenharmony_ci#define DS_UNREG_NACK 0x08 528c2ecf20Sopenharmony_ci#define DS_DATA 0x09 538c2ecf20Sopenharmony_ci#define DS_NACK 0x0a 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci __u32 len; 568c2ecf20Sopenharmony_ci}; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* Result codes */ 598c2ecf20Sopenharmony_ci#define DS_OK 0x00 608c2ecf20Sopenharmony_ci#define DS_REG_VER_NACK 0x01 618c2ecf20Sopenharmony_ci#define DS_REG_DUP 0x02 628c2ecf20Sopenharmony_ci#define DS_INV_HDL 0x03 638c2ecf20Sopenharmony_ci#define DS_TYPE_UNKNOWN 0x04 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistruct ds_version { 668c2ecf20Sopenharmony_ci __u16 major; 678c2ecf20Sopenharmony_ci __u16 minor; 688c2ecf20Sopenharmony_ci}; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistruct ds_ver_req { 718c2ecf20Sopenharmony_ci struct ds_msg_tag tag; 728c2ecf20Sopenharmony_ci struct ds_version ver; 738c2ecf20Sopenharmony_ci}; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistruct ds_ver_ack { 768c2ecf20Sopenharmony_ci struct ds_msg_tag tag; 778c2ecf20Sopenharmony_ci __u16 minor; 788c2ecf20Sopenharmony_ci}; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistruct ds_ver_nack { 818c2ecf20Sopenharmony_ci struct ds_msg_tag tag; 828c2ecf20Sopenharmony_ci __u16 major; 838c2ecf20Sopenharmony_ci}; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistruct ds_reg_req { 868c2ecf20Sopenharmony_ci struct ds_msg_tag tag; 878c2ecf20Sopenharmony_ci __u64 handle; 888c2ecf20Sopenharmony_ci __u16 major; 898c2ecf20Sopenharmony_ci __u16 minor; 908c2ecf20Sopenharmony_ci char svc_id[]; 918c2ecf20Sopenharmony_ci}; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistruct ds_reg_ack { 948c2ecf20Sopenharmony_ci struct ds_msg_tag tag; 958c2ecf20Sopenharmony_ci __u64 handle; 968c2ecf20Sopenharmony_ci __u16 minor; 978c2ecf20Sopenharmony_ci}; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistruct ds_reg_nack { 1008c2ecf20Sopenharmony_ci struct ds_msg_tag tag; 1018c2ecf20Sopenharmony_ci __u64 handle; 1028c2ecf20Sopenharmony_ci __u16 major; 1038c2ecf20Sopenharmony_ci}; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistruct ds_unreg_req { 1068c2ecf20Sopenharmony_ci struct ds_msg_tag tag; 1078c2ecf20Sopenharmony_ci __u64 handle; 1088c2ecf20Sopenharmony_ci}; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistruct ds_unreg_ack { 1118c2ecf20Sopenharmony_ci struct ds_msg_tag tag; 1128c2ecf20Sopenharmony_ci __u64 handle; 1138c2ecf20Sopenharmony_ci}; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistruct ds_unreg_nack { 1168c2ecf20Sopenharmony_ci struct ds_msg_tag tag; 1178c2ecf20Sopenharmony_ci __u64 handle; 1188c2ecf20Sopenharmony_ci}; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistruct ds_data { 1218c2ecf20Sopenharmony_ci struct ds_msg_tag tag; 1228c2ecf20Sopenharmony_ci __u64 handle; 1238c2ecf20Sopenharmony_ci}; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistruct ds_data_nack { 1268c2ecf20Sopenharmony_ci struct ds_msg_tag tag; 1278c2ecf20Sopenharmony_ci __u64 handle; 1288c2ecf20Sopenharmony_ci __u64 result; 1298c2ecf20Sopenharmony_ci}; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistruct ds_info; 1328c2ecf20Sopenharmony_cistruct ds_cap_state { 1338c2ecf20Sopenharmony_ci __u64 handle; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci void (*data)(struct ds_info *dp, 1368c2ecf20Sopenharmony_ci struct ds_cap_state *cp, 1378c2ecf20Sopenharmony_ci void *buf, int len); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci const char *service_id; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci u8 state; 1428c2ecf20Sopenharmony_ci#define CAP_STATE_UNKNOWN 0x00 1438c2ecf20Sopenharmony_ci#define CAP_STATE_REG_SENT 0x01 1448c2ecf20Sopenharmony_ci#define CAP_STATE_REGISTERED 0x02 1458c2ecf20Sopenharmony_ci}; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic void md_update_data(struct ds_info *dp, struct ds_cap_state *cp, 1488c2ecf20Sopenharmony_ci void *buf, int len); 1498c2ecf20Sopenharmony_cistatic void domain_shutdown_data(struct ds_info *dp, 1508c2ecf20Sopenharmony_ci struct ds_cap_state *cp, 1518c2ecf20Sopenharmony_ci void *buf, int len); 1528c2ecf20Sopenharmony_cistatic void domain_panic_data(struct ds_info *dp, 1538c2ecf20Sopenharmony_ci struct ds_cap_state *cp, 1548c2ecf20Sopenharmony_ci void *buf, int len); 1558c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 1568c2ecf20Sopenharmony_cistatic void dr_cpu_data(struct ds_info *dp, 1578c2ecf20Sopenharmony_ci struct ds_cap_state *cp, 1588c2ecf20Sopenharmony_ci void *buf, int len); 1598c2ecf20Sopenharmony_ci#endif 1608c2ecf20Sopenharmony_cistatic void ds_pri_data(struct ds_info *dp, 1618c2ecf20Sopenharmony_ci struct ds_cap_state *cp, 1628c2ecf20Sopenharmony_ci void *buf, int len); 1638c2ecf20Sopenharmony_cistatic void ds_var_data(struct ds_info *dp, 1648c2ecf20Sopenharmony_ci struct ds_cap_state *cp, 1658c2ecf20Sopenharmony_ci void *buf, int len); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic struct ds_cap_state ds_states_template[] = { 1688c2ecf20Sopenharmony_ci { 1698c2ecf20Sopenharmony_ci .service_id = "md-update", 1708c2ecf20Sopenharmony_ci .data = md_update_data, 1718c2ecf20Sopenharmony_ci }, 1728c2ecf20Sopenharmony_ci { 1738c2ecf20Sopenharmony_ci .service_id = "domain-shutdown", 1748c2ecf20Sopenharmony_ci .data = domain_shutdown_data, 1758c2ecf20Sopenharmony_ci }, 1768c2ecf20Sopenharmony_ci { 1778c2ecf20Sopenharmony_ci .service_id = "domain-panic", 1788c2ecf20Sopenharmony_ci .data = domain_panic_data, 1798c2ecf20Sopenharmony_ci }, 1808c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 1818c2ecf20Sopenharmony_ci { 1828c2ecf20Sopenharmony_ci .service_id = "dr-cpu", 1838c2ecf20Sopenharmony_ci .data = dr_cpu_data, 1848c2ecf20Sopenharmony_ci }, 1858c2ecf20Sopenharmony_ci#endif 1868c2ecf20Sopenharmony_ci { 1878c2ecf20Sopenharmony_ci .service_id = "pri", 1888c2ecf20Sopenharmony_ci .data = ds_pri_data, 1898c2ecf20Sopenharmony_ci }, 1908c2ecf20Sopenharmony_ci { 1918c2ecf20Sopenharmony_ci .service_id = "var-config", 1928c2ecf20Sopenharmony_ci .data = ds_var_data, 1938c2ecf20Sopenharmony_ci }, 1948c2ecf20Sopenharmony_ci { 1958c2ecf20Sopenharmony_ci .service_id = "var-config-backup", 1968c2ecf20Sopenharmony_ci .data = ds_var_data, 1978c2ecf20Sopenharmony_ci }, 1988c2ecf20Sopenharmony_ci}; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(ds_lock); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistruct ds_info { 2038c2ecf20Sopenharmony_ci struct ldc_channel *lp; 2048c2ecf20Sopenharmony_ci u8 hs_state; 2058c2ecf20Sopenharmony_ci#define DS_HS_START 0x01 2068c2ecf20Sopenharmony_ci#define DS_HS_DONE 0x02 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci u64 id; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci void *rcv_buf; 2118c2ecf20Sopenharmony_ci int rcv_buf_len; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci struct ds_cap_state *ds_states; 2148c2ecf20Sopenharmony_ci int num_ds_states; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci struct ds_info *next; 2178c2ecf20Sopenharmony_ci}; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic struct ds_info *ds_info_list; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic struct ds_cap_state *find_cap(struct ds_info *dp, u64 handle) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci unsigned int index = handle >> 32; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (index >= dp->num_ds_states) 2268c2ecf20Sopenharmony_ci return NULL; 2278c2ecf20Sopenharmony_ci return &dp->ds_states[index]; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic struct ds_cap_state *find_cap_by_string(struct ds_info *dp, 2318c2ecf20Sopenharmony_ci const char *name) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci int i; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci for (i = 0; i < dp->num_ds_states; i++) { 2368c2ecf20Sopenharmony_ci if (strcmp(dp->ds_states[i].service_id, name)) 2378c2ecf20Sopenharmony_ci continue; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci return &dp->ds_states[i]; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci return NULL; 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic int __ds_send(struct ldc_channel *lp, void *data, int len) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci int err, limit = 1000; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci err = -EINVAL; 2498c2ecf20Sopenharmony_ci while (limit-- > 0) { 2508c2ecf20Sopenharmony_ci err = ldc_write(lp, data, len); 2518c2ecf20Sopenharmony_ci if (!err || (err != -EAGAIN)) 2528c2ecf20Sopenharmony_ci break; 2538c2ecf20Sopenharmony_ci udelay(1); 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci return err; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic int ds_send(struct ldc_channel *lp, void *data, int len) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci unsigned long flags; 2628c2ecf20Sopenharmony_ci int err; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci spin_lock_irqsave(&ds_lock, flags); 2658c2ecf20Sopenharmony_ci err = __ds_send(lp, data, len); 2668c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ds_lock, flags); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci return err; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistruct ds_md_update_req { 2728c2ecf20Sopenharmony_ci __u64 req_num; 2738c2ecf20Sopenharmony_ci}; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistruct ds_md_update_res { 2768c2ecf20Sopenharmony_ci __u64 req_num; 2778c2ecf20Sopenharmony_ci __u32 result; 2788c2ecf20Sopenharmony_ci}; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic void md_update_data(struct ds_info *dp, 2818c2ecf20Sopenharmony_ci struct ds_cap_state *cp, 2828c2ecf20Sopenharmony_ci void *buf, int len) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci struct ldc_channel *lp = dp->lp; 2858c2ecf20Sopenharmony_ci struct ds_data *dpkt = buf; 2868c2ecf20Sopenharmony_ci struct ds_md_update_req *rp; 2878c2ecf20Sopenharmony_ci struct { 2888c2ecf20Sopenharmony_ci struct ds_data data; 2898c2ecf20Sopenharmony_ci struct ds_md_update_res res; 2908c2ecf20Sopenharmony_ci } pkt; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci rp = (struct ds_md_update_req *) (dpkt + 1); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci printk(KERN_INFO "ds-%llu: Machine description update.\n", dp->id); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci mdesc_update(); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci memset(&pkt, 0, sizeof(pkt)); 2998c2ecf20Sopenharmony_ci pkt.data.tag.type = DS_DATA; 3008c2ecf20Sopenharmony_ci pkt.data.tag.len = sizeof(pkt) - sizeof(struct ds_msg_tag); 3018c2ecf20Sopenharmony_ci pkt.data.handle = cp->handle; 3028c2ecf20Sopenharmony_ci pkt.res.req_num = rp->req_num; 3038c2ecf20Sopenharmony_ci pkt.res.result = DS_OK; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci ds_send(lp, &pkt, sizeof(pkt)); 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistruct ds_shutdown_req { 3098c2ecf20Sopenharmony_ci __u64 req_num; 3108c2ecf20Sopenharmony_ci __u32 ms_delay; 3118c2ecf20Sopenharmony_ci}; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistruct ds_shutdown_res { 3148c2ecf20Sopenharmony_ci __u64 req_num; 3158c2ecf20Sopenharmony_ci __u32 result; 3168c2ecf20Sopenharmony_ci char reason[1]; 3178c2ecf20Sopenharmony_ci}; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic void domain_shutdown_data(struct ds_info *dp, 3208c2ecf20Sopenharmony_ci struct ds_cap_state *cp, 3218c2ecf20Sopenharmony_ci void *buf, int len) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci struct ldc_channel *lp = dp->lp; 3248c2ecf20Sopenharmony_ci struct ds_data *dpkt = buf; 3258c2ecf20Sopenharmony_ci struct ds_shutdown_req *rp; 3268c2ecf20Sopenharmony_ci struct { 3278c2ecf20Sopenharmony_ci struct ds_data data; 3288c2ecf20Sopenharmony_ci struct ds_shutdown_res res; 3298c2ecf20Sopenharmony_ci } pkt; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci rp = (struct ds_shutdown_req *) (dpkt + 1); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci printk(KERN_ALERT "ds-%llu: Shutdown request from " 3348c2ecf20Sopenharmony_ci "LDOM manager received.\n", dp->id); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci memset(&pkt, 0, sizeof(pkt)); 3378c2ecf20Sopenharmony_ci pkt.data.tag.type = DS_DATA; 3388c2ecf20Sopenharmony_ci pkt.data.tag.len = sizeof(pkt) - sizeof(struct ds_msg_tag); 3398c2ecf20Sopenharmony_ci pkt.data.handle = cp->handle; 3408c2ecf20Sopenharmony_ci pkt.res.req_num = rp->req_num; 3418c2ecf20Sopenharmony_ci pkt.res.result = DS_OK; 3428c2ecf20Sopenharmony_ci pkt.res.reason[0] = 0; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci ds_send(lp, &pkt, sizeof(pkt)); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci orderly_poweroff(true); 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistruct ds_panic_req { 3508c2ecf20Sopenharmony_ci __u64 req_num; 3518c2ecf20Sopenharmony_ci}; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistruct ds_panic_res { 3548c2ecf20Sopenharmony_ci __u64 req_num; 3558c2ecf20Sopenharmony_ci __u32 result; 3568c2ecf20Sopenharmony_ci char reason[1]; 3578c2ecf20Sopenharmony_ci}; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic void domain_panic_data(struct ds_info *dp, 3608c2ecf20Sopenharmony_ci struct ds_cap_state *cp, 3618c2ecf20Sopenharmony_ci void *buf, int len) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci struct ldc_channel *lp = dp->lp; 3648c2ecf20Sopenharmony_ci struct ds_data *dpkt = buf; 3658c2ecf20Sopenharmony_ci struct ds_panic_req *rp; 3668c2ecf20Sopenharmony_ci struct { 3678c2ecf20Sopenharmony_ci struct ds_data data; 3688c2ecf20Sopenharmony_ci struct ds_panic_res res; 3698c2ecf20Sopenharmony_ci } pkt; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci rp = (struct ds_panic_req *) (dpkt + 1); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci printk(KERN_ALERT "ds-%llu: Panic request from " 3748c2ecf20Sopenharmony_ci "LDOM manager received.\n", dp->id); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci memset(&pkt, 0, sizeof(pkt)); 3778c2ecf20Sopenharmony_ci pkt.data.tag.type = DS_DATA; 3788c2ecf20Sopenharmony_ci pkt.data.tag.len = sizeof(pkt) - sizeof(struct ds_msg_tag); 3798c2ecf20Sopenharmony_ci pkt.data.handle = cp->handle; 3808c2ecf20Sopenharmony_ci pkt.res.req_num = rp->req_num; 3818c2ecf20Sopenharmony_ci pkt.res.result = DS_OK; 3828c2ecf20Sopenharmony_ci pkt.res.reason[0] = 0; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci ds_send(lp, &pkt, sizeof(pkt)); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci panic("PANIC requested by LDOM manager."); 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 3908c2ecf20Sopenharmony_cistruct dr_cpu_tag { 3918c2ecf20Sopenharmony_ci __u64 req_num; 3928c2ecf20Sopenharmony_ci __u32 type; 3938c2ecf20Sopenharmony_ci#define DR_CPU_CONFIGURE 0x43 3948c2ecf20Sopenharmony_ci#define DR_CPU_UNCONFIGURE 0x55 3958c2ecf20Sopenharmony_ci#define DR_CPU_FORCE_UNCONFIGURE 0x46 3968c2ecf20Sopenharmony_ci#define DR_CPU_STATUS 0x53 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci/* Responses */ 3998c2ecf20Sopenharmony_ci#define DR_CPU_OK 0x6f 4008c2ecf20Sopenharmony_ci#define DR_CPU_ERROR 0x65 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci __u32 num_records; 4038c2ecf20Sopenharmony_ci}; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistruct dr_cpu_resp_entry { 4068c2ecf20Sopenharmony_ci __u32 cpu; 4078c2ecf20Sopenharmony_ci __u32 result; 4088c2ecf20Sopenharmony_ci#define DR_CPU_RES_OK 0x00 4098c2ecf20Sopenharmony_ci#define DR_CPU_RES_FAILURE 0x01 4108c2ecf20Sopenharmony_ci#define DR_CPU_RES_BLOCKED 0x02 4118c2ecf20Sopenharmony_ci#define DR_CPU_RES_CPU_NOT_RESPONDING 0x03 4128c2ecf20Sopenharmony_ci#define DR_CPU_RES_NOT_IN_MD 0x04 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci __u32 stat; 4158c2ecf20Sopenharmony_ci#define DR_CPU_STAT_NOT_PRESENT 0x00 4168c2ecf20Sopenharmony_ci#define DR_CPU_STAT_UNCONFIGURED 0x01 4178c2ecf20Sopenharmony_ci#define DR_CPU_STAT_CONFIGURED 0x02 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci __u32 str_off; 4208c2ecf20Sopenharmony_ci}; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic void __dr_cpu_send_error(struct ds_info *dp, 4238c2ecf20Sopenharmony_ci struct ds_cap_state *cp, 4248c2ecf20Sopenharmony_ci struct ds_data *data) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci struct dr_cpu_tag *tag = (struct dr_cpu_tag *) (data + 1); 4278c2ecf20Sopenharmony_ci struct { 4288c2ecf20Sopenharmony_ci struct ds_data data; 4298c2ecf20Sopenharmony_ci struct dr_cpu_tag tag; 4308c2ecf20Sopenharmony_ci } pkt; 4318c2ecf20Sopenharmony_ci int msg_len; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci memset(&pkt, 0, sizeof(pkt)); 4348c2ecf20Sopenharmony_ci pkt.data.tag.type = DS_DATA; 4358c2ecf20Sopenharmony_ci pkt.data.handle = cp->handle; 4368c2ecf20Sopenharmony_ci pkt.tag.req_num = tag->req_num; 4378c2ecf20Sopenharmony_ci pkt.tag.type = DR_CPU_ERROR; 4388c2ecf20Sopenharmony_ci pkt.tag.num_records = 0; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci msg_len = (sizeof(struct ds_data) + 4418c2ecf20Sopenharmony_ci sizeof(struct dr_cpu_tag)); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci pkt.data.tag.len = msg_len - sizeof(struct ds_msg_tag); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci __ds_send(dp->lp, &pkt, msg_len); 4468c2ecf20Sopenharmony_ci} 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic void dr_cpu_send_error(struct ds_info *dp, 4498c2ecf20Sopenharmony_ci struct ds_cap_state *cp, 4508c2ecf20Sopenharmony_ci struct ds_data *data) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci unsigned long flags; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci spin_lock_irqsave(&ds_lock, flags); 4558c2ecf20Sopenharmony_ci __dr_cpu_send_error(dp, cp, data); 4568c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ds_lock, flags); 4578c2ecf20Sopenharmony_ci} 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci#define CPU_SENTINEL 0xffffffff 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_cistatic void purge_dups(u32 *list, u32 num_ents) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci unsigned int i; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci for (i = 0; i < num_ents; i++) { 4668c2ecf20Sopenharmony_ci u32 cpu = list[i]; 4678c2ecf20Sopenharmony_ci unsigned int j; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci if (cpu == CPU_SENTINEL) 4708c2ecf20Sopenharmony_ci continue; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci for (j = i + 1; j < num_ents; j++) { 4738c2ecf20Sopenharmony_ci if (list[j] == cpu) 4748c2ecf20Sopenharmony_ci list[j] = CPU_SENTINEL; 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cistatic int dr_cpu_size_response(int ncpus) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci return (sizeof(struct ds_data) + 4828c2ecf20Sopenharmony_ci sizeof(struct dr_cpu_tag) + 4838c2ecf20Sopenharmony_ci (sizeof(struct dr_cpu_resp_entry) * ncpus)); 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_cistatic void dr_cpu_init_response(struct ds_data *resp, u64 req_num, 4878c2ecf20Sopenharmony_ci u64 handle, int resp_len, int ncpus, 4888c2ecf20Sopenharmony_ci cpumask_t *mask, u32 default_stat) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci struct dr_cpu_resp_entry *ent; 4918c2ecf20Sopenharmony_ci struct dr_cpu_tag *tag; 4928c2ecf20Sopenharmony_ci int i, cpu; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci tag = (struct dr_cpu_tag *) (resp + 1); 4958c2ecf20Sopenharmony_ci ent = (struct dr_cpu_resp_entry *) (tag + 1); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci resp->tag.type = DS_DATA; 4988c2ecf20Sopenharmony_ci resp->tag.len = resp_len - sizeof(struct ds_msg_tag); 4998c2ecf20Sopenharmony_ci resp->handle = handle; 5008c2ecf20Sopenharmony_ci tag->req_num = req_num; 5018c2ecf20Sopenharmony_ci tag->type = DR_CPU_OK; 5028c2ecf20Sopenharmony_ci tag->num_records = ncpus; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci i = 0; 5058c2ecf20Sopenharmony_ci for_each_cpu(cpu, mask) { 5068c2ecf20Sopenharmony_ci ent[i].cpu = cpu; 5078c2ecf20Sopenharmony_ci ent[i].result = DR_CPU_RES_OK; 5088c2ecf20Sopenharmony_ci ent[i].stat = default_stat; 5098c2ecf20Sopenharmony_ci i++; 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci BUG_ON(i != ncpus); 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_cistatic void dr_cpu_mark(struct ds_data *resp, int cpu, int ncpus, 5158c2ecf20Sopenharmony_ci u32 res, u32 stat) 5168c2ecf20Sopenharmony_ci{ 5178c2ecf20Sopenharmony_ci struct dr_cpu_resp_entry *ent; 5188c2ecf20Sopenharmony_ci struct dr_cpu_tag *tag; 5198c2ecf20Sopenharmony_ci int i; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci tag = (struct dr_cpu_tag *) (resp + 1); 5228c2ecf20Sopenharmony_ci ent = (struct dr_cpu_resp_entry *) (tag + 1); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci for (i = 0; i < ncpus; i++) { 5258c2ecf20Sopenharmony_ci if (ent[i].cpu != cpu) 5268c2ecf20Sopenharmony_ci continue; 5278c2ecf20Sopenharmony_ci ent[i].result = res; 5288c2ecf20Sopenharmony_ci ent[i].stat = stat; 5298c2ecf20Sopenharmony_ci break; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci} 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_cistatic int dr_cpu_configure(struct ds_info *dp, struct ds_cap_state *cp, 5348c2ecf20Sopenharmony_ci u64 req_num, cpumask_t *mask) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci struct ds_data *resp; 5378c2ecf20Sopenharmony_ci int resp_len, ncpus, cpu; 5388c2ecf20Sopenharmony_ci unsigned long flags; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci ncpus = cpumask_weight(mask); 5418c2ecf20Sopenharmony_ci resp_len = dr_cpu_size_response(ncpus); 5428c2ecf20Sopenharmony_ci resp = kzalloc(resp_len, GFP_KERNEL); 5438c2ecf20Sopenharmony_ci if (!resp) 5448c2ecf20Sopenharmony_ci return -ENOMEM; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci dr_cpu_init_response(resp, req_num, cp->handle, 5478c2ecf20Sopenharmony_ci resp_len, ncpus, mask, 5488c2ecf20Sopenharmony_ci DR_CPU_STAT_CONFIGURED); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci mdesc_populate_present_mask(mask); 5518c2ecf20Sopenharmony_ci mdesc_fill_in_cpu_data(mask); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci for_each_cpu(cpu, mask) { 5548c2ecf20Sopenharmony_ci int err; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci printk(KERN_INFO "ds-%llu: Starting cpu %d...\n", 5578c2ecf20Sopenharmony_ci dp->id, cpu); 5588c2ecf20Sopenharmony_ci err = add_cpu(cpu); 5598c2ecf20Sopenharmony_ci if (err) { 5608c2ecf20Sopenharmony_ci __u32 res = DR_CPU_RES_FAILURE; 5618c2ecf20Sopenharmony_ci __u32 stat = DR_CPU_STAT_UNCONFIGURED; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci if (!cpu_present(cpu)) { 5648c2ecf20Sopenharmony_ci /* CPU not present in MD */ 5658c2ecf20Sopenharmony_ci res = DR_CPU_RES_NOT_IN_MD; 5668c2ecf20Sopenharmony_ci stat = DR_CPU_STAT_NOT_PRESENT; 5678c2ecf20Sopenharmony_ci } else if (err == -ENODEV) { 5688c2ecf20Sopenharmony_ci /* CPU did not call in successfully */ 5698c2ecf20Sopenharmony_ci res = DR_CPU_RES_CPU_NOT_RESPONDING; 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci printk(KERN_INFO "ds-%llu: CPU startup failed err=%d\n", 5738c2ecf20Sopenharmony_ci dp->id, err); 5748c2ecf20Sopenharmony_ci dr_cpu_mark(resp, cpu, ncpus, res, stat); 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci spin_lock_irqsave(&ds_lock, flags); 5798c2ecf20Sopenharmony_ci __ds_send(dp->lp, resp, resp_len); 5808c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ds_lock, flags); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci kfree(resp); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci /* Redistribute IRQs, taking into account the new cpus. */ 5858c2ecf20Sopenharmony_ci fixup_irqs(); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci return 0; 5888c2ecf20Sopenharmony_ci} 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_cistatic int dr_cpu_unconfigure(struct ds_info *dp, 5918c2ecf20Sopenharmony_ci struct ds_cap_state *cp, 5928c2ecf20Sopenharmony_ci u64 req_num, 5938c2ecf20Sopenharmony_ci cpumask_t *mask) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci struct ds_data *resp; 5968c2ecf20Sopenharmony_ci int resp_len, ncpus, cpu; 5978c2ecf20Sopenharmony_ci unsigned long flags; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci ncpus = cpumask_weight(mask); 6008c2ecf20Sopenharmony_ci resp_len = dr_cpu_size_response(ncpus); 6018c2ecf20Sopenharmony_ci resp = kzalloc(resp_len, GFP_KERNEL); 6028c2ecf20Sopenharmony_ci if (!resp) 6038c2ecf20Sopenharmony_ci return -ENOMEM; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci dr_cpu_init_response(resp, req_num, cp->handle, 6068c2ecf20Sopenharmony_ci resp_len, ncpus, mask, 6078c2ecf20Sopenharmony_ci DR_CPU_STAT_UNCONFIGURED); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci for_each_cpu(cpu, mask) { 6108c2ecf20Sopenharmony_ci int err; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci printk(KERN_INFO "ds-%llu: Shutting down cpu %d...\n", 6138c2ecf20Sopenharmony_ci dp->id, cpu); 6148c2ecf20Sopenharmony_ci err = remove_cpu(cpu); 6158c2ecf20Sopenharmony_ci if (err) 6168c2ecf20Sopenharmony_ci dr_cpu_mark(resp, cpu, ncpus, 6178c2ecf20Sopenharmony_ci DR_CPU_RES_FAILURE, 6188c2ecf20Sopenharmony_ci DR_CPU_STAT_CONFIGURED); 6198c2ecf20Sopenharmony_ci } 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci spin_lock_irqsave(&ds_lock, flags); 6228c2ecf20Sopenharmony_ci __ds_send(dp->lp, resp, resp_len); 6238c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ds_lock, flags); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci kfree(resp); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci return 0; 6288c2ecf20Sopenharmony_ci} 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_cistatic void dr_cpu_data(struct ds_info *dp, struct ds_cap_state *cp, void *buf, 6318c2ecf20Sopenharmony_ci int len) 6328c2ecf20Sopenharmony_ci{ 6338c2ecf20Sopenharmony_ci struct ds_data *data = buf; 6348c2ecf20Sopenharmony_ci struct dr_cpu_tag *tag = (struct dr_cpu_tag *) (data + 1); 6358c2ecf20Sopenharmony_ci u32 *cpu_list = (u32 *) (tag + 1); 6368c2ecf20Sopenharmony_ci u64 req_num = tag->req_num; 6378c2ecf20Sopenharmony_ci cpumask_t mask; 6388c2ecf20Sopenharmony_ci unsigned int i; 6398c2ecf20Sopenharmony_ci int err; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci switch (tag->type) { 6428c2ecf20Sopenharmony_ci case DR_CPU_CONFIGURE: 6438c2ecf20Sopenharmony_ci case DR_CPU_UNCONFIGURE: 6448c2ecf20Sopenharmony_ci case DR_CPU_FORCE_UNCONFIGURE: 6458c2ecf20Sopenharmony_ci break; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci default: 6488c2ecf20Sopenharmony_ci dr_cpu_send_error(dp, cp, data); 6498c2ecf20Sopenharmony_ci return; 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci purge_dups(cpu_list, tag->num_records); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci cpumask_clear(&mask); 6558c2ecf20Sopenharmony_ci for (i = 0; i < tag->num_records; i++) { 6568c2ecf20Sopenharmony_ci if (cpu_list[i] == CPU_SENTINEL) 6578c2ecf20Sopenharmony_ci continue; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci if (cpu_list[i] < nr_cpu_ids) 6608c2ecf20Sopenharmony_ci cpumask_set_cpu(cpu_list[i], &mask); 6618c2ecf20Sopenharmony_ci } 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci if (tag->type == DR_CPU_CONFIGURE) 6648c2ecf20Sopenharmony_ci err = dr_cpu_configure(dp, cp, req_num, &mask); 6658c2ecf20Sopenharmony_ci else 6668c2ecf20Sopenharmony_ci err = dr_cpu_unconfigure(dp, cp, req_num, &mask); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci if (err) 6698c2ecf20Sopenharmony_ci dr_cpu_send_error(dp, cp, data); 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ci#endif /* CONFIG_HOTPLUG_CPU */ 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_cistruct ds_pri_msg { 6748c2ecf20Sopenharmony_ci __u64 req_num; 6758c2ecf20Sopenharmony_ci __u64 type; 6768c2ecf20Sopenharmony_ci#define DS_PRI_REQUEST 0x00 6778c2ecf20Sopenharmony_ci#define DS_PRI_DATA 0x01 6788c2ecf20Sopenharmony_ci#define DS_PRI_UPDATE 0x02 6798c2ecf20Sopenharmony_ci}; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_cistatic void ds_pri_data(struct ds_info *dp, 6828c2ecf20Sopenharmony_ci struct ds_cap_state *cp, 6838c2ecf20Sopenharmony_ci void *buf, int len) 6848c2ecf20Sopenharmony_ci{ 6858c2ecf20Sopenharmony_ci struct ds_data *dpkt = buf; 6868c2ecf20Sopenharmony_ci struct ds_pri_msg *rp; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci rp = (struct ds_pri_msg *) (dpkt + 1); 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci printk(KERN_INFO "ds-%llu: PRI REQ [%llx:%llx], len=%d\n", 6918c2ecf20Sopenharmony_ci dp->id, rp->req_num, rp->type, len); 6928c2ecf20Sopenharmony_ci} 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_cistruct ds_var_hdr { 6958c2ecf20Sopenharmony_ci __u32 type; 6968c2ecf20Sopenharmony_ci#define DS_VAR_SET_REQ 0x00 6978c2ecf20Sopenharmony_ci#define DS_VAR_DELETE_REQ 0x01 6988c2ecf20Sopenharmony_ci#define DS_VAR_SET_RESP 0x02 6998c2ecf20Sopenharmony_ci#define DS_VAR_DELETE_RESP 0x03 7008c2ecf20Sopenharmony_ci}; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_cistruct ds_var_set_msg { 7038c2ecf20Sopenharmony_ci struct ds_var_hdr hdr; 7048c2ecf20Sopenharmony_ci char name_and_value[]; 7058c2ecf20Sopenharmony_ci}; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_cistruct ds_var_delete_msg { 7088c2ecf20Sopenharmony_ci struct ds_var_hdr hdr; 7098c2ecf20Sopenharmony_ci char name[]; 7108c2ecf20Sopenharmony_ci}; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_cistruct ds_var_resp { 7138c2ecf20Sopenharmony_ci struct ds_var_hdr hdr; 7148c2ecf20Sopenharmony_ci __u32 result; 7158c2ecf20Sopenharmony_ci#define DS_VAR_SUCCESS 0x00 7168c2ecf20Sopenharmony_ci#define DS_VAR_NO_SPACE 0x01 7178c2ecf20Sopenharmony_ci#define DS_VAR_INVALID_VAR 0x02 7188c2ecf20Sopenharmony_ci#define DS_VAR_INVALID_VAL 0x03 7198c2ecf20Sopenharmony_ci#define DS_VAR_NOT_PRESENT 0x04 7208c2ecf20Sopenharmony_ci}; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(ds_var_mutex); 7238c2ecf20Sopenharmony_cistatic int ds_var_doorbell; 7248c2ecf20Sopenharmony_cistatic int ds_var_response; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_cistatic void ds_var_data(struct ds_info *dp, 7278c2ecf20Sopenharmony_ci struct ds_cap_state *cp, 7288c2ecf20Sopenharmony_ci void *buf, int len) 7298c2ecf20Sopenharmony_ci{ 7308c2ecf20Sopenharmony_ci struct ds_data *dpkt = buf; 7318c2ecf20Sopenharmony_ci struct ds_var_resp *rp; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci rp = (struct ds_var_resp *) (dpkt + 1); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci if (rp->hdr.type != DS_VAR_SET_RESP && 7368c2ecf20Sopenharmony_ci rp->hdr.type != DS_VAR_DELETE_RESP) 7378c2ecf20Sopenharmony_ci return; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci ds_var_response = rp->result; 7408c2ecf20Sopenharmony_ci wmb(); 7418c2ecf20Sopenharmony_ci ds_var_doorbell = 1; 7428c2ecf20Sopenharmony_ci} 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_civoid ldom_set_var(const char *var, const char *value) 7458c2ecf20Sopenharmony_ci{ 7468c2ecf20Sopenharmony_ci struct ds_cap_state *cp; 7478c2ecf20Sopenharmony_ci struct ds_info *dp; 7488c2ecf20Sopenharmony_ci unsigned long flags; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci spin_lock_irqsave(&ds_lock, flags); 7518c2ecf20Sopenharmony_ci cp = NULL; 7528c2ecf20Sopenharmony_ci for (dp = ds_info_list; dp; dp = dp->next) { 7538c2ecf20Sopenharmony_ci struct ds_cap_state *tmp; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci tmp = find_cap_by_string(dp, "var-config"); 7568c2ecf20Sopenharmony_ci if (tmp && tmp->state == CAP_STATE_REGISTERED) { 7578c2ecf20Sopenharmony_ci cp = tmp; 7588c2ecf20Sopenharmony_ci break; 7598c2ecf20Sopenharmony_ci } 7608c2ecf20Sopenharmony_ci } 7618c2ecf20Sopenharmony_ci if (!cp) { 7628c2ecf20Sopenharmony_ci for (dp = ds_info_list; dp; dp = dp->next) { 7638c2ecf20Sopenharmony_ci struct ds_cap_state *tmp; 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci tmp = find_cap_by_string(dp, "var-config-backup"); 7668c2ecf20Sopenharmony_ci if (tmp && tmp->state == CAP_STATE_REGISTERED) { 7678c2ecf20Sopenharmony_ci cp = tmp; 7688c2ecf20Sopenharmony_ci break; 7698c2ecf20Sopenharmony_ci } 7708c2ecf20Sopenharmony_ci } 7718c2ecf20Sopenharmony_ci } 7728c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ds_lock, flags); 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci if (cp) { 7758c2ecf20Sopenharmony_ci union { 7768c2ecf20Sopenharmony_ci struct { 7778c2ecf20Sopenharmony_ci struct ds_data data; 7788c2ecf20Sopenharmony_ci struct ds_var_set_msg msg; 7798c2ecf20Sopenharmony_ci } header; 7808c2ecf20Sopenharmony_ci char all[512]; 7818c2ecf20Sopenharmony_ci } pkt; 7828c2ecf20Sopenharmony_ci char *base, *p; 7838c2ecf20Sopenharmony_ci int msg_len, loops; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci if (strlen(var) + strlen(value) + 2 > 7868c2ecf20Sopenharmony_ci sizeof(pkt) - sizeof(pkt.header)) { 7878c2ecf20Sopenharmony_ci printk(KERN_ERR PFX 7888c2ecf20Sopenharmony_ci "contents length: %zu, which more than max: %lu," 7898c2ecf20Sopenharmony_ci "so could not set (%s) variable to (%s).\n", 7908c2ecf20Sopenharmony_ci strlen(var) + strlen(value) + 2, 7918c2ecf20Sopenharmony_ci sizeof(pkt) - sizeof(pkt.header), var, value); 7928c2ecf20Sopenharmony_ci return; 7938c2ecf20Sopenharmony_ci } 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci memset(&pkt, 0, sizeof(pkt)); 7968c2ecf20Sopenharmony_ci pkt.header.data.tag.type = DS_DATA; 7978c2ecf20Sopenharmony_ci pkt.header.data.handle = cp->handle; 7988c2ecf20Sopenharmony_ci pkt.header.msg.hdr.type = DS_VAR_SET_REQ; 7998c2ecf20Sopenharmony_ci base = p = &pkt.header.msg.name_and_value[0]; 8008c2ecf20Sopenharmony_ci strcpy(p, var); 8018c2ecf20Sopenharmony_ci p += strlen(var) + 1; 8028c2ecf20Sopenharmony_ci strcpy(p, value); 8038c2ecf20Sopenharmony_ci p += strlen(value) + 1; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci msg_len = (sizeof(struct ds_data) + 8068c2ecf20Sopenharmony_ci sizeof(struct ds_var_set_msg) + 8078c2ecf20Sopenharmony_ci (p - base)); 8088c2ecf20Sopenharmony_ci msg_len = (msg_len + 3) & ~3; 8098c2ecf20Sopenharmony_ci pkt.header.data.tag.len = msg_len - sizeof(struct ds_msg_tag); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci mutex_lock(&ds_var_mutex); 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci spin_lock_irqsave(&ds_lock, flags); 8148c2ecf20Sopenharmony_ci ds_var_doorbell = 0; 8158c2ecf20Sopenharmony_ci ds_var_response = -1; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci __ds_send(dp->lp, &pkt, msg_len); 8188c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ds_lock, flags); 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci loops = 1000; 8218c2ecf20Sopenharmony_ci while (ds_var_doorbell == 0) { 8228c2ecf20Sopenharmony_ci if (loops-- < 0) 8238c2ecf20Sopenharmony_ci break; 8248c2ecf20Sopenharmony_ci barrier(); 8258c2ecf20Sopenharmony_ci udelay(100); 8268c2ecf20Sopenharmony_ci } 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci mutex_unlock(&ds_var_mutex); 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci if (ds_var_doorbell == 0 || 8318c2ecf20Sopenharmony_ci ds_var_response != DS_VAR_SUCCESS) 8328c2ecf20Sopenharmony_ci printk(KERN_ERR "ds-%llu: var-config [%s:%s] " 8338c2ecf20Sopenharmony_ci "failed, response(%d).\n", 8348c2ecf20Sopenharmony_ci dp->id, var, value, 8358c2ecf20Sopenharmony_ci ds_var_response); 8368c2ecf20Sopenharmony_ci } else { 8378c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "var-config not registered so " 8388c2ecf20Sopenharmony_ci "could not set (%s) variable to (%s).\n", 8398c2ecf20Sopenharmony_ci var, value); 8408c2ecf20Sopenharmony_ci } 8418c2ecf20Sopenharmony_ci} 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_cistatic char full_boot_str[256] __attribute__((aligned(32))); 8448c2ecf20Sopenharmony_cistatic int reboot_data_supported; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_civoid ldom_reboot(const char *boot_command) 8478c2ecf20Sopenharmony_ci{ 8488c2ecf20Sopenharmony_ci /* Don't bother with any of this if the boot_command 8498c2ecf20Sopenharmony_ci * is empty. 8508c2ecf20Sopenharmony_ci */ 8518c2ecf20Sopenharmony_ci if (boot_command && strlen(boot_command)) { 8528c2ecf20Sopenharmony_ci unsigned long len; 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci snprintf(full_boot_str, sizeof(full_boot_str), "boot %s", 8558c2ecf20Sopenharmony_ci boot_command); 8568c2ecf20Sopenharmony_ci len = strlen(full_boot_str); 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci if (reboot_data_supported) { 8598c2ecf20Sopenharmony_ci unsigned long ra = kimage_addr_to_ra(full_boot_str); 8608c2ecf20Sopenharmony_ci unsigned long hv_ret; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci hv_ret = sun4v_reboot_data_set(ra, len); 8638c2ecf20Sopenharmony_ci if (hv_ret != HV_EOK) 8648c2ecf20Sopenharmony_ci pr_err("SUN4V: Unable to set reboot data " 8658c2ecf20Sopenharmony_ci "hv_ret=%lu\n", hv_ret); 8668c2ecf20Sopenharmony_ci } else { 8678c2ecf20Sopenharmony_ci ldom_set_var("reboot-command", full_boot_str); 8688c2ecf20Sopenharmony_ci } 8698c2ecf20Sopenharmony_ci } 8708c2ecf20Sopenharmony_ci sun4v_mach_sir(); 8718c2ecf20Sopenharmony_ci} 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_civoid ldom_power_off(void) 8748c2ecf20Sopenharmony_ci{ 8758c2ecf20Sopenharmony_ci sun4v_mach_exit(0); 8768c2ecf20Sopenharmony_ci} 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_cistatic void ds_conn_reset(struct ds_info *dp) 8798c2ecf20Sopenharmony_ci{ 8808c2ecf20Sopenharmony_ci printk(KERN_ERR "ds-%llu: ds_conn_reset() from %ps\n", 8818c2ecf20Sopenharmony_ci dp->id, __builtin_return_address(0)); 8828c2ecf20Sopenharmony_ci} 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_cistatic int register_services(struct ds_info *dp) 8858c2ecf20Sopenharmony_ci{ 8868c2ecf20Sopenharmony_ci struct ldc_channel *lp = dp->lp; 8878c2ecf20Sopenharmony_ci int i; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci for (i = 0; i < dp->num_ds_states; i++) { 8908c2ecf20Sopenharmony_ci struct { 8918c2ecf20Sopenharmony_ci struct ds_reg_req req; 8928c2ecf20Sopenharmony_ci u8 id_buf[256]; 8938c2ecf20Sopenharmony_ci } pbuf; 8948c2ecf20Sopenharmony_ci struct ds_cap_state *cp = &dp->ds_states[i]; 8958c2ecf20Sopenharmony_ci int err, msg_len; 8968c2ecf20Sopenharmony_ci u64 new_count; 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci if (cp->state == CAP_STATE_REGISTERED) 8998c2ecf20Sopenharmony_ci continue; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci new_count = sched_clock() & 0xffffffff; 9028c2ecf20Sopenharmony_ci cp->handle = ((u64) i << 32) | new_count; 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci msg_len = (sizeof(struct ds_reg_req) + 9058c2ecf20Sopenharmony_ci strlen(cp->service_id)); 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci memset(&pbuf, 0, sizeof(pbuf)); 9088c2ecf20Sopenharmony_ci pbuf.req.tag.type = DS_REG_REQ; 9098c2ecf20Sopenharmony_ci pbuf.req.tag.len = (msg_len - sizeof(struct ds_msg_tag)); 9108c2ecf20Sopenharmony_ci pbuf.req.handle = cp->handle; 9118c2ecf20Sopenharmony_ci pbuf.req.major = 1; 9128c2ecf20Sopenharmony_ci pbuf.req.minor = 0; 9138c2ecf20Sopenharmony_ci strcpy(pbuf.id_buf, cp->service_id); 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci err = __ds_send(lp, &pbuf, msg_len); 9168c2ecf20Sopenharmony_ci if (err > 0) 9178c2ecf20Sopenharmony_ci cp->state = CAP_STATE_REG_SENT; 9188c2ecf20Sopenharmony_ci } 9198c2ecf20Sopenharmony_ci return 0; 9208c2ecf20Sopenharmony_ci} 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_cistatic int ds_handshake(struct ds_info *dp, struct ds_msg_tag *pkt) 9238c2ecf20Sopenharmony_ci{ 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci if (dp->hs_state == DS_HS_START) { 9268c2ecf20Sopenharmony_ci if (pkt->type != DS_INIT_ACK) 9278c2ecf20Sopenharmony_ci goto conn_reset; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci dp->hs_state = DS_HS_DONE; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci return register_services(dp); 9328c2ecf20Sopenharmony_ci } 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci if (dp->hs_state != DS_HS_DONE) 9358c2ecf20Sopenharmony_ci goto conn_reset; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci if (pkt->type == DS_REG_ACK) { 9388c2ecf20Sopenharmony_ci struct ds_reg_ack *ap = (struct ds_reg_ack *) pkt; 9398c2ecf20Sopenharmony_ci struct ds_cap_state *cp = find_cap(dp, ap->handle); 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci if (!cp) { 9428c2ecf20Sopenharmony_ci printk(KERN_ERR "ds-%llu: REG ACK for unknown " 9438c2ecf20Sopenharmony_ci "handle %llx\n", dp->id, ap->handle); 9448c2ecf20Sopenharmony_ci return 0; 9458c2ecf20Sopenharmony_ci } 9468c2ecf20Sopenharmony_ci printk(KERN_INFO "ds-%llu: Registered %s service.\n", 9478c2ecf20Sopenharmony_ci dp->id, cp->service_id); 9488c2ecf20Sopenharmony_ci cp->state = CAP_STATE_REGISTERED; 9498c2ecf20Sopenharmony_ci } else if (pkt->type == DS_REG_NACK) { 9508c2ecf20Sopenharmony_ci struct ds_reg_nack *np = (struct ds_reg_nack *) pkt; 9518c2ecf20Sopenharmony_ci struct ds_cap_state *cp = find_cap(dp, np->handle); 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci if (!cp) { 9548c2ecf20Sopenharmony_ci printk(KERN_ERR "ds-%llu: REG NACK for " 9558c2ecf20Sopenharmony_ci "unknown handle %llx\n", 9568c2ecf20Sopenharmony_ci dp->id, np->handle); 9578c2ecf20Sopenharmony_ci return 0; 9588c2ecf20Sopenharmony_ci } 9598c2ecf20Sopenharmony_ci cp->state = CAP_STATE_UNKNOWN; 9608c2ecf20Sopenharmony_ci } 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci return 0; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ciconn_reset: 9658c2ecf20Sopenharmony_ci ds_conn_reset(dp); 9668c2ecf20Sopenharmony_ci return -ECONNRESET; 9678c2ecf20Sopenharmony_ci} 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_cistatic void __send_ds_nack(struct ds_info *dp, u64 handle) 9708c2ecf20Sopenharmony_ci{ 9718c2ecf20Sopenharmony_ci struct ds_data_nack nack = { 9728c2ecf20Sopenharmony_ci .tag = { 9738c2ecf20Sopenharmony_ci .type = DS_NACK, 9748c2ecf20Sopenharmony_ci .len = (sizeof(struct ds_data_nack) - 9758c2ecf20Sopenharmony_ci sizeof(struct ds_msg_tag)), 9768c2ecf20Sopenharmony_ci }, 9778c2ecf20Sopenharmony_ci .handle = handle, 9788c2ecf20Sopenharmony_ci .result = DS_INV_HDL, 9798c2ecf20Sopenharmony_ci }; 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci __ds_send(dp->lp, &nack, sizeof(nack)); 9828c2ecf20Sopenharmony_ci} 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_cistatic LIST_HEAD(ds_work_list); 9858c2ecf20Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(ds_wait); 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_cistruct ds_queue_entry { 9888c2ecf20Sopenharmony_ci struct list_head list; 9898c2ecf20Sopenharmony_ci struct ds_info *dp; 9908c2ecf20Sopenharmony_ci int req_len; 9918c2ecf20Sopenharmony_ci int __pad; 9928c2ecf20Sopenharmony_ci u64 req[]; 9938c2ecf20Sopenharmony_ci}; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_cistatic void process_ds_work(void) 9968c2ecf20Sopenharmony_ci{ 9978c2ecf20Sopenharmony_ci struct ds_queue_entry *qp, *tmp; 9988c2ecf20Sopenharmony_ci unsigned long flags; 9998c2ecf20Sopenharmony_ci LIST_HEAD(todo); 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci spin_lock_irqsave(&ds_lock, flags); 10028c2ecf20Sopenharmony_ci list_splice_init(&ds_work_list, &todo); 10038c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ds_lock, flags); 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci list_for_each_entry_safe(qp, tmp, &todo, list) { 10068c2ecf20Sopenharmony_ci struct ds_data *dpkt = (struct ds_data *) qp->req; 10078c2ecf20Sopenharmony_ci struct ds_info *dp = qp->dp; 10088c2ecf20Sopenharmony_ci struct ds_cap_state *cp = find_cap(dp, dpkt->handle); 10098c2ecf20Sopenharmony_ci int req_len = qp->req_len; 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci if (!cp) { 10128c2ecf20Sopenharmony_ci printk(KERN_ERR "ds-%llu: Data for unknown " 10138c2ecf20Sopenharmony_ci "handle %llu\n", 10148c2ecf20Sopenharmony_ci dp->id, dpkt->handle); 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci spin_lock_irqsave(&ds_lock, flags); 10178c2ecf20Sopenharmony_ci __send_ds_nack(dp, dpkt->handle); 10188c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ds_lock, flags); 10198c2ecf20Sopenharmony_ci } else { 10208c2ecf20Sopenharmony_ci cp->data(dp, cp, dpkt, req_len); 10218c2ecf20Sopenharmony_ci } 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci list_del(&qp->list); 10248c2ecf20Sopenharmony_ci kfree(qp); 10258c2ecf20Sopenharmony_ci } 10268c2ecf20Sopenharmony_ci} 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_cistatic int ds_thread(void *__unused) 10298c2ecf20Sopenharmony_ci{ 10308c2ecf20Sopenharmony_ci DEFINE_WAIT(wait); 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci while (1) { 10338c2ecf20Sopenharmony_ci prepare_to_wait(&ds_wait, &wait, TASK_INTERRUPTIBLE); 10348c2ecf20Sopenharmony_ci if (list_empty(&ds_work_list)) 10358c2ecf20Sopenharmony_ci schedule(); 10368c2ecf20Sopenharmony_ci finish_wait(&ds_wait, &wait); 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci if (kthread_should_stop()) 10398c2ecf20Sopenharmony_ci break; 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci process_ds_work(); 10428c2ecf20Sopenharmony_ci } 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci return 0; 10458c2ecf20Sopenharmony_ci} 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_cistatic int ds_data(struct ds_info *dp, struct ds_msg_tag *pkt, int len) 10488c2ecf20Sopenharmony_ci{ 10498c2ecf20Sopenharmony_ci struct ds_data *dpkt = (struct ds_data *) pkt; 10508c2ecf20Sopenharmony_ci struct ds_queue_entry *qp; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci qp = kmalloc(sizeof(struct ds_queue_entry) + len, GFP_ATOMIC); 10538c2ecf20Sopenharmony_ci if (!qp) { 10548c2ecf20Sopenharmony_ci __send_ds_nack(dp, dpkt->handle); 10558c2ecf20Sopenharmony_ci } else { 10568c2ecf20Sopenharmony_ci qp->dp = dp; 10578c2ecf20Sopenharmony_ci memcpy(&qp->req, pkt, len); 10588c2ecf20Sopenharmony_ci list_add_tail(&qp->list, &ds_work_list); 10598c2ecf20Sopenharmony_ci wake_up(&ds_wait); 10608c2ecf20Sopenharmony_ci } 10618c2ecf20Sopenharmony_ci return 0; 10628c2ecf20Sopenharmony_ci} 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_cistatic void ds_up(struct ds_info *dp) 10658c2ecf20Sopenharmony_ci{ 10668c2ecf20Sopenharmony_ci struct ldc_channel *lp = dp->lp; 10678c2ecf20Sopenharmony_ci struct ds_ver_req req; 10688c2ecf20Sopenharmony_ci int err; 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci req.tag.type = DS_INIT_REQ; 10718c2ecf20Sopenharmony_ci req.tag.len = sizeof(req) - sizeof(struct ds_msg_tag); 10728c2ecf20Sopenharmony_ci req.ver.major = 1; 10738c2ecf20Sopenharmony_ci req.ver.minor = 0; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci err = __ds_send(lp, &req, sizeof(req)); 10768c2ecf20Sopenharmony_ci if (err > 0) 10778c2ecf20Sopenharmony_ci dp->hs_state = DS_HS_START; 10788c2ecf20Sopenharmony_ci} 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_cistatic void ds_reset(struct ds_info *dp) 10818c2ecf20Sopenharmony_ci{ 10828c2ecf20Sopenharmony_ci int i; 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci dp->hs_state = 0; 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci for (i = 0; i < dp->num_ds_states; i++) { 10878c2ecf20Sopenharmony_ci struct ds_cap_state *cp = &dp->ds_states[i]; 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci cp->state = CAP_STATE_UNKNOWN; 10908c2ecf20Sopenharmony_ci } 10918c2ecf20Sopenharmony_ci} 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_cistatic void ds_event(void *arg, int event) 10948c2ecf20Sopenharmony_ci{ 10958c2ecf20Sopenharmony_ci struct ds_info *dp = arg; 10968c2ecf20Sopenharmony_ci struct ldc_channel *lp = dp->lp; 10978c2ecf20Sopenharmony_ci unsigned long flags; 10988c2ecf20Sopenharmony_ci int err; 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci spin_lock_irqsave(&ds_lock, flags); 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci if (event == LDC_EVENT_UP) { 11038c2ecf20Sopenharmony_ci ds_up(dp); 11048c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ds_lock, flags); 11058c2ecf20Sopenharmony_ci return; 11068c2ecf20Sopenharmony_ci } 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci if (event == LDC_EVENT_RESET) { 11098c2ecf20Sopenharmony_ci ds_reset(dp); 11108c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ds_lock, flags); 11118c2ecf20Sopenharmony_ci return; 11128c2ecf20Sopenharmony_ci } 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci if (event != LDC_EVENT_DATA_READY) { 11158c2ecf20Sopenharmony_ci printk(KERN_WARNING "ds-%llu: Unexpected LDC event %d\n", 11168c2ecf20Sopenharmony_ci dp->id, event); 11178c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ds_lock, flags); 11188c2ecf20Sopenharmony_ci return; 11198c2ecf20Sopenharmony_ci } 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci err = 0; 11228c2ecf20Sopenharmony_ci while (1) { 11238c2ecf20Sopenharmony_ci struct ds_msg_tag *tag; 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci err = ldc_read(lp, dp->rcv_buf, sizeof(*tag)); 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci if (unlikely(err < 0)) { 11288c2ecf20Sopenharmony_ci if (err == -ECONNRESET) 11298c2ecf20Sopenharmony_ci ds_conn_reset(dp); 11308c2ecf20Sopenharmony_ci break; 11318c2ecf20Sopenharmony_ci } 11328c2ecf20Sopenharmony_ci if (err == 0) 11338c2ecf20Sopenharmony_ci break; 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci tag = dp->rcv_buf; 11368c2ecf20Sopenharmony_ci err = ldc_read(lp, tag + 1, tag->len); 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci if (unlikely(err < 0)) { 11398c2ecf20Sopenharmony_ci if (err == -ECONNRESET) 11408c2ecf20Sopenharmony_ci ds_conn_reset(dp); 11418c2ecf20Sopenharmony_ci break; 11428c2ecf20Sopenharmony_ci } 11438c2ecf20Sopenharmony_ci if (err < tag->len) 11448c2ecf20Sopenharmony_ci break; 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci if (tag->type < DS_DATA) 11478c2ecf20Sopenharmony_ci err = ds_handshake(dp, dp->rcv_buf); 11488c2ecf20Sopenharmony_ci else 11498c2ecf20Sopenharmony_ci err = ds_data(dp, dp->rcv_buf, 11508c2ecf20Sopenharmony_ci sizeof(*tag) + err); 11518c2ecf20Sopenharmony_ci if (err == -ECONNRESET) 11528c2ecf20Sopenharmony_ci break; 11538c2ecf20Sopenharmony_ci } 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ds_lock, flags); 11568c2ecf20Sopenharmony_ci} 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_cistatic int ds_probe(struct vio_dev *vdev, const struct vio_device_id *id) 11598c2ecf20Sopenharmony_ci{ 11608c2ecf20Sopenharmony_ci static int ds_version_printed; 11618c2ecf20Sopenharmony_ci struct ldc_channel_config ds_cfg = { 11628c2ecf20Sopenharmony_ci .event = ds_event, 11638c2ecf20Sopenharmony_ci .mtu = 4096, 11648c2ecf20Sopenharmony_ci .mode = LDC_MODE_STREAM, 11658c2ecf20Sopenharmony_ci }; 11668c2ecf20Sopenharmony_ci struct mdesc_handle *hp; 11678c2ecf20Sopenharmony_ci struct ldc_channel *lp; 11688c2ecf20Sopenharmony_ci struct ds_info *dp; 11698c2ecf20Sopenharmony_ci const u64 *val; 11708c2ecf20Sopenharmony_ci int err, i; 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci if (ds_version_printed++ == 0) 11738c2ecf20Sopenharmony_ci printk(KERN_INFO "%s", version); 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci dp = kzalloc(sizeof(*dp), GFP_KERNEL); 11768c2ecf20Sopenharmony_ci err = -ENOMEM; 11778c2ecf20Sopenharmony_ci if (!dp) 11788c2ecf20Sopenharmony_ci goto out_err; 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci hp = mdesc_grab(); 11818c2ecf20Sopenharmony_ci val = mdesc_get_property(hp, vdev->mp, "id", NULL); 11828c2ecf20Sopenharmony_ci if (val) 11838c2ecf20Sopenharmony_ci dp->id = *val; 11848c2ecf20Sopenharmony_ci mdesc_release(hp); 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci dp->rcv_buf = kzalloc(4096, GFP_KERNEL); 11878c2ecf20Sopenharmony_ci if (!dp->rcv_buf) 11888c2ecf20Sopenharmony_ci goto out_free_dp; 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci dp->rcv_buf_len = 4096; 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci dp->ds_states = kmemdup(ds_states_template, 11938c2ecf20Sopenharmony_ci sizeof(ds_states_template), GFP_KERNEL); 11948c2ecf20Sopenharmony_ci if (!dp->ds_states) 11958c2ecf20Sopenharmony_ci goto out_free_rcv_buf; 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci dp->num_ds_states = ARRAY_SIZE(ds_states_template); 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci for (i = 0; i < dp->num_ds_states; i++) 12008c2ecf20Sopenharmony_ci dp->ds_states[i].handle = ((u64)i << 32); 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci ds_cfg.tx_irq = vdev->tx_irq; 12038c2ecf20Sopenharmony_ci ds_cfg.rx_irq = vdev->rx_irq; 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci lp = ldc_alloc(vdev->channel_id, &ds_cfg, dp, "DS"); 12068c2ecf20Sopenharmony_ci if (IS_ERR(lp)) { 12078c2ecf20Sopenharmony_ci err = PTR_ERR(lp); 12088c2ecf20Sopenharmony_ci goto out_free_ds_states; 12098c2ecf20Sopenharmony_ci } 12108c2ecf20Sopenharmony_ci dp->lp = lp; 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci err = ldc_bind(lp); 12138c2ecf20Sopenharmony_ci if (err) 12148c2ecf20Sopenharmony_ci goto out_free_ldc; 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci spin_lock_irq(&ds_lock); 12178c2ecf20Sopenharmony_ci dp->next = ds_info_list; 12188c2ecf20Sopenharmony_ci ds_info_list = dp; 12198c2ecf20Sopenharmony_ci spin_unlock_irq(&ds_lock); 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci return err; 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ciout_free_ldc: 12248c2ecf20Sopenharmony_ci ldc_free(dp->lp); 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ciout_free_ds_states: 12278c2ecf20Sopenharmony_ci kfree(dp->ds_states); 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ciout_free_rcv_buf: 12308c2ecf20Sopenharmony_ci kfree(dp->rcv_buf); 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ciout_free_dp: 12338c2ecf20Sopenharmony_ci kfree(dp); 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ciout_err: 12368c2ecf20Sopenharmony_ci return err; 12378c2ecf20Sopenharmony_ci} 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_cistatic int ds_remove(struct vio_dev *vdev) 12408c2ecf20Sopenharmony_ci{ 12418c2ecf20Sopenharmony_ci return 0; 12428c2ecf20Sopenharmony_ci} 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_cistatic const struct vio_device_id ds_match[] = { 12458c2ecf20Sopenharmony_ci { 12468c2ecf20Sopenharmony_ci .type = "domain-services-port", 12478c2ecf20Sopenharmony_ci }, 12488c2ecf20Sopenharmony_ci {}, 12498c2ecf20Sopenharmony_ci}; 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_cistatic struct vio_driver ds_driver = { 12528c2ecf20Sopenharmony_ci .id_table = ds_match, 12538c2ecf20Sopenharmony_ci .probe = ds_probe, 12548c2ecf20Sopenharmony_ci .remove = ds_remove, 12558c2ecf20Sopenharmony_ci .name = "ds", 12568c2ecf20Sopenharmony_ci}; 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_cistatic int __init ds_init(void) 12598c2ecf20Sopenharmony_ci{ 12608c2ecf20Sopenharmony_ci unsigned long hv_ret, major, minor; 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci if (tlb_type == hypervisor) { 12638c2ecf20Sopenharmony_ci hv_ret = sun4v_get_version(HV_GRP_REBOOT_DATA, &major, &minor); 12648c2ecf20Sopenharmony_ci if (hv_ret == HV_EOK) { 12658c2ecf20Sopenharmony_ci pr_info("SUN4V: Reboot data supported (maj=%lu,min=%lu).\n", 12668c2ecf20Sopenharmony_ci major, minor); 12678c2ecf20Sopenharmony_ci reboot_data_supported = 1; 12688c2ecf20Sopenharmony_ci } 12698c2ecf20Sopenharmony_ci } 12708c2ecf20Sopenharmony_ci kthread_run(ds_thread, NULL, "kldomd"); 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci return vio_register_driver(&ds_driver); 12738c2ecf20Sopenharmony_ci} 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_cifs_initcall(ds_init); 1276