Re: [PATCH V2 2/6] kvm tools: Add SPAPR PPC64 hcall & rtascall structure

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, Dec 13, 2011 at 06:10:46PM +1100, Matt Evans wrote:
> This patch adds the basic structure for HV calls, their registration and some of
> the simpler calls.  A similar layout for RTAS calls is also added, again with
> some of the simpler RTAS calls used by the guest.  The SPAPR RTAS stub is
> generated inline.  Also, nodes for RTAS are added to the device
> tree.

[snip]
> diff --git a/tools/kvm/powerpc/spapr.h b/tools/kvm/powerpc/spapr.h
> new file mode 100644
> index 0000000..57cece1
> --- /dev/null
> +++ b/tools/kvm/powerpc/spapr.h
> @@ -0,0 +1,105 @@
> +/*
> + * SPAPR definitions and declarations
> + *
> + * Borrowed heavily from QEMU's spapr.h,
> + * Copyright (c) 2010 David Gibson, IBM Corporation.

So, most of the content of this file in qemu, I in turn took from
arch/powerpc/include/asm/hvcall.h in the kernel tree.  You might be
better off using that directly.

[snip]
> +static target_ulong h_logical_icbi(struct kvm_cpu *vcpu, target_ulong opcode, target_ulong *args)
> +{
> +	/* Nothing to do on emulation, KVM will trap this in the kernel */
> +	return H_SUCCESS;

hcalls that need to be handled by the host kernel should probably dump
an error here, rather than silently doing nothing, since if the host
kernel does handle them here they should never reach userspace at all.

