ref: e81c54ba2ecc673a4d5f8aed0e9b52841fe07b0d
dir: /os/pc/vgavesa.c/
/* * vga driver using just vesa bios to set up. */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "../port/pci.h" #include "../port/error.h" #define Ureg Ureg386 #include "/386/include/ureg.h" typedef struct Ureg386 Ureg386; #define Image IMAGE #include <draw.h> #include <memdraw.h> #include <cursor.h> #include "screen.h" enum { RealModeBuf = 0x9000, }; static uchar modebuf[0x1000]; static Chan *creg, *cmem; #define WORD(p) ((p)[0] | ((p)[1]<<8)) #define LONG(p) ((p)[0] | ((p)[1]<<8) | ((p)[2]<<16) | ((p)[3]<<24)) #define PWORD(p, v) (p)[0] = (v); (p)[1] = (v)>>8 #define PLONG(p, v) (p)[0] = (v); (p)[1] = (v)>>8; (p)[2] = (v)>>16; (p)[3] = (v)>>24 typedef struct Vmode Vmode; struct Vmode { char chan[32]; int attr; /* flags */ int bpl; int dx, dy; int depth; ulong paddr; }; static uchar* vbesetup(Ureg386 *u, int ax) { memset(modebuf, 0, sizeof modebuf); memset(u, 0, sizeof *u); u->ax = ax; u->es = (RealModeBuf>>4)&0xF000; u->di = RealModeBuf&0xFFFF; return modebuf; } static void vbecall(Ureg386 *u) { if(devtab[cmem->type]->write(cmem, modebuf, sizeof(modebuf), RealModeBuf) != sizeof(modebuf)) error("write modebuf"); u->trap = 0x10; if(devtab[creg->type]->write(creg, u, sizeof(*u), 0) != sizeof(*u)) error("write ureg"); if(devtab[creg->type]->read(creg, u, sizeof(*u), 0) != sizeof(*u)) error("read ureg"); if((u->ax&0xFFFF) != 0x004F) error("vesa bios error"); if(devtab[cmem->type]->read(cmem, modebuf, sizeof(modebuf), RealModeBuf) != sizeof(modebuf)) error("read modebuf"); } static void vbecheck(void) { Ureg386 u; uchar *p; p = vbesetup(&u, 0x4F00); strcpy((char*)p, "VBE2"); vbecall(&u); if(memcmp((char*)p, "VESA", 4) != 0) error("bad vesa signature"); if(p[5] < 2) error("bad vesa version"); } static int vbegetmode(void) { Ureg386 u; vbesetup(&u, 0x4F03); vbecall(&u); return u.bx; } static uchar* vbemodeinfo(int mode) { uchar *p; Ureg386 u; p = vbesetup(&u, 0x4F01); u.cx = mode; vbecall(&u); return p; } static char* vmode(Vmode *m, uchar *p) { m->attr = WORD(p); if(!(m->attr & (1<<4))) return "not in VESA graphics mode"; if(!(m->attr & (1<<7))) return "not in linear graphics mode"; m->bpl = WORD(p+16); m->dx = WORD(p+18); m->dy = WORD(p+20); m->depth = p[25]; m->paddr = LONG(p+40); if(m->depth <= 8) snprint(m->chan, sizeof m->chan, "%c%d", (m->attr & (1<<3)) ? 'm' : 'k', m->depth); else rgbmask2chan(m->chan, m->depth, (1UL<<p[31])-1 << p[32], (1UL<<p[33])-1 << p[34], (1UL<<p[35])-1 << p[36]); return nil; } static void vesalinear(VGAscr *scr, int, int) { int i, mode, size, havesize; Pcidev *pci; char *err; Vmode m; vbecheck(); mode = vbegetmode(); /* * bochs loses the top bits - cannot use this if((mode&(1<<14)) == 0) error("not in linear graphics mode"); */ mode &= 0x3FFF; if((err = vmode(&m, vbemodeinfo(mode))) != nil) error(err); size = m.dy * m.bpl; /* * figure out max size of memory so that we have * enough if the screen is resized. */ pci = nil; havesize = 0; while(!havesize && (pci = pcimatch(pci, 0, 0)) != nil){ if(pci->ccrb != Pcibcdisp) continue; for(i=0; i<nelem(pci->mem); i++){ uvlong a, e; if(pci->mem[i].size == 0 || (pci->mem[i].bar & 1) != 0) continue; a = pci->mem[i].bar & ~0xF; e = a + pci->mem[i].size; if(m.paddr >= a && (m.paddr+size) <= e){ size = e - m.paddr; havesize = 1; break; } } } /* no pci - heuristic guess */ if(!havesize) if(size < 4*1024*1024) size = 4*1024*1024; else size = ROUND(size, 1024*1024); vgalinearaddr(scr, m.paddr, size); if(scr->apsize) addvgaseg("vesascreen", scr->paddr, scr->apsize); scr->softscreen = 1; } static void vesaenable(VGAscr *) { cmem = namec("/dev/realmodemem", Aopen, ORDWR, 0); if(waserror()){ cclose(cmem); cmem = nil; nexterror(); } creg = namec("/dev/realmode", Aopen, ORDWR, 0); poperror(); } static void vesadisable(VGAscr *) { if(cmem != nil) cclose(cmem); if(creg != nil) cclose(creg); cmem = creg = nil; } static void vesablank(VGAscr *, int blank) { Ureg386 u; vbesetup(&u, 0x4f10); u.bx = blank ? 0x0101 : 0x0001; /* * dont wait forever when called from mouse kproc. * some BIOS get stuck in i/o poll loop after * blank/unblank for some reason. (Thinkpad A22p) */ /* TODO if(up->kp) procalarm(10000); */ if(!waserror()){ vbecall(&u); poperror(); } if(up->kp){ /* procalarm(0); */ /* TODO up->notepending = 0; */ } } static void vesadrawinit(VGAscr *scr) { scr->blank = vesablank; } VGAdev vgavesadev = { "vesa", vesaenable, vesadisable, 0, vesalinear, vesadrawinit, }; /* * called from multibootargs() to convert * vbe mode info (passed from bootloader) * to *bootscreen= parameter */ char* vesabootscreenconf(char *s, char *e, uchar *p) { Vmode m; if(vmode(&m, p) != nil) return s; return seprint(s, e, "*bootscreen=%dx%dx%d %s %#lux\n", m.bpl * 8 / m.depth, m.dy, m.depth, m.chan, m.paddr); }