diag.c 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. /*
  2. * handling diagnose instructions
  3. *
  4. * Copyright IBM Corp. 2008, 2011
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License (version 2 only)
  8. * as published by the Free Software Foundation.
  9. *
  10. * Author(s): Carsten Otte <cotte@de.ibm.com>
  11. * Christian Borntraeger <borntraeger@de.ibm.com>
  12. */
  13. #include <linux/kvm.h>
  14. #include <linux/kvm_host.h>
  15. #include "kvm-s390.h"
  16. #include "trace.h"
  17. static int diag_release_pages(struct kvm_vcpu *vcpu)
  18. {
  19. unsigned long start, end;
  20. unsigned long prefix = vcpu->arch.sie_block->prefix;
  21. start = vcpu->run->s.regs.gprs[(vcpu->arch.sie_block->ipa & 0xf0) >> 4];
  22. end = vcpu->run->s.regs.gprs[vcpu->arch.sie_block->ipa & 0xf] + 4096;
  23. if (start & ~PAGE_MASK || end & ~PAGE_MASK || start > end
  24. || start < 2 * PAGE_SIZE)
  25. return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
  26. VCPU_EVENT(vcpu, 5, "diag release pages %lX %lX", start, end);
  27. vcpu->stat.diagnose_10++;
  28. /* we checked for start > end above */
  29. if (end < prefix || start >= prefix + 2 * PAGE_SIZE) {
  30. gmap_discard(start, end, vcpu->arch.gmap);
  31. } else {
  32. if (start < prefix)
  33. gmap_discard(start, prefix, vcpu->arch.gmap);
  34. if (end >= prefix)
  35. gmap_discard(prefix + 2 * PAGE_SIZE,
  36. end, vcpu->arch.gmap);
  37. }
  38. return 0;
  39. }
  40. static int __diag_time_slice_end(struct kvm_vcpu *vcpu)
  41. {
  42. VCPU_EVENT(vcpu, 5, "%s", "diag time slice end");
  43. vcpu->stat.diagnose_44++;
  44. kvm_vcpu_on_spin(vcpu);
  45. return 0;
  46. }
  47. static int __diag_time_slice_end_directed(struct kvm_vcpu *vcpu)
  48. {
  49. struct kvm *kvm = vcpu->kvm;
  50. struct kvm_vcpu *tcpu;
  51. int tid;
  52. int i;
  53. tid = vcpu->run->s.regs.gprs[(vcpu->arch.sie_block->ipa & 0xf0) >> 4];
  54. vcpu->stat.diagnose_9c++;
  55. VCPU_EVENT(vcpu, 5, "diag time slice end directed to %d", tid);
  56. if (tid == vcpu->vcpu_id)
  57. return 0;
  58. kvm_for_each_vcpu(i, tcpu, kvm)
  59. if (tcpu->vcpu_id == tid) {
  60. kvm_vcpu_yield_to(tcpu);
  61. break;
  62. }
  63. return 0;
  64. }
  65. static int __diag_ipl_functions(struct kvm_vcpu *vcpu)
  66. {
  67. unsigned int reg = vcpu->arch.sie_block->ipa & 0xf;
  68. unsigned long subcode = vcpu->run->s.regs.gprs[reg] & 0xffff;
  69. VCPU_EVENT(vcpu, 5, "diag ipl functions, subcode %lx", subcode);
  70. switch (subcode) {
  71. case 3:
  72. vcpu->run->s390_reset_flags = KVM_S390_RESET_CLEAR;
  73. break;
  74. case 4:
  75. vcpu->run->s390_reset_flags = 0;
  76. break;
  77. default:
  78. return -EOPNOTSUPP;
  79. }
  80. atomic_set_mask(CPUSTAT_STOPPED, &vcpu->arch.sie_block->cpuflags);
  81. vcpu->run->s390_reset_flags |= KVM_S390_RESET_SUBSYSTEM;
  82. vcpu->run->s390_reset_flags |= KVM_S390_RESET_IPL;
  83. vcpu->run->s390_reset_flags |= KVM_S390_RESET_CPU_INIT;
  84. vcpu->run->exit_reason = KVM_EXIT_S390_RESET;
  85. VCPU_EVENT(vcpu, 3, "requesting userspace resets %llx",
  86. vcpu->run->s390_reset_flags);
  87. return -EREMOTE;
  88. }
  89. int kvm_s390_handle_diag(struct kvm_vcpu *vcpu)
  90. {
  91. int code = (vcpu->arch.sie_block->ipb & 0xfff0000) >> 16;
  92. trace_kvm_s390_handle_diag(vcpu, code);
  93. switch (code) {
  94. case 0x10:
  95. return diag_release_pages(vcpu);
  96. case 0x44:
  97. return __diag_time_slice_end(vcpu);
  98. case 0x9c:
  99. return __diag_time_slice_end_directed(vcpu);
  100. case 0x308:
  101. return __diag_ipl_functions(vcpu);
  102. default:
  103. return -EOPNOTSUPP;
  104. }
  105. }