From mboxrd@z Thu Jan 1 00:00:00 1970 To: 9fans@cse.psu.edu From: andrey mirtchovski MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="upas-hmsvcwmdhqvczrpmxnahpkocoj" Message-Id: <20020228002424.49B1219AE0@mail.cse.psu.edu> Subject: [9fans] devpcidev.c Date: Wed, 27 Feb 2002 17:25:32 -0700 Topicbox-Message-UUID: 5a8d4b0e-eaca-11e9-9e20-41e7f4b1d025 This is a multi-part message in MIME format. --upas-hmsvcwmdhqvczrpmxnahpkocoj Content-Disposition: inline Content-Type: text/plain; charset="US-ASCII" Content-Transfer-Encoding: 7bit Hi, This little program allows me to read/write to random memory addresses from userland. Russ Cox indicated that he has something similar, but since i wanted to learn how to write /dev/ stuff i decided to give it a try myself... I find it helpful when debugging vga (for example) from serial console. Maybe some of you will find it helpful for other things :) Something to note: it is unsafe to use peek and poke at the hardware from userland. There are so many ways to render your machine inoperable that I'm not going to bother listing them here. (Normally a reboot fixes things, but...) Don't take it out on me or on LANL if your hardware chokes as a consequence of pcidev. Do not attempt to remove bread stuck in a plugged toaster with a knife either. *shrug*... You know all this, bot nevertheless you have been warned. Pcidev (sorry, couldn't think of a better name) sits between you and the hardware devices on your computer. It allows the user to read/write (standard inb, inw, inl, outb, outw, outl) using simple shell scripts. Pcidev could be found as a drive named #Z. When bound to a directory it presents itself as a sigle file named 'base'. This file takes as input a memory address, to which the user wants to write or read. Having been given an address, pcidev creates a directory named after the it and populates it with 6 files used for reading/writing, namely: inb -> read a byte inw -> read a word (16 bits) inl -> read a long outb -> write to the port (byte) outw -> short outl -> long That's pretty much it.. It's really simple. Here's a sample session with it: ---- % bind -a '#Z' /tmp/z % cd /tmp/z % ls base % echo 0x3cc > base # address should be a valid atol()-understandable string, 0xF, 0F, 15 -> all accepted % ls 0x3cc # 0x3cc is VGA's Miscellaneous Output Register base % cd 0x3cc % ls inb inl inw outb outl outw % cat inb 0xe3% # no \n appended... % cat inl 0xff0800e3% % cd .. % echo 0x80 > base % rm 0x3cc % ls 0x80 # a pci post-card. write-only base % cd 0x80 % echo 0xf > outb # 0F shows on the device, % echo 14 > outb # 0D shows on the device ---- That's about it. The maximum addresses opened at a single time is 128. Just rm some stuff to open up space (rm base will not work, of course). To compile a kernel with pcidev: cd /sys/src/9/pc cp devpcidev.c . edit pcdisk and add 'pcidev' at the bottom of the 'dev' list mk 'CONF=9pcdisk' (or your favorite way ofdoing it) 9fat:; cp 9pcdisk /n/9fat reboot... Sorry, there is no man page for it, but there are no plans to put it into the official distribution either :) While discussing the 'design' with Ron Minnich another way of doing it came up -- have a single file for input, another one for output and manage the addresses with seek(). That way a 5-line C program could read and report the entire status of a device. I'm posting this implementation because it's a bit more complex (dealing with subdirectories, adding/deleting addresses) and not because it's necessarily the better way to present files for inb and outb. regards: andrey ps: as usual, flame me privately if you don't like it or if it doesn't work.. --upas-hmsvcwmdhqvczrpmxnahpkocoj Content-Disposition: attachment; filename=devpcidev.c Content-Type: text/plain; charset="US-ASCII" Content-Transfer-Encoding: 7bit #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "../port/error.h" static char Etoomany[] = "too many ports opened"; static char Enoclient[] = "no such pci device..."; static void *pcidevbase = 0; enum { Qtopdir = 0, Qbase, Qpcidir, Qinb, /* reading */ Qinw, Qinl, Qoutb, /* writing */ Qoutw, Qoutl, }; typedef struct Mypci Mypci; typedef struct Devpci Devpci; struct Mypci { QLock; ulong base; }; enum { Maxpcidev = 128, /* looks like a good number */ }; struct Devpci { Mypci *pci[Maxpcidev]; uint npci; }; static Devpci devpci; static Dirtab pcidevdir[] = { "inb", {Qinb}, 0, 0440, "inw", {Qinw}, 0, 0440, "inl", {Qinl}, 0, 0440, "outb", {Qoutb}, 0, 0220, "outw", {Qoutw}, 0, 0220, "outl", {Qoutl}, 0, 0220, }; static Dirtab pcitopdir[] = { "base", {Qbase}, 0, 0220, }; #define QSHIFT 4 /* location in qid of client # */ #define QID(q) (((q).path&0x0000000F)>>0) #define CLIENTPATH(q) ((q&0x07FFFFFF0)>>QSHIFT) #define CLIENT(q) CLIENTPATH((q).path) Mypci* pcislotpath(ulong path) { Mypci *cl; int slot; slot = CLIENTPATH(path); if(slot == 0) return nil; cl = devpci.pci[slot-1]; if(cl==0 || cl->base==0) return nil; return cl; } Mypci* pcislot(Chan *c) { Mypci *client; client = pcislotpath(c->qid.path); if(client == nil) error(Enoclient); return client; } static int pcidevgen(Chan *c, Dirtab *tab, int x, int s, Dir *dp) { int t; Qid q; ulong path; Mypci *cl; char buf[NAMELEN]; USED(tab, x); q.vers = 0; if(s == DEVDOTDOT){ switch(QID(c->qid)){ case Qpcidir: cl = pcislot(c); sprint(buf, "0x%lux", cl->base); devdir(c, (Qid){CHDIR|Qtopdir, 0}, buf, 0, eve, 0500, dp); break; default: panic("pcidevwalk %lux", c->qid.path); } return 1; } t = QID(c->qid); if(t == Qtopdir){ if(s == 0){ q = (Qid){Qbase, 0}; devdir(c, q, "base", 0, eve, 0600, dp); } else if(s <= devpci.npci){ cl = devpci.pci[s-1]; if(cl == 0) return 0; sprint(buf, "0x%lux", cl->base); q = (Qid){CHDIR|(s<qid.path&~(CHDIR|((1<qid.vers; switch(s){ case 0: q = (Qid){path|Qinb, c->qid.vers}; devdir(c, q, "inb", 0, eve, 0200, dp); break; case 1: q = (Qid){path|Qinw, c->qid.vers}; devdir(c, q, "inw", 0, eve, 0200, dp); break; case 2: q = (Qid){path|Qinl, c->qid.vers}; devdir(c, q, "inl", 0, eve, 0200, dp); break; case 3: q = (Qid){path|Qoutb, c->qid.vers}; devdir(c, q, "outb", 0, eve, 0400, dp); break; case 4: q = (Qid){path|Qoutw, c->qid.vers}; devdir(c, q, "outw", 0, eve, 0400, dp); break; case 5: q = (Qid){path|Qoutl, c->qid.vers}; devdir(c, q, "outl", 0, eve, 0400, dp); break; default: return -1; } return 1; } static void pcidevreset(void) { } void pcidevinit(void) { devinit(); } static Chan* pcidevattach(char* spec) { return devattach('Z', spec); } int pcidevwalk(Chan* c, char* name) { return devwalk(c, name, 0,0 , pcidevgen); } static void pcidevstat(Chan* c, char* dp) { devstat(c, dp, pcitopdir, nelem(pcitopdir), devgen); } static Chan* pcidevopen(Chan* c, int omode) { return devopen(c, omode, pcitopdir, nelem(pcitopdir), devgen); } static long pcidevread(Chan* c, void* a, long n, vlong) { char str[16]; int size = 0; ulong o; Mypci *cl; if(c->qid.path & CHDIR) return devdirread(c, a, n, 0, 0, pcidevgen); cl = pcislot(c); qlock(cl); if(waserror()){ qunlock(cl); nexterror(); } /* assume some things about 'a' that we probably shouldn't */ switch(QID(c->qid)){ case Qinb: size = sprint(str, "0x%2.2ux", inb(cl->base) & 0xFF); break; case Qinw: size = sprint(str, "0x%4.4ux", ins(cl->base) & 0xFFFF); break; case Qinl: size = sprint(str, "0x%8.8lux", inl(cl->base) & 0xFFFFFFFF); break; } qunlock(cl); poperror(); o = c->offset; if(o >= size) return 0; if(o+n > size) n = size-c->offset; memmove(a, str+o, n); return n; } static long pcidevwrite(Chan* c, void* a, long n, vlong off) { Mypci *cl; ulong offset = off; USED(offset, n); if(c->qid.path & CHDIR) error(Eisdir); if(QID(c->qid) == Qbase) { if(devpci.npci < Maxpcidev) { devpci.pci[devpci.npci] = (Mypci *)malloc(sizeof(Mypci)); devpci.pci[devpci.npci]->base = strtol(a, nil, 0); devpci.npci++; return 1; } else { error(Etoomany); return 0; } } cl = pcislot(c); qlock(cl); switch(QID(c->qid)){ case Qoutb: outb(PADDR(cl->base), atol(a) & 0xff); break; case Qoutw: outs(PADDR(cl->base), atol(a) & 0xffff); break; case Qoutl: outl(PADDR(cl->base), atol(a)); break; } qunlock(cl); return 1; } static void pcidevcreate(Chan *, char*, int, ulong) { } static Chan * pcidevclone(Chan *c1, Chan *c2) { return devclone(c1, c2); } static void pcidevremove(Chan *c) { int slot; Mypci *cl; slot = CLIENTPATH(c->qid.path); if(slot == 0) return; slot--; /* align with pci[] */ cl = devpci.pci[slot]; free(cl); devpci.npci--; while (slot < devpci.npci) { devpci.pci[slot] = devpci.pci[slot+1]; slot++; } } static void pcidevclose(Chan *c) { USED(c); } Dev pcidevdevtab = { 'Z', "pcidev", pcidevreset, devinit, pcidevattach, pcidevclone, pcidevwalk, pcidevstat, pcidevopen, pcidevcreate, pcidevclose, pcidevread, devbread, pcidevwrite, devbwrite, pcidevremove, devwstat, }; --upas-hmsvcwmdhqvczrpmxnahpkocoj--