#include #include #include #include typedef struct Ureg Ureg; #include "pci.h" #include "vga.h" typedef struct Vbe Vbe; typedef struct Vmode Vmode; enum { MemSize = 1024*1024, PageSize = 4096, RealModeBuf = 0x9000, }; struct Vbe { int rmfd; /* /dev/realmode */ int memfd; /* /dev/realmem */ uchar *mem; /* copy of memory; 1MB */ uchar *isvalid; /* 1byte per 4kB in mem */ uchar *modebuf; }; struct Vmode { char size[Namelen+1]; char chan[Namelen+1]; int id; int attr; /* flags */ int bpl; int dx, dy; int depth; char *model; int directcolor; /* flags */ ulong paddr; }; #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 static Vbe *vbe; static int dspcon; /* connected displays bitmask */ static int dspact; /* active displays bitmask */ static int (*setscale)(Vbe*, char*); Vbe *mkvbe(void); int vbecheck(Vbe*); uchar *vbemodes(Vbe*); int vbemodeinfo(Vbe*, int, Vmode*); int vbegetmode(Vbe*); int vbesetmode(Vbe*, int); void vbeprintinfo(Vbe*); void vbeprintmodeinfo(Vbe*, int, char*); int vbesnarf(Vbe*, Vga*); void vesaddc(void); Edid* vbeddcedid(Vbe *vbe); uchar* vbesetup(Vbe*, Ureg*, int); int vbecall(Vbe*, Ureg*); int setdisplay(Vbe *vbe, int display); int getdisplay(Vbe*); void fixbios(Vbe*); int dbvesa(Vga* vga) { if(vbe == nil){ vbe = mkvbe(); if(vbe == nil){ fprint(2, "mkvbe: %r\n"); return 0; } } if(vbecheck(vbe) < 0){ fprint(2, "dbvesa: %r\n"); return 0; } vga->link = alloc(sizeof(Ctlr)); *vga->link = vesa; vga->vesa = vga->link; vga->ctlr = vga->link; vga->link->link = alloc(sizeof(Ctlr)); *vga->link->link = softhwgc; vga->hwgc = vga->link->link; return 1; } static char* cracksize(char *size, char **scale, int *display) { static char buf[256]; char *f[4]; int i, n; *scale = nil; *display = 0; snprint(buf, sizeof(buf), "%s", size); n = getfields(buf, f, nelem(f), 0, ","); for(i=1; i>= n, m[1] >>= n, m[2] >>= n, m[3] >>= n; snprint(tmp, sizeof tmp, "%c%d%s", "rgbx"[c], n, buf); strcpy(buf, tmp); goto Next; } } return buf; } Mode* dbvesamode(Vga *vga, char *size) { int i, width, display; int oldmode, olddisplay; uchar *p, *ep; Vmode vm; Mode *m; Modelist *l; char *scale; if(vbe == nil) return nil; size = cracksize(size, &scale, &display); oldmode = olddisplay = 0; if(display != 0){ olddisplay = getdisplay(vbe); oldmode = vbegetmode(vbe); if(setdisplay(vbe, display) < 0) return nil; } if(strncmp(size, "0x", 2) == 0){ if(vbemodeinfo(vbe, strtol(size+2, nil, 16), &vm) == 0) goto havemode; }else{ if(p = vbemodes(vbe)){ for(ep=p+1024; (p[0]!=0xFF || p[1]!=0xFF) && px = vm.dx; m->y = vm.dy; m->ht = m->x; m->shb = m->x; m->ehb = m->x; m->shs = m->x; m->ehs = m->x; m->vt = m->y; m->vrs = m->y; m->vre = m->y; m->frequency = m->ht * m->vt * 60; /* get default monitor timing */ for(i=0; vesamodes[i]; i++){ if(vesamodes[i]->x != vm.dx || vesamodes[i]->y != vm.dy) continue; *m = *vesamodes[i]; break; } for(i=0; iedid); i++){ if(vga->edid[i] == nil) continue; for(l = vga->edid[i]->modelist; l; l = l->next){ if(l->x != vm.dx || l->y != vm.dy) continue; *m = *((Mode*)l); break; } } strcpy(m->type, "vesa"); strcpy(m->size, vm.size); strcpy(m->chan, vm.chan); m->z = vm.depth; /* account for framebuffer stride */ width = vm.bpl * 8 / m->z; if(width > m->x) m->attr = mkattr(m->attr, "virtx", "%d", width); if(scale != nil) m->attr = mkattr(m->attr, "scale", "%s", scale); if(display != 0) m->attr = mkattr(m->attr, "display", "%d", display); m->attr = mkattr(m->attr, "id", "0x%x", vm.id); return m; } static void snarf(Vga* vga, Ctlr* ctlr) { if(vbe == nil) vbe = mkvbe(); if(vbe != nil) vga->vesa = ctlr; vbesnarf(vbe, vga); vga->linear = 1; ctlr->flag |= Hlinear|Ulinear|Fsnarf; } static void options(Vga *vga, Ctlr *ctlr) { char *v; if(v = dbattr(vga->mode->attr, "virtx")){ vga->virtx = atoi(v); vga->virty = vga->mode->y; } ctlr->flag |= Foptions; } static void load(Vga* vga, Ctlr* ctlr) { int mode, display; int oldmode, olddisplay; char *ds, *scale; if(vbe == nil) error("no vesa bios\n"); mode = strtol(dbattr(vga->mode->attr, "id"), nil, 0); scale = dbattr(vga->mode->attr, "scale"); ds = dbattr(vga->mode->attr, "display"); display = ds == nil ? 0 : atoi(ds); olddisplay = oldmode = 0; /* need to reset scaling before switching displays */ if(setscale != nil) (*setscale)(vbe, "scalefull"); if(display != 0){ olddisplay = getdisplay(vbe); oldmode = vbegetmode(vbe); if(setdisplay(vbe, display) < 0){ fprint(2, "setdisplay: %r\n"); ctlr->flag |= Ferror; return; } } if(vbesetmode(vbe, mode) < 0){ fprint(2, "vbesetmode: %r\n"); ctlr->flag |= Ferror; if(display != 0){ setdisplay(vbe, olddisplay); vbesetmode(vbe, oldmode); } return; } if(setscale != nil){ if((*setscale)(vbe, scale) < 0){ fprint(2, "setscale: %r\n"); } } } static void dump(Vga *, Ctlr*) { int i; char did[0x200]; uchar *p, *ep; if(vbe == nil){ Bprint(&stdout, "no vesa bios\n"); return; } memset(did, 0, sizeof did); vbeprintinfo(vbe); p = vbemodes(vbe); if(p != nil){ for(ep=p+1024; (p[0]!=0xFF || p[1]!=0xFF) && prmfd = open("/dev/realmode", ORDWR)) < 0) return nil; if((vbe->memfd = open("/dev/realmodemem", ORDWR)) < 0) return nil; vbe->mem = alloc(MemSize); vbe->isvalid = alloc(MemSize/PageSize); vbe->modebuf = alloc(PageSize); fixbios(vbe); return vbe; } static void loadpage(Vbe *vbe, int p, int wr) { if(p >= MemSize/PageSize) return; if(vbe->isvalid[p] == 0) if(pread(vbe->memfd, vbe->mem+p*PageSize, PageSize, p*PageSize) != PageSize) error("read /dev/realmodemem: %r\n"); vbe->isvalid[p] = 1+wr; } static void flushpage(Vbe *vbe, int p) { if(p >= MemSize/PageSize || vbe->isvalid[p]!=2) return; if(pwrite(vbe->memfd, vbe->mem+p*PageSize, PageSize, p*PageSize) != PageSize) error("write /dev/realmodemem: %r\n"); vbe->isvalid[p] = 1; } static void* getmem(Vbe *vbe, int off, int wr) { if(off == 0 || off >= MemSize) return nil; loadpage(vbe, off/PageSize, wr); if(off % PageSize) loadpage(vbe, (off/PageSize)+1, wr); return vbe->mem+off; } static void* unfarptr(Vbe *vbe, uchar *p) { int seg, off; seg = WORD(p+2); off = WORD(p); off += seg<<4; return getmem(vbe, off, 0); } uchar* vbesetup(Vbe *vbe, Ureg *u, int ax) { uchar *p; memset(u, 0, sizeof *u); u->ax = ax; u->es = (RealModeBuf>>4)&0xF000; u->di = RealModeBuf&0xFFFF; p = getmem(vbe, RealModeBuf, 1); memset(p, 0, PageSize); return p; } void vbeflush(Vbe *vbe) { int p; for(p=0; pisvalid, 0, MemSize/PageSize); } int vbecall(Vbe *vbe, Ureg *u) { int ax; ax = u->ax >> 8; u->trap = 0x10; vbeflush(vbe); if(pwrite(vbe->rmfd, u, sizeof *u, 0) != sizeof *u) error("write /dev/realmode: %r\n"); if(pread(vbe->rmfd, u, sizeof *u, 0) != sizeof *u) error("read /dev/realmode: %r\n"); getmem(vbe, RealModeBuf, 0); if((u->ax&0xFFFF) != ax){ werrstr("VBE error %#.4lux", u->ax&0xFFFF); return -1; } return 0; } int vbecheck(Vbe *vbe) { uchar *p; Ureg u; p = vbesetup(vbe, &u, 0x4F00); strcpy((char*)p, "VBE2"); if(vbecall(vbe, &u) < 0) return -1; if(memcmp(p, "VESA", 4)){ werrstr("invalid vesa signature %.4H\n", p); return -1; } if(p[5] < 2){ werrstr("invalid vesa version: %.4H\n", p+4); return -1; } return 0; } int vbesnarf(Vbe *vbe, Vga *vga) { char *oem; uchar *p; Ureg u; p = vbesetup(vbe, &u, 0x4F00); strcpy((char*)p, "VBE2"); if(vbecall(vbe, &u) < 0) return -1; if(memcmp(p, "VESA", 4) != 0 || p[5] < 2) return -1; vga->apz = WORD(p+18)*0x10000UL; oem = unfarptr(vbe, p+6); if(strncmp(oem, "Intel", 5) == 0){ setscale = intelscale; /* detect connected display devices */ vbesetup(vbe, &u, 0x5F64); u.bx = 0x200; if(vbecall(vbe, &u) < 0) u.cx = 0; dspcon = u.cx >> 8; /* CH = connected, CL = available? */ /* detect active display devices */ vbesetup(vbe, &u, 0x5F64); u.bx = 0x100; if(vbecall(vbe, &u) < 0) u.cx = 0; dspact = u.cx; } /* breaks modeset on 10de/0392 "G73 [GeForce 7600 GS]" -- cinap else if(memcmp(oem, "NVIDIA", 6) == 0) setscale = nvidiascale; */ vga->edid[0] = vbeddcedid(vbe); return 0; } void vbeprintinfo(Vbe *vbe) { uchar *p; Ureg u; int i; p = vbesetup(vbe, &u, 0x4F00); strcpy((char*)p, "VBE2"); if(vbecall(vbe, &u) < 0) return; printitem("vesa", "sig"); Bprint(&stdout, "%.4s %d.%d\n", (char*)p, p[5], p[4]); if(p[5] < 2) return; printitem("vesa", "oem"); Bprint(&stdout, "%s %d.%d\n", unfarptr(vbe, p+6), p[21], p[20]); printitem("vesa", "vendor"); Bprint(&stdout, "%s\n", unfarptr(vbe, p+22)); printitem("vesa", "product"); Bprint(&stdout, "%s\n", unfarptr(vbe, p+26)); printitem("vesa", "rev"); Bprint(&stdout, "%s\n", unfarptr(vbe, p+30)); printitem("vesa", "cap"); printflags(capabilityflag, p[10]); printitem("vesa", "mem"); Bprint(&stdout, "%lud\n", WORD(p+18)*0x10000UL); if(dspcon != 0){ printitem("vesa", "dsp con"); for(i = 0; i < 8; i++) if(dspcon & (1<modebuf, unfarptr(vbe, p+14), 1024); return vbe->modebuf; } int vbemodeinfo(Vbe *vbe, int id, Vmode *m) { uchar *p; Ureg u; int mod; p = vbesetup(vbe, &u, 0x4F01); u.cx = id; if(vbecall(vbe, &u) < 0) return -1; m->id = id; m->attr = WORD(p); if(!(m->attr & AttrSupported)) goto Unsupported; if(!(m->attr & AttrGraphics)) goto Unsupported; if(!(m->attr & AttrLinear)) goto Unsupported; m->bpl = WORD(p+16); m->dx = WORD(p+18); m->dy = WORD(p+20); m->depth = p[25]; if((m->dx * m->dy * m->depth) <= 0) goto Unsupported; mod = p[27]; switch(mod){ default: Unsupported: werrstr("mode unsupported"); return -1; case ModCGA: case ModHercules: case ModPacked: case ModDirect: m->model = modelstr[mod]; break; } m->directcolor = p[39]; m->paddr = LONG(p+40); snprint(m->size, sizeof m->size, "%dx%dx%d", m->dx, m->dy, m->depth); if(m->depth <= 8) snprint(m->chan, sizeof m->chan, "%c%d", (m->attr & AttrColor) ? 'm' : 'k', m->depth); else rgbmask2chan(m->chan, m->depth, (1UL<