This commit is contained in:
Anish Athalye
2018-10-24 20:44:45 -04:00
parent da1f8392b1
commit c67463e23c
71 changed files with 4734 additions and 18 deletions

View File

@@ -16,7 +16,17 @@ LIB_SRCFILES := $(LIB_SRCFILES) \
lib/fork.c \
lib/ipc.c
LIB_SRCFILES := $(LIB_SRCFILES) \
lib/args.c \
lib/fd.c \
lib/file.c \
lib/fprintf.c \
lib/pageref.c \
lib/spawn.c
LIB_SRCFILES := $(LIB_SRCFILES) \
lib/pipe.c \
lib/wait.c
LIB_OBJFILES := $(patsubst lib/%.c, $(OBJDIR)/lib/%.o, $(LIB_SRCFILES))
LIB_OBJFILES := $(patsubst lib/%.S, $(OBJDIR)/lib/%.o, $(LIB_OBJFILES))

73
lib/args.c Normal file
View File

@@ -0,0 +1,73 @@
#include <inc/args.h>
#include <inc/string.h>
void
argstart(int *argc, char **argv, struct Argstate *args)
{
args->argc = argc;
args->argv = (const char **) argv;
args->curarg = (*argc > 1 && argv ? "" : 0);
args->argvalue = 0;
}
int
argnext(struct Argstate *args)
{
int arg;
args->argvalue = 0;
// Done processing arguments if args->curarg == 0
if (args->curarg == 0)
return -1;
if (!*args->curarg) {
// Need to process the next argument
// Check for end of argument list
if (*args->argc == 1
|| args->argv[1][0] != '-'
|| args->argv[1][1] == '\0')
goto endofargs;
// Shift arguments down one
args->curarg = args->argv[1] + 1;
memmove(args->argv + 1, args->argv + 2, sizeof(const char *) * (*args->argc - 1));
(*args->argc)--;
// Check for "--": end of argument list
if (args->curarg[0] == '-' && args->curarg[1] == '\0')
goto endofargs;
}
arg = (unsigned char) *args->curarg;
args->curarg++;
return arg;
endofargs:
args->curarg = 0;
return -1;
}
char *
argvalue(struct Argstate *args)
{
return (char*) (args->argvalue ? args->argvalue : argnextvalue(args));
}
char *
argnextvalue(struct Argstate *args)
{
if (!args->curarg)
return 0;
if (*args->curarg) {
args->argvalue = args->curarg;
args->curarg = "";
} else if (*args->argc > 1) {
args->argvalue = args->argv[1];
memmove(args->argv + 1, args->argv + 2, sizeof(const char *) * (*args->argc - 1));
(*args->argc)--;
} else {
args->argvalue = 0;
args->curarg = 0;
}
return (char*) args->argvalue;
}

View File

@@ -15,11 +15,114 @@ cputchar(int ch)
int
getchar(void)
{
unsigned char c;
int r;
// sys_cgetc does not block, but getchar should.
while ((r = sys_cgetc()) == 0)
sys_yield();
return r;
// JOS does, however, support standard _input_ redirection,
// allowing the user to redirect script files to the shell and such.
// getchar() reads a character from file descriptor 0.
r = read(0, &c, 1);
if (r < 0)
return r;
if (r < 1)
return -E_EOF;
return c;
}
// "Real" console file descriptor implementation.
// The putchar/getchar functions above will still come here by default,
// but now can be redirected to files, pipes, etc., via the fd layer.
static ssize_t devcons_read(struct Fd*, void*, size_t);
static ssize_t devcons_write(struct Fd*, const void*, size_t);
static int devcons_close(struct Fd*);
static int devcons_stat(struct Fd*, struct Stat*);
struct Dev devcons =
{
.dev_id = 'c',
.dev_name = "cons",
.dev_read = devcons_read,
.dev_write = devcons_write,
.dev_close = devcons_close,
.dev_stat = devcons_stat
};
int
iscons(int fdnum)
{
int r;
struct Fd *fd;
if ((r = fd_lookup(fdnum, &fd)) < 0)
return r;
return fd->fd_dev_id == devcons.dev_id;
}
int
opencons(void)
{
int r;
struct Fd* fd;
if ((r = fd_alloc(&fd)) < 0)
return r;
if ((r = sys_page_alloc(0, fd, PTE_P|PTE_U|PTE_W|PTE_SHARE)) < 0)
return r;
fd->fd_dev_id = devcons.dev_id;
fd->fd_omode = O_RDWR;
return fd2num(fd);
}
static ssize_t
devcons_read(struct Fd *fd, void *vbuf, size_t n)
{
int c;
if (n == 0)
return 0;
while ((c = sys_cgetc()) == 0)
sys_yield();
if (c < 0)
return c;
if (c == 0x04) // ctl-d is eof
return 0;
*(char*)vbuf = c;
return 1;
}
static ssize_t
devcons_write(struct Fd *fd, const void *vbuf, size_t n)
{
int tot, m;
char buf[128];
// mistake: have to nul-terminate arg to sys_cputs,
// so we have to copy vbuf into buf in chunks and nul-terminate.
for (tot = 0; tot < n; tot += m) {
m = n - tot;
if (m > sizeof(buf) - 1)
m = sizeof(buf) - 1;
memmove(buf, (char*)vbuf + tot, m);
sys_cputs(buf, m);
}
return tot;
}
static int
devcons_close(struct Fd *fd)
{
USED(fd);
return 0;
}
static int
devcons_stat(struct Fd *fd, struct Stat *stat)
{
strcpy(stat->st_name, "<cons>");
return 0;
}

