Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  book3s_pr_papr.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2011. Freescale Inc. All rights reserved.
 *
 * Authors:
 *    Alexander Graf <agraf@suse.de>
 *    Paul Mackerras <paulus@samba.org>
 *
 * Description:
 *
 * Hypercall handling for running PAPR guests in PR KVM on Book 3S
 * processors.
 */


#include <linux/anon_inodes.h>

#include <linux/uaccess.h>
#include <asm/kvm_ppc.h>
#include <asm/kvm_book3s.h>

#define HPTE_SIZE 16  /* bytes per HPT entry */

static unsigned long get_pteg_addr(struct kvm_vcpu *vcpu, long pte_index)
{
 struct kvmppc_vcpu_book3s *vcpu_book3s = to_book3s(vcpu);
 unsigned long pteg_addr;

 pte_index <<= 4;
 pte_index &= ((1 << ((vcpu_book3s->sdr1 & 0x1f) + 11)) - 1) << 7 | 0x70;
 pteg_addr = vcpu_book3s->sdr1 & 0xfffffffffffc0000ULL;
 pteg_addr |= pte_index;

 return pteg_addr;
}

static int kvmppc_h_pr_enter(struct kvm_vcpu *vcpu)
{
 long flags = kvmppc_get_gpr(vcpu, 4);
 long pte_index = kvmppc_get_gpr(vcpu, 5);
 __be64 pteg[2 * 8];
 __be64 *hpte;
 unsigned long pteg_addr, i;
 long int ret;

 i = pte_index & 7;
 pte_index &= ~7UL;
 pteg_addr = get_pteg_addr(vcpu, pte_index);

 mutex_lock(&vcpu->kvm->arch.hpt_mutex);
 ret = H_FUNCTION;
 if (copy_from_user(pteg, (void __user *)pteg_addr, sizeof(pteg)))
  goto done;
 hpte = pteg;

 ret = H_PTEG_FULL;
 if (likely((flags & H_EXACT) == 0)) {
  for (i = 0; ; ++i) {
   if (i == 8)
    goto done;
   if ((be64_to_cpu(*hpte) & HPTE_V_VALID) == 0)
    break;
   hpte += 2;
  }
 } else {
  hpte += i * 2;
  if (*hpte & HPTE_V_VALID)
   goto done;
 }

 hpte[0] = cpu_to_be64(kvmppc_get_gpr(vcpu, 6));
 hpte[1] = cpu_to_be64(kvmppc_get_gpr(vcpu, 7));
 pteg_addr += i * HPTE_SIZE;
 ret = H_FUNCTION;
 if (copy_to_user((void __user *)pteg_addr, hpte, HPTE_SIZE))
  goto done;
 kvmppc_set_gpr(vcpu, 4, pte_index | i);
 ret = H_SUCCESS;

 done:
 mutex_unlock(&vcpu->kvm->arch.hpt_mutex);
 kvmppc_set_gpr(vcpu, 3, ret);

 return EMULATE_DONE;
}

static int kvmppc_h_pr_remove(struct kvm_vcpu *vcpu)
{
 unsigned long flags= kvmppc_get_gpr(vcpu, 4);
 unsigned long pte_index = kvmppc_get_gpr(vcpu, 5);
 unsigned long avpn = kvmppc_get_gpr(vcpu, 6);
 unsigned long v = 0, pteg, rb;
 unsigned long pte[2];
 long int ret;

 pteg = get_pteg_addr(vcpu, pte_index);
 mutex_lock(&vcpu->kvm->arch.hpt_mutex);
 ret = H_FUNCTION;
 if (copy_from_user(pte, (void __user *)pteg, sizeof(pte)))
  goto done;
 pte[0] = be64_to_cpu((__force __be64)pte[0]);
 pte[1] = be64_to_cpu((__force __be64)pte[1]);

 ret = H_NOT_FOUND;
 if ((pte[0] & HPTE_V_VALID) == 0 ||
     ((flags & H_AVPN) && (pte[0] & ~0x7fUL) != avpn) ||
     ((flags & H_ANDCOND) && (pte[0] & avpn) != 0))
  goto done;

 ret = H_FUNCTION;
 if (copy_to_user((void __user *)pteg, &v, sizeof(v)))
  goto done;

 rb = compute_tlbie_rb(pte[0], pte[1], pte_index);
 vcpu->arch.mmu.tlbie(vcpu, rb, rb & 1 ? true : false);

 ret = H_SUCCESS;
 kvmppc_set_gpr(vcpu, 4, pte[0]);
 kvmppc_set_gpr(vcpu, 5, pte[1]);

 done:
 mutex_unlock(&vcpu->kvm->arch.hpt_mutex);
 kvmppc_set_gpr(vcpu, 3, ret);

 return EMULATE_DONE;
}

