Initial revision

This commit is contained in:
okuji 2002-12-27 08:53:07 +00:00
commit 6a161fa938
80 changed files with 23264 additions and 0 deletions

99
kern/device.c Normal file
View file

@ -0,0 +1,99 @@
/* device.c - device manager */
/*
* 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 <pupa/device.h>
#include <pupa/disk.h>
#include <pupa/net.h>
#include <pupa/fs.h>
#include <pupa/mm.h>
#include <pupa/misc.h>
static char *pupa_device_root;
pupa_err_t
pupa_device_set_root (const char *name)
{
pupa_free (pupa_device_root);
pupa_device_root = pupa_strdup (name);
return pupa_errno;
}
const char *
pupa_device_get_root (void)
{
if (! pupa_device_root)
pupa_error (PUPA_ERR_BAD_DEVICE, "no root device");
return pupa_device_root;
}
pupa_device_t
pupa_device_open (const char *name)
{
pupa_disk_t disk = 0;
pupa_device_t dev = 0;
if (! name)
{
if (! pupa_device_root)
{
pupa_error (PUPA_ERR_BAD_DEVICE, "no device is set");
goto fail;
}
name = pupa_device_root;
}
dev = pupa_malloc (sizeof (*dev));
if (! dev)
goto fail;
/* Try to open a disk. */
disk = pupa_disk_open (name);
if (! disk)
{
pupa_error (PUPA_ERR_BAD_DEVICE, "unknown device");
goto fail;
}
dev->disk = disk;
dev->net = 0; /* FIXME */
return dev;
fail:
if (disk)
pupa_disk_close (disk);
pupa_free (dev);
return 0;
}
pupa_err_t
pupa_device_close (pupa_device_t device)
{
if (device->disk)
pupa_disk_close (device->disk);
pupa_free (device);
return pupa_errno;
}

474
kern/disk.c Normal file
View file

@ -0,0 +1,474 @@
/*
* 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 <pupa/disk.h>
#include <pupa/err.h>
#include <pupa/mm.h>
#include <pupa/types.h>
#include <pupa/machine/partition.h>
#include <pupa/misc.h>
/* Disk cache. */
struct pupa_disk_cache
{
unsigned long id;
unsigned long sector;
char *data;
int lock;
};
static struct pupa_disk_cache pupa_disk_cache_table[PUPA_DISK_CACHE_NUM];
#if 0
static unsigned long pupa_disk_cache_hits;
static unsigned long pupa_disk_cache_misses;
void
pupa_disk_cache_get_performance (unsigned long *hits, unsigned long *misses)
{
*hits = pupa_disk_cache_hits;
*misses = pupa_disk_cache_misses;
}
#endif
static unsigned
pupa_disk_cache_get_index (unsigned long id, unsigned long sector)
{
return ((id * 2606459 + (sector >> PUPA_DISK_CACHE_BITS))
% PUPA_DISK_CACHE_NUM);
}
static void
pupa_disk_cache_invalidate (unsigned long id, unsigned long sector)
{
unsigned index;
struct pupa_disk_cache *cache;
sector &= ~(PUPA_DISK_CACHE_SIZE - 1);
index = pupa_disk_cache_get_index (id, sector);
cache = pupa_disk_cache_table + index;
if (cache->id == id && cache->sector == sector && cache->data)
{
cache->lock = 1;
pupa_free (cache->data);
cache->data = 0;
cache->lock = 0;
}
}
void
pupa_disk_cache_invalidate_all (void)
{
unsigned i;
for (i = 0; i < PUPA_DISK_CACHE_NUM; i++)
{
struct pupa_disk_cache *cache = pupa_disk_cache_table + i;
if (cache->data && ! cache->lock)
{
pupa_free (cache->data);
cache->data = 0;
}
}
}
static char *
pupa_disk_cache_fetch (unsigned long id, unsigned long sector)
{
struct pupa_disk_cache *cache;
unsigned index;
index = pupa_disk_cache_get_index (id, sector);
cache = pupa_disk_cache_table + index;
if (cache->id == id && cache->sector == sector)
{
cache->lock = 1;
#if 0
pupa_disk_cache_hits++;
#endif
return cache->data;
}
#if 0
pupa_disk_cache_misses++;
#endif
return 0;
}
static void
pupa_disk_cache_unlock (unsigned long id, unsigned long sector)
{
struct pupa_disk_cache *cache;
unsigned index;
index = pupa_disk_cache_get_index (id, sector);
cache = pupa_disk_cache_table + index;
if (cache->id == id && cache->sector == sector)
cache->lock = 0;
}
static pupa_err_t
pupa_disk_cache_store (unsigned long id, unsigned long sector,
const char *data)
{
unsigned index;
struct pupa_disk_cache *cache;
pupa_disk_cache_invalidate (id, sector);
index = pupa_disk_cache_get_index (id, sector);
cache = pupa_disk_cache_table + index;
cache->data = pupa_malloc (PUPA_DISK_SECTOR_SIZE << PUPA_DISK_CACHE_BITS);
if (! cache->data)
return pupa_errno;
pupa_memcpy (cache->data, data,
PUPA_DISK_SECTOR_SIZE << PUPA_DISK_CACHE_BITS);
cache->id = id;
cache->sector = sector;
return PUPA_ERR_NONE;
}
static pupa_disk_dev_t pupa_disk_dev_list;
void
pupa_disk_dev_register (pupa_disk_dev_t dev)
{
dev->next = pupa_disk_dev_list;
pupa_disk_dev_list = dev;
}
void
pupa_disk_dev_unregister (pupa_disk_dev_t dev)
{
pupa_disk_dev_t *p, q;
for (p = &pupa_disk_dev_list, q = *p; q; p = &(q->next), q = q->next)
if (q == dev)
{
*p = q->next;
break;
}
}
void
pupa_disk_dev_iterate (int (*hook) (const char *name))
{
pupa_disk_dev_t p;
for (p = pupa_disk_dev_list; p; p = p->next)
if ((p->iterate) (hook))
break;
}
pupa_disk_t
pupa_disk_open (const char *name)
{
char *p;
pupa_disk_t disk;
pupa_disk_dev_t dev;
char *raw = (char *) name;
disk = (pupa_disk_t) pupa_malloc (sizeof (*disk));
if (! disk)
return 0;
disk->dev = 0;
disk->read_hook = 0;
disk->partition = 0;
disk->data = 0;
disk->name = pupa_strdup (name);
if (! disk->name)
goto fail;
p = pupa_strchr (name, ',');
if (p)
{
pupa_size_t len = p - name;
raw = pupa_malloc (len + 1);
if (! raw)
goto fail;
pupa_memcpy (raw, name, len);
raw[len] = '\0';
}
for (dev = pupa_disk_dev_list; dev; dev = dev->next)
{
if ((dev->open) (raw, disk) == PUPA_ERR_NONE)
break;
else if (pupa_errno == PUPA_ERR_UNKNOWN_DEVICE)
pupa_errno = PUPA_ERR_NONE;
else
goto fail;
}
if (! dev)
{
pupa_error (PUPA_ERR_UNKNOWN_DEVICE, "no such disk");
goto fail;
}
if (p && ! disk->has_partitions)
{
pupa_error (PUPA_ERR_BAD_DEVICE, "no partition on this disk");
goto fail;
}
disk->dev = dev;
if (p)
disk->partition = pupa_partition_probe (disk, p + 1);
fail:
if (raw && raw != name)
pupa_free (raw);
if (pupa_errno != PUPA_ERR_NONE)
{
pupa_disk_close (disk);
return 0;
}
return disk;
}
void
pupa_disk_close (pupa_disk_t disk)
{
if (disk->dev && disk->dev->close)
(disk->dev->close) (disk);
pupa_free (disk->partition);
pupa_free ((void *) disk->name);
pupa_free (disk);
}
static pupa_err_t
pupa_disk_check_range (pupa_disk_t disk, unsigned long *sector,
unsigned long *offset, pupa_ssize_t size)
{
*sector += *offset >> PUPA_DISK_SECTOR_BITS;
*offset &= PUPA_DISK_SECTOR_SIZE - 1;
if (disk->partition)
{
unsigned long start, len;
start = pupa_partition_get_start (disk->partition);
len = pupa_partition_get_len (disk->partition);
if (*sector >= len
|| len - *sector < ((*offset + size + PUPA_DISK_SECTOR_SIZE - 1)
>> PUPA_DISK_SECTOR_BITS))
return pupa_error (PUPA_ERR_OUT_OF_RANGE, "out of partition");
*sector += start;
}
if (disk->total_sectors <= *sector
|| ((*offset + size + PUPA_DISK_SECTOR_SIZE - 1)
>> PUPA_DISK_SECTOR_BITS) > disk->total_sectors - *sector)
return pupa_error (PUPA_ERR_OUT_OF_RANGE, "out of disk");
return PUPA_ERR_NONE;
}
/* Read data from the disk. */
pupa_err_t
pupa_disk_read (pupa_disk_t disk, unsigned long sector,
unsigned long offset, unsigned long size, char *buf)
{
char *tmp_buf;
/* First of all, check if the region is within the disk. */
if (pupa_disk_check_range (disk, &sector, &offset, size) != PUPA_ERR_NONE)
return pupa_errno;
/* Allocate a temporary buffer. */
tmp_buf = pupa_malloc (PUPA_DISK_SECTOR_SIZE << PUPA_DISK_CACHE_BITS);
if (! tmp_buf)
return pupa_errno;
/* Until SIZE is zero... */
while (size)
{
char *data;
unsigned long start_sector;
unsigned long len;
unsigned long pos;
/* For reading bulk data. */
start_sector = sector & ~(PUPA_DISK_CACHE_SIZE - 1);
pos = (sector - start_sector) << PUPA_DISK_SECTOR_BITS;
len = (PUPA_DISK_SECTOR_SIZE << PUPA_DISK_CACHE_BITS) - pos - offset;
if (len > size)
len = size;
/* Fetch the cache. */
data = pupa_disk_cache_fetch (disk->id, start_sector);
if (data)
{
/* Just copy it! */
pupa_memcpy (buf, data + pos + offset, len);
pupa_disk_cache_unlock (disk->id, start_sector);
}
else
{
/* Otherwise read data from the disk actually. */
if ((disk->dev->read) (disk, start_sector,
PUPA_DISK_CACHE_SIZE, tmp_buf)
!= PUPA_ERR_NONE)
{
/* Uggh... Failed. Instead, just read necessary data. */
unsigned num;
/* If more data is required, no way. */
if (pos + size
>= (PUPA_DISK_SECTOR_SIZE << PUPA_DISK_CACHE_BITS))
goto finish;
num = ((size + PUPA_DISK_SECTOR_SIZE - 1)
>> PUPA_DISK_SECTOR_BITS);
if ((disk->dev->read) (disk, sector, num, tmp_buf))
goto finish;
pupa_memcpy (buf, tmp_buf + offset, size);
/* Call the read hook, if any. */
if (disk->read_hook)
while (size)
{
(disk->read_hook) (sector, offset,
((size > PUPA_DISK_SECTOR_SIZE)
? PUPA_DISK_SECTOR_SIZE
: size));
sector++;
size -= PUPA_DISK_SECTOR_SIZE - offset;
offset = 0;
}
/* This must be the end. */
goto finish;
}
/* Copy it and store it in the disk cache. */
pupa_memcpy (buf, tmp_buf + pos + offset, len);
pupa_disk_cache_store (disk->id, start_sector, tmp_buf);
}
/* Call the read hook, if any. */
if (disk->read_hook)
{
unsigned long s = sector;
unsigned long l = len;
while (l)
{
(disk->read_hook) (s, offset,
((l > PUPA_DISK_SECTOR_SIZE)
? PUPA_DISK_SECTOR_SIZE
: l));
s++;
l -= PUPA_DISK_SECTOR_SIZE - offset;
offset = 0;
}
}
sector = start_sector + PUPA_DISK_CACHE_SIZE;
buf += len;
size -= len;
offset = 0;
}
finish:
pupa_free (tmp_buf);
return pupa_errno;
}
pupa_err_t
pupa_disk_write (pupa_disk_t disk, unsigned long sector,
unsigned long offset, unsigned long size, const char *buf)
{
if (pupa_disk_check_range (disk, &sector, &offset, size) != PUPA_ERR_NONE)
return -1;
while (size)
{
if (offset != 0 || (size < PUPA_DISK_SECTOR_SIZE && size != 0))
{
char tmp_buf[PUPA_DISK_SECTOR_SIZE];
unsigned long len;
if (pupa_disk_read (disk, sector, 0, PUPA_DISK_SECTOR_SIZE, tmp_buf)
!= PUPA_ERR_NONE)
goto finish;
len = PUPA_DISK_SECTOR_SIZE - offset;
if (len > size)
len = size;
pupa_memcpy (tmp_buf + offset, buf, len);
pupa_disk_cache_invalidate (disk->id, sector);
if ((disk->dev->write) (disk, sector, 1, tmp_buf) != PUPA_ERR_NONE)
goto finish;
sector++;
buf += len;
size -= len;
offset = 0;
}
else
{
unsigned long len;
unsigned long n;
len = size & ~(PUPA_DISK_SECTOR_SIZE - 1);
n = size >> PUPA_DISK_SECTOR_BITS;
if ((disk->dev->write) (disk, sector, n, buf) != PUPA_ERR_NONE)
goto finish;
while (n--)
pupa_disk_cache_invalidate (disk->id, sector++);
buf += len;
size -= len;
}
}
finish:
return pupa_errno;
}