View File

@@ -4,6 +4,7 @@
void
exit(void)
{
close_all();
sys_env_destroy(0);
}

320
lib/fd.c Normal file
View File

@@ -0,0 +1,320 @@
#include <inc/lib.h>
#define debug 0
// Maximum number of file descriptors a program may hold open concurrently
#define MAXFD 32
// Bottom of file descriptor area
#define FDTABLE 0xD0000000
// Bottom of file data area. We reserve one data page for each FD,
// which devices can use if they choose.
#define FILEDATA (FDTABLE + MAXFD*PGSIZE)
// Return the 'struct Fd*' for file descriptor index i
#define INDEX2FD(i) ((struct Fd*) (FDTABLE + (i)*PGSIZE))
// Return the file data page for file descriptor index i
#define INDEX2DATA(i) ((char*) (FILEDATA + (i)*PGSIZE))
// --------------------------------------------------------------
// File descriptor manipulators
// --------------------------------------------------------------
int
fd2num(struct Fd *fd)
{
return ((uintptr_t) fd - FDTABLE) / PGSIZE;
}
char*
fd2data(struct Fd *fd)
{
return INDEX2DATA(fd2num(fd));
}
// Finds the smallest i from 0 to MAXFD-1 that doesn't have
// its fd page mapped.
// Sets *fd_store to the corresponding fd page virtual address.
//
// fd_alloc does NOT actually allocate an fd page.
// It is up to the caller to allocate the page somehow.
// This means that if someone calls fd_alloc twice in a row
// without allocating the first page we return, we'll return the same
// page the second time.
//
// Hint: Use INDEX2FD.
//
// Returns 0 on success, < 0 on error. Errors are:
// -E_MAX_FD: no more file descriptors
// On error, *fd_store is set to 0.
int
fd_alloc(struct Fd **fd_store)
{
int i;
struct Fd *fd;
for (i = 0; i < MAXFD; i++) {
fd = INDEX2FD(i);
if ((uvpd[PDX(fd)] & PTE_P) == 0 || (uvpt[PGNUM(fd)] & PTE_P) == 0) {
*fd_store = fd;
return 0;
}
}
*fd_store = 0;
return -E_MAX_OPEN;
}
// Check that fdnum is in range and mapped.
// If it is, set *fd_store to the fd page virtual address.
//
// Returns 0 on success (the page is in range and mapped), < 0 on error.
// Errors are:
// -E_INVAL: fdnum was either not in range or not mapped.
int
fd_lookup(int fdnum, struct Fd **fd_store)
{
struct Fd *fd;
if (fdnum < 0 || fdnum >= MAXFD) {
if (debug)
cprintf("[%08x] bad fd %d\n", thisenv->env_id, fdnum);
return -E_INVAL;
}
fd = INDEX2FD(fdnum);
if (!(uvpd[PDX(fd)] & PTE_P) || !(uvpt[PGNUM(fd)] & PTE_P)) {
if (debug)
cprintf("[%08x] closed fd %d\n", thisenv->env_id, fdnum);
return -E_INVAL;
}
*fd_store = fd;
return 0;
}
// Frees file descriptor 'fd' by closing the corresponding file
// and unmapping the file descriptor page.
// If 'must_exist' is 0, then fd can be a closed or nonexistent file
// descriptor; the function will return 0 and have no other effect.
// If 'must_exist' is 1, then fd_close returns -E_INVAL when passed a
// closed or nonexistent file descriptor.
// Returns 0 on success, < 0 on error.
int
fd_close(struct Fd *fd, bool must_exist)
{
struct Fd *fd2;
struct Dev *dev;
int r;
if ((r = fd_lookup(fd2num(fd), &fd2)) < 0
|| fd != fd2)
return (must_exist ? r : 0);
if ((r = dev_lookup(fd->fd_dev_id, &dev)) >= 0) {
if (dev->dev_close)
r = (*dev->dev_close)(fd);
else
r = 0;
}
// Make sure fd is unmapped. Might be a no-op if
// (*dev->dev_close)(fd) already unmapped it.
(void) sys_page_unmap(0, fd);
return r;
}
// --------------------------------------------------------------
// File functions
// --------------------------------------------------------------
static struct Dev *devtab[] =
{
&devfile,
&devpipe,
&devcons,
0
};
int
dev_lookup(int dev_id, struct Dev **dev)
{
int i;
for (i = 0; devtab[i]; i++)
if (devtab[i]->dev_id == dev_id) {
*dev = devtab[i];
return 0;
}
cprintf("[%08x] unknown device type %d\n", thisenv->env_id, dev_id);
*dev = 0;
return -E_INVAL;
}
int
close(int fdnum)
{
struct Fd *fd;
int r;
if ((r = fd_lookup(fdnum, &fd)) < 0)
return r;
else
return fd_close(fd, 1);
}
void
close_all(void)
{
int i;
for (i = 0; i < MAXFD; i++)
close(i);
}
// Make file descriptor 'newfdnum' a duplicate of file descriptor 'oldfdnum'.
// For instance, writing onto either file descriptor will affect the
// file and the file offset of the other.
// Closes any previously open file descriptor at 'newfdnum'.
// This is implemented using virtual memory tricks (of course!).
int
dup(int oldfdnum, int newfdnum)
{
int r;
char *ova, *nva;
pte_t pte;
struct Fd *oldfd, *newfd;
if ((r = fd_lookup(oldfdnum, &oldfd)) < 0)
return r;
close(newfdnum);
newfd = INDEX2FD(newfdnum);
ova = fd2data(oldfd);
nva = fd2data(newfd);
if ((uvpd[PDX(ova)] & PTE_P) && (uvpt[PGNUM(ova)] & PTE_P))
if ((r = sys_page_map(0, ova, 0, nva, uvpt[PGNUM(ova)] & PTE_SYSCALL)) < 0)
goto err;
if ((r = sys_page_map(0, oldfd, 0, newfd, uvpt[PGNUM(oldfd)] & PTE_SYSCALL)) < 0)
goto err;
return newfdnum;
err:
sys_page_unmap(0, newfd);
sys_page_unmap(0, nva);
return r;
}
ssize_t
read(int fdnum, void *buf, size_t n)
{
int r;
struct Dev *dev;
struct Fd *fd;
if ((r = fd_lookup(fdnum, &fd)) < 0
|| (r = dev_lookup(fd->fd_dev_id, &dev)) < 0)
return r;
if ((fd->fd_omode & O_ACCMODE) == O_WRONLY) {
cprintf("[%08x] read %d -- bad mode\n", thisenv->env_id, fdnum);
return -E_INVAL;
}
if (!dev->dev_read)
return -E_NOT_SUPP;
return (*dev->dev_read)(fd, buf, n);
}
ssize_t
readn(int fdnum, void *buf, size_t n)
{
int m, tot;
for (tot = 0; tot < n; tot += m) {
m = read(fdnum, (char*)buf + tot, n - tot);
if (m < 0)
return m;
if (m == 0)
break;
}
return tot;
}
ssize_t
write(int fdnum, const void *buf, size_t n)
{
int r;
struct Dev *dev;
struct Fd *fd;
if ((r = fd_lookup(fdnum, &fd)) < 0
|| (r = dev_lookup(fd->fd_dev_id, &dev)) < 0)
return r;
if ((fd->fd_omode & O_ACCMODE) == O_RDONLY) {
cprintf("[%08x] write %d -- bad mode\n", thisenv->env_id, fdnum);
return -E_INVAL;
}
if (debug)
cprintf("write %d %p %d via dev %s\n",
fdnum, buf, n, dev->dev_name);
if (!dev->dev_write)
return -E_NOT_SUPP;
return (*dev->dev_write)(fd, buf, n);
}
int
seek(int fdnum, off_t offset)
{
int r;
struct Fd *fd;
if ((r = fd_lookup(fdnum, &fd)) < 0)
return r;
fd->fd_offset = offset;
return 0;
}
int
ftruncate(int fdnum, off_t newsize)
{
int r;
struct Dev *dev;
struct Fd *fd;
if ((r = fd_lookup(fdnum, &fd)) < 0
|| (r = dev_lookup(fd->fd_dev_id, &dev)) < 0)
return r;
if ((fd->fd_omode & O_ACCMODE) == O_RDONLY) {
cprintf("[%08x] ftruncate %d -- bad mode\n",
thisenv->env_id, fdnum);
return -E_INVAL;
}
if (!dev->dev_trunc)
return -E_NOT_SUPP;
return (*dev->dev_trunc)(fd, newsize);
}
int
fstat(int fdnum, struct Stat *stat)
{
int r;
struct Dev *dev;
struct Fd *fd;
if ((r = fd_lookup(fdnum, &fd)) < 0
|| (r = dev_lookup(fd->fd_dev_id, &dev)) < 0)
return r;
if (!dev->dev_stat)
return -E_NOT_SUPP;
stat->st_name[0] = 0;
stat->st_size = 0;
stat->st_isdir = 0;
stat->st_dev = dev;
return (*dev->dev_stat)(fd, stat);
}
int
stat(const char *path, struct Stat *stat)
{
int fd, r;
if ((fd = open(path, O_RDONLY)) < 0)
return fd;
r = fstat(fd, stat);
close(fd);
return r;
}

