28 |
28 |
#include "hw/sysbus.h"
|
29 |
29 |
#include "kvm.h"
|
30 |
30 |
#include "device_tree.h"
|
|
31 |
#include "kvm_ppc.h"
|
31 |
32 |
|
32 |
33 |
#include "hw/spapr.h"
|
33 |
34 |
#include "hw/spapr_vio.h"
|
... | ... | |
359 |
360 |
return tswap64(val);
|
360 |
361 |
}
|
361 |
362 |
|
|
363 |
/*
|
|
364 |
* CRQ handling
|
|
365 |
*/
|
|
366 |
static target_ulong h_reg_crq(CPUState *env, sPAPREnvironment *spapr,
|
|
367 |
target_ulong opcode, target_ulong *args)
|
|
368 |
{
|
|
369 |
target_ulong reg = args[0];
|
|
370 |
target_ulong queue_addr = args[1];
|
|
371 |
target_ulong queue_len = args[2];
|
|
372 |
VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
|
|
373 |
|
|
374 |
if (!dev) {
|
|
375 |
hcall_dprintf("h_reg_crq on non-existent unit 0x"
|
|
376 |
TARGET_FMT_lx "\n", reg);
|
|
377 |
return H_PARAMETER;
|
|
378 |
}
|
|
379 |
|
|
380 |
/* We can't grok a queue size bigger than 256M for now */
|
|
381 |
if (queue_len < 0x1000 || queue_len > 0x10000000) {
|
|
382 |
hcall_dprintf("h_reg_crq, queue size too small or too big (0x%llx)\n",
|
|
383 |
(unsigned long long)queue_len);
|
|
384 |
return H_PARAMETER;
|
|
385 |
}
|
|
386 |
|
|
387 |
/* Check queue alignment */
|
|
388 |
if (queue_addr & 0xfff) {
|
|
389 |
hcall_dprintf("h_reg_crq, queue not aligned (0x%llx)\n",
|
|
390 |
(unsigned long long)queue_addr);
|
|
391 |
return H_PARAMETER;
|
|
392 |
}
|
|
393 |
|
|
394 |
/* Check if device supports CRQs */
|
|
395 |
if (!dev->crq.SendFunc) {
|
|
396 |
return H_NOT_FOUND;
|
|
397 |
}
|
|
398 |
|
|
399 |
|
|
400 |
/* Already a queue ? */
|
|
401 |
if (dev->crq.qsize) {
|
|
402 |
return H_RESOURCE;
|
|
403 |
}
|
|
404 |
dev->crq.qladdr = queue_addr;
|
|
405 |
dev->crq.qsize = queue_len;
|
|
406 |
dev->crq.qnext = 0;
|
|
407 |
|
|
408 |
dprintf("CRQ for dev 0x" TARGET_FMT_lx " registered at 0x"
|
|
409 |
TARGET_FMT_lx "/0x" TARGET_FMT_lx "\n",
|
|
410 |
reg, queue_addr, queue_len);
|
|
411 |
return H_SUCCESS;
|
|
412 |
}
|
|
413 |
|
|
414 |
static target_ulong h_free_crq(CPUState *env, sPAPREnvironment *spapr,
|
|
415 |
target_ulong opcode, target_ulong *args)
|
|
416 |
{
|
|
417 |
target_ulong reg = args[0];
|
|
418 |
VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
|
|
419 |
|
|
420 |
if (!dev) {
|
|
421 |
hcall_dprintf("h_free_crq on non-existent unit 0x"
|
|
422 |
TARGET_FMT_lx "\n", reg);
|
|
423 |
return H_PARAMETER;
|
|
424 |
}
|
|
425 |
|
|
426 |
dev->crq.qladdr = 0;
|
|
427 |
dev->crq.qsize = 0;
|
|
428 |
dev->crq.qnext = 0;
|
|
429 |
|
|
430 |
dprintf("CRQ for dev 0x" TARGET_FMT_lx " freed\n", reg);
|
|
431 |
|
|
432 |
return H_SUCCESS;
|
|
433 |
}
|
|
434 |
|
|
435 |
static target_ulong h_send_crq(CPUState *env, sPAPREnvironment *spapr,
|
|
436 |
target_ulong opcode, target_ulong *args)
|
|
437 |
{
|
|
438 |
target_ulong reg = args[0];
|
|
439 |
target_ulong msg_hi = args[1];
|
|
440 |
target_ulong msg_lo = args[2];
|
|
441 |
VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
|
|
442 |
uint64_t crq_mangle[2];
|
|
443 |
|
|
444 |
if (!dev) {
|
|
445 |
hcall_dprintf("h_send_crq on non-existent unit 0x"
|
|
446 |
TARGET_FMT_lx "\n", reg);
|
|
447 |
return H_PARAMETER;
|
|
448 |
}
|
|
449 |
crq_mangle[0] = cpu_to_be64(msg_hi);
|
|
450 |
crq_mangle[1] = cpu_to_be64(msg_lo);
|
|
451 |
|
|
452 |
if (dev->crq.SendFunc) {
|
|
453 |
return dev->crq.SendFunc(dev, (uint8_t *)crq_mangle);
|
|
454 |
}
|
|
455 |
|
|
456 |
return H_HARDWARE;
|
|
457 |
}
|
|
458 |
|
|
459 |
static target_ulong h_enable_crq(CPUState *env, sPAPREnvironment *spapr,
|
|
460 |
target_ulong opcode, target_ulong *args)
|
|
461 |
{
|
|
462 |
target_ulong reg = args[0];
|
|
463 |
VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
|
|
464 |
|
|
465 |
if (!dev) {
|
|
466 |
hcall_dprintf("h_enable_crq on non-existent unit 0x"
|
|
467 |
TARGET_FMT_lx "\n", reg);
|
|
468 |
return H_PARAMETER;
|
|
469 |
}
|
|
470 |
|
|
471 |
return 0;
|
|
472 |
}
|
|
473 |
|
|
474 |
/* Returns negative error, 0 success, or positive: queue full */
|
|
475 |
int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq)
|
|
476 |
{
|
|
477 |
int rc;
|
|
478 |
uint8_t byte;
|
|
479 |
|
|
480 |
if (!dev->crq.qsize) {
|
|
481 |
fprintf(stderr, "spapr_vio_send_creq on uninitialized queue\n");
|
|
482 |
return -1;
|
|
483 |
}
|
|
484 |
|
|
485 |
/* Maybe do a fast path for KVM just writing to the pages */
|
|
486 |
rc = spapr_tce_dma_read(dev, dev->crq.qladdr + dev->crq.qnext, &byte, 1);
|
|
487 |
if (rc) {
|
|
488 |
return rc;
|
|
489 |
}
|
|
490 |
if (byte != 0) {
|
|
491 |
return 1;
|
|
492 |
}
|
|
493 |
|
|
494 |
rc = spapr_tce_dma_write(dev, dev->crq.qladdr + dev->crq.qnext + 8,
|
|
495 |
&crq[8], 8);
|
|
496 |
if (rc) {
|
|
497 |
return rc;
|
|
498 |
}
|
|
499 |
|
|
500 |
kvmppc_eieio();
|
|
501 |
|
|
502 |
rc = spapr_tce_dma_write(dev, dev->crq.qladdr + dev->crq.qnext, crq, 8);
|
|
503 |
if (rc) {
|
|
504 |
return rc;
|
|
505 |
}
|
|
506 |
|
|
507 |
dev->crq.qnext = (dev->crq.qnext + 16) % dev->crq.qsize;
|
|
508 |
|
|
509 |
if (dev->signal_state & 1) {
|
|
510 |
qemu_irq_pulse(dev->qirq);
|
|
511 |
}
|
|
512 |
|
|
513 |
return 0;
|
|
514 |
}
|
|
515 |
|
362 |
516 |
static int spapr_vio_busdev_init(DeviceState *qdev, DeviceInfo *qinfo)
|
363 |
517 |
{
|
364 |
518 |
VIOsPAPRDeviceInfo *info = (VIOsPAPRDeviceInfo *)qinfo;
|
... | ... | |
431 |
585 |
/* hcall-tce */
|
432 |
586 |
spapr_register_hypercall(H_PUT_TCE, h_put_tce);
|
433 |
587 |
|
|
588 |
/* hcall-crq */
|
|
589 |
spapr_register_hypercall(H_REG_CRQ, h_reg_crq);
|
|
590 |
spapr_register_hypercall(H_FREE_CRQ, h_free_crq);
|
|
591 |
spapr_register_hypercall(H_SEND_CRQ, h_send_crq);
|
|
592 |
spapr_register_hypercall(H_ENABLE_CRQ, h_enable_crq);
|
|
593 |
|
434 |
594 |
for (qinfo = device_info_list; qinfo; qinfo = qinfo->next) {
|
435 |
595 |
VIOsPAPRDeviceInfo *info = (VIOsPAPRDeviceInfo *)qinfo;
|
436 |
596 |
|