plan9fox/sys/src/9/pc/vgavesa.c
cinap_lenrek 4f85115526 kernel: massive pci code rewrite
The new pci code is moved to port/pci.[hc] and shared by
all ports.

Each port has its own PCI controller implementation,
providing the pcicfgrw*() functions for low level pci
config space access. The locking for pcicfgrw*() is now
done by the caller (only port/pci.c).

Device drivers now need to include "../port/pci.h" in
addition to "io.h".

The new code now checks bridge windows and membars,
while enumerating the bus, giving the pc driver a chance
to re-assign them. This is needed because some UEFI
implementations fail to assign the bars for some devices,
so we need to do it outselfs. (See pcireservemem()).

While working on this, it was discovered that the pci
code assimed the smallest I/O bar size is 16 (pcibarsize()),
which is wrong. I/O bars can be as small as 4 bytes.
Bit 1 in an I/O bar is also reserved and should be masked off,
making the port mask: port = bar & ~3;
2020-09-13 20:33:17 +02:00

273 lines
4.9 KiB
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)
*/
if(up->kp)
procalarm(10000);
if(!waserror()){
vbecall(&u);
poperror();
}
if(up->kp){
procalarm(0);
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);
}