blob: 9c8839d1cffd4f62f98dff2cac7e1fa82d0a6e37 [file] [log] [blame]
David S. Millere4509922007-07-11 18:51:31 -07001/* ds.c: Domain Services driver for Logical Domains
2 *
3 * Copyright (C) 2007 David S. Miller <davem@davemloft.net>
4 */
5
6#include <linux/kernel.h>
7#include <linux/module.h>
8#include <linux/types.h>
9#include <linux/module.h>
10#include <linux/string.h>
11#include <linux/slab.h>
12#include <linux/sched.h>
13#include <linux/delay.h>
14
15#include <asm/ldc.h>
16#include <asm/vio.h>
17#include <asm/power.h>
David S. Miller43fdf272007-07-12 13:47:50 -070018#include <asm/mdesc.h>
David S. Millere4509922007-07-11 18:51:31 -070019
20#define DRV_MODULE_NAME "ds"
21#define PFX DRV_MODULE_NAME ": "
22#define DRV_MODULE_VERSION "1.0"
23#define DRV_MODULE_RELDATE "Jul 11, 2007"
24
25static char version[] __devinitdata =
26 DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
27MODULE_AUTHOR("David S. Miller (davem@davemloft.net)");
28MODULE_DESCRIPTION("Sun LDOM domain services driver");
29MODULE_LICENSE("GPL");
30MODULE_VERSION(DRV_MODULE_VERSION);
31
32struct ds_msg_tag {
33 __u32 type;
34#define DS_INIT_REQ 0x00
35#define DS_INIT_ACK 0x01
36#define DS_INIT_NACK 0x02
37#define DS_REG_REQ 0x03
38#define DS_REG_ACK 0x04
39#define DS_REG_NACK 0x05
40#define DS_UNREG_REQ 0x06
41#define DS_UNREG_ACK 0x07
42#define DS_UNREG_NACK 0x08
43#define DS_DATA 0x09
44#define DS_NACK 0x0a
45
46 __u32 len;
47};
48
49/* Result codes */
50#define DS_OK 0x00
51#define DS_REG_VER_NACK 0x01
52#define DS_REG_DUP 0x02
53#define DS_INV_HDL 0x03
54#define DS_TYPE_UNKNOWN 0x04
55
56struct ds_version {
57 __u16 major;
58 __u16 minor;
59};
60
61struct ds_ver_req {
62 struct ds_msg_tag tag;
63 struct ds_version ver;
64};
65
66struct ds_ver_ack {
67 struct ds_msg_tag tag;
68 __u16 minor;
69};
70
71struct ds_ver_nack {
72 struct ds_msg_tag tag;
73 __u16 major;
74};
75
76struct ds_reg_req {
77 struct ds_msg_tag tag;
78 __u64 handle;
79 __u16 major;
80 __u16 minor;
81 char svc_id[0];
82};
83
84struct ds_reg_ack {
85 struct ds_msg_tag tag;
86 __u64 handle;
87 __u16 minor;
88};
89
90struct ds_reg_nack {
91 struct ds_msg_tag tag;
92 __u64 handle;
93 __u16 major;
94};
95
96struct ds_unreg_req {
97 struct ds_msg_tag tag;
98 __u64 handle;
99};
100
101struct ds_unreg_ack {
102 struct ds_msg_tag tag;
103 __u64 handle;
104};
105
106struct ds_unreg_nack {
107 struct ds_msg_tag tag;
108 __u64 handle;
109};
110
111struct ds_data {
112 struct ds_msg_tag tag;
113 __u64 handle;
114};
115
116struct ds_data_nack {
117 struct ds_msg_tag tag;
118 __u64 handle;
119 __u64 result;
120};
121
122struct ds_cap_state {
123 __u64 handle;
124
125 void (*data)(struct ldc_channel *lp,
126 struct ds_cap_state *dp,
127 void *buf, int len);
128
129 const char *service_id;
130
131 u8 state;
132#define CAP_STATE_UNKNOWN 0x00
133#define CAP_STATE_REG_SENT 0x01
134#define CAP_STATE_REGISTERED 0x02
135};
136
137static int ds_send(struct ldc_channel *lp, void *data, int len)
138{
139 int err, limit = 1000;
140
141 err = -EINVAL;
142 while (limit-- > 0) {
143 err = ldc_write(lp, data, len);
144 if (!err || (err != -EAGAIN))
145 break;
146 udelay(1);
147 }
148
149 return err;
150}
151
152struct ds_md_update_req {
153 __u64 req_num;
154};
155
156struct ds_md_update_res {
157 __u64 req_num;
158 __u32 result;
159};
160
161static void md_update_data(struct ldc_channel *lp,
162 struct ds_cap_state *dp,
163 void *buf, int len)
164{
165 struct ds_data *dpkt = buf;
166 struct ds_md_update_req *rp;
167 struct {
168 struct ds_data data;
169 struct ds_md_update_res res;
170 } pkt;
171
172 rp = (struct ds_md_update_req *) (dpkt + 1);
173
David S. Miller43fdf272007-07-12 13:47:50 -0700174 printk(KERN_ERR PFX "Machine description update.\n");
David S. Millere4509922007-07-11 18:51:31 -0700175
176 memset(&pkt, 0, sizeof(pkt));
177 pkt.data.tag.type = DS_DATA;
178 pkt.data.tag.len = sizeof(pkt) - sizeof(struct ds_msg_tag);
179 pkt.data.handle = dp->handle;
180 pkt.res.req_num = rp->req_num;
181 pkt.res.result = DS_OK;
182
183 ds_send(lp, &pkt, sizeof(pkt));
David S. Miller43fdf272007-07-12 13:47:50 -0700184
185 mdesc_update();
David S. Millere4509922007-07-11 18:51:31 -0700186}
187
188struct ds_shutdown_req {
189 __u64 req_num;
190 __u32 ms_delay;
191};
192
193struct ds_shutdown_res {
194 __u64 req_num;
195 __u32 result;
196 char reason[1];
197};
198
199static void domain_shutdown_data(struct ldc_channel *lp,
200 struct ds_cap_state *dp,
201 void *buf, int len)
202{
203 struct ds_data *dpkt = buf;
204 struct ds_shutdown_req *rp;
205 struct {
206 struct ds_data data;
207 struct ds_shutdown_res res;
208 } pkt;
209
210 rp = (struct ds_shutdown_req *) (dpkt + 1);
211
212 printk(KERN_ALERT PFX "Shutdown request from "
213 "LDOM manager received.\n");
214
215 memset(&pkt, 0, sizeof(pkt));
216 pkt.data.tag.type = DS_DATA;
217 pkt.data.tag.len = sizeof(pkt) - sizeof(struct ds_msg_tag);
218 pkt.data.handle = dp->handle;
219 pkt.res.req_num = rp->req_num;
220 pkt.res.result = DS_OK;
221 pkt.res.reason[0] = 0;
222
223 ds_send(lp, &pkt, sizeof(pkt));
224
225 wake_up_powerd();
226}
227
228struct ds_panic_req {
229 __u64 req_num;
230};
231
232struct ds_panic_res {
233 __u64 req_num;
234 __u32 result;
235 char reason[1];
236};
237
238static void domain_panic_data(struct ldc_channel *lp,
239 struct ds_cap_state *dp,
240 void *buf, int len)
241{
242 struct ds_data *dpkt = buf;
243 struct ds_panic_req *rp;
244 struct {
245 struct ds_data data;
246 struct ds_panic_res res;
247 } pkt;
248
249 rp = (struct ds_panic_req *) (dpkt + 1);
250
251 printk(KERN_ERR PFX "Panic REQ [%lx], len=%d\n",
252 rp->req_num, len);
253
254 memset(&pkt, 0, sizeof(pkt));
255 pkt.data.tag.type = DS_DATA;
256 pkt.data.tag.len = sizeof(pkt) - sizeof(struct ds_msg_tag);
257 pkt.data.handle = dp->handle;
258 pkt.res.req_num = rp->req_num;
259 pkt.res.result = DS_OK;
260 pkt.res.reason[0] = 0;
261
262 ds_send(lp, &pkt, sizeof(pkt));
263
264 panic("PANIC requested by LDOM manager.");
265}
266
267struct ds_cpu_tag {
268 __u64 req_num;
269 __u32 type;
270#define DS_CPU_CONFIGURE 0x43
271#define DS_CPU_UNCONFIGURE 0x55
272#define DS_CPU_FORCE_UNCONFIGURE 0x46
273#define DS_CPU_STATUS 0x53
274
275/* Responses */
276#define DS_CPU_OK 0x6f
277#define DS_CPU_ERROR 0x65
278
279 __u32 num_records;
280};
281
282struct ds_cpu_record {
283 __u32 cpu_id;
284};
285
286static void dr_cpu_data(struct ldc_channel *lp,
287 struct ds_cap_state *dp,
288 void *buf, int len)
289{
290 struct ds_data *dpkt = buf;
291 struct ds_cpu_tag *rp;
292
293 rp = (struct ds_cpu_tag *) (dpkt + 1);
294
295 printk(KERN_ERR PFX "CPU REQ [%lx:%x], len=%d\n",
296 rp->req_num, rp->type, len);
297}
298
299struct ds_pri_msg {
300 __u64 req_num;
301 __u64 type;
302#define DS_PRI_REQUEST 0x00
303#define DS_PRI_DATA 0x01
304#define DS_PRI_UPDATE 0x02
305};
306
307static void ds_pri_data(struct ldc_channel *lp,
308 struct ds_cap_state *dp,
309 void *buf, int len)
310{
311 struct ds_data *dpkt = buf;
312 struct ds_pri_msg *rp;
313
314 rp = (struct ds_pri_msg *) (dpkt + 1);
315
316 printk(KERN_ERR PFX "PRI REQ [%lx:%lx], len=%d\n",
317 rp->req_num, rp->type, len);
318}
319
320struct ds_cap_state ds_states[] = {
321 {
322 .service_id = "md-update",
323 .data = md_update_data,
324 },
325 {
326 .service_id = "domain-shutdown",
327 .data = domain_shutdown_data,
328 },
329 {
330 .service_id = "domain-panic",
331 .data = domain_panic_data,
332 },
333 {
334 .service_id = "dr-cpu",
335 .data = dr_cpu_data,
336 },
337 {
338 .service_id = "pri",
339 .data = ds_pri_data,
340 },
341};
342
343static struct ds_cap_state *find_cap(u64 handle)
344{
345 unsigned int index = handle >> 32;
346
347 if (index >= ARRAY_SIZE(ds_states))
348 return NULL;
349 return &ds_states[index];
350}
351
352static DEFINE_SPINLOCK(ds_lock);
353
354struct ds_info {
355 struct ldc_channel *lp;
356 u8 hs_state;
357#define DS_HS_START 0x01
358#define DS_HS_DONE 0x02
359
360 void *rcv_buf;
361 int rcv_buf_len;
362};
363
364static void ds_conn_reset(struct ds_info *dp)
365{
366 printk(KERN_ERR PFX "ds_conn_reset() from %p\n",
367 __builtin_return_address(0));
368}
369
370static int register_services(struct ds_info *dp)
371{
372 struct ldc_channel *lp = dp->lp;
373 int i;
374
375 for (i = 0; i < ARRAY_SIZE(ds_states); i++) {
376 struct {
377 struct ds_reg_req req;
378 u8 id_buf[256];
379 } pbuf;
380 struct ds_cap_state *cp = &ds_states[i];
381 int err, msg_len;
382 u64 new_count;
383
384 if (cp->state == CAP_STATE_REGISTERED)
385 continue;
386
387 new_count = sched_clock() & 0xffffffff;
388 cp->handle = ((u64) i << 32) | new_count;
389
390 msg_len = (sizeof(struct ds_reg_req) +
391 strlen(cp->service_id));
392
393 memset(&pbuf, 0, sizeof(pbuf));
394 pbuf.req.tag.type = DS_REG_REQ;
395 pbuf.req.tag.len = (msg_len - sizeof(struct ds_msg_tag));
396 pbuf.req.handle = cp->handle;
397 pbuf.req.major = 1;
398 pbuf.req.minor = 0;
399 strcpy(pbuf.req.svc_id, cp->service_id);
400
401 err = ds_send(lp, &pbuf, msg_len);
402 if (err > 0)
403 cp->state = CAP_STATE_REG_SENT;
404 }
405 return 0;
406}
407
408static int ds_handshake(struct ds_info *dp, struct ds_msg_tag *pkt)
409{
410
411 if (dp->hs_state == DS_HS_START) {
412 if (pkt->type != DS_INIT_ACK)
413 goto conn_reset;
414
415 dp->hs_state = DS_HS_DONE;
416
417 return register_services(dp);
418 }
419
420 if (dp->hs_state != DS_HS_DONE)
421 goto conn_reset;
422
423 if (pkt->type == DS_REG_ACK) {
424 struct ds_reg_ack *ap = (struct ds_reg_ack *) pkt;
425 struct ds_cap_state *cp = find_cap(ap->handle);
426
427 if (!cp) {
428 printk(KERN_ERR PFX "REG ACK for unknown handle %lx\n",
429 ap->handle);
430 return 0;
431 }
432 printk(KERN_INFO PFX "Registered %s service.\n",
433 cp->service_id);
434 cp->state = CAP_STATE_REGISTERED;
435 } else if (pkt->type == DS_REG_NACK) {
436 struct ds_reg_nack *np = (struct ds_reg_nack *) pkt;
437 struct ds_cap_state *cp = find_cap(np->handle);
438
439 if (!cp) {
440 printk(KERN_ERR PFX "REG NACK for "
441 "unknown handle %lx\n",
442 np->handle);
443 return 0;
444 }
445 printk(KERN_ERR PFX "Could not register %s service\n",
446 cp->service_id);
447 cp->state = CAP_STATE_UNKNOWN;
448 }
449
450 return 0;
451
452conn_reset:
453 ds_conn_reset(dp);
454 return -ECONNRESET;
455}
456
457static int ds_data(struct ds_info *dp, struct ds_msg_tag *pkt, int len)
458{
459 struct ds_data *dpkt = (struct ds_data *) pkt;
460 struct ds_cap_state *cp = find_cap(dpkt->handle);
461
462 if (!cp) {
463 struct ds_data_nack nack = {
464 .tag = {
465 .type = DS_NACK,
466 .len = (sizeof(struct ds_data_nack) -
467 sizeof(struct ds_msg_tag)),
468 },
469 .handle = dpkt->handle,
470 .result = DS_INV_HDL,
471 };
472
473 printk(KERN_ERR PFX "Data for unknown handle %lu\n",
474 dpkt->handle);
475 ds_send(dp->lp, &nack, sizeof(nack));
476 } else {
477 cp->data(dp->lp, cp, dpkt, len);
478 }
479 return 0;
480}
481
482static void ds_up(struct ds_info *dp)
483{
484 struct ldc_channel *lp = dp->lp;
485 struct ds_ver_req req;
486 int err;
487
488 req.tag.type = DS_INIT_REQ;
489 req.tag.len = sizeof(req) - sizeof(struct ds_msg_tag);
490 req.ver.major = 1;
491 req.ver.minor = 0;
492
493 err = ds_send(lp, &req, sizeof(req));
494 if (err > 0)
495 dp->hs_state = DS_HS_START;
496}
497
498static void ds_event(void *arg, int event)
499{
500 struct ds_info *dp = arg;
501 struct ldc_channel *lp = dp->lp;
502 unsigned long flags;
503 int err;
504
505 spin_lock_irqsave(&ds_lock, flags);
506
507 if (event == LDC_EVENT_UP) {
508 ds_up(dp);
509 spin_unlock_irqrestore(&ds_lock, flags);
510 return;
511 }
512
513 if (event != LDC_EVENT_DATA_READY) {
514 printk(KERN_WARNING PFX "Unexpected LDC event %d\n", event);
515 spin_unlock_irqrestore(&ds_lock, flags);
516 return;
517 }
518
519 err = 0;
520 while (1) {
521 struct ds_msg_tag *tag;
522
523 err = ldc_read(lp, dp->rcv_buf, sizeof(*tag));
524
525 if (unlikely(err < 0)) {
526 if (err == -ECONNRESET)
527 ds_conn_reset(dp);
528 break;
529 }
530 if (err == 0)
531 break;
532
533 tag = dp->rcv_buf;
534 err = ldc_read(lp, tag + 1, tag->len);
535
536 if (unlikely(err < 0)) {
537 if (err == -ECONNRESET)
538 ds_conn_reset(dp);
539 break;
540 }
541 if (err < tag->len)
542 break;
543
544 if (tag->type < DS_DATA)
545 err = ds_handshake(dp, dp->rcv_buf);
546 else
547 err = ds_data(dp, dp->rcv_buf,
548 sizeof(*tag) + err);
549 if (err == -ECONNRESET)
550 break;
551 }
552
553 spin_unlock_irqrestore(&ds_lock, flags);
554}
555
556static int __devinit ds_probe(struct vio_dev *vdev,
557 const struct vio_device_id *id)
558{
559 static int ds_version_printed;
David S. Millere4509922007-07-11 18:51:31 -0700560 struct ldc_channel_config ds_cfg = {
561 .event = ds_event,
562 .mtu = 4096,
563 .mode = LDC_MODE_STREAM,
564 };
565 struct ldc_channel *lp;
566 struct ds_info *dp;
David S. Millere4509922007-07-11 18:51:31 -0700567 int err;
568
569 if (ds_version_printed++ == 0)
570 printk(KERN_INFO "%s", version);
571
David S. Millere4509922007-07-11 18:51:31 -0700572 dp = kzalloc(sizeof(*dp), GFP_KERNEL);
573 err = -ENOMEM;
574 if (!dp)
575 goto out_err;
576
577 dp->rcv_buf = kzalloc(4096, GFP_KERNEL);
578 if (!dp->rcv_buf)
579 goto out_free_dp;
580
581 dp->rcv_buf_len = 4096;
582
David S. Miller43fdf272007-07-12 13:47:50 -0700583 ds_cfg.tx_irq = vdev->tx_irq;
584 ds_cfg.rx_irq = vdev->rx_irq;
David S. Millere4509922007-07-11 18:51:31 -0700585
David S. Miller43fdf272007-07-12 13:47:50 -0700586 lp = ldc_alloc(vdev->channel_id, &ds_cfg, dp);
David S. Millere4509922007-07-11 18:51:31 -0700587 if (IS_ERR(lp)) {
588 err = PTR_ERR(lp);
589 goto out_free_rcv_buf;
590 }
591 dp->lp = lp;
592
David S. Miller133f09a2007-07-11 23:22:55 -0700593 err = ldc_bind(lp, "DS");
David S. Millere4509922007-07-11 18:51:31 -0700594 if (err)
595 goto out_free_ldc;
596
597 start_powerd();
598
599 return err;
600
601out_free_ldc:
602 ldc_free(dp->lp);
603
604out_free_rcv_buf:
605 kfree(dp->rcv_buf);
606
607out_free_dp:
608 kfree(dp);
609
610out_err:
611 return err;
612}
613
614static int ds_remove(struct vio_dev *vdev)
615{
616 return 0;
617}
618
619static struct vio_device_id ds_match[] = {
620 {
621 .type = "domain-services-port",
622 },
623 {},
624};
625
626static struct vio_driver ds_driver = {
627 .id_table = ds_match,
628 .probe = ds_probe,
629 .remove = ds_remove,
630 .driver = {
631 .name = "ds",
632 .owner = THIS_MODULE,
633 }
634};
635
636static int __init ds_init(void)
637{
638 int i;
639
640 for (i = 0; i < ARRAY_SIZE(ds_states); i++)
641 ds_states[i].handle = ((u64)i << 32);
642
643 return vio_register_driver(&ds_driver);
644}
645
646subsys_initcall(ds_init);