xref: /openbsd/sys/arch/amd64/amd64/efifb.c (revision 63294167)
1 /*	$OpenBSD: efifb.c,v 1.34 2022/07/15 17:57:25 kettenis Exp $	*/
2 
3 /*
4  * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net>
5  * Copyright (c) 2016 joshua stein <jcs@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/param.h>
21 #include <sys/systm.h>
22 
23 #include <uvm/uvm_extern.h>
24 #include <machine/bus.h>
25 
26 #include <dev/wscons/wsconsio.h>
27 #include <dev/wscons/wsdisplayvar.h>
28 #include <dev/rasops/rasops.h>
29 #include <dev/pci/pcivar.h>
30 
31 #include <machine/biosvar.h>
32 #include <machine/efifbvar.h>
33 
34 extern void mainbus_efifb_reattach(void);
35 
36 /* coreboot tables */
37 
38 struct cb_header {
39 	union {
40 		uint8_t signature[4]; /* "LBIO" */
41 		uint32_t signature32;
42 	};
43 	uint32_t	header_bytes;
44 	uint32_t	header_checksum;
45 	uint32_t	table_bytes;
46 	uint32_t	table_checksum;
47 	uint32_t	table_entries;
48 };
49 
50 struct cb_framebuffer {
51 	uint64_t	physical_address;
52 	uint32_t	x_resolution;
53 	uint32_t	y_resolution;
54 	uint32_t	bytes_per_line;
55 	uint8_t		bits_per_pixel;
56 	uint8_t		red_mask_pos;
57 	uint8_t		red_mask_size;
58 	uint8_t		green_mask_pos;
59 	uint8_t		green_mask_size;
60 	uint8_t		blue_mask_pos;
61 	uint8_t		blue_mask_size;
62 	uint8_t		reserved_mask_pos;
63 	uint8_t		reserved_mask_size;
64 };
65 
66 struct cb_entry {
67 	uint32_t	tag;
68 #define CB_TAG_VERSION          0x0004
69 #define CB_TAG_FORWARD          0x0011
70 #define CB_TAG_FRAMEBUFFER      0x0012
71 	uint32_t	size;
72 	union {
73 		char	string[0];
74 		uint64_t forward;
75 		struct cb_framebuffer fb;
76 	} u;
77 };
78 
79 struct efifb {
80 	struct rasops_info	 rinfo;
81 	int			 depth;
82 	paddr_t			 paddr;
83 	psize_t			 psize;
84 
85 	struct cb_framebuffer	 cb_table_fb;
86 };
87 
88 struct efifb_softc {
89 	struct device		 sc_dev;
90 	struct efifb		*sc_fb;
91 };
92 
93 int	 efifb_match(struct device *, void *, void *);
94 void	 efifb_attach(struct device *, struct device *, void *);
95 void	 efifb_rasops_init(struct efifb *, int);
96 int	 efifb_ioctl(void *, u_long, caddr_t, int, struct proc *);
97 paddr_t	 efifb_mmap(void *, off_t, int);
98 int	 efifb_alloc_screen(void *, const struct wsscreen_descr *, void **,
99 	    int *, int *, uint32_t *);
100 void	 efifb_efiinfo_init(struct efifb *);
101 void	 efifb_cnattach_common(void);
102 vaddr_t	 efifb_early_map(paddr_t);
103 void	 efifb_early_cleanup(void);
104 
105 struct cb_framebuffer *cb_find_fb(paddr_t);
106 
107 const struct cfattach efifb_ca = {
108 	sizeof(struct efifb_softc), efifb_match, efifb_attach, NULL
109 };
110 
111 #define	EFIFB_WIDTH	160
112 #define	EFIFB_HEIGHT	160
113 
114 struct wsscreen_descr efifb_std_descr = { "std" };
115 
116 const struct wsscreen_descr *efifb_descrs[] = {
117 	&efifb_std_descr
118 };
119 
120 const struct wsscreen_list efifb_screen_list = {
121 	nitems(efifb_descrs), efifb_descrs
122 };
123 
124 struct wsdisplay_accessops efifb_accessops = {
125 	.ioctl = efifb_ioctl,
126 	.mmap = efifb_mmap,
127 	.alloc_screen = efifb_alloc_screen,
128 	.free_screen = rasops_free_screen,
129 	.show_screen = rasops_show_screen,
130 	.getchar = rasops_getchar,
131 	.load_font = rasops_load_font,
132 	.list_font = rasops_list_font,
133 	.scrollback = rasops_scrollback,
134 };
135 
136 struct cfdriver efifb_cd = {
137 	NULL, "efifb", DV_DULL
138 };
139 
140 int efifb_detached;
141 struct efifb efifb_console;
142 struct wsdisplay_charcell efifb_bs[EFIFB_HEIGHT * EFIFB_WIDTH];
143 
144 int
efifb_match(struct device * parent,void * cf,void * aux)145 efifb_match(struct device *parent, void *cf, void *aux)
146 {
147 	struct efifb_attach_args *eaa = aux;
148 
149 	if (efifb_detached)
150 		return 0;
151 
152 	if (strcmp(eaa->eaa_name, efifb_cd.cd_name) == 0) {
153 		if (efifb_console.paddr != 0)
154 			return 1;
155 		if (bios_efiinfo != NULL && bios_efiinfo->fb_addr != 0)
156 			return 1;
157 	}
158 
159 	return 0;
160 }
161 
162 void
efifb_attach(struct device * parent,struct device * self,void * aux)163 efifb_attach(struct device *parent, struct device *self, void *aux)
164 {
165 	struct efifb		*fb;
166 	struct efifb_softc	*sc = (struct efifb_softc *)self;
167 	struct wsemuldisplaydev_attach_args aa;
168 	struct rasops_info 	*ri;
169 	int			 console = 0, ccol = 0, crow = 0;
170 	bus_space_tag_t		 iot = X86_BUS_SPACE_MEM;
171 	bus_space_handle_t	 ioh;
172 
173 	if (efifb_console.paddr != 0) {
174 		fb = &efifb_console;
175 		ri = &fb->rinfo;
176 		console = 1;
177 	} else {
178 		KASSERT(bios_efiinfo != NULL && bios_efiinfo->fb_addr != 0);
179 
180 		if ((fb = malloc(sizeof(*fb), M_DEVBUF, M_ZERO | M_NOWAIT))
181 		    == NULL)
182 			return;
183 
184 		ri = &fb->rinfo;
185 		efifb_efiinfo_init(fb);
186 
187 		if (bus_space_map(iot, fb->paddr, fb->psize,
188 		    BUS_SPACE_MAP_PREFETCHABLE | BUS_SPACE_MAP_LINEAR,
189 		    &ioh) != 0) {
190 			free(fb, M_DEVBUF, sizeof(*fb));
191 			return;
192 		}
193 		ri->ri_bits = bus_space_vaddr(iot, ioh);
194 		efifb_rasops_init(fb, RI_VCONS);
195 		efifb_std_descr.ncols = ri->ri_cols;
196 		efifb_std_descr.nrows = ri->ri_rows;
197 		efifb_std_descr.textops = &ri->ri_ops;
198 		efifb_std_descr.fontwidth = ri->ri_font->fontwidth;
199 		efifb_std_descr.fontheight = ri->ri_font->fontheight;
200 		efifb_std_descr.capabilities = ri->ri_caps;
201 	}
202 
203 	sc->sc_fb = fb;
204 	printf(": %dx%d, %dbpp\n", ri->ri_width, ri->ri_height, ri->ri_depth);
205 
206 	if (console) {
207 		uint32_t defattr = 0;
208 
209 		ccol = ri->ri_ccol;
210 		crow = ri->ri_crow;
211 
212 		efifb_rasops_init(fb, RI_VCONS);
213 
214 		ri->ri_ops.pack_attr(ri->ri_active, 0, 0, 0, &defattr);
215 		wsdisplay_cnattach(&efifb_std_descr, ri->ri_active, ccol, crow,
216 		    defattr);
217 	}
218 
219 	ri->ri_hw = sc;
220 	memset(&aa, 0, sizeof(aa));
221 	aa.console = console;
222 	aa.scrdata = &efifb_screen_list;
223 	aa.accessops = &efifb_accessops;
224 	aa.accesscookie = ri;
225 	aa.defaultscreens = 0;
226 
227 	config_found_sm(self, &aa, wsemuldisplaydevprint,
228 	    wsemuldisplaydevsubmatch);
229 }
230 
231 void
efifb_rasops_init(struct efifb * fb,int flags)232 efifb_rasops_init(struct efifb *fb, int flags)
233 {
234 #define bmnum(_x) (fls(_x) - ffs(_x) + 1)
235 #define bmpos(_x) (ffs(_x) - 1)
236 	struct rasops_info	*ri = &fb->rinfo;
237 
238 	if (efifb_console.cb_table_fb.x_resolution) {
239 		ri->ri_width = efifb_console.cb_table_fb.x_resolution;
240 		ri->ri_height = efifb_console.cb_table_fb.y_resolution;
241 		ri->ri_depth = fb->depth;
242 		ri->ri_stride = efifb_console.cb_table_fb.bytes_per_line;
243 		ri->ri_rnum = efifb_console.cb_table_fb.red_mask_size;
244 		ri->ri_rpos = efifb_console.cb_table_fb.red_mask_pos;
245 		ri->ri_gnum = efifb_console.cb_table_fb.green_mask_size;
246 		ri->ri_gpos = efifb_console.cb_table_fb.green_mask_pos;
247 		ri->ri_bnum = efifb_console.cb_table_fb.blue_mask_size;
248 		ri->ri_bpos = efifb_console.cb_table_fb.blue_mask_pos;
249 	} else {
250 		ri->ri_width = bios_efiinfo->fb_width;
251 		ri->ri_height = bios_efiinfo->fb_height;
252 		ri->ri_depth = fb->depth;
253 		ri->ri_stride = bios_efiinfo->fb_pixpsl * (fb->depth / 8);
254 		ri->ri_rnum = bmnum(bios_efiinfo->fb_red_mask);
255 		ri->ri_rpos = bmpos(bios_efiinfo->fb_red_mask);
256 		ri->ri_gnum = bmnum(bios_efiinfo->fb_green_mask);
257 		ri->ri_gpos = bmpos(bios_efiinfo->fb_green_mask);
258 		ri->ri_bnum = bmnum(bios_efiinfo->fb_blue_mask);
259 		ri->ri_bpos = bmpos(bios_efiinfo->fb_blue_mask);
260 	}
261 	ri->ri_bs = efifb_bs;
262 	/* if reinitializing, it is important to not clear all the flags */
263 	ri->ri_flg &= ~RI_CLEAR;
264 	ri->ri_flg |= flags | RI_CENTER | RI_WRONLY;
265 	rasops_init(ri, EFIFB_HEIGHT, EFIFB_WIDTH);
266 }
267 
268 int
efifb_ioctl(void * v,u_long cmd,caddr_t data,int flag,struct proc * p)269 efifb_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
270 {
271 	struct rasops_info	*ri = v;
272 	struct wsdisplay_fbinfo	*wdf;
273 
274 	switch (cmd) {
275 	case WSDISPLAYIO_GETPARAM:
276 		if (ws_get_param != NULL)
277 			return (*ws_get_param)((struct wsdisplay_param *)data);
278 		else
279 			return (-1);
280 	case WSDISPLAYIO_SETPARAM:
281 		if (ws_set_param != NULL)
282 			return (*ws_set_param)((struct wsdisplay_param *)data);
283 		else
284 			return (-1);
285 	case WSDISPLAYIO_GTYPE:
286 		*(u_int *)data = WSDISPLAY_TYPE_EFIFB;
287 		break;
288 	case WSDISPLAYIO_GINFO:
289 		wdf = (struct wsdisplay_fbinfo *)data;
290 		wdf->width = ri->ri_width;
291 		wdf->height = ri->ri_height;
292 		wdf->depth = ri->ri_depth;
293 		wdf->stride = ri->ri_stride;
294 		wdf->offset = 0;
295 		wdf->cmsize = 0;	/* color map is unavailable */
296 		break;
297 	case WSDISPLAYIO_LINEBYTES:
298 		*(u_int *)data = ri->ri_stride;
299 		break;
300 	case WSDISPLAYIO_SMODE:
301 		break;
302 	case WSDISPLAYIO_GETSUPPORTEDDEPTH:
303 		switch (ri->ri_depth) {
304 		case 32:
305 			*(u_int *)data = WSDISPLAYIO_DEPTH_24_32;
306 			break;
307 		case 24:
308 			*(u_int *)data = WSDISPLAYIO_DEPTH_24_24;
309 			break;
310 		case 16:
311 			*(u_int *)data = WSDISPLAYIO_DEPTH_16;
312 			break;
313 		case 15:
314 			*(u_int *)data = WSDISPLAYIO_DEPTH_15;
315 			break;
316 		case 8:
317 			*(u_int *)data = WSDISPLAYIO_DEPTH_8;
318 			break;
319 		default:
320 			return (-1);
321 		}
322 		break;
323 	default:
324 		return (-1);
325 	}
326 
327 	return (0);
328 }
329 
330 paddr_t
efifb_mmap(void * v,off_t off,int prot)331 efifb_mmap(void *v, off_t off, int prot)
332 {
333 	struct rasops_info	*ri = v;
334 	struct efifb_softc	*sc = ri->ri_hw;
335 
336 	if (off < 0 || off >= sc->sc_fb->psize)
337 		return (-1);
338 
339 	return ((sc->sc_fb->paddr + off) | PMAP_WC);
340 }
341 
342 int
efifb_alloc_screen(void * v,const struct wsscreen_descr * descr,void ** cookiep,int * curxp,int * curyp,uint32_t * attrp)343 efifb_alloc_screen(void *v, const struct wsscreen_descr *descr,
344     void **cookiep, int *curxp, int *curyp, uint32_t *attrp)
345 {
346 	struct rasops_info	*ri = v;
347 
348 	return rasops_alloc_screen(ri, cookiep, curxp, curyp, attrp);
349 }
350 
351 int
efifb_cnattach(void)352 efifb_cnattach(void)
353 {
354 	if (bios_efiinfo == NULL || bios_efiinfo->fb_addr == 0)
355 		return (-1);
356 
357 	memset(&efifb_console, 0, sizeof(efifb_console));
358 	efifb_efiinfo_init(&efifb_console);
359 	efifb_cnattach_common();
360 
361 	return (0);
362 }
363 
364 void
efifb_efiinfo_init(struct efifb * fb)365 efifb_efiinfo_init(struct efifb *fb)
366 {
367 	fb->paddr = bios_efiinfo->fb_addr;
368 	fb->depth = max(fb->depth, fls(bios_efiinfo->fb_red_mask));
369 	fb->depth = max(fb->depth, fls(bios_efiinfo->fb_green_mask));
370 	fb->depth = max(fb->depth, fls(bios_efiinfo->fb_blue_mask));
371 	fb->depth = max(fb->depth, fls(bios_efiinfo->fb_reserved_mask));
372 	fb->psize = bios_efiinfo->fb_height *
373 	    bios_efiinfo->fb_pixpsl * (fb->depth / 8);
374 }
375 
376 void
efifb_cnattach_common(void)377 efifb_cnattach_common(void)
378 {
379 	struct efifb		*fb = &efifb_console;
380 	struct rasops_info	*ri = &fb->rinfo;
381 	uint32_t		 defattr = 0;
382 
383 	ri->ri_bits = (u_char *)efifb_early_map(fb->paddr);
384 
385 	efifb_rasops_init(fb, RI_CLEAR);
386 
387 	efifb_std_descr.ncols = ri->ri_cols;
388 	efifb_std_descr.nrows = ri->ri_rows;
389 	efifb_std_descr.textops = &ri->ri_ops;
390 	efifb_std_descr.fontwidth = ri->ri_font->fontwidth;
391 	efifb_std_descr.fontheight = ri->ri_font->fontheight;
392 	efifb_std_descr.capabilities = ri->ri_caps;
393 
394 	ri->ri_ops.pack_attr(ri, 0, 0, 0, &defattr);
395 	wsdisplay_cnattach(&efifb_std_descr, ri, 0, 0, defattr);
396 }
397 
398 void
efifb_cnremap(void)399 efifb_cnremap(void)
400 {
401 	struct efifb		*fb = &efifb_console;
402 	struct rasops_info	*ri = &fb->rinfo;
403 	bus_space_tag_t		 iot = X86_BUS_SPACE_MEM;
404 	bus_space_handle_t	 ioh;
405 
406 	if (fb->paddr == 0)
407 		return;
408 
409 	if (_bus_space_map(iot, fb->paddr, fb->psize,
410 	    BUS_SPACE_MAP_PREFETCHABLE | BUS_SPACE_MAP_LINEAR, &ioh))
411 		panic("can't remap framebuffer");
412 	ri->ri_origbits = bus_space_vaddr(iot, ioh);
413 
414 	efifb_rasops_init(fb, 0);
415 
416 	efifb_early_cleanup();
417 }
418 
419 int
efifb_is_console(struct pci_attach_args * pa)420 efifb_is_console(struct pci_attach_args *pa)
421 {
422 	pci_chipset_tag_t pc = pa->pa_pc;
423 	pcitag_t tag = pa->pa_tag;
424 	pcireg_t type;
425 	bus_addr_t base;
426 	bus_size_t size;
427 	int reg;
428 
429 	for (reg = PCI_MAPREG_START; reg < PCI_MAPREG_END; reg += 4) {
430 		if (!pci_mapreg_probe(pc, tag, reg, &type))
431 			continue;
432 
433 		if (type == PCI_MAPREG_TYPE_IO)
434 			continue;
435 
436 		if (pci_mapreg_info(pc, tag, reg, type, &base, &size, NULL))
437 			continue;
438 
439 		if (efifb_console.paddr >= base &&
440 		    efifb_console.paddr < base + size)
441 			return 1;
442 
443 		if (type & PCI_MAPREG_MEM_TYPE_64BIT)
444 			reg += 4;
445 	}
446 
447 	return 0;
448 }
449 
450 int
efifb_is_primary(struct pci_attach_args * pa)451 efifb_is_primary(struct pci_attach_args *pa)
452 {
453 	pci_chipset_tag_t pc = pa->pa_pc;
454 	pcitag_t tag = pa->pa_tag;
455 	pcireg_t type;
456 	bus_addr_t base;
457 	bus_size_t size;
458 	int reg;
459 
460 	for (reg = PCI_MAPREG_START; reg < PCI_MAPREG_END; reg += 4) {
461 		if (!pci_mapreg_probe(pc, tag, reg, &type))
462 			continue;
463 
464 		if (type == PCI_MAPREG_TYPE_IO)
465 			continue;
466 
467 		if (pci_mapreg_info(pc, tag, reg, type, &base, &size, NULL))
468 			continue;
469 
470 		if (bios_efiinfo != NULL &&
471 		    bios_efiinfo->fb_addr >= base &&
472 		    bios_efiinfo->fb_addr < base + size)
473 			return 1;
474 
475 		if (efifb_console.paddr >= base &&
476 		    efifb_console.paddr < base + size)
477 			return 1;
478 
479 		if (type & PCI_MAPREG_MEM_TYPE_64BIT)
480 			reg += 4;
481 	}
482 
483 	return 0;
484 }
485 
486 void
efifb_detach(void)487 efifb_detach(void)
488 {
489 	efifb_detached = 1;
490 }
491 
492 void
efifb_reattach(void)493 efifb_reattach(void)
494 {
495 	efifb_detached = 0;
496 	mainbus_efifb_reattach();
497 }
498 
499 int
efifb_cb_cnattach(void)500 efifb_cb_cnattach(void)
501 {
502 	struct cb_framebuffer *cb_fb = cb_find_fb((paddr_t)0x0);
503 
504 	if (cb_fb == NULL || !cb_fb->x_resolution)
505 		return (-1);
506 
507 	memset(&efifb_console, 0, sizeof(efifb_console));
508 	memcpy(&efifb_console.cb_table_fb, cb_fb,
509 	    sizeof(struct cb_framebuffer));
510 
511 	efifb_console.paddr = cb_fb->physical_address;
512 	efifb_console.depth = cb_fb->bits_per_pixel;
513 	efifb_console.psize = cb_fb->y_resolution * cb_fb->bytes_per_line;
514 
515 	efifb_cnattach_common();
516 
517 	return (0);
518 }
519 
520 int
efifb_cb_found(void)521 efifb_cb_found(void)
522 {
523 	return (efifb_console.paddr && efifb_console.cb_table_fb.x_resolution);
524 }
525 
526 static uint16_t
cb_checksum(const void * addr,unsigned size)527 cb_checksum(const void *addr, unsigned size)
528 {
529 	const uint16_t *p = addr;
530 	unsigned i, n = size / 2;
531 	uint32_t sum = 0;
532 
533 	for (i = 0; i < n; i++)
534 		sum += p[i];
535 
536 	sum = (sum >> 16) + (sum & 0xffff);
537 	sum += (sum >> 16);
538 	sum = ~sum & 0xffff;
539 
540 	return (uint16_t)sum;
541 }
542 
543 struct cb_framebuffer *
cb_find_fb(paddr_t addr)544 cb_find_fb(paddr_t addr)
545 {
546 	int i, j;
547 
548 	for (i = 0; i < (4 * 1024); i += 16) {
549 		struct cb_header *cbh;
550 		struct cb_entry *cbe;
551 		paddr_t cbtable;
552 
553 		cbh = (struct cb_header *)(PMAP_DIRECT_MAP(addr + i));
554 		if (memcmp(cbh->signature, "LBIO", 4) != 0)
555 			continue;
556 
557 		if (!cbh->header_bytes)
558 			continue;
559 
560 		if (cb_checksum(cbh, sizeof(*cbh)) != 0)
561 			return NULL;
562 
563 		cbtable = PMAP_DIRECT_MAP(addr + i + cbh->header_bytes);
564 
565 		for (j = 0; j < cbh->table_bytes; j += cbe->size) {
566 			cbe = (struct cb_entry *)((char *)cbtable + j);
567 
568 			switch (cbe->tag) {
569 			case CB_TAG_FORWARD:
570 				return cb_find_fb(cbe->u.forward);
571 
572 			case CB_TAG_FRAMEBUFFER:
573 				return &cbe->u.fb;
574 			}
575 		}
576 	}
577 
578 	return NULL;
579 }
580 
581 psize_t
efifb_stolen(void)582 efifb_stolen(void)
583 {
584 	struct efifb *fb = &efifb_console;
585 	return fb->psize;
586 }
587 
588 vaddr_t
efifb_early_map(paddr_t pa)589 efifb_early_map(paddr_t pa)
590 {
591 	return pmap_set_pml4_early(pa);
592 }
593 
594 void
efifb_early_cleanup(void)595 efifb_early_cleanup(void)
596 {
597 	pmap_clear_pml4_early();
598 }
599