104f1ab15SAndrew Baumann /* 204f1ab15SAndrew Baumann * Raspberry Pi emulation (c) 2012 Gregory Estrade 36111a0c0SPhilippe Mathieu-Daudé * 46111a0c0SPhilippe Mathieu-Daudé * This work is licensed under the terms of the GNU GPL, version 2 or later. 56111a0c0SPhilippe Mathieu-Daudé * See the COPYING file in the top-level directory. 604f1ab15SAndrew Baumann */ 704f1ab15SAndrew Baumann 8c964b660SPeter Maydell #include "qemu/osdep.h" 9da34e65cSMarkus Armbruster #include "qapi/error.h" 1004f1ab15SAndrew Baumann #include "hw/misc/bcm2835_property.h" 11a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h" 12d6454270SMarkus Armbruster #include "migration/vmstate.h" 1364552b6bSMarkus Armbruster #include "hw/irq.h" 1404f1ab15SAndrew Baumann #include "hw/misc/bcm2835_mbox_defs.h" 1529ecf2deSThomas Huth #include "hw/arm/raspberrypi-fw-defs.h" 1604f1ab15SAndrew Baumann #include "sysemu/dma.h" 1703dd024fSPaolo Bonzini #include "qemu/log.h" 180b8fa32fSMarkus Armbruster #include "qemu/module.h" 1919845504SPhilippe Mathieu-Daudé #include "trace.h" 205dc49636SSergey Kambalin #include "hw/arm/raspi_platform.h" 2104f1ab15SAndrew Baumann 22cda5a7d6SSergey Kambalin #define VCHI_BUSADDR_SIZE sizeof(uint32_t) 23cda5a7d6SSergey Kambalin 2404f1ab15SAndrew Baumann /* https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface */ 2504f1ab15SAndrew Baumann 2604f1ab15SAndrew Baumann static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) 2704f1ab15SAndrew Baumann { 2804f1ab15SAndrew Baumann uint32_t tot_len; 29193100b5SPeter Maydell 30193100b5SPeter Maydell /* 31193100b5SPeter Maydell * Copy the current state of the framebuffer config; we will update 32193100b5SPeter Maydell * this copy as we process tags and then ask the framebuffer to use 33193100b5SPeter Maydell * it at the end. 34193100b5SPeter Maydell */ 35193100b5SPeter Maydell BCM2835FBConfig fbconfig = s->fbdev->config; 36193100b5SPeter Maydell bool fbconfig_updated = false; 3704f1ab15SAndrew Baumann 3804f1ab15SAndrew Baumann value &= ~0xf; 3904f1ab15SAndrew Baumann 4004f1ab15SAndrew Baumann s->addr = value; 4104f1ab15SAndrew Baumann 42eab71394SAndrew Baumann tot_len = ldl_le_phys(&s->dma_as, value); 4304f1ab15SAndrew Baumann 4404f1ab15SAndrew Baumann /* @(addr + 4) : Buffer response code */ 4504f1ab15SAndrew Baumann value = s->addr + 8; 4604f1ab15SAndrew Baumann while (value + 8 <= s->addr + tot_len) { 47*28fe81f0SPeter Maydell uint32_t tag = ldl_le_phys(&s->dma_as, value); 48*28fe81f0SPeter Maydell uint32_t bufsize = ldl_le_phys(&s->dma_as, value + 4); 4904f1ab15SAndrew Baumann /* @(value + 8) : Request/response indicator */ 50*28fe81f0SPeter Maydell size_t resplen = 0; 5104f1ab15SAndrew Baumann switch (tag) { 5225191826SSergey Kambalin case RPI_FWREQ_PROPERTY_END: 5304f1ab15SAndrew Baumann break; 5425191826SSergey Kambalin case RPI_FWREQ_GET_FIRMWARE_REVISION: 55eab71394SAndrew Baumann stl_le_phys(&s->dma_as, value + 12, 346337); 5604f1ab15SAndrew Baumann resplen = 4; 5704f1ab15SAndrew Baumann break; 5825191826SSergey Kambalin case RPI_FWREQ_GET_BOARD_MODEL: 5904f1ab15SAndrew Baumann qemu_log_mask(LOG_UNIMP, 60e1ecf8c8SPhilippe Mathieu-Daudé "bcm2835_property: 0x%08x get board model NYI\n", 61e1ecf8c8SPhilippe Mathieu-Daudé tag); 6204f1ab15SAndrew Baumann resplen = 4; 6304f1ab15SAndrew Baumann break; 6425191826SSergey Kambalin case RPI_FWREQ_GET_BOARD_REVISION: 65eab71394SAndrew Baumann stl_le_phys(&s->dma_as, value + 12, s->board_rev); 6604f1ab15SAndrew Baumann resplen = 4; 6704f1ab15SAndrew Baumann break; 6825191826SSergey Kambalin case RPI_FWREQ_GET_BOARD_MAC_ADDRESS: 6904f1ab15SAndrew Baumann resplen = sizeof(s->macaddr.a); 70ba06fe8aSPhilippe Mathieu-Daudé dma_memory_write(&s->dma_as, value + 12, s->macaddr.a, resplen, 71ba06fe8aSPhilippe Mathieu-Daudé MEMTXATTRS_UNSPECIFIED); 7204f1ab15SAndrew Baumann break; 7325191826SSergey Kambalin case RPI_FWREQ_GET_BOARD_SERIAL: 7404f1ab15SAndrew Baumann qemu_log_mask(LOG_UNIMP, 75e1ecf8c8SPhilippe Mathieu-Daudé "bcm2835_property: 0x%08x get board serial NYI\n", 76e1ecf8c8SPhilippe Mathieu-Daudé tag); 7704f1ab15SAndrew Baumann resplen = 8; 7804f1ab15SAndrew Baumann break; 7925191826SSergey Kambalin case RPI_FWREQ_GET_ARM_MEMORY: 8004f1ab15SAndrew Baumann /* base */ 81eab71394SAndrew Baumann stl_le_phys(&s->dma_as, value + 12, 0); 8204f1ab15SAndrew Baumann /* size */ 83355a8cccSGrégory ESTRADE stl_le_phys(&s->dma_as, value + 16, s->fbdev->vcram_base); 84355a8cccSGrégory ESTRADE resplen = 8; 85355a8cccSGrégory ESTRADE break; 8625191826SSergey Kambalin case RPI_FWREQ_GET_VC_MEMORY: 87355a8cccSGrégory ESTRADE /* base */ 88355a8cccSGrégory ESTRADE stl_le_phys(&s->dma_as, value + 12, s->fbdev->vcram_base); 89355a8cccSGrégory ESTRADE /* size */ 90355a8cccSGrégory ESTRADE stl_le_phys(&s->dma_as, value + 16, s->fbdev->vcram_size); 9104f1ab15SAndrew Baumann resplen = 8; 9204f1ab15SAndrew Baumann break; 9325191826SSergey Kambalin case RPI_FWREQ_SET_POWER_STATE: 94*28fe81f0SPeter Maydell { 95*28fe81f0SPeter Maydell /* 96*28fe81f0SPeter Maydell * Assume that whatever device they asked for exists, 97*28fe81f0SPeter Maydell * and we'll just claim we set it to the desired state. 9804f1ab15SAndrew Baumann */ 99*28fe81f0SPeter Maydell uint32_t state = ldl_le_phys(&s->dma_as, value + 16); 100*28fe81f0SPeter Maydell stl_le_phys(&s->dma_as, value + 16, (state & 1)); 10104f1ab15SAndrew Baumann resplen = 8; 10204f1ab15SAndrew Baumann break; 103*28fe81f0SPeter Maydell } 10404f1ab15SAndrew Baumann 10504f1ab15SAndrew Baumann /* Clocks */ 10604f1ab15SAndrew Baumann 10725191826SSergey Kambalin case RPI_FWREQ_GET_CLOCK_STATE: 108eab71394SAndrew Baumann stl_le_phys(&s->dma_as, value + 16, 0x1); 10904f1ab15SAndrew Baumann resplen = 8; 11004f1ab15SAndrew Baumann break; 11104f1ab15SAndrew Baumann 11225191826SSergey Kambalin case RPI_FWREQ_SET_CLOCK_STATE: 11304f1ab15SAndrew Baumann qemu_log_mask(LOG_UNIMP, 114e1ecf8c8SPhilippe Mathieu-Daudé "bcm2835_property: 0x%08x set clock state NYI\n", 115e1ecf8c8SPhilippe Mathieu-Daudé tag); 11604f1ab15SAndrew Baumann resplen = 8; 11704f1ab15SAndrew Baumann break; 11804f1ab15SAndrew Baumann 11925191826SSergey Kambalin case RPI_FWREQ_GET_CLOCK_RATE: 12025191826SSergey Kambalin case RPI_FWREQ_GET_MAX_CLOCK_RATE: 12125191826SSergey Kambalin case RPI_FWREQ_GET_MIN_CLOCK_RATE: 122eab71394SAndrew Baumann switch (ldl_le_phys(&s->dma_as, value + 12)) { 12325191826SSergey Kambalin case RPI_FIRMWARE_EMMC_CLK_ID: 1245dc49636SSergey Kambalin stl_le_phys(&s->dma_as, value + 16, RPI_FIRMWARE_EMMC_CLK_RATE); 12504f1ab15SAndrew Baumann break; 12625191826SSergey Kambalin case RPI_FIRMWARE_UART_CLK_ID: 1275dc49636SSergey Kambalin stl_le_phys(&s->dma_as, value + 16, RPI_FIRMWARE_UART_CLK_RATE); 12804f1ab15SAndrew Baumann break; 129074259c0SSergey Kambalin case RPI_FIRMWARE_CORE_CLK_ID: 130074259c0SSergey Kambalin stl_le_phys(&s->dma_as, value + 16, RPI_FIRMWARE_CORE_CLK_RATE); 131074259c0SSergey Kambalin break; 13204f1ab15SAndrew Baumann default: 1335dc49636SSergey Kambalin stl_le_phys(&s->dma_as, value + 16, 1345dc49636SSergey Kambalin RPI_FIRMWARE_DEFAULT_CLK_RATE); 13504f1ab15SAndrew Baumann break; 13604f1ab15SAndrew Baumann } 13704f1ab15SAndrew Baumann resplen = 8; 13804f1ab15SAndrew Baumann break; 13904f1ab15SAndrew Baumann 140cda5a7d6SSergey Kambalin case RPI_FWREQ_GET_CLOCKS: 141cda5a7d6SSergey Kambalin /* TODO: add more clock IDs if needed */ 142cda5a7d6SSergey Kambalin stl_le_phys(&s->dma_as, value + 12, 0); 143cda5a7d6SSergey Kambalin stl_le_phys(&s->dma_as, value + 16, RPI_FIRMWARE_ARM_CLK_ID); 144cda5a7d6SSergey Kambalin resplen = 8; 145cda5a7d6SSergey Kambalin break; 146cda5a7d6SSergey Kambalin 14725191826SSergey Kambalin case RPI_FWREQ_SET_CLOCK_RATE: 14825191826SSergey Kambalin case RPI_FWREQ_SET_MAX_CLOCK_RATE: 14925191826SSergey Kambalin case RPI_FWREQ_SET_MIN_CLOCK_RATE: 15004f1ab15SAndrew Baumann qemu_log_mask(LOG_UNIMP, 151e1ecf8c8SPhilippe Mathieu-Daudé "bcm2835_property: 0x%08x set clock rate NYI\n", 152e1ecf8c8SPhilippe Mathieu-Daudé tag); 15304f1ab15SAndrew Baumann resplen = 8; 15404f1ab15SAndrew Baumann break; 15504f1ab15SAndrew Baumann 15604f1ab15SAndrew Baumann /* Temperature */ 15704f1ab15SAndrew Baumann 15825191826SSergey Kambalin case RPI_FWREQ_GET_TEMPERATURE: 159eab71394SAndrew Baumann stl_le_phys(&s->dma_as, value + 16, 25000); 16004f1ab15SAndrew Baumann resplen = 8; 16104f1ab15SAndrew Baumann break; 16204f1ab15SAndrew Baumann 16325191826SSergey Kambalin case RPI_FWREQ_GET_MAX_TEMPERATURE: 164eab71394SAndrew Baumann stl_le_phys(&s->dma_as, value + 16, 99000); 16504f1ab15SAndrew Baumann resplen = 8; 16604f1ab15SAndrew Baumann break; 16704f1ab15SAndrew Baumann 168355a8cccSGrégory ESTRADE /* Frame buffer */ 169355a8cccSGrégory ESTRADE 17025191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_ALLOCATE: 171193100b5SPeter Maydell stl_le_phys(&s->dma_as, value + 12, fbconfig.base); 17227a5dc7bSSylvain Garrigues stl_le_phys(&s->dma_as, value + 16, 1739a1f03f4SPeter Maydell bcm2835_fb_get_size(&fbconfig)); 174355a8cccSGrégory ESTRADE resplen = 8; 175355a8cccSGrégory ESTRADE break; 17625191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_RELEASE: 177355a8cccSGrégory ESTRADE resplen = 0; 178355a8cccSGrégory ESTRADE break; 17925191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_BLANK: 180355a8cccSGrégory ESTRADE resplen = 4; 181355a8cccSGrégory ESTRADE break; 18225191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_TEST_PHYSICAL_WIDTH_HEIGHT: 18325191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_TEST_VIRTUAL_WIDTH_HEIGHT: 18401f18af9SPeter Maydell resplen = 8; 18501f18af9SPeter Maydell break; 18625191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_SET_PHYSICAL_WIDTH_HEIGHT: 187193100b5SPeter Maydell fbconfig.xres = ldl_le_phys(&s->dma_as, value + 12); 188193100b5SPeter Maydell fbconfig.yres = ldl_le_phys(&s->dma_as, value + 16); 189f8add62cSPeter Maydell bcm2835_fb_validate_config(&fbconfig); 190193100b5SPeter Maydell fbconfig_updated = true; 191f8add62cSPeter Maydell /* fall through */ 19225191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_GET_PHYSICAL_WIDTH_HEIGHT: 193f8add62cSPeter Maydell stl_le_phys(&s->dma_as, value + 12, fbconfig.xres); 194f8add62cSPeter Maydell stl_le_phys(&s->dma_as, value + 16, fbconfig.yres); 195355a8cccSGrégory ESTRADE resplen = 8; 196355a8cccSGrégory ESTRADE break; 19725191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_SET_VIRTUAL_WIDTH_HEIGHT: 19801f18af9SPeter Maydell fbconfig.xres_virtual = ldl_le_phys(&s->dma_as, value + 12); 19901f18af9SPeter Maydell fbconfig.yres_virtual = ldl_le_phys(&s->dma_as, value + 16); 200f8add62cSPeter Maydell bcm2835_fb_validate_config(&fbconfig); 20101f18af9SPeter Maydell fbconfig_updated = true; 202f8add62cSPeter Maydell /* fall through */ 20325191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_GET_VIRTUAL_WIDTH_HEIGHT: 204f8add62cSPeter Maydell stl_le_phys(&s->dma_as, value + 12, fbconfig.xres_virtual); 205f8add62cSPeter Maydell stl_le_phys(&s->dma_as, value + 16, fbconfig.yres_virtual); 20601f18af9SPeter Maydell resplen = 8; 20701f18af9SPeter Maydell break; 20825191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_TEST_DEPTH: 209355a8cccSGrégory ESTRADE resplen = 4; 210355a8cccSGrégory ESTRADE break; 21125191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_SET_DEPTH: 212193100b5SPeter Maydell fbconfig.bpp = ldl_le_phys(&s->dma_as, value + 12); 213f8add62cSPeter Maydell bcm2835_fb_validate_config(&fbconfig); 214193100b5SPeter Maydell fbconfig_updated = true; 215f8add62cSPeter Maydell /* fall through */ 21625191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_GET_DEPTH: 217f8add62cSPeter Maydell stl_le_phys(&s->dma_as, value + 12, fbconfig.bpp); 218355a8cccSGrégory ESTRADE resplen = 4; 219355a8cccSGrégory ESTRADE break; 22025191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_TEST_PIXEL_ORDER: 221355a8cccSGrégory ESTRADE resplen = 4; 222355a8cccSGrégory ESTRADE break; 22325191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_SET_PIXEL_ORDER: 224193100b5SPeter Maydell fbconfig.pixo = ldl_le_phys(&s->dma_as, value + 12); 225f8add62cSPeter Maydell bcm2835_fb_validate_config(&fbconfig); 226193100b5SPeter Maydell fbconfig_updated = true; 227f8add62cSPeter Maydell /* fall through */ 22825191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_GET_PIXEL_ORDER: 229f8add62cSPeter Maydell stl_le_phys(&s->dma_as, value + 12, fbconfig.pixo); 230355a8cccSGrégory ESTRADE resplen = 4; 231355a8cccSGrégory ESTRADE break; 23225191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_TEST_ALPHA_MODE: 233355a8cccSGrégory ESTRADE resplen = 4; 234355a8cccSGrégory ESTRADE break; 23525191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_SET_ALPHA_MODE: 236193100b5SPeter Maydell fbconfig.alpha = ldl_le_phys(&s->dma_as, value + 12); 237f8add62cSPeter Maydell bcm2835_fb_validate_config(&fbconfig); 238193100b5SPeter Maydell fbconfig_updated = true; 239f8add62cSPeter Maydell /* fall through */ 24025191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_GET_ALPHA_MODE: 241f8add62cSPeter Maydell stl_le_phys(&s->dma_as, value + 12, fbconfig.alpha); 242355a8cccSGrégory ESTRADE resplen = 4; 243355a8cccSGrégory ESTRADE break; 24425191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_GET_PITCH: 245193100b5SPeter Maydell stl_le_phys(&s->dma_as, value + 12, 2469a1f03f4SPeter Maydell bcm2835_fb_get_pitch(&fbconfig)); 247355a8cccSGrégory ESTRADE resplen = 4; 248355a8cccSGrégory ESTRADE break; 24925191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_TEST_VIRTUAL_OFFSET: 250355a8cccSGrégory ESTRADE resplen = 8; 251355a8cccSGrégory ESTRADE break; 25225191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_SET_VIRTUAL_OFFSET: 253193100b5SPeter Maydell fbconfig.xoffset = ldl_le_phys(&s->dma_as, value + 12); 254193100b5SPeter Maydell fbconfig.yoffset = ldl_le_phys(&s->dma_as, value + 16); 255f8add62cSPeter Maydell bcm2835_fb_validate_config(&fbconfig); 256193100b5SPeter Maydell fbconfig_updated = true; 257f8add62cSPeter Maydell /* fall through */ 25825191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_GET_VIRTUAL_OFFSET: 259f8add62cSPeter Maydell stl_le_phys(&s->dma_as, value + 12, fbconfig.xoffset); 260f8add62cSPeter Maydell stl_le_phys(&s->dma_as, value + 16, fbconfig.yoffset); 261355a8cccSGrégory ESTRADE resplen = 8; 262355a8cccSGrégory ESTRADE break; 26325191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_GET_OVERSCAN: 26425191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_TEST_OVERSCAN: 26525191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_SET_OVERSCAN: 266355a8cccSGrégory ESTRADE stl_le_phys(&s->dma_as, value + 12, 0); 267355a8cccSGrégory ESTRADE stl_le_phys(&s->dma_as, value + 16, 0); 268355a8cccSGrégory ESTRADE stl_le_phys(&s->dma_as, value + 20, 0); 269355a8cccSGrégory ESTRADE stl_le_phys(&s->dma_as, value + 24, 0); 270355a8cccSGrégory ESTRADE resplen = 16; 271355a8cccSGrégory ESTRADE break; 27225191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_SET_PALETTE: 2730892fffcSPeter Maydell { 2740892fffcSPeter Maydell uint32_t offset = ldl_le_phys(&s->dma_as, value + 12); 2750892fffcSPeter Maydell uint32_t length = ldl_le_phys(&s->dma_as, value + 16); 2760892fffcSPeter Maydell int resp; 2770892fffcSPeter Maydell 2780892fffcSPeter Maydell if (offset > 255 || length < 1 || length > 256) { 2790892fffcSPeter Maydell resp = 1; /* invalid request */ 2800892fffcSPeter Maydell } else { 2810892fffcSPeter Maydell for (uint32_t e = 0; e < length; e++) { 2820892fffcSPeter Maydell uint32_t color = ldl_le_phys(&s->dma_as, value + 20 + (e << 2)); 283355a8cccSGrégory ESTRADE stl_le_phys(&s->dma_as, 2840892fffcSPeter Maydell s->fbdev->vcram_base + ((offset + e) << 2), color); 285355a8cccSGrégory ESTRADE } 2860892fffcSPeter Maydell resp = 0; 2870892fffcSPeter Maydell } 2880892fffcSPeter Maydell stl_le_phys(&s->dma_as, value + 12, resp); 289355a8cccSGrégory ESTRADE resplen = 4; 290355a8cccSGrégory ESTRADE break; 2910892fffcSPeter Maydell } 29225191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_GET_NUM_DISPLAYS: 2933b9a030eSEnrik Berkhan stl_le_phys(&s->dma_as, value + 12, 1); 2943b9a030eSEnrik Berkhan resplen = 4; 2953b9a030eSEnrik Berkhan break; 29604f1ab15SAndrew Baumann 29725191826SSergey Kambalin case RPI_FWREQ_GET_DMA_CHANNELS: 29804f1ab15SAndrew Baumann /* channels 2-5 */ 299eab71394SAndrew Baumann stl_le_phys(&s->dma_as, value + 12, 0x003C); 30004f1ab15SAndrew Baumann resplen = 4; 30104f1ab15SAndrew Baumann break; 30204f1ab15SAndrew Baumann 30325191826SSergey Kambalin case RPI_FWREQ_GET_COMMAND_LINE: 304f802ff1eSDaniel Bertalan /* 305f802ff1eSDaniel Bertalan * We follow the firmware behaviour: no NUL terminator is 306f802ff1eSDaniel Bertalan * written to the buffer, and if the buffer is too short 307f802ff1eSDaniel Bertalan * we report the required length in the response header 308f802ff1eSDaniel Bertalan * and copy nothing to the buffer. 309f802ff1eSDaniel Bertalan */ 310f802ff1eSDaniel Bertalan resplen = strlen(s->command_line); 311f802ff1eSDaniel Bertalan if (bufsize >= resplen) 312f802ff1eSDaniel Bertalan address_space_write(&s->dma_as, value + 12, 313f802ff1eSDaniel Bertalan MEMTXATTRS_UNSPECIFIED, s->command_line, 314f802ff1eSDaniel Bertalan resplen); 31504f1ab15SAndrew Baumann break; 31604f1ab15SAndrew Baumann 317cda5a7d6SSergey Kambalin case RPI_FWREQ_GET_THROTTLED: 318cda5a7d6SSergey Kambalin stl_le_phys(&s->dma_as, value + 12, 0); 319cda5a7d6SSergey Kambalin resplen = 4; 320cda5a7d6SSergey Kambalin break; 321cda5a7d6SSergey Kambalin 322cda5a7d6SSergey Kambalin case RPI_FWREQ_VCHIQ_INIT: 323cda5a7d6SSergey Kambalin stl_le_phys(&s->dma_as, 324cda5a7d6SSergey Kambalin value + offsetof(rpi_firmware_prop_request_t, payload), 325cda5a7d6SSergey Kambalin 0); 326cda5a7d6SSergey Kambalin resplen = VCHI_BUSADDR_SIZE; 327cda5a7d6SSergey Kambalin break; 3285d5f1b60SRayhan Faizel 3295d5f1b60SRayhan Faizel /* Customer OTP */ 3305d5f1b60SRayhan Faizel 3315d5f1b60SRayhan Faizel case RPI_FWREQ_GET_CUSTOMER_OTP: 3324b648238SPeter Maydell { 3334b648238SPeter Maydell uint32_t start_num = ldl_le_phys(&s->dma_as, value + 12); 3344b648238SPeter Maydell uint32_t number = ldl_le_phys(&s->dma_as, value + 16); 3355d5f1b60SRayhan Faizel 3365d5f1b60SRayhan Faizel resplen = 8 + 4 * number; 3375d5f1b60SRayhan Faizel 33832f1c201SPeter Maydell for (uint32_t n = start_num; n < start_num + number && 3395d5f1b60SRayhan Faizel n < BCM2835_OTP_CUSTOMER_OTP_LEN; n++) { 3404b648238SPeter Maydell uint32_t otp_row = bcm2835_otp_get_row(s->otp, 3415d5f1b60SRayhan Faizel BCM2835_OTP_CUSTOMER_OTP + n); 3425d5f1b60SRayhan Faizel stl_le_phys(&s->dma_as, 3435d5f1b60SRayhan Faizel value + 20 + ((n - start_num) << 2), otp_row); 3445d5f1b60SRayhan Faizel } 3455d5f1b60SRayhan Faizel break; 3464b648238SPeter Maydell } 3475d5f1b60SRayhan Faizel case RPI_FWREQ_SET_CUSTOMER_OTP: 3484b648238SPeter Maydell { 3494b648238SPeter Maydell uint32_t start_num = ldl_le_phys(&s->dma_as, value + 12); 3504b648238SPeter Maydell uint32_t number = ldl_le_phys(&s->dma_as, value + 16); 3515d5f1b60SRayhan Faizel 3525d5f1b60SRayhan Faizel resplen = 4; 3535d5f1b60SRayhan Faizel 3545d5f1b60SRayhan Faizel /* Magic numbers to permanently lock customer OTP */ 3555d5f1b60SRayhan Faizel if (start_num == BCM2835_OTP_LOCK_NUM1 && 3565d5f1b60SRayhan Faizel number == BCM2835_OTP_LOCK_NUM2) { 3575d5f1b60SRayhan Faizel bcm2835_otp_set_row(s->otp, 3585d5f1b60SRayhan Faizel BCM2835_OTP_ROW_32, 3595d5f1b60SRayhan Faizel BCM2835_OTP_ROW_32_LOCK); 3605d5f1b60SRayhan Faizel break; 3615d5f1b60SRayhan Faizel } 3625d5f1b60SRayhan Faizel 3635d5f1b60SRayhan Faizel /* If row 32 has the lock bit, don't allow further writes */ 3645d5f1b60SRayhan Faizel if (bcm2835_otp_get_row(s->otp, BCM2835_OTP_ROW_32) & 3655d5f1b60SRayhan Faizel BCM2835_OTP_ROW_32_LOCK) { 3665d5f1b60SRayhan Faizel break; 3675d5f1b60SRayhan Faizel } 3685d5f1b60SRayhan Faizel 36932f1c201SPeter Maydell for (uint32_t n = start_num; n < start_num + number && 3705d5f1b60SRayhan Faizel n < BCM2835_OTP_CUSTOMER_OTP_LEN; n++) { 3714b648238SPeter Maydell uint32_t otp_row = ldl_le_phys(&s->dma_as, 3725d5f1b60SRayhan Faizel value + 20 + ((n - start_num) << 2)); 3735d5f1b60SRayhan Faizel bcm2835_otp_set_row(s->otp, 3745d5f1b60SRayhan Faizel BCM2835_OTP_CUSTOMER_OTP + n, otp_row); 3755d5f1b60SRayhan Faizel } 3765d5f1b60SRayhan Faizel break; 3774b648238SPeter Maydell } 3785d5f1b60SRayhan Faizel 3795d5f1b60SRayhan Faizel /* Device-specific private key */ 3805d5f1b60SRayhan Faizel case RPI_FWREQ_GET_PRIVATE_KEY: 3814b648238SPeter Maydell { 3824b648238SPeter Maydell uint32_t start_num = ldl_le_phys(&s->dma_as, value + 12); 3834b648238SPeter Maydell uint32_t number = ldl_le_phys(&s->dma_as, value + 16); 3845d5f1b60SRayhan Faizel 3855d5f1b60SRayhan Faizel resplen = 8 + 4 * number; 3865d5f1b60SRayhan Faizel 38732f1c201SPeter Maydell for (uint32_t n = start_num; n < start_num + number && 3885d5f1b60SRayhan Faizel n < BCM2835_OTP_PRIVATE_KEY_LEN; n++) { 3894b648238SPeter Maydell uint32_t otp_row = bcm2835_otp_get_row(s->otp, 3905d5f1b60SRayhan Faizel BCM2835_OTP_PRIVATE_KEY + n); 3915d5f1b60SRayhan Faizel stl_le_phys(&s->dma_as, 3925d5f1b60SRayhan Faizel value + 20 + ((n - start_num) << 2), otp_row); 3935d5f1b60SRayhan Faizel } 3945d5f1b60SRayhan Faizel break; 3954b648238SPeter Maydell } 3965d5f1b60SRayhan Faizel case RPI_FWREQ_SET_PRIVATE_KEY: 3974b648238SPeter Maydell { 3984b648238SPeter Maydell uint32_t start_num = ldl_le_phys(&s->dma_as, value + 12); 3994b648238SPeter Maydell uint32_t number = ldl_le_phys(&s->dma_as, value + 16); 4005d5f1b60SRayhan Faizel 4015d5f1b60SRayhan Faizel resplen = 4; 4025d5f1b60SRayhan Faizel 4035d5f1b60SRayhan Faizel /* If row 32 has the lock bit, don't allow further writes */ 4045d5f1b60SRayhan Faizel if (bcm2835_otp_get_row(s->otp, BCM2835_OTP_ROW_32) & 4055d5f1b60SRayhan Faizel BCM2835_OTP_ROW_32_LOCK) { 4065d5f1b60SRayhan Faizel break; 4075d5f1b60SRayhan Faizel } 4085d5f1b60SRayhan Faizel 40932f1c201SPeter Maydell for (uint32_t n = start_num; n < start_num + number && 4105d5f1b60SRayhan Faizel n < BCM2835_OTP_PRIVATE_KEY_LEN; n++) { 4114b648238SPeter Maydell uint32_t otp_row = ldl_le_phys(&s->dma_as, 4125d5f1b60SRayhan Faizel value + 20 + ((n - start_num) << 2)); 4135d5f1b60SRayhan Faizel bcm2835_otp_set_row(s->otp, 4145d5f1b60SRayhan Faizel BCM2835_OTP_PRIVATE_KEY + n, otp_row); 4155d5f1b60SRayhan Faizel } 4165d5f1b60SRayhan Faizel break; 4174b648238SPeter Maydell } 41804f1ab15SAndrew Baumann default: 419e1ecf8c8SPhilippe Mathieu-Daudé qemu_log_mask(LOG_UNIMP, 420e1ecf8c8SPhilippe Mathieu-Daudé "bcm2835_property: unhandled tag 0x%08x\n", tag); 42104f1ab15SAndrew Baumann break; 42204f1ab15SAndrew Baumann } 42304f1ab15SAndrew Baumann 42419845504SPhilippe Mathieu-Daudé trace_bcm2835_mbox_property(tag, bufsize, resplen); 42504f1ab15SAndrew Baumann if (tag == 0) { 42604f1ab15SAndrew Baumann break; 42704f1ab15SAndrew Baumann } 42804f1ab15SAndrew Baumann 429eab71394SAndrew Baumann stl_le_phys(&s->dma_as, value + 8, (1 << 31) | resplen); 43004f1ab15SAndrew Baumann value += bufsize + 12; 43104f1ab15SAndrew Baumann } 43204f1ab15SAndrew Baumann 433355a8cccSGrégory ESTRADE /* Reconfigure framebuffer if required */ 434193100b5SPeter Maydell if (fbconfig_updated) { 435193100b5SPeter Maydell bcm2835_fb_reconfigure(s->fbdev, &fbconfig); 436355a8cccSGrégory ESTRADE } 437355a8cccSGrégory ESTRADE 43804f1ab15SAndrew Baumann /* Buffer response code */ 439eab71394SAndrew Baumann stl_le_phys(&s->dma_as, s->addr + 4, (1 << 31)); 44004f1ab15SAndrew Baumann } 44104f1ab15SAndrew Baumann 44204f1ab15SAndrew Baumann static uint64_t bcm2835_property_read(void *opaque, hwaddr offset, 44304f1ab15SAndrew Baumann unsigned size) 44404f1ab15SAndrew Baumann { 44504f1ab15SAndrew Baumann BCM2835PropertyState *s = opaque; 44604f1ab15SAndrew Baumann uint32_t res = 0; 44704f1ab15SAndrew Baumann 44804f1ab15SAndrew Baumann switch (offset) { 44904f1ab15SAndrew Baumann case MBOX_AS_DATA: 45004f1ab15SAndrew Baumann res = MBOX_CHAN_PROPERTY | s->addr; 45104f1ab15SAndrew Baumann s->pending = false; 45204f1ab15SAndrew Baumann qemu_set_irq(s->mbox_irq, 0); 45304f1ab15SAndrew Baumann break; 45404f1ab15SAndrew Baumann 45504f1ab15SAndrew Baumann case MBOX_AS_PENDING: 45604f1ab15SAndrew Baumann res = s->pending; 45704f1ab15SAndrew Baumann break; 45804f1ab15SAndrew Baumann 45904f1ab15SAndrew Baumann default: 46004f1ab15SAndrew Baumann qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", 46104f1ab15SAndrew Baumann __func__, offset); 46204f1ab15SAndrew Baumann return 0; 46304f1ab15SAndrew Baumann } 46404f1ab15SAndrew Baumann 46504f1ab15SAndrew Baumann return res; 46604f1ab15SAndrew Baumann } 46704f1ab15SAndrew Baumann 46804f1ab15SAndrew Baumann static void bcm2835_property_write(void *opaque, hwaddr offset, 46904f1ab15SAndrew Baumann uint64_t value, unsigned size) 47004f1ab15SAndrew Baumann { 47104f1ab15SAndrew Baumann BCM2835PropertyState *s = opaque; 47204f1ab15SAndrew Baumann 47304f1ab15SAndrew Baumann switch (offset) { 47404f1ab15SAndrew Baumann case MBOX_AS_DATA: 47504f1ab15SAndrew Baumann /* bcm2835_mbox should check our pending status before pushing */ 47604f1ab15SAndrew Baumann assert(!s->pending); 47704f1ab15SAndrew Baumann s->pending = true; 47804f1ab15SAndrew Baumann bcm2835_property_mbox_push(s, value); 47904f1ab15SAndrew Baumann qemu_set_irq(s->mbox_irq, 1); 48004f1ab15SAndrew Baumann break; 48104f1ab15SAndrew Baumann 48204f1ab15SAndrew Baumann default: 48304f1ab15SAndrew Baumann qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", 48404f1ab15SAndrew Baumann __func__, offset); 48504f1ab15SAndrew Baumann return; 48604f1ab15SAndrew Baumann } 48704f1ab15SAndrew Baumann } 48804f1ab15SAndrew Baumann 48904f1ab15SAndrew Baumann static const MemoryRegionOps bcm2835_property_ops = { 49004f1ab15SAndrew Baumann .read = bcm2835_property_read, 49104f1ab15SAndrew Baumann .write = bcm2835_property_write, 49204f1ab15SAndrew Baumann .endianness = DEVICE_NATIVE_ENDIAN, 49304f1ab15SAndrew Baumann .valid.min_access_size = 4, 49404f1ab15SAndrew Baumann .valid.max_access_size = 4, 49504f1ab15SAndrew Baumann }; 49604f1ab15SAndrew Baumann 49704f1ab15SAndrew Baumann static const VMStateDescription vmstate_bcm2835_property = { 49804f1ab15SAndrew Baumann .name = TYPE_BCM2835_PROPERTY, 49904f1ab15SAndrew Baumann .version_id = 1, 50004f1ab15SAndrew Baumann .minimum_version_id = 1, 501e4ea952fSRichard Henderson .fields = (const VMStateField[]) { 50204f1ab15SAndrew Baumann VMSTATE_MACADDR(macaddr, BCM2835PropertyState), 50304f1ab15SAndrew Baumann VMSTATE_UINT32(addr, BCM2835PropertyState), 50404f1ab15SAndrew Baumann VMSTATE_BOOL(pending, BCM2835PropertyState), 50504f1ab15SAndrew Baumann VMSTATE_END_OF_LIST() 50604f1ab15SAndrew Baumann } 50704f1ab15SAndrew Baumann }; 50804f1ab15SAndrew Baumann 50904f1ab15SAndrew Baumann static void bcm2835_property_init(Object *obj) 51004f1ab15SAndrew Baumann { 51104f1ab15SAndrew Baumann BCM2835PropertyState *s = BCM2835_PROPERTY(obj); 51204f1ab15SAndrew Baumann 51304f1ab15SAndrew Baumann memory_region_init_io(&s->iomem, OBJECT(s), &bcm2835_property_ops, s, 51404f1ab15SAndrew Baumann TYPE_BCM2835_PROPERTY, 0x10); 515985c4a4eSAlexander Bulekov 516985c4a4eSAlexander Bulekov /* 517985c4a4eSAlexander Bulekov * bcm2835_property_ops call into bcm2835_mbox, which in-turn reads from 518985c4a4eSAlexander Bulekov * iomem. As such, mark iomem as re-entracy safe. 519985c4a4eSAlexander Bulekov */ 520985c4a4eSAlexander Bulekov s->iomem.disable_reentrancy_guard = true; 521985c4a4eSAlexander Bulekov 52204f1ab15SAndrew Baumann sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); 52304f1ab15SAndrew Baumann sysbus_init_irq(SYS_BUS_DEVICE(s), &s->mbox_irq); 52404f1ab15SAndrew Baumann } 52504f1ab15SAndrew Baumann 52604f1ab15SAndrew Baumann static void bcm2835_property_reset(DeviceState *dev) 52704f1ab15SAndrew Baumann { 52804f1ab15SAndrew Baumann BCM2835PropertyState *s = BCM2835_PROPERTY(dev); 52904f1ab15SAndrew Baumann 53004f1ab15SAndrew Baumann s->pending = false; 53104f1ab15SAndrew Baumann } 53204f1ab15SAndrew Baumann 53304f1ab15SAndrew Baumann static void bcm2835_property_realize(DeviceState *dev, Error **errp) 53404f1ab15SAndrew Baumann { 53504f1ab15SAndrew Baumann BCM2835PropertyState *s = BCM2835_PROPERTY(dev); 53604f1ab15SAndrew Baumann Object *obj; 53704f1ab15SAndrew Baumann 5384d21fcd5SMarkus Armbruster obj = object_property_get_link(OBJECT(dev), "fb", &error_abort); 539355a8cccSGrégory ESTRADE s->fbdev = BCM2835_FB(obj); 540355a8cccSGrégory ESTRADE 5414d21fcd5SMarkus Armbruster obj = object_property_get_link(OBJECT(dev), "dma-mr", &error_abort); 54204f1ab15SAndrew Baumann s->dma_mr = MEMORY_REGION(obj); 543e55a8b37SPhilippe Mathieu-Daudé address_space_init(&s->dma_as, s->dma_mr, TYPE_BCM2835_PROPERTY "-memory"); 54404f1ab15SAndrew Baumann 5455d5f1b60SRayhan Faizel obj = object_property_get_link(OBJECT(dev), "otp", &error_abort); 5465d5f1b60SRayhan Faizel s->otp = BCM2835_OTP(obj); 5475d5f1b60SRayhan Faizel 54804f1ab15SAndrew Baumann /* TODO: connect to MAC address of USB NIC device, once we emulate it */ 54904f1ab15SAndrew Baumann qemu_macaddr_default_if_unset(&s->macaddr); 55004f1ab15SAndrew Baumann 55104f1ab15SAndrew Baumann bcm2835_property_reset(dev); 55204f1ab15SAndrew Baumann } 55304f1ab15SAndrew Baumann 55404f1ab15SAndrew Baumann static Property bcm2835_property_props[] = { 555f0afa731SStephen Warren DEFINE_PROP_UINT32("board-rev", BCM2835PropertyState, board_rev, 0), 556f802ff1eSDaniel Bertalan DEFINE_PROP_STRING("command-line", BCM2835PropertyState, command_line), 55704f1ab15SAndrew Baumann DEFINE_PROP_END_OF_LIST() 55804f1ab15SAndrew Baumann }; 55904f1ab15SAndrew Baumann 56004f1ab15SAndrew Baumann static void bcm2835_property_class_init(ObjectClass *klass, void *data) 56104f1ab15SAndrew Baumann { 56204f1ab15SAndrew Baumann DeviceClass *dc = DEVICE_CLASS(klass); 56304f1ab15SAndrew Baumann 5644f67d30bSMarc-André Lureau device_class_set_props(dc, bcm2835_property_props); 56504f1ab15SAndrew Baumann dc->realize = bcm2835_property_realize; 56604f1ab15SAndrew Baumann dc->vmsd = &vmstate_bcm2835_property; 56704f1ab15SAndrew Baumann } 56804f1ab15SAndrew Baumann 5695e78c98bSBernhard Beschow static const TypeInfo bcm2835_property_info = { 57004f1ab15SAndrew Baumann .name = TYPE_BCM2835_PROPERTY, 57104f1ab15SAndrew Baumann .parent = TYPE_SYS_BUS_DEVICE, 57204f1ab15SAndrew Baumann .instance_size = sizeof(BCM2835PropertyState), 57304f1ab15SAndrew Baumann .class_init = bcm2835_property_class_init, 57404f1ab15SAndrew Baumann .instance_init = bcm2835_property_init, 57504f1ab15SAndrew Baumann }; 57604f1ab15SAndrew Baumann 57704f1ab15SAndrew Baumann static void bcm2835_property_register_types(void) 57804f1ab15SAndrew Baumann { 57904f1ab15SAndrew Baumann type_register_static(&bcm2835_property_info); 58004f1ab15SAndrew Baumann } 58104f1ab15SAndrew Baumann 58204f1ab15SAndrew Baumann type_init(bcm2835_property_register_types) 583