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