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 tag; 2904f1ab15SAndrew Baumann uint32_t bufsize; 3004f1ab15SAndrew Baumann uint32_t tot_len; 3104f1ab15SAndrew Baumann size_t resplen; 3204f1ab15SAndrew Baumann uint32_t tmp; 33193100b5SPeter Maydell 34193100b5SPeter Maydell /* 35193100b5SPeter Maydell * Copy the current state of the framebuffer config; we will update 36193100b5SPeter Maydell * this copy as we process tags and then ask the framebuffer to use 37193100b5SPeter Maydell * it at the end. 38193100b5SPeter Maydell */ 39193100b5SPeter Maydell BCM2835FBConfig fbconfig = s->fbdev->config; 40193100b5SPeter Maydell bool fbconfig_updated = false; 4104f1ab15SAndrew Baumann 4204f1ab15SAndrew Baumann value &= ~0xf; 4304f1ab15SAndrew Baumann 4404f1ab15SAndrew Baumann s->addr = value; 4504f1ab15SAndrew Baumann 46eab71394SAndrew Baumann tot_len = ldl_le_phys(&s->dma_as, value); 4704f1ab15SAndrew Baumann 4804f1ab15SAndrew Baumann /* @(addr + 4) : Buffer response code */ 4904f1ab15SAndrew Baumann value = s->addr + 8; 5004f1ab15SAndrew Baumann while (value + 8 <= s->addr + tot_len) { 51eab71394SAndrew Baumann tag = ldl_le_phys(&s->dma_as, value); 52eab71394SAndrew Baumann bufsize = ldl_le_phys(&s->dma_as, value + 4); 5304f1ab15SAndrew Baumann /* @(value + 8) : Request/response indicator */ 5404f1ab15SAndrew Baumann resplen = 0; 5504f1ab15SAndrew Baumann switch (tag) { 5625191826SSergey Kambalin case RPI_FWREQ_PROPERTY_END: 5704f1ab15SAndrew Baumann break; 5825191826SSergey Kambalin case RPI_FWREQ_GET_FIRMWARE_REVISION: 59eab71394SAndrew Baumann stl_le_phys(&s->dma_as, value + 12, 346337); 6004f1ab15SAndrew Baumann resplen = 4; 6104f1ab15SAndrew Baumann break; 6225191826SSergey Kambalin case RPI_FWREQ_GET_BOARD_MODEL: 6304f1ab15SAndrew Baumann qemu_log_mask(LOG_UNIMP, 64e1ecf8c8SPhilippe Mathieu-Daudé "bcm2835_property: 0x%08x get board model NYI\n", 65e1ecf8c8SPhilippe Mathieu-Daudé tag); 6604f1ab15SAndrew Baumann resplen = 4; 6704f1ab15SAndrew Baumann break; 6825191826SSergey Kambalin case RPI_FWREQ_GET_BOARD_REVISION: 69eab71394SAndrew Baumann stl_le_phys(&s->dma_as, value + 12, s->board_rev); 7004f1ab15SAndrew Baumann resplen = 4; 7104f1ab15SAndrew Baumann break; 7225191826SSergey Kambalin case RPI_FWREQ_GET_BOARD_MAC_ADDRESS: 7304f1ab15SAndrew Baumann resplen = sizeof(s->macaddr.a); 74ba06fe8aSPhilippe Mathieu-Daudé dma_memory_write(&s->dma_as, value + 12, s->macaddr.a, resplen, 75ba06fe8aSPhilippe Mathieu-Daudé MEMTXATTRS_UNSPECIFIED); 7604f1ab15SAndrew Baumann break; 7725191826SSergey Kambalin case RPI_FWREQ_GET_BOARD_SERIAL: 7804f1ab15SAndrew Baumann qemu_log_mask(LOG_UNIMP, 79e1ecf8c8SPhilippe Mathieu-Daudé "bcm2835_property: 0x%08x get board serial NYI\n", 80e1ecf8c8SPhilippe Mathieu-Daudé tag); 8104f1ab15SAndrew Baumann resplen = 8; 8204f1ab15SAndrew Baumann break; 8325191826SSergey Kambalin case RPI_FWREQ_GET_ARM_MEMORY: 8404f1ab15SAndrew Baumann /* base */ 85eab71394SAndrew Baumann stl_le_phys(&s->dma_as, value + 12, 0); 8604f1ab15SAndrew Baumann /* size */ 87355a8cccSGrégory ESTRADE stl_le_phys(&s->dma_as, value + 16, s->fbdev->vcram_base); 88355a8cccSGrégory ESTRADE resplen = 8; 89355a8cccSGrégory ESTRADE break; 9025191826SSergey Kambalin case RPI_FWREQ_GET_VC_MEMORY: 91355a8cccSGrégory ESTRADE /* base */ 92355a8cccSGrégory ESTRADE stl_le_phys(&s->dma_as, value + 12, s->fbdev->vcram_base); 93355a8cccSGrégory ESTRADE /* size */ 94355a8cccSGrégory ESTRADE stl_le_phys(&s->dma_as, value + 16, s->fbdev->vcram_size); 9504f1ab15SAndrew Baumann resplen = 8; 9604f1ab15SAndrew Baumann break; 9725191826SSergey Kambalin case RPI_FWREQ_SET_POWER_STATE: 9804f1ab15SAndrew Baumann /* Assume that whatever device they asked for exists, 9904f1ab15SAndrew Baumann * and we'll just claim we set it to the desired state 10004f1ab15SAndrew Baumann */ 101eab71394SAndrew Baumann tmp = ldl_le_phys(&s->dma_as, value + 16); 102eab71394SAndrew Baumann stl_le_phys(&s->dma_as, value + 16, (tmp & 1)); 10304f1ab15SAndrew Baumann resplen = 8; 10404f1ab15SAndrew Baumann break; 10504f1ab15SAndrew Baumann 10604f1ab15SAndrew Baumann /* Clocks */ 10704f1ab15SAndrew Baumann 10825191826SSergey Kambalin case RPI_FWREQ_GET_CLOCK_STATE: 109eab71394SAndrew Baumann stl_le_phys(&s->dma_as, value + 16, 0x1); 11004f1ab15SAndrew Baumann resplen = 8; 11104f1ab15SAndrew Baumann break; 11204f1ab15SAndrew Baumann 11325191826SSergey Kambalin case RPI_FWREQ_SET_CLOCK_STATE: 11404f1ab15SAndrew Baumann qemu_log_mask(LOG_UNIMP, 115e1ecf8c8SPhilippe Mathieu-Daudé "bcm2835_property: 0x%08x set clock state NYI\n", 116e1ecf8c8SPhilippe Mathieu-Daudé tag); 11704f1ab15SAndrew Baumann resplen = 8; 11804f1ab15SAndrew Baumann break; 11904f1ab15SAndrew Baumann 12025191826SSergey Kambalin case RPI_FWREQ_GET_CLOCK_RATE: 12125191826SSergey Kambalin case RPI_FWREQ_GET_MAX_CLOCK_RATE: 12225191826SSergey Kambalin case RPI_FWREQ_GET_MIN_CLOCK_RATE: 123eab71394SAndrew Baumann switch (ldl_le_phys(&s->dma_as, value + 12)) { 12425191826SSergey Kambalin case RPI_FIRMWARE_EMMC_CLK_ID: 1255dc49636SSergey Kambalin stl_le_phys(&s->dma_as, value + 16, RPI_FIRMWARE_EMMC_CLK_RATE); 12604f1ab15SAndrew Baumann break; 12725191826SSergey Kambalin case RPI_FIRMWARE_UART_CLK_ID: 1285dc49636SSergey Kambalin stl_le_phys(&s->dma_as, value + 16, RPI_FIRMWARE_UART_CLK_RATE); 12904f1ab15SAndrew Baumann break; 130074259c0SSergey Kambalin case RPI_FIRMWARE_CORE_CLK_ID: 131074259c0SSergey Kambalin stl_le_phys(&s->dma_as, value + 16, RPI_FIRMWARE_CORE_CLK_RATE); 132074259c0SSergey Kambalin break; 13304f1ab15SAndrew Baumann default: 1345dc49636SSergey Kambalin stl_le_phys(&s->dma_as, value + 16, 1355dc49636SSergey Kambalin RPI_FIRMWARE_DEFAULT_CLK_RATE); 13604f1ab15SAndrew Baumann break; 13704f1ab15SAndrew Baumann } 13804f1ab15SAndrew Baumann resplen = 8; 13904f1ab15SAndrew Baumann break; 14004f1ab15SAndrew Baumann 141cda5a7d6SSergey Kambalin case RPI_FWREQ_GET_CLOCKS: 142cda5a7d6SSergey Kambalin /* TODO: add more clock IDs if needed */ 143cda5a7d6SSergey Kambalin stl_le_phys(&s->dma_as, value + 12, 0); 144cda5a7d6SSergey Kambalin stl_le_phys(&s->dma_as, value + 16, RPI_FIRMWARE_ARM_CLK_ID); 145cda5a7d6SSergey Kambalin resplen = 8; 146cda5a7d6SSergey Kambalin break; 147cda5a7d6SSergey Kambalin 14825191826SSergey Kambalin case RPI_FWREQ_SET_CLOCK_RATE: 14925191826SSergey Kambalin case RPI_FWREQ_SET_MAX_CLOCK_RATE: 15025191826SSergey Kambalin case RPI_FWREQ_SET_MIN_CLOCK_RATE: 15104f1ab15SAndrew Baumann qemu_log_mask(LOG_UNIMP, 152e1ecf8c8SPhilippe Mathieu-Daudé "bcm2835_property: 0x%08x set clock rate NYI\n", 153e1ecf8c8SPhilippe Mathieu-Daudé tag); 15404f1ab15SAndrew Baumann resplen = 8; 15504f1ab15SAndrew Baumann break; 15604f1ab15SAndrew Baumann 15704f1ab15SAndrew Baumann /* Temperature */ 15804f1ab15SAndrew Baumann 15925191826SSergey Kambalin case RPI_FWREQ_GET_TEMPERATURE: 160eab71394SAndrew Baumann stl_le_phys(&s->dma_as, value + 16, 25000); 16104f1ab15SAndrew Baumann resplen = 8; 16204f1ab15SAndrew Baumann break; 16304f1ab15SAndrew Baumann 16425191826SSergey Kambalin case RPI_FWREQ_GET_MAX_TEMPERATURE: 165eab71394SAndrew Baumann stl_le_phys(&s->dma_as, value + 16, 99000); 16604f1ab15SAndrew Baumann resplen = 8; 16704f1ab15SAndrew Baumann break; 16804f1ab15SAndrew Baumann 169355a8cccSGrégory ESTRADE /* Frame buffer */ 170355a8cccSGrégory ESTRADE 17125191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_ALLOCATE: 172193100b5SPeter Maydell stl_le_phys(&s->dma_as, value + 12, fbconfig.base); 17327a5dc7bSSylvain Garrigues stl_le_phys(&s->dma_as, value + 16, 1749a1f03f4SPeter Maydell bcm2835_fb_get_size(&fbconfig)); 175355a8cccSGrégory ESTRADE resplen = 8; 176355a8cccSGrégory ESTRADE break; 17725191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_RELEASE: 178355a8cccSGrégory ESTRADE resplen = 0; 179355a8cccSGrégory ESTRADE break; 18025191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_BLANK: 181355a8cccSGrégory ESTRADE resplen = 4; 182355a8cccSGrégory ESTRADE break; 18325191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_TEST_PHYSICAL_WIDTH_HEIGHT: 18425191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_TEST_VIRTUAL_WIDTH_HEIGHT: 18501f18af9SPeter Maydell resplen = 8; 18601f18af9SPeter Maydell break; 18725191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_SET_PHYSICAL_WIDTH_HEIGHT: 188193100b5SPeter Maydell fbconfig.xres = ldl_le_phys(&s->dma_as, value + 12); 189193100b5SPeter Maydell fbconfig.yres = ldl_le_phys(&s->dma_as, value + 16); 190f8add62cSPeter Maydell bcm2835_fb_validate_config(&fbconfig); 191193100b5SPeter Maydell fbconfig_updated = true; 192f8add62cSPeter Maydell /* fall through */ 19325191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_GET_PHYSICAL_WIDTH_HEIGHT: 194f8add62cSPeter Maydell stl_le_phys(&s->dma_as, value + 12, fbconfig.xres); 195f8add62cSPeter Maydell stl_le_phys(&s->dma_as, value + 16, fbconfig.yres); 196355a8cccSGrégory ESTRADE resplen = 8; 197355a8cccSGrégory ESTRADE break; 19825191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_SET_VIRTUAL_WIDTH_HEIGHT: 19901f18af9SPeter Maydell fbconfig.xres_virtual = ldl_le_phys(&s->dma_as, value + 12); 20001f18af9SPeter Maydell fbconfig.yres_virtual = ldl_le_phys(&s->dma_as, value + 16); 201f8add62cSPeter Maydell bcm2835_fb_validate_config(&fbconfig); 20201f18af9SPeter Maydell fbconfig_updated = true; 203f8add62cSPeter Maydell /* fall through */ 20425191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_GET_VIRTUAL_WIDTH_HEIGHT: 205f8add62cSPeter Maydell stl_le_phys(&s->dma_as, value + 12, fbconfig.xres_virtual); 206f8add62cSPeter Maydell stl_le_phys(&s->dma_as, value + 16, fbconfig.yres_virtual); 20701f18af9SPeter Maydell resplen = 8; 20801f18af9SPeter Maydell break; 20925191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_TEST_DEPTH: 210355a8cccSGrégory ESTRADE resplen = 4; 211355a8cccSGrégory ESTRADE break; 21225191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_SET_DEPTH: 213193100b5SPeter Maydell fbconfig.bpp = ldl_le_phys(&s->dma_as, value + 12); 214f8add62cSPeter Maydell bcm2835_fb_validate_config(&fbconfig); 215193100b5SPeter Maydell fbconfig_updated = true; 216f8add62cSPeter Maydell /* fall through */ 21725191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_GET_DEPTH: 218f8add62cSPeter Maydell stl_le_phys(&s->dma_as, value + 12, fbconfig.bpp); 219355a8cccSGrégory ESTRADE resplen = 4; 220355a8cccSGrégory ESTRADE break; 22125191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_TEST_PIXEL_ORDER: 222355a8cccSGrégory ESTRADE resplen = 4; 223355a8cccSGrégory ESTRADE break; 22425191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_SET_PIXEL_ORDER: 225193100b5SPeter Maydell fbconfig.pixo = ldl_le_phys(&s->dma_as, value + 12); 226f8add62cSPeter Maydell bcm2835_fb_validate_config(&fbconfig); 227193100b5SPeter Maydell fbconfig_updated = true; 228f8add62cSPeter Maydell /* fall through */ 22925191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_GET_PIXEL_ORDER: 230f8add62cSPeter Maydell stl_le_phys(&s->dma_as, value + 12, fbconfig.pixo); 231355a8cccSGrégory ESTRADE resplen = 4; 232355a8cccSGrégory ESTRADE break; 23325191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_TEST_ALPHA_MODE: 234355a8cccSGrégory ESTRADE resplen = 4; 235355a8cccSGrégory ESTRADE break; 23625191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_SET_ALPHA_MODE: 237193100b5SPeter Maydell fbconfig.alpha = ldl_le_phys(&s->dma_as, value + 12); 238f8add62cSPeter Maydell bcm2835_fb_validate_config(&fbconfig); 239193100b5SPeter Maydell fbconfig_updated = true; 240f8add62cSPeter Maydell /* fall through */ 24125191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_GET_ALPHA_MODE: 242f8add62cSPeter Maydell stl_le_phys(&s->dma_as, value + 12, fbconfig.alpha); 243355a8cccSGrégory ESTRADE resplen = 4; 244355a8cccSGrégory ESTRADE break; 24525191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_GET_PITCH: 246193100b5SPeter Maydell stl_le_phys(&s->dma_as, value + 12, 2479a1f03f4SPeter Maydell bcm2835_fb_get_pitch(&fbconfig)); 248355a8cccSGrégory ESTRADE resplen = 4; 249355a8cccSGrégory ESTRADE break; 25025191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_TEST_VIRTUAL_OFFSET: 251355a8cccSGrégory ESTRADE resplen = 8; 252355a8cccSGrégory ESTRADE break; 25325191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_SET_VIRTUAL_OFFSET: 254193100b5SPeter Maydell fbconfig.xoffset = ldl_le_phys(&s->dma_as, value + 12); 255193100b5SPeter Maydell fbconfig.yoffset = ldl_le_phys(&s->dma_as, value + 16); 256f8add62cSPeter Maydell bcm2835_fb_validate_config(&fbconfig); 257193100b5SPeter Maydell fbconfig_updated = true; 258f8add62cSPeter Maydell /* fall through */ 25925191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_GET_VIRTUAL_OFFSET: 260f8add62cSPeter Maydell stl_le_phys(&s->dma_as, value + 12, fbconfig.xoffset); 261f8add62cSPeter Maydell stl_le_phys(&s->dma_as, value + 16, fbconfig.yoffset); 262355a8cccSGrégory ESTRADE resplen = 8; 263355a8cccSGrégory ESTRADE break; 26425191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_GET_OVERSCAN: 26525191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_TEST_OVERSCAN: 26625191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_SET_OVERSCAN: 267355a8cccSGrégory ESTRADE stl_le_phys(&s->dma_as, value + 12, 0); 268355a8cccSGrégory ESTRADE stl_le_phys(&s->dma_as, value + 16, 0); 269355a8cccSGrégory ESTRADE stl_le_phys(&s->dma_as, value + 20, 0); 270355a8cccSGrégory ESTRADE stl_le_phys(&s->dma_as, value + 24, 0); 271355a8cccSGrégory ESTRADE resplen = 16; 272355a8cccSGrégory ESTRADE break; 27325191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_SET_PALETTE: 2740892fffcSPeter Maydell { 2750892fffcSPeter Maydell uint32_t offset = ldl_le_phys(&s->dma_as, value + 12); 2760892fffcSPeter Maydell uint32_t length = ldl_le_phys(&s->dma_as, value + 16); 2770892fffcSPeter Maydell int resp; 2780892fffcSPeter Maydell 2790892fffcSPeter Maydell if (offset > 255 || length < 1 || length > 256) { 2800892fffcSPeter Maydell resp = 1; /* invalid request */ 2810892fffcSPeter Maydell } else { 2820892fffcSPeter Maydell for (uint32_t e = 0; e < length; e++) { 2830892fffcSPeter Maydell uint32_t color = ldl_le_phys(&s->dma_as, value + 20 + (e << 2)); 284355a8cccSGrégory ESTRADE stl_le_phys(&s->dma_as, 2850892fffcSPeter Maydell s->fbdev->vcram_base + ((offset + e) << 2), color); 286355a8cccSGrégory ESTRADE } 2870892fffcSPeter Maydell resp = 0; 2880892fffcSPeter Maydell } 2890892fffcSPeter Maydell stl_le_phys(&s->dma_as, value + 12, resp); 290355a8cccSGrégory ESTRADE resplen = 4; 291355a8cccSGrégory ESTRADE break; 2920892fffcSPeter Maydell } 29325191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_GET_NUM_DISPLAYS: 2943b9a030eSEnrik Berkhan stl_le_phys(&s->dma_as, value + 12, 1); 2953b9a030eSEnrik Berkhan resplen = 4; 2963b9a030eSEnrik Berkhan break; 29704f1ab15SAndrew Baumann 29825191826SSergey Kambalin case RPI_FWREQ_GET_DMA_CHANNELS: 29904f1ab15SAndrew Baumann /* channels 2-5 */ 300eab71394SAndrew Baumann stl_le_phys(&s->dma_as, value + 12, 0x003C); 30104f1ab15SAndrew Baumann resplen = 4; 30204f1ab15SAndrew Baumann break; 30304f1ab15SAndrew Baumann 30425191826SSergey Kambalin case RPI_FWREQ_GET_COMMAND_LINE: 305f802ff1eSDaniel Bertalan /* 306f802ff1eSDaniel Bertalan * We follow the firmware behaviour: no NUL terminator is 307f802ff1eSDaniel Bertalan * written to the buffer, and if the buffer is too short 308f802ff1eSDaniel Bertalan * we report the required length in the response header 309f802ff1eSDaniel Bertalan * and copy nothing to the buffer. 310f802ff1eSDaniel Bertalan */ 311f802ff1eSDaniel Bertalan resplen = strlen(s->command_line); 312f802ff1eSDaniel Bertalan if (bufsize >= resplen) 313f802ff1eSDaniel Bertalan address_space_write(&s->dma_as, value + 12, 314f802ff1eSDaniel Bertalan MEMTXATTRS_UNSPECIFIED, s->command_line, 315f802ff1eSDaniel Bertalan resplen); 31604f1ab15SAndrew Baumann break; 31704f1ab15SAndrew Baumann 318cda5a7d6SSergey Kambalin case RPI_FWREQ_GET_THROTTLED: 319cda5a7d6SSergey Kambalin stl_le_phys(&s->dma_as, value + 12, 0); 320cda5a7d6SSergey Kambalin resplen = 4; 321cda5a7d6SSergey Kambalin break; 322cda5a7d6SSergey Kambalin 323cda5a7d6SSergey Kambalin case RPI_FWREQ_VCHIQ_INIT: 324cda5a7d6SSergey Kambalin stl_le_phys(&s->dma_as, 325cda5a7d6SSergey Kambalin value + offsetof(rpi_firmware_prop_request_t, payload), 326cda5a7d6SSergey Kambalin 0); 327cda5a7d6SSergey Kambalin resplen = VCHI_BUSADDR_SIZE; 328cda5a7d6SSergey Kambalin break; 3295d5f1b60SRayhan Faizel 3305d5f1b60SRayhan Faizel /* Customer OTP */ 3315d5f1b60SRayhan Faizel 3325d5f1b60SRayhan Faizel case RPI_FWREQ_GET_CUSTOMER_OTP: 333*4b648238SPeter Maydell { 334*4b648238SPeter Maydell uint32_t start_num = ldl_le_phys(&s->dma_as, value + 12); 335*4b648238SPeter Maydell uint32_t number = ldl_le_phys(&s->dma_as, value + 16); 3365d5f1b60SRayhan Faizel 3375d5f1b60SRayhan Faizel resplen = 8 + 4 * number; 3385d5f1b60SRayhan Faizel 33932f1c201SPeter Maydell for (uint32_t n = start_num; n < start_num + number && 3405d5f1b60SRayhan Faizel n < BCM2835_OTP_CUSTOMER_OTP_LEN; n++) { 341*4b648238SPeter Maydell uint32_t otp_row = bcm2835_otp_get_row(s->otp, 3425d5f1b60SRayhan Faizel BCM2835_OTP_CUSTOMER_OTP + n); 3435d5f1b60SRayhan Faizel stl_le_phys(&s->dma_as, 3445d5f1b60SRayhan Faizel value + 20 + ((n - start_num) << 2), otp_row); 3455d5f1b60SRayhan Faizel } 3465d5f1b60SRayhan Faizel break; 347*4b648238SPeter Maydell } 3485d5f1b60SRayhan Faizel case RPI_FWREQ_SET_CUSTOMER_OTP: 349*4b648238SPeter Maydell { 350*4b648238SPeter Maydell uint32_t start_num = ldl_le_phys(&s->dma_as, value + 12); 351*4b648238SPeter Maydell uint32_t number = ldl_le_phys(&s->dma_as, value + 16); 3525d5f1b60SRayhan Faizel 3535d5f1b60SRayhan Faizel resplen = 4; 3545d5f1b60SRayhan Faizel 3555d5f1b60SRayhan Faizel /* Magic numbers to permanently lock customer OTP */ 3565d5f1b60SRayhan Faizel if (start_num == BCM2835_OTP_LOCK_NUM1 && 3575d5f1b60SRayhan Faizel number == BCM2835_OTP_LOCK_NUM2) { 3585d5f1b60SRayhan Faizel bcm2835_otp_set_row(s->otp, 3595d5f1b60SRayhan Faizel BCM2835_OTP_ROW_32, 3605d5f1b60SRayhan Faizel BCM2835_OTP_ROW_32_LOCK); 3615d5f1b60SRayhan Faizel break; 3625d5f1b60SRayhan Faizel } 3635d5f1b60SRayhan Faizel 3645d5f1b60SRayhan Faizel /* If row 32 has the lock bit, don't allow further writes */ 3655d5f1b60SRayhan Faizel if (bcm2835_otp_get_row(s->otp, BCM2835_OTP_ROW_32) & 3665d5f1b60SRayhan Faizel BCM2835_OTP_ROW_32_LOCK) { 3675d5f1b60SRayhan Faizel break; 3685d5f1b60SRayhan Faizel } 3695d5f1b60SRayhan Faizel 37032f1c201SPeter Maydell for (uint32_t n = start_num; n < start_num + number && 3715d5f1b60SRayhan Faizel n < BCM2835_OTP_CUSTOMER_OTP_LEN; n++) { 372*4b648238SPeter Maydell uint32_t otp_row = ldl_le_phys(&s->dma_as, 3735d5f1b60SRayhan Faizel value + 20 + ((n - start_num) << 2)); 3745d5f1b60SRayhan Faizel bcm2835_otp_set_row(s->otp, 3755d5f1b60SRayhan Faizel BCM2835_OTP_CUSTOMER_OTP + n, otp_row); 3765d5f1b60SRayhan Faizel } 3775d5f1b60SRayhan Faizel break; 378*4b648238SPeter Maydell } 3795d5f1b60SRayhan Faizel 3805d5f1b60SRayhan Faizel /* Device-specific private key */ 3815d5f1b60SRayhan Faizel case RPI_FWREQ_GET_PRIVATE_KEY: 382*4b648238SPeter Maydell { 383*4b648238SPeter Maydell uint32_t start_num = ldl_le_phys(&s->dma_as, value + 12); 384*4b648238SPeter Maydell uint32_t number = ldl_le_phys(&s->dma_as, value + 16); 3855d5f1b60SRayhan Faizel 3865d5f1b60SRayhan Faizel resplen = 8 + 4 * number; 3875d5f1b60SRayhan Faizel 38832f1c201SPeter Maydell for (uint32_t n = start_num; n < start_num + number && 3895d5f1b60SRayhan Faizel n < BCM2835_OTP_PRIVATE_KEY_LEN; n++) { 390*4b648238SPeter Maydell uint32_t otp_row = bcm2835_otp_get_row(s->otp, 3915d5f1b60SRayhan Faizel BCM2835_OTP_PRIVATE_KEY + n); 3925d5f1b60SRayhan Faizel stl_le_phys(&s->dma_as, 3935d5f1b60SRayhan Faizel value + 20 + ((n - start_num) << 2), otp_row); 3945d5f1b60SRayhan Faizel } 3955d5f1b60SRayhan Faizel break; 396*4b648238SPeter Maydell } 3975d5f1b60SRayhan Faizel case RPI_FWREQ_SET_PRIVATE_KEY: 398*4b648238SPeter Maydell { 399*4b648238SPeter Maydell uint32_t start_num = ldl_le_phys(&s->dma_as, value + 12); 400*4b648238SPeter Maydell uint32_t number = ldl_le_phys(&s->dma_as, value + 16); 4015d5f1b60SRayhan Faizel 4025d5f1b60SRayhan Faizel resplen = 4; 4035d5f1b60SRayhan Faizel 4045d5f1b60SRayhan Faizel /* If row 32 has the lock bit, don't allow further writes */ 4055d5f1b60SRayhan Faizel if (bcm2835_otp_get_row(s->otp, BCM2835_OTP_ROW_32) & 4065d5f1b60SRayhan Faizel BCM2835_OTP_ROW_32_LOCK) { 4075d5f1b60SRayhan Faizel break; 4085d5f1b60SRayhan Faizel } 4095d5f1b60SRayhan Faizel 41032f1c201SPeter Maydell for (uint32_t n = start_num; n < start_num + number && 4115d5f1b60SRayhan Faizel n < BCM2835_OTP_PRIVATE_KEY_LEN; n++) { 412*4b648238SPeter Maydell uint32_t otp_row = ldl_le_phys(&s->dma_as, 4135d5f1b60SRayhan Faizel value + 20 + ((n - start_num) << 2)); 4145d5f1b60SRayhan Faizel bcm2835_otp_set_row(s->otp, 4155d5f1b60SRayhan Faizel BCM2835_OTP_PRIVATE_KEY + n, otp_row); 4165d5f1b60SRayhan Faizel } 4175d5f1b60SRayhan Faizel break; 418*4b648238SPeter Maydell } 41904f1ab15SAndrew Baumann default: 420e1ecf8c8SPhilippe Mathieu-Daudé qemu_log_mask(LOG_UNIMP, 421e1ecf8c8SPhilippe Mathieu-Daudé "bcm2835_property: unhandled tag 0x%08x\n", tag); 42204f1ab15SAndrew Baumann break; 42304f1ab15SAndrew Baumann } 42404f1ab15SAndrew Baumann 42519845504SPhilippe Mathieu-Daudé trace_bcm2835_mbox_property(tag, bufsize, resplen); 42604f1ab15SAndrew Baumann if (tag == 0) { 42704f1ab15SAndrew Baumann break; 42804f1ab15SAndrew Baumann } 42904f1ab15SAndrew Baumann 430eab71394SAndrew Baumann stl_le_phys(&s->dma_as, value + 8, (1 << 31) | resplen); 43104f1ab15SAndrew Baumann value += bufsize + 12; 43204f1ab15SAndrew Baumann } 43304f1ab15SAndrew Baumann 434355a8cccSGrégory ESTRADE /* Reconfigure framebuffer if required */ 435193100b5SPeter Maydell if (fbconfig_updated) { 436193100b5SPeter Maydell bcm2835_fb_reconfigure(s->fbdev, &fbconfig); 437355a8cccSGrégory ESTRADE } 438355a8cccSGrégory ESTRADE 43904f1ab15SAndrew Baumann /* Buffer response code */ 440eab71394SAndrew Baumann stl_le_phys(&s->dma_as, s->addr + 4, (1 << 31)); 44104f1ab15SAndrew Baumann } 44204f1ab15SAndrew Baumann 44304f1ab15SAndrew Baumann static uint64_t bcm2835_property_read(void *opaque, hwaddr offset, 44404f1ab15SAndrew Baumann unsigned size) 44504f1ab15SAndrew Baumann { 44604f1ab15SAndrew Baumann BCM2835PropertyState *s = opaque; 44704f1ab15SAndrew Baumann uint32_t res = 0; 44804f1ab15SAndrew Baumann 44904f1ab15SAndrew Baumann switch (offset) { 45004f1ab15SAndrew Baumann case MBOX_AS_DATA: 45104f1ab15SAndrew Baumann res = MBOX_CHAN_PROPERTY | s->addr; 45204f1ab15SAndrew Baumann s->pending = false; 45304f1ab15SAndrew Baumann qemu_set_irq(s->mbox_irq, 0); 45404f1ab15SAndrew Baumann break; 45504f1ab15SAndrew Baumann 45604f1ab15SAndrew Baumann case MBOX_AS_PENDING: 45704f1ab15SAndrew Baumann res = s->pending; 45804f1ab15SAndrew Baumann break; 45904f1ab15SAndrew Baumann 46004f1ab15SAndrew Baumann default: 46104f1ab15SAndrew Baumann qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", 46204f1ab15SAndrew Baumann __func__, offset); 46304f1ab15SAndrew Baumann return 0; 46404f1ab15SAndrew Baumann } 46504f1ab15SAndrew Baumann 46604f1ab15SAndrew Baumann return res; 46704f1ab15SAndrew Baumann } 46804f1ab15SAndrew Baumann 46904f1ab15SAndrew Baumann static void bcm2835_property_write(void *opaque, hwaddr offset, 47004f1ab15SAndrew Baumann uint64_t value, unsigned size) 47104f1ab15SAndrew Baumann { 47204f1ab15SAndrew Baumann BCM2835PropertyState *s = opaque; 47304f1ab15SAndrew Baumann 47404f1ab15SAndrew Baumann switch (offset) { 47504f1ab15SAndrew Baumann case MBOX_AS_DATA: 47604f1ab15SAndrew Baumann /* bcm2835_mbox should check our pending status before pushing */ 47704f1ab15SAndrew Baumann assert(!s->pending); 47804f1ab15SAndrew Baumann s->pending = true; 47904f1ab15SAndrew Baumann bcm2835_property_mbox_push(s, value); 48004f1ab15SAndrew Baumann qemu_set_irq(s->mbox_irq, 1); 48104f1ab15SAndrew Baumann break; 48204f1ab15SAndrew Baumann 48304f1ab15SAndrew Baumann default: 48404f1ab15SAndrew Baumann qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", 48504f1ab15SAndrew Baumann __func__, offset); 48604f1ab15SAndrew Baumann return; 48704f1ab15SAndrew Baumann } 48804f1ab15SAndrew Baumann } 48904f1ab15SAndrew Baumann 49004f1ab15SAndrew Baumann static const MemoryRegionOps bcm2835_property_ops = { 49104f1ab15SAndrew Baumann .read = bcm2835_property_read, 49204f1ab15SAndrew Baumann .write = bcm2835_property_write, 49304f1ab15SAndrew Baumann .endianness = DEVICE_NATIVE_ENDIAN, 49404f1ab15SAndrew Baumann .valid.min_access_size = 4, 49504f1ab15SAndrew Baumann .valid.max_access_size = 4, 49604f1ab15SAndrew Baumann }; 49704f1ab15SAndrew Baumann 49804f1ab15SAndrew Baumann static const VMStateDescription vmstate_bcm2835_property = { 49904f1ab15SAndrew Baumann .name = TYPE_BCM2835_PROPERTY, 50004f1ab15SAndrew Baumann .version_id = 1, 50104f1ab15SAndrew Baumann .minimum_version_id = 1, 502e4ea952fSRichard Henderson .fields = (const VMStateField[]) { 50304f1ab15SAndrew Baumann VMSTATE_MACADDR(macaddr, BCM2835PropertyState), 50404f1ab15SAndrew Baumann VMSTATE_UINT32(addr, BCM2835PropertyState), 50504f1ab15SAndrew Baumann VMSTATE_BOOL(pending, BCM2835PropertyState), 50604f1ab15SAndrew Baumann VMSTATE_END_OF_LIST() 50704f1ab15SAndrew Baumann } 50804f1ab15SAndrew Baumann }; 50904f1ab15SAndrew Baumann 51004f1ab15SAndrew Baumann static void bcm2835_property_init(Object *obj) 51104f1ab15SAndrew Baumann { 51204f1ab15SAndrew Baumann BCM2835PropertyState *s = BCM2835_PROPERTY(obj); 51304f1ab15SAndrew Baumann 51404f1ab15SAndrew Baumann memory_region_init_io(&s->iomem, OBJECT(s), &bcm2835_property_ops, s, 51504f1ab15SAndrew Baumann TYPE_BCM2835_PROPERTY, 0x10); 516985c4a4eSAlexander Bulekov 517985c4a4eSAlexander Bulekov /* 518985c4a4eSAlexander Bulekov * bcm2835_property_ops call into bcm2835_mbox, which in-turn reads from 519985c4a4eSAlexander Bulekov * iomem. As such, mark iomem as re-entracy safe. 520985c4a4eSAlexander Bulekov */ 521985c4a4eSAlexander Bulekov s->iomem.disable_reentrancy_guard = true; 522985c4a4eSAlexander Bulekov 52304f1ab15SAndrew Baumann sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); 52404f1ab15SAndrew Baumann sysbus_init_irq(SYS_BUS_DEVICE(s), &s->mbox_irq); 52504f1ab15SAndrew Baumann } 52604f1ab15SAndrew Baumann 52704f1ab15SAndrew Baumann static void bcm2835_property_reset(DeviceState *dev) 52804f1ab15SAndrew Baumann { 52904f1ab15SAndrew Baumann BCM2835PropertyState *s = BCM2835_PROPERTY(dev); 53004f1ab15SAndrew Baumann 53104f1ab15SAndrew Baumann s->pending = false; 53204f1ab15SAndrew Baumann } 53304f1ab15SAndrew Baumann 53404f1ab15SAndrew Baumann static void bcm2835_property_realize(DeviceState *dev, Error **errp) 53504f1ab15SAndrew Baumann { 53604f1ab15SAndrew Baumann BCM2835PropertyState *s = BCM2835_PROPERTY(dev); 53704f1ab15SAndrew Baumann Object *obj; 53804f1ab15SAndrew Baumann 5394d21fcd5SMarkus Armbruster obj = object_property_get_link(OBJECT(dev), "fb", &error_abort); 540355a8cccSGrégory ESTRADE s->fbdev = BCM2835_FB(obj); 541355a8cccSGrégory ESTRADE 5424d21fcd5SMarkus Armbruster obj = object_property_get_link(OBJECT(dev), "dma-mr", &error_abort); 54304f1ab15SAndrew Baumann s->dma_mr = MEMORY_REGION(obj); 544e55a8b37SPhilippe Mathieu-Daudé address_space_init(&s->dma_as, s->dma_mr, TYPE_BCM2835_PROPERTY "-memory"); 54504f1ab15SAndrew Baumann 5465d5f1b60SRayhan Faizel obj = object_property_get_link(OBJECT(dev), "otp", &error_abort); 5475d5f1b60SRayhan Faizel s->otp = BCM2835_OTP(obj); 5485d5f1b60SRayhan Faizel 54904f1ab15SAndrew Baumann /* TODO: connect to MAC address of USB NIC device, once we emulate it */ 55004f1ab15SAndrew Baumann qemu_macaddr_default_if_unset(&s->macaddr); 55104f1ab15SAndrew Baumann 55204f1ab15SAndrew Baumann bcm2835_property_reset(dev); 55304f1ab15SAndrew Baumann } 55404f1ab15SAndrew Baumann 55504f1ab15SAndrew Baumann static Property bcm2835_property_props[] = { 556f0afa731SStephen Warren DEFINE_PROP_UINT32("board-rev", BCM2835PropertyState, board_rev, 0), 557f802ff1eSDaniel Bertalan DEFINE_PROP_STRING("command-line", BCM2835PropertyState, command_line), 55804f1ab15SAndrew Baumann DEFINE_PROP_END_OF_LIST() 55904f1ab15SAndrew Baumann }; 56004f1ab15SAndrew Baumann 56104f1ab15SAndrew Baumann static void bcm2835_property_class_init(ObjectClass *klass, void *data) 56204f1ab15SAndrew Baumann { 56304f1ab15SAndrew Baumann DeviceClass *dc = DEVICE_CLASS(klass); 56404f1ab15SAndrew Baumann 5654f67d30bSMarc-André Lureau device_class_set_props(dc, bcm2835_property_props); 56604f1ab15SAndrew Baumann dc->realize = bcm2835_property_realize; 56704f1ab15SAndrew Baumann dc->vmsd = &vmstate_bcm2835_property; 56804f1ab15SAndrew Baumann } 56904f1ab15SAndrew Baumann 5705e78c98bSBernhard Beschow static const TypeInfo bcm2835_property_info = { 57104f1ab15SAndrew Baumann .name = TYPE_BCM2835_PROPERTY, 57204f1ab15SAndrew Baumann .parent = TYPE_SYS_BUS_DEVICE, 57304f1ab15SAndrew Baumann .instance_size = sizeof(BCM2835PropertyState), 57404f1ab15SAndrew Baumann .class_init = bcm2835_property_class_init, 57504f1ab15SAndrew Baumann .instance_init = bcm2835_property_init, 57604f1ab15SAndrew Baumann }; 57704f1ab15SAndrew Baumann 57804f1ab15SAndrew Baumann static void bcm2835_property_register_types(void) 57904f1ab15SAndrew Baumann { 58004f1ab15SAndrew Baumann type_register_static(&bcm2835_property_info); 58104f1ab15SAndrew Baumann } 58204f1ab15SAndrew Baumann 58304f1ab15SAndrew Baumann type_init(bcm2835_property_register_types) 584