180
lib/file.c Normal file
View File

@@ -0,0 +1,180 @@
#include <inc/fs.h>
#include <inc/string.h>
#include <inc/lib.h>
#define debug 0
union Fsipc fsipcbuf __attribute__((aligned(PGSIZE)));
// Send an inter-environment request to the file server, and wait for
// a reply. The request body should be in fsipcbuf, and parts of the
// response may be written back to fsipcbuf.
// type: request code, passed as the simple integer IPC value.
// dstva: virtual address at which to receive reply page, 0 if none.
// Returns result from the file server.
static int
fsipc(unsigned type, void *dstva)
{
static envid_t fsenv;
if (fsenv == 0)
fsenv = ipc_find_env(ENV_TYPE_FS);
static_assert(sizeof(fsipcbuf) == PGSIZE);
if (debug)
cprintf("[%08x] fsipc %d %08x\n", thisenv->env_id, type, *(uint32_t *)&fsipcbuf);
ipc_send(fsenv, type, &fsipcbuf, PTE_P | PTE_W | PTE_U);
return ipc_recv(NULL, dstva, NULL);
}
static int devfile_flush(struct Fd *fd);
static ssize_t devfile_read(struct Fd *fd, void *buf, size_t n);
static ssize_t devfile_write(struct Fd *fd, const void *buf, size_t n);
static int devfile_stat(struct Fd *fd, struct Stat *stat);
static int devfile_trunc(struct Fd *fd, off_t newsize);
struct Dev devfile =
{
.dev_id = 'f',
.dev_name = "file",
.dev_read = devfile_read,
.dev_close = devfile_flush,
.dev_stat = devfile_stat,
.dev_write = devfile_write,
.dev_trunc = devfile_trunc
};
// Open a file (or directory).
//
// Returns:
// The file descriptor index on success
// -E_BAD_PATH if the path is too long (>= MAXPATHLEN)
// < 0 for other errors.
int
open(const char *path, int mode)
{
// Find an unused file descriptor page using fd_alloc.
// Then send a file-open request to the file server.
// Include 'path' and 'omode' in request,
// and map the returned file descriptor page
// at the appropriate fd address.
// FSREQ_OPEN returns 0 on success, < 0 on failure.
//
// (fd_alloc does not allocate a page, it just returns an
// unused fd address. Do you need to allocate a page?)
//
// Return the file descriptor index.
// If any step after fd_alloc fails, use fd_close to free the
// file descriptor.
int r;
struct Fd *fd;
if (strlen(path) >= MAXPATHLEN)
return -E_BAD_PATH;
if ((r = fd_alloc(&fd)) < 0)
return r;
strcpy(fsipcbuf.open.req_path, path);
fsipcbuf.open.req_omode = mode;
if ((r = fsipc(FSREQ_OPEN, fd)) < 0) {
fd_close(fd, 0);
return r;
}
return fd2num(fd);
}
// Flush the file descriptor. After this the fileid is invalid.
//
// This function is called by fd_close. fd_close will take care of
// unmapping the FD page from this environment. Since the server uses
// the reference counts on the FD pages to detect which files are
// open, unmapping it is enough to free up server-side resources.
// Other than that, we just have to make sure our changes are flushed
// to disk.
static int
devfile_flush(struct Fd *fd)
{
fsipcbuf.flush.req_fileid = fd->fd_file.id;
return fsipc(FSREQ_FLUSH, NULL);
}
// Read at most 'n' bytes from 'fd' at the current position into 'buf'.
//
// Returns:
// The number of bytes successfully read.
// < 0 on error.
static ssize_t
devfile_read(struct Fd *fd, void *buf, size_t n)
{
// Make an FSREQ_READ request to the file system server after
// filling fsipcbuf.read with the request arguments. The
// bytes read will be written back to fsipcbuf by the file
// system server.
int r;
fsipcbuf.read.req_fileid = fd->fd_file.id;
fsipcbuf.read.req_n = n;
if ((r = fsipc(FSREQ_READ, NULL)) < 0)
return r;
assert(r <= n);
assert(r <= PGSIZE);
memmove(buf, fsipcbuf.readRet.ret_buf, r);
return r;
}
// Write at most 'n' bytes from 'buf' to 'fd' at the current seek position.
//
// Returns:
// The number of bytes successfully written.
// < 0 on error.
static ssize_t
devfile_write(struct Fd *fd, const void *buf, size_t n)
{
// Make an FSREQ_WRITE request to the file system server. Be
// careful: fsipcbuf.write.req_buf is only so large, but
// remember that write is always allowed to write *fewer*
// bytes than requested.
// LAB 5: Your code here
panic("devfile_write not implemented");
}
static int
devfile_stat(struct Fd *fd, struct Stat *st)
{
int r;
fsipcbuf.stat.req_fileid = fd->fd_file.id;
if ((r = fsipc(FSREQ_STAT, NULL)) < 0)
return r;
strcpy(st->st_name, fsipcbuf.statRet.ret_name);
st->st_size = fsipcbuf.statRet.ret_size;
st->st_isdir = fsipcbuf.statRet.ret_isdir;
return 0;
}
// Truncate or extend an open file to 'size' bytes
static int
devfile_trunc(struct Fd *fd, off_t newsize)
{
fsipcbuf.set_size.req_fileid = fd->fd_file.id;
fsipcbuf.set_size.req_size = newsize;
return fsipc(FSREQ_SET_SIZE, NULL);
}
// Synchronize disk with buffer cache
int
sync(void)
{
// Ask the file server to update the disk
// by writing any dirty blocks in the buffer cache.
return fsipc(FSREQ_SYNC, NULL);
}

