Ollie Lho <[EMAIL PROTECTED]> writes:
> Eric,
> Should we change the interface of stream.init for init(void) to
> something like init(unsigned long kernel_start) ?? For DoC and ROM we
> have this hard coded. For IDE we just assume the kernel starts ate 0
> which is used for partition table.
Actually all we assume is that the ELF image starts within the first 4KB
of the disk. So the simple solution is to force a partition immedately
after the partition table. (usually about 63 sectors are wasted).
Additionally there is my little install ELF program that just sticks the
ELF header at 1KB into the disk and then the rest of the image can be anywhere
within the first 4GB of disk. To get bigger would require moving to 64bit elf.
So while this looks a little silly. I don't think this is to bad.
Anyway if you want to play with it here is my install_elf program. I should
probably put in the util directory but I haven't gotten around to it.
Eric
#include <stdio.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <elf.h>
#include <stdlib.h>
#include <elf.h>
#if 1
#include <linux/fs.h>
#include <linux/fd.h>
#include <linux/hdreg.h>
#endif
#define ELF_INSTALL_OFFSET 1024UL
#ifndef FIBMAP
#define FIBMAP BMAP_IOCTL
#endif
/* FIGETBSZ */
#ifndef HDIO_GETGEO
#define HDIO_GETGEO HDIO_REQ
#endif
#define SECTOR_SIZE 512
/* FIXME creating a device in the current directory is brittle... */
#define DEVPATH "./dev"
struct raw_file_info {
int fd;
int block_size;
off_t start_block;
off_t bytes;
dev_t rdev;
};
static int open_temp_dev(dev_t dev, int flags, mode_t mode)
{
int fd;
if (mknod(DEVPATH, mode, dev) < 0) {
fprintf(stderr, "Cannot create device %02lx:%02lx as %s\n",
(unsigned long)MAJOR(dev),
(unsigned long)MINOR(dev),
DEVPATH);
return -1;
}
fd = open(DEVPATH, flags);
unlink(DEVPATH);
if (fd < 0) {
fprintf(stderr, "Cannot open %s: %s",
DEVPATH, strerror(errno));
return -1;
}
printf("opened device: %02lx:%02lx\n",
(unsigned long)MAJOR(dev),
(unsigned long)MINOR(dev));
return fd;
}
static int partition_start(dev_t dev, struct raw_file_info *info)
{
struct hd_geometry hdprm;
struct stat st;
int fd;
/* play with MD_MAJOR... */
info->start_block = 0;
fd = open_temp_dev(dev, O_RDONLY, S_IFBLK | 0400);
if (fd < 0) {
return -1;
}
if (ioctl(fd, HDIO_GETGEO, &hdprm) < 0) {
fprintf(stderr, "Cannot get geometry for device %02lx:%02lx: %s\n",
(unsigned long)MAJOR(dev),
(unsigned long)MINOR(dev),
strerror(errno));
close(fd);
return -1;
}
if (fstat(fd, &st) < 0) {
fprintf(stderr, "Cannot stat device %02lx:%02lx: %s\n",
(unsigned long)MAJOR(dev),
(unsigned long)MINOR(dev),
strerror(errno));
close(fd);
return -1;
}
close(fd);
info->rdev = st.st_rdev;
info->start_block = hdprm.start;
return 0;
}
static int get_raw_file_info(char *filename, struct raw_file_info *info)
{
struct stat st;
dev_t parent_dev;
info->fd = open(filename, O_RDONLY);
if (info->fd < 0) {
fprintf(stderr, "Cannot open %s: %s\n",
filename, strerror(errno));
}
if (fstat(info->fd, &st) < 0) {
fprintf(stderr, "Cannot stat %s: %s\n",
filename, strerror(errno));
}
info->bytes = st.st_size;
parent_dev = S_ISREG(st.st_mode)? st.st_dev : st.st_rdev;
if (partition_start(parent_dev, info) < 0){
return -1;
}
if(ioctl(info->fd, FIGETBSZ, &info->block_size) < 0) {
fprintf(stderr, "FIGETBSZ %s: %s\n",
filename, strerror(errno));
return -1;
}
return 0;
}
off_t get_disk_offset(off_t offset, struct raw_file_info *info)
{
int block;
off_t disk_offset;
block = offset/info->block_size;
if (ioctl(info->fd, FIBMAP, &block) < 0) {
fprintf(stderr, "FIBMAP failed: %s\n",
strerror(errno));
return -1;
}
disk_offset = block;
disk_offset *= info->block_size;
disk_offset += offset%info->block_size;
disk_offset += info->start_block*512;
return disk_offset - ELF_INSTALL_OFFSET;
}
off_t disk_offset_floor(off_t offset, struct raw_file_info *info)
{
offset += ELF_INSTALL_OFFSET;
offset -= info->start_block*512;
offset /= info->block_size;
offset *= info->block_size;
offset += info->start_block*512;
offset -= ELF_INSTALL_OFFSET;
return offset;
}
off_t disk_offset_ceil(off_t offset, struct raw_file_info *info)
{
return disk_offset_floor(offset + info->block_size -1, info);
}
int elf_check_arch(Elf32_Ehdr *ehdr)
{
return (
((ehdr->e_machine == EM_386) || (ehdr->e_machine == EM_486)) &&
(ehdr->e_ident[EI_CLASS] == ELFCLASS32) &&
(ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
);
}
static void print_phdr_callback(void *arg, Elf32_Phdr *phdr)
{
static int part = 0;
unsigned long disk_start_addr, disk_end_addr;
disk_start_addr = phdr->p_offset;
disk_start_addr += ELF_INSTALL_OFFSET;
disk_end_addr = disk_start_addr + phdr->p_filesz;
part++;
#if 1
printf("dd if=/dev/hda of=part%d bs=1 skip=%ld count=%d\n",
part, disk_start_addr, phdr->p_filesz);
#endif
#if 0
printf("(%08lx-%08lx) size: %10ld filesz: %10ld\n",
disk_start_addr, disk_end_addr,
disk_end_addr - disk_start_addr,
(unsigned long)phdr->p_filesz);
#endif
#if 0
printf("p_type= %d\n", phdr->p_type);
printf("p_offset= 0x%08lx\n", (unsigned long)phdr->p_offset);
printf("p_vaddr= 0x%08lx\n", (unsigned long)phdr->p_vaddr);
printf("p_paddr= 0x%08lx\n", (unsigned long)phdr->p_paddr);
printf("p_filesz= 0x%08lx\n", (unsigned long)phdr->p_filesz);
printf("p_memsz= 0x%08lx\n", (unsigned long)phdr->p_memsz);
printf("p_flags= 0x%08lx\n", (unsigned long)phdr->p_flags);
printf("p_align= 0x%08lx\n", (unsigned long)phdr->p_align);
printf("\n");
#endif
}
static void count_phdr_callback(void *arg, Elf32_Phdr *phdr)
{
int *countp = arg;
*countp += 1;
}
static void write_phdr_callback(void *arg, Elf32_Phdr *phdr)
{
int out_fd;
out_fd = (int)arg;
if (write(out_fd, phdr, sizeof(*phdr)) != sizeof(*phdr)) {
fprintf(stderr, "Cannot write new elf phdr: %s\n",
strerror(errno));
exit(-2);
}
}
static void newphdr(
off_t disk_start, off_t disk_end,
off_t offset_start, off_t span,
Elf32_Phdr *old_phdr, Elf32_Phdr *new_phdr)
{
off_t offset_end = offset_start + span;
new_phdr->p_type = old_phdr->p_type;
new_phdr->p_offset = disk_start;
new_phdr->p_vaddr = old_phdr->p_vaddr + offset_start;
new_phdr->p_paddr = old_phdr->p_paddr + offset_start;
new_phdr->p_filesz = span;
new_phdr->p_memsz = new_phdr->p_filesz;
if (offset_end == old_phdr->p_filesz) {
new_phdr->p_memsz += (old_phdr->p_memsz - old_phdr->p_filesz);
}
new_phdr->p_flags = old_phdr->p_flags;
new_phdr->p_align = old_phdr->p_align;
}
typedef void (*newphdr_callback_t)(void *arg, Elf32_Phdr *new);
void traverse_phdr_pieces(
newphdr_callback_t callback, void *arg,
Elf32_Phdr *phdr, struct raw_file_info *info)
{
Elf32_Phdr new_phdr;
off_t offset;
off_t offset_start;
off_t disk_start;
off_t last_disk;
off_t disk, disk_end;
off_t span;
offset_start = 0;
disk_start = get_disk_offset(phdr->p_offset, info);
last_disk = disk_start - info->block_size;
for(offset = 0; offset < phdr->p_filesz; offset += info->block_size) {
disk = get_disk_offset(phdr->p_offset + offset, info);
if (disk != last_disk + info->block_size) {
off_t adjust;
/* FIXME why do I need to add an extra block here? */
disk_end = disk_offset_ceil(last_disk + info->block_size,
info);
adjust = disk_end - last_disk;
span = disk_end - disk_start;
newphdr(disk_start, disk_end, offset_start, span,
phdr, &new_phdr);
callback(arg, &new_phdr);
offset_start += span;
disk_start = disk_offset_floor(disk, info);
}
last_disk = disk;
}
disk_end = disk_start + (phdr->p_filesz - offset_start);
span = disk_end - disk_start;
newphdr(disk_start, disk_end, offset_start, span,
phdr, &new_phdr);
callback(arg, &new_phdr);
}
void traverse_phdrs(
newphdr_callback_t callback, void *arg,
int phnum, Elf32_Phdr *phdr, struct raw_file_info *info)
{
int i;
for(i = 0; i < phnum; i++) {
if (phdr[i].p_type != PT_LOAD) {
fprintf(stderr, "Bad program header table type...\n");
exit(1);
}
if (phdr[i].p_filesz > phdr[i].p_memsz) {
fprintf(stderr, "Bad program header filesz...\n");
exit(1);
}
traverse_phdr_pieces(callback, arg, &phdr[i], info);
}
}
int main(int argc, char **argv)
{
char *filename;
struct raw_file_info info;
Elf32_Ehdr ehdr;
Elf32_Phdr *phdr;
unsigned long phdr_len;
int out_fd;
int new_phdrs = 0;
int phnum;
filename = argv[1];
if (!filename) {
fprintf(stderr, "No filename given...\n");
exit(1);
}
if (get_raw_file_info(filename, &info) < 0) {
return -1;
}
#if 0
out_fd = open_temp_dev(info.rdev, O_RDWR, S_IFBLK | 0600);
#else
out_fd = open("/dev/hda", O_RDWR);
#endif
if (out_fd < 0) {
fprintf(stderr, "Cannot open primary device: %s\n",
strerror(errno));
return -1;
}
if (lseek(out_fd, ELF_INSTALL_OFFSET, SEEK_SET) < 0) {
fprintf(stderr, "lseek to %ld failed: %s\n",
ELF_INSTALL_OFFSET, strerror(errno));
}
/* Get the beginning of the file... */
if (lseek(info.fd, 0, SEEK_SET) < 0) {
fprintf(stderr, "lseek to 0 failed: %s\n",
strerror(errno));
return -1;
}
if (read(info.fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) {
fprintf(stderr, "read of ehdr failed: %s\n",
strerror(errno));
return -1;
}
/* Sanity check the elf header */
if ((memcmp(ehdr.e_ident, ELFMAG, 4) != 0) ||
(ehdr.e_type != ET_EXEC) ||
(!elf_check_arch(&ehdr)) ||
(ehdr.e_ehsize != sizeof(ehdr)) ||
(ehdr.e_ident[EI_VERSION] != EV_CURRENT) ||
(ehdr.e_version != EV_CURRENT) ||
(ehdr.e_phentsize != sizeof(Elf32_Phdr))) {
fprintf(stderr, "Bad ELF header: %s\n",
strerror(errno));
return -1;
}
phdr_len = sizeof(*phdr) * ehdr.e_phnum;
printf("sizeof(phdr) = %d, phdr_len = %ld\n",
sizeof(*phdr), phdr_len);
phdr = malloc(phdr_len);
if (!phdr) {
fprintf(stderr, "Cannot allocate Program Header buffer: %s\n",
strerror(errno));
return -1;
}
/* Get the beginning of program header... */
if (lseek(info.fd, ehdr.e_phoff, SEEK_SET) < 0) {
fprintf(stderr, "lseek to phdr(%08lx) failed: %s\n",
(unsigned long)ehdr.e_phoff, strerror(errno));
return -1;
}
if (read(info.fd, phdr, phdr_len) != phdr_len) {
fprintf(stderr, "read of ehder failed: %s\n",
strerror(errno));
return -1;
}
phnum = ehdr.e_phnum;
#if 0
traverse_phdrs(print_phdr_callback, 0, phnum, phdr, &info);
#endif
traverse_phdrs(count_phdr_callback, &new_phdrs, phnum, phdr, &info);
printf("%d new phdrs\n", new_phdrs);
#if 1
/* Now patch up the ELF header and write a new one. */
ehdr.e_phoff = sizeof(ehdr);
ehdr.e_phnum = new_phdrs;
ehdr.e_shoff = 0;
ehdr.e_shentsize = 0;
ehdr.e_shnum = 0;
ehdr.e_shstrndx = 0;
if (write(out_fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) {
fprintf(stderr, "Cannot write new elf header: %s\n",
strerror(errno));
return -1;
}
traverse_phdrs(write_phdr_callback, (void *)out_fd, phnum, phdr, &info);
fsync(out_fd);
#endif
return 0;
}