grub/kern/dl.c
okuji c04da07444 2003-01-17 Yoshinori K. Okuji <okuji@enbug.org>
* include/pupa/i386/pc/linux.h: New file.
	* loader/i386/pc/linux.c: Likewise.

	* loader/i386/pc/chainloader.c (pupa_chainloader_boot_sector):
	Removed.
	(pupa_chainloader_unload): Return PUPA_ERR_NONE.
	(pupa_rescue_cmd_chainloader): Read the image to 0x7C00 instead
	of PUPA_CHAINLOADER_BOOT_SECTOR.

	* kern/i386/pc/startup.S: Include pupa/machine/linux.h.
	(pupa_linux_prot_size): New variable.
	(pupa_linux_tmp_addr): Likewise.
	(pupa_linux_real_addr): Likewise.
	(pupa_linux_boot_zimage): New function.
	(pupa_linux_boot_bzimage): Likewise.

	* kern/i386/pc/init.c (struct mem_region): New structure.
	(MAX_REGIONS): New macro.
	(mem_regions): New variable.
	(num_regions): Likewise.
	(pupa_os_area_addr): Likewise.
	(pupa_os_area_size): Likewise.
	(pupa_lower_mem): Likewise.
	(pupa_upper_mem): Likewise.
	(add_mem_region): New function.
	(compact_mem_regions): Likewise.
	(pupa_machine_init): Set PUPA_LOWER_MEM and PUPA_UPPER_MEM to
	the size of the conventional memory and that of so-called upper
	memory (before the first memory hole).
	Instead of adding each found region to free memory, use
	add_mem_region and add them after removing overlaps.
	Also, add only 1/4 of the upper memory to free memory. The rest
	is used for loading OS images. Maybe this is ad hoc, but this
	makes it much easier to relocate OS images when booting.

	* kern/rescue.c (pupa_rescue_cmd_module): Removed.
	(pupa_enter_rescue_mode): Don't register initrd and module.

	* kern/mm.c: Include pupa/dl.h.

	* kern/main.c: Include pupa/file.h and pupa/device.h.

	* kern/loader.c (pupa_loader_load_module_func): Removed.
	(pupa_loader_load_module): Likewise.

	* kern/dl.c (pupa_dl_load): Use the suffix ``.mod'' instead of
	``.o''.

	* include/pupa/i386/pc/loader.h (pupa_linux_prot_size): Declared.
	(pupa_linux_tmp_addr): Likewise.
	(pupa_linux_real_addr): Likewise.
	(pupa_linux_boot_zimage): Likewise.
	(pupa_linux_boot_bzimage): Likewise.

	* include/pupa/i386/pc/init.h (pupa_lower_mem): Declared.
	(pupa_upper_mem): Likewise.
	(pupa_gate_a20): Don't export, because turning off Gate A20 in a
	module is too dangerous.

	* include/pupa/loader.h (pupa_os_area_addr): Declared.
	(pupa_os_area_size): Likewise.
	(pupa_loader_set): Remove the first argument. Loader doesn't
	manage modules or initrd any longer.
	(pupa_loader_load_module): Removed.

	* conf/i386-pc.rmk (pkgdata_MODULES): Added linux.mod.
	(linux_mod_SOURCES): New variable.
	(linux_mod_CFLAGS): Likewise.
2003-01-17 02:52:05 +00:00