81
lib/fprintf.c Normal file
View File

@@ -0,0 +1,81 @@
#include <inc/lib.h>
// Collect up to 256 characters into a buffer
// and perform ONE system call to print all of them,
// in order to make the lines output to the console atomic
// and prevent interrupts from causing context switches
// in the middle of a console output line and such.
struct printbuf {
int fd; // file descriptor
int idx; // current buffer index
ssize_t result; // accumulated results from write
int error; // first error that occurred
char buf[256];
};
static void
writebuf(struct printbuf *b)
{
if (b->error > 0) {
ssize_t result = write(b->fd, b->buf, b->idx);
if (result > 0)
b->result += result;
if (result != b->idx) // error, or wrote less than supplied
b->error = (result < 0 ? result : 0);
}
}
static void
putch(int ch, void *thunk)
{
struct printbuf *b = (struct printbuf *) thunk;
b->buf[b->idx++] = ch;
if (b->idx == 256) {
writebuf(b);
b->idx = 0;
}
}
int
vfprintf(int fd, const char *fmt, va_list ap)
{
struct printbuf b;
b.fd = fd;
b.idx = 0;
b.result = 0;
b.error = 1;
vprintfmt(putch, &b, fmt, ap);
if (b.idx > 0)
writebuf(&b);
return (b.result ? b.result : b.error);
}
int
fprintf(int fd, const char *fmt, ...)
{
va_list ap;
int cnt;
va_start(ap, fmt);
cnt = vfprintf(fd, fmt, ap);
va_end(ap);
return cnt;
}
int
printf(const char *fmt, ...)
{
va_list ap;
int cnt;
va_start(ap, fmt);
cnt = vfprintf(1, fmt, ap);
va_end(ap);
return cnt;
}