599
kern/dl.c Normal file
View file

@ -0,0 +1,599 @@
/* 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;
}
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 (*) ()) sym->st_value;
else if (pupa_strcmp (name, "pupa_mod_fini") == 0)
mod->fini = (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) ();
}
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;
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;
}
/* 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);
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)
{
mod->ref_count++;
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) + 3);
if (! filename)
return 0;
pupa_sprintf (filename, "%s/%s.o", 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. */
void
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;
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_unload (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);
}
void
pupa_dl_init (const char *dir)
{
pupa_dl_dir = (char *) dir;
}

61
kern/err.c Normal file
View file

@ -0,0 +1,61 @@
/* err.c - error handling routines */
/*
* 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 <pupa/err.h>
#include <pupa/misc.h>
#include <stdarg.h>
#define PUPA_MAX_ERRMSG 256
pupa_err_t pupa_errno;
char pupa_errmsg[PUPA_MAX_ERRMSG];
pupa_err_t
pupa_error (pupa_err_t n, const char *fmt, ...)
{
va_list ap;
pupa_errno = n;
va_start (ap, fmt);
pupa_vsprintf (pupa_errmsg, fmt, ap);
va_end (ap);
return n;
}
void
pupa_fatal (const char *fmt, ...)
{
va_list ap;
va_start (ap, fmt);
pupa_vprintf (fmt, ap);
va_end (ap);
pupa_stop ();
}
void
pupa_print_error (void)
{
if (pupa_errno != PUPA_ERR_NONE)
pupa_printf ("error: %s\n", pupa_errmsg);
}

158
kern/file.c Normal file
View file

@ -0,0 +1,158 @@
/* file.c - file I/O functions */
/*
* 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 <pupa/misc.h>
#include <pupa/err.h>
#include <pupa/file.h>
#include <pupa/mm.h>
#include <pupa/fs.h>
#include <pupa/device.h>
/* Get the device part of the filename NAME. It is enclosed by parentheses. */
char *
pupa_file_get_device_name (const char *name)
{
if (name[0] == '(')
{
char *p = pupa_strchr (name, ')');
char *ret;
if (! p)
{
pupa_error (PUPA_ERR_BAD_FILENAME, "missing `)'");
return 0;
}
ret = (char *) pupa_malloc (p - name);
if (! ret)
return 0;
pupa_memcpy (ret, name + 1, p - name - 1);
ret[p - name - 1] = '\0';
return ret;
}
return 0;
}
pupa_file_t
pupa_file_open (const char *name)
{
pupa_device_t device;
pupa_file_t file = 0;
char *device_name;
char *file_name;
device_name = pupa_file_get_device_name (name);
if (pupa_errno)
return 0;
/* Get the file part of NAME. */
file_name = pupa_strchr (name, ')');
if (file_name)
file_name++;
else
file_name = (char *) name;
device = pupa_device_open (device_name);
pupa_free (device_name);
if (! device)
goto fail;
file = (pupa_file_t) pupa_malloc (sizeof (*file));
if (! file)
goto fail;
file->device = device;
file->offset = 0;
file->data = 0;
file->read_hook = 0;
if (device->disk && file_name[0] != '/')
/* This is a block list. */
file->fs = &pupa_fs_blocklist;
else
{
file->fs = pupa_fs_probe (device);
if (! file->fs)
goto fail;
}
if ((file->fs->open) (file, file_name) != PUPA_ERR_NONE)
goto fail;
return file;
fail:
if (device)
pupa_device_close (device);
/* if (net) pupa_net_close (net); */
pupa_free (file);
return 0;
}
pupa_ssize_t
pupa_file_read (pupa_file_t file, char *buf, pupa_ssize_t len)
{
pupa_ssize_t res;
if (len == 0 || len > file->size - file->offset)
len = file->size - file->offset;
if (len == 0)
return 0;
res = (file->fs->read) (file, buf, len);
if (res > 0)
file->offset += res;
return res;
}
pupa_err_t
pupa_file_close (pupa_file_t file)
{
if (file->fs->close)
(file->fs->close) (file);
pupa_device_close (file->device);
pupa_free (file);
return pupa_errno;
}
pupa_ssize_t
pupa_file_seek (pupa_file_t file, pupa_ssize_t offset)
{
pupa_ssize_t old;
if (offset < 0 || offset >= file->size)
{
pupa_error (PUPA_ERR_OUT_OF_RANGE,
"attempt to seek outside of the file");
return -1;
}
old = file->offset;
file->offset = offset;
return old;
}

