| 
									
										
										
										
											2018-06-13 14:29:45 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * early boot framebuffer in guest ram | 
					
						
							|  |  |  |  * configured using fw_cfg | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright Red Hat, Inc. 2017 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Author: | 
					
						
							|  |  |  |  *     Gerd Hoffmann <kraxel@redhat.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This work is licensed under the terms of the GNU GPL, version 2 or later. | 
					
						
							|  |  |  |  * See the COPYING file in the top-level directory. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2019-08-12 07:23:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-13 14:29:45 +02:00
										 |  |  | #include "qemu/osdep.h"
 | 
					
						
							|  |  |  | #include "qapi/error.h"
 | 
					
						
							|  |  |  | #include "hw/loader.h"
 | 
					
						
							|  |  |  | #include "hw/display/ramfb.h"
 | 
					
						
							| 
									
										
										
										
											2020-04-29 13:52:35 +02:00
										 |  |  | #include "hw/display/bochs-vbe.h" /* for limits */
 | 
					
						
							| 
									
										
										
										
											2018-06-13 14:29:45 +02:00
										 |  |  | #include "ui/console.h"
 | 
					
						
							| 
									
										
										
										
											2019-08-12 07:23:38 +02:00
										 |  |  | #include "sysemu/reset.h"
 | 
					
						
							| 
									
										
										
										
											2018-06-13 14:29:45 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | struct QEMU_PACKED RAMFBCfg { | 
					
						
							|  |  |  |     uint64_t addr; | 
					
						
							|  |  |  |     uint32_t fourcc; | 
					
						
							|  |  |  |     uint32_t flags; | 
					
						
							|  |  |  |     uint32_t width; | 
					
						
							|  |  |  |     uint32_t height; | 
					
						
							|  |  |  |     uint32_t stride; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct RAMFBState { | 
					
						
							|  |  |  |     DisplaySurface *ds; | 
					
						
							|  |  |  |     uint32_t width, height; | 
					
						
							|  |  |  |     struct RAMFBCfg cfg; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-13 14:57:29 +03:00
										 |  |  | static void ramfb_unmap_display_surface(pixman_image_t *image, void *unused) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     void *data = pixman_image_get_data(image); | 
					
						
							|  |  |  |     uint32_t size = pixman_image_get_stride(image) * | 
					
						
							|  |  |  |         pixman_image_get_height(image); | 
					
						
							|  |  |  |     cpu_physical_memory_unmap(data, size, 0, 0); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static DisplaySurface *ramfb_create_display_surface(int width, int height, | 
					
						
							|  |  |  |                                                     pixman_format_code_t format, | 
					
						
							| 
									
										
										
										
											2020-04-29 13:52:36 +02:00
										 |  |  |                                                     hwaddr stride, hwaddr addr) | 
					
						
							| 
									
										
										
										
											2019-05-13 14:57:29 +03:00
										 |  |  | { | 
					
						
							|  |  |  |     DisplaySurface *surface; | 
					
						
							| 
									
										
										
										
											2020-04-29 13:52:36 +02:00
										 |  |  |     hwaddr size, mapsize, linesize; | 
					
						
							| 
									
										
										
										
											2019-05-13 14:57:29 +03:00
										 |  |  |     void *data; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-29 13:52:35 +02:00
										 |  |  |     if (width < 16 || width > VBE_DISPI_MAX_XRES || | 
					
						
							|  |  |  |         height < 16 || height > VBE_DISPI_MAX_YRES || | 
					
						
							|  |  |  |         format == 0 /* unknown format */) | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-29 13:52:36 +02:00
										 |  |  |     linesize = width * PIXMAN_FORMAT_BPP(format) / 8; | 
					
						
							|  |  |  |     if (stride == 0) { | 
					
						
							|  |  |  |         stride = linesize; | 
					
						
							| 
									
										
										
										
											2019-05-13 14:57:29 +03:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-29 13:52:36 +02:00
										 |  |  |     mapsize = size = stride * (height - 1) + linesize; | 
					
						
							|  |  |  |     data = cpu_physical_memory_map(addr, &mapsize, false); | 
					
						
							|  |  |  |     if (size != mapsize) { | 
					
						
							|  |  |  |         cpu_physical_memory_unmap(data, mapsize, 0, 0); | 
					
						
							| 
									
										
										
										
											2019-05-13 14:57:29 +03:00
										 |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     surface = qemu_create_displaysurface_from(width, height, | 
					
						
							| 
									
										
										
										
											2020-04-29 13:52:36 +02:00
										 |  |  |                                               format, stride, data); | 
					
						
							| 
									
										
										
										
											2019-05-13 14:57:29 +03:00
										 |  |  |     pixman_image_set_destroy_function(surface->image, | 
					
						
							|  |  |  |                                       ramfb_unmap_display_surface, NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return surface; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-13 14:29:45 +02:00
										 |  |  | static void ramfb_fw_cfg_write(void *dev, off_t offset, size_t len) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     RAMFBState *s = dev; | 
					
						
							| 
									
										
										
										
											2020-04-29 13:52:34 +02:00
										 |  |  |     DisplaySurface *surface; | 
					
						
							|  |  |  |     uint32_t fourcc, format, width, height; | 
					
						
							| 
									
										
										
										
											2019-05-13 14:57:29 +03:00
										 |  |  |     hwaddr stride, addr; | 
					
						
							| 
									
										
										
										
											2018-06-13 14:29:45 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-29 13:52:34 +02:00
										 |  |  |     width  = be32_to_cpu(s->cfg.width); | 
					
						
							|  |  |  |     height = be32_to_cpu(s->cfg.height); | 
					
						
							|  |  |  |     stride = be32_to_cpu(s->cfg.stride); | 
					
						
							|  |  |  |     fourcc = be32_to_cpu(s->cfg.fourcc); | 
					
						
							|  |  |  |     addr   = be64_to_cpu(s->cfg.addr); | 
					
						
							|  |  |  |     format = qemu_drm_format_to_pixman(fourcc); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     surface = ramfb_create_display_surface(width, height, | 
					
						
							|  |  |  |                                            format, stride, addr); | 
					
						
							|  |  |  |     if (!surface) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-06-13 14:29:45 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-29 13:52:34 +02:00
										 |  |  |     s->width = width; | 
					
						
							|  |  |  |     s->height = height; | 
					
						
							|  |  |  |     s->ds = surface; | 
					
						
							| 
									
										
										
										
											2018-06-13 14:29:45 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void ramfb_display_update(QemuConsole *con, RAMFBState *s) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!s->width || !s->height) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (s->ds) { | 
					
						
							|  |  |  |         dpy_gfx_replace_surface(con, s->ds); | 
					
						
							|  |  |  |         s->ds = NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* simple full screen update */ | 
					
						
							|  |  |  |     dpy_gfx_update_full(con); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-29 13:52:31 +02:00
										 |  |  | RAMFBState *ramfb_setup(Error **errp) | 
					
						
							| 
									
										
										
										
											2018-06-13 14:29:45 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     FWCfgState *fw_cfg = fw_cfg_find(); | 
					
						
							|  |  |  |     RAMFBState *s; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!fw_cfg || !fw_cfg->dma_enabled) { | 
					
						
							|  |  |  |         error_setg(errp, "ramfb device requires fw_cfg with DMA"); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     s = g_new0(RAMFBState, 1); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-08 12:38:47 +02:00
										 |  |  |     rom_add_vga("vgabios-ramfb.bin"); | 
					
						
							| 
									
										
										
										
											2018-06-13 14:29:45 +02:00
										 |  |  |     fw_cfg_add_file_callback(fw_cfg, "etc/ramfb", | 
					
						
							|  |  |  |                              NULL, ramfb_fw_cfg_write, s, | 
					
						
							|  |  |  |                              &s->cfg, sizeof(s->cfg), false); | 
					
						
							|  |  |  |     return s; | 
					
						
							|  |  |  | } |