qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

ppce500_spin.c (5503B)


      1 /*
      2  * QEMU PowerPC e500v2 ePAPR spinning code
      3  *
      4  * Copyright (C) 2011 Freescale Semiconductor, Inc. All rights reserved.
      5  *
      6  * Author: Alexander Graf, <agraf@suse.de>
      7  *
      8  * This library is free software; you can redistribute it and/or
      9  * modify it under the terms of the GNU Lesser General Public
     10  * License as published by the Free Software Foundation; either
     11  * version 2.1 of the License, or (at your option) any later version.
     12  *
     13  * This library is distributed in the hope that it will be useful,
     14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     16  * Lesser General Public License for more details.
     17  *
     18  * You should have received a copy of the GNU Lesser General Public
     19  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
     20  *
     21  * This code is not really a device, but models an interface that usually
     22  * firmware takes care of. It's used when QEMU plays the role of firmware.
     23  *
     24  * Specification:
     25  *
     26  * https://www.power.org/resources/downloads/Power_ePAPR_APPROVED_v1.1.pdf
     27  *
     28  */
     29 
     30 #include "qemu/osdep.h"
     31 #include "qemu/module.h"
     32 #include "qemu/units.h"
     33 #include "hw/hw.h"
     34 #include "hw/sysbus.h"
     35 #include "sysemu/hw_accel.h"
     36 #include "e500.h"
     37 #include "qom/object.h"
     38 
     39 #define MAX_CPUS 32
     40 
     41 typedef struct spin_info {
     42     uint64_t addr;
     43     uint64_t r3;
     44     uint32_t resv;
     45     uint32_t pir;
     46     uint64_t reserved;
     47 } QEMU_PACKED SpinInfo;
     48 
     49 #define TYPE_E500_SPIN "e500-spin"
     50 OBJECT_DECLARE_SIMPLE_TYPE(SpinState, E500_SPIN)
     51 
     52 struct SpinState {
     53     SysBusDevice parent_obj;
     54 
     55     MemoryRegion iomem;
     56     SpinInfo spin[MAX_CPUS];
     57 };
     58 
     59 static void spin_reset(DeviceState *dev)
     60 {
     61     SpinState *s = E500_SPIN(dev);
     62     int i;
     63 
     64     for (i = 0; i < MAX_CPUS; i++) {
     65         SpinInfo *info = &s->spin[i];
     66 
     67         stl_p(&info->pir, i);
     68         stq_p(&info->r3, i);
     69         stq_p(&info->addr, 1);
     70     }
     71 }
     72 
     73 static void mmubooke_create_initial_mapping(CPUPPCState *env,
     74                                      target_ulong va,
     75                                      hwaddr pa,
     76                                      hwaddr len)
     77 {
     78     ppcmas_tlb_t *tlb = booke206_get_tlbm(env, 1, 0, 1);
     79     hwaddr size;
     80 
     81     size = (booke206_page_size_to_tlb(len) << MAS1_TSIZE_SHIFT);
     82     tlb->mas1 = MAS1_VALID | size;
     83     tlb->mas2 = (va & TARGET_PAGE_MASK) | MAS2_M;
     84     tlb->mas7_3 = pa & TARGET_PAGE_MASK;
     85     tlb->mas7_3 |= MAS3_UR | MAS3_UW | MAS3_UX | MAS3_SR | MAS3_SW | MAS3_SX;
     86     env->tlb_dirty = true;
     87 }
     88 
     89 static void spin_kick(CPUState *cs, run_on_cpu_data data)
     90 {
     91     PowerPCCPU *cpu = POWERPC_CPU(cs);
     92     CPUPPCState *env = &cpu->env;
     93     SpinInfo *curspin = data.host_ptr;
     94     hwaddr map_size = 64 * MiB;
     95     hwaddr map_start;
     96 
     97     cpu_synchronize_state(cs);
     98     stl_p(&curspin->pir, env->spr[SPR_BOOKE_PIR]);
     99     env->nip = ldq_p(&curspin->addr) & (map_size - 1);
    100     env->gpr[3] = ldq_p(&curspin->r3);
    101     env->gpr[4] = 0;
    102     env->gpr[5] = 0;
    103     env->gpr[6] = 0;
    104     env->gpr[7] = map_size;
    105     env->gpr[8] = 0;
    106     env->gpr[9] = 0;
    107 
    108     map_start = ldq_p(&curspin->addr) & ~(map_size - 1);
    109     mmubooke_create_initial_mapping(env, 0, map_start, map_size);
    110 
    111     cs->halted = 0;
    112     cs->exception_index = -1;
    113     cs->stopped = false;
    114     qemu_cpu_kick(cs);
    115 }
    116 
    117 static void spin_write(void *opaque, hwaddr addr, uint64_t value,
    118                        unsigned len)
    119 {
    120     SpinState *s = opaque;
    121     int env_idx = addr / sizeof(SpinInfo);
    122     CPUState *cpu;
    123     SpinInfo *curspin = &s->spin[env_idx];
    124     uint8_t *curspin_p = (uint8_t*)curspin;
    125 
    126     cpu = qemu_get_cpu(env_idx);
    127     if (cpu == NULL) {
    128         /* Unknown CPU */
    129         return;
    130     }
    131 
    132     if (cpu->cpu_index == 0) {
    133         /* primary CPU doesn't spin */
    134         return;
    135     }
    136 
    137     curspin_p = &curspin_p[addr % sizeof(SpinInfo)];
    138     switch (len) {
    139     case 1:
    140         stb_p(curspin_p, value);
    141         break;
    142     case 2:
    143         stw_p(curspin_p, value);
    144         break;
    145     case 4:
    146         stl_p(curspin_p, value);
    147         break;
    148     }
    149 
    150     if (!(ldq_p(&curspin->addr) & 1)) {
    151         /* run CPU */
    152         run_on_cpu(cpu, spin_kick, RUN_ON_CPU_HOST_PTR(curspin));
    153     }
    154 }
    155 
    156 static uint64_t spin_read(void *opaque, hwaddr addr, unsigned len)
    157 {
    158     SpinState *s = opaque;
    159     uint8_t *spin_p = &((uint8_t*)s->spin)[addr];
    160 
    161     switch (len) {
    162     case 1:
    163         return ldub_p(spin_p);
    164     case 2:
    165         return lduw_p(spin_p);
    166     case 4:
    167         return ldl_p(spin_p);
    168     default:
    169         hw_error("ppce500: unexpected %s with len = %u", __func__, len);
    170     }
    171 }
    172 
    173 static const MemoryRegionOps spin_rw_ops = {
    174     .read = spin_read,
    175     .write = spin_write,
    176     .endianness = DEVICE_BIG_ENDIAN,
    177 };
    178 
    179 static void ppce500_spin_initfn(Object *obj)
    180 {
    181     SysBusDevice *dev = SYS_BUS_DEVICE(obj);
    182     SpinState *s = E500_SPIN(dev);
    183 
    184     memory_region_init_io(&s->iomem, obj, &spin_rw_ops, s,
    185                           "e500 spin pv device", sizeof(SpinInfo) * MAX_CPUS);
    186     sysbus_init_mmio(dev, &s->iomem);
    187 }
    188 
    189 static void ppce500_spin_class_init(ObjectClass *klass, void *data)
    190 {
    191     DeviceClass *dc = DEVICE_CLASS(klass);
    192 
    193     dc->reset = spin_reset;
    194 }
    195 
    196 static const TypeInfo ppce500_spin_info = {
    197     .name          = TYPE_E500_SPIN,
    198     .parent        = TYPE_SYS_BUS_DEVICE,
    199     .instance_size = sizeof(SpinState),
    200     .instance_init = ppce500_spin_initfn,
    201     .class_init    = ppce500_spin_class_init,
    202 };
    203 
    204 static void ppce500_spin_register_types(void)
    205 {
    206     type_register_static(&ppce500_spin_info);
    207 }
    208 
    209 type_init(ppce500_spin_register_types)