224
kern/fs.c Normal file
View file

@ -0,0 +1,224 @@
/* fs.c - filesystem manager */
/*
* 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 <pupa/disk.h>
#include <pupa/net.h>
#include <pupa/fs.h>
#include <pupa/file.h>
#include <pupa/err.h>
#include <pupa/misc.h>
#include <pupa/types.h>
#include <pupa/mm.h>
#include <pupa/term.h>
static pupa_fs_t pupa_fs_list;
void
pupa_fs_register (pupa_fs_t fs)
{
fs->next = pupa_fs_list;
pupa_fs_list = fs;
}
void
pupa_fs_unregister (pupa_fs_t fs)
{
pupa_fs_t *p, q;
for (p = &pupa_fs_list, q = *p; q; p = &(q->next), q = q->next)
if (q == fs)
{
*p = q->next;
break;
}
}
void
pupa_fs_iterate (int (*hook) (const pupa_fs_t fs))
{
pupa_fs_t p;
for (p = pupa_fs_list; p; p = p->next)
if (hook (p))
break;
}
pupa_fs_t
pupa_fs_probe (pupa_device_t device)
{
pupa_fs_t p;
auto int dummy_func (const char *filename, int dir);
int dummy_func (const char *filename __attribute__ ((unused)),
int dir __attribute__ ((unused)))
{
return 1;
}
if (device->disk)
{
for (p = pupa_fs_list; p; p = p->next)
{
(p->dir) (device, "/", dummy_func);
if (pupa_errno == PUPA_ERR_NONE)
return p;
if (pupa_errno != PUPA_ERR_BAD_FS)
return 0;
pupa_errno = PUPA_ERR_NONE;
}
}
else if (device->net->fs)
return device->net->fs;
pupa_error (PUPA_ERR_UNKNOWN_FS, "unknown filesystem");
return 0;
}
/* Block list support routines. */
struct pupa_fs_block
{
unsigned long offset;
unsigned long length;
};
static pupa_err_t
pupa_fs_blocklist_open (pupa_file_t file, const char *name)
{
char *p = (char *) name;
unsigned num = 0;
unsigned i;
pupa_disk_t disk = file->device->disk;
struct pupa_fs_block *blocks;
/* First, count the number of blocks. */
do
{
num++;
p = pupa_strchr (p, ',');
}
while (p);
/* Allocate a block list. */
blocks = pupa_malloc (sizeof (struct pupa_fs_block) * (num + 1));
if (! blocks)
return 0;
file->size = 0;
p = (char *) name;
for (i = 0; i < num; i++)
{
if (*p != '+')
{
blocks[i].offset = pupa_strtoul (p, &p, 0);
if (pupa_errno != PUPA_ERR_NONE || *p != '+')
{
pupa_error (PUPA_ERR_BAD_FILENAME,
"invalid file name `%s'", name);
goto fail;
}
}
else
blocks[i].offset = 0;
p++;
blocks[i].length = pupa_strtoul (p, &p, 0);
if (pupa_errno != PUPA_ERR_NONE
|| blocks[i].length == 0
|| (*p && *p != ',' && ! pupa_isspace (*p)))
{
pupa_error (PUPA_ERR_BAD_FILENAME,
"invalid file name `%s'", name);
goto fail;
}
if (disk->total_sectors < blocks[i].offset + blocks[i].length)
{
pupa_error (PUPA_ERR_BAD_FILENAME, "beyond the total sectors");
goto fail;
}
file->size += (blocks[i].length << PUPA_DISK_SECTOR_BITS);
p++;
}
blocks[i].length = 0;
file->data = blocks;
return PUPA_ERR_NONE;
fail:
pupa_free (blocks);
return pupa_errno;
}
static pupa_ssize_t
pupa_fs_blocklist_read (pupa_file_t file, char *buf, pupa_ssize_t len)
{
struct pupa_fs_block *p;
unsigned long sector;
unsigned long offset;
pupa_ssize_t ret = 0;
if (len > file->size - file->offset)
len = file->size - file->offset;
sector = (file->offset >> PUPA_DISK_SECTOR_BITS);
offset = (file->offset & (PUPA_DISK_SECTOR_SIZE - 1));
for (p = file->data; p->length && len > 0; p++)
{
if (sector < p->length)
{
pupa_ssize_t size;
size = len;
if (((size + offset + PUPA_DISK_SECTOR_SIZE - 1)
>> PUPA_DISK_SECTOR_BITS) > p->length - sector)
size = ((p->length - sector) << PUPA_DISK_SECTOR_BITS) - offset;
if (pupa_disk_read (file->device->disk, p->offset + sector, offset,
size, buf) != PUPA_ERR_NONE)
return -1;
ret += size;
len -= size;
sector -= ((size + offset) >> PUPA_DISK_SECTOR_BITS);
offset = ((size + offset) & (PUPA_DISK_SECTOR_SIZE - 1));
}
else
sector -= p->length;
}
return ret;
}
struct pupa_fs pupa_fs_blocklist =
{
.name = "blocklist",
.dir = 0,
.open = pupa_fs_blocklist_open,
.read = pupa_fs_blocklist_read,
.close = 0,
.next = 0
};

126
kern/i386/dl.c Normal file
View file