14
lib/pageref.c Normal file
View File

@@ -0,0 +1,14 @@
#include <inc/lib.h>
int
pageref(void *v)
{
pte_t pte;
if (!(uvpd[PDX(v)] & PTE_P))
return 0;
pte = uvpt[PGNUM(v)];
if (!(pte & PTE_P))
return 0;
return pages[PGNUM(pte)].pp_ref;
}

191
lib/pipe.c Normal file
View File

@@ -0,0 +1,191 @@
#include <inc/lib.h>
#define debug 0
static ssize_t devpipe_read(struct Fd *fd, void *buf, size_t n);
static ssize_t devpipe_write(struct Fd *fd, const void *buf, size_t n);
static int devpipe_stat(struct Fd *fd, struct Stat *stat);
static int devpipe_close(struct Fd *fd);
struct Dev devpipe =
{
.dev_id = 'p',
.dev_name = "pipe",
.dev_read = devpipe_read,
.dev_write = devpipe_write,
.dev_close = devpipe_close,
.dev_stat = devpipe_stat,
};
#define PIPEBUFSIZ 32 // small to provoke races
struct Pipe {
off_t p_rpos; // read position
off_t p_wpos; // write position
uint8_t p_buf[PIPEBUFSIZ]; // data buffer
};
int
pipe(int pfd[2])
{
int r;
struct Fd *fd0, *fd1;
void *va;
// allocate the file descriptor table entries
if ((r = fd_alloc(&fd0)) < 0
|| (r = sys_page_alloc(0, fd0, PTE_P|PTE_W|PTE_U|PTE_SHARE)) < 0)
goto err;
if ((r = fd_alloc(&fd1)) < 0
|| (r = sys_page_alloc(0, fd1, PTE_P|PTE_W|PTE_U|PTE_SHARE)) < 0)
goto err1;
// allocate the pipe structure as first data page in both
va = fd2data(fd0);
if ((r = sys_page_alloc(0, va, PTE_P|PTE_W|PTE_U|PTE_SHARE)) < 0)
goto err2;
if ((r = sys_page_map(0, va, 0, fd2data(fd1), PTE_P|PTE_W|PTE_U|PTE_SHARE)) < 0)
goto err3;
// set up fd structures
fd0->fd_dev_id = devpipe.dev_id;
fd0->fd_omode = O_RDONLY;
fd1->fd_dev_id = devpipe.dev_id;
fd1->fd_omode = O_WRONLY;
if (debug)
cprintf("[%08x] pipecreate %08x\n", thisenv->env_id, uvpt[PGNUM(va)]);
pfd[0] = fd2num(fd0);
pfd[1] = fd2num(fd1);
return 0;
err3:
sys_page_unmap(0, va);
err2:
sys_page_unmap(0, fd1);
err1:
sys_page_unmap(0, fd0);
err:
return r;
}
static int
_pipeisclosed(struct Fd *fd, struct Pipe *p)
{
int n, nn, ret;
while (1) {
n = thisenv->env_runs;
ret = pageref(fd) == pageref(p);
nn = thisenv->env_runs;
if (n == nn)
return ret;
if (n != nn && ret == 1)
cprintf("pipe race avoided\n", n, thisenv->env_runs, ret);
}
}
int
pipeisclosed(int fdnum)
{
struct Fd *fd;
struct Pipe *p;
int r;
if ((r = fd_lookup(fdnum, &fd)) < 0)
return r;
p = (struct Pipe*) fd2data(fd);
return _pipeisclosed(fd, p);
}
static ssize_t
devpipe_read(struct Fd *fd, void *vbuf, size_t n)
{
uint8_t *buf;
size_t i;
struct Pipe *p;
p = (struct Pipe*)fd2data(fd);
if (debug)
cprintf("[%08x] devpipe_read %08x %d rpos %d wpos %d\n",
thisenv->env_id, uvpt[PGNUM(p)], n, p->p_rpos, p->p_wpos);
buf = vbuf;
for (i = 0; i < n; i++) {
while (p->p_rpos == p->p_wpos) {
// pipe is empty
// if we got any data, return it
if (i > 0)
return i;
// if all the writers are gone, note eof
if (_pipeisclosed(fd, p))
return 0;
// yield and see what happens
if (debug)
cprintf("devpipe_read yield\n");
sys_yield();
}
// there's a byte. take it.
// wait to increment rpos until the byte is taken!
buf[i] = p->p_buf[p->p_rpos % PIPEBUFSIZ];
p->p_rpos++;
}
return i;
}
static ssize_t
devpipe_write(struct Fd *fd, const void *vbuf, size_t n)
{
const uint8_t *buf;
size_t i;
struct Pipe *p;
p = (struct Pipe*) fd2data(fd);
if (debug)
cprintf("[%08x] devpipe_write %08x %d rpos %d wpos %d\n",
thisenv->env_id, uvpt[PGNUM(p)], n, p->p_rpos, p->p_wpos);
buf = vbuf;
for (i = 0; i < n; i++) {
while (p->p_wpos >= p->p_rpos + sizeof(p->p_buf)) {
// pipe is full
// if all the readers are gone
// (it's only writers like us now),
// note eof
if (_pipeisclosed(fd, p))
return 0;
// yield and see what happens
if (debug)
cprintf("devpipe_write yield\n");
sys_yield();
}
// there's room for a byte. store it.
// wait to increment wpos until the byte is stored!
p->p_buf[p->p_wpos % PIPEBUFSIZ] = buf[i];
p->p_wpos++;
}
return i;
}
static int
devpipe_stat(struct Fd *fd, struct Stat *stat)
{
struct Pipe *p = (struct Pipe*) fd2data(fd);
strcpy(stat->st_name, "<pipe>");
stat->st_size = p->p_wpos - p->p_rpos;
stat->st_isdir = 0;
stat->st_dev = &devpipe;
return 0;
}
static int
devpipe_close(struct Fd *fd)
{
(void) sys_page_unmap(0, fd);
return sys_page_unmap(0, fd2data(fd));
}

View File

@@ -28,6 +28,13 @@ static const char * const error_string[MAXERROR] =
[E_FAULT] = "segmentation fault",
[E_IPC_NOT_RECV]= "env is not recving",
[E_EOF] = "unexpected end of file",
[E_NO_DISK] = "no free space on disk",
[E_MAX_OPEN] = "too many files are open",
[E_NOT_FOUND] = "file or block not found",
[E_BAD_PATH] = "invalid path",
[E_FILE_EXISTS] = "file already exists",
[E_NOT_EXEC] = "file is not a valid executable",
[E_NOT_SUPP] = "operation not supported",
};
/*

View File

@@ -9,15 +9,21 @@ readline(const char *prompt)
{
int i, c, echoing;
#if JOS_KERNEL
if (prompt != NULL)
cprintf("%s", prompt);
#else
if (prompt != NULL)
fprintf(1, "%s", prompt);
#endif
i = 0;
echoing = iscons(0);
while (1) {
c = getchar();
if (c < 0) {
cprintf("read error: %e\n", c);
if (c != -E_EOF)
cprintf("read error: %e\n", c);
return NULL;
} else if ((c == '\b' || c == '\x7f') && i > 0) {
if (echoing)

307
lib/spawn.c Normal file
View File

@@ -0,0 +1,307 @@
#include <inc/lib.h>
#include <inc/elf.h>
#define UTEMP2USTACK(addr) ((void*) (addr) + (USTACKTOP - PGSIZE) - UTEMP)
#define UTEMP2 (UTEMP + PGSIZE)
#define UTEMP3 (UTEMP2 + PGSIZE)
// Helper functions for spawn.
static int init_stack(envid_t child, const char **argv, uintptr_t *init_esp);
static int map_segment(envid_t child, uintptr_t va, size_t memsz,
int fd, size_t filesz, off_t fileoffset, int perm);
static int copy_shared_pages(envid_t child);
// Spawn a child process from a program image loaded from the file system.
// prog: the pathname of the program to run.
// argv: pointer to null-terminated array of pointers to strings,
// which will be passed to the child as its command-line arguments.
// Returns child envid on success, < 0 on failure.
int
spawn(const char *prog, const char **argv)
{
unsigned char elf_buf[512];
struct Trapframe child_tf;
envid_t child;
int fd, i, r;
struct Elf *elf;
struct Proghdr *ph;
int perm;
// This code follows this procedure:
//
// - Open the program file.
//
// - Read the ELF header, as you have before, and sanity check its
// magic number. (Check out your load_icode!)
//
// - Use sys_exofork() to create a new environment.
//
// - Set child_tf to an initial struct Trapframe for the child.
//
// - Call the init_stack() function above to set up
// the initial stack page for the child environment.
//
// - Map all of the program's segments that are of p_type
// ELF_PROG_LOAD into the new environment's address space.
// Use the p_flags field in the Proghdr for each segment
// to determine how to map the segment:
//
// * If the ELF flags do not include ELF_PROG_FLAG_WRITE,
// then the segment contains text and read-only data.
// Use read_map() to read the contents of this segment,
// and map the pages it returns directly into the child
// so that multiple instances of the same program
// will share the same copy of the program text.
// Be sure to map the program text read-only in the child.
// Read_map is like read but returns a pointer to the data in
// *blk rather than copying the data into another buffer.
//
// * If the ELF segment flags DO include ELF_PROG_FLAG_WRITE,
// then the segment contains read/write data and bss.
// As with load_icode() in Lab 3, such an ELF segment
// occupies p_memsz bytes in memory, but only the FIRST
// p_filesz bytes of the segment are actually loaded
// from the executable file - you must clear the rest to zero.
// For each page to be mapped for a read/write segment,
// allocate a page in the parent temporarily at UTEMP,
// read() the appropriate portion of the file into that page
// and/or use memset() to zero non-loaded portions.
// (You can avoid calling memset(), if you like, if
// page_alloc() returns zeroed pages already.)
// Then insert the page mapping into the child.
// Look at init_stack() for inspiration.
// Be sure you understand why you can't use read_map() here.
//
// Note: None of the segment addresses or lengths above
// are guaranteed to be page-aligned, so you must deal with
// these non-page-aligned values appropriately.
// The ELF linker does, however, guarantee that no two segments
// will overlap on the same page; and it guarantees that
// PGOFF(ph->p_offset) == PGOFF(ph->p_va).
//
// - Call sys_env_set_trapframe(child, &child_tf) to set up the
// correct initial eip and esp values in the child.
//
// - Start the child process running with sys_env_set_status().
if ((r = open(prog, O_RDONLY)) < 0)
return r;
fd = r;
// Read elf header
elf = (struct Elf*) elf_buf;
if (readn(fd, elf_buf, sizeof(elf_buf)) != sizeof(elf_buf)
|| elf->e_magic != ELF_MAGIC) {
close(fd);
cprintf("elf magic %08x want %08x\n", elf->e_magic, ELF_MAGIC);
return -E_NOT_EXEC;
}
// Create new child environment
if ((r = sys_exofork()) < 0)
return r;
child = r;
// Set up trap frame, including initial stack.
child_tf = envs[ENVX(child)].env_tf;
child_tf.tf_eip = elf->e_entry;
if ((r = init_stack(child, argv, &child_tf.tf_esp)) < 0)
return r;
// Set up program segments as defined in ELF header.
ph = (struct Proghdr*) (elf_buf + elf->e_phoff);
for (i = 0; i < elf->e_phnum; i++, ph++) {
if (ph->p_type != ELF_PROG_LOAD)
continue;
perm = PTE_P | PTE_U;
if (ph->p_flags & ELF_PROG_FLAG_WRITE)
perm |= PTE_W;
if ((r = map_segment(child, ph->p_va, ph->p_memsz,
fd, ph->p_filesz, ph->p_offset, perm)) < 0)
goto error;
}
close(fd);
fd = -1;
// Copy shared library state.
if ((r = copy_shared_pages(child)) < 0)
panic("copy_shared_pages: %e", r);
child_tf.tf_eflags |= FL_IOPL_3; // devious: see user/faultio.c
if ((r = sys_env_set_trapframe(child, &child_tf)) < 0)
panic("sys_env_set_trapframe: %e", r);
if ((r = sys_env_set_status(child, ENV_RUNNABLE)) < 0)
panic("sys_env_set_status: %e", r);
return child;
error:
sys_env_destroy(child);
close(fd);
return r;
}
// Spawn, taking command-line arguments array directly on the stack.
// NOTE: Must have a sentinal of NULL at the end of the args
// (none of the args may be NULL).
int
spawnl(const char *prog, const char *arg0, ...)
{
// We calculate argc by advancing the args until we hit NULL.
// The contract of the function guarantees that the last
// argument will always be NULL, and that none of the other
// arguments will be NULL.
int argc=0;
va_list vl;
va_start(vl, arg0);
while(va_arg(vl, void *) != NULL)
argc++;
va_end(vl);
// Now that we have the size of the args, do a second pass
// and store the values in a VLA, which has the format of argv
const char *argv[argc+2];
argv[0] = arg0;
argv[argc+1] = NULL;
va_start(vl, arg0);
unsigned i;
for(i=0;i<argc;i++)
argv[i+1] = va_arg(vl, const char *);
va_end(vl);
return spawn(prog, argv);
}
// Set up the initial stack page for the new child process with envid 'child'
// using the arguments array pointed to by 'argv',
// which is a null-terminated array of pointers to null-terminated strings.
//
// On success, returns 0 and sets *init_esp
// to the initial stack pointer with which the child should start.
// Returns < 0 on failure.
static int
init_stack(envid_t child, const char **argv, uintptr_t *init_esp)
{
size_t string_size;
int argc, i, r;
char *string_store;
uintptr_t *argv_store;
// Count the number of arguments (argc)
// and the total amount of space needed for strings (string_size).
string_size = 0;
for (argc = 0; argv[argc] != 0; argc++)
string_size += strlen(argv[argc]) + 1;
// Determine where to place the strings and the argv array.
// Set up pointers into the temporary page 'UTEMP'; we'll map a page
// there later, then remap that page into the child environment
// at (USTACKTOP - PGSIZE).
// strings is the topmost thing on the stack.
string_store = (char*) UTEMP + PGSIZE - string_size;
// argv is below that. There's one argument pointer per argument, plus
// a null pointer.
argv_store = (uintptr_t*) (ROUNDDOWN(string_store, 4) - 4 * (argc + 1));
// Make sure that argv, strings, and the 2 words that hold 'argc'
// and 'argv' themselves will all fit in a single stack page.
if ((void*) (argv_store - 2) < (void*) UTEMP)
return -E_NO_MEM;
// Allocate the single stack page at UTEMP.
if ((r = sys_page_alloc(0, (void*) UTEMP, PTE_P|PTE_U|PTE_W)) < 0)
return r;
// * Initialize 'argv_store[i]' to point to argument string i,
// for all 0 <= i < argc.
// Also, copy the argument strings from 'argv' into the
// newly-allocated stack page.
//
// * Set 'argv_store[argc]' to 0 to null-terminate the args array.
//
// * Push two more words onto the child's stack below 'args',
// containing the argc and argv parameters to be passed
// to the child's umain() function.
// argv should be below argc on the stack.
// (Again, argv should use an address valid in the child's
// environment.)
//
// * Set *init_esp to the initial stack pointer for the child,
// (Again, use an address valid in the child's environment.)
for (i = 0; i < argc; i++) {
argv_store[i] = UTEMP2USTACK(string_store);
strcpy(string_store, argv[i]);
string_store += strlen(argv[i]) + 1;
}
argv_store[argc] = 0;
assert(string_store == (char*)UTEMP + PGSIZE);
argv_store[-1] = UTEMP2USTACK(argv_store);
argv_store[-2] = argc;
*init_esp = UTEMP2USTACK(&argv_store[-2]);
// After completing the stack, map it into the child's address space
// and unmap it from ours!
if ((r = sys_page_map(0, UTEMP, child, (void*) (USTACKTOP - PGSIZE), PTE_P | PTE_U | PTE_W)) < 0)
goto error;
if ((r = sys_page_unmap(0, UTEMP)) < 0)
goto error;
return 0;
error:
sys_page_unmap(0, UTEMP);
return r;
}
static int
map_segment(envid_t child, uintptr_t va, size_t memsz,
int fd, size_t filesz, off_t fileoffset, int perm)
{
int i, r;
void *blk;
//cprintf("map_segment %x+%x\n", va, memsz);
if ((i = PGOFF(va))) {
va -= i;
memsz += i;
filesz += i;
fileoffset -= i;
}
for (i = 0; i < memsz; i += PGSIZE) {
if (i >= filesz) {
// allocate a blank page
if ((r = sys_page_alloc(child, (void*) (va + i), perm)) < 0)
return r;
} else {
// from file
if ((r = sys_page_alloc(0, UTEMP, PTE_P|PTE_U|PTE_W)) < 0)
return r;
if ((r = seek(fd, fileoffset + i)) < 0)
return r;
if ((r = readn(fd, UTEMP, MIN(PGSIZE, filesz-i))) < 0)
return r;
if ((r = sys_page_map(0, UTEMP, child, (void*) (va + i), perm)) < 0)
panic("spawn: sys_page_map data: %e", r);
sys_page_unmap(0, UTEMP);
}
}
return 0;
}
// Copy the mappings for shared pages into the child address space.
static int
copy_shared_pages(envid_t child)
{
// LAB 5: Your code here.
return 0;
}

View File

@@ -93,6 +93,12 @@ sys_env_set_status(envid_t envid, int status)
return syscall(SYS_env_set_status, 1, envid, status, 0, 0, 0);
}
int
sys_env_set_trapframe(envid_t envid, struct Trapframe *tf)
{
return syscall(SYS_env_set_trapframe, 1, envid, (uint32_t) tf, 0, 0, 0);
}
int
sys_env_set_pgfault_upcall(envid_t envid, void *upcall)
{

13
lib/wait.c Normal file
View File

@@ -0,0 +1,13 @@
#include <inc/lib.h>
// Waits until 'envid' exits.
void
wait(envid_t envid)
{
const volatile struct Env *e;
assert(envid != 0);
e = &envs[ENVX(envid)];
while (e->env_id == envid && e->env_status != ENV_FREE)
sys_yield();
}