diff --git a/include/elf.h b/include/elf.h new file mode 100644 index 0000000000..3503388078 --- /dev/null +++ b/include/elf.h @@ -0,0 +1,401 @@ +#ifndef ELF_H +#define ELF_H + +/* Standard ELF types. */ + +#include +#include +#include + +/* Type for a 16-bit quantity. */ +typedef uint16_t Elf32_Half; +typedef uint16_t Elf64_Half; + +/* Types for signed and unsigned 32-bit quantities. */ +typedef uint32_t Elf32_Word; +typedef int32_t Elf32_Sword; +typedef uint32_t Elf64_Word; +typedef int32_t Elf64_Sword; + +/* Types for signed and unsigned 64-bit quantities. */ +typedef uint64_t Elf32_Xword; +typedef int64_t Elf32_Sxword; +typedef uint64_t Elf64_Xword; +typedef int64_t Elf64_Sxword; + +/* Type of addresses. */ +typedef uint32_t Elf32_Addr; +typedef uint64_t Elf64_Addr; + +/* Type of file offsets. */ +typedef uint32_t Elf32_Off; +typedef uint64_t Elf64_Off; + +/* Type for section indices, which are 16-bit quantities. */ +typedef uint16_t Elf32_Section; +typedef uint16_t Elf64_Section; + +/* Type of symbol indices. */ +typedef uint32_t Elf32_Symndx; +typedef uint64_t Elf64_Symndx; + + +/* The ELF file header. This appears at the start of every ELF file. */ + +#define EI_NIDENT (16) + +typedef struct +{ + unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ + Elf32_Half e_type; /* Object file type */ + Elf32_Half e_machine; /* Architecture */ + Elf32_Word e_version; /* Object file version */ + Elf32_Addr e_entry; /* Entry point virtual address */ + Elf32_Off e_phoff; /* Program header table file offset */ + Elf32_Off e_shoff; /* Section header table file offset */ + Elf32_Word e_flags; /* Processor-specific flags */ + Elf32_Half e_ehsize; /* ELF header size in bytes */ + Elf32_Half e_phentsize; /* Program header table entry size */ + Elf32_Half e_phnum; /* Program header table entry count */ + Elf32_Half e_shentsize; /* Section header table entry size */ + Elf32_Half e_shnum; /* Section header table entry count */ + Elf32_Half e_shstrndx; /* Section header string table index */ +} Elf32_Ehdr; + +typedef struct +{ + unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ + Elf64_Half e_type; /* Object file type */ + Elf64_Half e_machine; /* Architecture */ + Elf64_Word e_version; /* Object file version */ + Elf64_Addr e_entry; /* Entry point virtual address */ + Elf64_Off e_phoff; /* Program header table file offset */ + Elf64_Off e_shoff; /* Section header table file offset */ + Elf64_Word e_flags; /* Processor-specific flags */ + Elf64_Half e_ehsize; /* ELF header size in bytes */ + Elf64_Half e_phentsize; /* Program header table entry size */ + Elf64_Half e_phnum; /* Program header table entry count */ + Elf64_Half e_shentsize; /* Section header table entry size */ + Elf64_Half e_shnum; /* Section header table entry count */ + Elf64_Half e_shstrndx; /* Section header string table index */ +} Elf64_Ehdr; + +/* Fields in the e_ident array. The EI_* macros are indices into the + array. The macros under each EI_* macro are the values the byte + may have. */ + +#define EI_MAG0 0 /* File identification byte 0 index */ +#define ELFMAG0 0x7f /* Magic number byte 0 */ + +#define EI_MAG1 1 /* File identification byte 1 index */ +#define ELFMAG1 'E' /* Magic number byte 1 */ + +#define EI_MAG2 2 /* File identification byte 2 index */ +#define ELFMAG2 'L' /* Magic number byte 2 */ + +#define EI_MAG3 3 /* File identification byte 3 index */ +#define ELFMAG3 'F' /* Magic number byte 3 */ + +/* Conglomeration of the identification bytes, for easy testing as a word. */ +#define ELFMAG "\177ELF" +#define SELFMAG 4 + +#define EI_CLASS 4 /* File class byte index */ +#define ELFCLASSNONE 0 /* Invalid class */ +#define ELFCLASS32 1 /* 32-bit objects */ +#define ELFCLASS64 2 /* 64-bit objects */ +#define ELFCLASSNUM 3 + +#define EI_DATA 5 /* Data encoding byte index */ +#define ELFDATANONE 0 /* Invalid data encoding */ +#define ELFDATA2LSB 1 /* 2's complement, little endian */ +#define ELFDATA2MSB 2 /* 2's complement, big endian */ +#define ELFDATANUM 3 + +#define EI_VERSION 6 /* File version byte index */ + /* Value must be EV_CURRENT */ + +#define EI_OSABI 7 /* OS ABI identification */ +#define ELFOSABI_SYSV 0 /* UNIX System V ABI */ +#define ELFOSABI_HPUX 1 /* HP-UX */ +#define ELFOSABI_ARM 97 /* ARM */ +#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ + +#define EI_ABIVERSION 8 /* ABI version */ + +#define EI_PAD 9 /* Byte index of padding bytes */ + +/* Legal values for e_type (object file type). */ + +#define ET_NONE 0 /* No file type */ +#define ET_REL 1 /* Relocatable file */ +#define ET_EXEC 2 /* Executable file */ +#define ET_DYN 3 /* Shared object file */ +#define ET_CORE 4 /* Core file */ +#define ET_NUM 5 /* Number of defined types */ +#define ET_LOPROC 0xff00 /* Processor-specific */ +#define ET_HIPROC 0xffff /* Processor-specific */ + +/* Legal values for e_machine (architecture). */ + +#define EM_NONE 0 /* No machine */ +#define EM_M32 1 /* AT&T WE 32100 */ +#define EM_SPARC 2 /* SUN SPARC */ +#define EM_386 3 /* Intel 80386 */ +#define EM_68K 4 /* Motorola m68k family */ +#define EM_88K 5 /* Motorola m88k family */ +#define EM_486 6 /* Intel 80486 */ +#define EM_860 7 /* Intel 80860 */ +#define EM_MIPS 8 /* MIPS R3000 big-endian */ +#define EM_S370 9 /* Amdahl */ +#define EM_MIPS_RS4_BE 10 /* MIPS R4000 big-endian */ +#define EM_RS6000 11 /* RS6000 */ + +#define EM_PARISC 15 /* HPPA */ +#define EM_nCUBE 16 /* nCUBE */ +#define EM_VPP500 17 /* Fujitsu VPP500 */ +#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ +#define EM_960 19 /* Intel 80960 */ +#define EM_PPC 20 /* PowerPC */ + +#define EM_V800 36 /* NEC V800 series */ +#define EM_FR20 37 /* Fujitsu FR20 */ +#define EM_RH32 38 /* TRW RH32 */ +#define EM_MMA 39 /* Fujitsu MMA */ +#define EM_ARM 40 /* ARM */ +#define EM_FAKE_ALPHA 41 /* Digital Alpha */ +#define EM_SH 42 /* Hitachi SH */ +#define EM_SPARCV9 43 /* SPARC v9 64-bit */ +#define EM_TRICORE 44 /* Siemens Tricore */ +#define EM_ARC 45 /* Argonaut RISC Core */ +#define EM_H8_300 46 /* Hitachi H8/300 */ +#define EM_H8_300H 47 /* Hitachi H8/300H */ +#define EM_H8S 48 /* Hitachi H8S */ +#define EM_H8_500 49 /* Hitachi H8/500 */ +#define EM_IA_64 50 /* Intel Merced */ +#define EM_MIPS_X 51 /* Stanford MIPS-X */ +#define EM_COLDFIRE 52 /* Motorola Coldfire */ +#define EM_68HC12 53 /* Motorola M68HC12 */ +#define EM_NUM 54 + +/* If it is necessary to assign new unofficial EM_* values, please + pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the + chances of collision with official or non-GNU unofficial values. */ + +#define EM_ALPHA 0x9026 + +/* Legal values for e_version (version). */ + +#define EV_NONE 0 /* Invalid ELF version */ +#define EV_CURRENT 1 /* Current version */ +#define EV_NUM 2 + + +/* Program segment header. */ + +typedef struct +{ + Elf32_Word p_type; /* Segment type */ + Elf32_Off p_offset; /* Segment file offset */ + Elf32_Addr p_vaddr; /* Segment virtual address */ + Elf32_Addr p_paddr; /* Segment physical address */ + Elf32_Word p_filesz; /* Segment size in file */ + Elf32_Word p_memsz; /* Segment size in memory */ + Elf32_Word p_flags; /* Segment flags */ + Elf32_Word p_align; /* Segment alignment */ +} Elf32_Phdr; + +typedef struct +{ + Elf64_Word p_type; /* Segment type */ + Elf64_Word p_flags; /* Segment flags */ + Elf64_Off p_offset; /* Segment file offset */ + Elf64_Addr p_vaddr; /* Segment virtual address */ + Elf64_Addr p_paddr; /* Segment physical address */ + Elf64_Xword p_filesz; /* Segment size in file */ + Elf64_Xword p_memsz; /* Segment size in memory */ + Elf64_Xword p_align; /* Segment alignment */ +} Elf64_Phdr; + +/* Legal values for p_type (segment type). */ + +#define PT_NULL 0 /* Program header table entry unused */ +#define PT_LOAD 1 /* Loadable program segment */ +#define PT_DYNAMIC 2 /* Dynamic linking information */ +#define PT_INTERP 3 /* Program interpreter */ +#define PT_NOTE 4 /* Auxiliary information */ +#define PT_SHLIB 5 /* Reserved */ +#define PT_PHDR 6 /* Entry for header table itself */ +#define PT_NUM 7 /* Number of defined types. */ +#define PT_LOOS 0x60000000 /* Start of OS-specific */ +#define PT_HIOS 0x6fffffff /* End of OS-specific */ +#define PT_LOPROC 0x70000000 /* Start of processor-specific */ +#define PT_HIPROC 0x7fffffff /* End of processor-specific */ + +/* Legal values for p_flags (segment flags). */ + +#define PF_X (1 << 0) /* Segment is executable */ +#define PF_W (1 << 1) /* Segment is writable */ +#define PF_R (1 << 2) /* Segment is readable */ +#define PF_MASKPROC 0xf0000000 /* Processor-specific */ + + +/* Note section contents. Each entry in the note section begins with + a header of a fixed form. */ + +typedef struct +{ + Elf32_Word n_namesz; /* Length of the note's name. */ + Elf32_Word n_descsz; /* Length of the note's descriptor. */ + Elf32_Word n_type; /* Type of the note. */ +} Elf32_Nhdr; + +typedef struct +{ + Elf64_Word n_namesz; /* Length of the note's name. */ + Elf64_Word n_descsz; /* Length of the note's descriptor. */ + Elf64_Word n_type; /* Type of the note. */ +} Elf64_Nhdr; + +/* Known names of notes. */ + +/* Solaris entries in the note section have this name. */ +#define ELF_NOTE_SOLARIS "SUNW Solaris" + +/* Note entries for GNU systems have this name. */ +#define ELF_NOTE_GNU "GNU" + + +/* Defined types of notes for Solaris. */ + +/* Value of descriptor (one word) is desired pagesize for the binary. */ +#define ELF_NOTE_PAGESIZE_HINT 1 + + +/* Defined note types for GNU systems. */ + +/* ABI information. The descriptor consists of words: + word 0: OS descriptor + word 1: major version of the ABI + word 2: minor version of the ABI + word 3: subminor version of the ABI +*/ +#define ELF_NOTE_ABI 1 + +/* Known OSes. These value can appear in word 0 of an ELF_NOTE_ABI + note section entry. */ +#define ELF_NOTE_OS_LINUX 0 +#define ELF_NOTE_OS_GNU 1 +#define ELF_NOTE_OS_SOLARIS2 2 + + +/* Motorola 68k specific definitions. */ + +/* Intel 80386 specific definitions. */ + +/* SUN SPARC specific definitions. */ + +/* Values for Elf64_Ehdr.e_flags. */ + +#define EF_SPARCV9_MM 3 +#define EF_SPARCV9_TSO 0 +#define EF_SPARCV9_PSO 1 +#define EF_SPARCV9_RMO 2 +#define EF_SPARC_EXT_MASK 0xFFFF00 +#define EF_SPARC_SUN_US1 0x000200 +#define EF_SPARC_HAL_R1 0x000400 + +/* MIPS R3000 specific definitions. */ + +/* Legal values for e_flags field of Elf32_Ehdr. */ + +#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used */ +#define EF_MIPS_PIC 2 /* Contains PIC code */ +#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence */ +#define EF_MIPS_XGOT 8 +#define EF_MIPS_64BIT_WHIRL 16 +#define EF_MIPS_ABI2 32 +#define EF_MIPS_ABI_ON32 64 +#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level */ + +/* Legal values for MIPS architecture level. */ + +#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ +#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ +#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ +#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ +#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ + +/* Legal values for p_type field of Elf32_Phdr. */ + +#define PT_MIPS_REGINFO 0x70000000 /* Register usage information */ +#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */ +#define PT_MIPS_OPTIONS 0x70000002 + +/* Special program header types. */ + +#define PF_MIPS_LOCAL 0x10000000 + + +/* HPPA specific definitions. */ + +/* Legal values for e_flags field of Elf32_Ehdr. */ + +#define EF_PARISC_TRAPNL 1 /* Trap nil pointer dereference. */ +#define EF_PARISC_EXT 2 /* Program uses arch. extensions. */ +#define EF_PARISC_ARCH 0xffff0000 /* Architecture version. */ +/* Defined values are: + 0x020b PA-RISC 1.0 big-endian + 0x0210 PA-RISC 1.1 big-endian + 0x028b PA-RISC 1.0 little-endian + 0x0290 PA-RISC 1.1 little-endian +*/ + + +/* Alpha specific definitions. */ + +/* Legal values for e_flags field of Elf64_Ehdr. */ + +#define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */ +#define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */ + + +/* PowerPC specific declarations */ + +/* ARM specific declarations */ + +/* Processor specific flags for the ELF header e_flags field. */ +#define EF_ARM_RELEXEC 0x01 +#define EF_ARM_HASENTRY 0x02 +#define EF_ARM_INTERWORK 0x04 +#define EF_ARM_APCS_26 0x08 +#define EF_ARM_APCS_FLOAT 0x10 +#define EF_ARM_PIC 0x20 +#define EF_ALIGN8 0x40 /* 8-bit structure alignment is in use */ +#define EF_NEW_ABI 0x80 +#define EF_OLD_ABI 0x100 + +/* ARM-specific program header flags */ +#define PF_ARM_SB 0x10000000 /* Segment contains the location + addressed by the static base */ + +#if ELF_CLASS == ELFCLASS32 +typedef Elf32_Ehdr Elf_ehdr; +typedef Elf32_Phdr Elf_phdr; +#endif + +#if ELF_CLASS == ELFCLASS64 +typedef Elf64_Ehdr Elf_ehdr; +typedef Elf64_Phdr Elf_phdr; +#endif + +extern int elf_check_arch(Elf_ehdr *ehdr); +extern void jmp_to_elf_entry(void *entry, unsigned long buffer); +struct lb_memory; +extern int elfboot(struct lb_memory *mem); + +#define FIRMWARE_TYPE "LinuxBIOS" +#define BOOTLOADER "elfboot" +#define BOOTLOADER_VERSION "1.3" + +#endif /* elf.h */ diff --git a/include/elf_boot.h b/include/elf_boot.h new file mode 100644 index 0000000000..ee6750d293 --- /dev/null +++ b/include/elf_boot.h @@ -0,0 +1,89 @@ +#ifndef ELF_BOOT_H +#define ELF_BOOT_H + +#include + +/* This defines the structure of a table of parameters useful for ELF + * bootable images. These parameters are all passed and generated + * by the bootloader to the booted image. For simplicity and + * consistency the Elf Note format is reused. + * + * All of the information must be Position Independent Data. + * That is it must be safe to relocate the whole ELF boot parameter + * block without changing the meaning or correctnes of the data. + * Additionally it must be safe to permute the order of the ELF notes + * to any possible permutation without changing the meaning or correctness + * of the data. + * + */ + +#define ELF_HEAD_SIZE (8*1024) +#define ELF_BOOT_MAGIC 0x0E1FB007 + +typedef uint16_t Elf_Half; +typedef uint32_t Elf_Word; +typedef uint64_t Elf_Xword; + +typedef struct +{ + Elf_Word b_signature; /* "0x0E1FB007" */ + Elf_Word b_size; + Elf_Half b_checksum; + Elf_Half b_records; +} Elf_Bhdr; + +typedef struct +{ + Elf_Word n_namesz; /* Length of the note's name. */ + Elf_Word n_descsz; /* Length of the note's descriptor. */ + Elf_Word n_type; /* Type of the note. */ +} Elf_Nhdr; + + +/* For standard notes n_namesz must be zero */ +/* All of the following standard note types provide a single null + * terminated string in the descriptor. + */ +#define EBN_FIRMWARE_TYPE 0x00000001 +/* On platforms that support multiple classes of firmware this field + * specifies the class of firmware you are loaded under. + */ +#define EBN_BOOTLOADER_NAME 0x00000002 +/* This specifies just the name of the bootloader for easy comparison */ +#define EBN_BOOTLOADER_VERSION 0x00000003 +/* This specifies the version of the bootlader */ +#define EBN_COMMAND_LINE 0x00000004 +/* This specifies a command line that can be set by user interaction, + * and is provided as a free form string to the loaded image. + */ + + +/* Standardized Elf image notes for booting... The name for all of these is ELFBoot */ + +#define ELF_NOTE_BOOT "ELFBoot" + +#define EIN_PROGRAM_NAME 0x00000001 +/* The program in this ELF file */ +#define EIN_PROGRAM_VERSION 0x00000002 +/* The version of the program in this ELF file */ +#define EIN_PROGRAM_CHECKSUM 0x00000003 +/* ip style checksum of the memory image. */ + + +/* Linux image notes for booting... The name for all of these is Linux */ + +#define LINUX_NOTE_BOOT "Linux" + +#define LIN_COMMAND_LINE 0x00000001 +/* The command line to pass to the loaded kernel. */ +#define LIN_ROOT_DEV 0x00000002 +/* The root dev to pass to the loaded kernel. */ +#define LIN_RAMDISK_FLAGS 0x00000003 +/* Various old ramdisk flags */ +#define LIN_INITRD_START 0x00000004 +/* Start of the ramdisk in bytes */ +#define LIN_INITRD_SIZE 0x00000005 +/* Size of the ramdisk in bytes */ + + +#endif /* ELF_BOOT_H */ diff --git a/include/linuxbios_tables.h b/include/linuxbios_tables.h new file mode 100644 index 0000000000..527c44d5fc --- /dev/null +++ b/include/linuxbios_tables.h @@ -0,0 +1,216 @@ +#ifndef LINUXBIOS_TABLES_H +#define LINUXBIOS_TABLES_H + +#include + +/* The linuxbios table information is for conveying information + * from the firmware to the loaded OS image. Primarily this + * is expected to be information that cannot be discovered by + * other means, such as quering the hardware directly. + * + * All of the information should be Position Independent Data. + * That is it should be safe to relocated any of the information + * without it's meaning/correctnes changing. For table that + * can reasonably be used on multiple architectures the data + * size should be fixed. This should ease the transition between + * 32 bit and 64 bit architectures etc. + * + * The completeness test for the information in this table is: + * - Can all of the hardware be detected? + * - Are the per motherboard constants available? + * - Is there enough to allow a kernel to run that was written before + * a particular motherboard is constructed? (Assuming the kernel + * has drivers for all of the hardware but it does not have + * assumptions on how the hardware is connected together). + * + * With this test it should be straight forward to determine if a + * table entry is required or not. This should remove much of the + * long term compatibility burden as table entries which are + * irrelevant or have been replaced by better alternatives may be + * dropped. Of course it is polite and expidite to include extra + * table entries and be backwards compatible, but it is not required. + */ + +/* Since LinuxBIOS is usually compiled 32bit, gcc will align 64bit + * types to 32bit boundaries. If the LinuxBIOS table is dumped on a + * 64bit system, a uint64_t would be aligned to 64bit boundaries, + * breaking the table format. + * + * lb_uint64 will keep 64bit LinuxBIOS table values aligned to 32bit + * to ensure compatibility. They can be accessed with the two functions + * below: unpack_lb64() and pack_lb64() + * + * See also: util/lbtdump/lbtdump.c + */ + +struct lb_uint64 { + uint32_t lo; + uint32_t hi; +}; + +static inline uint64_t unpack_lb64(struct lb_uint64 value) +{ + uint64_t result; + result = value.hi; + result = (result << 32) + value.lo; + return result; +} + +static inline struct lb_uint64 pack_lb64(uint64_t value) +{ + struct lb_uint64 result; + result.lo = (value >> 0) & 0xffffffff; + result.hi = (value >> 32) & 0xffffffff; + return result; +} + + + +struct lb_header +{ + uint8_t signature[4]; /* LBIO */ + uint32_t header_bytes; + uint32_t header_checksum; + uint32_t table_bytes; + uint32_t table_checksum; + uint32_t table_entries; +}; + +/* Every entry in the boot enviroment list will correspond to a boot + * info record. Encoding both type and size. The type is obviously + * so you can tell what it is. The size allows you to skip that + * boot enviroment record if you don't know what it easy. This allows + * forward compatibility with records not yet defined. + */ +struct lb_record { + uint32_t tag; /* tag ID */ + uint32_t size; /* size of record (in bytes) */ +}; + +#define LB_TAG_UNUSED 0x0000 + +#define LB_TAG_MEMORY 0x0001 + +struct lb_memory_range { + struct lb_uint64 start; + struct lb_uint64 size; + uint32_t type; +#define LB_MEM_RAM 1 /* Memory anyone can use */ +#define LB_MEM_RESERVED 2 /* Don't use this memory region */ +#define LB_MEM_TABLE 16 /* Ram configuration tables are kept in */ +}; + +struct lb_memory { + uint32_t tag; + uint32_t size; + struct lb_memory_range map[0]; +}; + +#define LB_TAG_HWRPB 0x0002 +struct lb_hwrpb { + uint32_t tag; + uint32_t size; + uint64_t hwrpb; +}; + +#define LB_TAG_MAINBOARD 0x0003 +struct lb_mainboard { + uint32_t tag; + uint32_t size; + uint8_t vendor_idx; + uint8_t part_number_idx; + uint8_t strings[0]; +}; + +#define LB_TAG_VERSION 0x0004 +#define LB_TAG_EXTRA_VERSION 0x0005 +#define LB_TAG_BUILD 0x0006 +#define LB_TAG_COMPILE_TIME 0x0007 +#define LB_TAG_COMPILE_BY 0x0008 +#define LB_TAG_COMPILE_HOST 0x0009 +#define LB_TAG_COMPILE_DOMAIN 0x000a +#define LB_TAG_COMPILER 0x000b +#define LB_TAG_LINKER 0x000c +#define LB_TAG_ASSEMBLER 0x000d +struct lb_string { + uint32_t tag; + uint32_t size; + uint8_t string[0]; +}; + +/* The following structures are for the cmos definitions table */ +#define LB_TAG_CMOS_OPTION_TABLE 200 +/* cmos header record */ +struct cmos_option_table { + uint32_t tag; /* CMOS definitions table type */ + uint32_t size; /* size of the entire table */ + uint32_t header_length; /* length of header */ +}; + +/* cmos entry record + This record is variable length. The name field may be + shorter than CMOS_MAX_NAME_LENGTH. The entry may start + anywhere in the byte, but can not span bytes unless it + starts at the beginning of the byte and the length is + fills complete bytes. +*/ +#define LB_TAG_OPTION 201 +struct cmos_entries { + uint32_t tag; /* entry type */ + uint32_t size; /* length of this record */ + uint32_t bit; /* starting bit from start of image */ + uint32_t length; /* length of field in bits */ + uint32_t config; /* e=enumeration, h=hex, r=reserved */ + uint32_t config_id; /* a number linking to an enumeration record */ +#define CMOS_MAX_NAME_LENGTH 32 + uint8_t name[CMOS_MAX_NAME_LENGTH]; /* name of entry in ascii, + variable length int aligned */ +}; + + +/* cmos enumerations record + This record is variable length. The text field may be + shorter than CMOS_MAX_TEXT_LENGTH. +*/ +#define LB_TAG_OPTION_ENUM 202 +struct cmos_enums { + uint32_t tag; /* enumeration type */ + uint32_t size; /* length of this record */ + uint32_t config_id; /* a number identifying the config id */ + uint32_t value; /* the value associated with the text */ +#define CMOS_MAX_TEXT_LENGTH 32 + uint8_t text[CMOS_MAX_TEXT_LENGTH]; /* enum description in ascii, + variable length int aligned */ +}; + +/* cmos defaults record + This record contains default settings for the cmos ram. +*/ +#define LB_TAG_OPTION_DEFAULTS 203 +struct cmos_defaults { + uint32_t tag; /* default type */ + uint32_t size; /* length of this record */ + uint32_t name_length; /* length of the following name field */ + uint8_t name[CMOS_MAX_NAME_LENGTH]; /* name identifying the default */ +#define CMOS_IMAGE_BUFFER_SIZE 128 + uint8_t default_set[CMOS_IMAGE_BUFFER_SIZE]; /* default settings */ +}; + +#define LB_TAG_OPTION_CHECKSUM 204 +struct cmos_checksum { + uint32_t tag; + uint32_t size; + /* In practice everything is byte aligned, but things are measured + * in bits to be consistent. + */ + uint32_t range_start; /* First bit that is checksummed (byte aligned) */ + uint32_t range_end; /* Last bit that is checksummed (byte aligned) */ + uint32_t location; /* First bit of the checksum (byte aligned) */ + uint32_t type; /* Checksum algorithm that is used */ +#define CHECKSUM_NONE 0 +#define CHECKSUM_PCBIOS 1 +}; + + + +#endif /* LINUXBIOS_TABLES_H */ diff --git a/include/tables.h b/include/tables.h new file mode 100644 index 0000000000..6c309b3c26 --- /dev/null +++ b/include/tables.h @@ -0,0 +1,8 @@ +#ifndef BOOT_TABLES_H +#define BOOT_TABLES_H + +#include + +struct lb_memory *write_tables(void); + +#endif /* BOOT_TABLES_H */ diff --git a/lib/elfboot.c b/lib/elfboot.c new file mode 100644 index 0000000000..c0bcd177da --- /dev/null +++ b/lib/elfboot.c @@ -0,0 +1,668 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Maximum physical address we can use for the linuxBIOS bounce buffer. + */ +#ifndef MAX_ADDR +#define MAX_ADDR -1UL +#endif + +extern unsigned char _ram_seg; +extern unsigned char _eram_seg; + +struct segment { + struct segment *next; + struct segment *prev; + struct segment *phdr_next; + struct segment *phdr_prev; + unsigned long s_addr; + unsigned long s_memsz; + unsigned long s_offset; + unsigned long s_filesz; +}; + +struct verify_callback { + struct verify_callback *next; + int (*callback)(struct verify_callback *vcb, + Elf_ehdr *ehdr, Elf_phdr *phdr, struct segment *head); + unsigned long desc_offset; + unsigned long desc_addr; +}; + +struct ip_checksum_vcb { + struct verify_callback data; + unsigned short ip_checksum; +}; + +int verify_ip_checksum( + struct verify_callback *vcb, + Elf_ehdr *ehdr, Elf_phdr *phdr, struct segment *head) +{ + struct ip_checksum_vcb *cb; + struct segment *ptr; + unsigned long bytes; + unsigned long checksum; + unsigned char buff[2], *n_desc; + cb = (struct ip_checksum_vcb *)vcb; + /* zero the checksum so it's value won't + * get in the way of verifying the checksum. + */ + n_desc = 0; + if (vcb->desc_addr) { + n_desc = (unsigned char *)(vcb->desc_addr); + memcpy(buff, n_desc, 2); + memset(n_desc, 0, 2); + } + bytes = 0; + checksum = compute_ip_checksum(ehdr, sizeof(*ehdr)); + bytes += sizeof(*ehdr); + checksum = add_ip_checksums(bytes, checksum, + compute_ip_checksum(phdr, ehdr->e_phnum*sizeof(*phdr))); + bytes += ehdr->e_phnum*sizeof(*phdr); + for(ptr = head->phdr_next; ptr != head; ptr = ptr->phdr_next) { + checksum = add_ip_checksums(bytes, checksum, + compute_ip_checksum((void *)ptr->s_addr, ptr->s_memsz)); + bytes += ptr->s_memsz; + } + if (n_desc != 0) { + memcpy(n_desc, buff, 2); + } + if (checksum != cb->ip_checksum) { + printk_err("Image checksum: %04x != computed checksum: %04x\n", + cb->ip_checksum, checksum); + } + return checksum == cb->ip_checksum; +} + +/* The problem: + * Static executables all want to share the same addresses + * in memory because only a few addresses are reliably present on + * a machine, and implementing general relocation is hard. + * + * The solution: + * - Allocate a buffer twice the size of the linuxBIOS image. + * - Anything that would overwrite linuxBIOS copy into the lower half of + * the buffer. + * - After loading an ELF image copy linuxBIOS to the upper half of the + * buffer. + * - Then jump to the loaded image. + * + * Benefits: + * - Nearly arbitrary standalone executables can be loaded. + * - LinuxBIOS is preserved, so it can be returned to. + * - The implementation is still relatively simple, + * and much simpler then the general case implemented in kexec. + * + */ + +static unsigned long get_bounce_buffer(struct lb_memory *mem) +{ + unsigned long lb_size; + unsigned long mem_entries; + unsigned long buffer; + int i; + lb_size = (unsigned long)(&_eram_seg - &_ram_seg); + /* Double linuxBIOS size so I have somewhere to place a copy to return to */ + lb_size = lb_size + lb_size; + mem_entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]); + buffer = 0; + for(i = 0; i < mem_entries; i++) { + unsigned long mstart, mend; + unsigned long msize; + unsigned long tbuffer; + if (mem->map[i].type != LB_MEM_RAM) + continue; + if (unpack_lb64(mem->map[i].start) > MAX_ADDR) + continue; + if (unpack_lb64(mem->map[i].size) < lb_size) + continue; + mstart = unpack_lb64(mem->map[i].start); + msize = MAX_ADDR - mstart +1; + if (msize > unpack_lb64(mem->map[i].size)) + msize = unpack_lb64(mem->map[i].size); + mend = mstart + msize; + tbuffer = mend - lb_size; + if (tbuffer < buffer) + continue; + buffer = tbuffer; + } + return buffer; +} + + +static struct verify_callback *process_elf_notes( + unsigned char *header, + unsigned long offset, unsigned long length) +{ + struct verify_callback *cb_chain; + unsigned char *note, *end; + char *program, *version; + + cb_chain = 0; + note = header + offset; + end = note + length; + program = version = 0; + while(note < end) { + Elf_Nhdr *hdr; + unsigned char *n_name, *n_desc, *next; + hdr = (Elf_Nhdr *)note; + n_name = note + sizeof(*hdr); + n_desc = n_name + ((hdr->n_namesz + 3) & ~3); + next = n_desc + ((hdr->n_descsz + 3) & ~3); + if (next > end) { + break; + } + if ((hdr->n_namesz == sizeof(ELF_NOTE_BOOT)) && + (memcmp(n_name, ELF_NOTE_BOOT, sizeof(ELF_NOTE_BOOT)) == 0)) { + switch(hdr->n_type) { + case EIN_PROGRAM_NAME: + if (n_desc[hdr->n_descsz -1] == 0) { + program = n_desc; + } + break; + case EIN_PROGRAM_VERSION: + if (n_desc[hdr->n_descsz -1] == 0) { + version = n_desc; + } + break; + case EIN_PROGRAM_CHECKSUM: + { + struct ip_checksum_vcb *cb; + cb = malloc(sizeof(*cb)); + cb->ip_checksum = *((uint16_t *)n_desc); + cb->data.callback = verify_ip_checksum; + cb->data.next = cb_chain; + cb->data.desc_offset = n_desc - header; + cb_chain = &cb->data; + break; + } + } + } + printk_spew("n_type: %08x n_name(%d): %-*.*s n_desc(%d): %-*.*s\n", + hdr->n_type, + hdr->n_namesz, hdr->n_namesz, hdr->n_namesz, n_name, + hdr->n_descsz,hdr->n_descsz, hdr->n_descsz, n_desc); + note = next; + } + if (program && version) { + printk_info("Loading %s version: %s\n", + program, version); + } + return cb_chain; +} + +static int valid_area(struct lb_memory *mem, unsigned long buffer, + unsigned long start, unsigned long len) +{ + /* Check through all of the memory segments and ensure + * the segment that was passed in is completely contained + * in RAM. + */ + int i; + unsigned long end = start + len; + unsigned long mem_entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]); + + /* See if I conflict with the bounce buffer */ + if (end >= buffer) { + return 0; + } + + /* Walk through the table of valid memory ranges and see if I + * have a match. + */ + for(i = 0; i < mem_entries; i++) { + uint64_t mstart, mend; + uint32_t mtype; + mtype = mem->map[i].type; + mstart = unpack_lb64(mem->map[i].start); + mend = mstart + unpack_lb64(mem->map[i].size); + if ((mtype == LB_MEM_RAM) && (start < mend) && (end > mstart)) { + break; + } + } + if (i == mem_entries) { + printk_err("No matching ram area found for range:\n"); + printk_err(" [0x%016lx, 0x%016lx)\n", start, end); + printk_err("Ram areas\n"); + for(i = 0; i < mem_entries; i++) { + uint64_t mstart, mend; + uint32_t mtype; + mtype = mem->map[i].type; + mstart = unpack_lb64(mem->map[i].start); + mend = mstart + unpack_lb64(mem->map[i].size); + printk_err(" [0x%016lx, 0x%016lx) %s\n", + (unsigned long)mstart, + (unsigned long)mend, + (mtype == LB_MEM_RAM)?"RAM":"Reserved"); + + } + return 0; + } + return 1; +} + +static void relocate_segment(unsigned long buffer, struct segment *seg) +{ + /* Modify all segments that want to load onto linuxBIOS + * to load onto the bounce buffer instead. + */ + unsigned long lb_start = (unsigned long)&_ram_seg; + unsigned long lb_end = (unsigned long)&_eram_seg; + unsigned long start, middle, end; + + printk_spew("lb: [0x%016lx, 0x%016lx)\n", + lb_start, lb_end); + + start = seg->s_addr; + middle = start + seg->s_filesz; + end = start + seg->s_memsz; + /* I don't conflict with linuxBIOS so get out of here */ + if ((end <= lb_start) || (start >= lb_end)) + return; + + printk_spew("segment: [0x%016lx, 0x%016lx, 0x%016lx)\n", + start, middle, end); + + /* Slice off a piece at the beginning + * that doesn't conflict with linuxBIOS. + */ + if (start < lb_start) { + struct segment *new; + unsigned long len = lb_start - start; + new = malloc(sizeof(*new)); + *new = *seg; + new->s_memsz = len; + seg->s_memsz -= len; + seg->s_addr += len; + seg->s_offset += len; + if (seg->s_filesz > len) { + new->s_filesz = len; + seg->s_filesz -= len; + } else { + seg->s_filesz = 0; + } + + /* Order by stream offset */ + new->next = seg; + new->prev = seg->prev; + seg->prev->next = new; + seg->prev = new; + /* Order by original program header order */ + new->phdr_next = seg; + new->phdr_prev = seg->phdr_prev; + seg->phdr_prev->phdr_next = new; + seg->phdr_prev = new; + + /* compute the new value of start */ + start = seg->s_addr; + + printk_spew(" early: [0x%016lx, 0x%016lx, 0x%016lx)\n", + new->s_addr, + new->s_addr + new->s_filesz, + new->s_addr + new->s_memsz); + } + + /* Slice off a piece at the end + * that doesn't conflict with linuxBIOS + */ + if (end > lb_end) { + unsigned long len = lb_end - start; + struct segment *new; + new = malloc(sizeof(*new)); + *new = *seg; + seg->s_memsz = len; + new->s_memsz -= len; + new->s_addr += len; + new->s_offset += len; + if (seg->s_filesz > len) { + seg->s_filesz = len; + new->s_filesz -= len; + } else { + new->s_filesz = 0; + } + /* Order by stream offset */ + new->next = seg->next; + new->prev = seg; + seg->next->prev = new; + seg->next = new; + /* Order by original program header order */ + new->phdr_next = seg->phdr_next; + new->phdr_prev = seg; + seg->phdr_next->phdr_prev = new; + seg->phdr_next = new; + + /* compute the new value of end */ + end = start + len; + + printk_spew(" late: [0x%016lx, 0x%016lx, 0x%016lx)\n", + new->s_addr, + new->s_addr + new->s_filesz, + new->s_addr + new->s_memsz); + + } + /* Now retarget this segment onto the bounce buffer */ + seg->s_addr = buffer + (seg->s_addr - lb_start); + + printk_spew(" bounce: [0x%016lx, 0x%016lx, 0x%016lx)\n", + seg->s_addr, + seg->s_addr + seg->s_filesz, + seg->s_addr + seg->s_memsz); +} + + +static int build_elf_segment_list( + struct segment *head, + unsigned long bounce_buffer, struct lb_memory *mem, + Elf_phdr *phdr, int headers) +{ + struct segment *ptr; + int i; + memset(head, 0, sizeof(*head)); + head->phdr_next = head->phdr_prev = head; + head->next = head->prev = head; + for(i = 0; i < headers; i++) { + struct segment *new; + /* Ignore data that I don't need to handle */ + if (phdr[i].p_type != PT_LOAD) { + printk_debug("Dropping non PT_LOAD segment\n"); + continue; + } + if (phdr[i].p_memsz == 0) { + printk_debug("Dropping empty segment\n"); + continue; + } + new = malloc(sizeof(*new)); + new->s_addr = phdr[i].p_paddr; + new->s_memsz = phdr[i].p_memsz; + new->s_offset = phdr[i].p_offset; + new->s_filesz = phdr[i].p_filesz; + printk_debug("New segment addr 0x%lx size 0x%lx offset 0x%lx filesize 0x%lx\n", + new->s_addr, new->s_memsz, new->s_offset, new->s_filesz); + /* Clean up the values */ + if (new->s_filesz > new->s_memsz) { + new->s_filesz = new->s_memsz; + } + printk_debug("(cleaned up) New segment addr 0x%lx size 0x%lx offset 0x%lx filesize 0x%lx\n", + new->s_addr, new->s_memsz, new->s_offset, new->s_filesz); + for(ptr = head->next; ptr != head; ptr = ptr->next) { + if (new->s_offset < ptr->s_offset) + break; + } + /* Order by stream offset */ + new->next = ptr; + new->prev = ptr->prev; + ptr->prev->next = new; + ptr->prev = new; + /* Order by original program header order */ + new->phdr_next = head; + new->phdr_prev = head->phdr_prev; + head->phdr_prev->phdr_next = new; + head->phdr_prev = new; + + /* Verify the memory addresses in the segment are valid */ + if (!valid_area(mem, bounce_buffer, new->s_addr, new->s_memsz)) + goto out; + + /* Modify the segment to load onto the bounce_buffer if necessary. + */ + relocate_segment(bounce_buffer, new); + } + return 1; + out: + return 0; +} + +static int load_elf_segments( + struct segment *head, unsigned char *header, unsigned long header_size) +{ + unsigned long offset; + struct segment *ptr; + + offset = 0; + for(ptr = head->next; ptr != head; ptr = ptr->next) { + unsigned long start_offset; + unsigned long skip_bytes, read_bytes; + unsigned char *dest, *middle, *end; + byte_offset_t result; + printk_debug("Loading Segment: addr: 0x%016lx memsz: 0x%016lx filesz: 0x%016lx\n", + ptr->s_addr, ptr->s_memsz, ptr->s_filesz); + + /* Compute the boundaries of the segment */ + dest = (unsigned char *)(ptr->s_addr); + end = dest + ptr->s_memsz; + middle = dest + ptr->s_filesz; + start_offset = ptr->s_offset; + /* Ignore s_offset if I have a pure bss segment */ + if (ptr->s_filesz == 0) { + start_offset = offset; + } + + printk_spew("[ 0x%016lx, %016lx, 0x%016lx) <- %016lx\n", + (unsigned long)dest, + (unsigned long)middle, + (unsigned long)end, + (unsigned long)start_offset); + + /* Skip intial buffer unused bytes */ + if (offset < header_size) { + if (start_offset < header_size) { + offset = start_offset; + } else { + offset = header_size; + } + } + + /* Skip the unused bytes */ + skip_bytes = start_offset - offset; + if (skip_bytes && + ((result = stream_skip(skip_bytes)) != skip_bytes)) { + printk_err("ERROR: Skip of %ld bytes skipped %ld bytes\n", + skip_bytes, result); + goto out; + } + offset = start_offset; + + /* Copy data from the initial buffer */ + if (offset < header_size) { + size_t len; + if ((ptr->s_filesz + start_offset) > header_size) { + len = header_size - start_offset; + } + else { + len = ptr->s_filesz; + } + memcpy(dest, &header[start_offset], len); + dest += len; + } + + /* Read the segment into memory */ + read_bytes = middle - dest; + if (read_bytes && + ((result = stream_read(dest, read_bytes)) != read_bytes)) { + printk_err("ERROR: Read of %ld bytes read %ld bytes...\n", + read_bytes, result); + goto out; + } + offset += ptr->s_filesz; + + /* Zero the extra bytes between middle & end */ + if (middle < end) { + printk_debug("Clearing Segment: addr: 0x%016lx memsz: 0x%016lx\n", + (unsigned long)middle, end - middle); + + /* Zero the extra bytes */ + memset(middle, 0, end - middle); + } + } + return 1; + out: + return 0; +} + +static int verify_loaded_image( + struct verify_callback *vcb, + Elf_ehdr *ehdr, Elf_phdr *phdr, + struct segment *head + ) +{ + struct segment *ptr; + int ok; + ok = 1; + for(; ok && vcb ; vcb = vcb->next) { + /* Find where the note is loaded */ + /* The whole note must be loaded intact + * so an address of 0 for the descriptor is impossible + */ + vcb->desc_addr = 0; + for(ptr = head->next; ptr != head; ptr = ptr->next) { + unsigned long desc_addr; + desc_addr = ptr->s_addr + vcb->desc_offset - ptr->s_offset; + if ((desc_addr >= ptr->s_addr) && + (desc_addr < (ptr->s_addr + ptr->s_filesz))) { + vcb->desc_addr = desc_addr; + } + } + ok = vcb->callback(vcb, ehdr, phdr, head); + } + return ok; +} + +int elfload(struct lb_memory *mem, + unsigned char *header, unsigned long header_size) +{ + Elf_ehdr *ehdr; + Elf_phdr *phdr; + void *entry; + struct segment head; + struct verify_callback *cb_chain; + unsigned long bounce_buffer; + + /* Find a bounce buffer so I can load to linuxBIOS's current location */ + bounce_buffer = get_bounce_buffer(mem); + if (!bounce_buffer) { + printk_err("Could not find a bounce buffer...\n"); + goto out; + } + + ehdr = (Elf_ehdr *)header; + entry = (void *)(ehdr->e_entry); + phdr = (Elf_phdr *)(&header[ehdr->e_phoff]); + + /* Digest elf note information... */ + cb_chain = 0; + if ((phdr[0].p_type == PT_NOTE) && + ((phdr[0].p_offset + phdr[0].p_filesz) < header_size)) { + cb_chain = process_elf_notes(header, + phdr[0].p_offset, phdr[0].p_filesz); + } + + /* Preprocess the elf segments */ + if (!build_elf_segment_list(&head, + bounce_buffer, mem, phdr, ehdr->e_phnum)) + goto out; + + /* Load the segments */ + if (!load_elf_segments(&head, header, header_size)) + goto out; + + printk_spew("Loaded segments\n"); + /* Verify the loaded image */ + if (!verify_loaded_image(cb_chain, ehdr, phdr, &head)) + goto out; + + printk_spew("verified segments\n"); + /* Shutdown the stream device */ + stream_fini(); + + printk_spew("closed down stream\n"); + /* Reset to booting from this image as late as possible */ + boot_successful(); + + printk_debug("Jumping to boot code at 0x%x\n", entry); + post_code(0xfe); + + /* Jump to kernel */ + jmp_to_elf_entry(entry, bounce_buffer); + return 1; + + out: + return 0; +} + +int elfboot(struct lb_memory *mem) +{ + Elf_ehdr *ehdr; + static unsigned char header[ELF_HEAD_SIZE]; + int header_offset; + int i, result; + + result = 0; + printk_info("\n"); + printk_info("Welcome to %s, the open sourced starter.\n", BOOTLOADER); + printk_info("January 2002, Eric Biederman.\n"); + printk_info("Version %s\n", BOOTLOADER_VERSION); + printk_info("\n"); + post_code(0xf8); + + if (stream_init() < 0) { + printk_err("Could not initialize driver...\n"); + goto out; + } + + /* Read in the initial ELF_HEAD_SIZE bytes */ + if (stream_read(header, ELF_HEAD_SIZE) != ELF_HEAD_SIZE) { + printk_err("Read failed...\n"); + goto out; + } + /* Scan for an elf header */ + header_offset = -1; + for(i = 0; i < ELF_HEAD_SIZE - (sizeof(Elf_ehdr) + sizeof(Elf_phdr)); i+=16) { + ehdr = (Elf_ehdr *)(&header[i]); + if (memcmp(ehdr->e_ident, ELFMAG, 4) != 0) { + printk_spew("NO header at %d\n", i); + continue; + } + printk_debug("Found ELF candidate at offset %d\n", i); + /* Sanity check the elf header */ + if ((ehdr->e_type == ET_EXEC) && + elf_check_arch(ehdr) && + (ehdr->e_ident[EI_VERSION] == EV_CURRENT) && + (ehdr->e_version == EV_CURRENT) && + (ehdr->e_ehsize == sizeof(Elf_ehdr)) && + (ehdr->e_phentsize = sizeof(Elf_phdr)) && + (ehdr->e_phoff < (ELF_HEAD_SIZE - i)) && + ((ehdr->e_phoff + (ehdr->e_phentsize * ehdr->e_phnum)) <= + (ELF_HEAD_SIZE - i))) { + header_offset = i; + break; + } + ehdr = 0; + } + printk_spew("header_offset is %d\n", header_offset); + if (header_offset == -1) { + goto out; + } + + printk_spew("Try to load at offset 0x%x\n", header_offset); + result = elfload(mem, + header + header_offset , ELF_HEAD_SIZE - header_offset); + out: + if (!result) { + /* Shutdown the stream device */ + stream_fini(); + + printk_err("Cannot Load ELF Image\n"); + + post_code(0xff); + } + return 0; + +}