@ -0,0 +1,126 @@
/* dl-386.c - arch-dependent part of 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 <pupa/dl.h>
#include <pupa/elf.h>
#include <pupa/misc.h>
#include <pupa/err.h>
/* Check if EHDR is a valid ELF header. */
int
pupa_arch_dl_check_header (void *ehdr, unsigned size)
{
Elf32_Ehdr *e = ehdr;
/* Check the header size. */
if (size < sizeof (Elf32_Ehdr))
return 0;
/* Check the magic numbers. */
if (e->e_ident[EI_MAG0] != ELFMAG0
|| e->e_ident[EI_MAG1] != ELFMAG1
|| e->e_ident[EI_MAG2] != ELFMAG2
|| e->e_ident[EI_MAG3] != ELFMAG3
|| e->e_version != EV_CURRENT
|| e->e_ident[EI_CLASS] != ELFCLASS32
|| e->e_ident[EI_DATA] != ELFDATA2LSB
|| e->e_machine != EM_386
|| e->e_type != ET_REL)
return 0;
/* Make sure that every section is within the core. */
if (size < e->e_shoff + e->e_shentsize * e->e_shnum)
return 0;
return 1;
}
/* Relocate symbols. */
pupa_err_t
pupa_arch_dl_relocate_symbols (pupa_dl_t mod, void *ehdr)
{
Elf32_Ehdr *e = ehdr;
Elf32_Shdr *s;
Elf32_Sym *symtab;
Elf32_Word entsize;
unsigned i;
/* Find a symbol table. */
for (i = 0, s = (Elf32_Shdr *) ((char *) e + e->e_shoff);
i < e->e_shnum;
i++, s = (Elf32_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 symtab found");
symtab = (Elf32_Sym *) ((char *) e + s->sh_offset);
entsize = s->sh_entsize;
for (i = 0, s = (Elf32_Shdr *) ((char *) e + e->e_shoff);
i < e->e_shnum;
i++, s = (Elf32_Shdr *) ((char *) s + e->e_shentsize))
if (s->sh_type == SHT_REL)
{
pupa_dl_segment_t seg;
/* Find the target segment. */
for (seg = mod->segment; seg; seg = seg->next)
if (seg->section == s->sh_info)
break;
if (seg)
{
Elf32_Rel *rel, *max;
for (rel = (Elf32_Rel *) ((char *) e + s->sh_offset),
max = rel + s->sh_size / s->sh_entsize;
rel < max;
rel++)
{
Elf32_Word *addr;
Elf32_Sym *sym;
if (seg->size < rel->r_offset)
return pupa_error (PUPA_ERR_BAD_MODULE,
"reloc offset is out of the segment");
addr = (Elf32_Word *) ((char *) seg->addr + rel->r_offset);
sym = (Elf32_Sym *) ((char *) symtab
+ entsize * ELF32_R_SYM (rel->r_info));
switch (ELF32_R_TYPE (rel->r_info))
{
case R_386_32:
*addr += sym->st_value;
break;
case R_386_PC32:
*addr += (sym->st_value - (Elf32_Word) seg->addr
- rel->r_offset);
break;
}
}
}
}
return PUPA_ERR_NONE;
}

114
kern/i386/pc/init.c Normal file
View file

@ -0,0 +1,114 @@
/*
* PUPA -- Preliminary Universal Programming Architecture for GRUB
* Copyright (C) 2002 Yoshinori K. Okuji <okuji@enbug.org>
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <pupa/kernel.h>
#include <pupa/mm.h>
#include <pupa/machine/init.h>
#include <pupa/machine/memory.h>
#include <pupa/machine/console.h>
#include <pupa/machine/biosdisk.h>
#include <pupa/types.h>
#include <pupa/err.h>
void
pupa_machine_init (void)
{
pupa_uint32_t cont;
struct pupa_machine_mmap_entry entry;
pupa_size_t lower_mem = (pupa_get_memsize (0) << 10);
pupa_addr_t end_addr = pupa_get_end_addr ();
/* Initialize the console as early as possible. */
pupa_console_init ();
/* Sanity check. */
if (lower_mem < PUPA_MEMORY_MACHINE_RESERVED_END)
pupa_fatal ("too small memory");
/* Turn on Gate A20 to access >1MB. */
pupa_gate_a20 (1);
/* Add the lower memory into free memory. */
if (lower_mem >= PUPA_MEMORY_MACHINE_RESERVED_END)
pupa_mm_init_region ((void *) PUPA_MEMORY_MACHINE_RESERVED_END,
lower_mem - PUPA_MEMORY_MACHINE_RESERVED_END);
pupa_mm_init_region ((void *) end_addr,
PUPA_MEMORY_MACHINE_RESERVED_START - end_addr);
/* Check if pupa_get_mmap_entry works. */
cont = pupa_get_mmap_entry (&entry, 0);
if (entry.size)
do
{
/* Avoid the lower memory. */
if (entry.addr < 0x100000)
{
if (entry.len <= 0x100000 - entry.addr)
goto next;
entry.len -= 0x100000 - entry.addr;
entry.addr = 0x100000;
}
/* Ignore >4GB. */
if (entry.addr <= 0xFFFFFFFF && entry.type == 1)
{
pupa_addr_t addr;
pupa_size_t len;
addr = (pupa_addr_t) entry.addr;
len = ((addr + entry.len > 0xFFFFFFFF)
? 0xFFFFFFFF - addr
: (pupa_size_t) entry.len);
pupa_mm_init_region ((void *) addr, len);
}
next:
if (! cont)
break;
cont = pupa_get_mmap_entry (&entry, cont);
}
while (entry.size);
else
{
pupa_uint32_t eisa_mmap = pupa_get_eisa_mmap ();
if (eisa_mmap)
{
if ((eisa_mmap & 0xFFFF) == 0x3C00)
pupa_mm_init_region ((void *) 0x100000,
(eisa_mmap << 16) + 0x100000 * 15);
else
{
pupa_mm_init_region ((void *) 0x100000,
(eisa_mmap & 0xFFFF) << 10);
pupa_mm_init_region ((void *) 0x1000000, eisa_mmap << 16);
}
}
else
pupa_mm_init_region ((void *) 0x100000,
(pupa_size_t) pupa_get_memsize (1) << 10);
}
/* The memory system was initialized, thus register built-in devices. */
pupa_biosdisk_init ();
}

1377
kern/i386/pc/startup.S Normal file

File diff suppressed because it is too large Load diff

67
kern/loader.c Normal file
View file

@ -0,0 +1,67 @@
/*
* 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 <pupa/loader.h>
#include <pupa/misc.h>
#include <pupa/mm.h>
#include <pupa/err.h>
static pupa_err_t (*pupa_loader_load_module_func) (int argc, char *argv[]);
static pupa_err_t (*pupa_loader_boot_func) (void);
static pupa_err_t (*pupa_loader_unload_func) (void);
static int pupa_loader_loaded;
void
pupa_loader_set (pupa_err_t (*load_module) (int argc, char *argv[]),
pupa_err_t (*boot) (void),
pupa_err_t (*unload) (void))
{
if (pupa_loader_loaded && pupa_loader_unload_func)
if (pupa_loader_unload_func () != PUPA_ERR_NONE)
return;
pupa_loader_load_module_func = load_module;
pupa_loader_boot_func = boot;
pupa_loader_unload_func = unload;
pupa_loader_loaded = 1;
}
pupa_err_t
pupa_loader_load_module (int argc, char *argv[])
{
if (! pupa_loader_loaded)
return pupa_error (PUPA_ERR_NO_KERNEL, "no loaded kernel");
if (! pupa_loader_load_module_func)
return pupa_error (PUPA_ERR_BAD_OS, "module not supported");
return pupa_loader_load_module_func (argc, argv);
}
pupa_err_t
pupa_loader_boot (void)
{
if (! pupa_loader_loaded)
return pupa_error (PUPA_ERR_NO_KERNEL, "no loaded kernel");
return (pupa_loader_boot_func) ();
}

86
kern/main.c Normal file
View file

@ -0,0 +1,86 @@
/*
* PUPA -- Preliminary Universal Programming Architecture for GRUB
* Copyright (C) 2002 Yoshinori K. Okuji <okuji@enbug.org>
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <pupa/kernel.h>
#include <pupa/misc.h>
#include <pupa/mm.h>
#include <pupa/symbol.h>
#include <pupa/dl.h>
#include <pupa/term.h>
#include <pupa/rescue.h>
/* Return the end of the core image. */
pupa_addr_t
pupa_get_end_addr (void)
{
return pupa_total_module_size + pupa_end_addr;
}
/* Load all modules in core. */
static void
pupa_load_modules (void)
{
struct pupa_module_header *header;
for (header = (struct pupa_module_header *) pupa_end_addr;
header < (struct pupa_module_header *) pupa_get_end_addr ();
header = (struct pupa_module_header *) ((char *) header + header->size))
{
if (! pupa_dl_load_core ((char *) header + header->offset,
(header->size - header->offset)))
pupa_fatal ("%s", pupa_errmsg);
}
}
/* Add the region where modules reside into dynamic memory. */
static void
pupa_add_unused_region (void)
{
if (pupa_total_module_size)
pupa_mm_init_region ((void *) pupa_end_addr, pupa_total_module_size);
}
/* The main routine. */
void
pupa_main (void)
{
void (*normal_func) (void);
/* First of all, initialize the machine. */
pupa_machine_init ();
/* Hello. */
pupa_setcolorstate (PUPA_TERM_COLOR_HIGHLIGHT);
pupa_printf ("Welcome to PUPA!");
pupa_setcolorstate (PUPA_TERM_COLOR_STANDARD);
pupa_printf ("\n\n");
pupa_register_exported_symbols ();
pupa_load_modules ();
pupa_add_unused_region ();
/* If the function pupa_enter_normal_mode is present, call it. */
normal_func = pupa_dl_resolve_symbol ("pupa_enter_normal_mode");
if (normal_func)
(*normal_func) ();
/* If pupa_enter_normal_mode fails or doesn't exist, enter rescue mode. */
pupa_printf ("Entering into rescue mode...\n");
pupa_enter_rescue_mode ();
}

377
kern/misc.c Normal file
View file

@ -0,0 +1,377 @@
/* misc.c - definitions of misc functions */
/*
* PUPA -- Preliminary Universal Programming Architecture for GRUB
* Copyright (C) 1999,2000,2001,2002 Free Software Foundation, Inc.
* 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 <pupa/misc.h>
#include <pupa/err.h>
#include <pupa/mm.h>
#include <stdarg.h>
#include <pupa/term.h>
void *
pupa_memcpy (void *dest, const void *src, pupa_size_t n)
{
char *d = (char *) dest;
char *s = (char *) src;
while (n--)
*d++ = *s++;
return dest;
}
int
pupa_printf (const char *fmt, ...)
{
va_list ap;
int ret;
va_start (ap, fmt);
ret = pupa_vprintf (fmt, ap);
va_end (ap);
return ret;
}
int
pupa_vprintf (const char *fmt, va_list args)
{
return pupa_vsprintf (0, fmt, args);
}
int
pupa_memcmp (const void *s1, const void *s2, pupa_size_t n)
{
const char *t1 = s1;
const char *t2 = s2;
while (n--)
{
if (*t1 != *t2)
return (int) *t1 - (int) *t2;
t1++;
t2++;
}
return 0;
}
int
pupa_strcmp (const char *s1, const char *s2)
{
while (*s1 && *s2)
{
if (*s1 != *s2)
return (int) *s1 - (int) *s2;
s1++;
s2++;
}
return (int) *s1 - (int) *s2;
}
char *
pupa_strchr (const char *s, int c)
{
while (*s)
{
if (*s == c)
return (char *) s;
s++;
}
return 0;
}
char *
pupa_strrchr (const char *s, int c)
{
char *p = 0;
while (*s)
{
if (*s == c)
p = (char *) s;
s++;
}
return p;
}
int
pupa_isspace (int c)
{
return (c == '\n' || c == '\r' || c == ' ' || c == '\t');
}
int
pupa_isprint (int c)
{
return (c >= ' ' && c <= '~');
}
int
pupa_isalpha (int c)
{
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}
int
pupa_tolower (int c)
{
if (c >= 'A' && c <= 'Z')
return c - 'A' + 'a';
return c;
}
unsigned long
pupa_strtoul (const char *str, char **end, int base)
{
unsigned long num = 0;
int found = 0;
/* Skip white spaces. */
while (*str && pupa_isspace (*str))
str++;
/* Guess the base, if not specified. The prefix `0x' means 16, and
the prefix `0' means 8. */
if (str[0] == '0')
{
if (str[1] == 'x')
{
if (base == 0 || base == 16)
{
base = 16;
str += 2;
}
}
else if (str[1] >= '0' && str[1] <= '7')
base = 8;
}
if (base == 0)
base = 10;
while (*str)
{
unsigned long digit;
digit = pupa_tolower (*str) - '0';
if (digit > 9)
{
digit += '0' - 'a' + 10;
if (digit >= (unsigned long) base)
break;
}
found = 1;
if (num > (~0UL - digit) / base)
{
pupa_error (PUPA_ERR_OUT_OF_RANGE, "overflow is detected");
return 0;
}
num += num * base + digit;
str++;
}
if (! found)
{
pupa_error (PUPA_ERR_BAD_NUMBER, "unrecognized number");
return 0;
}
if (end)
*end = (char *) str;
return num;
}
char *
pupa_strdup (const char *s)
{
pupa_size_t len;
char *p;
len = pupa_strlen (s) + 1;
p = (char *) pupa_malloc (len);
if (! p)
return 0;
return pupa_memcpy (p, s, len);
}
void *
pupa_memset (void *s, int c, pupa_size_t n)
{
unsigned char *p = (unsigned char *) s;
while (n--)
*p++ = (unsigned char) c;
return s;
}
pupa_size_t
pupa_strlen (const char *s)
{
char *p = (char *) s;
while (*p)
p++;
return p - s;
}
static inline void
pupa_reverse (char *str)
{
char *p = str + pupa_strlen (str) - 1;
while (str < p)
{
char tmp;
tmp = *str;
*str = *p;
*p = tmp;
str++;
p--;
}
}
char *
pupa_itoa (char *str, int c, unsigned n)
{
unsigned base = (c == 'x') ? 16 : 10;
char *p;
if ((int) n < 0 && c == 'd')
{
n = (unsigned) (-((int) n));
*str++ = '-';
}
p = str;
do
{
unsigned d = n % base;
*p++ = (d > 9) ? d + 'a' - 10 : d + '0';
}
while (n /= base);
*p = 0;
pupa_reverse (str);
return p;
}
int
pupa_vsprintf (char *str, const char *fmt, va_list args)
{
char c;
int count = 0;
auto void write_char (char c);
auto void write_str (const char *s);
void write_char (char c)
{
if (str)
*str++ = c;
else
pupa_putchar (c);
count++;
}
void write_str (const char *s)
{
while (*s)
write_char (*s++);
}
while ((c = *fmt++) != 0)
{
if (c != '%')
write_char (c);
else
{
char tmp[16];
char *p;
int n;
c = *fmt++;
switch (c)
{
case 'p':
write_str ("0x");
c = 'x';
/* fall through */
case 'x':
case 'u':
case 'd':
n = va_arg (args, int);
pupa_itoa (tmp, c, n);
write_str (tmp);
break;
case 'c':
n = va_arg (args, int);
write_char (n);
break;
case 's':
p = va_arg (args, char *);
if (p)
write_str (p);
else
write_str ("(null)");
break;
default:
write_char (c);
break;
}
}
}
if (str)
*str = '\0';
return count;
}
int
pupa_sprintf (char *str, const char *fmt, ...)
{
va_list ap;
int ret;
va_start (ap, fmt);
ret = pupa_vsprintf (str, fmt, ap);
va_end (ap);
return ret;
}

