Lab 5
This commit is contained in:
10
lib/Makefrag
10
lib/Makefrag
@@ -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
73
lib/args.c
Normal 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;
|
||||
}
|
||||
|
||||
111
lib/console.c
111
lib/console.c
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
void
|
||||
exit(void)
|
||||
{
|
||||
close_all();
|
||||
sys_env_destroy(0);
|
||||
}
|
||||
|
||||
|
||||
320
lib/fd.c
Normal file
320
lib/fd.c
Normal 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
180
lib/file.c
Normal 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
81
lib/fprintf.c
Normal 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
14
lib/pageref.c
Normal 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
191
lib/pipe.c
Normal 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));
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -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
307
lib/spawn.c
Normal 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;
|
||||
}
|
||||
|
||||
@@ -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
13
lib/wait.c
Normal 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();
|
||||
}
|
||||
Reference in New Issue
Block a user