sos-code-article7/sos/test-art7.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;
}