352
kern/mm.c Normal file
View file

@ -0,0 +1,352 @@
/* mm.c - functions for memory manager */
/*
* 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/mm.h>
#include <pupa/misc.h>
#include <pupa/err.h>
#include <pupa/types.h>
#include <pupa/disk.h>
/* Magic words. */
#define PUPA_MM_FREE_MAGIC 0x2d3c2808
#define PUPA_MM_ALLOC_MAGIC 0x6db08fa4
typedef struct pupa_mm_header
{
struct pupa_mm_header *next;
pupa_size_t size;
pupa_size_t magic;
#if PUPA_CPU_SIZEOF_VOID_P == 4
char padding[4];
#elif PUPA_CPU_SIZEOF_VOID_P == 8
char padding[8];
#else
# error "unknown word size"
#endif
}
*pupa_mm_header_t;
#if PUPA_CPU_SIZEOF_VOID_P == 4
# define PUPA_MM_ALIGN_LOG2 4
#elif PUPA_CPU_SIZEOF_VOID_P == 8
# define PUPA_MM_ALIGN_LOG2 8
#endif
#define PUPA_MM_ALIGN (1 << PUPA_MM_ALIGN_LOG2)
typedef struct pupa_mm_region
{
struct pupa_mm_header *first;
struct pupa_mm_region *next;
pupa_addr_t addr;
pupa_size_t size;
}
*pupa_mm_region_t;
static pupa_mm_region_t base;
/* Get a header from the pointer PTR, and set *P and *R to a pointer
to the header and a pointer to its region, respectively. PTR must
be allocated. */
static void
get_header_from_pointer (void *ptr, pupa_mm_header_t *p, pupa_mm_region_t *r)
{
if ((unsigned) ptr & (PUPA_MM_ALIGN - 1))
pupa_fatal ("unaligned pointer %p", ptr);
for (*r = base; *r; *r = (*r)->next)
if ((unsigned) ptr > (*r)->addr
&& (unsigned) ptr <= (*r)->addr + (*r)->size)
break;
if (! *r)
pupa_fatal ("out of range pointer %p", ptr);
*p = (pupa_mm_header_t) ptr - 1;
if ((*p)->magic != PUPA_MM_ALLOC_MAGIC)
pupa_fatal ("alloc magic is broken at %p", *p);
}
/* Initialize a region starting from ADDR and whose size is SIZE,
to use it as free space. */
void
pupa_mm_init_region (void *addr, pupa_size_t size)
{
pupa_mm_header_t h;
pupa_mm_region_t r, *p, q;
/* If this region is too small, ignore it. */
if (size < PUPA_MM_ALIGN * 2)
return;
/* Allocate a region from the head. */
r = (pupa_mm_region_t) (((pupa_addr_t) addr + PUPA_MM_ALIGN - 1)
& (~(PUPA_MM_ALIGN - 1)));
size -= (char *) r - (char *) addr + sizeof (*r);
h = (pupa_mm_header_t) ((char *) r + PUPA_MM_ALIGN);
h->next = h;
h->magic = PUPA_MM_FREE_MAGIC;
h->size = (size >> PUPA_MM_ALIGN_LOG2);
r->first = h;
r->addr = (pupa_addr_t) h;
r->size = (h->size << PUPA_MM_ALIGN_LOG2);
/* Find where to insert this region. Put a smaller one before bigger ones,
to prevent fragmentations. */
for (p = &base, q = *p; q; p = &(q->next), q = *p)
if (q->size > r->size)
break;
*p = r;
r->next = q;
}
/* Allocate the number of units N with the alignment ALIGN from the ring
buffer starting from *FIRST. ALIGN must be a power of two. Return a
non-NULL if successful, otherwise return NULL. */
static void *
pupa_real_malloc (pupa_mm_header_t *first, pupa_size_t n, pupa_size_t align)
{
pupa_mm_header_t p, q;
if ((*first)->magic == PUPA_MM_ALLOC_MAGIC)
return 0;
for (q = *first, p = q->next; ; q = p, p = p->next)
{
pupa_off_t extra;
extra = ((pupa_addr_t) (p + 1) >> PUPA_MM_ALIGN_LOG2) % align;
if (extra)
extra = align - extra;
if (! p)
pupa_fatal ("null in the ring");
if (p->magic != PUPA_MM_FREE_MAGIC)
pupa_fatal ("free magic is broken at %p", p);
if (p->size >= n + extra)
{
if (extra == 0 && p->size == n)
{
q->next = p->next;
p->magic = PUPA_MM_ALLOC_MAGIC;
}
else if (extra == 0 || p->size == n + extra)
{
p->size -= n;
p += p->size;
p->size = n;
p->magic = PUPA_MM_ALLOC_MAGIC;
}
else
{
pupa_mm_header_t r;
r = p + extra + n;
r->magic = PUPA_MM_FREE_MAGIC;
r->size = p->size - extra - n;
r->next = p->next;
p->size = extra;
p->next = r;
p += extra;
p->size = n;
p->magic = PUPA_MM_ALLOC_MAGIC;
}
*first = q;
return p + 1;
}
if (p == *first)
break;
}
return 0;
}
/* Allocate SIZE bytes with the alignment ALIGN and return the pointer. */
void *
pupa_memalign (pupa_size_t align, pupa_size_t size)
{
pupa_mm_region_t r;
pupa_size_t n = ((size + PUPA_MM_ALIGN - 1) >> PUPA_MM_ALIGN_LOG2) + 1;
int first = 1;
align = (align >> PUPA_MM_ALIGN_LOG2);
if (align == 0)
align = 1;
again:
for (r = base; r; r = r->next)
{
void *p;
p = pupa_real_malloc (&(r->first), n, align);
if (p)
return p;
}
/* If failed, invalidate disk caches to increase free memory. */
if (first)
{
pupa_disk_cache_invalidate_all ();
first = 0;
goto again;
}
pupa_error (PUPA_ERR_OUT_OF_MEMORY, "out of memory");
return 0;
}
/* Allocate SIZE bytes and return the pointer. */
void *
pupa_malloc (pupa_size_t size)
{
return pupa_memalign (0, size);
}
/* Deallocate the pointer PTR. */
void
pupa_free (void *ptr)
{
pupa_mm_header_t p;
pupa_mm_region_t r;
if (! ptr)
return;
get_header_from_pointer (ptr, &p, &r);
if (p == r->first)
{
p->magic = PUPA_MM_FREE_MAGIC;
p->next = p;
}
else
{
pupa_mm_header_t q;
for (q = r->first; q >= p || q->next <= p; q = q->next)
{
if (q->magic != PUPA_MM_FREE_MAGIC)
pupa_fatal ("free magic is broken at %p", q);
if (q >= q->next && (q < p || q->next > p))
break;
}
p->magic = PUPA_MM_FREE_MAGIC;
p->next = q->next;
q->next = p;
if (p + p->size == p->next)
{
p->next->magic = 0;
p->size += p->next->size;
p->next = p->next->next;
}
if (q + q->size == p)
{
p->magic = 0;
q->size += p->size;
q->next = p->next;
}
r->first = q;
}
}
/* Reallocate SIZE bytes and return the pointer. The contents will be
the same as that of PTR. */
void *
pupa_realloc (void *ptr, pupa_size_t size)
{
pupa_mm_header_t p;
pupa_mm_region_t r;
void *q;
pupa_size_t n;
if (! ptr)
return pupa_malloc (size);
if (! size)
{
pupa_free (ptr);
return 0;
}
/* FIXME: Not optimal. */
n = ((size + PUPA_MM_ALIGN - 1) >> PUPA_MM_ALIGN_LOG2) + 1;
get_header_from_pointer (ptr, &p, &r);
if (p->size >= n)
return p;
q = pupa_malloc (size);
if (! q)
return q;
pupa_memcpy (q, ptr, size);
pupa_free (ptr);
return q;
}
#if MM_DEBUG
void
pupa_mm_dump (unsigned lineno)
{
pupa_mm_region_t r;
pupa_printf ("called at line %u\n", lineno);
for (r = base; r; r = r->next)
{
pupa_mm_header_t p;
for (p = (pupa_mm_header_t) ((r->addr + PUPA_MM_ALIGN - 1)
& (~(PUPA_MM_ALIGN - 1)));
(pupa_addr_t) p < r->addr + r->size;
p++)
{
switch (p->magic)
{
case PUPA_MM_FREE_MAGIC:
pupa_printf ("F:%p:%u:%p\n",
p, p->size << PUPA_MM_ALIGN_LOG2, p->next);
break;
case PUPA_MM_ALLOC_MAGIC:
pupa_printf ("A:%p:%u\n", p, p->size << PUPA_MM_ALIGN_LOG2);
break;
}
}
}
pupa_printf ("\n");
}
#endif /* MM_DEBUG */

