quinn-os/execve.c

178 lines
4.7 KiB
C

#include "vfs.h"
#include "interrupts.h"
#include "schedule.h"
#include "string.h"
#include "memory.h"
extern struct task_t *current_task;
typedef struct {
unsigned char magic[4];
unsigned char bitness;
unsigned char endian;
unsigned char ver_1;
unsigned char res[9];
unsigned short file_type ;
unsigned short machine __attribute__((packed));
unsigned int ver_2 __attribute__((packed));
unsigned int entry __attribute__((packed));
unsigned int phtab_offset __attribute__((packed));
unsigned int shtab_offset __attribute__((packed));
unsigned int flags __attribute__((packed));
unsigned short file_hdr_size __attribute__((packed));
unsigned short phtab_ent_size __attribute__((packed));
unsigned short num_phs __attribute__((packed));
unsigned short shtab_ent_size __attribute__((packed));
unsigned short num_shs __attribute__((packed));
unsigned short shstrtab_index __attribute__((packed));
} elf_file_t; /* 52 bytes */
typedef struct {
unsigned int type __attribute__((packed));
unsigned int offset __attribute__((packed));
unsigned int virt_adr __attribute__((packed));
unsigned int phys_adr __attribute__((packed));
unsigned int disk_size __attribute__((packed));
unsigned int mem_size __attribute__((packed));
unsigned int flags __attribute__((packed));
unsigned int align __attribute__((packed));
} elf_seg_t; /* 32 bytes */
#define PAGE_SIZE 4096
#define PAGE_MASK (~(0xffffffff << 12))
static __inline__ unsigned int round_up_to_page( unsigned int addr )
{
if ( (addr & PAGE_MASK) != 0 ) {
addr &= ~(PAGE_MASK);
addr += PAGE_SIZE;
}
return addr;
}
int execve(struct regs *r, char *name, char **argv, char **env) {
char *exeimage;
if (!vfs_read_entire_file(name, &exeimage)) {
kprintf("Unable to read file\n");
return -1;
}
elf_file_t *elf_header = (elf_file_t *)exeimage;
// check magic...
if (elf_header->magic[0] == 0x7f && elf_header->magic[1] == 'E' && elf_header->magic[2] == 'L' && elf_header->magic[3] == 'F') {
// it's a valid ELF image.
int arg_sz = 0;
int env_sz = 0;
int envp = 0;
int argp = 0;
int i;
int j;
int k;
unsigned int l;
while(argv[argp]) {
arg_sz+= strlen(argv[argp++]) + 1;
}
while(env[envp]) {
env_sz+= strlen(env[envp++]) + 1;
}
// point of no return...
// setup task name
memset(current_task->name, 0, 32);
for (i=strlen(name)-1;i>=0;i--) {
if (name[i] == '/' || name[i] == ':') {
break;
}
}
k = 0;
for (j=i+1;j<32;j++) {
if (name[j] == '\0') {
break;
} else {
current_task->name[k++] = name[j];
}
}
// close all file pointers...
// vfs_close_all();
// free old argv pointers
// copy argv and argc to a specific location
memset((char *)0xf0000000, 0, 64 * 0x1000);
unsigned int **virt = (unsigned int **)(0xf0000000);
virt[0] = (unsigned int *)0xf0000010;
virt[1] = (unsigned int *)(0xf0000010 + ((argp + 1) * 4));
virt[2] = (unsigned int *)argp;
virt[3] = (unsigned int *)envp;
unsigned int argv_data_start = (unsigned int)virt[1] + ((envp + 1) * 4);
unsigned int *argv_cont = (unsigned int *)virt[0];
unsigned int *envp_cont = (unsigned int *)virt[1];
for (i=0;i<argp;i++) {
memcpy((unsigned char *)argv_data_start, argv[i], strlen(argv[i]) + 1);
argv_cont[i] = argv_data_start;
argv_data_start += strlen(argv[i]) + 1;
}
argv_cont[i] = 0;
unsigned int envp_data_start = argv_data_start;
for (i=0;i<envp;i++) {
memcpy((unsigned char *)envp_data_start, env[i], strlen(env[i])+ 1);
envp_cont[i] = envp_data_start;
envp_data_start += strlen(env[i]) + 1;
}
envp_cont[i] = 0;
int eip = elf_header->entry;
mem_clear_user_pages();
free(current_task->user_pages);
current_task->user_pages_cnt = 0;
current_task->user_pages_at = 0x40000000;
current_task->user_pages = (void *)0;
for (i=0;i<elf_header->num_phs;i++) {
elf_seg_t *seg = (elf_seg_t *)(exeimage + elf_header->phtab_offset + elf_header->phtab_ent_size * i);
if(seg->type == 2 || seg->type == 5) {
return -1;
} else if(seg->type == 1) {
if (current_task->user_pages_at < seg->virt_adr + seg->mem_size) {
for (l=current_task->user_pages_at;l< round_up_to_page(seg->virt_adr + seg->mem_size);l+=0x1000) {
current_task->user_pages_at = l + 0x1000;
mem_map_user_page(l);
}
}
memcpy((unsigned char *)seg->virt_adr, (unsigned char *)(exeimage + seg->offset), seg->disk_size);
if (seg->mem_size > seg->disk_size) {
memset((unsigned char *)(seg->virt_adr + seg->disk_size), 0, seg->mem_size - seg->disk_size);
}
}
}
r->eip = eip;
r->cs = 0x1b;
r->ss = r->es = r->gs = r->fs = r->ds = 0x23;
r->eflags = 0x200;
r->useresp = current_task->ustack;
} else {
return -1;
}
return 0;
}