qemu

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

bcm2835_property.c (14772B)


      1 /*
      2  * Raspberry Pi emulation (c) 2012 Gregory Estrade
      3  *
      4  * This work is licensed under the terms of the GNU GPL, version 2 or later.
      5  * See the COPYING file in the top-level directory.
      6  */
      7 
      8 #include "qemu/osdep.h"
      9 #include "qapi/error.h"
     10 #include "hw/misc/bcm2835_property.h"
     11 #include "hw/qdev-properties.h"
     12 #include "migration/vmstate.h"
     13 #include "hw/irq.h"
     14 #include "hw/misc/bcm2835_mbox_defs.h"
     15 #include "sysemu/dma.h"
     16 #include "qemu/log.h"
     17 #include "qemu/module.h"
     18 #include "trace.h"
     19 
     20 /* https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface */
     21 
     22 static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
     23 {
     24     uint32_t tag;
     25     uint32_t bufsize;
     26     uint32_t tot_len;
     27     size_t resplen;
     28     uint32_t tmp;
     29     int n;
     30     uint32_t offset, length, color;
     31 
     32     /*
     33      * Copy the current state of the framebuffer config; we will update
     34      * this copy as we process tags and then ask the framebuffer to use
     35      * it at the end.
     36      */
     37     BCM2835FBConfig fbconfig = s->fbdev->config;
     38     bool fbconfig_updated = false;
     39 
     40     value &= ~0xf;
     41 
     42     s->addr = value;
     43 
     44     tot_len = ldl_le_phys(&s->dma_as, value);
     45 
     46     /* @(addr + 4) : Buffer response code */
     47     value = s->addr + 8;
     48     while (value + 8 <= s->addr + tot_len) {
     49         tag = ldl_le_phys(&s->dma_as, value);
     50         bufsize = ldl_le_phys(&s->dma_as, value + 4);
     51         /* @(value + 8) : Request/response indicator */
     52         resplen = 0;
     53         switch (tag) {
     54         case 0x00000000: /* End tag */
     55             break;
     56         case 0x00000001: /* Get firmware revision */
     57             stl_le_phys(&s->dma_as, value + 12, 346337);
     58             resplen = 4;
     59             break;
     60         case 0x00010001: /* Get board model */
     61             qemu_log_mask(LOG_UNIMP,
     62                           "bcm2835_property: 0x%08x get board model NYI\n",
     63                           tag);
     64             resplen = 4;
     65             break;
     66         case 0x00010002: /* Get board revision */
     67             stl_le_phys(&s->dma_as, value + 12, s->board_rev);
     68             resplen = 4;
     69             break;
     70         case 0x00010003: /* Get board MAC address */
     71             resplen = sizeof(s->macaddr.a);
     72             dma_memory_write(&s->dma_as, value + 12, s->macaddr.a, resplen,
     73                              MEMTXATTRS_UNSPECIFIED);
     74             break;
     75         case 0x00010004: /* Get board serial */
     76             qemu_log_mask(LOG_UNIMP,
     77                           "bcm2835_property: 0x%08x get board serial NYI\n",
     78                           tag);
     79             resplen = 8;
     80             break;
     81         case 0x00010005: /* Get ARM memory */
     82             /* base */
     83             stl_le_phys(&s->dma_as, value + 12, 0);
     84             /* size */
     85             stl_le_phys(&s->dma_as, value + 16, s->fbdev->vcram_base);
     86             resplen = 8;
     87             break;
     88         case 0x00010006: /* Get VC memory */
     89             /* base */
     90             stl_le_phys(&s->dma_as, value + 12, s->fbdev->vcram_base);
     91             /* size */
     92             stl_le_phys(&s->dma_as, value + 16, s->fbdev->vcram_size);
     93             resplen = 8;
     94             break;
     95         case 0x00028001: /* Set power state */
     96             /* Assume that whatever device they asked for exists,
     97              * and we'll just claim we set it to the desired state
     98              */
     99             tmp = ldl_le_phys(&s->dma_as, value + 16);
    100             stl_le_phys(&s->dma_as, value + 16, (tmp & 1));
    101             resplen = 8;
    102             break;
    103 
    104         /* Clocks */
    105 
    106         case 0x00030001: /* Get clock state */
    107             stl_le_phys(&s->dma_as, value + 16, 0x1);
    108             resplen = 8;
    109             break;
    110 
    111         case 0x00038001: /* Set clock state */
    112             qemu_log_mask(LOG_UNIMP,
    113                           "bcm2835_property: 0x%08x set clock state NYI\n",
    114                           tag);
    115             resplen = 8;
    116             break;
    117 
    118         case 0x00030002: /* Get clock rate */
    119         case 0x00030004: /* Get max clock rate */
    120         case 0x00030007: /* Get min clock rate */
    121             switch (ldl_le_phys(&s->dma_as, value + 12)) {
    122             case 1: /* EMMC */
    123                 stl_le_phys(&s->dma_as, value + 16, 50000000);
    124                 break;
    125             case 2: /* UART */
    126                 stl_le_phys(&s->dma_as, value + 16, 3000000);
    127                 break;
    128             default:
    129                 stl_le_phys(&s->dma_as, value + 16, 700000000);
    130                 break;
    131             }
    132             resplen = 8;
    133             break;
    134 
    135         case 0x00038002: /* Set clock rate */
    136         case 0x00038004: /* Set max clock rate */
    137         case 0x00038007: /* Set min clock rate */
    138             qemu_log_mask(LOG_UNIMP,
    139                           "bcm2835_property: 0x%08x set clock rate NYI\n",
    140                           tag);
    141             resplen = 8;
    142             break;
    143 
    144         /* Temperature */
    145 
    146         case 0x00030006: /* Get temperature */
    147             stl_le_phys(&s->dma_as, value + 16, 25000);
    148             resplen = 8;
    149             break;
    150 
    151         case 0x0003000A: /* Get max temperature */
    152             stl_le_phys(&s->dma_as, value + 16, 99000);
    153             resplen = 8;
    154             break;
    155 
    156         /* Frame buffer */
    157 
    158         case 0x00040001: /* Allocate buffer */
    159             stl_le_phys(&s->dma_as, value + 12, fbconfig.base);
    160             stl_le_phys(&s->dma_as, value + 16,
    161                         bcm2835_fb_get_size(&fbconfig));
    162             resplen = 8;
    163             break;
    164         case 0x00048001: /* Release buffer */
    165             resplen = 0;
    166             break;
    167         case 0x00040002: /* Blank screen */
    168             resplen = 4;
    169             break;
    170         case 0x00044003: /* Test physical display width/height */
    171         case 0x00044004: /* Test virtual display width/height */
    172             resplen = 8;
    173             break;
    174         case 0x00048003: /* Set physical display width/height */
    175             fbconfig.xres = ldl_le_phys(&s->dma_as, value + 12);
    176             fbconfig.yres = ldl_le_phys(&s->dma_as, value + 16);
    177             bcm2835_fb_validate_config(&fbconfig);
    178             fbconfig_updated = true;
    179             /* fall through */
    180         case 0x00040003: /* Get physical display width/height */
    181             stl_le_phys(&s->dma_as, value + 12, fbconfig.xres);
    182             stl_le_phys(&s->dma_as, value + 16, fbconfig.yres);
    183             resplen = 8;
    184             break;
    185         case 0x00048004: /* Set virtual display width/height */
    186             fbconfig.xres_virtual = ldl_le_phys(&s->dma_as, value + 12);
    187             fbconfig.yres_virtual = ldl_le_phys(&s->dma_as, value + 16);
    188             bcm2835_fb_validate_config(&fbconfig);
    189             fbconfig_updated = true;
    190             /* fall through */
    191         case 0x00040004: /* Get virtual display width/height */
    192             stl_le_phys(&s->dma_as, value + 12, fbconfig.xres_virtual);
    193             stl_le_phys(&s->dma_as, value + 16, fbconfig.yres_virtual);
    194             resplen = 8;
    195             break;
    196         case 0x00044005: /* Test depth */
    197             resplen = 4;
    198             break;
    199         case 0x00048005: /* Set depth */
    200             fbconfig.bpp = ldl_le_phys(&s->dma_as, value + 12);
    201             bcm2835_fb_validate_config(&fbconfig);
    202             fbconfig_updated = true;
    203             /* fall through */
    204         case 0x00040005: /* Get depth */
    205             stl_le_phys(&s->dma_as, value + 12, fbconfig.bpp);
    206             resplen = 4;
    207             break;
    208         case 0x00044006: /* Test pixel order */
    209             resplen = 4;
    210             break;
    211         case 0x00048006: /* Set pixel order */
    212             fbconfig.pixo = ldl_le_phys(&s->dma_as, value + 12);
    213             bcm2835_fb_validate_config(&fbconfig);
    214             fbconfig_updated = true;
    215             /* fall through */
    216         case 0x00040006: /* Get pixel order */
    217             stl_le_phys(&s->dma_as, value + 12, fbconfig.pixo);
    218             resplen = 4;
    219             break;
    220         case 0x00044007: /* Test pixel alpha */
    221             resplen = 4;
    222             break;
    223         case 0x00048007: /* Set alpha */
    224             fbconfig.alpha = ldl_le_phys(&s->dma_as, value + 12);
    225             bcm2835_fb_validate_config(&fbconfig);
    226             fbconfig_updated = true;
    227             /* fall through */
    228         case 0x00040007: /* Get alpha */
    229             stl_le_phys(&s->dma_as, value + 12, fbconfig.alpha);
    230             resplen = 4;
    231             break;
    232         case 0x00040008: /* Get pitch */
    233             stl_le_phys(&s->dma_as, value + 12,
    234                         bcm2835_fb_get_pitch(&fbconfig));
    235             resplen = 4;
    236             break;
    237         case 0x00044009: /* Test virtual offset */
    238             resplen = 8;
    239             break;
    240         case 0x00048009: /* Set virtual offset */
    241             fbconfig.xoffset = ldl_le_phys(&s->dma_as, value + 12);
    242             fbconfig.yoffset = ldl_le_phys(&s->dma_as, value + 16);
    243             bcm2835_fb_validate_config(&fbconfig);
    244             fbconfig_updated = true;
    245             /* fall through */
    246         case 0x00040009: /* Get virtual offset */
    247             stl_le_phys(&s->dma_as, value + 12, fbconfig.xoffset);
    248             stl_le_phys(&s->dma_as, value + 16, fbconfig.yoffset);
    249             resplen = 8;
    250             break;
    251         case 0x0004000a: /* Get/Test/Set overscan */
    252         case 0x0004400a:
    253         case 0x0004800a:
    254             stl_le_phys(&s->dma_as, value + 12, 0);
    255             stl_le_phys(&s->dma_as, value + 16, 0);
    256             stl_le_phys(&s->dma_as, value + 20, 0);
    257             stl_le_phys(&s->dma_as, value + 24, 0);
    258             resplen = 16;
    259             break;
    260         case 0x0004800b: /* Set palette */
    261             offset = ldl_le_phys(&s->dma_as, value + 12);
    262             length = ldl_le_phys(&s->dma_as, value + 16);
    263             n = 0;
    264             while (n < length - offset) {
    265                 color = ldl_le_phys(&s->dma_as, value + 20 + (n << 2));
    266                 stl_le_phys(&s->dma_as,
    267                             s->fbdev->vcram_base + ((offset + n) << 2), color);
    268                 n++;
    269             }
    270             stl_le_phys(&s->dma_as, value + 12, 0);
    271             resplen = 4;
    272             break;
    273         case 0x00040013: /* Get number of displays */
    274             stl_le_phys(&s->dma_as, value + 12, 1);
    275             resplen = 4;
    276             break;
    277 
    278         case 0x00060001: /* Get DMA channels */
    279             /* channels 2-5 */
    280             stl_le_phys(&s->dma_as, value + 12, 0x003C);
    281             resplen = 4;
    282             break;
    283 
    284         case 0x00050001: /* Get command line */
    285             resplen = 0;
    286             break;
    287 
    288         default:
    289             qemu_log_mask(LOG_UNIMP,
    290                           "bcm2835_property: unhandled tag 0x%08x\n", tag);
    291             break;
    292         }
    293 
    294         trace_bcm2835_mbox_property(tag, bufsize, resplen);
    295         if (tag == 0) {
    296             break;
    297         }
    298 
    299         stl_le_phys(&s->dma_as, value + 8, (1 << 31) | resplen);
    300         value += bufsize + 12;
    301     }
    302 
    303     /* Reconfigure framebuffer if required */
    304     if (fbconfig_updated) {
    305         bcm2835_fb_reconfigure(s->fbdev, &fbconfig);
    306     }
    307 
    308     /* Buffer response code */
    309     stl_le_phys(&s->dma_as, s->addr + 4, (1 << 31));
    310 }
    311 
    312 static uint64_t bcm2835_property_read(void *opaque, hwaddr offset,
    313                                       unsigned size)
    314 {
    315     BCM2835PropertyState *s = opaque;
    316     uint32_t res = 0;
    317 
    318     switch (offset) {
    319     case MBOX_AS_DATA:
    320         res = MBOX_CHAN_PROPERTY | s->addr;
    321         s->pending = false;
    322         qemu_set_irq(s->mbox_irq, 0);
    323         break;
    324 
    325     case MBOX_AS_PENDING:
    326         res = s->pending;
    327         break;
    328 
    329     default:
    330         qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
    331                       __func__, offset);
    332         return 0;
    333     }
    334 
    335     return res;
    336 }
    337 
    338 static void bcm2835_property_write(void *opaque, hwaddr offset,
    339                                    uint64_t value, unsigned size)
    340 {
    341     BCM2835PropertyState *s = opaque;
    342 
    343     switch (offset) {
    344     case MBOX_AS_DATA:
    345         /* bcm2835_mbox should check our pending status before pushing */
    346         assert(!s->pending);
    347         s->pending = true;
    348         bcm2835_property_mbox_push(s, value);
    349         qemu_set_irq(s->mbox_irq, 1);
    350         break;
    351 
    352     default:
    353         qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
    354                       __func__, offset);
    355         return;
    356     }
    357 }
    358 
    359 static const MemoryRegionOps bcm2835_property_ops = {
    360     .read = bcm2835_property_read,
    361     .write = bcm2835_property_write,
    362     .endianness = DEVICE_NATIVE_ENDIAN,
    363     .valid.min_access_size = 4,
    364     .valid.max_access_size = 4,
    365 };
    366 
    367 static const VMStateDescription vmstate_bcm2835_property = {
    368     .name = TYPE_BCM2835_PROPERTY,
    369     .version_id = 1,
    370     .minimum_version_id = 1,
    371     .fields      = (VMStateField[]) {
    372         VMSTATE_MACADDR(macaddr, BCM2835PropertyState),
    373         VMSTATE_UINT32(addr, BCM2835PropertyState),
    374         VMSTATE_BOOL(pending, BCM2835PropertyState),
    375         VMSTATE_END_OF_LIST()
    376     }
    377 };
    378 
    379 static void bcm2835_property_init(Object *obj)
    380 {
    381     BCM2835PropertyState *s = BCM2835_PROPERTY(obj);
    382 
    383     memory_region_init_io(&s->iomem, OBJECT(s), &bcm2835_property_ops, s,
    384                           TYPE_BCM2835_PROPERTY, 0x10);
    385     sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
    386     sysbus_init_irq(SYS_BUS_DEVICE(s), &s->mbox_irq);
    387 }
    388 
    389 static void bcm2835_property_reset(DeviceState *dev)
    390 {
    391     BCM2835PropertyState *s = BCM2835_PROPERTY(dev);
    392 
    393     s->pending = false;
    394 }
    395 
    396 static void bcm2835_property_realize(DeviceState *dev, Error **errp)
    397 {
    398     BCM2835PropertyState *s = BCM2835_PROPERTY(dev);
    399     Object *obj;
    400 
    401     obj = object_property_get_link(OBJECT(dev), "fb", &error_abort);
    402     s->fbdev = BCM2835_FB(obj);
    403 
    404     obj = object_property_get_link(OBJECT(dev), "dma-mr", &error_abort);
    405     s->dma_mr = MEMORY_REGION(obj);
    406     address_space_init(&s->dma_as, s->dma_mr, TYPE_BCM2835_PROPERTY "-memory");
    407 
    408     /* TODO: connect to MAC address of USB NIC device, once we emulate it */
    409     qemu_macaddr_default_if_unset(&s->macaddr);
    410 
    411     bcm2835_property_reset(dev);
    412 }
    413 
    414 static Property bcm2835_property_props[] = {
    415     DEFINE_PROP_UINT32("board-rev", BCM2835PropertyState, board_rev, 0),
    416     DEFINE_PROP_END_OF_LIST()
    417 };
    418 
    419 static void bcm2835_property_class_init(ObjectClass *klass, void *data)
    420 {
    421     DeviceClass *dc = DEVICE_CLASS(klass);
    422 
    423     device_class_set_props(dc, bcm2835_property_props);
    424     dc->realize = bcm2835_property_realize;
    425     dc->vmsd = &vmstate_bcm2835_property;
    426 }
    427 
    428 static const TypeInfo bcm2835_property_info = {
    429     .name          = TYPE_BCM2835_PROPERTY,
    430     .parent        = TYPE_SYS_BUS_DEVICE,
    431     .instance_size = sizeof(BCM2835PropertyState),
    432     .class_init    = bcm2835_property_class_init,
    433     .instance_init = bcm2835_property_init,
    434 };
    435 
    436 static void bcm2835_property_register_types(void)
    437 {
    438     type_register_static(&bcm2835_property_info);
    439 }
    440 
    441 type_init(bcm2835_property_register_types)