diff options
author | aiju <devnull@localhost> | 2017-06-17 19:47:04 +0000 |
---|---|---|
committer | aiju <devnull@localhost> | 2017-06-17 19:47:04 +0000 |
commit | cbcd9b1d718bb827432c944620fac09847c6abc0 (patch) | |
tree | ea9b4cc4fda34100b871662aa6401ef9c8855db1 /sys | |
parent | 31d14c316a73766057a02d57fd08d890a25d4783 (diff) |
vmx: VESA support and other misc I/O improvements
Diffstat (limited to 'sys')
-rw-r--r-- | sys/src/cmd/vmx/dat.h | 16 | ||||
-rw-r--r-- | sys/src/cmd/vmx/exith.c | 8 | ||||
-rw-r--r-- | sys/src/cmd/vmx/fns.h | 7 | ||||
-rw-r--r-- | sys/src/cmd/vmx/io.c | 145 | ||||
-rw-r--r-- | sys/src/cmd/vmx/ksetup.c | 5 | ||||
-rw-r--r-- | sys/src/cmd/vmx/mkfile | 1 | ||||
-rw-r--r-- | sys/src/cmd/vmx/pci.c | 29 | ||||
-rw-r--r-- | sys/src/cmd/vmx/vesa.c | 718 | ||||
-rw-r--r-- | sys/src/cmd/vmx/vga.c | 347 | ||||
-rw-r--r-- | sys/src/cmd/vmx/virtio.c | 9 | ||||
-rw-r--r-- | sys/src/cmd/vmx/vmx.c | 6 |
11 files changed, 1159 insertions, 132 deletions
diff --git a/sys/src/cmd/vmx/dat.h b/sys/src/cmd/vmx/dat.h index eedae1cda..1435150bb 100644 --- a/sys/src/cmd/vmx/dat.h +++ b/sys/src/cmd/vmx/dat.h @@ -44,6 +44,14 @@ struct PCIBar { void *aux; }; +enum { + /* type */ + BARIO = 1, + BARMEM32 = 0, + BARMEM64 = 4, + BARPREF = 8, +}; + struct PCIDev { u32int bdf, viddid, clrev, subid; u16int ctrl; @@ -67,3 +75,11 @@ enum { IRQLTOGGLE = -1, IRQLLOHI = -2, }; + +typedef struct VgaMode VgaMode; +struct VgaMode { + u16int no; + int w, h, hbytes, sz; + u32int chan; + VgaMode *next; +}; diff --git a/sys/src/cmd/vmx/exith.c b/sys/src/cmd/vmx/exith.c index d444190a1..19d827170 100644 --- a/sys/src/cmd/vmx/exith.c +++ b/sys/src/cmd/vmx/exith.c @@ -61,6 +61,8 @@ stepmmio(uvlong pa, uvlong *val, int size, ExitInfo *ei) extern u32int io(int, u16int, u32int, int); +u32int iodebug[32]; + static void iohandler(ExitInfo *ei) { @@ -88,7 +90,11 @@ iohandler(ExitInfo *ei) if(len == 1) ax = (u8int) ax; else if(len == 2) ax = (u16int) ax; io(0, port, ax, len); + SET(val); } + if(port < 0x400 && (iodebug[port >> 5] >> (port & 31) & 1) != 0) + if(isin) vmdebug("in %#.4ux <- %#ux", port, val); + else vmdebug("out %#.4ux <- %#ux", port, (int)ax); skipinstr(ei); } @@ -232,7 +238,7 @@ cpuid(ExitInfo *ei) ax = cp->ax; bx = cp->bx & 0xffff; cx = cp->cx & 0x60de2203; - dx = cp->dx & 0x0682a179; + dx = cp->dx & 0x0782a179; break; case 2: goto literal; /* cache stuff */ case 3: goto zero; /* processor serial number */ diff --git a/sys/src/cmd/vmx/fns.h b/sys/src/cmd/vmx/fns.h index 037d8c740..231ffcbc4 100644 --- a/sys/src/cmd/vmx/fns.h +++ b/sys/src/cmd/vmx/fns.h @@ -5,6 +5,8 @@ void rpoke(char *, uvlong, int); #define rset(a,b) rpoke(a,b,0) void processexit(char *); void pitadvance(void); +void rtcadvance(void); +void settimer(vlong targ); void vmerror(char *, ...); #define vmdebug vmerror int ctl(char *, ...); @@ -16,7 +18,7 @@ void vgaresize(void); void uartinit(int, char *); void sendnotif(void (*)(void *), void *); PCIDev *mkpcidev(u32int, u32int, u32int, int); -PCIBar *mkpcibar(PCIDev *, u8int, u32int, void *, void *); +PCIBar *mkpcibar(PCIDev *, u8int, u32int, u32int, void *, void *); PCICap *mkpcicap(PCIDev *, u8int, u32int (*)(PCICap *, u8int), void(*)(PCICap *, u8int, u32int, u32int)); u32int allocbdf(void); void *gptr(u64int, u64int); @@ -38,3 +40,6 @@ void i8042kick(void *); #define PUT16(p,n,v) (*(u16int*)((u8int*)(p)+(n)) = (v)) #define PUT32(p,n,v) (*(u32int*)((u8int*)(p)+(n)) = (v)) #define PUT64(p,n,v) (*(u64int*)((u8int*)(p)+(n)) = (v)) +u32int roundpow2(u32int); +u32int vgagetpal(u8int); +void vgasetpal(u8int, u32int); diff --git a/sys/src/cmd/vmx/io.c b/sys/src/cmd/vmx/io.c index 9d3274797..ad18e1af8 100644 --- a/sys/src/cmd/vmx/io.c +++ b/sys/src/cmd/vmx/io.c @@ -7,10 +7,60 @@ #include "dat.h" #include "fns.h" +static uchar cmos[0x30] = { + [1] 0xff, [3] 0xff, [5] 0xff, + [0xa] 0x26, + [0xb] 1<<1, + [0xd] 1<<7, /* cmos valid */ + [0xf] 0x56, /* cmos tests pass */ + [0x11] 0x80, /* mouse enabled */ + [0x14] 0x2e, /* cga 80-column */ + [0x2d] 0x1c, /* caches + fpu enabled */ +}; +static vlong rtcnext = -1; + static uchar -bcd(uchar c) +bcd(uchar m, uchar c) +{ + return (m & 1) != 0 ? c : c / 10 << 4 | c % 10; +} + +void +rtcadvance(void) { - return c / 10 << 4 | c % 10; + vlong t; + + if(rtcnext != -1){ + t = nsec(); + if(t >= rtcnext){ + cmos[0xc] |= 0x40; + rtcnext = -1; + }else + settimer(rtcnext); + } + irqline(8, (cmos[0xc] & cmos[0xb] & 0x70) != 0); +} + +static void +rtcset(void) +{ + vlong t, b; + int d; + + rtcadvance(); + if((cmos[0xa] >> 4) > 2 || (cmos[0xa] & 15) == 0 || (cmos[0xc] & 0x40) != 0){ + rtcnext = -1; + return; + } + switch(cmos[0xa]){ + case 0x21: d = 12; break; + case 0x22: d = 13; break; + default: d = 4 + (cmos[0xa] & 0xf); + } + b = (1000000000ULL << d) / 1048576; + t = nsec(); + rtcnext = t + b - t % b; + settimer(rtcnext); } static u32int @@ -18,16 +68,7 @@ rtcio(int isin, u16int port, u32int val, int sz, void *) { static u8int addr; uintptr basemem, extmem; - static uchar cmos[0x30] = { - [1] 0xff, [3] 0xff, [5] 0xff, - [0xa] 0x26, - [0xb] 0, - [0xd] 1<<7, /* cmos valid */ - [0xf] 0x56, /* cmos tests pass */ - [0x11] 0x80, /* mouse enabled */ - [0x14] 0x2e, /* cga 80-column */ - [0x2d] 0x1c, /* caches + fpu enabled */ - }; + static int cmosinit; int i, s; Tm *tm; @@ -48,24 +89,43 @@ rtcio(int isin, u16int port, u32int val, int sz, void *) cmos[0x2f] = s; cmosinit = 1; } + if(sz != 1) vmerror("rtc: access size %d != 1", sz); + val = (u8int) val; switch(isin << 16 | port){ - case 0x10070: return addr; case 0x70: addr = val; return 0; + case 0x71: + switch(addr){ + case 0xa: cmos[addr] = val & 0x7f; rtcset(); break; + case 0xb: cmos[addr] = val | 2; rtcadvance(); break; + case 0xc: case 0xd: goto no; + default: + if(addr < nelem(cmos)) + cmos[addr] = val; + else no: + vmerror("rtc: write to unknown address %#x (val=%#x)", addr, val); + return 0; + } + case 0x10070: return addr; case 0x10071: tm = gmtime(time(nil)); switch(addr){ - case 0x00: return bcd(tm->sec); - case 0x02: return bcd(tm->min); - case 0x04: return bcd(tm->hour); - case 0x06: return bcd(tm->wday + 1); - case 0x07: return bcd(tm->mday); - case 0x08: return bcd(tm->mon + 1); - case 0x09: return bcd(tm->year % 100); - case 0x32: return bcd(tm->year / 100 + 19); + case 0x00: return bcd(cmos[0xb], tm->sec); + case 0x02: return bcd(cmos[0xb], tm->min); + case 0x04: return bcd(cmos[0xb], tm->hour); + case 0x06: return bcd(cmos[0xb], tm->wday + 1); + case 0x07: return bcd(cmos[0xb], tm->mday); + case 0x08: return bcd(cmos[0xb], tm->mon + 1); + case 0x09: return bcd(cmos[0xb], tm->year % 100); + case 0x0c: + i = cmos[0xc] | ((cmos[0xc] & cmos[0xb] & 0x70) != 0) << 7; + cmos[0xc] = 0; + rtcset(); + return i; + case 0x32: return bcd(cmos[0xb], tm->year / 100 + 19); default: if(addr < nelem(cmos)) return cmos[addr]; - vmerror("rtc read from unknown address %#x", addr); + vmerror("rtc: read from unknown address %#x", addr); return 0; } } @@ -191,7 +251,10 @@ irqack(int n) p = &pic[1]; else return; - if(p == &pic[1]) irqack(pic[0].base + 2); + if(p == &pic[1]){ + irqack(pic[0].base + 2); + irqline(2, 0); + } n &= 7; p->irr &= ~(1<<n); p->isr |= 1<<n; @@ -307,6 +370,7 @@ picio(int isin, u16int port, u32int val, int sz, void *) } break; case 0x10020: + case 0x100a0: if((p->flags & READSR) != 0) return p->isr; if((p->flags & POLL) != 0){ @@ -322,7 +386,6 @@ picio(int isin, u16int port, u32int val, int sz, void *) return 0; } return p->irr; - case 0x100a0: case 0x10021: case 0x100a1: return p->imr; @@ -571,6 +634,7 @@ struct I8042 { .cmd -1, }; Channel *kbdch, *mousech; +u8int mouseactive; typedef struct PCKeyb PCKeyb; struct PCKeyb { u8int buf[64]; @@ -632,7 +696,7 @@ kbdcmd(u8int val) if(val == 0) keyputc(1); kbd.actcmd = 0; break; - case 0x3d: /* set leds */ + case 0xed: /* set leds */ keyputc(0xfa); kbd.actcmd = 0; break; @@ -684,8 +748,8 @@ mousepacket(int force) dx = mouse.xy.x; dy = -mouse.xy.y; b0 = 8; - if((ulong)(dx + 256) > 511) dx = dx >> 31 & 0x1ff ^ 0xff; - if((ulong)(dy + 256) > 511) dy = dy >> 31 & 0x1ff ^ 0xff; + if((ulong)(dx + 256) > 511) dx = dx >> 31 ^ 0xff; + if((ulong)(dy + 256) > 511) dy = dy >> 31 ^ 0xff; b0 |= dx >> 5 & 0x10 | dy >> 4 & 0x20; b0 |= (mouse.buttons * 0x111 & 0x421) % 7; mouseputc(b0); @@ -693,7 +757,7 @@ mousepacket(int force) mouseputc((u8int)dy); mouse.xy.x -= dx; mouse.xy.y += dy; - mouse.gotmouse = 0; + mouse.gotmouse = mouse.xy.x != 0 || mouse.xy.y != 0; } static void @@ -796,7 +860,7 @@ i8042io(int isin, u16int port, u32int val, int sz, void *) case 0x60: i8042.stat &= ~8; switch(i8042.cmd){ - case 0x60: i8042.cfg = val; break; + case 0x60: i8042.cfg = val; mouseactive = (val & 0x20) == 0; break; case 0xd1: i8042.oport = val; irqline(1, i8042.oport >> 4 & 1); @@ -819,8 +883,8 @@ i8042io(int isin, u16int port, u32int val, int sz, void *) switch(val){ case 0x20: i8042putbuf(0x400 | i8042.cfg); return 0; case 0xa1: i8042putbuf(0x4f1); return 0; /* no keyboard password */ - case 0xa7: i8042.cfg |= 1<<5; return 0; - case 0xa8: i8042.cfg &= ~(1<<5); return 0; + case 0xa7: i8042.cfg |= 1<<5; mouseactive = 0; return 0; + case 0xa8: i8042.cfg &= ~(1<<5); mouseactive = 1; return 0; case 0xa9: i8042putbuf(0x400); return 0; /* test second port */ case 0xaa: i8042putbuf(0x455); return 0; /* test controller */ case 0xab: i8042putbuf(0x400); return 0; /* test first port */ @@ -1111,9 +1175,9 @@ u32int iowhine(int isin, u16int port, u32int val, int sz, void *mod) { if(isin) - vmerror("%s%sread from unknown i/o port %#ux ignored (sz=%d)", mod != nil ? mod : "", mod != nil ? ": " : "", port, sz); + vmerror("%s%sread from unknown i/o port %#ux ignored (sz=%d, pc=%#ullx)", mod != nil ? mod : "", mod != nil ? ": " : "", port, sz, rget(RPC)); else - vmerror("%s%swrite to unknown i/o port %#ux ignored (val=%#ux, sz=%d)", mod != nil ? mod : "", mod != nil ? ": " : "", port, val, sz); + vmerror("%s%swrite to unknown i/o port %#ux ignored (val=%#ux, sz=%d, pc=%#ullx)", mod != nil ? mod : "", mod != nil ? ": " : "", port, val, sz, rget(RPC)); return -1; } @@ -1126,6 +1190,7 @@ struct IOHandler { u32int vgaio(int, u16int, u32int, int, void *); u32int pciio(int, u16int, u32int, int, void *); +u32int vesaio(int, u16int, u32int, int, void *); IOHandler handlers[] = { 0x20, 0x21, picio, nil, 0x40, 0x43, pitio, nil, @@ -1139,20 +1204,30 @@ IOHandler handlers[] = { 0x3f8, 0x3ff, uartio, nil, 0x4d0, 0x4d1, picio, nil, 0xcf8, 0xcff, pciio, nil, + 0xfee0, 0xfeef, vesaio, nil, 0x170, 0x177, ideio, nil, /* ide secondary */ + 0x376, 0x376, ideio, nil, /* ide secondary (aux) */ 0x1f0, 0x1f7, ideio, nil, /* ide primary */ + 0x3f6, 0x3f6, ideio, nil, /* ide primary (aux) */ 0x3f0, 0x3f7, fdcio, nil, /* floppy */ 0x061, 0x061, nopio, nil, /* pc speaker */ 0x084, 0x084, nopio, nil, /* dma -- used by openbsd for delay by dummy read */ 0x100, 0x110, nopio, nil, /* elnk3 */ + 0x240, 0x25f, nopio, nil, /* ne2000 */ 0x279, 0x279, nopio, nil, /* isa pnp */ - 0x280, 0x28f, nopio, nil, /* 8003 */ + 0x280, 0x29f, nopio, nil, /* ne2000 */ 0x2e8, 0x2ef, nopio, nil, /* COM4 */ + 0x300, 0x31f, nopio, nil, /* ne2000 */ + 0x320, 0x32f, nopio, nil, /* etherexpress */ + 0x330, 0x33f, nopio, nil, /* uha scsi */ + 0x340, 0x35f, nopio, nil, /* adaptec scsi */ + 0x360, 0x373, nopio, nil, /* isolan */ 0x378, 0x37a, nopio, nil, /* LPT1 */ - 0x3e0, 0x3e3, nopio, nil, /* cardbus */ + 0x3e0, 0x3e5, nopio, nil, /* cardbus or isa pci bridges */ 0x3e8, 0x3ef, nopio, nil, /* COM3 */ + 0x650, 0x65f, nopio, nil, /* 3c503 ethernet */ 0x778, 0x77a, nopio, nil, /* LPT1 (ECP) */ 0xa79, 0xa79, nopio, nil, /* isa pnp */ }; diff --git a/sys/src/cmd/vmx/ksetup.c b/sys/src/cmd/vmx/ksetup.c index 5c3009e8b..9b8b54baf 100644 --- a/sys/src/cmd/vmx/ksetup.c +++ b/sys/src/cmd/vmx/ksetup.c @@ -221,7 +221,7 @@ elff(uchar **p, uchar *e, int sz) if(sz == -1) sz = elf64 ? 8 : 4; if(*p + sz > e){ - print("out of bounds: %p > %p", *p + sz, e); + fprint(2, "out of bounds: %p > %p", *p + sz, e); return 0; } switch(sz){ @@ -558,11 +558,12 @@ obsdargs(void) uvlong s, e; obsdstart(BOOTARG_MEMMAP); + obsdpack("vvi", (uvlong)0, (uvlong)0xa0000, BIOS_MAP_FREE); for(r = mmap; r != nil; r = r->next){ s = r->start; e = r->end; if(s < (1<<20)) s = 1<<20; - if(e <= s) continue; + if(e <= s || r->type == REGFB) continue; obsdpack("vvi", s, e - s, isusermem(r) ? BIOS_MAP_FREE : BIOS_MAP_RES); } obsdpack("vvi", 0ULL, 0ULL, BIOS_MAP_END); diff --git a/sys/src/cmd/vmx/mkfile b/sys/src/cmd/vmx/mkfile index 66621c2d1..be1860dc2 100644 --- a/sys/src/cmd/vmx/mkfile +++ b/sys/src/cmd/vmx/mkfile @@ -11,5 +11,6 @@ OFILES=\ vga.$O \ pci.$O \ virtio.$O \ + vesa.$O \ </sys/src/cmd/mkone diff --git a/sys/src/cmd/vmx/pci.c b/sys/src/cmd/vmx/pci.c index 43928b742..547c2f29e 100644 --- a/sys/src/cmd/vmx/pci.c +++ b/sys/src/cmd/vmx/pci.c @@ -37,8 +37,20 @@ allocbdf(void) return BDF(0, dev++, 0); } +u32int +roundpow2(u32int l) +{ + l = -l; + l &= (int)l >> 16; + l &= (int)l >> 8; + l &= (int)l >> 4; + l &= (int)l >> 2; + l &= (int)l >> 1; + return -l; +} + PCIBar * -mkpcibar(PCIDev *d, u8int t, u32int l, void *fn, void *aux) +mkpcibar(PCIDev *d, u8int t, u32int a, u32int l, void *fn, void *aux) { PCIBar *b; @@ -46,16 +58,14 @@ mkpcibar(PCIDev *d, u8int t, u32int l, void *fn, void *aux) assert((t & 1) != 0 || (t & 6) == 0); if((t & 1) != 0 && l < 4) l = 4; if((t & 1) == 0 && l < 4096) l = 4096; - if((l & l-1) != 0){ - do - l &= l-1; - while((l & l-1) == 0); - l <<= 1; - assert(l != 0); - } + if((l & l-1) != 0) + l = roundpow2(l); for(b = d->bar; b < d->bar + nelem(d->bar); b++) if(b->length == 0) break; + if(b == d->bar + nelem(d->bar)) + sysfatal("pci bdf %6ux: too many bars", d->bdf); + b->addr = a; b->type = t; b->length = l; b->busnext = b; @@ -207,6 +217,7 @@ pciwrite(PCIDev *d, int addr, u32int val, u32int mask) d->bar[n].addr = (d->bar[n].addr & ~mask | val & mask) & ~(d->bar[n].length - 1); updatebar(&d->bar[n]); return; + case 0x30: return; case 0x3c: d->irqno = (d->irqno & ~mask | val & mask) & 0xff; pciirqupdate(); return; } c = findpcicap(d, addr); @@ -275,7 +286,7 @@ pcibusmap(void) for(d = pcidevs; d != nil; d = d->next){ d->ctrl |= 3; for(b = d->bar; b < d->bar + nelem(d->bar); b++){ - if(b->length == 0) + if(b->length == 0 || b->addr != 0) continue; if((b->type & 1) == 0){ vmerror("pci device %.6ux: memory bars unsupported", d->bdf); diff --git a/sys/src/cmd/vmx/vesa.c b/sys/src/cmd/vmx/vesa.c new file mode 100644 index 000000000..96d8ef83f --- /dev/null +++ b/sys/src/cmd/vmx/vesa.c @@ -0,0 +1,718 @@ +#include <u.h> +#include <libc.h> +#include <thread.h> +#include <draw.h> +#include "dat.h" +#include "fns.h" + +static uchar vesabios[512] = { +0x55, 0xaa, 0x01, 0xcb, 0x60, 0x1e, 0x06, 0x8c, 0xd0, 0xba, 0xe0, 0xfe, 0xef, 0x89, 0xe0, 0xba, 0xe1, 0xfe, 0xef, 0xba, 0xe2, 0xfe, 0xed, 0x85, 0xc0, 0x7c, 0x28, 0x50, 0x25, 0x00, +0xf0, 0x8e, 0xc0, 0xba, 0xe3, 0xfe, 0xed, 0x89, 0xc6, 0xc3, 0x26, 0x8b, 0x04, 0xba, 0xe4, 0xfe, 0xef, 0xeb, 0xe2, 0xba, 0xe4, 0xfe, 0xed, 0x26, 0x89, 0x04, 0xeb, 0xd9, 0xba, 0xe4, +0xfe, 0xed, 0x26, 0x88, 0x04, 0xeb, 0xd0, 0x58, 0x58, 0x61, 0xcf, +}; +enum { + READCMD = 0x28, + WRITEWCMD = 0x31, + WRITEBCMD = 0x3a, +}; +typedef struct VESAIO VESAIO; +struct VESAIO { + u8int port; + u16int val; +}; +Channel *vesawchan, *vesarchan; +typedef struct Ureg16 Ureg16; +struct Ureg16 { + u16int ax, bx, cx, dx; + u16int si, di, bp; + u16int ds, es; +}; +#define ESDI(u) ((u).di + ((u).es<<4)) +typedef struct Vesa Vesa; +struct Vesa { + u32int romptr; + u32int oemstring, oemvendor, oemproduct, oemproductrev; + u32int modetab; + u8int pal8; +} vesa; +#define FARPTR(x) (((x)&0xf0000)<<12|(u16int)(x)) +extern VgaMode *modes, **modeslast, *curmode, *nextmode, textmode; +extern int curhbytes, nexthbytes; +extern uintptr fbaddr, fbsz; +extern int maxw, maxh; +enum { CMAP4 = CHAN1(CMap, 4) }; + + +static VgaMode * +findmode(u16int m) +{ + VgaMode *p; + + for(p = modes; p != nil; p = p->next) + if(p->no == m) + return p; + return nil; +} + +static int +vesagetsp(void) +{ + VESAIO io; + u32int rc; + +loop: + while(recv(vesarchan, &io), io.port != 0) + sendul(vesawchan, -1); + rc = io.val << 4; + sendul(vesawchan, -1); + recv(vesarchan, &io); + sendul(vesawchan, -1); + if(io.port != 1) goto loop; + return rc + io.val; +} + +static int +vesawrite(int addr, u32int val, int sz) +{ + VESAIO io; + + assert(sz == 1 || sz == 2 || sz == 4); + recv(vesarchan, &io); + if(io.port != 0x12){ + no: sendul(vesawchan, -1); + return -1; + } + sendul(vesawchan, (sz > 1 ? WRITEWCMD : WRITEBCMD) | addr >> 4 & 0xf000); + recv(vesarchan, &io); + if(io.port != 0x13) goto no; + sendul(vesawchan, addr); + recv(vesarchan, &io); + if(io.port != 0x14) goto no; + sendul(vesawchan, val); + if(sz == 4) + return vesawrite(addr + 2, val >> 16, 2); + return 0; +} + +static int +vesaread(int addr) +{ + VESAIO io; + + recv(vesarchan, &io); + if(io.port != 0x12){ + no: sendul(vesawchan, -1); + return -1; + } + sendul(vesawchan, READCMD); + recv(vesarchan, &io); + if(io.port != 0x13) goto no; + sendul(vesawchan, addr); + recv(vesarchan, &io); + if(io.port != 0x4) goto no; + sendul(vesawchan, -1); + return io.val; +} + +static int +vesaregs(u32int sp, Ureg16 *ur) +{ + int rc; + #define R(n, a) rc = vesaread(sp + n); if(rc < 0) return -1; ur->a = rc + + memset(ur, 0, sizeof(*ur)); + R(0, es); + R(2, ds); + R(4, di); + R(6, si); + R(8, bp); + R(12, bx); + R(14, dx); + R(16, cx); + R(18, ax); + return 0; + #undef R +} + +static int +vesasetregs(u32int sp, Ureg16 *ur) +{ + #define R(n, a) if(vesawrite(sp + n, ur->a, 2) < 0) return -1; + + R(0, es); + R(2, ds); + R(4, di); + R(6, si); + R(8, bp); + R(12, bx); + R(14, dx); + R(16, cx); + R(18, ax); + return 0; + #undef R +} + +#define vesasetax(sp, val) vesawrite(sp+18, val, 2) +#define vesasetbx(sp, val) vesawrite(sp+12, val, 2) + +static int +vesapack(int addr, char *fmt, ...) +{ + va_list va; + static u8int checksum; + int v; + char *p; + int numb; + + if(addr < 0) return -1; + numb = 0; + va_start(va, fmt); + for(; *fmt != 0; fmt++) + switch(*fmt){ + case ' ': break; + case 'b': v = va_arg(va, int); if(vesawrite(addr, v, 1) < 0) return -1; addr += 1; checksum += v; break; + case 'w': v = va_arg(va, int); if(vesawrite(addr, v, 2) < 0) return -1; addr += 2; checksum += v + (v >> 8); break; + case 'd': v = va_arg(va, int); if(vesawrite(addr, v, 4) < 0) return -1; addr += 4; checksum += v + (v >> 8) + (v >> 16) + (v >> 24); break; + case 's': + p = va_arg(va, char *); + for(; *p != 0; p++, addr++){ + if(vesawrite(addr, *p, 1) < 0) + return -1; + checksum += *p; + } + break; + case 'S': + p = va_arg(va, char *); + v = va_arg(va, int); + while(v-- > 0){ + if(vesawrite(addr++, *p, 1) < 0) + return -1; + checksum += *p++; + } + break; + case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': + numb = strtol(fmt, &fmt, 10); + fmt--; + break; + case 'f': + for(; numb >= 2; numb -= 2, addr += 2) + if(vesawrite(addr, 0, 2) < 0) return -1; + if(numb == 1){ + if(vesawrite(addr++, 0, 1) < 0) return -1; + numb = 0; + } + break; + case 'c': if(vesawrite(addr, -checksum, 1) < 0) return -1; addr += 1; break; + case 'C': checksum = 0; break; + default: vmerror("vesapack: unknown char %c", *fmt); return -1; + } + va_end(va); + return addr; +} + +static int +vesarompack(char *fmt, ...) +{ + va_list va; + uchar *v, *v0; + int rc, x; + void *s; + + v0 = gptr(vesa.romptr, 0x8000); + v = v0; + assert(v != nil); + va_start(va, fmt); + for(; *fmt != 0; fmt++) + switch(*fmt){ + case 'b': *(u8int*)v = va_arg(va, int); v++; break; + case 'w': *(u16int*)v = va_arg(va, int); v += 2; break; + case 'd': *(u32int*)v = va_arg(va, int); v += 4; break; + case 'a': v = (uchar*)strecpy((char*)v, (char*)v + 0x8000, va_arg(va, char*)); *v++ = 0; break; + case 'S': s = va_arg(va, void *); x = va_arg(va, int); memcpy(v, s, x); v += x; break; + default: sysfatal("vesarompack: unknown char %c", *fmt); + } + va_end(va); + rc = vesa.romptr; + vesa.romptr += v - v0; + return rc; +} + +static int +vesamodeget(int addr, u16int mode, u16int *retv) +{ + VgaMode *p; + u8int model; + u8int nred, pred, ngreen, pgreen, nblue, pblue, nx, px; + int i, pos, s; + + p = findmode(mode); + if(p == nil){ + vmerror("vesa: Return VBE Mode Information: unknown mode %#x", mode); + *retv = 0x014F; + return 0; + } + *retv = 0x004F; + model = 6; + nred = pred = ngreen = pgreen = 0; + nblue = pblue = nx = px = 0; + pos = 0; + for(i = 0; i < 4; i++){ + s = p->chan >> 8 * i & 15; + switch(p->chan >> 8 * i + 4 & 15){ + case CRed: nred = s; pred = pos; break; + case CGreen: ngreen = s; pgreen = pos; break; + case CBlue: nblue = s; pblue = pos; break; + case CAlpha: case CIgnore: nx = s; px = pos; break; + case CMap: model = 4; break; + } + pos += s; + } + return vesapack(addr, "wbbwwwwdw wwbbbbbbbbb bbbbbbbbb ddw wbbbbb bbbbbd 189f", + 1<<0|1<<1|1<<3|1<<4|1<<5|1<<6|1<<7, /* attributes: color graphics, vga incompatible, linear framebuffer */ + 0, 0, 0, 0, 0, 0, 0, p->hbytes, /* windowing crap */ + p->w, p->h, + 0, 0, /* character size */ + 1, chantodepth(p->chan), 1, /* 1 bank, 1 plane, N bpp */ + model, /* memory model */ + 0, 0, 1, /* no banking/paging */ + nred, pred, ngreen, pgreen, nblue, pblue, nx, px, /* masks */ + 0, /* no ramp, reserved bits are reserved */ + fbaddr, + 0, 0, /* reserved */ + p->hbytes, 0, 0, + nred, pred, ngreen, pgreen, nblue, pblue, nx, px, /* masks */ + p->w * p->h * 60 * 2 /* max pixelclock */); +} + +static int +vesamodeset(u16int mode, u16int *retv) +{ + VgaMode *p; + + mode &= 0x1ff; + if(mode == 3){ + *retv = 0x04F; + nextmode = &textmode; + return 0; + } + p = findmode(mode); + if(p == nil){ + vmerror("vesa: Set VBE Mode: unknown mode %#x", mode); + *retv = 0x14F; + return 0; + } + *retv = 0x04F; + nextmode = p; + nexthbytes = p->hbytes; + vesa.pal8 = 0; + return 0; +} + +static int +vesalinelen(Ureg16 *ur) +{ + int d; + int nhb, x; + + d = chantodepth(nextmode->chan); + if(nextmode == &textmode || d == 0) goto fail; + switch(ur->bx & 0xff){ + case 0: + nhb = d * ur->cx / 8; + set: + if((d & 7) != 0){ + vmerror("vesa: set logical length unsupported on bitdepth < 8"); + fail: ur->ax = 0x014F; + return 0; + } + if(nhb * nextmode->h > fbsz){ + ur->ax = 0x024F; + return 0; + } + nexthbytes = nhb; + ur->ax = 0x4F; + ur->bx = nhb; + ur->cx = nhb * 8 / d; + ur->dx = fbsz / nhb; + break; + case 1: + ur->ax = 0x4F; + ur->bx = nexthbytes; + ur->cx = nexthbytes * 8 / d; + ur->dx = fbsz / nexthbytes; + break; + case 2: + nhb = ur->cx; + goto set; + case 3: + x = fbsz / nextmode->h; + ur->ax = 0x4F; + ur->bx = x; + ur->cx = x * 8 / d; + ur->dx = nextmode->h; + break; + default: + vmerror("vesa: unsupported subfunction %#x of SetGetLogicalLineLength", ur->bx & 0xff); + } + return 0; +} + +static int +vesapalformat(Ureg16 *ur) +{ + switch(ur->bx & 0xff){ + case 0: + vesa.pal8 = (ur->bx >> 8) >= 8; + case 1: + ur->ax = 0x4F; + ur->bx = vesa.pal8 ? 8 : 6; + break; + default: + vmerror("vesa: unsupported subfunction %#x of SetGetDACPaletteFormat", ur->bx & 0xff); + } + return 0; +} + +static int +vesapal(Ureg16 *ur) +{ + int i; + int addr; + u32int c; + int r, g, b; + + switch(ur->bx & 0xff){ + case 0: + case 0x80: + if(ur->dx >= 256 || ur->cx > 256){ + ur->ax = 0x014F; + return 0; + } + ur->ax = 0x4F; + addr = ESDI(*ur); + for(i = ur->dx; i < ur->dx + ur->cx; i++){ + b = vesaread(addr); + if(b < 0) return -1; + g = b >> 8; + b = b & 0xff; + r = vesaread(addr + 2); + if(r < 0) return -1; + r &= 0xff; + if(!vesa.pal8){ + r <<= 2; + g <<= 2; + b <<= 2; + } + vgasetpal(i, r << 24 | g << 16 | b << 8 | 0xff); + addr += 4; + } + break; + case 0x1: + if(ur->dx >= 256 || ur->cx > 256){ + ur->ax = 0x014F; + return 0; + } + ur->ax = 0x4F; + addr = ESDI(*ur); + for(i = ur->dx; i < ur->dx + ur->cx; i++){ + c = vgagetpal(i); + r = c >> 24; g = c >> 16; b = c >> 8; + if(!vesa.pal8){ + r >>= 2; + g >>= 2; + b >>= 2; + } + addr = vesapack(addr, "bbbb", b, g, r, 0); + } + return addr >= 0 ? 0 : -1; + default: + vmerror("vesa: unsupported subfunction %#x of SetGetPaletteData", ur->bx & 0xff); + } + return 0; +} + +static void +stdtiming(u8int *arr) +{ + VgaMode *m; + u8int s1, s2; + int i; + + memset(arr, 1, 16); + for(m = modes; m != nil; m = m->next){ + if(m->w < 256 || m->w > 2288 || m->w > maxw || m->h > maxh) continue; + if((m->w & 7) != 0) continue; + if(m->w == 640 && m->h == 480) continue; + if(m->w == 800 && m->h == 600) continue; + if(m->w == 1024 && m->h == 768) continue; + if(m->w == 1280 && m->h == 1024) continue; + if(m->w * 10 == m->w * 16) + s2 = 0x00; + else if(m->w * 3 == m->h * 4) + s2 = 0x40; + else if(m->w * 4 == m->h * 5) + s2 = 0x80; + else if(m->w * 9 == m->w * 16) + s2 = 0xc0; + else + continue; + s1 = m->w / 8 - 31; + for(i = 0; i < 16; i += 2){ + if(arr[i] == s1 && arr[i+1] == s2) + goto skip; + if(arr[i] == 1 && arr[i+1] == 1) + break; + } + if(i == 16) skip: continue; + arr[i] = s1; + arr[i+1] = s2; + } +} + +static int +detailtiming(int addr, VgaMode **m) +{ + u32int hv, hfp, hsp, hbp, hmm; + u32int vv, vfp, vsp, vbp, vmm; + u32int freq; + +again: + if(*m == nil) + return vesapack(addr, "d14f", 0x10000000); + /* when in doubt, make shit up */ + hv = (*m)->w; + hfp = hv / 40; + hsp = hv * 3 / 20; + hbp = hfp * 4 + hsp; + vv = (*m)->h; + vfp = (vv + 24) / 48; + vsp = (vv + 120) / 240; + vbp = vfp * 3 + vsp; + freq = (hv + hfp + hsp + hbp) * (vv + vfp + vsp + vbp) * 60; + hmm = hv * 254 / 960; /* assume 96 dpi */ + vmm = vv * 254 / 960; + + *m = (*m)->next; + if(hv > maxw || vv > maxh || hv > 0xfff || hfp > 0x3ff || hsp > 0x3ff || vfp > 63 || vsp > 63) + goto again; + return vesapack(addr, "w bbbbbbbbbb bbbbb b", + freq / 10000, + hv, hbp, hv >> 4 & 0xf0 | hbp >> 8 & 0xf, + vv, vbp, vv >> 4 & 0xf0 | vbp >> 8 & 0xf, + hfp, hsp, + vfp << 4 & 0xf0 | vsp & 0xf, + hfp >> 2 & 0xc0 | hsp >> 4 & 0x30 | vfp >> 2 & 0x0c | vsp >> 4 & 0x03, + hmm, vmm, + hmm >> 4 & 0xf0 | vmm >> 8 & 0xf, + 0, 0, 0x1f); + +} + +static int +vesaddc(Ureg16 *ur) +{ + int addr; + u32int tim; + u8int std[16]; + VgaMode *m; + + switch(ur->bx & 0xff){ + case 0: + ur->ax = 0x004F; + ur->bx = 3; + break; + case 1: + addr = ESDI(*ur); + stdtiming(std); + switch(ur->dx){ + case 0: + ur->ax = 0x004F; + tim = 0; + if(maxw >= 640 && maxh >= 480) tim |= 0x3c; + if(maxw >= 800 && maxh >= 600) tim |= 0xc003; + if(maxw >= 1024 && maxh >= 768) tim |= 0xe00; + if(maxw >= 1280 && maxh >= 1024) tim |= 0x100; + addr = vesapack(addr, "Cdd bbbbdbb bb bbbbb bbbbb bbbbb wb S", + 0xFFFFFF00, + 0x00FFFFFF, + 0x0c, 0x34, 0xa7, 0xac, /* manufacturer & product id */ + 1701, /* serial no */ + 0xff, 27, /* model year 2017 */ + 1, 4, /* edid version */ + 0xa1, /* DVI input */ + maxw >= maxh ? maxw * 100 / maxh - 99 : 0, /* landscape aspect ratio */ + maxw < maxh ? maxh * 100 / maxw - 99 : 0, /* portrait aspect ratio */ + 0x78, /* gamma=2.2 */ + 7, /* srgb & have preferred timing mode */ + 0, 0, 0, 0, 0, /* chromaticity coordinates */ + 0, 0, 0, 0, 0, + tim, tim >> 16, + std, 16); + m = modes; + addr = detailtiming(addr, &m); + addr = detailtiming(addr, &m); + addr = detailtiming(addr, &m); + addr = vesapack(addr, "3fbb bbbb bbbb bbbbb", + 0xfd, 0xa, 1, 0xff, 1, 0xff, 0xff, 0x04, 0x11, + maxw >> 11 & 3, maxw >> 3, 0xf8, 0x18, 0, 60); + addr = vesapack(addr, "bc", 0); + if(addr < 0) return -1; + break; + default: + ur->ax = 0x014F; + } + break; + default: + vmerror("vesa: unsupported subfunction %#x of VBE/DDC", ur->bx & 0xff); + } + return 0; +} + +static void +vesathread(void *) +{ + u32int sp; + Ureg16 ur; + + for(;;){ + sp = vesagetsp(); +// vmdebug("SP = %.8ux", sp); + if(vesaregs(sp, &ur) < 0) continue; +// vmdebug("AX = %.4x BX=%.4x CX=%.4x DX=%.4x", ur.ax, ur.bx, ur.cx, ur.dx); +// vmdebug("SI = %.4x DI=%.4x BP=%.4x", ur.si, ur.di, ur.bp); +// vmdebug("DS = %.4x ES=%.4x", ur.ds, ur.es); + switch(ur.ax){ + case 0x4f00: + if(vesapack(ESDI(ur), "swdddwwddd", + "VESA", /* VbeSignature */ + 0x0300, /* VbeVersion */ + FARPTR(vesa.oemstring), /* OemStringPtr */ + 1, /* Capabilities (support 8 bit colors) */ + FARPTR(vesa.modetab), /* VideoModePtr */ + fbsz >> 16, /* TotalMemory */ + 0x100 /* OemSoftwareRev */, + FARPTR(vesa.oemvendor), /* OemVendorNamePtr */ + FARPTR(vesa.oemproduct), /* OemProductNamePtr */ + FARPTR(vesa.oemproductrev) /* OemProductRevPtr */) < 0) + continue; + if(vesasetax(sp, 0x004F) < 0) continue; + break; + case 0x4f01: + if(vesamodeget(ESDI(ur), ur.cx, &ur.ax) < 0) continue; + if(vesasetax(sp, ur.ax) < 0) continue; + break; + case 0x4f02: + if(vesamodeset(ur.bx, &ur.ax) < 0 || vesasetax(sp, ur.ax) < 0) continue; + break; + case 0x4f03: + if(vesasetax(sp, 0x004F) < 0) continue; + if(vesasetbx(sp, nextmode->no | 0x4000) < 0) continue; + break; + case 0x4f06: + if(vesalinelen(&ur) < 0 || vesasetregs(sp, &ur) < 0) continue; + break; + case 0x4f08: + if(vesapalformat(&ur) < 0 || vesasetregs(sp, &ur) < 0) continue; + break; + case 0x4f09: + if(vesapal(&ur) < 0 || vesasetregs(sp, &ur) < 0) continue; + break; + case 0x4f15: + if(vesaddc(&ur) < 0 || vesasetregs(sp, &ur) < 0) continue; + break; + default: + vmerror("vesa: unsupported function %#x", ur.ax); + } + } +} + +static void +addmode(int no, int x, int y, u32int chan) +{ + VgaMode *vm; + + vm = emalloc(sizeof(VgaMode)); + vm->no = no; + vm->w = x; + vm->h = y; + vm->chan = chan; + vm->hbytes = chantodepth(chan) * vm->w + 7 >> 3; + vm->sz = vm->hbytes * y; + *modeslast = vm; + modeslast = &vm->next; +} + +void +vesainit(void) +{ + void *v; + int i, c; + int mno; + VgaMode *p; + + v = gptr(0xc0000, sizeof(vesabios)); + if(v == nil) sysfatal("vesainit: gptr failed"); + for(i = 0, c = 0; i < sizeof(vesabios) - 1; i++) + c += vesabios[i]; + vesabios[sizeof(vesabios) - 1] = -c; + vesa.romptr = 0xc0000; + vesarompack("S", vesabios, sizeof(vesabios)); + + v = gptr(0, 1024); + if(v == nil) sysfatal("vesainit: gptr failed"); + PUT32(v, 0x40, 0xc0000004); + + vesa.oemstring = vesarompack("a", "9front vmx(1)"); + vesa.oemvendor = vesarompack("a", "9front"); + vesa.oemproduct = vesarompack("a", "cat graphics adapter"); + vesa.oemproductrev = vesarompack("a", "∞"); + + vesa.modetab = vesa.romptr; + mno = 0x120; + for(p = modes; p != nil; p = p->next){ + p->no = mno++; + vesarompack("w", p->no); + } + vesarompack("w", 0xffff); + + addmode(0x100, 640, 400, CMAP8); + addmode(0x101, 640, 480, CMAP8); + addmode(0x102, 800, 600, CMAP4); + addmode(0x6A, 800, 600, CMAP4); + addmode(0x103, 800, 600, CMAP8); + addmode(0x104, 1024, 768, CMAP4); + addmode(0x105, 1024, 768, CMAP8); + addmode(0x106, 1280, 1024, CMAP4); + addmode(0x107, 1280, 1024, CMAP8); + addmode(0x10D, 320, 200, RGB15); + addmode(0x10E, 320, 200, RGB16); + addmode(0x10F, 320, 200, RGB24); + addmode(0x110, 640, 480, RGB15); + addmode(0x111, 640, 480, RGB16); + addmode(0x112, 640, 480, RGB24); + addmode(0x113, 800, 600, RGB15); + addmode(0x114, 800, 600, RGB16); + addmode(0x115, 800, 600, RGB24); + addmode(0x116, 1024, 768, RGB15); + addmode(0x117, 1024, 768, RGB16); + addmode(0x118, 1024, 768, RGB24); + addmode(0x119, 1280, 1024, RGB15); + addmode(0x11A, 1280, 1024, RGB16); + addmode(0x11B, 1280, 1024, RGB24); + + vesarchan = chancreate(sizeof(VESAIO), 0); + vesawchan = chancreate(sizeof(ulong), 0); + threadcreate(vesathread, nil, 8192); +} + +u32int +vesaio(int isin, u16int port, u32int val, int sz, void *) +{ + VESAIO io; + + if(sz != 2) return iowhine(isin, port, val, sz, nil); + io.port = isin << 4 | port & 0xf; + io.val = val; + send(vesarchan, &io); + return recvul(vesawchan); +} diff --git a/sys/src/cmd/vmx/vga.c b/sys/src/cmd/vmx/vga.c index 1729eebd4..8f11c4344 100644 --- a/sys/src/cmd/vmx/vga.c +++ b/sys/src/cmd/vmx/vga.c @@ -6,38 +6,48 @@ #include <cursor.h> #include <mouse.h> #include <keyboard.h> +#include <ctype.h> #include "dat.h" #include "fns.h" -static uchar *fb; +static uchar *fb, *tfb; uintptr fbsz; uintptr fbaddr; -int textmode; -static ulong screenchan; -static int picw, pich, hbytes; +VgaMode *curmode, *nextmode, *modes, **modeslast = &modes; +int curhbytes, nexthbytes; +int vesamode, maxw, maxh; + +VgaMode textmode = { + .w 640, .h 400, .no 3 +}; + static Image *img, *bg; static Mousectl *mc; static Rectangle picr; Channel *kbdch, *mousech; u8int mousegrab; +extern u8int mouseactive; static uchar *sfb; typedef struct VGA VGA; struct VGA { u8int miscout; - u8int cidx; - u8int aidx; /* bit 7: access flipflop */ + u8int cidx; /* crtc */ + u8int aidx; /* attribute (bit 7: access flipflop) */ + u8int gidx; /* graphics */ + u8int sidx; /* sequencer */ u16int rdidx, wdidx; /* bit 0-1: color */ u8int attr[32]; u32int pal[256]; Image *col[256]; Image *acol[16]; u8int crtc[0x18]; + QLock; } vga = { .miscout 1, .attr { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, - .crtc { [11] 15 }, + .crtc { [10] 13, [11] 14 }, .pal { 0x000000ff, 0x0000a8ff, 0x00a800ff, 0x00a8a8ff, 0xa80000ff, 0xa800a8ff, 0xa85400ff, 0xa8a8a8ff, 0x545454ff, 0x5454fcff, 0x54fc54ff, 0x54fcfcff, 0xfc5454ff, 0xfc54fcff, 0xfcfc54ff, 0xfcfcfcff, @@ -79,10 +89,12 @@ newpal(int l, int n, int dofree) { int x; + assert(l >= 0 && n + l <= 256); for(; n-- > 0; l++){ if(dofree) freeimage(vga.col[l]); vga.col[l] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, vga.pal[l]); + if(vga.col[l] == nil) sysfatal("allocimage: %r"); } for(l = 0; l < 16; l++){ x = vga.attr[0x14] << 4 & 0xc0 | vga.attr[l] & 0x3f; @@ -92,17 +104,47 @@ newpal(int l, int n, int dofree) } } +u32int +vgagetpal(u8int n) +{ + return vga.pal[n]; +} + +void +vgasetpal(u8int n, u32int v) +{ + qlock(&vga); + vga.pal[n] = v; + newpal(n, 1, 1); + qunlock(&vga); +} + static void -screeninit(void) +screeninit(int resize) { Point p; + int ch; + if(!resize) + freeimage(img); + else{ + bg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF); + newpal(0, 256, 0); + } p = divpt(addpt(screen->r.min, screen->r.max), 2); - picr = (Rectangle){subpt(p, Pt(picw/2, pich/2)), addpt(p, Pt((picw+1)/2, (pich+1)/2))}; - bg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF); - img = allocimage(display, Rect(0, 0, picw, pich), screenchan == 0 ? screen->chan : screenchan, 0, 0); + picr = (Rectangle){subpt(p, Pt(curmode->w/2, curmode->h/2)), addpt(p, Pt((curmode->w+1)/2, (curmode->h+1)/2))}; + switch(curmode->chan){ + case 0: ch = screen->chan; break; + case CHAN1(CMap, 4): case CMAP8: + if(vesamode){ + ch = RGBA32; + break; + } + /* wet floor */ + default: ch = curmode->chan; break; + } + img = allocimage(display, Rect(0, 0, curmode->w, curmode->h), ch, 0, 0); draw(screen, screen->r, bg, nil, ZP); - newpal(0, 256, 0); } u32int @@ -117,19 +159,24 @@ vgaio(int isin, u16int port, u32int val, int sz, void *) if((vga.aidx & 0x80) != 0){ vmdebug("vga: attribute write %#.2x = %#.2x", vga.aidx & 0x1f, val); vga.attr[vga.aidx & 0x1f] = val; - newpal(0, 0, 0); + qlock(&vga); newpal(0, 0, 0); qunlock(&vga); }else vga.aidx = val & 0x3f; vga.aidx ^= 0x80; return 0; case 0x3c2: vga.miscout = val; return 0; + case 0x3c4: vga.sidx = val; return 0; + case 0x3c5: vmerror("vga: write to unknown sequencer register %#ux (val=%#ux)", vga.sidx, val); return 0; + case 0x3c6: return 0; case 0x3c7: vga.rdidx = val << 2; return 0; case 0x3c8: vga.wdidx = val << 2; return 0; case 0x3c9: vga.pal[vga.wdidx >> 2] = vga.pal[vga.wdidx >> 2] & ~(0xff << (~vga.wdidx << 3 & 24)) | val << 2 + (~vga.wdidx << 3 & 24); - newpal(vga.wdidx >> 2, 1, 1); + qlock(&vga); newpal(vga.wdidx >> 2, 1, 1); qunlock(&vga); vga.wdidx = vga.wdidx + 1 + (vga.wdidx >> 1 & 1) & 0x3ff; return 0; + case 0x3ce: vga.gidx = val; return 0; + case 0x3cf: vmerror("vga: write to unknown graphics register %#ux (val=%#ux)", vga.gidx, val); return 0; case 0x3d4: vga.cidx = val; return 0; case 0x3d5: switch(vga.cidx){ @@ -137,11 +184,14 @@ vgaio(int isin, u16int port, u32int val, int sz, void *) vga.crtc[vga.cidx] = val; return 0; default: - vmerror("write to unknown VGA register, 3d5/%#ux (val=%#ux)", vga.cidx, val); + vmerror("vga: write to unknown CRTC register %#ux (val=%#ux)", vga.cidx, val); } return 0; case 0x103c0: return vga.aidx & 0x3f; case 0x103c1: return vga.attr[vga.aidx & 0x1f]; + case 0x103c4: return vga.sidx; + case 0x103c5: vmerror("vga: read from unknown sequencer register %#ux (val=%#ux)", vga.sidx, val); return 0; + case 0x103c6: return 0xff; case 0x103c7: return vga.rdidx >> 2; case 0x103c8: return vga.wdidx >> 2; case 0x103c9: @@ -149,18 +199,21 @@ vgaio(int isin, u16int port, u32int val, int sz, void *) vga.rdidx = vga.rdidx + 1 + (vga.rdidx >> 1 & 1) & 0x3ff; return m; case 0x103cc: return vga.miscout; + case 0x103ce: return vga.gidx; + case 0x103cf: vmerror("vga: read from unknown graphics register %#ux", vga.gidx); return 0; case 0x103d4: return vga.cidx; case 0x103d5: switch(vga.cidx){ case 10: case 11: case 12: case 13: case 14: case 15: return vga.crtc[vga.cidx]; default: - vmerror("read from unknown VGA register, 3d5/%#ux", vga.cidx); + vmerror("vga: read from unknown CRTC register %#ux", vga.cidx); return 0; } + case 0x103ca: case 0x103da: - vga.aidx &= ~0x7f; - return 0; + vga.aidx &= 0x7f; + return 0; } return iowhine(isin, port, val, sz, "vga"); } @@ -242,6 +295,8 @@ kbdlayout(char *fn) Bterm(bp); } +static vlong kbwatchdog; /* used to release mouse grabbing if keyproc is stuck */ + void keyproc(void *) { @@ -264,9 +319,11 @@ keyproc(void *) memmove(buf, buf+n, sizeof(buf)-n); } if(buf[0] == 0){ + kbwatchdog = 0; n = read(fd, buf, sizeof(buf)-1); if(n <= 0) sysfatal("read /dev/kbd: %r"); + kbwatchdog = nsec(); buf[n-1] = 0; buf[n] = 0; } @@ -332,13 +389,15 @@ mousethread(void *) break; } if(!mousegrab){ - if(clicked && (m.buttons & 1) == 0 && !textmode){ + if(clicked && (m.buttons & 1) == 0 && mouseactive){ mousegrab = 1; setcursor(mc, &blank); } clicked = m.buttons & 1; break; } + if(kbwatchdog != 0 && nsec() - kbwatchdog > 1000ULL*1000*1000) + mousegrab = 0; gotm = 1; if(!ptinrect(m.xy, grabout)){ moveto(mc, mid); @@ -391,10 +450,10 @@ drawtext(void) sa = vga.crtc[12] << 8 | vga.crtc[13]; if(sa + 80*25 >= 0x10000){ memset(rbuf, 0, sizeof(rbuf)); - memmove(rbuf, fb + sa * 2, 0x10000 - 80*25 - sa); + memmove(rbuf, tfb + sa * 2, 0x10000 - 80*25 - sa); p = rbuf; }else - p = fb + sa * 2; + p = tfb + sa * 2; for(y = 0; y < 25; y++){ for(x = 0; x < 80; x++) buf[x] = cp437[p[2*x]]; @@ -409,9 +468,9 @@ drawtext(void) p += 160; } cp = (vga.crtc[14] << 8 | vga.crtc[15]); - if(cp >= sa && cp < sa + 80*25 && (vga.crtc[10] & 0x20) == 0){ - buf[0] = cp437[fb[cp*2]]; - attr = fb[cp*2+1]; + if(cp >= sa && cp < sa + 80*25 && (vga.crtc[10] & 0x20) == 0 && nsec() / 500000000 % 2 == 0){ + buf[0] = cp437[tfb[cp*2]]; + attr = tfb[cp*2+1]; r.min = Pt((cp - sa) % 80 * 8, (cp - sa) / 80 * 16); r.max = Pt(r.min.x + 8, r.min.y + (vga.crtc[11] & 0x1f) + 1); r.min.y += vga.crtc[10] & 0x1f; @@ -422,39 +481,70 @@ drawtext(void) } static void -drawfb(void) +drawfb(int redraw) { u32int *p, *q; Rectangle upd; - int xb, y; + int xb, y, hb; + u32int v; + uchar *cp; + u32int *buf, *bp; p = (u32int *) fb; q = (u32int *) sfb; upd.min.y = upd.max.y = -1; xb = 0; y = 0; - while(p < (u32int*)(fb + fbsz)){ - if(*p != *q){ + hb = curhbytes; + while(p < (u32int*)(fb + curmode->sz)){ + if(*p != *q || redraw){ if(upd.min.y < 0) upd.min.y = y; - upd.max.y = y + 1 + (xb + 4 > hbytes); + upd.max.y = y + 1 + (xb + 4 > hb); *q = *p; } p++; q++; xb += 4; - if(xb >= hbytes){ - xb -= hbytes; + if(xb >= hb){ + xb -= hb; y++; } } if(upd.min.y == upd.max.y) return; upd.min.x = 0; - upd.max.x = picw; - if(screenchan != screen->chan){ - loadimage(img, upd, sfb + upd.min.y * hbytes, (upd.max.y - upd.min.y) * hbytes); + upd.max.x = curmode->w; + if(vesamode && (curmode->chan >> 4 == CMap)){ + buf = emalloc(curmode->w * 4 * (upd.max.y - upd.min.y)); + bp = buf; + for(y = upd.min.y; y < upd.max.y; y++){ + cp = sfb + y * hb; + for(xb = 0; xb < curmode->w; xb++){ + if(curmode->chan == CMAP8) + v = *cp++; + else if((xb & 1) == 0) + v = *cp & 0xf; + else + v = *cp++ >> 4; + *bp++ = vga.pal[v]; + } + } + loadimage(img, upd, (void *) buf, curmode->w * 4 * (upd.max.y - upd.min.y)); + free(buf); draw(screen, rectaddpt(upd, picr.min), img, nil, upd.min); - }else - loadimage(screen, rectaddpt(upd, picr.min), sfb + upd.min.y * hbytes, (upd.max.y - upd.min.y) * hbytes); + }else if(curmode->chan != screen->chan || !rectinrect(picr, screen->r)){ + if(curmode->hbytes != hb){ + for(y = upd.min.y; y < upd.max.y; y++) + loadimage(img, Rect(0, y, curmode->w, y+1), sfb + y * hb, curmode->hbytes); + }else + loadimage(img, upd, sfb + upd.min.y * hb, (upd.max.y - upd.min.y) * hb); + draw(screen, rectaddpt(upd, picr.min), img, nil, upd.min); + }else{ + if(curmode->hbytes != hb){ + for(y = upd.min.y; y < upd.max.y; y++) + loadimage(screen, Rect(picr.min.x, picr.min.y + y, picr.max.x, picr.min.y + y + 1), sfb + y * hb, curmode->hbytes); + }else + loadimage(screen, rectaddpt(upd, picr.min), sfb + upd.min.y * hb, (upd.max.y - upd.min.y) * hb); + } flushimage(display, 1); } @@ -462,78 +552,172 @@ void drawproc(void *) { ulong ul; + int event; + VgaMode *m; threadsetname("draw"); sfb = emalloc(fbsz); for(;; sleep(20)){ - while(nbrecv(mc->resizec, &ul) > 0){ - if(getwindow(display, Refnone) < 0) + qlock(&vga); + event = 0; + m = nextmode; + if(m != curmode){ + event |= 1; + curmode = m; + curhbytes = m->hbytes; + } + if(nexthbytes != curhbytes){ + event |= 1; + curhbytes = nexthbytes; + } + while(nbrecv(mc->resizec, &ul) > 0) + event |= 2; + if(event != 0){ + if((event & 2) != 0 && getwindow(display, Refnone) < 0) sysfatal("resize failed: %r"); - screeninit(); + screeninit((event & 2) != 0); } - if(textmode) + if(curmode == &textmode) drawtext(); else - drawfb(); + drawfb(event != 0); + qunlock(&vga); + } +} + +static int +chancheck(u32int ch) +{ + u8int got; + int i, t; + + got = 0; + for(i = 0; i < 4; i++){ + t = ch >> 8 * i + 4 & 15; + if((ch >> 8 * i & 15) == 0) continue; + if(t >= NChan) return 0; + if((got & 1<<t) != 0) return 0; + got |= 1<<t; + } + if(!vesamode) return 1; + switch(got){ + case 1<<CRed|1<<CGreen|1<<CBlue: + case 1<<CRed|1<<CGreen|1<<CBlue|1<<CAlpha: + case 1<<CRed|1<<CGreen|1<<CBlue|1<<CIgnore: + return 1; + case 1<<CMap: + return chantodepth(ch) == 4 || chantodepth(ch) == 8; + default: + return 0; + } +} + +static char * +vgamodeparse(char *p, VgaMode **mp) +{ + char *r; + VgaMode *m; + char c; + + m = emalloc(sizeof(VgaMode)); + *mp = m; + *modeslast = m; + modeslast = &m->next; + m->w = strtoul(p, &r, 10); + if(*r != 'x') + nope: + sysfatal("invalid mode specifier"); + p = r + 1; + m->h = strtoul(p, &r, 10); + if(*r != 'x'){ + m->chan = XRGB32; + goto out; } + p = r + 1; + while(isalnum(*r)) + r++; + c = *r; + *r = 0; + m->chan = strtochan(p); + *r = c; + if(m->chan == 0 || !chancheck(m->chan)) + goto nope; +out: + if(m->w > maxw) maxw = m->w; + if(m->h > maxh) maxh = m->h; + return r; } void vgafbparse(char *fbstring) { - char buf[512]; char *p, *q; - uvlong addr; + VgaMode *m; - if(picw != 0) sysfatal("vga specified twice"); if(strcmp(fbstring, "text") == 0){ - picw = 640; - pich = 400; - fbsz = 80*25*2; - fbaddr = 0xb8000; - textmode++; - screenchan = 0; + curmode = &textmode; + return; + }else if(strncmp(fbstring, "vesa:", 5) == 0){ + vesamode = 1; + p = fbstring + 5; + }else + p = fbstring; + do{ + q = vgamodeparse(p, &m); + if(p == q || m->w <= 0 || m->h <= 0) + no: sysfatal("invalid mode specifier"); + m->hbytes = chantodepth(m->chan) * m->w + 7 >> 3; + m->sz = m->hbytes * m->h; + if(m->sz > fbsz) fbsz = m->sz; + p = q; + }while(*p++ == ','); + if(*--p == '@'){ + p++; + fbaddr = strtoul(p, &q, 0); + if(p == q) goto no; + p = q; + }else + fbaddr = 0xf0000000; + if(*p != 0) goto no; + if(modes == nil || vesamode == 0 && modes->next != nil) + goto no; + if(vesamode == 0){ + curmode = modes; + curhbytes = curmode->hbytes; }else{ - strecpy(buf, buf + nelem(buf), fbstring); - picw = strtol(buf, &p, 10); - if(*p != 'x') - nope: - sysfatal("vgafbparse: invalid framebuffer specifier: %#q (should be WxHxCHAN@ADDR or 'text')", fbstring); - pich = strtol(p+1, &p, 10); - if(*p != 'x') goto nope; - q = strchr(p+1, '@'); - if(q == nil) goto nope; - *q = 0; - screenchan = strtochan(p+1); - if(screenchan == 0) goto nope; - p = q + 1; - if(*p == 0) goto nope; - addr = strtoull(p, &p, 0); - fbaddr = addr; - if(fbaddr != addr) goto nope; - if(*p != 0) goto nope; - hbytes = chantodepth(screenchan) * picw + 7 >> 3; - fbsz = hbytes * pich; + curmode = &textmode; + if(fbsz < (1<<22)) + fbsz = 1<<22; + else + fbsz = roundpow2(fbsz); } } + void vgainit(void) { char buf[512]; int i; + PCIDev *d; + extern void vesainit(void); - if(picw == 0) return; - fb = gptr(fbaddr, fbsz); - if(fb == nil) - sysfatal("got nil ptr for framebuffer"); - if(textmode) - for(i = 0; i < 0x8000; i += 2) - PUT16(fb, i, 0x0700); - snprint(buf, sizeof(buf), "-dx %d -dy %d", picw+50, pich+50); + if(curmode == nil) return; + nextmode = curmode; + tfb = gptr(0xb8000, 0x8000); + if(tfb == nil) + sysfatal("got nil ptr for text framebuffer"); + for(i = 0; i < 0x8000; i += 2) + PUT16(tfb, i, 0x0720); + if(fbsz != 0){ + fb = gptr(fbaddr, fbsz); + if(fb == nil) + sysfatal("got nil ptr for framebuffer"); + } + snprint(buf, sizeof(buf), "-dx %d -dy %d", maxw+50, maxh+50); newwindow(buf); initdraw(nil, nil, "vmx"); - screeninit(); + screeninit(1); flushimage(display, 1); kbdlayout("/sys/lib/kbmap/us"); mc = initmouse(nil, screen); @@ -542,4 +726,9 @@ vgainit(void) proccreate(mousethread, nil, 4096); proccreate(keyproc, nil, 4096); proccreate(drawproc, nil, 4096); + if(vesamode){ + d = mkpcidev(allocbdf(), 0x06660666, 0x03000000, 0); + mkpcibar(d, BARMEM32 | BARPREF, fbaddr, fbsz, nil, nil); + vesainit(); + } } diff --git a/sys/src/cmd/vmx/virtio.c b/sys/src/cmd/vmx/virtio.c index eba5313ce..345c94b3c 100644 --- a/sys/src/cmd/vmx/virtio.c +++ b/sys/src/cmd/vmx/virtio.c @@ -365,7 +365,7 @@ mkviodev(u16int devid, u32int pciclass, u32int subid) d = emalloc(sizeof(VIODev)); d->pci = mkpcidev(allocbdf(), devid << 16 | 0x1AF4, pciclass << 8, 1); d->pci->subid = subid << 16; - mkpcibar(d->pci, 1, 256, vioio, d); + mkpcibar(d->pci, BARIO, 0, 256, vioio, d); return d; } @@ -501,9 +501,14 @@ vionetwproc(void *vp) continue; } if(len < 14){ - vmerror("virtio net: ignoring short packet (length=%d)", len); + /* openbsd ends up sending lots of zero length packets sometimes */ + if(len != 0) + vmerror("virtio net: ignoring short packet (length=%d)", len); vioputbuf(vb); continue; + }else if(len < 60){ /* openbsd doesn't seem to know about ethernet minimum packet lengths either */ + memset(txbuf + len, 0, 60 - len); + len = 60; } rc = write(v->net.writefd, txbuf, len); vioputbuf(vb); diff --git a/sys/src/cmd/vmx/vmx.c b/sys/src/cmd/vmx/vmx.c index a4674bb40..6671d7946 100644 --- a/sys/src/cmd/vmx/vmx.c +++ b/sys/src/cmd/vmx/vmx.c @@ -331,7 +331,7 @@ launch(void) s = rcflush(1); if(ctl("go %s", s == nil ? "" : s) < 0) - sysfatal("go: %r"); + sysfatal("go %s: %r", s == nil ? "" : s); getexit++; } @@ -414,6 +414,7 @@ runloop(void) break; case SLEEP: pitadvance(); + rtcadvance(); break; case NOTIF: notif.f(notif.arg); @@ -486,7 +487,6 @@ threadmain(int argc, char **argv) static int edevn; static uvlong gmemsz = 64*1024*1024; extern uintptr fbsz, fbaddr; - extern int textmode; int i; quotefmtinstall(); @@ -534,7 +534,7 @@ threadmain(int argc, char **argv) cmdlinev = argv + 1; mkregion(0, gmemsz, REGMEM); - if(fbsz != 0 && textmode == 0){ + if(fbsz != 0){ if(fbaddr + fbsz < fbaddr) sysfatal("invalid fb address"); if(fbaddr + fbsz < gmemsz) sysfatal("framebuffer overlaps with physical memory"); mkregion(fbaddr, fbsz, REGFB); |