576
kern/rescue.c Normal file
View file

@ -0,0 +1,576 @@
/* rescue.c - rescue mode */
/*
* PUPA -- Preliminary Universal Programming Architecture for GRUB
* Copyright (C) 2002 Yoshinori K. Okuji <okuji@enbug.org>
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <pupa/kernel.h>
#include <pupa/term.h>
#include <pupa/misc.h>
#include <pupa/disk.h>
#include <pupa/file.h>
#include <pupa/mm.h>
#include <pupa/err.h>
#include <pupa/loader.h>
#include <pupa/machine/partition.h>
#define PUPA_RESCUE_BUF_SIZE 256
#define PUPA_RESCUE_MAX_ARGS 20
struct pupa_rescue_command
{
const char *name;
void (*func) (int argc, char *argv[]);
const char *message;
struct pupa_rescue_command *next;
};
typedef struct pupa_rescue_command *pupa_rescue_command_t;
static char buf[PUPA_RESCUE_BUF_SIZE];
static pupa_rescue_command_t pupa_rescue_command_list;
void
pupa_rescue_register_command (const char *name,
void (*func) (int argc, char *argv[]),
const char *message)
{
pupa_rescue_command_t cmd;
cmd = (pupa_rescue_command_t) pupa_malloc (sizeof (*cmd));
if (! cmd)
return;
cmd->name = name;
cmd->func = func;
cmd->message = message;
cmd->next = pupa_rescue_command_list;
pupa_rescue_command_list = cmd;
}
void
pupa_rescue_unregister_command (const char *name)
{
pupa_rescue_command_t *p, q;
for (p = &pupa_rescue_command_list, q = *p; q; p = &(q->next), q = q->next)
if (pupa_strcmp (name, q->name) == 0)
{
*p = q->next;
pupa_free (q);
break;
}
}
/* Prompt to input a command and read the line. */
static void
pupa_rescue_get_command_line (const char *prompt)
{
int c;
int pos = 0;
pupa_printf (prompt);
pupa_memset (buf, 0, PUPA_RESCUE_BUF_SIZE);
while ((c = PUPA_TERM_ASCII_CHAR (pupa_getkey ())) != '\n' && c != '\r')
{
if (pupa_isprint (c))
{
if (pos < PUPA_RESCUE_BUF_SIZE - 1)
{
buf[pos++] = c;
pupa_putchar (c);
}
}
else if (c == '\b')
{
if (pos > 0)
{
buf[--pos] = 0;
pupa_putchar (c);
pupa_putchar (' ');
pupa_putchar (c);
}
}
}
pupa_putchar ('\n');
}
/* Get the next word in STR and return a next pointer. */
static char *
next_word (char **str)
{
char *word;
char *p = *str;
/* Skip spaces. */
while (*p && pupa_isspace (*p))
p++;
word = p;
/* Find a space. */
while (*p && ! pupa_isspace (*p))
p++;
*p = '\0';
*str = p + 1;
return word;
}
/* boot */
static void
pupa_rescue_cmd_boot (int argc __attribute__ ((unused)),
char *argv[] __attribute__ ((unused)))
{
pupa_loader_boot ();
}
/* cat FILE */
static void
pupa_rescue_cmd_cat (int argc, char *argv[])
{
pupa_file_t file;
char buf[PUPA_DISK_SECTOR_SIZE];
pupa_ssize_t size;
if (argc < 1)
{
pupa_error (PUPA_ERR_BAD_ARGUMENT, "no file specified");
return;
}
file = pupa_file_open (argv[0]);
if (! file)
return;
while ((size = pupa_file_read (file, buf, sizeof (buf))) > 0)
{
int i;
for (i = 0; i < size; i++)
{
unsigned char c = buf[i];
if (pupa_isprint (c) || pupa_isspace (c))
pupa_putchar (c);
else
{
pupa_setcolorstate (PUPA_TERM_COLOR_HIGHLIGHT);
pupa_printf ("<%x>", (int) c);
pupa_setcolorstate (PUPA_TERM_COLOR_STANDARD);
}
}
}
pupa_putchar ('\n');
pupa_file_close (file);
}
static int
pupa_rescue_print_disks (const char *name)
{
pupa_device_t dev;
auto int print_partition (const pupa_partition_t p);
int print_partition (const pupa_partition_t p)
{
char *pname = pupa_partition_get_name (p);
if (pname)
{
pupa_printf ("(%s,%s) ", name, pname);
pupa_free (pname);
}
return 0;
}
dev = pupa_device_open (name);
pupa_errno = PUPA_ERR_NONE;
if (dev)
{
pupa_printf ("(%s) ", name);
if (dev->disk && dev->disk->has_partitions)
{
pupa_partition_iterate (dev->disk, print_partition);
pupa_errno = PUPA_ERR_NONE;
}
pupa_device_close (dev);
}
return 0;
}
static int
pupa_rescue_print_files (const char *filename, int dir)
{
pupa_printf ("%s%s ", filename, dir ? "/" : "");
return 0;
}
/* ls [ARG] */
static void
pupa_rescue_cmd_ls (int argc, char *argv[])
{
if (argc < 1)
{
pupa_disk_dev_iterate (pupa_rescue_print_disks);
pupa_putchar ('\n');
}
else
{
char *device_name;
pupa_device_t dev;
pupa_fs_t fs;
char *path;
device_name = pupa_file_get_device_name (argv[0]);
dev = pupa_device_open (device_name);
if (! dev)
goto fail;
fs = pupa_fs_probe (dev);
path = pupa_strchr (argv[0], '/');
if (! path && ! device_name)
{
pupa_error (PUPA_ERR_BAD_ARGUMENT, "invalid argument");
goto fail;
}
if (! path)
{
if (pupa_errno == PUPA_ERR_UNKNOWN_FS)
pupa_errno = PUPA_ERR_NONE;
pupa_printf ("(%s): Filesystem is %s.\n",
device_name, fs ? fs->name : "unknown");
}
else if (fs)
{
(fs->dir) (dev, path, pupa_rescue_print_files);
pupa_putchar ('\n');
}
fail:
if (dev)
pupa_device_close (dev);
pupa_free (device_name);
}
}
/* help */
static void
pupa_rescue_cmd_help (int argc __attribute__ ((unused)),
char *argv[] __attribute__ ((unused)))
{
pupa_rescue_command_t p, q;
/* Sort the commands. This is not a good algorithm, but this is enough,
because rescue mode has a small number of commands. */
for (p = pupa_rescue_command_list; p; p = p->next)
for (q = p->next; q; q = q->next)
if (pupa_strcmp (p->name, q->name) > 0)
{
struct pupa_rescue_command tmp;
tmp.name = p->name;
tmp.func = p->func;
tmp.message = p->message;
p->name = q->name;
p->func = q->func;
p->message = q->message;
q->name = tmp.name;
q->func = tmp.func;
q->message = tmp.message;
}
/* Print them. */
for (p = pupa_rescue_command_list; p; p = p->next)
pupa_printf ("%s\t%s\n", p->name, p->message);
}
#if 0
static void
pupa_rescue_cmd_info (void)
{
extern void pupa_disk_cache_get_performance (unsigned long *,
unsigned long *);
unsigned long hits, misses;
pupa_disk_cache_get_performance (&hits, &misses);
pupa_printf ("Disk cache: hits = %u, misses = %u ", hits, misses);
if (hits + misses)
{
unsigned long ratio = hits * 10000 / (hits + misses);
pupa_printf ("(%u.%u%%)\n", ratio / 100, ratio % 100);
}
else
pupa_printf ("(N/A)\n");
}
#endif
/* (module|initrd) FILE [ARGS] */
static void
pupa_rescue_cmd_module (int argc, char *argv[])
{
pupa_loader_load_module (argc, argv);
}
/* root [DEVICE] */
static void
pupa_rescue_cmd_root (int argc, char *argv[])
{
pupa_device_t dev;
pupa_fs_t fs;
if (argc > 0)
{
char *device_name = pupa_file_get_device_name (argv[0]);
if (! device_name)
return;
pupa_device_set_root (device_name);
pupa_free (device_name);
}
dev = pupa_device_open (0);
if (! dev)
return;
fs = pupa_fs_probe (dev);
if (pupa_errno == PUPA_ERR_UNKNOWN_FS)
pupa_errno = PUPA_ERR_NONE;
pupa_printf ("(%s): Filesystem is %s.\n",
pupa_device_get_root (), fs ? fs->name : "unknown");
pupa_device_close (dev);
}
#if 0
static void
pupa_rescue_cmd_testload (int argc, char *argv[])
{
pupa_file_t file;
char *buf;
pupa_ssize_t size;
pupa_ssize_t pos;
auto void read_func (unsigned long sector, unsigned offset, unsigned len);
void read_func (unsigned long sector __attribute__ ((unused)),
unsigned offset __attribute__ ((unused)),
unsigned len __attribute__ ((unused)))
{
pupa_putchar ('.');
}
if (argc < 1)
{
pupa_error (PUPA_ERR_BAD_ARGUMENT, "no file specified");
return;
}
file = pupa_file_open (argv[0]);
if (! file)
return;
size = pupa_file_size (file) & ~(PUPA_DISK_SECTOR_SIZE - 1);
if (size == 0)
{
pupa_file_close (file);
return;
}
buf = pupa_malloc (size);
if (! buf)
goto fail;
pupa_printf ("Reading %s sequentially", argv[0]);
file->read_hook = read_func;
if (pupa_file_read (file, buf, size) != size)
goto fail;
pupa_printf (" Done.\n");
/* Read sequentially again. */
pupa_printf ("Reading %s sequentially again", argv[0]);
if (pupa_file_seek (file, 0) < 0)
goto fail;
for (pos = 0; pos < size; pos += PUPA_DISK_SECTOR_SIZE)
{
char sector[PUPA_DISK_SECTOR_SIZE];
if (pupa_file_read (file, sector, PUPA_DISK_SECTOR_SIZE)
!= PUPA_DISK_SECTOR_SIZE)
goto fail;
if (pupa_memcmp (sector, buf + pos, PUPA_DISK_SECTOR_SIZE) != 0)
{
pupa_printf ("\nDiffers in %d\n", pos);
goto fail;
}
}
pupa_printf (" Done.\n");
/* Read backwards and compare. */
pupa_printf ("Reading %s backwards", argv[0]);
pos = size;
while (pos > 0)
{
char sector[PUPA_DISK_SECTOR_SIZE];
pos -= PUPA_DISK_SECTOR_SIZE;
if (pupa_file_seek (file, pos) < 0)
goto fail;
if (pupa_file_read (file, sector, PUPA_DISK_SECTOR_SIZE)
!= PUPA_DISK_SECTOR_SIZE)
goto fail;
if (pupa_memcmp (sector, buf + pos, PUPA_DISK_SECTOR_SIZE) != 0)
{
int i;
pupa_printf ("\nDiffers in %d\n", pos);
for (i = 0; i < PUPA_DISK_SECTOR_SIZE; i++)
pupa_putchar (buf[pos + i]);
goto fail;
}
}
pupa_printf (" Done.\n");
fail:
pupa_file_close (file);
pupa_free (buf);
}
#endif
static void
pupa_rescue_cmd_dump (int argc, char *argv[])
{
pupa_uint8_t *addr;
pupa_size_t size = 4;
if (argc == 0)
{
pupa_error (PUPA_ERR_BAD_ARGUMENT, "no address specified");
return;
}
addr = (pupa_uint8_t *) pupa_strtoul (argv[0], 0, 0);
if (pupa_errno)
return;
if (argc > 1)
size = (pupa_size_t) pupa_strtoul (argv[1], 0, 0);
while (size--)
{
pupa_printf ("%x%x ", *addr >> 4, *addr & 0xf);
addr++;
}
}
/* Enter the rescue mode. */
void
pupa_enter_rescue_mode (void)
{
pupa_rescue_register_command ("boot", pupa_rescue_cmd_boot,
"boot an operating system");
pupa_rescue_register_command ("cat", pupa_rescue_cmd_cat,
"show the contents of a file");
pupa_rescue_register_command ("help", pupa_rescue_cmd_help,
"show this message");
pupa_rescue_register_command ("initrd", pupa_rescue_cmd_module,
"load an initrd");
pupa_rescue_register_command ("ls", pupa_rescue_cmd_ls,
"list devices or files");
pupa_rescue_register_command ("module", pupa_rescue_cmd_module,
"load an OS module");
pupa_rescue_register_command ("root", pupa_rescue_cmd_root,
"set a root device");
pupa_rescue_register_command ("dump", pupa_rescue_cmd_dump,
"dump memory");
while (1)
{
char *line = buf;
char *name;
int n;
pupa_rescue_command_t cmd;
char *args[PUPA_RESCUE_MAX_ARGS + 1];
/* Get a command line. */
pupa_rescue_get_command_line ("pupa rescue> ");
/* Get the command name. */
name = next_word (&line);
/* If nothing is specified, restart. */
if (*name == '\0')
continue;
/* Get arguments. */
for (n = 0; n <= PUPA_RESCUE_MAX_ARGS; n++)
{
char *arg = next_word (&line);
if (*arg)
args[n] = arg;
else
break;
}
args[n] = 0;
/* Find the command and execute it. */
for (cmd = pupa_rescue_command_list; cmd; cmd = cmd->next)
{
if (pupa_strcmp (name, cmd->name) == 0)
{
(cmd->func) (n, args);
break;
}
}
/* If not found, print an error message. */
if (! cmd)
{
pupa_printf ("Unknown command `%s'\n", name);
pupa_printf ("Try `help' for usage\n");
}
/* Print an error, if any. */
pupa_print_error ();
pupa_errno = PUPA_ERR_NONE;
}
}

