diag.c 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  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. #include "trace-s390.h"
  18. static int diag_release_pages(struct kvm_vcpu *vcpu)
  19. {
  20. unsigned long start, end;
  21. unsigned long prefix = vcpu->arch.sie_block->prefix;
  22. start = vcpu->run->s.regs.gprs[(vcpu->arch.sie_block->ipa & 0xf0) >> 4];
  23. end = vcpu->run->s.regs.gprs[vcpu->arch.sie_block->ipa & 0xf] + 4096;
  24. if (start & ~PAGE_MASK || end & ~PAGE_MASK || start > end
  25. || start < 2 * PAGE_SIZE)
  26. return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
  27. VCPU_EVENT(vcpu, 5, "diag release pages %lX %lX", start, end);
  28. vcpu->stat.diagnose_10++;
  29. /* we checked for start > end above */
  30. if (end < prefix || start >= prefix + 2 * PAGE_SIZE) {
  31. gmap_discard(start, end, vcpu->arch.gmap);
  32. } else {
  33. if (start < prefix)
  34. gmap_discard(start, prefix, vcpu->arch.gmap);
  35. if (end >= prefix)
  36. gmap_discard(prefix + 2 * PAGE_SIZE,
  37. end, vcpu->arch.gmap);
  38. }
  39. return 0;
  40. }
  41. static int __diag_time_slice_end(struct kvm_vcpu *vcpu)
  42. {
  43. VCPU_EVENT(vcpu, 5, "%s", "diag time slice end");
  44. vcpu->stat.diagnose_44++;
  45. kvm_vcpu_on_spin(vcpu);
  46. return 0;
  47. }
  48. static int __diag_time_slice_end_directed(struct kvm_vcpu *vcpu)
  49. {
  50. struct kvm *kvm = vcpu->kvm;
  51. struct kvm_vcpu *tcpu;
  52. int tid;
  53. int i;
  54. tid = vcpu->run->s.regs.gprs[(vcpu->arch.sie_block->ipa & 0xf0) >> 4];
  55. vcpu->stat.diagnose_9c++;
  56. VCPU_EVENT(vcpu, 5, "diag time slice end directed to %d", tid);
  57. if (tid == vcpu->vcpu_id)
  58. return 0;
  59. kvm_for_each_vcpu(i, tcpu, kvm)
  60. if (tcpu->vcpu_id == tid) {
  61. kvm_vcpu_yield_to(tcpu);
  62. break;
  63. }
  64. return 0;
  65. }
  66. static int __diag_ipl_functions(struct kvm_vcpu *vcpu)
  67. {
  68. unsigned int reg = vcpu->arch.sie_block->ipa & 0xf;
  69. unsigned long subcode = vcpu->run->s.regs.gprs[reg] & 0xffff;
  70. VCPU_EVENT(vcpu, 5, "diag ipl functions, subcode %lx", subcode);
  71. switch (subcode) {
  72. case 3:
  73. vcpu->run->s390_reset_flags = KVM_S390_RESET_CLEAR;
  74. break;
  75. case 4:
  76. vcpu->run->s390_reset_flags = 0;
  77. break;
  78. default:
  79. return -EOPNOTSUPP;
  80. }
  81. atomic_set_mask(CPUSTAT_STOPPED, &vcpu->arch.sie_block->cpuflags);
  82. vcpu->run->s390_reset_flags |= KVM_S390_RESET_SUBSYSTEM;
  83. vcpu->run->s390_reset_flags |= KVM_S390_RESET_IPL;
  84. vcpu->run->s390_reset_flags |= KVM_S390_RESET_CPU_INIT;
  85. vcpu->run->exit_reason = KVM_EXIT_S390_RESET;
  86. VCPU_EVENT(vcpu, 3, "requesting userspace resets %llx",
  87. vcpu->run->s390_reset_flags);
  88. trace_kvm_s390_request_resets(vcpu->run->s390_reset_flags);
  89. return -EREMOTE;
  90. }
  91. int kvm_s390_handle_diag(struct kvm_vcpu *vcpu)
  92. {
  93. int code = (vcpu->arch.sie_block->ipb & 0xfff0000) >> 16;
  94. trace_kvm_s390_handle_diag(vcpu, code);
  95. switch (code) {
  96. case 0x10:
  97. return diag_release_pages(vcpu);
  98. case 0x44:
  99. return __diag_time_slice_end(vcpu);
  100. case 0x9c:
  101. return __diag_time_slice_end_directed(vcpu);
  102. case 0x308:
  103. return __diag_ipl_functions(vcpu);
  104. default:
  105. return -EOPNOTSUPP;
  106. }
  107. }