/* Request defs for kvmppc_h_pr_bulk_remove() */
#define H_BULK_REMOVE_TYPE             0xc000000000000000ULL
#define   H_BULK_REMOVE_REQUEST        0x4000000000000000ULL
#define   H_BULK_REMOVE_RESPONSE       0x8000000000000000ULL
#define   H_BULK_REMOVE_END            0xc000000000000000ULL
#define H_BULK_REMOVE_CODE             0x3000000000000000ULL
#define   H_BULK_REMOVE_SUCCESS        0x0000000000000000ULL
#define   H_BULK_REMOVE_NOT_FOUND      0x1000000000000000ULL
#define   H_BULK_REMOVE_PARM           0x2000000000000000ULL
#define   H_BULK_REMOVE_HW             0x3000000000000000ULL
#define H_BULK_REMOVE_RC               0x0c00000000000000ULL
#define H_BULK_REMOVE_FLAGS            0x0300000000000000ULL
#define   H_BULK_REMOVE_ABSOLUTE       0x0000000000000000ULL
#define   H_BULK_REMOVE_ANDCOND        0x0100000000000000ULL
#define   H_BULK_REMOVE_AVPN           0x0200000000000000ULL
#define H_BULK_REMOVE_PTEX             0x00ffffffffffffffULL
#define H_BULK_REMOVE_MAX_BATCH        4

static int kvmppc_h_pr_bulk_remove(struct kvm_vcpu *vcpu)
{
 int i;
 int paramnr = 4;
 int ret = H_SUCCESS;

 mutex_lock(&vcpu->kvm->arch.hpt_mutex);
 for (i = 0; i < H_BULK_REMOVE_MAX_BATCH; i++) {
  unsigned long tsh = kvmppc_get_gpr(vcpu, paramnr+(2*i));
  unsigned long tsl = kvmppc_get_gpr(vcpu, paramnr+(2*i)+1);
  unsigned long pteg, rb, flags;
  unsigned long pte[2];
  unsigned long v = 0;

  if ((tsh & H_BULK_REMOVE_TYPE) == H_BULK_REMOVE_END) {
   break/* Exit success */
  } else if ((tsh & H_BULK_REMOVE_TYPE) !=
      H_BULK_REMOVE_REQUEST) {
   ret = H_PARAMETER;
   break/* Exit fail */
  }

  tsh &= H_BULK_REMOVE_PTEX | H_BULK_REMOVE_FLAGS;
  tsh |= H_BULK_REMOVE_RESPONSE;

  if ((tsh & H_BULK_REMOVE_ANDCOND) &&
      (tsh & H_BULK_REMOVE_AVPN)) {
   tsh |= H_BULK_REMOVE_PARM;
   kvmppc_set_gpr(vcpu, paramnr+(2*i), tsh);
   ret = H_PARAMETER;
   break/* Exit fail */
  }

  pteg = get_pteg_addr(vcpu, tsh & H_BULK_REMOVE_PTEX);
  if (copy_from_user(pte, (void __user *)pteg, sizeof(pte))) {
   ret = H_FUNCTION;
   break;
  }
  pte[0] = be64_to_cpu((__force __be64)pte[0]);
  pte[1] = be64_to_cpu((__force __be64)pte[1]);

  /* tsl = AVPN */
  flags = (tsh & H_BULK_REMOVE_FLAGS) >> 26;

  if ((pte[0] & HPTE_V_VALID) == 0 ||
      ((flags & H_AVPN) && (pte[0] & ~0x7fUL) != tsl) ||
      ((flags & H_ANDCOND) && (pte[0] & tsl) != 0)) {
   tsh |= H_BULK_REMOVE_NOT_FOUND;
  } else {
   /* Splat the pteg in (userland) hpt */
   if (copy_to_user((void __user *)pteg, &v, sizeof(v))) {
    ret = H_FUNCTION;
    break;
   }

   rb = compute_tlbie_rb(pte[0], pte[1],
           tsh & H_BULK_REMOVE_PTEX);
   vcpu->arch.mmu.tlbie(vcpu, rb, rb & 1 ? true : false);
   tsh |= H_BULK_REMOVE_SUCCESS;
   tsh |= (pte[1] & (HPTE_R_C | HPTE_R_R)) << 43;
  }
  kvmppc_set_gpr(vcpu, paramnr+(2*i), tsh);
 }
 mutex_unlock(&vcpu->kvm->arch.hpt_mutex);
 kvmppc_set_gpr(vcpu, 3, ret);

 return EMULATE_DONE;
}

static int kvmppc_h_pr_protect(struct kvm_vcpu *vcpu)
{
 unsigned long flags = kvmppc_get_gpr(vcpu, 4);
 unsigned long pte_index = kvmppc_get_gpr(vcpu, 5);
 unsigned long avpn = kvmppc_get_gpr(vcpu, 6);
 unsigned long rb, pteg, r, v;
 unsigned long pte[2];
 long int ret;

 pteg = get_pteg_addr(vcpu, pte_index);
 mutex_lock(&vcpu->kvm->arch.hpt_mutex);
 ret = H_FUNCTION;
 if (copy_from_user(pte, (void __user *)pteg, sizeof(pte)))
  goto done;
 pte[0] = be64_to_cpu((__force __be64)pte[0]);
 pte[1] = be64_to_cpu((__force __be64)pte[1]);

 ret = H_NOT_FOUND;
 if ((pte[0] & HPTE_V_VALID) == 0 ||
     ((flags & H_AVPN) && (pte[0] & ~0x7fUL) != avpn))
  goto done;

 v = pte[0];
 r = pte[1];
 r &= ~(HPTE_R_PP0 | HPTE_R_PP | HPTE_R_N | HPTE_R_KEY_HI |
        HPTE_R_KEY_LO);
 r |= (flags << 55) & HPTE_R_PP0;
 r |= (flags << 48) & HPTE_R_KEY_HI;
 r |= flags & (HPTE_R_PP | HPTE_R_N | HPTE_R_KEY_LO);

 pte[1] = r;

 rb = compute_tlbie_rb(v, r, pte_index);
 vcpu->arch.mmu.tlbie(vcpu, rb, rb & 1 ? true : false);
 pte[0] = (__force u64)cpu_to_be64(pte[0]);
 pte[1] = (__force u64)cpu_to_be64(pte[1]);
 ret = H_FUNCTION;
 if (copy_to_user((void __user *)pteg, pte, sizeof(pte)))
  goto done;
 ret = H_SUCCESS;

 done:
 mutex_unlock(&vcpu->kvm->arch.hpt_mutex);
 kvmppc_set_gpr(vcpu, 3, ret);

 return EMULATE_DONE;
}

static int kvmppc_h_pr_logical_ci_load(struct kvm_vcpu *vcpu)
{
 long rc;

 rc = kvmppc_h_logical_ci_load(vcpu);
 if (rc == H_TOO_HARD)
  return EMULATE_FAIL;
 kvmppc_set_gpr(vcpu, 3, rc);
 return EMULATE_DONE;
}

static int kvmppc_h_pr_logical_ci_store(struct kvm_vcpu *vcpu)
{
 long rc;

 rc = kvmppc_h_logical_ci_store(vcpu);
 if (rc == H_TOO_HARD)
  return EMULATE_FAIL;
 kvmppc_set_gpr(vcpu, 3, rc);
 return EMULATE_DONE;
}

static int kvmppc_h_pr_set_mode(struct kvm_vcpu *vcpu)
{
 unsigned long mflags = kvmppc_get_gpr(vcpu, 4);
 unsigned long resource = kvmppc_get_gpr(vcpu, 5);

 if (resource == H_SET_MODE_RESOURCE_ADDR_TRANS_MODE) {
  /* KVM PR does not provide AIL!=0 to guests */
  if (mflags == 0)
   kvmppc_set_gpr(vcpu, 3, H_SUCCESS);
  else
   kvmppc_set_gpr(vcpu, 3, H_UNSUPPORTED_FLAG_START - 63);
  return EMULATE_DONE;
 }
 return EMULATE_FAIL;
}

#ifdef CONFIG_SPAPR_TCE_IOMMU
static int kvmppc_h_pr_put_tce(struct kvm_vcpu *vcpu)
{
 unsigned long liobn = kvmppc_get_gpr(vcpu, 4);
 unsigned long ioba = kvmppc_get_gpr(vcpu, 5);
 unsigned long tce = kvmppc_get_gpr(vcpu, 6);
 long rc;

 rc = kvmppc_h_put_tce(vcpu, liobn, ioba, tce);
 if (rc == H_TOO_HARD)
  return EMULATE_FAIL;
 kvmppc_set_gpr(vcpu, 3, rc);
 return EMULATE_DONE;
}

static int kvmppc_h_pr_put_tce_indirect(struct kvm_vcpu *vcpu)
{
 unsigned long liobn = kvmppc_get_gpr(vcpu, 4);
 unsigned long ioba = kvmppc_get_gpr(vcpu, 5);
 unsigned long tce = kvmppc_get_gpr(vcpu, 6);
 unsigned long npages = kvmppc_get_gpr(vcpu, 7);
 long rc;

 rc = kvmppc_h_put_tce_indirect(vcpu, liobn, ioba,
   tce, npages);
 if (rc == H_TOO_HARD)
  return EMULATE_FAIL;
 kvmppc_set_gpr(vcpu, 3, rc);
 return EMULATE_DONE;
}

static int kvmppc_h_pr_stuff_tce(struct kvm_vcpu *vcpu)
{
 unsigned long liobn = kvmppc_get_gpr(vcpu, 4);
 unsigned long ioba = kvmppc_get_gpr(vcpu, 5);
 unsigned long tce_value = kvmppc_get_gpr(vcpu, 6);
 unsigned long npages = kvmppc_get_gpr(vcpu, 7);
 long rc;

 rc = kvmppc_h_stuff_tce(vcpu, liobn, ioba, tce_value, npages);
 if (rc == H_TOO_HARD)
  return EMULATE_FAIL;
 kvmppc_set_gpr(vcpu, 3, rc);
 return EMULATE_DONE;
}

#else /* CONFIG_SPAPR_TCE_IOMMU */
static int kvmppc_h_pr_put_tce(struct kvm_vcpu *vcpu)
{
 return EMULATE_FAIL;
}

static int kvmppc_h_pr_put_tce_indirect(struct kvm_vcpu *vcpu)
{
 return EMULATE_FAIL;
}

static int kvmppc_h_pr_stuff_tce(struct kvm_vcpu *vcpu)
{
 return EMULATE_FAIL;
}
#endif /* CONFIG_SPAPR_TCE_IOMMU */

static int kvmppc_h_pr_xics_hcall(struct kvm_vcpu *vcpu, u32 cmd)
{
 long rc = kvmppc_xics_hcall(vcpu, cmd);
 kvmppc_set_gpr(vcpu, 3, rc);
 return EMULATE_DONE;
}

int kvmppc_h_pr(struct kvm_vcpu *vcpu, unsigned long cmd)
{
 int rc, idx;

 if (cmd <= MAX_HCALL_OPCODE &&
     !test_bit(cmd/4, vcpu->kvm->arch.enabled_hcalls))
  return EMULATE_FAIL;

 switch (cmd) {
 case H_ENTER:
  return kvmppc_h_pr_enter(vcpu);
 case H_REMOVE:
  return kvmppc_h_pr_remove(vcpu);
 case H_PROTECT:
  return kvmppc_h_pr_protect(vcpu);
 case H_BULK_REMOVE:
  return kvmppc_h_pr_bulk_remove(vcpu);
 case H_PUT_TCE:
  return kvmppc_h_pr_put_tce(vcpu);
 case H_PUT_TCE_INDIRECT:
  return kvmppc_h_pr_put_tce_indirect(vcpu);
 case H_STUFF_TCE:
  return kvmppc_h_pr_stuff_tce(vcpu);
 case H_CEDE:
  kvmppc_set_msr_fast(vcpu, kvmppc_get_msr(vcpu) | MSR_EE);
  kvm_vcpu_halt(vcpu);
  vcpu->stat.generic.halt_wakeup++;
  return EMULATE_DONE;
 case H_LOGICAL_CI_LOAD:
  return kvmppc_h_pr_logical_ci_load(vcpu);
 case H_LOGICAL_CI_STORE:
  return kvmppc_h_pr_logical_ci_store(vcpu);
 case H_SET_MODE:
  return kvmppc_h_pr_set_mode(vcpu);
 case H_XIRR:
 case H_CPPR:
 case H_EOI:
 case H_IPI:
 case H_IPOLL:
 case H_XIRR_X:
  if (kvmppc_xics_enabled(vcpu))
   return kvmppc_h_pr_xics_hcall(vcpu, cmd);
  break;
 case H_RTAS:
  if (list_empty(&vcpu->kvm->arch.rtas_tokens))
   break;
  idx = srcu_read_lock(&vcpu->kvm->srcu);
  rc = kvmppc_rtas_hcall(vcpu);
  srcu_read_unlock(&vcpu->kvm->srcu, idx);
  if (rc)
   break;
  kvmppc_set_gpr(vcpu, 3, 0);
  return EMULATE_DONE;
 }

 return EMULATE_FAIL;
}

int kvmppc_hcall_impl_pr(unsigned long cmd)
{
 switch (cmd) {
 case H_ENTER:
 case H_REMOVE:
 case H_PROTECT:
 case H_BULK_REMOVE:
#ifdef CONFIG_SPAPR_TCE_IOMMU
 case H_GET_TCE:
 case H_PUT_TCE:
 case H_PUT_TCE_INDIRECT:
 case H_STUFF_TCE:
#endif
 case H_CEDE:
 case H_LOGICAL_CI_LOAD:
 case H_LOGICAL_CI_STORE:
 case H_SET_MODE:
#ifdef CONFIG_KVM_XICS
 case H_XIRR:
 case H_CPPR:
 case H_EOI:
 case H_IPI:
 case H_IPOLL:
 case H_XIRR_X:
#endif
  return 1;
 }
 return 0;
}

/*
 * List of hcall numbers to enable by default.
 * For compatibility with old userspace, we enable by default
 * all hcalls that were implemented before the hcall-enabling
 * facility was added.  Note this list should not include H_RTAS.
 */

static unsigned int default_hcall_list[] = {
 H_ENTER,
 H_REMOVE,
 H_PROTECT,
 H_BULK_REMOVE,
#ifdef CONFIG_SPAPR_TCE_IOMMU
 H_GET_TCE,
 H_PUT_TCE,
#endif
 H_CEDE,
 H_SET_MODE,
#ifdef CONFIG_KVM_XICS
 H_XIRR,
 H_CPPR,
 H_EOI,
 H_IPI,
 H_IPOLL,
 H_XIRR_X,
#endif
 0
};

void kvmppc_pr_init_default_hcalls(struct kvm *kvm)
{
 int i;
 unsigned int hcall;

 for (i = 0; default_hcall_list[i]; ++i) {
  hcall = default_hcall_list[i];
  WARN_ON(!kvmppc_hcall_impl_pr(hcall));
  __set_bit(hcall / 4, kvm->arch.enabled_hcalls);
 }
}

Messung V0.5
C=95 H=87 G=90

¤ Dauer der Verarbeitung: 0.5 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge