Add execve syscall.

This commit is contained in:
Lu Sitong 2021-04-22 15:21:00 +08:00
parent 27951515a2
commit fc4f8c56b2
15 changed files with 251 additions and 188 deletions

View File

@ -200,16 +200,14 @@ UPROGS=\
$U/_find\
$U/_rm\
$U/_wc\
$U/_test\
$U/_info\
$U/_usertests\
$U/_strace\
$U/_mv\
# $U/_forktest\
# $U/_ln\
# $U/_stressfs\
# $U/_grind\
# $U/_zombie\
$U/_test\
$U/_grind\
$U/_forktest\
$U/_stressfs\
userprogs: $(UPROGS)

View File

@ -3,8 +3,6 @@
#include "include/param.h"
#include "include/memlayout.h"
#include "include/riscv.h"
#include "include/spinlock.h"
#include "include/sleeplock.h"
#include "include/proc.h"
#include "include/elf.h"
#include "include/fat32.h"
@ -12,6 +10,7 @@
#include "include/vm.h"
#include "include/printf.h"
#include "include/string.h"
#include "include/syscall.h"
// Load a program segment into pagetable at virtual address va.
// va must be page-aligned
@ -40,30 +39,17 @@ loadseg(pagetable_t pagetable, uint64 va, struct dirent *ep, uint offset, uint s
return 0;
}
int exec(char *path, char **argv)
// All argvs are pointers came from user space, and should be checked by sys_caller
int execve(char *path, char **argv, char **envp)
{
char *s, *last;
int i, off;
uint64 argc, sz = 0, sp, ustack[MAXARG+1], stackbase;
struct elfhdr elf;
struct dirent *ep;
struct proghdr ph;
pagetable_t pagetable = 0, oldpagetable;
pagetable_t kpagetable = 0, oldkpagetable;
struct dirent *ep = NULL;
pagetable_t pagetable = NULL;
pagetable_t kpagetable = NULL;
char *buf = NULL;
struct proc *p = myproc();
// Make a copy of p->kpt without old user space,
// but with the same kstack we are using now, which can't be changed
if ((kpagetable = (pagetable_t)kalloc()) == NULL) {
return -1;
}
memmove(kpagetable, p->kpagetable, PGSIZE);
for (int i = 0; i < PX(2, MAXUVA); i++) {
kpagetable[i] = 0;
}
if((ep = ename(path)) == NULL) {
if ((buf = kalloc()) == NULL || copyinstr2(buf, (uint64)path, FAT32_MAX_PATH) < 0
|| (ep = ename(buf)) == NULL) {
#ifdef DEBUG
printf("[exec] %s not found\n", path);
#endif
@ -72,70 +58,114 @@ int exec(char *path, char **argv)
elock(ep);
// Check ELF header
if(eread(ep, 0, (uint64) &elf, 0, sizeof(elf)) != sizeof(elf))
struct elfhdr elf;
if (eread(ep, 0, (uint64) &elf, 0, sizeof(elf)) != sizeof(elf))
goto bad;
if(elf.magic != ELF_MAGIC)
goto bad;
if((pagetable = proc_pagetable(p)) == NULL)
if (elf.magic != ELF_MAGIC)
goto bad;
// Make a copy of p->kpt without old user space,
// but with the same kstack we are using now, which can't be changed.
// Load program into memory.
for(i=0, off=elf.phoff; i<elf.phnum; i++, off+=sizeof(ph)){
if(eread(ep, 0, (uint64)&ph, off, sizeof(ph)) != sizeof(ph))
uint64 sz = 0;
if ((pagetable = proc_pagetable(p)) == NULL)
goto bad;
if ((kpagetable = (pagetable_t)kalloc()) == NULL)
goto bad;
memmove(kpagetable, p->kpagetable, PGSIZE);
for (int i = 0; i < PX(2, MAXUVA); i++) {
kpagetable[i] = 0;
}
struct proghdr ph;
for (int i = 0, off = elf.phoff; i < elf.phnum; i++, off += sizeof(ph)) {
if (eread(ep, 0, (uint64)&ph, off, sizeof(ph)) != sizeof(ph))
goto bad;
if(ph.type != ELF_PROG_LOAD)
if (ph.type != ELF_PROG_LOAD)
continue;
if(ph.memsz < ph.filesz)
if (ph.memsz < ph.filesz)
goto bad;
if(ph.vaddr + ph.memsz < ph.vaddr)
if (ph.vaddr + ph.memsz < ph.vaddr)
goto bad;
uint64 sz1;
if((sz1 = uvmalloc(pagetable, kpagetable, sz, ph.vaddr + ph.memsz)) == 0)
if ((sz1 = uvmalloc(pagetable, kpagetable, sz, ph.vaddr + ph.memsz)) == 0)
goto bad;
sz = sz1;
if(ph.vaddr % PGSIZE != 0)
if (ph.vaddr % PGSIZE != 0)
goto bad;
if(loadseg(pagetable, ph.vaddr, ep, ph.off, ph.filesz) < 0)
if (loadseg(pagetable, ph.vaddr, ep, ph.off, ph.filesz) < 0)
goto bad;
}
char pname[16];
safestrcpy(pname, ep->filename, sizeof(pname));
eunlock(ep);
eput(ep);
ep = 0;
p = myproc();
uint64 oldsz = p->sz;
// Allocate two pages at the next page boundary.
// Use the second as the user stack.
sz = PGROUNDUP(sz);
uint64 sz1;
if((sz1 = uvmalloc(pagetable, kpagetable, sz, sz + 2*PGSIZE)) == 0)
if ((sz1 = uvmalloc(pagetable, kpagetable, sz, sz + 2 * PGSIZE)) == 0)
goto bad;
sz = sz1;
uvmclear(pagetable, sz-2*PGSIZE);
sp = sz;
stackbase = sp - PGSIZE;
// Push argument strings, prepare rest of stack in ustack.
for(argc = 0; argv[argc]; argc++) {
if(argc >= MAXARG)
goto bad;
sp -= strlen(argv[argc]) + 1;
sp -= sp % 16; // riscv sp must be 16-byte aligned
if(sp < stackbase)
goto bad;
if(copyout(pagetable, sp, argv[argc], strlen(argv[argc]) + 1) < 0)
goto bad;
ustack[argc] = sp;
}
ustack[argc] = 0;
// push the array of argv[] pointers.
sp -= (argc+1) * sizeof(uint64);
sp -= sp % 16;
if(sp < stackbase)
uvmclear(pagetable, sz - 2 * PGSIZE);
uint64 sp = sz;
uint64 stackbase = sp - PGSIZE;
sp -= sizeof(uint64);
if (copyout(pagetable, sp, (char *)&ep, sizeof(uint64)) < 0) // *ep is 0 now, borrow it
goto bad;
if(copyout(pagetable, sp, (char *)ustack, (argc+1)*sizeof(uint64)) < 0)
// Push env strings
uint64 argc = 0, envc = 0, uargv[MAXARG + 1], uenvp[MAXENV + 1], argp;
while (envp) {
if (envc >= MAXENV || fetchaddr((uint64)(envp + envc), &argp) < 0)
goto bad;
if (argp == 0)
break;
int envlen = fetchstr(argp, buf, PGSIZE); // '\0' included in PGSIZE, but not in envlen
if (envlen++ < 0) // including '\0'
goto bad;
sp -= envlen;
sp -= sp % 16;
if (sp < stackbase)
goto bad;
if (copyout(pagetable, sp, buf, envlen) < 0)
goto bad;
uenvp[envc++] = sp;
}
uenvp[envc] = 0;
// Push envp[] table
sp -= (envc + 1) * sizeof(uint64);
sp -= sp % 16;
if (sp < stackbase)
goto bad;
if (copyout(pagetable, sp, (char *)uenvp, (envc + 1) * sizeof(uint64)) < 0)
goto bad;
p->trapframe->a2 = sp;
// Push argument strings, prepare rest of stack in ustack.
while (argv) {
if (argc >= MAXARG || fetchaddr((uint64)(argv + argc), &argp) < 0)
goto bad;
if (argp == 0)
break;
int argvlen = fetchstr(argp, buf, PGSIZE);
if (argvlen++ < 0)
goto bad;
sp -= argvlen;
sp -= sp % 16; // riscv sp must be 16-byte aligned
if (sp < stackbase)
goto bad;
if (copyout(pagetable, sp, buf, argvlen) < 0)
goto bad;
uargv[argc++] = sp;
}
uargv[argc] = 0;
// push the array of argv[] pointers.
sp -= (argc + 1) * sizeof(uint64);
sp -= sp % 16;
if (sp < stackbase)
goto bad;
if (copyout(pagetable, sp, (char *)uargv, (argc + 1) * sizeof(uint64)) < 0)
goto bad;
// arguments to user main(argc, argv)
@ -144,19 +174,18 @@ int exec(char *path, char **argv)
p->trapframe->a1 = sp;
// Save program name for debugging.
for(last=s=path; *s; s++)
if(*s == '/')
last = s+1;
safestrcpy(p->name, last, sizeof(p->name));
memmove(p->name, pname, sizeof(p->name));
// Commit to the user image.
oldpagetable = p->pagetable;
oldkpagetable = p->kpagetable;
pagetable_t oldpagetable = p->pagetable;
pagetable_t oldkpagetable = p->kpagetable;
uint64 oldsz = p->sz;
p->pagetable = pagetable;
p->kpagetable = kpagetable;
p->sz = sz;
p->trapframe->epc = elf.entry; // initial program counter = main
p->trapframe->sp = sp; // initial stack pointer
kfree(buf);
proc_freepagetable(oldpagetable, oldsz);
w_satp(MAKE_SATP(p->kpagetable));
sfence_vma();
@ -167,11 +196,13 @@ int exec(char *path, char **argv)
#ifdef DEBUG
printf("[exec] reach bad\n");
#endif
if(pagetable)
if (buf)
kfree(buf);
if (pagetable)
proc_freepagetable(pagetable, sz);
if(kpagetable)
if (kpagetable)
kvmfree(kpagetable, 0);
if(ep){
if (ep) {
eunlock(ep);
eput(ep);
}

View File

@ -570,8 +570,7 @@ struct dirent *ealloc(struct dirent *dp, char *name, int attr)
ep->clus_cnt = 0;
ep->cur_clus = 0;
ep->dirty = 0;
strncpy(ep->filename, name, FAT32_MAX_FILENAME);
ep->filename[FAT32_MAX_FILENAME] = '\0';
safestrcpy(ep->filename, name, FAT32_MAX_FILENAME);
if (attr == ATTR_DIRECTORY) { // generate "." and ".." for ep
ep->attribute |= ATTR_DIRECTORY;
ep->cur_clus = ep->first_clus = alloc_clus(dp->dev);
@ -588,11 +587,9 @@ struct dirent *ealloc(struct dirent *dp, char *name, int attr)
struct dirent *edup(struct dirent *entry)
{
if (entry != 0) {
acquire(&ecache.lock);
entry->ref++;
release(&ecache.lock);
}
acquire(&ecache.lock);
entry->ref++;
release(&ecache.lock);
return entry;
}

View File

@ -9,6 +9,7 @@
#define NDEV 10 // maximum major device number
#define ROOTDEV 1 // device number of file system root disk
#define MAXARG 32 // max exec arguments
#define MAXENV 8 // max execve envs
#define MAXOPBLOCKS 10 // max # of blocks any FS op writes
#define LOGSIZE (MAXOPBLOCKS*3) // max data blocks in on-disk log
#define NBUF (MAXOPBLOCKS*3) // size of disk block cache

View File

@ -28,5 +28,6 @@
#define SYS_readdir 24
#define SYS_getcwd 25
#define SYS_rename 26
#define SYS_execve 27
#endif

View File

@ -116,6 +116,7 @@ extern uint64 sys_remove(void);
extern uint64 sys_trace(void);
extern uint64 sys_sysinfo(void);
extern uint64 sys_rename(void);
extern uint64 sys_execve(void);
static uint64 (*syscalls[])(void) = {
[SYS_fork] sys_fork,
@ -144,6 +145,7 @@ static uint64 (*syscalls[])(void) = {
[SYS_trace] sys_trace,
[SYS_sysinfo] sys_sysinfo,
[SYS_rename] sys_rename,
[SYS_execve] sys_execve,
};
static char *sysnames[] = {
@ -173,6 +175,7 @@ static char *sysnames[] = {
[SYS_trace] "trace",
[SYS_sysinfo] "sysinfo",
[SYS_rename] "rename",
[SYS_execve] "execve",
};
void

View File

@ -11,48 +11,67 @@
#include "include/string.h"
#include "include/printf.h"
extern int exec(char *path, char **argv);
extern int execve(char *path, char **argv, char **envp);
uint64
sys_exec(void)
{
char path[FAT32_MAX_PATH], *argv[MAXARG];
int i;
uint64 uargv, uarg;
uint64 path, argv;
if(argstr(0, path, FAT32_MAX_PATH) < 0 || argaddr(1, &uargv) < 0){
if(argaddr(0, &path) < 0 || argaddr(1, &argv) < 0){
return -1;
}
memset(argv, 0, sizeof(argv));
for(i=0;; i++){
if(i >= NELEM(argv)){
goto bad;
}
if(fetchaddr(uargv+sizeof(uint64)*i, (uint64*)&uarg) < 0){
goto bad;
}
if(uarg == 0){
argv[i] = 0;
break;
}
argv[i] = kalloc();
if(argv[i] == 0)
goto bad;
if(fetchstr(uarg, argv[i], PGSIZE) < 0)
goto bad;
return execve((char *)path, (char **)argv, 0);
// char path[FAT32_MAX_PATH], *argv[MAXARG];
// int i;
// uint64 uargv, uarg;
// if(argstr(0, path, FAT32_MAX_PATH) < 0 || argaddr(1, &uargv) < 0){
// return -1;
// }
// memset(argv, 0, sizeof(argv));
// for(i=0;; i++){
// if(i >= NELEM(argv)){
// goto bad;
// }
// if(fetchaddr(uargv+sizeof(uint64)*i, (uint64*)&uarg) < 0){
// goto bad;
// }
// if(uarg == 0){
// argv[i] = 0;
// break;
// }
// argv[i] = kalloc();
// if(argv[i] == 0)
// goto bad;
// if(fetchstr(uarg, argv[i], PGSIZE) < 0)
// goto bad;
// }
// int ret = execve(path, argv, 0);
// for(i = 0; i < NELEM(argv) && argv[i] != 0; i++)
// kfree(argv[i]);
// return ret;
// bad:
// for(i = 0; i < NELEM(argv) && argv[i] != 0; i++)
// kfree(argv[i]);
// return -1;
}
uint64
sys_execve(void)
{
uint64 path, argv, envp;
if(argaddr(0, &path) < 0 || argaddr(1, &argv) < 0 || argaddr(2, &envp)){
return -1;
}
int ret = exec(path, argv);
for(i = 0; i < NELEM(argv) && argv[i] != 0; i++)
kfree(argv[i]);
return ret;
bad:
for(i = 0; i < NELEM(argv) && argv[i] != 0; i++)
kfree(argv[i]);
return -1;
return execve((char *)path, (char **)argv, (char **)envp);
}
uint64

View File

@ -6,7 +6,6 @@
#include "kernel/include/types.h"
#include "kernel/include/stat.h"
#include "xv6-user/user.h"
#include "kernel/include/fs.h"
#include "kernel/include/fcntl.h"
#include "kernel/include/syscall.h"
#include "kernel/include/memlayout.h"
@ -72,13 +71,15 @@ go(int which_child)
} else if(what == 2){
close(open("grindir/../grindir/../b", O_CREATE|O_RDWR));
} else if(what == 3){
unlink("grindir/../a");
// unlink("grindir/../a");
remove("grindir/../a");
} else if(what == 4){
if(chdir("grindir") != 0){
printf("chdir grindir failed\n");
exit(1);
}
unlink("../b");
// unlink("../b");
remove("../b");
chdir("/");
} else if(what == 5){
close(fd);
@ -93,17 +94,19 @@ go(int which_child)
} else if(what == 9){
mkdir("grindir/../a");
close(open("a/../a/./a", O_CREATE|O_RDWR));
unlink("a/a");
// unlink("a/a");
remove("a/a");
} else if(what == 10){
mkdir("/../b");
close(open("grindir/../b/b", O_CREATE|O_RDWR));
unlink("b/b");
// unlink("b/b");
remove("b/b");
} else if(what == 11){
unlink("b");
link("../grindir/./../a", "../b");
// unlink("b");
// link("../grindir/./../a", "../b");
} else if(what == 12){
unlink("../grindir/../a");
link(".././b", "/grindir/../a");
// unlink("../grindir/../a");
// link(".././b", "/grindir/../a");
} else if(what == 13){
int pid = fork();
if(pid == 0){
@ -180,12 +183,15 @@ go(int which_child)
} else if(what == 20){
int pid = fork();
if(pid == 0){
unlink("a");
// unlink("a");
remove("a");
mkdir("a");
chdir("a");
unlink("../a");
// unlink("../a");
remove("../a");
fd = open("x", O_CREATE|O_RDWR);
unlink("x");
// unlink("x");
remove("x");
exit(0);
} else if(pid < 0){
printf("fork failed\n");
@ -193,7 +199,8 @@ go(int which_child)
}
wait(0);
} else if(what == 21){
unlink("c");
// unlink("c");
remove("c");
// should always succeed. check that there are free i-nodes,
// file descriptors, blocks.
int fd1 = open("c", O_CREATE|O_RDWR);
@ -214,12 +221,13 @@ go(int which_child)
printf("fstat reports wrong size %d\n", (int)st.size);
exit(1);
}
if(st.ino > 200){
printf("fstat reports crazy i-number %d\n", st.ino);
exit(1);
}
// if(st.ino > 200){
// printf("fstat reports crazy i-number %d\n", st.ino);
// exit(1);
// }
close(fd1);
unlink("c");
// unlink("c");
remove("c");
} else if(what == 22){
// echo hi | cat
int aa[2], bb[2];
@ -295,8 +303,10 @@ go(int which_child)
void
iter()
{
unlink("a");
unlink("b");
// unlink("a");
// unlink("b");
remove("a");
remove("b");
int pid1 = fork();
if(pid1 < 0){
@ -335,15 +345,18 @@ iter()
int
main()
{
while(1){
int pid = fork();
if(pid == 0){
iter();
exit(0);
if (fork() == 0) {
while(1){
int pid = fork();
if(pid == 0){
iter();
exit(0);
}
if(pid > 0){
wait(0);
}
sleep(20);
}
if(pid > 0){
wait(0);
}
sleep(20);
}
exit(0);
}

16
xv6-user/info.c Normal file
View File

@ -0,0 +1,16 @@
#include "kernel/include/types.h"
#include "kernel/include/stat.h"
#include "kernel/include/sysinfo.h"
#include "xv6-user/user.h"
int main()
{
struct sysinfo info;
if (sysinfo(&info) < 0) {
printf("sysinfo fail!\n");
} else {
printf("memory left: %d KB\n", info.freemem >> 10);
printf("process amount: %d\n", info.nproc);
}
exit(0);
}

View File

@ -1,15 +0,0 @@
#include "kernel/include/types.h"
#include "kernel/include/stat.h"
#include "xv6-user/user.h"
int
main(int argc, char *argv[])
{
if(argc != 3){
fprintf(2, "Usage: ln old new\n");
exit(1);
}
if(link(argv[1], argv[2]) < 0)
fprintf(2, "link %s %s: failed\n", argv[1], argv[2]);
exit(0);
}

View File

@ -10,7 +10,6 @@
#include "kernel/include/types.h"
#include "kernel/include/stat.h"
#include "xv6-user/user.h"
#include "kernel/include/fs.h"
#include "kernel/include/fcntl.h"
int

View File

@ -1,16 +1,28 @@
#include "kernel/include/types.h"
#include "kernel/include/stat.h"
#include "kernel/include/sysinfo.h"
#include "xv6-user/user.h"
#include "user.h"
int main()
char *myenvp[] = {
"PATH=/bin",
"env1=xxx",
"env2=yyy",
"env3=zzz",
};
int main(int argc, char *argv[], char *envp[])
{
struct sysinfo info;
if (sysinfo(&info) < 0) {
printf("sysinfo fail!\n");
} else {
printf("memory left: %d KB\n", info.freemem >> 10);
printf("process amount: %d\n", info.nproc);
printf("argc: %d\n", argc);
for (int i = 0; i < argc; i++) {
printf("arg%d: [%s]\n", i, argv[i]);
}
for (int i = 0; envp[i]; i++) {
printf("env%d: [%s]\n", i, envp[i]);
}
int fk = 0;
if (argc > 1) {
fk = atoi(argv[1]);
}
if (fk && fork() == 0) {
strcpy(argv[1], "0");
execve("test", argv, myenvp);
}
exit(0);
}

View File

@ -16,6 +16,7 @@ int read(int fd, void *buf, int len);
int close(int fd);
int kill(int pid);
int exec(char*, char**);
int execve(char *name, char *argv[], char *envp[]);
int open(const char *filename, int mode);
int fstat(int fd, struct stat*);
int mkdir(const char *dirname);

View File

@ -41,3 +41,4 @@ entry("remove");
entry("trace");
entry("sysinfo");
entry("rename");
entry("execve");

View File

@ -1,14 +0,0 @@
// Create a zombie process that
// must be reparented at exit.
#include "kernel/include/types.h"
#include "kernel/include/stat.h"
#include "xv6-user/user.h"
int
main(void)
{
if(fork() > 0)
sleep(5); // Let child exit before parent.
exit(0);
}