> +}
> +
> +static target_ulong h_logical_dcbf(struct kvm_cpu *vcpu, target_ulong opcode, target_ulong *args)
> +{
> +	/* Nothing to do on emulation, KVM will trap this in the kernel */
> +	return H_SUCCESS;
> +}
> +
> +void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn)
> +{
> +	spapr_hcall_fn *slot;
> +
> +	if (opcode <= MAX_HCALL_OPCODE) {
> +		assert((opcode & 0x3) == 0);
> +
> +		slot = &papr_hypercall_table[opcode / 4];
> +	} else {
> +		assert((opcode >= KVMPPC_HCALL_BASE) &&
> +		       (opcode <= KVMPPC_HCALL_MAX));
> +
> +		slot = &kvmppc_hypercall_table[opcode - KVMPPC_HCALL_BASE];
> +	}
> +
> +	assert(!(*slot) || (fn == *slot));
> +	*slot = fn;
> +}
> +
> +target_ulong spapr_hypercall(struct kvm_cpu *vcpu, target_ulong opcode,
> +			     target_ulong *args)
> +{
> +	if ((opcode <= MAX_HCALL_OPCODE)
> +	    && ((opcode & 0x3) == 0)) {
> +		spapr_hcall_fn fn = papr_hypercall_table[opcode / 4];
> +
> +		if (fn) {
> +			return fn(vcpu, opcode, args);
> +		}
> +	} else if ((opcode >= KVMPPC_HCALL_BASE) &&
> +		   (opcode <= KVMPPC_HCALL_MAX)) {
> +		spapr_hcall_fn fn = kvmppc_hypercall_table[opcode -
> +							   KVMPPC_HCALL_BASE];
> +
> +		if (fn) {
> +			return fn(vcpu, opcode, args);
> +		}
> +	}
> +
> +	hcall_dprintf("Unimplemented hcall 0x%lx\n", opcode);
> +	return H_FUNCTION;
> +}
> +
> +void hypercall_init(void)
> +{
> +	/* hcall-dabr */
> +	spapr_register_hypercall(H_SET_DABR, h_set_dabr);
> +
> +	spapr_register_hypercall(H_LOGICAL_CI_LOAD, h_logical_load);
> +	spapr_register_hypercall(H_LOGICAL_CI_STORE, h_logical_store);
> +	spapr_register_hypercall(H_LOGICAL_CACHE_LOAD, h_logical_load);
> +	spapr_register_hypercall(H_LOGICAL_CACHE_STORE, h_logical_store);
> +	spapr_register_hypercall(H_LOGICAL_ICBI, h_logical_icbi);
> +	spapr_register_hypercall(H_LOGICAL_DCBF, h_logical_dcbf);
> +
> +	/* KVM-PPC specific hcalls */
> +	spapr_register_hypercall(KVMPPC_H_RTAS, h_rtas);
> +}
> diff --git a/tools/kvm/powerpc/spapr_rtas.c b/tools/kvm/powerpc/spapr_rtas.c
> new file mode 100644
> index 0000000..72c6b02
> --- /dev/null
> +++ b/tools/kvm/powerpc/spapr_rtas.c
> @@ -0,0 +1,230 @@
> +/*
> + * SPAPR base RTAS calls
> + *
> + * Borrowed heavily from QEMU's spapr_rtas.c
> + * Copyright (c) 2010-2011 David Gibson, IBM Corporation.
> + *
> + * Modifications copyright 2011 Matt Evans <matt@xxxxxxxxxx>, IBM Corporation.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published
> + * by the Free Software Foundation.
> + */
> +
> +#include "kvm/kvm.h"
> +#include "kvm/kvm-cpu.h"
> +#include "kvm/util.h"
> +#include "kvm/term.h"
> +
> +#include "spapr.h"
> +
> +#include <stdio.h>
> +#include <assert.h>
> +#include <libfdt.h>
> +
> +#define TOKEN_BASE      0x2000
> +#define TOKEN_MAX       0x100
> +
> +#define RTAS_CONSOLE
> +
> +static struct rtas_call {
> +	const char *name;
> +	spapr_rtas_fn fn;
> +} rtas_table[TOKEN_MAX];
> +
> +struct rtas_call *rtas_next = rtas_table;
> +
> +
> +static void rtas_display_character(struct kvm_cpu *vcpu,
> +                                   uint32_t token, uint32_t nargs,
> +                                   target_ulong args,
> +                                   uint32_t nret, target_ulong rets)
> +{
> +	char c = rtas_ld(vcpu->kvm, args, 0);
> +	term_putc(CONSOLE_HV, &c, 1, 0);
> +	rtas_st(vcpu->kvm, rets, 0, 0);
> +}
> +
> +#ifdef RTAS_CONSOLE
> +static void rtas_put_term_char(struct kvm_cpu *vcpu,
> +			       uint32_t token, uint32_t nargs,
> +			       target_ulong args,
> +			       uint32_t nret, target_ulong rets)
> +{
> +	char c = rtas_ld(vcpu->kvm, args, 0);
> +	term_putc(CONSOLE_HV, &c, 1, 0);
> +	rtas_st(vcpu->kvm, rets, 0, 0);
> +}
> +
> +static void rtas_get_term_char(struct kvm_cpu *vcpu,
> +			       uint32_t token, uint32_t nargs,
> +			       target_ulong args,
> +			       uint32_t nret, target_ulong rets)
> +{
> +	int c;
> +	if (term_readable(CONSOLE_HV, 0) &&
> +	    (c = term_getc(CONSOLE_HV, 0)) >= 0) {
> +		rtas_st(vcpu->kvm, rets, 0, 0);
> +		rtas_st(vcpu->kvm, rets, 1, c);
> +	} else {
> +		rtas_st(vcpu->kvm, rets, 0, -2);
> +	}
> +}
> +#endif
> +
> +static void rtas_get_time_of_day(struct kvm_cpu *vcpu,
> +                                 uint32_t token, uint32_t nargs,
> +                                 target_ulong args,
> +                                 uint32_t nret, target_ulong rets)
> +{
> +	struct tm tm;
> +	time_t tnow;
> +
> +	if (nret != 8) {
> +		rtas_st(vcpu->kvm, rets, 0, -3);
> +		return;
> +	}
> +
> +	tnow = time(NULL);
> +	/* Guest time is currently not offset in any way. */
> +	gmtime_r(&tnow, &tm);
> +
> +	rtas_st(vcpu->kvm, rets, 0, 0); /* Success */
> +	rtas_st(vcpu->kvm, rets, 1, tm.tm_year + 1900);
> +	rtas_st(vcpu->kvm, rets, 2, tm.tm_mon + 1);
> +	rtas_st(vcpu->kvm, rets, 3, tm.tm_mday);
> +	rtas_st(vcpu->kvm, rets, 4, tm.tm_hour);
> +	rtas_st(vcpu->kvm, rets, 5, tm.tm_min);
> +	rtas_st(vcpu->kvm, rets, 6, tm.tm_sec);
> +	rtas_st(vcpu->kvm, rets, 7, 0);
> +}
> +
> +static void rtas_set_time_of_day(struct kvm_cpu *vcpu,
> +                                 uint32_t token, uint32_t nargs,
> +                                 target_ulong args,
> +                                 uint32_t nret, target_ulong rets)
> +{
> +	pr_warning("%s called; TOD set ignored.\n", __FUNCTION__);
> +}
> +
> +static void rtas_power_off(struct kvm_cpu *vcpu,
> +                           uint32_t token, uint32_t nargs, target_ulong args,
> +                           uint32_t nret, target_ulong rets)
> +{
> +	if (nargs != 2 || nret != 1) {
> +		rtas_st(vcpu->kvm, rets, 0, -3);
> +		return;
> +	}
> +	kvm_cpu__reboot();
> +}
> +
> +static void rtas_query_cpu_stopped_state(struct kvm_cpu *vcpu,
> +                                         uint32_t token, uint32_t nargs,
> +                                         target_ulong args,
> +                                         uint32_t nret, target_ulong rets)
> +{
> +	unsigned long id;
> +
> +	if (nargs != 1 || nret != 2) {
> +		rtas_st(vcpu->kvm, rets, 0, -3);
> +		return;
> +	}
> +
> +	id = rtas_ld(vcpu->kvm, args, 0);
> +
> +	/* We do start all CPUs.  So just return true. */
> +	rtas_st(vcpu->kvm, rets, 0, 0);
> +	rtas_st(vcpu->kvm, rets, 1, 2);
> +}
> +
> +static void rtas_start_cpu(struct kvm_cpu *vcpu,
> +                           uint32_t token, uint32_t nargs,
> +                           target_ulong args,
> +                           uint32_t nret, target_ulong rets)
> +{
> +	die(__FUNCTION__);
> +}
> +
> +target_ulong spapr_rtas_call(struct kvm_cpu *vcpu,
> +                             uint32_t token, uint32_t nargs, target_ulong args,
> +                             uint32_t nret, target_ulong rets)
> +{
> +	if ((token >= TOKEN_BASE)
> +	    && ((token - TOKEN_BASE) < TOKEN_MAX)) {
> +		struct rtas_call *call = rtas_table + (token - TOKEN_BASE);
> +
> +		if (call->fn) {
> +			call->fn(vcpu, token, nargs, args, nret, rets);
> +			return H_SUCCESS;
> +		}
> +	}
> +
> +	/*
> +	 * HACK: Some Linux early debug code uses RTAS display-character,
> +	 * but assumes the token value is 0xa (which it is on some real
> +	 * machines) without looking it up in the device tree.  This
> +	 * special case makes this work
> +	 */
> +	if (token == 0xa) {
> +		rtas_display_character(vcpu, 0xa, nargs, args, nret, rets);
> +		return H_SUCCESS;
> +	}
> +
> +	hcall_dprintf("Unknown RTAS token 0x%x\n", token);
> +	rtas_st(vcpu->kvm, rets, 0, -3);
> +	return H_PARAMETER;
> +}
> +
> +void spapr_rtas_register(const char *name, spapr_rtas_fn fn)
> +{
> +	assert(rtas_next < (rtas_table + TOKEN_MAX));
> +
> +	rtas_next->name = name;
> +	rtas_next->fn = fn;
> +
> +	rtas_next++;
> +}
> +
> +/*
> + * This is called from the context of an open /rtas node, in order to add
> + * properties for the rtas call tokens.
> + */
> +int spapr_rtas_fdt_setup(struct kvm *kvm, void *fdt)
> +{
> +	int ret;
> +	int i;
> +
> +	for (i = 0; i < TOKEN_MAX; i++) {
> +		struct rtas_call *call = &rtas_table[i];
> +
> +		if (!call->fn) {
> +			continue;
> +		}
> +
> +		ret = fdt_property_cell(fdt, call->name, i + TOKEN_BASE);
> +
> +		if (ret < 0) {
> +			pr_warning("Couldn't add rtas token for %s: %s\n",
> +				   call->name, fdt_strerror(ret));
> +			return ret;
> +		}
> +
> +	}
> +	return 0;
> +}
> +
> +void register_core_rtas(void)
> +{
> +	spapr_rtas_register("display-character", rtas_display_character);
> +	spapr_rtas_register("get-time-of-day", rtas_get_time_of_day);
> +	spapr_rtas_register("set-time-of-day", rtas_set_time_of_day);
> +	spapr_rtas_register("power-off", rtas_power_off);
> +	spapr_rtas_register("query-cpu-stopped-state",
> +			    rtas_query_cpu_stopped_state);
> +	spapr_rtas_register("start-cpu", rtas_start_cpu);
> +#ifdef RTAS_CONSOLE
> +	/* These are unused: We do console I/O via hcalls, not rtas. */
> +	spapr_rtas_register("put-term-char", rtas_put_term_char);
> +	spapr_rtas_register("get-term-char", rtas_get_term_char);
> +#endif
> +}

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux