uboot/drivers/xen/hypervisor.c
<<
>>
Prefs
   1// SPDX-License-Identifier: MIT License
   2/*
   3 * hypervisor.c
   4 *
   5 * Communication to/from hypervisor.
   6 *
   7 * Copyright (c) 2002-2003, K A Fraser
   8 * Copyright (c) 2005, Grzegorz Milos, gm281@cam.ac.uk,Intel Research Cambridge
   9 * Copyright (c) 2020, EPAM Systems Inc.
  10 */
  11#include <common.h>
  12#include <cpu_func.h>
  13#include <log.h>
  14#include <memalign.h>
  15
  16#include <asm/io.h>
  17#include <asm/armv8/mmu.h>
  18#include <asm/xen/system.h>
  19
  20#include <linux/bug.h>
  21
  22#include <xen/hvm.h>
  23#include <xen/events.h>
  24#include <xen/gnttab.h>
  25#include <xen/xenbus.h>
  26#include <xen/interface/memory.h>
  27
  28#define active_evtchns(cpu, sh, idx)    \
  29        ((sh)->evtchn_pending[idx] &    \
  30         ~(sh)->evtchn_mask[idx])
  31
  32int in_callback;
  33
  34/*
  35 * Shared page for communicating with the hypervisor.
  36 * Events flags go here, for example.
  37 */
  38struct shared_info *HYPERVISOR_shared_info;
  39
  40static const char *param_name(int op)
  41{
  42#define PARAM(x)[HVM_PARAM_##x] = #x
  43        static const char *const names[] = {
  44                PARAM(CALLBACK_IRQ),
  45                PARAM(STORE_PFN),
  46                PARAM(STORE_EVTCHN),
  47                PARAM(PAE_ENABLED),
  48                PARAM(IOREQ_PFN),
  49                PARAM(VPT_ALIGN),
  50                PARAM(CONSOLE_PFN),
  51                PARAM(CONSOLE_EVTCHN),
  52        };
  53#undef PARAM
  54
  55        if (op >= ARRAY_SIZE(names))
  56                return "unknown";
  57
  58        if (!names[op])
  59                return "reserved";
  60
  61        return names[op];
  62}
  63
  64/**
  65 * hvm_get_parameter_maintain_dcache - function to obtain a HVM
  66 * parameter value.
  67 * @idx: HVM parameter index
  68 * @value: Value to fill in
  69 *
  70 * According to Xen on ARM ABI (xen/include/public/arch-arm.h):
  71 * all memory which is shared with other entities in the system
  72 * (including the hypervisor and other guests) must reside in memory
  73 * which is mapped as Normal Inner Write-Back Outer Write-Back
  74 * Inner-Shareable.
  75 *
  76 * Thus, page attributes must be equally set for all the entities
  77 * working with that page.
  78 *
  79 * Before MMU setup the data cache is turned off, so it means that
  80 * manual data cache maintenance is required, because of the
  81 * difference of page attributes.
  82 */
  83int hvm_get_parameter_maintain_dcache(int idx, uint64_t *value)
  84{
  85        struct xen_hvm_param xhv;
  86        int ret;
  87
  88        invalidate_dcache_range((unsigned long)&xhv,
  89                                (unsigned long)&xhv + sizeof(xhv));
  90        xhv.domid = DOMID_SELF;
  91        xhv.index = idx;
  92        invalidate_dcache_range((unsigned long)&xhv,
  93                                (unsigned long)&xhv + sizeof(xhv));
  94
  95        ret = HYPERVISOR_hvm_op(HVMOP_get_param, &xhv);
  96        if (ret < 0) {
  97                pr_err("Cannot get hvm parameter %s (%d): %d!\n",
  98                           param_name(idx), idx, ret);
  99                BUG();
 100        }
 101        invalidate_dcache_range((unsigned long)&xhv,
 102                                (unsigned long)&xhv + sizeof(xhv));
 103
 104        *value = xhv.value;
 105
 106        return ret;
 107}
 108
 109int hvm_get_parameter(int idx, uint64_t *value)
 110{
 111        struct xen_hvm_param xhv;
 112        int ret;
 113
 114        xhv.domid = DOMID_SELF;
 115        xhv.index = idx;
 116        ret = HYPERVISOR_hvm_op(HVMOP_get_param, &xhv);
 117        if (ret < 0) {
 118                pr_err("Cannot get hvm parameter %s (%d): %d!\n",
 119                           param_name(idx), idx, ret);
 120                BUG();
 121        }
 122
 123        *value = xhv.value;
 124
 125        return ret;
 126}
 127
 128struct shared_info *map_shared_info(void *p)
 129{
 130        struct xen_add_to_physmap xatp;
 131
 132        HYPERVISOR_shared_info = (struct shared_info *)memalign(PAGE_SIZE,
 133                                                                PAGE_SIZE);
 134        if (!HYPERVISOR_shared_info)
 135                BUG();
 136
 137        xatp.domid = DOMID_SELF;
 138        xatp.idx = 0;
 139        xatp.space = XENMAPSPACE_shared_info;
 140        xatp.gpfn = virt_to_pfn(HYPERVISOR_shared_info);
 141        if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp) != 0)
 142                BUG();
 143
 144        return HYPERVISOR_shared_info;
 145}
 146
 147void unmap_shared_info(void)
 148{
 149        xen_pfn_t shared_info_pfn = virt_to_pfn(HYPERVISOR_shared_info);
 150        struct xen_remove_from_physmap xrfp = {0};
 151        struct xen_memory_reservation reservation = {0};
 152        xen_ulong_t nr_exts = 1;
 153
 154        xrfp.domid = DOMID_SELF;
 155        xrfp.gpfn = shared_info_pfn;
 156        if (HYPERVISOR_memory_op(XENMEM_remove_from_physmap, &xrfp) != 0)
 157                panic("Failed to unmap HYPERVISOR_shared_info\n");
 158
 159        /*
 160         * After removing from physmap there will be a hole in address space on
 161         * HYPERVISOR_shared_info address, so to free memory allocated with
 162         * memalign and prevent exceptions during access to this page we need to
 163         * fill this 4KB hole with XENMEM_populate_physmap before jumping to Linux.
 164         */
 165        reservation.domid = DOMID_SELF;
 166        reservation.extent_order = 0;
 167        reservation.address_bits = 0;
 168        set_xen_guest_handle(reservation.extent_start, &shared_info_pfn);
 169        reservation.nr_extents = nr_exts;
 170        if (HYPERVISOR_memory_op(XENMEM_populate_physmap, &reservation) != nr_exts)
 171                panic("Failed to populate memory on HYPERVISOR_shared_info addr\n");
 172
 173        /* Now we can return this to memory allocator */
 174        free(HYPERVISOR_shared_info);
 175}
 176
 177void do_hypervisor_callback(struct pt_regs *regs)
 178{
 179        unsigned long l1, l2, l1i, l2i;
 180        unsigned int port;
 181        int cpu = 0;
 182        struct shared_info *s = HYPERVISOR_shared_info;
 183        struct vcpu_info *vcpu_info = &s->vcpu_info[cpu];
 184
 185        in_callback = 1;
 186
 187        vcpu_info->evtchn_upcall_pending = 0;
 188        l1 = xchg(&vcpu_info->evtchn_pending_sel, 0);
 189
 190        while (l1 != 0) {
 191                l1i = __ffs(l1);
 192                l1 &= ~(1UL << l1i);
 193
 194                while ((l2 = active_evtchns(cpu, s, l1i)) != 0) {
 195                        l2i = __ffs(l2);
 196                        l2 &= ~(1UL << l2i);
 197
 198                        port = (l1i * (sizeof(unsigned long) * 8)) + l2i;
 199                        do_event(port, regs);
 200                }
 201        }
 202
 203        in_callback = 0;
 204}
 205
 206void force_evtchn_callback(void)
 207{
 208#ifdef XEN_HAVE_PV_UPCALL_MASK
 209        int save;
 210#endif
 211        struct vcpu_info *vcpu;
 212
 213        vcpu = &HYPERVISOR_shared_info->vcpu_info[smp_processor_id()];
 214#ifdef XEN_HAVE_PV_UPCALL_MASK
 215        save = vcpu->evtchn_upcall_mask;
 216#endif
 217
 218        while (vcpu->evtchn_upcall_pending) {
 219#ifdef XEN_HAVE_PV_UPCALL_MASK
 220                vcpu->evtchn_upcall_mask = 1;
 221#endif
 222                do_hypervisor_callback(NULL);
 223#ifdef XEN_HAVE_PV_UPCALL_MASK
 224                vcpu->evtchn_upcall_mask = save;
 225#endif
 226        };
 227}
 228
 229void mask_evtchn(uint32_t port)
 230{
 231        struct shared_info *s = HYPERVISOR_shared_info;
 232
 233        synch_set_bit(port, &s->evtchn_mask[0]);
 234}
 235
 236void unmask_evtchn(uint32_t port)
 237{
 238        struct shared_info *s = HYPERVISOR_shared_info;
 239        struct vcpu_info *vcpu_info = &s->vcpu_info[smp_processor_id()];
 240
 241        synch_clear_bit(port, &s->evtchn_mask[0]);
 242
 243        /*
 244         * Just like a real IO-APIC we 'lose the interrupt edge' if the
 245         * channel is masked.
 246         */
 247        if (synch_test_bit(port, &s->evtchn_pending[0]) &&
 248            !synch_test_and_set_bit(port / (sizeof(unsigned long) * 8),
 249                                    &vcpu_info->evtchn_pending_sel)) {
 250                vcpu_info->evtchn_upcall_pending = 1;
 251#ifdef XEN_HAVE_PV_UPCALL_MASK
 252                if (!vcpu_info->evtchn_upcall_mask)
 253#endif
 254                        force_evtchn_callback();
 255        }
 256}
 257
 258void clear_evtchn(uint32_t port)
 259{
 260        struct shared_info *s = HYPERVISOR_shared_info;
 261
 262        synch_clear_bit(port, &s->evtchn_pending[0]);
 263}
 264
 265int xen_init(void)
 266{
 267        debug("%s\n", __func__);
 268
 269        map_shared_info(NULL);
 270        init_events();
 271        init_xenbus();
 272        init_gnttab();
 273
 274        return 0;
 275}
 276
 277void xen_fini(void)
 278{
 279        debug("%s\n", __func__);
 280
 281        fini_gnttab();
 282        fini_xenbus();
 283        fini_events();
 284        unmap_shared_info();
 285}
 286