qemu

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

pnv_sbe.c (12960B)


      1 /*
      2  * QEMU PowerPC PowerNV Emulation of some SBE behaviour
      3  *
      4  * Copyright (c) 2022, IBM Corporation.
      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, as
      8  * published by the Free Software Foundation.
      9  *
     10  * This program is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  * GNU General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU General Public License
     16  * along with this program; if not, see <http://www.gnu.org/licenses/>.
     17  */
     18 
     19 #include "qemu/osdep.h"
     20 #include "target/ppc/cpu.h"
     21 #include "qapi/error.h"
     22 #include "qemu/log.h"
     23 #include "qemu/module.h"
     24 #include "hw/irq.h"
     25 #include "hw/qdev-properties.h"
     26 #include "hw/ppc/pnv.h"
     27 #include "hw/ppc/pnv_xscom.h"
     28 #include "hw/ppc/pnv_sbe.h"
     29 #include "trace.h"
     30 
     31 /*
     32  * Most register and command definitions come from skiboot.
     33  *
     34  * xscom addresses are adjusted to be relative to xscom subregion bases
     35  */
     36 
     37 /*
     38  * SBE MBOX register address
     39  *   Reg 0 - 3 : Host to send command packets to SBE
     40  *   Reg 4 - 7 : SBE to send response packets to Host
     41  */
     42 #define PSU_HOST_SBE_MBOX_REG0          0x00000000
     43 #define PSU_HOST_SBE_MBOX_REG1          0x00000001
     44 #define PSU_HOST_SBE_MBOX_REG2          0x00000002
     45 #define PSU_HOST_SBE_MBOX_REG3          0x00000003
     46 #define PSU_HOST_SBE_MBOX_REG4          0x00000004
     47 #define PSU_HOST_SBE_MBOX_REG5          0x00000005
     48 #define PSU_HOST_SBE_MBOX_REG6          0x00000006
     49 #define PSU_HOST_SBE_MBOX_REG7          0x00000007
     50 #define PSU_SBE_DOORBELL_REG_RW         0x00000010
     51 #define PSU_SBE_DOORBELL_REG_AND        0x00000011
     52 #define PSU_SBE_DOORBELL_REG_OR         0x00000012
     53 #define PSU_HOST_DOORBELL_REG_RW        0x00000013
     54 #define PSU_HOST_DOORBELL_REG_AND       0x00000014
     55 #define PSU_HOST_DOORBELL_REG_OR        0x00000015
     56 
     57 /*
     58  * Doorbell register to trigger SBE interrupt. Set by OPAL to inform
     59  * the SBE about a waiting message in the Host/SBE mailbox registers
     60  */
     61 #define HOST_SBE_MSG_WAITING            PPC_BIT(0)
     62 
     63 /*
     64  * Doorbell register for host bridge interrupt. Set by the SBE to inform
     65  * host about a response message in the Host/SBE mailbox registers
     66  */
     67 #define SBE_HOST_RESPONSE_WAITING       PPC_BIT(0)
     68 #define SBE_HOST_MSG_READ               PPC_BIT(1)
     69 #define SBE_HOST_STOP15_EXIT            PPC_BIT(2)
     70 #define SBE_HOST_RESET                  PPC_BIT(3)
     71 #define SBE_HOST_PASSTHROUGH            PPC_BIT(4)
     72 #define SBE_HOST_TIMER_EXPIRY           PPC_BIT(14)
     73 #define SBE_HOST_RESPONSE_MASK          (PPC_BITMASK(0, 4) | \
     74                                          SBE_HOST_TIMER_EXPIRY)
     75 
     76 /* SBE Control Register */
     77 #define SBE_CONTROL_REG_RW              0x00000000
     78 
     79 /* SBE interrupt s0/s1 bits */
     80 #define SBE_CONTROL_REG_S0              PPC_BIT(14)
     81 #define SBE_CONTROL_REG_S1              PPC_BIT(15)
     82 
     83 struct sbe_msg {
     84     uint64_t reg[4];
     85 };
     86 
     87 static uint64_t pnv_sbe_power9_xscom_ctrl_read(void *opaque, hwaddr addr,
     88                                           unsigned size)
     89 {
     90     uint32_t offset = addr >> 3;
     91     uint64_t val = 0;
     92 
     93     switch (offset) {
     94     default:
     95         qemu_log_mask(LOG_UNIMP, "SBE Unimplemented register: Ox%"
     96                       HWADDR_PRIx "\n", addr >> 3);
     97     }
     98 
     99     trace_pnv_sbe_xscom_ctrl_read(addr, val);
    100 
    101     return val;
    102 }
    103 
    104 static void pnv_sbe_power9_xscom_ctrl_write(void *opaque, hwaddr addr,
    105                                        uint64_t val, unsigned size)
    106 {
    107     uint32_t offset = addr >> 3;
    108 
    109     trace_pnv_sbe_xscom_ctrl_write(addr, val);
    110 
    111     switch (offset) {
    112     default:
    113         qemu_log_mask(LOG_UNIMP, "SBE Unimplemented register: Ox%"
    114                       HWADDR_PRIx "\n", addr >> 3);
    115     }
    116 }
    117 
    118 static const MemoryRegionOps pnv_sbe_power9_xscom_ctrl_ops = {
    119     .read = pnv_sbe_power9_xscom_ctrl_read,
    120     .write = pnv_sbe_power9_xscom_ctrl_write,
    121     .valid.min_access_size = 8,
    122     .valid.max_access_size = 8,
    123     .impl.min_access_size = 8,
    124     .impl.max_access_size = 8,
    125     .endianness = DEVICE_BIG_ENDIAN,
    126 };
    127 
    128 static void pnv_sbe_set_host_doorbell(PnvSBE *sbe, uint64_t val)
    129 {
    130     val &= SBE_HOST_RESPONSE_MASK; /* Is this right? What does HW do? */
    131     sbe->host_doorbell = val;
    132 
    133     trace_pnv_sbe_reg_set_host_doorbell(val);
    134     qemu_set_irq(sbe->psi_irq, !!val);
    135 }
    136 
    137 /* SBE Target Type */
    138 #define SBE_TARGET_TYPE_PROC            0x00
    139 #define SBE_TARGET_TYPE_EX              0x01
    140 #define SBE_TARGET_TYPE_PERV            0x02
    141 #define SBE_TARGET_TYPE_MCS             0x03
    142 #define SBE_TARGET_TYPE_EQ              0x04
    143 #define SBE_TARGET_TYPE_CORE            0x05
    144 
    145 /* SBE MBOX command class */
    146 #define SBE_MCLASS_FIRST                0xD1
    147 #define SBE_MCLASS_CORE_STATE           0xD1
    148 #define SBE_MCLASS_SCOM                 0xD2
    149 #define SBE_MCLASS_RING                 0xD3
    150 #define SBE_MCLASS_TIMER                0xD4
    151 #define SBE_MCLASS_MPIPL                0xD5
    152 #define SBE_MCLASS_SECURITY             0xD6
    153 #define SBE_MCLASS_GENERIC              0xD7
    154 #define SBE_MCLASS_LAST                 0xD7
    155 
    156 /*
    157  * Commands are provided in xxyy form where:
    158  *   - xx : command class
    159  *   - yy : command
    160  *
    161  * Both request and response message uses same seq ID,
    162  * command class and command.
    163  */
    164 #define SBE_CMD_CTRL_DEADMAN_LOOP       0xD101
    165 #define SBE_CMD_MULTI_SCOM              0xD201
    166 #define SBE_CMD_PUT_RING_FORM_IMAGE     0xD301
    167 #define SBE_CMD_CONTROL_TIMER           0xD401
    168 #define SBE_CMD_GET_ARCHITECTED_REG     0xD501
    169 #define SBE_CMD_CLR_ARCHITECTED_REG     0xD502
    170 #define SBE_CMD_SET_UNSEC_MEM_WINDOW    0xD601
    171 #define SBE_CMD_GET_SBE_FFDC            0xD701
    172 #define SBE_CMD_GET_CAPABILITY          0xD702
    173 #define SBE_CMD_READ_SBE_SEEPROM        0xD703
    174 #define SBE_CMD_SET_FFDC_ADDR           0xD704
    175 #define SBE_CMD_QUIESCE_SBE             0xD705
    176 #define SBE_CMD_SET_FABRIC_ID_MAP       0xD706
    177 #define SBE_CMD_STASH_MPIPL_CONFIG      0xD707
    178 
    179 /* SBE MBOX control flags */
    180 
    181 /* Generic flags */
    182 #define SBE_CMD_CTRL_RESP_REQ           0x0100
    183 #define SBE_CMD_CTRL_ACK_REQ            0x0200
    184 
    185 /* Deadman loop */
    186 #define CTRL_DEADMAN_LOOP_START         0x0001
    187 #define CTRL_DEADMAN_LOOP_STOP          0x0002
    188 
    189 /* Control timer */
    190 #define CONTROL_TIMER_START             0x0001
    191 #define CONTROL_TIMER_STOP              0x0002
    192 
    193 /* Stash MPIPL config */
    194 #define SBE_STASH_KEY_SKIBOOT_BASE      0x03
    195 
    196 static void sbe_timer(void *opaque)
    197 {
    198     PnvSBE *sbe = opaque;
    199 
    200     trace_pnv_sbe_cmd_timer_expired();
    201 
    202     pnv_sbe_set_host_doorbell(sbe, sbe->host_doorbell | SBE_HOST_TIMER_EXPIRY);
    203 }
    204 
    205 static void do_sbe_msg(PnvSBE *sbe)
    206 {
    207     struct sbe_msg msg;
    208     uint16_t cmd, ctrl_flags, seq_id;
    209     int i;
    210 
    211     memset(&msg, 0, sizeof(msg));
    212 
    213     for (i = 0; i < 4; i++) {
    214         msg.reg[i] = sbe->mbox[i];
    215     }
    216 
    217     cmd = msg.reg[0];
    218     seq_id = msg.reg[0] >> 16;
    219     ctrl_flags = msg.reg[0] >> 32;
    220 
    221     trace_pnv_sbe_msg_recv(cmd, seq_id, ctrl_flags);
    222 
    223     if (ctrl_flags & SBE_CMD_CTRL_ACK_REQ) {
    224         pnv_sbe_set_host_doorbell(sbe, sbe->host_doorbell | SBE_HOST_MSG_READ);
    225     }
    226 
    227     switch (cmd) {
    228     case SBE_CMD_CONTROL_TIMER:
    229         if (ctrl_flags & CONTROL_TIMER_START) {
    230             uint64_t us = msg.reg[1];
    231             trace_pnv_sbe_cmd_timer_start(us);
    232             timer_mod(sbe->timer, qemu_clock_get_us(QEMU_CLOCK_VIRTUAL) + us);
    233         }
    234         if (ctrl_flags & CONTROL_TIMER_STOP) {
    235             trace_pnv_sbe_cmd_timer_stop();
    236             timer_del(sbe->timer);
    237         }
    238         break;
    239     default:
    240         qemu_log_mask(LOG_UNIMP, "SBE Unimplemented command: 0x%x\n", cmd);
    241     }
    242 }
    243 
    244 static void pnv_sbe_set_sbe_doorbell(PnvSBE *sbe, uint64_t val)
    245 {
    246     val &= HOST_SBE_MSG_WAITING;
    247     sbe->sbe_doorbell = val;
    248 
    249     if (val & HOST_SBE_MSG_WAITING) {
    250         sbe->sbe_doorbell &= ~HOST_SBE_MSG_WAITING;
    251         do_sbe_msg(sbe);
    252     }
    253 }
    254 
    255 static uint64_t pnv_sbe_power9_xscom_mbox_read(void *opaque, hwaddr addr,
    256                                           unsigned size)
    257 {
    258     PnvSBE *sbe = PNV_SBE(opaque);
    259     uint32_t offset = addr >> 3;
    260     uint64_t val = 0;
    261 
    262     if (offset <= PSU_HOST_SBE_MBOX_REG7) {
    263         uint32_t idx = offset - PSU_HOST_SBE_MBOX_REG0;
    264         val = sbe->mbox[idx];
    265     } else {
    266         switch (offset) {
    267         case PSU_SBE_DOORBELL_REG_RW:
    268             val = sbe->sbe_doorbell;
    269             break;
    270         case PSU_HOST_DOORBELL_REG_RW:
    271             val = sbe->host_doorbell;
    272             break;
    273         default:
    274             qemu_log_mask(LOG_UNIMP, "SBE Unimplemented register: Ox%"
    275                           HWADDR_PRIx "\n", addr >> 3);
    276         }
    277     }
    278 
    279     trace_pnv_sbe_xscom_mbox_read(addr, val);
    280 
    281     return val;
    282 }
    283 
    284 static void pnv_sbe_power9_xscom_mbox_write(void *opaque, hwaddr addr,
    285                                        uint64_t val, unsigned size)
    286 {
    287     PnvSBE *sbe = PNV_SBE(opaque);
    288     uint32_t offset = addr >> 3;
    289 
    290     trace_pnv_sbe_xscom_mbox_write(addr, val);
    291 
    292     if (offset <= PSU_HOST_SBE_MBOX_REG7) {
    293         uint32_t idx = offset - PSU_HOST_SBE_MBOX_REG0;
    294         sbe->mbox[idx] = val;
    295     } else {
    296         switch (offset) {
    297         case PSU_SBE_DOORBELL_REG_RW:
    298             pnv_sbe_set_sbe_doorbell(sbe, val);
    299             break;
    300         case PSU_SBE_DOORBELL_REG_AND:
    301             pnv_sbe_set_sbe_doorbell(sbe, sbe->sbe_doorbell & val);
    302             break;
    303         case PSU_SBE_DOORBELL_REG_OR:
    304             pnv_sbe_set_sbe_doorbell(sbe, sbe->sbe_doorbell | val);
    305             break;
    306 
    307         case PSU_HOST_DOORBELL_REG_RW:
    308             pnv_sbe_set_host_doorbell(sbe, val);
    309             break;
    310         case PSU_HOST_DOORBELL_REG_AND:
    311             pnv_sbe_set_host_doorbell(sbe, sbe->host_doorbell & val);
    312             break;
    313         case PSU_HOST_DOORBELL_REG_OR:
    314             pnv_sbe_set_host_doorbell(sbe, sbe->host_doorbell | val);
    315             break;
    316 
    317         default:
    318             qemu_log_mask(LOG_UNIMP, "SBE Unimplemented register: Ox%"
    319                           HWADDR_PRIx "\n", addr >> 3);
    320         }
    321     }
    322 }
    323 
    324 static const MemoryRegionOps pnv_sbe_power9_xscom_mbox_ops = {
    325     .read = pnv_sbe_power9_xscom_mbox_read,
    326     .write = pnv_sbe_power9_xscom_mbox_write,
    327     .valid.min_access_size = 8,
    328     .valid.max_access_size = 8,
    329     .impl.min_access_size = 8,
    330     .impl.max_access_size = 8,
    331     .endianness = DEVICE_BIG_ENDIAN,
    332 };
    333 
    334 static void pnv_sbe_power9_class_init(ObjectClass *klass, void *data)
    335 {
    336     PnvSBEClass *psc = PNV_SBE_CLASS(klass);
    337     DeviceClass *dc = DEVICE_CLASS(klass);
    338 
    339     dc->desc = "PowerNV SBE Controller (POWER9)";
    340     psc->xscom_ctrl_size = PNV9_XSCOM_SBE_CTRL_SIZE;
    341     psc->xscom_ctrl_ops = &pnv_sbe_power9_xscom_ctrl_ops;
    342     psc->xscom_mbox_size = PNV9_XSCOM_SBE_MBOX_SIZE;
    343     psc->xscom_mbox_ops = &pnv_sbe_power9_xscom_mbox_ops;
    344 }
    345 
    346 static const TypeInfo pnv_sbe_power9_type_info = {
    347     .name          = TYPE_PNV9_SBE,
    348     .parent        = TYPE_PNV_SBE,
    349     .instance_size = sizeof(PnvSBE),
    350     .class_init    = pnv_sbe_power9_class_init,
    351 };
    352 
    353 static void pnv_sbe_power10_class_init(ObjectClass *klass, void *data)
    354 {
    355     PnvSBEClass *psc = PNV_SBE_CLASS(klass);
    356     DeviceClass *dc = DEVICE_CLASS(klass);
    357 
    358     dc->desc = "PowerNV SBE Controller (POWER10)";
    359     psc->xscom_ctrl_size = PNV10_XSCOM_SBE_CTRL_SIZE;
    360     psc->xscom_ctrl_ops = &pnv_sbe_power9_xscom_ctrl_ops;
    361     psc->xscom_mbox_size = PNV10_XSCOM_SBE_MBOX_SIZE;
    362     psc->xscom_mbox_ops = &pnv_sbe_power9_xscom_mbox_ops;
    363 }
    364 
    365 static const TypeInfo pnv_sbe_power10_type_info = {
    366     .name          = TYPE_PNV10_SBE,
    367     .parent        = TYPE_PNV9_SBE,
    368     .class_init    = pnv_sbe_power10_class_init,
    369 };
    370 
    371 static void pnv_sbe_realize(DeviceState *dev, Error **errp)
    372 {
    373     PnvSBE *sbe = PNV_SBE(dev);
    374     PnvSBEClass *psc = PNV_SBE_GET_CLASS(sbe);
    375 
    376     /* XScom regions for SBE registers */
    377     pnv_xscom_region_init(&sbe->xscom_ctrl_regs, OBJECT(dev),
    378                           psc->xscom_ctrl_ops, sbe, "xscom-sbe-ctrl",
    379                           psc->xscom_ctrl_size);
    380     pnv_xscom_region_init(&sbe->xscom_mbox_regs, OBJECT(dev),
    381                           psc->xscom_mbox_ops, sbe, "xscom-sbe-mbox",
    382                           psc->xscom_mbox_size);
    383 
    384     qdev_init_gpio_out(DEVICE(dev), &sbe->psi_irq, 1);
    385 
    386     sbe->timer = timer_new_us(QEMU_CLOCK_VIRTUAL, sbe_timer, sbe);
    387 }
    388 
    389 static void pnv_sbe_class_init(ObjectClass *klass, void *data)
    390 {
    391     DeviceClass *dc = DEVICE_CLASS(klass);
    392 
    393     dc->realize = pnv_sbe_realize;
    394     dc->desc = "PowerNV SBE Controller";
    395     dc->user_creatable = false;
    396 }
    397 
    398 static const TypeInfo pnv_sbe_type_info = {
    399     .name          = TYPE_PNV_SBE,
    400     .parent        = TYPE_DEVICE,
    401     .instance_size = sizeof(PnvSBE),
    402     .class_init    = pnv_sbe_class_init,
    403     .class_size    = sizeof(PnvSBEClass),
    404     .abstract      = true,
    405 };
    406 
    407 static void pnv_sbe_register_types(void)
    408 {
    409     type_register_static(&pnv_sbe_type_info);
    410     type_register_static(&pnv_sbe_power9_type_info);
    411     type_register_static(&pnv_sbe_power10_type_info);
    412 }
    413 
    414 type_init(pnv_sbe_register_types);