671 lines
13 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* dl.c - loadable module support */
/*
* PUPA -- Preliminary Universal Programming Architecture for GRUB
* Copyright (C) 2002 Yoshinori K. Okuji <okuji@enbug.org>
*
* PUPA 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 PUPA; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <config.h>
#include <pupa/elf.h>
#include <pupa/dl.h>
#include <pupa/misc.h>
#include <pupa/mm.h>
#include <pupa/err.h>
#include <pupa/types.h>
#include <pupa/symbol.h>
#include <pupa/file.h>
#if PUPA_HOST_SIZEOF_VOID_P == 4
typedef Elf32_Word Elf_Word;
typedef Elf32_Addr Elf_Addr;
typedef Elf32_Ehdr Elf_Ehdr;
typedef Elf32_Shdr Elf_Shdr;
typedef Elf32_Sym Elf_Sym;
# define ELF_ST_BIND(val) ELF32_ST_BIND (val)
# define ELF_ST_TYPE(val) ELF32_ST_TYPE (val)
#elif PUPA_HOST_SIZEOF_VOID_P == 8
typedef Elf64_Word Elf_Word;
typedef Elf64_Addr Elf_Addr;
typedef Elf64_Ehdr Elf_Ehdr;
typedef Elf64_Shdr Elf_Shdr;
typedef Elf64_Sym Elf_Sym;
# define ELF_ST_BIND(val) ELF64_ST_BIND (val)
# define ELF_ST_TYPE(val) ELF64_ST_TYPE (val)
#endif
struct pupa_dl_list
{
struct pupa_dl_list *next;
pupa_dl_t mod;
};
typedef struct pupa_dl_list *pupa_dl_list_t;
static pupa_dl_list_t pupa_dl_head;
static pupa_err_t
pupa_dl_add (pupa_dl_t mod)
{
pupa_dl_list_t l;
if (pupa_dl_get (mod->name))
return pupa_error (PUPA_ERR_BAD_MODULE,
"`%s' is already loaded", mod->name);
l = (pupa_dl_list_t) pupa_malloc (sizeof (*l));
if (! l)
return pupa_errno;
l->mod = mod;
l->next = pupa_dl_head;
pupa_dl_head = l;
return PUPA_ERR_NONE;
}
static void
pupa_dl_remove (pupa_dl_t mod)
{
pupa_dl_list_t *p, q;
for (p = &pupa_dl_head, q = *p; q; p = &q->next, q = *p)
if (q->mod == mod)
{
*p = q->next;
pupa_free (q);
return;
}
}
pupa_dl_t
pupa_dl_get (const char *name)
{
pupa_dl_list_t l;
for (l = pupa_dl_head; l; l = l->next)
if (pupa_strcmp (name, l->mod->name) == 0)
return l->mod;
return 0;
}
void
pupa_dl_iterate (int (*hook) (pupa_dl_t mod))
{
pupa_dl_list_t l;
for (l = pupa_dl_head; l; l = l->next)
if (hook (l->mod))
break;
}
struct pupa_symbol
{
struct pupa_symbol *next;
const char *name;
void *addr;
pupa_dl_t mod; /* The module to which this symbol belongs. */
};
typedef struct pupa_symbol *pupa_symbol_t;
/* The size of the symbol table. */
#define PUPA_SYMTAB_SIZE 509
/* The symbol table (using an open-hash). */
static struct pupa_symbol *pupa_symtab[PUPA_SYMTAB_SIZE];
/* Simple hash function. */
static unsigned
pupa_symbol_hash (const char *s)
{
unsigned key = 0;
while (*s)
key = key * 65599 + *s++;
return (key + (key >> 5)) % PUPA_SYMTAB_SIZE;
}
/* Resolve the symbol name NAME and return the address.
Return NULL, if not found. */
void *
pupa_dl_resolve_symbol (const char *name)
{
pupa_symbol_t sym;
for (sym = pupa_symtab[pupa_symbol_hash (name)]; sym; sym = sym->next)
if (pupa_strcmp (sym->name, name) == 0)
return sym->addr;
return 0;
}
/* Register a symbol with the name NAME and the address ADDR. */
pupa_err_t
pupa_dl_register_symbol (const char *name, void *addr, pupa_dl_t mod)
{
pupa_symbol_t sym;
unsigned k;
sym = (pupa_symbol_t) pupa_malloc (sizeof (*sym));
if (! sym)
return pupa_errno;
if (mod)
{
sym->name = pupa_strdup (name);
if (! sym->name)
{
pupa_free (sym);
return pupa_errno;
}
}
else
sym->name = name;
sym->addr = addr;
sym->mod = mod;
k = pupa_symbol_hash (name);
sym->next = pupa_symtab[k];
pupa_symtab[k] = sym;
return PUPA_ERR_NONE;
}
/* Unregister all the symbols defined in the module MOD. */
static void
pupa_dl_unregister_symbols (pupa_dl_t mod)
{
unsigned i;
if (! mod)
pupa_fatal ("core symbols cannot be unregistered");
for (i = 0; i < PUPA_SYMTAB_SIZE; i++)
{
pupa_symbol_t sym, *p, q;
for (p = &pupa_symtab[i], sym = *p; sym; sym = q)
{
q = sym->next;
if (sym->mod == mod)
{
*p = q;
pupa_free ((void *) sym->name);
pupa_free (sym);
}
else
p = &sym->next;
}
}
}
/* Return the address of a section whose index is N. */
static void *
pupa_dl_get_section_addr (pupa_dl_t mod, unsigned n)
{
pupa_dl_segment_t seg;
for (seg = mod->segment; seg; seg = seg->next)
if (seg->section == n)
return seg->addr;
return 0;
}
/* Load all segments from memory specified by E. */
static pupa_err_t
pupa_dl_load_segments (pupa_dl_t mod, const Elf_Ehdr *e)
{
unsigned i;
Elf_Shdr *s;
for (i = 0, s = (Elf_Shdr *)((char *) e + e->e_shoff);
i < e->e_shnum;
i++, s = (Elf_Shdr *)((char *) s + e->e_shentsize))
{
if (s->sh_flags & SHF_ALLOC)
{
pupa_dl_segment_t seg;
seg = (pupa_dl_segment_t) pupa_malloc (sizeof (*seg));
if (! seg)
return pupa_errno;
if (s->sh_size)
{
void *addr;
addr = pupa_memalign (s->sh_addralign, s->sh_size);
if (! addr)
{
pupa_free (seg);
return pupa_errno;
}
switch (s->sh_type)
{
case SHT_PROGBITS:
pupa_memcpy (addr, (char *) e + s->sh_offset, s->sh_size);
break;
case SHT_NOBITS:
pupa_memset (addr, 0, s->sh_size);
break;
}
seg->addr = addr;
}
else
seg->addr = 0;
seg->size = s->sh_size;
seg->section = i;
seg->next = mod->segment;
mod->segment = seg;
}
}
return PUPA_ERR_NONE;
}
static pupa_err_t
pupa_dl_resolve_symbols (pupa_dl_t mod, Elf_Ehdr *e)
{
unsigned i;
Elf_Shdr *s;
Elf_Sym *sym;
const char *str;
Elf_Word size, entsize;
for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
i < e->e_shnum;
i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
if (s->sh_type == SHT_SYMTAB)
break;
if (i == e->e_shnum)
return pupa_error (PUPA_ERR_BAD_MODULE, "no symbol table");
sym = (Elf_Sym *) ((char *) e + s->sh_offset);
size = s->sh_size;
entsize = s->sh_entsize;
s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shentsize * s->sh_link);
str = (char *) e + s->sh_offset;
for (i = 0;
i < size / entsize;
i++, sym = (Elf_Sym *) ((char *) sym + entsize))
{
unsigned char type = ELF_ST_TYPE (sym->st_info);
unsigned char bind = ELF_ST_BIND (sym->st_info);
const char *name = str + sym->st_name;
switch (type)
{
case STT_NOTYPE:
/* Resolve a global symbol. */
if (sym->st_name != 0 && sym->st_shndx == 0)
{
sym->st_value = (Elf_Addr) pupa_dl_resolve_symbol (name);
if (! sym->st_value)
return pupa_error (PUPA_ERR_BAD_MODULE,
"the symbol `%s' not found", name);
}
else
sym->st_value = 0;
break;
case STT_OBJECT:
sym->st_value += (Elf_Addr) pupa_dl_get_section_addr (mod,
sym->st_shndx);
if (bind != STB_LOCAL)
if (pupa_dl_register_symbol (name, (void *) sym->st_value, mod))
return pupa_errno;
break;
case STT_FUNC:
sym->st_value += (Elf_Addr) pupa_dl_get_section_addr (mod,
sym->st_shndx);
if (bind != STB_LOCAL)
if (pupa_dl_register_symbol (name, (void *) sym->st_value, mod))
return pupa_errno;
if (pupa_strcmp (name, "pupa_mod_init") == 0)
mod->init = (void (*) (pupa_dl_t)) sym->st_value;
else if (pupa_strcmp (name, "pupa_mod_fini") == 0)
mod->fini = (void (*) (void)) sym->st_value;
break;
case STT_SECTION:
sym->st_value = (Elf_Addr) pupa_dl_get_section_addr (mod,
sym->st_shndx);
break;
case STT_FILE:
sym->st_value = 0;
break;
default:
return pupa_error (PUPA_ERR_BAD_MODULE,
"unknown symbol type `%d'", (int) type);
}
}
return PUPA_ERR_NONE;
}
static void
pupa_dl_call_init (pupa_dl_t mod)
{
if (mod->init)
(mod->init) (mod);
}
static pupa_err_t
pupa_dl_resolve_name (pupa_dl_t mod, Elf_Ehdr *e)
{
Elf_Shdr *s;
const char *str;
unsigned i;
s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize);
str = (char *) e + s->sh_offset;
for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
i < e->e_shnum;
i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
if (pupa_strcmp (str + s->sh_name, ".modname") == 0)
{
mod->name = pupa_strdup ((char *) e + s->sh_offset);
if (! mod->name)
return pupa_errno;
break;
}
if (i == e->e_shnum)
return pupa_error (PUPA_ERR_BAD_MODULE, "no module name found");
return PUPA_ERR_NONE;
}
static pupa_err_t
pupa_dl_resolve_dependencies (pupa_dl_t mod, Elf_Ehdr *e)
{
Elf_Shdr *s;
const char *str;
unsigned i;
s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize);
str = (char *) e + s->sh_offset;
for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
i < e->e_shnum;
i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
if (pupa_strcmp (str + s->sh_name, ".moddeps") == 0)
{
const char *name = (char *) e + s->sh_offset;
const char *max = name + s->sh_size;
while (name < max)
{
pupa_dl_t m;
pupa_dl_dep_t dep;
m = pupa_dl_load (name);
if (! m)
return pupa_errno;
pupa_dl_ref (m);
dep = (pupa_dl_dep_t) pupa_malloc (sizeof (*dep));
if (! dep)
return pupa_errno;
dep->mod = m;
dep->next = mod->dep;
mod->dep = dep;
name += pupa_strlen (name) + 1;
}
}
return PUPA_ERR_NONE;
}
int
pupa_dl_ref (pupa_dl_t mod)
{
return ++mod->ref_count;
}
int
pupa_dl_unref (pupa_dl_t mod)
{
int ret;
ret = --mod->ref_count;
if (ret <= 0)
pupa_dl_unload (mod);
return ret;
}
/* Load a module from core memory. */
pupa_dl_t
pupa_dl_load_core (void *addr, pupa_size_t size)
{
Elf_Ehdr *e;
pupa_dl_t mod;
e = addr;
if (! pupa_arch_dl_check_header (e, size))
{
pupa_error (PUPA_ERR_BAD_MODULE, "invalid ELF header");
return 0;
}
mod = (pupa_dl_t) pupa_malloc (sizeof (*mod));
if (! mod)
return 0;
mod->name = 0;
mod->ref_count = 1;
mod->dep = 0;
mod->segment = 0;
mod->init = 0;
mod->fini = 0;
if (pupa_dl_resolve_name (mod, e)
|| pupa_dl_resolve_dependencies (mod, e)
|| pupa_dl_load_segments (mod, e)
|| pupa_dl_resolve_symbols (mod, e)
|| pupa_arch_dl_relocate_symbols (mod, e))
{
mod->fini = 0;
pupa_dl_unload (mod);
return 0;
}
pupa_dl_call_init (mod);
if (pupa_dl_add (mod))
{
pupa_dl_unload (mod);
return 0;
}
return mod;
}
/* Load a module from the file FILENAME. */
pupa_dl_t
pupa_dl_load_file (const char *filename)
{
pupa_file_t file;
pupa_ssize_t size;
void *core = 0;
pupa_dl_t mod = 0;
file = pupa_file_open (filename);
if (! file)
return 0;
size = pupa_file_size (file);
core = pupa_malloc (size);
if (! core)
goto failed;
if (pupa_file_read (file, core, size) != (int) size)
goto failed;
mod = pupa_dl_load_core (core, size);
mod->ref_count = 0;
failed:
pupa_file_close (file);
pupa_free (core);
return mod;
}
static char *pupa_dl_dir;
/* Load a module using a symbolic name. */
pupa_dl_t
pupa_dl_load (const char *name)
{
char *filename;
pupa_dl_t mod;
mod = pupa_dl_get (name);
if (mod)
return mod;
if (! pupa_dl_dir)
pupa_fatal ("module dir is not initialized yet");
filename = (char *) pupa_malloc (pupa_strlen (pupa_dl_dir) + 1
+ pupa_strlen (name) + 4 + 1);
if (! filename)
return 0;
pupa_sprintf (filename, "%s/%s.mod", pupa_dl_dir, name);
mod = pupa_dl_load_file (filename);
pupa_free (filename);
if (! mod)
return 0;
if (pupa_strcmp (mod->name, name) != 0)
pupa_error (PUPA_ERR_BAD_MODULE, "mismatched names");
return mod;
}
/* Unload the module MOD. */
int
pupa_dl_unload (pupa_dl_t mod)
{
pupa_dl_dep_t dep, depn;
pupa_dl_segment_t seg, segn;
if (mod->ref_count > 0)
return 0;
if (mod->fini)
(mod->fini) ();
pupa_dl_remove (mod);
pupa_dl_unregister_symbols (mod);
for (dep = mod->dep; dep; dep = depn)
{
depn = dep->next;
pupa_dl_unref (dep->mod);
pupa_free (dep);
}
for (seg = mod->segment; seg; seg = segn)
{
segn = seg->next;
pupa_free (seg->addr);
pupa_free (seg);
}
pupa_free (mod->name);
pupa_free (mod);
return 1;
}
/* Unload unneeded modules. */
void
pupa_dl_unload_unneeded (void)
{
/* Because pupa_dl_remove modifies the list of modules, this
implementation is tricky. */
pupa_dl_list_t p = pupa_dl_head;
while (p)
{
if (pupa_dl_unload (p->mod))
{
p = pupa_dl_head;
continue;
}
p = p->next;
}
}
/* Unload all modules. */
void
pupa_dl_unload_all (void)
{
while (pupa_dl_head)
{
pupa_dl_list_t p;
pupa_dl_unload_unneeded ();
/* Force to decrement the ref count. This will purge pre-loaded
modules and manually inserted modules. */
for (p = pupa_dl_head; p; p = p->next)
p->mod->ref_count--;
}
}
void
pupa_dl_set_prefix (const char *dir)
{
pupa_dl_dir = (char *) dir;
}
const char *
pupa_dl_get_prefix (void)
{
return pupa_dl_dir;
}