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; 335d5f1b60SRayhan Faizel uint32_t start_num, number, otp_row; 34193100b5SPeter Maydell 35193100b5SPeter Maydell /* 36193100b5SPeter Maydell * Copy the current state of the framebuffer config; we will update 37193100b5SPeter Maydell * this copy as we process tags and then ask the framebuffer to use 38193100b5SPeter Maydell * it at the end. 39193100b5SPeter Maydell */ 40193100b5SPeter Maydell BCM2835FBConfig fbconfig = s->fbdev->config; 41193100b5SPeter Maydell bool fbconfig_updated = false; 4204f1ab15SAndrew Baumann 4304f1ab15SAndrew Baumann value &= ~0xf; 4404f1ab15SAndrew Baumann 4504f1ab15SAndrew Baumann s->addr = value; 4604f1ab15SAndrew Baumann 47eab71394SAndrew Baumann tot_len = ldl_le_phys(&s->dma_as, value); 4804f1ab15SAndrew Baumann 4904f1ab15SAndrew Baumann /* @(addr + 4) : Buffer response code */ 5004f1ab15SAndrew Baumann value = s->addr + 8; 5104f1ab15SAndrew Baumann while (value + 8 <= s->addr + tot_len) { 52eab71394SAndrew Baumann tag = ldl_le_phys(&s->dma_as, value); 53eab71394SAndrew Baumann bufsize = ldl_le_phys(&s->dma_as, value + 4); 5404f1ab15SAndrew Baumann /* @(value + 8) : Request/response indicator */ 5504f1ab15SAndrew Baumann resplen = 0; 5604f1ab15SAndrew Baumann switch (tag) { 5725191826SSergey Kambalin case RPI_FWREQ_PROPERTY_END: 5804f1ab15SAndrew Baumann break; 5925191826SSergey Kambalin case RPI_FWREQ_GET_FIRMWARE_REVISION: 60eab71394SAndrew Baumann stl_le_phys(&s->dma_as, value + 12, 346337); 6104f1ab15SAndrew Baumann resplen = 4; 6204f1ab15SAndrew Baumann break; 6325191826SSergey Kambalin case RPI_FWREQ_GET_BOARD_MODEL: 6404f1ab15SAndrew Baumann qemu_log_mask(LOG_UNIMP, 65e1ecf8c8SPhilippe Mathieu-Daudé "bcm2835_property: 0x%08x get board model NYI\n", 66e1ecf8c8SPhilippe Mathieu-Daudé tag); 6704f1ab15SAndrew Baumann resplen = 4; 6804f1ab15SAndrew Baumann break; 6925191826SSergey Kambalin case RPI_FWREQ_GET_BOARD_REVISION: 70eab71394SAndrew Baumann stl_le_phys(&s->dma_as, value + 12, s->board_rev); 7104f1ab15SAndrew Baumann resplen = 4; 7204f1ab15SAndrew Baumann break; 7325191826SSergey Kambalin case RPI_FWREQ_GET_BOARD_MAC_ADDRESS: 7404f1ab15SAndrew Baumann resplen = sizeof(s->macaddr.a); 75ba06fe8aSPhilippe Mathieu-Daudé dma_memory_write(&s->dma_as, value + 12, s->macaddr.a, resplen, 76ba06fe8aSPhilippe Mathieu-Daudé MEMTXATTRS_UNSPECIFIED); 7704f1ab15SAndrew Baumann break; 7825191826SSergey Kambalin case RPI_FWREQ_GET_BOARD_SERIAL: 7904f1ab15SAndrew Baumann qemu_log_mask(LOG_UNIMP, 80e1ecf8c8SPhilippe Mathieu-Daudé "bcm2835_property: 0x%08x get board serial NYI\n", 81e1ecf8c8SPhilippe Mathieu-Daudé tag); 8204f1ab15SAndrew Baumann resplen = 8; 8304f1ab15SAndrew Baumann break; 8425191826SSergey Kambalin case RPI_FWREQ_GET_ARM_MEMORY: 8504f1ab15SAndrew Baumann /* base */ 86eab71394SAndrew Baumann stl_le_phys(&s->dma_as, value + 12, 0); 8704f1ab15SAndrew Baumann /* size */ 88355a8cccSGrégory ESTRADE stl_le_phys(&s->dma_as, value + 16, s->fbdev->vcram_base); 89355a8cccSGrégory ESTRADE resplen = 8; 90355a8cccSGrégory ESTRADE break; 9125191826SSergey Kambalin case RPI_FWREQ_GET_VC_MEMORY: 92355a8cccSGrégory ESTRADE /* base */ 93355a8cccSGrégory ESTRADE stl_le_phys(&s->dma_as, value + 12, s->fbdev->vcram_base); 94355a8cccSGrégory ESTRADE /* size */ 95355a8cccSGrégory ESTRADE stl_le_phys(&s->dma_as, value + 16, s->fbdev->vcram_size); 9604f1ab15SAndrew Baumann resplen = 8; 9704f1ab15SAndrew Baumann break; 9825191826SSergey Kambalin case RPI_FWREQ_SET_POWER_STATE: 9904f1ab15SAndrew Baumann /* Assume that whatever device they asked for exists, 10004f1ab15SAndrew Baumann * and we'll just claim we set it to the desired state 10104f1ab15SAndrew Baumann */ 102eab71394SAndrew Baumann tmp = ldl_le_phys(&s->dma_as, value + 16); 103eab71394SAndrew Baumann stl_le_phys(&s->dma_as, value + 16, (tmp & 1)); 10404f1ab15SAndrew Baumann resplen = 8; 10504f1ab15SAndrew Baumann break; 10604f1ab15SAndrew Baumann 10704f1ab15SAndrew Baumann /* Clocks */ 10804f1ab15SAndrew Baumann 10925191826SSergey Kambalin case RPI_FWREQ_GET_CLOCK_STATE: 110eab71394SAndrew Baumann stl_le_phys(&s->dma_as, value + 16, 0x1); 11104f1ab15SAndrew Baumann resplen = 8; 11204f1ab15SAndrew Baumann break; 11304f1ab15SAndrew Baumann 11425191826SSergey Kambalin case RPI_FWREQ_SET_CLOCK_STATE: 11504f1ab15SAndrew Baumann qemu_log_mask(LOG_UNIMP, 116e1ecf8c8SPhilippe Mathieu-Daudé "bcm2835_property: 0x%08x set clock state NYI\n", 117e1ecf8c8SPhilippe Mathieu-Daudé tag); 11804f1ab15SAndrew Baumann resplen = 8; 11904f1ab15SAndrew Baumann break; 12004f1ab15SAndrew Baumann 12125191826SSergey Kambalin case RPI_FWREQ_GET_CLOCK_RATE: 12225191826SSergey Kambalin case RPI_FWREQ_GET_MAX_CLOCK_RATE: 12325191826SSergey Kambalin case RPI_FWREQ_GET_MIN_CLOCK_RATE: 124eab71394SAndrew Baumann switch (ldl_le_phys(&s->dma_as, value + 12)) { 12525191826SSergey Kambalin case RPI_FIRMWARE_EMMC_CLK_ID: 1265dc49636SSergey Kambalin stl_le_phys(&s->dma_as, value + 16, RPI_FIRMWARE_EMMC_CLK_RATE); 12704f1ab15SAndrew Baumann break; 12825191826SSergey Kambalin case RPI_FIRMWARE_UART_CLK_ID: 1295dc49636SSergey Kambalin stl_le_phys(&s->dma_as, value + 16, RPI_FIRMWARE_UART_CLK_RATE); 13004f1ab15SAndrew Baumann break; 131074259c0SSergey Kambalin case RPI_FIRMWARE_CORE_CLK_ID: 132074259c0SSergey Kambalin stl_le_phys(&s->dma_as, value + 16, RPI_FIRMWARE_CORE_CLK_RATE); 133074259c0SSergey Kambalin break; 13404f1ab15SAndrew Baumann default: 1355dc49636SSergey Kambalin stl_le_phys(&s->dma_as, value + 16, 1365dc49636SSergey Kambalin RPI_FIRMWARE_DEFAULT_CLK_RATE); 13704f1ab15SAndrew Baumann break; 13804f1ab15SAndrew Baumann } 13904f1ab15SAndrew Baumann resplen = 8; 14004f1ab15SAndrew Baumann break; 14104f1ab15SAndrew Baumann 142cda5a7d6SSergey Kambalin case RPI_FWREQ_GET_CLOCKS: 143cda5a7d6SSergey Kambalin /* TODO: add more clock IDs if needed */ 144cda5a7d6SSergey Kambalin stl_le_phys(&s->dma_as, value + 12, 0); 145cda5a7d6SSergey Kambalin stl_le_phys(&s->dma_as, value + 16, RPI_FIRMWARE_ARM_CLK_ID); 146cda5a7d6SSergey Kambalin resplen = 8; 147cda5a7d6SSergey Kambalin break; 148cda5a7d6SSergey Kambalin 14925191826SSergey Kambalin case RPI_FWREQ_SET_CLOCK_RATE: 15025191826SSergey Kambalin case RPI_FWREQ_SET_MAX_CLOCK_RATE: 15125191826SSergey Kambalin case RPI_FWREQ_SET_MIN_CLOCK_RATE: 15204f1ab15SAndrew Baumann qemu_log_mask(LOG_UNIMP, 153e1ecf8c8SPhilippe Mathieu-Daudé "bcm2835_property: 0x%08x set clock rate NYI\n", 154e1ecf8c8SPhilippe Mathieu-Daudé tag); 15504f1ab15SAndrew Baumann resplen = 8; 15604f1ab15SAndrew Baumann break; 15704f1ab15SAndrew Baumann 15804f1ab15SAndrew Baumann /* Temperature */ 15904f1ab15SAndrew Baumann 16025191826SSergey Kambalin case RPI_FWREQ_GET_TEMPERATURE: 161eab71394SAndrew Baumann stl_le_phys(&s->dma_as, value + 16, 25000); 16204f1ab15SAndrew Baumann resplen = 8; 16304f1ab15SAndrew Baumann break; 16404f1ab15SAndrew Baumann 16525191826SSergey Kambalin case RPI_FWREQ_GET_MAX_TEMPERATURE: 166eab71394SAndrew Baumann stl_le_phys(&s->dma_as, value + 16, 99000); 16704f1ab15SAndrew Baumann resplen = 8; 16804f1ab15SAndrew Baumann break; 16904f1ab15SAndrew Baumann 170355a8cccSGrégory ESTRADE /* Frame buffer */ 171355a8cccSGrégory ESTRADE 17225191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_ALLOCATE: 173193100b5SPeter Maydell stl_le_phys(&s->dma_as, value + 12, fbconfig.base); 17427a5dc7bSSylvain Garrigues stl_le_phys(&s->dma_as, value + 16, 1759a1f03f4SPeter Maydell bcm2835_fb_get_size(&fbconfig)); 176355a8cccSGrégory ESTRADE resplen = 8; 177355a8cccSGrégory ESTRADE break; 17825191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_RELEASE: 179355a8cccSGrégory ESTRADE resplen = 0; 180355a8cccSGrégory ESTRADE break; 18125191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_BLANK: 182355a8cccSGrégory ESTRADE resplen = 4; 183355a8cccSGrégory ESTRADE break; 18425191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_TEST_PHYSICAL_WIDTH_HEIGHT: 18525191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_TEST_VIRTUAL_WIDTH_HEIGHT: 18601f18af9SPeter Maydell resplen = 8; 18701f18af9SPeter Maydell break; 18825191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_SET_PHYSICAL_WIDTH_HEIGHT: 189193100b5SPeter Maydell fbconfig.xres = ldl_le_phys(&s->dma_as, value + 12); 190193100b5SPeter Maydell fbconfig.yres = ldl_le_phys(&s->dma_as, value + 16); 191f8add62cSPeter Maydell bcm2835_fb_validate_config(&fbconfig); 192193100b5SPeter Maydell fbconfig_updated = true; 193f8add62cSPeter Maydell /* fall through */ 19425191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_GET_PHYSICAL_WIDTH_HEIGHT: 195f8add62cSPeter Maydell stl_le_phys(&s->dma_as, value + 12, fbconfig.xres); 196f8add62cSPeter Maydell stl_le_phys(&s->dma_as, value + 16, fbconfig.yres); 197355a8cccSGrégory ESTRADE resplen = 8; 198355a8cccSGrégory ESTRADE break; 19925191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_SET_VIRTUAL_WIDTH_HEIGHT: 20001f18af9SPeter Maydell fbconfig.xres_virtual = ldl_le_phys(&s->dma_as, value + 12); 20101f18af9SPeter Maydell fbconfig.yres_virtual = ldl_le_phys(&s->dma_as, value + 16); 202f8add62cSPeter Maydell bcm2835_fb_validate_config(&fbconfig); 20301f18af9SPeter Maydell fbconfig_updated = true; 204f8add62cSPeter Maydell /* fall through */ 20525191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_GET_VIRTUAL_WIDTH_HEIGHT: 206f8add62cSPeter Maydell stl_le_phys(&s->dma_as, value + 12, fbconfig.xres_virtual); 207f8add62cSPeter Maydell stl_le_phys(&s->dma_as, value + 16, fbconfig.yres_virtual); 20801f18af9SPeter Maydell resplen = 8; 20901f18af9SPeter Maydell break; 21025191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_TEST_DEPTH: 211355a8cccSGrégory ESTRADE resplen = 4; 212355a8cccSGrégory ESTRADE break; 21325191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_SET_DEPTH: 214193100b5SPeter Maydell fbconfig.bpp = ldl_le_phys(&s->dma_as, value + 12); 215f8add62cSPeter Maydell bcm2835_fb_validate_config(&fbconfig); 216193100b5SPeter Maydell fbconfig_updated = true; 217f8add62cSPeter Maydell /* fall through */ 21825191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_GET_DEPTH: 219f8add62cSPeter Maydell stl_le_phys(&s->dma_as, value + 12, fbconfig.bpp); 220355a8cccSGrégory ESTRADE resplen = 4; 221355a8cccSGrégory ESTRADE break; 22225191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_TEST_PIXEL_ORDER: 223355a8cccSGrégory ESTRADE resplen = 4; 224355a8cccSGrégory ESTRADE break; 22525191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_SET_PIXEL_ORDER: 226193100b5SPeter Maydell fbconfig.pixo = ldl_le_phys(&s->dma_as, value + 12); 227f8add62cSPeter Maydell bcm2835_fb_validate_config(&fbconfig); 228193100b5SPeter Maydell fbconfig_updated = true; 229f8add62cSPeter Maydell /* fall through */ 23025191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_GET_PIXEL_ORDER: 231f8add62cSPeter Maydell stl_le_phys(&s->dma_as, value + 12, fbconfig.pixo); 232355a8cccSGrégory ESTRADE resplen = 4; 233355a8cccSGrégory ESTRADE break; 23425191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_TEST_ALPHA_MODE: 235355a8cccSGrégory ESTRADE resplen = 4; 236355a8cccSGrégory ESTRADE break; 23725191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_SET_ALPHA_MODE: 238193100b5SPeter Maydell fbconfig.alpha = ldl_le_phys(&s->dma_as, value + 12); 239f8add62cSPeter Maydell bcm2835_fb_validate_config(&fbconfig); 240193100b5SPeter Maydell fbconfig_updated = true; 241f8add62cSPeter Maydell /* fall through */ 24225191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_GET_ALPHA_MODE: 243f8add62cSPeter Maydell stl_le_phys(&s->dma_as, value + 12, fbconfig.alpha); 244355a8cccSGrégory ESTRADE resplen = 4; 245355a8cccSGrégory ESTRADE break; 24625191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_GET_PITCH: 247193100b5SPeter Maydell stl_le_phys(&s->dma_as, value + 12, 2489a1f03f4SPeter Maydell bcm2835_fb_get_pitch(&fbconfig)); 249355a8cccSGrégory ESTRADE resplen = 4; 250355a8cccSGrégory ESTRADE break; 25125191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_TEST_VIRTUAL_OFFSET: 252355a8cccSGrégory ESTRADE resplen = 8; 253355a8cccSGrégory ESTRADE break; 25425191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_SET_VIRTUAL_OFFSET: 255193100b5SPeter Maydell fbconfig.xoffset = ldl_le_phys(&s->dma_as, value + 12); 256193100b5SPeter Maydell fbconfig.yoffset = ldl_le_phys(&s->dma_as, value + 16); 257f8add62cSPeter Maydell bcm2835_fb_validate_config(&fbconfig); 258193100b5SPeter Maydell fbconfig_updated = true; 259f8add62cSPeter Maydell /* fall through */ 26025191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_GET_VIRTUAL_OFFSET: 261f8add62cSPeter Maydell stl_le_phys(&s->dma_as, value + 12, fbconfig.xoffset); 262f8add62cSPeter Maydell stl_le_phys(&s->dma_as, value + 16, fbconfig.yoffset); 263355a8cccSGrégory ESTRADE resplen = 8; 264355a8cccSGrégory ESTRADE break; 26525191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_GET_OVERSCAN: 26625191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_TEST_OVERSCAN: 26725191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_SET_OVERSCAN: 268355a8cccSGrégory ESTRADE stl_le_phys(&s->dma_as, value + 12, 0); 269355a8cccSGrégory ESTRADE stl_le_phys(&s->dma_as, value + 16, 0); 270355a8cccSGrégory ESTRADE stl_le_phys(&s->dma_as, value + 20, 0); 271355a8cccSGrégory ESTRADE stl_le_phys(&s->dma_as, value + 24, 0); 272355a8cccSGrégory ESTRADE resplen = 16; 273355a8cccSGrégory ESTRADE break; 27425191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_SET_PALETTE: 2750892fffcSPeter Maydell { 2760892fffcSPeter Maydell uint32_t offset = ldl_le_phys(&s->dma_as, value + 12); 2770892fffcSPeter Maydell uint32_t length = ldl_le_phys(&s->dma_as, value + 16); 2780892fffcSPeter Maydell int resp; 2790892fffcSPeter Maydell 2800892fffcSPeter Maydell if (offset > 255 || length < 1 || length > 256) { 2810892fffcSPeter Maydell resp = 1; /* invalid request */ 2820892fffcSPeter Maydell } else { 2830892fffcSPeter Maydell for (uint32_t e = 0; e < length; e++) { 2840892fffcSPeter Maydell uint32_t color = ldl_le_phys(&s->dma_as, value + 20 + (e << 2)); 285355a8cccSGrégory ESTRADE stl_le_phys(&s->dma_as, 2860892fffcSPeter Maydell s->fbdev->vcram_base + ((offset + e) << 2), color); 287355a8cccSGrégory ESTRADE } 2880892fffcSPeter Maydell resp = 0; 2890892fffcSPeter Maydell } 2900892fffcSPeter Maydell stl_le_phys(&s->dma_as, value + 12, resp); 291355a8cccSGrégory ESTRADE resplen = 4; 292355a8cccSGrégory ESTRADE break; 2930892fffcSPeter Maydell } 29425191826SSergey Kambalin case RPI_FWREQ_FRAMEBUFFER_GET_NUM_DISPLAYS: 2953b9a030eSEnrik Berkhan stl_le_phys(&s->dma_as, value + 12, 1); 2963b9a030eSEnrik Berkhan resplen = 4; 2973b9a030eSEnrik Berkhan break; 29804f1ab15SAndrew Baumann 29925191826SSergey Kambalin case RPI_FWREQ_GET_DMA_CHANNELS: 30004f1ab15SAndrew Baumann /* channels 2-5 */ 301eab71394SAndrew Baumann stl_le_phys(&s->dma_as, value + 12, 0x003C); 30204f1ab15SAndrew Baumann resplen = 4; 30304f1ab15SAndrew Baumann break; 30404f1ab15SAndrew Baumann 30525191826SSergey Kambalin case RPI_FWREQ_GET_COMMAND_LINE: 306f802ff1eSDaniel Bertalan /* 307f802ff1eSDaniel Bertalan * We follow the firmware behaviour: no NUL terminator is 308f802ff1eSDaniel Bertalan * written to the buffer, and if the buffer is too short 309f802ff1eSDaniel Bertalan * we report the required length in the response header 310f802ff1eSDaniel Bertalan * and copy nothing to the buffer. 311f802ff1eSDaniel Bertalan */ 312f802ff1eSDaniel Bertalan resplen = strlen(s->command_line); 313f802ff1eSDaniel Bertalan if (bufsize >= resplen) 314f802ff1eSDaniel Bertalan address_space_write(&s->dma_as, value + 12, 315f802ff1eSDaniel Bertalan MEMTXATTRS_UNSPECIFIED, s->command_line, 316f802ff1eSDaniel Bertalan resplen); 31704f1ab15SAndrew Baumann break; 31804f1ab15SAndrew Baumann 319cda5a7d6SSergey Kambalin case RPI_FWREQ_GET_THROTTLED: 320cda5a7d6SSergey Kambalin stl_le_phys(&s->dma_as, value + 12, 0); 321cda5a7d6SSergey Kambalin resplen = 4; 322cda5a7d6SSergey Kambalin break; 323cda5a7d6SSergey Kambalin 324cda5a7d6SSergey Kambalin case RPI_FWREQ_VCHIQ_INIT: 325cda5a7d6SSergey Kambalin stl_le_phys(&s->dma_as, 326cda5a7d6SSergey Kambalin value + offsetof(rpi_firmware_prop_request_t, payload), 327cda5a7d6SSergey Kambalin 0); 328cda5a7d6SSergey Kambalin resplen = VCHI_BUSADDR_SIZE; 329cda5a7d6SSergey Kambalin break; 3305d5f1b60SRayhan Faizel 3315d5f1b60SRayhan Faizel /* Customer OTP */ 3325d5f1b60SRayhan Faizel 3335d5f1b60SRayhan Faizel case RPI_FWREQ_GET_CUSTOMER_OTP: 3345d5f1b60SRayhan Faizel start_num = ldl_le_phys(&s->dma_as, value + 12); 3355d5f1b60SRayhan Faizel number = ldl_le_phys(&s->dma_as, value + 16); 3365d5f1b60SRayhan Faizel 3375d5f1b60SRayhan Faizel resplen = 8 + 4 * number; 3385d5f1b60SRayhan Faizel 339*32f1c201SPeter Maydell for (uint32_t n = start_num; n < start_num + number && 3405d5f1b60SRayhan Faizel n < BCM2835_OTP_CUSTOMER_OTP_LEN; n++) { 3415d5f1b60SRayhan Faizel 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; 3475d5f1b60SRayhan Faizel case RPI_FWREQ_SET_CUSTOMER_OTP: 3485d5f1b60SRayhan Faizel start_num = ldl_le_phys(&s->dma_as, value + 12); 3495d5f1b60SRayhan Faizel number = ldl_le_phys(&s->dma_as, value + 16); 3505d5f1b60SRayhan Faizel 3515d5f1b60SRayhan Faizel resplen = 4; 3525d5f1b60SRayhan Faizel 3535d5f1b60SRayhan Faizel /* Magic numbers to permanently lock customer OTP */ 3545d5f1b60SRayhan Faizel if (start_num == BCM2835_OTP_LOCK_NUM1 && 3555d5f1b60SRayhan Faizel number == BCM2835_OTP_LOCK_NUM2) { 3565d5f1b60SRayhan Faizel bcm2835_otp_set_row(s->otp, 3575d5f1b60SRayhan Faizel BCM2835_OTP_ROW_32, 3585d5f1b60SRayhan Faizel BCM2835_OTP_ROW_32_LOCK); 3595d5f1b60SRayhan Faizel break; 3605d5f1b60SRayhan Faizel } 3615d5f1b60SRayhan Faizel 3625d5f1b60SRayhan Faizel /* If row 32 has the lock bit, don't allow further writes */ 3635d5f1b60SRayhan Faizel if (bcm2835_otp_get_row(s->otp, BCM2835_OTP_ROW_32) & 3645d5f1b60SRayhan Faizel BCM2835_OTP_ROW_32_LOCK) { 3655d5f1b60SRayhan Faizel break; 3665d5f1b60SRayhan Faizel } 3675d5f1b60SRayhan Faizel 368*32f1c201SPeter Maydell for (uint32_t n = start_num; n < start_num + number && 3695d5f1b60SRayhan Faizel n < BCM2835_OTP_CUSTOMER_OTP_LEN; n++) { 3705d5f1b60SRayhan Faizel otp_row = ldl_le_phys(&s->dma_as, 3715d5f1b60SRayhan Faizel value + 20 + ((n - start_num) << 2)); 3725d5f1b60SRayhan Faizel bcm2835_otp_set_row(s->otp, 3735d5f1b60SRayhan Faizel BCM2835_OTP_CUSTOMER_OTP + n, otp_row); 3745d5f1b60SRayhan Faizel } 3755d5f1b60SRayhan Faizel break; 3765d5f1b60SRayhan Faizel 3775d5f1b60SRayhan Faizel /* Device-specific private key */ 3785d5f1b60SRayhan Faizel 3795d5f1b60SRayhan Faizel case RPI_FWREQ_GET_PRIVATE_KEY: 3805d5f1b60SRayhan Faizel start_num = ldl_le_phys(&s->dma_as, value + 12); 3815d5f1b60SRayhan Faizel number = ldl_le_phys(&s->dma_as, value + 16); 3825d5f1b60SRayhan Faizel 3835d5f1b60SRayhan Faizel resplen = 8 + 4 * number; 3845d5f1b60SRayhan Faizel 385*32f1c201SPeter Maydell for (uint32_t n = start_num; n < start_num + number && 3865d5f1b60SRayhan Faizel n < BCM2835_OTP_PRIVATE_KEY_LEN; n++) { 3875d5f1b60SRayhan Faizel otp_row = bcm2835_otp_get_row(s->otp, 3885d5f1b60SRayhan Faizel BCM2835_OTP_PRIVATE_KEY + n); 3895d5f1b60SRayhan Faizel stl_le_phys(&s->dma_as, 3905d5f1b60SRayhan Faizel value + 20 + ((n - start_num) << 2), otp_row); 3915d5f1b60SRayhan Faizel } 3925d5f1b60SRayhan Faizel break; 3935d5f1b60SRayhan Faizel case RPI_FWREQ_SET_PRIVATE_KEY: 3945d5f1b60SRayhan Faizel start_num = ldl_le_phys(&s->dma_as, value + 12); 3955d5f1b60SRayhan Faizel number = ldl_le_phys(&s->dma_as, value + 16); 3965d5f1b60SRayhan Faizel 3975d5f1b60SRayhan Faizel resplen = 4; 3985d5f1b60SRayhan Faizel 3995d5f1b60SRayhan Faizel /* If row 32 has the lock bit, don't allow further writes */ 4005d5f1b60SRayhan Faizel if (bcm2835_otp_get_row(s->otp, BCM2835_OTP_ROW_32) & 4015d5f1b60SRayhan Faizel BCM2835_OTP_ROW_32_LOCK) { 4025d5f1b60SRayhan Faizel break; 4035d5f1b60SRayhan Faizel } 4045d5f1b60SRayhan Faizel 405*32f1c201SPeter Maydell for (uint32_t n = start_num; n < start_num + number && 4065d5f1b60SRayhan Faizel n < BCM2835_OTP_PRIVATE_KEY_LEN; n++) { 4075d5f1b60SRayhan Faizel otp_row = ldl_le_phys(&s->dma_as, 4085d5f1b60SRayhan Faizel value + 20 + ((n - start_num) << 2)); 4095d5f1b60SRayhan Faizel bcm2835_otp_set_row(s->otp, 4105d5f1b60SRayhan Faizel BCM2835_OTP_PRIVATE_KEY + n, otp_row); 4115d5f1b60SRayhan Faizel } 4125d5f1b60SRayhan Faizel break; 41304f1ab15SAndrew Baumann default: 414e1ecf8c8SPhilippe Mathieu-Daudé qemu_log_mask(LOG_UNIMP, 415e1ecf8c8SPhilippe Mathieu-Daudé "bcm2835_property: unhandled tag 0x%08x\n", tag); 41604f1ab15SAndrew Baumann break; 41704f1ab15SAndrew Baumann } 41804f1ab15SAndrew Baumann 41919845504SPhilippe Mathieu-Daudé trace_bcm2835_mbox_property(tag, bufsize, resplen); 42004f1ab15SAndrew Baumann if (tag == 0) { 42104f1ab15SAndrew Baumann break; 42204f1ab15SAndrew Baumann } 42304f1ab15SAndrew Baumann 424eab71394SAndrew Baumann stl_le_phys(&s->dma_as, value + 8, (1 << 31) | resplen); 42504f1ab15SAndrew Baumann value += bufsize + 12; 42604f1ab15SAndrew Baumann } 42704f1ab15SAndrew Baumann 428355a8cccSGrégory ESTRADE /* Reconfigure framebuffer if required */ 429193100b5SPeter Maydell if (fbconfig_updated) { 430193100b5SPeter Maydell bcm2835_fb_reconfigure(s->fbdev, &fbconfig); 431355a8cccSGrégory ESTRADE } 432355a8cccSGrégory ESTRADE 43304f1ab15SAndrew Baumann /* Buffer response code */ 434eab71394SAndrew Baumann stl_le_phys(&s->dma_as, s->addr + 4, (1 << 31)); 43504f1ab15SAndrew Baumann } 43604f1ab15SAndrew Baumann 43704f1ab15SAndrew Baumann static uint64_t bcm2835_property_read(void *opaque, hwaddr offset, 43804f1ab15SAndrew Baumann unsigned size) 43904f1ab15SAndrew Baumann { 44004f1ab15SAndrew Baumann BCM2835PropertyState *s = opaque; 44104f1ab15SAndrew Baumann uint32_t res = 0; 44204f1ab15SAndrew Baumann 44304f1ab15SAndrew Baumann switch (offset) { 44404f1ab15SAndrew Baumann case MBOX_AS_DATA: 44504f1ab15SAndrew Baumann res = MBOX_CHAN_PROPERTY | s->addr; 44604f1ab15SAndrew Baumann s->pending = false; 44704f1ab15SAndrew Baumann qemu_set_irq(s->mbox_irq, 0); 44804f1ab15SAndrew Baumann break; 44904f1ab15SAndrew Baumann 45004f1ab15SAndrew Baumann case MBOX_AS_PENDING: 45104f1ab15SAndrew Baumann res = s->pending; 45204f1ab15SAndrew Baumann break; 45304f1ab15SAndrew Baumann 45404f1ab15SAndrew Baumann default: 45504f1ab15SAndrew Baumann qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", 45604f1ab15SAndrew Baumann __func__, offset); 45704f1ab15SAndrew Baumann return 0; 45804f1ab15SAndrew Baumann } 45904f1ab15SAndrew Baumann 46004f1ab15SAndrew Baumann return res; 46104f1ab15SAndrew Baumann } 46204f1ab15SAndrew Baumann 46304f1ab15SAndrew Baumann static void bcm2835_property_write(void *opaque, hwaddr offset, 46404f1ab15SAndrew Baumann uint64_t value, unsigned size) 46504f1ab15SAndrew Baumann { 46604f1ab15SAndrew Baumann BCM2835PropertyState *s = opaque; 46704f1ab15SAndrew Baumann 46804f1ab15SAndrew Baumann switch (offset) { 46904f1ab15SAndrew Baumann case MBOX_AS_DATA: 47004f1ab15SAndrew Baumann /* bcm2835_mbox should check our pending status before pushing */ 47104f1ab15SAndrew Baumann assert(!s->pending); 47204f1ab15SAndrew Baumann s->pending = true; 47304f1ab15SAndrew Baumann bcm2835_property_mbox_push(s, value); 47404f1ab15SAndrew Baumann qemu_set_irq(s->mbox_irq, 1); 47504f1ab15SAndrew Baumann break; 47604f1ab15SAndrew Baumann 47704f1ab15SAndrew Baumann default: 47804f1ab15SAndrew Baumann qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", 47904f1ab15SAndrew Baumann __func__, offset); 48004f1ab15SAndrew Baumann return; 48104f1ab15SAndrew Baumann } 48204f1ab15SAndrew Baumann } 48304f1ab15SAndrew Baumann 48404f1ab15SAndrew Baumann static const MemoryRegionOps bcm2835_property_ops = { 48504f1ab15SAndrew Baumann .read = bcm2835_property_read, 48604f1ab15SAndrew Baumann .write = bcm2835_property_write, 48704f1ab15SAndrew Baumann .endianness = DEVICE_NATIVE_ENDIAN, 48804f1ab15SAndrew Baumann .valid.min_access_size = 4, 48904f1ab15SAndrew Baumann .valid.max_access_size = 4, 49004f1ab15SAndrew Baumann }; 49104f1ab15SAndrew Baumann 49204f1ab15SAndrew Baumann static const VMStateDescription vmstate_bcm2835_property = { 49304f1ab15SAndrew Baumann .name = TYPE_BCM2835_PROPERTY, 49404f1ab15SAndrew Baumann .version_id = 1, 49504f1ab15SAndrew Baumann .minimum_version_id = 1, 496e4ea952fSRichard Henderson .fields = (const VMStateField[]) { 49704f1ab15SAndrew Baumann VMSTATE_MACADDR(macaddr, BCM2835PropertyState), 49804f1ab15SAndrew Baumann VMSTATE_UINT32(addr, BCM2835PropertyState), 49904f1ab15SAndrew Baumann VMSTATE_BOOL(pending, BCM2835PropertyState), 50004f1ab15SAndrew Baumann VMSTATE_END_OF_LIST() 50104f1ab15SAndrew Baumann } 50204f1ab15SAndrew Baumann }; 50304f1ab15SAndrew Baumann 50404f1ab15SAndrew Baumann static void bcm2835_property_init(Object *obj) 50504f1ab15SAndrew Baumann { 50604f1ab15SAndrew Baumann BCM2835PropertyState *s = BCM2835_PROPERTY(obj); 50704f1ab15SAndrew Baumann 50804f1ab15SAndrew Baumann memory_region_init_io(&s->iomem, OBJECT(s), &bcm2835_property_ops, s, 50904f1ab15SAndrew Baumann TYPE_BCM2835_PROPERTY, 0x10); 510985c4a4eSAlexander Bulekov 511985c4a4eSAlexander Bulekov /* 512985c4a4eSAlexander Bulekov * bcm2835_property_ops call into bcm2835_mbox, which in-turn reads from 513985c4a4eSAlexander Bulekov * iomem. As such, mark iomem as re-entracy safe. 514985c4a4eSAlexander Bulekov */ 515985c4a4eSAlexander Bulekov s->iomem.disable_reentrancy_guard = true; 516985c4a4eSAlexander Bulekov 51704f1ab15SAndrew Baumann sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); 51804f1ab15SAndrew Baumann sysbus_init_irq(SYS_BUS_DEVICE(s), &s->mbox_irq); 51904f1ab15SAndrew Baumann } 52004f1ab15SAndrew Baumann 52104f1ab15SAndrew Baumann static void bcm2835_property_reset(DeviceState *dev) 52204f1ab15SAndrew Baumann { 52304f1ab15SAndrew Baumann BCM2835PropertyState *s = BCM2835_PROPERTY(dev); 52404f1ab15SAndrew Baumann 52504f1ab15SAndrew Baumann s->pending = false; 52604f1ab15SAndrew Baumann } 52704f1ab15SAndrew Baumann 52804f1ab15SAndrew Baumann static void bcm2835_property_realize(DeviceState *dev, Error **errp) 52904f1ab15SAndrew Baumann { 53004f1ab15SAndrew Baumann BCM2835PropertyState *s = BCM2835_PROPERTY(dev); 53104f1ab15SAndrew Baumann Object *obj; 53204f1ab15SAndrew Baumann 5334d21fcd5SMarkus Armbruster obj = object_property_get_link(OBJECT(dev), "fb", &error_abort); 534355a8cccSGrégory ESTRADE s->fbdev = BCM2835_FB(obj); 535355a8cccSGrégory ESTRADE 5364d21fcd5SMarkus Armbruster obj = object_property_get_link(OBJECT(dev), "dma-mr", &error_abort); 53704f1ab15SAndrew Baumann s->dma_mr = MEMORY_REGION(obj); 538e55a8b37SPhilippe Mathieu-Daudé address_space_init(&s->dma_as, s->dma_mr, TYPE_BCM2835_PROPERTY "-memory"); 53904f1ab15SAndrew Baumann 5405d5f1b60SRayhan Faizel obj = object_property_get_link(OBJECT(dev), "otp", &error_abort); 5415d5f1b60SRayhan Faizel s->otp = BCM2835_OTP(obj); 5425d5f1b60SRayhan Faizel 54304f1ab15SAndrew Baumann /* TODO: connect to MAC address of USB NIC device, once we emulate it */ 54404f1ab15SAndrew Baumann qemu_macaddr_default_if_unset(&s->macaddr); 54504f1ab15SAndrew Baumann 54604f1ab15SAndrew Baumann bcm2835_property_reset(dev); 54704f1ab15SAndrew Baumann } 54804f1ab15SAndrew Baumann 54904f1ab15SAndrew Baumann static Property bcm2835_property_props[] = { 550f0afa731SStephen Warren DEFINE_PROP_UINT32("board-rev", BCM2835PropertyState, board_rev, 0), 551f802ff1eSDaniel Bertalan DEFINE_PROP_STRING("command-line", BCM2835PropertyState, command_line), 55204f1ab15SAndrew Baumann DEFINE_PROP_END_OF_LIST() 55304f1ab15SAndrew Baumann }; 55404f1ab15SAndrew Baumann 55504f1ab15SAndrew Baumann static void bcm2835_property_class_init(ObjectClass *klass, void *data) 55604f1ab15SAndrew Baumann { 55704f1ab15SAndrew Baumann DeviceClass *dc = DEVICE_CLASS(klass); 55804f1ab15SAndrew Baumann 5594f67d30bSMarc-André Lureau device_class_set_props(dc, bcm2835_property_props); 56004f1ab15SAndrew Baumann dc->realize = bcm2835_property_realize; 56104f1ab15SAndrew Baumann dc->vmsd = &vmstate_bcm2835_property; 56204f1ab15SAndrew Baumann } 56304f1ab15SAndrew Baumann 5645e78c98bSBernhard Beschow static const TypeInfo bcm2835_property_info = { 56504f1ab15SAndrew Baumann .name = TYPE_BCM2835_PROPERTY, 56604f1ab15SAndrew Baumann .parent = TYPE_SYS_BUS_DEVICE, 56704f1ab15SAndrew Baumann .instance_size = sizeof(BCM2835PropertyState), 56804f1ab15SAndrew Baumann .class_init = bcm2835_property_class_init, 56904f1ab15SAndrew Baumann .instance_init = bcm2835_property_init, 57004f1ab15SAndrew Baumann }; 57104f1ab15SAndrew Baumann 57204f1ab15SAndrew Baumann static void bcm2835_property_register_types(void) 57304f1ab15SAndrew Baumann { 57404f1ab15SAndrew Baumann type_register_static(&bcm2835_property_info); 57504f1ab15SAndrew Baumann } 57604f1ab15SAndrew Baumann 57704f1ab15SAndrew Baumann type_init(bcm2835_property_register_types) 578