428 lines
12 KiB
C
428 lines
12 KiB
C
/* Copyright (C) 2005 David Decotigny
|
|
Copyright (C) 1995 TIS Committee (ELF typedefs, constants and macros)
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
|
USA.
|
|
*/
|
|
|
|
#include <sos/types.h>
|
|
#include <sos/klibc.h>
|
|
#include <drivers/bochs.h>
|
|
#include <sos/physmem.h>
|
|
#include <sos/assert.h>
|
|
|
|
#include <sos/process.h>
|
|
#include <sos/thread.h>
|
|
|
|
|
|
/**
|
|
* @file test-art7.c
|
|
*
|
|
* Basic tests for the user thread/process management API
|
|
*/
|
|
|
|
|
|
/**
|
|
* The "C" structure of a user program image in the kernel. Structures
|
|
* like this are created by the Makefile in the userland/ directory
|
|
*/
|
|
struct userprog_entry
|
|
{
|
|
const char *name;
|
|
sos_vaddr_t bottom_vaddr;
|
|
sos_vaddr_t top_vaddr;
|
|
};
|
|
|
|
|
|
/**
|
|
* Symbol marking the start of the userprogs table, as setup by the
|
|
* ld script in the userland/ directory
|
|
*/
|
|
extern char _userprogs_table;
|
|
|
|
|
|
/*
|
|
* Local functions
|
|
*/
|
|
|
|
|
|
/**
|
|
* Function to locate the given user program image in the kernel memory
|
|
*/
|
|
static struct userprog_entry * lookup_userprog(const char *name);
|
|
|
|
|
|
/**
|
|
* Function to create a new process containing the given USER program
|
|
* image. This function automatically locates the destination addresses
|
|
* of the program by examinating its ELF header
|
|
*
|
|
* @return The address of the first instruction of the program, as
|
|
* given by its ELF header, or 0 when the program is not a correct ELF
|
|
* image.
|
|
*/
|
|
static sos_uaddr_t load_elf_prog(const struct userprog_entry *prog);
|
|
|
|
|
|
/**
|
|
* Function that locates a USER program in the kernel image, creates a
|
|
* new USER process for it, and creates the given nb_uthreads USER
|
|
* threads inside it.
|
|
*/
|
|
static sos_ret_t spawn_program(const char *progname,
|
|
unsigned nb_uthreads);
|
|
|
|
|
|
/**
|
|
* The main function for our tests
|
|
*/
|
|
void test_art7()
|
|
{
|
|
spawn_program("myprog5", 5);
|
|
spawn_program("myprog1", 10);
|
|
spawn_program("myprog5", 1);
|
|
spawn_program("myprog6", 12);
|
|
spawn_program("myprog2", 10);
|
|
spawn_program("myprog5", 1);
|
|
spawn_program("myprog3", 10);
|
|
spawn_program("myprog5", 1);
|
|
spawn_program("myprog1", 10);
|
|
spawn_program("myprog6", 12);
|
|
spawn_program("myprog5", 1);
|
|
spawn_program("myprog4", 10);
|
|
spawn_program("myprog5", 1);
|
|
spawn_program("myprog2", 10);
|
|
spawn_program("myprog6", 12);
|
|
spawn_program("myprog5", 1);
|
|
}
|
|
|
|
|
|
static sos_ret_t spawn_program(const char *progname,
|
|
unsigned nb_uthreads)
|
|
{
|
|
int i;
|
|
|
|
sos_uaddr_t prog_entry, stack_top_uaddr;
|
|
struct userprog_entry *prog;
|
|
struct sos_process *new_proc;
|
|
|
|
prog = lookup_userprog(progname);
|
|
if (! prog)
|
|
return -SOS_EINVAL;
|
|
|
|
new_proc = sos_process_create_empty(progname);
|
|
if (! new_proc)
|
|
return -SOS_ENOMEM;
|
|
|
|
/* Squat this new process to map the user program into it */
|
|
SOS_ASSERT_FATAL(SOS_OK
|
|
== sos_thread_change_current_mm_context(sos_process_get_mm_context(new_proc)));
|
|
|
|
/* Load the user program image */
|
|
prog_entry = load_elf_prog(prog);
|
|
if (! prog_entry)
|
|
{
|
|
sos_process_unref(new_proc);
|
|
return -SOS_ENOMEM;
|
|
}
|
|
|
|
/* Map the user stacks into it and create the user threads */
|
|
/* By default, the first user stack will be located at the end of
|
|
the user address space (ie 4GB), the stacks of the other threads
|
|
will be located (12 pages) below. Each stack is USTACK_NPAGES
|
|
pages long */
|
|
#define USTACK_NPAGES 8
|
|
for (i = 0, stack_top_uaddr = 0xfffffffc ;
|
|
i < nb_uthreads ;
|
|
i++, stack_top_uaddr -= (USTACK_NPAGES + 4)*SOS_PAGE_SIZE)
|
|
{
|
|
int p;
|
|
char thrname[16];
|
|
sos_uaddr_t stack_base = SOS_PAGE_ALIGN_INF(stack_top_uaddr);
|
|
|
|
/* Allocate USTACK_NPAGES pages for the stack and map them */
|
|
for (p = 0 ; p < USTACK_NPAGES ; p++, stack_base -= SOS_PAGE_SIZE)
|
|
{
|
|
sos_ret_t retval;
|
|
sos_paddr_t ppage;
|
|
|
|
ppage = sos_physmem_ref_physpage_new(FALSE);
|
|
SOS_ASSERT_FATAL(ppage != 0);
|
|
|
|
/* Map it in the process space. Might fail is there is not
|
|
enough RAM (we don't support swap-out for the moment) */
|
|
retval = sos_paging_map(ppage, stack_base, TRUE,
|
|
SOS_VM_MAP_PROT_READ
|
|
| SOS_VM_MAP_PROT_WRITE);
|
|
SOS_ASSERT_FATAL(retval == SOS_OK);
|
|
|
|
retval = sos_physmem_unref_physpage(ppage);
|
|
SOS_ASSERT_FATAL(retval == 0);
|
|
|
|
/* Poison the stack to detect the use of uninitialized
|
|
variables */
|
|
memset((void*)stack_base, 0xa5, SOS_PAGE_SIZE);
|
|
}
|
|
|
|
/* Create the user thread */
|
|
snprintf(thrname, sizeof(thrname), "%s:%d", progname, i);
|
|
sos_bochs_printf("Spawning %s\n", thrname);
|
|
sos_create_user_thread(thrname,
|
|
new_proc,
|
|
prog_entry,
|
|
0, 0,
|
|
stack_top_uaddr,
|
|
SOS_SCHED_PRIO_TS_LOWEST);
|
|
|
|
sos_thread_yield();
|
|
}
|
|
|
|
/* Don't need the reference to the process anymore */
|
|
sos_process_unref(new_proc);
|
|
|
|
/* Revert to normal kernel thread's address space */
|
|
SOS_ASSERT_FATAL(SOS_OK
|
|
== sos_thread_change_current_mm_context(NULL));
|
|
|
|
return SOS_OK;
|
|
}
|
|
|
|
|
|
/**
|
|
* Lookup a user program located inside the kernel's image
|
|
*/
|
|
static struct userprog_entry * lookup_userprog(const char *name)
|
|
{
|
|
struct userprog_entry *prog;
|
|
|
|
if (! name)
|
|
return NULL;
|
|
|
|
/* Walk through the table of user program description structures to
|
|
find the user program with the given name */
|
|
for (prog = (struct userprog_entry*) & _userprogs_table ;
|
|
prog && (prog->name != NULL) ;
|
|
prog++)
|
|
{
|
|
if (0 == strcmp(name, prog->name))
|
|
/* Found it ! */
|
|
return prog;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/**
|
|
* Make sure the program is in a valid ELF format, map it into memory,
|
|
* and return the address of its entry point (ie _start function)
|
|
*
|
|
* @return 0 when the program is not a valid ELF
|
|
*/
|
|
static sos_uaddr_t load_elf_prog(const struct userprog_entry *prog)
|
|
{
|
|
int i;
|
|
|
|
/**
|
|
* Typedefs, constants and structure definitions as given by the ELF
|
|
* standard specifications.
|
|
*/
|
|
typedef unsigned long Elf32_Addr;
|
|
typedef unsigned long Elf32_Word;
|
|
typedef unsigned short Elf32_Half;
|
|
typedef unsigned long Elf32_Off;
|
|
typedef signed long Elf32_Sword;
|
|
|
|
/* Elf identification */
|
|
|
|
#define EI_NIDENT 16
|
|
typedef struct {
|
|
unsigned char e_ident[EI_NIDENT];
|
|
Elf32_Half e_type;
|
|
Elf32_Half e_machine;
|
|
Elf32_Word e_version;
|
|
Elf32_Addr e_entry;
|
|
Elf32_Off e_phoff;
|
|
Elf32_Off e_shoff;
|
|
Elf32_Word e_flags;
|
|
Elf32_Half e_ehsize;
|
|
Elf32_Half e_phentsize;
|
|
Elf32_Half e_phnum;
|
|
Elf32_Half e_shentsize;
|
|
Elf32_Half e_shnum;
|
|
Elf32_Half e_shstrndx;
|
|
} __attribute__((packed)) Elf32_Ehdr_t;
|
|
|
|
/* e_ident value */
|
|
#define ELFMAG0 0x7f
|
|
#define ELFMAG1 'E'
|
|
#define ELFMAG2 'L'
|
|
#define ELFMAG3 'F'
|
|
|
|
/* e_ident offsets */
|
|
#define EI_MAG0 0
|
|
#define EI_MAG1 1
|
|
#define EI_MAG2 2
|
|
#define EI_MAG3 3
|
|
#define EI_CLASS 4
|
|
#define EI_DATA 5
|
|
#define EI_VERSION 6
|
|
#define EI_PAD 7
|
|
|
|
/* e_ident[EI_CLASS] */
|
|
#define ELFCLASSNONE 0
|
|
#define ELFCLASS32 1
|
|
#define ELFCLASS64 2
|
|
|
|
/* e_ident[EI_DATA] */
|
|
#define ELFDATANONE 0
|
|
#define ELFDATA2LSB 1
|
|
#define ELFDATA2MSB 2
|
|
|
|
/* e_type */
|
|
#define ET_NONE 0 /* No file type */
|
|
#define ET_REL 1 /* Relocatable file */
|
|
#define ET_EXEC 2 /* Executable file */
|
|
#define ET_DYN 3 /* Shared object file */
|
|
#define ET_CORE 4 /* Core file */
|
|
#define ET_LOPROC 0xff00 /* Processor-specific */
|
|
#define ET_HIPROC 0xffff /* Processor-specific */
|
|
|
|
/* e_machine */
|
|
#define EM_NONE 0 /* No machine */
|
|
#define EM_M32 1 /* AT&T WE 32100 */
|
|
#define EM_SPARC 2 /* SPARC */
|
|
#define EM_386 3 /* Intel 80386 */
|
|
#define EM_68K 4 /* Motorola 68000 */
|
|
#define EM_88K 5 /* Motorola 88000 */
|
|
#define EM_860 7 /* Intel 80860 */
|
|
#define EM_MIPS 8 /* MIPS RS3000 */
|
|
|
|
/* e_version */
|
|
#define EV_NONE 0 /* invalid version */
|
|
#define EV_CURRENT 1 /* current version */
|
|
|
|
typedef struct {
|
|
Elf32_Word p_type;
|
|
Elf32_Off p_offset;
|
|
Elf32_Addr p_vaddr;
|
|
Elf32_Addr p_paddr;
|
|
Elf32_Word p_filesz;
|
|
Elf32_Word p_memsz;
|
|
Elf32_Word p_flags;
|
|
Elf32_Word p_align;
|
|
} __attribute__((packed)) Elf32_Phdr_t;
|
|
|
|
/* Reserved segment types p_type */
|
|
#define PT_NULL 0
|
|
#define PT_LOAD 1
|
|
#define PT_DYNAMIC 2
|
|
#define PT_INTERP 3
|
|
#define PT_NOTE 4
|
|
#define PT_SHLIB 5
|
|
#define PT_PHDR 6
|
|
#define PT_LOPROC 0x70000000
|
|
#define PT_HIPROC 0x7fffffff
|
|
|
|
/* p_flags */
|
|
#define PF_X 1
|
|
#define PF_W 2
|
|
#define PF_R 4
|
|
|
|
|
|
Elf32_Ehdr_t *elf_hdr = (Elf32_Ehdr_t*) prog->bottom_vaddr;
|
|
Elf32_Phdr_t *elf_phdrs;
|
|
|
|
/* Make sure the image is large enough to contain at least the ELF
|
|
header */
|
|
if (prog->bottom_vaddr + sizeof(Elf32_Ehdr_t) > prog->top_vaddr)
|
|
{
|
|
sos_bochs_printf("ELF prog %s: incorrect header\n", prog->name);
|
|
return 0;
|
|
}
|
|
|
|
/* Macro to check expected values for some fields in the ELF header */
|
|
#define ELF_CHECK(hdr,field,expected_value) \
|
|
({ if ((hdr)->field != (expected_value)) \
|
|
{ \
|
|
sos_bochs_printf("ELF prog %s: for %s, expected %x, got %x\n", \
|
|
prog->name, \
|
|
#field, \
|
|
(unsigned)(expected_value), \
|
|
(unsigned)(hdr)->field); \
|
|
return 0; \
|
|
} \
|
|
})
|
|
|
|
ELF_CHECK(elf_hdr, e_ident[EI_MAG0], ELFMAG0);
|
|
ELF_CHECK(elf_hdr, e_ident[EI_MAG1], ELFMAG1);
|
|
ELF_CHECK(elf_hdr, e_ident[EI_MAG2], ELFMAG2);
|
|
ELF_CHECK(elf_hdr, e_ident[EI_MAG3], ELFMAG3);
|
|
ELF_CHECK(elf_hdr, e_ident[EI_CLASS], ELFCLASS32);
|
|
ELF_CHECK(elf_hdr, e_ident[EI_DATA], ELFDATA2LSB);
|
|
ELF_CHECK(elf_hdr, e_type, ET_EXEC);
|
|
ELF_CHECK(elf_hdr, e_version, EV_CURRENT);
|
|
|
|
/* Get the begining of the program header table */
|
|
elf_phdrs = (Elf32_Phdr_t*) (prog->bottom_vaddr + elf_hdr->e_phoff);
|
|
|
|
/* Map the program segment in R/W mode. To make things clean, we
|
|
should iterate over the sections, not the program header */
|
|
for (i = 0 ; i < elf_hdr->e_phnum ; i++)
|
|
{
|
|
sos_uaddr_t uaddr;
|
|
|
|
/* Ignore the empty program headers that are not marked "LOAD" */
|
|
if (elf_phdrs[i].p_type != PT_LOAD && elf_phdrs[i].p_type != PT_NOTE)
|
|
{
|
|
if (elf_phdrs[i].p_memsz != 0)
|
|
{
|
|
SOS_FATAL_ERROR("ELF: non-empty (%ld) non-LOAD/non-NOTE (%ld) segments not supported yet", elf_phdrs[i].p_memsz, elf_phdrs[i].p_type);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (elf_phdrs[i].p_vaddr < SOS_PAGING_BASE_USER_ADDRESS)
|
|
{
|
|
SOS_FATAL_ERROR("User program has an incorrect address");
|
|
}
|
|
|
|
/* Map pages of physical memory into user space */
|
|
for (uaddr = SOS_PAGE_ALIGN_INF(elf_phdrs[i].p_vaddr) ;
|
|
uaddr < elf_phdrs[i].p_vaddr + elf_phdrs[i].p_memsz ;
|
|
uaddr += SOS_PAGE_SIZE)
|
|
{
|
|
sos_ret_t retval;
|
|
sos_paddr_t ppage;
|
|
ppage = sos_physmem_ref_physpage_new(TRUE);
|
|
|
|
retval = sos_paging_map(ppage, uaddr, TRUE,
|
|
SOS_VM_MAP_PROT_READ
|
|
| SOS_VM_MAP_PROT_WRITE);
|
|
SOS_ASSERT_FATAL(retval == SOS_OK);
|
|
|
|
retval = sos_physmem_unref_physpage(ppage);
|
|
SOS_ASSERT_FATAL(retval == 0);
|
|
}
|
|
|
|
/* Copy segment into memory */
|
|
memcpy((void*) elf_phdrs[i].p_vaddr,
|
|
(void*) (prog->bottom_vaddr + elf_phdrs[i].p_offset),
|
|
elf_phdrs[i].p_filesz);
|
|
}
|
|
|
|
return elf_hdr->e_entry;
|
|
}
|