153
kern/term.c Normal file
View file

@ -0,0 +1,153 @@
/*
* PUPA -- Preliminary Universal Programming Architecture for GRUB
* Copyright (C) 2002 Free Software Foundation, Inc.
* 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 <pupa/term.h>
#include <pupa/err.h>
#include <pupa/mm.h>
/* The list of terminals. */
static pupa_term_t pupa_term_list;
/* The current terminal. */
static pupa_term_t pupa_cur_term;
void
pupa_term_register (pupa_term_t term)
{
term->next = pupa_term_list;
pupa_term_list = term;
}
void
pupa_term_unregister (pupa_term_t term)
{
pupa_term_t *p, q;
for (p = &pupa_term_list, q = *p; q; p = &(q->next), q = q->next)
if (q == term)
{
*p = q->next;
break;
}
}
void
pupa_term_iterate (int (*hook) (pupa_term_t term))
{
pupa_term_t p;
for (p = pupa_term_list; p; p = p->next)
if (hook (p))
break;
}
void
pupa_term_set_current (pupa_term_t term)
{
pupa_cur_term = term;
}
pupa_term_t
pupa_term_get_current (void)
{
return pupa_cur_term;
}
void
pupa_putchar (int c)
{
if (c == '\n')
pupa_putchar ('\r');
else if (c == '\t' && pupa_cur_term->getxy)
{
int n;
n = 8 - ((pupa_getxy () >> 8) & 7);
while (n--)
pupa_putchar (' ');
return;
}
(pupa_cur_term->putchar) (c);
}
int
pupa_getkey (void)
{
return (pupa_cur_term->getkey) ();
}
int
pupa_checkkey (void)
{
return (pupa_cur_term->checkkey) ();
}
pupa_uint16_t
pupa_getxy (void)
{
return (pupa_cur_term->getxy) ();
}
void
pupa_gotoxy (pupa_uint8_t x, pupa_uint8_t y)
{
(pupa_cur_term->gotoxy) (x, y);
}
void
pupa_cls (void)
{
if (pupa_cur_term->flags & PUPA_TERM_DUMB)
pupa_putchar ('\n');
else
(pupa_cur_term->cls) ();
}
void
pupa_setcolorstate (pupa_term_color_state state)
{
if (pupa_cur_term->setcolorstate)
(pupa_cur_term->setcolorstate) (state);
}
void
pupa_setcolor (pupa_uint8_t normal_color, pupa_uint8_t highlight_color)
{
if (pupa_cur_term->setcolor)
(pupa_cur_term->setcolor) (normal_color, highlight_color);
}
int
pupa_setcursor (int on)
{
static int prev = 1;
int ret = prev;
if (pupa_cur_term->setcursor)
{
(pupa_cur_term->setcursor) (on);
prev = on;
}
return ret;
}