diff options
| author | Charles.Forsyth <devnull@localhost> | 2009-03-25 15:55:14 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2009-03-25 15:55:14 +0000 |
| commit | dfd1934d5e1ddbeb326f77fc0e52307c801a1a3e (patch) | |
| tree | f1e8b23278caae95e01d88b00421d6c3642357ef | |
| parent | 78dfdcbd59dc8f36975e7695933e3f753957474c (diff) | |
x20090325-1554
204 files changed, 62784 insertions, 0 deletions
diff --git a/emu/FreeBSD/asm-386.S b/emu/FreeBSD/asm-386.S new file mode 100644 index 00000000..35269c8f --- /dev/null +++ b/emu/FreeBSD/asm-386.S @@ -0,0 +1,107 @@ + .file "asm-FreeBSD-386.S" +#include <sys/syscall.h> + +/* + * executeonnewstack(void *tos, void (*tramp)(void *arg), void *arg) + */ + + .type ournewstack,@function + .global executeonnewstack +executeonnewstack: + pushl %ebp + movl %esp, %ebp + pushl %esi + + movl 8(%ebp), %esi /* get tos */ + subl $4, %esi + movl 16(%ebp), %eax + movl %eax, (%esi) /* stash arg on new stack */ + subl $4, %esi + movl 12(%ebp), %eax + movl %eax, (%esi) /* stash tramp on new stack */ + mov %esi, %esp /* swap stacks pronto */ + popl %eax /* recover the tramp address */ + call *%eax /* and jump to it (ho ho) */ + + /* if we return here, tramp didn't do it's job */ + + addl $8, %esp /* clean up for pose value */ + + leal SYS_exit, %eax + int $0x80 + +/* + * unlockandexit(int *key) + * + * NB: the return status may be rubbish if the stack is reused + * between the unlock and the system call, but this should + * not matter since no task is waiting for the result + */ + + .type unlockandexit,@function + .global unlockandexit +unlockandexit: + pushl %ebp + movl %esp, %ebp + + movl 8(%ebp), %esi /* get the key address */ + pushl $0 /* exit status 0 */ + movl $0, %eax /* unlock the stack allocator */ + movl %eax, (%esi) + leal SYS_exit, %eax /* call exit */ + int $0x80 + +/* + * umult(ulong m1, ulong m2, ulong *hi) + */ + + .type umult,@function + .global umult +umult: + pushl %ebp + movl %esp, %ebp + pushl %ebx + + movl 8(%ebp), %eax + movl 12(%ebp), %ebx + mull %ebx + movl 16(%ebp), %ebx + movl %edx, (%ebx) + + popl %ebx + popl %ebp + ret + + .type FPsave,@function + .global FPsave +FPsave: + pushl %ebp + movl %esp, %ebp + movl 8(%ebp), %eax + fstenv (%eax) + popl %ebp + ret + + .type FPrestore,@function + .global FPrestore +FPrestore: + pushl %ebp + movl %esp, %ebp + movl 8(%ebp), %eax + fldenv (%eax) + popl %ebp + ret + + .type getcallerpc,@function + .global getcallerpc +getcallerpc: + movl 4(%ebp), %eax + ret + + .type _tas,@function + .globl _tas +_tas: + movl $1, %eax + movl 4(%esp), %ecx + xchgl %eax, 0(%ecx) + ret diff --git a/emu/FreeBSD/audio.c b/emu/FreeBSD/audio.c new file mode 100644 index 00000000..ca92b7ca --- /dev/null +++ b/emu/FreeBSD/audio.c @@ -0,0 +1,547 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/filio.h> +#include "audio.h" +#include <sys/soundcard.h> + +#define Audio_Mic_Val SOUND_MIXER_MIC +#define Audio_Linein_Val SOUND_MIXER_LINE + +#define Audio_Speaker_Val SOUND_MIXER_SPEAKER +#define Audio_Headphone_Val SOUND_MIXER_PHONEOUT +#define Audio_Lineout_Val SOUND_MIXER_VOLUME + +#define Audio_Pcm_Val AFMT_S16_LE +#define Audio_Ulaw_Val AFMT_MU_LAW +#define Audio_Alaw_Val AFMT_A_LAW + +#include "audio-tbls.c" + +#define min(a,b) ((a) < (b) ? (a) : (b)) +static int debug; + +#define AUDIO_FILE_STRING "/dev/dsp" + +enum { + A_Pause, + A_UnPause +}; + +enum { + A_In, + A_Out +}; + +static QLock inlock; +static QLock outlock; + +static int audio_file = -1; /* file in/out */ +static int audio_file_in = -1; /* copy of above when opened O_READ/O_RDWR */ +static int audio_file_out = -1; /* copy of above when opened O_WRITE/O_RDWR */ + +static int audio_swap_flag = 0; /* endian swap */ + +static int audio_in_pause = A_UnPause; + +static Audio_t av; +static int mixerleftvol[32]; +static int mixerrightvol[32]; + +static int audio_enforce(Audio_t*); +static int audio_open(void); +static int audio_pause_in(int, int); +static int audio_flush(int, int); +static int audio_pause_out(int); +static int audio_set_blocking(int); +static int audio_set_info(int, Audio_d*, int); +static void audio_swap_endian(char*, int); + +void +audio_file_init(void) +{ + int i; + static ushort flag = 1; + + audio_swap_flag = *((uchar*)&flag) == 0; /* big-endian? */ + audio_info_init(&av); + for (i = 0; i < 32; i++) + mixerleftvol[i] = mixerrightvol[i] = 100; +} + +void +audio_ctl_init(void) +{ +} + +void +audio_file_open(Chan *c, int omode) +{ + char ebuf[ERRMAX]; + + if (debug) + print("audio_file_open(0x%.8lux, %d)\n", c, omode); + switch(omode){ + case OREAD: + qlock(&inlock); + if(waserror()){ + qunlock(&inlock); + nexterror(); + } + + if(audio_file_in >= 0) + error(Einuse); + if (audio_file < 0) + audio_file = audio_open(); + audio_file_in = audio_file; + poperror(); + qunlock(&inlock); + break; + case OWRITE: + qlock(&outlock); + if(waserror()){ + qunlock(&outlock); + nexterror(); + } + if(audio_file_out >= 0) + error(Einuse); + if (audio_file < 0) + audio_file = audio_open(); + audio_file_out = audio_file; + poperror(); + qunlock(&outlock); + break; + case ORDWR: + qlock(&inlock); + qlock(&outlock); + if(waserror()){ + qunlock(&outlock); + qunlock(&inlock); + nexterror(); + } + if(audio_file_in >= 0 || audio_file_out >= 0) + error(Einuse); + if (audio_file < 0) + audio_file = audio_open(); + audio_file_in = audio_file_out = audio_file; + poperror(); + qunlock(&outlock); + qunlock(&inlock); + break; + } + if (debug) + print("audio_file_open: success\nin %d out %d both %d\n", + audio_file_out, audio_file_in, audio_file); +} + +void +audio_ctl_open(Chan *c, int omode) +{ + USED(c); + USED(omode); +} + +void +audio_file_close(Chan *c) +{ + switch(c->mode){ + case OREAD: + qlock(&inlock); + qlock(&outlock); + if (audio_file_out < 0) { + close(audio_file); + audio_file = -1; + } + qunlock(&outlock); + audio_file_in = -1; + qunlock(&inlock); + break; + case OWRITE: + qlock(&inlock); + qlock(&outlock); + if (audio_file_in < 0) { + close(audio_file); + audio_file = -1; + } + audio_file_out = -1; + qunlock(&outlock); + qunlock(&inlock); + break; + case ORDWR: + qlock(&inlock); + qlock(&outlock); + close(audio_file); + audio_file_in = audio_file_out = audio_file = -1; + qunlock(&outlock); + qunlock(&inlock); + break; + } +} + +void +audio_ctl_close(Chan *c) +{ +} + +long +audio_file_read(Chan *c, void *va, long count, vlong offset) +{ + struct timespec time; + long ba, status, chunk, total; + char *pva = (char *) va; + + qlock(&inlock); + if(waserror()){ + qunlock(&inlock); + nexterror(); + } + + if(audio_file_in < 0) + error(Eperm); + + /* check block alignment */ + ba = av.in.bits * av.in.chan / Bits_Per_Byte; + + if(count % ba) + error(Ebadarg); + + if(! audio_pause_in(audio_file_in, A_UnPause)) + error(Eio); + + total = 0; + while(total < count) { + chunk = count - total; + osenter(); + status = read(audio_file_in, pva + total, chunk); + osleave(); + if(status < 0) + error(Eio); + total += status; + } + + if(total != count) + error(Eio); + + if(audio_swap_flag && av.out.bits == 16) + audio_swap_endian(pva, count); + + poperror(); + qunlock(&inlock); + + return count; +} + +long +audio_file_write(Chan *c, void *va, long count, vlong offset) +{ + struct timespec time; + long status = -1; + long ba, total, chunk, bufsz; + + if (debug > 1) + print("audio_file_write(0x%.8lux, 0x%.8lux, %ld, %uld)\n", + c, va, count, offset); + + qlock(&outlock); + if(waserror()){ + qunlock(&outlock); + nexterror(); + } + + if(audio_file_out < 0) + error(Eperm); + + /* check block alignment */ + ba = av.out.bits * av.out.chan / Bits_Per_Byte; + + if(count % ba) + error(Ebadarg); + + if(audio_swap_flag && av.out.bits == 16) + audio_swap_endian(va, count); + + total = 0; + bufsz = av.out.buf * Audio_Max_Buf / Audio_Max_Val; + + if(bufsz == 0) + error(Ebadarg); + + while(total < count) { + chunk = min(bufsz, count - total); + osenter(); + status = write(audio_file_out, va, chunk); + osleave(); + if(status <= 0) + error(Eio); + total += status; + } + + poperror(); + qunlock(&outlock); + + return count; +} + +static int +audio_open(void) +{ + int fd; + + /* open non-blocking in case someone already has it open */ + /* otherwise we would block until they close! */ + fd = open(AUDIO_FILE_STRING, O_RDWR|O_NONBLOCK); + if(fd < 0) + oserror(); + + /* change device to be blocking */ + if(!audio_set_blocking(fd)) { + if (debug) + print("audio_open: failed to set blocking\n"); + close(fd); + error("cannot set blocking mode"); + } + + if (debug) + print("audio_open: blocking set\n"); + + /* set audio info */ + av.in.flags = ~0; + av.out.flags = 0; + + if(! audio_set_info(fd, &av.in, A_In)) { + close(fd); + error(Ebadarg); + } + + av.in.flags = 0; + + /* tada, we're open, blocking, paused and flushed */ + return fd; +} + +long +audio_ctl_write(Chan *c, void *va, long count, vlong offset) +{ + int fd; + int ff; + Audio_t tmpav = av; + + tmpav.in.flags = 0; + tmpav.out.flags = 0; + + if (!audioparse(va, count, &tmpav)) + error(Ebadarg); + + if (!audio_enforce(&tmpav)) + error(Ebadarg); + + qlock(&inlock); + if (waserror()) { + qunlock(&inlock); + nexterror(); + } + + if (audio_file_in >= 0 && (tmpav.in.flags & AUDIO_MOD_FLAG)) { + if (!audio_pause_in(audio_file_in, A_Pause)) + error(Ebadarg); + if (!audio_flush(audio_file_in, A_In)) + error(Ebadarg); + if (!audio_set_info(audio_file_in, &tmpav.in, A_In)) + error(Ebadarg); + } + poperror(); + qunlock(&inlock); + + qlock(&outlock); + if (waserror()) { + qunlock(&outlock); + nexterror(); + } + if (audio_file_out >= 0 && (tmpav.out.flags & AUDIO_MOD_FLAG)){ + if (!audio_pause_out(audio_file_out)) + error(Ebadarg); + if (!audio_set_info(audio_file_out, &tmpav.out, A_Out)) + error(Ebadarg); + } + poperror(); + qunlock(&outlock); + + tmpav.in.flags = 0; + tmpav.out.flags = 0; + + av = tmpav; + + return count; +} + + + +static int +audio_set_blocking(int fd) +{ + int val; + + if((val = fcntl(fd, F_GETFL, 0)) == -1) + return 0; + + val &= ~O_NONBLOCK; + + if(fcntl(fd, F_SETFL, val) < 0) + return 0; + + return 1; +} + +static int +doioctl(int fd, int ctl, int *info) +{ + int status; + osenter(); + status = ioctl(fd, ctl, info); /* qlock and load general stuff */ + osleave(); + if (status < 0) + print("doioctl(0x%.8lux, 0x%.8lux) failed %d\n", ctl, *info, errno); + return status; +} + +static int +choosefmt(Audio_d *i) +{ + int newbits, newenc; + + newbits = i->bits; + newenc = i->enc; + switch (newenc) { + case Audio_Alaw_Val: + if (newbits == 8) + return AFMT_A_LAW; + break; + case Audio_Ulaw_Val: + if (newbits == 8) + return AFMT_MU_LAW; + break; + case Audio_Pcm_Val: + if (newbits == 8) + return AFMT_U8; + else if (newbits == 16) + return AFMT_S16_LE; + break; + } + return -1; +} + +static int +audio_set_info(int fd, Audio_d *i, int d) +{ + int status; + int unequal_stereo = 0; + + if(fd < 0) + return 0; + + /* fmt */ + if(i->flags & (AUDIO_BITS_FLAG || AUDIO_ENC_FLAG)) { + int oldfmt, newfmt; + oldfmt = AFMT_QUERY; + if (doioctl(fd, SNDCTL_DSP_SETFMT, &oldfmt) < 0) + return 0; + if (debug) + print("audio_set_info: current format 0x%.8lux\n", oldfmt); + newfmt = choosefmt(i); + if (debug) + print("audio_set_info: new format 0x%.8lux\n", newfmt); + if (newfmt == -1 || newfmt != oldfmt && doioctl(fd, SNDCTL_DSP_SETFMT, &newfmt) < 0) + return 0; + } + + /* channels */ + if(i->flags & AUDIO_CHAN_FLAG) { + int channels = i->chan; + if (debug) + print("audio_set_info: new channels %d\n", channels); + if (doioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0 + || channels != i->chan) + return 0; + } + + /* sample rate */ + if(i->flags & AUDIO_RATE_FLAG) { + int speed = i->rate; + if (debug) + print("audio_set_info: new speed %d\n", speed); + if (doioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0 || speed != i->rate) + return 0; + } + + /* dev volume */ + if(i->flags & (AUDIO_LEFT_FLAG | AUDIO_VOL_FLAG | AUDIO_RIGHT_FLAG)) { + int val; + if (i->flags & (AUDIO_LEFT_FLAG | AUDIO_VOL_FLAG)) + mixerleftvol[i->dev] = (i->left * 100) / Audio_Max_Val; + if (i->flags & (AUDIO_RIGHT_FLAG | AUDIO_VOL_FLAG)) + mixerrightvol[i->dev] = (i->right * 100) / Audio_Max_Val; + val = mixerleftvol[i->dev] | (mixerrightvol[i->dev] << 8); + doioctl(fd, MIXER_WRITE(i->dev), &val); + } + + if (i->flags & AUDIO_DEV_FLAG) { + } + + return 1; +} + +void +audio_swap_endian(char *p, int n) +{ + int b; + + while (n > 1) { + b = p[0]; + p[0] = p[1]; + p[1] = b; + p += 2; + n -= 2; + } +} + +static int +audio_pause_out(int fd) +{ + USED(fd); + return 1; +} + +static int +audio_pause_in(int fd, int f) +{ + USED(fd); + USED(f); + return 1; +} + +static int +audio_flush(int fd, int d) +{ + int x; + return doioctl(fd, SNDCTL_DSP_SYNC, &x) >= 0; +} + +static int +audio_enforce(Audio_t *t) +{ + if((t->in.enc == Audio_Ulaw_Val || t->in.enc == Audio_Alaw_Val) && + (t->in.rate != 8000 || t->in.chan != 1)) + return 0; + if((t->out.enc == Audio_Ulaw_Val || t->out.enc == Audio_Alaw_Val) && + (t->out.rate != 8000 || t->out.chan != 1)) + return 0; + return 1; +} + +Audio_t* +getaudiodev(void) +{ + return &av; +} diff --git a/emu/FreeBSD/cmd.c b/emu/FreeBSD/cmd.c new file mode 100644 index 00000000..ed4cabab --- /dev/null +++ b/emu/FreeBSD/cmd.c @@ -0,0 +1,213 @@ +#include <sys/types.h> +#include <signal.h> +#include <pwd.h> +#include <sys/resource.h> +#include <sys/wait.h> +#include <fcntl.h> + +#include "dat.h" +#include "fns.h" +#include "error.h" + +enum +{ + Debug = 0 +}; + +/* + * os-specific devcmd support. + * this version should be reasonably portable across Unix systems. + */ +typedef struct Targ Targ; +struct Targ +{ + int fd[3]; /* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */ + char** args; + char* dir; + int pid; + int wfd; /* child writes errors that occur after the fork or on exec */ + int uid; + int gid; +}; + +extern int gidnobody; +extern int uidnobody; + +static int +childproc(Targ *t) +{ + int i, nfd; + + if(Debug) + print("devcmd: '%s'", t->args[0]); + + nfd = getdtablesize(); + for(i = 0; i < nfd; i++) + if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd) + close(i); + + dup2(t->fd[0], 0); + dup2(t->fd[1], 1); + dup2(t->fd[2], 2); + close(t->fd[0]); + close(t->fd[1]); + close(t->fd[2]); + + /* should have an auth file to do host-specific authorisation? */ + if(t->gid != -1){ + if(setgid(t->gid) < 0 && getegid() == 0){ + fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno)); + _exit(1); + } + } + + if(t->uid != -1){ + if(setuid(t->uid) < 0 && geteuid() == 0){ + fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno)); + _exit(1); + } + } + + if(t->dir != nil && chdir(t->dir) < 0){ + fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno)); + _exit(1); + } + + signal(SIGPIPE, SIG_DFL); + + execvp(t->args[0], t->args); + if(Debug) + print("execvp: %s\n",strerror(errno)); + fprint(t->wfd, "exec failed: %s", strerror(errno)); + + _exit(1); +} + +void* +oscmd(char **args, int nice, char *dir, int *fd) +{ + Targ *t; + int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid; + + t = mallocz(sizeof(*t), 1); + if(t == nil) + return nil; + + fd0[0] = fd0[1] = -1; + fd1[0] = fd1[1] = -1; + fd2[0] = fd2[1] = -1; + wfd[0] = wfd[1] = -1; + if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0) + goto Error; + if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0) /* close on exec to give end of file on success */ + goto Error; + + t->fd[0] = fd0[0]; + t->fd[1] = fd1[1]; + t->fd[2] = fd2[1]; + t->wfd = wfd[1]; + t->args = args; + t->dir = dir; + t->gid = up->env->gid; + if(t->gid == -1) + t->gid = gidnobody; + t->uid = up->env->uid; + if(t->uid == -1) + t->uid = uidnobody; + + signal(SIGCHLD, SIG_DFL); + switch(pid = fork()) { + case -1: + goto Error; + case 0: + setpgid(0, getpid()); + if(nice) + oslopri(); + childproc(t); + _exit(1); + default: + t->pid = pid; + if(Debug) + print("cmd pid %d\n", t->pid); + break; + } + + close(fd0[0]); + close(fd1[1]); + close(fd2[1]); + close(wfd[1]); + + n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1); + close(wfd[0]); + if(n > 0){ + close(fd0[1]); + close(fd1[0]); + close(fd2[0]); + free(t); + up->genbuf[n] = 0; + if(Debug) + print("oscmd: bad exec: %q\n", up->genbuf); + error(up->genbuf); + return nil; + } + + fd[0] = fd0[1]; + fd[1] = fd1[0]; + fd[2] = fd2[0]; + return t; + +Error: + r = errno; + if(Debug) + print("oscmd: %q\n",strerror(r)); + close(fd0[0]); + close(fd0[1]); + close(fd1[0]); + close(fd1[1]); + close(fd2[0]); + close(fd2[1]); + close(wfd[0]); + close(wfd[1]); + error(strerror(r)); + return nil; +} + +int +oscmdkill(void *a) +{ + Targ *t = a; + + if(Debug) + print("kill: %d\n", t->pid); + return kill(-t->pid, SIGTERM); +} + +int +oscmdwait(void *a, char *buf, int n) +{ + Targ *t = a; + int s; + + if(waitpid(t->pid, &s, 0) == -1){ + if(Debug) + print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno)); + return -1; + } + if(WIFEXITED(s)){ + if(WEXITSTATUS(s) == 0) + return snprint(buf, n, "%d 0 0 0 ''", t->pid); + return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s)); + } + if(WIFSIGNALED(s)){ + if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL) + return snprint(buf, n, "%d 0 0 0 killed", t->pid); + return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s)); + } + return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s); +} + +void +oscmdfree(void *a) +{ + free(a); +} diff --git a/emu/FreeBSD/deveia.c b/emu/FreeBSD/deveia.c new file mode 100644 index 00000000..2ef622f7 --- /dev/null +++ b/emu/FreeBSD/deveia.c @@ -0,0 +1,39 @@ +/* + * FreeBSD serial port definitions + */ + +static char *sysdev[] = { + "/dev/cuaa0", + "/dev/cuaa1", + "/dev/cuaa2", + "/dev/cuaa3", +}; + +#include <sys/ioctl.h> +#include "deveia-posix.c" +#include "deveia-bsd.c" + + +static struct tcdef_t bps[] = { + {0, B0}, + {50, B50}, + {75, B75}, + {110, B110}, + {134, B134}, + {150, B150}, + {200, B200}, + {300, B300}, + {600, B600}, + {1200, B1200}, + {1800, B1800}, + {2400, B2400}, + {4800, B4800}, + {9600, B9600}, + {19200, B19200}, + {38400, B38400}, + {57600, B57600}, + {115200, B115200}, + {230400, B230400}, + {-1, -1} +}; + diff --git a/emu/FreeBSD/devfs.c b/emu/FreeBSD/devfs.c new file mode 100644 index 00000000..9017c3fc --- /dev/null +++ b/emu/FreeBSD/devfs.c @@ -0,0 +1 @@ +#include "devfs-posix.c" diff --git a/emu/FreeBSD/emu b/emu/FreeBSD/emu new file mode 100644 index 00000000..257df186 --- /dev/null +++ b/emu/FreeBSD/emu @@ -0,0 +1,106 @@ +dev + root + cons + env + mnt + pipe + prog + prof + srv + dup + ssl + cap + fs + cmd cmd + indir + + draw win-x11a + pointer + snarf + + ip ipif ipaux + eia + audio audio + mem + +lib + interp + tk + freetype + math + draw + + memlayer + memdraw + keyring + sec + mp + + 9 + +link + +mod + sys + draw + + tk + math + srv srv + keyring + loader + freetype + +port + alloc + cache + chan + dev + dial + dis + discall + env + error + errstr + exception + exportfs + inferno + latin1 + main + parse + pgrp + print + proc + qio + random + sysfile + uqid + +code + +init + emuinit + +root + /dev / + /fd / + /prog / + /net / + /net.alt / + /chan / + /nvfs / + /env / +# /chan +# /dev +# /dis +# /env +# /n +# /net +# /nvfs / +# /prog +# /icons +# /osinit.dis +# /dis/emuinit.dis +# /dis/lib/auth.dis +# /dis/lib/ssl.dis +# /n/local / diff --git a/emu/FreeBSD/ipif.c b/emu/FreeBSD/ipif.c new file mode 100644 index 00000000..853e2e93 --- /dev/null +++ b/emu/FreeBSD/ipif.c @@ -0,0 +1,372 @@ +#include <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <netdb.h> +#include "dat.h" +#include "fns.h" +#include "ip.h" +#include "error.h" +#include <sys/ioctl.h> + +int +so_socket(int type) +{ + int fd, one; + + switch(type) { + default: + error("bad protocol type"); + case S_TCP: + type = SOCK_STREAM; + break; + case S_UDP: + type = SOCK_DGRAM; + break; + } + + fd = socket(AF_INET, type, 0); + if(fd < 0) + oserror(); + if(type == SOCK_DGRAM){ + one = 1; + setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char*)&one, sizeof (one)); + }else{ + one = 1; + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(one)); + } + return fd; +} + +int +so_send(int sock, void *va, int len, void *hdr, int hdrlen) +{ + int r; + struct sockaddr sa; + struct sockaddr_in *sin; + char *h = hdr; + + + osenter(); + if(hdr == 0) + r = write(sock, va, len); + else { + memset(&sa, sizeof(sa), 0); + sin = (struct sockaddr_in*)&sa; + sin->sin_family = AF_INET; + switch(hdrlen){ + case OUdphdrlenv4: + memmove(&sin->sin_addr, h, 4); + memmove(&sin->sin_port, h+8, 2); + break; + case OUdphdrlen: + v6tov4((uchar*)&sin->sin_addr, h); + memmove(&sin->sin_port, h+2*IPaddrlen, 2); /* rport */ + break; + default: + v6tov4((uchar*)&sin->sin_addr, h); + memmove(&sin->sin_port, h+3*IPaddrlen, 2); + break; + } + r = sendto(sock, va, len, 0, &sa, sizeof(sa)); + } + osleave(); + return r; +} + +int +so_recv(int sock, void *va, int len, void *hdr, int hdrlen) +{ + int r, l; + struct sockaddr sa; + struct sockaddr_in *sin; + char h[Udphdrlen]; + + + osenter(); + if(hdr == 0) + r = read(sock, va, len); + else { + sin = (struct sockaddr_in*)&sa; + l = sizeof(sa); + r = recvfrom(sock, va, len, 0, &sa, &l); + if(r >= 0) { + memset(h, sizeof h, 0); + switch(hdrlen){ + case OUdphdrlenv4: + memmove(h, &sin->sin_addr, 4); + memmove(h+2*IPv4addrlen, &sin->sin_port, 2); + break; + case OUdphdrlen: + v4tov6(h, (uchar*)&sin->sin_addr); + memmove(h+2*IPaddrlen, &sin->sin_port, 2); + break; + default: + v4tov6(h, (uchar*)&sin->sin_addr); + memmove(h+3*IPaddrlen, &sin->sin_port, 2); + break; + } + + /* alas there's no way to get the local addr/port correctly. Pretend. */ + getsockname(sock, &sa, &l); + switch(hdrlen){ + case OUdphdrlenv4: + memmove(h+IPv4addrlen, &sin->sin_addr, IPv4addrlen); + memmove(h+2*IPv4addrlen+2, &sin->sin_port, 2); + break; + case OUdphdrlen: + v4tov6(h+IPaddrlen, (uchar*)&sin->sin_addr); + memmove(h+2*IPaddrlen+2, &sin->sin_port, 2); + break; + default: + v4tov6(h+IPaddrlen, (uchar*)&sin->sin_addr); + v4tov6(h+2*IPaddrlen, (uchar*)&sin->sin_addr); /* ifcaddr */ + memmove(h+3*IPaddrlen+2, &sin->sin_port, 2); + break; + } + memmove(hdr, h, hdrlen); + } + } + osleave(); + return r; +} + +void +so_close(int sock) +{ + close(sock); +} + +void +so_connect(int fd, unsigned long raddr, unsigned short rport) +{ + int r; + struct sockaddr sa; + struct sockaddr_in *sin; + + memset(&sa, 0, sizeof(sa)); + sin = (struct sockaddr_in*)&sa; + sin->sin_family = AF_INET; + hnputs(&sin->sin_port, rport); + hnputl(&sin->sin_addr.s_addr, raddr); + + osenter(); + r = connect(fd, &sa, sizeof(sa)); + osleave(); + if(r < 0) + oserror(); +} + +void +so_getsockname(int fd, unsigned long *laddr, unsigned short *lport) +{ + int len; + struct sockaddr sa; + struct sockaddr_in *sin; + + + len = sizeof(sa); + if(getsockname(fd, &sa, &len) < 0) + oserror(); + + sin = (struct sockaddr_in*)&sa; + if(sin->sin_family != AF_INET || len != sizeof(*sin)) + error("not AF_INET"); + + *laddr = nhgetl(&sin->sin_addr.s_addr); + *lport = nhgets(&sin->sin_port); +} + +void +so_listen(int fd) +{ + int r; + + osenter(); + r = listen(fd, 5); + osleave(); + if(r < 0) + oserror(); +} + +int +so_accept(int fd, unsigned long *raddr, unsigned short *rport) +{ + int nfd, len; + struct sockaddr sa; + struct sockaddr_in *sin; + + sin = (struct sockaddr_in*)&sa; + + len = sizeof(sa); + osenter(); + nfd = accept(fd, &sa, &len); + osleave(); + if(nfd < 0) + oserror(); + + if(sin->sin_family != AF_INET || len != sizeof(*sin)) + error("not AF_INET"); + + *raddr = nhgetl(&sin->sin_addr.s_addr); + *rport = nhgets(&sin->sin_port); + return nfd; +} + +void +so_bind(int fd, int su, unsigned long addr, unsigned short port) +{ + int i, one; + struct sockaddr sa; + struct sockaddr_in *sin; + + sin = (struct sockaddr_in*)&sa; + + one = 1; + if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) < 0) { + oserrstr(up->genbuf, sizeof(up->genbuf)); + print("setsockopt: %s", up->genbuf); + } + + if(su) { + for(i = 600; i < 1024; i++) { + memset(&sa, 0, sizeof(sa)); + sin->sin_family = AF_INET; + hnputl(&sin->sin_addr.s_addr, addr); + hnputs(&sin->sin_port, i); + + if(bind(fd, &sa, sizeof(sa)) >= 0) + return; + } + oserror(); + } + + memset(&sa, 0, sizeof(sa)); + sin->sin_family = AF_INET; + hnputl(&sin->sin_addr.s_addr, addr); + hnputs(&sin->sin_port, port); + + if(bind(fd, &sa, sizeof(sa)) < 0) + oserror(); +} + +void +so_setsockopt(int fd, int opt, int value) +{ + int r; + struct linger l; + + if(opt == SO_LINGER){ + l.l_onoff = 1; + l.l_linger = (short) value; + osenter(); + r = setsockopt(fd, SOL_SOCKET, opt, (char *)&l, sizeof(l)); + osleave(); + }else + error(Ebadctl); + if(r < 0) + oserror(); +} + +int +so_gethostbyname(char *host, char**hostv, int n) +{ + int i; + char buf[32]; + uchar *p; + struct hostent *hp; + + hp = gethostbyname(host); + if(hp == 0) + return 0; + + for(i = 0; hp->h_addr_list[i] && i < n; i++) { + p = hp->h_addr_list[i]; + snprint(buf, sizeof(buf), "%ud.%ud.%ud.%ud", p[0], p[1], p[2], p[3]); + hostv[i] = strdup(buf); + if(hostv[i] == 0) + break; + } + return i; +} + +int +so_gethostbyaddr(char *addr, char **hostv, int n) +{ + int i; + struct hostent *hp; + unsigned long straddr; + + straddr = inet_addr(addr); + if(straddr == -1) + return 0; + + hp = gethostbyaddr((char *)&straddr, sizeof(straddr), AF_INET); + if(hp == 0) + return 0; + + hostv[0] = strdup(hp->h_name); + if(hostv[0] == 0) + return 0; + for(i = 1; hp->h_aliases[i-1] && i < n; i++) { + hostv[i] = strdup(hp->h_aliases[i-1]); + if(hostv[i] == 0) + break; + } + return i; +} + +int +so_getservbyname(char *service, char *net, char *port) +{ + ushort p; + struct servent *s; + + s = getservbyname(service, net); + if(s == 0) + return -1; + p = s->s_port; + sprint(port, "%d", nhgets(&p)); + return 0; +} + +int +so_hangup(int fd, int linger) +{ + int r; + static struct linger l = {1, 1000}; + + osenter(); + if(linger) + setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&l, sizeof(l)); + r = shutdown(fd, 2); + if(r >= 0) + r = close(fd); + osleave(); + return r; +} + +void +arpadd(char *ipaddr, char *eaddr, int n) +{ + error("arp not implemented"); +} + +int +so_mustbind(int restricted, int port) +{ + return restricted || port != 0; +} + +void +so_keepalive(int fd, int ms) +{ + int on; + + USED(ms); + on = 1; + setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char*)&on, sizeof(on)); +} diff --git a/emu/FreeBSD/mkfile b/emu/FreeBSD/mkfile new file mode 100644 index 00000000..0b206a0b --- /dev/null +++ b/emu/FreeBSD/mkfile @@ -0,0 +1,46 @@ +SYSTARG=FreeBSD +OBJTYPE=386 +<../../mkconfig +SYSTARG=FreeBSD +OBJTYPE=386 + +#Configurable parameters + +CONF=emu #default configuration +CONFLIST=emu +CLEANCONFLIST= + +INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed + +#end configurable parameters + +<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system + +<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS + +OBJ=\ + asm-$OBJTYPE.$O\ + os.$O\ + $CONF.root.$O\ + lock.$O\ + $DEVS\ + $PORT\ + +HFILES=\ + +CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS +SYSLIBS= -lm -lX11 -lXext +KERNDATE=`{$NDATE} + +default:V: $O.$CONF + +<../port/portmkfile + +$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBFILES + $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c + $LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS + +install:V: $O.$CONF + cp $O.$CONF $INSTALLDIR/$CONF + +devfs.$O: ../port/devfs-posix.c diff --git a/emu/FreeBSD/mkfile-FreeBSD b/emu/FreeBSD/mkfile-FreeBSD new file mode 100644 index 00000000..17b100ae --- /dev/null +++ b/emu/FreeBSD/mkfile-FreeBSD @@ -0,0 +1,17 @@ +# +# architecture-dependent files for FreeBSD +# + +LDFLAGS= + +TARGFILES=devfs-posix.$O\ + deveia-FreeBSD.$O\ + devip.$O\ + ipif-posix.$O\ + os-FreeBSD.$O\ + win-x11.$O\ + srv.$O\ + lock.$O\ + asm-FreeBSD-$OBJTYPE.$O + +SYSLIBS=/usr/X11R6/lib/libX11.a -lm diff --git a/emu/FreeBSD/os.c b/emu/FreeBSD/os.c new file mode 100644 index 00000000..8ef3f1b5 --- /dev/null +++ b/emu/FreeBSD/os.c @@ -0,0 +1,513 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#undef getwd +#include <signal.h> +#include <sys/socket.h> +#include <time.h> +#include <sys/time.h> +#include <termios.h> +#include <sched.h> +#include <pwd.h> +#include <errno.h> +#include <unistd.h> +#include <sys/resource.h> + +enum +{ + DELETE = 0x7F, + NSTACKSPERALLOC = 16, + X11STACK= 256*1024 +}; +char *hosttype = "FreeBSD"; + +extern void unlockandexit(int*); +extern void executeonnewstack(void*, void (*f)(void*), void*); +static void *stackalloc(Proc *p, void **tos); +static void stackfreeandexit(void *stack); + +extern int dflag; + +void +pexit(char *msg, int t) +{ + Osenv *e; + Proc *p; + void *kstack; + + lock(&procs.l); + p = up; + if(p->prev) + p->prev->next = p->next; + else + procs.head = p->next; + + if(up->next) + p->next->prev = p->prev; + else + procs.tail = p->prev; + unlock(&procs.l); + + if(0) + print("pexit: %s: %s\n", up->text, msg); + + e = up->env; + if(e != nil) { + closefgrp(e->fgrp); + closepgrp(e->pgrp); + closeegrp(e->egrp); + closesigs(e->sigs); + } + kstack = p->kstack; + free(p->prog); + free(p); + if(kstack != nil) + stackfreeandexit(kstack); +} + +void +trapBUS(int signo, siginfo_t *info, void *context) +{ + if(info) + print("trapBUS: signo: %d code: %d addr: %lx\n", + info->si_signo, info->si_code, info->si_addr); + else + print("trapBUS: no info\n"); + disfault(nil, "Bus error"); +} + +static void +trapUSR1(int signo) +{ + int intwait; + + USED(signo); + + intwait = up->intwait; + up->intwait = 0; /* clear it to let proc continue in osleave */ + + if(up->type != Interp) /* Used to unblock pending I/O */ + return; + if(intwait == 0) /* Not posted so its a sync error */ + disfault(nil, Eintr); /* Should never happen */ +} + +static void +trapUSR2(int signo) +{ + USED(signo); + /* we've done our work of interrupting sigsuspend */ +} + +static void +trapILL(int signo) +{ + disfault(nil, "Illegal instruction"); +} + +static void +trapSEGV(int signo) +{ + disfault(nil, "Segmentation violation"); +} + +static sigset_t initmask; + +static void +setsigs(void) +{ + struct sigaction act; + sigset_t mask; + + memset(&act, 0 , sizeof(act)); + sigemptyset(&initmask); + + signal(SIGPIPE, SIG_IGN); /* prevent signal when devcmd child exits */ + if(signal(SIGTERM, SIG_IGN) != SIG_IGN) + signal(SIGTERM, cleanexit); + + act.sa_handler = trapUSR1; + act.sa_mask = initmask; + sigaction(SIGUSR1, &act, nil); + + act.sa_handler = trapUSR2; + sigaction(SIGUSR2, &act, nil); + sigemptyset(&mask); + sigaddset(&mask, SIGUSR2); + sigaddset(&initmask, SIGUSR2); + sigprocmask(SIG_BLOCK, &mask, NULL); + + /* + * prevent Zombies forming when any process terminates + */ + act.sa_sigaction = 0; + act.sa_flags |= SA_NOCLDWAIT; + if(sigaction(SIGCHLD, &act, nil)) + panic("sigaction SIGCHLD"); + + if(sflag == 0) { + act.sa_sigaction = trapBUS; + act.sa_flags |= SA_SIGINFO; + if(sigaction(SIGBUS, &act, nil)) + panic("sigaction SIGBUS"); + act.sa_handler = trapILL; + if(sigaction(SIGILL, &act, nil)) + panic("sigaction SIGBUS"); + act.sa_handler = trapSEGV; + if(sigaction(SIGSEGV, &act, nil)) + panic("sigaction SIGSEGV"); + if(sigaddset(&initmask, SIGINT) == -1) + panic("sigaddset"); + } + if(sigprocmask(SIG_BLOCK, &initmask, nil)!= 0) + panic("sigprocmask"); +} + +static int +tramp(void *arg) +{ + Proc *p; + + p = arg; + p->pid = p->sigid = getpid(); + sigprocmask(SIG_BLOCK, &initmask, nil); /* in 5.3, rfork_thread doesn't copy from parent, contrary to docs? */ + (*p->func)(p->arg); + pexit("{Tramp}", 0); + _exit(0); +} + +int +kproc(char *name, void (*func)(void*), void *arg, int flags) +{ + Proc *p; + Pgrp *pg; + Fgrp *fg; + Egrp *eg; + int pid; + void *tos; + + p = newproc(); + + if(flags & KPDUPPG) { + pg = up->env->pgrp; + incref(&pg->r); + p->env->pgrp = pg; + } + if(flags & KPDUPFDG) { + fg = up->env->fgrp; + incref(&fg->r); + p->env->fgrp = fg; + } + if(flags & KPDUPENVG) { + eg = up->env->egrp; + incref(&eg->r); + p->env->egrp = eg; + } + + p->env->uid = up->env->uid; + p->env->gid = up->env->gid; + kstrdup(&p->env->user, up->env->user); + + strcpy(p->text, name); + + p->func = func; + p->arg = arg; + + lock(&procs.l); + if(procs.tail != nil) { + p->prev = procs.tail; + procs.tail->next = p; + } + else { + procs.head = p; + p->prev = nil; + } + procs.tail = p; + unlock(&procs.l); + + if(flags & KPX11){ + p->kstack = nil; /* never freed; also up not defined */ + tos = (char*)mallocz(X11STACK, 0) + X11STACK - sizeof(void*); + }else + p->kstack = stackalloc(p, &tos); + pid = rfork_thread(RFPROC|RFMEM|RFNOWAIT, tos, tramp, p); + if(pid < 0) + panic("ourfork"); + + return pid; + +} + +void +oshostintr(Proc *p) +{ + kill(p->sigid, SIGUSR1); +} + +void +osblock(void) +{ + sigset_t mask; + + sigprocmask(SIG_SETMASK, NULL, &mask); + sigdelset(&mask, SIGUSR2); + sigsuspend(&mask); +} + +void +osready(Proc *p) +{ + if(kill(p->sigid, SIGUSR2) < 0) + fprint(2, "emu: osready failed: pid %d: %s\n", p->sigid, strerror(errno)); +} + +void +oslongjmp(void *regs, osjmpbuf env, int val) +{ + USED(regs); + siglongjmp(env, val); +} + +struct termios tinit; + +static void +termset(void) +{ + struct termios t; + + tcgetattr(0, &t); + tinit = t; + t.c_lflag &= ~(ICANON|ECHO|ISIG); + t.c_cc[VMIN] = 1; + t.c_cc[VTIME] = 0; + tcsetattr(0, TCSANOW, &t); +} + +static void +termrestore(void) +{ + tcsetattr(0, TCSANOW, &tinit); +} + +void +cleanexit(int x) +{ + USED(x); + + if(up->intwait) { + up->intwait = 0; + return; + } + + if(dflag == 0) + termrestore(); + + kill(0, SIGKILL); + exit(0); +} + +void +osreboot(char *file, char **argv) +{ + if(dflag == 0) + termrestore(); + execvp(file, argv); + panic("reboot failure"); +} + +int gidnobody= -1, uidnobody= -1; + +void +getnobody() +{ + struct passwd *pwd; + + if(pwd = getpwnam("nobody")) { + uidnobody = pwd->pw_uid; + gidnobody = pwd->pw_gid; + } +} + +void +libinit(char *imod) +{ + struct passwd *pw; + Proc *p; + void *tos; + char sys[64]; + + setsid(); + + gethostname(sys, sizeof(sys)); + kstrdup(&ossysname, sys); + getnobody(); + + if(dflag == 0) + termset(); + + setsigs(); + + p = newproc(); + p->kstack = stackalloc(p, &tos); + + pw = getpwuid(getuid()); + if(pw != nil) + kstrdup(&eve, pw->pw_name); + else + print("cannot getpwuid\n"); + + p->env->uid = getuid(); + p->env->gid = getgid(); + + executeonnewstack(tos, emuinit, imod); +} + +int +readkbd(void) +{ + int n; + char buf[1]; + + n = read(0, buf, sizeof(buf)); + if(n < 0) + print("keyboard close (n=%d, %s)\n", n, strerror(errno)); + if(n <= 0) + pexit("keyboard thread", 0); + + switch(buf[0]) { + case '\r': + buf[0] = '\n'; + break; + case DELETE: + cleanexit(0); + break; + } + return buf[0]; +} + +/* + * Return an abitrary millisecond clock time + */ +long +osmillisec(void) +{ + static long sec0 = 0, usec0; + struct timeval t; + + if(gettimeofday(&t,(struct timezone*)0)<0) + return 0; + if(sec0==0) { + sec0 = t.tv_sec; + usec0 = t.tv_usec; + } + return (t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000; +} + +int +limbosleep(ulong milsec) +{ + return osmillisleep(milsec); +} + +/* + * Return the time since the epoch in nanoseconds and microseconds + * The epoch is defined at 1 Jan 1970 + */ +vlong +osnsec(void) +{ + struct timeval t; + + gettimeofday(&t, nil); + return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000; +} + +vlong +osusectime(void) +{ + struct timeval t; + + gettimeofday(&t, nil); + return (vlong)t.tv_sec * 1000000 + t.tv_usec; +} + +int +osmillisleep(ulong milsec) +{ + struct timespec time; + + time.tv_sec = milsec / 1000; + time.tv_nsec = (milsec % 1000) * 1000000; + nanosleep(&time, 0); + return 0; +} + +void +osyield(void) +{ + sched_yield(); +} + +void +ospause(void) +{ + for(;;) + pause(); +} + +void +oslopri(void) +{ + setpriority(PRIO_PROCESS, 0, getpriority(PRIO_PROCESS,0)+4); +} + +static struct { + Lock l; + void *free; +} stacklist; + +static void +_stackfree(void *stack) +{ + *((void **)stack) = stacklist.free; + stacklist.free = stack; +} + +static void +stackfreeandexit(void *stack) +{ + lock(&stacklist.l); + _stackfree(stack); + unlockandexit(&stacklist.l.val); +} + +static void * +stackalloc(Proc *p, void **tos) +{ + void *rv; + lock(&stacklist.l); + if (stacklist.free == 0) { + int x; + /* + * obtain some more by using sbrk() + */ + void *more = sbrk(KSTACK * (NSTACKSPERALLOC + 1)); + if (more == 0) + panic("stackalloc: no more stacks"); + /* + * align to KSTACK + */ + more = (void *)((((unsigned long)more) + (KSTACK - 1)) & ~(KSTACK - 1)); + /* + * free all the new stacks onto the freelist + */ + for (x = 0; x < NSTACKSPERALLOC; x++) + _stackfree((char *)more + KSTACK * x); + } + rv = stacklist.free; + stacklist.free = *(void **)rv; + unlock(&stacklist.l); + *tos = rv + KSTACK - sizeof(void*); + *(Proc **)rv = p; + return rv; +} diff --git a/emu/Hp/NOTE b/emu/Hp/NOTE new file mode 100644 index 00000000..3f1041ea --- /dev/null +++ b/emu/Hp/NOTE @@ -0,0 +1 @@ +this, like Irix, lacks deveia diff --git a/emu/Hp/asm-s800.s b/emu/Hp/asm-s800.s new file mode 100644 index 00000000..865c0b2b --- /dev/null +++ b/emu/Hp/asm-s800.s @@ -0,0 +1,113 @@ +; +; /* +; * To get lock routine, compile this into a .s, then SUBSTITUTE +; * a LOAD AND CLEAR WORD instruction for the load and store of +; * l->key. +; * +; */ +; typedef struct Lock { +; int key; +; int pid; +; } Lock; +; +; int +; mutexlock(Lock *l) +; { +; int key; +; +; key = l->key; +; l->key = 0; +; return key != 0; +; } + + .LEVEL 1.1 + + .SPACE $TEXT$,SORT=8 + .SUBSPA $CODE$,QUAD=0,ALIGN=8,ACCESS=0x2c,CODE_ONLY,SORT=24 +mutexlock + .PROC + .CALLINFO FRAME=0,ARGS_SAVED + .ENTRY +; SUBSTITUTED LDW 0(%r26),%r31 +; SUBSTITUTED STWS %r0,0(%r26) + LDCWS 0(%r26),%r31 ; SUBSTITUTED + COMICLR,= 0,%r31,%r28 + LDI 1,%r28 + .EXIT + BV,N %r0(%r2) + .PROCEND + +; +; JIT help +; + .SPACE $TEXT$,SORT=8 + .SUBSPA $CODE$,QUAD=0,ALIGN=8,ACCESS=0x2c,CODE_ONLY,SORT=24 +calldata + .PROC + .CALLINFO CALLER,FRAME=16,SAVE_RP + .ENTRY + STW %r2,-20(%r30) + LDO 64(%r30),%r30 + ADDIL LR'dataptr-$global$,%r27 + LDW RR'dataptr-$global$(%r1),%r31 + BLE 0(%sr5,%r31) + COPY %r31,%r2 + LDW -84(%r30),%r2 + BV %r0(%r2) + .EXIT + LDO -64(%r30),%r30 + .PROCEND + + .SPACE $PRIVATE$ + .SUBSPA $SHORTBSS$ +dataptr .COMM 4 + .SUBSPA $SHORTDATA$,QUAD=1,ALIGN=8,ACCESS=0x1f,SORT=24 +dyncall + .WORD $$dyncall + .EXPORT calldata + .EXPORT dyncall + .IMPORT $$dyncall,MILLICODE + + .SPACE $TEXT$ + .SUBSPA $CODE$ + .SPACE $PRIVATE$,SORT=16 + .SUBSPA $DATA$,QUAD=1,ALIGN=8,ACCESS=0x1f,SORT=16 + .SPACE $TEXT$ + .SUBSPA $CODE$ + .EXPORT mutexlock,ENTRY,PRIV_LEV=3,ARGW0=GR,RTNVAL=GR + + .code +; segflush(addr,len) + .proc + .callinfo + .export segflush,entry +segflush + .enter + ldsid (0,%arg0),%r1 + mtsp %r1,%sr0 + ldo -1(%arg1),%arg1 + copy %arg0,%arg2 + copy %arg1,%arg3 + fdc %arg1(0,%arg0) + +loop1 + addib,>,n -16,%arg1,loop1 + fdc %arg1(0,%arg0) + fdc 0(0,%arg0) + sync + fic %arg3(%sr0,%arg2) +loop2 + addib,>,n -16,%arg3,loop2 + fic %arg3(%sr0,%arg2) + fic 0(%sr0,%arg2) + sync + nop + nop + nop + nop + nop + nop + nop + .leave + .procend + .end diff --git a/emu/Hp/cmd.c b/emu/Hp/cmd.c new file mode 100644 index 00000000..bff1f03b --- /dev/null +++ b/emu/Hp/cmd.c @@ -0,0 +1,213 @@ +#include <sys/types.h> +#include <signal.h> +#include <pwd.h> +#include <sys/resource.h> +#include <sys/wait.h> +#include <fcntl.h> + +#include "dat.h" +#include "fns.h" +#include "error.h" + +enum +{ + Debug = 0 +}; + +/* + * os-specific devcmd support. + * this version should be reasonably portable across Unix systems. + */ +typedef struct Targ Targ; +struct Targ +{ + int fd[3]; /* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */ + char** args; + char* dir; + int pid; + int wfd; /* child writes errors that occur after the fork or on exec */ + int uid; + int gid; +}; + +extern int gidnobody; +extern int uidnobody; + +static int +childproc(Targ *t) +{ + int i, nfd; + + if(Debug) + print("devcmd: '%s'", t->args[0]); + + nfd = getdtablesize(); + for(i = 0; i < nfd; i++) + if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd) + close(i); + + dup2(t->fd[0], 0); + dup2(t->fd[1], 1); + dup2(t->fd[2], 2); + close(t->fd[0]); + close(t->fd[1]); + close(t->fd[2]); + + /* should have an auth file to do host-specific authorisation? */ + if(t->gid != -1){ + if(setgid(t->gid) < 0 && getegid() == 0){ + fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno)); + _exit(1); + } + } + + if(t->uid != -1){ + if(setuid(t->uid) < 0 && geteuid() == 0){ + fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno)); + _exit(1); + } + } + + if(t->dir != nil && chdir(t->dir) < 0){ + fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno)); + _exit(1); + } + + signal(SIGPIPE, SIG_DFL); + + execvp(t->args[0], t->args); + if(Debug) + print("execvp: %s\n",strerror(errno)); + fprint(t->wfd, "exec failed: %s", strerror(errno)); + + _exit(1); +} + +void* +oscmd(char **args, int nice, char *dir, int *fd) +{ + Targ *t; + int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid; + + t = mallocz(sizeof(*t), 1); + if(t == nil) + return nil; + + fd0[0] = fd0[1] = -1; + fd1[0] = fd1[1] = -1; + fd2[0] = fd2[1] = -1; + wfd[0] = wfd[1] = -1; + if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0) + goto Error; + if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0) /* close on exec to give end of file on success */ + goto Error; + + t->fd[0] = fd0[0]; + t->fd[1] = fd1[1]; + t->fd[2] = fd2[1]; + t->wfd = wfd[1]; + t->args = args; + t->dir = dir; + t->gid = up->env->gid; + if(t->gid == -1) + t->gid = gidnobody; + t->uid = up->env->uid; + if(t->uid == -1) + t->uid = uidnobody; + + signal(SIGCHLD, SIG_DFL); + switch(pid = fork()) { + case -1: + goto Error; + case 0: + setpgrp(); + if(nice) + oslopri(); + childproc(t); + _exit(1); + default: + t->pid = pid; + if(Debug) + print("cmd pid %d\n", t->pid); + break; + } + + close(fd0[0]); + close(fd1[1]); + close(fd2[1]); + close(wfd[1]); + + n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1); + close(wfd[0]); + if(n > 0){ + close(fd0[1]); + close(fd1[0]); + close(fd2[0]); + free(t); + up->genbuf[n] = 0; + if(Debug) + print("oscmd: bad exec: %q\n", up->genbuf); + error(up->genbuf); + return nil; + } + + fd[0] = fd0[1]; + fd[1] = fd1[0]; + fd[2] = fd2[0]; + return t; + +Error: + r = errno; + if(Debug) + print("oscmd: %q\n",strerror(r)); + close(fd0[0]); + close(fd0[1]); + close(fd1[0]); + close(fd1[1]); + close(fd2[0]); + close(fd2[1]); + close(wfd[0]); + close(wfd[1]); + error(strerror(r)); + return nil; +} + +int +oscmdkill(void *a) +{ + Targ *t = a; + + if(Debug) + print("kill: %d\n", t->pid); + return kill(-t->pid, SIGTERM); +} + +int +oscmdwait(void *a, char *buf, int n) +{ + Targ *t = a; + int s; + + if(waitpid(t->pid, &s, 0) == -1){ + if(Debug) + print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno)); + return -1; + } + if(WIFEXITED(s)){ + if(WEXITSTATUS(s) == 0) + return snprint(buf, n, "%d 0 0 0 ''", t->pid); + return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s)); + } + if(WIFSIGNALED(s)){ + if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL) + return snprint(buf, n, "%d 0 0 0 killed", t->pid); + return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s)); + } + return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s); +} + +void +oscmdfree(void *a) +{ + free(a); +} diff --git a/emu/Hp/devaudio.c b/emu/Hp/devaudio.c new file mode 100644 index 00000000..c26143e3 --- /dev/null +++ b/emu/Hp/devaudio.c @@ -0,0 +1,506 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + +#include <sys/audio.h> + + +enum{ + Qdir, + Qaudio, + Qaudioctl, /* deprecated */ + Qvolume, + + Fmono = 1, + Fin = 2, + Fout = 4, + Fhearn = 8, + + Vaudio = 0, + Vsynth, + Vcd, + Vline, + Vmic, + Vspeaker, + Vtreb, + Vbass, + Vspeed, + Vbits, + Vchans, + Venc, + Nvol, + + Epcm = 0, + Eulaw, + Ealaw, + Nenc, + + Speed = 44100, + Ncmd = 50, /* max volume command words */ +}; +Dirtab audiotab[]={ + "audio", {Qaudio, 0}, 0, 0666, + "audioctl", {Qaudioctl, 0}, 0, 0666, + "volume", {Qvolume, 0}, 0, 0666, +}; + +typedef struct Audiodev Audiodev; + +struct Audiodev { + int fd; + int swab; + int repl1; + int repl2; +}; + +static struct +{ + QLock; /* XXX maybe we should use this guy! */ + int rivol[Nvol]; /* right/left input/output volumes */ + int livol[Nvol]; + int rovol[Nvol]; + int lovol[Nvol]; +} audio; + +static struct +{ + char* name; + int flag; + int ilval; /* initial values */ + int irval; +} volumes[] = +{ +/*[Vaudio]*/ "audio", Fout, 50, 50, +/*[Vsynth]*/ "synth", Fin|Fout, 0, 0, +/*[Vcd]*/ "cd", Fin|Fout, 0, 0, +/*[Vline]*/ "line", Fin|Fout, 0, 0, +/*[Vmic]*/ "mic", Fin|Fout|Fmono, 0, 0, +/*[Vspeaker]*/ "speaker", Fout|Fmono, 0, 0, + +/*[Vtreb]*/ "treb", Fout, 50, 50, +/*[Vbass]*/ "bass", Fout, 50, 50, + +/*[Vspeed]*/ "speed", Fin|Fout|Fmono, Speed, Speed, +/*[Vbits]*/ "bits", Fin|Fout|Fmono|Fhearn, 16, 16, +/*[Vchans]*/ "chans", Fin|Fout|Fmono|Fhearn, 2, 2, +/*[Venc]*/ "enc", Fin|Fout|Fmono|Fhearn, Epcm, Epcm, + 0 +}; + +static char *encname[] = +{ +/*Epcm*/ "pcm", +/*Eulaw*/ "ulaw", +/*Ealaw*/ "alaw", +}; + +static char Evolume[] = "illegal volume specifier"; + +static void +resetlevel(void) +{ + int i; + + for(i=0; volumes[i].name; i++) { + audio.lovol[i] = volumes[i].ilval; + audio.rovol[i] = volumes[i].irval; + audio.livol[i] = volumes[i].ilval; + audio.rivol[i] = volumes[i].irval; + } +} + +/* Start OS-dependant code */ +static int +doioctl(int fd, int whim, void *data, char *what) +{ + char ebuf[ERRMAX]; + int r, n; + + osenter(); + r = ioctl(fd, whim, data); + osleave(); + if (r < 0) { + n = snprint(ebuf, ERRMAX, "ioctl %s: ", what); + oserrstr(ebuf+n, sizeof ebuf-n); + error(ebuf); + } + return r; +} + +static void +setlevels(Audiodev *a) +{ + struct audio_describe au; + struct audio_gain gain; + int i, x; + +/* XXX todo: simulate it with a data conversion routine (could also do swab...) */ + if (audio.lovol[Venc] == Epcm && audio.lovol[Vbits] != 16) { + audio.lovol[Vbits] = 16; + error("pcm must be 16 bits"); + } + if (audio.lovol[Vchans] != 1 && audio.lovol[Vchans] != 2) { + audio.lovol[Vchans] = 1; + error("bad number of channels"); + } + + doioctl(a->fd, AUDIO_DESCRIBE, &au, "describe"); + doioctl(a->fd, AUDIO_SET_SAMPLE_RATE, audio.lovol[Vspeed], "rate"); /* what if input != output??? */ + doioctl(a->fd, AUDIO_SET_CHANNELS, audio.lovol[Vchans], "channels"); + + switch (audio.lovol[Venc]) { + default: + case Epcm: + x = AUDIO_FORMAT_LINEAR16BIT; + break; + case Eulaw: + x = AUDIO_FORMAT_ULAW; + break; + case Ealaw: + x = AUDIO_FORMAT_ALAW; + break; + } + doioctl(a->fd, AUDIO_SET_DATA_FORMAT, x, "set format"); + + x = 0; + if (audio.lovol[Vspeaker] != 0 || audio.rovol[Vspeaker] != 0) + x |= AUDIO_OUT_SPEAKER; + if (audio.lovol[Vaudio] != 0 || audio.rovol[Vaudio] != 0) + x |= AUDIO_OUT_HEADPHONE; + if (audio.lovol[Vline] != 0 || audio.rovol[Vline] != 0) + x |= AUDIO_OUT_LINE; + doioctl(a->fd, AUDIO_SET_OUTPUT, x, "set output"); + + x = 0; + if (audio.livol[Vline] != 0 || audio.rivol[Vline] != 0) + x |= AUDIO_IN_LINE; + if (audio.livol[Vmic] != 0 || audio.rivol[Vmic] != 0 || x == 0) /* must set at least one */ + x |= AUDIO_IN_MIKE; + doioctl(a->fd, AUDIO_SET_INPUT, x, "set input"); + +/* XXX todo: get the gains right. should scale 0-100 into min-max (as in struct audio_describe au) */ +/* doioctl(a->fd, AUDIO_GET_GAINS, &gain, "get gains"); */ + gain.channel_mask = AUDIO_CHANNEL_LEFT|AUDIO_CHANNEL_RIGHT; + for (i = 0; i < 2; i++) { + gain.cgain[i].receive_gain = au.min_receive_gain; + gain.cgain[i].monitor_gain = au.min_monitor_gain; + gain.cgain[i].transmit_gain = au.max_transmit_gain; + } + doioctl(a->fd, AUDIO_SET_GAINS, &gain, "set gains"); +} + +static char * +audiofname(int isctl) +{ + if (isctl) + return "/dev/audioCtl"; + else + return "/dev/audio"; +} + +static void +audioswab(uchar *p, int n) +{ + int x; + + /* XXX slow; should check for 16bit mode; should be combined with format conversion; etc */ + while (n >= 2) { + x = p[0]; + p[0] = p[1]; + p[1] = x; + p +=2; + n -=2; + } +} +/* End OS-dependant code */ + +static void +audioinit(void) +{ + resetlevel(); +} + +static Chan* +audioattach(char* spec) +{ + return devattach('A', spec); +} + +static int +audiowalk(Chan* c, char* name) +{ + return devwalk(c, name, audiotab, nelem(audiotab), devgen); +} + +static void +audiostat(Chan* c, char* db) +{ + devstat(c, db, audiotab, nelem(audiotab), devgen); +} + +static Chan* +audioopen(Chan *c, int omode) +{ + Audiodev *a; + long path; + char ebuf[ERRMAX]; + + path = c->qid.path & ~CHDIR; + if (path != Qdir){ +/* XXX Irix portability? (multiple opens -- how to match ctl with data???) */ + a = malloc(sizeof(Audiodev)); + if(a == nil) + error(Enomem); + if (waserror()) { + free(a); + nexterror(); + } + a->fd = open(audiofname(path != Qaudio), omode&7); + if(a->fd < 0) + oserror(); + if (path == Qaudio) + setlevels(a); + c->aux = a; + poperror(); + } + return devopen(c, omode, audiotab, nelem(audiotab), devgen); +} + +static void +audioclose(Chan* c) +{ + Audiodev *a; + + a = c->aux; + if (a != nil) { + close(a->fd); + free(a); + } +} + +static long +audioread(Chan* c, void *ua, long n, vlong offset) +{ + Audiodev *a; + char buf[300], ebuf[ERRMAX]; + int liv, riv, lov, rov; + int j, m; + long path; + + a = c->aux; + path = (c->qid.path & ~CHDIR); + switch(path){ + case Qdir: + return devdirread(c, a, n, audiotab, nelem(audiotab), devgen); + case Qaudio: + osenter(); + n = read(a->fd, ua, n); + osleave(); + if (n < 0) + oserror(); + audioswab(ua, n); /* XXX what if n is odd? also, only if 16 bit... must fix portability */ + break; + case Qaudioctl: + case Qvolume: + j = 0; + buf[0] = 0; + for(m=0; volumes[m].name; m++){ + if ((volumes[m].flag & Fhearn) && path == Qvolume) + continue; + liv = audio.livol[m]; + riv = audio.rivol[m]; + lov = audio.lovol[m]; + rov = audio.rovol[m]; + j += snprint(buf+j, sizeof(buf)-j, "%s", volumes[m].name); + if(m == Venc) + j += snprint(buf+j, sizeof(buf)-j, " %s", encname[lov]); + else if((volumes[m].flag & Fmono) || liv==riv && lov==rov){ + if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov) + j += snprint(buf+j, sizeof(buf)-j, " %d", liv); + else{ + if(volumes[m].flag & Fin) + j += snprint(buf+j, sizeof(buf)-j, " in %d", liv); + if(volumes[m].flag & Fout) + j += snprint(buf+j, sizeof(buf)-j, " out %d", lov); + } + }else{ + if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov && riv==rov) + j += snprint(buf+j, sizeof(buf)-j, " left %d right %d", + liv, riv); + else{ + if(volumes[m].flag & Fin) + j += snprint(buf+j, sizeof(buf)-j, " in left %d right %d", + liv, riv); + if(volumes[m].flag & Fout) + j += snprint(buf+j, sizeof(buf)-j, " out left %d right %d", + lov, rov); + } + } + j += snprint(buf+j, sizeof(buf)-j, "\n"); + } + return readstr(offset, ua, n, buf); + default: + n=0; + break; + } + return n; +} + +static long +audiowrite(Chan* c, char *ua, long n, vlong offset) +{ + Audiodev *a; + long m, n0; + int i, nf, v, left, right, in, out; + char buf[255], *field[Ncmd], ebuf[ERRMAX], *p; + + a = c->aux; + switch(c->qid.path & ~CHDIR){ + case Qaudio: + n &= ~1; + audioswab(ua, n); /* XXX VERY BAD BUG; THIS CHANGES THE CALLER'S DATA */ + osenter(); + n = write(a->fd, ua, n); + osleave(); + if (n < 0) + oserror(); + break; + case Qaudioctl: + case Qvolume: + v = Vaudio; + left = 1; + right = 1; + in = 1; + out = 1; + if(n > sizeof(buf)-1) + n = sizeof(buf)-1; + memmove(buf, ua, n); + buf[n] = '\0'; + + nf = getfields(buf, field, Ncmd, 1, " \t\n"); + for(i = 0; i < nf; i++){ + /* + * a number is volume + */ + if(field[i][0] >= '0' && field[i][0] <= '9') { + m = strtoul(field[i], &p, 10); + if (p != nil && *p == 'k') + m *= 1000; + if(left && out) + audio.lovol[v] = m; + if(left && in) + audio.livol[v] = m; + if(right && out) + audio.rovol[v] = m; + if(right && in) + audio.rivol[v] = m; + setlevels(a); + goto cont0; + } + + for(m=0; volumes[m].name; m++) { + if(strcmp(field[i], volumes[m].name) == 0) { + v = m; + in = 1; + out = 1; + left = 1; + right = 1; + goto cont0; + } + } + + if(strcmp(field[i], "reset") == 0) { + resetlevel(); + setlevels(a); + goto cont0; + } + if(strcmp(field[i], "in") == 0) { + in = 1; + out = 0; + goto cont0; + } + if(strcmp(field[i], "out") == 0) { + in = 0; + out = 1; + goto cont0; + } + if(strcmp(field[i], "left") == 0) { + left = 1; + right = 0; + goto cont0; + } + if(strcmp(field[i], "right") == 0) { + left = 0; + right = 1; + goto cont0; + } + if(strcmp(field[i], "rate") == 0) { + v = Vspeed; + in = 1; + out = 1; + left = 1; + right = 1; + goto cont0; + } + if(strcmp(field[i], "chan") == 0) { /* XXX egregious backward compatibility hack */ + v = Vchans; + in = 1; + out = 1; + left = 1; + right = 1; + goto cont0; + } + if(v == Venc) { + if (strcmp(field[i], "pcm") == 0) { + audio.lovol[v] = Epcm; + goto cont0; + } + if (strcmp(field[i], "ulaw") == 0) { + audio.lovol[v] = Eulaw; + goto cont0; + } + if (strcmp(field[i], "alaw") == 0) { + audio.lovol[v] = Ealaw; + goto cont0; + } + } + if(v == Vchans) { + if (strcmp(field[i], "mono") == 0) { + audio.lovol[v] = 1; + goto cont0; + } + if (strcmp(field[i], "stereo") == 0) { + audio.lovol[v] = 2; + goto cont0; + } + } + error(Evolume); + break; + cont0:; + } + break; + default: + error(Ebadusefd); + } + return n; +} + +Dev audiodevtab = { + 'A', + "audio", + + audioinit, + audioattach, + devclone, + audiowalk, + audiostat, + audioopen, + devcreate, + audioclose, + audioread, + devbread, + audiowrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/emu/Hp/devfs.c b/emu/Hp/devfs.c new file mode 100644 index 00000000..9017c3fc --- /dev/null +++ b/emu/Hp/devfs.c @@ -0,0 +1 @@ +#include "devfs-posix.c" diff --git a/emu/Hp/emu b/emu/Hp/emu new file mode 100644 index 00000000..806c642c --- /dev/null +++ b/emu/Hp/emu @@ -0,0 +1,107 @@ +dev + root + cons + env + mnt + pipe + prog + prof + srv + dup + ssl + cap + fs + cmd cmd + indir + + draw win-x11a + pointer + snarf + + ip ipif-posix ipaux +# eia +# audio audio + audio + mem + +lib + interp + tk + freetype + math + draw + + memlayer + memdraw + keyring + sec + mp + + 9 + +link + +mod + sys + draw + + tk + math + srv srv + keyring + loader + freetype + +port + alloc + cache + chan + dev + dial + dis + discall + env + error + errstr + exception + exportfs + inferno + latin1 + main + parse + pgrp + print + proc + qio + random + sysfile + uqid + +code + +init + emuinit + +root + /dev / + /fd / + /prog / + /net / + /net.alt / + /chan / + /nvfs / + /env / +# /chan +# /dev +# /dis +# /env +# /n +# /net +# /nvfs / +# /prog +# /icons +# /osinit.dis +# /dis/emuinit.dis +# /dis/lib/auth.dis +# /dis/lib/ssl.dis +# /n/local / diff --git a/emu/Hp/mkfile b/emu/Hp/mkfile new file mode 100644 index 00000000..f332db7a --- /dev/null +++ b/emu/Hp/mkfile @@ -0,0 +1,49 @@ +SYSTARG=Hp +OBJTYPE=s800 +<../../mkconfig +SYSTARG=Hp +OBJTYPE=s800 + +#Configurable parameters + +CONF=emu #default configuration +CONFLIST=emu +CLEANCONFLIST= + +INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed + +#end configurable parameters + +<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system + +<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS + +OBJ=\ + asm-$OBJTYPE.$O\ + os.$O\ + $CONF.root.$O\ + lock.$O\ + $DEVS\ + $PORT\ + +HFILES=\ + +CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS +SYSLIBS= -lm -lX11 -lcma +KERNDATE=`{$NDATE} + +default:V: $O.$CONF + +<../port/portmkfile + +$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBFILES + $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c + $LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS + +install:V: $O.$CONF + cp $O.$CONF $INSTALLDIR/$CONF + +devaudio.$O: devaudio.c + $CC $CFLAGS devaudio.c + +devfs.$O: ../port/devfs-posix.c diff --git a/emu/Hp/mkfile-Hp b/emu/Hp/mkfile-Hp new file mode 100644 index 00000000..37c625db --- /dev/null +++ b/emu/Hp/mkfile-Hp @@ -0,0 +1,13 @@ +# +# architecture-dependent files for Hp +# + +TARGFILES=asm-Hp-s800.$O\ + devfs-posix.$O\ + devip.$O\ + ipif-posix.$O\ + os-Hp.$O\ + win-x11.$O\ + devaudio-Hp.$O\ + srv.$O\ + lock.$O\ diff --git a/emu/Hp/os.c b/emu/Hp/os.c new file mode 100644 index 00000000..a371ac50 --- /dev/null +++ b/emu/Hp/os.c @@ -0,0 +1,591 @@ +#include <pthread.h> +#include <signal.h> +#include "dat.h" +#include "fns.h" +#include "error.h" +#include <sys/socket.h> +#include <time.h> +#include <sys/time.h> +#include <termios.h> +#include <pwd.h> +#include <errno.h> + +enum +{ + BUNCHES = 5000, + DELETE = 0x7F +}; +char *hosttype = "Hp"; + + +static pthread_key_t prdakey; + +extern int dflag; + +Lock mulock = {1, 0}; + +ulong +_tas(ulong *l) +{ + ulong v; + + while(!(mutexlock(&mulock))) + pthread_yield(); + + v = *l; + if(v == 0) + *l = 1; + mulock.key = 1; + return v; +} + +static ulong erendezvous(void*, ulong); + + +void +osblock(void) +{ + erendezvous(up, 0); +} + +void +osready(Proc *p) +{ + erendezvous(p, 0); +} + + +void +pexit(char *msg, int t) +{ + Osenv *e; + + lock(&procs.l); + if(up->prev) + up->prev->next = up->next; + else + procs.head = up->next; + + if(up->next) + up->next->prev = up->prev; + else + procs.tail = up->prev; + unlock(&procs.l); + + e = up->env; + if(e != nil) { + closefgrp(e->fgrp); + closepgrp(e->pgrp); + closeegrp(e->egrp); + closesigs(e->sigs); + } + free(up->prog); + free(up); + pthread_exit(0); +} + +void +trapBUS(int signo, siginfo_t *info, void *context) +{ + if(info) + print("trapBUS: signo: %d code: %d addr: %lx\n", + info->si_signo, info->si_code, info->si_addr); + else + print("trapBUS: no info\n"); + disfault(nil, "Bus error"); +} + +void +trapUSR1(void) +{ + int intwait; + + intwait = up->intwait; + up->intwait = 0; /* clear it to let proc continue in osleave */ + + if(up->type != Interp) /* Used to unblock pending I/O */ + return; + + if(intwait == 0) /* Not posted so it's a sync error */ + disfault(nil, Eintr); /* Should never happen */ +} + +void +trapILL(void) +{ + disfault(nil, "Illegal instruction"); +} + +void +trapSEGV(void) +{ + disfault(nil, "Segmentation violation"); +} + +sigset_t set; +setsigs() +{ + struct sigaction act; + + memset(&act, 0 , sizeof(act)); + sigemptyset(&set); + + act.sa_handler=SIG_IGN; + if(sigaction(SIGPIPE, &act, nil)) + panic("can't ignore sig pipe"); + + if(sigaddset(&set,SIGUSR1)== -1) + panic("sigaddset SIGUSR1"); + + if(sigaddset(&set,SIGUSR2)== -1) + panic("sigaddset SIGUSR2"); + + /* For the correct functioning of devcmd in the + * face of exiting slaves + */ + if(sflag == 0) { + act.sa_handler=trapBUS; + act.sa_flags|=SA_SIGINFO; + if(sigaction(SIGBUS, &act, nil)) + panic("sigaction SIGBUS"); + act.sa_handler=trapILL; + if(sigaction(SIGILL, &act, nil)) + panic("sigaction SIGILL"); + act.sa_handler=trapSEGV; + if(sigaction(SIGSEGV, &act, nil)) + panic("sigaction SIGSEGV"); + if(sigaddset(&set,SIGINT)== -1) + panic("sigaddset"); + } + if(sigprocmask(SIG_BLOCK,&set,nil)!= 0) + panic("sigprocmask"); +} + +static void * +tramp(void *v) +{ + struct Proc *Up; + pthread_t thread; + struct sigaction oldact; + + setsigs(); + if(sigaction(SIGBUS, nil, &oldact)) + panic("sigaction failed"); + if(oldact.sa_handler!=trapBUS && sflag==0) + panic("3rd old act sa_handler"); + + if(pthread_setspecific(prdakey,v)) { + print("set specific data failed in tramp\n"); + pthread_exit(0); + } + Up = v; + thread = pthread_self(); + Up->sigid = cma_thread_get_unique(&thread); + /* attempt to catch signals again */ + setsigs(); + Up->func(Up->arg); + pexit("", 0); +} + +pthread_t active_threads[BUNCHES]; /* this should be more than enuf */ + +int +kproc(char *name, void (*func)(void*), void *arg, int flags) +{ + pthread_t thread; + pthread_attr_t attr; + int id; + Proc *p; + Pgrp *pg; + Fgrp *fg; + Egrp *eg; + struct sigaction oldact; + + p = newproc(); + + if(flags & KPDUPPG) { + pg = up->env->pgrp; + incref(&pg->r); + p->env->pgrp = pg; + } + if(flags & KPDUPFDG) { + fg = up->env->fgrp; + incref(&fg->r); + p->env->fgrp = fg; + } + if(flags & KPDUPENVG) { + eg = up->env->egrp; + incref(&eg->r); + p->env->egrp = eg; + } + + p->env->uid = up->env->uid; + p->env->gid = up->env->gid; + kstrdup(&p->env->user, up->env->user); +; + + strcpy(p->text, name); + + p->func = func; + p->arg = arg; + + lock(&procs.l); + if(procs.tail != nil) { + p->prev = procs.tail; + procs.tail->next = p; + } + else { + procs.head = p; + p->prev = nil; + } + procs.tail = p; + unlock(&procs.l); + if((pthread_attr_create(&attr))== -1) + panic("pthread_attr_create failed"); + + errno=0; + pthread_attr_setsched(&attr,SCHED_OTHER); + if(errno) + panic("pthread_attr_setsched failed"); + + if(pthread_create(&thread, attr, tramp, p)) + panic("thr_create failed\n"); + if(sigaction(SIGBUS, nil, &oldact)) + panic("sigaction failed"); + if(oldact.sa_handler!=trapBUS && sflag == 0) + panic("2nd old act sa_handler"); + + if((id=cma_thread_get_unique(&thread))>=BUNCHES) + panic("id too big"); + active_threads[id]=thread; + return id; +} + +void +oshostintr(Proc *p) +{ + pthread_cancel(active_threads[p->sigid]); +} + +void +oslongjmp(void *regs, osjmpbuf env, int val) +{ + USED(regs); + siglongjmp(env, val); +} + +struct termios tinit; + +static void +termset(void) +{ + struct termios t; + + tcgetattr(0, &t); + tinit = t; + t.c_lflag &= ~(ICANON|ECHO|ISIG); + t.c_cc[VMIN] = 1; + t.c_cc[VTIME] = 0; + tcsetattr(0, TCSANOW, &t); +} + +static void +termrestore(void) +{ + tcsetattr(0, TCSANOW, &tinit); +} + +void +cleanexit(int x) +{ + USED(x); + + if(up->intwait) { + up->intwait = 0; + return; + } + + if(dflag == 0) + termrestore(); + + kill(0, SIGKILL); + exit(0); +} + +void +osreboot(char *file, char **argv) +{ + if(dflag == 0) + termrestore(); + execvp(file, argv); + panic("reboot failure"); +} + +int gidnobody= -1, uidnobody= -1; + +void +getnobody() +{ + struct passwd *pwd; + + if(pwd = getpwnam("nobody")) { + uidnobody = pwd->pw_uid; + gidnobody = pwd->pw_gid; + } +} + +static pthread_mutex_t rendezvouslock; + +void +libinit(char *imod) +{ + struct passwd *pw; + struct Proc *Up; + struct sigaction oldact; + int ii; + int retval; + int *pidptr; + char sys[64]; + + cma_init(); + setsid(); + /* mulock.key = 1; */ /* initialize to unlock */ + if(pthread_mutex_init(&rendezvouslock,pthread_mutexattr_default)) + panic("pthread_mutex_init"); + + gethostname(sys, sizeof(sys)); + kstrdup(&ossysname, sys); + getnobody(); + + if(dflag == 0) + termset(); + + setsigs(); + if(sigaction(SIGBUS, nil, &oldact)) { + panic("sigaction failed"); + } + if(oldact.sa_handler!=trapBUS && sflag == 0) + panic("1st old act sa_handler"); + + if(pthread_keycreate(&prdakey,NULL)) + print("keycreate failed\n"); + + Up = newproc(); + if(pthread_setspecific(prdakey,Up)) + panic("set specific thread data failed\n"); + + pw = getpwuid(getuid()); + if(pw != nil) + kstrdup(&eve, pw->pw_name); + else + print("cannot getpwuid\n"); + + up->env->uid = getuid(); + up->env->gid = getgid(); + emuinit(imod); +} + +int +readkbd(void) +{ + int n; + char buf[1]; + + n = read(0, buf, sizeof(buf)); + if(n != 1) { + print("keyboard close (n=%d, %s)\n", n, strerror(errno)); + pexit("keyboard thread", 0); + } + + switch(buf[0]) { + case '\r': + buf[0] = '\n'; + break; + case DELETE: + cleanexit(0); + break; + } + return buf[0]; +} + +enum +{ + NHLOG = 7, + NHASH = (1<<NHLOG) +}; + +typedef struct Tag Tag; +struct Tag +{ + void* tag; + ulong val; + int pid; + Tag* hash; + Tag* free; + pthread_cond_t cv; +}; + +static Tag* ht[NHASH]; +static Tag* ft; + +static ulong +erendezvous(void *tag, ulong value) +{ + int h; + ulong rval; + Tag *t, *f, **l; + int ii=0; + + h = (ulong)tag & (NHASH-1); + + if(pthread_mutex_lock(&rendezvouslock)) + panic("pthread_mutex_lock"); + + l = &ht[h]; + for(t = *l; t; t = t->hash) { + if(t->tag == tag) { + rval = t->val; + t->val = value; + t->tag = 0; + if(pthread_mutex_unlock(&rendezvouslock)) + panic("pthread_mutex_unlock"); + if(pthread_cond_signal(&(t->cv))) + panic("pthread_cond_signal"); + return rval; + } + } + + t = ft; + if(t == 0) { + t = malloc(sizeof(Tag)); + if(t == 0) + panic("rendezvous: no memory"); + if(pthread_cond_init(&(t->cv),pthread_condattr_default)) { + print("pthread_cond_init (errno: %s) \n", strerror(errno)); + panic("pthread_cond_init"); + } + } else + ft = t->free; + + t->tag = tag; + t->val = value; + t->hash = *l; + *l = t; + + while(t->tag) + pthread_cond_wait(&(t->cv),&rendezvouslock); + + rval = t->val; + for(f = *l; f; f = f->hash){ + if(f == t) { + *l = f->hash; + break; + } + l = &f->hash; + } + t->free = ft; + ft = t; + if(pthread_mutex_unlock(&rendezvouslock)) + panic("pthread_mutex_unlock"); + + return rval; +} + + +/* + * Return an abitrary millisecond clock time + */ +long +osmillisec(void) +{ + static long sec0 = 0, usec0; + struct timeval t; + + if(gettimeofday(&t,(struct timezone*)0)<0) + return(0); + if(sec0==0) { + sec0 = t.tv_sec; + usec0 = t.tv_usec; + } + return((t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000); +} + +/* + * Return the time since the epoch in nanoseconds and microseconds + * The epoch is defined at 1 Jan 1970 + */ +vlong +osnsec(void) +{ + struct timeval t; + + gettimeofday(&t, nil); + return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000; +} + +vlong +osusectime(void) +{ + struct timeval t; + + gettimeofday(&t, nil); + return (vlong)t.tv_sec * 1000000 + t.tv_usec; +} + + +int +osmillisleep(ulong milsec) +{ + struct timespec time; + time.tv_sec = milsec/1000; + time.tv_nsec= (milsec%1000)*1000000; + if(pthread_delay_np(&time)== -1) + ; /* might be interrupted */ + return 0; +} + +Proc * +getup(void) +{ + void *vp; + + vp=nil; + pthread_getspecific(prdakey,&vp); + return(vp); +} + +ulong +getcallerpc(void *arg) +{ + return 0 ; +} + +void +osyield(void) +{ + pthread_yield(); +} + +void +ospause(void) +{ + int s; + + for(;;) { + switch(s=sigwait(&set)) { + case SIGUSR1: + trapUSR1(); + case SIGINT: + cleanexit(0); + default: + print("signal: %d %s\n",s, strerror(errno)); + panic("sigwait"); + } + } +} + +void +oslopri(void) +{ + /* TO DO */ +} diff --git a/emu/Irix/NOTE b/emu/Irix/NOTE new file mode 100644 index 00000000..d317c90e --- /dev/null +++ b/emu/Irix/NOTE @@ -0,0 +1 @@ +for some strange reason, it lacks deveia! diff --git a/emu/Irix/asm-mips.s b/emu/Irix/asm-mips.s new file mode 100644 index 00000000..394baa36 --- /dev/null +++ b/emu/Irix/asm-mips.s @@ -0,0 +1,31 @@ +#include <sys/regdef.h> +#include <sys/asm.h> + +LEAF(FPsave) + cfc1 t0, $31 + sw t0, 0(a0) /* a0 is argument */ + j $31 + END(FPsave) + +LEAF(FPrestore) + lw t0, 0(a0) /* a0 is argument */ + ctc1 t0, $31 + j $31 + END(FPrestore) + + +/* + * lock from r4000 book + */ +LEAF(_tas) + .set noreorder +1: + ll v0,0(a0) /* a0 is argument */ + or t1, v0, 1 + sc t1,0(a0) + beq t1,zero,1b + nop + j $31 /* lock held */ + nop + .set reorder + END(_tas) diff --git a/emu/Irix/cmd.c b/emu/Irix/cmd.c new file mode 100644 index 00000000..bff1f03b --- /dev/null +++ b/emu/Irix/cmd.c @@ -0,0 +1,213 @@ +#include <sys/types.h> +#include <signal.h> +#include <pwd.h> +#include <sys/resource.h> +#include <sys/wait.h> +#include <fcntl.h> + +#include "dat.h" +#include "fns.h" +#include "error.h" + +enum +{ + Debug = 0 +}; + +/* + * os-specific devcmd support. + * this version should be reasonably portable across Unix systems. + */ +typedef struct Targ Targ; +struct Targ +{ + int fd[3]; /* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */ + char** args; + char* dir; + int pid; + int wfd; /* child writes errors that occur after the fork or on exec */ + int uid; + int gid; +}; + +extern int gidnobody; +extern int uidnobody; + +static int +childproc(Targ *t) +{ + int i, nfd; + + if(Debug) + print("devcmd: '%s'", t->args[0]); + + nfd = getdtablesize(); + for(i = 0; i < nfd; i++) + if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd) + close(i); + + dup2(t->fd[0], 0); + dup2(t->fd[1], 1); + dup2(t->fd[2], 2); + close(t->fd[0]); + close(t->fd[1]); + close(t->fd[2]); + + /* should have an auth file to do host-specific authorisation? */ + if(t->gid != -1){ + if(setgid(t->gid) < 0 && getegid() == 0){ + fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno)); + _exit(1); + } + } + + if(t->uid != -1){ + if(setuid(t->uid) < 0 && geteuid() == 0){ + fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno)); + _exit(1); + } + } + + if(t->dir != nil && chdir(t->dir) < 0){ + fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno)); + _exit(1); + } + + signal(SIGPIPE, SIG_DFL); + + execvp(t->args[0], t->args); + if(Debug) + print("execvp: %s\n",strerror(errno)); + fprint(t->wfd, "exec failed: %s", strerror(errno)); + + _exit(1); +} + +void* +oscmd(char **args, int nice, char *dir, int *fd) +{ + Targ *t; + int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid; + + t = mallocz(sizeof(*t), 1); + if(t == nil) + return nil; + + fd0[0] = fd0[1] = -1; + fd1[0] = fd1[1] = -1; + fd2[0] = fd2[1] = -1; + wfd[0] = wfd[1] = -1; + if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0) + goto Error; + if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0) /* close on exec to give end of file on success */ + goto Error; + + t->fd[0] = fd0[0]; + t->fd[1] = fd1[1]; + t->fd[2] = fd2[1]; + t->wfd = wfd[1]; + t->args = args; + t->dir = dir; + t->gid = up->env->gid; + if(t->gid == -1) + t->gid = gidnobody; + t->uid = up->env->uid; + if(t->uid == -1) + t->uid = uidnobody; + + signal(SIGCHLD, SIG_DFL); + switch(pid = fork()) { + case -1: + goto Error; + case 0: + setpgrp(); + if(nice) + oslopri(); + childproc(t); + _exit(1); + default: + t->pid = pid; + if(Debug) + print("cmd pid %d\n", t->pid); + break; + } + + close(fd0[0]); + close(fd1[1]); + close(fd2[1]); + close(wfd[1]); + + n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1); + close(wfd[0]); + if(n > 0){ + close(fd0[1]); + close(fd1[0]); + close(fd2[0]); + free(t); + up->genbuf[n] = 0; + if(Debug) + print("oscmd: bad exec: %q\n", up->genbuf); + error(up->genbuf); + return nil; + } + + fd[0] = fd0[1]; + fd[1] = fd1[0]; + fd[2] = fd2[0]; + return t; + +Error: + r = errno; + if(Debug) + print("oscmd: %q\n",strerror(r)); + close(fd0[0]); + close(fd0[1]); + close(fd1[0]); + close(fd1[1]); + close(fd2[0]); + close(fd2[1]); + close(wfd[0]); + close(wfd[1]); + error(strerror(r)); + return nil; +} + +int +oscmdkill(void *a) +{ + Targ *t = a; + + if(Debug) + print("kill: %d\n", t->pid); + return kill(-t->pid, SIGTERM); +} + +int +oscmdwait(void *a, char *buf, int n) +{ + Targ *t = a; + int s; + + if(waitpid(t->pid, &s, 0) == -1){ + if(Debug) + print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno)); + return -1; + } + if(WIFEXITED(s)){ + if(WEXITSTATUS(s) == 0) + return snprint(buf, n, "%d 0 0 0 ''", t->pid); + return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s)); + } + if(WIFSIGNALED(s)){ + if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL) + return snprint(buf, n, "%d 0 0 0 killed", t->pid); + return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s)); + } + return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s); +} + +void +oscmdfree(void *a) +{ + free(a); +} diff --git a/emu/Irix/devfs.c b/emu/Irix/devfs.c new file mode 100644 index 00000000..9017c3fc --- /dev/null +++ b/emu/Irix/devfs.c @@ -0,0 +1 @@ +#include "devfs-posix.c" diff --git a/emu/Irix/emu b/emu/Irix/emu new file mode 100644 index 00000000..e5d92554 --- /dev/null +++ b/emu/Irix/emu @@ -0,0 +1,106 @@ +dev + root + cons + env + mnt + pipe + prog + prof + srv + dup + ssl + cap + fs + cmd cmd + indir + + draw win-x11a + pointer + snarf + + ip ipif-posix ipaux +# eia +# audio audio + mem + +lib + interp + tk + freetype + math + draw + + memlayer + memdraw + keyring + sec + mp + + 9 + +link + +mod + sys + draw + + tk + math + srv srv + keyring + loader + freetype + +port + alloc + cache + chan + dev + dial + dis + discall + env + error + errstr + exception + exportfs + inferno + latin1 + main + parse + pgrp + print + proc + qio + random + sysfile + uqid + +code + +init + emuinit + +root + /dev / + /fd / + /prog / + /net / + /net.alt / + /chan / + /nvfs / + /env / +# /chan +# /dev +# /dis +# /env +# /n +# /net +# /nvfs / +# /prog +# /icons +# /osinit.dis +# /dis/emuinit.dis +# /dis/lib/auth.dis +# /dis/lib/ssl.dis +# /n/local / diff --git a/emu/Irix/mkfile b/emu/Irix/mkfile new file mode 100644 index 00000000..51825866 --- /dev/null +++ b/emu/Irix/mkfile @@ -0,0 +1,49 @@ +SYSTARG=Irix +OBJTYPE=mips +<../../mkconfig +SYSTARG=Irix +OBJTYPE=mips + +#Configurable parameters + +CONF=emu #default configuration +CONFLIST=emu +CLEANCONFLIST= + +INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed + +#end configurable parameters + +<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system + +<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS + +OBJ=\ + asm-$OBJTYPE.$O\ + os.$O\ + $CONF.root.$O\ + lock.$O\ + $DEVS\ + $PORT\ + +LIBNAMES=${LIBS:%=lib%.a} +#libs=${LIBS:%=$ROOT/$OBJDIR/lib/lib%.a} + +HFILES=\ + +CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS +SYSLIBS= -lfpe -lm -lX11 -lXext +KERNDATE=`{$NDATE} + +default:V: $O.$CONF + +$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBNAMES + $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c + $LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS + +install:V: $O.$CONF + cp $O.$CONF $INSTALLDIR/$CONF + +<../port/portmkfile + +devfs.$O: ../port/devfs-posix.c diff --git a/emu/Irix/mkfile-Irix b/emu/Irix/mkfile-Irix new file mode 100644 index 00000000..1d1bbdc3 --- /dev/null +++ b/emu/Irix/mkfile-Irix @@ -0,0 +1,14 @@ +# +# architecture-dependent files for IRIX +# + +TARGFILES=asm-Irix.$O\ + devfs-posix.$O\ + devip.$O\ + ipif-posix.$O\ + os-Irix.$O\ + win-x11.$O\ + srv.$O\ + lock.$O\ + +SYSLIBS= -lfpe -lm -lX11 diff --git a/emu/Irix/os.c b/emu/Irix/os.c new file mode 100644 index 00000000..42bcf99b --- /dev/null +++ b/emu/Irix/os.c @@ -0,0 +1,477 @@ +/* Link with -lfpe. See man pages for fpc + * and /usr/include/sigfpe.h, sys/fpu.h. + */ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include <time.h> +#include <ulocks.h> +#include <termios.h> +#include <sigfpe.h> +#include <sys/prctl.h> +#include <sys/fpu.h> +#include <sys/cachectl.h> +#undef _POSIX_SOURCE /* SGI incompetence */ +#include <signal.h> +#define _BSD_TIME +/* for gettimeofday(), which isn't POSIX, + * but is fairly common + */ +#include <sys/time.h> +#define _POSIX_SOURCE +#include <pwd.h> + +extern int rebootargc; +extern char** rebootargv; + +int gidnobody = -1; +int uidnobody = -1; +Proc** Xup; + +#define MAXSPROC 30000 /* max procs == MAXPID */ +static int sproctbl[MAXSPROC]; + +enum +{ + KSTACK = 64*1024, + DELETE = 0x7F +}; +char *hosttype = "Irix"; +char *cputype = "mips"; + +extern int dflag; + +void +pexit(char *msg, int t) +{ + Osenv *e; + + lock(&procs.l); + if(up->prev) + up->prev->next = up->next; + else + procs.head = up->next; + + if(up->next) + up->next->prev = up->prev; + else + procs.tail = up->prev; + + sproctbl[getpid()] = -1; + + unlock(&procs.l); + +/* print("pexit: %s: %s\n", up->text, msg); /**/ + e = up->env; + if(e != nil) { + closefgrp(e->fgrp); + closepgrp(e->pgrp); + closeegrp(e->egrp); + closesigs(e->sigs); + } + free(up->prog); + free(up); + exit(0); +} + +static void +tramp(void *p, size_t stacksz) +{ + up = p; + up->sigid = getpid(); + up->func(up->arg); + pexit("", 0); +} + +int +kproc(char *name, void (*func)(void*), void *arg, int flags) +{ + Proc *p; + Pgrp *pg; + Fgrp *fg; + Egrp *eg; + int pid; + int id; + int i; + + p = newproc(); + + if(flags & KPDUPPG) { + pg = up->env->pgrp; + incref(&pg->r); + p->env->pgrp = pg; + } + if(flags & KPDUPFDG) { + fg = up->env->fgrp; + incref(&fg->r); + p->env->fgrp = fg; + } + if(flags & KPDUPENVG) { + eg = up->env->egrp; + incref(&eg->r); + p->env->egrp = eg; + } + + p->env->uid = up->env->uid; + p->env->gid = up->env->gid; + kstrdup(&p->env->user, up->env->user); + + strcpy(p->text, name); + + p->func = func; + p->arg = arg; + + lock(&procs.l); + if(procs.tail != nil) { + p->prev = procs.tail; + procs.tail->next = p; + } + else { + procs.head = p; + p->prev = nil; + } + procs.tail = p; + + for(i = 1; i < MAXSPROC; i++) { + if(sproctbl[i] == -1) { + break; + } + } + + if(i==MAXSPROC) + return -1; + + sproctbl[i] = -i - 1; /* temporary hold of table index outside of lock */ + + unlock(&procs.l); + + pid = sprocsp(tramp, PR_SALL, p, 0, KSTACK); + + if(-1 < pid) + sproctbl[i] = pid; + else + sproctbl[i] = -1; + + return pid; +} + +void +osblock(void) +{ + blockproc(up->sigid); +} + +void +osready(Proc *p) +{ + unblockproc(p->sigid); +} + +void +trapUSR1(void) +{ + int intwait; + + intwait = up->intwait; + up->intwait = 0; /* clear it to let proc continue in osleave */ + + if(up->type != Interp) /* Used to unblock pending I/O */ + return; + + if(intwait == 0) /* Not posted so it's a sync error */ + disfault(nil, Eintr); /* Should never happen */ +} + +void +trapILL(void) +{ + disfault(nil, "Illegal instruction"); +} + +void +trapBUS(void) +{ + disfault(nil, "Bus error"); +} + +void +trapSEGV(void) +{ + disfault(nil, "Segmentation violation"); +} + +/* + * This is not a signal handler but rather a vector from real/FPcontrol-Irix.c + */ +void +trapFPE(unsigned exception[5], int value[2]) +{ + disfault(nil, "Floating point exception"); +} + +void +oshostintr(Proc *p) +{ + kill(p->sigid, SIGUSR1); +} + +void +oslongjmp(void *regs, osjmpbuf env, int val) +{ + USED(regs); + siglongjmp(env, val); +} + +static struct termios tinit; + +static void +termset(void) +{ + struct termios t; + + tcgetattr(0, &t); + tinit = t; + t.c_lflag &= ~(ICANON|ECHO|ISIG); + t.c_cc[VMIN] = 1; + t.c_cc[VTIME] = 0; + tcsetattr(0, TCSANOW, &t); +} + +static void +termrestore(void) +{ + tcsetattr(0, TCSANOW, &tinit); + +/* if(sproctbl[0] < 0) + panic("corrupt sproc tbl"); + + kill(sproctbl[0], SIGUSR2); + sginap(10000); */ +} + +void +trapUSR2(void) +{ + int i; + + for(i = MAXSPROC - 1; i > 0; i--) { + if(sproctbl[i] != -1) + kill(sproctbl[i], SIGKILL); + sproctbl[i] = -1; + } + + execvp(rebootargv[0], rebootargv); + panic("reboot failure"); +} + +void +cleanexit(int x) +{ + USED(x); + + if(up->intwait) { + up->intwait = 0; + return; + } + + if(dflag == 0) + termrestore(); + kill(0, SIGKILL); + exit(0); +} + +void +getnobody(void) +{ + struct passwd *pwd; + + pwd = getpwnam("nobody"); + if(pwd != nil) { + uidnobody = pwd->pw_uid; + gidnobody = pwd->pw_gid; + } +} + +void +osreboot(char *file, char **argv) +{ + if(dflag == 0) + termrestore(); + execvp(file, argv); + panic("reboot failure"); +} + +void +libinit(char *imod) +{ + struct sigaction act; + struct passwd *pw; + int i; + char sys[64]; + + setsid(); + + for(i=0; i<MAXSPROC; i++) + sproctbl[i] = -1; + + sproctbl[0] = getpid(); + + gethostname(sys, sizeof(sys)); + kstrdup(&ossysname, sys); + + if(dflag == 0) + termset(); + + if(signal(SIGTERM, SIG_IGN) != SIG_IGN) + signal(SIGTERM, cleanexit); + if(signal(SIGINT, SIG_IGN) != SIG_IGN) + signal(SIGINT, cleanexit); + signal(SIGUSR2, trapUSR2); + /* For the correct functioning of devcmd in the + * face of exiting slaves + */ + signal(SIGCLD, SIG_IGN); + signal(SIGPIPE, SIG_IGN); + memset(&act, 0 , sizeof(act)); + act.sa_handler=trapUSR1; + sigaction(SIGUSR1, &act, nil); + if(sflag == 0) { + act.sa_handler=trapBUS; + sigaction(SIGBUS, &act, nil); + act.sa_handler=trapILL; + sigaction(SIGILL, &act, nil); + act.sa_handler=trapSEGV; + sigaction(SIGSEGV, &act, nil); + } + + if(usconfig(CONF_INITUSERS, 1000) < 0) + panic("usconfig"); + + Xup = (Proc**)PRDA->usr_prda.fill; + up = newproc(); + + pw = getpwuid(getuid()); + if(pw != nil) { + if (strlen(pw->pw_name) + 1 <= KNAMELEN) + strcpy(eve, pw->pw_name); + else + print("pw_name too long\n"); + } + else + print("cannot getpwuid\n"); + + /* after setting up, since this takes locks */ + getnobody(); + up->env->uid = getuid(); + up->env->gid = getgid(); + emuinit(imod); +} + +int +readkbd(void) +{ + int n; + char buf[1]; + + n = read(0, buf, sizeof(buf)); + if(n < 0) + fprint(2, "keyboard read error: %s\n", strerror(errno)); + if(n <= 0) + pexit("keyboard thread", 0); + switch(buf[0]) { + case '\r': + buf[0] = '\n'; + break; + case DELETE: + cleanexit(0); + break; + } + return buf[0]; +} + +int +segflush(void *a, ulong n) +{ + cacheflush(a, n, BCACHE); + return 0; +} + +/* + * Return an abitrary millisecond clock time + */ +long +osmillisec(void) +{ + static long sec0 = 0, usec0; + struct timeval t; + + if(gettimeofday(&t,(struct timezone*)0)<0) + return(0); + if(sec0==0){ + sec0 = t.tv_sec; + usec0 = t.tv_usec; + } + return((t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000); +} + +/* + * Return the time since the epoch in nanoseconds and microseconds + * The epoch is defined at 1 Jan 1970 + */ +vlong +osnsec(void) +{ + struct timeval t; + + gettimeofday(&t, nil); + return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000; +} + +vlong +osusectime(void) +{ + struct timeval t; + + gettimeofday(&t, nil); + return (vlong)t.tv_sec * 1000000 + t.tv_usec; +} + +int +osmillisleep(ulong milsec) +{ + static int tick; + + /* + * Posix-conforming CLK_TCK implementations tend to call sysconf, + * and we don't need the overhead. + */ + if(tick == 0) + tick = CLK_TCK; + sginap((tick*milsec)/1000); + return 0; +} + +int +limbosleep(ulong milsec) +{ + return osmillisleep(milsec); +} + +void +osyield(void) +{ + sginap(0); +} + +void +ospause(void) +{ + for(;;) + pause(); +} + +void +oslopri(void) +{ + nice(2); +} diff --git a/emu/Linux/asm-386.S b/emu/Linux/asm-386.S new file mode 100644 index 00000000..31946335 --- /dev/null +++ b/emu/Linux/asm-386.S @@ -0,0 +1,103 @@ + .file "asm-Linux-386.S" +#include "syscall.h" + .text + +/* + * executeonnewstack(void *tos, void (*tramp)(void *arg), void *arg) + */ + + .type executeonnewstack,@function + .global executeonnewstack +executeonnewstack: + pushl %ebp + movl %esp, %ebp + pushl %esi + + movl 8(%ebp), %esi /* get tos */ + subl $4, %esi + movl 16(%ebp), %eax + movl %eax, (%esi) /* stash arg on new stack */ + subl $4, %esi + movl 12(%ebp), %eax + movl %eax, (%esi) /* stash tramp on new stack */ + mov %esi, %esp /* swap stacks pronto */ + popl %eax /* recover the tramp address */ + subl %ebp, %ebp /* stop stack traceback here */ + call *%eax /* and jump to it (ho ho) */ + + /* if we return here, tramp didn't do its job */ + + addl $8, %esp /* clean up for pose value */ + + leal SYS_exit, %eax + int $0x80 + +/* + * unlockandexit(int *key) + * + * NB: the return status may be garbaged if the stack is reused + * between the unlock and the system call, but this should + * not matter since no task is waiting for the result + */ + + .type unlockandexit,@function + .global unlockandexit +unlockandexit: + pushl %ebp + movl %esp, %ebp + + movl 8(%ebp), %esi /* get the key address */ + pushl $0 /* exit status 0 */ + movl $0, %eax /* unlock the stack allocator */ + movl %eax, (%esi) + leal SYS_exit, %eax /* call exit */ + int $0x80 + +/* + * umult(ulong m1, ulong m2, ulong *hi) + */ + + .type umult,@function + .global umult +umult: + pushl %ebp + movl %esp, %ebp + pushl %ebx + + movl 8(%ebp), %eax + movl 12(%ebp), %ebx + mull %ebx + movl 16(%ebp), %ebx + movl %edx, (%ebx) + + popl %ebx + popl %ebp + ret + + .type FPsave,@function + .global FPsave +FPsave: + pushl %ebp + movl %esp, %ebp + movl 8(%ebp), %eax + fstenv (%eax) + popl %ebp + ret + + .type FPrestore,@function + .global FPrestore +FPrestore: + pushl %ebp + movl %esp, %ebp + movl 8(%ebp), %eax + fldenv (%eax) + popl %ebp + ret + + .type _tas,@function + .globl _tas +_tas: + movl $1, %eax + movl 4(%esp), %ecx + xchgl %eax, 0(%ecx) + ret diff --git a/emu/Linux/asm-arm.S b/emu/Linux/asm-arm.S new file mode 100644 index 00000000..d13bcd0e --- /dev/null +++ b/emu/Linux/asm-arm.S @@ -0,0 +1,132 @@ + .file "asm-Linux-arm.S" +#include "syscall.h" + .text + +/* + * void executeonnewstack(void *tos, void (*tramp)(void *arg), void *arg) + */ + + .align 2 + .global executeonnewstack + .type executeonnewstack, %function +executeonnewstack: + @ args = 0, pretend = 0, frame = 12 + @ frame_needed = 1, uses_anonymous_args = 0 + mov ip, sp + stmfd sp!, {fp, ip, lr, pc} + sub fp, ip, #4 + sub sp, sp, #12 + str r0, [fp, #-16] /* store tos */ + str r1, [fp, #-20] /* store tramp */ + str r2, [fp, #-24] /* store arg */ + ldr r0, [fp, #-24] /* get arg */ + ldr r2, [fp, #-16] /* get tos */ + mov sp, r2 /* set new stack */ + mov lr, pc + blx r1 /* call tramp*/ + + /* if we return here, tramp didn't do it's job */ + swi SYS_exit + ldmea fp, {fp, sp, pc} + .size executeonnewstack, .-executeonnewstack + +/* + * void unlockandexit(int *key) + * + * NB: the return status may be garbaged if the stack is reused + * between the unlock and the system call, but this should + * not matter since no task is waiting for the result + */ + + .align 2 + .global unlockandexit + .type unlockandexit, %function +unlockandexit: + @ args = 0, pretend = 0, frame = 4 + @ frame_needed = 1, uses_anonymous_args = 0 + mov ip, sp + stmfd sp!, {fp, ip, lr, pc} + sub fp, ip, #4 + sub sp, sp, #4 + mov r1, #0 + str r1, [r0] + swi SYS_exit + ldmea fp, {fp, sp, pc} + .size unlockandexit, .-unlockandexit + +/* + * ulong umult(ulong m1, ulong m2, ulong *hi) + */ + + .align 2 + .global umult + .type umult, %function +umult: + @ args = 0, pretend = 0, frame = 12 + @ frame_needed = 1, uses_anonymous_args = 0 + mov ip, sp + stmfd sp!, {fp, ip, lr, pc} + sub fp, ip, #4 + sub sp, sp, #12 + str r0, [fp, #-16] + str r1, [fp, #-20] + str r2, [fp, #-24] + ldr r1, [fp, #-16] + ldr r2, [fp, #-20] + umull r0, r3, r1, r2 + ldr r1, [fp, #-24] + str r3, [r1] + ldmea fp, {fp, sp, pc} + .size umult, .-umult + +/* + * void FPsave(void*); + */ + + .align 2 + .global FPsave + .type FPsave, %function +FPsave: + @ args = 0, pretend = 0, frame = 4 + @ frame_needed = 1, uses_anonymous_args = 0 + mov ip, sp + stmfd sp!, {fp, ip, lr, pc} + sub fp, ip, #4 + sub sp, sp, #4 + str r0, [fp, #-16] + ldmea fp, {fp, sp, pc} + .size FPsave, .-FPsave + +/* + * void FPrestore(void*); + */ + .align 2 + .global FPrestore + .type FPrestore, %function +FPrestore: + @ args = 0, pretend = 0, frame = 4 + @ frame_needed = 1, uses_anonymous_args = 0 + mov ip, sp + stmfd sp!, {fp, ip, lr, pc} + sub fp, ip, #4 + sub sp, sp, #4 + str r0, [fp, #-16] + ldmea fp, {fp, sp, pc} + .size FPrestore, .-FPrestore + +/* + * ulong _tas(ulong*); + */ + .align 2 + .global _tas + .type _tas, %function +_tas: + @ args = 0, pretend = 0, frame = 0 + @ frame_needed = 0, uses_anonymous_args = 0 + @ link register save eliminated. + @ lr needed for prologue + mov r3, #1 + mov r1, r0 + swp r0, r3, [r1] + mov pc, lr + .size _tas, .-_tas diff --git a/emu/Linux/asm-power.S b/emu/Linux/asm-power.S new file mode 100644 index 00000000..ff5f97cd --- /dev/null +++ b/emu/Linux/asm-power.S @@ -0,0 +1,72 @@ +#include <asm-ppc/reg.h> + + .file "asm-power.S" + .section ".text" + .align 2 + .globl FPsave + .type FPsave, @function +FPsave: + .set _framesize,0 + mffs f0 + stfd f0,0(r3) + blr + .size FPsave,.-FPsave + + .align 2 + .globl FPrestore +FPrestore: + lfd f0,0(r3) + mtfsf 0xff,f0 + blr + .size FPrestore, .-FPrestore + + .align 2 + .globl _tas +_tas: + sync + mr r4,r3 + addi r5,0,0x1 +1: + lwarx r3,0,r4 + cmpwi r3,0x0 + bne- 2f + stwcx. r5,0,r4 + bne- 1b /* Lost reservation, try again */ +2: + sync + blr + .size _tas,.-_tas + +/* + * void executeonnewstack(void *tos, void (*tramp)(void *arg), void *arg) + */ + .align 2 + .globl executeonnewstack: +executeonnewstack: + mr r1,r3 /* change stacks */ + stwu 1,-16(r1) /* save lr to aid the traceback */ + li r0,0 + stw r0,20(r1) + mr r3,r5 + mtctr r4 + bctrl /* tramp(arg) */ + br . /* failed */ + .size executeonnewstack,.-executeonnewstack + +/* + * void unlockandexit(int *key) + * + * NB: the return status may be garbaged if the stack is reused + * between the unlock and the system call, but this should + * not matter since no task is waiting for the result + */ + .align 2 + .globl unlockandexit +unlockandexit: + li r0,0x0 + stw r0,0(r3) /* unlock */ + li r0,1 /* sys exit; 234 is exit group */ + li r3,0 /* exit status */ + sc + br . /* not reached */ + .size unlockandexit,.-unlockandexit diff --git a/emu/Linux/cmd.c b/emu/Linux/cmd.c new file mode 100644 index 00000000..0b8c960b --- /dev/null +++ b/emu/Linux/cmd.c @@ -0,0 +1,213 @@ +#include <sys/types.h> +#include <signal.h> +#include <pwd.h> +#include <sys/resource.h> +#include <sys/wait.h> +#include <fcntl.h> + +#include "dat.h" +#include "fns.h" +#include "error.h" + +enum +{ + Debug = 0 +}; + +/* + * os-specific devcmd support. + * this version should be reasonably portable across Unix systems. + */ +typedef struct Targ Targ; +struct Targ +{ + int fd[3]; /* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */ + char** args; + char* dir; + int pid; + int wfd; /* child writes errors that occur after the fork or on exec */ + int uid; + int gid; +}; + +extern int gidnobody; +extern int uidnobody; + +static int +childproc(Targ *t) +{ + int i, nfd; + + if(Debug) + print("devcmd: '%s'", t->args[0]); + + nfd = getdtablesize(); + for(i = 0; i < nfd; i++) + if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd) + close(i); + + dup2(t->fd[0], 0); + dup2(t->fd[1], 1); + dup2(t->fd[2], 2); + close(t->fd[0]); + close(t->fd[1]); + close(t->fd[2]); + + /* should have an auth file to do host-specific authorisation? */ + if(t->gid != -1){ + if(setgid(t->gid) < 0 && getegid() == 0){ + fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno)); + _exit(1); + } + } + + if(t->uid != -1){ + if(setuid(t->uid) < 0 && geteuid() == 0){ + fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno)); + _exit(1); + } + } + + if(t->dir != nil && chdir(t->dir) < 0){ + fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno)); + _exit(1); + } + + signal(SIGPIPE, SIG_DFL); + + execvp(t->args[0], t->args); + if(Debug) + print("execvp: %s\n",strerror(errno)); + fprint(t->wfd, "exec failed: %s", strerror(errno)); + + _exit(1); +} + +void* +oscmd(char **args, int nice, char *dir, int *fd) +{ + Targ *t; + int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid; + + t = mallocz(sizeof(*t), 1); + if(t == nil) + return nil; + + fd0[0] = fd0[1] = -1; + fd1[0] = fd1[1] = -1; + fd2[0] = fd2[1] = -1; + wfd[0] = wfd[1] = -1; + if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0) + goto Error; + if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0) /* close on exec to give end of file on success */ + goto Error; + + t->fd[0] = fd0[0]; + t->fd[1] = fd1[1]; + t->fd[2] = fd2[1]; + t->wfd = wfd[1]; + t->args = args; + t->dir = dir; + t->gid = up->env->gid; + if(t->gid == -1) + t->gid = gidnobody; + t->uid = up->env->uid; + if(t->uid == -1) + t->uid = uidnobody; + + signal(SIGCHLD, SIG_DFL); + switch(pid = fork()) { + case -1: + goto Error; + case 0: + setpgrp(); + if(nice) + setpriority(PRIO_PROCESS, 0, 19); + childproc(t); + _exit(1); + default: + t->pid = pid; + if(Debug) + print("cmd pid %d\n", t->pid); + break; + } + + close(fd0[0]); + close(fd1[1]); + close(fd2[1]); + close(wfd[1]); + + n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1); + close(wfd[0]); + if(n > 0){ + close(fd0[1]); + close(fd1[0]); + close(fd2[0]); + free(t); + up->genbuf[n] = 0; + if(Debug) + print("oscmd: bad exec: %q\n", up->genbuf); + error(up->genbuf); + return nil; + } + + fd[0] = fd0[1]; + fd[1] = fd1[0]; + fd[2] = fd2[0]; + return t; + +Error: + r = errno; + if(Debug) + print("oscmd: %q\n",strerror(r)); + close(fd0[0]); + close(fd0[1]); + close(fd1[0]); + close(fd1[1]); + close(fd2[0]); + close(fd2[1]); + close(wfd[0]); + close(wfd[1]); + error(strerror(r)); + return nil; +} + +int +oscmdkill(void *a) +{ + Targ *t = a; + + if(Debug) + print("kill: %d\n", t->pid); + return kill(-t->pid, SIGTERM); +} + +int +oscmdwait(void *a, char *buf, int n) +{ + Targ *t = a; + int s; + + if(waitpid(t->pid, &s, 0) == -1){ + if(Debug) + print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno)); + return -1; + } + if(WIFEXITED(s)){ + if(WEXITSTATUS(s) == 0) + return snprint(buf, n, "%d 0 0 0 ''", t->pid); + return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s)); + } + if(WIFSIGNALED(s)){ + if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL) + return snprint(buf, n, "%d 0 0 0 killed", t->pid); + return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s)); + } + return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s); +} + +void +oscmdfree(void *a) +{ + free(a); +} diff --git a/emu/Linux/deveia.c b/emu/Linux/deveia.c new file mode 100644 index 00000000..a1f0951f --- /dev/null +++ b/emu/Linux/deveia.c @@ -0,0 +1,44 @@ +/* + * Linux serial port definitions + */ + +static char *sysdev[] = { + "/dev/ttyS0", + "/dev/ttyS1", + "/dev/ttyS2", + "/dev/ttyS3", + "/dev/ttyS4", + "/dev/ttyS5", + "/dev/ttyS6", + "/dev/ttyS7", +}; + +#include <sys/ioctl.h> +#include "deveia-posix.c" +#include "deveia-bsd.c" + + +static struct tcdef_t bps[] = { + {0, B0}, + {50, B50}, + {75, B75}, + {110, B110}, + {134, B134}, + {150, B150}, + {200, B200}, + {300, B300}, + {600, B600}, + {1200, B1200}, + {1800, B1800}, + {2400, B2400}, + {4800, B4800}, + {9600, B9600}, + {19200, B19200}, + {38400, B38400}, + {57600, B57600}, + {115200, B115200}, + {230400, B230400}, + {460800, B460800}, + {-1, -1} +}; + diff --git a/emu/Linux/devfs.c b/emu/Linux/devfs.c new file mode 100644 index 00000000..9017c3fc --- /dev/null +++ b/emu/Linux/devfs.c @@ -0,0 +1 @@ +#include "devfs-posix.c" diff --git a/emu/Linux/emu b/emu/Linux/emu new file mode 100644 index 00000000..0e7c17f9 --- /dev/null +++ b/emu/Linux/emu @@ -0,0 +1,106 @@ +dev + root + cons + env + mnt + pipe + prog + prof + srv + dup + ssl + cap + fs + cmd cmd + indir + + draw win-x11a + pointer + snarf + + ip ipif-posix ipaux + eia +# audio audio + mem + +lib + interp + tk + freetype + math + draw + + memlayer + memdraw + keyring + sec + mp + + 9 + +link + +mod + sys + draw + + tk + math + srv srv + keyring + loader + freetype + +port + alloc + cache + chan + dev + dial + dis + discall + env + error + errstr + exception + exportfs + inferno + latin1 + main + parse + pgrp + print + proc + qio + random + sysfile + uqid + +code + +init + emuinit + +root + /dev / + /fd / + /prog / + /net / + /net.alt / + /chan / + /nvfs / + /env / +# /chan +# /dev +# /dis +# /env +# /n +# /net +# /nvfs / +# /prog +# /icons +# /osinit.dis +# /dis/emuinit.dis +# /dis/lib/auth.dis +# /dis/lib/ssl.dis +# /n/local / diff --git a/emu/Linux/emu-g b/emu/Linux/emu-g new file mode 100644 index 00000000..31d29125 --- /dev/null +++ b/emu/Linux/emu-g @@ -0,0 +1,94 @@ +dev + root + cons + env + mnt + pipe + prog + prof + srv + dup + ssl + cap + fs + cmd cmd + indir + + ip ipif-posix ipaux + eia +# audio audio + mem + +lib + interp + math + keyring + sec + mp + + 9 + +link + +mod + sys + math + srv srv + keyring + loader + +port + alloc + cache + chan + dev + dial + dis + discall + env + error + errstr + exception + exportfs + inferno + latin1 + main + parse + pgrp + print + proc + qio + random + sysfile + uqid + +code + void setpointer(int x, int y){USED(x); USED(y);} + ulong strtochan(char *s){USED(s); return ~0;} + +init + emuinit + +root + /dev / + /fd / + /prog / + /net / + /net.alt / + /chan / + /nvfs / + /env / +# /chan +# /dev +# /dis +# /env +# /n +# /net +# /nvfs / +# /prog +# /icons +# /osinit.dis +# /dis/emuinit.dis +# /dis/lib/auth.dis +# /dis/lib/ssl.dis +# /n/local / diff --git a/emu/Linux/mkfile b/emu/Linux/mkfile new file mode 100644 index 00000000..77693e4e --- /dev/null +++ b/emu/Linux/mkfile @@ -0,0 +1,47 @@ +SYSTARG=Linux +<../../mkconfig +SYSTARG=Linux + +#Configurable parameters + +CONF=emu #default configuration +CONFLIST=emu +CLEANCONFLIST= + +INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed + +#end configurable parameters + +<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system + +<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS + +OBJ=\ + asm-$OBJTYPE.$O\ + os.$O\ + $CONF.root.$O\ + lock.$O\ + $DEVS\ + $PORT\ + +LIBNAMES=${LIBS:%=lib%.a} +#libs=${LIBS:%=$ROOT/$OBJDIR/lib/lib%.a} + +HFILES=\ + +CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS +SYSLIBS= -lm -lX11 -lXext +KERNDATE=`{$NDATE} + +default:V: $O.$CONF + +$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBNAMES + $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c + $LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS + +install:V: $O.$CONF + cp $O.$CONF $INSTALLDIR/$CONF + +<../port/portmkfile + +devfs.$O: ../port/devfs-posix.c diff --git a/emu/Linux/os.c b/emu/Linux/os.c new file mode 100644 index 00000000..49aec7e5 --- /dev/null +++ b/emu/Linux/os.c @@ -0,0 +1,535 @@ +#include <sys/types.h> +#include <time.h> +#include <termios.h> +#include <signal.h> +#include <pwd.h> +#include <sched.h> +#include <sys/resource.h> +#include <sys/wait.h> +#include <sys/time.h> + +#include "dat.h" +#include "fns.h" +#include "error.h" + +/* glibc 2.3.3-NTPL messes up getpid() by trying to cache the result, so we'll do it ourselves */ +#include <sys/syscall.h> +#define getpid() syscall(SYS_getpid) + +/* temporarily suppress CLONE_PTRACE so it works on broken Linux kernels */ +#undef CLONE_PTRACE +#define CLONE_PTRACE 0 + +enum +{ + DELETE = 0x7f, + CTRLC = 'C'-'@', + NSTACKSPERALLOC = 16, + X11STACK= 256*1024 +}; +char *hosttype = "Linux"; + +static void *stackalloc(Proc *p, void **tos); +static void stackfreeandexit(void *stack); + +extern int dflag; + +int gidnobody = -1; +int uidnobody = -1; +static struct termios tinit; + +void +pexit(char *msg, int t) +{ + Osenv *e; + void *kstack; + + lock(&procs.l); + if(up->prev) + up->prev->next = up->next; + else + procs.head = up->next; + + if(up->next) + up->next->prev = up->prev; + else + procs.tail = up->prev; + unlock(&procs.l); + + if(0) + print("pexit: %s: %s\n", up->text, msg); + + e = up->env; + if(e != nil) { + closefgrp(e->fgrp); + closepgrp(e->pgrp); + closeegrp(e->egrp); + closesigs(e->sigs); + } + kstack = up->kstack; + free(up->prog); + free(up); + if(kstack != nil) + stackfreeandexit(kstack); +} + +void +tramp(void *arg) +{ + Proc *p; + p = arg; + p->pid = p->sigid = getpid(); + (*p->func)(p->arg); + pexit("{Tramp}", 0); +} + +int +kproc(char *name, void (*func)(void*), void *arg, int flags) +{ + int pid; + Proc *p; + Pgrp *pg; + Fgrp *fg; + Egrp *eg; + void *tos; + + p = newproc(); + if(0) + print("start %s:%.8lx\n", name, p); + if(p == nil) { + print("kproc(%s): no memory", name); + return; + } + + if(flags & KPDUPPG) { + pg = up->env->pgrp; + incref(&pg->r); + p->env->pgrp = pg; + } + if(flags & KPDUPFDG) { + fg = up->env->fgrp; + incref(&fg->r); + p->env->fgrp = fg; + } + if(flags & KPDUPENVG) { + eg = up->env->egrp; + incref(&eg->r); + p->env->egrp = eg; + } + + p->env->uid = up->env->uid; + p->env->gid = up->env->gid; + kstrdup(&p->env->user, up->env->user); + + strcpy(p->text, name); + + p->func = func; + p->arg = arg; + + if(flags & KPX11){ + p->kstack = nil; /* never freed; also up not defined */ + tos = (char*)mallocz(X11STACK, 0) + X11STACK - sizeof(void*); + }else + p->kstack = stackalloc(p, &tos); + + lock(&procs.l); + if(procs.tail != nil) { + p->prev = procs.tail; + procs.tail->next = p; + } + else { + procs.head = p; + p->prev = nil; + } + procs.tail = p; + unlock(&procs.l); + + if (__clone(tramp, tos, CLONE_PTRACE|CLONE_VM|CLONE_FS|CLONE_FILES|SIGCHLD, p) <= 0) + panic("kproc: clone failed"); + + return 0; +} + +/* + * TO DO: + * To get pc on trap, use sigaction instead of signal and + * examine its siginfo structure + */ + +/* +static void +diserr(char *s, int pc) +{ + char buf[ERRMAX]; + + snprint(buf, sizeof(buf), "%s: pc=0x%lux", s, pc); + disfault(nil, buf); +} +*/ + +static void +trapILL(int signo) +{ + USED(signo); + disfault(nil, "Illegal instruction"); +} + +static void +trapBUS(int signo) +{ + USED(signo); + disfault(nil, "Bus error"); +} + +static void +trapSEGV(int signo) +{ + USED(signo); + disfault(nil, "Segmentation violation"); +} + +#include <fpuctl.h> +static void +trapFPE(int signo) +{ + USED(signo); + print("FPU status=0x%.4lux", getfsr()); + disfault(nil, "Floating exception"); +} + +static void +trapUSR1(int signo) +{ + int intwait; + + USED(signo); + + intwait = up->intwait; + up->intwait = 0; /* clear it to let proc continue in osleave */ + + if(up->type != Interp) /* Used to unblock pending I/O */ + return; + + if(intwait == 0) /* Not posted so it's a sync error */ + disfault(nil, Eintr); /* Should never happen */ +} + +/* called to wake up kproc blocked on a syscall */ +void +oshostintr(Proc *p) +{ + kill(p->sigid, SIGUSR1); +} + +static void +trapUSR2(int signo) +{ + USED(signo); + /* we've done our work of interrupting sigsuspend */ +} + +void +osblock(void) +{ + sigset_t mask; + + sigprocmask(SIG_SETMASK, NULL, &mask); + sigdelset(&mask, SIGUSR2); + sigsuspend(&mask); +} + +void +osready(Proc *p) +{ + if(kill(p->sigid, SIGUSR2) < 0) + fprint(2, "emu: osready failed: pid %d: %s\n", p->sigid, strerror(errno)); +} + +void +oslongjmp(void *regs, osjmpbuf env, int val) +{ + USED(regs); + siglongjmp(env, val); +} + +static void +termset(void) +{ + struct termios t; + + tcgetattr(0, &t); + tinit = t; + t.c_lflag &= ~(ICANON|ECHO|ISIG); + t.c_cc[VMIN] = 1; + t.c_cc[VTIME] = 0; + tcsetattr(0, TCSANOW, &t); +} + +static void +termrestore(void) +{ + tcsetattr(0, TCSANOW, &tinit); +} + +void +cleanexit(int x) +{ + USED(x); + + if(up->intwait) { + up->intwait = 0; + return; + } + + if(dflag == 0) + termrestore(); + + kill(0, SIGKILL); + exit(0); +} + +void +osreboot(char *file, char **argv) +{ +} + +void +libinit(char *imod) +{ + struct termios t; + struct sigaction act; + sigset_t mask; + struct passwd *pw; + Proc *p; + void *tos; + char sys[64]; + + setsid(); + + gethostname(sys, sizeof(sys)); + kstrdup(&ossysname, sys); + pw = getpwnam("nobody"); + if(pw != nil) { + uidnobody = pw->pw_uid; + gidnobody = pw->pw_gid; + } + + if(dflag == 0) + termset(); + + memset(&act, 0 , sizeof(act)); + act.sa_handler = trapUSR1; + sigaction(SIGUSR1, &act, nil); + + sigemptyset(&mask); + sigaddset(&mask, SIGUSR2); + sigprocmask(SIG_BLOCK, &mask, NULL); + + memset(&act, 0 , sizeof(act)); + act.sa_handler = trapUSR2; + sigaction(SIGUSR2, &act, nil); + + act.sa_handler = SIG_IGN; + sigaction(SIGCHLD, &act, nil); + + /* + * For the correct functioning of devcmd in the + * face of exiting slaves + */ + signal(SIGPIPE, SIG_IGN); + if(signal(SIGTERM, SIG_IGN) != SIG_IGN) + signal(SIGTERM, cleanexit); + if(signal(SIGINT, SIG_IGN) != SIG_IGN) + signal(SIGINT, cleanexit); + + if(sflag == 0) { + act.sa_handler = trapBUS; + sigaction(SIGBUS, &act, nil); + act.sa_handler = trapILL; + sigaction(SIGILL, &act, nil); + act.sa_handler = trapSEGV; + sigaction(SIGSEGV, &act, nil); + act.sa_handler = trapFPE; + sigaction(SIGFPE, &act, nil); + } + + p = newproc(); + p->kstack = stackalloc(p, &tos); + + pw = getpwuid(getuid()); + if(pw != nil) + kstrdup(&eve, pw->pw_name); + else + print("cannot getpwuid\n"); + + p->env->uid = getuid(); + p->env->gid = getgid(); + + executeonnewstack(tos, emuinit, imod); +} + +int +readkbd(void) +{ + int n; + char buf[1]; + + n = read(0, buf, sizeof(buf)); + if(n < 0) + print("keyboard close (n=%d, %s)\n", n, strerror(errno)); + if(n <= 0) + pexit("keyboard thread", 0); + + switch(buf[0]) { + case '\r': + buf[0] = '\n'; + break; + case DELETE: + buf[0] = 'H' - '@'; + break; + case CTRLC: + cleanexit(0); + break; + } + return buf[0]; +} + +/* + * Return an abitrary millisecond clock time + */ +long +osmillisec(void) +{ + static long sec0 = 0, usec0; + struct timeval t; + + if(gettimeofday(&t,(struct timezone*)0)<0) + return 0; + + if(sec0 == 0) { + sec0 = t.tv_sec; + usec0 = t.tv_usec; + } + return (t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000; +} + +/* + * Return the time since the epoch in nanoseconds and microseconds + * The epoch is defined at 1 Jan 1970 + */ +vlong +osnsec(void) +{ + struct timeval t; + + gettimeofday(&t, nil); + return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000; +} + +vlong +osusectime(void) +{ + struct timeval t; + + gettimeofday(&t, nil); + return (vlong)t.tv_sec * 1000000 + t.tv_usec; +} + +int +osmillisleep(ulong milsec) +{ + struct timespec time; + + time.tv_sec = milsec/1000; + time.tv_nsec= (milsec%1000)*1000000; + nanosleep(&time, NULL); + return 0; +} + +int +limbosleep(ulong milsec) +{ + return osmillisleep(milsec); +} + +void +osyield(void) +{ + sched_yield(); +} + +void +ospause(void) +{ + for(;;) + pause(); +} + +void +oslopri(void) +{ + setpriority(PRIO_PROCESS, 0, getpriority(PRIO_PROCESS,0)+4); +} + +static struct { + Lock l; + void *free; +} stacklist; + +static void +_stackfree(void *stack) +{ + *((void **)stack) = stacklist.free; + stacklist.free = stack; +} + +static void +stackfreeandexit(void *stack) +{ + lock(&stacklist.l); + _stackfree(stack); + unlockandexit(&stacklist.l.val); +} + +static void * +stackalloc(Proc *p, void **tos) +{ + void *rv; + lock(&stacklist.l); + if (stacklist.free == 0) { + int x; + /* + * obtain some more by using sbrk() + */ + void *more = sbrk(KSTACK * (NSTACKSPERALLOC + 1)); + if (more == 0) + panic("stackalloc: no more stacks"); + /* + * align to KSTACK + */ + more = (void *)((((unsigned long)more) + (KSTACK - 1)) & ~(KSTACK - 1)); + /* + * free all the new stacks onto the freelist + */ + for (x = 0; x < NSTACKSPERALLOC; x++) + _stackfree((char *)more + KSTACK * x); + } + rv = stacklist.free; + stacklist.free = *(void **)rv; + unlock(&stacklist.l); + *tos = rv + KSTACK - sizeof(void *); + *(Proc **)rv = p; + return rv; +} + +#ifdef LINUX_ARM +#define SYS_cacheflush __ARM_NR_cacheflush + +int +segflush(void *a, ulong n) +{ + if(n) + syscall(SYS_cacheflush, a, (char*)a+n-1, 1); + return 0; +} +#endif diff --git a/emu/Linux/segflush-power.c b/emu/Linux/segflush-power.c new file mode 100644 index 00000000..07f9a3cb --- /dev/null +++ b/emu/Linux/segflush-power.c @@ -0,0 +1,27 @@ +/* + * from geoff collyer's port + * invalidate instruction cache and write back data cache from a to a+n-1, + * at least. + */ +void +segflush(void *a, ulong n) +{ + ulong *p; + + // cache blocks are often eight words (32 bytes) long, sometimes 16 bytes. + // need to determine it dynamically? + for (p = (ulong *)((ulong)a & ~3UL); (char *)p < (char *)a + n; p++) + __asm__("dcbst 0,%0\n\t" // not dcbf, which writes back, then invalidates + "icbi 0,%0\n\t" + : // no output + : "ar" (p) + ); + __asm__("sync\n\t" + : // no output + : + ); + __asm__("isync\n\t" + : // no output + : + ); +} diff --git a/emu/MacOSX/NOTE b/emu/MacOSX/NOTE new file mode 100644 index 00000000..b9f8748f --- /dev/null +++ b/emu/MacOSX/NOTE @@ -0,0 +1,4 @@ +- still interp mode only, jit not complete +- mkfile uses Carbon only; mkfile-x11 uses X11R6 from Apple +- both powerpc and x86 versions compiled from this source +- mkfile-g doesn't include graphics (uses emu-g not emu as configuration) diff --git a/emu/MacOSX/NOTICE b/emu/MacOSX/NOTICE new file mode 100644 index 00000000..a14c52c9 --- /dev/null +++ b/emu/MacOSX/NOTICE @@ -0,0 +1,3 @@ +MacOSX port Copyright © 2001-2003 Corpus Callosum Corporation, +with portions Copyright © 2001-2003 Geoff Collyer +MacOSX-x86 Copyright © 2006 Corpus Callosum Corporation diff --git a/emu/MacOSX/asm-386.s b/emu/MacOSX/asm-386.s new file mode 100644 index 00000000..fc903bcb --- /dev/null +++ b/emu/MacOSX/asm-386.s @@ -0,0 +1,28 @@ +/* + * these are the same as on the PC (eg, Linux) +*/ + + .globl _FPsave +_FPsave: + pushl %ebp + movl %esp, %ebp + movl 8(%ebp), %eax + fstenv (%eax) + popl %ebp + ret + + .globl _FPrestore +_FPrestore: + pushl %ebp + movl %esp, %ebp + movl 8(%ebp), %eax + fldenv (%eax) + popl %ebp + ret + + .globl __tas +__tas: + movl $1, %eax + movl 4(%esp), %ecx + xchgl %eax, 0(%ecx) + ret diff --git a/emu/MacOSX/asm-power.s b/emu/MacOSX/asm-power.s new file mode 100644 index 00000000..d7db8188 --- /dev/null +++ b/emu/MacOSX/asm-power.s @@ -0,0 +1,36 @@ +/* + * File: asm-power.s + * + * Copyright (c) 2003, Corpus Callosum Corporation. All rights reserved. + */ + +#include <architecture/ppc/asm_help.h> + +.text + +LEAF(_FPsave) + mffs f0 + stfd f0,0(r3) + blr +END(_FPsave) + +LEAF(_FPrestore) + lfd f0,0(r3) + mtfsf 0xff,f0 + blr +END(_FPrestore) + +LEAF(__tas) + sync + mr r4,r3 + addi r5,0,0x1 +1: + lwarx r3,0,r4 + cmpwi r3,0x0 + bne- 2f + stwcx. r5,0,r4 + bne- 1b /* Lost reservation, try again */ +2: + sync + blr +END(__tas) diff --git a/emu/MacOSX/cmd.c b/emu/MacOSX/cmd.c new file mode 100644 index 00000000..58f6dba7 --- /dev/null +++ b/emu/MacOSX/cmd.c @@ -0,0 +1,214 @@ +#include <sys/types.h> +#include <signal.h> +#include <pwd.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/wait.h> +#include <fcntl.h> + +#include "dat.h" +#include "fns.h" +#include "error.h" + +enum +{ + Debug = 0 +}; + +/* + * os-specific devcmd support. + * this version should be reasonably portable across Unix systems. + */ +typedef struct Targ Targ; +struct Targ +{ + int fd[3]; /* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */ + char** args; + char* dir; + int pid; + int wfd; /* child writes errors that occur after the fork or on exec */ + int uid; + int gid; +}; + +extern int gidnobody; +extern int uidnobody; + +static int +childproc(Targ *t) +{ + int i, nfd; + + if(Debug) + print("devcmd: '%s'", t->args[0]); + + nfd = getdtablesize(); + for(i = 0; i < nfd; i++) + if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd) + close(i); + + dup2(t->fd[0], 0); + dup2(t->fd[1], 1); + dup2(t->fd[2], 2); + close(t->fd[0]); + close(t->fd[1]); + close(t->fd[2]); + + /* should have an auth file to do host-specific authorisation? */ + if(t->gid != -1){ + if(setgid(t->gid) < 0 && getegid() == 0){ + fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno)); + _exit(1); + } + } + + if(t->uid != -1){ + if(setuid(t->uid) < 0 && geteuid() == 0){ + fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno)); + _exit(1); + } + } + + if(t->dir != nil && chdir(t->dir) < 0){ + fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno)); + _exit(1); + } + + signal(SIGPIPE, SIG_DFL); + + execvp(t->args[0], t->args); + if(Debug) + print("execvp: %s\n",strerror(errno)); + fprint(t->wfd, "exec failed: %s", strerror(errno)); + + _exit(1); +} + +void* +oscmd(char **args, int nice, char *dir, int *fd) +{ + Targ *t; + int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid; + + t = mallocz(sizeof(*t), 1); + if(t == nil) + return nil; + + fd0[0] = fd0[1] = -1; + fd1[0] = fd1[1] = -1; + fd2[0] = fd2[1] = -1; + wfd[0] = wfd[1] = -1; + if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0) + goto Error; + if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0) /* close on exec to give end of file on success */ + goto Error; + + t->fd[0] = fd0[0]; + t->fd[1] = fd1[1]; + t->fd[2] = fd2[1]; + t->wfd = wfd[1]; + t->args = args; + t->dir = dir; + t->gid = up->env->gid; + if(t->gid == -1) + t->gid = gidnobody; + t->uid = up->env->uid; + if(t->uid == -1) + t->uid = uidnobody; + + signal(SIGCHLD, SIG_DFL); + switch(pid = fork()) { + case -1: + goto Error; + case 0: + setpgid(0, getpid()); + if(nice) + oslopri(); + childproc(t); + _exit(1); + default: + t->pid = pid; + if(Debug) + print("cmd pid %d\n", t->pid); + break; + } + + close(fd0[0]); + close(fd1[1]); + close(fd2[1]); + close(wfd[1]); + + n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1); + close(wfd[0]); + if(n > 0){ + close(fd0[1]); + close(fd1[0]); + close(fd2[0]); + free(t); + up->genbuf[n] = 0; + if(Debug) + print("oscmd: bad exec: %q\n", up->genbuf); + error(up->genbuf); + return nil; + } + + fd[0] = fd0[1]; + fd[1] = fd1[0]; + fd[2] = fd2[0]; + return t; + +Error: + r = errno; + if(Debug) + print("oscmd: %q\n",strerror(r)); + close(fd0[0]); + close(fd0[1]); + close(fd1[0]); + close(fd1[1]); + close(fd2[0]); + close(fd2[1]); + close(wfd[0]); + close(wfd[1]); + error(strerror(r)); + return nil; +} + +int +oscmdkill(void *a) +{ + Targ *t = a; + + if(Debug) + print("kill: %d\n", t->pid); + return kill(-t->pid, SIGTERM); +} + +int +oscmdwait(void *a, char *buf, int n) +{ + Targ *t = a; + int s; + + if(waitpid(t->pid, &s, 0) == -1){ + if(Debug) + print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno)); + return -1; + } + if(WIFEXITED(s)){ + if(WEXITSTATUS(s) == 0) + return snprint(buf, n, "%d 0 0 0 ''", t->pid); + return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s)); + } + if(WIFSIGNALED(s)){ + if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL) + return snprint(buf, n, "%d 0 0 0 killed", t->pid); + return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s)); + } + return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s); +} + +void +oscmdfree(void *a) +{ + free(a); +} diff --git a/emu/MacOSX/deveia.c b/emu/MacOSX/deveia.c new file mode 100644 index 00000000..bb7a01c6 --- /dev/null +++ b/emu/MacOSX/deveia.c @@ -0,0 +1,154 @@ +/* + * Darwin serial port definitions, uses IOKit to build sysdev + * Loosely based on FreeBSD/deveia.c + * Copyright © 1998, 1999 Lucent Technologies Inc. All rights reserved. + * Revisions Copyright © 1999, 2000 Vita Nuova Limited. All rights reserved. + * Revisions Copyright © 2003 Corpus Callosum Corporation. All rights reserved. +*/ + +#include <termios.h> +#include <sys/param.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> + +#include <mach/mach.h> + +#include <CoreFoundation/CFNumber.h> + +#include <IOKit/IOKitLib.h> +#include <IOKit/IOCFPlugIn.h> +#include <IOKit/usb/IOUSBLib.h> +#include <IOKit/serial/IOSerialKeys.h> +#include <IOKit/IOBSD.h> + +#define B14400 14400 +#define B28800 28800 +#define B57600 57600 +#define B76800 76800 +#define B115200 115200 +#define B230400 230400 + +extern int vflag; + +#define MAXDEV 8 +static char *sysdev[MAXDEV]; + +#include <sys/ioctl.h> +#include <sys/ttycom.h> + +static void _buildsysdev(void); +#define buildsysdev() _buildsysdev() /* for devfs-posix.c */ + +static void +_buildsysdev(void) +{ + kern_return_t kernResult; + mach_port_t masterPort; + CFMutableDictionaryRef classesToMatch; + io_iterator_t serialPortIterator; + io_object_t serialDevice; + CFMutableArrayRef array; + CFIndex idx; + + kernResult = IOMasterPort(MACH_PORT_NULL, &masterPort); + if (KERN_SUCCESS != kernResult) + { + printf("IOMasterPort returned %d\n", kernResult); + } else { + classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue); + if (classesToMatch == NULL) + { + printf("IOServiceMatching returned a NULL dictionary.\n"); + } else { + CFDictionarySetValue(classesToMatch, + CFSTR(kIOSerialBSDTypeKey), + CFSTR(kIOSerialBSDAllTypes)); + } + + kernResult = IOServiceGetMatchingServices(masterPort, classesToMatch, &serialPortIterator); + if (KERN_SUCCESS != kernResult) + { + printf("IOServiceGetMatchingServices returned %d\n", kernResult); + } else { + array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + + while ((serialDevice = IOIteratorNext(serialPortIterator))) { + CFTypeRef bsdPathAsCFString; + bsdPathAsCFString = IORegistryEntryCreateCFProperty(serialDevice, + CFSTR(kIOCalloutDeviceKey), + kCFAllocatorDefault, + 0); + if (bsdPathAsCFString) { + CFArrayAppendValue(array, bsdPathAsCFString); + } + + (void) IOObjectRelease(serialDevice); + } + + idx = CFArrayGetCount(array); + if (idx > 0) { + Boolean result; + char bsdPath[MAXPATHLEN]; + char *tmpsysdev[idx+1]; + CFIndex i; + + for (i=0; i<idx; i++) { + result = CFStringGetCString(CFArrayGetValueAtIndex(array, i), + bsdPath, + sizeof(bsdPath), + kCFStringEncodingASCII); + if (result) { + int len = strlen(bsdPath); + tmpsysdev[i] = (char *)malloc((len+1)*sizeof(char)); + strcpy(tmpsysdev[i], bsdPath); + } + } + tmpsysdev[idx] = NULL; + for (i=0; i < idx; i++) { + sysdev[i] = tmpsysdev[i]; + if (vflag) + printf("BSD path: '%s'\n", sysdev[i]); + } + } + + CFRelease(array); + } + } + + if (serialPortIterator) + IOObjectRelease(serialPortIterator); + if (masterPort) + mach_port_deallocate(mach_task_self(), masterPort); + + return; +} + +#undef nil + +#include "deveia-posix.c" +#include "deveia-bsd.c" + +static struct tcdef_t bps[] = { + {0, B0}, + {50, B50}, + {75, B75}, + {110, B110}, + {134, B134}, + {150, B150}, + {200, B200}, + {300, B300}, + {600, B600}, + {1200, B1200}, + {1800, B1800}, + {2400, B2400}, + {4800, B4800}, + {9600, B9600}, + {19200, B19200}, + {38400, B38400}, + {57600, B57600}, + {76800, B76800}, + {115200, B115200}, + {230400, B230400}, + {0, -1} +}; diff --git a/emu/MacOSX/devfs.c b/emu/MacOSX/devfs.c new file mode 100644 index 00000000..9017c3fc --- /dev/null +++ b/emu/MacOSX/devfs.c @@ -0,0 +1 @@ +#include "devfs-posix.c" diff --git a/emu/MacOSX/emu b/emu/MacOSX/emu new file mode 100644 index 00000000..91efe264 --- /dev/null +++ b/emu/MacOSX/emu @@ -0,0 +1,108 @@ +dev + root + cons + env + mnt + pipe + prog + prof + srv + dup + ssl + cap + fs + cmd cmd + indir + + draw win + pointer + snarf + + ip ipif ipaux + eia +# audio audio + mem + +lib + interp + tk + freetype + math + draw + + memlayer + memdraw + keyring + sec + mp + + 9 + +link + +mod + sys + draw + + tk + math + srv srv + keyring + loader + freetype + +port + alloc + cache + chan + dev + dial + dis + discall + env + error + errstr + exception + exportfs + inferno + latin1 + main + parse + pgrp + print + proc + qio + random + sysfile + uqid + +code + int dontcompile = 1; + int macjit = 1; + +init + emuinit + +root + /dev / + /fd / + /prog / + /net / + /net.alt / + /chan / + /nvfs / + /env / +# /chan +# /dev +# /dis +# /env +# /n +# /net +# /nvfs / +# /prog +# /icons +# /osinit.dis +# /dis/emuinit.dis +# /dis/lib/auth.dis +# /dis/lib/ssl.dis +# /n/local / diff --git a/emu/MacOSX/emu-g b/emu/MacOSX/emu-g new file mode 100644 index 00000000..380ece95 --- /dev/null +++ b/emu/MacOSX/emu-g @@ -0,0 +1,96 @@ +dev + root + cons + env + mnt + pipe + prog + prof + srv + dup + ssl + cap + fs + cmd cmd + indir + + ip ipif ipaux + eia +# audio audio + mem + +lib + interp + math + keyring + sec + mp + + 9 + +link + +mod + sys + math + srv srv + keyring + loader + +port + alloc + cache + chan + dev + dial + dis + discall + env + error + errstr + exception + exportfs + inferno + latin1 + main + parse + pgrp + print + proc + qio + random + sysfile + uqid + +code + int dontcompile = 1; + int macjit = 1; + void setpointer(int x, int y){USED(x); USED(y);} + ulong strtochan(char *s){USED(s); return ~0;} + +init + emuinit + +root + /dev / + /fd / + /prog / + /net / + /net.alt / + /chan / + /nvfs / + /env / +# /chan +# /dev +# /dis +# /env +# /n +# /net +# /nvfs / +# /prog +# /icons +# /osinit.dis +# /dis/emuinit.dis +# /dis/lib/auth.dis +# /dis/lib/ssl.dis +# /n/local / diff --git a/emu/MacOSX/ipif.c b/emu/MacOSX/ipif.c new file mode 100644 index 00000000..060c4087 --- /dev/null +++ b/emu/MacOSX/ipif.c @@ -0,0 +1 @@ +#include "../FreeBSD/ipif.c" diff --git a/emu/MacOSX/keycodes.h b/emu/MacOSX/keycodes.h new file mode 100644 index 00000000..52328ace --- /dev/null +++ b/emu/MacOSX/keycodes.h @@ -0,0 +1,189 @@ +/* These are the Macintosh key scancode constants -- from Inside Macintosh */ +#define QZ_ESCAPE 0x35 +#define QZ_F1 0x7A +#define QZ_F2 0x78 +#define QZ_F3 0x63 +#define QZ_F4 0x76 +#define QZ_F5 0x60 +#define QZ_F6 0x61 +#define QZ_F7 0x62 +#define QZ_F8 0x64 +#define QZ_F9 0x65 +#define QZ_F10 0x6D +#define QZ_F11 0x67 +#define QZ_F12 0x6F +#define QZ_PRINT 0x69 +#define QZ_SCROLLOCK 0x6B +#define QZ_PAUSE 0x71 +#define QZ_POWER 0x7F +#define QZ_BACKQUOTE 0x32 +#define QZ_1 0x12 +#define QZ_2 0x13 +#define QZ_3 0x14 +#define QZ_4 0x15 +#define QZ_5 0x17 +#define QZ_6 0x16 +#define QZ_7 0x1A +#define QZ_8 0x1C +#define QZ_9 0x19 +#define QZ_0 0x1D +#define QZ_MINUS 0x1B +#define QZ_EQUALS 0x18 +#define QZ_BACKSPACE 0x33 +#define QZ_INSERT 0x72 +#define QZ_HOME 0x73 +#define QZ_PAGEUP 0x74 +#define QZ_NUMLOCK 0x47 +#define QZ_KP_EQUALS 0x51 +#define QZ_KP_DIVIDE 0x4B +#define QZ_KP_MULTIPLY 0x43 +#define QZ_TAB 0x30 +#define QZ_q 0x0C +#define QZ_w 0x0D +#define QZ_e 0x0E +#define QZ_r 0x0F +#define QZ_t 0x11 +#define QZ_y 0x10 +#define QZ_u 0x20 +#define QZ_i 0x22 +#define QZ_o 0x1F +#define QZ_p 0x23 +#define QZ_LEFTBRACKET 0x21 +#define QZ_RIGHTBRACKET 0x1E +#define QZ_BACKSLASH 0x2A +#define QZ_DELETE 0x75 +#define QZ_END 0x77 +#define QZ_PAGEDOWN 0x79 +#define QZ_KP7 0x59 +#define QZ_KP8 0x5B +#define QZ_KP9 0x5C +#define QZ_KP_MINUS 0x4E +#define QZ_CAPSLOCK 0x39 +#define QZ_a 0x00 +#define QZ_s 0x01 +#define QZ_d 0x02 +#define QZ_f 0x03 +#define QZ_g 0x05 +#define QZ_h 0x04 +#define QZ_j 0x26 +#define QZ_k 0x28 +#define QZ_l 0x25 +#define QZ_SEMICOLON 0x29 +#define QZ_QUOTE 0x27 +#define QZ_RETURN 0x24 +#define QZ_KP4 0x56 +#define QZ_KP5 0x57 +#define QZ_KP6 0x58 +#define QZ_KP_PLUS 0x45 +#define QZ_LSHIFT 0x38 +#define QZ_z 0x06 +#define QZ_x 0x07 +#define QZ_c 0x08 +#define QZ_v 0x09 +#define QZ_b 0x0B +#define QZ_n 0x2D +#define QZ_m 0x2E +#define QZ_COMMA 0x2B +#define QZ_PERIOD 0x2F +#define QZ_SLASH 0x2C +/* These are the same as the left versions - use left by default */ +#if 0 +#define QZ_RSHIFT 0x38 +#endif +#define QZ_UP 0x7E +#define QZ_KP1 0x53 +#define QZ_KP2 0x54 +#define QZ_KP3 0x55 +#define QZ_KP_ENTER 0x4C +#define QZ_LCTRL 0x3B +#define QZ_LALT 0x3A +#define QZ_LMETA 0x37 +#define QZ_SPACE 0x31 +/* These are the same as the left versions - use left by default */ +#if 0 +#define QZ_RMETA 0x37 +#define QZ_RALT 0x3A +#define QZ_RCTRL 0x3B +#endif +#define QZ_LEFT 0x7B +#define QZ_DOWN 0x7D +#define QZ_RIGHT 0x7C +#define QZ_KP0 0x52 +#define QZ_KP_PERIOD 0x41 + +/* Wierd, these keys are on my iBook under MacOS X */ +#define QZ_IBOOK_ENTER 0x34 +#define QZ_IBOOK_LEFT 0x3B +#define QZ_IBOOK_RIGHT 0x3C +#define QZ_IBOOK_DOWN 0x3D +#define QZ_IBOOK_UP 0x3E +#define KEY_ENTER 13 +#define KEY_TAB 9 + +#define KEY_BASE 0x100 + +/* Function keys */ +#define KEY_F (KEY_BASE+64) + +/* Control keys */ +#define KEY_CTRL (KEY_BASE) +#define KEY_BACKSPACE (KEY_CTRL+0) +#define KEY_DELETE (KEY_CTRL+1) +#define KEY_INSERT (KEY_CTRL+2) +#define KEY_HOME (KEY_CTRL+3) +#define KEY_END (KEY_CTRL+4) +#define KEY_PAGE_UP (KEY_CTRL+5) +#define KEY_PAGE_DOWN (KEY_CTRL+6) +#define KEY_ESC (KEY_CTRL+7) + +/* Control keys short name */ +#define KEY_BS KEY_BACKSPACE +#define KEY_DEL KEY_DELETE +#define KEY_INS KEY_INSERT +#define KEY_PGUP KEY_PAGE_UP +#define KEY_PGDOWN KEY_PAGE_DOWN +#define KEY_PGDWN KEY_PAGE_DOWN + +/* Cursor movement */ +#define KEY_CRSR (KEY_BASE+16) +#define KEY_RIGHT (KEY_CRSR+0) +#define KEY_LEFT (KEY_CRSR+1) +#define KEY_DOWN (KEY_CRSR+2) +#define KEY_UP (KEY_CRSR+3) + +/* Multimedia keyboard/remote keys */ +#define KEY_MM_BASE (0x100+384) +#define KEY_POWER (KEY_MM_BASE+0) +#define KEY_MENU (KEY_MM_BASE+1) +#define KEY_PLAY (KEY_MM_BASE+2) +#define KEY_PAUSE (KEY_MM_BASE+3) +#define KEY_PLAYPAUSE (KEY_MM_BASE+4) +#define KEY_STOP (KEY_MM_BASE+5) +#define KEY_FORWARD (KEY_MM_BASE+6) +#define KEY_REWIND (KEY_MM_BASE+7) +#define KEY_NEXT (KEY_MM_BASE+8) +#define KEY_PREV (KEY_MM_BASE+9) +#define KEY_VOLUME_UP (KEY_MM_BASE+10) +#define KEY_VOLUME_DOWN (KEY_MM_BASE+11) +#define KEY_MUTE (KEY_MM_BASE+12) + +/* Keypad keys */ +#define KEY_KEYPAD (KEY_BASE+32) +#define KEY_KP0 (KEY_KEYPAD+0) +#define KEY_KP1 (KEY_KEYPAD+1) +#define KEY_KP2 (KEY_KEYPAD+2) +#define KEY_KP3 (KEY_KEYPAD+3) +#define KEY_KP4 (KEY_KEYPAD+4) +#define KEY_KP5 (KEY_KEYPAD+5) +#define KEY_KP6 (KEY_KEYPAD+6) +#define KEY_KP7 (KEY_KEYPAD+7) +#define KEY_KP8 (KEY_KEYPAD+8) +#define KEY_KP9 (KEY_KEYPAD+9) +#define KEY_KPDEC (KEY_KEYPAD+10) +#define KEY_KPINS (KEY_KEYPAD+11) +#define KEY_KPDEL (KEY_KEYPAD+12) +#define KEY_KPENTER (KEY_KEYPAD+13) + +/* Special keys */ +#define KEY_INTERN (0x1000) +#define KEY_CLOSE_WIN (KEY_INTERN+0) diff --git a/emu/MacOSX/mkfile b/emu/MacOSX/mkfile new file mode 100644 index 00000000..5f716701 --- /dev/null +++ b/emu/MacOSX/mkfile @@ -0,0 +1,63 @@ +SYSTARG=MacOSX +#OBJTYPE=power +<../../mkconfig +SYSTARG=MacOSX +#OBJTYPE=power + +#Configurable parameters + +CONF=emu #default configuration +CONFLIST=emu +CLEANCONFLIST= + +INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed + +#end configurable parameters + +<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system + +<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS + +OBJ=\ + asm-$OBJTYPE.$O\ + os.$O\ + $CONF.root.$O\ + lock.$O\ + $DEVS\ + $PORT\ + +HFILES=\ + +CFLAGS='-DROOT="'$ROOT'"'\ + '-DOBJTYPE="'$OBJTYPE'"'\ + -DEMU -I. -I../port\ + -I$ROOT/$SYSTARG/$OBJTYPE/include\ + -I$ROOT/include -I$ROOT/libinterp\ + $CTHREADFLAGS $CFLAGS $EMUOPTIONS\ + +KERNDATE=`{$NDATE} + +LDFLAGS=$LDFLAGS -L/usr/X11R6/lib + +SYSLIBS= \ + -lm\ + -framework Carbon -framework QuickTime\ + -lpthread\ + -framework CoreFoundation\ + -framework IOKit\ +# -framework ApplicationServices\ + +default:V: $O.$CONF + + +$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBFILES + $CC $CFLAGS -DKERNDATE=$KERNDATE $CONF.c + $LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS + +install:V: $O.$CONF + cp $O.$CONF $INSTALLDIR/$CONF + +<../port/portmkfile + +ipif.c:N: ../FreeBSD/ipif.c +devfs.c:N: ../port/devfs-posix.c diff --git a/emu/MacOSX/mkfile-g b/emu/MacOSX/mkfile-g new file mode 100644 index 00000000..c1f9fa4b --- /dev/null +++ b/emu/MacOSX/mkfile-g @@ -0,0 +1,62 @@ +SYSTARG=MacOSX +#OBJTYPE=power +<../../mkconfig +SYSTARG=MacOSX +#OBJTYPE=power + +#Configurable parameters + +CONF=emu #default configuration +CONFLIST=emu +CLEANCONFLIST= + +INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed + +#end configurable parameters + +<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system + +<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS + +OBJ=\ + asm-$OBJTYPE.$O\ + os.$O\ + $CONF.root.$O\ + lock.$O\ + $DEVS\ + $PORT\ + +HFILES=\ + +CFLAGS='-DROOT="'$ROOT'"'\ + '-DOBJTYPE="'$OBJTYPE'"'\ + -DEMU -I. -I../port\ + -I$ROOT/$SYSTARG/$OBJTYPE/include\ + -I$ROOT/include -I$ROOT/libinterp\ + $CTHREADFLAGS $CFLAGS $EMUOPTIONS\ + +KERNDATE=`{$NDATE} + +LDFLAGS=$LDFLAGS -L/usr/X11R6/lib + +SYSLIBS= \ + -lm\ + -lpthread\ + -framework CoreFoundation\ + -framework IOKit\ +# -framework ApplicationServices\ + +default:V: $O.$CONF + + +$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBFILES + $CC $CFLAGS -DKERNDATE=$KERNDATE $CONF.c + $LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS + +install:V: $O.$CONF + cp $O.$CONF $INSTALLDIR/$CONF + +<../port/portmkfile + +ipif.c:N: ../FreeBSD/ipif.c +devfs.c:N: ../port/devfs-posix.c diff --git a/emu/MacOSX/mkfile-x11 b/emu/MacOSX/mkfile-x11 new file mode 100644 index 00000000..25fb99c6 --- /dev/null +++ b/emu/MacOSX/mkfile-x11 @@ -0,0 +1,64 @@ +SYSTARG=MacOSX +#OBJTYPE=power +<../../mkconfig +SYSTARG=MacOSX +#OBJTYPE=power + +#Configurable parameters + +CONF=emu #default configuration +CONFLIST=emu +CLEANCONFLIST= + +INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed + +#end configurable parameters + +<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system + +<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS + +OBJ=\ + asm-$OBJTYPE.$O\ + os.$O\ + win-x11a.$O\ + $CONF.root.$O\ + lock.$O\ + $DEVS\ + $PORT\ + +HFILES=\ + +CFLAGS='-DROOT="'$ROOT'"'\ + '-DOBJTYPE="'$OBJTYPE'"'\ + -DEMU -I. -I../port\ + -I$ROOT/$SYSTARG/$OBJTYPE/include\ + -I$ROOT/include -I$ROOT/libinterp\ + $CTHREADFLAGS $CFLAGS $EMUOPTIONS\ + -I/usr/X11R6/include + +KERNDATE=`{$NDATE} + +LDFLAGS=$LDFLAGS -L/usr/X11R6/lib + +SYSLIBS= \ + -lm -lX11 -lXext\ + -lpthread\ + -framework CoreFoundation\ + -framework IOKit\ +# -framework ApplicationServices\ + +default:V: $O.$CONF + + +$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBFILES + $CC $CFLAGS -DKERNDATE=$KERNDATE $CONF.c + $LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS + +install:V: $O.$CONF + cp $O.$CONF $INSTALLDIR/$CONF + +<../port/portmkfile + +ipif.c:N: ../FreeBSD/ipif.c +devfs.c:N: ../port/devfs-posix.c diff --git a/emu/MacOSX/os.c b/emu/MacOSX/os.c new file mode 100644 index 00000000..3363b5ad --- /dev/null +++ b/emu/MacOSX/os.c @@ -0,0 +1,647 @@ +/* + * Loosely based on FreeBSD/os.c and Solaris/os.c + * Copyright © 1998, 1999 Lucent Technologies Inc. All rights reserved. + * Revisions Copyright © 1999, 2000 Vita Nuova Limited. All rights reserved. + * Revisions Copyright © 2002, 2003 Corpus Callosum Corporation. All rights reserved. + */ + +#include "dat.h" +#include "fns.h" +#include "error.h" + +#undef _POSIX_C_SOURCE +#undef getwd + +#include <unistd.h> +#include <pthread.h> +#include <time.h> +#include <termios.h> +#include <signal.h> +#include <pwd.h> +#include <sys/resource.h> +#include <sys/time.h> + +#include <sys/socket.h> +#include <sched.h> +#include <errno.h> +#include <sys/ucontext.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <mach/mach_init.h> +#include <mach/task.h> +#include <mach/vm_map.h> + +#if defined(__ppc__) +#include <architecture/ppc/cframe.h> +#endif + +enum +{ + DELETE = 0x7F +}; +char *hosttype = "MacOSX"; +char *cputype = OBJTYPE; + +static pthread_key_t prdakey; + +extern int dflag; + +Proc * +getup(void) { + return (Proc *)pthread_getspecific(prdakey); +} + +/* Pthread version */ +void +pexit(char *msg, int t) +{ + Osenv *e; + Proc *p; + + USED(t); + USED(msg); + + lock(&procs.l); + p = up; + if(p->prev) + p->prev->next = p->next; + else + procs.head = p->next; + + if(p->next) + p->next->prev = p->prev; + else + procs.tail = p->prev; + unlock(&procs.l); + + if(0) + print("pexit: %s: %s\n", p->text, msg); + + e = p->env; + if(e != nil) { + closefgrp(e->fgrp); + closepgrp(e->pgrp); + closeegrp(e->egrp); + closesigs(e->sigs); + } + free(e->user); + free(p->prog); + free(p); + pthread_exit(0); +} + +void +trapBUS(int signo) +{ + USED(signo); + disfault(nil, "Bus error"); +} + +void +trapUSR1(int signo) +{ + USED(signo); + + if(up->type != Interp) /* Used to unblock pending I/O */ + return; + if(up->intwait == 0) /* Not posted so its a sync error */ + disfault(nil, Eintr); /* Should never happen */ + + up->intwait = 0; /* Clear it so the proc can continue */ +} + +void +trapILL(int signo) +{ + USED(signo); + disfault(nil, "Illegal instruction"); +} + +/* from geoff collyer's port */ +void +printILL(int sig, siginfo_t *siginfo, void *v) +{ + USED(sig); + USED(v); + panic("Illegal instruction with code=%d at address=%x, opcode=%x.\n" + ,siginfo->si_code, siginfo->si_addr,*(char*)siginfo->si_addr); +} + +void +trapSEGV(int signo) +{ + USED(signo); + disfault(nil, "Segmentation violation"); +} + +void +trapFPE(int signo) +{ + USED(signo); + disfault(nil, "Floating point exception"); +} + +static void +setsigs(void) +{ + struct sigaction act; + + memset(&act, 0 , sizeof(act)); + + /* + * For the correct functioning of devcmd in the + * face of exiting slaves + */ + signal(SIGPIPE, SIG_IGN); + if(signal(SIGTERM, SIG_IGN) != SIG_IGN) + signal(SIGTERM, cleanexit); + + act.sa_handler=trapUSR1; + sigaction(SIGUSR1, &act, nil); + + if(sflag == 0) { + act.sa_handler = trapBUS; + sigaction(SIGBUS, &act, nil); + act.sa_handler = trapILL; + sigaction(SIGILL, &act, nil); + act.sa_handler = trapSEGV; + sigaction(SIGSEGV, &act, nil); + act.sa_handler = trapFPE; + sigaction(SIGFPE, &act, nil); + if(signal(SIGINT, SIG_IGN) != SIG_IGN) + signal(SIGINT, cleanexit); + } else { + act.sa_sigaction = printILL; + act.sa_flags=SA_SIGINFO; + sigaction(SIGILL, &act, nil); + } +} + + +void * +tramp(void *arg) +{ + Proc *p = arg; + p->sigid = (int)pthread_self(); + if(pthread_setspecific(prdakey, arg)) { + print("set specific data failed in tramp\n"); + pthread_exit(0); + } + p->func(p->arg); + pexit("{Tramp}", 0); + return NULL; +} + +int +kproc(char *name, void (*func)(void*), void *arg, int flags) +{ + pthread_t thread; + Proc *p; + Pgrp *pg; + Fgrp *fg; + Egrp *eg; + + pthread_attr_t attr; + + p = newproc(); + if(p == nil) + panic("kproc: no memory"); + + if(flags & KPDUPPG) { + pg = up->env->pgrp; + incref(&pg->r); + p->env->pgrp = pg; + } + if(flags & KPDUPFDG) { + fg = up->env->fgrp; + incref(&fg->r); + p->env->fgrp = fg; + } + if(flags & KPDUPENVG) { + eg = up->env->egrp; + incref(&eg->r); + p->env->egrp = eg; + } + + p->env->uid = up->env->uid; + p->env->gid = up->env->gid; + kstrdup(&p->env->user, up->env->user); + + strcpy(p->text, name); + + p->func = func; + p->arg = arg; + + lock(&procs.l); + if(procs.tail != nil) { + p->prev = procs.tail; + procs.tail->next = p; + } else { + procs.head = p; + p->prev = nil; + } + procs.tail = p; + unlock(&procs.l); + + up->kid = p; + + if((pthread_attr_init(&attr))== -1) + panic("pthread_attr_init failed"); + + errno=0; + pthread_attr_setschedpolicy(&attr,SCHED_OTHER); + if(errno) + panic("pthread_attr_setschedpolicy failed"); + + pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + if(pthread_create(&thread, &attr, tramp, p)) + panic("thr_create failed\n"); + pthread_attr_destroy(&attr); + + return (int)thread; +} + +int +segflush(void *va, ulong len) +{ + kern_return_t err; + vm_machine_attribute_val_t value = MATTR_VAL_ICACHE_FLUSH; + + err = vm_machine_attribute( (vm_map_t)mach_task_self(), + (vm_address_t)va, + (vm_size_t)len, + MATTR_CACHE, + &value); + if (err != KERN_SUCCESS) { + print("segflush: failure (%d) address %lud\n", err, va); + } + return (int)err; +} + +/* from geoff collyer's port +invalidate instruction cache and write back data cache from a to a+n-1, +at least. + +void +segflush(void *a, ulong n) +{ + ulong *p; + + // paranoia, flush the world + __asm__("isync\n\t" + "eieio\n\t" + : // no output + : + ); + // cache blocks are often eight words (32 bytes) long, sometimes 16 bytes. + // need to determine it dynamically? + for (p = (ulong *)((ulong)a & ~3UL); (char *)p < (char *)a + n; p++) + __asm__("dcbst 0,%0\n\t" // not dcbf, which writes back, then invalidates + "icbi 0,%0\n\t" + : // no output + : "ar" (p) + ); + __asm__("isync\n\t" + "eieio\n\t" + : // no output + : + ); +} +*/ + +void +oshostintr(Proc *p) +{ + pthread_kill((pthread_t)p->sigid, SIGUSR1); +} + +static ulong erendezvous(void*, ulong); + +void +osblock(void) +{ + erendezvous(up, 0); +} + +void +osready(Proc *p) +{ + erendezvous(p, 0); +} + +void +oslongjmp(void *regs, osjmpbuf env, int val) +{ + USED(regs); + siglongjmp(env, val); +} + +struct termios tinit; + +static void +termset(void) +{ + struct termios t; + + tcgetattr(0, &t); + tinit = t; + t.c_lflag &= ~(ICANON|ECHO|ISIG); + t.c_cc[VMIN] = 1; + t.c_cc[VTIME] = 0; + tcsetattr(0, TCSANOW, &t); +} + +static void +termrestore(void) +{ + tcsetattr(0, TCSANOW, &tinit); +} + + +void +cleanexit(int x) +{ + USED(x); + + if(up->intwait) { + up->intwait = 0; + return; + } + + if(dflag == 0) + termrestore(); + + exit(0); +} + +void +osreboot(char *file, char **argv) +{ + if(dflag == 0) + termrestore(); + execvp(file, argv); + panic("reboot failure"); +} + +int gidnobody= -1, uidnobody= -1; + +void +getnobody() +{ + struct passwd *pwd; + + if((pwd = getpwnam("nobody"))) { + uidnobody = pwd->pw_uid; + gidnobody = pwd->pw_gid; + } +} + +/* Pthread version */ +static pthread_mutex_t rendezvouslock; +static pthread_mutexattr_t *pthread_mutexattr_default = NULL; + +void +libinit(char *imod) +{ + struct passwd *pw; + Proc *p; + char sys[64]; + + setsid(); + + // setup personality + gethostname(sys, sizeof(sys)); + kstrdup(&ossysname, sys); + getnobody(); + + if(dflag == 0) + termset(); + + setsigs(); + + if(pthread_mutex_init(&rendezvouslock, pthread_mutexattr_default)) + panic("pthread_mutex_init"); + + if(pthread_key_create(&prdakey,NULL)) + print("key_create failed\n"); + + p = newproc(); + if(pthread_setspecific(prdakey, p)) + panic("set specific thread data failed\n"); + + pw = getpwuid(getuid()); + if(pw != nil) + kstrdup(&eve, pw->pw_name); + else + print("cannot getpwuid\n"); + + up->env->uid = getuid(); + up->env->gid = getgid(); + + emuinit(imod); +} + +int +readkbd(void) +{ + int n; + char buf[1]; + + n = read(0, buf, sizeof(buf)); + if(n < 0) + print("keyboard close (n=%d, %s)\n", n, strerror(errno)); + if(n <= 0) + pexit("keyboard thread", 0); + + switch(buf[0]) { + case '\r': + buf[0] = '\n'; + break; + case DELETE: + cleanexit(0); + break; + } + return buf[0]; +} + +enum +{ + NHLOG = 7, + NHASH = (1<<NHLOG) +}; + +typedef struct Tag Tag; +struct Tag +{ + void* tag; + ulong val; + pthread_cond_t cv; + Tag* next; +}; + +static Tag* ht[NHASH]; +static Tag* ft; +//static Lock hlock; + +static ulong +erendezvous(void *tag, ulong value) +{ + int h; + ulong rval; + Tag *t, **l, *f; + + h = (ulong)tag & (NHASH-1); + +// lock(&hlock); + pthread_mutex_lock(&rendezvouslock); + l = &ht[h]; + for(t = ht[h]; t; t = t->next) { + if(t->tag == tag) { + rval = t->val; + t->val = value; + t->tag = 0; + pthread_mutex_unlock(&rendezvouslock); +// unlock(&hlock); + if(pthread_cond_signal(&(t->cv))) + panic("pthread_cond_signal"); + return rval; + } + } + + t = ft; + if(t == 0) { + t = malloc(sizeof(Tag)); + if(t == nil) + panic("rendezvous: no memory"); + if(pthread_cond_init(&(t->cv), NULL)) { + print("pthread_cond_init (errno: %s) \n", strerror(errno)); + panic("pthread_cond_init"); + } + } else + ft = t->next; + + t->tag = tag; + t->val = value; + t->next = *l; + *l = t; +// pthread_mutex_unlock(&rendezvouslock); +// unlock(&hlock); + + while(t->tag != nil) + pthread_cond_wait(&(t->cv),&rendezvouslock); + +// pthread_mutex_lock(&rendezvouslock); +// lock(&hlock); + rval = t->val; + for(f = *l; f; f = f->next){ + if(f == t) { + *l = f->next; + break; + } + l = &f->next; + } + t->next = ft; + ft = t; + pthread_mutex_unlock(&rendezvouslock); +// unlock(&hlock); + + return rval; +} + +/* + * Return an abitrary millisecond clock time + */ +long +osmillisec(void) +{ + static long sec0 = 0, usec0; + struct timeval t; + + if(gettimeofday(&t, NULL)<0) + return(0); + if(sec0==0) { + sec0 = t.tv_sec; + usec0 = t.tv_usec; + } + return((t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000); +} + +/* + * Return the time since the epoch in nanoseconds and microseconds + * The epoch is defined at 1 Jan 1970 + */ +vlong +osnsec(void) +{ + struct timeval t; + + gettimeofday(&t, nil); + return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000; +} + +vlong +osusectime(void) +{ + struct timeval t; + + gettimeofday(&t, nil); + return (vlong)t.tv_sec * 1000000 + t.tv_usec; +} + + +int +osmillisleep(ulong milsec) +{ + struct timespec time; + time.tv_sec = milsec / 1000; + time.tv_nsec = (milsec % 1000) * 1000000; + nanosleep(&time, nil); + return 0; +} + +int +limbosleep(ulong milsec) +{ + return osmillisleep(milsec); +} + +void +osyield(void) +{ + pthread_yield_np(); + // sched_yield(); +} + +void +ospause(void) +{ + for(;;) + pause(); +} + +void +oslopri(void) +{ +// pthread_setschedparam(pthread_t thread, int policy, const struct sched_param *param); + setpriority(PRIO_PROCESS, 0, getpriority(PRIO_PROCESS,0)+4); +} + +__typeof__(sbrk(0)) +sbrk(int size) +{ + void *brk; + kern_return_t err; + + err = vm_allocate( (vm_map_t) mach_task_self(), + (vm_address_t *)&brk, + size, + VM_FLAGS_ANYWHERE); + if (err != KERN_SUCCESS) + brk = (void*)-1; + + return brk; +} diff --git a/emu/MacOSX/win.c b/emu/MacOSX/win.c new file mode 100644 index 00000000..41ca8d9d --- /dev/null +++ b/emu/MacOSX/win.c @@ -0,0 +1,755 @@ +// in this file, _Rect is os x Rect, +// _Point is os x Point +#define Point _Point +#define Rect _Rect + +#include <Carbon/Carbon.h> +#include <QuickTime/QuickTime.h> // for full screen + +#undef Rect +#undef Point + +#undef nil + +#include "dat.h" +#include "fns.h" +#undef log2 +#include <draw.h> +#include <memdraw.h> +#include "cursor.h" +#include "keyboard.h" +#include "keycodes.h" + +#define Kup Up +#define Kleft Left +#define Kdown Down +#define Kright Right +#define Kalt LAlt +#define Kctl LCtrl +#define Kshift LShift +#define Kpgup Pgup +#define Kpgdown Pgdown +#define Khome Home +#define Kins Ins +#define Kend End + +#define rWindowResource 128 + +extern void flushmemscreen(Rectangle); + +Memimage *gscreen; + +static int readybit; +static Rendez rend; +static int triedscreen; + +/// +// menu +// +static MenuRef windMenu; +static MenuRef viewMenu; + +enum { + kQuitCmd = 1, + kFullScreenCmd = 2, +}; + +static WindowGroupRef winGroup = NULL; +static WindowRef theWindow = NULL; +static CGContextRef context; +static CGDataProviderRef dataProviderRef; +static CGImageRef fullScreenImage; +static CGRect devRect; +static CGRect bounds; +static PasteboardRef appleclip; +static _Rect winRect; + +static Boolean altPressed = false; +static Boolean button2 = false; +static Boolean button3 = false; + +static Boolean needflush = false; + + +static int +isready(void*a) +{ + return readybit; +} + +CGContextRef QuartzContext; + +static OSStatus MainWindowEventHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData); +static OSStatus MainWindowCommandHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData); + +static void winproc(void *a); +static void flushproc(void *a); + +void +screeninit(void) +{ + int fmt; + int dx, dy; + ProcessSerialNumber psn = { 0, kCurrentProcess }; + TransformProcessType(&psn, kProcessTransformToForegroundApplication); + SetFrontProcess(&psn); + + fmt = XBGR32; //XRGB32; + + devRect = CGDisplayBounds(CGMainDisplayID()); +// devRect.origin.x = 0; +// devRect.origin.y = 0; +// devRect.size.width = 1024; +// devRect.size.height = 768; + dx = devRect.size.width; + dy = devRect.size.height; + + if(1){ /* TO DO: new dev draw for changing screen size */ + dx = Xsize; + dy = Ysize; + } + + gscreen = allocmemimage(Rect(0,0,dx,dy), fmt); + dataProviderRef = CGDataProviderCreateWithData(0, gscreen->data->bdata, + dx * dy * 4, 0); + fullScreenImage = CGImageCreate(dx, dy, 8, 32, dx * 4, + CGColorSpaceCreateDeviceRGB(), + kCGImageAlphaNoneSkipLast, + dataProviderRef, 0, 0, kCGRenderingIntentDefault); + + kproc("osxscreen", winproc, nil, 0); + kproc("osxflush", flushproc, nil, 0); + Sleep(&rend, isready, nil); +} + +void +window_resized(void) +{ + GetWindowBounds(theWindow, kWindowContentRgn, &winRect); + + bounds = CGRectMake(0, 0, winRect.right-winRect.left, winRect.bottom - winRect.top); +} + +static void +flushproc(void *a) +{ + for(;;) { + if(needflush) { + drawqlock(); + QDBeginCGContext(GetWindowPort(theWindow), &context); + CGContextFlush(context); + QDEndCGContext(GetWindowPort(theWindow), &context); + needflush = false; + drawqunlock(); + } + usleep(33333); + } +} + +static void +winproc(void *a) +{ + MenuItemIndex index; + int dx, dy; + + winRect.left = 30; + winRect.top = 60; + dx = devRect.size.width*0.75; /* devRect is full screen; take only most of it */ + dy = devRect.size.height*0.75; + if(1){ /* TO DO */ + dx = Xsize; + dy = Ysize; + } + winRect.bottom = winRect.top + dy; + winRect.right = winRect.left + dx; + + ClearMenuBar(); + InitCursor(); + + CreateStandardWindowMenu(0, &windMenu); + InsertMenu(windMenu, 0); + + CreateNewMenu(1004, 0, &viewMenu); + SetMenuTitleWithCFString(viewMenu, CFSTR("View")); + AppendMenuItemTextWithCFString(viewMenu, CFSTR("Full Screen"), 0, + kFullScreenCmd, &index); + SetMenuItemCommandKey(viewMenu, index, 0, 'F'); + AppendMenuItemTextWithCFString(viewMenu, CFSTR("ctrl-opt to return"), + kMenuItemAttrDisabled, + kFullScreenCmd, &index); + InsertMenu(viewMenu, GetMenuID(windMenu)); + + DrawMenuBar(); + uint32_t windowAttrs = 0 + | kWindowCloseBoxAttribute + | kWindowCollapseBoxAttribute +// | kWindowResizableAttribute // TO DO + | kWindowStandardHandlerAttribute +// | kWindowFullZoomAttribute // TO DO + ; + + CreateNewWindow(kDocumentWindowClass, windowAttrs, &winRect, &theWindow); + CreateWindowGroup(0, &winGroup); + SetWindowGroup(theWindow, winGroup); + + SetWindowTitleWithCFString(theWindow, CFSTR("Inferno")); + + if(PasteboardCreate(kPasteboardClipboard, &appleclip) != noErr) + sysfatal("pasteboard create failed"); + + const EventTypeSpec commands[] = { + { kEventClassWindow, kEventWindowClosed }, + { kEventClassWindow, kEventWindowBoundsChanged }, + { kEventClassCommand, kEventCommandProcess } + }; + const EventTypeSpec events[] = { + { kEventClassKeyboard, kEventRawKeyDown }, + { kEventClassKeyboard, kEventRawKeyModifiersChanged }, + { kEventClassKeyboard, kEventRawKeyRepeat }, + { kEventClassMouse, kEventMouseDown }, + { kEventClassMouse, kEventMouseUp }, + { kEventClassMouse, kEventMouseMoved }, + { kEventClassMouse, kEventMouseDragged }, + { kEventClassMouse, kEventMouseWheelMoved }, + }; + + InstallApplicationEventHandler ( + NewEventHandlerUPP (MainWindowEventHandler), + GetEventTypeCount(events), + events, + NULL, + NULL); + InstallWindowEventHandler ( + theWindow, + NewEventHandlerUPP (MainWindowCommandHandler), + GetEventTypeCount(commands), + commands, + theWindow, + NULL); + + ShowWindow(theWindow); + ShowMenuBar(); + window_resized(); + SelectWindow(theWindow); + // Run the event loop + readybit = 1; + Wakeup(&rend); + RunApplicationEventLoop(); + +} + +static int +convert_key(UInt32 key, UInt32 charcode) +{ + switch(key) { + case QZ_IBOOK_ENTER: + case QZ_RETURN: return '\n'; + case QZ_ESCAPE: return 27; + case QZ_BACKSPACE: return '\b'; + case QZ_LALT: return Kalt; + case QZ_LCTRL: return Kctl; + case QZ_LSHIFT: return Kshift; + case QZ_F1: return KF+1; + case QZ_F2: return KF+2; + case QZ_F3: return KF+3; + case QZ_F4: return KF+4; + case QZ_F5: return KF+5; + case QZ_F6: return KF+6; + case QZ_F7: return KF+7; + case QZ_F8: return KF+8; + case QZ_F9: return KF+9; + case QZ_F10: return KF+10; + case QZ_F11: return KF+11; + case QZ_F12: return KF+12; + case QZ_INSERT: return Kins; + case QZ_DELETE: return 0x7F; + case QZ_HOME: return Khome; + case QZ_END: return Kend; + case QZ_KP_PLUS: return '+'; + case QZ_KP_MINUS: return '-'; + case QZ_TAB: return '\t'; + case QZ_PAGEUP: return Kpgup; + case QZ_PAGEDOWN: return Kpgdown; + case QZ_UP: return Kup; + case QZ_DOWN: return Kdown; + case QZ_LEFT: return Kleft; + case QZ_RIGHT: return Kright; + case QZ_KP_MULTIPLY: return '*'; + case QZ_KP_DIVIDE: return '/'; + case QZ_KP_ENTER: return '\n'; + case QZ_KP_PERIOD: return '.'; + case QZ_KP0: return '0'; + case QZ_KP1: return '1'; + case QZ_KP2: return '2'; + case QZ_KP3: return '3'; + case QZ_KP4: return '4'; + case QZ_KP5: return '5'; + case QZ_KP6: return '6'; + case QZ_KP7: return '7'; + case QZ_KP8: return '8'; + case QZ_KP9: return '9'; + default: return charcode; + } +} + +void +sendbuttons(int b, int x, int y) +{ + mousetrack(b, x, y, 0); +} + +static Ptr fullScreenRestore; +static int amFullScreen = 0; +static WindowRef oldWindow = NULL; + +static void +leave_full_screen(void) +{ + if (0 && amFullScreen) { + EndFullScreen(fullScreenRestore, 0); + theWindow = oldWindow; + ShowWindow(theWindow); + amFullScreen = 0; + window_resized(); + Rectangle rect = { { 0, 0 }, { bounds.size.width, bounds.size.height} }; + drawqlock(); + flushmemscreen(rect); + drawqunlock(); + } +} + +static void +full_screen(void) +{ + if (0 && !amFullScreen) { + oldWindow = theWindow; + HideWindow(theWindow); + BeginFullScreen(&fullScreenRestore, 0, 0, 0, &theWindow, 0, 0); + amFullScreen = 1; + window_resized(); + Rectangle rect = { { 0, 0 }, + { bounds.size.width, + bounds.size.height} }; + drawqlock(); + flushmemscreen(rect); + drawqunlock(); + } +} + +static OSStatus +MainWindowEventHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData) +{ + OSStatus result = noErr; + result = CallNextEventHandler(nextHandler, event); + UInt32 class = GetEventClass (event); + UInt32 kind = GetEventKind (event); + static uint32_t mousebuttons = 0; // bitmask of buttons currently down + static uint32_t mouseX = 0; + static uint32_t mouseY = 0; + + if(class == kEventClassKeyboard) { + char macCharCodes; + UInt32 macKeyCode; + UInt32 macKeyModifiers; + + GetEventParameter(event, kEventParamKeyMacCharCodes, typeChar, + NULL, sizeof(macCharCodes), NULL, &macCharCodes); + GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, + sizeof(macKeyCode), NULL, &macKeyCode); + GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, + sizeof(macKeyModifiers), NULL, &macKeyModifiers); + switch(kind) { + case kEventRawKeyModifiersChanged: + if (macKeyModifiers == (controlKey | optionKey)) leave_full_screen(); + + switch(macKeyModifiers & (optionKey | cmdKey)) { + case (optionKey | cmdKey): + /* due to chording we need to handle the case when both + * modifier keys are pressed at the same time. + * currently it's only 2-3 snarf and the 3-2 noop + */ + altPressed = true; + if(mousebuttons & 1 || mousebuttons & 2 || mousebuttons & 4) { + mousebuttons |= 2; /* set button 2 */ + mousebuttons |= 4; /* set button 3 */ + button2 = true; + button3 = true; + sendbuttons(mousebuttons, mouseX, mouseY); + } + break; + case optionKey: + altPressed = true; + if(mousebuttons & 1 || mousebuttons & 4) { + mousebuttons |= 2; /* set button 2 */ + button2 = true; + sendbuttons(mousebuttons, mouseX, mouseY); + } + break; + case cmdKey: + if(mousebuttons & 1 || mousebuttons & 2) { + mousebuttons |= 4; /* set button 3 */ + button3 = true; + sendbuttons(mousebuttons, mouseX, mouseY); + }else + gkbdputc(gkbdq, Latin); + break; + case 0: + default: + if(button2 || button3) { + if(button2) { + mousebuttons &= ~2; /* clear button 2 */ + button2 = false; + altPressed = false; + } + if(button3) { + mousebuttons &= ~4; /* clear button 3 */ + button3 = false; + } + sendbuttons(mousebuttons, mouseX, mouseY); + } + if(altPressed) { + gkbdputc(gkbdq, Kalt); + altPressed = false; + } + break; + } + break; + case kEventRawKeyDown: + case kEventRawKeyRepeat: + if(macKeyModifiers != cmdKey) { + int key; + key = convert_key(macKeyCode, macCharCodes); + if(key != -1) + gkbdputc(gkbdq, key); + }else + result = eventNotHandledErr; + break; + default: + break; + } + } + else if(class == kEventClassMouse) { + _Point mousePos; + + GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, + 0, sizeof mousePos, 0, &mousePos); + + switch(kind) { + case kEventMouseWheelMoved: + { + int32_t wheeldelta; + GetEventParameter(event,kEventParamMouseWheelDelta,typeSInt32, + 0,sizeof(wheeldelta), 0, &wheeldelta); + mouseX = mousePos.h - winRect.left; + mouseY = mousePos.v - winRect.top; + sendbuttons(wheeldelta>0 ? 8 : 16, mouseX, mouseY); + break; + } + case kEventMouseUp: + case kEventMouseDown: + { + uint32_t buttons; + uint32_t modifiers; + GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, + 0, sizeof(modifiers), 0, &modifiers); + GetEventParameter(event, kEventParamMouseChord, typeUInt32, + 0, sizeof buttons, 0, &buttons); + /* simulate other buttons via alt/apple key. like x11 */ + if(modifiers & optionKey) { + mousebuttons = ((buttons & 1) ? 2 : 0); + altPressed = false; + } else if(modifiers & cmdKey) + mousebuttons = ((buttons & 1) ? 4 : 0); + else + mousebuttons = (buttons & 1); + + mousebuttons |= ((buttons & 2)<<1); + mousebuttons |= ((buttons & 4)>>1); + + } /* Fallthrough */ + case kEventMouseMoved: + case kEventMouseDragged: + mouseX = mousePos.h - winRect.left; + mouseY = mousePos.v - winRect.top; + sendbuttons(mousebuttons, mouseX, mouseY); + break; + default: + result = eventNotHandledErr; + break; + } + } + return result; +} + + +//default window command handler (from menus) +static OSStatus +MainWindowCommandHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData) +{ + OSStatus result = noErr; + UInt32 class = GetEventClass (event); + UInt32 kind = GetEventKind (event); + + result = CallNextEventHandler(nextHandler, event); + + if(class == kEventClassCommand) { + HICommand theHICommand; + GetEventParameter(event, kEventParamDirectObject, typeHICommand, + NULL, sizeof(HICommand), NULL, &theHICommand); + + switch(theHICommand.commandID) { + case kHICommandQuit: + cleanexit(0); + break; + + case kFullScreenCmd: + full_screen(); + break; + + default: + result = eventNotHandledErr; + break; + } + } else if(class == kEventClassWindow) { + WindowRef window; + _Rect rectPort = {0,0,0,0}; + + GetEventParameter(event, kEventParamDirectObject, typeWindowRef, + NULL, sizeof(WindowRef), NULL, &window); + + if(window) + GetPortBounds(GetWindowPort(window), &rectPort); + + switch(kind) { + case kEventWindowClosed: + theWindow = NULL; + cleanexit(0); // only one window + break; + + //resize window + case kEventWindowBoundsChanged: + window_resized(); + Rectangle rect = { { 0, 0 }, + { bounds.size.width, + bounds.size.height} }; + drawqlock(); + flushmemscreen(rect); + drawqunlock(); + break; + + default: + result = eventNotHandledErr; + break; + } + } + + return result; +} + +void +flushmemscreen(Rectangle r) +{ + CGRect rbounds; + + // sanity check. Trips from the initial "terminal" + if (r.max.x < r.min.x || r.max.y < r.min.y) + return; + + rbounds.size.width = r.max.x - r.min.x; + rbounds.size.height = r.max.y - r.min.y; + rbounds.origin.x = r.min.x; + rbounds.origin.y = r.min.y; + + if(rbounds.size.width <= 0 || rbounds.size.height <= 0) + return; + + QDBeginCGContext(GetWindowPort(theWindow), &context); + + // The sub-image is relative to our whole screen image. + CGImageRef subimg = CGImageCreateWithImageInRect(fullScreenImage, rbounds); + + // Drawing the sub-image is relative to the window. + rbounds.origin.y = winRect.bottom - winRect.top - r.min.y - rbounds.size.height; + CGContextDrawImage(context, rbounds, subimg); + CGImageRelease(subimg); + QDEndCGContext(GetWindowPort(theWindow), &context); + + needflush = true; +} + +uchar* +attachscreen(Rectangle *r, ulong *chan, int *depth, int *width, int *softscreen) +{ + if(!triedscreen) { + triedscreen = 1; + screeninit(); /* TO DO: call this elsewhere? */ + } + *r = gscreen->r; + *chan = gscreen->chan; + *depth = gscreen->depth; + *width = gscreen->width; + *softscreen = 1; + + return gscreen->data->bdata; +} + +// PAL - no palette handling. Don't intend to either. +void +getcolor(ulong i, ulong *r, ulong *g, ulong *b) +{ + +// PAL: Certainly wrong to return a grayscale. + *r = i; + *g = i; + *b = i; +} + +void +setcolor(ulong index, ulong r, ulong g, ulong b) +{ + USED(index); USED(r); USED(g); USED(b); +} + +enum{ + SnarfSize= 100*1024 +}; + +static char snarf[3*SnarfSize+1]; +static Rune rsnarf[SnarfSize+1]; + +char* +clipread(void) +{ + CFDataRef cfdata; + OSStatus err = noErr; + ItemCount nitems; + int i; + char *s; + + // Wow. This is ridiculously complicated. + PasteboardSynchronize(appleclip); + if((err = PasteboardGetItemCount(appleclip, &nitems)) != noErr) { + fprint(2, "apple pasteboard GetItemCount failed - Error %d\n", err); + return 0; + } + + // Yes, based at 1. Silly API. + for(i = 1; i <= nitems; i++) { + PasteboardItemID itemID; + CFArrayRef flavorTypeArray; + CFIndex flavorCount; + + if((err = PasteboardGetItemIdentifier(appleclip, i, &itemID)) != noErr){ + fprint(2, "Can't get pasteboard item identifier: %d\n", err); + return 0; + } + + if((err = PasteboardCopyItemFlavors(appleclip, itemID, &flavorTypeArray))!=noErr){ + fprint(2, "Can't copy pasteboard item flavors: %d\n", err); + return 0; + } + + flavorCount = CFArrayGetCount(flavorTypeArray); + CFIndex flavorIndex; + for(flavorIndex = 0; flavorIndex < flavorCount; ++flavorIndex){ + CFStringRef flavorType; + flavorType = (CFStringRef)CFArrayGetValueAtIndex(flavorTypeArray, flavorIndex); + if (UTTypeConformsTo(flavorType, CFSTR("public.utf16-plain-text"))){ + if((err = PasteboardCopyItemFlavorData(appleclip, itemID, + CFSTR("public.utf16-plain-text"), &cfdata)) != noErr){ + fprint(2, "apple pasteboard CopyItem failed - Error %d\n", err); + return 0; + } + CFIndex length = CFDataGetLength(cfdata); + if (length > sizeof rsnarf) length = sizeof rsnarf; + CFDataGetBytes(cfdata, CFRangeMake(0, length), (uint8_t *)rsnarf); + snprint(snarf, sizeof snarf, "%.*S", length/sizeof(Rune), rsnarf); + for(s = snarf; *s; s++) + if(*s == '\r') + *s = '\n'; + CFRelease(cfdata); + return strdup(snarf); + } + } + } + return 0; +} + +int +clipwrite(char *snarf) +{ + CFDataRef cfdata; + PasteboardSyncFlags flags; + + runeseprint(rsnarf, rsnarf+nelem(rsnarf), "%s", snarf); + if(PasteboardClear(appleclip) != noErr){ + fprint(2, "apple pasteboard clear failed\n"); + return 0; + } + flags = PasteboardSynchronize(appleclip); + if((flags&kPasteboardModified) || !(flags&kPasteboardClientIsOwner)){ + fprint(2, "apple pasteboard cannot assert ownership\n"); + return 0; + } + cfdata = CFDataCreate(kCFAllocatorDefault, (uchar*)rsnarf, runestrlen(rsnarf)*2); + if(cfdata == nil){ + fprint(2, "apple pasteboard cfdatacreate failed\n"); + return 0; + } + if(PasteboardPutItemFlavor(appleclip, (PasteboardItemID)1, + CFSTR("public.utf16-plain-text"), cfdata, 0) != noErr){ + fprint(2, "apple pasteboard putitem failed\n"); + CFRelease(cfdata); + return 0; + } + CFRelease(cfdata); + return 1; +} + +void +setpointer(int x, int y) +{ + CGPoint pnt; + + pnt.x = x + winRect.left; + pnt.y = y + winRect.top; + CGWarpMouseCursorPosition(pnt); +} + +void +drawcursor(Drawcursor* c) +{ + Cursor crsr; + uchar *bc, *bs, *ps, *pm; + int i, j, h, w, bpl; + + if(c->data == nil || c->minx >= c->maxx){ + InitCursor(); + return; + } + memset(crsr.data, 0, sizeof(crsr.data)); + memset(crsr.mask, 0, sizeof(crsr.mask)); + ps = (uchar*)crsr.data; + pm = (uchar*)crsr.mask; + h = (c->maxy - c->miny)/2; /* bounds include both masks, strangely */ + bpl = bytesperline(Rect(c->minx, c->miny, c->maxx, c->maxy), 1); + if((w = bpl) > 2) + w = 2; + bc = c->data; + bs = c->data + h*bpl; + if(h > 16) + h = 16; + for(i = 0; i < h; i++){ + for(j = 0; j < w; j++){ + ps[j] = bs[j]; + pm[j] = bs[j] | bc[j]; + } + bs += bpl; + bc += bpl; + ps += 2; + pm += 2; + } + crsr.hotSpot.h = -c->hotx; + crsr.hotSpot.v = -c->hoty; + SetCursor(&crsr); +} diff --git a/emu/NOTICE b/emu/NOTICE new file mode 100644 index 00000000..c815136b --- /dev/null +++ b/emu/NOTICE @@ -0,0 +1,32 @@ +This copyright NOTICE applies to all files in this directory and +subdirectories, unless another copyright notice appears in a given +file or subdirectory. If you take substantial code from this software to use in +other programs, you must somehow include with it an appropriate +copyright notice that includes the copyright notice and the other +notices below. It is fine (and often tidier) to do that in a separate +file such as NOTICE, LICENCE or COPYING. + + Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. + Portions Copyright © 1997-1999 Vita Nuova Limited + Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) + Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others + Portions Copyright © 2005 Russ Cox, MIT + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/emu/NetBSD/asm-386.S b/emu/NetBSD/asm-386.S new file mode 100644 index 00000000..ca701782 --- /dev/null +++ b/emu/NetBSD/asm-386.S @@ -0,0 +1,107 @@ + .file "asm-NetBSD-386.S" +#include <sys/syscall.h> + +/* + * executeonnewstack(void *tos, void (*tramp)(void *arg), void *arg) + */ + + .type ournewstack,@function + .global executeonnewstack +executeonnewstack: + pushl %ebp + movl %esp, %ebp + pushl %esi + + movl 8(%ebp), %esi /* get tos */ + subl $4, %esi + movl 16(%ebp), %eax + movl %eax, (%esi) /* stash arg on new stack */ + subl $4, %esi + movl 12(%ebp), %eax + movl %eax, (%esi) /* stash tramp on new stack */ + mov %esi, %esp /* swap stacks pronto */ + popl %eax /* recover the tramp address */ + call *%eax /* and jump to it (ho ho) */ + + /* if we return here, tramp didn't do it's job */ + + addl $8, %esp /* clean up for pose value */ + + leal SYS_exit, %eax + int $0x80 + +/* + * unlockandexit(int *key) + * + * NB: the return status may be rubbish if the stack is reused + * between the unlock and the system call, but this should + * not matter since no task is waiting for the result + */ + + .type unlockandexit,@function + .global unlockandexit +unlockandexit: + pushl %ebp + movl %esp, %ebp + + movl 8(%ebp), %esi /* get the key address */ + pushl $0 /* exit status 0 */ + movl $0, %eax /* unlock the stack allocator */ + movl %eax, (%esi) + leal SYS_exit, %eax /* call exit */ + int $0x80 + +/* + * umult(ulong m1, ulong m2, ulong *hi) + */ + + .type umult,@function + .global umult +umult: + pushl %ebp + movl %esp, %ebp + pushl %ebx + + movl 8(%ebp), %eax + movl 12(%ebp), %ebx + mull %ebx + movl 16(%ebp), %ebx + movl %edx, (%ebx) + + popl %ebx + popl %ebp + ret + + .type FPsave,@function + .global FPsave +FPsave: + pushl %ebp + movl %esp, %ebp + movl 8(%ebp), %eax + fstenv (%eax) + popl %ebp + ret + + .type FPrestore,@function + .global FPrestore +FPrestore: + pushl %ebp + movl %esp, %ebp + movl 8(%ebp), %eax + fldenv (%eax) + popl %ebp + ret + + .type getcallerpc,@function + .global getcallerpc +getcallerpc: + movl 4(%ebp), %eax + ret + + .type _tas,@function + .globl _tas +_tas: + movl $1, %eax + movl 4(%esp), %ecx + xchgl %eax, 0(%ecx) + ret diff --git a/emu/NetBSD/audio.c b/emu/NetBSD/audio.c new file mode 100644 index 00000000..ca92b7ca --- /dev/null +++ b/emu/NetBSD/audio.c @@ -0,0 +1,547 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/filio.h> +#include "audio.h" +#include <sys/soundcard.h> + +#define Audio_Mic_Val SOUND_MIXER_MIC +#define Audio_Linein_Val SOUND_MIXER_LINE + +#define Audio_Speaker_Val SOUND_MIXER_SPEAKER +#define Audio_Headphone_Val SOUND_MIXER_PHONEOUT +#define Audio_Lineout_Val SOUND_MIXER_VOLUME + +#define Audio_Pcm_Val AFMT_S16_LE +#define Audio_Ulaw_Val AFMT_MU_LAW +#define Audio_Alaw_Val AFMT_A_LAW + +#include "audio-tbls.c" + +#define min(a,b) ((a) < (b) ? (a) : (b)) +static int debug; + +#define AUDIO_FILE_STRING "/dev/dsp" + +enum { + A_Pause, + A_UnPause +}; + +enum { + A_In, + A_Out +}; + +static QLock inlock; +static QLock outlock; + +static int audio_file = -1; /* file in/out */ +static int audio_file_in = -1; /* copy of above when opened O_READ/O_RDWR */ +static int audio_file_out = -1; /* copy of above when opened O_WRITE/O_RDWR */ + +static int audio_swap_flag = 0; /* endian swap */ + +static int audio_in_pause = A_UnPause; + +static Audio_t av; +static int mixerleftvol[32]; +static int mixerrightvol[32]; + +static int audio_enforce(Audio_t*); +static int audio_open(void); +static int audio_pause_in(int, int); +static int audio_flush(int, int); +static int audio_pause_out(int); +static int audio_set_blocking(int); +static int audio_set_info(int, Audio_d*, int); +static void audio_swap_endian(char*, int); + +void +audio_file_init(void) +{ + int i; + static ushort flag = 1; + + audio_swap_flag = *((uchar*)&flag) == 0; /* big-endian? */ + audio_info_init(&av); + for (i = 0; i < 32; i++) + mixerleftvol[i] = mixerrightvol[i] = 100; +} + +void +audio_ctl_init(void) +{ +} + +void +audio_file_open(Chan *c, int omode) +{ + char ebuf[ERRMAX]; + + if (debug) + print("audio_file_open(0x%.8lux, %d)\n", c, omode); + switch(omode){ + case OREAD: + qlock(&inlock); + if(waserror()){ + qunlock(&inlock); + nexterror(); + } + + if(audio_file_in >= 0) + error(Einuse); + if (audio_file < 0) + audio_file = audio_open(); + audio_file_in = audio_file; + poperror(); + qunlock(&inlock); + break; + case OWRITE: + qlock(&outlock); + if(waserror()){ + qunlock(&outlock); + nexterror(); + } + if(audio_file_out >= 0) + error(Einuse); + if (audio_file < 0) + audio_file = audio_open(); + audio_file_out = audio_file; + poperror(); + qunlock(&outlock); + break; + case ORDWR: + qlock(&inlock); + qlock(&outlock); + if(waserror()){ + qunlock(&outlock); + qunlock(&inlock); + nexterror(); + } + if(audio_file_in >= 0 || audio_file_out >= 0) + error(Einuse); + if (audio_file < 0) + audio_file = audio_open(); + audio_file_in = audio_file_out = audio_file; + poperror(); + qunlock(&outlock); + qunlock(&inlock); + break; + } + if (debug) + print("audio_file_open: success\nin %d out %d both %d\n", + audio_file_out, audio_file_in, audio_file); +} + +void +audio_ctl_open(Chan *c, int omode) +{ + USED(c); + USED(omode); +} + +void +audio_file_close(Chan *c) +{ + switch(c->mode){ + case OREAD: + qlock(&inlock); + qlock(&outlock); + if (audio_file_out < 0) { + close(audio_file); + audio_file = -1; + } + qunlock(&outlock); + audio_file_in = -1; + qunlock(&inlock); + break; + case OWRITE: + qlock(&inlock); + qlock(&outlock); + if (audio_file_in < 0) { + close(audio_file); + audio_file = -1; + } + audio_file_out = -1; + qunlock(&outlock); + qunlock(&inlock); + break; + case ORDWR: + qlock(&inlock); + qlock(&outlock); + close(audio_file); + audio_file_in = audio_file_out = audio_file = -1; + qunlock(&outlock); + qunlock(&inlock); + break; + } +} + +void +audio_ctl_close(Chan *c) +{ +} + +long +audio_file_read(Chan *c, void *va, long count, vlong offset) +{ + struct timespec time; + long ba, status, chunk, total; + char *pva = (char *) va; + + qlock(&inlock); + if(waserror()){ + qunlock(&inlock); + nexterror(); + } + + if(audio_file_in < 0) + error(Eperm); + + /* check block alignment */ + ba = av.in.bits * av.in.chan / Bits_Per_Byte; + + if(count % ba) + error(Ebadarg); + + if(! audio_pause_in(audio_file_in, A_UnPause)) + error(Eio); + + total = 0; + while(total < count) { + chunk = count - total; + osenter(); + status = read(audio_file_in, pva + total, chunk); + osleave(); + if(status < 0) + error(Eio); + total += status; + } + + if(total != count) + error(Eio); + + if(audio_swap_flag && av.out.bits == 16) + audio_swap_endian(pva, count); + + poperror(); + qunlock(&inlock); + + return count; +} + +long +audio_file_write(Chan *c, void *va, long count, vlong offset) +{ + struct timespec time; + long status = -1; + long ba, total, chunk, bufsz; + + if (debug > 1) + print("audio_file_write(0x%.8lux, 0x%.8lux, %ld, %uld)\n", + c, va, count, offset); + + qlock(&outlock); + if(waserror()){ + qunlock(&outlock); + nexterror(); + } + + if(audio_file_out < 0) + error(Eperm); + + /* check block alignment */ + ba = av.out.bits * av.out.chan / Bits_Per_Byte; + + if(count % ba) + error(Ebadarg); + + if(audio_swap_flag && av.out.bits == 16) + audio_swap_endian(va, count); + + total = 0; + bufsz = av.out.buf * Audio_Max_Buf / Audio_Max_Val; + + if(bufsz == 0) + error(Ebadarg); + + while(total < count) { + chunk = min(bufsz, count - total); + osenter(); + status = write(audio_file_out, va, chunk); + osleave(); + if(status <= 0) + error(Eio); + total += status; + } + + poperror(); + qunlock(&outlock); + + return count; +} + +static int +audio_open(void) +{ + int fd; + + /* open non-blocking in case someone already has it open */ + /* otherwise we would block until they close! */ + fd = open(AUDIO_FILE_STRING, O_RDWR|O_NONBLOCK); + if(fd < 0) + oserror(); + + /* change device to be blocking */ + if(!audio_set_blocking(fd)) { + if (debug) + print("audio_open: failed to set blocking\n"); + close(fd); + error("cannot set blocking mode"); + } + + if (debug) + print("audio_open: blocking set\n"); + + /* set audio info */ + av.in.flags = ~0; + av.out.flags = 0; + + if(! audio_set_info(fd, &av.in, A_In)) { + close(fd); + error(Ebadarg); + } + + av.in.flags = 0; + + /* tada, we're open, blocking, paused and flushed */ + return fd; +} + +long +audio_ctl_write(Chan *c, void *va, long count, vlong offset) +{ + int fd; + int ff; + Audio_t tmpav = av; + + tmpav.in.flags = 0; + tmpav.out.flags = 0; + + if (!audioparse(va, count, &tmpav)) + error(Ebadarg); + + if (!audio_enforce(&tmpav)) + error(Ebadarg); + + qlock(&inlock); + if (waserror()) { + qunlock(&inlock); + nexterror(); + } + + if (audio_file_in >= 0 && (tmpav.in.flags & AUDIO_MOD_FLAG)) { + if (!audio_pause_in(audio_file_in, A_Pause)) + error(Ebadarg); + if (!audio_flush(audio_file_in, A_In)) + error(Ebadarg); + if (!audio_set_info(audio_file_in, &tmpav.in, A_In)) + error(Ebadarg); + } + poperror(); + qunlock(&inlock); + + qlock(&outlock); + if (waserror()) { + qunlock(&outlock); + nexterror(); + } + if (audio_file_out >= 0 && (tmpav.out.flags & AUDIO_MOD_FLAG)){ + if (!audio_pause_out(audio_file_out)) + error(Ebadarg); + if (!audio_set_info(audio_file_out, &tmpav.out, A_Out)) + error(Ebadarg); + } + poperror(); + qunlock(&outlock); + + tmpav.in.flags = 0; + tmpav.out.flags = 0; + + av = tmpav; + + return count; +} + + + +static int +audio_set_blocking(int fd) +{ + int val; + + if((val = fcntl(fd, F_GETFL, 0)) == -1) + return 0; + + val &= ~O_NONBLOCK; + + if(fcntl(fd, F_SETFL, val) < 0) + return 0; + + return 1; +} + +static int +doioctl(int fd, int ctl, int *info) +{ + int status; + osenter(); + status = ioctl(fd, ctl, info); /* qlock and load general stuff */ + osleave(); + if (status < 0) + print("doioctl(0x%.8lux, 0x%.8lux) failed %d\n", ctl, *info, errno); + return status; +} + +static int +choosefmt(Audio_d *i) +{ + int newbits, newenc; + + newbits = i->bits; + newenc = i->enc; + switch (newenc) { + case Audio_Alaw_Val: + if (newbits == 8) + return AFMT_A_LAW; + break; + case Audio_Ulaw_Val: + if (newbits == 8) + return AFMT_MU_LAW; + break; + case Audio_Pcm_Val: + if (newbits == 8) + return AFMT_U8; + else if (newbits == 16) + return AFMT_S16_LE; + break; + } + return -1; +} + +static int +audio_set_info(int fd, Audio_d *i, int d) +{ + int status; + int unequal_stereo = 0; + + if(fd < 0) + return 0; + + /* fmt */ + if(i->flags & (AUDIO_BITS_FLAG || AUDIO_ENC_FLAG)) { + int oldfmt, newfmt; + oldfmt = AFMT_QUERY; + if (doioctl(fd, SNDCTL_DSP_SETFMT, &oldfmt) < 0) + return 0; + if (debug) + print("audio_set_info: current format 0x%.8lux\n", oldfmt); + newfmt = choosefmt(i); + if (debug) + print("audio_set_info: new format 0x%.8lux\n", newfmt); + if (newfmt == -1 || newfmt != oldfmt && doioctl(fd, SNDCTL_DSP_SETFMT, &newfmt) < 0) + return 0; + } + + /* channels */ + if(i->flags & AUDIO_CHAN_FLAG) { + int channels = i->chan; + if (debug) + print("audio_set_info: new channels %d\n", channels); + if (doioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0 + || channels != i->chan) + return 0; + } + + /* sample rate */ + if(i->flags & AUDIO_RATE_FLAG) { + int speed = i->rate; + if (debug) + print("audio_set_info: new speed %d\n", speed); + if (doioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0 || speed != i->rate) + return 0; + } + + /* dev volume */ + if(i->flags & (AUDIO_LEFT_FLAG | AUDIO_VOL_FLAG | AUDIO_RIGHT_FLAG)) { + int val; + if (i->flags & (AUDIO_LEFT_FLAG | AUDIO_VOL_FLAG)) + mixerleftvol[i->dev] = (i->left * 100) / Audio_Max_Val; + if (i->flags & (AUDIO_RIGHT_FLAG | AUDIO_VOL_FLAG)) + mixerrightvol[i->dev] = (i->right * 100) / Audio_Max_Val; + val = mixerleftvol[i->dev] | (mixerrightvol[i->dev] << 8); + doioctl(fd, MIXER_WRITE(i->dev), &val); + } + + if (i->flags & AUDIO_DEV_FLAG) { + } + + return 1; +} + +void +audio_swap_endian(char *p, int n) +{ + int b; + + while (n > 1) { + b = p[0]; + p[0] = p[1]; + p[1] = b; + p += 2; + n -= 2; + } +} + +static int +audio_pause_out(int fd) +{ + USED(fd); + return 1; +} + +static int +audio_pause_in(int fd, int f) +{ + USED(fd); + USED(f); + return 1; +} + +static int +audio_flush(int fd, int d) +{ + int x; + return doioctl(fd, SNDCTL_DSP_SYNC, &x) >= 0; +} + +static int +audio_enforce(Audio_t *t) +{ + if((t->in.enc == Audio_Ulaw_Val || t->in.enc == Audio_Alaw_Val) && + (t->in.rate != 8000 || t->in.chan != 1)) + return 0; + if((t->out.enc == Audio_Ulaw_Val || t->out.enc == Audio_Alaw_Val) && + (t->out.rate != 8000 || t->out.chan != 1)) + return 0; + return 1; +} + +Audio_t* +getaudiodev(void) +{ + return &av; +} diff --git a/emu/NetBSD/cmd.c b/emu/NetBSD/cmd.c new file mode 100644 index 00000000..ed4cabab --- /dev/null +++ b/emu/NetBSD/cmd.c @@ -0,0 +1,213 @@ +#include <sys/types.h> +#include <signal.h> +#include <pwd.h> +#include <sys/resource.h> +#include <sys/wait.h> +#include <fcntl.h> + +#include "dat.h" +#include "fns.h" +#include "error.h" + +enum +{ + Debug = 0 +}; + +/* + * os-specific devcmd support. + * this version should be reasonably portable across Unix systems. + */ +typedef struct Targ Targ; +struct Targ +{ + int fd[3]; /* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */ + char** args; + char* dir; + int pid; + int wfd; /* child writes errors that occur after the fork or on exec */ + int uid; + int gid; +}; + +extern int gidnobody; +extern int uidnobody; + +static int +childproc(Targ *t) +{ + int i, nfd; + + if(Debug) + print("devcmd: '%s'", t->args[0]); + + nfd = getdtablesize(); + for(i = 0; i < nfd; i++) + if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd) + close(i); + + dup2(t->fd[0], 0); + dup2(t->fd[1], 1); + dup2(t->fd[2], 2); + close(t->fd[0]); + close(t->fd[1]); + close(t->fd[2]); + + /* should have an auth file to do host-specific authorisation? */ + if(t->gid != -1){ + if(setgid(t->gid) < 0 && getegid() == 0){ + fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno)); + _exit(1); + } + } + + if(t->uid != -1){ + if(setuid(t->uid) < 0 && geteuid() == 0){ + fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno)); + _exit(1); + } + } + + if(t->dir != nil && chdir(t->dir) < 0){ + fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno)); + _exit(1); + } + + signal(SIGPIPE, SIG_DFL); + + execvp(t->args[0], t->args); + if(Debug) + print("execvp: %s\n",strerror(errno)); + fprint(t->wfd, "exec failed: %s", strerror(errno)); + + _exit(1); +} + +void* +oscmd(char **args, int nice, char *dir, int *fd) +{ + Targ *t; + int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid; + + t = mallocz(sizeof(*t), 1); + if(t == nil) + return nil; + + fd0[0] = fd0[1] = -1; + fd1[0] = fd1[1] = -1; + fd2[0] = fd2[1] = -1; + wfd[0] = wfd[1] = -1; + if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0) + goto Error; + if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0) /* close on exec to give end of file on success */ + goto Error; + + t->fd[0] = fd0[0]; + t->fd[1] = fd1[1]; + t->fd[2] = fd2[1]; + t->wfd = wfd[1]; + t->args = args; + t->dir = dir; + t->gid = up->env->gid; + if(t->gid == -1) + t->gid = gidnobody; + t->uid = up->env->uid; + if(t->uid == -1) + t->uid = uidnobody; + + signal(SIGCHLD, SIG_DFL); + switch(pid = fork()) { + case -1: + goto Error; + case 0: + setpgid(0, getpid()); + if(nice) + oslopri(); + childproc(t); + _exit(1); + default: + t->pid = pid; + if(Debug) + print("cmd pid %d\n", t->pid); + break; + } + + close(fd0[0]); + close(fd1[1]); + close(fd2[1]); + close(wfd[1]); + + n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1); + close(wfd[0]); + if(n > 0){ + close(fd0[1]); + close(fd1[0]); + close(fd2[0]); + free(t); + up->genbuf[n] = 0; + if(Debug) + print("oscmd: bad exec: %q\n", up->genbuf); + error(up->genbuf); + return nil; + } + + fd[0] = fd0[1]; + fd[1] = fd1[0]; + fd[2] = fd2[0]; + return t; + +Error: + r = errno; + if(Debug) + print("oscmd: %q\n",strerror(r)); + close(fd0[0]); + close(fd0[1]); + close(fd1[0]); + close(fd1[1]); + close(fd2[0]); + close(fd2[1]); + close(wfd[0]); + close(wfd[1]); + error(strerror(r)); + return nil; +} + +int +oscmdkill(void *a) +{ + Targ *t = a; + + if(Debug) + print("kill: %d\n", t->pid); + return kill(-t->pid, SIGTERM); +} + +int +oscmdwait(void *a, char *buf, int n) +{ + Targ *t = a; + int s; + + if(waitpid(t->pid, &s, 0) == -1){ + if(Debug) + print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno)); + return -1; + } + if(WIFEXITED(s)){ + if(WEXITSTATUS(s) == 0) + return snprint(buf, n, "%d 0 0 0 ''", t->pid); + return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s)); + } + if(WIFSIGNALED(s)){ + if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL) + return snprint(buf, n, "%d 0 0 0 killed", t->pid); + return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s)); + } + return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s); +} + +void +oscmdfree(void *a) +{ + free(a); +} diff --git a/emu/NetBSD/deveia.c b/emu/NetBSD/deveia.c new file mode 100644 index 00000000..93b95fb5 --- /dev/null +++ b/emu/NetBSD/deveia.c @@ -0,0 +1,39 @@ +/* + * NetBSD serial port definitions + */ + +static char *sysdev[] = { + "/dev/dty00", + "/dev/dty01", + "/dev/dty02", + "/dev/dty03", +}; + +#include <sys/ioctl.h> +#include "deveia-posix.c" +#include "deveia-bsd.c" + + +static struct tcdef_t bps[] = { + {0, B0}, + {50, B50}, + {75, B75}, + {110, B110}, + {134, B134}, + {150, B150}, + {200, B200}, + {300, B300}, + {600, B600}, + {1200, B1200}, + {1800, B1800}, + {2400, B2400}, + {4800, B4800}, + {9600, B9600}, + {19200, B19200}, + {38400, B38400}, + {57600, B57600}, + {115200, B115200}, + {230400, B230400}, + {-1, -1} +}; + diff --git a/emu/NetBSD/devfs.c b/emu/NetBSD/devfs.c new file mode 100644 index 00000000..9017c3fc --- /dev/null +++ b/emu/NetBSD/devfs.c @@ -0,0 +1 @@ +#include "devfs-posix.c" diff --git a/emu/NetBSD/emu b/emu/NetBSD/emu new file mode 100644 index 00000000..257df186 --- /dev/null +++ b/emu/NetBSD/emu @@ -0,0 +1,106 @@ +dev + root + cons + env + mnt + pipe + prog + prof + srv + dup + ssl + cap + fs + cmd cmd + indir + + draw win-x11a + pointer + snarf + + ip ipif ipaux + eia + audio audio + mem + +lib + interp + tk + freetype + math + draw + + memlayer + memdraw + keyring + sec + mp + + 9 + +link + +mod + sys + draw + + tk + math + srv srv + keyring + loader + freetype + +port + alloc + cache + chan + dev + dial + dis + discall + env + error + errstr + exception + exportfs + inferno + latin1 + main + parse + pgrp + print + proc + qio + random + sysfile + uqid + +code + +init + emuinit + +root + /dev / + /fd / + /prog / + /net / + /net.alt / + /chan / + /nvfs / + /env / +# /chan +# /dev +# /dis +# /env +# /n +# /net +# /nvfs / +# /prog +# /icons +# /osinit.dis +# /dis/emuinit.dis +# /dis/lib/auth.dis +# /dis/lib/ssl.dis +# /n/local / diff --git a/emu/NetBSD/emu.c b/emu/NetBSD/emu.c new file mode 100644 index 00000000..69e1139d --- /dev/null +++ b/emu/NetBSD/emu.c @@ -0,0 +1,88 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "interp.h" + + +#include "emu.root.h" + +ulong ndevs = 29; + +extern Dev rootdevtab; +extern Dev consdevtab; +extern Dev envdevtab; +extern Dev mntdevtab; +extern Dev pipedevtab; +extern Dev progdevtab; +extern Dev profdevtab; +extern Dev srvdevtab; +extern Dev dupdevtab; +extern Dev ssldevtab; +extern Dev capdevtab; +extern Dev fsdevtab; +extern Dev cmddevtab; +extern Dev indirdevtab; +extern Dev drawdevtab; +extern Dev pointerdevtab; +extern Dev snarfdevtab; +extern Dev ipdevtab; +extern Dev eiadevtab; +extern Dev audiodevtab; +extern Dev memdevtab; +Dev* devtab[]={ + &rootdevtab, + &consdevtab, + &envdevtab, + &mntdevtab, + &pipedevtab, + &progdevtab, + &profdevtab, + &srvdevtab, + &dupdevtab, + &ssldevtab, + &capdevtab, + &fsdevtab, + &cmddevtab, + &indirdevtab, + &drawdevtab, + &pointerdevtab, + &snarfdevtab, + &ipdevtab, + &eiadevtab, + &audiodevtab, + &memdevtab, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, +}; + +void links(void){ +} + +extern void sysmodinit(void); +extern void drawmodinit(void); +extern void tkmodinit(void); +extern void mathmodinit(void); +extern void srvmodinit(void); +extern void keyringmodinit(void); +extern void loadermodinit(void); +extern void freetypemodinit(void); +void modinit(void){ + sysmodinit(); + drawmodinit(); + tkmodinit(); + mathmodinit(); + srvmodinit(); + keyringmodinit(); + loadermodinit(); + freetypemodinit(); +} + +char* conffile = "emu"; +ulong kerndate = KERNDATE; diff --git a/emu/NetBSD/ipif.c b/emu/NetBSD/ipif.c new file mode 100644 index 00000000..853e2e93 --- /dev/null +++ b/emu/NetBSD/ipif.c @@ -0,0 +1,372 @@ +#include <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <netdb.h> +#include "dat.h" +#include "fns.h" +#include "ip.h" +#include "error.h" +#include <sys/ioctl.h> + +int +so_socket(int type) +{ + int fd, one; + + switch(type) { + default: + error("bad protocol type"); + case S_TCP: + type = SOCK_STREAM; + break; + case S_UDP: + type = SOCK_DGRAM; + break; + } + + fd = socket(AF_INET, type, 0); + if(fd < 0) + oserror(); + if(type == SOCK_DGRAM){ + one = 1; + setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char*)&one, sizeof (one)); + }else{ + one = 1; + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(one)); + } + return fd; +} + +int +so_send(int sock, void *va, int len, void *hdr, int hdrlen) +{ + int r; + struct sockaddr sa; + struct sockaddr_in *sin; + char *h = hdr; + + + osenter(); + if(hdr == 0) + r = write(sock, va, len); + else { + memset(&sa, sizeof(sa), 0); + sin = (struct sockaddr_in*)&sa; + sin->sin_family = AF_INET; + switch(hdrlen){ + case OUdphdrlenv4: + memmove(&sin->sin_addr, h, 4); + memmove(&sin->sin_port, h+8, 2); + break; + case OUdphdrlen: + v6tov4((uchar*)&sin->sin_addr, h); + memmove(&sin->sin_port, h+2*IPaddrlen, 2); /* rport */ + break; + default: + v6tov4((uchar*)&sin->sin_addr, h); + memmove(&sin->sin_port, h+3*IPaddrlen, 2); + break; + } + r = sendto(sock, va, len, 0, &sa, sizeof(sa)); + } + osleave(); + return r; +} + +int +so_recv(int sock, void *va, int len, void *hdr, int hdrlen) +{ + int r, l; + struct sockaddr sa; + struct sockaddr_in *sin; + char h[Udphdrlen]; + + + osenter(); + if(hdr == 0) + r = read(sock, va, len); + else { + sin = (struct sockaddr_in*)&sa; + l = sizeof(sa); + r = recvfrom(sock, va, len, 0, &sa, &l); + if(r >= 0) { + memset(h, sizeof h, 0); + switch(hdrlen){ + case OUdphdrlenv4: + memmove(h, &sin->sin_addr, 4); + memmove(h+2*IPv4addrlen, &sin->sin_port, 2); + break; + case OUdphdrlen: + v4tov6(h, (uchar*)&sin->sin_addr); + memmove(h+2*IPaddrlen, &sin->sin_port, 2); + break; + default: + v4tov6(h, (uchar*)&sin->sin_addr); + memmove(h+3*IPaddrlen, &sin->sin_port, 2); + break; + } + + /* alas there's no way to get the local addr/port correctly. Pretend. */ + getsockname(sock, &sa, &l); + switch(hdrlen){ + case OUdphdrlenv4: + memmove(h+IPv4addrlen, &sin->sin_addr, IPv4addrlen); + memmove(h+2*IPv4addrlen+2, &sin->sin_port, 2); + break; + case OUdphdrlen: + v4tov6(h+IPaddrlen, (uchar*)&sin->sin_addr); + memmove(h+2*IPaddrlen+2, &sin->sin_port, 2); + break; + default: + v4tov6(h+IPaddrlen, (uchar*)&sin->sin_addr); + v4tov6(h+2*IPaddrlen, (uchar*)&sin->sin_addr); /* ifcaddr */ + memmove(h+3*IPaddrlen+2, &sin->sin_port, 2); + break; + } + memmove(hdr, h, hdrlen); + } + } + osleave(); + return r; +} + +void +so_close(int sock) +{ + close(sock); +} + +void +so_connect(int fd, unsigned long raddr, unsigned short rport) +{ + int r; + struct sockaddr sa; + struct sockaddr_in *sin; + + memset(&sa, 0, sizeof(sa)); + sin = (struct sockaddr_in*)&sa; + sin->sin_family = AF_INET; + hnputs(&sin->sin_port, rport); + hnputl(&sin->sin_addr.s_addr, raddr); + + osenter(); + r = connect(fd, &sa, sizeof(sa)); + osleave(); + if(r < 0) + oserror(); +} + +void +so_getsockname(int fd, unsigned long *laddr, unsigned short *lport) +{ + int len; + struct sockaddr sa; + struct sockaddr_in *sin; + + + len = sizeof(sa); + if(getsockname(fd, &sa, &len) < 0) + oserror(); + + sin = (struct sockaddr_in*)&sa; + if(sin->sin_family != AF_INET || len != sizeof(*sin)) + error("not AF_INET"); + + *laddr = nhgetl(&sin->sin_addr.s_addr); + *lport = nhgets(&sin->sin_port); +} + +void +so_listen(int fd) +{ + int r; + + osenter(); + r = listen(fd, 5); + osleave(); + if(r < 0) + oserror(); +} + +int +so_accept(int fd, unsigned long *raddr, unsigned short *rport) +{ + int nfd, len; + struct sockaddr sa; + struct sockaddr_in *sin; + + sin = (struct sockaddr_in*)&sa; + + len = sizeof(sa); + osenter(); + nfd = accept(fd, &sa, &len); + osleave(); + if(nfd < 0) + oserror(); + + if(sin->sin_family != AF_INET || len != sizeof(*sin)) + error("not AF_INET"); + + *raddr = nhgetl(&sin->sin_addr.s_addr); + *rport = nhgets(&sin->sin_port); + return nfd; +} + +void +so_bind(int fd, int su, unsigned long addr, unsigned short port) +{ + int i, one; + struct sockaddr sa; + struct sockaddr_in *sin; + + sin = (struct sockaddr_in*)&sa; + + one = 1; + if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) < 0) { + oserrstr(up->genbuf, sizeof(up->genbuf)); + print("setsockopt: %s", up->genbuf); + } + + if(su) { + for(i = 600; i < 1024; i++) { + memset(&sa, 0, sizeof(sa)); + sin->sin_family = AF_INET; + hnputl(&sin->sin_addr.s_addr, addr); + hnputs(&sin->sin_port, i); + + if(bind(fd, &sa, sizeof(sa)) >= 0) + return; + } + oserror(); + } + + memset(&sa, 0, sizeof(sa)); + sin->sin_family = AF_INET; + hnputl(&sin->sin_addr.s_addr, addr); + hnputs(&sin->sin_port, port); + + if(bind(fd, &sa, sizeof(sa)) < 0) + oserror(); +} + +void +so_setsockopt(int fd, int opt, int value) +{ + int r; + struct linger l; + + if(opt == SO_LINGER){ + l.l_onoff = 1; + l.l_linger = (short) value; + osenter(); + r = setsockopt(fd, SOL_SOCKET, opt, (char *)&l, sizeof(l)); + osleave(); + }else + error(Ebadctl); + if(r < 0) + oserror(); +} + +int +so_gethostbyname(char *host, char**hostv, int n) +{ + int i; + char buf[32]; + uchar *p; + struct hostent *hp; + + hp = gethostbyname(host); + if(hp == 0) + return 0; + + for(i = 0; hp->h_addr_list[i] && i < n; i++) { + p = hp->h_addr_list[i]; + snprint(buf, sizeof(buf), "%ud.%ud.%ud.%ud", p[0], p[1], p[2], p[3]); + hostv[i] = strdup(buf); + if(hostv[i] == 0) + break; + } + return i; +} + +int +so_gethostbyaddr(char *addr, char **hostv, int n) +{ + int i; + struct hostent *hp; + unsigned long straddr; + + straddr = inet_addr(addr); + if(straddr == -1) + return 0; + + hp = gethostbyaddr((char *)&straddr, sizeof(straddr), AF_INET); + if(hp == 0) + return 0; + + hostv[0] = strdup(hp->h_name); + if(hostv[0] == 0) + return 0; + for(i = 1; hp->h_aliases[i-1] && i < n; i++) { + hostv[i] = strdup(hp->h_aliases[i-1]); + if(hostv[i] == 0) + break; + } + return i; +} + +int +so_getservbyname(char *service, char *net, char *port) +{ + ushort p; + struct servent *s; + + s = getservbyname(service, net); + if(s == 0) + return -1; + p = s->s_port; + sprint(port, "%d", nhgets(&p)); + return 0; +} + +int +so_hangup(int fd, int linger) +{ + int r; + static struct linger l = {1, 1000}; + + osenter(); + if(linger) + setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&l, sizeof(l)); + r = shutdown(fd, 2); + if(r >= 0) + r = close(fd); + osleave(); + return r; +} + +void +arpadd(char *ipaddr, char *eaddr, int n) +{ + error("arp not implemented"); +} + +int +so_mustbind(int restricted, int port) +{ + return restricted || port != 0; +} + +void +so_keepalive(int fd, int ms) +{ + int on; + + USED(ms); + on = 1; + setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char*)&on, sizeof(on)); +} diff --git a/emu/NetBSD/mkfile b/emu/NetBSD/mkfile new file mode 100644 index 00000000..8531a68b --- /dev/null +++ b/emu/NetBSD/mkfile @@ -0,0 +1,46 @@ +SYSTARG=NetBSD +OBJTYPE=386 +<../../mkconfig +SYSTARG=NetBSD +OBJTYPE=386 + +#Configurable parameters + +CONF=emu #default configuration +CONFLIST=emu +CLEANCONFLIST= + +INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed + +#end configurable parameters + +<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system + +<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS + +OBJ=\ + asm-$OBJTYPE.$O\ + os.$O\ + $CONF.root.$O\ + lock.$O\ + $DEVS\ + $PORT\ + +HFILES=\ + +CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS +SYSLIBS= -lXext -lX11 -lossaudio -lm +KERNDATE=`{$NDATE} + +default:V: $O.$CONF + +<../port/portmkfile + +$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBFILES + $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c + $LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS + +install:V: $O.$CONF + cp $O.$CONF $INSTALLDIR/$CONF + +devfs.$O: ../port/devfs-posix.c diff --git a/emu/NetBSD/mkfile-NetBSD b/emu/NetBSD/mkfile-NetBSD new file mode 100644 index 00000000..3fdc164e --- /dev/null +++ b/emu/NetBSD/mkfile-NetBSD @@ -0,0 +1,17 @@ +# +# architecture-dependent files for NetBSD +# + +LDFLAGS= + +TARGFILES=devfs-posix.$O\ + deveia-NetBSD.$O\ + devip.$O\ + ipif-posix.$O\ + os-NetBSD.$O\ + win-x11.$O\ + srv.$O\ + lock.$O\ + asm-NetBSD-$OBJTYPE.$O + +#SYSLIBS= -lXext -lX11 -lossaudio -lm diff --git a/emu/NetBSD/os.c b/emu/NetBSD/os.c new file mode 100644 index 00000000..8a1d0b88 --- /dev/null +++ b/emu/NetBSD/os.c @@ -0,0 +1,528 @@ +#include <sys/types.h> +#include <time.h> +#include <termios.h> +#include <signal.h> +#include <pwd.h> +#include <sched.h> +#include <sys/resource.h> +#include <sys/wait.h> +#include <sys/time.h> +#include <errno.h> + +#include "dat.h" +#include "fns.h" +#include "error.h" + +enum +{ + DELETE = 0x7f, + CTRLC = 'C'-'@', + NSTACKSPERALLOC = 16, + X11STACK= 256*1024 +}; +char *hosttype = "NetBSD"; + +static void *stackalloc(Proc *p, void **tos); +static void stackfreeandexit(void *stack); + +void executeonnewstack(void *tos, void (*tramp)(void *arg), void *arg); +void unlockandexit(ulong *key); + +extern int dflag; + +int gidnobody = -1; +int uidnobody = -1; +static struct termios tinit; + +void +pexit(char *msg, int t) +{ + Osenv *e; + void *kstack; + + lock(&procs.l); + if(up->prev) + up->prev->next = up->next; + else + procs.head = up->next; + + if(up->next) + up->next->prev = up->prev; + else + procs.tail = up->prev; + unlock(&procs.l); + + if(0) + print("pexit: %s: %s\n", up->text, msg); + + e = up->env; + if(e != nil) { + closefgrp(e->fgrp); + closepgrp(e->pgrp); + closeegrp(e->egrp); + closesigs(e->sigs); + } + kstack = up->kstack; + free(up->prog); + free(up); + if(kstack != nil) + stackfreeandexit(kstack); +} + +int +tramp(void *arg) +{ + Proc *p; + p = arg; + p->pid = p->sigid = getpid(); + (*p->func)(p->arg); + pexit("{Tramp}", 0); + return 0; +} + +int +kproc(char *name, void (*func)(void*), void *arg, int flags) +{ + int pid; + Proc *p; + Pgrp *pg; + Fgrp *fg; + Egrp *eg; + void *tos; + + p = newproc(); + if(0) + print("start %s:%.8lx\n", name, p); + if(p == nil) { + print("kproc(%s): no memory", name); + panic("kproc: no memory"); + return -1; + } + + if(flags & KPDUPPG) { + pg = up->env->pgrp; + incref(&pg->r); + p->env->pgrp = pg; + } + if(flags & KPDUPFDG) { + fg = up->env->fgrp; + incref(&fg->r); + p->env->fgrp = fg; + } + if(flags & KPDUPENVG) { + eg = up->env->egrp; + incref(&eg->r); + p->env->egrp = eg; + } + + p->env->uid = up->env->uid; + p->env->gid = up->env->gid; + kstrdup(&p->env->user, up->env->user); + + strcpy(p->text, name); + + p->func = func; + p->arg = arg; + + if(flags & KPX11){ + p->kstack = nil; /* never freed; also up not defined */ + tos = (char*)mallocz(X11STACK, 0) + X11STACK - sizeof(void*); + }else + p->kstack = stackalloc(p, &tos); + + lock(&procs.l); + if(procs.tail != nil) { + p->prev = procs.tail; + procs.tail->next = p; + } + else { + procs.head = p; + p->prev = nil; + } + procs.tail = p; + unlock(&procs.l); + + if (__clone(tramp, tos, /*CLONE_PTRACE|*/CLONE_VM|CLONE_FS|CLONE_FILES|SIGCHLD, p) <= 0) { + fprint(2, "emu: clone failed: %s\n", strerror(errno)); + panic("kproc: clone failed"); + } + + return 0; +} + +/* + * TO DO: + * To get pc on trap, use sigaction instead of signal and + * examine its siginfo structure + */ + +/* +static void +diserr(char *s, int pc) +{ + char buf[ERRMAX]; + + snprint(buf, sizeof(buf), "%s: pc=0x%lux", s, pc); + disfault(nil, buf); +} +*/ + +static void +trapILL(int signo) +{ + USED(signo); + disfault(nil, "Illegal instruction"); +} + +static void +trapBUS(int signo) +{ + USED(signo); + disfault(nil, "Bus error"); +} + +static void +trapSEGV(int signo) +{ + USED(signo); + disfault(nil, "Segmentation violation"); +} + +#include <fpuctl.h> +static void +trapFPE(int signo) +{ + USED(signo); + print("FPU status=0x%.4lux", getfsr()); + disfault(nil, "Floating exception"); +} + +static void +trapUSR1(int signo) +{ + int intwait; + + USED(signo); + + intwait = up->intwait; + up->intwait = 0; /* clear it to let proc continue in osleave */ + + if(up->type != Interp) /* Used to unblock pending I/O */ + return; + + if(intwait == 0) /* Not posted so it's a sync error */ + disfault(nil, Eintr); /* Should never happen */ +} + +/* called to wake up kproc blocked on a syscall */ +void +oshostintr(Proc *p) +{ + kill(p->sigid, SIGUSR1); +} + +static void +trapUSR2(int signo) +{ + USED(signo); + /* we've done our work of interrupting sigsuspend */ +} + +void +osblock(void) +{ + sigset_t mask; + + sigprocmask(SIG_SETMASK, NULL, &mask); + sigdelset(&mask, SIGUSR2); + sigsuspend(&mask); +} + +void +osready(Proc *p) +{ + if(kill(p->sigid, SIGUSR2) < 0) + fprint(2, "emu: osready failed: pid %d: %s\n", p->sigid, strerror(errno)); +} + +void +oslongjmp(void *regs, osjmpbuf env, int val) +{ + USED(regs); + siglongjmp(env, val); +} + +static void +termset(void) +{ + struct termios t; + + tcgetattr(0, &t); + tinit = t; + t.c_lflag &= ~(ICANON|ECHO|ISIG); + t.c_cc[VMIN] = 1; + t.c_cc[VTIME] = 0; + tcsetattr(0, TCSANOW, &t); +} + +static void +termrestore(void) +{ + tcsetattr(0, TCSANOW, &tinit); +} + +void +cleanexit(int x) +{ + USED(x); + + if(up->intwait) { + up->intwait = 0; + return; + } + + if(dflag == 0) + termrestore(); + + kill(0, SIGKILL); + exit(0); +} + +void +osreboot(char *file, char **argv) +{ + if(dflag == 0) + termrestore(); + execvp(file, argv); + error("reboot failure"); +} + +void +libinit(char *imod) +{ + struct termios t; + struct sigaction act; + sigset_t mask; + struct passwd *pw; + Proc *p; + void *tos; + char sys[64]; + + setsid(); + + gethostname(sys, sizeof(sys)); + kstrdup(&ossysname, sys); + + pw = getpwnam("nobody"); + if(pw != nil) { + uidnobody = pw->pw_uid; + gidnobody = pw->pw_gid; + } + + if(dflag == 0) + termset(); + + memset(&act, 0 , sizeof(act)); + act.sa_handler = trapUSR1; + sigaction(SIGUSR1, &act, nil); + + sigemptyset(&mask); + sigaddset(&mask, SIGUSR2); + sigprocmask(SIG_BLOCK, &mask, NULL); + + memset(&act, 0 , sizeof(act)); + act.sa_handler = trapUSR2; + sigaction(SIGUSR2, &act, nil); + + act.sa_handler = SIG_IGN; + sigaction(SIGCHLD, &act, nil); + + /* + * For the correct functioning of devcmd in the + * face of exiting slaves + */ + signal(SIGPIPE, SIG_IGN); + if(signal(SIGTERM, SIG_IGN) != SIG_IGN) + signal(SIGTERM, cleanexit); + if(signal(SIGINT, SIG_IGN) != SIG_IGN) + signal(SIGINT, cleanexit); + + if(sflag == 0) { + act.sa_handler = trapBUS; + sigaction(SIGBUS, &act, nil); + act.sa_handler = trapILL; + sigaction(SIGILL, &act, nil); + act.sa_handler = trapSEGV; + sigaction(SIGSEGV, &act, nil); + act.sa_handler = trapFPE; + sigaction(SIGFPE, &act, nil); + } + + p = newproc(); + p->kstack = stackalloc(p, &tos); + + pw = getpwuid(getuid()); + if(pw != nil) + kstrdup(&eve, pw->pw_name); + else + print("cannot getpwuid\n"); + + p->env->uid = getuid(); + p->env->gid = getgid(); + + executeonnewstack(tos, emuinit, imod); +} + +int +readkbd(void) +{ + int n; + char buf[1]; + + n = read(0, buf, sizeof(buf)); + if(n < 0) + print("keyboard close (n=%d, %s)\n", n, strerror(errno)); + if(n <= 0) + pexit("keyboard thread", 0); + + switch(buf[0]) { + case '\r': + buf[0] = '\n'; + break; + case DELETE: + buf[0] = 'H' - '@'; + break; + case CTRLC: + cleanexit(0); + break; + } + return buf[0]; +} + +/* + * Return an abitrary millisecond clock time + */ +long +osmillisec(void) +{ + static long sec0 = 0, usec0; + struct timeval t; + + if(gettimeofday(&t,(struct timezone*)0)<0) + return 0; + + if(sec0 == 0) { + sec0 = t.tv_sec; + usec0 = t.tv_usec; + } + return (t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000; +} + +/* + * Return the time since the epoch in nanoseconds and microseconds + * The epoch is defined at 1 Jan 1970 + */ +vlong +osnsec(void) +{ + struct timeval t; + + gettimeofday(&t, nil); + return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000; +} + +vlong +osusectime(void) +{ + struct timeval t; + + gettimeofday(&t, nil); + return (vlong)t.tv_sec * 1000000 + t.tv_usec; +} + +int +osmillisleep(ulong milsec) +{ + struct timespec time; + + time.tv_sec = milsec/1000; + time.tv_nsec= (milsec%1000)*1000000; + nanosleep(&time, NULL); + return 0; +} + +int +limbosleep(ulong milsec) +{ + return osmillisleep(milsec); +} + +void +osyield(void) +{ + sched_yield(); +} + +void +ospause(void) +{ + for(;;) + pause(); +} + +void +oslopri(void) +{ + setpriority(PRIO_PROCESS, 0, getpriority(PRIO_PROCESS,0)+4); +} + +static struct { + Lock l; + void *free; +} stacklist; + +static void +_stackfree(void *stack) +{ + *((void **)stack) = stacklist.free; + stacklist.free = stack; +} + +static void +stackfreeandexit(void *stack) +{ + lock(&stacklist.l); + _stackfree(stack); + unlockandexit(&stacklist.l.val); +} + +static void * +stackalloc(Proc *p, void **tos) +{ + void *rv; + lock(&stacklist.l); + if (stacklist.free == 0) { + int x; + /* + * obtain some more by using sbrk() + */ + void *more = sbrk(KSTACK * (NSTACKSPERALLOC + 1)); + if (more == 0) + panic("stackalloc: no more stacks"); + /* + * align to KSTACK + */ + more = (void *)((((unsigned long)more) + (KSTACK - 1)) & ~(KSTACK - 1)); + /* + * free all the new stacks onto the freelist + */ + for (x = 0; x < NSTACKSPERALLOC; x++) + _stackfree((char *)more + KSTACK * x); + } + rv = stacklist.free; + stacklist.free = *(void **)rv; + unlock(&stacklist.l); + *tos = rv + KSTACK - sizeof(void *); + *(Proc **)rv = p; + return rv; +} diff --git a/emu/Nt/audio.c b/emu/Nt/audio.c new file mode 100644 index 00000000..e1bc6f86 --- /dev/null +++ b/emu/Nt/audio.c @@ -0,0 +1,810 @@ +#define Unknown win_Unknown +#include <windows.h> +#include <mmsystem.h> +#undef Unknown + +#include "dat.h" +#include "fns.h" +#include "error.h" + +#define Audio_Mic_Val 0 +#define Audio_Linein_Val -1 + +#define Audio_Speaker_Val 0 +#define Audio_Headphone_Val -1 +#define Audio_Lineout_Val -1 + +#define Audio_Pcm_Val WAVE_FORMAT_PCM +#define Audio_Ulaw_Val (WAVE_FORMAT_PCM+1) +#define Audio_Alaw_Val (WAVE_FORMAT_PCM+2) + +#define Audio_Max_Queue 8 + +#define BUFLEN 1000 + +#define INISOPEN 0x00000002 // the microphone is open +#define OUTISOPEN 0x00000004 // the speaker is open +#define INPUTISGOING 0x00000020 // microphone is being recorded/read + +#include "audio.h" +#include "audio-tbls.c" + +static int debug = 0; + +/* TO DO: sensible expression of double-buffering */ +#define Ping 0 +#define Pong 1 + +static HWAVEIN audio_file_in; +static HWAVEOUT audio_file_out; + +static long out_buf_count; + +typedef struct _awin { + WAVEHDR hdr; + long sz; + char* ptr; + char data[Audio_Max_Buf]; +} AWin; + +static AWin audio_ping; +static AWin audio_pong; + +static long paddle = Ping; +static int ping_is_filling; +static int pong_is_filling; + +static long audio_flags = 0; +static int audio_init = 0; + +static QLock flag_lock; + +static Audio_t av; + +static HANDLE outlock; +static HANDLE inlock; + +static int audio_open_in(HWAVEIN*, Audio_d*); +static int audio_open_out(HWAVEOUT*, Audio_d*); +static void audio_close_in(void); +static void audio_close_out(void); +static void CALLBACK waveInProc(HWAVEIN, UINT, DWORD, DWORD, DWORD); +static void CALLBACK waveOutProc(HWAVEOUT, UINT, DWORD, DWORD, DWORD); + +#define AUDIOIN 0 +#define AUDIOOUT 1 + +/* +* Error routines +*/ +static int +audioerror(unsigned int code, int in_out, char *msg) +{ + char errorText[MAXERRORLENGTH]; + + if (code != MMSYSERR_NOERROR) { + switch(in_out) { + case AUDIOIN: + waveInGetErrorText(code, errorText, sizeof(errorText)); + //print("ERROR -- %s: %s\n", msg, errorText); + return(-1); + case AUDIOOUT: + waveOutGetErrorText(code, errorText, sizeof(errorText)); + //print("ERROR -- %s: %s\n", msg, errorText); + return(-1); + default: + print("%s: Unknown device\n", msg); + } + } + //print("TRACE %s\n", msg); + return 0; +} + +void +audio_file_init(void) +{ + audio_info_init(&av); +} + +void +audio_file_open(Chan *c, int omode) +{ + int in_is_open = 0; + + switch(omode){ + case OREAD: + qlock(&flag_lock); + + if(waserror()) { + qunlock(&flag_lock); + nexterror(); + } + + if(audio_flags & INISOPEN) + error(Einuse); + + inlock = CreateMutex(NULL, FALSE, NULL); + if(inlock == NULL) + error(Einuse); + + if(!audio_open_in(&audio_file_in, &av.in) ) { + CloseHandle(inlock); + error(Ebadarg); + } + + ping_is_filling = 0; + pong_is_filling = 0; + paddle = Ping; + audio_flags |= INISOPEN; + + poperror(); + qunlock(&flag_lock); + break; + case OWRITE: + qlock(&flag_lock); + if(waserror()){ + qunlock(&flag_lock); + nexterror(); + } + + if(audio_flags & OUTISOPEN) + error(Einuse); + + outlock = CreateMutex(NULL, FALSE, NULL); + if(outlock == NULL) + error(Einuse); + + if(!audio_open_out(&audio_file_out, &av.out) ) { + CloseHandle(outlock); + error(Ebadarg); + } + + out_buf_count = 0; + audio_flags |= OUTISOPEN; + + poperror(); + qunlock(&flag_lock); + break; + case ORDWR: + qlock(&flag_lock); + if(waserror()){ + qunlock(&flag_lock); + if(in_is_open) + audio_close_in(); + nexterror(); + } + + if((audio_flags & INISOPEN) || (audio_flags & OUTISOPEN)) + error(Einuse); + + if(!audio_open_in(&audio_file_in, &av.in) ) + error(Ebadarg); + + in_is_open = 1; + + if(!audio_open_out(&audio_file_out, &av.out)) { + CloseHandle(outlock); + error(Ebadarg); + } + + inlock = CreateMutex(NULL, FALSE, NULL); + if(inlock == NULL) + error(Einuse); + + outlock = CreateMutex(NULL, FALSE, NULL); + if(outlock == NULL) { + CloseHandle(inlock); + error(Einuse); + } + + audio_flags |= INISOPEN; + audio_flags |= OUTISOPEN; + ping_is_filling = 0; + pong_is_filling = 0; + paddle = Ping; + out_buf_count = 0; + + poperror(); + qunlock(&flag_lock); + break; + default: + error(Egreg); + } +} + +static int +audio_open_in(HWAVEIN* h, Audio_d* d) +{ + HWAVEIN th; + WAVEFORMATEX format; + + format.wFormatTag = d->enc; + format.nChannels = d->chan; + format.nSamplesPerSec = d->rate; + format.wBitsPerSample = d->bits; + format.nBlockAlign = (d->chan * d->bits) / Bits_Per_Byte; + format.nAvgBytesPerSec = + format.nSamplesPerSec * format.nBlockAlign; + format.cbSize = 0; + + if (audioerror( + waveInOpen(&th, WAVE_MAPPER, &format, (DWORD)waveInProc, 0, CALLBACK_FUNCTION), + AUDIOIN, + "cannot open microphone/line-in") == 0) { + *h = th; + return 1; + } + return 0; +} + +static int +audio_open_out(HWAVEOUT* h, Audio_d* d) +{ + unsigned int code; + HWAVEOUT th; + WAVEFORMATEX format; + + format.wFormatTag = d->enc; + format.nChannels = d->chan; + format.nSamplesPerSec = d->rate; + format.wBitsPerSample = d->bits; + format.nBlockAlign = (d->chan * d->bits) / Bits_Per_Byte; + format.nAvgBytesPerSec = + format.nSamplesPerSec * format.nBlockAlign; + format.cbSize = 0; + + code = waveOutOpen(&th, WAVE_MAPPER, &format, (DWORD)waveOutProc, 0, CALLBACK_FUNCTION); + + if (audioerror(code, AUDIOOUT, "cannot open speaker/line-out") == 0) { + out_buf_count = 0; + *h = th; + return 1; + } + + return 0; +} + +void +audio_file_close(Chan *c) +{ + switch(c->mode){ + case OREAD: + qlock(&flag_lock); + audio_close_in(); + audio_flags &= ~(INISOPEN|INPUTISGOING); + CloseHandle(inlock); + qunlock(&flag_lock); + break; + case OWRITE: + qlock(&flag_lock); + audio_close_out(); + audio_flags &= ~OUTISOPEN; + CloseHandle(outlock); + qunlock(&flag_lock); + break; + case ORDWR: + qlock(&flag_lock); + audio_close_in(); + audio_close_out(); + + audio_flags &= ~(INISOPEN|INPUTISGOING|OUTISOPEN); + + CloseHandle(outlock); + CloseHandle(inlock); + qunlock(&flag_lock); + break; + } +} + +static void +audio_close_in() +{ + audioerror(waveInStop(audio_file_in), AUDIOIN, "audio_close_in Stop"); + audioerror(waveInReset(audio_file_in), AUDIOIN, "audio_close_in Reset"); + + audioerror(waveInUnprepareHeader(audio_file_in, &audio_ping.hdr, + sizeof(WAVEHDR)), AUDIOIN, "in un prepare ping header"); + audio_ping.sz = 0; + audio_ping.ptr = &audio_ping.data[0]; + audioerror(waveInUnprepareHeader(audio_file_in, &audio_pong.hdr, + sizeof(WAVEHDR)), AUDIOIN, "in un prepare pong header"); + audio_pong.sz = 0; + audio_pong.ptr = &audio_pong.data[0]; + + audioerror(waveInClose(audio_file_in), AUDIOIN, "in close"); + +} + +static void +audio_close_out() +{ +Again: + WaitForSingleObject(outlock, INFINITE); + while(out_buf_count > 0) { + ReleaseMutex(outlock); + sleep(0); + goto Again; + } + ReleaseMutex(outlock); + + audioerror(waveOutReset(audio_file_out), AUDIOOUT, "close wave out reset"); + audioerror(waveOutClose(audio_file_out), AUDIOOUT, "closing out device"); +} + + +long +audio_file_read(Chan *c, void *va, long count, vlong offset) +{ + MMRESULT status; + long len = av.in.buf * Audio_Max_Buf / Audio_Max_Val; + char *v = (char *) va; + char *p; + long ba, n, chunk, total; + + + qlock(&flag_lock); + WaitForSingleObject(inlock, INFINITE); + + if(waserror()) { + audioerror(waveInStop(audio_file_in), AUDIOIN, + "audio_file_read Stop 1"); + audioerror(waveInReset(audio_file_in), AUDIOIN, + "audio_file_read Reset 1"); + audioerror(waveInUnprepareHeader(audio_file_in, + &audio_ping.hdr, sizeof(WAVEHDR)), AUDIOIN, + "in unprepare ping"); + audioerror(waveInUnprepareHeader(audio_file_in, + &audio_pong.hdr, sizeof(WAVEHDR)), + AUDIOIN, "in unprepare pong"); + + audio_ping.sz = 0; + audio_ping.ptr = &audio_ping.data[0]; + audio_pong.sz = 0; + audio_pong.ptr = &audio_pong.data[0]; + + ping_is_filling = pong_is_filling = 0; + paddle = Ping; + + qunlock(&flag_lock); + ReleaseMutex(inlock); + + nexterror(); + } + + if(!(audio_flags & INISOPEN)) + error(Eperm); + + /* check for block alignment */ + ba = av.in.bits * av.in.chan / Bits_Per_Byte; + + if(len < 1 || count % ba) + error(Ebadarg); + + if(!(audio_flags & INPUTISGOING)) { + if(audioerror(waveInStart(audio_file_in), AUDIOIN, + "in start") == -1) + error(Eio); + + audio_ping.sz = 0; + audio_ping.ptr = &audio_ping.data[0]; + audio_ping.hdr.lpData = audio_ping.ptr; + audio_ping.hdr.dwBufferLength = len; + audio_ping.hdr.dwUser = Ping; + audio_ping.hdr.dwFlags = 0; + + status = waveInPrepareHeader(audio_file_in, &audio_ping.hdr, + sizeof(WAVEHDR)); + + if (audioerror(status, AUDIOIN, "in prepare header") == -1) + error(Eio); + + audio_pong.sz = 0; + audio_pong.ptr = &audio_pong.data[0]; + audio_pong.hdr.lpData = audio_pong.ptr; + audio_pong.hdr.dwBufferLength = len; + audio_pong.hdr.dwUser = Pong; + audio_pong.hdr.dwFlags = 0; + + status = waveInPrepareHeader(audio_file_in, &audio_pong.hdr, + sizeof(WAVEHDR)); + + if (audioerror(status, AUDIOIN, "in prepare header") == -1) + error(Eio); + + status = waveInAddBuffer(audio_file_in, &audio_ping.hdr, + sizeof(WAVEHDR)); + if (audioerror(status, AUDIOIN, "file_read Add Buffer")== -1){ + waveInUnprepareHeader(audio_file_in, &audio_ping.hdr, + sizeof(WAVEHDR)); + audio_ping.sz = 0; + audio_ping.ptr = &audio_ping.data[0]; + error(Eio); + } + + ping_is_filling = 1; + pong_is_filling = 0; + paddle = Ping; + audio_flags |= INPUTISGOING; + } + poperror(); + ReleaseMutex(inlock); + + total = 0; + +Draining: + + WaitForSingleObject(inlock, INFINITE); + if(waserror()) { + audioerror(waveInStop(audio_file_in), AUDIOIN, + "audio_file_read Stop 2"); + audioerror(waveInReset(audio_file_in), AUDIOIN, + "audio_file_read Reset 2"); + audioerror(waveInUnprepareHeader(audio_file_in, + &audio_ping.hdr, sizeof(WAVEHDR)), AUDIOIN, + "in unprepare ping"); + audioerror(waveInUnprepareHeader(audio_file_in, + &audio_pong.hdr, sizeof(WAVEHDR)), AUDIOIN, + "in unprepare pong"); + + audio_ping.sz = 0; + audio_ping.ptr = &audio_ping.data[0]; + audio_pong.sz = 0; + audio_pong.ptr = &audio_pong.data[0]; + + audio_flags &= ~INPUTISGOING; + + ReleaseMutex(inlock); + qunlock(&flag_lock); + nexterror(); + } + + while((total < count) && ((audio_ping.sz > 0) || (audio_pong.sz > 0))) { + n = paddle == Ping ? audio_ping.sz : audio_pong.sz; + p = paddle == Ping ? audio_ping.ptr : audio_pong.ptr; + + chunk = min(n, count - total); + + memmove(v+total, p , chunk); + + total += chunk; + + if(paddle == Ping) { + if(!pong_is_filling) { + + if(audioerror(waveInAddBuffer(audio_file_in, + &audio_pong.hdr, sizeof(WAVEHDR)), AUDIOIN, + "draining ping calling add buffer pong") == -1) + error(Eio); + + pong_is_filling = 1; + } + + audio_ping.sz -= chunk; + if(audio_ping.sz > 0) { + audio_ping.ptr += chunk; + } else { + audio_ping.ptr = &audio_ping.data[0]; + ping_is_filling = 0; + paddle = Pong; + } + } else { + if(!ping_is_filling) { + + if(audioerror(waveInAddBuffer(audio_file_in, + &audio_ping.hdr, sizeof(WAVEHDR)), AUDIOIN, + "draining pong calling add buffer ping") == -1) + error(Eio); + + ping_is_filling = 1; + } + + audio_pong.sz -= chunk; + if(audio_pong.sz > 0) { + audio_pong.ptr += chunk; + } else { + audio_pong.ptr = &audio_pong.data[0]; + pong_is_filling = 0; + paddle = Ping; + } + } + } + + poperror(); + + ReleaseMutex(inlock); + + if(total == count) { + qunlock(&flag_lock); + return count; + } + +Filling: + WaitForSingleObject(inlock, INFINITE); + while((audio_ping.sz < 1) && (audio_pong.sz < 1)) { + ReleaseMutex(inlock); + sleep(0); + goto Filling; + } + ReleaseMutex(inlock); + + goto Draining; +} + + +long +audio_file_write(Chan *c, void *va, long count, vlong offset) +{ + MMRESULT status; + WAVEHDR *hHdr = (WAVEHDR *) NULL; + char *hData = NULL; + char *p = (char *) va; + long ba; + long bufsz; + long chunk; + long total; + + qlock(&flag_lock); + if(waserror()){ + qunlock(&flag_lock); + nexterror(); + } + + if(!(audio_flags & OUTISOPEN)) + error(Eperm); + + /* check for block alignment */ + ba = av.out.bits * av.out.chan / Bits_Per_Byte; + + if(count % ba) + error(Ebadarg); + + bufsz = av.out.buf * Audio_Max_Buf / Audio_Max_Val; + + if(bufsz < 1) + error(Ebadarg); + + total = 0; + + while(total < count) { + +Again: + chunk = min(bufsz, count - total); + +Drain: + WaitForSingleObject(outlock, INFINITE); + while(out_buf_count > bufsz) { + ReleaseMutex(outlock); + sleep(0); + goto Drain; + } + + if(out_buf_count == 0) + audioerror(waveOutReset(audio_file_out), AUDIOOUT, "wave out reset"); + ReleaseMutex(outlock); + + /* + * allocate and lock the memory for the wave header + * and data blocks + */ + hHdr = (WAVEHDR *) malloc(sizeof(WAVEHDR)); + if (!hHdr) + error(Enomem); + + hData = malloc(chunk); + if (!hData) { + free(hHdr); + error(Enomem); + } + + /* + * initialize the wave header struct + */ + + /* + * copy user data into write Q + */ + memmove(hData, p+total, chunk); + + hHdr->lpData = hData; + hHdr->dwBufferLength = chunk; + hHdr->dwBytesRecorded = 0; + hHdr->dwUser = chunk; + hHdr->dwFlags = 0; + hHdr->dwLoops = 0; + hHdr->lpNext = 0; + hHdr->reserved = 0; + + status = waveOutPrepareHeader(audio_file_out, hHdr, sizeof(WAVEHDR)); + + if (audioerror(status, AUDIOOUT, "out prepare header") == -1) { + free(hHdr); + free(hData); + error(Eio); + } + + status = + waveOutWrite(audio_file_out, hHdr, sizeof(WAVEHDR)); + + if (audioerror(status, AUDIOOUT, "out write data") == -1) { + waveOutUnprepareHeader(audio_file_out, hHdr, sizeof(WAVEHDR)); + free(hHdr); + free(hData); + error(Eio); + } + + WaitForSingleObject(outlock, INFINITE); + out_buf_count += chunk; + ReleaseMutex(outlock); + + total += chunk; + + } + + poperror(); + qunlock(&flag_lock); + osmillisleep(1); /* hack to get around thread scheduler */ + + return count; +} + +void CALLBACK +waveInProc(HWAVEIN hwi, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) +{ + LPWAVEHDR hHdr; + long count; + + switch(uMsg) { + case WIM_OPEN: + break; + case WIM_CLOSE: + break; + case WIM_DATA: + hHdr = (LPWAVEHDR)dwParam1; + if(hHdr != NULL) { + count = hHdr->dwBytesRecorded; + if(count > 0) { + WaitForSingleObject(inlock, INFINITE); + if(hHdr->dwUser == Ping) + audio_ping.sz = count; + else + audio_pong.sz = count; + ReleaseMutex(inlock); + } + } + break; + } + return; +} + + +void CALLBACK +waveOutProc(HWAVEOUT hwo, UINT uMsg, DWORD dwOutstance, DWORD dwParam1, DWORD dwParam2) +{ + LPWAVEHDR hHdr; + + switch(uMsg) { + case WOM_DONE: + hHdr = (LPWAVEHDR)dwParam1; + if(hHdr != NULL) { + WaitForSingleObject(outlock, INFINITE); + out_buf_count -= hHdr->dwUser; + ReleaseMutex(outlock); + audioerror( + waveOutUnprepareHeader( + audio_file_out, hHdr, sizeof(WAVEHDR)), + AUDIOOUT, "out un prepare header"); + if(hHdr->lpData != NULL) + free(hHdr->lpData); + free(hHdr); + } + break; + case WOM_CLOSE: + WaitForSingleObject(outlock, INFINITE); + out_buf_count = 0; + ReleaseMutex(outlock); + break; + case WOM_OPEN: + break; + } +} + +long +audio_ctl_write(Chan *c, void *va, long count, vlong offset) +{ + WAVEFORMATEX format; + Audio_t tmpav = av; + + tmpav.in.flags = 0; + tmpav.out.flags = 0; + + if(!audioparse(va, count, &tmpav)) + error(Ebadarg); + + if((tmpav.in.enc != Audio_Pcm_Val) || (tmpav.out.enc != Audio_Pcm_Val)) + error(Ebadarg); + + if(tmpav.in.flags & AUDIO_MOD_FLAG) { + format.wFormatTag = tmpav.in.enc; + format.wBitsPerSample = tmpav.in.bits; + format.nChannels = tmpav.in.chan; + format.nSamplesPerSec = tmpav.in.rate; + format.nBlockAlign = + (tmpav.in.chan * tmpav.in.bits) / Bits_Per_Byte; + format.nAvgBytesPerSec = + format.nSamplesPerSec * format.nBlockAlign; + format.cbSize = 0; + + if(audioerror( + waveInOpen(NULL, WAVE_MAPPER, &format, 0, 0, WAVE_FORMAT_QUERY), + AUDIOIN, "cannot open microphone/line-in to test parameters") == -1) + error(Ebadarg); + + qlock(&flag_lock); + + if(waserror()){ + qunlock(&flag_lock); + nexterror(); + } + + if(audio_flags & INISOPEN) { + audio_close_in(); + audio_flags &= ~INISOPEN; + audio_flags &= ~INPUTISGOING; + if(!audio_open_in(&audio_file_in, &tmpav.in)) + error(Eio); + audio_flags |= INISOPEN; + } + poperror(); + qunlock(&flag_lock); + } + + if(tmpav.out.flags & AUDIO_MOD_FLAG) { + + format.wFormatTag = tmpav.out.enc; + format.wBitsPerSample = tmpav.out.bits; + format.nChannels = tmpav.out.chan; + format.nSamplesPerSec = tmpav.out.rate; + format.nBlockAlign = + (tmpav.out.chan * tmpav.out.bits) / Bits_Per_Byte; + format.nAvgBytesPerSec = + format.nSamplesPerSec * format.nBlockAlign; + format.cbSize = 0; + + if (audioerror(waveOutOpen(NULL, WAVE_MAPPER, + &format, + 0, 0, WAVE_FORMAT_QUERY), + AUDIOOUT, "cannot open output to test parameters") == -1) + error(Ebadarg); + + qlock(&flag_lock); + if(waserror()){ + qunlock(&flag_lock); + nexterror(); + } + if(audio_flags & OUTISOPEN) { + audio_close_out(); + + audio_flags &= ~OUTISOPEN; + if(!audio_open_out(&audio_file_out, &tmpav.out)) { + error(Eio); + return -1; + } + audio_flags |= OUTISOPEN; + } + poperror(); + qunlock(&flag_lock); + } + + tmpav.in.flags = 0; + tmpav.out.flags = 0; + + av = tmpav; + + return count; +} + +Audio_t* +getaudiodev(void) +{ + return &av; +} diff --git a/emu/Nt/cmd.c b/emu/Nt/cmd.c new file mode 100644 index 00000000..764d328b --- /dev/null +++ b/emu/Nt/cmd.c @@ -0,0 +1,243 @@ +#define Unknown win_Unknown +#define UNICODE +#include <windows.h> +#include <winbase.h> +#include <winsock.h> +#undef Unknown +#include "dat.h" +#include "fns.h" +#include "error.h" + +extern int nth2fd(HANDLE); +extern wchar_t *widen(char*); + +/* + * thanks to rcsh for these. + * + * windows quoting rules - I think + * Words are separated by space or tab + * Words containing a space or tab can be quoted using " + * 2N backslashes + " ==> N backslashes and end quote + * 2N+1 backslashes + " ==> N backslashes + literal " + * N backslashes not followed by " ==> N backslashes + */ +static char * +dblquote(char *cmd, char *s) +{ + int nb; + char *p; + + for(p=s; *p; p++) + if(*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == '"') + break; + + if(*p == 0){ /* easy case */ + strcpy(cmd, s); + return cmd+(p-s); + } + + *cmd++ = '"'; + for(;;) { + for(nb=0; *s=='\\'; nb++) + *cmd++ = *s++; + + if(*s == 0) { /* trailing backslashes -> 2N */ + while(nb-- > 0) + *cmd++ = '\\'; + break; + } + + if(*s == '"') { /* literal quote -> 2N+1 backslashes */ + while(nb-- > 0) + *cmd++ = '\\'; + *cmd++ = '\\'; /* escape the quote */ + } + *cmd++ = *s++; + } + + *cmd++ = '"'; + *cmd = 0; + + return cmd; +} + +static char * +ntquotedcmd(char **argv) +{ + int i, n; + char *cmd, *p; + + /* conservatively calculate length of command; + * backslash expansion can cause growth in dblquote(). + */ + for(i=0,n=0; argv[i]; i++) + n += 2*strlen(argv[i]); + n++; + + cmd = malloc(n); + if(cmd == nil) + return nil; + for(i=0,p=cmd; argv[i]; i++) { + p = dblquote(p, argv[i]); + *p++ = ' '; + } + if(p != cmd) + p--; + *p = 0; + + return cmd; +} + +static HANDLE +exporthandle(HANDLE h, int close) +{ + HANDLE cp, dh; + DWORD flags = DUPLICATE_SAME_ACCESS; + if (close) + flags |= DUPLICATE_CLOSE_SOURCE; + cp = GetCurrentProcess(); + if (!DuplicateHandle(cp, h, cp, &dh, DUPLICATE_SAME_ACCESS, 1, flags)) + return nil; + return dh; +} + +/* TO DO: check that oserrstr will have the right text on error */ + +void* +oscmd(char **args, int nice, char *dir, int *fd) +{ + STARTUPINFO si; + SECURITY_ATTRIBUTES sec; + HANDLE rh, wh, eh, srh, swh, seh; + PROCESS_INFORMATION pinfo; + char *cmd; + wchar_t *wcmd, *wdir; + int prio; + + wdir = nil; + if(dir != nil) + wdir = widen(dir); + + cmd = ntquotedcmd(args); + if(cmd == nil) + error(Enomem); + + wcmd = widen(cmd); + sec.nLength = sizeof(sec); + sec.lpSecurityDescriptor = 0; + sec.bInheritHandle = 0; + rh = wh = eh = srh = swh = seh = nil; + if(!CreatePipe(&rh, &swh, &sec, 0)) + goto Error; + if(!CreatePipe(&srh, &wh, &sec, 0)) + goto Error; + if(!CreatePipe(&seh, &eh, &sec, 0)) + goto Error; + rh = exporthandle(rh, 1); + if(rh == nil) + goto Error; + wh = exporthandle(wh, 1); + if(wh == nil) + goto Error; + eh = exporthandle(eh, 1); + if(eh == nil) + goto Error; + + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES; + si.wShowWindow = SW_SHOW; + si.hStdInput = rh; + si.hStdOutput = wh; + si.hStdError = eh; + + prio = 0; + if(nice){ + prio = IDLE_PRIORITY_CLASS; + if(nice > 1) + prio |= CREATE_SUSPENDED; + } + + /* default of nil for wpath seems to be what we want; nil for env exports our current one */ + if(!CreateProcess(nil/*wpath*/, wcmd, 0, 0, 1, + CREATE_NEW_PROCESS_GROUP|CREATE_DEFAULT_ERROR_MODE|prio, + 0 /*env*/, wdir, &si, &pinfo)){ + //print("can't create process '%Q' %d\n", wcmd, GetLastError()); + goto Error; + } + + fd[0] = nth2fd(swh); + fd[1] = nth2fd(srh); + fd[2] = nth2fd(seh); + if(fd[1] == 1 || fd[2] == 2) + panic("invalid mapping of handle to fd"); + CloseHandle(si.hStdInput); + CloseHandle(si.hStdOutput); + CloseHandle(si.hStdError); + + if(prio & CREATE_SUSPENDED){ + if(nice > 1) + SetThreadPriority(pinfo.hThread, + nice>3? THREAD_PRIORITY_IDLE: + nice>2? THREAD_PRIORITY_LOWEST: + THREAD_PRIORITY_BELOW_NORMAL); + ResumeThread(pinfo.hThread); + } + CloseHandle(pinfo.hThread); + /* don't close process handle */ + free(cmd); + free(wcmd); + free(wdir); + return pinfo.hProcess; + +Error: + if(rh) + CloseHandle(rh); + if(wh) + CloseHandle(wh); + if(eh) + CloseHandle(eh); + if(srh) + CloseHandle(srh); + if(swh) + CloseHandle(swh); + if(seh) + CloseHandle(seh); + free(cmd); + free(wcmd); + free(wdir); + return nil; +} + +int +oscmdwait(void *v, char *buf, int n) +{ + int status; + HANDLE proc = (HANDLE)v; + + /* need not worry about being interrupted */ + if(WaitForSingleObject(proc, INFINITE) == WAIT_FAILED) + return -1; + if(!GetExitCodeProcess(proc, &status)) + status = 1; + if(status) + n = snprint(buf, n, "0 0 0 0 'status %d'", status); + else + n = snprint(buf, n, "0 0 0 0 ''"); + return n; + +} + +int +oscmdkill(void *v) +{ + if(TerminateProcess((HANDLE)v, 666) == FALSE) + return -1; + return 0; +} + +void +oscmdfree(void *v) +{ + CloseHandle((HANDLE)v); +} diff --git a/emu/Nt/devarch.c b/emu/Nt/devarch.c new file mode 100644 index 00000000..2ddaf763 --- /dev/null +++ b/emu/Nt/devarch.c @@ -0,0 +1,414 @@ +/* + * platform-specific interface + */ + +#define Unknown win_Unknown +#define UNICODE +#include <windows.h> +#include <winbase.h> +#include <winsock.h> +#undef Unknown +#include "dat.h" +#include "fns.h" +#include "error.h" + + +enum{ + Qdir, + Qarchctl, + Qcputype, + Qregquery, + Qhostmem +}; + +static +Dirtab archtab[]={ + ".", {Qdir, 0, QTDIR}, 0, 0555, + "archctl", {Qarchctl, 0}, 0, 0444, + "cputype", {Qcputype}, 0, 0444, + "regquery", {Qregquery}, 0, 0666, + "hostmem", {Qhostmem}, 0, 0444, +}; + +typedef struct Value Value; +struct Value { + int type; + int size; + union { + ulong w; + vlong q; + Rune utf[1]; /* more allocated as required */ + char data[1]; + }; +}; + +typedef struct Regroot Regroot; +struct Regroot { + char* name; + HKEY root; +}; + +static Regroot roots[] = { + {"HKEY_CLASSES_ROOT", HKEY_CLASSES_ROOT}, + {"HKEY_CURRENT_CONFIG", HKEY_CURRENT_CONFIG}, + {"HKEY_CURRENT_USER", HKEY_CURRENT_USER}, + {"HKEY_LOCAL_MACHINE", HKEY_LOCAL_MACHINE}, + {"HKEY_PERFORMANCE_DATA", HKEY_PERFORMANCE_DATA}, + {"HKEY_USERS", HKEY_USERS}, +}; + +static struct { + ulong mhz; + int ncpu; + char cpu[64]; +} arch; + +static QLock reglock; + +extern wchar_t *widen(char*); +static Value* getregistry(HKEY, Rune*, Rune*); +static int nprocs(void); + +static void +archinit(void) +{ + Value *v; + char *p; + + v = getregistry(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", L"ProcessorNameString"); + if(v != nil){ + snprint(arch.cpu, sizeof(arch.cpu), "%S", v->utf); + if((p = strrchr(arch.cpu, ' ')) != nil) + for(; p >= arch.cpu && *p == ' '; p--) + *p = '\0'; + free(v); + }else{ + v = getregistry(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", L"VendorIdentifier"); + if(v != nil){ + snprint(arch.cpu, sizeof(arch.cpu), "%S", v->utf); + free(v); + }else + snprint(arch.cpu, sizeof(arch.cpu), "unknown"); + } + v = getregistry(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", L"~MHz"); + if(v != nil){ + arch.mhz = v->w; + free(v); + } + arch.ncpu = nprocs(); +} + +static int +nprocs(void) +{ + int n; + char *p; + Rune *r; + Value *v; + n = 0; + for(;;){ + p = smprint("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\%d", n); + if(waserror()){ + free(p); + nexterror(); + } + r = widen(p); + free(p); + v = getregistry(HKEY_LOCAL_MACHINE, r, L"~MHz"); + free(r); + if(v == nil) + break; + free(v); + n++; + } + return n; +} + +static Chan* +archattach(char* spec) +{ + return devattach('a', spec); +} + +static Walkqid* +archwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, archtab, nelem(archtab), devgen); +} + +static int +archstat(Chan* c, uchar *db, int n) +{ + return devstat(c, db, n, archtab, nelem(archtab), devgen); +} + +static Chan* +archopen(Chan* c, int omode) +{ + return devopen(c, omode, archtab, nelem(archtab), devgen); +} + +static void +archclose(Chan* c) +{ + if((ulong)c->qid.path == Qregquery && c->aux != nil) + free(c->aux); +} + +static long +archread(Chan* c, void* a, long n, vlong offset) +{ + char *p; + Value *v; + int i, l; + MEMORYSTATUS mem; + + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, a, n, archtab, nelem(archtab), devgen); + case Qarchctl: + case Qcputype: + l = 0; + if((ulong)c->qid.path == Qcputype) + l = 4; + p = smalloc(READSTR); + if(waserror()){ + free(p); + nexterror(); + } + snprint(p, READSTR, "cpu %q %lud %d\n", arch.cpu, arch.mhz, arch.ncpu); + n = readstr(offset, a, n, p+l); + poperror(); + free(p); + break; + case Qregquery: + v = c->aux; + if(v == nil) + return 0; + p = smalloc(READSTR); + if(waserror()){ + free(p); + nexterror(); + } + switch(v->type){ + case REG_NONE: + n = readstr(offset, a, n, "nil"); + break; + case REG_DWORD: + snprint(p, READSTR, "int %ld", v->w); + n = readstr(offset, a, n, p); + break; +#ifdef REG_QWORD + case REG_QWORD: + snprint(p, READSTR, "int %lld", v->q); + n = readstr(offset, a, n, p); + break; +#endif + case REG_SZ: + case REG_EXPAND_SZ: + if(v->utf[0]) + snprint(p, READSTR, "str %Q", v->utf); + n = readstr(offset, a, n, p); + break; + case REG_MULTI_SZ: + l = snprint(p, READSTR, "str"); + for(i=0;;){ + l += snprint(p+l, READSTR-l, " %Q", v->utf+i); + while(v->utf[i++] != 0){ + /* skip */ + } + if(v->utf[i] == 0) + break; /* final terminator */ + } + n = readstr(offset, a, n, p); + break; + case REG_BINARY: + l = n; + n = readstr(offset, a, l, "bin"); + if(n >= 3){ + offset -= 3; + if(offset+l > v->size) + l = v->size - offset; + memmove((char*)a+n, v->data+offset, l); + n += l; + } + break; + default: + error("unknown registry type"); + n=0; + break; + } + poperror(); + free(p); + c->aux = nil; + free(v); + break; + case Qhostmem: + mem.dwLength = sizeof(mem); + GlobalMemoryStatus(&mem); /* GlobalMemoryStatusEx isn't on NT */ + p = smalloc(READSTR); + if(waserror()){ + free(p); + nexterror(); + } + snprint(p, READSTR, "load %ld\nphys %lud %lud\nvirt %lud %lud\nswap %lud %lud\n", + mem.dwMemoryLoad, + mem.dwAvailPhys, mem.dwTotalPhys, mem.dwAvailVirtual, mem.dwTotalVirtual, + mem.dwAvailPageFile, mem.dwTotalPageFile); + n = readstr(offset, a, n, p); + poperror(); + free(p); + break; + default: + n=0; + break; + } + return n; +} + +static long +archwrite(Chan* c, void* a, long n, vlong offset) +{ + Value *v; + int i; + Cmdbuf *cb; + Rune *key, *item; + + if((ulong)c->qid.path != Qregquery) + error(Eperm); + USED(offset); + if(c->aux != nil){ + free(c->aux); + c->aux = nil; + } + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + if(cb->nf < 3) + error(Ebadctl); + for(i=0; i<nelem(roots); i++) + if(strcmp(cb->f[0], roots[i].name) == 0) + break; + if(i >= nelem(roots)) + errorf("unknown root: %s", cb->f[0]); + key = widen(cb->f[1]); + if(waserror()){ + free(key); + nexterror(); + } + item = widen(cb->f[2]); + if(waserror()){ + free(item); + nexterror(); + } + v = getregistry(roots[i].root, key, item); + if(v == nil) + error(up->env->errstr); + c->aux = v; + poperror(); + free(item); + poperror(); + free(key); + poperror(); + free(cb); + return n; +} + +Dev archdevtab = { + 'a', + "arch", + + archinit, + archattach, + archwalk, + archstat, + archopen, + devcreate, + archclose, + archread, + devbread, + archwrite, + devbwrite, + devremove, + devwstat, +}; + +static void +regerr(int rc) +{ + Rune err[64]; + + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, + 0, rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + err, sizeof(err), 0); + errorf("%S", err); +} + +static Value* +getregistry(HKEY root, Rune *keyname, Rune *name) +{ + long res; + HKEY key; + DWORD dtype, n; + void* vp; + Value *val; + + qlock(®lock); + if(waserror()){ + qunlock(®lock); + return nil; + } + res = RegOpenKey(root, keyname, &key); + if(res != ERROR_SUCCESS) + regerr(res); + if(waserror()){ + RegCloseKey(key); + nexterror(); + } + n = 0; + res = RegQueryValueEx(key, name, NULL, &dtype, NULL, &n); + if(res != ERROR_SUCCESS) + regerr(res); + val = smalloc(sizeof(Value)+n); + if(waserror()){ + free(val); + nexterror(); + } + if(0) + fprint(2, "%S\\%S: %d %d\n", keyname, name, dtype, n); + val->type = dtype; + val->size = n; + switch(dtype){ + case REG_DWORD: + vp = &val->w; + break; +#ifdef REG_QWORD + case REG_QWORD: + vp = &val->q; + break; +#endif + case REG_SZ: + case REG_EXPAND_SZ: + case REG_MULTI_SZ: + vp = val->utf; + break; + case REG_BINARY: + case REG_NONE: + vp = val->data; + break; + default: + errorf("unsupported registry type: %d", dtype); + return nil; /* for compiler */ + } + res = RegQueryValueEx(key, name, NULL, NULL, vp, &n); + if(res != ERROR_SUCCESS) + regerr(res); + poperror(); + poperror(); + RegCloseKey(key); + poperror(); + qunlock(®lock); + return val; +} diff --git a/emu/Nt/deveia.c b/emu/Nt/deveia.c new file mode 100644 index 00000000..0ffa2e7a --- /dev/null +++ b/emu/Nt/deveia.c @@ -0,0 +1,672 @@ +/* + * Windows serial driver + * + * to do: + * scan the registry for serial ports? + */ + +#define Unknown win_Unknown +#include <windows.h> +#undef Unknown +#undef Sleep +#include "dat.h" +#include "fns.h" +#include "error.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdio.h> +#include <lm.h> +#include <direct.h> + +// local fcts +static void openport(int); +static void wrctl(int, char*); +static long rdstat(int, void*, long, ulong ); + +enum +{ + Devchar = 't', + + Ndataqid = 1, + Nctlqid, + Nstatqid, + Nqid = 3, /* number of QIDs */ + + Maxctl = 128, + + // in/out buffer sizes for comm port (NT requires an even number) + // set it to x* the max styx message rounded up to the + // nearest 4 byte value + CommBufSize = ((((8192+128)*2)+3) & ~3) +}; + +/* + * Macros to manage QIDs + */ +#define NETTYPE(x) ((x)&0x0F) +#define NETID(x) ((x)>>4) +#define NETQID(i,t) (((i)<<4)|(t)) + +static Dirtab *eiadir; +static int ndir; + +typedef struct Eia Eia; +struct Eia { + Ref r; + HANDLE comfh; //handle to open port + int restore; //flag to restore prev. states + DCB dcb; //win32 device control block used for restore + int id; //index to host port name in sysdev +}; + +// the same timeouts are used for all ports +// currently there is no Inferno interface to +// change the timeouts. +static COMMTIMEOUTS timeouts; + +// std win32 serial port names are COM1..COM4 +// however there can be more and they can be +// named anything. we should be more flexible +// pehaps adding a ctl command to allow you to +// access any win32 comm port +static char* sysdev[] = { + "COM1:", + "COM2:", + "COM3:", + "COM4:", + "COM5:", + "COM6:", + "COM7:", + "COM8:", + NULL +}; + +static Eia *eia; + +typedef struct OptTable OptTable; +struct OptTable { + char *str; + DWORD flag; +}; + +#define BAD ((DWORD)-1) + +// valid bit-per-byte sizes +static OptTable size[] = { + {"5", 5}, + {"6", 6}, + {"7", 7}, + {"8", 8}, + {NULL, BAD} +}; + +// valid stop bits +static OptTable stopbits[] = { + {"1", ONESTOPBIT}, + {"1.5", ONE5STOPBITS}, + {"2", TWOSTOPBITS}, + {NULL, BAD} +}; + +// valid parity settings +static OptTable parity[] = { + {"o", ODDPARITY}, + {"e", EVENPARITY}, + {"s", SPACEPARITY}, + {"m", MARKPARITY}, + {"n", NOPARITY}, + {NULL, NOPARITY} +}; + + +static char * +ftos(OptTable *tbl, DWORD flag) +{ + while(tbl->str && tbl->flag != flag) + tbl++; + if(tbl->str == 0) + return "unknown"; + return tbl->str; +} + +static DWORD +stof(OptTable *tbl, char *str) +{ + while(tbl->str && strcmp(tbl->str, str) != 0) + tbl++; + return tbl->flag; +} + +static void +eiainit(void) +{ + int i,x; + byte ports; //bitmask of active host ports + int nports; //number of active host ports + int max; //number of highest port + Dirtab *dp; + + // setup the timeouts; choose carefully + timeouts.ReadIntervalTimeout = 2; + timeouts.ReadTotalTimeoutMultiplier = 0; + timeouts.ReadTotalTimeoutConstant = 200; + timeouts.WriteTotalTimeoutMultiplier = 0; + timeouts.WriteTotalTimeoutConstant = 400; + + // check to see which ports exist by trying to open them + // keep results in a bitmask + ports = nports = max = 0; + for(i=0; (sysdev[i] != NULL) && (i<8); i++) { + HANDLE comfh = CreateFile(sysdev[i], 0, 0, NULL, /* no security attrs */ + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if(comfh != INVALID_HANDLE_VALUE) { + ports |= 1<<i; + CloseHandle(comfh); + nports++; + max = i; + } + } + + if(nports == 0) + return; //no ports + + // allocate directory table and eia structure + // for each active port. + ndir = Nqid*nports+1; + dp = eiadir = malloc(ndir*sizeof(Dirtab)); + if(dp == 0) + panic("eiainit"); + eia = malloc(nports*sizeof(Eia)); + if(eia == 0) { + free(dp); + panic("eiainit"); + } + + // fill in the directory table and initialize + // the eia structure. skip inactive ports. + sprint(dp->name, "."); + dp->qid.path = 0; + dp->qid.type = QTDIR; + dp->perm = DMDIR|0555; + dp++; + x = 0; // index in eia[] + for(i = 0; i <= max; i++) { + if( (ports & (1<<i)) == 0) + continue; //port 'i' is not active + sprint(dp->name, "eia%d", i); + dp->qid.path = NETQID(x, Ndataqid); + dp->perm = 0660; + dp++; + sprint(dp->name, "eia%dctl", i); + dp->qid.path = NETQID(x, Nctlqid); + dp->perm = 0660; + dp++; + sprint(dp->name, "eia%dstatus", i); + dp->qid.path = NETQID(x, Nstatqid); + dp->perm = 0660; + dp++; + // init the eia structure + eia[x].restore = 0; + eia[x].id = i; + x++; + } +} + +static Chan* +eiaattach(char *spec) +{ + if(eiadir == nil) + error(Enodev); + + return devattach(Devchar, spec); +} + +static Walkqid* +eiawalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, eiadir, ndir, devgen); +} + +static int +eiastat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, eiadir, ndir, devgen); +} + +static Chan* +eiaopen(Chan *c, int mode) +{ + int port = NETID(c->qid.path); + + c = devopen(c, mode, eiadir, ndir, devgen); + + switch(NETTYPE(c->qid.path)) { + case Nctlqid: + case Ndataqid: + case Nstatqid: + if(incref(&eia[port].r) != 1) + break; + if(waserror()) { + decref(&eia[port].r); + nexterror(); + } + openport(port); + poperror(); + break; + } + return c; +} + +static void +eiaclose(Chan *c) +{ + int port = NETID(c->qid.path); + + if((c->flag & COPEN) == 0) + return; + + switch(NETTYPE(c->qid.path)) { + case Nctlqid: + case Ndataqid: + case Nstatqid: + if(decref(&eia[port].r) == 0) { + osenter(); + CloseHandle(eia[port].comfh); + osleave(); + } + break; + } + +} + +static long +eiaread(Chan *c, void *buf, long n, vlong offset) +{ + DWORD cnt; + int port = NETID(c->qid.path); + BOOL good; + + if(c->qid.type & QTDIR) + return devdirread(c, buf, n, eiadir, ndir, devgen); + + switch(NETTYPE(c->qid.path)) { + case Ndataqid: + cnt = 0; + // if ReadFile timeouts and cnt==0 then just re-read + // this will give osleave() a chance to detect an + // interruption (i.e. killprog) + while(cnt==0) { + osenter(); + good = ReadFile(eia[port].comfh, buf, n, &cnt, NULL); + SleepEx(0,FALSE); //allow another thread access to port + osleave(); + if(!good) + oserror(); + } + return cnt; + case Nctlqid: + return readnum(offset, buf, n, eia[port].id, NUMSIZE); + case Nstatqid: + return rdstat(port, buf, n, offset); + } + + return 0; +} + +static long +eiawrite(Chan *c, void *buf, long n, vlong offset) +{ + DWORD cnt; + char cmd[Maxctl]; + int port = NETID(c->qid.path); + BOOL good; + uchar *data; + + if(c->qid.type & QTDIR) + error(Eperm); + + switch(NETTYPE(c->qid.path)) { + case Ndataqid: + cnt = 0; + data = (uchar*)buf; + // if WriteFile times out (i.e. return true; cnt<n) then + // allow osleave() to check for an interrupt otherwise try + // to send the unsent data. + while(n>0) { + osenter(); + good = WriteFile(eia[port].comfh, data, n, &cnt, NULL); + osleave(); + if(!good) + oserror(); + data += cnt; + n -= cnt; + } + return (data-(uchar*)buf); + case Nctlqid: + if(n >= sizeof(cmd)) + n = sizeof(cmd)-1; + memmove(cmd, buf, n); + cmd[n] = 0; + wrctl(port, cmd); + return n; + } + return 0; +} + +static int +eiawstat(Chan *c, uchar *dp, int n) +{ + Dir d; + int i; + + if(!iseve()) + error(Eperm); + if(c->qid.type & QTDIR) + error(Eperm); + if(NETTYPE(c->qid.path) == Nstatqid) + error(Eperm); + + n = convM2D(dp, n, &d, nil); + i = Nqid*NETID(c->qid.path)+NETTYPE(c->qid.path)-Ndataqid; + if(d.mode != ~0UL) + eiadir[i+1].perm = d.mode&0666; + return n; +} + +Dev eiadevtab = { + Devchar, + "eia", + + eiainit, + eiaattach, + eiawalk, + eiastat, + eiaopen, + devcreate, + eiaclose, + eiaread, + devbread, + eiawrite, + devbwrite, + devremove, + eiawstat +}; + + +// +// local functions +// + +/* + * open the indicated comm port and then set + * the default settings for the port. + */ +static void +openport(int port) +{ + Eia* p = &eia[port]; + + // open the port + p->comfh = CreateFile(sysdev[p->id], + GENERIC_READ|GENERIC_WRITE, //open underlying port for rd/wr + 0, //comm port can't be shared + NULL, //no security attrs + OPEN_EXISTING, //a must for comm port + FILE_ATTRIBUTE_NORMAL, //nonoverlapped io + NULL); //another must for comm port + + if(p->comfh == INVALID_HANDLE_VALUE) + oserror(); + if(waserror()){ + CloseHandle(p->comfh); + p->comfh = INVALID_HANDLE_VALUE; + nexterror(); + } + + // setup in/out buffers (NT requires an even number) + if(!SetupComm(p->comfh, CommBufSize, CommBufSize)) + oserror(); + + // either use existing settings or set defaults + if(!p->restore) { + // set default settings + if(!GetCommState(p->comfh, &p->dcb)) + oserror(); + p->dcb.BaudRate = 9600; + p->dcb.ByteSize = 8; + p->dcb.fParity = 0; + p->dcb.Parity = NOPARITY; + p->dcb.StopBits = ONESTOPBIT; + p->dcb.fInX = 0; //default to xoff + p->dcb.fOutX = 0; + p->dcb.fAbortOnError = 1; //read/write abort on err + } + + // set state and timeouts + if(!SetCommState(p->comfh, &p->dcb) || + !SetCommTimeouts(p->comfh, &timeouts)) + oserror(); + poperror(); +} + +/* + * Obtain status information on the com port. + */ +static long +rdstat(int port, void *buf, long n, ulong offset) +{ + HANDLE comfh = eia[port].comfh; + char str[Maxctl]; + char *s; + DCB dcb; + DWORD modemstatus; + DWORD porterr; + COMSTAT portstat; + int frame, overrun, i; + + // valid line control ids + static enum { + L_CTS, L_DSR, L_RING, L_DCD, L_DTR, L_RTS, L_MAX + }; + int status[L_MAX]; + + // line control strings (should match above id's) + static char* lines[] = { + "cts", "dsr", "ring", "dcd", "dtr", "rts", NULL + }; + + + // get any error conditions; also clears error flag + // and enables io + if(!ClearCommError(comfh, &porterr, &portstat)) + oserror(); + + // get comm port state + if(!GetCommState(comfh, &dcb)) + oserror(); + + // get modem line information + if(!GetCommModemStatus(comfh, &modemstatus)) + oserror(); + + // now set our local flags + status[L_CTS] = MS_CTS_ON & modemstatus; + status[L_DSR] = MS_DSR_ON & modemstatus; + status[L_RING] = MS_RING_ON & modemstatus; + status[L_DCD] = MS_RLSD_ON & modemstatus; + status[L_DTR] = FALSE; //?? cand this work: dcb.fDtrControl; + status[L_RTS] = FALSE; //?? dcb.fRtsControl; + frame = porterr & CE_FRAME; + overrun = porterr & CE_OVERRUN; + + /* TO DO: mimic native eia driver's first line */ + + s = seprint(str, str+sizeof(str), "opens %d ferr %d oerr %d baud %d", + eia[port].r.ref-1, + frame, + overrun, + dcb.BaudRate); + + // add line settings + for(i=0; i < L_MAX; i++) + if(status[i]) + s = seprint(s, str+sizeof(str), " %s", lines[i]); + seprint(s, str+sizeof(str), "\n"); + return readstr(offset, buf, n, str); +} + +// +// write on ctl file. modify the settings for +// the underlying port. +// +static void +wrctl(int port, char *cmd) +{ + DCB dcb; + int nf, n, i; + char *f[16]; + HANDLE comfh = eia[port].comfh; + DWORD flag, opt; + BOOL rslt; + int chg; + + // get the current settings for the port + if(!GetCommState(comfh, &dcb)) + oserror(); + + chg = 0; + nf = tokenize(cmd, f, nelem(f)); + for(i = 0; i < nf; i++){ + if(strcmp(f[i], "break") == 0){ + if(!SetCommBreak(comfh)) + oserror(); + SleepEx((DWORD)300, FALSE); + if(!ClearCommBreak(comfh)) + oserror(); + continue; + } + + n = atoi(f[i]+1); + switch(*f[i]) { + case 'B': + case 'b': // set the baud rate + if(n < 110) + error(Ebadarg); + dcb.BaudRate = n; + chg = 1; + break; + case 'C': + case 'c': + /* dcd */ + break; + case 'D': + case 'd': // set DTR + opt = n ? SETDTR : CLRDTR; + if(!EscapeCommFunction(comfh, opt)) + oserror(); + break; + case 'E': + case 'e': + /* dsr */ + break; + case 'F': + case 'f': // flush any untransmitted data + if(!PurgeComm(comfh, PURGE_TXCLEAR)) + oserror(); + break; + case 'H': + case 'h': + /* hangup */ + /* TO DO: close handle */ + break; + case 'I': + case 'i': + /* fifo: nothing to do */ + break; + case 'K': + case 'k': + /* send a break */ + if(!SetCommBreak(comfh)) + oserror(); + SleepEx((DWORD)300, FALSE); + if(!ClearCommBreak(comfh)) + oserror(); + break; + case 'L': + case 'l': // set bits per byte + flag = stof(size, f[0]+1); + if(flag == BAD) + error(Ebadarg); + dcb.ByteSize = (BYTE)flag; + chg = 1; + break; + case 'M': + case 'm': // set CTS (modem control) + dcb.fOutxCtsFlow = (n!=0); + chg = 1; + break; + case 'N': + case 'n': + /* don't block on output */ + break; + case 'P': + case 'p': // set parity -- even or odd + flag = stof(parity, f[0]+1); + if(flag==BAD) + error(Ebadarg); + dcb.Parity = (BYTE)flag; + chg = 1; + break; + case 'Q': + case 'q': + /* set i/o queue limits */ + break; + case 'R': + case 'r': // set RTS + opt = n ? SETRTS : CLRRTS; + if(!EscapeCommFunction(comfh, opt)) + oserror(); + break; + case 'S': + case 's': // set stop bits -- valid: 1 or 2 (win32 allows 1.5??) + flag = stof(stopbits, f[0]+1); + if(flag==BAD) + error(Ebadarg); + dcb.StopBits = flag; + chg = 1; + break; + case 'T': + case 't': + break; + case 'W': + case 'w': + /* set uart timer */ + break; + case 'X': + case 'x': // xon/xoff + opt = n ? SETXON : SETXOFF; + if(!EscapeCommFunction(comfh, opt)) + oserror(); + break; + default: + /* ignore */ + break; + } + } + + if(!chg) + return; + // make the changes on the underlying port, but flush + // outgoing chars down the port before + osenter(); + rslt = FlushFileBuffers(comfh); + if(rslt) + rslt = SetCommState(comfh, &dcb); + osleave(); + if(!rslt) + oserror(); + eia[port].restore = 1; + eia[port].dcb = dcb; +} diff --git a/emu/Nt/devfs.c b/emu/Nt/devfs.c new file mode 100644 index 00000000..89d52e83 --- /dev/null +++ b/emu/Nt/devfs.c @@ -0,0 +1,2504 @@ +#define UNICODE +#define Unknown win_Unknown +#include <windows.h> +#include <winbase.h> +#undef Unknown +#undef Sleep +#include "dat.h" +#include "fns.h" +#include "error.h" +#include <lm.h> + +/* TODO: try using / in place of \ in path names */ + +enum +{ + MAX_SID = sizeof(SID) + SID_MAX_SUB_AUTHORITIES*sizeof(DWORD), + ACL_ROCK = sizeof(ACL) + 20*(sizeof(ACCESS_ALLOWED_ACE)+MAX_SID), + SD_ROCK = SECURITY_DESCRIPTOR_MIN_LENGTH + MAX_SID + ACL_ROCK, + MAXCOMP = 128, +}; + +typedef struct User User; +typedef struct Gmem Gmem; +typedef struct Stat Stat; +typedef struct Fsinfo Fsinfo; +typedef WIN32_FIND_DATA Fsdir; + +#ifndef INVALID_SET_FILE_POINTER +#define INVALID_SET_FILE_POINTER ((DWORD)-1) +#endif + +struct Fsinfo +{ + int uid; + int gid; + int mode; + int fd; + vlong offset; + QLock oq; + char* spec; + Rune* srv; + Cname* name; /* Windows' idea of the file name */ + ushort usesec; + ushort checksec; + Fsdir* de; /* non-nil for saved entry from last dirread at offset */ +}; +#define FS(c) ((Fsinfo*)(c)->aux) + +/* + * info about a user or group + * there are two ways to specify a user: + * by sid, a unique identifier + * by user and domain names + * this structure is used to convert between the two, + * as well as figure out which groups a users belongs to. + * the user information never gets thrown away, + * but the group information gets refreshed with each setid. + */ +struct User +{ + QLock lk; /* locks the gotgroup and group fields */ + SID *sid; + Rune *name; + Rune *dom; + int type; /* the type of sid, ie SidTypeUser, SidTypeAlias, ... */ + int gotgroup; /* tried to add group */ + Gmem *group; /* global and local groups to which this user or group belongs. */ + User *next; +}; + +struct Gmem +{ + User *user; + Gmem *next; +}; + +/* + * intermediate stat information + */ +struct Stat +{ + User *owner; + User *group; + ulong mode; +}; + +/* + * some "well-known" sids + */ +static SID *creatorowner; +static SID *creatorgroup; +static SID *everyone; +static SID *ntignore; +static SID *ntroot; /* user who is supposed to run emu as a server */ + +/* + * all users we ever see end up in this table + * users are never deleted, but we should update + * group information for users sometime + */ +static struct +{ + QLock lk; + User *u; +}users; + +/* + * conversion from inferno permission modes to nt access masks + * is this good enough? this is what nt sets, except for NOMODE + */ +#define NOMODE (READ_CONTROL|FILE_READ_EA|FILE_READ_ATTRIBUTES) +#define RMODE (READ_CONTROL|SYNCHRONIZE\ + |FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES) +#define XMODE (READ_CONTROL|SYNCHRONIZE\ + |FILE_EXECUTE|FILE_READ_ATTRIBUTES) +#define WMODE (DELETE|READ_CONTROL|SYNCHRONIZE|WRITE_DAC|WRITE_OWNER\ + |FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA\ + |FILE_DELETE_CHILD|FILE_WRITE_ATTRIBUTES) + +static int +modetomask[] = +{ + NOMODE, + XMODE, + WMODE, + WMODE|XMODE, + RMODE, + RMODE|XMODE, + RMODE|WMODE, + RMODE|WMODE|XMODE, +}; + +extern DWORD PlatformId; + char rootdir[MAXROOT] = "\\inferno"; + Rune rootname[] = L"inferno-server"; +static Qid rootqid; +static User *fsnone; +static User *fsuser; +static Rune *ntsrv; +static int usesec; +static int checksec; +static int isserver; +static int file_share_delete; +static uchar isntfrog[256]; + +static void fsremove(Chan*); + + wchar_t *widen(char *s); + char *narrowen(wchar_t *ws); + int widebytes(wchar_t *ws); + +static char Etoolong[] = "file name too long"; + +/* + * these lan manager functions are not supplied + * on windows95, so we have to load the dll by hand + */ +static struct { + NET_API_STATUS (NET_API_FUNCTION *UserGetLocalGroups)( + LPWSTR servername, + LPWSTR username, + DWORD level, + DWORD flags, + LPBYTE *bufptr, + DWORD prefmaxlen, + LPDWORD entriesread, + LPDWORD totalentries); + NET_API_STATUS (NET_API_FUNCTION *UserGetGroups)( + LPWSTR servername, + LPWSTR username, + DWORD level, + LPBYTE *bufptr, + DWORD prefmaxlen, + LPDWORD entriesread, + LPDWORD totalentries); + NET_API_STATUS (NET_API_FUNCTION *GetAnyDCName)( + LPCWSTR ServerName, + LPCWSTR DomainName, + LPBYTE *Buffer); + NET_API_STATUS (NET_API_FUNCTION *ApiBufferFree)(LPVOID Buffer); +} net; + +extern int nth2fd(HANDLE); +extern HANDLE ntfd2h(int); +static int cnisroot(Cname*); +static int fsisroot(Chan*); +static int okelem(char*, int); +static int fsexist(char*, Qid*); +static char* fspath(Cname*, char*, char*, char*); +static Cname* fswalkpath(Cname*, char*, int); +static char* fslastelem(Cname*); +static long fsdirread(Chan*, uchar*, int, vlong); +static ulong fsqidpath(char*); +static int fsomode(int); +static int fsdirset(char*, int, WIN32_FIND_DATA*, char*, Chan*, int isdir); +static int fsdirsize(WIN32_FIND_DATA*, char*, Chan*); +static void fssettime(char*, long, long); +static long unixtime(FILETIME); +static FILETIME wintime(ulong); +static void secinit(void); +static int secstat(Dir*, char*, Rune*); +static int secsize(char*, Rune*); +static void seccheck(char*, ulong, Rune*); +static int sechasperm(char*, ulong, Rune*); +static SECURITY_DESCRIPTOR* secsd(char*, char[SD_ROCK]); +static int secsdhasperm(SECURITY_DESCRIPTOR*, ulong, Rune*); +static int secsdstat(SECURITY_DESCRIPTOR*, Stat*, Rune*); +static SECURITY_DESCRIPTOR* secmksd(char[SD_ROCK], Stat*, ACL*, int); +static SID *dupsid(SID*); +static int ismembersid(Rune*, User*, SID*); +static int ismember(User*, User*); +static User *sidtouser(Rune*, SID*); +static User *domnametouser(Rune*, Rune*, Rune*); +static User *nametouser(Rune*, Rune*); +static User *unametouser(Rune*, char*); +static void addgroups(User*, int); +static User *mkuser(SID*, int, Rune*, Rune*); +static Rune *domsrv(Rune *, Rune[MAX_PATH]); +static Rune *filesrv(char*); +static int fsacls(char*); +static User *secuser(void); + + int runeslen(Rune*); + Rune* runesdup(Rune*); + Rune* utftorunes(Rune*, char*, int); + char* runestoutf(char*, Rune*, int); + int runescmp(Rune*, Rune*); + + +int +winfilematch(char *path, WIN32_FIND_DATA *data) +{ + char *p; + wchar_t *wpath; + int r; + + p = path+strlen(path); + while(p > path && p[-1] != '\\') + --p; + wpath = widen(p); + r = (data->cFileName[0] == '.' && runeslen(data->cFileName) == 1) + || runescmp(data->cFileName, wpath) == 0; + free(wpath); + return r; +} + +int +winfileclash(char *path) +{ + HANDLE h; + WIN32_FIND_DATA data; + wchar_t *wpath; + + wpath = widen(path); + h = FindFirstFile(wpath, &data); + free(wpath); + if (h != INVALID_HANDLE_VALUE) { + FindClose(h); + return !winfilematch(path, &data); + } + return 0; +} + + +/* + * this gets called to set up the environment when we switch users + */ +void +setid(char *name, int owner) +{ + User *u; + + if(owner && !iseve()) + return; + + kstrdup(&up->env->user, name); + + if(!usesec) + return; + + u = unametouser(ntsrv, up->env->user); + if(u == nil) + u = fsnone; + else { + qlock(&u->lk); + addgroups(u, 1); + qunlock(&u->lk); + } + if(u == nil) + panic("setid: user nil\n"); + + up->env->ui = u; +} + +static void +fsfree(Chan *c) +{ + cnameclose(FS(c)->name); + if(FS(c)->de != nil) + free(FS(c)->de); + free(FS(c)); +} + +void +fsinit(void) +{ + int n, isvol; + ulong attr; + char *p, tmp[MAXROOT]; + wchar_t *wp, *wpath, *last; + wchar_t wrootdir[MAXROOT]; + + isntfrog['/'] = 1; + isntfrog['\\'] = 1; + isntfrog[':'] = 1; + isntfrog['*'] = 1; + isntfrog['?'] = 1; + isntfrog['"'] = 1; + isntfrog['<'] = 1; + isntfrog['>'] = 1; + + /* + * vet the root + */ + strcpy(tmp, rootdir); + for(p = tmp; *p; p++) + if(*p == '/') + *p = '\\'; + if(tmp[0] != 0 && tmp[1] == ':') { + if(tmp[2] == 0) { + tmp[2] = '\\'; + tmp[3] = 0; + } + else if(tmp[2] != '\\') { + /* don't allow c:foo - only c:\foo */ + panic("illegal root pathX"); + } + } + wrootdir[0] = '\0'; + wpath = widen(tmp); + for(wp = wpath; *wp; wp++) { + if(*wp < 32 || (*wp < 256 && isntfrog[*wp] && *wp != '\\' && *wp != ':')) + panic("illegal root path"); + } + n = GetFullPathName(wpath, MAXROOT, wrootdir, &last); + free(wpath); + runestoutf(rootdir, wrootdir, MAXROOT); + if(n >= MAXROOT || n == 0) + panic("illegal root path"); + + /* get rid of trailing \ */ + while(rootdir[n-1] == '\\') { + if(n <= 2) { + panic("illegal root path"); + } + rootdir[--n] = '\0'; + } + + isvol = 0; + if(rootdir[1] == ':' && rootdir[2] == '\0') + isvol = 1; + else if(rootdir[0] == '\\' && rootdir[1] == '\\') { + p = strchr(&rootdir[2], '\\'); + if(p == nil) + panic("inferno root can't be a server"); + isvol = strchr(p+1, '\\') == nil; + } + + if(strchr(rootdir, '\\') == nil) + strcat(rootdir, "\\."); + attr = GetFileAttributes(wrootdir); + if(attr == 0xFFFFFFFF) + panic("root path '%s' does not exist", narrowen(wrootdir)); + rootqid.path = fsqidpath(rootdir); + if(attr & FILE_ATTRIBUTE_DIRECTORY) + rootqid.type |= QTDIR; + rootdir[n] = '\0'; + + rootqid.vers = time(0); + + /* + * set up for nt file security checking + */ + ntsrv = filesrv(rootdir); + usesec = PlatformId == VER_PLATFORM_WIN32_NT; /* true for NT and 2000 */ + if(usesec){ + file_share_delete = FILE_SHARE_DELETE; /* sensible handling of shared files by delete and rename */ + secinit(); + if(!fsacls(rootdir)) + usesec = 0; + } + checksec = usesec && isserver; +} + +Chan* +fsattach(char *spec) +{ + Chan *c; + static int devno; + static Lock l; + char *drive = (char *)spec; + + if (!emptystr(drive) && (drive[1] != ':' || drive[2] != '\0')) + error(Ebadspec); + + c = devattach('U', spec); + lock(&l); + c->dev = devno++; + unlock(&l); + c->qid = rootqid; + c->aux = smalloc(sizeof(Fsinfo)); + FS(c)->srv = ntsrv; + if(!emptystr(spec)) { + char *s = smalloc(strlen(spec)+1); + strcpy(s, spec); + FS(c)->spec = s; + FS(c)->srv = filesrv(spec); + if(usesec) + FS(c)->usesec = fsacls(spec); + FS(c)->checksec = FS(c)->usesec && isserver; + c->qid.path = fsqidpath(spec); + c->qid.type = QTDIR; + c->qid.vers = 0; + }else{ + FS(c)->usesec = usesec; + FS(c)->checksec = checksec; + } + FS(c)->name = newcname("/"); + return c; +} + +Walkqid* +fswalk(Chan *c, Chan *nc, char **name, int nname) +{ + int j, alloc; + Walkqid *wq; + char path[MAX_PATH], *p; + Cname *ph; + Cname *current, *next; + + if(nname > 0) + isdir(c); + + alloc = 0; + current = nil; + wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid)); + if(waserror()){ + if(alloc && wq->clone != nil) + cclose(wq->clone); + cnameclose(current); + free(wq); + return nil; + } + if(nc == nil){ + nc = devclone(c); + nc->type = 0; + alloc = 1; + } + wq->clone = nc; + current = FS(c)->name; + if(current != nil) + incref(¤t->r); + for(j = 0; j < nname; j++){ + if(!(nc->qid.type&QTDIR)){ + if(j==0) + error(Enotdir); + break; + } + if(!okelem(name[j], 0)){ + if(j == 0) + error(Efilename); + break; + } + p = fspath(current, name[j], path, FS(c)->spec); + if(FS(c)->checksec) { + *p = '\0'; + if(!sechasperm(path, XMODE, FS(c)->srv)){ + if(j == 0) + error(Eperm); + break; + } + *p = '\\'; + } + + if(strcmp(name[j], "..") == 0) { + if(fsisroot(c)) + nc->qid = rootqid; + else{ + ph = fswalkpath(current, "..", 1); + if(cnisroot(ph)){ + nc->qid = rootqid; + current = ph; + if(current != nil) + incref(¤t->r); + } + else { + fspath(ph, 0, path, FS(c)->spec); + if(!fsexist(path, &nc->qid)){ + cnameclose(ph); + if(j == 0) + error(Enonexist); + break; + } + } + next = fswalkpath(current, name[j], 1); + cnameclose(current); + current = next; + cnameclose(ph); + } + } + else{ + if(!fsexist(path, &nc->qid)){ + if(j == 0) + error(Enonexist); + break; + } + next = fswalkpath(current, name[j], 1); + cnameclose(current); + current = next; + } + wq->qid[wq->nqid++] = nc->qid; + } + poperror(); + if(wq->nqid < nname){ + cnameclose(current); + if(alloc) + cclose(wq->clone); + wq->clone = nil; + }else if(wq->clone){ + nc->aux = smalloc(sizeof(Fsinfo)); + nc->type = c->type; + FS(nc)->spec = FS(c)->spec; + FS(nc)->srv = FS(c)->srv; + FS(nc)->name = current; + FS(nc)->usesec = FS(c)->usesec; + FS(nc)->checksec = FS(c)->checksec; + } + return wq; +} + +Chan* +fsopen(Chan *c, int mode) +{ + HANDLE h; + int m, isdir, aflag, cflag; + char path[MAX_PATH]; + wchar_t *wpath; + + isdir = c->qid.type & QTDIR; + if(isdir && mode != OREAD) + error(Eperm); + fspath(FS(c)->name, 0, path, FS(c)->spec); + + if(FS(c)->checksec) { + switch(mode & (OTRUNC|3)) { + case OREAD: + seccheck(path, RMODE, FS(c)->srv); + break; + case OWRITE: + case OWRITE|OTRUNC: + seccheck(path, WMODE, FS(c)->srv); + break; + case ORDWR: + case ORDWR|OTRUNC: + case OREAD|OTRUNC: + seccheck(path, RMODE|WMODE, FS(c)->srv); + break; + case OEXEC: + seccheck(path, XMODE, FS(c)->srv); + break; + default: + error(Ebadarg); + } + } + + c->mode = openmode(mode); + if(isdir) + FS(c)->fd = nth2fd(INVALID_HANDLE_VALUE); + else { + m = fsomode(mode & 3); + cflag = OPEN_EXISTING; + if(mode & OTRUNC) + cflag = TRUNCATE_EXISTING; + aflag = FILE_FLAG_RANDOM_ACCESS; + if(mode & ORCLOSE) + aflag |= FILE_FLAG_DELETE_ON_CLOSE; + if (winfileclash(path)) + error(Eexist); + wpath = widen(path); + h = CreateFile(wpath, m, FILE_SHARE_READ|FILE_SHARE_WRITE|file_share_delete, 0, cflag, aflag, 0); + free(wpath); + if(h == INVALID_HANDLE_VALUE) + oserror(); + FS(c)->fd = nth2fd(h); + } + + c->offset = 0; + FS(c)->offset = 0; + c->flag |= COPEN; + return c; +} + +void +fscreate(Chan *c, char *name, int mode, ulong perm) +{ + Stat st; + HANDLE h; + int m, aflag; + SECURITY_ATTRIBUTES sa; + SECURITY_DESCRIPTOR *sd; + BY_HANDLE_FILE_INFORMATION hi; + char *p, path[MAX_PATH], sdrock[SD_ROCK]; + wchar_t *wpath; + ACL *acl; + + if(!okelem(name, 1)) + error(Efilename); + + m = fsomode(mode & 3); + p = fspath(FS(c)->name, name, path, FS(c)->spec); + acl = (ACL*)smalloc(ACL_ROCK); + sd = nil; + if(FS(c)->usesec) { + *p = '\0'; + sd = secsd(path, sdrock); + *p = '\\'; + if(sd == nil){ + free(acl); + oserror(); + } + if(FS(c)->checksec && !secsdhasperm(sd, WMODE, FS(c)->srv) + || !secsdstat(sd, &st, FS(c)->srv)){ + if(sd != (void*)sdrock) + free(sd); + free(acl); + error(Eperm); + } + if(sd != (void*)sdrock) + free(sd); + if(perm & DMDIR) + st.mode = (perm & ~0777) | (st.mode & perm & 0777); + else + st.mode = (perm & ~0666) | (st.mode & perm & 0666); + st.owner = up->env->ui; + if(!isserver) + st.owner = fsuser; + sd = secmksd(sdrock, &st, acl, perm & DMDIR); + if(sd == nil){ + free(acl); + oserror(); + } + } + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = sd; + sa.bInheritHandle = 0; + + if(perm & DMDIR) { + if(mode != OREAD) { + free(acl); + error(Eisdir); + } + wpath = widen(path); + if(!CreateDirectory(wpath, &sa) || !fsexist(path, &c->qid)) { + free(wpath); + free(acl); + oserror(); + } + free(wpath); + FS(c)->fd = nth2fd(INVALID_HANDLE_VALUE); + } + else { + aflag = 0; + if(mode & ORCLOSE) + aflag = FILE_FLAG_DELETE_ON_CLOSE; + if (winfileclash(path)) + error(Eexist); + wpath = widen(path); + h = CreateFile(wpath, m, FILE_SHARE_READ|FILE_SHARE_WRITE|file_share_delete, &sa, CREATE_ALWAYS, aflag, 0); + free(wpath); + if(h == INVALID_HANDLE_VALUE) { + free(acl); + oserror(); + } + FS(c)->fd = nth2fd(h); + c->qid.path = fsqidpath(path); + c->qid.type = 0; + c->qid.vers = 0; + if(GetFileInformationByHandle(h, &hi)) + c->qid.vers = unixtime(hi.ftLastWriteTime); + } + + c->mode = openmode(mode); + c->offset = 0; + FS(c)->offset = 0; + c->flag |= COPEN; + FS(c)->name = fswalkpath(FS(c)->name, name, 0); + free(acl); +} + +void +fsclose(Chan *c) +{ + HANDLE h; + + if(c->flag & COPEN){ + h = ntfd2h(FS(c)->fd); + if(h != INVALID_HANDLE_VALUE){ + if(c->qid.type & QTDIR) + FindClose(h); + else + CloseHandle(h); + } + } + if(c->flag & CRCLOSE){ + if(!waserror()){ + fsremove(c); + poperror(); + } + return; + } + fsfree(c); +} + +/* + * 64-bit seeks, using SetFilePointer because SetFilePointerEx + * is not supported by NT + */ +static void +fslseek(HANDLE h, vlong offset) +{ + LONG hi; + + if(offset <= 0x7fffffff){ + if(SetFilePointer(h, (LONG)offset, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) + oserror(); + }else{ + hi = offset>>32; + if(SetFilePointer(h, (LONG)offset, &hi, FILE_BEGIN) == INVALID_SET_FILE_POINTER && + GetLastError() != NO_ERROR) + oserror(); + } +} + +long +fsread(Chan *c, void *va, long n, vlong offset) +{ + DWORD n2; + HANDLE h; + + qlock(&FS(c)->oq); + if(waserror()){ + qunlock(&FS(c)->oq); + nexterror(); + } + if(c->qid.type & QTDIR) { + n2 = fsdirread(c, va, n, offset); + } + else { + h = ntfd2h(FS(c)->fd); + if(FS(c)->offset != offset){ + fslseek(h, offset); + FS(c)->offset = offset; + } + if(!ReadFile(h, va, n, &n2, NULL)) + oserror(); + FS(c)->offset += n2; + } + qunlock(&FS(c)->oq); + poperror(); + return n2; +} + +long +fswrite(Chan *c, void *va, long n, vlong offset) +{ + DWORD n2; + HANDLE h; + + qlock(&FS(c)->oq); + if(waserror()){ + qunlock(&FS(c)->oq); + nexterror(); + } + h = ntfd2h(FS(c)->fd); + if(FS(c)->offset != offset){ + fslseek(h, offset); + FS(c)->offset = offset; + } + if(!WriteFile(h, va, n, &n2, NULL)) + oserror(); + FS(c)->offset += n2; + qunlock(&FS(c)->oq); + poperror(); + return n2; +} + +int +fsstat(Chan *c, uchar *buf, int n) +{ + WIN32_FIND_DATA data; + char path[MAX_PATH]; + wchar_t *wpath; + + /* + * have to fake up a data for volumes like + * c: and \\server\share since you can't FindFirstFile them + */ + if(fsisroot(c)){ + strcpy(path, rootdir); + if(strchr(path, '\\') == nil) + strcat(path, "\\."); + wpath = widen(path); + data.dwFileAttributes = GetFileAttributes(wpath); + free(wpath); + if(data.dwFileAttributes == 0xffffffff) + oserror(); + data.ftCreationTime = + data.ftLastAccessTime = + data.ftLastWriteTime = wintime(time(0)); + data.nFileSizeHigh = 0; + data.nFileSizeLow = 0; + utftorunes(data.cFileName, ".", MAX_PATH); + } else { + HANDLE h = INVALID_HANDLE_VALUE; + + fspath(FS(c)->name, 0, path, FS(c)->spec); + if (c->flag & COPEN) + h = ntfd2h(FS(c)->fd); + + if (h != INVALID_HANDLE_VALUE) { + BY_HANDLE_FILE_INFORMATION fi; + if (c->mode & OWRITE) + FlushFileBuffers(h); + if (!GetFileInformationByHandle(h, &fi)) + oserror(); + data.dwFileAttributes = fi.dwFileAttributes; + data.ftCreationTime = fi.ftCreationTime; + data.ftLastAccessTime = fi.ftLastAccessTime; + data.ftLastWriteTime = fi.ftLastWriteTime;; + data.nFileSizeHigh = fi.nFileSizeHigh; + data.nFileSizeLow = fi.nFileSizeLow; + } else { + wpath = widen(path); + h = FindFirstFile(wpath, &data); + free(wpath); + if(h == INVALID_HANDLE_VALUE) + oserror(); + if (!winfilematch(path, &data)) { + FindClose(h); + error(Enonexist); + } + FindClose(h); + } + utftorunes(data.cFileName, fslastelem(FS(c)->name), MAX_PATH); + } + + return fsdirset(buf, n, &data, path, c, 0); +} + +int +fswstat(Chan *c, uchar *buf, int n) +{ + int wsd; + Dir dir; + Stat st; + Cname * volatile ph; + HANDLE h; + ulong attr; + User *ou, *gu; + WIN32_FIND_DATA data; + SECURITY_DESCRIPTOR *sd; + char *last, sdrock[SD_ROCK], path[MAX_PATH], newpath[MAX_PATH], strs[4*256]; + wchar_t wspath[MAX_PATH], wsnewpath[MAX_PATH]; + wchar_t *wpath; + int nmatch; + + n = convM2D(buf, n, &dir, strs); + if(n == 0) + error(Eshortstat); + + last = fspath(FS(c)->name, 0, path, FS(c)->spec); + utftorunes(wspath, path, MAX_PATH); + + if(fsisroot(c)){ + if(dir.atime != ~0) + data.ftLastAccessTime = wintime(dir.atime); + if(dir.mtime != ~0) + data.ftLastWriteTime = wintime(dir.mtime); + utftorunes(data.cFileName, ".", MAX_PATH); + }else{ + h = FindFirstFile(wspath, &data); + if(h == INVALID_HANDLE_VALUE) + oserror(); + if (!winfilematch(path, &data)) { + FindClose(h); + error(Enonexist); + } + FindClose(h); + } + + wsd = 0; + ou = nil; + gu = nil; + if(FS(c)->usesec) { + if(FS(c)->checksec && up->env->ui == fsnone) + error(Eperm); + + /* + * find new owner and group + */ + if(!emptystr(dir.uid)){ + ou = unametouser(FS(c)->srv, dir.uid); + if(ou == nil) + oserror(); + } + if(!emptystr(dir.gid)){ + gu = unametouser(FS(c)->srv, dir.gid); + if(gu == nil){ + if(strcmp(dir.gid, "unknown") != 0 + && strcmp(dir.gid, "deleted") != 0) + oserror(); + gu = ou; + } + } + + /* + * find old stat info + */ + sd = secsd(path, sdrock); + if(sd == nil || !secsdstat(sd, &st, FS(c)->srv)){ + if(sd != nil && sd != (void*)sdrock) + free(sd); + oserror(); + } + if(sd != (void*)sdrock) + free(sd); + + /* + * permission rules: + * if none, can't do anything + * chown => no way + * chgrp => current owner or group, and in new group + * mode/time => owner or in either group + * rename => write in parent + */ + if(ou == nil) + ou = st.owner; + if(FS(c)->checksec && st.owner != ou) + error(Eperm); + + if(gu == nil) + gu = st.group; + if(st.group != gu){ + if(FS(c)->checksec + &&(!ismember(up->env->ui, ou) && !ismember(up->env->ui, gu) + || !ismember(up->env->ui, st.group))) + error(Eperm); + wsd = 1; + } + + if(dir.atime != ~0 && unixtime(data.ftLastAccessTime) != dir.atime + || dir.mtime != ~0 && unixtime(data.ftLastWriteTime) != dir.mtime + || dir.mode != ~0 && st.mode != dir.mode){ + if(FS(c)->checksec + && !ismember(up->env->ui, ou) + && !ismember(up->env->ui, gu) + && !ismember(up->env->ui, st.group)) + error(Eperm); + if(dir.mode != ~0 && st.mode != dir.mode) + wsd = 1; + } + } + wpath = widen(dir.name); + nmatch = runescmp(wpath, data.cFileName); + free(wpath); + if(!emptystr(dir.name) && nmatch != 0){ + if(!okelem(dir.name, 1)) + error(Efilename); + ph = fswalkpath(FS(c)->name, "..", 1); + if(waserror()){ + cnameclose(ph); + nexterror(); + } + ph = fswalkpath(ph, dir.name, 0); + fspath(ph, 0, newpath, FS(c)->spec); + utftorunes(wsnewpath, newpath, MAX_PATH); + if(GetFileAttributes(wpath) != 0xffffffff && !winfileclash(newpath)) + error("file already exists"); + if(fsisroot(c)) + error(Eperm); + if(FS(c)->checksec){ + *last = '\0'; + seccheck(path, WMODE, FS(c)->srv); + *last = '\\'; + } + poperror(); + cnameclose(ph); + } + + if(dir.atime != ~0 && unixtime(data.ftLastAccessTime) != dir.atime + || dir.mtime != ~0 && unixtime(data.ftLastWriteTime) != dir.mtime) + fssettime(path, dir.atime, dir.mtime); + + attr = data.dwFileAttributes; + if(dir.mode & 0222) + attr &= ~FILE_ATTRIBUTE_READONLY; + else + attr |= FILE_ATTRIBUTE_READONLY; + if(!fsisroot(c) + && attr != data.dwFileAttributes + && (attr & FILE_ATTRIBUTE_READONLY)) + SetFileAttributes(wspath, attr); + if(FS(c)->usesec && wsd){ + ACL *acl = (ACL *) smalloc(ACL_ROCK); + st.owner = ou; + st.group = gu; + if(dir.mode != ~0) + st.mode = dir.mode; + sd = secmksd(sdrock, &st, acl, data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); + if(sd == nil || !SetFileSecurity(wspath, DACL_SECURITY_INFORMATION, sd)){ + free(acl); + oserror(); + } + free(acl); + } + + if(!fsisroot(c) + && attr != data.dwFileAttributes + && !(attr & FILE_ATTRIBUTE_READONLY)) + SetFileAttributes(wspath, attr); + + /* do last so path is valid throughout */ + wpath = widen(dir.name); + nmatch = runescmp(wpath, data.cFileName); + free(wpath); + if(!emptystr(dir.name) && nmatch != 0) { + ph = fswalkpath(FS(c)->name, "..", 1); + if(waserror()){ + cnameclose(ph); + nexterror(); + } + ph = fswalkpath(ph, dir.name, 0); + fspath(ph, 0, newpath, FS(c)->spec); + utftorunes(wsnewpath, newpath, MAX_PATH); + /* + * can't rename if it is open: if this process has it open, close it temporarily. + */ + if(!file_share_delete && c->flag & COPEN){ + h = ntfd2h(FS(c)->fd); + if(h != INVALID_HANDLE_VALUE) + CloseHandle(h); /* woe betide it if ORCLOSE */ + FS(c)->fd = nth2fd(INVALID_HANDLE_VALUE); + } + if(!MoveFile(wspath, wsnewpath)) { + oserror(); + } else if(!file_share_delete && c->flag & COPEN) { + int aflag; + SECURITY_ATTRIBUTES sa; + + /* The move succeeded, so open new file to maintain handle */ + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = sd; + sa.bInheritHandle = 0; + if(c->flag & CRCLOSE) + aflag = FILE_FLAG_DELETE_ON_CLOSE; + h = CreateFile(wsnewpath, fsomode(c->mode & 0x3), FILE_SHARE_READ|FILE_SHARE_WRITE|file_share_delete, &sa, OPEN_EXISTING, aflag, 0); + if(h == INVALID_HANDLE_VALUE) + oserror(); + FS(c)->fd = nth2fd(h); + } + cnameclose(FS(c)->name); + poperror(); + FS(c)->name = ph; + } + return n; +} + +static void +fsremove(Chan *c) +{ + int n; + char *p, path[MAX_PATH]; + wchar_t wspath[MAX_PATH]; + + if(waserror()){ + fsfree(c); + nexterror(); + } + if(fsisroot(c)) + error(Eperm); + p = fspath(FS(c)->name, 0, path, FS(c)->spec); + utftorunes(wspath, path, MAX_PATH); + if(FS(c)->checksec){ + *p = '\0'; + seccheck(path, WMODE, FS(c)->srv); + *p = '\\'; + } + if(c->qid.type & QTDIR) + n = RemoveDirectory(wspath); + else + n = DeleteFile(wspath); + if (!n) { + ulong attr, mode; + SECURITY_DESCRIPTOR *sd = nil; + char sdrock[SD_ROCK]; + Stat st; + int secok; + attr = GetFileAttributes(wspath); + if(attr != 0xFFFFFFFF) { + if (FS(c)->usesec) { + sd = secsd(path, sdrock); + secok = (sd != nil) && secsdstat(sd, &st, FS(c)->srv); + if (secok) { + ACL *acl = (ACL *) smalloc(ACL_ROCK); + mode = st.mode; + st.mode |= 0660; + sd = secmksd(sdrock, &st, acl, attr & FILE_ATTRIBUTE_DIRECTORY); + if(sd != nil) { + SetFileSecurity(wspath, DACL_SECURITY_INFORMATION, sd); + } + free(acl); + if(sd != nil && sd != (void*)sdrock) + free(sd); + sd = nil; + } + } + SetFileAttributes(wspath, FILE_ATTRIBUTE_NORMAL); + if(c->qid.type & QTDIR) + n = RemoveDirectory(wspath); + else + n = DeleteFile(wspath); + if (!n) { + if (FS(c)->usesec && secok) { + ACL *acl = (ACL *) smalloc(ACL_ROCK); + st.mode = mode; + sd = secmksd(sdrock, &st, acl, attr & FILE_ATTRIBUTE_DIRECTORY); + if(sd != nil) { + SetFileSecurity(wspath, DACL_SECURITY_INFORMATION, sd); + } + free(acl); + } + SetFileAttributes(wspath, attr); + if(sd != nil && sd != (void*)sdrock) + free(sd); + } + } + } + if(!n) + oserror(); + poperror(); + fsfree(c); +} + +/* + * check elem for illegal characters /\:*?"<> + * ... and relatives are also disallowed, + * since they specify grandparents, which we + * are not prepared to handle + */ +static int +okelem(char *elem, int nodots) +{ + int c, dots; + + dots = 0; + while((c = *(uchar*)elem) != 0){ + if(isntfrog[c]) + return 0; + if(c == '.' && dots >= 0) + dots++; + else + dots = -1; + elem++; + } + if(nodots) + return dots <= 0; + return dots <= 2; +} + +static int +cnisroot(Cname *c) +{ + return strcmp(c->s, "/") == 0; +} + +static int +fsisroot(Chan *c) +{ + return strcmp(FS(c)->name->s, "/") == 0; +} + +static char* +fspath(Cname *c, char *ext, char *path, char *spec) +{ + char *p, *last, *rootd; + int extlen = 0; + + rootd = spec != nil ? spec : rootdir; + if(ext) + extlen = strlen(ext) + 1; + if(strlen(rootd) + extlen >= MAX_PATH) + error(Etoolong); + strcpy(path, rootd); + if(cnisroot(c)){ + if(ext) { + strcat(path, "\\"); + strcat(path, ext); + } + }else{ + if(*c->s != '/') { + if(strlen(path) + 1 >= MAX_PATH) + error(Etoolong); + strcat(path, "\\"); + } + if(strlen(path) + strlen(c->s) + extlen >= MAX_PATH) + error(Etoolong); + strcat(path, c->s); + if(ext){ + strcat(path, "\\"); + strcat(path, ext); + } + } + last = path; + for(p = path; *p != '\0'; p++){ + if(*p == '/' || *p == '\\'){ + *p = '\\'; + last = p; + } + } + return last; +} + +extern void cleancname(Cname*); + +static Cname * +fswalkpath(Cname *c, char *name, int dup) +{ + if(dup) + c = newcname(c->s); + c = addelem(c, name); + if(isdotdot(name)) + cleancname(c); + return c; +} + +static char * +fslastelem(Cname *c) +{ + char *p; + + p = c->s + c->len; + while(p > c->s && p[-1] != '/') + p--; + return p; +} + +static int +fsdirbadentry(WIN32_FIND_DATA *data) +{ + wchar_t *s; + + s = data->cFileName; + if(s[0] == 0) + return 1; + if(s[0] == '.' && (s[1] == 0 || s[1] == '.' && s[2] == 0)) + return 1; + + return 0; +} + +static Fsdir* +fsdirent(Chan *c, char *path, Fsdir *data) +{ + wchar_t *wpath; + HANDLE h; + + h = ntfd2h(FS(c)->fd); + if(data == nil) + data = smalloc(sizeof(*data)); + if(FS(c)->offset == 0){ + if(h != INVALID_HANDLE_VALUE) + FindClose(h); + wpath = widen(path); + h = FindFirstFile(wpath, data); + free(wpath); + FS(c)->fd = nth2fd(h); + if(h == INVALID_HANDLE_VALUE){ + free(data); + return nil; + } + if(!fsdirbadentry(data)) + return data; + } + do{ + if(!FindNextFile(h, data)){ + free(data); + return nil; + } + }while(fsdirbadentry(data)); + return data; +} + +static long +fsdirread(Chan *c, uchar *va, int count, vlong offset) +{ + int i, r; + char path[MAX_PATH], *p; + Fsdir *de; + vlong o; + + if(count == 0 || offset < 0) + return 0; + p = fspath(FS(c)->name, "*.*", path, FS(c)->spec); + p++; + de = nil; + if(FS(c)->offset != offset){ + de = FS(c)->de; + if(FS(c)->de != nil){ + free(FS(c)->de); + FS(c)->de = nil; + } + FS(c)->offset = 0; + for(o = 0; o < offset;){ + de = fsdirent(c, path, de); + if(de == nil){ + FS(c)->offset = o; + return 0; + } + runestoutf(p, de->cFileName, &path[MAX_PATH]-p); + path[MAX_PATH-1] = '\0'; + o += fsdirsize(de, path, c); + } + FS(c)->offset = offset; + } + for(i = 0; i < count;){ + if(FS(c)->de != nil){ /* left over from previous read at offset */ + de = FS(c)->de; + FS(c)->de = nil; + }else{ + de = fsdirent(c, path, de); + if(de == nil) + break; + } + runestoutf(p, de->cFileName, &path[MAX_PATH]-p); + path[MAX_PATH-1] = '\0'; + r = fsdirset(va+i, count-i, de, path, c, 1); + if(r <= 0){ + /* won't fit; save for next read at this offset */ + FS(c)->de = de; + break; + } + i += r; + FS(c)->offset += r; + } + return i; +} + +static ulong +fsqidpath(char *p) +{ + ulong h; + int c; + + h = 0; + while(*p != '\0'){ + /* force case insensitive file names */ + c = *p++; + if(c >= 'A' && c <= 'Z') + c += 'a'-'A'; + h = h * 19 ^ c; + } + return h; +} + +/* TO DO: substitute fixed, made-up (unlikely) names for these */ +static char* devf[] = { "aux", "com1", "com2", "lpt1", "nul", "prn", nil }; + +static int +devfile(char *p) +{ + char *s, *t, *u, **ss; + + if((u = strrchr(p, '\\')) != nil) + u++; + else if((u = strrchr(p, '/')) != nil) + u++; + else + u = p; + for(ss = devf; *ss != nil; ss++){ + for(s = *ss, t = u; *s != '\0' && *t != '\0' && *t != '.'; s++, t++) + if(*s != *t && *s != *t+'a'-'A') + break; + if(*s == '\0' && (*t == '\0' || *t == '.')) + return 1; + } + return 0; +} + +/* + * there are other ways to figure out + * the attributes and times for a file. + * perhaps they are faster + */ +static int +fsexist(char *p, Qid *q) +{ + HANDLE h; + WIN32_FIND_DATA data; + wchar_t *wpath; + + if(devfile(p)) + return 0; + wpath = widen(p); + h = FindFirstFile(wpath, &data); + free(wpath); + if(h == INVALID_HANDLE_VALUE) + return 0; + if (!winfilematch(p, &data)) { + FindClose(h); + return 0; + } + FindClose(h); + + q->path = fsqidpath(p); + q->type = 0; + + if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + q->type |= QTDIR; + + q->vers = unixtime(data.ftLastWriteTime); + + return 1; +} + +static int +fsdirset(char *edir, int n, WIN32_FIND_DATA *data, char *path, Chan *c, int isdir) +{ + Dir dir; + static char neveryone[] = "Everyone"; + + dir.name = narrowen(data->cFileName); + dir.muid = nil; + dir.qid.path = fsqidpath(path); + dir.qid.vers = 0; + dir.qid.type = 0; + dir.mode = 0; + dir.atime = unixtime(data->ftLastAccessTime); + dir.mtime = unixtime(data->ftLastWriteTime); + dir.qid.vers = dir.mtime; + dir.length = ((uvlong)data->nFileSizeHigh<<32) | ((uvlong)data->nFileSizeLow & ~((uvlong)0xFFFFFFFF<<32)); + dir.type = 'U'; + dir.dev = c->dev; + + if(!FS(c)->usesec){ + /* no NT security so make something up */ + dir.uid = neveryone; + dir.gid = neveryone; + dir.mode = 0777; + }else if(!secstat(&dir, path, FS(c)->srv)) + oserror(); + + if(data->dwFileAttributes & FILE_ATTRIBUTE_READONLY) + dir.mode &= ~0222; + if(data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){ + dir.qid.type |= QTDIR; + dir.mode |= DMDIR; + dir.length = 0; + } + + if(isdir && sizeD2M(&dir) > n) + n = -1; + else + n = convD2M(&dir, edir, n); + if(dir.uid != neveryone) + free(dir.uid); + if(dir.gid != neveryone) + free(dir.gid); + free(dir.name); + return n; +} + +static int +fsdirsize(WIN32_FIND_DATA *data, char *path, Chan *c) +{ + int i, n; + + n = widebytes(data->cFileName); + if(!FS(c)->usesec) + n += 8+8; + else{ + i = secsize(path, FS(c)->srv); + if(i < 0) + oserror(); + n += i; + } + return STATFIXLEN+n; +} + +static void +fssettime(char *path, long at, long mt) +{ + HANDLE h; + FILETIME atime, mtime; + wchar_t *wpath; + + wpath = widen(path); + h = CreateFile(wpath, GENERIC_WRITE, + 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + free(wpath); + if(h == INVALID_HANDLE_VALUE) + return; + mtime = wintime(mt); + atime = wintime(at); + if(!SetFileTime(h, 0, &atime, &mtime)){ + CloseHandle(h); + oserror(); + } + CloseHandle(h); +} + +static int +fsomode(int m) +{ + switch(m & 0x3) { + case OREAD: + case OEXEC: + return GENERIC_READ; + case OWRITE: + return GENERIC_WRITE; + case ORDWR: + return GENERIC_READ|GENERIC_WRITE; + } + error(Ebadarg); + return 0; +} + +static long +unixtime(FILETIME ft) +{ + vlong t; + + t = (vlong)ft.dwLowDateTime + ((vlong)ft.dwHighDateTime<<32); + t -= (vlong)10000000*134774*24*60*60; + + return (long)(t/10000000); +} + +static FILETIME +wintime(ulong t) +{ + FILETIME ft; + vlong vt; + + vt = (vlong)t*10000000+(vlong)10000000*134774*24*60*60; + + ft.dwLowDateTime = vt; + ft.dwHighDateTime = vt>>32; + + return ft; +} + +/* + * the sec routines manage file permissions for nt. + * nt files have an associated security descriptor, + * which has in it an owner, a group, + * and a discretionary acces control list, or acl, + * which specifies the permissions for the file. + * + * the strategy for mapping between inferno owner, + * group, other, and mode and nt file security is: + * + * inferno owner == nt file owner + * inferno other == nt Everyone + * inferno group == first non-owner, + * non-Everyone user given in the acl, + * or the owner if there is no such user. + * we examine the entire acl when check for permissions, + * but only report a subset. + * + * when we write an acl, we also give all permissions to + * the special user rootname, who is supposed to run emu in server mode. + */ +static void +secinit(void) +{ + HMODULE lib; + HANDLE token; + TOKEN_PRIVILEGES *priv; + char privrock[sizeof(TOKEN_PRIVILEGES) + 1*sizeof(LUID_AND_ATTRIBUTES)]; + SID_IDENTIFIER_AUTHORITY id = SECURITY_CREATOR_SID_AUTHORITY; + SID_IDENTIFIER_AUTHORITY wid = SECURITY_WORLD_SID_AUTHORITY; + SID_IDENTIFIER_AUTHORITY ntid = SECURITY_NT_AUTHORITY; + + lib = LoadLibraryA("netapi32"); + if(lib == 0) { + usesec = 0; + return; + } + + net.UserGetGroups = (void*)GetProcAddress(lib, "NetUserGetGroups"); + if(net.UserGetGroups == 0) + panic("bad netapi32 library"); + net.UserGetLocalGroups = (void*)GetProcAddress(lib, "NetUserGetLocalGroups"); + if(net.UserGetLocalGroups == 0) + panic("bad netapi32 library"); + net.GetAnyDCName = (void*)GetProcAddress(lib, "NetGetAnyDCName"); + if(net.GetAnyDCName == 0) + panic("bad netapi32 library"); + net.ApiBufferFree = (void*)GetProcAddress(lib, "NetApiBufferFree"); + if(net.ApiBufferFree == 0) + panic("bad netapi32 library"); + + if(!AllocateAndInitializeSid(&id, 1, + SECURITY_CREATOR_OWNER_RID, + 1, 2, 3, 4, 5, 6, 7, &creatorowner) + || !AllocateAndInitializeSid(&id, 1, + SECURITY_CREATOR_GROUP_RID, + 1, 2, 3, 4, 5, 6, 7, &creatorgroup) + || !AllocateAndInitializeSid(&wid, 1, + SECURITY_WORLD_RID, + 1, 2, 3, 4, 5, 6, 7, &everyone) + || !AllocateAndInitializeSid(&ntid, 1, + 0, + 1, 2, 3, 4, 5, 6, 7, &ntignore)) + panic("can't initialize well-known sids"); + + fsnone = sidtouser(ntsrv, everyone); + if(fsnone == nil) + panic("can't make none user"); + + /* + * see if we are running as the emu server user + * if so, set up SE_RESTORE_NAME privilege, + * which allows setting the owner field in a security descriptor. + * other interesting privileges are SE_TAKE_OWNERSHIP_NAME, + * which enables changing the ownership of a file to yourself + * regardless of the permissions on the file, SE_BACKUP_NAME, + * which enables reading any files regardless of permission, + * and SE_CHANGE_NOTIFY_NAME, which enables walking through + * directories without X permission. + * SE_RESTORE_NAME and SE_BACKUP_NAME together allow writing + * and reading any file data, regardless of permission, + * if the file is opened with FILE_BACKUP_SEMANTICS. + */ + isserver = 0; + fsuser = secuser(); + if(fsuser == nil) + fsuser = fsnone; + else if(runescmp(fsuser->name, rootname) == 0 + && OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token)){ + priv = (TOKEN_PRIVILEGES*)privrock; + priv->PrivilegeCount = 1; + priv->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + if(LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &priv->Privileges[0].Luid) + && AdjustTokenPrivileges(token, 0, priv, 0, NULL, NULL)) + isserver = 1; + CloseHandle(token); + } +} + +/* + * get the User for the executing process + */ +static User* +secuser(void) +{ + DWORD need; + HANDLE token; + TOKEN_USER *tu; + char turock[sizeof(TOKEN_USER) + MAX_SID]; + + if(!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) + return nil; + + tu = (TOKEN_USER*)turock; + if(!GetTokenInformation(token, TokenUser, tu, sizeof(turock), &need)){ + CloseHandle(token); + return nil; + } + CloseHandle(token); + return sidtouser(nil, tu->User.Sid); +} + +static int +secstat(Dir *dir, char *file, Rune *srv) +{ + int ok, n; + Stat st; + char sdrock[SD_ROCK]; + SECURITY_DESCRIPTOR *sd; + + sd = secsd(file, sdrock); + if(sd == nil){ + int e = GetLastError(); + if(e == ERROR_ACCESS_DENIED || e == ERROR_SHARING_VIOLATION){ + dir->uid = strdup("unknown"); + dir->gid = strdup("unknown"); + if(dir->uid == nil || dir->gid == nil){ + free(dir->uid); + error(Enomem); /* will change to use kstrdup */ + } + dir->mode = 0; + return 1; + } + return 0; + } + ok = secsdstat(sd, &st, srv); + if(sd != (void*)sdrock) + free(sd); + if(ok){ + dir->mode = st.mode; + n = runenlen(st.owner->name, runeslen(st.owner->name)); + dir->uid = smalloc(n+1); + runestoutf(dir->uid, st.owner->name, n+1); + n = runenlen(st.group->name, runeslen(st.group->name)); + dir->gid = smalloc(n+1); + runestoutf(dir->gid, st.group->name, n+1); + } + return ok; +} + +static int +secsize(char *file, Rune *srv) +{ + int ok; + Stat st; + char sdrock[SD_ROCK]; + SECURITY_DESCRIPTOR *sd; + + sd = secsd(file, sdrock); + if(sd == nil){ + int e = GetLastError(); + if(e == ERROR_ACCESS_DENIED || e == ERROR_SHARING_VIOLATION) + return 7+7; + return -1; + } + ok = secsdstat(sd, &st, srv); + if(sd != (void*)sdrock) + free(sd); + if(ok) + return runenlen(st.owner->name, runeslen(st.owner->name))+runenlen(st.group->name, runeslen(st.group->name)); + return -1; +} + +/* + * verify that u had access to file + */ +static void +seccheck(char *file, ulong access, Rune *srv) +{ + if(!sechasperm(file, access, srv)) + error(Eperm); +} + +static int +sechasperm(char *file, ulong access, Rune *srv) +{ + int ok; + char sdrock[SD_ROCK]; + SECURITY_DESCRIPTOR *sd; + + /* + * only really needs dacl info + */ + sd = secsd(file, sdrock); + if(sd == nil) + return 0; + ok = secsdhasperm(sd, access, srv); + if(sd != (void*)sdrock) + free(sd); + return ok; +} + +static SECURITY_DESCRIPTOR* +secsd(char *file, char sdrock[SD_ROCK]) +{ + DWORD need; + SECURITY_DESCRIPTOR *sd; + char *path, pathrock[6]; + wchar_t *wpath; + + path = file; + if(path[0] != '\0' && path[1] == ':' && path[2] == '\0'){ + path = pathrock; + strcpy(path, "?:\\."); + path[0] = file[0]; + } + sd = (SECURITY_DESCRIPTOR*)sdrock; + need = 0; + wpath = widen(path); + if(GetFileSecurity(wpath, OWNER_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION, sd, SD_ROCK, &need)) { + free(wpath); + return sd; + } + if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + free(wpath); + return nil; + } + sd = malloc(need); + if(sd == nil) { + free(wpath); + error(Enomem); + } + if(GetFileSecurity(wpath, OWNER_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION, sd, need, &need)) { + free(wpath); + return sd; + } + free(wpath); + free(sd); + return nil; +} + +static int +secsdstat(SECURITY_DESCRIPTOR *sd, Stat *st, Rune *srv) +{ + ACL *acl; + BOOL hasacl, b; + ACE_HEADER *aceh; + User *owner, *group; + SID *sid, *osid, *gsid; + ACCESS_ALLOWED_ACE *ace; + int i, allow, deny, *p, m; + ACL_SIZE_INFORMATION size; + + st->mode = 0; + + osid = nil; + gsid = nil; + if(!GetSecurityDescriptorOwner(sd, &osid, &b) + || !GetSecurityDescriptorDacl(sd, &hasacl, &acl, &b)) + return 0; + + if(acl == 0) + size.AceCount = 0; + else if(!GetAclInformation(acl, &size, sizeof(size), AclSizeInformation)) + return 0; + + /* + * first pass through acl finds group + */ + for(i = 0; i < size.AceCount; i++){ + if(!GetAce(acl, i, &aceh)) + continue; + if(aceh->AceFlags & INHERIT_ONLY_ACE) + continue; + + if(aceh->AceType != ACCESS_ALLOWED_ACE_TYPE + && aceh->AceType != ACCESS_DENIED_ACE_TYPE) + continue; + + ace = (ACCESS_ALLOWED_ACE*)aceh; + sid = (SID*)&ace->SidStart; + if(EqualSid(sid, creatorowner) || EqualSid(sid, creatorgroup)) + continue; + + if(EqualSid(sid, everyone)) + ; + else if(EqualSid(sid, osid)) + ; + else if(EqualPrefixSid(sid, ntignore)) + continue; /* boring nt accounts */ + else{ + gsid = sid; + break; + } + } + if(gsid == nil) + gsid = osid; + + owner = sidtouser(srv, osid); + if(owner == nil) + return 0; + group = sidtouser(srv, gsid); + if(group == nil) + return 0; + + /* no acl means full access */ + allow = 0; + if(acl == 0) + allow = 0777; + deny = 0; + for(i = 0; i < size.AceCount; i++){ + if(!GetAce(acl, i, &aceh)) + continue; + if(aceh->AceFlags & INHERIT_ONLY_ACE) + continue; + + if(aceh->AceType == ACCESS_ALLOWED_ACE_TYPE) + p = &allow; + else if(aceh->AceType == ACCESS_DENIED_ACE_TYPE) + p = &deny; + else + continue; + + ace = (ACCESS_ALLOWED_ACE*)aceh; + sid = (SID*)&ace->SidStart; + if(EqualSid(sid, creatorowner) || EqualSid(sid, creatorgroup)) + continue; + + m = 0; + if(ace->Mask & FILE_EXECUTE) + m |= 1; + if(ace->Mask & FILE_WRITE_DATA) + m |= 2; + if(ace->Mask & FILE_READ_DATA) + m |= 4; + + if(ismembersid(srv, owner, sid)) + *p |= (m << 6) & ~(allow|deny) & 0700; + if(ismembersid(srv, group, sid)) + *p |= (m << 3) & ~(allow|deny) & 0070; + if(EqualSid(everyone, sid)) + *p |= m & ~(allow|deny) & 0007; + } + + st->mode = allow & ~deny; + st->owner = owner; + st->group = group; + return 1; +} + +static int +secsdhasperm(SECURITY_DESCRIPTOR *sd, ulong access, Rune *srv) +{ + User *u; + ACL *acl; + BOOL hasacl, b; + ACE_HEADER *aceh; + SID *sid, *osid, *gsid; + int i, allow, deny, *p, m; + ACCESS_ALLOWED_ACE *ace; + ACL_SIZE_INFORMATION size; + + u = up->env->ui; + allow = 0; + deny = 0; + osid = nil; + gsid = nil; + if(!GetSecurityDescriptorDacl(sd, &hasacl, &acl, &b)) + return 0; + + /* no acl means full access */ + if(acl == 0) + return 1; + if(!GetAclInformation(acl, &size, sizeof(size), AclSizeInformation)) + return 0; + for(i = 0; i < size.AceCount; i++){ + if(!GetAce(acl, i, &aceh)) + continue; + if(aceh->AceFlags & INHERIT_ONLY_ACE) + continue; + + if(aceh->AceType == ACCESS_ALLOWED_ACE_TYPE) + p = &allow; + else if(aceh->AceType == ACCESS_DENIED_ACE_TYPE) + p = &deny; + else + continue; + + ace = (ACCESS_ALLOWED_ACE*)aceh; + sid = (SID*)&ace->SidStart; + if(EqualSid(sid, creatorowner) || EqualSid(sid, creatorgroup)) + continue; + + m = ace->Mask; + + if(ismembersid(srv, u, sid)) + *p |= m & ~(allow|deny); + } + + allow &= ~deny; + + return (allow & access) == access; +} + +static SECURITY_DESCRIPTOR* +secmksd(char *sdrock, Stat *st, ACL *dacl, int isdir) +{ + int m; + + ulong mode; + ACE_HEADER *aceh; + SECURITY_DESCRIPTOR *sd; + + sd = (SECURITY_DESCRIPTOR*)sdrock; + if(!InitializeAcl(dacl, ACL_ROCK, ACL_REVISION)) + return nil; + + mode = st->mode; + if(st->owner == st->group){ + mode |= (mode >> 3) & 0070; + mode |= (mode << 3) & 0700; + } + + + m = modetomask[(mode>>6) & 7]; + if(!AddAccessAllowedAce(dacl, ACL_REVISION, m, st->owner->sid)) + return nil; + + if(isdir && !AddAccessAllowedAce(dacl, ACL_REVISION, m, creatorowner)) + return nil; + + m = modetomask[(mode>>3) & 7]; + if(!AddAccessAllowedAce(dacl, ACL_REVISION, m, st->group->sid)) + return nil; + + m = modetomask[(mode>>0) & 7]; + if(!AddAccessAllowedAce(dacl, ACL_REVISION, m, everyone)) + return nil; + + if(isdir){ + /* hack to add inherit flags */ + if(!GetAce(dacl, 1, &aceh)) + return nil; + aceh->AceFlags |= OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE; + if(!GetAce(dacl, 2, &aceh)) + return nil; + aceh->AceFlags |= OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE; + if(!GetAce(dacl, 3, &aceh)) + return nil; + aceh->AceFlags |= OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE; + } + + /* + * allow server user to access any file + */ + if(isserver){ + if(!AddAccessAllowedAce(dacl, ACL_REVISION, RMODE|WMODE|XMODE, fsuser->sid)) + return nil; + if(isdir){ + if(!GetAce(dacl, 4, &aceh)) + return nil; + aceh->AceFlags |= OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE; + } + } + + if(!InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION)) + return nil; + if(!SetSecurityDescriptorDacl(sd, 1, dacl, 0)) + return nil; +// if(isserver && !SetSecurityDescriptorOwner(sd, st->owner->sid, 0)) +// return nil; + return sd; +} + +/* + * the user manipulation routines + * just make it easier to deal with user identities + */ +static User* +sidtouser(Rune *srv, SID *s) +{ + SID_NAME_USE type; + Rune aname[100], dname[100]; + DWORD naname, ndname; + User *u; + + qlock(&users.lk); + for(u = users.u; u != 0; u = u->next) + if(EqualSid(s, u->sid)) + break; + qunlock(&users.lk); + + if(u != 0) + return u; + + naname = sizeof(aname); + ndname = sizeof(dname); + + if(!LookupAccountSidW(srv, s, aname, &naname, dname, &ndname, &type)) + return mkuser(s, SidTypeUnknown, L"unknown", L"unknown"); + return mkuser(s, type, aname, dname); +} + +static User* +domnametouser(Rune *srv, Rune *name, Rune *dom) +{ + User *u; + + qlock(&users.lk); + for(u = users.u; u != 0; u = u->next) + if(runescmp(name, u->name) == 0 && runescmp(dom, u->dom) == 0) + break; + qunlock(&users.lk); + if(u == 0) + u = nametouser(srv, name); + return u; +} + +static User* +nametouser(Rune *srv, Rune *name) +{ + char sidrock[MAX_SID]; + SID *sid; + SID_NAME_USE type; + Rune dom[MAX_PATH]; + DWORD nsid, ndom; + + sid = (SID*)sidrock; + nsid = sizeof(sidrock); + ndom = sizeof(dom); + if(!LookupAccountNameW(srv, name, sid, &nsid, dom, &ndom, &type)) + return nil; + + return mkuser(sid, type, name, dom); +} + +/* + * this mapping could be cached + */ +static User* +unametouser(Rune *srv, char *name) +{ + Rune rname[MAX_PATH]; + + utftorunes(rname, name, MAX_PATH); + return nametouser(srv, rname); +} + +/* + * make a user structure and add it to the global cache. + */ +static User* +mkuser(SID *sid, int type, Rune *name, Rune *dom) +{ + User *u; + + qlock(&users.lk); + for(u = users.u; u != 0; u = u->next){ + if(EqualSid(sid, u->sid)){ + qunlock(&users.lk); + return u; + } + } + + switch(type) { + default: + break; + case SidTypeDeletedAccount: + name = L"deleted"; + break; + case SidTypeInvalid: + name = L"invalid"; + break; + case SidTypeUnknown: + name = L"unknown"; + break; + } + + u = malloc(sizeof(User)); + if(u == nil){ + qunlock(&users.lk); + return 0; + } + u->next = nil; + u->group = nil; + u->sid = dupsid(sid); + u->type = type; + u->name = nil; + if(name != nil) + u->name = runesdup(name); + u->dom = nil; + if(dom != nil) + u->dom = runesdup(dom); + + u->next = users.u; + users.u = u; + + qunlock(&users.lk); + return u; +} + +/* + * check if u is a member of gsid, + * which might be a group. + */ +static int +ismembersid(Rune *srv, User *u, SID *gsid) +{ + User *g; + + if(EqualSid(u->sid, gsid)) + return 1; + + g = sidtouser(srv, gsid); + if(g == 0) + return 0; + return ismember(u, g); +} + +static int +ismember(User *u, User *g) +{ + Gmem *grps; + + if(EqualSid(u->sid, g->sid)) + return 1; + + if(EqualSid(g->sid, everyone)) + return 1; + + qlock(&u->lk); + addgroups(u, 0); + for(grps = u->group; grps != 0; grps = grps->next){ + if(EqualSid(grps->user->sid, g->sid)){ + qunlock(&u->lk); + return 1; + } + } + qunlock(&u->lk); + return 0; +} + +/* + * find out what groups a user belongs to. + * if force, throw out the old info and do it again. + * + * note that a global group is also know as a group, + * and a local group is also know as an alias. + * global groups can only contain users. + * local groups can contain global groups or users. + * this code finds all global groups to which a user belongs, + * and all the local groups to which the user or a global group + * containing the user belongs. + */ +static void +addgroups(User *u, int force) +{ + LOCALGROUP_USERS_INFO_0 *loc; + GROUP_USERS_INFO_0 *grp; + DWORD i, n, rem; + User *gu; + Gmem *g, *next; + Rune *srv, srvrock[MAX_PATH]; + + if(force){ + u->gotgroup = 0; + for(g = u->group; g != nil; g = next){ + next = g->next; + free(g); + } + u->group = nil; + } + if(u->gotgroup) + return; + u->gotgroup = 1; + + n = 0; + srv = domsrv(u->dom, srvrock); + i = net.UserGetGroups(srv, u->name, 0, + (BYTE**)&grp, MAX_PREFERRED_LENGTH, &n, &rem); + if(i == NERR_Success || i == ERROR_MORE_DATA){ + for(i = 0; i < n; i++){ + gu = domnametouser(srv, grp[i].grui0_name, u->dom); + if(gu == 0) + continue; + g = malloc(sizeof(Gmem)); + if(g == nil) + error(Enomem); + g->user = gu; + g->next = u->group; + u->group = g; + } + net.ApiBufferFree(grp); + } + + n = 0; + i = net.UserGetLocalGroups(srv, u->name, 0, LG_INCLUDE_INDIRECT, + (BYTE**)&loc, MAX_PREFERRED_LENGTH, &n, &rem); + if(i == NERR_Success || i == ERROR_MORE_DATA){ + for(i = 0; i < n; i++){ + gu = domnametouser(srv, loc[i].lgrui0_name, u->dom); + if(gu == NULL) + continue; + g = malloc(sizeof(Gmem)); + if(g == nil) + error(Enomem); + g->user = gu; + g->next = u->group; + u->group = g; + } + net.ApiBufferFree(loc); + } +} + +static SID* +dupsid(SID *sid) +{ + SID *nsid; + int n; + + n = GetLengthSid(sid); + nsid = malloc(n); + if(nsid == nil || !CopySid(n, nsid, sid)) + panic("can't copy sid"); + return nsid; +} + +/* + * return the name of the server machine for file + */ +static Rune* +filesrv(char *file) +{ + int n; + Rune *srv; + char *p, uni[MAX_PATH], mfile[MAX_PATH]; + wchar_t vol[3]; + + strcpy(mfile, file); + /* assume file is a fully qualified name - X: or \\server */ + if(file[1] == ':') { + vol[0] = file[0]; + vol[1] = file[1]; + vol[2] = 0; + if(GetDriveType(vol) != DRIVE_REMOTE) + return 0; + n = sizeof(uni); + if(WNetGetUniversalName(vol, UNIVERSAL_NAME_INFO_LEVEL, uni, &n) != NO_ERROR) + return nil; + runestoutf(mfile, ((UNIVERSAL_NAME_INFO*)uni)->lpUniversalName, MAX_PATH); + file = mfile; + } + file += 2; + p = strchr(file, '\\'); + if(p == 0) + n = strlen(file); + else + n = p - file; + if(n >= MAX_PATH) + n = MAX_PATH-1; + + memmove(uni, file, n); + uni[n] = '\0'; + + srv = malloc((n + 1) * sizeof(Rune)); + if(srv == nil) + panic("filesrv: no memory"); + utftorunes(srv, uni, n+1); + return srv; +} + +/* + * does the file system support acls? + */ +static int +fsacls(char *file) +{ + char *p; + DWORD flags; + char path[MAX_PATH]; + wchar_t wpath[MAX_PATH]; + + /* assume file is a fully qualified name - X: or \\server */ + if(file[1] == ':') { + path[0] = file[0]; + path[1] = file[1]; + path[2] = '\\'; + path[3] = 0; + } else { + strcpy(path, file); + p = strchr(path+2, '\\'); + if(p == 0) + return 0; + p = strchr(p+1, '\\'); + if(p == 0) + strcat(path, "\\"); + else + p[1] = 0; + } + utftorunes(wpath, path, MAX_PATH); + if(!GetVolumeInformation(wpath, NULL, 0, NULL, NULL, &flags, NULL, 0)) + return 0; + + return flags & FS_PERSISTENT_ACLS; +} + +/* + * given a domain, find out the server to ask about its users. + * we just ask the local machine to do the translation, + * so it might fail sometimes. in those cases, we don't + * trust the domain anyway, and vice versa, so it's not + * clear what benifit we would gain by getting the answer "right". + */ +static Rune* +domsrv(Rune *dom, Rune srv[MAX_PATH]) +{ + Rune *psrv; + int n, r; + + if(dom[0] == 0) + return nil; + + r = net.GetAnyDCName(NULL, dom, (LPBYTE*)&psrv); + if(r == NERR_Success) { + n = runeslen(psrv); + if(n >= MAX_PATH) + n = MAX_PATH-1; + memmove(srv, psrv, n*sizeof(Rune)); + srv[n] = 0; + net.ApiBufferFree(psrv); + return srv; + } + + return nil; +} + +Rune* +runesdup(Rune *r) +{ + int n; + Rune *s; + + n = runeslen(r) + 1; + s = malloc(n * sizeof(Rune)); + if(s == nil) + error(Enomem); + memmove(s, r, n * sizeof(Rune)); + return s; +} + +int +runeslen(Rune *r) +{ + int n; + + n = 0; + while(*r++ != 0) + n++; + return n; +} + +char* +runestoutf(char *p, Rune *r, int nc) +{ + char *op, *ep; + int n, c; + + op = p; + ep = p + nc; + while(c = *r++) { + n = 1; + if(c >= Runeself) + n = runelen(c); + if(p + n >= ep) + break; + if(c < Runeself) + *p++ = c; + else + p += runetochar(p, r-1); + } + *p = '\0'; + return op; +} + +Rune* +utftorunes(Rune *r, char *p, int nc) +{ + Rune *or, *er; + + or = r; + er = r + nc; + while(*p != '\0' && r + 1 < er) + p += chartorune(r++, p); + *r = '\0'; + return or; +} + +int +runescmp(Rune *s1, Rune *s2) +{ + Rune r1, r2; + + for(;;) { + r1 = *s1++; + r2 = *s2++; + if(r1 != r2) { + if(r1 > r2) + return 1; + return -1; + } + if(r1 == 0) + return 0; + } +} + +Dev fsdevtab = { + 'U', + "fs", + + fsinit, + fsattach, + fswalk, + fsstat, + fsopen, + fscreate, + fsclose, + fsread, + devbread, + fswrite, + devbwrite, + fsremove, + fswstat +}; diff --git a/emu/Nt/emu b/emu/Nt/emu new file mode 100644 index 00000000..14530018 --- /dev/null +++ b/emu/Nt/emu @@ -0,0 +1,107 @@ +env + LDFLAGS= -subsystem:console -entry:WinMainCRTStartup $LDFLAGS + +dev + root + cons + env + mnt + pipe + prog + prof + srv + dup + ssl + cap + fs + cmd cmd + indir + + draw win + + ip ipif ipaux + eia + audio audio + mem + arch + pointer + snarf + +lib + interp + tk + freetype + math + draw + memlayer + memdraw + keyring + sec + mp + 9 + +link + +mod + sys + draw + tk + math + srv srv + keyring + loader + freetype + +port + alloc + cache + chan + dev + dial + dis + discall + env + error + errstr + exception + exportfs + inferno + latin1 + main + parse + pgrp + print + proc + qio + random + sysfile + uqid + +code + +init + emuinit + +root + /dev / + /fd / + /prog / + /net / + /net.alt / + /chan / + /nvfs / + /env / +# /chan +# /dev +# /dis +# /env +# /n +# /net +# /nvfs / +# /prog +# /icons +# /osinit.dis +# /dis/emuinit.dis +# /dis/lib/auth.dis +# /dis/lib/ssl.dis +# /n/local / diff --git a/emu/Nt/fp.c b/emu/Nt/fp.c new file mode 100644 index 00000000..586c5964 --- /dev/null +++ b/emu/Nt/fp.c @@ -0,0 +1,107 @@ +typedef unsigned long ulong; +typedef unsigned int uint; +typedef unsigned short ushort; +typedef unsigned char uchar; +typedef signed char schar; + +#include <float.h> +#include "mathi.h" + +#define NANEXP (2047<<20) +#define NANMASK (2047<<20) +#define NANSIGN (1<<31) + +int isInf(double, int); + +double +NaN(void) +{ + union + { + double d; + long x[2]; + } a; + + a.x[1] = NANEXP; + a.x[0] = 1; + return a.d; +} + +int +isNaN(double d) +{ + union + { + double d; + long x[2]; + } a; + + a.d = d; + if((a.x[1] & NANMASK) != NANEXP) + return 0; + return !isInf(d, 0); +} + +double +Inf(int sign) +{ + union + { + double d; + long x[2]; + } a; + + a.x[1] = NANEXP; + a.x[0] = 0; + if(sign < 0) + a.x[1] |= NANSIGN; + return a.d; +} + +int +isInf(double d, int sign) +{ + union + { + double d; + long x[2]; + } a; + + a.d = d; + if(a.x[0] != 0) + return 0; + if(a.x[1] == NANEXP) + return sign >= 0; + if(a.x[1] == (NANEXP|NANSIGN)) + return sign <= 0; + return 0; +} + +ulong +getfcr(void) +{ + return getFPcontrol(); +} + +void +setfcr(ulong m) +{ + FPcontrol(m, ~0); +} + +ulong +getfsr(void) +{ + return getFPstatus(); +} + +void +setfsr(ulong m) +{ + FPstatus(m, ~0); +} + + + + + diff --git a/emu/Nt/ie b/emu/Nt/ie new file mode 100644 index 00000000..e92d72d5 --- /dev/null +++ b/emu/Nt/ie @@ -0,0 +1,105 @@ +env + LDFLAGS= -subsystem:windows $LDFLAGS + OSX= ie-os + +dev + root + cons + env + mnt + pipe + prog + prof + srv + dup + ssl + cap + fs + indir + + draw ie-win + + ip ipif ipaux + eia + audio audio + mem + pointer +# arch + +lib + interp + tk + freetype + math + draw + memlayer + memdraw + keyring + sec + mp + 9 + +link + +mod + sys + draw + tk + math + srv srv + keyring + loader + freetype + +port + alloc + cache + chan + dev + dial + dis + discall + env + error + errstr + exception + exportfs + inferno + latin1 + main + parse + pgrp + print + proc + qio + random + sysfile + uqid + +code + +init + emuinit + +root + /root / + /root/dev / + /root/fd / + /root/prog / + /root/net / + /root/net.alt / + /root/chan / + /root/nvfs / + /root/env / + /root/dis / + /root/dis/lib / + /root/dis/install / + /root/dis/install/archfs.dis /dis/install/archfs.dis + /root/dis/install/arch.dis /dis/install/arch.dis + /root/dis/gunzip.dis /dis/gunzip.dis + /root/dis/lib/bufio.dis /dis/lib/bufio.dis + /root/dis/lib/inflate.dis /dis/lib/inflate.dis + /root/dis/lib/string.dis /dis/lib/string.dis + /root/dis/lib/daytime.dis /dis/lib/daytime.dis + /root/dis/lib/arg.dis /dis/lib/arg.dis + /root/dis/lib/styx.dis /dis/lib/styx.dis diff --git a/emu/Nt/ie-os.c b/emu/Nt/ie-os.c new file mode 100644 index 00000000..f2e559ca --- /dev/null +++ b/emu/Nt/ie-os.c @@ -0,0 +1,840 @@ +#define Unknown win_Unknown +#define UNICODE +#include <windows.h> +#include <winbase.h> +#include <winsock.h> +#undef Unknown +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "ieplugin.h" + +extern int SYS_SLEEP = 2; +extern int SOCK_SELECT = 3; +#define MAXSLEEPERS 1500 + +extern Plugin* plugin; +extern void newiop(); +extern int sendiop(); + +DWORD PlatformId; +static char* path; +static HANDLE kbdh = INVALID_HANDLE_VALUE; +static HANDLE conh = INVALID_HANDLE_VALUE; +static int sleepers; + +static ulong erendezvous(void*, ulong); + + wchar_t *widen(char *s); + char *narrowen(wchar_t *ws); + int widebytes(wchar_t *ws); + int runeslen(Rune*); + Rune* runesdup(Rune*); + Rune* utftorunes(Rune*, char*, int); + char* runestoutf(char*, Rune*, int); + int runescmp(Rune*, Rune*); + +__declspec(thread) Proc *up; + +HANDLE ntfd2h(int); +int nth2fd(HANDLE); +char *hosttype = "Nt"; + +static void +pfree(Proc *p) +{ + Osenv *e; + + lock(&procs.l); + if(p->prev) + p->prev->next = p->next; + else + procs.head = p->next; + + if(p->next) + p->next->prev = p->prev; + else + procs.tail = p->prev; + unlock(&procs.l); + + e = p->env; + if(e != nil) { + closefgrp(e->fgrp); + closepgrp(e->pgrp); + closeegrp(e->egrp); + closesigs(e->sigs); + } + free(e->user); + free(p->prog); + free(p); +} + +void +osblock(void) +{ + erendezvous(up, 0); +} + +void +osready(Proc *p) +{ + erendezvous(p, 0); +} + + +void +pexit(char *msg, int t) +{ + pfree(up); + ExitThread(0); +} + +LONG TrapHandler(LPEXCEPTION_POINTERS ureg); + +__cdecl +Exhandler(EXCEPTION_RECORD *rec, void *frame, CONTEXT *context, void *dcon) +{ + EXCEPTION_POINTERS ep; + ep.ExceptionRecord = rec; + ep.ContextRecord = context; + TrapHandler(&ep); + return ExceptionContinueExecution; +} + +DWORD WINAPI +tramp(LPVOID p) +{ + // install our own exception handler + // replacing all others installed on this thread + DWORD handler = (DWORD)Exhandler; + _asm { + mov eax,handler + push eax + mov eax,-1 + push eax + mov fs:[0],esp + } + + up = p; + up->func(up->arg); + pexit("", 0); + // should never get here but tidy up anyway + _asm { + mov fs:[0],-1 + add esp, 8 + } + return 0; +} + +int +kproc(char *name, void (*func)(void*), void *arg, int flags) +{ + DWORD h; + Proc *p; + Pgrp *pg; + Fgrp *fg; + Egrp *eg; + + p = newproc(); + if(p == nil){ + print("out of kernel processes\n"); + return -1; + } + + if(flags & KPDUPPG) { + pg = up->env->pgrp; + incref(&pg->r); + p->env->pgrp = pg; + } + if(flags & KPDUPFDG) { + fg = up->env->fgrp; + incref(&fg->r); + p->env->fgrp = fg; + } + if(flags & KPDUPENVG) { + eg = up->env->egrp; + incref(&eg->r); + p->env->egrp = eg; + } + + p->env->ui = up->env->ui; + kstrdup(&p->env->user, up->env->user); + strcpy(p->text, name); + + p->func = func; + p->arg = arg; + + lock(&procs.l); + if(procs.tail != nil) { + p->prev = procs.tail; + procs.tail->next = p; + } + else { + procs.head = p; + p->prev = nil; + } + procs.tail = p; + unlock(&procs.l); + + p->pid = (int)CreateThread(0, 16384, tramp, p, 0, &h); + if(p->pid <= 0){ + pfree(p); + print("ran out of kernel processes\n"); + return -1; + } + return p->pid; +} + +#if(_WIN32_WINNT >= 0x0400) +void APIENTRY sleepintr(DWORD param) +{ +} +#endif + +void +oshostintr(Proc *p) +{ + if (p->syscall == SOCK_SELECT) + return; + p->intwait = 0; +#if(_WIN32_WINNT >= 0x0400) + if(p->syscall == SYS_SLEEP) { + QueueUserAPC(sleepintr, (HANDLE) p->pid, (DWORD) p->pid); + } +#endif +} + +void +oslongjmp(void *regs, osjmpbuf env, int val) +{ + USED(regs); + longjmp(env, val); +} + +int +readkbd(void) +{ + DWORD r; + char buf[1]; + + if(ReadFile(plugin->conin, buf, sizeof(buf), &r, 0) == FALSE) + panic("keyboard fail"); + if (r == 0) + panic("keyboard EOF"); + + if(buf[0] == '\r') + buf[0] = '\n'; + return buf[0]; +} + +void +cleanexit(int x) +{ + newiop(); + IOP.op = Iquit; + sendiop(); + ExitProcess(x); +} + +struct ecodes { + DWORD code; + char* name; +} ecodes[] = { + EXCEPTION_ACCESS_VIOLATION, "Segmentation violation", + EXCEPTION_DATATYPE_MISALIGNMENT, "Data Alignment", + EXCEPTION_BREAKPOINT, "Breakpoint", + EXCEPTION_SINGLE_STEP, "SingleStep", + EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "Array Bounds Check", + EXCEPTION_FLT_DENORMAL_OPERAND, "Denormalized Float", + EXCEPTION_FLT_DIVIDE_BY_ZERO, "Floating Point Divide by Zero", + EXCEPTION_FLT_INEXACT_RESULT, "Inexact Floating Point", + EXCEPTION_FLT_INVALID_OPERATION, "Invalid Floating Operation", + EXCEPTION_FLT_OVERFLOW, "Floating Point Result Overflow", + EXCEPTION_FLT_STACK_CHECK, "Floating Point Stack Check", + EXCEPTION_FLT_UNDERFLOW, "Floating Point Result Underflow", + EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by Zero", + EXCEPTION_INT_OVERFLOW, "Integer Overflow", + EXCEPTION_PRIV_INSTRUCTION, "Privileged Instruction", + EXCEPTION_IN_PAGE_ERROR, "Page-in Error", + EXCEPTION_ILLEGAL_INSTRUCTION, "Illegal Instruction", + EXCEPTION_NONCONTINUABLE_EXCEPTION, "Non-Continuable Exception", + EXCEPTION_STACK_OVERFLOW, "Stack Overflow", + EXCEPTION_INVALID_DISPOSITION, "Invalid Disposition", + EXCEPTION_GUARD_PAGE, "Guard Page Violation", + 0, nil +}; + +void +dodisfault(void) +{ + disfault(nil, up->env->errstr); +} + +typedef struct Ereg Ereg; +struct Ereg { + Ereg *prev; + FARPROC handler; +}; + +void +dumpex() +{ + Ereg *er; + int i; + _asm { mov eax,fs:[0] }; + _asm { mov [er],eax }; + + i = 0; + while ((unsigned)er != ~0) { + print("handler %ux\n", er->handler); + i++; + er = er->prev; + } + print("EXCEPTION CHAIN LENGTH = %d\n", i); +} + +LONG +TrapHandler(LPEXCEPTION_POINTERS ureg) +{ + int i; + char *name; + DWORD code; + // WORD pc; + char buf[ERRMAX]; + + code = ureg->ExceptionRecord->ExceptionCode; + // pc = ureg->ContextRecord->Eip; + + name = nil; + for(i = 0; i < nelem(ecodes); i++) { + if(ecodes[i].code == code) { + name = ecodes[i].name; + break; + } + } + + if(name == nil) { + snprint(buf, sizeof(buf), "Unrecognized Machine Trap (%.8lux)\n", code); + name = buf; + } +/* + if(pc != 0) { + snprint(buf, sizeof(buf), "%s: pc=0x%lux", name, pc); + name = buf; + } +*/ + /* YUCK! */ + strncpy(up->env->errstr, name, ERRMAX); + switch (code) { + case EXCEPTION_FLT_DENORMAL_OPERAND: + case EXCEPTION_FLT_DIVIDE_BY_ZERO: + case EXCEPTION_FLT_INEXACT_RESULT: + case EXCEPTION_FLT_INVALID_OPERATION: + case EXCEPTION_FLT_OVERFLOW: + case EXCEPTION_FLT_STACK_CHECK: + case EXCEPTION_FLT_UNDERFLOW: + /* clear exception flags and register stack */ + _asm { fnclex }; + ureg->ContextRecord->FloatSave.StatusWord = 0x0000; + ureg->ContextRecord->FloatSave.TagWord = 0xffff; + } + ureg->ContextRecord->Eip = (DWORD)dodisfault; + return EXCEPTION_CONTINUE_EXECUTION; +} + +static int rebootok = 0; /* is shutdown -r supported? */ + +void +osreboot(char *file, char **argv) +{ + if(rebootok){ + execvp(file, argv); + panic("reboot failure"); + }else + error("reboot option not supported on this system"); +} + +void +libinit(char *imod) +{ + WSADATA wasdat; +// DWORD lasterror, namelen; + OSVERSIONINFO os; +// char sys[64]; + + os.dwOSVersionInfoSize = sizeof(os); + if(!GetVersionEx(&os)) + panic("can't get os version"); + PlatformId = os.dwPlatformId; + if (PlatformId == VER_PLATFORM_WIN32_NT) { /* true for NT and 2000 */ + rebootok = 1; + } else { + rebootok = 0; + } + + if((int)INVALID_HANDLE_VALUE != -1 || sizeof(HANDLE) != sizeof(int)) + panic("invalid handle value or size"); + + if(WSAStartup(MAKEWORD(1, 1), &wasdat) != 0) + panic("no winsock.dll"); + +// gethostname(sys, sizeof(sys)); +// kstrdup(&ossysname, sys); + kstrdup(&ossysname, "plugin"); + +// if(sflag == 0) +// SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)TrapHandler); + + path = getenv("PATH"); + if(path == nil) + path = "."; + + up = newproc(); + if(up == nil) + panic("cannot create kernel process"); + + kstrdup(&eve, "system"); + emuinit(imod); +} + +enum +{ + NHLOG = 7, + NHASH = (1<<NHLOG) +}; + +typedef struct Tag Tag; +struct Tag +{ + void* tag; + ulong val; + HANDLE pid; + Tag* next; +}; + +static Tag* ht[NHASH]; +static Tag* ft; +static Lock hlock; +static int nsema; + +ulong +erendezvous(void *tag, ulong value) +{ + int h; + ulong rval; + Tag *t, **l, *f; + + + h = (ulong)tag & (NHASH-1); + + lock(&hlock); + l = &ht[h]; + for(t = ht[h]; t; t = t->next) { + if(t->tag == tag) { + rval = t->val; + t->val = value; + t->tag = 0; + unlock(&hlock); + if(SetEvent(t->pid) == FALSE) + panic("Release failed\n"); + return rval; + } + } + + t = ft; + if(t == 0) { + t = malloc(sizeof(Tag)); + if(t == nil) + panic("rendezvous: no memory"); + t->pid = CreateEvent(0, 0, 0, 0); + } + else + ft = t->next; + + t->tag = tag; + t->val = value; + t->next = *l; + *l = t; + unlock(&hlock); + + if(WaitForSingleObject(t->pid, INFINITE) != WAIT_OBJECT_0) + panic("WaitForSingleObject failed\n"); + + lock(&hlock); + rval = t->val; + for(f = *l; f; f = f->next) { + if(f == t) { + *l = f->next; + break; + } + l = &f->next; + } + t->next = ft; + ft = t; + unlock(&hlock); + + return rval; +} + +void +FPsave(void *fptr) +{ + _asm { + mov eax, fptr + fstenv [eax] + } +} + +void +FPrestore(void *fptr) +{ + _asm { + mov eax, fptr + fldenv [eax] + } +} + +ulong +umult(ulong a, ulong b, ulong *high) +{ + ulong lo, hi; + + _asm { + mov eax, a + mov ecx, b + MUL ecx + mov lo, eax + mov hi, edx + } + *high = hi; + return lo; +} + +int +close(int fd) +{ + if(fd != -1) + CloseHandle(ntfd2h(fd)); + return 0; +} + +int +read(int fd, void *buf, uint n) +{ + if(!ReadFile(ntfd2h(fd), buf, n, &n, NULL)) + return -1; + return n; +} + +int +write(int fd, void *buf, uint n) +{ + if(fd == 1 || fd == 2){ + int w; + if (plugin->conout == NULL) + return n; + if (!WriteFile(plugin->conout, buf, n, &w, NULL) || n != w) + abort(); + return n; + } + if(!WriteFile(ntfd2h(fd), buf, n, &n, NULL)) + return -1; + return n; +} + +/* + * map handles and fds. + * this code assumes sizeof(HANDLE) == sizeof(int), + * that INVALID_HANDLE_VALUE is -1, and assumes + * that all tests of invalid fds check only for -1, not < 0 + */ +int +nth2fd(HANDLE h) +{ + return (int)h; +} + +HANDLE +ntfd2h(int fd) +{ + return (HANDLE)fd; +} + +void +oslopri(void) +{ + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL); +} + +/* Resolve system header name conflict */ +#undef Sleep +void +sleep(int secs) +{ + Sleep(secs*1000); +} + +void* +sbrk(int size) +{ + void *brk; + + brk = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); + if(brk == 0) + return (void*)-1; + + return brk; +} + +ulong +getcallerpc(void *arg) +{ + ulong cpc; + _asm { + mov eax, dword ptr [ebp] + mov eax, dword ptr [eax+4] + mov dword ptr cpc, eax + } + return cpc; +} + +/* +ulong +getpc(void *arg, ulong *narg) +{ + ulong *a = arg, *fp, pc; + + if(a == nil){ + *narg = 0; + return 0; + } + fp = a-2; + pc = fp[1]; + fp = *(ulong**)fp; + if(fp == nil) + *narg = 0; + else + *narg = (ulong)(fp+2); + return pc; +} +*/ + +/* + * Return an abitrary millisecond clock time + */ +long +osmillisec(void) +{ + return GetTickCount(); +} + +#define SEC2MIN 60L +#define SEC2HOUR (60L*SEC2MIN) +#define SEC2DAY (24L*SEC2HOUR) + +/* + * days per month plus days/year + */ +static int dmsize[] = +{ + 365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; +static int ldmsize[] = +{ + 366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +/* + * return the days/month for the given year + */ +static int* +yrsize(int yr) +{ + /* a leap year is a multiple of 4, excluding centuries + * that are not multiples of 400 */ + if( (yr % 4 == 0) && (yr % 100 != 0 || yr % 400 == 0) ) + return ldmsize; + else + return dmsize; +} + +static long +tm2sec(SYSTEMTIME *tm) +{ + long secs; + int i, *d2m; + + secs = 0; + + /* + * seconds per year + */ + for(i = 1970; i < tm->wYear; i++){ + d2m = yrsize(i); + secs += d2m[0] * SEC2DAY; + } + + /* + * seconds per month + */ + d2m = yrsize(tm->wYear); + for(i = 1; i < tm->wMonth; i++) + secs += d2m[i] * SEC2DAY; + + /* + * secs in last month + */ + secs += (tm->wDay-1) * SEC2DAY; + + /* + * hours, minutes, seconds + */ + secs += tm->wHour * SEC2HOUR; + secs += tm->wMinute * SEC2MIN; + secs += tm->wSecond; + + return secs; +} + +/* + * Return the time since the epoch in microseconds + * The epoch is defined at 1 Jan 1970 + */ +vlong +osusectime(void) +{ + SYSTEMTIME tm; + vlong secs; + + GetSystemTime(&tm); + secs = tm2sec(&tm); + return secs * 1000000 + tm.wMilliseconds * 1000; +} + +vlong +osnsec(void) +{ + return osusectime()*1000; /* TO DO better */ +} + +int +osmillisleep(ulong milsec) +{ + up->syscall = 1; + SleepEx(milsec, FALSE); + up->syscall = 0; + return 0; +} + +int +limbosleep(ulong milsec) +{ + if (sleepers > MAXSLEEPERS) + return -1; + sleepers++; + up->syscall = SYS_SLEEP; + SleepEx(milsec, TRUE); + up->syscall = 0; + sleepers--; + return 0; +} + +void +osyield(void) +{ + sleep(0); +} + +void +ospause(void) +{ + for(;;) + sleep(1000000); +} + +/* + * these should never be called, and are included + * as stubs since we are linking against a library which defines them + */ +int +open(const char *path, int how, ...) +{ + panic("open"); + return -1; +} + +int +creat(const char *path, int how) +{ + panic("creat"); + return -1; +} + +int +stat(const char *path, struct stat *sp) +{ + panic("stat"); + return -1; +} + +int +chown(const char *path, int uid, int gid) +{ + panic("chown"); + return -1; +} + +int +chmod(const char *path, int mode) +{ + panic("chmod"); + return -1; +} + +void +link(char *path, char *next) +{ + panic("link"); +} + +int +segflush(void *a, ulong n) +{ + return 0; +} + +wchar_t * +widen(char *s) +{ + int n; + wchar_t *ws; + + n = utflen(s) + 1; + ws = smalloc(n*sizeof(wchar_t)); + utftorunes(ws, s, n); + return ws; +} + + +char * +narrowen(wchar_t *ws) +{ + char *s; + int n; + + n = widebytes(ws); + s = smalloc(n); + runestoutf(s, ws, n); + return s; +} + + +int +widebytes(wchar_t *ws) +{ + int n = 0; + + while (*ws) + n += runelen(*ws++); + return n+1; +} diff --git a/emu/Nt/ie-win.c b/emu/Nt/ie-win.c new file mode 100644 index 00000000..9e7ff2ef --- /dev/null +++ b/emu/Nt/ie-win.c @@ -0,0 +1,224 @@ +#define Unknown win_Unknown +#include <windows.h> +#undef Unknown +#include "dat.h" +#include "fns.h" +#include "error.h" + +#include "keyboard.h" +#include "cursor.h" +#include "ieplugin.h" + +/* + * image channel descriptors - copied from draw.h as it clashes with windows.h on many things + */ +enum { + CRed = 0, + CGreen, + CBlue, + CGrey, + CAlpha, + CMap, + CIgnore, + NChan, +}; + +#define __DC(type, nbits) ((((type)&15)<<4)|((nbits)&15)) +#define CHAN1(a,b) __DC(a,b) +#define CHAN2(a,b,c,d) (CHAN1((a),(b))<<8|__DC((c),(d))) +#define CHAN3(a,b,c,d,e,f) (CHAN2((a),(b),(c),(d))<<8|__DC((e),(f))) +#define CHAN4(a,b,c,d,e,f,g,h) (CHAN3((a),(b),(c),(d),(e),(f))<<8|__DC((g),(h))) + +#define NBITS(c) ((c)&15) +#define TYPE(c) (((c)>>4)&15) + +enum { + GREY1 = CHAN1(CGrey, 1), + GREY2 = CHAN1(CGrey, 2), + GREY4 = CHAN1(CGrey, 4), + GREY8 = CHAN1(CGrey, 8), + CMAP8 = CHAN1(CMap, 8), + RGB15 = CHAN4(CIgnore, 1, CRed, 5, CGreen, 5, CBlue, 5), + RGB16 = CHAN3(CRed, 5, CGreen, 6, CBlue, 5), + RGB24 = CHAN3(CRed, 8, CGreen, 8, CBlue, 8), + RGBA32 = CHAN4(CRed, 8, CGreen, 8, CBlue, 8, CAlpha, 8), + ARGB32 = CHAN4(CAlpha, 8, CRed, 8, CGreen, 8, CBlue, 8), /* stupid VGAs */ + XRGB32 = CHAN4(CIgnore, 8, CRed, 8, CGreen, 8, CBlue, 8), +}; + +extern ulong displaychan; + +extern void drawend(void); + +extern int chantodepth(ulong); +extern int main(int argc, char **argv); +static void dprint(char*, ...); +static DWORD WINAPI winproc(LPVOID); + +static HINSTANCE inst; +static HINSTANCE previnst; +static int attached; +static ulong *data; + +extern DWORD PlatformId; +char* gkscanid = "emu_win32vk"; + +extern int cflag; +Plugin *plugin = NULL; + +DWORD WINAPI +pluginproc(LPVOID p) +{ + int x, y, b; + + for (;;) { + WaitForSingleObject(plugin->dopop, INFINITE); + switch (POP.op) { + case Pgfxkey: + if(gkbdq != nil) + gkbdputc(gkbdq, POP.u.key); + break; + case Pmouse: + x = POP.u.m.x; + y = POP.u.m.y; + b = POP.u.m.b; + mousetrack(b, x, y, 0); + break; + } + SetEvent(plugin->popdone); + } +} + +int WINAPI +WinMain(HINSTANCE winst, HINSTANCE wprevinst, LPSTR cmdline, int wcmdshow) +{ + HANDLE sharedmem; + uint pid = _getpid(); + char iname[16]; + inst = winst; + previnst = wprevinst; + sprint(iname, "%uX", pid); + sharedmem = OpenFileMapping(FILE_MAP_WRITE, FALSE, iname); + if (sharedmem != NULL) + plugin = MapViewOfFile(sharedmem, FILE_MAP_WRITE, 0, 0, 0); + if (plugin != NULL) { + DWORD tid; + int i; + Xsize = plugin->Xsize; + Ysize = plugin->Ysize; + displaychan = plugin->cdesc; + cflag = plugin->cflag; + for (i = 0; i < PI_NCLOSE; i++) + CloseHandle(plugin->closehandles[i]); + CreateThread(0, 0, pluginproc, 0, 0, &tid); + + /* cmdline passed into WinMain does not contain name of executable. + * The globals __argc and __argv to include this info - like UNIX + */ + main(__argc, __argv); + UnmapViewOfFile(plugin); + plugin = NULL; + } + if (sharedmem != NULL) + CloseHandle(sharedmem); + return 0; +} + +static Lock ioplock; + +void +newiop() +{ + lock(&ioplock); +} + +int +sendiop() +{ + int val; + SetEvent(plugin->doiop); + WaitForSingleObject(plugin->iopdone, INFINITE); + val = plugin->iop.val; + unlock(&ioplock); + return val; +} + +void +dprint(char *fmt, ...) +{ + va_list arg; + char buf[128]; + + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, (LPSTR)arg); + va_end(arg); + OutputDebugString("inferno: "); + OutputDebugString(buf); +} + +uchar* +attachscreen(IRectangle *r, ulong *chan, int *d, int *width, int *softscreen) +{ + int k; + + if (!attached) { + newiop(); + IOP.op = Iattachscr; + if (sendiop() != 0) + return nil; + data = plugin->screen; + attached = 1; + } + r->min.x = 0; + r->min.y = 0; + r->max.x = Xsize; + r->max.y = Ysize; + + if(displaychan == 0) + displaychan = CMAP8; + *chan = displaychan; + + k = chantodepth(displaychan); + *d = k; + *width = (Xsize/4)*(k/8); + *softscreen = 1; + return (uchar*)data; +} + +void +flushmemscreen(IRectangle r) +{ + if(r.max.x<=r.min.x || r.max.y<=r.min.y) + return; + newiop(); + IOP.op = Iflushscr; + IOP.u.r = r; + sendiop(); +} + +void +setpointer(int x, int y) +{ + USED(x); USED(y); + // TODO +} + +void +drawcursor(Drawcursor* c) +{ + USED(c); + // TODO +} + +char* +clipread(void) +{ + return nil; +} + +int +clipwrite(char *p) +{ + USED(p); + return -1; +} diff --git a/emu/Nt/ieplugin.h b/emu/Nt/ieplugin.h new file mode 100644 index 00000000..bb8db90c --- /dev/null +++ b/emu/Nt/ieplugin.h @@ -0,0 +1,75 @@ +typedef struct Pop Pop; +typedef struct Iop Iop; +typedef struct IPoint IPoint; +typedef struct IRectangle IRectangle; +typedef struct Plugin Plugin; + +enum { + // messages from Plugin to Inferno + Pgfxkey, + Pmouse, + + // message from Inferno to Plugin + Iattachscr, + Iflushscr, + Isetcur, + Idrawcur, + Iquit, +}; + +struct Pop { + int op; + union { + int key; // Pgfxkey + struct { + int x; + int y; + int b; + int modify; + } m; // Pmouse + } u; +}; + +struct IPoint +{ + LONG x; + LONG y; +}; + +struct IRectangle +{ + IPoint min; + IPoint max; +}; + +struct Iop { + int op; + int val; + union { + IRectangle r; // Iflushscr + // need additional support for Isetcur & Idrawcur + } u; +}; +#define PI_NCLOSE 2 + +struct Plugin { + LONG sz; // size of this data struct (including screen mem) + HANDLE conin; // console input (from plugin) - never NULL + HANDLE conout; // console output (to plugin) - can be NULL + HANDLE datain; // #C data file for initialisation (HACK!) + HANDLE dopop; // new Pop available + HANDLE popdone; // acknowledgement of Pop + HANDLE doiop; // new Iop available + HANDLE iopdone; // acknowledgement of Iop + HANDLE closehandles[PI_NCLOSE]; + Pop pop; + Iop iop; + int Xsize; // screen dimensions + int Ysize; + ULONG cdesc; // display chans descriptor + int cflag; + ULONG screen[1]; +}; + +#define IOP (plugin->iop) +#define POP (plugin->pop) diff --git a/emu/Nt/ipif.c b/emu/Nt/ipif.c new file mode 100644 index 00000000..4a4e8a12 --- /dev/null +++ b/emu/Nt/ipif.c @@ -0,0 +1,409 @@ +#define Unknown win_Unknown +#include <windows.h> +#include <winbase.h> +#include <sys/types.h> +#include <winsock.h> +#undef Unknown +#include "dat.h" +#include "fns.h" +#include "ip.h" +#include "error.h" + +extern int SOCK_SELECT; + +int +so_socket(int type) +{ + int fd, one; + + switch(type) { + default: + error("bad protocol type"); + case S_TCP: + type = SOCK_STREAM; + break; + case S_UDP: + type = SOCK_DGRAM; + break; + } + fd = socket(AF_INET, type, 0); + if(fd < 0) + oserror(); + if(type == SOCK_DGRAM){ + one = 1; + setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char*)&one, sizeof (one)); + }else{ + one = 1; + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(one)); + } + return fd; +} + +int +so_send(int sock, void *va, int len, void *hdr, int hdrlen) +{ + int r; + struct sockaddr sa; + struct sockaddr_in *sin; + char *h = hdr; + + + osenter(); + if(hdr == 0) + r = send(sock, va, len, 0); + else { + memset(&sa, sizeof(sa), 0); + sin = (struct sockaddr_in*)&sa; + sin->sin_family = AF_INET; + switch(hdrlen){ + case OUdphdrlenv4: + memmove(&sin->sin_addr, h, 4); + memmove(&sin->sin_port, h+8, 2); + break; + case OUdphdrlen: + v6tov4((uchar*)&sin->sin_addr, h); + memmove(&sin->sin_port, h+2*IPaddrlen, 2); /* rport */ + break; + default: + v6tov4((uchar*)&sin->sin_addr, h); + memmove(&sin->sin_port, h+3*IPaddrlen, 2); + break; + } + r = sendto(sock, va, len, 0, &sa, sizeof(sa)); + } + osleave(); + return r; +} + +static int +doselect(int sock) +{ + fd_set waitr; + struct timeval seltime; + + up->syscall = SOCK_SELECT; + FD_ZERO(&waitr); + FD_SET(sock, &waitr); + for(;;){ + int nfds; + fd_set in, exc; + + in = waitr; + exc = waitr; + seltime.tv_sec = 1; + seltime.tv_usec = 0L; + nfds = select(sizeof(fd_set)*8, &in, (fd_set*)0, &exc, &seltime); + if(up->intwait) { + up->intwait = 0; + return -1; + } + if(nfds < 0) { + print("select error\n"); + return 0; + } + if(FD_ISSET(sock, &in) || FD_ISSET(sock, &exc)){ + return 0; + } + } +} + +int +so_recv(int sock, void *va, int len, void *hdr, int hdrlen) +{ + int r, l; + struct sockaddr sa; + struct sockaddr_in *sin; + char h[Udphdrlen]; + + osenter(); + if(doselect(sock) < 0) { + osleave(); + return -1; + } + if(hdr == 0) + r = recv(sock, va, len, 0); + else { + sin = (struct sockaddr_in*)&sa; + l = sizeof(sa); + r = recvfrom(sock, va, len, 0, &sa, &l); + if(r >= 0) { + memset(h, sizeof h, 0); + switch(hdrlen){ + case OUdphdrlenv4: + memmove(h, &sin->sin_addr, 4); + memmove(h+2*IPv4addrlen, &sin->sin_port, 2); + break; + case OUdphdrlen: + v4tov6(h, (uchar*)&sin->sin_addr); + memmove(h+2*IPaddrlen, &sin->sin_port, 2); + break; + default: + v4tov6(h, (uchar*)&sin->sin_addr); + memmove(h+3*IPaddrlen, &sin->sin_port, 2); + break; + } + + /* alas there's no way to get the local addr/port correctly. Pretend. */ + getsockname(sock, &sa, &l); + switch(hdrlen){ + case OUdphdrlenv4: + memmove(h+IPv4addrlen, &sin->sin_addr, IPv4addrlen); + memmove(h+2*IPv4addrlen+2, &sin->sin_port, 2); + break; + case OUdphdrlen: + v4tov6(h+IPaddrlen, (uchar*)&sin->sin_addr); + memmove(h+2*IPaddrlen+2, &sin->sin_port, 2); + break; + default: + v4tov6(h+IPaddrlen, (uchar*)&sin->sin_addr); + v4tov6(h+2*IPaddrlen, (uchar*)&sin->sin_addr); /* ifcaddr */ + memmove(h+3*IPaddrlen+2, &sin->sin_port, 2); + break; + } + memmove(hdr, h, hdrlen); + } + } + osleave(); + return r; +} + +void +so_close(int sock) +{ + closesocket(sock); +} + +void +so_connect(int fd, unsigned long raddr, unsigned short rport) +{ + int r; + struct sockaddr sa; + struct sockaddr_in *sin; + + memset(&sa, 0, sizeof(sa)); + sin = (struct sockaddr_in*)&sa; + sin->sin_family = AF_INET; + hnputs(&sin->sin_port, rport); + hnputl(&sin->sin_addr.s_addr, raddr); + + osenter(); + r = connect(fd, &sa, sizeof(sa)); + osleave(); + if(r < 0) + oserror(); +} + +void +so_getsockname(int fd, unsigned long *laddr, unsigned short *lport) +{ + int len; + struct sockaddr sa; + struct sockaddr_in *sin; + + sin = (struct sockaddr_in*)&sa; + + len = sizeof(sa); + if(getsockname(fd, &sa, &len) < 0) + oserror(); + + if(sin->sin_family != AF_INET || len != sizeof(*sin)) + error("not AF_INET"); + + *laddr = nhgetl(&sin->sin_addr.s_addr); + *lport = nhgets(&sin->sin_port); +} + +void +so_listen(int fd) +{ + int r; + + osenter(); + r = listen(fd, 5); + osleave(); + if(r < 0) + oserror(); +} + +int +so_accept(int fd, unsigned long *raddr, unsigned short *rport) +{ + int nfd, len; + struct sockaddr sa; + struct sockaddr_in *sin; + + sin = (struct sockaddr_in*)&sa; + + len = sizeof(sa); + osenter(); + if(doselect(fd) < 0) { + osleave(); + return -1; + } + nfd = accept(fd, &sa, &len); + osleave(); + if(nfd < 0) + oserror(); + + if(sin->sin_family != AF_INET || len != sizeof(*sin)) + error("not AF_INET"); + + *raddr = nhgetl(&sin->sin_addr.s_addr); + *rport = nhgets(&sin->sin_port); + return nfd; +} + +void +so_bind(int fd, int su, unsigned long addr, unsigned short port) +{ + int i, one; + struct sockaddr sa; + struct sockaddr_in *sin; + + sin = (struct sockaddr_in*)&sa; + + one = 1; +// if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) < 0) { +// oserrstr(up->genbuf, sizeof(up->genbuf)); +// print("setsockopt: %s", err); +// } + + if(su) { + for(i = 600; i < 1024; i++) { + memset(&sa, 0, sizeof(sa)); + sin->sin_family = AF_INET; + hnputl(&sin->sin_addr.s_addr, addr); + hnputs(&sin->sin_port, i); + + if(bind(fd, &sa, sizeof(sa)) >= 0) + return; + } + oserror(); + } + + memset(&sa, 0, sizeof(sa)); + sin->sin_family = AF_INET; + hnputl(&sin->sin_addr.s_addr, addr); + hnputs(&sin->sin_port, port); + + if(bind(fd, &sa, sizeof(sa)) < 0) + oserror(); +} + +void +so_setsockopt(int fd, int opt, int value) +{ + int r; + struct linger l; + + if(opt == SO_LINGER){ + l.l_onoff = 1; + l.l_linger = (short) value; + osenter(); + r = setsockopt(fd, SOL_SOCKET, opt, (char *)&l, sizeof(l)); + osleave(); + }else + error(Ebadctl); + if(r < 0) + oserror(); +} + +int +so_gethostbyname(char *host, char**hostv, int n) +{ + int i; + unsigned char buf[32], *p; + struct hostent *hp; + + hp = gethostbyname(host); + if(hp == 0) + return 0; + + for(i = 0; hp->h_addr_list[i] && i < n; i++) { + p = hp->h_addr_list[i]; + sprint(buf, "%ud.%ud.%ud.%ud", p[0], p[1], p[2], p[3]); + hostv[i] = strdup(buf); + if(hostv[i] == 0) + break; + } + return i; +} + +int +so_gethostbyaddr(char *addr, char **hostv, int n) +{ + int i; + struct hostent *hp; + unsigned long straddr; + + straddr = inet_addr(addr); + if(straddr == -1) + return 0; + + hp = gethostbyaddr((char *)&straddr, sizeof (straddr), AF_INET); + if(hp == 0) + return 0; + + hostv[0] = strdup(hp->h_name); + if(hostv[0] == 0) + return 0; + for(i = 1; hp->h_aliases[i-1] && i < n; i++) { + hostv[i] = strdup(hp->h_aliases[i-1]); + if(hostv[i] == 0) + break; + } + return i; +} + +int +so_getservbyname(char *service, char *net, char *port) +{ + ushort p; + struct servent *s; + + s = getservbyname(service, net); + if(s == 0) + return -1; + p = s->s_port; + sprint(port, "%d", nhgets(&p)); + return 0; +} + +int +so_hangup(int fd, int linger) +{ + int r; + static struct linger l = {1, 1000}; + + osenter(); + if(linger) + setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&l, sizeof(l)); + r = closesocket(fd); + osleave(); + return r; +} + +void +arpadd(char *ipaddr, char *eaddr, int n) +{ + error("arp not implemented"); +} + +int +so_mustbind(int restricted, int port) +{ + USED(restricted); + USED(port); + /* Windows requires bound sockets, even on port 0 */ + return 1; +} + +void +so_keepalive(int fd, int ms) +{ + int on; + + USED(ms); + on = 1; + setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char*)&on, sizeof(on)); +} diff --git a/emu/Nt/mkfile b/emu/Nt/mkfile new file mode 100644 index 00000000..e1e9d24d --- /dev/null +++ b/emu/Nt/mkfile @@ -0,0 +1,49 @@ +SYSTARG=Nt +OBJTYPE=386 +#uncomment following line for full Microsoft debug symbols +#LDEBUG=-debug -debugtype:cv -pdb:none +<../../mkconfig +SYSTARG=Nt +OBJTYPE=386 + +#Configurable parameters + +CONF=emu #default configuration +CONFLIST=emu ie +CLEANCONFLIST= + +INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed + +#end configurable parameters + +OSX=os + +<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system + +<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS + +OBJ=\ + $OSX.$O\ + $CONF.root.$O\ + lock.$O\ + fp.$O\ + vlrt.$O\ + $DEVS\ + $PORT\ + +HFILES=\ + +CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS +SYSLIBS= $SYSLIBS wsock32.lib user32.lib gdi32.lib advapi32.lib winmm.lib mpr.lib +KERNDATE=`{$NDATE} + +default:V: i$CONF.exe + +<../port/portmkfile + +i$CONF.exe: $OBJ $CONF.c $CONF.root.h $LIBFILES + $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c + $LD $LDFLAGS -out:$target $OBJ $CONF.$O $LIBFILES $SYSLIBS + +install:V: i$CONF.exe + cp i$CONF.exe $INSTALLDIR/$CONF.exe diff --git a/emu/Nt/nt.rc b/emu/Nt/nt.rc new file mode 100644 index 00000000..dd31bc0b --- /dev/null +++ b/emu/Nt/nt.rc @@ -0,0 +1 @@ +100 ICON inferno.ico diff --git a/emu/Nt/os.c b/emu/Nt/os.c new file mode 100644 index 00000000..9bf6736a --- /dev/null +++ b/emu/Nt/os.c @@ -0,0 +1,798 @@ +#define Unknown win_Unknown +#define UNICODE +#include <windows.h> +#include <winbase.h> +#include <winsock.h> +#undef Unknown +#include "dat.h" +#include "fns.h" +#include "error.h" + +int SYS_SLEEP = 2; +int SOCK_SELECT = 3; +#define MAXSLEEPERS 1500 + +extern int cflag; + +DWORD PlatformId; +DWORD consolestate; +static char* path; +static HANDLE kbdh = INVALID_HANDLE_VALUE; +static HANDLE conh = INVALID_HANDLE_VALUE; +static HANDLE errh = INVALID_HANDLE_VALUE; +static int donetermset = 0; +static int sleepers = 0; + + wchar_t *widen(char *s); + char *narrowen(wchar_t *ws); + int widebytes(wchar_t *ws); + int runeslen(Rune*); + Rune* runesdup(Rune*); + Rune* utftorunes(Rune*, char*, int); + char* runestoutf(char*, Rune*, int); + int runescmp(Rune*, Rune*); + +__declspec(thread) Proc *up; + +HANDLE ntfd2h(int); +int nth2fd(HANDLE); +void termrestore(void); +char *hosttype = "Nt"; +char *cputype = "386"; + +static void +pfree(Proc *p) +{ + Osenv *e; + + lock(&procs.l); + if(p->prev) + p->prev->next = p->next; + else + procs.head = p->next; + + if(p->next) + p->next->prev = p->prev; + else + procs.tail = p->prev; + unlock(&procs.l); + + e = p->env; + if(e != nil) { + closefgrp(e->fgrp); + closepgrp(e->pgrp); + closeegrp(e->egrp); + closesigs(e->sigs); + } + free(e->user); + free(p->prog); + CloseHandle((HANDLE)p->os); + free(p); +} + +void +osblock(void) +{ + if(WaitForSingleObject((HANDLE)up->os, INFINITE) != WAIT_OBJECT_0) + panic("osblock failed"); +} + +void +osready(Proc *p) +{ + if(SetEvent((HANDLE)p->os) == FALSE) + panic("osready failed"); +} + +void +pexit(char *msg, int t) +{ + pfree(up); + ExitThread(0); +} + +LONG TrapHandler(LPEXCEPTION_POINTERS ureg); + +__cdecl +Exhandler(EXCEPTION_RECORD *rec, void *frame, CONTEXT *context, void *dcon) +{ + EXCEPTION_POINTERS ep; + ep.ExceptionRecord = rec; + ep.ContextRecord = context; + TrapHandler(&ep); + return ExceptionContinueExecution; +} + +DWORD WINAPI +tramp(LPVOID p) +{ + // install our own exception handler + // replacing all others installed on this thread + DWORD handler = (DWORD)Exhandler; + _asm { + mov eax,handler + push eax + mov eax,-1 + push eax + mov fs:[0],esp + } + + up = p; + up->func(up->arg); + pexit("", 0); + /* not reached */ + for(;;) + panic("tramp"); + return 0; +} + +int +kproc(char *name, void (*func)(void*), void *arg, int flags) +{ + DWORD h; + Proc *p; + Pgrp *pg; + Fgrp *fg; + Egrp *eg; + + p = newproc(); + if(p == nil){ + print("out of kernel processes\n"); + return -1; + } + p->os = CreateEvent(NULL, FALSE, FALSE, NULL); + if(p->os == NULL){ + pfree(p); + print("can't allocate os event\n"); + return -1; + } + + if(flags & KPDUPPG) { + pg = up->env->pgrp; + incref(&pg->r); + p->env->pgrp = pg; + } + if(flags & KPDUPFDG) { + fg = up->env->fgrp; + incref(&fg->r); + p->env->fgrp = fg; + } + if(flags & KPDUPENVG) { + eg = up->env->egrp; + incref(&eg->r); + p->env->egrp = eg; + } + + p->env->ui = up->env->ui; + kstrdup(&p->env->user, up->env->user); + strcpy(p->text, name); + + p->func = func; + p->arg = arg; + + lock(&procs.l); + if(procs.tail != nil) { + p->prev = procs.tail; + procs.tail->next = p; + } + else { + procs.head = p; + p->prev = nil; + } + procs.tail = p; + unlock(&procs.l); + + p->pid = (int)CreateThread(0, 16384, tramp, p, 0, &h); + if(p->pid <= 0){ + pfree(p); + print("ran out of kernel processes\n"); + return -1; + } + return p->pid; +} + +#if(_WIN32_WINNT >= 0x0400) +void APIENTRY sleepintr(DWORD param) +{ +} +#endif + +void +oshostintr(Proc *p) +{ + if (p->syscall == SOCK_SELECT) + return; + p->intwait = 0; +#if(_WIN32_WINNT >= 0x0400) + if(p->syscall == SYS_SLEEP) { + QueueUserAPC(sleepintr, (HANDLE) p->pid, (DWORD) p->pid); + } +#endif +} + +void +oslongjmp(void *regs, osjmpbuf env, int val) +{ + USED(regs); + longjmp(env, val); +} + +int +readkbd(void) +{ + DWORD r; + char buf[1]; + + if(ReadFile(kbdh, buf, sizeof(buf), &r, 0) == FALSE) + panic("keyboard fail"); + if (r == 0) + panic("keyboard EOF"); + + if (buf[0] == 0x03) { + // INTR (CTRL+C) + termrestore(); + ExitProcess(0); + } + if(buf[0] == '\r') + buf[0] = '\n'; + return buf[0]; +} + +void +cleanexit(int x) +{ + sleep(2); /* give user a chance to see message */ + termrestore(); + ExitProcess(x); +} + +struct ecodes { + DWORD code; + char* name; +} ecodes[] = { + EXCEPTION_ACCESS_VIOLATION, "Segmentation violation", + EXCEPTION_DATATYPE_MISALIGNMENT, "Data Alignment", + EXCEPTION_BREAKPOINT, "Breakpoint", + EXCEPTION_SINGLE_STEP, "SingleStep", + EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "Array Bounds Check", + EXCEPTION_FLT_DENORMAL_OPERAND, "Denormalized Float", + EXCEPTION_FLT_DIVIDE_BY_ZERO, "Floating Point Divide by Zero", + EXCEPTION_FLT_INEXACT_RESULT, "Inexact Floating Point", + EXCEPTION_FLT_INVALID_OPERATION, "Invalid Floating Operation", + EXCEPTION_FLT_OVERFLOW, "Floating Point Result Overflow", + EXCEPTION_FLT_STACK_CHECK, "Floating Point Stack Check", + EXCEPTION_FLT_UNDERFLOW, "Floating Point Result Underflow", + EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by Zero", + EXCEPTION_INT_OVERFLOW, "Integer Overflow", + EXCEPTION_PRIV_INSTRUCTION, "Privileged Instruction", + EXCEPTION_IN_PAGE_ERROR, "Page-in Error", + EXCEPTION_ILLEGAL_INSTRUCTION, "Illegal Instruction", + EXCEPTION_NONCONTINUABLE_EXCEPTION, "Non-Continuable Exception", + EXCEPTION_STACK_OVERFLOW, "Stack Overflow", + EXCEPTION_INVALID_DISPOSITION, "Invalid Disposition", + EXCEPTION_GUARD_PAGE, "Guard Page Violation", + 0, nil +}; + +void +dodisfault(void) +{ + disfault(nil, up->env->errstr); +} + +typedef struct Ereg Ereg; +struct Ereg { + Ereg *prev; + FARPROC handler; +}; + +void +dumpex() +{ + Ereg *er; + int i; + _asm { mov eax,fs:[0] }; + _asm { mov [er],eax }; + + i = 0; + while ((unsigned)er != ~0) { + print("handler %ux\n", er->handler); + i++; + er = er->prev; + } + print("EXCEPTION CHAIN LENGTH = %d\n", i); +} + +LONG +TrapHandler(LPEXCEPTION_POINTERS ureg) +{ + int i; + char *name; + DWORD code; + // WORD pc; + char buf[ERRMAX]; + + code = ureg->ExceptionRecord->ExceptionCode; + // pc = ureg->ContextRecord->Eip; + + name = nil; + for(i = 0; i < nelem(ecodes); i++) { + if(ecodes[i].code == code) { + name = ecodes[i].name; + break; + } + } + + if(name == nil) { + snprint(buf, sizeof(buf), "Unrecognized Machine Trap (%.8lux)\n", code); + name = buf; + } +/* + if(pc != 0) { + snprint(buf, sizeof(buf), "%s: pc=0x%lux", name, pc); + name = buf; + } +*/ + /* YUCK! */ + strncpy(up->env->errstr, name, ERRMAX); + switch (code) { + case EXCEPTION_FLT_DENORMAL_OPERAND: + case EXCEPTION_FLT_DIVIDE_BY_ZERO: + case EXCEPTION_FLT_INEXACT_RESULT: + case EXCEPTION_FLT_INVALID_OPERATION: + case EXCEPTION_FLT_OVERFLOW: + case EXCEPTION_FLT_STACK_CHECK: + case EXCEPTION_FLT_UNDERFLOW: + /* clear exception flags and register stack */ + _asm { fnclex }; + ureg->ContextRecord->FloatSave.StatusWord = 0x0000; + ureg->ContextRecord->FloatSave.TagWord = 0xffff; + } + ureg->ContextRecord->Eip = (DWORD)dodisfault; + return EXCEPTION_CONTINUE_EXECUTION; +} + +static void +termset(void) +{ + DWORD flag; + + if(donetermset) + return; + donetermset = 1; + conh = GetStdHandle(STD_OUTPUT_HANDLE); + kbdh = GetStdHandle(STD_INPUT_HANDLE); + errh = GetStdHandle(STD_ERROR_HANDLE); + if(errh == INVALID_HANDLE_VALUE) + errh = conh; + + // The following will fail if kbdh not from console (e.g. a pipe) + // in which case we don't care + GetConsoleMode(kbdh, &consolestate); + flag = consolestate; + flag = flag & ~(ENABLE_PROCESSED_INPUT|ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT); + SetConsoleMode(kbdh, flag); +} + +void +termrestore(void) +{ + if(kbdh != INVALID_HANDLE_VALUE) + SetConsoleMode(kbdh, consolestate); +} + +static int rebootok = 0; /* is shutdown -r supported? */ + +void +osreboot(char *file, char **argv) +{ + if(rebootok){ + termrestore(); + execvp(file, argv); + panic("reboot failure"); + } +} + +void +libinit(char *imod) +{ + WSADATA wasdat; + DWORD lasterror, namelen; + OSVERSIONINFO os; + char sys[64], uname[64]; + wchar_t wuname[64]; + char *uns; + + os.dwOSVersionInfoSize = sizeof(os); + if(!GetVersionEx(&os)) + panic("can't get os version"); + PlatformId = os.dwPlatformId; + if (PlatformId == VER_PLATFORM_WIN32_NT) { /* true for NT and 2000 */ + rebootok = 1; + } else { + rebootok = 0; + } + termset(); + + if((int)INVALID_HANDLE_VALUE != -1 || sizeof(HANDLE) != sizeof(int)) + panic("invalid handle value or size"); + + if(WSAStartup(MAKEWORD(1, 1), &wasdat) != 0) + panic("no winsock.dll"); + + gethostname(sys, sizeof(sys)); + kstrdup(&ossysname, sys); +// if(sflag == 0) +// SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)TrapHandler); + + path = getenv("PATH"); + if(path == nil) + path = "."; + + up = newproc(); + if(up == nil) + panic("cannot create kernel process"); + + strcpy(uname, "inferno"); + namelen = sizeof(wuname); + if(GetUserName(wuname, &namelen) != TRUE) { + lasterror = GetLastError(); + if(PlatformId == VER_PLATFORM_WIN32_NT || lasterror != ERROR_NOT_LOGGED_ON) + print("cannot GetUserName: %d\n", lasterror); + }else{ + uns = narrowen(wuname); + snprint(uname, sizeof(uname), "%s", uns); + free(uns); + } + kstrdup(&eve, uname); + + emuinit(imod); +} + +void +FPsave(void *fptr) +{ + _asm { + mov eax, fptr + fstenv [eax] + } +} + +void +FPrestore(void *fptr) +{ + _asm { + mov eax, fptr + fldenv [eax] + } +} + +ulong +umult(ulong a, ulong b, ulong *high) +{ + ulong lo, hi; + + _asm { + mov eax, a + mov ecx, b + MUL ecx + mov lo, eax + mov hi, edx + } + *high = hi; + return lo; +} + +int +close(int fd) +{ + if(fd == -1) + return 0; + CloseHandle(ntfd2h(fd)); + return 0; +} + +int +read(int fd, void *buf, uint n) +{ + if(!ReadFile(ntfd2h(fd), buf, n, &n, NULL)) + return -1; + return n; +} + +int +write(int fd, void *buf, uint n) +{ + HANDLE h; + + if(fd == 1 || fd == 2){ + if(!donetermset) + termset(); + if(fd == 1) + h = conh; + else + h = errh; + if(h == INVALID_HANDLE_VALUE) + return -1; + if(!WriteFile(h, buf, n, &n, NULL)) + return -1; + return n; + } + if(!WriteFile(ntfd2h(fd), buf, n, &n, NULL)) + return -1; + return n; +} + +/* + * map handles and fds. + * this code assumes sizeof(HANDLE) == sizeof(int), + * that INVALID_HANDLE_VALUE is -1, and assumes + * that all tests of invalid fds check only for -1, not < 0 + */ +int +nth2fd(HANDLE h) +{ + return (int)h; +} + +HANDLE +ntfd2h(int fd) +{ + return (HANDLE)fd; +} + +void +oslopri(void) +{ + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL); +} + +/* Resolve system header name conflict */ +#undef Sleep +void +sleep(int secs) +{ + Sleep(secs*1000); +} + +void* +sbrk(int size) +{ + void *brk; + + brk = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); + if(brk == 0) + return (void*)-1; + + return brk; +} + +ulong +getcallerpc(void *arg) +{ + ulong cpc; + _asm { + mov eax, dword ptr [ebp] + mov eax, dword ptr [eax+4] + mov dword ptr cpc, eax + } + return cpc; +} + +/* + * Return an abitrary millisecond clock time + */ +long +osmillisec(void) +{ + return GetTickCount(); +} + +#define SEC2MIN 60L +#define SEC2HOUR (60L*SEC2MIN) +#define SEC2DAY (24L*SEC2HOUR) + +/* + * days per month plus days/year + */ +static int dmsize[] = +{ + 365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; +static int ldmsize[] = +{ + 366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +/* + * return the days/month for the given year + */ +static int* +yrsize(int yr) +{ + /* a leap year is a multiple of 4, excluding centuries + * that are not multiples of 400 */ + if( (yr % 4 == 0) && (yr % 100 != 0 || yr % 400 == 0) ) + return ldmsize; + else + return dmsize; +} + +static long +tm2sec(SYSTEMTIME *tm) +{ + long secs; + int i, *d2m; + + secs = 0; + + /* + * seconds per year + */ + for(i = 1970; i < tm->wYear; i++){ + d2m = yrsize(i); + secs += d2m[0] * SEC2DAY; + } + + /* + * seconds per month + */ + d2m = yrsize(tm->wYear); + for(i = 1; i < tm->wMonth; i++) + secs += d2m[i] * SEC2DAY; + + /* + * secs in last month + */ + secs += (tm->wDay-1) * SEC2DAY; + + /* + * hours, minutes, seconds + */ + secs += tm->wHour * SEC2HOUR; + secs += tm->wMinute * SEC2MIN; + secs += tm->wSecond; + + return secs; +} + +/* + * Return the time since the epoch in microseconds + * The epoch is defined at 1 Jan 1970 + */ +vlong +osusectime(void) +{ + SYSTEMTIME tm; + vlong secs; + + GetSystemTime(&tm); + secs = tm2sec(&tm); + return secs * 1000000 + tm.wMilliseconds * 1000; +} + +vlong +osnsec(void) +{ + return osusectime()*1000; /* TO DO better */ +} + +int +osmillisleep(ulong milsec) +{ + SleepEx(milsec, FALSE); + return 0; +} + +int +limbosleep(ulong milsec) +{ + if (sleepers > MAXSLEEPERS) + return -1; + sleepers++; + up->syscall = SYS_SLEEP; + SleepEx(milsec, TRUE); + up->syscall = 0; + sleepers--; + return 0; +} + +void +osyield(void) +{ + sleep(0); +} + +void +ospause(void) +{ + for(;;) + sleep(1000000); +} + +/* + * these should never be called, and are included + * as stubs since we are linking against a library which defines them + */ +int +open(const char *path, int how, ...) +{ + panic("open"); + return -1; +} + +int +creat(const char *path, int how) +{ + panic("creat"); + return -1; +} + +int +stat(const char *path, struct stat *sp) +{ + panic("stat"); + return -1; +} + +int +chown(const char *path, int uid, int gid) +{ + panic("chown"); + return -1; +} + +int +chmod(const char *path, int mode) +{ + panic("chmod"); + return -1; +} + +void +link(char *path, char *next) +{ + panic("link"); +} + +int +segflush(void *a, ulong n) +{ + return 0; +} + +wchar_t * +widen(char *s) +{ + int n; + wchar_t *ws; + + n = utflen(s) + 1; + ws = smalloc(n*sizeof(wchar_t)); + utftorunes(ws, s, n); + return ws; +} + + +char * +narrowen(wchar_t *ws) +{ + char *s; + int n; + + n = widebytes(ws); + s = smalloc(n); + runestoutf(s, ws, n); + return s; +} + + +int +widebytes(wchar_t *ws) +{ + int n = 0; + + while (*ws) + n += runelen(*ws++); + return n+1; +} diff --git a/emu/Nt/vlrt.c b/emu/Nt/vlrt.c new file mode 100644 index 00000000..43313a29 --- /dev/null +++ b/emu/Nt/vlrt.c @@ -0,0 +1,751 @@ +#include "dat.h" + +/* + * typedef unsigned long ulong; + * typedef unsigned int uint; + * typedef unsigned short ushort; + * typedef unsigned char uchar; + * typedef signed char schar; +*/ + +#define SIGN(n) (1UL<<(n-1)) + +typedef struct Vlong Vlong; +struct Vlong +{ + ulong lo; + ulong hi; +}; + +void abort(void); + +void +_addv(Vlong *r, Vlong a, Vlong b) +{ + ulong lo, hi; + + lo = a.lo + b.lo; + hi = a.hi + b.hi; + if(lo < a.lo) + hi++; + r->lo = lo; + r->hi = hi; +} + +void +_subv(Vlong *r, Vlong a, Vlong b) +{ + ulong lo, hi; + + lo = a.lo - b.lo; + hi = a.hi - b.hi; + if(lo > a.lo) + hi--; + r->lo = lo; + r->hi = hi; +} + +void +_mulv(Vlong *r, Vlong a, Vlong b) +{ + ulong ahi, alo, chi, clo, x; + int i; + + ahi = a.hi; + alo = a.lo; + chi = 0; + clo = 0; + + x = b.lo; + for(i=0; i<32; i++) { + if(x & 1) { + chi += ahi; + clo += alo; + if(clo < alo) + chi++; + } + ahi <<= 1; + if(alo & SIGN(32)) + ahi += 1; + alo <<= 1; + x >>= 1; + } + + /* + * same, but + * alo is known to be 0 + * can stop when x == 0 + */ + for(x=b.hi; x; x>>=1) { + if(x & 1) + chi += ahi; + ahi <<= 1; + } + + r->hi = chi; + r->lo = clo; +} + +void +_d2v(Vlong *y, double d) +{ + Vlong x; + ulong xhi, xlo, ylo, yhi; + int sh; + + *(double*)&x = d; + + xhi = (x.hi & 0xfffff) | 0x100000; + xlo = x.lo; + sh = 1075 - ((x.hi >> 20) & 0x7ff); + + ylo = 0; + yhi = 0; + if(sh >= 0) { + /* v = (hi||lo) >> sh */ + if(sh < 32) { + if(sh == 0) { + ylo = xlo; + yhi = xhi; + } else { + ylo = (xlo >> sh) | (xhi << (32-sh)); + yhi = xhi >> sh; + } + } else { + if(sh == 32) { + ylo = xhi; + } else + if(sh < 64) { + ylo = xhi >> (sh-32); + } + } + } else { + /* v = (hi||lo) << -sh */ + sh = -sh; + if(sh <= 10) { + ylo = xlo << sh; + yhi = (xhi << sh) | (xlo >> (32-sh)); + } else { + /* overflow */ + yhi = d; /* causes something awful */ + } + } + if(x.hi & SIGN(32)) { + if(ylo != 0) { + ylo = -ylo; + yhi = ~yhi; + } else + yhi = -yhi; + } + + y->hi = yhi; + y->lo = ylo; +} + +void +_f2v(Vlong *y, float f) +{ + + _d2v(y, f); +} + +double +_v2d(Vlong x) +{ + if(x.hi & SIGN(32)) { + if(x.lo) { + x.lo = -x.lo; + x.hi = ~x.hi; + } else + x.hi = -x.hi; + return -((long)x.hi*4294967296. + x.lo); + } + return (long)x.hi*4294967296. + x.lo; +} + +float +_v2f(Vlong x) +{ + return _v2d(x); +} + +static void +dodiv(Vlong num, Vlong den, Vlong *q, Vlong *r) +{ + ulong numlo, numhi, denhi, denlo, quohi, quolo, t; + int i; + + numhi = num.hi; + numlo = num.lo; + denhi = den.hi; + denlo = den.lo; + + /* + * get a divide by zero + */ + if(denlo==0 && denhi==0) { + numlo = numlo / denlo; + } + + /* + * set up the divisor and find the number of iterations needed + */ + if(numhi >= SIGN(32)) { + quohi = SIGN(32); + quolo = 0; + } else { + quohi = numhi; + quolo = numlo; + } + i = 0; + while(denhi < quohi || (denhi == quohi && denlo < quolo)) { + denhi = (denhi<<1) | (denlo>>31); + denlo <<= 1; + i++; + } + + quohi = 0; + quolo = 0; + for(; i >= 0; i--) { + quohi = (quohi<<1) | (quolo>>31); + quolo <<= 1; + if(numhi > denhi || (numhi == denhi && numlo >= denlo)) { + t = numlo; + numlo -= denlo; + if(numlo > t) + numhi--; + numhi -= denhi; + quolo |= 1; + } + denlo = (denlo>>1) | (denhi<<31); + denhi >>= 1; + } + + if(q) { + q->lo = quolo; + q->hi = quohi; + } + if(r) { + r->lo = numlo; + r->hi = numhi; + } +} + +void +_divvu(Vlong *q, Vlong n, Vlong d) +{ + + if(n.hi == 0 && d.hi == 0) { + q->hi = 0; + q->lo = n.lo / d.lo; + return; + } + dodiv(n, d, q, 0); +} + +void +_modvu(Vlong *r, Vlong n, Vlong d) +{ + + if(n.hi == 0 && d.hi == 0) { + r->hi = 0; + r->lo = n.lo % d.lo; + return; + } + dodiv(n, d, 0, r); +} + +static void +vneg(Vlong *v) +{ + + if(v->lo == 0) { + v->hi = -v->hi; + return; + } + v->lo = -v->lo; + v->hi = ~v->hi; +} + +void +_divv(Vlong *q, Vlong n, Vlong d) +{ + long nneg, dneg; + + if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) { + q->lo = (long)n.lo / (long)d.lo; + q->hi = ((long)q->lo) >> 31; + return; + } + nneg = n.hi >> 31; + if(nneg) + vneg(&n); + dneg = d.hi >> 31; + if(dneg) + vneg(&d); + dodiv(n, d, q, 0); + if(nneg != dneg) + vneg(q); +} + +void +_modv(Vlong *r, Vlong n, Vlong d) +{ + long nneg, dneg; + + if(n.hi == (((long)n.lo)>>31) && d.hi == (((long)d.lo)>>31)) { + r->lo = (long)n.lo % (long)d.lo; + r->hi = ((long)r->lo) >> 31; + return; + } + nneg = n.hi >> 31; + if(nneg) + vneg(&n); + dneg = d.hi >> 31; + if(dneg) + vneg(&d); + dodiv(n, d, 0, r); + if(nneg) + vneg(r); +} + +void +_rshav(Vlong *r, Vlong a, int b) +{ + long t; + + t = a.hi; + if(b >= 32) { + r->hi = t>>31; + if(b >= 64) { + /* this is illegal re C standard */ + r->lo = t>>31; + return; + } + r->lo = t >> (b-32); + return; + } + if(b <= 0) { + r->hi = t; + r->lo = a.lo; + return; + } + r->hi = t >> b; + r->lo = (t << (32-b)) | (a.lo >> b); +} + +void +_rshlv(Vlong *r, Vlong a, int b) +{ + ulong t; + + t = a.hi; + if(b >= 32) { + r->hi = 0; + if(b >= 64) { + /* this is illegal re C standard */ + r->lo = 0; + return; + } + r->lo = t >> (b-32); + return; + } + if(b <= 0) { + r->hi = t; + r->lo = a.lo; + return; + } + r->hi = t >> b; + r->lo = (t << (32-b)) | (a.lo >> b); +} + +void +_lshv(Vlong *r, Vlong a, int b) +{ + ulong t; + + t = a.lo; + if(b >= 32) { + r->lo = 0; + if(b >= 64) { + /* this is illegal re C standard */ + r->hi = 0; + return; + } + r->hi = t << (b-32); + return; + } + if(b <= 0) { + r->lo = t; + r->hi = a.hi; + return; + } + r->lo = t << b; + r->hi = (t >> (32-b)) | (a.hi << b); +} + +void +_andv(Vlong *r, Vlong a, Vlong b) +{ + r->hi = a.hi & b.hi; + r->lo = a.lo & b.lo; +} + +void +_orv(Vlong *r, Vlong a, Vlong b) +{ + r->hi = a.hi | b.hi; + r->lo = a.lo | b.lo; +} + +void +_xorv(Vlong *r, Vlong a, Vlong b) +{ + r->hi = a.hi ^ b.hi; + r->lo = a.lo ^ b.lo; +} + +void +_vpp(Vlong *l, Vlong *r) +{ + + l->hi = r->hi; + l->lo = r->lo; + r->lo++; + if(r->lo == 0) + r->hi++; +} + +void +_vmm(Vlong *l, Vlong *r) +{ + + l->hi = r->hi; + l->lo = r->lo; + if(r->lo == 0) + r->hi--; + r->lo--; +} + +void +_ppv(Vlong *l, Vlong *r) +{ + + r->lo++; + if(r->lo == 0) + r->hi++; + l->hi = r->hi; + l->lo = r->lo; +} + +void +_mmv(Vlong *l, Vlong *r) +{ + + if(r->lo == 0) + r->hi--; + r->lo--; + l->hi = r->hi; + l->lo = r->lo; +} + +void +_vasop(Vlong *ret, void *lv, void fn(Vlong*, Vlong, Vlong), int type, Vlong rv) +{ + Vlong t, u; + + u.lo = 0; + u.hi = 0; + switch(type) { + default: + abort(); + break; + + case 1: /* schar */ + t.lo = *(schar*)lv; + t.hi = t.lo >> 31; + fn(&u, t, rv); + *(schar*)lv = u.lo; + break; + + case 2: /* uchar */ + t.lo = *(uchar*)lv; + t.hi = 0; + fn(&u, t, rv); + *(uchar*)lv = u.lo; + break; + + case 3: /* short */ + t.lo = *(short*)lv; + t.hi = t.lo >> 31; + fn(&u, t, rv); + *(short*)lv = u.lo; + break; + + case 4: /* ushort */ + t.lo = *(ushort*)lv; + t.hi = 0; + fn(&u, t, rv); + *(ushort*)lv = u.lo; + break; + + case 9: /* int */ + t.lo = *(int*)lv; + t.hi = t.lo >> 31; + fn(&u, t, rv); + *(int*)lv = u.lo; + break; + + case 10: /* uint */ + t.lo = *(uint*)lv; + t.hi = 0; + fn(&u, t, rv); + *(uint*)lv = u.lo; + break; + + case 5: /* long */ + t.lo = *(long*)lv; + t.hi = t.lo >> 31; + fn(&u, t, rv); + *(long*)lv = u.lo; + break; + + case 6: /* ulong */ + t.lo = *(ulong*)lv; + t.hi = 0; + fn(&u, t, rv); + *(ulong*)lv = u.lo; + break; + + case 7: /* vlong */ + case 8: /* uvlong */ + fn(&u, *(Vlong*)lv, rv); + *(Vlong*)lv = u; + break; + } + *ret = u; +} + +void +_p2v(Vlong *ret, void *p) +{ + long t; + + t = (ulong)p; + ret->lo = t; + ret->hi = 0; +} + +void +_sl2v(Vlong *ret, long sl) +{ + long t; + + t = sl; + ret->lo = t; + ret->hi = t >> 31; +} + +void +_ul2v(Vlong *ret, ulong ul) +{ + long t; + + t = ul; + ret->lo = t; + ret->hi = 0; +} + +void +_si2v(Vlong *ret, int si) +{ + long t; + + t = si; + ret->lo = t; + ret->hi = t >> 31; +} + +void +_ui2v(Vlong *ret, uint ui) +{ + long t; + + t = ui; + ret->lo = t; + ret->hi = 0; +} + +void +_sh2v(Vlong *ret, long sh) +{ + long t; + + t = (sh << 16) >> 16; + ret->lo = t; + ret->hi = t >> 31; +} + +void +_uh2v(Vlong *ret, ulong ul) +{ + long t; + + t = ul & 0xffff; + ret->lo = t; + ret->hi = 0; +} + +void +_sc2v(Vlong *ret, long uc) +{ + long t; + + t = (uc << 24) >> 24; + ret->lo = t; + ret->hi = t >> 31; +} + +void +_uc2v(Vlong *ret, ulong ul) +{ + long t; + + t = ul & 0xff; + ret->lo = t; + ret->hi = 0; +} + +long +_v2sc(Vlong rv) +{ + long t; + + t = rv.lo & 0xff; + return (t << 24) >> 24; +} + +long +_v2uc(Vlong rv) +{ + + return rv.lo & 0xff; +} + +long +_v2sh(Vlong rv) +{ + long t; + + t = rv.lo & 0xffff; + return (t << 16) >> 16; +} + +long +_v2uh(Vlong rv) +{ + + return rv.lo & 0xffff; +} + +long +_v2sl(Vlong rv) +{ + + return rv.lo; +} + +long +_v2ul(Vlong rv) +{ + + return rv.lo; +} + +long +_v2si(Vlong rv) +{ + + return rv.lo; +} + +long +_v2ui(Vlong rv) +{ + + return rv.lo; +} + +int +_testv(Vlong rv) +{ + return rv.lo || rv.hi; +} + +int +_eqv(Vlong lv, Vlong rv) +{ + return lv.lo == rv.lo && lv.hi == rv.hi; +} + +int +_nev(Vlong lv, Vlong rv) +{ + return lv.lo != rv.lo || lv.hi != rv.hi; +} + +int +_ltv(Vlong lv, Vlong rv) +{ + return (long)lv.hi < (long)rv.hi || + (lv.hi == rv.hi && lv.lo < rv.lo); +} + +int +_lev(Vlong lv, Vlong rv) +{ + return (long)lv.hi < (long)rv.hi || + (lv.hi == rv.hi && lv.lo <= rv.lo); +} + +int +_gtv(Vlong lv, Vlong rv) +{ + return (long)lv.hi > (long)rv.hi || + (lv.hi == rv.hi && lv.lo > rv.lo); +} + +int +_gev(Vlong lv, Vlong rv) +{ + return (long)lv.hi > (long)rv.hi || + (lv.hi == rv.hi && lv.lo >= rv.lo); +} + +int +_lov(Vlong lv, Vlong rv) +{ + return lv.hi < rv.hi || + (lv.hi == rv.hi && lv.lo < rv.lo); +} + +int +_lsv(Vlong lv, Vlong rv) +{ + return lv.hi < rv.hi || + (lv.hi == rv.hi && lv.lo <= rv.lo); +} + +int +_hiv(Vlong lv, Vlong rv) +{ + return lv.hi > rv.hi || + (lv.hi == rv.hi && lv.lo > rv.lo); +} + +int +_hsv(Vlong lv, Vlong rv) +{ + return lv.hi > rv.hi || + (lv.hi == rv.hi && lv.lo >= rv.lo); +} diff --git a/emu/Nt/win.c b/emu/Nt/win.c new file mode 100644 index 00000000..93c79c25 --- /dev/null +++ b/emu/Nt/win.c @@ -0,0 +1,795 @@ +#define Unknown WUnknown +#define Colormap WColormap +#define Cursor WCursor +#define Display WDisplay +#define Drawable WDrawable +#define Font WFont +#define GC WGC +#define Point WPoint +#define Rectangle WRectangle +#define Screen WScreen +#define Visual WVisual +#define Window WWindow + +#include <windows.h> + +#undef Colormap +#undef Cursor +#undef Display +#undef XDrawable +#undef Font +#undef GC +#undef Point +#undef Rectangle +#undef Screen +#undef Visual +#undef Window +#undef Unknown + +#include "dat.h" +#include "fns.h" +#include "error.h" +#include <draw.h> +#include "keyboard.h" +#include "cursor.h" + +extern ulong displaychan; + +extern char* runestoutf(char*, Rune*, int); +extern int bytesperline(Rectangle, int); +extern int main(int argc, char **argv); +static void dprint(char*, ...); +static DWORD WINAPI winproc(LPVOID); + +static HINSTANCE inst; +static HINSTANCE previnst; +static int cmdshow; +static HWND window; +static HDC screen; +static HPALETTE palette; +static int maxxsize; +static int maxysize; +static int attached; +static int isunicode = 1; +static HCURSOR hcursor; + +char *argv0 = "inferno"; +static ulong *data; + +extern DWORD PlatformId; +char* gkscanid = "emu_win32vk"; + +int WINAPI +WinMain(HINSTANCE winst, HINSTANCE wprevinst, LPSTR cmdline, int wcmdshow) +{ + inst = winst; + previnst = wprevinst; + cmdshow = wcmdshow; + + /* cmdline passed into WinMain does not contain name of executable. + * The globals __argc and __argv to include this info - like UNIX + */ + main(__argc, __argv); + return 0; +} + +static void +dprint(char *fmt, ...) +{ + va_list arg; + char buf[128]; + + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, (LPSTR)arg); + va_end(arg); + OutputDebugString("inferno: "); + OutputDebugString(buf); +} + +static void +graphicscmap(PALETTEENTRY *pal) +{ + int r, g, b, cr, cg, cb, v, p; + int num, den; + int i, j; + for(r=0,i=0;r!=4;r++) for(v=0;v!=4;v++,i+=16){ + for(g=0,j=v-r;g!=4;g++) for(b=0;b!=4;b++,j++){ + den=r; + if(g>den) den=g; + if(b>den) den=b; + if(den==0) /* divide check -- pick grey shades */ + cr=cg=cb=v*17; + else{ + num=17*(4*den+v); + cr=r*num/den; + cg=g*num/den; + cb=b*num/den; + } + p = i+(j&15); + pal[p].peRed = cr*0x01010101; + pal[p].peGreen = cg*0x01010101; + pal[p].peBlue = cb*0x01010101; + pal[p].peFlags = 0; + } + } +} + +static void +graphicsgmap(PALETTEENTRY *pal, int d) +{ + int i, j, s, m, p; + + s = 8-d; + m = 1; + while(--d >= 0) + m *= 2; + m = 255/(m-1); + for(i=0; i < 256; i++){ + j = (i>>s)*m; + p = 255-i; + pal[p].peRed = pal[p].peGreen = pal[p].peBlue = (255-j)*0x01010101; + pal[p].peFlags = 0; + } +} + +static ulong +autochan(void) +{ + HDC dc; + int bpp; + + dc = GetDC(NULL); + if (dc == NULL) + return CMAP8; + + bpp = GetDeviceCaps(dc, BITSPIXEL); + if (bpp < 15) + return CMAP8; + if (bpp < 24) + return RGB15; + if (bpp < 32) + return RGB24; + return XRGB32; +} + +uchar* +attachscreen(Rectangle *r, ulong *chan, int *d, int *width, int *softscreen) +{ + int i, k; + ulong c; + DWORD h; + RECT bs; + RGBQUAD *rgb; + HBITMAP bits; + BITMAPINFO *bmi; + LOGPALETTE *logpal; + PALETTEENTRY *pal; + int bsh, bsw, sx, sy; + + if(attached) + goto Return; + + /* Compute bodersizes */ + memset(&bs, 0, sizeof(bs)); + AdjustWindowRect(&bs, WS_OVERLAPPEDWINDOW, 0); + bsw = bs.right - bs.left; + bsh = bs.bottom - bs.top; + sx = GetSystemMetrics(SM_CXFULLSCREEN) - bsw; + Xsize -= Xsize % 4; /* Round down */ + if(Xsize > sx) + Xsize = sx; + sy = GetSystemMetrics(SM_CYFULLSCREEN) - bsh + 20; + if(Ysize > sy) + Ysize = sy; + + logpal = malloc(sizeof(LOGPALETTE) + 256*sizeof(PALETTEENTRY)); + if(logpal == nil) + return nil; + logpal->palVersion = 0x300; + logpal->palNumEntries = 256; + pal = logpal->palPalEntry; + + c = displaychan; + if(c == 0) + c = autochan(); + k = 8; + if(TYPE(c) == CGrey){ + graphicsgmap(pal, NBITS(c)); + c = GREY8; + }else{ + if(c == RGB15) + k = 16; + else if(c == RGB24) + k = 24; + else if(c == XRGB32) + k = 32; + else + c = CMAP8; + graphicscmap(pal); + } + + palette = CreatePalette(logpal); + + if(k == 8) + bmi = malloc(sizeof(BITMAPINFOHEADER) + 256*sizeof(RGBQUAD)); + else + bmi = malloc(sizeof(BITMAPINFOHEADER)); + if(bmi == nil) + return nil; + bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi->bmiHeader.biWidth = Xsize; + bmi->bmiHeader.biHeight = -Ysize; /* - => origin upper left */ + bmi->bmiHeader.biPlanes = 1; /* always 1 */ + bmi->bmiHeader.biBitCount = k; + bmi->bmiHeader.biCompression = BI_RGB; + bmi->bmiHeader.biSizeImage = 0; /* Xsize*Ysize*(k/8) */ + bmi->bmiHeader.biXPelsPerMeter = 0; + bmi->bmiHeader.biYPelsPerMeter = 0; + bmi->bmiHeader.biClrUsed = 0; + bmi->bmiHeader.biClrImportant = 0; /* number of important colors: 0 means all */ + + if(k == 8){ + rgb = bmi->bmiColors; + for(i = 0; i < 256; i++){ + rgb[i].rgbRed = pal[i].peRed; + rgb[i].rgbGreen = pal[i].peGreen; + rgb[i].rgbBlue = pal[i].peBlue; + } + } + + screen = CreateCompatibleDC(NULL); + if(screen == nil){ + fprint(2, "screen dc nil\n"); + return nil; + } + + if(SelectPalette(screen, palette, 1) == nil){ + fprint(2, "select pallete failed\n"); + } + i = RealizePalette(screen); + GdiFlush(); + bits = CreateDIBSection(screen, bmi, DIB_RGB_COLORS, &data, nil, 0); + if(bits == nil){ + fprint(2, "CreateDIBSection failed\n"); + return nil; + } + + SelectObject(screen, bits); + GdiFlush(); + CreateThread(0, 16384, winproc, nil, 0, &h); + attached = 1; + + Return: + r->min.x = 0; + r->min.y = 0; + r->max.x = Xsize; + r->max.y = Ysize; + displaychan = c; + *chan = c; + *d = k; + *width = (Xsize/4)*(k/8); + *softscreen = 1; + return (uchar*)data; +} + +void +flushmemscreen(Rectangle r) +{ + RECT wr; + + if(r.max.x<=r.min.x || r.max.y<=r.min.y) + return; + wr.left = r.min.x; + wr.top = r.min.y; + wr.right = r.max.x; + wr.bottom = r.max.y; + InvalidateRect(window, &wr, 0); +} + +static void +scancode(WPARAM wparam, LPARAM lparam, int keyup) +{ + uchar buf[2]; + + if(!(lparam & (1<<30))) { /* don't auto-repeat chars */ + buf[0] = wparam; + buf[1] = wparam >> 8; + if (keyup) + buf[1] |= 0x80; + qproduce(gkscanq, buf, sizeof buf); + } +} + +LRESULT CALLBACK +WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + PAINTSTRUCT paint; + HDC hdc; + LPMINMAXINFO mmi; + LONG x, y, w, h, b; + HCURSOR dcurs; + + switch(msg) { + case WM_SETCURSOR: + /* User set */ + if(hcursor != NULL) { + SetCursor(hcursor); + break; + } + /* Pick the default */ + dcurs = LoadCursor(NULL, IDC_ARROW); + SetCursor(dcurs); + break; + case WM_LBUTTONDBLCLK: + b = (1<<8) | 1; + goto process; + case WM_MBUTTONDBLCLK: + b = (1<<8) | 2; + goto process; + case WM_RBUTTONDBLCLK: + b = (1<<8) | 4; + goto process; + case WM_MOUSEMOVE: + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + b = 0; + process: + x = LOWORD(lparam); + y = HIWORD(lparam); + if(wparam & MK_LBUTTON) + b |= 1; + if(wparam & MK_MBUTTON) + b |= 2; + if(wparam & MK_RBUTTON) { + if(wparam & MK_CONTROL) + b |= 2; //simulate middle button + else + b |= 4; //right button + } + mousetrack(b, x, y, 0); + break; + case WM_SYSKEYDOWN: + if(gkscanq) + scancode(wparam, lparam, 0); + break; + case WM_SYSKEYUP: + if(gkscanq) + scancode(wparam, lparam, 1); + else if(wparam == VK_MENU) + gkbdputc(gkbdq, Latin); + break; + case WM_KEYDOWN: + if(gkscanq) { + scancode(wparam, lparam, 0); + break; + } + switch(wparam) { + default: + return 0; + case VK_HOME: + wparam = Home; + break; + case VK_END: + wparam = End; + break; + case VK_UP: + wparam = Up; + break; + case VK_DOWN: + wparam = Down; + break; + case VK_LEFT: + wparam = Left; + break; + case VK_RIGHT: + wparam = Right; + break; + case VK_PRIOR: /* VK_PAGE_UP */ + wparam = Pgup; + break; + case VK_NEXT: /* VK_PAGE_DOWN */ + wparam = Pgdown; + break; + case VK_PRINT: + wparam = Print; + break; + case VK_SCROLL: + wparam = Scroll; + break; + case VK_PAUSE: + wparam = Pause; + break; + case VK_INSERT: + wparam = Ins; + break; + case VK_DELETE: + wparam = Del; + break; +/* + case VK_TAB: + if(GetKeyState(VK_SHIFT)<0) + wparam = BackTab; + else + wparam = '\t'; + break; +*/ + } + gkbdputc(gkbdq, wparam); + break; + case WM_KEYUP: + if(gkscanq) + scancode(wparam, lparam, 1); + break; + case WM_CHAR: + if(gkscanq) + break; + switch(wparam) { + case '\n': + wparam = '\r'; + break; + case '\r': + wparam = '\n'; + break; + case '\t': + if(GetKeyState(VK_SHIFT)<0) + wparam = BackTab; + else + wparam = '\t'; + break; + } + if(lparam & KF_ALTDOWN) + wparam = APP | (wparam & 0xFF); + gkbdputc(gkbdq, wparam); + break; + case WM_CLOSE: + // no longer used? + //m.b = 128; + //m.modify = 1; + //mousetrack(128, 0, 0, 1); + DestroyWindow(hwnd); + break; + case WM_DESTROY: + PostQuitMessage(0); + cleanexit(0); + break; + case WM_PALETTECHANGED: + if((HWND)wparam == hwnd) + break; + /* fall through */ + case WM_QUERYNEWPALETTE: + hdc = GetDC(hwnd); + SelectPalette(hdc, palette, 0); + if(RealizePalette(hdc) != 0) + InvalidateRect(hwnd, nil, 0); + ReleaseDC(hwnd, hdc); + break; + case WM_PAINT: + hdc = BeginPaint(hwnd, &paint); + SelectPalette(hdc, palette, 0); + RealizePalette(hdc); + x = paint.rcPaint.left; + y = paint.rcPaint.top; + w = paint.rcPaint.right - x; + h = paint.rcPaint.bottom - y; + BitBlt(hdc, x, y, w, h, screen, x, y, SRCCOPY); + EndPaint(hwnd, &paint); + break; + case WM_GETMINMAXINFO: + mmi = (LPMINMAXINFO)lparam; + mmi->ptMaxSize.x = maxxsize; + mmi->ptMaxSize.y = maxysize; + mmi->ptMaxTrackSize.x = maxxsize; + mmi->ptMaxTrackSize.y = maxysize; + break; + case WM_SYSCHAR: + case WM_COMMAND: + case WM_CREATE: + case WM_SETFOCUS: + case WM_DEVMODECHANGE: + case WM_WININICHANGE: + case WM_INITMENU: + default: + if(isunicode) + return DefWindowProcW(hwnd, msg, wparam, lparam); + return DefWindowProcA(hwnd, msg, wparam, lparam); + } + return 0; +} + +static DWORD WINAPI +winproc(LPVOID x) +{ + MSG msg; + RECT size; + WNDCLASSW wc; + WNDCLASSA wca; + DWORD ws; + + if(!previnst){ + wc.style = CS_DBLCLKS; + wc.lpfnWndProc = WindowProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = inst; + wc.hIcon = LoadIcon(inst, MAKEINTRESOURCE(100)); + wc.hCursor = NULL; + wc.hbrBackground = GetStockObject(WHITE_BRUSH); + + wc.lpszMenuName = 0; + wc.lpszClassName = L"inferno"; + + if(RegisterClassW(&wc) == 0){ + wca.style = wc.style; + wca.lpfnWndProc = wc.lpfnWndProc; + wca.cbClsExtra = wc.cbClsExtra; + wca.cbWndExtra = wc.cbWndExtra; + wca.hInstance = wc.hInstance; + wca.hIcon = wc.hIcon; + wca.hCursor = wc.hCursor; + wca.hbrBackground = wc.hbrBackground; + + wca.lpszMenuName = 0; + wca.lpszClassName = "inferno"; + isunicode = 0; + + RegisterClassA(&wca); + } + } + + size.left = 0; + size.top = 0; + size.right = Xsize; + size.bottom = Ysize; + + ws = WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX; + + if(AdjustWindowRect(&size, ws, 0)) { + maxxsize = size.right - size.left; + maxysize = size.bottom - size.top; + }else{ + maxxsize = Xsize + 40; + maxysize = Ysize + 40; + } + + if(isunicode) { + window = CreateWindowExW( + 0, /* extended style */ + L"inferno", /* class */ + L"Inferno", /* caption */ + ws, /* style */ + CW_USEDEFAULT, /* init. x pos */ + CW_USEDEFAULT, /* init. y pos */ + maxxsize, /* init. x size */ + maxysize, /* init. y size */ + NULL, /* parent window (actually owner window for overlapped) */ + NULL, /* menu handle */ + inst, /* program handle */ + NULL /* create parms */ + ); + }else{ + window = CreateWindowExA( + 0, /* extended style */ + "inferno", /* class */ + "Inferno", /* caption */ + ws, /* style */ + CW_USEDEFAULT, /* init. x pos */ + CW_USEDEFAULT, /* init. y pos */ + maxxsize, /* init. x size */ + maxysize, /* init. y size */ + NULL, /* parent window (actually owner window for overlapped) */ + NULL, /* menu handle */ + inst, /* program handle */ + NULL /* create parms */ + ); + } + + if(window == nil){ + fprint(2, "can't make window\n"); + ExitThread(0); + } + + SetForegroundWindow(window); + ShowWindow(window, cmdshow); + UpdateWindow(window); + // CloseWindow(window); + + if(isunicode) { + while(GetMessageW(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + }else{ + while(GetMessageA(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessageA(&msg); + } + } + attached = 0; + ExitThread(msg.wParam); + return 0; +} + +void +setpointer(int x, int y) +{ + POINT pt; + + pt.x = x; pt.y = y; + ClientToScreen(window, &pt); + SetCursorPos(pt.x, pt.y); +} + +void +drawcursor(Drawcursor* c) +{ + HCURSOR nh, oh; + Rectangle ir; + int i, h, j, bpl, ch, cw; + uchar *bs, *bc, *and, *xor, *cand, *cxor; + + /* Set the default system cursor */ + if(c->data == nil) { + oh = hcursor; + hcursor = NULL; + if(oh != NULL) { + SendMessage(window, WM_SETCURSOR, (int)window, 0); + DestroyCursor(oh); + } + return; + } + + ir.min.x = c->minx; + ir.min.y = c->miny; + ir.max.x = c->maxx; + ir.max.y = c->maxy; + bpl = bytesperline(ir, 1); + + h = (c->maxy-c->miny)/2; + + ch = GetSystemMetrics(SM_CYCURSOR); + cw = (GetSystemMetrics(SM_CXCURSOR)+7)/8; + + i = ch*cw; + and = malloc(2*i); + if(and == nil) + return; + xor = and + i; + memset(and, 0xff, i); + memset(xor, 0, i); + + cand = and; + cxor = xor; + bc = c->data; + bs = c->data + h*bpl; + + for(i = 0; i < ch && i < h; i++) { + for(j = 0; j < cw && j < bpl; j++) { + cand[j] = ~(bs[j] | bc[j]); + cxor[j] = ~bs[j] & bc[j]; + } + cand += cw; + cxor += cw; + bs += bpl; + bc += bpl; + } + nh = CreateCursor(inst, -c->hotx, -c->hoty, 8*cw, ch, and, xor); + if(nh != NULL) { + oh = hcursor; + hcursor = nh; + SendMessage(window, WM_SETCURSOR, (int)window, 0); + if(oh != NULL) + DestroyCursor(oh); + }else{ + print("CreateCursor error %d\n", GetLastError()); + print("CXCURSOR=%d\n", GetSystemMetrics(SM_CXCURSOR)); + print("CYCURSOR=%d\n", GetSystemMetrics(SM_CYCURSOR)); + } + free(and); +} + +/* + * thanks to drawterm for these + */ + +static char* +clipreadunicode(HANDLE h) +{ + Rune *p; + int n; + char *q; + + p = GlobalLock(h); + n = runenlen(p, runestrlen(p)+1); + q = malloc(n); + if(q != nil) + runestoutf(q, p, n); + GlobalUnlock(h); + + if(q == nil) + error(Enovmem); + return q; +} + +static char * +clipreadutf(HANDLE h) +{ + uchar *p; + + p = GlobalLock(h); + p = strdup(p); + GlobalUnlock(h); + + if(p == nil) + error(Enovmem); + return p; +} + +char* +clipread(void) +{ + HANDLE h; + char *p; + + if(!OpenClipboard(window)) + return strdup(""); + + if((h = GetClipboardData(CF_UNICODETEXT))) + p = clipreadunicode(h); + else if((h = GetClipboardData(CF_TEXT))) + p = clipreadutf(h); + else + p = strdup(""); + + CloseClipboard(); + return p; +} + +int +clipwrite(char *buf) +{ + HANDLE h; + char *p, *e; + Rune *rp; + int n; + + n = 0; + if(buf != nil) + n = strlen(buf); + if(!OpenClipboard(window)) + return -1; + + if(!EmptyClipboard()){ + CloseClipboard(); + return -1; + } + + h = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, (n+1)*sizeof(Rune)); + if(h == NULL) + error(Enovmem); + rp = GlobalLock(h); + p = buf; + e = p+n; + while(p<e) + p += chartorune(rp++, p); + *rp = 0; + GlobalUnlock(h); + + SetClipboardData(CF_UNICODETEXT, h); + + h = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, n+1); + if(h == NULL) + error(Enovmem); + p = GlobalLock(h); + memmove(p, buf, n); + p[n] = 0; + GlobalUnlock(h); + + SetClipboardData(CF_TEXT, h); + + CloseClipboard(); + return n; +} diff --git a/emu/OpenBSD/asm-386.S b/emu/OpenBSD/asm-386.S new file mode 100644 index 00000000..f3a3f9cf --- /dev/null +++ b/emu/OpenBSD/asm-386.S @@ -0,0 +1,111 @@ + .file "asm-OpenBSD-386.S" + +#include <sys/syscall.h> +#include <machine/asm.h> + +#include "rfork_thread.S" + +/* + * executeonnewstack(void *tos, void (*tramp)(void *arg), void *arg) + */ + + .type ournewstack,@function + .global executeonnewstack +executeonnewstack: + pushl %ebp + movl %esp, %ebp + pushl %esi + + movl 8(%ebp), %esi /* get tos */ + subl $4, %esi + movl 16(%ebp), %eax + movl %eax, (%esi) /* stash arg on new stack */ + subl $4, %esi + movl 12(%ebp), %eax + movl %eax, (%esi) /* stash tramp on new stack */ + mov %esi, %esp /* swap stacks pronto */ + popl %eax /* recover the tramp address */ + call *%eax /* and jump to it (ho ho) */ + + /* if we return here, tramp didn't do it's job */ + + addl $8, %esp /* clean up for pose value */ + + leal SYS_exit, %eax + int $0x80 + +/* + * unlockandexit(int *key) + * + * NB: the return status may be rubbish if the stack is reused + * between the unlock and the system call, but this should + * not matter since no task is waiting for the result + */ + + .type unlockandexit,@function + .global unlockandexit +unlockandexit: + pushl %ebp + movl %esp, %ebp + + movl 8(%ebp), %esi /* get the key address */ + pushl $0 /* exit status 0 */ + movl $0, %eax /* unlock the stack allocator */ + movl %eax, (%esi) + leal SYS_exit, %eax /* call exit */ + int $0x80 + +/* + * umult(ulong m1, ulong m2, ulong *hi) + */ + + .type umult,@function + .global umult +umult: + pushl %ebp + movl %esp, %ebp + pushl %ebx + + movl 8(%ebp), %eax + movl 12(%ebp), %ebx + mull %ebx + movl 16(%ebp), %ebx + movl %edx, (%ebx) + + popl %ebx + popl %ebp + ret + + .type FPsave,@function + .global FPsave +FPsave: + pushl %ebp + movl %esp, %ebp + movl 8(%ebp), %eax + fstenv (%eax) + popl %ebp + ret + + .type FPrestore,@function + .global FPrestore +FPrestore: + pushl %ebp + movl %esp, %ebp + movl 8(%ebp), %eax + fldenv (%eax) + popl %ebp + ret + + .type getcallerpc,@function + .global getcallerpc +getcallerpc: + movl 4(%ebp), %eax + ret + + .type _tas,@function + .globl _tas +_tas: + movl $1, %eax + movl 4(%esp), %ecx + xchgl %eax, 0(%ecx) + ret diff --git a/emu/OpenBSD/audio.c b/emu/OpenBSD/audio.c new file mode 100644 index 00000000..4f2de3f6 --- /dev/null +++ b/emu/OpenBSD/audio.c @@ -0,0 +1,547 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/filio.h> +#include "audio.h" +#include <soundcard.h> + +#define Audio_Mic_Val SOUND_MIXER_MIC +#define Audio_Linein_Val SOUND_MIXER_LINE + +#define Audio_Speaker_Val SOUND_MIXER_SPEAKER +#define Audio_Headphone_Val SOUND_MIXER_PHONEOUT +#define Audio_Lineout_Val SOUND_MIXER_VOLUME + +#define Audio_Pcm_Val AFMT_S16_LE +#define Audio_Ulaw_Val AFMT_MU_LAW +#define Audio_Alaw_Val AFMT_A_LAW + +#include "audio-tbls.c" + +#define min(a,b) ((a) < (b) ? (a) : (b)) +static int debug; + +#define AUDIO_FILE_STRING "/dev/dsp" + +enum { + A_Pause, + A_UnPause +}; + +enum { + A_In, + A_Out +}; + +static QLock inlock; +static QLock outlock; + +static int audio_file = -1; /* file in/out */ +static int audio_file_in = -1; /* copy of above when opened O_READ/O_RDWR */ +static int audio_file_out = -1; /* copy of above when opened O_WRITE/O_RDWR */ + +static int audio_swap_flag = 0; /* endian swap */ + +static int audio_in_pause = A_UnPause; + +static Audio_t av; +static int mixerleftvol[32]; +static int mixerrightvol[32]; + +static int audio_enforce(Audio_t*); +static int audio_open(void); +static int audio_pause_in(int, int); +static int audio_flush(int, int); +static int audio_pause_out(int); +static int audio_set_blocking(int); +static int audio_set_info(int, Audio_d*, int); +static void audio_swap_endian(char*, int); + +void +audio_file_init(void) +{ + int i; + static ushort flag = 1; + + audio_swap_flag = *((uchar*)&flag) == 0; /* big-endian? */ + audio_info_init(&av); + for (i = 0; i < 32; i++) + mixerleftvol[i] = mixerrightvol[i] = 100; +} + +void +audio_ctl_init(void) +{ +} + +void +audio_file_open(Chan *c, int omode) +{ + char ebuf[ERRMAX]; + + if (debug) + print("audio_file_open(0x%.8lux, %d)\n", c, omode); + switch(omode){ + case OREAD: + qlock(&inlock); + if(waserror()){ + qunlock(&inlock); + nexterror(); + } + + if(audio_file_in >= 0) + error(Einuse); + if (audio_file < 0) + audio_file = audio_open(); + audio_file_in = audio_file; + poperror(); + qunlock(&inlock); + break; + case OWRITE: + qlock(&outlock); + if(waserror()){ + qunlock(&outlock); + nexterror(); + } + if(audio_file_out >= 0) + error(Einuse); + if (audio_file < 0) + audio_file = audio_open(); + audio_file_out = audio_file; + poperror(); + qunlock(&outlock); + break; + case ORDWR: + qlock(&inlock); + qlock(&outlock); + if(waserror()){ + qunlock(&outlock); + qunlock(&inlock); + nexterror(); + } + if(audio_file_in >= 0 || audio_file_out >= 0) + error(Einuse); + if (audio_file < 0) + audio_file = audio_open(); + audio_file_in = audio_file_out = audio_file; + poperror(); + qunlock(&outlock); + qunlock(&inlock); + break; + } + if (debug) + print("audio_file_open: success\nin %d out %d both %d\n", + audio_file_out, audio_file_in, audio_file); +} + +void +audio_ctl_open(Chan *c, int omode) +{ + USED(c); + USED(omode); +} + +void +audio_file_close(Chan *c) +{ + switch(c->mode){ + case OREAD: + qlock(&inlock); + qlock(&outlock); + if (audio_file_out < 0) { + close(audio_file); + audio_file = -1; + } + qunlock(&outlock); + audio_file_in = -1; + qunlock(&inlock); + break; + case OWRITE: + qlock(&inlock); + qlock(&outlock); + if (audio_file_in < 0) { + close(audio_file); + audio_file = -1; + } + audio_file_out = -1; + qunlock(&outlock); + qunlock(&inlock); + break; + case ORDWR: + qlock(&inlock); + qlock(&outlock); + close(audio_file); + audio_file_in = audio_file_out = audio_file = -1; + qunlock(&outlock); + qunlock(&inlock); + break; + } +} + +void +audio_ctl_close(Chan *c) +{ +} + +long +audio_file_read(Chan *c, void *va, long count, vlong offset) +{ + struct timespec time; + long ba, status, chunk, total; + char *pva = (char *) va; + + qlock(&inlock); + if(waserror()){ + qunlock(&inlock); + nexterror(); + } + + if(audio_file_in < 0) + error(Eperm); + + /* check block alignment */ + ba = av.in.bits * av.in.chan / Bits_Per_Byte; + + if(count % ba) + error(Ebadarg); + + if(! audio_pause_in(audio_file_in, A_UnPause)) + error(Eio); + + total = 0; + while(total < count) { + chunk = count - total; + osenter(); + status = read(audio_file_in, pva + total, chunk); + osleave(); + if(status < 0) + error(Eio); + total += status; + } + + if(total != count) + error(Eio); + + if(audio_swap_flag && av.out.bits == 16) + audio_swap_endian(pva, count); + + poperror(); + qunlock(&inlock); + + return count; +} + +long +audio_file_write(Chan *c, void *va, long count, vlong offset) +{ + struct timespec time; + long status = -1; + long ba, total, chunk, bufsz; + + if (debug > 1) + print("audio_file_write(0x%.8lux, 0x%.8lux, %ld, %uld)\n", + c, va, count, offset); + + qlock(&outlock); + if(waserror()){ + qunlock(&outlock); + nexterror(); + } + + if(audio_file_out < 0) + error(Eperm); + + /* check block alignment */ + ba = av.out.bits * av.out.chan / Bits_Per_Byte; + + if(count % ba) + error(Ebadarg); + + if(audio_swap_flag && av.out.bits == 16) + audio_swap_endian(va, count); + + total = 0; + bufsz = av.out.buf * Audio_Max_Buf / Audio_Max_Val; + + if(bufsz == 0) + error(Ebadarg); + + while(total < count) { + chunk = min(bufsz, count - total); + osenter(); + status = write(audio_file_out, va, chunk); + osleave(); + if(status <= 0) + error(Eio); + total += status; + } + + poperror(); + qunlock(&outlock); + + return count; +} + +static int +audio_open(void) +{ + int fd; + + /* open non-blocking in case someone already has it open */ + /* otherwise we would block until they close! */ + fd = open(AUDIO_FILE_STRING, O_RDWR|O_NONBLOCK); + if(fd < 0) + oserror(); + + /* change device to be blocking */ + if(!audio_set_blocking(fd)) { + if (debug) + print("audio_open: failed to set blocking\n"); + close(fd); + error("cannot set blocking mode"); + } + + if (debug) + print("audio_open: blocking set\n"); + + /* set audio info */ + av.in.flags = ~0; + av.out.flags = 0; + + if(! audio_set_info(fd, &av.in, A_In)) { + close(fd); + error(Ebadarg); + } + + av.in.flags = 0; + + /* tada, we're open, blocking, paused and flushed */ + return fd; +} + +long +audio_ctl_write(Chan *c, void *va, long count, vlong offset) +{ + int fd; + int ff; + Audio_t tmpav = av; + + tmpav.in.flags = 0; + tmpav.out.flags = 0; + + if (!audioparse(va, count, &tmpav)) + error(Ebadarg); + + if (!audio_enforce(&tmpav)) + error(Ebadarg); + + qlock(&inlock); + if (waserror()) { + qunlock(&inlock); + nexterror(); + } + + if (audio_file_in >= 0 && (tmpav.in.flags & AUDIO_MOD_FLAG)) { + if (!audio_pause_in(audio_file_in, A_Pause)) + error(Ebadarg); + if (!audio_flush(audio_file_in, A_In)) + error(Ebadarg); + if (!audio_set_info(audio_file_in, &tmpav.in, A_In)) + error(Ebadarg); + } + poperror(); + qunlock(&inlock); + + qlock(&outlock); + if (waserror()) { + qunlock(&outlock); + nexterror(); + } + if (audio_file_out >= 0 && (tmpav.out.flags & AUDIO_MOD_FLAG)){ + if (!audio_pause_out(audio_file_out)) + error(Ebadarg); + if (!audio_set_info(audio_file_out, &tmpav.out, A_Out)) + error(Ebadarg); + } + poperror(); + qunlock(&outlock); + + tmpav.in.flags = 0; + tmpav.out.flags = 0; + + av = tmpav; + + return count; +} + + + +static int +audio_set_blocking(int fd) +{ + int val; + + if((val = fcntl(fd, F_GETFL, 0)) == -1) + return 0; + + val &= ~O_NONBLOCK; + + if(fcntl(fd, F_SETFL, val) < 0) + return 0; + + return 1; +} + +static int +doioctl(int fd, int ctl, int *info) +{ + int status; + osenter(); + status = ioctl(fd, ctl, info); /* qlock and load general stuff */ + osleave(); + if (status < 0) + print("doioctl(0x%.8lux, 0x%.8lux) failed %d\n", ctl, *info, errno); + return status; +} + +static int +choosefmt(Audio_d *i) +{ + int newbits, newenc; + + newbits = i->bits; + newenc = i->enc; + switch (newenc) { + case Audio_Alaw_Val: + if (newbits == 8) + return AFMT_A_LAW; + break; + case Audio_Ulaw_Val: + if (newbits == 8) + return AFMT_MU_LAW; + break; + case Audio_Pcm_Val: + if (newbits == 8) + return AFMT_U8; + else if (newbits == 16) + return AFMT_S16_LE; + break; + } + return -1; +} + +static int +audio_set_info(int fd, Audio_d *i, int d) +{ + int status; + int unequal_stereo = 0; + + if(fd < 0) + return 0; + + /* fmt */ + if(i->flags & (AUDIO_BITS_FLAG || AUDIO_ENC_FLAG)) { + int oldfmt, newfmt; + oldfmt = AFMT_QUERY; + if (doioctl(fd, SNDCTL_DSP_SETFMT, &oldfmt) < 0) + return 0; + if (debug) + print("audio_set_info: current format 0x%.8lux\n", oldfmt); + newfmt = choosefmt(i); + if (debug) + print("audio_set_info: new format 0x%.8lux\n", newfmt); + if (newfmt == -1 || newfmt != oldfmt && doioctl(fd, SNDCTL_DSP_SETFMT, &newfmt) < 0) + return 0; + } + + /* channels */ + if(i->flags & AUDIO_CHAN_FLAG) { + int channels = i->chan; + if (debug) + print("audio_set_info: new channels %d\n", channels); + if (doioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0 + || channels != i->chan) + return 0; + } + + /* sample rate */ + if(i->flags & AUDIO_RATE_FLAG) { + int speed = i->rate; + if (debug) + print("audio_set_info: new speed %d\n", speed); + if (doioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0 || speed != i->rate) + return 0; + } + + /* dev volume */ + if(i->flags & (AUDIO_LEFT_FLAG | AUDIO_VOL_FLAG | AUDIO_RIGHT_FLAG)) { + int val; + if (i->flags & (AUDIO_LEFT_FLAG | AUDIO_VOL_FLAG)) + mixerleftvol[i->dev] = (i->left * 100) / Audio_Max_Val; + if (i->flags & (AUDIO_RIGHT_FLAG | AUDIO_VOL_FLAG)) + mixerrightvol[i->dev] = (i->right * 100) / Audio_Max_Val; + val = mixerleftvol[i->dev] | (mixerrightvol[i->dev] << 8); + doioctl(fd, MIXER_WRITE(i->dev), &val); + } + + if (i->flags & AUDIO_DEV_FLAG) { + } + + return 1; +} + +void +audio_swap_endian(char *p, int n) +{ + int b; + + while (n > 1) { + b = p[0]; + p[0] = p[1]; + p[1] = b; + p += 2; + n -= 2; + } +} + +static int +audio_pause_out(int fd) +{ + USED(fd); + return 1; +} + +static int +audio_pause_in(int fd, int f) +{ + USED(fd); + USED(f); + return 1; +} + +static int +audio_flush(int fd, int d) +{ + int x; + return doioctl(fd, SNDCTL_DSP_SYNC, &x) >= 0; +} + +static int +audio_enforce(Audio_t *t) +{ + if((t->in.enc == Audio_Ulaw_Val || t->in.enc == Audio_Alaw_Val) && + (t->in.rate != 8000 || t->in.chan != 1)) + return 0; + if((t->out.enc == Audio_Ulaw_Val || t->out.enc == Audio_Alaw_Val) && + (t->out.rate != 8000 || t->out.chan != 1)) + return 0; + return 1; +} + +Audio_t* +getaudiodev(void) +{ + return &av; +} diff --git a/emu/OpenBSD/cmd.c b/emu/OpenBSD/cmd.c new file mode 100644 index 00000000..ed4cabab --- /dev/null +++ b/emu/OpenBSD/cmd.c @@ -0,0 +1,213 @@ +#include <sys/types.h> +#include <signal.h> +#include <pwd.h> +#include <sys/resource.h> +#include <sys/wait.h> +#include <fcntl.h> + +#include "dat.h" +#include "fns.h" +#include "error.h" + +enum +{ + Debug = 0 +}; + +/* + * os-specific devcmd support. + * this version should be reasonably portable across Unix systems. + */ +typedef struct Targ Targ; +struct Targ +{ + int fd[3]; /* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */ + char** args; + char* dir; + int pid; + int wfd; /* child writes errors that occur after the fork or on exec */ + int uid; + int gid; +}; + +extern int gidnobody; +extern int uidnobody; + +static int +childproc(Targ *t) +{ + int i, nfd; + + if(Debug) + print("devcmd: '%s'", t->args[0]); + + nfd = getdtablesize(); + for(i = 0; i < nfd; i++) + if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd) + close(i); + + dup2(t->fd[0], 0); + dup2(t->fd[1], 1); + dup2(t->fd[2], 2); + close(t->fd[0]); + close(t->fd[1]); + close(t->fd[2]); + + /* should have an auth file to do host-specific authorisation? */ + if(t->gid != -1){ + if(setgid(t->gid) < 0 && getegid() == 0){ + fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno)); + _exit(1); + } + } + + if(t->uid != -1){ + if(setuid(t->uid) < 0 && geteuid() == 0){ + fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno)); + _exit(1); + } + } + + if(t->dir != nil && chdir(t->dir) < 0){ + fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno)); + _exit(1); + } + + signal(SIGPIPE, SIG_DFL); + + execvp(t->args[0], t->args); + if(Debug) + print("execvp: %s\n",strerror(errno)); + fprint(t->wfd, "exec failed: %s", strerror(errno)); + + _exit(1); +} + +void* +oscmd(char **args, int nice, char *dir, int *fd) +{ + Targ *t; + int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid; + + t = mallocz(sizeof(*t), 1); + if(t == nil) + return nil; + + fd0[0] = fd0[1] = -1; + fd1[0] = fd1[1] = -1; + fd2[0] = fd2[1] = -1; + wfd[0] = wfd[1] = -1; + if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0) + goto Error; + if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0) /* close on exec to give end of file on success */ + goto Error; + + t->fd[0] = fd0[0]; + t->fd[1] = fd1[1]; + t->fd[2] = fd2[1]; + t->wfd = wfd[1]; + t->args = args; + t->dir = dir; + t->gid = up->env->gid; + if(t->gid == -1) + t->gid = gidnobody; + t->uid = up->env->uid; + if(t->uid == -1) + t->uid = uidnobody; + + signal(SIGCHLD, SIG_DFL); + switch(pid = fork()) { + case -1: + goto Error; + case 0: + setpgid(0, getpid()); + if(nice) + oslopri(); + childproc(t); + _exit(1); + default: + t->pid = pid; + if(Debug) + print("cmd pid %d\n", t->pid); + break; + } + + close(fd0[0]); + close(fd1[1]); + close(fd2[1]); + close(wfd[1]); + + n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1); + close(wfd[0]); + if(n > 0){ + close(fd0[1]); + close(fd1[0]); + close(fd2[0]); + free(t); + up->genbuf[n] = 0; + if(Debug) + print("oscmd: bad exec: %q\n", up->genbuf); + error(up->genbuf); + return nil; + } + + fd[0] = fd0[1]; + fd[1] = fd1[0]; + fd[2] = fd2[0]; + return t; + +Error: + r = errno; + if(Debug) + print("oscmd: %q\n",strerror(r)); + close(fd0[0]); + close(fd0[1]); + close(fd1[0]); + close(fd1[1]); + close(fd2[0]); + close(fd2[1]); + close(wfd[0]); + close(wfd[1]); + error(strerror(r)); + return nil; +} + +int +oscmdkill(void *a) +{ + Targ *t = a; + + if(Debug) + print("kill: %d\n", t->pid); + return kill(-t->pid, SIGTERM); +} + +int +oscmdwait(void *a, char *buf, int n) +{ + Targ *t = a; + int s; + + if(waitpid(t->pid, &s, 0) == -1){ + if(Debug) + print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno)); + return -1; + } + if(WIFEXITED(s)){ + if(WEXITSTATUS(s) == 0) + return snprint(buf, n, "%d 0 0 0 ''", t->pid); + return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s)); + } + if(WIFSIGNALED(s)){ + if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL) + return snprint(buf, n, "%d 0 0 0 killed", t->pid); + return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s)); + } + return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s); +} + +void +oscmdfree(void *a) +{ + free(a); +} diff --git a/emu/OpenBSD/deveia.c b/emu/OpenBSD/deveia.c new file mode 100644 index 00000000..2ef622f7 --- /dev/null +++ b/emu/OpenBSD/deveia.c @@ -0,0 +1,39 @@ +/* + * FreeBSD serial port definitions + */ + +static char *sysdev[] = { + "/dev/cuaa0", + "/dev/cuaa1", + "/dev/cuaa2", + "/dev/cuaa3", +}; + +#include <sys/ioctl.h> +#include "deveia-posix.c" +#include "deveia-bsd.c" + + +static struct tcdef_t bps[] = { + {0, B0}, + {50, B50}, + {75, B75}, + {110, B110}, + {134, B134}, + {150, B150}, + {200, B200}, + {300, B300}, + {600, B600}, + {1200, B1200}, + {1800, B1800}, + {2400, B2400}, + {4800, B4800}, + {9600, B9600}, + {19200, B19200}, + {38400, B38400}, + {57600, B57600}, + {115200, B115200}, + {230400, B230400}, + {-1, -1} +}; + diff --git a/emu/OpenBSD/devfs.c b/emu/OpenBSD/devfs.c new file mode 100644 index 00000000..9017c3fc --- /dev/null +++ b/emu/OpenBSD/devfs.c @@ -0,0 +1 @@ +#include "devfs-posix.c" diff --git a/emu/OpenBSD/emu b/emu/OpenBSD/emu new file mode 100644 index 00000000..5130db7c --- /dev/null +++ b/emu/OpenBSD/emu @@ -0,0 +1,106 @@ +dev + root + cons + env + mnt + pipe + prog + prof + srv + dup + ssl + cap + fs + cmd cmd + indir + + draw + pointer + snarf + + ip ipif ipaux + eia + audio audio + mem + +lib + interp + tk + freetype + math + draw + + memlayer + memdraw + keyring + sec + mp + + 9 + +link + +mod + sys + draw + + tk + math + srv srv + keyring + loader + freetype + +port + alloc + cache + chan + dev + dial + dis + discall + env + error + errstr + exception + exportfs + inferno + latin1 + main + parse + pgrp + print + proc + qio + random + sysfile + uqid + +code + +init + emuinit + +root + /dev / + /fd / + /prog / + /net / + /net.alt / + /chan / + /nvfs / + /env / +# /chan +# /dev +# /dis +# /env +# /n +# /net +# /nvfs / +# /prog +# /icons +# /osinit.dis +# /dis/emuinit.dis +# /dis/lib/auth.dis +# /dis/lib/ssl.dis +# /n/local / diff --git a/emu/OpenBSD/ipif.c b/emu/OpenBSD/ipif.c new file mode 100644 index 00000000..853e2e93 --- /dev/null +++ b/emu/OpenBSD/ipif.c @@ -0,0 +1,372 @@ +#include <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <netdb.h> +#include "dat.h" +#include "fns.h" +#include "ip.h" +#include "error.h" +#include <sys/ioctl.h> + +int +so_socket(int type) +{ + int fd, one; + + switch(type) { + default: + error("bad protocol type"); + case S_TCP: + type = SOCK_STREAM; + break; + case S_UDP: + type = SOCK_DGRAM; + break; + } + + fd = socket(AF_INET, type, 0); + if(fd < 0) + oserror(); + if(type == SOCK_DGRAM){ + one = 1; + setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char*)&one, sizeof (one)); + }else{ + one = 1; + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(one)); + } + return fd; +} + +int +so_send(int sock, void *va, int len, void *hdr, int hdrlen) +{ + int r; + struct sockaddr sa; + struct sockaddr_in *sin; + char *h = hdr; + + + osenter(); + if(hdr == 0) + r = write(sock, va, len); + else { + memset(&sa, sizeof(sa), 0); + sin = (struct sockaddr_in*)&sa; + sin->sin_family = AF_INET; + switch(hdrlen){ + case OUdphdrlenv4: + memmove(&sin->sin_addr, h, 4); + memmove(&sin->sin_port, h+8, 2); + break; + case OUdphdrlen: + v6tov4((uchar*)&sin->sin_addr, h); + memmove(&sin->sin_port, h+2*IPaddrlen, 2); /* rport */ + break; + default: + v6tov4((uchar*)&sin->sin_addr, h); + memmove(&sin->sin_port, h+3*IPaddrlen, 2); + break; + } + r = sendto(sock, va, len, 0, &sa, sizeof(sa)); + } + osleave(); + return r; +} + +int +so_recv(int sock, void *va, int len, void *hdr, int hdrlen) +{ + int r, l; + struct sockaddr sa; + struct sockaddr_in *sin; + char h[Udphdrlen]; + + + osenter(); + if(hdr == 0) + r = read(sock, va, len); + else { + sin = (struct sockaddr_in*)&sa; + l = sizeof(sa); + r = recvfrom(sock, va, len, 0, &sa, &l); + if(r >= 0) { + memset(h, sizeof h, 0); + switch(hdrlen){ + case OUdphdrlenv4: + memmove(h, &sin->sin_addr, 4); + memmove(h+2*IPv4addrlen, &sin->sin_port, 2); + break; + case OUdphdrlen: + v4tov6(h, (uchar*)&sin->sin_addr); + memmove(h+2*IPaddrlen, &sin->sin_port, 2); + break; + default: + v4tov6(h, (uchar*)&sin->sin_addr); + memmove(h+3*IPaddrlen, &sin->sin_port, 2); + break; + } + + /* alas there's no way to get the local addr/port correctly. Pretend. */ + getsockname(sock, &sa, &l); + switch(hdrlen){ + case OUdphdrlenv4: + memmove(h+IPv4addrlen, &sin->sin_addr, IPv4addrlen); + memmove(h+2*IPv4addrlen+2, &sin->sin_port, 2); + break; + case OUdphdrlen: + v4tov6(h+IPaddrlen, (uchar*)&sin->sin_addr); + memmove(h+2*IPaddrlen+2, &sin->sin_port, 2); + break; + default: + v4tov6(h+IPaddrlen, (uchar*)&sin->sin_addr); + v4tov6(h+2*IPaddrlen, (uchar*)&sin->sin_addr); /* ifcaddr */ + memmove(h+3*IPaddrlen+2, &sin->sin_port, 2); + break; + } + memmove(hdr, h, hdrlen); + } + } + osleave(); + return r; +} + +void +so_close(int sock) +{ + close(sock); +} + +void +so_connect(int fd, unsigned long raddr, unsigned short rport) +{ + int r; + struct sockaddr sa; + struct sockaddr_in *sin; + + memset(&sa, 0, sizeof(sa)); + sin = (struct sockaddr_in*)&sa; + sin->sin_family = AF_INET; + hnputs(&sin->sin_port, rport); + hnputl(&sin->sin_addr.s_addr, raddr); + + osenter(); + r = connect(fd, &sa, sizeof(sa)); + osleave(); + if(r < 0) + oserror(); +} + +void +so_getsockname(int fd, unsigned long *laddr, unsigned short *lport) +{ + int len; + struct sockaddr sa; + struct sockaddr_in *sin; + + + len = sizeof(sa); + if(getsockname(fd, &sa, &len) < 0) + oserror(); + + sin = (struct sockaddr_in*)&sa; + if(sin->sin_family != AF_INET || len != sizeof(*sin)) + error("not AF_INET"); + + *laddr = nhgetl(&sin->sin_addr.s_addr); + *lport = nhgets(&sin->sin_port); +} + +void +so_listen(int fd) +{ + int r; + + osenter(); + r = listen(fd, 5); + osleave(); + if(r < 0) + oserror(); +} + +int +so_accept(int fd, unsigned long *raddr, unsigned short *rport) +{ + int nfd, len; + struct sockaddr sa; + struct sockaddr_in *sin; + + sin = (struct sockaddr_in*)&sa; + + len = sizeof(sa); + osenter(); + nfd = accept(fd, &sa, &len); + osleave(); + if(nfd < 0) + oserror(); + + if(sin->sin_family != AF_INET || len != sizeof(*sin)) + error("not AF_INET"); + + *raddr = nhgetl(&sin->sin_addr.s_addr); + *rport = nhgets(&sin->sin_port); + return nfd; +} + +void +so_bind(int fd, int su, unsigned long addr, unsigned short port) +{ + int i, one; + struct sockaddr sa; + struct sockaddr_in *sin; + + sin = (struct sockaddr_in*)&sa; + + one = 1; + if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) < 0) { + oserrstr(up->genbuf, sizeof(up->genbuf)); + print("setsockopt: %s", up->genbuf); + } + + if(su) { + for(i = 600; i < 1024; i++) { + memset(&sa, 0, sizeof(sa)); + sin->sin_family = AF_INET; + hnputl(&sin->sin_addr.s_addr, addr); + hnputs(&sin->sin_port, i); + + if(bind(fd, &sa, sizeof(sa)) >= 0) + return; + } + oserror(); + } + + memset(&sa, 0, sizeof(sa)); + sin->sin_family = AF_INET; + hnputl(&sin->sin_addr.s_addr, addr); + hnputs(&sin->sin_port, port); + + if(bind(fd, &sa, sizeof(sa)) < 0) + oserror(); +} + +void +so_setsockopt(int fd, int opt, int value) +{ + int r; + struct linger l; + + if(opt == SO_LINGER){ + l.l_onoff = 1; + l.l_linger = (short) value; + osenter(); + r = setsockopt(fd, SOL_SOCKET, opt, (char *)&l, sizeof(l)); + osleave(); + }else + error(Ebadctl); + if(r < 0) + oserror(); +} + +int +so_gethostbyname(char *host, char**hostv, int n) +{ + int i; + char buf[32]; + uchar *p; + struct hostent *hp; + + hp = gethostbyname(host); + if(hp == 0) + return 0; + + for(i = 0; hp->h_addr_list[i] && i < n; i++) { + p = hp->h_addr_list[i]; + snprint(buf, sizeof(buf), "%ud.%ud.%ud.%ud", p[0], p[1], p[2], p[3]); + hostv[i] = strdup(buf); + if(hostv[i] == 0) + break; + } + return i; +} + +int +so_gethostbyaddr(char *addr, char **hostv, int n) +{ + int i; + struct hostent *hp; + unsigned long straddr; + + straddr = inet_addr(addr); + if(straddr == -1) + return 0; + + hp = gethostbyaddr((char *)&straddr, sizeof(straddr), AF_INET); + if(hp == 0) + return 0; + + hostv[0] = strdup(hp->h_name); + if(hostv[0] == 0) + return 0; + for(i = 1; hp->h_aliases[i-1] && i < n; i++) { + hostv[i] = strdup(hp->h_aliases[i-1]); + if(hostv[i] == 0) + break; + } + return i; +} + +int +so_getservbyname(char *service, char *net, char *port) +{ + ushort p; + struct servent *s; + + s = getservbyname(service, net); + if(s == 0) + return -1; + p = s->s_port; + sprint(port, "%d", nhgets(&p)); + return 0; +} + +int +so_hangup(int fd, int linger) +{ + int r; + static struct linger l = {1, 1000}; + + osenter(); + if(linger) + setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&l, sizeof(l)); + r = shutdown(fd, 2); + if(r >= 0) + r = close(fd); + osleave(); + return r; +} + +void +arpadd(char *ipaddr, char *eaddr, int n) +{ + error("arp not implemented"); +} + +int +so_mustbind(int restricted, int port) +{ + return restricted || port != 0; +} + +void +so_keepalive(int fd, int ms) +{ + int on; + + USED(ms); + on = 1; + setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char*)&on, sizeof(on)); +} diff --git a/emu/OpenBSD/mkfile b/emu/OpenBSD/mkfile new file mode 100644 index 00000000..ad4e3155 --- /dev/null +++ b/emu/OpenBSD/mkfile @@ -0,0 +1,45 @@ +<../../mkconfig +SYSTARG=OpenBSD +OBJTYPE=386 + +#Configurable parameters + +CONF=emu #default configuration +CONFLIST=emu +CLEANCONFLIST= + +INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed + +#end configurable parameters + +<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system + +<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS + +OBJ=\ + asm-$OBJTYPE.$O\ + os.$O\ + win-x11a.$O\ + $CONF.root.$O\ + lock.$O\ + $DEVS\ + $PORT\ + +HFILES=\ + +CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS +SYSLIBS= -lm -lX11 -lXext -lossaudio +KERNDATE=`{$NDATE} + +default:V: $O.$CONF + +<../port/portmkfile + +$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBFILES + $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c + $LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS + +install:V: $O.$CONF + cp $O.$CONF $INSTALLDIR/$CONF + +devfs.$O: ../port/devfs-posix.c diff --git a/emu/OpenBSD/mkfile-OpenBSD b/emu/OpenBSD/mkfile-OpenBSD new file mode 100644 index 00000000..d1278ec4 --- /dev/null +++ b/emu/OpenBSD/mkfile-OpenBSD @@ -0,0 +1,17 @@ +# +# architecture-dependent files for OpenBSD +# + +LDFLAGS= + +TARGFILES=devfs-posix.$O\ + deveia-OpenBSD.$O\ + devip.$O\ + ipif-posix.$O\ + os-OpenBSD.$O\ + win-x11.$O\ + srv.$O\ + lock.$O\ + asm-OpenBSD-$OBJTYPE.$O + +SYSLIBS=/usr/X11R6/lib/libX11.a -lm diff --git a/emu/OpenBSD/os.c b/emu/OpenBSD/os.c new file mode 100644 index 00000000..2dd4c4d1 --- /dev/null +++ b/emu/OpenBSD/os.c @@ -0,0 +1,524 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#undef getwd +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/param.h> +#include <sys/resource.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <signal.h> +#include <time.h> +#include <termios.h> +#include <sched.h> +#include <pwd.h> +#include <errno.h> +#include <unistd.h> + +enum +{ + DELETE = 0x7F, + NSTACKSPERALLOC = 16, + X11STACK= 256*1024 +}; +char *hosttype = "OpenBSD"; + +int rfork_thread(int, void *, void (*)(void *), void *); + +extern void unlockandexit(int*); +extern void executeonnewstack(void*, void (*f)(void*), void*); +static void *stackalloc(Proc *p, void **tos); +static void stackfreeandexit(void *stack); + +extern int dflag; + +void +pexit(char *msg, int t) +{ + Osenv *e; + Proc *p; + void *kstack; + + lock(&procs.l); + p = up; + if(p->prev) + p->prev->next = p->next; + else + procs.head = p->next; + + if(up->next) + p->next->prev = p->prev; + else + procs.tail = p->prev; + unlock(&procs.l); + + if(0) + print("pexit: %s: %s\n", up->text, msg); + + e = up->env; + if(e != nil) { + closefgrp(e->fgrp); + closepgrp(e->pgrp); + closeegrp(e->egrp); + closesigs(e->sigs); + } + kstack = p->kstack; + free(p->prog); + free(p); + if(kstack != nil) + stackfreeandexit(kstack); +} + +void +trapBUS(int signo, siginfo_t *info, void *context) +{ + if(info) + print("trapBUS: signo: %d code: %d addr: %lx\n", + info->si_signo, info->si_code, info->si_addr); + else + print("trapBUS: no info\n"); + disfault(nil, "Bus error"); +} + +static void +trapUSR1(int signo) +{ + int intwait; + + USED(signo); + + intwait = up->intwait; + up->intwait = 0; /* clear it to let proc continue in osleave */ + + if(up->type != Interp) /* Used to unblock pending I/O */ + return; + if(intwait == 0) /* Not posted so its a sync error */ + disfault(nil, Eintr); /* Should never happen */ +} + +static void +trapUSR2(int signo) +{ + USED(signo); + /* we've done our work of interrupting sigsuspend */ +} + +static void +trapILL(int signo) +{ + disfault(nil, "Illegal instruction"); +} + +static void +trapSEGV(int signo) +{ + disfault(nil, "Segmentation violation"); +} + +static sigset_t initmask; + +static void +setsigs(void) +{ + struct sigaction act; + sigset_t mask; + + memset(&act, 0 , sizeof(act)); + sigemptyset(&initmask); + + signal(SIGPIPE, SIG_IGN); /* prevent signal when devcmd child exits */ + if(signal(SIGTERM, SIG_IGN) != SIG_IGN) + signal(SIGTERM, cleanexit); + + act.sa_handler = trapUSR1; + act.sa_mask = initmask; + sigaction(SIGUSR1, &act, nil); + + act.sa_handler = trapUSR2; + sigaction(SIGUSR2, &act, nil); + sigemptyset(&mask); + sigaddset(&mask, SIGUSR2); + sigaddset(&initmask, SIGUSR2); + sigprocmask(SIG_BLOCK, &mask, NULL); + + /* + * prevent Zombies forming when any process terminates + */ + act.sa_sigaction = 0; + act.sa_flags |= SA_NOCLDWAIT; + if(sigaction(SIGCHLD, &act, nil)) + panic("sigaction SIGCHLD"); + + if(sflag == 0) { + act.sa_sigaction = trapBUS; + act.sa_flags |= SA_SIGINFO; + if(sigaction(SIGBUS, &act, nil)) + panic("sigaction SIGBUS"); + act.sa_handler = trapILL; + if(sigaction(SIGILL, &act, nil)) + panic("sigaction SIGBUS"); + act.sa_handler = trapSEGV; + if(sigaction(SIGSEGV, &act, nil)) + panic("sigaction SIGSEGV"); + if(sigaddset(&initmask, SIGINT) == -1) + panic("sigaddset"); + } + if(sigprocmask(SIG_BLOCK, &initmask, nil)!= 0) + panic("sigprocmask"); +} + +static void +tramp(void *arg) +{ + Proc *p; + + p = arg; + p->pid = p->sigid = getpid(); + sigprocmask(SIG_BLOCK, &initmask, nil); /* in 5.3, rfork_thread doesn't copy from parent, contrary to docs? */ + (*p->func)(p->arg); + pexit("{Tramp}", 0); + _exit(0); +} + +int +kproc(char *name, void (*func)(void*), void *arg, int flags) +{ + Proc *p; + Pgrp *pg; + Fgrp *fg; + Egrp *eg; + int pid; + void *tos; + + p = newproc(); + + if(flags & KPDUPPG) { + pg = up->env->pgrp; + incref(&pg->r); + p->env->pgrp = pg; + } + if(flags & KPDUPFDG) { + fg = up->env->fgrp; + incref(&fg->r); + p->env->fgrp = fg; + } + if(flags & KPDUPENVG) { + eg = up->env->egrp; + incref(&eg->r); + p->env->egrp = eg; + } + + p->env->uid = up->env->uid; + p->env->gid = up->env->gid; + kstrdup(&p->env->user, up->env->user); + + strcpy(p->text, name); + + p->func = func; + p->arg = arg; + + lock(&procs.l); + if(procs.tail != nil) { + p->prev = procs.tail; + procs.tail->next = p; + } + else { + procs.head = p; + p->prev = nil; + } + procs.tail = p; + unlock(&procs.l); + + if(flags & KPX11){ + p->kstack = nil; /* never freed; also up not defined */ + tos = (char*)mallocz(X11STACK, 0) + X11STACK - sizeof(void*); + }else + p->kstack = stackalloc(p, &tos); + pid = rfork_thread(RFPROC|RFMEM|RFNOWAIT, tos, tramp, p); + if(pid < 0) + panic("ourfork"); + + return pid; + +} + +void +oshostintr(Proc *p) +{ + kill(p->sigid, SIGUSR1); +} + +void +osblock(void) +{ + sigset_t mask; + + sigprocmask(SIG_SETMASK, NULL, &mask); + sigdelset(&mask, SIGUSR2); + sigsuspend(&mask); +} + +void +osready(Proc *p) +{ + if(kill(p->sigid, SIGUSR2) < 0) + fprint(2, "emu: osready failed: pid %d: %s\n", p->sigid, strerror(errno)); +} + +void +oslongjmp(void *regs, osjmpbuf env, int val) +{ + USED(regs); + siglongjmp(env, val); +} + +struct termios tinit; + +static void +termset(void) +{ + struct termios t; + + tcgetattr(0, &t); + tinit = t; + t.c_lflag &= ~(ICANON|ECHO|ISIG); + t.c_cc[VMIN] = 1; + t.c_cc[VTIME] = 0; + tcsetattr(0, TCSANOW, &t); +} + +static void +termrestore(void) +{ + tcsetattr(0, TCSANOW, &tinit); +} + +void +cleanexit(int x) +{ + USED(x); + + if(up->intwait) { + up->intwait = 0; + return; + } + + if(dflag == 0) + termrestore(); + + kill(0, SIGKILL); + exit(0); +} + +void +osreboot(char *file, char **argv) +{ + if(dflag == 0) + termrestore(); + execvp(file, argv); + panic("reboot failure"); +} + +int gidnobody= -1, uidnobody= -1; + +void +getnobody() +{ + struct passwd *pwd; + + if(pwd = getpwnam("nobody")) { + uidnobody = pwd->pw_uid; + gidnobody = pwd->pw_gid; + } +} + +void +libinit(char *imod) +{ + struct passwd *pw; + Proc *p; + void *tos; + char sys[64]; + + setsid(); + + gethostname(sys, sizeof(sys)); + kstrdup(&ossysname, sys); + getnobody(); + + if(dflag == 0) + termset(); + + setsigs(); + + p = newproc(); + p->kstack = stackalloc(p, &tos); + + pw = getpwuid(getuid()); + if(pw != nil) + kstrdup(&eve, pw->pw_name); + else + print("cannot getpwuid\n"); + + p->env->uid = getuid(); + p->env->gid = getgid(); + + executeonnewstack(tos, emuinit, imod); +} + +int +readkbd(void) +{ + int n; + char buf[1]; + + n = read(0, buf, sizeof(buf)); + if(n < 0) + print("keyboard close (n=%d, %s)\n", n, strerror(errno)); + if(n <= 0) + pexit("keyboard thread", 0); + + switch(buf[0]) { + case '\r': + buf[0] = '\n'; + break; + case DELETE: + cleanexit(0); + break; + } + return buf[0]; +} + +/* + * Return an abitrary millisecond clock time + */ +long +osmillisec(void) +{ + static long sec0 = 0, usec0; + struct timeval t; + + if(gettimeofday(&t,(struct timezone*)0)<0) + return 0; + if(sec0==0) { + sec0 = t.tv_sec; + usec0 = t.tv_usec; + } + return (t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000; +} + +int +limbosleep(ulong milsec) +{ + return osmillisleep(milsec); +} + +/* + * Return the time since the epoch in nanoseconds and microseconds + * The epoch is defined at 1 Jan 1970 + */ +vlong +osnsec(void) +{ + struct timeval t; + + gettimeofday(&t, nil); + return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000; +} + +vlong +osusectime(void) +{ + struct timeval t; + + gettimeofday(&t, nil); + return (vlong)t.tv_sec * 1000000 + t.tv_usec; +} + +int +osmillisleep(ulong milsec) +{ + struct timespec time; + + time.tv_sec = milsec / 1000; + time.tv_nsec = (milsec % 1000) * 1000000; + nanosleep(&time, 0); + return 0; +} + +void +osyield(void) +{ + sched_yield(); +} + +void +ospause(void) +{ + for(;;) + pause(); +} + +void +oslopri(void) +{ + setpriority(PRIO_PROCESS, 0, getpriority(PRIO_PROCESS,0)+4); +} + +static struct { + Lock l; + void *free; +} stacklist; + +static void +_stackfree(void *stack) +{ + *((void **)stack) = stacklist.free; + stacklist.free = stack; +} + +static void +stackfreeandexit(void *stack) +{ + lock(&stacklist.l); + _stackfree(stack); + unlockandexit(&stacklist.l.val); +} + +static void * +stackalloc(Proc *p, void **tos) +{ + void *rv; + lock(&stacklist.l); + if (stacklist.free == 0) { + int x; + /* + * obtain some more by using sbrk() + */ + void *more = sbrk(KSTACK * (NSTACKSPERALLOC + 1)); + if (more == 0) + panic("stackalloc: no more stacks"); + /* + * align to KSTACK + */ + more = (void *)((((unsigned long)more) + (KSTACK - 1)) & ~(KSTACK - 1)); + /* + * free all the new stacks onto the freelist + */ + for (x = 0; x < NSTACKSPERALLOC; x++) + _stackfree((char *)more + KSTACK * x); + } + rv = stacklist.free; + stacklist.free = *(void **)rv; + unlock(&stacklist.l); + *tos = rv + KSTACK - sizeof(void*); + *(Proc **)rv = p; + return rv; +} + +int +segflush(void *p, ulong n) +{ + return mprotect(p, n, PROT_EXEC|PROT_READ|PROT_WRITE); +} diff --git a/emu/OpenBSD/rfork_thread.S b/emu/OpenBSD/rfork_thread.S new file mode 100644 index 00000000..bb7cd010 --- /dev/null +++ b/emu/OpenBSD/rfork_thread.S @@ -0,0 +1,104 @@ +/*- + * Copyright (c) 2000 Peter Wemm <peter@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * 8 12 16 20 + * rfork_thread(flags, stack_addr, start_fnc, start_arg); + * + * flags: Flags to rfork system call. See rfork(2). + * stack_addr: Top of stack for thread. + * start_fnc: Address of thread function to call in child. + * start_arg: Argument to pass to the thread function in child. + */ + +ENTRY(rfork_thread) + pushl %ebp + movl %esp, %ebp + pushl %esi + + /* + * Push thread info onto the new thread's stack + */ + movl 12(%ebp), %esi # get stack addr + + subl $4, %esi + movl 20(%ebp), %eax # get start argument + movl %eax, (%esi) + + subl $4, %esi + movl 16(%ebp), %eax # get start thread address + movl %eax, (%esi) + + /* + * Prepare and execute the thread creation syscall + */ + pushl 8(%ebp) + pushl $0 + movl $SYS_rfork, %eax + int $0x80 + jb 2f + + /* + * Check to see if we are in the parent or child + */ + cmpl $0, %edx + jnz 1f + addl $8, %esp + popl %esi + movl %ebp, %esp + popl %ebp + ret + .p2align 2 + + /* + * If we are in the child (new thread), then + * set-up the call to the internal subroutine. If it + * returns, then call __exit. + */ +1: + movl %esi,%esp + popl %eax + call *%eax + addl $4, %esp + + /* + * Exit system call + */ + pushl %eax + pushl $0 + movl $SYS_threxit, %eax + int $0x80 + + /* + * Branch here if the thread creation fails: + */ +2: + addl $8, %esp + popl %esi + movl %ebp, %esp + popl %ebp + PIC_PROLOGUE + jmp PIC_PLT(_C_LABEL(__cerror)) diff --git a/emu/Plan9/asm-386.s b/emu/Plan9/asm-386.s new file mode 100644 index 00000000..b8193f71 --- /dev/null +++ b/emu/Plan9/asm-386.s @@ -0,0 +1,33 @@ + +TEXT tramp(SB),$0 + MOVL nsp+0(FP), BX /* new stack */ + MOVL fn+4(FP), CX /* func to exec */ + MOVL arg+8(FP),DX + + LEAL -8(BX), SP /* new stack */ + PUSHL DX + CALL *CX + POPL AX + + PUSHL $0 + CALL _exits(SB) + POPL AX + RET + +TEXT vstack(SB),$0 + MOVL arg+0(FP), AX + MOVL ustack(SB), SP + PUSHL AX + CALL exectramp(SB) + POPL AX /* dammit ken! */ + RET + +TEXT FPsave(SB), 1, $0 + MOVL fpu+0(FP), AX + FSTENV 0(AX) + RET + +TEXT FPrestore(SB), 1, $0 + MOVL fpu+0(FP), AX + FLDENV 0(AX) + RET diff --git a/emu/Plan9/asm-mips.s b/emu/Plan9/asm-mips.s new file mode 100644 index 00000000..b03f8209 --- /dev/null +++ b/emu/Plan9/asm-mips.s @@ -0,0 +1,24 @@ + + TEXT tramp(SB), 1, $0 + ADDU $-8, R1, R3 /* new stack */ + MOVW 4(FP), R2 /* func to exec */ + MOVW 8(FP), R1 /* arg to reg */ + MOVW R3, R29 /* new stack */ + JAL (R2) + MOVW R0, R1 + JMP _exits(SB) + + TEXT vstack(SB), 1, $0 /* Passes &targ through R1 */ + MOVW ustack(SB), R29 + JMP exectramp(SB) + RET + + TEXT FPsave(SB), 1, $0 + MOVW FCR31, R2 + MOVW R2, 0(R1) + RET + + TEXT FPrestore(SB), 1, $0 + MOVW 0(R1), R2 + MOVW R2, FCR31 + RET diff --git a/emu/Plan9/asm-power.s b/emu/Plan9/asm-power.s new file mode 100644 index 00000000..ff1c57f2 --- /dev/null +++ b/emu/Plan9/asm-power.s @@ -0,0 +1,28 @@ + TEXT tramp(SB), 1, $0 + ADD $-8, R3, R4 /* new stack */ + MOVW 4(FP), R5 /* func to exec */ + MOVW R5, LR + MOVW 8(FP), R3 /* arg to reg */ + MOVW R4, R1 /* new stack */ + BL (LR) + MOVW R0, R3 + MOVW $_exits(SB), R4 + MOVW R4, LR + BR (LR) + + TEXT vstack(SB), 1, $0 /* Passes &targ through R3 */ + MOVW ustack(SB), R1 + MOVW $exectramp(SB), R4 + MOVW R4, CTR + BR (CTR) + RETURN + + TEXT FPsave(SB), 1, $0 + MOVFL FPSCR, F0 + FMOVD F0, 0(R3) + RETURN + + TEXT FPrestore(SB), 1, $0 + FMOVD 0(R3), F0 + MOVFL F0, FPSCR + RETURN diff --git a/emu/Plan9/asm-sparc.s b/emu/Plan9/asm-sparc.s new file mode 100644 index 00000000..f68fd02d --- /dev/null +++ b/emu/Plan9/asm-sparc.s @@ -0,0 +1,22 @@ + TEXT tramp(SB), 1, $0 + ADD $-8, R7, R3 /* new stack */ + MOVW 4(FP), R4 /* func to exec */ + MOVW 8(FP), R7 /* arg to reg */ + MOVW R3, R1 /* new stack */ + JMPL (R4) + MOVW R0, R7 + JMPL _exits(SB) /* Leaks the stack in R29 */ + + TEXT vstack(SB), 1, $0 /* Passes &targ through R7 */ + MOVW ustack(SB), R1 + MOVW $exectramp(SB), R3 + JMP (R3) + RETURN + + TEXT FPsave(SB), 1, $0 + MOVW FSR, 0(R7) + RETURN + + TEXT FPrestore(SB), 1, $0 + MOVW 0(R7), FSR + RETURN diff --git a/emu/Plan9/cmd.c b/emu/Plan9/cmd.c new file mode 100644 index 00000000..8d311f2a --- /dev/null +++ b/emu/Plan9/cmd.c @@ -0,0 +1,189 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + +extern void vstack(void*); + +/* + * all this is for the benefit of devcmd. + * i hope it's grateful. + */ + +typedef struct Targ Targ; +struct Targ +{ + int fd[3]; /* standard input, output and error */ + int wfd; + int* spin; + char** args; + char* dir; + int pid; + int nice; +}; + +/* + * called by vstack once it has moved to + * the unshared stack in the new process. + */ +void +exectramp(Targ *t) +{ + int *fd, i, nfd; + char filename[128], err[ERRMAX], status[2*ERRMAX]; + + t->pid = getpid(); + *t->spin = 0; /* allow parent to proceed: can't just rendezvous: see below */ + fd = t->fd; + + snprint(filename, sizeof(filename), "#d/%d", t->wfd); + t->wfd = open(filename, OWRITE|OCEXEC); + /* if it failed, we'll manage */ + + nfd = MAXNFD; /* TO DO: should read from /fd */ + for(i = 0; i < nfd; i++) + if(i != fd[0] && i != fd[1] && i != fd[2] && i != t->wfd) + close(i); + + if(fd[0] != 0){ + dup(fd[0], 0); + close(fd[0]); + } + if(fd[1] != 1){ + dup(fd[1], 1); + close(fd[1]); + } + if(fd[2] != 2){ + dup(fd[2], 2); + close(fd[2]); + } + + if(t->dir != nil && chdir(t->dir) < 0){ + if(t->wfd > 0) + fprint(t->wfd, "chdir: %s: %r", t->dir); + _exits("bad dir"); + } + if(t->nice) + oslopri(); + + exec(t->args[0], t->args); + err[0] = 0; + errstr(err, sizeof(err)); + if(t->args[0][0] != '/' && t->args[0][0] != '#' && + strncmp(t->args[0], "../", 3) != 0 && strncmp(t->args[0], "./", 2) != 0 && + strlen(t->args[0])+5 < sizeof(filename)){ + snprint(filename, sizeof(filename), "/bin/%s", t->args[0]); + exec(filename, t->args); + errstr(err, sizeof(err)); + } + snprint(status, sizeof(status), "%s: can't exec: %s", t->args[0], err); + if(t->wfd > 0) + write(t->wfd, status, strlen(status)); + _exits(status); +} + +void* +oscmd(char **args, int nice, char *dir, int *fd) +{ + Targ *t; + int spin, *spinptr, fd0[2], fd1[2], fd2[2], wfd[2], n; + Dir *d; + + up->genbuf[0] = 0; + t = mallocz(sizeof(*t), 1); + if(t == nil) + return nil; + t->args = args; + t->dir = dir; + t->nice = nice; + fd0[0] = fd0[1] = -1; + fd1[0] = fd1[1] = -1; + fd2[0] = fd2[1] = -1; + wfd[0] = wfd[1] = -1; + if(dir != nil){ + d = dirstat(dir); + if(d == nil) + goto Error; + free(d); + } + if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0) + goto Error; + + spinptr = &spin; + spin = 1; + + t->fd[0] = fd0[0]; + t->fd[1] = fd1[1]; + t->fd[2] = fd2[1]; + t->wfd = wfd[1]; + t->spin = spinptr; + switch(rfork(RFPROC|RFMEM|RFREND|RFNOTEG|RFFDG|RFNAMEG|RFENVG)) { + case -1: + goto Error; + case 0: + /* if child returns first from rfork, its call to vstack replaces ... */ + vstack(t); + /* ... parent's return address from rfork and parent returns here */ + default: + /* if parent returns first from rfork, it comes here */ + /* can't call anything: on shared stack until child releases spin in exectramp */ + while(*spinptr) + ; + break; + } + + close(fd0[0]); + close(fd1[1]); + close(fd2[1]); + close(wfd[1]); + + n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1); + close(wfd[0]); + if(n > 0){ + close(fd0[1]); + close(fd1[0]); + close(fd2[0]); + up->genbuf[n] = 0; + errstr(up->genbuf, sizeof(up->genbuf)); + free(t); + return nil; + } + + fd[0] = fd0[1]; + fd[1] = fd1[0]; + fd[2] = fd2[0]; + return t; + +Error: + errstr(up->genbuf, sizeof(up->genbuf)); /* save the message before close */ + close(fd0[0]); + close(fd0[1]); + close(fd1[0]); + close(fd1[1]); + close(fd2[0]); + close(fd2[1]); + close(wfd[0]); + close(wfd[1]); + free(t); + errstr(up->genbuf, sizeof(up->genbuf)); + return nil; +} + +int +oscmdkill(void *a) +{ + Targ *t = a; + + return postnote(PNGROUP, t->pid, "kill"); +} + +int +oscmdwait(void*, char *buf, int n) +{ + return await(buf, n); +} + +void +oscmdfree(void *a) +{ + free(a); +} diff --git a/emu/Plan9/devfs.c b/emu/Plan9/devfs.c new file mode 100644 index 00000000..d0db522d --- /dev/null +++ b/emu/Plan9/devfs.c @@ -0,0 +1,365 @@ +/* + * Plan 9 file system interface + */ +#include "dat.h" +#include "fns.h" +#include "error.h" + +typedef struct Fsinfo Fsinfo; +struct Fsinfo +{ + int fd; + QLock; /* serialise access to offset */ + ulong offset; /* offset used only for directory reads */ + Cname* name; /* Plan 9's name for file */ + Qid rootqid; /* Plan 9's qid for Inferno's root */ + char* root; /* prefix to strip from all names in diagnostics */ +}; +#define FS(c) ((Fsinfo*)((c)->aux)) + +char rootdir[MAXROOT] = ROOT; + +static void +fserr(Fsinfo *f) +{ + int n; + char *p; + + oserrstr(up->env->errstr, ERRMAX); + if(f != nil && *up->env->errstr == '\'' && (n = strlen(f->root)) > 1){ + /* don't reveal full names */ + if(strncmp(up->env->errstr+1, f->root, n-1) == 0){ + p = up->env->errstr+1+n; + memmove(up->env->errstr+1, p, strlen(p)+1); + } + } + error(up->env->errstr); +} + +static void +fsfree(Chan *c) +{ + cnameclose(FS(c)->name); + free(c->aux); +} + +Chan* +fsattach(char *spec) +{ + Chan *c; + Dir *d; + char *root; + Qid rootqid; + static int devno; + static Lock l; + + if(!emptystr(spec)){ + if(strcmp(spec, "*") != 0) + error(Ebadspec); + root = "/"; + }else + root = rootdir; + + d = dirstat(root); + if(d == nil) + fserr(nil); + rootqid = d->qid; + free(d); + + c = devattach('U', spec); + lock(&l); + c->dev = devno++; + c->qid = rootqid; + unlock(&l); + c->aux = smalloc(sizeof(Fsinfo)); + FS(c)->name = newcname(root); + FS(c)->rootqid = rootqid; + FS(c)->fd = -1; + FS(c)->root = root; + + return c; +} + +Walkqid* +fswalk(Chan *c, Chan *nc, char **name, int nname) +{ + int j, alloc; + Walkqid *wq; + Dir *dir; + char *n; + Cname *current, *next; + Qid rootqid; + + if(nname > 0) + isdir(c); /* do we need this? */ + + alloc = 0; + current = nil; + wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid)); + if(waserror()){ + if(alloc && wq->clone!=nil) + cclose(wq->clone); + cnameclose(current); + free(wq); + return nil; + } + if(nc == nil){ + nc = devclone(c); + nc->type = 0; + alloc = 1; + } + wq->clone = nc; + + rootqid = FS(c)->rootqid; + current = FS(c)->name; + if(current != nil) + incref(¤t->r); + for(j=0; j<nname; j++){ + if(!(nc->qid.type&QTDIR)){ + if(j==0) + error(Enotdir); + break; + } + n = name[j]; + if(strcmp(n, ".") != 0 && !(isdotdot(n) && nc->qid.path == rootqid.path)){ /* TO DO: underlying qids aliased */ + //print("** ufs walk '%s' -> %s\n", current->s, n); + next = current; + incref(&next->r); + next = addelem(current, n); + dir = dirstat(next->s); + if(dir == nil){ + cnameclose(next); + if(j == 0) + error(Enonexist); + strcpy(up->env->errstr, Enonexist); + break; + } + nc->qid = dir->qid; + free(dir); + cnameclose(current); + current = next; + } + wq->qid[wq->nqid++] = nc->qid; + } +// print("** ufs walk '%s'\n", current->s); + + poperror(); + if(wq->nqid < nname){ + cnameclose(current); + if(alloc) + cclose(wq->clone); + wq->clone = nil; + }else if(wq->clone){ + /* now attach to our device */ + nc->aux = smalloc(sizeof(Fsinfo)); + nc->type = c->type; + FS(nc)->rootqid = FS(c)->rootqid; + FS(nc)->name = current; + FS(nc)->fd = -1; + FS(nc)->root = FS(c)->root; + }else + panic("fswalk: can't happen"); + return wq; +} + +int +fsstat(Chan *c, uchar *dp, int n) +{ + if(FS(c)->fd >= 0) + n = fstat(FS(c)->fd, dp, n); + else + n = stat(FS(c)->name->s, dp, n); + if(n < 0) + fserr(FS(c)); + /* TO DO: change name to / if rootqid */ + return n; +} + +Chan* +fsopen(Chan *c, int mode) +{ + osenter(); + FS(c)->fd = open(FS(c)->name->s, mode); + osleave(); + if(FS(c)->fd < 0) + fserr(FS(c)); + c->mode = openmode(mode); + c->offset = 0; + FS(c)->offset = 0; + c->flag |= COPEN; + return c; +} + +void +fscreate(Chan *c, char *name, int mode, ulong perm) +{ + Dir *d; + Cname *n; + + if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) + error(Efilename); + n = addelem(newcname(FS(c)->name->s), name); + osenter(); + FS(c)->fd = create(n->s, mode, perm); + osleave(); + if(FS(c)->fd < 0) { + cnameclose(n); + fserr(FS(c)); + } + d = dirfstat(FS(c)->fd); + if(d == nil) { + cnameclose(n); + close(FS(c)->fd); + FS(c)->fd = -1; + fserr(FS(c)); + } + c->qid = d->qid; + free(d); + + cnameclose(FS(c)->name); + FS(c)->name = n; + + c->mode = openmode(mode); + c->offset = 0; + FS(c)->offset = 0; + c->flag |= COPEN; +} + +void +fsclose(Chan *c) +{ + if(c->flag & COPEN){ + osenter(); + close(FS(c)->fd); + osleave(); + } + /* don't need to check for CRCLOSE, because Plan 9 itself implements ORCLOSE */ + fsfree(c); +} + +static long +fsdirread(Chan *c, void *va, long count, vlong offset) +{ + long n, r; + static char slop[16384]; + + if(FS(c)->offset != offset){ + seek(FS(c)->fd, 0, 0); + for(n=0; n<offset;) { + r = offset - n; + if(r > sizeof(slop)) + r = sizeof(slop); + osenter(); + r = read(FS(c)->fd, slop, r); + osleave(); + if(r <= 0){ + FS(c)->offset = n; + return 0; + } + n += r; + } + FS(c)->offset = offset; + } + osenter(); + r = read(FS(c)->fd, va, count); + osleave(); + if(r < 0) + return r; + FS(c)->offset = offset+r; + return r; +} + +long +fsread(Chan *c, void *va, long n, vlong offset) +{ + int r; + + if(c->qid.type & QTDIR){ /* need to maintain offset only for directories */ + qlock(FS(c)); + if(waserror()){ + qunlock(FS(c)); + nexterror(); + } + r = fsdirread(c, va, n, offset); + poperror(); + qunlock(FS(c)); + }else{ + osenter(); + r = pread(FS(c)->fd, va, n, offset); + osleave(); + } + if(r < 0) + fserr(FS(c)); + return r; +} + +long +fswrite(Chan *c, void *va, long n, vlong offset) +{ + int r; + + osenter(); + r = pwrite(FS(c)->fd, va, n, offset); + osleave(); + if(r < 0) + fserr(FS(c)); + return r; +} + +void +fsremove(Chan *c) +{ + int r; + + if(waserror()){ + fsfree(c); + nexterror(); + } + osenter(); + r = remove(FS(c)->name->s); + osleave(); + if(r < 0) + fserr(FS(c)); + poperror(); + fsfree(c); +} + +int +fswstat(Chan *c, uchar *dp, int n) +{ + osenter(); + if(FS(c)->fd >= 0) + n = fwstat(FS(c)->fd, dp, n); + else + n = wstat(FS(c)->name->s, dp, n); + osleave(); + if(n < 0) + fserr(FS(c)); + return n; +} + +void +setid(char *name, int owner) +{ + if(!owner || iseve()) + kstrdup(&up->env->user, name); +} + +Dev fsdevtab = { + 'U', + "fs", + + devinit, + fsattach, + fswalk, + fsstat, + fsopen, + fscreate, + fsclose, + fsread, + devbread, + fswrite, + devbwrite, + fsremove, + fswstat +}; diff --git a/emu/Plan9/devsrv9.c b/emu/Plan9/devsrv9.c new file mode 100644 index 00000000..7ea26dfb --- /dev/null +++ b/emu/Plan9/devsrv9.c @@ -0,0 +1,395 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + +typedef struct Srv Srv; +struct Srv +{ + Ref; + int fd; /* fd for opened /srv or /srv/X, or -1 */ + int sfd; /* fd for created /srv entry or -1 */ + uvlong path; + Srv *next; +}; + +static QLock srv9lk; +static Srv *srv9; +static Srv *srvroot; + +static char* +srvname(Chan *c) +{ + char *p; + + p = strrchr(c->name->s, '/'); + if(p == nil) + return ""; + return p+1; +} + +static Srv* +srvget(uvlong path) +{ + Srv *sv; + + qlock(&srv9lk); + for(sv = srv9; sv != nil; sv = sv->next) + if(sv->path == path){ + incref(sv); + qunlock(&srv9lk); + return sv; + } + sv = smalloc(sizeof(*sv)); + sv->path = path; + sv->fd = -1; + sv->sfd = -1; + sv->ref = 1; + sv->next = srv9; + srv9 = sv; + qunlock(&srv9lk); + return sv; +} + +static void +srvput(Srv *sv) +{ + Srv **l; + int fd, sfd; + + if(sv != nil && decref(sv) == 0){ + qlock(&srv9lk); + for(l = &srv9; *l != nil; l = &(*l)->next) + if(*l == sv){ + *l = sv->next; + break; + } + qunlock(&srv9lk); + fd = sv->fd; + sfd = sv->sfd; + free(sv); + if(sfd >= 0){ + osenter(); + close(sfd); + osleave(); + } + if(fd >= 0){ + osenter(); + close(fd); + osleave(); + } + } +} + +static void +srv9init(void) +{ + Srv *sv; + + sv = mallocz(sizeof(*srvroot), 1); + sv->path = 0; + sv->fd = -1; + sv->ref = 1; /* subsequently never reaches zero */ + srvroot = srv9 = sv; +} + +static Chan* +srv9attach(char *spec) +{ + Chan *c; + + if(*spec) + error(Ebadspec); + c = devattach(L'₪', spec); + if(c != nil){ + incref(srvroot); + c->aux = srvroot; + } + return c; +} + +static Walkqid* +srv9walk(Chan *c, Chan *nc, char **name, int nname) +{ + int j, alloc; + Walkqid *wq; + char *n; + Dir *d; + + if(nname > 0) + isdir(c); + + alloc = 0; + wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid)); + if(waserror()){ + if(alloc) + cclose(wq->clone); + free(wq); + return nil; + } + if(nc == nil){ + nc = devclone(c); + nc->type = 0; /* device doesn't know about this channel yet */ + alloc = 1; + } + wq->clone = nc; + + for(j=0; j<nname; j++){ + if(!(nc->qid.type&QTDIR)){ + if(j==0) + error(Enotdir); + break; + } + n = name[j]; + if(strcmp(n, ".") != 0 && strcmp(n, "..") != 0){ + snprint(up->genbuf, sizeof(up->genbuf), "/srv/%s", n); + d = dirstat(up->genbuf); + if(d == nil){ + if(j == 0) + error(Enonexist); + kstrcpy(up->env->errstr, Enonexist, ERRMAX); + break; + } + nc->qid = d->qid; + free(d); + } + wq->qid[wq->nqid++] = nc->qid; + } + poperror(); + if(wq->nqid < nname){ + if(alloc) + cclose(wq->clone); + wq->clone = nil; + }else{ + /* attach cloned channel to device */ + wq->clone->type = c->type; + if(wq->clone != c) + nc->aux = srvget(nc->qid.path); + } + return wq; +} + +static int +srv9stat(Chan *c, uchar *db, int n) +{ + Srv *sv; + Dir d; + + if(c->qid.type & QTDIR){ + devdir(c, c->qid, "#₪", 0, eve, 0775, &d); + n = convD2M(&d, db, n); + if(n == 0) + error(Eshortstat); + return n; + } + sv = c->aux; + if(sv->fd >= 0){ + osenter(); + n = fstat(sv->fd, db, n); + osleave(); + }else{ + osenter(); + n = stat(srvname(c), db, n); + osleave(); + } + return n; +} + +static Chan* +srv9open(Chan *c, int omode) +{ + Srv *sv; + char *args[10]; + int fd[2], i, ifd, is9p; + Dir *d; + + sv = c->aux; + if(c->qid.type == QTDIR){ + osenter(); + sv->fd = open("/srv", omode); + osleave(); + if(sv->fd < 0) + oserror(); + c->mode = omode; + c->flag |= COPEN; + c->offset = 0; + return c; + } + + if(omode&OTRUNC || openmode(omode) != ORDWR) + error(Eperm); + if(sv->fd < 0){ + snprint(up->genbuf, sizeof(up->genbuf), "/srv/%s", srvname(c)); + + /* check permission */ + osenter(); + ifd = open(up->genbuf, omode); + osleave(); + if(ifd < 0) + oserror(); + osenter(); + d = dirfstat(ifd); + is9p = d != nil && d->qid.type & QTMOUNT; + free(d); + osleave(); + + if(is9p){ + close(ifd); + + /* spawn exportfs */ + args[0] = "exportfs"; + args[1] = "-S"; + args[2] = up->genbuf; + args[3] = nil; + if(pipe(fd) < 0) + oserror(); + /* TO DO: without RFMEM there's a copy made of each page touched by any kproc until the exec */ + switch(rfork(RFPROC|RFNOWAIT|RFREND|RFFDG|RFNAMEG|RFENVG)){ /* no sharing except NOTEG */ + case -1: + oserrstr(up->genbuf, sizeof(up->genbuf)); + close(fd[0]); + close(fd[1]); + error(up->genbuf); + case 0: + for(i=3; i<MAXNFD; i++) + if(i != fd[1]) + close(i); + dup(fd[1], 0); + if(fd[0] != 0) + close(fd[0]); + dup(0, 1); + exec("/bin/exportfs", args); + exits("exportfs failed"); + default: + sv->fd = fd[0]; + close(fd[1]); + break; + } + }else + sv->fd = ifd; + } + + c->mode = ORDWR; + c->offset = 0; + c->flag |= COPEN; + return c; +} + +static void +srv9close(Chan *c) +{ + srvput(c->aux); +} + +static long +srv9read(Chan *c, void *va, long n, vlong off) +{ + Srv *sv; + + sv = c->aux; + osenter(); + n = pread(sv->fd, va, n, off); + osleave(); + if(n < 0) + oserror(); + return n; +} + +static long +srv9write(Chan *c, void *va, long n, vlong off) +{ + Srv *sv; + + sv = c->aux; + osenter(); + n = pwrite(sv->fd, va, n, off); + osleave(); + if(n == 0) + error(Ehungup); + if(n < 0) + oserror(); + return n; +} + +static void +srv9create(Chan *c, char *name, int omode, ulong perm) +{ + Srv *sv; + int sfd, fd[2]; + vlong path; + Dir *d; + + if(openmode(omode) != ORDWR) + error(Eperm); + + if(pipe(fd) < 0) + oserror(); + if(waserror()){ + close(fd[0]); + close(fd[1]); + nexterror(); + } + + snprint(up->genbuf, sizeof(up->genbuf), "/srv/%s", name); + osenter(); + sfd = create(up->genbuf, OWRITE|ORCLOSE, perm); + osleave(); + if(sfd < 0) + oserror(); + if(waserror()){ + close(sfd); + nexterror(); + } + osenter(); + if(fprint(sfd, "%d", fd[1]) < 0){ + osleave(); + oserror(); + } + d = dirfstat(sfd); + osleave(); + if(d != nil){ + path = d->qid.path; + free(d); + }else + oserror(); + + poperror(); + poperror(); + close(fd[1]); + + if(waserror()){ + close(sfd); + close(fd[0]); + nexterror(); + } + sv = srvget(path); + sv->fd = fd[0]; + sv->sfd = sfd; + poperror(); + + srvput((Srv*)c->aux); + c->qid.type = QTFILE; + c->qid.path = path; + c->aux = sv; + c->flag |= COPEN; + c->mode = ORDWR; + c->offset = 0; +} + +Dev srv9devtab = { + L'₪', + "srv9", + + srv9init, + srv9attach, + srv9walk, + srv9stat, + srv9open, + srv9create, /* TO DO */ + srv9close, + srv9read, + devbread, + srv9write, + devbwrite, + devremove, /* TO DO */ + devwstat, /* TO DO */ +}; diff --git a/emu/Plan9/emu b/emu/Plan9/emu new file mode 100644 index 00000000..0a6c0f53 --- /dev/null +++ b/emu/Plan9/emu @@ -0,0 +1,109 @@ +dev + root + cons + env + mnt + pipe + prog + prof + srv + dup + ssl + cap + fs + cmd cmd + indir + sign + + draw win + pointer + + dynld + mem + srv9 + +# ip and eia are simply bound in from Plan 9 + +lib + interp + tk + freetype + math + draw + + memlayer + memdraw + keyring + sec + mp + dynld + 9 + +link + +mod + sys + draw + + tk + math +# srv not used on Plan 9 + keyring + loader + freetype + +port + alloc + cache + chan + dev + dial + dis + discall + env + error + errstr + exception + exportfs + exptab + inferno + latin1 + main + parse + pgrp + print + proc + qio + random + sysfile + uqid + +code + +init + emuinit + +root + /chan / + /dev / + /fd / + /prog / + /net / + /net.alt / + /nvfs / + /env / + /root / + /srv / +# /tmp / +# /dis +# /env +# /n +# /net +# /nvfs / +# /prog +# /icons +# /osinit.dis +# /dis/emuinit.dis +# /dis/lib/auth.dis +# /dis/lib/ssl.dis +# /n/local / diff --git a/emu/Plan9/emusig b/emu/Plan9/emusig new file mode 100644 index 00000000..1975a4cc --- /dev/null +++ b/emu/Plan9/emusig @@ -0,0 +1,95 @@ +dev + root + cons + env + mnt + pipe + prog + prof + srv + ssl + cap + fs + cmd cmd + indir + sign + + draw win + +# ip and eia are simply bound in from Plan 9 + +lib + interp + tk + freetype + math + draw + memlayer + memdraw + keyring + crypt + 9 + +link + +mod + sys + draw + tk + math +# srv not used on Plan 9 + keyring + loader + freetype + +port + alloc + cache + chan + dev + dial + dis + discall + env + error + errstr + exception + exportfs + inferno + latin1 + main + parse + pgrp + print + proc + qio + sysfile + uqid + +code + +init + emuinit + +root + /dev / + /prog / + /net / + /net.alt / + /chan / + /nvfs / + /env / +# /chan +# /dev +# /dis +# /env +# /n +# /net +# /nvfs / +# /prog +# /icons +# /osinit.dis +# /dis/emuinit.dis +# /dis/lib/auth.dis +# /dis/lib/ssl.dis +# /n/local / diff --git a/emu/Plan9/mkfile b/emu/Plan9/mkfile new file mode 100644 index 00000000..a7266830 --- /dev/null +++ b/emu/Plan9/mkfile @@ -0,0 +1,45 @@ +SYSTARG=Plan9 +OBJTYPE=$objtype +<../../mkconfig + +#Configurable parameters + +CONF=emu #default configuration +CONFLIST=emu +CLEANCONFLIST= + +INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed + +#end configurable parameters + +<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system + +<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS + +OBJ=\ + asm-$OBJTYPE.$O\ + os.$O\ + $CONF.root.$O\ + $DEVS\ + $PORT\ + + +HFILES=\ + +CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS +KERNDATE=`{$NDATE} + +default:V: $O.$CONF + +<../port/portmkfile + +$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBFILES + $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c + $LD -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS + +safeinstall:V: $O.$CONF + mv $INSTALLDIR/$CONF $INSTALLDIR/$CONF.`{date -n} + cp $O.$CONF $INSTALLDIR/$CONF + +install:V: $O.$CONF + cp $O.$CONF $INSTALLDIR/$CONF diff --git a/emu/Plan9/os.c b/emu/Plan9/os.c new file mode 100644 index 00000000..d681ec67 --- /dev/null +++ b/emu/Plan9/os.c @@ -0,0 +1,422 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + +enum +{ + KSTACK = 16*1024, + DELETE = 0x7F, +}; + +Proc **Xup; + +extern void killrefresh(void); +extern void tramp(char*, void (*)(void*), void*); + +extern int usenewwin; + +int *ustack; /* address on unshared stack: see vstack in asm*.s */ +extern int dflag; +char *hosttype = "Plan9"; +char *cputype; + +void +osblock(void) +{ + rendezvous(up, nil); +} + +void +osready(Proc *p) +{ + rendezvous(p, nil); +} + +void +pexit(char *msg, int) +{ + Osenv *e; + + USED(msg); + + lock(&procs.l); + if(up->prev) + up->prev->next = up->next; + else + procs.head = up->next; + + if(up->next) + up->next->prev = up->prev; + else + procs.tail = up->prev; + unlock(&procs.l); + +/* print("pexit: %s: %s\n", up->text, msg); /**/ + e = up->env; + if(e != nil) { + closefgrp(e->fgrp); + closepgrp(e->pgrp); + closeegrp(e->egrp); + closesigs(e->sigs); + } + free(e->user); + free(up->prog); + up->prog = nil; + up->type = Moribund; + longjmp(up->privstack, 1); +} + +int +kproc(char *name, void (*func)(void*), void *arg, int flags) +{ + int pid; + Proc *p; + Pgrp *pg; + Fgrp *fg; + Egrp *eg; + + p = newproc(); + if(p == nil) + panic("kproc: no memory"); + p->kstack = mallocz(KSTACK, 0); + if(p->kstack == nil) + panic("kproc: no memory"); + + if(flags & KPDUPPG) { + pg = up->env->pgrp; + incref(&pg->r); + p->env->pgrp = pg; + } + if(flags & KPDUPFDG) { + fg = up->env->fgrp; + incref(&fg->r); + p->env->fgrp = fg; + } + if(flags & KPDUPENVG) { + eg = up->env->egrp; + incref(&eg->r); + p->env->egrp = eg; + } + + p->env->uid = up->env->uid; + p->env->gid = up->env->gid; + kstrdup(&p->env->user, up->env->user); + + strcpy(p->text, name); + + p->func = func; + p->arg = arg; + + lock(&procs.l); + if(procs.tail != nil) { + p->prev = procs.tail; + procs.tail->next = p; + } + else { + procs.head = p; + p->prev = nil; + } + procs.tail = p; + unlock(&procs.l); + + /* + * switch back to the unshared stack to do the fork + * only the parent returns from kproc + */ + up->kid = p; + up->kidsp = p->kstack; + pid = setjmp(up->sharestack); + if(pid == 0) + longjmp(up->privstack, 1); + return pid; +} + +void +traphandler(void *reg, char *msg) +{ + int intwait; + + intwait = up->intwait; + up->intwait = 0; + /* Ignore pipe writes from devcmd */ + if(strstr(msg, "write on closed pipe") != nil) + noted(NCONT); + + if(sflag) { + if(intwait && strcmp(msg, Eintr) == 0) + noted(NCONT); + else + noted(NDFLT); + } + if(intwait == 0) + disfault(reg, msg); + noted(NCONT); +} + +int +readfile(char *path, char *buf, int n) +{ + int fd; + + fd = open(path, OREAD); + if(fd >= 0) { + n = read(fd, buf, n-1); + if(n > 0) /* both calls to readfile() have a ``default'' */ + buf[n] = '\0'; + close(fd); + return n; + } + return 0; +} + +static void +dobinds(void) +{ + char dir[MAXROOT+9]; + + snprint(dir, sizeof(dir), "%s/net", rootdir); + bind("/net", dir, MREPL); + + snprint(dir, sizeof(dir), "%s/net.alt", rootdir); + bind("/net.alt", dir, MREPL); + + snprint(dir, sizeof(dir), "%s/dev", rootdir); + bind("#t", dir, MAFTER); + bind("#A", dir, MAFTER); +} + +void +libinit(char *imod) +{ + char *sp; + Proc *xup, *p; + int fd, n, pid; + char nbuf[64]; + + xup = nil; + Xup = &xup; + + /* + * setup personality + */ + if(readfile("/dev/user", nbuf, sizeof nbuf)) + kstrdup(&eve, nbuf); + if(readfile("/dev/sysname", nbuf, sizeof nbuf)) + kstrdup(&ossysname, nbuf); + if(readfile("/env/cputype", nbuf, sizeof nbuf)) + kstrdup(&cputype, nbuf); + + /* + * guess at a safe stack for vstack + */ + ustack = &fd; + + rfork(RFNAMEG|RFREND); + + if(!dflag){ + fd = open("/dev/consctl", OWRITE); + if(fd < 0) + fprint(2, "libinit: open /dev/consctl: %r\n"); + n = write(fd, "rawon", 5); + if(n != 5) + fprint(2, "keyboard rawon (n=%d, %r)\n", n); + } + + osmillisec(); /* set the epoch */ + dobinds(); + + notify(traphandler); + + /* + * dummy up a up and stack so the first proc + * calls emuinit after setting up his private jmp_buf + */ + p = newproc(); + p->kstack = mallocz(KSTACK, 0); + if(p == nil || p->kstack == nil) + panic("libinit: no memory"); + sp = p->kstack; + p->func = emuinit; + p->arg = imod; + + /* + * set up a stack for forking kids on separate stacks. + * longjmp back here from kproc. + */ + while(setjmp(p->privstack)){ + if(up->type == Moribund){ + free(up->kstack); + free(up); + _exits(""); + } + p = up->kid; + sp = up->kidsp; + up->kid = nil; + switch(pid = rfork(RFPROC|RFMEM|RFNOWAIT)){ + case 0: + /* + * send the kid around the loop to set up his private jmp_buf + */ + break; + default: + /* + * parent just returns to his shared stack in kproc + */ + longjmp(up->sharestack, pid); + panic("longjmp failed"); + } + } + + /* + * you get here only once per Proc + * go to the shared memory stack + */ + up = p; + up->pid = up->sigid = getpid(); + tramp(sp+KSTACK, up->func, up->arg); + panic("tramp returned"); +} + +void +oshostintr(Proc *p) +{ + postnote(PNPROC, p->sigid, Eintr); +} + +void +oslongjmp(void *regs, osjmpbuf env, int val) +{ + if(regs != nil) + notejmp(regs, env, val); + else + longjmp(env, val); +} + +void +osreboot(char*, char**) +{ +} + +void +cleanexit(int x) +{ + USED(x); + killrefresh(); + postnote(PNGROUP, getpid(), "interrupt"); + exits("interrupt"); +} + +int +readkbd(void) +{ + int n; + char buf[1]; + + n = read(0, buf, sizeof(buf)); + if(n < 0) + fprint(2, "emu: keyboard read error: %r\n"); + if(n <= 0) + pexit("keyboard", 0); + switch(buf[0]) { + case DELETE: + cleanexit(0); + case '\r': + buf[0] = '\n'; + } + return buf[0]; +} + +static vlong +b2v(uchar *p) +{ + int i; + vlong v; + + v = 0; + for(i=0; i<sizeof(uvlong); i++) + v = (v<<8)|p[i]; + return v; +} + +vlong +nsec(void) +{ + int n; + static int nsecfd = -1; + uchar buf[sizeof(uvlong)]; + + if(nsecfd < 0){ + nsecfd = open("/dev/bintime", OREAD|OCEXEC); /* never closed */ + if(nsecfd<0){ + fprint(2,"can't open /dev/bintime: %r\n"); + return 0; + } + } + n = read(nsecfd, buf, sizeof(buf)); + if(n!=sizeof(buf)) { + fprint(2,"read err on /dev/bintime: %r\n"); + return 0; + } + return b2v(buf); +} + +long +osmillisec(void) +{ + static vlong nsec0 = 0; + + if(nsec0 == 0){ + nsec0 = nsec(); + return 0; + } + return (nsec()-nsec0)/1000000; +} + +/* + * Return the time since the epoch in microseconds + * The epoch is defined at 1 Jan 1970 + */ +vlong +osusectime(void) +{ + return nsec()/1000; +} + +int +osmillisleep(ulong milsec) +{ + sleep(milsec); + return 0; +} + +int +limbosleep(ulong milsec) +{ + return osmillisleep(milsec); +} + +void +osyield(void) +{ + sleep(0); +} + +void +ospause(void) +{ + for(;;) + sleep(1000000); +} + +void +oslopri(void) +{ + int fd; + char buf[32]; + + snprint(buf, sizeof(buf), "/proc/%d/ctl", getpid()); + if((fd = open(buf, OWRITE)) >= 0){ + fprint(fd, "pri 8"); + close(fd); + } +} diff --git a/emu/Plan9/win.c b/emu/Plan9/win.c new file mode 100644 index 00000000..2f280853 --- /dev/null +++ b/emu/Plan9/win.c @@ -0,0 +1,564 @@ +#include "dat.h" +#include "fns.h" +#include "kernel.h" +#include "error.h" + +#include <draw.h> +#include <memdraw.h> +#include <cursor.h> +#include "keyboard.h" + +enum +{ + Margin = 4, + Lsize = 100, +}; + +extern Memimage *screenimage; + +static ulong* attachwindow(Rectangle*, ulong*, int*, int*); + +static void plan9readmouse(void*); +static void plan9readkeybd(void*); +static int mapspecials(char *s1, char *s2, int *n); + +int usenewwin = 1; +int kbdiscons; +static int truedepth; + +static int datafd; +static int ctlfd; +static int mousefd = -1; +static int keybdfd; +static int mousepid = -1; +static int keybdpid = -1; +static int cursfd; +static char winname[64]; + +/* Following updated by attachwindow() asynchronously */ +static QLock ql; +static Rectangle tiler; +static ulong* data; +static uchar* loadbuf; +static int cursfd; +static int imageid; +static Rectangle imager; +static uchar *chunk; +static int chunksize; +static int dispbufsize; + +#define NINFO 12*12 +#define HDR 21 + + +void +killrefresh(void) +{ + if(mousepid < 0) + return; + close(mousefd); + close(ctlfd); + close(datafd); + postnote(PNPROC, mousepid, Eintr); + postnote(PNPROC, keybdpid, Eintr); +} + +uchar* +attachscreen(Rectangle *r, ulong *chan, int *d, int *width, int *softscreen) +{ + int fd; + char *p, buf[128], info[NINFO+1]; + + if(usenewwin){ + p = getenv("wsys"); + if(p == nil) + return nil; + + fd = open(p, ORDWR); + if(fd < 0) { + fprint(2, "attachscreen: can't open window manager: %r\n"); + return nil; + } + sprint(buf, "new -dx %d -dy %d", Xsize+2*Margin, Ysize+2*Margin); + if(mount(fd, -1, "/mnt/wsys", MREPL, buf) < 0) { + fprint(2, "attachscreen: can't mount window manager: %r\n"); + return nil; + } + if(bind("/mnt/wsys", "/dev", MBEFORE) < 0){ + fprint(2, "attachscreen: can't bind /mnt/wsys before /dev: %r\n"); + return nil; + } + } + + cursfd = open("/dev/cursor", OWRITE); + if(cursfd < 0) { + fprint(2, "attachscreen: open cursor: %r\n"); + return nil; + } + + /* Set up graphics window console (chars->gkbdq) */ + keybdfd = open("/dev/cons", OREAD); + if(keybdfd < 0) { + fprint(2, "attachscreen: open keyboard: %r\n"); + return nil; + } + mousefd = open("/dev/mouse", ORDWR); + if(mousefd < 0){ + fprint(2, "attachscreen: can't open mouse: %r\n"); + return nil; + } + if(usenewwin || 1){ + fd = open("/dev/consctl", OWRITE); + if(fd < 0) + fprint(2, "attachscreen: open /dev/consctl: %r\n"); + if(write(fd, "rawon", 5) != 5) + fprint(2, "attachscreen: write /dev/consctl: %r\n"); + } + + /* Set up graphics files */ + ctlfd = open("/dev/draw/new", ORDWR); + if(ctlfd < 0){ + fprint(2, "attachscreen: can't open graphics control file: %r\n"); + return nil; + } + if(read(ctlfd, info, sizeof info) < NINFO){ + close(ctlfd); + fprint(2, "attachscreen: can't read graphics control file: %r\n"); + return nil; + } + sprint(buf, "/dev/draw/%d/data", atoi(info+0*12)); + datafd = open(buf, ORDWR|OCEXEC); + if(datafd < 0){ + close(ctlfd); + fprint(2, "attachscreen: can't read graphics data file: %r\n"); + return nil; + } + dispbufsize = iounit(datafd); + if(dispbufsize <= 0) + dispbufsize = 8000; + if(dispbufsize < 512){ + close(ctlfd); + close(datafd); + fprint(2, "attachscreen: iounit %d too small\n", dispbufsize); + return nil; + } + chunksize = dispbufsize - 64; + + if(attachwindow(r, chan, d, width) == nil){ + close(ctlfd); + close(datafd); + return nil; + } + + mousepid = kproc("readmouse", plan9readmouse, nil, 0); + keybdpid = kproc("readkbd", plan9readkeybd, nil, 0); + + fd = open("/dev/label", OWRITE); + if(fd >= 0){ + snprint(buf, sizeof(buf), "inferno %d", getpid()); + write(fd, buf, strlen(buf)); + close(fd); + } + + *softscreen = 1; + return (uchar*)data; +} + +static ulong* +attachwindow(Rectangle *r, ulong *chan, int *d, int *width) +{ + int n, fd, nb; + char buf[256]; + uchar ubuf[128]; + ulong imagechan; + + /* + * Discover name of window + */ + fd = open("/mnt/wsys/winname", OREAD); + if(fd<0 || (n=read(fd, winname, sizeof winname))<=0){ + fprint(2, "attachwindow: can only run inferno under rio, not stand-alone\n"); + return nil; + } + close(fd); + /* + * If had previous window, release it + */ + if(imageid > 0){ + ubuf[0] = 'f'; + BPLONG(ubuf+1, imageid); + if(write(datafd, ubuf, 1+4) != 1+4) + fprint(2, "attachwindow: cannot free old window: %r\n"); + } + /* + * Allocate image pointing to window, and discover its ID + */ + ubuf[0] = 'n'; + ++imageid; + BPLONG(ubuf+1, imageid); + ubuf[5] = n; + memmove(ubuf+6, winname, n); + if(write(datafd, ubuf, 6+n) != 6+n){ + fprint(2, "attachwindow: cannot bind %d to window id '%s': %r\n", imageid, winname); + return nil; + } + if(read(ctlfd, buf, sizeof buf) < 12*12){ + fprint(2, "attachwindow: cannot read window id: %r\n"); + return nil; + } + imagechan = strtochan(buf+2*12); + truedepth = chantodepth(imagechan); + if(truedepth == 0){ + fprint(2, "attachwindow: cannot handle window depth specifier %.12s\n", buf+2*12); + return nil; + } + + /* + * Report back + */ + if(chan != nil) + *chan = imagechan; + if(d != nil) + *d = chantodepth(imagechan); + nb = 0; + if(r != nil){ + Xsize = atoi(buf+6*12)-atoi(buf+4*12)-2*Margin; + Ysize = atoi(buf+7*12)-atoi(buf+5*12)-2*Margin; + r->min.x = 0; + r->min.y = 0; + r->max.x = Xsize; + r->max.y = Ysize; + nb = bytesperline(*r, truedepth); + data = malloc(nb*Ysize); + loadbuf = malloc(nb*Lsize+1); + chunk = malloc(HDR+chunksize+5); /* +5 for flush (1 old, 5 new) */ + } + imager.min.x = atoi(buf+4*12); + imager.min.y = atoi(buf+5*12); + imager.max.x = atoi(buf+6*12); + imager.max.y = atoi(buf+7*12); + + if(width != nil) + *width = nb/4; + + tiler.min.x = atoi(buf+4*12)+Margin; + tiler.min.y = atoi(buf+5*12)+Margin; + tiler.max.x = atoi(buf+6*12)-Margin; + tiler.max.y = atoi(buf+7*12)-Margin; + + return data; +} + +static int +plan9loadimage(Rectangle r, uchar *data, int ndata) +{ + long dy; + int n, bpl; + + if(!rectinrect(r, imager)){ + werrstr("loadimage: bad rectangle"); + return -1; + } + bpl = bytesperline(r, truedepth); + n = bpl*Dy(r); + if(n > ndata){ + werrstr("loadimage: insufficient data"); + return -1; + } + ndata = 0; + while(r.max.y > r.min.y){ + dy = r.max.y - r.min.y; + if(dy*bpl> chunksize) + dy = chunksize/bpl; + n = dy*bpl; + chunk[0] = 'y'; + BPLONG(chunk+1, imageid); + BPLONG(chunk+5, r.min.x); + BPLONG(chunk+9, r.min.y); + BPLONG(chunk+13, r.max.x); + BPLONG(chunk+17, r.min.y+dy); + memmove(chunk+21, data, n); + ndata += n; + data += n; + r.min.y += dy; + n += 21; + if(r.min.y >= r.max.y) /* flush to screen */ + chunk[n++] = 'v'; + if(write(datafd, chunk, n) != n) + return -1; + } + return ndata; +} + +static void +_flushmemscreen(Rectangle r) +{ + int n, dy, l; + Rectangle rr; + + if(data == nil || loadbuf == nil || chunk==nil) + return; + if(!rectclip(&r, Rect(0, 0, Xsize, Ysize))) + return; + if(!rectclip(&r, Rect(0, 0, Dx(tiler), Dy(tiler)))) + return; + if(Dx(r)<=0 || Dy(r)<=0) + return; + l = bytesperline(r, truedepth); + while(r.min.y < r.max.y){ + dy = Dy(r); + if(dy > Lsize) + dy = Lsize; + rr = r; + rr.max.y = rr.min.y+dy; + n = unloadmemimage(screenimage, rr, loadbuf, l*dy); + /* offset from (0,0) to window */ + rr.min.x += tiler.min.x; + rr.min.y += tiler.min.y; + rr.max.x += tiler.min.x; + rr.max.y += tiler.min.y; + if(plan9loadimage(rr, loadbuf, n) != n) + fprint(2, "flushmemscreen: %d bytes: %r\n", n); + r.min.y += dy; + } +} + +void +flushmemscreen(Rectangle r) +{ + qlock(&ql); + _flushmemscreen(r); + qunlock(&ql); +} + +void +drawcursor(Drawcursor *c) +{ + int j, i, h, w, bpl; + uchar *bc, *bs, *cclr, *cset, curs[2*4+2*2*16]; + + /* Set the default system cursor */ + if(c->data == nil) { + write(cursfd, curs, 0); + return; + } + + BPLONG(curs+0*4, c->hotx); + BPLONG(curs+1*4, c->hoty); + + w = (c->maxx-c->minx); + h = (c->maxy-c->miny)/2; + + cclr = curs+2*4; + cset = curs+2*4+2*16; + bpl = bytesperline(Rect(c->minx, c->miny, c->maxx, c->maxy), 1); + bc = c->data; + bs = c->data + h*bpl; + + if(h > 16) + h = 16; + if(w > 16) + w = 16; + w /= 8; + for(i = 0; i < h; i++) { + for(j = 0; j < w; j++) { + cclr[j] = bc[j]; + cset[j] = bs[j]; + } + bc += bpl; + bs += bpl; + cclr += 2; + cset += 2; + } + write(cursfd, curs, sizeof curs); +} + +static int +checkmouse(char *buf, int n) +{ + int x, y, tick, b; + static int lastb, lastt, lastx, lasty, lastclick; + + switch(n){ + default: + kwerrstr("atomouse: bad count"); + return -1; + + case 1+4*12: + if(buf[0] == 'r'){ + qlock(&ql); + if(attachwindow(nil, nil, nil, nil) == nil) { + qunlock(&ql); + return -1; + } + _flushmemscreen(Rect(0, 0, Xsize, Ysize)); + qunlock(&ql); + } + x = atoi(buf+1+0*12) - tiler.min.x; + y = atoi(buf+1+1*12) - tiler.min.y; + b = atoi(buf+1+2*12); + tick = atoi(buf+1+3*12); + if(b && lastb == 0){ /* button newly pressed */ + if(b==lastclick && tick-lastt<400 + && abs(x-lastx)<10 && abs(y-lasty)<10) + b |= (1<<8); + lastt = tick; + lastclick = b&0xff; + lastx = x; + lasty = y; + } + lastb = b&0xff; + //mouse.msec = tick; + mousetrack(b, x, y, 0); + return n; + } +} + +static void +plan9readmouse(void *v) +{ + int n; + char buf[128]; + + USED(v); + for(;;){ + n = read(mousefd, buf, sizeof(buf)); + if(n < 0) /* probably interrupted */ + _exits(0); + checkmouse(buf, n); + } +} + +static void +plan9readkeybd(void*) +{ + int n, partial; + char buf[32]; + char dbuf[32 * 3]; /* overestimate but safe */ + + partial = 0; + for(;;){ + n = read(keybdfd, buf + partial, sizeof(buf) - partial); + if(n < 0) /* probably interrupted */ + _exits(0); + partial += n; + n = mapspecials(dbuf, buf, &partial); + qproduce(gkbdq, dbuf, n); + } +} + +void +setpointer(int x, int y) +{ + char buf[50]; + int n; + + if(mousefd < 0) + return; + x += tiler.min.x; + y += tiler.min.y; + n = snprint(buf, sizeof buf, "m%11d %11d ", x, y); + write(mousefd, buf, n); +} + +/* + * plan9 keyboard codes; from /sys/include/keyboard.h; can't include directly + * because constant names clash. + */ +enum { + P9KF= 0xF000, /* Rune: beginning of private Unicode space */ + P9Spec= 0xF800, + /* KF|1, KF|2, ..., KF|0xC is F1, F2, ..., F12 */ + Khome= P9KF|0x0D, + Kup= P9KF|0x0E, + Kpgup= P9KF|0x0F, + Kprint= P9KF|0x10, + Kleft= P9KF|0x11, + Kright= P9KF|0x12, + Kdown= P9Spec|0x00, + Kview= P9Spec|0x00, + Kpgdown= P9KF|0x13, + Kins= P9KF|0x14, + Kend= KF|0x18, + + Kalt= P9KF|0x15, + Kshift= P9KF|0x16, + Kctl= P9KF|0x17, +}; + +/* + * translate plan 9 special characters from s2 (of length *n) into s1; + * return number of chars placed into s1. + * any trailing incomplete chars are moved to the beginning of s2, + * and *n set to the number moved there. + */ +static int +mapspecials(char *s1, char *s2, int *n) +{ + char *s, *d, *es2; + Rune r; + d = s1; + s = s2; + es2 = s2 + *n; + while (fullrune(s, es2 - s)) { + s += chartorune(&r, s); + switch (r) { + case Kshift: + r = LShift; + break; + case Kctl: + r = LCtrl; + break; + case Kalt: + r = LAlt; + break; + case Khome: + r = Home; + break; + case Kend: + r = End; + break; + case Kup: + r = Up; + break; + case Kdown: + r = Down; + break; + case Kleft: + r = Left; + break; + case Kright: + r = Right; + break; + case Kpgup: + r = Pgup; + break; + case Kpgdown: + r = Pgdown; + break; + case Kins: + r = Ins; + break; + /* + * function keys + */ + case P9KF|1: + case P9KF|2: + case P9KF|3: + case P9KF|4: + case P9KF|5: + case P9KF|6: + case P9KF|7: + case P9KF|8: + case P9KF|9: + case P9KF|10: + case P9KF|11: + case P9KF|12: + r = (r - P9KF) + KF; + } + d += runetochar(d, &r); + } + *n = es2 - s; + memmove(s2, s, *n); + return d - s1; +} diff --git a/emu/Solaris/asm-386.s b/emu/Solaris/asm-386.s new file mode 100644 index 00000000..60dc44eb --- /dev/null +++ b/emu/Solaris/asm-386.s @@ -0,0 +1,72 @@ + .section .bss + .align 4 +.L4_.bss: + .align 4 +Solaris_Asm_IntP: / Offset 0 + .type Solaris_Asm_IntP,@object + .size Solaris_Asm_IntP,4 + .set .,.+4 +Solaris_Asm_VoidP: / Offset 4 + .type Solaris_Asm_VoidP,@object + .size Solaris_Asm_VoidP,4 + .set .,.+4 + .section .text + .align 4 +.L1_.text: + +/==================== +/ FPsave +/-------------------- + .align 4 + .align 4 + .globl FPsave +FPsave: + pushl %ebp + movl %esp,%ebp + movl 8(%ebp),%eax + movl %eax,Solaris_Asm_VoidP + fstenv (%eax) + leave + ret + .align 4 + .type FPsave,@function + .size FPsave,.-FPsave + +/==================== +/ FPrestore +/-------------------- + .align 4 + .globl FPrestore +FPrestore: + pushl %ebp + movl %esp,%ebp + movl 8(%ebp),%eax + movl %eax,Solaris_Asm_VoidP + fldenv (%eax) + leave + ret + .align 4 + .type FPrestore,@function + .size FPrestore,.-FPrestore + + +/==================== +/ getcallerpc +/-------------------- + .align 4 + .globl getcallerpc +getcallerpc: + movl 4(%ebp),%eax + ret + .align 4 + .type getcallerpc,@function + .size getcallerpc,.-getcallerpc + +/ test-and-set + .align 4 + .globl _tas +_tas: + movl $1, %eax + movl 4(%esp), %ecx + xchgl %eax, 0(%ecx) + ret diff --git a/emu/Solaris/asm-sparc.s b/emu/Solaris/asm-sparc.s new file mode 100644 index 00000000..c53a0bc0 --- /dev/null +++ b/emu/Solaris/asm-sparc.s @@ -0,0 +1,76 @@ + + .section ".text", #alloc, #execinstr + .align 8 + .skip 16 + .global segflush + .type segflush,2 + + ! The flush instruction works on 8-byte chunks. + ! We truncate the pointer and increase the count + ! to make sure we flush the right range. + + ! SPARC requires 5 instructions after flush to + ! let the caches settle. The loop code supplies + ! the delay instructions. + +segflush: ! int segflush(void *p, ulong len) + + and %o0,-8,%o0 ! clear low 3 bits of p + add %o1, 7, %o1 ! len += 7 +1: + flush %o0 ! synchronize cache + sub %o1, 8, %o1 ! len -= 8 + cmp %o1, 0 ! if len > 0, repeat + bg 1b + add %o0, 8, %o0 ! p += 8 in delay slot + + retl + add %g0, %g0, %o0 ! return 0 + .size segflush,(.-segflush) + + + .section ".text", #alloc, #execinstr + .align 8 + .skip 16 + .global FPsave + .type FPsave,2 +FPsave: + retl + st %fsr,[%o0] + .size FPsave,(.-FPsave) + + + .section ".text", #alloc, #execinstr + .align 8 + .skip 16 + .global FPrestore + .type FPrestore,2 +FPrestore: + retl + ld [%o0],%fsr + .size FPrestore,(.-FPrestore) + + + .section ".text", #alloc, #execinstr + .align 8 + .skip 16 + .global getcallerpc + .type getcallerpc, 2 +getcallerpc: ! ignore argument + retl + add %i7,0,%o0 + + .size getcallerpc,(.-getcallerpc) + + + .section ".text", #alloc, #execinstr + .align 8 + .skip 16 + .global _tas + .type _tas, 2 +_tas: + or %g0,1,%o1 + swap [%o0],%o1 + retl + or %g0,%o1,%o0 + .size _tas,(.-_tas) diff --git a/emu/Solaris/audio.c b/emu/Solaris/audio.c new file mode 100644 index 00000000..91ffd8d6 --- /dev/null +++ b/emu/Solaris/audio.c @@ -0,0 +1,593 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#define __EXTENSIONS__ +#include <sys/time.h> +#include <time.h> +#include <fcntl.h> +#include <stropts.h> +#include <sys/audioio.h> +#include <sys/ioctl.h> +#include <sys/filio.h> +#include "audio.h" +#include <sys/audioio.h> + +#define Audio_Mic_Val AUDIO_MICROPHONE +#define Audio_Linein_Val AUDIO_LINE_IN + +#define Audio_Speaker_Val AUDIO_SPEAKER +#define Audio_Headphone_Val AUDIO_HEADPHONE +#define Audio_Lineout_Val AUDIO_LINE_OUT + +#define Audio_Pcm_Val AUDIO_ENCODING_LINEAR +#define Audio_Ulaw_Val AUDIO_ENCODING_ULAW +#define Audio_Alaw_Val AUDIO_ENCODING_ALAW + +#include "audio-tbls.c" + +#define min(a,b) ((a) < (b) ? (a) : (b)) +static int debug = 0; + +extern int nanosleep(const struct timespec *, struct timespec *); + +#define AUDIO_FILE_STRING "/dev/audio" + +enum { + A_Pause, + A_UnPause +}; + +enum { + A_In, + A_Out +}; + +static QLock inlock; +static QLock outlock; + +static int audio_file_in = -1; /* file in */ +static int audio_file_out = -1; /* file out */ + +static int audio_swap_flag = 0; /* endian swap */ + +static int audio_in_pause = A_UnPause; + +static Audio_t av; + +static int audio_enforce(Audio_t*); +static int audio_open_in(void); +static int audio_open_out(void); +static int audio_pause_in(int, int); +static int audio_flush(int, int); +static int audio_pause_out(int); +static int audio_set_blocking(int); +static int audio_set_info(int, Audio_d*, int); +static void audio_swap_endian(char*, int); + +void +audio_file_init(void) +{ + static ushort flag = 1; + audio_swap_flag = *((uchar*)&flag) == 0; /* big-endian? */ + audio_info_init(&av); +} + +void +audio_file_open(Chan *c, int omode) +{ + switch(omode){ + case OREAD: + qlock(&inlock); + if(waserror()){ + qunlock(&inlock); + nexterror(); + } + + if(audio_file_in >= 0) + error(Einuse); + if((audio_file_in = audio_open_in()) < 0) + oserror(); + + poperror(); + qunlock(&inlock); + break; + case OWRITE: + qlock(&outlock); + if(waserror()){ + qunlock(&outlock); + nexterror(); + } + if(audio_file_out >= 0) + error(Einuse); + if((audio_file_out = audio_open_out() ) < 0) + oserror(); + poperror(); + qunlock(&outlock); + break; + case ORDWR: + qlock(&inlock); + qlock(&outlock); + if(waserror()){ + qunlock(&inlock); + qunlock(&outlock); + nexterror(); + } + if(audio_file_in >= 0 || audio_file_out >= 0) + error(Einuse); + + if((audio_file_in = audio_open_in()) < 0) + oserror(); + if(waserror()){ + close(audio_file_in); + audio_file_in = -1; + nexterror(); + } + if((audio_file_out = audio_open_out()) < 0) + oserror(); + poperror(); + + poperror(); + qunlock(&inlock); + qunlock(&outlock); + break; + } +} + +void +audio_file_close(Chan *c) +{ + switch(c->mode){ + case OREAD: + qlock(&inlock); + close(audio_file_in); + audio_file_in = -1; + qunlock(&inlock); + break; + case OWRITE: + qlock(&outlock); + close(audio_file_out); + audio_file_out = -1; + qunlock(&outlock); + break; + case ORDWR: + qlock(&inlock); + close(audio_file_in); + audio_file_in = -1; + qunlock(&inlock); + qlock(&outlock); + close(audio_file_out); + audio_file_out = -1; + qunlock(&outlock); + break; + } +} + +long +audio_file_read(Chan *c, void *va, long count, vlong offset) +{ + struct timespec time; + long ba, status, chunk, total; + char *pva = (char *) va; + + qlock(&inlock); + if(waserror()){ + qunlock(&inlock); + nexterror(); + } + + if(audio_file_in < 0) + error(Eperm); + + /* check block alignment */ + ba = av.in.bits * av.in.chan / Bits_Per_Byte; + + if(count % ba) + error(Ebadarg); + + if(!audio_pause_in(audio_file_in, A_UnPause)) + error(Eio); + + total = 0; + while(total < count) { + chunk = count - total; + osenter(); + status = read(audio_file_in, pva + total, chunk); + osleave(); + if(status < 0) + error(Eio); + total += status; + } + + if(total != count) + error(Eio); + + if(audio_swap_flag && av.out.bits == 16) + audio_swap_endian(pva, count); + + poperror(); + qunlock(&inlock); + + time.tv_sec = 0; /* hack around broken thread scheduler in Solaris */ + time.tv_nsec= 1; + nanosleep(&time,nil); + + return count; +} + +long +audio_file_write(Chan *c, void *va, long count, vlong offset) +{ + struct timespec time; + long status = -1; + long ba, total, chunk, bufsz; + + qlock(&outlock); + if(waserror()){ + qunlock(&outlock); + nexterror(); + } + + if(audio_file_out < 0) + error(Eperm); + + /* check block alignment */ + ba = av.out.bits * av.out.chan / Bits_Per_Byte; + + if(count % ba) + error(Ebadarg); + + if(audio_swap_flag && av.out.bits == 16) + audio_swap_endian(va, count); + + total = 0; + bufsz = av.out.buf * Audio_Max_Buf / Audio_Max_Val; + + if(bufsz == 0) + error(Ebadarg); + + while(total < count) { + chunk = min(bufsz, count - total); + osenter(); + status = write(audio_file_out, va, chunk); + osleave(); + if(status <= 0) + error(Eio); + total += status; + } + + poperror(); + qunlock(&outlock); + + time.tv_sec = 0; /* hack around broken thread scheduler in Solaris */ + time.tv_nsec= 1; + nanosleep(&time,nil); + + return count; +} + +int +audio_open_in(void) +{ + int fd; + + /* open non-blocking in case someone already has it open */ + /* otherwise we would block until they close! */ + fd = open(AUDIO_FILE_STRING, O_RDONLY|O_NONBLOCK); + + if(fd < 0) + oserror(); + + /* change device to be blocking */ + if(!audio_set_blocking(fd)) { + close(fd); + error(Eio); + } + + if(!audio_pause_in(fd, A_Pause)) { + close(fd); + error(Eio); + } + + if(!audio_flush(fd, A_In)) { + close(fd); + error(Eio); + } + + /* set audio info */ + av.in.flags = ~0; + av.out.flags = 0; + + if(!audio_set_info(fd, &av.in, A_In)) { + close(fd); + error(Ebadarg); + } + + av.in.flags = 0; + + /* tada, we're open, blocking, paused and flushed */ + return fd; +} + +int +audio_open_out(void) +{ + int fd; + struct audio_info hdr; + + /* open non-blocking in case someone already has it open */ + /* otherwise we would block until they close! */ + fd = open(AUDIO_FILE_STRING, O_WRONLY|O_NONBLOCK); + + if(fd < 0) + oserror(); + + /* change device to be blocking */ + if(!audio_set_blocking(fd)) { + close(fd); + error("cannot set blocking mode"); + } + + /* set audio info */ + av.in.flags = 0; + av.out.flags = ~0; + + if(!audio_set_info(fd, &av.out, A_Out)) { + close(fd); + error(Ebadarg); + } + + av.out.flags = 0; + + return fd; +} + +long +audio_ctl_write(Chan *c, void *va, long count, vlong offset) +{ + int fd; + int ff; + Audio_t tmpav = av; + + tmpav.in.flags = 0; + tmpav.out.flags = 0; + + if (!audioparse(va, count, &tmpav)) + error(Ebadarg); + + if (!audio_enforce(&tmpav)) + error(Ebadarg); + + qlock(&inlock); + if (waserror()) { + qunlock(&inlock); + nexterror(); + } + + if (audio_file_in >= 0 && (tmpav.in.flags & AUDIO_MOD_FLAG)) { + if (!audio_pause_in(audio_file_in, A_Pause)) + error(Ebadarg); + if (!audio_flush(audio_file_in, A_In)) + error(Ebadarg); + if (!audio_set_info(audio_file_in, &tmpav.in, A_In)) + error(Ebadarg); + } + poperror(); + qunlock(&inlock); + + qlock(&outlock); + if (waserror()) { + qunlock(&outlock); + nexterror(); + } + if (audio_file_out >= 0 && (tmpav.out.flags & AUDIO_MOD_FLAG)){ + if (!audio_pause_out(audio_file_out)) + error(Ebadarg); + if (!audio_set_info(audio_file_out, &tmpav.out, A_Out)) + error(Ebadarg); + } + poperror(); + qunlock(&outlock); + + tmpav.in.flags = 0; + tmpav.out.flags = 0; + + av = tmpav; + + return count; +} + +static int +audio_set_blocking(int fd) +{ + int val; + + if((val = fcntl(fd, F_GETFL, 0)) == -1) + return 0; + + val &= ~O_NONBLOCK; + + if(fcntl(fd, F_SETFL, val) < 0) + return 0; + + return 1; +} + +static int +audio_set_info(int fd, Audio_d *i, int d) +{ + int status; + int unequal_stereo = 0; + audio_info_t info; + audio_prinfo_t *dev; + + if(fd < 0) + return 0; + + /* devitialize header */ + AUDIO_INITINFO(&info); + + if(d == A_In) + dev = &info.record; + else + dev = &info.play; + + /* sample rate */ + if(i->flags & AUDIO_RATE_FLAG) + dev->sample_rate = i->rate; + + /* channels */ + if(i->flags & AUDIO_CHAN_FLAG) + dev->channels = i->chan; + + /* precision */ + if(i->flags & AUDIO_BITS_FLAG) + dev->precision = i->bits; + + /* encoding */ + if(i->flags & AUDIO_ENC_FLAG) + dev->encoding = i->enc; + + /* devices */ + if(i->flags & AUDIO_DEV_FLAG) + dev->port = i->dev; + + /* dev volume */ + if(i->flags & (AUDIO_LEFT_FLAG|AUDIO_VOL_FLAG)) { + dev->gain = (i->left * AUDIO_MAX_GAIN) / Audio_Max_Val; + + /* do left first then right later */ + if(i->left == i->right) + dev->balance = AUDIO_MID_BALANCE; + else { + dev->balance = AUDIO_LEFT_BALANCE; + if(i->chan != 1) + unequal_stereo = 1; + } + } + + osenter(); + status = ioctl(fd, AUDIO_SETINFO, &info); /* qlock and load general stuff */ + osleave(); + + if(status == -1) { + if(debug) print("audio_set_info 1 failed: fd = %d errno = %d\n", fd, errno); + return 0; + } + + /* check for different right and left for dev */ + if(unequal_stereo) { + + /* re-init header */ + AUDIO_INITINFO(&info); + + dev->gain = (i->right * AUDIO_MAX_GAIN) / Audio_Max_Val; + dev->balance == AUDIO_RIGHT_BALANCE; + + osenter(); + status = ioctl(fd, AUDIO_SETINFO, &info); + osleave(); + + if(status == -1) { + if(debug) print("audio_set_info 2 failed: fd = %d errno = %d\n",fd, errno); + return 0; + } + } + + return 1; +} + +void +audio_swap_endian(char *p, int n) +{ + int b; + + while (n > 1) { + b = p[0]; + p[0] = p[1]; + p[1] = b; + p += 2; + n -= 2; + } +} + +static int +audio_pause_out(int fd) +{ + audio_info_t info; + int foo = 0; + int status; + + osenter(); + status = ioctl(fd, AUDIO_DRAIN, &foo); + osleave(); + + if(status == -1) + return 0; + return 1; +} + +static int +audio_pause_in(int fd, int f) +{ + audio_info_t info; + int status; + + if(fd < 0) + return 0; + + if(audio_in_pause == f) + return 1; + + /* initialize header */ + AUDIO_INITINFO(&info); + + /* unpause input */ + if(f == A_Pause) + info.record.pause = 1; + else + info.record.pause = 0; + + osenter(); + status = ioctl(fd, AUDIO_SETINFO, &info); + osleave(); + + if(status == -1) + return 0; + + audio_in_pause = f; + + return 1; +} + +static int +audio_flush(int fd, int d) +{ + int flag = d==A_In? FLUSHR: FLUSHW; + int status; + + osenter(); + status = ioctl(fd, I_FLUSH, flag); /* drain anything already put into buffer */ + osleave(); + + if(status == -1) + return 0; + return 1; +} + +static int +audio_enforce(Audio_t *t) +{ + if((t->in.enc == Audio_Ulaw_Val || t->in.enc == Audio_Alaw_Val) && + (t->in.rate != 8000 || t->in.chan != 1)) + return 0; + if((t->out.enc == Audio_Ulaw_Val || t->out.enc == Audio_Alaw_Val) && + (t->out.rate != 8000 || t->out.chan != 1)) + return 0; + return 1; +} + +Audio_t* +getaudiodev(void) +{ + return &av; +} diff --git a/emu/Solaris/cmd.c b/emu/Solaris/cmd.c new file mode 100644 index 00000000..bff1f03b --- /dev/null +++ b/emu/Solaris/cmd.c @@ -0,0 +1,213 @@ +#include <sys/types.h> +#include <signal.h> +#include <pwd.h> +#include <sys/resource.h> +#include <sys/wait.h> +#include <fcntl.h> + +#include "dat.h" +#include "fns.h" +#include "error.h" + +enum +{ + Debug = 0 +}; + +/* + * os-specific devcmd support. + * this version should be reasonably portable across Unix systems. + */ +typedef struct Targ Targ; +struct Targ +{ + int fd[3]; /* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */ + char** args; + char* dir; + int pid; + int wfd; /* child writes errors that occur after the fork or on exec */ + int uid; + int gid; +}; + +extern int gidnobody; +extern int uidnobody; + +static int +childproc(Targ *t) +{ + int i, nfd; + + if(Debug) + print("devcmd: '%s'", t->args[0]); + + nfd = getdtablesize(); + for(i = 0; i < nfd; i++) + if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd) + close(i); + + dup2(t->fd[0], 0); + dup2(t->fd[1], 1); + dup2(t->fd[2], 2); + close(t->fd[0]); + close(t->fd[1]); + close(t->fd[2]); + + /* should have an auth file to do host-specific authorisation? */ + if(t->gid != -1){ + if(setgid(t->gid) < 0 && getegid() == 0){ + fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno)); + _exit(1); + } + } + + if(t->uid != -1){ + if(setuid(t->uid) < 0 && geteuid() == 0){ + fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno)); + _exit(1); + } + } + + if(t->dir != nil && chdir(t->dir) < 0){ + fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno)); + _exit(1); + } + + signal(SIGPIPE, SIG_DFL); + + execvp(t->args[0], t->args); + if(Debug) + print("execvp: %s\n",strerror(errno)); + fprint(t->wfd, "exec failed: %s", strerror(errno)); + + _exit(1); +} + +void* +oscmd(char **args, int nice, char *dir, int *fd) +{ + Targ *t; + int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid; + + t = mallocz(sizeof(*t), 1); + if(t == nil) + return nil; + + fd0[0] = fd0[1] = -1; + fd1[0] = fd1[1] = -1; + fd2[0] = fd2[1] = -1; + wfd[0] = wfd[1] = -1; + if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0) + goto Error; + if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0) /* close on exec to give end of file on success */ + goto Error; + + t->fd[0] = fd0[0]; + t->fd[1] = fd1[1]; + t->fd[2] = fd2[1]; + t->wfd = wfd[1]; + t->args = args; + t->dir = dir; + t->gid = up->env->gid; + if(t->gid == -1) + t->gid = gidnobody; + t->uid = up->env->uid; + if(t->uid == -1) + t->uid = uidnobody; + + signal(SIGCHLD, SIG_DFL); + switch(pid = fork()) { + case -1: + goto Error; + case 0: + setpgrp(); + if(nice) + oslopri(); + childproc(t); + _exit(1); + default: + t->pid = pid; + if(Debug) + print("cmd pid %d\n", t->pid); + break; + } + + close(fd0[0]); + close(fd1[1]); + close(fd2[1]); + close(wfd[1]); + + n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1); + close(wfd[0]); + if(n > 0){ + close(fd0[1]); + close(fd1[0]); + close(fd2[0]); + free(t); + up->genbuf[n] = 0; + if(Debug) + print("oscmd: bad exec: %q\n", up->genbuf); + error(up->genbuf); + return nil; + } + + fd[0] = fd0[1]; + fd[1] = fd1[0]; + fd[2] = fd2[0]; + return t; + +Error: + r = errno; + if(Debug) + print("oscmd: %q\n",strerror(r)); + close(fd0[0]); + close(fd0[1]); + close(fd1[0]); + close(fd1[1]); + close(fd2[0]); + close(fd2[1]); + close(wfd[0]); + close(wfd[1]); + error(strerror(r)); + return nil; +} + +int +oscmdkill(void *a) +{ + Targ *t = a; + + if(Debug) + print("kill: %d\n", t->pid); + return kill(-t->pid, SIGTERM); +} + +int +oscmdwait(void *a, char *buf, int n) +{ + Targ *t = a; + int s; + + if(waitpid(t->pid, &s, 0) == -1){ + if(Debug) + print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno)); + return -1; + } + if(WIFEXITED(s)){ + if(WEXITSTATUS(s) == 0) + return snprint(buf, n, "%d 0 0 0 ''", t->pid); + return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s)); + } + if(WIFSIGNALED(s)){ + if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL) + return snprint(buf, n, "%d 0 0 0 killed", t->pid); + return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s)); + } + return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s); +} + +void +oscmdfree(void *a) +{ + free(a); +} diff --git a/emu/Solaris/deveia.c b/emu/Solaris/deveia.c new file mode 100644 index 00000000..884411ac --- /dev/null +++ b/emu/Solaris/deveia.c @@ -0,0 +1,38 @@ +/* + * Solaris serial port definitions + */ + +static char *sysdev[] = { + "/dev/term/a", + "/dev/term/b" +}; + +#include "deveia-posix.c" +#include "deveia-bsd.c" + +static struct tcdef_t bps[] = { + {0, B0}, + {50, B50}, + {75, B75}, + {110, B110}, + {134, B134}, + {150, B150}, + {200, B200}, + {300, B300}, + {600, B600}, + {1200, B1200}, + {1800, B1800}, + {2400, B2400}, + {4800, B4800}, + {9600, B9600}, + {19200, B19200}, + {38400, B38400}, + {57600, B57600}, + {76800, B76800}, + {115200, B115200}, + {153600, B153600}, + {230400, B230400}, + {307200, B307200}, + {460800, B460800}, + {-1, -1} +}; diff --git a/emu/Solaris/devfs.c b/emu/Solaris/devfs.c new file mode 100644 index 00000000..9017c3fc --- /dev/null +++ b/emu/Solaris/devfs.c @@ -0,0 +1 @@ +#include "devfs-posix.c" diff --git a/emu/Solaris/emu b/emu/Solaris/emu new file mode 100644 index 00000000..a72e6b7b --- /dev/null +++ b/emu/Solaris/emu @@ -0,0 +1,106 @@ +dev + root + cons + env + mnt + pipe + prog + prof + srv + dup + ssl + cap + fs + cmd cmd + indir + + draw win-x11a + pointer + snarf + + ip ipif-posix ipaux + eia + audio audio + mem + +lib + interp + tk + freetype + math + draw + + memlayer + memdraw + keyring + sec + mp + + 9 + +link + +mod + sys + draw + + tk + math + srv srv + keyring + loader + freetype + +port + alloc + cache + chan + dev + dial + dis + discall + env + error + errstr + exception + exportfs + inferno + latin1 + main + parse + pgrp + print + proc + qio + random + sysfile + uqid + +code + +init + emuinit + +root + /dev / + /fd / + /prog / + /net / + /net.alt / + /chan / + /nvfs / + /env / +# /chan +# /dev +# /dis +# /env +# /n +# /net +# /nvfs / +# /prog +# /icons +# /osinit.dis +# /dis/emuinit.dis +# /dis/lib/auth.dis +# /dis/lib/ssl.dis +# /n/local / diff --git a/emu/Solaris/mkfile b/emu/Solaris/mkfile new file mode 100644 index 00000000..6f578086 --- /dev/null +++ b/emu/Solaris/mkfile @@ -0,0 +1,46 @@ +SYSTARG=Solaris +OBJTYPE=sparc +<../../mkconfig +SYSTARG=Solaris +OBJTYPE=sparc + +#Configurable parameters + +CONF=emu #default configuration +CONFLIST=emu +CLEANCONFLIST= + +INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed + +#end configurable parameters + +<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system + +<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS + +OBJ=\ + asm-$OBJTYPE.$O\ + os.$O\ + $CONF.root.$O\ + lock.$O\ + $DEVS\ + $PORT\ + +HFILES=\ + +CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS +SYSLIBS=$EMULIBS +KERNDATE=`{$NDATE} + +default:V: $O.$CONF + +<../port/portmkfile + +$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBFILES + $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c + $LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS + +install:V: $O.$CONF + cp $O.$CONF $INSTALLDIR/$CONF + +devfs.$O: ../port/devfs-posix.c diff --git a/emu/Solaris/os.c b/emu/Solaris/os.c new file mode 100644 index 00000000..02862220 --- /dev/null +++ b/emu/Solaris/os.c @@ -0,0 +1,437 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#undef _POSIX_C_SOURCE +#undef getwd +#include <unistd.h> +#include <thread.h> +#include <time.h> +#include <termios.h> +#include <signal.h> +#include <pwd.h> +#include <sys/resource.h> +#include <sys/time.h> + +enum +{ + DELETE = 0x7F +}; +char *hosttype = "Solaris"; + +static thread_key_t prdakey; + +static siginfo_t siginfo; + +extern int dflag; + +Proc* +getup(void) +{ + void *vp; + + if (thr_getspecific(prdakey, &vp)) + return nil; + return vp; +} + +void +pexit(char *msg, int t) +{ + Osenv *e; + + lock(&procs.l); + if(up->prev) + up->prev->next = up->next; + else + procs.head = up->next; + + if(up->next) + up->next->prev = up->prev; + else + procs.tail = up->prev; + unlock(&procs.l); + + /*print("pexit: %s: %s\n", up->text, msg);*/ + e = up->env; + if(e != nil) { + closefgrp(e->fgrp); + closepgrp(e->pgrp); + closeegrp(e->egrp); + closesigs(e->sigs); + } + free(up->prog); + sema_destroy(up->os); + free(up->os); + free(up); + thr_exit(0); +} + +static void * +tramp(void *v) +{ + struct Proc *Up; + + if(thr_setspecific(prdakey, v)) { + print("set specific data failed in tramp\n"); + thr_exit(0); + } + Up = v; + Up->sigid = thr_self(); + Up->func(Up->arg); + pexit("", 0); +} + +int +kproc(char *name, void (*func)(void*), void *arg, int flags) +{ + thread_t thread; + Proc *p; + Pgrp *pg; + Fgrp *fg; + Egrp *eg; + sema_t *sem; + + p = newproc(); + + sem = malloc(sizeof(*sem)); + if(sem == nil) + panic("can't allocate semaphore"); + sema_init(sem, 0, USYNC_THREAD, 0); + p->os = sem; + + if(flags & KPDUPPG) { + pg = up->env->pgrp; + incref(&pg->r); + p->env->pgrp = pg; + } + if(flags & KPDUPFDG) { + fg = up->env->fgrp; + incref(&fg->r); + p->env->fgrp = fg; + } + if(flags & KPDUPENVG) { + eg = up->env->egrp; + incref(&eg->r); + p->env->egrp = eg; + } + + p->env->uid = up->env->uid; + p->env->gid = up->env->gid; + kstrdup(&p->env->user, up->env->user); + + strcpy(p->text, name); + + p->func = func; + p->arg = arg; + + lock(&procs.l); + if(procs.tail != nil) { + p->prev = procs.tail; + procs.tail->next = p; + } + else { + procs.head = p; + p->prev = nil; + } + procs.tail = p; + unlock(&procs.l); + + if(thr_create(0, 0, &tramp, p, THR_BOUND|THR_DETACHED, &thread)) + panic("thr_create failed\n"); + thr_yield(); + return(thread); +} + +/* to get pc on trap use siginfo.si_pc field and define all trap handlers + as printILL - have to set sa_sigaction, sa_flags not sa_handler +*/ + +static void +trapUSR1(void) +{ + int intwait; + + intwait = up->intwait; + up->intwait = 0; /* clear it to let proc continue in osleave */ + + if(up->type != Interp) /* Used to unblock pending I/O */ + return; + if(intwait == 0) /* Not posted so its a sync error */ + disfault(nil, Eintr); /* Should never happen */ +} + +static void +trapILL(void) +{ + disfault(nil, "Illegal instruction"); +} + +static void +printILL(int sig, siginfo_t *siginfo, void *v) +{ + panic("Illegal instruction with code=%d at address=%x, opcode=%x.\n", + siginfo->si_code, siginfo->si_addr,*(char*)siginfo->si_addr); +} + +static void +trapBUS(void) +{ + disfault(nil, "Bus error"); +} + +static void +trapSEGV(void) +{ + disfault(nil, "Segmentation violation"); +} + +static void +trapFPE(void) +{ + disfault(nil, "Floating point exception"); +} + +void +oshostintr(Proc *p) +{ + thr_kill(p->sigid, SIGUSR1); +} + +void +osblock(void) +{ + while(sema_wait(up->os)) + ; /* retry on signals */ +} + +void +osready(Proc *p) +{ + sema_post(p->os); +} + +void +oslongjmp(void *regs, osjmpbuf env, int val) +{ + USED(regs); + siglongjmp(env, val); +} + +static struct termios tinit; + +static void +termset(void) +{ + struct termios t; + + tcgetattr(0, &t); + tinit = t; + t.c_lflag &= ~(ICANON|ECHO|ISIG); + t.c_cc[VMIN] = 1; + t.c_cc[VTIME] = 0; + tcsetattr(0, TCSANOW, &t); +} + +static void +termrestore(void) +{ + tcsetattr(0, TCSANOW, &tinit); +} + +void +cleanexit(int x) +{ + USED(x); + + if(up->intwait) { + up->intwait = 0; + return; + } + + if(dflag == 0) + termrestore(); + exit(0); +} + +int gidnobody= -1, uidnobody= -1; + +void +getnobody(void) +{ + struct passwd *pwd; + + if (pwd=getpwnam("nobody")) { + uidnobody = pwd->pw_uid; + gidnobody = pwd->pw_gid; + } +} + +void +osreboot(char *file, char **argv) +{ + if(dflag == 0) + termrestore(); + execvp(file, argv); + panic("reboot failure"); +} + +void +libinit(char *imod) +{ + struct Proc *Up; + struct sigaction act; + struct passwd *pw; + char sys[64]; + + setsid(); + + if(dflag == 0) + termset(); + + gethostname(sys, sizeof(sys)); + kstrdup(&ossysname, sys); + getnobody(); + + memset(&act, 0 , sizeof(act)); + act.sa_handler=trapUSR1; + sigaction(SIGUSR1, &act, nil); + /* + * For the correct functioning of devcmd in the + * face of exiting slaves + */ + signal(SIGPIPE, SIG_IGN); + if(signal(SIGTERM, SIG_IGN) != SIG_IGN) + signal(SIGTERM, cleanexit); + if(sflag == 0) { + act.sa_handler = trapBUS; + sigaction(SIGBUS, &act, nil); + act.sa_handler = trapILL; + sigaction(SIGILL, &act, nil); + act.sa_handler = trapSEGV; + sigaction(SIGSEGV, &act, nil); + act.sa_handler = trapFPE; + sigaction(SIGFPE, &act, nil); + if(signal(SIGINT, SIG_IGN) != SIG_IGN) + signal(SIGINT, cleanexit); + } else{ + act.sa_sigaction = printILL; + act.sa_flags=SA_SIGINFO; + sigaction(SIGILL, &act, nil); + } + + if(thr_keycreate(&prdakey,NULL)) + print("keycreate failed\n"); + + Up = newproc(); + if(thr_setspecific(prdakey,Up)) + panic("set specific thread data failed\n"); + + pw = getpwuid(getuid()); + if(pw != nil) + kstrdup(&eve, pw->pw_name); + else + print("cannot getpwuid\n"); + + up->env->uid = getuid(); + up->env->gid = getgid(); + emuinit(imod); +} + +int +readkbd(void) +{ + int n; + char buf[1]; + + n = read(0, buf, sizeof(buf)); + if(n < 0) + fprint(2, "keyboard read: %s\n", strerror(errno)); + if(n <= 0) + pexit("keyboard thread", 0); + + switch(buf[0]) { + case '\r': + buf[0] = '\n'; + break; + case DELETE: + cleanexit(0); + break; + } + return buf[0]; +} + +/* + * Return an abitrary millisecond clock time + */ +long +osmillisec(void) +{ + static long sec0 = 0, usec0; + struct timeval t; + + if(gettimeofday(&t, NULL)<0) + return(0); + if(sec0==0){ + sec0 = t.tv_sec; + usec0 = t.tv_usec; + } + return((t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000); +} + +/* + * Return the time since the epoch in nanoseconds and microseconds + * The epoch is defined at 1 Jan 1970 + */ +vlong +osnsec(void) +{ + struct timeval t; + + gettimeofday(&t, nil); + return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000; +} + +vlong +osusectime(void) +{ + struct timeval t; + + gettimeofday(&t, nil); + return (vlong)t.tv_sec * 1000000 + t.tv_usec; +} + +int +osmillisleep(ulong milsec) +{ + struct timespec time; + + time.tv_sec = milsec/1000; + time.tv_nsec= (milsec%1000)*1000000; + nanosleep(&time,nil); + return 0; +} + +int +limbosleep(ulong milsec) +{ + return osmillisleep(milsec); +} + +void +osyield(void) +{ + thr_yield(); +} + +void +ospause(void) +{ + for(;;) + pause(); +} + +void +oslopri(void) +{ + setpriority(PRIO_PROCESS, 0, getpriority(PRIO_PROCESS,0)+4); +} diff --git a/emu/Unixware/asm-386.s b/emu/Unixware/asm-386.s new file mode 100644 index 00000000..e3978dd1 --- /dev/null +++ b/emu/Unixware/asm-386.s @@ -0,0 +1,71 @@ + .section .bss + .align 4 +.L4_.bss: + .align 4 +Unixware_Asm_IntP: / Offset 0 + .type Unixware_Asm_IntP,@object + .size Unixware_Asm_IntP,4 + .set .,.+4 +Unixware_Asm_VoidP: / Offset 4 + .type Unixware_Asm_VoidP,@object + .size Unixware_Asm_VoidP,4 + .set .,.+4 + .section .text + .align 4 +.L1_.text: +/==================== +/ FPsave +/-------------------- + .align 4 + .align 4 + .globl FPsave +FPsave: + pushl %ebp + movl %esp,%ebp + movl 8(%ebp),%eax + movl %eax,Unixware_Asm_VoidP + fstenv (%eax) + leave + ret + .align 4 + .type FPsave,@function + .size FPsave,.-FPsave + +/==================== +/ FPrestore +/-------------------- + .align 4 + .globl FPrestore +FPrestore: + pushl %ebp + movl %esp,%ebp + movl 8(%ebp),%eax + movl %eax,Unixware_Asm_VoidP + fldenv (%eax) + leave + ret + .align 4 + .type FPrestore,@function + .size FPrestore,.-FPrestore + + +/==================== +/ getcallerpc +/-------------------- + .align 4 + .globl getcallerpc +getcallerpc: + movl 4(%ebp),%eax + ret + .align 4 + .type getcallerpc,@function + .size getcallerpc,.-getcallerpc + +/ test-and-set + .align 4 + .globl _tas +_tas: + movl $1, %eax + movl 4(%esp), %ecx + xchgl %eax, 0(%ecx) + ret diff --git a/emu/Unixware/cmd.c b/emu/Unixware/cmd.c new file mode 100644 index 00000000..bff1f03b --- /dev/null +++ b/emu/Unixware/cmd.c @@ -0,0 +1,213 @@ +#include <sys/types.h> +#include <signal.h> +#include <pwd.h> +#include <sys/resource.h> +#include <sys/wait.h> +#include <fcntl.h> + +#include "dat.h" +#include "fns.h" +#include "error.h" + +enum +{ + Debug = 0 +}; + +/* + * os-specific devcmd support. + * this version should be reasonably portable across Unix systems. + */ +typedef struct Targ Targ; +struct Targ +{ + int fd[3]; /* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */ + char** args; + char* dir; + int pid; + int wfd; /* child writes errors that occur after the fork or on exec */ + int uid; + int gid; +}; + +extern int gidnobody; +extern int uidnobody; + +static int +childproc(Targ *t) +{ + int i, nfd; + + if(Debug) + print("devcmd: '%s'", t->args[0]); + + nfd = getdtablesize(); + for(i = 0; i < nfd; i++) + if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd) + close(i); + + dup2(t->fd[0], 0); + dup2(t->fd[1], 1); + dup2(t->fd[2], 2); + close(t->fd[0]); + close(t->fd[1]); + close(t->fd[2]); + + /* should have an auth file to do host-specific authorisation? */ + if(t->gid != -1){ + if(setgid(t->gid) < 0 && getegid() == 0){ + fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno)); + _exit(1); + } + } + + if(t->uid != -1){ + if(setuid(t->uid) < 0 && geteuid() == 0){ + fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno)); + _exit(1); + } + } + + if(t->dir != nil && chdir(t->dir) < 0){ + fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno)); + _exit(1); + } + + signal(SIGPIPE, SIG_DFL); + + execvp(t->args[0], t->args); + if(Debug) + print("execvp: %s\n",strerror(errno)); + fprint(t->wfd, "exec failed: %s", strerror(errno)); + + _exit(1); +} + +void* +oscmd(char **args, int nice, char *dir, int *fd) +{ + Targ *t; + int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid; + + t = mallocz(sizeof(*t), 1); + if(t == nil) + return nil; + + fd0[0] = fd0[1] = -1; + fd1[0] = fd1[1] = -1; + fd2[0] = fd2[1] = -1; + wfd[0] = wfd[1] = -1; + if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0) + goto Error; + if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0) /* close on exec to give end of file on success */ + goto Error; + + t->fd[0] = fd0[0]; + t->fd[1] = fd1[1]; + t->fd[2] = fd2[1]; + t->wfd = wfd[1]; + t->args = args; + t->dir = dir; + t->gid = up->env->gid; + if(t->gid == -1) + t->gid = gidnobody; + t->uid = up->env->uid; + if(t->uid == -1) + t->uid = uidnobody; + + signal(SIGCHLD, SIG_DFL); + switch(pid = fork()) { + case -1: + goto Error; + case 0: + setpgrp(); + if(nice) + oslopri(); + childproc(t); + _exit(1); + default: + t->pid = pid; + if(Debug) + print("cmd pid %d\n", t->pid); + break; + } + + close(fd0[0]); + close(fd1[1]); + close(fd2[1]); + close(wfd[1]); + + n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1); + close(wfd[0]); + if(n > 0){ + close(fd0[1]); + close(fd1[0]); + close(fd2[0]); + free(t); + up->genbuf[n] = 0; + if(Debug) + print("oscmd: bad exec: %q\n", up->genbuf); + error(up->genbuf); + return nil; + } + + fd[0] = fd0[1]; + fd[1] = fd1[0]; + fd[2] = fd2[0]; + return t; + +Error: + r = errno; + if(Debug) + print("oscmd: %q\n",strerror(r)); + close(fd0[0]); + close(fd0[1]); + close(fd1[0]); + close(fd1[1]); + close(fd2[0]); + close(fd2[1]); + close(wfd[0]); + close(wfd[1]); + error(strerror(r)); + return nil; +} + +int +oscmdkill(void *a) +{ + Targ *t = a; + + if(Debug) + print("kill: %d\n", t->pid); + return kill(-t->pid, SIGTERM); +} + +int +oscmdwait(void *a, char *buf, int n) +{ + Targ *t = a; + int s; + + if(waitpid(t->pid, &s, 0) == -1){ + if(Debug) + print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno)); + return -1; + } + if(WIFEXITED(s)){ + if(WEXITSTATUS(s) == 0) + return snprint(buf, n, "%d 0 0 0 ''", t->pid); + return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s)); + } + if(WIFSIGNALED(s)){ + if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL) + return snprint(buf, n, "%d 0 0 0 killed", t->pid); + return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s)); + } + return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s); +} + +void +oscmdfree(void *a) +{ + free(a); +} diff --git a/emu/Unixware/deveia.c b/emu/Unixware/deveia.c new file mode 100644 index 00000000..1e452c00 --- /dev/null +++ b/emu/Unixware/deveia.c @@ -0,0 +1,11 @@ +/* + * Solaris serial port definitions + */ + +static char *sysdev[] = { + "/dev/cua/a", + "/dev/cua/b" +}; + +#include "deveia-posix.c" +#include "deveia-bsd.c" diff --git a/emu/Unixware/devfs.c b/emu/Unixware/devfs.c new file mode 100644 index 00000000..9017c3fc --- /dev/null +++ b/emu/Unixware/devfs.c @@ -0,0 +1 @@ +#include "devfs-posix.c" diff --git a/emu/Unixware/emu b/emu/Unixware/emu new file mode 100644 index 00000000..fd657978 --- /dev/null +++ b/emu/Unixware/emu @@ -0,0 +1,106 @@ +dev + root + cons + env + mnt + pipe + prog + prof + srv + dup + ssl + cap + fs + cmd cmd + indir + + draw win-x11a + pointer + snarf + + ip ipif ipaux + eia +# audio audio + mem + +lib + interp + tk + freetype + math + draw + + memlayer + memdraw + keyring + sec + mp + + 9 + +link + +mod + sys + draw + + tk + math + srv srv + keyring + loader + freetype + +port + alloc + cache + chan + dev + dial + dis + discall + env + error + errstr + exception + exportfs + inferno + latin1 + main + parse + pgrp + print + proc + qio + random + sysfile + uqid + +code + +init + emuinit + +root + /dev / + /fd / + /prog / + /net / + /net.alt / + /chan / + /nvfs / + /env / +# /chan +# /dev +# /dis +# /env +# /n +# /net +# /nvfs / +# /prog +# /icons +# /osinit.dis +# /dis/emuinit.dis +# /dis/lib/auth.dis +# /dis/lib/ssl.dis +# /n/local / diff --git a/emu/Unixware/ipif.c b/emu/Unixware/ipif.c new file mode 100644 index 00000000..db7edde5 --- /dev/null +++ b/emu/Unixware/ipif.c @@ -0,0 +1 @@ +#include "../port/ipif-posix.c" diff --git a/emu/Unixware/mkfile b/emu/Unixware/mkfile new file mode 100644 index 00000000..dae437bf --- /dev/null +++ b/emu/Unixware/mkfile @@ -0,0 +1,44 @@ +SYSTARG=Unixware +OBJTYPE=386 +<../../mkconfig +SYSTARG=Unixware +OBJTYPE=386 + +#Configurable parameters + +CONF=emu #default configuration +CONFLIST=emu +CLEANCONFLIST= + +INSTALLDIR=$ROOT/$SYSTARG/$OBJTYPE/bin #path of directory where kernel is installed + +#end configurable parameters + +<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE #set vars based on target system + +<| $SHELLNAME ../port/mkdevlist $CONF #sets $IP, $DEVS, $PORT, $LIBS + +OBJ=\ + asm-$OBJTYPE.$O\ + os.$O\ + $CONF.root.$O\ + lock.$O\ + $DEVS\ + $PORT\ + +HFILES=\ + +CFLAGS='-DROOT="'$ROOT'"' -DEMU -I. -I../port -I$ROOT/$SYSTARG/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp $CTHREADFLAGS $CFLAGS $EMUOPTIONS +SYSLIBS= -lm -lX11 -lsocket -lnsl -lresolv -lxti -Kthread +KERNDATE=`{$NDATE} + +default:V: $O.$CONF + +<../port/portmkfile + +$O.$CONF: $OBJ $CONF.c $CONF.root.h $LIBNAMES + $CC $CFLAGS '-DKERNDATE='$KERNDATE $CONF.c + $LD $LDFLAGS -o $target $OBJ $CONF.$O $LIBFILES $SYSLIBS + +install:V: $O.$CONF + cp $O.$CONF $INSTALLDIR/$CONF diff --git a/emu/Unixware/mkfile-Unixware b/emu/Unixware/mkfile-Unixware new file mode 100644 index 00000000..52a8977d --- /dev/null +++ b/emu/Unixware/mkfile-Unixware @@ -0,0 +1,15 @@ +# +# architecture-dependent files for Unixware +# + +TARGFILES=asm-Unixware-$OBJTYPE.$O\ + devfs-posix.$O\ + devip.$O\ + ipif-posix.$O\ + os-Unixware.$O\ + win-x11.$O\ + deveia-Unixware.$O\ + srv.$O\ + lock.$O\ + +SYSLIBS= -lm -lX11 -lsocket -lnsl -lresolv -lxti -Kthread diff --git a/emu/Unixware/os.c b/emu/Unixware/os.c new file mode 100644 index 00000000..6493aa87 --- /dev/null +++ b/emu/Unixware/os.c @@ -0,0 +1,438 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#undef _POSIX_C_SOURCE +#undef getwd +#include <unistd.h> +#include <thread.h> +#include <time.h> +#include <termios.h> +#include <signal.h> +#include <pwd.h> +#include <sys/resource.h> +#include <sys/time.h> + +enum +{ + DELETE = 0x7F +}; +char *hosttype = "Unixware"; + +static thread_key_t prdakey; + +static siginfo_t siginfo; + +extern int dflag; + +Proc* +getup(void) +{ + void *vp; + + if (thr_getspecific(prdakey, &vp)) + return nil; + return vp; +} + +void +pexit(char *msg, int t) +{ + Osenv *e; + + lock(&procs.l); + if(up->prev) + up->prev->next = up->next; + else + procs.head = up->next; + + if(up->next) + up->next->prev = up->prev; + else + procs.tail = up->prev; + unlock(&procs.l); + + /*print("pexit: %s: %s\n", up->text, msg);*/ + e = up->env; + if(e != nil) { + closefgrp(e->fgrp); + closepgrp(e->pgrp); + closeegrp(e->egrp); + closesigs(e->sigs); + } + free(up->prog); + sema_destroy(up->os); + free(up->os); + free(up); + thr_exit(0); +} + +static void * +tramp(void *v) +{ + struct Proc *Up; + + if(thr_setspecific(prdakey, v)) { + print("set specific data failed in tramp\n"); + thr_exit(0); + } + Up = v; + Up->sigid = thr_self(); + Up->func(Up->arg); + pexit("", 0); +} + +int +kproc(char *name, void (*func)(void*), void *arg, int flags) +{ + thread_t thread; + Proc *p; + Pgrp *pg; + Fgrp *fg; + Egrp *eg; + sema_t *sem; + + p = newproc(); + + sem = malloc(sizeof(*sem)); + if(sem == nil) + panic("can't allocate semaphore"); + sema_init(sem, 0, USYNC_THREAD, 0); + p->os = sem; + + if(flags & KPDUPPG) { + pg = up->env->pgrp; + incref(&pg->r); + p->env->pgrp = pg; + } + if(flags & KPDUPFDG) { + fg = up->env->fgrp; + incref(&fg->r); + p->env->fgrp = fg; + } + if(flags & KPDUPENVG) { + eg = up->env->egrp; + incref(&eg->r); + p->env->egrp = eg; + } + + p->env->uid = up->env->uid; + p->env->gid = up->env->gid; + kstrdup(&p->env->user, up->env->user); + + strcpy(p->text, name); + + p->func = func; + p->arg = arg; + + lock(&procs.l); + if(procs.tail != nil) { + p->prev = procs.tail; + procs.tail->next = p; + } + else { + procs.head = p; + p->prev = nil; + } + procs.tail = p; + unlock(&procs.l); + + if(thr_create(0, 0, &tramp, p, THR_BOUND|THR_DETACHED, &thread)) + panic("thr_create failed\n"); + thr_yield(); + return(thread); +} + +/* to get pc on trap use siginfo.si_pc field and define all trap handlers + as printILL - have to set sa_sigaction, sa_flags not sa_handler +*/ + +static void +trapUSR1(void) +{ + int intwait; + + intwait = up->intwait; + up->intwait = 0; /* clear it to let proc continue in osleave */ + + if(up->type != Interp) /* Used to unblock pending I/O */ + return; + + if(intwait == 0) /* Not posted so it's a sync error */ + disfault(nil, Eintr); /* Should never happen */ +} + +static void +trapILL(void) +{ + disfault(nil, "Illegal instruction"); +} + +static void +printILL(int sig, siginfo_t *siginfo, void *v) +{ + panic("Illegal instruction with code=%d at address=%x, opcode=%x.\n", + siginfo->si_code, siginfo->si_addr,*(char*)siginfo->si_addr); +} + +static void +trapBUS(void) +{ + disfault(nil, "Bus error"); +} + +static void +trapSEGV(void) +{ + disfault(nil, "Segmentation violation"); +} + +static void +trapFPE(void) +{ + disfault(nil, "Floating point exception"); +} + +void +oshostintr(Proc *p) +{ + thr_kill(p->sigid, SIGUSR1); +} + +void +osblock(void) +{ + while(sema_wait(up->os)) + ; /* retry on signals */ +} + +void +osready(Proc *p) +{ + sema_post(p->os); +} + +void +oslongjmp(void *regs, osjmpbuf env, int val) +{ + USED(regs); + siglongjmp(env, val); +} + +static struct termios tinit; + +static void +termset(void) +{ + struct termios t; + + tcgetattr(0, &t); + tinit = t; + t.c_lflag &= ~(ICANON|ECHO|ISIG); + t.c_cc[VMIN] = 1; + t.c_cc[VTIME] = 0; + tcsetattr(0, TCSANOW, &t); +} + +static void +termrestore(void) +{ + tcsetattr(0, TCSANOW, &tinit); +} + +void +cleanexit(int x) +{ + USED(x); + + if(up->intwait) { + up->intwait = 0; + return; + } + + if(dflag == 0) + termrestore(); + exit(0); +} + +int gidnobody= -1, uidnobody= -1; + +void +getnobody(void) +{ + struct passwd *pwd; + + if (pwd=getpwnam("nobody")) { + uidnobody = pwd->pw_uid; + gidnobody = pwd->pw_gid; + } +} + +void +osreboot(char *file, char **argv) +{ + if(dflag == 0) + termrestore(); + execvp(file, argv); + panic("reboot failure"); +} + +void +libinit(char *imod) +{ + struct Proc *Up; + struct sigaction act; + struct passwd *pw; + char sys[64]; + + setsid(); + + if(dflag == 0) + termset(); + + gethostname(sys, sizeof(sys)); + kstrdup(&ossysname, sys); + getnobody(); + + memset(&act, 0 , sizeof(act)); + act.sa_handler=trapUSR1; + sigaction(SIGUSR1, &act, nil); + /* + * For the correct functioning of devcmd in the + * face of exiting slaves + */ + signal(SIGPIPE, SIG_IGN); + if(signal(SIGTERM, SIG_IGN) != SIG_IGN) + signal(SIGTERM, cleanexit); + if(sflag == 0) { + act.sa_handler = trapBUS; + sigaction(SIGBUS, &act, nil); + act.sa_handler = trapILL; + sigaction(SIGILL, &act, nil); + act.sa_handler = trapSEGV; + sigaction(SIGSEGV, &act, nil); + act.sa_handler = trapFPE; + sigaction(SIGFPE, &act, nil); + if(signal(SIGINT, SIG_IGN) != SIG_IGN) + signal(SIGINT, cleanexit); + } else{ + act.sa_sigaction = printILL; + act.sa_flags=SA_SIGINFO; + sigaction(SIGILL, &act, nil); + } + + if(thr_keycreate(&prdakey,NULL)) + print("keycreate failed\n"); + + Up = newproc(); + if(thr_setspecific(prdakey,Up)) + panic("set specific thread data failed\n"); + + pw = getpwuid(getuid()); + if(pw != nil) + kstrdup(&eve, pw->pw_name); + else + print("cannot getpwuid\n"); + + up->env->uid = getuid(); + up->env->gid = getgid(); + emuinit(imod); +} + +int +readkbd(void) +{ + int n; + char buf[1]; + + n = read(0, buf, sizeof(buf)); + if(n < 0) + fprint(2, "keyboard read: %s\n", strerror(errno)); + if(n <= 0) + pexit("keyboard thread", 0); + + switch(buf[0]) { + case '\r': + buf[0] = '\n'; + break; + case DELETE: + cleanexit(0); + break; + } + return buf[0]; +} + +/* + * Return an abitrary millisecond clock time + */ +long +osmillisec(void) +{ + static long sec0 = 0, usec0; + struct timeval t; + + if(gettimeofday(&t, NULL)<0) + return(0); + if(sec0==0){ + sec0 = t.tv_sec; + usec0 = t.tv_usec; + } + return((t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000); +} + +/* + * Return the time since the epoch in microseconds + * The epoch is defined at 1 Jan 1970 + */ +vlong +osnsec(void) +{ + struct timeval t; + + gettimeofday(&t, nil); + return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000; +} + +vlong +osusectime(void) +{ + struct timeval t; + + gettimeofday(&t, nil); + return (vlong)t.tv_sec * 1000000 + t.tv_usec; +} + +int +osmillisleep(ulong milsec) +{ + struct timespec time; + + time.tv_sec = milsec/1000; + time.tv_nsec= (milsec%1000)*1000000; + nanosleep(&time,nil); + return 0; +} + +int +limbosleep(ulong milsec) +{ + return osmillisleep(milsec); +} + +void +osyield(void) +{ + thr_yield(); +} + +void +ospause(void) +{ + for(;;) + pause(); +} + +void +oslopri(void) +{ + setpriority(PRIO_PROCESS, 0, getpriority(PRIO_PROCESS,0)+4); +} diff --git a/emu/mkfile b/emu/mkfile new file mode 100644 index 00000000..779e5f16 --- /dev/null +++ b/emu/mkfile @@ -0,0 +1,19 @@ +<../mkconfig + +all:V: all-$HOSTMODEL +install:V: install-$HOSTMODEL +safeinstall:V: safeinstall-$HOSTMODEL +clean:V: clean-$HOSTMODEL +nuke:V: nuke-$HOSTMODEL + +&-Posix:QV: + echo "(cd $SYSTARG; mk $MKFLAGS $stem)" + (cd $SYSTARG; mk $MKFLAGS $stem) || exit 1 + +&-Nt:QV: + echo '@{builtin cd' $SYSTARG '; mk $MKFLAGS $stem}' + @{builtin cd $SYSTARG; mk $MKFLAGS $stem } + +&-Plan9:QV: + echo '@{builtin cd' $SYSTARG '; mk $MKFLAGS $stem}' + @{builtin cd $SYSTARG; mk $MKFLAGS $stem } diff --git a/emu/port/acme-offset b/emu/port/acme-offset new file mode 100644 index 00000000..4f083732 --- /dev/null +++ b/emu/port/acme-offset @@ -0,0 +1 @@ +X ,x/b?(read|write)\(.*ulong.*\)$/v/(bread|bwrite)/x/ulong offset/c/vlong offset diff --git a/emu/port/alloc.c b/emu/port/alloc.c new file mode 100644 index 00000000..e2206c3f --- /dev/null +++ b/emu/port/alloc.c @@ -0,0 +1,1021 @@ +#include "dat.h" +#include "fns.h" +#include "interp.h" +#include "error.h" + +enum +{ + MAXPOOL = 4 +}; + +#define left u.s.bhl +#define right u.s.bhr +#define fwd u.s.bhf +#define prev u.s.bhv +#define parent u.s.bhp + +#define RESERVED 512*1024 + +struct Pool +{ + char* name; + int pnum; + ulong maxsize; + int quanta; + int chunk; + int monitor; + ulong ressize; /* restricted size */ + ulong cursize; + ulong arenasize; + ulong hw; + Lock l; + Bhdr* root; + Bhdr* chain; + ulong nalloc; + ulong nfree; + int nbrk; + int lastfree; + void (*move)(void*, void*); +}; + +void* initbrk(ulong); + +struct +{ + int n; + Pool pool[MAXPOOL]; + /* Lock l; */ +} table = { + 3, + { + { "main", 0, 32*1024*1024, 31, 512*1024, 0, 31*1024*1024 }, + { "heap", 1, 32*1024*1024, 31, 512*1024, 0, 31*1024*1024 }, + { "image", 2, 32*1024*1024+256, 31, 4*1024*1024, 1, 31*1024*1024 }, + } +}; + +Pool* mainmem = &table.pool[0]; +Pool* heapmem = &table.pool[1]; +Pool* imagmem = &table.pool[2]; + +static void _auditmemloc(char *, void *); +void (*auditmemloc)(char *, void *) = _auditmemloc; +static void _poolfault(void *, char *, ulong); +void (*poolfault)(void *, char *, ulong) = _poolfault; + +/* non tracing + * +enum { + Npadlong = 0, + MallocOffset = 0, + ReallocOffset = 0 +}; + * + */ + +/* tracing */ +enum { + Npadlong = 2, + MallocOffset = 0, + ReallocOffset = 1 +}; + +enum { + Monitor = 1 +}; + +void (*memmonitor)(int, ulong, ulong, ulong) = nil; +#define MM(v,pc,base,size) if(!Monitor || memmonitor==nil){} else memmonitor((v),(pc),(base),(size)) + +#define CKLEAK 0 +int ckleak; +#define ML(v, sz, pc) if(CKLEAK && ckleak && v){ if(sz) fprint(2, "%lux %lux %lux\n", (ulong)v, (ulong)sz, (ulong)pc); else fprint(2, "%lux\n", (ulong)v); } + +int +memusehigh(void) +{ + return mainmem->cursize > mainmem->ressize || + heapmem->cursize > heapmem->ressize || + 0 && imagmem->cursize > imagmem->ressize; +} + +int +memlow(void) +{ + return heapmem->cursize > (heapmem->maxsize)/2; +} + +int +poolsetsize(char *s, int size) +{ + int i; + + for(i = 0; i < table.n; i++) { + if(strcmp(table.pool[i].name, s) == 0) { + table.pool[i].maxsize = size; + table.pool[i].ressize = size-RESERVED; + if(size < RESERVED) + panic("not enough memory"); + return 1; + } + } + return 0; +} + +void +poolimmutable(void *v) +{ + Bhdr *b; + + D2B(b, v); + b->magic = MAGIC_I; +} + +void +poolmutable(void *v) +{ + Bhdr *b; + + D2B(b, v); + b->magic = MAGIC_A; + ((Heap*)v)->color = mutator; +} + +char* +poolname(Pool *p) +{ + return p->name; +} + +Bhdr* +poolchain(Pool *p) +{ + return p->chain; +} + +void +pooldel(Pool *p, Bhdr *t) +{ + Bhdr *s, *f, *rp, *q; + + if(t->parent == nil && p->root != t) { + t->prev->fwd = t->fwd; + t->fwd->prev = t->prev; + return; + } + + if(t->fwd != t) { + f = t->fwd; + s = t->parent; + f->parent = s; + if(s == nil) + p->root = f; + else { + if(s->left == t) + s->left = f; + else + s->right = f; + } + + rp = t->left; + f->left = rp; + if(rp != nil) + rp->parent = f; + rp = t->right; + f->right = rp; + if(rp != nil) + rp->parent = f; + + t->prev->fwd = t->fwd; + t->fwd->prev = t->prev; + return; + } + + if(t->left == nil) + rp = t->right; + else { + if(t->right == nil) + rp = t->left; + else { + f = t; + rp = t->right; + s = rp->left; + while(s != nil) { + f = rp; + rp = s; + s = rp->left; + } + if(f != t) { + s = rp->right; + f->left = s; + if(s != nil) + s->parent = f; + s = t->right; + rp->right = s; + if(s != nil) + s->parent = rp; + } + s = t->left; + rp->left = s; + s->parent = rp; + } + } + q = t->parent; + if(q == nil) + p->root = rp; + else { + if(t == q->left) + q->left = rp; + else + q->right = rp; + } + if(rp != nil) + rp->parent = q; +} + +void +pooladd(Pool *p, Bhdr *q) +{ + int size; + Bhdr *tp, *t; + + q->magic = MAGIC_F; + + q->left = nil; + q->right = nil; + q->parent = nil; + q->fwd = q; + q->prev = q; + + t = p->root; + if(t == nil) { + p->root = q; + return; + } + + size = q->size; + + tp = nil; + while(t != nil) { + if(size == t->size) { + q->prev = t->prev; + q->prev->fwd = q; + q->fwd = t; + t->prev = q; + return; + } + tp = t; + if(size < t->size) + t = t->left; + else + t = t->right; + } + + q->parent = tp; + if(size < tp->size) + tp->left = q; + else + tp->right = q; +} + +static void* +dopoolalloc(Pool *p, ulong asize, ulong pc) +{ + Bhdr *q, *t; + int alloc, ldr, ns, frag; + int osize, size; + + if(asize >= 1024*1024*1024) /* for sanity and to avoid overflow */ + return nil; + size = asize; + osize = size; + size = (size + BHDRSIZE + p->quanta) & ~(p->quanta); + + lock(&p->l); + p->nalloc++; + + t = p->root; + q = nil; + while(t) { + if(t->size == size) { + t = t->fwd; + pooldel(p, t); + t->magic = MAGIC_A; + p->cursize += t->size; + if(p->cursize > p->hw) + p->hw = p->cursize; + unlock(&p->l); + if(p->monitor) + MM(p->pnum, pc, (ulong)B2D(t), size); + return B2D(t); + } + if(size < t->size) { + q = t; + t = t->left; + } + else + t = t->right; + } + if(q != nil) { + pooldel(p, q); + q->magic = MAGIC_A; + frag = q->size - size; + if(frag < (size>>2) && frag < 0x8000) { + p->cursize += q->size; + if(p->cursize > p->hw) + p->hw = p->cursize; + unlock(&p->l); + if(p->monitor) + MM(p->pnum, pc, (ulong)B2D(q), size); + return B2D(q); + } + /* Split */ + ns = q->size - size; + q->size = size; + B2T(q)->hdr = q; + t = B2NB(q); + t->size = ns; + B2T(t)->hdr = t; + pooladd(p, t); + p->cursize += q->size; + if(p->cursize > p->hw) + p->hw = p->cursize; + unlock(&p->l); + if(p->monitor) + MM(p->pnum, pc, (ulong)B2D(q), size); + return B2D(q); + } + + ns = p->chunk; + if(size > ns) + ns = size; + ldr = p->quanta+1; + + alloc = ns+ldr+ldr; + p->arenasize += alloc; + if(p->arenasize > p->maxsize) { + p->arenasize -= alloc; + ns = p->maxsize-p->arenasize-ldr-ldr; + ns &= ~p->quanta; + if (ns < size) { + if(poolcompact(p)) { + unlock(&p->l); + return poolalloc(p, osize); + } + + unlock(&p->l); + print("arena %s too large: size %d cursize %lud arenasize %lud maxsize %lud\n", + p->name, size, p->cursize, p->arenasize, p->maxsize); + return nil; + } + alloc = ns+ldr+ldr; + p->arenasize += alloc; + } + + p->nbrk++; + t = (Bhdr *)sbrk(alloc); + if(t == (void*)-1) { + p->nbrk--; + unlock(&p->l); + return nil; + } + /* Double alignment */ + t = (Bhdr *)(((ulong)t + 7) & ~7); + + if(p->chain != nil && (char*)t-(char*)B2LIMIT(p->chain)-ldr == 0){ + /* can merge chains */ + if(0)print("merging chains %p and %p in %s\n", p->chain, t, p->name); + q = B2LIMIT(p->chain); + q->magic = MAGIC_A; + q->size = alloc; + B2T(q)->hdr = q; + t = B2NB(q); + t->magic = MAGIC_E; + p->chain->csize += alloc; + p->cursize += alloc; + unlock(&p->l); + poolfree(p, B2D(q)); /* for backward merge */ + return poolalloc(p, osize); + } + + t->magic = MAGIC_E; /* Make a leader */ + t->size = ldr; + t->csize = ns+ldr; + t->clink = p->chain; + p->chain = t; + B2T(t)->hdr = t; + t = B2NB(t); + + t->magic = MAGIC_A; /* Make the block we are going to return */ + t->size = size; + B2T(t)->hdr = t; + q = t; + + ns -= size; /* Free the rest */ + if(ns > 0) { + q = B2NB(t); + q->size = ns; + B2T(q)->hdr = q; + pooladd(p, q); + } + B2NB(q)->magic = MAGIC_E; /* Mark the end of the chunk */ + + p->cursize += t->size; + if(p->cursize > p->hw) + p->hw = p->cursize; + unlock(&p->l); + if(p->monitor) + MM(p->pnum, pc, (ulong)B2D(t), size); + return B2D(t); +} + +void * +poolalloc(Pool *p, ulong asize) +{ + Prog *prog; + + if(p->cursize > p->ressize && (prog = currun()) != nil && prog->flags&Prestricted) + return nil; + return dopoolalloc(p, asize, getcallerpc(&p)); +} + +void +poolfree(Pool *p, void *v) +{ + Bhdr *b, *c; + extern Bhdr *ptr; + + D2B(b, v); + if(p->monitor) + MM(p->pnum|(1<<8), getcallerpc(&p), (ulong)v, b->size); + + lock(&p->l); + p->nfree++; + p->cursize -= b->size; + c = B2NB(b); + if(c->magic == MAGIC_F) { /* Join forward */ + if(c == ptr) + ptr = b; + pooldel(p, c); + c->magic = 0; + b->size += c->size; + B2T(b)->hdr = b; + } + + c = B2PT(b)->hdr; + if(c->magic == MAGIC_F) { /* Join backward */ + if(b == ptr) + ptr = c; + pooldel(p, c); + b->magic = 0; + c->size += b->size; + b = c; + B2T(b)->hdr = b; + } + pooladd(p, b); + unlock(&p->l); +} + +void * +poolrealloc(Pool *p, void *v, ulong size) +{ + Bhdr *b; + void *nv; + int osize; + + if(size >= 1024*1024*1024) /* for sanity and to avoid overflow */ + return nil; + if(size == 0){ + poolfree(p, v); + return nil; + } + SET(osize); + if(v != nil){ + lock(&p->l); + D2B(b, v); + osize = b->size - BHDRSIZE; + unlock(&p->l); + if(osize >= size) + return v; + } + nv = poolalloc(p, size); + if(nv != nil && v != nil){ + memmove(nv, v, osize); + poolfree(p, v); + } + return nv; +} + +ulong +poolmsize(Pool *p, void *v) +{ + Bhdr *b; + ulong size; + + if(v == nil) + return 0; + lock(&p->l); + D2B(b, v); + size = b->size - BHDRSIZE; + unlock(&p->l); + return size; +} + +static ulong +poolmax(Pool *p) +{ + Bhdr *t; + ulong size; + + lock(&p->l); + size = p->maxsize - p->cursize; + t = p->root; + if(t != nil) { + while(t->right != nil) + t = t->right; + if(size < t->size) + size = t->size; + } + if(size >= BHDRSIZE) + size -= BHDRSIZE; + unlock(&p->l); + return size; +} + +ulong +poolmaxsize(void) +{ + int i; + ulong total; + + total = 0; + for(i = 0; i < nelem(table.pool); i++) + total += table.pool[i].maxsize; + return total; +} + +int +poolread(char *va, int count, ulong offset) +{ + Pool *p; + int n, i, signed_off; + + n = 0; + signed_off = offset; + for(i = 0; i < table.n; i++) { + p = &table.pool[i]; + n += snprint(va+n, count-n, "%11lud %11lud %11lud %11lud %11lud %11d %11lud %s\n", + p->cursize, + p->maxsize, + p->hw, + p->nalloc, + p->nfree, + p->nbrk, + poolmax(p), + p->name); + + if(signed_off > 0) { + signed_off -= n; + if(signed_off < 0) { + memmove(va, va+n+signed_off, -signed_off); + n = -signed_off; + } + else + n = 0; + } + + } + return n; +} + +void* +smalloc(size_t size) +{ + void *v; + + for(;;){ + v = malloc(size); + if(v != nil) + break; + if(0) + print("smalloc waiting from %lux\n", getcallerpc(&size)); + osenter(); + osmillisleep(100); + osleave(); + } + setmalloctag(v, getcallerpc(&size)); + setrealloctag(v, 0); + return v; +} + +void* +kmalloc(size_t size) +{ + void *v; + + v = dopoolalloc(mainmem, size+Npadlong*sizeof(ulong), getcallerpc(&size)); + if(v != nil){ + ML(v, size, getcallerpc(&size)); + if(Npadlong){ + v = (ulong*)v+Npadlong; + setmalloctag(v, getcallerpc(&size)); + setrealloctag(v, 0); + } + memset(v, 0, size); + MM(0, getcallerpc(&size), (ulong)v, size); + } + return v; +} + + + +void* +malloc(size_t size) +{ + void *v; + + v = poolalloc(mainmem, size+Npadlong*sizeof(ulong)); + if(v != nil){ + ML(v, size, getcallerpc(&size)); + if(Npadlong){ + v = (ulong*)v+Npadlong; + setmalloctag(v, getcallerpc(&size)); + setrealloctag(v, 0); + } + memset(v, 0, size); + MM(0, getcallerpc(&size), (ulong)v, size); + } else + print("malloc failed from %lux\n", getcallerpc(&size)); + return v; +} + +void* +mallocz(ulong size, int clr) +{ + void *v; + + v = poolalloc(mainmem, size+Npadlong*sizeof(ulong)); + if(v != nil){ + ML(v, size, getcallerpc(&size)); + if(Npadlong){ + v = (ulong*)v+Npadlong; + setmalloctag(v, getcallerpc(&size)); + setrealloctag(v, 0); + } + if(clr) + memset(v, 0, size); + MM(0, getcallerpc(&size), (ulong)v, size); + } else + print("mallocz failed from %lux\n", getcallerpc(&size)); + return v; +} + +void +free(void *v) +{ + Bhdr *b; + + if(v != nil) { + if(Npadlong) + v = (ulong*)v-Npadlong; + D2B(b, v); + ML(v, 0, 0); + MM(1<<8|0, getcallerpc(&v), (ulong)((ulong*)v+Npadlong), b->size); + poolfree(mainmem, v); + } +} + +void* +realloc(void *v, size_t size) +{ + void *nv; + + if(size == 0) + return malloc(size); /* temporary change until realloc calls can be checked */ + if(v != nil) + v = (ulong*)v-Npadlong; + if(Npadlong!=0 && size!=0) + size += Npadlong*sizeof(ulong); + nv = poolrealloc(mainmem, v, size); + ML(v, 0, 0); + ML(nv, size, getcallerpc(&v)); + if(nv != nil) { + nv = (ulong*)nv+Npadlong; + setrealloctag(nv, getcallerpc(&v)); + if(v == nil) + setmalloctag(v, getcallerpc(&v)); + } else + print("realloc failed from %lux\n", getcallerpc(&v)); + return nv; +} + +void +setmalloctag(void *v, ulong pc) +{ + ulong *u; + + USED(v); + USED(pc); + if(Npadlong <= MallocOffset || v == nil) + return; + u = v; + u[-Npadlong+MallocOffset] = pc; +} + +ulong +getmalloctag(void *v) +{ + USED(v); + if(Npadlong <= MallocOffset) + return ~0; + return ((ulong*)v)[-Npadlong+MallocOffset]; +} + +void +setrealloctag(void *v, ulong pc) +{ + ulong *u; + + USED(v); + USED(pc); + if(Npadlong <= ReallocOffset || v == nil) + return; + u = v; + u[-Npadlong+ReallocOffset] = pc; +} + +ulong +getrealloctag(void *v) +{ + USED(v); + if(Npadlong <= ReallocOffset) + return ((ulong*)v)[-Npadlong+ReallocOffset]; + return ~0; +} + +ulong +msize(void *v) +{ + if(v == nil) + return 0; + return poolmsize(mainmem, (ulong*)v-Npadlong)-Npadlong*sizeof(ulong); +} + +void* +calloc(size_t n, size_t szelem) +{ + return malloc(n*szelem); +} + +/* +void +pooldump(Pool *p) +{ + Bhdr *b, *base, *limit, *ptr; + + b = p->chain; + if(b == nil) + return; + base = b; + ptr = b; + limit = B2LIMIT(b); + + while(base != nil) { + print("\tbase #%.8lux ptr #%.8lux", base, ptr); + if(ptr->magic == MAGIC_A || ptr->magic == MAGIC_I) + print("\tA%.5d\n", ptr->size); + else if(ptr->magic == MAGIC_E) + print("\tE\tL#%.8lux\tS#%.8lux\n", ptr->clink, ptr->csize); + else + print("\tF%.5d\tL#%.8lux\tR#%.8lux\tF#%.8lux\tP#%.8lux\tT#%.8lux\n", + ptr->size, ptr->left, ptr->right, ptr->fwd, ptr->prev, ptr->parent); + ptr = B2NB(ptr); + if(ptr >= limit) { + print("link to #%.8lux\n", base->clink); + base = base->clink; + if(base == nil) + break; + ptr = base; + limit = B2LIMIT(base); + } + } +} +*/ + +void +poolsetcompact(Pool *p, void (*move)(void*, void*)) +{ + p->move = move; +} + +int +poolcompact(Pool *pool) +{ + Bhdr *base, *limit, *ptr, *end, *next; + int compacted, nb; + + if(pool->move == nil || pool->lastfree == pool->nfree) + return 0; + + pool->lastfree = pool->nfree; + + base = pool->chain; + ptr = B2NB(base); /* First Block in arena has clink */ + limit = B2LIMIT(base); + compacted = 0; + + pool->root = nil; + end = ptr; + while(base != nil) { + next = B2NB(ptr); + if(ptr->magic == MAGIC_A || ptr->magic == MAGIC_I) { + if(ptr != end) { + memmove(end, ptr, ptr->size); + pool->move(B2D(ptr), B2D(end)); + compacted = 1; + } + end = B2NB(end); + } + if(next >= limit) { + nb = (uchar*)limit - (uchar*)end; + if(nb > 0){ + if(nb < pool->quanta+1){ + print("poolcompact: leftover too small\n"); + abort(); + } + end->size = nb; + B2T(end)->hdr = end; + pooladd(pool, end); + } + base = base->clink; + if(base == nil) + break; + ptr = B2NB(base); + end = ptr; /* could do better by copying between chains */ + limit = B2LIMIT(base); + } else + ptr = next; + } + + return compacted; +} + +static void +_poolfault(void *v, char *msg, ulong c) +{ + auditmemloc(msg, v); + panic("%s %lux (from %lux/%lux)", msg, v, getcallerpc(&v), c); +} + +static void +dumpvl(char *msg, ulong *v, int n) +{ + int i, l; + + l = print("%s at %p: ", msg, v); + for (i = 0; i < n; i++) { + if (l >= 60) { + print("\n"); + l = print(" %p: ", v); + } + l += print(" %lux", *v++); + } + print("\n"); +} + +static void +corrupted(char *str, char *msg, Pool *p, Bhdr *b, void *v) +{ + print("%s(%p): pool %s CORRUPT: %s at %p'%lud(magic=%lux)\n", + str, v, p->name, msg, b, b->size, b->magic); + dumpvl("bad Bhdr", (ulong *)((ulong)b & ~3)-4, 10); +} + +static void +_auditmemloc(char *str, void *v) +{ + Pool *p; + Bhdr *bc, *ec, *b, *nb, *fb = nil; + char *fmsg, *msg; + ulong fsz; + + SET(fsz); + SET(fmsg); + for (p = &table.pool[0]; p < &table.pool[nelem(table.pool)]; p++) { + lock(&p->l); + for (bc = p->chain; bc != nil; bc = bc->clink) { + if (bc->magic != MAGIC_E) { + unlock(&p->l); + corrupted(str, "chain hdr!=MAGIC_E", p, bc, v); + goto nextpool; + } + ec = B2LIMIT(bc); + if (((Bhdr*)v >= bc) && ((Bhdr*)v < ec)) + goto found; + } + unlock(&p->l); +nextpool: ; + } + print("%s: %p not in pools\n", str, v); + return; + +found: + for (b = bc; b < ec; b = nb) { + switch(b->magic) { + case MAGIC_F: + msg = "free blk"; + break; + case MAGIC_I: + msg = "immutable block"; + break; + case MAGIC_A: + msg = "block"; + break; + default: + if (b == bc && b->magic == MAGIC_E) { + msg = "pool hdr"; + break; + } + unlock(&p->l); + corrupted(str, "bad magic", p, b, v); + goto badchunk; + } + if (b->size <= 0 || (b->size & p->quanta)) { + unlock(&p->l); + corrupted(str, "bad size", p, b, v); + goto badchunk; + } + if (fb != nil) + break; + nb = B2NB(b); + if ((Bhdr*)v < nb) { + fb = b; + fsz = b->size; + fmsg = msg; + } + } + unlock(&p->l); + if (b >= ec) { + if (b > ec) + corrupted(str, "chain size mismatch", p, b, v); + else if (b->magic != MAGIC_E) + corrupted(str, "chain end!=MAGIC_E", p, b, v); + } +badchunk: + if (fb != nil) { + print("%s: %p in %s:", str, v, p->name); + if (fb == v) + print(" is %s '%lux\n", fmsg, fsz); + else + print(" in %s at %p'%lux\n", fmsg, fb, fsz); + dumpvl("area", (ulong *)((ulong)v & ~3)-4, 20); + } +} + +char * +poolaudit(char*(*audit)(int, Bhdr *)) +{ + Pool *p; + Bhdr *bc, *ec, *b; + char *r = nil; + + for (p = &table.pool[0]; p < &table.pool[nelem(table.pool)]; p++) { + lock(&p->l); + for (bc = p->chain; bc != nil; bc = bc->clink) { + if (bc->magic != MAGIC_E) { + unlock(&p->l); + return "bad chain hdr"; + } + ec = B2LIMIT(bc); + for (b = bc; b < ec; b = B2NB(b)) { + if (b->size <= 0 || (b->size & p->quanta)) + r = "bad size in bhdr"; + else + switch(b->magic) { + case MAGIC_E: + if (b != bc) { + r = "unexpected MAGIC_E"; + break; + } + case MAGIC_F: + case MAGIC_A: + case MAGIC_I: + r = audit(p->pnum, b); + break; + default: + r = "bad magic"; + } + if (r != nil) { + unlock(&p->l); + return r; + } + } + if (b != ec || b->magic != MAGIC_E) { + unlock(&p->l); + return "bad chain ending"; + } + } + unlock(&p->l); + } + return r; +} diff --git a/emu/port/audio-tbls.c b/emu/port/audio-tbls.c new file mode 100644 index 00000000..ed84bec6 --- /dev/null +++ b/emu/port/audio-tbls.c @@ -0,0 +1,53 @@ +svp_t audio_bits_tbl[] = { + { "8", 8 } , /* 8 bits per sample */ + { "16", 16 }, /* 16 bits per sample */ + {nil}, +}; + +svp_t audio_chan_tbl[] = { + { "1", 1 }, /* 1 channel */ + { "2", 2 }, /* 2 channels */ + {nil}, +}; + +svp_t audio_indev_tbl[] = { + { "mic", Audio_Mic_Val }, /* input microphone */ + { "line", Audio_Linein_Val }, /* line in */ + {nil}, +}; + +svp_t audio_outdev_tbl[] = { + { "spkr", Audio_Speaker_Val }, /* output speaker */ + { "hdph", Audio_Headphone_Val },/* head phones */ + { "line", Audio_Lineout_Val }, /* line out */ + {nil}, +}; + +svp_t audio_enc_tbl[] = { + { "ulaw", Audio_Ulaw_Val }, /* u-law encoding */ + { "alaw", Audio_Alaw_Val }, /* A-law encoding */ + { "pcm", Audio_Pcm_Val }, /* Pulse Code Modulation */ + {nil}, +}; + +svp_t audio_rate_tbl[] = { + { "8000", 8000 }, /* 8000 samples per second */ + { "11025", 11025 }, /* 11025 samples per second */ + { "22050", 22050 }, /* 22050 samples per second */ + { "44100", 44100 }, /* 44100 samples per second */ + {nil}, +}; + +Audio_d Default_Audio_Format = { + 0, + 16, /* bits per sample */ + Audio_Max_Val, /* buffer size (as percentage) */ + 2, /* number of channels */ + -1, /* device */ + Audio_Pcm_Val, /* encoding format */ + 8000, /* samples per second */ + Audio_Max_Val, /* left channel gain */ + Audio_Max_Val, /* right channel gain */ +}; +int Default_Audio_Input = Audio_Mic_Val; +int Default_Audio_Output = Audio_Speaker_Val; diff --git a/emu/port/audio.h b/emu/port/audio.h new file mode 100644 index 00000000..5846cda8 --- /dev/null +++ b/emu/port/audio.h @@ -0,0 +1,81 @@ +#define AUDIO_BITS_FLAG 0x00000001 +#define AUDIO_BUF_FLAG 0x00000002 +#define AUDIO_CHAN_FLAG 0x00000004 +#define AUDIO_COUNT_FLAG 0x00000008 +#define AUDIO_DEV_FLAG 0x00000010 +#define AUDIO_ENC_FLAG 0x00000020 +#define AUDIO_RATE_FLAG 0x00000040 +#define AUDIO_VOL_FLAG 0x00000080 +#define AUDIO_LEFT_FLAG 0x00000100 +#define AUDIO_RIGHT_FLAG 0x00000200 +#define AUDIO_IN_FLAG 0x00000400 +#define AUDIO_OUT_FLAG 0x00000800 +#define AUDIO_MOD_FLAG 0x10000000 + +#define Audio_Min_Val 0 +#define Audio_Max_Val 100 + +#define Audio_No_Val 0 +#define Audio_In_Val 1 +#define Audio_Out_Val 2 + +#define Audio_Max_Buf 32768 +#define Bits_Per_Byte 8 + +typedef struct Audio_d Audio_d; +struct Audio_d { + ulong flags; /* bit flag for fields */ + ulong bits; /* bits per sample */ + ulong buf; /* buffer size */ + ulong chan; /* number of channels */ + ulong dev; /* device */ + ulong enc; /* encoding format */ + ulong rate; /* samples per second */ + ulong left; /* left channel gain */ + ulong right; /* right channel gain */ +}; + +typedef struct Audio_t Audio_t; +struct Audio_t { + Audio_d in; /* input device */ + Audio_d out; /* output device */ +}; + +#define AUDIO_CMD_MAXNUM 32 + +void audio_info_init(Audio_t*); +int audioparse(char*, int n, Audio_t*); + +enum +{ + Qdir = 0, /* must start at 0 representing a directory */ + Qaudio, + Qaudioctl +}; + +/* required external platform specific functions */ +void audio_file_init(void); +void audio_file_open(Chan*, int); +long audio_file_read(Chan*, void*, long, vlong); +long audio_file_write(Chan*, void*, long, vlong); +long audio_ctl_write(Chan*, void*, long, vlong); +void audio_file_close(Chan*); + +typedef struct _svp_t { + char* s; /* string */ + unsigned long v; /* value */ +} svp_t; + +/* string value pairs for default audio values */ +extern svp_t audio_chan_tbl[]; +extern svp_t audio_indev_tbl[]; +extern svp_t audio_outdev_tbl[]; +extern svp_t audio_enc_tbl[]; +extern svp_t audio_rate_tbl[]; +extern svp_t audio_val_tbl[]; +extern svp_t audio_bits_tbl[]; + +extern Audio_d Default_Audio_Format; +extern int Default_Audio_Input, Default_Audio_Output; + +extern Audio_t* getaudiodev(void); diff --git a/emu/port/cache.c b/emu/port/cache.c new file mode 100644 index 00000000..fff1c4e1 --- /dev/null +++ b/emu/port/cache.c @@ -0,0 +1,45 @@ +#include "dat.h" +#include "fns.h" + +/* + * no cache in hosted mode + */ +void +cinit(void) +{ +} + +void +copen(Chan *c) +{ + c->flag &= ~CCACHE; +} + +int +cread(Chan *c, uchar *b, int n, vlong off) +{ + USED(c); + USED(b); + USED(n); + USED(off); + return 0; +} + +void +cwrite(Chan *c, uchar *buf, int n, vlong off) +{ + USED(c); + USED(buf); + USED(n); + USED(off); +} + +void +cupdate(Chan *c, uchar *buf, int n, vlong off) +{ + USED(c); + USED(buf); + USED(n); + USED(off); +} + diff --git a/emu/port/chan.c b/emu/port/chan.c new file mode 100644 index 00000000..fc936b0f --- /dev/null +++ b/emu/port/chan.c @@ -0,0 +1,1403 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + +char* +c2name(Chan *c) /* DEBUGGING */ +{ + if(c == nil) + return "<nil chan>"; + if(c->name == nil) + return "<nil name>"; + if(c->name->s == nil) + return "<nil name.s>"; + return c->name->s; +} + +enum +{ + CNAMESLOP = 20 +}; + +struct +{ + Lock l; + int fid; + Chan *free; + Chan *list; +}chanalloc; + +typedef struct Elemlist Elemlist; + +struct Elemlist +{ + char *name; /* copy of name, so '/' can be overwritten */ + int nelems; + char **elems; + int *off; + int mustbedir; +}; + +#define SEP(c) ((c) == 0 || (c) == '/') +void cleancname(Cname*); + +int +isdotdot(char *p) +{ + return p[0]=='.' && p[1]=='.' && p[2]=='\0'; +} + +int +incref(Ref *r) +{ + int x; + + lock(&r->lk); + x = ++r->ref; + unlock(&r->lk); + return x; +} + +int +decref(Ref *r) +{ + int x; + + lock(&r->lk); + x = --r->ref; + unlock(&r->lk); + if(x < 0) + panic("decref, pc=0x%lux", getcallerpc(&r)); + + return x; +} + +/* + * Rather than strncpy, which zeros the rest of the buffer, kstrcpy + * truncates if necessary, always zero terminates, does not zero fill, + * and puts ... at the end of the string if it's too long. Usually used to + * save a string in up->genbuf; + */ +void +kstrcpy(char *s, char *t, int ns) +{ + int nt; + + nt = strlen(t); + if(nt+1 <= ns){ + memmove(s, t, nt+1); + return; + } + /* too long */ + if(ns < 4){ + /* but very short! */ + strncpy(s, t, ns); + return; + } + /* truncate with ... at character boundary (very rare case) */ + memmove(s, t, ns-4); + ns -= 4; + s[ns] = '\0'; + /* look for first byte of UTF-8 sequence by skipping continuation bytes */ + while(ns>0 && (s[--ns]&0xC0)==0x80) + ; + strcpy(s+ns, "..."); +} + +int +emptystr(char *s) +{ + return s == nil || s[0] == '\0'; +} + +/* + * Atomically replace *p with copy of s + */ +void +kstrdup(char **p, char *s) +{ + int n; + char *t, *prev; + + n = strlen(s)+1; + t = kmalloc(n); + if(t == nil) + panic("kstrdup: no memory"); + memmove(t, s, n); + prev = *p; + *p = t; + free(prev); +} + +static char isfrog[256]= +{ + /*NUL*/ 1, 1, 1, 1, 1, 1, 1, 1, + /*BKS*/ 1, 1, 1, 1, 1, 1, 1, 1, + /*DLE*/ 1, 1, 1, 1, 1, 1, 1, 1, + /*CAN*/ 1, 1, 1, 1, 1, 1, 1, 1 +}; + +void +chandevinit(void) +{ + int i; + + +/* isfrog[' '] = 1; */ /* let's see what happens */ + isfrog['/'] = 1; + isfrog[0x7f] = 1; + + for(i=0; devtab[i] != nil; i++) + devtab[i]->init(); +} + +Chan* +newchan(void) +{ + Chan *c; + + lock(&chanalloc.l); + c = chanalloc.free; + if(c != 0) + chanalloc.free = c->next; + unlock(&chanalloc.l); + + if(c == nil) { + c = malloc(sizeof(Chan)); + if(c == nil) + error(Enomem); + lock(&chanalloc.l); + c->fid = ++chanalloc.fid; + c->link = chanalloc.list; + chanalloc.list = c; + unlock(&chanalloc.l); + } + + /* if you get an error before associating with a dev, + close calls rootclose, a nop */ + c->type = 0; + c->flag = 0; + c->r.ref = 1; + c->dev = 0; + c->offset = 0; + c->iounit = 0; + c->umh = 0; + c->uri = 0; + c->dri = 0; + c->aux = 0; + c->mchan = 0; + c->mcp = 0; + c->mux = 0; + c->mqid.path = 0; + c->mqid.vers = 0; + c->mqid.type = 0; + c->name = 0; + return c; +} + +static Ref ncname; + +Cname* +newcname(char *s) +{ + Cname *n; + int i; + + n = smalloc(sizeof(Cname)); + i = strlen(s); + n->len = i; + n->alen = i+CNAMESLOP; + n->s = smalloc(n->alen); + memmove(n->s, s, i+1); + n->r.ref = 1; + incref(&ncname); + return n; +} + +void +cnameclose(Cname *n) +{ + if(n == nil) + return; + if(decref(&n->r)) + return; + decref(&ncname); + free(n->s); + free(n); +} + +Cname* +addelem(Cname *n, char *s) +{ + int i, a; + char *t; + Cname *new; + + if(s[0]=='.' && s[1]=='\0') + return n; + + if(n->r.ref > 1){ + /* copy on write */ + new = newcname(n->s); + cnameclose(n); + n = new; + } + + i = strlen(s); + if(n->len+1+i+1 > n->alen){ + a = n->len+1+i+1 + CNAMESLOP; + t = smalloc(a); + memmove(t, n->s, n->len+1); + free(n->s); + n->s = t; + n->alen = a; + } + if(n->len>0 && n->s[n->len-1]!='/' && s[0]!='/') /* don't insert extra slash if one is present */ + n->s[n->len++] = '/'; + memmove(n->s+n->len, s, i+1); + n->len += i; + if(isdotdot(s)) + cleancname(n); + return n; +} + +void +chanfree(Chan *c) +{ + c->flag = CFREE; + + if(c->umh != nil){ + putmhead(c->umh); + c->umh = nil; + } + if(c->umc != nil){ + cclose(c->umc); + c->umc = nil; + } + if(c->mux != nil){ + muxclose(c->mux); + c->mux = nil; + } + if(c->mchan != nil){ + cclose(c->mchan); + c->mchan = nil; + } + + cnameclose(c->name); + + lock(&chanalloc.l); + c->next = chanalloc.free; + chanalloc.free = c; + unlock(&chanalloc.l); +} + +void +cclose(Chan *c) +{ + if(c == 0) + return; + + if(c->flag&CFREE) + panic("cclose %lux", getcallerpc(&c)); + + if(decref(&c->r)) + return; + + if(!waserror()){ + devtab[c->type]->close(c); + poperror(); + } + + chanfree(c); +} + +/* + * Make sure we have the only copy of c. (Copy on write.) + */ +Chan* +cunique(Chan *c) +{ + Chan *nc; + + if(c->r.ref != 1) { + nc = cclone(c); + cclose(c); + c = nc; + } + + return c; +} + +int +eqqid(Qid a, Qid b) +{ + return a.path==b.path && a.vers==b.vers; +} + +int +eqchan(Chan *a, Chan *b, int pathonly) +{ + if(a->qid.path != b->qid.path) + return 0; + if(!pathonly && a->qid.vers!=b->qid.vers) + return 0; + if(a->type != b->type) + return 0; + if(a->dev != b->dev) + return 0; + return 1; +} + +int +eqchantdqid(Chan *a, int type, int dev, Qid qid, int pathonly) +{ + if(a->qid.path != qid.path) + return 0; + if(!pathonly && a->qid.vers!=qid.vers) + return 0; + if(a->type != type) + return 0; + if(a->dev != dev) + return 0; + return 1; +} + +Mhead* +newmhead(Chan *from) +{ + Mhead *mh; + + mh = smalloc(sizeof(Mhead)); + mh->r.ref = 1; + mh->from = from; + incref(&from->r); + +/* + n = from->name->len; + if(n >= sizeof(mh->fromname)) + n = sizeof(mh->fromname)-1; + memmove(mh->fromname, from->name->s, n); + mh->fromname[n] = 0; +*/ + return mh; +} + +int +cmount(Chan *new, Chan *old, int flag, char *spec) +{ + Pgrp *pg; + int order, flg; + Mhead *m, **l, *mh; + Mount *nm, *f, *um, **h; + + if(QTDIR & (old->qid.type^new->qid.type)) + error(Emount); + +if(old->umh) + print("cmount old extra umh\n"); + + order = flag&MORDER; + + if((old->qid.type&QTDIR)==0 && order != MREPL) + error(Emount); + + mh = new->umh; + + /* + * Not allowed to bind when the old directory + * is itself a union. (Maybe it should be allowed, but I don't see + * what the semantics would be.) + * + * We need to check mh->mount->next to tell unions apart from + * simple mount points, so that things like + * mount -c fd /root + * bind -c /root / + * work. The check of mount->mflag catches things like + * mount fd /root + * bind -c /root / + * + * This is far more complicated than it should be, but I don't + * see an easier way at the moment. -rsc + */ + if((flag&MCREATE) && mh && mh->mount + && (mh->mount->next || !(mh->mount->mflag&MCREATE))) + error(Emount); + + pg = up->env->pgrp; + wlock(&pg->ns); + + l = &MOUNTH(pg, old->qid); + for(m = *l; m; m = m->hash) { + if(eqchan(m->from, old, 1)) + break; + l = &m->hash; + } + + if(m == nil) { + /* + * nothing mounted here yet. create a mount + * head and add to the hash table. + */ + m = newmhead(old); + *l = m; + + /* + * if this is a union mount, add the old + * node to the mount chain. + */ + if(order != MREPL) + m->mount = newmount(m, old, 0, 0); + } + wlock(&m->lock); + if(waserror()){ + wunlock(&m->lock); + nexterror(); + } + wunlock(&pg->ns); + + nm = newmount(m, new, flag, spec); + if(mh != nil && mh->mount != nil) { + /* + * copy a union when binding it onto a directory + */ + flg = order; + if(order == MREPL) + flg = MAFTER; + h = &nm->next; + um = mh->mount; + for(um = um->next; um; um = um->next) { + f = newmount(m, um->to, flg, um->spec); + *h = f; + h = &f->next; + } + } + + if(m->mount && order == MREPL) { + mountfree(m->mount); + m->mount = 0; + } + + if(flag & MCREATE) + nm->mflag |= MCREATE; + + if(m->mount && order == MAFTER) { + for(f = m->mount; f->next; f = f->next) + ; + f->next = nm; + } + else { + for(f = nm; f->next; f = f->next) + ; + f->next = m->mount; + m->mount = nm; + } + + wunlock(&m->lock); + poperror(); + return nm->mountid; +} + +void +cunmount(Chan *mnt, Chan *mounted) +{ + Pgrp *pg; + Mhead *m, **l; + Mount *f, **p; + + if(mnt->umh) /* should not happen */ + print("cunmount newp extra umh %p has %p\n", mnt, mnt->umh); + + /* + * It _can_ happen that mounted->umh is non-nil, + * because mounted is the result of namec(Aopen) + * (see sysfile.c:/^sysunmount). + * If we open a union directory, it will have a umh. + * Although surprising, this is okay, since the + * cclose will take care of freeing the umh. + */ + + pg = up->env->pgrp; + wlock(&pg->ns); + + l = &MOUNTH(pg, mnt->qid); + for(m = *l; m; m = m->hash) { + if(eqchan(m->from, mnt, 1)) + break; + l = &m->hash; + } + + if(m == 0) { + wunlock(&pg->ns); + error(Eunmount); + } + + wlock(&m->lock); + if(mounted == 0) { + *l = m->hash; + wunlock(&pg->ns); + mountfree(m->mount); + m->mount = nil; + cclose(m->from); + wunlock(&m->lock); + putmhead(m); + return; + } + + p = &m->mount; + for(f = *p; f; f = f->next) { + /* BUG: Needs to be 2 pass */ + if(eqchan(f->to, mounted, 1) || + (f->to->mchan && eqchan(f->to->mchan, mounted, 1))) { + *p = f->next; + f->next = 0; + mountfree(f); + if(m->mount == nil) { + *l = m->hash; + cclose(m->from); + wunlock(&m->lock); + wunlock(&pg->ns); + putmhead(m); + return; + } + wunlock(&m->lock); + wunlock(&pg->ns); + return; + } + p = &f->next; + } + wunlock(&m->lock); + wunlock(&pg->ns); + error(Eunion); +} + +Chan* +cclone(Chan *c) +{ + Chan *nc; + Walkqid *wq; + + wq = devtab[c->type]->walk(c, nil, nil, 0); + if(wq == nil) + error("clone failed"); + nc = wq->clone; + free(wq); + nc->name = c->name; + if(c->name) + incref(&c->name->r); + return nc; +} + +int +findmount(Chan **cp, Mhead **mp, int type, int dev, Qid qid) +{ + Pgrp *pg; + Mhead *m; + + pg = up->env->pgrp; + rlock(&pg->ns); + for(m = MOUNTH(pg, qid); m; m = m->hash){ + rlock(&m->lock); +if(m->from == nil){ + print("m %p m->from 0\n", m); + runlock(&m->lock); + continue; +} + if(eqchantdqid(m->from, type, dev, qid, 1)) { + runlock(&pg->ns); + if(mp != nil){ + incref(&m->r); + if(*mp != nil) + putmhead(*mp); + *mp = m; + } + if(*cp != nil) + cclose(*cp); + incref(&m->mount->to->r); + *cp = m->mount->to; + runlock(&m->lock); + return 1; + } + runlock(&m->lock); + } + + runlock(&pg->ns); + return 0; +} + +int +domount(Chan **cp, Mhead **mp) +{ + return findmount(cp, mp, (*cp)->type, (*cp)->dev, (*cp)->qid); +} + +Chan* +undomount(Chan *c, Cname *name) +{ + Chan *nc; + Pgrp *pg; + Mount *t; + Mhead **h, **he, *f; + + pg = up->env->pgrp; + rlock(&pg->ns); + if(waserror()) { + runlock(&pg->ns); + nexterror(); + } + + he = &pg->mnthash[MNTHASH]; + for(h = pg->mnthash; h < he; h++) { + for(f = *h; f; f = f->hash) { + if(strcmp(f->from->name->s, name->s) != 0) + continue; + for(t = f->mount; t; t = t->next) { + if(eqchan(c, t->to, 1)) { + /* + * We want to come out on the left hand side of the mount + * point using the element of the union that we entered on. + * To do this, find the element that has a from name of + * c->name->s. + */ + if(strcmp(t->head->from->name->s, name->s) != 0) + continue; + nc = t->head->from; + incref(&nc->r); + cclose(c); + c = nc; + break; + } + } + } + } + poperror(); + runlock(&pg->ns); + return c; +} + +/* + * Either walks all the way or not at all. No partial results in *cp. + * *nerror is the number of names to display in an error message. + */ +static char Edoesnotexist[] = "does not exist"; +int +walk(Chan **cp, char **names, int nnames, int nomount, int *nerror) +{ + int dev, dotdot, i, n, nhave, ntry, type; + Chan *c, *nc; + Cname *cname; + Mount *f; + Mhead *mh, *nmh; + Walkqid *wq; + + c = *cp; + incref(&c->r); + cname = c->name; + incref(&cname->r); + mh = nil; + + /* + * While we haven't gotten all the way down the path: + * 1. step through a mount point, if any + * 2. send a walk request for initial dotdot or initial prefix without dotdot + * 3. move to the first mountpoint along the way. + * 4. repeat. + * + * An invariant is that each time through the loop, c is on the undomount + * side of the mount point, and c's name is cname. + */ + for(nhave=0; nhave<nnames; nhave+=n){ + if((c->qid.type&QTDIR)==0){ + if(nerror) + *nerror = nhave; + cnameclose(cname); + cclose(c); + strcpy(up->env->errstr, Enotdir); + if(mh != nil) + putmhead(mh); + return -1; + } + ntry = nnames - nhave; + if(ntry > MAXWELEM) + ntry = MAXWELEM; + dotdot = 0; + for(i=0; i<ntry; i++){ + if(isdotdot(names[nhave+i])){ + if(i==0) { + dotdot = 1; + ntry = 1; + } else + ntry = i; + break; + } + } + + if(!dotdot && !nomount) + domount(&c, &mh); + + type = c->type; + dev = c->dev; + + if((wq = devtab[type]->walk(c, nil, names+nhave, ntry)) == nil){ + /* try a union mount, if any */ + if(mh && !nomount){ + /* + * mh->mount == c, so start at mh->mount->next + */ + rlock(&mh->lock); + for(f = mh->mount->next; f; f = f->next) + if((wq = devtab[f->to->type]->walk(f->to, nil, names+nhave, ntry)) != nil) + break; + runlock(&mh->lock); + if(f != nil){ + type = f->to->type; + dev = f->to->dev; + } + } + if(wq == nil){ + cclose(c); + cnameclose(cname); + if(nerror) + *nerror = nhave+1; + if(mh != nil) + putmhead(mh); + return -1; + } + } + + nmh = nil; + if(dotdot) { + assert(wq->nqid == 1); + assert(wq->clone != nil); + + cname = addelem(cname, ".."); + nc = undomount(wq->clone, cname); + n = 1; + } else { + nc = nil; + if(!nomount) + for(i=0; i<wq->nqid && i<ntry-1; i++) + if(findmount(&nc, &nmh, type, dev, wq->qid[i])) + break; + if(nc == nil){ /* no mount points along path */ + if(wq->clone == nil){ + cclose(c); + cnameclose(cname); + if(wq->nqid==0 || (wq->qid[wq->nqid-1].type&QTDIR)){ + if(nerror) + *nerror = nhave+wq->nqid+1; + strcpy(up->env->errstr, Edoesnotexist); + }else{ + if(nerror) + *nerror = nhave+wq->nqid; + strcpy(up->env->errstr, Enotdir); + } + free(wq); + if(mh != nil) + putmhead(mh); + return -1; + } + n = wq->nqid; + nc = wq->clone; + }else{ /* stopped early, at a mount point */ + if(wq->clone != nil){ + cclose(wq->clone); + wq->clone = nil; + } + n = i+1; + } + for(i=0; i<n; i++) + cname = addelem(cname, names[nhave+i]); + } + cclose(c); + c = nc; + putmhead(mh); + mh = nmh; + free(wq); + } + + putmhead(mh); + + c = cunique(c); + + if(c->umh != nil){ /* BUG */ + print("walk umh\n"); + putmhead(c->umh); + c->umh = nil; + } + + cnameclose(c->name); + c->name = cname; + + cclose(*cp); + *cp = c; + if(nerror) + *nerror = 0; + return 0; +} + +/* + * c is a mounted non-creatable directory. find a creatable one. + */ +Chan* +createdir(Chan *c, Mhead *m) +{ + Chan *nc; + Mount *f; + + rlock(&m->lock); + if(waserror()) { + runlock(&m->lock); + nexterror(); + } + for(f = m->mount; f; f = f->next) { + if(f->mflag&MCREATE) { + nc = cclone(f->to); + runlock(&m->lock); + poperror(); + cclose(c); + return nc; + } + } + error(Enocreate); + return 0; +} + +/* + * In place, rewrite name to compress multiple /, eliminate ., and process .. + */ +void +cleancname(Cname *n) +{ + char *p; + + if(n->s[0] == '#'){ + p = strchr(n->s, '/'); + if(p == nil) + return; + cleanname(p); + + /* + * The correct name is #i rather than #i/, + * but the correct name of #/ is #/. + */ + if(strcmp(p, "/")==0 && n->s[1] != '/') + *p = '\0'; + }else + cleanname(n->s); + n->len = strlen(n->s); +} + +static void +growparse(Elemlist *e) +{ + char **new; + int *inew; + enum { Delta = 8 }; + + if(e->nelems % Delta == 0){ + new = smalloc((e->nelems+Delta) * sizeof(char*)); + memmove(new, e->elems, e->nelems*sizeof(char*)); + free(e->elems); + e->elems = new; + inew = smalloc((e->nelems+Delta+1) * sizeof(int)); + memmove(inew, e->off, e->nelems*sizeof(int)); + free(e->off); + e->off = inew; + } +} + +/* + * The name is known to be valid. + * Copy the name so slashes can be overwritten. + * An empty string will set nelem=0. + * A path ending in / or /. or /.//./ etc. will have + * e.mustbedir = 1, so that we correctly + * reject, e.g., "/adm/users/." when /adm/users is a file + * rather than a directory. + */ +static void +parsename(char *name, Elemlist *e) +{ + char *slash; + + kstrdup(&e->name, name); + name = e->name; + e->nelems = 0; + e->elems = nil; + e->off = smalloc(sizeof(int)); + e->off[0] = skipslash(name) - name; + for(;;){ + name = skipslash(name); + if(*name=='\0'){ + e->mustbedir = 1; + break; + } + growparse(e); + + e->elems[e->nelems++] = name; + slash = utfrune(name, '/'); + if(slash == nil){ + e->off[e->nelems] = name+strlen(name) - e->name; + e->mustbedir = 0; + break; + } + e->off[e->nelems] = slash - e->name; + *slash++ = '\0'; + name = slash; + } +} + +static void* +kmemrchr(void *va, int c, long n) +{ + uchar *a, *e; + + a = va; + for(e=a+n-1; e>a; e--) + if(*e == c) + return e; + return nil; +} + +static void +saveregisters(void) +{ +} + +/* + * Turn a name into a channel. + * &name[0] is known to be a valid address. It may be a kernel address. + * + * Opening with amode Aopen, Acreate, or Aremove guarantees + * that the result will be the only reference to that particular fid. + * This is necessary since we might pass the result to + * devtab[]->remove(). + * + * Opening Atodir, Amount, or Aaccess does not guarantee this. + * + * Opening Aaccess can, under certain conditions, return a + * correct Chan* but with an incorrect Cname attached. + * Since the functions that open Aaccess (sysstat, syswstat, sys_stat) + * do not use the Cname*, this avoids an unnecessary clone. + */ +Chan* +namec(char *aname, int amode, int omode, ulong perm) +{ + int n, prefix, len, t, nomount, npath; + Chan *c, *cnew; + Cname *cname; + Elemlist e; + Rune r; + Mhead *m; + char *createerr, tmperrbuf[ERRMAX]; + char *name; + + name = aname; + if(name[0] == '\0') + error("empty file name"); + validname(name, 1); + + /* + * Find the starting off point (the current slash, the root of + * a device tree, or the current dot) as well as the name to + * evaluate starting there. + */ + nomount = 0; + switch(name[0]){ + case '/': + c = up->env->pgrp->slash; + incref(&c->r); + break; + + case '#': + nomount = 1; + up->genbuf[0] = '\0'; + n = 0; + while(*name!='\0' && (*name != '/' || n < 2)){ + if(n >= sizeof(up->genbuf)-1) + error(Efilename); + up->genbuf[n++] = *name++; + } + up->genbuf[n] = '\0'; + n = chartorune(&r, up->genbuf+1)+1; + if(r == 'M') + error(Enoattach); + /* + * the nodevs exceptions are + * | it only gives access to pipes you create + * e this process's environment + * s private file2chan creation space + * D private secure sockets name space + * a private TLS name space + */ + if(up->env->pgrp->nodevs && + (utfrune("|esDa", r) == nil || r == 's' && up->genbuf[n]!='\0')) + error(Enoattach); + t = devno(r, 1); + if(t == -1) + error(Ebadsharp); + c = devtab[t]->attach(up->genbuf+n); + break; + + default: + c = up->env->pgrp->dot; + incref(&c->r); + break; + } + prefix = name - aname; + + e.name = nil; + e.elems = nil; + e.off = nil; + e.nelems = 0; + if(waserror()){ + cclose(c); + free(e.name); + free(e.elems); + free(e.off); +//dumpmount(); + nexterror(); + } + + /* + * Build a list of elements in the path. + */ + parsename(name, &e); + + /* + * On create, .... + */ + if(amode == Acreate){ + /* perm must have DMDIR if last element is / or /. */ + if(e.mustbedir && !(perm&DMDIR)){ + npath = e.nelems; + strcpy(tmperrbuf, "create without DMDIR"); + goto NameError; + } + + /* don't try to walk the last path element just yet. */ + if(e.nelems == 0) + error(Eexist); + e.nelems--; + } + + if(walk(&c, e.elems, e.nelems, nomount, &npath) < 0){ + if(npath < 0 || npath > e.nelems){ + print("namec %s walk error npath=%d\n", aname, npath); + nexterror(); + } + strcpy(tmperrbuf, up->env->errstr); + NameError: + len = prefix+e.off[npath]; + if(len < ERRMAX/3 || (name=kmemrchr(aname, '/', len))==nil || name==aname) + snprint(up->genbuf, sizeof up->genbuf, "%.*s", len, aname); + else + snprint(up->genbuf, sizeof up->genbuf, "...%.*s", (int)(len-(name-aname)), name); + snprint(up->env->errstr, ERRMAX, "%#q %s", up->genbuf, tmperrbuf); + nexterror(); + } + + if(e.mustbedir && !(c->qid.type&QTDIR)){ + npath = e.nelems; + strcpy(tmperrbuf, "not a directory"); + goto NameError; + } + + if(amode == Aopen && (omode&3) == OEXEC && (c->qid.type&QTDIR)){ + npath = e.nelems; + error("cannot exec directory"); + } + + switch(amode){ + case Aaccess: + if(!nomount) + domount(&c, nil); + break; + + case Abind: + m = nil; + if(!nomount) + domount(&c, &m); + if(c->umh != nil) + putmhead(c->umh); + c->umh = m; + break; + + case Aremove: + case Aopen: + Open: + /* save the name; domount might change c */ + cname = c->name; + incref(&cname->r); + m = nil; + if(!nomount) + domount(&c, &m); + + /* our own copy to open or remove */ + c = cunique(c); + + /* now it's our copy anyway, we can put the name back */ + cnameclose(c->name); + c->name = cname; + + switch(amode){ + case Aremove: + putmhead(m); + break; + + case Aopen: + case Acreate: +if(c->umh != nil){ + print("cunique umh\n"); + putmhead(c->umh); + c->umh = nil; +} + + /* only save the mount head if it's a multiple element union */ + if(m && m->mount && m->mount->next) + c->umh = m; + else + putmhead(m); + + /* save registers else error() in open has wrong value of c saved */ + saveregisters(); + + if(omode == OEXEC) + c->flag &= ~CCACHE; + + c = devtab[c->type]->open(c, omode&~OCEXEC); + + if(omode & OCEXEC) + c->flag |= CCEXEC; + if(omode & ORCLOSE) + c->flag |= CRCLOSE; + break; + } + break; + + case Atodir: + /* + * Directories (e.g. for cd) are left before the mount point, + * so one may mount on / or . and see the effect. + */ + if(!(c->qid.type & QTDIR)) + error(Enotdir); + break; + + case Amount: + /* + * When mounting on an already mounted upon directory, + * one wants subsequent mounts to be attached to the + * original directory, not the replacement. Don't domount. + */ + break; + + case Acreate: + /* + * We've already walked all but the last element. + * If the last exists, try to open it OTRUNC. + * If omode&OEXCL is set, just give up. + */ + e.nelems++; + if(walk(&c, e.elems+e.nelems-1, 1, nomount, nil) == 0){ + if(omode&OEXCL) + error(Eexist); + omode |= OTRUNC; + goto Open; + } + + /* + * The semantics of the create(2) system call are that if the + * file exists and can be written, it is to be opened with truncation. + * On the other hand, the create(5) message fails if the file exists. + * If we get two create(2) calls happening simultaneously, + * they might both get here and send create(5) messages, but only + * one of the messages will succeed. To provide the expected create(2) + * semantics, the call with the failed message needs to try the above + * walk again, opening for truncation. This correctly solves the + * create/create race, in the sense that any observable outcome can + * be explained as one happening before the other. + * The create/create race is quite common. For example, it happens + * when two rc subshells simultaneously update the same + * environment variable. + * + * The implementation still admits a create/create/remove race: + * (A) walk to file, fails + * (B) walk to file, fails + * (A) create file, succeeds, returns + * (B) create file, fails + * (A) remove file, succeeds, returns + * (B) walk to file, return failure. + * + * This is hardly as common as the create/create race, and is really + * not too much worse than what might happen if (B) got a hold of a + * file descriptor and then the file was removed -- either way (B) can't do + * anything with the result of the create call. So we don't care about this race. + * + * Applications that care about more fine-grained decision of the races + * can use the OEXCL flag to get at the underlying create(5) semantics; + * by default we provide the common case. + * + * We need to stay behind the mount point in case we + * need to do the first walk again (should the create fail). + * + * We also need to cross the mount point and find the directory + * in the union in which we should be creating. + * + * The channel staying behind is c, the one moving forward is cnew. + */ + m = nil; + cnew = nil; /* is this assignment necessary? */ + if(!waserror()){ /* try create */ + if(!nomount && findmount(&cnew, &m, c->type, c->dev, c->qid)) + cnew = createdir(cnew, m); + else{ + cnew = c; + incref(&cnew->r); + } + + /* + * We need our own copy of the Chan because we're + * about to send a create, which will move it. Once we have + * our own copy, we can fix the name, which might be wrong + * if findmount gave us a new Chan. + */ + cnew = cunique(cnew); + cnameclose(cnew->name); + cnew->name = c->name; + incref(&cnew->name->r); + + devtab[cnew->type]->create(cnew, e.elems[e.nelems-1], omode&~(OEXCL|OCEXEC), perm); + poperror(); + if(omode & OCEXEC) + cnew->flag |= CCEXEC; + if(omode & ORCLOSE) + cnew->flag |= CRCLOSE; + if(m) + putmhead(m); + cclose(c); + c = cnew; + c->name = addelem(c->name, e.elems[e.nelems-1]); + break; + }else{ /* create failed */ + cclose(cnew); + if(m) + putmhead(m); + if(omode & OEXCL) + nexterror(); + /* save error */ + createerr = up->env->errstr; + up->env->errstr = tmperrbuf; + /* note: we depend that walk does not error */ + if(walk(&c, e.elems+e.nelems-1, 1, nomount, nil) < 0){ + up->env->errstr = createerr; + error(createerr); /* report true error */ + } + up->env->errstr = createerr; + omode |= OTRUNC; + goto Open; + } + + default: + panic("unknown namec access %d\n", amode); + } + + poperror(); + + /* place final element in genbuf for e.g. exec */ + if(e.nelems > 0) + kstrcpy(up->genbuf, e.elems[e.nelems-1], sizeof up->genbuf); + else + kstrcpy(up->genbuf, ".", sizeof up->genbuf); + free(e.name); + free(e.elems); + free(e.off); + + return c; +} + +/* + * name is valid. skip leading / and ./ as much as possible + */ +char* +skipslash(char *name) +{ + while(name[0]=='/' || (name[0]=='.' && (name[1]==0 || name[1]=='/'))) + name++; + return name; +} + +/* + * Check that the name + * a) is in valid memory. + * b) is shorter than 2^16 bytes, so it can fit in a 9P string field. + * c) contains no frogs. + * The first byte is known to be addressible by the requester, so the + * routine works for kernel and user memory both. + * The parameter slashok flags whether a slash character is an error + * or a valid character. + */ +void +validname(char *aname, int slashok) +{ + char *ename, *name; + int c; + Rune r; + + name = aname; + ename = memchr(name, 0, (1<<16)); + + if(ename==nil || ename-name>=(1<<16)) + error("name too long"); + + while(*name){ + /* all characters above '~' are ok */ + c = *(uchar*)name; + if(c >= Runeself) + name += chartorune(&r, name); + else{ + if(isfrog[c]) + if(!slashok || c!='/'){ + snprint(up->genbuf, sizeof(up->genbuf), "%s: %q", Ebadchar, aname); + error(up->genbuf); + } + name++; + } + } +} + +void +isdir(Chan *c) +{ + if(c->qid.type & QTDIR) + return; + error(Enotdir); +} + +/* + * This is necessary because there are many + * pointers to the top of a given mount list: + * + * - the mhead in the namespace hash table + * - the mhead in chans returned from findmount: + * used in namec and then by unionread. + * - the mhead in chans returned from createdir: + * used in the open/create race protect, which is gone. + * + * The RWlock in the Mhead protects the mount list it contains. + * The mount list is deleted when we cunmount. + * The RWlock ensures that nothing is using the mount list at that time. + * + * It is okay to replace c->mh with whatever you want as + * long as you are sure you have a unique reference to it. + * + * This comment might belong somewhere else. + */ +void +putmhead(Mhead *m) +{ + if(m && decref(&m->r) == 0){ + m->mount = (Mount*)0xCafeBeef; + free(m); + } +} diff --git a/emu/port/dat.h b/emu/port/dat.h new file mode 100644 index 00000000..b245b61c --- /dev/null +++ b/emu/port/dat.h @@ -0,0 +1,512 @@ +typedef struct Block Block; +typedef struct Chan Chan; +typedef struct Cmdbuf Cmdbuf; +typedef struct Cmdtab Cmdtab; +typedef struct Cname Cname; +typedef struct Dev Dev; +typedef struct Dirtab Dirtab; +typedef struct Egrp Egrp; +typedef struct Evalue Evalue; +typedef struct Fgrp Fgrp; +typedef struct Mount Mount; +typedef struct Mntcache Mntcache; +typedef struct Mntparam Mntparam; +typedef struct Mntrpc Mntrpc; +typedef struct Mntwalk Mntwalk; +typedef struct Mnt Mnt; +typedef struct Mhead Mhead; +typedef struct Osenv Osenv; +typedef struct Pgrp Pgrp; +typedef struct Proc Proc; +typedef struct Queue Queue; +typedef struct Ref Ref; +typedef struct Rendez Rendez; +typedef struct Rept Rept; +typedef struct Rootdata Rootdata; +/*typedef struct RWlock RWlock;*/ +typedef struct RWLock RWlock; +typedef struct Procs Procs; +typedef struct Signerkey Signerkey; +typedef struct Skeyset Skeyset; +typedef struct Uqid Uqid; +typedef struct Uqidtab Uqidtab; +typedef struct Walkqid Walkqid; + +#include "lib9.h" +#undef CHDIR +#undef NAMELEN +#undef ERRLEN + +#pragma incomplete Queue +#pragma incomplete Mntrpc + +#include "fcall.h" + +#include "pool.h" + +typedef int Devgen(Chan*, char*, Dirtab*, int, int, Dir*); + +enum +{ + NERR = 32, + KNAMELEN = 28, + MAXROOT = 5*KNAMELEN, /* Maximum root pathname len of devfs-* */ + NUMSIZE = 11, + PRINTSIZE = 256, + READSTR = 1000 /* temporary buffer size for device reads */ +}; + +struct Ref +{ + Lock lk; + long ref; +}; + +struct Rendez +{ + Lock l; + Proc* p; +}; + +struct Rept +{ + Lock l; + Rendez r; + void *o; + int t; + int (*active)(void*); + int (*ck)(void*, int); + void (*f)(void*); /* called with VM acquire()'d */ +}; + +/* + * Access types in namec & channel flags + */ +enum +{ + Aaccess, /* as in access, stat */ + Abind, /* for left-hand-side of bind */ + Atodir, /* as in chdir */ + Aopen, /* for i/o */ + Amount, /* to be mounted upon */ + Acreate, /* file is to be created */ + Aremove, /* will be removed by caller */ + + COPEN = 0x0001, /* for i/o */ + CMSG = 0x0002, /* the message channel for a mount */ +/*rsc CCREATE = 0x0004, /* permits creation if c->mnt */ + CCEXEC = 0x0008, /* close on exec */ + CFREE = 0x0010, /* not in use */ + CRCLOSE = 0x0020, /* remove on close */ + CCACHE = 0x0080 /* client cache */ +}; + +struct Chan +{ + Lock l; + Ref r; + Chan* next; /* allocation */ + Chan* link; + vlong offset; /* in file */ + ushort type; + ulong dev; + ushort mode; /* read/write */ + ushort flag; + Qid qid; + int fid; /* for devmnt */ + ulong iounit; /* chunk size for i/o; 0==default */ + Mhead* umh; /* mount point that derived Chan; used in unionread */ + Chan* umc; /* channel in union; held for union read */ + QLock umqlock; /* serialize unionreads */ + int uri; /* union read index */ + int dri; /* devdirread index */ + ulong mountid; + Mntcache *mcp; /* Mount cache pointer */ + Mnt *mux; /* Mnt for clients using me for messages */ + void* aux; /* device specific data */ + Chan* mchan; /* channel to mounted server */ + Qid mqid; /* qid of root of mount point */ + Cname *name; +}; + +struct Cname +{ + Ref r; + int alen; /* allocated length */ + int len; /* strlen(s) */ + char *s; +}; + +struct Dev +{ + int dc; + char* name; + + void (*init)(void); + Chan* (*attach)(char*); + Walkqid* (*walk)(Chan*, Chan*, char**, int); + int (*stat)(Chan*, uchar*, int); + Chan* (*open)(Chan*, int); + void (*create)(Chan*, char*, int, ulong); + void (*close)(Chan*); + long (*read)(Chan*, void*, long, vlong); + Block* (*bread)(Chan*, long, ulong); + long (*write)(Chan*, void*, long, vlong); + long (*bwrite)(Chan*, Block*, ulong); + void (*remove)(Chan*); + int (*wstat)(Chan*, uchar*, int); +}; + +enum +{ + BINTR = (1<<0), + BFREE = (1<<1), + BMORE = (1<<2) /* continued in next block */ +}; + +struct Block +{ + Block* next; + Block* list; + uchar* rp; /* first unconsumed byte */ + uchar* wp; /* first empty byte */ + uchar* lim; /* 1 past the end of the buffer */ + uchar* base; /* start of the buffer */ + void (*free)(Block*); + ulong flag; +}; +#define BLEN(s) ((s)->wp - (s)->rp) +#define BALLOC(s) ((s)->lim - (s)->base) + +struct Dirtab +{ + char name[KNAMELEN]; + Qid qid; + vlong length; + long perm; +}; + +struct Walkqid +{ + Chan *clone; + int nqid; + Qid qid[1]; +}; + +enum +{ + NSMAX = 1000, + NSLOG = 7, + NSCACHE = (1<<NSLOG) +}; + +struct Mntwalk /* state for /proc/#/ns */ +{ + int cddone; + ulong id; + Mhead* mh; + Mount* cm; +}; + +struct Mount +{ + ulong mountid; + Mount* next; + Mhead* head; + Mount* copy; + Mount* order; + Chan* to; /* channel replacing channel */ + int mflag; + char *spec; +}; + +struct Mhead +{ + Ref r; + RWlock lock; + Chan* from; /* channel mounted upon */ + Mount* mount; /* what's mounted upon it */ + Mhead* hash; /* Hash chain */ +}; + +struct Mnt +{ + Lock l; + /* references are counted using c->ref; channels on this mount point incref(c->mchan) == Mnt.c */ + Chan* c; /* Channel to file service */ + Proc* rip; /* Reader in progress */ + Mntrpc* queue; /* Queue of pending requests on this channel */ + ulong id; /* Multiplexor id for channel check */ + Mnt* list; /* Free list */ + int flags; /* cache */ + int msize; /* data + IOHDRSZ */ + char *version; /* 9P version */ + Queue *q; /* input queue */ +}; + +enum +{ + MNTLOG = 5, + MNTHASH = 1<<MNTLOG, /* Hash to walk mount table */ + DELTAFD= 20, /* allocation quantum for process file descriptors */ + MAXNFD = 4000, /* max per process file descriptors */ + MAXKEY = 8 /* keys for signed modules */ +}; +#define MOUNTH(p,qid) ((p)->mnthash[(qid).path&((1<<MNTLOG)-1)]) + +struct Mntparam { + Chan* chan; + Chan* authchan; + char* spec; + int flags; +}; + +struct Pgrp +{ + Ref r; /* also used as a lock when mounting */ + ulong pgrpid; + RWlock ns; /* Namespace n read/one write lock */ + QLock nsh; + Mhead* mnthash[MNTHASH]; + int progmode; + int privatemem; /* deny access to /prog by debuggers */ + Chan* dot; + Chan* slash; + int nodevs; + int pin; +}; + +enum +{ + Nopin = -1 +}; + +struct Fgrp +{ + Lock l; + Ref r; + Chan** fd; + int nfd; /* number of fd slots */ + int maxfd; /* highest fd in use */ + int minfd; /* lower bound on free fd */ +}; + +struct Evalue +{ + char *var; + char *val; + int len; + Qid qid; + Evalue *next; +}; + +struct Egrp +{ + Ref r; + QLock l; + ulong path; + ulong vers; + Evalue *entries; +}; + +struct Signerkey +{ + Ref r; + char* owner; + ushort footprint; + ulong expires; + void* alg; + void* pk; + void (*pkfree)(void*); +}; + +struct Skeyset +{ + Ref r; + QLock l; + ulong flags; + char* devs; + int nkey; + Signerkey *keys[MAXKEY]; +}; + +struct Uqid +{ + Ref r; + int type; + int dev; + vlong oldpath; + vlong newpath; + Uqid* next; +}; + +enum +{ + Nqidhash = 32 +}; + +struct Uqidtab +{ + QLock l; + Uqid* qids[Nqidhash]; + ulong pathgen; +}; + +struct Osenv +{ + char *syserrstr; /* last error from a system call, errbuf0 or 1 */ + char *errstr; /* reason we're unwinding the error stack, errbuf1 or 0 */ + char errbuf0[ERRMAX]; + char errbuf1[ERRMAX]; + Pgrp* pgrp; /* Ref to namespace, working dir and root */ + Fgrp* fgrp; /* Ref to file descriptors */ + Egrp* egrp; /* Environment vars */ + Skeyset* sigs; /* Signed module keys */ + Rendez* rend; /* Synchro point */ + Queue* waitq; /* Info about dead children */ + Queue* childq; /* Info about children for debuggers */ + void* debug; /* Debugging master */ + char* user; /* Inferno user name */ + FPU fpu; /* Floating point thread state */ + int uid; /* Numeric user id for host system */ + int gid; /* Numeric group id for host system */ + void *ui; /* User info for NT */ +}; + +enum +{ + Unknown = 0xdeadbabe, + IdleGC = 0x16, + Interp = 0x17, + BusyGC = 0x18, + Moribund +}; + +struct Proc +{ + int type; /* interpreter or not */ + char text[KNAMELEN]; + Proc* qnext; /* list of processes waiting on a Qlock */ + long pid; + Proc* next; /* list of created processes */ + Proc* prev; + Lock rlock; /* sync between sleep/swiproc for r */ + Rendez* r; /* rendezvous point slept on */ + Rendez sleep; /* place to sleep */ + int killed; /* by swiproc */ + int swipend; /* software interrupt pending for Prog */ + int syscall; /* set true under sysio for interruptable syscalls */ + int intwait; /* spin wait for note to turn up */ + int sigid; /* handle used for signal/note/exception */ + Lock sysio; /* note handler lock */ + char genbuf[128]; /* buffer used e.g. for last name element from namec */ + int nerr; /* error stack SP */ + osjmpbuf estack[NERR]; /* vector of error jump labels */ + char* kstack; + void (*func)(void*); /* saved trampoline pointer for kproc */ + void* arg; /* arg for invoked kproc function */ + void* iprog; /* work for Prog after release */ + void* prog; /* fake prog for slaves eg. exportfs */ + Osenv* env; /* effective operating system environment */ + Osenv defenv; /* default env for slaves with no prog */ + osjmpbuf privstack; /* private stack for making new kids */ + osjmpbuf sharestack; + Proc *kid; + void *kidsp; + void *os; /* host os specific data */ +}; + +#define poperror() up->nerr-- +#define waserror() (up->nerr++, ossetjmp(up->estack[up->nerr-1])) + +enum +{ + /* kproc flags */ + KPDUPPG = (1<<0), + KPDUPFDG = (1<<1), + KPDUPENVG = (1<<2), + KPX11 = (1<<8), /* needs silly amount of stack */ + KPDUP = (KPDUPPG|KPDUPFDG|KPDUPENVG) +}; + +struct Procs +{ + Lock l; + Proc* head; + Proc* tail; +}; + +struct Rootdata +{ + int dotdot; + void *ptr; + int size; + int *sizep; +}; + +extern Dev* devtab[]; +extern char *ossysname; +extern char *eve; +extern Queue* kbdq; +extern Queue* gkbdq; +extern Queue* gkscanq; +extern int Xsize; +extern int Ysize; +extern Pool* mainmem; +extern char rootdir[MAXROOT]; /* inferno root */ +extern Procs procs; +extern int sflag; +extern int xtblbit; +extern int globfs; +extern int greyscale; +extern uint qiomaxatomic; + +/* + * floating point control and status register masks + */ +enum +{ + INVAL = 0x0001, + ZDIV = 0x0002, + OVFL = 0x0004, + UNFL = 0x0008, + INEX = 0x0010, + RND_NR = 0x0000, + RND_NINF = 0x0100, + RND_PINF = 0x0200, + RND_Z = 0x0300, + RND_MASK = 0x0300 +}; + +struct Cmdbuf +{ + char *buf; + char **f; + int nf; +}; + +struct Cmdtab +{ + int index; /* used by client to switch on result */ + char *cmd; /* command name */ + int narg; /* expected #args; 0 ==> variadic */ +}; + +/* queue state bits, Qmsg, Qcoalesce, and Qkick can be set in qopen */ +enum +{ + /* Queue.state */ + Qstarve = (1<<0), /* consumer starved */ + Qmsg = (1<<1), /* message stream */ + Qclosed = (1<<2), /* queue has been closed/hungup */ + Qflow = (1<<3), /* producer flow controlled */ + Qcoalesce = (1<<4), /* coallesce packets on read */ + Qkick = (1<<5), /* always call the kick routine after qwrite */ +}; + +#define DEVDOTDOT -1 + +#pragma varargck type "I" uchar* +#pragma varargck type "E" uchar* + +extern void (*mainmonitor)(int, void*, ulong); diff --git a/emu/port/dev.c b/emu/port/dev.c new file mode 100644 index 00000000..ee4053aa --- /dev/null +++ b/emu/port/dev.c @@ -0,0 +1,446 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + +extern ulong kerndate; + +void +mkqid(Qid *q, vlong path, ulong vers, int type) +{ + q->type = type; + q->vers = vers; + q->path = path; +} + +int +devno(int c, int user) +{ + int i; + + for(i = 0; devtab[i] != nil; i++) { + if(devtab[i]->dc == c) + return i; + } + if(user == 0) + panic("devno %C 0x%ux", c, c); + + return -1; +} + +void +devdir(Chan *c, Qid qid, char *n, long length, char *user, long perm, Dir *db) +{ + db->name = n; + if(c->flag&CMSG) + qid.type |= QTMOUNT; + db->qid = qid; + db->type = devtab[c->type]->dc; + db->dev = c->dev; + db->mode = perm | (qid.type << 24); + db->atime = time(0); + db->mtime = kerndate; + db->length = length; + db->uid = user; + db->gid = eve; + db->muid = user; +} + +/* + * the zeroth element of the table MUST be the directory itself for .. + */ +int +devgen(Chan *c, char *name, Dirtab *tab, int ntab, int i, Dir *dp) +{ + USED(name); + if(tab == 0) + return -1; + if(i != DEVDOTDOT){ + /* skip over the first element, that for . itself */ + i++; + if(i >= ntab) + return -1; + tab += i; + } + devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp); + return 1; +} + +void +devinit(void) +{ +} + +void +devshutdown(void) +{ +} + +Chan* +devattach(int tc, char *spec) +{ + Chan *c; + char *buf; + + c = newchan(); + mkqid(&c->qid, 0, 0, QTDIR); + c->type = devno(tc, 0); + if(spec == nil) + spec = ""; + buf = smalloc(4+strlen(spec)+1); + sprint(buf, "#%C%s", tc, spec); + c->name = newcname(buf); + free(buf); + return c; +} + +Chan* +devclone(Chan *c) +{ + Chan *nc; + + if(c->flag & COPEN) + panic("clone of open file type %C\n", devtab[c->type]->dc); + + nc = newchan(); + nc->type = c->type; + nc->dev = c->dev; + nc->mode = c->mode; + nc->qid = c->qid; + nc->offset = c->offset; + nc->umh = nil; + nc->mountid = c->mountid; + nc->aux = c->aux; + nc->mqid = c->mqid; + nc->mcp = c->mcp; + return nc; +} + +Walkqid* +devwalk(Chan *c, Chan *nc, char **name, int nname, Dirtab *tab, int ntab, Devgen *gen) +{ + int i, j; + volatile int alloc; + Walkqid *wq; + char *n; + Dir dir; + + if(nname > 0) + isdir(c); + + alloc = 0; + wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid)); + if(waserror()){ + if(alloc && wq->clone!=nil) + cclose(wq->clone); + free(wq); + return nil; + } + if(nc == nil){ + nc = devclone(c); + nc->type = 0; /* device doesn't know about this channel yet */ + alloc = 1; + } + wq->clone = nc; + + for(j=0; j<nname; j++){ + if(!(nc->qid.type&QTDIR)){ + if(j==0) + error(Enotdir); + goto Done; + } + n = name[j]; + if(strcmp(n, ".") == 0){ + Accept: + wq->qid[wq->nqid++] = nc->qid; + continue; + } + if(strcmp(n, "..") == 0){ + (*gen)(nc, nil, tab, ntab, DEVDOTDOT, &dir); + nc->qid = dir.qid; + goto Accept; + } + /* + * Ugly problem: If we're using devgen, make sure we're + * walking the directory itself, represented by the first + * entry in the table, and not trying to step into a sub- + * directory of the table, e.g. /net/net. Devgen itself + * should take care of the problem, but it doesn't have + * the necessary information (that we're doing a walk). + */ + if(gen==devgen && nc->qid.path!=tab[0].qid.path) + goto Notfound; + for(i=0;; i++) { + switch((*gen)(nc, n, tab, ntab, i, &dir)){ + case -1: + Notfound: + if(j == 0) + error(Enonexist); + kstrcpy(up->env->errstr, Enonexist, ERRMAX); + goto Done; + case 0: + continue; + case 1: + if(strcmp(n, dir.name) == 0){ + nc->qid = dir.qid; + goto Accept; + } + continue; + } + } + } + /* + * We processed at least one name, so will return some data. + * If we didn't process all nname entries succesfully, we drop + * the cloned channel and return just the Qids of the walks. + */ +Done: + poperror(); + if(wq->nqid < nname){ + if(alloc) + cclose(wq->clone); + wq->clone = nil; + }else if(wq->clone){ + /* attach cloned channel to same device */ + wq->clone->type = c->type; + } + return wq; +} + +int +devstat(Chan *c, uchar *db, int n, Dirtab *tab, int ntab, Devgen *gen) +{ + int i; + Dir dir; + char *p, *elem; + + for(i=0;; i++) + switch((*gen)(c, nil, tab, ntab, i, &dir)){ + case -1: + if(c->qid.type & QTDIR){ + if(c->name == nil) + elem = "???"; + else if(strcmp(c->name->s, "/") == 0) + elem = "/"; + else + for(elem=p=c->name->s; *p; p++) + if(*p == '/') + elem = p+1; + devdir(c, c->qid, elem, 0, eve, DMDIR|0555, &dir); + n = convD2M(&dir, db, n); + if(n == 0) + error(Ebadarg); + return n; + } + print("%s %s: devstat %C %llux\n", + up->text, up->env->user, + devtab[c->type]->dc, c->qid.path); + + error(Enonexist); + case 0: + break; + case 1: + if(c->qid.path == dir.qid.path) { + if(c->flag&CMSG) + dir.mode |= DMMOUNT; + n = convD2M(&dir, db, n); + if(n == 0) + error(Ebadarg); + return n; + } + break; + } +} + +long +devdirread(Chan *c, char *d, long n, Dirtab *tab, int ntab, Devgen *gen) +{ + long m, dsz; + struct{ + Dir d; + char slop[100]; /* TO DO */ + }dir; + + for(m=0; m<n; c->dri++) { + switch((*gen)(c, nil, tab, ntab, c->dri, &dir.d)){ + case -1: + return m; + + case 0: + break; + + case 1: + dsz = convD2M(&dir.d, (uchar*)d, n-m); + if(dsz <= BIT16SZ){ /* <= not < because this isn't stat; read is stuck */ + if(m == 0) + error(Eshort); + return m; + } + m += dsz; + d += dsz; + break; + } + } + + return m; +} + +/* + * error(Eperm) if open permission not granted for up->env->user. + */ +void +devpermcheck(char *fileuid, ulong perm, int omode) +{ + ulong t; + static int access[] = { 0400, 0200, 0600, 0100 }; + + if(strcmp(up->env->user, fileuid) == 0) + perm <<= 0; + else + if(strcmp(up->env->user, eve) == 0) + perm <<= 3; + else + perm <<= 6; + + t = access[omode&3]; + if((t&perm) != t) + error(Eperm); +} + +Chan* +devopen(Chan *c, int omode, Dirtab *tab, int ntab, Devgen *gen) +{ + int i; + Dir dir; + + for(i=0;; i++) { + switch((*gen)(c, nil, tab, ntab, i, &dir)){ + case -1: + goto Return; + case 0: + break; + case 1: + if(c->qid.path == dir.qid.path) { + devpermcheck(dir.uid, dir.mode, omode); + goto Return; + } + break; + } + } +Return: + c->offset = 0; + if((c->qid.type&QTDIR) && omode!=OREAD) + error(Eperm); + c->mode = openmode(omode); + c->flag |= COPEN; + return c; +} + +Block* +devbread(Chan *c, long n, ulong offset) +{ + Block *bp; + + bp = allocb(n); + if(waserror()) { + freeb(bp); + nexterror(); + } + bp->wp += devtab[c->type]->read(c, bp->wp, n, offset); + poperror(); + return bp; +} + +long +devbwrite(Chan *c, Block *bp, ulong offset) +{ + long n; + + if(waserror()) { + freeb(bp); + nexterror(); + } + n = devtab[c->type]->write(c, bp->rp, BLEN(bp), offset); + poperror(); + freeb(bp); + + return n; +} + +void +devcreate(Chan *c, char *name, int mode, ulong perm) +{ + USED(c); + USED(name); + USED(mode); + USED(perm); + error(Eperm); +} + +void +devremove(Chan *c) +{ + USED(c); + error(Eperm); +} + +int +devwstat(Chan *c, uchar *dp, int n) +{ + USED(c); + USED(dp); + USED(n); + error(Eperm); + return 0; +} + +int +readstr(ulong off, char *buf, ulong n, char *str) +{ + int size; + + size = strlen(str); + if(off >= size) + return 0; + if(off+n > size) + n = size-off; + memmove(buf, str+off, n); + return n; +} + +int +readnum(ulong off, char *buf, ulong n, ulong val, int size) +{ + char tmp[64]; + + if(size > 64) size = 64; + + snprint(tmp, sizeof(tmp), "%*.0lud ", size, val); + if(off >= size) + return 0; + if(off+n > size) + n = size-off; + memmove(buf, tmp+off, n); + return n; +} + +/* + * check that the name in a wstat is plausible + */ +void +validwstatname(char *name) +{ + validname(name, 0); + if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) + error(Efilename); +} + +Dev* +devbyname(char *name) +{ + int i; + + for(i = 0; devtab[i] != nil; i++) + if(strcmp(devtab[i]->name, name) == 0) + return devtab[i]; + return nil; +} diff --git a/emu/port/devaudio.c b/emu/port/devaudio.c new file mode 100644 index 00000000..c592f88c --- /dev/null +++ b/emu/port/devaudio.c @@ -0,0 +1,397 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "audio.h" + +Dirtab audiotab[] = +{ + ".", {Qdir, 0, QTDIR}, 0, 0555, + "audio", {Qaudio}, 0, 0666, + "audioctl", {Qaudioctl}, 0, 0666, +}; + +static void +audioinit(void) +{ + audio_file_init(); +} + +static Chan* +audioattach(char *spec) +{ + return devattach('A', spec); +} + +static Walkqid* +audiowalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, audiotab, nelem(audiotab), devgen); +} + +static int +audiostat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, audiotab, nelem(audiotab), devgen); +} + +static Chan* +audioopen(Chan *c, int omode) +{ + c = devopen(c, omode, audiotab, nelem(audiotab), devgen); + if(waserror()){ + c->flag &= ~COPEN; + nexterror(); + } + switch(c->qid.path) { + case Qdir: + case Qaudioctl: + break; + case Qaudio: + audio_file_open(c, c->mode); + break; + default: + error(Egreg); + } + poperror(); + return c; +} + +static void +audioclose(Chan *c) +{ + if((c->flag & COPEN) == 0) + return; + + switch(c->qid.path) { + case Qdir: + case Qaudioctl: + break; + case Qaudio: + audio_file_close(c); + break; + default: + error(Egreg); + } +} + +static int ctlsummary(char*, int, Audio_t*); + +static long +audioread(Chan *c, void *va, long count, vlong offset) +{ + char *buf; + int n; + + if(c->qid.type & QTDIR) + return devdirread(c, va, count, audiotab, nelem(audiotab), devgen); + switch(c->qid.path) { + case Qaudio: + return audio_file_read(c, va, count, offset); + case Qaudioctl: + buf = smalloc(READSTR); + if(waserror()){ + free(buf); + nexterror(); + } + n = ctlsummary(buf, READSTR, getaudiodev()); + count = readstr(offset, va, n, buf); + poperror(); + free(buf); + return count; + } + return 0; +} + +static long +audiowrite(Chan *c, void *va, long count, vlong offset) +{ + switch(c->qid.path) { + case Qaudio: + return audio_file_write(c, va, count, offset); + case Qaudioctl: + return audio_ctl_write(c, va, count, offset); + } + return 0; +} + +static int sval(char*, unsigned long*, ulong, ulong); +static int str2val(svp_t*, char*, ulong*); +static char* val2str(svp_t*, ulong); + +int +audioparse(char* args, int len, Audio_t *t) +{ + int i, n; + ulong v; + Cmdbuf *cb; + ulong tf; + Audio_t info = *t; + + cb = parsecmd(args, len); + if(waserror()){ + free(cb); + return 0; + } + + tf = 0; + n = cb->nf; + for(i = 0; i < cb->nf-1; i++) { + if(strcmp(cb->f[i], "in") == 0){ + tf |= AUDIO_IN_FLAG; + continue; + } + if(strcmp(cb->f[i], "out") == 0) { + tf |= AUDIO_OUT_FLAG; + continue; + } + if(tf == 0) + tf = AUDIO_IN_FLAG | AUDIO_OUT_FLAG; + if(strcmp(cb->f[i], "bits") == 0) { + if(!str2val(audio_bits_tbl, cb->f[i+1], &v)) + break; + i++; + if(tf & AUDIO_IN_FLAG) { + info.in.flags |= AUDIO_BITS_FLAG | AUDIO_MOD_FLAG; + info.in.bits = v; + } + if(tf & AUDIO_OUT_FLAG) { + info.out.flags |= AUDIO_BITS_FLAG | AUDIO_MOD_FLAG; + info.out.bits = v; + } + } else if(strcmp(cb->f[i], "buf") == 0) { + if(!sval(cb->f[i+1], &v, Audio_Max_Val, Audio_Min_Val)) + break; + i++; + if(tf & AUDIO_IN_FLAG) { + info.in.flags |= AUDIO_BUF_FLAG | AUDIO_MOD_FLAG; + info.in.buf = v; + } + if(tf & AUDIO_OUT_FLAG) { + info.out.flags |= AUDIO_BUF_FLAG | AUDIO_MOD_FLAG; + info.out.buf = v; + } + } else if(strcmp(cb->f[i], "chans") == 0) { + if(!str2val(audio_chan_tbl, cb->f[i+1], &v)) + break; + i++; + if(tf & AUDIO_IN_FLAG) { + info.in.flags |= AUDIO_CHAN_FLAG | AUDIO_MOD_FLAG; + info.in.chan = v; + } + if(tf & AUDIO_OUT_FLAG) { + info.out.flags |= AUDIO_CHAN_FLAG | AUDIO_MOD_FLAG; + info.out.chan = v; + } + } else if(strcmp(cb->f[i], "indev") == 0) { + if(!str2val(audio_indev_tbl, cb->f[i+1], &v)) + break; + i++; + info.in.flags |= AUDIO_DEV_FLAG | AUDIO_MOD_FLAG; + info.in.dev = v; + } else if(strcmp(cb->f[i], "outdev") == 0) { + if(!str2val(audio_outdev_tbl, cb->f[i+1], &v)) + break; + i++; + info.out.flags |= AUDIO_DEV_FLAG | AUDIO_MOD_FLAG; + info.out.dev = v; + } else if(strcmp(cb->f[i], "enc") == 0) { + if(!str2val(audio_enc_tbl, cb->f[i+1], &v)) + break; + i++; + if(tf & AUDIO_IN_FLAG) { + info.in.flags |= AUDIO_ENC_FLAG | AUDIO_MOD_FLAG; + info.in.enc = v; + } + if(tf & AUDIO_OUT_FLAG) { + info.out.flags |= AUDIO_ENC_FLAG | AUDIO_MOD_FLAG; + info.out.enc = v; + } + } else if(strcmp(cb->f[i], "rate") == 0) { + if(!str2val(audio_rate_tbl, cb->f[i+1], &v)) + break; + i++; + if(tf & AUDIO_IN_FLAG) { + info.in.flags |= AUDIO_RATE_FLAG | AUDIO_MOD_FLAG; + info.in.rate = v; + } + if(tf & AUDIO_OUT_FLAG) { + info.out.flags |= AUDIO_RATE_FLAG | AUDIO_MOD_FLAG; + info.out.rate = v; + } + } else if(strcmp(cb->f[i], "vol") == 0) { + if(!sval(cb->f[i+1], &v, Audio_Max_Val, Audio_Min_Val)) + break; + i++; + if(tf & AUDIO_IN_FLAG) { + info.in.flags |= AUDIO_VOL_FLAG | AUDIO_MOD_FLAG; + info.in.left = v; + info.in.right = v; + } + if(tf & AUDIO_OUT_FLAG) { + info.out.flags |= AUDIO_VOL_FLAG | AUDIO_MOD_FLAG; + info.out.left = v; + info.out.right = v; + } + } else if(strcmp(cb->f[i], "left") == 0) { + if(!sval(cb->f[i+1], &v, Audio_Max_Val, Audio_Min_Val)) + break; + i++; + if(tf & AUDIO_IN_FLAG) { + info.in.flags |= AUDIO_LEFT_FLAG | AUDIO_MOD_FLAG; + info.in.left = v; + } + if(tf & AUDIO_OUT_FLAG) { + info.out.flags |= AUDIO_LEFT_FLAG | AUDIO_MOD_FLAG; + info.out.left = v; + } + } else if(strcmp(cb->f[i], "right") == 0) { + if(!sval(cb->f[i+1], &v, Audio_Max_Val, Audio_Min_Val)) + break; + i++; + if(tf & AUDIO_IN_FLAG) { + info.in.flags |= AUDIO_RIGHT_FLAG | AUDIO_MOD_FLAG; + info.in.right = v; + } + if(tf & AUDIO_OUT_FLAG) { + info.out.flags |= AUDIO_RIGHT_FLAG | AUDIO_MOD_FLAG; + info.out.right = v; + } + }else + break; + } + poperror(); + free(cb); + + if(i < n) + return 0; + + *t = info; /* set information back */ + return 1; +} + +static char* +audioparam(char* p, char* e, char* name, int val, svp_t* tbl) +{ + char *s; + svp_t *sv; + + if((s = val2str(tbl, val)) != nil){ + p = seprint(p, e, "%s %s", name, s); /* current setting */ + for(sv = tbl; sv->s != nil; sv++) + if(sv->v != val) + p = seprint(p, e, " %s", sv->s); /* other possible values */ + p = seprint(p, e, "\n"); + }else + p = seprint(p, e, "%s unknown\n", name); + return p; +} + +static char* +audioioparam(char* p, char* e, char* name, int ival, int oval, svp_t* tbl) +{ + if(ival == oval) + return audioparam(p, e, name, ival, tbl); + p = audioparam(seprint(p, e, "in "), e, name, ival, tbl); + p = audioparam(seprint(p, e, "out "), e, name, oval, tbl); + return p; +} + +static int +ctlsummary(char *buf, int bsize, Audio_t *adev) +{ + Audio_d *in, *out; + char *p, *e; + + in = &adev->in; + out = &adev->out; + + p = buf; + e = p + bsize; + + p = audioparam(p, e, "indev", in->dev, audio_indev_tbl); + p = audioparam(p, e, "outdev", out->dev, audio_outdev_tbl); + p = audioioparam(p, e, "enc", in->enc, out->enc, audio_enc_tbl); + p = audioioparam(p, e, "rate", in->rate, out->rate, audio_rate_tbl); + p = audioioparam(p, e, "bits", in->bits, out->bits, audio_bits_tbl); /* this one is silly */ + p = audioioparam(p, e, "chans", in->chan, out->chan, audio_chan_tbl); + /* TO DO: minimise in/out left/right where possible */ + if(in->left != in->right){ + p = seprint(p, e, "in left %d 0 100\n", in->left); + p = seprint(p, e, "in right %d 0 100\n", in->right); + }else + p = seprint(p, e, "in %d 0 100\n", in->right); + if(out->left != out->right){ + p = seprint(p, e, "out left %d 0 100\n", out->left); + p = seprint(p, e, "out right %d 0 100\n", out->right); + }else + p = seprint(p, e, "out %d 0 100\n", out->right); + p = seprint(p, e, "in buf %d %d %d\n", in->buf, Audio_Min_Val, Audio_Max_Val); + p = seprint(p, e, "out buf %d %d %d\n", out->buf, Audio_Min_Val, Audio_Max_Val); + + return p-buf; +} + +void +audio_info_init(Audio_t *t) +{ + t->in = Default_Audio_Format; + t->in.dev = Default_Audio_Input; + t->out = Default_Audio_Format; + t->out.dev = Default_Audio_Output; +} + +static int +str2val(svp_t* t, char* s, ulong *v) +{ + if(t == nil || s == nil) + return 0; + for(; t->s != nil; t++) { + if(strncmp(t->s, s, strlen(t->s)) == 0) { + *v = t->v; + return 1; + } + } + return 0; +} + +static char* +val2str(svp_t* t, ulong v) +{ + if(t == nil) + return nil; + for(; t->s != nil; t++) + if(t->v == v) + return t->s; + return nil; +} + +static int +sval(char* buf, ulong* v, ulong max, ulong min) +{ + unsigned long val = strtoul(buf, 0, 10); + + if(val > max || val < min) + return 0; + *v = val; + return 1; +} + +Dev audiodevtab = { + 'A', + "audio", + + audioinit, + audioattach, + audiowalk, + audiostat, + audioopen, + devcreate, + audioclose, + audioread, + devbread, + audiowrite, + devbwrite, + devremove, + devwstat +}; + diff --git a/emu/port/devcap.c b/emu/port/devcap.c new file mode 100644 index 00000000..00d1c2be --- /dev/null +++ b/emu/port/devcap.c @@ -0,0 +1,243 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "mp.h" +#include "libsec.h" + +/* + * Copyright © 2003 Vita Nuova Holdings Limited. All rights reserved. + */ + +enum { + Captimeout = 15, /* seconds until expiry */ + Capidletime = 60 /* idle seconds before capwatch exits */ +}; + +typedef struct Caps Caps; +struct Caps +{ + uchar hash[SHA1dlen]; + ulong time; + Caps* next; +}; + +struct { + QLock l; + Caps* caps; + int kpstarted; +} allcaps; + +enum { + Qdir, + Qhash, + Quse +}; + +static Dirtab capdir[] = +{ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "capuse", {Quse, 0}, 0, 0222, + "caphash", {Qhash, 0}, 0, 0200, +}; + +static int ncapdir = nelem(capdir); + +static void +capwatch(void *a) +{ + Caps *c, **l; + int idletime; + + USED(a); + idletime = 0; + for(;;){ + osmillisleep(30*1000); + qlock(&allcaps.l); + for(l = &allcaps.caps; (c = *l) != nil;) + if(++c->time > Captimeout){ + *l = c->next; + free(c); + }else + l = &c->next; + if(allcaps.caps == nil){ + if(++idletime > Capidletime){ + allcaps.kpstarted = 0; + qunlock(&allcaps.l); + pexit("", 0); + } + }else + idletime = 0; + qunlock(&allcaps.l); + } +} + +static Chan * +capattach(char *spec) +{ + return devattach(0x00A4, spec); /* L'¤' */ +} + +static Walkqid* +capwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, capdir, nelem(capdir), devgen); +} + +static int +capstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, capdir, nelem(capdir), devgen); +} + +static Chan* +capopen(Chan *c, int omode) +{ + if(c->qid.type & QTDIR) { + if(omode != OREAD) + error(Eisdir); + c->mode = omode; + c->flag |= COPEN; + c->offset = 0; + return c; + } + + if(c->qid.path == Qhash && !iseve()) + error(Eperm); + + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static void +capclose(Chan *c) +{ + USED(c); +} + +static long +capread(Chan *c, void *va, long n, vlong vl) +{ + USED(vl); + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, va, n, capdir, ncapdir, devgen); + + default: + error(Eperm); + break; + } + return n; +} + +static int +capwritehash(uchar *a, int l) +{ + Caps *c; + + if(l != SHA1dlen) + return -1; + c = malloc(sizeof(*c)); + if(c == nil) + return -1; + memmove(c->hash, a, l); + c->time = 0; + qlock(&allcaps.l); + c->next = allcaps.caps; + allcaps.caps = c; + if(!allcaps.kpstarted){ + allcaps.kpstarted = 1; + kproc("capwatch", capwatch, 0, 0); + } + qunlock(&allcaps.l); + return 0; +} + +static int +capwriteuse(uchar *a, int len) +{ + int n; + uchar digest[SHA1dlen]; + char buf[128], *p, *users[3]; + Caps *c, **l; + + if(len >= sizeof(buf)-1) + return -1; + memmove(buf, a, len); + buf[len] = 0; + p = strrchr(buf, '@'); + if(p == nil) + return -1; + *p++ = 0; + len = strlen(p); + n = strlen(buf); + if(len == 0 || n == 0) + return -1; + hmac_sha1((uchar*)buf, n, (uchar*)p, len, digest, nil); + n = getfields(buf, users, nelem(users), 0, "@"); + if(n == 1) + users[1] = users[0]; + else if(n != 2) + return -1; + if(*users[0] == 0 || *users[1] == 0) + return -1; + qlock(&allcaps.l); + for(l = &allcaps.caps; (c = *l) != nil; l = &c->next) + if(memcmp(c->hash, digest, sizeof(c->hash)) == 0){ + *l = c->next; + qunlock(&allcaps.l); + free(c); + if(n == 2 && strcmp(up->env->user, users[0]) != 0) + return -1; + setid(users[1], 0); /* could use users[2] to mean `host OS user' */ + return 0; + } + qunlock(&allcaps.l); + return -1; +} + +static long +capwrite(Chan* c, void* buf, long n, vlong offset) +{ + USED(offset); + switch((ulong)c->qid.path){ + case Qhash: + if(capwritehash(buf, n) < 0) + error(Ebadarg); + return n; + case Quse: + if(capwriteuse(buf, n) < 0) + error("invalid capability"); + return n; + } + error(Ebadarg); + return 0; +} + +static void +capremove(Chan *c) +{ + if(c->qid.path != Qhash || !iseve()) + error(Eperm); + ncapdir = nelem(capdir)-1; +} + +Dev capdevtab = { + 0x00A4, /* L'¤' */ + "cap", + + devinit, + capattach, + capwalk, + capstat, + capopen, + devcreate, + capclose, + capread, + devbread, + capwrite, + devbwrite, + capremove, + devwstat +}; diff --git a/emu/port/devcmd.c b/emu/port/devcmd.c new file mode 100644 index 00000000..f6cccf39 --- /dev/null +++ b/emu/port/devcmd.c @@ -0,0 +1,683 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + +enum +{ + Qtopdir, /* top level directory */ + Qcmd, + Qclonus, + Qconvdir, + Qconvbase, + Qdata = Qconvbase, + Qstderr, + Qctl, + Qstatus, + Qwait, + + Debug=0 /* to help debug os.c */ +}; +#define TYPE(x) ((ulong)(x).path & 0xf) +#define CONV(x) (((ulong)(x).path >> 4)&0xfff) +#define QID(c, y) (((c)<<4) | (y)) + +typedef struct Conv Conv; +struct Conv +{ + int x; + int inuse; + int fd[3]; /* stdin, stdout, and stderr */ + int count[3]; /* number of readers on stdin/stdout/stderr */ + int perm; + char* owner; + char* state; + Cmdbuf* cmd; + char* dir; + QLock l; /* protects state changes */ + Queue* waitq; + void* child; + char* error; /* on start up */ + int nice; + short killonclose; + short killed; + Rendez startr; +}; + +static struct +{ + QLock l; + int nc; + int maxconv; + Conv** conv; +} cmd; + +static Conv* cmdclone(char*); +static void cmdproc(void*); + +static int +cmd3gen(Chan *c, int i, Dir *dp) +{ + Qid q; + Conv *cv; + + cv = cmd.conv[CONV(c->qid)]; + switch(i){ + default: + return -1; + case Qdata: + mkqid(&q, QID(CONV(c->qid), Qdata), 0, QTFILE); + devdir(c, q, "data", 0, cv->owner, cv->perm, dp); + return 1; + case Qstderr: + mkqid(&q, QID(CONV(c->qid), Qstderr), 0, QTFILE); + devdir(c, q, "stderr", 0, cv->owner, 0444, dp); + return 1; + case Qctl: + mkqid(&q, QID(CONV(c->qid), Qctl), 0, QTFILE); + devdir(c, q, "ctl", 0, cv->owner, cv->perm, dp); + return 1; + case Qstatus: + mkqid(&q, QID(CONV(c->qid), Qstatus), 0, QTFILE); + devdir(c, q, "status", 0, cv->owner, 0444, dp); + return 1; + case Qwait: + mkqid(&q, QID(CONV(c->qid), Qwait), 0, QTFILE); + devdir(c, q, "wait", 0, cv->owner, 0444, dp); + return 1; + } +} + +static int +cmdgen(Chan *c, char *name, Dirtab *d, int nd, int s, Dir *dp) +{ + Qid q; + Conv *cv; + + USED(name); + USED(nd); + USED(d); + + if(s == DEVDOTDOT){ + switch(TYPE(c->qid)){ + case Qtopdir: + case Qcmd: + mkqid(&q, QID(0, Qtopdir), 0, QTDIR); + devdir(c, q, "#C", 0, eve, DMDIR|0555, dp); + break; + case Qconvdir: + mkqid(&q, QID(0, Qcmd), 0, QTDIR); + devdir(c, q, "cmd", 0, eve, DMDIR|0555, dp); + break; + default: + panic("cmdgen %llux", c->qid.path); + } + return 1; + } + + switch(TYPE(c->qid)) { + case Qtopdir: + if(s >= 1) + return -1; + mkqid(&q, QID(0, Qcmd), 0, QTDIR); + devdir(c, q, "cmd", 0, "cmd", DMDIR|0555, dp); + return 1; + case Qcmd: + if(s < cmd.nc) { + cv = cmd.conv[s]; + mkqid(&q, QID(s, Qconvdir), 0, QTDIR); + sprint(up->genbuf, "%d", s); + devdir(c, q, up->genbuf, 0, cv->owner, DMDIR|0555, dp); + return 1; + } + s -= cmd.nc; + if(s == 0){ + mkqid(&q, QID(0, Qclonus), 0, QTFILE); + devdir(c, q, "clone", 0, "cmd", 0666, dp); + return 1; + } + return -1; + case Qclonus: + if(s == 0){ + mkqid(&q, QID(0, Qclonus), 0, QTFILE); + devdir(c, q, "clone", 0, "cmd", 0666, dp); + return 1; + } + return -1; + case Qconvdir: + return cmd3gen(c, Qconvbase+s, dp); + case Qdata: + case Qstderr: + case Qctl: + case Qstatus: + case Qwait: + return cmd3gen(c, TYPE(c->qid), dp); + } + return -1; +} + +static void +cmdinit(void) +{ + cmd.maxconv = 1000; + cmd.conv = mallocz(sizeof(Conv*)*(cmd.maxconv+1), 1); + /* cmd.conv is checked by cmdattach, below */ +} + +static Chan * +cmdattach(char *spec) +{ + Chan *c; + + if(cmd.conv == nil) + error(Enomem); + c = devattach('C', spec); + mkqid(&c->qid, QID(0, Qtopdir), 0, QTDIR); + return c; +} + +static Walkqid* +cmdwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, 0, 0, cmdgen); +} + +static int +cmdstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, 0, 0, cmdgen); +} + +static Chan * +cmdopen(Chan *c, int omode) +{ + int perm; + Conv *cv; + char *user; + + perm = 0; + omode = openmode(omode); + switch(omode) { + case OREAD: + perm = 4; + break; + case OWRITE: + perm = 2; + break; + case ORDWR: + perm = 6; + break; + } + + switch(TYPE(c->qid)) { + default: + break; + case Qtopdir: + case Qcmd: + case Qconvdir: + case Qstatus: + if(omode != OREAD) + error(Eperm); + break; + case Qclonus: + qlock(&cmd.l); + if(waserror()){ + qunlock(&cmd.l); + nexterror(); + } + cv = cmdclone(up->env->user); + poperror(); + qunlock(&cmd.l); + if(cv == 0) + error(Enodev); + mkqid(&c->qid, QID(cv->x, Qctl), 0, QTFILE); + break; + case Qdata: + case Qstderr: + case Qctl: + case Qwait: + qlock(&cmd.l); + cv = cmd.conv[CONV(c->qid)]; + qlock(&cv->l); + if(waserror()){ + qunlock(&cv->l); + qunlock(&cmd.l); + nexterror(); + } + user = up->env->user; + if((perm & (cv->perm>>6)) != perm) { + if(strcmp(user, cv->owner) != 0 || + (perm & cv->perm) != perm) + error(Eperm); + } + switch(TYPE(c->qid)){ + case Qdata: + if(omode == OWRITE || omode == ORDWR) + cv->count[0]++; + if(omode == OREAD || omode == ORDWR) + cv->count[1]++; + break; + case Qstderr: + if(omode != OREAD) + error(Eperm); + cv->count[2]++; + break; + case Qwait: + if(cv->waitq == nil) + cv->waitq = qopen(1024, Qmsg, nil, 0); + break; + } + cv->inuse++; + if(cv->inuse == 1) { + cv->state = "Open"; + kstrdup(&cv->owner, user); + cv->perm = 0660; + cv->nice = 0; + } + poperror(); + qunlock(&cv->l); + qunlock(&cmd.l); + break; + } + c->mode = omode; + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static void +closeconv(Conv *c) +{ + kstrdup(&c->owner, "cmd"); + kstrdup(&c->dir, rootdir); + c->perm = 0666; + c->state = "Closed"; + c->killonclose = 0; + c->killed = 0; + c->nice = 0; + free(c->cmd); + c->cmd = nil; + if(c->waitq != nil){ + qfree(c->waitq); + c->waitq = nil; + } + free(c->error); + c->error = nil; +} + +static void +cmdfdclose(Conv *c, int fd) +{ + if(--c->count[fd] == 0 && c->fd[fd] != -1){ + close(c->fd[fd]); + c->fd[fd] = -1; + } +} + +static void +cmdclose(Chan *c) +{ + Conv *cc; + int r; + + if((c->flag & COPEN) == 0) + return; + + switch(TYPE(c->qid)) { + case Qctl: + case Qdata: + case Qstderr: + case Qwait: + cc = cmd.conv[CONV(c->qid)]; + qlock(&cc->l); + if(TYPE(c->qid) == Qdata){ + if(c->mode == OWRITE || c->mode == ORDWR) + cmdfdclose(cc, 0); + if(c->mode == OREAD || c->mode == ORDWR) + cmdfdclose(cc, 1); + }else if(TYPE(c->qid) == Qstderr) + cmdfdclose(cc, 2); + + r = --cc->inuse; + if(cc->child != nil){ + if(!cc->killed) + if(r == 0 || (cc->killonclose && TYPE(c->qid) == Qctl)){ + oscmdkill(cc->child); + cc->killed = 1; + } + }else if(r == 0) + closeconv(cc); + + qunlock(&cc->l); + break; + } +} + +static long +cmdread(Chan *ch, void *a, long n, vlong offset) +{ + Conv *c; + char *p, *cmds; + int fd; + + USED(offset); + + p = a; + switch(TYPE(ch->qid)) { + default: + error(Eperm); + case Qcmd: + case Qtopdir: + case Qconvdir: + return devdirread(ch, a, n, 0, 0, cmdgen); + case Qctl: + sprint(up->genbuf, "%ld", CONV(ch->qid)); + return readstr(offset, p, n, up->genbuf); + case Qstatus: + c = cmd.conv[CONV(ch->qid)]; + cmds = ""; + if(c->cmd != nil) + cmds = c->cmd->f[1]; + snprint(up->genbuf, sizeof(up->genbuf), "cmd/%d %d %s %q %q\n", + c->x, c->inuse, c->state, c->dir, cmds); + return readstr(offset, p, n, up->genbuf); + case Qdata: + case Qstderr: + fd = 1; + if(TYPE(ch->qid) == Qstderr) + fd = 2; + c = cmd.conv[CONV(ch->qid)]; + qlock(&c->l); + if(c->fd[fd] == -1){ + qunlock(&c->l); + return 0; + } + qunlock(&c->l); + osenter(); + n = read(c->fd[fd], a, n); + osleave(); + if(n < 0) + oserror(); + return n; + case Qwait: + c = cmd.conv[CONV(ch->qid)]; + return qread(c->waitq, a, n); + } +} + +static int +cmdstarted(void *a) +{ + Conv *c; + + c = a; + return c->child != nil || c->error != nil || strcmp(c->state, "Execute") != 0; +} + +enum +{ + CMdir, + CMexec, + CMkill, + CMnice, + CMkillonclose +}; + +static +Cmdtab cmdtab[] = { + CMdir, "dir", 2, + CMexec, "exec", 0, + CMkill, "kill", 1, + CMnice, "nice", 0, + CMkillonclose, "killonclose", 0, +}; + +static long +cmdwrite(Chan *ch, void *a, long n, vlong offset) +{ + int i, r; + Conv *c; + Cmdbuf *cb; + Cmdtab *ct; + + USED(offset); + + switch(TYPE(ch->qid)) { + default: + error(Eperm); + case Qctl: + c = cmd.conv[CONV(ch->qid)]; + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + ct = lookupcmd(cb, cmdtab, nelem(cmdtab)); + switch(ct->index){ + case CMdir: + kstrdup(&c->dir, cb->f[1]); + break; + case CMexec: + poperror(); /* cb */ + qlock(&c->l); + if(waserror()){ + qunlock(&c->l); + free(cb); + nexterror(); + } + if(c->child != nil || c->cmd != nil) + error(Einuse); + for(i = 0; i < nelem(c->fd); i++) + if(c->fd[i] != -1) + error(Einuse); + if(cb->nf < 1) + error(Etoosmall); + kproc("cmdproc", cmdproc, c, 0); /* cmdproc held back until unlock below */ + free(c->cmd); + c->cmd = cb; /* don't free cb */ + c->state = "Execute"; + poperror(); + qunlock(&c->l); + while(waserror()) + ; + Sleep(&c->startr, cmdstarted, c); + poperror(); + if(c->error) + error(c->error); + return n; /* avoid free(cb) below */ + case CMkill: + qlock(&c->l); + if(waserror()){ + qunlock(&c->l); + nexterror(); + } + if(c->child == nil) + error("not started"); + if(oscmdkill(c->child) < 0) + oserror(); + poperror(); + qunlock(&c->l); + break; + case CMnice: + c->nice = cb->nf > 1? atoi(cb->f[1]): 1; + break; + case CMkillonclose: + c->killonclose = 1; + break; + } + poperror(); + free(cb); + break; + case Qdata: + c = cmd.conv[CONV(ch->qid)]; + qlock(&c->l); + if(c->fd[0] == -1){ + qunlock(&c->l); + error(Ehungup); + } + qunlock(&c->l); + osenter(); + r = write(c->fd[0], a, n); + osleave(); + if(r == 0) + error(Ehungup); + if(r < 0) { + /* XXX perhaps should kill writer "write on closed pipe" here, 2nd time around? */ + oserror(); + } + return r; + } + return n; +} + +static int +cmdwstat(Chan *c, uchar *dp, int n) +{ + Dir *d; + Conv *cv; + + switch(TYPE(c->qid)){ + default: + error(Eperm); + case Qctl: + case Qdata: + case Qstderr: + d = malloc(sizeof(*d)+n); + if(d == nil) + error(Enomem); + if(waserror()){ + free(d); + nexterror(); + } + n = convM2D(dp, n, d, (char*)&d[1]); + if(n == 0) + error(Eshortstat); + cv = cmd.conv[CONV(c->qid)]; + if(!iseve() && strcmp(up->env->user, cv->owner) != 0) + error(Eperm); + if(!emptystr(d->uid)) + kstrdup(&cv->owner, d->uid); + if(d->mode != ~0UL) + cv->perm = d->mode & 0777; + poperror(); + free(d); + break; + } + return n; +} + +static Conv* +cmdclone(char *user) +{ + Conv *c, **pp, **ep; + int i; + + c = nil; + ep = &cmd.conv[cmd.maxconv]; + for(pp = cmd.conv; pp < ep; pp++) { + c = *pp; + if(c == nil) { + c = malloc(sizeof(Conv)); + if(c == nil) + error(Enomem); + qlock(&c->l); + c->inuse = 1; + c->x = pp - cmd.conv; + cmd.nc++; + *pp = c; + break; + } + if(canqlock(&c->l)){ + if(c->inuse == 0 && c->child == nil) + break; + qunlock(&c->l); + } + } + if(pp >= ep) + return nil; + + c->inuse = 1; + kstrdup(&c->owner, user); + kstrdup(&c->dir, rootdir); + c->perm = 0660; + c->state = "Closed"; + for(i=0; i<nelem(c->fd); i++) + c->fd[i] = -1; + + qunlock(&c->l); + return c; +} + +static void +cmdproc(void *a) +{ + Conv *c; + int n; + char status[ERRMAX]; + void *t; + + c = a; + qlock(&c->l); + if(Debug) + print("f[0]=%q f[1]=%q\n", c->cmd->f[0], c->cmd->f[1]); + if(waserror()){ + if(Debug) + print("failed: %q\n", up->env->errstr); + kstrdup(&c->error, up->env->errstr); + c->state = "Done"; + qunlock(&c->l); + Wakeup(&c->startr); + pexit("cmdproc", 0); + } + t = oscmd(c->cmd->f+1, c->nice, c->dir, c->fd); + if(t == nil) + oserror(); + c->child = t; /* to allow oscmdkill */ + poperror(); + qunlock(&c->l); + Wakeup(&c->startr); + if(Debug) + print("started\n"); + while(waserror()) + oscmdkill(t); + osenter(); + n = oscmdwait(t, status, sizeof(status)); + osleave(); + if(n < 0){ + oserrstr(up->genbuf, sizeof(up->genbuf)); + n = snprint(status, sizeof(status), "0 0 0 0 %q", up->genbuf); + } + qlock(&c->l); + c->child = nil; + oscmdfree(t); + if(Debug){ + status[n]=0; + print("done %d %d %d: %q\n", c->fd[0], c->fd[1], c->fd[2], status); + } + if(c->inuse > 0){ + c->state = "Done"; + if(c->waitq != nil) + qproduce(c->waitq, status, n); + }else + closeconv(c); + qunlock(&c->l); + pexit("", 0); +} + +Dev cmddevtab = { + 'C', + "cmd", + + cmdinit, + cmdattach, + cmdwalk, + cmdstat, + cmdopen, + devcreate, + cmdclose, + cmdread, + devbread, + cmdwrite, + devbwrite, + devremove, + cmdwstat +}; diff --git a/emu/port/devcons.c b/emu/port/devcons.c new file mode 100644 index 00000000..8e823839 --- /dev/null +++ b/emu/port/devcons.c @@ -0,0 +1,675 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "version.h" +#include "mp.h" +#include "libsec.h" +#include "keyboard.h" + +extern int cflag; +int exdebug; +extern int keepbroken; + +enum +{ + Qdir, + Qcons, + Qconsctl, + Qdrivers, + Qhostowner, + Qhoststdin, + Qhoststdout, + Qhoststderr, + Qjit, + Qkeyboard, + Qkprint, + Qmemory, + Qmsec, + Qnotquiterandom, + Qnull, + Qpin, + Qrandom, + Qscancode, + Qsysctl, + Qsysname, + Qtime, + Quser +}; + +Dirtab contab[] = +{ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "cons", {Qcons}, 0, 0666, + "consctl", {Qconsctl}, 0, 0222, + "drivers", {Qdrivers}, 0, 0444, + "hostowner", {Qhostowner}, 0, 0644, + "hoststdin", {Qhoststdin}, 0, 0444, + "hoststdout", {Qhoststdout}, 0, 0222, + "hoststderr", {Qhoststderr}, 0, 0222, + "jit", {Qjit}, 0, 0666, + "keyboard", {Qkeyboard}, 0, 0666, + "kprint", {Qkprint}, 0, 0444, + "memory", {Qmemory}, 0, 0444, + "msec", {Qmsec}, NUMSIZE, 0444, + "notquiterandom", {Qnotquiterandom}, 0, 0444, + "null", {Qnull}, 0, 0666, + "pin", {Qpin}, 0, 0666, + "random", {Qrandom}, 0, 0444, + "scancode", {Qscancode}, 0, 0444, + "sysctl", {Qsysctl}, 0, 0644, + "sysname", {Qsysname}, 0, 0644, + "time", {Qtime}, 0, 0644, + "user", {Quser}, 0, 0644, +}; + +Queue* gkscanq; /* Graphics keyboard raw scancodes */ +char* gkscanid; /* name of raw scan format (if defined) */ +Queue* gkbdq; /* Graphics keyboard unprocessed input */ +Queue* kbdq; /* Console window unprocessed keyboard input */ +Queue* lineq; /* processed console input */ + +char *ossysname; + +static struct +{ + RWlock l; + Queue* q; +} kprintq; + +vlong timeoffset; + +extern int dflag; + +static int sysconwrite(void*, ulong); +extern char** rebootargv; + +static struct +{ + QLock q; + QLock gq; /* separate lock for the graphical input */ + + int raw; /* true if we shouldn't process input */ + Ref ctl; /* number of opens to the control file */ + Ref ptr; /* number of opens to the ptr file */ + int scan; /* true if reading raw scancodes */ + int x; /* index into line */ + char line[1024]; /* current input line */ + + Rune c; + int count; +} kbd; + +void +kbdslave(void *a) +{ + char b; + + USED(a); + for(;;) { + b = readkbd(); + if(kbd.raw == 0){ + switch(b){ + case 0x15: + write(1, "^U\n", 3); + break; + default: + write(1, &b, 1); + break; + } + } + qproduce(kbdq, &b, 1); + } + /* pexit("kbdslave", 0); */ /* not reached */ +} + +void +gkbdputc(Queue *q, int ch) +{ + int n; + Rune r; + static uchar kc[5*UTFmax]; + static int nk, collecting = 0; + char buf[UTFmax]; + + r = ch; + if(r == Latin) { + collecting = 1; + nk = 0; + return; + } + if(collecting) { + int c; + nk += runetochar((char*)&kc[nk], &r); + c = latin1(kc, nk); + if(c < -1) /* need more keystrokes */ + return; + collecting = 0; + if(c == -1) { /* invalid sequence */ + qproduce(q, kc, nk); + return; + } + r = (Rune)c; + } + n = runetochar(buf, &r); + if(n == 0) + return; + /* if(!isdbgkey(r)) */ + qproduce(q, buf, n); +} + +void +consinit(void) +{ + kbdq = qopen(512, 0, nil, nil); + if(kbdq == 0) + panic("no memory"); + lineq = qopen(2*1024, 0, nil, nil); + if(lineq == 0) + panic("no memory"); + gkbdq = qopen(512, 0, nil, nil); + if(gkbdq == 0) + panic("no memory"); + randominit(); +} + +/* + * return true if current user is eve + */ +int +iseve(void) +{ + return strcmp(eve, up->env->user) == 0; +} + +static Chan* +consattach(char *spec) +{ + static int kp; + + if(kp == 0 && !dflag) { + kp = 1; + kproc("kbd", kbdslave, 0, 0); + } + return devattach('c', spec); +} + +static Walkqid* +conswalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, contab, nelem(contab), devgen); +} + +static int +consstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, contab, nelem(contab), devgen); +} + +static Chan* +consopen(Chan *c, int omode) +{ + c = devopen(c, omode, contab, nelem(contab), devgen); + switch((ulong)c->qid.path) { + case Qconsctl: + incref(&kbd.ctl); + break; + + case Qscancode: + qlock(&kbd.gq); + if(gkscanq != nil || gkscanid == nil) { + qunlock(&kbd.q); + c->flag &= ~COPEN; + if(gkscanq) + error(Einuse); + else + error("not supported"); + } + gkscanq = qopen(256, 0, nil, nil); + qunlock(&kbd.gq); + break; + + case Qkprint: + wlock(&kprintq.l); + if(waserror()){ + wunlock(&kprintq.l); + c->flag &= ~COPEN; + nexterror(); + } + if(kprintq.q != nil) + error(Einuse); + kprintq.q = qopen(32*1024, Qcoalesce, nil, nil); + if(kprintq.q == nil) + error(Enomem); + qnoblock(kprintq.q, 1); + poperror(); + wunlock(&kprintq.l); + c->iounit = qiomaxatomic; + break; + } + return c; +} + +static void +consclose(Chan *c) +{ + if((c->flag & COPEN) == 0) + return; + + switch((ulong)c->qid.path) { + case Qconsctl: + /* last close of control file turns off raw */ + if(decref(&kbd.ctl) == 0) + kbd.raw = 0; + break; + + case Qscancode: + qlock(&kbd.gq); + if(gkscanq) { + qfree(gkscanq); + gkscanq = nil; + } + qunlock(&kbd.gq); + break; + + case Qkprint: + wlock(&kprintq.l); + qfree(kprintq.q); + kprintq.q = nil; + wunlock(&kprintq.l); + break; + } +} + +static long +consread(Chan *c, void *va, long n, vlong offset) +{ + ulong l; + int i, send; + char *p, buf[64], ch; + + if(c->qid.type & QTDIR) + return devdirread(c, va, n, contab, nelem(contab), devgen); + + switch((ulong)c->qid.path) { + default: + error(Egreg); + + case Qsysctl: + return readstr(offset, va, n, VERSION); + + case Qsysname: + if(ossysname == nil) + return 0; + return readstr(offset, va, n, ossysname); + + case Qrandom: + return randomread(va, n); + + case Qnotquiterandom: + genrandom(va, n); + return n; + + case Qpin: + p = "pin set"; + if(up->env->pgrp->pin == Nopin) + p = "no pin"; + return readstr(offset, va, n, p); + + case Qhostowner: + return readstr(offset, va, n, eve); + + case Qhoststdin: + return read(0, va, n); /* should be pread */ + + case Quser: + return readstr(offset, va, n, up->env->user); + + case Qjit: + snprint(buf, sizeof(buf), "%d", cflag); + return readstr(offset, va, n, buf); + + case Qtime: + snprint(buf, sizeof(buf), "%.lld", timeoffset + osusectime()); + return readstr(offset, va, n, buf); + + case Qdrivers: + p = malloc(READSTR); + if(p == nil) + error(Enomem); + l = 0; + for(i = 0; devtab[i] != nil; i++) + l += snprint(p+l, READSTR-l, "#%C %s\n", devtab[i]->dc, devtab[i]->name); + if(waserror()){ + free(p); + nexterror(); + } + n = readstr(offset, va, n, p); + poperror(); + free(p); + return n; + + case Qmemory: + return poolread(va, n, offset); + + case Qnull: + return 0; + + case Qmsec: + return readnum(offset, va, n, osmillisec(), NUMSIZE); + + case Qcons: + qlock(&kbd.q); + if(waserror()){ + qunlock(&kbd.q); + nexterror(); + } + + if(dflag) + error(Enonexist); + + while(!qcanread(lineq)) { + if(qread(kbdq, &ch, 1) == 0) + continue; + send = 0; + if(ch == 0){ + /* flush output on rawoff -> rawon */ + if(kbd.x > 0) + send = !qcanread(kbdq); + }else if(kbd.raw){ + kbd.line[kbd.x++] = ch; + send = !qcanread(kbdq); + }else{ + switch(ch){ + case '\b': + if(kbd.x) + kbd.x--; + break; + case 0x15: + kbd.x = 0; + break; + case 0x04: + send = 1; + break; + case '\n': + send = 1; + default: + kbd.line[kbd.x++] = ch; + break; + } + } + if(send || kbd.x == sizeof kbd.line){ + qwrite(lineq, kbd.line, kbd.x); + kbd.x = 0; + } + } + n = qread(lineq, va, n); + qunlock(&kbd.q); + poperror(); + return n; + + case Qscancode: + if(offset == 0) + return readstr(0, va, n, gkscanid); + return qread(gkscanq, va, n); + + case Qkeyboard: + return qread(gkbdq, va, n); + + case Qkprint: + rlock(&kprintq.l); + if(waserror()){ + runlock(&kprintq.l); + nexterror(); + } + n = qread(kprintq.q, va, n); + poperror(); + runlock(&kprintq.l); + return n; + } +} + +static long +conswrite(Chan *c, void *va, long n, vlong offset) +{ + char buf[128], *a, ch; + int x; + + if(c->qid.type & QTDIR) + error(Eperm); + + switch((ulong)c->qid.path) { + default: + error(Egreg); + + case Qcons: + if(canrlock(&kprintq.l)){ + if(kprintq.q != nil){ + if(waserror()){ + runlock(&kprintq.l); + nexterror(); + } + qwrite(kprintq.q, va, n); + poperror(); + runlock(&kprintq.l); + return n; + } + runlock(&kprintq.l); + } + return write(1, va, n); + + case Qsysctl: + return sysconwrite(va, n); + + case Qconsctl: + if(n >= sizeof(buf)) + n = sizeof(buf)-1; + strncpy(buf, va, n); + buf[n] = 0; + for(a = buf; a;){ + if(strncmp(a, "rawon", 5) == 0){ + kbd.raw = 1; + /* clumsy hack - wake up reader */ + ch = 0; + qwrite(kbdq, &ch, 1); + } else if(strncmp(buf, "rawoff", 6) == 0){ + kbd.raw = 0; + } + if((a = strchr(a, ' ')) != nil) + a++; + } + break; + + case Qkeyboard: + for(x=0; x<n; ) { + Rune r; + x += chartorune(&r, &((char*)va)[x]); + gkbdputc(gkbdq, r); + } + break; + + case Qnull: + break; + + case Qpin: + if(up->env->pgrp->pin != Nopin) + error("pin already set"); + if(n >= sizeof(buf)) + n = sizeof(buf)-1; + strncpy(buf, va, n); + buf[n] = '\0'; + up->env->pgrp->pin = atoi(buf); + break; + + case Qtime: + if(n >= sizeof(buf)) + n = sizeof(buf)-1; + strncpy(buf, va, n); + buf[n] = '\0'; + timeoffset = strtoll(buf, 0, 0)-osusectime(); + break; + + case Qhostowner: + if(!iseve()) + error(Eperm); + if(offset != 0 || n >= sizeof(buf)) + error(Ebadarg); + memmove(buf, va, n); + buf[n] = '\0'; + if(n > 0 && buf[n-1] == '\n') + buf[--n] = '\0'; + if(n == 0) + error(Ebadarg); + /* renameuser(eve, buf); */ + /* renameproguser(eve, buf); */ + kstrdup(&eve, buf); + kstrdup(&up->env->user, buf); + break; + + case Quser: + if(!iseve()) + error(Eperm); + if(offset != 0) + error(Ebadarg); + if(n <= 0 || n >= sizeof(buf)) + error(Ebadarg); + strncpy(buf, va, n); + buf[n] = '\0'; + if(n > 0 && buf[n-1] == '\n') + buf[--n] = '\0'; + if(n == 0) + error(Ebadarg); + setid(buf, 0); + break; + + case Qhoststdout: + return write(1, va, n); + + case Qhoststderr: + return write(2, va, n); + + case Qjit: + if(n >= sizeof(buf)) + n = sizeof(buf)-1; + strncpy(buf, va, n); + buf[n] = '\0'; + x = atoi(buf); + if(x < 0 || x > 9) + error(Ebadarg); + cflag = x; + break; + + case Qsysname: + if(offset != 0) + error(Ebadarg); + if(n < 0 || n >= sizeof(buf)) + error(Ebadarg); + strncpy(buf, va, n); + buf[n] = '\0'; + if(buf[n-1] == '\n') + buf[n-1] = 0; + kstrdup(&ossysname, buf); + break; + } + return n; +} + +static int +sysconwrite(void *va, ulong count) +{ + Cmdbuf *cb; + int e; + cb = parsecmd(va, count); + if(waserror()){ + free(cb); + nexterror(); + } + if(cb->nf == 0) + error(Enoctl); + if(strcmp(cb->f[0], "reboot") == 0){ + osreboot(rebootargv[0], rebootargv); + error("reboot not supported"); + }else if(strcmp(cb->f[0], "halt") == 0){ + if(cb->nf > 1) + e = atoi(cb->f[1]); + else + e = 0; + cleanexit(e); /* XXX ignored for the time being (and should be a string anyway) */ + }else if(strcmp(cb->f[0], "broken") == 0) + keepbroken = 1; + else if(strcmp(cb->f[0], "nobroken") == 0) + keepbroken = 0; + else if(strcmp(cb->f[0], "exdebug") == 0) + exdebug = !exdebug; + else + error(Enoctl); + poperror(); + free(cb); + return count; +} + +Dev consdevtab = { + 'c', + "cons", + + consinit, + consattach, + conswalk, + consstat, + consopen, + devcreate, + consclose, + consread, + devbread, + conswrite, + devbwrite, + devremove, + devwstat +}; + +static ulong randn; + +static void +seedrand(void) +{ + randomread((void*)&randn, sizeof(randn)); +} + +int +nrand(int n) +{ + if(randn == 0) + seedrand(); + randn = randn*1103515245 + 12345 + osusectime(); + return (randn>>16) % n; +} + +int +rand(void) +{ + nrand(1); + return randn; +} + +ulong +truerand(void) +{ + ulong x; + + randomread(&x, sizeof(x)); + return x; +} + +QLock grandomlk; + +void +_genrandomqlock(void) +{ + qlock(&grandomlk); +} + + +void +_genrandomqunlock(void) +{ + qunlock(&grandomlk); +} diff --git a/emu/port/devdraw.c b/emu/port/devdraw.c new file mode 100644 index 00000000..bc6d6352 --- /dev/null +++ b/emu/port/devdraw.c @@ -0,0 +1,2016 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + +#include <draw.h> +#include <memdraw.h> +#include <memlayer.h> +#include <cursor.h> +//#include "screen.h" + +enum +{ + Qtopdir = 0, + Qnew, + Q3rd, + Q2nd, + Qcolormap, + Qctl, + Qdata, + Qrefresh +}; + +/* + * Qid path is: + * 4 bits of file type (qids above) + * 24 bits of mux slot number +1; 0 means not attached to client + */ +#define QSHIFT 4 /* location in qid of client # */ + +#define QID(q) ((((ulong)(q).path)&0x0000000F)>>0) +#define CLIENTPATH(q) ((((ulong)q)&0x7FFFFFF0)>>QSHIFT) +#define CLIENT(q) CLIENTPATH((q).path) + +#define NHASH (1<<5) +#define HASHMASK (NHASH-1) +#define IOUNIT (64*1024) + +typedef struct Client Client; +typedef struct Draw Draw; +typedef struct DImage DImage; +typedef struct DScreen DScreen; +typedef struct CScreen CScreen; +typedef struct FChar FChar; +typedef struct Refresh Refresh; +typedef struct Refx Refx; +typedef struct DName DName; + +ulong blanktime = 30; /* in minutes; a half hour */ + +struct Draw +{ + QLock q; + int clientid; + int nclient; + Client** client; + int nname; + DName* name; + int vers; + int softscreen; + int blanked; /* screen turned off */ + ulong blanktime; /* time of last operation */ + ulong savemap[3*256]; +}; + +struct Client +{ + Ref r; + DImage* dimage[NHASH]; + CScreen* cscreen; + Refresh* refresh; + Rendez refrend; + uchar* readdata; + int nreaddata; + int busy; + int clientid; + int slot; + int refreshme; + int infoid; + int op; /* compositing operator - SoverD by default */ +}; + +struct Refresh +{ + DImage* dimage; + Rectangle r; + Refresh* next; +}; + +struct Refx +{ + Client* client; + DImage* dimage; +}; + +struct DName +{ + char *name; + Client *client; + DImage* dimage; + int vers; +}; + +struct FChar +{ + int minx; /* left edge of bits */ + int maxx; /* right edge of bits */ + uchar miny; /* first non-zero scan-line */ + uchar maxy; /* last non-zero scan-line + 1 */ + schar left; /* offset of baseline */ + uchar width; /* width of baseline */ +}; + +/* + * Reference counts in DImages: + * one per open by original client + * one per screen image or fill + * one per image derived from this one by name + */ +struct DImage +{ + int id; + int ref; + char *name; + int vers; + Memimage* image; + int ascent; + int nfchar; + FChar* fchar; + DScreen* dscreen; /* 0 if not a window */ + DImage* fromname; /* image this one is derived from, by name */ + DImage* next; +}; + +struct CScreen +{ + DScreen* dscreen; + CScreen* next; +}; + +struct DScreen +{ + int id; + int public; + int ref; + DImage *dimage; + DImage *dfill; + Memscreen* screen; + Client* owner; + DScreen* next; +}; + +static Draw sdraw; + Memimage *screenimage; /* accessed by some win-*.c */ +static Memdata screendata; +static Rectangle flushrect; +static int waste; +static DScreen* dscreen; +extern void flushmemscreen(Rectangle); + void drawmesg(Client*, void*, int); + void drawuninstall(Client*, int); + void drawfreedimage(DImage*); + Client* drawclientofpath(ulong); +static int drawclientop(Client*); + +static char Enodrawimage[] = "unknown id for draw image"; +static char Enodrawscreen[] = "unknown id for draw screen"; +static char Eshortdraw[] = "short draw message"; +static char Eshortread[] = "draw read too short"; +static char Eimageexists[] = "image id in use"; +static char Escreenexists[] = "screen id in use"; +static char Edrawmem[] = "out of memory: image"; +static char Ereadoutside[] = "readimage outside image"; +static char Ewriteoutside[] = "writeimage outside image"; +static char Enotfont[] = "image not a font"; +static char Eindex[] = "character index out of range"; +static char Enoclient[] = "no such draw client"; +static char Enameused[] = "image name in use"; +static char Enoname[] = "no image with that name"; +static char Eoldname[] = "named image no longer valid"; +static char Enamed[] = "image already has name"; +static char Ewrongname[] = "wrong name for image"; + +static int +drawgen(Chan *c, char *name, Dirtab *tab, int x, int s, Dir *dp) +{ + int t; + Qid q; + ulong path; + Client *cl; + + USED(name); + USED(tab); + USED(x); + q.vers = 0; + + if(s == DEVDOTDOT){ + switch(QID(c->qid)){ + case Qtopdir: + case Q2nd: + mkqid(&q, Qtopdir, 0, QTDIR); + devdir(c, q, "#i", 0, eve, 0500, dp); + break; + case Q3rd: + cl = drawclientofpath(c->qid.path); + if(cl == nil) + strcpy(up->genbuf, "??"); + else + sprint(up->genbuf, "%d", cl->clientid); + mkqid(&q, Q2nd, 0, QTDIR); + devdir(c, q, up->genbuf, 0, eve, 0500, dp); + break; + default: + panic("drawwalk %llux", c->qid.path); + } + return 1; + } + + /* + * Top level directory contains the name of the device. + */ + t = QID(c->qid); + if(t == Qtopdir){ + switch(s){ + case 0: + mkqid(&q, Q2nd, 0, QTDIR); + devdir(c, q, "draw", 0, eve, 0555, dp); + break; + default: + return -1; + } + return 1; + } + + /* + * Second level contains "new" plus all the clients. + */ + if(t == Q2nd || t == Qnew){ + if(s == 0){ + mkqid(&q, Qnew, 0, QTFILE); + devdir(c, q, "new", 0, eve, 0666, dp); + } + else if(s <= sdraw.nclient){ + cl = sdraw.client[s-1]; + if(cl == 0) + return 0; + sprint(up->genbuf, "%d", cl->clientid); + mkqid(&q, (s<<QSHIFT)|Q3rd, 0, QTDIR); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); + return 1; + } + else + return -1; + return 1; + } + + /* + * Third level. + */ + path = c->qid.path&~((1<<QSHIFT)-1); /* slot component */ + q.vers = c->qid.vers; + q.type = QTFILE; + switch(s){ + case 0: + q.path = path|Qcolormap; + devdir(c, q, "colormap", 0, eve, 0600, dp); + break; + case 1: + q.path = path|Qctl; + devdir(c, q, "ctl", 0, eve, 0600, dp); + break; + case 2: + q.path = path|Qdata; + devdir(c, q, "data", 0, eve, 0600, dp); + break; + case 3: + q.path = path|Qrefresh; + devdir(c, q, "refresh", 0, eve, 0400, dp); + break; + default: + return -1; + } + return 1; +} + +static +int +drawrefactive(void *a) +{ + Client *c; + + c = a; + return c->refreshme || c->refresh!=0; +} + +static +void +drawrefreshscreen(DImage *l, Client *client) +{ + while(l != nil && l->dscreen == nil) + l = l->fromname; + if(l != nil && l->dscreen->owner != client) + l->dscreen->owner->refreshme = 1; +} + +static +void +drawrefresh(Memimage *l, Rectangle r, void *v) +{ + Refx *x; + DImage *d; + Client *c; + Refresh *ref; + + USED(l); + if(v == 0) + return; + x = v; + c = x->client; + d = x->dimage; + for(ref=c->refresh; ref; ref=ref->next) + if(ref->dimage == d){ + combinerect(&ref->r, r); + return; + } + ref = malloc(sizeof(Refresh)); + if(ref){ + ref->dimage = d; + ref->r = r; + ref->next = c->refresh; + c->refresh = ref; + } +} + +static void +addflush(Rectangle r) +{ + int abb, ar, anbb; + Rectangle nbb; + + if(sdraw.softscreen==0 || !rectclip(&r, screenimage->r)) + return; + + if(flushrect.min.x >= flushrect.max.x){ + flushrect = r; + waste = 0; + return; + } + nbb = flushrect; + combinerect(&nbb, r); + ar = Dx(r)*Dy(r); + abb = Dx(flushrect)*Dy(flushrect); + anbb = Dx(nbb)*Dy(nbb); + /* + * Area of new waste is area of new bb minus area of old bb, + * less the area of the new segment, which we assume is not waste. + * This could be negative, but that's OK. + */ + waste += anbb-abb - ar; + if(waste < 0) + waste = 0; + /* + * absorb if: + * total area is small + * waste is less than half total area + * rectangles touch + */ + if(anbb<=1024 || waste*2<anbb || rectXrect(flushrect, r)){ + flushrect = nbb; + return; + } + /* emit current state */ + if(flushrect.min.x < flushrect.max.x) + flushmemscreen(flushrect); + flushrect = r; + waste = 0; +} + +static +void +dstflush(Memimage *dst, Rectangle r) +{ + Memlayer *l; + + if(dst == screenimage){ + combinerect(&flushrect, r); + return; + } + l = dst->layer; + if(l == nil) + return; + do{ + if(l->screen->image->data != screenimage->data) + return; + r = rectaddpt(r, l->delta); + l = l->screen->image->layer; + }while(l); + addflush(r); +} + +static +void +drawflush(void) +{ + if(flushrect.min.x < flushrect.max.x) + flushmemscreen(flushrect); + flushrect = Rect(10000, 10000, -10000, -10000); +} + +static +int +drawcmp(char *a, char *b, int n) +{ + if(strlen(a) != n) + return 1; + return memcmp(a, b, n); +} + +DName* +drawlookupname(int n, char *str) +{ + DName *name, *ename; + + name = sdraw.name; + ename = &name[sdraw.nname]; + for(; name<ename; name++) + if(drawcmp(name->name, str, n) == 0) + return name; + return 0; +} + +int +drawgoodname(DImage *d) +{ + DName *n; + + /* if window, validate the screen's own images */ + if(d->dscreen) + if(drawgoodname(d->dscreen->dimage) == 0 + || drawgoodname(d->dscreen->dfill) == 0) + return 0; + if(d->name == nil) + return 1; + n = drawlookupname(strlen(d->name), d->name); + if(n==nil || n->vers!=d->vers) + return 0; + return 1; +} + +DImage* +drawlookup(Client *client, int id, int checkname) +{ + DImage *d; + + d = client->dimage[id&HASHMASK]; + while(d){ + if(d->id == id){ + if(checkname && !drawgoodname(d)) + error(Eoldname); + return d; + } + d = d->next; + } + return 0; +} + +DScreen* +drawlookupdscreen(int id) +{ + DScreen *s; + + s = dscreen; + while(s){ + if(s->id == id) + return s; + s = s->next; + } + return 0; +} + +DScreen* +drawlookupscreen(Client *client, int id, CScreen **cs) +{ + CScreen *s; + + s = client->cscreen; + while(s){ + if(s->dscreen->id == id){ + *cs = s; + return s->dscreen; + } + s = s->next; + } + error(Enodrawscreen); + return 0; +} + +Memimage* +drawinstall(Client *client, int id, Memimage *i, DScreen *dscreen) +{ + DImage *d; + + d = malloc(sizeof(DImage)); + if(d == 0) + return 0; + d->id = id; + d->ref = 1; + d->name = 0; + d->vers = 0; + d->image = i; + d->nfchar = 0; + d->fchar = 0; + d->fromname = 0; + d->dscreen = dscreen; + d->next = client->dimage[id&HASHMASK]; + client->dimage[id&HASHMASK] = d; + return i; +} + +Memscreen* +drawinstallscreen(Client *client, DScreen *d, int id, DImage *dimage, DImage *dfill, int public) +{ + Memscreen *s; + CScreen *c; + + c = malloc(sizeof(CScreen)); + if(dimage && dimage->image && dimage->image->chan == 0) + panic("bad image %p in drawinstallscreen", dimage->image); + + if(c == 0) + return 0; + if(d == 0){ + d = malloc(sizeof(DScreen)); + if(d == 0){ + free(c); + return 0; + } + s = malloc(sizeof(Memscreen)); + if(s == 0){ + free(c); + free(d); + return 0; + } + s->frontmost = 0; + s->rearmost = 0; + d->dimage = dimage; + if(dimage){ + s->image = dimage->image; + dimage->ref++; + } + d->dfill = dfill; + if(dfill){ + s->fill = dfill->image; + dfill->ref++; + } + d->ref = 0; + d->id = id; + d->screen = s; + d->public = public; + d->next = dscreen; + d->owner = client; + dscreen = d; + } + c->dscreen = d; + d->ref++; + c->next = client->cscreen; + client->cscreen = c; + return d->screen; +} + +void +drawdelname(DName *name) +{ + int i; + + i = name-sdraw.name; + memmove(name, name+1, (sdraw.nname-(i+1))*sizeof(DName)); + sdraw.nname--; +} + +void +drawfreedscreen(DScreen *this) +{ + DScreen *ds, *next; + + this->ref--; + if(this->ref < 0) + print("negative ref in drawfreedscreen\n"); + if(this->ref > 0) + return; + ds = dscreen; + if(ds == this){ + dscreen = this->next; + goto Found; + } + while(next = ds->next){ /* assign = */ + if(next == this){ + ds->next = this->next; + goto Found; + } + ds = next; + } + error(Enodrawimage); + + Found: + if(this->dimage) + drawfreedimage(this->dimage); + if(this->dfill) + drawfreedimage(this->dfill); + free(this->screen); + free(this); +} + +void +drawfreedimage(DImage *dimage) +{ + int i; + Memimage *l; + DScreen *ds; + + dimage->ref--; + if(dimage->ref < 0) + print("negative ref in drawfreedimage\n"); + if(dimage->ref > 0) + return; + + /* any names? */ + for(i=0; i<sdraw.nname; ) + if(sdraw.name[i].dimage == dimage) + drawdelname(sdraw.name+i); + else + i++; + if(dimage->fromname){ /* acquired by name; owned by someone else*/ + drawfreedimage(dimage->fromname); + goto Return; + } + if(dimage->image == screenimage) /* don't free the display */ + goto Return; + ds = dimage->dscreen; + if(ds){ + l = dimage->image; + if(l->data == screenimage->data) + dstflush(l->layer->screen->image, l->layer->screenr); + if(l->layer->refreshfn == drawrefresh) /* else true owner will clean up */ + free(l->layer->refreshptr); + l->layer->refreshptr = nil; + if(drawgoodname(dimage)) + memldelete(l); + else + memlfree(l); + drawfreedscreen(ds); + }else + freememimage(dimage->image); + Return: + free(dimage->fchar); + free(dimage); +} + +void +drawuninstallscreen(Client *client, CScreen *this) +{ + CScreen *cs, *next; + + cs = client->cscreen; + if(cs == this){ + client->cscreen = this->next; + drawfreedscreen(this->dscreen); + free(this); + return; + } + while(next = cs->next){ /* assign = */ + if(next == this){ + cs->next = this->next; + drawfreedscreen(this->dscreen); + free(this); + return; + } + cs = next; + } +} + +void +drawuninstall(Client *client, int id) +{ + DImage *d, *next; + + d = client->dimage[id&HASHMASK]; + if(d == 0) + error(Enodrawimage); + if(d->id == id){ + client->dimage[id&HASHMASK] = d->next; + drawfreedimage(d); + return; + } + while(next = d->next){ /* assign = */ + if(next->id == id){ + d->next = next->next; + drawfreedimage(next); + return; + } + d = next; + } + error(Enodrawimage); +} + +void +drawaddname(Client *client, DImage *di, int n, char *str) +{ + DName *name, *ename, *new, *t; + + name = sdraw.name; + ename = &name[sdraw.nname]; + for(; name<ename; name++) + if(drawcmp(name->name, str, n) == 0) + error(Enameused); + t = smalloc((sdraw.nname+1)*sizeof(DName)); + memmove(t, sdraw.name, sdraw.nname*sizeof(DName)); + free(sdraw.name); + sdraw.name = t; + new = &sdraw.name[sdraw.nname++]; + new->name = smalloc(n+1); + memmove(new->name, str, n); + new->name[n] = 0; + new->dimage = di; + new->client = client; + new->vers = ++sdraw.vers; +} + +Client* +drawnewclient(void) +{ + Client *cl, **cp; + int i; + + for(i=0; i<sdraw.nclient; i++){ + cl = sdraw.client[i]; + if(cl == 0) + break; + } + if(i == sdraw.nclient){ + cp = malloc((sdraw.nclient+1)*sizeof(Client*)); + if(cp == 0) + return 0; + memmove(cp, sdraw.client, sdraw.nclient*sizeof(Client*)); + free(sdraw.client); + sdraw.client = cp; + sdraw.nclient++; + cp[i] = 0; + } + cl = malloc(sizeof(Client)); + if(cl == 0) + return 0; + memset(cl, 0, sizeof(Client)); + cl->slot = i; + cl->clientid = ++sdraw.clientid; + cl->op = SoverD; + sdraw.client[i] = cl; + return cl; +} + +static int +drawclientop(Client *cl) +{ + int op; + + op = cl->op; + cl->op = SoverD; + return op; +} + +int +drawhasclients(void) +{ + /* + * if draw has ever been used, we can't resize the frame buffer, + * even if all clients have exited (nclients is cumulative); it's too + * hard to make work. + */ + return sdraw.nclient != 0; +} + +Client* +drawclientofpath(ulong path) +{ + Client *cl; + int slot; + + slot = CLIENTPATH(path); + if(slot == 0) + return nil; + cl = sdraw.client[slot-1]; + if(cl==0 || cl->clientid==0) + return nil; + return cl; +} + + +Client* +drawclient(Chan *c) +{ + Client *client; + + client = drawclientofpath(c->qid.path); + if(client == nil) + error(Enoclient); + return client; +} + +Memimage* +drawimage(Client *client, uchar *a) +{ + DImage *d; + + d = drawlookup(client, BGLONG(a), 1); + if(d == nil) + error(Enodrawimage); + return d->image; +} + +void +drawrectangle(Rectangle *r, uchar *a) +{ + r->min.x = BGLONG(a+0*4); + r->min.y = BGLONG(a+1*4); + r->max.x = BGLONG(a+2*4); + r->max.y = BGLONG(a+3*4); +} + +void +drawpoint(Point *p, uchar *a) +{ + p->x = BGLONG(a+0*4); + p->y = BGLONG(a+1*4); +} + +Point +drawchar(Memimage *dst, Point p, Memimage *src, Point *sp, DImage *font, int index, int op) +{ + FChar *fc; + Rectangle r; + Point sp1; + + fc = &font->fchar[index]; + r.min.x = p.x+fc->left; + r.min.y = p.y-(font->ascent-fc->miny); + r.max.x = r.min.x+(fc->maxx-fc->minx); + r.max.y = r.min.y+(fc->maxy-fc->miny); + sp1.x = sp->x+fc->left; + sp1.y = sp->y+fc->miny; + memdraw(dst, r, src, sp1, font->image, Pt(fc->minx, fc->miny), op); + p.x += fc->width; + sp->x += fc->width; + return p; +} + +static int +initscreenimage(void) +{ + int width, depth; + ulong chan; + Rectangle r; + + if(screenimage != nil) + return 1; + + memimageinit(); + screendata.base = nil; + screendata.bdata = attachscreen(&r, &chan, &depth, &width, &sdraw.softscreen); + if(screendata.bdata == nil) + return 0; + screendata.ref = 1; + + screenimage = allocmemimaged(r, chan, &screendata); + if(screenimage == nil){ + /* RSC: BUG: detach screen */ + return 0; + } + + screenimage->width = width; + screenimage->clipr = r; + return 1; +} + +void +deletescreenimage(void) +{ + qlock(&sdraw.q); + /* RSC: BUG: detach screen */ + if(screenimage) + freememimage(screenimage); + screenimage = nil; + qunlock(&sdraw.q); +} + +Chan* +drawattach(char *spec) +{ + qlock(&sdraw.q); + if(!initscreenimage()){ + qunlock(&sdraw.q); + error("no frame buffer"); + } + qunlock(&sdraw.q); + return devattach('i', spec); +} + +static Walkqid* +drawwalk(Chan *c, Chan *nc, char **name, int nname) +{ + if(screendata.bdata == nil) + error("no frame buffer"); + return devwalk(c, nc, name, nname, 0, 0, drawgen); +} + +static int +drawstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, 0, 0, drawgen); +} + +static Chan* +drawopen(Chan *c, int omode) +{ + Client *cl; + + if(c->qid.type & QTDIR) + return devopen(c, omode, 0, 0, drawgen); + + qlock(&sdraw.q); + if(waserror()){ + qunlock(&sdraw.q); + nexterror(); + } + + if(QID(c->qid) == Qnew){ + cl = drawnewclient(); + if(cl == 0) + error(Enodev); + c->qid.path = Qctl|((cl->slot+1)<<QSHIFT); + } + + switch(QID(c->qid)){ + case Qnew: + break; + + case Qctl: + cl = drawclient(c); + if(cl->busy) + error(Einuse); + cl->busy = 1; + flushrect = Rect(10000, 10000, -10000, -10000); + drawinstall(cl, 0, screenimage, 0); + incref(&cl->r); + break; + case Qcolormap: + case Qdata: + case Qrefresh: + cl = drawclient(c); + incref(&cl->r); + break; + } + qunlock(&sdraw.q); + poperror(); + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + c->iounit = IOUNIT; + return c; +} + +static void +drawclose(Chan *c) +{ + int i; + DImage *d, **dp; + Client *cl; + Refresh *r; + + if(QID(c->qid) < Qcolormap) /* Qtopdir, Qnew, Q3rd, Q2nd have no client */ + return; + qlock(&sdraw.q); + if(waserror()){ + qunlock(&sdraw.q); + nexterror(); + } + + cl = drawclient(c); + if(QID(c->qid) == Qctl) + cl->busy = 0; + if((c->flag&COPEN) && (decref(&cl->r)==0)){ + while(r = cl->refresh){ /* assign = */ + cl->refresh = r->next; + free(r); + } + /* free names */ + for(i=0; i<sdraw.nname; ) + if(sdraw.name[i].client == cl) + drawdelname(sdraw.name+i); + else + i++; + while(cl->cscreen) + drawuninstallscreen(cl, cl->cscreen); + /* all screens are freed, so now we can free images */ + dp = cl->dimage; + for(i=0; i<NHASH; i++){ + while((d = *dp) != nil){ + *dp = d->next; + drawfreedimage(d); + } + dp++; + } + sdraw.client[cl->slot] = 0; + drawflush(); /* to erase visible, now dead windows */ + free(cl); + } + qunlock(&sdraw.q); + poperror(); +} + +long +drawread(Chan *c, void *a, long n, vlong off) +{ + int index, m; + ulong red, green, blue; + Client *cl; + uchar *p; + Refresh *r; + DImage *di; + Memimage *i; + ulong offset = off; + char buf[16]; + + USED(offset); + if(c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, drawgen); + cl = drawclient(c); + qlock(&sdraw.q); + if(waserror()){ + qunlock(&sdraw.q); + nexterror(); + } + switch(QID(c->qid)){ + case Qctl: + if(n < 12*12) + error(Eshortread); + if(cl->infoid < 0) + error(Enodrawimage); + if(cl->infoid == 0){ + i = screenimage; + if(i == nil) + error(Enodrawimage); + }else{ + di = drawlookup(cl, cl->infoid, 1); + if(di == nil) + error(Enodrawimage); + i = di->image; + } + n = sprint(a, "%11d %11d %11s %11d %11d %11d %11d %11d %11d %11d %11d %11d ", + cl->clientid, cl->infoid, chantostr(buf, i->chan), (i->flags&Frepl)==Frepl, + i->r.min.x, i->r.min.y, i->r.max.x, i->r.max.y, + i->clipr.min.x, i->clipr.min.y, i->clipr.max.x, i->clipr.max.y); + cl->infoid = -1; + break; + + case Qcolormap: +#ifdef COLORMAP + p = malloc(4*12*256+1); + if(p == 0) + error(Enomem); + m = 0; + for(index = 0; index < 256; index++){ + getcolor(index, &red, &green, &blue); + m += sprint((char*)p+m, "%11d %11lud %11lud %11lud\n", index, red>>24, green>>24, blue>>24); + } + n = readstr(offset, a, n, (char*)p); + free(p); +#else + n = 0; +#endif + break; + + case Qdata: + if(cl->readdata == nil) + error("no draw data"); + if(n < cl->nreaddata) + error(Eshortread); + n = cl->nreaddata; + memmove(a, cl->readdata, cl->nreaddata); + free(cl->readdata); + cl->readdata = nil; + break; + + case Qrefresh: + if(n < 5*4) + error(Ebadarg); + for(;;){ + if(cl->refreshme || cl->refresh) + break; + qunlock(&sdraw.q); + if(waserror()){ + qlock(&sdraw.q); /* restore lock for waserror() above */ + nexterror(); + } + Sleep(&cl->refrend, drawrefactive, cl); + poperror(); + qlock(&sdraw.q); + } + p = a; + while(cl->refresh && n>=5*4){ + r = cl->refresh; + BPLONG(p+0*4, r->dimage->id); + BPLONG(p+1*4, r->r.min.x); + BPLONG(p+2*4, r->r.min.y); + BPLONG(p+3*4, r->r.max.x); + BPLONG(p+4*4, r->r.max.y); + cl->refresh = r->next; + free(r); + p += 5*4; + n -= 5*4; + } + cl->refreshme = 0; + n = p-(uchar*)a; + } + qunlock(&sdraw.q); + poperror(); + return n; +} + +void +drawwakeall(void) +{ + Client *cl; + int i; + + for(i=0; i<sdraw.nclient; i++){ + cl = sdraw.client[i]; + if(cl && (cl->refreshme || cl->refresh)) + Wakeup(&cl->refrend); + } +} + +static long +drawwrite(Chan *c, void *a, long n, vlong off) +{ + char buf[128], *fields[4], *q; + Client *cl; + int i, m, red, green, blue, x; + ulong offset = off; + + USED(offset); + if(c->qid.type & QTDIR) + error(Eisdir); + cl = drawclient(c); + qlock(&sdraw.q); + if(waserror()){ + drawwakeall(); + qunlock(&sdraw.q); + nexterror(); + } + switch(QID(c->qid)){ + case Qctl: + if(n != 4) + error("unknown draw control request"); + cl->infoid = BGLONG((uchar*)a); + break; + + case Qcolormap: +#ifdef COLORMAP + m = n; + n = 0; + while(m > 0){ + x = m; + if(x > sizeof(buf)-1) + x = sizeof(buf)-1; + q = memccpy(buf, a, '\n', x); + if(q == 0) + break; + i = q-buf; + n += i; + a = (char*)a + i; + m -= i; + *q = 0; + if(tokenize(buf, fields, nelem(fields)) != 4) + error(Ebadarg); + i = strtoul(fields[0], 0, 0); + red = strtoul(fields[1], 0, 0); + green = strtoul(fields[2], 0, 0); + blue = strtoul(fields[3], &q, 0); + if(fields[3] == q) + error(Ebadarg); + if(red>255 || green>255 || blue>255 || i<0 || i>255) + error(Ebadarg); + red |= red<<8; + red |= red<<16; + green |= green<<8; + green |= green<<16; + blue |= blue<<8; + blue |= blue<<16; + setcolor(i, red, green, blue); + } +#else + n = 0; +#endif + break; + + case Qdata: + drawmesg(cl, a, n); + drawwakeall(); + break; + + default: + error(Ebadusefd); + } + qunlock(&sdraw.q); + poperror(); + return n; +} + +uchar* +drawcoord(uchar *p, uchar *maxp, int oldx, int *newx) +{ + int b, x; + + if(p >= maxp) + error(Eshortdraw); + b = *p++; + x = b & 0x7F; + if(b & 0x80){ + if(p+1 >= maxp) + error(Eshortdraw); + x |= *p++ << 7; + x |= *p++ << 15; + if(x & (1<<22)) + x |= ~0<<23; + }else{ + if(b & 0x40) + x |= ~0<<7; + x += oldx; + } + *newx = x; + return p; +} + +static void +printmesg(char *fmt, uchar *a, int plsprnt) +{ + char buf[256]; + char *p, *q; + int s; + + if(1|| plsprnt==0){ + SET(s); SET(q); SET(p); + USED(fmt); USED(a); USED(buf); USED(p); USED(q); USED(s); + return; + } + q = buf; + *q++ = *a++; + for(p=fmt; *p; p++){ + switch(*p){ + case 'l': + q += sprint(q, " %ld", (long)BGLONG(a)); + a += 4; + break; + case 'L': + q += sprint(q, " %.8lux", (ulong)BGLONG(a)); + a += 4; + break; + case 'R': + q += sprint(q, " [%d %d %d %d]", BGLONG(a), BGLONG(a+4), BGLONG(a+8), BGLONG(a+12)); + a += 16; + break; + case 'P': + q += sprint(q, " [%d %d]", BGLONG(a), BGLONG(a+4)); + a += 8; + break; + case 'b': + q += sprint(q, " %d", *a++); + break; + case 's': + q += sprint(q, " %d", BGSHORT(a)); + a += 2; + break; + case 'S': + q += sprint(q, " %.4ux", BGSHORT(a)); + a += 2; + break; + } + } + *q++ = '\n'; + *q = 0; + iprint("%.*s", (int)(q-buf), buf); +} + +void +drawmesg(Client *client, void *av, int n) +{ + int c, op, repl, m, y, dstid, scrnid, ni, ci, j, nw, e0, e1, ox, oy, esize, oesize, doflush; + uchar *u, *a, refresh; + char *fmt; + ulong value, chan; + Rectangle r, clipr; + Point p, q, *pp, sp; + Memimage *i, *dst, *src, *mask; + Memimage *l, **lp; + Memscreen *scrn; + DImage *font, *ll, *di, *ddst, *dsrc; + DName *dn; + DScreen *dscrn; + FChar *fc; + Refx *refx; + CScreen *cs; + Refreshfn reffn; + + a = av; + m = 0; + fmt = nil; + if(waserror()){ + if(fmt) printmesg(fmt, a, 1); + /* iprint("error: %s\n", up->env->errstr); */ + nexterror(); + } + while((n-=m) > 0){ + USED(fmt); + a += m; + switch(*a){ + default: + error("bad draw command"); + /* new allocate: 'b' id[4] screenid[4] refresh[1] chan[4] repl[1] R[4*4] clipR[4*4] rrggbbaa[4] */ + case 'b': + printmesg(fmt="LLbLbRRL", a, 0); + m = 1+4+4+1+4+1+4*4+4*4+4; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + scrnid = BGSHORT(a+5); + refresh = a[9]; + chan = BGLONG(a+10); + repl = a[14]; + drawrectangle(&r, a+15); + drawrectangle(&clipr, a+31); + value = BGLONG(a+47); + if(drawlookup(client, dstid, 0)) + error(Eimageexists); + if(scrnid){ + dscrn = drawlookupscreen(client, scrnid, &cs); + scrn = dscrn->screen; + if(repl || chan!=scrn->image->chan) + error("image parameters incompatible with screen"); + reffn = nil; + switch(refresh){ + case Refbackup: + break; + case Refnone: + reffn = memlnorefresh; + break; + case Refmesg: + reffn = drawrefresh; + break; + default: + error("unknown refresh method"); + } + l = memlalloc(scrn, r, reffn, 0, value); + if(l == 0) + error(Edrawmem); + dstflush(l->layer->screen->image, l->layer->screenr); + l->clipr = clipr; + rectclip(&l->clipr, r); + if(drawinstall(client, dstid, l, dscrn) == 0){ + memldelete(l); + error(Edrawmem); + } + dscrn->ref++; + if(reffn){ + refx = nil; + if(reffn == drawrefresh){ + refx = malloc(sizeof(Refx)); + if(refx == 0){ + drawuninstall(client, dstid); + error(Edrawmem); + } + refx->client = client; + refx->dimage = drawlookup(client, dstid, 1); + } + memlsetrefresh(l, reffn, refx); + } + continue; + } + i = allocmemimage(r, chan); + if(i == 0) + error(Edrawmem); + if(repl) + i->flags |= Frepl; + i->clipr = clipr; + if(!repl) + rectclip(&i->clipr, r); + if(drawinstall(client, dstid, i, 0) == 0){ + freememimage(i); + error(Edrawmem); + } + memfillcolor(i, value); + continue; + + /* allocate screen: 'A' id[4] imageid[4] fillid[4] public[1] */ + case 'A': + printmesg(fmt="LLLb", a, 1); + m = 1+4+4+4+1; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + if(dstid == 0) + error(Ebadarg); + if(drawlookupdscreen(dstid)) + error(Escreenexists); + ddst = drawlookup(client, BGLONG(a+5), 1); + dsrc = drawlookup(client, BGLONG(a+9), 1); + if(ddst==0 || dsrc==0) + error(Enodrawimage); + if(drawinstallscreen(client, 0, dstid, ddst, dsrc, a[13]) == 0) + error(Edrawmem); + continue; + + /* set repl and clip: 'c' dstid[4] repl[1] clipR[4*4] */ + case 'c': + printmesg(fmt="LbR", a, 0); + m = 1+4+1+4*4; + if(n < m) + error(Eshortdraw); + ddst = drawlookup(client, BGLONG(a+1), 1); + if(ddst == nil) + error(Enodrawimage); + if(ddst->name) + error("cannot change repl/clipr of shared image"); + dst = ddst->image; + if(a[5]) + dst->flags |= Frepl; + drawrectangle(&dst->clipr, a+6); + continue; + + /* draw: 'd' dstid[4] srcid[4] maskid[4] R[4*4] P[2*4] P[2*4] */ + case 'd': + printmesg(fmt="LLLRPP", a, 0); + m = 1+4+4+4+4*4+2*4+2*4; + if(n < m) + error(Eshortdraw); + dst = drawimage(client, a+1); + src = drawimage(client, a+5); + mask = drawimage(client, a+9); + drawrectangle(&r, a+13); + drawpoint(&p, a+29); + drawpoint(&q, a+37); + op = drawclientop(client); + memdraw(dst, r, src, p, mask, q, op); + dstflush(dst, r); + continue; + + /* toggle debugging: 'D' val[1] */ + case 'D': + printmesg(fmt="b", a, 0); + m = 1+1; + if(n < m) + error(Eshortdraw); + drawdebug = a[1]; + continue; + + /* ellipse: 'e' dstid[4] srcid[4] center[2*4] a[4] b[4] thick[4] sp[2*4] alpha[4] phi[4]*/ + case 'e': + case 'E': + printmesg(fmt="LLPlllPll", a, 0); + m = 1+4+4+2*4+4+4+4+2*4+2*4; + if(n < m) + error(Eshortdraw); + dst = drawimage(client, a+1); + src = drawimage(client, a+5); + drawpoint(&p, a+9); + e0 = BGLONG(a+17); + e1 = BGLONG(a+21); + if(e0<0 || e1<0) + error("invalid ellipse semidiameter"); + j = BGLONG(a+25); + if(j < 0) + error("negative ellipse thickness"); + drawpoint(&sp, a+29); + c = j; + if(*a == 'E') + c = -1; + ox = BGLONG(a+37); + oy = BGLONG(a+41); + op = drawclientop(client); + /* high bit indicates arc angles are present */ + if(ox & (1<<31)){ + if((ox & (1<<30)) == 0) + ox &= ~(1<<31); + memarc(dst, p, e0, e1, c, src, sp, ox, oy, op); + }else + memellipse(dst, p, e0, e1, c, src, sp, op); + dstflush(dst, Rect(p.x-e0-j, p.y-e1-j, p.x+e0+j+1, p.y+e1+j+1)); + continue; + + /* free: 'f' id[4] */ + case 'f': + printmesg(fmt="L", a, 1); + m = 1+4; + if(n < m) + error(Eshortdraw); + ll = drawlookup(client, BGLONG(a+1), 0); + if(ll && ll->dscreen && ll->dscreen->owner != client) + ll->dscreen->owner->refreshme = 1; + drawuninstall(client, BGLONG(a+1)); + continue; + + /* free screen: 'F' id[4] */ + case 'F': + printmesg(fmt="L", a, 1); + m = 1+4; + if(n < m) + error(Eshortdraw); + drawlookupscreen(client, BGLONG(a+1), &cs); + drawuninstallscreen(client, cs); + continue; + + /* initialize font: 'i' fontid[4] nchars[4] ascent[1] */ + case 'i': + printmesg(fmt="Llb", a, 1); + m = 1+4+4+1; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + dst = drawimage(client, a+1); + if(dstid == 0 || dst == screenimage) + error("cannot use display as font"); + font = drawlookup(client, dstid, 1); + if(font == 0) + error(Enodrawimage); + if(font->image->layer) + error("cannot use window as font"); + ni = BGLONG(a+5); + if(ni<=0 || ni>4096) + error("bad font size (4096 chars max)"); + free(font->fchar); /* should we complain if non-zero? */ + font->fchar = malloc(ni*sizeof(FChar)); + if(font->fchar == 0) + error(Enomem); + memset(font->fchar, 0, ni*sizeof(FChar)); + font->nfchar = ni; + font->ascent = a[9]; + continue; + + /* load character: 'l' fontid[4] srcid[4] index[2] R[4*4] P[2*4] left[1] width[1] */ + case 'l': + printmesg(fmt="LLSRPbb", a, 0); + m = 1+4+4+2+4*4+2*4+1+1; + if(n < m) + error(Eshortdraw); + font = drawlookup(client, BGLONG(a+1), 1); + if(font == 0) + error(Enodrawimage); + if(font->nfchar == 0) + error(Enotfont); + src = drawimage(client, a+5); + ci = BGSHORT(a+9); + if(ci >= font->nfchar) + error(Eindex); + drawrectangle(&r, a+11); + drawpoint(&p, a+27); + memdraw(font->image, r, src, p, memopaque, p, SoverD); + fc = &font->fchar[ci]; + fc->minx = r.min.x; + fc->maxx = r.max.x; + fc->miny = r.min.y; + fc->maxy = r.max.y; + fc->left = a[35]; + fc->width = a[36]; + continue; + + /* draw line: 'L' dstid[4] p0[2*4] p1[2*4] end0[4] end1[4] radius[4] srcid[4] sp[2*4] */ + case 'L': + printmesg(fmt="LPPlllLP", a, 0); + m = 1+4+2*4+2*4+4+4+4+4+2*4; + if(n < m) + error(Eshortdraw); + dst = drawimage(client, a+1); + drawpoint(&p, a+5); + drawpoint(&q, a+13); + e0 = BGLONG(a+21); + e1 = BGLONG(a+25); + j = BGLONG(a+29); + if(j < 0) + error("negative line width"); + src = drawimage(client, a+33); + drawpoint(&sp, a+37); + op = drawclientop(client); + memline(dst, p, q, e0, e1, j, src, sp, op); + /* avoid memlinebbox if possible */ + if(dst == screenimage || dst->layer!=nil){ + /* BUG: this is terribly inefficient: update maximal containing rect*/ + r = memlinebbox(p, q, e0, e1, j); + dstflush(dst, insetrect(r, -(1+1+j))); + } + continue; + + /* create image mask: 'm' newid[4] id[4] */ +/* + * + case 'm': + printmesg("LL", a, 0); + m = 4+4; + if(n < m) + error(Eshortdraw); + break; + * + */ + + /* attach to a named image: 'n' dstid[4] j[1] name[j] */ + case 'n': + printmesg(fmt="Lz", a, 0); + m = 1+4+1; + if(n < m) + error(Eshortdraw); + j = a[5]; + if(j == 0) /* give me a non-empty name please */ + error(Eshortdraw); + m += j; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + if(drawlookup(client, dstid, 0)) + error(Eimageexists); + dn = drawlookupname(j, (char*)a+6); + if(dn == nil) + error(Enoname); + if(drawinstall(client, dstid, dn->dimage->image, 0) == 0) + error(Edrawmem); + di = drawlookup(client, dstid, 0); + if(di == 0) + error("draw: cannot happen"); + di->vers = dn->vers; + di->name = smalloc(j+1); + di->fromname = dn->dimage; + di->fromname->ref++; + memmove(di->name, a+6, j); + di->name[j] = 0; + client->infoid = dstid; + continue; + + /* name an image: 'N' dstid[4] in[1] j[1] name[j] */ + case 'N': + printmesg(fmt="Lbz", a, 0); + m = 1+4+1+1; + if(n < m) + error(Eshortdraw); + c = a[5]; + j = a[6]; + if(j == 0) /* give me a non-empty name please */ + error(Eshortdraw); + m += j; + if(n < m) + error(Eshortdraw); + di = drawlookup(client, BGLONG(a+1), 0); + if(di == 0) + error(Enodrawimage); + if(di->name) + error(Enamed); + if(c) + drawaddname(client, di, j, (char*)a+7); + else{ + dn = drawlookupname(j, (char*)a+7); + if(dn == nil) + error(Enoname); + if(dn->dimage != di) + error(Ewrongname); + drawdelname(dn); + } + continue; + + /* position window: 'o' id[4] r.min [2*4] screenr.min [2*4] */ + case 'o': + printmesg(fmt="LPP", a, 0); + m = 1+4+2*4+2*4; + if(n < m) + error(Eshortdraw); + dst = drawimage(client, a+1); + if(dst->layer){ + drawpoint(&p, a+5); + drawpoint(&q, a+13); + r = dst->layer->screenr; + ni = memlorigin(dst, p, q); + if(ni < 0) + error("image origin failed"); + if(ni > 0){ + dstflush(dst->layer->screen->image, r); + dstflush(dst->layer->screen->image, dst->layer->screenr); + ll = drawlookup(client, BGLONG(a+1), 1); + drawrefreshscreen(ll, client); + } + } + continue; + + /* set compositing operator for next draw operation: 'O' op */ + case 'O': + printmesg(fmt="b", a, 0); + m = 1+1; + if(n < m) + error(Eshortdraw); + client->op = *(a+1); + continue; + + /* filled polygon: 'P' dstid[4] n[2] wind[4] ignore[2*4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */ + /* polygon: 'p' dstid[4] n[2] end0[4] end1[4] radius[4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */ + case 'p': + case 'P': + printmesg(fmt="LslllLPP", a, 0); + m = 1+4+2+4+4+4+4+2*4; + if(n < m) + error(Eshortdraw); + dst = drawimage(client, a+1); + ni = BGSHORT(a+5); + if(ni < 0) + error("negative count in polygon"); + e0 = BGLONG(a+7); + e1 = BGLONG(a+11); + j = 0; + if(*a == 'p'){ + j = BGLONG(a+15); + if(j < 0) + error("negative polygon line width"); + } + src = drawimage(client, a+19); + drawpoint(&sp, a+23); + drawpoint(&p, a+31); + ni++; + pp = malloc(ni*sizeof(Point)); + if(pp == nil) + error(Enomem); + doflush = 0; + if(dst == screenimage || (dst->layer && dst->layer->screen->image->data == screenimage->data)) + doflush = 1; /* simplify test in loop */ + ox = oy = 0; + esize = 0; + u = a+m; + for(y=0; y<ni; y++){ + q = p; + oesize = esize; + u = drawcoord(u, a+n, ox, &p.x); + u = drawcoord(u, a+n, oy, &p.y); + ox = p.x; + oy = p.y; + if(doflush){ + esize = j; + if(*a == 'p'){ + if(y == 0){ + c = memlineendsize(e0); + if(c > esize) + esize = c; + } + if(y == ni-1){ + c = memlineendsize(e1); + if(c > esize) + esize = c; + } + } + if(*a=='P' && e0!=1 && e0 !=~0) + r = dst->clipr; + else if (y>0){ + r = Rect(q.x-oesize, q.y-oesize, q.x+oesize+1, q.y+oesize+1); + combinerect(&r, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1)); + } + if (rectclip(&r, dst->clipr)) /* should perhaps be an arg to dstflush */ + dstflush(dst, r); + } + pp[y] = p; + } + if (y == 1) + dstflush(dst, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1)); + op = drawclientop(client); + if(*a == 'p') + mempoly(dst, pp, ni, e0, e1, j, src, sp, op); + else + memfillpoly(dst, pp, ni, e0, src, sp, op); + free(pp); + m = u-a; + continue; + + /* read: 'r' id[4] R[4*4] */ + case 'r': + printmesg(fmt="LR", a, 0); + m = 1+4+4*4; + if(n < m) + error(Eshortdraw); + i = drawimage(client, a+1); + drawrectangle(&r, a+5); + if(!rectinrect(r, i->r)) + error(Ereadoutside); + c = bytesperline(r, i->depth); + c *= Dy(r); + free(client->readdata); + client->readdata = mallocz(c, 0); + if(client->readdata == nil) + error("readimage malloc failed"); + client->nreaddata = memunload(i, r, client->readdata, c); + if(client->nreaddata < 0){ + free(client->readdata); + client->readdata = nil; + error("bad readimage call"); + } + continue; + + /* string: 's' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] ni*(index[2]) */ + /* stringbg: 'x' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] bgid[4] bgpt[2*4] ni*(index[2]) */ + case 's': + case 'x': + printmesg(fmt="LLLPRPs", a, 0); + m = 1+4+4+4+2*4+4*4+2*4+2; + if(*a == 'x') + m += 4+2*4; + if(n < m) + error(Eshortdraw); + + dst = drawimage(client, a+1); + src = drawimage(client, a+5); + font = drawlookup(client, BGLONG(a+9), 1); + if(font == 0) + error(Enodrawimage); + if(font->nfchar == 0) + error(Enotfont); + drawpoint(&p, a+13); + drawrectangle(&r, a+21); + drawpoint(&sp, a+37); + ni = BGSHORT(a+45); + u = a+m; + m += ni*2; + if(n < m) + error(Eshortdraw); + clipr = dst->clipr; + dst->clipr = r; + op = drawclientop(client); + if(*a == 'x'){ + /* paint background */ + l = drawimage(client, a+47); + drawpoint(&q, a+51); + r.min.x = p.x; + r.min.y = p.y-font->ascent; + r.max.x = p.x; + r.max.y = r.min.y+Dy(font->image->r); + j = ni; + while(--j >= 0){ + ci = BGSHORT(u); + if(ci<0 || ci>=font->nfchar){ + dst->clipr = clipr; + error(Eindex); + } + r.max.x += font->fchar[ci].width; + u += 2; + } + memdraw(dst, r, l, q, memopaque, ZP, op); + u -= 2*ni; + } + q = p; + while(--ni >= 0){ + ci = BGSHORT(u); + if(ci<0 || ci>=font->nfchar){ + dst->clipr = clipr; + error(Eindex); + } + q = drawchar(dst, q, src, &sp, font, ci, op); + u += 2; + } + dst->clipr = clipr; + p.y -= font->ascent; + dstflush(dst, Rect(p.x, p.y, q.x, p.y+Dy(font->image->r))); + continue; + + /* use public screen: 'S' id[4] chan[4] */ + case 'S': + printmesg(fmt="Ll", a, 0); + m = 1+4+4; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + if(dstid == 0) + error(Ebadarg); + dscrn = drawlookupdscreen(dstid); + if(dscrn==0 || (dscrn->public==0 && dscrn->owner!=client)) + error(Enodrawscreen); + if(dscrn->screen->image->chan != BGLONG(a+5)) + error("inconsistent chan"); + if(drawinstallscreen(client, dscrn, 0, 0, 0, 0) == 0) + error(Edrawmem); + continue; + + /* top or bottom windows: 't' top[1] nw[2] n*id[4] */ + case 't': + printmesg(fmt="bsL", a, 0); + m = 1+1+2; + if(n < m) + error(Eshortdraw); + nw = BGSHORT(a+2); + if(nw < 0) + error(Ebadarg); + if(nw == 0) + continue; + m += nw*4; + if(n < m) + error(Eshortdraw); + lp = malloc(nw*sizeof(Memimage*)); + if(lp == 0) + error(Enomem); + if(waserror()){ + free(lp); + nexterror(); + } + for(j=0; j<nw; j++) + lp[j] = drawimage(client, a+1+1+2+j*4); + if(lp[0]->layer == 0) + error("images are not windows"); + for(j=1; j<nw; j++) + if(lp[j]->layer->screen != lp[0]->layer->screen) + error("images not on same screen"); + if(a[1]) + memltofrontn(lp, nw); + else + memltorearn(lp, nw); + if(lp[0]->layer->screen->image->data == screenimage->data) + for(j=0; j<nw; j++) + dstflush(lp[j]->layer->screen->image, lp[j]->layer->screenr); + ll = drawlookup(client, BGLONG(a+1+1+2), 1); + drawrefreshscreen(ll, client); + poperror(); + free(lp); + continue; + + /* visible: 'v' */ + case 'v': + printmesg(fmt="", a, 0); + m = 1; + drawflush(); + continue; + + /* write: 'y' id[4] R[4*4] data[x*1] */ + /* write from compressed data: 'Y' id[4] R[4*4] data[x*1] */ + case 'y': + case 'Y': + printmesg(fmt="LR", a, 0); + // iprint("load %c\n", *a); + m = 1+4+4*4; + if(n < m) + error(Eshortdraw); + dst = drawimage(client, a+1); + drawrectangle(&r, a+5); + if(!rectinrect(r, dst->r)) + error(Ewriteoutside); + y = memload(dst, r, a+m, n-m, *a=='Y'); + if(y < 0) + error("bad writeimage call"); + dstflush(dst, r); + m += y; + continue; + } + } + poperror(); +} + +int +drawlsetrefresh(ulong qidpath, int id, void *reffn, void *refx) +{ + DImage *d; + Memimage *i; + Client *client; + + client = drawclientofpath(qidpath); + if(client == 0) + return 0; + d = drawlookup(client, id, 0); + if(d == nil) + return 0; + i = d->image; + if(i->layer == nil) + return 0; + return memlsetrefresh(i, reffn, refx); +} + +void +drawqlock(void) +{ + qlock(&sdraw.q); +} + +void +drawqunlock(void) +{ + qunlock(&sdraw.q); +} + +void +interf(void) +{ + /* force it to load */ + drawreplxy(0, 0, 0); +} + +Dev drawdevtab = { + 'i', + "draw", + + devinit, + drawattach, + drawwalk, + drawstat, + drawopen, + devcreate, + drawclose, + drawread, + devbread, + drawwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/emu/port/devdup.c b/emu/port/devdup.c new file mode 100644 index 00000000..7796a8f9 --- /dev/null +++ b/emu/port/devdup.c @@ -0,0 +1,150 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "interp.h" + +/* Qid is (2*fd + (file is ctl))+1 */ + +static int +dupgen(Chan *c, char *name, Dirtab *tab, int ntab, int s, Dir *dp) +{ + Fgrp *fgrp = up->env->fgrp; + Chan *f; + static int perm[] = { 0400, 0200, 0600, 0 }; + int p; + Qid q; + + USED(name); USED(tab); USED(ntab); + if(s == DEVDOTDOT){ + devdir(c, c->qid, ".", 0, eve, DMDIR|0555, dp); + return 1; + } + if(s == 0) + return 0; + s--; + if(s/2 > fgrp->maxfd) + return -1; + if((f=fgrp->fd[s/2]) == nil) + return 0; + if(s & 1){ + p = 0400; + sprint(up->genbuf, "%dctl", s/2); + }else{ + p = perm[f->mode&3]; + sprint(up->genbuf, "%d", s/2); + } + mkqid(&q, s+1, 0, QTFILE); + devdir(c, q, up->genbuf, 0, eve, p, dp); + return 1; +} + +static Chan* +dupattach(char *spec) +{ + return devattach('d', spec); +} + +static Walkqid* +dupwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, nil, 0, dupgen); +} + +static int +dupstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, nil, 0, dupgen); +} + +static Chan* +dupopen(Chan *c, int omode) +{ + Chan *f; + int fd, twicefd; + + if(c->qid.type & QTDIR){ + if(omode != 0) + error(Eisdir); + c->mode = 0; + c->flag |= COPEN; + c->offset = 0; + return c; + } + if(c->qid.type & QTAUTH) + error(Eperm); + twicefd = c->qid.path - 1; + fd = twicefd/2; + if((twicefd & 1)){ + /* ctl file */ + f = c; + f->mode = openmode(omode); + f->flag |= COPEN; + f->offset = 0; + }else{ + /* fd file */ + f = fdtochan(up->env->fgrp, fd, openmode(omode), 0, 1); + cclose(c); + } + if(omode & OCEXEC) + f->flag |= CCEXEC; + return f; +} + +static void +dupclose(Chan *c) +{ + USED(c); +} + +static long +dupread(Chan *c, void *va, long n, vlong offset) +{ + char *a = va; + char buf[256]; + int fd, twicefd; + + if(c->qid.type == QTDIR) + return devdirread(c, a, n, nil, 0, dupgen); + twicefd = c->qid.path - 1; + fd = twicefd/2; + if(twicefd & 1){ + c = fdtochan(up->env->fgrp, fd, -1, 0, 1); + if(waserror()){ + cclose(c); + nexterror(); + } + progfdprint(c, fd, 0, buf, sizeof buf); + poperror(); + cclose(c); + return readstr((ulong)offset, va, n, buf); + } + panic("dupread"); + return 0; +} + +static long +dupwrite(Chan *c, void *a, long n, vlong o) +{ + USED(c); USED(a); USED(n); USED(o); + panic("dupwrite"); + return 0; /* not reached */ +} + +Dev dupdevtab = { + 'd', + "dup", + + devinit, + dupattach, + dupwalk, + dupstat, + dupopen, + devcreate, + dupclose, + dupread, + devbread, + dupwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/emu/port/devdynld.c b/emu/port/devdynld.c new file mode 100644 index 00000000..68084707 --- /dev/null +++ b/emu/port/devdynld.c @@ -0,0 +1,357 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include <a.out.h> +#include <dynld.h> + +#define DBG if(1) print + +extern ulong ndevs; + +enum +{ + Qdir, + Qdynld, + Qdynsyms, + + DEVCHAR = 'L', +}; + +static Dirtab dltab[] = +{ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "dynld", {Qdynld}, 0, 0644, + "dynsyms", {Qdynsyms}, 0, 0444, +}; + +enum +{ + DLdev, + DLudev, +}; + +static Cmdtab dlcmd[] = +{ + DLdev, "dev", 2, + DLudev, "udev", 2, +}; + +typedef struct Dyndev Dyndev; + +struct Dyndev +{ + char *path; + Dynobj *o; + Dev *dev; + Dyndev *next; +}; + +static Dyndev *loaded; +static QLock dllock; + +typedef struct Fd Fd; +struct Fd { + int fd; +}; + +static long +readfd(void *a, void *buf, long nbytes) +{ + return kread(((Fd*)a)->fd, buf, nbytes); +} + +static vlong +seekfd(void *a, vlong off, int t) +{ + return kseek(((Fd*)a)->fd, off, t); +} + +static void +errfd(char *s) +{ + kstrcpy(up->env->errstr, s, ERRMAX); +} + +static void +dlfree(Dyndev *l) +{ + if(l != nil){ + free(l->path); + dynobjfree(l->o); + free(l); + } +} + +static Dyndev* +dlload(char *path, Dynsym *tab, int ntab) +{ + Fd f; + Dyndev *l; + + f.fd = kopen(path, OREAD); + if(f.fd < 0) + error("cannot open"); + if(waserror()){ + kclose(f.fd); + nexterror(); + } + l = mallocz(sizeof(Dyndev), 1); + if(l == nil) + error(Enomem); + if(waserror()){ + dlfree(l); + nexterror(); + } + l->path = strdup(path); + if(l->path == nil) + error(Enomem); + l->o = dynloadgen(&f, readfd, seekfd, errfd, tab, ntab, 0); + if(l->o == nil) + error(up->env->errstr); + poperror(); + poperror(); + kclose(f.fd); + return l; +} + +static void +devload(char *path) +{ + int i; + Dyndev *l; + Dev *dev; + char devname[32]; + + l = dlload(path, _exporttab, dyntabsize(_exporttab)); + if(waserror()){ + dlfree(l); + nexterror(); + } + snprint(devname, sizeof(devname), "%sdevtab", "XXX"); /* TO DO */ + dev = dynimport(l->o, devname, signof(*dev)); + if(dev == nil) + error("no devtab"); + if(devno(dev->dc, 1) >= 0) + error("device loaded"); + for(i = 0; devtab[i] != nil; i++) + ; + if(i >= ndevs || devtab[i+1] != nil) + error("device table full"); + l->dev = devtab[i] = dev; + dev->init(); + l->next = loaded; + loaded = l; + poperror(); +} + +static void +devunload(char *path) +{ + int i, dc; + Dyndev *l, **ll; + + dc = 0; + if(strlen(path) == 1) + dc = path[0]; + for(ll = &loaded; *ll != nil; ll = &(*ll)->next){ + if(path != nil && strcmp(path, (*ll)->path) == 0) + break; + if(dc != 0 && (*ll)->dev && dc == (*ll)->dev->dc) + break; + } + if((l = *ll) != nil){ + for(i = 0; i < ndevs; i++) + if(l->dev == devtab[i]){ + devtab[i] = nil; + break; + } +/* + if(l->dev) + l->dev->shutdown(); +*/ + *ll = l->next; + dlfree(l); + } +} + +static long +readdl(void *a, ulong n, ulong offset) +{ + char *p; + Dyndev *l; + int m, len; + + m = 0; + for(l = loaded; l != nil; l = l->next) + m++; + m *= 48; + p = malloc(m); + if(p == nil) + error(Enomem); + if(waserror()){ + free(p); + nexterror(); + } + *p = 0; + len = 0; + for(l = loaded; l != nil; l = l->next) + if(l->dev) + len += snprint(p+len, m-len, "#%C\t%.8p\t%.8lux\t%s\n", + l->dev->dc, l->o->base, l->o->size, l->dev->name); + n = readstr(offset, a, n, p); + poperror(); + free(p); + return n; +} + +static long +readsyms(char *a, ulong n, ulong offset) +{ + char *p; + Dynsym *t; + long l, nr; + + p = malloc(READSTR); + if(p == nil) + error(Enomem); + if(waserror()){ + free(p); + nexterror(); + } + nr = 0; + for(t = _exporttab; n > 0 && t->name != nil; t++){ + l = snprint(p, READSTR, "%.8lux %.8lux %s\n", t->addr, t->sig, t->name); + if(offset >= l){ + offset -= l; + continue; + } + l = readstr(offset, a, n, p); + offset = 0; + n -= l; + a += l; + nr += l; + } + poperror(); + free(p); + return nr; +} + +static Chan* +dlattach(char *spec) +{ + return devattach(DEVCHAR, spec); +} + +static Walkqid* +dlwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, dltab, nelem(dltab), devgen); +} + +static int +dlstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, dltab, nelem(dltab), devgen); +} + +static Chan* +dlopen(Chan *c, int omode) +{ + return devopen(c, omode, dltab, nelem(dltab), devgen); +} + +static void +dlclose(Chan *c) +{ + USED(c); +} + +static long +dlread(Chan *c, void *a, long n, vlong voffset) +{ + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, a, n, dltab, nelem(dltab), devgen); + case Qdynld: + return readdl(a, n, (ulong)voffset); + case Qdynsyms: + return readsyms(a, n, (ulong)voffset); + default: + error(Egreg); + } + return n; +} + +static long +dlwrite(Chan *c, void *a, long n, vlong voffset) +{ + Cmdbuf *cmd; + Cmdtab *ct; + + USED(voffset); + switch((ulong)c->qid.path){ + case Qdynld: + cmd = parsecmd(a, n); + qlock(&dllock); + if(waserror()){ + qunlock(&dllock); + free(cmd); + nexterror(); + } + ct = lookupcmd(cmd, dlcmd, nelem(dlcmd)); + switch(ct->index){ + case DLdev: + devload(cmd->f[1]); + break; + case DLudev: + devunload(cmd->f[1]); + break; + } + poperror(); + qunlock(&dllock); + free(cmd); + break; + default: + error(Egreg); + } + return n; +} + +Dev dynlddevtab = { + DEVCHAR, + "dynld", + + devinit, + dlattach, + dlwalk, + dlstat, + dlopen, + devcreate, + dlclose, + dlread, + devbread, + dlwrite, + devbwrite, + devremove, + devwstat, +}; + +/* auxiliary routines for dynamic loading of C modules */ + +Dynobj* +dynld(int fd) +{ + Fd f; + + f.fd = fd; + return dynloadgen(&f, readfd, seekfd, errfd, _exporttab, dyntabsize(_exporttab), 0); +} + +int +dynldable(int fd) +{ + Fd f; + + f.fd = fd; + return dynloadable(&f, readfd, seekfd); +} diff --git a/emu/port/devdynldx.c b/emu/port/devdynldx.c new file mode 100644 index 00000000..738b9ed5 --- /dev/null +++ b/emu/port/devdynldx.c @@ -0,0 +1,357 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include <a.out.h> +#include <dynld.h> + +/* + * TO DO + * - dynamic allocation of Dev.dc + * - inter-module dependencies + * - reference count on Dev to allow error("inuse") [or how else to do it] + * - is Dev.shutdown the right function to call? Dev.config perhaps? + */ + +#define DBG if(1) print + +extern ulong ndevs; + +enum +{ + Qdir, + Qdynld, + Qdynsyms, + + DEVCHAR = 'L', +}; + +static Dirtab dltab[] = +{ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "dynld", {Qdynld}, 0, 0644, + "dynsyms", {Qdynsyms}, 0, 0444, +}; + +typedef struct Dyndev Dyndev; + +struct Dyndev +{ + char* name; /* device name (eg, "dynld") */ + char* tag; /* version tag (eg, MD5 or SHA1 hash of content) */ + char* path; /* file from whence it came */ + Dynobj* o; + Dev* dev; + Dyndev* next; +}; + +static Dyndev *loaded; +static QLock dllock; + +static Dyndev** finddyndev(char*); +static int matched(Dyndev*, char*, char*); + +extern Dynobj* kdynloadfd(int, Dynsym*, int, ulong); + +static void +dlfree(Dyndev *l) +{ + if(l != nil){ + free(l->tag); + free(l->name); + free(l->path); + dynobjfree(l->o); + free(l); + } +} + +static Dyndev* +dlload(char *path, Dynsym *tab, int ntab) +{ + Dyndev *l; + int fd; + + /* in Plan 9, would probably use Chan* interface here */ + fd = kopen(path, OREAD); + if(fd < 0) + error("cannot open"); + if(waserror()){ + kclose(fd); + nexterror(); + } + l = mallocz(sizeof(Dyndev), 1); + if(l == nil) + error(Enomem); + if(waserror()){ + dlfree(l); + nexterror(); + } + l->path = strdup(path); + if(l->path == nil) + error(Enomem); + l->o = kdynloadfd(fd, tab, ntab, 0); + if(l->o == nil) + error(up->env->errstr); + poperror(); + poperror(); + kclose(fd); + return l; +} + +static void +devload(char *name, char *path, char *tag) +{ + int i; + Dyndev *l, **lp; + Dev *dev; + char tabname[32]; + + lp = finddyndev(name); + if(*lp != nil) + error("already loaded"); /* i'm assuming the name (eg, "cons") is to be unique */ + l = dlload(path, _exporttab, dyntabsize(_exporttab)); + if(waserror()){ + dlfree(l); + nexterror(); + } + snprint(tabname, sizeof(tabname), "%sdevtab", name); + dev = dynimport(l->o, tabname, signof(*dev)); + if(dev == nil) + errorf("can't find %sdevtab in module", name); + kstrdup(&l->name, name); + kstrdup(&l->tag, tag != nil? tag: ""); + if(dev->name == nil) + dev->name = l->name; + else if(strcmp(dev->name, l->name) != 0) + errorf("module file has device %s", dev->name); + /* TO DO: allocate dev->dc dynamically (cf. brucee's driver) */ + if(devno(dev->dc, 1) >= 0) + errorf("devchar %C already used", dev->dc); + for(i = 0; devtab[i] != nil; i++) + ; + if(i >= ndevs || devtab[i+1] != nil) + error("device table full"); +#ifdef NATIVE + i = splhi(); + dev->reset(); + splx(i); +#endif + dev->init(); + l->dev = devtab[i] = dev; + l->next = loaded; + loaded = l; /* recently loaded ones first: good unload order? */ + poperror(); +} + +static Dyndev** +finddyndev(char *name) +{ + Dyndev *l, **lp; + + for(lp = &loaded; (l = *lp) != nil; lp = &l->next) + if(strcmp(l->name, name) == 0) + break; + return lp; +} + +static int +matched(Dyndev *l, char *path, char *tag) +{ + if(path != nil && strcmp(l->path, path) != 0) + return 0; + if(tag != nil && strcmp(l->tag, tag) != 0) + return 0; + return 1; +} + +static void +devunload(char *name, char *path, char *tag) +{ + int i; + Dyndev *l, **lp; + + lp = finddyndev(name); + l = *lp; + if(l == nil) + error("not loaded"); + if(!matched(l, path, tag)) + error("path/tag mismatch"); + for(i = 0; i < ndevs; i++) + if(l->dev == devtab[i]){ + devtab[i] = nil; /* TO DO: ensure driver is not currently in use */ + break; + } +#ifdef NATIVE + l->dev->shutdown(); +#endif + *lp = l->next; + dlfree(l); +} + +static long +readdynld(void *a, ulong n, ulong offset) +{ + char *p; + Dyndev *l; + int m, len; + + m = 0; + for(l = loaded; l != nil; l = l->next) + m += 48 + strlen(l->name) + strlen(l->path) + strlen(l->tag); + p = malloc(m); + if(p == nil) + error(Enomem); + if(waserror()){ + free(p); + nexterror(); + } + *p = 0; + len = 0; + for(l = loaded; l != nil; l = l->next) + if(l->dev) + len += snprint(p+len, m-len, "#%C\t%.8p\t%.8lud\t%q\t%q\t%q\n", + l->dev->dc, l->o->base, l->o->size, l->name, l->path, l->tag); + n = readstr(offset, a, n, p); + poperror(); + free(p); + return n; +} + +static long +readsyms(char *a, ulong n, ulong offset) +{ + char *p; + Dynsym *t; + long l, nr; + + p = malloc(READSTR); + if(p == nil) + error(Enomem); + if(waserror()){ + free(p); + nexterror(); + } + nr = 0; + for(t = _exporttab; n > 0 && t->name != nil; t++){ + l = snprint(p, READSTR, "%.8lux %.8lux %s\n", t->addr, t->sig, t->name); + if(offset >= l){ + offset -= l; + continue; + } + l = readstr(offset, a, n, p); + offset = 0; + n -= l; + a += l; + nr += l; + } + poperror(); + free(p); + return nr; +} + +static Chan* +dlattach(char *spec) +{ + return devattach(DEVCHAR, spec); +} + +static Walkqid* +dlwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, dltab, nelem(dltab), devgen); +} + +static int +dlstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, dltab, nelem(dltab), devgen); +} + +static Chan* +dlopen(Chan *c, int omode) +{ + return devopen(c, omode, dltab, nelem(dltab), devgen); +} + +static void +dlclose(Chan *c) +{ + USED(c); +} + +static long +dlread(Chan *c, void *a, long n, vlong voffset) +{ + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, a, n, dltab, nelem(dltab), devgen); + case Qdynld: + return readdynld(a, n, voffset); + case Qdynsyms: + return readsyms(a, n, voffset); + default: + error(Egreg); + } + return n; +} + +static long +dlwrite(Chan *c, void *a, long n, vlong voffset) +{ + Cmdbuf *cb; + char *name, *tag, *path; + + USED(voffset); + switch((ulong)c->qid.path){ + case Qdynld: + cb = parsecmd(a, n); + qlock(&dllock); + if(waserror()){ + qunlock(&dllock); + free(cb); + nexterror(); + } + if(cb->nf < 3 || strcmp(cb->f[1], "dev") != 0) /* only do devices */ + cmderror(cb, Ebadctl); + name = cb->f[2]; + path = nil; + if(cb->nf > 3) + path = cb->f[3]; + tag = nil; + if(cb->nf > 4) + tag = cb->f[4]; + if(strcmp(cb->f[0], "load") == 0){ + if(path == nil) + cmderror(cb, "missing load path"); + devload(name, path, tag); /* TO DO: remaining parameters might be dependencies? */ + }else if(strcmp(cb->f[0], "unload") == 0) + devunload(name, path, tag); + else + cmderror(cb, Ebadctl); + poperror(); + qunlock(&dllock); + free(cb); + break; + default: + error(Egreg); + } + return n; +} + +Dev dynlddevtab = { + DEVCHAR, + "dynld", + + devinit, + dlattach, + dlwalk, + dlstat, + dlopen, + devcreate, + dlclose, + dlread, + devbread, + dlwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/emu/port/deveia-bsd.c b/emu/port/deveia-bsd.c new file mode 100644 index 00000000..3640837d --- /dev/null +++ b/emu/port/deveia-bsd.c @@ -0,0 +1,95 @@ +/* + * BSD serial port control features not found in POSIX + * including modem line control and hardware flow control + */ + +static struct flagmap lines[] = { + {"cts", TIOCM_CTS}, + {"dsr", TIOCM_DSR}, + {"ring", TIOCM_RI}, + {"dcd", TIOCM_CD}, + {"dtr", TIOCM_DTR}, + {"rts", TIOCM_RTS}, + {0, -1} +}; + +static void +resxtra(int port, struct termios *ts) +{ + int fd = eia[port].fd; + + USED(ts); + + if(eia[port].dtr) + ioctl(fd, TIOCM_DTR, eia[port].dtr); + if(eia[port].rts) + ioctl(fd, TIOCM_RTS, eia[port].rts); + if(eia[port].cts) + ioctl(fd, TIOCM_CTS, eia[port].cts); +} + +static char * +rdxtra(int port, struct termios *ts, char *str) +{ + int fd = eia[port].fd; + int line; +// struct flagmap *lp; + char *s = str; + + USED(ts); + + if(ioctl(fd, TIOCMGET, &line) < 0) + oserror(); + +// for(lp = lines; lp->str; lp++) +// if(line&lp->flag) +// s += sprint(s, " %s", lp->str); + + return s; +} + +static char * +wrxtra(int port, struct termios *ts, char *cmd) +{ + int fd = eia[port].fd; + int n, r, flag, iocmd, *l; + + USED(ts); + + switch(*cmd) { + case 'D': + case 'd': + flag = TIOCM_DTR; + l = &eia[port].dtr; + break; + case 'R': + case 'r': + flag = TIOCM_RTS; + l = &eia[port].rts; + break; + case 'M': + case 'm': + flag = TIOCM_CTS; + l = &eia[port].cts; + break; + default: + return nil; + } + + n = atoi(cmd+1); + if(n) + iocmd = TIOCMBIS; + else + iocmd = TIOCMBIC; + + osenter(); + r = ioctl(fd, iocmd, &flag); + osleave(); + if(r < 0) + oserror(); + + eia[port].restore = 1; + *l = iocmd; + + return nil; +} diff --git a/emu/port/deveia-posix.c b/emu/port/deveia-posix.c new file mode 100644 index 00000000..5db8ae86 --- /dev/null +++ b/emu/port/deveia-posix.c @@ -0,0 +1,463 @@ +/* + * Driver for POSIX serial ports + */ +#include "dat.h" +#include "fns.h" +#include "error.h" +#undef _POSIX_C_SOURCE /* for deveia-bsd.c */ +#include <sys/stat.h> +#include <termios.h> + +enum +{ + Devchar = 't', + + Ndataqid = 1, + Nctlqid, + Nstatqid, + Nqid = 3, /* number of QIDs */ + + CTLS= 023, + CTLQ= 021, + + Maxctl = 128, + Maxfield = 32 +}; + +/* + * Macros to manage QIDs + */ +#define NETTYPE(x) ((x)&0x0F) +#define NETID(x) ((x)>>4) +#define NETQID(i,t) (((i)<<4)|(t)) + +static Dirtab *eiadir; +static int ndir; + +static char Devname[] = "eia"; + +typedef struct Eia Eia; +struct Eia { + Ref r; + int fd; + int overrun; + int frame; + int restore; /* flag to restore prev. states */ + struct termios ts; + int dtr; + int rts; + int cts; +}; + +static Eia *eia; + +struct tcdef_t { + int val; + tcflag_t flag; +}; + +struct flagmap { + char* s; + tcflag_t flag; +}; + +static struct tcdef_t bps[]; + +static struct tcdef_t size[] = { + {5, CS5}, + {6, CS6}, + {7, CS7}, + {8, CS8}, + {-1, -1} +}; + +static char * +ftos(char *buf, struct tcdef_t *tbl, tcflag_t flag) +{ + for(; tbl->val >= 0; tbl++) + if(tbl->flag == flag){ + sprint(buf, "%d", tbl->val); + return buf; + } + return "unknown"; +} + +static tcflag_t +stof(struct tcdef_t *tbl, int val) +{ + for(; tbl->val >= 0 && tbl->val != val; tbl++) + {} + return tbl->flag; +} + +static char * +rdxtra(int port, struct termios *ts, char *str); /* non-POSIX extensions */ + +static long +rdstat(int port, void *buf, long n, ulong offset) +{ + int fd = eia[port].fd; + struct termios ts; + char str[Maxctl]; + char sbuf[20]; + char *s; + + if(tcgetattr(fd, &ts) < 0) + oserror(); + + s = str; + s += sprint(s, "opens %d ferr %d oerr %d baud %s", + eia[port].r.ref-1, eia[port].frame, eia[port].overrun, + ftos(sbuf, bps, (tcflag_t)cfgetospeed(&ts))); + s = rdxtra(port, &ts, s); + sprint(s, "\n"); + + return readstr(offset, buf, n, str); +} + +static char * +wrxtra(int port, struct termios *ts, char *cmd); /* non-POSIX extensions */ + +static void +wrctl(int port, char *cmd) +{ + struct termios ts; + char *xerr; + int r, nf, n, i; + char *f[Maxfield]; + int fd = eia[port].fd; + tcflag_t flag; + + if(tcgetattr(fd, &ts) < 0) { +Error: + oserror(); + } + + nf = tokenize(cmd, f, nelem(f)); + for(i = 0; i < nf; i++){ + if(strncmp(f[i], "break", 5) == 0){ + tcsendbreak(fd, 0); + continue; + } + n = atoi(f[i]+1); + switch(*f[i]) { + case 'F': + case 'f': + if(tcflush(fd, TCOFLUSH) < 0) + goto Error; + break; + case 'K': + case 'k': + if(tcsendbreak(fd, 0) < 0) + ; /* ignore it */ + break; + case 'H': + case 'h': + cfsetospeed(&ts, B0); + break; + case 'B': + case 'b': + flag = stof(bps, n); + if((int)flag == -1) + error(Ebadarg); + cfsetispeed(&ts, (speed_t)flag); + cfsetospeed(&ts, (speed_t)flag); + break; + case 'L': + case 'l': + flag = stof(size, n); + if((int)flag == -1) + error(Ebadarg); + ts.c_cflag &= ~CSIZE; + ts.c_cflag |= flag; + break; + case 'S': + case 's': + if(n == 1) + ts.c_cflag &= ~CSTOPB; + else if(n ==2) + ts.c_cflag |= CSTOPB; + else + error(Ebadarg); + break; + case 'P': + case 'p': + if(*(f[i]+1) == 'o') + ts.c_cflag |= PARENB|PARODD; + else if(*(f[i]+1) == 'e') { + ts.c_cflag |= PARENB; + ts.c_cflag &= ~PARODD; + } + else + ts.c_cflag &= ~PARENB; + break; + case 'X': + case 'x': + if(n == 0) + ts.c_iflag &= ~(IXON|IXOFF); + else + ts.c_iflag |= (IXON|IXOFF); + break; + case 'i': + case 'I': + /* enable fifo; ignore */ + break; + default: + if((xerr = wrxtra(port, &ts, f[i])) != nil) + error(xerr); + } + } + + osenter(); + r = tcsetattr(fd, TCSADRAIN, &ts); + osleave(); + if(r < 0) + goto Error; + eia[port].restore = 1; + eia[port].ts = ts; +} + +static void +eiainit(void) +{ + int i, nports; + Dirtab *dp; + struct stat sb; + +#ifdef buildsysdev + buildsysdev(); +#endif + + /* check to see which ports exist by trying to stat them */ + nports = 0; + for (i=0; i < nelem(sysdev); i++) { + if(stat(sysdev[i], &sb) < 0) + break; + + nports++; + } + + if (!nports) + return; + + ndir = Nqid*nports+1; + dp = eiadir = malloc(ndir*sizeof(Dirtab)); + if(dp == 0) + panic("eiainit"); + strcpy(dp->name, "."); + dp->qid.path = 0; + dp->qid.type = QTDIR; + dp->perm = DMDIR|0555; + dp++; + eia = malloc(nports*sizeof(Eia)); + if(eia == 0) + panic("eiainit"); + for(i = 0; i < nports; i++) { + sprint(dp->name, "%s%d", Devname, i); + dp->qid.path = NETQID(i, Ndataqid); + dp->perm = 0660; + dp++; + sprint(dp->name, "%s%dctl", Devname, i); + dp->qid.path = NETQID(i, Nctlqid); + dp->perm = 0660; + dp++; + sprint(dp->name, "%s%dstatus", Devname, i); + dp->qid.path = NETQID(i, Nstatqid); + dp->perm = 0660; + dp++; + eia[i].frame = eia[i].overrun = 0; + eia[i].restore = eia[i].dtr = eia[i].rts = eia[i].cts = 0; + } +} + +static Chan* +eiaattach(char *spec) +{ + if(eiadir == nil) + error(Enodev); + + return devattach(Devchar, spec); +} + +Walkqid* +eiawalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, eiadir, ndir, devgen); +} + +int +eiastat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, eiadir, ndir, devgen); +} + +static void +resxtra(int port, struct termios *ts); /* non-POSIX extensions */ + +static Chan* +eiaopen(Chan *c, int mode) +{ + int port = NETID(c->qid.path); + struct termios ts; + int r; + + c = devopen(c, mode, eiadir, ndir, devgen); + + switch(NETTYPE(c->qid.path)) { + case Nctlqid: + case Ndataqid: + case Nstatqid: + if(incref(&eia[port].r) != 1) + break; + + osenter(); + eia[port].fd = open(sysdev[port], O_RDWR); + osleave(); + if(eia[port].fd < 0) + oserror(); + + /* make port settings sane */ + if(tcgetattr(eia[port].fd, &ts) < 0) + oserror(); + ts.c_iflag = ts.c_oflag = ts.c_lflag = 0; + if(eia[port].restore) + ts = eia[port].ts; + else { + cfsetispeed(&ts, B9600); + cfsetospeed(&ts, B9600); + ts.c_iflag |= IGNPAR; + ts.c_cflag &= ~CSIZE; + ts.c_cflag |= CS8|CREAD; + ts.c_cflag &= ~(PARENB|PARODD); + ts.c_cc[VMIN] = 1; + ts.c_cc[VTIME] = 0; + } + osenter(); + r = tcsetattr(eia[port].fd, TCSANOW, &ts); + osleave(); + if(r < 0) + oserror(); + + if(eia[port].restore) + resxtra(port, &ts); + break; + } + return c; +} + +static void +eiaclose(Chan *c) +{ + int port = NETID(c->qid.path); + + if((c->flag & COPEN) == 0) + return; + + switch(NETTYPE(c->qid.path)) { + case Nctlqid: + case Ndataqid: + case Nstatqid: + if(decref(&eia[port].r) != 0) + break; + if(eia[port].fd >= 0) { + osenter(); + close(eia[port].fd); + osleave(); + } + break; + } + +} + +static long +eiaread(Chan *c, void *buf, long n, vlong offset) +{ + ssize_t cnt; + int port = NETID(c->qid.path); + + if(c->qid.type & QTDIR) + return devdirread(c, buf, n, eiadir, ndir, devgen); + + switch(NETTYPE(c->qid.path)) { + case Ndataqid: + osenter(); + cnt = read(eia[port].fd, buf, n); + osleave(); + if(cnt == -1) + oserror(); + return cnt; + case Nctlqid: + return readnum(offset, buf, n, port, NUMSIZE); + case Nstatqid: + return rdstat(port, buf, n, offset); + } + + return 0; +} + +static long +eiawrite(Chan *c, void *buf, long n, vlong offset) +{ + ssize_t cnt; + char cmd[Maxctl]; + int port = NETID(c->qid.path); + + USED(offset); + + if(c->qid.type & QTDIR) + error(Eperm); + + switch(NETTYPE(c->qid.path)) { + case Ndataqid: + osenter(); + cnt = write(eia[port].fd, buf, n); + osleave(); + if(cnt == -1) + oserror(); + return cnt; + case Nctlqid: + if(n >= (long)sizeof(cmd)) + n = sizeof(cmd)-1; + memmove(cmd, buf, n); + cmd[n] = 0; + wrctl(port, cmd); + return n; + } + return 0; +} + +int +eiawstat(Chan *c, uchar *dp, int n) +{ + Dir d; + int i; + + if(strcmp(up->env->user, eve) != 0) + error(Eperm); + if(c->qid.type & QTDIR) + error(Eperm); + + n = convM2D(dp, n, &d, nil); + i = Nqid*NETID(c->qid.path)+NETTYPE(c->qid.path)-Ndataqid; + eiadir[i+1].perm = d.mode&0666; + return n; +} + +Dev eiadevtab = { + Devchar, + Devname, + + eiainit, + eiaattach, + eiawalk, + eiastat, + eiaopen, + devcreate, + eiaclose, + eiaread, + devbread, + eiawrite, + devbwrite, + devremove, + eiawstat +}; diff --git a/emu/port/devenv.c b/emu/port/devenv.c new file mode 100644 index 00000000..9f56ce7d --- /dev/null +++ b/emu/port/devenv.c @@ -0,0 +1,253 @@ +/* + * devenv - environment + */ +#include "dat.h" +#include "fns.h" +#include "error.h" + +enum +{ + Maxenvlen= 16*1024-1, +}; + + +static void envremove(Chan*); + +static int +envgen(Chan *c, char *name, Dirtab *d, int nd, int s, Dir *dp) +{ + Egrp *eg; + Evalue *e; + + USED(name); + USED(d); + USED(nd); + if(s == DEVDOTDOT){ + devdir(c, c->qid, "#e", 0, eve, DMDIR|0775, dp); + return 1; + } + eg = up->env->egrp; + qlock(&eg->l); + for(e = eg->entries; e != nil && s != 0; e = e->next) + s--; + if(e == nil) { + qunlock(&eg->l); + return -1; + } + /* make sure name string continues to exist after we release lock */ + kstrcpy(up->genbuf, e->var, sizeof up->genbuf); + devdir(c, e->qid, up->genbuf, e->len, eve, 0666, dp); + qunlock(&eg->l); + return 1; +} + +static Chan* +envattach(char *spec) +{ + return devattach('e', spec); +} + +static Walkqid* +envwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, 0, 0, envgen); +} + +static int +envstat(Chan *c, uchar *db, int n) +{ + if(c->qid.type & QTDIR) + c->qid.vers = up->env->egrp->vers; + return devstat(c, db, n, 0, 0, envgen); +} + +static Chan * +envopen(Chan *c, int mode) +{ + Egrp *eg; + Evalue *e; + + if(c->qid.type & QTDIR) { + if(mode != OREAD) + error(Eperm); + c->mode = mode; + c->flag |= COPEN; + c->offset = 0; + return c; + } + eg = up->env->egrp; + qlock(&eg->l); + for(e = eg->entries; e != nil; e = e->next) + if(e->qid.path == c->qid.path) + break; + if(e == nil) { + qunlock(&eg->l); + error(Enonexist); + } + if(mode == (OWRITE|OTRUNC) && e->val) { + free(e->val); + e->val = 0; + e->len = 0; + e->qid.vers++; + } + qunlock(&eg->l); + c->offset = 0; + c->flag |= COPEN; + c->mode = mode&~OTRUNC; + return c; +} + +static void +envcreate(Chan *c, char *name, int mode, ulong perm) +{ + Egrp *eg; + Evalue *e, *le; + + USED(perm); + if(c->qid.type != QTDIR) + error(Eperm); + if(strlen(name) >= sizeof(up->genbuf)) + error("name too long"); /* needs to fit for stat */ + eg = up->env->egrp; + qlock(&eg->l); + for(le = nil, e = eg->entries; e != nil; le = e, e = e->next) + if(strcmp(e->var, name) == 0) { + qunlock(&eg->l); + error(Eexist); + } + e = smalloc(sizeof(Evalue)); + e->var = smalloc(strlen(name)+1); + strcpy(e->var, name); + e->val = 0; + e->len = 0; + e->qid.path = ++eg->path; + if (le == nil) + eg->entries = e; + else + le->next = e; + e->qid.vers = 0; + c->qid = e->qid; + eg->vers++; + qunlock(&eg->l); + c->offset = 0; + c->flag |= COPEN; + c->mode = mode; +} + +static void +envclose(Chan * c) +{ + if(c->flag & CRCLOSE) + envremove(c); +} + +static long +envread(Chan *c, void *a, long n, vlong offset) +{ + Egrp *eg; + Evalue *e; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, envgen); + eg = up->env->egrp; + qlock(&eg->l); + for(e = eg->entries; e != nil; e = e->next) + if(e->qid.path == c->qid.path) + break; + if(e == nil) { + qunlock(&eg->l); + error(Enonexist); + } + if(offset > e->len) /* protects against overflow converting vlong to ulong */ + n = 0; + else if(offset + n > e->len) + n = e->len - offset; + if(n <= 0) + n = 0; + else + memmove(a, e->val+offset, n); + qunlock(&eg->l); + return n; +} + +static long +envwrite(Chan *c, void *a, long n, vlong offset) +{ + char *s; + ulong ve; + Egrp *eg; + Evalue *e; + + if(n <= 0) + return 0; + ve = offset+n; + if(ve > Maxenvlen) + error(Etoobig); + eg = up->env->egrp; + qlock(&eg->l); + for(e = eg->entries; e != nil; e = e->next) + if(e->qid.path == c->qid.path) + break; + if(e == nil) { + qunlock(&eg->l); + error(Enonexist); + } + if(ve > e->len) { + s = smalloc(ve); + memmove(s, e->val, e->len); + if(e->val) + free(e->val); + e->val = s; + e->len = ve; + } + memmove(e->val+offset, a, n); + e->qid.vers++; + qunlock(&eg->l); + return n; +} + +static void +envremove(Chan *c) +{ + Egrp *eg; + Evalue *e, **l; + + if(c->qid.type & QTDIR) + error(Eperm); + eg = up->env->egrp; + qlock(&eg->l); + for(l = &eg->entries; *l != nil; l = &(*l)->next) + if((*l)->qid.path == c->qid.path) + break; + e = *l; + if(e == nil) { + qunlock(&eg->l); + error(Enonexist); + } + *l = e->next; + eg->vers++; + qunlock(&eg->l); + free(e->var); + if(e->val != nil) + free(e->val); + free(e); +} + +Dev envdevtab = { + 'e', + "env", + + devinit, + envattach, + envwalk, + envstat, + envopen, + envcreate, + envclose, + envread, + devbread, + envwrite, + devbwrite, + envremove, + devwstat +}; diff --git a/emu/port/devfs-posix.c b/emu/port/devfs-posix.c new file mode 100644 index 00000000..bd1d9a94 --- /dev/null +++ b/emu/port/devfs-posix.c @@ -0,0 +1,1052 @@ +/* + * Unix file system interface + */ +#define _LARGEFILE64_SOURCE 1 +#define _FILE_OFFSET_BITS 64 +#include "dat.h" +#include "fns.h" +#include "error.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/fcntl.h> +#include <utime.h> +#include <dirent.h> +#include <stdio.h> +#define __EXTENSIONS__ +#undef getwd +#include <unistd.h> +#include <pwd.h> +#include <grp.h> + +typedef struct Fsinfo Fsinfo; +struct Fsinfo +{ + int uid; + int gid; + int mode; /* Unix mode */ + DIR* dir; /* open directory */ + struct dirent* de; /* directory reading */ + int fd; /* open files */ + ulong offset; /* offset when reading directory */ + int eod; /* end of directory */ + QLock oq; /* mutex for offset */ + char* spec; + Cname* name; /* Unix's name for file */ + Qid rootqid; /* Plan 9's qid for Inferno's root */ +}; + +#define FS(c) ((Fsinfo*)(c)->aux) + +enum +{ + IDSHIFT = 8, + NID = 1 << IDSHIFT, + IDMASK = NID - 1, + MAXPATH = 1024 /* TO DO: eliminate this */ +}; + +typedef struct User User; +struct User +{ + int id; /* might be user or group ID */ + int gid; /* if it's a user not a group, the group ID (only for setid) */ + char* name; + int nmem; + int* mem; /* member array, if nmem != 0 */ + User* next; +}; + +char rootdir[MAXROOT] = ROOT; + +static User* uidmap[NID]; +static User* gidmap[NID]; +static QLock idl; +static User* name2user(User**, char*, User* (*get)(char*)); +static User* id2user(User**, int, User* (*get)(int)); +static User* newuid(int); +static User* newgid(int); +static User* newuname(char*); +static User* newgname(char*); + +static Qid fsqid(struct stat *); +static void fspath(Cname*, char*, char*); +static int fsdirconv(Chan*, char*, struct stat*, uchar*, int, int); +static Cname* fswalkpath(Cname*, char*, int); +static char* fslastelem(Cname*); +static int ingroup(int id, int gid); +static void fsperm(Chan*, int); +static long fsdirread(Chan*, uchar*, int, vlong); +static int fsomode(int); +static void fsremove(Chan*); + +/* + * this crud is to compensate for invalid symbolic links; + * all you can do is delete them, and good riddance + */ +static int +xstat(char *f, struct stat *sb) +{ + if(stat(f, sb) >= 0) + return 0; + /* could possibly generate ->name as rob once suggested */ + return lstat(f, sb); +} + +static void +fsfree(Chan *c) +{ + cnameclose(FS(c)->name); + free(FS(c)); +} + +Chan* +fsattach(char *spec) +{ + Chan *c; + struct stat stbuf; + static int devno; + static Lock l; + + if(!emptystr(spec) && strcmp(spec, "*") != 0) + error(Ebadspec); + if(stat(rootdir, &stbuf) < 0) + oserror(); + + c = devattach('U', spec); + c->qid = fsqid(&stbuf); + c->aux = smalloc(sizeof(Fsinfo)); + FS(c)->dir = nil; + FS(c)->de = nil; + FS(c)->fd = -1; + FS(c)->gid = stbuf.st_gid; + FS(c)->uid = stbuf.st_uid; + FS(c)->mode = stbuf.st_mode; + lock(&l); + c->dev = devno++; + unlock(&l); + if (!emptystr(spec)){ + FS(c)->spec = "/"; + FS(c)->name = newcname(FS(c)->spec); + }else + FS(c)->name = newcname(rootdir); + FS(c)->rootqid = c->qid; + + return c; +} + +Walkqid* +fswalk(Chan *c, Chan *nc, char **name, int nname) +{ + int j; + volatile int alloc; + Walkqid *wq; + struct stat stbuf; + char *n; + Cname *next; + Cname *volatile current; + Qid rootqid; + + if(nname > 0) + isdir(c); + + alloc = 0; + current = nil; + wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid)); + if(waserror()){ + if(alloc && wq->clone != nil) + cclose(wq->clone); + cnameclose(current); + free(wq); + return nil; + } + if(nc == nil){ + nc = devclone(c); + nc->type = 0; + alloc = 1; + } + wq->clone = nc; + rootqid = FS(c)->rootqid; + current = FS(c)->name; + if(current != nil) + incref(¤t->r); + for(j = 0; j < nname; j++){ + if(!(nc->qid.type&QTDIR)){ + if(j==0) + error(Enotdir); + break; + } + n = name[j]; + if(strcmp(n, ".") != 0 && !(isdotdot(n) && nc->qid.path == rootqid.path)){ + next = current; + incref(&next->r); + next = addelem(current, n); + //print("** ufs walk '%s' -> %s [%s]\n", current->s, n, next->s); + if(xstat(next->s, &stbuf) < 0){ + cnameclose(next); + if(j == 0) + error(Enonexist); + strcpy(up->env->errstr, Enonexist); + break; + } + nc->qid = fsqid(&stbuf); + cnameclose(current); + current = next; + } + wq->qid[wq->nqid++] = nc->qid; + } + poperror(); + if(wq->nqid < nname){ + cnameclose(current); + if(alloc) + cclose(wq->clone); + wq->clone = nil; + }else if(wq->clone){ + nc->aux = smalloc(sizeof(Fsinfo)); + nc->type = c->type; + if (nname) { + FS(nc)->gid = stbuf.st_gid; + FS(nc)->uid = stbuf.st_uid; + FS(nc)->mode = stbuf.st_mode; + } else { + FS(nc)->gid = FS(c)->gid; + FS(nc)->uid = FS(c)->uid; + FS(nc)->mode = FS(c)->mode; + } + FS(nc)->name = current; + FS(nc)->spec = FS(c)->spec; + FS(nc)->rootqid = rootqid; + FS(nc)->fd = -1; + FS(nc)->dir = nil; + FS(nc)->de = nil; + } + return wq; +} + +static int +fsstat(Chan *c, uchar *dp, int n) +{ + struct stat stbuf; + char *p; + + if(xstat(FS(c)->name->s, &stbuf) < 0) + oserror(); + p = fslastelem(FS(c)->name); + if(*p == 0) + p = "/"; + qlock(&idl); + n = fsdirconv(c, p, &stbuf, dp, n, 0); + qunlock(&idl); + return n; +} + +static Chan* +fsopen(Chan *c, int mode) +{ + int m, isdir; + + m = mode & (OTRUNC|3); + switch(m) { + case 0: + fsperm(c, 4); + break; + case 1: + case 1|16: + fsperm(c, 2); + break; + case 2: + case 0|16: + case 2|16: + fsperm(c, 4); + fsperm(c, 2); + break; + case 3: + fsperm(c, 1); + break; + default: + error(Ebadarg); + } + + isdir = c->qid.type & QTDIR; + + if(isdir && mode != OREAD) + error(Eperm); + + m = fsomode(m & 3); + c->mode = openmode(mode); + + if(isdir) { + FS(c)->dir = opendir(FS(c)->name->s); + if(FS(c)->dir == 0) + oserror(); + FS(c)->eod = 0; + } + else { + if(mode & OTRUNC) + m |= O_TRUNC; + FS(c)->fd = open(FS(c)->name->s, m, 0666); + if(FS(c)->fd < 0) + oserror(); + } + + c->offset = 0; + FS(c)->offset = 0; + c->flag |= COPEN; + return c; +} + +static void +fscreate(Chan *c, char *name, int mode, ulong perm) +{ + int fd, m, o; + struct stat stbuf; + Cname *n; + + fsperm(c, 2); + + m = fsomode(mode&3); + openmode(mode); /* get the errors out of the way */ + + if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) + error(Efilename); + n = fswalkpath(FS(c)->name, name, 1); + if(waserror()){ + cnameclose(n); + nexterror(); + } + if(perm & DMDIR) { + if(m) + error(Eperm); + + perm &= ~0777 | (FS(c)->mode & 0777); + if(mkdir(n->s, perm) < 0) + oserror(); + + fd = open(n->s, 0); + if(fd < 0) + oserror(); + fchmod(fd, perm); + fchown(fd, up->env->uid, FS(c)->gid); + if(fstat(fd, &stbuf) <0){ + close(fd); + oserror(); + } + close(fd); + FS(c)->dir = opendir(n->s); + if(FS(c)->dir == nil) + oserror(); + FS(c)->eod = 0; + } else { + o = (O_CREAT | O_EXCL) | (mode&3); + if(mode & OTRUNC) + o |= O_TRUNC; + perm &= ~0666 | (FS(c)->mode & 0666); + fd = open(n->s, o, perm); + if(fd < 0) + oserror(); + fchmod(fd, perm); + fchown(fd, up->env->uid, FS(c)->gid); + if(fstat(fd, &stbuf) < 0){ + close(fd); + oserror(); + } + FS(c)->fd = fd; + } + cnameclose(FS(c)->name); + FS(c)->name = n; + poperror(); + + c->qid = fsqid(&stbuf); + FS(c)->gid = stbuf.st_gid; + FS(c)->uid = stbuf.st_uid; + FS(c)->mode = stbuf.st_mode; + c->mode = openmode(mode); + c->offset = 0; + FS(c)->offset = 0; + c->flag |= COPEN; +} + +static void +fsclose(Chan *c) +{ + if((c->flag & COPEN) != 0){ + if(c->qid.type & QTDIR) + closedir(FS(c)->dir); + else + close(FS(c)->fd); + } + if(c->flag & CRCLOSE) { + if(!waserror()) { + fsremove(c); + poperror(); + } + return; + } + fsfree(c); +} + +static long +fsread(Chan *c, void *va, long n, vlong offset) +{ + long r; + + if(c->qid.type & QTDIR){ + qlock(&FS(c)->oq); + if(waserror()) { + qunlock(&FS(c)->oq); + nexterror(); + } + r = fsdirread(c, va, n, offset); + poperror(); + qunlock(&FS(c)->oq); + }else{ + r = pread(FS(c)->fd, va, n, offset); + if(r < 0 && (errno == ESPIPE || errno == EPIPE)){ + r = read(FS(c)->fd, va, n); + if(r < 0) + oserror(); + } + } + return r; +} + +static long +fswrite(Chan *c, void *va, long n, vlong offset) +{ + long r; + + r = pwrite(FS(c)->fd, va, n, offset); + if(r < 0 && (errno == ESPIPE || errno == EPIPE)){ + r = write(FS(c)->fd, va, n); + if(r < 0) + oserror(); + } + return r; +} + +static void +fswchk(Cname *c) +{ + struct stat stbuf; + + if(stat(c->s, &stbuf) < 0) + oserror(); + + if(stbuf.st_uid == up->env->uid) + stbuf.st_mode >>= 6; + else + if(stbuf.st_gid == up->env->gid || ingroup(up->env->uid, stbuf.st_gid)) + stbuf.st_mode >>= 3; + + if(stbuf.st_mode & S_IWOTH) + return; + + error(Eperm); +} + +static void +fsremove(Chan *c) +{ + int n; + volatile struct { Cname *dir; } dir; + + dir.dir = fswalkpath(FS(c)->name, "..", 1); + if(waserror()){ + if(dir.dir != nil) + cnameclose(dir.dir); + fsfree(c); + nexterror(); + } + fswchk(dir.dir); + cnameclose(dir.dir); + dir.dir = nil; + if(c->qid.type & QTDIR) + n = rmdir(FS(c)->name->s); + else + n = remove(FS(c)->name->s); + if(n < 0) + oserror(); + poperror(); + fsfree(c); +} + +static int +fswstat(Chan *c, uchar *buf, int nb) +{ + Dir *d; + User *p; + volatile struct { Cname *ph; } ph; + struct stat stbuf; + struct utimbuf utbuf; + int tsync; + + if(FS(c)->fd >= 0){ + if(fstat(FS(c)->fd, &stbuf) < 0) + oserror(); + }else{ + if(stat(FS(c)->name->s, &stbuf) < 0) + oserror(); + } + d = malloc(sizeof(*d)+nb); + if(d == nil) + error(Enomem); + if(waserror()){ + free(d); + nexterror(); + } + tsync = 1; + nb = convM2D(buf, nb, d, (char*)&d[1]); + if(nb == 0) + error(Eshortstat); + if(!emptystr(d->name) && strcmp(d->name, fslastelem(FS(c)->name)) != 0) { + tsync = 0; + validname(d->name, 0); + ph.ph = fswalkpath(FS(c)->name, "..", 1); + if(waserror()){ + cnameclose(ph.ph); + nexterror(); + } + fswchk(ph.ph); + ph.ph = fswalkpath(ph.ph, d->name, 0); + if(rename(FS(c)->name->s, ph.ph->s) < 0) + oserror(); + cnameclose(FS(c)->name); + poperror(); + FS(c)->name = ph.ph; + } + + if(d->mode != ~0 && (d->mode&0777) != (stbuf.st_mode&0777)) { + tsync = 0; + if(up->env->uid != stbuf.st_uid) + error(Eowner); + if(FS(c)->fd >= 0){ + if(fchmod(FS(c)->fd, d->mode&0777) < 0) + oserror(); + }else{ + if(chmod(FS(c)->name->s, d->mode&0777) < 0) + oserror(); + } + FS(c)->mode &= ~0777; + FS(c)->mode |= d->mode&0777; + } + + if(d->atime != ~0 && d->atime != stbuf.st_atime + || d->mtime != ~0 && d->mtime != stbuf.st_mtime) { + tsync = 0; + if(up->env->uid != stbuf.st_uid) + error(Eowner); + if(d->mtime != ~0) + utbuf.modtime = d->mtime; + else + utbuf.modtime = stbuf.st_mtime; + if(d->atime != ~0) + utbuf.actime = d->atime; + else + utbuf.actime = stbuf.st_atime; + if(utime(FS(c)->name->s, &utbuf) < 0) /* TO DO: futimes isn't portable */ + oserror(); + } + + if(*d->gid){ + tsync = 0; + qlock(&idl); + if(waserror()){ + qunlock(&idl); + nexterror(); + } + p = name2user(gidmap, d->gid, newgname); + if(p == 0) + error(Eunknown); + if(p->id != stbuf.st_gid) { + if(up->env->uid != stbuf.st_uid) + error(Eowner); + if(FS(c)->fd >= 0){ + if(fchown(FS(c)->fd, stbuf.st_uid, p->id) < 0) + oserror(); + }else{ + if(chown(FS(c)->name->s, stbuf.st_uid, p->id) < 0) + oserror(); + } + FS(c)->gid = p->id; + } + poperror(); + qunlock(&idl); + } + + if(d->length != ~(uvlong)0){ + tsync = 0; + if(FS(c)->fd >= 0){ + fsperm(c, 2); + if(ftruncate(FS(c)->fd, d->length) < 0) + oserror(); + }else{ + fswchk(FS(c)->name); + if(truncate(FS(c)->name->s, d->length) < 0) + oserror(); + } + } + + poperror(); + free(d); + if(tsync && FS(c)->fd >= 0 && fsync(FS(c)->fd) < 0) + oserror(); + return nb; +} + +#define QDEVBITS 4 /* 16 devices should be plenty */ +#define MAXDEV (1<<QDEVBITS) +#define QDEVSHIFT (64-QDEVBITS) +#define QINOMASK (((uvlong)1<<QDEVSHIFT)-1) +#define QPATH(d,i) (((uvlong)(d)<<QDEVSHIFT)|((uvlong)(i)&QINOMASK)) + +static Qid +fsqid(struct stat *st) +{ + Qid q; + ulong dev; + int idev; + static int nqdev = 0; + static ulong qdev[MAXDEV]; + static Lock l; + + q.type = QTFILE; + if(S_ISDIR(st->st_mode)) + q.type = QTDIR; + + dev = st->st_dev; + lock(&l); + for(idev = 0; idev < nqdev; idev++) + if(qdev[idev] == dev) + break; + if(idev == nqdev) { + if(nqdev == MAXDEV) { + unlock(&l); + error("too many devices"); + } + qdev[nqdev++] = dev; + } + unlock(&l); + + if(0) /* we'll just let it be masked off */ + if((uvlong)st->st_ino & ~QINOMASK) + error("inode number too large"); + + q.path = QPATH(idev, st->st_ino); + q.vers = st->st_mtime; + + return q; +} + +static void +fspath(Cname *c, char *name, char *path) +{ + int n; + + if(c->len+strlen(name) >= MAXPATH) + panic("fspath: name too long"); + memmove(path, c->s, c->len); + n = c->len; + if(path[n-1] != '/') + path[n++] = '/'; + strcpy(path+n, name); + if(isdotdot(name)) + cleanname(path); +/*print("->%s\n", path);*/ +} + +static Cname * +fswalkpath(Cname *c, char *name, int dup) +{ + if(dup) + c = newcname(c->s); + c = addelem(c, name); + if(isdotdot(name)) + cleancname(c); + return c; +} + +static char * +fslastelem(Cname *c) +{ + char *p; + + p = c->s + c->len; + while(p > c->s && p[-1] != '/') + p--; + return p; +} + +static void +fsperm(Chan *c, int mask) +{ + int m; + + m = FS(c)->mode; +/* + print("fsperm: %o %o uuid %d ugid %d cuid %d cgid %d\n", + m, mask, up->env->uid, up->env->gid, FS(c)->uid, FS(c)->gid); +*/ + if(FS(c)->uid == up->env->uid) + m >>= 6; + else + if(FS(c)->gid == up->env->gid || ingroup(up->env->uid, FS(c)->gid)) + m >>= 3; + + m &= mask; + if(m == 0) + error(Eperm); +} + +static int +isdots(char *name) +{ + return name[0] == '.' && (name[1] == '\0' || name[1] == '.' && name[2] == '\0'); +} + +static int +fsdirconv(Chan *c, char *name, struct stat *s, uchar *va, int nb, int indir) +{ + Dir d; + char uidbuf[NUMSIZE], gidbuf[NUMSIZE]; + User *u; + + memset(&d, 0, sizeof(d)); + d.name = name; + u = id2user(uidmap, s->st_uid, newuid); + if(u == nil){ + snprint(uidbuf, sizeof(uidbuf), "#%lud", (long)s->st_uid); + d.uid = uidbuf; + }else + d.uid = u->name; + u = id2user(gidmap, s->st_gid, newgid); + if(u == nil){ + snprint(gidbuf, sizeof(gidbuf), "#%lud", (long)s->st_gid); + d.gid = gidbuf; + }else + d.gid = u->name; + d.muid = ""; + d.qid = fsqid(s); + d.mode = (d.qid.type<<24)|(s->st_mode&0777); + d.atime = s->st_atime; + d.mtime = s->st_mtime; + d.length = s->st_size; + if(d.mode&DMDIR) + d.length = 0; + d.type = 'U'; + d.dev = c->dev; + if(indir && sizeD2M(&d) > nb) + return -1; /* directory reader needs to know it didn't fit */ + return convD2M(&d, va, nb); +} + +static long +fsdirread(Chan *c, uchar *va, int count, vlong offset) +{ + int i; + long n, r; + struct stat stbuf; + char path[MAXPATH], *ep; + struct dirent *de; + static char slop[8192]; + + i = 0; + fspath(FS(c)->name, "", path); + ep = path+strlen(path); + if(FS(c)->offset != offset) { + seekdir(FS(c)->dir, 0); + FS(c)->de = nil; + FS(c)->eod = 0; + for(n=0; n<offset; ) { + de = readdir(FS(c)->dir); + if(de == 0) { + /* EOF, so stash offset and return 0 */ + FS(c)->offset = n; + FS(c)->eod = 1; + return 0; + } + if(de->d_ino==0 || de->d_name[0]==0 || isdots(de->d_name)) + continue; + strecpy(ep, path+sizeof(path), de->d_name); + if(xstat(path, &stbuf) < 0) { + fprint(2, "dir: bad path %s\n", path); + continue; + } + qlock(&idl); + r = fsdirconv(c, de->d_name, &stbuf, slop, sizeof(slop), 1); + qunlock(&idl); + if(r <= 0) { + FS(c)->offset = n; + return 0; + } + n += r; + } + FS(c)->offset = offset; + } + + if(FS(c)->eod) + return 0; + + /* + * Take idl on behalf of id2name. Stalling attach, which is a + * rare operation, until the readdir completes is probably + * preferable to adding lock round-trips. + */ + qlock(&idl); + while(i < count){ + de = FS(c)->de; + FS(c)->de = nil; + if(de == nil) + de = readdir(FS(c)->dir); + if(de == nil){ + FS(c)->eod = 1; + break; + } + + if(de->d_ino==0 || de->d_name[0]==0 || isdots(de->d_name)) + continue; + + strecpy(ep, path+sizeof(path), de->d_name); + if(xstat(path, &stbuf) < 0) { + fprint(2, "dir: bad path %s\n", path); + continue; + } + r = fsdirconv(c, de->d_name, &stbuf, va+i, count-i, 1); + if(r <= 0){ + FS(c)->de = de; + break; + } + i += r; + FS(c)->offset += r; + } + qunlock(&idl); + return i; +} + +static int +fsomode(int m) +{ + if(m < 0 || m > 3) + error(Ebadarg); + return m == 3? 0: m; +} + +void +setid(char *name, int owner) +{ + User *u; + + if(owner && !iseve()) + return; + kstrdup(&up->env->user, name); + + qlock(&idl); + u = name2user(uidmap, name, newuname); + if(u == nil){ + qunlock(&idl); + up->env->uid = -1; + up->env->gid = -1; + return; + } + + up->env->uid = u->id; + up->env->gid = u->gid; + qunlock(&idl); +} + +static User** +hashuser(User** tab, int id) +{ + int i; + + i = (id>>IDSHIFT) ^ id; + return &tab[i & IDMASK]; +} + +/* + * the caller of the following functions must hold QLock idl. + */ + +/* + * we could keep separate maps of user and group names to Users to + * speed this up, but the reverse lookup currently isn't common (ie, change group by wstat and setid) + */ +static User* +name2user(User **tab, char *name, User* (*get)(char*)) +{ + int i; + User *u, **h; + static User *prevu; + static User **prevtab; + + if(prevu != nil && prevtab == tab && strcmp(name, prevu->name) == 0) + return prevu; /* it's often the one we've just seen */ + + for(i=0; i<NID; i++) + for(u = tab[i]; u != nil; u = u->next) + if(strcmp(name, u->name) == 0) { + prevtab = tab; + prevu = u; + return u; + } + + u = get(name); + if(u == nil) + return nil; + h = hashuser(tab, u->id); + u->next = *h; + *h = u; + prevtab = tab; + prevu = u; + return u; +} + +static void +freeuser(User *u) +{ + if(u != nil){ + free(u->name); + free(u->mem); + free(u); + } +} + +static User* +newuser(int id, int gid, char *name, int nmem) +{ + User *u; + + u = malloc(sizeof(*u)); + if(u == nil) + return nil; + u->name = strdup(name); + if(u->name == nil){ + free(u); + return nil; + } + u->nmem = nmem; + if(nmem){ + u->mem = malloc(nmem*sizeof(*u->mem)); + if(u->mem == nil){ + free(u->name); + free(u); + return nil; + } + }else + u->mem = nil; + u->id = id; + u->gid = gid; + u->next = nil; + return u; +} + +static User* +newuname(char *name) +{ + struct passwd *p; + User *u; + + p = getpwnam(name); + if(p == nil) + return nil; + return newuser(p->pw_uid, p->pw_gid, name, 0); +} + +static User* +newuid(int id) +{ + struct passwd *p; + User *u; + + p = getpwuid(id); + if(p == nil) + return nil; + return newuser(p->pw_uid, p->pw_gid, p->pw_name, 0); +} + +static User* +newgroup(struct group *g) +{ + User *u, *gm; + int n, o; + + if(g == nil) + return nil; + for(n=0; g->gr_mem[n] != nil; n++) + ; + u = newuser(g->gr_gid, g->gr_gid, g->gr_name, n); + if(u == nil) + return nil; + o = 0; + for(n=0; g->gr_mem[n] != nil; n++){ + gm = name2user(uidmap, g->gr_mem[n], newuname); + if(gm != nil) + u->mem[o++] = gm->id; + /* ignore names that don't map to IDs */ + } + u->nmem = o; + return u; +} + +static User* +newgid(int id) +{ + return newgroup(getgrgid(id)); +} + +static User* +newgname(char *name) +{ + return newgroup(getgrnam(name)); +} + +static User* +id2user(User **tab, int id, User* (*get)(int)) +{ + int i; + User *u, **h; + + h = hashuser(tab, id); + for(u = *h; u != nil; u = u->next) + if(u->id == id) + return u; + u = get(id); + if(u == nil) + return nil; + u->next = *h; + *h = u; + return u; +} + +static int +ingroup(int id, int gid) +{ + int i; + User *g; + + g = id2user(gidmap, gid, newgid); + if(g == nil || g->mem == nil) + return 0; + for(i = 0; i < g->nmem; i++) + if(g->mem[i] == id) + return 1; + return 0; +} + +Dev fsdevtab = { + 'U', + "fs", + + devinit, + fsattach, + fswalk, + fsstat, + fsopen, + fscreate, + fsclose, + fsread, + devbread, + fswrite, + devbwrite, + fsremove, + fswstat +}; diff --git a/emu/port/devindir.c b/emu/port/devindir.c new file mode 100644 index 00000000..9c214927 --- /dev/null +++ b/emu/port/devindir.c @@ -0,0 +1,35 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + +static Chan * +indirattach(char *spec) +{ + char *p; + Dev *d; + + if(*spec == 0) + error(Ebadspec); + p = strrchr(spec, '!'); + if(p == nil) + p = ""; + else + *p++ = 0; + d = devbyname(spec); + if(d == nil || d->dc == '*'){ + snprint(up->env->errstr, ERRMAX, "unknown device: %s", spec); + error(up->env->errstr); + } + if(up->env->pgrp->nodevs && + (utfrune("|esDa", d->dc) == nil || d->dc == 's' && *p!='\0')) + error(Enoattach); + return d->attach(p); +} + +Dev indirdevtab = { + '*', + "indir", + + devinit, + indirattach, +}; diff --git a/emu/port/devip.c b/emu/port/devip.c new file mode 100644 index 00000000..e70d7dc3 --- /dev/null +++ b/emu/port/devip.c @@ -0,0 +1,1143 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "ip.h" + +enum +{ + Qtopdir = 1, /* top level directory */ + Qtopbase, + Qarp= Qtopbase, +/* Qiproute, */ +/* Qipselftab, */ + Qndb, + + Qprotodir, /* directory for a protocol */ + Qprotobase, + Qclone= Qprotobase, + Qstats, + + Qconvdir, /* directory for a conversation */ + Qconvbase, + Qctl= Qconvbase, + Qdata, + Qlisten, + Qlocal, + Qremote, + Qstatus, + + Logtype= 5, + Masktype= (1<<Logtype)-1, + Logconv= 12, + Maskconv= (1<<Logconv)-1, + Shiftconv= Logtype, + Logproto= 8, + Maskproto= (1<<Logproto)-1, + Shiftproto= Logtype + Logconv, + + Statelen = 256, + + Nfs= 1, + + Maxproto = 4, + MAXCONV = 4096 +}; +#define TYPE(x) ( ((ulong)(x).path) & Masktype ) +#define CONV(x) ( (((ulong)(x).path) >> Shiftconv) & Maskconv ) +#define PROTO(x) ( (((ulong)(x).path) >> Shiftproto) & Maskproto ) +#define QID(p, c, y) ( ((p)<<(Shiftproto)) | ((c)<<Shiftconv) | (y) ) + +enum +{ + Idle= 0, + Announcing= 1, + Announced= 2, + Connecting= 3, + Connected= 4, + Hungup= 5, +}; + +struct Conv +{ + QLock l; + + int x; /* conversation index */ + Proto* p; + + uchar laddr[IPaddrlen]; /* local IP address */ + uchar raddr[IPaddrlen]; /* remote IP address */ + int restricted; /* remote port is restricted */ + ushort lport; /* local port number */ + ushort rport; /* remote port number */ + + char* owner; /* protections */ + int perm; + int inuse; /* opens of listen/data/ctl */ + int state; + + /* udp specific */ + int headers; /* data src/dst headers in udp */ + + char cerr[ERRMAX]; + + QLock listenq; + + void* ptcl; /* protocol specific stuff */ + + int sfd; + QLock wlock; /* prevent data from being split by concurrent writes */ +}; + +struct Proto +{ + QLock l; + int x; + int ipproto; + int stype; + char* name; + int maxconv; + Fs* f; /* file system this proto is part of */ + Conv** conv; /* array of conversations */ + int pctlsize; /* size of per protocol ctl block */ + int nc; /* number of conversations */ + int ac; + Qid qid; /* qid for protocol directory */ + /* port allocation isn't done here when hosted */ + + void* priv; +}; + +/* + * one per IP protocol stack + */ +struct Fs +{ + RWlock l; + int dev; + + int np; + Proto* p[Maxproto+1]; /* list of supported protocols */ + Proto* t2p[256]; /* vector of all protocols */ + + char ndb[1024]; /* an ndb entry for this interface */ + int ndbvers; + long ndbmtime; +}; + +static Fs *ipfs[Nfs]; /* attached fs's */ +static char network[] = "network"; +static char* ipstates[] = { + "Closed", /* Idle */ + "Announcing", + "Announced", + "Connecting", + "Established", /* Connected */ + "Closed", /* Hungup */ +}; + +static Conv* protoclone(Proto*, char*, int); +static Conv* newconv(Proto*, Conv **); +static void setladdr(Conv*); +static ulong ip6w(uchar*); +static void ipw6(uchar*, ulong); + +static int +ip3gen(Chan *c, int i, Dir *dp) +{ + Qid q; + Conv *cv; + char *p; + + cv = ipfs[c->dev]->p[PROTO(c->qid)]->conv[CONV(c->qid)]; + if(cv->owner == nil) + kstrdup(&cv->owner, eve); + mkqid(&q, QID(PROTO(c->qid), CONV(c->qid), i), 0, QTFILE); + + switch(i) { + default: + return -1; + case Qctl: + devdir(c, q, "ctl", 0, cv->owner, cv->perm, dp); + return 1; + case Qdata: + devdir(c, q, "data", 0, cv->owner, cv->perm, dp); + return 1; + case Qlisten: + devdir(c, q, "listen", 0, cv->owner, cv->perm, dp); + return 1; + case Qlocal: + p = "local"; + break; + case Qremote: + p = "remote"; + break; + case Qstatus: + p = "status"; + break; + } + devdir(c, q, p, 0, cv->owner, 0444, dp); + return 1; +} + +static int +ip2gen(Chan *c, int i, Dir *dp) +{ + Qid q; + + switch(i) { + case Qclone: + mkqid(&q, QID(PROTO(c->qid), 0, Qclone), 0, QTFILE); + devdir(c, q, "clone", 0, network, 0666, dp); + return 1; + case Qstats: + mkqid(&q, QID(PROTO(c->qid), 0, Qstats), 0, QTFILE); + devdir(c, q, "stats", 0, network, 0444, dp); + return 1; + } + return -1; +} + +static int +ip1gen(Chan *c, int i, Dir *dp) +{ + Qid q; + char *p; + int prot; + int len = 0; + Fs *f; + extern ulong kerndate; + + f = ipfs[c->dev]; + + prot = 0664; + mkqid(&q, QID(0, 0, i), 0, QTFILE); + switch(i) { + default: + return -1; + case Qarp: + p = "arp"; + break; + case Qndb: + p = "ndb"; + len = strlen(ipfs[c->dev]->ndb); + break; +/* case Qiproute: + p = "iproute"; + break; + case Qipselftab: + p = "ipselftab"; + prot = 0444; + break; + case Qiprouter: + p = "iprouter"; + break; + case Qlog: + p = "log"; + break; +*/ + } + devdir(c, q, p, len, network, prot, dp); + if(i == Qndb && f->ndbmtime > kerndate) + dp->mtime = f->ndbmtime; + return 1; +} + +static int +ipgen(Chan *c, char *name, Dirtab *tab, int x, int s, Dir *dp) +{ + Qid q; + Conv *cv; + Fs *f; + + USED(name); + USED(tab); + USED(x); + f = ipfs[c->dev]; + + switch(TYPE(c->qid)) { + case Qtopdir: + if(s == DEVDOTDOT){ + mkqid(&q, QID(0, 0, Qtopdir), 0, QTDIR); + sprint(up->genbuf, "#I%lud", c->dev); + devdir(c, q, up->genbuf, 0, network, 0555, dp); + return 1; + } + if(s < f->np) { +/* if(f->p[s]->connect == nil) + return 0; /* protocol with no user interface */ + mkqid(&q, QID(s, 0, Qprotodir), 0, QTDIR); + devdir(c, q, f->p[s]->name, 0, network, 0555, dp); + return 1; + } + s -= f->np; + return ip1gen(c, s+Qtopbase, dp); + case Qarp: + case Qndb: +/* case Qiproute: + case Qiprouter: + case Qipselftab: */ + return ip1gen(c, TYPE(c->qid), dp); + case Qprotodir: + if(s == DEVDOTDOT){ + mkqid(&q, QID(0, 0, Qtopdir), 0, QTDIR); + sprint(up->genbuf, "#I%lud", c->dev); + devdir(c, q, up->genbuf, 0, network, 0555, dp); + return 1; + } + if(s < f->p[PROTO(c->qid)]->ac) { + cv = f->p[PROTO(c->qid)]->conv[s]; + sprint(up->genbuf, "%d", s); + mkqid(&q, QID(PROTO(c->qid), s, Qconvdir), 0, QTDIR); + devdir(c, q, up->genbuf, 0, cv->owner, 0555, dp); + return 1; + } + s -= f->p[PROTO(c->qid)]->ac; + return ip2gen(c, s+Qprotobase, dp); + case Qclone: + case Qstats: + return ip2gen(c, TYPE(c->qid), dp); + case Qconvdir: + if(s == DEVDOTDOT){ + s = PROTO(c->qid); + mkqid(&q, QID(s, 0, Qprotodir), 0, QTDIR); + devdir(c, q, f->p[s]->name, 0, network, 0555, dp); + return 1; + } + return ip3gen(c, s+Qconvbase, dp); + case Qctl: + case Qdata: + case Qlisten: + case Qlocal: + case Qremote: + case Qstatus: + return ip3gen(c, TYPE(c->qid), dp); + } + return -1; +} + +static void +newproto(char *name, int type, int maxconv) +{ + Proto *p; + + p = smalloc(sizeof(*p)); + p->name = name; + p->stype = type; + p->ipproto = type+1; /* temporary */ + p->nc = maxconv; + if(Fsproto(ipfs[0], p)) + panic("can't newproto %s", name); +} + +void +ipinit(void) +{ + ipfs[0] = malloc(sizeof(Fs)); + if(ipfs[0] == nil) + panic("no memory for IP stack"); + + newproto("udp", S_UDP, 64); + newproto("tcp", S_TCP, 2048); + + fmtinstall('i', eipfmt); + fmtinstall('I', eipfmt); + fmtinstall('E', eipfmt); + fmtinstall('V', eipfmt); + fmtinstall('M', eipfmt); +} + +Chan * +ipattach(char *spec) +{ + Chan *c; + + if(atoi(spec) != 0) + error("bad specification"); + + c = devattach('I', spec); + mkqid(&c->qid, QID(0, 0, Qtopdir), 0, QTDIR); + c->dev = 0; + + return c; +} + +static Walkqid* +ipwalk(Chan* c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, nil, 0, ipgen); +} + +static int +ipstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, 0, 0, ipgen); +} + +static int m2p[] = { + 4, + 2, + 6, +}; + +static Chan * +ipopen(Chan *c, int omode) +{ + Conv *cv, *nc; + Proto *p; + ulong raddr; + ushort rport; + int perm, sfd; + Fs *f; + + perm = m2p[omode&3]; + + f = ipfs[c->dev]; + + switch(TYPE(c->qid)) { + default: + break; + case Qtopdir: + case Qprotodir: + case Qconvdir: + case Qstatus: + case Qremote: + case Qlocal: + case Qstats: + /* case Qipselftab: */ + if(omode != OREAD) + error(Eperm); + break; + case Qndb: + if(omode & (OWRITE|OTRUNC) && !iseve()) + error(Eperm); + if((omode & (OWRITE|OTRUNC)) == (OWRITE|OTRUNC)){ + f->ndb[0] = 0; + f->ndbvers++; + } + break; + case Qclone: + p = f->p[PROTO(c->qid)]; + cv = protoclone(p, up->env->user, -1); + if(cv == 0) + error(Enodev); + mkqid(&c->qid, QID(p->x, cv->x, Qctl), 0, QTFILE); + break; + case Qdata: + case Qctl: + p = f->p[PROTO(c->qid)]; + qlock(&p->l); + cv = p->conv[CONV(c->qid)]; + qlock(&cv->l); + if(waserror()){ + qunlock(&cv->l); + qunlock(&p->l); + nexterror(); + } + if((perm & (cv->perm>>6)) != perm) { + if(strcmp(up->env->user, cv->owner) != 0) + error(Eperm); + if((perm & cv->perm) != perm) + error(Eperm); + } + cv->inuse++; + if(cv->inuse == 1) { + kstrdup(&cv->owner, up->env->user); + cv->perm = 0660; + if(cv->sfd < 0) + cv->sfd = so_socket(p->stype); + } + poperror(); + qunlock(&cv->l); + qunlock(&p->l); + break; + case Qlisten: + p = f->p[PROTO(c->qid)]; + cv = p->conv[CONV(c->qid)]; + if((perm & (cv->perm>>6)) != perm){ + if(strcmp(up->env->user, cv->owner) != 0) + error(Eperm); + if((perm & cv->perm) != perm) + error(Eperm); + } + + if(cv->state != Announced) + error("not announced"); + + qlock(&cv->listenq); + if(waserror()){ + qunlock(&cv->listenq); + nexterror(); + } + + sfd = so_accept(cv->sfd, &raddr, &rport); + + nc = protoclone(p, up->env->user, sfd); + if(nc == 0) { + so_close(sfd); + error(Enodev); + } + ipw6(nc->raddr, raddr); + nc->rport = rport; + setladdr(nc); + nc->state = Connected; + mkqid(&c->qid, QID(PROTO(c->qid), nc->x, Qctl), 0, QTFILE); + + poperror(); + qunlock(&cv->listenq); + break; + } + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static void +closeconv(Conv *cv) +{ + int fd; + + qlock(&cv->l); + + if(--cv->inuse > 0) { + qunlock(&cv->l); + return; + } + + if(waserror()){ + qunlock(&cv->l); + return; + } + kstrdup(&cv->owner, network); + cv->perm = 0660; + /* cv->p->close(cv); */ + cv->state = Idle; + cv->restricted = 0; + fd = cv->sfd; + cv->sfd = -1; + if(fd >= 0) + so_close(fd); + poperror(); + qunlock(&cv->l); +} + +static void +ipclose(Chan *c) +{ + Fs *f; + + f = ipfs[c->dev]; + switch(TYPE(c->qid)) { + case Qdata: + case Qctl: + if(c->flag & COPEN) + closeconv(f->p[PROTO(c->qid)]->conv[CONV(c->qid)]); + break; + } +} + +static long +ipread(Chan *ch, void *a, long n, vlong off) +{ + int r; + Conv *c; + Proto *x; + char *p, *s; + Fs *f; + ulong offset = off; + + f = ipfs[ch->dev]; + + p = a; + switch(TYPE(ch->qid)) { + default: + error(Eperm); + case Qprotodir: + case Qtopdir: + case Qconvdir: + return devdirread(ch, a, n, 0, 0, ipgen); + case Qarp: + error(Eperm); /* TO DO */ + case Qndb: + return readstr(off, a, n, f->ndb); + case Qctl: + sprint(up->genbuf, "%lud", CONV(ch->qid)); + return readstr(offset, p, n, up->genbuf); + case Qremote: + x = f->p[PROTO(ch->qid)]; + c = x->conv[CONV(ch->qid)]; + sprint(up->genbuf, "%I!%d\n", c->raddr, c->rport); + return readstr(offset, p, n, up->genbuf); + case Qlocal: + x = f->p[PROTO(ch->qid)]; + c = x->conv[CONV(ch->qid)]; + sprint(up->genbuf, "%I!%d\n", c->laddr, c->lport); + return readstr(offset, p, n, up->genbuf); + case Qstatus: + x = f->p[PROTO(ch->qid)]; + c = x->conv[CONV(ch->qid)]; + s = smalloc(Statelen); + if(waserror()){ + free(s); + nexterror(); + } + snprint(s, Statelen, "%s\n", ipstates[c->state]); + n = readstr(offset, p, n, s); + poperror(); + free(s); + return n; + case Qdata: + x = f->p[PROTO(ch->qid)]; + c = x->conv[CONV(ch->qid)]; + if(c->sfd < 0) + error(Ehungup); + if(c->headers) { + if(n < c->headers) + error(Ebadarg); + p = a; + r = so_recv(c->sfd, p + c->headers, n - c->headers, p, c->headers); + if(r > 0) + r += c->headers; + } else + r = so_recv(c->sfd, a, n, nil, 0); + if(r < 0) + oserror(); + return r; + case Qstats: + error("stats not implemented"); + return n; + } +} + +static void +setladdr(Conv *c) +{ + ulong laddr; + + /* TO DO: this can't be right for hosts with several addresses before connect/accept */ + so_getsockname(c->sfd, &laddr, &c->lport); + ipw6(c->laddr, laddr); +} + +/* + * pick a local port and set it + */ +static void +setlport(Conv *c) +{ + ulong laddr; + ushort p; + + so_bind(c->sfd, c->restricted, ip6w(c->laddr), c->lport); + if(c->lport == 0 || ipcmp(c->laddr, IPnoaddr) == 0){ + so_getsockname(c->sfd, &laddr, &p); + if(c->lport == 0) + c->lport = p; + if(ipcmp(c->laddr, IPnoaddr) == 0) + ipw6(c->laddr, laddr); + } +} + +static int +portno(char *p) +{ + long n; + char *e; + + n = strtoul(p, &e, 0); + if(p == e) + error("non-numeric port number"); + return n; +} + +/* + * set a local address and port from a string of the form + * [address!]port[!r] + */ +static void +setladdrport(Conv *c, char *str, int announcing) +{ + char *p; + int lport; + + /* + * ignore restricted part if it exists. it's + * meaningless on local ports. + */ + p = strchr(str, '!'); + if(p != nil){ + *p++ = 0; + if(strcmp(p, "r") == 0) + p = nil; + } + + c->lport = 0; + if(p == nil){ + if(announcing) + ipmove(c->laddr, IPnoaddr); + else if(0) + setladdr(c); + p = str; + } else { + if(strcmp(str, "*") == 0) + ipmove(c->laddr, IPnoaddr); + else if(parseip(c->laddr, str) == 0) + error("invalid IP address"); + } + + if(announcing && strcmp(p, "*") == 0){ + if(!iseve()) + error(Eperm); + c->lport = 0; + setlport(c); + return; + } + + lport = portno(p); + if(lport <= 0) + c->lport = 0; + else + c->lport = lport; + + setlport(c); +} + +static char* +setraddrport(Conv *c, char *str) +{ + char *p; + + p = strchr(str, '!'); + if(p == nil) + return "malformed address"; + *p++ = 0; + if(parseip(c->raddr, str) == 0) + return "invalid IP address"; + c->rport = portno(p); + p = strchr(p, '!'); + if(p){ + if(strstr(p, "!r") != nil) + c->restricted = 1; + } + return nil; +} + +static void +connectctlmsg(Proto *x, Conv *c, Cmdbuf *cb) +{ + char *p; + + USED(x); + if(c->state != Idle) + error(Econinuse); + c->state = Connecting; + c->cerr[0] = '\0'; + switch(cb->nf) { + default: + error("bad args to connect"); + case 2: + p = setraddrport(c, cb->f[1]); + if(p != nil) + error(p); + break; + case 3: + p = setraddrport(c, cb->f[1]); + if(p != nil) + error(p); + c->lport = portno(cb->f[2]); + setlport(c); + break; + } + qunlock(&c->l); + if(waserror()){ + qlock(&c->l); + c->state = Connected; /* sic */ + nexterror(); + } + /* p = x->connect(c, cb->f, cb->nf); */ + so_connect(c->sfd, ip6w(c->raddr), c->rport); + qlock(&c->l); + poperror(); + setladdr(c); + c->state = Connected; +} + +static void +announcectlmsg(Proto *x, Conv *c, Cmdbuf *cb) +{ + if(c->state != Idle) + error(Econinuse); + c->state = Announcing; + c->cerr[0] = '\0'; + ipmove(c->raddr, IPnoaddr); + c->rport = 0; + switch(cb->nf){ + default: + error("bad args to announce"); + case 2: + setladdrport(c, cb->f[1], 1); + break; + } + USED(x); + /* p = x->announce(c, cb->f, cb->nf); */ + if(c->p->stype != S_UDP){ + qunlock(&c->l); + if(waserror()){ + c->state = Announced; /* sic */ + qlock(&c->l); + nexterror(); + } + so_listen(c->sfd); + qlock(&c->l); + poperror(); + } + c->state = Announced; +} + +static void +bindctlmsg(Proto *x, Conv *c, Cmdbuf *cb) +{ + USED(x); + switch(cb->nf){ + default: + error("bad args to bind"); + case 2: + setladdrport(c, cb->f[1], 0); + break; + } +} + +static long +ipwrite(Chan *ch, void *a, long n, vlong off) +{ + Conv *c; + Proto *x; + char *p; + Cmdbuf *cb; + Fs *f; + + f = ipfs[ch->dev]; + + switch(TYPE(ch->qid)) { + default: + error(Eperm); + case Qdata: + x = f->p[PROTO(ch->qid)]; + c = x->conv[CONV(ch->qid)]; + if(c->sfd < 0) + error(Ehungup); + qlock(&c->wlock); + if(waserror()){ + qunlock(&c->wlock); + nexterror(); + } + if(c->headers) { + if(n < c->headers) + error(Eshort); + p = a; + n = so_send(c->sfd, p + c->headers, n - c->headers, p, c->headers); + if(n >= 0) + n += c->headers; + } else + n = so_send(c->sfd, a, n, nil, 0); + poperror(); + qunlock(&c->wlock); + if(n < 0) + oserror(); + break; + case Qarp: + return arpwrite(a, n); + case Qndb: + if(off > strlen(f->ndb)) + error(Eio); + if(off+n >= sizeof(f->ndb)-1) + error(Eio); + memmove(f->ndb+off, a, n); + f->ndb[off+n] = 0; + f->ndbvers++; + f->ndbmtime = seconds(); + break; + case Qctl: + x = f->p[PROTO(ch->qid)]; + c = x->conv[CONV(ch->qid)]; + cb = parsecmd(a, n); + qlock(&c->l); + if(waserror()){ + qunlock(&c->l); + free(cb); + nexterror(); + } + if(cb->nf < 1) + error("short control request"); + if(strcmp(cb->f[0], "connect") == 0) + connectctlmsg(x, c, cb); + else if(strcmp(cb->f[0], "announce") == 0) + announcectlmsg(x, c, cb); + else if(strcmp(cb->f[0], "bind") == 0) + bindctlmsg(x, c, cb); + else if(strcmp(cb->f[0], "ttl") == 0){ + /* ignored */ + } else if(strcmp(cb->f[0], "tos") == 0){ + /* ignored */ + } else if(strcmp(cb->f[0], "ignoreadvice") == 0){ + /* ignored */ + } else if(strcmp(cb->f[0], "headers4") == 0){ + if(c->p->stype != S_UDP) + error(Enoctl); + c->headers = OUdphdrlenv4; + } else if(strcmp(cb->f[0], "oldheaders") == 0){ + if(c->p->stype != S_UDP) + error(Enoctl); + c->headers = OUdphdrlen; + } else if(strcmp(cb->f[0], "headers") == 0){ + if(c->p->stype != S_UDP) + error(Enoctl); + c->headers = Udphdrlen; + } else if(strcmp(cb->f[0], "hangup") == 0){ + if(c->p->stype != S_TCP) + error(Enoctl); + qunlock(&c->l); + if(waserror()){ + qlock(&c->l); + nexterror(); + } + /* TO DO: check fd status if socket close/hangup interrupted */ + if(c->sfd >= 0 && so_hangup(c->sfd, 1) < 0) + oserror(); + qlock(&c->l); + poperror(); + c->sfd = -1; + c->state = Hungup; + } else if(strcmp(cb->f[0], "keepalive") == 0){ + if(c->p->stype != S_TCP) + error(Enoctl); + if(c->sfd < 0) + error("not connected"); + so_keepalive(c->sfd, cb->nf>1? atoi(cb->f[1]): 0); + } else + error(Enoctl); + poperror(); + qunlock(&c->l); + free(cb); + break; + } + return n; +} + +static int +ipwstat(Chan *c, uchar *dp, int n) +{ + Dir *d; + Conv *cv; + Proto *p; + Fs *f; + + f = ipfs[c->dev]; + switch(TYPE(c->qid)) { + default: + error(Eperm); + break; + case Qctl: + case Qdata: + break; + } + + d = smalloc(sizeof(*d)+n); + if(waserror()){ + free(d); + nexterror(); + } + n = convM2D(dp, n, d, (char*)&d[1]); + if(n == 0) + error(Eshortstat); + p = f->p[PROTO(c->qid)]; + cv = p->conv[CONV(c->qid)]; + if(!iseve() && strcmp(up->env->user, cv->owner) != 0) + error(Eperm); + if(!emptystr(d->uid)) + kstrdup(&cv->owner, d->uid); + if(d->mode != ~0UL) + cv->perm = d->mode & 0777; + poperror(); + free(d); + return n; +} + +static Conv* +protoclone(Proto *p, char *user, int nfd) +{ + Conv *c, **pp, **ep, **np; + int maxconv; + + c = 0; + qlock(&p->l); + if(waserror()) { + qunlock(&p->l); + nexterror(); + } + ep = &p->conv[p->nc]; + for(pp = p->conv; pp < ep; pp++) { + c = *pp; + if(c == 0) { + c = newconv(p, pp); + break; + } + if(canqlock(&c->l)){ + if(c->inuse == 0) + break; + qunlock(&c->l); + } + } + if(pp >= ep) { + if(p->nc >= MAXCONV) { + qunlock(&p->l); + poperror(); + return 0; + } + maxconv = 2 * p->nc; + if(maxconv > MAXCONV) + maxconv = MAXCONV; + np = realloc(p->conv, sizeof(Conv*) * maxconv); + if(np == nil) + error(Enomem); + p->conv = np; + pp = &p->conv[p->nc]; + memset(pp, 0, sizeof(Conv*)*(maxconv - p->nc)); + p->nc = maxconv; + c = newconv(p, pp); + } + + c->inuse = 1; + kstrdup(&c->owner, user); + c->perm = 0660; + c->state = Idle; + ipmove(c->laddr, IPnoaddr); + ipmove(c->raddr, IPnoaddr); + c->lport = 0; + c->rport = 0; + c->restricted = 0; + c->sfd = nfd; + if(nfd == -1) + c->sfd = so_socket(p->stype); + + qunlock(&c->l); + qunlock(&p->l); + poperror(); + return c; +} + +static Conv* +newconv(Proto *p, Conv **pp) +{ + Conv *c; + + *pp = c = malloc(sizeof(Conv)); + if(c == 0) + error(Enomem); + qlock(&c->l); + c->inuse = 1; + c->p = p; + c->x = pp - p->conv; + p->ac++; + return c; +} + +int +arpwrite(char *s, int len) +{ + int n; + char *f[4], buf[256]; + + if(len >= sizeof(buf)) + len = sizeof(buf)-1; + memmove(buf, s, len); + buf[len] = 0; + if(len > 0 && buf[len-1] == '\n') + buf[len-1] = 0; + + n = getfields(buf, f, 4, 1, " "); + if(strcmp(f[0], "add") == 0) { + if(n == 3) { + arpadd(f[1], f[2], n); + return len; + } + } + error("bad arp request"); + + return len; +} + +Dev ipdevtab = { + 'I', + "ip", + + ipinit, + ipattach, + ipwalk, + ipstat, + ipopen, + devcreate, + ipclose, + ipread, + devbread, + ipwrite, + devbwrite, + devremove, + ipwstat +}; + +int +Fsproto(Fs *f, Proto *p) +{ + if(f->np >= Maxproto) + return -1; + + p->f = f; + + if(p->ipproto > 0){ + if(f->t2p[p->ipproto] != nil) + return -1; + f->t2p[p->ipproto] = p; + } + + p->qid.type = QTDIR; + p->qid.path = QID(f->np, 0, Qprotodir); + p->conv = malloc(sizeof(Conv*)*(p->nc+1)); + if(p->conv == nil) + panic("Fsproto"); + + p->x = f->np; + f->p[f->np++] = p; + + return 0; +} + +/* + * return true if this protocol is + * built in + */ +int +Fsbuiltinproto(Fs* f, uchar proto) +{ + return f->t2p[proto] != nil; +} + +/* + * temporarily convert ipv6 addresses to ipv4 as ulong for + * ipif.c interface + */ +static ulong +ip6w(uchar *a) +{ + uchar v4[IPv4addrlen]; + + v6tov4(v4, a); + return (((((v4[0]<<8)|v4[1])<<8)|v4[2])<<8)|v4[3]; +} + +static void +ipw6(uchar *a, ulong w) +{ + memmove(a, v4prefix, IPv4off); + hnputl(a+IPv4off, w); +} diff --git a/emu/port/devlogfs.c b/emu/port/devlogfs.c new file mode 100755 index 00000000..2d867496 --- /dev/null +++ b/emu/port/devlogfs.c @@ -0,0 +1,1522 @@ +#ifndef EMU +#include "u.h" +#include "../port/lib.h" +#include "../port/error.h" +#else +#include "error.h" +#endif +#include <dat.h> +#include <fns.h> +#include <kernel.h> +#include <logfs.h> +#include <nandfs.h> + +#ifndef EMU +#define Sleep sleep +#define Wakeup wakeup +#endif + +#ifndef offsetof +#define offsetof(T,X) ((ulong)&(((T*)0)->X)) +#endif + +typedef struct Devlogfs Devlogfs; +typedef struct DevlogfsSession DevlogfsSession; + +//#define CALLTRACE + +enum { + DEVLOGFSDEBUG = 0, + DEVLOGFSIODEBUG = 0, + DEVLOGFSBAD = 1, +}; + +enum { + Qdir, + Qctl, + Qusers, + Qdump, + Qfs, + Qfsboot, + Qend, +}; + +typedef enum DevlogfsServerState { Closed, BootOpen, NeedVersion, NeedAttach, Attached, Hungup } DevlogfsServerState; + +struct Devlogfs { + QLock qlock; + Ref ref; + int instance; + int trace; /* (debugging) trace of read/write actions */ + int nand; + char *name; + char *device; + char *filename[Qend - Qfs]; + LogfsLowLevel *ll; + Chan *flash, *flashctl; + QLock bootqlock; + int logfstrace; + LogfsBoot *lb; + /* stuff for server */ + ulong openflags; + Fcall in; + Fcall out; + int reading; + DevlogfsServerState state; + Rendez readrendez; + Rendez writerendez; + uint readcount; + ulong readbufsize; + uchar *readbuf; + uchar *readp; + LogfsServer *server; + Devlogfs *next; +}; + +#define MAXMSIZE 8192 + +static struct { + RWlock rwlock; /* rlock when walking, wlock when changing */ + QLock configqlock; /* serialises addition of new configurations */ + Devlogfs *head; + char *defname; +} devlogfslist; + +static LogfsIdentityStore *is; + +#ifndef EMU +char Eunknown[] = "unknown user or group id"; +#endif + +static void devlogfsfree(Devlogfs*); + +#define SPLITPATH(path, qtype, instance, qid, qt) { instance = path >> 4; qid = path & 0xf; qt = qtype & QTDIR; } +#define DATAQID(q, qt) (!(qt) && (q) >= Qfs && (q) < Qend) +#define MKPATH(instance, qid) ((instance << 4) | qid) + +#define PREFIX "logfs" + +static char *devlogfsprefix = PREFIX; +static char *devlogfsctlname = PREFIX "ctl"; +static char *devlogfsusersname = PREFIX "users"; +static char *devlogfsdumpname = PREFIX "dump"; +static char *devlogfsbootsuffix = "boot"; +static char *devlogfs9pversion = "9P2000"; + +enum { + Toshiba = 0x98, + Samsung = 0xec, +}; + +static struct { + uchar manufacturer; + uchar device; +} nandtab[] = { + { 0, 0xe6 }, + { 0, 0xea }, + { 0, 0xe3 }, + { 0, 0xe5 }, + { 0, 0x73 }, + { 0, 0x75 }, + { 0, 0x76 }, +}; + +static void +errorany(char *errmsg) +{ + if (errmsg) + error(errmsg); +} + +static void * +emalloc(ulong size) +{ + void *p; + p = logfsrealloc(nil, size); + if (p == nil) + error(Enomem); + return p; +} + +static char * +estrdup(char *q) +{ + void *p; + if (q == nil) + return nil; + p = logfsrealloc(nil, strlen(q) + 1); + if (p == nil) + error(Enomem); + return strcpy(p, q); +} + +static char * +estrconcat(char *a, ...) +{ + va_list l; + char *p, *r; + int t; + + t = strlen(a); + va_start(l, a); + while ((p = va_arg(l, char *)) != nil) + t += strlen(p); + + r = logfsrealloc(nil, t + 1); + if (r == nil) + error(Enomem); + + strcpy(r, a); + va_start(l, a); + while ((p = va_arg(l, char *)) != nil) + strcat(r, p); + + va_end(l); + + return r; +} + +static int +gen(Chan *c, int i, Dir *dp, int lockit) +{ + Devlogfs *l; + long size; + Qid qid; + qid.vers = 0; + qid.type = 0; + + if (i + Qctl < Qfs) { + switch (i + Qctl) { + case Qctl: + qid.path = Qctl; + devdir(c, qid, devlogfsctlname, 0, eve, 0666, dp); + return 1; + case Qusers: + qid.path = Qusers; + devdir(c, qid, devlogfsusersname, 0, eve, 0444, dp); + return 1; + case Qdump: + qid.path = Qdump; + devdir(c, qid, devlogfsdumpname, 0, eve, 0444, dp); + return 1; + } + } + + i -= Qfs - Qctl; + + if (lockit) + rlock(&devlogfslist.rwlock); + + if (waserror()) { + if (lockit) + runlock(&devlogfslist.rwlock); + nexterror(); + } + + for (l = devlogfslist.head; l; l = l->next) { + if (i < Qend - Qfs) + break; + i -= Qend - Qfs; + } + + if (l == nil) { + poperror(); + if (lockit) + runlock(&devlogfslist.rwlock); + return -1; + } + + switch (Qfs + i) { + case Qfsboot: + size = l->lb ? logfsbootgetsize(l->lb) : 0; + break; + default: + size = 0; + break; + } + /* perhaps the user id should come from the underlying file */ + qid.path = MKPATH(l->instance, Qfs + i); + devdir(c, qid, l->filename[i], size, eve, 0666, dp); + + poperror(); + if (lockit) + runlock(&devlogfslist.rwlock); + + return 1; +} + +static int +devlogfsgen(Chan *c, char *n, Dirtab *tab, int ntab, int i, Dir *dp) +{ + USED(n); + USED(tab); + USED(ntab); + return gen(c, i, dp, 1); +} + +static int +devlogfsgennolock(Chan *c, char *n, Dirtab *tab, int ntab, int i, Dir *dp) +{ + USED(n); + USED(tab); + USED(ntab); + return gen(c, i, dp, 0); +} + +/* called under lock */ +static Devlogfs * +devlogfsfind(int instance) +{ + Devlogfs *l; + + for (l = devlogfslist.head; l; l = l->next) + if (l->instance == instance) + break; + return l; +} + +static Devlogfs * +devlogfsget(int instance) +{ + Devlogfs *l; + rlock(&devlogfslist.rwlock); + for (l = devlogfslist.head; l; l = l->next) + if (l->instance == instance) + break; + if (l) + incref(&l->ref); + runlock(&devlogfslist.rwlock); + return l; +} + +static Devlogfs * +devlogfsfindbyname(char *name) +{ + Devlogfs *l; + + rlock(&devlogfslist.rwlock); + for (l = devlogfslist.head; l; l = l->next) + if (strcmp(l->name, name) == 0) + break; + runlock(&devlogfslist.rwlock); + return l; +} + +static Devlogfs * +devlogfssetdefname(char *name) +{ + Devlogfs *l; + char *searchname; + wlock(&devlogfslist.rwlock); + if (waserror()) { + wunlock(&devlogfslist.rwlock); + nexterror(); + } + if (name == nil) + searchname = devlogfslist.defname; + else + searchname = name; + for (l = devlogfslist.head; l; l = l->next) + if (strcmp(l->name, searchname) == 0) + break; + if (l == nil) { + logfsfreemem(devlogfslist.defname); + devlogfslist.defname = nil; + } + else if (name) { + if (devlogfslist.defname) { + logfsfreemem(devlogfslist.defname); + devlogfslist.defname = nil; + } + devlogfslist.defname = estrdup(name); + } + poperror(); + wunlock(&devlogfslist.rwlock); + return l; +} + +static Chan * +devlogfskopen(char *name, char *suffix, int mode) +{ + Chan *c; + char *fn; + int fd; + + fn = estrconcat(name, suffix, 0); + fd = kopen(fn, mode); + logfsfreemem(fn); + if (fd < 0) + error(up->env->errstr); + c = fdtochan(up->env->fgrp, fd, mode, 0, 1); + kclose(fd); + return c; +} + +static char * +xread(void *a, void *buf, long nbytes, ulong offset) +{ + Devlogfs *l = a; + long rv; + + if (DEVLOGFSIODEBUG || l->trace) + print("devlogfs: %s: read(0x%lux, %ld)\n", l->device, offset, nbytes); + l->flash->offset = offset; + rv = kchanio(l->flash, buf, nbytes, OREAD); + if (rv < 0) { + print("devlogfs: %s: flash read error: %s\n", l->device, up->env->errstr); + return up->env->errstr; + } + if (rv != nbytes) { + print("devlogfs: %s: short flash read: offset %lud, %ld not %ld\n", l->device, offset, rv, nbytes); + return "short read"; + } + return nil; +} + +static char * +xwrite(void *a, void *buf, long nbytes, ulong offset) +{ + Devlogfs *l = a; + long rv; + + if (DEVLOGFSIODEBUG || l->trace) + print("devlogfs: %s: write(0x%lux, %ld)\n", l->device, offset, nbytes); + l->flash->offset = offset; + rv = kchanio(l->flash, buf, nbytes, OWRITE); + if (rv < 0) { + print("devlogfs: %s: flash write error: %s\n", l->device, up->env->errstr); + return up->env->errstr; + } + if (rv != nbytes) { + print("devlogfs: %s: short flash write: offset %lud, %ld not %ld\n", l->device, offset, rv, nbytes); + return "short write"; + } + return nil; +} + +static char * +xerase(void *a, long address) +{ + Devlogfs *l = a; + char cmd[40]; + + if (DEVLOGFSIODEBUG || l->trace) + print("devlogfs: %s: erase(0x%lux)\n", l->device, address); + snprint(cmd, sizeof(cmd), "erase 0x%8.8lux", address); + if (kchanio(l->flashctl, cmd, strlen(cmd), OWRITE) <= 0) { + print("devlogfs: %s: flash erase error: %s\n", l->device, up->env->errstr); + return up->env->errstr; + } + return nil; +} + +static char * +xsync(void *a) +{ + Devlogfs *l = a; + uchar statbuf[STATFIXLEN]; + + if (DEVLOGFSIODEBUG || l->trace) + print("devlogfs: %s: sync()\n", l->device); + memset(statbuf, 0xff, sizeof(statbuf)); + memset(statbuf + STATFIXLEN - 8, 0x00, 8); + PBIT16(statbuf, sizeof(statbuf) - BIT16SZ); + if (kwstat(l->device, statbuf, sizeof(statbuf)) < 0) + return up->env->errstr; + return nil; +} + +//#define LEAKHUNT +#ifdef LEAKHUNT +#define MAXLIVE 2000 +typedef struct Live { + void *p; + int freed; + ulong callerpc; +} Live; + +static Live livemem[MAXLIVE]; + +static void +leakalloc(void *p, ulong callerpc) +{ + int x; + int use = -1; + for (x = 0; x < MAXLIVE; x++) { + if (livemem[x].p == p) { + if (!livemem[x].freed) + print("leakalloc: unexpected realloc of 0x%.8lux from 0x%.8lux\n", p, callerpc); +// else +// print("leakalloc: reusing address 0x%.8lux from 0x%.8lux\n", p, callerpc); + livemem[x].freed = 0; + livemem[x].callerpc = callerpc; + return; + } + else if (use < 0 && livemem[x].p == 0) + use = x; + } + if (use < 0) + panic("leakalloc: too many live entries"); + livemem[use].p = p; + livemem[use].freed = 0; + livemem[use].callerpc = callerpc; +} + +static void +leakaudit(void) +{ + int x; + for (x = 0; x < MAXLIVE; x++) { + if (livemem[x].p && !livemem[x].freed) + print("leakaudit: 0x%.8lux from 0x%.8lux\n", livemem[x].p, livemem[x].callerpc); + } +} + +static void +leakfree(void *p, ulong callerpc) +{ + int x; + if (p == nil) + return; + for (x = 0; x < MAXLIVE; x++) { + if (livemem[x].p == p) { + if (livemem[x].freed) + print("leakfree: double free of 0x%.8lux from 0x%.8lux, originally by 0x%.8lux\n", + p, callerpc, livemem[x].callerpc); + livemem[x].freed = 1; + livemem[x].callerpc = callerpc; + return; + } + } + print("leakfree: free of unalloced address 0x%.8lux from 0x%.8lux\n", p, callerpc); + leakaudit(); +} + +static void +leakrealloc(void *newp, void *oldp, ulong callerpc) +{ + leakfree(oldp, callerpc); + leakalloc(newp, callerpc); +} +#endif + + +#ifdef LEAKHUNT +static void *_realloc(void *p, ulong size, ulong callerpc) +#else +void * +logfsrealloc(void *p, ulong size) +#endif +{ + void *q; + ulong osize; + if (waserror()) { + print("wobbly thrown in memory allocator: %s\n", up->env->errstr); + nexterror(); + } + if (p == nil) { + q = smalloc(size); + poperror(); +#ifdef LEAKHUNT + leakrealloc(q, nil, callerpc); +#endif + return q; + } + q = realloc(p, size); + if (q) { + poperror(); +#ifdef LEAKHUNT + leakrealloc(q, p, callerpc); +#endif + return q; + } + q = smalloc(size); + osize = msize(p); + if (osize > size) + osize = size; + memmove(q, p, osize); + free(p); + poperror(); +#ifdef LEAKHUNT + leakrealloc(q, p, callerpc); +#endif + return q; +} + +#ifdef LEAKHUNT +void * +logfsrealloc(void *p, ulong size) +{ + return _realloc(p, size, getcallerpc(&p)); +} + +void * +nandfsrealloc(void *p, ulong size) +{ + return _realloc(p, size, getcallerpc(&p)); +} +#else +void * +nandfsrealloc(void *p, ulong size) +{ + return logfsrealloc(p, size); +} +#endif + +void +logfsfreemem(void *p) +{ +#ifdef LEAKHUNT + leakfree(p, getcallerpc(&p)); +#endif + free(p); +} + +void +nandfsfreemem(void *p) +{ +#ifdef LEAKHUNT + leakfree(p, getcallerpc(&p)); +#endif + free(p); +} + +static Devlogfs * +devlogfsconfig(char *name, char *device) +{ + Devlogfs *newl, *l; + int i; + int n; + char buf[100], *fields[8]; + long rawblocksize, rawsize; + + newl = nil; + + qlock(&devlogfslist.configqlock); + + if (waserror()) { + qunlock(&devlogfslist.configqlock); + devlogfsfree(newl); + nexterror(); + } + + rlock(&devlogfslist.rwlock); + for (l = devlogfslist.head; l; l = l->next) + if (strcmp(l->name, name) == 0) { + runlock(&devlogfslist.rwlock); + error(Einuse); + } + + /* horrid n^2 solution to finding a unique instance number */ + + for (i = 0;; i++) { + for (l = devlogfslist.head; l; l = l->next) + if (l->instance == i) + break; + if (l == nil) + break; + } + runlock(&devlogfslist.rwlock); + + newl = emalloc(sizeof(Devlogfs)); + newl->instance = i; + newl->name = estrdup(name); + newl->device = estrdup(device); + newl->filename[Qfs - Qfs] = estrconcat(devlogfsprefix, name, nil); + newl->filename[Qfsboot - Qfs] = estrconcat(devlogfsprefix, name, devlogfsbootsuffix, nil); + newl->flash = devlogfskopen(device, nil, ORDWR); + newl->flashctl = devlogfskopen(device, "ctl", ORDWR); + newl->flashctl->offset = 0; + if ((n = kchanio(newl->flashctl, buf, sizeof(buf), OREAD)) <= 0) { + print("devlogfsconfig: read ctl failed: %s\n", up->env->errstr); + error(up->env->errstr); + } + + if (n >= sizeof(buf)) + n = sizeof(buf) - 1; + buf[n] = 0; + n = getfields(buf, fields, nelem(fields), 1, " \t\n"); + newl->nand = 0; + if (n >= 2) { + /* detect NAND devices, and learn parameters from there */ + ulong manufacturer = strtoul(fields[0], nil, 16); + ulong device = strtoul(fields[1], nil, 16); + int d; + + for (d = 0; d < sizeof(nandtab) / sizeof(nandtab[0]); d++) { + if ((nandtab[d].manufacturer == manufacturer + && nandtab[d].device == device) + || (nandtab[d].manufacturer == 0 + && (manufacturer == Toshiba || manufacturer == Samsung) + && nandtab[d].device == device)) + { + if (DEVLOGFSDEBUG) + print("devlogfsconfig: nand device detected\n"); + newl->nand = 1; + break; + } + } + } + if (n < 4) + error("unknown erase size"); + rawblocksize = strtol(fields[5], nil, 0); + rawsize = strtol(fields[4], nil, 0)-strtol(fields[3], nil, 0); + if (newl->nand == 0) + error("only NAND supported at the moment"); + errorany(nandfsinit(newl, rawsize, rawblocksize, xread, xwrite, xerase, xsync, &newl->ll)); + wlock(&devlogfslist.rwlock); + newl->next = devlogfslist.head; + devlogfslist.head = newl; + logfsfreemem(devlogfslist.defname); + devlogfslist.defname = nil; + if (waserror()) { + } + else { + devlogfslist.defname = estrdup(name); + poperror(); + } + wunlock(&devlogfslist.rwlock); + poperror(); + qunlock(&devlogfslist.configqlock); + return newl; +} + +void +devlogfsunconfig(Devlogfs *devlogfs) +{ + Devlogfs **lp; + + qlock(&devlogfslist.configqlock); + + if (waserror()) { + qunlock(&devlogfslist.configqlock); + nexterror(); + } + + wlock(&devlogfslist.rwlock); + + if (waserror()) { + wunlock(&devlogfslist.rwlock); + nexterror(); + } + + for (lp = &devlogfslist.head; *lp && (*lp) != devlogfs; lp = &(*lp)->next) + ; + if (*lp == nil) { + if (DEVLOGFSBAD) + print("devlogfsunconfig: not in list\n"); + } + else + *lp = devlogfs->next; + + poperror(); + wunlock(&devlogfslist.rwlock); + + /* now invisible to the naked eye */ + devlogfsfree(devlogfs); + poperror(); + qunlock(&devlogfslist.configqlock); +} + +static void +devlogfsllopen(Devlogfs *l) +{ + qlock(&l->qlock); + if (waserror()) { + qunlock(&l->qlock); + nexterror(); + } + if (l->lb == nil) + errorany(logfsbootopen(l->ll, 0, 0, l->logfstrace, 1, &l->lb)); + l->state = BootOpen; + poperror(); + qunlock(&l->qlock); +} + +static void +devlogfsllformat(Devlogfs *l, long bootsize) +{ + qlock(&l->qlock); + if (waserror()) { + qunlock(&l->qlock); + nexterror(); + } + if (l->lb == nil) + errorany(logfsformat(l->ll, 0, 0, bootsize, l->logfstrace)); + poperror(); + qunlock(&l->qlock); +} + +static Chan * +devlogfsattach(char *spec) +{ + Chan *c; +#ifdef CALLTRACE + print("devlogfsattach(spec = %s) - start\n", spec); +#endif + /* create the identity store on first attach */ + if (is == nil) + errorany(logfsisnew(&is)); + c = devattach(0x29f, spec); +// c = devattach(L'ʟ', spec); +#ifdef CALLTRACE + print("devlogfsattach(spec = %s) - return %.8lux\n", spec, (ulong)c); +#endif + return c; +} + +static Walkqid* +devlogfswalk(Chan *c, Chan *nc, char **name, int nname) +{ + int instance, qid, qt, clone; + Walkqid *wq; + +#ifdef CALLTRACE + print("devlogfswalk(c = 0x%.8lux, nc = 0x%.8lux, name = 0x%.8lux, nname = %d) - start\n", + (ulong)c, (ulong)nc, (ulong)name, nname); +#endif + clone = 0; + if(nc == nil){ + nc = devclone(c); + nc->type = 0; + SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt); + if(DATAQID(qid, qt)) + nc->aux = devlogfsget(instance); + clone = 1; + } + wq = devwalk(c, nc, name, nname, 0, 0, devlogfsgen); + if (wq == nil || wq->nqid < nname) { + if(clone) + cclose(nc); + } + else if (clone) { + wq->clone = nc; + nc->type = c->type; + } +#ifdef CALLTRACE + print("devlogfswalk(c = 0x%.8lux, nc = 0x%.8lux, name = 0x%.8lux, nname = %d) - return\n", + (ulong)c, (ulong)nc, (ulong)name, nname); +#endif + return wq; +} + +static int +devlogfsstat(Chan *c, uchar *dp, int n) +{ +#ifdef CALLTRACE + print("devlogfsstat(c = 0x%.8lux, dp = 0x%.8lux n= %d)\n", + (ulong)c, (ulong)dp, n); +#endif + return devstat(c, dp, n, 0, 0, devlogfsgen); +} + +static Chan* +devlogfsopen(Chan *c, int omode) +{ + int instance, qid, qt; + + omode = openmode(omode); + SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt); +#ifdef CALLTRACE + print("devlogfsopen(c = 0x%.8lux, omode = %o, instance = %d, qid = %d, qt = %d)\n", + (ulong)c, omode, instance, qid, qt); +#endif + + + rlock(&devlogfslist.rwlock); + if (waserror()) { + runlock(&devlogfslist.rwlock); +#ifdef CALLTRACE + print("devlogfsopen(c = 0x%.8lux, omode = %o) - error %s\n", (ulong)c, omode, up->env->errstr); +#endif + nexterror(); + } + + if (DATAQID(qid, qt)) { + Devlogfs *d; + d = devlogfsfind(instance); + if (d == nil) + error(Enodev); + if (strcmp(up->env->user, eve) != 0) + error(Eperm); + if (qid == Qfs && d->state != BootOpen) + error(Eperm); + if (d->server == nil) { + errorany(logfsservernew(d->lb, d->ll, is, d->openflags, d->logfstrace, &d->server)); + d->state = NeedVersion; + } + c = devopen(c, omode, 0, 0, devlogfsgennolock); + incref(&d->ref); + c->aux = d; + } + else if (qid == Qctl || qid == Qusers) { + if (strcmp(up->env->user, eve) != 0) + error(Eperm); + c = devopen(c, omode, 0, 0, devlogfsgennolock); + } + else + c = devopen(c, omode, 0, 0, devlogfsgennolock); + poperror(); + runlock(&devlogfslist.rwlock); +#ifdef CALLTRACE + print("devlogfsopen(c = 0x%.8lux, omode = %o) - return\n", (ulong)c, omode); +#endif + return c; +} + +static void +devlogfsclose(Chan *c) +{ + int instance, qid, qt; +#ifdef CALLTRACE + print("devlogfsclose(c = 0x%.8lux)\n", (ulong)c); +#endif + SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt); + USED(instance); + if(DATAQID(qid, qt) && (c->flag & COPEN) != 0) { + Devlogfs *d; + d = c->aux; + qlock(&d->qlock); + if (qid == Qfs && d->state == Attached) { + logfsserverflush(d->server); + logfsserverfree(&d->server); + d->state = BootOpen; + } + qunlock(&d->qlock); + decref(&d->ref); + } +#ifdef CALLTRACE + print("devlogfsclose(c = 0x%.8lux) - return\n", (ulong)c); +#endif +} + +typedef char *(SMARTIOFN)(void *magic, void *buf, long n, ulong offset, int write); + +void +smartio(SMARTIOFN *io, void *magic, void *buf, long n, ulong offset, long blocksize, int write) +{ + void *tmp = nil; + ulong blocks, toread; + + if (waserror()) { + logfsfreemem(tmp); + nexterror(); + } + if (offset % blocksize) { + ulong aoffset; + int tmpoffset; + int tocopy; + + if (tmp == nil) + tmp = emalloc(blocksize); + aoffset = offset / blocksize; + aoffset *= blocksize; + errorany((*io)(magic, tmp, blocksize, aoffset, 0)); + tmpoffset = offset - aoffset; + tocopy = blocksize - tmpoffset; + if (tocopy > n) + tocopy = n; + if (write) { + memmove((uchar *)tmp + tmpoffset, buf, tocopy); + errorany((*io)(magic, tmp, blocksize, aoffset, 1)); + } + else + memmove(buf, (uchar *)tmp + tmpoffset, tocopy); + buf = (uchar *)buf + tocopy; + n -= tocopy; + offset = aoffset + blocksize; + } + blocks = n / blocksize; + toread = blocks * blocksize; + errorany((*io)(magic, buf, toread, offset, write)); + buf = (uchar *)buf + toread; + n -= toread; + offset += toread; + if (n) { + if (tmp == nil) + tmp = emalloc(blocksize); + errorany((*io)(magic, tmp, blocksize, offset, 0)); + if (write) { + memmove(tmp, buf, n); + errorany((*io)(magic, tmp, blocksize, offset, 1)); + } + memmove(buf, tmp, n); + } + poperror(); + logfsfreemem(tmp); +} + +static int +readok(void *a) +{ + Devlogfs *d = a; + return d->reading; +} + +static int +writeok(void *a) +{ + Devlogfs *d = a; + return !d->reading; +} + +long +devlogfsserverread(Devlogfs *d, void *buf, long n) +{ + if (d->state == Hungup) + error(Ehungup); + Sleep(&d->readrendez, readok, d); + if (n > d->readcount) + n = d->readcount; + memmove(buf, d->readp, n); + d->readp += n; + d->readcount -= n; + if (d->readcount == 0) { + d->reading = 0; + Wakeup(&d->writerendez); + } + return n; +} + +static void +reply(Devlogfs *d) +{ + d->readp = d->readbuf; + d->readcount = convS2M(&d->out, d->readp, d->readbufsize); +//print("reply is %d bytes\n", d->readcount); + if (d->readcount == 0) + panic("logfs: reply: did not fit\n"); + d->reading = 1; + Wakeup(&d->readrendez); +} + +static void +rerror(Devlogfs *d, char *ename) +{ + d->out.type = Rerror; + d->out.ename = ename; + reply(d); +} + +static struct { + QLock qlock; + int (*read)(void *magic, Devlogfs *d, int line, char *buf, int buflen); + void *magic; + Devlogfs *d; + int line; +} dump; + +static void * +extentdumpinit(Devlogfs *d, int argc, char **argv) +{ + int *p; + ulong path; + ulong flashaddr, length; + long block; + int page, offset; + if (argc != 1) + error(Ebadarg); + path = strtoul(argv[0], 0, 0); + errorany(logfsserverreadpathextent(d->server, path, 0, &flashaddr, &length, &block, &page, &offset)); + p = emalloc(sizeof(ulong)); + *p = path; + return p; +} + +static int +extentdumpread(void *magic, Devlogfs *d, int line, char *buf, int buflen) +{ + ulong *p = magic; + ulong flashaddr, length; + long block; + int page, offset; + USED(d); + errorany(logfsserverreadpathextent(d->server, *p, line, &flashaddr, &length, &block, &page, &offset)); + if (length == 0) + return 0; + return snprint(buf, buflen, "%.8ux %ud %ld %d %d\n", flashaddr, length, block, page, offset); +} + +void +devlogfsdumpinit(Devlogfs *d, + void *(*init)(Devlogfs *d, int argc, char **argv), + int (*read)(void *magic, Devlogfs *d, int line, char *buf, int buflen), int argc, char **argv) +{ + qlock(&dump.qlock); + if (waserror()) { + qunlock(&dump.qlock); + nexterror(); + } + if (d) { + if (d->state < NeedVersion) + error("not mounted"); + qlock(&d->qlock); + if (waserror()) { + qunlock(&d->qlock); + nexterror(); + } + } + if (dump.magic) { + logfsfreemem(dump.magic); + dump.magic = nil; + } + dump.d = d; + dump.magic = (*init)(d, argc, argv); + dump.read = read; + dump.line = 0; + if (d) { + poperror(); + qunlock(&d->qlock); + } + poperror(); + qunlock(&dump.qlock); +} + +long +devlogfsdumpread(char *buf, int buflen) +{ + char *tmp = nil; + long n; + qlock(&dump.qlock); + if (waserror()) { + logfsfreemem(tmp); + qunlock(&dump.qlock); + nexterror(); + } + if (dump.magic == nil) + error(Eio); + tmp = emalloc(READSTR); + if (dump.d) { + if (dump.d->state < NeedVersion) + error("not mounted"); + qlock(&dump.d->qlock); + if (waserror()) { + qunlock(&dump.d->qlock); + nexterror(); + } + } + n = (*dump.read)(dump.magic, dump.d, dump.line, tmp, READSTR); + if (n) { + dump.line++; + n = readstr(0, buf, buflen, tmp); + } + if (dump.d) { + poperror(); + qunlock(&dump.d->qlock); + } + logfsfreemem(tmp); + poperror(); + qunlock(&dump.qlock); + return n; +} + +void +devlogfsserverlogsweep(Devlogfs *d, int justone) +{ + int didsomething; + if (d->state < NeedVersion) + error("not mounted"); + qlock(&d->qlock); + if (waserror()) { + qunlock(&d->qlock); + nexterror(); + } + errorany(logfsserverlogsweep(d->server, justone, &didsomething)); + poperror(); + qunlock(&d->qlock); +} + +void +devlogfsserverwrite(Devlogfs *d, void *buf, long n) +{ + int locked = 0; + if (d->state == Hungup) + error(Ehungup); + Sleep(&d->writerendez, writeok, d); + if (convM2S(buf, n, &d->in) != n) { + /* + * someone is writing drivel; have nothing to do with them anymore + * most common cause; trying to mount authenticated + */ + d->state = Hungup; + error(Ehungup); + } + d->out.tag = d->in.tag; + d->out.fid = d->in.fid; + d->out.type = d->in.type + 1; + if (waserror()) { + if (locked) + qunlock(&d->qlock); + rerror(d, up->env->errstr); + return; + } + if (d->in.type != Tversion && d->in.type != Tattach) { + if (d->state != Attached) + error("must be attached"); + qlock(&d->qlock); + locked = 1; + } + switch (d->in.type) { + case Tauth: + error("no authentication needed"); + case Tversion: { + char *rversion; + if (d->state != NeedVersion) + error("unexpected Tversion"); + if (d->in.tag != NOTAG) + error("protocol botch"); + /* + * check the version string + */ + if (strcmp(d->in.version, devlogfs9pversion) != 0) + rversion = "unknown"; + else + rversion = devlogfs9pversion; + /* + * allocate the reply buffer + */ + d->readbufsize = d->in.msize; + if (d->readbufsize > MAXMSIZE) + d->readbufsize = MAXMSIZE; + d->readbuf = emalloc(d->readbufsize); + /* + * compose the Rversion + */ + d->out.msize = d->readbufsize; + d->out.version = rversion; + d->state = NeedAttach; + break; + } + case Tattach: + if (d->state != NeedAttach) + error("unexpected attach"); + if (d->in.afid != NOFID) + error("unexpected afid"); + errorany(logfsserverattach(d->server, d->in.fid, d->in.uname, &d->out.qid)); + d->state = Attached; + break; + case Tclunk: + errorany(logfsserverclunk(d->server, d->in.fid)); + break; + case Tcreate: + errorany(logfsservercreate(d->server, d->in.fid, d->in.name, d->in.perm, d->in.mode, &d->out.qid)); + d->out.iounit = d->readbufsize - 11; + break; + case Tflush: + break; + case Topen: + errorany(logfsserveropen(d->server, d->in.fid, d->in.mode, &d->out.qid)); + d->out.iounit = d->readbufsize - 11; + break; + case Tread: + d->out.data = (char *)d->readbuf + 11; + /* TODO - avoid memmove */ + errorany(logfsserverread(d->server, d->in.fid, d->in.offset, d->in.count, (uchar *)d->out.data, + d->readbufsize - 11, &d->out.count)); + break; + case Tremove: + errorany(logfsserverremove(d->server, d->in.fid)); + break; + case Tstat: + d->out.stat = d->readbuf + 9; + /* TODO - avoid memmove */ + errorany(logfsserverstat(d->server, d->in.fid, d->out.stat, d->readbufsize - 9, &d->out.nstat)); +// print("nstat %d\n", d->out.nstat); + break; + case Twalk: + errorany(logfsserverwalk(d->server, d->in.fid, d->in.newfid, + d->in.nwname, d->in.wname, &d->out.nwqid, d->out.wqid)); + break; + case Twrite: + errorany(logfsserverwrite(d->server, d->in.fid, d->in.offset, d->in.count, (uchar *)d->in.data, + &d->out.count)); + break; + case Twstat: + errorany(logfsserverwstat(d->server, d->in.fid, d->in.stat, d->in.nstat)); + break; + default: + print("devlogfsserverwrite: msg %d unimplemented\n", d->in.type); + error("unimplemented"); + } + poperror(); + if (locked) + qunlock(&d->qlock); + reply(d); +} + +static long +devlogfsread(Chan *c, void *buf, long n, vlong off) +{ + int instance, qid, qt; + + SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt); + USED(instance); +#ifdef CALLTRACE + print("devlogfsread(c = 0x%.8lux, buf = 0x%.8lux, n = %ld, instance = %d, qid = %d, qt = %d) - start\n", + (ulong)c, (ulong)buf, n, instance, qid, qt); +#endif + if(qt & QTDIR) { +#ifdef CALLTRACE + print("devlogfsread(c = 0x%.8lux, buf = 0x%.8lux, n = %ld, instance = %d, qid = %d, qt = %d) - calling devdirread\n", + (ulong)c, (ulong)buf, n, instance, qid, qt); +#endif + return devdirread(c, buf, n, 0, 0, devlogfsgen); + } + + if(DATAQID(qid, qt)) { + if (qid == Qfsboot) { + Devlogfs *l = c->aux; + qlock(&l->bootqlock); + if (waserror()) { + qunlock(&l->bootqlock); + nexterror(); + } + smartio((SMARTIOFN *)logfsbootio, l->lb, buf, n, off, logfsbootgetiosize(l->lb), 0); + poperror(); + qunlock(&l->bootqlock); + return n; + } + else if (qid == Qfs) { + Devlogfs *d = c->aux; + return devlogfsserverread(d, buf, n); + } + error(Eio); + } + + if (qid == Qusers) { + long nr; + errorany(logfsisusersread(is, buf, n, (ulong)off, &nr)); + return nr; + } + else if (qid == Qdump) + return devlogfsdumpread(buf, n); + + if (qid != Qctl) + error(Egreg); + + return 0; +} + +static long +devlogfswrite(Chan *c, void *buf, long n, vlong off) +{ + char cmd[64], *realfields[6]; + int i; + int instance, qid, qt; + + if(n <= 0) + return 0; + SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt); +#ifdef CALLTRACE + print("devlogfswrite(c = 0x%.8lux, buf = 0x%.8lux, n = %ld, instance = %d, qid = %d, qt = %d) - start\n", + (ulong)c, (ulong)buf, n, instance, qid, qt); +#endif + USED(instance); + if(DATAQID(qid, qt)){ + if (qid == Qfsboot) { + Devlogfs *l = c->aux; + qlock(&l->bootqlock); + if (waserror()) { + qunlock(&l->bootqlock); + nexterror(); + } + smartio((SMARTIOFN *)logfsbootio, l->lb, buf, n, off, logfsbootgetiosize(l->lb), 1); + poperror(); + qunlock(&l->bootqlock); + return n; + } + else if (qid == Qfs) { + Devlogfs *d = c->aux; + devlogfsserverwrite(d, buf, n); + return n; + } + error(Eio); + } + else if (qid == Qctl) { + Devlogfs *l = nil; + char **fields; + + if(n > sizeof(cmd)-1) + n = sizeof(cmd)-1; + memmove(cmd, buf, n); + cmd[n] = 0; + i = getfields(cmd, realfields, 6, 1, " \t\n"); +//print("i = %d\n", i); + if (i <= 0) + error(Ebadarg); + fields = realfields; + if (i == 3 && strcmp(fields[0], "uname") == 0) { + switch (fields[2][0]) { + default: + errorany(logfsisgroupcreate(is, fields[1], fields[2])); + break; + case ':': + errorany(logfsisgroupcreate(is, fields[1], fields[2] + 1)); + break; + case '%': + errorany(logfsisgrouprename(is, fields[1], fields[2] + 1)); + break; + case '=': + errorany(logfsisgroupsetleader(is, fields[1], fields[2] + 1)); + break; + case '+': + errorany(logfsisgroupaddmember(is, fields[1], fields[2] + 1)); + break; + case '-': + errorany(logfsisgroupremovemember(is, fields[1], fields[2] + 1)); + break; + } + i = 0; + } + if (i == 4 && strcmp(fields[0], "fsys") == 0 && strcmp(fields[2], "config") == 0) { + l = devlogfsconfig(fields[1], fields[3]); + i = 0; + } + else if (i >= 2 && strcmp(fields[0], "fsys") == 0) { + l = devlogfssetdefname(fields[1]); + if (l == nil) + error(Ebadarg); + i -= 2; + fields += 2; + } + if (i != 0) { + if (l == nil) + l = devlogfssetdefname(nil); + if (i >= 1 && strcmp(fields[0], "open") == 0) { + int a; + if (l == nil) + error(Ebadarg); + for (a = 1; a < i; a++) + if (fields[a][0] == '-') + switch (fields[a][1]) { + case 'P': + l->openflags |= LogfsOpenFlagNoPerm; + break; + case 'W': + l->openflags |= LogfsOpenFlagWstatAllow; + break; + default: + error(Ebadarg); + } + devlogfsllopen(l); + i = 0; + } + else if (i == 2 && strcmp(fields[0], "format") == 0) { + if (l == nil) + error(Ebadarg); + devlogfsllformat(l, strtol(fields[1], nil, 0)); + i = 0; + } + else if (i >= 1 && strcmp(fields[0], "sweep") == 0) { + if (l == nil) + error(Ebadarg); + devlogfsserverlogsweep(l, 0); + i = 0; + } + else if (i >= 1 && strcmp(fields[0], "sweepone") == 0) { + if (l == nil) + error(Ebadarg); + devlogfsserverlogsweep(l, 1); + i = 0; + } + else if (i <= 2&& strcmp(fields[0], "trace") == 0) { + if (l == nil) + error(Ebadarg); + l->logfstrace = i > 1 ? strtol(fields[1], nil, 0) : 0; + if (l->server) + logfsservertrace(l->server, l->logfstrace); + if (l->lb) + logfsboottrace(l->lb, l->logfstrace); + i = 0; + } + else if (i == 1 && strcmp(fields[0], "unconfig") == 0) { + if (l == nil) + error(Ebadarg); + if (l->ref.ref > 0) + error(Einuse); + devlogfsunconfig(l); + i = 0; + } + else if (i == 2 && strcmp(fields[0], "extent") == 0) { + if (l == nil) + error(Ebadarg); + devlogfsdumpinit(l, extentdumpinit, extentdumpread, i - 1, fields + 1); + i = 0; + } + else if (i >= 2 && strcmp(fields[0], "test") == 0) { + if (l == nil) + error(Ebadarg); + errorany(logfsservertestcmd(l->server, i - 1, fields + 1)); + i = 0; + } +#ifdef LEAKHUNT + else if (i == 1 && strcmp(fields[0], "leakaudit") == 0) { + leakaudit(); + i = 0; + } +#endif + } + if (i != 0) + error(Ebadarg); + return n; + } + error(Egreg); + return 0; /* not reached */ +} + +static void +devlogfsfree(Devlogfs *devlogfs) +{ + if (devlogfs != nil) { + int i; + logfsfreemem(devlogfs->device); + logfsfreemem(devlogfs->name); + for (i = 0; i < Qend - Qfs; i++) + logfsfreemem(devlogfs->filename[i]); + cclose(devlogfs->flash); + cclose(devlogfs->flashctl); + qlock(&devlogfs->qlock); + logfsserverfree(&devlogfs->server); + logfsbootfree(devlogfs->lb); + if (devlogfs->ll) + (*devlogfs->ll->free)(devlogfs->ll); + logfsfreemem(devlogfs->readbuf); + qunlock(&devlogfs->qlock); + logfsfreemem(devlogfs); + } +} + +#ifdef EMU +ulong +logfsnow(void) +{ + extern vlong timeoffset; + return (timeoffset + osusectime()) / 1000000; +} +#endif + +Dev logfsdevtab = { + 0x29f, +// L'ʟ', + "logfs", + +#ifndef EMU + devreset, +#endif + devinit, +#ifndef EMU + devshutdown, +#endif + devlogfsattach, + devlogfswalk, + devlogfsstat, + devlogfsopen, + devcreate, + devlogfsclose, + devlogfsread, + devbread, + devlogfswrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/emu/port/devmem.c b/emu/port/devmem.c new file mode 100644 index 00000000..78f772c6 --- /dev/null +++ b/emu/port/devmem.c @@ -0,0 +1,484 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "interp.h" + +enum +{ + Qdir, + Qctl, + Qstate, + Qsum, + Qevent, + Qprof, + Qheap, + Qgc +}; + +static +Dirtab memdir[] = +{ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "memctl", {Qctl}, 0, 0666, + "memstate", {Qstate}, 0, 0444, + "memsum", {Qsum}, 0, 0444, + "memevent", {Qevent}, 0, 0444, + "memprof", {Qprof}, 0, 0444, + "memheap", {Qheap}, 0, 0444, + "memgc", {Qgc}, 0, 0444, +}; + +enum +{ + /* these are the top two bits of size */ + Pflags= 3<<30, + Pfree= 0<<30, + Palloc= 1<<30, + Paend= 2<<30, + Pimmutable= 3<<30, + + Npool = 3, + Nevent = 10000, + Nstate = 12000 +}; + +/* + * pool snapshots + */ +typedef struct Pstate Pstate; +struct Pstate +{ + ulong base; + ulong size; +}; + +static struct +{ + Pstate state[3+Nstate]; + Pstate* lim; + Pstate* ptr; + int summary; +} poolstate[Npool]; +static Ref stateopen; + +/* + * pool/heap allocation events + */ +typedef struct Pevent Pevent; +struct Pevent +{ + int pool; + ulong pc; + ulong base; + ulong size; +}; + +static struct +{ + Lock l; + Ref inuse; + Rendez r; + int open; + Pevent events[Nevent]; + int rd; + int wr; + int full; + int want; + ulong lost; +} poolevents; + +/* + * allocation profiles + */ +typedef struct Pprof Pprof; +typedef struct Pbucket Pbucket; + +struct Pbucket +{ + ulong val; + ulong pool; + ulong count; + ulong size; + Pbucket* next; +}; + +static struct { + Ref inuse; /* only one of these things */ + Lock l; + Pbucket buckets[1000]; + Pbucket snap[1000]; + int used; + int snapped; + Pbucket* hash[128]; + ulong lost; +} memprof; + +extern void (*memmonitor)(int, ulong, ulong, ulong); +extern ulong gcnruns; +extern ulong gcsweeps; +extern ulong gcbroken; +extern ulong gchalted; +extern ulong gcepochs; +extern uvlong gcdestroys; +extern uvlong gcinspects; +extern uvlong gcbusy; +extern uvlong gcidle; +extern uvlong gcidlepass; +extern uvlong gcpartial; + + +static void +mprofreset(void) +{ + lock(&memprof.l); /* need ilock in kernel */ + memset(memprof.hash, 0, sizeof(memprof.hash)); + memprof.used = 0; + memprof.lost = 0; + unlock(&memprof.l); +} + +static void +mprofmonitor(int pool, ulong pc, ulong base, ulong size) +{ + Pbucket **h0, **h, *p; + + if((pool&7) == 1) + return; /* ignore heap */ + USED(base); + h0 = &memprof.hash[((pc>>16)^(pc>>4))&(nelem(memprof.hash)-1)]; + lock(&memprof.l); + for(h = h0; (p = *h) != nil; h = &p->next) + if(p->val == pc && p->pool == pool){ + p->count++; + p->size += size; + *h = p->next; + p->next = *h0; + *h0 = p; + unlock(&memprof.l); + return; + } + if(memprof.used >= nelem(memprof.buckets)){ + memprof.lost++; + unlock(&memprof.l); + return; + } + p = &memprof.buckets[memprof.used++]; + p->val = pc; + p->pool = pool; + p->count = 1; + p->size = size; + p->next = *h0; + *h0 = p; + unlock(&memprof.l); +} + +static void +_memmonitor(int pool, ulong pc, ulong base, ulong size) +{ + Pevent e; + + e.pool = pool; + e.pc = pc; + e.base = base; + e.size = size; + lock(&poolevents.l); + if(!poolevents.full){ + poolevents.events[poolevents.wr] = e; + if(++poolevents.wr == nelem(poolevents.events)) + poolevents.wr = 0; + if(poolevents.wr == poolevents.rd) + poolevents.full = 1; + }else + poolevents.lost++; + if(poolevents.want){ + poolevents.want = 0; + Wakeup(&poolevents.r); + } + unlock(&poolevents.l); +} + +static int +ismemdata(void *v) +{ + USED(v); + return poolevents.full || poolevents.rd != poolevents.wr; +} + +static char* +memaudit(int pno, Bhdr *b) +{ + Pstate *p; + + if(pno >= Npool) + return "too many pools for memaudit"; + if((p = poolstate[pno].ptr) == poolstate[pno].lim){ + if(b->magic == MAGIC_E) + return nil; + p = &poolstate[pno].state[1]; + if(b->magic == MAGIC_F) + p++; + p->base++; + p->size += b->size; + return nil; + } + poolstate[pno].ptr++; + p->base = (ulong)b; + p->size = b->size; + switch(b->magic){ + case MAGIC_A: + p->size |= Palloc; + break; + case MAGIC_F: + p->size |= Pfree; + break; + case MAGIC_E: + p->size = b->csize | Paend; + break; + case MAGIC_I: + p->size |= Pimmutable; + break; + default: + return "bad magic number in block"; + } + return nil; +} + +static void +mput4(uchar *m, ulong v) +{ + m[0] = v>>24; + m[1] = v>>16; + m[2] = v>>8; + m[3] = v; +} + +static Chan* +memattach(char *spec) +{ + return devattach('%', spec); +} + +static Walkqid* +memwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, memdir, nelem(memdir), devgen); +} + +static int +memstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, memdir, nelem(memdir), devgen); +} + +static Chan* +memopen(Chan *c, int omode) +{ + if(memmonitor != nil && c->qid.path != Qgc) + error(Einuse); + c = devopen(c, omode, memdir, nelem(memdir), devgen); + switch((ulong)c->qid.path){ + case Qevent: + if(incref(&poolevents.inuse) != 1){ + decref(&poolevents.inuse); + c->flag &= ~COPEN; + error(Einuse); + } + poolevents.rd = poolevents.wr = 0; + poolevents.full = 0; + poolevents.want = 0; + poolevents.lost = 0; + memmonitor = _memmonitor; + poolevents.open = 1; + break; + case Qstate: + if(incref(&stateopen) != 1){ + decref(&stateopen); + c->flag &= ~COPEN; + error(Einuse); + } + break; + case Qprof: + if(incref(&memprof.inuse) != 1){ + decref(&memprof.inuse); + c->flag &= ~COPEN; + error(Einuse); + } + memmonitor = mprofmonitor; + break; + } + return c; +} + +static void +memclose(Chan *c) +{ + if((c->flag & COPEN) == 0) + return; + switch((ulong)c->qid.path) { + case Qevent: + memmonitor = nil; + poolevents.open = 0; + decref(&poolevents.inuse); + break; + case Qstate: + decref(&stateopen); + break; + case Qprof: + decref(&memprof.inuse); + memmonitor = nil; + break; + } + +} + +static long +memread(Chan *c, void *va, long count, vlong offset) +{ + uchar *m; + char *e, *s; + int i, summary; + long n, nr; + Pstate *p; + Pevent pe; + Pbucket *b; + + if(c->qid.type & QTDIR) + return devdirread(c, va, count, memdir, nelem(memdir), devgen); + + summary = 0; + switch((ulong)c->qid.path) { + default: + error(Egreg); + case Qctl: + return 0; + case Qsum: + summary = 1; + /* fall through */ + case Qstate: + if(offset == 0){ + for(i=0; i<Npool; i++){ + poolstate[i].ptr = &poolstate[i].state[3]; + poolstate[i].lim = poolstate[i].ptr + Nstate; + memset(poolstate[i].state, 0, sizeof(poolstate[i].state)); + poolstate[i].summary = summary; + } + e = poolaudit(memaudit); + if(e != nil){ + print("mem: %s\n", e); + error(e); + } + } + m = va; + nr = offset/8; + for(i=0; i<Npool && count >= 8; i++){ + n = poolstate[i].ptr - poolstate[i].state; + poolstate[i].state[0].base = i; + poolstate[i].state[0].size = n; + if(nr >= n){ + nr -= n; + continue; + } + n -= nr; + p = &poolstate[i].state[nr]; + for(; --n >= 0 && (count -= 8) >= 0; m += 8, p++){ + mput4(m, p->base); + mput4(m+4, p->size); + } + } + return m-(uchar*)va; + case Qevent: + while(!ismemdata(nil)){ + poolevents.want = 1; + Sleep(&poolevents.r, ismemdata, nil); + } + m = va; + do{ + if((count -= 4*4) < 0) + return m-(uchar*)va; + pe = poolevents.events[poolevents.rd]; + mput4(m, pe.pool); + mput4(m+4, pe.pc); + mput4(m+8, pe.base); + mput4(m+12, pe.size); + m += 4*4; + if(++poolevents.rd >= nelem(poolevents.events)) + poolevents.rd = 0; + }while(poolevents.rd != poolevents.wr); + poolevents.full = 0; + return m-(uchar*)va; + case Qprof: + if(offset == 0){ + lock(&memprof.l); + memmove(memprof.snap, memprof.buckets, memprof.used*sizeof(memprof.buckets[0])); + memprof.snapped = memprof.used; + unlock(&memprof.l); + } + m = va; + for(i = offset/(4*4); i < memprof.snapped && (count -= 4*4) >= 0; i++){ + b = &memprof.snap[i]; + mput4(m, b->pool); + mput4(m+4, b->val); + mput4(m+8, b->count); + mput4(m+12, b->size); + m += 4*4; + } + return m-(uchar*)va; + case Qgc: + s = malloc(READSTR); + if(s == nil) + error(Enomem); + if(waserror()){ + free(s); + nexterror(); + } + snprint(s, READSTR, "runs: %lud\nsweeps: %lud\nbchain: %lud\nhalted: %lud\nepochs: %lud\ndestroy: %llud\ninspects: %llud\nbusy: %llud\nidle: %llud\nidlepass: %llud\npartial: %llud\n", + gcnruns, gcsweeps, gcbroken, gchalted, gcepochs, gcdestroys, gcinspects, gcbusy, gcidle, gcidlepass, gcpartial); + count = readstr(offset, va, count, s); + poperror(); + free(s); + return count; + } +} + +static long +memwrite(Chan *c, void *va, long count, vlong offset) +{ + USED(offset); + USED(count); + USED(va); + + if(c->qid.type & QTDIR) + error(Eperm); + + switch((ulong)c->qid.path) { + default: + error(Egreg); + case Qctl: + error(Ebadarg); + case Qstate: + error(Eperm); + case Qprof: + mprofreset(); + break; + } + return 0; +} + +Dev memdevtab = { + '%', + "mem", + + devinit, + memattach, + memwalk, + memstat, + memopen, + devcreate, + memclose, + memread, + devbread, + memwrite, + devbwrite, + devremove, + devwstat +}; diff --git a/emu/port/devmnt.c b/emu/port/devmnt.c new file mode 100644 index 00000000..6a18dd0a --- /dev/null +++ b/emu/port/devmnt.c @@ -0,0 +1,1203 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + +/* + * References are managed as follows: + * The channel to the server - a network connection or pipe - has one + * reference for every Chan open on the server. The server channel has + * c->mux set to the Mnt used for muxing control to that server. Mnts + * have no reference count; they go away when c goes away. + * Each channel derived from the mount point has mchan set to c, + * and increfs/decrefs mchan to manage references on the server + * connection. + */ + +#define MAXRPC (IOHDRSZ+8192) + +struct Mntrpc +{ + Chan* c; /* Channel for whom we are working */ + Mntrpc* list; /* Free/pending list */ + Fcall request; /* Outgoing file system protocol message */ + Fcall reply; /* Incoming reply */ + Mnt* m; /* Mount device during rpc */ + Rendez r; /* Place to hang out */ + uchar* rpc; /* I/O Data buffer */ + uint rpclen; /* len of buffer */ + Block *b; /* reply blocks */ + char done; /* Rpc completed */ + uvlong stime; /* start time for mnt statistics */ + ulong reqlen; /* request length for mnt statistics */ + ulong replen; /* reply length for mnt statistics */ + Mntrpc* flushed; /* message this one flushes */ +}; + +enum +{ + TAGSHIFT = 5, /* ulong has to be 32 bits */ + TAGMASK = (1<<TAGSHIFT)-1, + NMASK = (64*1024)>>TAGSHIFT, +}; + +struct Mntalloc +{ + Lock l; + Mnt* list; /* Mount devices in use */ + Mnt* mntfree; /* Free list */ + Mntrpc* rpcfree; + int nrpcfree; + int nrpcused; + ulong id; + ulong tagmask[NMASK]; +}mntalloc; + +void mattach(Mnt*, Chan*, char*); +Mnt* mntchk(Chan*); +void mntdirfix(uchar*, Chan*); +Mntrpc* mntflushalloc(Mntrpc*, ulong); +void mntflushfree(Mnt*, Mntrpc*); +void mntfree(Mntrpc*); +void mntgate(Mnt*); +void mntpntfree(Mnt*); +void mntqrm(Mnt*, Mntrpc*); +Mntrpc* mntralloc(Chan*, ulong); +long mntrdwr(int, Chan*, void*, long, vlong); +int mntrpcread(Mnt*, Mntrpc*); +void mountio(Mnt*, Mntrpc*); +void mountmux(Mnt*, Mntrpc*); +void mountrpc(Mnt*, Mntrpc*); +int rpcattn(void*); +Chan* mntchan(void); + +char Esbadstat[] = "invalid directory entry received from server"; +char Enoversion[] = "version not established for mount channel"; + + +void (*mntstats)(int, Chan*, uvlong, ulong); + +static void +mntinit(void) +{ + mntalloc.id = 1; + mntalloc.tagmask[0] = 1; /* don't allow 0 as a tag */ + mntalloc.tagmask[NMASK-1] = 0x80000000UL; /* don't allow NOTAG */ + fmtinstall('F', fcallfmt); +/* fmtinstall('D', dirfmt); */ +/* fmtinstall('M', dirmodefmt); */ + + cinit(); +} + +/* + * Version is not multiplexed: message sent only once per connection. + */ +long +mntversion(Chan *c, char *version, int msize, int returnlen) +{ + Fcall f; + uchar *msg; + Mnt *m; + char *v; + long k, l; + uvlong oo; + char buf[128]; + + qlock(&c->umqlock); /* make sure no one else does this until we've established ourselves */ + if(waserror()){ + qunlock(&c->umqlock); + nexterror(); + } + + /* defaults */ + if(msize == 0) + msize = MAXRPC; + if(msize > c->iounit && c->iounit != 0) + msize = c->iounit; + v = version; + if(v == nil || v[0] == '\0') + v = VERSION9P; + + /* validity */ + if(msize < 0) + error("bad iounit in version call"); + if(strncmp(v, VERSION9P, strlen(VERSION9P)) != 0) + error("bad 9P version specification"); + + m = c->mux; + + if(m != nil){ + qunlock(&c->umqlock); + poperror(); + + strecpy(buf, buf+sizeof buf, m->version); + k = strlen(buf); + if(strncmp(buf, v, k) != 0){ + snprint(buf, sizeof buf, "incompatible 9P versions %s %s", m->version, v); + error(buf); + } + if(returnlen > 0){ + if(returnlen < k) + error(Eshort); + memmove(version, buf, k); + } + return k; + } + + f.type = Tversion; + f.tag = NOTAG; + f.msize = msize; + f.version = v; + msg = malloc(8192+IOHDRSZ); + if(msg == nil) + exhausted("version memory"); + if(waserror()){ + free(msg); + nexterror(); + } + k = convS2M(&f, msg, 8192+IOHDRSZ); + if(k == 0) + error("bad fversion conversion on send"); + + lock(&c->l); + oo = c->offset; + c->offset += k; + unlock(&c->l); + + l = devtab[c->type]->write(c, msg, k, oo); + + if(l < k){ + lock(&c->l); + c->offset -= k - l; + unlock(&c->l); + error("short write in fversion"); + } + + /* message sent; receive and decode reply */ + k = devtab[c->type]->read(c, msg, 8192+IOHDRSZ, c->offset); + if(k <= 0) + error("EOF receiving fversion reply"); + + lock(&c->l); + c->offset += k; + unlock(&c->l); + + l = convM2S(msg, k, &f); + if(l != k) + error("bad fversion conversion on reply"); + if(f.type != Rversion){ + if(f.type == Rerror) + error(f.ename); + error("unexpected reply type in fversion"); + } + if(f.msize > msize) + error("server tries to increase msize in fversion"); + if(f.msize<256 || f.msize>1024*1024) + error("nonsense value of msize in fversion"); + if(strncmp(f.version, v, strlen(f.version)) != 0) + error("bad 9P version returned from server"); + + /* now build Mnt associated with this connection */ + lock(&mntalloc.l); + m = mntalloc.mntfree; + if(m != 0) + mntalloc.mntfree = m->list; + else { + m = malloc(sizeof(Mnt)); + if(m == 0) { + unlock(&mntalloc.l); + exhausted("mount devices"); + } + } + m->list = mntalloc.list; + mntalloc.list = m; + m->version = nil; + kstrdup(&m->version, f.version); + m->id = mntalloc.id++; + m->q = qopen(10*MAXRPC, 0, nil, nil); + m->msize = f.msize; + unlock(&mntalloc.l); + + poperror(); /* msg */ + free(msg); + + lock(&m->l); + m->queue = 0; + m->rip = 0; + + c->flag |= CMSG; + c->mux = m; + m->c = c; + unlock(&m->l); + + poperror(); /* c */ + qunlock(&c->umqlock); + + k = strlen(f.version); + if(returnlen > 0){ + if(returnlen < k) + error(Eshort); + memmove(version, f.version, k); + } + + return k; +} + +Chan* +mntauth(Chan *c, char *spec) +{ + Mnt *m; + Mntrpc *r; + + m = c->mux; + + if(m == nil){ + mntversion(c, VERSION9P, MAXRPC, 0); + m = c->mux; + if(m == nil) + error(Enoversion); + } + + c = mntchan(); + if(waserror()) { + /* Close must not be called since it will + * call mnt recursively + */ + chanfree(c); + nexterror(); + } + + r = mntralloc(0, m->msize); + + if(waserror()) { + mntfree(r); + nexterror(); + } + + r->request.type = Tauth; + r->request.afid = c->fid; + r->request.uname = up->env->user; + r->request.aname = spec; + mountrpc(m, r); + + c->qid = r->reply.aqid; + c->mchan = m->c; + incref(&m->c->r); + c->mqid = c->qid; + c->mode = ORDWR; + + poperror(); /* r */ + mntfree(r); + + poperror(); /* c */ + + return c; + +} + +static Chan* +mntattach(char *muxattach) +{ + Mnt *m; + Chan *c; + Mntrpc *r; + struct bogus{ + Chan *chan; + Chan *authchan; + char *spec; + int flags; + }bogus; + + bogus = *((struct bogus *)muxattach); + c = bogus.chan; + + m = c->mux; + + if(m == nil){ + mntversion(c, nil, 0, 0); + m = c->mux; + if(m == nil) + error(Enoversion); + } + + c = mntchan(); + if(waserror()) { + /* Close must not be called since it will + * call mnt recursively + */ + chanfree(c); + nexterror(); + } + + r = mntralloc(0, m->msize); + + if(waserror()) { + mntfree(r); + nexterror(); + } + + r->request.type = Tattach; + r->request.fid = c->fid; + if(bogus.authchan == nil) + r->request.afid = NOFID; + else + r->request.afid = bogus.authchan->fid; + r->request.uname = up->env->user; + r->request.aname = bogus.spec; + mountrpc(m, r); + + c->qid = r->reply.qid; + c->mchan = m->c; + incref(&m->c->r); + c->mqid = c->qid; + + poperror(); /* r */ + mntfree(r); + + poperror(); /* c */ + + if(bogus.flags&MCACHE) + c->flag |= CCACHE; + return c; +} + +Chan* +mntchan(void) +{ + Chan *c; + + c = devattach('M', 0); + lock(&mntalloc.l); + c->dev = mntalloc.id++; + unlock(&mntalloc.l); + + if(c->mchan) + panic("mntchan non-zero %p", c->mchan); + return c; +} + +static Walkqid* +mntwalk(Chan *c, Chan *nc, char **name, int nname) +{ + volatile int alloc; + int i; + Mnt *m; + Mntrpc *r; + Walkqid *wq; + + if(nc != nil) + print("mntwalk: nc != nil\n"); + if(nname > MAXWELEM) + error("devmnt: too many name elements"); + alloc = 0; + wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid)); + if(waserror()){ + if(alloc && wq->clone!=nil) + cclose(wq->clone); + free(wq); + return nil; + } + + alloc = 0; + m = mntchk(c); + r = mntralloc(c, m->msize); + if(nc == nil){ + nc = devclone(c); + /* + * Until the other side accepts this fid, we can't mntclose it. + * Therefore set type to 0 for now; rootclose is known to be safe. + */ + nc->type = 0; + alloc = 1; + } + wq->clone = nc; + + if(waserror()) { + mntfree(r); + nexterror(); + } + r->request.type = Twalk; + r->request.fid = c->fid; + r->request.newfid = nc->fid; + r->request.nwname = nname; + memmove(r->request.wname, name, nname*sizeof(char*)); + + mountrpc(m, r); + + if(r->reply.nwqid > nname) + error("too many QIDs returned by walk"); + if(r->reply.nwqid < nname){ + if(alloc) + cclose(nc); + wq->clone = nil; + if(r->reply.nwqid == 0){ + free(wq); + wq = nil; + goto Return; + } + } + + /* move new fid onto mnt device and update its qid */ + if(wq->clone != nil){ + if(wq->clone != c){ + wq->clone->type = c->type; + wq->clone->mchan = c->mchan; + incref(&c->mchan->r); + } + if(r->reply.nwqid > 0) + wq->clone->qid = r->reply.wqid[r->reply.nwqid-1]; + } + wq->nqid = r->reply.nwqid; + for(i=0; i<wq->nqid; i++) + wq->qid[i] = r->reply.wqid[i]; + + Return: + poperror(); + mntfree(r); + poperror(); + return wq; +} + +static int +mntstat(Chan *c, uchar *dp, int n) +{ + Mnt *m; + Mntrpc *r; + + if(n < BIT16SZ) + error(Eshortstat); + m = mntchk(c); + r = mntralloc(c, m->msize); + if(waserror()) { + mntfree(r); + nexterror(); + } + r->request.type = Tstat; + r->request.fid = c->fid; + mountrpc(m, r); + + if(r->reply.nstat > n){ + /* doesn't fit; just patch the count and return */ + PBIT16((uchar*)dp, r->reply.nstat); + n = BIT16SZ; + }else{ + n = r->reply.nstat; + memmove(dp, r->reply.stat, n); + validstat(dp, n); + mntdirfix(dp, c); + } + poperror(); + mntfree(r); + return n; +} + +static Chan* +mntopencreate(int type, Chan *c, char *name, int omode, ulong perm) +{ + Mnt *m; + Mntrpc *r; + + m = mntchk(c); + r = mntralloc(c, m->msize); + if(waserror()) { + mntfree(r); + nexterror(); + } + r->request.type = type; + r->request.fid = c->fid; + r->request.mode = omode; + if(type == Tcreate){ + r->request.perm = perm; + r->request.name = name; + } + mountrpc(m, r); + + c->qid = r->reply.qid; + c->offset = 0; + c->mode = openmode(omode); + c->iounit = r->reply.iounit; + if(c->iounit == 0 || c->iounit > m->msize-IOHDRSZ) + c->iounit = m->msize-IOHDRSZ; + c->flag |= COPEN; + poperror(); + mntfree(r); + + if(c->flag & CCACHE) + copen(c); + + return c; +} + +static Chan* +mntopen(Chan *c, int omode) +{ + return mntopencreate(Topen, c, nil, omode, 0); +} + +static void +mntcreate(Chan *c, char *name, int omode, ulong perm) +{ + mntopencreate(Tcreate, c, name, omode, perm); +} + +static void +mntclunk(Chan *c, int t) +{ + Mnt *m; + Mntrpc *r; + + m = mntchk(c); + r = mntralloc(c, m->msize); + if(waserror()){ + mntfree(r); + nexterror(); + } + + r->request.type = t; + r->request.fid = c->fid; + mountrpc(m, r); + mntfree(r); + poperror(); +} + +void +muxclose(Mnt *m) +{ + Mntrpc *q, *r; + + for(q = m->queue; q; q = r) { + r = q->list; + mntfree(q); + } + m->id = 0; + free(m->version); + m->version = nil; + mntpntfree(m); +} + +void +mntpntfree(Mnt *m) +{ + Mnt *f, **l; + Queue *q; + + lock(&mntalloc.l); + l = &mntalloc.list; + for(f = *l; f; f = f->list) { + if(f == m) { + *l = m->list; + break; + } + l = &f->list; + } + m->list = mntalloc.mntfree; + mntalloc.mntfree = m; + q = m->q; + unlock(&mntalloc.l); + + qfree(q); +} + +static void +mntclose(Chan *c) +{ + mntclunk(c, Tclunk); +} + +static void +mntremove(Chan *c) +{ + mntclunk(c, Tremove); +} + +static int +mntwstat(Chan *c, uchar *dp, int n) +{ + Mnt *m; + Mntrpc *r; + + m = mntchk(c); + r = mntralloc(c, m->msize); + if(waserror()) { + mntfree(r); + nexterror(); + } + r->request.type = Twstat; + r->request.fid = c->fid; + r->request.nstat = n; + r->request.stat = dp; + mountrpc(m, r); + poperror(); + mntfree(r); + return n; +} + +static long +mntread(Chan *c, void *buf, long n, vlong off) +{ + uchar *p, *e; + int nc, cache, isdir, dirlen; + + isdir = 0; + cache = c->flag & CCACHE; + if(c->qid.type & QTDIR) { + cache = 0; + isdir = 1; + } + + p = buf; + if(cache) { + nc = cread(c, buf, n, off); + if(nc > 0) { + n -= nc; + if(n == 0) + return nc; + p += nc; + off += nc; + } + n = mntrdwr(Tread, c, p, n, off); + cupdate(c, p, n, off); + return n + nc; + } + + n = mntrdwr(Tread, c, buf, n, off); + if(isdir) { + for(e = &p[n]; p+BIT16SZ < e; p += dirlen){ + dirlen = BIT16SZ+GBIT16(p); + if(p+dirlen > e) + break; + validstat(p, dirlen); + mntdirfix(p, c); + } + if(p != e) + error(Esbadstat); + } + return n; +} + +static long +mntwrite(Chan *c, void *buf, long n, vlong off) +{ + return mntrdwr(Twrite, c, buf, n, off); +} + +long +mntrdwr(int type, Chan *c, void *buf, long n, vlong off) +{ + Mnt *m; + Mntrpc *r; /* TO DO: volatile struct { Mntrpc *r; } r; */ + char *uba; + int cache; + ulong cnt, nr, nreq; + + m = mntchk(c); + uba = buf; + cnt = 0; + cache = c->flag & CCACHE; + if(c->qid.type & QTDIR) + cache = 0; + for(;;) { + r = mntralloc(c, m->msize); + if(waserror()) { + mntfree(r); + nexterror(); + } + r->request.type = type; + r->request.fid = c->fid; + r->request.offset = off; + r->request.data = uba; + nr = n; + if(nr > m->msize-IOHDRSZ) + nr = m->msize-IOHDRSZ; + r->request.count = nr; + mountrpc(m, r); + nreq = r->request.count; + nr = r->reply.count; + if(nr > nreq) + nr = nreq; + + if(type == Tread) + r->b = bl2mem((uchar*)uba, r->b, nr); + else if(cache) + cwrite(c, (uchar*)uba, nr, off); + + poperror(); + mntfree(r); + off += nr; + uba += nr; + cnt += nr; + n -= nr; + if(nr != nreq || n == 0 || up->killed) + break; + } + return cnt; +} + +void +mountrpc(Mnt *m, Mntrpc *r) +{ + char *sn, *cn; + int t; + + r->reply.tag = 0; + r->reply.type = Tmax; /* can't ever be a valid message type */ + + mountio(m, r); + + t = r->reply.type; + switch(t) { + case Rerror: + error(r->reply.ename); + case Rflush: + error(Eintr); + default: + if(t == r->request.type+1) + break; + sn = "?"; + if(m->c->name != nil) + sn = m->c->name->s; + cn = "?"; + if(r->c != nil && r->c->name != nil) + cn = r->c->name->s; + print("mnt: proc %s %lud: mismatch from %s %s rep 0x%p tag %d fid %d T%d R%d rp %d\n", + up->text, up->pid, sn, cn, + r, r->request.tag, r->request.fid, r->request.type, + r->reply.type, r->reply.tag); + error(Emountrpc); + } +} + +void +mountio(Mnt *m, Mntrpc *r) +{ + int n; + + while(waserror()) { + if(m->rip == up) + mntgate(m); + if(strcmp(up->env->errstr, Eintr) != 0){ + mntflushfree(m, r); + nexterror(); + } + r = mntflushalloc(r, m->msize); + } + + lock(&m->l); + r->m = m; + r->list = m->queue; + m->queue = r; + unlock(&m->l); + + /* Transmit a file system rpc */ + if(m->msize == 0) + panic("msize"); + n = convS2M(&r->request, r->rpc, m->msize); + if(n < 0) + panic("bad message type in mountio"); + if(devtab[m->c->type]->write(m->c, r->rpc, n, 0) != n) + error(Emountrpc); +/* r->stime = fastticks(nil); */ + r->reqlen = n; + + /* Gate readers onto the mount point one at a time */ + for(;;) { + lock(&m->l); + if(m->rip == 0) + break; + unlock(&m->l); + Sleep(&r->r, rpcattn, r); + if(r->done){ + poperror(); + mntflushfree(m, r); + return; + } + } + m->rip = up; + unlock(&m->l); + while(r->done == 0) { + if(mntrpcread(m, r) < 0) + error(Emountrpc); + mountmux(m, r); + } + mntgate(m); + poperror(); + mntflushfree(m, r); +} + +static int +doread(Mnt *m, int len) +{ + Block *b; + + while(qlen(m->q) < len){ + b = devtab[m->c->type]->bread(m->c, m->msize, 0); + if(b == nil) + return -1; + if(blocklen(b) == 0){ + freeblist(b); + return -1; + } + qaddlist(m->q, b); + } + return 0; +} + +int +mntrpcread(Mnt *m, Mntrpc *r) +{ + int i, t, len, hlen; + Block *b, **l, *nb; + + r->reply.type = 0; + r->reply.tag = 0; + + /* read at least length, type, and tag and pullup to a single block */ + if(doread(m, BIT32SZ+BIT8SZ+BIT16SZ) < 0) + return -1; + nb = pullupqueue(m->q, BIT32SZ+BIT8SZ+BIT16SZ); + + /* read in the rest of the message, avoid ridiculous (for now) message sizes */ + len = GBIT32(nb->rp); + if(len > m->msize){ + qdiscard(m->q, qlen(m->q)); + return -1; + } + if(doread(m, len) < 0) + return -1; + + /* pullup the header (i.e. everything except data) */ + t = nb->rp[BIT32SZ]; + switch(t){ + case Rread: + hlen = BIT32SZ+BIT8SZ+BIT16SZ+BIT32SZ; + break; + default: + hlen = len; + break; + } + nb = pullupqueue(m->q, hlen); + + if(convM2S(nb->rp, len, &r->reply) <= 0){ + /* bad message, dump it */ + print("mntrpcread: convM2S failed\n"); + qdiscard(m->q, len); + return -1; + } + + /* hang the data off of the fcall struct */ + l = &r->b; + *l = nil; + do { + b = qremove(m->q); + if(hlen > 0){ + b->rp += hlen; + len -= hlen; + hlen = 0; + } + i = BLEN(b); + if(i <= len){ + len -= i; + *l = b; + l = &(b->next); + } else { + /* split block and put unused bit back */ + nb = allocb(i-len); + memmove(nb->wp, b->rp+len, i-len); + b->wp = b->rp+len; + nb->wp += i-len; + qputback(m->q, nb); + *l = b; + return 0; + } + }while(len > 0); + + return 0; +} + +void +mntgate(Mnt *m) +{ + Mntrpc *q; + + lock(&m->l); + m->rip = 0; + for(q = m->queue; q; q = q->list) { + if(q->done == 0) + if(Wakeup(&q->r)) + break; + } + unlock(&m->l); +} + +void +mountmux(Mnt *m, Mntrpc *r) +{ + Mntrpc **l, *q; + + lock(&m->l); + l = &m->queue; + for(q = *l; q; q = q->list) { + /* look for a reply to a message */ + if(q->request.tag == r->reply.tag) { + *l = q->list; + if(q != r) { + /* + * Completed someone else. + * Trade pointers to receive buffer. + */ + q->reply = r->reply; + q->b = r->b; + r->b = nil; + } + q->done = 1; + unlock(&m->l); + if(mntstats != nil) + (*mntstats)(q->request.type, + m->c, q->stime, + q->reqlen + r->replen); + if(q != r) + Wakeup(&q->r); + return; + } + l = &q->list; + } + unlock(&m->l); + if(r->reply.type == Rerror) + print("unexpected reply tag %ud; type %d (error %q)\n", r->reply.tag, r->reply.type, r->reply.ename); + else + print("unexpected reply tag %ud; type %d\n", r->reply.tag, r->reply.type); +} + +/* + * Create a new flush request and chain the previous + * requests from it + */ +Mntrpc* +mntflushalloc(Mntrpc *r, ulong iounit) +{ + Mntrpc *fr; + + fr = mntralloc(0, iounit); + + fr->request.type = Tflush; + if(r->request.type == Tflush) + fr->request.oldtag = r->request.oldtag; + else + fr->request.oldtag = r->request.tag; + fr->flushed = r; + + return fr; +} + +/* + * Free a chain of flushes. Remove each unanswered + * flush and the original message from the unanswered + * request queue. Mark the original message as done + * and if it hasn't been answered set the reply to to + * Rflush. + */ +void +mntflushfree(Mnt *m, Mntrpc *r) +{ + Mntrpc *fr; + + while(r){ + fr = r->flushed; + if(!r->done){ + r->reply.type = Rflush; + mntqrm(m, r); + } + if(fr) + mntfree(r); + r = fr; + } +} + +static int +alloctag(void) +{ + int i, j; + ulong v; + + for(i = 0; i < NMASK; i++){ + v = mntalloc.tagmask[i]; + if(v == ~0UL) + continue; + for(j = 0; j < 1<<TAGSHIFT; j++) + if((v & (1<<j)) == 0){ + mntalloc.tagmask[i] |= 1<<j; + return (i<<TAGSHIFT) + j; + } + } + /* panic("no devmnt tags left"); */ + return NOTAG; +} + +static void +freetag(int t) +{ + mntalloc.tagmask[t>>TAGSHIFT] &= ~(1<<(t&TAGMASK)); +} + +Mntrpc* +mntralloc(Chan *c, ulong msize) +{ + Mntrpc *new; + + lock(&mntalloc.l); + new = mntalloc.rpcfree; + if(new == nil){ + new = malloc(sizeof(Mntrpc)); + if(new == nil) { + unlock(&mntalloc.l); + exhausted("mount rpc header"); + } + /* + * The header is split from the data buffer as + * mountmux may swap the buffer with another header. + */ + new->rpc = mallocz(msize, 0); + if(new->rpc == nil){ + free(new); + unlock(&mntalloc.l); + exhausted("mount rpc buffer"); + } + new->rpclen = msize; + new->request.tag = alloctag(); + if(new->request.tag == NOTAG){ + free(new); + unlock(&mntalloc.l); + exhausted("rpc tags"); + } + } + else { + mntalloc.rpcfree = new->list; + mntalloc.nrpcfree--; + if(new->rpclen < msize){ + free(new->rpc); + new->rpc = mallocz(msize, 0); + if(new->rpc == nil){ + free(new); + mntalloc.nrpcused--; + unlock(&mntalloc.l); + exhausted("mount rpc buffer"); + } + new->rpclen = msize; + } + } + mntalloc.nrpcused++; + unlock(&mntalloc.l); + new->c = c; + new->done = 0; + new->flushed = nil; + new->b = nil; + return new; +} + +void +mntfree(Mntrpc *r) +{ + if(r->b != nil) + freeblist(r->b); + lock(&mntalloc.l); + if(mntalloc.nrpcfree >= 10){ + free(r->rpc); + free(r); + freetag(r->request.tag); + } + else{ + r->list = mntalloc.rpcfree; + mntalloc.rpcfree = r; + mntalloc.nrpcfree++; + } + mntalloc.nrpcused--; + unlock(&mntalloc.l); +} + +void +mntqrm(Mnt *m, Mntrpc *r) +{ + Mntrpc **l, *f; + + lock(&m->l); + r->done = 1; + + l = &m->queue; + for(f = *l; f; f = f->list) { + if(f == r) { + *l = r->list; + break; + } + l = &f->list; + } + unlock(&m->l); +} + +Mnt* +mntchk(Chan *c) +{ + Mnt *m; + + /* This routine is mostly vestiges of prior lives; now it's just sanity checking */ + + if(c->mchan == nil) + panic("mntchk 1: nil mchan c %s\n", c2name(c)); + + m = c->mchan->mux; + + if(m == nil) + print("mntchk 2: nil mux c %s c->mchan %s \n", c2name(c), c2name(c->mchan)); + + /* + * Was it closed and reused (was error(Eshutdown); now, it can't happen) + */ + if(m->id == 0 || m->id >= c->dev) + panic("mntchk 3: can't happen"); + + return m; +} + +/* + * Rewrite channel type and dev for in-flight data to + * reflect local values. These entries are known to be + * the first two in the Dir encoding after the count. + */ +void +mntdirfix(uchar *dirbuf, Chan *c) +{ + uint r; + + r = devtab[c->type]->dc; + dirbuf += BIT16SZ; /* skip count */ + PBIT16(dirbuf, r); + dirbuf += BIT16SZ; + PBIT32(dirbuf, c->dev); +} + +int +rpcattn(void *v) +{ + Mntrpc *r; + + r = v; + return r->done || r->m->rip == 0; +} + +Dev mntdevtab = { + 'M', + "mnt", + + mntinit, + mntattach, + mntwalk, + mntstat, + mntopen, + mntcreate, + mntclose, + mntread, + devbread, + mntwrite, + devbwrite, + mntremove, + mntwstat, +}; diff --git a/emu/port/devpipe.c b/emu/port/devpipe.c new file mode 100644 index 00000000..32c16ab6 --- /dev/null +++ b/emu/port/devpipe.c @@ -0,0 +1,460 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include <interp.h> + +#define NETTYPE(x) ((ulong)(x)&0x1f) +#define NETID(x) (((ulong)(x))>>5) +#define NETQID(i,t) (((i)<<5)|(t)) + +typedef struct Pipe Pipe; +struct Pipe +{ + QLock l; + Pipe* next; + int ref; + ulong path; + Queue* q[2]; + int qref[2]; + Dirtab* pipedir; + char* user; +}; + +static struct +{ + Lock l; + ulong path; + int pipeqsize; +} pipealloc; + +enum +{ + Qdir, + Qdata0, + Qdata1 +}; + +Dirtab pipedir[] = +{ + ".", {Qdir,0,QTDIR}, 0, DMDIR|0500, + "data", {Qdata0}, 0, 0660, + "data1", {Qdata1}, 0, 0660, +}; + +static void +freepipe(Pipe *p) +{ + if(p != nil){ + free(p->user); + free(p->q[0]); + free(p->q[1]); + free(p->pipedir); + free(p); + } +} + +static void +pipeinit(void) +{ + pipealloc.pipeqsize = 32*1024; +} + +/* + * create a pipe, no streams are created until an open + */ +static Chan* +pipeattach(char *spec) +{ + Pipe *p; + Chan *c; + + c = devattach('|', spec); + p = malloc(sizeof(Pipe)); + if(p == 0) + error(Enomem); + if(waserror()){ + freepipe(p); + nexterror(); + } + p->pipedir = malloc(sizeof(pipedir)); + if (p->pipedir == 0) + error(Enomem); + memmove(p->pipedir, pipedir, sizeof(pipedir)); + kstrdup(&p->user, up->env->user); + p->ref = 1; + + p->q[0] = qopen(pipealloc.pipeqsize, 0, 0, 0); + if(p->q[0] == 0) + error(Enomem); + p->q[1] = qopen(pipealloc.pipeqsize, 0, 0, 0); + if(p->q[1] == 0) + error(Enomem); + poperror(); + + lock(&pipealloc.l); + p->path = ++pipealloc.path; + unlock(&pipealloc.l); + + c->qid.path = NETQID(2*p->path, Qdir); + c->qid.vers = 0; + c->qid.type = QTDIR; + c->aux = p; + c->dev = 0; + return c; +} + +static int +pipegen(Chan *c, char *name, Dirtab *tab, int ntab, int i, Dir *dp) +{ + int id, len; + Qid qid; + Pipe *p; + + USED(name); + if(i == DEVDOTDOT){ + devdir(c, c->qid, "#|", 0, eve, 0555, dp); + return 1; + } + i++; /* skip . */ + if(tab==0 || i>=ntab) + return -1; + tab += i; + p = c->aux; + switch(NETTYPE(tab->qid.path)){ + case Qdata0: + len = qlen(p->q[0]); + break; + case Qdata1: + len = qlen(p->q[1]); + break; + default: + len = tab->length; + break; + } + id = NETID(c->qid.path); + qid.path = NETQID(id, tab->qid.path); + qid.vers = 0; + qid.type = QTFILE; + devdir(c, qid, tab->name, len, eve, tab->perm, dp); + return 1; +} + +static Walkqid* +pipewalk(Chan *c, Chan *nc, char **name, int nname) +{ + Walkqid *wq; + Pipe *p; + + p = c->aux; + wq = devwalk(c, nc, name, nname, p->pipedir, nelem(pipedir), pipegen); + if(wq != nil && wq->clone != nil && wq->clone != c){ + qlock(&p->l); + p->ref++; + if(c->flag & COPEN){ + switch(NETTYPE(c->qid.path)){ + case Qdata0: + p->qref[0]++; + break; + case Qdata1: + p->qref[1]++; + break; + } + } + qunlock(&p->l); + } + return wq; +} + +static int +pipestat(Chan *c, uchar *db, int n) +{ + Pipe *p; + Dir dir; + Dirtab *tab; + + p = c->aux; + tab = p->pipedir; + + switch(NETTYPE(c->qid.path)){ + case Qdir: + devdir(c, c->qid, ".", 0, eve, DMDIR|0555, &dir); + break; + case Qdata0: + devdir(c, c->qid, tab[1].name, qlen(p->q[0]), eve, tab[1].perm, &dir); + break; + case Qdata1: + devdir(c, c->qid, tab[2].name, qlen(p->q[1]), eve, tab[2].perm, &dir); + break; + default: + panic("pipestat"); + } + n = convD2M(&dir, db, n); + if(n < BIT16SZ) + error(Eshortstat); + return n; +} + +/* + * if the stream doesn't exist, create it + */ +static Chan* +pipeopen(Chan *c, int omode) +{ + Pipe *p; + + if(c->qid.type & QTDIR){ + if(omode != OREAD) + error(Ebadarg); + c->mode = omode; + c->flag |= COPEN; + c->offset = 0; + return c; + } + + openmode(omode); /* check it */ + + p = c->aux; + qlock(&p->l); + if(waserror()){ + qunlock(&p->l); + nexterror(); + } + switch(NETTYPE(c->qid.path)){ + case Qdata0: + devpermcheck(p->user, p->pipedir[1].perm, omode); + p->qref[0]++; + break; + case Qdata1: + devpermcheck(p->user, p->pipedir[2].perm, omode); + p->qref[1]++; + break; + } + poperror(); + qunlock(&p->l); + + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + c->iounit = qiomaxatomic; + return c; +} + +static void +pipeclose(Chan *c) +{ + Pipe *p; + + p = c->aux; + qlock(&p->l); + + if(c->flag & COPEN){ + /* + * closing either side hangs up the stream + */ + switch(NETTYPE(c->qid.path)){ + case Qdata0: + p->qref[0]--; + if(p->qref[0] == 0){ + qhangup(p->q[1], 0); + qclose(p->q[0]); + } + break; + case Qdata1: + p->qref[1]--; + if(p->qref[1] == 0){ + qhangup(p->q[0], 0); + qclose(p->q[1]); + } + break; + } + } + + + /* + * if both sides are closed, they are reusable + */ + if(p->qref[0] == 0 && p->qref[1] == 0){ + qreopen(p->q[0]); + qreopen(p->q[1]); + } + + /* + * free the structure on last close + */ + p->ref--; + if(p->ref == 0){ + qunlock(&p->l); + freepipe(p); + } else + qunlock(&p->l); +} + +static long +piperead(Chan *c, void *va, long n, vlong junk) +{ + Pipe *p; + + p = c->aux; + + USED(junk); + switch(NETTYPE(c->qid.path)){ + case Qdir: + return devdirread(c, va, n, p->pipedir, nelem(pipedir), pipegen); + case Qdata0: + return qread(p->q[0], va, n); + case Qdata1: + return qread(p->q[1], va, n); + default: + panic("piperead"); + } + return -1; /* not reached */ +} + +static Block* +pipebread(Chan *c, long n, ulong offset) +{ + Pipe *p; + + p = c->aux; + + switch(NETTYPE(c->qid.path)){ + case Qdata0: + return qbread(p->q[0], n); + case Qdata1: + return qbread(p->q[1], n); + } + + return devbread(c, n, offset); +} + +/* + * a write to a closed pipe causes an exception to be sent to + * the prog. + */ +static long +pipewrite(Chan *c, void *va, long n, vlong junk) +{ + Pipe *p; + Prog *r; + + USED(junk); + if(waserror()) { + /* avoid exceptions when pipe is a mounted queue */ + if((c->flag & CMSG) == 0) { + r = up->iprog; + if(r != nil && r->kill == nil) + r->kill = "write on closed pipe"; + } + nexterror(); + } + + p = c->aux; + + switch(NETTYPE(c->qid.path)){ + case Qdata0: + n = qwrite(p->q[1], va, n); + break; + + case Qdata1: + n = qwrite(p->q[0], va, n); + break; + + default: + panic("pipewrite"); + } + + poperror(); + return n; +} + +static long +pipebwrite(Chan *c, Block *bp, ulong junk) +{ + long n; + Pipe *p; + Prog *r; + + USED(junk); + if(waserror()) { + /* avoid exceptions when pipe is a mounted queue */ + if((c->flag & CMSG) == 0) { + r = up->iprog; + if(r != nil && r->kill == nil) + r->kill = "write on closed pipe"; + } + nexterror(); + } + + p = c->aux; + switch(NETTYPE(c->qid.path)){ + case Qdata0: + n = qbwrite(p->q[1], bp); + break; + + case Qdata1: + n = qbwrite(p->q[0], bp); + break; + + default: + n = 0; + panic("pipebwrite"); + } + + poperror(); + return n; +} + +static int +pipewstat(Chan *c, uchar *dp, int n) +{ + Dir *d; + Pipe *p; + int d1; + + if (c->qid.type&QTDIR) + error(Eperm); + p = c->aux; + if(strcmp(up->env->user, p->user) != 0) + error(Eperm); + d = smalloc(sizeof(*d)+n); + if(waserror()){ + free(d); + nexterror(); + } + n = convM2D(dp, n, d, (char*)&d[1]); + if(n == 0) + error(Eshortstat); + d1 = NETTYPE(c->qid.path) == Qdata1; + if(!emptystr(d->name)){ + validwstatname(d->name); + if(strlen(d->name) >= KNAMELEN) + error(Efilename); + if(strcmp(p->pipedir[1+!d1].name, d->name) == 0) + error(Eexist); + kstrcpy(p->pipedir[1+d1].name, d->name, KNAMELEN); + } + if(d->mode != ~0UL) + p->pipedir[d1 + 1].perm = d->mode & 0777; + poperror(); + free(d); + return n; +} + +Dev pipedevtab = { + '|', + "pipe", + + pipeinit, + pipeattach, + pipewalk, + pipestat, + pipeopen, + devcreate, + pipeclose, + piperead, + pipebread, + pipewrite, + pipebwrite, + devremove, + pipewstat, +}; diff --git a/emu/port/devpointer.c b/emu/port/devpointer.c new file mode 100644 index 00000000..30bad028 --- /dev/null +++ b/emu/port/devpointer.c @@ -0,0 +1,304 @@ +/* + * mouse or stylus + */ + +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include <draw.h> +#include <memdraw.h> +#include <cursor.h> + +#define cursorenable() +#define cursordisable() + +enum{ + Qdir, + Qpointer, + Qcursor +}; + +typedef struct Pointer Pointer; + +struct Pointer { + int x; + int y; + int b; + ulong msec; +}; + +static struct +{ + Pointer v; + int modify; + int lastb; + Rendez r; + Ref ref; + QLock q; +} mouse; + +static +Dirtab pointertab[]={ + ".", {Qdir, 0, QTDIR}, 0, 0555, + "pointer", {Qpointer}, 0, 0666, + "cursor", {Qcursor}, 0, 0222, +}; + +enum { + Nevent = 16 /* enough for some */ +}; + +static struct { + int rd; + int wr; + Pointer clicks[Nevent]; + Rendez r; + int full; + int put; + int get; +} ptrq; + +/* + * called by any source of pointer data + */ +void +mousetrack(int b, int x, int y, int isdelta) +{ + int lastb; + ulong msec; + Pointer e; + + if(isdelta){ + x += mouse.v.x; + y += mouse.v.y; + } + msec = osmillisec(); + if(0 && b && (mouse.v.b ^ b)&0x1f){ + if(msec - mouse.v.msec < 300 && mouse.lastb == b + && abs(mouse.v.x - x) < 12 && abs(mouse.v.y - y) < 12) + b |= 1<<8; + mouse.lastb = b & 0x1f; + mouse.v.msec = msec; + } + if((b&(1<<8))==0 && x == mouse.v.x && y == mouse.v.y && mouse.v.b == b) + return; + lastb = mouse.v.b; + mouse.v.x = x; + mouse.v.y = y; + mouse.v.b = b; + mouse.v.msec = msec; + if(!ptrq.full && lastb != b){ + e = mouse.v; + ptrq.clicks[ptrq.wr] = e; + if(++ptrq.wr >= Nevent) + ptrq.wr = 0; + if(ptrq.wr == ptrq.rd) + ptrq.full = 1; + } + mouse.modify = 1; + ptrq.put++; + Wakeup(&ptrq.r); +/* drawactive(1); */ +/* setpointer(x, y); */ +} + +static int +ptrqnotempty(void *x) +{ + USED(x); + return ptrq.full || ptrq.put != ptrq.get; +} + +static Pointer +mouseconsume(void) +{ + Pointer e; + + Sleep(&ptrq.r, ptrqnotempty, 0); + ptrq.full = 0; + ptrq.get = ptrq.put; + if(ptrq.rd != ptrq.wr){ + e = ptrq.clicks[ptrq.rd]; + if(++ptrq.rd >= Nevent) + ptrq.rd = 0; + }else + e = mouse.v; + return e; +} + +Point +mousexy(void) +{ + return Pt(mouse.v.x, mouse.v.y); +} + + +static Chan* +pointerattach(char* spec) +{ + return devattach('m', spec); +} + +static Walkqid* +pointerwalk(Chan *c, Chan *nc, char **name, int nname) +{ + Walkqid *wq; + + wq = devwalk(c, nc, name, nname, pointertab, nelem(pointertab), devgen); + if(wq != nil && wq->clone != c && wq->clone != nil && (ulong)c->qid.path == Qpointer) + incref(&mouse.ref); /* can this happen? */ + return wq; +} + +static int +pointerstat(Chan* c, uchar *db, int n) +{ + return devstat(c, db, n, pointertab, nelem(pointertab), devgen); +} + +static Chan* +pointeropen(Chan* c, int omode) +{ + c = devopen(c, omode, pointertab, nelem(pointertab), devgen); + if((ulong)c->qid.path == Qpointer){ + if(waserror()){ + c->flag &= ~COPEN; + nexterror(); + } + if(!canqlock(&mouse.q)) + error(Einuse); + if(incref(&mouse.ref) != 1){ + qunlock(&mouse.q); + error(Einuse); + } + cursorenable(); + qunlock(&mouse.q); + poperror(); + } + return c; +} + +static void +pointerclose(Chan* c) +{ + if((c->flag & COPEN) == 0) + return; + switch((ulong)c->qid.path){ + case Qpointer: + qlock(&mouse.q); + if(decref(&mouse.ref) == 0){ + cursordisable(); + } + qunlock(&mouse.q); + break; + } +} + +static long +pointerread(Chan* c, void* a, long n, vlong off) +{ + Pointer mt; + char buf[1+4*12+1]; + int l; + + USED(&off); + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, a, n, pointertab, nelem(pointertab), devgen); + case Qpointer: + qlock(&mouse.q); + if(waserror()) { + qunlock(&mouse.q); + nexterror(); + } + mt = mouseconsume(); + poperror(); + qunlock(&mouse.q); + l = snprint(buf, sizeof(buf), "m%11d %11d %11d %11lud ", mt.x, mt.y, mt.b, mt.msec); + if(l < n) + n = l; + memmove(a, buf, n); + break; + default: + n=0; + break; + } + return n; +} + +static long +pointerwrite(Chan* c, void* va, long n, vlong off) +{ + char *a = va; + char buf[128]; + int b, x, y; + Drawcursor cur; + + USED(&off); + switch((ulong)c->qid.path){ + case Qpointer: + if(n > sizeof buf-1) + n = sizeof buf -1; + memmove(buf, va, n); + buf[n] = 0; + x = strtoul(buf+1, &a, 0); + if(*a == 0) + error(Eshort); + y = strtoul(a, &a, 0); + if(*a != 0) + b = strtoul(a, 0, 0); + else + b = mouse.v.b; + /*mousetrack(b, x, y, msec);*/ + setpointer(x, y); + USED(b); + break; + case Qcursor: + /* TO DO: perhaps interpret data as an Image */ + /* + * hotx[4] hoty[4] dx[4] dy[4] clr[dx/8 * dy/2] set[dx/8 * dy/2] + * dx must be a multiple of 8; dy must be a multiple of 2. + */ + if(n == 0){ + cur.data = nil; + drawcursor(&cur); + break; + } + if(n < 8) + error(Eshort); + cur.hotx = BGLONG((uchar*)va+0*4); + cur.hoty = BGLONG((uchar*)va+1*4); + cur.minx = 0; + cur.miny = 0; + cur.maxx = BGLONG((uchar*)va+2*4); + cur.maxy = BGLONG((uchar*)va+3*4); + if(cur.maxx%8 != 0 || cur.maxy%2 != 0 || n-4*4 != (cur.maxx/8 * cur.maxy)) + error(Ebadarg); + cur.data = (uchar*)va + 4*4; + drawcursor(&cur); + break; + default: + error(Ebadusefd); + } + return n; +} + +Dev pointerdevtab = { + 'm', + "pointer", + + devinit, + pointerattach, + pointerwalk, + pointerstat, + pointeropen, + devcreate, + pointerclose, + pointerread, + devbread, + pointerwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/emu/port/devprof.c b/emu/port/devprof.c new file mode 100644 index 00000000..86d6d962 --- /dev/null +++ b/emu/port/devprof.c @@ -0,0 +1,817 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "interp.h" +#include <isa.h> +#include "runt.h" + +extern Pool* imagmem; +extern void (*memmonitor)(int, ulong, ulong, ulong); + +static void cpxec(Prog *); +static void memprof(int, void*, ulong); +static void memprofmi(int, ulong, ulong, ulong); + +extern Inst* pc2dispc(Inst*, Module*); + +static int interval = 100; /* Sampling interval in milliseconds */ + +enum +{ + HSIZE = 32, +}; + +#define HASH(m) ((m)%HSIZE) + +/* cope with multiple profilers some day */ + +typedef struct Record Record; +struct Record +{ + int id; + char* name; + char* path; + Inst* base; + int size; + /*Module* m; */ + ulong mtime; + Qid qid; + Record* hash; + Record* link; + ulong bucket[1]; +}; + +struct +{ + Lock l; + vlong time; + Record* hash[HSIZE]; + Record* list; +} profile; + +typedef struct Pmod Pmod; +struct Pmod +{ + char* name; + Pmod* link; +} *pmods; + +#define QSHIFT 4 +#define QID(q) ((ulong)(q).path&0xf) +#define QPID(pid) ((pid)<<QSHIFT) +#define PID(q) ((q).vers) +#define PATH(q) ((ulong)(q).path&~((1<<QSHIFT)-1)) + +enum +{ + Qdir, + Qname, + Qpath, + Qhist, + Qpctl, + Qctl, +}; + +Dirtab profdir[] = +{ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "name", {Qname}, 0, 0444, + "path", {Qpath}, 0, 0444, + "histogram", {Qhist}, 0, 0444, + "pctl", {Qpctl}, 0, 0222, + "ctl", {Qctl}, 0, 0222, +}; + +enum{ + Pnil, /* null profiler */ + Psam, /* sampling profiler */ + Pcov, /* coverage profiler */ + Pmem, /* heap memory profiler */ +}; + +enum{ + Mnone = 0, + Mmain = 1, + Mheap = 2, + Mimage = 4, +}; + +static int profiler = Pnil; +static int mprofiler = Mnone; + +static int ids; +static int samplefn; + +static void sampler(void*); + +static Record* +getrec(int id) +{ + Record *r; + + for(r = profile.list; r != nil; r = r->link) + if(r->id == id) + break; + return r; +} + +static void +addpmod(char *m) +{ + Pmod *p = malloc(sizeof(Pmod)); + + if(p == nil) + return; + p->name = malloc(strlen(m)+1); + if(p->name == nil){ + free(p); + return; + } + strcpy(p->name, m); + p->link = pmods; + pmods = p; +} + +static void +freepmods(void) +{ + Pmod *p, *np; + + for(p = pmods; p != nil; p = np){ + free(p->name); + np = p->link; + free(p); + } + pmods = nil; +} + +static int +inpmods(char *m) +{ + Pmod *p; + + for(p = pmods; p != nil; p = p->link) + if(strcmp(p->name, m) == 0) + return 1; + return 0; +} + +static void +freeprof(void) +{ + int i; + Record *r, *nr; + + ids = 0; + profiler = Pnil; + mprofiler = Mnone; + freepmods(); + for(r = profile.list; r != nil; r = nr){ + free(r->name); + free(r->path); + nr = r->link; + free(r); + } + profile.list = nil; + profile.time = 0; + for(i = 0; i < HSIZE; i++) + profile.hash[i] = nil; +} + +static int +profgen(Chan *c, char *name, Dirtab *d, int nd, int s, Dir *dp) +{ + Qid qid; + Record *r; + ulong path, perm, len; + Dirtab *tab; + + USED(name); + USED(d); + USED(nd); + + if(s == DEVDOTDOT) { + mkqid(&qid, Qdir, 0, QTDIR); + devdir(c, qid, "#P", 0, eve, 0555, dp); + return 1; + } + + if(c->qid.path == Qdir && c->qid.type & QTDIR) { + acquire(); + if(s-- == 0){ + tab = &profdir[Qctl]; + mkqid(&qid, PATH(c->qid)|tab->qid.path, c->qid.vers, QTFILE); + devdir(c, qid, tab->name, tab->length, eve, tab->perm, dp); + release(); + return 1; + } + r = profile.list; + while(s-- && r != nil) + r = r->link; + if(r == nil) { + release(); + return -1; + } + sprint(up->genbuf, "%.8lux", (ulong)r->id); + mkqid(&qid, (r->id<<QSHIFT), r->id, QTDIR); + devdir(c, qid, up->genbuf, 0, eve, DMDIR|0555, dp); + release(); + return 1; + } + if(s >= nelem(profdir)-1) + error(Enonexist); /* was return -1; */ + tab = &profdir[s]; + path = PATH(c->qid); + + acquire(); + r = getrec(PID(c->qid)); + if(r == nil) { + release(); + error(Enonexist); /* was return -1; */ + } + + perm = tab->perm; + len = tab->length; + mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE); + devdir(c, qid, tab->name, len, eve, perm, dp); + release(); + return 1; +} + +static Chan* +profattach(char *spec) +{ + return devattach('P', spec); +} + +static Walkqid* +profwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, 0, 0, profgen); +} + +static int +profstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, 0, 0, profgen); +} + +static Chan* +profopen(Chan *c, int omode) +{ + int qid; + Record *r; + + if(c->qid.type & QTDIR) { + if(omode != OREAD) + error(Eisdir); + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; + } + + if(omode&OTRUNC) + error(Eperm); + + qid = QID(c->qid); + if(qid == Qctl || qid == Qpctl){ + if (omode != OWRITE) + error(Eperm); + } + else{ + if(omode != OREAD) + error(Eperm); + } + + if(qid != Qctl){ + acquire(); + r = getrec(PID(c->qid)); + release(); + if(r == nil) + error(Ethread); + } + + c->offset = 0; + c->flag |= COPEN; + c->mode = openmode(omode); + if(QID(c->qid) == Qhist) + c->aux = nil; + return c; +} + +static int +profwstat(Chan *c, uchar *dp, int n) +{ + Dir d; + Record *r; + + if(strcmp(up->env->user, eve)) + error(Eperm); + if(c->qid.type & QTDIR) + error(Eperm); + acquire(); + r = getrec(PID(c->qid)); + release(); + if(r == nil) + error(Ethread); + n = convM2D(dp, n, &d, nil); + if(n == 0) + error(Eshortstat); + d.mode &= 0777; + /* TO DO: copy to c->aux->perm, once that exists */ + return n; +} + +static void +profclose(Chan *c) +{ + USED(c); +} + +static long +profread(Chan *c, void *va, long n, vlong offset) +{ + int i; + Record *r; + char *a = va; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, profgen); + acquire(); + r = getrec(PID(c->qid)); + release(); + if(r == nil) + error(Ethread); + switch(QID(c->qid)){ + case Qname: + return readstr(offset, va, n, r->name); + case Qpath: + return readstr(offset, va, n, r->path); + case Qhist: + i = (int)c->aux; + while(i < r->size && r->bucket[i] == 0) + i++; + if(i >= r->size) + return 0; + c->aux = (void*)(i+1); + if(n < 20) + error(Etoosmall); + return sprint(a, "%d %lud", i, r->bucket[i]); + case Qctl: + error(Eperm); + } + return 0; +} + +static long +profwrite(Chan *c, void *va, long n, vlong offset) +{ + int i; + char *a = va; + char buf[128], *fields[128]; + void (*f)(int, ulong, ulong, ulong); + + USED(va); + USED(n); + USED(offset); + + if(c->qid.type & QTDIR) + error(Eisdir); + switch(QID(c->qid)){ + case Qctl: + if(n > sizeof(buf)-1) + n = sizeof(buf)-1; + memmove(buf, a, n); + buf[n] = 0; + i = getfields(buf, fields, nelem(fields), 1, " \t\n"); + if(i > 0 && strcmp(fields[0], "module") == 0){ + f = memmonitor; + memmonitor = nil; + freepmods(); + while(--i > 0) + addpmod(fields[i]); + memmonitor = f; + return n; + } + if(i == 1){ + if(strcmp(fields[0], "start") == 0){ + if(profiler == Pnil) { + profiler = Psam; + if(!samplefn){ + samplefn = 1; + kproc("prof", sampler, 0, 0); + } + } + } + else if(strncmp(fields[0], "startmp", 7) == 0){ + if(profiler == Pnil){ + profiler = Pmem; + for(a = &fields[0][7]; *a != '\0'; a++){ + if(*a == '1'){ + memmonitor = memprofmi; + mprofiler |= Mmain; + } + else if(*a == '2'){ + heapmonitor = memprof; + mprofiler |= Mheap; + } + else if(*a == '3'){ + memmonitor = memprofmi; + mprofiler |= Mimage; + } + }; + } + } + else if(strcmp(fields[0], "stop") == 0){ + profiler = Pnil; + mprofiler = Mnone; + } + else if(strcmp(fields[0], "end") == 0){ + profiler = Pnil; + mprofiler = Mnone; + memmonitor = nil; + freeprof(); + interval = 100; + } + else + error(Ebadarg); + } + else if (i == 2){ + if(strcmp(fields[0], "interval") == 0) + interval = strtoul(fields[1], nil, 0); + else if(strcmp(fields[0], "startcp") == 0){ + Prog *p; + + acquire(); + p = progpid(strtoul(fields[1], nil, 0)); + if(p == nil){ + release(); + return -1; + } + if(profiler == Pnil){ + profiler = Pcov; + p->xec = cpxec; + } + release(); + } + else + error(Ebadarg); + } + else + error(Ebadarg); + return n; + default: + error(Eperm); + } + return 0; +} + +static Record* +newmodule(Module *m, int vm, int scale, int origin) +{ + int dsize; + Record *r, **l; + + if(!vm) + acquire(); + if((m->compiled && m->pctab == nil) || m->prog == nil) { + if(!vm) + release(); + return nil; + } + if(m->compiled) + dsize = m->nprog * sizeof(r->bucket[0]); + else + dsize = (msize(m->prog)/sizeof(Inst)) * sizeof(r->bucket[0]); + dsize *= scale; + dsize += origin; + r = malloc(sizeof(Record)+dsize); + if(r == nil) { + if(!vm) + release(); + return nil; + } + + r->id = ++ids; + if(ids == (1<<8)-1) + ids = 0; + kstrdup(&r->name, m->name); + kstrdup(&r->path, m->path); + r->base = m->prog; + r->size = dsize/sizeof(r->bucket[0]); + /* r->m = m; */ + r->mtime = m->mtime; + r->qid.path = m->qid.path; + r->qid.vers = m->qid.vers; + memset(r->bucket, 0, dsize); + r->link = profile.list; + profile.list = r; + + l = &profile.hash[HASH(m->mtime)]; + r->hash = *l; + *l = r; + + if(!vm) + release(); + return r; +} + +#define LIMBO(m) ((m)->path[0] != '$') + +Module* +limbomodule(void) +{ + Frame *f; + uchar *fp; + Module *m; + + m = R.M->m; + if(LIMBO(m)) + return m; + for(fp = R.FP ; fp != nil; fp = f->fp){ + f = (Frame*)fp; + if(f->mr != nil){ + m = f->mr->m; + if(LIMBO(m)) + return m; + } + } + return nil; +} + +static Record* +mlook(Module *m, int limbo, int vm, int scale, int origin) +{ + Record *r; + void (*f)(int, ulong, ulong, ulong); + + if(limbo) + m = limbomodule(); + if(m == nil) + return nil; + for(r = profile.hash[HASH(m->mtime)]; r; r = r->hash){ + if(r->mtime == m->mtime && r->qid.path == m->qid.path && r->qid.vers == m->qid.vers && strcmp(r->name, m->name) == 0 && strcmp(r->path, m->path) == 0){ + r->base = m->prog; + return r; + } + } + if(pmods == nil || inpmods(m->name) || inpmods(m->path)){ + f = memmonitor; + memmonitor = nil; /* prevent monitoring of our memory usage */ + r = newmodule(m, vm, scale, origin); + memmonitor = f; + return r; + } + return nil; +} + +static void +sampler(void* a) +{ + int i; + Module *m; + Record *r; + Inst *p; + + USED(a); + for(;;) { + osmillisleep(interval); + if(profiler != Psam) + break; + lock(&profile.l); + profile.time += interval; + if(R.M == H || (m = R.M->m) == nil){ + unlock(&profile.l); + continue; + } + p = R.PC; + r = mlook(m, 0, 0, 1, 0); + if(r == nil){ + unlock(&profile.l); + continue; + } + if(m->compiled && m->pctab != nil) + p = pc2dispc(p, m); + if((i = p-r->base) >= 0 && i < r->size) + r->bucket[i]++; + unlock(&profile.l); + } + samplefn = 0; + pexit("", 0); +} + +/* + * coverage profiling + */ + +static void +cpxec(Prog *p) +{ + int op, i; + Module *m; + Record *r; + Prog *n; + + R = p->R; + R.MP = R.M->MP; + R.IC = p->quanta; + + if(p->kill != nil){ + char *m; + m = p->kill; + p->kill = nil; + error(m); + } + + if(R.M->compiled) + comvec(); + else{ + m = R.M->m; + r = profiler == Pcov ? mlook(m, 0, 1, 1, 0) : nil; + do{ + dec[R.PC->add](); + op = R.PC->op; + if(r != nil){ + i = R.PC-r->base; + if(i >= 0 && i < r->size) + r->bucket[i]++; + } + R.PC++; + optab[op](); + if(op == ISPAWN || op == IMSPAWN){ + n = delruntail(Pdebug); /* any state will do */ + n->xec = cpxec; + addrun(n); + } + if(m != R.M->m){ + m = R.M->m; + r = profiler == Pcov ? mlook(m, 0, 1, 1, 0) : nil; + } + }while(--R.IC != 0); + } + + p->R = R; +} + +/* memory profiling */ + +enum{ + Mhalloc, + Mhfree, + Mgcfree, + Mmfree, + Mmalloc, + Mifree, + Mialloc, +}; + +static void +memprof(int c, void *v, ulong n) +{ + int i, j, k; + ulong kk, *b; + Module *m; + Record *r; + Inst *p; + Heap *h; + + USED(v); + USED(n); + if(profiler != Pmem){ + memmonitor = nil; + heapmonitor = nil; + return; + } + lock(&profile.l); + m = nil; + if(c != Mgcfree && (R.M == H || (m = R.M->m) == nil)){ + unlock(&profile.l); + return; + } + h = v; + if(c == Mhalloc || c == Mmalloc || c == Mialloc){ + p = R.PC; + if(m->compiled && m->pctab != nil) + p = pc2dispc(p, m); + if((r = mlook(m, 1, 1, 2, 2)) == nil){ + unlock(&profile.l); + return; + } + i = p-r->base; + k = (r->id<<24) | i; + if(c == Mhalloc){ + h->hprof = k; + j = hmsize(h)-sizeof(Heap); + } + else if(c == Mmalloc){ + setmalloctag(v, k); + j = msize(v); + } + else{ + ((ulong*)v)[1] = k; + j = poolmsize(imagmem, v)-sizeof(ulong); + } + } + else{ + if(c == Mmfree) + k = getmalloctag(v); + else if(c == Mifree) + k = ((ulong*)v)[1]; + else + k = h->hprof; + if((r = getrec(k>>24)) == nil){ + unlock(&profile.l); + return; + } + i = k&0xffffff; + if(c == Mmfree) + j = msize(v); + else if(c == Mifree) + j = poolmsize(imagmem, v)-sizeof(ulong); + else + j = hmsize(h)-sizeof(Heap); + j = -j; + } + i = 2*(i+1); + b = r->bucket; + if(i >= 0 && i < r->size){ + if(0){ + if(c == 1){ + b[0] -= j; + b[i] -= j; + } + else if(c == 2){ + b[1] -= j; + b[i+1] -= j; + } + } + else{ + b[0] += j; + if((int)b[0] < 0) + b[0] = 0; + b[i] += j; + if((int)b[i] < 0) + b[i] = 0; + if(j > 0){ + if((kk = b[0]) > b[1]) + b[1] = kk; + if((kk = b[i]) > b[i+1]) + b[i+1] = kk; + } + } + } + unlock(&profile.l); +} + +/* main and image memory */ +static void +memprofmi(int c, ulong pc, ulong v, ulong n) +{ + USED(pc); + + if(c&2){ + if(!(mprofiler&Mimage)) + return; + } + else{ + if(!(mprofiler&Mmain)) + return; + } + switch(c){ + case 0: + c = Mmalloc; + break; + case 2: + c = Mialloc; + break; + case 0 | 1<<8: + c = Mmfree; + break; + case 2 | 1<<8: + c = Mifree; + break; + default: + print("bad profile code %d\n", c); + } + memprof(c, (void*)v, n); +} + +Dev profdevtab = { + 'P', + "prof", + + devinit, + profattach, + profwalk, + profstat, + profopen, + devcreate, + profclose, + profread, + devbread, + profwrite, + devbwrite, + devremove, + profwstat +}; diff --git a/emu/port/devprog.c b/emu/port/devprog.c new file mode 100644 index 00000000..8ee6a75b --- /dev/null +++ b/emu/port/devprog.c @@ -0,0 +1,1507 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include <interp.h> +#include <isa.h> +#include "runt.h" + +/* + * Enable the heap device for environments that allow debugging => + * Must be 1 for a production environment. + */ +int SECURE = 0; + +enum +{ + Qdir, + Qctl, + Qdbgctl, + Qheap, + Qns, + Qnsgrp, + Qpgrp, + Qstack, + Qstatus, + Qtext, + Qwait, + Qfd, + Qexception, +}; + +/* + * must be in same order as enum + */ +Dirtab progdir[] = +{ + "ctl", {Qctl}, 0, 0200, + "dbgctl", {Qdbgctl}, 0, 0600, + "heap", {Qheap}, 0, 0600, + "ns", {Qns}, 0, 0400, + "nsgrp", {Qnsgrp}, 0, 0444, + "pgrp", {Qpgrp}, 0, 0444, + "stack", {Qstack}, 0, 0400, + "status", {Qstatus}, 0, 0444, + "text", {Qtext}, 0, 0000, + "wait", {Qwait}, 0, 0400, + "fd", {Qfd}, 0, 0400, + "exception", {Qexception}, 0, 0400, +}; + +enum +{ + CMkill, + CMkillgrp, + CMrestricted, + CMexceptions, + CMprivate +}; + +static +Cmdtab progcmd[] = { + CMkill, "kill", 1, + CMkillgrp, "killgrp", 1, + CMrestricted, "restricted", 1, + CMexceptions, "exceptions", 2, + CMprivate, "private", 1, +}; + +enum +{ + CDstep, + CDtoret, + CDcont, + CDstart, + CDstop, + CDunstop, + CDmaim, + CDbpt +}; + +static +Cmdtab progdbgcmd[] = { + CDstep, "step", 0, /* known below to be first, to cope with stepN */ + CDtoret, "toret", 1, + CDcont, "cont", 1, + CDstart, "start", 1, + CDstop, "stop", 1, + CDunstop, "unstop", 1, + CDmaim, "maim", 1, + CDbpt, "bpt", 4, +}; + +typedef struct Heapqry Heapqry; +struct Heapqry +{ + char fmt; + ulong addr; + ulong module; + int count; +}; + +typedef struct Bpt Bpt; + +struct Bpt +{ + Bpt *next; + int pc; + char *file; + char path[1]; +}; + +typedef struct Progctl Progctl; +struct Progctl +{ + Rendez r; + int ref; + Proc *debugger; /* waiting for dbgxec */ + char *msg; /* reply from dbgxec */ + int step; /* instructions to try */ + int stop; /* stop running the program */ + Bpt* bpts; /* active breakpoints */ + Queue* q; /* status queue */ +}; + +#define QSHIFT 4 /* location in qid of pid */ +#define QID(q) (((ulong)(q).path&0x0000000F)>>0) +#define QPID(pid) (((pid)<<QSHIFT)) +#define PID(q) ((q).vers) +#define PATH(q) ((ulong)(q).path&~((1<<QSHIFT)-1)) + +static char *progstate[] = /* must correspond to include/interp.h */ +{ + "alt", /* blocked in alt instruction */ + "send", /* waiting to send */ + "recv", /* waiting to recv */ + "debug", /* debugged */ + "ready", /* ready to be scheduled */ + "release", /* interpreter released */ + "exiting", /* exit because of kill or error */ + "broken", /* thread crashed */ +}; + +static void dbgstep(Progctl*, Prog*, int); +static void dbgstart(Prog*); +static void freebpts(Bpt*); +static Bpt* delbpt(Bpt*, char*, int); +static Bpt* setbpt(Bpt*, char*, int); +static void mntscan(Mntwalk*, Pgrp*); +extern Type *Trdchan; +extern Type *Twrchan; +extern Module* modules; +static char Emisalign[] = "misaligned address"; + +static int +proggen(Chan *c, char *name, Dirtab *tab, int ntab, int s, Dir *dp) +{ + Qid qid; + Prog *p; + char *e; + Osenv *o; + ulong pid, path, perm, len; + + USED(ntab); + + if(s == DEVDOTDOT){ + mkqid(&qid, Qdir, 0, QTDIR); + devdir(c, qid, "#p", 0, eve, DMDIR|0555, dp); + return 1; + } + + if((ulong)c->qid.path == Qdir) { + if(name != nil){ + /* ignore s and use name to find pid */ + pid = strtoul(name, &e, 0); + if(pid == 0 || *e != '\0') + return -1; + acquire(); + p = progpid(pid); + if(p == nil){ + release(); + return -1; + } + }else{ + acquire(); + p = progn(s); + if(p == nil) { + release(); + return -1; + } + pid = p->pid; + } + o = p->osenv; + sprint(up->genbuf, "%lud", pid); + if(name != nil && strcmp(name, up->genbuf) != 0){ + release(); + return -1; + } + mkqid(&qid, pid<<QSHIFT, pid, QTDIR); + devdir(c, qid, up->genbuf, 0, o->user, DMDIR|0555, dp); + release(); + return 1; + } + + if(s >= nelem(progdir)) + return -1; + tab = &progdir[s]; + path = PATH(c->qid); + + acquire(); + p = progpid(PID(c->qid)); + if(p == nil) { + release(); + return -1; + } + + o = p->osenv; + + perm = tab->perm; + if((perm & 7) == 0) + perm = (perm|(perm>>3)|(perm>>6)) & o->pgrp->progmode; + + len = tab->length; + mkqid(&qid, path|tab->qid.path, c->qid.vers, QTFILE); + devdir(c, qid, tab->name, len, o->user, perm, dp); + release(); + return 1; +} + +static Chan* +progattach(char *spec) +{ + return devattach('p', spec); +} + +static Walkqid* +progwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, 0, 0, proggen); +} + +static int +progstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, 0, 0, proggen); +} + +static Chan* +progopen(Chan *c, int omode) +{ + Prog *p; + Osenv *o; + Progctl *ctl; + int perm; + + if(c->qid.type & QTDIR) + return devopen(c, omode, 0, 0, proggen); + + acquire(); + if (waserror()) { + release(); + nexterror(); + } + p = progpid(PID(c->qid)); + if(p == nil) + error(Ethread); + o = p->osenv; + perm = progdir[QID(c->qid)-1].perm; + if((perm & 7) == 0) + perm = (perm|(perm>>3)|(perm>>6)) & o->pgrp->progmode; + devpermcheck(o->user, perm, omode); + omode = openmode(omode); + + switch(QID(c->qid)){ + default: + error(Egreg); + case Qnsgrp: + case Qpgrp: + case Qtext: + case Qstatus: + case Qstack: + case Qctl: + case Qfd: + case Qexception: + break; + case Qwait: + c->aux = qopen(1024, Qmsg, nil, nil); + if(c->aux == nil) + error(Enomem); + o->childq = c->aux; + break; + case Qns: + c->aux = malloc(sizeof(Mntwalk)); + if(c->aux == nil) + error(Enomem); + break; + case Qheap: + if(SECURE || o->pgrp->privatemem || omode != ORDWR) + error(Eperm); + c->aux = malloc(sizeof(Heapqry)); + if(c->aux == nil) + error(Enomem); + break; + case Qdbgctl: + if(SECURE || o->pgrp->privatemem || omode != ORDWR) + error(Eperm); + ctl = malloc(sizeof(Progctl)); + if(ctl == nil) + error(Enomem); + ctl->q = qopen(1024, Qmsg, nil, nil); + if(ctl->q == nil) { + free(ctl); + error(Enomem); + } + ctl->bpts = nil; + ctl->ref = 1; + c->aux = ctl; + break; + } + if(p->state != Pexiting) + c->qid.vers = p->pid; + + poperror(); + release(); + c->offset = 0; + c->mode = omode; + c->flag |= COPEN; + return c; +} + +static int +progwstat(Chan *c, uchar *db, int n) +{ + Dir d; + Prog *p; + char *u; + Osenv *o; + + if(c->qid.type&QTDIR) + error(Eperm); + acquire(); + p = progpid(PID(c->qid)); + if(p == nil) { + release(); + error(Ethread); + } + + u = up->env->user; + o = p->osenv; + if(strcmp(u, o->user) != 0 && strcmp(u, eve) != 0) { + release(); + error(Eperm); + } + + n = convM2D(db, n, &d, nil); + if(n == 0){ + release(); + error(Eshortstat); + } + if(d.mode != ~0UL) + o->pgrp->progmode = d.mode&0777; + release(); + return n; +} + +static void +closedbgctl(Progctl *ctl, Prog *p) +{ + Osenv *o; + + if(ctl->ref-- > 1) + return; + freebpts(ctl->bpts); + if(p != nil){ + o = p->osenv; + if(o->debug == ctl){ + o->debug = nil; + p->xec = xec; + } + } + qfree(ctl->q); + free(ctl); +} + +static void +progclose(Chan *c) +{ + int i; + Prog *f; + Osenv *o; + Progctl *ctl; + + switch(QID(c->qid)) { + case Qns: + case Qheap: + free(c->aux); + break; + case Qdbgctl: + if((c->flag & COPEN) == 0) + return; + ctl = c->aux; + acquire(); + closedbgctl(ctl, progpid(PID(c->qid))); + release(); + break; + case Qwait: + acquire(); + i = 0; + for(;;) { + f = progn(i++); + if(f == nil) + break; + o = f->osenv; + if(o->waitq == c->aux) + o->waitq = nil; + if(o->childq == c->aux) + o->childq = nil; + } + release(); + qfree(c->aux); + } +} + +static int +progsize(Prog *p) +{ + int size; + Frame *f; + uchar *fp; + Modlink *m; + + m = p->R.M; + size = 0; + if(m->MP != H) + size += hmsize(D2H(m->MP)); + if(m->prog != nil) + size += msize(m->prog); + + fp = p->R.FP; + while(fp != nil) { + f = (Frame*)fp; + fp = f->fp; + if(f->mr != nil) { + if(f->mr->MP != H) + size += hmsize(D2H(f->mr->MP)); + if(f->mr->prog != nil) + size += msize(f->mr->prog); + } + if(f->t == nil) + size += msize(SEXTYPE(f)); + } + return size/1024; +} + +static long +progoffset(long offset, char *va, int *np) +{ + if(offset > 0) { + offset -= *np; + if(offset < 0) { + memmove(va, va+*np+offset, -offset); + *np = -offset; + } + else + *np = 0; + } + return offset; +} + +static int +progqidwidth(Chan *c) +{ + char buf[32]; + + return sprint(buf, "%lud", c->qid.vers); +} + +int +progfdprint(Chan *c, int fd, int w, char *s, int ns) +{ + int n; + + if(w == 0) + w = progqidwidth(c); + n = snprint(s, ns, "%3d %.2s %C %4ld (%.16llux %*lud %.2ux) %5ld %8lld %s\n", + fd, + &"r w rw"[(c->mode&3)<<1], + devtab[c->type]->dc, c->dev, + c->qid.path, w, c->qid.vers, c->qid.type, + c->iounit, c->offset, c->name->s); + return n; +} + +static int +progfds(Osenv *o, char *va, int count, long offset) +{ + Fgrp *f; + Chan *c; + int n, i, w, ww; + + f = o->fgrp; /* f is not locked because we've acquired */ + n = readstr(0, va, count, o->pgrp->dot->name->s); + n += snprint(va+n, count-n, "\n"); + offset = progoffset(offset, va, &n); + /* compute width of qid.path */ + w = 0; + for(i = 0; i <= f->maxfd; i++) { + c = f->fd[i]; + if(c == nil) + continue; + ww = progqidwidth(c); + if(ww > w) + w = ww; + } + for(i = 0; i <= f->maxfd; i++) { + c = f->fd[i]; + if(c == nil) + continue; + n += progfdprint(c, i, w, va+n, count-n); + offset = progoffset(offset, va, &n); + } + return n; +} + +Inst * +pc2dispc(Inst *pc, Module *mod) +{ + ulong l, u, m, v; + ulong *tab = mod->pctab; + + v = (ulong)pc - (ulong)mod->prog; + l = 0; + u = mod->nprog-1; + while(l < u){ + m = (l+u+1)/2; + if(tab[m] < v) + l = m; + else if(tab[m] > v) + u = m-1; + else + l = u = m; + } + if(l == u && tab[u] <= v && u != mod->nprog-1 && tab[u+1] > v) + return &mod->prog[u]; + return 0; +} + +static int +progstack(REG *reg, int state, char *va, int count, long offset) +{ + int n; + Frame *f; + Inst *pc; + uchar *fp; + Modlink *m; + + n = 0; + m = reg->M; + fp = reg->FP; + pc = reg->PC; + + /* + * all states other than debug and ready block, + * but interp has already advanced the PC + */ + if(!m->compiled && state != Pready && state != Pdebug && pc > m->prog) + pc--; + if(m->compiled && m->m->pctab != nil) + pc = pc2dispc(pc, m->m); + + while(fp != nil) { + f = (Frame*)fp; + n += snprint(va+n, count-n, "%.8lux %.8lux %.8lux %.8lux %d %s\n", + (ulong)f, /* FP */ + (ulong)(pc - m->prog), /* PC in dis instructions */ + (ulong)m->MP, /* MP */ + (ulong)m->prog, /* Code for module */ + m->compiled && m->m->pctab == nil, /* True if native assembler: fool stack utility for now */ + m->m->path); /* File system path */ + + if(offset > 0) { + offset -= n; + if(offset < 0) { + memmove(va, va+n+offset, -offset); + n = -offset; + } + else + n = 0; + } + + pc = f->lr; + fp = f->fp; + if(f->mr != nil) + m = f->mr; + if(!m->compiled) + pc--; + else if(m->m->pctab != nil) + pc = pc2dispc(pc, m->m)-1; + } + return n; +} + +static int +calldepth(REG *reg) +{ + int n; + uchar *fp; + + n = 0; + for(fp = reg->FP; fp != nil; fp = ((Frame*)fp)->fp) + n++; + return n; +} + +static int +progheap(Heapqry *hq, char *va, int count, ulong offset) +{ + WORD *w; + void *p; + List *hd; + Array *a; + char *fmt, *str; + Module *m; + Modlink *ml; + Channel *c; + ulong addr; + String *ss; + union { REAL r; LONG l; WORD w[2]; } rock; + int i, s, n, len, signed_off; + Type *t; + + n = 0; + s = 0; + signed_off = offset; + addr = hq->addr; + for(i = 0; i < hq->count; i++) { + switch(hq->fmt) { + case 'W': + if(addr & 3) + return -1; + n += snprint(va+n, count-n, "%d\n", *(WORD*)addr); + s = sizeof(WORD); + break; + case 'B': + n += snprint(va+n, count-n, "%d\n", *(BYTE*)addr); + s = sizeof(BYTE); + break; + case 'V': + if(addr & 3) + return -1; + w = (WORD*)addr; + rock.w[0] = w[0]; + rock.w[1] = w[1]; + n += snprint(va+n, count-n, "%lld\n", rock.l); + s = sizeof(LONG); + break; + case 'R': + if(addr & 3) + return -1; + w = (WORD*)addr; + rock.w[0] = w[0]; + rock.w[1] = w[1]; + n += snprint(va+n, count-n, "%g\n", rock.r); + s = sizeof(REAL); + break; + case 'I': + if(addr & 3) + return -1; + for(m = modules; m != nil; m = m->link) + if(m == (Module*)hq->module) + break; + if(m == nil) + error(Ebadctl); + addr = (ulong)(m->prog+addr); + n += snprint(va+n, count-n, "%D\n", (Inst*)addr); + s = sizeof(Inst); + break; + case 'P': + if(addr & 3) + return -1; + p = *(void**)addr; + fmt = "nil\n"; + if(p != H) + fmt = "%lux\n"; + n += snprint(va+n, count-n, fmt, p); + s = sizeof(WORD); + break; + case 'L': + if(addr & 3) + return -1; + hd = *(List**)addr; + if(hd == H || D2H(hd)->t != &Tlist) + return -1; + n += snprint(va+n, count-n, "%lux.%lux\n", (ulong)&hd->tail, (ulong)hd->data); + s = sizeof(WORD); + break; + case 'A': + if(addr & 3) + return -1; + a = *(Array**)addr; + if(a == H) + n += snprint(va+n, count-n, "nil\n"); + else { + if(D2H(a)->t != &Tarray) + return -1; + n += snprint(va+n, count-n, "%d.%lux\n", a->len, (ulong)a->data); + } + s = sizeof(WORD); + break; + case 'C': + if(addr & 3) + return -1; + ss = *(String**)addr; + if(ss == H) + ss = &snil; + else + if(D2H(ss)->t != &Tstring) + return -1; + n += snprint(va+n, count-n, "%d.", abs(ss->len)); + str = string2c(ss); + len = strlen(str); + if(count-n < len) + len = count-n; + if(len > 0) { + memmove(va+n, str, len); + n += len; + } + break; + case 'M': + if(addr & 3) + return -1; + ml = *(Modlink**)addr; + fmt = ml == H ? "nil\n" : "%lux\n"; + n += snprint(va+n, count-n, fmt, ml->MP); + s = sizeof(WORD); + break; + case 'c': + if(addr & 3) + return -1; + c = *(Channel**)addr; + if(c == H) + n += snprint(va+n, count-n, "nil\n"); + else{ + t = D2H(c)->t; + if(t != &Tchannel && t != Trdchan && t != Twrchan) + return -1; + if(c->buf == H) + n += snprint(va+n, count-n, "0.%lux\n", (ulong)c); + else + n += snprint(va+n, count-n, "%d.%lux.%d.%d\n", c->buf->len, (ulong)c->buf->data, c->front, c->size); + } + break; + + } + addr += s; + if(signed_off > 0) { + signed_off -= n; + if(signed_off < 0) { + memmove(va, va+n+signed_off, -signed_off); + n = -signed_off; + } + else + n = 0; + } + } + return n; +} + +WORD +modstatus(REG *r, char *ptr, int len) +{ + Inst *PC; + Frame *f; + + if(r->M->m->name[0] == '$') { + f = (Frame*)r->FP; + snprint(ptr, len, "%s[%s]", f->mr->m->name, r->M->m->name); + if(f->mr->compiled) + return (WORD)f->lr; + return f->lr - f->mr->prog; + } + memmove(ptr, r->M->m->name, len); + if(r->M->compiled) + return (WORD)r->PC; + PC = r->PC; + /* should really check for blocked states */ + if(PC > r->M->prog) + PC--; + return PC - r->M->prog; +} + +static void +int2flag(int flag, char *s) +{ + if(flag == 0){ + *s = '\0'; + return; + } + *s++ = '-'; + if(flag & MAFTER) + *s++ = 'a'; + if(flag & MBEFORE) + *s++ = 'b'; + if(flag & MCREATE) + *s++ = 'c'; + if(flag & MCACHE) + *s++ = 'C'; + *s = '\0'; +} + +static char* +progtime(ulong msec, char *buf, char *ebuf) +{ + int tenths, sec; + + tenths = msec/100; + sec = tenths/10; + seprint(buf, ebuf, "%4d:%2.2d.%d", sec/60, sec%60, tenths%10); + return buf; +} + +static long +progread(Chan *c, void *va, long n, vlong offset) +{ + int i; + Prog *p; + Osenv *o; + Mntwalk *mw; + ulong grpid; + char *a = va; + Progctl *ctl; + char mbuf[64], timebuf[12]; + char flag[10]; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, proggen); + + switch(QID(c->qid)){ + case Qdbgctl: + ctl = c->aux; + return qread(ctl->q, va, n); + case Qstatus: + acquire(); + p = progpid(PID(c->qid)); + if(p == nil || p->state == Pexiting || p->R.M == H) { + release(); + snprint(up->genbuf, sizeof(up->genbuf), "%8lud %8d %10s %s %10s %5dK %s", + PID(c->qid), + 0, + eve, + progtime(0, timebuf, timebuf+sizeof(timebuf)), + progstate[Pexiting], + 0, + "[$Sys]"); + return readstr(offset, va, n, up->genbuf); + } + modstatus(&p->R, mbuf, sizeof(mbuf)); + o = p->osenv; + snprint(up->genbuf, sizeof(up->genbuf), "%8d %8d %10s %s %10s %5dK %s", + p->pid, + p->group!=nil? p->group->id: 0, + o->user, + progtime(p->ticks, timebuf, timebuf+sizeof(timebuf)), + progstate[p->state], + progsize(p), + mbuf); + release(); + return readstr(offset, va, n, up->genbuf); + case Qwait: + return qread(c->aux, va, n); + case Qns: + acquire(); + if(waserror()){ + release(); + nexterror(); + } + p = progpid(PID(c->qid)); + if(p == nil) + error(Ethread); + mw = c->aux; + if(mw->cddone){ + poperror(); + release(); + return 0; + } + o = p->osenv; + mntscan(mw, o->pgrp); + if(mw->mh == 0) { + mw->cddone = 1; + i = snprint(a, n, "cd %s\n", o->pgrp->dot->name->s); + poperror(); + release(); + return i; + } + int2flag(mw->cm->mflag, flag); + if(strcmp(mw->cm->to->name->s, "#M") == 0){ + i = snprint(a, n, "mount %s %s %s %s\n", flag, + mw->cm->to->mchan->name->s, + mw->mh->from->name->s, mw->cm->spec? mw->cm->spec : ""); + }else + i = snprint(a, n, "bind %s %s %s\n", flag, + mw->cm->to->name->s, mw->mh->from->name->s); + poperror(); + release(); + return i; + case Qnsgrp: + acquire(); + p = progpid(PID(c->qid)); + if(p == nil) { + release(); + error(Ethread); + } + grpid = ((Osenv *)p->osenv)->pgrp->pgrpid; + release(); + return readnum(offset, va, n, grpid, NUMSIZE); + case Qpgrp: + acquire(); + p = progpid(PID(c->qid)); + if(p == nil) { + release(); + error(Ethread); + } + grpid = p->group!=nil? p->group->id: 0; + release(); + return readnum(offset, va, n, grpid, NUMSIZE); + case Qstack: + acquire(); + p = progpid(PID(c->qid)); + if(p == nil || p->state == Pexiting) { + release(); + error(Ethread); + } + if(p->state == Pready) { + release(); + error(Estopped); + } + n = progstack(&p->R, p->state, va, n, offset); + release(); + return n; + case Qheap: + acquire(); + if(waserror()){ + release(); + nexterror(); + } + n = progheap(c->aux, va, n, offset); + if(n == -1) + error(Emisalign); + poperror(); + release(); + return n; + case Qfd: + acquire(); + if(waserror()) { + release(); + nexterror(); + } + p = progpid(PID(c->qid)); + if(p == nil) + error(Ethread); + o = p->osenv; + n = progfds(o, va, n, offset); + poperror(); + release(); + return n; + case Qexception: + acquire(); + p = progpid(PID(c->qid)); + if(p == nil) { + release(); + error(Ethread); + } + if(p->exstr == nil) + up->genbuf[0] = 0; + else + snprint(up->genbuf, sizeof(up->genbuf), p->exstr); + release(); + return readstr(offset, va, n, up->genbuf); + } + error(Egreg); + return 0; +} + +static void +mntscan(Mntwalk *mw, Pgrp *pg) +{ + Mount *t; + Mhead *f; + int nxt, i; + ulong last, bestmid; + + rlock(&pg->ns); + + nxt = 0; + bestmid = ~0; + + last = 0; + if(mw->mh) + last = mw->cm->mountid; + + for(i = 0; i < MNTHASH; i++) { + for(f = pg->mnthash[i]; f; f = f->hash) { + for(t = f->mount; t; t = t->next) { + if(mw->mh == 0 || + (t->mountid > last && t->mountid < bestmid)) { + mw->cm = t; + mw->mh = f; + bestmid = mw->cm->mountid; + nxt = 1; + } + } + } + } + if(nxt == 0) + mw->mh = 0; + + runlock(&pg->ns); +} + +static long +progwrite(Chan *c, void *va, long n, vlong offset) +{ + Prog *p, *f; + Heapqry *hq; + char buf[512]; + Progctl *ctl; + char *b; + int i, pc; + Cmdbuf *cb; + Cmdtab *ct; + Osenv *o; + + USED(offset); + USED(va); + + if(c->qid.type & QTDIR) + error(Eisdir); + + acquire(); + if(waserror()) { + release(); + nexterror(); + } + p = progpid(PID(c->qid)); + if(p == nil) + error(Ethread); + + switch(QID(c->qid)){ + case Qctl: + cb = parsecmd(va, n); + if(waserror()){ + free(cb); + nexterror(); + } + ct = lookupcmd(cb, progcmd, nelem(progcmd)); + switch(ct->index){ + case CMkillgrp: + killgrp(p, "killed"); + break; + case CMkill: + killprog(p, "killed"); + break; + case CMrestricted: + p->flags |= Prestrict; + break; + case CMexceptions: + if(p->group->id != p->pid) + error(Eperm); + if(strcmp(cb->f[1], "propagate") == 0) + p->flags |= Ppropagate; + else if(strcmp(cb->f[1], "notifyleader") == 0) + p->flags |= Pnotifyleader; + else + error(Ebadctl); + break; + case CMprivate: + o = p->osenv; + o->pgrp->privatemem = 1; + break; + } + poperror(); + free(cb); + break; + case Qdbgctl: + cb = parsecmd(va, n); + if(waserror()){ + free(cb); + nexterror(); + } + if(cb->nf == 1 && strncmp(cb->f[0], "step", 4) == 0) + ct = progdbgcmd; + else + ct = lookupcmd(cb, progdbgcmd, nelem(progdbgcmd)); + switch(ct->index){ + case CDstep: + if(cb->nf == 1) + i = strtoul(cb->f[0]+4, nil, 0); + else + i = strtoul(cb->f[1], nil, 0); + dbgstep(c->aux, p, i); + break; + case CDtoret: + f = currun(); + i = calldepth(&p->R); + while(f->kill == nil) { + dbgstep(c->aux, p, 1024); + if(i > calldepth(&p->R)) + break; + } + break; + case CDcont: + f = currun(); + while(f->kill == nil) + dbgstep(c->aux, p, 1024); + break; + case CDstart: + dbgstart(p); + break; + case CDstop: + ctl = c->aux; + ctl->stop = 1; + break; + case CDunstop: + ctl = c->aux; + ctl->stop = 0; + break; + case CDbpt: + pc = strtoul(cb->f[3], nil, 10); + ctl = c->aux; + if(strcmp(cb->f[1], "set") == 0) + ctl->bpts = setbpt(ctl->bpts, cb->f[2], pc); + else if(strcmp(cb->f[1], "del") == 0) + ctl->bpts = delbpt(ctl->bpts, cb->f[2], pc); + else + error(Ebadctl); + break; + case CDmaim: + p->kill = "maim"; + break; + } + poperror(); + free(cb); + break; + case Qheap: + /* + * Heap query: + * addr.Fn + * pc+module.In + */ + i = n; + if(i > sizeof(buf)-1) + i = sizeof(buf)-1; + memmove(buf, va, i); + buf[i] = '\0'; + hq = c->aux; + hq->addr = strtoul(buf, &b, 0); + if(*b == '+') + hq->module = strtoul(b, &b, 0); + if(*b++ != '.') + error(Ebadctl); + hq->fmt = *b++; + hq->count = strtoul(b, nil, 0); + break; + default: + print("unknown qid in procwrite\n"); + error(Egreg); + } + poperror(); + release(); + return n; +} + +static Bpt* +setbpt(Bpt *bpts, char *path, int pc) +{ + int n; + Bpt *b; + + n = strlen(path); + b = mallocz(sizeof *b + n, 0); + if(b == nil) + return bpts; + b->pc = pc; + memmove(b->path, path, n+1); + b->file = b->path; + path = strrchr(b->path, '/'); + if(path != nil) + b->file = path + 1; + b->next = bpts; + return b; +} + +static Bpt* +delbpt(Bpt *bpts, char *path, int pc) +{ + Bpt *b, **last; + + last = &bpts; + for(b = bpts; b != nil; b = b->next){ + if(b->pc == pc && strcmp(b->path, path) == 0) { + *last = b->next; + free(b); + break; + } + last = &b->next; + } + return bpts; +} + +static void +freebpts(Bpt *b) +{ + Bpt *next; + + for(; b != nil; b = next){ + next = b->next; + free(b); + } +} + +static void +telldbg(Progctl *ctl, char *msg) +{ + kstrcpy(ctl->msg, msg, ERRMAX); + ctl->debugger = nil; + Wakeup(&ctl->r); +} + +static void +dbgstart(Prog *p) +{ + Osenv *o; + Progctl *ctl; + + o = p->osenv; + ctl = o->debug; + if(ctl != nil && ctl->debugger != nil) + error("prog debugged"); + if(p->state == Pdebug) + addrun(p); + o->debug = nil; + p->xec = xec; +} + +static int +xecdone(void *vc) +{ + Progctl *ctl = vc; + + return ctl->debugger == nil; +} + +static void +dbgstep(Progctl *vctl, Prog *p, int n) +{ + Osenv * volatile o; + Progctl * volatile ctl; + char buf[ERRMAX+20], *msg; + + if(p == currun()) + error("cannot step yourself"); + + if(p->state == Pbroken) + error("prog broken"); + + ctl = vctl; + if(ctl->debugger != nil) + error("prog already debugged"); + + o = p->osenv; + if(o->debug == nil) { + o->debug = ctl; + p->xec = dbgxec; + }else if(o->debug != ctl) + error("prog already debugged"); + + ctl->step = n; + if(p->state == Pdebug) + addrun(p); + ctl->debugger = up; + strcpy(buf, "child: "); + msg = buf+7; + ctl->msg = msg; + + /* + * wait for reply from dbgxec; release is okay because prog is now + * debugged, cannot exit. + */ + if(waserror()){ + acquire(); + ctl->debugger = nil; + ctl->msg = nil; + o->debug = nil; + p->xec = xec; + nexterror(); + } + release(); + Sleep(&ctl->r, xecdone, ctl); + poperror(); + acquire(); + if(msg[0] != '\0') + error(buf); +} + +void +dbgexit(Prog *kid, int broken, char *estr) +{ + int n; + Osenv *e; + Progctl *ctl; + char buf[ERRMAX+20]; + + e = kid->osenv; + ctl = e->debug; + e->debug = nil; + kid->xec = xec; + + if(broken) + n = snprint(buf, sizeof(buf), "broken: %s", estr); + else + n = snprint(buf, sizeof(buf), "exited"); + if(ctl->debugger) + telldbg(ctl, buf); + qproduce(ctl->q, buf, n); +} + +static void +dbgaddrun(Prog *p) +{ + Osenv *o; + + p->state = Pdebug; + p->addrun = nil; + o = p->osenv; + if(o->rend != nil) + Wakeup(o->rend); + o->rend = nil; +} + +static int +bdone(void *vp) +{ + Prog *p = vp; + + return p->addrun == nil; +} + +static void +dbgblock(Prog *p) +{ + Osenv *o; + Progctl *ctl; + + o = p->osenv; + ctl = o->debug; + qproduce(ctl->q, progstate[p->state], strlen(progstate[p->state])); + pushrun(p); + p->addrun = dbgaddrun; + o->rend = &up->sleep; + + /* + * bdone(p) is safe after release because p is being debugged, + * so cannot exit. + */ + if(waserror()){ + acquire(); + nexterror(); + } + release(); + if(o->rend != nil) + Sleep(o->rend, bdone, p); + poperror(); + acquire(); + if(p->kill != nil) + error(p->kill); + ctl = o->debug; + if(ctl != nil) + qproduce(ctl->q, "run", 3); +} + +void +dbgxec(Prog *p) +{ + Bpt *b; + Prog *kid; + Osenv *env; + Progctl *ctl; + int op, pc, n; + char buf[ERRMAX+10]; + extern void (*dec[])(void); + + env = p->osenv; + ctl = env->debug; + ctl->ref++; + if(waserror()){ + closedbgctl(ctl, p); + nexterror(); + } + + R = p->R; + R.MP = R.M->MP; + + R.IC = p->quanta; + if((ulong)R.IC > ctl->step) + R.IC = ctl->step; + if(ctl->stop) + R.IC = 0; + + + buf[0] = '\0'; + + if(R.IC != 0 && R.M->compiled) { + comvec(); + if(p != currun()) + dbgblock(p); + goto save; + } + + while(R.IC != 0) { + if(0) + print("step: %lux: %s %4ld %D\n", + (ulong)p, R.M->m->name, R.PC-R.M->prog, R.PC); + + dec[R.PC->add](); + op = R.PC->op; + R.PC++; + optab[op](); + + /* + * check notification about new progs + */ + if(op == ISPAWN || op == IMSPAWN) { + /* pick up the kid from the end of the run queue */ + kid = delruntail(Pdebug); + n = snprint(buf, sizeof buf, "new %d", kid->pid); + qproduce(ctl->q, buf, n); + buf[0] = '\0'; + } + if(op == ILOAD) { + n = snprint(buf, sizeof buf, "load %s", string2c(*(String**)R.s)); + qproduce(ctl->q, buf, n); + buf[0] = '\0'; + } + + /* + * check for returns at big steps + */ + if(op == IRET) + R.IC = 1; + + /* + * check for blocked progs + */ + if(R.IC == 1 && p != currun()) + dbgblock(p); + if(ctl->stop) + R.IC = 1; + R.IC--; + + if(ctl->bpts == nil) + continue; + + pc = R.PC - R.M->prog; + for(b = ctl->bpts; b != nil; b = b->next) { + if(pc == b->pc && + (strcmp(R.M->m->path, b->path) == 0 || + strcmp(R.M->m->path, b->file) == 0)) { + strcpy(buf, "breakpoint"); + goto save; + } + } + } +save: + if(ctl->stop) + strcpy(buf, "stopped"); + + p->R = R; + + if(env->debug == nil) { + poperror(); + return; + } + + if(p == currun()) + delrun(Pdebug); + + telldbg(env->debug, buf); + poperror(); + closedbgctl(env->debug, p); +} + +Dev progdevtab = { + 'p', + "prog", + + devinit, + progattach, + progwalk, + progstat, + progopen, + devcreate, + progclose, + progread, + devbread, + progwrite, + devbwrite, + devremove, + progwstat +}; diff --git a/emu/port/devroot.c b/emu/port/devroot.c new file mode 100644 index 00000000..695d151c --- /dev/null +++ b/emu/port/devroot.c @@ -0,0 +1,150 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + +extern Rootdata rootdata[]; +extern Dirtab roottab[]; +extern int rootmaxq; + +static Chan* +rootattach(char *spec) +{ + int i; + ulong len; + Rootdata *r; + + if(*spec) + error(Ebadspec); + for (i = 0; i < rootmaxq; i++){ + r = &rootdata[i]; + if (r->sizep){ + len = *r->sizep; + r->size = len; + roottab[i].length = len; + } + } + return devattach('/', spec); +} + +static int +rootgen(Chan *c, char *name, Dirtab *tab, int nd, int s, Dir *dp) +{ + int p, i; + Rootdata *r; + + if(s == DEVDOTDOT){ + p = rootdata[c->qid.path].dotdot; + c->qid.path = p; + c->qid.type = QTDIR; + name = "#/"; + if(p != 0){ + for(i = 0; i < rootmaxq; i++) + if(roottab[i].qid.path == c->qid.path){ + name = roottab[i].name; + break; + } + } + devdir(c, c->qid, name, 0, eve, 0555, dp); + return 1; + } + if(name != nil){ + isdir(c); + r = &rootdata[(int)c->qid.path]; + tab = r->ptr; + for(i=0; i<r->size; i++, tab++) + if(strcmp(tab->name, name) == 0){ + devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp); + return 1; + } + return -1; + } + if(s >= nd) + return -1; + tab += s; + devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp); + return 1; +} + +static Walkqid* +rootwalk(Chan *c, Chan *nc, char **name, int nname) +{ + ulong p; + + p = c->qid.path; + if(nname == 0) + p = rootdata[p].dotdot; + return devwalk(c, nc, name, nname, rootdata[p].ptr, rootdata[p].size, rootgen); +} + +static int +rootstat(Chan *c, uchar *dp, int n) +{ + int p; + + p = rootdata[c->qid.path].dotdot; + return devstat(c, dp, n, rootdata[p].ptr, rootdata[p].size, rootgen); +} + +static Chan* +rootopen(Chan *c, int omode) +{ + int p; + + p = rootdata[c->qid.path].dotdot; + return devopen(c, omode, rootdata[p].ptr, rootdata[p].size, rootgen); +} + +/* + * sysremove() knows this is a nop + */ +static void +rootclose(Chan *c) +{ + USED(c); +} + +static long +rootread(Chan *c, void *buf, long n, vlong offset) +{ + ulong p, len; + uchar *data; + + p = c->qid.path; + if(c->qid.type & QTDIR) + return devdirread(c, buf, n, rootdata[p].ptr, rootdata[p].size, rootgen); + len = rootdata[p].size; + if(offset < 0 || offset >= len) + return 0; + if(offset+n > len) + n = len - offset; + data = rootdata[p].ptr; + memmove(buf, data+offset, n); + return n; +} + +static long +rootwrite(Chan *c, void *a, long n, vlong off) +{ + USED(c); USED(a); USED(n); USED(off); + error(Eperm); + return 0; +} + +Dev rootdevtab = { + '/', + "root", + + devinit, + rootattach, + rootwalk, + rootstat, + rootopen, + devcreate, + rootclose, + rootread, + devbread, + rootwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/emu/port/devsign.c b/emu/port/devsign.c new file mode 100644 index 00000000..ba44e286 --- /dev/null +++ b/emu/port/devsign.c @@ -0,0 +1,440 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "interp.h" +#include <isa.h> +#include "runt.h" +#include "mp.h" +#include "libsec.h" +#include "../../libkeyring/keys.h" + +/* + * experimental version of signed modules + */ + +enum +{ + Qdir, + Qkey, + Qctl, + + Maxkey = 2048 +}; + +static Dirtab signdir[] = +{ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "signerkey", {Qkey}, 0, 0644, + "signerctl", {Qctl}, 0, 0600, +}; + +typedef struct Get Get; +struct Get { + uchar* p; + uchar* ep; +}; + +#define G32(b) ((b[0]<<24)|(b[1]<<16)|(b[2]<<8)|b[3]) + +static int vc(Get*); +static int vs(void*, int, Get*, int); +static Signerkey* findsignerkey(Skeyset*, char*, int, char*); +extern vlong osusectime(void); + +int +verifysigner(uchar *sign, int len, uchar *data, ulong ndata) +{ + Get sig; + int alg; + ulong issued, expires, now; + int footprint, r, n; + uchar buf[128], digest[SHA1dlen]; + DigestState *ds; + volatile struct {mpint* b;} b; + volatile struct {mpint* s;} s; + SigAlgVec *sa; + Signerkey *key; + Skeyset *sigs; + + /* alg[1] issued[4] expires[4] footprint[2] signer[n] sig[m] */ + sigs = up->env->sigs; + if(sigs == nil) + return 1; /* not enforcing signed modules */ + sig.p = sign; + sig.ep = sign+len; + alg = vc(&sig); + if(alg != 2) + return 0; /* we do only SHA1/RSA */ + sa = findsigalg("rsa"); + if(sa == nil) + return 0; + if(vs(buf, sizeof(buf), &sig, 4) < 0) + return 0; + now = osusectime()/1000000; + issued = G32(buf); + if(vs(buf, sizeof(buf), &sig, 4) < 0) + return 0; + if(issued != 0 && now < issued) + return 0; + expires = G32(buf); + if(expires != 0 && now >= expires) + return 0; + footprint = vc(&sig) << 8; + footprint |= vc(&sig); + if(footprint < 0) + return 0; + r = 0; + b.b = nil; + s.s = nil; + qlock(&sigs->l); + if(waserror()) + goto out; + if((n = vs(buf, sizeof(buf)-NUMSIZE-1, &sig, -1)) < 0) /* owner */ + goto out; + buf[n] = 0; + key = findsignerkey(sigs, sa->name, footprint, (char*)buf); + if(key == nil) + goto out; + n += snprint((char*)buf+n, NUMSIZE, " %lud", expires); + ds = sha1(buf, n, nil, nil); + sha1(data, ndata, digest, ds); + b.b = betomp(digest, SHA1dlen, nil); + if(b.b == nil) + goto out; + s.s = betomp(sig.p, sig.ep-sig.p, nil); + if(s.s == nil) + goto out; + r = (*sa->verify)(b.b, s.s, key->pk); +out: + qunlock(&sigs->l); + if(b.b != nil) + mpfree(b.b); + if(s.s != nil) + mpfree(s.s); + return r; +} + +int +mustbesigned(char *path, uchar *code, ulong length, Dir *dir) +{ + USED(code); USED(length); +if(0)print("load %s: %d %C\n", path, up->env->sigs!=nil, dir==nil?'?':dir->type); + /* allow only signed modules and those in #/; already loaded modules are reloaded from cache */ + return up->env->sigs != nil && (dir == nil || dir->type != '/'); +} + +static int +vc(Get *g) +{ + return g->p < g->ep? *g->p++: -1; +} + +static int +vs(void *s, int lim, Get *g, int n) +{ + int nr; + + if(n < 0){ + if(g->p >= g->ep) + return -1; + n = *g->p++; + lim--; + } + if(n > lim) + return -1; + nr = g->ep - g->p; + if(n > nr) + return -1; + if(s != nil) + memmove(s, g->p, n); + g->p += n; + return n; +} + +static char* +cstring(char *str, char **strp) +{ + char *p, *s; + int n; + + p = strchr(str, '\n'); + if(p == 0) + p = str + strlen(str); + n = p - str; + s = malloc(n+1); + if(s == nil) + return nil; + memmove(s, str, n); + s[n] = 0; + + if(strp){ + if(*p) + p++; + *strp = p; + } + + return s; +} + +static SigAlgVec* +cstrtoalg(char *str, char **strp) +{ + int n; + char *p, name[KNAMELEN]; + + p = strchr(str, '\n'); + if(p == 0){ + p = str + strlen(str); + if(strp) + *strp = p; + } else { + if(strp) + *strp = p+1; + } + + n = p - str; + if(n >= sizeof(name)) + return nil; + strncpy(name, str, n); + name[n] = 0; + return findsigalg(name); +} + +static Signerkey* +strtopk(char *buf) +{ + SigAlgVec *sa; + char *p; + Signerkey *key; + + key = malloc(sizeof(*key)); + if(key == nil) + return nil; + key->r.ref = 1; + sa = cstrtoalg(buf, &p); + if(sa == nil){ + free(key); + return nil; + } + key->alg = sa; + key->pkfree = sa->pkfree; + key->owner = cstring(p, &p); + if(key->owner == nil){ + free(key); + return nil; + } + key->pk = (*sa->str2pk)(p, &p); + if(key->pk == nil){ + free(key->owner); + free(key); + return nil; + } + return key; +} + +static Signerkey* +findsignerkey(Skeyset *sigs, char *alg, int footprint, char *owner) +{ + int i; + Signerkey *key; + + for(i=0; i<sigs->nkey; i++){ + key = sigs->keys[i]; + if(key->footprint == footprint && + strcmp(alg, ((SigAlgVec*)key->alg)->name) == 0 && + strcmp(key->owner, owner) == 0) + return key; + } + return nil; +} + +static Chan* +signattach(char *spec) +{ + return devattach(0x03A3, spec); /* L'Σ' */ +} + +static Walkqid* +signwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, signdir, nelem(signdir), devgen); +} + +static int +signstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, signdir, nelem(signdir), devgen); +} + +static Chan* +signopen(Chan *c, int omode) +{ + if(c->qid.type & QTDIR) { + if(omode != OREAD) + error(Eisdir); + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; + } + + switch((ulong)c->qid.path){ + case Qctl: + if(!iseve()) + error(Eperm); + break; + + case Qkey: + if(omode != OREAD && !iseve()) + error(Eperm); + break; + } + + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static void +signclose(Chan *c) +{ + USED(c); +} + +static long +signread(Chan *c, void *va, long n, vlong offset) +{ + char *buf, *p; + SigAlgVec *sa; + Skeyset *sigs; + Signerkey *key; + int i; + + if(c->qid.type & QTDIR) + return devdirread(c, va, n, signdir, nelem(signdir), devgen); + sigs = up->env->sigs; + if(sigs == nil) + return 0; + switch((ulong)c->qid.path){ + case Qkey: + buf = smalloc(Maxkey); + if(waserror()){ + free(buf); + nexterror(); + } + qlock(&sigs->l); + if(waserror()){ + qunlock(&sigs->l); + nexterror(); + } + p = buf; + for(i=0; i<sigs->nkey; i++){ + key = sigs->keys[i]; + sa = key->alg; + p = seprint(p, buf+Maxkey, "owner=%s alg=%s footprint=%ud expires=%lud\n", + key->owner, sa->name, key->footprint, key->expires); + } + poperror(); + qunlock(&sigs->l); + n = readstr(offset, va, n, buf); + poperror(); + free(buf); + return n; + + case Qctl: + return readnum(offset, va, n, sigs->nkey, NUMSIZE); + } + return 0; +} + +static long +signwrite(Chan *c, void *va, long n, vlong offset) +{ + char *buf; + Skeyset *sigs; + Signerkey *okey, *key; + int i; + + if(c->qid.type & QTDIR) + error(Eisdir); + USED(offset); + switch((ulong)c->qid.path){ + case Qkey: + if(n >= Maxkey) + error(Etoobig); + buf = smalloc(Maxkey); + if(waserror()){ + free(buf); + nexterror(); + } + memmove(buf, va, n); + buf[n] = 0; + + key = strtopk(buf); + if(key == nil) + error("bad key syntax"); + poperror(); + free(buf); + + if(waserror()){ + freeskey(key); + nexterror(); + } + sigs = up->env->sigs; + if(sigs == nil){ + sigs = malloc(sizeof(*sigs)); + if(sigs == nil) + error(Enomem); + sigs->r.ref = 1; + up->env->sigs = sigs; + } + qlock(&sigs->l); + if(waserror()){ + qunlock(&sigs->l); + nexterror(); + } + for(i=0; i<sigs->nkey; i++){ + okey = sigs->keys[i]; + if(strcmp(okey->owner, key->owner) == 0){ + /* replace existing key */ + sigs->keys[i] = key; + freeskey(okey); + break; + } + } + if(i >= sigs->nkey){ + if(sigs->nkey >= nelem(sigs->keys)) + error("too many keys"); + sigs->keys[sigs->nkey++] = key; + } + poperror(); + qunlock(&sigs->l); + poperror(); /* key */ + + return n; + case Qctl: + error(Ebadctl); + break; + } + return 0; +} + +Dev signdevtab = { + 0x03A3, /* L'Σ', /* U+03A3 */ + "sign", + + devinit, + signattach, + signwalk, + signstat, + signopen, + devcreate, + signclose, + signread, + devbread, + signwrite, + devbwrite, + devremove, + devwstat +}; diff --git a/emu/port/devsnarf.c b/emu/port/devsnarf.c new file mode 100644 index 00000000..4778c130 --- /dev/null +++ b/emu/port/devsnarf.c @@ -0,0 +1,161 @@ +/* + * host's snarf buffer + */ + +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +enum{ + Qdir, + Qsnarf, + + Maxsnarf= 100*1024 +}; + +static +Dirtab snarftab[]={ + ".", {Qdir, 0, QTDIR}, 0, 0555, + "snarf", {Qsnarf}, 0, 0666, +}; + +static QLock snarflock; /* easiest to synchronise all access */ + +static Chan* +snarfattach(char *spec) +{ + return devattach('^', spec); +} + +static Walkqid* +snarfwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, snarftab, nelem(snarftab), devgen); +} + +static int +snarfstat(Chan* c, uchar *db, int n) +{ + return devstat(c, db, n, snarftab, nelem(snarftab), devgen); +} + +static Chan* +snarfopen(Chan* c, int omode) +{ + c = devopen(c, omode, snarftab, nelem(snarftab), devgen); + if(c->qid.path == Qsnarf){ + if(c->mode == ORDWR || c->mode == OWRITE){ + qlock(&snarflock); + free(c->aux); + c->aux = nil; + qunlock(&snarflock); + } + } + return c; +} + +static void +snarfclose(Chan* c) +{ + if((c->flag & COPEN) == 0) + return; + if(c->qid.path == Qsnarf){ + /* this must be the last reference: no need to lock */ + if(c->mode == ORDWR || c->mode == OWRITE){ + if(!waserror()){ + clipwrite(c->aux); + poperror(); + } + } + free(c->aux); + } +} + +static long +snarfread(Chan* c, void* a, long n, vlong offset) +{ + void *p; + + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, a, n, snarftab, nelem(snarftab), devgen); + case Qsnarf: + qlock(&snarflock); + if(waserror()){ + qunlock(&snarflock); + nexterror(); + } + if(offset == 0){ + p = c->aux; + c->aux = nil; + free(p); + c->aux = clipread(); + } + if(c->aux != nil) + n = readstr(offset, a, n, c->aux); + else + n = 0; + poperror(); + qunlock(&snarflock); + break; + default: + n=0; + break; + } + return n; +} + +static long +snarfwrite(Chan* c, void* va, long n, vlong offset) +{ + ulong l; + char *p; + + switch((ulong)c->qid.path){ + case Qsnarf: + /* append only */ + USED(offset); /* not */ + qlock(&snarflock); + if(waserror()){ + qunlock(&snarflock); + nexterror(); + } + if(c->aux != nil) + l = strlen(c->aux); + else + l = 0; + if(l+n > Maxsnarf) + error(Etoobig); + c->aux = realloc(c->aux, l+n+1); + if((p = c->aux) == nil) + error(Enovmem); + memmove(p+l, va, n); + p[l+n] = 0; + snarftab[1].qid.vers++; + poperror(); + qunlock(&snarflock); + break; + default: + error(Ebadusefd); + } + return n; +} + +Dev snarfdevtab = { + '^', + "snarf", + + devinit, + snarfattach, + snarfwalk, + snarfstat, + snarfopen, + devcreate, + snarfclose, + snarfread, + devbread, + snarfwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/emu/port/devsrv.c b/emu/port/devsrv.c new file mode 100644 index 00000000..f655bf96 --- /dev/null +++ b/emu/port/devsrv.c @@ -0,0 +1,725 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "interp.h" +#include <isa.h> +#include "runt.h" + +typedef struct SrvFile SrvFile; +struct SrvFile +{ + char* spec; + char* name; + char* user; + ulong perm; + vlong length; + Qid qid; + int ref; + int opens; + int flags; + Channel* read; + Channel* write; + SrvFile* entry; + SrvFile* dir; + SrvFile* devlist; +}; + +enum +{ + SORCLOSE = (1<<0), + SRDCLOSE = (1<<1), + SWRCLOSE = (1<<2), + SREMOVED = (1<<3), +}; + +typedef struct SrvDev SrvDev; +struct SrvDev +{ + Type* Rread; + Type* Rwrite; + QLock l; + ulong pathgen; + SrvFile* devices; +}; + +static SrvDev dev; + +void freechan(Heap*, int); +static void freerdchan(Heap*, int); +static void freewrchan(Heap*, int); + +Type *Trdchan; +Type *Twrchan; + +static int +srvgen(Chan *c, char *name, Dirtab *tab, int ntab, int s, Dir *dp) +{ + SrvFile *f; + + USED(name); + USED(tab); + USED(ntab); + + if(s == DEVDOTDOT){ + devdir(c, c->qid, "#s", 0, eve, 0555, dp); + return 1; + } + f = c->aux; + if((c->qid.type & QTDIR) == 0){ + if(s > 0) + return -1; + devdir(c, f->qid, f->name, f->length, f->user, f->perm, dp); + return 1; + } + + for(f = f->entry; f != nil; f = f->entry){ + if(s-- == 0) + break; + } + if(f == nil) + return -1; + + devdir(c, f->qid, f->name, f->length, f->user, f->perm, dp); + return 1; +} + +static void +srvinit(void) +{ + static uchar rmap[] = Sys_Rread_map; + static uchar wmap[] = Sys_Rwrite_map; + + Trdchan = dtype(freerdchan, sizeof(Channel), Tchannel.map, Tchannel.np); + Twrchan = dtype(freewrchan, sizeof(Channel), Tchannel.map, Tchannel.np); + + dev.pathgen = 1; + dev.Rread = dtype(freeheap, Sys_Rread_size, rmap, sizeof(rmap)); + dev.Rwrite = dtype(freeheap, Sys_Rwrite_size, wmap, sizeof(wmap)); +} + +static int +srvchkattach(SrvFile *d) +{ + if(strcmp(d->user, up->env->user) == 0) + return 1; + + /* + * Need write permission in other to allow attaches if + * we are not the owner + */ + if(d->perm & 2) + return 1; + + return 0; +} + +static Chan* +srvattach(char *spec) +{ + Chan *c; + SrvFile *d; + + if(spec[0] != '\0'){ + qlock(&dev.l); + for(d = dev.devices; d != nil; d = d->devlist){ + if(strcmp(spec, d->spec) == 0){ + if(srvchkattach(d) == 0){ + qunlock(&dev.l); + error(Eperm); + } + d->ref++; + break; + } + } + qunlock(&dev.l); + + if(d != nil){ + c = devattach('s', spec); + c->aux = d; + c->qid = d->qid; + return c; + } + } + + d = malloc(sizeof(SrvFile)); + if(d == nil) + error(Enomem); + + c = devattach('s', spec); + + d->ref = 1; + kstrdup(&d->spec, spec); + kstrdup(&d->user, up->env->user); + snprint(up->genbuf, sizeof(up->genbuf), "srv%ld", up->env->pgrp->pgrpid); + kstrdup(&d->name, up->genbuf); + d->perm = DMDIR|0770; + + qlock(&dev.l); + mkqid(&d->qid, dev.pathgen++, 0, QTDIR); + d->devlist = dev.devices; + dev.devices = d; + qunlock(&dev.l); + + c->aux = d; + c->qid = d->qid; + + return c; +} + +static Walkqid* +srvwalk(Chan *c, Chan *nc, char **name, int nname) +{ + SrvFile *d, *pd; + Walkqid *w; + + pd = c->aux; + qlock(&dev.l); + if(waserror()){ + qunlock(&dev.l); + nexterror(); + } + + w = devwalk(c, nc, name, nname, nil, 0, srvgen); + if(w != nil && w->clone != nil){ + if(nname != 0){ + for(d = pd->entry; d != nil; d = d->entry) + if(d->qid.path == w->clone->qid.path) + break; + if(d == nil) + panic("srvwalk"); + if(w->clone == c) + pd->ref--; + }else + d = pd; + w->clone->aux = d; + d->ref++; + } + poperror(); + qunlock(&dev.l); + return w; +} + +static int +srvstat(Chan *c, uchar *db, int n) +{ + qlock(&dev.l); + if(waserror()){ + qunlock(&dev.l); + nexterror(); + } + n = devstat(c, db, n, 0, 0, srvgen); + poperror(); + qunlock(&dev.l); + return n; +} + +static Chan* +srvopen(Chan *c, int omode) +{ + SrvFile *sf; + + openmode(omode); /* check it */ + if(c->qid.type & QTDIR){ + if(omode != OREAD) + error(Eisdir); + c->mode = omode; + c->flag |= COPEN; + c->offset = 0; + return c; + } + + sf = c->aux; + + qlock(&dev.l); + if(waserror()){ + qunlock(&dev.l); + nexterror(); + } + devpermcheck(sf->user, sf->perm, omode); + if(omode&ORCLOSE && strcmp(sf->user, up->env->user) != 0) + error(Eperm); + if(sf->perm & DMEXCL && sf->opens != 0) + error(Einuse); + sf->opens++; + if(omode&ORCLOSE) + sf->flags |= SORCLOSE; + poperror(); + qunlock(&dev.l); + + c->offset = 0; + c->flag |= COPEN; + c->mode = openmode(omode); + + return c; +} + +static int +srvwstat(Chan *c, uchar *dp, int n) +{ + Dir *d; + SrvFile *sf, *f; + + sf = c->aux; + if(strcmp(up->env->user, sf->user) != 0) + error(Eperm); + + d = smalloc(sizeof(*d)+n); + if(waserror()){ + free(d); + nexterror(); + } + n = convM2D(dp, n, d, (char*)&d[1]); + if(n == 0) + error(Eshortstat); + if(!emptystr(d->name)){ + if(sf->dir == nil) + error(Eperm); + validwstatname(d->name); + qlock(&dev.l); + for(f = sf->dir; f != nil; f = f->entry) + if(strcmp(f->name, d->name) == 0){ + qunlock(&dev.l); + error(Eexist); + } + kstrdup(&sf->name, d->name); + qunlock(&dev.l); + } + if(d->mode != ~0UL) + sf->perm = d->mode & (DMEXCL|DMAPPEND|0777); + if(d->length != (vlong)-1) + sf->length = d->length; + poperror(); + free(d); + return n; +} + +static void +srvputdir(SrvFile *sf) +{ + SrvFile **l, *d; + + sf->ref--; + if(sf->ref != 0) + return; + + for(l = &dev.devices; (d = *l) != nil; l = &d->devlist) + if(d == sf){ + *l = d->devlist; + break; + } + free(sf->spec); + free(sf->user); + free(sf->name); + free(sf); +} + +static void +srvunblock(SrvFile *sf, int fid) +{ + Channel *d; + Sys_FileIO_read rreq; + Sys_FileIO_write wreq; + + acquire(); + if(waserror()){ + release(); + nexterror(); + } + d = sf->read; + if(d != H){ + rreq.t0 = 0; + rreq.t1 = 0; + rreq.t2 = fid; + rreq.t3 = H; + csendalt(d, &rreq, d->mid.t, -1); + } + + d = sf->write; + if(d != H){ + wreq.t0 = 0; + wreq.t1 = H; + wreq.t2 = fid; + wreq.t3 = H; + csendalt(d, &wreq, d->mid.t, -1); + } + poperror(); + release(); +} + +static void +srvdecr(SrvFile *sf, int remove) +{ + SrvFile *f, **l; + + if(remove){ + l = &sf->dir->entry; + for(f = *l; f != nil; f = f->entry){ + if(sf == f){ + *l = f->entry; + break; + } + l = &f->entry; + } + sf->ref--; + sf->flags |= SREMOVED; + } + + if(sf->ref != 0) + return; + + if(sf->dir != nil) + srvputdir(sf->dir); + + free(sf->spec); + free(sf->user); + free(sf->name); + free(sf); +} + +static void +srvfree(SrvFile *sf, int flag) +{ + sf->flags |= flag; + if((sf->flags & (SRDCLOSE | SWRCLOSE)) == (SRDCLOSE | SWRCLOSE)){ + sf->ref--; + srvdecr(sf, (sf->flags & SREMOVED) == 0); + } +} + +static void +freerdchan(Heap *h, int swept) +{ + SrvFile *sf; + + release(); + qlock(&dev.l); + sf = H2D(Channel*, h)->aux; + sf->read = H; + srvfree(sf, SRDCLOSE); + qunlock(&dev.l); + acquire(); + freechan(h, swept); +} + +static void +freewrchan(Heap *h, int swept) +{ + SrvFile *sf; + + release(); + qlock(&dev.l); + sf = H2D(Channel*, h)->aux; + sf->write = H; + srvfree(sf, SWRCLOSE); + qunlock(&dev.l); + acquire(); + freechan(h, swept); +} + +static void +srvclunk(Chan *c, int remove) +{ + int opens, noperm; + SrvFile *sf; + + sf = c->aux; + qlock(&dev.l); + if(c->qid.type & QTDIR){ + srvputdir(sf); + qunlock(&dev.l); + if(remove) + error(Eperm); + return; + } + + opens = 0; + if(c->flag & COPEN){ + opens = sf->opens--; + if (sf->read != H || sf->write != H) + srvunblock(sf, c->fid); + } + + sf->ref--; + if(opens == 1){ + if((sf->flags & (SORCLOSE | SREMOVED)) == SORCLOSE) + remove = 1; + } + + noperm = 0; + if(remove && strcmp(sf->dir->user, up->env->user) != 0){ + noperm = 1; + remove = 0; + } + + srvdecr(sf, remove); + qunlock(&dev.l); + + if(noperm) + error(Eperm); +} + +static void +srvclose(Chan *c) +{ + srvclunk(c, 0); +} + +static void +srvremove(Chan *c) +{ + srvclunk(c, 1); +} + +static long +srvread(Chan *c, void *va, long count, vlong offset) +{ + int l; + Heap * volatile h; + Array *a; + SrvFile *sp; + Channel *rc; + Channel *rd; + Sys_Rread * volatile r; + Sys_FileIO_read req; + + if(c->qid.type & QTDIR){ + qlock(&dev.l); + if(waserror()){ + qunlock(&dev.l); + nexterror(); + } + l = devdirread(c, va, count, 0, 0, srvgen); + poperror(); + qunlock(&dev.l); + return l; + } + + sp = c->aux; + + acquire(); + if(waserror()){ + release(); + nexterror(); + } + + rd = sp->read; + if(rd == H) + error(Eshutdown); + + rc = cnewc(dev.Rread, movtmp, 1); + ptradd(D2H(rc)); + if(waserror()){ + ptrdel(D2H(rc)); + destroy(rc); + nexterror(); + } + + req.t0 = offset; + req.t1 = count; + req.t2 = c->fid; + req.t3 = rc; + csend(rd, &req); + + h = heap(dev.Rread); + r = H2D(Sys_Rread *, h); + ptradd(h); + if(waserror()){ + ptrdel(h); + destroy(r); + nexterror(); + } + + crecv(rc, r); + if(r->t1 != H) + error(string2c(r->t1)); + + a = r->t0; + l = 0; + if(a != H){ + l = a->len; + if(l > count) + l = count; + memmove(va, a->data, l); + } + + poperror(); + ptrdel(h); + destroy(r); + + poperror(); + ptrdel(D2H(rc)); + destroy(rc); + + poperror(); + release(); + + return l; +} + +static long +srvwrite(Chan *c, void *va, long count, vlong offset) +{ + long l; + Heap * volatile h; + SrvFile *sp; + Channel *wc; + Channel *wr; + Sys_Rwrite * volatile w; + Sys_FileIO_write req; + + if(c->qid.type & QTDIR) + error(Eperm); + + acquire(); + if(waserror()){ + release(); + nexterror(); + } + + sp = c->aux; + wr = sp->write; + if(wr == H) + error(Eshutdown); + + wc = cnewc(dev.Rwrite, movtmp, 1); + ptradd(D2H(wc)); + if(waserror()){ + ptrdel(D2H(wc)); + destroy(wc); + nexterror(); + } + + req.t0 = offset; + req.t1 = mem2array(va, count); + req.t2 = c->fid; + req.t3 = wc; + + ptradd(D2H(req.t1)); + if(waserror()){ + ptrdel(D2H(req.t1)); + destroy(req.t1); + nexterror(); + } + + csend(wr, &req); + + poperror(); + ptrdel(D2H(req.t1)); + destroy(req.t1); + + h = heap(dev.Rwrite); + w = H2D(Sys_Rwrite *, h); + ptradd(h); + if(waserror()){ + ptrdel(h); + destroy(w); + nexterror(); + } + crecv(wc, w); + if(w->t1 != H) + error(string2c(w->t1)); + poperror(); + ptrdel(h); + l = w->t0; + destroy(w); + + poperror(); + ptrdel(D2H(wc)); + destroy(wc); + + poperror(); + release(); + if(l < 0) + l = 0; + return l; +} + +static void +srvretype(Channel *c, SrvFile *f, Type *t) +{ + Heap *h; + + h = D2H(c); + h->t->ref--; + h->t = t; + t->ref++; + c->aux = f; +} + +int +srvf2c(char *dir, char *file, Sys_FileIO *io) +{ + SrvFile *s, *f; + volatile struct { Chan *c; } c; + + c.c = nil; + if(waserror()){ + cclose(c.c); + return -1; + } + + if(strchr(file, '/') != nil || strlen(file) >= 64 || strcmp(file, ".") == 0 || strcmp(file, "..") == 0) + error(Efilename); + + c.c = namec(dir, Aaccess, 0, 0); + if((c.c->qid.type&QTDIR) == 0 || devtab[c.c->type]->dc != 's') + error("directory not a srv device"); + + s = c.c->aux; + + qlock(&dev.l); + for(f = s->entry; f != nil; f = f->entry){ + if(strcmp(f->name, file) == 0){ + qunlock(&dev.l); + error(Eexist); + } + } + + f = malloc(sizeof(SrvFile)); + if(f == nil){ + qunlock(&dev.l); + error(Enomem); + } + + srvretype(io->read, f, Trdchan); + srvretype(io->write, f, Twrchan); + f->read = io->read; + f->write = io->write; + + kstrdup(&f->name, file); + kstrdup(&f->user, up->env->user); + f->perm = 0666 & (~0666 | (s->perm & 0666)); + f->length = 0; + f->ref = 2; + mkqid(&f->qid, dev.pathgen++, 0, QTFILE); + + f->entry = s->entry; + s->entry = f; + s->ref++; + f->dir = s; + qunlock(&dev.l); + + cclose(c.c); + poperror(); + + return 0; +} + +Dev srvdevtab = { + 's', + "srv", + + srvinit, + srvattach, + srvwalk, + srvstat, + srvopen, + devcreate, + srvclose, + srvread, + devbread, + srvwrite, + devbwrite, + srvremove, + srvwstat +}; diff --git a/emu/port/devssl.c b/emu/port/devssl.c new file mode 100644 index 00000000..c8c16fc3 --- /dev/null +++ b/emu/port/devssl.c @@ -0,0 +1,1441 @@ +/* + * devssl - secure sockets layer + */ +#include "dat.h" +#include "fns.h" +#include "error.h" + +#include "mp.h" +#include "libsec.h" + +typedef struct OneWay OneWay; +struct OneWay +{ + QLock q; + QLock ctlq; + + void *state; /* encryption state */ + int slen; /* secret data length */ + uchar *secret; /* secret */ + ulong mid; /* message id */ +}; + +enum +{ + /* connection states */ + Sincomplete= 0, + Sclear= 1, + Sencrypting= 2, + Sdigesting= 4, + Sdigenc= Sencrypting|Sdigesting, + + /* encryption algorithms */ + Noencryption= 0, + DESCBC= 1, + DESECB= 2, + RC4= 3, + IDEACBC= 4, + IDEAECB= 5 +}; + +typedef struct Dstate Dstate; +struct Dstate +{ + Chan *c; /* io channel */ + uchar state; /* state of connection */ + int ref; /* serialized by dslock for atomic destroy */ + + uchar encryptalg; /* encryption algorithm */ + ushort blocklen; /* blocking length */ + + ushort diglen; /* length of digest */ + DigestState *(*hf)(uchar*, ulong, uchar*, DigestState*); /* hash func */ + + /* for SSL format */ + int max; /* maximum unpadded data per msg */ + int maxpad; /* maximum padded data per msg */ + + /* input side */ + OneWay in; + Block *processed; + Block *unprocessed; + + /* output side */ + OneWay out; + + /* protections */ + char* user; + int perm; +}; + +enum +{ + Maxdmsg= 1<<16, + Maxdstate= 1<<10, +}; + +Lock dslock; +int dshiwat; +int maxdstate = 20; +Dstate** dstate; + +enum{ + Qtopdir = 1, /* top level directory */ + Qclonus, + Qconvdir, /* directory for a conversation */ + Qdata, + Qctl, + Qsecretin, + Qsecretout, + Qencalgs, + Qhashalgs +}; + +#define TYPE(x) ((ulong)(x).path & 0xf) +#define CONV(x) (((ulong)(x).path >> 4)&(Maxdstate-1)) +#define QID(c, y) (((c)<<4) | (y)) + +static char* encalgs; +static char* hashalgs; + +void producerand(void); + +static void alglistinit(void); +static void ensure(Dstate*, Block**, int); +static void consume(Block**, uchar*, int); +static void setsecret(OneWay*, uchar*, int); +static Block* encryptb(Dstate*, Block*, int); +static Block* decryptb(Dstate*, Block*); +static Block* digestb(Dstate*, Block*, int); +static void checkdigestb(Dstate*, Block*); +static Chan* buftochan(char*); +static void sslhangup(Dstate*); +static void dsclone(Chan *c); +static void dsnew(Chan *c, Dstate **); + +static int +sslgen(Chan *c, char *dname, Dirtab *d, int nd, int s, Dir *dp) +{ + Qid q; + Dstate *ds; + char *p, *nm; + + USED(dname); + USED(nd); + USED(d); + q.type = QTFILE; + q.vers = 0; + if(s == DEVDOTDOT){ + q.path = QID(0, Qtopdir); + q.type = QTDIR; + devdir(c, q, "#D", 0, eve, 0555, dp); + return 1; + } + switch(TYPE(c->qid)) { + case Qtopdir: + if(s < dshiwat) { + q.path = QID(s, Qconvdir); + q.type = QTDIR; + ds = dstate[s]; + if(ds != 0) + nm = ds->user; + else + nm = eve; + snprint(up->genbuf, sizeof(up->genbuf), "%d", s); + devdir(c, q, up->genbuf, 0, nm, DMDIR|0555, dp); + return 1; + } + if(s > dshiwat) + return -1; + /* fall through */ + case Qclonus: + q.path = QID(0, Qclonus); + devdir(c, q, "clone", 0, eve, 0666, dp); + return 1; + case Qconvdir: + ds = dstate[CONV(c->qid)]; + if(ds != 0) + nm = ds->user; + else + nm = eve; + switch(s) { + default: + return -1; + case 0: + q.path = QID(CONV(c->qid), Qctl); + p = "ctl"; + break; + case 1: + q.path = QID(CONV(c->qid), Qdata); + p = "data"; + break; + case 2: + q.path = QID(CONV(c->qid), Qsecretin); + p = "secretin"; + break; + case 3: + q.path = QID(CONV(c->qid), Qsecretout); + p = "secretout"; + break; + case 4: + q.path = QID(CONV(c->qid), Qencalgs); + p = "encalgs"; + break; + case 5: + q.path = QID(CONV(c->qid), Qhashalgs); + p = "hashalgs"; + break; + } + devdir(c, q, p, 0, nm, 0660, dp); + return 1; + } + return -1; +} + +static void +sslinit(void) +{ + if((dstate = malloc(sizeof(Dstate*) * maxdstate)) == 0) + panic("sslinit"); + alglistinit(); +} + +static Chan * +sslattach(char *spec) +{ + Chan *c; + + c = devattach('D', spec); + c->qid.path = QID(0, Qtopdir); + c->qid.vers = 0; + c->qid.type = QTDIR; + return c; +} + +static Walkqid* +sslwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, 0, 0, sslgen); +} + +static int +sslstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, 0, 0, sslgen); +} + +static Chan* +sslopen(Chan *c, int omode) +{ + Dstate *s, **pp; + int perm; + + perm = 0; + omode &= 3; + switch(omode) { + case OREAD: + perm = 4; + break; + case OWRITE: + perm = 2; + break; + case ORDWR: + perm = 6; + break; + } + + switch(TYPE(c->qid)) { + default: + panic("sslopen"); + case Qtopdir: + case Qconvdir: + if(omode != OREAD) + error(Eperm); + break; + case Qclonus: + dsclone(c); + break; + case Qctl: + case Qdata: + case Qsecretin: + case Qsecretout: + if(waserror()) { + unlock(&dslock); + nexterror(); + } + lock(&dslock); + pp = &dstate[CONV(c->qid)]; + s = *pp; + if(s == 0) + dsnew(c, pp); + else { + if((perm & (s->perm>>6)) != perm + && (strcmp(up->env->user, s->user) != 0 + || (perm & s->perm) != perm)) + error(Eperm); + + s->ref++; + } + unlock(&dslock); + poperror(); + break; + case Qencalgs: + case Qhashalgs: + if(omode != OREAD) + error(Eperm); + break; + } + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static int +sslwstat(Chan *c, uchar *db, int n) +{ + Dir *dir; + Dstate *s; + int m; + + s = dstate[CONV(c->qid)]; + if(s == 0) + error(Ebadusefd); + if(strcmp(s->user, up->env->user) != 0) + error(Eperm); + + dir = smalloc(sizeof(Dir)+n); + m = convM2D(db, n, &dir[0], (char*)&dir[1]); + if(m == 0){ + free(dir); + error(Eshortstat); + } + + if(!emptystr(dir->uid)) + kstrdup(&s->user, dir->uid); + if(dir->mode != ~0UL) + s->perm = dir->mode; + + free(dir); + return m; +} + +static void +sslclose(Chan *c) +{ + Dstate *s; + + switch(TYPE(c->qid)) { + case Qctl: + case Qdata: + case Qsecretin: + case Qsecretout: + if((c->flag & COPEN) == 0) + break; + + s = dstate[CONV(c->qid)]; + if(s == 0) + break; + + lock(&dslock); + if(--s->ref > 0) { + unlock(&dslock); + break; + } + dstate[CONV(c->qid)] = 0; + unlock(&dslock); + + sslhangup(s); + if(s->c) + cclose(s->c); + free(s->user); + free(s->in.secret); + free(s->out.secret); + free(s->in.state); + free(s->out.state); + free(s); + } +} + +/* + * make sure we have at least 'n' bytes in list 'l' + */ +static void +ensure(Dstate *s, Block **l, int n) +{ + int sofar, i; + Block *b, *bl; + + sofar = 0; + for(b = *l; b; b = b->next){ + sofar += BLEN(b); + if(sofar >= n) + return; + l = &b->next; + } + + while(sofar < n){ + bl = devtab[s->c->type]->bread(s->c, Maxdmsg, 0); + if(bl == 0) + error(Ehungup); + *l = bl; + i = 0; + for(b = bl; b; b = b->next){ + i += BLEN(b); + l = &b->next; + } + if(i == 0) + error(Ehungup); + + sofar += i; + } +} + +/* + * copy 'n' bytes from 'l' into 'p' and free + * the bytes in 'l' + */ +static void +consume(Block **l, uchar *p, int n) +{ + Block *b; + int i; + + for(; *l && n > 0; n -= i){ + b = *l; + i = BLEN(b); + if(i > n) + i = n; + memmove(p, b->rp, i); + b->rp += i; + p += i; + if(BLEN(b) < 0) + panic("consume"); + if(BLEN(b)) + break; + *l = b->next; + freeb(b); + } +} + +/* + * remove at most n bytes from the queue, if discard is set + * dump the remainder + */ +static Block* +qtake(Block **l, int n, int discard) +{ + Block *nb, *b, *first; + int i; + + first = *l; + for(b = first; b; b = b->next){ + i = BLEN(b); + if(i == n){ + if(discard){ + freeblist(b->next); + *l = 0; + } else + *l = b->next; + b->next = 0; + return first; + } else if(i > n){ + i -= n; + if(discard){ + freeblist(b->next); + *l = 0; + } else { + nb = allocb(i); + memmove(nb->wp, b->rp+n, i); + nb->wp += i; + nb->next = b->next; + *l = nb; + } + b->wp -= i; + b->next = 0; + if(BLEN(b) < 0) + panic("qtake"); + return first; + } else + n -= i; + if(BLEN(b) < 0) + panic("qtake"); + } + *l = 0; + return first; +} + +static Block* +sslbread(Chan *c, long n, ulong offset) +{ + volatile struct { Dstate *s; } s; + volatile struct { int nc; } nc; + Block *b; + uchar count[3]; + int len, pad; + + USED(offset); + s.s = dstate[CONV(c->qid)]; + if(s.s == 0) + panic("sslbread"); + if(s.s->state == Sincomplete) + error(Ebadusefd); + + nc.nc = 0; + if(waserror()){ + qunlock(&s.s->in.q); + if(strcmp(up->env->errstr, "interrupted") == 0){ + if(nc.nc > 0){ + b = allocb(nc.nc); + memmove(b->wp, count, nc.nc); + b->wp += nc.nc; + b->next = s.s->unprocessed; + s.s->unprocessed = b; + } + } else + sslhangup(s.s); + nexterror(); + } + qlock(&s.s->in.q); + + if(s.s->processed == 0){ + /* read in the whole message */ + ensure(s.s, &s.s->unprocessed, 2); + + consume(&s.s->unprocessed, count, 2); + nc.nc += 2; + if(count[0] & 0x80){ + len = ((count[0] & 0x7f)<<8) | count[1]; + ensure(s.s, &s.s->unprocessed, len); + pad = 0; + } else { + len = ((count[0] & 0x3f)<<8) | count[1]; + ensure(s.s, &s.s->unprocessed, len+1); + consume(&s.s->unprocessed, count + nc.nc, 1); + pad = count[nc.nc]; + nc.nc++; + if(pad > len){ + print("pad %d buf len %d\n", pad, len); + error("bad pad in ssl message"); + } + } + nc.nc = 0; + + /* put extra on unprocessed queue */ + s.s->processed = qtake(&s.s->unprocessed, len, 0); + + if(waserror()){ + qunlock(&s.s->in.ctlq); + nexterror(); + } + qlock(&s.s->in.ctlq); + switch(s.s->state){ + case Sencrypting: + s.s->processed = decryptb(s.s, s.s->processed); + break; + case Sdigesting: + s.s->processed = pullupblock(s.s->processed, s.s->diglen); + if(s.s->processed == 0) + error("ssl message too short"); + checkdigestb(s.s, s.s->processed); + s.s->processed->rp += s.s->diglen; + break; + case Sdigenc: + s.s->processed = decryptb(s.s, s.s->processed); + s.s->processed = pullupblock(s.s->processed, s.s->diglen); + if(s.s->processed == 0) + error("ssl message too short"); + checkdigestb(s.s, s.s->processed); + s.s->processed->rp += s.s->diglen; + len -= s.s->diglen; + break; + } + s.s->in.mid++; + qunlock(&s.s->in.ctlq); + poperror(); + + /* remove pad */ + if(pad) + s.s->processed = qtake(&s.s->processed, len - pad, 1); + } + + /* return at most what was asked for */ + b = qtake(&s.s->processed, n, 0); + + qunlock(&s.s->in.q); + poperror(); + + return b; +} + +static long +sslread(Chan *c, void *a, long n, vlong offset) +{ + volatile struct { Block *b; } b; + Block *nb; + uchar *va; + int i; + char buf[128]; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, sslgen); + + switch(TYPE(c->qid)) { + default: + error(Ebadusefd); + case Qctl: + sprint(buf, "%ld", CONV(c->qid)); + return readstr(offset, a, n, buf); + case Qdata: + b.b = sslbread(c, n, offset); + break; + case Qencalgs: + return readstr(offset, a, n, encalgs); + case Qhashalgs: + return readstr(offset, a, n, hashalgs); + } + + n = 0; + va = a; + for(nb = b.b; nb; nb = nb->next){ + i = BLEN(nb); + memmove(va+n, nb->rp, i); + n += i; + } + + freeblist(b.b); + + return n; +} + +/* + * this algorithm doesn't have to be great since we're just + * trying to obscure the block fill + */ +static void +randfill(uchar *buf, int len) +{ + while(len-- > 0) + *buf++ = nrand(256); +} + +/* + * use SSL record format, add in count and digest or encrypt + */ +static long +sslbwrite(Chan *c, Block *b, ulong offset) +{ + volatile struct { Dstate *s; } s; + volatile struct { Block *b; } bb; + Block *nb; + int h, n, m, pad, rv; + uchar *p; + + bb.b = b; + s.s = dstate[CONV(c->qid)]; + if(s.s == 0) + panic("sslbwrite"); + if(s.s->state == Sincomplete){ + freeb(b); + error(Ebadusefd); + } + + if(waserror()){ + qunlock(&s.s->out.q); + if(bb.b) + freeb(bb.b); + sslhangup(s.s); + nexterror(); + } + qlock(&s.s->out.q); + + rv = 0; + while(bb.b){ + m = n = BLEN(bb.b); + h = s.s->diglen + 2; + + /* trim to maximum block size */ + pad = 0; + if(m > s.s->max){ + m = s.s->max; + } else if(s.s->blocklen != 1){ + pad = (m + s.s->diglen)%s.s->blocklen; + if(pad){ + if(m > s.s->maxpad){ + pad = 0; + m = s.s->maxpad; + } else { + pad = s.s->blocklen - pad; + h++; + } + } + } + + rv += m; + if(m != n){ + nb = allocb(m + h + pad); + memmove(nb->wp + h, bb.b->rp, m); + nb->wp += m + h; + bb.b->rp += m; + } else { + /* add header space */ + nb = padblock(bb.b, h); + bb.b = 0; + } + m += s.s->diglen; + + /* SSLv2 style count */ + if(pad){ + nb = padblock(nb, -pad); + randfill(nb->wp, pad); + nb->wp += pad; + m += pad; + + p = nb->rp; + p[0] = (m>>8); + p[1] = m; + p[2] = pad; + offset = 3; + } else { + p = nb->rp; + p[0] = (m>>8) | 0x80; + p[1] = m; + offset = 2; + } + + switch(s.s->state){ + case Sencrypting: + nb = encryptb(s.s, nb, offset); + break; + case Sdigesting: + nb = digestb(s.s, nb, offset); + break; + case Sdigenc: + nb = digestb(s.s, nb, offset); + nb = encryptb(s.s, nb, offset); + break; + } + + s.s->out.mid++; + + m = BLEN(nb); + devtab[s.s->c->type]->bwrite(s.s->c, nb, s.s->c->offset); + s.s->c->offset += m; + } + qunlock(&s.s->out.q); + poperror(); + + return rv; +} + +static void +setsecret(OneWay *w, uchar *secret, int n) +{ + free(w->secret); + w->secret = mallocz(n, 0); + if(w->secret == nil) + error(Enomem); + memmove(w->secret, secret, n); + w->slen = n; +} + +static void +initIDEAkey(OneWay *w) +{ + + free(w->state); + w->state = malloc(sizeof(IDEAstate)); + if(w->state == nil) + error(Enomem); + if(w->slen >= 24) + setupIDEAstate(w->state, w->secret, w->secret+16); + else if(w->slen >= 16) + setupIDEAstate(w->state, w->secret, 0); + else + error("secret too short"); +} + +static void +initDESkey(OneWay *w) +{ + + free(w->state); + w->state = malloc(sizeof(DESstate)); + if (!w->state) + error(Enomem); + if(w->slen >= 16) + setupDESstate(w->state, w->secret, w->secret+8); + else if(w->slen >= 8) + setupDESstate(w->state, w->secret, 0); + else + error("secret too short"); +} + +/* + * 40 bit DES is the same as 56 bit DES. However, + * 16 bits of the key are masked to zero. + */ +static void +initDESkey_40(OneWay *w) +{ + uchar key[8]; + + + if(w->slen >= 8) { + memmove(key, w->secret, 8); + key[0] &= 0x0f; + key[2] &= 0x0f; + key[4] &= 0x0f; + key[6] &= 0x0f; + } + + free(w->state); + w->state = malloc(sizeof(DESstate)); + if (!w->state) + error(Enomem); + if(w->slen >= 16) + setupDESstate(w->state, key, w->secret+8); + else if(w->slen >= 8) + setupDESstate(w->state, key, 0); + else + error("secret too short"); +} + +static void +initRC4key(OneWay *w) +{ + free(w->state); + w->state = malloc(sizeof(RC4state)); + if (!w->state) + error(Enomem); + setupRC4state(w->state, w->secret, w->slen); +} + +/* + * 40 bit RC4 is the same as n-bit RC4. However, + * we ignore all but the first 40 bits of the key. + */ +static void +initRC4key_40(OneWay *w) +{ + int slen = w->slen; + + if(slen > 5) + slen = 5; + + free(w->state); + w->state = malloc(sizeof(RC4state)); + if (!w->state) + error(Enomem); + setupRC4state(w->state, w->secret, slen); +} + +/* + * 128 bit RC4 is the same as n-bit RC4. However, + * we ignore all but the first 128 bits of the key. + */ +static void +initRC4key_128(OneWay *w) +{ + int slen = w->slen; + + if(slen > 16) + slen = 16; + + free(w->state); + w->state = malloc(sizeof(RC4state)); + if (!w->state) + error(Enomem); + setupRC4state(w->state, w->secret, slen); +} + +typedef struct Hashalg Hashalg; +struct Hashalg +{ + char *name; + int diglen; + DigestState *(*hf)(uchar*, ulong, uchar*, DigestState*); +}; + +Hashalg hashtab[] = +{ + { "md4", MD4dlen, md4, }, + { "md5", MD5dlen, md5, }, + { "sha1", SHA1dlen, sha1, }, + { "sha", SHA1dlen, sha1, }, + { 0 } +}; + +static int +parsehashalg(char *p, Dstate *s) +{ + Hashalg *ha; + + for(ha = hashtab; ha->name; ha++){ + if(strcmp(p, ha->name) == 0){ + s->hf = ha->hf; + s->diglen = ha->diglen; + s->state &= ~Sclear; + s->state |= Sdigesting; + return 0; + } + } + return -1; +} + +typedef struct Encalg Encalg; +struct Encalg +{ + char *name; + int blocklen; + int alg; + void (*keyinit)(OneWay*); +}; + +Encalg encrypttab[] = +{ + { "descbc", 8, DESCBC, initDESkey, }, /* DEPRECATED -- use des_56_cbc */ + { "desecb", 8, DESECB, initDESkey, }, /* DEPRECATED -- use des_56_ecb */ + { "des_56_cbc", 8, DESCBC, initDESkey, }, + { "des_56_ecb", 8, DESECB, initDESkey, }, + { "des_40_cbc", 8, DESCBC, initDESkey_40, }, + { "des_40_ecb", 8, DESECB, initDESkey_40, }, + { "rc4", 1, RC4, initRC4key_40, }, /* DEPRECATED -- use rc4_X */ + { "rc4_256", 1, RC4, initRC4key, }, + { "rc4_128", 1, RC4, initRC4key_128, }, + { "rc4_40", 1, RC4, initRC4key_40, }, + { "ideacbc", 8, IDEACBC, initIDEAkey, }, + { "ideaecb", 8, IDEAECB, initIDEAkey, }, + { 0 } +}; + +static int +parseencryptalg(char *p, Dstate *s) +{ + Encalg *ea; + + for(ea = encrypttab; ea->name; ea++){ + if(strcmp(p, ea->name) == 0){ + s->encryptalg = ea->alg; + s->blocklen = ea->blocklen; + (*ea->keyinit)(&s->in); + (*ea->keyinit)(&s->out); + s->state &= ~Sclear; + s->state |= Sencrypting; + return 0; + } + } + return -1; +} + +static void +alglistinit(void) +{ + Hashalg *h; + Encalg *e; + int n; + + n = 1; + for(e = encrypttab; e->name != nil; e++) + n += strlen(e->name) + 1; + encalgs = malloc(n); + if(encalgs == nil) + panic("sslinit"); + n = 0; + for(e = encrypttab; e->name != nil; e++){ + strcpy(encalgs+n, e->name); + n += strlen(e->name); + if(e[1].name == nil) + break; + encalgs[n++] = ' '; + } + encalgs[n] = 0; + + n = 1; + for(h = hashtab; h->name != nil; h++) + n += strlen(h->name) + 1; + hashalgs = malloc(n); + if(hashalgs == nil) + panic("sslinit"); + n = 0; + for(h = hashtab; h->name != nil; h++){ + strcpy(hashalgs+n, h->name); + n += strlen(h->name); + if(h[1].name == nil) + break; + hashalgs[n++] = ' '; + } + hashalgs[n] = 0; +} + +static long +sslwrite(Chan *c, void *a, long n, vlong offset) +{ + volatile struct { Dstate *s; } s; + volatile struct { Block *b; } b; + int m, t; + char *p, *np, *e, buf[32]; + uchar *x; + + s.s = dstate[CONV(c->qid)]; + if(s.s == 0) + panic("sslwrite"); + + t = TYPE(c->qid); + if(t == Qdata){ + if(s.s->state == Sincomplete) + error(Ebadusefd); + + p = a; + e = p + n; + do { + m = e - p; + if(m > s.s->max) + m = s.s->max; + + b.b = allocb(m); + memmove(b.b->wp, p, m); + b.b->wp += m; + + sslbwrite(c, b.b, offset); + + p += m; + } while(p < e); + return n; + } + + /* mutex with operations using what we're about to change */ + if(waserror()){ + qunlock(&s.s->in.ctlq); + qunlock(&s.s->out.q); + nexterror(); + } + qlock(&s.s->in.ctlq); + qlock(&s.s->out.q); + + switch(t){ + default: + panic("sslwrite"); + case Qsecretin: + setsecret(&s.s->in, a, n); + goto out; + case Qsecretout: + setsecret(&s.s->out, a, n); + goto out; + case Qctl: + break; + } + + if(n >= sizeof(buf)) + error(Ebadarg); + strncpy(buf, a, n); + buf[n] = 0; + p = strchr(buf, '\n'); + if(p) + *p = 0; + p = strchr(buf, ' '); + if(p) + *p++ = 0; + + if(strcmp(buf, "fd") == 0){ + s.s->c = buftochan(p); + + /* default is clear (msg delimiters only) */ + s.s->state = Sclear; + s.s->blocklen = 1; + s.s->diglen = 0; + s.s->maxpad = s.s->max = (1<<15) - s.s->diglen - 1; + s.s->in.mid = 0; + s.s->out.mid = 0; + } else if(strcmp(buf, "alg") == 0 && p != 0){ + s.s->blocklen = 1; + s.s->diglen = 0; + + if(s.s->c == 0) + error("must set fd before algorithm"); + + if(strcmp(p, "clear") == 0){ + s.s->state = Sclear; + s.s->maxpad = s.s->max = (1<<15) - s.s->diglen - 1; + goto out; + } + + if(s.s->in.secret && s.s->out.secret == 0) + setsecret(&s.s->out, s.s->in.secret, s.s->in.slen); + if(s.s->out.secret && s.s->in.secret == 0) + setsecret(&s.s->in, s.s->out.secret, s.s->out.slen); + if(s.s->in.secret == 0 || s.s->out.secret == 0) + error("algorithm but no secret"); + + s.s->hf = 0; + s.s->encryptalg = Noencryption; + s.s->blocklen = 1; + + for(;;){ + np = strchr(p, ' '); + if(np) + *np++ = 0; + else{ + np = strchr(p, '/'); + if(np) + *np++ = 0; + } + if(parsehashalg(p, s.s) < 0) + if(parseencryptalg(p, s.s) < 0) + error(Ebadarg); + + if(np == 0) + break; + p = np; + } + + if(s.s->hf == 0 && s.s->encryptalg == Noencryption) + error(Ebadarg); + + if(s.s->blocklen != 1){ + /* make multiple of blocklen */ + s.s->max = (1<<15) - s.s->diglen - 1; + s.s->max -= s.s->max % s.s->blocklen; + s.s->maxpad = (1<<14) - s.s->diglen - 1; + s.s->maxpad -= s.s->maxpad % s.s->blocklen; + } else + s.s->maxpad = s.s->max = (1<<15) - s.s->diglen - 1; + } else if(strcmp(buf, "secretin") == 0 && p != 0) { + m = (strlen(p)*3)/2; + x = smalloc(m); + if(waserror()){ + free(x); + nexterror(); + } + t = dec64(x, m, p, strlen(p)); + setsecret(&s.s->in, x, t); + poperror(); + free(x); + } else if(strcmp(buf, "secretout") == 0 && p != 0) { + m = (strlen(p)*3)/2; + x = smalloc(m); + if(waserror()){ + free(x); + nexterror(); + } + t = dec64(x, m, p, strlen(p)); + setsecret(&s.s->out, x, t); + poperror(); + free(x); + } else + error(Ebadarg); + +out: + qunlock(&s.s->in.ctlq); + qunlock(&s.s->out.q); + poperror(); + return n; +} + + +static Block* +encryptb(Dstate *s, Block *b, int offset) +{ + uchar *p, *ep, *p2, *ip, *eip; + DESstate *ds; + IDEAstate *is; + + switch(s->encryptalg){ + case DESECB: + ds = s->out.state; + ep = b->rp + BLEN(b); + for(p = b->rp + offset; p < ep; p += 8) + block_cipher(ds->expanded, p, 0); + break; + case DESCBC: + ds = s->out.state; + ep = b->rp + BLEN(b); + for(p = b->rp + offset; p < ep; p += 8){ + p2 = p; + ip = ds->ivec; + for(eip = ip+8; ip < eip; ) + *p2++ ^= *ip++; + block_cipher(ds->expanded, p, 0); + memmove(ds->ivec, p, 8); + } + break; + case IDEAECB: + is = s->out.state; + ep = b->rp + BLEN(b); + for(p = b->rp + offset; p < ep; p += 8) + idea_cipher(is->edkey, p, 0); + break; + case IDEACBC: + is = s->out.state; + ep = b->rp + BLEN(b); + for(p = b->rp + offset; p < ep; p += 8){ + p2 = p; + ip = is->ivec; + for(eip = ip+8; ip < eip; ) + *p2++ ^= *ip++; + idea_cipher(is->edkey, p, 0); + memmove(is->ivec, p, 8); + } + break; + case RC4: + rc4(s->out.state, b->rp + offset, BLEN(b) - offset); + break; + } + return b; +} + +static Block* +decryptb(Dstate *s, Block *inb) +{ + Block *b, **l; + uchar *p, *ep, *tp, *ip, *eip; + DESstate *ds; + IDEAstate *is; + uchar tmp[8]; + int i; + + l = &inb; + for(b = inb; b; b = b->next){ + /* make sure we have a multiple of s->blocklen */ + if(s->blocklen > 1){ + i = BLEN(b); + if(i % s->blocklen){ + *l = b = pullupblock(b, i + s->blocklen - (i%s->blocklen)); + if(b == 0) + error("ssl encrypted message too short"); + } + } + l = &b->next; + + /* decrypt */ + switch(s->encryptalg){ + case DESECB: + ds = s->in.state; + ep = b->rp + BLEN(b); + for(p = b->rp; p < ep; p += 8) + block_cipher(ds->expanded, p, 1); + break; + case DESCBC: + ds = s->in.state; + ep = b->rp + BLEN(b); + for(p = b->rp; p < ep;){ + memmove(tmp, p, 8); + block_cipher(ds->expanded, p, 1); + tp = tmp; + ip = ds->ivec; + for(eip = ip+8; ip < eip; ){ + *p++ ^= *ip; + *ip++ = *tp++; + } + } + break; + case IDEAECB: + is = s->in.state; + ep = b->rp + BLEN(b); + for(p = b->rp; p < ep; p += 8) + idea_cipher(is->edkey, p, 1); + break; + case IDEACBC: + is = s->in.state; + ep = b->rp + BLEN(b); + for(p = b->rp; p < ep;){ + memmove(tmp, p, 8); + idea_cipher(is->edkey, p, 1); + tp = tmp; + ip = is->ivec; + for(eip = ip+8; ip < eip; ){ + *p++ ^= *ip; + *ip++ = *tp++; + } + } + break; + case RC4: + rc4(s->in.state, b->rp, BLEN(b)); + break; + } + } + return inb; +} + +static Block* +digestb(Dstate *s, Block *b, int offset) +{ + uchar *p; + DigestState ss; + uchar msgid[4]; + ulong n, h; + OneWay *w; + + w = &s->out; + + memset(&ss, 0, sizeof(ss)); + h = s->diglen + offset; + n = BLEN(b) - h; + + /* hash secret + message */ + (*s->hf)(w->secret, w->slen, 0, &ss); + (*s->hf)(b->rp + h, n, 0, &ss); + + /* hash message id */ + p = msgid; + n = w->mid; + *p++ = n>>24; + *p++ = n>>16; + *p++ = n>>8; + *p = n; + (*s->hf)(msgid, 4, b->rp + offset, &ss); + + return b; +} + +static void +checkdigestb(Dstate *s, Block *inb) +{ + uchar *p; + DigestState ss; + uchar msgid[4]; + int n, h; + OneWay *w; + uchar digest[128]; + Block *b; + + w = &s->in; + + memset(&ss, 0, sizeof(ss)); + + /* hash secret */ + (*s->hf)(w->secret, w->slen, 0, &ss); + + /* hash message */ + h = s->diglen; + for(b = inb; b; b = b->next){ + n = BLEN(b) - h; + if(n < 0) + panic("checkdigestb"); + (*s->hf)(b->rp + h, n, 0, &ss); + h = 0; + } + + /* hash message id */ + p = msgid; + n = w->mid; + *p++ = n>>24; + *p++ = n>>16; + *p++ = n>>8; + *p = n; + (*s->hf)(msgid, 4, digest, &ss); + + /* requires pullupblock */ + if(memcmp(digest, inb->rp, s->diglen) != 0) + error("bad digest"); +} + +/* get channel associated with an fd */ +static Chan* +buftochan(char *p) +{ + Chan *c; + int fd; + + if(p == 0) + error(Ebadarg); + fd = strtoul(p, 0, 0); + if(fd < 0) + error(Ebadarg); + c = fdtochan(up->env->fgrp, fd, -1, 0, 1); /* error check and inc ref */ + return c; +} + +/* hang up a digest connection */ +static void +sslhangup(Dstate *s) +{ + qlock(&s->in.q); + freeblist(s->processed); + s->processed = 0; + freeblist(s->unprocessed); + s->unprocessed = 0; + s->state = Sincomplete; + qunlock(&s->in.q); +} + +static void +dsclone(Chan *ch) +{ + Dstate **pp, **ep, **np; + int newmax; + + lock(&dslock); + if(waserror()) { + unlock(&dslock); + nexterror(); + } + ep = &dstate[maxdstate]; + for(pp = dstate; pp < ep; pp++) { + if(*pp == 0) { + dsnew(ch, pp); + break; + } + } + if(pp >= ep) { + if(maxdstate >= Maxdstate) + error(Enodev); + newmax = 2 * maxdstate; + if(newmax > Maxdstate) + newmax = Maxdstate; + + np = realloc(dstate, sizeof(Dstate*) * newmax); + if(np == 0) + error(Enomem); + dstate = np; + pp = &dstate[maxdstate]; + memset(pp, 0, sizeof(Dstate*)*(newmax - maxdstate)); + + maxdstate = newmax; + dsnew(ch, pp); + } + poperror(); + unlock(&dslock); +} + +static void +dsnew(Chan *ch, Dstate **pp) +{ + Dstate *s; + int t; + + *pp = s = mallocz(sizeof(*s), 1); + if(s == nil) + error(Enomem); + if(pp - dstate >= dshiwat) + dshiwat++; + s->state = Sincomplete; + s->ref = 1; + kstrdup(&s->user, up->env->user); + s->perm = 0660; + t = TYPE(ch->qid); + if(t == Qclonus) + t = Qctl; + ch->qid.path = QID(pp - dstate, t); + ch->qid.vers = 0; + ch->qid.type = QTFILE; +} + +Dev ssldevtab = { + 'D', + "ssl", + + sslinit, + sslattach, + sslwalk, + sslstat, + sslopen, + devcreate, + sslclose, + sslread, + sslbread, + sslwrite, + sslbwrite, + devremove, + sslwstat +}; diff --git a/emu/port/devtinyfs.c b/emu/port/devtinyfs.c new file mode 100644 index 00000000..33689745 --- /dev/null +++ b/emu/port/devtinyfs.c @@ -0,0 +1,895 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + + +enum{ + Qdir, + Qmedium, + + Maxfs= 10, /* max file systems */ + + Blen= 48, /* block length */ + Nlen= 28, /* name length */ + Dlen= Blen - 4, + + Tagdir= 'd', + Tagdata= 'D', + Tagend= 'e', + Tagfree= 'f', + + Notapin= 0xffff, + Notabno= 0xffff, + + Fcreating= 1, + Frmonclose= 2 +}; + +/* representation of a Tdir on medium */ +typedef struct Mdir Mdir; +struct Mdir { + uchar type; + uchar bno[2]; + uchar pin[2]; + char name[Nlen]; + char pad[Blen - Nlen - 6]; + uchar sum; +}; + +/* representation of a Tdata/Tend on medium */ +typedef struct Mdata Mdata; +struct Mdata { + uchar type; + uchar bno[2]; + uchar data[Dlen]; + uchar sum; +}; + +typedef struct Tfile Tfile; +struct Tfile { + int r; + char name[Nlen]; + ushort bno; + ushort dbno; + ushort pin; + uchar flag; + ulong length; + + /* hint to avoid egregious reading */ + ushort fbno; + ulong finger; +}; + +typedef struct Tfs Tfs; +struct Tfs { + QLock ql; + int r; + Chan *c; + uchar *map; + int nblocks; + Tfile *f; + int nf; + int fsize; +}; + +static struct { + Tfs fs[Maxfs]; +} tinyfs; + +#define GETS(x) ((x)[0]|((x)[1]<<8)) +#define PUTS(x, v) {(x)[0] = (v);(x)[1] = ((v)>>8);} + +#define GETL(x) (GETS(x)|(GETS(x+2)<<16)) +#define PUTL(x, v) {PUTS(x, v);PUTS(x+2, (v)>>16)}; + +static uchar +checksum(uchar *p) +{ + uchar *e; + uchar s; + + s = 0; + for(e = p + Blen; p < e; p++) + s += *p; + return s; +} + +static void +mapclr(Tfs *fs, ulong bno) +{ + fs->map[bno>>3] &= ~(1<<(bno&7)); +} + +static void +mapset(Tfs *fs, ulong bno) +{ + fs->map[bno>>3] |= 1<<(bno&7); +} + +static int +isalloced(Tfs *fs, ulong bno) +{ + return fs->map[bno>>3] & (1<<(bno&7)); +} + +static int +mapalloc(Tfs *fs) +{ + int i, j, lim; + uchar x; + + lim = (fs->nblocks + 8 - 1)/8; + for(i = 0; i < lim; i++){ + x = fs->map[i]; + if(x == 0xff) + continue; + for(j = 0; j < 8; j++) + if((x & (1<<j)) == 0){ + fs->map[i] = x|(1<<j); + return i*8 + j; + } + } + + return Notabno; +} + +static Mdir* +validdir(Tfs *fs, uchar *p) +{ + Mdir *md; + ulong x; + + if(checksum(p) != 0) + return 0; + if(p[0] != Tagdir) + return 0; + md = (Mdir*)p; + x = GETS(md->bno); + if(x >= fs->nblocks) + return 0; + return md; +} + +static Mdata* +validdata(Tfs *fs, uchar *p, int *lenp) +{ + Mdata *md; + ulong x; + + if(checksum(p) != 0) + return 0; + md = (Mdata*)p; + switch(md->type){ + case Tagdata: + x = GETS(md->bno); + if(x >= fs->nblocks) + return 0; + if(lenp) + *lenp = Dlen; + break; + case Tagend: + x = GETS(md->bno); + if(x > Dlen) + return 0; + if(lenp) + *lenp = x; + break; + default: + return 0; + } + return md; +} + +static Mdata* +readdata(Tfs *fs, ulong bno, uchar *buf, int *lenp) +{ + if(bno >= fs->nblocks) + return 0; + if(devtab[fs->c->type]->read(fs->c, buf, Blen, Blen*bno) != Blen) + error(Eio); + return validdata(fs, buf, lenp); +} + +static void +writedata(Tfs *fs, ulong bno, ulong next, uchar *buf, int len, int last) +{ + Mdata md; + + if(bno >= fs->nblocks) + error(Eio); + if(len > Dlen) + len = Dlen; + if(len < 0) + error(Eio); + memset(&md, 0, sizeof(md)); + if(last){ + md.type = Tagend; + PUTS(md.bno, len); + } else { + md.type = Tagdata; + PUTS(md.bno, next); + } + memmove(md.data, buf, len); + md.sum = 0 - checksum((uchar*)&md); + + if(devtab[fs->c->type]->write(fs->c, &md, Blen, Blen*bno) != Blen) + error(Eio); +} + +static void +writedir(Tfs *fs, Tfile *f) +{ + Mdir *md; + uchar buf[Blen]; + + if(f->bno == Notabno) + return; + + md = (Mdir*)buf; + memset(buf, 0, Blen); + md->type = Tagdir; + strncpy(md->name, f->name, sizeof(md->name)-1); + PUTS(md->bno, f->dbno); + PUTS(md->pin, f->pin); + md->sum = 0 - checksum(buf); + + if(devtab[fs->c->type]->write(fs->c, buf, Blen, Blen*f->bno) != Blen) + error(Eio); +} + +static void +freeblocks(Tfs *fs, ulong bno, ulong bend) +{ + uchar buf[Blen]; + Mdata *md; + + if(waserror()) + return; + + while(bno != bend && bno != Notabno){ + mapclr(fs, bno); + if(devtab[fs->c->type]->read(fs->c, buf, Blen, Blen*bno) != Blen) + break; + md = validdata(fs, buf, 0); + if(md == 0) + break; + if(md->type == Tagend) + break; + bno = GETS(md->bno); + } + + poperror(); +} + +static void +freefile(Tfs *fs, Tfile *f, ulong bend) +{ + uchar buf[Blen]; + + /* remove blocks from map */ + freeblocks(fs, f->dbno, bend); + + /* change file type to free on medium */ + if(f->bno != Notabno){ + memset(buf, 0x55, Blen); + devtab[fs->c->type]->write(fs->c, buf, Blen, Blen*f->bno); + mapclr(fs, f->bno); + } + + /* forget we ever knew about it */ + memset(f, 0, sizeof(*f)); +} + +static void +expand(Tfs *fs) +{ + Tfile *f; + + fs->fsize += 8; + f = malloc(fs->fsize*sizeof(*f)); + if(f == nil) + error(Enomem); + + if(fs->f){ + memmove(f, fs->f, fs->nf*sizeof(*f)); + free(fs->f); + } + fs->f = f; +} + +static Tfile* +newfile(Tfs *fs, char *name) +{ + int i; + volatile struct { + Tfile *f; + Tfs *fs; + } rock; + + /* find free entry in file table */ + rock.f = 0; + rock.fs = fs; + for(;;) { + for(i = 0; i < rock.fs->fsize; i++){ + rock.f = &rock.fs->f[i]; + if(rock.f->name[0] == 0){ + strncpy(rock.f->name, name, sizeof(rock.f->name)-1); + break; + } + } + + if(i < rock.fs->fsize){ + if(i >= rock.fs->nf) + rock.fs->nf = i+1; + break; + } + + expand(rock.fs); + } + + rock.f->flag = Fcreating; + rock.f->dbno = Notabno; + rock.f->bno = mapalloc(rock.fs); + rock.f->fbno = Notabno; + rock.f->r = 1; + rock.f->pin = up->env->pgrp->pin; + + /* write directory block */ + if(waserror()){ + freefile(rock.fs, rock.f, Notabno); + nexterror(); + } + if(rock.f->bno == Notabno) + error("out of space"); + writedir(rock.fs, rock.f); + poperror(); + + return rock.f; +} + +/* + * Read the whole medium and build a file table and used + * block bitmap. Inconsistent files are purged. The medium + * had better be small or this could take a while. + */ +static void +tfsinit(Tfs *fs) +{ + uchar dbuf[STATFIXLEN+32*4]; + Dir d; + uchar buf[Blen]; + ulong x, bno; + int n, done; + Tfile *f; + Mdir *mdir; + Mdata *mdata; + + n = devtab[fs->c->type]->stat(fs->c, dbuf, sizeof(dbuf)); + n = convM2D(dbuf, n, &d, nil); + if(n <= 0) + error("cannot stat tinyfs medium"); + fs->nblocks = d.length/Blen; + if(fs->nblocks < 3) + error("tinyfs medium too small"); + + /* bitmap for block usage */ + x = (fs->nblocks + 8 - 1)/8; + fs->map = malloc(x); + if(fs->map == nil) + error(Enomem); + for(bno = fs->nblocks; bno < x*8; bno++) + mapset(fs, bno); + + /* find files */ + for(bno = 0; bno < fs->nblocks; bno++){ + n = devtab[fs->c->type]->read(fs->c, buf, Blen, Blen*bno); + if(n != Blen) + break; + + mdir = validdir(fs, buf); + if(mdir == 0) + continue; + + if(fs->nf >= fs->fsize) + expand(fs); + + f = &fs->f[fs->nf++]; + + x = GETS(mdir->bno); + mapset(fs, bno); + strncpy(f->name, mdir->name, sizeof(f->name)); + f->pin = GETS(mdir->pin); + f->bno = bno; + f->dbno = x; + f->fbno = Notabno; + } + + /* follow files */ + for(f = fs->f; f < &(fs->f[fs->nf]); f++){ + bno = f->dbno; + for(done = 0; !done;) { + if(isalloced(fs, bno)){ + freefile(fs, f, bno); + break; + } + n = devtab[fs->c->type]->read(fs->c, buf, Blen, Blen*bno); + if(n != Blen){ + freefile(fs, f, bno); + break; + } + mdata = validdata(fs, buf, 0); + if(mdata == 0){ + freefile(fs, f, bno); + break; + } + mapset(fs, bno); + switch(mdata->type){ + case Tagdata: + bno = GETS(mdata->bno); + f->length += Dlen; + break; + case Tagend: + f->length += GETS(mdata->bno); + done = 1; + break; + } + if(done) + f->flag &= ~Fcreating; + } + } +} + +/* + * single directory + */ +static int +tinyfsgen(Chan *c, char *name, Dirtab *tab, int ntab, int i, Dir *dp) +{ + Tfs *fs; + Tfile *f; + Qid qid; + + USED(name); + USED(ntab); + USED(tab); + + fs = &tinyfs.fs[c->dev]; + if(i >= fs->nf) + return -1; + qid.vers = 0; + if(i == DEVDOTDOT){ + qid.type = QTDIR; + devdir(c, qid, ".", 0, eve, DMDIR|0555, dp); + return 1; + } + f = &fs->f[i]; + if(f->name[0] == 0) + return 0; + qid.path = i+1; + qid.type = QTFILE; + devdir(c, qid, f->name, f->length, eve, 0664, dp); + return 1; +} + +/* + * specifier is the name of a device in /dev + */ +static Chan * +tinyfsattach(char *spec) +{ + Tfs *fs; + Chan *c; + volatile struct { Chan *cc; } rock; + int i; + char buf[KNAMELEN*3]; + + if(*spec == 0 || strchr(spec, '/') != nil) + error("bad specifier"); + + snprint(buf, sizeof(buf), "/dev/%s", spec); + rock.cc = namec(buf, Aopen, ORDWR, 0); + if(waserror()){ + cclose(rock.cc); + nexterror(); + } + + fs = 0; + for(i = 0; i < Maxfs; i++){ + fs = &tinyfs.fs[i]; + qlock(&fs->ql); + if(fs->r && eqchan(rock.cc, fs->c, 1)) + break; + qunlock(&fs->ql); + } + if(i < Maxfs){ + fs->r++; + qunlock(&fs->ql); + cclose(rock.cc); + } else { + for(fs = tinyfs.fs; fs < &tinyfs.fs[Maxfs]; fs++){ + qlock(&fs->ql); + if(fs->r == 0) + break; + qunlock(&fs->ql); + } + if(fs == &tinyfs.fs[Maxfs]) + error("too many tinyfs's"); + fs->c = rock.cc; + fs->r = 1; + fs->f = 0; + fs->nf = 0; + fs->fsize = 0; + tfsinit(fs); + qunlock(&fs->ql); + } + poperror(); + + c = devattach('F', spec); + c->dev = fs - tinyfs.fs; + c->qid.type = QTDIR; + c->qid.vers = 0; + + return c; +} + +static Walkqid* +tinyfswalk(Chan *c, Chan *nc, char **name, int nname) +{ + Tfs *fs; + Walkqid *wq; + + fs = &tinyfs.fs[c->dev]; + + qlock(&fs->ql); + wq = devwalk(c, nc, name, nname, 0, 0, tinyfsgen); + if(wq != nil && (nc = wq->clone) != nil && nc->qid.path != Qdir){ + fs = &tinyfs.fs[nc->dev]; + fs->f[nc->qid.path-1].r++; + } + qunlock(&fs->ql); + return wq; +} + +static int +tinyfsstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, 0, 0, tinyfsgen); +} + +static Chan * +tinyfsopen(Chan *c, int omode) +{ + Tfile *f; + volatile struct { Tfs *fs; } rock; + + rock.fs = &tinyfs.fs[c->dev]; + + if(c->qid.type & QTDIR){ + if(omode != OREAD) + error(Eperm); + } else { + qlock(&rock.fs->ql); + if(waserror()){ + qunlock(&rock.fs->ql); + nexterror(); + } + switch(omode){ + case OTRUNC|ORDWR: + case OTRUNC|OWRITE: + f = newfile(rock.fs, rock.fs->f[c->qid.path-1].name); + rock.fs->f[c->qid.path-1].r--; + c->qid.path = f - rock.fs->f; + break; + case OREAD: + break; + default: + error(Eperm); + } + qunlock(&rock.fs->ql); + poperror(); + } + + return devopen(c, omode, 0, 0, tinyfsgen); +} + +static void +tinyfscreate(Chan *c, char *name, int omode, ulong perm) +{ + volatile struct { Tfs *fs; } rock; + Tfile *f; + + USED(perm); + + rock.fs = &tinyfs.fs[c->dev]; + + qlock(&rock.fs->ql); + if(waserror()){ + qunlock(&rock.fs->ql); + nexterror(); + } + f = newfile(rock.fs, name); + qunlock(&rock.fs->ql); + poperror(); + + c->qid.path = (f - rock.fs->f)+1; + c->qid.vers = 0; + c->qid.type = QTFILE; + c->mode = openmode(omode); +} + +static void +tinyfsremove(Chan *c) +{ + Tfs *fs; + Tfile *f; + + if(c->qid.path == Qdir) + error(Eperm); + fs = &tinyfs.fs[c->dev]; + f = &fs->f[c->qid.path-1]; + qlock(&fs->ql); + freefile(fs, f, Notabno); + qunlock(&fs->ql); +} + +static void +tinyfsclose(Chan *c) +{ + volatile struct { Tfs *fs; } rock; + Tfile *f, *nf; + int i; + + rock.fs = &tinyfs.fs[c->dev]; + + qlock(&rock.fs->ql); + + /* dereference file and remove old versions */ + if(!waserror()){ + if(c->qid.path != Qdir){ + f = &rock.fs->f[c->qid.path-1]; + f->r--; + if(f->r == 0){ + if(f->flag & Frmonclose) + freefile(rock.fs, f, Notabno); + else if(f->flag & Fcreating){ + /* remove all other files with this name */ + for(i = 0; i < rock.fs->fsize; i++){ + nf = &rock.fs->f[i]; + if(f == nf) + continue; + if(strcmp(nf->name, f->name) == 0){ + if(nf->r) + nf->flag |= Frmonclose; + else + freefile(rock.fs, nf, Notabno); + } + } + f->flag &= ~Fcreating; + } + } + } + poperror(); + } + + /* dereference rock.fs and remove on zero refs */ + rock.fs->r--; + if(rock.fs->r == 0){ + if(rock.fs->f) + free(rock.fs->f); + rock.fs->f = 0; + rock.fs->nf = 0; + rock.fs->fsize = 0; + if(rock.fs->map) + free(rock.fs->map); + rock.fs->map = 0; + cclose(rock.fs->c); + rock.fs->c = 0; + } + qunlock(&rock.fs->ql); +} + +static long +tinyfsread(Chan *c, void *a, long n, vlong offset) +{ + volatile struct { Tfs *fs; } rock; + Tfile *f; + int sofar, i, off; + ulong bno; + Mdata *md; + uchar buf[Blen]; + uchar *p; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, tinyfsgen); + + p = a; + rock.fs = &tinyfs.fs[c->dev]; + f = &rock.fs->f[c->qid.path-1]; + if(offset >= f->length) + return 0; + + qlock(&rock.fs->ql); + if(waserror()){ + qunlock(&rock.fs->ql); + nexterror(); + } + if(n + offset >= f->length) + n = f->length - offset; + + /* walk to starting data block */ + if(0 && f->finger <= offset && f->fbno != Notabno){ + sofar = f->finger; + bno = f->fbno; + } else { + sofar = 0; + bno = f->dbno; + } + for(; sofar + Dlen <= offset; sofar += Dlen){ + md = readdata(rock.fs, bno, buf, 0); + if(md == 0) + error(Eio); + bno = GETS(md->bno); + } + + /* read data */ + off = offset%Dlen; + offset -= off; + for(sofar = 0; sofar < n; sofar += i){ + md = readdata(rock.fs, bno, buf, &i); + if(md == 0) + error(Eio); + + /* update finger for successful read */ + f->finger = offset; + f->fbno = bno; + offset += Dlen; + + i -= off; + if(i > n - sofar) + i = n - sofar; + memmove(p, md->data+off, i); + p += i; + bno = GETS(md->bno); + off = 0; + } + qunlock(&rock.fs->ql); + poperror(); + + return sofar; +} + +/* + * if we get a write error in this routine, blocks will + * be lost. They should be recovered next fsinit. + */ +static long +tinyfswrite(Chan *c, void *a, long n, vlong offset) +{ + Tfile *f; + int last, next, i, finger, off, used; + ulong bno, fbno; + Mdata *md; + uchar buf[Blen]; + uchar *p; + volatile struct { + Tfs *fs; + ulong dbno; + } rock; + + if(c->qid.type & QTDIR) + error(Eperm); + + if(n == 0) + return 0; + + p = a; + rock.fs = &tinyfs.fs[c->dev]; + f = &rock.fs->f[c->qid.path-1]; + + qlock(&rock.fs->ql); + rock.dbno = Notabno; + if(waserror()){ + freeblocks(rock.fs, rock.dbno, Notabno); + qunlock(&rock.fs->ql); + nexterror(); + } + + /* files are append only, anything else is illegal */ + if(offset != f->length) + error("append only"); + + /* write blocks backwards */ + p += n; + last = offset + n; + fbno = Notabno; + finger = 0; + off = offset; /* so we have something signed to compare against */ + for(next = ((last-1)/Dlen)*Dlen; next >= off; next -= Dlen){ + bno = mapalloc(rock.fs); + if(bno == Notabno) + error("out of space"); + i = last - next; + p -= i; + if(last == n+offset){ + writedata(rock.fs, bno, rock.dbno, p, i, 1); + finger = next; /* remember for later */ + fbno = bno; + } else { + writedata(rock.fs, bno, rock.dbno, p, i, 0); + } + rock.dbno = bno; + last = next; + } + + /* walk to last data block */ + md = (Mdata*)buf; + if(0 && f->finger < offset && f->fbno != Notabno){ + next = f->finger; + bno = f->fbno; + } else { + next = 0; + bno = f->dbno; + } + + used = 0; + while(bno != Notabno){ + md = readdata(rock.fs, bno, buf, &used); + if(md == 0) + error(Eio); + if(md->type == Tagend){ + if(next + Dlen < offset) + panic("devtinyfs1"); + break; + } + next += Dlen; + if(next > offset) + panic("devtinyfs1"); + bno = GETS(md->bno); + } + + /* point to new blocks */ + if(offset == 0){ + /* first block in a file */ + f->dbno = rock.dbno; + writedir(rock.fs, f); + } else { + /* updating a current block */ + i = last - offset; + if(i > 0){ + p -= i; + memmove(md->data + used, p, i); + used += i; + } + writedata(rock.fs, bno, rock.dbno, md->data, used, last == n+offset); + } + f->length += n; + + /* update finger */ + if(fbno != Notabno){ + f->finger = finger; + f->fbno = fbno; + } + poperror(); + qunlock(&rock.fs->ql); + + return n; +} + +Dev tinyfsdevtab = { + 'F', + "tinyfs", + + devinit, + tinyfsattach, + tinyfswalk, + tinyfsstat, + tinyfsopen, + tinyfscreate, + tinyfsclose, + tinyfsread, + devbread, + tinyfswrite, + devbwrite, + tinyfsremove, + devwstat +}; diff --git a/emu/port/devtk.c b/emu/port/devtk.c new file mode 100644 index 00000000..6f31967b --- /dev/null +++ b/emu/port/devtk.c @@ -0,0 +1,177 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + +#include <interp.h> + +#include "image.h" +#include <memimage.h> +#include <memlayer.h> +#include <cursor.h> + +enum{ + Qdir, + Qtkevents +}; + +static +Dirtab tkdirtab[]={ + "tkevents", {Qtkevents, 0}, 0, 0600, +}; + +static struct { + QLock l; + Queue* eq; + Ref inuse; +} tkevents; + +static void +tkwiretapper(void *top, char *cmd, char *result, void *image, Rectangle *rp) +{ + Block *b; + int n; + char *s, *e; + + n = 12; + if(cmd != nil) + n += strlen(cmd)+2+1; + if(result != nil) + n += strlen(result)+2+1; + if(image != nil) + n += 12; + if(rp != nil) + n += 4*20; + n++; + b = allocb(n); + if(b != nil){ + s = (char*)b->wp; + e = s+n; + s += snprint(s, e-s, "%p", top); + if(cmd != nil){ + *s++ = ' '; + *s++ = '['; + n = strlen(cmd); + memmove(s, cmd, n); + s += n; + *s++ = ']'; + } + /* ignore result for now */ + if(image != nil) + s += snprint(s, e-s, " %p", image); + if(rp != nil) + s += snprint(s, e-s, " %d %d %d %d", rp->min.x, rp->min.y, rp->max.x, rp->max.y); + b->wp = (uchar*)s; + release(); + qlock(&tkevents.l); + if(waserror()){ + freeb(b); + qunlock(&tkevents.l); + acquire(); + return; + } + if(tkevents.eq != nil) + qbwrite(tkevents.eq, b); + qunlock(&tkevents.l); + acquire(); + } +} + +void (*tkwiretap)(void*, char*, char*, void*, Rectangle*) = tkwiretapper; + +static Chan* +tkattach(char* spec) +{ + return devattach(L'τ', spec); +} + +static int +tkwalk(Chan* c, char* name) +{ + return devwalk(c, name, tkdirtab, nelem(tkdirtab), devgen); +} + +static void +tkstat(Chan* c, char* db) +{ + devstat(c, db, tkdirtab, nelem(tkdirtab), devgen); +} + +static Chan* +tkopen(Chan* c, int omode) +{ + if(c->qid.type & QTDIR) + return devopen(c, omode, tkdirtab, nelem(tkdirtab), devgen); + switch(c->qid.path){ + case Qtkevents: + c = devopen(c, omode, tkdirtab, nelem(tkdirtab), devgen); + qlock(&tkevents.l); + if(incref(&tkevents.inuse) != 1){ + qunlock(&tkevents.l); + error(Einuse); + } + if(tkevents.eq == nil) + tkevents.eq = qopen(256*1024, 0, nil, nil); + else + qreopen(tkevents.eq); + qunlock(&tkevents.l); + break; + } + return c; +} + +static void +tkclose(Chan* c) +{ + if(c->qid.type & QTDIR || (c->flag & COPEN) == 0) + return; + qlock(&tkevents.l); + if(decref(&tkevents.inuse) == 0) + qclose(tkevents.eq); + qunlock(&tkevents.l); +} + +static long +tkread(Chan* c, void* a, long n, vlong offset) +{ + USED(offset); + switch(c->qid.path & ~CHDIR){ + case Qdir: + return devdirread(c, a, n, tkdirtab, nelem(tkdirtab), devgen); + case Qtkevents: + return qread(tkevents.eq, a, n); + default: + n=0; + break; + } + return n; +} + +static long +tkwrite(Chan *c, void* a, long n, vlong offset) +{ + USED(c); USED(a); USED(n); USED(offset); + error(Ebadusefd); + return 0; +} + +Dev tkdevtab = { + L'τ', + "tk", + +// devreset, + devinit, + tkattach, +// devdetach, + devclone, + tkwalk, + tkstat, + tkopen, + devcreate, + tkclose, + tkread, + devbread, + tkwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/emu/port/dial.c b/emu/port/dial.c new file mode 100644 index 00000000..97930c16 --- /dev/null +++ b/emu/port/dial.c @@ -0,0 +1,414 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "kernel.h" + +typedef struct DS DS; + +static int call(char*, char*, DS*); +static int csdial(DS*); +static void _dial_string_parse(char*, DS*); +static int nettrans(char*, char*, int na, char*, int); + +enum +{ + Maxstring= 128, + Maxpath= 100 +}; + +struct DS +{ + char buf[Maxstring]; /* dist string */ + char *netdir; + char *proto; + char *rem; + char *local; /* other args */ + char *dir; + int *cfdp; +}; + +/* + * the dialstring is of the form '[/net/]proto!dest' + */ +int +kdial(char *dest, char *local, char *dir, int *cfdp) +{ + DS ds; + int rv; + char err[ERRMAX], alterr[ERRMAX]; + + ds.local = local; + ds.dir = dir; + ds.cfdp = cfdp; + + _dial_string_parse(dest, &ds); + if(ds.netdir) + return csdial(&ds); + + ds.netdir = "/net"; + rv = csdial(&ds); + if(rv >= 0) + return rv; + + err[0] = 0; + kerrstr(err, sizeof err); + if(strstr(err, "refused") != 0){ + kerrstr(err, sizeof err); + return rv; + } + + ds.netdir = "/net.alt"; + rv = csdial(&ds); + if(rv >= 0) + return rv; + + alterr[0] = 0; + kerrstr(alterr, sizeof err); + + if(strstr(alterr, "translate") || strstr(alterr, "does not exist")) + kerrstr(err, sizeof err); + else + kerrstr(alterr, sizeof alterr); + return rv; +} + +static int +csdial(DS *ds) +{ + int n, fd, rv; + char *p, buf[Maxstring], clone[Maxpath], err[ERRMAX], besterr[ERRMAX]; + + /* + * open connection server + */ + snprint(buf, sizeof(buf), "%s/cs", ds->netdir); + fd = kopen(buf, ORDWR); + if(fd < 0){ + /* no connection server, don't translate */ + snprint(clone, sizeof(clone), "%s/%s/clone", ds->netdir, ds->proto); + return call(clone, ds->rem, ds); + } + + /* + * ask connection server to translate + */ + sprint(buf, "%s!%s", ds->proto, ds->rem); + if(kwrite(fd, buf, strlen(buf)) < 0){ + kerrstr(err, sizeof err); + kclose(fd); + kwerrstr("%s (%s)", err, buf); + return -1; + } + + /* + * loop through each address from the connection server till + * we get one that works. + */ + *besterr = 0; + strcpy(err, Egreg); + rv = -1; + kseek(fd, 0, 0); + while((n = kread(fd, buf, sizeof(buf) - 1)) > 0){ + buf[n] = 0; + p = strchr(buf, ' '); + if(p == 0) + continue; + *p++ = 0; + rv = call(buf, p, ds); + if(rv >= 0) + break; + err[0] = 0; + kerrstr(err, sizeof err); + if(strstr(err, "does not exist") == 0) + memmove(besterr, err, sizeof besterr); + } + kclose(fd); + + if(rv < 0 && *besterr) + kerrstr(besterr, sizeof besterr); + else + kerrstr(err, sizeof err); + return rv; +} + +static int +call(char *clone, char *dest, DS *ds) +{ + int fd, cfd, n; + char name[Maxpath], data[Maxpath], err[ERRMAX], *p; + + cfd = kopen(clone, ORDWR); + if(cfd < 0){ + kerrstr(err, sizeof err); + kwerrstr("%s (%s)", err, clone); + return -1; + } + + /* get directory name */ + n = kread(cfd, name, sizeof(name)-1); + if(n < 0){ + kerrstr(err, sizeof err); + kclose(cfd); + kwerrstr("read %s: %s", clone, err); + return -1; + } + name[n] = 0; + for(p = name; *p == ' '; p++) + ; + sprint(name, "%ld", strtoul(p, 0, 0)); + p = strrchr(clone, '/'); + *p = 0; + if(ds->dir) + snprint(ds->dir, NETPATHLEN, "%s/%s", clone, name); + snprint(data, sizeof(data), "%s/%s/data", clone, name); + + /* connect */ + if(ds->local) + snprint(name, sizeof(name), "connect %s %s", dest, ds->local); + else + snprint(name, sizeof(name), "connect %s", dest); + if(kwrite(cfd, name, strlen(name)) < 0){ + err[0] = 0; + kerrstr(err, sizeof err); + kclose(cfd); + kwerrstr("%s (%s)", err, name); + return -1; + } + + /* open data connection */ + fd = kopen(data, ORDWR); + if(fd < 0){ + err[0] = 0; + kerrstr(err, sizeof err); + kwerrstr("%s (%s)", err, data); + kclose(cfd); + return -1; + } + if(ds->cfdp) + *ds->cfdp = cfd; + else + kclose(cfd); + + return fd; +} + +/* + * parse a dial string + */ +static void +_dial_string_parse(char *str, DS *ds) +{ + char *p, *p2; + + strncpy(ds->buf, str, Maxstring); + ds->buf[Maxstring-1] = 0; + + p = strchr(ds->buf, '!'); + if(p == 0) { + ds->netdir = 0; + ds->proto = "net"; + ds->rem = ds->buf; + } else { + if(*ds->buf != '/' && *ds->buf != '#'){ + ds->netdir = 0; + ds->proto = ds->buf; + } else { + for(p2 = p; *p2 != '/'; p2--) + ; + *p2++ = 0; + ds->netdir = ds->buf; + ds->proto = p2; + } + *p = 0; + ds->rem = p + 1; + } +} + +/* + * announce a network service. + */ +int +kannounce(char *addr, char *dir) +{ + int ctl, n, m; + char buf[NETPATHLEN]; + char buf2[Maxpath]; + char netdir[NETPATHLEN]; + char naddr[Maxpath]; + char *cp; + + /* + * translate the address + */ + if(nettrans(addr, naddr, sizeof(naddr), netdir, sizeof(netdir)) < 0) + return -1; + + /* + * get a control channel + */ + ctl = kopen(netdir, ORDWR); + if(ctl<0) + return -1; + cp = strrchr(netdir, '/'); + *cp = 0; + + /* + * find out which line we have + */ + n = sprint(buf, "%.*s/", sizeof buf, netdir); + m = kread(ctl, &buf[n], sizeof(buf)-n-1); + if(m <= 0){ + kclose(ctl); + return -1; + } + buf[n+m] = 0; + + /* + * make the call + */ + n = snprint(buf2, sizeof buf2, "announce %s", naddr); + if(kwrite(ctl, buf2, n)!=n){ + kclose(ctl); + return -1; + } + + /* + * return directory etc. + */ + if(dir) + strcpy(dir, buf); + return ctl; +} + +/* + * listen for an incoming call + */ +int +klisten(char *dir, char *newdir) +{ + int ctl, n, m; + char buf[NETPATHLEN]; + char *cp; + + /* + * open listen, wait for a call + */ + snprint(buf, sizeof buf, "%s/listen", dir); + ctl = kopen(buf, ORDWR); + if(ctl < 0) + return -1; + + /* + * find out which line we have + */ + strcpy(buf, dir); + cp = strrchr(buf, '/'); + *++cp = 0; + n = cp-buf; + m = kread(ctl, cp, sizeof(buf) - n - 1); + if(m <= 0){ + kclose(ctl); + return -1; + } + buf[n+m] = 0; + + /* + * return directory etc. + */ + if(newdir) + strcpy(newdir, buf); + return ctl; + +} + +/* + * perform the identity translation (in case we can't reach cs) + */ +static int +identtrans(char *netdir, char *addr, char *naddr, int na, char *file, int nf) +{ + char proto[Maxpath]; + char *p; + + USED(nf); + + /* parse the protocol */ + strncpy(proto, addr, sizeof(proto)); + proto[sizeof(proto)-1] = 0; + p = strchr(proto, '!'); + if(p) + *p++ = 0; + + snprint(file, nf, "%s/%s/clone", netdir, proto); + strncpy(naddr, p, na); + naddr[na-1] = 0; + + return 1; +} + +/* + * call up the connection server and get a translation + */ +static int +nettrans(char *addr, char *naddr, int na, char *file, int nf) +{ + int i, fd; + char buf[Maxpath]; + char netdir[NETPATHLEN]; + char *p, *p2; + long n; + + /* + * parse, get network directory + */ + p = strchr(addr, '!'); + if(p == 0){ + kwerrstr("bad dial string: %s", addr); + return -1; + } + if(*addr != '/'){ + strcpy(netdir, "/net"); + } else { + for(p2 = p; *p2 != '/'; p2--) + ; + i = p2 - addr; + if(i == 0 || i >= sizeof(netdir)){ + kwerrstr("bad dial string: %s", addr); + return -1; + } + strncpy(netdir, addr, i); + netdir[i] = 0; + addr = p2 + 1; + } + + /* + * ask the connection server + */ + sprint(buf, "%s/cs", netdir); + fd = kopen(buf, ORDWR); + if(fd < 0) + return identtrans(netdir, addr, naddr, na, file, nf); + if(kwrite(fd, addr, strlen(addr)) < 0){ + kclose(fd); + return -1; + } + kseek(fd, 0, 0); + n = kread(fd, buf, sizeof(buf)-1); + kclose(fd); + if(n <= 0) + return -1; + buf[n] = 0; + + /* + * parse the reply + */ + p = strchr(buf, ' '); + if(p == 0) + return -1; + *p++ = 0; + strncpy(naddr, p, na); + naddr[na-1] = 0; + strncpy(file, buf, nf); + file[nf-1] = 0; + return 0; +} diff --git a/emu/port/dis.c b/emu/port/dis.c new file mode 100644 index 00000000..7f8db7b2 --- /dev/null +++ b/emu/port/dis.c @@ -0,0 +1,1138 @@ +#include "dat.h" +#include "fns.h" +#include <isa.h> +#include <interp.h> +#include <kernel.h> +#include "error.h" +#include "raise.h" + +struct +{ + Lock l; + Prog* runhd; + Prog* runtl; + Prog* head; + Prog* tail; + Rendez irend; + int idle; + int nyield; + int creating; + Proc* vmq; /* queue of procs wanting vm */ + Proc* vmqt; + Proc* idlevmq; /* queue of procs wanting work */ + Atidle* idletasks; +} isched; + +int bflag; +int cflag; +uvlong gcbusy; +uvlong gcidle; +uvlong gcidlepass; +uvlong gcpartial; +int keepbroken = 1; +extern int vflag; +static Prog* proghash[64]; + +static Progs* delgrp(Prog*); +static void addgrp(Prog*, Prog*); +void printgrp(Prog*, char*); + +static Prog** +pidlook(int pid) +{ + ulong h; + Prog **l; + + h = (ulong)pid % nelem(proghash); + for(l = &proghash[h]; *l != nil && (*l)->pid != pid; l = &(*l)->pidlink) + ; + return l; +} + +int +tready(void *a) +{ + USED(a); + return isched.runhd != nil || isched.vmq != nil; +} + +Prog* +progpid(int pid) +{ + return *pidlook(pid); +} + +Prog* +progn(int n) +{ + Prog *p; + + for(p = isched.head; p && n--; p = p->next) + ; + return p; +} + +int +nprog(void) +{ + int n; + Prog *p; + + n = 0; + for(p = isched.head; p; p = p->next) + n++; + return n; +} + +static void +execatidle(void) +{ + int done; + + if(tready(nil)) + return; + + gcidle++; + up->type = IdleGC; + up->iprog = nil; + addrun(up->prog); + done = gccolor+3; + while(gccolor < done && gcruns()) { + if(isched.vmq != nil || isched.runhd != isched.runtl) { + gcpartial++; + break; + } + rungc(isched.head); + gcidlepass++; + osyield(); + } + up->type = Interp; + delrunq(up->prog); +} + +Prog* +newprog(Prog *p, Modlink *m) +{ + Heap *h; + Prog *n, **ph; + Osenv *on, *op; + static int pidnum; + + if(p != nil){ + if(p->group != nil) + p->flags |= p->group->flags & Pkilled; + if(p->kill != nil) + error(p->kill); + if(p->flags & Pkilled) + error(""); + } + n = malloc(sizeof(Prog)+sizeof(Osenv)); + if(n == 0){ + if(p == nil) + panic("no memory"); + else + error(exNomem); + } + + n->pid = ++pidnum; + if(n->pid <= 0) + panic("no pids"); + n->group = nil; + + if(isched.tail != nil) { + n->prev = isched.tail; + isched.tail->next = n; + } + else { + isched.head = n; + n->prev = nil; + } + isched.tail = n; + + ph = pidlook(n->pid); + if(*ph != nil) + panic("dup pid"); + n->pidlink = nil; + *ph = n; + + n->osenv = (Osenv*)((uchar*)n + sizeof(Prog)); + n->xec = xec; + n->quanta = PQUANTA; + n->flags = 0; + n->exval = H; + + h = D2H(m); + h->ref++; + Setmark(h); + n->R.M = m; + n->R.MP = m->MP; + if(m->MP != H) + Setmark(D2H(m->MP)); + addrun(n); + + if(p == nil){ + newgrp(n); + return n; + } + + addgrp(n, p); + n->flags = p->flags; + if(p->flags & Prestrict) + n->flags |= Prestricted; + memmove(n->osenv, p->osenv, sizeof(Osenv)); + op = p->osenv; + on = n->osenv; + on->waitq = op->childq; + on->childq = nil; + on->debug = nil; + incref(&on->pgrp->r); + incref(&on->fgrp->r); + incref(&on->egrp->r); + if(on->sigs != nil) + incref(&on->sigs->r); + on->user = nil; + kstrdup(&on->user, op->user); + on->errstr = on->errbuf0; + on->syserrstr = on->errbuf1; + + return n; +} + +void +delprog(Prog *p, char *msg) +{ + Osenv *o; + Prog **ph; + + tellsomeone(p, msg); /* call before being removed from prog list */ + + o = p->osenv; + release(); + closepgrp(o->pgrp); + closefgrp(o->fgrp); + closeegrp(o->egrp); + closesigs(o->sigs); + acquire(); + + delgrp(p); + + if(p->prev) + p->prev->next = p->next; + else + isched.head = p->next; + + if(p->next) + p->next->prev = p->prev; + else + isched.tail = p->prev; + + ph = pidlook(p->pid); + if(*ph == nil) + panic("lost pid"); + *ph = p->pidlink; + + if(p == isched.runhd) { + isched.runhd = p->link; + if(p->link == nil) + isched.runtl = nil; + } + p->state = 0xdeadbeef; + free(o->user); + free(p->killstr); + free(p->exstr); + free(p); +} + +void +renameproguser(char *old, char *new) +{ + Prog *p; + Osenv *o; + + acquire(); + for(p = isched.head; p; p = p->next){ + o = p->osenv; + if(o->user != nil && strcmp(o->user, old) == 0) + kstrdup(&o->user, new); + } + release(); +} + +void +tellsomeone(Prog *p, char *buf) +{ + Osenv *o; + + if(waserror()) + return; + o = p->osenv; + if(o->childq != nil) + qproduce(o->childq, buf, strlen(buf)); + if(o->waitq != nil) + qproduce(o->waitq, buf, strlen(buf)); + poperror(); +} + +static void +swiprog(Prog *p) +{ + Proc *q; + + lock(&procs.l); + for(q = procs.head; q; q = q->next) { + if(q->iprog == p) { + unlock(&procs.l); + swiproc(q, 1); + return; + } + } + unlock(&procs.l); + /*print("didn't find\n");*/ +} + +static Prog* +grpleader(Prog *p) +{ + Progs *g; + Prog *l; + + g = p->group; + if(g != nil && (l = g->head) != nil && l->pid == g->id) + return l; + return nil; +} + +int +exprog(Prog *p, char *exc) +{ + /* similar code to killprog but not quite */ + switch(p->state) { + case Palt: + altdone(p->R.s, p, nil, -1); + break; + case Psend: + cqdelp(&p->chan->send, p); + break; + case Precv: + cqdelp(&p->chan->recv, p); + break; + case Pready: + break; + case Prelease: + swiprog(p); + break; + case Pexiting: + case Pbroken: + case Pdebug: + return 0; + default: + panic("exprog - bad state 0x%x\n", p->state); + } + if(p->state != Pready && p->state != Prelease) + addrun(p); + if(p->kill == nil){ + if(p->killstr == nil){ + p->killstr = malloc(ERRMAX); + if(p->killstr == nil){ + p->kill = Enomem; + return 1; + } + } + kstrcpy(p->killstr, exc, ERRMAX); + p->kill = p->killstr; + } + return 1; +} + +static void +propex(Prog *p, char *estr) +{ + Prog *f, *nf, *pgl; + + if(!(p->flags & (Ppropagate|Pnotifyleader)) || p->group == nil) + return; + if(*estr == 0){ + if((p->flags & Pkilled) == 0) + return; + estr = "killed"; + } + pgl = grpleader(p); + if(pgl == nil) + pgl = p; + if(!(pgl->flags & (Ppropagate|Pnotifyleader))) + return; /* exceptions are local; don't propagate */ + for(f = p->group->head; f != nil; f = nf){ + nf = f->grpnext; + if(f != p && f != pgl){ + if(pgl->flags & Ppropagate) + exprog(f, estr); + else{ + f->flags &= ~(Ppropagate|Pnotifyleader); /* prevent recursion */ + killprog(f, "killed"); + } + } + } + if(p != pgl) + exprog(pgl, estr); +} + +int +killprog(Prog *p, char *cause) +{ + Osenv *env; + char msg[ERRMAX+2*KNAMELEN]; + + if(p == isched.runhd) { + p->kill = ""; + p->flags |= Pkilled; + p->state = Pexiting; + return 0; + } + + switch(p->state) { + case Palt: + altdone(p->R.s, p, nil, -1); + break; + case Psend: + cqdelp(&p->chan->send, p); + break; + case Precv: + cqdelp(&p->chan->recv, p); + break; + case Pready: + delrunq(p); + break; + case Prelease: + p->kill = ""; + p->flags |= Pkilled; + p->state = Pexiting; + swiprog(p); + /* No break */ + case Pexiting: + return 0; + case Pbroken: + case Pdebug: + break; + default: + panic("killprog - bad state 0x%x\n", p->state); + } + + if(p->addrun != nil) { + p->kill = ""; + p->flags |= Pkilled; + p->addrun(p); + p->addrun = nil; + return 0; + } + + env = p->osenv; + if(env->debug != nil) { + p->state = Pbroken; + dbgexit(p, 0, cause); + return 0; + } + + propex(p, "killed"); + + snprint(msg, sizeof(msg), "%d \"%s\":%s", p->pid, p->R.M->m->name, cause); + + p->state = Pexiting; + gclock(); + destroystack(&p->R); + delprog(p, msg); + gcunlock(); + + return 1; +} + +void +newgrp(Prog *p) +{ + Progs *pg, *g; + + if(p->group != nil && p->group->id == p->pid) + return; + g = malloc(sizeof(*g)); + if(g == nil) + error(Enomem); + p->flags &= ~(Ppropagate|Pnotifyleader); + g->id = p->pid; + g->flags = 0; + g->child = nil; + pg = delgrp(p); + g->head = g->tail = p; + p->group = g; + if(pg != nil){ + g->sib = pg->child; + pg->child = g; + } + g->parent = pg; +} + +static void +addgrp(Prog *n, Prog *p) +{ + Progs *g; + + n->group = p->group; + if((g = n->group) != nil){ + n->grpnext = nil; + if(g->head != nil){ + n->grpprev = g->tail; + g->tail->grpnext = n; + }else{ + n->grpprev = nil; + g->head = n; + } + g->tail = n; + } +} + +static Progs* +delgrp(Prog *p) +{ + Progs *g, *pg, *cg, **l; + + g = p->group; + if(g == nil) + return nil; + if(p->grpprev) + p->grpprev->grpnext = p->grpnext; + else + g->head = p->grpnext; + if(p->grpnext) + p->grpnext->grpprev = p->grpprev; + else + g->tail = p->grpprev; + p->grpprev = p->grpnext = nil; + p->group = nil; + + if(g->head == nil){ + /* move up, giving subgroups of groups with no Progs to their parents */ + do{ + if((pg = g->parent) != nil){ + pg = g->parent; + for(l = &pg->child; *l != nil && *l != g; l = &(*l)->sib) + ; + *l = g->sib; + } + /* put subgroups in new parent group */ + while((cg = g->child) != nil){ + g->child = cg->sib; + cg->parent = pg; + if(pg != nil){ + cg->sib = pg->child; + pg->child = cg; + } + } + free(g); + }while((g = pg) != nil && g->head == nil); + } + return g; +} + +void +printgrp(Prog *p, char *v) +{ + Progs *g; + Prog *q; + + g = p->group; + print("%s pid %d grp %d pgrp %d: [pid", v, p->pid, g->id, g->parent!=nil?g->parent->id:0); + for(q = g->head; q != nil; q = q->grpnext) + print(" %d", q->pid); + print(" subgrp"); + for(g = g->child; g != nil; g = g->sib) + print(" %d", g->id); + print("]\n"); +} + +int +killgrp(Prog *p, char *msg) +{ + int i, npid, *pids; + Prog *f; + Progs *g; + + /* interpreter has been acquired */ + g = p->group; + if(g == nil || g->head == nil) + return 0; + while(g->flags & Pkilled){ + release(); + acquire(); + } + npid = 0; + for(f = g->head; f != nil; f = f->grpnext) + if(f->group != g) + panic("killgrp"); + else + npid++; + /* use pids not Prog* because state can change during killprog (eg, in delprog) */ + pids = malloc(npid*sizeof(int)); + if(pids == nil) + error(Enomem); + npid = 0; + for(f = g->head; f != nil; f = f->grpnext) + pids[npid++] = f->pid; + g->flags |= Pkilled; + if(waserror()) { + g->flags &= ~Pkilled; + free(pids); + nexterror(); + } + for(i = 0; i < npid; i++) { + f = progpid(pids[i]); + if(f != nil && f != currun()) + killprog(f, msg); + } + poperror(); + g->flags &= ~Pkilled; + free(pids); + return 1; +} + +char changup[] = "channel hangup"; + +void +killcomm(Progq **q) +{ + Prog *p; + Progq *f; + + for (f = *q; f != nil; f = *q) { + *q = f->next; + p = f->prog; + free(f); + if(p == nil) + return; + p->ptr = nil; + switch(p->state) { + case Prelease: + swiprog(p); + break; + case Psend: + case Precv: + p->kill = changup; + addrun(p); + break; + case Palt: + altgone(p); + break; + } + } +} + +void +addprog(Proc *p) +{ + Prog *n; + + n = malloc(sizeof(Prog)); + if(n == nil) + panic("no memory"); + p->prog = n; + n->osenv = p->env; +} + +static void +cwakeme(Prog *p) +{ + Osenv *o; + + p->addrun = nil; + o = p->osenv; + Wakeup(o->rend); +} + +static int +cdone(void *vp) +{ + Prog *p = vp; + + return p->addrun == nil || p->kill != nil; +} + +void +cblock(Prog *p) +{ + Osenv *o; + + p->addrun = cwakeme; + o = p->osenv; + o->rend = &up->sleep; + release(); + + /* + * To allow cdone(p) safely after release, + * p must be currun before the release. + * Exits in the error case with the vm acquired. + */ + if(waserror()) { + acquire(); + p->addrun = nil; + nexterror(); + } + Sleep(o->rend, cdone, p); + if (p->kill != nil) + error(Eintr); + poperror(); + acquire(); +} + +void +addrun(Prog *p) +{ + if(p->addrun != 0) { + p->addrun(p); + return; + } + p->state = Pready; + p->link = nil; + if(isched.runhd == nil) + isched.runhd = p; + else + isched.runtl->link = p; + + isched.runtl = p; +} + +Prog* +delrun(int state) +{ + Prog *p; + + p = isched.runhd; + p->state = state; + isched.runhd = p->link; + if(p->link == nil) + isched.runtl = nil; + + return p; +} + +void +delrunq(Prog *p) +{ + Prog *prev, *f; + + prev = nil; + for(f = isched.runhd; f; f = f->link) { + if(f == p) + break; + prev = f; + } + if(f == nil) + return; + if(prev == nil) + isched.runhd = p->link; + else + prev->link = p->link; + if(p == isched.runtl) + isched.runtl = prev; +} + +Prog* +delruntail(int state) +{ + Prog *p; + + p = isched.runtl; + delrunq(p); + p->state = state; + return p; +} + +Prog* +currun(void) +{ + return isched.runhd; +} + +Prog* +schedmod(Module *m) +{ + Heap *h; + Type *t; + Prog *p; + Modlink *ml; + Frame f, *fp; + + ml = mklinkmod(m, 0); + + if(m->origmp != H && m->ntype > 0) { + t = m->type[0]; + h = nheap(t->size); + h->t = t; + t->ref++; + ml->MP = H2D(uchar*, h); + newmp(ml->MP, m->origmp, t); + } + + p = newprog(nil, ml); + h = D2H(ml); + h->ref--; + p->R.PC = m->entry; + fp = &f; + R.s = &fp; + f.t = m->entryt; + newstack(p); + initmem(m->entryt, p->R.FP); + + return p; +} + +/* +static char* +m(Prog *p) +{ + if(p) + if(p->R.M) + if(p->R.M->m) + return p->R.M->m->name; + return "nil"; +} +*/ + +void +acquire(void) +{ + int empty; + Prog *p; + + lock(&isched.l); + if(isched.idle) { + isched.idle = 0; + unlock(&isched.l); + } + else { + up->qnext = nil; + if(isched.vmq != nil){ + empty = 0; + isched.vmqt->qnext = up; + }else{ + isched.vmq = up; + empty = 1; + } + isched.vmqt = up; + + unlock(&isched.l); + strcpy(up->text, "acquire"); + if(empty) + Wakeup(&isched.irend); + osblock(); + } + + if(up->type == Interp) { + p = up->iprog; + up->iprog = nil; + irestore(p); + } + else + p = up->prog; + + p->state = Pready; + p->link = isched.runhd; + isched.runhd = p; + if(p->link == nil) + isched.runtl = p; + + strcpy(up->text, "dis"); +} + +void +release(void) +{ + Proc *p, **pq; + int f; + + if(up->type == Interp) + up->iprog = isave(); + else + delrun(Prelease); + + lock(&isched.l); + if(*(pq = &isched.vmq) == nil && *(pq = &isched.idlevmq) == nil) { + isched.idle = 1; + f = isched.creating; + isched.creating = 1; + unlock(&isched.l); + if(f == 0) + kproc("dis", vmachine, nil, 0); + return; + } + p = *pq; + *pq = p->qnext; + unlock(&isched.l); + + osready(p); /* wake up thread to run VM */ + strcpy(up->text, "released"); +} + +void +iyield(void) +{ + Proc *p; + + lock(&isched.l); + p = isched.vmq; + if(p == nil) { + unlock(&isched.l); + return; + } + isched.nyield++; + isched.vmq = p->qnext; + + if(up->iprog != nil) + panic("iyield but iprog, type %d", up->type); + if(up->type != Interp){ + static int once; + if(!once++) + print("tell charles: #%p->type==%d\n", up, up->type); + } + up->qnext = isched.idlevmq; + isched.idlevmq = up; + + unlock(&isched.l); + osready(p); /* wake up acquiring kproc */ + strcpy(up->text, "yield"); + osblock(); /* sleep */ + strcpy(up->text, "dis"); +} + +void +startup(void) +{ + + up->type = Interp; + up->iprog = nil; + + lock(&isched.l); + isched.creating = 0; + if(isched.idle) { + isched.idle = 0; + unlock(&isched.l); + return; + } + up->qnext = isched.idlevmq; + isched.idlevmq = up; + unlock(&isched.l); + + osblock(); +} + +void +progexit(void) +{ + Prog *r; + Module *m; + int broken; + char *estr, msg[ERRMAX+2*KNAMELEN]; + + estr = up->env->errstr; + broken = 0; + if(estr[0] != '\0' && strcmp(estr, Eintr) != 0 && strncmp(estr, "fail:", 5) != 0) + broken = 1; + + r = up->iprog; + if(r != nil) + acquire(); + else + r = currun(); + + if(*estr == '\0' && r->flags & Pkilled) + estr = "killed"; + + m = R.M->m; + if(broken){ + if(cflag){ /* only works on Plan9 for now */ + char *pc = strstr(estr, "pc="); + + if(pc != nil) + R.PC = r->R.PC = (Inst*)strtol(pc+3, nil, 0); /* for debugging */ + } + print("[%s] Broken: \"%s\"\n", m->name, estr); + } + + snprint(msg, sizeof(msg), "%d \"%s\":%s", r->pid, m->name, estr); + + if(up->env->debug != nil) { + dbgexit(r, broken, estr); + broken = 1; + /* must force it to break if in debug */ + }else if(broken && (!keepbroken || strncmp(estr, "out of memory", 13)==0 || memusehigh())) + broken = 0; /* don't want them or short of memory */ + + if(broken){ + tellsomeone(r, msg); + r = isave(); + r->state = Pbroken; + return; + } + + gclock(); + destroystack(&R); + delprog(r, msg); + gcunlock(); + + if(isched.head == nil) + cleanexit(0); +} + +void +disfault(void *reg, char *msg) +{ + Prog *p; + + USED(reg); + + if(strncmp(msg, Eintr, 6) == 0) + exits(0); + + if(up == nil) { + print("EMU: faults: %s\n", msg); + cleanexit(0); + } + if(up->type != Interp) { + print("SYS: process %s faults: %s\n", up->text, msg); + cleanexit(0); + } + + if(up->iprog != nil) + acquire(); + + p = currun(); + if(p == nil) + panic("Interp faults with no dis prog"); + + /* cause an exception in the dis prog. As for error(), but Plan 9 needs reg*/ + kstrcpy(up->env->errstr, msg, ERRMAX); + oslongjmp(reg, up->estack[--up->nerr], 1); +} + +void +vmachine(void *a) +{ + Prog *r; + Osenv *o; + int cycles; + static int gccounter; + + USED(a); + + startup(); + + while(waserror()) { + if(up->iprog != nil) + acquire(); + if(handler(up->env->errstr) == 0) { + propex(currun(), up->env->errstr); + progexit(); + } + up->env = &up->defenv; + } + + cycles = 0; + for(;;) { + if(tready(nil) == 0) { + execatidle(); + strcpy(up->text, "idle"); + Sleep(&isched.irend, tready, 0); + strcpy(up->text, "dis"); + } + + if(isched.vmq != nil && (isched.runhd == nil || ++cycles > 2)){ + iyield(); + cycles = 0; + } + + r = isched.runhd; + if(r != nil) { + o = r->osenv; + up->env = o; + + FPrestore(&o->fpu); + r->xec(r); + FPsave(&o->fpu); + + if(isched.runhd != nil) + if(r == isched.runhd) + if(isched.runhd != isched.runtl) { + isched.runhd = r->link; + r->link = nil; + isched.runtl->link = r; + isched.runtl = r; + } + up->env = &up->defenv; + } + if(isched.runhd != nil) + if((++gccounter&0xFF) == 0 || memlow()) { + gcbusy++; + up->type = BusyGC; + pushrun(up->prog); + rungc(isched.head); + up->type = Interp; + delrunq(up->prog); + } + } +} + +void +disinit(void *a) +{ + Prog *p; + Osenv *o; + Module *root; + char *initmod = a; + + if(waserror()) + panic("disinit error: %r"); + + if(vflag) + print("Initial Dis: \"%s\"\n", initmod); + + fmtinstall('D', Dconv); + + FPinit(); + FPsave(&up->env->fpu); + + opinit(); + modinit(); + excinit(); + + root = load(initmod); + if(root == 0) { + kgerrstr(up->genbuf, sizeof up->genbuf); + panic("loading \"%s\": %s", initmod, up->genbuf); + } + + p = schedmod(root); + + memmove(p->osenv, up->env, sizeof(Osenv)); + o = p->osenv; + incref(&o->pgrp->r); + incref(&o->fgrp->r); + incref(&o->egrp->r); + if(o->sigs != nil) + incref(&o->sigs->r); + o->user = nil; + kstrdup(&o->user, up->env->user); + o->errstr = o->errbuf0; + o->syserrstr = o->errbuf1; + + isched.idle = 1; + poperror(); + vmachine(nil); +} + +void +pushrun(Prog *p) +{ + if(p->addrun != nil) + panic("pushrun addrun"); + p->state = Pready; + p->link = isched.runhd; + isched.runhd = p; + if(p->link == nil) + isched.runtl = p; +} diff --git a/emu/port/discall.c b/emu/port/discall.c new file mode 100644 index 00000000..56470d77 --- /dev/null +++ b/emu/port/discall.c @@ -0,0 +1,179 @@ +#include "dat.h" +#include "fns.h" +#include <interp.h> + +#define QP(l) (Prog**)((char*)(l)+sizeof(QLock)) + +void* +libqlowner(void *l) +{ + return *QP(l); +} + +void +libqlock(void *l) +{ + Prog *p; + QLock *q; + + q = l; + p = currun(); + if(p == nil) + abort(); + + if(!canqlock(q)) { + release(); + qlock(q); + acquire(); + } + *QP(l) = p; +} + +void +libqunlock(void *l) +{ + Prog *p; + QLock *q; + + q = l; + p = currun(); + if(p == nil) + abort(); + if(*QP(l) != p) + abort(); + + *QP(l) = nil; + qunlock(q); +} + +void* +libqlalloc(void) +{ + QLock *q; + + q = mallocz(sizeof(QLock)+sizeof(Prog*), 1); + return q; +} + +void +libqlfree(void *l) +{ + free(l); +} + +vlong +libseek(int fd, vlong off, int whence) +{ + release(); + off = kseek(fd, off, whence); + acquire(); + return off; +} + +int +libread(int fd, void *buf, int n) +{ + release(); + n = kread(fd, buf, n); + acquire(); + return n; +} + +int +libreadn(int fd, void *av, long n) +{ + char *a; + long m, t; + + a = av; + t = 0; + release(); + while(t < n){ + m = kread(fd, a+t, n-t); + if(m <= 0){ + if(t == 0){ + acquire(); + return m; + } + break; + } + t += m; + } + acquire(); + return t; +} + +int +libwrite(int fd, void *buf, int n) +{ + release(); + n = kwrite(fd, buf, n); + acquire(); + return n; +} + +int +libopen(char *name, int omode) +{ + int fd; + + release(); + fd = kopen(name, omode); + acquire(); + return fd; +} + +int +libclose(int fd) +{ + release(); + fd = kclose(fd); + acquire(); + return fd; +} + +Dir* +libdirfstat(int fd) +{ + Dir *d; + + release(); + d = kdirfstat(fd); + acquire(); + return d; +} + +int +libbind(char *s, char *t, int f) +{ + int n; + + release(); + n = kbind(s, t, f); + acquire(); + return n; +} + +void +libchanclose(void *chan) +{ + release(); + cclose(chan); + acquire(); +} + +void* +libfdtochan(int fd, int mode) +{ + Chan *c; + + release(); + if(waserror()) { + acquire(); + return nil; + } + c = fdtochan(up->env->fgrp, fd, mode, 0, 1); + poperror(); + acquire(); + return c; +} diff --git a/emu/port/dynld.c b/emu/port/dynld.c new file mode 100644 index 00000000..6c5bf8e4 --- /dev/null +++ b/emu/port/dynld.c @@ -0,0 +1,52 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include <a.out.h> +#include <dynld.h> + +/* + * kernel interface to dynld, for use by devdynld.c, + * libinterp/dlm.c, and possibly others + */ + +typedef struct Fd Fd; +struct Fd { + int fd; +}; + +static long +readfd(void *a, void *buf, long nbytes) +{ + return kread(((Fd*)a)->fd, buf, nbytes); +} + +static vlong +seekfd(void *a, vlong off, int t) +{ + return kseek(((Fd*)a)->fd, off, t); +} + +static void +errfd(char *s) +{ + kstrcpy(up->env->errstr, s, ERRMAX); +} + +Dynobj* +kdynloadfd(int fd, Dynsym *tab, int ntab) +{ + Dynobj *o; + Fd f; + + f.fd = fd; + return dynloadgen(&f, readfd, seekfd, errfd, tab, ntab, 0); +} + +int +kdynloadable(int fd) +{ + Fd f; + + f.fd = fd; + return dynloadable(&f, readfd, seekfd); +} diff --git a/emu/port/dynldc.c b/emu/port/dynldc.c new file mode 100644 index 00000000..2237f7ab --- /dev/null +++ b/emu/port/dynldc.c @@ -0,0 +1,65 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include <a.out.h> +#include <dynld.h> + +/* + * channel-based kernel interface to dynld, for use by devdynld.c, + * libinterp/dlm.c, and possibly others + */ + +static long +readfc(void *a, void *buf, long nbytes) +{ + Chan *c = a; + + if(waserror()) + return -1; + nbytes = devtab[c->type]->read(c, buf, nbytes, c->offset); + poperror(); + return nbytes; +} + +static vlong +seekfc(void *a, vlong off, int t) +{ + Chan *c = a; + + if(c->qid.type & QTDIR || off < 0) + return -1; /* won't happen */ + switch(t){ + case 0: + lock(c); + c->offset = off; + unlock(c); + break; + case 1: + lock(c); + off += c->offset; + c->offset = off; + unlock(c); + break; + case 2: + return -1; /* not needed */ + } + return off; +} + +static void +errfc(char *s) +{ + kstrcpy(up->env->errstr, s, ERRMAX); +} + +Dynobj* +kdynloadchan(Chan *c, Dynsym *tab, int ntab) +{ + return dynloadgen(c, readfc, seekfc, errfc, tab, ntab, 0); +} + +int +kdynloadable(Chan *c) +{ + return dynloadable(c, readfc, seekfc); +} diff --git a/emu/port/env.c b/emu/port/env.c new file mode 100644 index 00000000..36ca5636 --- /dev/null +++ b/emu/port/env.c @@ -0,0 +1,77 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + +Egrp* +newegrp(void) +{ + Egrp *e; + + e = smalloc(sizeof(Egrp)); + if (e == nil) + error(Enomem); + e->r.ref = 1; + return e; +} + +void +closeegrp(Egrp *e) +{ + Evalue *el, *nl; + + if(e == nil || decref(&e->r) != 0) + return; + for (el = e->entries; el != nil; el = nl) { + free(el->var); + if (el->val) + free(el->val); + nl = el->next; + free(el); + } + free(e); +} + +void +egrpcpy(Egrp *to, Egrp *from) +{ + Evalue *e, *ne, **last; + + last = &to->entries; + qlock(&from->l); + for (e = from->entries; e != nil; e = e->next) { + ne = smalloc(sizeof(Evalue)); + ne->var = smalloc(strlen(e->var)+1); + strcpy(ne->var, e->var); + if (e->val) { + ne->val = smalloc(e->len); + memmove(ne->val, e->val, e->len); + ne->len = e->len; + } + ne->qid.path = ++to->path; + *last = ne; + last = &ne->next; + } + qunlock(&from->l); +} + +void +ksetenv(char *var, char *val, int conf) +{ + Chan *c; + char buf[2*KNAMELEN]; + + USED(conf); + snprint(buf, sizeof(buf), "#e/%s", var); + if(waserror()) + return; + c = namec(buf, Acreate, OWRITE, 0600); + poperror(); + if(!waserror()){ + if(!waserror()){ + devtab[c->type]->write(c, val, strlen(val), 0); + poperror(); + } + poperror(); + } + cclose(c); +} diff --git a/emu/port/error.c b/emu/port/error.c new file mode 100644 index 00000000..ad1342fc --- /dev/null +++ b/emu/port/error.c @@ -0,0 +1,66 @@ +char Enoerror[] = "no error"; +char Emount[] = "inconsistent mount"; +char Eunmount[] = "not mounted"; +char Eunion[] = "not in union"; +char Emountrpc[] = "mount rpc error"; +char Eshutdown[] = "mounted device shut down"; +char Eowner[] = "not owner"; +char Eunknown[] = "unknown user or group id"; +char Enocreate[] = "mounted directory forbids creation"; +char Enonexist[] = "file does not exist"; +char Eexist[] = "file already exists"; +char Ebadsharp[] = "unknown device in # filename"; +char Enotdir[] = "not a directory"; +char Eisdir[] = "file is a directory"; +char Ebadchar[] = "bad character in file name"; +char Efilename[] = "file name syntax"; +char Eperm[] = "permission denied"; +char Ebadusefd[] = "inappropriate use of fd"; +char Ebadarg[] = "bad arg in system call"; +char Einuse[] = "device or object already in use"; +char Eio[] = "i/o error"; +char Etoobig[] = "read or write too large"; +char Etoosmall[] = "read or write too small"; +char Enetaddr[] = "bad network address"; +char Emsgsize[] = "message is too big for protocol"; +char Enetbusy[] = "network device is busy or allocated"; +char Enoproto[] = "network protocol not supported"; +char Enoport[] = "network port not available"; +char Enoifc[] = "bad interface or no free interface slots"; +char Enolisten[] = "not announced"; +char Ehungup[] = "i/o on hungup channel"; +char Ebadctl[] = "bad process or channel control request"; +char Enodev[] = "no free devices"; +char Enoenv[] = "no free environment resources"; +char Emuxshutdown[] = "mux server shut down"; +char Emuxbusy[] = "all mux channels busy"; +char Emuxmsg[] = "bad mux message format or mismatch"; +char Ethread[] = "thread exited"; +char Enochild[] = "no living children"; +char Eioload[] = "i/o error in demand load"; +char Enovmem[] = "out of memory: virtual memory"; +char Ebadld[] = "illegal line discipline"; +char Ebadfd[] = "fd out of range or not open"; +char Eisstream[] = "seek on a stream"; +char Ebadexec[] = "exec header invalid"; +char Etimedout[] = "connection timed out"; +char Econrefused[] = "connection refused"; +char Econinuse[] = "connection in use"; +char Enetunreach[] = "network unreachable"; +char Eintr[] = "interrupted"; +char Enomem[] = "out of memory: kernel"; +char Esfnotcached[] = "subfont not cached"; +char Esoverlap[] = "segments overlap"; +char Emouseset[] = "mouse type already set"; +char Eshort[] = "i/o count too small"; +/* char Enobitstore[] = "out of screen memory"; */ +char Egreg[] = "jim'll fix it"; +char Ebadspec[] = "bad attach specifier"; +char Estopped[] = "thread must be stopped"; +char Enoattach[] = "mount/attach disallowed"; +char Eshortstat[] = "stat buffer too small"; +char Enegoff[] = "negative i/o offset"; +char Ebadstat[] = "malformed stat buffer"; +char Ecmdargs[] = "wrong #args in control message"; +char Enofd[] = "no free file descriptors"; +char Enoctl[] = "unknown control request"; diff --git a/emu/port/error.h b/emu/port/error.h new file mode 100644 index 00000000..e6d61d9d --- /dev/null +++ b/emu/port/error.h @@ -0,0 +1,65 @@ +extern char Enoerror[]; /* no error */ +extern char Emount[]; /* inconsistent mount */ +extern char Eunmount[]; /* not mounted */ +extern char Eunion[]; /* not in union */ +extern char Emountrpc[]; /* mount rpc error */ +extern char Eshutdown[]; /* mounted device shut down */ +extern char Eowner[]; /* not owner */ +extern char Eunknown[]; /* unknown user or group id */ +extern char Enocreate[]; /* mounted directory forbids creation */ +extern char Enonexist[]; /* file does not exist */ +extern char Eexist[]; /* file already exists */ +extern char Ebadsharp[]; /* unknown device in # filename */ +extern char Enotdir[]; /* not a directory */ +extern char Eisdir[]; /* file is a directory */ +extern char Ebadchar[]; /* bad character in file name */ +extern char Efilename[]; /* file name syntax */ +extern char Eperm[]; /* permission denied */ +extern char Ebadusefd[]; /* inappropriate use of fd */ +extern char Ebadarg[]; /* bad arg in system call */ +extern char Einuse[]; /* device or object already in use */ +extern char Eio[]; /* i/o error */ +extern char Etoobig[]; /* read or write too large */ +extern char Etoosmall[]; /* read or write too small */ +extern char Enetaddr[]; /* bad network address */ +extern char Emsgsize[]; /* message is too big for protocol */ +extern char Enetbusy[]; /* network device is busy or allocated */ +extern char Enoproto[]; /* network protocol not supported */ +extern char Enoport[]; /* network port not available */ +extern char Enoifc[]; /* bad interface or no free interface slots */ +extern char Enolisten[]; /* not announced */ +extern char Ehungup[]; /* i/o on hungup channel */ +extern char Ebadctl[]; /* bad process or channel control request */ +extern char Enodev[]; /* no free devices */ +extern char Enoenv[]; /* no free environment resources */ +extern char Ethread[]; /* thread exited */ +extern char Enochild[]; /* no living children */ +extern char Eioload[]; /* i/o error in demand load */ +extern char Enovmem[]; /* out of memory: virtual memory */ +extern char Ebadld[]; /* illegal line discipline */ +extern char Ebadfd[]; /* fd out of range or not open */ +extern char Eisstream[]; /* seek on a stream */ +extern char Ebadexec[]; /* exec header invalid */ +extern char Etimedout[]; /* connection timed out */ +extern char Econrefused[]; /* connection refused */ +extern char Econinuse[]; /* connection in use */ +extern char Enetunreach[]; /* network unreachable */ +extern char Eintr[]; /* interrupted */ +extern char Enomem[]; /* out of memory: kernel */ +extern char Esfnotcached[]; /* subfont not cached */ +extern char Esoverlap[]; /* segments overlap */ +extern char Emouseset[]; /* mouse type already set */ +extern char Eshort[]; /* i/o count too small */ +extern char Enobitstore[]; /* out of screen memory */ +extern char Egreg[]; /* jim'll fix it */ +extern char Ebadspec[]; /* bad attach specifier */ +extern char Estopped[]; /* thread must be stopped */ +extern char Enoattach[]; /* mount/attach disallowed */ +extern char Eshortstat[]; /* stat buffer too small */ +extern char Enegoff[]; /* negative i/o offset */ +extern char Ebadstat[]; /* malformed stat buffer */ +extern char Ecmdargs[]; /* wrong #args in control message */ +extern char Enofd[]; /* no free file descriptors */ +extern char Enoctl[]; /* unknown control request */ + +extern void error(char*); diff --git a/emu/port/errstr.c b/emu/port/errstr.c new file mode 100644 index 00000000..dfb41ed4 --- /dev/null +++ b/emu/port/errstr.c @@ -0,0 +1,38 @@ +#include "dat.h" +#include "fns.h" + +/* + * General OS interface to errors + */ +void +kwerrstr(char *fmt, ...) +{ + va_list arg; + char buf[ERRMAX]; + + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + kstrcpy(up->env->errstr, buf, ERRMAX); +} + +void +kerrstr(char *err, uint size) +{ + char tmp[ERRMAX]; + + kstrcpy(tmp, up->env->errstr, sizeof(tmp)); + kstrcpy(up->env->errstr, err, ERRMAX); + kstrcpy(err, tmp, size); +} + +void +kgerrstr(char *err, uint size) +{ + char *s; + + s = "<no-up>"; + if(up != nil) + s = up->env->errstr; + kstrcpy(err, s, size); /* TO DO */ +} diff --git a/emu/port/exception.c b/emu/port/exception.c new file mode 100644 index 00000000..8808ed56 --- /dev/null +++ b/emu/port/exception.c @@ -0,0 +1,214 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "interp.h" +#include "isa.h" +#include "runt.h" +#include "kernel.h" +#include "raise.h" + +static int +ematch(char *pat, char *exp) +{ + int l; + + if(strcmp(pat, exp) == 0) + return 1; + + l = strlen(pat); + if(l == 0) + return 0; + if(pat[l-1] == '*') { + if(l == 1) + return 1; + if(strncmp(pat, exp, l-1) == 0) + return 1; + } + return 0; +} + +static void +setstr(String *s, char *p) +{ + if(s == H) + return; + if(s->len < 0 || s->max < 4) + return; + kstrcpy(s->Sascii, p, s->max); /* TO DO: we are assuming they aren't runes */ + s->len = strlen(s->Sascii); +} + +static String *exstr; + +void +excinit(void) +{ + exstr = newstring(ERRMAX); + poolimmutable(D2H(exstr)); +} + +static String* +newestring(char *estr) +{ + String *s; + + if(waserror()){ + setstr(exstr, estr); + D2H(exstr)->ref++; + return exstr; + } + s = c2string(estr, strlen(estr)); + poperror(); + return s; +} + +#define NOPC 0xffffffff + +#define FRTYPE(f) ((f)->t == nil ? SEXTYPE(f)->reg.TR : (f)->t) + +/* + * clear up an uncalled frame + */ +static void +freeframe(uchar *fp, int setsp) +{ + Frame *f; + + f = (Frame*)fp; + if(f->t == nil) + unextend(f); + else if(f->t->np) + freeptrs(f, f->t); + if(setsp) + R.SP = fp; +} + +int +handler(char *estr) +{ + Prog *p; + Modlink *m, *mr; + int str, ne; + ulong pc, newpc; + long eoff; + uchar *fp, **eadr; + Frame *f; + Type *t, *zt; + Handler *h; + Except *e; + void *v; + + p = currun(); + if(*estr == 0 || p == nil) + return 0; + str = p->exval == H || D2H(p->exval)->t == &Tstring; + m = R.M; + if(m->compiled) + pc = (ulong)R.PC-(ulong)m->prog; + else + pc = R.PC-m->prog; + pc--; + fp = R.FP; + + while(fp != nil){ /* look for a handler */ + if((h = m->m->htab) != nil){ + for( ; h->etab != nil; h++){ + if(pc < h->pc1 || pc >= h->pc2) + continue; + eoff = h->eoff; + zt = h->t; + for(e = h->etab, ne = h->ne; e->s != nil; e++, ne--){ + if(ematch(e->s, estr) && (str && ne <= 0 || !str && ne > 0)){ + newpc = e->pc; + goto found; + } + } + newpc = e->pc; + if(newpc != NOPC) + goto found; + } + } + if(!str && fp != R.FP){ /* becomes a string exception in immediate caller */ + v = p->exval; + p->exval = *(String**)v; + D2H(p->exval)->ref++; + destroy(v); + str = 1; + continue; + } + f = (Frame*)fp; + if(f->mr != nil) + m = f->mr; + if(m->compiled) + pc = (ulong)f->lr-(ulong)m->prog; + else + pc = f->lr-m->prog; + pc--; + fp = f->fp; + } + destroy(p->exval); + p->exval = H; + return 0; +found: + { + int n; + char name[3*KNAMELEN]; + + pc = modstatus(&R, name, sizeof(name)); + n = 10+1+strlen(name)+1+strlen(estr)+1; + p->exstr = realloc(p->exstr, n); + if(p->exstr != nil) + snprint(p->exstr, n, "%lud %s %s", pc, name, estr); + } + + /* + * there may be an uncalled frame at the top of the stack + */ + f = (Frame*)R.FP; + t = FRTYPE(f); + if(R.FP < R.EX || R.FP >= R.TS) + freeframe(R.EX+OA(Stkext, reg.tos.fr), 0); + else if(R.FP+t->size < R.SP) + freeframe(R.FP+t->size, 1); + + m = R.M; + while(R.FP != fp){ + f = (Frame*)R.FP; + R.PC = f->lr; + R.FP = f->fp; + R.SP = (uchar*)f; + mr = f->mr; + if(f->t == nil) + unextend(f); + else if(f->t->np) + freeptrs(f, f->t); + if(mr != nil){ + m = mr; + destroy(R.M); + R.M = m; + R.MP = m->MP; + } + } + if(zt != nil){ + freeptrs(fp, zt); + initmem(zt, fp); + } + eadr = (uchar**)(fp+eoff); + destroy(*eadr); + *eadr = H; + if(p->exval == H) + *eadr = (uchar*)newestring(estr); /* might fail */ + else{ + D2H(p->exval)->ref++; + *eadr = p->exval; + } + if(m->compiled) + R.PC = (Inst*)((ulong)m->prog+newpc); + else + R.PC = m->prog+newpc; + memmove(&p->R, &R, sizeof(R)); + p->kill = nil; + destroy(p->exval); + p->exval = H; + return 1; +} diff --git a/emu/port/exportfs.c b/emu/port/exportfs.c new file mode 100644 index 00000000..1feae7ae --- /dev/null +++ b/emu/port/exportfs.c @@ -0,0 +1,1219 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "kernel.h" + +typedef struct Fid Fid; +typedef struct Export Export; +typedef struct Exq Exq; + +enum +{ + Nfidhash = 32, + MAXFDATA = 8192, + MAXRPCDEF = IOHDRSZ+MAXFDATA, /* initial/default */ + MAXRPCMAX = IOHDRSZ+64*1024, /* most every allowed */ + MSGHDRSZ = BIT32SZ+BIT8SZ+BIT16SZ +}; + +struct Export +{ + Lock l; + Ref r; + Exq* work; + Lock fidlock; + Fid* fid[Nfidhash]; + Uqidtab uqids; + Chan* io; + Chan* root; + Pgrp* pgrp; + Egrp* egrp; + Fgrp* fgrp; + int async; + int readonly; + int uid; + int gid; + int msize; + char* user; +}; + +struct Fid +{ + Fid* next; + Fid** last; + Chan* chan; + int fid; + int ref; /* fcalls using the fid; locked by Export.Lock */ + vlong offset; /* last offset used (within directory) */ + int attached; /* fid attached or cloned but not clunked */ + Uqid* qid; /* generated qid */ +}; + +struct Exq +{ + Lock l; + int busy; /* fcall in progress */ + int finished; /* will do no more work on this request or flushes */ + Exq* next; + int shut; /* has been noted for shutdown */ + Exq* flush; /* queued flush requests */ + Exq* flusht; /* tail of flush queue */ + Export* export; + Proc* slave; + Fcall in, out; + uchar* buf; + int bsize; +}; + +struct +{ + Lock l; + QLock qwait; + Rendez rwait; + Exq *head; /* work waiting for a slave */ + Exq *tail; +}exq; + +static void exshutdown(Export*); +static int exflushed(Export*, Exq*); +static void exslave(void*); +static void exfree(Export*); +static void exfreeq(Exq*); +static void exportproc(void*); +static void exreply(Exq*, char*); +static int exisroot(Export*, Chan*); + +static char* Exversion(Export*, Fcall*, Fcall*); +static char* Exauth(Export*, Fcall*, Fcall*); +static char* Exattach(Export*, Fcall*, Fcall*); +static char* Exclunk(Export*, Fcall*, Fcall*); +static char* Excreate(Export*, Fcall*, Fcall*); +static char* Exopen(Export*, Fcall*, Fcall*); +static char* Exread(Export*, Fcall*, Fcall*); +static char* Exremove(Export*, Fcall*, Fcall*); +static char* Exstat(Export*, Fcall*, Fcall*); +static char* Exwalk(Export*, Fcall*, Fcall*); +static char* Exwrite(Export*, Fcall*, Fcall*); +static char* Exwstat(Export*, Fcall*, Fcall*); + +static char *(*fcalls[Tmax])(Export*, Fcall*, Fcall*); + +static char Enofid[] = "no such fid"; +static char Eseekdir[] = "can't seek on a directory"; +static char Eopen[] = "walk of open fid"; +static char Emode[] = "open/create -- unknown mode"; +static char Edupfid[] = "fid in use"; +static char Eaccess[] = "read/write -- not open in suitable mode"; +static char Ecount[] = "read/write -- count too big"; +int exdebug = 0; + +int +export(int fd, char *dir, int async) +{ + Chan *c, *dc; + Pgrp *pg; + Egrp *eg; + Export *fs; + + if(waserror()) + return -1; + c = fdtochan(up->env->fgrp, fd, ORDWR, 1, 1); + poperror(); + + if(waserror()){ + cclose(c); + return -1; + } + dc = namec(dir, Atodir, 0, 0); + poperror(); + + fs = malloc(sizeof(Export)); + if(fs == nil){ + cclose(c); + cclose(dc); + error(Enomem); + } + + fs->r.ref = 1; + pg = up->env->pgrp; + fs->pgrp = pg; + incref(&pg->r); + eg = up->env->egrp; + fs->egrp = eg; + incref(&eg->r); + fs->fgrp = newfgrp(nil); + fs->uid = up->env->uid; + fs->gid = up->env->gid; + kstrdup(&fs->user, up->env->user); + fs->root = dc; + fs->io = c; + uqidinit(&fs->uqids); + fs->msize = 0; + c->flag |= CMSG; + fs->async = async; + + if(async){ + if(waserror()) + return -1; + kproc("exportfs", exportproc, fs, 0); + poperror(); + }else + exportproc(fs); + + return 0; +} + +static void +exportinit(void) +{ + lock(&exq.l); + if(fcalls[Tversion] != nil) { + unlock(&exq.l); + return; + } + fcalls[Tversion] = Exversion; + fcalls[Tauth] = Exauth; + fcalls[Tattach] = Exattach; + fcalls[Twalk] = Exwalk; + fcalls[Topen] = Exopen; + fcalls[Tcreate] = Excreate; + fcalls[Tread] = Exread; + fcalls[Twrite] = Exwrite; + fcalls[Tclunk] = Exclunk; + fcalls[Tremove] = Exremove; + fcalls[Tstat] = Exstat; + fcalls[Twstat] = Exwstat; + unlock(&exq.l); +} + +static int +exisroot(Export *fs, Chan *c) +{ + return eqchan(fs->root, c, 1); +} + +static int +exreadn(Chan *c, void *buf, int n) +{ + int nr, t; + + if(waserror()) + return -1; + for(nr = 0; nr < n;){ + t = devtab[c->type]->read(c, (char*)buf+nr, n-nr, 0); + if(t <= 0) + break; + nr += t; + } + poperror(); + return nr; +} + +static int +exreadmsg(Chan *c, void *a, uint n) +{ + int m, len; + uchar *buf; + + buf = a; + m = exreadn(c, buf, BIT32SZ); + if(m < BIT32SZ){ + if(m < 0) + return -1; + return 0; + } + len = GBIT32(buf); + if(len <= BIT32SZ || len > n){ + kwerrstr("bad length in Styx message header"); + return -1; + } + len -= BIT32SZ; + m = exreadn(c, buf+BIT32SZ, len); + if(m < len){ + if(m < 0) + return -1; + return 0; + } + return BIT32SZ+m; +} + +static void +exportproc(void *a) +{ + Exq *q; + int async, msize; + int n, type; + Export *fs = a; + + exportinit(); + + for(;;){ + + msize = fs->msize; + if(msize == 0) + msize = MAXRPCDEF; + for(n=0;; n++){ /* we don't use smalloc, to avoid memset */ + q = mallocz(sizeof(*q)+msize, 0); + if(q != nil || n > 6000) + break; + if(n%600 == 0) + print("exportproc %ld: waiting for memory (%d) for request\n", up->pid, msize); + osenter(); + osmillisleep(100); + osleave(); + } + if(q == nil){ + kwerrstr("out of memory: read request"); + n = -1; + break; + } + memset(q, 0, sizeof(*q)); + q->buf = (uchar*)q + sizeof(*q); + q->bsize = msize; + + n = exreadmsg(fs->io, q->buf, msize); /* TO DO: avoid copy */ + if(n <= 0) + break; + if(convM2S(q->buf, n, &q->in) != n){ + kwerrstr("bad T-message"); + n = -1; + break; + } + type = q->in.type; + if(type < Tversion || type >= Tmax || type&1 || type == Terror){ + kwerrstr("invalid T-message type %d", type); + n = -1; + break; + } + + if(exdebug) + print("export %ld <- %F\n", up->pid, &q->in); + + q->out.type = type+1; + q->out.tag = q->in.tag; + + q->export = fs; + incref(&fs->r); + + if(fs->readonly){ + switch(type){ + case Topen: + if((q->in.mode & (ORCLOSE|OTRUNC|3)) == OREAD) + break; + /* FALL THROUGH */ + case Tcreate: + case Twrite: + case Tremove: + case Twstat: + q->out.type = Rerror; + q->out.ename = "file system read only"; + exreply(q, "exportproc"); + exfreeq(q); + continue; + } + } + + if(q->in.type == Tflush){ + if(exflushed(fs, q)){ + /* not yet started or not found (flush arrived after reply); we reply */ + if(exdebug) + print("export: flush %d\n", q->in.oldtag); + exreply(q, "exportproc"); + exfreeq(q); + } + continue; + } + + lock(&exq.l); + if(exq.head == nil) + exq.head = q; + else + exq.tail->next = q; + q->next = nil; + exq.tail = q; + unlock(&exq.l); + if(exq.qwait.head == nil) + kproc("exslave", exslave, nil, 0); + Wakeup(&exq.rwait); + } + + if(exdebug){ + if(n < 0) + print("exportproc %ld shut down: %s\n", up->pid, up->env->errstr); + else + print("exportproc %ld shut down\n", up->pid); + } + + free(q); + exshutdown(fs); + async = fs->async; + exfree(fs); + + if(async) + pexit("mount shut down", 0); +} + +static int +exflushed(Export *fs, Exq *fq) +{ + Exq *q, **last; + ulong pid; + + /* not yet started? */ + lock(&exq.l); + for(last = &exq.head; (q = *last) != nil; last = &q->next) + if(q->export == fs && q->in.tag == fq->in.oldtag){ + *last = q->next; + unlock(&exq.l); + /* not yet started: discard, and Rflush */ + exfreeq(q); + return 1; + } + unlock(&exq.l); + + /* tricky case: in progress */ + lock(&fs->l); + for(q = fs->work; q != nil; q = q->next) + if(q->in.tag == fq->in.oldtag){ + pid = 0; + lock(&q->l); + if(q->finished){ + /* slave replied and emptied its flush queue; we can Rflush now */ + unlock(&q->l); + return 1; + } + /* append to slave's flush queue */ + fq->next = nil; + if(q->flush != nil) + q->flusht->next = fq; + else + q->flush = fq; + q->flusht = fq; + if(q->busy){ + pid = q->slave->pid; + swiproc(q->slave, 0); + } + unlock(&q->l); + unlock(&fs->l); + if(exdebug && pid) + print("export: swiproc %ld to flush %d\n", pid, fq->in.oldtag); + return 0; + } + unlock(&fs->l); + + /* not found */ + return 1; +} + +static void +exfreeq(Exq *q) +{ + Exq *fq; + + while((fq = q->flush) != nil){ + q->flush = fq->next; + exfree(fq->export); + free(fq); + } + exfree(q->export); + free(q); +} + +static void +exshutdown(Export *fs) +{ + Exq *q, **last; + + /* work not started */ + lock(&exq.l); + for(last = &exq.head; (q = *last) != nil;) + if(q->export == fs){ + *last = q->next; + exfreeq(q); + }else + last = &q->next; + unlock(&exq.l); + + /* tell slaves to abandon work in progress */ + lock(&fs->l); + while((q = fs->work) != nil){ + fs->work = q->next; + lock(&q->l); + q->shut = 1; + swiproc(q->slave, 0); /* whether busy or not */ + unlock(&q->l); + } + unlock(&fs->l); +} + +static void +exfreefids(Export *fs) +{ + Fid *f, *n; + int i; + + for(i = 0; i < Nfidhash; i++){ + for(f = fs->fid[i]; f != nil; f = n){ + n = f->next; + f->attached = 0; + if(f->ref == 0) { + if(f->chan != nil) + cclose(f->chan); + freeuqid(&fs->uqids, f->qid); + free(f); + } else + print("exfreefids: busy fid\n"); + } + } +} + +static void +exfree(Export *fs) +{ + if(exdebug) + print("export p/s %ld free %p ref %ld\n", up->pid, fs, fs->r.ref); + if(decref(&fs->r) != 0) + return; + closepgrp(fs->pgrp); + closeegrp(fs->egrp); + closefgrp(fs->fgrp); + cclose(fs->root); + cclose(fs->io); + exfreefids(fs); + free(fs->user); + free(fs); +} + +static int +exwork(void *a) +{ + USED(a); + return exq.head != nil; +} + +static void +exslave(void *a) +{ + Export *fs; + Exq *q, *t, *fq, **last; + char *err; + + USED(a); + + for(;;){ + qlock(&exq.qwait); + if(waserror()){ + qunlock(&exq.qwait); + continue; + } + Sleep(&exq.rwait, exwork, nil); + poperror(); + + lock(&exq.l); + q = exq.head; + if(q == nil) { + unlock(&exq.l); + qunlock(&exq.qwait); + continue; + } + exq.head = q->next; + + qunlock(&exq.qwait); + + /* + * put the job on the work queue before it's + * visible as off of the head queue, so it's always + * findable for flushes and shutdown + */ + notkilled(); + q->slave = up; + q->busy = 1; /* fcall in progress: interruptible */ + fs = q->export; + lock(&fs->l); + q->next = fs->work; + fs->work = q; + unlock(&fs->l); + unlock(&exq.l); + + up->env->pgrp = q->export->pgrp; + up->env->egrp = q->export->egrp; + up->env->fgrp = q->export->fgrp; + up->env->uid = q->export->uid; + up->env->gid = q->export->gid; + kstrdup(&up->env->user, q->export->user); + + if(exdebug > 1) + print("exslave %ld dispatch %F\n", up->pid, &q->in); + + if(waserror()){ + print("exslave %ld err %s\n", up->pid, up->env->errstr); /* shouldn't happen */ + err = up->env->errstr; + }else{ + if(q->in.type >= Tmax || !fcalls[q->in.type]){ + snprint(up->genbuf, sizeof(up->genbuf), "unknown message: %F", &q->in); + err = up->genbuf; + }else{ + switch(q->in.type){ + case Tread: + q->out.data = (char*)q->buf + IOHDRSZ; + break; + case Tstat: + q->out.stat = q->buf + MSGHDRSZ + BIT16SZ; /* leaves it just where we want it */ + q->out.nstat = q->bsize-(MSGHDRSZ+BIT16SZ); + break; + } + err = (*fcalls[q->in.type])(fs, &q->in, &q->out); + } + poperror(); + } + + /* + * if the fcall completed without error we must reply, + * even if a flush is pending (because the underlying server + * might have changed state), unless the export has shut down completely. + * must also reply to each flush in order, and only after the original reply (if sent). + */ + lock(&q->l); + notkilled(); + q->busy = 0; /* operation complete */ + if(!q->shut){ + if(q->flush == nil || err == nil){ + unlock(&q->l); + q->out.type = q->in.type+1; + q->out.tag = q->in.tag; + if(err){ + q->out.type = Rerror; + q->out.ename = err; + } + exreply(q, "exslave"); + lock(&q->l); + } + while((fq = q->flush) != nil && !q->shut){ + q->flush = fq->next; + unlock(&q->l); + exreply(fq, "exslave"); + exfreeq(fq); + lock(&q->l); + } + } + q->finished = 1; /* promise not to send any more */ + unlock(&q->l); + + lock(&fs->l); + for(last = &fs->work; (t = *last) != nil; last = &t->next) + if(t == q){ + *last = q->next; + break; + } + unlock(&fs->l); + + notkilled(); + exfreeq(q); + } +} + +static void +exreply(Exq *q, char *who) +{ + Export *fs; + Fcall *r; + int n; + + fs = q->export; + r = &q->out; + + n = convS2M(r, q->buf, q->bsize); + if(n == 0){ + r->type = Rerror; + if(fs->msize == 0) + r->ename = "Tversion not seen"; + else + r->ename = "failed to convert R-message"; + n = convS2M(r, q->buf, q->bsize); + } + + if(exdebug) + print("%s %ld -> %F\n", who, up->pid, r); + + if(!waserror()){ + devtab[fs->io->type]->write(fs->io, q->buf, n, 0); + poperror(); + } +} + +static int +exiounit(Export *fs, Chan *c) +{ + int iounit; + + iounit = fs->msize-IOHDRSZ; + if(c->iounit != 0 && c->iounit < fs->msize) + iounit = c->iounit; + return iounit; +} + +static Fid* +Exmkfid(Export *fs, ulong fid) +{ + ulong h; + Fid *f, *nf; + + nf = malloc(sizeof(Fid)); + if(nf == nil) + return nil; + lock(&fs->fidlock); + h = fid % Nfidhash; + for(f = fs->fid[h]; f != nil; f = f->next){ + if(f->fid == fid){ + unlock(&fs->fidlock); + free(nf); + return nil; + } + } + + nf->next = fs->fid[h]; + if(nf->next != nil) + nf->next->last = &nf->next; + nf->last = &fs->fid[h]; + fs->fid[h] = nf; + + nf->fid = fid; + nf->ref = 1; + nf->attached = 1; + nf->offset = 0; + nf->chan = nil; + nf->qid = nil; + unlock(&fs->fidlock); + return nf; +} + +static Fid* +Exgetfid(Export *fs, ulong fid) +{ + Fid *f; + ulong h; + + lock(&fs->fidlock); + h = fid % Nfidhash; + for(f = fs->fid[h]; f; f = f->next) { + if(f->fid == fid){ + if(f->attached == 0) + break; + f->ref++; + unlock(&fs->fidlock); + return f; + } + } + unlock(&fs->fidlock); + return nil; +} + +static void +Exputfid(Export *fs, Fid *f) +{ + Chan *c; + + lock(&fs->fidlock); + f->ref--; + if(f->ref == 0 && f->attached == 0){ + c = f->chan; + f->chan = nil; + *f->last = f->next; + if(f->next != nil) + f->next->last = f->last; + unlock(&fs->fidlock); + freeuqid(&fs->uqids, f->qid); + free(f); + if(c != nil) + cclose(c); + return; + } + unlock(&fs->fidlock); +} + +static Chan* +exmount(Chan *c, Mhead **mp, int doname) +{ + struct {Chan *nc;} nc; + Cname *oname; + + nc.nc = nil; + if((c->flag & COPEN) == 0 && findmount(&nc.nc, mp, c->type, c->dev, c->qid)){ + if(waserror()){ + cclose(nc.nc); + nexterror(); + } + nc.nc = cunique(nc.nc); + poperror(); + if(doname){ + oname = c->name; + incref(&oname->r); + cnameclose(nc.nc->name); + nc.nc->name = oname; + } + return nc.nc; + } + incref(&c->r); + return c; +} + +static char* +Exversion(Export *fs, Fcall *t, Fcall *r) +{ + char *p; + static char version[] = VERSION9P; + int iounit; + + r->msize = t->msize; + if(r->msize > MAXRPCMAX) + r->msize = MAXRPCMAX; + iounit = fs->io->iounit; + if(iounit != 0 && iounit > 64 && iounit < r->msize) + r->msize = iounit; + if(r->msize < 64) + return "message size too small"; + if(0) + print("msgsize=%d\n", r->msize); + if((p = strchr(t->version, '.')) != nil) + *p = 0; + if(strncmp(t->version, "9P", 2) ==0 && strcmp(version, t->version) <= 0){ + r->version = version; + fs->msize = r->msize; + }else + r->version = "unknown"; + return nil; +} + +static char* +Exauth(Export *fs, Fcall *t, Fcall *r) +{ + USED(fs); + USED(t); + USED(r); + return "authentication not required"; +} + +static char* +Exattach(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + + f = Exmkfid(fs, t->fid); + if(f == nil) + return Edupfid; + if(waserror()){ + f->attached = 0; + Exputfid(fs, f); + return up->env->errstr; + } + f->chan = cclone(fs->root); + f->qid = uqidalloc(&fs->uqids, f->chan); + poperror(); + r->qid = mkuqid(f->chan, f->qid); + Exputfid(fs, f); + return nil; +} + +static char* +Exclunk(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + + USED(r); + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + f->attached = 0; + Exputfid(fs, f); + return nil; +} + +static int +safewalk(Chan **cp, char **names, int nnames, int nomount, int *nerror) +{ + int r; + + /* walk can raise error */ + if(waserror()) + return -1; + r = walk(cp, names, nnames, nomount, nerror); + poperror(); + return r; +} + +static char* +Exwalk(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f, *nf; + Chan *c; + char *name; + Uqid *qid; + int i; + + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + if(f->chan->flag & COPEN){ + Exputfid(fs, f); + return Eopen; + } + if(waserror()) + return up->env->errstr; + c = cclone(f->chan); + poperror(); + qid = f->qid; + incref(&qid->r); + r->nwqid = 0; + if(t->nwname > 0){ + for(i=0; i<t->nwname; i++){ + name = t->wname[i]; + if(!exisroot(fs, c) || *name != '\0' && strcmp(name, "..") != 0){ + if(safewalk(&c, &name, 1, 0, nil) < 0){ + /* leave the original state on error */ + cclose(c); + freeuqid(&fs->uqids, qid); + Exputfid(fs, f); + if(i == 0) + return up->env->errstr; + return nil; + } + freeuqid(&fs->uqids, qid); + qid = uqidalloc(&fs->uqids, c); + } + r->wqid[r->nwqid++] = mkuqid(c, qid); + } + } + + if(t->newfid != t->fid){ + nf = Exmkfid(fs, t->newfid); + if(nf == nil){ + cclose(c); + freeuqid(&fs->uqids, qid); + Exputfid(fs, f); + return Edupfid; + } + nf->chan = c; + nf->qid = qid; + Exputfid(fs, nf); + }else{ + cclose(f->chan); + f->chan = c; + freeuqid(&fs->uqids, f->qid); + f->qid = qid; + } + Exputfid(fs, f); + return nil; +} + +static char* +Exopen(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + Chan *c; + Uqid *qid; + Mhead *m; + + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + if(f->chan->flag & COPEN){ + Exputfid(fs, f); + return Emode; + } + m = nil; + c = exmount(f->chan, &m, 1); + if(waserror()){ + cclose(c); + Exputfid(fs, f); + return up->env->errstr; + } + + /* only save the mount head if it's a multiple element union */ + if(m && m->mount && m->mount->next) + c->umh = m; + else + putmhead(m); + + c = devtab[c->type]->open(c, t->mode); + if(t->mode & ORCLOSE) + c->flag |= CRCLOSE; + + qid = uqidalloc(&fs->uqids, c); + poperror(); + freeuqid(&fs->uqids, f->qid); + cclose(f->chan); + f->chan = c; + f->qid = qid; + f->offset = 0; + r->qid = mkuqid(c, f->qid); + r->iounit = exiounit(fs, c); + Exputfid(fs, f); + return nil; +} + +static char* +Excreate(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + volatile struct {Chan *c;} c, dc; + Cname *oname; + Uqid *qid; + Mhead *m; + + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + if(f->chan->flag & COPEN){ + Exputfid(fs, f); + return Emode; + } + if(waserror()){ + Exputfid(fs, f); + return up->env->errstr; + } + validname(t->name, 0); + if(t->name[0] == '.' && (t->name[1] == '\0' || t->name[1] == '.' && t->name[2] == '\0')) + error(Efilename); /* underlying server should check, but stop it here */ + + m = nil; + c.c = exmount(f->chan, &m, 1); + if(waserror()){ + cclose(c.c); + if(m != nil) + putmhead(m); + nexterror(); + } + if(m != nil){ + oname = c.c->name; + incref(&oname->r); + if(waserror()){ + cnameclose(oname); + nexterror(); + } + dc.c = createdir(c.c, m); + if(waserror()){ + cclose(dc.c); + nexterror(); + } + c.c = cunique(dc.c); + poperror(); + cnameclose(c.c->name); + poperror(); + c.c->name = oname; + } + devtab[c.c->type]->create(c.c, t->name, t->mode, t->perm); + c.c->name = addelem(c.c->name, t->name); + if(t->mode & ORCLOSE) + c.c->flag |= CRCLOSE; + qid = uqidalloc(&fs->uqids, c.c); + poperror(); + if(m != nil) + putmhead(m); + + poperror(); + cclose(f->chan); + f->chan = c.c; + freeuqid(&fs->uqids, f->qid); + f->qid = qid; + r->qid = mkuqid(c.c, f->qid); + r->iounit = exiounit(fs, c.c); + Exputfid(fs, f); + return nil; +} + +static char* +Exread(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + Chan *c; + Lock *cl; + long off; + int dir, n, seek; + + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + + if(waserror()) { + Exputfid(fs, f); + return up->env->errstr; + } + c = f->chan; + if((c->flag & COPEN) == 0) + error(Emode); + if(c->mode != OREAD && c->mode != ORDWR) + error(Eaccess); + if(t->count < 0 || t->count > fs->msize-IOHDRSZ) + error(Ecount); + if(t->offset < 0) + error(Enegoff); + dir = c->qid.type & QTDIR; + if(dir && t->offset != f->offset){ + if(t->offset != 0) + error(Eseekdir); + f->offset = 0; + c->uri = 0; + c->dri = 0; + } + + for(;;){ + n = t->count; + seek = 0; + off = t->offset; + if(dir && f->offset != off){ + off = f->offset; + n = t->offset - off; + if(n > MAXFDATA) + n = MAXFDATA; + seek = 1; + } + if(dir && c->umh != nil){ + if(0) + print("union read %d uri %d dri %d\n", seek, c->uri, c->dri); + n = unionread(c, r->data, n); + } + else{ + cl = &c->l; + lock(cl); + c->offset = off; + unlock(cl); + n = devtab[c->type]->read(c, r->data, n, off); + lock(cl); + c->offset += n; + unlock(cl); + } + f->offset = off + n; + if(n == 0 || !seek) + break; + } + r->count = n; + + poperror(); + Exputfid(fs, f); + return nil; +} + +static char* +Exwrite(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + Chan *c; + + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->env->errstr; + } + c = f->chan; + if((c->flag & COPEN) == 0) + error(Emode); + if(c->mode != OWRITE && c->mode != ORDWR) + error(Eaccess); + if(c->qid.type & QTDIR) + error(Eisdir); + if(t->count < 0 || t->count > fs->msize-IOHDRSZ) + error(Ecount); + if(t->offset < 0) + error(Enegoff); + r->count = devtab[c->type]->write(c, t->data, t->count, t->offset); + poperror(); + Exputfid(fs, f); + return nil; +} + +static char* +Exstat(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + Chan *c; + int n; + + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + c = exmount(f->chan, nil, 1); + if(waserror()){ + cclose(c); + Exputfid(fs, f); + return up->env->errstr; + } + n = devtab[c->type]->stat(c, r->stat, r->nstat); + if(n <= BIT16SZ) + error(Eshortstat); + r->nstat = n; + poperror(); + /* TO DO: need to change qid */ + cclose(c); + Exputfid(fs, f); + return nil; +} + +static char* +Exwstat(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + Chan *c; + + USED(r); + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->env->errstr; + } + validstat(t->stat, t->nstat); /* check name */ + + c = exmount(f->chan, nil, 0); + if(waserror()){ + cclose(c); + nexterror(); + } + devtab[c->type]->wstat(c, t->stat, t->nstat); + poperror(); + + cclose(c); + poperror(); + Exputfid(fs, f); + return nil; +} + +static char* +Exremove(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + Chan *c; + + USED(r); + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + if(waserror()){ + f->attached = 0; + Exputfid(fs, f); + return up->env->errstr; + } + c = exmount(f->chan, nil, 0); + if(waserror()){ + c->type = 0; /* see below */ + cclose(c); + nexterror(); + } + devtab[c->type]->remove(c); + poperror(); + poperror(); + + /* + * chan is already clunked by remove. + * however, we need to recover the chan, + * and follow sysremove's lead in making it point to root. + */ + c->type = 0; + cclose(c); + + f->attached = 0; + Exputfid(fs, f); + return nil; +} diff --git a/emu/port/exptab.c b/emu/port/exptab.c new file mode 100644 index 00000000..782ef21d --- /dev/null +++ b/emu/port/exptab.c @@ -0,0 +1,6 @@ +#include "dat.h" +#include <dynld.h> + +/* dummy export table */ + +Dynsym _exporttab[] = { 0, 0, nil }; diff --git a/emu/port/file.c b/emu/port/file.c new file mode 100644 index 00000000..91000ebb --- /dev/null +++ b/emu/port/file.c @@ -0,0 +1,16 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + +int +openmode(ulong o) +{ + if(o >= (OTRUNC|OCEXEC|ORCLOSE|OEXEC)) + error(Ebadarg); + o &= ~(OTRUNC|OCEXEC|ORCLOSE); + if(o > OEXEC) + error(Ebadarg); + if(o == OEXEC) + return OREAD; + return o; +} diff --git a/emu/port/fns.h b/emu/port/fns.h new file mode 100644 index 00000000..545b578f --- /dev/null +++ b/emu/port/fns.h @@ -0,0 +1,247 @@ +ulong FPcontrol(ulong,ulong); +ulong FPstatus(ulong,ulong); +void FPsave(void*); +void FPrestore(void*); +void Sleep(Rendez*, int (*)(void*), void*); +int Wakeup(Rendez*); +void FPinit(void); +void addprog(Proc*); +Block* adjustblock(Block*, int); +Block* allocb(int); +Block* bl2mem(uchar*, Block*, int); +int blocklen(Block*); +char* c2name(Chan*); +int canlock(Lock*); +int canqlock(QLock*); +void cclose(Chan*); +void chandevinit(void); +void chanfree(Chan*); +Dir* chandirstat(Chan*); +void cinit(void); +char* clipread(void); +int clipwrite(char*); +void copen(Chan*); +void cmderror(Cmdbuf*, char*); +Block* concatblock(Block*); +int cread(Chan*, uchar*, int, vlong); +void cwrite(Chan*, uchar*, int, vlong); +Chan* cunique(Chan*); +void cupdate(Chan*, uchar*, int, vlong); +char* cleanname(char*); +Chan* cclone(Chan*); +void closeegrp(Egrp*); +void closefgrp(Fgrp*); +void closepgrp(Pgrp*); +void closesigs(Skeyset*); +int cmount(Chan*, Chan*, int, char*); +Chan* createdir(Chan*, Mhead*); +void cunmount(Chan*, Chan*); +int decref(Ref*); +long devbwrite(Chan*, Block*, ulong); +void devcreate(Chan*, char*, int, ulong); +void devdir(Chan*, Qid, char*, long, char*, long, Dir*); +long devdirread(Chan*, char*, long, Dirtab*, int, Devgen*); +void devinit(void); +int devno(int, int); +Dev* devbyname(char*); +void devpermcheck(char*, ulong, int); +void devremove(Chan*); +int devstat(Chan*, uchar*, int, Dirtab*, int, Devgen*); +int devwstat(Chan*, uchar*, int); +Chan* devattach(int, char*); +Block* devbread(Chan*, long, ulong); +Chan* devclone(Chan*); +Devgen devgen; +Chan* devopen(Chan*, int, Dirtab*, int, Devgen*); +Walkqid* devwalk(Chan*, Chan*, char**, int, Dirtab*, int, Devgen*); +void disfault(void*, char*); +void disinit(void*); +int domount(Chan**, Mhead**); +void drawqlock(void); +void drawqunlock(void); +Fgrp* dupfgrp(Fgrp*); +void egrpcpy(Egrp*, Egrp*); +int emptystr(char*); +void emuinit(void*); +int eqchan(Chan*, Chan*, int); +int eqqid(Qid, Qid); +void error(char*); +void errorf(char*, ...); +#pragma varargck argpos errorf 1 +void excinit(void); +void exhausted(char*); +int export(int, char*, int); +Chan* fdtochan(Fgrp*, int, int, int, int); +int findmount(Chan**, Mhead**, int, int, Qid); +void freeb(Block*); +void freeblist(Block*); +void freeskey(Signerkey*); +ulong getcallerpc(void*); +ulong getFPcontrol(void); +ulong getFPstatus(void); +void gkbdputc(Queue*, int); +int incref(Ref*); +int iprint(char*, ...); +void isdir(Chan*); +int isdotdot(char*); +int iseve(void); +int kannounce(char*, char*); +int kdial(char*, char*, char*, int*); +int kproc(char*, void (*)(void*), void*, int); +int kfgrpclose(Fgrp*, int); +void ksetenv(char*, char*, int); +void kstrcpy(char*, char*, int); +void kstrdup(char**, char*); +long latin1(uchar*, int); +void libinit(char*); +void links(void); +void lock(Lock*); +Cmdtab* lookupcmd(Cmdbuf*, Cmdtab*, int); +Block* mem2bl(uchar*, int); +int memusehigh(void); +int memlow(void); +void mkqid(Qid*, vlong, ulong, int); +Qid mkuqid(Chan*, Uqid*); +Chan* mntauth(Chan*, char*); +long mntversion(Chan*, char*, int, int); +void mountfree(Mount*); +void mousetrack(int, int, int, int); +void muxclose(Mnt*); +Chan* namec(char*, int, int, ulong); +Chan* newchan(void); +Cname* newcname(char*); +Egrp* newegrp(void); +Fgrp* newfgrp(Fgrp*); +Mount* newmount(Mhead*, Chan*, int, char*); +Pgrp* newpgrp(void); +Proc* newproc(void); +void nexterror(void); +void notkilled(void); +int openmode(ulong); +void osblock(void); +void* oscmd(char**, int, char*, int*); +int oscmdwait(void*, char*, int); +int oscmdkill(void*); +void oscmdfree(void*); +void oserror(void); +void oserrstr(char*, uint); +void oslongjmp(void*, osjmpbuf, int); +long osmillisec(void); +int osmillisleep(ulong); +void osready(Proc*); +int limbosleep(ulong); +vlong osusectime(void); +Block* packblock(Block*); +Block* padblock(Block*, int); +void panic(char*, ...); +Cmdbuf* parsecmd(char*, int); +void pexit(char*, int); +void pgrpcpy(Pgrp*, Pgrp*); +int progfdprint(Chan*, int, int, char*, int); +void putenvq(char*, char*, int); +void putenvqv(char*, char**, int, int); +void putmhead(Mhead*); +Block* pullupblock(Block*, int); +Block* pullupqueue(Queue*, int); +void qaddlist(Queue*, Block*); +Block* qbread(Queue*, int); +long qbwrite(Queue*, Block*); +Queue* qbypass(void (*)(void*, Block*), void*); +int qcanread(Queue*); +void qclose(Queue*); +int qisclosed(Queue*); +int qconsume(Queue*, void*, int); +Block* qcopy(Queue*, int, ulong); +int qdiscard(Queue*, int); +void qflush(Queue*); +void qfree(Queue*); +int qfull(Queue*); +Block* qget(Queue*); +void qhangup(Queue*, char*); +int qiwrite(Queue*, void*, int); +int qlen(Queue*); +void qlock(QLock*); +void qnoblock(Queue*, int); +Queue* qopen(int, int, void (*)(void*), void*); +int qpass(Queue*, Block*); +int qproduce(Queue*, void*, int); +void qputback(Queue*, Block*); +long qread(Queue*, void*, int); +Block* qremove(Queue*); +void qreopen(Queue*); +void qsetlimit(Queue*, int); +int qstate(Queue*); +void qunlock(QLock*); +int qwindow(Queue*); +int qwrite(Queue*, void*, int); +ulong randomread(void *xp, ulong n); +void randominit(void); +int readkbd(void); +int readnum(ulong, char*, ulong, ulong, int); +int readnum_vlong(ulong, char*, ulong, vlong, int); +int readstr(ulong, char*, ulong, char*); +#define seconds() (osusectime()/1000000) +void seterror(char*, ...); +void setid(char*, int); +void setpointer(int, int); +char* skipslash(char*); +void srvrtinit(void); +void swiproc(Proc*, int); +Block* trimblock(Block*, int, int); +long unionread(Chan*, void*, long); +void unlock(Lock*); +Uqid* uqidalloc(Uqidtab*, Chan*); +void uqidinit(Uqidtab*); +void freeuqid(Uqidtab*, Uqid*); +void validname(char*, int); +void validstat(uchar*, int); +void validwstatname(char*); +void vmachine(void*); +int walk(Chan**, char**, int, int, int*); +void cleanexit(int); +void oshostintr(Proc*); +void osenter(void); +void osleave(void); +void oslopri(void); +void ospause(void); +void osyield(void); +void osreboot(char*, char**); +ulong poolmaxsize(void); +Pool* poolmk(char*, int, int, int); +void hnputv(void*, vlong); +void hnputl(void*, ulong); +void hnputs(void*, ushort); +vlong nhgetv(void*); +ulong nhgetl(void*); +ushort nhgets(void*); +void* smalloc(size_t); +void* kmalloc(size_t); + +/* Namespace Emulation */ +int kbind(char*, char*, int); +int kclose(int); +int kcreate(char*, int, ulong); +int kdup(int, int); +int kfstat(int, uchar*, int); +int kfwstat(int, uchar*, int); +int kmount(int, int, char*, int, char*); +int kunmount(char*, char*); +int kopen(char*, int); +long kread(int, void*, long); +int kremove(char*); +vlong kseek(int, vlong, int); +int kstat(char*, uchar*, int); +long kwrite(int, void*, long); +int kwstat(char*, uchar*, int); +Dir* kdirstat(char*); +Dir* kdirfstat(int); +int kdirwstat(char*, Dir*); +int kdirfwstat(int, Dir*); +long kdirread(int, Dir**); +int klisten(char*, char*); + +Cname* addelem(Cname*, char*); +void cleancname(Cname*); +void cnameclose(Cname*); + +#pragma varargck argpos iprint 1 diff --git a/emu/port/inferno.c b/emu/port/inferno.c new file mode 100644 index 00000000..b568f9fa --- /dev/null +++ b/emu/port/inferno.c @@ -0,0 +1,1061 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "interp.h" +#include "isa.h" +#include "runt.h" +#include "kernel.h" + +/* + * here because Sys_FileIO is not public + */ +extern int srvf2c(char*, char*, Sys_FileIO*); + +/* + * System types connected to gc + */ +uchar FDmap[] = Sys_FD_map; +uchar FileIOmap[] = Sys_FileIO_map; +void freeFD(Heap*, int); +void freeFileIO(Heap*, int); +Type* TFD; +Type* TFileIO; + +static uchar rmap[] = Sys_FileIO_read_map; +static uchar wmap[] = Sys_FileIO_write_map; +static Type* FioTread; +static Type* FioTwrite; +static uchar dmap[] = Sys_Dir_map; +static Type* Tdir; + +typedef struct FD FD; +struct FD +{ + Sys_FD fd; + Fgrp* grp; +}; + +void +sysinit(void) +{ + TFD = dtype(freeFD, sizeof(FD), FDmap, sizeof(FDmap)); + TFileIO = dtype(freeFileIO, Sys_FileIO_size, FileIOmap, sizeof(FileIOmap)); + + /* Support for devsrv.c */ + FioTread = dtype(freeheap, Sys_FileIO_read_size, rmap, sizeof(rmap)); + FioTwrite = dtype(freeheap, Sys_FileIO_write_size, wmap, sizeof(wmap)); + + /* Support for dirread */ + Tdir = dtype(freeheap, Sys_Dir_size, dmap, sizeof(dmap)); +} + +void +freeFD(Heap *h, int swept) +{ + FD *handle; + + USED(swept); + + handle = H2D(FD*, h); + + release(); + if(handle->fd.fd >= 0) + kfgrpclose(handle->grp, handle->fd.fd); + closefgrp(handle->grp); + acquire(); +} + +void +freeFileIO(Heap *h, int swept) +{ + Sys_FileIO *fio; + + if(swept) + return; + + fio = H2D(Sys_FileIO*, h); + destroy(fio->read); + destroy(fio->write); +} + +Sys_FD* +mkfd(int fd) +{ + Heap *h; + Fgrp *fg; + FD *handle; + + h = heap(TFD); + handle = H2D(FD*, h); + handle->fd.fd = fd; + fg = up->env->fgrp; + handle->grp = fg; + incref(&fg->r); + return (Sys_FD*)handle; +} +#define fdchk(x) ((x) == (Sys_FD*)H ? -1 : (x)->fd) + +void +seterror(char *err, ...) +{ + char *estr; + va_list arg; + + estr = up->env->errstr; + va_start(arg, err); + vseprint(estr, estr+ERRMAX, err, arg); + va_end(arg); +} + +char* +syserr(char *s, char *es, Prog *p) +{ + Osenv *o; + + o = p->osenv; + kstrcpy(s, o->errstr, es - s); + return s + strlen(s); +} + +void +Sys_millisec(void *fp) +{ + F_Sys_millisec *f; + + f = fp; + *f->ret = osmillisec(); +} + +void +Sys_open(void *fp) +{ + int fd; + F_Sys_open *f; + + f = fp; + destroy(*f->ret); + *f->ret = H; + release(); + fd = kopen(string2c(f->s), f->mode); + acquire(); + if(fd == -1) + return; + + *f->ret = mkfd(fd); +} + +void +Sys_pipe(void *fp) +{ + Array *a; + int fd[2]; + Sys_FD **sfd; + F_Sys_pipe *f; + + f = fp; + *f->ret = -1; + + a = f->fds; + if(a->len < 2) + return; + if(kpipe(fd) < 0) + return; + + sfd = (Sys_FD**)a->data; + destroy(sfd[0]); + destroy(sfd[1]); + sfd[0] = H; + sfd[1] = H; + sfd[0] = mkfd(fd[0]); + sfd[1] = mkfd(fd[1]); + *f->ret = 0; +} + +void +Sys_fildes(void *fp) +{ + F_Sys_fildes *f; + int fd; + + f = fp; + destroy(*f->ret); + *f->ret = H; + release(); + fd = kdup(f->fd, -1); + acquire(); + if(fd == -1) + return; + *f->ret = mkfd(fd); +} + +void +Sys_dup(void *fp) +{ + F_Sys_dup *f; + + f = fp; + release(); + *f->ret = kdup(f->old, f->new); + acquire(); +} + +void +Sys_create(void *fp) +{ + int fd; + F_Sys_create *f; + + f = fp; + destroy(*f->ret); + *f->ret = H; + release(); + fd = kcreate(string2c(f->s), f->mode, f->perm); + acquire(); + if(fd == -1) + return; + + *f->ret = mkfd(fd); +} + +void +Sys_remove(void *fp) +{ + F_Sys_remove *f; + + f = fp; + release(); + *f->ret = kremove(string2c(f->s)); + acquire(); +} + +void +Sys_seek(void *fp) +{ + F_Sys_seek *f; + + f = fp; + release(); + *f->ret = kseek(fdchk(f->fd), f->off, f->start); + acquire(); +} + +void +Sys_unmount(void *fp) +{ + F_Sys_unmount *f; + + f = fp; + release(); + *f->ret = kunmount(string2c(f->s1), string2c(f->s2)); + acquire(); +} + +void +Sys_read(void *fp) +{ + int n; + F_Sys_read *f; + + f = fp; + n = f->n; + if(f->buf == (Array*)H || n < 0) { + *f->ret = 0; + return; + } + if(n > f->buf->len) + n = f->buf->len; + + release(); + *f->ret = kread(fdchk(f->fd), f->buf->data, n); + acquire(); +} + +void +Sys_readn(void *fp) +{ + int fd, m, n, t; + F_Sys_readn *f; + + f = fp; + n = f->n; + if(f->buf == (Array*)H || n < 0) { + *f->ret = 0; + return; + } + if(n > f->buf->len) + n = f->buf->len; + fd = fdchk(f->fd); + + release(); + for(t = 0; t < n; t += m){ + m = kread(fd, (char*)f->buf->data+t, n-t); + if(m <= 0){ + if(t == 0) + t = m; + break; + } + } + *f->ret = t; + acquire(); +} + +void +Sys_pread(void *fp) +{ + int n; + F_Sys_pread *f; + + f = fp; + n = f->n; + if(f->buf == (Array*)H || n < 0) { + *f->ret = 0; + return; + } + if(n > f->buf->len) + n = f->buf->len; + + release(); + *f->ret = kpread(fdchk(f->fd), f->buf->data, n, f->off); + acquire(); +} + +void +Sys_chdir(void *fp) +{ + F_Sys_chdir *f; + + f = fp; + release(); + *f->ret = kchdir(string2c(f->path)); + acquire(); +} + +void +Sys_write(void *fp) +{ + int n; + F_Sys_write *f; + + f = fp; + n = f->n; + if(f->buf == (Array*)H || n < 0) { + *f->ret = 0; + return; + } + if(n > f->buf->len) + n = f->buf->len; + + release(); + *f->ret = kwrite(fdchk(f->fd), f->buf->data, n); + acquire(); +} + +void +Sys_pwrite(void *fp) +{ + int n; + F_Sys_pwrite *f; + + f = fp; + n = f->n; + if(f->buf == (Array*)H || n < 0) { + *f->ret = 0; + return; + } + if(n > f->buf->len) + n = f->buf->len; + + release(); + *f->ret = kpwrite(fdchk(f->fd), f->buf->data, n, f->off); + acquire(); +} + +static void +unpackdir(Dir *d, Sys_Dir *sd) +{ + retstr(d->name, &sd->name); + retstr(d->uid, &sd->uid); + retstr(d->gid, &sd->gid); + retstr(d->muid, &sd->muid); + sd->qid.path = d->qid.path; + sd->qid.vers = d->qid.vers; + sd->qid.qtype = d->qid.type; + sd->mode = d->mode; + sd->atime = d->atime; + sd->mtime = d->mtime; + sd->length = d->length; + sd->dtype = d->type; + sd->dev = d->dev; +} + +static Dir* +packdir(Sys_Dir *sd) +{ + char *nm[4], *p; + int i, n; + Dir *d; + + nm[0] = string2c(sd->name); + nm[1] = string2c(sd->uid); + nm[2] = string2c(sd->gid); + nm[3] = string2c(sd->muid); + n = 0; + for(i=0; i<4; i++) + n += strlen(nm[i])+1; + d = smalloc(sizeof(*d)+n); + p = (char*)d+sizeof(*d); + for(i=0; i<4; i++){ + n = strlen(nm[i])+1; + memmove(p, nm[i], n); + nm[i] = p; + p += n; + } + d->name = nm[0]; + d->uid = nm[1]; + d->gid = nm[2]; + d->muid = nm[3]; + d->qid.path = sd->qid.path; + d->qid.vers = sd->qid.vers; + d->qid.type = sd->qid.qtype; + d->mode = sd->mode; + d->atime = sd->atime; + d->mtime = sd->mtime; + d->length = sd->length; + d->type = sd->dtype; + d->dev = sd->dev; + return d; +} + +void +Sys_fstat(void *fp) +{ + Dir *d; + F_Sys_fstat *f; + + f = fp; + f->ret->t0 = -1; + release(); + d = kdirfstat(fdchk(f->fd)); + acquire(); + if(d == nil) + return; + if(waserror() == 0){ + unpackdir(d, &f->ret->t1); + f->ret->t0 = 0; + poperror(); + } + free(d); +} + +void +Sys_stat(void *fp) +{ + Dir *d; + F_Sys_stat *f; + + f = fp; + f->ret->t0 = -1; + release(); + d = kdirstat(string2c(f->s)); + acquire(); + if(d == nil) + return; + if(waserror() == 0){ + unpackdir(d, &f->ret->t1); + f->ret->t0 = 0; + poperror(); + } + free(d); +} + +void +Sys_fd2path(void *fp) +{ + F_Sys_fd2path *f; + char *s; + void *r; + + f = fp; + r = *f->ret; + *f->ret = H; + destroy(r); + release(); + s = kfd2path(fdchk(f->fd)); + acquire(); + if(waserror() == 0){ + retstr(s, f->ret); + poperror(); + } + free(s); +} + +void +Sys_mount(void *fp) +{ + F_Sys_mount *f; + + f = fp; + release(); + *f->ret = kmount(fdchk(f->fd), fdchk(f->afd), string2c(f->on), f->flags, string2c(f->spec)); + acquire(); +} + +void +Sys_bind(void *fp) +{ + F_Sys_bind *f; + + f = fp; + release(); + *f->ret = kbind(string2c(f->s), string2c(f->on), f->flags); + acquire(); +} + +void +Sys_wstat(void *fp) +{ + Dir *d; + F_Sys_wstat *f; + + f = fp; + d = packdir(&f->d); + release(); + *f->ret = kdirwstat(string2c(f->s), d); + acquire(); + free(d); +} + +void +Sys_fwstat(void *fp) +{ + Dir *d; + F_Sys_fwstat *f; + + f = fp; + d = packdir(&f->d); + release(); + *f->ret = kdirfwstat(fdchk(f->fd), d); + acquire(); + free(d); +} + +void +Sys_print(void *fp) +{ + int n; + Prog *p; + Chan *c; + char buf[1024], *b = buf; + F_Sys_print *f; + f = fp; + c = up->env->fgrp->fd[1]; + if(c == nil) + return; + p = currun(); + + release(); + n = xprint(p, f, &f->vargs, f->s, buf, sizeof(buf)); + if (n >= sizeof(buf)-UTFmax-2) + n = bigxprint(p, f, &f->vargs, f->s, &b, sizeof(buf)); + *f->ret = kwrite(1, b, n); + if (b != buf) + free(b); + acquire(); +} + +void +Sys_fprint(void *fp) +{ + int n; + Prog *p; + char buf[1024], *b = buf; + F_Sys_fprint *f; + + f = fp; + p = currun(); + release(); + n = xprint(p, f, &f->vargs, f->s, buf, sizeof(buf)); + if (n >= sizeof(buf)-UTFmax-2) + n = bigxprint(p, f, &f->vargs, f->s, &b, sizeof(buf)); + *f->ret = kwrite(fdchk(f->fd), b, n); + if (b != buf) + free(b); + acquire(); +} + +void +Sys_werrstr(void *fp) +{ + F_Sys_werrstr *f; + + f = fp; + *f->ret = 0; + kstrcpy(up->env->errstr, string2c(f->s), ERRMAX); +} + +void +Sys_dial(void *fp) +{ + int cfd; + char dir[NETPATHLEN], *a, *l; + F_Sys_dial *f; + + f = fp; + a = string2c(f->addr); + l = string2c(f->local); + release(); + f->ret->t0 = kdial(a, l, dir, &cfd); + acquire(); + destroy(f->ret->t1.dfd); + f->ret->t1.dfd = H; + destroy(f->ret->t1.cfd); + f->ret->t1.cfd = H; + if(f->ret->t0 == -1) + return; + + f->ret->t1.dfd = mkfd(f->ret->t0); + f->ret->t0 = 0; + f->ret->t1.cfd = mkfd(cfd); + retstr(dir, &f->ret->t1.dir); +} + +void +Sys_announce(void *fp) +{ + char dir[NETPATHLEN], *a; + F_Sys_announce *f; + + f = fp; + a = string2c(f->addr); + release(); + f->ret->t0 = kannounce(a, dir); + acquire(); + destroy(f->ret->t1.dfd); + f->ret->t1.dfd = H; + destroy(f->ret->t1.cfd); + f->ret->t1.cfd = H; + if(f->ret->t0 == -1) + return; + + f->ret->t1.cfd = mkfd(f->ret->t0); + f->ret->t0 = 0; + retstr(dir, &f->ret->t1.dir); +} + +void +Sys_listen(void *fp) +{ + F_Sys_listen *f; + char dir[NETPATHLEN], *d; + + f = fp; + d = string2c(f->c.dir); + release(); + f->ret->t0 = klisten(d, dir); + acquire(); + + destroy(f->ret->t1.dfd); + f->ret->t1.dfd = H; + destroy(f->ret->t1.cfd); + f->ret->t1.cfd = H; + if(f->ret->t0 == -1) + return; + + f->ret->t1.cfd = mkfd(f->ret->t0); + f->ret->t0 = 0; + retstr(dir, &f->ret->t1.dir); +} + +void +Sys_sleep(void *fp) +{ + F_Sys_sleep *f; + + f = fp; + release(); + if(f->period > 0){ + if(waserror()){ + acquire(); + error(""); + } + osenter(); + *f->ret = limbosleep(f->period); + osleave(); + poperror(); + } + acquire(); +} + +void +Sys_stream(void *fp) +{ + Prog *p; + uchar *buf; + int src, dst; + F_Sys_stream *f; + int nbytes, t, n; + + f = fp; + buf = malloc(f->bufsiz); + if(buf == nil) { + kwerrstr(Enomem); + *f->ret = -1; + return; + } + + src = fdchk(f->src); + dst = fdchk(f->dst); + + p = currun(); + + release(); + t = 0; + nbytes = 0; + while(p->kill == nil) { + n = kread(src, buf+t, f->bufsiz-t); + if(n <= 0) + break; + t += n; + if(t >= f->bufsiz) { + if(kwrite(dst, buf, t) != t) { + t = 0; + break; + } + + nbytes += t; + t = 0; + } + } + if(t != 0) { + kwrite(dst, buf, t); + nbytes += t; + } + acquire(); + free(buf); + *f->ret = nbytes; +} + +void +Sys_export(void *fp) +{ + F_Sys_export *f; + + f = fp; + release(); + *f->ret = export(fdchk(f->c), string2c(f->dir), f->flag&Sys_EXPASYNC); + acquire(); +} + +void +Sys_file2chan(void *fp) +{ + int r; + Heap *h; + Channel *c; + Sys_FileIO *fio; + F_Sys_file2chan *f; + void *sv; + + h = heap(TFileIO); + + fio = H2D(Sys_FileIO*, h); + + c = cnewc(FioTread, movtmp, 16); + fio->read = c; + + c = cnewc(FioTwrite, movtmp, 16); + fio->write = c; + + f = fp; + sv = *f->ret; + *f->ret = fio; + destroy(sv); + + release(); + r = srvf2c(string2c(f->dir), string2c(f->file), fio); + acquire(); + if(r == -1) { + *f->ret = H; + destroy(fio); + } +} + +enum +{ + /* the following pctl calls can block and must release the virtual machine */ + BlockingPctl= Sys_NEWFD|Sys_FORKFD|Sys_NEWNS|Sys_FORKNS|Sys_NEWENV|Sys_FORKENV +}; + +void +Sys_pctl(void *fp) +{ + int fd; + Prog *p; + List *l; + Chan *c; + volatile struct {Pgrp *np;} np; + Pgrp *opg; + Chan *dot; + Osenv *o; + F_Sys_pctl *f; + Fgrp *fg, *ofg, *nfg; + volatile struct {Egrp *ne;} ne; + Egrp *oe; + + f = fp; + + p = currun(); + if(f->flags & BlockingPctl) + release(); + + np.np = nil; + ne.ne = nil; + if(waserror()) { + closepgrp(np.np); + closeegrp(ne.ne); + if(f->flags & BlockingPctl) + acquire(); + *f->ret = -1; + return; + } + + o = p->osenv; + if(f->flags & Sys_NEWFD) { + ofg = o->fgrp; + nfg = newfgrp(ofg); + lock(&ofg->l); + /* file descriptors to preserve */ + for(l = f->movefd; l != H; l = l->tail) { + fd = *(int*)l->data; + if(fd >= 0 && fd <= ofg->maxfd) { + c = ofg->fd[fd]; + if(c != nil && fd < nfg->nfd && nfg->fd[fd] == nil) { + incref(&c->r); + nfg->fd[fd] = c; + if(nfg->maxfd < fd) + nfg->maxfd = fd; + } + } + } + unlock(&ofg->l); + o->fgrp = nfg; + closefgrp(ofg); + } + else + if(f->flags & Sys_FORKFD) { + ofg = o->fgrp; + fg = dupfgrp(ofg); + /* file descriptors to close */ + for(l = f->movefd; l != H; l = l->tail) + kclose(*(int*)l->data); + o->fgrp = fg; + closefgrp(ofg); + } + + if(f->flags & Sys_NEWNS) { + np.np = newpgrp(); + dot = o->pgrp->dot; + np.np->dot = cclone(dot); + np.np->slash = cclone(dot); + cnameclose(np.np->slash->name); + np.np->slash->name = newcname("/"); + np.np->pin = o->pgrp->pin; /* pin is ALWAYS inherited */ + np.np->nodevs = o->pgrp->nodevs; + opg = o->pgrp; + o->pgrp = np.np; + np.np = nil; + closepgrp(opg); + } + else + if(f->flags & Sys_FORKNS) { + np.np = newpgrp(); + pgrpcpy(np.np, o->pgrp); + opg = o->pgrp; + o->pgrp = np.np; + np.np = nil; + closepgrp(opg); + } + + if(f->flags & Sys_NEWENV) { + oe = o->egrp; + o->egrp = newegrp(); + closeegrp(oe); + } + else + if(f->flags & Sys_FORKENV) { + ne.ne = newegrp(); + egrpcpy(ne.ne, o->egrp); + oe = o->egrp; + o->egrp = ne.ne; + ne.ne = nil; + closeegrp(oe); + } + + if(f->flags & Sys_NEWPGRP) + newgrp(p); + + if(f->flags & Sys_NODEVS) + o->pgrp->nodevs = 1; + + poperror(); + + if(f->flags & BlockingPctl) + acquire(); + + *f->ret = p->pid; +} + +void +Sys_dirread(void *fp) +{ + + Dir *b; + int i, n; + Heap *h; + uchar *d; + void *r; + F_Sys_dirread *f; + + f = fp; + f->ret->t0 = -1; + r = f->ret->t1; + f->ret->t1 = H; + destroy(r); + release(); + n = kdirread(fdchk(f->fd), &b); + acquire(); + if(n <= 0) { + f->ret->t0 = n; + free(b); + return; + } + if(waserror()){ + free(b); + return; + } + h = heaparray(Tdir, n); + poperror(); + d = H2D(Array*, h)->data; + for(i = 0; i < n; i++) { + unpackdir(b+i, (Sys_Dir*)d); + d += Sys_Dir_size; + } + f->ret->t0 = n; + f->ret->t1 = H2D(Array*, h); + free(b); +} + +void +Sys_fauth(void *fp) +{ + int fd; + F_Sys_fauth *f; + void *r; + + f = fp; + r = *f->ret; + *f->ret = H; + destroy(r); + release(); + fd = kfauth(fdchk(f->fd), string2c(f->aname)); + acquire(); + if(fd >= 0) + *f->ret = mkfd(fd); +} + +void +Sys_fversion(void *fp) +{ + void *r; + F_Sys_fversion *f; + int n; + char buf[20], *s; + + f = fp; + f->ret->t0 = -1; + r = f->ret->t1; + f->ret->t1 = H; + destroy(r); + s = string2c(f->version); + n = strlen(s); + if(n >= sizeof(buf)-1) + n = sizeof(buf)-1; + memmove(buf, s, n); + buf[n] = 0; + release(); + n = kfversion(fdchk(f->fd), f->msize, buf, sizeof(buf)); + acquire(); + if(n >= 0){ + f->ret->t0 = f->msize; + retnstr(buf, n, &f->ret->t1); + } +} + +void +Sys_iounit(void *fp) +{ + F_Sys_iounit *f; + + f = fp; + release(); + *f->ret = kiounit(fdchk(f->fd)); + acquire(); +} + +void +ccom(Progq **cl, Prog *p) +{ + volatile struct {Progq **cl;} vcl; + + cqadd(cl, p); + vcl.cl = cl; + if(waserror()) { + if(p->ptr != nil) { /* no killcomm */ + cqdelp(vcl.cl, p); + p->ptr = nil; + } + nexterror(); + } + cblock(p); + poperror(); +} + +void +crecv(Channel *c, void *ip) +{ + Prog *p; + REG rsav; + + if(c->send->prog == nil && c->size == 0) { + p = currun(); + p->ptr = ip; + ccom(&c->recv, p); + return; + } + + rsav = R; + R.s = &c; + R.d = ip; + irecv(); + R = rsav; +} + +void +csend(Channel *c, void *ip) +{ + Prog *p; + REG rsav; + + if(c->recv->prog == nil && (c->buf == H || c->size == c->buf->len)) { + p = currun(); + p->ptr = ip; + ccom(&c->send, p); + return; + } + + rsav = R; + R.s = ip; + R.d = &c; + isend(); + R = rsav; +} diff --git a/emu/port/ip.h b/emu/port/ip.h new file mode 100644 index 00000000..1462b755 --- /dev/null +++ b/emu/port/ip.h @@ -0,0 +1,64 @@ +enum +{ + IPaddrlen = 16, /* IPv6 */ + IPv4addrlen = 4, /* IPv4 */ + IPv4off = 12, /* length of IPv6 prefix for IPv4 addresses */ + Udphdrlen = 3*IPaddrlen+2*2, + OUdphdrlen = 2*IPaddrlen+2*2, + OUdphdrlenv4 = 2*IPv4addrlen+2*2, + + S_TCP = 0, + S_UDP +}; + +typedef struct Fs Fs; +typedef struct Proto Proto; +typedef struct Conv Conv; + +extern int so_socket(int type); +extern void so_connect(int, unsigned long, unsigned short); +extern void so_getsockname(int, unsigned long*, unsigned short*); +extern void so_bind(int, int, unsigned long, unsigned short); +extern void so_listen(int); +extern int so_accept(int, unsigned long*, unsigned short*); +extern int so_getservbyname(char*, char*, char*); +extern int so_gethostbyname(char*, char**, int); +extern int so_gethostbyaddr(char*, char**, int); +extern int so_recv(int, void*, int, void*, int); +extern int so_send(int, void*, int, void*, int); +extern void so_close(int); +extern int so_hangup(int, int); +extern void so_setsockopt(int, int, int); +extern int so_mustbind(int, int); +extern void so_keepalive(int, int); + + +extern void hnputl(void *p, unsigned long v); +extern void hnputs(void *p, unsigned short v); +extern unsigned long nhgetl(void *p); +extern unsigned short nhgets(void *p); +extern unsigned long parseip(uchar *to, char *from); +extern int parsemac(uchar *to, char *from, int len); +extern char* v4parseip(uchar*, char*); +extern int bipipe(int[]); + +extern int isv4(uchar*); +extern void v4tov6(uchar *v6, uchar *v4); +extern int v6tov4(uchar *v4, uchar *v6); +extern int eipfmt(Fmt*); + +#define ipmove(x, y) memmove(x, y, IPaddrlen) +#define ipcmp(x, y) ( (x)[IPaddrlen-1] != (y)[IPaddrlen-1] || memcmp(x, y, IPaddrlen) ) + +extern uchar IPv4bcast[IPaddrlen]; +extern uchar IPv4bcastobs[IPaddrlen]; +extern uchar IPv4allsys[IPaddrlen]; +extern uchar IPv4allrouter[IPaddrlen]; +extern uchar IPnoaddr[IPaddrlen]; +extern uchar v4prefix[IPaddrlen]; +extern uchar IPallbits[IPaddrlen]; + +extern void arpadd(char*, char*, int); +extern int arpwrite(char*, int); + +extern int Fsproto(Fs*, Proto*); diff --git a/emu/port/ipaux.c b/emu/port/ipaux.c new file mode 100644 index 00000000..9a938331 --- /dev/null +++ b/emu/port/ipaux.c @@ -0,0 +1,525 @@ +#include "dat.h" +#include "fns.h" +#include "ip.h" +#include "error.h" + +/* + * well known IP addresses + */ +uchar IPv4bcast[IPaddrlen] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff +}; +uchar IPv4allsys[IPaddrlen] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0xff, 0xff, + 0xe0, 0, 0, 0x01 +}; +uchar IPv4allrouter[IPaddrlen] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0xff, 0xff, + 0xe0, 0, 0, 0x02 +}; +uchar IPallbits[IPaddrlen] = { + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff +}; + +uchar IPnoaddr[IPaddrlen]; + +/* + * prefix of all v4 addresses + */ +uchar v4prefix[IPaddrlen] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0xff, 0xff, + 0, 0, 0, 0 +}; + +/* + * well known IPv6 addresses + */ +uchar v6Unspecified[IPaddrlen] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +uchar v6loopback[IPaddrlen] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0x01 +}; +uchar v6linklocal[IPaddrlen] = { + 0xfe, 0x80, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +uchar v6linklocalmask[IPaddrlen] = { + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +int v6linklocalprefix = 8; +uchar v6sitelocal[IPaddrlen] = { + 0xfe, 0xc0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +uchar v6sitelocalmask[IPaddrlen] = { + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +int v6sitelocalprefix = 6; +uchar v6glunicast[IPaddrlen] = { + 0x08, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +uchar v6multicast[IPaddrlen] = { + 0xff, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +uchar v6multicastmask[IPaddrlen] = { + 0xff, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +int v6multicastprefix = 1; +uchar v6allnodesN[IPaddrlen] = { + 0xff, 0x01, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0x01 +}; +uchar v6allnodesNmask[IPaddrlen] = { + 0xff, 0xff, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +int v6allnodesprefix = 2; +uchar v6allnodesL[IPaddrlen] = { + 0xff, 0x02, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0x01 +}; +uchar v6allnodesLmask[IPaddrlen] = { + 0xff, 0xff, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 +}; +int v6allnodesLprefix = 2; +uchar v6allroutersN[IPaddrlen] = { + 0xff, 0x01, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0x02 +}; +uchar v6allroutersL[IPaddrlen] = { + 0xff, 0x02, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0x02 +}; +uchar v6allroutersS[IPaddrlen] = { + 0xff, 0x05, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0x02 +}; +uchar v6solicitednode[IPaddrlen] = { + 0xff, 0x02, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0x01, + 0xff, 0, 0, 0 +}; +uchar v6solicitednodemask[IPaddrlen] = { + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0x0, 0x0, 0x0 +}; +int v6solicitednodeprefix = 13; + +enum +{ + Isprefix= 16, +}; + +int +eipfmt(Fmt *f) +{ + char buf[5*8]; + static char *efmt = "%.2lux%.2lux%.2lux%.2lux%.2lux%.2lux"; + static char *ifmt = "%d.%d.%d.%d"; + uchar *p, ip[16]; + ulong *lp; + ushort s; + int i, j, n, eln, eli, m, v; + + switch(f->r) { + case 'E': /* Ethernet address */ + p = va_arg(f->args, uchar*); + return fmtprint(f, efmt, p[0], p[1], p[2], p[3], p[4], p[5]); + case 'I': /* Ip address */ + p = va_arg(f->args, uchar*); +common: + if(memcmp(p, v4prefix, 12) == 0) + return fmtprint(f, ifmt, p[12], p[13], p[14], p[15]); + + /* find longest elision */ + eln = eli = -1; + for(i = 0; i < 16; i += 2){ + for(j = i; j < 16; j += 2) + if(p[j] != 0 || p[j+1] != 0) + break; + if(j > i && j - i > eln){ + eli = i; + eln = j - i; + } + } + + /* print with possible elision */ + n = 0; + for(i = 0; i < 16; i += 2){ + if(i == eli){ + n += sprint(buf+n, "::"); + i += eln; + if(i >= 16) + break; + } else if(i != 0) + n += sprint(buf+n, ":"); + s = (p[i]<<8) + p[i+1]; + n += sprint(buf+n, "%ux", s); + } + return fmtstrcpy(f, buf); + + case 'i': /* v6 address as 4 longs */ + lp = va_arg(f->args, ulong*); + for(i = 0; i < 4; i++) + hnputl(ip+4*i, *lp++); + p = ip; + goto common; + + case 'V': /* v4 ip address */ + p = va_arg(f->args, uchar*); + return fmtprint(f, ifmt, p[0], p[1], p[2], p[3]); + + case 'M': /* ip mask */ + p = va_arg(f->args, uchar*); + + /* look for a prefix mask */ + for(i = 0; i < 16; i++) + if(p[i] != 0xff) + break; + for(j = i+1; j < 16; j++) + if(p[j] != 0) + goto common; + n = 8*i; + if(i < IPaddrlen){ + v = p[i]; + for(m = 0x80; m != 0; m >>= 1){ + if((v & m) == 0) + break; + v &= ~m; + n++; + } + if(v != 0) + goto common; + } + + /* got one, use /xx format */ + return fmtprint(f, "/%d", n); + + } + return fmtstrcpy(f, "(eipfmt)"); +} + +#define CLASS(p) ((*(uchar*)(p))>>6) + +char* +v4parseip(uchar *to, char *from) +{ + int i; + char *p; + + p = from; + for(i = 0; i < 4 && *p; i++){ + to[i] = strtoul(p, &p, 0); + if(*p == '.') + p++; + } + switch(CLASS(to)){ + case 0: /* class A - 1 uchar net */ + case 1: + if(i == 3){ + to[3] = to[2]; + to[2] = to[1]; + to[1] = 0; + } else if(i == 2){ + to[3] = to[1]; + to[1] = 0; + } + break; + case 2: /* class B - 2 uchar net */ + if(i == 3){ + to[3] = to[2]; + to[2] = 0; + } + break; + } + return p; +} + +int +isv4(uchar *ip) +{ + return memcmp(ip, v4prefix, IPv4off) == 0; +} + + +/* + * the following routines are unrolled with no memset's to speed + * up the usual case + */ +void +v4tov6(uchar *v6, uchar *v4) +{ + v6[0] = 0; + v6[1] = 0; + v6[2] = 0; + v6[3] = 0; + v6[4] = 0; + v6[5] = 0; + v6[6] = 0; + v6[7] = 0; + v6[8] = 0; + v6[9] = 0; + v6[10] = 0xff; + v6[11] = 0xff; + v6[12] = v4[0]; + v6[13] = v4[1]; + v6[14] = v4[2]; + v6[15] = v4[3]; +} + +int +v6tov4(uchar *v4, uchar *v6) +{ + if(v6[0] == 0 + && v6[1] == 0 + && v6[2] == 0 + && v6[3] == 0 + && v6[4] == 0 + && v6[5] == 0 + && v6[6] == 0 + && v6[7] == 0 + && v6[8] == 0 + && v6[9] == 0 + && v6[10] == 0xff + && v6[11] == 0xff) + { + v4[0] = v6[12]; + v4[1] = v6[13]; + v4[2] = v6[14]; + v4[3] = v6[15]; + return 0; + } else { + memset(v4, 0, 4); + return -1; + } +} + +ulong +parseip(uchar *to, char *from) +{ + int i, elipsis = 0, v4 = 1; + ulong x; + char *p, *op; + + memset(to, 0, IPaddrlen); + p = from; + for(i = 0; i < 16 && *p; i+=2){ + op = p; + x = strtoul(p, &p, 16); + if(*p == '.' || (*p == 0 && i == 0)){ + p = v4parseip(to+i, op); + i += 4; + break; + } else { + to[i] = x>>8; + to[i+1] = x; + } + if(*p == ':'){ + v4 = 0; + if(*++p == ':'){ + elipsis = i+2; + p++; + } + } + } + if(i < 16){ + memmove(&to[elipsis+16-i], &to[elipsis], i-elipsis); + memset(&to[elipsis], 0, 16-i); + } + if(v4){ + to[10] = to[11] = 0xff; + return nhgetl(to+12); + } else + return 6; +} + +/* + * hack to allow ip v4 masks to be entered in the old + * style + */ +ulong +parseipmask(uchar *to, char *from) +{ + ulong x; + int i; + uchar *p; + + if(*from == '/'){ + /* as a number of prefix bits */ + i = atoi(from+1); + if(i < 0) + i = 0; + if(i > 128) + i = 128; + memset(to, 0, IPaddrlen); + for(p = to; i >= 8; i -= 8) + *p++ = 0xff; + if(i > 0) + *p = ~((1<<(8-i))-1); + x = nhgetl(to+IPv4off); + } else { + /* as a straight bit mask */ + x = parseip(to, from); + if(memcmp(to, v4prefix, IPv4off) == 0) + memset(to, 0xff, IPv4off); + } + return x; +} + +void +maskip(uchar *from, uchar *mask, uchar *to) +{ + int i; + + for(i = 0; i < IPaddrlen; i++) + to[i] = from[i] & mask[i]; +} + +uchar classmask[4][16] = { + 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0x00,0x00,0x00, + 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0x00,0x00,0x00, + 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0x00,0x00, + 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0x00, +}; + +uchar* +defmask(uchar *ip) +{ + if(isv4(ip)) + return classmask[ip[IPv4off]>>6]; + else { + if(ipcmp(ip, v6loopback) == 0) + return IPallbits; + else if(memcmp(ip, v6linklocal, v6linklocalprefix) == 0) + return v6linklocalmask; + else if(memcmp(ip, v6sitelocal, v6sitelocalprefix) == 0) + return v6sitelocalmask; + else if(memcmp(ip, v6solicitednode, v6solicitednodeprefix) == 0) + return v6solicitednodemask; + else if(memcmp(ip, v6multicast, v6multicastprefix) == 0) + return v6multicastmask; + return IPallbits; + } +} + +/* + * parse a hex mac address + */ +int +parsemac(uchar *to, char *from, int len) +{ + char nip[4]; + char *p; + int i; + + p = from; + memset(to, 0, len); + for(i = 0; i < len; i++){ + if(p[0] == '\0' || p[1] == '\0') + break; + + nip[0] = p[0]; + nip[1] = p[1]; + nip[2] = '\0'; + p += 2; + + to[i] = strtoul(nip, 0, 16); + if(*p == ':') + p++; + } + return i; +} + +void +hnputl(void *p, unsigned long v) +{ + unsigned char *a; + + a = p; + a[0] = v>>24; + a[1] = v>>16; + a[2] = v>>8; + a[3] = v; +} + +void +hnputs(void *p, unsigned short v) +{ + unsigned char *a; + + a = p; + a[0] = v>>8; + a[1] = v; +} + +unsigned long +nhgetl(void *p) +{ + unsigned char *a; + a = p; + return (a[0]<<24)|(a[1]<<16)|(a[2]<<8)|(a[3]<<0); +} + +unsigned short +nhgets(void *p) +{ + unsigned char *a; + a = p; + return (a[0]<<8)|(a[1]<<0); +} diff --git a/emu/port/ipif-posix.c b/emu/port/ipif-posix.c new file mode 100644 index 00000000..fffc4e7c --- /dev/null +++ b/emu/port/ipif-posix.c @@ -0,0 +1,417 @@ +#ifdef sun +#define uint uxuint +#define ulong uxulong +#define ushort uxushort +#endif +#include <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <netdb.h> +#include <sys/ioctl.h> +#undef ulong +#undef ushort +#undef uint + +#include "dat.h" +#include "fns.h" +#include "ip.h" +#include "error.h" + +int +so_socket(int type) +{ + int fd, one; + + switch(type) { + default: + error("bad protocol type"); + case S_TCP: + type = SOCK_STREAM; + break; + case S_UDP: + type = SOCK_DGRAM; + break; + } + + fd = socket(AF_INET, type, 0); + if(fd < 0) + oserror(); + if(type == SOCK_DGRAM){ + one = 1; + setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char*)&one, sizeof (one)); + }else{ + one = 1; + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(one)); + } + return fd; +} + +int +so_send(int sock, void *va, int len, void *hdr, int hdrlen) +{ + int r; + struct sockaddr sa; + struct sockaddr_in *sin; + char *h = hdr; + + + osenter(); + if(hdr == 0) + r = write(sock, va, len); + else { + memset(&sa, sizeof(sa), 0); + sin = (struct sockaddr_in*)&sa; + sin->sin_family = AF_INET; + switch(hdrlen){ + case OUdphdrlenv4: + memmove(&sin->sin_addr, h, 4); + memmove(&sin->sin_port, h+8, 2); + break; + case OUdphdrlen: + v6tov4((uchar*)&sin->sin_addr, h); + memmove(&sin->sin_port, h+2*IPaddrlen, 2); /* rport */ + break; + default: + v6tov4((uchar*)&sin->sin_addr, h); + memmove(&sin->sin_port, h+3*IPaddrlen, 2); + break; + } + r = sendto(sock, va, len, 0, &sa, sizeof(sa)); + } + osleave(); + return r; +} + +int +so_recv(int sock, void *va, int len, void *hdr, int hdrlen) +{ + int r, l; + struct sockaddr sa; + struct sockaddr_in *sin; + char h[Udphdrlen]; + + + osenter(); + if(hdr == 0) + r = read(sock, va, len); + else { + sin = (struct sockaddr_in*)&sa; + l = sizeof(sa); + r = recvfrom(sock, va, len, 0, &sa, &l); + if(r >= 0) { + memset(h, sizeof h, 0); + switch(hdrlen){ + case OUdphdrlenv4: + memmove(h, &sin->sin_addr, 4); + memmove(h+2*IPv4addrlen, &sin->sin_port, 2); + break; + case OUdphdrlen: + v4tov6(h, (uchar*)&sin->sin_addr); + memmove(h+2*IPaddrlen, &sin->sin_port, 2); + break; + default: + v4tov6(h, (uchar*)&sin->sin_addr); + memmove(h+3*IPaddrlen, &sin->sin_port, 2); + break; + } + + /* alas there's no way to get the local addr/port correctly. Pretend. */ + getsockname(sock, &sa, &l); + switch(hdrlen){ + case OUdphdrlenv4: + memmove(h+IPv4addrlen, &sin->sin_addr, IPv4addrlen); + memmove(h+2*IPv4addrlen+2, &sin->sin_port, 2); + break; + case OUdphdrlen: + v4tov6(h+IPaddrlen, (uchar*)&sin->sin_addr); + memmove(h+2*IPaddrlen+2, &sin->sin_port, 2); + break; + default: + v4tov6(h+IPaddrlen, (uchar*)&sin->sin_addr); + v4tov6(h+2*IPaddrlen, (uchar*)&sin->sin_addr); /* ifcaddr */ + memmove(h+3*IPaddrlen+2, &sin->sin_port, 2); + break; + } + memmove(hdr, h, hdrlen); + } + } + osleave(); + return r; +} + +void +so_close(int sock) +{ + close(sock); +} + +void +so_connect(int fd, unsigned long raddr, unsigned short rport) +{ + int r; + struct sockaddr sa; + struct sockaddr_in *sin; + + memset(&sa, 0, sizeof(sa)); + sin = (struct sockaddr_in*)&sa; + sin->sin_family = AF_INET; + hnputs(&sin->sin_port, rport); + hnputl(&sin->sin_addr.s_addr, raddr); + + osenter(); + r = connect(fd, &sa, sizeof(sa)); + osleave(); + if(r < 0) + oserror(); +} + +void +so_getsockname(int fd, unsigned long *laddr, unsigned short *lport) +{ + int len; + struct sockaddr sa; + struct sockaddr_in *sin; + + len = sizeof(sa); + if(getsockname(fd, &sa, &len) < 0) + oserror(); + + sin = (struct sockaddr_in*)&sa; + if(sin->sin_family != AF_INET || len != sizeof(*sin)) + error("not AF_INET"); + + *laddr = nhgetl(&sin->sin_addr.s_addr); + *lport = nhgets(&sin->sin_port); +} + +void +so_listen(int fd) +{ + int r; + + osenter(); + r = listen(fd, 5); + osleave(); + if(r < 0) + oserror(); +} + +int +so_accept(int fd, unsigned long *raddr, unsigned short *rport) +{ + int nfd, len; + struct sockaddr sa; + struct sockaddr_in *sin; + + sin = (struct sockaddr_in*)&sa; + + len = sizeof(sa); + osenter(); + nfd = accept(fd, &sa, &len); + osleave(); + if(nfd < 0) + oserror(); + + if(sin->sin_family != AF_INET || len != sizeof(*sin)) + error("not AF_INET"); + + *raddr = nhgetl(&sin->sin_addr.s_addr); + *rport = nhgets(&sin->sin_port); + return nfd; +} + +void +so_bind(int fd, int su, unsigned long addr, unsigned short port) +{ + int i, one; + struct sockaddr sa; + struct sockaddr_in *sin; + + sin = (struct sockaddr_in*)&sa; + + one = 1; + if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) < 0) { + oserrstr(up->genbuf, sizeof(up->genbuf)); + print("setsockopt: %s", up->genbuf); + } + + if(su) { + for(i = 600; i < 1024; i++) { + memset(&sa, 0, sizeof(sa)); + sin->sin_family = AF_INET; + hnputl(&sin->sin_addr.s_addr, addr); + hnputs(&sin->sin_port, i); + + if(bind(fd, &sa, sizeof(sa)) >= 0) + return; + } + oserror(); + } + + memset(&sa, 0, sizeof(sa)); + sin->sin_family = AF_INET; + hnputl(&sin->sin_addr.s_addr, addr); + hnputs(&sin->sin_port, port); + + if(bind(fd, &sa, sizeof(sa)) < 0) + oserror(); +} + +void +so_setsockopt(int fd, int opt, int value) +{ + int r; + struct linger l; + + if(opt == SO_LINGER){ + l.l_onoff = 1; + l.l_linger = (short) value; + osenter(); + r = setsockopt(fd, SOL_SOCKET, opt, (char *)&l, sizeof(l)); + osleave(); + }else + error(Ebadctl); + if(r < 0) + oserror(); +} + +int +so_gethostbyname(char *host, char**hostv, int n) +{ + int i; + unsigned char buf[32], *p; + struct hostent *hp; + + hp = gethostbyname(host); + if(hp == 0) + return 0; + + for(i = 0; hp->h_addr_list[i] && i < n; i++) { + p = hp->h_addr_list[i]; + sprint(buf, "%ud.%ud.%ud.%ud", p[0], p[1], p[2], p[3]); + hostv[i] = strdup(buf); + if(hostv[i] == 0) + break; + } + return i; +} + +int +so_gethostbyaddr(char *addr, char **hostv, int n) +{ + int i; + struct hostent *hp; + unsigned long straddr; + + straddr = inet_addr(addr); + if(straddr == -1) + return 0; + + hp = gethostbyaddr((char *)&straddr, sizeof(straddr), AF_INET); + if(hp == 0) + return 0; + + hostv[0] = strdup(hp->h_name); + if(hostv[0] == 0) + return 0; + for(i = 1; hp->h_aliases[i-1] && i < n; i++) { + hostv[i] = strdup(hp->h_aliases[i-1]); + if(hostv[i] == 0) + break; + } + return i; +} + +int +so_getservbyname(char *service, char *net, char *port) +{ + ushort p; + struct servent *s; + + s = getservbyname(service, net); + if(s == 0) + return -1; + p = s->s_port; + sprint(port, "%d", nhgets(&p)); + return 0; +} + +int +so_hangup(int fd, int linger) +{ + int r; + static struct linger l = {1, 1000}; + + osenter(); + if(linger) + setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&l, sizeof(l)); + r = shutdown(fd, 2); + if(r >= 0) + r = close(fd); + osleave(); + return r; +} + +void +arpadd(char *ipaddr, char *eaddr, int n) +{ +#ifdef SIOCGARP + struct arpreq a; + struct sockaddr_in pa; + int s; + uchar addr[IPaddrlen]; + + s = socket(AF_INET, SOCK_DGRAM, 0); + memset(&a, 0, sizeof(a)); + memset(&pa, 0, sizeof(pa)); + pa.sin_family = AF_INET; + pa.sin_port = 0; + parseip(addr, ipaddr); + if(!isv4(addr)){ + close(s); + error(Ebadarg); + } + memmove(&pa.sin_addr, ipaddr+IPv4off, IPv4addrlen); + memmove(&a.arp_pa, &pa, sizeof(pa)); + while(ioctl(s, SIOCGARP, &a) != -1) { + ioctl(s, SIOCDARP, &a); + memset(&a.arp_ha, 0, sizeof(a.arp_ha)); + } + a.arp_ha.sa_family = AF_UNSPEC; + parsemac((uchar*)a.arp_ha.sa_data, eaddr, 6); + a.arp_flags = ATF_PERM; + if(ioctl(s, SIOCSARP, &a) == -1) { + oserrstr(up->env->errstr, ERRMAX); + close(s); + error(up->env->errstr); + } + close(s); +#else + error("arp not implemented"); +#endif +} + +int +so_mustbind(int restricted, int port) +{ + return restricted || port != 0; +} + +void +so_keepalive(int fd, int ms) +{ + int on; + + on = 1; + setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char*)&on, sizeof(on)); +#ifdef TCP_KEEPIDLE + if(ms <= 120000) + ms = 120000; + ms /= 1000; + setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, (char*)&ms, sizeof(ms)); +#endif +} diff --git a/emu/port/keysym2ucs.h b/emu/port/keysym2ucs.h new file mode 100644 index 00000000..1f23ac66 --- /dev/null +++ b/emu/port/keysym2ucs.h @@ -0,0 +1,9 @@ +/* $XFree86: xc/programs/xterm/keysym2ucs.h,v 1.1 1999/06/12 15:37:18 dawes Exp $ */ +/* + * This module converts keysym values into the corresponding ISO 10646-1 + * (UCS, Unicode) values. + */ + +#include <X11/X.h> + +long keysym2ucs(KeySym keysym); diff --git a/emu/port/latin1.c b/emu/port/latin1.c new file mode 100644 index 00000000..9e8abf43 --- /dev/null +++ b/emu/port/latin1.c @@ -0,0 +1,83 @@ +#include "dat.h" + +/* + * The code makes two assumptions: strlen(ld) is 1 or 2; latintab[i].ld can be a + * prefix of latintab[j].ld only when j<i. + */ +static +struct cvlist +{ + char *ld; /* must be seen before using this conversion */ + char *si; /* options for last input characters */ + char *so; /* the corresponding Rune for each si entry */ +} latintab[] = { +#include "latin1.h" + 0, 0, 0 +}; + +/* + * Given 5 characters k[0]..k[4], find the rune or return -1 for failure. + */ +static long +unicode(uchar *k) +{ + long i, c; + + k++; /* skip 'X' */ + c = 0; + for(i=0; i<4; i++,k++){ + c <<= 4; + if('0'<=*k && *k<='9') + c += *k-'0'; + else if('a'<=*k && *k<='f') + c += 10 + *k-'a'; + else if('A'<=*k && *k<='F') + c += 10 + *k-'A'; + else + return -1; + } + return c; +} + +/* + * Given n characters k[0]..k[n-1], find the corresponding rune or return -1 for + * failure, or something < -1 if n is too small. In the latter case, the result + * is minus the required n. + */ +long +latin1(uchar *k, int n) +{ + struct cvlist *l; + int c; + char* p; + + if(k[0] == 'X') + if(n>=5) + return unicode(k); + else + return -5; + for(l=latintab; l->ld!=0; l++) + if(k[0] == l->ld[0]){ + if(n == 1) + return -2; + if(l->ld[1] == 0) + c = k[1]; + else if(l->ld[1] != k[1]) + continue; + else if(n == 2) + return -3; + else + c = k[2]; + for(p=l->si; *p!=0; p++) + if(*p == c) { + Rune r; + int i = p - l->si; + p = l->so; + for(; i >= 0; i--) + p += chartorune(&r, p); + return r; + } + return -1; + } + return -1; +} diff --git a/emu/port/latin1.h b/emu/port/latin1.h new file mode 100644 index 00000000..8ebab538 --- /dev/null +++ b/emu/port/latin1.h @@ -0,0 +1,99 @@ + " ", " i", "␣ı", + "!~", "-=~", "≄≇≉", + "!", "!<=>?bmp", "¡≮≠≯‽⊄∉⊅", + "\"*", "IUiu", "ΪΫϊϋ", + "\"", "\"AEIOUYaeiouy", "¨ÄËÏÖÜŸäëïöüÿ", + "$*", "fhk", "ϕϑϰ", + "$", "BEFHILMRVaefglopv", "ℬℰℱℋℐℒℳℛƲɑℯƒℊℓℴ℘ʋ", + "\'\"", "Uu", "Ǘǘ", + "\'", "\'ACEILNORSUYZacegilnorsuyz", "´ÁĆÉÍĹŃÓŔŚÚÝŹáćéģíĺńóŕśúýź", + "*", "*ABCDEFGHIKLMNOPQRSTUWXYZabcdefghiklmnopqrstuwxyz", "∗ΑΒΞΔΕΦΓΘΙΚΛΜΝΟΠΨΡΣΤΥΩΧΗΖαβξδεφγθικλμνοπψρστυωχηζ", + "+", "-O", "±⊕", + ",", ",ACEGIKLNORSTUacegiklnorstu", "¸ĄÇĘĢĮĶĻŅǪŖŞŢŲąçęģįķļņǫŗşţų", + "-*", "l", "ƛ", + "-", "+-2:>DGHILOTZbdghiltuz~", "∓ƻ÷→ÐǤĦƗŁ⊖ŦƵƀðǥℏɨłŧʉƶ≂", + ".", ".CEGILOZceglz", "·ĊĖĠİĿ⊙Żċėġŀż", + "/", "Oo", "Øø", + "1", "234568", "½⅓¼⅕⅙⅛", + "2", "-35", "ƻ⅔⅖", + "3", "458", "¾⅗⅜", + "4", "5", "⅘", + "5", "68", "⅚⅝", + "7", "8", "⅞", + ":", ")-=", "☺÷≔", + "<!", "=~", "≨⋦", + "<", "-<=>~", "←«≤≶≲", + "=", ":<=>OV", "≕⋜≡⋝⊜⇒", + ">!", "=~", "≩⋧", + ">", "<=>~", "≷≥»≳", + "?", "!?", "‽¿", + "@\'", "\'", "ъ", + "@@", "\'EKSTYZekstyz", "ьЕКСТЫЗекстыз", + "@C", "Hh", "ЧЧ", + "@E", "Hh", "ЭЭ", + "@K", "Hh", "ХХ", + "@S", "CHch", "ЩШЩШ", + "@T", "Ss", "ЦЦ", + "@Y", "AEOUaeou", "ЯЕЁЮЯЕЁЮ", + "@Z", "Hh", "ЖЖ", + "@c", "h", "ч", + "@e", "h", "э", + "@k", "h", "х", + "@s", "ch", "щш", + "@t", "s", "ц", + "@y", "aeou", "яеёю", + "@z", "h", "ж", + "@", "ABDFGIJLMNOPRUVXabdfgijlmnopruvx", "АБДФГИЙЛМНОПРУВХабдфгийлмнопрувх", + "A", "E", "Æ", + "C", "ACU", "⋂ℂ⋃", + "Dv", "Zz", "DŽDž", + "D", "-e", "Ð∆", + "G", "-", "Ǥ", + "H", "-H", "Ħℍ", + "I", "-J", "ƗIJ", + "L", "&-Jj|", "⋀ŁLJLj⋁", + "N", "JNj", "NJℕNj", + "O", "*+-./=EIcoprx", "⊛⊕⊖⊙⊘⊜ŒƢ©⊚℗®⊗", + "P", "P", "ℙ", + "Q", "Q", "ℚ", + "R", "R", "ℝ", + "S", "123S", "¹²³§", + "T", "-u", "Ŧ⊨", + "V", "=", "⇐", + "Y", "R", "Ʀ", + "Z", "-ACSZ", "Ƶℤ", + "^", "ACEGHIJOSUWYaceghijosuwy", "ÂĈÊĜĤÎĴÔŜÛŴŶâĉêĝĥîĵôŝûŵŷ", + "_\"", "AUau", "ǞǕǟǖ", + "_,", "Oo", "Ǭǭ", + "_.", "Aa", "Ǡǡ", + "_", "AEIOU_aeiou", "ĀĒĪŌŪ¯āēīōū", + "`\"", "Uu", "Ǜǜ", + "`", "AEIOUaeiou", "ÀÈÌÒÙàèìòù", + "a", "ben", "↔æ∠", + "b", "()+-0123456789=bknpqru", "₍₎₊₋₀₁₂₃₄₅₆₇₈₉₌♝♚♞♟♛♜•", + "c", "$Oagu", "¢©∩≅∪", + "dv", "z", "dž", + "d", "-adegz", "ð↓‡°†ʣ", + "e", "$lmns", "€⋯—–∅", + "f", "a", "∀", + "g", "$-r", "¤ǥ∇", + "h", "-v", "ℏƕ", + "i", "-bfjps", "ɨ⊆∞ij⊇∫", + "l", "\"$&\'-jz|", "“£∧‘łlj⋄∨", + "m", "iou", "µ∈×", + "n", "jo", "nj¬", + "o", "AOUaeiu", "Å⊚Ůåœƣů", + "p", "Odgrt", "℗∂¶∏∝", + "r", "\"\'O", "”’®", + "s", "()+-0123456789=abnoprstu", "⁽⁾⁺⁻⁰ⁱ⁴⁵⁶⁷⁸⁹⁼ª⊂ⁿº⊃√ß∍∑", + "t", "-efmsu", "ŧ∃∴™ς⊢", + "u", "-AEGIOUaegiou", "ʉĂĔĞĬŎŬ↑ĕğĭŏŭ", + "v\"", "Uu", "Ǚǚ", + "v", "ACDEGIKLNORSTUZacdegijklnorstuz", "ǍČĎĚǦǏǨĽŇǑŘŠŤǓŽǎčďěǧǐǰǩľňǒřšťǔž", + "w", "bknpqr", "♗♔♘♙♕♖", + "x", "O", "⊗", + "y", "$", "¥", + "z", "-", "ƶ", + "|", "Pp|", "Þþ¦", + "~!", "=", "≆", + "~", "-=AINOUainou~", "≃≅ÃĨÑÕŨãĩñõũ≈", diff --git a/emu/port/lock.c b/emu/port/lock.c new file mode 100644 index 00000000..48b5d8c2 --- /dev/null +++ b/emu/port/lock.c @@ -0,0 +1,141 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + +void +lock(Lock *l) +{ + int i; + + if(_tas(&l->val) == 0) + return; + for(i=0; i<100; i++){ + if(_tas(&l->val) == 0) + return; + osyield(); + } + for(i=1;; i++){ + if(_tas(&l->val) == 0) + return; + osmillisleep(i*10); + if(i > 100){ + osyield(); + i = 1; + } + } +} + +int +canlock(Lock *l) +{ + return _tas(&l->val) == 0; +} + +void +unlock(Lock *l) +{ + l->val = 0; +} + +void +qlock(QLock *q) +{ + Proc *p; + + lock(&q->use); + if(!q->locked) { + q->locked = 1; + unlock(&q->use); + return; + } + p = q->tail; + if(p == 0) + q->head = up; + else + p->qnext = up; + q->tail = up; + up->qnext = 0; + unlock(&q->use); + osblock(); +} + +int +canqlock(QLock *q) +{ + if(!canlock(&q->use)) + return 0; + if(q->locked){ + unlock(&q->use); + return 0; + } + q->locked = 1; + unlock(&q->use); + return 1; +} + +void +qunlock(QLock *q) +{ + Proc *p; + + lock(&q->use); + p = q->head; + if(p) { + q->head = p->qnext; + if(q->head == 0) + q->tail = 0; + unlock(&q->use); + osready(p); + return; + } + q->locked = 0; + unlock(&q->use); +} + +void +rlock(RWlock *l) +{ + qlock(&l->x); /* wait here for writers and exclusion */ + lock(&l->l); + l->readers++; + canqlock(&l->k); /* block writers if we are the first reader */ + unlock(&l->l); + qunlock(&l->x); +} + +/* same as rlock but punts if there are any writers waiting */ +int +canrlock(RWlock *l) +{ + if (!canqlock(&l->x)) + return 0; + lock(&l->l); + l->readers++; + canqlock(&l->k); /* block writers if we are the first reader */ + unlock(&l->l); + qunlock(&l->x); + return 1; +} + +void +runlock(RWlock *l) +{ + lock(&l->l); + if(--l->readers == 0) /* last reader out allows writers */ + qunlock(&l->k); + unlock(&l->l); +} + +void +wlock(RWlock *l) +{ + qlock(&l->x); /* wait here for writers and exclusion */ + qlock(&l->k); /* wait here for last reader */ +} + +void +wunlock(RWlock *l) +{ + qunlock(&l->k); + qunlock(&l->x); +} diff --git a/emu/port/main.c b/emu/port/main.c new file mode 100644 index 00000000..2409916d --- /dev/null +++ b/emu/port/main.c @@ -0,0 +1,440 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "interp.h" +#include "kernel.h" +#include "draw.h" +#include "version.h" + +int rebootargc = 0; +char** rebootargv; +static char *imod = "/dis/emuinit.dis"; +extern char* hosttype; +char* tkfont; /* for libtk/utils.c */ +int tkstylus; /* libinterp/tk.c */ +extern int mflag; + int dflag; + int vflag; + int vflag; + Procs procs; + char *eve; + int Xsize = 640; + int Ysize = 480; + int bflag = 1; + int sflag; + int qflag; + int xtblbit; + ulong displaychan; +char *cputype; + +static void +usage(void) +{ + fprint(2, "Usage: emu [options...] [file.dis [args...]]\n" + "\t-gXxY\n" + "\t-c[0-9]\n" + "\t-d file.dis\n" + "\t-s\n" + "\t-v\n" + "\t-p<poolname>=maxsize\n" + "\t-f<fontpath>\n" + "\t-r<rootpath>\n" + "\t-7\n" + "\t-B\n" + "\t-C<channel string>\n" + "\t-S\n"); + + exits("usage"); +} + +static void +envusage(void) +{ + fprint(2, "emu: bad option in EMU environment variable (%s)\n", getenv("EMU")); + usage(); +} + +static int +isnum(char *p) +{ + if (*p == 0) return 0; + while (*p) { + if (*p < '0' || *p > '9') return 0; + p++; + } + return 1; +} + +static int +geom(char *val) +{ + char *p; + int x, y; + if (val == '\0' || (*val < '0' || *val > '9')) + return 0; + x = strtoul(val, &p, 0); + if(x >= 64) + Xsize = x; + if (*p++ != 'x' || !isnum(p)) + return 0; + y = strtoul(p, &p, 0); + if(y >= 48) + Ysize = y; + if (*p != '\0') return 0; + return 1; +} + +static void +poolopt(char *str) +{ + char *var; + int n; + ulong x; + + var = str; + while(*str && *str != '=') + str++; + if(*str != '=' || str[1] == '\0') + usage(); + *str++ = '\0'; + n = strlen(str); + x = atoi(str); + switch(str[n - 1]){ + case 'k': + case 'K': + x *= 1024; + break; + case 'm': + case 'M': + x *= 1024*1024; + break; + } + if(poolsetsize(var, x) == 0) + usage(); +} + +static void +option(int argc, char *argv[], void (*badusage)(void)) +{ + char *cp; + + ARGBEGIN { + default: + badusage(); + case 'g': /* Window geometry */ + if (geom(EARGF(badusage())) == 0) + badusage(); + break; + case 'b': /* jit array bounds checking (obsolete, now on by default) */ + break; + case 'B': /* suppress jit array bounds checks */ + bflag = 0; + break; + case 'c': /* Compile on the fly */ + cp = EARGF(badusage()); + if (!isnum(cp)) + badusage(); + cflag = atoi(cp); + if(cflag < 0|| cflag > 9) + usage(); + break; + case 'I': /* (temporary option) run without cons */ + dflag++; + break; + case 'd': /* run as a daemon */ + dflag++; + imod = EARGF(badusage()); + break; + case 's': /* No trap handling */ + sflag++; + break; + case 'm': /* gc mark and sweep */ + cp = EARGF(badusage()); + if (!isnum(cp)) + badusage(); + mflag = atoi(cp); + if(mflag < 0|| mflag > 9) + usage(); + break; + case 'p': /* pool option */ + poolopt(EARGF(badusage())); + break; + case 'f': /* Set font path */ + tkfont = EARGF(badusage()); + break; + case 'r': /* Set inferno root */ + strecpy(rootdir, rootdir+sizeof(rootdir), EARGF(badusage())); + break; + case '7': /* use 7 bit colormap in X */ + xtblbit = 1; + break; + case 'G': /* allow global access to file system (obsolete) */ + break; + case 'C': /* channel specification for display */ + cp = EARGF(badusage()); + displaychan = strtochan(cp); + if(displaychan == 0){ + fprint(2, "emu: invalid channel specifier (-C): %q\n", cp); + exits("usage"); + } + break; + case 'S': + tkstylus = 1; + break; + case 'v': + vflag = 1; /* print startup messages */ + break; + } ARGEND +} + +static void +savestartup(int argc, char *argv[]) +{ + int i; + + rebootargc = argc; + rebootargv = malloc((argc+1)*sizeof(char*)); + if(rebootargv == nil) + panic("can't save startup args"); + for(i = 0; i < argc; i++) { + rebootargv[i] = strdup(argv[i]); + if(rebootargv[i] == nil) + panic("can't save startup args"); + } + rebootargv[i] = nil; +} + +void +putenvq(char *name, char *val, int conf) +{ + val = smprint("%q", val); + ksetenv(name, val, conf); + free(val); +} + +void +putenvqv(char *name, char **v, int n, int conf) +{ + Fmt f; + int i; + char *val; + + fmtstrinit(&f); + for(i=0; i<n; i++) + fmtprint(&f, "%s%q", i?" ":"", v[i]); + val = fmtstrflush(&f); + ksetenv(name, val, conf); + free(val); +} + +void +main(int argc, char *argv[]) +{ + char *opt, *p; + char *enva[20]; + int envc; + + quotefmtinstall(); + savestartup(argc, argv); + /* set default root now, so either $EMU or -r can override it later */ + if((p = getenv("INFERNO")) != nil || (p = getenv("ROOT")) != nil) + strecpy(rootdir, rootdir+sizeof(rootdir), p); + opt = getenv("EMU"); + if(opt != nil && *opt != '\0') { + enva[0] = "emu"; + envc = tokenize(opt, &enva[1], sizeof(enva)-1) + 1; + enva[envc] = 0; + option(envc, enva, envusage); + } + option(argc, argv, usage); + eve = strdup("inferno"); + + opt = "interp"; + if(cflag) + opt = "compile"; + + if(vflag) + print("Inferno %s main (pid=%d) %s\n", VERSION, getpid(), opt); + + libinit(imod); +} + +void +emuinit(void *imod) +{ + Osenv *e; + + e = up->env; + e->pgrp = newpgrp(); + e->fgrp = newfgrp(nil); + e->egrp = newegrp(); + e->errstr = e->errbuf0; + e->syserrstr = e->errbuf1; + e->user = strdup(""); + + links(); + chandevinit(); + + if(waserror()) + panic("setting root and dot"); + + e->pgrp->slash = namec("#/", Atodir, 0, 0); + cnameclose(e->pgrp->slash->name); + e->pgrp->slash->name = newcname("/"); + e->pgrp->dot = cclone(e->pgrp->slash); + poperror(); + + strcpy(up->text, "main"); + + if(kopen("#c/cons", OREAD) != 0) + fprint(2, "failed to make fd0 from #c/cons: %r\n"); + kopen("#c/cons", OWRITE); + kopen("#c/cons", OWRITE); + + /* the setid cannot precede the bind of #U */ + kbind("#U", "/", MAFTER|MCREATE); + setid(eve, 0); + kbind("#^", "/dev", MBEFORE); /* snarf */ + kbind("#^", "/chan", MBEFORE); + kbind("#m", "/dev", MBEFORE); /* pointer */ + kbind("#c", "/dev", MBEFORE); + kbind("#p", "/prog", MREPL); + kbind("#d", "/fd", MREPL); + kbind("#I", "/net", MAFTER); /* will fail on Plan 9 */ + + /* BUG: we actually only need to do these on Plan 9 */ + kbind("#U/dev", "/dev", MAFTER); + kbind("#U/net", "/net", MAFTER); + kbind("#U/net.alt", "/net.alt", MAFTER); + + if(cputype != nil) + ksetenv("cputype", cputype, 1); + putenvqv("emuargs", rebootargv, rebootargc, 1); + putenvq("emuroot", rootdir, 1); + ksetenv("emuhost", hosttype, 1); + + kproc("main", disinit, imod, KPDUPFDG|KPDUPPG|KPDUPENVG); + + for(;;) + ospause(); +} + +void +errorf(char *fmt, ...) +{ + va_list arg; + char buf[PRINTSIZE]; + + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + error(buf); +} + +void +error(char *err) +{ + if(err != up->env->errstr && up->env->errstr != nil) + kstrcpy(up->env->errstr, err, ERRMAX); +// ossetjmp(up->estack[NERR-1]); + nexterror(); +} + +void +exhausted(char *resource) +{ + char buf[64]; + int n; + + n = snprint(buf, sizeof(buf), "no free %s\n", resource); + iprint(buf); + buf[n-1] = 0; + error(buf); +} + +void +nexterror(void) +{ + oslongjmp(nil, up->estack[--up->nerr], 1); +} + +/* for dynamic modules - functions not macros */ + +void* +waserr(void) +{ + up->nerr++; + return up->estack[up->nerr-1]; +} + +void +poperr(void) +{ + up->nerr--; +} + +char* +enverror(void) +{ + return up->env->errstr; +} + +void +panic(char *fmt, ...) +{ + va_list arg; + char buf[512]; + + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + fprint(2, "panic: %s\n", buf); + if(sflag) + abort(); + + cleanexit(0); +} + +int +iprint(char *fmt, ...) +{ + + int n; + va_list va; + char buf[1024]; + + va_start(va, fmt); + n = vseprint(buf, buf+sizeof buf, fmt, va) - buf; + va_end(va); + + write(1, buf, n); + return 1; +} + +void +_assert(char *fmt) +{ + panic("assert failed: %s", fmt); +} + +/* + * mainly for libmp + */ +void +sysfatal(char *fmt, ...) +{ + va_list arg; + char buf[64]; + + va_start(arg, fmt); + vsnprint(buf, sizeof(buf), fmt, arg); + va_end(arg); + error(buf); +} + +void +oserror(void) +{ + oserrstr(up->env->errstr, ERRMAX); + error(up->env->errstr); +} diff --git a/emu/port/master b/emu/port/master new file mode 100644 index 00000000..6532f7a2 --- /dev/null +++ b/emu/port/master @@ -0,0 +1,36 @@ +# do not edit; automatically generated + +% mem +* indir +/ root +A audio +C cmd +D ssl +F tinyfs +I ip +M mnt +P prof +U fs +^ snarf +a arch +c cons +d dup +e env +i draw +m pointer +p prog +s srv +| pipe +τ tk +₪ srv9 + +α local use +β local use +γ local use +δ local use +ε local use +ζ local use +η local use +θ local use +ι local use +κ local use diff --git a/emu/port/master.local b/emu/port/master.local new file mode 100644 index 00000000..5d9438d3 --- /dev/null +++ b/emu/port/master.local @@ -0,0 +1,10 @@ +α local use +β local use +γ local use +δ local use +ε local use +ζ local use +η local use +θ local use +ι local use +κ local use diff --git a/emu/port/mkdevc b/emu/port/mkdevc new file mode 100644 index 00000000..825c76b0 --- /dev/null +++ b/emu/port/mkdevc @@ -0,0 +1,99 @@ +$AWK ' +BEGIN{ + if(ARGC < 2) + exit +} + +/^$/{ + next; +} +/^#/{ + next; +} +collect && /^[^ \t]/{ + collect = 0; +} +collect && section ~ "dev"{ + dev[ndev++] = $1; +} +collect && section ~ "ip"{ + ip[nip++] = $1; +} +collect && section ~ "link"{ + link[nlink++] = $1; +} +collect && section ~ "mod"{ + mod[nmod++] = $1; +} +collect && section ~ "misc"{ + misc[nmisc++] = $1; +} +collect && section ~ "port"{ + port[nport++] = $0; +} +collect && section ~ "code"{ + code[ncode++] = $0; +} +$0 ~ /^[^ \t]/{ + if($0 ~ "(code|dev|ip|lib|link|mod|misc|port|root)"){ + section = $0; + collect = 1; + } + next; +} + +END{ + if(ARGC < 2) + exit "usage" + + printf "#include \"dat.h\"\n" + printf "#include \"fns.h\"\n" + printf "#include \"error.h\"\n" + printf "#include \"interp.h\"\n\n\n" + printf "#include \"%s.root.h\"\n\n", ARGV[1]; + + nildev = 8; + printf "ulong ndevs = %s;\n\n", ndev+nildev + for(i = 0; i < ndev; i++) + printf "extern Dev %sdevtab;\n", dev[i]; + printf "Dev* devtab[]={\n" + for(i = 0; i < ndev; i++) + printf "\t&%sdevtab,\n", dev[i]; + for(i = 0; i < nildev; i++) + printf("\tnil,\n"); + printf "\tnil,\n};\n\n"; + + + for(i = 0; i < nlink; i++) + printf "extern void %slink(void);\n", link[i]; + + printf "void links(void){\n"; + for(i = 0; i < nlink; i++) + printf "\t%slink();\n", link[i]; + printf "}\n\n"; + + for(i = 0; i < nmod; i++) + printf "extern void %smodinit(void);\n", mod[i]; + printf "void modinit(void){\n"; + for(i = 0; i < nmod; i++) + printf "\t%smodinit();\n",mod[i]; + printf "}\n\n"; + + if(nip){ + printf "#include \"../ip/ip.h\"\n"; + for(i = 0; i < nip; i++) + printf "extern void %sinit(Fs*);\n", ip[i]; + printf "void (*ipprotoinit[])(Fs*) = {\n"; + for(i = 0; i < nip; i++) + printf "\t%sinit,\n", ip[i]; + printf "\tnil,\n};\n\n"; + } + + for(i = 0; i < ncode; i++) + printf "%s\n", code[i]; + + printf "char* conffile = \"%s\";\n", ARGV[1]; + printf "ulong kerndate = KERNDATE;\n"; + + exit +}' $* diff --git a/emu/port/mkdevlist b/emu/port/mkdevlist new file mode 100644 index 00000000..eea5ae16 --- /dev/null +++ b/emu/port/mkdevlist @@ -0,0 +1,75 @@ +$AWK ' +BEGIN{ + var["init"] = "INIT="; + var["ip"] = "IP="; + var["lib"] = "LIBS="; + var["root"] = "ROOTFILES="; + infernoroot = ENVIRON["ROOT"]; +} +/^$/{ next; +} +/^#/{ next; +} +/^env/{ + inenv = 1; + next; +} +inenv != 0 && /^[ ]/{ + sub("^[ ]*", "", $0) + printf "%s\n", $0 + next +} + +/^(code|dev|init|ip|lib|link|mod|misc|port|root)/{ + inenv = 0; + type = $1; + next; +} +/^[^ ]/ { + inenv = 0; +} +type && /^[ ]/{ + if(type == "code") + next; + if(type == "root"){ + if (NF > 1) + file = $2; + else if ($1 == "/osinit.dis") + next; # handled via explicit dependency + else + file = $1; + if(rootfile[file] == 0){ + var[type] = var[type] " " infernoroot file; + rootfile[file]++; + } + next; + } + if(type == "init" || type == "lib"){ + var[type] = var[type] " " $1; + next; + } + file = $1 "'.$O'" + if(type == "port") + port[file]++; + else if(type == "dev") + obj["dev" file]++; + else if(type != "mod") + obj[file]++; + for(i = 2; i <= NF; i++){ + if($i !~ "^[+=-].*") + obj[$i "'.$O'"]++; + } + next; +} +END{ + x = "" + for(i in obj) + x = x " " i + printf "DEVS=%s\n", x; + x = "" + for(i in port) + x = x " " i + printf "PORT=%s\n", x + for(v in var) + printf "%s\n", var[v] +}' $* diff --git a/emu/port/mkfile b/emu/port/mkfile new file mode 100644 index 00000000..4817e894 --- /dev/null +++ b/emu/port/mkfile @@ -0,0 +1,15 @@ +# If the existence of this mkfile screws something up, rename it. -rsc + +master:DV: + { + echo '# do not edit; automatically generated' + echo + echo ' + X , s/Dev (.*)devtab.*{.*\n L?''(.*)''/DEV \1 \2\n/ + X ,x g/^DEV/ p + ' | sam -d ../*/dev*.c >[2]/dev/null | + awk '/^DEV/ { printf("%s\t%s\n", $3, $2); }' | + sort -u + echo + cat master.local + } >master diff --git a/emu/port/mkroot b/emu/port/mkroot new file mode 100644 index 00000000..46f0a0b0 --- /dev/null +++ b/emu/port/mkroot @@ -0,0 +1,121 @@ +$AWK ' +BEGIN{ + if (ARGC < 2) + exit "usage"; + + conf = ARGV[1]; + infernoroot = ENVIRON["ROOT"]; + init = ENVIRON["INIT"]; + data2c = ENVIRON["DATA2C"]; + if(data2c == "") + data2c = "data2c" + nroot = 0; +} +/^$/{ + next; +} +/^#/{ + next; +} +collect && /^[^ \t]/{ + collect = 0; +} +collect && section ~ "root"{ + dst[nroot] = $1; + if (NF > 1) + src[nroot] = infernoroot $2; + else if (dst[nroot] == "/osinit.dis") + src[nroot] = infernoroot "/dis/" init ".dis"; + else + src[nroot] = infernoroot $1; + for(i=0; i<nroot; i++) + if(dst[i] == dst[nroot]) + break; + if(i == nroot) + nroot++; +} +$0 ~ /^[^ \t]/{ + if($0 ~ "(code|dev|ether|ip|lib|link|mod|misc|port|root|vga)"){ + section = $0; + collect = 1; + } + next; +} +END{ + rootdata = conf ".root.c"; + system("rm -f " rootdata); + print("/* Generated by mkroot */") >rootdata; + close(rootdata); + isdir[0] = 1; + dotdot[0] = 0; + qid = 1; + for (i = 0; i < nroot; i++) { + ncomp = split(dst[i], comp, "/"); + if (comp[1] != "" || ncomp < 2) + continue; + q = 0; + for (j = 2; j <= ncomp; j++) { + key = q "/" comp[j]; + if (walk[key] == 0) { + walk[key] = qid; + dotdot[qid] = q; + q = qid++; + name[q] = comp[j]; + if (j < ncomp) + isdir[q] = 1; + } + else + q = walk[key]; + } + if (system("test -d " src[i]) == 0) + isdir[q] = 1; + else { + if (system(data2c " root" q " <" src[i] " >>" rootdata) != 0) + exit 1; + print("extern unsigned char root" q "code[];"); + print("extern int root" q "len;"); + } + } + + x = 1; + sort[0] = 0; + unsort[0] = 0; + for (q = 0; q < qid; q++) { + if (isdir[q]) { + nchild[q] = 0; + for (q2 = 1; q2 < qid; q2++) { + if (dotdot[q2] == q) { + if (nchild[q]++ == 0) + child0[q] = x; + sort[q2] = x++; + unsort[sort[q2]] = q2; + } + } + } + } + + print("int rootmaxq = " qid ";"); + + print("Dirtab roottab[" qid "] = {"); + for (oq = 0; oq < qid; oq++) { + q = unsort[oq]; + if (!isdir[q]) + print("\t\"" name[q] "\",\t{" oq ", 0, QTFILE},\t", "0,\t0444,"); + else + print("\t\"" name[q] "\",\t{" oq ", 0, QTDIR},\t", "0,\t0555,"); + } + print("};"); + + print("Rootdata rootdata[" qid "] = {"); + for (oq = 0; oq < qid; oq++) { + q = unsort[oq]; + if (!isdir[q]) + print("\t" sort[dotdot[q]] ",\t", "root" q "code,\t", "0,\t", "&root" q "len,"); + else if (nchild[q]) + print("\t" sort[dotdot[q]] ",\t", "&roottab[" child0[q] "],\t", nchild[q] ",\tnil,"); + else + print("\t" sort[dotdot[q]] ",\t", "nil,\t", "0,\t", "nil,"); + } + print("};"); +} +' $1 >$1.root.h diff --git a/emu/port/parse.c b/emu/port/parse.c new file mode 100644 index 00000000..fc84d17b --- /dev/null +++ b/emu/port/parse.c @@ -0,0 +1,111 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + +/* + * Generous estimate of number of fields, including terminal nil pointer + */ +static int +ncmdfield(char *p, int n) +{ + int white, nwhite; + char *ep; + int nf; + + if(p == nil) + return 1; + + nf = 0; + ep = p+n; + white = 1; /* first text will start field */ + while(p < ep){ + nwhite = (strchr(" \t\r\n", *p++ & 0xFF) != 0); /* UTF is irrelevant */ + if(white && !nwhite) /* beginning of field */ + nf++; + white = nwhite; + } + return nf+1; /* +1 for nil */ +} + +/* + * parse a command written to a device + */ +Cmdbuf* +parsecmd(char *p, int n) +{ + Cmdbuf *volatile cb; + int nf; + char *sp; + + nf = ncmdfield(p, n); + + /* allocate Cmdbuf plus string pointers plus copy of string including \0 */ + sp = smalloc(sizeof(*cb) + nf * sizeof(char*) + n + 1); + cb = (Cmdbuf*)sp; + cb->f = (char**)(&cb[1]); + cb->buf = (char*)(&cb->f[nf]); + + if(up!=nil && waserror()){ + free(cb); + nexterror(); + } + memmove(cb->buf, p, n); + if(up != nil) + poperror(); + + /* dump new line and null terminate */ + if(n > 0 && cb->buf[n-1] == '\n') + n--; + cb->buf[n] = '\0'; + + cb->nf = tokenize(cb->buf, cb->f, nf-1); + cb->f[cb->nf] = nil; + + return cb; +} + +/* + * Reconstruct original message, for error diagnostic + */ +void +cmderror(Cmdbuf *cb, char *s) +{ + int i; + char *p, *e; + + p = up->genbuf; + e = p+ERRMAX-10; + p = seprint(p, e, "%s \"", s); + for(i=0; i<cb->nf; i++){ + if(i > 0) + p = seprint(p, e, " "); + p = seprint(p, e, "%q", cb->f[i]); + } + strcpy(p, "\""); + error(up->genbuf); +} + +/* + * Look up entry in table + */ +Cmdtab* +lookupcmd(Cmdbuf *cb, Cmdtab *ctab, int nctab) +{ + int i; + Cmdtab *ct; + + if(cb->nf == 0) + error("empty control message"); + + for(ct = ctab, i=0; i<nctab; i++, ct++){ + if(strcmp(ct->cmd, "*") !=0) /* wildcard always matches */ + if(strcmp(ct->cmd, cb->f[0]) != 0) + continue; + if(ct->narg != 0 && ct->narg != cb->nf) + cmderror(cb, Ecmdargs); + return ct; + } + + cmderror(cb, "unknown control message"); + return nil; +} diff --git a/emu/port/pgrp.c b/emu/port/pgrp.c new file mode 100644 index 00000000..b3d473f3 --- /dev/null +++ b/emu/port/pgrp.c @@ -0,0 +1,265 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + +static Ref pgrpid; +static Ref mountid; + +Pgrp* +newpgrp(void) +{ + Pgrp *p; + + p = malloc(sizeof(Pgrp)); + if(p == nil) + error(Enomem); + p->r.ref = 1; + p->pgrpid = incref(&pgrpid); + p->pin = Nopin; + p->progmode = 0644; + p->privatemem = 0; + return p; +} + +void +closepgrp(Pgrp *p) +{ + Mhead **h, **e, *f, *next; + + if(p == nil || decref(&p->r) != 0) + return; + + wlock(&p->ns); + p->pgrpid = -1; + e = &p->mnthash[MNTHASH]; + for(h = p->mnthash; h < e; h++) { + for(f = *h; f; f = next) { + wlock(&f->lock); + cclose(f->from); + mountfree(f->mount); + f->mount = nil; + next = f->hash; + wunlock(&f->lock); + putmhead(f); + } + } + wunlock(&p->ns); + cclose(p->slash); + cclose(p->dot); + free(p); +} + +void +pgrpinsert(Mount **order, Mount *m) +{ + Mount *f; + + m->order = 0; + if(*order == 0) { + *order = m; + return; + } + for(f = *order; f; f = f->order) { + if(m->mountid < f->mountid) { + m->order = f; + *order = m; + return; + } + order = &f->order; + } + *order = m; +} + +/* + * pgrpcpy MUST preserve the mountid allocation order of the parent group + */ +void +pgrpcpy(Pgrp *to, Pgrp *from) +{ + int i; + Mount *n, *m, **link, *order; + Mhead *f, **tom, **l, *mh; + + wlock(&from->ns); + order = 0; + tom = to->mnthash; + for(i = 0; i < MNTHASH; i++) { + l = tom++; + for(f = from->mnthash[i]; f; f = f->hash) { + rlock(&f->lock); + mh = malloc(sizeof(Mhead)); + if(mh == nil) { + runlock(&f->lock); + wunlock(&from->ns); + error(Enomem); + } + mh->from = f->from; + mh->r.ref = 1; + incref(&mh->from->r); + *l = mh; + l = &mh->hash; + link = &mh->mount; + for(m = f->mount; m; m = m->next) { + n = newmount(mh, m->to, m->mflag, m->spec); + if(n == nil) { + runlock(&f->lock); + wunlock(&from->ns); + error(Enomem); + } + m->copy = n; + pgrpinsert(&order, m); + *link = n; + link = &n->next; + } + runlock(&f->lock); + } + } + /* + * Allocate mount ids in the same sequence as the parent group + */ + lock(&mountid.lk); + for(m = order; m; m = m->order) + m->copy->mountid = mountid.ref++; + unlock(&mountid.lk); + + to->pin = from->pin; + + to->slash = cclone(from->slash); + to->dot = cclone(from->dot); + to->nodevs = from->nodevs; + wunlock(&from->ns); +} + +Fgrp* +newfgrp(Fgrp *old) +{ + Fgrp *new; + int n; + + new = malloc(sizeof(Fgrp)); + if(new == nil) + error(Enomem); + new->r.ref = 1; + n = DELTAFD; + if(old != nil){ + lock(&old->l); + if(old->maxfd >= n) + n = (old->maxfd+1 + DELTAFD-1)/DELTAFD * DELTAFD; + new->maxfd = old->maxfd; + unlock(&old->l); + } + new->nfd = n; + new->fd = malloc(n*sizeof(Chan*)); + if(new->fd == nil){ + free(new); + error(Enomem); + } + return new; +} + +Fgrp* +dupfgrp(Fgrp *f) +{ + int i; + Chan *c; + Fgrp *new; + int n; + + new = malloc(sizeof(Fgrp)); + if(new == nil) + error(Enomem); + new->r.ref = 1; + lock(&f->l); + n = DELTAFD; + if(f->maxfd >= n) + n = (f->maxfd+1 + DELTAFD-1)/DELTAFD * DELTAFD; + new->nfd = n; + new->fd = malloc(n*sizeof(Chan*)); + if(new->fd == nil){ + unlock(&f->l); + free(new); + error(Enomem); + } + new->maxfd = f->maxfd; + new->minfd = f->minfd; + for(i = 0; i <= f->maxfd; i++) { + if(c = f->fd[i]){ + incref(&c->r); + new->fd[i] = c; + } + } + unlock(&f->l); + + return new; +} + +void +closefgrp(Fgrp *f) +{ + int i; + Chan *c; + + if(f != nil && decref(&f->r) == 0) { + for(i = 0; i <= f->maxfd; i++) + if(c = f->fd[i]) + cclose(c); + free(f->fd); + free(f); + } +} + +Mount* +newmount(Mhead *mh, Chan *to, int flag, char *spec) +{ + Mount *m; + + m = malloc(sizeof(Mount)); + if(m == nil) + error(Enomem); + m->to = to; + m->head = mh; + incref(&to->r); + m->mountid = incref(&mountid); + m->mflag = flag; + if(spec != 0) + kstrdup(&m->spec, spec); + + return m; +} + +void +mountfree(Mount *m) +{ + Mount *f; + + while(m) { + f = m->next; + cclose(m->to); + m->mountid = 0; + free(m->spec); + free(m); + m = f; + } +} + +void +closesigs(Skeyset *s) +{ + int i; + + if(s == nil || decref(&s->r) != 0) + return; + for(i=0; i<s->nkey; i++) + freeskey(s->keys[i]); + free(s); +} + +void +freeskey(Signerkey *key) +{ + if(key == nil || decref(&key->r) != 0) + return; + free(key->owner); + (*key->pkfree)(key->pk); + free(key); +} diff --git a/emu/port/portmkfile b/emu/port/portmkfile new file mode 100644 index 00000000..498d5674 --- /dev/null +++ b/emu/port/portmkfile @@ -0,0 +1,169 @@ +PORTHFILES=\ + $ROOT/$OBJDIR/include/lib9.h\ + $ROOT/include/fcall.h\ + $ROOT/include/interp.h\ + $ROOT/include/draw.h\ + ../port/error.h\ + ../port/dat.h\ + ../port/fns.h\ + +LIBNAMES=${LIBS:%=lib%.a} +LIBFILES=${LIBS:%=$ROOT/$SYSTARG/$OBJTYPE/lib/lib%.a} + +$ROOT/$SYSTARG/$OBJTYPE/lib/lib%.a:N: lib%.a + +%.$O: %.s + $AS $ASFLAGS $stem.s + +%.$O: %.S$MACOSINF + $AS $ASFLAGS $stem.S + +%.$O: %.c + $CC $CFLAGS $stem.c + +%.$O: ../port/%.c + $CC $CFLAGS -I. ../port/$stem.c + +%.$O: ../ip/%.c + $CC $CFLAGS -I. ../ip/$stem.c + +&.$O: $HFILES $PORTHFILES + +$INSTALLDIR/%: % + cp $stem $INSTALLDIR/$stem + +installall:V: install-$SHELLTYPE +all:V: default-$SHELLTYPE + +# Plan 9 only (requires the compiler) +acid:V: i$CONF.acid + +i$CONF.acid: i$CONF + { + for (i in `{srclist -ec -r $ROOT/ i$CONF}) { + echo '//FILE: ' $i + $CC -I. -I../port -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include -I$ROOT/libinterp '-DKERNDATE='$KERNDATE -a $i + } + echo 'include ("inferno");' + } >i$CONF.acid + +lib%.a:V: $SHELLTYPE-lib%.a + +rc-lib%.a nt-lib%.a:VQ: + echo '@{builtin cd' $ROOT/lib$stem '; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install}' + @{builtin cd $ROOT/lib$stem; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE install} + +sh-lib%.a:VQ: + echo "(cd $ROOT/lib$stem ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install)" + (cd $ROOT/lib$stem; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE install) + +%-rc %-nt:V: + for(i in $CONFLIST) + mk 'CONF='$i $stem + +%-sh:V: + for i in $CONFLIST + do + mk 'CONF='$i $stem + done + +clean:V: cleanconf-$SHELLTYPE + rm -f *.[$OS] *.root.[shc] errstr.h *.out *.obj + +cleanconf-sh:V: + for i in $CONFLIST $CLEANCONFLIST + do + rm -f $i.c [$OS].$i i$i i$i.* $i.ver + done + +cleanconf-rc:V: + for(i in $CONFLIST $CLEANCONFLIST) + rm -f $i.c [$OS].$i i$i i$i.* $i.ver + +cleanconf-nt:V: + for(i in $CONFLIST $CLEANCONFLIST) + rm -f $i.c [$OS].$i i$i i$i.* $i.ver $i.exe + +nuke-sh:QV: + for i in $LIBDIRS + do + echo "(cd $ROOT/lib$i ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE nuke)" + (cd $ROOT/lib$i; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE nuke) + done + +nuke-rc nuke-nt:QV: + for (i in $LIBDIRS) + { + echo '@{cd $ROOT/lib$i ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE nuke}' + @{cd $ROOT/lib$i; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE nuke} + } + +nuke:V: clean nuke-$SHELLTYPE + rm -f srv.h srvm.h + +$CONF.c: ../port/mkdevc $CONF + $SHELLNAME ../port/mkdevc $CONF > $CONF.c + +errstr.h: ../port/error.h + sed 's/extern //;s,;.*/\* , = ",;s, \*/,";,' < ../port/error.h > errstr.h + +../init/%.dis: ../init/%.b + cd ../init; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE $stem.dis + +$ROOT/libinterp/runt.h: + cd $ROOT/libinterp + mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE runt.h + +INTERP=$ROOT/include/interp.h +RUNT=$ROOT/libinterp/runt.h # for culling dependencies + +alloc.$O: ../../include/pool.h\ + $INTERP +devcons.$O: $ROOT/include/version.h +devdraw.$O: $ROOT/include/draw.h\ + $ROOT/include/memdraw.h\ + $ROOT/include/memlayer.h +devmem.$O: $INTERP +devpipe.$O: $INTERP +devprof.$O: $INTERP +devprog.$O: $ROOT/libinterp/runt.h\ + $INTERP +devsign.$O: $INTERP +devsign.$O: $ROOT/libkeyring/keys.h +devsrv.$O: $ROOT/libinterp/runt.h\ + $INTERP +devtk.$O: $INTERP +dis.$O: $INTERP +discall.$O: $INTERP +exception.$O: $INTERP +inferno.$O: $ROOT/libinterp/runt.h\ + $INTERP +devmnt.$O: ../../include/fcall.h +main.$O: $ROOT/include/version.h +main.$O: ../port/error.h\ + $INTERP +proc.$O: errstr.h\ + $INTERP +srv.$O: $INTERP +devroot.$O: errstr.h +latin1.$O: ../port/latin1.h +netif.$O: ../port/netif.h +devprog.$O: $RUNT +devsrv.$O: $RUNT +exception.$O: $RUNT +inferno.$O: $RUNT +ipif-$SYSTARG.$O devip.$O: ../port/ip.h + +devroot.$O: $CONF.root.h +$CONF.$O: $CONF.root.h +$CONF.root.c $CONF.root.h: $CONF ../port/mkroot $ROOTFILES + $SHELLNAME ../port/mkroot $CONF + +audio.c:N: ../port/audio-tbls.c +audio.c:N: ../port/audio.h + +srv.$O: srv.h srvm.h +srv.h srvm.h:D: $ROOT/module/srvrunt.b $ROOT/module/srv.m + rm -f $alltarget + limbo -a -I$ROOT/module $ROOT/module/srvrunt.b >srv.h + limbo -t Srv -I$ROOT/module $ROOT/module/srvrunt.b >srvm.h diff --git a/emu/port/print.c b/emu/port/print.c new file mode 100644 index 00000000..4d166506 --- /dev/null +++ b/emu/port/print.c @@ -0,0 +1,23 @@ +#include "dat.h" +#include "fns.h" + +static Lock fmtl; + +void +_fmtlock(void) +{ + lock(&fmtl); +} + +void +_fmtunlock(void) +{ + unlock(&fmtl); +} + +int +_efgfmt(Fmt *f) +{ + USED(f); + return -1; +} diff --git a/emu/port/proc.c b/emu/port/proc.c new file mode 100644 index 00000000..fccf1a36 --- /dev/null +++ b/emu/port/proc.c @@ -0,0 +1,239 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "interp.h" + +Proc* +newproc(void) +{ + Proc *p; + + p = malloc(sizeof(Proc)); + if(p == nil) + return nil; + + p->type = Unknown; + p->killed = 0; + p->swipend = 0; + p->env = &p->defenv; + kstrdup(&p->env->user, "*nouser"); + p->env->errstr = p->env->errbuf0; + p->env->syserrstr = p->env->errbuf1; + addprog(p); + + return p; +} + +void +Sleep(Rendez *r, int (*f)(void*), void *arg) +{ + lock(&up->rlock); + lock(&r->l); + + /* + * if interrupted or condition happened, never mind + */ + if(up->killed || f(arg)) { + unlock(&r->l); + }else{ + if(r->p != nil) + panic("double sleep pc=0x%lux %s[%lud] %s[%lud] r=0x%lux\n", getcallerpc(&r), r->p->text, r->p->pid, up->text, up->pid, r); + + r->p = up; + unlock(&r->l); + up->swipend = 0; + up->r = r; /* for swiproc */ + unlock(&up->rlock); + + osblock(); + + lock(&up->rlock); + up->r = nil; + } + + if(up->killed || up->swipend) { + up->killed = 0; + up->swipend = 0; + unlock(&up->rlock); + error(Eintr); + } + unlock(&up->rlock); +} + +int +Wakeup(Rendez *r) +{ + Proc *p; + + lock(&r->l); + p = r->p; + if(p != nil) { + r->p = nil; + osready(p); + } + unlock(&r->l); + return p != nil; +} + +void +swiproc(Proc *p, int interp) +{ + Rendez *r; + + if(p == nil) + return; + + /* + * Pull out of emu Sleep + */ + lock(&p->rlock); + if(!interp) + p->killed = 1; + r = p->r; + if(r != nil) { + lock(&r->l); + if(r->p == p){ + p->swipend = 1; + r->p = nil; + osready(p); + } + unlock(&r->l); + unlock(&p->rlock); + return; + } + unlock(&p->rlock); + + /* + * Maybe pull out of Host OS + */ + lock(&p->sysio); + if(p->syscall && p->intwait == 0) { + p->swipend = 1; + p->intwait = 1; + unlock(&p->sysio); + oshostintr(p); + return; + } + unlock(&p->sysio); +} + +void +notkilled(void) +{ + lock(&up->rlock); + up->killed = 0; + unlock(&up->rlock); +} + +void +osenter(void) +{ + up->syscall = 1; +} + +void +osleave(void) +{ + int r; + + lock(&up->rlock); + r = up->swipend; + up->swipend = 0; + unlock(&up->rlock); + + lock(&up->sysio); + up->syscall = 0; + unlock(&up->sysio); + + /* Cleared by the signal/note/exception handler */ + while(up->intwait) + osyield(); + + if(r != 0) + error(Eintr); +} + +void +rptwakeup(void *o, void *ar) +{ + Rept *r; + + r = ar; + if(r == nil) + return; + lock(&r->l); + r->o = o; + unlock(&r->l); + Wakeup(&r->r); +} + +static int +rptactive(void *a) +{ + Rept *r = a; + int i; + lock(&r->l); + i = r->active(r->o); + unlock(&r->l); + return i; +} + +static void +rproc(void *a) +{ + long now, then; + ulong t; + int i; + void *o; + Rept *r; + + up->env->fgrp = newfgrp(nil); + r = a; + t = (ulong)r->t; + +Wait: + Sleep(&r->r, rptactive, r); + lock(&r->l); + o = r->o; + unlock(&r->l); + then = osmillisec(); + for(;;){ + osmillisleep(t); + now = osmillisec(); + if(waserror()) + break; + i = r->ck(o, now-then); + poperror(); + if(i == -1) + goto Wait; + if(i == 0) + continue; + then = now; + acquire(); + if(waserror()) { + release(); + break; + } + r->f(o); + poperror(); + release(); + } + pexit("", 0); +} + +void* +rptproc(char *s, int t, void *o, int (*active)(void*), int (*ck)(void*, int), void (*f)(void*)) +{ + Rept *r; + + r = mallocz(sizeof(Rept), 1); + if(r == nil) + return nil; + r->t = t; + r->active = active; + r->ck = ck; + r->f = f; + r->o = o; + kproc(s, rproc, r, KPDUPPG); + return r; +} diff --git a/emu/port/qio.c b/emu/port/qio.c new file mode 100644 index 00000000..b900dff3 --- /dev/null +++ b/emu/port/qio.c @@ -0,0 +1,1554 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + +#define QDEBUG if(0) + +/* + * IO queues + */ + +struct Queue +{ + Lock l; + + Block* bfirst; /* buffer */ + Block* blast; + + int len; /* bytes allocated to queue */ + int dlen; /* data bytes in queue */ + int limit; /* max bytes in queue */ + int inilim; /* initial limit */ + int state; + int noblock; /* true if writes return immediately when q full */ + int eof; /* number of eofs read by user */ + + void (*kick)(void*); /* restart output */ + void (*bypass)(void*, Block*); /* bypass queue altogether */ + void* arg; /* argument to kick */ + + QLock rlock; /* mutex for reading processes */ + Rendez rr; /* process waiting to read */ + QLock wlock; /* mutex for writing processes */ + Rendez wr; /* process waiting to write */ + + char err[ERRMAX]; + int want; +}; + +enum +{ + Maxatomic = 32*1024 +}; + +uint qiomaxatomic = Maxatomic; + +void +checkb(Block *b, char *msg) +{ + if(b->base > b->lim) + panic("checkb 0 %s %lux %lux", msg, b->base, b->lim); + if(b->rp < b->base) + panic("checkb 1 %s %lux %lux", msg, b->base, b->rp); + if(b->wp < b->base) + panic("checkb 2 %s %lux %lux", msg, b->base, b->wp); + if(b->rp > b->lim) + panic("checkb 3 %s %lux %lux", msg, b->rp, b->lim); + if(b->wp > b->lim) + panic("checkb 4 %s %lux %lux", msg, b->wp, b->lim); +} + +void +freeb(Block *b) +{ + if(b == nil) + return; + + /* + * drivers which perform non cache coherent DMA manage their own buffer + * pool of uncached buffers and provide their own free routine. + */ + if(b->free) { + b->free(b); + return; + } + + /* poison the block in case someone is still holding onto it */ + b->next = (void*)0xdeadbabe; + b->rp = (void*)0xdeadbabe; + b->wp = (void*)0xdeadbabe; + b->lim = (void*)0xdeadbabe; + b->base = (void*)0xdeadbabe; + + free(b); +} + +/* + * free a list of blocks + */ +void +freeblist(Block *b) +{ + Block *next; + + for(; b != 0; b = next){ + next = b->next; + b->next = 0; + freeb(b); + } +} + +ulong padblockoverhead; + +/* + * pad a block to the front (or the back if size is negative) + */ +Block* +padblock(Block *bp, int size) +{ + int n; + Block *nbp; + + if(size >= 0){ + if(bp->rp - bp->base >= size){ + bp->rp -= size; + return bp; + } + + if(bp->next) + panic("padblock 0x%luX", getcallerpc(&bp)); + n = BLEN(bp); + padblockoverhead += n; + nbp = allocb(size+n); + nbp->rp += size; + nbp->wp = nbp->rp; + memmove(nbp->wp, bp->rp, n); + nbp->wp += n; + freeb(bp); + nbp->rp -= size; + } else { + size = -size; + + if(bp->next) + panic("padblock 0x%luX", getcallerpc(&bp)); + + if(bp->lim - bp->wp >= size) + return bp; + + n = BLEN(bp); + padblockoverhead += n; + nbp = allocb(size+n); + memmove(nbp->wp, bp->rp, n); + nbp->wp += n; + freeb(bp); + } + return nbp; +} + +/* + * return count of bytes in a string of blocks + */ +int +blocklen(Block *bp) +{ + int len; + + len = 0; + while(bp) { + len += BLEN(bp); + bp = bp->next; + } + return len; +} + +/* + * return count of space in blocks + */ +int +blockalloclen(Block *bp) +{ + int len; + + len = 0; + while(bp) { + len += BALLOC(bp); + bp = bp->next; + } + return len; +} + +/* + * copy the string of blocks into + * a single block and free the string + */ +Block* +concatblock(Block *bp) +{ + int len; + Block *nb, *f; + + if(bp->next == 0) + return bp; + + nb = allocb(blocklen(bp)); + for(f = bp; f; f = f->next) { + len = BLEN(f); + memmove(nb->wp, f->rp, len); + nb->wp += len; + } + freeblist(bp); + return nb; +} + +/* + * make sure the first block has at least n bytes. If we started with + * less than n bytes, make sure we have exactly n bytes. devssl.c depends + * on this. + */ +Block* +pullupblock(Block *bp, int n) +{ + int i; + Block *nbp; + + /* + * this should almost always be true, the rest it + * just to avoid every caller checking. + */ + if(BLEN(bp) >= n) + return bp; + + /* + * if not enough room in the first block, + * add another to the front of the list. + */ + if(bp->lim - bp->rp < n){ + nbp = allocb(n); + nbp->next = bp; + bp = nbp; + } + + /* + * copy bytes from the trailing blocks into the first + */ + n -= BLEN(bp); + while(nbp = bp->next){ + i = BLEN(nbp); + if(i > n) { + memmove(bp->wp, nbp->rp, n); + bp->wp += n; + nbp->rp += n; + return bp; + } + else { + memmove(bp->wp, nbp->rp, i); + bp->wp += i; + bp->next = nbp->next; + nbp->next = 0; + freeb(nbp); + n -= i; + if(n == 0) + return bp; + } + } + freeblist(bp); + return 0; +} + +/* + * make sure the first block has at least n bytes + */ +Block* +pullupqueue(Queue *q, int n) +{ + Block *b; + + if(BLEN(q->bfirst) >= n) + return q->bfirst; + q->bfirst = pullupblock(q->bfirst, n); + for(b = q->bfirst; b != nil && b->next != nil; b = b->next) + ; + q->blast = b; + return q->bfirst; +} + +/* + * trim to len bytes starting at offset + */ +Block * +trimblock(Block *bp, int offset, int len) +{ + ulong l; + Block *nb, *startb; + + if(blocklen(bp) < offset+len) { + freeblist(bp); + return nil; + } + + while((l = BLEN(bp)) < offset) { + offset -= l; + nb = bp->next; + bp->next = nil; + freeb(bp); + bp = nb; + } + + startb = bp; + bp->rp += offset; + + while((l = BLEN(bp)) < len) { + len -= l; + bp = bp->next; + } + + bp->wp -= (BLEN(bp) - len); + + if(bp->next) { + freeblist(bp->next); + bp->next = nil; + } + + return startb; +} + +/* + * copy 'count' bytes into a new block + */ +Block* +copyblock(Block *bp, int count) +{ + int l; + Block *nbp; + + nbp = allocb(count); + for(; count > 0 && bp != 0; bp = bp->next){ + l = BLEN(bp); + if(l > count) + l = count; + memmove(nbp->wp, bp->rp, l); + nbp->wp += l; + count -= l; + } + if(count > 0){ + memset(nbp->wp, 0, count); + nbp->wp += count; + } + + return nbp; +} + +Block* +adjustblock(Block* bp, int len) +{ + int n; + Block *nbp; + + if(len < 0){ + freeb(bp); + return nil; + } + + if(bp->rp+len > bp->lim){ + nbp = copyblock(bp, len); + freeblist(bp); + QDEBUG checkb(nbp, "adjustblock 1"); + + return nbp; + } + + n = BLEN(bp); + if(len > n) + memset(bp->wp, 0, len-n); + bp->wp = bp->rp+len; + QDEBUG checkb(bp, "adjustblock 2"); + + return bp; +} + +/* + * throw away up to count bytes from a + * list of blocks. Return count of bytes + * thrown away. + */ +int +pullblock(Block **bph, int count) +{ + Block *bp; + int n, bytes; + + bytes = 0; + if(bph == nil) + return 0; + + while(*bph != nil && count != 0) { + bp = *bph; + n = BLEN(bp); + if(count < n) + n = count; + bytes += n; + count -= n; + bp->rp += n; + if(BLEN(bp) == 0) { + *bph = bp->next; + bp->next = nil; + freeb(bp); + } + } + return bytes; +} + +/* + * allocate queues and blocks (round data base address to 64 bit boundary) + */ +Block* +iallocb(int size) +{ + Block *b; + ulong addr; + + b = kmalloc(sizeof(Block)+size); + if(b == 0) + return 0; + memset(b, 0, sizeof(Block)); + + addr = (ulong)b + sizeof(Block); + b->base = (uchar*)addr; + b->lim = b->base + size; + b->rp = b->base; + b->wp = b->rp; + + return b; +} + + +/* + * call error if iallocb fails + */ +Block* +allocb(int size) +{ + Block *b; + + b = iallocb(size); + if(b == 0) + exhausted("allocb"); + return b; +} + + +/* + * get next block from a queue, return null if nothing there + */ +Block* +qget(Queue *q) +{ + int dowakeup; + Block *b; + + /* sync with qwrite */ + lock(&q->l); + + b = q->bfirst; + if(b == 0){ + q->state |= Qstarve; + unlock(&q->l); + return 0; + } + q->bfirst = b->next; + b->next = 0; + q->len -= BALLOC(b); + q->dlen -= BLEN(b); + QDEBUG checkb(b, "qget"); + + /* if writer flow controlled, restart */ + if((q->state & Qflow) && q->len < q->limit/2){ + q->state &= ~Qflow; + dowakeup = 1; + } else + dowakeup = 0; + + unlock(&q->l); + + if(dowakeup) + Wakeup(&q->wr); + + return b; +} + +/* + * throw away the next 'len' bytes in the queue + */ +int +qdiscard(Queue *q, int len) +{ + Block *b; + int dowakeup, n, sofar; + + lock(&q->l); + for(sofar = 0; sofar < len; sofar += n){ + b = q->bfirst; + if(b == nil) + break; + QDEBUG checkb(b, "qdiscard"); + n = BLEN(b); + if(n <= len - sofar){ + q->bfirst = b->next; + b->next = 0; + q->len -= BALLOC(b); + q->dlen -= BLEN(b); + freeb(b); + } else { + n = len - sofar; + b->rp += n; + q->dlen -= n; + } + } + + /* if writer flow controlled, restart */ + if((q->state & Qflow) && q->len < q->limit){ + q->state &= ~Qflow; + dowakeup = 1; + } else + dowakeup = 0; + + unlock(&q->l); + + if(dowakeup) + Wakeup(&q->wr); + + return sofar; +} + +/* + * Interrupt level copy out of a queue, return # bytes copied. + */ +int +qconsume(Queue *q, void *vp, int len) +{ + Block *b; + int n, dowakeup; + uchar *p = vp; + Block *tofree = nil; + + /* sync with qwrite */ + lock(&q->l); + + for(;;) { + b = q->bfirst; + if(b == 0){ + q->state |= Qstarve; + unlock(&q->l); + return -1; + } + QDEBUG checkb(b, "qconsume 1"); + + n = BLEN(b); + if(n > 0) + break; + q->bfirst = b->next; + q->len -= BALLOC(b); + + /* remember to free this */ + b->next = tofree; + tofree = b; + } + + if(n < len) + len = n; + memmove(p, b->rp, len); + if((q->state & Qmsg) || len == n) + q->bfirst = b->next; + b->rp += len; + q->dlen -= len; + + /* discard the block if we're done with it */ + if((q->state & Qmsg) || len == n){ + b->next = 0; + q->len -= BALLOC(b); + q->dlen -= BLEN(b); + + /* remember to free this */ + b->next = tofree; + tofree = b; + } + + /* if writer flow controlled, restart */ + if((q->state & Qflow) && q->len < q->limit/2){ + q->state &= ~Qflow; + dowakeup = 1; + } else + dowakeup = 0; + + unlock(&q->l); + + if(dowakeup) + Wakeup(&q->wr); + + if(tofree != nil) + freeblist(tofree); + + return len; +} + +int +qpass(Queue *q, Block *b) +{ + int dlen, len, dowakeup; + + /* sync with qread */ + dowakeup = 0; + lock(&q->l); + if(q->len >= q->limit){ + unlock(&q->l); + freeblist(b); + return -1; + } + if(q->state & Qclosed){ + unlock(&q->l); + len = blocklen(b); + freeblist(b); + return len; + } + + /* add buffer to queue */ + if(q->bfirst) + q->blast->next = b; + else + q->bfirst = b; + len = BALLOC(b); + dlen = BLEN(b); + QDEBUG checkb(b, "qpass"); + while(b->next){ + b = b->next; + QDEBUG checkb(b, "qpass"); + len += BALLOC(b); + dlen += BLEN(b); + } + q->blast = b; + q->len += len; + q->dlen += dlen; + + if(q->len >= q->limit/2) + q->state |= Qflow; + + if(q->state & Qstarve){ + q->state &= ~Qstarve; + dowakeup = 1; + } + unlock(&q->l); + + if(dowakeup) + Wakeup(&q->rr); + + return len; +} + +int +qpassnolim(Queue *q, Block *b) +{ + int dlen, len, dowakeup; + + /* sync with qread */ + dowakeup = 0; + lock(&q->l); + + len = BALLOC(b); + if(q->state & Qclosed){ + unlock(&q->l); + freeblist(b); + return len; + } + + /* add buffer to queue */ + if(q->bfirst) + q->blast->next = b; + else + q->bfirst = b; + dlen = BLEN(b); + QDEBUG checkb(b, "qpass"); + while(b->next){ + b = b->next; + QDEBUG checkb(b, "qpass"); + len += BALLOC(b); + dlen += BLEN(b); + } + q->blast = b; + q->len += len; + q->dlen += dlen; + + if(q->len >= q->limit/2) + q->state |= Qflow; + + if(q->state & Qstarve){ + q->state &= ~Qstarve; + dowakeup = 1; + } + unlock(&q->l); + + if(dowakeup) + Wakeup(&q->rr); + + return len; +} + +/* + * if the allocated space is way out of line with the used + * space, reallocate to a smaller block + */ +Block* +packblock(Block *bp) +{ + Block **l, *nbp; + int n; + + for(l = &bp; *l; l = &(*l)->next){ + nbp = *l; + n = BLEN(nbp); + if((n<<2) < BALLOC(nbp)){ + *l = allocb(n); + memmove((*l)->wp, nbp->rp, n); + (*l)->wp += n; + (*l)->next = nbp->next; + freeb(nbp); + } + } + + return bp; +} + +int +qproduce(Queue *q, void *vp, int len) +{ + Block *b; + int dowakeup; + uchar *p = vp; + + /* sync with qread */ + dowakeup = 0; + lock(&q->l); + + if(q->state & Qclosed){ + unlock(&q->l); + return -1; + } + + /* no waiting receivers, room in buffer? */ + if(q->len >= q->limit){ + q->state |= Qflow; + unlock(&q->l); + return -1; + } + + /* save in buffer */ + b = iallocb(len); + if(b == 0){ + unlock(&q->l); + print("qproduce: iallocb failed\n"); + return -1; + } + memmove(b->wp, p, len); + b->wp += len; + if(q->bfirst) + q->blast->next = b; + else + q->bfirst = b; + q->blast = b; + /* b->next = 0; done by allocb() */ + q->len += BALLOC(b); + q->dlen += BLEN(b); + QDEBUG checkb(b, "qproduce"); + + if(q->state & Qstarve){ + q->state &= ~Qstarve; + dowakeup = 1; + } + + if(q->len >= q->limit) + q->state |= Qflow; + unlock(&q->l); + + + if(dowakeup) + Wakeup(&q->rr); + + return len; +} + +/* + * copy from offset in the queue + */ +Block* +qcopy(Queue *q, int len, ulong offset) +{ + int sofar; + int n; + Block *b, *nb; + uchar *p; + + nb = allocb(len); + + lock(&q->l); + + /* go to offset */ + b = q->bfirst; + for(sofar = 0; ; sofar += n){ + if(b == nil){ + unlock(&q->l); + return nb; + } + n = BLEN(b); + if(sofar + n > offset){ + p = b->rp + offset - sofar; + n -= offset - sofar; + break; + } + b = b->next; + } + + /* copy bytes from there */ + for(sofar = 0; sofar < len;){ + if(n > len - sofar) + n = len - sofar; + memmove(nb->wp, p, n); + sofar += n; + nb->wp += n; + b = b->next; + if(b == nil) + break; + n = BLEN(b); + p = b->rp; + } + unlock(&q->l); + + return nb; +} + +/* + * called by non-interrupt code + */ +Queue* +qopen(int limit, int msg, void (*kick)(void*), void *arg) +{ + Queue *q; + + q = kmalloc(sizeof(Queue)); + if(q == 0) + return 0; + + q->limit = q->inilim = limit; + q->kick = kick; + q->arg = arg; + q->state = msg; + q->state |= Qstarve; + q->eof = 0; + q->noblock = 0; + + return q; +} + +/* open a queue to be bypassed */ +Queue* +qbypass(void (*bypass)(void*, Block*), void *arg) +{ + Queue *q; + + q = malloc(sizeof(Queue)); + if(q == 0) + return 0; + + q->limit = 0; + q->arg = arg; + q->bypass = bypass; + q->state = 0; + + return q; +} + +static int +notempty(void *a) +{ + Queue *q = a; + + return (q->state & Qclosed) || q->bfirst != 0; +} + +/* + * wait for the queue to be non-empty or closed. + * called with q ilocked. + */ +static int +qwait(Queue *q) +{ + /* wait for data */ + for(;;){ + if(q->bfirst != nil) + break; + + if(q->state & Qclosed){ + if(++q->eof > 3) + return -1; + if(*q->err && strcmp(q->err, Ehungup) != 0) + return -1; + return 0; + } + + q->state |= Qstarve; /* flag requesting producer to wake me */ + unlock(&q->l); + Sleep(&q->rr, notempty, q); + lock(&q->l); + } + return 1; +} + +/* + * add a block list to a queue + */ +void +qaddlist(Queue *q, Block *b) +{ + /* queue the block */ + if(q->bfirst) + q->blast->next = b; + else + q->bfirst = b; + q->len += blockalloclen(b); + q->dlen += blocklen(b); + while(b->next) + b = b->next; + q->blast = b; +} + +/* + * called with q locked + */ +Block* +qremove(Queue *q) +{ + Block *b; + + b = q->bfirst; + if(b == nil) + return nil; + q->bfirst = b->next; + b->next = nil; + q->dlen -= BLEN(b); + q->len -= BALLOC(b); + QDEBUG checkb(b, "qremove"); + return b; +} + +/* + * copy the contents of a string of blocks into + * memory. emptied blocks are freed. return + * pointer to first unconsumed block. + */ +Block* +bl2mem(uchar *p, Block *b, int n) +{ + int i; + Block *next; + + for(; b != nil; b = next){ + i = BLEN(b); + if(i > n){ + memmove(p, b->rp, n); + b->rp += n; + return b; + } + memmove(p, b->rp, i); + n -= i; + p += i; + b->rp += i; + next = b->next; + freeb(b); + } + return nil; +} + +/* + * copy the contents of memory into a string of blocks. + * return nil on error. + */ +Block* +mem2bl(uchar *p, int len) +{ + int n; + Block *b, *first, **l; + + first = nil; + l = &first; + if(waserror()){ + freeblist(first); + nexterror(); + } + do { + n = len; + if(n > Maxatomic) + n = Maxatomic; + + *l = b = allocb(n); + setmalloctag(b, (up->text[0]<<24)|(up->text[1]<<16)|(up->text[2]<<8)|up->text[3]); + memmove(b->wp, p, n); + b->wp += n; + p += n; + len -= n; + l = &b->next; + } while(len > 0); + poperror(); + + return first; +} + +/* + * put a block back to the front of the queue + * called with q ilocked + */ +void +qputback(Queue *q, Block *b) +{ + b->next = q->bfirst; + if(q->bfirst == nil) + q->blast = b; + q->bfirst = b; + q->len += BALLOC(b); + q->dlen += BLEN(b); +} + +/* + * flow control, get producer going again + * called with q locked + */ +static void +qwakeup_unlock(Queue *q) +{ + int dowakeup = 0; + + /* if writer flow controlled, restart */ + if((q->state & Qflow) && q->len < q->limit/2){ + q->state &= ~Qflow; + dowakeup = 1; + } + + unlock(&q->l); + + /* wakeup flow controlled writers */ + if(dowakeup){ + if(q->kick) + q->kick(q->arg); + Wakeup(&q->wr); + } +} + +/* + * get next block from a queue (up to a limit) + */ +Block* +qbread(Queue *q, int len) +{ + Block *b, *nb; + int n; + + qlock(&q->rlock); + if(waserror()){ + qunlock(&q->rlock); + nexterror(); + } + + lock(&q->l); + switch(qwait(q)){ + case 0: + /* queue closed */ + unlock(&q->l); + poperror(); + qunlock(&q->rlock); + return nil; + case -1: + /* multiple reads on a closed queue */ + unlock(&q->l); + error(q->err); + } + + /* if we get here, there's at least one block in the queue */ + b = qremove(q); + n = BLEN(b); + + /* split block if it's too big and this is not a message oriented queue */ + nb = b; + if(n > len){ + if((q->state&Qmsg) == 0){ + n -= len; + b = allocb(n); + memmove(b->wp, nb->rp+len, n); + b->wp += n; + qputback(q, b); + } + nb->wp = nb->rp + len; + } + + /* restart producer */ + qwakeup_unlock(q); + + poperror(); + qunlock(&q->rlock); + return nb; +} + +/* + * read a queue. if no data is queued, wait on its Rendez + */ +long +qread(Queue *q, void *vp, int len) +{ + Block *b, *first, **l; + int m, n; + + qlock(&q->rlock); + if(waserror()){ + qunlock(&q->rlock); + nexterror(); + } + + lock(&q->l); +again: + switch(qwait(q)){ + case 0: + /* queue closed */ + unlock(&q->l); + poperror(); + qunlock(&q->rlock); + return 0; + case -1: + /* multiple reads on a closed queue */ + unlock(&q->l); + error(q->err); + } + + /* if we get here, there's at least one block in the queue */ + if(q->state & Qcoalesce){ + /* when coalescing, 0 length blocks just go away */ + b = q->bfirst; + if(BLEN(b) <= 0){ + freeb(qremove(q)); + goto again; + } + + /* grab the first block plus as many + * following blocks as will completely + * fit in the read. + */ + n = 0; + l = &first; + m = BLEN(b); + for(;;) { + *l = qremove(q); + l = &b->next; + n += m; + + b = q->bfirst; + if(b == nil) + break; + m = BLEN(b); + if(n+m > len) + break; + } + } else { + first = qremove(q); + n = BLEN(first); + } + + /* copy to user space outside of the ilock */ + unlock(&q->l); + b = bl2mem(vp, first, len); + lock(&q->l); + + /* take care of any left over partial block */ + if(b != nil){ + n -= BLEN(b); + if(q->state & Qmsg) + freeb(b); + else + qputback(q, b); + } + + /* restart producer */ + qwakeup_unlock(q); + + poperror(); + qunlock(&q->rlock); + return n; +} + +static int +qnotfull(void *a) +{ + Queue *q = a; + + return q->len < q->limit || (q->state & Qclosed); +} + +/* + * add a block to a queue obeying flow control + */ +long +qbwrite(Queue *q, Block *b) +{ + int n, dowakeup; + volatile struct {Block *b;} cb; + + dowakeup = 0; + n = BLEN(b); + if(q->bypass){ + (*q->bypass)(q->arg, b); + return n; + } + cb.b = b; + + qlock(&q->wlock); + if(waserror()){ + if(cb.b != nil) + freeb(cb.b); + qunlock(&q->wlock); + nexterror(); + } + + lock(&q->l); + + /* give up if the queue is closed */ + if(q->state & Qclosed){ + unlock(&q->l); + error(q->err); + } + + /* if nonblocking, don't queue over the limit */ + if(q->len >= q->limit){ + if(q->noblock){ + unlock(&q->l); + freeb(b); + poperror(); + qunlock(&q->wlock); + return n; + } + } + + /* queue the block */ + if(q->bfirst) + q->blast->next = b; + else + q->bfirst = b; + q->blast = b; + b->next = 0; + q->len += BALLOC(b); + q->dlen += n; + QDEBUG checkb(b, "qbwrite"); + cb.b = nil; + + if(q->state & Qstarve){ + q->state &= ~Qstarve; + dowakeup = 1; + } + + unlock(&q->l); + + /* get output going again */ + if(q->kick && (dowakeup || (q->state&Qkick))) + q->kick(q->arg); + + if(dowakeup) + Wakeup(&q->rr); + + /* + * flow control, wait for queue to get below the limit + * before allowing the process to continue and queue + * more. We do this here so that postnote can only + * interrupt us after the data has been queued. This + * means that things like 9p flushes and ssl messages + * will not be disrupted by software interrupts. + * + * Note - this is moderately dangerous since a process + * that keeps getting interrupted and rewriting will + * queue infinite crud. + */ + for(;;){ + if(q->noblock || qnotfull(q)) + break; + + lock(&q->l); + q->state |= Qflow; + unlock(&q->l); + Sleep(&q->wr, qnotfull, q); + } + + qunlock(&q->wlock); + poperror(); + return n; +} + +/* + * write to a queue. only Maxatomic bytes at a time is atomic. + */ +int +qwrite(Queue *q, void *vp, int len) +{ + int n, sofar; + Block *b; + uchar *p = vp; + + sofar = 0; + do { + n = len-sofar; + if(n > Maxatomic) + n = Maxatomic; + + b = allocb(n); + setmalloctag(b, getcallerpc(&q)); + if(waserror()){ + freeb(b); + nexterror(); + } + memmove(b->wp, p+sofar, n); + poperror(); + b->wp += n; + + qbwrite(q, b); + + sofar += n; + } while(sofar < len && (q->state & Qmsg) == 0); + + return len; +} + +/* + * used by print() to write to a queue. Since we may be splhi or not in + * a process, don't qlock. + */ +int +qiwrite(Queue *q, void *vp, int len) +{ + int n, sofar, dowakeup; + Block *b; + uchar *p = vp; + + dowakeup = 0; + + sofar = 0; + do { + n = len-sofar; + if(n > Maxatomic) + n = Maxatomic; + + b = iallocb(n); + if (b == 0) { + print("qiwrite: iallocb failed\n"); + break; + } + memmove(b->wp, p+sofar, n); + b->wp += n; + + lock(&q->l); + + QDEBUG checkb(b, "qiwrite"); + if(q->bfirst) + q->blast->next = b; + else + q->bfirst = b; + q->blast = b; + q->len += BALLOC(b); + q->dlen += n; + + if(q->state & Qstarve){ + q->state &= ~Qstarve; + dowakeup = 1; + } + + unlock(&q->l); + + if(dowakeup){ + if(q->kick) + q->kick(q->arg); + Wakeup(&q->rr); + } + + sofar += n; + } while(sofar < len && (q->state & Qmsg) == 0); + + return sofar; +} + +/* + * be extremely careful when calling this, + * as there is no reference accounting + */ +void +qfree(Queue *q) +{ + qclose(q); + free(q); +} + +/* + * Mark a queue as closed. No further IO is permitted. + * All blocks are released. + */ +void +qclose(Queue *q) +{ + Block *bfirst; + + if(q == nil) + return; + + /* mark it */ + lock(&q->l); + q->state |= Qclosed; + q->state &= ~(Qflow|Qstarve); + strcpy(q->err, Ehungup); + bfirst = q->bfirst; + q->bfirst = 0; + q->len = 0; + q->dlen = 0; + q->noblock = 0; + unlock(&q->l); + + /* free queued blocks */ + freeblist(bfirst); + + /* wake up readers/writers */ + Wakeup(&q->rr); + Wakeup(&q->wr); +} + +/* + * Mark a queue as closed. Wakeup any readers. Don't remove queued + * blocks. + */ +void +qhangup(Queue *q, char *msg) +{ + /* mark it */ + lock(&q->l); + q->state |= Qclosed; + if(msg == 0 || *msg == 0) + strcpy(q->err, Ehungup); + else + kstrcpy(q->err, msg, sizeof q->err); + unlock(&q->l); + + /* wake up readers/writers */ + Wakeup(&q->rr); + Wakeup(&q->wr); +} + +/* + * return non-zero if the q is hungup + */ +int +qisclosed(Queue *q) +{ + return q->state & Qclosed; +} + +/* + * mark a queue as no longer hung up + */ +void +qreopen(Queue *q) +{ + lock(&q->l); + q->state &= ~Qclosed; + q->state |= Qstarve; + q->eof = 0; + q->limit = q->inilim; + unlock(&q->l); +} + +/* + * return bytes queued + */ +int +qlen(Queue *q) +{ + return q->dlen; +} + +/* + * return space remaining before flow control + */ +int +qwindow(Queue *q) +{ + int l; + + l = q->limit - q->len; + if(l < 0) + l = 0; + return l; +} + +/* + * return true if we can read without blocking + */ +int +qcanread(Queue *q) +{ + return q->bfirst!=0; +} + +/* + * change queue limit + */ +void +qsetlimit(Queue *q, int limit) +{ + q->limit = limit; +} + +/* + * set blocking/nonblocking + */ +void +qnoblock(Queue *q, int onoff) +{ + q->noblock = onoff; +} + +/* + * flush the output queue + */ +void +qflush(Queue *q) +{ + Block *bfirst; + + /* mark it */ + lock(&q->l); + bfirst = q->bfirst; + q->bfirst = 0; + q->len = 0; + q->dlen = 0; + unlock(&q->l); + + /* free queued blocks */ + freeblist(bfirst); + + /* wake up readers/writers */ + Wakeup(&q->wr); +} + +int +qfull(Queue *q) +{ + return q->state & Qflow; +} + +int +qstate(Queue *q) +{ + return q->state; +} + +int +qclosed(Queue *q) +{ + return q->state & Qclosed; +} diff --git a/emu/port/random.c b/emu/port/random.c new file mode 100644 index 00000000..90d20c6a --- /dev/null +++ b/emu/port/random.c @@ -0,0 +1,167 @@ +#include "dat.h" +#include "fns.h" + +static struct +{ + QLock l; + Rendez producer; + Rendez consumer; + Rendez clock; + ulong randomcount; + uchar buf[1024]; + uchar *ep; + uchar *rp; + uchar *wp; + uchar next; + uchar bits; + uchar wakeme; + uchar filled; + int kprocstarted; + ulong randn; + int target; +} rb; + +static int +rbnotfull(void *v) +{ + int i; + + USED(v); + i = rb.wp - rb.rp; + if(i < 0) + i += sizeof(rb.buf); + return i < rb.target; +} + +static int +rbnotempty(void *v) +{ + USED(v); + return rb.wp != rb.rp; +} + +/* + * spin counting up + */ +static void +genrandom(void *v) +{ + USED(v); + oslopri(); + for(;;){ + for(;;) + if(++rb.randomcount > 65535) + break; + if(rb.filled || !rbnotfull(0)) + Sleep(&rb.producer, rbnotfull, 0); + } +} + +/* + * produce random bits in a circular buffer + */ +static void +randomclock(void *v) +{ + uchar *p; + + USED(v); + for(;; osmillisleep(20)){ + while(!rbnotfull(0)){ + rb.filled = 1; + Sleep(&rb.clock, rbnotfull, 0); + } + if(rb.randomcount == 0) + continue; + + rb.bits = (rb.bits<<2) ^ rb.randomcount; + rb.randomcount = 0; + + rb.next++; + if(rb.next != 8/2) + continue; + rb.next = 0; + + p = rb.wp; + *p ^= rb.bits; + if(++p == rb.ep) + p = rb.buf; + rb.wp = p; + + if(rb.wakeme) + Wakeup(&rb.consumer); + } +} + +void +randominit(void) +{ + rb.target = 16; + rb.ep = rb.buf + sizeof(rb.buf); + rb.rp = rb.wp = rb.buf; +} + +/* + * consume random bytes from a circular buffer + */ +ulong +randomread(void *xp, ulong n) +{ + uchar *e, *p, *r; + ulong x; + int i; + + p = xp; + +if(0)print("A%ld.%d.%lux|", n, rb.target, getcallerpc(&xp)); + if(waserror()){ + qunlock(&rb.l); + nexterror(); + } + + qlock(&rb.l); + if(!rb.kprocstarted){ + rb.kprocstarted = 1; + kproc("genrand", genrandom, 0, 0); + kproc("randomclock", randomclock, 0, 0); + } + + for(e = p + n; p < e; ){ + r = rb.rp; + if(r == rb.wp){ + rb.wakeme = 1; + Wakeup(&rb.clock); + Wakeup(&rb.producer); + Sleep(&rb.consumer, rbnotempty, 0); + rb.wakeme = 0; + continue; + } + + /* + * beating clocks will be predictable if + * they are synchronized. Use a cheap pseudo + * random number generator to obscure any cycles. + */ + x = rb.randn*1103515245 ^ *r; + *p++ = rb.randn = x; + + if(++r == rb.ep) + r = rb.buf; + rb.rp = r; + } + if(rb.filled && rb.wp == rb.rp){ + i = 2*rb.target; + if(i > sizeof(rb.buf) - 1) + i = sizeof(rb.buf) - 1; + rb.target = i; + rb.filled = 0; + } + qunlock(&rb.l); + poperror(); + + Wakeup(&rb.clock); + Wakeup(&rb.producer); + +if(0)print("B"); + return n; +} diff --git a/emu/port/srv.c b/emu/port/srv.c new file mode 100644 index 00000000..86a004c8 --- /dev/null +++ b/emu/port/srv.c @@ -0,0 +1,153 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include <interp.h> +#include <isa.h> +#include "ip.h" +#include "srv.h" +#include "srvm.h" + +static QLock dbq; + +void +Srv_init(void *fp) +{ + USED(fp); +} + +void +Srv_iph2a(void *fp) +{ + Heap *hpt; + String *ss; + F_Srv_iph2a *f; + int i, n, nhost; + List **h, *l, *nl; + char *hostv[10]; + void *r; + + f = fp; + r = *f->ret; + *f->ret = H; + destroy(r); + release(); + qlock(&dbq); + if(waserror()){ + qunlock(&dbq); + acquire(); + return; + } + nhost = so_gethostbyname(string2c(f->host), hostv, nelem(hostv)); + poperror(); + qunlock(&dbq); + acquire(); + if(nhost == 0) + return; + + l = (List*)H; + h = &l; + for(i = 0; i < nhost; i++) { + n = strlen(hostv[i]); + ss = newstring(n); + memmove(ss->Sascii, hostv[i], n); + free(hostv[i]); + + hpt = nheap(sizeof(List) + IBY2WD); + hpt->t = &Tlist; + hpt->t->ref++; + nl = H2D(List*, hpt); + nl->t = &Tptr; + Tptr.ref++; + nl->tail = (List*)H; + *(String**)nl->data = ss; + + *h = nl; + h = &nl->tail; + } + *f->ret = l; +} + +void +Srv_ipa2h(void *fp) +{ + Heap *hpt; + String *ss; + F_Srv_ipa2h *f; + int i, n, naliases; + List **h, *l, *nl; + char *hostv[10]; + void *r; + + f = fp; + r = *f->ret; + *f->ret = H; + destroy(r); + release(); + qlock(&dbq); + if(waserror()){ + qunlock(&dbq); + acquire(); + return; + } + naliases = so_gethostbyaddr(string2c(f->addr), hostv, nelem(hostv)); + poperror(); + qunlock(&dbq); + acquire(); + if(naliases == 0) + return; + + l = (List*)H; + h = &l; + for(i = 0; i < naliases; i++) { + n = strlen(hostv[i]); + ss = newstring(n); + memmove(ss->Sascii, hostv[i], n); + free(hostv[i]); + + hpt = nheap(sizeof(List) + IBY2WD); + hpt->t = &Tlist; + hpt->t->ref++; + nl = H2D(List*, hpt); + nl->t = &Tptr; + Tptr.ref++; + nl->tail = (List*)H; + *(String**)nl->data = ss; + + *h = nl; + h = &nl->tail; + } + *f->ret = l; +} + +void +Srv_ipn2p(void *fp) +{ + int n; + char buf[16]; + F_Srv_ipn2p *f; + void *r; + + f = fp; + r = *f->ret; + *f->ret = H; + destroy(r); + release(); + qlock(&dbq); + if(waserror()){ + qunlock(&dbq); + acquire(); + return; + } + n = so_getservbyname(string2c(f->service), string2c(f->net), buf); + poperror(); + qunlock(&dbq); + acquire(); + if(n >= 0) + retstr(buf, f->ret); +} + +void +srvmodinit(void) +{ + builtinmod("$Srv", Srvmodtab, Srvmodlen); +} diff --git a/emu/port/styx.c b/emu/port/styx.c new file mode 100644 index 00000000..2eb9d757 --- /dev/null +++ b/emu/port/styx.c @@ -0,0 +1,701 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + +static +uchar* +gstring(uchar *p, uchar *ep, char **s) +{ + uint n; + + if(p+BIT16SZ > ep) + return nil; + n = GBIT16(p); + p += BIT16SZ - 1; + if(p+n+1 > ep) + return nil; + /* move it down, on top of count, to make room for '\0' */ + memmove(p, p + 1, n); + p[n] = '\0'; + *s = (char*)p; + p += n+1; + return p; +} + +static +uchar* +gqid(uchar *p, uchar *ep, Qid *q) +{ + if(p+QIDSZ > ep) + return nil; + q->type = GBIT8(p); + p += BIT8SZ; + q->vers = GBIT32(p); + p += BIT32SZ; + q->path = GBIT64(p); + p += BIT64SZ; + return p; +} + +/* + * no syntactic checks. + * three causes for error: + * 1. message size field is incorrect + * 2. input buffer too short for its own data (counts too long, etc.) + * 3. too many names or qids + * gqid() and gstring() return nil if they would reach beyond buffer. + * main switch statement checks range and also can fall through + * to test at end of routine. + */ +uint +convM2S(uchar *ap, uint nap, Fcall *f) +{ + uchar *p, *ep; + uint i, size; + + p = ap; + ep = p + nap; + + if(p+BIT32SZ+BIT8SZ+BIT16SZ > ep) + return 0; + size = GBIT32(p); + p += BIT32SZ; + + if(size < BIT32SZ+BIT8SZ+BIT16SZ) + return 0; + + f->type = GBIT8(p); + p += BIT8SZ; + f->tag = GBIT16(p); + p += BIT16SZ; + + switch(f->type) + { + default: + return 0; + + case Tversion: + if(p+BIT32SZ > ep) + return 0; + f->msize = GBIT32(p); + p += BIT32SZ; + p = gstring(p, ep, &f->version); + break; + + case Tflush: + if(p+BIT16SZ > ep) + return 0; + f->oldtag = GBIT16(p); + p += BIT16SZ; + break; + + case Tauth: + if(p+BIT32SZ > ep) + return 0; + f->afid = GBIT32(p); + p += BIT32SZ; + p = gstring(p, ep, &f->uname); + if(p == nil) + break; + p = gstring(p, ep, &f->aname); + if(p == nil) + break; + break; + + case Tattach: + if(p+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + if(p+BIT32SZ > ep) + return 0; + f->afid = GBIT32(p); + p += BIT32SZ; + p = gstring(p, ep, &f->uname); + if(p == nil) + break; + p = gstring(p, ep, &f->aname); + if(p == nil) + break; + break; + + case Twalk: + if(p+BIT32SZ+BIT32SZ+BIT16SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + f->newfid = GBIT32(p); + p += BIT32SZ; + f->nwname = GBIT16(p); + p += BIT16SZ; + if(f->nwname > MAXWELEM) + return 0; + for(i=0; i<f->nwname; i++){ + p = gstring(p, ep, &f->wname[i]); + if(p == nil) + break; + } + break; + + case Topen: + if(p+BIT32SZ+BIT8SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + f->mode = GBIT8(p); + p += BIT8SZ; + break; + + case Tcreate: + if(p+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + p = gstring(p, ep, &f->name); + if(p == nil) + break; + if(p+BIT32SZ+BIT8SZ > ep) + return 0; + f->perm = GBIT32(p); + p += BIT32SZ; + f->mode = GBIT8(p); + p += BIT8SZ; + break; + + case Tread: + if(p+BIT32SZ+BIT64SZ+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + f->offset = GBIT64(p); + p += BIT64SZ; + f->count = GBIT32(p); + p += BIT32SZ; + break; + + case Twrite: + if(p+BIT32SZ+BIT64SZ+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + f->offset = GBIT64(p); + p += BIT64SZ; + f->count = GBIT32(p); + p += BIT32SZ; + if(p+f->count > ep) + return 0; + f->data = (char*)p; + p += f->count; + break; + + case Tclunk: + case Tremove: + if(p+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + break; + + case Tstat: + if(p+BIT32SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + break; + + case Twstat: + if(p+BIT32SZ+BIT16SZ > ep) + return 0; + f->fid = GBIT32(p); + p += BIT32SZ; + f->nstat = GBIT16(p); + p += BIT16SZ; + if(p+f->nstat > ep) + return 0; + f->stat = p; + p += f->nstat; + break; + +/* + */ + case Rversion: + if(p+BIT32SZ > ep) + return 0; + f->msize = GBIT32(p); + p += BIT32SZ; + p = gstring(p, ep, &f->version); + break; + + case Rerror: + p = gstring(p, ep, &f->ename); + break; + + case Rflush: + break; + + case Rauth: + p = gqid(p, ep, &f->aqid); + if(p == nil) + break; + break; + + case Rattach: + p = gqid(p, ep, &f->qid); + if(p == nil) + break; + break; + + case Rwalk: + if(p+BIT16SZ > ep) + return 0; + f->nwqid = GBIT16(p); + p += BIT16SZ; + if(f->nwqid > MAXWELEM) + return 0; + for(i=0; i<f->nwqid; i++){ + p = gqid(p, ep, &f->wqid[i]); + if(p == nil) + break; + } + break; + + case Ropen: + case Rcreate: + p = gqid(p, ep, &f->qid); + if(p == nil) + break; + if(p+BIT32SZ > ep) + return 0; + f->iounit = GBIT32(p); + p += BIT32SZ; + break; + + case Rread: + if(p+BIT32SZ > ep) + return 0; + f->count = GBIT32(p); + p += BIT32SZ; + if(p+f->count > ep) + return 0; + f->data = (char*)p; + p += f->count; + break; + + case Rwrite: + if(p+BIT32SZ > ep) + return 0; + f->count = GBIT32(p); + p += BIT32SZ; + break; + + case Rclunk: + case Rremove: + break; + + case Rstat: + if(p+BIT16SZ > ep) + return 0; + f->nstat = GBIT16(p); + p += BIT16SZ; + if(p+f->nstat > ep) + return 0; + f->stat = p; + p += f->nstat; + break; + + case Rwstat: + break; + } + + if(p==nil || p>ep) + return 0; + if(ap+size == p) + return size; + return 0; +} + + + + +static +uchar* +pstring(uchar *p, char *s) +{ + uint n; + + if(s == nil){ + PBIT16(p, 0); + p += BIT16SZ; + return p; + } + + n = strlen(s); + PBIT16(p, n); + p += BIT16SZ; + memmove(p, s, n); + p += n; + return p; +} + +static +uchar* +pqid(uchar *p, Qid *q) +{ + PBIT8(p, q->type); + p += BIT8SZ; + PBIT32(p, q->vers); + p += BIT32SZ; + PBIT64(p, q->path); + p += BIT64SZ; + return p; +} + +static +uint +stringsz(char *s) +{ + if(s == nil) + return BIT16SZ; + + return BIT16SZ+strlen(s); +} + +uint +sizeS2M(Fcall *f) +{ + uint n; + int i; + + n = 0; + n += BIT32SZ; /* size */ + n += BIT8SZ; /* type */ + n += BIT16SZ; /* tag */ + + switch(f->type) + { + default: + return 0; + + case Tversion: + n += BIT32SZ; + n += stringsz(f->version); + break; + + case Tflush: + n += BIT16SZ; + break; + + case Tauth: + n += BIT32SZ; + n += stringsz(f->uname); + n += stringsz(f->aname); + break; + + case Tattach: + n += BIT32SZ; + n += BIT32SZ; + n += stringsz(f->uname); + n += stringsz(f->aname); + break; + + case Twalk: + n += BIT32SZ; + n += BIT32SZ; + n += BIT16SZ; + for(i=0; i<f->nwname; i++) + n += stringsz(f->wname[i]); + break; + + case Topen: + n += BIT32SZ; + n += BIT8SZ; + break; + + case Tcreate: + n += BIT32SZ; + n += stringsz(f->name); + n += BIT32SZ; + n += BIT8SZ; + break; + + case Tread: + n += BIT32SZ; + n += BIT64SZ; + n += BIT32SZ; + break; + + case Twrite: + n += BIT32SZ; + n += BIT64SZ; + n += BIT32SZ; + n += f->count; + break; + + case Tclunk: + case Tremove: + n += BIT32SZ; + break; + + case Tstat: + n += BIT32SZ; + break; + + case Twstat: + n += BIT32SZ; + n += BIT16SZ; + n += f->nstat; + break; +/* + */ + + case Rversion: + n += BIT32SZ; + n += stringsz(f->version); + break; + + case Rerror: + n += stringsz(f->ename); + break; + + case Rflush: + break; + + case Rauth: + n += QIDSZ; + break; + + case Rattach: + n += QIDSZ; + break; + + case Rwalk: + n += BIT16SZ; + n += f->nwqid*QIDSZ; + break; + + case Ropen: + case Rcreate: + n += QIDSZ; + n += BIT32SZ; + break; + + case Rread: + n += BIT32SZ; + n += f->count; + break; + + case Rwrite: + n += BIT32SZ; + break; + + case Rclunk: + break; + + case Rremove: + break; + + case Rstat: + n += BIT16SZ; + n += f->nstat; + break; + + case Rwstat: + break; + } + return n; +} + +uint +convS2M(Fcall *f, uchar *ap, uint nap) +{ + uchar *p; + uint i, size; + + size = sizeS2M(f); + if(size == 0) + return 0; + if(size > nap) + return 0; + + p = (uchar*)ap; + + PBIT32(p, size); + p += BIT32SZ; + PBIT8(p, f->type); + p += BIT8SZ; + PBIT16(p, f->tag); + p += BIT16SZ; + + switch(f->type) + { + default: + return 0; + + case Tversion: + PBIT32(p, f->msize); + p += BIT32SZ; + p = pstring(p, f->version); + break; + + case Tflush: + PBIT16(p, f->oldtag); + p += BIT16SZ; + break; + + case Tauth: + PBIT32(p, f->afid); + p += BIT32SZ; + p = pstring(p, f->uname); + p = pstring(p, f->aname); + break; + + case Tattach: + PBIT32(p, f->fid); + p += BIT32SZ; + PBIT32(p, f->afid); + p += BIT32SZ; + p = pstring(p, f->uname); + p = pstring(p, f->aname); + break; + + case Twalk: + PBIT32(p, f->fid); + p += BIT32SZ; + PBIT32(p, f->newfid); + p += BIT32SZ; + PBIT16(p, f->nwname); + p += BIT16SZ; + if(f->nwname > MAXWELEM) + return 0; + for(i=0; i<f->nwname; i++) + p = pstring(p, f->wname[i]); + break; + + case Topen: + PBIT32(p, f->fid); + p += BIT32SZ; + PBIT8(p, f->mode); + p += BIT8SZ; + break; + + case Tcreate: + PBIT32(p, f->fid); + p += BIT32SZ; + p = pstring(p, f->name); + PBIT32(p, f->perm); + p += BIT32SZ; + PBIT8(p, f->mode); + p += BIT8SZ; + break; + + case Tread: + PBIT32(p, f->fid); + p += BIT32SZ; + PBIT64(p, f->offset); + p += BIT64SZ; + PBIT32(p, f->count); + p += BIT32SZ; + break; + + case Twrite: + PBIT32(p, f->fid); + p += BIT32SZ; + PBIT64(p, f->offset); + p += BIT64SZ; + PBIT32(p, f->count); + p += BIT32SZ; + memmove(p, f->data, f->count); + p += f->count; + break; + + case Tclunk: + case Tremove: + PBIT32(p, f->fid); + p += BIT32SZ; + break; + + case Tstat: + PBIT32(p, f->fid); + p += BIT32SZ; + break; + + case Twstat: + PBIT32(p, f->fid); + p += BIT32SZ; + PBIT16(p, f->nstat); + p += BIT16SZ; + memmove(p, f->stat, f->nstat); + p += f->nstat; + break; +/* + */ + + case Rversion: + PBIT32(p, f->msize); + p += BIT32SZ; + p = pstring(p, f->version); + break; + + case Rerror: + p = pstring(p, f->ename); + break; + + case Rflush: + break; + + case Rauth: + p = pqid(p, &f->aqid); + break; + + case Rattach: + p = pqid(p, &f->qid); + break; + + case Rwalk: + PBIT16(p, f->nwqid); + p += BIT16SZ; + if(f->nwqid > MAXWELEM) + return 0; + for(i=0; i<f->nwqid; i++) + p = pqid(p, &f->wqid[i]); + break; + + case Ropen: + case Rcreate: + p = pqid(p, &f->qid); + PBIT32(p, f->iounit); + p += BIT32SZ; + break; + + case Rread: + PBIT32(p, f->count); + p += BIT32SZ; + memmove(p, f->data, f->count); + p += f->count; + break; + + case Rwrite: + PBIT32(p, f->count); + p += BIT32SZ; + break; + + case Rclunk: + break; + + case Rremove: + break; + + case Rstat: + PBIT16(p, f->nstat); + p += BIT16SZ; + memmove(p, f->stat, f->nstat); + p += f->nstat; + break; + + case Rwstat: + break; + } + if(size != p-ap) + return 0; + return size; +} diff --git a/emu/port/sysfile.c b/emu/port/sysfile.c new file mode 100644 index 00000000..8159ae2d --- /dev/null +++ b/emu/port/sysfile.c @@ -0,0 +1,1089 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "kernel.h" + +static int +growfd(Fgrp *f, int fd) +{ + int n; + Chan **nfd, **ofd; + + if(fd < f->nfd) + return 0; + n = f->nfd+DELTAFD; + if(n > MAXNFD) + n = MAXNFD; + if(fd >= n) + return -1; + nfd = malloc(n*sizeof(Chan*)); + if(nfd == nil) + return -1; + ofd = f->fd; + memmove(nfd, ofd, f->nfd*sizeof(Chan *)); + f->fd = nfd; + f->nfd = n; + free(ofd); + return 0; +} + +static int +newfd(Chan *c) +{ + int i; + Fgrp *f = up->env->fgrp; + + lock(&f->l); + for(i=f->minfd; i<f->nfd; i++) + if(f->fd[i] == 0) + break; + if(i >= f->nfd && growfd(f, i) < 0){ + unlock(&f->l); + exhausted("file descriptors"); + return -1; + } + f->minfd = i + 1; + if(i > f->maxfd) + f->maxfd = i; + f->fd[i] = c; + unlock(&f->l); + return i; +} + +Chan* +fdtochan(Fgrp *f, int fd, int mode, int chkmnt, int iref) +{ + Chan *c; + + c = 0; + lock(&f->l); + if(fd<0 || f->maxfd<fd || (c = f->fd[fd])==0) { + unlock(&f->l); + error(Ebadfd); + } + if(iref) + incref(&c->r); + unlock(&f->l); + + if(chkmnt && (c->flag&CMSG)) + goto bad; + if(mode<0 || c->mode==ORDWR) + return c; + if((mode&OTRUNC) && c->mode==OREAD) + goto bad; + if((mode&~OTRUNC) != c->mode) + goto bad; + return c; +bad: + if(iref) + cclose(c); + error(Ebadusefd); + return nil; +} + +long +kchanio(void *vc, void *buf, int n, int mode) +{ + int r; + Chan *c; + + c = vc; + if(waserror()) + return -1; + + if(mode == OREAD) + r = devtab[c->type]->read(c, buf, n, c->offset); + else + r = devtab[c->type]->write(c, buf, n, c->offset); + + lock(&c->l); + c->offset += r; + unlock(&c->l); + poperror(); + return r; +} + +int +openmode(ulong o) +{ + if(o >= (OTRUNC|OCEXEC|ORCLOSE|OEXEC)) + error(Ebadarg); + o &= ~(OTRUNC|OCEXEC|ORCLOSE); + if(o > OEXEC) + error(Ebadarg); + if(o == OEXEC) + return OREAD; + return o; +} + +static void +fdclose(Fgrp *f, int fd) +{ + int i; + Chan *c; + + lock(&f->l); + c = f->fd[fd]; + if(c == 0) { + /* can happen for users with shared fd tables */ + unlock(&f->l); + return; + } + f->fd[fd] = 0; + if(fd == f->maxfd) + for(i=fd; --i>=0 && f->fd[i]==0; ) + f->maxfd = i; + if(fd < f->minfd) + f->minfd = fd; + unlock(&f->l); + cclose(c); +} + +int +kchdir(char *path) +{ + Chan *c; + Pgrp *pg; + + if(waserror()) + return -1; + + c = namec(path, Atodir, 0, 0); + pg = up->env->pgrp; + cclose(pg->dot); + pg->dot = c; + poperror(); + return 0; +} + +int +kfgrpclose(Fgrp *f, int fd) +{ + if(waserror()) + return -1; + + /* + * Take no reference on the chan because we don't really need the + * data structure, and are calling fdtochan only for error checks. + * fdclose takes care of processes racing through here. + */ + fdtochan(f, fd, -1, 0, 0); + fdclose(f, fd); + poperror(); + return 0; +} + +int +kclose(int fd) +{ + return kfgrpclose(up->env->fgrp, fd); +} + +int +kcreate(char *path, int mode, ulong perm) +{ + int fd; + volatile struct { Chan *c; } c; + + c.c = nil; + if(waserror()) { + cclose(c.c); + return -1; + } + + openmode(mode&~OEXCL); /* error check only; OEXCL okay here */ + c.c = namec(path, Acreate, mode, perm); + fd = newfd(c.c); + if(fd < 0) + error(Enofd); + poperror(); + return fd; +} + +int +kdup(int old, int new) +{ + int fd; + Chan *oc; + Fgrp *f = up->env->fgrp; + volatile struct { Chan *c; } c; + + if(waserror()) + return -1; + + c.c = fdtochan(up->env->fgrp, old, -1, 0, 1); + if(c.c->qid.type & QTAUTH) + error(Eperm); + fd = new; + if(fd != -1) { + lock(&f->l); + if(fd < 0 || growfd(f, fd) < 0) { + unlock(&f->l); + cclose(c.c); + error(Ebadfd); + } + if(fd > f->maxfd) + f->maxfd = fd; + oc = f->fd[fd]; + f->fd[fd] = c.c; + unlock(&f->l); + if(oc != 0) + cclose(oc); + } + else { + if(waserror()) { + cclose(c.c); + nexterror(); + } + fd = newfd(c.c); + if(fd < 0) + error(Enofd); + poperror(); + } + poperror(); + return fd; +} + +int +kfstat(int fd, uchar *buf, int n) +{ + volatile struct { Chan *c; } c; + + c.c = nil; + if(waserror()) { + cclose(c.c); + return -1; + } + c.c = fdtochan(up->env->fgrp, fd, -1, 0, 1); + devtab[c.c->type]->stat(c.c, buf, n); + poperror(); + cclose(c.c); + return n; +} + +char* +kfd2path(int fd) +{ + Chan *c; + char *s; + + if(waserror()) + return nil; + c = fdtochan(up->env->fgrp, fd, -1, 0, 1); + s = nil; + if(c->name != nil){ + s = malloc(c->name->len+1); + if(s == nil){ + cclose(c); + error(Enomem); + } + memmove(s, c->name->s, c->name->len+1); + cclose(c); + } + poperror(); + return s; +} + +int +kfauth(int fd, char *aname) +{ + Chan *c, *ac; + + if(waserror()) + return -1; + + validname(aname, 0); + c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1); + if(waserror()){ + cclose(c); + nexterror(); + } + + ac = mntauth(c, aname); + + /* at this point ac is responsible for keeping c alive */ + poperror(); /* c */ + cclose(c); + + if(waserror()){ + cclose(ac); + nexterror(); + } + + fd = newfd(ac); + if(fd < 0) + error(Enofd); + poperror(); /* ac */ + + poperror(); + + return fd; +} + +int +kfversion(int fd, uint msize, char *vers, uint arglen) +{ + int m; + Chan *c; + + if(waserror()) + return -1; + + /* check there's a NUL in the version string */ + if(arglen==0 || memchr(vers, 0, arglen)==0) + error(Ebadarg); + + c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1); + if(waserror()){ + cclose(c); + nexterror(); + } + + m = mntversion(c, vers, msize, arglen); + + poperror(); + cclose(c); + + poperror(); + return m; +} + +int +kpipe(int fd[2]) +{ + Dev *d; + Fgrp *f; + Chan *c[2]; + static char *names[] = {"data", "data1"}; + + f = up->env->fgrp; + + d = devtab[devno('|', 0)]; + c[0] = namec("#|", Atodir, 0, 0); + c[1] = 0; + fd[0] = -1; + fd[1] = -1; + if(waserror()) { + if(c[0] != 0) + cclose(c[0]); + if(c[1] != 0) + cclose(c[1]); + if(fd[0] >= 0) + f->fd[fd[0]]=0; + if(fd[1] >= 0) + f->fd[fd[1]]=0; + return -1; + } + c[1] = cclone(c[0]); + if(walk(&c[0], &names[0], 1, 1, nil) < 0) + error(Egreg); + if(walk(&c[1], &names[1], 1, 1, nil) < 0) + error(Egreg); + c[0] = d->open(c[0], ORDWR); + c[1] = d->open(c[1], ORDWR); + fd[0] = newfd(c[0]); + if(fd[0] < 0) + error(Enofd); + fd[1] = newfd(c[1]); + if(fd[1] < 0) + error(Enofd); + poperror(); + return 0; +} + +int +kfwstat(int fd, uchar *buf, int n) +{ + volatile struct { Chan *c; } c; + + c.c = nil; + if(waserror()) { + cclose(c.c); + return -1; + } + validstat(buf, n); + c.c = fdtochan(up->env->fgrp, fd, -1, 1, 1); + n = devtab[c.c->type]->wstat(c.c, buf, n); + poperror(); + cclose(c.c); + return n; +} + +long +bindmount(Chan *c, char *old, int flag, char *spec) +{ + int ret; + volatile struct { Chan *c; } c1; + + if(flag>MMASK || (flag&MORDER) == (MBEFORE|MAFTER)) + error(Ebadarg); + + c1.c = namec(old, Amount, 0, 0); + if(waserror()){ + cclose(c1.c); + nexterror(); + } + ret = cmount(c, c1.c, flag, spec); + + poperror(); + cclose(c1.c); + return ret; +} + +int +kbind(char *new, char *old, int flags) +{ + long r; + volatile struct { Chan *c; } c0; + + c0.c = nil; + if(waserror()) { + cclose(c0.c); + return -1; + } + c0.c = namec(new, Abind, 0, 0); + r = bindmount(c0.c, old, flags, ""); + poperror(); + cclose(c0.c); + return r; +} + +int +kmount(int fd, int afd, char *old, int flags, char *spec) +{ + long r; + volatile struct { Chan *c; } c0; + volatile struct { Chan *c; } bc; + volatile struct { Chan *c; } ac; + Mntparam mntparam; + + ac.c = nil; + bc.c = nil; + c0.c = nil; + if(waserror()) { + cclose(ac.c); + cclose(bc.c); + cclose(c0.c); + return -1; + } + bc.c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1); + if(afd >= 0) + ac.c = fdtochan(up->env->fgrp, afd, ORDWR, 0, 1); + mntparam.chan = bc.c; + mntparam.authchan = ac.c; + mntparam.spec = spec; + mntparam.flags = flags; + c0.c = devtab[devno('M', 0)]->attach((char*)&mntparam); + + r = bindmount(c0.c, old, flags, spec); + poperror(); + cclose(ac.c); + cclose(bc.c); + cclose(c0.c); + + return r; +} + +int +kunmount(char *old, char *new) +{ + volatile struct { Chan *c; } cmount; + volatile struct { Chan *c; } cmounted; + + cmount.c = nil; + cmounted.c = nil; + if(waserror()) { + cclose(cmount.c); + cclose(cmounted.c); + return -1; + } + + cmount.c = namec(new, Amount, 0, 0); + if(old != nil && old[0] != '\0') { + /* + * This has to be namec(..., Aopen, ...) because + * if arg[0] is something like /srv/cs or /fd/0, + * opening it is the only way to get at the real + * Chan underneath. + */ + cmounted.c = namec(old, Aopen, OREAD, 0); + } + + cunmount(cmount.c, cmounted.c); + poperror(); + cclose(cmount.c); + cclose(cmounted.c); + return 0; +} + +int +kopen(char *path, int mode) +{ + int fd; + volatile struct { Chan *c; } c; + + if(waserror()) + return -1; + + openmode(mode); /* error check only */ + c.c = namec(path, Aopen, mode, 0); + if(waserror()){ + cclose(c.c); + nexterror(); + } + fd = newfd(c.c); + if(fd < 0) + error(Enofd); + poperror(); + + poperror(); + return fd; +} + +long +unionread(Chan *c, void *va, long n) +{ + int i; + long nr; + Mhead *m; + Mount *mount; + + qlock(&c->umqlock); + m = c->umh; + rlock(&m->lock); + mount = m->mount; + /* bring mount in sync with c->uri and c->umc */ + for(i = 0; mount != nil && i < c->uri; i++) + mount = mount->next; + + nr = 0; + while(mount != nil) { + /* Error causes component of union to be skipped */ + if(mount->to && !waserror()) { + if(c->umc == nil){ + c->umc = cclone(mount->to); + c->umc = devtab[c->umc->type]->open(c->umc, OREAD); + } + + nr = devtab[c->umc->type]->read(c->umc, va, n, c->umc->offset); + if(nr < 0) + nr = 0; /* dev.c can return -1 */ + c->umc->offset += nr; + poperror(); + } + if(nr > 0) + break; + + /* Advance to next element */ + c->uri++; + if(c->umc) { + cclose(c->umc); + c->umc = nil; + } + mount = mount->next; + } + runlock(&m->lock); + qunlock(&c->umqlock); + return nr; +} + +static void +unionrewind(Chan *c) +{ + qlock(&c->umqlock); + c->uri = 0; + if(c->umc){ + cclose(c->umc); + c->umc = nil; + } + qunlock(&c->umqlock); +} + +static long +rread(int fd, void *va, long n, vlong *offp) +{ + int dir; + Lock *cl; + volatile struct { Chan *c; } c; + vlong off; + + if(waserror()) + return -1; + + c.c = fdtochan(up->env->fgrp, fd, OREAD, 1, 1); + if(waserror()){ + cclose(c.c); + nexterror(); + } + + if(n < 0) + error(Etoosmall); + + dir = c.c->qid.type & QTDIR; + if(dir && c.c->umh) + n = unionread(c.c, va, n); + else{ + cl = &c.c->l; + if(offp == nil){ + lock(cl); /* lock for vlong assignment */ + off = c.c->offset; + unlock(cl); + }else + off = *offp; + if(off < 0) + error(Enegoff); + if(off == 0){ + if(offp == nil){ + lock(cl); + c.c->offset = 0; + c.c->dri = 0; + unlock(cl); + } + unionrewind(c.c); + } + n = devtab[c.c->type]->read(c.c, va, n, off); + lock(cl); + c.c->offset += n; + unlock(cl); + } + + poperror(); + cclose(c.c); + + poperror(); + return n; +} + +long +kread(int fd, void *va, long n) +{ + return rread(fd, va, n, nil); +} + +long +kpread(int fd, void *va, long n, vlong off) +{ + return rread(fd, va, n, &off); +} + +int +kremove(char *path) +{ + volatile struct { Chan *c; } c; + + if(waserror()) + return -1; + + c.c = namec(path, Aremove, 0, 0); + if(waserror()){ + c.c->type = 0; /* see below */ + cclose(c.c); + nexterror(); + } + devtab[c.c->type]->remove(c.c); + /* + * Remove clunks the fid, but we need to recover the Chan + * so fake it up. rootclose() is known to be a nop. + */ + c.c->type = 0; + poperror(); + cclose(c.c); + + poperror(); + return 0; +} + +vlong +kseek(int fd, vlong off, int whence) +{ + Dir *dir; + Chan *c; + + if(waserror()) + return -1; + + c = fdtochan(up->env->fgrp, fd, -1, 1, 1); + if(waserror()) { + cclose(c); + nexterror(); + } + + if(devtab[c->type]->dc == '|') + error(Eisstream); + + switch(whence) { + case 0: + if(c->qid.type & QTDIR){ + if(off != 0) + error(Eisdir); + unionrewind(c); + }else if(off < 0) + error(Enegoff); + lock(&c->l); /* lock for vlong assignment */ + c->offset = off; + unlock(&c->l); + break; + + case 1: + if(c->qid.type & QTDIR) + error(Eisdir); + lock(&c->l); /* lock for read/write update */ + off += c->offset; + if(off < 0){ + unlock(&c->l); + error(Enegoff); + } + c->offset = off; + unlock(&c->l); + break; + + case 2: + if(c->qid.type & QTDIR) + error(Eisdir); + dir = chandirstat(c); + if(dir == nil) + error("internal error: stat error in seek"); + off += dir->length; + free(dir); + if(off < 0) + error(Enegoff); + lock(&c->l); /* lock for read/write update */ + c->offset = off; + unlock(&c->l); + break; + + default: + error(Ebadarg); + break; + } + poperror(); + c->dri = 0; + cclose(c); + poperror(); + return off; +} + +void +validstat(uchar *s, int n) +{ + int m; + char buf[64]; + + if(statcheck(s, n) < 0) + error(Ebadstat); + /* verify that name entry is acceptable */ + s += STATFIXLEN - 4*BIT16SZ; /* location of first string */ + /* + * s now points at count for first string. + * if it's too long, let the server decide; this is + * only for his protection anyway. otherwise + * we'd have to allocate and waserror. + */ + m = GBIT16(s); + s += BIT16SZ; + if(m+1 > sizeof buf) + return; + memmove(buf, s, m); + buf[m] = '\0'; + /* name could be '/' */ + if(strcmp(buf, "/") != 0) + validname(buf, 0); +} + +int +kstat(char *path, uchar *buf, int n) +{ + volatile struct { Chan *c; } c; + + c.c = nil; + if(waserror()){ + cclose(c.c); + return -1; + } + c.c = namec(path, Aaccess, 0, 0); + devtab[c.c->type]->stat(c.c, buf, n); + poperror(); + cclose(c.c); + return 0; +} + +static long +rwrite(int fd, void *va, long n, vlong *offp) +{ + Lock *cl; + volatile struct { Chan *c; } c; + vlong off; + long m; + + if(waserror()) + return -1; + c.c = fdtochan(up->env->fgrp, fd, OWRITE, 1, 1); + if(waserror()){ + cclose(c.c); + nexterror(); + } + if(c.c->qid.type & QTDIR) + error(Eisdir); + + if(n < 0) + error(Etoosmall); + + cl = &c.c->l; + if(offp == nil){ + lock(cl); + off = c.c->offset; + c.c->offset += n; + unlock(cl); + }else + off = *offp; + + if(waserror()){ + if(offp == nil){ + lock(cl); + c.c->offset -= n; + unlock(cl); + } + nexterror(); + } + if(off < 0) + error(Enegoff); + m = devtab[c.c->type]->write(c.c, va, n, off); + poperror(); + + if(offp == nil && m < n){ + lock(cl); + c.c->offset -= n - m; + unlock(cl); + } + + poperror(); + cclose(c.c); + + poperror(); + return m; +} + +long +kwrite(int fd, void *va, long n) +{ + return rwrite(fd, va, n, nil); +} + +long +kpwrite(int fd, void *va, long n, vlong off) +{ + return rwrite(fd, va, n, &off); +} + +int +kwstat(char *path, uchar *buf, int n) +{ + volatile struct { Chan *c; } c; + + c.c = nil; + if(waserror()){ + cclose(c.c); + return -1; + } + validstat(buf, n); + c.c = namec(path, Aaccess, 0, 0); + n = devtab[c.c->type]->wstat(c.c, buf, n); + poperror(); + cclose(c.c); + return n; +} + +enum +{ + DIRSIZE = STATFIXLEN + 32 * 4, + DIRREADLIM = 2048, /* should handle the largest reasonable directory entry */ +}; + +Dir* +chandirstat(Chan *c) +{ + Dir *d; + uchar *buf; + int n, nd, i; + + nd = DIRSIZE; + for(i=0; i<2; i++){ /* should work by the second try */ + d = smalloc(sizeof(Dir) + nd); + buf = (uchar*)&d[1]; + if(waserror()){ + free(d); + return nil; + } + n = devtab[c->type]->stat(c, buf, nd); + poperror(); + if(n < BIT16SZ){ + free(d); + return nil; + } + nd = GBIT16((uchar*)buf) + BIT16SZ; /* size needed to store whole stat buffer including count */ + if(nd <= n){ + convM2D(buf, n, d, (char*)&d[1]); + return d; + } + /* else sizeof(Dir)+nd is plenty */ + free(d); + } + return nil; + +} + +Dir* +kdirstat(char *name) +{ + volatile struct {Chan *c;} c; + Dir *d; + + c.c = nil; + if(waserror()){ + cclose(c.c); + return nil; + } + c.c = namec(name, Aaccess, 0, 0); + d = chandirstat(c.c); + poperror(); + cclose(c.c); + return d; +} + +Dir* +kdirfstat(int fd) +{ + volatile struct { Chan *c; } c; + Dir *d; + + c.c = nil; + if(waserror()) { + cclose(c.c); + return nil; + } + c.c = fdtochan(up->env->fgrp, fd, -1, 0, 1); + d = chandirstat(c.c); + poperror(); + cclose(c.c); + return d; +} + +int +kdirwstat(char *name, Dir *dir) +{ + uchar *buf; + int r; + + r = sizeD2M(dir); + buf = smalloc(r); + convD2M(dir, buf, r); + r = kwstat(name, buf, r); + free(buf); + return r < 0? r: 0; +} + +int +kdirfwstat(int fd, Dir *dir) +{ + uchar *buf; + int r; + + r = sizeD2M(dir); + buf = smalloc(r); + convD2M(dir, buf, r); + r = kfwstat(fd, buf, r); + free(buf); + return r < 0? r: 0; +} + +static long +dirpackage(uchar *buf, long ts, Dir **d) +{ + char *s; + long ss, i, n, nn, m; + + *d = nil; + if(ts <= 0) + return ts; + + /* + * first find number of all stats, check they look like stats, & size all associated strings + */ + ss = 0; + n = 0; + for(i = 0; i < ts; i += m){ + m = BIT16SZ + GBIT16(&buf[i]); + if(statcheck(&buf[i], m) < 0) + break; + ss += m; + n++; + } + + if(i != ts) + error("bad directory format"); + + *d = malloc(n * sizeof(Dir) + ss); + if(*d == nil) + error(Enomem); + + /* + * then convert all buffers + */ + s = (char*)*d + n * sizeof(Dir); + nn = 0; + for(i = 0; i < ts; i += m){ + m = BIT16SZ + GBIT16((uchar*)&buf[i]); + if(nn >= n || convM2D(&buf[i], m, *d + nn, s) != m){ + free(*d); + *d = nil; + error("bad directory entry"); + } + nn++; + s += m; + } + + return nn; +} + +long +kdirread(int fd, Dir **d) +{ + uchar *buf; + long ts; + + *d = nil; + if(waserror()) + return -1; + buf = malloc(DIRREADLIM); + if(buf == nil) + error(Enomem); + if(waserror()){ + free(buf); + nexterror(); + } + ts = kread(fd, buf, DIRREADLIM); + if(ts > 0) + ts = dirpackage(buf, ts, d); + + poperror(); + free(buf); + poperror(); + return ts; +} + +int +kiounit(int fd) +{ + Chan *c; + int n; + + c = fdtochan(up->env->fgrp, fd, -1, 0, 1); + if(waserror()){ + cclose(c); + return 0; /* n.b. */ + } + n = c->iounit; + poperror(); + cclose(c); + return n; +} diff --git a/emu/port/uqid.c b/emu/port/uqid.c new file mode 100644 index 00000000..bd43db3d --- /dev/null +++ b/emu/port/uqid.c @@ -0,0 +1,114 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + +/* + * unique Qid path generation, + * for exportfs.c and various devfs-* + */ + +#define QIDMASK (((vlong)1<<48)-1) + +void +uqidinit(Uqidtab *tab) +{ + memset(tab, 0, sizeof(*tab)); +} + +static int +uqidhash(vlong path) +{ + ulong p; + p = (ulong)path; + return ((p>>16) ^ (p>>8) ^ p) & (Nqidhash-1); +} + +static Uqid ** +uqidlook(Uqid **tab, Chan *c, vlong path) +{ + Uqid **hp, *q; + + for(hp = &tab[uqidhash(path)]; (q = *hp) != nil; hp = &q->next) + if(q->type == c->type && q->dev == c->dev && q->oldpath == path) + break; + return hp; +} + +static int +uqidexists(Uqid **tab, vlong path) +{ + int i; + Uqid *q; + + for(i=0; i<Nqidhash; i++) + for(q = tab[i]; q != nil; q = q->next) + if(q->newpath == path) + return 1; + return 0; +} + +Uqid * +uqidalloc(Uqidtab *tab, Chan *c) +{ + Uqid **hp, *q; + + qlock(&tab->l); + hp = uqidlook(tab->qids, c, c->qid.path); + if((q = *hp) != nil){ + incref(&q->r); + qunlock(&tab->l); + return q; + } + q = mallocz(sizeof(*q), 1); + if(q == nil){ + qunlock(&tab->l); + error(Enomem); + } + q->r.ref = 1; + q->type = c->type; + q->dev = c->dev; + q->oldpath = c->qid.path; + q->newpath = c->qid.path; + while(uqidexists(tab->qids, q->newpath)){ + if(++tab->pathgen >= (1<<16)) + tab->pathgen = 1; + q->newpath = ((vlong)tab->pathgen<<48) | (q->newpath & QIDMASK); + } + q->next = nil; + *hp = q; + qunlock(&tab->l); + return q; +} + +void +freeuqid(Uqidtab *tab, Uqid *q) +{ + Uqid **hp; + + if(q == nil) + return; + qlock(&tab->l); + if(decref(&q->r) == 0){ + hp = &tab->qids[uqidhash(q->oldpath)]; + for(; *hp != nil; hp = &(*hp)->next) + if(*hp == q){ + *hp = q->next; + free(q); + break; + } + } + qunlock(&tab->l); +} + +Qid +mkuqid(Chan *c, Uqid *qid) +{ + Qid q; + + if(qid == nil) + return c->qid; + q.path = qid->newpath; + q.vers = c->qid.vers; + q.type = c->qid.type; + return q; +} diff --git a/emu/port/win-x11-old.c b/emu/port/win-x11-old.c new file mode 100644 index 00000000..e06d455c --- /dev/null +++ b/emu/port/win-x11-old.c @@ -0,0 +1,847 @@ +#include "dat.h" +#include "fns.h" +#include "cursor.h" +#include "keyboard.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <X11/Xutil.h> +#include <X11/keysym.h> + +#define ABS(x) ((x) < 0 ? -(x) : (x)) + +/* + * alias defs for image types to overcome name conflicts + */ +typedef struct ICursor ICursor; +typedef struct IPoint IPoint; +typedef struct IRectangle IRectangle; +typedef struct CRemapTbl CRemapTbl; +struct ICursor +{ + int w; + int h; + int hotx; + int hoty; + char *src; + char *mask; +}; + +struct IPoint +{ + int x; + int y; +}; + +struct IRectangle +{ + IPoint min; + IPoint max; +}; + +struct CRemapTbl +{ + ulong inferno[256]; /* The corresponding inferno colormap vals */ + ulong openslot[256]; + Bool cellused[256]; + int cnt; + int opencnt; +}; + +enum +{ + DblTime = 300 /* double click time in msec */ +}; + +XColor map[256]; /* Inferno colormap array */ +XColor map7[128]; /* Inferno colormap array */ +uchar map7to8[128][2]; +Colormap xcmap; /* Default shared colormap */ +int infernotox11[256]; /* Values for mapping between */ +int x11toinferno[256]; /* X11 and inferno */ +int x24bitswap = 0; /* swap endian for 24bit RGB */ + +static int triedscreen; +static XModifierKeymap *modmap; +static int keypermod; +static Drawable xdrawable; +static Atom wm_take_focus; +static void xexpose(XEvent*); +static void xmouse(XEvent*); +static void xkeyboard(XEvent*); +static void xmapping(XEvent*); +static void xproc(void*); +static void xinitscreen(int, int); +static void initmap(Window); +static GC creategc(Drawable); +static CRemapTbl crtbl; +static void graphicscmap(XColor*); + int xscreendepth; + Drawable xscreenid; + Display* xdisplay; + Display* xkmcon; + Visual *xvis; + GC xgcfill, xgccopy, xgcsimplesrc, xgczero, xgcreplsrc; + GC xgcfill0, xgccopy0, xgcsimplesrc0, xgczero0, xgcreplsrc0; + +char *gkscanid = "emu_x11"; + + +ulong* +attachscreen(IRectangle *r, int *ld, int *width, int *softscreen) +{ + extern ulong* makememones(); + + r->min.x = 0; + r->min.y = 0; + r->max.x = Xsize; + r->max.y = Ysize; + *ld = 3; + *width = Xsize/4; + *softscreen = 1; + if(!triedscreen){ + triedscreen = 1; + xinitscreen(Xsize, Ysize); + if(kproc("xproc", xproc, nil, 0) < 0) { + fprint(2, "emu: win-x11 can't make X proc\n"); + return 0; + } + } + return makememones(); +} + +void +flushmemscreen(IRectangle r) +{ + if(r.min.x > r.max.x) + return; + XCopyArea(xdisplay, xscreenid, xdrawable, xgccopy, + r.min.x, r.min.y, + r.max.x-r.min.x, r.max.y-r.min.y, r.min.x, r.min.y); + XFlush(xdisplay); +} + +static int +revbyte(int b) +{ + int r; + + r = 0; + r |= (b&0x01) << 7; + r |= (b&0x02) << 5; + r |= (b&0x04) << 3; + r |= (b&0x08) << 1; + r |= (b&0x10) >> 1; + r |= (b&0x20) >> 3; + r |= (b&0x40) >> 5; + r |= (b&0x80) >> 7; + return r; +} + +static void +gotcursor(ICursor c) +{ + Cursor xc; + XColor fg, bg; + Pixmap xsrc, xmask; + static Cursor xcursor; + + if(c.src == nil){ + if(xcursor != 0) { + XFreeCursor(xdisplay, xcursor); + xcursor = 0; + } + XUndefineCursor(xdisplay, xdrawable); + XFlush(xdisplay); + return; + } + xsrc = XCreateBitmapFromData(xdisplay, xdrawable, c.src, c.w, c.h); + xmask = XCreateBitmapFromData(xdisplay, xdrawable, c.mask, c.w, c.h); + + fg = map[255]; + bg = map[0]; + fg.pixel = infernotox11[255]; + bg.pixel = infernotox11[0]; + xc = XCreatePixmapCursor(xdisplay, xsrc, xmask, &fg, &bg, -c.hotx, -c.hoty); + if(xc != 0) { + XDefineCursor(xdisplay, xdrawable, xc); + if(xcursor != 0) + XFreeCursor(xdisplay, xcursor); + xcursor = xc; + } + XFreePixmap(xdisplay, xsrc); + XFreePixmap(xdisplay, xmask); + XFlush(xdisplay); + free(c.src); +} + +void +setcursor(IPoint p) +{ + XWarpPointer(xdisplay, None, xdrawable, 0, 0, 0, 0, p.x, p.y); + XFlush(xdisplay); +} + +void +drawcursor(Drawcursor* c) +{ + ICursor ic; + IRectangle ir; + uchar *bs, *bc; + int i, h, j, bpl; + char *src, *mask, *csrc, *cmask; + + /* Set the default system cursor */ + src = nil; + mask = nil; + if(c->data != nil){ + h = (c->maxy-c->miny)/2; + ir.min.x = c->minx; + ir.min.y = c->miny; + ir.max.x = c->maxx; + ir.max.y = c->maxy; + /* passing IRectangle to Rectangle is safe */ + bpl = bytesperline(ir, 1); + + i = h*bpl; + src = malloc(2*i); + if(src == nil) + return; + mask = src + i; + + csrc = src; + cmask = mask; + bc = c->data; + bs = c->data + h*bpl; + for(i = 0; i < h; i++){ + for(j = 0; j < bpl; j++) { + *csrc++ = revbyte(bs[j]); + *cmask++ = revbyte(bs[j] | bc[j]); + } + bs += bpl; + bc += bpl; + } + } + ic.w = 8*bpl; + ic.h = h; + ic.hotx = c->hotx; + ic.hoty = c->hoty; + ic.src = src; + ic.mask = mask; + + gotcursor(ic); +} + +static void +xproc(void *arg) +{ + ulong mask; + XEvent event; + + closepgrp(up->env->pgrp); + closefgrp(up->env->fgrp); + closeegrp(up->env->egrp); + closesigs(up->env->sigs); + + mask = KeyPressMask| + KeyReleaseMask| + ButtonPressMask| + ButtonReleaseMask| + PointerMotionMask| + Button1MotionMask| + Button2MotionMask| + Button3MotionMask| + ExposureMask; + + XSelectInput(xkmcon, xdrawable, mask); + for(;;) { + XWindowEvent(xkmcon, xdrawable, mask, &event); + switch(event.type) { + case KeyPress: + case KeyRelease: + xkeyboard(&event); + break; + case ButtonPress: + case ButtonRelease: + case MotionNotify: + xmouse(&event); + break; + case Expose: + xexpose(&event); + break; + case MappingNotify: + xmapping(&event); + break; + default: + break; + } + } +} + +static int +shutup(Display *d, XErrorEvent *e) +{ + char buf[200]; + print("X error: error code=%d, request_code=%d, minor=%d\n", e->error_code, e->request_code, e->minor_code); + XGetErrorText(d, e->error_code, buf, sizeof(buf)); + print("%s\n", buf); + USED(d); + USED(e); + return 0; +} + +static void +xinitscreen(int xsize, int ysize) +{ + int i, pmid; + char *argv[2]; + char *disp_val; + Window rootwin; + XWMHints hints; + Screen *screen; + XVisualInfo xvi; + int rootscreennum; + XTextProperty name; + XClassHint classhints; + XSizeHints normalhints; + XSetWindowAttributes attrs; + + xscreenid = 0; + xdrawable = 0; + + xdisplay = XOpenDisplay(NULL); + if(xdisplay == 0){ + disp_val = getenv("DISPLAY"); + if(disp_val == 0) + disp_val = "not set"; + fprint(2, "emu: win-x11 open %r, DISPLAY is %s\n", disp_val); + cleanexit(0); + } + + rootscreennum = DefaultScreen(xdisplay); + rootwin = DefaultRootWindow(xdisplay); + + xscreendepth = DefaultDepth(xdisplay, rootscreennum); + if(XMatchVisualInfo(xdisplay, rootscreennum, 24, TrueColor, &xvi) + || XMatchVisualInfo(xdisplay, rootscreennum, 24, DirectColor, &xvi)){ + xvis = xvi.visual; + xscreendepth = 24; + xtblbit = 1; + } + else if(XMatchVisualInfo(xdisplay, rootscreennum, 8, PseudoColor, &xvi) + || XMatchVisualInfo(xdisplay, rootscreennum, 8, StaticColor, &xvi)){ + if(xscreendepth > 8) { + fprint(2, "emu: win-x11 can't deal with depth %d screens\n", xscreendepth); + cleanexit(0); + } + xvis = xvi.visual; + xscreendepth = 8; + } + else{ + if(xscreendepth != 8){ + fprint(2, "emu: win-x11 can't deal with depth %d screens\n", xscreendepth); + cleanexit(0); + } + xvis = DefaultVisual(xdisplay, rootscreennum); + } + + screen = DefaultScreenOfDisplay(xdisplay); + xcmap = DefaultColormapOfScreen(screen); + + if(xvis->class != StaticColor){ + graphicscmap(map); + initmap(rootwin); + } + + if(modmap = XGetModifierMapping(xdisplay)) + keypermod = modmap->max_keypermod; + + attrs.colormap = xcmap; + attrs.background_pixel = 0; + attrs.border_pixel = 0; + /* attrs.override_redirect = 1;*/ /* WM leave me alone! |CWOverrideRedirect */ + xdrawable = XCreateWindow(xdisplay, rootwin, 0, 0, xsize, ysize, 0, xscreendepth, InputOutput, xvis, CWBackPixel|CWBorderPixel|CWColormap, &attrs); + + /* + * set up property as required by ICCCM + */ + name.value = "inferno"; + name.encoding = XA_STRING; + name.format = 8; + name.nitems = strlen(name.value); + normalhints.flags = USSize|PMaxSize; + normalhints.max_width = normalhints.width = xsize; + normalhints.max_height = normalhints.height = ysize; + hints.flags = InputHint|StateHint; + hints.input = 1; + hints.initial_state = NormalState; + classhints.res_name = "inferno"; + classhints.res_class = "Inferno"; + argv[0] = "inferno"; + argv[1] = nil; + XSetWMProperties(xdisplay, xdrawable, + &name, /* XA_WM_NAME property for ICCCM */ + &name, /* XA_WM_ICON_NAME */ + argv, /* XA_WM_COMMAND */ + 1, /* argc */ + &normalhints, /* XA_WM_NORMAL_HINTS */ + &hints, /* XA_WM_HINTS */ + &classhints); /* XA_WM_CLASS */ + + /* + * put the window on the screen + */ + XMapWindow(xdisplay, xdrawable); + + xscreenid = XCreatePixmap(xdisplay, xdrawable, xsize, ysize, xscreendepth); + XFlush(xdisplay); + + xgcfill = creategc(xscreenid); + XSetFillStyle(xdisplay, xgcfill, FillSolid); + xgccopy = creategc(xscreenid); + xgcsimplesrc = creategc(xscreenid); + XSetFillStyle(xdisplay, xgcsimplesrc, FillStippled); + xgczero = creategc(xscreenid); + xgcreplsrc = creategc(xscreenid); + XSetFillStyle(xdisplay, xgcreplsrc, FillTiled); + + pmid = XCreatePixmap(xdisplay, xdrawable, 1, 1, 1); + xgcfill0 = creategc(pmid); + XSetFillStyle(xdisplay, xgcfill0, FillSolid); + xgccopy0 = creategc(pmid); + xgcsimplesrc0 = creategc(pmid); + XSetFillStyle(xdisplay, xgcsimplesrc0, FillStippled); + xgczero0 = creategc(pmid); + xgcreplsrc0 = creategc(pmid); + XSetFillStyle(xdisplay, xgcreplsrc0, FillTiled); + XFreePixmap(xdisplay, pmid); + + XSetForeground(xdisplay, xgccopy, infernotox11[0]); + XFillRectangle(xdisplay, xscreenid, xgccopy, 0, 0, xsize, ysize); + + xkmcon = XOpenDisplay(NULL); + if(xkmcon == 0){ + disp_val = getenv("DISPLAY"); + if(disp_val == 0) + disp_val = "not set"; + fprint(2, "emu: win-x11 open %r, DISPLAY is %s\n", disp_val); + cleanexit(0); + } +} + +static void +graphicscmap(XColor *map) +{ + int r, g, b, cr, cg, cb, v, num, den, idx, v7, idx7; + + for(r=0; r!=4; r++) { + for(g = 0; g != 4; g++) { + for(b = 0; b!=4; b++) { + for(v = 0; v!=4; v++) { + den=r; + if(g > den) + den=g; + if(b > den) + den=b; + /* divide check -- pick grey shades */ + if(den==0) + cr=cg=cb=v*17; + else { + num=17*(4*den+v); + cr=r*num/den; + cg=g*num/den; + cb=b*num/den; + } + idx = r*64 + v*16 + ((g*4 + b + v - r) & 15); + idx = 255 - idx; + map[idx].red = cr*0x0101; + map[idx].green = cg*0x0101; + map[idx].blue = cb*0x0101; + map[idx].pixel = idx; + map[idx].flags = DoRed|DoGreen|DoBlue; + + v7 = v >> 1; + idx7 = r*32 + v7*16 + g*4 + b; + if((v & 1) == v7){ + map7to8[idx7][0] = idx; + if(den == 0) { /* divide check -- pick grey shades */ + cr = ((255.0/7.0)*v7)+0.5; + cg = cr; + cb = cr; + } + else { + num=17*15*(4*den+v7*2)/14; + cr=r*num/den; + cg=g*num/den; + cb=b*num/den; + } + map7[idx7].red = cr*0x0101; + map7[idx7].green = cg*0x0101; + map7[idx7].blue = cb*0x0101; + map7[idx7].pixel = idx7; + map7[idx7].flags = DoRed|DoGreen|DoBlue; + } + else + map7to8[idx7][1] = idx; + } + } + } + } +} + +/* + * Initialize and install the Inferno colormap as a private colormap for this + * application. Inferno gets the best colors here when it has the cursor focus. + */ +static void +initmap(Window w) +{ + XColor c; + XColor xcol; + ulong v; + int i, pix, ncmaps; + + if(xscreendepth <= 1) + return; + + if(xvis->class == TrueColor || xvis->class == DirectColor) { + int color_order_init = 0; + uint pp, p; + + for(i = 0; i < 256; i++) { + c = map[i]; + /* find out index into colormap for our RGB */ + if(!XAllocColor(xdisplay, xcmap, &c)) { + fprint(2, "emu: win-x11 can't alloc color\n"); + cleanexit(0); + } + + /* The pixel value returned from XGetPixel needs to + * be converted to RGB so we can call rgb2cmap() + * to translate between 24 bit X and our color. Unfortunately, + * the return value appears to be display server endian + * dependant. Therefore, we run some heuristics to later + * determine how to mask the int value correctly. + * Yeah, I know we can look at xvis->byte_order but + * some displays say MSB even though they run on LSB. + * Besides, this is more anal. + */ + + p = c.pixel; + pp = rgb2cmap((p>>16)&0xff,(p>>8)&0xff,p&0xff); + if(!color_order_init && (pp!=map[i].pixel)) { + /* check if endian is other way */ + pp = rgb2cmap(p&0xff,(p>>8)&0xff,(p>>16)&0xff); + if(pp!=map[i].pixel) { + fprint(2, "emu: win-x11 can't convert 24bit colors\n"); + cleanexit(0); + } + color_order_init = 1; + x24bitswap = 1; + } + + if(color_order_init) { + pp = rgb2cmap(p&0xff,(p>>8)&0xff,(p>>16)&0xff); + if(pp!=map[i].pixel) { + fprint(2, "emu: win-x11 can't convert 24bit colors\n"); + cleanexit(0); + } + /* no x11toinferno; later use rgb2cmap() */ + infernotox11[map[i].pixel] = c.pixel; + } + else if(pp!=map[i].pixel) { + fprint(2, "emu: win-x11 can't convert 24bit colors\n"); + cleanexit(0); + } + else { + /* no x11toinferno; later use rgb2cmap() */ + infernotox11[map[i].pixel] = c.pixel; + } + } + } + else if(xvis->class == PseudoColor) { + if(xtblbit == 0){ + xcmap = XCreateColormap(xdisplay, w, xvis, AllocAll); + XStoreColors(xdisplay, xcmap, map, 256); + for(i = 0; i < 256; i++) { + infernotox11[i] = i; + x11toinferno[i] = i; + } + } + else { + for(i = 0; i < 128; i++) { + c = map7[i]; + if(!XAllocColor(xdisplay, xcmap, &c)) { + fprint(2, "emu: win-x11 can't alloc colors in default map, don't use -7\n"); + cleanexit(0); + } + infernotox11[map7to8[i][0]] = c.pixel; + infernotox11[map7to8[i][1]] = c.pixel; + x11toinferno[c.pixel] = map7to8[i][0]; + } + } + } + else { + xtblbit = 0; + fprint(2, "emu: win-x11 unsupported visual class %d\n", xvis->class); + } + return; +} + +static void +xmapping(XEvent *e) +{ + XMappingEvent *xe; + + if(e->type != MappingNotify) + return; + xe = (XMappingEvent*)e; + if(modmap) + XFreeModifiermap(modmap); + modmap = XGetModifierMapping(xe->display); + if(modmap) + keypermod = modmap->max_keypermod; +} + + +/* + * Disable generation of GraphicsExpose/NoExpose events in the GC. + */ +static GC +creategc(Drawable d) +{ + XGCValues gcv; + + gcv.function = GXcopy; + gcv.graphics_exposures = False; + return XCreateGC(xdisplay, d, GCFunction|GCGraphicsExposures, &gcv); +} + +static void +xexpose(XEvent *e) +{ + IRectangle r; + XExposeEvent *xe; + + if(e->type != Expose) + return; + xe = (XExposeEvent*)e; + r.min.x = xe->x; + r.min.y = xe->y; + r.max.x = xe->x + xe->width; + r.max.y = xe->y + xe->height; + drawqlock(); + flushmemscreen(r); + drawqunlock(); +} + + +static void +xkeyboard(XEvent *e) +{ + int ind, n; + KeySym k; + Rune r; + unsigned int md; + char buf[1]; + + if(gkscanq) { + uchar ch = (KeyCode)e->xkey.keycode; + if(e->xany.type == KeyRelease) + ch |= 0x80; + qproduce(gkscanq, &ch, 1); + return; + } + + /* + * I tried using XtGetActionKeysym, but it didn't seem to + * do case conversion properly + * (at least, with Xterminal servers and R4 intrinsics) + */ + if(e->xany.type != KeyPress) + return; + + md = e->xkey.state; + ind = 0; + if(md & ShiftMask) + ind = 1; + + k = XKeycodeToKeysym(e->xany.display, (KeyCode)e->xkey.keycode, ind); + + /* May have to try unshifted version */ + if(k == NoSymbol && ind == 1) + k = XKeycodeToKeysym(e->xany.display, (KeyCode)e->xkey.keycode, 0); + + if(k == XK_Multi_key || k == NoSymbol) + return; + if(k&0xFF00){ + switch(k){ + case XK_BackSpace: + case XK_Tab: + case XK_Escape: + case XK_Delete: + case XK_KP_0: + case XK_KP_1: + case XK_KP_2: + case XK_KP_3: + case XK_KP_4: + case XK_KP_5: + case XK_KP_6: + case XK_KP_7: + case XK_KP_8: + case XK_KP_9: + case XK_KP_Divide: + case XK_KP_Multiply: + case XK_KP_Subtract: + case XK_KP_Add: + case XK_KP_Decimal: + k &= 0x7F; + break; + case XK_Linefeed: + k = '\r'; + break; + case XK_KP_Enter: + case XK_Return: + k = '\n'; + break; + case XK_Alt_L: + case XK_Alt_R: + case XK_Meta_L: + case XK_Meta_R: + k = Latin; + break; + case XK_Left: + case XK_KP_Left: + k = Left; + break; + case XK_Down: + case XK_KP_Down: + k = Down; + break; + case XK_Right: + case XK_KP_Right: + k = Right; + break; + case XK_Up: + case XK_KP_Up: + k = Up; + break; + case XK_Home: + case XK_KP_Home: + k = Home; + break; + case XK_End: + case XK_KP_End: + k = End; + break; + case XK_Page_Up: + case XK_KP_Page_Up: + k = Pgup; + break; + case XK_Page_Down: + case XK_KP_Page_Down: + k = Pgdown; + break; + default: /* not ISO-1 or tty control */ + return; + } + } + /* Compensate for servers that call a minus a hyphen */ + if(k == XK_hyphen) + k = XK_minus; + /* Do control mapping ourselves if translator doesn't */ + if(md & ControlMask) + k &= 0x9f; + if(k == '\t' && ind) + k = BackTab; + + if(md & Mod1Mask) + k = APP|(k&0xff); + if(k == NoSymbol) + return; + + gkbdputc(gkbdq, k); +} + +static void +xmouse(XEvent *e) +{ + int s, dbl; + XButtonEvent *be; + XMotionEvent *me; + XEvent motion; + Pointer m; + static ulong lastb, lastt; + + dbl = 0; + switch(e->type){ + case ButtonPress: + be = (XButtonEvent *)e; + m.x = be->x; + m.y = be->y; + s = be->state; + if(be->button == lastb && be->time - lastt < DblTime) + dbl = 1; + lastb = be->button; + lastt = be->time; + switch(be->button){ + case 1: + s |= Button1Mask; + break; + case 2: + s |= Button2Mask; + break; + case 3: + s |= Button3Mask; + break; + } + break; + case ButtonRelease: + be = (XButtonEvent *)e; + m.x = be->x; + m.y = be->y; + s = be->state; + switch(be->button){ + case 1: + s &= ~Button1Mask; + break; + case 2: + s &= ~Button2Mask; + break; + case 3: + s &= ~Button3Mask; + break; + } + break; + case MotionNotify: + me = (XMotionEvent *) e; + + /* remove excess MotionNotify events from queue and keep last one */ + while(XCheckTypedWindowEvent(xkmcon, xdrawable, MotionNotify, &motion) == True) + me = (XMotionEvent *) &motion; + + s = me->state; + m.x = me->x; + m.y = me->y; + break; + default: + return; + } + + m.b = 0; + if(s & Button1Mask) + m.b |= 1; + if(s & Button2Mask) + m.b |= 2; + if(s & Button3Mask) + m.b |= 4; + if(dbl) + m.b |= 1<<4; + + m.modify = 1; + mouseproduce(m); +} + diff --git a/emu/port/win-x11a.c b/emu/port/win-x11a.c new file mode 100644 index 00000000..424a09a2 --- /dev/null +++ b/emu/port/win-x11a.c @@ -0,0 +1,1620 @@ +/* + * This implementation of the screen functions for X11 uses the + * portable implementation of the Inferno drawing operations (libmemdraw) + * to do the work, then has flushmemscreen copy the result to the X11 display. + * Thus it potentially supports all colour depths but with a possible + * performance penalty (although it tries to use the X11 shared memory extension + * to copy the result to the screen, which might reduce the latter). + * + * CraigN + */ + +#define _GNU_SOURCE 1 +#define XTHREADS +#include "dat.h" +#include "fns.h" +#undef log2 +#include <draw.h> +#include "cursor.h" +#include "keyboard.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#define Colormap XColormap +#define Cursor XCursor +#define Display XDisplay +#define Drawable XDrawable +#define Font XFont +#define GC XGC +#define Point XPoint +#define Rectangle XRectangle +#define Screen XScreen +#define Visual XVisual +#define Window XWindow + +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <X11/Xutil.h> +#include <X11/keysym.h> +#include <X11/extensions/XShm.h> + +#include "keysym2ucs.h" + +#undef Colormap +#undef Cursor +#undef Display +#undef XDrawable +#undef Font +#undef GC +#undef Point +#undef Rectangle +#undef Screen +#undef Visual +#undef Window + +#include <sys/ipc.h> +#include <sys/shm.h> + +static int displaydepth; +extern ulong displaychan; + +enum +{ + DblTime = 300 /* double click time in msec */ +}; + +/* screen data .... */ +static uchar* gscreendata; +static uchar* xscreendata; + +XColor map[256]; /* Inferno colormap array */ +XColor mapr[256]; /* Inferno red colormap array */ +XColor mapg[256]; /* Inferno green colormap array */ +XColor mapb[256]; /* Inferno blue colormap array */ +XColor map7[128]; /* Inferno colormap array */ +uchar map7to8[128][2]; + +/* for copy/paste, lifted from plan9ports via drawterm */ +static Atom clipboard; +static Atom utf8string; +static Atom targets; +static Atom text; +static Atom compoundtext; + +static Atom cursorchange; + +static XColormap xcmap; /* Default shared colormap */ +static int infernotox11[256]; /* Values for mapping between */ +static int infernortox11[256]; /* Values for mapping between */ +static int infernogtox11[256]; /* Values for mapping between */ +static int infernobtox11[256]; /* Values for mapping between */ +static int triedscreen; +static XDrawable xdrawable; +static void xexpose(XEvent*); +static void xmouse(XEvent*); +static void xkeyboard(XEvent*); +static void xsetcursor(XEvent*); +static void xkbdproc(void*); +static void xdestroy(XEvent*); +static void xselect(XEvent*, XDisplay*); +static void xproc(void*); +static void xinitscreen(int, int, ulong, ulong*, int*); +static void initxcmap(XWindow); +static XGC creategc(XDrawable); +static void graphicsgmap(XColor*, int); +static void graphicscmap(XColor*); +static void graphicsrgbmap(XColor*, XColor*, XColor*); + +static int xscreendepth; +static XDisplay* xdisplay; /* used holding draw lock */ +static XDisplay* xmcon; /* used only in xproc */ +static XDisplay* xkbdcon; /* used only in xkbdproc */ +static XDisplay* xsnarfcon; /* used holding clip.lk */ +static XVisual *xvis; +static XGC xgc; +static XImage *img; +static int is_shm; + +static int putsnarf, assertsnarf; +char *gkscanid = "emu_x11"; + +/* + * The documentation for the XSHM extension implies that if the server + * supports XSHM but is not the local machine, the XShm calls will + * return False; but this turns out not to be the case. Instead, the + * server throws a BadAccess error. So, we need to catch X errors + * around all of our XSHM calls, sigh. + */ +static int shm_got_x_error = 0; +static XErrorHandler old_handler = 0; +static XErrorHandler old_io_handler = 0; + +static int +shm_ehandler(XDisplay *dpy, XErrorEvent *error) +{ + shm_got_x_error = 1; + return 0; +} + +static void +clean_errhandlers(void) +{ + /* remove X11 error handler(s) */ + if(old_handler) + XSetErrorHandler(old_handler); + old_handler = 0; + if(old_io_handler) + XSetErrorHandler(old_io_handler); + old_io_handler = 0; +} + +static int +makesharedfb(void) +{ + XShmSegmentInfo *shminfo; + + shminfo = malloc(sizeof(XShmSegmentInfo)); + if(shminfo == nil) { + fprint(2, "emu: cannot allocate XShmSegmentInfo\n"); + cleanexit(0); + } + + /* setup to catch X11 error(s) */ + XSync(xdisplay, 0); + shm_got_x_error = 0; + if(old_handler != shm_ehandler) + old_handler = XSetErrorHandler(shm_ehandler); + if(old_io_handler != shm_ehandler) + old_io_handler = XSetErrorHandler(shm_ehandler); + + img = XShmCreateImage(xdisplay, xvis, xscreendepth, ZPixmap, + NULL, shminfo, Xsize, Ysize); + XSync(xdisplay, 0); + + /* did we get an X11 error? if so then try without shm */ + if(shm_got_x_error) { + free(shminfo); + shminfo = NULL; + clean_errhandlers(); + return 0; + } + + if(img == nil) { + fprint(2, "emu: cannot allocate virtual screen buffer\n"); + cleanexit(0); + } + + shminfo->shmid = shmget(IPC_PRIVATE, img->bytes_per_line * img->height, IPC_CREAT|0777); + shminfo->shmaddr = img->data = shmat(shminfo->shmid, 0, 0); + shminfo->readOnly = True; + + if(!XShmAttach(xdisplay, shminfo)) { + fprint(2, "emu: cannot allocate virtual screen buffer\n"); + cleanexit(0); + } + XSync(xdisplay, 0); + + /* + * Delete the shared segment right now; the segment + * won't actually go away until both the client and + * server have deleted it. The server will delete it + * as soon as the client disconnects, so we might as + * well delete our side now as later. + */ + shmctl(shminfo->shmid, IPC_RMID, 0); + + /* did we get an X11 error? if so then try without shm */ + if(shm_got_x_error) { + XDestroyImage(img); + XSync(xdisplay, 0); + free(shminfo); + shminfo = NULL; + clean_errhandlers(); + return 0; + } + + gscreendata = malloc(Xsize * Ysize * (displaydepth >> 3)); + if(gscreendata == nil) { + fprint(2, "emu: cannot allocate screen buffer (%dx%dx%d)\n", Xsize, Ysize, displaydepth); + cleanexit(0); + } + xscreendata = (uchar*)img->data; + + clean_errhandlers(); + return 1; +} + +uchar* +attachscreen(Rectangle *r, ulong *chan, int *d, int *width, int *softscreen) +{ + int depth; + + Xsize &= ~0x3; /* ensure multiple of 4 */ + + r->min.x = 0; + r->min.y = 0; + r->max.x = Xsize; + r->max.y = Ysize; + + if(!triedscreen){ + xinitscreen(Xsize, Ysize, displaychan, chan, d); + /* + * moved xproc from here to end since it could cause an expose event and + * hence a flushmemscreen before xscreendata is initialized + */ + } + else{ + *chan = displaychan; + *d = displaydepth; + } + + *width = (Xsize/4)*(*d/8); + *softscreen = 1; + displaychan = *chan; + displaydepth = *d; + + /* check for X Shared Memory Extension */ + is_shm = XShmQueryExtension(xdisplay); + + if(!is_shm || !makesharedfb()){ + is_shm = 0; + depth = xscreendepth; + if(depth == 24) + depth = 32; + + /* allocate virtual screen */ + gscreendata = malloc(Xsize * Ysize * (displaydepth >> 3)); + xscreendata = malloc(Xsize * Ysize * (depth >> 3)); + if(gscreendata == nil || xscreendata == nil) { + fprint(2, "emu: can not allocate virtual screen buffer (%dx%dx%d[%d])\n", Xsize, Ysize, displaydepth, depth); + return 0; + } + img = XCreateImage(xdisplay, xvis, xscreendepth, ZPixmap, 0, + (char*)xscreendata, Xsize, Ysize, 8, Xsize * (depth >> 3)); + if(img == nil) { + fprint(2, "emu: can not allocate virtual screen buffer (%dx%dx%d)\n", Xsize, Ysize, depth); + return 0; + } + + } + + if(!triedscreen){ + triedscreen = 1; + kproc("xproc", xproc, xmcon, 0); + kproc("xkbdproc", xkbdproc, xkbdcon, KPX11); /* silly stack size for bloated X11 */ + } + + return gscreendata; +} + +static void +copy32to32(Rectangle r) +{ + int dx, width; + uchar *p, *ep, *cp; + u32int v, w, *dp, *wp, *edp, *lp; + + width = Dx(r); + dx = Xsize - width; + dp = (u32int*)(gscreendata + (r.min.y * Xsize + r.min.x) * 4); + wp = (u32int*)(xscreendata + (r.min.y * Xsize + r.min.x) * 4); + edp = (u32int*)(gscreendata + (r.max.y * Xsize + r.max.x) * 4); + while(dp < edp) { + lp = dp + width; + while(dp < lp){ + v = *dp++; + w = infernortox11[(v>>16)&0xff]<<16|infernogtox11[(v>>8)&0xff]<<8|infernobtox11[(v>>0)&0xff]<<0; + *wp++ = w; + } + dp += dx; + wp += dx; + } +} + +static void +copy8to32(Rectangle r) +{ + int dx, width; + uchar *p, *ep, *lp; + u32int *wp; + + width = Dx(r); + dx = Xsize - width; + p = gscreendata + r.min.y * Xsize + r.min.x; + wp = (u32int *)(xscreendata + (r.min.y * Xsize + r.min.x) * 4); + ep = gscreendata + r.max.y * Xsize + r.max.x; + while(p < ep) { + lp = p + width; + while(p < lp) + *wp++ = infernotox11[*p++]; + p += dx; + wp += dx; + } +} + +static void +copy8to24(Rectangle r) +{ + int dx, width, v; + uchar *p, *cp, *ep, *lp; + + width = Dx(r); + dx = Xsize - width; + p = gscreendata + r.min.y * Xsize + r.min.x; + cp = xscreendata + (r.min.y * Xsize + r.min.x) * 3; + ep = gscreendata + r.max.y * Xsize + r.max.x; + while(p < ep) { + lp = p + width; + while(p < lp){ + v = infernotox11[*p++]; + cp[0] = (v>>16)&0xff; + cp[1] = (v>>8)&0xff; + cp[2] = (v>>0)&0xff; + cp += 3; + } + p += dx; + cp += 3*dx; + } +} + +static void +copy8to16(Rectangle r) +{ + int dx, width; + uchar *p, *ep, *lp; + u16int *sp; + + width = Dx(r); + dx = Xsize - width; + p = gscreendata + r.min.y * Xsize + r.min.x; + sp = (unsigned short *)(xscreendata + (r.min.y * Xsize + r.min.x) * 2); + ep = gscreendata + r.max.y * Xsize + r.max.x; + while(p < ep) { + lp = p + width; + while(p < lp) + *sp++ = infernotox11[*p++]; + p += dx; + sp += dx; + } +} + +static void +copy8to8(Rectangle r) +{ + int dx, width; + uchar *p, *cp, *ep, *lp; + + width = Dx(r); + dx = Xsize - width; + p = gscreendata + r.min.y * Xsize + r.min.x; + cp = xscreendata + r.min.y * Xsize + r.min.x; + ep = gscreendata + r.max.y * Xsize + r.max.x; + while(p < ep) { + lp = p + width; + while(p < lp) + *cp++ = infernotox11[*p++]; + p += dx; + cp += dx; + } +} + +static void +copy8topixel(Rectangle r) +{ + int x, y; + uchar *p; + + /* mainly for 4-bit greyscale */ + for (y = r.min.y; y < r.max.y; y++) { + x = r.min.x; + p = gscreendata + y * Xsize + x; + while (x < r.max.x) + XPutPixel(img, x++, y, infernotox11[*p++]); + } +} + +void +flushmemscreen(Rectangle r) +{ + char chanbuf[16]; + + // Clip to screen + if(r.min.x < 0) + r.min.x = 0; + if(r.min.y < 0) + r.min.y = 0; + if(r.max.x >= Xsize) + r.max.x = Xsize - 1; + if(r.max.y >= Ysize) + r.max.y = Ysize - 1; + if(r.max.x <= r.min.x || r.max.y <= r.min.y) + return; + + switch(displaydepth){ + case 32: + copy32to32(r); + break; + case 8: + switch(xscreendepth){ + case 24: + /* copy8to24(r); */ /* doesn't happen? */ + /* break */ + case 32: + copy8to32(r); + break; + case 16: + copy8to16(r); + break; + case 8: + copy8to8(r); + break; + default: + copy8topixel(r); + break; + } + break; + default: + fprint(2, "emu: bad display depth %d chan %s xscreendepth %d\n", displaydepth, + chantostr(chanbuf, displaychan), xscreendepth); + cleanexit(0); + } + + XLockDisplay(xdisplay); + /* Display image on X11 */ + if(is_shm) + XShmPutImage(xdisplay, xdrawable, xgc, img, r.min.x, r.min.y, r.min.x, r.min.y, Dx(r), Dy(r), 0); + else + XPutImage(xdisplay, xdrawable, xgc, img, r.min.x, r.min.y, r.min.x, r.min.y, Dx(r), Dy(r)); + XSync(xdisplay, 0); + XUnlockDisplay(xdisplay); +} + +static int +revbyte(int b) +{ + int r; + + r = 0; + r |= (b&0x01) << 7; + r |= (b&0x02) << 5; + r |= (b&0x04) << 3; + r |= (b&0x08) << 1; + r |= (b&0x10) >> 1; + r |= (b&0x20) >> 3; + r |= (b&0x40) >> 5; + r |= (b&0x80) >> 7; + return r; +} + +void +setpointer(int x, int y) +{ + drawqlock(); + XLockDisplay(xdisplay); + XWarpPointer(xdisplay, None, xdrawable, 0, 0, 0, 0, x, y); + XFlush(xdisplay); + XUnlockDisplay(xdisplay); + drawqunlock(); +} + +static void +xkbdproc(void *arg) +{ + XEvent event; + XDisplay *xd; + + xd = arg; + + /* BEWARE: the value of up is not defined for this proc on some systems */ + + XLockDisplay(xd); /* should be ours alone */ + XSelectInput(xd, xdrawable, KeyPressMask | KeyReleaseMask); + for(;;){ + XNextEvent(xd, &event); + xkeyboard(&event); + xsetcursor(&event); + } +} + +static void +xproc(void *arg) +{ + ulong mask; + XEvent event; + XDisplay *xd; + + closepgrp(up->env->pgrp); + closefgrp(up->env->fgrp); + closeegrp(up->env->egrp); + closesigs(up->env->sigs); + + xd = arg; + mask = ButtonPressMask| + ButtonReleaseMask| + PointerMotionMask| + Button1MotionMask| + Button2MotionMask| + Button3MotionMask| + Button4MotionMask| + Button5MotionMask| + ExposureMask| + StructureNotifyMask; + + XLockDisplay(xd); /* should be ours alone */ + XSelectInput(xd, xdrawable, mask); + for(;;){ + XNextEvent(xd, &event); + xselect(&event, xd); + xmouse(&event); + xexpose(&event); + xdestroy(&event); + } +} + +/* + * this crud is here because X11 can put huge amount of data + * on the stack during keyboard translation and cursor changing(!). + * we do both in a dedicated process with lots of stack, perhaps even enough. + */ + +enum { + CursorSize= 32 /* biggest cursor size */ +}; + +typedef struct ICursor ICursor; +struct ICursor { + int inuse; + int modify; + int hotx; + int hoty; + int w; + int h; + uchar src[(CursorSize/8)*CursorSize]; /* image and mask bitmaps */ + uchar mask[(CursorSize/8)*CursorSize]; +}; +static ICursor icursor; + +static void +xcurslock(void) +{ + while(_tas(&icursor.inuse) != 0) + osyield(); +} + +static void +xcursunlock(void) +{ + icursor.inuse = 0; +} + +static void +xcursnotify(void) +{ + XClientMessageEvent e; + + memset(&e, 0, sizeof e); + e.type = ClientMessage; + e.window = xdrawable; + e.message_type = cursorchange; + e.format = 8; + XLockDisplay(xdisplay); + XSendEvent(xdisplay, xdrawable, True, KeyPressMask, (XEvent*)&e); + XFlush(xdisplay); + XUnlockDisplay(xdisplay); +} + +void +drawcursor(Drawcursor* c) +{ + uchar *bs, *bc, *ps, *pm; + int i, j, w, h, bpl; + + if(c->data == nil){ + drawqlock(); + if(icursor.h != 0){ + xcurslock(); + icursor.h = 0; + icursor.modify = 1; + xcursunlock(); + } + xcursnotify(); + drawqunlock(); + return; + } + + drawqlock(); + xcurslock(); + icursor.modify = 0; /* xsetcursor will now ignore it */ + xcursunlock(); + + h = (c->maxy-c->miny)/2; /* image, then mask */ + if(h > CursorSize) + h = CursorSize; + bpl = bytesperline(Rect(c->minx, c->miny, c->maxx, c->maxy), 1); + w = bpl; + if(w > CursorSize/8) + w = CursorSize/8; + + ps = icursor.src; + pm = icursor.mask; + bc = c->data; + bs = c->data + h*bpl; + for(i = 0; i < h; i++){ + for(j = 0; j < bpl && j < w; j++) { + *ps++ = revbyte(bs[j]); + *pm++ = revbyte(bs[j] | bc[j]); + } + bs += bpl; + bc += bpl; + } + icursor.h = h; + icursor.w = w*8; + icursor.hotx = c->hotx; + icursor.hoty = c->hoty; + icursor.modify = 1; + xcursnotify(); + drawqunlock(); +} + +static void +xsetcursor(XEvent *e) +{ + ICursor ic; + XCursor xc; + XColor fg, bg; + Pixmap xsrc, xmask; + static XCursor xcursor; + + if(e->type != ClientMessage || !e->xclient.send_event || e->xclient.message_type != cursorchange) + return; + + xcurslock(); + if(icursor.modify == 0){ + xcursunlock(); + return; + } + icursor.modify = 0; + if(icursor.h == 0){ + xcursunlock(); + /* set the default system cursor */ + if(xcursor != 0) { + XFreeCursor(xkbdcon, xcursor); + xcursor = 0; + } + XUndefineCursor(xkbdcon, xdrawable); + XFlush(xkbdcon); + return; + } + ic = icursor; + xcursunlock(); + + xsrc = XCreateBitmapFromData(xkbdcon, xdrawable, (char*)ic.src, ic.w, ic.h); + xmask = XCreateBitmapFromData(xkbdcon, xdrawable, (char*)ic.mask, ic.w, ic.h); + + fg = map[0]; + bg = map[255]; + fg.pixel = infernotox11[0]; + bg.pixel = infernotox11[255]; + xc = XCreatePixmapCursor(xkbdcon, xsrc, xmask, &fg, &bg, -ic.hotx, -ic.hoty); + if(xc != 0) { + XDefineCursor(xkbdcon, xdrawable, xc); + if(xcursor != 0) + XFreeCursor(xkbdcon, xcursor); + xcursor = xc; + } + XFreePixmap(xkbdcon, xsrc); + XFreePixmap(xkbdcon, xmask); + XFlush(xkbdcon); +} + +typedef struct Mg Mg; +struct Mg +{ + int code; + int bit; + int len; + ulong mask; +}; + +static int +maskx(Mg* g, int code, ulong mask) +{ + int i; + + for(i=0; i<32; i++) + if(mask & (1<<i)) + break; + if(i == 32) + return 0; + g->code = code; + g->bit = i; + g->mask = mask; + for(g->len = 0; i<32 && (mask & (1<<i))!=0; i++) + g->len++; + return 1; +} + +/* + * for a given depth, we need to check the available formats + * to find how many actual bits are used per pixel. + */ +static int +xactualdepth(int screenno, int depth) +{ + XPixmapFormatValues *pfmt; + int i, n; + + pfmt = XListPixmapFormats(xdisplay, &n); + for(i=0; i<n; i++) + if(pfmt[i].depth == depth) + return pfmt[i].bits_per_pixel; + return -1; +} + +static int +xtruevisual(int screenno, int reqdepth, XVisualInfo *vi, ulong *chan) +{ + XVisual *xv; + Mg r, g, b; + int pad, d; + ulong c; + char buf[30]; + + if(XMatchVisualInfo(xdisplay, screenno, reqdepth, TrueColor, vi) || + XMatchVisualInfo(xdisplay, screenno, reqdepth, DirectColor, vi)){ + xv = vi->visual; + if(maskx(&r, CRed, xv->red_mask) && + maskx(&g, CGreen, xv->green_mask) && + maskx(&b, CBlue, xv->blue_mask)){ + d = xactualdepth(screenno, reqdepth); + if(d < 0) + return 0; + pad = d - (r.len + g.len + b.len); + if(0){ + fprint(2, "r: %8.8lux %d %d\ng: %8.8lux %d %d\nb: %8.8lux %d %d\n", + xv->red_mask, r.bit, r.len, xv->green_mask, g.bit, g.len, xv->blue_mask, b.bit, b.len); + } + if(r.bit > b.bit) + c = CHAN3(CRed, r.len, CGreen, g.len, CBlue, b.len); + else + c = CHAN3(CBlue, b.len, CGreen, g.len, CRed, r.len); + if(pad > 0) + c |= CHAN1(CIgnore, pad) << 24; + *chan = c; + xscreendepth = reqdepth; + if(0) + fprint(2, "chan=%s reqdepth=%d bits=%d\n", chantostr(buf, c), reqdepth, d); + return 1; + } + } + return 0; +} + +static int +xmapvisual(int screenno, XVisualInfo *vi, ulong *chan) +{ + if(XMatchVisualInfo(xdisplay, screenno, 8, PseudoColor, vi) || + XMatchVisualInfo(xdisplay, screenno, 8, StaticColor, vi)){ + *chan = CMAP8; + xscreendepth = 8; + return 1; + } + return 0; +} + +static void +xinitscreen(int xsize, int ysize, ulong reqchan, ulong *chan, int *d) +{ + char *argv[2]; + char *dispname; + XWindow rootwin; + XWMHints hints; + XVisualInfo xvi; + XScreen *screen; + int rootscreennum; + XTextProperty name; + XClassHint classhints; + XSizeHints normalhints; + XSetWindowAttributes attrs; + char buf[30]; + int i; + + xdrawable = 0; + + dispname = getenv("DISPLAY"); + if(dispname == nil) + dispname = "not set"; + XInitThreads(); + xdisplay = XOpenDisplay(NULL); + if(xdisplay == 0){ + fprint(2, "emu: win-x11 open %r, DISPLAY is %s\n", dispname); + cleanexit(0); + } + + rootscreennum = DefaultScreen(xdisplay); + rootwin = DefaultRootWindow(xdisplay); + xscreendepth = DefaultDepth(xdisplay, rootscreennum); + xvis = DefaultVisual(xdisplay, rootscreennum); + screen = DefaultScreenOfDisplay(xdisplay); + xcmap = DefaultColormapOfScreen(screen); + + if(reqchan == 0){ + *chan = 0; + if(xscreendepth <= 16){ /* try for better colour */ + xtruevisual(rootscreennum, 16, &xvi, chan) || + xtruevisual(rootscreennum, 15, &xvi, chan) || + xtruevisual(rootscreennum, 24, &xvi, chan) || + xmapvisual(rootscreennum, &xvi, chan); + }else{ + xtruevisual(rootscreennum, xscreendepth, &xvi, chan) || + xtruevisual(rootscreennum, 24, &xvi, chan); + } + if(*chan == 0){ + fprint(2, "emu: could not find suitable x11 pixel format for depth %d on this display\n", xscreendepth); + cleanexit(0); + } + reqchan = *chan; + *d = chantodepth(reqchan); + xvis = xvi.visual; + }else{ + *chan = reqchan; /* not every channel description will work */ + *d = chantodepth(reqchan); + if(*d != xactualdepth(rootscreennum, *d)){ + fprint(2, "emu: current x11 display configuration does not support %s (depth %d) directly\n", + chantostr(buf, reqchan), *d); + cleanexit(0); + } + } + + if(xvis->class != StaticColor) { + if(TYPE(*chan) == CGrey) + graphicsgmap(map, NBITS(reqchan)); + else{ + graphicscmap(map); + graphicsrgbmap(mapr, mapg, mapb); + } + initxcmap(rootwin); + } + + memset(&attrs, 0, sizeof(attrs)); + attrs.colormap = xcmap; + attrs.background_pixel = 0; + attrs.border_pixel = 0; + /* attrs.override_redirect = 1;*/ /* WM leave me alone! |CWOverrideRedirect */ + xdrawable = XCreateWindow(xdisplay, rootwin, 0, 0, xsize, ysize, 0, xscreendepth, + InputOutput, xvis, CWBackPixel|CWBorderPixel|CWColormap, &attrs); + + /* + * set up property as required by ICCCM + */ + memset(&name, 0, sizeof(name)); + name.value = (uchar*)"inferno"; + name.encoding = XA_STRING; + name.format = 8; + name.nitems = strlen((char*)name.value); + + memset(&normalhints, 0, sizeof(normalhints)); + normalhints.flags = USSize|PMaxSize; + normalhints.max_width = normalhints.width = xsize; + normalhints.max_height = normalhints.height = ysize; + hints.flags = InputHint|StateHint; + hints.input = 1; + hints.initial_state = NormalState; + + memset(&classhints, 0, sizeof(classhints)); + classhints.res_name = "inferno"; + classhints.res_class = "Inferno"; + argv[0] = "inferno"; + argv[1] = nil; + XSetWMProperties(xdisplay, xdrawable, + &name, /* XA_WM_NAME property for ICCCM */ + &name, /* XA_WM_ICON_NAME */ + argv, /* XA_WM_COMMAND */ + 1, /* argc */ + &normalhints, /* XA_WM_NORMAL_HINTS */ + &hints, /* XA_WM_HINTS */ + &classhints); /* XA_WM_CLASS */ + + XMapWindow(xdisplay, xdrawable); + XFlush(xdisplay); + + xgc = creategc(xdrawable); + + xmcon = XOpenDisplay(NULL); + xsnarfcon = XOpenDisplay(NULL); + xkbdcon = XOpenDisplay(NULL); + if(xmcon == 0 || xsnarfcon == 0 || xkbdcon == 0){ + fprint(2, "emu: win-x11 open %r, DISPLAY is %s\n", dispname); + cleanexit(0); + } + + clipboard = XInternAtom(xmcon, "CLIPBOARD", False); + utf8string = XInternAtom(xmcon, "UTF8_STRING", False); + targets = XInternAtom(xmcon, "TARGETS", False); + text = XInternAtom(xmcon, "TEXT", False); + compoundtext = XInternAtom(xmcon, "COMPOUND_TEXT", False); + + cursorchange = XInternAtom(xkbdcon, "TheCursorHasChanged", False); + +} + +static void +graphicsgmap(XColor *map, int d) +{ + int i, j, s, m, p; + + s = 8-d; + m = 1; + while(--d >= 0) + m *= 2; + m = 255/(m-1); + for(i=0; i < 256; i++){ + j = (i>>s)*m; + p = 255-i; + map[p].red = map[p].green = map[p].blue = (255-j)*0x0101; + map[p].pixel = p; + map[p].flags = DoRed|DoGreen|DoBlue; + } +} + +static void +graphicscmap(XColor *map) +{ + int r, g, b, cr, cg, cb, v, num, den, idx, v7, idx7; + + for(r=0; r!=4; r++) { + for(g = 0; g != 4; g++) { + for(b = 0; b!=4; b++) { + for(v = 0; v!=4; v++) { + den=r; + if(g > den) + den=g; + if(b > den) + den=b; + /* divide check -- pick grey shades */ + if(den==0) + cr=cg=cb=v*17; + else { + num=17*(4*den+v); + cr=r*num/den; + cg=g*num/den; + cb=b*num/den; + } + idx = r*64 + v*16 + ((g*4 + b + v - r) & 15); + /* was idx = 255 - idx; */ + map[idx].red = cr*0x0101; + map[idx].green = cg*0x0101; + map[idx].blue = cb*0x0101; + map[idx].pixel = idx; + map[idx].flags = DoRed|DoGreen|DoBlue; + + v7 = v >> 1; + idx7 = r*32 + v7*16 + g*4 + b; + if((v & 1) == v7){ + map7to8[idx7][0] = idx; + if(den == 0) { /* divide check -- pick grey shades */ + cr = ((255.0/7.0)*v7)+0.5; + cg = cr; + cb = cr; + } + else { + num=17*15*(4*den+v7*2)/14; + cr=r*num/den; + cg=g*num/den; + cb=b*num/den; + } + map7[idx7].red = cr*0x0101; + map7[idx7].green = cg*0x0101; + map7[idx7].blue = cb*0x0101; + map7[idx7].pixel = idx7; + map7[idx7].flags = DoRed|DoGreen|DoBlue; + } + else + map7to8[idx7][1] = idx; + } + } + } + } +} + +static void +graphicsrgbmap(XColor *mapr, XColor *mapg, XColor *mapb) +{ + int i; + + memset(mapr, 0, 256*sizeof(XColor)); + memset(mapg, 0, 256*sizeof(XColor)); + memset(mapb, 0, 256*sizeof(XColor)); + for(i=0; i < 256; i++){ + mapr[i].red = mapg[i].green = mapb[i].blue = i*0x0101; + mapr[i].pixel = mapg[i].pixel = mapb[i].pixel = i; + mapr[i].flags = mapg[i].flags = mapb[i].flags = DoRed|DoGreen|DoBlue; + } +} + +/* + * Initialize and install the Inferno colormap as a private colormap for this + * application. Inferno gets the best colors here when it has the cursor focus. + */ +static void +initxcmap(XWindow w) +{ + XColor c; + int i; + + if(xscreendepth <= 1) + return; + + switch(xvis->class){ + case TrueColor: + case DirectColor: + for(i = 0; i < 256; i++) { + c = map[i]; + /* find index into colormap for our RGB */ + if(!XAllocColor(xdisplay, xcmap, &c)) { + fprint(2, "emu: win-x11 can't alloc color\n"); + cleanexit(0); + } + infernotox11[map[i].pixel] = c.pixel; + if(xscreendepth >= 24){ + c = mapr[i]; + XAllocColor(xdisplay, xcmap, &c); + infernortox11[i] = (c.pixel>>16)&0xff; + c = mapg[i]; + XAllocColor(xdisplay, xcmap, &c); + infernogtox11[i] = (c.pixel>>8)&0xff; + c = mapb[i]; + XAllocColor(xdisplay, xcmap, &c); + infernobtox11[i] = (c.pixel>>0)&0xff; + } + } +if(0){int i, j; for(i=0;i<256; i+=16){print("%3d", i); for(j=i; j<i+16; j++)print(" %2.2ux/%2.2ux/%2.2ux", infernortox11[j], infernogtox11[j],infernobtox11[j]); print("\n");}} + /* TO DO: if the map(s) used give the identity map, don't use the map during copy */ + break; + + case PseudoColor: + if(xtblbit == 0){ + xcmap = XCreateColormap(xdisplay, w, xvis, AllocAll); + XStoreColors(xdisplay, xcmap, map, 256); + for(i = 0; i < 256; i++) + infernotox11[i] = i; + /* TO DO: the map is the identity, so don't need the map in copy */ + } else { + for(i = 0; i < 128; i++) { + c = map7[i]; + if(!XAllocColor(xdisplay, xcmap, &c)) { + fprint(2, "emu: win-x11 can't alloc colors in default map, don't use -7\n"); + cleanexit(0); + } + infernotox11[map7to8[i][0]] = c.pixel; + infernotox11[map7to8[i][1]] = c.pixel; + } + } + break; + + default: + xtblbit = 0; + fprint(2, "emu: win-x11 unsupported visual class %d\n", xvis->class); + break; + } +} + +static void +xdestroy(XEvent *e) +{ + XDestroyWindowEvent *xe; + if(e->type != DestroyNotify) + return; + xe = (XDestroyWindowEvent*)e; + if(xe->window == xdrawable) + cleanexit(0); +} + +/* + * Disable generation of GraphicsExpose/NoExpose events in the XGC. + */ +static XGC +creategc(XDrawable d) +{ + XGCValues gcv; + + gcv.function = GXcopy; + gcv.graphics_exposures = False; + return XCreateGC(xdisplay, d, GCFunction|GCGraphicsExposures, &gcv); +} + +static void +xexpose(XEvent *e) +{ + Rectangle r; + XExposeEvent *xe; + + if(e->type != Expose) + return; + xe = (XExposeEvent*)e; + r.min.x = xe->x; + r.min.y = xe->y; + r.max.x = xe->x + xe->width; + r.max.y = xe->y + xe->height; + drawqlock(); + flushmemscreen(r); + drawqunlock(); +} + +static void +xkeyboard(XEvent *e) +{ + int ind, md; + KeySym k; + + if(gkscanq != nil && (e->type == KeyPress || e->type == KeyRelease)){ + uchar ch = e->xkey.keycode; + if(e->xany.type == KeyRelease) + ch |= 0x80; + qproduce(gkscanq, &ch, 1); + return; + } + + /* + * I tried using XtGetActionKeysym, but it didn't seem to + * do case conversion properly + * (at least, with Xterminal servers and R4 intrinsics) + */ + if(e->xany.type != KeyPress) + return; + + md = e->xkey.state; + ind = 0; + if(md & ShiftMask) + ind = 1; + if(0){ + k = XKeycodeToKeysym(e->xany.display, (KeyCode)e->xkey.keycode, ind); + + /* May have to try unshifted version */ + if(k == NoSymbol && ind == 1) + k = XKeycodeToKeysym(e->xany.display, (KeyCode)e->xkey.keycode, 0); + }else + XLookupString((XKeyEvent*)e, NULL, 0, &k, NULL); + + if(k == XK_Multi_key || k == NoSymbol) + return; + if(k&0xFF00){ + switch(k){ + case XK_BackSpace: + case XK_Tab: + case XK_Escape: + case XK_Delete: + case XK_KP_0: + case XK_KP_1: + case XK_KP_2: + case XK_KP_3: + case XK_KP_4: + case XK_KP_5: + case XK_KP_6: + case XK_KP_7: + case XK_KP_8: + case XK_KP_9: + case XK_KP_Divide: + case XK_KP_Multiply: + case XK_KP_Subtract: + case XK_KP_Add: + case XK_KP_Decimal: + k &= 0x7F; + break; + case XK_Linefeed: + k = '\r'; + break; + case XK_KP_Space: + k = ' '; + break; +// case XK_Home: +// case XK_KP_Home: +// k = Khome; +// break; + case XK_Left: + case XK_KP_Left: + k = Left; + break; + case XK_Up: + case XK_KP_Up: + k = Up; + break; + case XK_Down: + case XK_KP_Down: + k = Down; + break; + case XK_Right: + case XK_KP_Right: + k = Right; + break; +// case XK_Page_Down: +// case XK_KP_Page_Down: +// k = Kpgdown; +// break; + case XK_End: + case XK_KP_End: + k = End; + break; +// case XK_Page_Up: +// case XK_KP_Page_Up: +// k = Kpgup; +// break; +// case XK_Insert: +// case XK_KP_Insert: +// k = Kins; +// break; + case XK_KP_Enter: + case XK_Return: + k = '\n'; + break; + case XK_Alt_L: + case XK_Alt_R: + k = Latin; + break; + case XK_Shift_L: + case XK_Shift_R: + case XK_Control_L: + case XK_Control_R: + case XK_Caps_Lock: + case XK_Shift_Lock: + + case XK_Meta_L: + case XK_Meta_R: + case XK_Super_L: + case XK_Super_R: + case XK_Hyper_L: + case XK_Hyper_R: + return; + default: /* not ISO-1 or tty control */ + if(k>0xff){ + k = keysym2ucs(k); /* supplied by X */ + if(k == -1) + return; + } + break; + } + } + + /* Compensate for servers that call a minus a hyphen */ + if(k == XK_hyphen) + k = XK_minus; + /* Do control mapping ourselves if translator doesn't */ + if(md & ControlMask) + k &= 0x9f; + if(0){ + if(k == '\t' && ind) + k = BackTab; + + if(md & Mod1Mask) + k = APP|(k&0xff); + } + if(k == NoSymbol) + return; + + gkbdputc(gkbdq, k); +} + +static void +xmouse(XEvent *e) +{ + int s, dbl; + XButtonEvent *be; + XMotionEvent *me; + XEvent motion; + int x, y, b; + static ulong lastb, lastt; + + if(putsnarf != assertsnarf){ + assertsnarf = putsnarf; + XSetSelectionOwner(xmcon, XA_PRIMARY, xdrawable, CurrentTime); + if(clipboard != None) + XSetSelectionOwner(xmcon, clipboard, xdrawable, CurrentTime); + XFlush(xmcon); + } + + dbl = 0; + switch(e->type){ + case ButtonPress: + be = (XButtonEvent *)e; + /* + * Fake message, just sent to make us announce snarf. + * Apparently state and button are 16 and 8 bits on + * the wire, since they are truncated by the time they + * get to us. + */ + if(be->send_event + && (~be->state&0xFFFF)==0 + && (~be->button&0xFF)==0) + return; + x = be->x; + y = be->y; + s = be->state; + if(be->button == lastb && be->time - lastt < DblTime) + dbl = 1; + lastb = be->button; + lastt = be->time; + switch(be->button){ + case 1: + s |= Button1Mask; + break; + case 2: + s |= Button2Mask; + break; + case 3: + s |= Button3Mask; + break; + case 4: + s |= Button4Mask; + break; + case 5: + s |= Button5Mask; + break; + } + break; + case ButtonRelease: + be = (XButtonEvent *)e; + x = be->x; + y = be->y; + s = be->state; + switch(be->button){ + case 1: + s &= ~Button1Mask; + break; + case 2: + s &= ~Button2Mask; + break; + case 3: + s &= ~Button3Mask; + break; + case 4: + s &= ~Button4Mask; + break; + case 5: + s &= ~Button5Mask; + break; + } + break; + case MotionNotify: + me = (XMotionEvent *) e; + + /* remove excess MotionNotify events from queue and keep last one */ + while(XCheckTypedWindowEvent(xmcon, xdrawable, MotionNotify, &motion) == True) + me = (XMotionEvent *) &motion; + + s = me->state; + x = me->x; + y = me->y; + break; + default: + return; + } + + b = 0; + if(s & Button1Mask) + b |= 1; + if(s & Button2Mask) + b |= 2; + if(s & Button3Mask) + b |= 4; + if(s & Button4Mask) + b |= 8; + if(s & Button5Mask) + b |= 16; + if(dbl) + b |= 1<<8; + + mousetrack(b, x, y, 0); +} + +#include "x11-keysym2ucs.c" + +/* + * Cut and paste. Just couldn't stand to make this simple... + */ + +enum{ + SnarfSize= 100*1024 +}; + +typedef struct Clip Clip; +struct Clip +{ + char buf[SnarfSize]; + QLock lk; +}; +Clip clip; + +#undef long /* sic */ +#undef ulong + +static char* +_xgetsnarf(XDisplay *xd) +{ + uchar *data, *xdata; + Atom clipboard, type, prop; + unsigned long len, lastlen, dummy; + int fmt, i; + XWindow w; + + qlock(&clip.lk); + /* + * Have we snarfed recently and the X server hasn't caught up? + */ + if(putsnarf != assertsnarf) + goto mine; + + /* + * Is there a primary selection (highlighted text in an xterm)? + */ + clipboard = XA_PRIMARY; + w = XGetSelectionOwner(xd, XA_PRIMARY); + if(w == xdrawable){ + mine: + data = (uchar*)strdup(clip.buf); + goto out; + } + + /* + * If not, is there a clipboard selection? + */ + if(w == None && clipboard != None){ + clipboard = clipboard; + w = XGetSelectionOwner(xd, clipboard); + if(w == xdrawable) + goto mine; + } + + /* + * If not, give up. + */ + if(w == None){ + data = nil; + goto out; + } + + /* + * We should be waiting for SelectionNotify here, but it might never + * come, and we have no way to time out. Instead, we will clear + * local property #1, request our buddy to fill it in for us, and poll + * until he's done or we get tired of waiting. + * + * We should try to go for utf8string instead of XA_STRING, + * but that would add to the polling. + */ + prop = 1; + XChangeProperty(xd, xdrawable, prop, XA_STRING, 8, PropModeReplace, (uchar*)"", 0); + XConvertSelection(xd, clipboard, XA_STRING, prop, xdrawable, CurrentTime); + XFlush(xd); + lastlen = 0; + for(i=0; i<10 || (lastlen!=0 && i<30); i++){ + osmillisleep(100); + XGetWindowProperty(xd, xdrawable, prop, 0, 0, 0, AnyPropertyType, + &type, &fmt, &dummy, &len, &data); + if(lastlen == len && len > 0) + break; + lastlen = len; + } + if(i == 10){ + data = nil; + goto out; + } + /* get the property */ + data = nil; + XGetWindowProperty(xd, xdrawable, prop, 0, SnarfSize/sizeof(unsigned long), 0, + AnyPropertyType, &type, &fmt, &len, &dummy, &xdata); + if((type != XA_STRING && type != utf8string) || len == 0){ + if(xdata) + XFree(xdata); + data = nil; + }else{ + if(xdata){ + data = (uchar*)strdup((char*)xdata); + XFree(xdata); + }else + data = nil; + } +out: + qunlock(&clip.lk); + return (char*)data; +} + +static void +_xputsnarf(XDisplay *xd, char *data) +{ + XButtonEvent e; + + if(strlen(data) >= SnarfSize) + return; + qlock(&clip.lk); + strcpy(clip.buf, data); + + /* leave note for mouse proc to assert selection ownership */ + putsnarf++; + + /* send mouse a fake event so snarf is announced */ + memset(&e, 0, sizeof e); + e.type = ButtonPress; + e.window = xdrawable; + e.state = ~0; + e.button = ~0; + XSendEvent(xd, xdrawable, True, ButtonPressMask, (XEvent*)&e); + XFlush(xd); + qunlock(&clip.lk); +} + +static void +xselect(XEvent *e, XDisplay *xd) +{ + char *name; + XEvent r; + XSelectionRequestEvent *xe; + Atom a[4]; + + if(e->xany.type != SelectionRequest) + return; + + memset(&r, 0, sizeof r); + xe = (XSelectionRequestEvent*)e; +if(0) iprint("xselect target=%d requestor=%d property=%d selection=%d\n", + xe->target, xe->requestor, xe->property, xe->selection); + r.xselection.property = xe->property; + if(xe->target == targets){ + a[0] = XA_STRING; + a[1] = utf8string; + a[2] = text; + a[3] = compoundtext; + + XChangeProperty(xd, xe->requestor, xe->property, xe->target, + 8, PropModeReplace, (uchar*)a, sizeof a); + }else if(xe->target == XA_STRING || xe->target == utf8string || xe->target == text || xe->target == compoundtext){ + /* if the target is STRING we're supposed to reply with Latin1 XXX */ + qlock(&clip.lk); + XChangeProperty(xd, xe->requestor, xe->property, xe->target, + 8, PropModeReplace, (uchar*)clip.buf, strlen(clip.buf)); + qunlock(&clip.lk); + }else{ + iprint("get %d\n", xe->target); + name = XGetAtomName(xd, xe->target); + if(name == nil) + iprint("XGetAtomName failed\n"); + else if(strcmp(name, "TIMESTAMP") != 0) + iprint("%s: cannot handle selection request for '%s' (%d)\n", argv0, name, (int)xe->target); + r.xselection.property = None; + } + + r.xselection.display = xe->display; + /* r.xselection.property filled above */ + r.xselection.target = xe->target; + r.xselection.type = SelectionNotify; + r.xselection.requestor = xe->requestor; + r.xselection.time = xe->time; + r.xselection.send_event = True; + r.xselection.selection = xe->selection; + XSendEvent(xd, xe->requestor, False, 0, &r); + XFlush(xd); +} + +char* +clipread(void) +{ + char *p; + + if(xsnarfcon == nil) + return nil; + XLockDisplay(xsnarfcon); + p = _xgetsnarf(xsnarfcon); + XUnlockDisplay(xsnarfcon); + return p; +} + +int +clipwrite(char *buf) +{ + if(xsnarfcon == nil) + return 0; + XLockDisplay(xsnarfcon); + _xputsnarf(xsnarfcon, buf); + XUnlockDisplay(xsnarfcon); + return 0; +} diff --git a/emu/port/x11-keysym2ucs.c b/emu/port/x11-keysym2ucs.c new file mode 100644 index 00000000..b96d9624 --- /dev/null +++ b/emu/port/x11-keysym2ucs.c @@ -0,0 +1,857 @@ +/* $XFree86: xc/programs/xterm/keysym2ucs.c,v 1.5 2001/06/18 19:09:26 dickey Exp $ + * This module converts keysym values into the corresponding ISO 10646 + * (UCS, Unicode) values. + * + * The array keysymtab[] contains pairs of X11 keysym values for graphical + * characters and the corresponding Unicode value. The function + * keysym2ucs() maps a keysym onto a Unicode value using a binary search, + * therefore keysymtab[] must remain SORTED by keysym value. + * + * The keysym -> UTF-8 conversion will hopefully one day be provided + * by Xlib via XmbLookupString() and should ideally not have to be + * done in X applications. But we are not there yet. + * + * We allow to represent any UCS character in the range U-00000000 to + * U-00FFFFFF by a keysym value in the range 0x01000000 to 0x01ffffff. + * This admittedly does not cover the entire 31-bit space of UCS, but + * it does cover all of the characters up to U-10FFFF, which can be + * represented by UTF-16, and more, and it is very unlikely that higher + * UCS codes will ever be assigned by ISO. So to get Unicode character + * U+ABCD you can directly use keysym 0x0100abcd. + * + * NOTE: The comments in the table below contain the actual character + * encoded in UTF-8, so for viewing and editing best use an editor in + * UTF-8 mode. + * + * Author: Markus G. Kuhn <mkuhn@acm.org>, University of Cambridge, April 2001 + * + * Special thanks to Richard Verhoeven <river@win.tue.nl> for preparing + * an initial draft of the mapping table. + * + * This software is in the public domain. Share and enjoy! + * + * AUTOMATICALLY GENERATED FILE, DO NOT EDIT !!! (unicode/convmap.pl) + */ + +#ifndef KEYSYM2UCS_INCLUDED + +#include "keysym2ucs.h" +#define VISIBLE /* */ + +#else + +#define VISIBLE static + +#endif + +static struct codepair { + unsigned short keysym; + unsigned short ucs; +} keysymtab[] = { + { 0x01a1, 0x0104 }, /* Aogonek Ą LATIN CAPITAL LETTER A WITH OGONEK */ + { 0x01a2, 0x02d8 }, /* breve ˘ BREVE */ + { 0x01a3, 0x0141 }, /* Lstroke Ł LATIN CAPITAL LETTER L WITH STROKE */ + { 0x01a5, 0x013d }, /* Lcaron Ľ LATIN CAPITAL LETTER L WITH CARON */ + { 0x01a6, 0x015a }, /* Sacute Ś LATIN CAPITAL LETTER S WITH ACUTE */ + { 0x01a9, 0x0160 }, /* Scaron Š LATIN CAPITAL LETTER S WITH CARON */ + { 0x01aa, 0x015e }, /* Scedilla Ş LATIN CAPITAL LETTER S WITH CEDILLA */ + { 0x01ab, 0x0164 }, /* Tcaron Ť LATIN CAPITAL LETTER T WITH CARON */ + { 0x01ac, 0x0179 }, /* Zacute Ź LATIN CAPITAL LETTER Z WITH ACUTE */ + { 0x01ae, 0x017d }, /* Zcaron Ž LATIN CAPITAL LETTER Z WITH CARON */ + { 0x01af, 0x017b }, /* Zabovedot Ż LATIN CAPITAL LETTER Z WITH DOT ABOVE */ + { 0x01b1, 0x0105 }, /* aogonek ą LATIN SMALL LETTER A WITH OGONEK */ + { 0x01b2, 0x02db }, /* ogonek ˛ OGONEK */ + { 0x01b3, 0x0142 }, /* lstroke ł LATIN SMALL LETTER L WITH STROKE */ + { 0x01b5, 0x013e }, /* lcaron ľ LATIN SMALL LETTER L WITH CARON */ + { 0x01b6, 0x015b }, /* sacute ś LATIN SMALL LETTER S WITH ACUTE */ + { 0x01b7, 0x02c7 }, /* caron ˇ CARON */ + { 0x01b9, 0x0161 }, /* scaron š LATIN SMALL LETTER S WITH CARON */ + { 0x01ba, 0x015f }, /* scedilla ş LATIN SMALL LETTER S WITH CEDILLA */ + { 0x01bb, 0x0165 }, /* tcaron ť LATIN SMALL LETTER T WITH CARON */ + { 0x01bc, 0x017a }, /* zacute ź LATIN SMALL LETTER Z WITH ACUTE */ + { 0x01bd, 0x02dd }, /* doubleacute ˝ DOUBLE ACUTE ACCENT */ + { 0x01be, 0x017e }, /* zcaron ž LATIN SMALL LETTER Z WITH CARON */ + { 0x01bf, 0x017c }, /* zabovedot ż LATIN SMALL LETTER Z WITH DOT ABOVE */ + { 0x01c0, 0x0154 }, /* Racute Ŕ LATIN CAPITAL LETTER R WITH ACUTE */ + { 0x01c3, 0x0102 }, /* Abreve Ă LATIN CAPITAL LETTER A WITH BREVE */ + { 0x01c5, 0x0139 }, /* Lacute Ĺ LATIN CAPITAL LETTER L WITH ACUTE */ + { 0x01c6, 0x0106 }, /* Cacute Ć LATIN CAPITAL LETTER C WITH ACUTE */ + { 0x01c8, 0x010c }, /* Ccaron Č LATIN CAPITAL LETTER C WITH CARON */ + { 0x01ca, 0x0118 }, /* Eogonek Ę LATIN CAPITAL LETTER E WITH OGONEK */ + { 0x01cc, 0x011a }, /* Ecaron Ě LATIN CAPITAL LETTER E WITH CARON */ + { 0x01cf, 0x010e }, /* Dcaron Ď LATIN CAPITAL LETTER D WITH CARON */ + { 0x01d0, 0x0110 }, /* Dstroke Đ LATIN CAPITAL LETTER D WITH STROKE */ + { 0x01d1, 0x0143 }, /* Nacute Ń LATIN CAPITAL LETTER N WITH ACUTE */ + { 0x01d2, 0x0147 }, /* Ncaron Ň LATIN CAPITAL LETTER N WITH CARON */ + { 0x01d5, 0x0150 }, /* Odoubleacute Ő LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */ + { 0x01d8, 0x0158 }, /* Rcaron Ř LATIN CAPITAL LETTER R WITH CARON */ + { 0x01d9, 0x016e }, /* Uring Ů LATIN CAPITAL LETTER U WITH RING ABOVE */ + { 0x01db, 0x0170 }, /* Udoubleacute Ű LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */ + { 0x01de, 0x0162 }, /* Tcedilla Ţ LATIN CAPITAL LETTER T WITH CEDILLA */ + { 0x01e0, 0x0155 }, /* racute ŕ LATIN SMALL LETTER R WITH ACUTE */ + { 0x01e3, 0x0103 }, /* abreve ă LATIN SMALL LETTER A WITH BREVE */ + { 0x01e5, 0x013a }, /* lacute ĺ LATIN SMALL LETTER L WITH ACUTE */ + { 0x01e6, 0x0107 }, /* cacute ć LATIN SMALL LETTER C WITH ACUTE */ + { 0x01e8, 0x010d }, /* ccaron č LATIN SMALL LETTER C WITH CARON */ + { 0x01ea, 0x0119 }, /* eogonek ę LATIN SMALL LETTER E WITH OGONEK */ + { 0x01ec, 0x011b }, /* ecaron ě LATIN SMALL LETTER E WITH CARON */ + { 0x01ef, 0x010f }, /* dcaron ď LATIN SMALL LETTER D WITH CARON */ + { 0x01f0, 0x0111 }, /* dstroke đ LATIN SMALL LETTER D WITH STROKE */ + { 0x01f1, 0x0144 }, /* nacute ń LATIN SMALL LETTER N WITH ACUTE */ + { 0x01f2, 0x0148 }, /* ncaron ň LATIN SMALL LETTER N WITH CARON */ + { 0x01f5, 0x0151 }, /* odoubleacute ő LATIN SMALL LETTER O WITH DOUBLE ACUTE */ + { 0x01f8, 0x0159 }, /* rcaron ř LATIN SMALL LETTER R WITH CARON */ + { 0x01f9, 0x016f }, /* uring ů LATIN SMALL LETTER U WITH RING ABOVE */ + { 0x01fb, 0x0171 }, /* udoubleacute ű LATIN SMALL LETTER U WITH DOUBLE ACUTE */ + { 0x01fe, 0x0163 }, /* tcedilla ţ LATIN SMALL LETTER T WITH CEDILLA */ + { 0x01ff, 0x02d9 }, /* abovedot ˙ DOT ABOVE */ + { 0x02a1, 0x0126 }, /* Hstroke Ħ LATIN CAPITAL LETTER H WITH STROKE */ + { 0x02a6, 0x0124 }, /* Hcircumflex Ĥ LATIN CAPITAL LETTER H WITH CIRCUMFLEX */ + { 0x02a9, 0x0130 }, /* Iabovedot İ LATIN CAPITAL LETTER I WITH DOT ABOVE */ + { 0x02ab, 0x011e }, /* Gbreve Ğ LATIN CAPITAL LETTER G WITH BREVE */ + { 0x02ac, 0x0134 }, /* Jcircumflex Ĵ LATIN CAPITAL LETTER J WITH CIRCUMFLEX */ + { 0x02b1, 0x0127 }, /* hstroke ħ LATIN SMALL LETTER H WITH STROKE */ + { 0x02b6, 0x0125 }, /* hcircumflex ĥ LATIN SMALL LETTER H WITH CIRCUMFLEX */ + { 0x02b9, 0x0131 }, /* idotless ı LATIN SMALL LETTER DOTLESS I */ + { 0x02bb, 0x011f }, /* gbreve ğ LATIN SMALL LETTER G WITH BREVE */ + { 0x02bc, 0x0135 }, /* jcircumflex ĵ LATIN SMALL LETTER J WITH CIRCUMFLEX */ + { 0x02c5, 0x010a }, /* Cabovedot Ċ LATIN CAPITAL LETTER C WITH DOT ABOVE */ + { 0x02c6, 0x0108 }, /* Ccircumflex Ĉ LATIN CAPITAL LETTER C WITH CIRCUMFLEX */ + { 0x02d5, 0x0120 }, /* Gabovedot Ġ LATIN CAPITAL LETTER G WITH DOT ABOVE */ + { 0x02d8, 0x011c }, /* Gcircumflex Ĝ LATIN CAPITAL LETTER G WITH CIRCUMFLEX */ + { 0x02dd, 0x016c }, /* Ubreve Ŭ LATIN CAPITAL LETTER U WITH BREVE */ + { 0x02de, 0x015c }, /* Scircumflex Ŝ LATIN CAPITAL LETTER S WITH CIRCUMFLEX */ + { 0x02e5, 0x010b }, /* cabovedot ċ LATIN SMALL LETTER C WITH DOT ABOVE */ + { 0x02e6, 0x0109 }, /* ccircumflex ĉ LATIN SMALL LETTER C WITH CIRCUMFLEX */ + { 0x02f5, 0x0121 }, /* gabovedot ġ LATIN SMALL LETTER G WITH DOT ABOVE */ + { 0x02f8, 0x011d }, /* gcircumflex ĝ LATIN SMALL LETTER G WITH CIRCUMFLEX */ + { 0x02fd, 0x016d }, /* ubreve ŭ LATIN SMALL LETTER U WITH BREVE */ + { 0x02fe, 0x015d }, /* scircumflex ŝ LATIN SMALL LETTER S WITH CIRCUMFLEX */ + { 0x03a2, 0x0138 }, /* kra ĸ LATIN SMALL LETTER KRA */ + { 0x03a3, 0x0156 }, /* Rcedilla Ŗ LATIN CAPITAL LETTER R WITH CEDILLA */ + { 0x03a5, 0x0128 }, /* Itilde Ĩ LATIN CAPITAL LETTER I WITH TILDE */ + { 0x03a6, 0x013b }, /* Lcedilla Ļ LATIN CAPITAL LETTER L WITH CEDILLA */ + { 0x03aa, 0x0112 }, /* Emacron Ē LATIN CAPITAL LETTER E WITH MACRON */ + { 0x03ab, 0x0122 }, /* Gcedilla Ģ LATIN CAPITAL LETTER G WITH CEDILLA */ + { 0x03ac, 0x0166 }, /* Tslash Ŧ LATIN CAPITAL LETTER T WITH STROKE */ + { 0x03b3, 0x0157 }, /* rcedilla ŗ LATIN SMALL LETTER R WITH CEDILLA */ + { 0x03b5, 0x0129 }, /* itilde ĩ LATIN SMALL LETTER I WITH TILDE */ + { 0x03b6, 0x013c }, /* lcedilla ļ LATIN SMALL LETTER L WITH CEDILLA */ + { 0x03ba, 0x0113 }, /* emacron ē LATIN SMALL LETTER E WITH MACRON */ + { 0x03bb, 0x0123 }, /* gcedilla ģ LATIN SMALL LETTER G WITH CEDILLA */ + { 0x03bc, 0x0167 }, /* tslash ŧ LATIN SMALL LETTER T WITH STROKE */ + { 0x03bd, 0x014a }, /* ENG Ŋ LATIN CAPITAL LETTER ENG */ + { 0x03bf, 0x014b }, /* eng ŋ LATIN SMALL LETTER ENG */ + { 0x03c0, 0x0100 }, /* Amacron Ā LATIN CAPITAL LETTER A WITH MACRON */ + { 0x03c7, 0x012e }, /* Iogonek Į LATIN CAPITAL LETTER I WITH OGONEK */ + { 0x03cc, 0x0116 }, /* Eabovedot Ė LATIN CAPITAL LETTER E WITH DOT ABOVE */ + { 0x03cf, 0x012a }, /* Imacron Ī LATIN CAPITAL LETTER I WITH MACRON */ + { 0x03d1, 0x0145 }, /* Ncedilla Ņ LATIN CAPITAL LETTER N WITH CEDILLA */ + { 0x03d2, 0x014c }, /* Omacron Ō LATIN CAPITAL LETTER O WITH MACRON */ + { 0x03d3, 0x0136 }, /* Kcedilla Ķ LATIN CAPITAL LETTER K WITH CEDILLA */ + { 0x03d9, 0x0172 }, /* Uogonek Ų LATIN CAPITAL LETTER U WITH OGONEK */ + { 0x03dd, 0x0168 }, /* Utilde Ũ LATIN CAPITAL LETTER U WITH TILDE */ + { 0x03de, 0x016a }, /* Umacron Ū LATIN CAPITAL LETTER U WITH MACRON */ + { 0x03e0, 0x0101 }, /* amacron ā LATIN SMALL LETTER A WITH MACRON */ + { 0x03e7, 0x012f }, /* iogonek į LATIN SMALL LETTER I WITH OGONEK */ + { 0x03ec, 0x0117 }, /* eabovedot ė LATIN SMALL LETTER E WITH DOT ABOVE */ + { 0x03ef, 0x012b }, /* imacron ī LATIN SMALL LETTER I WITH MACRON */ + { 0x03f1, 0x0146 }, /* ncedilla ņ LATIN SMALL LETTER N WITH CEDILLA */ + { 0x03f2, 0x014d }, /* omacron ō LATIN SMALL LETTER O WITH MACRON */ + { 0x03f3, 0x0137 }, /* kcedilla ķ LATIN SMALL LETTER K WITH CEDILLA */ + { 0x03f9, 0x0173 }, /* uogonek ų LATIN SMALL LETTER U WITH OGONEK */ + { 0x03fd, 0x0169 }, /* utilde ũ LATIN SMALL LETTER U WITH TILDE */ + { 0x03fe, 0x016b }, /* umacron ū LATIN SMALL LETTER U WITH MACRON */ + { 0x047e, 0x203e }, /* overline ‾ OVERLINE */ + { 0x04a1, 0x3002 }, /* kana_fullstop 。 IDEOGRAPHIC FULL STOP */ + { 0x04a2, 0x300c }, /* kana_openingbracket 「 LEFT CORNER BRACKET */ + { 0x04a3, 0x300d }, /* kana_closingbracket 」 RIGHT CORNER BRACKET */ + { 0x04a4, 0x3001 }, /* kana_comma 、 IDEOGRAPHIC COMMA */ + { 0x04a5, 0x30fb }, /* kana_conjunctive ・ KATAKANA MIDDLE DOT */ + { 0x04a6, 0x30f2 }, /* kana_WO ヲ KATAKANA LETTER WO */ + { 0x04a7, 0x30a1 }, /* kana_a ァ KATAKANA LETTER SMALL A */ + { 0x04a8, 0x30a3 }, /* kana_i ィ KATAKANA LETTER SMALL I */ + { 0x04a9, 0x30a5 }, /* kana_u ゥ KATAKANA LETTER SMALL U */ + { 0x04aa, 0x30a7 }, /* kana_e ェ KATAKANA LETTER SMALL E */ + { 0x04ab, 0x30a9 }, /* kana_o ォ KATAKANA LETTER SMALL O */ + { 0x04ac, 0x30e3 }, /* kana_ya ャ KATAKANA LETTER SMALL YA */ + { 0x04ad, 0x30e5 }, /* kana_yu ュ KATAKANA LETTER SMALL YU */ + { 0x04ae, 0x30e7 }, /* kana_yo ョ KATAKANA LETTER SMALL YO */ + { 0x04af, 0x30c3 }, /* kana_tsu ッ KATAKANA LETTER SMALL TU */ + { 0x04b0, 0x30fc }, /* prolongedsound ー KATAKANA-HIRAGANA PROLONGED SOUND MARK */ + { 0x04b1, 0x30a2 }, /* kana_A ア KATAKANA LETTER A */ + { 0x04b2, 0x30a4 }, /* kana_I イ KATAKANA LETTER I */ + { 0x04b3, 0x30a6 }, /* kana_U ウ KATAKANA LETTER U */ + { 0x04b4, 0x30a8 }, /* kana_E エ KATAKANA LETTER E */ + { 0x04b5, 0x30aa }, /* kana_O オ KATAKANA LETTER O */ + { 0x04b6, 0x30ab }, /* kana_KA カ KATAKANA LETTER KA */ + { 0x04b7, 0x30ad }, /* kana_KI キ KATAKANA LETTER KI */ + { 0x04b8, 0x30af }, /* kana_KU ク KATAKANA LETTER KU */ + { 0x04b9, 0x30b1 }, /* kana_KE ケ KATAKANA LETTER KE */ + { 0x04ba, 0x30b3 }, /* kana_KO コ KATAKANA LETTER KO */ + { 0x04bb, 0x30b5 }, /* kana_SA サ KATAKANA LETTER SA */ + { 0x04bc, 0x30b7 }, /* kana_SHI シ KATAKANA LETTER SI */ + { 0x04bd, 0x30b9 }, /* kana_SU ス KATAKANA LETTER SU */ + { 0x04be, 0x30bb }, /* kana_SE セ KATAKANA LETTER SE */ + { 0x04bf, 0x30bd }, /* kana_SO ソ KATAKANA LETTER SO */ + { 0x04c0, 0x30bf }, /* kana_TA タ KATAKANA LETTER TA */ + { 0x04c1, 0x30c1 }, /* kana_CHI チ KATAKANA LETTER TI */ + { 0x04c2, 0x30c4 }, /* kana_TSU ツ KATAKANA LETTER TU */ + { 0x04c3, 0x30c6 }, /* kana_TE テ KATAKANA LETTER TE */ + { 0x04c4, 0x30c8 }, /* kana_TO ト KATAKANA LETTER TO */ + { 0x04c5, 0x30ca }, /* kana_NA ナ KATAKANA LETTER NA */ + { 0x04c6, 0x30cb }, /* kana_NI ニ KATAKANA LETTER NI */ + { 0x04c7, 0x30cc }, /* kana_NU ヌ KATAKANA LETTER NU */ + { 0x04c8, 0x30cd }, /* kana_NE ネ KATAKANA LETTER NE */ + { 0x04c9, 0x30ce }, /* kana_NO ノ KATAKANA LETTER NO */ + { 0x04ca, 0x30cf }, /* kana_HA ハ KATAKANA LETTER HA */ + { 0x04cb, 0x30d2 }, /* kana_HI ヒ KATAKANA LETTER HI */ + { 0x04cc, 0x30d5 }, /* kana_FU フ KATAKANA LETTER HU */ + { 0x04cd, 0x30d8 }, /* kana_HE ヘ KATAKANA LETTER HE */ + { 0x04ce, 0x30db }, /* kana_HO ホ KATAKANA LETTER HO */ + { 0x04cf, 0x30de }, /* kana_MA マ KATAKANA LETTER MA */ + { 0x04d0, 0x30df }, /* kana_MI ミ KATAKANA LETTER MI */ + { 0x04d1, 0x30e0 }, /* kana_MU ム KATAKANA LETTER MU */ + { 0x04d2, 0x30e1 }, /* kana_ME メ KATAKANA LETTER ME */ + { 0x04d3, 0x30e2 }, /* kana_MO モ KATAKANA LETTER MO */ + { 0x04d4, 0x30e4 }, /* kana_YA ヤ KATAKANA LETTER YA */ + { 0x04d5, 0x30e6 }, /* kana_YU ユ KATAKANA LETTER YU */ + { 0x04d6, 0x30e8 }, /* kana_YO ヨ KATAKANA LETTER YO */ + { 0x04d7, 0x30e9 }, /* kana_RA ラ KATAKANA LETTER RA */ + { 0x04d8, 0x30ea }, /* kana_RI リ KATAKANA LETTER RI */ + { 0x04d9, 0x30eb }, /* kana_RU ル KATAKANA LETTER RU */ + { 0x04da, 0x30ec }, /* kana_RE レ KATAKANA LETTER RE */ + { 0x04db, 0x30ed }, /* kana_RO ロ KATAKANA LETTER RO */ + { 0x04dc, 0x30ef }, /* kana_WA ワ KATAKANA LETTER WA */ + { 0x04dd, 0x30f3 }, /* kana_N ン KATAKANA LETTER N */ + { 0x04de, 0x309b }, /* voicedsound ゛ KATAKANA-HIRAGANA VOICED SOUND MARK */ + { 0x04df, 0x309c }, /* semivoicedsound ゜ KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */ + { 0x05ac, 0x060c }, /* Arabic_comma ، ARABIC COMMA */ + { 0x05bb, 0x061b }, /* Arabic_semicolon ؛ ARABIC SEMICOLON */ + { 0x05bf, 0x061f }, /* Arabic_question_mark ؟ ARABIC QUESTION MARK */ + { 0x05c1, 0x0621 }, /* Arabic_hamza ء ARABIC LETTER HAMZA */ + { 0x05c2, 0x0622 }, /* Arabic_maddaonalef آ ARABIC LETTER ALEF WITH MADDA ABOVE */ + { 0x05c3, 0x0623 }, /* Arabic_hamzaonalef أ ARABIC LETTER ALEF WITH HAMZA ABOVE */ + { 0x05c4, 0x0624 }, /* Arabic_hamzaonwaw ؤ ARABIC LETTER WAW WITH HAMZA ABOVE */ + { 0x05c5, 0x0625 }, /* Arabic_hamzaunderalef إ ARABIC LETTER ALEF WITH HAMZA BELOW */ + { 0x05c6, 0x0626 }, /* Arabic_hamzaonyeh ئ ARABIC LETTER YEH WITH HAMZA ABOVE */ + { 0x05c7, 0x0627 }, /* Arabic_alef ا ARABIC LETTER ALEF */ + { 0x05c8, 0x0628 }, /* Arabic_beh ب ARABIC LETTER BEH */ + { 0x05c9, 0x0629 }, /* Arabic_tehmarbuta ة ARABIC LETTER TEH MARBUTA */ + { 0x05ca, 0x062a }, /* Arabic_teh ت ARABIC LETTER TEH */ + { 0x05cb, 0x062b }, /* Arabic_theh ث ARABIC LETTER THEH */ + { 0x05cc, 0x062c }, /* Arabic_jeem ج ARABIC LETTER JEEM */ + { 0x05cd, 0x062d }, /* Arabic_hah ح ARABIC LETTER HAH */ + { 0x05ce, 0x062e }, /* Arabic_khah خ ARABIC LETTER KHAH */ + { 0x05cf, 0x062f }, /* Arabic_dal د ARABIC LETTER DAL */ + { 0x05d0, 0x0630 }, /* Arabic_thal ذ ARABIC LETTER THAL */ + { 0x05d1, 0x0631 }, /* Arabic_ra ر ARABIC LETTER REH */ + { 0x05d2, 0x0632 }, /* Arabic_zain ز ARABIC LETTER ZAIN */ + { 0x05d3, 0x0633 }, /* Arabic_seen س ARABIC LETTER SEEN */ + { 0x05d4, 0x0634 }, /* Arabic_sheen ش ARABIC LETTER SHEEN */ + { 0x05d5, 0x0635 }, /* Arabic_sad ص ARABIC LETTER SAD */ + { 0x05d6, 0x0636 }, /* Arabic_dad ض ARABIC LETTER DAD */ + { 0x05d7, 0x0637 }, /* Arabic_tah ط ARABIC LETTER TAH */ + { 0x05d8, 0x0638 }, /* Arabic_zah ظ ARABIC LETTER ZAH */ + { 0x05d9, 0x0639 }, /* Arabic_ain ع ARABIC LETTER AIN */ + { 0x05da, 0x063a }, /* Arabic_ghain غ ARABIC LETTER GHAIN */ + { 0x05e0, 0x0640 }, /* Arabic_tatweel ـ ARABIC TATWEEL */ + { 0x05e1, 0x0641 }, /* Arabic_feh ف ARABIC LETTER FEH */ + { 0x05e2, 0x0642 }, /* Arabic_qaf ق ARABIC LETTER QAF */ + { 0x05e3, 0x0643 }, /* Arabic_kaf ك ARABIC LETTER KAF */ + { 0x05e4, 0x0644 }, /* Arabic_lam ل ARABIC LETTER LAM */ + { 0x05e5, 0x0645 }, /* Arabic_meem م ARABIC LETTER MEEM */ + { 0x05e6, 0x0646 }, /* Arabic_noon ن ARABIC LETTER NOON */ + { 0x05e7, 0x0647 }, /* Arabic_ha ه ARABIC LETTER HEH */ + { 0x05e8, 0x0648 }, /* Arabic_waw و ARABIC LETTER WAW */ + { 0x05e9, 0x0649 }, /* Arabic_alefmaksura ى ARABIC LETTER ALEF MAKSURA */ + { 0x05ea, 0x064a }, /* Arabic_yeh ي ARABIC LETTER YEH */ + { 0x05eb, 0x064b }, /* Arabic_fathatan ً ARABIC FATHATAN */ + { 0x05ec, 0x064c }, /* Arabic_dammatan ٌ ARABIC DAMMATAN */ + { 0x05ed, 0x064d }, /* Arabic_kasratan ٍ ARABIC KASRATAN */ + { 0x05ee, 0x064e }, /* Arabic_fatha َ ARABIC FATHA */ + { 0x05ef, 0x064f }, /* Arabic_damma ُ ARABIC DAMMA */ + { 0x05f0, 0x0650 }, /* Arabic_kasra ِ ARABIC KASRA */ + { 0x05f1, 0x0651 }, /* Arabic_shadda ّ ARABIC SHADDA */ + { 0x05f2, 0x0652 }, /* Arabic_sukun ْ ARABIC SUKUN */ + { 0x06a1, 0x0452 }, /* Serbian_dje ђ CYRILLIC SMALL LETTER DJE */ + { 0x06a2, 0x0453 }, /* Macedonia_gje ѓ CYRILLIC SMALL LETTER GJE */ + { 0x06a3, 0x0451 }, /* Cyrillic_io ё CYRILLIC SMALL LETTER IO */ + { 0x06a4, 0x0454 }, /* Ukrainian_ie є CYRILLIC SMALL LETTER UKRAINIAN IE */ + { 0x06a5, 0x0455 }, /* Macedonia_dse ѕ CYRILLIC SMALL LETTER DZE */ + { 0x06a6, 0x0456 }, /* Ukrainian_i і CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I */ + { 0x06a7, 0x0457 }, /* Ukrainian_yi ї CYRILLIC SMALL LETTER YI */ + { 0x06a8, 0x0458 }, /* Cyrillic_je ј CYRILLIC SMALL LETTER JE */ + { 0x06a9, 0x0459 }, /* Cyrillic_lje љ CYRILLIC SMALL LETTER LJE */ + { 0x06aa, 0x045a }, /* Cyrillic_nje њ CYRILLIC SMALL LETTER NJE */ + { 0x06ab, 0x045b }, /* Serbian_tshe ћ CYRILLIC SMALL LETTER TSHE */ + { 0x06ac, 0x045c }, /* Macedonia_kje ќ CYRILLIC SMALL LETTER KJE */ + { 0x06ae, 0x045e }, /* Byelorussian_shortu ў CYRILLIC SMALL LETTER SHORT U */ + { 0x06af, 0x045f }, /* Cyrillic_dzhe џ CYRILLIC SMALL LETTER DZHE */ + { 0x06b0, 0x2116 }, /* numerosign № NUMERO SIGN */ + { 0x06b1, 0x0402 }, /* Serbian_DJE Ђ CYRILLIC CAPITAL LETTER DJE */ + { 0x06b2, 0x0403 }, /* Macedonia_GJE Ѓ CYRILLIC CAPITAL LETTER GJE */ + { 0x06b3, 0x0401 }, /* Cyrillic_IO Ё CYRILLIC CAPITAL LETTER IO */ + { 0x06b4, 0x0404 }, /* Ukrainian_IE Є CYRILLIC CAPITAL LETTER UKRAINIAN IE */ + { 0x06b5, 0x0405 }, /* Macedonia_DSE Ѕ CYRILLIC CAPITAL LETTER DZE */ + { 0x06b6, 0x0406 }, /* Ukrainian_I І CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I */ + { 0x06b7, 0x0407 }, /* Ukrainian_YI Ї CYRILLIC CAPITAL LETTER YI */ + { 0x06b8, 0x0408 }, /* Cyrillic_JE Ј CYRILLIC CAPITAL LETTER JE */ + { 0x06b9, 0x0409 }, /* Cyrillic_LJE Љ CYRILLIC CAPITAL LETTER LJE */ + { 0x06ba, 0x040a }, /* Cyrillic_NJE Њ CYRILLIC CAPITAL LETTER NJE */ + { 0x06bb, 0x040b }, /* Serbian_TSHE Ћ CYRILLIC CAPITAL LETTER TSHE */ + { 0x06bc, 0x040c }, /* Macedonia_KJE Ќ CYRILLIC CAPITAL LETTER KJE */ + { 0x06be, 0x040e }, /* Byelorussian_SHORTU Ў CYRILLIC CAPITAL LETTER SHORT U */ + { 0x06bf, 0x040f }, /* Cyrillic_DZHE Џ CYRILLIC CAPITAL LETTER DZHE */ + { 0x06c0, 0x044e }, /* Cyrillic_yu ю CYRILLIC SMALL LETTER YU */ + { 0x06c1, 0x0430 }, /* Cyrillic_a а CYRILLIC SMALL LETTER A */ + { 0x06c2, 0x0431 }, /* Cyrillic_be б CYRILLIC SMALL LETTER BE */ + { 0x06c3, 0x0446 }, /* Cyrillic_tse ц CYRILLIC SMALL LETTER TSE */ + { 0x06c4, 0x0434 }, /* Cyrillic_de д CYRILLIC SMALL LETTER DE */ + { 0x06c5, 0x0435 }, /* Cyrillic_ie е CYRILLIC SMALL LETTER IE */ + { 0x06c6, 0x0444 }, /* Cyrillic_ef ф CYRILLIC SMALL LETTER EF */ + { 0x06c7, 0x0433 }, /* Cyrillic_ghe г CYRILLIC SMALL LETTER GHE */ + { 0x06c8, 0x0445 }, /* Cyrillic_ha х CYRILLIC SMALL LETTER HA */ + { 0x06c9, 0x0438 }, /* Cyrillic_i и CYRILLIC SMALL LETTER I */ + { 0x06ca, 0x0439 }, /* Cyrillic_shorti й CYRILLIC SMALL LETTER SHORT I */ + { 0x06cb, 0x043a }, /* Cyrillic_ka к CYRILLIC SMALL LETTER KA */ + { 0x06cc, 0x043b }, /* Cyrillic_el л CYRILLIC SMALL LETTER EL */ + { 0x06cd, 0x043c }, /* Cyrillic_em м CYRILLIC SMALL LETTER EM */ + { 0x06ce, 0x043d }, /* Cyrillic_en н CYRILLIC SMALL LETTER EN */ + { 0x06cf, 0x043e }, /* Cyrillic_o о CYRILLIC SMALL LETTER O */ + { 0x06d0, 0x043f }, /* Cyrillic_pe п CYRILLIC SMALL LETTER PE */ + { 0x06d1, 0x044f }, /* Cyrillic_ya я CYRILLIC SMALL LETTER YA */ + { 0x06d2, 0x0440 }, /* Cyrillic_er р CYRILLIC SMALL LETTER ER */ + { 0x06d3, 0x0441 }, /* Cyrillic_es с CYRILLIC SMALL LETTER ES */ + { 0x06d4, 0x0442 }, /* Cyrillic_te т CYRILLIC SMALL LETTER TE */ + { 0x06d5, 0x0443 }, /* Cyrillic_u у CYRILLIC SMALL LETTER U */ + { 0x06d6, 0x0436 }, /* Cyrillic_zhe ж CYRILLIC SMALL LETTER ZHE */ + { 0x06d7, 0x0432 }, /* Cyrillic_ve в CYRILLIC SMALL LETTER VE */ + { 0x06d8, 0x044c }, /* Cyrillic_softsign ь CYRILLIC SMALL LETTER SOFT SIGN */ + { 0x06d9, 0x044b }, /* Cyrillic_yeru ы CYRILLIC SMALL LETTER YERU */ + { 0x06da, 0x0437 }, /* Cyrillic_ze з CYRILLIC SMALL LETTER ZE */ + { 0x06db, 0x0448 }, /* Cyrillic_sha ш CYRILLIC SMALL LETTER SHA */ + { 0x06dc, 0x044d }, /* Cyrillic_e э CYRILLIC SMALL LETTER E */ + { 0x06dd, 0x0449 }, /* Cyrillic_shcha щ CYRILLIC SMALL LETTER SHCHA */ + { 0x06de, 0x0447 }, /* Cyrillic_che ч CYRILLIC SMALL LETTER CHE */ + { 0x06df, 0x044a }, /* Cyrillic_hardsign ъ CYRILLIC SMALL LETTER HARD SIGN */ + { 0x06e0, 0x042e }, /* Cyrillic_YU Ю CYRILLIC CAPITAL LETTER YU */ + { 0x06e1, 0x0410 }, /* Cyrillic_A А CYRILLIC CAPITAL LETTER A */ + { 0x06e2, 0x0411 }, /* Cyrillic_BE Б CYRILLIC CAPITAL LETTER BE */ + { 0x06e3, 0x0426 }, /* Cyrillic_TSE Ц CYRILLIC CAPITAL LETTER TSE */ + { 0x06e4, 0x0414 }, /* Cyrillic_DE Д CYRILLIC CAPITAL LETTER DE */ + { 0x06e5, 0x0415 }, /* Cyrillic_IE Е CYRILLIC CAPITAL LETTER IE */ + { 0x06e6, 0x0424 }, /* Cyrillic_EF Ф CYRILLIC CAPITAL LETTER EF */ + { 0x06e7, 0x0413 }, /* Cyrillic_GHE Г CYRILLIC CAPITAL LETTER GHE */ + { 0x06e8, 0x0425 }, /* Cyrillic_HA Х CYRILLIC CAPITAL LETTER HA */ + { 0x06e9, 0x0418 }, /* Cyrillic_I И CYRILLIC CAPITAL LETTER I */ + { 0x06ea, 0x0419 }, /* Cyrillic_SHORTI Й CYRILLIC CAPITAL LETTER SHORT I */ + { 0x06eb, 0x041a }, /* Cyrillic_KA К CYRILLIC CAPITAL LETTER KA */ + { 0x06ec, 0x041b }, /* Cyrillic_EL Л CYRILLIC CAPITAL LETTER EL */ + { 0x06ed, 0x041c }, /* Cyrillic_EM М CYRILLIC CAPITAL LETTER EM */ + { 0x06ee, 0x041d }, /* Cyrillic_EN Н CYRILLIC CAPITAL LETTER EN */ + { 0x06ef, 0x041e }, /* Cyrillic_O О CYRILLIC CAPITAL LETTER O */ + { 0x06f0, 0x041f }, /* Cyrillic_PE П CYRILLIC CAPITAL LETTER PE */ + { 0x06f1, 0x042f }, /* Cyrillic_YA Я CYRILLIC CAPITAL LETTER YA */ + { 0x06f2, 0x0420 }, /* Cyrillic_ER Р CYRILLIC CAPITAL LETTER ER */ + { 0x06f3, 0x0421 }, /* Cyrillic_ES С CYRILLIC CAPITAL LETTER ES */ + { 0x06f4, 0x0422 }, /* Cyrillic_TE Т CYRILLIC CAPITAL LETTER TE */ + { 0x06f5, 0x0423 }, /* Cyrillic_U У CYRILLIC CAPITAL LETTER U */ + { 0x06f6, 0x0416 }, /* Cyrillic_ZHE Ж CYRILLIC CAPITAL LETTER ZHE */ + { 0x06f7, 0x0412 }, /* Cyrillic_VE В CYRILLIC CAPITAL LETTER VE */ + { 0x06f8, 0x042c }, /* Cyrillic_SOFTSIGN Ь CYRILLIC CAPITAL LETTER SOFT SIGN */ + { 0x06f9, 0x042b }, /* Cyrillic_YERU Ы CYRILLIC CAPITAL LETTER YERU */ + { 0x06fa, 0x0417 }, /* Cyrillic_ZE З CYRILLIC CAPITAL LETTER ZE */ + { 0x06fb, 0x0428 }, /* Cyrillic_SHA Ш CYRILLIC CAPITAL LETTER SHA */ + { 0x06fc, 0x042d }, /* Cyrillic_E Э CYRILLIC CAPITAL LETTER E */ + { 0x06fd, 0x0429 }, /* Cyrillic_SHCHA Щ CYRILLIC CAPITAL LETTER SHCHA */ + { 0x06fe, 0x0427 }, /* Cyrillic_CHE Ч CYRILLIC CAPITAL LETTER CHE */ + { 0x06ff, 0x042a }, /* Cyrillic_HARDSIGN Ъ CYRILLIC CAPITAL LETTER HARD SIGN */ + { 0x07a1, 0x0386 }, /* Greek_ALPHAaccent Ά GREEK CAPITAL LETTER ALPHA WITH TONOS */ + { 0x07a2, 0x0388 }, /* Greek_EPSILONaccent Έ GREEK CAPITAL LETTER EPSILON WITH TONOS */ + { 0x07a3, 0x0389 }, /* Greek_ETAaccent Ή GREEK CAPITAL LETTER ETA WITH TONOS */ + { 0x07a4, 0x038a }, /* Greek_IOTAaccent Ί GREEK CAPITAL LETTER IOTA WITH TONOS */ + { 0x07a5, 0x03aa }, /* Greek_IOTAdiaeresis Ϊ GREEK CAPITAL LETTER IOTA WITH DIALYTIKA */ + { 0x07a7, 0x038c }, /* Greek_OMICRONaccent Ό GREEK CAPITAL LETTER OMICRON WITH TONOS */ + { 0x07a8, 0x038e }, /* Greek_UPSILONaccent Ύ GREEK CAPITAL LETTER UPSILON WITH TONOS */ + { 0x07a9, 0x03ab }, /* Greek_UPSILONdieresis Ϋ GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA */ + { 0x07ab, 0x038f }, /* Greek_OMEGAaccent Ώ GREEK CAPITAL LETTER OMEGA WITH TONOS */ + { 0x07ae, 0x0385 }, /* Greek_accentdieresis ΅ GREEK DIALYTIKA TONOS */ + { 0x07af, 0x2015 }, /* Greek_horizbar ― HORIZONTAL BAR */ + { 0x07b1, 0x03ac }, /* Greek_alphaaccent ά GREEK SMALL LETTER ALPHA WITH TONOS */ + { 0x07b2, 0x03ad }, /* Greek_epsilonaccent έ GREEK SMALL LETTER EPSILON WITH TONOS */ + { 0x07b3, 0x03ae }, /* Greek_etaaccent ή GREEK SMALL LETTER ETA WITH TONOS */ + { 0x07b4, 0x03af }, /* Greek_iotaaccent ί GREEK SMALL LETTER IOTA WITH TONOS */ + { 0x07b5, 0x03ca }, /* Greek_iotadieresis ϊ GREEK SMALL LETTER IOTA WITH DIALYTIKA */ + { 0x07b6, 0x0390 }, /* Greek_iotaaccentdieresis ΐ GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS */ + { 0x07b7, 0x03cc }, /* Greek_omicronaccent ό GREEK SMALL LETTER OMICRON WITH TONOS */ + { 0x07b8, 0x03cd }, /* Greek_upsilonaccent ύ GREEK SMALL LETTER UPSILON WITH TONOS */ + { 0x07b9, 0x03cb }, /* Greek_upsilondieresis ϋ GREEK SMALL LETTER UPSILON WITH DIALYTIKA */ + { 0x07ba, 0x03b0 }, /* Greek_upsilonaccentdieresis ΰ GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS */ + { 0x07bb, 0x03ce }, /* Greek_omegaaccent ώ GREEK SMALL LETTER OMEGA WITH TONOS */ + { 0x07c1, 0x0391 }, /* Greek_ALPHA Α GREEK CAPITAL LETTER ALPHA */ + { 0x07c2, 0x0392 }, /* Greek_BETA Β GREEK CAPITAL LETTER BETA */ + { 0x07c3, 0x0393 }, /* Greek_GAMMA Γ GREEK CAPITAL LETTER GAMMA */ + { 0x07c4, 0x0394 }, /* Greek_DELTA Δ GREEK CAPITAL LETTER DELTA */ + { 0x07c5, 0x0395 }, /* Greek_EPSILON Ε GREEK CAPITAL LETTER EPSILON */ + { 0x07c6, 0x0396 }, /* Greek_ZETA Ζ GREEK CAPITAL LETTER ZETA */ + { 0x07c7, 0x0397 }, /* Greek_ETA Η GREEK CAPITAL LETTER ETA */ + { 0x07c8, 0x0398 }, /* Greek_THETA Θ GREEK CAPITAL LETTER THETA */ + { 0x07c9, 0x0399 }, /* Greek_IOTA Ι GREEK CAPITAL LETTER IOTA */ + { 0x07ca, 0x039a }, /* Greek_KAPPA Κ GREEK CAPITAL LETTER KAPPA */ + { 0x07cb, 0x039b }, /* Greek_LAMBDA Λ GREEK CAPITAL LETTER LAMDA */ + { 0x07cc, 0x039c }, /* Greek_MU Μ GREEK CAPITAL LETTER MU */ + { 0x07cd, 0x039d }, /* Greek_NU Ν GREEK CAPITAL LETTER NU */ + { 0x07ce, 0x039e }, /* Greek_XI Ξ GREEK CAPITAL LETTER XI */ + { 0x07cf, 0x039f }, /* Greek_OMICRON Ο GREEK CAPITAL LETTER OMICRON */ + { 0x07d0, 0x03a0 }, /* Greek_PI Π GREEK CAPITAL LETTER PI */ + { 0x07d1, 0x03a1 }, /* Greek_RHO Ρ GREEK CAPITAL LETTER RHO */ + { 0x07d2, 0x03a3 }, /* Greek_SIGMA Σ GREEK CAPITAL LETTER SIGMA */ + { 0x07d4, 0x03a4 }, /* Greek_TAU Τ GREEK CAPITAL LETTER TAU */ + { 0x07d5, 0x03a5 }, /* Greek_UPSILON Υ GREEK CAPITAL LETTER UPSILON */ + { 0x07d6, 0x03a6 }, /* Greek_PHI Φ GREEK CAPITAL LETTER PHI */ + { 0x07d7, 0x03a7 }, /* Greek_CHI Χ GREEK CAPITAL LETTER CHI */ + { 0x07d8, 0x03a8 }, /* Greek_PSI Ψ GREEK CAPITAL LETTER PSI */ + { 0x07d9, 0x03a9 }, /* Greek_OMEGA Ω GREEK CAPITAL LETTER OMEGA */ + { 0x07e1, 0x03b1 }, /* Greek_alpha α GREEK SMALL LETTER ALPHA */ + { 0x07e2, 0x03b2 }, /* Greek_beta β GREEK SMALL LETTER BETA */ + { 0x07e3, 0x03b3 }, /* Greek_gamma γ GREEK SMALL LETTER GAMMA */ + { 0x07e4, 0x03b4 }, /* Greek_delta δ GREEK SMALL LETTER DELTA */ + { 0x07e5, 0x03b5 }, /* Greek_epsilon ε GREEK SMALL LETTER EPSILON */ + { 0x07e6, 0x03b6 }, /* Greek_zeta ζ GREEK SMALL LETTER ZETA */ + { 0x07e7, 0x03b7 }, /* Greek_eta η GREEK SMALL LETTER ETA */ + { 0x07e8, 0x03b8 }, /* Greek_theta θ GREEK SMALL LETTER THETA */ + { 0x07e9, 0x03b9 }, /* Greek_iota ι GREEK SMALL LETTER IOTA */ + { 0x07ea, 0x03ba }, /* Greek_kappa κ GREEK SMALL LETTER KAPPA */ + { 0x07eb, 0x03bb }, /* Greek_lambda λ GREEK SMALL LETTER LAMDA */ + { 0x07ec, 0x03bc }, /* Greek_mu μ GREEK SMALL LETTER MU */ + { 0x07ed, 0x03bd }, /* Greek_nu ν GREEK SMALL LETTER NU */ + { 0x07ee, 0x03be }, /* Greek_xi ξ GREEK SMALL LETTER XI */ + { 0x07ef, 0x03bf }, /* Greek_omicron ο GREEK SMALL LETTER OMICRON */ + { 0x07f0, 0x03c0 }, /* Greek_pi π GREEK SMALL LETTER PI */ + { 0x07f1, 0x03c1 }, /* Greek_rho ρ GREEK SMALL LETTER RHO */ + { 0x07f2, 0x03c3 }, /* Greek_sigma σ GREEK SMALL LETTER SIGMA */ + { 0x07f3, 0x03c2 }, /* Greek_finalsmallsigma ς GREEK SMALL LETTER FINAL SIGMA */ + { 0x07f4, 0x03c4 }, /* Greek_tau τ GREEK SMALL LETTER TAU */ + { 0x07f5, 0x03c5 }, /* Greek_upsilon υ GREEK SMALL LETTER UPSILON */ + { 0x07f6, 0x03c6 }, /* Greek_phi φ GREEK SMALL LETTER PHI */ + { 0x07f7, 0x03c7 }, /* Greek_chi χ GREEK SMALL LETTER CHI */ + { 0x07f8, 0x03c8 }, /* Greek_psi ψ GREEK SMALL LETTER PSI */ + { 0x07f9, 0x03c9 }, /* Greek_omega ω GREEK SMALL LETTER OMEGA */ + { 0x08a1, 0x23b7 }, /* leftradical ⎷ ??? */ + { 0x08a2, 0x250c }, /* topleftradical ┌ BOX DRAWINGS LIGHT DOWN AND RIGHT */ + { 0x08a3, 0x2500 }, /* horizconnector ─ BOX DRAWINGS LIGHT HORIZONTAL */ + { 0x08a4, 0x2320 }, /* topintegral ⌠ TOP HALF INTEGRAL */ + { 0x08a5, 0x2321 }, /* botintegral ⌡ BOTTOM HALF INTEGRAL */ + { 0x08a6, 0x2502 }, /* vertconnector │ BOX DRAWINGS LIGHT VERTICAL */ + { 0x08a7, 0x23a1 }, /* topleftsqbracket ⎡ ??? */ + { 0x08a8, 0x23a3 }, /* botleftsqbracket ⎣ ??? */ + { 0x08a9, 0x23a4 }, /* toprightsqbracket ⎤ ??? */ + { 0x08aa, 0x23a6 }, /* botrightsqbracket ⎦ ??? */ + { 0x08ab, 0x239b }, /* topleftparens ⎛ ??? */ + { 0x08ac, 0x239d }, /* botleftparens ⎝ ??? */ + { 0x08ad, 0x239e }, /* toprightparens ⎞ ??? */ + { 0x08ae, 0x23a0 }, /* botrightparens ⎠ ??? */ + { 0x08af, 0x23a8 }, /* leftmiddlecurlybrace ⎨ ??? */ + { 0x08b0, 0x23ac }, /* rightmiddlecurlybrace ⎬ ??? */ +/* 0x08b1 topleftsummation ? ??? */ +/* 0x08b2 botleftsummation ? ??? */ +/* 0x08b3 topvertsummationconnector ? ??? */ +/* 0x08b4 botvertsummationconnector ? ??? */ +/* 0x08b5 toprightsummation ? ??? */ +/* 0x08b6 botrightsummation ? ??? */ +/* 0x08b7 rightmiddlesummation ? ??? */ + { 0x08bc, 0x2264 }, /* lessthanequal ≤ LESS-THAN OR EQUAL TO */ + { 0x08bd, 0x2260 }, /* notequal ≠ NOT EQUAL TO */ + { 0x08be, 0x2265 }, /* greaterthanequal ≥ GREATER-THAN OR EQUAL TO */ + { 0x08bf, 0x222b }, /* integral ∫ INTEGRAL */ + { 0x08c0, 0x2234 }, /* therefore ∴ THEREFORE */ + { 0x08c1, 0x221d }, /* variation ∝ PROPORTIONAL TO */ + { 0x08c2, 0x221e }, /* infinity ∞ INFINITY */ + { 0x08c5, 0x2207 }, /* nabla ∇ NABLA */ + { 0x08c8, 0x223c }, /* approximate ∼ TILDE OPERATOR */ + { 0x08c9, 0x2243 }, /* similarequal ≃ ASYMPTOTICALLY EQUAL TO */ + { 0x08cd, 0x21d4 }, /* ifonlyif ⇔ LEFT RIGHT DOUBLE ARROW */ + { 0x08ce, 0x21d2 }, /* implies ⇒ RIGHTWARDS DOUBLE ARROW */ + { 0x08cf, 0x2261 }, /* identical ≡ IDENTICAL TO */ + { 0x08d6, 0x221a }, /* radical √ SQUARE ROOT */ + { 0x08da, 0x2282 }, /* includedin ⊂ SUBSET OF */ + { 0x08db, 0x2283 }, /* includes ⊃ SUPERSET OF */ + { 0x08dc, 0x2229 }, /* intersection ∩ INTERSECTION */ + { 0x08dd, 0x222a }, /* union ∪ UNION */ + { 0x08de, 0x2227 }, /* logicaland ∧ LOGICAL AND */ + { 0x08df, 0x2228 }, /* logicalor ∨ LOGICAL OR */ + { 0x08ef, 0x2202 }, /* partialderivative ∂ PARTIAL DIFFERENTIAL */ + { 0x08f6, 0x0192 }, /* function ƒ LATIN SMALL LETTER F WITH HOOK */ + { 0x08fb, 0x2190 }, /* leftarrow ← LEFTWARDS ARROW */ + { 0x08fc, 0x2191 }, /* uparrow ↑ UPWARDS ARROW */ + { 0x08fd, 0x2192 }, /* rightarrow → RIGHTWARDS ARROW */ + { 0x08fe, 0x2193 }, /* downarrow ↓ DOWNWARDS ARROW */ +/* 0x09df blank ? ??? */ + { 0x09e0, 0x25c6 }, /* soliddiamond ◆ BLACK DIAMOND */ + { 0x09e1, 0x2592 }, /* checkerboard ▒ MEDIUM SHADE */ + { 0x09e2, 0x2409 }, /* ht ␉ SYMBOL FOR HORIZONTAL TABULATION */ + { 0x09e3, 0x240c }, /* ff ␌ SYMBOL FOR FORM FEED */ + { 0x09e4, 0x240d }, /* cr ␍ SYMBOL FOR CARRIAGE RETURN */ + { 0x09e5, 0x240a }, /* lf ␊ SYMBOL FOR LINE FEED */ + { 0x09e8, 0x2424 }, /* nl  SYMBOL FOR NEWLINE */ + { 0x09e9, 0x240b }, /* vt ␋ SYMBOL FOR VERTICAL TABULATION */ + { 0x09ea, 0x2518 }, /* lowrightcorner ┘ BOX DRAWINGS LIGHT UP AND LEFT */ + { 0x09eb, 0x2510 }, /* uprightcorner ┐ BOX DRAWINGS LIGHT DOWN AND LEFT */ + { 0x09ec, 0x250c }, /* upleftcorner ┌ BOX DRAWINGS LIGHT DOWN AND RIGHT */ + { 0x09ed, 0x2514 }, /* lowleftcorner └ BOX DRAWINGS LIGHT UP AND RIGHT */ + { 0x09ee, 0x253c }, /* crossinglines ┼ BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */ + { 0x09ef, 0x23ba }, /* horizlinescan1 ⎺ HORIZONTAL SCAN LINE-1 (Unicode 3.2 draft) */ + { 0x09f0, 0x23bb }, /* horizlinescan3 ⎻ HORIZONTAL SCAN LINE-3 (Unicode 3.2 draft) */ + { 0x09f1, 0x2500 }, /* horizlinescan5 ─ BOX DRAWINGS LIGHT HORIZONTAL */ + { 0x09f2, 0x23bc }, /* horizlinescan7 ⎼ HORIZONTAL SCAN LINE-7 (Unicode 3.2 draft) */ + { 0x09f3, 0x23bd }, /* horizlinescan9 ⎽ HORIZONTAL SCAN LINE-9 (Unicode 3.2 draft) */ + { 0x09f4, 0x251c }, /* leftt ├ BOX DRAWINGS LIGHT VERTICAL AND RIGHT */ + { 0x09f5, 0x2524 }, /* rightt ┤ BOX DRAWINGS LIGHT VERTICAL AND LEFT */ + { 0x09f6, 0x2534 }, /* bott ┴ BOX DRAWINGS LIGHT UP AND HORIZONTAL */ + { 0x09f7, 0x252c }, /* topt ┬ BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */ + { 0x09f8, 0x2502 }, /* vertbar │ BOX DRAWINGS LIGHT VERTICAL */ + { 0x0aa1, 0x2003 }, /* emspace EM SPACE */ + { 0x0aa2, 0x2002 }, /* enspace EN SPACE */ + { 0x0aa3, 0x2004 }, /* em3space THREE-PER-EM SPACE */ + { 0x0aa4, 0x2005 }, /* em4space FOUR-PER-EM SPACE */ + { 0x0aa5, 0x2007 }, /* digitspace FIGURE SPACE */ + { 0x0aa6, 0x2008 }, /* punctspace PUNCTUATION SPACE */ + { 0x0aa7, 0x2009 }, /* thinspace THIN SPACE */ + { 0x0aa8, 0x200a }, /* hairspace HAIR SPACE */ + { 0x0aa9, 0x2014 }, /* emdash — EM DASH */ + { 0x0aaa, 0x2013 }, /* endash – EN DASH */ +/* 0x0aac signifblank ? ??? */ + { 0x0aae, 0x2026 }, /* ellipsis … HORIZONTAL ELLIPSIS */ + { 0x0aaf, 0x2025 }, /* doubbaselinedot ‥ TWO DOT LEADER */ + { 0x0ab0, 0x2153 }, /* onethird ⅓ VULGAR FRACTION ONE THIRD */ + { 0x0ab1, 0x2154 }, /* twothirds ⅔ VULGAR FRACTION TWO THIRDS */ + { 0x0ab2, 0x2155 }, /* onefifth ⅕ VULGAR FRACTION ONE FIFTH */ + { 0x0ab3, 0x2156 }, /* twofifths ⅖ VULGAR FRACTION TWO FIFTHS */ + { 0x0ab4, 0x2157 }, /* threefifths ⅗ VULGAR FRACTION THREE FIFTHS */ + { 0x0ab5, 0x2158 }, /* fourfifths ⅘ VULGAR FRACTION FOUR FIFTHS */ + { 0x0ab6, 0x2159 }, /* onesixth ⅙ VULGAR FRACTION ONE SIXTH */ + { 0x0ab7, 0x215a }, /* fivesixths ⅚ VULGAR FRACTION FIVE SIXTHS */ + { 0x0ab8, 0x2105 }, /* careof ℅ CARE OF */ + { 0x0abb, 0x2012 }, /* figdash ‒ FIGURE DASH */ + { 0x0abc, 0x2329 }, /* leftanglebracket 〈 LEFT-POINTING ANGLE BRACKET */ +/* 0x0abd decimalpoint ? ??? */ + { 0x0abe, 0x232a }, /* rightanglebracket 〉 RIGHT-POINTING ANGLE BRACKET */ +/* 0x0abf marker ? ??? */ + { 0x0ac3, 0x215b }, /* oneeighth ⅛ VULGAR FRACTION ONE EIGHTH */ + { 0x0ac4, 0x215c }, /* threeeighths ⅜ VULGAR FRACTION THREE EIGHTHS */ + { 0x0ac5, 0x215d }, /* fiveeighths ⅝ VULGAR FRACTION FIVE EIGHTHS */ + { 0x0ac6, 0x215e }, /* seveneighths ⅞ VULGAR FRACTION SEVEN EIGHTHS */ + { 0x0ac9, 0x2122 }, /* trademark ™ TRADE MARK SIGN */ + { 0x0aca, 0x2613 }, /* signaturemark ☓ SALTIRE */ +/* 0x0acb trademarkincircle ? ??? */ + { 0x0acc, 0x25c1 }, /* leftopentriangle ◁ WHITE LEFT-POINTING TRIANGLE */ + { 0x0acd, 0x25b7 }, /* rightopentriangle ▷ WHITE RIGHT-POINTING TRIANGLE */ + { 0x0ace, 0x25cb }, /* emopencircle ○ WHITE CIRCLE */ + { 0x0acf, 0x25af }, /* emopenrectangle ▯ WHITE VERTICAL RECTANGLE */ + { 0x0ad0, 0x2018 }, /* leftsinglequotemark ‘ LEFT SINGLE QUOTATION MARK */ + { 0x0ad1, 0x2019 }, /* rightsinglequotemark ’ RIGHT SINGLE QUOTATION MARK */ + { 0x0ad2, 0x201c }, /* leftdoublequotemark “ LEFT DOUBLE QUOTATION MARK */ + { 0x0ad3, 0x201d }, /* rightdoublequotemark ” RIGHT DOUBLE QUOTATION MARK */ + { 0x0ad4, 0x211e }, /* prescription ℞ PRESCRIPTION TAKE */ + { 0x0ad6, 0x2032 }, /* minutes ′ PRIME */ + { 0x0ad7, 0x2033 }, /* seconds ″ DOUBLE PRIME */ + { 0x0ad9, 0x271d }, /* latincross ✝ LATIN CROSS */ +/* 0x0ada hexagram ? ??? */ + { 0x0adb, 0x25ac }, /* filledrectbullet ▬ BLACK RECTANGLE */ + { 0x0adc, 0x25c0 }, /* filledlefttribullet ◀ BLACK LEFT-POINTING TRIANGLE */ + { 0x0add, 0x25b6 }, /* filledrighttribullet ▶ BLACK RIGHT-POINTING TRIANGLE */ + { 0x0ade, 0x25cf }, /* emfilledcircle ● BLACK CIRCLE */ + { 0x0adf, 0x25ae }, /* emfilledrect ▮ BLACK VERTICAL RECTANGLE */ + { 0x0ae0, 0x25e6 }, /* enopencircbullet ◦ WHITE BULLET */ + { 0x0ae1, 0x25ab }, /* enopensquarebullet ▫ WHITE SMALL SQUARE */ + { 0x0ae2, 0x25ad }, /* openrectbullet ▭ WHITE RECTANGLE */ + { 0x0ae3, 0x25b3 }, /* opentribulletup △ WHITE UP-POINTING TRIANGLE */ + { 0x0ae4, 0x25bd }, /* opentribulletdown ▽ WHITE DOWN-POINTING TRIANGLE */ + { 0x0ae5, 0x2606 }, /* openstar ☆ WHITE STAR */ + { 0x0ae6, 0x2022 }, /* enfilledcircbullet • BULLET */ + { 0x0ae7, 0x25aa }, /* enfilledsqbullet ▪ BLACK SMALL SQUARE */ + { 0x0ae8, 0x25b2 }, /* filledtribulletup ▲ BLACK UP-POINTING TRIANGLE */ + { 0x0ae9, 0x25bc }, /* filledtribulletdown ▼ BLACK DOWN-POINTING TRIANGLE */ + { 0x0aea, 0x261c }, /* leftpointer ☜ WHITE LEFT POINTING INDEX */ + { 0x0aeb, 0x261e }, /* rightpointer ☞ WHITE RIGHT POINTING INDEX */ + { 0x0aec, 0x2663 }, /* club ♣ BLACK CLUB SUIT */ + { 0x0aed, 0x2666 }, /* diamond ♦ BLACK DIAMOND SUIT */ + { 0x0aee, 0x2665 }, /* heart ♥ BLACK HEART SUIT */ + { 0x0af0, 0x2720 }, /* maltesecross ✠ MALTESE CROSS */ + { 0x0af1, 0x2020 }, /* dagger † DAGGER */ + { 0x0af2, 0x2021 }, /* doubledagger ‡ DOUBLE DAGGER */ + { 0x0af3, 0x2713 }, /* checkmark ✓ CHECK MARK */ + { 0x0af4, 0x2717 }, /* ballotcross ✗ BALLOT X */ + { 0x0af5, 0x266f }, /* musicalsharp ♯ MUSIC SHARP SIGN */ + { 0x0af6, 0x266d }, /* musicalflat ♭ MUSIC FLAT SIGN */ + { 0x0af7, 0x2642 }, /* malesymbol ♂ MALE SIGN */ + { 0x0af8, 0x2640 }, /* femalesymbol ♀ FEMALE SIGN */ + { 0x0af9, 0x260e }, /* telephone ☎ BLACK TELEPHONE */ + { 0x0afa, 0x2315 }, /* telephonerecorder ⌕ TELEPHONE RECORDER */ + { 0x0afb, 0x2117 }, /* phonographcopyright ℗ SOUND RECORDING COPYRIGHT */ + { 0x0afc, 0x2038 }, /* caret ‸ CARET */ + { 0x0afd, 0x201a }, /* singlelowquotemark ‚ SINGLE LOW-9 QUOTATION MARK */ + { 0x0afe, 0x201e }, /* doublelowquotemark „ DOUBLE LOW-9 QUOTATION MARK */ +/* 0x0aff cursor ? ??? */ + { 0x0ba3, 0x003c }, /* leftcaret < LESS-THAN SIGN */ + { 0x0ba6, 0x003e }, /* rightcaret > GREATER-THAN SIGN */ + { 0x0ba8, 0x2228 }, /* downcaret ∨ LOGICAL OR */ + { 0x0ba9, 0x2227 }, /* upcaret ∧ LOGICAL AND */ + { 0x0bc0, 0x00af }, /* overbar ¯ MACRON */ + { 0x0bc2, 0x22a5 }, /* downtack ⊥ UP TACK */ + { 0x0bc3, 0x2229 }, /* upshoe ∩ INTERSECTION */ + { 0x0bc4, 0x230a }, /* downstile ⌊ LEFT FLOOR */ + { 0x0bc6, 0x005f }, /* underbar _ LOW LINE */ + { 0x0bca, 0x2218 }, /* jot ∘ RING OPERATOR */ + { 0x0bcc, 0x2395 }, /* quad ⎕ APL FUNCTIONAL SYMBOL QUAD */ + { 0x0bce, 0x22a4 }, /* uptack ⊤ DOWN TACK */ + { 0x0bcf, 0x25cb }, /* circle ○ WHITE CIRCLE */ + { 0x0bd3, 0x2308 }, /* upstile ⌈ LEFT CEILING */ + { 0x0bd6, 0x222a }, /* downshoe ∪ UNION */ + { 0x0bd8, 0x2283 }, /* rightshoe ⊃ SUPERSET OF */ + { 0x0bda, 0x2282 }, /* leftshoe ⊂ SUBSET OF */ + { 0x0bdc, 0x22a2 }, /* lefttack ⊢ RIGHT TACK */ + { 0x0bfc, 0x22a3 }, /* righttack ⊣ LEFT TACK */ + { 0x0cdf, 0x2017 }, /* hebrew_doublelowline ‗ DOUBLE LOW LINE */ + { 0x0ce0, 0x05d0 }, /* hebrew_aleph א HEBREW LETTER ALEF */ + { 0x0ce1, 0x05d1 }, /* hebrew_bet ב HEBREW LETTER BET */ + { 0x0ce2, 0x05d2 }, /* hebrew_gimel ג HEBREW LETTER GIMEL */ + { 0x0ce3, 0x05d3 }, /* hebrew_dalet ד HEBREW LETTER DALET */ + { 0x0ce4, 0x05d4 }, /* hebrew_he ה HEBREW LETTER HE */ + { 0x0ce5, 0x05d5 }, /* hebrew_waw ו HEBREW LETTER VAV */ + { 0x0ce6, 0x05d6 }, /* hebrew_zain ז HEBREW LETTER ZAYIN */ + { 0x0ce7, 0x05d7 }, /* hebrew_chet ח HEBREW LETTER HET */ + { 0x0ce8, 0x05d8 }, /* hebrew_tet ט HEBREW LETTER TET */ + { 0x0ce9, 0x05d9 }, /* hebrew_yod י HEBREW LETTER YOD */ + { 0x0cea, 0x05da }, /* hebrew_finalkaph ך HEBREW LETTER FINAL KAF */ + { 0x0ceb, 0x05db }, /* hebrew_kaph כ HEBREW LETTER KAF */ + { 0x0cec, 0x05dc }, /* hebrew_lamed ל HEBREW LETTER LAMED */ + { 0x0ced, 0x05dd }, /* hebrew_finalmem ם HEBREW LETTER FINAL MEM */ + { 0x0cee, 0x05de }, /* hebrew_mem מ HEBREW LETTER MEM */ + { 0x0cef, 0x05df }, /* hebrew_finalnun ן HEBREW LETTER FINAL NUN */ + { 0x0cf0, 0x05e0 }, /* hebrew_nun נ HEBREW LETTER NUN */ + { 0x0cf1, 0x05e1 }, /* hebrew_samech ס HEBREW LETTER SAMEKH */ + { 0x0cf2, 0x05e2 }, /* hebrew_ayin ע HEBREW LETTER AYIN */ + { 0x0cf3, 0x05e3 }, /* hebrew_finalpe ף HEBREW LETTER FINAL PE */ + { 0x0cf4, 0x05e4 }, /* hebrew_pe פ HEBREW LETTER PE */ + { 0x0cf5, 0x05e5 }, /* hebrew_finalzade ץ HEBREW LETTER FINAL TSADI */ + { 0x0cf6, 0x05e6 }, /* hebrew_zade צ HEBREW LETTER TSADI */ + { 0x0cf7, 0x05e7 }, /* hebrew_qoph ק HEBREW LETTER QOF */ + { 0x0cf8, 0x05e8 }, /* hebrew_resh ר HEBREW LETTER RESH */ + { 0x0cf9, 0x05e9 }, /* hebrew_shin ש HEBREW LETTER SHIN */ + { 0x0cfa, 0x05ea }, /* hebrew_taw ת HEBREW LETTER TAV */ + { 0x0da1, 0x0e01 }, /* Thai_kokai ก THAI CHARACTER KO KAI */ + { 0x0da2, 0x0e02 }, /* Thai_khokhai ข THAI CHARACTER KHO KHAI */ + { 0x0da3, 0x0e03 }, /* Thai_khokhuat ฃ THAI CHARACTER KHO KHUAT */ + { 0x0da4, 0x0e04 }, /* Thai_khokhwai ค THAI CHARACTER KHO KHWAI */ + { 0x0da5, 0x0e05 }, /* Thai_khokhon ฅ THAI CHARACTER KHO KHON */ + { 0x0da6, 0x0e06 }, /* Thai_khorakhang ฆ THAI CHARACTER KHO RAKHANG */ + { 0x0da7, 0x0e07 }, /* Thai_ngongu ง THAI CHARACTER NGO NGU */ + { 0x0da8, 0x0e08 }, /* Thai_chochan จ THAI CHARACTER CHO CHAN */ + { 0x0da9, 0x0e09 }, /* Thai_choching ฉ THAI CHARACTER CHO CHING */ + { 0x0daa, 0x0e0a }, /* Thai_chochang ช THAI CHARACTER CHO CHANG */ + { 0x0dab, 0x0e0b }, /* Thai_soso ซ THAI CHARACTER SO SO */ + { 0x0dac, 0x0e0c }, /* Thai_chochoe ฌ THAI CHARACTER CHO CHOE */ + { 0x0dad, 0x0e0d }, /* Thai_yoying ญ THAI CHARACTER YO YING */ + { 0x0dae, 0x0e0e }, /* Thai_dochada ฎ THAI CHARACTER DO CHADA */ + { 0x0daf, 0x0e0f }, /* Thai_topatak ฏ THAI CHARACTER TO PATAK */ + { 0x0db0, 0x0e10 }, /* Thai_thothan ฐ THAI CHARACTER THO THAN */ + { 0x0db1, 0x0e11 }, /* Thai_thonangmontho ฑ THAI CHARACTER THO NANGMONTHO */ + { 0x0db2, 0x0e12 }, /* Thai_thophuthao ฒ THAI CHARACTER THO PHUTHAO */ + { 0x0db3, 0x0e13 }, /* Thai_nonen ณ THAI CHARACTER NO NEN */ + { 0x0db4, 0x0e14 }, /* Thai_dodek ด THAI CHARACTER DO DEK */ + { 0x0db5, 0x0e15 }, /* Thai_totao ต THAI CHARACTER TO TAO */ + { 0x0db6, 0x0e16 }, /* Thai_thothung ถ THAI CHARACTER THO THUNG */ + { 0x0db7, 0x0e17 }, /* Thai_thothahan ท THAI CHARACTER THO THAHAN */ + { 0x0db8, 0x0e18 }, /* Thai_thothong ธ THAI CHARACTER THO THONG */ + { 0x0db9, 0x0e19 }, /* Thai_nonu น THAI CHARACTER NO NU */ + { 0x0dba, 0x0e1a }, /* Thai_bobaimai บ THAI CHARACTER BO BAIMAI */ + { 0x0dbb, 0x0e1b }, /* Thai_popla ป THAI CHARACTER PO PLA */ + { 0x0dbc, 0x0e1c }, /* Thai_phophung ผ THAI CHARACTER PHO PHUNG */ + { 0x0dbd, 0x0e1d }, /* Thai_fofa ฝ THAI CHARACTER FO FA */ + { 0x0dbe, 0x0e1e }, /* Thai_phophan พ THAI CHARACTER PHO PHAN */ + { 0x0dbf, 0x0e1f }, /* Thai_fofan ฟ THAI CHARACTER FO FAN */ + { 0x0dc0, 0x0e20 }, /* Thai_phosamphao ภ THAI CHARACTER PHO SAMPHAO */ + { 0x0dc1, 0x0e21 }, /* Thai_moma ม THAI CHARACTER MO MA */ + { 0x0dc2, 0x0e22 }, /* Thai_yoyak ย THAI CHARACTER YO YAK */ + { 0x0dc3, 0x0e23 }, /* Thai_rorua ร THAI CHARACTER RO RUA */ + { 0x0dc4, 0x0e24 }, /* Thai_ru ฤ THAI CHARACTER RU */ + { 0x0dc5, 0x0e25 }, /* Thai_loling ล THAI CHARACTER LO LING */ + { 0x0dc6, 0x0e26 }, /* Thai_lu ฦ THAI CHARACTER LU */ + { 0x0dc7, 0x0e27 }, /* Thai_wowaen ว THAI CHARACTER WO WAEN */ + { 0x0dc8, 0x0e28 }, /* Thai_sosala ศ THAI CHARACTER SO SALA */ + { 0x0dc9, 0x0e29 }, /* Thai_sorusi ษ THAI CHARACTER SO RUSI */ + { 0x0dca, 0x0e2a }, /* Thai_sosua ส THAI CHARACTER SO SUA */ + { 0x0dcb, 0x0e2b }, /* Thai_hohip ห THAI CHARACTER HO HIP */ + { 0x0dcc, 0x0e2c }, /* Thai_lochula ฬ THAI CHARACTER LO CHULA */ + { 0x0dcd, 0x0e2d }, /* Thai_oang อ THAI CHARACTER O ANG */ + { 0x0dce, 0x0e2e }, /* Thai_honokhuk ฮ THAI CHARACTER HO NOKHUK */ + { 0x0dcf, 0x0e2f }, /* Thai_paiyannoi ฯ THAI CHARACTER PAIYANNOI */ + { 0x0dd0, 0x0e30 }, /* Thai_saraa ะ THAI CHARACTER SARA A */ + { 0x0dd1, 0x0e31 }, /* Thai_maihanakat ั THAI CHARACTER MAI HAN-AKAT */ + { 0x0dd2, 0x0e32 }, /* Thai_saraaa า THAI CHARACTER SARA AA */ + { 0x0dd3, 0x0e33 }, /* Thai_saraam ำ THAI CHARACTER SARA AM */ + { 0x0dd4, 0x0e34 }, /* Thai_sarai ิ THAI CHARACTER SARA I */ + { 0x0dd5, 0x0e35 }, /* Thai_saraii ี THAI CHARACTER SARA II */ + { 0x0dd6, 0x0e36 }, /* Thai_saraue ึ THAI CHARACTER SARA UE */ + { 0x0dd7, 0x0e37 }, /* Thai_sarauee ื THAI CHARACTER SARA UEE */ + { 0x0dd8, 0x0e38 }, /* Thai_sarau ุ THAI CHARACTER SARA U */ + { 0x0dd9, 0x0e39 }, /* Thai_sarauu ู THAI CHARACTER SARA UU */ + { 0x0dda, 0x0e3a }, /* Thai_phinthu ฺ THAI CHARACTER PHINTHU */ +/* 0x0dde Thai_maihanakat_maitho ? ??? */ + { 0x0ddf, 0x0e3f }, /* Thai_baht ฿ THAI CURRENCY SYMBOL BAHT */ + { 0x0de0, 0x0e40 }, /* Thai_sarae เ THAI CHARACTER SARA E */ + { 0x0de1, 0x0e41 }, /* Thai_saraae แ THAI CHARACTER SARA AE */ + { 0x0de2, 0x0e42 }, /* Thai_sarao โ THAI CHARACTER SARA O */ + { 0x0de3, 0x0e43 }, /* Thai_saraaimaimuan ใ THAI CHARACTER SARA AI MAIMUAN */ + { 0x0de4, 0x0e44 }, /* Thai_saraaimaimalai ไ THAI CHARACTER SARA AI MAIMALAI */ + { 0x0de5, 0x0e45 }, /* Thai_lakkhangyao ๅ THAI CHARACTER LAKKHANGYAO */ + { 0x0de6, 0x0e46 }, /* Thai_maiyamok ๆ THAI CHARACTER MAIYAMOK */ + { 0x0de7, 0x0e47 }, /* Thai_maitaikhu ็ THAI CHARACTER MAITAIKHU */ + { 0x0de8, 0x0e48 }, /* Thai_maiek ่ THAI CHARACTER MAI EK */ + { 0x0de9, 0x0e49 }, /* Thai_maitho ้ THAI CHARACTER MAI THO */ + { 0x0dea, 0x0e4a }, /* Thai_maitri ๊ THAI CHARACTER MAI TRI */ + { 0x0deb, 0x0e4b }, /* Thai_maichattawa ๋ THAI CHARACTER MAI CHATTAWA */ + { 0x0dec, 0x0e4c }, /* Thai_thanthakhat ์ THAI CHARACTER THANTHAKHAT */ + { 0x0ded, 0x0e4d }, /* Thai_nikhahit ํ THAI CHARACTER NIKHAHIT */ + { 0x0df0, 0x0e50 }, /* Thai_leksun ๐ THAI DIGIT ZERO */ + { 0x0df1, 0x0e51 }, /* Thai_leknung ๑ THAI DIGIT ONE */ + { 0x0df2, 0x0e52 }, /* Thai_leksong ๒ THAI DIGIT TWO */ + { 0x0df3, 0x0e53 }, /* Thai_leksam ๓ THAI DIGIT THREE */ + { 0x0df4, 0x0e54 }, /* Thai_leksi ๔ THAI DIGIT FOUR */ + { 0x0df5, 0x0e55 }, /* Thai_lekha ๕ THAI DIGIT FIVE */ + { 0x0df6, 0x0e56 }, /* Thai_lekhok ๖ THAI DIGIT SIX */ + { 0x0df7, 0x0e57 }, /* Thai_lekchet ๗ THAI DIGIT SEVEN */ + { 0x0df8, 0x0e58 }, /* Thai_lekpaet ๘ THAI DIGIT EIGHT */ + { 0x0df9, 0x0e59 }, /* Thai_lekkao ๙ THAI DIGIT NINE */ + { 0x0ea1, 0x3131 }, /* Hangul_Kiyeog ㄱ HANGUL LETTER KIYEOK */ + { 0x0ea2, 0x3132 }, /* Hangul_SsangKiyeog ㄲ HANGUL LETTER SSANGKIYEOK */ + { 0x0ea3, 0x3133 }, /* Hangul_KiyeogSios ㄳ HANGUL LETTER KIYEOK-SIOS */ + { 0x0ea4, 0x3134 }, /* Hangul_Nieun ㄴ HANGUL LETTER NIEUN */ + { 0x0ea5, 0x3135 }, /* Hangul_NieunJieuj ㄵ HANGUL LETTER NIEUN-CIEUC */ + { 0x0ea6, 0x3136 }, /* Hangul_NieunHieuh ㄶ HANGUL LETTER NIEUN-HIEUH */ + { 0x0ea7, 0x3137 }, /* Hangul_Dikeud ㄷ HANGUL LETTER TIKEUT */ + { 0x0ea8, 0x3138 }, /* Hangul_SsangDikeud ㄸ HANGUL LETTER SSANGTIKEUT */ + { 0x0ea9, 0x3139 }, /* Hangul_Rieul ㄹ HANGUL LETTER RIEUL */ + { 0x0eaa, 0x313a }, /* Hangul_RieulKiyeog ㄺ HANGUL LETTER RIEUL-KIYEOK */ + { 0x0eab, 0x313b }, /* Hangul_RieulMieum ㄻ HANGUL LETTER RIEUL-MIEUM */ + { 0x0eac, 0x313c }, /* Hangul_RieulPieub ㄼ HANGUL LETTER RIEUL-PIEUP */ + { 0x0ead, 0x313d }, /* Hangul_RieulSios ㄽ HANGUL LETTER RIEUL-SIOS */ + { 0x0eae, 0x313e }, /* Hangul_RieulTieut ㄾ HANGUL LETTER RIEUL-THIEUTH */ + { 0x0eaf, 0x313f }, /* Hangul_RieulPhieuf ㄿ HANGUL LETTER RIEUL-PHIEUPH */ + { 0x0eb0, 0x3140 }, /* Hangul_RieulHieuh ㅀ HANGUL LETTER RIEUL-HIEUH */ + { 0x0eb1, 0x3141 }, /* Hangul_Mieum ㅁ HANGUL LETTER MIEUM */ + { 0x0eb2, 0x3142 }, /* Hangul_Pieub ㅂ HANGUL LETTER PIEUP */ + { 0x0eb3, 0x3143 }, /* Hangul_SsangPieub ㅃ HANGUL LETTER SSANGPIEUP */ + { 0x0eb4, 0x3144 }, /* Hangul_PieubSios ㅄ HANGUL LETTER PIEUP-SIOS */ + { 0x0eb5, 0x3145 }, /* Hangul_Sios ㅅ HANGUL LETTER SIOS */ + { 0x0eb6, 0x3146 }, /* Hangul_SsangSios ㅆ HANGUL LETTER SSANGSIOS */ + { 0x0eb7, 0x3147 }, /* Hangul_Ieung ㅇ HANGUL LETTER IEUNG */ + { 0x0eb8, 0x3148 }, /* Hangul_Jieuj ㅈ HANGUL LETTER CIEUC */ + { 0x0eb9, 0x3149 }, /* Hangul_SsangJieuj ㅉ HANGUL LETTER SSANGCIEUC */ + { 0x0eba, 0x314a }, /* Hangul_Cieuc ㅊ HANGUL LETTER CHIEUCH */ + { 0x0ebb, 0x314b }, /* Hangul_Khieuq ㅋ HANGUL LETTER KHIEUKH */ + { 0x0ebc, 0x314c }, /* Hangul_Tieut ㅌ HANGUL LETTER THIEUTH */ + { 0x0ebd, 0x314d }, /* Hangul_Phieuf ㅍ HANGUL LETTER PHIEUPH */ + { 0x0ebe, 0x314e }, /* Hangul_Hieuh ㅎ HANGUL LETTER HIEUH */ + { 0x0ebf, 0x314f }, /* Hangul_A ㅏ HANGUL LETTER A */ + { 0x0ec0, 0x3150 }, /* Hangul_AE ㅐ HANGUL LETTER AE */ + { 0x0ec1, 0x3151 }, /* Hangul_YA ㅑ HANGUL LETTER YA */ + { 0x0ec2, 0x3152 }, /* Hangul_YAE ㅒ HANGUL LETTER YAE */ + { 0x0ec3, 0x3153 }, /* Hangul_EO ㅓ HANGUL LETTER EO */ + { 0x0ec4, 0x3154 }, /* Hangul_E ㅔ HANGUL LETTER E */ + { 0x0ec5, 0x3155 }, /* Hangul_YEO ㅕ HANGUL LETTER YEO */ + { 0x0ec6, 0x3156 }, /* Hangul_YE ㅖ HANGUL LETTER YE */ + { 0x0ec7, 0x3157 }, /* Hangul_O ㅗ HANGUL LETTER O */ + { 0x0ec8, 0x3158 }, /* Hangul_WA ㅘ HANGUL LETTER WA */ + { 0x0ec9, 0x3159 }, /* Hangul_WAE ㅙ HANGUL LETTER WAE */ + { 0x0eca, 0x315a }, /* Hangul_OE ㅚ HANGUL LETTER OE */ + { 0x0ecb, 0x315b }, /* Hangul_YO ㅛ HANGUL LETTER YO */ + { 0x0ecc, 0x315c }, /* Hangul_U ㅜ HANGUL LETTER U */ + { 0x0ecd, 0x315d }, /* Hangul_WEO ㅝ HANGUL LETTER WEO */ + { 0x0ece, 0x315e }, /* Hangul_WE ㅞ HANGUL LETTER WE */ + { 0x0ecf, 0x315f }, /* Hangul_WI ㅟ HANGUL LETTER WI */ + { 0x0ed0, 0x3160 }, /* Hangul_YU ㅠ HANGUL LETTER YU */ + { 0x0ed1, 0x3161 }, /* Hangul_EU ㅡ HANGUL LETTER EU */ + { 0x0ed2, 0x3162 }, /* Hangul_YI ㅢ HANGUL LETTER YI */ + { 0x0ed3, 0x3163 }, /* Hangul_I ㅣ HANGUL LETTER I */ + { 0x0ed4, 0x11a8 }, /* Hangul_J_Kiyeog ᆨ HANGUL JONGSEONG KIYEOK */ + { 0x0ed5, 0x11a9 }, /* Hangul_J_SsangKiyeog ᆩ HANGUL JONGSEONG SSANGKIYEOK */ + { 0x0ed6, 0x11aa }, /* Hangul_J_KiyeogSios ᆪ HANGUL JONGSEONG KIYEOK-SIOS */ + { 0x0ed7, 0x11ab }, /* Hangul_J_Nieun ᆫ HANGUL JONGSEONG NIEUN */ + { 0x0ed8, 0x11ac }, /* Hangul_J_NieunJieuj ᆬ HANGUL JONGSEONG NIEUN-CIEUC */ + { 0x0ed9, 0x11ad }, /* Hangul_J_NieunHieuh ᆭ HANGUL JONGSEONG NIEUN-HIEUH */ + { 0x0eda, 0x11ae }, /* Hangul_J_Dikeud ᆮ HANGUL JONGSEONG TIKEUT */ + { 0x0edb, 0x11af }, /* Hangul_J_Rieul ᆯ HANGUL JONGSEONG RIEUL */ + { 0x0edc, 0x11b0 }, /* Hangul_J_RieulKiyeog ᆰ HANGUL JONGSEONG RIEUL-KIYEOK */ + { 0x0edd, 0x11b1 }, /* Hangul_J_RieulMieum ᆱ HANGUL JONGSEONG RIEUL-MIEUM */ + { 0x0ede, 0x11b2 }, /* Hangul_J_RieulPieub ᆲ HANGUL JONGSEONG RIEUL-PIEUP */ + { 0x0edf, 0x11b3 }, /* Hangul_J_RieulSios ᆳ HANGUL JONGSEONG RIEUL-SIOS */ + { 0x0ee0, 0x11b4 }, /* Hangul_J_RieulTieut ᆴ HANGUL JONGSEONG RIEUL-THIEUTH */ + { 0x0ee1, 0x11b5 }, /* Hangul_J_RieulPhieuf ᆵ HANGUL JONGSEONG RIEUL-PHIEUPH */ + { 0x0ee2, 0x11b6 }, /* Hangul_J_RieulHieuh ᆶ HANGUL JONGSEONG RIEUL-HIEUH */ + { 0x0ee3, 0x11b7 }, /* Hangul_J_Mieum ᆷ HANGUL JONGSEONG MIEUM */ + { 0x0ee4, 0x11b8 }, /* Hangul_J_Pieub ᆸ HANGUL JONGSEONG PIEUP */ + { 0x0ee5, 0x11b9 }, /* Hangul_J_PieubSios ᆹ HANGUL JONGSEONG PIEUP-SIOS */ + { 0x0ee6, 0x11ba }, /* Hangul_J_Sios ᆺ HANGUL JONGSEONG SIOS */ + { 0x0ee7, 0x11bb }, /* Hangul_J_SsangSios ᆻ HANGUL JONGSEONG SSANGSIOS */ + { 0x0ee8, 0x11bc }, /* Hangul_J_Ieung ᆼ HANGUL JONGSEONG IEUNG */ + { 0x0ee9, 0x11bd }, /* Hangul_J_Jieuj ᆽ HANGUL JONGSEONG CIEUC */ + { 0x0eea, 0x11be }, /* Hangul_J_Cieuc ᆾ HANGUL JONGSEONG CHIEUCH */ + { 0x0eeb, 0x11bf }, /* Hangul_J_Khieuq ᆿ HANGUL JONGSEONG KHIEUKH */ + { 0x0eec, 0x11c0 }, /* Hangul_J_Tieut ᇀ HANGUL JONGSEONG THIEUTH */ + { 0x0eed, 0x11c1 }, /* Hangul_J_Phieuf ᇁ HANGUL JONGSEONG PHIEUPH */ + { 0x0eee, 0x11c2 }, /* Hangul_J_Hieuh ᇂ HANGUL JONGSEONG HIEUH */ + { 0x0eef, 0x316d }, /* Hangul_RieulYeorinHieuh ㅭ HANGUL LETTER RIEUL-YEORINHIEUH */ + { 0x0ef0, 0x3171 }, /* Hangul_SunkyeongeumMieum ㅱ HANGUL LETTER KAPYEOUNMIEUM */ + { 0x0ef1, 0x3178 }, /* Hangul_SunkyeongeumPieub ㅸ HANGUL LETTER KAPYEOUNPIEUP */ + { 0x0ef2, 0x317f }, /* Hangul_PanSios ㅿ HANGUL LETTER PANSIOS */ + { 0x0ef3, 0x3181 }, /* Hangul_KkogjiDalrinIeung ㆁ HANGUL LETTER YESIEUNG */ + { 0x0ef4, 0x3184 }, /* Hangul_SunkyeongeumPhieuf ㆄ HANGUL LETTER KAPYEOUNPHIEUPH */ + { 0x0ef5, 0x3186 }, /* Hangul_YeorinHieuh ㆆ HANGUL LETTER YEORINHIEUH */ + { 0x0ef6, 0x318d }, /* Hangul_AraeA ㆍ HANGUL LETTER ARAEA */ + { 0x0ef7, 0x318e }, /* Hangul_AraeAE ㆎ HANGUL LETTER ARAEAE */ + { 0x0ef8, 0x11eb }, /* Hangul_J_PanSios ᇫ HANGUL JONGSEONG PANSIOS */ + { 0x0ef9, 0x11f0 }, /* Hangul_J_KkogjiDalrinIeung ᇰ HANGUL JONGSEONG YESIEUNG */ + { 0x0efa, 0x11f9 }, /* Hangul_J_YeorinHieuh ᇹ HANGUL JONGSEONG YEORINHIEUH */ + { 0x0eff, 0x20a9 }, /* Korean_Won ₩ WON SIGN */ + { 0x13a4, 0x20ac }, /* Euro € EURO SIGN */ + { 0x13bc, 0x0152 }, /* OE Œ LATIN CAPITAL LIGATURE OE */ + { 0x13bd, 0x0153 }, /* oe œ LATIN SMALL LIGATURE OE */ + { 0x13be, 0x0178 }, /* Ydiaeresis Ÿ LATIN CAPITAL LETTER Y WITH DIAERESIS */ + { 0x20ac, 0x20ac }, /* EuroSign € EURO SIGN */ +}; + +VISIBLE +long keysym2ucs(KeySym keysym) +{ + int min = 0; + int max = sizeof(keysymtab) / sizeof(struct codepair) - 1; + int mid; + + /* first check for Latin-1 characters (1:1 mapping) */ + if ((keysym >= 0x0020 && keysym <= 0x007e) || + (keysym >= 0x00a0 && keysym <= 0x00ff)) + return keysym; + + /* also check for directly encoded 24-bit UCS characters */ + if ((keysym & 0xff000000) == 0x01000000) + return keysym & 0x00ffffff; + + /* binary search in table */ + while (max >= min) { + mid = (min + max) / 2; + if (keysymtab[mid].keysym < keysym) + min = mid + 1; + else if (keysymtab[mid].keysym > keysym) + max = mid - 1; + else { + /* found it */ + return keysymtab[mid].ucs; + } + } + + /* no matching Unicode value found */ + return -1; +} |
