diff options
author | Michael Brown | 2005-03-08 19:53:11 +0100 |
---|---|---|
committer | Michael Brown | 2005-03-08 19:53:11 +0100 |
commit | 3d6123e69ab879c72ff489afc5bf93ef0b7a94ce (patch) | |
tree | 9f3277569153a550fa8d81ebd61bd88f266eb8da /src/arch | |
download | ipxe-3d6123e69ab879c72ff489afc5bf93ef0b7a94ce.tar.gz ipxe-3d6123e69ab879c72ff489afc5bf93ef0b7a94ce.tar.xz ipxe-3d6123e69ab879c72ff489afc5bf93ef0b7a94ce.zip |
Initial revision
Diffstat (limited to 'src/arch')
165 files changed, 22389 insertions, 0 deletions
diff --git a/src/arch/armnommu/Config b/src/arch/armnommu/Config new file mode 100644 index 00000000..4c220cd5 --- /dev/null +++ b/src/arch/armnommu/Config @@ -0,0 +1,23 @@ +# Config for armnommu Etherboot +# + +# For a clean compilation, switch in global Config +# off: -DCONFIG_PCI, -DTAGGED_IMAGE, -DELF_IMAGE, -DPXE*, -DRELOCATE and INCLUDE_FILO +# on : -DRAW_IMAGE + +# Serial line settings +CFLAGS+= -DCONSOLE_SERIAL -DCONSPEED=57600 + +# System Frequency +CFLAGS+= -DSYSCLK=73728000 + +# Image Download Address +CFLAGS+= -DRAWADDR=0x40100000 + +# NIC Debug Outputs +#CFLAGS+= -DDEBUG_NIC + +# Fixed MAC address +# p2001_eth has no flash and fixed mac address +#CFLAGS+= -DMAC_HW_ADDR_DRV="'H','Y','L','N','X','1'" +CFLAGS+= -DMAC_HW_ADDR_DRV="0x00,0x09,0x4F,0x00,0x00,0x02" diff --git a/src/arch/armnommu/Makefile b/src/arch/armnommu/Makefile new file mode 100644 index 00000000..a0cbb0c0 --- /dev/null +++ b/src/arch/armnommu/Makefile @@ -0,0 +1,58 @@ +ARCH_FORMAT= armnommu + +ROMLIMIT= 20480 +CHECKSIZE= { read d1; read d1 d2 d3 size d4; [ $$size -gt $(ROMLIMIT) ] &&\ + { $(RM) $@; echo "ERROR: code size exceeds limit!"; exit 1; }; exit 0; } + +START= $(BIN)/start.o + +SRCS+= arch/armnommu/core/arm_timer.c +SRCS+= arch/armnommu/core/start.S +SRCS+= arch/armnommu/core/serial.c +SRCS+= arch/armnommu/core/mem.c +SRCS+= arch/armnommu/core/setjmp.S +SRCS+= arch/armnommu/drivers/net/p2001_eth.c + +# not greater than 100kB +ROMLIMIT:=1024000 + +include $(BIN)/Roms + +ROMS= $(BIN)/p2001_eth.rom +IMGS= $(BIN)/p2001_eth.img + + +allfiles: $(ROMS) + +BOBJS+= $(BIN)/arm_timer.o +BOBJS+= $(BIN)/serial.o +BOBJS+= $(BIN)/mem.o +BOBJS+= $(BIN)/setjmp.o +BOBJS+= $(BIN)/lib1funcs.o + +# Utilities + +$(BIN)/nrv2b: util/nrv2b.c + $(HOST_CC) -O2 -DENCODE -DDECODE -DMAIN -DVERBOSE -DNDEBUG -DBITSIZE=32 -DENDIAN=0 -o $@ $< + +# Pattern Rules +# General for compiling/assembly source files +$(BIN)/%.o: arch/armnommu/core/%.c $(MAKEDEPS) + $(CC) $(CFLAGS) -o $@ -c $< + +$(BIN)/%.o: arch/armnommu/drivers/net/%.c $(MAKEDEPS) + $(CC) $(CFLAGS) -o $@ -c $< + +$(BIN)/%.S: arch/armnommu/core/%.c $(MAKEDEPS) + $(CC) $(CFLAGS) -S -o $@ -c $< + +$(BIN)/%.o: arch/armnommu/core/%.S $(MAKEDEPS) + $(CPP) $(CFLAGS) -D ASSEMBLY $< | $(AS) $(ASFLAGS) -o $@ + +# general ruls for generating .img files +$(BIN)/%.tmp: $(BIN)/%.o $(START) $(BIN)/config.o arch/$(ARCH)/core/etherboot.lds $(LIBS) $(STDDEPS) $(MAKEDEPS) + $(LD) $(LDFLAGS) -T arch/$(ARCH)/core/etherboot.lds -o $@ $(START) $(BIN)/config.o $< $(LIBS) + @$(SIZE) $@ | $(CHECKSIZE) + +$(BIN)/%.img: $(BIN)/%.tmp $(MAKEDEPS) + $(OBJCOPY) -O binary $< $@ diff --git a/src/arch/armnommu/core/arm_timer.c b/src/arch/armnommu/core/arm_timer.c new file mode 100644 index 00000000..b858ca8d --- /dev/null +++ b/src/arch/armnommu/core/arm_timer.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include "etherboot.h" +#include "timer.h" +#include "latch.h" +#include "hardware.h" + + +/* get timer returns the contents of the timer */ +static unsigned long get_timer(void) +{ + return P2001_TIMER->Freerun_Timer; +} + +/* ------ Calibrate the TSC ------- + * Time how long it takes to excute a loop that runs in known time. + * And find the convertion needed to get to CLOCK_TICK_RATE + */ + +static unsigned long configure_timer(void) +{ + return (1); +} + +static unsigned long clocks_per_tick = 1; + +void setup_timers(void) +{ + if (!clocks_per_tick) { + clocks_per_tick = configure_timer(); + } +} + +unsigned long currticks(void) +{ + return get_timer(); /* /clocks_per_tick */ +} + +static unsigned long timer_timeout; +static int __timer_running(void) +{ + return get_timer() < timer_timeout; +} + +void udelay(unsigned int usecs) +{ + unsigned long now; + now = get_timer(); + timer_timeout = now + usecs * ((clocks_per_tick * TICKS_PER_SEC)/(1000*1000)); + while(__timer_running()); +} +void ndelay(unsigned int nsecs) +{ + unsigned long now; + now = get_timer(); + timer_timeout = now + nsecs * ((clocks_per_tick * TICKS_PER_SEC)/(1000*1000*1000)); + while(__timer_running()); +} + +void load_timer2(unsigned int timer2_ticks) +{ + unsigned long now; + unsigned long clocks; + now = get_timer(); + clocks = timer2_ticks * ((clocks_per_tick * TICKS_PER_SEC)/CLOCK_TICK_RATE); + timer_timeout = now + clocks; +} + +int timer2_running(void) +{ + return __timer_running(); +} diff --git a/src/arch/armnommu/core/etherboot.lds b/src/arch/armnommu/core/etherboot.lds new file mode 100644 index 00000000..0901a2f9 --- /dev/null +++ b/src/arch/armnommu/core/etherboot.lds @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") +OUTPUT_ARCH(arm) +ENTRY(_start) +SECTIONS +{ + /*. = 0x00000000;*/ /* PIC */ + /*. = 0x00000400;*/ /* ROM Bootloader */ + . = 0x40000000; /* SDRAM */ + + . = ALIGN(4); + _text = . ; + .text : + { + _start = .; + _virt_start = .; + bin/start.o (.text) + *(.text) + + . = ALIGN(16); + isa_drivers = . ; + *(.drivers.isa); + isa_drivers_end = . ; + } + + . = ALIGN(4); + .rodata : { *(.rodata) } + + . = ALIGN(4); + .data : { *(.data) } + + . = ALIGN(4); + .got : { *(.got) } + + . = ALIGN(4); + _bss = . ; + .bss : { *(.bss) } + + . = ALIGN(4); + _ebss = .; + _end = .; + + . = ALIGN(16); + .text : + { + *(.dma.desc); + *(.dma.buffer); + } +} diff --git a/src/arch/armnommu/core/lib1funcs.c b/src/arch/armnommu/core/lib1funcs.c new file mode 100644 index 00000000..e9e3a8fa --- /dev/null +++ b/src/arch/armnommu/core/lib1funcs.c @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + + +/* + * from gcc/config/udivmodsi4.c + */ +unsigned long +udivmodsi4(unsigned long num, unsigned long den, int modwanted) +{ + unsigned long bit = 1; + unsigned long res = 0; + + while (den < num && bit && !(den & (1L<<31))) + { + den <<=1; + bit <<=1; + } + while (bit) + { + if (num >= den) + { + num -= den; + res |= bit; + } + bit >>=1; + den >>=1; + } + if (modwanted) return num; + return res; +} + + +/* + * from gcc/config/udivmod.c + */ +long +__udivsi3 (long a, long b) +{ + return udivmodsi4 (a, b, 0); +} + +long +__umodsi3 (long a, long b) +{ + return udivmodsi4 (a, b, 1); +} + + +/* + * from gcc/config/divmod.c + */ +long +__divsi3 (long a, long b) +{ + int neg = 0; + long res; + + if (a < 0) + { + a = -a; + neg = !neg; + } + + if (b < 0) + { + b = -b; + neg = !neg; + } + + res = udivmodsi4 (a, b, 0); + + if (neg) + res = -res; + + return res; +} + +long +__modsi3 (long a, long b) +{ + int neg = 0; + long res; + + if (a < 0) + { + a = -a; + neg = 1; + } + + if (b < 0) + b = -b; + + res = udivmodsi4 (a, b, 1); + + if (neg) + res = -res; + + return res; +} diff --git a/src/arch/armnommu/core/mem.c b/src/arch/armnommu/core/mem.c new file mode 100644 index 00000000..a185c5d6 --- /dev/null +++ b/src/arch/armnommu/core/mem.c @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include "hooks.h" +#include "io.h" +#include "etherboot.h" + +struct meminfo meminfo; +void get_memsizes(void) +{ +/* We initialize the meminfo structure + * according to our development board's specs + * We do not have a way to automatically probe the + * memspace instead we initialize it manually + */ + meminfo.basememsize = 0x00000000; + meminfo.memsize = 0x00008000; + meminfo.map_count = 1; + + meminfo.map[0].addr = 0x40000000; + meminfo.map[0].size = 0x01000000; + meminfo.map[0].type = E820_RAM; +} diff --git a/src/arch/armnommu/core/raw_loader.c b/src/arch/armnommu/core/raw_loader.c new file mode 100644 index 00000000..1d49ffeb --- /dev/null +++ b/src/arch/armnommu/core/raw_loader.c @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifdef RAW_IMAGE +static unsigned long raw_load_addr; + +int mach_boot(register unsigned long entry_point) +{ + void (*fnc)(void) = (void *) entry_point; + // r0 = 0 + // r1 = 625 (machine nr. MACH_TYPE_P2001) + (*fnc)(); + + return 0; /* We should never reach this point ! */ +} + +static sector_t raw_download(unsigned char *data, unsigned int len, int eof) +{ + memcpy(phys_to_virt(raw_load_addr), data, len); + raw_load_addr += len; + if (!eof) + return 0; + + done(1); + printf("Starting program.\n"); + mach_boot(RAWADDR); + printf("Bootsector returned?"); + longjmp(restart_etherboot, -2); + return 1; +} + +static os_download_t raw_probe(unsigned char *data __unused, unsigned int len __unused) +{ + printf("(RAW"); + // probe something here... + printf(")... \n"); + + //raw_load_addr = phys_to_virt(_end); + raw_load_addr = RAWADDR; + printf("Writing image to 0x%x\n", raw_load_addr); + return raw_download; +} + +#endif diff --git a/src/arch/armnommu/core/serial.c b/src/arch/armnommu/core/serial.c new file mode 100644 index 00000000..3ae98d47 --- /dev/null +++ b/src/arch/armnommu/core/serial.c @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include "etherboot.h" +#include "hardware.h" +#ifdef CONSOLE_SERIAL + +/* + * void serial_putc(int ch); + * Write character `ch' to port UART_BASE. + */ +void serial_putc(int ch) +{ + /* wait for room in the 32 byte tx FIFO */ + while ((P2001_UART->r.STATUS & 0x3f) > /* 30 */ 0) ; + P2001_UART->w.TX1 = ch & 0xff; +} + +/* + * int serial_getc(void); + * Read a character from port UART_BASE. + */ +int serial_getc(void) +{ + while (((P2001_UART->r.STATUS >> 6) & 0x3f) == 0) ; + return P2001_UART->r.RX1 & 0xff; +} + +/* + * int serial_ischar(void); + * If there is a character in the input buffer of port UART_BASE, + * return nonzero; otherwise return 0. + */ +int serial_ischar(void) +{ + return (P2001_UART->r.STATUS >> 6) & 0x3f; +} + +/* + * int serial_init(void); + * Initialize port to speed 57.600, line settings 8N1. + */ +int serial_init(void) +{ + static unsigned int N; + // const M=3 + P2001_UART->w.Clear = 0; // clear + N = ((SYSCLK/8)*3)/CONSPEED; + P2001_UART->w.Baudrate = (N<<16)+3; // set 57.600 BAUD + P2001_UART->w.Config = 0xcc100; // set 8N1, *water = 12 + return 1; +} + +/* + * void serial_fini(void); + * Cleanup our use of the serial port, in particular flush the + * output buffer so we don't accidentially loose characters. + */ +void serial_fini(void) +{ +} +#endif diff --git a/src/arch/armnommu/core/setjmp.S b/src/arch/armnommu/core/setjmp.S new file mode 100644 index 00000000..7e0a3eec --- /dev/null +++ b/src/arch/armnommu/core/setjmp.S @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +.text + +.global sigsetjmp; +.type sigsetjmp,%function +.align 4; +sigsetjmp: + /* Save registers */ + stmia r0, {v1-v6, sl, fp, sp, lr} + + mov r0, #0 + bx lr +.size sigsetjmp,.-sigsetjmp; + + + +.global longjmp; +.type longjmp,%function +.align 4; +longjmp: + mov ip, r0 /* save jmp_buf pointer */ + + movs r0, r1 /* get the return value in place */ + moveq r0, #1 /* can't let setjmp() return zero! */ + + ldmia ip, {v1-v6, sl, fp, sp, pc} +.size longjmp,.-longjmp; diff --git a/src/arch/armnommu/core/start.S b/src/arch/armnommu/core/start.S new file mode 100644 index 00000000..61b1d31b --- /dev/null +++ b/src/arch/armnommu/core/start.S @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +.global _start + +/* Mode definitions */ +#define Mode_USR 0x10 +#define Mode_FIQ 0x11 +#define Mode_IRQ 0x12 +#define Mode_SVC 0x13 +#define Mode_ABT 0x17 +#define Mode_UNDEF 0x1B +#define Mode_SYS 0x1F // only available on ARM Arch. v4 +#define I_Bit 0x80 +#define F_Bit 0x40 + +/* LPEC register definitions */ +#define Adr_SYS_BASE 0x00100000 +#define REL_Adr_SDRAM_Ctrl 0x10 +#define REL_Adr_ExtMem_Ctrl 0x14 +#define REL_Adr_WaitState_Ext 0x18 +#define REL_Adr_WaitState_Asic 0x1c +#define Adr_TIMER_BASE 0x00110000 +#define REL_Adr_Timer12_PreDiv 0x0c +#define REL_Adr_PLL_12000_config 0x30 +#define REL_Adr_PLL_12288_config 0x34 +#define REL_Adr_DIV_12288_config 0x38 +#define REL_Adr_FSC_CONFIG 0x44 +#define Adr_GPIO_BASE 0x00120000 +#define REL_Adr_NRES_OUT 0x2c + + + +/* Define entry point */ +.arm // Next instruction will be ARM +_start: + +/* + * Initialize memory system + */ + + +/* + * Initialize stack pointer registers + */ + +/* Enter SVC mode and set up the SVC stack pointer */ + mov r0, #(Mode_SVC|I_Bit|F_Bit) + msr cpsr_c, r0 + ldr sp, SP_SVC + + +/* + * Initialize critical IO devices + */ + + /* watchdog off */ + mov r0, #Adr_TIMER_BASE + ldr r1, Timer12_PreDiv + str r1, [r0, #REL_Adr_Timer12_PreDiv] + + /* NRES=1 */ + mov r0, #Adr_GPIO_BASE + ldr r1, NRES_OUT + str r1, [r0, #REL_Adr_NRES_OUT] + + /* ExtMem */ + mov r0, #Adr_SYS_BASE + ldr r1, ExtMem_Ctrl + str r1, [r0, #REL_Adr_ExtMem_Ctrl] + + /* SDRAM */ + mov r0, #Adr_SYS_BASE + ldr r1, SDRAM_Ctrl + str r1, [r0, #REL_Adr_SDRAM_Ctrl] +/* +_wait_sdram_ctrl: + ldr r1, [r0] + tst r1, #0x20000 + beq _wait_sdram_ctrl +*/ + + /* WaitState_Ext */ + ldr r1, WaitState_Ext + str r1, [r0, #REL_Adr_WaitState_Ext] + /* WaitState_Asic */ + ldr r1, WaitState_Asic + str r1, [r0, #REL_Adr_WaitState_Asic] + + /* PLL_12288 */ + mov r0, #Adr_TIMER_BASE + ldr r1, PLL_12288_config + str r1, [r0, #REL_Adr_PLL_12288_config] + /* DIV_12288 */ + ldr r1, DIV_12288_config + str r1, [r0, #REL_Adr_DIV_12288_config] + /* PLL_12200 */ + ldr r1, PLL_12000_config + str r1, [r0, #REL_Adr_PLL_12000_config] + + /* FSC_CONFIG */ + ldr r1, [r0, #REL_Adr_FSC_CONFIG] + bic r1, r1, #0x07 + ldr r2, FSC_CONFIG + orr r1, r1, r2 + str r1, [r0, #REL_Adr_FSC_CONFIG] + + +/* + * Initialize interrupt system variables here + */ + + +/* + * Initialize memory required by main C code + */ + + +/* jump to main program */ + mov r0, #0 + b main + + +Timer12_PreDiv: + .word 0x40bb0000 /* watchdog off */ +NRES_OUT: + .word 0x00000003 /* NRES_OUT_DRV=1, NRES_OUT_DAT=1 */ + +#if SYSCLK == 73728000 +ExtMem_Ctrl: + .word 0x000000e8 /* fuer FPGA 32 Bit konfiguriert */ +SDRAM_Ctrl: +// .word 0x28fc0037 /* default */ + .word 0xaef40027 /* p2001_bit_compact */ +WaitState_Ext: + .word 0xa0001245 /* fuer 73 MHz */ +// .word 0x0000fff3 /* rom bootloader */ +WaitState_Asic: + .word 0x00ff8a5f /* fuer 85 MHz */ +// .word 0x00000203 /* rom bootloader */ +PLL_12288_config: + .word 0x00000004 /* fuer 73 MHz */ +DIV_12288_config: + .word 0x00010601 /* fuer 73 MHz */ +PLL_12000_config: + .word 0x10004e75 /* fuer 85 MHz */ +FSC_CONFIG: + .word 0xc0000005 /* fuer 73 MHz */ +#else +#error "Please define proper timings and wait states for that sysclk." +#endif + +SP_SVC: + .word 0x40fffffc + + + +#ifndef NORELOCATE +/************************************************************************** +RELOCATE_TO - relocate etherboot to the specified address +**************************************************************************/ + .global relocate_to + +relocate_to: + ldr r1, =_start + ldr r2, =_end + + /* while (r1 < r2) { *(r0++) = *(r1++) } */ +_relocate_loop: + cmp r1, r2 + ldrcc r3, [r1], #4 + strcc r3, [r0], #4 + bcc _relocate_loop + mov pc, lr +#endif + + +.global __gccmain +__gccmain: + mov pc, lr /* return from subroutine */ diff --git a/src/arch/armnommu/drivers/net/p2001_eth.c b/src/arch/armnommu/drivers/net/p2001_eth.c new file mode 100644 index 00000000..81bc84c3 --- /dev/null +++ b/src/arch/armnommu/drivers/net/p2001_eth.c @@ -0,0 +1,525 @@ +/************************************************************************** +Etherboot - BOOTP/TFTP Bootstrap Program +P2001 NIC driver for Etherboot +***************************************************************************/ + +/* + * Copyright (C) 2004 Tobias Lorenz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* to get some global routines like printf */ +#include "etherboot.h" +/* to get the interface to the body of the program */ +#include "nic.h" +/* to get the ISA support functions, if this is an ISA NIC */ +#include "isa.h" + +#include "hardware.h" +#include "lxt971a.h" +#include "timer.h" + + +/* NIC specific static variables go here */ +static unsigned char MAC_HW_ADDR[6]={MAC_HW_ADDR_DRV}; + +/* DMA descriptors and buffers */ +#define NUM_RX_DESC 4 /* Number of Rx descriptor registers. */ +#define DMA_BUF_SIZE 2048 /* Buffer size */ +static DMA_DSC txd __attribute__ ((__section__(".dma.desc"))); +static DMA_DSC rxd[NUM_RX_DESC] __attribute__ ((__section__(".dma.desc"))); +static unsigned char rxb[NUM_RX_DESC * DMA_BUF_SIZE] __attribute__ ((__section__(".dma.buffer"))); +static unsigned char txb[ DMA_BUF_SIZE] __attribute__ ((__section__(".dma.buffer"))); +static unsigned int cur_rx; + +/* Device selectors */ +static unsigned int cur_channel; // DMA channel : 0..3 +static unsigned int cur_phy; // PHY Address : 0..31 +static P2001_ETH_regs_ptr EU; // Ethernet Unit : 0x0018_000 with _=0..3 +static P2001_ETH_regs_ptr MU; // Management Unit: 0x00180000 + +#define MDIO_MAXCOUNT 1000 /* mdio abort */ +static unsigned int mdio_error; /* mdio error */ + +/* Function prototypes */ +static void p2001_eth_mdio_init (); +static void p2001_eth_mdio_write(unsigned int phyadr, unsigned int regadr, unsigned int data); +static unsigned int p2001_eth_mdio_read (unsigned int phyadr, unsigned int regadr); +extern unsigned int p2001_eth_mdio_error; + +static int p2001_eth_poll (struct nic *nic, int retrieve); +static void p2001_eth_transmit (struct nic *nic, const char *d, + unsigned int t, unsigned int s, const char *p); + +static void p2001_eth_irq (struct nic *nic, irq_action_t action); + +static void p2001_eth_init (); +static void p2001_eth_disable (struct dev *dev); + +static int p2001_eth_check_link(unsigned int phy); +static int p2001_eth_probe (struct dev *dev, unsigned short *probe_addrs __unused); + + +/************************************************************************** +PHY MANAGEMENT UNIT - Read/write +***************************************************************************/ +static void p2001_eth_mdio_init() +{ + /* reset ethernet PHYs */ + printf("Resetting PHYs...\n"); + + /* GPIO24/25: TX_ER2/TX_ER0 */ + /* GPIO26/27: PHY_RESET/TX_ER1 */ + P2001_GPIO->PIN_MUX |= 0x0018; + // 31-16: 0000 1111 0000 0000 + P2001_GPIO->GPIO2_En |= 0x0400; + + P2001_GPIO->GPIO2_Out |= 0x04000000; + P2001_GPIO->GPIO2_Out &= ~0x0400; + mdelay(500); + P2001_GPIO->GPIO2_Out |= 0x0400; + + /* set management unit clock divisor */ + // max. MDIO CLK = 2.048 MHz (EU.doc) + // max. MDIO CLK = 8.000 MHz (LXT971A) + // sysclk/(2*(n+1)) = MDIO CLK <= 2.048 MHz + // n >= sysclk/4.096 MHz - 1 +#if SYSCLK == 73728000 + P2001_MU->MU_DIV = 17; // 73.728 MHZ =17=> 2.020 MHz +#else + //MU->MU_DIV = (SYSCLK/4.096)-1; +#error "Please define a proper MDIO CLK divisor for that sysclk." +#endif + asm("nop \n nop"); +} + +static void p2001_eth_mdio_write(unsigned int phyadr, unsigned int regadr, unsigned int data) +{ + static unsigned int count; + count = 0; + + /* Warten bis Hardware inaktiv (MIU = "0") */ + while ((MU->MU_CNTL & 0x8000) && (count < MDIO_MAXCOUNT)) + count++; + + /* Schreiben MU_DATA */ + MU->MU_DATA = data; + + /* Schreiben MU_CNTL */ + MU->MU_CNTL = regadr + (phyadr<<5) + (1<<10); + + /* Warten bis Hardware aktiv (MIU = "1") */ + while (((MU->MU_CNTL & 0x8000) == 0) && (count < MDIO_MAXCOUNT)) + count++; + //asm("nop \r\n nop"); + + /* Warten bis Hardware inaktiv (MIU = "0") */ + while ((MU->MU_CNTL & 0x8000) && (count < MDIO_MAXCOUNT)) + count++; + + mdio_error = (count >= MDIO_MAXCOUNT); +} + +static unsigned int p2001_eth_mdio_read(unsigned int phyadr, unsigned int regadr) +{ + static unsigned int count; + count = 0; + + do { + /* Warten bis Hardware inaktiv (MIU = "0") */ + while ((MU->MU_CNTL & 0x8000) && (count < MDIO_MAXCOUNT)) + count++; + + /* Schreiben MU_CNTL */ + MU->MU_CNTL = regadr + (phyadr<<5) + (2<<10); + + /* Warten bis Hardware aktiv (MIU = "1") */ + while (((MU->MU_CNTL & 0x8000) == 0) && (count < MDIO_MAXCOUNT)) + count++; + //asm("nop \r\n nop"); + + /* Warten bis Hardware inaktiv (MIU = "0") */ + while ((MU->MU_CNTL & 0x8000) && (count < MDIO_MAXCOUNT)) + count++; + + /* Fehler, wenn MDIO Read Error (MRE = "1") */ + } while ((MU->MU_CNTL & 0x4000) && (count < MDIO_MAXCOUNT)); + + /* Lesen MU_DATA */ + mdio_error = (count >= MDIO_MAXCOUNT); + return MU->MU_DATA; +} + + +/************************************************************************** +POLL - Wait for a frame +***************************************************************************/ +/* Function: p2001_eth_poll + * + * Description: checks for a received packet and returns it if found. + * + * Arguments: struct nic *nic: NIC data structure + * + * Returns: 1 if a packet was received. + * 0 if no pacet was received. + * + * Side effects: + * Returns (copies) the packet to the array nic->packet. + * Returns the length of the packet in nic->packetlen. + */ +static int p2001_eth_poll(struct nic *nic, int retrieve) +{ + /* return true if there's an ethernet packet ready to read */ + /* nic->packet should contain data on return */ + /* nic->packetlen should contain length of data */ + + int retstat = 0; + + if (rxd[cur_rx].stat & (1<<31)) // OWN + return retstat; + + if (!retrieve) + return 1; + + nic->packetlen = rxd[cur_rx].cntl & 0xffff; + + if (rxd[cur_rx].stat & ((1<<26)|(1<<25)|(1<<24)|(1<<23)|(1<<22))) { + /* corrupted packet received */ + printf("p2001_eth_poll: Corrupted packet received, stat = %X\n", + rxd[cur_rx].stat); + retstat = 0; + } else { + /* give packet to higher routine */ + memcpy(nic->packet, (rxb + cur_rx*DMA_BUF_SIZE), nic->packetlen); + retstat = 1; + } + +#ifdef DEBUG_NIC + printf("p2001_eth_poll: packet from %! to %! received\n", + (rxb+cur_rx*DMA_BUF_SIZE)+ETH_ALEN, + (rxb+cur_rx*DMA_BUF_SIZE)); +#endif + + /* disable receiver */ + // FIXME: is that ok? it can produce grave errors. + EU->RMAC_DMA_EN = 0; /* clear run bit */ + + /* return the descriptor and buffer to receive ring */ + rxd[cur_rx].stat = (1<<31) | (1<<30) | (1<<29); // DSC0 OWN|START|END + rxd[cur_rx].cntl = (1<<23); // DSC1 RECEIVE + rxd[cur_rx].cntl |= cur_channel << 16; // DSC1 CHANNEL + rxd[cur_rx].cntl |= DMA_BUF_SIZE; // DSC1 LEN + + if (++cur_rx == NUM_RX_DESC) + cur_rx = 0; + + /* enable receiver */ + if (!(EU->RMAC_DMA_EN & 0x01)) + EU->RMAC_DMA_EN = 0x01; /* set run bit */ + +#ifdef DEBUG_NIC + printf("RMAC_MIB0..5: %d:%d:%d:%d:%d:%d\n", + EU->RMAC_MIB0, EU->RMAC_MIB1, + EU->RMAC_MIB2, EU->RMAC_MIB3, + EU->RMAC_MIB4, EU->RMAC_MIB5); +#endif + + return retstat; /* initially as this is called to flush the input */ +} + + +/************************************************************************** +TRANSMIT - Transmit a frame +***************************************************************************/ +/* Function: p2001_eth_transmit + * + * Description: transmits a packet and waits for completion or timeout. + * + * Arguments: char d[6]: destination ethernet address. + * unsigned short t: ethernet protocol type. + * unsigned short s: size of the data-part of the packet. + * char *p: the data for the packet. + * + * Returns: void. + */ +static void p2001_eth_transmit( + struct nic *nic __unused, + const char *d, /* Destination */ + unsigned int t, /* Type */ + unsigned int s, /* size */ + const char *p) /* Packet */ +{ + unsigned int nstype; +#ifdef DEBUG_NIC + unsigned int status; +#endif + + /* assemble packet */ + memcpy(txb, d, ETH_ALEN); // destination + memcpy(txb+ETH_ALEN, nic->node_addr, ETH_ALEN); // source + nstype = htons(t); + memcpy(txb+2*ETH_ALEN, (char*)&nstype, 2); // type + memcpy(txb+ETH_HLEN, p, s); // packet + s += ETH_HLEN; + + /* pad to minimum packet size */ +// while (s<ETH_ZLEN) +// txb[s++] = '\0'; + // TMAC_CNTL.ATP does the same + +#ifdef DEBUG_NIC + printf("p2001_eth_transmit: packet from %! to %! sent\n", txb+ETH_ALEN, txb); +#endif + + /* configure descriptor */ + txd.stat = (1<<31) | (1<<30) | (1<<29); // DSC0 OWN|START|END + txd.cntl = cur_channel << 16; // DSC1 CHANNEL + txd.cntl |= s; // DSC1 LEN + + /* restart the transmitter */ + EU->TMAC_DMA_EN = 0x01; /* set run bit */ + while(EU->TMAC_DMA_EN & 0x01) ; /* wait */ + +#ifdef DEBUG_NIC + /* check status */ + status = EU->TMAC_DMA_STAT; + if (status & ~(0x40)) + printf("p2001_eth_transmit: dma status=0x%hx\n", status); + + printf("TMAC_MIB6..7: %d:%d\n", EU->TMAC_MIB6, EU->TMAC_MIB7); +#endif +} + + +/************************************************************************** +IRQ - Enable, Disable or Force Interrupts +***************************************************************************/ +/* Function: p2001_eth_irq + * + * Description: Enable, Disable, or Force, interrupts + * + * Arguments: struct nic *nic: NIC data structure + * irq_action_t action: Requested action + * + * Returns: void. + */ + +static void +p2001_eth_irq(struct nic *nic __unused, irq_action_t action __unused) +{ + switch ( action ) { + case DISABLE : + break; + case ENABLE : + break; + case FORCE : + break; + } +} + + +/************************************************************************** +INIT - Initialize device +***************************************************************************/ +/* Function: p2001_init + * + * Description: resets the ethernet controller chip and various + * data structures required for sending and receiving packets. + * + * returns: void. + */ +static void p2001_eth_init() +{ + static int i; + + /* disable transceiver */ +// EU->TMAC_DMA_EN = 0; /* clear run bit */ +// EU->RMAC_DMA_EN = 0; /* clear run bit */ + + /* set rx filter (physical mac addresses) */ + EU->RMAC_PHYU = + (MAC_HW_ADDR[0]<< 8) + + (MAC_HW_ADDR[1]<< 0); + EU->RMAC_PHYL = + (MAC_HW_ADDR[2]<<24) + + (MAC_HW_ADDR[3]<<16) + + (MAC_HW_ADDR[4]<<8 ) + + (MAC_HW_ADDR[5]<<0 ); + + /* initialize the tx descriptor ring */ +// txd.stat = (1<<31) | (1<<30) | (1<<29); // DSC0 OWN|START|END +// txd.cntl = cur_channel << 16; // DSC1 CHANNEL +// txd.cntl |= DMA_BUF_SIZE; // DSC1 LEN + txd.buf = &txb; // DSC2 BUFFER + txd.next = &txd; // DSC3 NEXTDSC @self + EU->TMAC_DMA_DESC = &txd; + + /* initialize the rx descriptor ring */ + cur_rx = 0; + for (i = 0; i < NUM_RX_DESC; i++) { + rxd[i].stat = (1<<31) | (1<<30) | (1<<29); // DSC0 OWN|START|END + rxd[i].cntl = (1<<23); // DSC1 RECEIVE + rxd[i].cntl |= cur_channel << 16; // DSC1 CHANNEL + rxd[i].cntl |= DMA_BUF_SIZE; // DSC1 LEN + rxd[i].buf = &rxb[i*DMA_BUF_SIZE]; // DSC2 BUFFER (EU-RX data) + rxd[i].next = &rxd[i+1]; // DSC3 NEXTDSC @next + } + rxd[NUM_RX_DESC-1].next = &rxd[0]; // DSC3 NEXTDSC @first + EU->RMAC_DMA_DESC = &rxd[0]; + + /* set transmitter mode */ + EU->TMAC_CNTL = (1<<4) | /* COI: Collision ignore */ + //(1<<3) | /* CSI: Carrier Sense ignore */ + (1<<2); /* ATP: Automatic Transmit Padding */ + + /* set receive mode */ + EU->RMAC_CNTL = (1<<3) | /* BROAD: Broadcast packets */ + (1<<1); /* PHY : Packets to out MAC address */ + + /* enable receiver */ + EU->RMAC_DMA_EN = 1; /* set run bit */ +} + + +/************************************************************************** +DISABLE - Turn off ethernet interface +***************************************************************************/ +static void p2001_eth_disable(struct dev *dev __unused) +{ + /* put the card in its initial state */ + /* This function serves 3 purposes. + * This disables DMA and interrupts so we don't receive + * unexpected packets or interrupts from the card after + * etherboot has finished. + * This frees resources so etherboot may use + * this driver on another interface + * This allows etherboot to reinitialize the interface + * if something is something goes wrong. + */ + + /* disable transmitter */ + EU->TMAC_DMA_EN = 0; /* clear run bit */ + + /* disable receiver */ + EU->RMAC_DMA_EN = 0; /* clear run bit */ +} + + +/************************************************************************** +LINK - Check for valid link +***************************************************************************/ +static int p2001_eth_check_link(unsigned int phy) +{ + static int status; + static unsigned int count; + count = 0; + + /* Use 0x3300 for restarting NWay */ + printf("Starting auto-negotiation... "); + p2001_eth_mdio_write(phy, Adr_LXT971A_Control, 0x3300); + if (mdio_error) + goto failed; + + /* Bits 1.5 and 17.7 are set to 1 once the Auto-Negotiation process to completed. */ + do { + mdelay(500); + status = p2001_eth_mdio_read(phy, Adr_LXT971A_Status1); + if (mdio_error || (count++ > 6)) // 6*500ms = 3s timeout + goto failed; + } while (!(status & 0x20)); + + /* Bits 1.2 and 17.10 are set to 1 once the link is established. */ + if (p2001_eth_mdio_read(phy, Adr_LXT971A_Status1) & 0x04) { + /* Bits 17.14 and 17.9 can be used to determine the link operation conditions (speed and duplex). */ + printf("Valid link, operating at: %sMb-%s\n", + (p2001_eth_mdio_read(phy, Adr_LXT971A_Status2) & 0x4000) ? "100" : "10", + (p2001_eth_mdio_read(phy, Adr_LXT971A_Status2) & 0x0200) ? "FD" : "HD"); + return 1; + } + +failed: + if (mdio_error) + printf("Failed\n"); + else + printf("No valid link\n"); + return 0; +} + + +/************************************************************************** +PROBE - Look for an adapter, this routine's visible to the outside +***************************************************************************/ +static int p2001_eth_probe(struct dev *dev, unsigned short *probe_addrs __unused) +{ + struct nic *nic = (struct nic *)dev; + /* if probe_addrs is 0, then routine can use a hardwired default */ + static int board_found; + static int valid_link; + + /* reset phys and configure mdio clk */ + p2001_eth_mdio_init(); + + /* find the correct PHY/DMA/MAC combination */ + MU = P2001_MU; // MU for all PHYs is only in EU0 + printf("Searching for P2001 NICs...\n"); + for (cur_channel=0; cur_channel<4; cur_channel++) { + switch(cur_channel) { + case 0: + EU = P2001_EU0; + cur_phy = 0; + break; + case 1: + EU = P2001_EU1; + cur_phy = 1; + break; + case 2: + EU = P2001_EU2; + cur_phy = 2; + break; + case 3: + EU = P2001_EU3; + cur_phy = 3; + break; + } + + /* first a non destructive test for initial value RMAC_TLEN=1518 */ + board_found = (EU->RMAC_TLEN == 1518); + if (board_found) { + printf("Checking EU%d...\n", cur_channel); + + valid_link = p2001_eth_check_link(cur_phy); + if (valid_link) { + /* initialize device */ + p2001_eth_init(nic); + + /* set node address */ + printf("Setting MAC address to %!\n", MAC_HW_ADDR); + memcpy(nic->node_addr, MAC_HW_ADDR, 6); + + /* point to NIC specific routines */ + dev->disable = p2001_eth_disable; + nic->poll = p2001_eth_poll; + nic->transmit = p2001_eth_transmit; + nic->irq = p2001_eth_irq; + + /* Report the ISA pnp id of the board */ + dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR); + dev->devid.vendor_id = htons(0x1234); + return 1; + } + } + } + /* else */ + return 0; +} + +ISA_ROM("p2001_eth", "P2001 Ethernet Driver") +static struct isa_driver p2001_eth_driver __isa_driver = { + .type = NIC_DRIVER, + .name = "P2001 Ethernet Driver", + .probe = p2001_eth_probe, + .ioaddrs = 0, +}; diff --git a/src/arch/armnommu/include/bits/byteswap.h b/src/arch/armnommu/include/bits/byteswap.h new file mode 100644 index 00000000..2aae0800 --- /dev/null +++ b/src/arch/armnommu/include/bits/byteswap.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef ETHERBOOT_BITS_BYTESWAP_H +#define ETHERBOOT_BITS_BYTESWAP_H + +/* We do not have byte swap functions ... We are + * RISC processor ... + */ + +static inline unsigned short __swap16(volatile unsigned short v) +{ + return ((v << 8) | (v >> 8)); +} + +static inline unsigned int __swap32(volatile unsigned long v) +{ + return ((v << 24) | ((v & 0xff00) << 8) | ((v & 0xff0000) >> 8) | (v >> 24)); +} + +#define __bswap_constant_16(x) \ + ((uint16_t)((((uint16_t)(x) & 0x00ff) << 8) | \ + (((uint16_t)(x) & 0xff00) >> 8))) + +#define __bswap_constant_32(x) \ + ((uint32_t)((((uint32_t)(x) & 0x000000ffU) << 24) | \ + (((uint32_t)(x) & 0x0000ff00U) << 8) | \ + (((uint32_t)(x) & 0x00ff0000U) >> 8) | \ + (((uint32_t)(x) & 0xff000000U) >> 24))) + +# define __bswap_16(x) \ + (__extension__ \ + ({ unsigned short int __bsx = (x); \ + ((((__bsx) >> 8) & 0xff) | (((__bsx) & 0xff) << 8)); })) + + +# define __bswap_32(x) \ + (__extension__ \ + ({ unsigned int __bsx = (x); \ + ((((__bsx) & 0xff000000) >> 24) | (((__bsx) & 0x00ff0000) >> 8) | \ + (((__bsx) & 0x0000ff00) << 8) | (((__bsx) & 0x000000ff) << 24)); })) + +#endif /* ETHERBOOT_BITS_BYTESWAP_H */ diff --git a/src/arch/armnommu/include/bits/cpu.h b/src/arch/armnommu/include/bits/cpu.h new file mode 100644 index 00000000..9516a600 --- /dev/null +++ b/src/arch/armnommu/include/bits/cpu.h @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef ARM_BITS_CPU_H +#define ARM_BITS_CPU_H + +#define cpu_setup() do {} while(0) + +#endif /* ARM_BITS_CPU_H */ diff --git a/src/arch/armnommu/include/bits/elf.h b/src/arch/armnommu/include/bits/elf.h new file mode 100644 index 00000000..e0a17aa5 --- /dev/null +++ b/src/arch/armnommu/include/bits/elf.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef ARM_BITS_ELF_H +#define ARM_BITS_ELF_H + +/* ELF Defines for the current architecture */ +#define EM_CURRENT EM_ARM +#define ELFDATA_CURRENT ELFDATA2LSB + +#define ELF_CHECK_ARCH(x) \ + ((x).e_machine == EM_CURRENT) + +#endif /* ARM_BITS_ELF_H */ diff --git a/src/arch/armnommu/include/bits/endian.h b/src/arch/armnommu/include/bits/endian.h new file mode 100644 index 00000000..0a18d97d --- /dev/null +++ b/src/arch/armnommu/include/bits/endian.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef ETHERBOOT_BITS_ENDIAN_H +#define ETHERBOOT_BITS_ENDIAN_H + +#ifdef __ARMEB__ +#define __BYTE_ORDER __BIG_ENDIAN +#else +#define __BYTE_ORDER __LITTLE_ENDIAN +#endif + +#endif /* ETHERBOOT_BITS_ENDIAN_H */ diff --git a/src/arch/armnommu/include/bits/string.h b/src/arch/armnommu/include/bits/string.h new file mode 100644 index 00000000..9cfce809 --- /dev/null +++ b/src/arch/armnommu/include/bits/string.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef ETHERBOOT_BITS_STRING_H +#define ETHERBOOT_BITS_STRING_H + +#endif /* ETHERBOOT_BITS_STRING_H */ diff --git a/src/arch/armnommu/include/callbacks_arch.h b/src/arch/armnommu/include/callbacks_arch.h new file mode 100644 index 00000000..fa882cb5 --- /dev/null +++ b/src/arch/armnommu/include/callbacks_arch.h @@ -0,0 +1 @@ +/* empty file */ diff --git a/src/arch/armnommu/include/hardware.h b/src/arch/armnommu/include/hardware.h new file mode 100644 index 00000000..203b78b7 --- /dev/null +++ b/src/arch/armnommu/include/hardware.h @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * Architecture: ARM9TDMI + * Processor : P2001 + */ + +#ifndef ARCH_HARDWARE_H +#define ARCH_HARDWARE_H + +#ifndef __ASSEMBLY__ + +/* DMA descriptor */ +typedef struct { + unsigned int stat; /* status: own, start, end, offset, status */ + unsigned int cntl; /* control: loop, int, type, channel, length */ + char *buf; /* buffer */ + void *next; /* nextdsc */ +} DMA_DSC; + + +/* The address definitions are from asic_bf.h */ +typedef struct { // 0x00100000U + volatile unsigned int reserved1[0x3]; + volatile unsigned int ArmDmaPri; // 0x0000000CU + volatile unsigned int SDRAM_Ctrl; // 0x00000010U + volatile unsigned int ExtMem_Ctrl; // 0x00000014U + volatile unsigned int WaitState_Ext; // 0x00000018U + volatile unsigned int WaitState_Asic; // 0x0000001CU + volatile unsigned int TOP; // 0x00000020U + volatile unsigned int reserved2[0x3]; + volatile unsigned int Adr1_EQ_30Bit; // 0x00000030U + volatile unsigned int Adr2_EQ_30Bit; // 0x00000034U + volatile unsigned int Adr3_EQ_30Bit; // 0x00000038U + volatile unsigned int Dat3_EQ_32Bit; // 0x0000003CU + volatile unsigned int Adr4_HE_20Bit; // 0x00000040U + volatile unsigned int Adr4_LT_20Bit; // 0x00000044U + volatile unsigned int Adr5_HE_20Bit; // 0x00000048U + volatile unsigned int Adr5_LT_20Bit; // 0x0000004CU + volatile unsigned int Adr_Control; // 0x00000050U + volatile unsigned int ABORT_IA_32Bit; // 0x00000054U +} *P2001_SYS_regs_ptr; +#define P2001_SYS ((volatile P2001_SYS_regs_ptr) 0x00100000) + +typedef struct { // 0x00110000U + volatile unsigned int Timer1; // 0x00000000U + volatile unsigned int Timer2; // 0x00000004U + volatile unsigned int TIMER_PRELOAD; // 0x00000008U + volatile unsigned int Timer12_PreDiv; // 0x0000000CU + volatile unsigned int TIMER_INT; // 0x00000010U + volatile unsigned int Freerun_Timer; // 0x00000014U + volatile unsigned int WatchDog_Timer; // 0x00000018U + volatile unsigned int PWM_CNT; // 0x00000020U + volatile unsigned int PWM_CNT2; // 0x00000024U + volatile unsigned int PLL_12000_config; // 0x00000030U + volatile unsigned int PLL_12288_config; // 0x00000034U + volatile unsigned int DIV_12288_config; // 0x00000038U + volatile unsigned int MOD_CNT_768; // 0x0000003CU + volatile unsigned int FSC_IRQ_STATUS; // 0x00000040U + volatile unsigned int FSC_CONFIG; // 0x00000044U + volatile unsigned int FSC_CONSTRUCT; // 0x00000048U + volatile unsigned int FSC_base_clk_reg; // 0x0000004CU + volatile unsigned int SYSCLK_SHAPE; // 0x00000050U + volatile unsigned int SDRAMCLK_SHAPE; // 0x00000054U + volatile unsigned int RING_OSZI; // 0x00000058U +} *P2001_TIMER_regs_ptr; +#define P2001_TIMER ((volatile P2001_TIMER_regs_ptr) 0x00110000) + +typedef struct { // 0x00120000U + volatile unsigned int reserved1[0x5]; + volatile unsigned int GPIO_Config; // 0x00000014U + volatile unsigned int GPIO_INT; // 0x00000018U + volatile unsigned int GPIO_Out; // 0x0000001CU + volatile unsigned int GPIO_IN; // 0x00000020U + volatile unsigned int GPIO_En; // 0x00000024U + volatile unsigned int PIN_MUX; // 0x00000028U + volatile unsigned int NRES_OUT; // 0x0000002CU + volatile unsigned int GPIO2_Out; // 0x00000030U + volatile unsigned int GPIO2_IN; // 0x00000034U + volatile unsigned int GPIO2_En; // 0x00000038U + volatile unsigned int GPIO_INT_SEL; // 0x0000003CU + volatile unsigned int GPI3_IN; // 0x00000040U + volatile unsigned int GPO4_OUT; // 0x00000044U +} *P2001_GPIO_regs_ptr; +#define P2001_GPIO ((volatile P2001_GPIO_regs_ptr) 0x00120000) + +typedef struct { // 0x00130000U + volatile unsigned int Main_NFIQ_Int_Ctrl; // 0x00000000U + volatile unsigned int Main_NIRQ_Int_Ctrl; // 0x00000004U + volatile unsigned int Status_NFIQ; // 0x00000008U + volatile unsigned int Status_NIRQ; // 0x0000000CU +} *P2001_INT_CTRL_regs_ptr; +#define P2001_INT_CTRL ((volatile P2001_INT_CTRL_regs_ptr) 0x00130000) + +typedef union { // 0x00140000U + struct { // write + volatile unsigned int TX1; // 0x00000000U + volatile unsigned int TX2; // 0x00000004U + volatile unsigned int TX3; // 0x00000008U + volatile unsigned int TX4; // 0x0000000CU + volatile unsigned int Baudrate; // 0x00000010U + volatile unsigned int reserved1[0x3]; + volatile unsigned int Config; // 0x00000020U + volatile unsigned int Clear; // 0x00000024U + volatile unsigned int Echo_EN; // 0x00000028U + volatile unsigned int IRQ_Status; // 0x0000002CU + } w; // write + + struct { // read + volatile unsigned int RX1; // 0x00000000U + volatile unsigned int RX2; // 0x00000004U + volatile unsigned int RX3; // 0x00000008U + volatile unsigned int RX4; // 0x0000000CU + volatile unsigned int reserved1[0x4]; + volatile unsigned int PRE_STATUS; // 0x00000020U + volatile unsigned int STATUS; // 0x00000024U + volatile unsigned int reserved2[0x1]; + volatile unsigned int IRQ_Status; // 0x0000002CU + } r; // read +} *P2001_UART_regs_ptr; +#define P2001_UART ((volatile P2001_UART_regs_ptr) 0x00140000) + +typedef struct { // 0x0018_000U _=0,1,2,3 + volatile DMA_DSC * RMAC_DMA_DESC; // 0x00000000U + volatile unsigned int RMAC_DMA_CNTL; // 0x00000004U + volatile unsigned int RMAC_DMA_STAT; // 0x00000008U + volatile unsigned int RMAC_DMA_EN; // 0x0000000CU + volatile unsigned int RMAC_CNTL; // 0x00000010U + volatile unsigned int RMAC_TLEN; // 0x00000014U + volatile unsigned int RMAC_PHYU; // 0x00000018U + volatile unsigned int RMAC_PHYL; // 0x0000001CU + volatile unsigned int RMAC_PFM0; // 0x00000020U + volatile unsigned int RMAC_PFM1; // 0x00000024U + volatile unsigned int RMAC_PFM2; // 0x00000028U + volatile unsigned int RMAC_PFM3; // 0x0000002CU + volatile unsigned int RMAC_PFM4; // 0x00000030U + volatile unsigned int RMAC_PFM5; // 0x00000034U + volatile unsigned int RMAC_PFM6; // 0x00000038U + volatile unsigned int RMAC_PFM7; // 0x0000003CU + volatile unsigned int RMAC_MIB0; // 0x00000040U + volatile unsigned int RMAC_MIB1; // 0x00000044U + volatile unsigned int RMAC_MIB2; // 0x00000048U + volatile unsigned int RMAC_MIB3; // 0x0000004CU + volatile unsigned int RMAC_MIB4; // 0x00000050U + volatile unsigned int RMAC_MIB5; // 0x00000054U + volatile unsigned int reserved1[0x1e8]; + volatile unsigned int RMAC_DMA_DATA; // 0x000007F8U + volatile unsigned int RMAC_DMA_ADR; // 0x000007FCU + volatile DMA_DSC * TMAC_DMA_DESC; // 0x00000800U + volatile unsigned int TMAC_DMA_CNTL; // 0x00000804U + volatile unsigned int TMAC_DMA_STAT; // 0x00000808U + volatile unsigned int TMAC_DMA_EN; // 0x0000080CU + volatile unsigned int TMAC_CNTL; // 0x00000810U + volatile unsigned int TMAC_MIB6; // 0x00000814U + volatile unsigned int TMAC_MIB7; // 0x00000818U + volatile unsigned int reserved2[0x1]; + volatile unsigned int MU_CNTL; // 0x00000820U + volatile unsigned int MU_DATA; // 0x00000824U + volatile unsigned int MU_DIV; // 0x00000828U + volatile unsigned int CONF_RMII; // 0x0000082CU + volatile unsigned int reserved3[0x1f2]; + volatile unsigned int TMAC_DMA_DATA; // 0x00000FF8U + volatile unsigned int TMAC_DMA_ADR; // 0x00000FFCU +} *P2001_ETH_regs_ptr; +#define P2001_EU0 ((volatile P2001_ETH_regs_ptr) 0x00180000) +#define P2001_EU1 ((volatile P2001_ETH_regs_ptr) 0x00181000) +#define P2001_EU2 ((volatile P2001_ETH_regs_ptr) 0x00182000) +#define P2001_EU3 ((volatile P2001_ETH_regs_ptr) 0x00183000) +#define P2001_MU P2001_EU0 + +#endif + +#endif /* ARCH_HARDWARE_H */ diff --git a/src/arch/armnommu/include/hooks.h b/src/arch/armnommu/include/hooks.h new file mode 100644 index 00000000..cdcf023b --- /dev/null +++ b/src/arch/armnommu/include/hooks.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef ETHERBOOT_ARM_HOOKS_H +#define ETHERBOOT_ARM_HOOKS_H + +struct Elf_Bhdr; + +#define arch_main(data, params) do {} while(0) +//void arch_main(in_call_data_t *data, va_list params); + +#define arch_on_exit(status) do {} while(0) +//void arch_on_exit(int status); + +#define arch_relocate_to(addr) do {} while(0) +//void arch_relocate_to(unsigned long addr); + +#define arch_relocated_from(old_addr) do {} while(0) +//void arch_relocate_from(unsigned long old_addr); + +#endif /* ETHERBOOT_ARM_HOOKS_H */ diff --git a/src/arch/armnommu/include/io.h b/src/arch/armnommu/include/io.h new file mode 100644 index 00000000..478e443d --- /dev/null +++ b/src/arch/armnommu/include/io.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef ETHERBOOT_IO_H +#define ETHERBOOT_IO_H + +#define virt_to_phys(vaddr) ((unsigned long) (vaddr)) +#define phys_to_virt(vaddr) ((void *) (vaddr)) + +#define virt_to_bus virt_to_phys +#define bus_to_virt phys_to_virt + +#define iounmap(addr) ((void)0) +#define ioremap(physaddr, size) (physaddr) + +extern unsigned char inb (unsigned long int port); +extern unsigned short int inw (unsigned long int port); +extern unsigned long int inl (unsigned long int port); +extern void outb (unsigned char value, unsigned long int port); +extern void outw (unsigned short value, unsigned long int port); +extern void outl (unsigned long value, unsigned long int port); + +#endif /* ETHERBOOT_IO_H */ diff --git a/src/arch/armnommu/include/latch.h b/src/arch/armnommu/include/latch.h new file mode 100644 index 00000000..23ceaa30 --- /dev/null +++ b/src/arch/armnommu/include/latch.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef LATCH_H +#define LATCH_H + +// Freerun_Timer is always at 12.288 MHZ +#define TICKS_PER_SEC (12288000UL) +//#define TICKS_PER_SEC (73728000UL) + +#endif /* LATCH_H */ diff --git a/src/arch/armnommu/include/limits.h b/src/arch/armnommu/include/limits.h new file mode 100644 index 00000000..8df03ef2 --- /dev/null +++ b/src/arch/armnommu/include/limits.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __LIMITS_H +#define __LIMITS_H 1 + +#define MB_LEN_MAX 16 + +#define CHAR_BIT 8 + +#define SCHAR_MIN (-128) +#define SCHAR_MAX 127 + +#define UCHAR_MAX 255 + +#define CHAR_MIN SCHAR_MIN +#define CHAR_MAX SCHAR_MAX + +#define SHRT_MIN (-32768) +#define SHRT_MAX 32767 + +#define USHRT_MAX 65535 + +#define INT_MIN (-INT_MAX - 1) +#define INT_MAX 2147483647 + +#define UINT_MAX 4294967295U + +#define LONG_MAX 2147483647L +#define LONG_MIN (-LONG_MAX - 1L) + +#define ULONG_MAX 4294967295UL + +#define LLONG_MAX 9223372036854775807LL +#define LLONG_MIN (-LLONG_MAX - 1LL) + +#define ULLONG_MAX 18446744073709551615ULL + +#endif diff --git a/src/arch/armnommu/include/lxt971a.h b/src/arch/armnommu/include/lxt971a.h new file mode 100644 index 00000000..16314ec2 --- /dev/null +++ b/src/arch/armnommu/include/lxt971a.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * Intel LXT971ALE (MII-compatible PHY) + */ + +#define Adr_LXT971A_Control 0 /* Control Register */ +#define Adr_LXT971A_Status1 1 /* MII Status Register #1 */ +#define Adr_LXT971A_PHY_ID1 2 /* PHY Identification Register 1 */ +#define Adr_LXT971A_PHY_ID2 3 /* PHY Identification Register 2 */ +#define Adr_LXT971A_AN_Advertise 4 /* Auto Negotiation Advertisement Register */ +#define Adr_LXT971A_AN_Link_Ability 5 /* Auto Negotiation Link Partner Base Page Ability Register */ +#define Adr_LXT971A_AN_Expansion 6 /* Auto Negotiation Expansion */ +#define Adr_LXT971A_AN_Next_Page_Txmit 7 /* Auto Negotiation Next Page Transmit Register */ +#define Adr_LXT971A_AN_Link_Next_Page 8 /* Auto Negotiation Link Partner Next Page Receive Register */ +#define Adr_LXT971A_Fast_Control 9 /* Not Implemented */ +#define Adr_LXT971A_Fast_Status 10 /* Not Implemented */ +#define Adr_LXT971A_Extended_Status 15 /* Not Implemented */ +#define Adr_LXT971A_Port_Config 16 /* Configuration Register */ +#define Adr_LXT971A_Status2 17 /* Status Register #2 */ +#define Adr_LXT971A_Interrupt_Enable 18 /* Interrupt Enable Register */ +#define Adr_LXT971A_Interrupt_Status 19 /* Interrupt Status Register */ +#define Adr_LXT971A_LED_Config 20 /* LED Configuration Register */ +#define Adr_LXT971A_Transmit_Control 30 /* Transmit Control Register */ diff --git a/src/arch/armnommu/include/setjmp.h b/src/arch/armnommu/include/setjmp.h new file mode 100644 index 00000000..6499c488 --- /dev/null +++ b/src/arch/armnommu/include/setjmp.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef ETHERBOOT_SETJMP_H +#define ETHERBOOT_SETJMP_H + +#ifndef __ASSEMBLER__ +/* Jump buffer contains v1-v6, sl, fp, sp and pc. Other registers are not + saved. */ +//typedef int jmp_buf[22]; +typedef int jmp_buf[10]; +#endif + +extern int sigsetjmp(jmp_buf __env, int __savemask); +extern void longjmp(jmp_buf __env, int __val) __attribute__((__noreturn__)); + +#define setjmp(env) sigsetjmp(env,0) + +#endif /* ETHERBOOT_SETJMP_H */ diff --git a/src/arch/armnommu/include/stdint.h b/src/arch/armnommu/include/stdint.h new file mode 100644 index 00000000..3f5dc3f9 --- /dev/null +++ b/src/arch/armnommu/include/stdint.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2004 Tobias Lorenz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef STDINT_H +#define STDINT_H + +typedef unsigned long size_t; + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned long uint32_t; +typedef unsigned long long uint64_t; + +typedef signed char int8_t; +typedef signed short int16_t; +typedef signed int int32_t; +typedef signed long long int64_t; + +#endif /* STDINT_H */ diff --git a/src/arch/e1/Config b/src/arch/e1/Config new file mode 100644 index 00000000..69a7a978 --- /dev/null +++ b/src/arch/e1/Config @@ -0,0 +1,7 @@ +# Config for e1 Etherboot +# + +CFLAGS+= -mgnu-param -DCONSOLE_SERIAL -DCOMCONSOLE=0x01C00000 -DCONSPEED=28800 +CFLAGS+= -DSIZEINDICATOR -DNO_DHCP_SUPPORT -DBAR_PROGRESS +#CFLAGS+= -DEMBEDDED -DMAC_HW_ADDR_DRV="'H','Y','L','N','X','1'" + diff --git a/src/arch/e1/Makefile b/src/arch/e1/Makefile new file mode 100644 index 00000000..a7c6f3b8 --- /dev/null +++ b/src/arch/e1/Makefile @@ -0,0 +1,70 @@ +ARCH_FORMAT= coff-e1 + +BUILD_ROMS= $(ROMS) +BUILD_COFFS= $(patsubst %img, %coff, $(IMGS)) +SUFFIXES+= rom zrom coff + +CC= e1-coff-gcc +AS= e1-coff-as +LD= e1-coff-ld +SIZE= e1-coff-size +AR= e1-coff-ar +RANLIB= e1-coff-ranlib +OBJCOPY=e1-coff-objcopy + +# DMAC_HW_ADDR_DRV holds the ethernet's MAC address. It is passed as +# flag to the low level driver instead of reading it from an +# external EEPROM, which we do not have! +EXTRA_CFLAGS = -DEMBEDDED -DMAC_HW_ADDR_DRV="'H','Y','L','N','X','1'" + +START= $(BIN)/start.o +START16= $(BIN)/start.o + +SRCS+= arch/e1/core/e132_xs.c +SRCS+= arch/e1/core/e1_timer.c +SRCS+= arch/e1/core/longjmp.c +SRCS+= arch/e1/core/memcmp.S +SRCS+= arch/e1/core/memcpy.S +SRCS+= arch/e1/core/memset.S +SRCS+= arch/e1/core/setjmp.c +SRCS+= arch/e1/core/strcmp.S +SRCS+= arch/e1/core/start.S + +ROMLIMIT:=3276800 + +include $(BIN)/Roms + +ROMS= $(BIN)/cs89x0.rom +IMGS= $(BIN)/cs89x0.img + +#allfiles: $(BUILD_ROMS) +all: $(BUILD_COFFS) + +BOBJS+= $(BIN)/e1_timer.o +BOBJS+= $(BIN)/memcmp.o $(BIN)/memcpy.o $(BIN)/memset.o +BOBJS+= $(BIN)/setjmp.o $(BIN)/longjmp.o +BOBJS+= $(BIN)/e132_xs.o + +# Utilities + +$(BIN)/nrv2b: util/nrv2b.c + $(HOST_CC) -O2 -DENCODE -DDECODE -DMAIN -DVERBOSE -DNDEBUG -DBITSIZE=32 -DENDIAN=0 -o $@ $< + +# Pattern Rules +# General for compiling/assembly source files +$(BIN)/cs89x0.o: drivers/net/cs89x0.c $(MAKEDEPS) + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -o $@ -c $< +# With the current tools we have problem with the compilation +# of the vsprintf file when the -O2 is selected. So we compile +# the aforemntioned file with -O1 !!! +$(BIN)/vsprintf.o: core/vsprintf.c $(MAKEDEPS) + $(CC) $(CFLAGS) -O1 -o $@ -c $< + +$(BIN)/%.o: arch/e1/core/%.c $(MAKEDEPS) + $(CC) $(CFLAGS) -o $@ -c $< + +$(BIN)/%.o: arch/e1/core/%.S $(MAKEDEPS) + $(CPP) $(CFLAGS) -D ASSEMBLY $< | $(AS) $(ASFLAGS) -o $@ + +$(BIN)/%.coff: $(BIN)/%.tmp $(MAKEDEPS) + mv $< $(BIN)/etherboot.coff diff --git a/src/arch/e1/Makefile.working b/src/arch/e1/Makefile.working new file mode 100644 index 00000000..a0ebefe5 --- /dev/null +++ b/src/arch/e1/Makefile.working @@ -0,0 +1,67 @@ +ARCH_FORMAT= coff-e1 + +CC= e1-coff-gcc +AS= e1-coff-as +LD= e1-coff-ld +SIZE= e1-coff-size +AR= e1-coff-ar +RANLIB= e1-coff-ranlib +OBJCOPY=e1-coff-objcopy + +EXTRA_CFLAGS = -DEMBEDDED -DMAC_HW_ADDR_DRV="'H','Y','L','N','X','1'" + +BUILD_ROMS= $(ROMS) +BUILD_COFFS= $(BIN)/cs89x0.coff +#BUILD_COFFS= $(patsubst %img, %coff, $(IMGS)) + +START= $(BIN)/start.o +START16= $(BIN)/start.o + +#SRCS+= arch/e1/core/coff_loader.c +SRCS+= arch/e1/core/e132_xs.c +SRCS+= arch/e1/core/e1_timer.c +SRCS+= arch/e1/core/longjmp.c +SRCS+= arch/e1/core/memcmp.S +SRCS+= arch/e1/core/memcpy.S +SRCS+= arch/e1/core/memset.S +SRCS+= arch/e1/core/setjmp.c +SRCS+= arch/e1/core/strcmp.S +SRCS+= arch/e1/core/start.S + +ROMLIMIT:=3276800 + +include $(BIN)/Roms + +hyperstone: $(BUILD_COFFS) +coff: $(BUILD_COFFS) + +BOBJS+= $(BIN)/e1_timer.o +BOBJS+= $(BIN)/memcmp.o $(BIN)/memcpy.o $(BIN)/memset.o +BOBJS+= $(BIN)/setjmp.o $(BIN)/longjmp.o +BOBJS+= $(BIN)/e132_xs.o + +# Utilities + +$(BIN)/nrv2b: util/nrv2b.c + $(HOST_CC) -O2 -DENCODE -DDECODE -DMAIN -DVERBOSE -DNDEBUG -DBITSIZE=32 -DENDIAN=0 -o $@ $< + +# Pattern Rules +# General for compiling/assembly source files + +$(BIN)/cs89x0.o: drivers/net/cs89x0.c $(MAKEDEPS) + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -o $@ -c $< +# With the current tools we have problem with the compilation +# of the vsprintf file when the -O2 is selected. So we compile +# the aforemntioned file with -O1 !!! +$(BIN)/vsprintf.o: core/vsprintf.c $(MAKEDEPS) + $(CC) $(CFLAGS) -O1 -o $@ -c $< + +$(BIN)/%.o: arch/e1/core/%.c $(MAKEDEPS) + $(CC) $(CFLAGS) -o $@ -c $< + +$(BIN)/%.o: arch/e1/core/%.S $(MAKEDEPS) + $(CPP) $(CFLAGS) -D ASSEMBLY $< | $(AS) $(ASFLAGS) -o $@ + +$(BIN)/%.coff: $(BIN)/%.tmp $(MAKEDEPS) + mv $< $(BIN)/etherboot.coff + diff --git a/src/arch/e1/README b/src/arch/e1/README new file mode 100644 index 00000000..cdf676a8 --- /dev/null +++ b/src/arch/e1/README @@ -0,0 +1,80 @@ +Introduction +--------------------- +This README file provides guideliness to compile successfully the +Etherboot for Hyperstone. +This directory (src/arch/e1) contains the files that depend on +Hyperstone's architecture. The header files that include the +configuration of the system are based on Hyperstone's E132-XS +development board. The can be very easily modified to support +anyother configuration environment. + +Software Perquisites: +--------------------- +The build environment requires a C compiler for the E1-32XS +processor. Normally you can simply install a cross-compiling tool +chain based on the GNU tools (that is binutils and gcc). If you +are running a Linux system on a x86 CPU then you can just download +the toolchain as a binary from Hyperstone's official web-site. The +binary distribution will untar the tools in the /usr/local/e1-coff +directory. On any other system you will need to build the tool +chain from the sources. + +To compile successfully the following tools should be available: + - GNU toolchain: + - GCC ver 2.95.2 20030110 (release) e1-coff-gcc -v + - LD ver 2.12.90 20020726 e1-coff-ld -V + +Hardware Perquisites: +--------------------- +The etherboot has been successfully tested in the E1-32XS +development board. A second serial device is initialized +to act as a console. The standard messages +are redirected to the console. Nevertheless, if one wants not +to use the serial console he may omit the corresponding switches +from the Config file located under "src/arch/e1/" directory. + +On the E1-32XS board that was used, a daughter-board was employed +to connect a second HyIce to the development board. Since the HyIce +embeds a standard 16550 device, the Etherboot's standard device +driver is used. + +The position of the jumpers of the development board in order to +initialize both the second HyIce and the Ethernet device is +depicted in the following table: + +Jumper: Position +------:-------------- +J3 1-2 (default) +J4 1-2 (default) +J13 5-6 +J5 1-2 (default) +J6 1-2 & 3-4 +J7 3-4 +J9 1-2 (default) +J10 1-2 +J11 3-4 + +Compilation +--------------------- +In order to compile Etherboot for Hyperstone, the following steps should be followed: +1) Edit the main Makefile (located under "src" directory") and comment-out +the ARCH variable (by putting a "#" in front of it). Append the following line: +ARCH:=e1 +2) Edit the Config file (the one located under "src" directory) and make sure that +the CFLAGS variable will contain *only* the following swithces: +CFLAGS+= -DCONFIG_ISA +CFLAGS+= -DBOOT_FIRST=BOOT_NIC +CFLAGS+= -DALLOW_ONLY_ENCAPSULATED +CFLAGS+= -DBACKOFF_LIMIT=7 -DCONGESTED +CFLAGS+= -DCOFF_IMAGE +CFLAGS+= -DDOWNLOAD_PROTO_TFTP +Please note that extra or any other switches may cause failure of compilation! +3) type "make hyperstone" or "make coff" +4) the generated file will be located under the "src/bin" directory and will be called : + "etherboot.coff". Now you may download it with usual way using e1-coff-gdb .. + +Have Fun + +Yannis Mitsos, George Thanos +{gmitsos,gthanos}@telecom.ntua.gr + diff --git a/src/arch/e1/core/coff_loader.c b/src/arch/e1/core/coff_loader.c new file mode 100644 index 00000000..d628e5ed --- /dev/null +++ b/src/arch/e1/core/coff_loader.c @@ -0,0 +1,176 @@ +/* + * Copyright 2003 Yannis Mitsos and George Thanos + * {gmitsos@gthanos}@telecom.ntua.gr + * Released under GPL2, see the file COPYING in the top directory + * COFF loader is based on the source code of the ELF loader. + * + */ +#include "coff.h" + +#define COFF_DEBUG 0 + +typedef struct { + COFF_filehdr coff32; + COFF_opthdr opthdr32; + union { + COFF_scnhdr scnhdr32[1]; + unsigned char dummy[1024]; + } p; + unsigned long curaddr; + signed int segment; /* current segment number, -1 for none */ + unsigned int loc; /* start offset of current block */ + unsigned int skip; /* padding to be skipped to current segment */ + unsigned long toread; /* remaining data to be read in the segment */ +}coff_state; + +coff_state cstate; + +static sector_t coff32_download(unsigned char *data, unsigned int len, int eof); +static inline os_download_t coff_probe(unsigned char *data, unsigned int len) +{ + unsigned long phdr_size; + + if (len < (sizeof(cstate.coff32)+ sizeof(cstate.opthdr32))) { + return 0; + } + memcpy(&cstate.coff32, data, (sizeof(cstate.coff32)+sizeof(cstate.opthdr32))); + + if ((cstate.coff32.f_magic != EM_E1) || + (cstate.opthdr32.magic != O_MAGIC)){ + return 0; + } + printf("(COFF"); + printf(")... \n"); + + if (cstate.coff32.f_opthdr == 0){ + printf("No optional header in COFF file, cannot find the entry point\n"); + return dead_download; + } + + phdr_size = cstate.coff32.f_nscns * sizeof(cstate.p.scnhdr32); + if (sizeof(cstate.coff32) + cstate.coff32.f_opthdr + phdr_size > len) { + printf("COFF header outside first block\n"); + return dead_download; + } + + memcpy(&cstate.p.scnhdr32, data + (sizeof(cstate.coff32) + cstate.coff32.f_opthdr), phdr_size); + + /* Check for Etherboot related limitations. Memory + * between _text and _end is not allowed. + * Reasons: the Etherboot code/data area. + */ + for (cstate.segment = 0; cstate.segment < cstate.coff32.f_nscns; cstate.segment++) { + unsigned long start, mid, end, istart, iend; + + if ((cstate.p.scnhdr32[cstate.segment].s_flags != S_TYPE_TEXT) && + (cstate.p.scnhdr32[cstate.segment].s_flags != S_TYPE_DATA) && + (cstate.p.scnhdr32[cstate.segment].s_flags != S_TYPE_BSS)){ /* Do we realy need to check the BSS section ? */ +#ifdef COFF_DEBUG + printf("Section <%s> in not a loadable section \n",cstate.p.scnhdr32[cstate.segment].s_name); +#endif + continue; + } + + start = cstate.p.scnhdr32[cstate.segment].s_paddr; + mid = start + cstate.p.scnhdr32[cstate.segment].s_size; + end = start + cstate.p.scnhdr32[cstate.segment].s_size; + + /* Do we need the following variables ? */ + istart = 0x8000; + iend = 0x8000; + + if (!prep_segment(start, mid, end, istart, iend)) { + return dead_download; + } +} + cstate.segment = -1; + cstate.loc = 0; + cstate.skip = 0; + cstate.toread = 0; + return coff32_download; +} + +extern int mach_boot(unsigned long entry_point); +static sector_t coff32_download(unsigned char *data, unsigned int len, int eof) +{ + unsigned long skip_sectors = 0; + unsigned int offset; /* working offset in the current data block */ + int i; + + offset = 0; + do { + if (cstate.segment != -1) { + if (cstate.skip) { + if (cstate.skip >= len - offset) { + cstate.skip -= len - offset; + break; + } + offset += cstate.skip; + cstate.skip = 0; + } + + if (cstate.toread) { + unsigned int cplen; + cplen = len - offset; + if (cplen >= cstate.toread) { + cplen = cstate.toread; + } + memcpy(phys_to_virt(cstate.curaddr), data+offset, cplen); + cstate.curaddr += cplen; + cstate.toread -= cplen; + offset += cplen; + if (cstate.toread) + break; + } + } + + /* Data left, but current segment finished - look for the next + * segment (in file offset order) that needs to be loaded. + * We can only seek forward, so select the program headers, + * in the correct order. + */ + cstate.segment = -1; + for (i = 0; i < cstate.coff32.f_nscns; i++) { + + if ((cstate.p.scnhdr32[i].s_flags != S_TYPE_TEXT) && + (cstate.p.scnhdr32[i].s_flags != S_TYPE_DATA)) + continue; + if (cstate.p.scnhdr32[i].s_size == 0) + continue; + if (cstate.p.scnhdr32[i].s_scnptr < cstate.loc + offset) + continue; /* can't go backwards */ + if ((cstate.segment != -1) && + (cstate.p.scnhdr32[i].s_scnptr >= cstate.p.scnhdr32[cstate.segment].s_scnptr)) + continue; /* search minimum file offset */ + cstate.segment = i; + } + + if (cstate.segment == -1) { + /* No more segments to be loaded, so just start the + * kernel. This saves a lot of network bandwidth if + * debug info is in the kernel but not loaded. */ + goto coff_startkernel; + break; + } + cstate.curaddr = cstate.p.scnhdr32[cstate.segment].s_paddr; + cstate.skip = cstate.p.scnhdr32[cstate.segment].s_scnptr - (cstate.loc + offset); + cstate.toread = cstate.p.scnhdr32[cstate.segment].s_size; +#if COFF_DEBUG + printf("PHDR %d, size %#lX, curaddr %#lX\n", + cstate.segment, cstate.toread, cstate.curaddr); +#endif + } while (offset < len); + + cstate.loc += len + (cstate.skip & ~0x1ff); + skip_sectors = cstate.skip >> 9; + cstate.skip &= 0x1ff; + + if (eof) { + unsigned long entry; +coff_startkernel: + entry = cstate.opthdr32.entry; + done(); + mach_boot(entry); + } + return skip_sectors; +} diff --git a/src/arch/e1/core/e132_xs.c b/src/arch/e1/core/e132_xs.c new file mode 100644 index 00000000..cc4eaae2 --- /dev/null +++ b/src/arch/e1/core/e132_xs.c @@ -0,0 +1,70 @@ +/* + * Copyright 2003 Yannis Mitsos and George Thanos + * {gmitsos@gthanos}@telecom.ntua.gr + * Released under GPL2, see the file COPYING in the top directory + * + */ +#include "hooks.h" +#include "io.h" +#include "etherboot.h" +#include "e132_xs_board.h" + +unsigned int io_periph[NR_CS] = {[0 ... NR_CS-1] = 0 }; + +/* +void arch_main(struct Elf_Bhdr *ptr __unused) +{ + +} +*/ + +void init_peripherals(void) +{ + int i; + + for(i=0; i< NR_CS; i++){ + io_periph[i]= (SLOW_IO_ACCESS | i << 22); + } + + io_periph[ETHERNET_CS] = (io_periph[ETHERNET_CS] | 1 << IOWait); + + asm volatile(" + ori SR, 0x20 + movi FCR, 0x66ffFFFF" + : + :); +} + +struct meminfo meminfo; +void get_memsizes(void) +{ +/* We initialize the meminfo structure + * according to our development board's specs + * We do not have a way to automatically probe the + * memspace instead we initialize it manually + */ + meminfo.basememsize = BASEMEM; + meminfo.memsize = SDRAM_SIZE; + meminfo.map_count = NR_MEMORY_REGNS; + + meminfo.map[0].addr = SDRAM_BASEMEM; + meminfo.map[0].size = SDRAM_SIZE; + meminfo.map[0].type = E820_RAM; + meminfo.map[1].addr = SRAM_BASEMEM; + meminfo.map[1].size = SRAM_SIZE; + meminfo.map[1].type = E820_RAM; + meminfo.map[2].addr = IRAM_BASEMEM; + meminfo.map[2].size = IRAM_SIZE; + meminfo.map[2].type = E820_RAM; +} + +int mach_boot(register unsigned long entry_point) +{ + asm volatile( + "mov PC, %0" + : /* no outputs */ + : "l" (entry_point) ); + return 0; /* We should never reach this point ! */ + +} + diff --git a/src/arch/e1/core/e1_timer.c b/src/arch/e1/core/e1_timer.c new file mode 100644 index 00000000..d3ddb644 --- /dev/null +++ b/src/arch/e1/core/e1_timer.c @@ -0,0 +1,94 @@ +/* + * Copyright 2003 Yannis Mitsos and George Thanos + * {gmitsos@gthanos}@telecom.ntua.gr + * Released under GPL2, see the file COPYING in the top directory + * + */ +#include "etherboot.h" +#include "timer.h" +#include "e132_xs_board.h" + +/* get timer returns the contents of the timer */ +static inline unsigned long get_timer(void) +{ + unsigned long result; + __asm__ __volatile__(" + ORI SR, 0x20 + mov %0, TR" + : "=l"(result)); + return result; +} + +/* ------ Calibrate the TSC ------- + * Time how long it takes to excute a loop that runs in known time. + * And find the convertion needed to get to CLOCK_TICK_RATE + */ + +static unsigned long configure_timer(void) +{ + unsigned long TPR_value; /* Timer Prescalar Value */ + + TPR_value = 0x000C00000; + + asm volatile (" + FETCH 4 + ORI SR, 0x20 + MOV TPR, %0 + ORI SR, 0x20 + MOVI TR, 0x0" + : /* no outputs */ + : "l" (TPR_value) + ); + + printf("The time prescaler register is set to: <%#x>\n",TPR_value); + return (1); +} + +static unsigned long clocks_per_tick; + +void setup_timers(void) +{ + if (!clocks_per_tick) { + clocks_per_tick = configure_timer(); + } +} + +unsigned long currticks(void) +{ + return get_timer()/clocks_per_tick; +} + +static unsigned long timer_timeout; +static int __timer_running(void) +{ + return get_timer() < timer_timeout; +} + +void udelay(unsigned int usecs) +{ + unsigned long now; + now = get_timer(); + timer_timeout = now + usecs * ((clocks_per_tick * TICKS_PER_SEC)/(1000*1000)); + while(__timer_running()); +} +void ndelay(unsigned int nsecs) +{ + unsigned long now; + now = get_timer(); + timer_timeout = now + nsecs * ((clocks_per_tick * TICKS_PER_SEC)/(1000*1000*1000)); + while(__timer_running()); +} + +void load_timer2(unsigned int timer2_ticks) +{ + unsigned long now; + unsigned long clocks; + now = get_timer(); + clocks = timer2_ticks * ((clocks_per_tick * TICKS_PER_SEC)/CLOCK_TICK_RATE); + timer_timeout = now + clocks; +} + +int timer2_running(void) +{ + return __timer_running(); +} diff --git a/src/arch/e1/core/etherboot.lds b/src/arch/e1/core/etherboot.lds new file mode 100644 index 00000000..75d2dba1 --- /dev/null +++ b/src/arch/e1/core/etherboot.lds @@ -0,0 +1,126 @@ +/* Default linker script, for normal executables */ +OUTPUT_FORMAT("coff-e1-big") +MEMORY + { + romvec : ORIGIN = 0x2000000, LENGTH = 0x0000400 + flash : ORIGIN = 0xE0000000, LENGTH = 0x20000 + eflash : ORIGIN = 0x2200000, LENGTH = 0x20000 + ram : ORIGIN = 0x00000000, LENGTH = 0x1000000 + eram16MB : ORIGIN = 0x01000000, LENGTH = 0 + sram : ORIGIN = 0x40000000, LENGTH = 0x40000 + iram : ORIGIN = 0xC0000000, LENGTH = 0x4000 + } + +SEARCH_DIR("/usr/local/e1-coff/lib"); +ENTRY(Main) +MEM0start = 0x00000000; +MEM0size = 0x40000000; +MEM1start = 0x40000000; +MEM1size = 0x40000000; +MEM2start = 0x80000000; +MEM2size = 0x40000000; +IRAMstart = 0xC0000000; +IRAMsize = 0x20000000; +MEM3start = 0xE0000000; +MEM3size = 0x20000000; +Stack1Reserve = 560; +_Stack1Size = DEFINED(_Stack1Size)? _Stack1Size : 1*1024; +_Stack2Size = DEFINED(_Stack2Size)? _Stack2Size : 16*1024; +_Stack1Base = DEFINED(_Stack1Base)? _Stack1Base : __bss_end__; +_Stack2Base = DEFINED(_Stack2Base)? _Stack2Base : __bss_end__ + _Stack1Size + Stack1Reserve; +_Mem0HeapBase = DEFINED(_Mem0HeapBase)? _Mem0HeapBase : _Stack2Base + _Stack2Size; +_Mem1HeapBase = DEFINED(_Mem1HeapBase)? _Mem1HeapBase : 0; +Priority = DEFINED(Priority) ? Priority : 31; +TextBase = DEFINED(TextBase) ? TextBase : 0xa00000; +SECTIONS +{ + .G6 (DEFINED(G6Base) ? G6Base : 0x9000) : { + *(.G6) + } + .G7 (DEFINED(G7Base) ? G7Base : 0x40001000) : { + *(.G7) + } + .G8 (DEFINED(G8Base) ? G8Base : 0xC0000000) : { + *(.G8) + } + .G9 (DEFINED(G9Base) ? G9Base : 0) : { + *(.G9) + } + .G10 (DEFINED(G10Base) ? G10Base : 0) : { + *(.G10) + } + .G11 (DEFINED(G11Base) ? G11Base : 0) : { + *(.G11) + } + .G12 (DEFINED(G12Base) ? G12Base : 0) : { + *(.G12) + } + .G13 (DEFINED(G13Base) ? G13Base : 0) : { + *(.G13) + } + + .text TextBase : { + __virt_start = .; + __text = . ; + *(.text) + *(.fini) + . = ALIGN(16); + _isa_drivers = . ; + *(.drivisa); + _isa_drivers_end = . ; + . = ALIGN(16); + + *(.init) + _etext = . ; + /* _init = DEFINED(_init) ? _init : 0; */ + /* _fini = DEFINED(_fini) ? _fini : 0; */ + /* _argc = DEFINED(_argc) ? _argc : 0; */ + /* _argv = DEFINED(_argv) ? _argv : 0; */ + /* _envp = DEFINED(_envp) ? _envp : 0; */ + /* _hwinit = DEFINED(_hwinit) ? _hwinit : 0; */ + /* _atexit = DEFINED(_atexit) ? _atexit : 0; */ + G6Size = SIZEOF(.G6); + G7Size = SIZEOF(.G7); + G8Size = SIZEOF(.G8); + G9Size = SIZEOF(.G9); + G10Size = SIZEOF(.G10); + G11Size = SIZEOF(.G11); + G12Size = SIZEOF(.G12); + G13Size = SIZEOF(.G13); + +} + + .data SIZEOF(.text) + ADDR(.text) : { + *(.data) + _edata = . ; +} + + .bss SIZEOF(.data) + ADDR(.data) : + { + __bss_start__ = ALIGN( 0x10 ) ; + __bss = . ; + *(.bss) + *(COMMON) + __end = . ; + __bss_end__ = ALIGN( 0x10 ) ; + __ebss = . ; + } + +.eram16MB : + { + ___ramend = . - 0x7000; + } > eram16MB + + .stab 0 (NOLOAD) : + { + [ .stab ] + } + .stabstr 0 (NOLOAD) : + { + [ .stabstr ] + } + _GOT_ 0 (NOLOAD) : + { + [ _GOT_ ] + } +} diff --git a/src/arch/e1/core/longjmp.c b/src/arch/e1/core/longjmp.c new file mode 100644 index 00000000..1665f07d --- /dev/null +++ b/src/arch/e1/core/longjmp.c @@ -0,0 +1,35 @@ +/* + * Copyright 2003 Yannis Mitsos and George Thanos + * {gmitsos@gthanos}@telecom.ntua.gr + * Released under GPL2, see the file COPYING in the top directory + * + */ +#include "setjmp.h" + +unsigned long jmpbuf_ptr; + +void longjmp(jmp_buf state, int value ) +{ + if(!value) + state->__jmpbuf->ReturnValue = 1; + else + state->__jmpbuf->ReturnValue = value; + + jmpbuf_ptr = (unsigned long)state; + +#define _state_ ((struct __jmp_buf_tag*)jmpbuf_ptr) + asm volatile("mov L0, %0\n\t" + "mov L1, %1\n\t" + "mov L2, %2\n\t" + "mov G3, %3\n\t" + "mov G4, %4\n\t" + "ret PC, L1\n\t" + :/*no output*/ + :"l"(_state_->__jmpbuf->ReturnValue), + "l"(_state_->__jmpbuf->SavedPC), + "l"(_state_->__jmpbuf->SavedSR), + "l"(_state_->__jmpbuf->G3), + "l"(_state_->__jmpbuf->G4) + :"%G3", "%G4", "%L0", "%L1" ); +#undef _state_ +} diff --git a/src/arch/e1/core/memcmp.S b/src/arch/e1/core/memcmp.S new file mode 100644 index 00000000..2347317a --- /dev/null +++ b/src/arch/e1/core/memcmp.S @@ -0,0 +1,54 @@ +/* + * Derived from the Hyperstone's library source code. + * Modefied src in order to apply the -mgnu-param compiler option. + * Copyright (C) 2002-2003 GDT, Yannis Mitsos <gmitsos@telecom.ntua.gr> + * George Thanos <gthanos@telecom.ntua.gr> + */ + .text + .align 2 + .global _memcmp + +;ENTRY (_memcmp) +_memcmp: + FRAME L9, L3 # get incoming parameters + CMPBI L2,3 # check word alignment + BNZ byte_compare + CMPBI L1,3 # check word alignment + BNZ byte_compare + +double_compare: + ADDI L0, -8 + BLT is_equal + LDD.P L1, L5 + LDD.P L2, L7 + SUB L5, L7 + DBNZ corr_8 + SUB L6, L8 + BZ double_compare + ADDI L0, 4 + ADDI L2, -4 + ADDI L1, -4 + BR byte_compare + +corr_8: ADDI L0, 8 + ADDI L2, -8 + ADDI L1, -8 +byte_compare: + ADDI L0, -1 + BLT equal + LDBU.N L2, L5, 1 # Load and compare bytes + LDBU.N L1, L6, 1 + SUB L5, L6 + BZ byte_compare + MOV L2, L5 + RET PC, L3 + +is_equal: CMPI L0, -8 + DBNE byte_compare + ADDI L0, 8 +equal: + MOVI L2, 0 + RET PC, L3 + + .END + diff --git a/src/arch/e1/core/memcpy.S b/src/arch/e1/core/memcpy.S new file mode 100644 index 00000000..0581c9d1 --- /dev/null +++ b/src/arch/e1/core/memcpy.S @@ -0,0 +1,79 @@ +/* + * Derived from the Hyperstone's library source code. + * Modefied src in order to apply the -mgnu-param compiler option. + * Copyright (C) 2002-2003 GDT, Yannis Mitsos <gmitsos@telecom.ntua.gr> + * George Thanos <gthanos@telecom.ntua.gr> + */ + .text + .align 2 + .global _memcpy +;ENTRY(_memcpy) +_memcpy: + FRAME L8, L3 + MOV L7, L2 # Save for return + +#***************************** +# Perform byte copy if both +# not on a word alignment +#***************************** + CMPBI L2, 3 # check word alignment + BNZ mem_except + CMPBI L1, 3 # check word alignment + BNZ mem_except + +#***************************** +# Copy Double,Word,Halfword, +# then byte +#***************************** +DBL_LOOP: + CMPI L0, 8 # Copy Doubles + BLT DO_WORD + LDD.P L1, L5 + ADDI L0, -8 + DBR DBL_LOOP + STD.P L2, L5 + +DO_WORD: + CMPI L0, 4 # Copy leftover word + BLT DO_HALF + LDW.P L1, L5 + ADDI L0, -4 + DBZ DONE # Done if L0 is 0 + STW.P L2, L5 + +DO_HALF: + CMPI L0, 2 # Copy leftover byte + BLT DO_BYTE + LDHU.N L1, L5, 2 + ADDI L0, -2 + DBZ DONE # Done if L0 is 0 + STHU.N L2, L5, 2 + +DO_BYTE: + CMPI L0, 1 # Copy leftover byte + BLT DONE + LDBU.D L1, L5, 0 + STBU.D L2, L5, 0 + +DONE: # Copy done + MOV L2, L7 # Return pointer + RET PC, L3 + +#**************************** +# Byte memcpy +#**************************** +mem_except: + DBR L_5 + MOVI L6,0 +L_3: + LDBS.D L1, L5, 0 # Transfer the byte + ADDI L6, 1 + STBS.D L2, L5, 0 + ADDI L2, 1 + ADDI L1, 1 +L_5: # Loop test + CMP L6, L0 + BST L_3 + MOV L2, L7 # Return pointer + RET PC, L3 + .END diff --git a/src/arch/e1/core/memset.S b/src/arch/e1/core/memset.S new file mode 100644 index 00000000..a8ceb535 --- /dev/null +++ b/src/arch/e1/core/memset.S @@ -0,0 +1,47 @@ +/* + * Derived from the Hyperstone's library source code. + * Modefied src in order to apply the -mgnu-param compiler option. + * Copyright (C) 2002-2003 GDT, Yannis Mitsos <gmitsos@telecom.ntua.gr> + * George Thanos <gthanos@telecom.ntua.gr> + */ + .text + .align 2 + .global _memset + +;ENTRY(_memset) +_memset: FRAME L9, L3 + MASK L5, L1, 0xFF + MOV L8, L2 + CMPI L0, 0 # if n = 0 then return + BE retour + +loop0: CMPBI L8, 0x3 + BZ word_bound + ADDI L0, -1 + DBNZ loop0 + STBU.N L8, L5, 1 +retour: RET PC, L3 + +word_bound: + CMPI L0, 8 + DBLT loop2 + MOV L7, L5 + SHLI L7, 8 + OR L5, L7 + MOV L7, L5 + SHLI L7, 16 + OR L5, L7 + MOV L6, L5 +loop1: ADDI L0, -8 + CMPI L0, 8 + DBGE loop1 + STD.P L8, L5 + CMPI L0, 0 + DBNZ loop2 + ANDNI L5, ~ 0xFF + RET PC, L3 + +loop2: ADDI L0, -1 + DBNZ loop2 + STBU.N L8, L5, 1 + RET PC, L3 diff --git a/src/arch/e1/core/setjmp.c b/src/arch/e1/core/setjmp.c new file mode 100644 index 00000000..63bcc484 --- /dev/null +++ b/src/arch/e1/core/setjmp.c @@ -0,0 +1,26 @@ +/* + * Copyright 2003 Yannis Mitsos and George Thanos + * {gmitsos@gthanos}@telecom.ntua.gr + * Released under GPL2, see the file COPYING in the top directory + * + */ +#include "setjmp.h" + +int setjmp( jmp_buf state) +{ + asm volatile( "mov %0, G3\n\t" + "mov %1, G4\n\t" + :"=l"(state->__jmpbuf->G3), + "=l"(state->__jmpbuf->G4) + :/*no input*/ + :"%G3", "%G4" ); + + asm volatile( "setadr %0\n\t" + "mov %1, L1\n\t" + "mov %2, L2\n\t" + :"=l"(state->__jmpbuf->SavedSP), + "=l"(state->__jmpbuf->SavedPC), + "=l"(state->__jmpbuf->SavedSR) + :/*no input*/); + return 0; +} diff --git a/src/arch/e1/core/start.S b/src/arch/e1/core/start.S new file mode 100644 index 00000000..691f39bd --- /dev/null +++ b/src/arch/e1/core/start.S @@ -0,0 +1,111 @@ +/* + * Derived from the Hyperstone's library source code. + * Copyright (C) 2002-2003 GDT, Yannis Mitsos <gmitsos@telecom.ntua.gr> + * George Thanos <gthanos@telecom.ntua.gr> + */ + .global Priority ; Task-Priority + .global _Stack1Size ; Size of hardware stack + .global _Stack2Size ; Size of aggregate stack + .global _Stack1Base ; Base of hardware stack + .global _Stack2Base ; Base of aggregate stack + .global _Mem0HeapBase ; Base of Heap in Mem0 + .global _Mem1HeapBase ; Base of Heap in Mem1 + + .global _init_peripherals + .global _main + .global Main + + .global __exit + .global __fmode + .global __MaxArgCount + + .text +BasePtrs: + .weak G6Base,G7Base,G8Base,G9Base,G10Base,G11Base,G12Base,G13Base + .long G6Base,G7Base,G8Base,G9Base,G10Base,G11Base,G12Base,G13Base +BasePtrsEnd: +HeapPtrs: + .long _Mem0HeapBase + .long _Mem1HeapBase +HeapPtrsEnd: + +__MaxArgCount: + .long 32 +__StandAloneMode: + .long 0 ; 0 indicate stand alone mode + +;============================================================================; +; Startup-Code ; +;============================================================================; + .data + +; do not change the order of: __argc,.. +__argc: + .long 0 +__argv: + .long 0 +__IsShell: + .long 1 +ErrorLevel: + .long 0 +__lab: + .long 0 +__fmode: + .long 0x4000 ; O_TEXT attribute + +_isa_drivers: + .long 0 + +_isa_drivers_end: + .long 0 + + + .text +Main: +StartUp: + FRAME L5, L0 + MOVI L2, __bss_start__ ; clear the .bss segment +0: CMPI L2, __bss_end__ + BHE 0f + DBR 0b + STW.P L2, 0 +0: SUM L2, PC, BasePtrs-$ ; Load BasePtrs G6-G13 + LDD.P L2, G6 + LDD.P L2, G8 +; LDD.P L2, G10 + LDD.P L2, G12 + MOVI L2, 1 + SUM L3, PC, __StandAloneMode-$ + STW.R L3, L2 + + ;----------------------------------------------------------------; + ; Call main C function ; + ;----------------------------------------------------------------; +2: LDW.D PC, L2, __argc - $ ; pass count of arguments to main + LDW.D PC, L3, __argv - $ ; pass pointer array to main + CALL L4, PC, _init_peripherals - $ + CALL L4, PC, _main - $ ; --> Call Main-Program + CHK PC, PC ; trap if execution arrives here + +__exit: + FRAME L5, L1 + STW.D PC, L0, ErrorLevel - $ ; Store ERRORLEVEL + + CHK PC, PC + RET PC, L1 + + .global ___main +___main: + FRAME L4, L1 + MOVI L3, 2 + STW.D PC, L3, __StandAloneMode-$ + RET PC, L1 ; does not return + + .section _GOT_ + .long Main+4 ; OnCreate + .long Main+8 ; OnError + .long BasePtrs ; G6 + .long BasePtrs+4 ; G7 + .long BasePtrs+8 ; G8 + + .END diff --git a/src/arch/e1/core/strcmp.S b/src/arch/e1/core/strcmp.S new file mode 100644 index 00000000..5f09daa7 --- /dev/null +++ b/src/arch/e1/core/strcmp.S @@ -0,0 +1,76 @@ +/* + * Derived from the Hyperstone's library source code. + * Modefied src in order to apply the -mgnu-param compiler option. + * Copyright (C) 2002-2003 GDT, Yannis Mitsos <gmitsos@telecom.ntua.gr> + * George Thanos <gthanos@telecom.ntua.gr> + */ + .text + .align 2 + .global _strcmp +;ENTRY(_strcmp) +_strcmp: + FRAME L8,L2 + CMPBI L1, 3 # check for word alignment + BNZ str_except + CMPBI L0, 3 # check for word alignment + BNZ str_except + +start: + LDD.P L1, L4 # post inc mode + LDD.P L0, L6 # post inc mode + CMPBI L4, ANYBZ + BE correct1 + CMP L4, L6 + BNE correct1 + CMP L5, L7 + BNE correct + CMPBI L5, ANYBZ + BE correct + CMPBI L6, ANYBZ + BE correct1 + CMPBI L7, ANYBZ + BNE start + +correct: MASK L4, L5, 0xff000000 + MASK L6, L7, 0xff000000 + CMP L4, L6 + BNE Exit + SHLI L5, 8 + CMPI L4, 0 + DBNE correct + SHLI L7, 8 + MOV L1, L4 + RET PC, L2 + +Exit: SUB L4, L6 # Subtract chars + SARI L4, 24 + MOV L1, L4 + RET PC, L2 + +correct1: MASK L5, L4, 0xff000000 + MASK L7, L6, 0xff000000 + CMP L5, L7 + BNE Exit1 + SHLI L4, 8 + CMPI L5, 0 + DBNE correct1 + SHLI L6, 8 + MOV L1, L5 + RET PC, L2 +Exit1: SUB L5, L7 # Subtract chars + SARI L5, 24 + MOV L1, L5 + RET PC, L2 + +testzero: CMPI L4, 0 + BE L_5 +str_except: + LDBU.N L1, L4, 1 # Load *s1, compare bytes + LDBU.N L0, L5, 1 # Load *s2, compare bytes + CMP L4, L5 + BE testzero + SUB L4, L5 # Subtract chars +L_5: MOV L1, L4 + RET PC, L2 + .END + diff --git a/src/arch/e1/include/bits/byteswap.h b/src/arch/e1/include/bits/byteswap.h new file mode 100644 index 00000000..1d1a7d2f --- /dev/null +++ b/src/arch/e1/include/bits/byteswap.h @@ -0,0 +1,39 @@ +#ifndef ETHERBOOT_BITS_BYTESWAP_H +#define ETHERBOOT_BITS_BYTESWAP_H + +/* We do not have byte swap functions ... We are + * RISC processor ... + */ + +static inline unsigned short __swap16(volatile unsigned short v) +{ + return ((v << 8) | (v >> 8)); +} + +static inline unsigned int __swap32(volatile unsigned long v) +{ + return ((v << 24) | ((v & 0xff00) << 8) | ((v & 0xff0000) >> 8) | (v >> 24)); +} + +#define __bswap_constant_16(x) \ + ((uint16_t)((((uint16_t)(x) & 0x00ff) << 8) | \ + (((uint16_t)(x) & 0xff00) >> 8))) + +#define __bswap_constant_32(x) \ + ((uint32_t)((((uint32_t)(x) & 0x000000ffU) << 24) | \ + (((uint32_t)(x) & 0x0000ff00U) << 8) | \ + (((uint32_t)(x) & 0x00ff0000U) >> 8) | \ + (((uint32_t)(x) & 0xff000000U) >> 24))) + +#define __bswap_16(x) \ + (__builtin_constant_p(x) ? \ + __bswap_constant_16(x) : \ + __swap16(x)) + + +#define __bswap_32(x) \ + (__builtin_constant_p(x) ? \ + __bswap_constant_32(x) : \ + __swap32(x)) + +#endif /* ETHERBOOT_BITS_BYTESWAP_H */ diff --git a/src/arch/e1/include/bits/cpu.h b/src/arch/e1/include/bits/cpu.h new file mode 100644 index 00000000..f25c009a --- /dev/null +++ b/src/arch/e1/include/bits/cpu.h @@ -0,0 +1,6 @@ +#ifndef E1_BITS_CPU_H +#define E1_BITS_CPU_H + +#define cpu_setup() do {} while(0) + +#endif /* E1_BITS_CPU_H */ diff --git a/src/arch/e1/include/bits/elf.h b/src/arch/e1/include/bits/elf.h new file mode 100644 index 00000000..aa40e110 --- /dev/null +++ b/src/arch/e1/include/bits/elf.h @@ -0,0 +1,6 @@ +#ifndef E1_BITS_ELF_H +#define E1_BITS_ELF_H + +/* dummy file, needed for the compilation of core/nic.c */ + +#endif /* E1_BITS_ELF_H */ diff --git a/src/arch/e1/include/bits/endian.h b/src/arch/e1/include/bits/endian.h new file mode 100644 index 00000000..4145518b --- /dev/null +++ b/src/arch/e1/include/bits/endian.h @@ -0,0 +1,6 @@ +#ifndef ETHERBOOT_BITS_ENDIAN_H +#define ETHERBOOT_BITS_ENDIAN_H + +#define __BYTE_ORDER __BIG_ENDIAN + +#endif /* ETHERBOOT_BITS_ENDIAN_H */ diff --git a/src/arch/e1/include/bits/string.h b/src/arch/e1/include/bits/string.h new file mode 100644 index 00000000..b6df2fcb --- /dev/null +++ b/src/arch/e1/include/bits/string.h @@ -0,0 +1,35 @@ +#ifndef ETHERBOOT_BITS_STRING_H +#define ETHERBOOT_BITS_STRING_H + +/* define inline optimized string functions here */ + +#define __HAVE_ARCH_MEMCPY +//extern void * memcpy(const void *d, const void *s, size_t count); + +#define __HAVE_ARCH_MEMCMP +//extern int memcmp(const void * s ,const void * d ,size_t ); + +#define __HAVE_ARCH_MEMSET +//extern void * memset(const void * s, int c, size_t count); + +#define __HAVE_ARCH_MEMMOVE +static inline void *memmove(void *s1, const void *s2, size_t n) { + + unsigned int i; + char *tmp = s1; + char *cs2 = (char *) s2; + + if (tmp < cs2) { + for(i=0; i<n; ++i, ++tmp, ++cs2) + *tmp = *cs2; + } + else { + tmp += n - 1; + cs2 += n - 1; + for(i=0; i<n; ++i, --tmp, --cs2) + *tmp = *cs2; + } + return(s1); +} + +#endif /* ETHERBOOT_BITS_STRING_H */ diff --git a/src/arch/e1/include/e132_xs_board.h b/src/arch/e1/include/e132_xs_board.h new file mode 100644 index 00000000..257cfc37 --- /dev/null +++ b/src/arch/e1/include/e132_xs_board.h @@ -0,0 +1,22 @@ +#ifndef __E132_XS_BOARD_H +#define __E132_XS_BOARD_H + +#define CONFIG_HYPERSTONE_OSC_FREQ_MHZ 15 + +#define NR_MEMORY_REGNS 3 +#define BASEMEM 0x0 + +/* SDRAM mapping */ +#define SDRAM_SIZE 0x01000000 +#define SDRAM_BASEMEM BASEMEM + +/* SRAM mapping */ +#define SRAM_BASEMEM 0x40000000 +#define SRAM_SIZE 0x0003FFFF + +/* IRAM mapping */ +#define IRAM_BASEMEM 0xC0000000 +#define IRAM_SIZE 0x00003FFF + + +#endif /* __E132_XS_BOARD_H */ diff --git a/src/arch/e1/include/hooks.h b/src/arch/e1/include/hooks.h new file mode 100644 index 00000000..a67aa193 --- /dev/null +++ b/src/arch/e1/include/hooks.h @@ -0,0 +1,9 @@ +#ifndef ETHERBOOT_E1_HOOKS_H +#define ETHERBOOT_E1_HOOKS_H + +#define arch_main(data,params) do {} while(0) +#define arch_on_exit(status) do {} while(0) +#define arch_relocate_to(addr) do {} while(0) +#define arch_relocated_from(old_addr) do {} while(0) + +#endif /* ETHERBOOT_E1_HOOKS_H */ diff --git a/src/arch/e1/include/io.h b/src/arch/e1/include/io.h new file mode 100644 index 00000000..acf940ce --- /dev/null +++ b/src/arch/e1/include/io.h @@ -0,0 +1,210 @@ +#ifndef ETHERBOOT_IO_H +#define ETHERBOOT_IO_H + +/* Don't require identity mapped physical memory, + * osloader.c is the only valid user at the moment. + */ +#if 0 +static inline unsigned long virt_to_phys(volatile const void *virt_addr) +{ + return ((unsigned long)virt_addr); +} +#else +#define virt_to_phys(vaddr) ((unsigned long) (vaddr)) +#endif + +#if 0 +static inline void *phys_to_virt(unsigned long phys_addr) +{ + return (void *)(phys_addr); +} +#else +#define phys_to_virt(vaddr) ((void *) (vaddr)) +#endif + +/* virt_to_bus converts an addresss inside of etherboot [_start, _end] + * into a memory address cards can use. + */ +#define virt_to_bus virt_to_phys + +/* bus_to_virt reverses virt_to_bus, the address must be output + * from virt_to_bus to be valid. This function does not work on + * all bus addresses. + */ +#define bus_to_virt phys_to_virt + +#define iounmap(addr) ((void)0) +#define ioremap(physaddr, size) (physaddr) + +#define IORegAddress 13 +#define IOWait 11 +#define IOSetupTime 8 +#define IOAccessTime 5 +#define IOHoldTime 3 + +#define SLOW_IO_ACCESS ( 0x3 << IOSetupTime | 0x0 << IOWait | 7 << IOAccessTime | 3 << IOHoldTime ) + +/* The development board can generate up to 15 Chip selects */ +#define NR_CS 16 + +extern unsigned int io_periph[NR_CS]; +#define ETHERNET_CS 4 + +static inline unsigned short _swapw(volatile unsigned short v) +{ + return ((v << 8) | (v >> 8)); +} + +static inline unsigned int _swapl(volatile unsigned long v) +{ + return ((v << 24) | ((v & 0xff00) << 8) | ((v & 0xff0000) >> 8) | (v >> 24)); +} + +#define hy_inpw(addr) \ + ({ register unsigned long dummy, dummy1; \ + dummy = addr; \ + asm volatile ("LDW.IOD %1, %0, 0" \ + : "=l" (dummy1) \ + : "l" (dummy)); dummy1; }) + + +#define hy_outpw(x, addr) \ + ({ register unsigned long dummy0,dummy1; \ + dummy0 = addr; \ + dummy1 = x; \ + asm volatile ("STW.IOD %1, %0, 0" \ + : "=l" (dummy1) \ + : "l"(dummy0), "l" (dummy1)); dummy1; }) + +#define readb(addr) ({ unsigned char __v = inregb(addr); __v; }) +#define readw(addr) ({ unsigned short __v = inregw(addr); __v; }) +#define readl(addr) ({ unsigned long __v = inregl(addr); __v; }) + +#define writeb(b,addr) (void)(outreg(b, addr)) +#define writew(b,addr) (void)(outreg(b, addr)) +#define writel(b,addr) (void)(outreg(b, addr)) + +static inline unsigned long common_io_access(unsigned long addr) +{ + return io_periph[(addr & 0x03C00000) >> 22]; +} + +static inline volatile unsigned char inregb(volatile unsigned long reg) +{ + unsigned char val; + + val = hy_inpw(common_io_access(reg) | ((0xf & reg) << IORegAddress)); + return val; +} + +static inline volatile unsigned short inregw(volatile unsigned long reg) +{ + unsigned short val; + + val = hy_inpw(common_io_access(reg) | ((0xf & reg) << IORegAddress)); + return val; +} + +static inline volatile unsigned long inregl(volatile unsigned long reg) +{ + unsigned long val; + + val = hy_inpw(common_io_access(reg) | ((0xf & reg) << IORegAddress)); + return val; +} + +static inline void outreg(volatile unsigned long val, volatile unsigned long reg) +{ + + hy_outpw(val, (common_io_access(reg) | ((0xf & reg) << IORegAddress))); +} + +static inline void io_outsb(unsigned int addr, void *buf, int len) +{ + unsigned long tmp; + unsigned char *bp = (unsigned char *) buf; + + tmp = (common_io_access(addr)) | ((0xf & addr) << IORegAddress); + + while (len--){ + hy_outpw(_swapw(*bp++), tmp); + } +} + +static inline void io_outsw(volatile unsigned int addr, void *buf, int len) +{ + unsigned long tmp; + unsigned short *bp = (unsigned short *) buf; + + tmp = (common_io_access(addr)) | ((0xf & addr) << IORegAddress); + + while (len--){ + hy_outpw(_swapw(*bp++), tmp); + } +} + +static inline void io_outsl(volatile unsigned int addr, void *buf, int len) +{ + unsigned long tmp; + unsigned int *bp = (unsigned int *) buf; + + tmp = (common_io_access(addr)) | ((0xf & addr) << IORegAddress); + + while (len--){ + hy_outpw(_swapl(*bp++), tmp); + } +} + +static inline void io_insb(volatile unsigned int addr, void *buf, int len) +{ + unsigned long tmp; + unsigned char *bp = (unsigned char *) buf; + + tmp = (common_io_access(addr)) | ((0xf & addr) << IORegAddress); + + while (len--) + *bp++ = hy_inpw((unsigned char) tmp); + +} + +static inline void io_insw(unsigned int addr, void *buf, int len) +{ + unsigned long tmp; + unsigned short *bp = (unsigned short *) buf; + + tmp = (common_io_access(addr)) | ((0xf & addr) << IORegAddress); + + while (len--) + *bp++ = _swapw((unsigned short)hy_inpw(tmp)); + +} + +static inline void io_insl(unsigned int addr, void *buf, int len) +{ + unsigned long tmp; + unsigned int *bp = (unsigned int *) buf; + + tmp = (common_io_access(addr)) | ((0xf & addr) << IORegAddress); + + while (len--) + *bp++ = _swapl((unsigned int)hy_inpw(tmp)); +} + +#define inb(addr) readb(addr) +#define inw(addr) readw(addr) +#define inl(addr) readl(addr) +#define outb(x,addr) ((void) writeb(x,addr)) +#define outw(x,addr) ((void) writew(x,addr)) +#define outl(x,addr) ((void) writel(x,addr)) + +#define insb(a,b,l) io_insb(a,b,l) +#define insw(a,b,l) io_insw(a,b,l) +#define insl(a,b,l) io_insl(a,b,l) +#define outsb(a,b,l) io_outsb(a,b,l) +#define outsw(a,b,l) io_outsw(a,b,l) +#define outsl(a,b,l) io_outsl(a,b,l) + +#define memcpy_fromio(a,b,c) memcpy((a),(void *)(b),(c)) +#define memcpy_toio(a,b,c) memcpy((void *)(a),(b),(c)) + +#endif /* ETHERBOOT_IO_H */ diff --git a/src/arch/e1/include/latch.h b/src/arch/e1/include/latch.h new file mode 100644 index 00000000..0ee6fb2a --- /dev/null +++ b/src/arch/e1/include/latch.h @@ -0,0 +1,12 @@ +#ifndef LATCH_H +#define LATCH_H + +//#define TICKS_PER_SEC (1000000UL) +#define TICKS_PER_SEC (625000UL) + +/* Fixed timer interval used for calibrating a more precise timer */ +//#define LATCHES_PER_SEC 10 + +void sleep_latch(void); + +#endif /* LATCH_H */ diff --git a/src/arch/e1/include/limits.h b/src/arch/e1/include/limits.h new file mode 100644 index 00000000..e7056ce5 --- /dev/null +++ b/src/arch/e1/include/limits.h @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------*/ +/* Project: ANSI C Standard Header Files */ +/* File: LIMITS.H */ +/* Edited by: hyperstone electronics GmbH */ +/* Am Seerhein 8 */ +/* D-78467 Konstanz, Germany */ +/* Date: January 30, 1996 */ +/*--------------------------------------------------------------------------*/ +/* Purpose: */ +/* The header file <limits.h> defines limits of ordinal types */ +/* (char, short, int, long) */ +/*--------------------------------------------------------------------------*/ + +#ifndef __LIMITS_H +#define __LIMITS_H 1 + +#define MB_LEN_MAX 1 +#define CHAR_BIT 8 +#define SCHAR_MIN -128L +#define SCHAR_MAX 127L +#define UCHAR_MAX 255 +#define CHAR_MIN 0 +#define CHAR_MAX UCHAR_MAX +#define SHRT_MIN -32768 +#define SHRT_MAX 32767 +#define USHRT_MAX 65535 +#define INT_MIN 0x80000000 +#define INT_MAX 0x7FFFFFFF +#define UINT_MAX 0xFFFFFFFFL +#define LONG_MIN INT_MIN +#define LONG_MAX INT_MAX +#define ULONG_MAX UINT_MAX + +#endif diff --git a/src/arch/e1/include/setjmp.h b/src/arch/e1/include/setjmp.h new file mode 100644 index 00000000..ef401b62 --- /dev/null +++ b/src/arch/e1/include/setjmp.h @@ -0,0 +1,23 @@ +#ifndef _SETJMP_H +#define _SETJMP_H + + +typedef struct { + unsigned long G3; + unsigned long G4; + unsigned long SavedSP; + unsigned long SavedPC; + unsigned long SavedSR; + unsigned long ReturnValue; +} __jmp_buf[1]; + +typedef struct __jmp_buf_tag /* C++ doesn't like tagless structs. */ + { + __jmp_buf __jmpbuf; /* Calling environment. */ + int __mask_was_saved; /* Saved the signal mask? */ + } jmp_buf[1]; + +void longjmp(jmp_buf state, int value ); +int setjmp( jmp_buf state); + +#endif diff --git a/src/arch/e1/include/stdint.h b/src/arch/e1/include/stdint.h new file mode 100644 index 00000000..8a7ad978 --- /dev/null +++ b/src/arch/e1/include/stdint.h @@ -0,0 +1,16 @@ +#ifndef STDINT_H +#define STDINT_H + +typedef unsigned long size_t; + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned long uint32_t; +typedef unsigned long long uint64_t; + +typedef signed char int8_t; +typedef signed short int16_t; +typedef signed int int32_t; +typedef signed long long int64_t; + +#endif /* STDINT_H */ diff --git a/src/arch/i386/Config b/src/arch/i386/Config new file mode 100644 index 00000000..e8ac91b8 --- /dev/null +++ b/src/arch/i386/Config @@ -0,0 +1,131 @@ +# Config for i386 Etherboot +# +# Do not delete the tag OptionDescription and /OptionDescription +# It is used to automatically generate the documentation. +# +# @OptionDescrition@ +# +# BIOS interface options: +# +# -DPCBIOS +# Compile in support for the normal pcbios +# -DLINUXBIOS +# Compile in support for LinuxBIOS +# -DBBS_BUT_NOT_PNP_COMPLIANT +# Some BIOSes claim to be PNP but they don't conform +# to the BBS spec which specifies that ES:DI must +# point to the string $PnP on entry. This option +# works around those. This option must be added to +# LCONFIG. +# -DNO_DELAYED_INT +# Take control as soon as BIOS detects the ROM. +# Normally hooks onto INT18H or INT19H. Use only if you +# have a very non-conformant BIOS as it bypasses +# BIOS initialisation of devices. This only works for +# legacy ROMs, i.e. PCI_PNP_HEADER not defined. +# This option was formerly called NOINT19H. +# -DBOOT_INT18H +# Etherboot normally hooks onto INT19H for legacy ROMs. +# You can choose to hook onto INT18H (BASIC interpreter +# entry point) instead. This entry point is used when +# all boot devices have been exhausted. This option must +# be added to LCONFIG. +# -DCONFIG_PCI_DIRECT +# Define this for PCI BIOSes that do not implement +# BIOS32 or not correctly. Normally not needed. +# Only works for BIOSes of a certain era. +# -DCONFIG_TSC_CURRTICKS +# Uses the processor time stamp counter instead of reading +# the BIOS time counter. This allows Etherboot to work +# even without a BIOS. This only works on late model +# 486s and above. +# -DCONFIG_NO_TIMER2 +# Some systems do not have timer2 implemented. +# If you have a RTC this will allow you to roughly calibrate +# it using outb instructions. +# -DIBM_L40 +# This option uses the 0x92 method of controlling +# A20 instead of the traditional method of using the +# keyboard controller. An explanation of A20 is here: +# http://www.win.tue.nl/~aeb/linux/kbd/A20.html +# This occurs on MCA, EISA and some embedded boards, +# and sometimes with the Fast Gate A20 option on some +# BIOSes. +# Enable this only if you are sure of what you are doing. +# +# Extended cpu options + +# -DCONFIG_X86_64 +# Compile in support for booting x86_64 64bit binaries. +# +# PXE loader options: +# +# -DPXELOADER_KEEP_ALL +# Prevent PXE loader (prefix) from unloading the +# PXE stack. You will want to use this if, for +# example, you are booting via PXE-on-floppy. +# You may want to use it under certain +# circumstances when using the Etherboot UNDI +# driver; these are complex and best practice is +# not yet established. +# +# Obscure options you probably don't need to touch: +# +# -DIGNORE_E820_MAP +# Ignore the memory map returned by the E820 BIOS +# call. May be necessary on some buggy BIOSes. +# -DT503_AUI +# Use AUI by default on 3c503 cards. +# -DFLATTEN_REAL_MODE +# Use 4GB segment limits when calling out to or +# returning to real-mode code. This is necessary to +# work around some buggy code (e.g. OpenBSD's pxeboot) +# that uses flat real-mode without being sufficiently +# paranoid about the volatility of its segment limits. + +# +# @/OptionDescription@ + +# BIOS select don't change unless you know what you are doing +CFLAGS+= -DPCBIOS + +# Compile in k8/hammer support +# CFLAGS+= -DCONFIG_X86_64 + +# Options to make a version of Etherboot that will work under linuxBIOS. +# CFLAGS+= -DLINUXBIOS -DCONFIG_TSC_CURRTICKS -DCONSOLE_SERIAL -DCOMCONSOLE=0x3f8 -DCOMPRESERVE -DCONFIG_PCI_DIRECT -DELF_IMAGE + +# These options affect the loader that is prepended to the Etherboot image +# LCONFIG+= -DBBS_BUT_NOT_PNP_COMPLIANT +# LCONFIG+= -DBOOT_INT18H + +# Produce code that will work inside the Bochs emulator. The pnic +# driver is probably the best one to try. +# CFLAGS+= -DCONFIG_PCI_DIRECT + +# Produce code that will work with OpenBSD's pxeboot +# CFLAGS+= -DFLATTEN_REAL_MODE + +CFLAGS+= -fstrength-reduce -fomit-frame-pointer -march=i386 +# Squeeze the code in as little space as possible. +# gcc3 needs a different syntax to gcc2 if you want to avoid spurious warnings. +GCC_VERSION = $(subst ., ,$(shell $(CC) -dumpversion)) +GCC_MAJORVERSION = $(firstword $(GCC_VERSION)) +ifeq ($(GCC_MAJORVERSION),2) +CFLAGS+= -malign-jumps=1 -malign-loops=1 -malign-functions=1 +else +CFLAGS+= -falign-jumps=1 -falign-loops=1 -falign-functions=1 +endif +GCC_MINORVERSION = $(word 2, $(GCC_VERSION)) +ifneq ($(GCC_MINORVERSION),4) +CFLAGS+= -mcpu=i386 +endif + +LDFLAGS+= -N + +ifeq "$(shell uname -s)" "FreeBSD" +CFLAGS+= -DIMAGE_FREEBSD -DELF_IMAGE -DAOUT_IMAGE +endif + +# An alternate location for isolinux.bin can be set here +# ISOLINUX_BIN=/path/to/isolinux.bin diff --git a/src/arch/i386/Makefile b/src/arch/i386/Makefile new file mode 100644 index 00000000..cded46c3 --- /dev/null +++ b/src/arch/i386/Makefile @@ -0,0 +1,373 @@ +ARCH_FORMAT= elf32-i386 + +# For debugging, don't delete intermediates +#.SECONDARY: + +LDSCRIPT= arch/i386/core/etherboot.lds +PLDSCRIPT= arch/i386/core/etherboot.prefix.lds + +LCONFIG+= -Ui386 + +ROMLIMIT= 524288 +CHECKSIZE= { read d1; read d1 d2 d3 size d4; [ $$size -gt $(ROMLIMIT) ] &&\ + { $(RM) $@; echo "ERROR: code size exceeds limit!"; exit 1; }; exit 0; } + +START= $(BIN)/start32.o $(BIN)/linuxbios.o \ + $(BIN)/bios.o $(BIN)/console.o $(BIN)/memsizes.o $(BIN)/basemem.o \ + $(BIN)/hidemem.o $(BIN)/e820mangler.o \ + $(BIN)/realmode.o $(BIN)/realmode_asm.o \ + $(BIN)/callbacks.o $(BIN)/pxe_callbacks.o + +SRCS+= arch/i386/prefix/floppyprefix.S +SRCS+= arch/i386/prefix/unhuf.S +SRCS+= arch/i386/prefix/unnrv2b.S +SRCS+= arch/i386/firmware/pcbios/bios.c +SRCS+= arch/i386/firmware/pcbios/console.c +SRCS+= arch/i386/firmware/pcbios/memsizes.c +SRCS+= arch/i386/firmware/pcbios/basemem.c +SRCS+= arch/i386/firmware/pcbios/hidemem.c +SRCS+= arch/i386/firmware/pcbios/e820mangler.S +SRCS+= arch/i386/prefix/liloprefix.S +SRCS+= arch/i386/prefix/elfprefix.S +SRCS+= arch/i386/prefix/lmelf_prefix.S +SRCS+= arch/i386/prefix/elf_dprefix.S +SRCS+= arch/i386/prefix/lmelf_dprefix.S +SRCS+= arch/i386/prefix/comprefix.S +SRCS+= arch/i386/prefix/exeprefix.S +SRCS+= arch/i386/prefix/pxeprefix.S +SRCS+= arch/i386/prefix/romprefix.S + +SRCS+= arch/i386/core/init.S +SRCS+= arch/i386/core/start32.S +SRCS+= arch/i386/core/pci_io.c +SRCS+= arch/i386/core/i386_timer.c +SRCS+= arch/i386/core/elf.c +SRCS+= arch/i386/core/cpu.c +SRCS+= arch/i386/core/video_subr.c +SRCS+= arch/i386/core/pic8259.c +SRCS+= arch/i386/core/hooks.c +SRCS+= arch/i386/core/callbacks.c +SRCS+= arch/i386/core/realmode.c +SRCS+= arch/i386/core/realmode_asm.S +SRCS+= arch/i386/core/pxe_callbacks.c + +# ROM loaders: ISA and PCI versions +ISAPREFIX= $(BIN)/isaprefix.o +ISAENTRY= $(BIN)/isaprefix.entry.o +ISAEXIT= $(BIN)/isaprefix.exit.o +PCIPREFIX= $(BIN)/pciprefix.o +PCIENTRY= $(BIN)/pciprefix.entry.o +PCIEXIT= $(BIN)/pciprefix.exit.o +# Variables xxx_ROMTYPE are defined by genrules.pl. ROMENTRY and +# ROMEXIT will evaluate to give the correct objects to use. +TARGETBASE=$(patsubst $(BIN)/%,%,$(firstword $(subst ., ,$@))) +ROMCARD=$(firstword $(subst --, ,$(TARGETBASE))) +ROMTYPE=$(firstword $(ROMTYPE_$(ROMCARD)) ISA) +romENTRY=$($(ROMTYPE)ENTRY) +romEXIT=$($(ROMTYPE)EXIT) + +# Target type for generic prf rules +TARGETTYPE=$(patsubst .%,%, $(suffix $(basename $@))) +TARGETENTRY=$($(TARGETTYPE)ENTRY) +TARGETEXIT=$($(TARGETTYPE)EXIT) + +# Other real-mode entry loaders +dskPREFIX= $(BIN)/floppyprefix.o +dskENTRY= $(BIN)/floppyprefix.entry.o +dskEXIT= $(BIN)/floppyprefix.exit.o +comPREFIX= $(BIN)/comprefix.o +comENTRY= $(BIN)/comprefix.entry.o +comEXIT= $(BIN)/comprefix.exit.o +exePREFIX= $(BIN)/exeprefix.o +exeENTRY= $(BIN)/exeprefix.entry.o +exeEXIT= $(BIN)/exeprefix.exit.o +liloPREFIX= $(BIN)/liloprefix.o +liloENTRY= $(BIN)/liloprefix.entry.o +liloEXIT= $(BIN)/liloprefix.exit.o +bImagePREFIX= $(BIN)/bImageprefix.o +bImageENTRY= $(BIN)/bImageprefix.entry.o +bImageEXIT= $(BIN)/bImageprefix.exit.o +pxePREFIX= $(BIN)/pxeprefix.o +pxeENTRY= $(BIN)/pxeprefix.entry.o +pxeEXIT= $(BIN)/pxeprefix.exit.o +rawPREFIX= $(BIN)/nullprefix.o +rawENTRY= $(BIN)/nullprefix.entry.o +rawEXIT= $(BIN)/nullprefix.exit.o + +# Protected mode entry loaders +elfPREFIX= $(BIN)/elfprefix.o +elfENTRY= $(BIN)/elfprefix.entry.o +elfEXIT= $(BIN)/elfprefix.exit.o +lmelfPREFIX= $(BIN)/lmelf_prefix.o +lmelfENTRY= $(BIN)/lmelf_prefix.entry.o +lmelfEXIT= $(BIN)/lmelf_prefix.exit.o +elfdPREFIX= $(BIN)/elf_dprefix.o +elfdENTRY= $(BIN)/elf_dprefix.entry.o +elfdEXIT= $(BIN)/elf_dprefix.exit.o +lmelfdPREFIX= $(BIN)/lmelf_dprefix.o +lmelfdENTRY= $(BIN)/lmelf_dprefix.entry.o +lmelfdEXIT= $(BIN)/lmelf_dprefix.exit.o + +include $(BIN)/Roms + +all: $(ROMS) +allroms: $(ROMS) +allzroms: $(ROMS) +alldsks: $(EB_DSKS) +allzdsks: $(EB_ZDSKS) +alllilos: $(EB_LILOS) +allzlilos: $(EB_ZLILOS) +allbImages: $(EB_BIMAGES) +allbzImages: $(EB_BZIMAGES) +allpxes: $(EB_PXES) +allzpxes: $(EB_ZPXES) +allelfs: $(EB_ELFS) +allzelfs: $(EB_ZELFS) +alllmelfs: $(EB_LMELFS) +allzlmelfs: $(EB_ZLMELFS) +allelfds: $(EB_ELFDS) +allzelfds: $(EB_ZELFDS) +alllmelfds: $(EB_LMELFDS) +allzlmelfds: $(EB_ZLMELFDS) +allcoms: $(EB_COMS) +allexes: $(EB_EXES) +allisos: $(EB_ISOS) +alllisos: $(EB_LISOS) + +BOBJS+= $(BIN)/pci_io.o $(BIN)/i386_timer.o +BOBJS+= $(BIN)/elf.o $(BIN)/cpu.o $(BIN)/video_subr.o +BOBJS+= $(BIN)/pic8259.o $(BIN)/hooks.o + +# ROM loaders + +$(ISAPREFIX): arch/i386/prefix/romprefix.S $(MAKEDEPS) + $(CPP) $(CFLAGS) $(LCONFIG) -Ui386 -D ASSEMBLY $< \ + | $(AS) $(ASFLAGS) -o $@ + +$(PCIPREFIX): arch/i386/prefix/romprefix.S $(MAKEDEPS) + $(CPP) -DPCI_PNP_HEADER $(CFLAGS) $(LCONFIG) -Ui386 -D ASSEMBLY $< \ + | $(AS) $(ASFLAGS) -o $@ + +# Prefix splitters +$(BIN)/%prefix.entry.o: $(BIN)/%prefix.o $(MAKEDEPS) + $(OBJCOPY) -R .text16 $< $@ + +$(BIN)/%prefix.exit.o: $(BIN)/%prefix.o $(MAKEDEPS) + $(OBJCOPY) -R .prefix $< $@ + +# Generic prefix objects +PREFIXOBJS = $(BIN)/init.o +ZPREFIXOBJS = $(BIN)/init.o $(BIN)/unnrv2b.o + +# Utilities +$(BIN)/nrv2b: util/nrv2b.c + $(HOST_CC) -O2 -DENCODE -DDECODE -DMAIN -DVERBOSE -DNDEBUG -DBITSIZE=32 -DENDIAN=0 -o $@ $< + +ZFILELEN = perl util/zfilelen.pl + +# Pattern Rules + +# General for compiling/assembly source files + +$(BIN)/%.o: arch/i386/core/%.c $(MAKEDEPS) + $(CC) $(CFLAGS) -o $@ -c $< + +$(BIN)/%.o: arch/i386/core/%.S $(MAKEDEPS) + $(CPP) $(CFLAGS) -Ui386 -D ASSEMBLY $< | $(AS) $(ASFLAGS) -o $@ + +$(BIN)/%.o: arch/i386/firmware/pcbios/%.c $(MAKEDEPS) + $(CC) $(CFLAGS) -o $@ -c $< + +$(BIN)/%.o: arch/i386/firmware/pcbios/%.S $(MAKEDEPS) + $(CPP) $(CFLAGS) -Ui386 -D ASSEMBLY $< | $(AS) $(ASFLAGS) -o $@ + +$(BIN)/%.o: arch/i386/prefix/%.S $(MAKEDEPS) + $(CPP) $(CFLAGS) -Ui386 -D ASSEMBLY $< | $(AS) $(ASFLAGS) -o $@ + +# general rule for 16bit .o, may be overridden +$(BIN)/%.o: $(BIN)/%.s + $(AS) $(ASFLAGS) -o $@ $< + +# general rule for .bin (plain binary loader code), may be overridden +$(BIN)/%.bin: $(BIN)/%.o + $(OBJCOPY) -O binary $< $@ + +# general rule for .z (compressed binary code), may be overridden +# rule for .z is in top level Makefile +# Give the directory name, e.g. use $(BIN)/rtl8139.com as the target. + +$(BIN)/%.zo: $(BIN)/%.zbin arch/i386/core/prefixzdata.lds $(MAKEDEPS) + $(LD) -T arch/i386/core/prefixzdata.lds -b binary $< -o $@ + +$(BIN)/%.uo: $(BIN)/%.bin arch/i386/core/prefixudata.lds $(MAKEDEPS) + $(LD) -T arch/i386/core/prefixudata.lds -b binary $< -o $@ + +# Intermediate prf rules + +%.prf: %.rt $(PREFIXOBJS) %.rt1.uo %.rt2.uo $(MAKEDEPS) + $(MAKE) $(TARGETENTRY) + $(LD) $(LDFLAGS) -T $(PLDSCRIPT) $(TARGETENTRY) -R $(subst $(MAKEDEPS),,$^) -o $@ + +%.zprf: %.rt $(ZPREFIXOBJS) %.rt1.uo %.rt2.zo $(MAKEDEPS) + $(MAKE) $(TARGETENTRY) + $(LD) $(LDFLAGS) -T $(PLDSCRIPT) $(TARGETENTRY) -R $(subst $(MAKEDEPS),,$^) -o $@ + +# general rules for normal/compressed ROM images, may be overridden +SUFFIXES += rom zrom + +$(BIN)/%.rom.rt: $(BIN)/%.rt.o $(ISAENTRY) $(PCIENTRY) $(ISAEXIT) $(PCIEXIT) $(LDSCRIPT) $(MAKEDEPS) + $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $(romEXIT) $< + @$(SIZE) $@ | $(CHECKSIZE) + +$(BIN)/%.rom: $(BIN)/%.rom.prf + $(OBJCOPY) -O binary $< $@ + $(MAKEROM) $(MAKEROM_FLAGS) $(MAKEROM_$(ROMCARD)) $(MAKEROM_ID_$(ROMCARD)) -i$(IDENT) $@ + +$(BIN)/%.zrom: $(BIN)/%.rom.zprf + $(OBJCOPY) -O binary $< $@ + $(MAKEROM) $(MAKEROM_FLAGS) $(MAKEROM_$(ROMCARD)) $(MAKEROM_ID_$(ROMCARD)) -i$(IDENT) $@ + +# general rules for ELF images +SUFFIXES += elf zelf +$(BIN)/%.elf.rt: $(BIN)/%.rt.o $(elfENTRY) $(elfEXIT) $(LDSCRIPT) $(MAKEDEPS) + $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $(elfEXIT) $< + +$(BIN)/%.elf: $(BIN)/%.elf.prf + $(OBJCOPY) -O binary $< $@ + +$(BIN)/%.zelf: $(BIN)/%.elf.zprf + $(OBJCOPY) -O binary $< $@ + +# general rules for Long Mode ELF images +SUFFIXES += lmelf zlmelf +$(BIN)/%.lmelf.rt: $(BIN)/%.rt.o $(lmelfENTRY) $(lmelfEXIT) $(LDSCRIPT) $(MAKEDEPS) + $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $(lmelfEXIT) $< + +$(BIN)/%.lmelf: $(BIN)/%.lmelf.prf + $(OBJCOPY) -O binary $< $@ + +$(BIN)/%.zlmelf: $(BIN)/%.lmelf.zprf + $(OBJCOPY) -O binary $< $@ + +# general rules for ELF dynamic images +SUFFIXES += elfd zelfd +$(BIN)/%.elfd.rt: $(BIN)/%.rt.o $(elfdENTRY) $(elfdEXIT) $(LDSCRIPT) $(MAKEDEPS) + $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $(elfdEXIT) $< + +$(BIN)/%.elfd: $(BIN)/%.elfd.prf + $(OBJCOPY) -O binary $< $@ + +$(BIN)/%.zelfd: $(BIN)/%.elfd.zprf + $(OBJCOPY) -O binary $< $@ + +# general rules for Long Mode ELF dynamic images +SUFFIXES += lmelfd zlmelfd +$(BIN)/%.lmelfd.rt: $(BIN)/%.rt.o $(lmelfdENTRY) $(lmelfdEXIT) $(LDSCRIPT) $(MAKEDEPS) + $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $(lmelfdEXIT) $< + +$(BIN)/%.lmelfd: $(BIN)/%.lmelfd.prf + $(OBJCOPY) -O binary $< $@ + +$(BIN)/%.zlmelfd: $(BIN)/%.lmelfd.zprf + $(OBJCOPY) -O binary $< $@ + +# rules to generate a DOS loadable .com executable +SUFFIXES += com +$(BIN)/%.com.rt: $(BIN)/%.rt.o $(comENTRY) $(comEXIT) $(LDSCRIPT) $(MAKEDEPS) + $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $< $(comEXIT) + +$(BIN)/%.com: $(BIN)/%.com.zprf + $(OBJCOPY) -O binary $< $@ + +# rules to generate a DOS loadable .exe executable +SUFFIXES += exe +$(BIN)/%.exe.rt: $(BIN)/%.rt.o $(exeENTRY) $(exeEXIT) $(LDSCRIPT) $(MAKEDEPS) + $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $< $(exeEXIT) + @$(SIZE) $@ | $(CHECKSIZE) + +$(BIN)/%.exe: $(BIN)/%.exe.prf + $(OBJCOPY) -O binary $< $@ + +# rules to make a LILO loadable image +SUFFIXES += lilo zlilo + +$(BIN)/%.lilo.rt: $(BIN)/%.rt.o $(liloENTRY) $(liloEXIT) $(LDSCRIPT) $(MAKEDEPS) + $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $< $(liloEXIT) + @$(SIZE) $@ | $(CHECKSIZE) + +$(BIN)/%.lilo: $(BIN)/%.lilo.prf + $(OBJCOPY) -O binary $< $@ + +$(BIN)/%.zlilo: $(BIN)/%.lilo.zprf + $(OBJCOPY) -O binary $< $@ + +# rules to make big linux boot protocol image +SUFFIXES += bImage bzImage + +$(BIN)/%.bImage.rt: $(BIN)/%.rt.o $(bImageENTRY) $(bImageEXIT) $(LDSCRIPT) $(MAKEDEPS) + $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $< $(bImageEXIT) + +$(BIN)/%.bImage: $(BIN)/%.bImage.prf + $(OBJCOPY) -O binary $< $@ + +$(BIN)/%.bzImage: $(BIN)/%.bImage.zprf + $(OBJCOPY) -O binary $< $@ + + +# rules to generate a PXE loadable image +SUFFIXES += pxe zpxe + +$(BIN)/%.pxe.rt: $(BIN)/%.rt.o $(pxeENTRY) $(pxeEXIT) $(LDSCRIPT) $(MAKEDEPS) + $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $< $(pxeEXIT) + @$(SIZE) $@ | $(CHECKSIZE) + +$(BIN)/%.pxe: $(BIN)/%.pxe.prf + $(OBJCOPY) -O binary $< $@ + +$(BIN)/%.zpxe: $(BIN)/%.pxe.zprf + $(OBJCOPY) -O binary $< $@ + +# rules to generate the .dsk/.zdsk floppy images +SUFFIXES += dsk zdsk + +$(BIN)/%.dsk.rt: $(BIN)/%.rt.o $(dskENTRY) $(dskEXIT) $(LDSCRIPT) $(MAKEDEPS) + $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $< $(dskEXIT) + @$(SIZE) $@ | $(CHECKSIZE) + +$(BIN)/%.dsk: $(BIN)/%.dsk.prf + $(OBJCOPY) -O binary $< $@ + +$(BIN)/%.zdsk: $(BIN)/%.dsk.zprf + $(OBJCOPY) -O binary $< $@ + +# rules to write the .dsk/.zdsk image onto a blank floppy +SUFFIXES += fd0 zfd0 +%.fd0: %.dsk + dd if=$< bs=512 conv=sync of=/dev/fd0 + sync + +%.zfd0: %.zdsk + dd if=$< bs=512 conv=sync of=/dev/fd0 + sync + +# rules to create raw executable images +SUFFIXES += raw zraw +$(BIN)/%.raw.rt: $(BIN)/%.rt.o $(rawENTRY) $(rawEXIT) $(LDSCRIPT) $(MAKEDEPS) + $(LD) $(LDFLAGS) -T $(LDSCRIPT) -o $@ $< $(rawEXIT) + +$(BIN)/%.raw: $(BIN)/%.raw.prf + $(OBJCOPY) -O binary $< $@ + +$(BIN)/%.zraw: $(BIN)/%.raw.zprf + $(OBJCOPY) -O binary $< $@ + +# rule to make a non-emulation ISO boot image +SUFFIXES += iso +%.iso: util/geniso %.zlilo + ISOLINUX_BIN=${ISOLINUX_BIN} bash util/geniso $*.iso $*.zlilo + +# rule to make a floppy emulation ISO boot image +SUFFIXES += liso +%.liso: util/genliso %.zlilo + bash util/genliso $*.liso $*.zlilo + diff --git a/src/arch/i386/core/aout_loader.c b/src/arch/i386/core/aout_loader.c new file mode 100644 index 00000000..f85620e9 --- /dev/null +++ b/src/arch/i386/core/aout_loader.c @@ -0,0 +1,144 @@ +/* a.out */ +struct exec { + unsigned long a_midmag; /* flags<<26 | mid<<16 | magic */ + unsigned long a_text; /* text segment size */ + unsigned long a_data; /* initialized data size */ + unsigned long a_bss; /* uninitialized data size */ + unsigned long a_syms; /* symbol table size */ + unsigned long a_entry; /* entry point */ + unsigned long a_trsize; /* text relocation size */ + unsigned long a_drsize; /* data relocation size */ +}; + +struct aout_state { + struct exec head; + unsigned long curaddr; + int segment; /* current segment number, -1 for none */ + unsigned long loc; /* start offset of current block */ + unsigned long skip; /* padding to be skipped to current segment */ + unsigned long toread; /* remaining data to be read in the segment */ +}; + +static struct aout_state astate; + +static sector_t aout_download(unsigned char *data, unsigned int len, int eof); +static inline os_download_t aout_probe(unsigned char *data, unsigned int len) +{ + unsigned long start, mid, end, istart, iend; + if (len < sizeof(astate.head)) { + return 0; + } + memcpy(&astate.head, data, sizeof(astate.head)); + if ((astate.head.a_midmag & 0xffff) != 0x010BL) { + return 0; + } + + printf("(a.out"); + aout_freebsd_probe(); + printf(")... "); + /* Check the aout image */ + start = astate.head.a_entry; + mid = (((start + astate.head.a_text) + 4095) & ~4095) + astate.head.a_data; + end = ((mid + 4095) & ~4095) + astate.head.a_bss; + istart = 4096; + iend = istart + (mid - start); + if (!prep_segment(start, mid, end, istart, iend)) + return dead_download; + astate.segment = -1; + astate.loc = 0; + astate.skip = 0; + astate.toread = 0; + return aout_download; +} + +static sector_t aout_download(unsigned char *data, unsigned int len, int eof) +{ + unsigned int offset; /* working offset in the current data block */ + + offset = 0; + +#ifdef AOUT_LYNX_KDI + astate.segment++; + if (astate.segment == 0) { + astate.curaddr = 0x100000; + astate.head.a_entry = astate.curaddr + 0x20; + } + memcpy(phys_to_virt(astate.curaddr), data, len); + astate.curaddr += len; + return 0; +#endif + + do { + if (astate.segment != -1) { + if (astate.skip) { + if (astate.skip >= len - offset) { + astate.skip -= len - offset; + break; + } + offset += astate.skip; + astate.skip = 0; + } + + if (astate.toread) { + if (astate.toread >= len - offset) { + memcpy(phys_to_virt(astate.curaddr), data+offset, + len - offset); + astate.curaddr += len - offset; + astate.toread -= len - offset; + break; + } + memcpy(phys_to_virt(astate.curaddr), data+offset, astate.toread); + offset += astate.toread; + astate.toread = 0; + } + } + + /* Data left, but current segment finished - look for the next + * segment. This is quite simple for a.out files. */ + astate.segment++; + switch (astate.segment) { + case 0: + /* read text */ + astate.curaddr = astate.head.a_entry; + astate.skip = 4096; + astate.toread = astate.head.a_text; + break; + case 1: + /* read data */ + /* skip and curaddr may be wrong, but I couldn't find + * examples where this failed. There is no reasonable + * documentation for a.out available. */ + astate.skip = ((astate.curaddr + 4095) & ~4095) - astate.curaddr; + astate.curaddr = (astate.curaddr + 4095) & ~4095; + astate.toread = astate.head.a_data; + break; + case 2: + /* initialize bss and start kernel */ + astate.curaddr = (astate.curaddr + 4095) & ~4095; + astate.skip = 0; + astate.toread = 0; + memset(phys_to_virt(astate.curaddr), '\0', astate.head.a_bss); + goto aout_startkernel; + default: + break; + } + } while (offset < len); + + astate.loc += len; + + if (eof) { + unsigned long entry; + +aout_startkernel: + entry = astate.head.a_entry; + done(1); + + aout_freebsd_boot(); +#ifdef AOUT_LYNX_KDI + xstart32(entry); +#endif + printf("unexpected a.out variant\n"); + longjmp(restart_etherboot, -2); + } + return 0; +} diff --git a/src/arch/i386/core/callbacks.c b/src/arch/i386/core/callbacks.c new file mode 100644 index 00000000..d45e63e2 --- /dev/null +++ b/src/arch/i386/core/callbacks.c @@ -0,0 +1,107 @@ +/* Callout/callback interface for Etherboot + * + * This file provides the mechanisms for making calls from Etherboot + * to external programs and vice-versa. + * + * Initial version by Michael Brown <mbrown@fensystems.co.uk>, January 2004. + */ + +#include "etherboot.h" +#include "callbacks.h" +#include "realmode.h" +#include "segoff.h" +#include <stdarg.h> + +/* Maximum amount of stack data that prefix may request to be passed + * to its exit routine + */ +#define MAX_PREFIX_STACK_DATA 16 + +/* Prefix exit routine is defined in prefix object */ +extern void prefix_exit ( void ); +extern void prefix_exit_end ( void ); + +/***************************************************************************** + * + * IN_CALL INTERFACE + * + ***************************************************************************** + */ + +/* in_call(): entry point for calls in to Etherboot from external code. + * + * Parameters: some set up by assembly code _in_call(), others as + * passed from external code. + */ +uint32_t i386_in_call ( va_list ap, i386_pm_in_call_data_t pm_data, + uint32_t opcode ) { + uint32_t ret; + i386_rm_in_call_data_t rm_data; + in_call_data_t in_call_data = { &pm_data, NULL }; + struct { + int data[MAX_PREFIX_STACK_DATA/4]; + } in_stack; + + /* Fill out rm_data if we were called from real mode */ + if ( opcode & EB_CALL_FROM_REAL_MODE ) { + in_call_data.rm = &rm_data; + rm_data = va_arg ( ap, typeof(rm_data) ); + /* Null return address indicates to use the special + * prefix exit mechanism, and that there are + * parameters on the stack that the prefix wants + * handed to its exit routine. + */ + if ( rm_data.ret_addr.offset == 0 ) { + int n = va_arg ( ap, int ) / 4; + int i; + for ( i = 0; i < n; i++ ) { + in_stack.data[i] = va_arg ( ap, int ); + } + } + } + + /* Hand off to main in_call() routine */ + ret = in_call ( &in_call_data, opcode, ap ); + + /* If real-mode return address is null, it means that we + * should exit via the prefix's exit path, which is part of + * our image. (This arrangement is necessary since the prefix + * code itself may have been vapourised by the time we want to + * return.) + */ + if ( ( opcode & EB_CALL_FROM_REAL_MODE ) && + ( rm_data.ret_addr.offset == 0 ) ) { + real_call ( prefix_exit, &in_stack, NULL ); + /* Should never return */ + } + + return ret; +} + +#ifdef CODE16 + +/* install_rm_callback_interface(): install real-mode callback + * interface at specified address. + * + * Real-mode code may then call to this address (or lcall to this + * address plus RM_IN_CALL_FAR) in order to make an in_call() to + * Etherboot. + * + * Returns the size of the installed code, or 0 if the code could not + * be installed. + */ +int install_rm_callback_interface ( void *address, size_t available ) { + if ( available && + ( available < rm_callback_interface_size ) ) return 0; + + /* Inform RM code where to find Etherboot */ + rm_etherboot_location = virt_to_phys(_text); + + /* Install callback interface */ + memcpy ( address, &rm_callback_interface, + rm_callback_interface_size ); + + return rm_callback_interface_size; +} + +#endif /* CODE16 */ diff --git a/src/arch/i386/core/cpu.c b/src/arch/i386/core/cpu.c new file mode 100644 index 00000000..8a0f7333 --- /dev/null +++ b/src/arch/i386/core/cpu.c @@ -0,0 +1,86 @@ +#ifdef CONFIG_X86_64 +#include "stdint.h" +#include "string.h" +#include "bits/cpu.h" + + +/* Standard macro to see if a specific flag is changeable */ +static inline int flag_is_changeable_p(uint32_t flag) +{ + uint32_t f1, f2; + + asm("pushfl\n\t" + "pushfl\n\t" + "popl %0\n\t" + "movl %0,%1\n\t" + "xorl %2,%0\n\t" + "pushl %0\n\t" + "popfl\n\t" + "pushfl\n\t" + "popl %0\n\t" + "popfl\n\t" + : "=&r" (f1), "=&r" (f2) + : "ir" (flag)); + + return ((f1^f2) & flag) != 0; +} + + +/* Probe for the CPUID instruction */ +static inline int have_cpuid_p(void) +{ + return flag_is_changeable_p(X86_EFLAGS_ID); +} + +static void identify_cpu(struct cpuinfo_x86 *c) +{ + unsigned xlvl; + + c->cpuid_level = -1; /* CPUID not detected */ + c->x86_model = c->x86_mask = 0; /* So far unknown... */ + c->x86_vendor_id[0] = '\0'; /* Unset */ + memset(&c->x86_capability, 0, sizeof c->x86_capability); + + if (!have_cpuid_p()) { + /* CPU doesn'thave CPUID */ + + /* If there are any capabilities, they'r vendor-specific */ + /* enable_cpuid() would have set c->x86 for us. */ + } + else { + /* CPU does have CPUID */ + + /* Get vendor name */ + cpuid(0x00000000, &c->cpuid_level, + (int *)&c->x86_vendor_id[0], + (int *)&c->x86_vendor_id[8], + (int *)&c->x86_vendor_id[4]); + + /* Initialize the standard set of capabilities */ + /* Note that the vendor-specific code below might override */ + + /* Intel-defined flags: level 0x00000001 */ + if ( c->cpuid_level >= 0x00000001 ) { + unsigned tfms, junk; + cpuid(0x00000001, &tfms, &junk, &junk, + &c->x86_capability[0]); + c->x86 = (tfms >> 8) & 15; + c->x86_model = (tfms >> 4) & 15; + c->x86_mask = tfms & 15; + } + + /* AMD-defined flags: level 0x80000001 */ + xlvl = cpuid_eax(0x80000000); + if ( (xlvl & 0xffff0000) == 0x80000000 ) { + if ( xlvl >= 0x80000001 ) + c->x86_capability[1] = cpuid_edx(0x80000001); + } + } +} + +struct cpuinfo_x86 cpu_info; +void cpu_setup(void) +{ + identify_cpu(&cpu_info); +} +#endif /* CONFIG_X86_64 */ diff --git a/src/arch/i386/core/elf.c b/src/arch/i386/core/elf.c new file mode 100644 index 00000000..6ef78c71 --- /dev/null +++ b/src/arch/i386/core/elf.c @@ -0,0 +1,135 @@ +#include "etherboot.h" +#include "elf.h" + + +#define NAME "Etherboot" + +#if defined(PCBIOS) +#define FIRMWARE "PCBIOS" +#endif +#if defined(LINUXBIOS) +#define FIRMWARE "LinuxBIOS" +#endif +#if !defined(FIRMWARE) +#error "No BIOS selected" +#endif + +#define SZ(X) ((sizeof(X)+3) & ~3) +#define CP(D,S) (memcpy(&(D), &(S), sizeof(S))) + +struct elf_notes { + /* The note header */ + struct Elf_Bhdr hdr; + + /* First the Fixed sized entries that must be well aligned */ + + /* Pointer to bootp data */ + Elf_Nhdr nf1; + char nf1_name[SZ(EB_PARAM_NOTE)]; + uint32_t nf1_bootp_data; + + /* Pointer to ELF header */ + Elf_Nhdr nf2; + char nf2_name[SZ(EB_PARAM_NOTE)]; + uint32_t nf2_header; + + /* A copy of the i386 memory map */ + Elf_Nhdr nf3; + char nf3_name[SZ(EB_PARAM_NOTE)]; + struct meminfo nf3_meminfo; + + /* Then the variable sized data string data where alignment does not matter */ + + /* The bootloader name */ + Elf_Nhdr nv1; + char nv1_desc[SZ(NAME)]; + /* The bootloader version */ + Elf_Nhdr nv2; + char nv2_desc[SZ(VERSION)]; + /* The firmware type */ + Elf_Nhdr nv3; + char nv3_desc[SZ(FIRMWARE)]; + /* Name of the loaded image */ + Elf_Nhdr nv4; + char nv4_loaded_image[128]; + /* An empty command line */ + Elf_Nhdr nv5; + char nv5_cmdline[SZ("")]; +}; + +#define ELF_NOTE_COUNT (3 + 5) + +static struct elf_notes notes; +struct Elf_Bhdr *prepare_boot_params(void *header) +{ + memset(¬es, 0, sizeof(notes)); + notes.hdr.b_signature = ELF_BHDR_MAGIC; + notes.hdr.b_size = sizeof(notes); + notes.hdr.b_checksum = 0; + notes.hdr.b_records = ELF_NOTE_COUNT; + + /* Initialize the fixed length entries. */ + notes.nf1.n_namesz = sizeof(EB_PARAM_NOTE); + notes.nf1.n_descsz = sizeof(notes.nf1_bootp_data); + notes.nf1.n_type = EB_BOOTP_DATA; + CP(notes.nf1_name, EB_PARAM_NOTE); + notes.nf1_bootp_data = virt_to_phys(BOOTP_DATA_ADDR); + + notes.nf2.n_namesz = sizeof(EB_PARAM_NOTE); + notes.nf2.n_descsz = sizeof(notes.nf2_header); + notes.nf2.n_type = EB_HEADER; + CP(notes.nf2_name, EB_PARAM_NOTE); + notes.nf2_header = virt_to_phys(header); + + notes.nf3.n_namesz = sizeof(EB_PARAM_NOTE); + notes.nf3.n_descsz = sizeof(notes.nf3_meminfo); + notes.nf3.n_type = EB_I386_MEMMAP; + CP(notes.nf3_name, EB_PARAM_NOTE); + memcpy(¬es.nf3_meminfo, &meminfo, sizeof(meminfo)); + + /* Initialize the variable length entries */ + notes.nv1.n_namesz = 0; + notes.nv1.n_descsz = sizeof(NAME); + notes.nv1.n_type = EBN_BOOTLOADER_NAME; + CP(notes.nv1_desc, NAME); + + notes.nv2.n_namesz = 0; + notes.nv2.n_descsz = sizeof(VERSION); + notes.nv2.n_type = EBN_BOOTLOADER_VERSION; + CP(notes.nv2_desc, VERSION); + + notes.nv3.n_namesz = 0; + notes.nv3.n_descsz = sizeof(FIRMWARE); + notes.nv3.n_type = EBN_FIRMWARE_TYPE; + CP(notes.nv3_desc, FIRMWARE); + + /* Attempt to pass the name of the loaded image */ + notes.nv4.n_namesz = 0; + notes.nv4.n_descsz = sizeof(notes.nv4_loaded_image); + notes.nv4.n_type = EBN_LOADED_IMAGE; + memcpy(¬es.nv4_loaded_image, KERNEL_BUF, sizeof(notes.nv4_loaded_image)); + + /* Pass an empty command line for now */ + notes.nv5.n_namesz = 0; + notes.nv5.n_descsz = sizeof(""); + notes.nv5.n_type = EBN_COMMAND_LINE; + CP(notes.nv5_cmdline, ""); + + + notes.hdr.b_checksum = ipchksum(¬es, sizeof(notes)); + /* Like UDP invert a 0 checksum to show that a checksum is present */ + if (notes.hdr.b_checksum == 0) { + notes.hdr.b_checksum = 0xffff; + } + return ¬es.hdr; +} + +int elf_start(unsigned long machine __unused_i386, unsigned long entry, unsigned long params) +{ +#if defined(CONFIG_X86_64) + if (machine == EM_X86_64) { + return xstart_lm(entry, params); + } +#endif + return xstart32(entry, params); +} diff --git a/src/arch/i386/core/etherboot.lds b/src/arch/i386/core/etherboot.lds new file mode 100644 index 00000000..6f406329 --- /dev/null +++ b/src/arch/i386/core/etherboot.lds @@ -0,0 +1,90 @@ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) + +ENTRY(_text) +SECTIONS { + . = ALIGN(16); + /* Start address of Etherboot in the virtual address space */ + _virt_start = 0; + _text = . ; + .text.nocompress : { + *(.text*.nocompress) + . = ALIGN(16); + } = 0x9090 + + .text16 : { + _text16 = .; + *(.text16) + *(.text16.*) + _etext16 = . ; + } + .text.compress : { + *(.text) + *(.text.*) + } = 0x9090 + .rodata : { + . = ALIGN(4); + *(.rodata) + *(.rodata.*) + } + . = ALIGN(4); + .drivers.pci : { + pci_drivers = . ; + *(.drivers.pci); + pci_drivers_end = . ; + } + . = ALIGN(4); + .drivers.isa : { + isa_drivers = . ; + *(.drivers.isa); + isa_drivers_end = .; + } + _etext = . ; + _data = . ; + .data : { + *(.data) + *(.data.*) + } + _edata = . ; + _uncompressed_verbatim_end = . ; + . = ALIGN(16); + .bss.preserve : { + *(.bss.preserve) + *(.bss.preserve.*) + } + _bss = . ; + .bss : { + *(.bss) + *(.bss.*) + } + . = ALIGN(16); + _ebss = .; + _stack = . ; + .stack : { + _stack_start = . ; + *(.stack) + *(.stack.*) + _stack_end = . ; + } + _bss_size = _ebss - _bss; + _stack_offset = _stack - _text ; + _stack_offset_pgh = _stack_offset / 16 ; + _stack_size = _stack_end - _stack_start ; + . = ALIGN(16); + _end = . ; + + /DISCARD/ : { + *(.comment) + *(.note) + } + + /* PXE-specific symbol calculations. The results of these are + * needed in romprefix.S, which is why they must be calculated + * here. + */ + _pxe_stack_size = _pxe_stack_t_size + + _pxe_callback_interface_size + + _rm_callback_interface_size + + _e820mangler_size + 15 ; + +} diff --git a/src/arch/i386/core/etherboot.prefix.lds b/src/arch/i386/core/etherboot.prefix.lds new file mode 100644 index 00000000..3550a2a3 --- /dev/null +++ b/src/arch/i386/core/etherboot.prefix.lds @@ -0,0 +1,100 @@ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) + +ENTRY(_prefix_start) +SECTIONS { + /* Prefix */ + .prefix : { + _verbatim_start = . ; + _prefix_start = . ; + *(.prefix) + . = ALIGN(16); + _prefix_end = . ; + } = 0x9090 + _prefix_size = _prefix_end - _prefix_start; + + .text.nocompress : { + *(.prefix.udata) + } = 0x9090 + + decompress_to = . ; + .prefix.zdata : { + _compressed = . ; + *(.prefix.zdata) + _compressed_end = . ; + } + _compressed_size = _compressed_end - _compressed; + + . = ALIGN(16); + _verbatim_end = . ; + + + /* Size of the core of etherboot in memory */ + _base_size = _end - _text; + + /* _prefix_size is the length of the non-core etherboot prefix */ + _prefix_size = _prefix_end - _prefix_start; + + /* _verbatim_size is the actual amount that has to be copied to base memory */ + _verbatim_size = _verbatim_end - _verbatim_start; + + /* _image_size is the amount of base memory needed to run */ + _image_size = _base_size + _prefix_size; + + /* Standard sizes rounded up to paragraphs */ + _prefix_size_pgh = (_prefix_size + 15) / 16; + _verbatim_size_pgh = (_verbatim_size + 15) / 16; + _image_size_pgh = (_image_size + 15) / 16 ; + + /* Standard sizes in sectors */ + _prefix_size_sct = (_prefix_size + 511) / 512; + _verbatim_size_sct = (_verbatim_size + 511) / 512; + _image_size_sct = (_image_size + 511) / 512; + + /* Symbol offsets and sizes for the exe prefix */ + _exe_hdr_size = 32; + _exe_size = _verbatim_size; /* Should this be - 32 to exclude the header? */ + _exe_size_tail = (_exe_size) % 512; + _exe_size_pages = ((_exe_size) + 511) / 512; + _exe_bss_size = ((_image_size - _verbatim_size) + 15) / 16; + _exe_ss_offset = (_stack_offset + _prefix_size - _exe_hdr_size + 15) / 16 ; + + /* This is where we copy the compressed image before decompression. + * Prepare to decompress in place. The end mark is about 8.25 bytes long, + * and the worst case symbol is about 16.5 bytes long. Therefore + * We need to reserve at least 25 bytes of slack here. + * Currently I reserve 2048 bytes of just slack to be safe :) + * 2048 bytes easily falls within the BSS (the defualt stack is 4096 bytes) + * so we really are decompressing in place. + * + * Hmm. I missed a trick. In the very worst case (no compression) + * the encoded data is 9/8 the size as it started out so to be completely + * safe I need to be 1/8 of the uncompressed code size past the end. + * This will still fit compfortably into our bss in any conceivable scenario. + */ + _compressed_copy = _edata + _prefix_size - _compressed_size + + /* The amount to overflow _edata */ + MAX( ((_edata - _text + 7) / 8) , 2016 ) + 32; + _assert = ASSERT( ( _compressed_copy - _prefix_size ) < _ebss , "Cannot decompress in place" ) ; + + decompress = DEFINED(decompress) ? decompress : 0; + /DISCARD/ : { + *(.comment) + *(.note) + } + + /* Symbols used by the prefixes whose addresses are inconvinient + * to compute, at runtime in the code. + */ + image_basemem_size = DEFINED(image_basemem_size)? image_basemem_size : 65536; + image_basemem = DEFINED(image_basemem)? image_basemem : 65536; + _prefix_real_to_prot = _real_to_prot + _prefix_size ; + _prefix_prot_to_real = _prot_to_real + _prefix_size ; + _prefix_image_basemem_size = image_basemem_size + _prefix_size ; + _prefix_image_basemem = image_basemem + _prefix_size ; + _prefix_rm_in_call = _rm_in_call + _prefix_size ; + _prefix_in_call = _in_call + _prefix_size ; + _prefix_rom = rom + _prefix_size ; + _prefix_rm_etherboot_location = rm_etherboot_location + _prefix_size ; + _prefix_stack_end = _stack_end + _prefix_size ; +} diff --git a/src/arch/i386/core/freebsd_loader.c b/src/arch/i386/core/freebsd_loader.c new file mode 100644 index 00000000..4e820e8e --- /dev/null +++ b/src/arch/i386/core/freebsd_loader.c @@ -0,0 +1,377 @@ +/* bootinfo */ +#define BOOTINFO_VERSION 1 +#define NODEV (-1) /* non-existent device */ +#define PAGE_SHIFT 12 /* LOG2(PAGE_SIZE) */ +#define PAGE_SIZE (1<<PAGE_SHIFT) /* bytes/page */ +#define PAGE_MASK (PAGE_SIZE-1) +#define N_BIOS_GEOM 8 + +struct bootinfo { + unsigned int bi_version; + const unsigned char *bi_kernelname; + struct nfs_diskless *bi_nfs_diskless; + /* End of fields that are always present. */ +#define bi_endcommon bi_n_bios_used + unsigned int bi_n_bios_used; + unsigned long bi_bios_geom[N_BIOS_GEOM]; + unsigned int bi_size; + unsigned char bi_memsizes_valid; + unsigned char bi_pad[3]; + unsigned long bi_basemem; + unsigned long bi_extmem; + unsigned long bi_symtab; + unsigned long bi_esymtab; + /* Note that these are in the FreeBSD headers but were not here... */ + unsigned long bi_kernend; /* end of kernel space */ + unsigned long bi_envp; /* environment */ + unsigned long bi_modulep; /* preloaded modules */ +}; + +static struct bootinfo bsdinfo; + +#ifdef ELF_IMAGE +static Elf32_Shdr *shdr; /* To support the FreeBSD kludge! */ +static Address symtab_load; +static Address symstr_load; +static int symtabindex; +static int symstrindex; +#endif + +static enum { + Unknown, Tagged, Aout, Elf, Aout_FreeBSD, Elf_FreeBSD, +} image_type = Unknown; + +static unsigned int off; + + +#ifdef ELF_IMAGE +static void elf_freebsd_probe(void) +{ + image_type = Elf; + if ( (estate.e.elf32.e_entry & 0xf0000000) && + (estate.e.elf32.e_type == ET_EXEC)) + { + image_type = Elf_FreeBSD; + printf("/FreeBSD"); + off = -(estate.e.elf32.e_entry & 0xff000000); + estate.e.elf32.e_entry += off; + } + /* Make sure we have a null to start with... */ + shdr = 0; + + /* Clear the symbol index values... */ + symtabindex = -1; + symstrindex = -1; + + /* ...and the load addresses of the symbols */ + symtab_load = 0; + symstr_load = 0; +} + +static void elf_freebsd_fixup_segment(void) +{ + if (image_type == Elf_FreeBSD) { + estate.p.phdr32[estate.segment].p_paddr += off; + } +} + +static void elf_freebsd_find_segment_end(void) +{ + /* Count the bytes read even for the last block + * as we will need to know where the last block + * ends in order to load the symbols correctly. + * (plus it could be useful elsewhere...) + * Note that we need to count the actual size, + * not just the end of the disk image size. + */ + estate.curaddr += + (estate.p.phdr32[estate.segment].p_memsz - + estate.p.phdr32[estate.segment].p_filesz); +} + +static int elf_freebsd_debug_loader(unsigned int offset) +{ + /* No more segments to be loaded - time to start the + * nasty state machine to support the loading of + * FreeBSD debug symbols due to the fact that FreeBSD + * uses/exports the kernel's debug symbols in order + * to make much of the system work! Amazing (arg!) + * + * We depend on the fact that for the FreeBSD kernel, + * there is only one section of debug symbols and that + * the section is after all of the loaded sections in + * the file. This assumes a lot but is somewhat required + * to make this code not be too annoying. (Where do you + * load symbols when the code has not loaded yet?) + * Since this function is actually just a callback from + * the network data transfer code, we need to be able to + * work with the data as it comes in. There is no chance + * for doing a seek other than forwards. + * + * The process we use is to first load the section + * headers. Once they are loaded (shdr != 0) we then + * look for where the symbol table and symbol table + * strings are and setup some state that we found + * them and fall into processing the first one (which + * is the symbol table) and after that has been loaded, + * we try the symbol strings. Note that the order is + * actually required as the memory image depends on + * the symbol strings being loaded starting at the + * end of the symbol table. The kernel assumes this + * layout of the image. + * + * At any point, if we get to the end of the load file + * or the section requested is earlier in the file than + * the current file pointer, we just end up falling + * out of this and booting the kernel without this + * information. + */ + + /* Make sure that the next address is long aligned... */ + /* Assumes size of long is a power of 2... */ + estate.curaddr = (estate.curaddr + sizeof(long) - 1) & ~(sizeof(long) - 1); + + /* If we have not yet gotten the shdr loaded, try that */ + if (shdr == 0) + { + estate.toread = estate.e.elf32.e_shnum * estate.e.elf32.e_shentsize; + estate.skip = estate.e.elf32.e_shoff - (estate.loc + offset); + if (estate.toread) + { +#if ELF_DEBUG + printf("shdr *, size %lX, curaddr %lX\n", + estate.toread, estate.curaddr); +#endif + + /* Start reading at the curaddr and make that the shdr */ + shdr = (Elf32_Shdr *)phys_to_virt(estate.curaddr); + + /* Start to read... */ + return 1; + } + } + else + { + /* We have the shdr loaded, check if we have found + * the indexs where the symbols are supposed to be */ + if ((symtabindex == -1) && (symstrindex == -1)) + { + int i; + /* Make sure that the address is page aligned... */ + /* Symbols need to start in their own page(s)... */ + estate.curaddr = (estate.curaddr + 4095) & ~4095; + + /* Need to make new indexes... */ + for (i=0; i < estate.e.elf32.e_shnum; i++) + { + if (shdr[i].sh_type == SHT_SYMTAB) + { + int j; + for (j=0; j < estate.e.elf32.e_phnum; j++) + { + /* Check only for loaded sections */ + if ((estate.p.phdr32[j].p_type | 0x80) == (PT_LOAD | 0x80)) + { + /* Only the extra symbols */ + if ((shdr[i].sh_offset >= estate.p.phdr32[j].p_offset) && + ((shdr[i].sh_offset + shdr[i].sh_size) <= + (estate.p.phdr32[j].p_offset + estate.p.phdr32[j].p_filesz))) + { + shdr[i].sh_offset=0; + shdr[i].sh_size=0; + break; + } + } + } + if ((shdr[i].sh_offset != 0) && (shdr[i].sh_size != 0)) + { + symtabindex = i; + symstrindex = shdr[i].sh_link; + } + } + } + } + + /* Check if we have a symbol table index and have not loaded it */ + if ((symtab_load == 0) && (symtabindex >= 0)) + { + /* No symbol table yet? Load it first... */ + + /* This happens to work out in a strange way. + * If we are past the point in the file already, + * we will skip a *large* number of bytes which + * ends up bringing us to the end of the file and + * an old (default) boot. Less code and lets + * the state machine work in a cleaner way but this + * is a nasty side-effect trick... */ + estate.skip = shdr[symtabindex].sh_offset - (estate.loc + offset); + + /* And we need to read this many bytes... */ + estate.toread = shdr[symtabindex].sh_size; + + if (estate.toread) + { +#if ELF_DEBUG + printf("db sym, size %lX, curaddr %lX\n", + estate.toread, estate.curaddr); +#endif + /* Save where we are loading this... */ + symtab_load = phys_to_virt(estate.curaddr); + + *((long *)phys_to_virt(estate.curaddr)) = estate.toread; + estate.curaddr += sizeof(long); + + /* Start to read... */ + return 1; + } + } + else if ((symstr_load == 0) && (symstrindex >= 0)) + { + /* We have already loaded the symbol table, so + * now on to the symbol strings... */ + + + /* Same nasty trick as above... */ + estate.skip = shdr[symstrindex].sh_offset - (estate.loc + offset); + + /* And we need to read this many bytes... */ + estate.toread = shdr[symstrindex].sh_size; + + if (estate.toread) + { +#if ELF_DEBUG + printf("db str, size %lX, curaddr %lX\n", + estate.toread, estate.curaddr); +#endif + /* Save where we are loading this... */ + symstr_load = phys_to_virt(estate.curaddr); + + *((long *)phys_to_virt(estate.curaddr)) = estate.toread; + estate.curaddr += sizeof(long); + + /* Start to read... */ + return 1; + } + } + } + /* all done */ + return 0; +} + +static void elf_freebsd_boot(unsigned long entry) +{ + if (image_type != Elf_FreeBSD) + return; + + memset(&bsdinfo, 0, sizeof(bsdinfo)); + bsdinfo.bi_basemem = meminfo.basememsize; + bsdinfo.bi_extmem = meminfo.memsize; + bsdinfo.bi_memsizes_valid = 1; + bsdinfo.bi_version = BOOTINFO_VERSION; + bsdinfo.bi_kernelname = virt_to_phys(KERNEL_BUF); + bsdinfo.bi_nfs_diskless = NULL; + bsdinfo.bi_size = sizeof(bsdinfo); +#define RB_BOOTINFO 0x80000000 /* have `struct bootinfo *' arg */ + if(freebsd_kernel_env[0] != '\0'){ + freebsd_howto |= RB_BOOTINFO; + bsdinfo.bi_envp = (unsigned long)freebsd_kernel_env; + } + + /* Check if we have symbols loaded, and if so, + * made the meta_data needed to pass those to + * the kernel. */ + if ((symtab_load !=0) && (symstr_load != 0)) + { + unsigned long *t; + + bsdinfo.bi_symtab = symtab_load; + + /* End of symbols (long aligned...) */ + /* Assumes size of long is a power of 2... */ + bsdinfo.bi_esymtab = (symstr_load + + sizeof(long) + + *((long *)symstr_load) + + sizeof(long) - 1) & ~(sizeof(long) - 1); + + /* Where we will build the meta data... */ + t = phys_to_virt(bsdinfo.bi_esymtab); + +#if ELF_DEBUG + printf("Metadata at %lX\n",t); +#endif + + /* Set up the pointer to the memory... */ + bsdinfo.bi_modulep = virt_to_phys(t); + + /* The metadata structure is an array of 32-bit + * words where we store some information about the + * system. This is critical, as FreeBSD now looks + * only for the metadata for the extended symbol + * information rather than in the bootinfo. + */ + /* First, do the kernel name and the kernel type */ + /* Note that this assumed x86 byte order... */ + + /* 'kernel\0\0' */ + *t++=MODINFO_NAME; *t++= 7; *t++=0x6E72656B; *t++=0x00006C65; + + /* 'elf kernel\0\0' */ + *t++=MODINFO_TYPE; *t++=11; *t++=0x20666C65; *t++=0x6E72656B; *t++ = 0x00006C65; + + /* Now the symbol start/end - note that they are + * here in local/physical address - the Kernel + * boot process will relocate the addresses. */ + *t++=MODINFOMD_SSYM | MODINFO_METADATA; *t++=sizeof(*t); *t++=bsdinfo.bi_symtab; + *t++=MODINFOMD_ESYM | MODINFO_METADATA; *t++=sizeof(*t); *t++=bsdinfo.bi_esymtab; + + *t++=MODINFO_END; *t++=0; /* end of metadata */ + + /* Since we have symbols we need to make + * sure that the kernel knows its own end + * of memory... It is not _end but after + * the symbols and the metadata... */ + bsdinfo.bi_kernend = virt_to_phys(t); + + /* Signal locore.s that we have a valid bootinfo + * structure that was completely filled in. */ + freebsd_howto |= 0x80000000; + } + + xstart32(entry, freebsd_howto, NODEV, 0, 0, 0, + virt_to_phys(&bsdinfo), 0, 0, 0); + longjmp(restart_etherboot, -2); +} +#endif + +#ifdef AOUT_IMAGE +static void aout_freebsd_probe(void) +{ + image_type = Aout; + if (((astate.head.a_midmag >> 16) & 0xffff) == 0) { + /* Some other a.out variants have a different + * value, and use other alignments (e.g. 1K), + * not the 4K used by FreeBSD. */ + image_type = Aout_FreeBSD; + printf("/FreeBSD"); + off = -(astate.head.a_entry & 0xff000000); + astate.head.a_entry += off; + } +} + +static void aout_freebsd_boot(void) +{ + if (image_type == Aout_FreeBSD) { + memset(&bsdinfo, 0, sizeof(bsdinfo)); + bsdinfo.bi_basemem = meminfo.basememsize; + bsdinfo.bi_extmem = meminfo.memsize; + bsdinfo.bi_memsizes_valid = 1; + bsdinfo.bi_version = BOOTINFO_VERSION; + bsdinfo.bi_kernelname = virt_to_phys(KERNEL_BUF); + bsdinfo.bi_nfs_diskless = NULL; + bsdinfo.bi_size = sizeof(bsdinfo); + xstart32(astate.head.a_entry, freebsd_howto, NODEV, 0, 0, 0, + virt_to_phys(&bsdinfo), 0, 0, 0); + longjmp(restart_etherboot, -2); + } +} +#endif diff --git a/src/arch/i386/core/hooks.c b/src/arch/i386/core/hooks.c new file mode 100644 index 00000000..9ca6f480 --- /dev/null +++ b/src/arch/i386/core/hooks.c @@ -0,0 +1,35 @@ +#include "etherboot.h" +#include "callbacks.h" +#include <stdarg.h> + +void arch_main ( in_call_data_t *data __unused, va_list params __unused ) +{ +#ifdef PCBIOS + /* Deallocate base memory used for the prefix, if applicable + */ + forget_prefix_base_memory(); +#endif + +} + +void arch_relocated_from (unsigned long old_addr ) +{ + +#ifdef PCBIOS + /* Deallocate base memory used for the Etherboot runtime, + * if applicable + */ + forget_runtime_base_memory( old_addr ); +#endif + +} + +void arch_on_exit ( int exit_status __unused ) +{ +#ifdef PCBIOS + /* Deallocate the real-mode stack now. We will reallocate + * the stack if are going to use it after this point. + */ + forget_real_mode_stack(); +#endif +} diff --git a/src/arch/i386/core/i386_timer.c b/src/arch/i386/core/i386_timer.c new file mode 100644 index 00000000..531183d4 --- /dev/null +++ b/src/arch/i386/core/i386_timer.c @@ -0,0 +1,191 @@ +/* A couple of routines to implement a low-overhead timer for drivers */ + + /* + * 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, or (at + * your option) any later version. + */ + +#include "etherboot.h" +#include "timer.h" +#include "latch.h" + +void __load_timer2(unsigned int ticks) +{ + /* + * Now let's take care of PPC channel 2 + * + * Set the Gate high, program PPC channel 2 for mode 0, + * (interrupt on terminal count mode), binary count, + * load 5 * LATCH count, (LSB and MSB) to begin countdown. + * + * Note some implementations have a bug where the high bits byte + * of channel 2 is ignored. + */ + /* Set up the timer gate, turn off the speaker */ + /* Set the Gate high, disable speaker */ + outb((inb(PPC_PORTB) & ~PPCB_SPKR) | PPCB_T2GATE, PPC_PORTB); + /* binary, mode 0, LSB/MSB, Ch 2 */ + outb(TIMER2_SEL|WORD_ACCESS|MODE0|BINARY_COUNT, TIMER_MODE_PORT); + /* LSB of ticks */ + outb(ticks & 0xFF, TIMER2_PORT); + /* MSB of ticks */ + outb(ticks >> 8, TIMER2_PORT); +} + +static int __timer2_running(void) +{ + return ((inb(PPC_PORTB) & PPCB_T2OUT) == 0); +} + +#if !defined(CONFIG_TSC_CURRTICKS) +void setup_timers(void) +{ + return; +} + +void load_timer2(unsigned int ticks) +{ + return __load_timer2(ticks); +} + +int timer2_running(void) +{ + return __timer2_running(); +} + +void ndelay(unsigned int nsecs) +{ + waiton_timer2((nsecs * CLOCK_TICK_RATE)/1000000000); +} +void udelay(unsigned int usecs) +{ + waiton_timer2((usecs * TICKS_PER_MS)/1000); +} +#endif /* !defined(CONFIG_TSC_CURRTICKS) */ + +#if defined(CONFIG_TSC_CURRTICKS) + +#define rdtsc(low,high) \ + __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high)) + +#define rdtscll(val) \ + __asm__ __volatile__ ("rdtsc" : "=A" (val)) + + +/* Number of clock ticks to time with the rtc */ +#define LATCH 0xFF + +#define LATCHES_PER_SEC ((CLOCK_TICK_RATE + (LATCH/2))/LATCH) +#define TICKS_PER_LATCH ((LATCHES_PER_SEC + (TICKS_PER_SEC/2))/TICKS_PER_SEC) + +static void sleep_latch(void) +{ + __load_timer2(LATCH); + while(__timer2_running()); +} + +/* ------ Calibrate the TSC ------- + * Time how long it takes to excute a loop that runs in known time. + * And find the convertion needed to get to CLOCK_TICK_RATE + */ + + +static unsigned long long calibrate_tsc(void) +{ + unsigned long startlow, starthigh; + unsigned long endlow, endhigh; + + rdtsc(startlow,starthigh); + sleep_latch(); + rdtsc(endlow,endhigh); + + /* 64-bit subtract - gcc just messes up with long longs */ + __asm__("subl %2,%0\n\t" + "sbbl %3,%1" + :"=a" (endlow), "=d" (endhigh) + :"g" (startlow), "g" (starthigh), + "0" (endlow), "1" (endhigh)); + + /* Error: ECPUTOOFAST */ + if (endhigh) + goto bad_ctc; + + endlow *= TICKS_PER_LATCH; + return endlow; + + /* + * The CTC wasn't reliable: we got a hit on the very first read, + * or the CPU was so fast/slow that the quotient wouldn't fit in + * 32 bits.. + */ +bad_ctc: + printf("bad_ctc\n"); + return 0; +} + +static unsigned long clocks_per_tick; +void setup_timers(void) +{ + if (!clocks_per_tick) { + clocks_per_tick = calibrate_tsc(); + /* Display the CPU Mhz to easily test if the calibration was bad */ + printf("CPU %ld Mhz\n", (clocks_per_tick/1000 * TICKS_PER_SEC)/1000); + } +} + +unsigned long currticks(void) +{ + unsigned long clocks_high, clocks_low; + unsigned long currticks; + /* Read the Time Stamp Counter */ + rdtsc(clocks_low, clocks_high); + + /* currticks = clocks / clocks_per_tick; */ + __asm__("divl %1" + :"=a" (currticks) + :"r" (clocks_per_tick), "0" (clocks_low), "d" (clocks_high)); + + + return currticks; +} + +static unsigned long long timer_timeout; +static int __timer_running(void) +{ + unsigned long long now; + rdtscll(now); + return now < timer_timeout; +} + +void udelay(unsigned int usecs) +{ + unsigned long long now; + rdtscll(now); + timer_timeout = now + usecs * ((clocks_per_tick * TICKS_PER_SEC)/(1000*1000)); + while(__timer_running()); +} +void ndelay(unsigned int nsecs) +{ + unsigned long long now; + rdtscll(now); + timer_timeout = now + nsecs * ((clocks_per_tick * TICKS_PER_SEC)/(1000*1000*1000)); + while(__timer_running()); +} + +void load_timer2(unsigned int timer2_ticks) +{ + unsigned long long now; + unsigned long clocks; + rdtscll(now); + clocks = timer2_ticks * ((clocks_per_tick * TICKS_PER_SEC)/CLOCK_TICK_RATE); + timer_timeout = now + clocks; +} + +int timer2_running(void) +{ + return __timer_running(); +} + +#endif /* RTC_CURRTICKS */ diff --git a/src/arch/i386/core/init.S b/src/arch/i386/core/init.S new file mode 100644 index 00000000..71717c25 --- /dev/null +++ b/src/arch/i386/core/init.S @@ -0,0 +1,305 @@ +#include "callbacks.h" + .equ CR0_PE, 1 + + .text + .arch i386 + .section ".prefix", "ax", @progbits + +#undef CODE16 +#if defined(PCBIOS) +#define CODE16 +#endif + +/* We have two entry points: "conventional" (at the start of the file) + * and "callback" (at _entry, 2 bytes in). The "callback" entry + * should be used if the caller wishes to provide a specific opcode. + * It is equivalent to a call to in_call. Using the "conventional" + * entry point is equivalent to using the "callback" entry point with + * an opcode of EB_OPCODE_MAIN. + * + * Both entry points can be called in either 16-bit real or 32-bit + * protected mode with flat physical addresses. We detect which mode + * the processor is in and call either in_call or rm_in_call as + * appropriate. Note that the mode detection code must therefore be + * capable of doing the same thing in either mode, even though the + * machine code instructions will be interpreted differently. + * + * The decompressor will be invoked if necessary to decompress + * Etherboot before attempting to jump to it. + */ + +/****************************************************************************** + * Entry points and mode detection code + ****************************************************************************** + */ + + .code32 +/* "Conventional" entry point: caller provides no opcode */ + .globl _start +_start: + /* Set flag to indicate conventional entry point used */ + pushl $0 /* "pushw $0" in 16-bit code */ + /* Fall through to "callback" entry point */ + +/* "Callback" entry point */ + .globl _entry +_entry: + +#ifdef CODE16 + /* CPU mode detection code */ + pushl %eax /* "pushw %ax" in 16-bit code */ + pushw %ax /* "pushl %eax" in 16-bit code */ + movl %cr0, %eax /* Test protected mode bit */ + testb $CR0_PE, %al + popw %ax /* "popl %eax" in 16-bit code */ + popl %eax /* "popw %eax" in 16-bit code */ + jz rmode +#endif /* CODE16 */ + +/****************************************************************************** + * Entered in protected mode + ****************************************************************************** + */ + + .code32 +pmode: + cmpl $0, 0(%esp) /* Conventional entry point used? */ + jne 1f + /* Entered via conventional entry point: set up stack */ + xchgl %eax, 4(%esp) /* %eax = return addr, store %eax */ + movl %eax, 0(%esp) /* 0(%esp) = return address */ + movl $(EB_OPCODE_MAIN|EB_USE_INTERNAL_STACK|EB_SKIP_OPCODE), %eax + xchgl %eax, 4(%esp) /* 4(%esp) = opcode, restore %eax */ +1: + /* Run decompressor if necessary */ + pushl %eax + movl $decompress, %eax + testl %eax, %eax + jz 1f + call decompress +1: popl %eax + + /* Make in_call to Etherboot */ + jmp _prefix_in_call + +/****************************************************************************** + * Entered in real mode + ****************************************************************************** + */ + +#ifdef CODE16 + .code16 +rmode: + pushw %ax /* Padding */ + pushw %bp + movw %sp, %bp + cmpw $0, 6(%bp) /* Conventional entry point used? */ + jne 1f + /* Entered via conventional entry point: set up stack */ + pushw %ax + movw 6(%bp), %ax + movw %ax, 2(%bp) /* Move return address down */ + movl $(EB_OPCODE_MAIN|EB_USE_INTERNAL_STACK|EB_SKIP_OPCODE), 4(%bp) + popw %ax + popw %bp + jmp 2f +1: /* Entered via callback entry point: do nothing */ + popw %bp + popw %ax +2: + /* Preserve registers */ + pushw %ds + pushl %eax + + /* Run decompressor if necessary. Decompressor is 32-bit + * code, so we must switch to pmode first. Save and restore + * GDT over transition to pmode. + */ + movl $decompress, %eax + testl %eax, %eax + jz 1f + pushw %ds + pushw %es + pushw %fs + pushw %gs + subw $8, %sp + pushw %bp + movw %sp, %bp + sgdt 2(%bp) + pushw %ss /* Store params for _prot_to_real */ + pushw %cs + call _prefix_real_to_prot + .code32 + call decompress + call _prefix_prot_to_real + .code16 + popw %ax /* skip */ + popw %ax /* skip */ + lgdt 2(%bp) + popw %bp + addw $8, %sp + popw %gs + popw %fs + popw %es + popw %ds +1: + + /* Set rm_etherboot_location */ + xorl %eax, %eax + movw %cs, %ax + movw %ax, %ds + shll $4, %eax + addl $_prefix_size, %eax + movl %eax, _prefix_rm_etherboot_location + + /* Restore registers */ + popl %eax + popw %ds + + /* Make real-mode in_call to Etherboot */ + jmp _prefix_rm_in_call +#endif /* CODE16 */ + +/****************************************************************************** + * Utility routines that can be called by the "prefix". + ****************************************************************************** + */ + +#ifdef CODE16 + +/* Prelocate code: either to an area at the top of free base memory. + * Switch stacks to use the stack within the resulting + * Etherboot image. + * + * On entry, %cs:0000 must be the start of the prefix: this is used to + * locate the code to be copied. + * + * This routine takes a single word parameter: the number of bytes to + * be transferred from the old stack to the new stack (excluding the + * return address and this parameter itself, which will always be + * copied). If this value is negative, the stacks will not be + * switched. + * + * Control will "return" to the appropriate point in the relocated + * image. + */ + +#define PRELOC_PRESERVE ( 20 ) +#define PRELOC_OFFSET_RETADDR ( PRELOC_PRESERVE ) +#define PRELOC_OFFSET_RETADDR_E ( PRELOC_OFFSET_RETADDR + 4 ) +#define PRELOC_OFFSET_COPY ( PRELOC_OFFSET_RETADDR_E ) +#define PRELOC_OFFSET_COPY_E ( PRELOC_OFFSET_COPY + 2 ) + +#define PRELOC_ALWAYS_COPY ( PRELOC_OFFSET_COPY_E ) + + .code16 + .globl prelocate +prelocate: + /* Pad to allow for expansion of return address */ + pushw %ax + + /* Preserve registers */ + pushaw + pushw %ds + pushw %es + + /* Claim an area of base memory from the BIOS and put the + * payload there. + */ + movw $0x40, %bx + movw %bx, %es + movw %es:(0x13), %bx /* FBMS in kb to %ax */ + shlw $6, %bx /* ... in paragraphs */ + subw $_image_size_pgh, %bx /* Subtract space for image */ + shrw $6, %bx /* Round down to nearest kb */ + movw %bx, %es:(0x13) /* ...and claim memory from BIOS */ + shlw $6, %bx + + /* At this point %bx contains the segment address for the + * start of the image (image = prefix + runtime). + */ + + /* Switch stacks */ + movw %ss, %ax + movw %ax, %ds + movw %sp, %si /* %ds:si = current %ss:sp */ + movw %ss:PRELOC_OFFSET_COPY(%si), %cx + testw %cx, %cx + js 1f + leaw _stack_offset_pgh(%bx), %ax /* %ax = new %ss */ + movw %ax, %es + movw $_stack_size, %di + addw $PRELOC_ALWAYS_COPY, %cx + subw %cx, %di /* %es:di = new %ss:sp */ + movw %ax, %ss /* Set new %ss:sp */ + movw %di, %sp + cld + rep movsb /* Copy stack contents */ +1: + + /* Do the image copy backwards, since if there's overlap with + * a forward copy then it means we're going to get trashed + * during the copy anyway... + */ + pushal /* Preserve 32-bit registers */ + movw %bx, %es /* Destination base for copy */ + pushw %cs + popw %ds /* Source base for copy */ + movl $_verbatim_size-1, %ecx /* Offset to last byte */ + movl %ecx, %esi + movl %ecx, %edi + incl %ecx /* Length */ + std /* Backwards copy of binary */ + ADDR32 rep movsb + cld + popal /* Restore 32-bit registers */ + + /* Store (%bx<<4) as image_basemem to be picked up by + * basemem.c. Also store image_size, since there's no other + * way that we can later know how much memory we allocated. + * (_zfile_size is unavailable when rt2 is linked). + */ + pushl %eax + xorl %eax, %eax + movw %bx, %ax + shll $4, %eax + movl %eax, %es:_prefix_image_basemem + movl $_image_size, %es:_prefix_image_basemem_size + popl %eax + + /* Expand original near return address into far return to new + * code location. + */ + movw %sp, %bp + xchgw %bx, (PRELOC_OFFSET_RETADDR+2)(%bp) + movw %bx, (PRELOC_OFFSET_RETADDR+0)(%bp) + + /* Restore registers and return */ + popw %es + popw %ds + popaw + lret /* Jump to relocated code */ + + /* Utility routine to free base memory allocated by prelocate. + * Ensure that said memory is not in use (e.g. for the CPU + * stack) before calling this routine. + */ + .globl deprelocate +deprelocate: + /* Claim an area of base memory from the BIOS and put the + * payload there. + */ + pushw %ax + pushw %es + movw $0x40, %ax + movw %ax, %es + movw %es:(0x13), %ax /* FBMS in kb to %ax */ + shlw $6, %ax /* ... in paragraphs */ + addw $_image_size_pgh+0x40-1, %ax /* Add space for image and... */ + shrw $6, %ax /* ...round up to nearest kb */ + movw %ax, %es:(0x13) /* Give memory back to BIOS */ + popw %es + popw %ax + ret + +#endif /* CODE16 */ diff --git a/src/arch/i386/core/multiboot_loader.c b/src/arch/i386/core/multiboot_loader.c new file mode 100644 index 00000000..e9785f1c --- /dev/null +++ b/src/arch/i386/core/multiboot_loader.c @@ -0,0 +1,143 @@ +/* Multiboot support + * + * 2003-07-02 mmap fix and header probe by SONE Takeshi + */ + +struct multiboot_mods { + unsigned mod_start; + unsigned mod_end; + unsigned char *string; + unsigned reserved; +}; + +struct multiboot_mmap { + unsigned int size; + unsigned int base_addr_low; + unsigned int base_addr_high; + unsigned int length_low; + unsigned int length_high; + unsigned int type; +}; + +/* The structure of a Multiboot 0.6 parameter block. */ +struct multiboot_info { + unsigned int flags; +#define MULTIBOOT_MEM_VALID 0x01 +#define MULTIBOOT_BOOT_DEV_VALID 0x02 +#define MULTIBOOT_CMDLINE_VALID 0x04 +#define MULTIBOOT_MODS_VALID 0x08 +#define MULTIBOOT_AOUT_SYMS_VALID 0x10 +#define MULTIBOOT_ELF_SYMS_VALID 0x20 +#define MULTIBOOT_MMAP_VALID 0x40 + unsigned int memlower; + unsigned int memupper; + unsigned int bootdev; + unsigned int cmdline; /* physical address of the command line */ + unsigned mods_count; + struct multiboot_mods *mods_addr; + unsigned syms_num; + unsigned syms_size; + unsigned syms_addr; + unsigned syms_shndx; + unsigned mmap_length; + unsigned mmap_addr; + /* The structure actually ends here, so I might as well put + * the ugly e820 parameters here... + */ + struct multiboot_mmap mmap[E820MAX]; +}; + +/* Multiboot image header (minimal part) */ +struct multiboot_header { + unsigned int magic; +#define MULTIBOOT_HEADER_MAGIC 0x1BADB002 + unsigned int flags; + unsigned int checksum; +}; + +static struct multiboot_header *mbheader; + +static struct multiboot_info mbinfo; + +static void multiboot_probe(unsigned char *data, int len) +{ + int offset; + struct multiboot_header *h; + + /* Multiboot spec requires the header to be in first 8KB of the image */ + if (len > 8192) + len = 8192; + + for (offset = 0; offset < len; offset += 4) { + h = (struct multiboot_header *) (data + offset); + if (h->magic == MULTIBOOT_HEADER_MAGIC + && h->magic + h->flags + h->checksum == 0) { + printf("/Multiboot"); + mbheader = h; + return; + } + } + mbheader = 0; +} + +static inline void multiboot_boot(unsigned long entry) +{ + unsigned char cmdline[512], *c; + int i; + if (!mbheader) + return; + /* Etherboot limits the command line to the kernel name, + * default parameters and user prompted parameters. All of + * them are shorter than 256 bytes. As the kernel name and + * the default parameters come from the same BOOTP/DHCP entry + * (or if they don't, the parameters are empty), only two + * strings of the maximum size are possible. Note this buffer + * can overrun if a stupid file name is chosen. Oh well. */ + c = cmdline; + for (i = 0; KERNEL_BUF[i] != 0; i++) { + switch (KERNEL_BUF[i]) { + case ' ': + case '\\': + case '"': + *c++ = '\\'; + break; + default: + break; + } + *c++ = KERNEL_BUF[i]; + } + (void)sprintf(c, " -retaddr %#lX", virt_to_phys(xend32)); + + mbinfo.flags = MULTIBOOT_MMAP_VALID | MULTIBOOT_MEM_VALID |MULTIBOOT_CMDLINE_VALID; + mbinfo.memlower = meminfo.basememsize; + mbinfo.memupper = meminfo.memsize; + mbinfo.bootdev = 0; /* not booted from disk */ + mbinfo.cmdline = virt_to_phys(cmdline); + for (i = 0; i < (int) meminfo.map_count; i++) { + mbinfo.mmap[i].size = sizeof(struct multiboot_mmap) + - sizeof(unsigned int); + mbinfo.mmap[i].base_addr_low = + (unsigned int) meminfo.map[i].addr; + mbinfo.mmap[i].base_addr_high = + (unsigned int) (meminfo.map[i].addr >> 32); + mbinfo.mmap[i].length_low = + (unsigned int) meminfo.map[i].size; + mbinfo.mmap[i].length_high = + (unsigned int) (meminfo.map[i].size >> 32); + mbinfo.mmap[i].type = meminfo.map[i].type; + } + mbinfo.mmap_length = meminfo.map_count * sizeof(struct multiboot_mmap); + mbinfo.mmap_addr = virt_to_phys(mbinfo.mmap); + + /* The Multiboot 0.6 spec requires all segment registers to be + * loaded with an unrestricted, writeable segment. + * xstart32 does this for us. + */ + + /* Start the kernel, passing the Multiboot information record + * and the magic number. */ + os_regs.eax = 0x2BADB002; + os_regs.ebx = virt_to_phys(&mbinfo); + xstart32(entry); + longjmp(restart_etherboot, -2); +} diff --git a/src/arch/i386/core/pci_io.c b/src/arch/i386/core/pci_io.c new file mode 100644 index 00000000..e89561e4 --- /dev/null +++ b/src/arch/i386/core/pci_io.c @@ -0,0 +1,352 @@ +/* +** Support for NE2000 PCI clones added David Monro June 1997 +** Generalised to other NICs by Ken Yap July 1997 +** +** Most of this is taken from: +** +** /usr/src/linux/drivers/pci/pci.c +** /usr/src/linux/include/linux/pci.h +** /usr/src/linux/arch/i386/bios32.c +** /usr/src/linux/include/linux/bios32.h +** /usr/src/linux/drivers/net/ne.c +*/ +#ifdef CONFIG_PCI +#include "etherboot.h" +#include "pci.h" + +#ifdef CONFIG_PCI_DIRECT +#define PCIBIOS_SUCCESSFUL 0x00 + +#define DEBUG 0 + +/* + * Functions for accessing PCI configuration space with type 1 accesses + */ + +#define CONFIG_CMD(bus, device_fn, where) (0x80000000 | (bus << 16) | (device_fn << 8) | (where & ~3)) + +int pcibios_read_config_byte(unsigned int bus, unsigned int device_fn, + unsigned int where, uint8_t *value) +{ + outl(CONFIG_CMD(bus,device_fn,where), 0xCF8); + *value = inb(0xCFC + (where&3)); + return PCIBIOS_SUCCESSFUL; +} + +int pcibios_read_config_word (unsigned int bus, + unsigned int device_fn, unsigned int where, uint16_t *value) +{ + outl(CONFIG_CMD(bus,device_fn,where), 0xCF8); + *value = inw(0xCFC + (where&2)); + return PCIBIOS_SUCCESSFUL; +} + +int pcibios_read_config_dword (unsigned int bus, unsigned int device_fn, + unsigned int where, uint32_t *value) +{ + outl(CONFIG_CMD(bus,device_fn,where), 0xCF8); + *value = inl(0xCFC); + return PCIBIOS_SUCCESSFUL; +} + +int pcibios_write_config_byte (unsigned int bus, unsigned int device_fn, + unsigned int where, uint8_t value) +{ + outl(CONFIG_CMD(bus,device_fn,where), 0xCF8); + outb(value, 0xCFC + (where&3)); + return PCIBIOS_SUCCESSFUL; +} + +int pcibios_write_config_word (unsigned int bus, unsigned int device_fn, + unsigned int where, uint16_t value) +{ + outl(CONFIG_CMD(bus,device_fn,where), 0xCF8); + outw(value, 0xCFC + (where&2)); + return PCIBIOS_SUCCESSFUL; +} + +int pcibios_write_config_dword (unsigned int bus, unsigned int device_fn, unsigned int where, uint32_t value) +{ + outl(CONFIG_CMD(bus,device_fn,where), 0xCF8); + outl(value, 0xCFC); + return PCIBIOS_SUCCESSFUL; +} + +#undef CONFIG_CMD + +#else /* CONFIG_PCI_DIRECT not defined */ + +#if !defined(PCBIOS) +#error "The pcibios can only be used when the PCBIOS support is compiled in" +#endif + +/* Macro for calling the BIOS32 service. This replaces the old + * bios32_call function. Use in a statement such as + * __asm__ ( BIOS32_CALL, + * : <output registers> + * : "S" ( bios32_entry ), <other input registers> ); + */ +#define BIOS32_CALL "call _virt_to_phys\n\t" \ + "pushl %%cs\n\t" \ + "call *%%esi\n\t" \ + "cli\n\t" \ + "cld\n\t" \ + "call _phys_to_virt\n\t" + +static unsigned long bios32_entry; +static unsigned long pcibios_entry; + +static unsigned long bios32_service(unsigned long service) +{ + unsigned char return_code; /* %al */ + unsigned long address; /* %ebx */ + unsigned long length; /* %ecx */ + unsigned long entry; /* %edx */ + + __asm__(BIOS32_CALL + : "=a" (return_code), + "=b" (address), + "=c" (length), + "=d" (entry) + : "0" (service), + "1" (0), + "S" (bios32_entry)); + + switch (return_code) { + case 0: + return address + entry; + case 0x80: /* Not present */ + printf("bios32_service(%d) : not present\n", service); + return 0; + default: /* Shouldn't happen */ + printf("bios32_service(%d) : returned %#X????\n", + service, return_code); + return 0; + } +} + +int pcibios_read_config_byte(unsigned int bus, + unsigned int device_fn, unsigned int where, uint8_t *value) +{ + unsigned long ret; + unsigned long bx = (bus << 8) | device_fn; + + __asm__(BIOS32_CALL + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=c" (*value), + "=a" (ret) + : "1" (PCIBIOS_READ_CONFIG_BYTE), + "b" (bx), + "D" ((long) where), + "S" (pcibios_entry)); + return (int) (ret & 0xff00) >> 8; +} + +int pcibios_read_config_word(unsigned int bus, + unsigned int device_fn, unsigned int where, uint16_t *value) +{ + unsigned long ret; + unsigned long bx = (bus << 8) | device_fn; + + __asm__(BIOS32_CALL + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=c" (*value), + "=a" (ret) + : "1" (PCIBIOS_READ_CONFIG_WORD), + "b" (bx), + "D" ((long) where), + "S" (pcibios_entry)); + return (int) (ret & 0xff00) >> 8; +} + +int pcibios_read_config_dword(unsigned int bus, + unsigned int device_fn, unsigned int where, uint32_t *value) +{ + unsigned long ret; + unsigned long bx = (bus << 8) | device_fn; + + __asm__(BIOS32_CALL + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=c" (*value), + "=a" (ret) + : "1" (PCIBIOS_READ_CONFIG_DWORD), + "b" (bx), + "D" ((long) where), + "S" (pcibios_entry)); + return (int) (ret & 0xff00) >> 8; +} + +int pcibios_write_config_byte (unsigned int bus, + unsigned int device_fn, unsigned int where, uint8_t value) +{ + unsigned long ret; + unsigned long bx = (bus << 8) | device_fn; + + __asm__(BIOS32_CALL + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=a" (ret) + : "0" (PCIBIOS_WRITE_CONFIG_BYTE), + "c" (value), + "b" (bx), + "D" ((long) where), + "S" (pcibios_entry)); + return (int) (ret & 0xff00) >> 8; +} + +int pcibios_write_config_word (unsigned int bus, + unsigned int device_fn, unsigned int where, uint16_t value) +{ + unsigned long ret; + unsigned long bx = (bus << 8) | device_fn; + + __asm__(BIOS32_CALL + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=a" (ret) + : "0" (PCIBIOS_WRITE_CONFIG_WORD), + "c" (value), + "b" (bx), + "D" ((long) where), + "S" (pcibios_entry)); + return (int) (ret & 0xff00) >> 8; +} + +int pcibios_write_config_dword (unsigned int bus, + unsigned int device_fn, unsigned int where, uint32_t value) +{ + unsigned long ret; + unsigned long bx = (bus << 8) | device_fn; + + __asm__(BIOS32_CALL + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:" + : "=a" (ret) + : "0" (PCIBIOS_WRITE_CONFIG_DWORD), + "c" (value), + "b" (bx), + "D" ((long) where), + "S" (pcibios_entry)); + return (int) (ret & 0xff00) >> 8; +} + +static void check_pcibios(void) +{ + unsigned long signature; + unsigned char present_status; + unsigned char major_revision; + unsigned char minor_revision; + int pack; + + if ((pcibios_entry = bios32_service(PCI_SERVICE))) { + __asm__(BIOS32_CALL + "jc 1f\n\t" + "xor %%ah, %%ah\n" + "1:\tshl $8, %%eax\n\t" + "movw %%bx, %%ax" + : "=d" (signature), + "=a" (pack) + : "1" (PCIBIOS_PCI_BIOS_PRESENT), + "S" (pcibios_entry) + : "bx", "cx"); + + present_status = (pack >> 16) & 0xff; + major_revision = (pack >> 8) & 0xff; + minor_revision = pack & 0xff; + if (present_status || (signature != PCI_SIGNATURE)) { + printf("ERROR: BIOS32 says PCI BIOS, but no PCI " + "BIOS????\n"); + pcibios_entry = 0; + } +#if DEBUG + if (pcibios_entry) { + printf ("pcibios_init : PCI BIOS revision %hhX.%hhX" + " entry at %#X\n", major_revision, + minor_revision, pcibios_entry); + } +#endif + } +} + +static void pcibios_init(void) +{ + union bios32 *check; + unsigned char sum; + int i, length; + bios32_entry = 0; + + /* + * Follow the standard procedure for locating the BIOS32 Service + * directory by scanning the permissible address range from + * 0xe0000 through 0xfffff for a valid BIOS32 structure. + * + */ + + for (check = phys_to_virt(0xe0000); (void *)check <= phys_to_virt(0xffff0); ++check) { + if (check->fields.signature != BIOS32_SIGNATURE) + continue; + length = check->fields.length * 16; + if (!length) + continue; + sum = 0; + for (i = 0; i < length ; ++i) + sum += check->chars[i]; + if (sum != 0) + continue; + if (check->fields.revision != 0) { + printf("pcibios_init : unsupported revision %d at %#X, mail drew@colorado.edu\n", + check->fields.revision, check); + continue; + } +#if DEBUG + printf("pcibios_init : BIOS32 Service Directory " + "structure at %#X\n", check); +#endif + if (!bios32_entry) { + if (check->fields.entry >= 0x100000) { + printf("pcibios_init: entry in high " + "memory, giving up\n"); + return; + } else { + bios32_entry = check->fields.entry; +#if DEBUG + printf("pcibios_init : BIOS32 Service Directory" + " entry at %#X\n", bios32_entry); +#endif + } + } + } + if (bios32_entry) + check_pcibios(); +} +#endif /* CONFIG_PCI_DIRECT not defined*/ + +unsigned long pcibios_bus_base(unsigned int bus __unused) +{ + /* architecturally this must be 0 */ + return 0; +} + +void find_pci(int type, struct pci_device *dev) +{ +#ifndef CONFIG_PCI_DIRECT + if (!pcibios_entry) { + pcibios_init(); + } + if (!pcibios_entry) { + printf("pci_init: no BIOS32 detected\n"); + return; + } +#endif + return scan_pci_bus(type, dev); +} +#endif /* CONFIG_PCI */ diff --git a/src/arch/i386/core/pic8259.c b/src/arch/i386/core/pic8259.c new file mode 100644 index 00000000..cc2071fc --- /dev/null +++ b/src/arch/i386/core/pic8259.c @@ -0,0 +1,331 @@ +/* + * Basic support for controlling the 8259 Programmable Interrupt Controllers. + * + * Initially written by Michael Brown (mcb30). + */ + +#include <etherboot.h> +#include "pic8259.h" +#include "realmode.h" + +#ifdef DEBUG_IRQ +#define DBG(...) printf ( __VA_ARGS__ ) +#else +#define DBG(...) +#endif + +/* State of trivial IRQ handler */ +irq_t trivial_irq_installed_on = IRQ_NONE; +static uint16_t trivial_irq_previous_trigger_count = 0; + +/* The actual trivial IRQ handler + * + * Note: we depend on the C compiler not realising that we're putting + * variables in the ".text16" section and therefore not forcing them + * back to the ".data" section. I don't see any reason to expect this + * behaviour to change. + * + * These must *not* be the first variables to appear in this file; the + * first variable to appear gets the ".data" directive. + */ +RM_FRAGMENT(_trivial_irq_handler, + "pushw %bx\n\t" + "call 1f\n1:\tpopw %bx\n\t" /* PIC access to variables */ + "incw %cs:(_trivial_irq_trigger_count-1b)(%bx)\n\t" + "popw %bx\n\t" + "iret\n\t" + "\n\t" + ".globl _trivial_irq_trigger_count\n\t" + "_trivial_irq_trigger_count: .short 0\n\t" + "\n\t" + ".globl _trivial_irq_chain_to\n\t" + "_trivial_irq_chain_to: .short 0,0\n\t" + "\n\t" + ".globl _trivial_irq_chain\n\t" + "_trivial_irq_chain: .byte 0\n\t" + ); +extern volatile uint16_t _trivial_irq_trigger_count; +extern segoff_t _trivial_irq_chain_to; +extern int8_t _trivial_irq_chain; + +/* Current locations of trivial IRQ handler. These will change at + * runtime when relocation is used; the handler needs to be copied to + * base memory before being installed. + */ +void (*trivial_irq_handler)P((void)) = _trivial_irq_handler; +uint16_t volatile *trivial_irq_trigger_count = &_trivial_irq_trigger_count; +segoff_t *trivial_irq_chain_to = &_trivial_irq_chain_to; +uint8_t *trivial_irq_chain = &_trivial_irq_chain; + +/* Install a handler for the specified IRQ. Address of previous + * handler will be stored in previous_handler. Enabled/disabled state + * of IRQ will be preserved across call, therefore if the handler does + * chaining, ensure that either (a) IRQ is disabled before call, or + * (b) previous_handler points directly to the place that the handler + * picks up its chain-to address. + */ + +int install_irq_handler ( irq_t irq, segoff_t *handler, + uint8_t *previously_enabled, + segoff_t *previous_handler ) { + segoff_t *irq_vector = IRQ_VECTOR ( irq ); + *previously_enabled = irq_enabled ( irq ); + + if ( irq > IRQ_MAX ) { + DBG ( "Invalid IRQ number %d\n" ); + return 0; + } + + previous_handler->segment = irq_vector->segment; + previous_handler->offset = irq_vector->offset; + if ( *previously_enabled ) disable_irq ( irq ); + DBG ( "Installing handler at %hx:%hx for IRQ %d (vector 0000:%hx)," + " leaving %s\n", + handler->segment, handler->offset, irq, virt_to_phys(irq_vector), + ( *previously_enabled ? "enabled" : "disabled" ) ); + DBG ( "...(previous handler at %hx:%hx)\n", + previous_handler->segment, previous_handler->offset ); + irq_vector->segment = handler->segment; + irq_vector->offset = handler->offset; + if ( *previously_enabled ) enable_irq ( irq ); + return 1; +} + +/* Remove handler for the specified IRQ. Routine checks that another + * handler has not been installed that chains to handler before + * uninstalling handler. Enabled/disabled state of the IRQ will be + * restored to that specified by previously_enabled. + */ + +int remove_irq_handler ( irq_t irq, segoff_t *handler, + uint8_t *previously_enabled, + segoff_t *previous_handler ) { + segoff_t *irq_vector = IRQ_VECTOR ( irq ); + + if ( irq > IRQ_MAX ) { + DBG ( "Invalid IRQ number %d\n" ); + return 0; + } + if ( ( irq_vector->segment != handler->segment ) || + ( irq_vector->offset != handler->offset ) ) { + DBG ( "Cannot remove handler for IRQ %d\n" ); + return 0; + } + + DBG ( "Removing handler for IRQ %d\n", irq ); + disable_irq ( irq ); + irq_vector->segment = previous_handler->segment; + irq_vector->offset = previous_handler->offset; + if ( *previously_enabled ) enable_irq ( irq ); + return 1; +} + +/* Install the trivial IRQ handler. This routine installs the + * handler, tests it and enables the IRQ. + */ + +int install_trivial_irq_handler ( irq_t irq ) { + segoff_t trivial_irq_handler_segoff = SEGOFF(trivial_irq_handler); + + if ( trivial_irq_installed_on != IRQ_NONE ) { + DBG ( "Can install trivial IRQ handler only once\n" ); + return 0; + } + if ( SEGMENT(trivial_irq_handler) > 0xffff ) { + DBG ( "Trivial IRQ handler not in base memory\n" ); + return 0; + } + + DBG ( "Installing trivial IRQ handler on IRQ %d\n", irq ); + if ( ! install_irq_handler ( irq, &trivial_irq_handler_segoff, + trivial_irq_chain, + trivial_irq_chain_to ) ) + return 0; + trivial_irq_installed_on = irq; + + DBG ( "Testing trivial IRQ handler\n" ); + disable_irq ( irq ); + *trivial_irq_trigger_count = 0; + trivial_irq_previous_trigger_count = 0; + fake_irq ( irq ); + if ( ! trivial_irq_triggered ( irq ) ) { + DBG ( "Installation of trivial IRQ handler failed\n" ); + remove_trivial_irq_handler ( irq ); + return 0; + } + /* Send EOI just in case there was a leftover interrupt */ + send_specific_eoi ( irq ); + DBG ( "Trivial IRQ handler installed successfully\n" ); + enable_irq ( irq ); + return 1; +} + +/* Remove the trivial IRQ handler. + */ + +int remove_trivial_irq_handler ( irq_t irq ) { + segoff_t trivial_irq_handler_segoff = SEGOFF(trivial_irq_handler); + + if ( trivial_irq_installed_on == IRQ_NONE ) return 1; + if ( irq != trivial_irq_installed_on ) { + DBG ( "Cannot uninstall trivial IRQ handler from IRQ %d; " + "is installed on IRQ %d\n", irq, + trivial_irq_installed_on ); + return 0; + } + + if ( ! remove_irq_handler ( irq, &trivial_irq_handler_segoff, + trivial_irq_chain, + trivial_irq_chain_to ) ) + return 0; + + if ( trivial_irq_triggered ( trivial_irq_installed_on ) ) { + DBG ( "Sending EOI for unwanted trivial IRQ\n" ); + send_specific_eoi ( trivial_irq_installed_on ); + } + + trivial_irq_installed_on = IRQ_NONE; + return 1; +} + +/* Safe method to detect whether or not trivial IRQ has been + * triggered. Using this call avoids potential race conditions. This + * call will return success only once per trigger. + */ + +int trivial_irq_triggered ( irq_t irq ) { + uint16_t trivial_irq_this_trigger_count = *trivial_irq_trigger_count; + int triggered = ( trivial_irq_this_trigger_count - + trivial_irq_previous_trigger_count ); + + /* irq is not used at present, but we have it in the API for + * future-proofing; in case we want the facility to have + * multiple trivial IRQ handlers installed simultaneously. + * + * Avoid compiler warning about unused variable. + */ + if ( irq == IRQ_NONE ) {}; + + trivial_irq_previous_trigger_count = trivial_irq_this_trigger_count; + return triggered ? 1 : 0; +} + +/* Copy trivial IRQ handler to a new location. Typically used to copy + * the handler into base memory; when relocation is being used we need + * to do this before installing the handler. + * + * Call with target=NULL in order to restore the handler to its + * original location. + */ + +int copy_trivial_irq_handler ( void *target, size_t target_size ) { + irq_t currently_installed_on = trivial_irq_installed_on; + uint32_t offset = ( target == NULL ? 0 : + target - (void*)_trivial_irq_handler ); + + if (( target != NULL ) && ( target_size < TRIVIAL_IRQ_HANDLER_SIZE )) { + DBG ( "Insufficient space to copy trivial IRQ handler\n" ); + return 0; + } + + if ( currently_installed_on != IRQ_NONE ) { + DBG ("WARNING: relocating trivial IRQ handler while in use\n"); + if ( ! remove_trivial_irq_handler ( currently_installed_on ) ) + return 0; + } + + /* Do the actual copy */ + if ( target != NULL ) { + DBG ( "Copying trivial IRQ handler to %hx:%hx\n", + SEGMENT(target), OFFSET(target) ); + memcpy ( target, _trivial_irq_handler, + TRIVIAL_IRQ_HANDLER_SIZE ); + } else { + DBG ( "Restoring trivial IRQ handler to original location\n" ); + } + /* Update all the pointers to structures within the handler */ + trivial_irq_handler = ( void (*)P((void)) ) + ( (void*)_trivial_irq_handler + offset ); + trivial_irq_trigger_count = (uint16_t*) + ( (void*)&_trivial_irq_trigger_count + offset ); + trivial_irq_chain_to = (segoff_t*) + ( (void*)&_trivial_irq_chain_to + offset ); + trivial_irq_chain = (uint8_t*) + ( (void*)&_trivial_irq_chain + offset ); + + if ( currently_installed_on != IRQ_NONE ) { + if ( ! install_trivial_irq_handler ( currently_installed_on ) ) + return 0; + } + return 1; +} + +/* Send non-specific EOI(s). This seems to be inherently unsafe. + */ + +void send_nonspecific_eoi ( irq_t irq ) { + DBG ( "Sending non-specific EOI for IRQ %d\n", irq ); + if ( irq >= IRQ_PIC_CUTOFF ) { + outb ( ICR_EOI_NON_SPECIFIC, PIC2_ICR ); + } + outb ( ICR_EOI_NON_SPECIFIC, PIC1_ICR ); +} + +/* Send specific EOI(s). + */ + +void send_specific_eoi ( irq_t irq ) { + DBG ( "Sending specific EOI for IRQ %d\n", irq ); + outb ( ICR_EOI_SPECIFIC | ICR_VALUE(irq), ICR_REG(irq) ); + if ( irq >= IRQ_PIC_CUTOFF ) { + outb ( ICR_EOI_SPECIFIC | ICR_VALUE(CHAINED_IRQ), + ICR_REG(CHAINED_IRQ) ); + } +} + +/* Fake an IRQ + */ + +void fake_irq ( irq_t irq ) { + struct { + uint16_t int_number; + } PACKED in_stack; + + /* Convert IRQ to INT number: + * + * subb $0x08,%cl Invert bit 3, set bits 4-7 iff irq < 8 + * xorb $0x70,%cl Invert bits 4-6 + * andb $0x7f,%cl Clear bit 7 + * + * No, it's not the most intuitive method, but I was proud to + * get it down to three lines of assembler when this routine + * was originally implemented in pcbios.S. + */ + in_stack.int_number = ( ( irq - 8 ) ^ 0x70 ) & 0x7f; + + RM_FRAGMENT(rm_fake_irq, + "popw %ax\n\t" /* %ax = INT number */ + "call 1f\n1:\tpop %bx\n\t" + "movb %al, %cs:(2f-1b+1)(%bx)\n\t" /* Overwrite INT number..*/ + "\n2:\tint $0x00\n\t" /* ..in this instruction */ + ); + + real_call ( rm_fake_irq, &in_stack, NULL ); +} + +/* Dump current 8259 status: enabled IRQs and handler addresses. + */ + +#ifdef DEBUG_IRQ +void dump_irq_status ( void ) { + int irq = 0; + + for ( irq = 0; irq < 16; irq++ ) { + if ( irq_enabled ( irq ) ) { + printf ( "IRQ%d enabled, ISR at %hx:%hx\n", irq, + IRQ_VECTOR(irq)->segment, + IRQ_VECTOR(irq)->offset ); + } + } +} +#endif diff --git a/src/arch/i386/core/prefixudata.lds b/src/arch/i386/core/prefixudata.lds new file mode 100644 index 00000000..1c76128e --- /dev/null +++ b/src/arch/i386/core/prefixudata.lds @@ -0,0 +1,8 @@ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) + +SECTIONS { + .prefix.udata : { + *(*) + } +} diff --git a/src/arch/i386/core/prefixzdata.lds b/src/arch/i386/core/prefixzdata.lds new file mode 100644 index 00000000..bf6ea977 --- /dev/null +++ b/src/arch/i386/core/prefixzdata.lds @@ -0,0 +1,8 @@ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) + +SECTIONS { + .prefix.zdata : { + *(*) + } +} diff --git a/src/arch/i386/core/pxe_callbacks.c b/src/arch/i386/core/pxe_callbacks.c new file mode 100644 index 00000000..344d34fe --- /dev/null +++ b/src/arch/i386/core/pxe_callbacks.c @@ -0,0 +1,364 @@ +/* PXE callback mechanisms. This file contains only the portions + * specific to i386: i.e. the low-level mechanisms for calling in from + * an NBP to the PXE stack and for starting an NBP from the PXE stack. + */ + +#ifdef PXE_EXPORT + +#include "etherboot.h" +#include "callbacks.h" +#include "realmode.h" +#include "pxe.h" +#include "pxe_callbacks.h" +#include "pxe_export.h" +#include "hidemem.h" +#include <stdarg.h> + +#define INSTALLED(x) ( (typeof(&x)) ( (void*)(&x) \ + - &pxe_callback_interface \ + + (void*)&pxe_stack->arch_data ) ) +#define pxe_intercept_int1a INSTALLED(_pxe_intercept_int1a) +#define pxe_intercepted_int1a INSTALLED(_pxe_intercepted_int1a) +#define pxe_pxenv_location INSTALLED(_pxe_pxenv_location) +#define INT1A_VECTOR ( (segoff_t*) ( phys_to_virt( 4 * 0x1a ) ) ) + +/* The overall size of the PXE stack is ( sizeof(pxe_stack_t) + + * pxe_callback_interface_size + rm_callback_interface_size ). + * Unfortunately, this isn't a compile-time constant, since + * {pxe,rm}_callback_interface_size depend on the length of the + * assembly code in these interfaces. + * + * We used to have a function pxe_stack_size() which returned this + * value. However, it actually needs to be a link-time constant, so + * that it can appear in the UNDIROMID structure in romprefix.S. We + * therefore export the three component sizes as absolute linker + * symbols, get the linker to add them together and generate a new + * absolute symbol _pxe_stack_size. We then import this value into a + * C variable pxe_stack_size, for access from C code. + */ + +/* gcc won't let us use extended asm outside a function (compiler + * bug), ao we have to put these asm statements inside a dummy + * function. + */ +static void work_around_gcc_bug ( void ) __attribute__ ((used)); +static void work_around_gcc_bug ( void ) { + /* Export sizeof(pxe_stack_t) as absolute linker symbol */ + __asm__ ( ".globl _pxe_stack_t_size" ); + __asm__ ( ".equ _pxe_stack_t_size, %c0" + : : "i" (sizeof(pxe_stack_t)) ); +} +/* Import _pxe_stack_size absolute linker symbol into C variable */ +extern int pxe_stack_size; +__asm__ ( "pxe_stack_size: .long _pxe_stack_size" ); + +/* Utility routine: byte checksum + */ +uint8_t byte_checksum ( void *address, size_t size ) { + unsigned int i, sum = 0; + + for ( i = 0; i < size; i++ ) { + sum += ((uint8_t*)address)[i]; + } + return (uint8_t)sum; +} + +/* install_pxe_stack(): install PXE stack. + * + * Use base = NULL for auto-allocation of base memory + * + * IMPORTANT: no further allocation of base memory should take place + * before the PXE stack is removed. This is to work around a small + * but important deficiency in the PXE specification. + */ +pxe_stack_t * install_pxe_stack ( void *base ) { + pxe_t *pxe; + pxenv_t *pxenv; + void *pxe_callback_code; + void (*pxe_in_call_far)(void); + void (*pxenv_in_call_far)(void); + void *rm_callback_code; + void *e820mangler_code; + void *end; + + /* If already installed, just return */ + if ( pxe_stack != NULL ) return pxe_stack; + + /* Allocate base memory if requested to do so + */ + if ( base == NULL ) { + base = allot_base_memory ( pxe_stack_size ); + if ( base == NULL ) return NULL; + } + + /* Round address up to 16-byte physical alignment */ + pxe_stack = (pxe_stack_t *) + ( phys_to_virt ( ( virt_to_phys(base) + 0xf ) & ~0xf ) ); + /* Zero out allocated stack */ + memset ( pxe_stack, 0, sizeof(*pxe_stack) ); + + /* Calculate addresses for portions of the stack */ + pxe = &(pxe_stack->pxe); + pxenv = &(pxe_stack->pxenv); + pxe_callback_code = &(pxe_stack->arch_data); + pxe_in_call_far = _pxe_in_call_far + + ( pxe_callback_code - &pxe_callback_interface ); + pxenv_in_call_far = _pxenv_in_call_far + + ( pxe_callback_code - &pxe_callback_interface ); + rm_callback_code = pxe_callback_code + pxe_callback_interface_size; + + e820mangler_code = (void*)(((int)rm_callback_code + + rm_callback_interface_size + 0xf ) & ~0xf); + end = e820mangler_code + e820mangler_size; + + /* Initialise !PXE data structures */ + memcpy ( pxe->Signature, "!PXE", 4 ); + pxe->StructLength = sizeof(*pxe); + pxe->StructRev = 0; + pxe->reserved_1 = 0; + /* We don't yet have an UNDI ROM ID structure */ + pxe->UNDIROMID.segment = 0; + pxe->UNDIROMID.offset = 0; + /* or a BC ROM ID structure */ + pxe->BaseROMID.segment = 0; + pxe->BaseROMID.offset = 0; + pxe->EntryPointSP.segment = SEGMENT(pxe_stack); + pxe->EntryPointSP.offset = (void*)pxe_in_call_far - (void*)pxe_stack; + /* No %esp-compatible entry point yet */ + pxe->EntryPointESP.segment = 0; + pxe->EntryPointESP.offset = 0; + pxe->StatusCallout.segment = -1; + pxe->StatusCallout.offset = -1; + pxe->reserved_2 = 0; + pxe->SegDescCn = 7; + pxe->FirstSelector = 0; + /* PXE specification doesn't say anything about when the stack + * space should get freed. We work around this by claiming it + * as our data segment as well. + */ + pxe->Stack.Seg_Addr = pxe->UNDIData.Seg_Addr = real_mode_stack >> 4; + pxe->Stack.Phy_Addr = pxe->UNDIData.Phy_Addr = real_mode_stack; + pxe->Stack.Seg_Size = pxe->UNDIData.Seg_Size = real_mode_stack_size; + /* Code segment has to be the one containing the data structures... */ + pxe->UNDICode.Seg_Addr = SEGMENT(pxe_stack); + pxe->UNDICode.Phy_Addr = virt_to_phys(pxe_stack); + pxe->UNDICode.Seg_Size = end - (void*)pxe_stack; + /* No base code loaded */ + pxe->BC_Data.Seg_Addr = 0; + pxe->BC_Data.Phy_Addr = 0; + pxe->BC_Data.Seg_Size = 0; + pxe->BC_Code.Seg_Addr = 0; + pxe->BC_Code.Phy_Addr = 0; + pxe->BC_Code.Seg_Size = 0; + pxe->BC_CodeWrite.Seg_Addr = 0; + pxe->BC_CodeWrite.Phy_Addr = 0; + pxe->BC_CodeWrite.Seg_Size = 0; + pxe->StructCksum -= byte_checksum ( pxe, sizeof(*pxe) ); + + /* Initialise PXENV+ data structures */ + memcpy ( pxenv->Signature, "PXENV+", 6 ); + pxenv->Version = 0x201; + pxenv->Length = sizeof(*pxenv); + pxenv->RMEntry.segment = SEGMENT(pxe_stack); + pxenv->RMEntry.offset = (void*)pxenv_in_call_far - (void*)pxe_stack; + pxenv->PMOffset = 0; /* "Do not use" says the PXE spec */ + pxenv->PMSelector = 0; /* "Do not use" says the PXE spec */ + pxenv->StackSeg = pxenv->UNDIDataSeg = real_mode_stack >> 4; + pxenv->StackSize = pxenv->UNDIDataSize = real_mode_stack_size; + pxenv->BC_CodeSeg = 0; + pxenv->BC_CodeSize = 0; + pxenv->BC_DataSeg = 0; + pxenv->BC_DataSize = 0; + /* UNDIData{Seg,Size} set above */ + pxenv->UNDICodeSeg = SEGMENT(pxe_stack); + pxenv->UNDICodeSize = end - (void*)pxe_stack; + pxenv->PXEPtr.segment = SEGMENT(pxe); + pxenv->PXEPtr.offset = OFFSET(pxe); + pxenv->Checksum -= byte_checksum ( pxenv, sizeof(*pxenv) ); + + /* Mark stack as inactive */ + pxe_stack->state = CAN_UNLOAD; + + /* Install PXE and RM callback code and E820 mangler */ + memcpy ( pxe_callback_code, &pxe_callback_interface, + pxe_callback_interface_size ); + install_rm_callback_interface ( rm_callback_code, 0 ); + install_e820mangler ( e820mangler_code ); + + return pxe_stack; +} + +/* Use the UNDI data segment as our real-mode stack. This is for when + * we have been loaded via the UNDI loader + */ +void use_undi_ds_for_rm_stack ( uint16_t ds ) { + forget_real_mode_stack(); + real_mode_stack = virt_to_phys ( VIRTUAL ( ds, 0 ) ); + lock_real_mode_stack = 1; +} + +/* Activate PXE stack (i.e. hook interrupt vectors). The PXE stack + * *can* be used before it is activated, but it really shoudln't. + */ +int hook_pxe_stack ( void ) { + if ( pxe_stack == NULL ) return 0; + if ( pxe_stack->state >= MIDWAY ) return 1; + + /* Hook INT15 handler */ + hide_etherboot(); + + /* Hook INT1A handler */ + *pxe_intercepted_int1a = *INT1A_VECTOR; + pxe_pxenv_location->segment = SEGMENT(pxe_stack); + pxe_pxenv_location->offset = (void*)&pxe_stack->pxenv + - (void*)pxe_stack; + INT1A_VECTOR->segment = SEGMENT(&pxe_stack->arch_data); + INT1A_VECTOR->offset = (void*)pxe_intercept_int1a + - (void*)&pxe_stack->arch_data; + + /* Mark stack as active */ + pxe_stack->state = MIDWAY; + return 1; +} + +/* Deactivate the PXE stack (i.e. unhook interrupt vectors). + */ +int unhook_pxe_stack ( void ) { + if ( pxe_stack == NULL ) return 0; + if ( pxe_stack->state <= CAN_UNLOAD ) return 1; + + /* Restore original INT15 and INT1A handlers */ + *INT1A_VECTOR = *pxe_intercepted_int1a; + if ( !unhide_etherboot() ) { + /* Cannot unhook INT15. We're up the creek without + * even a suitable log out of which to fashion a + * paddle. There are some very badly behaved NBPs + * that will ignore plaintive pleas such as + * PXENV_KEEP_UNDI and just zero out our code anyway. + * This means they end up vapourising an active INT15 + * handler, which is generally not a good thing to do. + */ + return 0; + } + + /* Mark stack as inactive */ + pxe_stack->state = CAN_UNLOAD; + return 1; +} + +/* remove_pxe_stack(): remove PXE stack installed by install_pxe_stack() + */ +void remove_pxe_stack ( void ) { + /* Ensure stack is deactivated, then free up the memory */ + if ( ensure_pxe_state ( CAN_UNLOAD ) ) { + forget_base_memory ( pxe_stack, pxe_stack_size ); + pxe_stack = NULL; + } else { + printf ( "Cannot remove PXE stack!\n" ); + } +} + +/* xstartpxe(): start up a PXE image + */ +int xstartpxe ( void ) { + int nbp_exit; + struct { + reg16_t bx; + reg16_t es; + segoff_t pxe; + } PACKED in_stack; + + /* Set up registers and stack parameters to pass to PXE NBP */ + in_stack.es.word = SEGMENT(&(pxe_stack->pxenv)); + in_stack.bx.word = OFFSET(&(pxe_stack->pxenv)); + in_stack.pxe.segment = SEGMENT(&(pxe_stack->pxe)); + in_stack.pxe.offset = OFFSET(&(pxe_stack->pxe)); + + /* Real-mode trampoline fragment used to jump to PXE NBP + */ + RM_FRAGMENT(jump_to_pxe_nbp, + "popw %bx\n\t" + "popw %es\n\t" + "lcall $" RM_STR(PXE_LOAD_SEGMENT) ", $" RM_STR(PXE_LOAD_OFFSET) "\n\t" + ); + + /* Call to PXE image */ + gateA20_unset(); + nbp_exit = real_call ( jump_to_pxe_nbp, &in_stack, NULL ); + gateA20_set(); + + return nbp_exit; +} + +int pxe_in_call ( in_call_data_t *in_call_data, va_list params ) { + /* i386 calling conventions; the only two defined by Intel's + * PXE spec. + * + * Assembly code must pass a long containing the PXE version + * code (i.e. 0x201 for !PXE, 0x200 for PXENV+) as the first + * parameter after the in_call opcode. This is used to decide + * whether to take parameters from the stack (!PXE) or from + * registers (PXENV+). + */ + uint32_t api_version = va_arg ( params, typeof(api_version) ); + uint16_t opcode; + segoff_t segoff; + t_PXENV_ANY *structure; + + if ( api_version >= 0x201 ) { + /* !PXE calling convention */ + pxe_call_params_t pxe_params + = va_arg ( params, typeof(pxe_params) ); + opcode = pxe_params.opcode; + segoff = pxe_params.segoff; + } else { + /* PXENV+ calling convention */ + opcode = in_call_data->pm->regs.bx; + segoff.segment = in_call_data->rm->seg_regs.es; + segoff.offset = in_call_data->pm->regs.di; + } + structure = VIRTUAL ( segoff.segment, segoff.offset ); + return pxe_api_call ( opcode, structure ); +} + +#ifdef TEST_EXCLUDE_ALGORITHM +/* This code retained because it's a difficult algorithm to tweak with + * confidence + */ +int ___test_exclude ( int start, int len, int estart, int elen, int fixbase ); +void __test_exclude ( int start, int len, int estart, int elen, int fixbase ) { + int newrange = ___test_exclude ( start, len, estart, elen, fixbase ); + int newstart = ( newrange >> 16 ) & 0xffff; + int newlen = ( newrange & 0xffff ); + + printf ( "[%x,%x): excluding [%x,%x) %s gives [%x,%x)\n", + start, start + len, + estart, estart + elen, + ( fixbase == 0 ) ? " " : "fb", + newstart, newstart + newlen ); +} +void _test_exclude ( int start, int len, int estart, int elen ) { + __test_exclude ( start, len, estart, elen, 0 ); + __test_exclude ( start, len, estart, elen, 1 ); +} +void test_exclude ( void ) { + _test_exclude ( 0x8000, 0x1000, 0x0400, 0x200 ); /* before */ + _test_exclude ( 0x8000, 0x1000, 0x9000, 0x200 ); /* after */ + _test_exclude ( 0x8000, 0x1000, 0x7f00, 0x200 ); /* before overlap */ + _test_exclude ( 0x8000, 0x1000, 0x8f00, 0x200 ); /* after overlap */ + _test_exclude ( 0x8000, 0x1000, 0x8000, 0x200 ); /* align start */ + _test_exclude ( 0x8000, 0x1000, 0x8e00, 0x200 ); /* align end */ + _test_exclude ( 0x8000, 0x1000, 0x8100, 0x200 ); /* early overlap */ + _test_exclude ( 0x8000, 0x1000, 0x8d00, 0x200 ); /* late overlap */ + _test_exclude ( 0x8000, 0x1000, 0x7000, 0x3000 ); /* total overlap */ + _test_exclude ( 0x8000, 0x1000, 0x8000, 0x1000 ); /* exact overlap */ +} +#endif /* TEST_EXCLUDE_ALGORITHM */ + +#else /* PXE_EXPORT */ + +/* Define symbols used by the linker scripts, to prevent link errors */ +__asm__ ( ".globl _pxe_stack_t_size" ); +__asm__ ( ".equ _pxe_stack_t_size, 0" ); + +#endif /* PXE_EXPORT */ diff --git a/src/arch/i386/core/pxe_loader.c b/src/arch/i386/core/pxe_loader.c new file mode 100644 index 00000000..1b611891 --- /dev/null +++ b/src/arch/i386/core/pxe_loader.c @@ -0,0 +1,94 @@ +/* + * PXE image loader for Etherboot. + * + * Note: There is no signature check for PXE images because there is + * no signature. Well done, Intel! Consequently, pxe_probe() must be + * called last of all the image_probe() routines, because it will + * *always* claim the image. + */ + +#ifndef PXE_EXPORT +#error PXE_IMAGE requires PXE_EXPORT +#endif + +#include "etherboot.h" +#include "pxe_callbacks.h" +#include "pxe_export.h" +#include "pxe.h" + +unsigned long pxe_load_offset; + +static sector_t pxe_download ( unsigned char *data, + unsigned int len, int eof ); + +static inline os_download_t pxe_probe ( unsigned char *data __unused, + unsigned int len __unused ) { + printf("(PXE)"); + pxe_load_offset = 0; + return pxe_download; +} + +static sector_t pxe_download ( unsigned char *data, + unsigned int len, int eof ) { + unsigned long block_address = PXE_LOAD_ADDRESS + pxe_load_offset; + PXENV_STATUS_t nbp_exit; + + /* Check segment will fit. We can't do this in probe() + * because there's nothing in the non-existent header to tell + * us how long the image is. + */ + if ( ! prep_segment ( block_address, block_address + len, + block_address + len, + pxe_load_offset, pxe_load_offset + len ) ) { + longjmp ( restart_etherboot, -2 ); + } + + /* Load block into memory, continue loading until eof */ + memcpy ( phys_to_virt ( block_address ), data, len ); + pxe_load_offset += len; + if ( ! eof ) { + return 0; + } + + /* Start up PXE NBP */ + done ( 0 ); + + /* Install and activate a PXE stack */ + pxe_stack = install_pxe_stack ( NULL ); + if ( ensure_pxe_state ( READY ) ) { + /* Invoke the NBP */ + nbp_exit = xstartpxe(); + } else { + /* Fake success so we tear down the stack */ + nbp_exit = PXENV_STATUS_SUCCESS; + } + + /* NBP has three exit codes: + * PXENV_STATUS_KEEP_UNDI : keep UNDI and boot next device + * PXENV_STATUS_KEEP_ALL : keep all and boot next device + * anything else : remove all and boot next device + * + * Strictly, we're meant to hand back to the BIOS, but this + * would prevent the useful combination of "PXE NBP fails, so + * let Etherboot try to boot its next device". We therefore + * take liberties. + */ + if ( nbp_exit != PXENV_STATUS_KEEP_UNDI && + nbp_exit != PXENV_STATUS_KEEP_ALL ) { + /* Tear down PXE stack */ + remove_pxe_stack(); + } + + /* Boot next device. Under strict PXE compliance, exit back + * to the BIOS, otherwise let Etherboot move to the next + * device. + */ +#ifdef PXE_STRICT + longjmp ( restart_etherboot, 255 ); +#else + longjmp ( restart_etherboot, 4 ); +#endif + + /* Never reached; avoid compiler warning */ + return ( 0 ); +} diff --git a/src/arch/i386/core/realmode.c b/src/arch/i386/core/realmode.c new file mode 100644 index 00000000..ef4ede86 --- /dev/null +++ b/src/arch/i386/core/realmode.c @@ -0,0 +1,148 @@ +/* Real-mode interface: C portions. + * + * Initial version by Michael Brown <mbrown@fensystems.co.uk>, January 2004. + */ + +#include "etherboot.h" +#include "realmode.h" +#include "segoff.h" + +#define RM_STACK_SIZE ( 0x1000 ) + +/* gcc won't let us use extended asm outside a function (compiler + * bug), ao we have to put these asm statements inside a dummy + * function. + */ +static void work_around_gcc_bug ( void ) __attribute__ ((used)); +static void work_around_gcc_bug ( void ) { + /* Export _real_mode_stack_size as absolute linker symbol */ + __asm__ ( ".globl _real_mode_stack_size" ); + __asm__ ( ".equ _real_mode_stack_size, %c0" : : "i" (RM_STACK_SIZE) ); +} + +/* While Etherboot remains in base memory the real-mode stack is + * placed in the Etherboot main stack. The first allocation or + * deallocation of base memory will cause a 'proper' real-mode stack + * to be allocated. This will happen before Etherboot is relocated to + * high memory. + */ +uint32_t real_mode_stack = 0; +size_t real_mode_stack_size = RM_STACK_SIZE; +int lock_real_mode_stack = 0; /* Set to make stack immobile */ + +/* Make a call to a real-mode code block. + */ + +/* These is the structure that exists on the stack as the paramters + * passed in to _real_call. We pass a pointer to this struct to + * prepare_real_call(), to save stack space. + */ +typedef struct { + void *fragment; + int fragment_len; + void *in_stack; + int in_stack_len; + void *out_stack; + int out_stack_len; +} real_call_params_t; + +uint32_t prepare_real_call ( real_call_params_t *p, + int local_stack_len, char *local_stack ) { + char *stack_base; + char *stack_end; + char *stack; + char *s; + prot_to_real_params_t *p2r_params; + real_to_prot_params_t *r2p_params; + + /* Work out where we're putting the stack */ + if ( virt_to_phys(local_stack) < 0xa0000 ) { + /* Current stack is in base memory. We can therefore + * use it directly, with a constraint on the size that + * we don't know; assume that we can use up to + * real_mode_stack_size. (Not a valid assumption, but + * it will do). + */ + stack_end = local_stack + local_stack_len; + stack_base = stack_end - real_mode_stack_size; + } else { + if (!real_mode_stack) { + allot_real_mode_stack(); + } + /* Use the allocated real-mode stack in base memory. + * This has fixed start and end points. + */ + stack_base = phys_to_virt(real_mode_stack); + stack_end = stack_base + real_mode_stack_size; + } + s = stack = stack_end - local_stack_len; + + /* Compile input stack and trampoline code to stack */ + if ( p->in_stack_len ) { + memcpy ( s, p->in_stack, p->in_stack_len ); + s += p->in_stack_len; + } + memcpy ( s, _prot_to_real_prefix, prot_to_real_prefix_size ); + s += prot_to_real_prefix_size; + p2r_params = (prot_to_real_params_t*) ( s - sizeof(*p2r_params) ); + memcpy ( s, p->fragment, p->fragment_len ); + s += p->fragment_len; + memcpy ( s, _real_to_prot_suffix, real_to_prot_suffix_size ); + s += real_to_prot_suffix_size; + r2p_params = (real_to_prot_params_t*) ( s - sizeof(*r2p_params) ); + + /* Set parameters within compiled stack */ + p2r_params->ss = p2r_params->cs = SEGMENT ( stack_base ); + p2r_params->esp = virt_to_phys ( stack ); + p2r_params->r2p_params = virt_to_phys ( r2p_params ); + r2p_params->out_stack = ( p->out_stack == NULL ) ? + 0 : virt_to_phys ( p->out_stack ); + r2p_params->out_stack_len = p->out_stack_len; + + return virt_to_phys ( stack + p->in_stack_len ); +} + + +/* Parameters are not genuinely unused; they are passed to + * prepare_real_call() as part of a real_call_params_t struct. + */ +uint16_t _real_call ( void *fragment, int fragment_len, + void *in_stack __unused, int in_stack_len, + void *out_stack __unused, int out_stack_len __unused ) { + uint16_t retval; + + /* This code is basically equivalent to + * + * uint32_t trampoline; + * char local_stack[ in_stack_len + prot_to_real_prefix_size + + * fragment_len + real_to_prot_suffix_size ]; + * trampoline = prepare_real_call ( &fragment, local_stack ); + * __asm__ ( "call _virt_to_phys\n\t" + * "call %%eax\n\t" + * "call _phys_to_virt\n\t" + * : "=a" (retval) : "0" (trampoline) ); + * + * but implemented in assembly to avoid problems with not + * being certain exactly how gcc handles %esp. + */ + + __asm__ ( "pushl %%ebp\n\t" + "movl %%esp, %%ebp\n\t" /* %esp preserved via %ebp */ + "subl %%ecx, %%esp\n\t" /* space for inline RM stack */ + "pushl %%esp\n\t" /* set up RM stack */ + "pushl %%ecx\n\t" + "pushl %%eax\n\t" + "call prepare_real_call\n\t" /* %eax = trampoline addr */ + "addl $12, %%esp\n\t" + "call _virt_to_phys\n\t" /* switch to phys addr */ + "call *%%eax\n\t" /* call to trampoline */ + "call _phys_to_virt\n\t" /* switch to virt addr */ + "movl %%ebp, %%esp\n\t" /* restore %esp & %ebp */ + "popl %%ebp\n\t" + : "=a" ( retval ) + : "0" ( &fragment ) + , "c" ( ( ( in_stack_len + prot_to_real_prefix_size + + fragment_len + real_to_prot_suffix_size ) + + 0x3 ) & ~0x3 ) ); + return retval; +} diff --git a/src/arch/i386/core/realmode_asm.S b/src/arch/i386/core/realmode_asm.S new file mode 100644 index 00000000..28a5bfed --- /dev/null +++ b/src/arch/i386/core/realmode_asm.S @@ -0,0 +1,695 @@ +/* Real-mode interface: assembly-language portions. + * + * Initial version by Michael Brown <mbrown@fensystems.co.uk>, January 2004. + */ + +#include "realmode.h" +#include "callbacks.h" + +#if 1 /* CODE16 */ + +#define BOCHSBP xchgw %bx,%bx + +#define NUM_PUSHA_REGS (8) +#define NUM_SEG_REGS (6) + + .text + .arch i386 + .section ".text16.nocompress", "ax", @progbits + .code16 + + .equ CR0_PE,1 + +#ifdef GAS291 +#define DATA32 data32; +#define ADDR32 addr32; +#define LJMPI(x) ljmp x +#else +#define DATA32 data32 +#define ADDR32 addr32 +/* newer GAS295 require #define LJMPI(x) ljmp *x */ +#define LJMPI(x) ljmp x +#endif + +/**************************************************************************** + * REAL-MODE CALLBACK INTERFACE + * + * This must be copied down to base memory in order for external + * programs to be able to make calls in to Etherboot. Store the + * current physical address of Etherboot (i.e. virt_to_phys(_text)) in + * (uint32_t)rm_etherboot_location, then copy + * (uint16_t)rm_callback_interface_size bytes starting at + * &((void)rm_callback_interface). + * + * There are two defined entry points: + * Offset RM_IN_CALL = 0 Near call entry point + * Offset RM_IN_CALL_FAR = 2 Far call entry point + * + * Note that the routines _prot_to_real and _real_to_prot double as + * trampoline fragments for external calls (calls from Etherboot to + * real-mode code). _prot_to_real does not automatically re-enable + * interrupts; this is to allow for the potential of using Etherboot + * code as an ISR. _real_to_prot does automatically disable + * interrupts, since we don't have a protected-mode IDT. + **************************************************************************** + */ + + .globl rm_callback_interface + .code16 +rm_callback_interface: + .globl _rm_in_call +_rm_in_call: + jmp _real_in_call + .globl _rm_in_call_far +_rm_in_call_far: + jmp _real_in_call_far + +/**************************************************************************** + * _real_in_call + * + * Parameters: + * 16-bit real-mode near/far return address (implicit from [l]call + * to routine) Other parameters as for _in_call_far(). + * + * This routine will convert the 16-bit real-mode far return address + * to a 32-bit real-mode far return address, switch to protected mode + * using _real_to_prot and call in to _in_call_far. + **************************************************************************** + */ + +#define RIC_PRESERVE ( 8 ) +#define RIC_OFFSET_CALLADDR ( RIC_PRESERVE ) +#define RIC_OFFSET_CALLADDR_E ( RIC_OFFSET_CALLADDR + 4 ) +#define RIC_OFFSET_CONTADDR ( RIC_OFFSET_CALLADDR_E ) +#define RIC_OFFSET_CONTADDR_E ( RIC_OFFSET_CONTADDR + 4 ) +#define RIC_OFFSET_OPCODE ( RIC_OFFSET_CONTADDR_E ) +#define RIC_OFFSET_OPCODE_E ( RIC_OFFSET_OPCODE + 4 ) +#define RIC_OFFSET_SEG_REGS ( RIC_OFFSET_OPCODE_E ) +#define RIC_OFFSET_SEG_REGS_E ( RIC_OFFSET_SEG_REGS + ( NUM_SEG_REGS * 2 ) ) +#define RIC_OFFSET_PAD ( RIC_OFFSET_SEG_REGS_E ) +#define RIC_OFFSET_PAD_E ( RIC_OFFSET_PAD + 2 ) +#define RIC_OFFSET_FLAGS ( RIC_OFFSET_PAD_E ) +#define RIC_OFFSET_FLAGS_E ( RIC_OFFSET_FLAGS + 2 ) +#define RIC_OFFSET_RETADDR ( RIC_OFFSET_FLAGS_E ) +#define RIC_OFFSET_RETADDR_E ( RIC_OFFSET_RETADDR + 4 ) +#define RIC_OFFSET_ORIG_OPCODE ( RIC_OFFSET_RETADDR_E ) +#define RIC_INSERT_LENGTH ( RIC_OFFSET_OPCODE_E - RIC_OFFSET_CALLADDR ) + + .code16 +_real_in_call: + /* Expand near return address to far return address + */ + pushw %ax /* Extend stack, store %ax */ + pushfw + pushw %bp + movw %sp, %bp + movw %cs, %ax + xchgw %ax, 6(%bp) + xchgw %ax, 4(%bp) /* also restores %ax */ + popw %bp + popfw + /* Fall through to _real_in_call_far */ + +_real_in_call_far: + /* Store flags and pad */ + pushfw + pushw %ax + + /* Store segment registers. Order matches that of seg_regs_t */ + pushw %gs + pushw %fs + pushw %es + pushw %ds + pushw %ss + pushw %cs + + /* Switch to protected mode */ + call _real_to_prot + .code32 + + /* Allow space for expanded stack */ + subl $RIC_INSERT_LENGTH, %esp + + /* Store temporary registers */ + pushl %ebp + pushl %eax + + /* Copy opcode, set EB_CALL_FROM_REAL_MODE and EP_SKIP_OPCODE. + * Copy it because _in_call() and i386_in_call() expect it at + * a fixed position, not as part of the va_list. + */ + movl RIC_OFFSET_ORIG_OPCODE(%esp), %eax + orl $(EB_CALL_FROM_REAL_MODE|EB_SKIP_OPCODE), %eax + movl %eax, RIC_OFFSET_OPCODE(%esp) + + /* Set up call and return addresses */ + call 1f +1: popl %ebp + subl $1b, %ebp /* %ebp = offset */ + movl rm_etherboot_location(%ebp), %eax /* Etherboot phys addr */ + subl $_text, %eax + addl $_in_call, %eax /* _in_call phys addr */ + movl %eax, RIC_OFFSET_CALLADDR(%esp) + leal 2f(%ebp), %eax /* continuation address */ + movl %eax, RIC_OFFSET_CONTADDR(%esp) + + /* Restore temporary registers */ + popl %eax + popl %ebp + + /* Call to _in_call */ + ret + /* opcode will be popped automatically thanks to EB_SKIP_OPCODE */ + +2: /* Continuation point */ + call _prot_to_real /* Return to real mode */ + /* Note: the first two words of our segment register store + * happens to be exactly what we need to pass as parameters to + * _prot_to_real. + */ + .code16 + popw %ds /* Restore segment registers */ + popw %ds /* (skip cs&ss since these */ + popw %ds /* have already been set by */ + popw %es /* _prot_to_real */ + popw %fs + popw %gs + addw $2, %sp /* skip pad */ + + /* Check for EB_SKIP_OPCODE */ + pushw %bp + movw %sp, %bp + testl $EB_SKIP_OPCODE, 6(%bp) + popw %bp + jnz 1f + /* Normal return */ + popfw /* Restore interrupt status */ + lret /* Back to caller */ +1: /* Return and skip opcode */ + popfw + lret $4 + +/**************************************************************************** + * rm_etherboot_location: the current physical location of Etherboot. + * Needed so that real-mode callback routines can locate Etherboot. + **************************************************************************** + */ + .globl rm_etherboot_location +rm_etherboot_location: .long 0 + +/**************************************************************************** + * _prot_to_real_prefix + * + * Trampoline fragment. Switch from 32-bit protected mode with flat + * physical addresses to 16-bit real mode. Store registers in the + * trampoline for restoration by _real_to_prot_suffix. Switch to + * stack in base memory. + **************************************************************************** + */ + + .globl _prot_to_real_prefix + .code32 +_prot_to_real_prefix: + /* Registers to preserve */ + pushl %ebx + pushl %esi + pushl %edi + pushl %ebp + + /* Calculate offset */ + call 1f +1: popl %ebp + subl $1b, %ebp /* %ebp = offset for labels in p2r*/ + + /* Preserve registers and return address in r2p_params */ + movl p2r_r2p_params(%ebp), %ebx + subl $r2p_params, %ebx /* %ebx = offset for labels in r2p */ + popl r2p_ebp(%ebx) + popl r2p_edi(%ebx) + popl r2p_esi(%ebx) + popl r2p_ebx(%ebx) + popl r2p_ret_addr(%ebx) + movl %esp, r2p_esp(%ebx) + + /* Switch stacks */ + movl p2r_esp(%ebp), %esp + + /* Switch to real mode */ + pushl p2r_segments(%ebp) + call _prot_to_real + .code16 + addw $4, %sp + + /* Fall through to next trampoline fragment */ + jmp _prot_to_real_prefix_end + +/**************************************************************************** + * _prot_to_real + * + * Switch from 32-bit protected mode with flat physical addresses to + * 16-bit real mode. Stack and code must be in base memory when + * called. %cs, %ss, %eip, %esp are changed to real-mode values, + * other segment registers are destroyed, all other registers are + * preserved. Interrupts are *not* enabled. + * + * Parameters: + * %cs Real-mode code segment (word) + * %ss Real-mode stack segment (word) + **************************************************************************** + */ + +#define P2R_PRESERVE ( 12 ) +#define P2R_OFFSET_RETADDR ( P2R_PRESERVE ) +#define P2R_OFFSET_RETADDR_E ( P2R_OFFSET_RETADDR + 4 ) +#define P2R_OFFSET_CS ( P2R_OFFSET_RETADDR_E ) +#define P2R_OFFSET_CS_E ( P2R_OFFSET_CS + 2 ) +#define P2R_OFFSET_SS ( P2R_OFFSET_CS_E ) +#define P2R_OFFSET_SS_E ( P2R_OFFSET_SS + 2 ) + + .globl _prot_to_real + .code32 +_prot_to_real: + /* Preserve registers */ + pushl %ebp + pushl %ebx + pushl %eax + + /* Calculate offset */ + call 1f +1: popl %ebp + subl $1b, %ebp /* %ebp = offset for labels in p2r*/ + + /* Set up GDT with real-mode limits and appropriate bases for + * real-mode %cs and %ss. Set up protected-mode continuation + * point on stack. + */ + /* Fixup GDT */ + leal p2r_gdt(%ebp), %eax + movl %eax, p2r_gdt_addr(%ebp) + + /* Calculate CS base address: set GDT code segment, adjust + * return address, set up continuation address on stack. + */ + movzwl P2R_OFFSET_CS(%esp), %eax + shll $4, %eax + /* Change return address to real-mode far address */ + subl %eax, P2R_OFFSET_RETADDR(%esp) + movl %eax, %ebx + shrl $4, %ebx + movw %bx, (P2R_OFFSET_RETADDR+2)(%esp) + /* First real mode address */ + movl %eax, %ebx + shrl $4, %ebx + pushw %bx + leal 3f(%ebp), %ebx + subl %eax, %ebx + pushw %bx + /* Continuation address */ + pushl $(p2r_rmcs - p2r_gdt) + leal 2f(%ebp), %ebx + subl %eax, %ebx + pushl %ebx + /* Code segment in GDT */ + movw %ax, (p2r_rmcs+2)(%ebp) + shrl $16, %eax /* Remainder of cs base addr */ + movb %al, (p2r_rmcs+4)(%ebp) + movb %ah, (p2r_rmcs+7)(%ebp) + + /* Calculate SS base address: set GDT data segment, retain to + * use for adjusting %esp. + */ + movzwl (12+P2R_OFFSET_SS)(%esp), %eax /* Set ss base address */ + shll $4, %eax + movw %ax, (p2r_rmds+2)(%ebp) + movl %eax, %ebx + shrl $16, %ebx + movb %bl, (p2r_rmds+4)(%ebp) + movb %bh, (p2r_rmds+7)(%ebp) + + /* Load GDT */ + lgdt p2r_gdt(%ebp) + /* Reload all segment registers and adjust %esp */ + movw $(p2r_rmds - p2r_gdt), %bx /* Pmode DS */ + movw %bx, %ss + subl %eax, %esp /* %esp now less than 0x10000 */ + movw %bx, %ds + movw %bx, %es + movw %bx, %fs + movw %bx, %gs + lret /* %cs:eip */ +2: /* Segment registers now have 16-bit limits. */ + .code16 + + /* Switch to real mode */ + movl %cr0, %ebx + andb $0!CR0_PE, %bl + movl %ebx, %cr0 + + /* Make intersegment jmp to flush the processor pipeline + * and reload %cs:%eip (to clear upper 16 bits of %eip). + */ + lret +3: + + /* Load real-mode segment value to %ss. %sp already OK */ + shrl $4, %eax + movw %ax, %ss + + /* Restore registers */ + popl %eax + popl %ebx + popl %ebp + + /* Return to caller in real-mode */ + lret + +#ifdef FLATTEN_REAL_MODE +#define RM_LIMIT_16_19__AVL__SIZE__GRANULARITY 0x8f +#else +#define RM_LIMIT_16_19__AVL__SIZE__GRANULARITY 0x00 +#endif + +p2r_gdt: +p2r_gdtarg: +p2r_gdt_limit: .word p2r_gdt_end - p2r_gdt - 1 +p2r_gdt_addr: .long 0 +p2r_gdt_padding: .word 0 +p2r_rmcs: + /* 16 bit real mode code segment */ + .word 0xffff,(0&0xffff) + .byte (0>>16),0x9b,RM_LIMIT_16_19__AVL__SIZE__GRANULARITY,(0>>24) +p2r_rmds: + /* 16 bit real mode data segment */ + .word 0xffff,(0&0xffff) + .byte (0>>16),0x93,RM_LIMIT_16_19__AVL__SIZE__GRANULARITY,(0>>24) +p2r_gdt_end: + + /* This is the end of the trampoline prefix code. When used + * as a prefix, fall through to the following code in the + * trampoline. + */ +p2r_params: /* Structure must match prot_to_real_params_t in realmode.h */ +p2r_esp: .long 0 +p2r_segments: +p2r_cs: .word 0 +p2r_ss: .word 0 +p2r_r2p_params: .long 0 + .globl _prot_to_real_prefix_end +_prot_to_real_prefix_end: + + .globl _prot_to_real_prefix_size + .equ _prot_to_real_prefix_size, _prot_to_real_prefix_end - _prot_to_real_prefix + .globl prot_to_real_prefix_size +prot_to_real_prefix_size: + .word _prot_to_real_prefix_size + +/**************************************************************************** + * _real_to_prot_suffix + * + * Trampoline fragment. Switch from 16-bit real-mode to 32-bit + * protected mode with flat physical addresses. Copy returned stack + * parameters to output_stack. Restore registers preserved by + * _prot_to_real_prefix. Restore stack to previous location. + **************************************************************************** + */ + + .globl _real_to_prot_suffix + .code16 +_real_to_prot_suffix: + + /* Switch to protected mode */ + call _real_to_prot + .code32 + + /* Calculate offset */ + call 1f +1: popl %ebp + subl $1b, %ebp /* %ebp = offset for labels in r2p */ + + /* Copy stack to out_stack */ + movl r2p_out_stack(%ebp), %edi + movl r2p_out_stack_len(%ebp), %ecx + movl %esp, %esi + cld + rep movsb + + /* Switch back to original stack */ + movl r2p_esp(%ebp), %esp + + /* Restore registers and return */ + pushl r2p_ret_addr(%ebp) /* Set up return address on stack */ + movl r2p_ebx(%ebp), %ebx + movl r2p_esi(%ebp), %esi + movl r2p_edi(%ebp), %edi + movl r2p_ebp(%ebp), %ebp + ret + +/**************************************************************************** + * _real_to_prot + * + * Switch from 16-bit real-mode to 32-bit protected mode with flat + * physical addresses. All segment registers are destroyed, %eip and + * %esp are changed to flat physical values, all other registers are + * preserved. Interrupts are disabled. + * + * Parameters: none + **************************************************************************** + */ + +#define R2P_PRESERVE ( 12 ) +#define R2P_OFFSET_RETADDR ( R2P_PRESERVE ) +#define R2P_OFFSET_ORIG_RETADDR ( R2P_OFFSET_RETADDR + 2 ) + + .globl _real_to_prot + .code16 +_real_to_prot: + /* Disable interrupts */ + cli + /* zero extend the return address */ + pushw $0 + + /* Preserve registers */ + pushl %ebp + pushl %ebx + pushl %eax + + /* Convert 16-bit real-mode near return address to + * 32-bit pmode physical near return address + */ + movw %sp, %bp + xorl %ebx, %ebx + push %cs + popw %bx + movw %bx, %ds + shll $4, %ebx + movzwl %ss:R2P_OFFSET_ORIG_RETADDR(%bp), %eax + addl %ebx, %eax + movl %eax, %ss:(R2P_OFFSET_RETADDR)(%bp) + + /* Store the code segment physical base address in %ebp */ + movl %ebx, %ebp + + /* Find the offset within the code segment that I am running at */ + xorl %ebx, %ebx + call 1f +1: popw %bx + + /* Set up GDT */ + leal (r2p_gdt-1b)(%bx), %eax /* %ds:ebx = %ds:bx = &(r2p_gdt) */ + addl %ebp, %eax /* %eax = &r2p_gdt (physical) */ + movl %eax, %ds:(r2p_gdt-1b+2)(%bx) /* Set phys. addr. in r2p_gdt */ + + /* Compute the first protected mode physical address */ + leal (2f-1b)(%bx), %eax + addl %ebp, %eax + movl %eax, %ds:(r2p_paddr-1b)(%bx) + + /* Calculate new %esp */ + xorl %eax, %eax + push %ss + popw %ax + shll $4, %eax + movzwl %sp, %ebp + addl %eax, %ebp /* %ebp = new %esp */ + + /* Load GDT */ + DATA32 lgdt %ds:(r2p_gdt-1b)(%bx) /* Load GDT */ + + /* Switch to protected mode */ + movl %cr0, %eax + orb $CR0_PE, %al + movl %eax, %cr0 + + /* flush prefetch queue, and reload %cs:%eip */ + DATA32 ljmp %ds:(r2p_paddr-1b)(%bx) + .code32 +2: + + /* Load segment registers, adjust %esp */ + movw $(r2p_pmds-r2p_gdt), %ax + movw %ax, %ss + movl %ebp, %esp + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + + /* Restore registers */ + popl %eax + popl %ebx + popl %ebp + + /* return to caller */ + ret + +r2p_gdt: + .word r2p_gdt_end - r2p_gdt - 1 /* limit */ + .long 0 /* addr */ + .word 0 +r2p_pmcs: + /* 32 bit protected mode code segment, physical addresses */ + .word 0xffff, 0 + .byte 0, 0x9f, 0xcf, 0 +r2p_pmds: + /* 32 bit protected mode data segment, physical addresses */ + .word 0xffff,0 + .byte 0,0x93,0xcf,0 +r2p_gdt_end: + +r2p_paddr: + .long 2b + .word r2p_pmcs - r2p_gdt, 0 + + + /* This is the end of the trampoline suffix code. + */ +r2p_params: /* Structure must match real_to_prot_params_t in realmode.h */ +r2p_ret_addr: .long 0 +r2p_esp: .long 0 +r2p_ebx: .long 0 +r2p_esi: .long 0 +r2p_edi: .long 0 +r2p_ebp: .long 0 +r2p_out_stack: .long 0 +r2p_out_stack_len: .long 0 + .globl _real_to_prot_suffix_end +_real_to_prot_suffix_end: + + .globl _real_to_prot_suffix_size + .equ _real_to_prot_suffix_size, _real_to_prot_suffix_end - _real_to_prot_suffix + .globl real_to_prot_suffix_size +real_to_prot_suffix_size: + .word _real_to_prot_suffix_size + +rm_callback_interface_end: + + .globl _rm_callback_interface_size + .equ _rm_callback_interface_size, rm_callback_interface_end - rm_callback_interface + .globl rm_callback_interface_size +rm_callback_interface_size: + .word _rm_callback_interface_size + +/**************************************************************************** + * END OF REAL-MODE CALLBACK INTERFACE + **************************************************************************** + */ + + +#ifdef PXE_EXPORT +/**************************************************************************** + * PXE CALLBACK INTERFACE + * + * Prepend this to rm_callback_interface to create a real-mode PXE + * callback interface. + **************************************************************************** + */ + .section ".text16", "ax", @progbits + .globl pxe_callback_interface + .code16 +pxe_callback_interface: + +/* Macro to calculate offset of labels within code segment in + * installed copy of code. + */ +#define INSTALLED(x) ( (x) - pxe_callback_interface ) + +/**************************************************************************** + * PXE entry points (!PXE and PXENV+ APIs) + **************************************************************************** + */ + /* in_call mechanism for !PXE API calls */ + .globl _pxe_in_call_far +_pxe_in_call_far: + /* Prepend "PXE API call" and "API version 0x201" to stack */ + pushl $0x201 + jmp 1f + /* in_call mechanism for PXENV+ API calls */ + .globl _pxenv_in_call_far +_pxenv_in_call_far: + /* Prepend "PXE API call" and "API version 0x200" to stack */ + pushl $0x200 +1: pushl $EB_OPCODE_PXE + /* Perform real-mode in_call */ + call pxe_rm_in_call + /* Return */ + addw $8, %sp + lret + +/**************************************************************************** + * PXE installation check (INT 1A) code + **************************************************************************** + */ + .globl _pxe_intercept_int1a +_pxe_intercept_int1a: + pushfw + cmpw $0x5650, %ax + jne 2f +1: /* INT 1A,5650 - Intercept */ + popfw + /* Set up return values according to PXE spec: */ + movw $0x564e, %ax /* AX := 564Eh (VN) */ + pushw %cs:INSTALLED(_pxe_pxenv_segment) + popw %es /* ES:BX := &(PXENV+ structure) */ + movw %cs:INSTALLED(_pxe_pxenv_offset), %bx + clc /* CF is cleared */ + lret $2 /* 'iret' without reloading flags */ +2: /* INT 1A,other - Do not intercept */ + popfw + ljmp %cs:*INSTALLED(_pxe_intercepted_int1a) + + .globl _pxe_intercepted_int1a +_pxe_intercepted_int1a: .word 0,0 + .globl _pxe_pxenv_location +_pxe_pxenv_location: +_pxe_pxenv_offset: .word 0 +_pxe_pxenv_segment: .word 0 + +pxe_rm_in_call: +pxe_attach_rm: + /* rm_callback_interface must be appended here */ + +pxe_callback_interface_end: + + .globl _pxe_callback_interface_size + .equ _pxe_callback_interface_size, pxe_callback_interface_end - pxe_callback_interface + .globl pxe_callback_interface_size +pxe_callback_interface_size: + .word _pxe_callback_interface_size + +#else /* PXE_EXPORT */ + +/* Define symbols used by the linker scripts, to prevent link errors */ + .globl _pxe_callback_interface_size + .equ _pxe_callback_interface_size, 0 + +#endif /* PXE_EXPORT */ + +#else /* CODE16 */ + +/* Define symbols used by the linker scripts, to prevent link errors */ + .globl _rm_callback_interface_size + .equ _rm_callback_interface_size, 0 + .globl _pxe_callback_interface_size + .equ _pxe_callback_interface_size, 0 + +#endif /* CODE16 */ diff --git a/src/arch/i386/core/start16.S b/src/arch/i386/core/start16.S new file mode 100644 index 00000000..72fcfbfb --- /dev/null +++ b/src/arch/i386/core/start16.S @@ -0,0 +1,285 @@ +/***************************************************************************** + * + * THIS FILE IS NOW OBSOLETE. + * + * The functions of this file are now placed in init.S. + * + ***************************************************************************** + */ + +#ifndef PCBIOS +#error "16bit code is only supported with the PCBIOS" +#endif + +#define CODE_SEG 0x08 +#define DATA_SEG 0x10 + +#define EXEC_IN_SITU_MAGIC 0x45524548 /* 'HERE' */ + + .equ CR0_PE, 1 + +#ifdef GAS291 +#define DATA32 data32; +#define ADDR32 addr32; +#define LJMPI(x) ljmp x +#else +#define DATA32 data32 +#define ADDR32 addr32 +/* newer GAS295 require #define LJMPI(x) ljmp *x */ +#define LJMPI(x) ljmp x +#endif + +/***************************************************************************** + * + * start16 : move payload to desired area of memory, set up for exit + * back to prefix, set up for 32-bit code. + * + * Enter (from prefix) with es:di = 0x4552:0x4548 if you want to + * prevent start16 from moving the payload. There are three + * motivations for moving the payload: + * + * 1. It may be in ROM, in which case we need to move it to RAM. + * 2. Whatever loaded us probably didn't know about our memory usage + * beyond the end of the image file. We should claim this memory + * before using it. + * + * Unless the prefix instructs us otherwise we will move the payload to: + * + * An area of memory claimed from the BIOS via 40:13. + * + * We use the main Etherboot stack (within the image target) as our + * stack; we don't rely on the prefix passing us a stack usable for + * anything other than the prefix's return address. The (first 512 + * bytes of the) prefix code segment is copied to a safe archive + * location. + * + * When we return to the prefix (from start32), we copy this code back + * to a new area of memory, restore the prefix's ss:sp and ljmp back + * to the copy of the prefix. The prefix will see a return from + * start16 *but* may be executing at a new location. Code following + * the lcall to start16 must therefore be position-independent and + * must also be within [cs:0000,cs:01ff]. We make absolutely no + * guarantees about the stack contents when the prefix regains + * control. + * + * Trashes just about all registers, including all the segment + * registers. + * + ***************************************************************************** + */ + + .text + .code16 + .arch i386 + .org 0 + .globl _start16 +_start16: + +/***************************************************************************** + * Work out where we are going to place our image (image = optional + * decompressor + runtime). Exit this stage with %ax containing the + * runtime target address divided by 16 (i.e. a real-mode segment + * address). + ***************************************************************************** + */ + movw %es, %ax + cmpw $(EXEC_IN_SITU_MAGIC >> 16), %ax + jne exec_moved + cmpw $(EXEC_IN_SITU_MAGIC & 0xffff), %di + jne exec_moved +exec_in_situ: + /* Prefix has warned us not to move the payload. Simply + * calculate where the image is going to end up, so we can + * work out where to put our stack. + */ + movw %cs, %ax + addw $((payload-_start16)/16), %ax + jmp 99f +exec_moved: + /* Claim an area of base memory from the BIOS and put the + * payload there. arch_relocated_to() will deal with freeing + * up this memory once we've relocated to high memory. + */ + movw $0x40, %ax + movw %ax, %es + movw %es:(0x13), %ax /* FBMS in kb to %ax */ + shlw $6, %ax /* ... in paragraphs */ + subw $__image_size_pgh, %ax /* Subtract space for image */ + shrw $6, %ax /* Round down to nearest kb */ + movw %ax, %es:(0x13) /* ...and claim memory from BIOS */ + shlw $6, %ax +99: + /* At this point %ax contains the segment address for the + * start of the image (image = optional decompressor + runtime). + */ + +/***************************************************************************** + * Set up stack in start32's stack space within the place we're going + * to copy Etherboot to, reserve space for GDT, copy return address + * from prefix stack, store prefix stack address + ***************************************************************************** + */ + popl %esi /* Return address */ + mov %ss, %bx /* %es:di = prefix stack address */ + mov %bx, %es /* (*after* pop of return address) */ + movw %sp, %di + movw $__offset_stack_pgh, %bx /* Set up Etherboot stack */ + addw %ax, %bx + movw %bx, %ss + movw $__stack_size, %sp + subw $(_gdt_end - _gdt), %sp /* Reserve space for GDT */ + movw %sp, %bp /* Record GDT location */ + /* Set up i386_rm_in_call_data_t structure on stack. This is + * the same structure as is set up by rm_in_call. + */ + pushl $0 /* Dummy opcode */ + pushl %esi /* Prefix return address */ + pushfw /* Flags */ + pushw %di /* Prefix %sp */ + pushw %gs /* Segment registers */ + pushw %fs + pushw %es + pushw %ds + pushw %es /* Prefix %ss */ + pushw %cs + /* Stack is now 32-bit aligned */ + + /* %ax still contains image target segment address */ + +/***************************************************************************** + * Calculate image target and prefix code physical addresses, store on stack + * for use in copy routine. + ***************************************************************************** + */ + movzwl %es:-2(%di), %ebx /* Prefix code segment */ + shll $4, %ebx + pushl %ebx /* Prefix code physical address */ + movzwl %ax, %edi /* Image target segment */ + shll $4, %edi + pushl %edi /* Image target physical address */ + +/***************************************************************************** + * Transition to 32-bit protected mode. Set up all segment + * descriptors to use flat physical addresses. + ***************************************************************************** + */ + /* Copy gdt to area reserved on stack + */ + push %cs /* GDT source location -> %ds:%si */ + pop %ds + mov $(_gdt - _start16), %si + push %ss /* GDT target location -> %es:%di */ + pop %es + mov %bp, %di + mov $(_gdt_end - _gdt), %cx + cld + rep movsb /* Copy GDT to stack */ + movl %ss, %eax + shll $4, %eax + movzwl %bp, %ebx + addl %eax, %ebx /* Physical addr of GDT copy -> %ebx */ + movl %ebx, 2(%bp) /* Fill in addr field in GDT */ + + /* Compute the offset I am running at. + */ + movl %cs, %ebx + shll $4, %ebx /* %ebx = offset for start16 symbols */ + + /* Switch to 32bit protected mode. + */ + cli /* Disable interrupts */ + lgdt (%bp) /* Load GDT from stack */ + movl %cr0, %eax /* Set protected mode bit */ + orb $CR0_PE, %al + movl %eax, %cr0 + movl %ss, %eax /* Convert stack pointer to 32bit */ + shll $4, %eax + movzwl %sp, %esp + addl %eax, %esp + movl $DATA_SEG, %eax /* Reload the segment registers */ + movl %eax, %ds + movl %eax, %es + movl %eax, %ss + movl %eax, %fs + movl %eax, %gs + /* Flush prefetch queue, and reload %cs:%eip by effectively ljmping + * to code32_start. Do the jump via pushl and lret because the text + * may not be writable/ + */ + pushl $CODE_SEG + ADDR32 leal (code32_start-_start16)(%ebx), %eax + pushl %eax + DATA32 lret /* DATA32 needed, because we're still in 16-bit mode */ + +_gdt: +gdtarg: + .word _gdt_end - _gdt - 1 /* limit */ + .long 0 /* addr */ + .word 0 +_pmcs: + /* 32 bit protected mode code segment */ + .word 0xffff, 0 + .byte 0, 0x9f, 0xcf, 0 +_pmds: + /* 32 bit protected mode data segment */ + .word 0xffff,0 + .byte 0,0x93,0xcf,0 +_gdt_end: + + .code32 +code32_start: + +/***************************************************************************** + * Copy payload to target location. Do the copy backwards, since if + * there's overlap with a forward copy then it means start16 is going + * to get trashed during the copy anyway... + ***************************************************************************** + */ + popl %edi /* Image target physical address */ + pushl %edi + leal (payload-_start16)(%ebx), %esi /* Image source physical addr */ + movl $__payload_size, %ecx /* Payload size (not image size) */ + addl %ecx, %edi /* Start at last byte (length - 1) */ + decl %edi + addl %ecx, %esi + decl %esi + std /* Backward copy of image */ + rep movsb + cld + popl %edi /* Restore image target physical address */ + leal __decompressor_uncompressed(%edi), %ebx + subl $_text, %ebx /* %ebx = offset for runtime symbols */ + +/***************************************************************************** + * Copy prefix to storage area within Etherboot image. + ***************************************************************************** + */ + popl %esi /* Prefix source physical address */ + pushl %edi + leal _prefix_copy(%ebx), %edi /* Prefix copy phys. addr. */ + leal _eprefix_copy(%ebx), %ecx + subl %edi, %ecx /* Prefix copy size */ + rep movsb /* Forward copy of prefix */ + popl %edi /* Restore image target physical address */ + +/***************************************************************************** + * Record base memory used by Etherboot image + ***************************************************************************** + */ + movl %edi, _prefix_image_basemem (%ebx) + +/***************************************************************************** + * Jump to start of the image (i.e. the decompressor, or start32 if + * non-compressed). + ***************************************************************************** + */ + pushl $0 /* Inform start32 that exit path is 16-bit */ + jmpl *%edi /* Jump to image */ + + .balign 16 + /* Etherboot needs to be 16byte aligned or data that + * is virtually aligned is no longer physically aligned + * which is just nasty in general. 16byte alignment + * should be sufficient though. + */ +payload: diff --git a/src/arch/i386/core/start16.lds b/src/arch/i386/core/start16.lds new file mode 100644 index 00000000..544fc78f --- /dev/null +++ b/src/arch/i386/core/start16.lds @@ -0,0 +1,8 @@ +/* When linking with an uncompressed image, these symbols are not + * defined so we provide them here. + */ + +__decompressor_uncompressed = 0 ; +__decompressor__start = 0 ; + +INCLUDE arch/i386/core/start16z.lds diff --git a/src/arch/i386/core/start16z.lds b/src/arch/i386/core/start16z.lds new file mode 100644 index 00000000..711bcf7b --- /dev/null +++ b/src/arch/i386/core/start16z.lds @@ -0,0 +1,65 @@ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) + +/* Linker-generated symbols are prefixed with a double underscore. + * Decompressor symbols are prefixed with __decompressor_. All other + * symbols are the same as in the original object file, i.e. the + * runtime addresses. + */ + +ENTRY(_start16) + +SECTIONS { + .text : { + *(.text) + } + .payload : { + __payload_start = .; + *(.data) + __payload_end = .; + } + + /* _payload_size is the size of the binary image appended to + * start16, in bytes. + */ + __payload_size = __payload_end - __payload_start ; + + /* _size is the size of the runtime image + * (start32 + the C code), in bytes. + */ + __size = _end - _start ; + + /* _decompressor_size is the size of the decompressor, in + * bytes. For a non-compressed image, start16.lds sets + * _decompressor_uncompressed = _decompressor__start = 0. + */ + __decompressor_size = __decompressor_uncompressed - __decompressor__start ; + + /* image__size is the total size of the image, after + * decompression and including the decompressor if applicable. + * It is therefore the amount of memory that start16's payload + * needs in order to execute, in bytes. + */ + __image_size = __size + __decompressor_size ; + + /* Amount to add to runtime symbols to obtain the offset of + * that symbol within the image. + */ + __offset_adjust = __decompressor_size - _start ; + + /* Calculations for the stack + */ + __stack_size = _estack - _stack ; + __offset_stack = _stack + __offset_adjust ; + + /* Some symbols will be larger than 16 bits but guaranteed to + * be multiples of 16. We calculate them in paragraphs and + * export these symbols which can be used in 16-bit code + * without risk of overflow. + */ + __image_size_pgh = ( __image_size / 16 ); + __start_pgh = ( _start / 16 ); + __decompressor_size_pgh = ( __decompressor_size / 16 ); + __offset_stack_pgh = ( __offset_stack / 16 ); +} + diff --git a/src/arch/i386/core/start32.S b/src/arch/i386/core/start32.S new file mode 100644 index 00000000..6dc3f203 --- /dev/null +++ b/src/arch/i386/core/start32.S @@ -0,0 +1,767 @@ +/* #defines because ljmp wants a number, probably gas bug */ +/* .equ KERN_CODE_SEG,_pmcs-_gdt */ +#define KERN_CODE_SEG 0x08 + .equ KERN_DATA_SEG,_pmds-_gdt +/* .equ REAL_CODE_SEG,_rmcs-_gdt */ +#define REAL_CODE_SEG 0x18 + .equ REAL_DATA_SEG,_rmds-_gdt + .equ FLAT_CODE_SEG,_pmcs2-_gdt + .equ FLAT_DATA_SEG,_pmds2-_gdt + .equ CR0_PE,1 +#ifdef CONFIG_X86_64 + .equ LM_CODE_SEG, _lmcs-_gdt + .equ LM_DATA_SEG, _lmds-_gdt +#endif + + .equ MSR_K6_EFER, 0xC0000080 + .equ EFER_LME, 0x00000100 + .equ X86_CR4_PAE, 0x00000020 + .equ CR0_PG, 0x80000000 + +#ifdef GAS291 +#define DATA32 data32; +#define ADDR32 addr32; +#define LJMPI(x) ljmp x +#else +#define DATA32 data32 +#define ADDR32 addr32 +/* newer GAS295 require #define LJMPI(x) ljmp *x */ +#define LJMPI(x) ljmp x +#endif + +#define BOCHSBP xchgw %bx, %bx + +#include "callbacks.h" +#define NUM_PUSHA_REGS (8) +#define NUM_SEG_REGS (6) + +/* + * NOTE: if you write a subroutine that is called from C code (gcc/egcs), + * then you only have to take care of %ebx, %esi, %edi and %ebp. These + * registers must not be altered under any circumstance. All other registers + * may be clobbered without any negative side effects. If you don't follow + * this rule then you'll run into strange effects that only occur on some + * gcc versions (because the register allocator may use different registers). + * + * All the data32 prefixes for the ljmp instructions are necessary, because + * the assembler emits code with a relocation address of 0. This means that + * all destinations are initially negative, which the assembler doesn't grok, + * because for some reason negative numbers don't fit into 16 bits. The addr32 + * prefixes are there for the same reasons, because otherwise the memory + * references are only 16 bit wide. Theoretically they are all superfluous. + * One last note about prefixes: the data32 prefixes on all call _real_to_prot + * instructions could be removed if the _real_to_prot function is changed to + * deal correctly with 16 bit return addresses. I tried it, but failed. + */ + +/************************************************************************** + * START + * + * This file is no longer enterered from the top. init.S will jump to + * either _in_call or _rm_in_call, depending on the processor mode + * when init.S was entered. + **************************************************************************/ + .text + .arch i386 + .code32 + +/************************************************************************** +_IN_CALL - make a call in to Etherboot. +**************************************************************************/ + +/* There are two 32-bit entry points: _in_call and _in_call_far, for + * near calls and far calls respectively. Both should be called with + * flat physical addresses. They will result in a call to the C + * routine in_call(); see there for API details. + * + * Note that this routine makes fairly heavy use of the stack and no + * use of fixed data areas. This is because it must be re-entrant; + * there may be more than one concurrent call in to Etherboot. + */ + +#define IC_OFFSET_VA_LIST_PTR ( 0 ) +#define IC_OFFSET_VA_LIST_PTR_E ( IC_OFFSET_VA_LIST_PTR + 4 ) +#define IC_OFFSET_REGISTERS ( IC_OFFSET_VA_LIST_PTR_E ) +#define IC_OFFSET_REGISTERS_E ( IC_OFFSET_REGISTERS + ( NUM_PUSHA_REGS * 4 ) ) +#define IC_OFFSET_SEG_REGS ( IC_OFFSET_REGISTERS_E ) +#define IC_OFFSET_SEG_REGS_E ( IC_OFFSET_SEG_REGS + ( NUM_SEG_REGS * 2 ) ) +#define IC_OFFSET_GDT ( IC_OFFSET_SEG_REGS_E ) +#define IC_OFFSET_GDT_E ( IC_OFFSET_GDT + 8 ) +#define IC_OFFSET_FLAGS ( IC_OFFSET_GDT_E ) +#define IC_OFFSET_FLAGS_E ( IC_OFFSET_FLAGS + 4 ) +#define IC_OFFSET_RETADDR ( IC_OFFSET_FLAGS_E ) +#define IC_OFFSET_RETADDR_E ( IC_OFFSET_RETADDR + 8 ) +#define IC_OFFSET_ORIG_STACK ( IC_OFFSET_RETADDR ) +#define IC_OFFSET_OPCODE ( IC_OFFSET_ORIG_STACK + 8 ) +#define IC_OFFSET_OPCODE_E ( IC_OFFSET_OPCODE + 4 ) +#define IC_OFFSET_VA_LIST ( IC_OFFSET_OPCODE_E ) + + .code32 + .globl _in_call + .globl _in_call_far +_in_call: + /* Expand to far return address */ + pushl %eax /* Store %eax */ + xorl %eax, %eax + movw %cs, %ax + xchgl %eax, 4(%esp) /* 4(%esp) = %cs, %eax = ret addr */ + xchgl %eax, 0(%esp) /* 0(%esp) = ret addr, restore %eax */ +_in_call_far: + /* Store flags */ + pushfl + /* Store the GDT */ + subl $8, %esp + sgdt 0(%esp) + /* Store segment register values */ + pushw %gs + pushw %fs + pushw %es + pushw %ds + pushw %ss + pushw %cs + /* Store general-purpose register values */ + pushal + /* Replace %esp in store with physical %esp value on entry */ + leal (IC_OFFSET_ORIG_STACK - IC_OFFSET_REGISTERS)(%esp), %eax + movl %eax, (IC_OFFSET_REGISTERS - IC_OFFSET_REGISTERS + 12)(%esp) + /* Store va_list pointer (physical address) */ + leal (IC_OFFSET_VA_LIST - IC_OFFSET_VA_LIST_PTR_E)(%esp), %eax + pushl %eax + /* IC_OFFSET_*(%esp) are now valid */ + + /* Switch to virtual addresses */ + call _phys_to_virt + + /* Fixup the va_list pointer */ + movl virt_offset, %ebp + subl %ebp, IC_OFFSET_VA_LIST_PTR(%esp) + + /* Check opcode for EB_USE_INTERNAL_STACK flag */ + movl IC_OFFSET_OPCODE(%esp), %eax + testl $EB_USE_INTERNAL_STACK, %eax + je 2f + /* Use internal stack flag set */ + /* Check %esp is not already in internal stack range */ + leal _stack, %esi /* %esi = bottom of internal stack */ + leal _estack, %edi /* %edi = top of internal stack */ + cmpl %esi, %esp + jb 1f + cmpl %edi, %esp + jbe 2f +1: /* %esp not currently in internal stack range */ + movl %esp, %esi /* %esi = original stack */ + movl $IC_OFFSET_OPCODE_E, %ecx /* %ecx = length to transfer */ + subl %ecx, %edi /* %edi = internal stack pos */ + movl %edi, %esp /* = new %esp */ + rep movsb /* Copy data to internal stack */ +2: + + /* Call to C code */ + call i386_in_call + + /* Set %eax (return code from C) in registers structure on + * stack, so that we return it to the caller. + */ + movl %eax, (IC_OFFSET_REGISTERS + 28)(%esp) + + /* Calculate physical continuation address */ + movl virt_offset, %ebp + movzwl (IC_OFFSET_SEG_REGS + 0)(%esp), %eax /* %cs */ + movzwl (IC_OFFSET_SEG_REGS + 2)(%esp), %ebx /* %ss */ + pushl %eax /* Continuation segment */ + leal 1f(%ebp), %eax + pushl %eax /* Continuation offset */ + + /* Restore caller's GDT */ + cli /* Temporarily disable interrupts */ + lgdt (8+IC_OFFSET_GDT)(%esp) + /* Reset %ss and adjust %esp */ + movw %bx, %ss + addl %ebp, %esp + lret /* Reload %cs:eip, flush prefetch */ +1: + + /* Skip va_list ptr */ + popl %eax + /* Reload general-purpose registers to be returned */ + popal + /* Reload segment registers as passed in from caller */ + popw %gs + popw %fs + popw %es + popw %ds + addl $(4+8), %esp /* Skip %cs, %ss and GDT (already reloaded) */ + /* Restore flags (including revert of interrupt status) */ + popfl + + /* Restore physical %esp from entry. It will only be + * different if EB_USE_INTERNAL_STACK was specified. + */ + movl ( 12 + IC_OFFSET_REGISTERS - IC_OFFSET_RETADDR )(%esp), %esp + + /* Check for EB_SKIP_OPCODE */ + pushfl + testl $EB_SKIP_OPCODE, 12(%esp) + jnz 1f + /* Normal return */ + popfl + lret +1: /* Return and skip opcode */ + popfl + lret $4 + +/************************************************************************** +RELOCATE_TO - relocate etherboot to the specified address +**************************************************************************/ + .globl relocate_to +relocate_to: + /* Save the callee save registers */ + pushl %ebp + pushl %esi + pushl %edi + + /* Compute the virtual destination address */ + movl 16(%esp), %edi # dest + subl virt_offset, %edi + + + /* Compute the new value of virt_offset */ + movl 16(%esp), %ebp # virt_offset + subl $_text, %ebp + + /* Fixup the gdt */ + pushl $_pmcs + pushl %ebp # virt_offset + call set_seg_base + addl $8, %esp + + /* Fixup gdtarg */ + leal _gdt(%ebp), %eax + movl %eax, gdtarg +2 + + /* Fixup virt_offset */ + movl %ebp, virt_offset + + /* Load the move parameters */ + movl $_text, %esi + movl $_end, %ecx + subl %esi, %ecx + + /* Move etherboot uses %esi, %edi, %ecx */ + rep + movsb + + /* Reload the gdt */ + cs + lgdt gdtarg + + /* Reload %cs */ + ljmp $KERN_CODE_SEG, $1f +1: + /* reload other segment registers */ + movl $KERN_DATA_SEG, %eax + movl %eax,%ds + movl %eax,%es + movl %eax,%ss + movl %eax,%fs + movl %eax,%gs + + /* Restore the callee save registers */ + popl %edi + popl %esi + popl %ebp + + /* return */ + ret + +/************************************************************************** +XSTART32 - Transfer control to the kernel just loaded +**************************************************************************/ + .globl xstart32 +xstart32: + /* Save the callee save registers */ + movl %ebp, os_regs + 32 + movl %esi, os_regs + 36 + movl %edi, os_regs + 40 + movl %ebx, os_regs + 44 + + /* save the return address */ + popl %eax + movl %eax, os_regs + 48 + + /* save the stack pointer */ + movl %esp, os_regs + 52 + + /* Get the new destination address */ + popl %ecx + + /* Store the physical address of xend on the stack */ + movl $xend32, %ebx + addl virt_offset, %ebx + pushl %ebx + + /* Store the destination address on the stack */ + pushl $FLAT_CODE_SEG + pushl %ecx + + /* Cache virt_offset */ + movl virt_offset, %ebp + + /* Switch to using physical addresses */ + call _virt_to_phys + + /* Save the target stack pointer */ + movl %esp, os_regs + 12(%ebp) + leal os_regs(%ebp), %esp + + /* Store the pointer to os_regs */ + movl %esp, os_regs_ptr(%ebp) + + /* Load my new registers */ + popal + movl (-32 + 12)(%esp), %esp + + /* Jump to the new kernel + * The lret switches to a flat code segment + */ + lret + + .balign 4 + .globl xend32 +xend32: + /* Fixup %eflags */ + nop + cli + cld + + /* Load %esp with &os_regs + virt_offset */ + .byte 0xbc /* movl $0, %esp */ +os_regs_ptr: + .long 0 + + /* Save the result registers */ + addl $32, %esp + pushal + + /* Compute virt_offset */ + movl %esp, %ebp + subl $os_regs, %ebp + + /* Load the stack pointer */ + movl 52(%esp), %esp + + /* Enable the virtual addresses */ + leal _phys_to_virt(%ebp), %eax + call *%eax + + /* Restore the callee save registers */ + movl os_regs + 32, %ebp + movl os_regs + 36, %esi + movl os_regs + 40, %edi + movl os_regs + 44, %ebx + movl os_regs + 48, %edx + movl os_regs + 52, %esp + + /* Get the C return value */ + movl os_regs + 28, %eax + + jmpl *%edx + +#ifdef CONFIG_X86_64 + .arch sledgehammer +/************************************************************************** +XSTART_lm - Transfer control to the kernel just loaded in long mode +**************************************************************************/ + .globl xstart_lm +xstart_lm: + /* Save the callee save registers */ + pushl %ebp + pushl %esi + pushl %edi + pushl %ebx + + /* Cache virt_offset && (virt_offset & 0xfffff000) */ + movl virt_offset, %ebp + movl %ebp, %ebx + andl $0xfffff000, %ebx + + /* Switch to using physical addresses */ + call _virt_to_phys + + /* Initialize the page tables */ + /* Level 4 */ + leal 0x23 + pgt_level3(%ebx), %eax + leal pgt_level4(%ebx), %edi + movl %eax, (%edi) + + /* Level 3 */ + leal 0x23 + pgt_level2(%ebx), %eax + leal pgt_level3(%ebx), %edi + movl %eax, 0x00(%edi) + addl $4096, %eax + movl %eax, 0x08(%edi) + addl $4096, %eax + movl %eax, 0x10(%edi) + addl $4096, %eax + movl %eax, 0x18(%edi) + + /* Level 2 */ + movl $0xe3, %eax + leal pgt_level2(%ebx), %edi + leal 16384(%edi), %esi +pgt_level2_loop: + movl %eax, (%edi) + addl $8, %edi + addl $0x200000, %eax + cmp %esi, %edi + jne pgt_level2_loop + + /* Point at the x86_64 page tables */ + leal pgt_level4(%ebx), %edi + movl %edi, %cr3 + + + /* Setup for the return from 64bit mode */ + /* 64bit align the stack */ + movl %esp, %ebx /* original stack pointer + 16 */ + andl $0xfffffff8, %esp + + /* Save original stack pointer + 16 */ + pushl %ebx + + /* Save virt_offset */ + pushl %ebp + + /* Setup for the jmp to 64bit long mode */ + leal start_lm(%ebp), %eax + movl %eax, 0x00 + start_lm_addr(%ebp) + movl $LM_CODE_SEG, %eax + movl %eax, 0x04 + start_lm_addr(%ebp) + + /* Setup for the jump out of 64bit long mode */ + leal end_lm(%ebp), %eax + movl %eax, 0x00 + end_lm_addr(%ebp) + movl $FLAT_CODE_SEG, %eax + movl %eax, 0x04 + end_lm_addr(%ebp) + + /* Enable PAE mode */ + movl %cr4, %eax + orl $X86_CR4_PAE, %eax + movl %eax, %cr4 + + /* Enable long mode */ + movl $MSR_K6_EFER, %ecx + rdmsr + orl $EFER_LME, %eax + wrmsr + + /* Start paging, entering 32bit compatiblity mode */ + movl %cr0, %eax + orl $CR0_PG, %eax + movl %eax, %cr0 + + /* Enter 64bit long mode */ + ljmp *start_lm_addr(%ebp) + .code64 +start_lm: + /* Load 64bit data segments */ + movl $LM_DATA_SEG, %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %ss + + andq $0xffffffff, %rbx + /* Get the address to jump to */ + movl 20(%rbx), %edx + andq $0xffffffff, %rdx + + /* Get the argument pointer */ + movl 24(%rbx), %ebx + andq $0xffffffff, %rbx + + /* Jump to the 64bit code */ + call *%rdx + + /* Preserve the result */ + movl %eax, %edx + + /* Fixup %eflags */ + cli + cld + + /* Switch to 32bit compatibility mode */ + ljmp *end_lm_addr(%rip) + + .code32 +end_lm: + /* Disable paging */ + movl %cr0, %eax + andl $~CR0_PG, %eax + movl %eax, %cr0 + + /* Disable long mode */ + movl $MSR_K6_EFER, %ecx + rdmsr + andl $~EFER_LME, %eax + wrmsr + + /* Disable PAE */ + movl %cr4, %eax + andl $~X86_CR4_PAE, %eax + movl %eax, %cr4 + + /* Compute virt_offset */ + popl %ebp + + /* Compute the original stack pointer + 16 */ + popl %ebx + movl %ebx, %esp + + /* Enable the virtual addresses */ + leal _phys_to_virt(%ebp), %eax + call *%eax + + /* Restore the callee save registers */ + popl %ebx + popl %esi + popl %edi + popl %ebp + + /* Get the C return value */ + movl %edx, %eax + + /* Return */ + ret + + .arch i386 +#endif /* CONFIG_X86_64 */ + +/************************************************************************** +SETJMP - Save stack context for non-local goto +**************************************************************************/ + .globl setjmp +setjmp: + movl 4(%esp),%ecx /* jmpbuf */ + movl 0(%esp),%edx /* return address */ + movl %edx,0(%ecx) + movl %ebx,4(%ecx) + movl %esp,8(%ecx) + movl %ebp,12(%ecx) + movl %esi,16(%ecx) + movl %edi,20(%ecx) + movl $0,%eax + ret + +/************************************************************************** +LONGJMP - Non-local jump to a saved stack context +**************************************************************************/ + .globl longjmp +longjmp: + movl 4(%esp),%edx /* jumpbuf */ + movl 8(%esp),%eax /* result */ + movl 0(%edx),%ecx + movl 4(%edx),%ebx + movl 8(%edx),%esp + movl 12(%edx),%ebp + movl 16(%edx),%esi + movl 20(%edx),%edi + cmpl $0,%eax + jne 1f + movl $1,%eax +1: movl %ecx,0(%esp) + ret + +/************************************************************************** +_VIRT_TO_PHYS - Transition from virtual to physical addresses + Preserves all preservable registers and flags +**************************************************************************/ + .globl _virt_to_phys +_virt_to_phys: + pushfl + pushl %ebp + pushl %eax + movl virt_offset, %ebp /* Load virt_offset */ + addl %ebp, 12(%esp) /* Adjust the return address */ + + /* reload the code segment */ + pushl $FLAT_CODE_SEG + leal 1f(%ebp), %eax + pushl %eax + lret + +1: + /* reload other segment registers */ + movl $FLAT_DATA_SEG, %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %ss + addl %ebp, %esp /* Adjust the stack pointer */ + movl %eax, %fs + movl %eax, %gs + + popl %eax + popl %ebp + popfl + ret + + +/************************************************************************** +_PHYS_TO_VIRT - Transition from using physical to virtual addresses + Preserves all preservable registers and flags +**************************************************************************/ + .globl _phys_to_virt +_phys_to_virt: + pushfl + pushl %ebp + pushl %eax + + call 1f +1: popl %ebp + subl $1b, %ebp + movl %ebp, virt_offset(%ebp) + + /* Fixup the gdt */ + leal _pmcs(%ebp), %eax + pushl %eax + pushl %ebp + call set_seg_base + addl $8, %esp + + /* Fixup gdtarg */ + leal _gdt(%ebp), %eax + movl %eax, (gdtarg+2)(%ebp) + + /* Load the global descriptor table */ + cli + lgdt %cs:gdtarg(%ebp) + ljmp $KERN_CODE_SEG, $1f +1: + /* reload other segment regsters */ + movl $KERN_DATA_SEG, %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %ss + subl %ebp, %esp /* Adjust the stack pointer */ + movl %eax, %fs + movl %eax, %gs + + subl %ebp, 12(%esp) /* Adjust the return address */ + popl %eax + popl %ebp + popfl + ret + + +/************************************************************************** +SET_SEG_BASE - Set the base address of a segment register +**************************************************************************/ + .globl set_seg_base +set_seg_base: + pushl %eax + pushl %ebx + movl 12(%esp), %eax /* %eax = base address */ + movl 16(%esp), %ebx /* %ebx = &code_descriptor */ + movw %ax, (0+2)(%ebx) /* CS base bits 0-15 */ + movw %ax, (8+2)(%ebx) /* DS base bits 0-15 */ + shrl $16, %eax + movb %al, (0+4)(%ebx) /* CS base bits 16-23 */ + movb %al, (8+4)(%ebx) /* DS base bits 16-23 */ + movb %ah, (0+7)(%ebx) /* CS base bits 24-31 */ + movb %ah, (8+7)(%ebx) /* DS base bits 24-31 */ + popl %ebx + popl %eax + ret + +/************************************************************************** +GLOBAL DESCRIPTOR TABLE +**************************************************************************/ + .data + .align 4 + + .globl _gdt + .globl gdtarg +_gdt: +gdtarg: + .word _gdt_end - _gdt - 1 /* limit */ + .long _gdt /* addr */ + .word 0 + + .globl _pmcs +_pmcs: + /* 32 bit protected mode code segment */ + .word 0xffff,0 + .byte 0,0x9f,0xcf,0 + +_pmds: + /* 32 bit protected mode data segment */ + .word 0xffff,0 + .byte 0,0x93,0xcf,0 + +_rmcs: + /* 16 bit real mode code segment */ + .word 0xffff,(0&0xffff) + .byte (0>>16),0x9b,0x00,(0>>24) + +_rmds: + /* 16 bit real mode data segment */ + .word 0xffff,(0&0xffff) + .byte (0>>16),0x93,0x00,(0>>24) + +_pmcs2: + /* 32 bit protected mode code segment, base 0 */ + .word 0xffff,0 + .byte 0,0x9f,0xcf,0 + +_pmds2: + /* 32 bit protected mode data segment, base 0 */ + .word 0xffff,0 + .byte 0,0x93,0xcf,0 + +#ifdef CONFIG_X86_64 +_lmcs: + /* 64bit long mode code segment, base 0 */ + .word 0xffff, 0 + .byte 0x00, 0x9f, 0xaf , 0x00 +_lmds: + /* 64bit long mode data segment, base 0 */ + .word 0xffff, 0 + .byte 0x00, 0x93, 0xcf, 0x00 +#endif +_gdt_end: + + /* The initial register contents */ + .balign 4 + .globl initial_regs +initial_regs: + .fill 8, 4, 0 + + /* The virtual address offset */ + .globl virt_offset +virt_offset: + .long 0 + + .section ".stack" + .p2align 3 + /* allocate a 4K stack in the stack segment */ + .globl _stack +_stack: + .space 4096 + .globl _estack +_estack: +#ifdef CONFIG_X86_64 + .section ".bss" + .p2align 12 + /* Include a dummy space in case we are loaded badly aligned */ + .space 4096 + /* Reserve enough space for a page table convering 4GB with 2MB pages */ +pgt_level4: + .space 4096 +pgt_level3: + .space 4096 +pgt_level2: + .space 16384 +start_lm_addr: + .space 8 +end_lm_addr: + .space 8 +#endif diff --git a/src/arch/i386/core/tagged_loader.c b/src/arch/i386/core/tagged_loader.c new file mode 100644 index 00000000..9c6d6c25 --- /dev/null +++ b/src/arch/i386/core/tagged_loader.c @@ -0,0 +1,201 @@ +#include "realmode.h" +#include "segoff.h" + +struct segheader +{ + unsigned char length; + unsigned char vendortag; + unsigned char reserved; + unsigned char flags; + unsigned long loadaddr; + unsigned long imglength; + unsigned long memlength; +}; + +struct imgheader +{ + unsigned long magic; + unsigned long length; /* and flags */ + union + { + segoff_t segoff; + unsigned long location; + } u; + unsigned long execaddr; +}; + +/* Keep all context about loaded image in one place */ +static struct tagged_context +{ + struct imgheader img; /* copy of header */ + unsigned long linlocation; /* addr of header */ + unsigned long last0, last1; /* of prev segment */ + unsigned long segaddr, seglen; /* of current segment */ + unsigned char segflags; + unsigned char first; + unsigned long curaddr; +} tctx; + +#define TAGGED_PROGRAM_RETURNS (tctx.img.length & 0x00000100) /* bit 8 */ +#define LINEAR_EXEC_ADDR (tctx.img.length & 0x80000000) /* bit 31 */ + +static sector_t tagged_download(unsigned char *data, unsigned int len, int eof); +void xstart16 (unsigned long execaddr, segoff_t location, + void *bootp); + +static inline os_download_t tagged_probe(unsigned char *data, unsigned int len) +{ + struct segheader *sh; + unsigned long loc; + if (*((uint32_t *)data) != 0x1B031336L) { + return 0; + } + printf("(NBI)"); + /* If we don't have enough data give up */ + if (len < 512) + return dead_download; + /* Zero all context info */ + memset(&tctx, 0, sizeof(tctx)); + /* Copy first 4 longwords */ + memcpy(&tctx.img, data, sizeof(tctx.img)); + /* Memory location where we are supposed to save it */ + tctx.segaddr = tctx.linlocation = + ((tctx.img.u.segoff.segment) << 4) + tctx.img.u.segoff.offset; + if (!prep_segment(tctx.segaddr, tctx.segaddr + 512, tctx.segaddr + 512, + 0, 512)) { + return dead_download; + } + /* Now verify the segments we are about to load */ + loc = 512; + for(sh = (struct segheader *)(data + + ((tctx.img.length & 0x0F) << 2) + + ((tctx.img.length & 0xF0) >> 2) ); + (sh->length > 0) && ((unsigned char *)sh < data + 512); + sh = (struct segheader *)((unsigned char *)sh + + ((sh->length & 0x0f) << 2) + ((sh->length & 0xf0) >> 2)) ) { + if (!prep_segment( + sh->loadaddr, + sh->loadaddr + sh->imglength, + sh->loadaddr + sh->imglength, + loc, loc + sh->imglength)) { + return dead_download; + } + loc = loc + sh->imglength; + if (sh->flags & 0x04) + break; + } + if (!(sh->flags & 0x04)) + return dead_download; + /* Grab a copy */ + memcpy(phys_to_virt(tctx.segaddr), data, 512); + /* Advance to first segment descriptor */ + tctx.segaddr += ((tctx.img.length & 0x0F) << 2) + + ((tctx.img.length & 0xF0) >> 2); + /* Remember to skip the first 512 data bytes */ + tctx.first = 1; + + return tagged_download; + +} +static sector_t tagged_download(unsigned char *data, unsigned int len, int eof) +{ + int i; + + if (tctx.first) { + tctx.first = 0; + if (len > 512) { + len -= 512; + data += 512; + /* and fall through to deal with rest of block */ + } else + return 0; + } + for (;;) { + if (len == 0) /* Detect truncated files */ + eof = 0; + while (tctx.seglen == 0) { + struct segheader sh; + if (tctx.segflags & 0x04) { + done(1); + if (LINEAR_EXEC_ADDR) { + int result; + /* no gateA20_unset for PM call */ + result = xstart32(tctx.img.execaddr, + virt_to_phys(&loaderinfo), + tctx.linlocation, + virt_to_phys(BOOTP_DATA_ADDR)); + printf("Secondary program returned %d\n", + result); + if (!TAGGED_PROGRAM_RETURNS) { + /* We shouldn't have returned */ + result = -2; + } + if (result == 0) + result = -2; + longjmp(restart_etherboot, result); + + } else { + gateA20_unset(); + xstart16(tctx.img.execaddr, + tctx.img.u.segoff, + BOOTP_DATA_ADDR); + longjmp(restart_etherboot, -2); + } + } + sh = *((struct segheader *)phys_to_virt(tctx.segaddr)); + tctx.seglen = sh.imglength; + if ((tctx.segflags = sh.flags & 0x03) == 0) + tctx.curaddr = sh.loadaddr; + else if (tctx.segflags == 0x01) + tctx.curaddr = tctx.last1 + sh.loadaddr; + else if (tctx.segflags == 0x02) + tctx.curaddr = (Address)(meminfo.memsize * 1024L + + 0x100000L) + - sh.loadaddr; + else + tctx.curaddr = tctx.last0 - sh.loadaddr; + tctx.last1 = (tctx.last0 = tctx.curaddr) + sh.memlength; + tctx.segflags = sh.flags; + tctx.segaddr += ((sh.length & 0x0F) << 2) + + ((sh.length & 0xF0) >> 2); + /* Avoid lock-up */ + if ( sh.length == 0 ) longjmp(restart_etherboot, -2); + } + if ((len <= 0) && !eof) + break; + i = (tctx.seglen > len) ? len : tctx.seglen; + memcpy(phys_to_virt(tctx.curaddr), data, i); + tctx.seglen -= i; + tctx.curaddr += i; + len -= i; + data += i; + } + return 0; +} + +void xstart16 (unsigned long execaddr, segoff_t location, + void *bootp) { + struct { + segoff_t execaddr; + segoff_t location; + segoff_t bootp; + } PACKED in_stack; + + /* AFAICT, execaddr is actually already a segment:offset */ + *((unsigned long *)&in_stack.execaddr) = execaddr; + in_stack.location = location; + in_stack.bootp.segment = SEGMENT(bootp); + in_stack.bootp.offset = OFFSET(bootp); + + RM_FRAGMENT(rm_xstart16, + "popl %eax\n\t" /* Calculated lcall */ + "pushw %cs\n\t" + "call 1f\n1:\tpopw %bp\n\t" + "leaw (2f-1b)(%bp), %bx\n\t" + "pushw %bx\n\t" + "pushl %eax\n\t" + "lret\n2:\n\t" + ); + + real_call ( rm_xstart16, &in_stack, NULL ); +} diff --git a/src/arch/i386/core/video_subr.c b/src/arch/i386/core/video_subr.c new file mode 100644 index 00000000..dccdd97c --- /dev/null +++ b/src/arch/i386/core/video_subr.c @@ -0,0 +1,94 @@ +/* + * + * modified from linuxbios code + * by Cai Qiang <rimy2000@hotmail.com> + * + */ + +#ifdef CONSOLE_DIRECT_VGA + +#include <etherboot.h> +#include <vga.h> + +static char *vidmem; /* The video buffer */ +static int video_line, video_col; + +#define VIDBUFFER 0xB8000 + +static void memsetw(void *s, int c, unsigned int n) +{ + int i; + u16 *ss = (u16 *) s; + + for (i = 0; i < n; i++) { + ss[i] = ( u16 ) c; + } +} + +void video_init(void) +{ + static int inited=0; + + vidmem = (unsigned char *)phys_to_virt(VIDBUFFER); + + if (!inited) { + video_line = 0; + video_col = 0; + + memsetw(vidmem, VGA_ATTR_CLR_WHT, 2*1024); // + + inited=1; + } +} + +static void video_scroll(void) +{ + int i; + + memcpy(vidmem, vidmem + COLS * 2, (LINES - 1) * COLS * 2); + for (i = (LINES - 1) * COLS * 2; i < LINES * COLS * 2; i += 2) + vidmem[i] = ' '; +} + +void vga_putc(unsigned char byte) +{ + if (byte == '\n') { + video_line++; + video_col = 0; + + } else if (byte == '\r') { + video_col = 0; + + } else if (byte == '\b') { + video_col--; + + } else if (byte == '\t') { + video_col += 4; + + } else if (byte == '\a') { + //beep + //beep(500); + + } else { + vidmem[((video_col + (video_line *COLS)) * 2)] = byte; + vidmem[((video_col + (video_line *COLS)) * 2) +1] = VGA_ATTR_CLR_WHT; + video_col++; + } + if (video_col < 0) { + video_col = 0; + } + if (video_col >= COLS) { + video_line++; + video_col = 0; + } + if (video_line >= LINES) { + video_scroll(); + video_line--; + } + // move the cursor + write_crtc((video_col + (video_line *COLS)) >> 8, CRTC_CURSOR_HI); + write_crtc((video_col + (video_line *COLS)) & 0x0ff, CRTC_CURSOR_LO); +} + +#endif + diff --git a/src/arch/i386/core/wince_loader.c b/src/arch/i386/core/wince_loader.c new file mode 100644 index 00000000..f452b659 --- /dev/null +++ b/src/arch/i386/core/wince_loader.c @@ -0,0 +1,273 @@ +#define LOAD_DEBUG 0 + +static int get_x_header(unsigned char *data, unsigned long now); +static void jump_2ep(); +static unsigned char ce_signature[] = {'B', '0', '0', '0', 'F', 'F', '\n',}; +static char ** ep; + +#define BOOT_ARG_PTR_LOCATION 0x001FFFFC + +typedef struct _BOOT_ARGS{ + unsigned char ucVideoMode; + unsigned char ucComPort; + unsigned char ucBaudDivisor; + unsigned char ucPCIConfigType; + + unsigned long dwSig; + #define BOOTARG_SIG 0x544F4F42 + unsigned long dwLen; + + unsigned char ucLoaderFlags; + unsigned char ucEshellFlags; + unsigned char ucEdbgAdapterType; + unsigned char ucEdbgIRQ; + + unsigned long dwEdbgBaseAddr; + unsigned long dwEdbgDebugZone; + unsigned long dwDHCPLeaseTime; + unsigned long dwEdbgFlags; + + unsigned long dwEBootFlag; + unsigned long dwEBootAddr; + unsigned long dwLaunchAddr; + + unsigned long pvFlatFrameBuffer; + unsigned short vesaMode; + unsigned short cxDisplayScreen; + unsigned short cyDisplayScreen; + unsigned short cxPhysicalScreen; + unsigned short cyPhysicalScreen; + unsigned short cbScanLineLength; + unsigned short bppScreen; + + unsigned char RedMaskSize; + unsigned char REdMaskPosition; + unsigned char GreenMaskSize; + unsigned char GreenMaskPosition; + unsigned char BlueMaskSize; + unsigned char BlueMaskPosition; +} BOOT_ARGS; + +BOOT_ARGS BootArgs; + +static struct segment_info{ + unsigned long addr; // Section Address + unsigned long size; // Section Size + unsigned long checksum; // Section CheckSum +} X; + +#define PSIZE (1500) //Max Packet Size +#define DSIZE (PSIZE+12) +static unsigned long dbuffer_available =0; +static unsigned long not_loadin =0; +static unsigned long d_now =0; + +unsigned long entry; +static unsigned long ce_curaddr; + + +static sector_t ce_loader(unsigned char *data, unsigned int len, int eof); +static os_download_t wince_probe(unsigned char *data, unsigned int len) +{ + if (strncmp(ce_signature, data, sizeof(ce_signature)) != 0) { + return 0; + } + printf("(WINCE)"); + return ce_loader; +} + +static sector_t ce_loader(unsigned char *data, unsigned int len, int eof) +{ + static unsigned char dbuffer[DSIZE]; + int this_write = 0; + static int firsttime = 1; + + /* + * new packet in, we have to + * [1] copy data to dbuffer, + * + * update... + * [2] dbuffer_available + */ + memcpy( (dbuffer+dbuffer_available), data, len); //[1] + dbuffer_available += len; // [2] + len = 0; + + d_now = 0; + +#if 0 + printf("dbuffer_available =%ld \n", dbuffer_available); +#endif + + if (firsttime) + { + d_now = sizeof(ce_signature); + printf("String Physical Address = %lx \n", + *(unsigned long *)(dbuffer+d_now)); + + d_now += sizeof(unsigned long); + printf("Image Size = %ld [%lx]\n", + *(unsigned long *)(dbuffer+d_now), + *(unsigned long *)(dbuffer+d_now)); + + d_now += sizeof(unsigned long); + dbuffer_available -= d_now; + + d_now = (unsigned long)get_x_header(dbuffer, d_now); + firsttime = 0; + } + + if (not_loadin == 0) + { + d_now = get_x_header(dbuffer, d_now); + } + + while ( not_loadin > 0 ) + { + /* dbuffer do not have enough data to loading, copy all */ +#if LOAD_DEBUG + printf("[0] not_loadin = [%ld], dbuffer_available = [%ld] \n", + not_loadin, dbuffer_available); + printf("[0] d_now = [%ld] \n", d_now); +#endif + + if( dbuffer_available <= not_loadin) + { + this_write = dbuffer_available ; + memcpy(phys_to_virt(ce_curaddr), (dbuffer+d_now), this_write ); + ce_curaddr += this_write; + not_loadin -= this_write; + + /* reset index and available in the dbuffer */ + dbuffer_available = 0; + d_now = 0; +#if LOAD_DEBUG + printf("[1] not_loadin = [%ld], dbuffer_available = [%ld] \n", + not_loadin, dbuffer_available); + printf("[1] d_now = [%ld], this_write = [%d] \n", + d_now, this_write); +#endif + + // get the next packet... + return (0); + } + + /* dbuffer have more data then loading ... , copy partital.... */ + else + { + this_write = not_loadin; + memcpy(phys_to_virt(ce_curaddr), (dbuffer+d_now), this_write); + ce_curaddr += this_write; + not_loadin = 0; + + /* reset index and available in the dbuffer */ + dbuffer_available -= this_write; + d_now += this_write; +#if LOAD_DEBUG + printf("[2] not_loadin = [%ld], dbuffer_available = [%ld] \n", + not_loadin, dbuffer_available); + printf("[2] d_now = [%ld], this_write = [%d] \n\n", + d_now, this_write); +#endif + + /* dbuffer not empty, proceed processing... */ + + // don't have enough data to get_x_header.. + if ( dbuffer_available < (sizeof(unsigned long) * 3) ) + { +// printf("we don't have enough data remaining to call get_x. \n"); + memcpy( (dbuffer+0), (dbuffer+d_now), dbuffer_available); + return (0); + } + else + { +#if LOAD_DEBUG + printf("with remaining data to call get_x \n"); + printf("dbuffer available = %ld , d_now = %ld\n", + dbuffer_available, d_now); +#endif + d_now = get_x_header(dbuffer, d_now); + } + } + } + return (0); +} + +static int get_x_header(unsigned char *dbuffer, unsigned long now) +{ + X.addr = *(unsigned long *)(dbuffer + now); + X.size = *(unsigned long *)(dbuffer + now + sizeof(unsigned long)); + X.checksum = *(unsigned long *)(dbuffer + now + sizeof(unsigned long)*2); + + if (X.addr == 0) + { + entry = X.size; + done(1); + printf("Entry Point Address = [%lx] \n", entry); + jump_2ep(); + } + + if (!prep_segment(X.addr, X.addr + X.size, X.addr + X.size, 0, 0)) { + longjmp(restart_etherboot, -2); + } + + ce_curaddr = X.addr; + now += sizeof(unsigned long)*3; + + /* re-calculate dbuffer available... */ + dbuffer_available -= sizeof(unsigned long)*3; + + /* reset index of this section */ + not_loadin = X.size; + +#if 1 + printf("\n"); + printf("\t Section Address = [%lx] \n", X.addr); + printf("\t Size = %d [%lx]\n", X.size, X.size); + printf("\t Checksum = %ld [%lx]\n", X.checksum, X.checksum); +#endif +#if LOAD_DEBUG + printf("____________________________________________\n"); + printf("\t dbuffer_now = %ld \n", now); + printf("\t dbuffer available = %ld \n", dbuffer_available); + printf("\t not_loadin = %ld \n", not_loadin); +#endif + + return now; +} + +static void jump_2ep() +{ + BootArgs.ucVideoMode = 1; + BootArgs.ucComPort = 1; + BootArgs.ucBaudDivisor = 1; + BootArgs.ucPCIConfigType = 1; // do not fill with 0 + + BootArgs.dwSig = BOOTARG_SIG; + BootArgs.dwLen = sizeof(BootArgs); + + if(BootArgs.ucVideoMode == 0) + { + BootArgs.cxDisplayScreen = 640; + BootArgs.cyDisplayScreen = 480; + BootArgs.cxPhysicalScreen = 640; + BootArgs.cyPhysicalScreen = 480; + BootArgs.bppScreen = 16; + BootArgs.cbScanLineLength = 1024; + BootArgs.pvFlatFrameBuffer = 0x800a0000; // ollie say 0x98000000 + } + else if(BootArgs.ucVideoMode != 0xFF) + { + BootArgs.cxDisplayScreen = 0; + BootArgs.cyDisplayScreen = 0; + BootArgs.cxPhysicalScreen = 0; + BootArgs.cyPhysicalScreen = 0; + BootArgs.bppScreen = 0; + BootArgs.cbScanLineLength = 0; + BootArgs.pvFlatFrameBuffer = 0; + } + + ep = phys_to_virt(BOOT_ARG_PTR_LOCATION); + *ep= virt_to_phys(&BootArgs); + xstart32(entry); +} diff --git a/src/arch/i386/drivers/net/undi.c b/src/arch/i386/drivers/net/undi.c new file mode 100644 index 00000000..084fc18a --- /dev/null +++ b/src/arch/i386/drivers/net/undi.c @@ -0,0 +1,1458 @@ +/************************************************************************** +Etherboot - BOOTP/TFTP Bootstrap Program +UNDI NIC driver for Etherboot + +This file Copyright (C) 2003 Michael Brown <mbrown@fensystems.co.uk> +of Fen Systems Ltd. (http://www.fensystems.co.uk/). All rights +reserved. + +$Id$ +***************************************************************************/ + +/* + * 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, or (at + * your option) any later version. + */ + +#ifdef PCBIOS + +/* to get some global routines like printf */ +#include "etherboot.h" +/* to get the interface to the body of the program */ +#include "nic.h" +/* to get the PCI support functions, if this is a PCI NIC */ +#include "pci.h" +/* UNDI and PXE defines. Includes pxe.h. */ +#include "undi.h" +/* 8259 PIC defines */ +#include "pic8259.h" +/* Real-mode calls */ +#include "realmode.h" +/* E820 map mangler */ +#include "hidemem.h" + +/* NIC specific static variables go here */ +static undi_t undi = { + .pnp_bios = NULL, + .rom = NULL, + .undi_rom_id = NULL, + .pxe = NULL, + .pxs = NULL, + .xmit_data = NULL, + .base_mem_data = NULL, + .driver_code = NULL, + .driver_code_size = 0, + .driver_data = NULL, + .driver_data_size = 0, + .xmit_buffer = NULL, + .prestarted = 0, + .started = 0, + .initialized = 0, + .opened = 0, + .pci = { 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, NULL }, + .irq = IRQ_NONE +}; + +/* Function prototypes */ +static int allocate_base_mem_data ( void ); +static int free_base_mem_data ( void ); +static int eb_pxenv_undi_shutdown ( void ); +static int eb_pxenv_stop_undi ( void ); +static int undi_unload_base_code ( void ); +static int undi_full_shutdown ( void ); + +/* Trivial/nontrivial IRQ handler selection */ +#ifdef UNDI_NONTRIVIAL_IRQ +static void nontrivial_irq_handler ( void ); +static void nontrivial_irq_handler_end ( void ); +static int install_nontrivial_irq_handler ( irq_t irq ); +static int remove_nontrivial_irq_handler ( irq_t irq ); +static int nontrivial_irq_triggered ( irq_t irq ); +static int copy_nontrivial_irq_handler ( void *target, size_t target_size ); +#define NONTRIVIAL_IRQ_HANDLER_SIZE FRAGMENT_SIZE(nontrivial_irq_handler) +#define install_undi_irq_handler(irq) install_nontrivial_irq_handler(irq) +#define remove_undi_irq_handler(irq) remove_nontrivial_irq_handler(irq) +#define undi_irq_triggered(irq) nontrivial_irq_triggered(irq) +#define UNDI_IRQ_HANDLER_SIZE NONTRIVIAL_IRQ_HANDLER_SIZE +#define copy_undi_irq_handler(dest,size) copy_nontrivial_irq_handler(dest,size) +#else +#define install_undi_irq_handler(irq) install_trivial_irq_handler(irq) +#define remove_undi_irq_handler(irq) remove_trivial_irq_handler(irq) +#define undi_irq_triggered(irq) trivial_irq_triggered(irq) +#define UNDI_IRQ_HANDLER_SIZE TRIVIAL_IRQ_HANDLER_SIZE +#define copy_undi_irq_handler(dest,size) copy_trivial_irq_handler(dest,size) +#endif /* UNDI_NONTRIVIAL_IRQ */ + +/* Size of variable-length data in base_mem_data */ +#define BASE_MEM_VARDATA_SIZE ( UNDI_IRQ_HANDLER_SIZE > e820mangler_size ? \ + UNDI_IRQ_HANDLER_SIZE : e820mangler_size ) + +/************************************************************************** + * Utility functions + **************************************************************************/ + +/* Checksum a block. + */ + +static uint8_t checksum ( void *block, size_t size ) { + uint8_t sum = 0; + uint16_t i = 0; + for ( i = 0; i < size; i++ ) { + sum += ( ( uint8_t * ) block )[i]; + } + return sum; +} + +/* Print the status of a !PXE structure + */ + +static void pxe_dump ( void ) { + printf ( "API %hx:%hx St %hx:%hx UD %hx:%hx UC %hx:%hx " + "BD %hx:%hx BC %hx:%hx\n", + undi.pxe->EntryPointSP.segment, undi.pxe->EntryPointSP.offset, + undi.pxe->Stack.Seg_Addr, undi.pxe->Stack.Seg_Size, + undi.pxe->UNDIData.Seg_Addr, undi.pxe->UNDIData.Seg_Size, + undi.pxe->UNDICode.Seg_Addr, undi.pxe->UNDICode.Seg_Size, + undi.pxe->BC_Data.Seg_Addr, undi.pxe->BC_Data.Seg_Size, + undi.pxe->BC_Code.Seg_Addr, undi.pxe->BC_Code.Seg_Size ); +} + +/* Allocate/free space for structures that must reside in base memory + */ + +static int allocate_base_mem_data ( void ) { + /* Allocate space in base memory. + * Initialise pointers to base memory structures. + */ + if ( undi.base_mem_data == NULL ) { + undi.base_mem_data = + allot_base_memory ( sizeof(undi_base_mem_data_t) + + BASE_MEM_VARDATA_SIZE ); + if ( undi.base_mem_data == NULL ) { + printf ( "Failed to allocate base memory\n" ); + free_base_mem_data(); + return 0; + } + memset ( undi.base_mem_data, 0, sizeof(undi_base_mem_data_t) ); + undi.pxs = &undi.base_mem_data->pxs; + undi.xmit_data = &undi.base_mem_data->xmit_data; + undi.xmit_buffer = undi.base_mem_data->xmit_buffer; + } + return 1; +} + +static int free_base_mem_data ( void ) { + if ( undi.base_mem_data != NULL ) { + forget_base_memory ( undi.base_mem_data, + sizeof(undi_base_mem_data_t) + + BASE_MEM_VARDATA_SIZE ); + undi.base_mem_data = NULL; + undi.pxs = NULL; + undi.xmit_data = NULL; + undi.xmit_buffer = NULL; + copy_undi_irq_handler ( NULL, 0 ); + } + return 1; +} + +static void assemble_firing_squad ( firing_squad_lineup_t *lineup, + void *start, size_t size, + firing_squad_shoot_t shoot ) { + int target; + int index; + int bit; + int start_kb = virt_to_phys(start) >> 10; + int end_kb = ( virt_to_phys(start+size) + (1<<10) - 1 ) >> 10; + + for ( target = start_kb; target <= end_kb; target++ ) { + index = FIRING_SQUAD_TARGET_INDEX ( target ); + bit = FIRING_SQUAD_TARGET_BIT ( target ); + lineup->targets[index] = ( shoot << bit ) | + ( lineup->targets[index] & ~( 1 << bit ) ); + } +} + +static void shoot_targets ( firing_squad_lineup_t *lineup ) { + int shoot_this_target = 0; + int shoot_last_target = 0; + int start_target = 0; + int target; + + for ( target = 0; target <= 640; target++ ) { + shoot_this_target = ( target == 640 ? 0 : + ( 1 << FIRING_SQUAD_TARGET_BIT(target) ) & + lineup->targets[FIRING_SQUAD_TARGET_INDEX(target)] ); + if ( shoot_this_target && !shoot_last_target ) { + start_target = target; + } else if ( shoot_last_target && !shoot_this_target ) { + size_t range_size = ( target - start_target ) << 10; + forget_base_memory ( phys_to_virt( start_target<<10 ), + range_size ); + } + shoot_last_target = shoot_this_target; + } +} + +/* Debug macros + */ + +#ifdef TRACE_UNDI +#define DBG(...) printf ( __VA_ARGS__ ) +#else +#define DBG(...) +#endif + +#define UNDI_STATUS(pxs) ( (pxs)->Status == PXENV_EXIT_SUCCESS ? \ + "SUCCESS" : \ + ( (pxs)->Status == PXENV_EXIT_FAILURE ? \ + "FAILURE" : "UNKNOWN" ) ) + +/************************************************************************** + * Base memory scanning functions + **************************************************************************/ + +/* Locate the $PnP structure indicating a PnP BIOS. + */ + +static int hunt_pnp_bios ( void ) { + uint32_t off = 0x10000; + + printf ( "Hunting for PnP BIOS..." ); + while ( off > 0 ) { + off -= 16; + undi.pnp_bios = (pnp_bios_t *) phys_to_virt ( 0xf0000 + off ); + if ( undi.pnp_bios->signature == PNP_BIOS_SIGNATURE ) { + printf ( "found $PnP at f000:%hx...", off ); + if ( checksum(undi.pnp_bios,sizeof(pnp_bios_t)) !=0) { + printf ( "invalid checksum\n..." ); + continue; + } + printf ( "ok\n" ); + return 1; + } + } + printf ( "none found\n" ); + undi.pnp_bios = NULL; + return 0; +} + +/* Locate the !PXE structure indicating a loaded UNDI driver. + */ + +static int hunt_pixie ( void ) { + static uint32_t ptr = 0; + pxe_t *pxe = NULL; + + printf ( "Hunting for pixies..." ); + if ( ptr == 0 ) ptr = 0xa0000; + while ( ptr > 0x10000 ) { + ptr -= 16; + pxe = (pxe_t *) phys_to_virt ( ptr ); + if ( memcmp ( pxe->Signature, "!PXE", 4 ) == 0 ) { + printf ( "found !PXE at %x...", ptr ); + if ( checksum ( pxe, sizeof(pxe_t) ) != 0 ) { + printf ( "invalid checksum\n..." ); + continue; + } + if ( ptr < get_free_base_memory() ) { + printf ( "in free base memory!\n\n" + "WARNING: a valid !PXE structure was " + "found in an area of memory marked " + "as free!\n\n" ); + undi.pxe = pxe; + pxe_dump(); + undi.pxe = NULL; + printf ( "\nIgnoring and continuing, but this " + "may cause problems later!\n\n" ); + continue; + } + printf ( "ok\n" ); + undi.pxe = pxe; + pxe_dump(); + printf ( "Resetting pixie...\n" ); + undi_unload_base_code(); + eb_pxenv_stop_undi(); + pxe_dump(); + return 1; + } + } + printf ( "none found\n" ); + ptr = 0; + return 0; +} + +/* Locate PCI PnP ROMs. + */ + +static int hunt_rom ( void ) { + static uint32_t ptr = 0; + + /* If we are not a PCI device, we cannot search for a ROM that + * matches us (?) + */ + if ( ! undi.pci.vendor ) + return 0; + + printf ( "Hunting for ROMs..." ); + if ( ptr == 0 ) ptr = 0x100000; + while ( ptr > 0x0c0000 ) { + ptr -= 0x800; + undi.rom = ( rom_t * ) phys_to_virt ( ptr ); + if ( undi.rom->signature == ROM_SIGNATURE ) { + pcir_header_t *pcir_header = NULL; + pnp_header_t *pnp_header = NULL; + + printf ( "found 55AA at %x...", ptr ); + if ( undi.rom->pcir_off == 0 ) { + printf ( "not a PCI ROM\n..." ); + continue; + } + pcir_header = (pcir_header_t*)( ( void * ) undi.rom + + undi.rom->pcir_off ); + if ( pcir_header->signature != PCIR_SIGNATURE ) { + printf ( "invalid PCI signature\n..." ); + continue; + } + printf ( "PCI:%hx:%hx...", pcir_header->vendor_id, + pcir_header->device_id ); + if ( ( pcir_header->vendor_id != undi.pci.vendor ) || + ( pcir_header->device_id != undi.pci.dev_id ) ) { + printf ( "not me (%hx:%hx)\n...", + undi.pci.vendor, + undi.pci.dev_id ); + continue; + } + if ( undi.rom->pnp_off == 0 ) { + printf ( "not a PnP ROM\n..." ); + continue; + } + pnp_header = (pnp_header_t*)( ( void * ) undi.rom + + undi.rom->pnp_off ); + if ( pnp_header->signature != PNP_SIGNATURE ) { + printf ( "invalid $PnP signature\n..." ); + continue; + } + if ( checksum(pnp_header,sizeof(pnp_header_t)) != 0 ) { + printf ( "invalid PnP checksum\n..." ); + continue; + } + printf ( "ok\nROM contains %s by %s\n", + pnp_header->product_str_off==0 ? "(unknown)" : + (void*)undi.rom+pnp_header->product_str_off, + pnp_header->manuf_str_off==0 ? "(unknown)" : + (void*)undi.rom+pnp_header->manuf_str_off ); + return 1; + } + } + printf ( "none found\n" ); + ptr = 0; + undi.rom = NULL; + return 0; +} + +/* Locate ROMs containing UNDI drivers. + */ + +static int hunt_undi_rom ( void ) { + while ( hunt_rom() ) { + if ( undi.rom->undi_rom_id_off == 0 ) { + printf ( "Not a PXE ROM\n" ); + continue; + } + undi.undi_rom_id = (undi_rom_id_t *) + ( (void *)undi.rom + undi.rom->undi_rom_id_off ); + if ( undi.undi_rom_id->signature != UNDI_SIGNATURE ) { + printf ( "Invalid UNDI signature\n" ); + continue; + } + if ( checksum ( undi.undi_rom_id, + undi.undi_rom_id->struct_length ) != 0 ) { + printf ( "Invalid checksum\n" ); + continue; + } + printf ( "Located UNDI ROM supporting revision %d.%d.%d\n", + undi.undi_rom_id->undi_rev[2], + undi.undi_rom_id->undi_rev[1], + undi.undi_rom_id->undi_rev[0] ); + return 1; + } + return 0; +} + +/************************************************************************** + * Low-level UNDI API call wrappers + **************************************************************************/ + +/* Make a real-mode UNDI API call to the UNDI routine at + * routine_seg:routine_off, passing in three uint16 parameters on the + * real-mode stack. + */ + +static PXENV_EXIT_t _undi_call ( uint16_t routine_seg, + uint16_t routine_off, uint16_t st0, + uint16_t st1, uint16_t st2 ) { + PXENV_EXIT_t ret = PXENV_EXIT_FAILURE; + struct { + segoff_t routine; + uint16_t st0; + uint16_t st1; + uint16_t st2; + } PACKED in_stack = { + { routine_off, routine_seg }, st0, st1, st2 + }; + + RM_FRAGMENT(rm_undi_call, + "popw %di\n\t" /* %es:di = routine */ + "popw %es\n\t" + "pushw %cs\n\t" /* set up return address */ + "call 1f\n\t1:popw %bx\n\t" + "leaw (2f-1b)(%bx), %ax\n\t" + "pushw %ax\n\t" + "pushw %es\n\t" /* routine address to stack */ + "pushw %di\n\t" + "lret\n\t" /* calculated lcall */ + "\n2:\n\t" /* continuation point */ + ); + + /* Parameters are left on stack: set out_stack = in_stack */ + ret = real_call ( rm_undi_call, &in_stack, &in_stack ); + + /* UNDI API calls may rudely change the status of A20 and not + * bother to restore it afterwards. Intel is known to be + * guilty of this. + * + * Note that we will return to this point even if A20 gets + * screwed up by the UNDI driver, because Etherboot always + * resides in an even megabyte of RAM. + */ + gateA20_set(); + + return ret; +} + +/* Make a real-mode call to the UNDI loader routine at + * routine_seg:routine_off, passing in the seg:off address of a + * pxenv_structure on the real-mode stack. + */ + +static int undi_call_loader ( void ) { + PXENV_EXIT_t pxenv_exit = PXENV_EXIT_FAILURE; + + /* Hide Etherboot around the loader, so that the PXE stack + * doesn't trash our memory areas + */ + install_e820mangler ( undi.base_mem_data->e820mangler ); + hide_etherboot(); + pxenv_exit = _undi_call ( SEGMENT( undi.rom ), + undi.undi_rom_id->undi_loader_off, + OFFSET( undi.pxs ), + SEGMENT( undi.pxs ), + 0 /* Unused for UNDI loader API */ ); + if ( !unhide_etherboot() ) { + printf ( "FATAL: corrupt INT15\n" ); + return 0; + } + + /* Return 1 for success, to be consistent with other routines */ + if ( pxenv_exit == PXENV_EXIT_SUCCESS ) return 1; + printf ( "UNDI loader call failed with status %#hx\n", + undi.pxs->Status ); + return 0; +} + +/* Make a real-mode UNDI API call, passing in the opcode and the + * seg:off address of a pxenv_structure on the real-mode stack. + * + * Two versions: undi_call() will automatically report any failure + * codes, undi_call_silent() will not. + */ + +static int undi_call_silent ( uint16_t opcode ) { + PXENV_EXIT_t pxenv_exit = PXENV_EXIT_FAILURE; + + pxenv_exit = _undi_call ( undi.pxe->EntryPointSP.segment, + undi.pxe->EntryPointSP.offset, + opcode, + OFFSET( undi.pxs ), + SEGMENT( undi.pxs ) ); + /* Return 1 for success, to be consistent with other routines */ + return pxenv_exit == PXENV_EXIT_SUCCESS ? 1 : 0; +} + +static int undi_call ( uint16_t opcode ) { + if ( undi_call_silent ( opcode ) ) return 1; + printf ( "UNDI API call %#hx failed with status %#hx\n", + opcode, undi.pxs->Status ); + return 0; +} + +#ifdef UNDI_NONTRIVIAL_IRQ +/* IRQ handler that actually calls PXENV_UNDI_ISR. It's probably + * better to use the trivial IRQ handler, since this seems to work for + * just about all known NICs and doesn't involve making a PXE API call + * in interrupt context. + * + * This routine is mainly used for testing the Etherboot PXE stack's + * ability to be called in interrupt context. It is not compiled in + * by default. + * + * This code has fewer safety checks than those in the + * trivial_irq_handler routines. These are omitted because this code + * is not intended for mainstream use. + */ + +uint16_t nontrivial_irq_previous_trigger_count = 0; + +static int copy_nontrivial_irq_handler ( void *target, + size_t target_size __unused ) { + RM_FRAGMENT(nontrivial_irq_handler, + /* Will be installed on a paragraph boundary, so access variables + * using %cs:(xxx-irqstart) + */ + "\n\t" + "irqstart:\n\t" + /* Fields here must match those in undi_irq_handler_t */ + "chain_to:\t.word 0,0\n\t" + "irq_chain:\t.byte 0,0,0,0\n\t" + "entry:\t.word 0,0\n\t" + "count_all:\t.word 0\n\t" + "count_ours:\t.word 0\n\t" + "undi_isr:\n\t" + "undi_isr_Status:\t.word 0\n\t" + "undi_isr_FuncFlag:\t.word 0\n\t" + "undi_isr_others:\t.word 0,0,0,0,0,0\n\t" + "handler:\n\t" + /* Assume that PXE stack will corrupt everything */ + "pushal\n\t" + "push %ds\n\t" + "push %es\n\t" + "push %fs\n\t" + "push %gs\n\t" + /* Set DS == CS */ + "pushw %cs\n\t" + "popw %ds\n\t" + /* Set up parameters for call */ + "movw $" RM_STR(PXENV_UNDI_ISR_IN_START) ", %ds:(undi_isr_FuncFlag-irqstart)\n\t" + "pushw %cs\n\t" + "popw %es\n\t" + "movw $(undi_isr-irqstart), %di\n\t" + "movw $" RM_STR(PXENV_UNDI_ISR) ", %bx\n\t" + "pushw %es\n\t" /* Registers for PXENV+, stack for !PXE */ + "pushw %di\n\t" + "pushw %bx\n\t" + /* Make PXE API call */ + "lcall *%ds:(entry-irqstart)\n\t" + "addw $6, %sp\n\t" + /* Set DS == CS */ + "pushw %cs\n\t" + "popw %ds\n\t" + /* Check return status to see if it's one of our interrupts */ + "cmpw $" RM_STR(PXENV_STATUS_SUCCESS) ", %cs:(undi_isr_Status-irqstart)\n\t" + "jne 1f\n\t" + "cmpw $" RM_STR(PXENV_UNDI_ISR_OUT_OURS) ", %cs:(undi_isr_FuncFlag-irqstart)\n\t" + "jne 1f\n\t" + /* Increment count_ours if so */ + "incw %ds:(count_ours-irqstart)\n\t" + "1:\n\t" + /* Increment count_all anyway */ + "incw %ds:(count_all-irqstart)\n\t" + /* Restore registers and return */ + "popw %gs\n\t" + "popw %fs\n\t" + "popw %es\n\t" + "popw %ds\n\t" + "popal\n\t" + "\n\t" + /* Chain to acknowledge the interrupt */ + "cmpb $0, %cs:(irq_chain-irqstart)\n\t" + "jz 2f\n\t" + "ljmp %cs:(chain_to-irqstart)\n\t" + "2:\n\t" + "\n\t" + "iret\n\t" + "\n\t" + ); + + /* Copy handler */ + memcpy ( target, nontrivial_irq_handler, NONTRIVIAL_IRQ_HANDLER_SIZE ); + + return 1; +} + +static int install_nontrivial_irq_handler ( irq_t irq ) { + undi_irq_handler_t *handler = + &undi.base_mem_data->nontrivial_irq_handler; + segoff_t isr_segoff; + + printf ( "WARNING: using non-trivial IRQ handler [EXPERIMENTAL]\n" ); + /* + * This code is deliberately quick and dirty. The whole + * nontrivial IRQ stuff is only present in order to test out + * calling our PXE stack in interrupt context. Do NOT use + * this in production code. + */ + + disable_irq ( irq ); + handler->count_all = 0; + handler->count_ours = 0; + handler->entry = undi.pxe->EntryPointSP; + nontrivial_irq_previous_trigger_count = 0; + isr_segoff.segment = SEGMENT(handler); + isr_segoff.offset = (void*)&handler->code - (void*)handler; + install_irq_handler( irq, &isr_segoff, + &handler->irq_chain, &handler->chain_to); + enable_irq ( irq ); + + return 1; +} + +static int remove_nontrivial_irq_handler ( irq_t irq ) { + undi_irq_handler_t *handler = + &undi.base_mem_data->nontrivial_irq_handler; + segoff_t isr_segoff; + + isr_segoff.segment = SEGMENT(handler); + isr_segoff.offset = (char*)&handler->code - (char*)handler; + remove_irq_handler( irq, &isr_segoff, + &handler->irq_chain, &handler->chain_to); + return 1; +} + +static int nontrivial_irq_triggered ( irq_t irq __unused ) { + undi_irq_handler_t *handler = + &undi.base_mem_data->nontrivial_irq_handler; + uint16_t nontrivial_irq_this_trigger_count = handler->count_ours; + int triggered = ( nontrivial_irq_this_trigger_count - + nontrivial_irq_previous_trigger_count ); + + nontrivial_irq_previous_trigger_count = + nontrivial_irq_this_trigger_count; + return triggered ? 1 : 0; +} + +static void nontrivial_irq_debug ( irq_t irq ) { + undi_irq_handler_t *handler = + &undi.base_mem_data->nontrivial_irq_handler; + + printf ( "IRQ %d triggered %d times (%d of which were ours)\n", + irq, handler->count_all, handler->count_ours ); +} +#endif /* UNDI_NONTRIVIAL_IRQ */ + +/************************************************************************** + * High-level UNDI API call wrappers + **************************************************************************/ + +/* Install the UNDI driver from a located UNDI ROM. + */ + +static int undi_loader ( void ) { + pxe_t *pxe = NULL; + + if ( ! undi.pci.vendor ) { + printf ( "ERROR: attempted to call loader of an ISA ROM?\n" ); + return 0; + } + + /* AX contains PCI bus:devfn (PCI specification) */ + undi.pxs->loader.ax = ( undi.pci.bus << 8 ) | undi.pci.devfn; + /* BX and DX set to 0xffff for non-ISAPnP devices + * (BIOS boot specification) + */ + undi.pxs->loader.bx = 0xffff; + undi.pxs->loader.dx = 0xffff; + /* ES:DI points to PnP BIOS' $PnP structure + * (BIOS boot specification) + */ + if ( undi.pnp_bios ) { + undi.pxs->loader.es = 0xf000; + undi.pxs->loader.di = virt_to_phys ( undi.pnp_bios ) - 0xf0000; + } else { + /* Set to a NULL pointer and hope that we don't need it */ + undi.pxs->loader.es = 0x0000; + undi.pxs->loader.di = 0x0000; + } + + /* Allocate space for UNDI driver's code and data segments */ + undi.driver_code_size = undi.undi_rom_id->code_size; + undi.driver_code = allot_base_memory ( undi.driver_code_size ); + if ( undi.driver_code == NULL ) { + printf ( "Could not allocate %d bytes for UNDI code segment\n", + undi.driver_code_size ); + return 0; + } + undi.pxs->loader.undi_cs = SEGMENT( undi.driver_code ); + + undi.driver_data_size = undi.undi_rom_id->data_size; + undi.driver_data = allot_base_memory ( undi.driver_data_size ); + if ( undi.driver_data == NULL ) { + printf ( "Could not allocate %d bytes for UNDI code segment\n", + undi.driver_data_size ); + return 0; + } + undi.pxs->loader.undi_ds = SEGMENT( undi.driver_data ); + + printf ( "Installing UNDI driver code to %hx:0000, data at %hx:0000\n", + undi.pxs->loader.undi_cs, undi.pxs->loader.undi_ds ); + + /* Do the API call to install the loader */ + if ( ! undi_call_loader () ) return 0; + + pxe = VIRTUAL( undi.pxs->loader.undi_cs, + undi.pxs->loader.pxe_ptr.offset ); + printf ( "UNDI driver created a pixie at %hx:%hx...", + undi.pxs->loader.undi_cs, undi.pxs->loader.pxe_ptr.offset ); + if ( memcmp ( pxe->Signature, "!PXE", 4 ) != 0 ) { + printf ( "invalid signature\n" ); + return 0; + } + if ( checksum ( pxe, sizeof(pxe_t) ) != 0 ) { + printf ( "invalid checksum\n" ); + return 0; + } + printf ( "ok\n" ); + undi.pxe = pxe; + pxe_dump(); + return 1; +} + +/* Start the UNDI driver. + */ + +static int eb_pxenv_start_undi ( void ) { + int success = 0; + + /* AX contains PCI bus:devfn (PCI specification) */ + undi.pxs->start_undi.ax = ( undi.pci.bus << 8 ) | undi.pci.devfn; + + /* BX and DX set to 0xffff for non-ISAPnP devices + * (BIOS boot specification) + */ + undi.pxs->start_undi.bx = 0xffff; + undi.pxs->start_undi.dx = 0xffff; + /* ES:DI points to PnP BIOS' $PnP structure + * (BIOS boot specification) + */ + if ( undi.pnp_bios ) { + undi.pxs->start_undi.es = 0xf000; + undi.pxs->start_undi.di = + virt_to_phys ( undi.pnp_bios ) - 0xf0000; + } else { + /* Set to a NULL pointer and hope that we don't need it */ + undi.pxs->start_undi.es = 0x0000; + undi.pxs->start_undi.di = 0x0000; + } + + DBG ( "PXENV_START_UNDI => AX=%hx BX=%hx DX=%hx ES:DI=%hx:%hx\n", + undi.pxs->start_undi.ax, + undi.pxs->start_undi.bx, undi.pxs->start_undi.dx, + undi.pxs->start_undi.es, undi.pxs->start_undi.di ); + success = undi_call ( PXENV_START_UNDI ); + DBG ( "PXENV_START_UNDI <= Status=%s\n", UNDI_STATUS(undi.pxs) ); + if ( success ) undi.prestarted = 1; + return success; +} + +static int eb_pxenv_undi_startup ( void ) { + int success = 0; + + DBG ( "PXENV_UNDI_STARTUP => (void)\n" ); + success = undi_call ( PXENV_UNDI_STARTUP ); + DBG ( "PXENV_UNDI_STARTUP <= Status=%s\n", UNDI_STATUS(undi.pxs) ); + if ( success ) undi.started = 1; + return success; +} + +static int eb_pxenv_undi_cleanup ( void ) { + int success = 0; + + DBG ( "PXENV_UNDI_CLEANUP => (void)\n" ); + success = undi_call ( PXENV_UNDI_CLEANUP ); + DBG ( "PXENV_UNDI_CLEANUP <= Status=%s\n", UNDI_STATUS(undi.pxs) ); + return success; +} + +static int eb_pxenv_undi_initialize ( void ) { + int success = 0; + + undi.pxs->undi_initialize.ProtocolIni = 0; + memset ( &undi.pxs->undi_initialize.reserved, 0, + sizeof ( undi.pxs->undi_initialize.reserved ) ); + DBG ( "PXENV_UNDI_INITIALIZE => ProtocolIni=%x\n" ); + success = undi_call ( PXENV_UNDI_INITIALIZE ); + DBG ( "PXENV_UNDI_INITIALIZE <= Status=%s\n", UNDI_STATUS(undi.pxs) ); + if ( success ) undi.initialized = 1; + return success; +} + +static int eb_pxenv_undi_shutdown ( void ) { + int success = 0; + + DBG ( "PXENV_UNDI_SHUTDOWN => (void)\n" ); + success = undi_call ( PXENV_UNDI_SHUTDOWN ); + DBG ( "PXENV_UNDI_SHUTDOWN <= Status=%s\n", UNDI_STATUS(undi.pxs) ); + if ( success ) { + undi.initialized = 0; + undi.started = 0; + } + return success; +} + +static int eb_pxenv_undi_open ( void ) { + int success = 0; + + undi.pxs->undi_open.OpenFlag = 0; + undi.pxs->undi_open.PktFilter = FLTR_DIRECTED | FLTR_BRDCST; + + /* Multicast support not yet implemented */ + undi.pxs->undi_open.R_Mcast_Buf.MCastAddrCount = 0; + DBG ( "PXENV_UNDI_OPEN => OpenFlag=%hx PktFilter=%hx " + "MCastAddrCount=%hx\n", + undi.pxs->undi_open.OpenFlag, undi.pxs->undi_open.PktFilter, + undi.pxs->undi_open.R_Mcast_Buf.MCastAddrCount ); + success = undi_call ( PXENV_UNDI_OPEN ); + DBG ( "PXENV_UNDI_OPEN <= Status=%s\n", UNDI_STATUS(undi.pxs) ); + if ( success ) undi.opened = 1; + return success; +} + +static int eb_pxenv_undi_close ( void ) { + int success = 0; + + DBG ( "PXENV_UNDI_CLOSE => (void)\n" ); + success = undi_call ( PXENV_UNDI_CLOSE ); + DBG ( "PXENV_UNDI_CLOSE <= Status=%s\n", UNDI_STATUS(undi.pxs) ); + if ( success ) undi.opened = 0; + return success; +} + +static int eb_pxenv_undi_transmit_packet ( void ) { + int success = 0; + static const uint8_t broadcast[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF }; + + /* XMitFlag selects unicast / broadcast */ + if ( memcmp ( undi.xmit_data->destaddr, broadcast, + sizeof(broadcast) ) == 0 ) { + undi.pxs->undi_transmit.XmitFlag = XMT_BROADCAST; + } else { + undi.pxs->undi_transmit.XmitFlag = XMT_DESTADDR; + } + + /* Zero reserved dwords */ + undi.pxs->undi_transmit.Reserved[0] = 0; + undi.pxs->undi_transmit.Reserved[1] = 0; + + /* Segment:offset pointer to DestAddr in base memory */ + undi.pxs->undi_transmit.DestAddr.segment = + SEGMENT( undi.xmit_data->destaddr ); + undi.pxs->undi_transmit.DestAddr.offset = + OFFSET( undi.xmit_data->destaddr ); + + /* Segment:offset pointer to TBD in base memory */ + undi.pxs->undi_transmit.TBD.segment = SEGMENT( &undi.xmit_data->tbd ); + undi.pxs->undi_transmit.TBD.offset = OFFSET( &undi.xmit_data->tbd ); + + /* Use only the "immediate" part of the TBD */ + undi.xmit_data->tbd.DataBlkCount = 0; + + DBG ( "PXENV_UNDI_TRANSMIT_PACKET => Protocol=%hx XmitFlag=%hx ...\n" + "... DestAddr=%hx:%hx TBD=%hx:%hx ...\n", + undi.pxs->undi_transmit.Protocol, + undi.pxs->undi_transmit.XmitFlag, + undi.pxs->undi_transmit.DestAddr.segment, + undi.pxs->undi_transmit.DestAddr.offset, + undi.pxs->undi_transmit.TBD.segment, + undi.pxs->undi_transmit.TBD.offset ); + DBG ( "... TBD { ImmedLength=%hx Xmit=%hx:%hx DataBlkCount=%hx }\n", + undi.xmit_data->tbd.ImmedLength, + undi.xmit_data->tbd.Xmit.segment, + undi.xmit_data->tbd.Xmit.offset, + undi.xmit_data->tbd.DataBlkCount ); + success = undi_call ( PXENV_UNDI_TRANSMIT ); + DBG ( "PXENV_UNDI_TRANSMIT_PACKET <= Status=%s\n", + UNDI_STATUS(undi.pxs) ); + return success; +} + +static int eb_pxenv_undi_set_station_address ( void ) { + /* This will spuriously fail on some cards. Ignore failures. + * We only ever use it to set the MAC address to the card's + * permanent value anyway, so it's a useless call (although we + * make it because PXE spec says we should). + */ + DBG ( "PXENV_UNDI_SET_STATION_ADDRESS => " + "StationAddress=%!\n", + undi.pxs->undi_set_station_address.StationAddress ); + undi_call_silent ( PXENV_UNDI_SET_STATION_ADDRESS ); + DBG ( "PXENV_UNDI_SET_STATION_ADDRESS <= Status=%s\n", + UNDI_STATUS(undi.pxs) ); + return 1; +} + +static int eb_pxenv_undi_get_information ( void ) { + int success = 0; + memset ( undi.pxs, 0, sizeof ( undi.pxs ) ); + DBG ( "PXENV_UNDI_GET_INFORMATION => (void)\n" ); + success = undi_call ( PXENV_UNDI_GET_INFORMATION ); + DBG ( "PXENV_UNDI_GET_INFORMATION <= Status=%s " + "BaseIO=%hx IntNumber=%hx ...\n" + "... MaxTranUnit=%hx HwType=%hx HwAddrlen=%hx ...\n" + "... CurrentNodeAddress=%! PermNodeAddress=%! ...\n" + "... ROMAddress=%hx RxBufCt=%hx TxBufCt=%hx\n", + UNDI_STATUS(undi.pxs), + undi.pxs->undi_get_information.BaseIo, + undi.pxs->undi_get_information.IntNumber, + undi.pxs->undi_get_information.MaxTranUnit, + undi.pxs->undi_get_information.HwType, + undi.pxs->undi_get_information.HwAddrLen, + undi.pxs->undi_get_information.CurrentNodeAddress, + undi.pxs->undi_get_information.PermNodeAddress, + undi.pxs->undi_get_information.ROMAddress, + undi.pxs->undi_get_information.RxBufCt, + undi.pxs->undi_get_information.TxBufCt ); + return success; +} + +static int eb_pxenv_undi_get_iface_info ( void ) { + int success = 0; + + DBG ( "PXENV_UNDI_GET_IFACE_INFO => (void)\n" ); + success = undi_call ( PXENV_UNDI_GET_IFACE_INFO ); + DBG ( "PXENV_UNDI_GET_IFACE_INFO <= Status=%s IfaceType=%s ...\n" + "... LinkSpeed=%x ServiceFlags=%x\n", + UNDI_STATUS(undi.pxs), + undi.pxs->undi_get_iface_info.IfaceType, + undi.pxs->undi_get_iface_info.LinkSpeed, + undi.pxs->undi_get_iface_info.ServiceFlags ); + return success; +} + +static int eb_pxenv_undi_isr ( void ) { + int success = 0; + + DBG ( "PXENV_UNDI_ISR => FuncFlag=%hx\n", + undi.pxs->undi_isr.FuncFlag ); + success = undi_call ( PXENV_UNDI_ISR ); + DBG ( "PXENV_UNDI_ISR <= Status=%s FuncFlag=%hx BufferLength=%hx ...\n" + "... FrameLength=%hx FrameHeaderLength=%hx Frame=%hx:%hx " + "ProtType=%hhx ...\n... PktType=%hhx\n", + UNDI_STATUS(undi.pxs), undi.pxs->undi_isr.FuncFlag, + undi.pxs->undi_isr.BufferLength, + undi.pxs->undi_isr.FrameLength, + undi.pxs->undi_isr.FrameHeaderLength, + undi.pxs->undi_isr.Frame.segment, + undi.pxs->undi_isr.Frame.offset, + undi.pxs->undi_isr.ProtType, + undi.pxs->undi_isr.PktType ); + return success; +} + +static int eb_pxenv_stop_undi ( void ) { + int success = 0; + + DBG ( "PXENV_STOP_UNDI => (void)\n" ); + success = undi_call ( PXENV_STOP_UNDI ); + DBG ( "PXENV_STOP_UNDI <= Status=%s\n", UNDI_STATUS(undi.pxs) ); + if ( success ) undi.prestarted = 0; + return success; +} + +static int eb_pxenv_unload_stack ( void ) { + int success = 0; + + memset ( undi.pxs, 0, sizeof ( undi.pxs ) ); + DBG ( "PXENV_UNLOAD_STACK => (void)\n" ); + success = undi_call_silent ( PXENV_UNLOAD_STACK ); + DBG ( "PXENV_UNLOAD_STACK <= Status=%s ...\n... (%s)\n", + UNDI_STATUS(undi.pxs), + ( undi.pxs->Status == PXENV_STATUS_SUCCESS ? + "base-code is ready to be removed" : + ( undi.pxs->Status == PXENV_STATUS_FAILURE ? + "the size of free base memory has been changed" : + ( undi.pxs->Status == PXENV_STATUS_KEEP_ALL ? + "the NIC interrupt vector has been changed" : + "UNEXPECTED STATUS CODE" ) ) ) ); + return success; +} + +static int eb_pxenv_stop_base ( void ) { + int success = 0; + + DBG ( "PXENV_STOP_BASE => (void)\n" ); + success = undi_call ( PXENV_STOP_BASE ); + DBG ( "PXENV_STOP_BASE <= Status=%s\n", UNDI_STATUS(undi.pxs) ); + return success; +} + +/* Unload UNDI base code (if any present) and free memory. + */ +static int undi_unload_base_code ( void ) { + void *bc_code = VIRTUAL( undi.pxe->BC_Code.Seg_Addr, 0 ); + size_t bc_code_size = undi.pxe->BC_Code.Seg_Size; + void *bc_data = VIRTUAL( undi.pxe->BC_Data.Seg_Addr, 0 ); + size_t bc_data_size = undi.pxe->BC_Data.Seg_Size; + void *bc_stck = VIRTUAL( undi.pxe->Stack.Seg_Addr, 0 ); + size_t bc_stck_size = undi.pxe->Stack.Seg_Size; + firing_squad_lineup_t lineup; + + /* Since we never start the base code, the only time we should + * reach this is if we were loaded via PXE. There are many + * different and conflicting versions of the "correct" way to + * unload the PXE base code, several of which appear within + * the PXE specification itself. This one seems to work for + * our purposes. + * + * We always call PXENV_STOP_BASE and PXENV_UNLOAD_STACK even + * if the !PXE structure indicates that no base code is + * present. We do this for the case that there is a + * base-code-less UNDI driver loaded that has hooked some + * interrupts. If the base code really is absent, then these + * calls will fail, we will ignore the failure, and our + * subsequent memory-freeing code is robust enough to handle + * whatever's thrown at it. + */ + eb_pxenv_stop_base(); + eb_pxenv_unload_stack(); + if ( ( undi.pxs->unload_stack.Status != PXENV_STATUS_SUCCESS ) && + ( undi.pxs->unload_stack.Status != PXENV_STATUS_FAILURE ) && + ( undi.pxe->BC_Code.Seg_Addr != 0 ) ) + { + printf ( "Could not free memory allocated to PXE base code: " + "possible memory leak\n" ); + return 0; + } + /* Free data structures. Forget what the PXE specification + * says about how to calculate the new size of base memory; + * basemem.c takes care of all that for us. Note that we also + * have to free the stack (even though PXE spec doesn't say + * anything about it) because nothing else is going to do so. + * + * Structures will almost certainly not be kB-aligned and + * there's a reasonable chance that the UNDI code or data + * portions will lie in the same kB as the base code. Since + * forget_base_memory works only in 1kB increments, this means + * we have to do some arcane trickery. + */ + memset ( &lineup, 0, sizeof(lineup) ); + if ( SEGMENT(bc_code) != 0 ) + assemble_firing_squad( &lineup, bc_code, bc_code_size, SHOOT ); + if ( SEGMENT(bc_data) != 0 ) + assemble_firing_squad( &lineup, bc_data, bc_data_size, SHOOT ); + if ( SEGMENT(bc_stck) != 0 ) + assemble_firing_squad( &lineup, bc_stck, bc_stck_size, SHOOT ); + /* Don't shoot any bits of the UNDI driver code or data */ + assemble_firing_squad ( &lineup, + VIRTUAL(undi.pxe->UNDICode.Seg_Addr, 0), + undi.pxe->UNDICode.Seg_Size, DONTSHOOT ); + assemble_firing_squad ( &lineup, + VIRTUAL(undi.pxe->UNDIData.Seg_Addr, 0), + undi.pxe->UNDIData.Seg_Size, DONTSHOOT ); + shoot_targets ( &lineup ); + undi.pxe->BC_Code.Seg_Addr = 0; + undi.pxe->BC_Data.Seg_Addr = 0; + undi.pxe->Stack.Seg_Addr = 0; + + /* Free and reallocate our own base memory data structures, to + * allow the freed base-code blocks to be fully released. + */ + free_base_mem_data(); + if ( ! allocate_base_mem_data() ) { + printf ( "FATAL: memory unaccountably lost\n" ); + while ( 1 ) {}; + } + + return 1; +} + +/* UNDI full initialization + * + * This calls all the various UNDI initialization routines in sequence. + */ + +static int undi_full_startup ( void ) { + if ( ! eb_pxenv_start_undi() ) return 0; + if ( ! eb_pxenv_undi_startup() ) return 0; + if ( ! eb_pxenv_undi_initialize() ) return 0; + if ( ! eb_pxenv_undi_get_information() ) return 0; + undi.irq = undi.pxs->undi_get_information.IntNumber; + copy_undi_irq_handler ( undi.base_mem_data->irq_handler, + UNDI_IRQ_HANDLER_SIZE ); + if ( ! install_undi_irq_handler ( undi.irq ) ) { + undi.irq = IRQ_NONE; + return 0; + } + memmove ( &undi.pxs->undi_set_station_address.StationAddress, + &undi.pxs->undi_get_information.PermNodeAddress, + sizeof (undi.pxs->undi_set_station_address.StationAddress) ); + if ( ! eb_pxenv_undi_set_station_address() ) return 0; + if ( ! eb_pxenv_undi_open() ) return 0; + return 1; +} + +/* UNDI full shutdown + * + * This calls all the various UNDI shutdown routines in sequence and + * also frees any memory that it can. + */ + +static int undi_full_shutdown ( void ) { + if ( undi.pxe != NULL ) { + /* In case we didn't allocate the driver's memory in the first + * place, try to grab the code and data segments and sizes + * from the !PXE structure. + */ + if ( undi.driver_code == NULL ) { + undi.driver_code = VIRTUAL(undi.pxe->UNDICode.Seg_Addr, + 0 ); + undi.driver_code_size = undi.pxe->UNDICode.Seg_Size; + } + if ( undi.driver_data == NULL ) { + undi.driver_data = VIRTUAL(undi.pxe->UNDIData.Seg_Addr, + 0 ); + undi.driver_data_size = undi.pxe->UNDIData.Seg_Size; + } + + /* Ignore errors and continue in the hope of shutting + * down anyway + */ + if ( undi.opened ) eb_pxenv_undi_close(); + if ( undi.started ) { + eb_pxenv_undi_cleanup(); + /* We may get spurious UNDI API errors at this + * point. If startup() succeeded but + * initialize() failed then according to the + * spec, we should call shutdown(). However, + * some NICS will fail with a status code + * 0x006a (INVALID_STATE). + */ + eb_pxenv_undi_shutdown(); + } + if ( undi.irq != IRQ_NONE ) { + remove_undi_irq_handler ( undi.irq ); + undi.irq = IRQ_NONE; + } + undi_unload_base_code(); + if ( undi.prestarted ) { + eb_pxenv_stop_undi(); + /* Success OR Failure indicates that memory + * can be freed. Any other status code means + * that it can't. + */ + if (( undi.pxs->Status == PXENV_STATUS_KEEP_UNDI ) || + ( undi.pxs->Status == PXENV_STATUS_KEEP_ALL ) ) { + printf ("Could not free memory allocated to " + "UNDI driver: possible memory leak\n"); + return 0; + } + } + } + /* Free memory allocated to UNDI driver */ + if ( undi.driver_code != NULL ) { + /* Clear contents in order to eliminate !PXE and PXENV + * signatures to prevent spurious detection via base + * memory scan. + */ + memset ( undi.driver_code, 0, undi.driver_code_size ); + forget_base_memory ( undi.driver_code, undi.driver_code_size ); + undi.driver_code = NULL; + undi.driver_code_size = 0; + } + if ( undi.driver_data != NULL ) { + forget_base_memory ( undi.driver_data, undi.driver_data_size ); + undi.driver_data = NULL; + undi.driver_data_size = 0; + } + /* !PXE structure now gone; memory freed */ + undi.pxe = NULL; + return 1; +} + +/************************************************************************** +POLL - Wait for a frame +***************************************************************************/ +static int undi_poll(struct nic *nic, int retrieve) +{ + /* Fun, fun, fun. UNDI drivers don't use polling; they use + * interrupts. We therefore cheat and pretend that an + * interrupt has occurred every time undi_poll() is called. + * This isn't too much of a hack; PCI devices share IRQs and + * so the first thing that a proper ISR should do is call + * PXENV_UNDI_ISR to determine whether or not the UNDI NIC + * generated the interrupt; there is no harm done by spurious + * calls to PXENV_UNDI_ISR. Similarly, we wouldn't be + * handling them any more rapidly than the usual rate of + * undi_poll() being called even if we did implement a full + * ISR. So it should work. Ha! + * + * Addendum (21/10/03). Some cards don't play nicely with + * this trick, so instead of doing it the easy way we have to + * go to all the hassle of installing a genuine interrupt + * service routine and dealing with the wonderful 8259 + * Programmable Interrupt Controller. Joy. + */ + + /* See if a hardware interrupt has occurred since the last poll(). + */ + if ( ! undi_irq_triggered ( undi.irq ) ) return 0; + + /* Given the frailty of PXE stacks, it's probably not safe to + * risk calling PXENV_UNDI_ISR with + * FuncFlag=PXENV_UNDI_ISR_START twice for the same interrupt, + * so we cheat slightly and assume that there is something + * ready to retrieve as long as an interrupt has occurred. + */ + if ( ! retrieve ) return 1; + +#ifdef UNDI_NONTRIVIAL_IRQ + /* With the nontrivial IRQ handler, we have already called + * PXENV_UNDI_ISR with PXENV_UNDI_ISR_IN_START and determined + * that it is one of ours. + */ +#else + /* Ask the UNDI driver if this is "our" interrupt. + */ + undi.pxs->undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_START; + if ( ! eb_pxenv_undi_isr() ) return 0; + if ( undi.pxs->undi_isr.FuncFlag == PXENV_UNDI_ISR_OUT_NOT_OURS ) { + /* "Not our interrupt" translates to "no packet ready + * to read". + */ + /* FIXME: Technically, we shouldn't be the one sending + * EOI. However, since our IRQ handlers don't yet + * support chaining, nothing else gets the chance to. + * One nice side-effect of doing this is that it means + * we can cheat and claim the timer interrupt as our + * NIC interrupt; it will be inefficient but will + * work. + */ + send_specific_eoi ( undi.irq ); + return 0; + } +#endif + + /* At this stage, the device should have cleared its interrupt + * line so we can send EOI to the 8259. + */ + send_specific_eoi ( undi.irq ); + + /* We might have received a packet, or this might be a + * "transmit completed" interrupt. Zero nic->packetlen, + * increment whenever we receive a bit of a packet, test + * nic->packetlen when we're done to see whether or not we + * actually received anything. + */ + nic->packetlen = 0; + undi.pxs->undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_PROCESS; + if ( ! eb_pxenv_undi_isr() ) return 0; + while ( undi.pxs->undi_isr.FuncFlag != PXENV_UNDI_ISR_OUT_DONE ) { + switch ( undi.pxs->undi_isr.FuncFlag ) { + case PXENV_UNDI_ISR_OUT_TRANSMIT: + /* We really don't care about transmission complete + * interrupts. + */ + break; + case PXENV_UNDI_ISR_OUT_BUSY: + /* This should never happen. + */ + printf ( "UNDI ISR thinks it's being re-entered!\n" + "Aborting receive\n" ); + return 0; + case PXENV_UNDI_ISR_OUT_RECEIVE: + /* Copy data to receive buffer */ + memcpy ( nic->packet + nic->packetlen, + VIRTUAL( undi.pxs->undi_isr.Frame.segment, + undi.pxs->undi_isr.Frame.offset ), + undi.pxs->undi_isr.BufferLength ); + nic->packetlen += undi.pxs->undi_isr.BufferLength; + break; + default: + printf ( "UNDI ISR returned bizzare status code %d\n", + undi.pxs->undi_isr.FuncFlag ); + } + undi.pxs->undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT; + if ( ! eb_pxenv_undi_isr() ) return 0; + } + return nic->packetlen > 0 ? 1 : 0; +} + +/************************************************************************** +TRANSMIT - Transmit a frame +***************************************************************************/ +static void undi_transmit( + struct nic *nic __unused, + const char *d, /* Destination */ + unsigned int t, /* Type */ + unsigned int s, /* size */ + const char *p) /* Packet */ +{ + /* Copy destination to buffer in base memory */ + memcpy ( undi.xmit_data->destaddr, d, sizeof(MAC_ADDR) ); + + /* Translate packet type to UNDI packet type */ + switch ( t ) { + case IP : undi.pxs->undi_transmit.Protocol = P_IP; break; + case ARP: undi.pxs->undi_transmit.Protocol = P_ARP; break; + case RARP: undi.pxs->undi_transmit.Protocol = P_RARP; break; + default: printf ( "Unknown packet type %hx\n", t ); + return; + } + + /* Store packet length in TBD */ + undi.xmit_data->tbd.ImmedLength = s; + + /* Check to see if data to be transmitted is currently in base + * memory. If not, allocate temporary storage in base memory + * and copy it there. + */ + if ( SEGMENT( p ) <= 0xffff ) { + undi.xmit_data->tbd.Xmit.segment = SEGMENT( p ); + undi.xmit_data->tbd.Xmit.offset = OFFSET( p ); + } else { + memcpy ( undi.xmit_buffer, p, s ); + undi.xmit_data->tbd.Xmit.segment = SEGMENT( undi.xmit_buffer ); + undi.xmit_data->tbd.Xmit.offset = OFFSET( undi.xmit_buffer ); + } + + eb_pxenv_undi_transmit_packet(); +} + +/************************************************************************** +DISABLE - Turn off ethernet interface +***************************************************************************/ +static void undi_disable ( struct dev *dev __unused ) { + undi_full_shutdown(); + free_base_mem_data(); +} + +/************************************************************************** +PROBE - Look for an adapter, this routine's visible to the outside +***************************************************************************/ + +/* Locate an UNDI driver by first scanning through base memory for an + * installed driver and then by scanning for UNDI ROMs and attempting + * to install their drivers. + */ + +static int hunt_pixies_and_undi_roms ( void ) { + static uint8_t hunt_type = HUNT_FOR_PIXIES; + + if ( hunt_type == HUNT_FOR_PIXIES ) { + if ( hunt_pixie() ) { + return 1; + } + } + hunt_type = HUNT_FOR_UNDI_ROMS; + while ( hunt_undi_rom() ) { + if ( undi_loader() ) { + return 1; + } + undi_full_shutdown(); /* Free any allocated memory */ + } + hunt_type = HUNT_FOR_PIXIES; + return 0; +} + +/* The actual Etherboot probe routine. + */ + +static int undi_probe(struct dev *dev, struct pci_device *pci) +{ + struct nic *nic = (struct nic *)dev; + + /* Zero out global undi structure */ + memset ( &undi, 0, sizeof(undi) ); + + /* Store PCI parameters; we will need them to initialize the + * UNDI driver later. If not a PCI device, leave as 0. + */ + if ( pci ) { + memcpy ( &undi.pci, pci, sizeof(undi.pci) ); + } + + /* Find the BIOS' $PnP structure */ + if ( ! hunt_pnp_bios() ) { + /* Not all PXE stacks actually insist on a PnP BIOS. + * In particular, an Etherboot PXE stack will work + * just fine without one. + * + * We used to make this a fatal error, but now we just + * warn and continue. Note that this is necessary in + * order to be able to debug the Etherboot PXE stack + * under Bochs, since Bochs' BIOS is non-PnP. + */ + printf ( "WARNING: No PnP BIOS found\n" ); + } + + /* Allocate base memory data structures */ + if ( ! allocate_base_mem_data() ) return 0; + + /* Search thoroughly for UNDI drivers */ + for ( ; hunt_pixies_and_undi_roms(); undi_full_shutdown() ) { + /* Try to initialise UNDI driver */ + printf ( "Initializing UNDI driver. Please wait...\n" ); + if ( ! undi_full_startup() ) { + if ( undi.pxs->Status == + PXENV_STATUS_UNDI_MEDIATEST_FAILED ) { + printf ( "Cable not connected (code %#hx)\n", + PXENV_STATUS_UNDI_MEDIATEST_FAILED ); + } + continue; + } + /* Basic information: MAC, IO addr, IRQ */ + if ( ! eb_pxenv_undi_get_information() ) continue; + printf ( "Initialized UNDI NIC with IO %#hx, IRQ %d, MAC %!\n", + undi.pxs->undi_get_information.BaseIo, + undi.pxs->undi_get_information.IntNumber, + undi.pxs->undi_get_information.CurrentNodeAddress ); + /* Fill out MAC address in nic structure */ + memcpy ( nic->node_addr, + undi.pxs->undi_get_information.CurrentNodeAddress, + ETH_ALEN ); + /* More diagnostic information including link speed */ + if ( ! eb_pxenv_undi_get_iface_info() ) continue; + printf ( "NDIS type %s interface at %d Mbps\n", + undi.pxs->undi_get_iface_info.IfaceType, + undi.pxs->undi_get_iface_info.LinkSpeed / 1000000 ); + dev->disable = undi_disable; + nic->poll = undi_poll; + nic->transmit = undi_transmit; + return 1; + } + undi_disable ( dev ); /* To free base memory structures */ + return 0; +} + +static int undi_isa_probe ( struct dev *dev, + unsigned short *probe_addrs __unused ) { + return undi_probe ( dev, NULL ); +} + + +/* UNDI driver states that it is suitable for any PCI NIC (i.e. any + * PCI device of class PCI_CLASS_NETWORK_ETHERNET). If there are any + * obscure UNDI NICs that have the incorrect PCI class, add them to + * this list. + */ +static struct pci_id undi_nics[] = { + /* PCI_ROM(0x0000, 0x0000, "undi", "UNDI adaptor"), */ +}; + +static struct pci_driver undi_driver __pci_driver = { + .type = NIC_DRIVER, + .name = "UNDI", + .probe = undi_probe, + .ids = undi_nics, + .id_count = sizeof(undi_nics)/sizeof(undi_nics[0]), + .class = PCI_CLASS_NETWORK_ETHERNET, +}; + +static struct isa_driver undi_isa_driver __isa_driver = { + .type = NIC_DRIVER, + .name = "UNDI", + .probe = undi_isa_probe, + .ioaddrs = 0, +}; + +#endif /* PCBIOS */ diff --git a/src/arch/i386/drivers/net/undi.h b/src/arch/i386/drivers/net/undi.h new file mode 100644 index 00000000..3ba76c35 --- /dev/null +++ b/src/arch/i386/drivers/net/undi.h @@ -0,0 +1,178 @@ +/************************************************************************** +Etherboot - BOOTP/TFTP Bootstrap Program +UNDI NIC driver for Etherboot - header file + +This file Copyright (C) 2003 Michael Brown <mbrown@fensystems.co.uk> +of Fen Systems Ltd. (http://www.fensystems.co.uk/). All rights +reserved. + +$Id$ +***************************************************************************/ + +/* + * 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, or (at + * your option) any later version. + */ + +#include "pxe.h" +#include "pic8259.h" + +/* A union that can function as the parameter block for any UNDI API call. + */ +typedef t_PXENV_ANY pxenv_structure_t; + +/* BIOS PnP parameter block. We scan for this so that we can pass it + * to the UNDI driver. + */ + +#define PNP_BIOS_SIGNATURE ( ('$'<<0) + ('P'<<8) + ('n'<<16) + ('P'<<24) ) +typedef struct pnp_bios { + uint32_t signature; + uint8_t version; + uint8_t length; + uint16_t control; + uint8_t checksum; + uint8_t dontcare[24]; +} PACKED pnp_bios_t; + +/* Structures within the PXE ROM. + */ + +#define ROM_SIGNATURE 0xaa55 +typedef struct rom { + uint16_t signature; + uint8_t unused[0x14]; + uint16_t undi_rom_id_off; + uint16_t pcir_off; + uint16_t pnp_off; +} PACKED rom_t; + +#define PCIR_SIGNATURE ( ('P'<<0) + ('C'<<8) + ('I'<<16) + ('R'<<24) ) +typedef struct pcir_header { + uint32_t signature; + uint16_t vendor_id; + uint16_t device_id; +} PACKED pcir_header_t; + +#define PNP_SIGNATURE ( ('$'<<0) + ('P'<<8) + ('n'<<16) + ('P'<<24) ) +typedef struct pnp_header { + uint32_t signature; + uint8_t struct_revision; + uint8_t length; + uint16_t next; + uint8_t reserved; + uint8_t checksum; + uint16_t id[2]; + uint16_t manuf_str_off; + uint16_t product_str_off; + uint8_t base_type; + uint8_t sub_type; + uint8_t interface_type; + uint8_t indicator; + uint16_t boot_connect_off; + uint16_t disconnect_off; + uint16_t initialise_off; + uint16_t reserved2; + uint16_t info; +} PACKED pnp_header_t; + +#define UNDI_SIGNATURE ( ('U'<<0) + ('N'<<8) + ('D'<<16) + ('I'<<24) ) +typedef struct undi_rom_id { + uint32_t signature; + uint8_t struct_length; + uint8_t struct_cksum; + uint8_t struct_rev; + uint8_t undi_rev[3]; + uint16_t undi_loader_off; + uint16_t stack_size; + uint16_t data_size; + uint16_t code_size; +} PACKED undi_rom_id_t; + +/* Nontrivial IRQ handler structure */ +typedef struct { + segoff_t chain_to; + uint8_t irq_chain, pad1, pad2, pad3; + segoff_t entry; + uint16_t count_all; + uint16_t count_ours; + t_PXENV_UNDI_ISR undi_isr; + char code[0]; +} PACKED undi_irq_handler_t ; + +/* Storage buffers that we need in base memory. We collect these into + * a single structure to make allocation simpler. + */ + +typedef struct undi_base_mem_xmit_data { + MAC_ADDR destaddr; + t_PXENV_UNDI_TBD tbd; +} undi_base_mem_xmit_data_t; + +typedef struct undi_base_mem_data { + pxenv_structure_t pxs; + undi_base_mem_xmit_data_t xmit_data; + char xmit_buffer[ETH_FRAME_LEN]; + /* Must be last in structure and paragraph-aligned */ + union { + char e820mangler[0]; + char irq_handler[0]; + undi_irq_handler_t nontrivial_irq_handler; + } __attribute__ ((aligned(16))); +} undi_base_mem_data_t; + +/* Macros and data structures used when freeing bits of base memory + * used by the UNDI driver. + */ + +#define FIRING_SQUAD_TARGET_SIZE 8 +#define FIRING_SQUAD_TARGET_INDEX(x) ( (x) / FIRING_SQUAD_TARGET_SIZE ) +#define FIRING_SQUAD_TARGET_BIT(x) ( (x) % FIRING_SQUAD_TARGET_SIZE ) +typedef struct firing_squad_lineup { + uint8_t targets[ 640 / FIRING_SQUAD_TARGET_SIZE ]; +} firing_squad_lineup_t; +typedef enum firing_squad_shoot { + DONTSHOOT = 0, + SHOOT = 1 +} firing_squad_shoot_t; + +/* Driver private data structure. + */ + +typedef struct undi { + /* Pointers to various data structures */ + pnp_bios_t *pnp_bios; + rom_t *rom; + undi_rom_id_t *undi_rom_id; + pxe_t *pxe; + pxenv_structure_t *pxs; + undi_base_mem_xmit_data_t *xmit_data; + /* Pointers and sizes to keep track of allocated base memory */ + undi_base_mem_data_t *base_mem_data; + void *driver_code; + size_t driver_code_size; + void *driver_data; + size_t driver_data_size; + char *xmit_buffer; + /* Flags. We keep our own instead of trusting the UNDI driver + * to have implemented PXENV_UNDI_GET_STATE correctly. Plus + * there's the small issue of PXENV_UNDI_GET_STATE being the + * same API call as PXENV_STOP_UNDI... + */ + uint8_t prestarted; /* pxenv_start_undi() has been called */ + uint8_t started; /* pxenv_undi_startup() has been called */ + uint8_t initialized; /* pxenv_undi_initialize() has been called */ + uint8_t opened; /* pxenv_undi_open() has been called */ + /* Parameters that we need to store for future reference + */ + struct pci_device pci; + irq_t irq; +} undi_t; + +/* Constants + */ + +#define HUNT_FOR_PIXIES 0 +#define HUNT_FOR_UNDI_ROMS 1 diff --git a/src/arch/i386/firmware/pcbios/basemem.c b/src/arch/i386/firmware/pcbios/basemem.c new file mode 100644 index 00000000..c93b19e9 --- /dev/null +++ b/src/arch/i386/firmware/pcbios/basemem.c @@ -0,0 +1,317 @@ +#ifdef PCBIOS + +#include "etherboot.h" +#include "realmode.h" /* for real_mode_stack */ + +/* Routines to allocate base memory in a BIOS-compatible way, by + * updating the Free Base Memory Size counter at 40:13h. + * + * Michael Brown <mbrown@fensystems.co.uk> (mcb30) + * $Id$ + */ + +#define fbms ( ( uint16_t * ) phys_to_virt ( 0x413 ) ) +#define BASE_MEMORY_MAX ( 640 ) +#define FREE_BLOCK_MAGIC ( ('!'<<0) + ('F'<<8) + ('R'<<16) + ('E'<<24) ) +#define FREE_BASE_MEMORY ( (uint32_t) ( *fbms << 10 ) ) + +/* Prototypes */ +void * _allot_base_memory ( size_t size ); +void _forget_base_memory ( void *ptr, size_t size ); + +typedef struct free_base_memory_block { + uint32_t magic; + uint16_t size_kb; +} free_base_memory_block_t; + +/* Return amount of free base memory in bytes + */ + +uint32_t get_free_base_memory ( void ) { + return FREE_BASE_MEMORY; +} + +/* Start of our image in base memory. + */ +#define __text16_nocompress __attribute__ ((section (".text16.nocompress"))) +uint32_t image_basemem __text16_nocompress = 0; +uint32_t image_basemem_size __text16_nocompress = 0; + +/* Allot/free the real-mode stack + */ + +void allot_real_mode_stack ( void ) +{ + void *new_real_mode_stack; + + if ( lock_real_mode_stack ) + return; + + /* This is evil hack. + * Until we have a real_mode stack use 0x7c00. + * Except for 0 - 0x600 membory below 0x7c00 is hardly every used. + * This stack should never be used unless the stack allocation fails, + * or if someone has placed a print statement in a dangerous location. + */ + if (!real_mode_stack) { + real_mode_stack = 0x7c00; + } + new_real_mode_stack = _allot_base_memory ( real_mode_stack_size ); + if ( ! new_real_mode_stack ) { + printf ( "FATAL: No real-mode stack\n" ); + while ( 1 ) {}; + } + real_mode_stack = virt_to_phys ( new_real_mode_stack ); + get_memsizes(); +} + +void forget_real_mode_stack ( void ) +{ + if ( lock_real_mode_stack ) + return; + + if ( real_mode_stack) { + _forget_base_memory ( phys_to_virt(real_mode_stack), + real_mode_stack_size ); + /* get_memsizes() uses the real_mode stack we just freed + * for it's BIOS calls. + */ + get_memsizes(); + real_mode_stack = 0; + } +} + +/* Allocate N bytes of base memory. Amount allocated will be rounded + * up to the nearest kB, since that's the granularity of the BIOS FBMS + * counter. Returns NULL if memory cannot be allocated. + */ + +static void * _allot_base_memory ( size_t size ) +{ + uint16_t size_kb = ( size + 1023 ) >> 10; + void *ptr = NULL; + +#ifdef DEBUG_BASEMEM + printf ( "Trying to allocate %d kB of base memory from %d kB free\n", + size_kb, *fbms ); +#endif + + /* Free up any unused memory before we start */ + free_unused_base_memory(); + + /* Check available base memory */ + if ( size_kb > *fbms ) { return NULL; } + + /* Reduce available base memory */ + *fbms -= size_kb; + + /* Calculate address of memory allocated */ + ptr = phys_to_virt ( FREE_BASE_MEMORY ); + + /* Zero out memory. We do this so that allocation of + * already-used space will show up in the form of a crash as + * soon as possible. + * + * Update: there's another reason for doing this. If we don't + * zero the contents, then they could still retain our "free + * block" markers and be liable to being freed whenever a + * base-memory allocation routine is next called. + */ + memset ( ptr, 0, size_kb << 10 ); + +#ifdef DEBUG_BASEMEM + printf ( "Allocated %d kB at [%x,%x)\n", size_kb, + virt_to_phys ( ptr ), + virt_to_phys ( ptr ) + size_kb * 1024 ); +#endif + + return ptr; +} + +void * allot_base_memory ( size_t size ) +{ + void *ptr; + + /* Free real-mode stack, allocate memory, reallocate real-mode + * stack. + */ + forget_real_mode_stack(); + ptr = _allot_base_memory ( size ); + get_memsizes(); + return ptr; +} + +/* Free base memory allocated by allot_base_memory. The BIOS provides + * nothing better than a LIFO mechanism for freeing memory (i.e. it + * just has the single "total free memory" counter), but we improve + * upon this slightly; as long as you free all the allotted blocks, it + * doesn't matter what order you free them in. (This will only work + * for blocks that are freed via forget_base_memory()). + * + * Yes, it's annoying that you have to remember the size of the blocks + * you've allotted. However, since our granularity of allocation is + * 1K, the alternative is to risk wasting the occasional kB of base + * memory, which is a Bad Thing. Really, you should be using as + * little base memory as possible, so consider the awkwardness of the + * API to be a feature! :-) + */ + +static void _forget_base_memory ( void *ptr, size_t size ) +{ + uint16_t remainder = virt_to_phys(ptr) & 1023; + uint16_t size_kb = ( size + remainder + 1023 ) >> 10; + free_base_memory_block_t *free_block = + ( free_base_memory_block_t * ) ( ptr - remainder ); + + if ( ( ptr == NULL ) || ( size == 0 ) ) { + return; + } + +#ifdef DEBUG_BASEMEM + printf ( "Trying to free %d bytes base memory at 0x%x\n", + size, virt_to_phys ( ptr ) ); + if ( remainder > 0 ) { + printf ( "WARNING: destructively expanding free block " + "downwards to 0x%x\n", + virt_to_phys ( ptr - remainder ) ); + } +#endif + + /* Mark every kilobyte within this block as free. This is + * overkill for normal purposes, but helps when something has + * allocated base memory with a granularity finer than the + * BIOS granularity of 1kB. PXE ROMs tend to do this when + * they allocate their own memory. This method allows us to + * free their blocks (admittedly in a rather dangerous, + * tread-on-anything-either-side sort of way, but there's no + * other way to do it). + * + * Since we're marking every kB as free, there's actually no + * need for recording the size of the blocks. However, we + * keep this in so that debug messages are friendlier. It + * probably adds around 8 bytes to the overall code size. + */ + while ( size_kb > 0 ) { + /* Mark this block as unused */ + free_block->magic = FREE_BLOCK_MAGIC; + free_block->size_kb = size_kb; + /* Move up by 1 kB */ + free_block = (void *)(((char *)free_block) + (1 << 10)); + size_kb--; + } + + /* Free up unused base memory */ + free_unused_base_memory(); +} + +void forget_base_memory ( void *ptr, size_t size ) +{ + /* Free memory, free real-mode stack, re-allocate real-mode + * stack. Do this so that we don't end up wasting a huge + * block of memory trapped behind the real-mode stack. + */ + _forget_base_memory ( ptr, size ); + forget_real_mode_stack(); + get_memsizes(); +} + +/* Do the actual freeing of memory. This is split out from + * forget_base_memory() so that it may be called separately. It + * should be called whenever base memory is deallocated by an external + * entity (if we can detect that it has done so) so that we get the + * chance to free up our own blocks. + */ +static void free_unused_base_memory ( void ) { + free_base_memory_block_t *free_block = NULL; + + /* Try to release memory back to the BIOS. Free all + * consecutive blocks marked as free. + */ + while ( 1 ) { + /* Calculate address of next potential free block */ + free_block = ( free_base_memory_block_t * ) + phys_to_virt ( FREE_BASE_MEMORY ); + + /* Stop processing if we're all the way up to 640K or + * if this is not a free block + */ + if ( ( *fbms == BASE_MEMORY_MAX ) || + ( free_block->magic != FREE_BLOCK_MAGIC ) ) { + break; + } + + /* Return memory to BIOS */ + *fbms += free_block->size_kb; + +#ifdef DEBUG_BASEMEM + printf ( "Freed %d kB base memory, %d kB now free\n", + free_block->size_kb, *fbms ); +#endif + + /* Zero out freed block. We do this in case + * the block contained any structures that + * might be located by scanning through + * memory. + */ + memset ( free_block, 0, free_block->size_kb << 10 ); + } +} + +/* Free base memory used by the prefix. Called once at start of + * Etherboot by arch_main(). + */ +void forget_prefix_base_memory ( void ) +{ + /* runtime_start_kb is _text rounded down to a physical kB boundary */ + uint32_t runtime_start_kb = virt_to_phys(_text) & ~0x3ff; + /* prefix_size_kb is the prefix size excluding any portion + * that overlaps into the first kB used by the runtime image + */ + uint32_t prefix_size_kb = runtime_start_kb - image_basemem; + +#ifdef DEBUG_BASEMEM + printf ( "Attempting to free base memory used by prefix\n" ); +#endif + + /* If the decompressor is in allocated base memory + * *and* the Etherboot text is in base + * memory, then free the decompressor. + */ + if ( ( image_basemem >= FREE_BASE_MEMORY ) && + ( runtime_start_kb >= FREE_BASE_MEMORY ) && + ( runtime_start_kb <= ( BASE_MEMORY_MAX << 10 ) ) ) + { + forget_base_memory ( phys_to_virt ( image_basemem ), + prefix_size_kb ); + /* Update image_basemem and image_basemem_size to + * indicate that our allocation now starts with _text + */ + image_basemem = runtime_start_kb; + image_basemem_size -= prefix_size_kb; + } +} + +/* Free base memory used by the runtime image. Called after + * relocation by arch_relocated_from(). + */ +void forget_runtime_base_memory ( unsigned long old_addr ) +{ + /* text_start_kb is old _text rounded down to a physical KB boundary */ + uint32_t old_text_start_kb = old_addr & ~0x3ff; + +#ifdef DEBUG_BASEMEM + printf ( "Attempting to free base memory used by runtime image\n" ); +#endif + + if ( ( image_basemem >= FREE_BASE_MEMORY ) && + ( image_basemem == old_text_start_kb ) ) + { + forget_base_memory ( phys_to_virt ( image_basemem ), + image_basemem_size ); + /* Update image_basemem to show no longer in use */ + image_basemem = 0; + image_basemem_size = 0; + } +} + +#endif /* PCBIOS */ diff --git a/src/arch/i386/firmware/pcbios/bios.c b/src/arch/i386/firmware/pcbios/bios.c new file mode 100644 index 00000000..70f26703 --- /dev/null +++ b/src/arch/i386/firmware/pcbios/bios.c @@ -0,0 +1,155 @@ +/* Etherboot routines for PCBIOS firmware. + * + * Body of routines taken from old pcbios.S + */ + +#ifdef PCBIOS + +#include "etherboot.h" +#include "realmode.h" +#include "segoff.h" + +#define CF ( 1 << 0 ) + +/************************************************************************** +CURRTICKS - Get Time +Use direct memory access to BIOS variables, longword 0040:006C (ticks +today) and byte 0040:0070 (midnight crossover flag) instead of calling +timeofday BIOS interrupt. +**************************************************************************/ +#if defined(CONFIG_TSC_CURRTICKS) +#undef CONFIG_BIOS_CURRTICKS +#else +#define CONFIG_BIOS_CURRTICKS 1 +#endif +#if defined(CONFIG_BIOS_CURRTICKS) +unsigned long currticks (void) +{ + static uint32_t days = 0; + uint32_t *ticks = VIRTUAL(0x0040,0x006c); + uint8_t *midnight = VIRTUAL(0x0040,0x0070); + + /* Re-enable interrupts so that the timer interrupt can occur + */ + RM_FRAGMENT(rm_currticks, + "sti\n\t" + "nop\n\t" + "nop\n\t" + "cli\n\t" + ); + + real_call ( rm_currticks, NULL, NULL ); + + if ( *midnight ) { + *midnight = 0; + days += 0x1800b0; + } + return ( days + *ticks ); +} +#endif /* CONFIG_BIOS_CURRTICKS */ + +/************************************************************************** +INT15 - Call Interrupt 0x15 +**************************************************************************/ +int int15 ( int ax ) +{ + struct { + reg16_t ax; + } PACKED in_stack; + struct { + reg16_t flags; + } PACKED out_stack; + reg16_t ret_ax; + + RM_FRAGMENT(rm_int15, + "sti\n\t" + "popw %ax\n\t" + "stc\n\t" + "int $0x15\n\t" + "pushf\n\t" + "cli\n\t" + ); + + in_stack.ax.word = ax; + ret_ax.word = real_call ( rm_int15, &in_stack, &out_stack ); + + /* Carry flag clear indicates function not supported */ + if ( ! ( out_stack.flags.word & CF ) ) return 0; + return ret_ax.h; +} + +#ifdef POWERSAVE +/************************************************************************** +CPU_NAP - Save power by halting the CPU until the next interrupt +**************************************************************************/ +void cpu_nap ( void ) +{ + RM_FRAGMENT(rm_cpu_nap, + "sti\n\t" + "hlt\n\t" + "cli\n\t" + ); + + real_call ( rm_cpu_nap, NULL, NULL ); +} +#endif /* POWERSAVE */ + +#if (TRY_FLOPPY_FIRST > 0) +/************************************************************************** +DISK_INIT - Initialize the disk system +**************************************************************************/ +void disk_init ( void ) +{ + RM_FRAGMENT(rm_disk_init, + "sti\n\t" + "xorw %ax,%ax\n\t" + "movb $0x80,%dl\n\t" + "int $0x13\n\t" + "cli\n\t" + ); + + real_call ( rm_disk_init, NULL, NULL ); +} + +/************************************************************************** +DISK_READ - Read a sector from disk +**************************************************************************/ +unsigned int pcbios_disk_read ( int drive, int cylinder, int head, int sector, + char *buf ) { + struct { + reg16_t ax; + reg16_t cx; + reg16_t dx; + segoff_t buffer; + } PACKED in_stack; + struct { + reg16_t flags; + } PACKED out_stack; + reg16_t ret_ax; + + RM_FRAGMENT(rm_pcbios_disk_read, + "sti\n\t" + "popw %ax\n\t" + "popw %cx\n\t" + "popw %dx\n\t" + "popw %bx\n\t" + "popw %es\n\t" + "int $0x13\n\t" + "pushfw\n\t" + "cli\n\t" + ); + + in_stack.ax.h = 2; /* INT 13,2 - Read disk sector */ + in_stack.ax.l = 1; /* Read one sector */ + in_stack.cx.h = cylinder & 0xff; + in_stack.cx.l = ( ( cylinder >> 8 ) & 0x3 ) | sector; + in_stack.dx.h = head; + in_stack.dx.l = drive; + in_stack.buffer.segment = SEGMENT ( buf ); + in_stack.buffer.offset = OFFSET ( buf ); + ret_ax.word = real_call ( rm_pcbios_disk_read, &in_stack, &out_stack ); + return ( out_stack.flags.word & CF ) ? ret_ax.word : 0; +} +#endif /* TRY_FLOPPY_FIRST */ + +#endif /* PCBIOS */ diff --git a/src/arch/i386/firmware/pcbios/console.c b/src/arch/i386/firmware/pcbios/console.c new file mode 100644 index 00000000..f994f06e --- /dev/null +++ b/src/arch/i386/firmware/pcbios/console.c @@ -0,0 +1,85 @@ +/* Etherboot routines for PCBIOS firmware. + * + * Body of routines taken from old pcbios.S + */ + +#ifdef PCBIOS + +#include "etherboot.h" +#include "realmode.h" +#include "segoff.h" + +#define ZF ( 1 << 6 ) + +/************************************************************************** +CONSOLE_PUTC - Print a character on console +**************************************************************************/ +void console_putc ( int character ) +{ + struct { + reg16_t ax; + } PACKED in_stack; + + RM_FRAGMENT(rm_console_putc, + "sti\n\t" + "popw %ax\n\t" + "movb $0x0e, %ah\n\t" + "movl $1, %ebx\n\t" + "int $0x10\n\t" + "cli\n\t" + ); + + in_stack.ax.l = character; + real_call ( rm_console_putc, &in_stack, NULL ); +} + +/************************************************************************** +CONSOLE_GETC - Get a character from console +**************************************************************************/ +int console_getc ( void ) +{ + RM_FRAGMENT(rm_console_getc, + "sti\n\t" + "xorw %ax, %ax\n\t" + "int $0x16\n\t" + "xorb %ah, %ah\n\t" + "cli\n\t" + ); + + return real_call ( rm_console_getc, NULL, NULL ); +} + +/************************************************************************** +CONSOLE_ISCHAR - Check for keyboard interrupt +**************************************************************************/ +int console_ischar ( void ) +{ + RM_FRAGMENT(rm_console_ischar, + "sti\n\t" + "movb $1, %ah\n\t" + "int $0x16\n\t" + "pushfw\n\t" + "popw %ax\n\t" + "cli\n\t" + ); + + return ( ( real_call ( rm_console_ischar, NULL, NULL ) & ZF ) == 0 ); +} + +/************************************************************************** +GETSHIFT - Get keyboard shift state +**************************************************************************/ +int getshift ( void ) +{ + RM_FRAGMENT(rm_getshift, + "sti\n\t" + "movb $2, %ah\n\t" + "int $0x16\n\t" + "andw $0x3, %ax\n\t" + "cli\n\t" + ); + + return real_call ( rm_getshift, NULL, NULL ); +} + +#endif /* PCBIOS */ diff --git a/src/arch/i386/firmware/pcbios/e820mangler.S b/src/arch/i386/firmware/pcbios/e820mangler.S new file mode 100644 index 00000000..9349cf2b --- /dev/null +++ b/src/arch/i386/firmware/pcbios/e820mangler.S @@ -0,0 +1,296 @@ +#undef CODE16 +#if defined(PCBIOS) +#define CODE16 +#endif + +#ifdef CODE16 + +#define BOCHSBP xchgw %bx,%bx + + .text + .arch i386 + .section ".text16", "ax", @progbits + .code16 + +/**************************************************************************** + * Memory map mangling code + **************************************************************************** + */ + + .globl e820mangler +e820mangler: + +/* Macro to calculate offset of labels within code segment in + * installed copy of code. + */ +#define INSTALLED(x) ( (x) - e820mangler ) + +/**************************************************************************** + * Intercept INT 15 memory calls and remove the hidden memory ranges + * from the resulting memory map. + **************************************************************************** + */ + .globl _intercept_int15 +_intercept_int15: + /* Preserve registers */ + pushw %bp + /* Store %ax for future reference */ + pushw %ax + /* Make INT-style call to old INT15 routine */ + pushfw + lcall %cs:*INSTALLED(_intercepted_int15) + /* Preserve flags returned by original E820 routine */ + pushfw + /* Check for valid INT15 routine */ + jc intercept_int15_exit + /* Check for a routine we want to intercept */ + movw %sp, %bp + cmpw $0xe820, 2(%bp) + je intercept_e820 + cmpw $0xe801, 2(%bp) + je intercept_e801 + cmpb $0x88, 3(%bp) + je intercept_88 +intercept_int15_exit: + /* Restore registers and return */ + popfw + popw %bp /* discard original %ax */ + popw %bp + lret $2 /* 'iret' - flags already loaded */ + + .globl _intercepted_int15 +_intercepted_int15: .word 0,0 + +/**************************************************************************** + * Exclude an address range from a potentially overlapping address range + * + * Note: this *can* be called even if the range doesn't overlap; it + * will simply return the range unaltered. It copes with all the + * possible cases of overlap, including total overlap (which will + * modify the range to length zero). If the to-be-excluded range is + * in the middle of the target range, then the larger remaining + * portion will be returned. If %di is nonzero on entry then the + * range will only be truncated from the high end, i.e. the base + * address will never be altered. All this in less than 30 + * instructions. :) + * + * Parameters: + * %eax Base address of memory range + * %ecx Length of memory range + * %ebx Base address of memory range to exclude + * %edx Length of memory range to exclude + * %di 0 => truncate either end, 1 => truncate high end only + * Returns: + * %eax Updated base address of range + * %ecx Updated length of range + * %ebx,%edx Undefined + * All other registers (including %di) preserved + * + * Note: "ja" is used rather than "jg" because we are comparing + * unsigned ints + **************************************************************************** + */ +#ifdef TEST_EXCLUDE_ALGORITHM + .code32 +#endif /* TEST_EXCLUDE_ALGORITHM */ +exclude_memory_range: + /* Convert (start,length) to (start,end) */ + addl %eax, %ecx + addl %ebx, %edx + /* Calculate "prefix" length */ + subl %eax, %ebx /* %ebx = "prefix" length */ + ja 1f + xorl %ebx, %ebx /* Truncate to zero if negative */ +1: /* %di == 0 => truncate either end + * %di != 0 => truncate only high end + */ + testw %di, %di + je use_either + cmpl %eax, %edx + jbe 99f /* excl. range is below target range */ +use_prefix: /* Use prefix, discard suffix */ + addl %eax, %ebx /* %ebx = candidate end address */ + cmpl %ecx, %ebx /* %ecx = min ( %ebx, %ecx ) */ + ja 1f + movl %ebx, %ecx +1: jmp 99f +use_either: + /* Calculate "suffix" length */ + subl %ecx, %edx /* %edx = -( "suffix" length ) */ + jb 1f + xorl %edx, %edx /* Truncate to zero if negative */ +1: negl %edx /* %edx = "suffix" length */ + /* Use whichever is longest of "prefix" and "suffix" */ + cmpl %ebx, %edx + jbe use_prefix +use_suffix: /* Use suffix, discard prefix */ + negl %edx + addl %ecx, %edx /* %edx = candidate start address */ + cmpl %eax, %edx /* %eax = max ( %eax, %edx ) */ + jb 1f + movl %edx, %eax +1: +99: subl %eax, %ecx /* Convert back to (start,length) */ + ret + +#ifdef TEST_EXCLUDE_ALGORITHM + .globl __test_exclude +__test_exclude: + pushl %ebx + pushl %edi + movl 12(%esp), %eax + movl 16(%esp), %ecx + movl 20(%esp), %ebx + movl 24(%esp), %edx + movl 28(%esp), %edi + call exclude_memory_range + shll $16, %eax + orl %ecx, %eax + popl %edi + popl %ebx + ret + .code16 +#endif /* TEST_EXCLUDE_ALGORITHM */ + +/**************************************************************************** + * Exclude Etherboot-reserved address ranges from a potentially + * overlapping address range + * + * Parameters: + * %eax Base address of memory range + * %ecx Length of memory range + * %di 0 => truncate either end, 1 => truncate high end only + * Returns: + * %eax Updated base address of range + * %ecx Updated length of range + * All other registers (including %di) preserved + **************************************************************************** + */ +exclude_hidden_memory_ranges: + pushw %si + pushl %ebx + pushl %edx + movw $INSTALLED(_hide_memory), %si +2: movl %cs:0(%si), %ebx + movl %cs:4(%si), %edx + call exclude_memory_range + addw $8, %si + cmpw $INSTALLED(_hide_memory_end), %si + jl 2b + popl %edx + popl %ebx + popw %si + ret + + .globl _hide_memory +_hide_memory: + .long 0,0 /* Etherboot text (base,length) */ + .long 0,0 /* Heap (base,length) */ +_hide_memory_end: + +/**************************************************************************** + * Intercept INT 15,E820 calls and remove the hidden memory ranges + * from the resulting memory map. + **************************************************************************** + */ +#define SMAP ( 0x534d4150 ) +intercept_e820: + /* Check for valid E820 routine */ + cmpl $SMAP, %eax + jne intercept_int15_exit + /* If base address isn't in the low 4GB, return unaltered + * (since we never claim memory above 4GB). WARNING: we cheat + * by assuming that no E820 region will straddle the 4GB + * boundary: if this is not a valid assumption then things + * will probably break. + */ + cmpl $0, %es:4(%di) + jne intercept_int15_exit + /* Preserve registers */ + pushl %eax + pushl %ecx + /* Update returned memory range */ + movl %es:0(%di), %eax /* Base */ + movl %es:8(%di), %ecx /* Length */ + pushw %di + xorw %di, %di /* "truncate either end" flag */ + call exclude_hidden_memory_ranges + popw %di + movl %eax, %es:0(%di) /* Store updated base */ + movl %ecx, %es:8(%di) /* Store updated length */ + /* Restore registers and return */ + popl %ecx + popl %eax + jmp intercept_int15_exit + +/**************************************************************************** + * Intercept INT 15,E801 calls and remove the hidden memory ranges + * from the resulting memory map. + **************************************************************************** + */ +intercept_e801: + /* Adjust return values */ + call e801_adjust + xchgw %ax, %cx + xchgw %bx, %dx + call e801_adjust + xchgw %ax, %cx + xchgw %bx, %dx + jmp intercept_int15_exit + + /* %ax = #KB from 1MB+, %bx = #64KB from 16MB+ + * Return with modified values in %ax, %bx. Preserver other regs. + */ +e801_adjust: + pushw %di + pushl %ecx + pushl %eax + movw $1, %di /* "truncate only high end" flag */ + + /* Truncate #64KB from 16MB+ as appropriate */ + movw %bx, %cx /* (no need to zero high word) */ + shll $16, %ecx /* %ecx = length in bytes */ + movl $(1<<24), %eax /* 16MB start address */ + call exclude_hidden_memory_ranges + shrl $16, %ecx /* %cx = updated length in 64KB */ + movw %cx, %bx /* Return in %bx */ + + /* Truncate #KB from 1MB+ as appropriate */ + popw %cx /* Orig. %ax (high word already 0) */ + shll $10, %ecx /* %ecx = length in bytes */ + shrl $4, %eax /* 1MB start address */ + call exclude_hidden_memory_ranges + shrl $10, %ecx /* %cx = updated length in KB */ + pushw %cx /* Will be picked up in %eax */ + + popl %eax + popl %ecx + popw %di + ret + +/**************************************************************************** + * Intercept INT 15,88 calls and remove the hidden memory ranges + * from the resulting memory map. + **************************************************************************** + */ +intercept_88: + pushw %bx /* E801 adjust, ignore %bx */ + call e801_adjust + popw %bx + jmp intercept_int15_exit + + .globl e820mangler_end +e820mangler_end: + + .globl _e820mangler_size + .equ _e820mangler_size, e820mangler_end - e820mangler + .globl e820mangler_size +e820mangler_size: + .word _e820mangler_size + +#else + + .globl _e820mangler_size + .equ _e820mangler_size, 0 + +#endif /* CODE16 */ diff --git a/src/arch/i386/firmware/pcbios/hidemem.c b/src/arch/i386/firmware/pcbios/hidemem.c new file mode 100644 index 00000000..a9ae001e --- /dev/null +++ b/src/arch/i386/firmware/pcbios/hidemem.c @@ -0,0 +1,94 @@ +/* Utility functions to hide Etherboot by manipulating the E820 memory + * map. These could go in memsizes.c, but are placed here because not + * all images will need them. + */ + +#include "etherboot.h" +#include "hidemem.h" + +#ifdef CODE16 + +static int mangling = 0; +static void *mangler = NULL; + +#define INSTALLED(x) ( (typeof(&x)) ( (void*)(&x) - (void*)e820mangler \ + + mangler ) ) +#define intercept_int15 INSTALLED(_intercept_int15) +#define intercepted_int15 INSTALLED(_intercepted_int15) +#define hide_memory INSTALLED(_hide_memory) +#define INT15_VECTOR ( (segoff_t*) ( phys_to_virt( 4 * 0x15 ) ) ) + +int install_e820mangler ( void *new_mangler ) { + if ( mangling ) return 0; + memcpy ( new_mangler, &e820mangler, e820mangler_size ); + mangler = new_mangler; + return 1; +} + +/* Intercept INT15 calls and pass them through the mangler. The + * mangler must have been copied to base memory before making this + * call, and "mangler" must point to the base memory copy, which must + * be 16-byte aligned. + */ +int hide_etherboot ( void ) { + if ( mangling ) return 1; + if ( !mangler ) return 0; + + /* Hook INT15 handler */ + *intercepted_int15 = *INT15_VECTOR; + (*hide_memory)[0].start = virt_to_phys(_text); + (*hide_memory)[0].length = _end - _text; + /* IMPORTANT, possibly even FIXME: + * + * Etherboot has a tendency to claim a very large area of + * memory as possible heap; enough to make it impossible to + * load an OS if we hide all of it. We hide only the portion + * that's currently in use. This means that we MUST NOT + * perform further allocations from the heap while the mangler + * is active. + */ + (*hide_memory)[1].start = heap_ptr; + (*hide_memory)[1].length = heap_bot - heap_ptr; + INT15_VECTOR->segment = SEGMENT(mangler); + INT15_VECTOR->offset = 0; + + mangling = 1; + return 1; +} + +int unhide_etherboot ( void ) { + if ( !mangling ) return 1; + + /* Restore original INT15 handler + */ + if ( VIRTUAL(INT15_VECTOR->segment,INT15_VECTOR->offset) != mangler ) { + /* Oh dear... */ + +#ifdef WORK_AROUND_BPBATCH_BUG + /* BpBatch intercepts INT15, so can't unhook it, and + * then proceeds to ignore our PXENV_KEEP_UNDI return + * status, which means that it ends up zeroing out the + * INT15 handler routine. + * + * This rather ugly hack involves poking into + * BpBatch's code and changing it's stored value for + * the "next handler" in the INT15 chain. + */ + segoff_t *bp_chain = VIRTUAL ( 0x0060, 0x8254 ); + + if ( ( bp_chain->segment == SEGMENT(mangler) ) && + ( bp_chain->offset == 0 ) ) { + printf ( "\nBPBATCH bug workaround enabled\n" ); + *bp_chain = *intercepted_int15; + } +#endif /* WORK_AROUND_BPBATCH_BUG */ + + return 0; + } + *INT15_VECTOR = *intercepted_int15; + + mangling = 0; + return 1; +} + +#endif /* CODE16 */ diff --git a/src/arch/i386/firmware/pcbios/memsizes.c b/src/arch/i386/firmware/pcbios/memsizes.c new file mode 100644 index 00000000..18ae86da --- /dev/null +++ b/src/arch/i386/firmware/pcbios/memsizes.c @@ -0,0 +1,201 @@ +#ifdef PCBIOS + +#include "etherboot.h" +#include "realmode.h" + +#define CF ( 1 << 0 ) + +#ifndef MEMSIZES_DEBUG +#define MEMSIZES_DEBUG 0 +#endif + +/* by Eric Biederman */ + +struct meminfo meminfo; + +/************************************************************************** +BASEMEMSIZE - Get size of the conventional (base) memory +**************************************************************************/ +unsigned short basememsize ( void ) +{ + RM_FRAGMENT(rm_basememsize, + "int $0x12\n\t" + ); + return real_call ( rm_basememsize, NULL, NULL ); +} + +/************************************************************************** +MEMSIZE - Determine size of extended memory +**************************************************************************/ +unsigned int memsize ( void ) +{ + struct { + reg16_t ax; + } PACKED in_stack; + struct { + reg16_t ax; + reg16_t bx; + reg16_t cx; + reg16_t dx; + reg16_t flags; + } PACKED out_stack; + int memsize; + + RM_FRAGMENT(rm_memsize, + /* Some buggy BIOSes don't clear/set carry on pass/error of + * e801h memory size call or merely pass cx,dx through without + * changing them, so we set carry and zero cx,dx before call. + */ + "stc\n\t" + "xorw %cx,%cx\n\t" + "xorw %dx,%dx\n\t" + "popw %ax\n\t" + "int $0x15\n\t" + "pushfw\n\t" + "pushw %dx\n\t" + "pushw %cx\n\t" + "pushw %bx\n\t" + "pushw %ax\n\t" + ); + + /* Try INT 15,e801 first */ + in_stack.ax.word = 0xe801; + real_call ( rm_memsize, &in_stack, &out_stack ); + if ( out_stack.flags.word & CF ) { + /* INT 15,e801 not supported: try INT 15,88 */ + in_stack.ax.word = 0x8800; + memsize = real_call ( rm_memsize, &in_stack, &out_stack ); + } else { + /* Some BIOSes report extended memory via ax,bx rather + * than cx,dx + */ + if ( (out_stack.cx.word==0) && (out_stack.dx.word==0) ) { + /* Use ax,bx */ + memsize = ( out_stack.bx.word<<6 ) + out_stack.ax.word; + } else { + /* Use cx,dx */ + memsize = ( out_stack.dx.word<<6 ) + out_stack.cx.word; + } + } + return memsize; +} + +#define SMAP ( 0x534d4150 ) +int meme820 ( struct e820entry *buf, int count ) +{ + struct { + reg16_t flags; + reg32_t eax; + reg32_t ebx; + struct e820entry entry; + } PACKED stack; + int index = 0; + + RM_FRAGMENT(rm_meme820, + "addw $6, %sp\n\t" /* skip flags, eax */ + "popl %ebx\n\t" + "pushw %ss\n\t" /* es:di = ss:sp */ + "popw %es\n\t" + "movw %sp, %di\n\t" + "movl $0xe820, %eax\n\t" + "movl $" RM_STR(SMAP) ", %edx\n\t" + "movl $" RM_STR(E820ENTRY_SIZE) ", %ecx\n\t" + "int $0x15\n\t" + "pushl %ebx\n\t" + "pushl %eax\n\t" + "pushfw\n\t" + ); + + stack.ebx.dword = 0; /* 'EOF' marker */ + while ( ( index < count ) && + ( ( index == 0 ) || ( stack.ebx.dword != 0 ) ) ) { + real_call ( rm_meme820, &stack, &stack ); + if ( stack.eax.dword != SMAP ) return 0; + if ( stack.flags.word & CF ) return 0; + buf[index++] = stack.entry; + } + return index; +} + +void get_memsizes(void) +{ + /* Ensure we don't stomp bios data structutres. + * the interrupt table: 0x000 - 0x3ff + * the bios data area: 0x400 - 0x502 + * Dos variables: 0x502 - 0x5ff + */ + static const unsigned min_addr = 0x600; + unsigned i; + unsigned basemem; + basemem = get_free_base_memory(); + meminfo.basememsize = basememsize(); + meminfo.memsize = memsize(); +#ifndef IGNORE_E820_MAP + meminfo.map_count = meme820(meminfo.map, E820MAX); +#else + meminfo.map_count = 0; +#endif + if (meminfo.map_count == 0) { + /* If we don't have an e820 memory map fake it */ + meminfo.map_count = 2; + meminfo.map[0].addr = 0; + meminfo.map[0].size = meminfo.basememsize << 10; + meminfo.map[0].type = E820_RAM; + meminfo.map[1].addr = 1024*1024; + meminfo.map[1].size = meminfo.memsize << 10; + meminfo.map[1].type = E820_RAM; + } + /* Scrub the e820 map */ + for(i = 0; i < meminfo.map_count; i++) { + if (meminfo.map[i].type != E820_RAM) { + continue; + } + /* Reserve the bios data structures */ + if (meminfo.map[i].addr < min_addr) { + unsigned long delta; + delta = min_addr - meminfo.map[i].addr; + if (delta > meminfo.map[i].size) { + delta = meminfo.map[i].size; + } + meminfo.map[i].addr = min_addr; + meminfo.map[i].size -= delta; + } + /* Ensure the returned e820 map is in sync + * with the actual memory state + */ + if ((meminfo.map[i].addr < 0xa0000) && + ((meminfo.map[i].addr + meminfo.map[i].size) > basemem)) + { + if (meminfo.map[i].addr <= basemem) { + meminfo.map[i].size = basemem - meminfo.map[i].addr; + } else { + meminfo.map[i].addr = basemem; + meminfo.map[i].size = 0; + } + } + } +#if MEMSIZES_DEBUG +{ + int i; + printf("basememsize %d\n", meminfo.basememsize); + printf("memsize %d\n", meminfo.memsize); + printf("Memory regions(%d):\n", meminfo.map_count); + for(i = 0; i < meminfo.map_count; i++) { + unsigned long long r_start, r_end; + r_start = meminfo.map[i].addr; + r_end = r_start + meminfo.map[i].size; + printf("[%X%X, %X%X) type %d\n", + (unsigned long)(r_start >> 32), + (unsigned long)r_start, + (unsigned long)(r_end >> 32), + (unsigned long)r_end, + meminfo.map[i].type); +#if defined(CONSOLE_FIRMWARE) + sleep(1); /* No way to see 32 entries on a standard 80x25 screen... */ +#endif + } +} +#endif +} + +#endif /* PCBIOS */ diff --git a/src/arch/i386/include/bits/byteswap.h b/src/arch/i386/include/bits/byteswap.h new file mode 100644 index 00000000..1bc84fda --- /dev/null +++ b/src/arch/i386/include/bits/byteswap.h @@ -0,0 +1,45 @@ +#ifndef ETHERBOOT_BITS_BYTESWAP_H +#define ETHERBOOT_BITS_BYTESWAP_H + +static inline uint16_t __i386_bswap_16(uint16_t x) +{ + __asm__("xchgb %b0,%h0\n\t" + : "=q" (x) + : "0" (x)); + return x; +} + +static inline uint32_t __i386_bswap_32(uint32_t x) +{ + __asm__("xchgb %b0,%h0\n\t" + "rorl $16,%0\n\t" + "xchgb %b0,%h0" + : "=q" (x) + : "0" (x)); + return x; +} + + +#define __bswap_constant_16(x) \ + ((uint16_t)((((uint16_t)(x) & 0x00ff) << 8) | \ + (((uint16_t)(x) & 0xff00) >> 8))) + +#define __bswap_constant_32(x) \ + ((uint32_t)((((uint32_t)(x) & 0x000000ffU) << 24) | \ + (((uint32_t)(x) & 0x0000ff00U) << 8) | \ + (((uint32_t)(x) & 0x00ff0000U) >> 8) | \ + (((uint32_t)(x) & 0xff000000U) >> 24))) + +#define __bswap_16(x) \ + ((uint16_t)(__builtin_constant_p(x) ? \ + __bswap_constant_16(x) : \ + __i386_bswap_16(x))) + + +#define __bswap_32(x) \ + ((uint32_t)(__builtin_constant_p(x) ? \ + __bswap_constant_32(x) : \ + __i386_bswap_32(x))) + + +#endif /* ETHERBOOT_BITS_BYTESWAP_H */ diff --git a/src/arch/i386/include/bits/cpu.h b/src/arch/i386/include/bits/cpu.h new file mode 100644 index 00000000..2e9b27ba --- /dev/null +++ b/src/arch/i386/include/bits/cpu.h @@ -0,0 +1,243 @@ +#ifndef I386_BITS_CPU_H +#define I386_BITS_CPU_H + + +/* Sample usage: CPU_FEATURE_P(cpu.x86_capability, FPU) */ +#define CPU_FEATURE_P(CAP, FEATURE) \ + (!!(CAP[(X86_FEATURE_##FEATURE)/32] & ((X86_FEATURE_##FEATURE) & 0x1f))) + +#define NCAPINTS 4 /* Currently we have 4 32-bit words worth of info */ + +/* Intel-defined CPU features, CPUID level 0x00000001, word 0 */ +#define X86_FEATURE_FPU (0*32+ 0) /* Onboard FPU */ +#define X86_FEATURE_VME (0*32+ 1) /* Virtual Mode Extensions */ +#define X86_FEATURE_DE (0*32+ 2) /* Debugging Extensions */ +#define X86_FEATURE_PSE (0*32+ 3) /* Page Size Extensions */ +#define X86_FEATURE_TSC (0*32+ 4) /* Time Stamp Counter */ +#define X86_FEATURE_MSR (0*32+ 5) /* Model-Specific Registers, RDMSR, WRMSR */ +#define X86_FEATURE_PAE (0*32+ 6) /* Physical Address Extensions */ +#define X86_FEATURE_MCE (0*32+ 7) /* Machine Check Architecture */ +#define X86_FEATURE_CX8 (0*32+ 8) /* CMPXCHG8 instruction */ +#define X86_FEATURE_APIC (0*32+ 9) /* Onboard APIC */ +#define X86_FEATURE_SEP (0*32+11) /* SYSENTER/SYSEXIT */ +#define X86_FEATURE_MTRR (0*32+12) /* Memory Type Range Registers */ +#define X86_FEATURE_PGE (0*32+13) /* Page Global Enable */ +#define X86_FEATURE_MCA (0*32+14) /* Machine Check Architecture */ +#define X86_FEATURE_CMOV (0*32+15) /* CMOV instruction (FCMOVCC and FCOMI too if FPU present) */ +#define X86_FEATURE_PAT (0*32+16) /* Page Attribute Table */ +#define X86_FEATURE_PSE36 (0*32+17) /* 36-bit PSEs */ +#define X86_FEATURE_PN (0*32+18) /* Processor serial number */ +#define X86_FEATURE_CLFLSH (0*32+19) /* Supports the CLFLUSH instruction */ +#define X86_FEATURE_DTES (0*32+21) /* Debug Trace Store */ +#define X86_FEATURE_ACPI (0*32+22) /* ACPI via MSR */ +#define X86_FEATURE_MMX (0*32+23) /* Multimedia Extensions */ +#define X86_FEATURE_FXSR (0*32+24) /* FXSAVE and FXRSTOR instructions (fast save and restore */ + /* of FPU context), and CR4.OSFXSR available */ +#define X86_FEATURE_XMM (0*32+25) /* Streaming SIMD Extensions */ +#define X86_FEATURE_XMM2 (0*32+26) /* Streaming SIMD Extensions-2 */ +#define X86_FEATURE_SELFSNOOP (0*32+27) /* CPU self snoop */ +#define X86_FEATURE_HT (0*32+28) /* Hyper-Threading */ +#define X86_FEATURE_ACC (0*32+29) /* Automatic clock control */ +#define X86_FEATURE_IA64 (0*32+30) /* IA-64 processor */ + +/* AMD-defined CPU features, CPUID level 0x80000001, word 1 */ +/* Don't duplicate feature flags which are redundant with Intel! */ +#define X86_FEATURE_SYSCALL (1*32+11) /* SYSCALL/SYSRET */ +#define X86_FEATURE_MMXEXT (1*32+22) /* AMD MMX extensions */ +#define X86_FEATURE_LM (1*32+29) /* Long Mode (x86-64) */ +#define X86_FEATURE_3DNOWEXT (1*32+30) /* AMD 3DNow! extensions */ +#define X86_FEATURE_3DNOW (1*32+31) /* 3DNow! */ + +/* Transmeta-defined CPU features, CPUID level 0x80860001, word 2 */ +#define X86_FEATURE_RECOVERY (2*32+ 0) /* CPU in recovery mode */ +#define X86_FEATURE_LONGRUN (2*32+ 1) /* Longrun power control */ +#define X86_FEATURE_LRTI (2*32+ 3) /* LongRun table interface */ + +/* Other features, Linux-defined mapping, word 3 */ +/* This range is used for feature bits which conflict or are synthesized */ +#define X86_FEATURE_CXMMX (3*32+ 0) /* Cyrix MMX extensions */ +#define X86_FEATURE_K6_MTRR (3*32+ 1) /* AMD K6 nonstandard MTRRs */ +#define X86_FEATURE_CYRIX_ARR (3*32+ 2) /* Cyrix ARRs (= MTRRs) */ +#define X86_FEATURE_CENTAUR_MCR (3*32+ 3) /* Centaur MCRs (= MTRRs) */ + +#define MAX_X86_VENDOR_ID 16 +struct cpuinfo_x86 { + uint8_t x86; /* CPU family */ + uint8_t x86_model; + uint8_t x86_mask; + + int cpuid_level; /* Maximum supported CPUID level, -1=no CPUID */ + unsigned x86_capability[NCAPINTS]; + char x86_vendor_id[MAX_X86_VENDOR_ID]; +}; + + +#define X86_VENDOR_INTEL 0 +#define X86_VENDOR_CYRIX 1 +#define X86_VENDOR_AMD 2 +#define X86_VENDOR_UMC 3 +#define X86_VENDOR_NEXGEN 4 +#define X86_VENDOR_CENTAUR 5 +#define X86_VENDOR_RISE 6 +#define X86_VENDOR_TRANSMETA 7 +#define X86_VENDOR_NSC 8 +#define X86_VENDOR_UNKNOWN 0xff + +/* + * EFLAGS bits + */ +#define X86_EFLAGS_CF 0x00000001 /* Carry Flag */ +#define X86_EFLAGS_PF 0x00000004 /* Parity Flag */ +#define X86_EFLAGS_AF 0x00000010 /* Auxillary carry Flag */ +#define X86_EFLAGS_ZF 0x00000040 /* Zero Flag */ +#define X86_EFLAGS_SF 0x00000080 /* Sign Flag */ +#define X86_EFLAGS_TF 0x00000100 /* Trap Flag */ +#define X86_EFLAGS_IF 0x00000200 /* Interrupt Flag */ +#define X86_EFLAGS_DF 0x00000400 /* Direction Flag */ +#define X86_EFLAGS_OF 0x00000800 /* Overflow Flag */ +#define X86_EFLAGS_IOPL 0x00003000 /* IOPL mask */ +#define X86_EFLAGS_NT 0x00004000 /* Nested Task */ +#define X86_EFLAGS_RF 0x00010000 /* Resume Flag */ +#define X86_EFLAGS_VM 0x00020000 /* Virtual Mode */ +#define X86_EFLAGS_AC 0x00040000 /* Alignment Check */ +#define X86_EFLAGS_VIF 0x00080000 /* Virtual Interrupt Flag */ +#define X86_EFLAGS_VIP 0x00100000 /* Virtual Interrupt Pending */ +#define X86_EFLAGS_ID 0x00200000 /* CPUID detection flag */ + +/* + * Generic CPUID function + */ +static inline void cpuid(int op, + unsigned int *eax, unsigned int *ebx, unsigned int *ecx, unsigned int *edx) +{ + __asm__("cpuid" + : "=a" (*eax), + "=b" (*ebx), + "=c" (*ecx), + "=d" (*edx) + : "0" (op)); +} + +/* + * CPUID functions returning a single datum + */ +static inline unsigned int cpuid_eax(unsigned int op) +{ + unsigned int eax; + + __asm__("cpuid" + : "=a" (eax) + : "0" (op) + : "bx", "cx", "dx"); + return eax; +} +static inline unsigned int cpuid_ebx(unsigned int op) +{ + unsigned int eax, ebx; + + __asm__("cpuid" + : "=a" (eax), "=b" (ebx) + : "0" (op) + : "cx", "dx" ); + return ebx; +} +static inline unsigned int cpuid_ecx(unsigned int op) +{ + unsigned int eax, ecx; + + __asm__("cpuid" + : "=a" (eax), "=c" (ecx) + : "0" (op) + : "bx", "dx" ); + return ecx; +} +static inline unsigned int cpuid_edx(unsigned int op) +{ + unsigned int eax, edx; + + __asm__("cpuid" + : "=a" (eax), "=d" (edx) + : "0" (op) + : "bx", "cx"); + return edx; +} + +/* + * Intel CPU features in CR4 + */ +#define X86_CR4_VME 0x0001 /* enable vm86 extensions */ +#define X86_CR4_PVI 0x0002 /* virtual interrupts flag enable */ +#define X86_CR4_TSD 0x0004 /* disable time stamp at ipl 3 */ +#define X86_CR4_DE 0x0008 /* enable debugging extensions */ +#define X86_CR4_PSE 0x0010 /* enable page size extensions */ +#define X86_CR4_PAE 0x0020 /* enable physical address extensions */ +#define X86_CR4_MCE 0x0040 /* Machine check enable */ +#define X86_CR4_PGE 0x0080 /* enable global pages */ +#define X86_CR4_PCE 0x0100 /* enable performance counters at ipl 3 */ +#define X86_CR4_OSFXSR 0x0200 /* enable fast FPU save and restore */ +#define X86_CR4_OSXMMEXCPT 0x0400 /* enable unmasked SSE exceptions */ + + +#define MSR_K6_EFER 0xC0000080 +/* EFER bits: */ +#define _EFER_SCE 0 /* SYSCALL/SYSRET */ +#define _EFER_LME 8 /* Long mode enable */ +#define _EFER_LMA 10 /* Long mode active (read-only) */ +#define _EFER_NX 11 /* No execute enable */ + +#define EFER_SCE (1<<_EFER_SCE) +#define EFER_LME (1<<EFER_LME) +#define EFER_LMA (1<<EFER_LMA) +#define EFER_NX (1<<_EFER_NX) + +#define rdmsr(msr,val1,val2) \ + __asm__ __volatile__("rdmsr" \ + : "=a" (val1), "=d" (val2) \ + : "c" (msr)) + +#define wrmsr(msr,val1,val2) \ + __asm__ __volatile__("wrmsr" \ + : /* no outputs */ \ + : "c" (msr), "a" (val1), "d" (val2)) + + +#define read_cr0() ({ \ + unsigned int __dummy; \ + __asm__( \ + "movl %%cr0, %0\n\t" \ + :"=r" (__dummy)); \ + __dummy; \ +}) +#define write_cr0(x) \ + __asm__("movl %0,%%cr0": :"r" (x)); + +#define read_cr3() ({ \ + unsigned int __dummy; \ + __asm__( \ + "movl %%cr3, %0\n\t" \ + :"=r" (__dummy)); \ + __dummy; \ +}) +#define write_cr3x(x) \ + __asm__("movl %0,%%cr3": :"r" (x)); + + +#define read_cr4() ({ \ + unsigned int __dummy; \ + __asm__( \ + "movl %%cr4, %0\n\t" \ + :"=r" (__dummy)); \ + __dummy; \ +}) +#define write_cr4x(x) \ + __asm__("movl %0,%%cr4": :"r" (x)); + + +extern struct cpuinfo_x86 cpu_info; +#ifdef CONFIG_X86_64 +extern void cpu_setup(void); +#else +#define cpu_setup() do {} while(0) +#endif + +#endif /* I386_BITS_CPU_H */ diff --git a/src/arch/i386/include/bits/elf.h b/src/arch/i386/include/bits/elf.h new file mode 100644 index 00000000..dad9c7b8 --- /dev/null +++ b/src/arch/i386/include/bits/elf.h @@ -0,0 +1,91 @@ +#ifndef I386_BITS_ELF_H +#define I386_BITS_ELF_H + +#include "cpu.h" + +#ifdef CONFIG_X86_64 +/* ELF Defines for the 64bit version of the current architecture */ +#define EM_CURRENT_64 EM_X86_64 +#define EM_CURRENT_64_PRESENT ( \ + CPU_FEATURE_P(cpu_info.x86_capability, LM) && \ + CPU_FEATURE_P(cpu_info.x86_capability, PAE) && \ + CPU_FEATURE_P(cpu_info.x86_capability, PSE)) + +#define ELF_CHECK_X86_64_ARCH(x) \ + (EM_CURRENT_64_PRESENT && ((x).e_machine == EM_X86_64)) +#define __unused_i386 +#else +#define ELF_CHECK_X86_64_ARCH(x) 0 +#define __unused_i386 __unused +#endif + + +/* ELF Defines for the current architecture */ +#define EM_CURRENT EM_386 +#define ELFDATA_CURRENT ELFDATA2LSB + +#define ELF_CHECK_I386_ARCH(x) \ + (((x).e_machine == EM_386) || ((x).e_machine == EM_486)) + +#define ELF_CHECK_ARCH(x) \ + ((ELF_CHECK_I386_ARCH(x) || ELF_CHECK_X86_64_ARCH(x)) && \ + ((x).e_entry <= 0xffffffffUL)) + +#ifdef IMAGE_FREEBSD +/* + * FreeBSD has this rather strange "feature" of its design. + * At some point in its evolution, FreeBSD started to rely + * externally on private/static/debug internal symbol information. + * That is, some of the interfaces that software uses to access + * and work with the FreeBSD kernel are made available not + * via the shared library symbol information (the .DYNAMIC section) + * but rather the debug symbols. This means that any symbol, not + * just publicly defined symbols can be (and are) used by system + * tools to make the system work. (such as top, swapinfo, swapon, + * etc) + * + * Even worse, however, is the fact that standard ELF loaders do + * not know how to load the symbols since they are not within + * an ELF PT_LOAD section. The kernel needs these symbols to + * operate so the following changes/additions to the boot + * loading of EtherBoot have been made to get the kernel to load. + * All of the changes are within IMAGE_FREEBSD such that the + * extra/changed code only compiles when FREEBSD support is + * enabled. + */ + +/* + * Section header for FreeBSD (debug symbol kludge!) support + */ +typedef struct { + Elf32_Word sh_name; /* Section name (index into the + section header string table). */ + Elf32_Word sh_type; /* Section type. */ + Elf32_Word sh_flags; /* Section flags. */ + Elf32_Addr sh_addr; /* Address in memory image. */ + Elf32_Off sh_offset; /* Offset in file. */ + Elf32_Size sh_size; /* Size in bytes. */ + Elf32_Word sh_link; /* Index of a related section. */ + Elf32_Word sh_info; /* Depends on section type. */ + Elf32_Size sh_addralign; /* Alignment in bytes. */ + Elf32_Size sh_entsize; /* Size of each entry in section. */ +} Elf32_Shdr; + +/* sh_type */ +#define SHT_SYMTAB 2 /* symbol table section */ +#define SHT_STRTAB 3 /* string table section */ + +/* + * Module information subtypes (for the metadata that we need to build) + */ +#define MODINFO_END 0x0000 /* End of list */ +#define MODINFO_NAME 0x0001 /* Name of module (string) */ +#define MODINFO_TYPE 0x0002 /* Type of module (string) */ +#define MODINFO_METADATA 0x8000 /* Module-specfic */ + +#define MODINFOMD_SSYM 0x0003 /* start of symbols */ +#define MODINFOMD_ESYM 0x0004 /* end of symbols */ + +#endif /* IMAGE_FREEBSD */ + +#endif /* I386_BITS_ELF_H */ diff --git a/src/arch/i386/include/bits/elf_x.h b/src/arch/i386/include/bits/elf_x.h new file mode 100644 index 00000000..86c67250 --- /dev/null +++ b/src/arch/i386/include/bits/elf_x.h @@ -0,0 +1,5 @@ +#define ARCH_ELF_CLASS ELFCLASS32 +#define ARCH_ELF_DATA ELFDATA2LSB +#define ARCH_ELF_MACHINE_OK(x) ((x)==EM_386 || (x)==EM_486) +typedef Elf32_Ehdr Elf_ehdr; +typedef Elf32_Phdr Elf_phdr; diff --git a/src/arch/i386/include/bits/eltorito.h b/src/arch/i386/include/bits/eltorito.h new file mode 100644 index 00000000..d43e9aac --- /dev/null +++ b/src/arch/i386/include/bits/eltorito.h @@ -0,0 +1,3 @@ +#ifndef ELTORITO_PLATFORM +#define ELTORITO_PLATFORM ELTORITO_PLATFORM_X86 +#endif /* ELTORITO_PLATFORM */ diff --git a/src/arch/i386/include/bits/endian.h b/src/arch/i386/include/bits/endian.h new file mode 100644 index 00000000..b23b233a --- /dev/null +++ b/src/arch/i386/include/bits/endian.h @@ -0,0 +1,9 @@ +#ifndef ETHERBOOT_BITS_ENDIAN_H +#define ETHERBOOT_BITS_ENDIAN_H + +#define __BYTE_ORDER __LITTLE_ENDIAN + +#define le32_to_cpup(x) (*(uint32_t *)(x)) +#define cpu_to_le16p(x) (*(uint16_t*)(x)) + +#endif /* ETHERBOOT_BITS_ENDIAN_H */ diff --git a/src/arch/i386/include/bits/string.h b/src/arch/i386/include/bits/string.h new file mode 100644 index 00000000..3743d011 --- /dev/null +++ b/src/arch/i386/include/bits/string.h @@ -0,0 +1,99 @@ +#ifndef ETHERBOOT_BITS_STRING_H +#define ETHERBOOT_BITS_STRING_H +/* + * Taken from Linux /usr/include/asm/string.h + * All except memcpy, memmove, memset and memcmp removed. + */ + +/* + * This string-include defines all string functions as inline + * functions. Use gcc. It also assumes ds=es=data space, this should be + * normal. Most of the string-functions are rather heavily hand-optimized, + * see especially strtok,strstr,str[c]spn. They should work, but are not + * very easy to understand. Everything is done entirely within the register + * set, making the functions fast and clean. String instructions have been + * used through-out, making for "slightly" unclear code :-) + * + * NO Copyright (C) 1991, 1992 Linus Torvalds, + * consider these trivial functions to be PD. + */ + + +#define __HAVE_ARCH_MEMMOVE +static inline void * memmove(void * dest,const void * src, size_t n) +{ +int d0, d1, d2; +if (dest<src) +__asm__ __volatile__( + "cld\n\t" + "rep\n\t" + "movsb" + : "=&c" (d0), "=&S" (d1), "=&D" (d2) + :"0" (n),"1" (src),"2" (dest) + : "memory"); +else +__asm__ __volatile__( + "std\n\t" + "rep\n\t" + "movsb\n\t" + "cld" + : "=&c" (d0), "=&S" (d1), "=&D" (d2) + :"0" (n), + "1" (n-1+(const char *)src), + "2" (n-1+(char *)dest) + :"memory"); +return dest; +} + +#define __HAVE_ARCH_MEMSET +static inline void *memset(void *s, int c,size_t count) +{ +int d0, d1; +__asm__ __volatile__( + "cld\n\t" + "rep\n\t" + "stosb" + : "=&c" (d0), "=&D" (d1) + :"a" (c),"1" (s),"0" (count) + :"memory"); +return s; +} + +#define __HAVE_ARCH_STRNCMP +static inline int strncmp(const char * cs,const char * ct,size_t count) +{ +register int __res; +int d0, d1, d2; +__asm__ __volatile__( + "1:\tdecl %3\n\t" + "js 2f\n\t" + "lodsb\n\t" + "scasb\n\t" + "jne 3f\n\t" + "testb %%al,%%al\n\t" + "jne 1b\n" + "2:\txorl %%eax,%%eax\n\t" + "jmp 4f\n" + "3:\tsbbl %%eax,%%eax\n\t" + "orb $1,%%al\n" + "4:" + :"=a" (__res), "=&S" (d0), "=&D" (d1), "=&c" (d2) + :"1" (cs),"2" (ct),"3" (count)); +return __res; +} + +#define __HAVE_ARCH_STRLEN +static inline size_t strlen(const char * s) +{ +int d0; +register int __res; +__asm__ __volatile__( + "repne\n\t" + "scasb\n\t" + "notl %0\n\t" + "decl %0" + :"=c" (__res), "=&D" (d0) :"1" (s),"a" (0), "0" (0xffffffff)); +return __res; +} + +#endif /* ETHERBOOT_BITS_STRING_H */ diff --git a/src/arch/i386/include/callbacks_arch.h b/src/arch/i386/include/callbacks_arch.h new file mode 100644 index 00000000..f9cba488 --- /dev/null +++ b/src/arch/i386/include/callbacks_arch.h @@ -0,0 +1,243 @@ +/* Callout/callback interface for Etherboot + * + * This file provides the mechanisms for making calls from Etherboot + * to external programs and vice-versa. + * + * Initial version by Michael Brown <mbrown@fensystems.co.uk>, January 2004. + * + * $Id$ + */ + +#ifndef CALLBACKS_ARCH_H +#define CALLBACKS_ARCH_H + +/* Skip the definitions that won't make sense to the assembler */ +#ifndef ASSEMBLY + +/* Struct to hold general-purpose register values. PUSHAL and POPAL + * can work directly with this structure; do not change the order of + * registers. + */ +typedef struct { + union { + uint16_t di; + uint32_t edi; + }; + union { + uint16_t si; + uint32_t esi; + }; + union { + uint16_t bp; + uint32_t ebp; + }; + union { + uint16_t sp; + uint32_t esp; + }; + union { + struct { + uint8_t bl; + uint8_t bh; + } PACKED; + uint16_t bx; + uint32_t ebx; + }; + union { + struct { + uint8_t dl; + uint8_t dh; + } PACKED; + uint16_t dx; + uint32_t edx; + }; + union { + struct { + uint8_t cl; + uint8_t ch; + } PACKED; + uint16_t cx; + uint32_t ecx; + }; + union { + struct { + uint8_t al; + uint8_t ah; + } PACKED; + uint16_t ax; + uint32_t eax; + }; +} regs_t; + +/* Struct to hold segment register values. Don't change the order; + * many bits of assembly code rely on it. + */ +typedef struct { + uint16_t cs; + uint16_t ss; + uint16_t ds; + uint16_t es; + uint16_t fs; + uint16_t gs; +} PACKED seg_regs_t; + +/* Struct for a GDT descriptor */ +typedef struct { + uint16_t limit; + uint32_t address; + uint16_t padding; +} PACKED gdt_descriptor_t; + +/* Struct for a GDT entry. Use GDT_SEGMENT() to fill it in. + */ +typedef struct { + uint16_t limit_0_15; + uint16_t base_0_15; + uint8_t base_16_23; + uint8_t accessed__type__sflag__dpl__present; + uint8_t limit_16_19__avl__size__granularity; + uint8_t base_24_31; +} PACKED gdt_segment_t; + +#define GDT_SEGMENT(base,limit,type,sflag,dpl,avl,size,granularity) \ + ( (gdt_segment_t) { \ + ( (limit) & 0xffff ), \ + ( (base) & 0xffff ), \ + ( ( (base) >> 16 ) & 0xff ), \ + ( ( 1 << 0 ) | ( (type) << 1 ) | \ + ( (sflag) << 4 ) | ( (dpl) << 5 ) | ( 1 << 7 ) ), \ + ( ( (limit) >> 16 ) | \ + ( (avl) << 4 ) | ( (size) << 5 ) | ( (granularity) << 7 ) ),\ + ( (base) >> 24 ) \ + } ) +#define GDT_SEGMENT_BASE(gdt_segment) \ + ( (gdt_segment)->base_0_15 | \ + (gdt_segment)->base_16_23 << 16 | \ + (gdt_segment)->base_24_31 << 24 ) +#define GDT_SEGMENT_LIMIT(gdt_segment) \ + ( (gdt_segment)->limit_0_15 | \ + ( ( (gdt_segment)->limit_16_19__avl__size__granularity \ + & 0xf ) << 16 ) ) +#define GDT_SEGMENT_GRANULARITY(gdt_segment) \ + ( ( (gdt_segment)->limit_16_19__avl__size__granularity \ + & 0x80 ) >> 7 ) +#define GDT_SEGMENT_TYPE(gdt_segment) \ + ( ( (gdt_segment)->accessed__type__sflag__dpl__present & 0x0e ) >> 1 ) +#define GDT_SEGMENT_SIZE(gdt_segment) \ + ( ( (gdt_segment)->limit_16_19__avl__size__granularity \ + & 0x60 ) >> 5 ) + +#define GDT_TYPE_DATA (0x0) +#define GDT_TYPE_STACK (0x2) +#define GDT_TYPE_WRITEABLE (0x1) +#define GDT_TYPE_CODE (0x6) +#define GDT_TYPE_EXEC_ONLY_CODE (0x4) +#define GDT_TYPE_CONFORMING (0x1) +#define GDT_SFLAG_SYSTEM (0) +#define GDT_SFLAG_NORMAL (1) +#define GDT_AVL_NORMAL (0) +#define GDT_SIZE_16BIT (0x0) +#define GDT_SIZE_32BIT (0x2) +#define GDT_SIZE_64BIT (0x1) +#define GDT_SIZE_UNKNOWN (0x3) +#define GDT_GRANULARITY_SMALL (0) +#define GDT_GRANULARITY_LARGE (1) +#define GDT_SEGMENT_NORMAL(base,limit,type,size,granularity) \ + GDT_SEGMENT ( base, limit, type, \ + GDT_SFLAG_NORMAL, 0, GDT_AVL_NORMAL, \ + size, granularity ) + +/* Protected mode code segment */ +#define GDT_SEGMENT_PMCS(base) GDT_SEGMENT_NORMAL ( \ + base, 0xfffff, GDT_TYPE_CODE | GDT_TYPE_CONFORMING, \ + GDT_SIZE_32BIT, GDT_GRANULARITY_LARGE ) +#define GDT_SEGMENT_PMCS_PHYS GDT_SEGMENT_PMCS(0) +/* Protected mode data segment */ +#define GDT_SEGMENT_PMDS(base) GDT_SEGMENT_NORMAL ( \ + base, 0xfffff, GDT_TYPE_DATA | GDT_TYPE_WRITEABLE, \ + GDT_SIZE_32BIT, GDT_GRANULARITY_LARGE ) +#define GDT_SEGMENT_PMDS_PHYS GDT_SEGMENT_PMDS(0) +/* Real mode code segment */ +/* Not sure if there's any reason to use GDT_TYPE_EXEC_ONLY_CODE + * instead of just GDT_TYPE_CODE, but that's what our old GDT did and + * it worked, so I'm not changing it. + */ +#define GDT_SEGMENT_RMCS(base) GDT_SEGMENT_NORMAL ( \ + base, 0xffff, GDT_TYPE_EXEC_ONLY_CODE | GDT_TYPE_CONFORMING, \ + GDT_SIZE_16BIT, GDT_GRANULARITY_SMALL ) +/* Real mode data segment */ +#define GDT_SEGMENT_RMDS(base) GDT_SEGMENT_NORMAL ( \ + base, 0xffff, GDT_TYPE_DATA | GDT_TYPE_WRITEABLE, \ + GDT_SIZE_16BIT, GDT_GRANULARITY_SMALL ) +/* Long mode code segment */ +#define GDT_SEGMENT_LMCS(base) GDT_SEGMENT_NORMAL ( \ + base, 0xfffff, GDT_TYPE_CODE | GDT_TYPE_CONFORMING, \ + GDT_SIZE_64BIT, GDT_GRANULARITY_LARGE ) +#define GDT_SEGMENT_LMCS_PHYS GDT_SEGMENT_LMCS(0) +/* Long mode data segment */ +/* AFIACT, GDT_SIZE_64BIT applies only to code segments */ +#define GDT_SEGMENT_LMDS(base) GDT_SEGMENT_NORMAL ( \ + base, 0xfffff, GDT_TYPE_DATA | GDT_TYPE_WRITEABLE, \ + GDT_SIZE_32BIT, GDT_GRANULARITY_LARGE ) +#define GDT_SEGMENT_LMDS_PHYS GDT_SEGMENT_LMDS(0) + +/* Template for creating GDT structures (including segment register + * lists), suitable for passing as parameters to external_call(). + */ +#define GDT_STRUCT_t(num_segments) \ + struct { \ + gdt_descriptor_t descriptor; \ + gdt_segment_t segments[num_segments]; \ + } PACKED +/* And utility function for filling it in */ +#define GDT_ADJUST(structure) { \ + (structure)->descriptor.address = \ + virt_to_phys(&((structure)->descriptor.limit)); \ + (structure)->descriptor.limit = \ + sizeof((structure)->segments) + 8 - 1; \ + (structure)->descriptor.padding = 0; \ +} + +/* Data passed in to in_call() by assembly wrapper. + */ +typedef struct { + regs_t regs; + seg_regs_t seg_regs; + gdt_descriptor_t gdt_desc; + uint32_t flags; + struct { + uint32_t offset; + uint32_t segment; + } ret_addr; +} PACKED i386_pm_in_call_data_t; + +typedef struct { + seg_regs_t seg_regs; + union { + uint16_t pad; + uint16_t prefix_sp; + }; + uint16_t flags; + struct { + uint16_t offset; + uint16_t segment; + } ret_addr; + uint32_t orig_opcode; +} PACKED i386_rm_in_call_data_t; + +typedef struct { + i386_pm_in_call_data_t *pm; + i386_rm_in_call_data_t *rm; +} i386_in_call_data_t; +#define in_call_data_t i386_in_call_data_t + +/* Function prototypes + */ +extern int install_rm_callback_interface ( void *address, size_t available ); + +#endif /* ASSEMBLY */ + +#define RM_IN_CALL (0) +#define RM_IN_CALL_FAR (2) + +#endif /* CALLBACKS_ARCH_H */ diff --git a/src/arch/i386/include/hidemem.h b/src/arch/i386/include/hidemem.h new file mode 100644 index 00000000..600a8692 --- /dev/null +++ b/src/arch/i386/include/hidemem.h @@ -0,0 +1,21 @@ +#ifndef HIDEMEM_H +#define HIDEMEM_H + +#include "segoff.h" + +extern int install_e820mangler ( void *new_mangler ); +extern int hide_etherboot ( void ); +extern int unhide_etherboot ( void ); + +/* Symbols in e820mangler.S */ +extern void e820mangler ( void ); +extern void _intercept_int15 ( void ); +extern segoff_t _intercepted_int15; +typedef struct { + uint32_t start; + uint32_t length; +} exclude_range_t; +extern exclude_range_t _hide_memory[2]; +extern uint16_t e820mangler_size; + +#endif /* HIDEMEM_H */ diff --git a/src/arch/i386/include/hooks.h b/src/arch/i386/include/hooks.h new file mode 100644 index 00000000..0764edaf --- /dev/null +++ b/src/arch/i386/include/hooks.h @@ -0,0 +1,9 @@ +#ifndef ETHERBOOT_I386_HOOKS_H +#define ETHERBOOT_I386_HOOKS_H + +void arch_main(in_call_data_t *data, va_list params); +void arch_on_exit(int status); +#define arch_relocate_to(addr) +void arch_relocated_from ( uint32_t old_addr ); + +#endif /* ETHERBOOT_I386_HOOKS_H */ diff --git a/src/arch/i386/include/io.h b/src/arch/i386/include/io.h new file mode 100644 index 00000000..e351a0c1 --- /dev/null +++ b/src/arch/i386/include/io.h @@ -0,0 +1,246 @@ +#ifndef ETHERBOOT_IO_H +#define ETHERBOOT_IO_H + + +/* Amount of relocation etherboot is experiencing */ +extern unsigned long virt_offset; + +/* Don't require identity mapped physical memory, + * osloader.c is the only valid user at the moment. + */ +static inline unsigned long virt_to_phys(volatile const void *virt_addr) +{ + return ((unsigned long)virt_addr) + virt_offset; +} + +static inline void *phys_to_virt(unsigned long phys_addr) +{ + return (void *)(phys_addr - virt_offset); +} + +/* virt_to_bus converts an addresss inside of etherboot [_start, _end] + * into a memory access cards can use. + */ +#define virt_to_bus virt_to_phys + + +/* bus_to_virt reverses virt_to_bus, the address must be output + * from virt_to_bus to be valid. This function does not work on + * all bus addresses. + */ +#define bus_to_virt phys_to_virt + +/* ioremap converts a random 32bit bus address into something + * etherboot can access. + */ +static inline void *ioremap(unsigned long bus_addr, unsigned long length __unused) +{ + return bus_to_virt(bus_addr); +} + +/* iounmap cleans up anything ioremap had to setup */ +static inline void iounmap(void *virt_addr __unused) +{ + return; +} + +/* + * This file contains the definitions for the x86 IO instructions + * inb/inw/inl/outb/outw/outl and the "string versions" of the same + * (insb/insw/insl/outsb/outsw/outsl). You can also use "pausing" + * versions of the single-IO instructions (inb_p/inw_p/..). + * + * This file is not meant to be obfuscating: it's just complicated + * to (a) handle it all in a way that makes gcc able to optimize it + * as well as possible and (b) trying to avoid writing the same thing + * over and over again with slight variations and possibly making a + * mistake somewhere. + */ + +/* + * Thanks to James van Artsdalen for a better timing-fix than + * the two short jumps: using outb's to a nonexistent port seems + * to guarantee better timings even on fast machines. + * + * On the other hand, I'd like to be sure of a non-existent port: + * I feel a bit unsafe about using 0x80 (should be safe, though) + * + * Linus + */ + +#ifdef SLOW_IO_BY_JUMPING +#define __SLOW_DOWN_IO __asm__ __volatile__("jmp 1f\n1:\tjmp 1f\n1:") +#else +#define __SLOW_DOWN_IO __asm__ __volatile__("outb %al,$0x80") +#endif + +#ifdef REALLY_SLOW_IO +#define SLOW_DOWN_IO { __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; } +#else +#define SLOW_DOWN_IO __SLOW_DOWN_IO +#endif + +/* + * readX/writeX() are used to access memory mapped devices. On some + * architectures the memory mapped IO stuff needs to be accessed + * differently. On the x86 architecture, we just read/write the + * memory location directly. + */ +#define readb(addr) (*(volatile unsigned char *) (addr)) +#define readw(addr) (*(volatile unsigned short *) (addr)) +#define readl(addr) (*(volatile unsigned int *) (addr)) + +#define writeb(b,addr) ((*(volatile unsigned char *) (addr)) = (b)) +#define writew(b,addr) ((*(volatile unsigned short *) (addr)) = (b)) +#define writel(b,addr) ((*(volatile unsigned int *) (addr)) = (b)) + +#define memcpy_fromio(a,b,c) memcpy((a),(void *)(b),(c)) +#define memcpy_toio(a,b,c) memcpy((void *)(a),(b),(c)) + +/* + * Force strict CPU ordering. + * And yes, this is required on UP too when we're talking + * to devices. + * + * For now, "wmb()" doesn't actually do anything, as all + * Intel CPU's follow what Intel calls a *Processor Order*, + * in which all writes are seen in the program order even + * outside the CPU. + * + * I expect future Intel CPU's to have a weaker ordering, + * but I'd also expect them to finally get their act together + * and add some real memory barriers if so. + * + * Some non intel clones support out of order store. wmb() ceases to be a + * nop for these. + */ + +#define mb() __asm__ __volatile__ ("lock; addl $0,0(%%esp)": : :"memory") +#define rmb() mb() +#define wmb() mb(); + + +/* + * Talk about misusing macros.. + */ + +#define __OUT1(s,x) \ +extern void __out##s(unsigned x value, unsigned short port); \ +extern inline void __out##s(unsigned x value, unsigned short port) { + +#define __OUT2(s,s1,s2) \ +__asm__ __volatile__ ("out" #s " %" s1 "0,%" s2 "1" + +#define __OUT(s,s1,x) \ +__OUT1(s,x) __OUT2(s,s1,"w") : : "a" (value), "d" (port)); } \ +__OUT1(s##c,x) __OUT2(s,s1,"") : : "a" (value), "id" (port)); } \ +__OUT1(s##_p,x) __OUT2(s,s1,"w") : : "a" (value), "d" (port)); SLOW_DOWN_IO; } \ +__OUT1(s##c_p,x) __OUT2(s,s1,"") : : "a" (value), "id" (port)); SLOW_DOWN_IO; } + +#define __IN1(s,x) \ +extern unsigned x __in##s(unsigned short port); \ +extern inline unsigned x __in##s(unsigned short port) { unsigned x _v; + +#define __IN2(s,s1,s2) \ +__asm__ __volatile__ ("in" #s " %" s2 "1,%" s1 "0" + +#define __IN(s,s1,x,i...) \ +__IN1(s,x) __IN2(s,s1,"w") : "=a" (_v) : "d" (port) ,##i ); return _v; } \ +__IN1(s##c,x) __IN2(s,s1,"") : "=a" (_v) : "id" (port) ,##i ); return _v; } \ +__IN1(s##_p,x) __IN2(s,s1,"w") : "=a" (_v) : "d" (port) ,##i ); SLOW_DOWN_IO; return _v; } \ +__IN1(s##c_p,x) __IN2(s,s1,"") : "=a" (_v) : "id" (port) ,##i ); SLOW_DOWN_IO; return _v; } + +#define __INS(s) \ +extern void ins##s(unsigned short port, void * addr, unsigned long count); \ +extern inline void ins##s(unsigned short port, void * addr, unsigned long count) \ +{ __asm__ __volatile__ ("cld ; rep ; ins" #s \ +: "=D" (addr), "=c" (count) : "d" (port),"0" (addr),"1" (count)); } + +#define __OUTS(s) \ +extern void outs##s(unsigned short port, const void * addr, unsigned long count); \ +extern inline void outs##s(unsigned short port, const void * addr, unsigned long count) \ +{ __asm__ __volatile__ ("cld ; rep ; outs" #s \ +: "=S" (addr), "=c" (count) : "d" (port),"0" (addr),"1" (count)); } + +__IN(b,"", char) +__IN(w,"",short) +__IN(l,"", long) + +__OUT(b,"b",char) +__OUT(w,"w",short) +__OUT(l,,int) + +__INS(b) +__INS(w) +__INS(l) + +__OUTS(b) +__OUTS(w) +__OUTS(l) + +/* + * Note that due to the way __builtin_constant_p() works, you + * - can't use it inside a inline function (it will never be true) + * - you don't have to worry about side effects within the __builtin.. + */ +#define outb(val,port) \ +((__builtin_constant_p((port)) && (port) < 256) ? \ + __outbc((val),(port)) : \ + __outb((val),(port))) + +#define inb(port) \ +((__builtin_constant_p((port)) && (port) < 256) ? \ + __inbc(port) : \ + __inb(port)) + +#define outb_p(val,port) \ +((__builtin_constant_p((port)) && (port) < 256) ? \ + __outbc_p((val),(port)) : \ + __outb_p((val),(port))) + +#define inb_p(port) \ +((__builtin_constant_p((port)) && (port) < 256) ? \ + __inbc_p(port) : \ + __inb_p(port)) + +#define outw(val,port) \ +((__builtin_constant_p((port)) && (port) < 256) ? \ + __outwc((val),(port)) : \ + __outw((val),(port))) + +#define inw(port) \ +((__builtin_constant_p((port)) && (port) < 256) ? \ + __inwc(port) : \ + __inw(port)) + +#define outw_p(val,port) \ +((__builtin_constant_p((port)) && (port) < 256) ? \ + __outwc_p((val),(port)) : \ + __outw_p((val),(port))) + +#define inw_p(port) \ +((__builtin_constant_p((port)) && (port) < 256) ? \ + __inwc_p(port) : \ + __inw_p(port)) + +#define outl(val,port) \ +((__builtin_constant_p((port)) && (port) < 256) ? \ + __outlc((val),(port)) : \ + __outl((val),(port))) + +#define inl(port) \ +((__builtin_constant_p((port)) && (port) < 256) ? \ + __inlc(port) : \ + __inl(port)) + +#define outl_p(val,port) \ +((__builtin_constant_p((port)) && (port) < 256) ? \ + __outlc_p((val),(port)) : \ + __outl_p((val),(port))) + +#define inl_p(port) \ +((__builtin_constant_p((port)) && (port) < 256) ? \ + __inlc_p(port) : \ + __inl_p(port)) + +#endif /* ETHERBOOT_IO_H */ diff --git a/src/arch/i386/include/latch.h b/src/arch/i386/include/latch.h new file mode 100644 index 00000000..38a8bd21 --- /dev/null +++ b/src/arch/i386/include/latch.h @@ -0,0 +1,10 @@ +#ifndef LATCH_H +#define LATCH_H + +#define TICKS_PER_SEC 18 + +/* For different calibrators of the TSC move the declaration of + * sleep_latch and the definitions of it's length here... + */ + +#endif /* LATCH_H */ diff --git a/src/arch/i386/include/limits.h b/src/arch/i386/include/limits.h new file mode 100644 index 00000000..f13db267 --- /dev/null +++ b/src/arch/i386/include/limits.h @@ -0,0 +1,59 @@ +#ifndef LIMITS_H +#define LIMITS_H 1 + +/* Number of bits in a `char' */ +#define CHAR_BIT 8 + +/* Minimum and maximum values a `signed char' can hold */ +#define SCHAR_MIN (-128) +#define SCHAR_MAX 127 + +/* Maximum value an `unsigned char' can hold. (Minimum is 0.) */ +#define UCHAR_MAX 255 + +/* Minimum and maximum values a `char' can hold */ +#define CHAR_MIN SCHAR_MIN +#define CHAR_MAX SCHAR_MAX + +/* Minimum and maximum values a `signed short int' can hold */ +#define SHRT_MIN (-32768) +#define SHRT_MAX 32767 + +/* Maximum value an `unsigned short' can hold. (Minimum is 0.) */ +#define USHRT_MAX 65535 + + +/* Minimum and maximum values a `signed int' can hold */ +#define INT_MIN (-INT_MAX - 1) +#define INT_MAX 2147483647 + +/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */ +#define UINT_MAX 4294967295U + + +/* Minimum and maximum values a `signed int' can hold */ +#define INT_MAX 2147483647 +#define INT_MIN (-INT_MAX - 1) + + +/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */ +#define UINT_MAX 4294967295U + + +/* Minimum and maximum values a `signed long' can hold */ +#define LONG_MAX 2147483647 +#define LONG_MIN (-LONG_MAX - 1L) + +/* Maximum value an `unsigned long' can hold. (Minimum is 0.) */ +#define ULONG_MAX 4294967295UL + +/* Minimum and maximum values a `signed long long' can hold */ +#define LLONG_MAX 9223372036854775807LL +#define LLONG_MIN (-LONG_MAX - 1LL) + + +/* Maximum value an `unsigned long long' can hold. (Minimum is 0.) */ +#define ULLONG_MAX 18446744073709551615ULL + + +#endif /* LIMITS_H */ diff --git a/src/arch/i386/include/pic8259.h b/src/arch/i386/include/pic8259.h new file mode 100644 index 00000000..694e9d13 --- /dev/null +++ b/src/arch/i386/include/pic8259.h @@ -0,0 +1,96 @@ +/* + * Basic support for controlling the 8259 Programmable Interrupt Controllers. + * + * Initially written by Michael Brown (mcb30). + */ + +#ifndef PIC8259_H +#define PIC8259_H + +/* For segoff_t */ +#include "segoff.h" + +#define IRQ_PIC_CUTOFF (8) + +/* 8259 register locations */ +#define PIC1_ICW1 (0x20) +#define PIC1_OCW2 (0x20) +#define PIC1_OCW3 (0x20) +#define PIC1_ICR (0x20) +#define PIC1_IRR (0x20) +#define PIC1_ISR (0x20) +#define PIC1_ICW2 (0x21) +#define PIC1_ICW3 (0x21) +#define PIC1_ICW4 (0x21) +#define PIC1_IMR (0x21) +#define PIC2_ICW1 (0xa0) +#define PIC2_OCW2 (0xa0) +#define PIC2_OCW3 (0xa0) +#define PIC2_ICR (0xa0) +#define PIC2_IRR (0xa0) +#define PIC2_ISR (0xa0) +#define PIC2_ICW2 (0xa1) +#define PIC2_ICW3 (0xa1) +#define PIC2_ICW4 (0xa1) +#define PIC2_IMR (0xa1) + +/* Register command values */ +#define OCW3_ID (0x08) +#define OCW3_READ_IRR (0x03) +#define OCW3_READ_ISR (0x02) +#define ICR_EOI_NON_SPECIFIC (0x20) +#define ICR_EOI_NOP (0x40) +#define ICR_EOI_SPECIFIC (0x60) +#define ICR_EOI_SET_PRIORITY (0xc0) + +/* Macros to enable/disable IRQs */ +#define IMR_REG(x) ( (x) < IRQ_PIC_CUTOFF ? PIC1_IMR : PIC2_IMR ) +#define IMR_BIT(x) ( 1 << ( (x) % IRQ_PIC_CUTOFF ) ) +#define irq_enabled(x) ( ( inb ( IMR_REG(x) ) & IMR_BIT(x) ) == 0 ) +#define enable_irq(x) outb ( inb( IMR_REG(x) ) & ~IMR_BIT(x), IMR_REG(x) ) +#define disable_irq(x) outb ( inb( IMR_REG(x) ) | IMR_BIT(x), IMR_REG(x) ) + +/* Macros for acknowledging IRQs */ +#define ICR_REG(x) ( (x) < IRQ_PIC_CUTOFF ? PIC1_ICR : PIC2_ICR ) +#define ICR_VALUE(x) ( (x) % IRQ_PIC_CUTOFF ) +#define CHAINED_IRQ 2 + +/* Utility macros to convert IRQ numbers to INT numbers and INT vectors */ +#define IRQ_INT(x) ( (x)<IRQ_PIC_CUTOFF ? (x)+0x08 : (x)-IRQ_PIC_CUTOFF+0x70 ) +#define INT_VECTOR(x) ( (segoff_t*) phys_to_virt( 4 * (x) ) ) +#define IRQ_VECTOR(x) ( INT_VECTOR ( IRQ_INT(x) ) ) + +/* Other constants */ +typedef uint8_t irq_t; +#define IRQ_MAX (15) +#define IRQ_NONE (0xff) + +/* Function prototypes + */ +int install_irq_handler ( irq_t irq, segoff_t *handler, + uint8_t *previously_enabled, + segoff_t *previous_handler ); +int remove_irq_handler ( irq_t irq, segoff_t *handler, + uint8_t *previously_enabled, + segoff_t *previous_handler ); +int install_trivial_irq_handler ( irq_t irq ); +int remove_trivial_irq_handler ( irq_t irq ); +int trivial_irq_triggered ( irq_t irq ); +int copy_trivial_irq_handler ( void *target, size_t target_size ); +void send_non_specific_eoi ( irq_t irq ); +void send_specific_eoi ( irq_t irq ); +void fake_irq ( irq_t irq ); +#ifdef DEBUG_IRQ +void dump_irq_status ( void ); +#else +#define dump_irq_status() +#endif + +/* Other code (e.g. undi.c) needs to know the size of the trivial IRQ + * handler code, so we put prototypes and the size macro here. + */ +extern void _trivial_irq_handler ( void ); +extern void _trivial_irq_handler_end ( void ); +#define TRIVIAL_IRQ_HANDLER_SIZE FRAGMENT_SIZE(_trivial_irq_handler) + +#endif /* PIC8259_H */ diff --git a/src/arch/i386/include/pxe_callbacks.h b/src/arch/i386/include/pxe_callbacks.h new file mode 100644 index 00000000..9b941931 --- /dev/null +++ b/src/arch/i386/include/pxe_callbacks.h @@ -0,0 +1,33 @@ +/* Header for pxe_callbacks.c. + */ + +#ifndef PXE_CALLBACKS_H +#define PXE_CALLBACKS_H + +#include "etherboot.h" +#include "segoff.h" +#include "pxe.h" + +typedef struct { + segoff_t orig_retaddr; + uint16_t opcode; + segoff_t segoff; +} PACKED pxe_call_params_t; + +/* + * These values are hard-coded into the PXE spec + */ +#define PXE_LOAD_SEGMENT (0x0000) +#define PXE_LOAD_OFFSET (0x7c00) +#define PXE_LOAD_ADDRESS ( ( PXE_LOAD_SEGMENT << 4 ) + PXE_LOAD_OFFSET ) + +/* Function prototypes + */ +extern pxe_stack_t * install_pxe_stack ( void *base ); +extern void use_undi_ds_for_rm_stack ( uint16_t ds ); +extern int hook_pxe_stack ( void ); +extern int unhook_pxe_stack ( void ); +extern void remove_pxe_stack ( void ); +extern int xstartpxe ( void ); + +#endif /* PXE_CALLBACKS_H */ diff --git a/src/arch/i386/include/pxe_types.h b/src/arch/i386/include/pxe_types.h new file mode 100644 index 00000000..7b093e6a --- /dev/null +++ b/src/arch/i386/include/pxe_types.h @@ -0,0 +1,35 @@ +/* + * Architecture-specific portion of pxe.h for Etherboot + * + * This file has to define the types SEGOFF16_t, SEGDESC_t and + * SEGSEL_t for use in other PXE structures. See pxe.h for details. + */ + +#ifndef PXE_TYPES_H +#define PXE_TYPES_H + +/* SEGOFF16_t defined in separate header + */ +#include "segoff.h" +typedef segoff_t I386_SEGOFF16_t; +#define SEGOFF16_t I386_SEGOFF16_t + +#define IS_NULL_SEGOFF16(x) ( ( (x).segment == 0 ) && ( (x).offset == 0 ) ) +#define SEGOFF16_TO_PTR(x) ( VIRTUAL( (x).segment, (x).offset ) ) +#define PTR_TO_SEGOFF16(ptr,segoff16) \ + (segoff16).segment = SEGMENT(ptr); \ + (segoff16).offset = OFFSET(ptr); + +typedef struct { + uint16_t Seg_Addr; + uint32_t Phy_Addr; + uint16_t Seg_Size; +} PACKED I386_SEGDESC_t; /* PACKED is required, otherwise gcc pads + * this out to 12 bytes - + * mbrown@fensystems.co.uk (mcb30) 17/5/03 */ +#define SEGDESC_t I386_SEGDESC_t + +typedef uint16_t I386_SEGSEL_t; +#define SEGSEL_t I386_SEGSEL_t + +#endif /* PXE_TYPES_H */ diff --git a/src/arch/i386/include/realmode.h b/src/arch/i386/include/realmode.h new file mode 100644 index 00000000..eca92b9b --- /dev/null +++ b/src/arch/i386/include/realmode.h @@ -0,0 +1,124 @@ +/* Real-mode interface + */ + +#ifndef ASSEMBLY + +#include "etherboot.h" +#include "segoff.h" + +typedef union { + struct { + union { + uint8_t l; + uint8_t byte; + }; + uint8_t h; + } PACKED; + uint16_t word; +} PACKED reg16_t; + +typedef union { + reg16_t w; + uint32_t dword; +} PACKED reg32_t; + +/* Macros to help with defining inline real-mode trampoline fragments. + */ +#define RM_XSTR(x) #x /* Macro hackery needed to stringify */ +#define RM_STR(x) RM_XSTR(x) +#define RM_FRAGMENT(name, asm_code_str) \ + extern void name ( void ); \ + extern void name ## _end (void); \ + __asm__( \ + ".section \".text16\"\n\t" \ + ".code16\n\t" \ + ".arch i386\n\t" \ + ".globl " #name " \n\t" \ + #name ":\n\t" \ + asm_code_str "\n\t" \ + ".globl " #name "_end\n\t" \ + #name "_end:\n\t" \ + ".code32\n\t" \ + ".previous\n\t" \ + ) + +#define FRAGMENT_SIZE(fragment) ( (size_t) ( ( (void*) fragment ## _end )\ + - ( (void*) (fragment) ) ) ) + +/* Data structures in _prot_to_real and _real_to_prot. These + * structures are accessed by assembly code as well as C code. + */ +typedef struct { + uint32_t esp; + uint16_t cs; + uint16_t ss; + uint32_t r2p_params; +} PACKED prot_to_real_params_t; + +typedef struct { + uint32_t ret_addr; + uint32_t esp; + uint32_t ebx; + uint32_t esi; + uint32_t edi; + uint32_t ebp; + uint32_t out_stack; + uint32_t out_stack_len; +} PACKED real_to_prot_params_t; + +/* Function prototypes: realmode.c + */ +#define real_call( fragment, in_stack, out_stack ) \ + _real_call ( fragment, FRAGMENT_SIZE(fragment), \ + (void*)(in_stack), \ + ( (in_stack) == NULL ? 0 : sizeof(*(in_stack)) ), \ + (void*)(out_stack), \ + ( (out_stack) == NULL ? 0 : sizeof(*(out_stack)) ) ) +extern uint16_t _real_call ( void *fragment, int fragment_len, + void *in_stack, int in_stack_len, + void *out_stack, int out_stack_len ); +/* Function prototypes: realmode_asm.S + */ +extern void rm_callback_interface; +extern uint16_t rm_callback_interface_size; +extern uint32_t rm_etherboot_location; +extern void _rm_in_call ( void ); +extern void _rm_in_call_far ( void ); + +extern void _prot_to_real_prefix ( void ); +extern void _prot_to_real_prefix_end ( void ); +extern uint16_t prot_to_real_prefix_size; + +extern void _real_to_prot_suffix ( void ); +extern void _real_to_prot_suffix_end ( void ); +extern uint16_t real_to_prot_suffix_size; + +/* PXE assembler bits */ +extern void pxe_callback_interface; +extern uint16_t pxe_callback_interface_size; +extern void _pxe_in_call_far ( void ); +extern void _pxenv_in_call_far ( void ); +extern void _pxe_intercept_int1a ( void ); +extern segoff_t _pxe_intercepted_int1a; +extern segoff_t _pxe_pxenv_location; + +/* Global variables + */ +extern uint32_t real_mode_stack; +extern size_t real_mode_stack_size; +extern int lock_real_mode_stack; + + +/* Function prototypes from basemem.c + */ +#ifdef LINUXBIOS +/* A silly hard code that let's the code compile and work. + * When this becomes a problem feel free to implement + * something better. + */ +static inline void allot_real_mode_stack(void) { real_mode_stack = 0x7c00; } +#else +void allot_real_mode_stack(void); +#endif + +#endif /* ASSEMBLY */ diff --git a/src/arch/i386/include/segoff.h b/src/arch/i386/include/segoff.h new file mode 100644 index 00000000..822ddd33 --- /dev/null +++ b/src/arch/i386/include/segoff.h @@ -0,0 +1,41 @@ +/* + * Segment:offset types and macros + * + * Initially written by Michael Brown (mcb30). + */ + +#ifndef SEGOFF_H +#define SEGOFF_H + +#include <stdint.h> +#include <osdep.h> +#include <io.h> + +/* Segment:offset structure. Note that the order within the structure + * is offset:segment. + */ +typedef struct { + uint16_t offset; + uint16_t segment; +} segoff_t; + +/* Macros for converting from virtual to segment:offset addresses, + * when we don't actually care which of the many isomorphic results we + * get. + */ +#ifdef DEBUG_SEGMENT +uint16_t SEGMENT ( const void * const ptr ) { + uint32_t phys = virt_to_phys ( ptr ); + if ( phys > 0xfffff ) { + printf ( "FATAL ERROR: segment address out of range\n" ); + } + return phys >> 4; +} +#else +#define SEGMENT(x) ( virt_to_phys ( x ) >> 4 ) +#endif +#define OFFSET(x) ( virt_to_phys ( x ) & 0xf ) +#define SEGOFF(x) { OFFSET(x), SEGMENT(x) } +#define VIRTUAL(x,y) ( phys_to_virt ( ( ( x ) << 4 ) + ( y ) ) ) + +#endif /* SEGOFF_H */ diff --git a/src/arch/i386/include/setjmp.h b/src/arch/i386/include/setjmp.h new file mode 100644 index 00000000..ed2be270 --- /dev/null +++ b/src/arch/i386/include/setjmp.h @@ -0,0 +1,12 @@ +#ifndef ETHERBOOT_SETJMP_H +#define ETHERBOOT_SETJMP_H + + +/* Define a type for use by setjmp and longjmp */ +#define JBLEN 6 +typedef unsigned long jmp_buf[JBLEN]; + +extern int setjmp (jmp_buf env); +extern void longjmp (jmp_buf env, int val); + +#endif /* ETHERBOOT_SETJMP_H */ diff --git a/src/arch/i386/include/stdint.h b/src/arch/i386/include/stdint.h new file mode 100644 index 00000000..42b04830 --- /dev/null +++ b/src/arch/i386/include/stdint.h @@ -0,0 +1,16 @@ +#ifndef STDINT_H +#define STDINT_H + +typedef unsigned size_t; + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned long uint32_t; +typedef unsigned long long uint64_t; + +typedef signed char int8_t; +typedef signed short int16_t; +typedef signed long int32_t; +typedef signed long long int64_t; + +#endif /* STDINT_H */ diff --git a/src/arch/i386/include/vga.h b/src/arch/i386/include/vga.h new file mode 100644 index 00000000..01fc39d8 --- /dev/null +++ b/src/arch/i386/include/vga.h @@ -0,0 +1,228 @@ +/* + * + * modified + * by Steve M. Gehlbach <steve@kesa.com> + * + * Originally from linux/drivers/video/vga16.c by + * Ben Pfaff <pfaffben@debian.org> and Petr Vandrovec <VANDROVE@vc.cvut.cz> + * Copyright 1999 Ben Pfaff <pfaffben@debian.org> and Petr Vandrovec <VANDROVE@vc.cvut.cz> + * Based on VGA info at http://www.goodnet.com/~tinara/FreeVGA/home.htm + * Based on VESA framebuffer (c) 1998 Gerd Knorr <kraxel@goldbach.in-berlin.de> + * + */ + +#ifndef VGA_H_INCL +#define VGA_H_INCL 1 + +//#include <cpu/p5/io.h> + +#define u8 unsigned char +#define u16 unsigned short +#define u32 unsigned int +#define __u32 u32 + +#define VERROR -1 +#define CHAR_HEIGHT 16 +#define LINES 25 +#define COLS 80 + +// macros for writing to vga regs +#define write_crtc(data,addr) outb(addr,CRT_IC); outb(data,CRT_DC) +#define write_att(data,addr) inb(IS1_RC); inb(0x80); outb(addr,ATT_IW); inb(0x80); outb(data,ATT_IW); inb(0x80) +#define write_seq(data,addr) outb(addr,SEQ_I); outb(data,SEQ_D) +#define write_gra(data,addr) outb(addr,GRA_I); outb(data,GRA_D) +u8 read_seq_b(u16 addr); +u8 read_gra_b(u16 addr); +u8 read_crtc_b(u16 addr); +u8 read_att_b(u16 addr); + + +#ifdef VGA_HARDWARE_FIXUP +void vga_hardware_fixup(void); +#else +#define vga_hardware_fixup() do{} while(0) +#endif + +#define SYNC_HOR_HIGH_ACT 1 /* horizontal sync high active */ +#define SYNC_VERT_HIGH_ACT 2 /* vertical sync high active */ +#define SYNC_EXT 4 /* external sync */ +#define SYNC_COMP_HIGH_ACT 8 /* composite sync high active */ +#define SYNC_BROADCAST 16 /* broadcast video timings */ + /* vtotal = 144d/288n/576i => PAL */ + /* vtotal = 121d/242n/484i => NTSC */ + +#define SYNC_ON_GREEN 32 /* sync on green */ + +#define VMODE_NONINTERLACED 0 /* non interlaced */ +#define VMODE_INTERLACED 1 /* interlaced */ +#define VMODE_DOUBLE 2 /* double scan */ +#define VMODE_MASK 255 + +#define VMODE_YWRAP 256 /* ywrap instead of panning */ +#define VMODE_SMOOTH_XPAN 512 /* smooth xpan possible (internally used) */ +#define VMODE_CONUPDATE 512 /* don't update x/yoffset */ + +/* VGA data register ports */ +#define CRT_DC 0x3D5 /* CRT Controller Data Register - color emulation */ +#define CRT_DM 0x3B5 /* CRT Controller Data Register - mono emulation */ +#define ATT_R 0x3C1 /* Attribute Controller Data Read Register */ +#define GRA_D 0x3CF /* Graphics Controller Data Register */ +#define SEQ_D 0x3C5 /* Sequencer Data Register */ + +#define MIS_R 0x3CC // Misc Output Read Register +#define MIS_W 0x3C2 // Misc Output Write Register + +#define IS1_RC 0x3DA /* Input Status Register 1 - color emulation */ +#define IS1_RM 0x3BA /* Input Status Register 1 - mono emulation */ +#define PEL_D 0x3C9 /* PEL Data Register */ +#define PEL_MSK 0x3C6 /* PEL mask register */ + +/* EGA-specific registers */ +#define GRA_E0 0x3CC /* Graphics enable processor 0 */ +#define GRA_E1 0x3CA /* Graphics enable processor 1 */ + + +/* VGA index register ports */ +#define CRT_IC 0x3D4 /* CRT Controller Index - color emulation */ +#define CRT_IM 0x3B4 /* CRT Controller Index - mono emulation */ +#define ATT_IW 0x3C0 /* Attribute Controller Index & Data Write Register */ +#define GRA_I 0x3CE /* Graphics Controller Index */ +#define SEQ_I 0x3C4 /* Sequencer Index */ +#define PEL_IW 0x3C8 /* PEL Write Index */ +#define PEL_IR 0x3C7 /* PEL Read Index */ + +/* standard VGA indexes max counts */ +#define CRTC_C 25 /* 25 CRT Controller Registers sequentially set*/ + // the remainder are not in the par array +#define ATT_C 21 /* 21 Attribute Controller Registers */ +#define GRA_C 9 /* 9 Graphics Controller Registers */ +#define SEQ_C 5 /* 5 Sequencer Registers */ +#define MIS_C 1 /* 1 Misc Output Register */ + +#define CRTC_H_TOTAL 0 +#define CRTC_H_DISP 1 +#define CRTC_H_BLANK_START 2 +#define CRTC_H_BLANK_END 3 +#define CRTC_H_SYNC_START 4 +#define CRTC_H_SYNC_END 5 +#define CRTC_V_TOTAL 6 +#define CRTC_OVERFLOW 7 +#define CRTC_PRESET_ROW 8 +#define CRTC_MAX_SCAN 9 +#define CRTC_CURSOR_START 0x0A +#define CRTC_CURSOR_END 0x0B +#define CRTC_START_HI 0x0C +#define CRTC_START_LO 0x0D +#define CRTC_CURSOR_HI 0x0E +#define CRTC_CURSOR_LO 0x0F +#define CRTC_V_SYNC_START 0x10 +#define CRTC_V_SYNC_END 0x11 +#define CRTC_V_DISP_END 0x12 +#define CRTC_OFFSET 0x13 +#define CRTC_UNDERLINE 0x14 +#define CRTC_V_BLANK_START 0x15 +#define CRTC_V_BLANK_END 0x16 +#define CRTC_MODE 0x17 +#define CRTC_LINE_COMPARE 0x18 + +#define ATC_MODE 0x10 +#define ATC_OVERSCAN 0x11 +#define ATC_PLANE_ENABLE 0x12 +#define ATC_PEL 0x13 +#define ATC_COLOR_PAGE 0x14 + +#define SEQ_CLOCK_MODE 0x01 +#define SEQ_PLANE_WRITE 0x02 +#define SEQ_CHARACTER_MAP 0x03 +#define SEQ_MEMORY_MODE 0x04 + +#define GDC_SR_VALUE 0x00 +#define GDC_SR_ENABLE 0x01 +#define GDC_COMPARE_VALUE 0x02 +#define GDC_DATA_ROTATE 0x03 +#define GDC_PLANE_READ 0x04 +#define GDC_MODE 0x05 +#define GDC_MISC 0x06 +#define GDC_COMPARE_MASK 0x07 +#define GDC_BIT_MASK 0x08 + +// text attributes +#define VGA_ATTR_CLR_RED 0x4 +#define VGA_ATTR_CLR_GRN 0x2 +#define VGA_ATTR_CLR_BLU 0x1 +#define VGA_ATTR_CLR_YEL (VGA_ATTR_CLR_RED | VGA_ATTR_CLR_GRN) +#define VGA_ATTR_CLR_CYN (VGA_ATTR_CLR_GRN | VGA_ATTR_CLR_BLU) +#define VGA_ATTR_CLR_MAG (VGA_ATTR_CLR_BLU | VGA_ATTR_CLR_RED) +#define VGA_ATTR_CLR_BLK 0 +#define VGA_ATTR_CLR_WHT (VGA_ATTR_CLR_RED | VGA_ATTR_CLR_GRN | VGA_ATTR_CLR_BLU) +#define VGA_ATTR_BNK 0x80 +#define VGA_ATTR_ITN 0x08 + +/* + * vga register parameters + * these are copied to the + * registers. + * + */ +struct vga_par { + u8 crtc[CRTC_C]; + u8 atc[ATT_C]; + u8 gdc[GRA_C]; + u8 seq[SEQ_C]; + u8 misc; // the misc register, MIS_W + u8 vss; +}; + + +/* Interpretation of offset for color fields: All offsets are from the right, + * inside a "pixel" value, which is exactly 'bits_per_pixel' wide (means: you + * can use the offset as right argument to <<). A pixel afterwards is a bit + * stream and is written to video memory as that unmodified. This implies + * big-endian byte order if bits_per_pixel is greater than 8. + */ +struct fb_bitfield { + __u32 offset; /* beginning of bitfield */ + __u32 length; /* length of bitfield */ + __u32 msb_right; /* != 0 : Most significant bit is */ + /* right */ +}; + +struct screeninfo { + __u32 xres; /* visible resolution */ + __u32 yres; + __u32 xres_virtual; /* virtual resolution */ + __u32 yres_virtual; + __u32 xoffset; /* offset from virtual to visible */ + __u32 yoffset; /* resolution */ + + __u32 bits_per_pixel; /* guess what */ + __u32 grayscale; /* != 0 Graylevels instead of colors */ + + struct fb_bitfield red; /* bitfield in fb mem if true color, */ + struct fb_bitfield green; /* else only length is significant */ + struct fb_bitfield blue; + struct fb_bitfield transp; /* transparency */ + + __u32 nonstd; /* != 0 Non standard pixel format */ + + __u32 activate; /* see FB_ACTIVATE_* */ + + __u32 height; /* height of picture in mm */ + __u32 width; /* width of picture in mm */ + + __u32 accel_flags; /* acceleration flags (hints) */ + + /* Timing: All values in pixclocks, except pixclock (of course) */ + __u32 pixclock; /* pixel clock in ps (pico seconds) */ + __u32 left_margin; /* time from sync to picture */ + __u32 right_margin; /* time from picture to sync */ + __u32 upper_margin; /* time from sync to picture */ + __u32 lower_margin; + __u32 hsync_len; /* length of horizontal sync */ + __u32 vsync_len; /* length of vertical sync */ + __u32 sync; /* sync polarity */ + __u32 vmode; /* interlaced etc */ + __u32 reserved[6]; /* Reserved for future compatibility */ +}; + +#endif diff --git a/src/arch/i386/prefix/bImageprefix.S b/src/arch/i386/prefix/bImageprefix.S new file mode 100644 index 00000000..a478713d --- /dev/null +++ b/src/arch/i386/prefix/bImageprefix.S @@ -0,0 +1,614 @@ +/* + Copyright (C) 2000, Entity Cyber, Inc. + + Authors: Gary Byers (gb@thinguin.org) + Marty Connor (mdc@thinguin.org) + Eric Biederman (ebiederman@lnxi.com) + + This code also derives a lot from arch/i386/boot/setup.S in + the linux kernel. + + This software may be used and distributed according to the terms + of the GNU Public License (GPL), incorporated herein by reference. + + Description: + + This is just a little bit of code and data that can get prepended + to an Etherboot ROM image in order to allow LILO to load the + result as if it were a Linux kernel image. + + A real Linux kernel image consists of a one-sector boot loader + (to load the image from a floppy disk), followed a few sectors + of setup code, followed by the kernel code itself. There's + a table in the first sector (starting at offset 497) that indicates + how many sectors of setup code follow the first sector and which + contains some other parameters that aren't interesting in this + case. + + When LILO loads the sectors that comprise a kernel image, it doesn't + execute the code in the first sector (since that code would try to + load the image from a floppy disk.) The code in the first sector + below doesn't expect to get executed (and prints an error message + if it ever -is- executed.) LILO's only interested in knowing the + number of setup sectors advertised in the table (at offset 497 in + the first sector.) + + Etherboot doesn't require much in the way of setup code. + Historically, the Linux kernel required at least 4 sectors of + setup code. Current versions of LILO look at the byte at + offset 497 in the first sector to indicate how many sectors + of setup code are contained in the image. + + The setup code that is present here does a lot of things + exactly the way the linux kernel does them instead of in + ways more typical of etherboot. Generally this is so + the code can be strongly compatible with the linux kernel. + In addition the general etherboot technique of enabling the a20 + after we switch into protected mode does not work if etherboot + is being loaded at 1MB. +*/ + + .equ CR0_PE,1 + +#ifdef GAS291 +#define DATA32 data32; +#define ADDR32 addr32; +#define LJMPI(x) ljmp x +#else +#define DATA32 data32 +#define ADDR32 addr32 +/* newer GAS295 require #define LJMPI(x) ljmp *x */ +#define LJMPI(x) ljmp x +#endif + +/* Simple and small GDT entries for booting only */ +#define GDT_ENTRY_BOOT_CS 2 +#define GDT_ENTRY_BOOT_DS (GDT_ENTRY_BOOT_CS + 1) +#define __BOOT_CS (GDT_ENTRY_BOOT_CS * 8) +#define __BOOT_DS (GDT_ENTRY_BOOT_DS * 8) + + +#define SETUPSECS 4 /* Minimal nr of setup-sectors */ +#define PREFIXSIZE ((SETUPSECS+1)*512) +#define PREFIXPGH (PREFIXSIZE / 16 ) +#define BOOTSEG 0x07C0 /* original address of boot-sector */ +#define INITSEG 0x9000 /* we move boot here - out of the way */ +#define SETUPSEG 0x9020 /* setup starts here */ +#define SYSSEG 0x1000 /* system loaded at 0x10000 (65536). */ + +#define DELTA_INITSEG (SETUPSEG - INITSEG) /* 0x0020 */ + +/* Signature words to ensure LILO loaded us right */ +#define SIG1 0xAA55 +#define SIG2 0x5A5A + + .text + .code16 + .arch i386 + .org 0 + .section ".prefix", "ax", @progbits + .globl _prefix +_prefix: + +/* + This is a minimal boot sector. If anyone tries to execute it (e.g., if + a .lilo file is dd'ed to a floppy), print an error message. +*/ + +bootsector: + jmp $BOOTSEG, $go - _prefix /* reload cs:ip to match relocation addr */ +go: + movw $0x2000, %di /* 0x2000 is arbitrary value >= length + of bootsect + room for stack */ + + movw $BOOTSEG, %ax + movw %ax,%ds + movw %ax,%es + + cli + movw %ax, %ss /* put stack at BOOTSEG:0x2000. */ + movw %di,%sp + sti + + movw $why_end-why, %cx + movw $why - _prefix, %si + + movw $0x0007, %bx /* page 0, attribute 7 (normal) */ + movb $0x0e, %ah /* write char, tty mode */ +prloop: + lodsb + int $0x10 + loop prloop +freeze: jmp freeze + +why: .ascii "This image cannot be loaded from a floppy disk.\r\n" +why_end: + + + .org 497 +setup_sects: + .byte SETUPSECS +root_flags: + .word 0 +syssize: + .word _verbatim_size_pgh - PREFIXPGH +swap_dev: + .word 0 +ram_size: + .word 0 +vid_mode: + .word 0 +root_dev: + .word 0 +boot_flag: + .word 0xAA55 + +/* + We're now at the beginning of the second sector of the image - + where the setup code goes. + + We don't need to do too much setup for Etherboot. + + This code gets loaded at SETUPSEG:0. It wants to start + executing the Etherboot image that's loaded at SYSSEG:0 and + whose entry point is SYSSEG:0. +*/ +setup_code: + jmp trampoline +# This is the setup header, and it must start at %cs:2 (old 0x9020:2) + + .ascii "HdrS" # header signature + .word 0x0203 # header version number (>= 0x0105) + # or else old loadlin-1.5 will fail) +realmode_swtch: .word 0, 0 # default_switch, SETUPSEG +start_sys_seg: .word SYSSEG # low load segment (obsolete) + .word kernel_version - setup_code + # pointing to kernel version string + # above section of header is compatible + # with loadlin-1.5 (header v1.5). Don't + # change it. + +type_of_loader: .byte 0 # = 0, old one (LILO, Loadlin, + # Bootlin, SYSLX, bootsect...) + # See Documentation/i386/boot.txt for + # assigned ids + +# flags, unused bits must be zero (RFU) bit within loadflags +loadflags: +LOADED_HIGH = 1 # If set, the kernel is loaded high +CAN_USE_HEAP = 0x80 # If set, the loader also has set + # heap_end_ptr to tell how much + # space behind setup.S can be used for + # heap purposes. + # Only the loader knows what is free + .byte LOADED_HIGH + +setup_move_size: .word 0x8000 # size to move, when setup is not + # loaded at 0x90000. We will move setup + # to 0x90000 then just before jumping + # into the kernel. However, only the + # loader knows how much data behind + # us also needs to be loaded. + +code32_start: # here loaders can put a different + # start address for 32-bit code. + .long 0x100000 # 0x100000 = default for big kernel + +ramdisk_image: .long 0 # address of loaded ramdisk image + # Here the loader puts the 32-bit + # address where it loaded the image. + # This only will be read by the kernel. + +ramdisk_size: .long 0 # its size in bytes + +bootsect_kludge: + .long 0 # obsolete + +heap_end_ptr: .word 0 # (Header version 0x0201 or later) + # space from here (exclusive) down to + # end of setup code can be used by setup + # for local heap purposes. + +pad1: .word 0 +cmd_line_ptr: .long 0 # (Header version 0x0202 or later) + # If nonzero, a 32-bit pointer + # to the kernel command line. + # The command line should be + # located between the start of + # setup and the end of low + # memory (0xa0000), or it may + # get overwritten before it + # gets read. If this field is + # used, there is no longer + # anything magical about the + # 0x90000 segment; the setup + # can be located anywhere in + # low memory 0x10000 or higher. + +ramdisk_max: .long 0 # (Header version 0x0203 or later) + # The highest safe address for + # the contents of an initrd + +trampoline: call start_of_setup +trampoline_end: + .space 1024 +# End of setup header ##################################################### + +start_of_setup: +# Set %ds = %cs, we know that SETUPSEG = %cs at this point + movw %cs, %ax # aka SETUPSEG + movw %ax, %ds +# Check signature at end of setup + cmpw $SIG1, (setup_sig1 - setup_code) + jne bad_sig + + cmpw $SIG2, (setup_sig2 - setup_code) + jne bad_sig + + jmp good_sig1 + +# Routine to print asciiz string at ds:si +prtstr: + lodsb + andb %al, %al + jz fin + + call prtchr + jmp prtstr + +fin: ret + +# Part of above routine, this one just prints ascii al +prtchr: pushw %ax + pushw %cx + movw $7,%bx + movw $0x01, %cx + movb $0x0e, %ah + int $0x10 + popw %cx + popw %ax + ret + +no_sig_mess: .string "No setup signature found ..." + +good_sig1: + jmp good_sig + +# We now have to find the rest of the setup code/data +bad_sig: + movw %cs, %ax # SETUPSEG + subw $DELTA_INITSEG, %ax # INITSEG + movw %ax, %ds + xorb %bh, %bh + movb (497), %bl # get setup sect from bootsect + subw $4, %bx # LILO loads 4 sectors of setup + shlw $8, %bx # convert to words (1sect=2^8 words) + movw %bx, %cx + shrw $3, %bx # convert to segment + addw $SYSSEG, %bx + movw %bx, %cs:(start_sys_seg - setup_code) +# Move rest of setup code/data to here + movw $2048, %di # four sectors loaded by LILO + subw %si, %si + pushw %cs + popw %es + movw $SYSSEG, %ax + movw %ax, %ds + rep + movsw + movw %cs, %ax # aka SETUPSEG + movw %ax, %ds + cmpw $SIG1, (setup_sig1 - setup_code) + jne no_sig + + cmpw $SIG2, (setup_sig2 - setup_code) + jne no_sig + + jmp good_sig + +no_sig: + lea (no_sig_mess - setup_code), %si + call prtstr + +no_sig_loop: + hlt + jmp no_sig_loop + +good_sig: + cmpw $0, %cs:(realmode_swtch - setup_code) + jz rmodeswtch_normal + + lcall *%cs:(realmode_swtch - setup_code) + jmp rmodeswtch_end + +rmodeswtch_normal: + pushw %cs + call default_switch + +rmodeswtch_end: +# we get the code32 start address and modify the below 'jmpi' +# (loader may have changed it) + movl %cs:(code32_start - setup_code), %eax + movl %eax, %cs:(code32 - setup_code) + +# then we load the segment descriptors + movw %cs, %ax # aka SETUPSEG + movw %ax, %ds + +# +# Enable A20. This is at the very best an annoying procedure. +# A20 code ported from SYSLINUX 1.52-1.63 by H. Peter Anvin. +# + +A20_TEST_LOOPS = 32 # Iterations per wait +A20_ENABLE_LOOPS = 255 # Total loops to try + +a20_try_loop: + + # First, see if we are on a system with no A20 gate. +a20_none: + call a20_test + jnz a20_done + + # Next, try the BIOS (INT 0x15, AX=0x2401) +a20_bios: + movw $0x2401, %ax + pushfl # Be paranoid about flags + int $0x15 + popfl + + call a20_test + jnz a20_done + + # Try enabling A20 through the keyboard controller +a20_kbc: + call empty_8042 + + call a20_test # Just in case the BIOS worked + jnz a20_done # but had a delayed reaction. + + movb $0xD1, %al # command write + outb %al, $0x64 + call empty_8042 + + movb $0xDF, %al # A20 on + outb %al, $0x60 + call empty_8042 + + # Wait until a20 really *is* enabled; it can take a fair amount of + # time on certain systems; Toshiba Tecras are known to have this + # problem. +a20_kbc_wait: + xorw %cx, %cx +a20_kbc_wait_loop: + call a20_test + jnz a20_done + loop a20_kbc_wait_loop + + # Final attempt: use "configuration port A" +a20_fast: + inb $0x92, %al # Configuration Port A + orb $0x02, %al # "fast A20" version + andb $0xFE, %al # don't accidentally reset + outb %al, $0x92 + + # Wait for configuration port A to take effect +a20_fast_wait: + xorw %cx, %cx +a20_fast_wait_loop: + call a20_test + jnz a20_done + loop a20_fast_wait_loop + + # A20 is still not responding. Try frobbing it again. + # + decb (a20_tries - setup_code) + jnz a20_try_loop + + movw $(a20_err_msg - setup_code), %si + call prtstr + +a20_die: + hlt + jmp a20_die + +a20_tries: + .byte A20_ENABLE_LOOPS + +a20_err_msg: + .ascii "linux: fatal error: A20 gate not responding!" + .byte 13, 10, 0 + + # If we get here, all is good +a20_done: + # Leave the idt alone + + # set up gdt + xorl %eax, %eax # Compute gdt_base + movw %ds, %ax # (Convert %ds:gdt to a linear ptr) + shll $4, %eax + addl $(bImage_gdt - setup_code), %eax + movl %eax, (bImage_gdt_48+2 - setup_code) + DATA32 lgdt %ds:(bImage_gdt_48 - setup_code) # load gdt with whatever is + # appropriate + + # Switch to protected mode + movl %cr0, %eax + orb $CR0_PE, %al + movl %eax, %cr0 + + DATA32 ljmp %ds:(code32 - setup_code) +code32: + .long 0x100000 + .word __BOOT_CS, 0 + +# Here's a bunch of information about your current kernel.. +kernel_version: .ascii "Etherboot " + .ascii VERSION + .byte 0 + +# This is the default real mode switch routine. +# to be called just before protected mode transition +default_switch: + cli # no interrupts allowed ! + movb $0x80, %al # disable NMI for bootup + # sequence + outb %al, $0x70 + lret + +# This routine tests whether or not A20 is enabled. If so, it +# exits with zf = 0. +# +# The memory address used, 0x200, is the int $0x80 vector, which +# should be safe. + +A20_TEST_ADDR = 4*0x80 + +a20_test: + pushw %cx + pushw %ax + xorw %cx, %cx + movw %cx, %fs # Low memory + decw %cx + movw %cx, %gs # High memory area + movw $A20_TEST_LOOPS, %cx + movw %fs:(A20_TEST_ADDR), %ax + pushw %ax +a20_test_wait: + incw %ax + movw %ax, %fs:(A20_TEST_ADDR) + call delay # Serialize and make delay constant + cmpw %gs:(A20_TEST_ADDR+0x10), %ax + loope a20_test_wait + + popw %fs:(A20_TEST_ADDR) + popw %ax + popw %cx + ret + + +# This routine checks that the keyboard command queue is empty +# (after emptying the output buffers) +# +# Some machines have delusions that the keyboard buffer is always full +# with no keyboard attached... +# +# If there is no keyboard controller, we will usually get 0xff +# to all the reads. With each IO taking a microsecond and +# a timeout of 100,000 iterations, this can take about half a +# second ("delay" == outb to port 0x80). That should be ok, +# and should also be plenty of time for a real keyboard controller +# to empty. +# + +empty_8042: + pushl %ecx + movl $100000, %ecx + +empty_8042_loop: + decl %ecx + jz empty_8042_end_loop + + call delay + + inb $0x64, %al # 8042 status port + testb $1, %al # output buffer? + jz no_output + + call delay + inb $0x60, %al # read it + jmp empty_8042_loop + +no_output: + testb $2, %al # is input buffer full? + jnz empty_8042_loop # yes - loop +empty_8042_end_loop: + popl %ecx + + +# Delay is needed after doing I/O +delay: + outb %al,$0x80 + ret + +# Descriptor tables +# +# NOTE: The intel manual says gdt should be sixteen bytes aligned for +# efficiency reasons. However, there are machines which are known not +# to boot with misaligned GDTs, so alter this at your peril! If you alter +# GDT_ENTRY_BOOT_CS (in asm/segment.h) remember to leave at least two +# empty GDT entries (one for NULL and one reserved). +# +# NOTE: On some CPUs, the GDT must be 8 byte aligned. This is +# true for the Voyager Quad CPU card which will not boot without +# This directive. 16 byte aligment is recommended by intel. +# + .balign 16 +bImage_gdt: + .fill GDT_ENTRY_BOOT_CS,8,0 + + .word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb) + .word 0 # base address = 0 + .word 0x9A00 # code read/exec + .word 0x00CF # granularity = 4096, 386 + # (+5th nibble of limit) + + .word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb) + .word 0 # base address = 0 + .word 0x9200 # data read/write + .word 0x00CF # granularity = 4096, 386 + # (+5th nibble of limit) +bImage_gdt_end: + .balign 4 + + .word 0 # alignment byte +bImage_idt_48: + .word 0 # idt limit = 0 + .long 0 # idt base = 0L + + .word 0 # alignment byte +bImage_gdt_48: + .word bImage_gdt_end - bImage_gdt - 1 # gdt limit + .long bImage_gdt_48 - setup_code # gdt base (filled in later) + + .section ".text16", "ax", @progbits + .globl prefix_exit +prefix_exit: + int $0x19 /* should try to boot machine */ + .globl prefix_exit_end +prefix_exit_end: + .previous + + + .org (PREFIXSIZE - 4) +# Setup signature -- must be last +setup_sig1: .word SIG1 +setup_sig2: .word SIG2 + /* Etherboot expects to be contiguous in memory once loaded. + * The linux bImage protocol does not do this, but since we + * don't need any information that's left in the prefix, it + * doesn't matter: we just have to ensure that we make it to _start + * + * protected_start will live at 0x100000 and it will be the + * the first code called as we enter protected mode. + */ + .code32 +protected_start: + /* Load segment registers */ + movw $__BOOT_DS, %ax + movw %ax, %ss + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + + /* Use the internal etherboot stack */ + movl $(_prefix_stack_end - protected_start + 0x100000), %esp + + pushl $0 /* No parameters to preserve for exit path */ + pushl $0 /* Use prefix exit path mechanism */ + + jmp _start +/* + That's about it. +*/ diff --git a/src/arch/i386/prefix/boot1a.s b/src/arch/i386/prefix/boot1a.s new file mode 100644 index 00000000..557462f1 --- /dev/null +++ b/src/arch/i386/prefix/boot1a.s @@ -0,0 +1,410 @@ +# This code is no longer used in Etherboot. It is not maintained and +# may not work. + + +# +# Copyright (c) 1998 Robert Nordier +# All rights reserved. +# Very small bootrom changes by Luigi Rizzo +# <comment author="Luigi Rizzo"> +# I recently had the problem of downloading the etherboot code +# from a hard disk partition instead of a floppy, and noticed that +# floppyload.S does not do the job. With a bit of hacking to +# the FreeBSD's boot1.s code, I managed to obtain a boot sector +# which works both for floppies and hard disks -- basically you +# do something like +# +# cat boot1a bin32/<yourcard>.lzrom > /dev/ad0s4 +# +# (or whatever is the HD partition you are using, I am using slice +# 4 on FreeBSD) and you are up and running. +# Then with "fdisk" you have to mark your partition as having type "1" +# (which is listed as DOS-- but basically it must be something matching +# the variable PRT_BSD in the assembly source below). +# </comment> +# +# Redistribution and use in source and binary forms are freely +# permitted provided that the above copyright notice and this +# paragraph and the following disclaimer are duplicated in all +# such forms. +# +# This software is provided "AS IS" and without any express or +# implied warranties, including, without limitation, the implied +# warranties of merchantability and fitness for a particular +# purpose. +# +# Makefile: +#boot1a: boot1a.out +# objcopy -S -O binary boot1a.out boot1a +# +#boot1a.out: boot1a.o +# ld -nostdlib -static -N -e start -Ttext 0x7c00 -o boot1a.out boot1a.o +# +#boot1a.o: boot1a.s +# as --defsym FLAGS=0x80 boot1a.s -o boot1a.o +# +# + +# $FreeBSD: src/sys/boot/i386/boot2/boot1.s,v 1.10.2.2 2000/07/07 21:12:32 jhb Exp $ + +# Memory Locations + .set MEM_REL,0x700 # Relocation address + .set MEM_ARG,0x900 # Arguments + .set MEM_ORG,0x7c00 # Origin + .set MEM_BUF,0x8c00 # Load area + .set MEM_BTX,0x9000 # BTX start + .set MEM_JMP,0x9010 # BTX entry point + .set MEM_USR,0xa000 # Client start + .set BDA_BOOT,0x472 # Boot howto flag + +# Partition Constants + .set PRT_OFF,0x1be # Partition offset + .set PRT_NUM,0x4 # Partitions + .set PRT_BSD,0x1 # Partition type + +# Flag Bits + .set FL_PACKET,0x80 # Packet mode + +# Misc. Constants + .set SIZ_PAG,0x1000 # Page size + .set SIZ_SEC,0x200 # Sector size + + .globl start + .globl xread + .code16 + +start: jmp main # Start recognizably + + .org 0x4,0x90 +# +# Trampoline used by boot2 to call read to read data from the disk via +# the BIOS. Call with: +# +# %cx:%ax - long - LBA to read in +# %es:(%bx) - caddr_t - buffer to read data into +# %dl - byte - drive to read from +# %dh - byte - num sectors to read +# + +xread: push %ss # Address + pop %ds # data +# +# Setup an EDD disk packet and pass it to read +# +xread.1: # Starting + pushl $0x0 # absolute + push %cx # block + push %ax # number + push %es # Address of + push %bx # transfer buffer + xor %ax,%ax # Number of + movb %dh,%al # blocks to + push %ax # transfer + push $0x10 # Size of packet + mov %sp,%bp # Packet pointer + callw read # Read from disk + lea 0x10(%bp),%sp # Clear stack + lret # To far caller +# +# Load the rest of boot2 and BTX up, copy the parts to the right locations, +# and start it all up. +# + +# +# Setup the segment registers to flat addressing (segment 0) and setup the +# stack to end just below the start of our code. +# +main: cld # String ops inc + xor %cx,%cx # Zero + mov %cx,%es # Address + mov %cx,%ds # data + mov %cx,%ss # Set up + mov $start,%sp # stack +# +# Relocate ourself to MEM_REL. Since %cx == 0, the inc %ch sets +# %cx == 0x100. +# + mov %sp,%si # Source + mov $MEM_REL,%di # Destination + incb %ch # Word count + rep # Copy + movsw # code +# +# If we are on a hard drive, then load the MBR and look for the first +# FreeBSD slice. We use the fake partition entry below that points to +# the MBR when we call nread. The first pass looks for the first active +# FreeBSD slice. The second pass looks for the first non-active FreeBSD +# slice if the first one fails. +# + mov $part4,%si # Partition + cmpb $0x80,%dl # Hard drive? + jb main.4 # No + movb $0x1,%dh # Block count + callw nread # Read MBR + mov $0x1,%cx # Two passes +main.1: mov $MEM_BUF+PRT_OFF,%si # Partition table + movb $0x1,%dh # Partition +main.2: cmpb $PRT_BSD,0x4(%si) # Our partition type? + jne main.3 # No + jcxz main.5 # If second pass + testb $0x80,(%si) # Active? + jnz main.5 # Yes +main.3: add $0x10,%si # Next entry + incb %dh # Partition + cmpb $0x1+PRT_NUM,%dh # In table? + jb main.2 # Yes + dec %cx # Do two + jcxz main.1 # passes +# +# If we get here, we didn't find any FreeBSD slices at all, so print an +# error message and die. +# +booterror: mov $msg_part,%si # Message + jmp error # Error +# +# Floppies use partition 0 of drive 0. +# +main.4: xor %dx,%dx # Partition:drive +# +# Ok, we have a slice and drive in %dx now, so use that to locate and load +# boot2. %si references the start of the slice we are looking for, so go +# ahead and load up the first 16 sectors (boot1 + boot2) from that. When +# we read it in, we conveniently use 0x8c00 as our transfer buffer. Thus, +# boot1 ends up at 0x8c00, and boot2 starts at 0x8c00 + 0x200 = 0x8e00. +# The first part of boot2 is the disklabel, which is 0x200 bytes long. +# The second part is BTX, which is thus loaded into 0x9000, which is where +# it also runs from. The boot2.bin binary starts right after the end of +# BTX, so we have to figure out where the start of it is and then move the +# binary to 0xb000. Normally, BTX clients start at MEM_USR, or 0xa000, but +# when we use btxld create boot2, we use an entry point of 0x1000. That +# entry point is relative to MEM_USR; thus boot2.bin starts at 0xb000. +# +main.5: mov %dx,MEM_ARG # Save args + movb $0x2,%dh # Sector count + mov $0x7e00, %bx + callw nreadbx # Read disk + movb $0x40,%dh # Sector count + movb %dh, %al + callw puthex + mov $0x7e00, %bx + callw nreadbx # Read disk + push %si + mov $msg_r1,%si + callw putstr + pop %si + lcall $0x800,$0 # enter the rom code + int $0x19 + +msg_r1: .asciz " done\r\n" + +.if 0 + mov $MEM_BTX,%bx # BTX + mov 0xa(%bx),%si # Get BTX length and set + add %bx,%si # %si to start of boot2.bin + mov $MEM_USR+SIZ_PAG,%di # Client page 1 + mov $MEM_BTX+0xe*SIZ_SEC,%cx # Byte + sub %si,%cx # count + rep # Relocate + movsb # client + sub %di,%cx # Byte count + xorb %al,%al # Zero assumed bss from + rep # the end of boot2.bin + stosb # up to 0x10000 + callw seta20 # Enable A20 + jmp start+MEM_JMP-MEM_ORG # Start BTX +# +# Enable A20 so we can access memory above 1 meg. +# +seta20: cli # Disable interrupts +seta20.1: inb $0x64,%al # Get status + testb $0x2,%al # Busy? + jnz seta20.1 # Yes + movb $0xd1,%al # Command: Write + outb %al,$0x64 # output port +seta20.2: inb $0x64,%al # Get status + testb $0x2,%al # Busy? + jnz seta20.2 # Yes + movb $0xdf,%al # Enable + outb %al,$0x60 # A20 + sti # Enable interrupts + retw # To caller +.endif +# +# Trampoline used to call read from within boot1. +# +nread: mov $MEM_BUF,%bx # Transfer buffer +nreadbx: # same but address is in bx + mov 0x8(%si),%ax # Get + mov 0xa(%si),%cx # LBA + push %bx + push %ax + callw putword + pop %ax + pop %bx + push %cs # Read from + callw xread.1 # disk + jnc return # If success, return + mov $msg_read,%si # Otherwise, set the error + # message and fall through to + # the error routine +# +# Print out the error message pointed to by %ds:(%si) followed +# by a prompt, wait for a keypress, and then reboot the machine. +# +error: callw putstr # Display message + mov $prompt,%si # Display + callw putstr # prompt + xorb %ah,%ah # BIOS: Get + int $0x16 # keypress + movw $0x1234, BDA_BOOT # Do a warm boot + ljmp $0xffff,$0x0 # reboot the machine +# +# Display a null-terminated string using the BIOS output. +# +putstr.0: call putchar +putstr: lodsb # Get char + testb %al,%al # End of string? + jne putstr.0 # No + retw + +putword: push %ax + movb $'.', %al + callw putchar + movb %ah, %al + callw puthex + pop %ax +puthex: push %ax + shr $4, %al + callw putdigit + pop %ax +putdigit: + andb $0xf, %al + addb $0x30, %al + cmpb $0x39, %al + jbe putchar + addb $7, %al +putchar: push %ax + mov $0x7,%bx + movb $0xe,%ah + int $0x10 + pop %ax + retw + +# +# Overused return code. ereturn is used to return an error from the +# read function. Since we assume putstr succeeds, we (ab)use the +# same code when we return from putstr. +# +ereturn: movb $0x1,%ah # Invalid + stc # argument +return: retw # To caller +# +# Reads sectors from the disk. If EDD is enabled, then check if it is +# installed and use it if it is. If it is not installed or not enabled, then +# fall back to using CHS. Since we use a LBA, if we are using CHS, we have to +# fetch the drive parameters from the BIOS and divide it out ourselves. +# Call with: +# +# %dl - byte - drive number +# stack - 10 bytes - EDD Packet +# +read: push %dx # Save + movb $0x8,%ah # BIOS: Get drive + int $0x13 # parameters + movb %dh,%ch # Max head number + pop %dx # Restore + jc return # If error + andb $0x3f,%cl # Sectors per track + jz ereturn # If zero + cli # Disable interrupts + mov 0x8(%bp),%eax # Get LBA + push %dx # Save + movzbl %cl,%ebx # Divide by + xor %edx,%edx # sectors + div %ebx # per track + movb %ch,%bl # Max head number + movb %dl,%ch # Sector number + inc %bx # Divide by + xorb %dl,%dl # number + div %ebx # of heads + movb %dl,%bh # Head number + pop %dx # Restore + cmpl $0x3ff,%eax # Cylinder number supportable? + sti # Enable interrupts + ja read.7 # No, try EDD + xchgb %al,%ah # Set up cylinder + rorb $0x2,%al # number + orb %ch,%al # Merge + inc %ax # sector + xchg %ax,%cx # number + movb %bh,%dh # Head number + subb %ah,%al # Sectors this track + mov 0x2(%bp),%ah # Blocks to read + cmpb %ah,%al # To read + jb read.2 # this + movb %ah,%al # track +read.2: mov $0x5,%di # Try count +read.3: les 0x4(%bp),%bx # Transfer buffer + push %ax # Save + movb $0x2,%ah # BIOS: Read + int $0x13 # from disk + pop %bx # Restore + jnc read.4 # If success + dec %di # Retry? + jz read.6 # No + xorb %ah,%ah # BIOS: Reset + int $0x13 # disk system + xchg %bx,%ax # Block count + jmp read.3 # Continue +read.4: movzbw %bl,%ax # Sectors read + add %ax,0x8(%bp) # Adjust + jnc read.5 # LBA, + incw 0xa(%bp) # transfer +read.5: shlb %bl # buffer + add %bl,0x5(%bp) # pointer, + sub %al,0x2(%bp) # block count + ja read # If not done +read.6: retw # To caller +read.7: testb $FL_PACKET,%cs:MEM_REL+flags-start # LBA support enabled? + jz ereturn # No, so return an error + mov $0x55aa,%bx # Magic + push %dx # Save + movb $0x41,%ah # BIOS: Check + int $0x13 # extensions present + pop %dx # Restore + jc return # If error, return an error + cmp $0xaa55,%bx # Magic? + jne ereturn # No, so return an error + testb $0x1,%cl # Packet interface? + jz ereturn # No, so return an error + mov %bp,%si # Disk packet + movb $0x42,%ah # BIOS: Extended + int $0x13 # read + retw # To caller + +# Messages + +msg_read: .asciz "Rd" +msg_part: .asciz "Boot" + +prompt: .asciz " err\r\n" + +flags: .byte FLAGS # Flags + + .org PRT_OFF,0x90 + +# Partition table + + .fill 0x30,0x1,0x0 +part4: .byte 0x80 + .byte 0x00 # start head + .byte 0x01 # start sector (6 bits) + start cyl (2 bit) + .byte 0x00 # start cyl (low 8 bits) + .byte 0x1 # part.type + .byte 0xff # end head + .byte 0xff # end sect (6) + end_cyl(2) + .byte 0xff # end cyl + .byte 0x00, 0x00, 0x00, 0x00 # explicit start + .byte 0x50, 0xc3, 0x00, 0x00 # 50000 sectors long, bleh + + .word 0xaa55 # Magic number diff --git a/src/arch/i386/prefix/comprefix.S b/src/arch/i386/prefix/comprefix.S new file mode 100644 index 00000000..8be8db77 --- /dev/null +++ b/src/arch/i386/prefix/comprefix.S @@ -0,0 +1,49 @@ +/* We need a real mode stack that won't be stomped on by Etherboot + which starts at 0x20000. Choose something that's sufficiently high, + but not in DOC territory. Note that we couldn't do this in a real + .com program since stack variables are in the same segment as the + code and data, but this isn't really a .com program, it just looks + like one to make DOS load it into memory. It still has the 64kB + limitation of .com files though. */ +#define STACK_SEG 0x7000 +#define STACK_SIZE 0x4000 + + .text + .code16 + .arch i386 + .section ".prefix", "ax", @progbits + .globl _prefix + +/* Cheat a little with the relocations: .COM files are loaded at 0x100 */ +_prefix: + /* Set up temporary stack */ + movw $STACK_SEG, %ax + movw %ax, %ss + movw $STACK_SIZE, %sp + + pushl $0 /* No parameters to preserve for exit path */ + pushw $0 /* Dummy return address - use prefix_exit */ + + /* Calculate segment address of image start */ + pushw %cs + popw %ax + addw $(0x100/16), %ax + pushw %ax + pushw $_start + /* Calculated lcall to _start with %cs:0000 = image start */ + lret + + .section ".text16", "ax", @progbits + .globl prefix_exit +prefix_exit: + movw $0x4c00,%ax /* return to DOS */ + int $0x21 /* reach this on Quit */ + .globl prefix_exit_end +prefix_exit_end: + .previous + +/* The body of etherboot is attached here at build time. + * Force 16 byte alignment + */ + .align 16,0 +_body: diff --git a/src/arch/i386/prefix/elf_dprefix.S b/src/arch/i386/prefix/elf_dprefix.S new file mode 100644 index 00000000..d2453be4 --- /dev/null +++ b/src/arch/i386/prefix/elf_dprefix.S @@ -0,0 +1,96 @@ +#include "elf.h" + + .arch i386 + .section ".prefix", "a", @progbits + +#define LOAD_ADDR 0x10000 + + /* ELF Header */ + .globl elf_header +elf_header: +e_ident: .byte 0x7f, 'E', 'L', 'F', 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 +e_type: .short ET_DYN +e_machine: .short EM_386 +e_version: .long 1 +e_entry: .long LOAD_ADDR + _start - elf_header +e_phoff: .long elf_program_header - elf_header +e_shoff: .long 0 +e_flags: .long 0 +e_ehsize: .short elf_header_end - elf_header +e_phentsize: .short ELF32_PHDR_SIZE +e_phnum: .short (elf_program_header_end - elf_program_header)/ELF32_PHDR_SIZE +e_shentsize: .short 0 +e_shnum: .short 0 +e_shstrndx: .short 0 +elf_header_end: + +elf_program_header: +phdr1_p_type: .long PT_NOTE +phdr1_p_offset: .long elf_note - elf_header +phdr1_p_vaddr: .long elf_note +phdr1_p_paddr: .long elf_note +phdr1_p_filesz: .long elf_note_end - elf_note +phdr1_p_memsz: .long elf_note_end - elf_note +phdr1_p_flags: .long PF_R | PF_W | PF_X +phdr1_p_align: .long 0 + +/* The decompressor */ +phdr2_p_type: .long PT_LOAD +phdr2_p_offset: .long 0 +phdr2_p_vaddr: .long elf_header +phdr2_p_paddr: .long LOAD_ADDR +phdr2_p_filesz: .long _verbatim_size +phdr2_p_memsz: .long _image_size +phdr2_p_flags: .long PF_R | PF_W | PF_X +phdr2_p_align: .long 16 + +elf_program_header_end: + + .globl elf_note +elf_note: + .balign 4 + .int 2f - 1f + .int 4f - 3f + .int EIN_PROGRAM_NAME +1: .asciz "ELFBoot" +2: + .balign 4 +3: + .asciz "Etherboot" +4: + + + .balign 4 + .int 2f - 1f + .int 4f - 3f + .int EIN_PROGRAM_VERSION +1: .asciz "ELFBoot" +2: + .balign 4 +3: + .asciz VERSION +4: + +#if 0 + .balign 4 + .int 2f - 1f + .int 4f - 3f + .int EIN_PROGRAM_CHECKSUM +1: .asciz "ELFBoot" +2: + .balign 4 +3: + .word 0 +4: +#endif + .balign 4 +elf_note_end: + + /* Dummy routines to satisfy the build */ + .section ".text16", "ax", @progbits + .globl prefix_exit +prefix_exit: + + .globl prefix_exit_end +prefix_exit_end: + .previous diff --git a/src/arch/i386/prefix/elfprefix.S b/src/arch/i386/prefix/elfprefix.S new file mode 100644 index 00000000..c5200678 --- /dev/null +++ b/src/arch/i386/prefix/elfprefix.S @@ -0,0 +1,96 @@ +#include "elf.h" + + .arch i386 + .section ".prefix", "a", @progbits + +#define LOAD_ADDR 0x10000 + + /* ELF Header */ + .globl elf_header +elf_header: +e_ident: .byte 0x7f, 'E', 'L', 'F', 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 +e_type: .short ET_EXEC +e_machine: .short EM_386 +e_version: .long 1 +e_entry: .long LOAD_ADDR + _start - elf_header +e_phoff: .long elf_program_header - elf_header +e_shoff: .long 0 +e_flags: .long 0 +e_ehsize: .short elf_header_end - elf_header +e_phentsize: .short ELF32_PHDR_SIZE +e_phnum: .short (elf_program_header_end - elf_program_header)/ELF32_PHDR_SIZE +e_shentsize: .short 0 +e_shnum: .short 0 +e_shstrndx: .short 0 +elf_header_end: + +elf_program_header: +phdr1_p_type: .long PT_NOTE +phdr1_p_offset: .long elf_note - elf_header +phdr1_p_vaddr: .long elf_note +phdr1_p_paddr: .long elf_note +phdr1_p_filesz: .long elf_note_end - elf_note +phdr1_p_memsz: .long elf_note_end - elf_note +phdr1_p_flags: .long PF_R | PF_W | PF_X +phdr1_p_align: .long 0 + +/* The decompressor */ +phdr2_p_type: .long PT_LOAD +phdr2_p_offset: .long 0 +phdr2_p_vaddr: .long elf_header +phdr2_p_paddr: .long LOAD_ADDR +phdr2_p_filesz: .long _verbatim_size +phdr2_p_memsz: .long _image_size +phdr2_p_flags: .long PF_R | PF_W | PF_X +phdr2_p_align: .long 16 + +elf_program_header_end: + + .globl elf_note +elf_note: + .balign 4 + .int 2f - 1f + .int 4f - 3f + .int EIN_PROGRAM_NAME +1: .asciz "ELFBoot" +2: + .balign 4 +3: + .asciz "Etherboot" +4: + + + .balign 4 + .int 2f - 1f + .int 4f - 3f + .int EIN_PROGRAM_VERSION +1: .asciz "ELFBoot" +2: + .balign 4 +3: + .asciz VERSION +4: + +#if 0 + .balign 4 + .int 2f - 1f + .int 4f - 3f + .int EIN_PROGRAM_CHECKSUM +1: .asciz "ELFBoot" +2: + .balign 4 +3: + .word 0 +4: +#endif + .balign 4 +elf_note_end: + + /* Dummy routines to satisfy the build */ + .section ".text16", "ax", @progbits + .globl prefix_exit +prefix_exit: + + .globl prefix_exit_end +prefix_exit_end: + .previous diff --git a/src/arch/i386/prefix/exeprefix.S b/src/arch/i386/prefix/exeprefix.S new file mode 100755 index 00000000..d169563e --- /dev/null +++ b/src/arch/i386/prefix/exeprefix.S @@ -0,0 +1,44 @@ +/* + Prefix for .exe images + Doesn't work yet, even though it starts off the same as a .com + image as shown by DOS debug. +*/ + + .text + .code16 + .arch i386 + .section ".prefix", "ax", @progbits + .globl _prefix + +_prefix: + .byte 'M', 'Z' + .short _exe_size_tail /* tail */ + .short _exe_size_pages /* pages */ + .short 0 /* relocations */ + .short 2 /* header paras */ + .short _exe_bss_size /* min */ + .short 0xFFFF /* max paras */ + .short _exe_ss_offset /* SS */ + .short _stack_size /* SP */ + .short 0 /* checksum */ + .short 0 /* IP */ + .short 0 /* CS */ + .short 0x1C /* reloc offset */ + .short 0 /* overlay number */ + .short 0 /* fill */ + .short 0 /* fill */ + + .section ".text16", "ax", @progbits + .globl prefix_exit +prefix_exit: + movw $0x4c00,%ax /* return to DOS */ + int $0x21 /* reach this on Quit */ + .globl prefix_exit_end +prefix_exit_end: + .previous + +/* The body of etherboot is attached here at build time. + * Force 16 byte alignment + */ + .align 16,0 +_body: diff --git a/src/arch/i386/prefix/floppyprefix.S b/src/arch/i386/prefix/floppyprefix.S new file mode 100644 index 00000000..18bed4c8 --- /dev/null +++ b/src/arch/i386/prefix/floppyprefix.S @@ -0,0 +1,359 @@ +/* NOTE: this boot sector contains instructions that need at least an 80186. + * Yes, as86 has a bug somewhere in the valid instruction set checks. + * + * SYS_SIZE is the number of clicks (16 bytes) to be loaded. + */ +.equ SYSSIZE, 8192 # 8192 * 16 bytes = 128kB maximum size of .ROM file + +/* floppyload.S Copyright (C) 1991, 1992 Linus Torvalds + * modified by Drew Eckhardt + * modified by Bruce Evans (bde) + * + * floppyprefix.S is loaded at 0x0000:0x7c00 by the bios-startup routines. + * + * It then loads the system at SYSSEG<<4, using BIOS interrupts. + * + * The loader has been made as simple as possible, and continuous read errors + * will result in a unbreakable loop. Reboot by hand. It loads pretty fast by + * getting whole tracks at a time whenever possible. + */ + +.equ BOOTSEG, 0x07C0 /* original address of boot-sector */ + +.equ SYSSEG, 0x1000 /* system loaded at SYSSEG<<4 */ + + .org 0 + .arch i386 + .text + .section ".prefix", "ax", @progbits + .code16 + + jmp $BOOTSEG, $go /* reload cs:ip to match relocation addr */ +go: + movw $0x2000-12, %di /* 0x2000 is arbitrary value >= length */ + /* of bootsect + room for stack + 12 for */ + /* saved disk parm block */ + + movw $BOOTSEG, %ax + movw %ax,%ds + movw %ax,%es + movw %ax,%ss /* put stack at BOOTSEG:0x4000-12. */ + movw %di,%sp + +/* Many BIOS's default disk parameter tables will not recognize multi-sector + * reads beyond the maximum sector number specified in the default diskette + * parameter tables - this may mean 7 sectors in some cases. + * + * Since single sector reads are slow and out of the question, we must take care + * of this by creating new parameter tables (for the first disk) in RAM. We + * will set the maximum sector count to 36 - the most we will encounter on an + * ED 2.88. High doesn't hurt. Low does. + * + * Segments are as follows: ds=es=ss=cs - BOOTSEG + */ + + xorw %cx,%cx + movw %cx,%es /* access segment 0 */ + movw $0x78, %bx /* 0:bx is parameter table address */ + pushw %ds /* save ds */ +/* 0:bx is parameter table address */ + ldsw %es:(%bx),%si /* loads ds and si */ + + movw %ax,%es /* ax is BOOTSECT (loaded above) */ + movb $6, %cl /* copy 12 bytes */ + cld + pushw %di /* keep a copy for later */ + rep + movsw /* ds:si is source, es:di is dest */ + popw %di + + movb $36,%es:4(%di) + + movw %cx,%ds /* access segment 0 */ + xchgw %di,(%bx) + movw %es,%si + xchgw %si,2(%bx) + popw %ds /* restore ds */ + movw %di, dpoff /* save old parameters */ + movw %si, dpseg /* to restore just before finishing */ + pushw %ds + popw %es /* reload es */ + +/* Note that es is already set up. Also cx is 0 from rep movsw above. */ + + xorb %ah,%ah /* reset FDC */ + xorb %dl,%dl + int $0x13 + +/* Get disk drive parameters, specifically number of sectors/track. + * + * It seems that there is no BIOS call to get the number of sectors. Guess + * 36 sectors if sector 36 can be read, 18 sectors if sector 18 can be read, + * 15 if sector 15 can be read. Otherwise guess 9. + */ + + movw $disksizes, %si /* table of sizes to try */ + +probe_loop: + lodsb + cbtw /* extend to word */ + movw %ax, sectors + cmpw $disksizes+4, %si + jae got_sectors /* if all else fails, try 9 */ + xchgw %cx,%ax /* cx = track and sector */ + xorw %dx,%dx /* drive 0, head 0 */ + movw $0x0200, %bx /* address after boot sector */ + /* (512 bytes from origin, es = cs) */ + movw $0x0201, %ax /* service 2, 1 sector */ + int $0x13 + jc probe_loop /* try next value */ + +got_sectors: + movw $msg1end-msg1, %cx + movw $msg1, %si + call print_str + +/* ok, we've written the Loading... message, now we want to load the system */ + + pushw %es /* = ds */ + movw $SYSSEG, %ax + movw %ax,%es /* segment of SYSSEG<<4 */ + pushw %es + call read_it + +/* This turns off the floppy drive motor, so that we enter the kernel in a + * known state, and don't have to worry about it later. + */ + movw $0x3f2, %dx + xorb %al,%al + outb %al,%dx + + call print_nl + pop %es /* = SYSSEG */ + pop %es /* balance push/pop es */ +sigok: + +/* Restore original disk parameters */ + movw $0x78, %bx + movw dpoff, %di + movw dpseg, %si + xorw %ax,%ax + movw %ax,%ds + movw %di,(%bx) + movw %si,2(%bx) + +/* after that (everything loaded), we call to the .ROM file loaded. */ + + pushl $0 /* No parameters to preserve for exit path */ + pushw $0 /* Use prefix exit path mechanism */ + ljmp $SYSSEG, $_start + + .section ".text16", "ax", @progbits + .globl prefix_exit +prefix_exit: + xchgw %bx, %bx + int $0x19 /* should try to boot machine */ + .globl prefix_exit_end +prefix_exit_end: + .previous + +/* This routine loads the system at address SYSSEG<<4, making sure no 64kB + * boundaries are crossed. We try to load it as fast as possible, loading whole + * tracks whenever we can. + * + * in: es - starting address segment (normally SYSSEG) + */ +read_it: + movw $0,sread /* read whole image incl boot sector */ + movw %es,%ax + testw $0x0fff, %ax +die: jne die /* es must be at 64kB boundary */ + xorw %bx,%bx /* bx is starting address within segment */ +rp_read: + movw %es,%ax + movw %bx,%dx + movb $4, %cl + shrw %cl,%dx /* bx is always divisible by 16 */ + addw %dx,%ax + cmpw $SYSSEG+SYSSIZE, %ax /* have we loaded all yet? */ + jb ok1_read + ret +ok1_read: + movw sectors, %ax + subw sread, %ax + movw %ax,%cx + shlw $9, %cx + addw %bx,%cx + jnc ok2_read + je ok2_read + xorw %ax,%ax + subw %bx,%ax + shrw $9, %ax +ok2_read: + call read_track + movw %ax,%cx + addw sread, %ax + cmpw sectors, %ax + jne ok3_read + movw $1, %ax + subw head, %ax + jne ok4_read + incw track +ok4_read: + movw %ax, head + xorw %ax,%ax +ok3_read: + movw %ax, sread + shlw $9, %cx + addw %cx,%bx + jnc rp_read + movw %es,%ax + addb $0x10, %ah + movw %ax,%es + xorw %bx,%bx + jmp rp_read + +read_track: + pusha + pushw %ax + pushw %bx + pushw %bp /* just in case the BIOS is buggy */ + movw $0x0e2e, %ax /* 0x2e = . */ + movw $0x0007, %bx + int $0x10 + popw %bp + popw %bx + popw %ax + + movw track, %dx + movw sread, %cx + incw %cx + movb %dl,%ch + movw head, %dx + movb %dl,%dh + andw $0x0100, %dx + movb $2, %ah + + pushw %dx /* save for error dump */ + pushw %cx + pushw %bx + pushw %ax + + int $0x13 + jc bad_rt + addw $8, %sp + popa + ret + +bad_rt: pushw %ax /* save error code */ + call print_all /* ah = error, al = read */ + + xorb %ah,%ah + xorb %dl,%dl + int $0x13 + + addw $10, %sp + popa + jmp read_track + +/* print_all is for debugging purposes. It will print out all of the registers. + * The assumption is that this is called from a routine, with a stack frame like + * dx + * cx + * bx + * ax + * error + * ret <- sp + */ + +print_all: + call print_nl /* nl for readability */ + movw $5, %cx /* error code + 4 registers */ + movw %sp,%bp + +print_loop: + pushw %cx /* save count left */ + + cmpb $5, %cl + jae no_reg /* see if register name is needed */ + + movw $0x0007, %bx /* page 0, attribute 7 (normal) */ + movw $0xe05+0x41-1, %ax + subb %cl,%al + int $0x10 + + movb $0x58, %al /* 'X' */ + int $0x10 + + movb $0x3A, %al /* ':' */ + int $0x10 + +no_reg: + addw $2, %bp /* next register */ + call print_hex /* print it */ + movb $0x20, %al /* print a space */ + int $0x10 + popw %cx + loop print_loop + call print_nl /* nl for readability */ + ret + +print_str: + movw $0x0007, %bx /* page 0, attribute 7 (normal) */ + movb $0x0e, %ah /* write char, tty mode */ +prloop: + lodsb + int $0x10 + loop prloop + ret + +print_nl: + movw $0x0007, %bx /* page 0, attribute 7 (normal) */ + movw $0xe0d, %ax /* CR */ + int $0x10 + movb $0xa, %al /* LF */ + int $0x10 + ret + +/* print_hex prints the word pointed to by ss:bp in hexadecimal. */ + +print_hex: + movw (%bp),%dx /* load word into dx */ + movb $4, %cl + movb $0x0e, %ah /* write char, tty mode */ + movw $0x0007, %bx /* page 0, attribute 7 (normal) */ + call print_digit + call print_digit + call print_digit +/* fall through */ +print_digit: + rol %cl,%dx /* rotate so that lowest 4 bits are used */ + movb $0x0f, %al /* mask for nybble */ + andb %dl,%al + addb $0x90, %al /* convert al to ascii hex (four instructions) */ + daa + adcb $0x40, %al + daa + int $0x10 + ret + +sread: .word 0 /* sectors read of current track */ +head: .word 0 /* current head */ +track: .word 0 /* current track */ + +sectors: + .word 0 + +dpseg: .word 0 +dpoff: .word 0 + +disksizes: + .byte 36,18,15,9 + +msg1: + .ascii "Loading ROM image" +msg1end: + + .org 510, 0 + .word 0xAA55 + diff --git a/src/arch/i386/prefix/huf.lds b/src/arch/i386/prefix/huf.lds new file mode 100644 index 00000000..1e8da162 --- /dev/null +++ b/src/arch/i386/prefix/huf.lds @@ -0,0 +1,6 @@ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) + +SECTIONS { + .huf : { *(*) } +} diff --git a/src/arch/i386/prefix/img.lds b/src/arch/i386/prefix/img.lds new file mode 100644 index 00000000..e383d89e --- /dev/null +++ b/src/arch/i386/prefix/img.lds @@ -0,0 +1,6 @@ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) + +SECTIONS { + .img : { *(*) } +} diff --git a/src/arch/i386/prefix/liloprefix.S b/src/arch/i386/prefix/liloprefix.S new file mode 100644 index 00000000..1aeb071f --- /dev/null +++ b/src/arch/i386/prefix/liloprefix.S @@ -0,0 +1,144 @@ +/* + Copyright (C) 2000, Entity Cyber, Inc. + + Authors: Gary Byers (gb@thinguin.org) + Marty Connor (mdc@thinguin.org) + + This software may be used and distributed according to the terms + of the GNU Public License (GPL), incorporated herein by reference. + + Description: + + This is just a little bit of code and data that can get prepended + to an Etherboot ROM image in order to allow LILO to load the + result as if it were a Linux kernel image. + + A real Linux kernel image consists of a one-sector boot loader + (to load the image from a floppy disk), followed a few sectors + of setup code, followed by the kernel code itself. There's + a table in the first sector (starting at offset 497) that indicates + how many sectors of setup code follow the first sector and which + contains some other parameters that aren't interesting in this + case. + + When LILO loads the sectors that comprise a kernel image, it doesn't + execute the code in the first sector (since that code would try to + load the image from a floppy disk.) The code in the first sector + below doesn't expect to get executed (and prints an error message + if it ever -is- executed.) LILO's only interested in knowing the + number of setup sectors advertised in the table (at offset 497 in + the first sector.) + + Etherboot doesn't require much in the way of setup code. + Historically, the Linux kernel required at least 4 sectors of + setup code. Current versions of LILO look at the byte at + offset 497 in the first sector to indicate how many sectors + of setup code are contained in the image. + +*/ + +#define SETUPSECS 4 /* Minimal nr of setup-sectors */ +#define PREFIXSIZE ((SETUPSECS+1)*512) +#define PREFIXPGH (PREFIXSIZE / 16 ) +#define BOOTSEG 0x07C0 /* original address of boot-sector */ +#define INITSEG 0x9000 /* we move boot here - out of the way */ +#define SETUPSEG 0x9020 /* setup starts here */ +#define SYSSEG 0x1000 /* system loaded at 0x10000 (65536). */ + + .text + .code16 + .arch i386 + .org 0 + .section ".prefix", "ax", @progbits + .globl _prefix +_prefix: + +/* + This is a minimal boot sector. If anyone tries to execute it (e.g., if + a .lilo file is dd'ed to a floppy), print an error message. +*/ + +bootsector: + jmp $BOOTSEG, $go - _prefix /* reload cs:ip to match relocation addr */ +go: + movw $0x2000, %di /* 0x2000 is arbitrary value >= length + of bootsect + room for stack */ + + movw $BOOTSEG, %ax + movw %ax,%ds + movw %ax,%es + + cli + movw %ax, %ss /* put stack at BOOTSEG:0x2000. */ + movw %di,%sp + sti + + movw $why_end-why, %cx + movw $why - _prefix, %si + + movw $0x0007, %bx /* page 0, attribute 7 (normal) */ + movb $0x0e, %ah /* write char, tty mode */ +prloop: + lodsb + int $0x10 + loop prloop +freeze: jmp freeze + +why: .ascii "This image cannot be loaded from a floppy disk.\r\n" +why_end: + + + .org 497 +setup_sects: + .byte SETUPSECS +root_flags: + .word 0 +syssize: + .word _verbatim_size_pgh - PREFIXPGH +swap_dev: + .word 0 +ram_size: + .word 0 +vid_mode: + .word 0 +root_dev: + .word 0 +boot_flag: + .word 0xAA55 + +/* + We're now at the beginning of the second sector of the image - + where the setup code goes. + + We don't need to do too much setup for Etherboot. + + This code gets loaded at SETUPSEG:0. It wants to start + executing the Etherboot image that's loaded at SYSSEG:0 and + whose entry point is SYSSEG:0. +*/ +setup_code: + pushl $0 /* No parameters to preserve for exit path */ + pushw $0 /* Use prefix exit path mechanism */ + /* Etherboot expects to be contiguous in memory once loaded. + * LILO doesn't do this, but since we don't need any + * information that's left in the prefix, it doesn't matter: + * we just have to ensure that %cs:0000 is where the start of + * the Etherboot image *would* be. + */ + ljmp $(SYSSEG-(PREFIXSIZE/16)), $_start + + .section ".text16", "ax", @progbits + .globl prefix_exit +prefix_exit: + int $0x19 /* should try to boot machine */ + .globl prefix_exit_end +prefix_exit_end: + .previous + + .org (PREFIXSIZE-1) + .byte 0 +prefix_end: +/* + That's about it. +*/ + diff --git a/src/arch/i386/prefix/lmelf_dprefix.S b/src/arch/i386/prefix/lmelf_dprefix.S new file mode 100644 index 00000000..65fe1356 --- /dev/null +++ b/src/arch/i386/prefix/lmelf_dprefix.S @@ -0,0 +1,163 @@ +#include "elf.h" + .arch sledgehammer + .code32 + .equ FLAT_CODE_SEG,_pmcs-_gdt + .equ FLAT_DATA_SEG,_pmds-_gdt + .equ MSR_K6_EFER, 0xC0000080 + .equ EFER_LME, 0x00000100 + .equ X86_CR4_PAE, 0x00000020 + .equ CR0_PG, 0x80000000 + + .section ".prefix", "ax", @progbits + +#define LOAD_ADDR 0x10000 + + /* ELF Header */ + .globl elf_header +elf_header: +e_ident: .byte 0x7f, 'E', 'L', 'F', 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 +e_type: .short ET_DYN +e_machine: .short EM_X86_64 +e_version: .long 1 +e_entry: .long LOAD_ADDR + elf_start - elf_header +e_phoff: .long elf_program_header - elf_header +e_shoff: .long 0 +e_flags: .long 0 +e_ehsize: .short elf_header_end - elf_header +e_phentsize: .short ELF32_PHDR_SIZE +e_phnum: .short (elf_program_header_end - elf_program_header)/ELF32_PHDR_SIZE +e_shentsize: .short 0 +e_shnum: .short 0 +e_shstrndx: .short 0 +elf_header_end: + +elf_program_header: +phdr1_p_type: .long PT_NOTE +phdr1_p_offset: .long elf_note - elf_header +phdr1_p_vaddr: .long elf_note +phdr1_p_paddr: .long elf_note +phdr1_p_filesz: .long elf_note_end - elf_note +phdr1_p_memsz: .long elf_note_end - elf_note +phdr1_p_flags: .long PF_R | PF_W | PF_X +phdr1_p_align: .long 0 + +/* The decompressor */ +phdr2_p_type: .long PT_LOAD +phdr2_p_offset: .long 0 +phdr2_p_vaddr: .long elf_header +phdr2_p_paddr: .long LOAD_ADDR +phdr2_p_filesz: .long _verbatim_size +phdr2_p_memsz: .long _image_size +phdr2_p_flags: .long PF_R | PF_W | PF_X +phdr2_p_align: .long 16 + +elf_program_header_end: + + .globl elf_note +elf_note: + .balign 4 + .int 2f - 1f + .int 4f - 3f + .int EIN_PROGRAM_NAME +1: .asciz "ELFBoot" +2: + .balign 4 +3: + .asciz "Etherboot" +4: + + + .balign 4 + .int 2f - 1f + .int 4f - 3f + .int EIN_PROGRAM_VERSION +1: .asciz "ELFBoot" +2: + .balign 4 +3: + .asciz VERSION +4: + +#if 0 + .balign 4 + .int 2f - 1f + .int 4f - 3f + .int EIN_PROGRAM_CHECKSUM +1: .asciz "ELFBoot" +2: + .balign 4 +3: + .word 0 +4: +#endif + .balign 4 +elf_note_end: + +elf_start: + .code64 + /* Reload the gdt to something I know */ + leaq _gdt(%rip), %rax + movq %rax, 0x02 + gdtptr(%rip) + lgdt gdtptr(%rip) + + /* Enter 32bit compatibility mode */ + leaq elf_start32(%rip), %rax + movl %eax, 0x00 + elf_start32_addr(%rip) + ljmp *elf_start32_addr(%rip) + +elf_start32: + .code32 + /* Reload the data segments */ + movl $FLAT_DATA_SEG, %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %ss + + /* Disable paging */ + movl %cr0, %eax + andl $~CR0_PG, %eax + movl %eax, %cr0 + + /* Disable long mode */ + movl $MSR_K6_EFER, %ecx + rdmsr + andl $~EFER_LME, %eax + wrmsr + + /* Disable PAE */ + movl %cr4, %eax + andl $~X86_CR4_PAE, %eax + movl %eax, %cr4 + + /* Save the first argument */ + pushl %ebx + jmp _start + +gdtptr: + .word _gdt_end - _gdt -1 + .long _gdt + .long 0 +_gdt: +elf_start32_addr: + .long elf_start32 + .long FLAT_CODE_SEG +_pmcs: + /* 32 bit protected mode code segment, base 0 */ + .word 0xffff,0 + .byte 0,0x9f,0xcf,0 + +_pmds: + /* 32 bit protected mode data segment, base 0 */ + .word 0xffff,0 + .byte 0,0x93,0xcf,0 +_gdt_end: + + + /* Dummy routines to satisfy the build */ + .section ".text16", "ax", @progbits + .globl prefix_exit +prefix_exit: + + .globl prefix_exit_end +prefix_exit_end: + .previous diff --git a/src/arch/i386/prefix/lmelf_prefix.S b/src/arch/i386/prefix/lmelf_prefix.S new file mode 100644 index 00000000..7488f452 --- /dev/null +++ b/src/arch/i386/prefix/lmelf_prefix.S @@ -0,0 +1,163 @@ +#include "elf.h" + .arch sledgehammer + .code32 + .equ FLAT_CODE_SEG,_pmcs-_gdt + .equ FLAT_DATA_SEG,_pmds-_gdt + .equ MSR_K6_EFER, 0xC0000080 + .equ EFER_LME, 0x00000100 + .equ X86_CR4_PAE, 0x00000020 + .equ CR0_PG, 0x80000000 + + .section ".prefix", "ax", @progbits + +#define LOAD_ADDR 0x10000 + + /* ELF Header */ + .globl elf_header +elf_header: +e_ident: .byte 0x7f, 'E', 'L', 'F', 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 +e_type: .short ET_EXEC +e_machine: .short EM_X86_64 +e_version: .long 1 +e_entry: .long LOAD_ADDR + elf_start - elf_header +e_phoff: .long elf_program_header - elf_header +e_shoff: .long 0 +e_flags: .long 0 +e_ehsize: .short elf_header_end - elf_header +e_phentsize: .short ELF32_PHDR_SIZE +e_phnum: .short (elf_program_header_end - elf_program_header)/ELF32_PHDR_SIZE +e_shentsize: .short 0 +e_shnum: .short 0 +e_shstrndx: .short 0 +elf_header_end: + +elf_program_header: +phdr1_p_type: .long PT_NOTE +phdr1_p_offset: .long elf_note - elf_header +phdr1_p_vaddr: .long elf_note +phdr1_p_paddr: .long elf_note +phdr1_p_filesz: .long elf_note_end - elf_note +phdr1_p_memsz: .long elf_note_end - elf_note +phdr1_p_flags: .long PF_R | PF_W | PF_X +phdr1_p_align: .long 0 + +/* The decompressor */ +phdr2_p_type: .long PT_LOAD +phdr2_p_offset: .long 0 +phdr2_p_vaddr: .long elf_header +phdr2_p_paddr: .long LOAD_ADDR +phdr2_p_filesz: .long _verbatim_size +phdr2_p_memsz: .long _image_size +phdr2_p_flags: .long PF_R | PF_W | PF_X +phdr2_p_align: .long 16 + +elf_program_header_end: + + .globl elf_note +elf_note: + .balign 4 + .int 2f - 1f + .int 4f - 3f + .int EIN_PROGRAM_NAME +1: .asciz "ELFBoot" +2: + .balign 4 +3: + .asciz "Etherboot" +4: + + + .balign 4 + .int 2f - 1f + .int 4f - 3f + .int EIN_PROGRAM_VERSION +1: .asciz "ELFBoot" +2: + .balign 4 +3: + .asciz VERSION +4: + +#if 0 + .balign 4 + .int 2f - 1f + .int 4f - 3f + .int EIN_PROGRAM_CHECKSUM +1: .asciz "ELFBoot" +2: + .balign 4 +3: + .word 0 +4: +#endif + .balign 4 +elf_note_end: + +elf_start: + .code64 + /* Reload the gdt to something I know */ + leaq _gdt(%rip), %rax + movq %rax, 0x02 + gdtptr(%rip) + lgdt gdtptr(%rip) + + /* Enter 32bit compatibility mode */ + leaq elf_start32(%rip), %rax + movl %eax, 0x00 + elf_start32_addr(%rip) + ljmp *elf_start32_addr(%rip) + +elf_start32: + .code32 + /* Reload the data segments */ + movl $FLAT_DATA_SEG, %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %ss + + /* Disable paging */ + movl %cr0, %eax + andl $~CR0_PG, %eax + movl %eax, %cr0 + + /* Disable long mode */ + movl $MSR_K6_EFER, %ecx + rdmsr + andl $~EFER_LME, %eax + wrmsr + + /* Disable PAE */ + movl %cr4, %eax + andl $~X86_CR4_PAE, %eax + movl %eax, %cr4 + + /* Save the first argument */ + pushl %ebx + jmp _start + +gdtptr: + .word _gdt_end - _gdt -1 + .long _gdt + .long 0 +_gdt: +elf_start32_addr: + .long elf_start32 + .long FLAT_CODE_SEG +_pmcs: + /* 32 bit protected mode code segment, base 0 */ + .word 0xffff,0 + .byte 0,0x9f,0xcf,0 + +_pmds: + /* 32 bit protected mode data segment, base 0 */ + .word 0xffff,0 + .byte 0,0x93,0xcf,0 +_gdt_end: + + + /* Dummy routines to satisfy the build */ + .section ".text16", "ax", @progbits + .globl prefix_exit +prefix_exit: + + .globl prefix_exit_end +prefix_exit_end: + .previous diff --git a/src/arch/i386/prefix/nullprefix.S b/src/arch/i386/prefix/nullprefix.S new file mode 100644 index 00000000..dbee141b --- /dev/null +++ b/src/arch/i386/prefix/nullprefix.S @@ -0,0 +1,16 @@ + .org 0 + .text + .arch i386 + + .section ".prefix", "ax", @progbits + .code16 + .globl _prefix +_prefix: + + .section ".text16", "ax", @progbits + .globl prefix_exit +prefix_exit: + + .globl prefix_exit_end +prefix_exit_end: + .previous diff --git a/src/arch/i386/prefix/pxeprefix.S b/src/arch/i386/prefix/pxeprefix.S new file mode 100644 index 00000000..246e22b2 --- /dev/null +++ b/src/arch/i386/prefix/pxeprefix.S @@ -0,0 +1,398 @@ +/* Offsets of words containing ROM's CS and size (in 512 byte blocks) + * from start of floppy boot block at 0x7c00 + * Offsets must match those in etherboot.h + */ +#define FLOPPY_SEGMENT 0x7c0 + +#define PXENV_UNDI_CLEANUP 0x02 +#define PXENV_UNDI_SHUTDOWN 0x05 +#define PXENV_STOP_UNDI 0x15 +#define PXENV_UNLOAD_STACK 0x70 +#define PXENV_STOP_BASE 0x76 + +#define PUSHA_SIZE 16 +#define PXE_STACK_MAGIC 0x57ac /* 'STac' */ + + .text + .code16 + .arch i386 + .org 0 + .section ".prefix", "ax", @progbits + .globl _prefix +/***************************************************************************** + * Entry point: set cs, ds, bp, print welcome message + ***************************************************************************** + */ +_prefix: + jmp $FLOPPY_SEGMENT, $code_start-_prefix +10: .asciz "PXE->EB " +code_start: + pusha /* Preserve all registers */ + push %ds + movw %sp, %bp /* %bp must be preserved, hence do + * this after the pusha */ + push $PXE_STACK_MAGIC /* PXE stack magic marker */ + + push %cs /* Set up data segment */ + pop %ds + mov $0x40, %cx /* Set up %fs for access to 40:13 */ + mov %cx, %fs + movw $10b-_prefix, %si /* Print welcome message */ + call print_message + +/***************************************************************************** + * Detect type of PXE available (!PXE, PXENV+ or none) + ***************************************************************************** + */ +detect_pxe: + les 4+PUSHA_SIZE+2(%bp), %di /* !PXE structure */ + cmpl $0x45585021, %es:(%di) /* '!PXE' signature */ + je detected_pxe + mov $0x5650, %ax + int $0x1a + cmp $0x564e, %ax + jne detected_nothing + cmpl $0x4e455850, %es:(%bx) /* 'PXEN' signature */ + jne detected_nothing + cmpw $0x2b56, %es:4(%bx) /* 'V+' signature */ + je detected_pxenv + +detected_nothing: + movw $10f-_prefix, %si + call print_message + jmp finished_with_error +10: .asciz "No PXE " + +detected_pxenv: /* es:bx points to PXENV+ structure */ + push %es + push %bx + push %es:0x24(%bx) /* UNDI code segment */ + push %es:0x26(%bx) /* UNDI code size */ + push %es:0x20(%bx) /* UNDI data segment */ + push %es:0x22(%bx) /* UNDI data size */ + les %es:0x0a(%bx), %di /* Entry point to %es:%di */ + movw $10f-_prefix, %si + jmp pxe_setup_done +10: .asciz "PXENV+ " + +detected_pxe: /* es:di points to !PXE structure */ + push %es + push %di + push %es:0x30(%di) /* UNDI code segment */ + push %es:0x36(%di) /* UNDI code size */ + push %es:0x28(%di) /* UNDI data segment */ + push %es:0x2e(%di) /* UNDI data size */ + les %es:0x10(%di), %di /* Entry point to %es:%di */ + movw $10f-_prefix, %si + jmp pxe_setup_done +10: .asciz "!PXE " + +pxe_setup_done: + mov %es, pxe_entry_segment - _prefix + mov %di, pxe_entry_offset - _prefix + pop %ax + mov %ax, undi_data_size - _prefix + pop %ax + mov %ax, undi_data_segment - _prefix + pop %ax + mov %ax, undi_code_size - _prefix + pop %ax + mov %ax, undi_code_segment - _prefix + call print_message + pop %di + pop %es /* Exit with %es:%di containing structure address */ + +/***************************************************************************** + * Print information about located structure + ***************************************************************************** + */ +print_structure_information: + call print_segoff /* %es:%di contains address of structure */ + les %ds:(pxe_entry_segoff - _prefix), %di + call print_segoff + les %ds:(undi_code_segoff - _prefix), %di + call print_segoff + les %ds:(undi_data_segoff - _prefix), %di + call print_segoff + +/***************************************************************************** + * Unload PXE base code and UNDI driver + ***************************************************************************** + */ +#ifdef PXELOADER_KEEP_ALL + xor %ax, %ax /* Force zero flag to show success */ + jmp do_not_free_base_mem /* Skip the unloading */ +#endif /* PXELOADER_KEEP_ALL */ + +unload_pxe: + mov $PXENV_UNLOAD_STACK, %bx + call pxe_call + mov $PXENV_STOP_UNDI, %bx + call pxe_call + pushfw /* Ignore PXENV_UNDI_CLEANUP errors */ + mov $PXENV_UNDI_CLEANUP, %bx + call pxe_call + popfw + /* On exit, zero flag is set iff all calls were successful */ + +/***************************************************************************** + * Free base memory + ***************************************************************************** + */ +free_base_mem: + jnz do_not_free_base_mem /* Using zero flag from unload_pxe */ + + mov undi_code_segment - _prefix, %bx + mov undi_data_segment - _prefix, %cx + mov undi_code_size - _prefix, %ax + cmp %bx, %cx + jb 1f + mov %cx, %bx + mov undi_data_size - _prefix, %ax +1: add $0x0f, %ax /* Round up to next segment */ + shr $4, %ax + add %bx, %ax /* Highest segment address into %ax */ + add $(1024 / 16 - 1), %ax /* Round up to next kb */ + shr $6, %ax /* New free basemem size in %ax */ + mov %fs:(0x13), %bx /* Old free base memory in %bx */ + mov %ax, %fs:(0x13) /* Store new free base memory size */ + + /* Note that zero_mem_loop will also zero out our stack, so make + * sure the stack is empty at this point. + */ + mov %ax, %dx + sub %bx, %dx /* numberof kb to zero in %dx */ + shl $6, %bx /* Segment address into %bx */ +zero_mem_loop: + mov %bx, %es /* kB boundary into %es:00 */ + xor %ax, %ax + xor %di, %di + mov $0x400, %cx + rep stosb /* fill kB with zeroes */ + add $(1024 / 16), %bx + dec %dx + jnz zero_mem_loop + /* Will exit here with zero flag set, so no need to set it explicitly + * in order to indicate success. + */ + +do_not_free_base_mem: + pushf /* Save success (zero) flag status */ + mov %fs:(0x13), %ax /* Free base memory in %ax */ + call print_hex_word /* Print free base memory */ + popf /* Restore success (zero) flag */ + +/***************************************************************************** + * Exit point + * Jump to finished with the zero flag set to indicate success, or to + * finished_with_error to always report an error + ***************************************************************************** + */ +finished: + movw $10f-_prefix, %si + jz 1f +finished_with_error: + movw $20f-_prefix, %si +1: + call print_message + jmp 99f +10: .asciz " ok\n" +20: .asciz " err\n" + + + /* We place a stack here. It doesn't get used until after all + * the above code is finished, so we can happily write all + * over it. Putting the stack here ensures that it doesn't + * accidentally go over the 512 byte threshold, which would + * cause problems when returning via start32's prefix + * relocation mechanism. + */ +_estack: +99: + +/***************************************************************************** + * Run Etherboot main code + ***************************************************************************** + */ +run_etherboot: + /* Very temporarily switch stacks to one internal to the + * prefix. Do this because the stack provided by the PXE ROM + * could be absolutely anywhere, including in an area of + * memory that the call to prelocate will vapourise... + */ + pushw %ss /* PXE stack pointer to ES:DI */ + popw %es + movw %sp, %di + pushw %ds /* Set up stack in "safe" area */ + popw %ss + movw $_estack-_prefix, %sp + pushw %es /* Record PXE stack pointer */ + pushw %di + /* Relocate payload and stack to claimed base memory */ + pushw $4 /* Preserve old PXE stack pointer */ + call prelocate + popw %ax /* Remove parameter */ + pushl $4 /* Preserve old PXE stack pointer */ + pushw $0 /* Indicate prefix exit mechanism */ + jmp _start /* Run Etherboot */ + + .section ".text16", "ax", @progbits + .globl prefix_exit +prefix_exit: + pushw %cs /* Set %ds, %bp for access to text */ + popw %ds + call 1f +1: popw %bp + popw %di /* Old PXE stack to %es:di */ + popw %es + cmpw $PXE_STACK_MAGIC, %es:0(%di) /* See if PXE stack intact */ + jne exit_via_int18 +exit_via_pxe: /* Stack OK, return to PXE */ + push %es /* Restore PXE stack pointer */ + pop %ss + mov %di, %sp + pop %ax /* Discard PXE_STACK_MAGIC marker */ + leaw (10f-1b)(%bp), %si + call print_exit_message + pop %ds /* Restore PXE's DS */ + popa /* Restore PXE's other registers */ + movw $0, %ax /* Return PXENV_STATUS_SUCCESS */ + lret /* Return control to PXE ROM */ +10: .asciz "EB->PXE\r\n" +exit_via_int18: /* Stack damaged, do int 18 */ + leaw (10f-1b)(%bp), %si + call print_exit_message + int $0x18 +10: .asciz "EB->BIOS\r\n" +print_exit_message: + movw $0x0007, %bx /* page 0, attribute 7 (normal) */ + movb $0x0e, %ah /* write char, tty mode */ +1: lodsb + testb %al, %al + je 2f + int $0x10 + jmp 1b +2: ret + .globl prefix_exit_end +prefix_exit_end: + .previous + +/***************************************************************************** + * Subroutine: print character in %al (with LF -> LF,CR translation) + ***************************************************************************** + */ +print_character: + movw $0x0007, %bx /* page 0, attribute 7 (normal) */ + movb $0x0e, %ah /* write char, tty mode */ + cmpb $0x0a, %al /* '\n'? */ + jne 1f + int $0x10 + movb $0x0d, %al +1: int $0x10 + ret + +/***************************************************************************** + * Subroutine: print a zero-terminated message starting at %si + ***************************************************************************** + */ +print_message: +1: lodsb + testb %al, %al + je 2f + call print_character + jmp 1b +2: ret + +/***************************************************************************** + * Subroutine: print hex word in %ax + ***************************************************************************** + */ +print_hex_word: + mov $4, %cx +1: + push %ax + shr $12, %ax + /* Courtesy of Norbert Juffa <norbert.juffa@amd.com> */ + cmp $10, %al + sbb $0x69, %al + das + call print_character + pop %ax + shl $4, %ax + loop 1b + ret + +/***************************************************************************** + * Subroutine: print segment:offset address in %es:%di + ***************************************************************************** + */ +print_segoff: + push %di + push %es + pop %ax + call print_hex_word + movb $0x3a,%al /* ':' */ + call print_character + pop %ax + call print_hex_word + mov $0x20, %al /* ' ' */ + call print_character + ret + +/***************************************************************************** + * Make a PXE API call. Works with either !PXE or PXENV+ API. + * Opcode in %bx. pxe_parameter_structure always used. + * Returns status code (not exit code) in %bx and prints it. + * ORs status code with overall status code in pxe_overall_status, returns + * with zero flag set iff all PXE API calls have been successful. + ***************************************************************************** + */ +pxe_call: + /* Set up registers for PXENV+ API. %bx already set up */ + push %ds + pop %es + mov $pxe_parameter_structure - _prefix, %di + /* Set up stack for !PXE API */ + pushw %cs + pushw %di + pushw %bx + /* Make the API call */ + lcall *(pxe_entry_segoff - _prefix) + /* Reset the stack */ + add $6, %sp + mov pxe_parameter_structure - _prefix, %ax + push %ax + call print_hex_word + mov $0x20, %ax /* ' ' */ + call print_character + pop %bx + or %bx, pxe_overall_status - _prefix + ret + +/***************************************************************************** + * PXE data structures + ***************************************************************************** + */ + +pxe_overall_status: .word 0 + +pxe_entry_segoff: +pxe_entry_offset: .word 0 +pxe_entry_segment: .word 0 + +undi_code_segoff: +undi_code_size: .word 0 +undi_code_segment: .word 0 + +undi_data_segoff: +undi_data_size: .word 0 +undi_data_segment: .word 0 + +pxe_parameter_structure: + .word 0 + .word 0,0,0,0,0 + +end_of_pxeloader: + + .balign 16, 0 +payload: diff --git a/src/arch/i386/prefix/romprefix.S b/src/arch/i386/prefix/romprefix.S new file mode 100644 index 00000000..c6c83489 --- /dev/null +++ b/src/arch/i386/prefix/romprefix.S @@ -0,0 +1,416 @@ +/* At entry, the processor is in 16 bit real mode and the code is being + * executed from an address it was not linked to. Code must be pic and + * 32 bit sensitive until things are fixed up. + * + * Also be very careful as the stack is at the rear end of the interrupt + * table so using a noticeable amount of stack space is a no-no. + */ + +/* Define DELAYED_INT when NO_DELAYED_INT is not defined. + * This allows positive tests instead of tests that contain + * double negatives, and become confusing. + */ +#ifndef NO_DELAYED_INT +#define DELAYED_INT +#endif + +/* We need some unique magic ID, if we defer startup thru the INT18H or INT19H + * handler. This way, we can check if we have already been installed. + */ +#ifndef MAGIC +#define MAGIC 0xE44C +#endif + +/* Hook into INT18H or INT19H handler */ +#ifdef BOOT_INT18H +#define BOOT_INT 0x18 +#else +#define BOOT_INT 0x19 +#endif + +#define BOOT_INT_VEC BOOT_INT*4 +#define SCRATCHVEC 0x300 + +/* Prefix exit codes. We store these on the stack so that we will + * know how to return control to the BIOS when Etherboot exits. + */ +#define EXIT_VIA_LRET 0x0 +#define EXIT_VIA_INT_18 0x1 +#define EXIT_VIA_BOOT_INT 0x2 + + .text + .code16 + .arch i386 + .org 0 + .section ".prefix", "ax", @progbits + .globl _prefix +_prefix: + .word 0xAA55 /* BIOS extension signature */ +size: .byte 0 /* number of 512 byte blocks */ + /* = number of 256 word blocks */ + /* filled in by makerom program */ + jmp over /* skip over checksum */ + .byte 0 /* checksum */ + jmp legacyentry /* alternate entry point +6 */ + /* used by mknbi-rom */ + +#ifdef PCI_PNP_HEADER +mfgstr: + .asciz "Etherboot" + +#ifdef PXE_EXPORT + .org 0x16 + .word UNDIROMID - _prefix +#endif /* PXE_EXPORT */ + + .org 0x18 + .word PCI - _prefix + .word PnP - _prefix + +PCI: + .ascii "PCIR" + .word 0x0000 /* vendor ID, filled in by makerom */ + .word 0x0000 /* device ID, filled in by makerom */ + .word 0x0000 /* pointer to vital product data */ + .word 0x0018 /* PCI data structure length */ + .byte 0x00 /* PCI data structure revision */ + .byte 0x02 /* Device Base Type code */ + .byte 0x00 /* Device Sub-Type code */ + .byte 0x00 /* Device Interface Type code */ + .word 0x0000 /* Image length same as offset 02h */ + .word 0x0001 /* revision level of code/data */ + .byte 0x00 /* code type */ + .byte 0x80 /* indicator (last PCI data structure) */ + .word 0x0000 /* reserved */ + +PnP: + .ascii "$PnP" + .byte 0x01 /* structure revision */ + .byte 0x02 /* length (in 16 byte increments) */ + .word 0x0000 /* offset of next header */ + .byte 0x00 /* Reserved */ + .byte 0x00 /* checksum filled by makerom */ + .long 0x00000000 /* Device identifier */ + .word mfgstr - _prefix + .word 0x0 /* pointer to product name */ + /* filled by makerom */ + .byte 0x02 /* Device Base Type code */ + .byte 0x00 /* Device Sub-Type code */ + .byte 0x00 /* Device Interface Type code */ + .byte 0x14 /* device indicator */ + .word 0x0000 /* boot connection vector */ + .word 0x0000 /* disconnect vector */ + .word pnpentry - _prefix + .word 0x0000 /* reserved */ + .word 0x0000 /* static resource information vector */ +#ifdef PXE_EXPORT +UNDIROMID: + .ascii "UNDI" + .byte UNDIROMID_end - UNDIROMID /* length of structure */ + .byte 0 /* Checksum */ + .byte 0 /* Structure revision */ + .byte 0,1,2 /* PXE version 2.1.0 */ + .word UNDILoader - _prefix /* Offset to loader routine */ + .word UNDIStackSize /* Stack segment size */ + .word UNDIDataSize /* Data segment size */ + .word UNDICodeSize /* Code segment size */ + .ascii "PCIR" + + /* The code segment contains our pxe_stack_t plus the PXE and + * RM callback interfaces. We don't actually use a data + * segment, but we put a nonzero value here to avoid confusing + * things. 16k of stack space should be enough. + * + * When we claim our own memory, we fill out the data segment + * with the address and size of the real-mode stack, so that + * NBPs will free that area of memory for us. When the UNDI + * loader is used to initialise us, we will never need a + * real-mode stack because we will only ever be called via the + * PXE API, hence our stack is already in base memory. + */ + .equ UNDICodeSize, _pxe_stack_size + .equ UNDIDataSize, _real_mode_stack_size + .equ UNDIStackSize, _real_mode_stack_size +UNDIROMID_end: +#endif /* PXE_EXPORT */ + +#endif /* PCI_PNP_HEADER */ + +/* + * Explicitly specify DI is wrt ES to avoid problems with some BIOSes + * Discovered by Eric Biederman + * In addition, some BIOSes don't point DI to the string $PnP so + * we need another #define to take care of that. + */ +over: +#ifdef DEBUG_ROMPREFIX + call print_bcv +#endif +/* Omit this test for ISA cards anyway */ +#ifdef PCI_PNP_HEADER +/* Accept old name too for backward compatibility */ +#if !defined(BBS_BUT_NOT_PNP_COMPLIANT) && !defined(PNP_BUT_NOT_BBS_COMPLIANT) + cmpw $'$'+'P'*256,%es:0(%di) + jne notpnp + cmpw $'n'+'P'*256,%es:2(%di) + jne notpnp +#endif /* BBS_BUT_NOT_PNP_COMPLIANT */ + movw $0x20,%ax + lret +#endif /* PCI_PNP_HEADER */ +notpnp: +#ifdef DEBUG_ROMPREFIX + call print_notpnp +#endif +#ifdef DELAYED_INT + pushw %ax + pushw %ds + xorw %ax,%ax + movw %ax,%ds /* access first 64kB segment */ + movw SCRATCHVEC+4, %ax /* check if already installed */ + cmpw $MAGIC, %ax /* check magic word */ + jz installed + movw BOOT_INT_VEC, %ax /* hook into INT18H or INT19H */ + movw %ax, SCRATCHVEC + movw BOOT_INT_VEC+2, %ax + movw %ax, SCRATCHVEC+2 + movw $start_int - _prefix, %ax + movw %ax, BOOT_INT_VEC + movw %cs,%ax + movw %ax, BOOT_INT_VEC+2 + movw $MAGIC, %ax /* set magic word */ + movw %ax, SCRATCHVEC+4 +#ifdef DEBUG_ROMPREFIX + call print_installed +#endif +installed: + popw %ds + popw %ax + movw $0x20,%ax + lret + +start_int: /* clobber magic id, so that we will */ +#ifdef DEBUG_ROMPREFIX + call print_start_int +#endif + xorw %ax,%ax /* not inadvertendly end up in an */ + movw %ax,%ds /* endless loop */ + movw %ax, SCRATCHVEC+4 + movw SCRATCHVEC+2, %ax /* restore original INT19h handler */ + movw %ax, BOOT_INT_VEC+2 + movw SCRATCHVEC, %ax + movw %ax, BOOT_INT_VEC + pushl %eax /* padding */ + pushw $EXIT_VIA_BOOT_INT + jmp invoke +#endif /* DELAYED_INT */ + + + + +legacyentry: +#ifdef DEBUG_ROMPREFIX + call print_legacyentry +#endif + pushw $EXIT_VIA_LRET + jmp invoke + + + +#ifdef PCI_PNP_HEADER +pnpentry: +#ifdef DEBUG_ROMPREFIX + call print_bev +#endif + pushl %eax /* padding */ + pushw $EXIT_VIA_INT_18 + jmp invoke +#endif /* PCI_PNP_HEADER */ + + +invoke: + /* Store ROM segment and size on stack */ + pushw %ax + pushw %ds + pushw %cs + movzbw %cs:(size-_prefix), %ax + shlw $9, %ax /* 512-byte blocks */ + pushw %ax + /* Relocate to free base memory, switch stacks */ + pushw $12 /* Preserve exit code & far ret addr */ + call prelocate + /* We are now running in RAM */ + popw %ax /* padding */ + movw %cs, %ax + movw %ax, %ds + popw %ds:(_prefix_rom+2) /* ROM size */ + popw %ds:(_prefix_rom+0) /* ROM segment */ + popw %ds /* Original %ds */ + popw %ax /* Original %ax */ + pushw %ax /* 4-byte alignment */ + pushl $8 /* Preserve exit code & far ret addr */ + pushw $0 /* Set null return address */ + jmp _start + + + .section ".text16", "ax", @progbits + .globl prefix_exit +prefix_exit: + popw %ax /* padding */ + popw %ax /* %ax = exit code */ + cmpw $EXIT_VIA_LRET, %ax + jne 1f + /* Exit via LRET */ + lret +1: addw $4, %sp /* Strip padding */ + cmpw $EXIT_VIA_BOOT_INT, %ax + jne 2f + /* Exit via int BOOT_INT */ + int $BOOT_INT /* Try original vector */ +2: /* Exit via int $0x18 */ + int $0x18 /* As per BIOS Boot Spec, next dev */ + .globl prefix_exit_end +prefix_exit_end: + .previous + + + +#ifdef PXE_EXPORT + +#include "callbacks.h" +#define PXENV_UNDI_LOADER 0x104d + + .section ".prefix" +UNDILoader: + /* Loader API is different to the usual PXE API; there is no + * opcode on the stack. We arrange the stack to look like a + * normal PXE API call; this makes the Etherboot internals + * cleaner and avoids adding an extra API type just for the + * PXE loader. + */ + pushw %bx + movw %sp, %ax /* Store original %ss:sp */ + pushw %ss + pushw %ax + pushl %eax /* Space for loader structure ptr */ + pushw %bp + movw %sp, %bp + movw 16(%bp), %ax /* Copy loader structure ptr */ + movw %ax, 2(%bp) + movw 18(%bp), %ax + movw %ax, 4(%bp) + popw %bp + pushw $PXENV_UNDI_LOADER /* PXE 'opcode' */ + pushl %eax /* dummy return address */ + /* Stack now looks like a normal PXE API call */ + /* Store ROM segment and size on stack */ + pushw %ax + pushw %cs + movzbw %cs:(size-_prefix), %ax + shlw $9, %ax /* 512-byte blocks */ + pushw %ax + /* Unpack Etherboot into temporarily claimed base memory */ + pushw $20 /* Dummy ret, PXE params, orig ss:sp */ + call prelocate + popw %ax /* discard */ + popw %cs:(_prefix_rom+2) /* ROM size */ + popw %cs:(_prefix_rom+0) /* ROM segment */ + popw %ax /* Original %ax */ + /* Inhibit automatic deallocation of base memory */ + movl $0, %cs:_prefix_image_basemem + /* Make PXE API call to Etherboot */ + pushl $0x201 /* PXE API version */ + /* Need to USE_INTERNAL_STACK, since we will call relocate() */ + pushl $(EB_OPCODE_PXE|EB_USE_INTERNAL_STACK) /* PXE API call type */ + call _entry + addw $18, %sp /* discard */ + popw %bx /* Restore original %ss:sp */ + popw %ss + movw %bx, %sp + popw %bx + call deprelocate + lret $2 /* Skip our PXE 'opcode' */ +#endif /* PXE_EXPORT */ + +#ifdef DEBUG_ROMPREFIX + .section ".prefix" + +print_bcv: + pushw %si + movw $1f-_prefix, %si + call print_message + popw %si + ret +1: .asciz "ROM detected\r\n" + +print_bev: + pushw %si + movw $1f-_prefix, %si + call print_message + popw %si + ret +1: .asciz "booting\r\n" + +print_notpnp: + pushw %si + movw $1f-_prefix, %si + call print_message + popw %si + ret +1: .asciz ": Non-PnP BIOS detected!\r\n" + +print_legacyentry: + pushw %si + movw $1f-_prefix, %si + call print_message + popw %si + ret +1: .asciz "ROM using legacy boot mechanism\r\n" + +print_installed: + pushw %si + movw $1f-_prefix, %si + call print_message + popw %si + ret +1: .ascii "hooked boot via INT" +#ifdef BOOT_INT18H + .asciz "18\r\n" +#else + .asciz "19\r\n" +#endif + +print_start_int: + pushw %si + movw $1f-_prefix, %si + call print_message + popw %si + ret +1: .asciz "booting via hooked interrupt\r\n" + +print_message: + pushaw + pushw %ds + pushw %cs + popw %ds + pushw %si + movw $1f-_prefix, %si + call print_string + popw %si + call print_string + popw %ds + popaw + ret +1: .asciz "Etherboot " + +print_string: +1: lodsb + testb %al,%al + je 2f + movw $0x0007, %bx /* page 0, attribute 7 (normal) */ + movb $0x0e, %ah /* write char, tty mode */ + int $0x10 + jmp 1b +2: ret + +#endif diff --git a/src/arch/i386/prefix/unhuf.S b/src/arch/i386/prefix/unhuf.S new file mode 100644 index 00000000..390556e3 --- /dev/null +++ b/src/arch/i386/prefix/unhuf.S @@ -0,0 +1,400 @@ +/***************************************************************************** + * NOTE: This code is no longer used in Etherboot. The obsolete + * Makefile target .lzrom refers to it, but it is no longer being + * maintained and may no longer work. Use .zrom instead (which uses + * the unnrv2b decompressor). + ***************************************************************************** + */ + +/* At entry, the processor is in 16 bit real mode and the code is being + * executed from an address it was not linked to. Code must be pic and + * 32 bit sensitive until things are fixed up. + */ + + +/* LZHuf (LZSS) Decompressing boot loader for ROM images + * + * this code is based on the work of Haruyasu Yoshizaki and Haruhiko Okumura + * who implemented the original compressor and decompressor in C code + * + * Converted to 32bit assembly 16 July 2002 Eric Biederman <ebiederman@lnxi.com> + * Made PIC 10 Aug 2002 Eric Biederman <ebiederman@lnxi.com> + * + * Copyright 1997 by M. Gutschke <gutschk@math.uni-muenster.de> + * + * Compression pays off, as soon as the uncompressed image is bigger than + * about 1.5kB. This assumes an average compressibility of about 60%. + */ + + +/* Do not change these values unless you really know what you are doing + * the pre-computed lookup tables rely on the buffer size being 4kB or + * smaller. The buffer size must be a power of two. The lookahead size has + * to fit into 6 bits. If you change any of these numbers, you will also + * have to adjust the compressor accordingly. + */ +#define BUFSZ 4096 +#define LOOKAHEAD 60 +#define THRESHOLD 2 +#define NCHAR (256+LOOKAHEAD-THRESHOLD) +#define TABLESZ (NCHAR+NCHAR-1) +#define ROOT (TABLESZ-1) + + .text + .arch i386 + .globl _start +_start: + cli + + /* Save the initial register values */ + pushal + + /* + * See where I am running, and compute %ebp + */ + call 1f +1: pop %ebp + subl $1b, %ebp + +/* + * INIT -- initializes all data structures + * ==== + */ + +init: + cld + leal dcodrle(%ebp), %esi /* uncompress run length encoded */ + leal dcode(%ebp), %edi /* lookup table for codes */ + movb $6, %dl + movb $0x20, %dh + xorb %bh,%bh +init0: + lodsb + movb %al,%bl +init1: + xorl %ecx, %ecx + movb %dh,%cl + movb %bh,%al + rep + stosb + incb %bh + decb %bl + jnz init1 + shrb %dh + decb %dl + jnz init0 + movb $1, %bl /* uncompress run length encoded */ + movb $6, %bh /* lookup table for code lengths */ +init2: + lodsb + xorl %ecx, %ecx + movb %al,%cl + movb %bl,%al + rep + stosb + incb %bl + decb %bh + jnz init2 + + movl $NCHAR, %ecx /* set all frequencies of leaf nodes */ + movw $1, %ax /* to one */ + rep + stosw + leal freq(%ebp), %esi + movl $ROOT+1-NCHAR, %ecx +init3: + lodsw /* update frequencies of non-leaf nodes */ + movw %ax,%bx + lodsw + addw %bx,%ax + stosw + loop init3 + movw $0xFFFF, %ax + stosw /* sentinel with infinite frequency */ + movl $NCHAR, %ecx + movw $TABLESZ, %ax +init4: + stosw /* update son pointers for leaf nodes */ + incw %ax + loop init4 + movl $ROOT+1-NCHAR, %ecx + xorw %ax,%ax +init5: + stosw /* update son ptrs for non-leaf nodes */ + addw $2, %ax + loop init5 + movl $ROOT+1-NCHAR, %ecx + movw $NCHAR, %ax +init6: + stosw /* update parent ptrs for non-leaf nd. */ + stosw + incw %ax + loop init6 + movl $NCHAR, %ecx + xorw %ax,%ax + stosw /* root node has no parent */ +init7: + stosw /* update parent ptrs for leaf nodes */ + incw %ax + loop init7 + xorw %ax,%ax + stosb /* clear getlen */ + stosw /* clear getbuf */ + movb $0x20, %al /* fill text buffer with spaces */ + leal spaces(%ebp), %edi + movl $BUFSZ-LOOKAHEAD, %ecx + rep + + stosb + /* fall thru */ + +/* + * MAIN -- reads compressed codes and writes decompressed data + * ==== + */ + + leal _payload(%ebp), %esi /* get length of compressed data stream */ + leal uncompressed(%ebp), %edi + + lodsl + movl %eax, %ecx +main1: + pushl %ecx + call dcdchr /* decode one code symbol */ + orb %ah,%ah /* test if 8bit character */ + jnz main2 + stosb /* store verbatim */ + popl %ecx + loop main1 /* proceed with next compressed code */ + jmp done /* until end of input is detected */ +main2: + pushl %eax + call dcdpos /* compute position in output buffer */ + movl %esi, %eax + subl %edi, %ebx + notl %ebx + movl %ebx, %esi /* si := di - dcdpos() - 1 */ + popl %ecx + subl $255-THRESHOLD, %ecx /* compute length of code sequence */ + movl %ecx, %edx + rep + movsb + movl %eax,%esi + popl %ecx + subl %edx, %ecx /* check end of input condition */ + jnz main1 /* proceed with next compressed code */ +done: + /* Start Etherboot */ + popal + jmp uncompressed +/* + * GETBIT -- gets one bit pointed to by DS:ESI + * ====== + * + * changes: AX,CX,DL + */ + +getbit: + movb $8, %cl + movb getlen(%ebp), %dl /* compute number of bits required */ + subb %dl,%cl /* to fill read buffer */ + jae getbit1 + movw getbuf(%ebp), %ax /* there is still enough read ahead data */ + jmp getbit2 +getbit1: + lodsb /* get next byte from input stream */ + xorb %ah,%ah + shlw %cl,%ax /* shift, so that it will fit into */ + movw getbuf(%ebp), %cx /* read ahead buffer */ + orw %cx,%ax + addb $8, %dl /* update number of bits in buffer */ +getbit2: + movw %ax,%cx + shlw %cx /* extract one bit from buffer */ + movw %cx, getbuf(%ebp) + decb %dl + movb %dl, getlen(%ebp) /* and update number of bits */ + shlw %ax /* return in carry flag */ + ret + + +/* + * DCDPOS -- decodes position in textbuffer as pointed to by DS:SI, result in BX + * ====== + * + * changes: AX,EBX,ECX,DX + */ + +dcdpos: + movl $0x0800, %ebx +dcdpos1: + shlb %bl /* read one byte */ + call getbit + jnc dcdpos2 + incb %bl +dcdpos2: + decb %bh + jnz dcdpos1 + movb %bl,%dh /* read length of code from table */ + xorb %bh,%bh + xorl %ecx, %ecx + movb dlen(%ebx, %ebp),%cl + movb dcode(%ebx, %ebp),%bl /* get top six bits from table */ + shll $6, %ebx +dcdpos3: + pushl %ecx /* read the rest from the input stream */ + shlb %dh + call getbit + jnc dcdpos4 + incb %dh +dcdpos4: + popl %ecx + loop dcdpos3 + andb $0x3f, %dh /* combine upper and lower half of code */ + orb %dh,%bl + ret + +/* + * DCDCHR -- decodes one compressed character pointed to by DS:SI + * ====== + * + * changes: AX,BX,CX,DX + */ + +dcdchr: + movl $ROOT, %ebx /* start at root entry */ + shll %ebx + movzwl son(%ebx, %ebp),%ebx +dcdchr1: + call getbit /* get a single bit */ + jnc dcdchr2 + incl %ebx /* travel left or right */ +dcdchr2: + shll %ebx + movzwl son(%ebx, %ebp), %ebx + cmpl $TABLESZ, %ebx /* until we come to a leaf node */ + jb dcdchr1 + movl %ebx, %eax + subl $TABLESZ, %eax + /* fall thru */ + +/* + * UPDATE -- updates huffman tree after incrementing frequency for code in BX + * ====== + * + * changes: BX,CX,DX + */ + +update: + /* we do not check whether the frequency count has overrun. + * this will cause problems for large files, but be should be fine + * as long as the compressed size does not exceed 32kB and we + * cannot do more than this anyways, because we load into the + * upper 32kB of conventional memory + */ + pushl %esi + pushl %eax + shll %ebx + movzwl parent(%ebx, %ebp),%ebx +update1: + shll %ebx + movzwl freq(%ebx, %ebp), %edx + incl %edx /* increment frequency count by one */ + movw %dx, freq(%ebx, %ebp) + leal 2+freq(%ebx, %ebp), %esi + lodsw /* check if nodes need reordering */ + cmpw %ax, %dx + jbe update5 +update2: + lodsw + cmpw %dx, %ax + jb update2 + movzwl -4(%esi), %ecx + movw %cx, freq(%ebx, %ebp) /* swap frequency of entries */ + movw %dx, -4(%esi) + + movl %esi, %eax /* compute index of new entry */ + subl $freq+4, %eax + subl %ebp, %eax + + movl %eax, %edx + shrl %eax + movzwl son(%ebx, %ebp), %ecx /* get son of old entry */ + movl %ecx, %esi + addl %esi, %esi + movw %ax, parent(%esi, %ebp) /* and update the ptr to new parent */ + cmpl $TABLESZ, %ecx + jae update3 /* do this for both branches */ + movw %ax, parent+2(%esi, %ebp) /* if not a leaf node */ +update3: + movl %edx, %esi + movzwl son(%esi, %ebp), %edx /* get son of new entry */ + movw %cx, son(%esi, %ebp) /* update its contents */ + movl %edx, %esi + addl %esi, %esi + movl %ebx, %ecx + shrl %ecx + movw %cx, parent(%esi, %ebp) /* and update the ptr to new paren */ + cmpl $TABLESZ, %edx + jae update4 /* do this for both branches */ + movw %cx, parent+2(%esi, %ebp) /* if not a leaf node */ +update4: + movw %dx, son(%ebx, %ebp) /* update son of old entry */ + movl %eax, %ebx /* continue with new entry */ + shll %ebx +update5: + movzwl parent(%ebx, %ebp), %ebx /* continue with parent */ + orl %ebx, %ebx + jnz update1 /* until we found the root entry */ + popl %eax + popl %esi + ret + +/* + * constant data. this part of the program resides in ROM and cannot be + * changed + * + * run length encoded tables will be uncompressed into the bss segment + * take care with any symbols here for .com files to add 0x100 to address + */ + +dcodrle: .byte 0x01,0x03,0x08,0x0C,0x18,0x10 +dlenrle: .byte 0x20,0x30,0x40,0x30,0x30,0x10 + +/* + * variable data segment (bss) + * this segment will always be found at 0x90000 (i.e. at RELOC - SCRATCH) + * + * do not change the order or the sizes of any of the following tables + * the initialization code makes assumptions on the exact layout of the + * data structures... + */ + +.bss +/* lookup table for index into buffer of recently output characters */ +dcode: .skip 256 + +/* lookup table for length of code sequence from buffer of recent characters */ +dlen: .skip 256 + +/* table with frequency counts for all codes */ +freq: .skip 2*(TABLESZ+1) + +/* pointer to child nodes */ +son: .skip 2*(TABLESZ) + +/* the first part of this table contains all the codes (0..TABLESZ-1) */ +/* the second part contains all leaf nodes (TABLESZ..) */ +parent: .skip 2*(TABLESZ+NCHAR) + +/* temporary storage for extracting bits from compressed data stream */ +getlen: .skip 1 +getbuf: .skip 1 + + /* the initial buffer has to be filled with spaces */ + .balign 4 +spaces: + .skip BUFSZ - LOOKAHEAD + /* uncompressed data will be written here */ +uncompressed: + diff --git a/src/arch/i386/prefix/unhuf.lds b/src/arch/i386/prefix/unhuf.lds new file mode 100644 index 00000000..00d6b55b --- /dev/null +++ b/src/arch/i386/prefix/unhuf.lds @@ -0,0 +1,33 @@ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) + +SECTIONS +{ + . = 0; + .text : { + _text = .; + *(.head) + *(.text) + } = 0x9090 + .rodata : { + *(.rodata) + } + _etext = . ; + .data : { + *(.data) + /* Force 4 byte alignment */ + . = ALIGN(4); + _payload = . ; + *(.huf) + _epayload = . ; + } + _edata = . ; + _data_size = _edata - _start; + /* Etherboot needs to be 16 byte aligned */ + . = ALIGN(16); + .bss : { + *(.bss) + } + _end = . ; + _image_size = _end - _start; +} diff --git a/src/arch/i386/prefix/unnrv2b.S b/src/arch/i386/prefix/unnrv2b.S new file mode 100644 index 00000000..1836fa71 --- /dev/null +++ b/src/arch/i386/prefix/unnrv2b.S @@ -0,0 +1,129 @@ +/* + * Copyright (C) 1996-2002 Markus Franz Xaver Johannes Oberhumer + * + * This file 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. + * + * Originally this code was part of ucl the data compression library + * for upx the ``Ultimate Packer of eXecutables''. + * + * - Converted to gas assembly, and refitted to work with etherboot. + * Eric Biederman 20 Aug 2002 + * + * - Structure modified to be a subroutine call rather than an + * executable prefix. + * Michael Brown 30 Mar 2004 + */ + + + .text + .arch i386 + .section ".prefix", "ax", @progbits + .code32 + + .globl decompress +decompress: + /* Save the initial register values */ + pushal + + /* + * See where I am running, and compute %ebp + * %ebp holds delta between physical and virtual addresses. + */ + call 1f +1: popl %ebp + subl $1b, %ebp + + /* "compressed" and "decompress_to" defined by linker script */ + /* move compressed image up to temporary area before decompressing */ + std + movl $_compressed_size, %ecx + leal _compressed+4-1(%ebp, %ecx), %esi + leal _compressed_copy-1(%ebp, %ecx), %edi + rep movsb + /* Setup to run the decompressor */ + cld + leal _compressed_copy(%ebp), %esi + leal decompress_to(%ebp), %edi + movl $-1, %ebp /* last_m_off = -1 */ + jmp dcl1_n2b + +/* ------------- DECOMPRESSION ------------- + + Input: + %esi - source + %edi - dest + %ebp - -1 + cld + + Output: + %eax - 0 + %ecx - 0 +*/ + +.macro getbit bits +.if \bits == 1 + addl %ebx, %ebx + jnz 1f +.endif + movl (%esi), %ebx + subl $-4, %esi /* sets carry flag */ + adcl %ebx, %ebx +1: +.endm + +decompr_literals_n2b: + movsb + +decompr_loop_n2b: + addl %ebx, %ebx + jnz dcl2_n2b +dcl1_n2b: + getbit 32 +dcl2_n2b: + jc decompr_literals_n2b + xorl %eax, %eax + incl %eax /* m_off = 1 */ +loop1_n2b: + getbit 1 + adcl %eax, %eax /* m_off = m_off*2 + getbit() */ + getbit 1 + jnc loop1_n2b /* while(!getbit()) */ + xorl %ecx, %ecx + subl $3, %eax + jb decompr_ebpeax_n2b /* if (m_off == 2) goto decompr_ebpeax_n2b ? */ + shll $8, %eax + movb (%esi), %al /* m_off = (m_off - 3)*256 + src[ilen++] */ + incl %esi + xorl $-1, %eax + jz decompr_end_n2b /* if (m_off == 0xffffffff) goto decomp_end_n2b */ + movl %eax, %ebp /* last_m_off = m_off ?*/ +decompr_ebpeax_n2b: + getbit 1 + adcl %ecx, %ecx /* m_len = getbit() */ + getbit 1 + adcl %ecx, %ecx /* m_len = m_len*2 + getbit()) */ + jnz decompr_got_mlen_n2b /* if (m_len == 0) goto decompr_got_mlen_n2b */ + incl %ecx /* m_len++ */ +loop2_n2b: + getbit 1 + adcl %ecx, %ecx /* m_len = m_len*2 + getbit() */ + getbit 1 + jnc loop2_n2b /* while(!getbit()) */ + incl %ecx + incl %ecx /* m_len += 2 */ +decompr_got_mlen_n2b: + cmpl $-0xd00, %ebp + adcl $1, %ecx /* m_len = m_len + 1 + (last_m_off > 0xd00) */ + pushl %esi + leal (%edi,%ebp), %esi /* m_pos = dst + olen + -m_off */ + rep + movsb /* dst[olen++] = *m_pos++ while(m_len > 0) */ + popl %esi + jmp decompr_loop_n2b +decompr_end_n2b: + /* Restore the initial register values */ + popal + ret diff --git a/src/arch/ia64/Config b/src/arch/ia64/Config new file mode 100644 index 00000000..6b7405f1 --- /dev/null +++ b/src/arch/ia64/Config @@ -0,0 +1,22 @@ +# Config for ia64 Etherboot +# +# Do not delete the tag OptionDescription and /OptionDescription +# It is used to automatically generate the documentation. +# +# @OptionDescrition@ +# +# BIOS interface options: +# +# -DCONFIG_EFI +# Compile in support for EFI +# +# @/OptionDescription@ + +CFLAGS+= -DCONSOLE_FIRMWARE +CFLAGS+= -DCONFIG_EFI + +CFLAGS+= -fpic -mconstant-gp -mauto-pic +ASFLAGS+= -mconstant-gp -mauto-pic + +LDFLAGS+= -static -shared -Bsymbolic --warn-multiple-gp --warn-common + diff --git a/src/arch/ia64/Makefile b/src/arch/ia64/Makefile new file mode 100644 index 00000000..42e0b47b --- /dev/null +++ b/src/arch/ia64/Makefile @@ -0,0 +1,125 @@ +ARCH_FORMAT= elf64-ia64-little + +LCONFIG+= + + +BUILD_EFIS= $(patsubst %.img, %.efi, $(IMGS)) $(patsubst %.img, %.zefi, $(IMGS)) + +START= $(BIN)/start.o $(BIN)/reloc.o +#START+= $(BIN)/efi_main.o + +SRCS+= arch/ia64/prefix/efi_prefix.S arch/ia64/prefix/unnrv2b.S +SRCS+= arch/ia64/core/__call.S +SRCS+= arch/ia64/core/ia64_timer.c +SRCS+= arch/ia64/core/idiv32.S +SRCS+= arch/ia64/core/idiv64.S +SRCS+= arch/ia64/core/longjmp.S +SRCS+= arch/ia64/core/memmove.S +SRCS+= arch/ia64/core/memset.S +SRCS+= arch/ia64/core/pal.c +SRCS+= arch/ia64/core/pci_io.c +SRCS+= arch/ia64/core/reloc.S +SRCS+= arch/ia64/core/relocate_to.S +SRCS+= arch/ia64/core/sal.c +SRCS+= arch/ia64/core/setjmp.S +SRCS+= arch/ia64/core/start.S +SRCS+= arch/ia64/core/efi.c + +ROMLIMIT:=3276800 + +include $(BIN)/Roms + +# We need allefis because $(IMGS) is not defined until +# the Makefile fragment "Roms" is read. +allefis: $(BUILD_EFIS) + + +#BOBJS+= $(BIN)/acpi.o +BOBJS+= $(BIN)/sal.o $(BIN)/pal.o +BOBJS+= $(BIN)/efi.o +BOBJS+= $(BIN)/memset.o $(BIN)/memmove.o +BOBJS+= $(BIN)/setjmp.o $(BIN)/longjmp.o +BOBJS+= $(BIN)/relocate_to.o $(BIN)/__call.o +BOBJS+= $(BIN)/pci_io.o $(BIN)/ia64_timer.o +BOBJS+= $(BIN)/__divdi3.o $(BIN)/__udivdi3.o $(BIN)/__moddi3.o $(BIN)/__umoddi3.o +BOBJS+= $(BIN)/__divsi3.o $(BIN)/__udivsi3.o $(BIN)/__modsi3.o $(BIN)/__umodsi3.o + + +# IA64 Division routines +$(BIN)/__divdi3.o: arch/ia64/core/idiv64.S + $(CPP) $(CFLAGS) -DASSEMBLY $< | $(AS) $(ASFLAGS) -o $@ + +$(BIN)/__udivdi3.o: arch/ia64/core/idiv64.S + $(CPP) $(CFLAGS) -DASSEMBLY -DUNSIGNED $< | $(AS) $(ASFLAGS) -o $@ + +$(BIN)/__moddi3.o: arch/ia64/core/idiv64.S + $(CPP) $(CFLAGS) -DASSEMBLY -DMODULO $< | $(AS) $(ASFLAGS) -o $@ + +$(BIN)/__umoddi3.o: arch/ia64/core/idiv64.S + $(CPP) $(CFLAGS) -DASSEMBLY -DUNSIGNED -DMODULO $< | $(AS) $(ASFLAGS) -o $@ + +$(BIN)/__divsi3.o: arch/ia64/core/idiv32.S + $(CPP) $(CFLAGS) -DASSEMBLY $< | $(AS) $(ASFLAGS) -o $@ + +$(BIN)/__udivsi3.o: arch/ia64/core/idiv32.S + $(CPP) $(CFLAGS) -DASSEMBLY -DUNSIGNED $< | $(AS) $(ASFLAGS) -o $@ + +$(BIN)/__modsi3.o: arch/ia64/core/idiv32.S + $(CPP) $(CFLAGS) -DASSEMBLY -DMODULO $< | $(AS) $(ASFLAGS) -o $@ + +$(BIN)/__umodsi3.o: arch/ia64/core/idiv32.S + $(CPP) $(CFLAGS) -DASSEMBLY -DUNSIGNED -DMODULO $< | $(AS) $(ASFLAGS) -o $@ + + + +# Utilities +$(BIN)/nrv2b: util/nrv2b.c + $(HOST_CC) -O2 -DENCODE -DDECODE -DMAIN -DVERBOSE -DNDEBUG -DBITSIZE=64 -DENDIAN=0 -o $@ $< + +# Pattern Rules + +# General for compiling assembly source files +$(BIN)/%.o: arch/ia64/core/%.c $(MAKEDEPS) + $(CC) $(CFLAGS) -o $@ -c $< + +$(BIN)/%.o: arch/ia64/prefix/%.S $(MAKEDEPS) + $(CPP) $(CFLAGS) -DASSEMBLY $< | $(AS) $(ASFLAGS) -o $@ + +$(BIN)/%.o: arch/ia64/core/%.S $(MAKEDEPS) + $(CPP) $(CFLAGS) -DASSEMBLY $< | $(AS) $(ASFLAGS) -o $@ + +$(BIN)/%.o: $(BIN)/%.s + $(AS) $(ASFLAGS) -o $@ $< + +# General rules for bootable images + +# Rules for nrv2b compressed images +$(BIN)/unnrv2b.tmp: $(BIN)/unnrv2b.o arch/ia64/prefix/unnrv2b.lds $(MAKEDEPS) + $(LD) -T arch/ia64/prefix/unnrv2b.lds $< -o $@ + +$(BIN)/unnrv2b: $(BIN)/unnrv2b.tmp $(MAKEDEPS) + $(OBJCOPY) -O binary $< $@ + +$(BIN)/%.zimg: $(BIN)/%.z $(BIN)/unnrv2b arch/ia64/prefix/apply_unnrv2b_prefix.pl $(MAKEDEPS) + $(PERL) arch/ia64/prefix/apply_unnrv2b_prefix.pl $(BIN)/unnrv2b $< > $@ + +# Placeholder; add no extra symbols to %.sym +$(BIN)/%.zsym: $(BIN)/%.sym $(MAKEDEPS) + cp -f $< $@ + +# rules to generate efi loadable image +SUFFIXES += efi zefi +$(BIN)/efi_prefix.tmp: $(BIN)/efi_prefix.o arch/ia64/prefix/efi_prefix.lds $(MAKEDEPS) + $(LD) -T arch/ia64/prefix/efi_prefix.lds $< -o $@ + +$(BIN)/efi_prefix: $(BIN)/efi_prefix.tmp $(MAKEDEPS) + $(OBJCOPY) -O binary $< $@ + +$(BIN)/%.efi: $(BIN)/%.img $(BIN)/%.tmp $(BIN)/efi_prefix arch/ia64/prefix/apply_efi_prefix.pl $(MAKEDEPS) + @$(SIZE) $(BIN)/$(*).tmp | (read l1; read d1 d2 bss rest ; echo $$bss ) + $(PERL) arch/ia64/prefix/apply_efi_prefix.pl $(BIN)/efi_prefix $< `$(SIZE) $(BIN)/$(*).tmp | (read l1; read d1 d2 bss rest ; echo $$bss )` > $@ + +$(BIN)/%.zefi: $(BIN)/%.zimg $(BIN)/%.tmp $(BIN)/efi_prefix arch/ia64/prefix/apply_efi_prefix.pl $(MAKEDEPS) + @$(SIZE) $(BIN)/$(*).tmp | (read l1; read d1 d2 d3 size rest ; echo $$size ) + $(PERL) arch/ia64/prefix/apply_efi_prefix.pl $(BIN)/efi_prefix $< `$(SIZE) $(BIN)/$(*).tmp | (read l1; read d1 d2 d3 size rest ; echo $$size )` > $@ + diff --git a/src/arch/ia64/core/__call.S b/src/arch/ia64/core/__call.S new file mode 100644 index 00000000..a11b8264 --- /dev/null +++ b/src/arch/ia64/core/__call.S @@ -0,0 +1,68 @@ + /* Trampoline for calling outside of etherboot */ + + .text + .globl __call + .proc __call +__call: + alloc loc0=ar.pfs,8,3,8,0 /* in, local, out, rotating */ + mov loc1=rp + mov loc2=gp + ld8 r14=[in0],8 + ;; + ld8 gp=[in0] + mov r28=in1 /* So we can use stacked pal calling conventions */ + mov out0=in1 + mov out1=in2 + mov out2=in3 + mov out3=in4 + mov out4=in5 + mov out5=in6 + mov out6=in7 + mov out7=0 /* So we can work with sal calling conventions */ + + mov b6=r14 + ;; + br.call.sptk.few rp=b6 + ;; + rsm psr.i /* disable interrupts */ + ;; + mov gp=loc2 + mov rp=loc1 + ;; + mov ar.pfs=loc0 + br.ret.sptk.many rp + + .size __call, . - __call + .endp __call + + + .text + .globl pal_call + .proc pal_call +pal_call: + alloc loc0 = ar.pfs,4,3,0,0 /* in, local, out, rotating */ + mov loc1 = rp + mov loc2 = gp + add r8 = @gprel(pal_entry),gp + add r9 = @gprel(pal_ret),gp + ;; + ld8 r14 = [r8] + ;; + mov r28 = in0 + mov r29 = in1 + mov r30 = in2 + mov r31 = in3 + mov b6 = r14 + mov rp = r9 + rsm psr.i /* disable interrupts */ + ;; + br.sptk.few b6 + ;; +pal_ret: + rsm psr.i /* disable interrupts */ + ;; + mov gp=loc2 + mov rp=loc1 + ;; + mov ar.pfs=loc0 + br.ret.sptk.many rp diff --git a/src/arch/ia64/core/efi.c b/src/arch/ia64/core/efi.c new file mode 100644 index 00000000..97e585fc --- /dev/null +++ b/src/arch/ia64/core/efi.c @@ -0,0 +1,1026 @@ +#include "efi/efi.h" +#include "etherboot.h" +#include "elf.h" +#include "sal.h" +#include "pal.h" + +#warning "Place a declaration of lookup_efi_nic somewhere useful" +EFI_NETWORK_INTERFACE_IDENTIFIER_INTERFACE *lookup_efi_nic(int index); + +#warning "Place the declaraction of __call someplace more appropriate\n" +extern EFI_STATUS __call(void *,...); + +/* Keep 16M free in case EFI needs to allocate some memory. + * In the worst case this is only 1/8 the memory on an Itanium. + */ +#define EFI_RESERVE_LOW_PAGES ((8*1024*1024)/EFI_PAGE_SIZE) +#define EFI_RESERVE_HIGH_PAGES ((8*1024*1024)/EFI_PAGE_SIZE) + +struct console_info { + uint16_t num_cols; + uint16_t num_rows; + uint16_t orig_x; + uint16_t orig_y; +}; + +struct efi_mem_map { + uint64_t map_size; + uint64_t map_key; + uint64_t descriptor_size; + uint32_t descriptor_version; + uint32_t pad; + EFI_MEMORY_DESCRIPTOR map[64]; +}; + +struct efi_info { + int flags; +#define READ_SYSTAB 1 +#define READ_FPSWA 2 +#define READ_MEMMAP 4 +#define READ_CONINFO 8 + EFI_SYSTEM_TABLE *systab; + void *fpswa; + struct efi_mem_map mem_map; + struct console_info coninfo; +}; + + +unsigned long io_base; + +/* local globals */ +static struct efi_info efi_info; +static EFI_HANDLE etherboot_handle; +static EFI_BOOT_SERVICES *boot_services; +static SIMPLE_TEXT_OUTPUT_INTERFACE *conout; +static SIMPLE_INPUT_INTERFACE *conin; +static void *mps_table; +static void *acpi20_table; +static void *smbios_table; +static void *nii_table; + +/* local functions */ + +static EFI_STATUS efi_locate_handle( + EFI_LOCATE_SEARCH_TYPE search_type, + EFI_GUID *protocol, void *search_key, + UINTN *buffer_size, EFI_HANDLE *buffer) +{ + if (!boot_services) + return EFI_NOT_FOUND; + return __call(boot_services->LocateHandle, + search_type, protocol, search_key, buffer_size, buffer); +} + +static EFI_STATUS efi_handle_protocol(EFI_HANDLE handle, EFI_GUID *protocol, void **interface) +{ + if (!boot_services) + return EFI_UNSUPPORTED; + return __call(boot_services->HandleProtocol, handle, protocol, interface); +} + +static EFI_STATUS efi_locate_device_path(EFI_GUID *protocol, EFI_DEVICE_PATH **device_path, + EFI_HANDLE *device) +{ + if (!boot_services) + return EFI_NOT_FOUND; + return __call(boot_services->LocateDevicePath, protocol, device_path, device); +} + +static EFI_STATUS efi_allocate_pages(EFI_ALLOCATE_TYPE type, EFI_MEMORY_TYPE memory_type, + UINTN pages, EFI_PHYSICAL_ADDRESS *memory) +{ + if (!boot_services) + return EFI_OUT_OF_RESOURCES; + return __call(boot_services->AllocatePages, + type, memory_type, pages, memory); +} + +static EFI_STATUS efi_free_pages(EFI_PHYSICAL_ADDRESS memory, UINTN pages) +{ + if(pages == 0) + return EFI_SUCCESS; + if (!boot_services) + return EFI_INVALID_PARAMETER; + return __call(boot_services->FreePages, memory, pages); +} + +static EFI_STATUS efi_get_memory_map(UINTN *map_size, EFI_MEMORY_DESCRIPTOR *map, + UINTN *map_key, UINTN *descriptor_size, UINT32 *descriptor_version) +{ + if (!boot_services) + return EFI_INVALID_PARAMETER; + return __call(boot_services->GetMemoryMap, + map_size, map, map_key, descriptor_size, descriptor_version); +} + +static EFI_STATUS efi_free_pool(void *buffer) +{ + if (!boot_services) + return EFI_INVALID_PARAMETER; + return __call(boot_services->FreePool, buffer); +} +static EFI_STATUS efi_stall(UINTN microseconds) +{ + if (!boot_services) + return EFI_UNSUPPORTED; + return __call(boot_services->Stall, microseconds); +} + +static EFI_STATUS efi_set_watchdog_timer( + UINTN timeout, UINT64 watchdog_code, UINTN data_size, CHAR16 *watchdog_data) +{ + if (!boot_services) + return EFI_UNSUPPORTED; + return __call(boot_services->SetWatchdogTimer, + timeout, watchdog_code, data_size, watchdog_data); +} + + +static void efi_exit_boot_services(struct efi_mem_map *map) +{ + EFI_STATUS status; + if (!boot_services) + return; + status = __call(boot_services->ExitBootServices, + etherboot_handle, map->map_key); + if (status != EFI_SUCCESS) { + printf("ExitBootServices failed: %lx\n", status); + } + conout = 0; + conin = 0; + boot_services = 0; +} + +static void efi_free_memory(struct efi_mem_map *map) +{ + EFI_MEMORY_DESCRIPTOR *desc, *tail; +#define next_desc(desc, size) ((EFI_MEMORY_DESCRIPTOR *)(((char *)(desc)) + (size))) + tail = next_desc(map->map, map->map_size); + for(desc = map->map; desc < tail; desc = next_desc(desc, map->descriptor_size)) { + EFI_STATUS status; + EFI_PHYSICAL_ADDRESS start, end; + UINTN pages; + int may_free; + + start = desc->PhysicalStart; + pages = desc->NumberOfPages; + end = start + pages * EFI_PAGE_SIZE; + + + may_free = 0; + /* The only canidates are Loader Code and Data */ + if ((desc->Type == EfiLoaderData) || + (desc->Type == EfiLoaderCode)) + may_free = 1; + + /* Don't free anything etherboot lives in */ + if ((may_free) && + (start < virt_to_phys(_end)) && + (end > virt_to_phys(_text))) + may_free = 0; + + /* Continue if it is not memory we want to free */ + if (!may_free) + continue; + + status = efi_free_pages(start, pages); + if (status != EFI_SUCCESS) { + printf("free_pages: %lx\n", status); + } + } +#undef next_desc +} + +static void read_efi_mem_map(struct efi_mem_map *map) +{ + EFI_STATUS status; + map->map_size = sizeof(map->map); + status = efi_get_memory_map( + &map->map_size, map->map, &map->map_key, + &map->descriptor_size, &map->descriptor_version); + if (status != EFI_SUCCESS) { + printf("read_efi_mem_map failed: %lx\n", status); + map->map_size = 0; + } + /* map->descriptor_size should only grow larger */ + /* map->descriptor_version should only increase and retain + * a backward compatible format. + */ +} + +#if 0 +static const char *efi_mem_type_name(uint32_t type) +{ + const char *type_name; + if (type == EfiReservedMemoryType) + type_name = "EfiReservedMemoryType "; + else if (type == EfiLoaderCode) + type_name = "EfiLoaderCode "; + else if (type == EfiLoaderData) + type_name = "EfiLoaderData "; + else if (type == EfiBootServicesCode) + type_name = "EfiBootServicesCode "; + else if (type == EfiBootServicesData) + type_name = "EfiBootServicesData "; + else if (type == EfiRuntimeServicesCode) + type_name = "EfiRuntimeServicesCode "; + else if (type == EfiRuntimeServicesData) + type_name = "EfiRuntimeServicesData "; + else if (type == EfiConventionalMemory) + type_name = "EfiConventionalMemory "; + else if (type == EfiUnusableMemory) + type_name = "EfiUnusableMemory "; + else if (type == EfiACPIReclaimMemory) + type_name = "EfiACPIReclaimMemory "; + else if (type == EfiACPIMemoryNVS) + type_name = "EfiACPIMemoryNVS "; + else if (type == EfiMemoryMappedIO) + type_name = "EfiMemoryMappedIO "; + else if (type == EfiMemoryMappedIOPortSpace) + type_name = "EfiMemoryMappedIOPortSpace"; + else if (type == EfiPalCode) + type_name = "EfiPalCode "; + else + type_name = "???? "; + return type_name; +} + +static void print_efi_mem_map(struct efi_mem_map *map) +{ + EFI_MEMORY_DESCRIPTOR *desc, *end; +#define next_desc(desc, size) ((EFI_MEMORY_DESCRIPTOR *)(((char *)(desc)) + (size))) + end = next_desc(map->map, map->map_size); + for(desc = map->map; desc < end ; desc = next_desc(desc, map->descriptor_size)) { + const char *mem_type; + unsigned long start, end, virt, virt_end; + uint64_t attr; + mem_type = efi_mem_type_name(desc->Type); + start = desc->PhysicalStart; + end = start + desc->NumberOfPages*EFI_PAGE_SIZE; + virt = desc->VirtualStart; + virt_end = virt + desc->NumberOfPages*EFI_PAGE_SIZE; + attr = desc->Attribute; + printf( "mem: %hhx %s @ %#lx-%#lx", + desc->Type, mem_type, start, end); + if (attr & EFI_MEMORY_UC) + printf("UC "); + if (attr & EFI_MEMORY_WC) + printf("WC "); + if (attr & EFI_MEMORY_WT) + printf("WT "); + if (attr & EFI_MEMORY_WB) + printf("WB "); + if (attr & EFI_MEMORY_UCE) + printf("UCE "); + + if (attr & EFI_MEMORY_WP) + printf("WP "); + if (attr & EFI_MEMORY_RP) + printf("RP "); + if (attr & EFI_MEMORY_XP) + printf("XP "); + + if (attr & EFI_MEMORY_RUNTIME) + printf("RUNTIME "); + + printf("\n"); + } +#undef next_desc +} +#endif + +static void efi_allocate_memory(struct efi_mem_map *map) +{ + EFI_MEMORY_DESCRIPTOR *desc, *end; + unsigned long low_free, high_free; + +#define next_desc(desc, size) ((EFI_MEMORY_DESCRIPTOR *)(((char *)(desc)) + (size))) + end = next_desc(map->map, map->map_size); + /* Find out how much memory is free */ + low_free = high_free = 0; + for(desc = map->map; desc < end ; desc = next_desc(desc, map->descriptor_size)) { + unsigned long start, middle, end; + if (desc->Type != EfiConventionalMemory) + continue; + start = desc->PhysicalStart; + end = desc->PhysicalStart + (desc->NumberOfPages*EFI_PAGE_SIZE); + if (start < 0x100000000UL) { + if (end > 0x100000000UL) { + middle = 0x10000000UL; + } else { + middle = end; + } + } else { + middle = start; + } + + low_free += (middle - start)/EFI_PAGE_SIZE; + high_free += (end - middle)/EFI_PAGE_SIZE; + } + /* O.k. Now allocate all of the conventional memory, reserving only a tiny + * fraction for efi. + */ + for(desc = map->map; desc < end ; desc = next_desc(desc, map->descriptor_size)) { + EFI_STATUS status; + EFI_PHYSICAL_ADDRESS address; + UINTN pages; + unsigned long start, middle, end; + unsigned long low_pages, high_pages; + if (desc->Type != EfiConventionalMemory) + continue; + start = desc->PhysicalStart; + end = desc->PhysicalStart + (desc->NumberOfPages*EFI_PAGE_SIZE); + if (start < 0x100000000UL) { + if (end > 0x100000000UL) { + middle = 0x10000000UL; + } else { + middle = end; + } + } else { + middle = start; + } + low_pages = (middle - start)/EFI_PAGE_SIZE; + high_pages = (end - middle)/EFI_PAGE_SIZE; + if (low_pages && (low_free > EFI_RESERVE_LOW_PAGES)) { + address = start; + pages = low_pages; + if ((low_free - pages) < EFI_RESERVE_LOW_PAGES) { + pages = low_free - EFI_RESERVE_LOW_PAGES; + } + status = efi_allocate_pages( + AllocateAddress, EfiLoaderData, pages, &address); + if (status != EFI_SUCCESS) { + printf("allocate_pages @%lx for %ld pages failed: %ld\n", + desc->PhysicalStart, pages, status); + } + low_free -= pages; + } + if (high_pages && (high_free > EFI_RESERVE_HIGH_PAGES)) { + address = middle; + pages = high_pages; + if ((high_free - pages) < EFI_RESERVE_HIGH_PAGES) { + pages = high_free - EFI_RESERVE_HIGH_PAGES; + } + status = efi_allocate_pages( + AllocateAddress, EfiLoaderData, pages, &address); + if (status != EFI_SUCCESS) { + printf("allocate_pages @%lx for %ld pages failed: %ld\n", + desc->PhysicalStart, pages, status); + } + high_free -= pages; + } + } +#undef next_desc +} + +static void set_io_base(struct efi_mem_map *map) +{ + EFI_MEMORY_DESCRIPTOR *desc, *end; + + io_base = ia64_get_kr0(); /* Default to ar.kr0 */ + +#define next_desc(desc, size) ((EFI_MEMORY_DESCRIPTOR *)(((char *)(desc)) + (size))) + end = next_desc(map->map, map->map_size); + + for(desc = map->map; desc < end ; desc = next_desc(desc, map->descriptor_size)) { + if (desc->Type == EfiMemoryMappedIOPortSpace) { + io_base = desc->PhysicalStart; + break; + } + } +#undef next_desc +} + +#define MAX_EFI_DEVICES 32 +static void efi_stop_nics(void) +{ + static EFI_GUID simple_net_protocol = EFI_SIMPLE_NETWORK_PROTOCOL; + EFI_SIMPLE_NETWORK *simple; + EFI_STATUS status; + EFI_HANDLE handles[MAX_EFI_DEVICES]; + EFI_HANDLE handle; + UINTN devices; + unsigned i; + + if (!boot_services) + return; + + devices = sizeof(handles); + status = efi_locate_handle( + ByProtocol, &simple_net_protocol, 0, &devices, handles); + if (status != EFI_SUCCESS) + return; + devices /= sizeof(handles[0]); + for(i = 0; i < devices; i++) { + void *that; + handle = handles[i]; + status = efi_handle_protocol(handle, &simple_net_protocol, &that); + if (status != EFI_SUCCESS) + continue; + simple = that; + if ((simple->Mode->State == EfiSimpleNetworkInitialized)) { + status = __call(simple->Shutdown, simple); + status = __call(simple->Stop, simple); + } + else if (simple->Mode->State == EfiSimpleNetworkStarted) { + status = __call(simple->Stop, simple); + } + } +} + +static void efi_get_coninfo(struct console_info *info) +{ + EFI_STATUS status; + UINTN cols, rows; + + /* Initialize with some silly safe values */ + info->num_cols = 80; + info->num_rows = 24; + info->orig_x = 0; + info->orig_y = 0; + + status = EFI_UNSUPPORTED; + if (conout) { + status = __call(conout->QueryMode, conout, conout->Mode->Mode, &cols, &rows); + if (status) { + printf("QueryMode Failed cannout get console parameters: %ld\n", status); + } else { + info->num_cols = cols; + info->num_rows = rows; + info->orig_x = conout->Mode->CursorColumn; + info->orig_y = conout->Mode->CursorRow; + } + } +} + +static void *efi_get_fpswa(void) +{ + static EFI_GUID fpswa_protocol = FPSWA_PROTOCOL; + EFI_STATUS status; + EFI_HANDLE fpswa_handle; + UINTN devices; + void *result; + + /* The FPSWA is the Floating Point Software Assist driver, + * to some extent it makes sense but it has one large flaw. + * It fails to install an EFI Configuration table, so the + * OS does not need assistance from the bootloader to find it. + */ + devices = sizeof(fpswa_handle); + status = efi_locate_handle( + ByProtocol, &fpswa_protocol, 0, &devices, &fpswa_handle); + if (status != EFI_SUCCESS) + return 0; + + status = efi_handle_protocol( + fpswa_handle, &fpswa_protocol, &result); + if (status != EFI_SUCCESS) + return 0; + + return result; +} + + +/* Exported functions */ + + +void arch_main(in_call_data_t *data, va_list params) +{ + EFI_STATUS status; + unsigned char *note, *end; + + /* IA64 doesn't have an in_call() implementation; start.S + * passes in this parameter directly on the stack instead of + * as part of the in_call_data_t structure or the parameter + * list. params is unusable: don't attempt to access it. + */ + struct Elf_Bhdr *ptr = (struct Elf_Bhdr *)data; + + memset(&efi_info, 0, sizeof(efi_info)); + note = ((char *)bhdr) + sizeof(*bhdr); + end = ((char *)bhdr) + bhdr->b_size; + if (bhdr->b_signature != 0x0E1FB007) { + printf("Bad bhdr(%lx) signature(%x)!\n", + (unsigned long) bhdr, bhdr->b_signature); + note = end = 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 0 + printf("n_type: %x n_name(%d): n_desc(%d): \n", + hdr->n_type, hdr->n_namesz, hdr->n_descsz); +#endif + if ((hdr->n_namesz == 10) && + (memcmp(n_name, "Etherboot", 10) == 0)) { + switch(hdr->n_type) { + case EB_IA64_IMAGE_HANDLE: + { + uint64_t *handlep = (void *)n_desc; + etherboot_handle = (EFI_HANDLE)(*handlep); + break; + } + case EB_IA64_SYSTAB: + { + uint64_t *systabp = (void *)n_desc; + efi_info.systab = (void *)(*systabp); + efi_info.flags |= READ_SYSTAB; + break; + } + case EB_IA64_FPSWA: + { + uint64_t*fpswap = (void *)n_desc; + efi_info.fpswa = (void *)(*fpswap); + efi_info.flags |= READ_FPSWA; + break; + } + case EB_IA64_CONINFO: + { + struct console_info *coninfop = (void *)n_desc; + efi_info.coninfo = *coninfop; + efi_info.flags |= READ_CONINFO; + break; + } + case EB_IA64_MEMMAP: + { + struct efi_mem_map *mem_mapp = (void *)n_desc; + efi_info.mem_map = *mem_mapp; + efi_info.flags |= READ_MEMMAP; + break; + } + default: + break; + } + } + note = next; + } + if (!(efi_info.flags & READ_SYSTAB)) { + printf("No EFI systab\n"); + return; + } + + /* If I have an efi memory map assume ExitBootServices has been called. + */ +#warning "FIXME see if there is a better test for boot services still being active " + printf("FIXME Develop a better test for boot services still being active\n"); + if (!(efi_info.flags & READ_MEMMAP)) { + conout = efi_info.systab->ConOut; + conin = efi_info.systab->ConIn; + boot_services = efi_info.systab->BootServices; + } + + if (!(efi_info.flags & READ_CONINFO)) { + efi_info.flags |= READ_CONINFO; + efi_get_coninfo(&efi_info.coninfo); + } + if (!(efi_info.flags & READ_FPSWA)) { + efi_info.flags |= READ_FPSWA; + efi_info.fpswa = efi_get_fpswa(); + } + if (!(efi_info.flags & READ_MEMMAP)) { + efi_info.flags |= READ_MEMMAP; + read_efi_mem_map(&efi_info.mem_map); + /* Allocate all of the memory efi can spare */ + efi_allocate_memory(&efi_info.mem_map); + /* Now refresh the memory map */ + read_efi_mem_map(&efi_info.mem_map); + } + /* Get the io_base for legacy i/o */ + set_io_base(&efi_info.mem_map); + + /* Attempt to disable the watchdog timer.. + * Nothing useful can be done if this fails, so ignore the return code. + */ + status = efi_set_watchdog_timer(0, 1, 0, 0); + + /* Shutdown efi network drivers so efi doesn't get too confused */ + efi_stop_nics(); + + if (efi_info.systab) { + static const EFI_GUID mps_table_guid = MPS_TABLE_GUID; + static const EFI_GUID acpi20_table_guid = ACPI_20_TABLE_GUID; + static const EFI_GUID smbios_table_guid = SMBIOS_TABLE_GUID; + static const EFI_GUID sal_system_table_guid = SAL_SYSTEM_TABLE_GUID; + static const EFI_GUID nii_table_guid = EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL; + EFI_SYSTEM_TABLE *systab; + unsigned i; + systab = efi_info.systab; + for(i = 0; i < systab->NumberOfTableEntries; i++) { + EFI_GUID *guid; + void *table = systab->ConfigurationTable[i].VendorTable; + guid = &systab->ConfigurationTable[i].VendorGuid; + +#if 0 + printf("GUID: %x-%hx-%hx-%hhx-%hhx-%hhx-%hhx-%hhx-%hhx-%hhx-%hhx Table: %lx\n", + guid->Data1, guid->Data2, guid->Data3, + guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3], + guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7], + table); +#endif + + if (memcmp(guid, &mps_table_guid, 16) == 0) { + mps_table = table; + } + if (memcmp(guid, &acpi20_table_guid, 16) == 0) { + acpi20_table = table; + } + if (memcmp(guid, &smbios_table_guid, 16) == 0) { + smbios_table = table; + } + if (memcmp(guid, &sal_system_table_guid, 16) == 0) { + parse_sal_system_table(table); + } + if (memcmp(guid, &nii_table_guid, 16) == 0) { + nii_table = table; + } + } + } +} + +void arch_on_exit(int status __unused) +{ + if (!boot_services) + return; + read_efi_mem_map(&efi_info.mem_map); + efi_free_memory(&efi_info.mem_map); +} + +void arch_relocate_to(unsigned long addr) +{ + EFI_PHYSICAL_ADDRESS address, end; + UINTN pages; + EFI_STATUS status; + + if (!boot_services) + return; + + /* Find the efi pages where the new etherboot will sit */ + address = addr & ~(EFI_PAGE_SIZE -1); + end = (addr + (_end - _text) + EFI_PAGE_SIZE -1) & ~EFI_PAGE_SIZE; + pages = (end - address)/EFI_PAGE_SIZE; + + /* Reallocate the memory for the new copy of etherboot as LoaderCode */ + status = efi_free_pages(address, pages); + if (status != EFI_SUCCESS) { + printf("efi_free_pages failed!: %lx\n", status); + return; + } + status = efi_allocate_pages(AllocateAddress, EfiLoaderCode, pages, &address); + if (status != EFI_SUCCESS) { + printf("efi_allocate_pages failed! %lx\n", status); + return; + } +} + + +struct meminfo meminfo; +void get_memsizes(void) +{ + EFI_MEMORY_DESCRIPTOR *desc, *end; + struct efi_mem_map *map; +#define next_desc(desc, size) ((EFI_MEMORY_DESCRIPTOR *)(((char *)(desc)) + (size))) + + map = &efi_info.mem_map; + end = next_desc(map->map, map->map_size); + + meminfo.map_count = 0; + for(desc = map->map; desc < end ; desc = next_desc(desc, map->descriptor_size)) { + uint64_t start, size, end; + unsigned long mem_k; + + start = desc->PhysicalStart; + size = desc->NumberOfPages*EFI_PAGE_SIZE; + end = start + size; + + if ((desc->Type != EfiLoaderCode) && + (desc->Type != EfiLoaderData)) { + continue; + } + + meminfo.map[meminfo.map_count].addr = start; + meminfo.map[meminfo.map_count].size = size; + meminfo.map[meminfo.map_count].type = E820_RAM; + meminfo.map_count++; + + end >>= 10; + mem_k = end; + if (end & 0xFFFFFFFF00000000ULL) { + mem_k = 0xFFFFFFFF; + } + /* Set the base basememsize */ + if ((mem_k <= 640) && (meminfo.basememsize <= mem_k)) { + meminfo.basememsize = mem_k; + } + /* Set the total memsize */ + if ((mem_k >= 1024) && (meminfo.memsize <= (mem_k - 1024))) { + meminfo.memsize = mem_k - 1024; + } + if (meminfo.map_count == E820MAX) + break; + } +#undef next_desc +} + + +EFI_NETWORK_INTERFACE_IDENTIFIER_INTERFACE *lookup_efi_nic(int index) +{ + static EFI_GUID protocol = EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL; + EFI_HANDLE handles[MAX_EFI_DEVICES]; + EFI_STATUS status; + UINTN devices; + void *that; + + if (!boot_services) + return 0; + if (index < 0) { + return 0; + } + devices = sizeof(handles); + status = efi_locate_handle( + ByProtocol, &protocol, 0, &devices, handles); + if (status != EFI_SUCCESS) + return 0; + devices /= sizeof(handles[0]); + if (index >= devices) + return 0; + status = efi_handle_protocol(handles[index], &protocol, &that); + if (status != EFI_SUCCESS) + return 0; + return that; +} + +#if defined(CONSOLE_FIRMWARE) +void console_putc(int c) +{ + CHAR16 str[2]; + if (!conout) + return; + str[0] = c; + str[1] = 0; + __call(conout->OutputString, conout, str); +} + +static int efi_have_key = 0; +static int efi_key; +int console_ischar(void) +{ + EFI_STATUS status; + EFI_INPUT_KEY new_key; + if (!conin) + return 0; + if (efi_have_key) { + return 1; + } + status = __call(conin->ReadKeyStroke, conin, &new_key); + if (status == EFI_SUCCESS) { + if ((new_key.UnicodeChar >= 0) && (new_key.UnicodeChar < 0x7f)) { + efi_have_key = 1; + efi_key = new_key.UnicodeChar; + } + else if (new_key.ScanCode == 0x17) { + efi_have_key = 1; + efi_key = K_ESC; + } + } + return efi_have_key; +} + +int console_getc(void) +{ + if (efi_have_key) { + efi_have_key = 0; + } + + return efi_key; +} +#endif /* CONSOLE_FIRMWARE */ + +#define NAME "Etherboot" +#define FIRMWARE "EFI" + +#define SZ(X) ((sizeof(X)+3) & ~3) +#define CP(D,S) (memcpy(&(D), &(S), sizeof(S))) + + +struct elf_notes { + /* CAREFUL this structure is carefully arranged to avoid + * alignment problems. + */ + /* The note header */ + struct Elf_Bhdr hdr; + + /* First the Fixed sized entries that must be well aligned */ + + /* Insert a nop record so the next record is 64bit aligned */ + Elf_Nhdr nf0; + + /* Pointer to bootp data */ + Elf_Nhdr nf1; + char nf1_name[SZ(EB_PARAM_NOTE)]; + uint64_t nf1_bootp_data; + + /* Pointer to ELF header */ + Elf_Nhdr nf2; + char nf2_name[SZ(EB_PARAM_NOTE)]; + uint64_t nf2_header; + + /* The EFI systab pointer */ + Elf_Nhdr nf3; + char nf3_name[SZ(EB_PARAM_NOTE)]; + uint64_t nf3_systab; + + /* The FPSWA pointer */ + Elf_Nhdr nf4; + char nf4_name[SZ(EB_PARAM_NOTE)]; + uint64_t nf4_fpswa; + + /* The memory map */ + Elf_Nhdr nf5; + char nf5_name[SZ(EB_PARAM_NOTE)]; + struct efi_mem_map nf5_map; + + /* The console info, silly but elilo passes it... */ + Elf_Nhdr nf6; + char nf6_name[SZ(EB_PARAM_NOTE)]; + struct console_info nf6_coninfo; + + /* Then the variable sized data string data where alignment does not matter */ + + /* The bootloader name */ + Elf_Nhdr nv1; + char nv1_desc[SZ(NAME)]; + /* The bootloader version */ + Elf_Nhdr nv2; + char nv2_desc[SZ(VERSION)]; + /* The firmware type */ + Elf_Nhdr nv3; + char nv3_desc[SZ(FIRMWARE)]; + /* Name of the loaded image */ + Elf_Nhdr nv4; + char nv4_loaded_image[128]; + /* An empty command line */ + Elf_Nhdr nv5; + char nv5_cmdline[SZ("")]; +}; + +#define ELF_NOTE_COUNT (6+5) + +static struct elf_notes notes; +struct Elf_Bhdr *prepare_boot_params(void *header) +{ + /* Shutdown the boot services */ + if (boot_services) { + efi_get_coninfo(&efi_info.coninfo); + read_efi_mem_map(&efi_info.mem_map); + efi_exit_boot_services(&efi_info.mem_map); + } + + memset(¬es, 0, sizeof(notes)); + notes.hdr.b_signature = 0x0E1FB007; + notes.hdr.b_size = sizeof(notes); + notes.hdr.b_checksum = 0; + notes.hdr.b_records = ELF_NOTE_COUNT; + + /* Initialize the fixed length entries. */ + + /* Align the fixed length entries to a 64bit boundary */ + notes.nf0.n_namesz = 0; + notes.nf0.n_descsz = 0; + notes.nf0.n_type = EBN_NOP; + + notes.nf1.n_namesz = sizeof(EB_PARAM_NOTE); + notes.nf1.n_descsz = sizeof(notes.nf1_bootp_data); + notes.nf1.n_type = EB_BOOTP_DATA; + CP(notes.nf1_name, EB_PARAM_NOTE); + notes.nf1_bootp_data = virt_to_phys(BOOTP_DATA_ADDR); + + notes.nf2.n_namesz = sizeof(EB_PARAM_NOTE); + notes.nf2.n_descsz = sizeof(notes.nf2_header); + notes.nf2.n_type = EB_HEADER; + CP(notes.nf2_name, EB_PARAM_NOTE); + notes.nf2_header = virt_to_phys(header); + + notes.nf3.n_namesz = sizeof(EB_PARAM_NOTE); + notes.nf3.n_descsz = sizeof(notes.nf3_systab); + notes.nf3.n_type = EB_IA64_SYSTAB; + CP(notes.nf3_name, EB_PARAM_NOTE); + notes.nf3_systab = (unsigned long)efi_info.systab; + + notes.nf4.n_namesz = sizeof(EB_PARAM_NOTE); + notes.nf4.n_descsz = sizeof(notes.nf4_fpswa); + notes.nf4.n_type = EB_IA64_FPSWA; + CP(notes.nf4_name, EB_PARAM_NOTE); + notes.nf4_fpswa = (unsigned long)efi_info.fpswa; + + notes.nf5.n_namesz = sizeof(EB_PARAM_NOTE); + notes.nf5.n_descsz = sizeof(notes.nf5_map); + notes.nf5.n_type = EB_IA64_MEMMAP; + CP(notes.nf5_name, EB_PARAM_NOTE); + notes.nf5_map = efi_info.mem_map; + + notes.nf6.n_namesz = sizeof(EB_PARAM_NOTE); + notes.nf6.n_descsz = sizeof(notes.nf6_coninfo); + notes.nf6.n_type = EB_IA64_CONINFO; + CP(notes.nf6_name, EB_PARAM_NOTE); + notes.nf6_coninfo = efi_info.coninfo; + + /* Initialize the variable length entries */ + notes.nv1.n_namesz = 0; + notes.nv1.n_descsz = sizeof(NAME); + notes.nv1.n_type = EBN_BOOTLOADER_NAME; + CP(notes.nv1_desc, NAME); + + notes.nv2.n_namesz = 0; + notes.nv2.n_descsz = sizeof(VERSION); + notes.nv2.n_type = EBN_BOOTLOADER_VERSION; + CP(notes.nv2_desc, VERSION); + + notes.nv3.n_namesz = 0; + notes.nv3.n_descsz = sizeof(FIRMWARE); + notes.nv3.n_type = EBN_FIRMWARE_TYPE; + CP(notes.nv3_desc, FIRMWARE); + + /* Attempt to pass the name of the loaded image */ + notes.nv4.n_namesz = 0; + notes.nv4.n_descsz = sizeof(notes.nv4_loaded_image); + notes.nv4.n_type = EBN_LOADED_IMAGE; + memcpy(¬es.nv4_loaded_image, KERNEL_BUF, sizeof(notes.nv4_loaded_image)); + + /* Pass an empty command line for now */ + notes.nv5.n_namesz = 0; + notes.nv5.n_descsz = sizeof(""); + notes.nv5.n_type = EBN_COMMAND_LINE; + CP(notes.nv5_cmdline, ""); + + notes.hdr.b_checksum = ipchksum(¬es, sizeof(notes)); + /* Like UDP invert a 0 checksum to show that a checksum is present */ + if (notes.hdr.b_checksum == 0) { + notes.hdr.b_checksum = 0xffff; + } + + return ¬es.hdr; +} + +int elf_start(unsigned long machine __unused, unsigned long entry, unsigned long params) +{ + struct elf_notes *notes; + int result; + /* Since we can do both be polite and also pass the linux + * ia64_boot_param table. + */ + static struct ia64_boot_param { + uint64_t command_line; /* physical address of command line arguments */ + uint64_t efi_systab; /* physical address of EFI system table */ + uint64_t efi_memmap; /* physical address of EFI memory map */ + uint64_t efi_memmap_size; /* size of EFI memory map */ + uint64_t efi_memdesc_size; /* size of an EFI memory map descriptor */ + uint32_t efi_memdesc_version; /* memory descriptor version */ + struct { + uint16_t num_cols; /* number of columns on console output device */ + uint16_t num_rows; /* number of rows on console output device */ + uint16_t orig_x; /* cursor's x position */ + uint16_t orig_y; /* cursor's y position */ + } console_info; + uint64_t fpswa; /* physical address of the fpswa interface */ + uint64_t initrd_start; + uint64_t initrd_size; + } bp; + + notes = phys_to_virt(params); + /* If I don't have notes don't attempt to start the image */ + if (notes == 0) { + return -2; + } + + bp.command_line = (unsigned long)¬es->nv5_cmdline; + bp.efi_systab = notes->nf3_systab; + bp.efi_memmap = (unsigned long)¬es->nf5_map.map; + bp.efi_memmap_size = notes->nf5_map.map_size; + bp.efi_memdesc_size = notes->nf5_map.descriptor_size; + bp.efi_memdesc_version = notes->nf5_map.descriptor_version; + bp.console_info.num_cols = notes->nf6_coninfo.num_cols; + bp.console_info.num_rows = notes->nf6_coninfo.num_rows; + bp.console_info.orig_x = notes->nf6_coninfo.orig_x; + bp.console_info.orig_y = notes->nf6_coninfo.orig_y; + bp.fpswa = notes->nf4_fpswa; + bp.initrd_start = 0; + bp.initrd_size = 0; + + + asm volatile( + ";;\n\t" + "mov r28=%2\n\t" + "mov out0=%3\n\t" + "br.call.sptk.few rp=%1\n\t" + "mov %0=r8\n\t" + : "=r" (result) + : "b"(entry), "r"(&bp),"r"(params) + ); + return result; +} diff --git a/src/arch/ia64/core/etherboot.lds b/src/arch/ia64/core/etherboot.lds new file mode 100644 index 00000000..216cce92 --- /dev/null +++ b/src/arch/ia64/core/etherboot.lds @@ -0,0 +1,82 @@ +OUTPUT_FORMAT("elf64-ia64-little") + +OUTPUT_ARCH(ia64) + +ENTRY(_start) +SECTIONS { + . = 0; + __gp = . + 0x200000; + _virt_start = .; + _text = . ; + .text : { + /* Start address of etherboot in the virtual address space */ + *(.text) + *(.text.*) + _etext = . ; + + _rodata = . ; + . = ALIGN(16); + *(.rodata) + *(.rodata.*) + *(.srodata) + . = ALIGN(16); + pci_drivers = . ; + *(.drivers.pci); + pci_drivers_end = . ; + . = ALIGN(16); + isa_drivers = . ; + *(.drivers.isa); + isa_drivers_end = . ; + . = ALIGN(16); + + . = ALIGN(16); + _rela = . ; + *(.rela.text) + *(.rela.rodata) + *(.rela.drivers.pci) + *(.rela.drivers.isa) + *(.rela.drivers.efi) + *(.rela.data) + *(.rela.sdata) + *(.rela.got) + _erela = . ; + . = ALIGN(16); + _erodata = . ; + } + _rela_size = _erela - _rela ; + .data : { + _data = . ; + *(.data) + *(.got.plt) + *(.got) + *(.sdata) + *(.sbss) + *(.scommon) + *(.data.*) + *(.data1) + . = ALIGN(16); + _edata = . ; + } + _bss = . ; + .bss : { + *(.sbss) + *(.scommon) + *(.dynbss) + *(.bss) + *(COMMON) + } + _ebss = .; + _end = .; + /DISCARD/ : { + *(.comment) + *(.note) + *(.hash) + *(.dynstr) + *(.dynsym) + *(.IA_64.unwind) + *(.IA_64.unwind_info) + *(.IA64_unwind) + *(.IA64_unwind_info) + *(.dynamic) + } +} diff --git a/src/arch/ia64/core/ia64_timer.c b/src/arch/ia64/core/ia64_timer.c new file mode 100644 index 00000000..3115714a --- /dev/null +++ b/src/arch/ia64/core/ia64_timer.c @@ -0,0 +1,89 @@ +#include "etherboot.h" +#include "timer.h" +#include "sal.h" +#include "pal.h" + +static inline unsigned long get_cycles(void) +{ + unsigned long result; + __asm__ __volatile__(";;mov %0=ar.itc;;" : "=r"(result)); + return result; +} + +/* ------ Calibrate the TSC ------- + * Time how long it takes to excute a loop that runs in known time. + * And find the convertion needed to get to CLOCK_TICK_RATE + */ + +static unsigned long calibrate_cycles(void) +{ + unsigned long platform_ticks_per_second, drift_info; + struct pal_freq_ratio itc_ratio; + long result; + result = sal_freq_base(SAL_FREQ_BASE_PLATFORM, &platform_ticks_per_second, &drift_info); + if (result != 0) { + printf("sal_freq_base failed: %lx\n",result); + exit(1); + } else { + result = pal_freq_ratios(0,0,&itc_ratio); + if (result != 0) { + printf("pal_freq_ratios failed: %lx\n", result); + exit(1); + } + } + /* Avoid division by zero */ + if (itc_ratio.den == 0) + itc_ratio.den = 1; + + return (platform_ticks_per_second *itc_ratio.num)/(itc_ratio.den*TICKS_PER_SEC); +} + +static unsigned long clocks_per_tick; +void setup_timers(void) +{ + if (!clocks_per_tick) { + clocks_per_tick = calibrate_cycles(); + /* Display the CPU Mhz to easily test if the calibration was bad */ + printf("ITC %ld Mhz\n", (clocks_per_tick/1000 * TICKS_PER_SEC)/1000); + } +} + +unsigned long currticks(void) +{ + return get_cycles()/clocks_per_tick; +} + +static unsigned long timer_timeout; +static int __timer_running(void) +{ + return get_cycles() < timer_timeout; +} + +void udelay(unsigned int usecs) +{ + unsigned long now; + now = get_cycles(); + timer_timeout = now + usecs * ((clocks_per_tick * TICKS_PER_SEC)/(1000*1000)); + while(__timer_running()); +} +void ndelay(unsigned int nsecs) +{ + unsigned long now; + now = get_cycles(); + timer_timeout = now + nsecs * ((clocks_per_tick * TICKS_PER_SEC)/(1000*1000*1000)); + while(__timer_running()); +} + +void load_timer2(unsigned int timer2_ticks) +{ + unsigned long now; + unsigned long clocks; + now = get_cycles(); + clocks = timer2_ticks * ((clocks_per_tick * TICKS_PER_SEC)/CLOCK_TICK_RATE); + timer_timeout = now + clocks; +} + +int timer2_running(void) +{ + return __timer_running(); +} diff --git a/src/arch/ia64/core/idiv32.S b/src/arch/ia64/core/idiv32.S new file mode 100644 index 00000000..283eff0e --- /dev/null +++ b/src/arch/ia64/core/idiv32.S @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2000 Hewlett-Packard Co + * Copyright (C) 2000 David Mosberger-Tang <davidm@hpl.hp.com> + * + * 32-bit integer division. + * + * This code is based on the application note entitled "Divide, Square Root + * and Remainder Algorithms for the IA-64 Architecture". This document + * is available as Intel document number 248725-002 or via the web at + * http://developer.intel.com/software/opensource/numerics/ + * + * For more details on the theory behind these algorithms, see "IA-64 + * and Elementary Functions" by Peter Markstein; HP Professional Books + * (http://www.hp.com/go/retailbooks/) + */ + +#ifdef MODULO +# define OP mod +#else +# define OP div +#endif + +#ifdef UNSIGNED +# define SGN u +# define EXTEND zxt4 +# define INT_TO_FP(a,b) fcvt.xuf.s1 a=b +# define FP_TO_INT(a,b) fcvt.fxu.trunc.s1 a=b +#else +# define SGN +# define EXTEND sxt4 +# define INT_TO_FP(a,b) fcvt.xf a=b +# define FP_TO_INT(a,b) fcvt.fx.trunc.s1 a=b +#endif + +#define PASTE1(a,b) a##b +#define PASTE(a,b) PASTE1(a,b) +#define NAME PASTE(PASTE(__,SGN),PASTE(OP,si3)) + + .text + .global NAME + .proc NAME +NAME : + .regstk 2,0,0,0 + // Transfer inputs to FP registers. + mov r2 = 0xffdd // r2 = -34 + 65535 (fp reg format bias) + EXTEND in0 = in0 // in0 = a + EXTEND in1 = in1 // in1 = b + ;; + setf.sig f8 = in0 + setf.sig f9 = in1 +#ifdef MODULO + sub in1 = r0, in1 // in1 = -b +#endif + ;; + // Convert the inputs to FP, to avoid FP software-assist faults. + INT_TO_FP(f8, f8) + INT_TO_FP(f9, f9) + ;; + setf.exp f7 = r2 // f7 = 2^-34 + frcpa.s1 f6, p6 = f8, f9 // y0 = frcpa(b) + ;; +(p6) fmpy.s1 f8 = f8, f6 // q0 = a*y0 +(p6) fnma.s1 f6 = f9, f6, f1 // e0 = -b*y0 + 1 + ;; +#ifdef MODULO + setf.sig f9 = in1 // f9 = -b +#endif +(p6) fma.s1 f8 = f6, f8, f8 // q1 = e0*q0 + q0 +(p6) fma.s1 f6 = f6, f6, f7 // e1 = e0*e0 + 2^-34 + ;; +#ifdef MODULO + setf.sig f7 = in0 +#endif +(p6) fma.s1 f6 = f6, f8, f8 // q2 = e1*q1 + q1 + ;; + FP_TO_INT(f6, f6) // q = trunc(q2) + ;; +#ifdef MODULO + xma.l f6 = f6, f9, f7 // r = q*(-b) + a + ;; +#endif + getf.sig r8 = f6 // transfer result to result register + br.ret.sptk.many rp + + .size NAME, . - NAME + .endp NAME diff --git a/src/arch/ia64/core/idiv64.S b/src/arch/ia64/core/idiv64.S new file mode 100644 index 00000000..d989a2f5 --- /dev/null +++ b/src/arch/ia64/core/idiv64.S @@ -0,0 +1,96 @@ +/* + * Copyright (C) 1999-2000, 2002 Hewlett-Packard Co + * Copyright (C) 1999-2000 David Mosberger-Tang <davidm@hpl.hp.com> + * + * 64-bit integer division. + * + * This code is based on the application note entitled "Divide, Square Root + * and Remainder Algorithms for the IA-64 Architecture". This document + * is available as Intel document number 248725-002 or via the web at + * http://developer.intel.com/software/opensource/numerics/ + * + * For more details on the theory behind these algorithms, see "IA-64 + * and Elementary Functions" by Peter Markstein; HP Professional Books + * (http://www.hp.com/go/retailbooks/) + */ + +#ifdef MODULO +# define OP mod +#else +# define OP div +#endif + +#ifdef UNSIGNED +# define SGN u +# define INT_TO_FP(a,b) fcvt.xuf.s1 a=b +# define FP_TO_INT(a,b) fcvt.fxu.trunc.s1 a=b +#else +# define SGN +# define INT_TO_FP(a,b) fcvt.xf a=b +# define FP_TO_INT(a,b) fcvt.fx.trunc.s1 a=b +#endif + +#define PASTE1(a,b) a##b +#define PASTE(a,b) PASTE1(a,b) +#define NAME PASTE(PASTE(__,SGN),PASTE(OP,di3)) + + .text + .global NAME + .proc NAME +NAME : + .prologue + .regstk 2,0,0,0 + // Transfer inputs to FP registers. + setf.sig f8 = in0 + setf.sig f9 = in1 + ;; + .fframe 16 + .save.f 0x20 + stf.spill [sp] = f17,-16 + + // Convert the inputs to FP, to avoid FP software-assist faults. + INT_TO_FP(f8, f8) + ;; + + .save.f 0x10 + stf.spill [sp] = f16 + .body + INT_TO_FP(f9, f9) + ;; + frcpa.s1 f17, p6 = f8, f9 // y0 = frcpa(b) + ;; +(p6) fmpy.s1 f7 = f8, f17 // q0 = a*y0 +(p6) fnma.s1 f6 = f9, f17, f1 // e0 = -b*y0 + 1 + ;; +(p6) fma.s1 f16 = f7, f6, f7 // q1 = q0*e0 + q0 +(p6) fmpy.s1 f7 = f6, f6 // e1 = e0*e0 + ;; +#ifdef MODULO + sub in1 = r0, in1 // in1 = -b +#endif +(p6) fma.s1 f16 = f16, f7, f16 // q2 = q1*e1 + q1 +(p6) fma.s1 f6 = f17, f6, f17 // y1 = y0*e0 + y0 + ;; +(p6) fma.s1 f6 = f6, f7, f6 // y2 = y1*e1 + y1 +(p6) fnma.s1 f7 = f9, f16, f8 // r = -b*q2 + a + ;; +#ifdef MODULO + setf.sig f8 = in0 // f8 = a + setf.sig f9 = in1 // f9 = -b +#endif +(p6) fma.s1 f17 = f7, f6, f16 // q3 = r*y2 + q2 + ;; + .restore sp + ldf.fill f16 = [sp], 16 + FP_TO_INT(f17, f17) // q = trunc(q3) + ;; +#ifdef MODULO + xma.l f17 = f17, f9, f8 // r = q*(-b) + a + ;; +#endif + getf.sig r8 = f17 // transfer result to result register + ldf.fill f17 = [sp] + br.ret.sptk.many rp + + .size NAME, . - NAME + .endp NAME diff --git a/src/arch/ia64/core/longjmp.S b/src/arch/ia64/core/longjmp.S new file mode 100644 index 00000000..d5c8e5ab --- /dev/null +++ b/src/arch/ia64/core/longjmp.S @@ -0,0 +1,163 @@ +/* Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. + Contributed by David Mosberger-Tang <davidm@hpl.hp.com>. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. + + Note that __sigsetjmp() did NOT flush the register stack. Instead, + we do it here since __longjmp() is usually much less frequently + invoked than __sigsetjmp(). The only difficulty is that __sigsetjmp() + didn't (and wouldn't be able to) save ar.rnat either. This is a problem + because if we're not careful, we could end up loading random NaT bits. + There are two cases: + + (i) ar.bsp < ia64_rse_rnat_addr(jmpbuf.ar_bsp) + ar.rnat contains the desired bits---preserve ar.rnat + across loadrs and write to ar.bspstore + + (ii) ar.bsp >= ia64_rse_rnat_addr(jmpbuf.ar_bsp) + The desired ar.rnat is stored in + ia64_rse_rnat_addr(jmpbuf.ar_bsp). Load those + bits into ar.rnat after setting ar.bspstore. */ + + +# define pPos p6 /* is rotate count positive? */ +# define pNeg p7 /* is rotate count negative? */ + + + /* longjmp(__jmp_buf buf, int val) */ + + .text + .global longjmp + .proc longjmp +longjmp: + + alloc r8=ar.pfs,2,1,0,0 + mov r27=ar.rsc + add r2=0x98,in0 // r2 <- &jmpbuf.orig_jmp_buf_addr + ;; + ld8 r8=[r2],-16 // r8 <- orig_jmp_buf_addr + mov r10=ar.bsp + and r11=~0x3,r27 // clear ar.rsc.mode + ;; + flushrs // flush dirty regs to backing store (must be first in insn grp) + ld8 r23=[r2],8 // r23 <- jmpbuf.ar_bsp + sub r8=r8,in0 // r8 <- &orig_jmpbuf - &jmpbuf + ;; + ld8 r25=[r2] // r25 <- jmpbuf.ar_unat + extr.u r8=r8,3,6 // r8 <- (&orig_jmpbuf - &jmpbuf)/8 & 0x3f + ;; + cmp.lt pNeg,pPos=r8,r0 + mov r2=in0 + ;; +(pPos) mov r16=r8 +(pNeg) add r16=64,r8 +(pPos) sub r17=64,r8 +(pNeg) sub r17=r0,r8 + ;; + mov ar.rsc=r11 // put RSE in enforced lazy mode + shr.u r8=r25,r16 + add r3=8,in0 // r3 <- &jmpbuf.r1 + shl r9=r25,r17 + ;; + or r25=r8,r9 + ;; + mov r26=ar.rnat + mov ar.unat=r25 // setup ar.unat (NaT bits for r1, r4-r7, and r12) + ;; + ld8.fill.nta sp=[r2],16 // r12 (sp) + ld8.fill.nta gp=[r3],16 // r1 (gp) + dep r11=-1,r23,3,6 // r11 <- ia64_rse_rnat_addr(jmpbuf.ar_bsp) + ;; + ld8.nta r16=[r2],16 // caller's unat + ld8.nta r17=[r3],16 // fpsr + ;; + ld8.fill.nta r4=[r2],16 // r4 + ld8.fill.nta r5=[r3],16 // r5 (gp) + cmp.geu p8,p0=r10,r11 // p8 <- (ar.bsp >= jmpbuf.ar_bsp) + ;; + ld8.fill.nta r6=[r2],16 // r6 + ld8.fill.nta r7=[r3],16 // r7 + ;; + mov ar.unat=r16 // restore caller's unat + mov ar.fpsr=r17 // restore fpsr + ;; + ld8.nta r16=[r2],16 // b0 + ld8.nta r17=[r3],16 // b1 + ;; +(p8) ld8 r26=[r11] // r26 <- *ia64_rse_rnat_addr(jmpbuf.ar_bsp) + mov ar.bspstore=r23 // restore ar.bspstore + ;; + ld8.nta r18=[r2],16 // b2 + ld8.nta r19=[r3],16 // b3 + ;; + ld8.nta r20=[r2],16 // b4 + ld8.nta r21=[r3],16 // b5 + ;; + ld8.nta r11=[r2],16 // ar.pfs + ld8.nta r22=[r3],56 // ar.lc + ;; + ld8.nta r24=[r2],32 // pr + mov b0=r16 + ;; + ldf.fill.nta f2=[r2],32 + ldf.fill.nta f3=[r3],32 + mov b1=r17 + ;; + ldf.fill.nta f4=[r2],32 + ldf.fill.nta f5=[r3],32 + mov b2=r18 + ;; + ldf.fill.nta f16=[r2],32 + ldf.fill.nta f17=[r3],32 + mov b3=r19 + ;; + ldf.fill.nta f18=[r2],32 + ldf.fill.nta f19=[r3],32 + mov b4=r20 + ;; + ldf.fill.nta f20=[r2],32 + ldf.fill.nta f21=[r3],32 + mov b5=r21 + ;; + ldf.fill.nta f22=[r2],32 + ldf.fill.nta f23=[r3],32 + mov ar.lc=r22 + ;; + ldf.fill.nta f24=[r2],32 + ldf.fill.nta f25=[r3],32 + cmp.eq p8,p9=0,in1 + ;; + ldf.fill.nta f26=[r2],32 + ldf.fill.nta f27=[r3],32 + mov ar.pfs=r11 + ;; + ldf.fill.nta f28=[r2],32 + ldf.fill.nta f29=[r3],32 + ;; + ldf.fill.nta f30=[r2] + ldf.fill.nta f31=[r3] +(p8) mov r8=1 + + mov ar.rnat=r26 // restore ar.rnat + ;; + mov ar.rsc=r27 // restore ar.rsc +(p9) mov r8=in1 + + invala // virt. -> phys. regnum mapping may change + mov pr=r24,-1 + br.ret.sptk.few b0 + .size longjmp, . - longjmp + .endp longjmp diff --git a/src/arch/ia64/core/memmove.S b/src/arch/ia64/core/memmove.S new file mode 100644 index 00000000..63e093d9 --- /dev/null +++ b/src/arch/ia64/core/memmove.S @@ -0,0 +1,244 @@ +/* Optimized version of the standard memmove() function. + This file is part of the GNU C Library. + Copyright (C) 2000, 2001 Free Software Foundation, Inc. + Contributed by Dan Pop <Dan.Pop@cern.ch>. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* Return: dest + + Inputs: + in0: dest + in1: src + in2: byte count + + The core of the function is the memcpy implementation used in memcpy.S. + When bytes have to be copied backwards, only the easy case, when + all arguments are multiples of 8, is optimised. + + In this form, it assumes little endian mode. For big endian mode, + sh1 must be computed using an extra instruction: sub sh1 = 64, sh1 + or the UM.be bit should be cleared at the beginning and set at the end. */ + + +#define OP_T_THRES 16 +#define OPSIZ 8 + +#define adest r15 +#define saved_pr r17 +#define saved_lc r18 +#define dest r19 +#define src r20 +#define len r21 +#define asrc r22 +#define tmp2 r23 +#define tmp3 r24 +#define tmp4 r25 +#define ptable r26 +#define ploop56 r27 +#define loopaddr r28 +#define sh1 r29 +#define loopcnt r30 +#define value r31 + +#define LOOP(shift) \ + .align 32 ; \ +.loop##shift##: \ +(p[0]) ld8 r[0] = [asrc], 8 ; /* w1 */ \ +(p[MEMLAT+1]) st8 [dest] = value, 8 ; \ +(p[MEMLAT]) shrp value = r[MEMLAT], r[MEMLAT+1], shift ; \ + nop.b 0 ; \ + nop.b 0 ; \ + br.ctop.sptk .loop##shift ; \ + br.cond.sptk .cpyfew ; /* deal with the remaining bytes */ + +#define MEMLAT 21 +#define Nrot (((2*MEMLAT+3) + 7) & ~7) + + .text + .global memmove, memcpy + .proc memove +memcpy: +memmove: + .prologue + alloc r2 = ar.pfs, 3, Nrot - 3, 0, Nrot + .rotr r[MEMLAT + 2], q[MEMLAT + 1] + .rotp p[MEMLAT + 2] + mov ret0 = in0 // return value = dest + .save pr, saved_pr + mov saved_pr = pr // save the predicate registers + .save ar.lc, saved_lc + mov saved_lc = ar.lc // save the loop counter + .body + or tmp3 = in0, in1 ;; // tmp3 = dest | src + or tmp3 = tmp3, in2 // tmp3 = dest | src | len + mov dest = in0 // dest + mov src = in1 // src + mov len = in2 // len + sub tmp2 = r0, in0 // tmp2 = -dest + cmp.eq p6, p0 = in2, r0 // if (len == 0) +(p6) br.cond.spnt .restore_and_exit;;// return dest; + and tmp4 = 7, tmp3 // tmp4 = (dest | src | len) & 7 + cmp.le p6, p0 = dest, src // if dest <= src it's always safe +(p6) br.cond.spnt .forward // to copy forward + add tmp3 = src, len;; + cmp.lt p6, p0 = dest, tmp3 // if dest > src && dest < src + len +(p6) br.cond.spnt .backward // we have to copy backward + +.forward: + shr.u loopcnt = len, 4 ;; // loopcnt = len / 16 + cmp.ne p6, p0 = tmp4, r0 // if ((dest | src | len) & 7 != 0) +(p6) br.cond.sptk .next // goto next; + +// The optimal case, when dest, src and len are all multiples of 8 + + and tmp3 = 0xf, len + mov pr.rot = 1 << 16 // set rotating predicates + mov ar.ec = MEMLAT + 1 ;; // set the epilog counter + cmp.ne p6, p0 = tmp3, r0 // do we have to copy an extra word? + adds loopcnt = -1, loopcnt;; // --loopcnt +(p6) ld8 value = [src], 8;; +(p6) st8 [dest] = value, 8 // copy the "odd" word + mov ar.lc = loopcnt // set the loop counter + cmp.eq p6, p0 = 8, len +(p6) br.cond.spnt .restore_and_exit;;// the one-word special case + adds adest = 8, dest // set adest one word ahead of dest + adds asrc = 8, src ;; // set asrc one word ahead of src + nop.b 0 // get the "golden" alignment for + nop.b 0 // the next loop +.l0: +(p[0]) ld8 r[0] = [src], 16 +(p[0]) ld8 q[0] = [asrc], 16 +(p[MEMLAT]) st8 [dest] = r[MEMLAT], 16 +(p[MEMLAT]) st8 [adest] = q[MEMLAT], 16 + br.ctop.dptk .l0 ;; + + mov pr = saved_pr, -1 // restore the predicate registers + mov ar.lc = saved_lc // restore the loop counter + br.ret.sptk.many b0 +.next: + cmp.ge p6, p0 = OP_T_THRES, len // is len <= OP_T_THRES + and loopcnt = 7, tmp2 // loopcnt = -dest % 8 +(p6) br.cond.spnt .cpyfew // copy byte by byte + ;; + cmp.eq p6, p0 = loopcnt, r0 +(p6) br.cond.sptk .dest_aligned + sub len = len, loopcnt // len -= -dest % 8 + adds loopcnt = -1, loopcnt // --loopcnt + ;; + mov ar.lc = loopcnt +.l1: // copy -dest % 8 bytes + ld1 value = [src], 1 // value = *src++ + ;; + st1 [dest] = value, 1 // *dest++ = value + br.cloop.dptk .l1 +.dest_aligned: + and sh1 = 7, src // sh1 = src % 8 + and tmp2 = -8, len // tmp2 = len & -OPSIZ + and asrc = -8, src // asrc = src & -OPSIZ -- align src + shr.u loopcnt = len, 3 // loopcnt = len / 8 + and len = 7, len;; // len = len % 8 + adds loopcnt = -1, loopcnt // --loopcnt + addl tmp4 = @ltoff(.table), gp + addl tmp3 = @ltoff(.loop56), gp + mov ar.ec = MEMLAT + 1 // set EC + mov pr.rot = 1 << 16;; // set rotating predicates + mov ar.lc = loopcnt // set LC + cmp.eq p6, p0 = sh1, r0 // is the src aligned? +(p6) br.cond.sptk .src_aligned + add src = src, tmp2 // src += len & -OPSIZ + shl sh1 = sh1, 3 // sh1 = 8 * (src % 8) + ld8 ploop56 = [tmp3] // ploop56 = &loop56 + ld8 ptable = [tmp4];; // ptable = &table + add tmp3 = ptable, sh1;; // tmp3 = &table + sh1 + mov ar.ec = MEMLAT + 1 + 1 // one more pass needed + ld8 tmp4 = [tmp3];; // tmp4 = loop offset + sub loopaddr = ploop56,tmp4 // loopadd = &loop56 - loop offset + ld8 r[1] = [asrc], 8;; // w0 + mov b6 = loopaddr;; + br b6 // jump to the appropriate loop + + LOOP(8) + LOOP(16) + LOOP(24) + LOOP(32) + LOOP(40) + LOOP(48) + LOOP(56) + +.src_aligned: +.l3: +(p[0]) ld8 r[0] = [src], 8 +(p[MEMLAT]) st8 [dest] = r[MEMLAT], 8 + br.ctop.dptk .l3 +.cpyfew: + cmp.eq p6, p0 = len, r0 // is len == 0 ? + adds len = -1, len // --len; +(p6) br.cond.spnt .restore_and_exit ;; + mov ar.lc = len +.l4: + ld1 value = [src], 1 + ;; + st1 [dest] = value, 1 + br.cloop.dptk .l4 ;; +.restore_and_exit: + mov pr = saved_pr, -1 // restore the predicate registers + mov ar.lc = saved_lc // restore the loop counter + br.ret.sptk.many b0 + +// In the case of a backward copy, optimise only the case when everything +// is a multiple of 8, otherwise copy byte by byte. The backward copy is +// used only when the blocks are overlapping and dest > src. + +.backward: + shr.u loopcnt = len, 3 // loopcnt = len / 8 + add src = src, len // src points one byte past the end + add dest = dest, len ;; // dest points one byte past the end + mov ar.ec = MEMLAT + 1 // set the epilog counter + mov pr.rot = 1 << 16 // set rotating predicates + adds loopcnt = -1, loopcnt // --loopcnt + cmp.ne p6, p0 = tmp4, r0 // if ((dest | src | len) & 7 != 0) +(p6) br.cond.sptk .bytecopy ;; // copy byte by byte backward + adds src = -8, src // src points to the last word + adds dest = -8, dest // dest points to the last word + mov ar.lc = loopcnt;; // set the loop counter +.l5: +(p[0]) ld8 r[0] = [src], -8 +(p[MEMLAT]) st8 [dest] = r[MEMLAT], -8 + br.ctop.dptk .l5 + br.cond.sptk .restore_and_exit +.bytecopy: + adds src = -1, src // src points to the last byte + adds dest = -1, dest // dest points to the last byte + adds loopcnt = -1, len;; // loopcnt = len - 1 + mov ar.lc = loopcnt;; // set the loop counter +.l6: +(p[0]) ld1 r[0] = [src], -1 +(p[MEMLAT]) st1 [dest] = r[MEMLAT], -1 + br.ctop.dptk .l6 + br.cond.sptk .restore_and_exit +.table: + data8 0 // dummy entry + data8 .loop56 - .loop8 + data8 .loop56 - .loop16 + data8 .loop56 - .loop24 + data8 .loop56 - .loop32 + data8 .loop56 - .loop40 + data8 .loop56 - .loop48 + data8 .loop56 - .loop56 + + .size memmove, . - memove + .endp memmove diff --git a/src/arch/ia64/core/memset.S b/src/arch/ia64/core/memset.S new file mode 100644 index 00000000..a6cc40fc --- /dev/null +++ b/src/arch/ia64/core/memset.S @@ -0,0 +1,133 @@ +/* + * Copyright (C) 1999-2002 Hewlett-Packard Co. + * Contributed by Stephane Eranian <eranian@hpl.hp.com> + * + * This file is part of the ELILO, the EFI Linux boot loader. + * + * ELILO 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, or (at your option) + * any later version. + * + * ELILO 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 ELILO; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Please check out the elilo.txt for complete documentation on how + * to use this program. + * + * This code is derived from the Linux/ia64 source code. + */ + +/* + * + * Optimized version of the standard memset() function + * + * Return: none + * + * Inputs: + * in0: address of buffer + * in1: byte value to use for storing + * in2: length of the buffer + * + */ + +// arguments +// +#define buf r32 +#define val r33 +#define len r34 + +// +// local registers +// +#define saved_pfs r14 +#define cnt r18 +#define buf2 r19 +#define saved_lc r20 +#define tmp r21 + .text + .global memset + .proc memset +memset: + .prologue + .save ar.pfs, saved_pfs + alloc saved_pfs=ar.pfs,3,0,0,0 // cnt is sink here + cmp.eq p8,p0=r0,len // check for zero length + .save ar.lc, saved_lc + mov saved_lc=ar.lc // preserve ar.lc (slow) + ;; + + .body + + adds tmp=-1,len // br.ctop is repeat/until + tbit.nz p6,p0=buf,0 // odd alignment +(p8) br.ret.spnt.few rp + + cmp.lt p7,p0=16,len // if len > 16 then long memset + mux1 val=val,@brcst // prepare value +(p7) br.cond.dptk.few long_memset + ;; + mov ar.lc=tmp // initialize lc for small count + ;; // avoid RAW and WAW on ar.lc +1: // worst case 15 cyles, avg 8 cycles + st1 [buf]=val,1 + br.cloop.dptk.few 1b + ;; // avoid RAW on ar.lc + mov ar.lc=saved_lc + mov ar.pfs=saved_pfs + br.ret.sptk.few rp // end of short memset + + // at this point we know we have more than 16 bytes to copy + // so we focus on alignment +long_memset: +(p6) st1 [buf]=val,1 // 1-byte aligned +(p6) adds len=-1,len;; // sync because buf is modified + tbit.nz p6,p0=buf,1 + ;; +(p6) st2 [buf]=val,2 // 2-byte aligned +(p6) adds len=-2,len;; + tbit.nz p6,p0=buf,2 + ;; +(p6) st4 [buf]=val,4 // 4-byte aligned +(p6) adds len=-4,len;; + tbit.nz p6,p0=buf,3 + ;; +(p6) st8 [buf]=val,8 // 8-byte aligned +(p6) adds len=-8,len;; + shr.u cnt=len,4 // number of 128-bit (2x64bit) words + ;; + cmp.eq p6,p0=r0,cnt + adds tmp=-1,cnt +(p6) br.cond.dpnt.few .dotail // we have less than 16 bytes left + ;; + adds buf2=8,buf // setup second base pointer + mov ar.lc=tmp + ;; +2: // 16bytes/iteration + st8 [buf]=val,16 + st8 [buf2]=val,16 + br.cloop.dptk.few 2b + ;; +.dotail: // tail correction based on len only + tbit.nz p6,p0=len,3 + ;; +(p6) st8 [buf]=val,8 // at least 8 bytes + tbit.nz p6,p0=len,2 + ;; +(p6) st4 [buf]=val,4 // at least 4 bytes + tbit.nz p6,p0=len,1 + ;; +(p6) st2 [buf]=val,2 // at least 2 bytes + tbit.nz p6,p0=len,0 + mov ar.lc=saved_lc + ;; +(p6) st1 [buf]=val // only 1 byte left + br.ret.dptk.few rp + .endp memset diff --git a/src/arch/ia64/core/pal.c b/src/arch/ia64/core/pal.c new file mode 100644 index 00000000..cfb518d0 --- /dev/null +++ b/src/arch/ia64/core/pal.c @@ -0,0 +1,84 @@ +#include "etherboot.h" +#include "sal.h" +#include "pal.h" + +struct fptr pal_entry; +/* + * Note that some of these calls use a static-register only calling + * convention which has nothing to do with the regular calling + * convention. + */ +#define PAL_CACHE_FLUSH 1 /* flush i/d cache */ +#define PAL_CACHE_INFO 2 /* get detailed i/d cache info */ +#define PAL_CACHE_INIT 3 /* initialize i/d cache */ +#define PAL_CACHE_SUMMARY 4 /* get summary of cache heirarchy */ +#define PAL_MEM_ATTRIB 5 /* list supported memory attributes */ +#define PAL_PTCE_INFO 6 /* purge TLB info */ +#define PAL_VM_INFO 7 /* return supported virtual memory features */ +#define PAL_VM_SUMMARY 8 /* return summary on supported vm features */ +#define PAL_BUS_GET_FEATURES 9 /* return processor bus interface features settings */ +#define PAL_BUS_SET_FEATURES 10 /* set processor bus features */ +#define PAL_DEBUG_INFO 11 /* get number of debug registers */ +#define PAL_FIXED_ADDR 12 /* get fixed component of processors's directed address */ +#define PAL_FREQ_BASE 13 /* base frequency of the platform */ +#define PAL_FREQ_RATIOS 14 /* ratio of processor, bus and ITC frequency */ +#define PAL_PERF_MON_INFO 15 /* return performance monitor info */ +#define PAL_PLATFORM_ADDR 16 /* set processor interrupt block and IO port space addr */ +#define PAL_PROC_GET_FEATURES 17 /* get configurable processor features & settings */ +#define PAL_PROC_SET_FEATURES 18 /* enable/disable configurable processor features */ +#define PAL_RSE_INFO 19 /* return rse information */ +#define PAL_VERSION 20 /* return version of PAL code */ +#define PAL_MC_CLEAR_LOG 21 /* clear all processor log info */ +#define PAL_MC_DRAIN 22 /* drain operations which could result in an MCA */ +#define PAL_MC_EXPECTED 23 /* set/reset expected MCA indicator */ +#define PAL_MC_DYNAMIC_STATE 24 /* get processor dynamic state */ +#define PAL_MC_ERROR_INFO 25 /* get processor MCA info and static state */ +#define PAL_MC_RESUME 26 /* Return to interrupted process */ +#define PAL_MC_REGISTER_MEM 27 /* Register memory for PAL to use during MCAs and inits */ +#define PAL_HALT 28 /* enter the low power HALT state */ +#define PAL_HALT_LIGHT 29 /* enter the low power light halt state*/ +#define PAL_COPY_INFO 30 /* returns info needed to relocate PAL */ +#define PAL_CACHE_LINE_INIT 31 /* init tags & data of cache line */ +#define PAL_PMI_ENTRYPOINT 32 /* register PMI memory entry points with the processor */ +#define PAL_ENTER_IA_32_ENV 33 /* enter IA-32 system environment */ +#define PAL_VM_PAGE_SIZE 34 /* return vm TC and page walker page sizes */ + +#define PAL_MEM_FOR_TEST 37 /* get amount of memory needed for late processor test */ +#define PAL_CACHE_PROT_INFO 38 /* get i/d cache protection info */ +#define PAL_REGISTER_INFO 39 /* return AR and CR register information*/ +#define PAL_SHUTDOWN 40 /* enter processor shutdown state */ +#define PAL_PREFETCH_VISIBILITY 41 + +#define PAL_COPY_PAL 256 /* relocate PAL procedures and PAL PMI */ +#define PAL_HALT_INFO 257 /* return the low power capabilities of processor */ +#define PAL_TEST_PROC 258 /* perform late processor self-test */ +#define PAL_CACHE_READ 259 /* read tag & data of cacheline for diagnostic testing */ +#define PAL_CACHE_WRITE 260 /* write tag & data of cacheline for diagnostic testing */ +#define PAL_VM_TR_READ 261 /* read contents of translation register */ + + +/* + * Get the ratios for processor frequency, bus frequency and interval timer to + * to base frequency of the platform + */ +long pal_freq_ratios(struct pal_freq_ratio *proc_ratio, + struct pal_freq_ratio *bus_ratio, struct pal_freq_ratio *itc_ratio) +{ + struct freq_ratios { + long status; + struct pal_freq_ratio proc_ratio; + struct pal_freq_ratio bus_ratio; + struct pal_freq_ratio itc_ratio; + }; + struct freq_ratios result; + extern struct freq_ratios pal_call(unsigned long which, ...); + result = pal_call(PAL_FREQ_RATIOS, 0, 0, 0); + if (proc_ratio) + *proc_ratio = result.proc_ratio; + if (bus_ratio) + *bus_ratio = result.bus_ratio; + if (itc_ratio) + *itc_ratio = result.itc_ratio; + return result.status; + +} diff --git a/src/arch/ia64/core/pci_io.c b/src/arch/ia64/core/pci_io.c new file mode 100644 index 00000000..f8069bb9 --- /dev/null +++ b/src/arch/ia64/core/pci_io.c @@ -0,0 +1,62 @@ +#include "etherboot.h" +#include "pci.h" +#include "sal.h" + +int pcibios_read_config_byte(unsigned int bus, unsigned int devfn, unsigned int reg, uint8_t *rvalue) +{ + unsigned long value; + long result; + result = sal_pci_config_read(PCI_SAL_ADDRESS(0,bus, 0, devfn, reg), 1, &value); + *rvalue = value; + return result; +} +int pcibios_read_config_word(unsigned int bus, unsigned int devfn, unsigned int reg, uint16_t *rvalue) +{ + unsigned long value; + long result; + result = sal_pci_config_read(PCI_SAL_ADDRESS(0,bus, 0, devfn, reg), 2, &value); + *rvalue = value; + return result; +} +int pcibios_read_config_dword(unsigned int bus, unsigned int devfn, unsigned int reg, uint32_t *rvalue) +{ + unsigned long value; + long result; + result = sal_pci_config_read(PCI_SAL_ADDRESS(0,bus, 0, devfn, reg), 4, &value); + *rvalue = value; + return result; +} + +int pcibios_write_config_byte(unsigned int bus, unsigned int devfn, unsigned int reg, uint8_t value) +{ + return sal_pci_config_write(PCI_SAL_ADDRESS(0,bus, 0, devfn, reg), 1, value); +} + +int pcibios_write_config_word(unsigned int bus, unsigned int devfn, unsigned int reg, uint16_t value) +{ + return sal_pci_config_write(PCI_SAL_ADDRESS(0,bus, 0, devfn, reg), 2, value); +} + +int pcibios_write_config_dword(unsigned int bus, unsigned int devfn, unsigned int reg, uint32_t value) +{ + return sal_pci_config_write(PCI_SAL_ADDRESS(0,bus, 0, devfn, reg), 4, value); +} + +/* So far I have not see a non-zero PCI_BUS_OFFSET + * and an AML parser to get it much to much trouble. + */ +#ifndef PCI_BUS_OFFSET +#define PCI_BUS_OFFSET 0 +#endif + +unsigned long pcibios_bus_base(unsigned int bus) +{ + return PCI_BUS_OFFSET; +} + +void find_pci(int type, struct pci_device *dev) +{ + /* Should I check for sal functions being present? */ + return scan_pci_bus(type, dev); +} + diff --git a/src/arch/ia64/core/reloc.S b/src/arch/ia64/core/reloc.S new file mode 100644 index 00000000..31689bfb --- /dev/null +++ b/src/arch/ia64/core/reloc.S @@ -0,0 +1,133 @@ +/* reloc.S - position independent IA-64 ELF shared object relocator + Copyright (C) 1999 Hewlett-Packard Co. + Contributed by David Mosberger <davidm@hpl.hp.com>. + Copyright (C) 2002 Eric Biederman sponsored by Linux Networx + + This file is part of etherboot. + This file was derived from reloc_ia64.S from GNU-EFI, the GNU EFI development environment. + + GNU EFI 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, or (at your option) + any later version. + + GNU EFI 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 GNU EFI; see the file COPYING. If not, write to the Free + Software Foundation, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +/* + * This is written in assembly because the entire code needs to be position + * independent. Note that the compiler does not generate code that's position + * independent by itself because it relies on the global offset table being + * relocated. + * + * This code assumes the code was compiled with -mconstant-gp -mauto-pic -static -shared + * Which generates position independent code, but not position indepedent data. + * This code assumes in the linker script the rela entries are bracked with: + * _rela, _erela and _rela_size gives the total size of the rela entries. + * This gives a much smaller binary than when compiled as a true shared object. + * + * This code assumes the original shared object was initially relocated, + * So that it only needs to apply changes for the new address the code is linked + * at. + */ + .text + .psr abi64 + .psr lsb + .lsb + + +#define ST_VALUE_OFF 8 /* offset of st_value in elf sym */ + +#define RET_SUCCESS 0 +#define RET_LOAD_ERROR 1 + +#define R_IA64_NONE 0 +#define R_IA64_REL64MSB 0x6e +#define R_IA64_REL64LSB 0x6f +#define R_IA64_DIR64MSB 0x26 +#define R_IA64_DIR64LSB 0x27 +#define R_IA64_FPTR64MSB 0x46 +#define R_IA64_FPTR64LSB 0x47 + +#define delta in0 /* Chaing in load address (address of .text) */ + +#define ldbase r15 +#define target r16 +#define val r17 +#define rela r18 +#define relasz r19 +#define relaent r20 +#define addr r21 +#define r_info r22 +#define r_offset r23 +#define r_type r25 + +#define Pmore p6 + +#define Pnone p6 +#define Prel p7 +#define Pdir p8 + + + .global _relocate +_relocate: + alloc r2=ar.pfs,1,0,0,0 + add rela=@gprel(_rela),gp + add ldbase=@gprel(_text),gp + add r3=@ltoff(_rela_size),gp + ;; + ld8 relasz = [r3] + mov relaent=24 + br.sptk.few apply_relocs + +apply_loop: + ld8 r_offset = [rela] + add addr = 8,rela + sub relasz = relasz,relaent + + ;; + ld8 r_info = [addr], 8 + ;; + add target = ldbase, r_offset + add rela = rela,relaent + extr.u r_type = r_info, 0, 32 + ;; + cmp.eq Pnone,p0 = R_IA64_NONE, r_type + cmp.eq Prel,p0 = R_IA64_REL64LSB, r_type + cmp.eq Pdir,p0 = R_IA64_DIR64LSB, r_type /* Needed? */ + ;; +(Pnone) br.cond.sptk.few apply_relocs +(Prel) br.cond.sptk.few apply_REL64 +(Pdir) br.cond.sptk.few apply_DIR64 /* Needed? */ + ;; + +apply_error: + mov r8 = RET_LOAD_ERROR + br.ret.sptk.few rp + +apply_REL64: +apply_DIR64: + ld8 val = [target] + ;; + add val = val, delta + ;; + st8 [target] = val + ;; + /* fall through to apply_relocs */ +apply_relocs: + cmp.ltu Pmore,p0=0,relasz +(Pmore) br.cond.sptk.few apply_loop + ;; + + mov r8 = RET_SUCCESS + br.ret.sptk.few rp + + .size _relocate, . - _relocate + .endp _relocate diff --git a/src/arch/ia64/core/relocate_to.S b/src/arch/ia64/core/relocate_to.S new file mode 100644 index 00000000..37b5c9f8 --- /dev/null +++ b/src/arch/ia64/core/relocate_to.S @@ -0,0 +1,92 @@ + /* Temporarily ignore the stack, as I am still using efi's stack */ + +#define newbase in0 +#define base loc2 +#define newgp loc3 +#define len loc4 + + .explicit + .globl relocate_to + .proc relocate_to +relocate_to: + /* In incoming variable the new base addres of etherboot. */ + alloc loc0=ar.pfs,1,5,3,0 /* in, local, out, rotating */ + mov loc1=rp + + /* Compute the current location of _text */ + /* Compute the new gp value */ + add base=@gprel(_text),gp + add len =@gprel(_end),gp + movl newgp=@gprel(_text) + ;; + sub newgp=newbase,newgp /* gp = _text - @gprel(_text) */ + sub len=len,base + ;; + + /* Copy etherboot to the new location */ + mov out0=newbase + mov out1=base + mov out2=len + br.call.sptk.few rp=memcpy + ;; + + /* Jump to my __relocate_to in the new location */ + movl r14=@gprel(__relocate_to) + ;; + add r14=r14,newgp + ;; + mov b6=r14 + ;; + br.cond.sptk.few b6 + ;; +__relocate_to: + /* I am at the new location set the newgp as the default */ + mov gp=newgp + ;; + /* New apply relocations to the new copy */ + sub out0=newbase,base + br.call.sptk.few rp=_relocate + ;; + + /* Lookup restart_etherboot */ + add out0=@gprel(restart_etherboot),gp + ;; + + /* Adjust the gp and return address. + * NOTE: This only works when the setjmp can modify it's caller's gp + * address. Essentially this means etherboot must be compiled with + * compiled with -mconstant-gp, though an inline version of setjmp might work. + */ + add r14=0x40,out0 + add r16=0x08,out0 + ;; + ld8 r15=[r14] + ld8 r17=[r16] + ;; + sub r15=r15,base + sub r17=r17,base + ;; + add r15=r15,newbase + add r17=r17,newbase + ;; + st8 [r14]=r15 + st8 [r16]=r17 + ;; + mov out1=256 + br.call.sptk.few rp=longjmp + + /* And just in case lonjmp returns... */ + + + /* Adjust my return address and return */ + sub loc1=loc1,base + ;; + add loc1=loc1,newbase + ;; + mov ar.pfs=loc0 + mov rp=loc1 + ;; + br.ret.sptk.few rp + + .size relocate_to, . - relocate_to + .endp relocate_to diff --git a/src/arch/ia64/core/sal.c b/src/arch/ia64/core/sal.c new file mode 100644 index 00000000..aa040f80 --- /dev/null +++ b/src/arch/ia64/core/sal.c @@ -0,0 +1,278 @@ +#include "etherboot.h" +#include "sal.h" + +struct sal_entry_base { + uint8_t entry_type; +#define SAL_TYPE_ENTRYPOINT 0 +#define SAL_TYPE_MEMORY 1 +#define SAL_TYPE_PLATFORM_FEATURES 2 +#define SAL_TYPE_TRANSLATION_REGISTER 3 +#define SAL_TYPE_PURGE_DOMAIN 4 +#define SAL_TYPE_AP_WAKEUP 5 +}; + +struct sal_entry_point_descriptor { + uint8_t entry_type; + uint8_t reserved[7]; + uint64_t pal_proc; + uint64_t sal_proc; + uint64_t sal_gp; + uint8_t reserved2[16]; +}; + +struct sal_memory_descriptor { + uint8_t entry_type; + uint8_t sal_needs_virt_mapping; + uint8_t mem_attr; +#define MEM_ATTR_WB 0 +#define MEM_ATTR_UC 8 +#define MEM_ATTR_UCE 9 +#define MEM_ATTR_WC 10 + uint8_t access_rights; + uint8_t mem_attr_support; +#define MEM_ATTR_SUPPORTS_WB 1 +#define MEM_ATTR_SUPPORTS_UC 2 +#define MEM_ATTR_SUPPORTS_UCE 4 +#define MEM_ATTR_SUPPORTS_WC 8 + uint8_t reserved; + uint8_t mem_type; +#define MEM_TYPE_RAM 0 +#define MEM_TYPE_MIO 1 +#define MEM_TYPE_SAPIC 2 +#define MEM_TYPE_PIO 3 +#define MEM_TYPE_FIRMWARE 4 +#define MEM_TYPE_BAD_RAM 9 +#define MEM_TYPE_BLACK_HOLE 10 + uint8_t mem_usage; +#define MEM_USAGE_UNSPECIFIED 0 +#define MEM_USAGE_PAL_CODE 1 +#define MEM_USAGE_BOOT_SERVICES_CODE 2 +#define MEM_USAGE_BOOT_SERVICES_DATA 3 +#define MEM_USAGE_RUNTIME_SERVICES_CODE 4 +#define MEM_USAGE_RUNTIME_SERVICES_DATA 5 +#define MEM_USAGE_IA32_OPTION_ROM 6 +#define MEM_USAGE_IA32_SYSTEM_ROM 7 +#define MEM_USAGE_ACPI_RECLAIM_MEMORY 8 +#define MEM_USAGE_ACPI_NVS_MEMORY 9 +#define MEM_USAGE_SAL_PMI_CODE 10 +#define MEM_USAGE_SAL_PMI_DATA 11 +#define MEM_USAGE_FIRMWARE_RESERVED_RAM 12 + +#define MEM_USAGE_CPU_TO_IO 0 + uint64_t phys_address; + uint32_t pages; /* In 4k pages */ + uint32_t reserved2; + uint8_t oem_reserved[8]; +}; + +struct sal_platform_features { + uint8_t entry_type; + uint8_t feature_list; +#define SAL_FEATURE_BUS_LOCK 1 +#define SAL_FEATURE_PLATFORM_REDIRECTION_HINT 2 +#define SAL_FEATURE_PROCESSOR_REDIRECTION_HINT 3 + uint8_t reserved[14]; +}; +struct sal_translation_register { + uint8_t entry_type; + uint8_t tr_type; +#define SAL_ITR 0 +#define SAL_DTR 1 + uint8_t tr_number; + uint8_t reserved[5]; + uint64_t virtual_address; + uint64_t page_size; + uint8_t reserved2[8]; +}; + +struct sal_purge_translation_cache_coherency_domain { + uint8_t entry_type; + uint8_t reserved[3]; + uint32_t coherence_domain_count; + uint64_t coherence_domain_addr; +}; + +struct sal_ap_wakeup_descriptor { + uint8_t entry_type; + uint8_t wakeup_mechanism; + uint8_t reserved[6]; + uint64_t interrupt; +}; + +struct sal_entry { + union { + struct sal_entry_base base; + struct sal_entry_point_descriptor entry_point; + struct sal_memory_descriptor mem; + struct sal_platform_features features; + struct sal_translation_register tr; + struct sal_purge_translation_cache_coherency_domain purge; + struct sal_ap_wakeup_descriptor ap_wakeup; + }; +}; + +struct sal_system_table { + uint8_t signature[4]; /* SST_ */ + uint32_t table_length; + + uint16_t sal_rev; + uint16_t entry_count; + uint8_t checksum; + uint8_t reserved1[7]; + uint16_t sal_a_version; + uint16_t sal_b_version; + + uint8_t oem_id[32]; + uint8_t product_id[32]; + uint8_t reserved2[8]; + struct sal_entry entry[0]; +}; + +static struct sal_system_table *sal; +struct fptr sal_entry; + +int parse_sal_system_table(void *table) +{ + struct sal_system_table *salp = table; + uint8_t *ptr; + uint8_t checksum; + struct sal_entry *entry; + unsigned i; + if (memcmp(salp->signature, "SST_", 4) != 0) { + return 0; + } + ptr = table; + checksum = 0; + for(i = 0; i < salp->table_length; i++) { + checksum += ptr[i]; + } + if (checksum != 0) { + return 0; + } +#if 0 + printf("SALA: %hx SALB: %hx\n", + salp->sal_a_version, + salp->sal_b_version); + printf("SAL OEM: "); + for(i = 0; i < sizeof(salp->oem_id); i++) { + uint8_t ch = salp->oem_id[i]; + if (ch == 0) + break; + printf("%c", ch); + } + printf("\n"); + + printf("SAL PRODUCT: "); + for(i = 0; i < sizeof(salp->product_id); i++) { + uint8_t ch = salp->product_id[i]; + if (ch == 0) + break; + printf("%c", ch); + } + printf("\n"); +#endif + sal = salp; + pal_entry.entry = 0; + pal_entry.gp = 0; + sal_entry.entry = 0; + sal_entry.gp = 0; + entry = sal->entry; + i = 0; + while(i < salp->entry_count) { + unsigned long size = 0; + + switch(entry->base.entry_type) { + case SAL_TYPE_ENTRYPOINT: + size = sizeof(entry->entry_point); + pal_entry.entry = entry->entry_point.pal_proc; + sal_entry.entry = entry->entry_point.sal_proc; + sal_entry.gp = entry->entry_point.sal_gp; + break; + case SAL_TYPE_MEMORY: + size = sizeof(entry->mem); + break; + case SAL_TYPE_PLATFORM_FEATURES: + size = sizeof(entry->features); + break; + case SAL_TYPE_TRANSLATION_REGISTER: + size = sizeof(entry->tr); + break; + case SAL_TYPE_PURGE_DOMAIN: + size = sizeof(entry->purge); + break; + case SAL_TYPE_AP_WAKEUP: + size = sizeof(entry->ap_wakeup); + break; + default: + break; + } + entry = (struct sal_entry *)(((char *)entry) + size); + i++; + } + return 1; +} + +#define SAL_SET_VECTORS 0x01000000 +#define SAL_GET_STATE_INFO 0x01000001 +#define SAL_GET_STATE_INFO_SIZE 0x01000002 +#define SAL_CLEAR_STATE_INFO 0x01000003 +#define SAL_MC_RENDEZ 0x01000004 +#define SAL_MC_SET_PARAMS 0x01000005 +#define SAL_REGISTER_PHYSICAL_ADDR 0x01000006 + +#define SAL_CACHE_FLUSH 0x01000008 +#define SAL_CACHE_INIT 0x01000009 +#define SAL_PCI_CONFIG_READ 0x01000010 +#define SAL_PCI_CONFIG_WRITE 0x01000011 +#define SAL_FREQ_BASE 0x01000012 + +#define SAL_UPDATE_PAL 0x01000020 + +/* + * Now define a couple of inline functions for improved type checking + * and convenience. + */ +long sal_freq_base (unsigned long which, unsigned long *ticks_per_second, + unsigned long *drift_info) +{ + struct { + long status; + unsigned long ticks_per_second; + unsigned long drift_info; + } result, __call(void *,...); + + result = __call(&sal_entry, SAL_FREQ_BASE, which, 0, 0, 0, 0, 0, 0); + + *ticks_per_second = result.ticks_per_second; + *drift_info = result.drift_info; + return result.status; +} + + + +/* Read from PCI configuration space */ +long sal_pci_config_read ( + unsigned long pci_config_addr, unsigned long size, unsigned long *value) +{ + struct { + long status; + unsigned long value; + } result, __call(void *,...); + + result = __call(&sal_entry, SAL_PCI_CONFIG_READ, pci_config_addr, size, 0, 0, 0, 0, 0); + if (value) + *value = result.value; + return result.status; +} + +/* Write to PCI configuration space */ +long sal_pci_config_write ( + unsigned long pci_config_addr, unsigned long size, unsigned long value) +{ + struct { + long status; + } result, __call(void *,...); + + result = __call(&sal_entry, SAL_PCI_CONFIG_WRITE, pci_config_addr, size, value, 0, 0, 0, 0); + return result.status; +} diff --git a/src/arch/ia64/core/setjmp.S b/src/arch/ia64/core/setjmp.S new file mode 100644 index 00000000..f3e57678 --- /dev/null +++ b/src/arch/ia64/core/setjmp.S @@ -0,0 +1,173 @@ +/* Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. + Contributed by David Mosberger-Tang <davidm@hpl.hp.com>. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. + + The layout of the jmp_buf is as follows. This is subject to change + and user-code should never depend on the particular layout of + jmp_buf! + + + offset: description: + ------- ------------ + 0x000 stack pointer (r12) ; unchangeable (see _JMPBUF_UNWINDS) + 0x008 r1 (gp) + 0x010 caller's unat + 0x018 fpsr + 0x020 r4 + 0x028 r5 + 0x030 r6 + 0x038 r7 + 0x040 rp (b0) + 0x048 b1 + 0x050 b2 + 0x058 b3 + 0x060 b4 + 0x068 b5 + 0x070 ar.pfs + 0x078 ar.lc + 0x080 pr + 0x088 ar.bsp ; unchangeable (see __longjmp.S) + 0x090 ar.unat + 0x098 &__jmp_buf ; address of the jmpbuf (needed to locate NaT bits in unat) + 0x0a0 f2 + 0x0b0 f3 + 0x0c0 f4 + 0x0d0 f5 + 0x0e0 f16 + 0x0f0 f17 + 0x100 f18 + 0x110 f19 + 0x120 f20 + 0x130 f21 + 0x130 f22 + 0x140 f23 + 0x150 f24 + 0x160 f25 + 0x170 f26 + 0x180 f27 + 0x190 f28 + 0x1a0 f29 + 0x1b0 f30 + 0x1c0 f31 */ + + + /* The following two entry points are the traditional entry points: */ + + .text + .global setjmp + .proc setjmp +setjmp: + alloc r8=ar.pfs,2,0,0,0 + mov in1=1 + br.cond.sptk.many __sigsetjmp + .size setjmp, . - setjmp + .endp setjmp + + /* __sigsetjmp(__jmp_buf buf, int savemask) */ + +__sigsetjmp: +#if 0 + .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(2) +#endif + alloc loc1=ar.pfs,2,2,2,0 + mov r16=ar.unat + ;; + mov r17=ar.fpsr + mov r2=in0 + add r3=8,in0 + ;; + st8.spill.nta [r2]=sp,16 // r12 (sp) + st8.spill.nta [r3]=gp,16 // r1 (gp) + ;; + st8.nta [r2]=r16,16 // save caller's unat + st8.nta [r3]=r17,16 // save fpsr + add r8=0xa0,in0 + ;; + st8.spill.nta [r2]=r4,16 // r4 + st8.spill.nta [r3]=r5,16 // r5 + add r9=0xb0,in0 + ;; + stf.spill.nta [r8]=f2,32 + stf.spill.nta [r9]=f3,32 + mov loc0=rp + .body + ;; + stf.spill.nta [r8]=f4,32 + stf.spill.nta [r9]=f5,32 + mov r17=b1 + ;; + stf.spill.nta [r8]=f16,32 + stf.spill.nta [r9]=f17,32 + mov r18=b2 + ;; + stf.spill.nta [r8]=f18,32 + stf.spill.nta [r9]=f19,32 + mov r19=b3 + ;; + stf.spill.nta [r8]=f20,32 + stf.spill.nta [r9]=f21,32 + mov r20=b4 + ;; + stf.spill.nta [r8]=f22,32 + stf.spill.nta [r9]=f23,32 + mov r21=b5 + ;; + stf.spill.nta [r8]=f24,32 + stf.spill.nta [r9]=f25,32 + mov r22=ar.lc + ;; + stf.spill.nta [r8]=f26,32 + stf.spill.nta [r9]=f27,32 + mov r24=pr + ;; + stf.spill.nta [r8]=f28,32 + stf.spill.nta [r9]=f29,32 + ;; + stf.spill.nta [r8]=f30 + stf.spill.nta [r9]=f31 + + st8.spill.nta [r2]=r6,16 // r6 + st8.spill.nta [r3]=r7,16 // r7 + ;; + mov r23=ar.bsp + mov r25=ar.unat + mov out0=in0 + + st8.nta [r2]=loc0,16 // b0 + st8.nta [r3]=r17,16 // b1 + mov out1=in1 + ;; + st8.nta [r2]=r18,16 // b2 + st8.nta [r3]=r19,16 // b3 + ;; + st8.nta [r2]=r20,16 // b4 + st8.nta [r3]=r21,16 // b5 + ;; + st8.nta [r2]=loc1,16 // ar.pfs + st8.nta [r3]=r22,16 // ar.lc + ;; + st8.nta [r2]=r24,16 // pr + st8.nta [r3]=r23,16 // ar.bsp + ;; + st8.nta [r2]=r25 // ar.unat + st8.nta [r3]=in0 // &__jmp_buf + mov r8=0 + mov rp=loc0 + mov ar.pfs=loc1 + br.ret.sptk.many rp + + .endp __sigsetjmp diff --git a/src/arch/ia64/core/start.S b/src/arch/ia64/core/start.S new file mode 100644 index 00000000..72b94ce1 --- /dev/null +++ b/src/arch/ia64/core/start.S @@ -0,0 +1,28 @@ + .text + .align 4 + .proc _start + .globl _start +_start: + { + alloc loc0 = ar.pfs,1,2,1,0 /* in, local, out, rotating */ + mov loc1 = rp + mov r14 = ip /* Get the address of _start */ + } + movl r15 = @gprel(_start) + ;; + sub gp = r14,r15 + ;; + rsm psr.i /* disable interrupts */ + ;; + add out0 = @gprel(_text),gp + br.call.sptk.few rp = _relocate + ;; + cmp.eq p6,p7 = r0,r8 /* r8 == SUCCESS? */ + mov ar.pfs = loc0 + mov rp = loc1 + ;; +(p6) br.cond.sptk.few main +(p7) br.ret.sptk.few rp + + .size _start, . - _start + .endp _start diff --git a/src/arch/ia64/drivers/net/undi_nii.c b/src/arch/ia64/drivers/net/undi_nii.c new file mode 100644 index 00000000..c1aabf00 --- /dev/null +++ b/src/arch/ia64/drivers/net/undi_nii.c @@ -0,0 +1,1079 @@ +#include "efi/efi.h" +#include "etherboot.h" +#include "isa.h" +#include "dev.h" +#include "nic.h" +#include "timer.h" + +#warning "Place the declaraction of __call someplace more appropriate\n" +extern EFI_STATUS __call(void *,...); +#warning "Place a declaration of lookup_efi_nic somewhere useful" +EFI_NETWORK_INTERFACE_IDENTIFIER_INTERFACE *lookup_efi_nic(int index); + +struct sw_undi { + uint8_t signature[4]; + uint8_t len; + uint8_t fudge; + uint8_t rev; + uint8_t ifcnt; + uint8_t major; + uint8_t minor; + uint16_t reserved1; + uint32_t implementation; +#define UNDI_IMP_CMD_COMPLETE_INT_SUPPORTED 0x00000001 +#define UNDI_IMP_PACKET_RX_INT_SUPPORTED 0x00000002 +#define UNDI_IMP_TX_COMPLETE_INT_SUPPORTED 0x00000004 +#define UNDI_IMP_SOFTWARE_INT_SUPPORTED 0x00000008 +#define UNDI_IMP_FILTERED_MULTICAST_RX_SUPPORTED 0x00000010 +#define UNDI_IMP_BROADCAST_RX_SUPPORTED 0x00000020 +#define UNDI_IMP_PROMISCUOUS_RX_SUPPORTED 0x00000040 +#define UNDI_IMP_PROMISCUOUS_MULTICAST_RX_SUPPORTED 0x00000080 +#define UNDI_IMP_STATION_ADDR_SETTABLE 0x00000100 +#define UNDI_IMP_STATISTICS_SUPPORTED 0x00000200 +#define UNDI_IMP_NVDATA_SUPPORT_MASK 0x00000C00 +#define UNDI_IMP_NVDATA_NOT_AVAILABLE 0x00000000 +#define UNDI_IMP_NVDATA_READ_ONLY 0x00000400 +#define UNDI_IMP_NVDATA_SPARSE_WRITEABLE 0x00000800 +#define UNDI_IMP_NVDATA_BULK_WRITEABLE 0x00000C00 +#define UNDI_IMP_MULTI_FRAME_SUPPORTED 0x00001000 +#define UNDI_IMP_CMD_QUEUE_SUPPORTED 0x00002000 +#define UNDI_IMP_CMD_LINK_SUPPORTED 0x00004000 +#define UNDI_IMP_FRAG_SUPPORTED 0x00008000 +#define UNDI_IMP_64BIT_DEVICE 0x00010000 +#define UNDI_IMP_SW_VIRT_ADDR 0x40000000 +#define UNDI_IMP_HW_UNDI 0x80000000 + uint64_t entry_point; + uint8_t reserved2[3]; + uint8_t bus_type_cnt; + uint32_t bus_type[0]; +}; + +struct cdb { + uint16_t op_code; +#define CDB_OP_GET_STATE 0x0000 +#define CDB_OP_START 0x0001 +#define CDB_OP_STOP 0x0002 +#define CDB_OP_GET_INIT_INFO 0x0003 +#define CDB_OP_GET_CONFIG_INFO 0x0004 +#define CDB_OP_INITIALIZE 0x0005 +#define CDB_OP_RESET 0x0006 +#define CDB_OP_SHUTDOWN 0x0007 +#define CDB_OP_INTERRUPT_ENABLES 0x0008 +#define CDB_OP_RECEIVE_FILTERS 0x0009 +#define CDB_OP_STATION_ADDRESS 0x000a +#define CDB_OP_STATISTICS 0x000b +#define CDB_OP_MCAST_IP_TO_MAC 0x000c +#define CDB_OP_NVDATA 0x000d +#define CDB_OP_GET_STATUS 0x000e +#define CDB_OP_FILL_HEADER 0x000f +#define CDB_OP_TRANSMIT 0x0010 +#define CDB_OP_RECEIVE 0x0011 + uint16_t op_flags; +#define CDB_OPFLAGS_NOT_USED 0x0000 +/* Initialize */ +#define CDB_OPFLAGS_INIT_CABLE_DETECT_MASK 0x0001 +#define CDB_OPFLAGS_INIT_DETECT_CABLE 0x0000 +#define CDB_OPFLAGS_INIT_DO_NOT_DETECT_CABLE 0x0001 +/* Reset */ +#define CDB_OPFLAGS_RESET_DISABLE_INTERRUPTS 0x0001 +#define CDB_OPFLAGS_RESET_DISABLE_FILTERS 0x0002 +/* Interrupt Enables */ +#define CDB_OPFLAGS_INTERRUPT_OPMASK 0xc000 +#define CDB_OPFLAGS_INTERRUPT_ENABLE 0x8000 +#define CDB_OPFLAGS_INTERRUPT_DISABLE 0x4000 +#define CDB_OPFLAGS_INTERRUPT_READ 0x0000 +#define CDB_OPFLAGS_INTERRUPT_RECEIVE 0x0001 +#define CDB_OPFLAGS_INTERRUPT_TRANSMIT 0x0002 +#define CDB_OPFLAGS_INTERRUPT_COMMAND 0x0004 +#define CDB_OPFLAGS_INTERRUPT_SOFTWARE 0x0008 +/* Receive Filters */ +#define CDB_OPFLAGS_RECEIVE_FILTER_OPMASK 0xc000 +#define CDB_OPFLAGS_RECEIVE_FILTER_ENABLE 0x8000 +#define CDB_OPFLAGS_RECEIVE_FILTER_DISABLE 0x4000 +#define CDB_OPFLAGS_RECEIVE_FILTER_READ 0x0000 +#define CDB_OPFLAGS_RECEIVE_FILTER_RESET_MCAST_LIST 0x2000 +#define CDB_OPFLAGS_RECEIVE_FILTER_UNICAST 0x0001 +#define CDB_OPFLAGS_RECEIVE_FILTER_BROADCAST 0x0002 +#define CDB_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST 0x0004 +#define CDB_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS 0x0008 +#define CDB_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST 0x0010 +/* Station Address */ +#define CDB_OPFLAGS_STATION_ADDRESS_READ 0x0000 +#define CDB_OPFLAGS_STATION_ADDRESS_WRITE 0x0000 +#define CDB_OPFLAGS_STATION_ADDRESS_RESET 0x0001 +/* Statistics */ +#define CDB_OPFLAGS_STATISTICS_READ 0x0000 +#define CDB_OPFLAGS_STATISTICS_RESET 0x0001 +/* MCast IP to MAC */ +#define CDB_OPFLAGS_MCAST_IP_TO_MAC_OPMASK 0x0003 +#define CDB_OPFLAGS_MCAST_IPV4_TO_MAC 0x0000 +#define CDB_OPFLAGS_MCAST_IPV6_TO_MAC 0x0001 +/* NvData */ +#define CDB_OPFLAGS_NVDATA_OPMASK 0x0001 +#define CDB_OPFLAGS_NVDATA_READ 0x0000 +#define CDB_OPFLAGS_NVDATA_WRITE 0x0001 +/* Get Status */ +#define CDB_OPFLAGS_GET_INTERRUPT_STATUS 0x0001 +#define CDB_OPFLAGS_GET_TRANSMITTED_BUFFERS 0x0002 +/* Fill Header */ +#define CDB_OPFLAGS_FILL_HEADER_OPMASK 0x0001 +#define CDB_OPFLAGS_FILL_HEADER_FRAGMENTED 0x0001 +#define CDB_OPFLAGS_FILL_HEADER_WHOLE 0x0000 +/* Transmit */ +#define CDB_OPFLAGS_SWUNDI_TRANSMIT_OPMASK 0x0001 +#define CDB_OPFLAGS_TRANSMIT_BLOCK 0x0001 +#define CDB_OPFLAGS_TRANSMIT_DONT_BLOCK 0x0000 + +#define CDB_OPFLAGS_TRANSMIT_OPMASK 0x0002 +#define CDB_OPFLAGS_TRANSMIT_FRAGMENTED 0x0002 +#define CDB_OPFLAGS_TRANSMIT_WHOLE 0x0000 +/* Receive */ + uint16_t cpb_size; + uint16_t db_size; + uint64_t cpb_addr; + uint64_t db_addr; + uint16_t stat_code; +#define CDB_STATCODE_INITIALIZE 0x0000 +/* Common stat_code values */ +#define CDB_STATCODE_SUCCESS 0x0000 +#define CDB_STATCODE_INVALID_CDB 0x0001 +#define CDB_STATCODE_INVALID_CPB 0x0002 +#define CDB_STATCODE_BUSY 0x0003 +#define CDB_STATCODE_QUEUE_FULL 0x0004 +#define CDB_STATCODE_ALREADY_STARTED 0x0005 +#define CDB_STATCODE_NOT_STARTED 0x0006 +#define CDB_STATCODE_NOT_SHUTDOWN 0x0007 +#define CDB_STATCODE_ALREADY_INITIALIZED 0x0008 +#define CDB_STATCODE_NOT_INITIALIZED 0x0009 +#define CDB_STATCODE_DEVICE_FAILURE 0x000A +#define CDB_STATCODE_NVDATA_FAILURE 0x000B +#define CDB_STATCODE_UNSUPPORTED 0x000C +#define CDB_STATCODE_BUFFER_FULL 0x000D +#define CDB_STATCODE_INVALID_PARAMETER 0x000E +#define CDB_STATCODE_INVALID_UNDI 0x000F +#define CDB_STATCODE_IPV4_NOT_SUPPORTED 0x0010 +#define CDB_STATCODE_IPV6_NOT_SUPPORTED 0x0011 +#define CDB_STATCODE_NOT_ENOUGH_MEMORY 0x0012 +#define CDB_STATCODE_NO_DATA 0x0013 + + uint16_t stat_flags; +#define CDB_STATFLAGS_INITIALIZE 0x0000 +/* Common stat_flags */ +#define CDB_STATFLAGS_STATUS_MASK 0xc000 +#define CDB_STATFLAGS_COMMAND_COMPLETE 0xc000 +#define CDB_STATFLAGS_COMMAND_FAILED 0x8000 +#define CDB_STATFLAGS_COMMAND_QUEUED 0x4000 +/* Get State */ +#define CDB_STATFLAGS_GET_STATE_MASK 0x0003 +#define CDB_STATFLAGS_GET_STATE_INITIALIZED 0x0002 +#define CDB_STATFLAGS_GET_STATE_STARTED 0x0001 +#define CDB_STATFLAGS_GET_STATE_STOPPED 0x0000 +/* Start */ +/* Get Init Info */ +#define CDB_STATFLAGS_CABLE_DETECT_MASK 0x0001 +#define CDB_STATFLAGS_CABLE_DETECT_NOT_SUPPORTED 0x0000 +#define CDB_STATFLAGS_CABLE_DETECT_SUPPORTED 0x0001 +/* Initialize */ +#define CDB_STATFLAGS_INITIALIZED_NO_MEDIA 0x0001 +/* Reset */ +#define CDB_STATFLAGS_RESET_NO_MEDIA 0x0001 +/* Shutdown */ +/* Interrupt Enables */ +#define CDB_STATFLAGS_INTERRUPT_RECEIVE 0x0001 +#define CDB_STATFLAGS_INTERRUPT_TRANSMIT 0x0002 +#define CDB_STATFLAGS_INTERRUPT_COMMAND 0x0004 +/* Receive Filters */ +#define CDB_STATFLAGS_RECEIVE_FILTER_UNICAST 0x0001 +#define CDB_STATFLAGS_RECEIVE_FILTER_BROADCAST 0x0002 +#define CDB_STATFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST 0x0004 +#define CDB_STATFLAGS_RECEIVE_FILTER_PROMISCUOUS 0x0008 +#define CDB_STATFLAGS_RECEIVE_FILTER_ALL_MULTICAST 0x0010 +/* Statistics */ +/* MCast IP to MAC */ +/* NvData */ +/* Get Status */ +#define CDB_STATFLAGS_GET_STATUS_INTERRUPT_MASK 0x000F +#define CDB_STATFLAGS_GET_STATUS_NO_INTERRUPTS 0x0000 +#define CDB_STATFLAGS_GET_STATUS_RECEIVE 0x0001 +#define CDB_STATFLAGS_GET_STATUS_TRANSMIT 0x0002 +#define CDB_STATFLAGS_GET_STATUS_COMMAND 0x0004 +#define CDB_STATFLAGS_GET_STATUS_SOFTWARE 0x0008 +#define CDB_STATFLAGS_GET_STATUS_TXBUF_QUEUE_EMPTY 0x0010 +#define CDB_STATFLAGS_GET_STATUS_NO_TXBUFS_WRITTEN 0x0020 +/* Fill Header */ +/* Transmit */ +/* Receive */ + uint16_t ifnum; +#define CDB_IFNUM_START 0x0000 +#define CDB_IFNUM_INVALID 0x0000 + uint16_t control; +#define CDB_CONTROL_QUEUE_IF_BUSY 0x0002 + +#define CDB_CONTROL_LINK 0x0001 +#define CDB_CONTROL_LAST_CDB_IN_LIST 0x0000 +}; + +#define UNDI_MAC_LENGTH 32 +typedef uint8_t undi_mac_addr[UNDI_MAC_LENGTH]; +typedef uint16_t undi_media_protocol; +typedef uint8_t undi_frame_type; +#define UNDI_FRAME_TYPE_NONE 0x00 +#define UNDI_FRAME_TYPE_UNICAST 0x01 +#define UNDI_FRAME_TYPE_BROADCAST 0x02 +#define UNDI_FRAME_TYPE_MULTICAST 0x03 +#define UNDI_FRAME_TYPE_PROMISCUOUS 0x04 + +#define UNDI_MAX_XMIT_BUFFERS 32 +#define UNDI_MAX_MCAST_ADDRESS_CNT 8 + +#define UNDI_BUS_TYPE(a,b,c,d) \ + ((((d) & 0xff) << 24) | \ + (((c) & 0xff) << 16) | \ + (((b) & 0xff) << 8) | \ + (((a) & 0xff) << 0)) + +#define UNDI_BUS_TYPE_PCI UNDI_BUS_TYPE('P','C','I','R') +#define UNDI_BUS_TYPE_PCC UNDI_BUS_TYPE('P','C','C','R') +#define UNDI_BUS_TYPE_USB UNDI_BUS_TYPE('U','S','B','R') +#define UNDI_BUS_TYPE_1394 UNDI_BUS_TYPE('1','3','9','4') + +struct cpb_start { + void *delay; + void *block; + void *virt2phys; + void *mem_io; +} PACKED; + +struct db_init_info { + uint32_t memory_required; + uint32_t frame_data_len; + uint32_t link_speeds[4]; + uint32_t nv_count; + uint16_t nv_width; + uint16_t media_header_len; + uint16_t hw_addr_len; + uint16_t mcast_filter_cnt; + uint16_t tx_buf_cnt; + uint16_t tx_buf_size; + uint16_t rx_buf_cnt; + uint16_t rx_buf_size; + uint8_t if_type; + uint8_t duplex; +#define UNDI_DUPLEX_ENABLE_FULL_SUPPORTED 1 +#define UNDI_DUPLEX_FORCE_FULL_SUPPORTED 2 + uint8_t loopback; +#define UNDI_LOOPBACK_INTERNAL_SUPPORTED 1 +#define UNDI_LOOPBACK_EXTERNAL_SUPPORTED 2 +} PACKED; + + +struct db_pci_config_info { + uint32_t bus_type; + uint16_t bus; + uint8_t device; + uint8_t function; + uint8_t config[256]; +}; +struct db_pcc_config_info { + uint32_t bus_type; + uint16_t bus; + uint8_t device; + uint8_t function; + uint8_t config[256]; +}; +struct db_usb_config_info { + uint32_t bus_type; +}; +struct db_iee1394_config_info { + uint32_t bus_type; +}; +struct db_config_info { + union { + struct db_pci_config_info pci; + struct db_pcc_config_info pcc; + struct db_usb_config_info usb; + struct db_iee1394_config_info iee1394; + }; +}; + +struct cpb_initialize { + uint64_t memory_addr; + uint32_t memory_length; + uint32_t link_speed; + uint16_t tx_buf_cnt; + uint16_t tx_buf_size; + uint16_t rx_buf_cnt; + uint16_t rx_buf_size; + uint8_t duplex; + uint8_t loopback; +} PACKED; + +struct db_initialize { + uint32_t memory_used; + uint16_t tx_buf_cnt; + uint16_t tx_buf_size; + uint16_t rx_buf_cnt; + uint16_t rx_buf_size; +} PACKED; + +struct cpb_station_address { + undi_mac_addr station_addr; +} PACKED; + +struct db_station_address { + undi_mac_addr station_address; + undi_mac_addr broadcast_address; + undi_mac_addr permanent_address; +} PACKED; + +struct cpb_receive_filters { + undi_mac_addr mcast_list[UNDI_MAX_MCAST_ADDRESS_CNT]; +} PACKED; + +struct db_receive_filters { + undi_mac_addr mcast_list[UNDI_MAX_MCAST_ADDRESS_CNT]; +} PACKED; + + +struct db_get_status { + uint32_t rx_frame_len; + uint32_t reserved; + uint64_t tx_buffer[UNDI_MAX_XMIT_BUFFERS]; +} PACKED; + +struct cpb_transmit { + uint64_t frame_addr; + uint32_t data_len; + uint16_t media_header_len; + uint16_t reserved; +} PACKED; + +struct cpb_receive { + uint64_t buffer_addr; + uint32_t buffer_len; + uint32_t reserved; +} PACKED; +struct db_receive { + undi_mac_addr src_addr; + undi_mac_addr dest_addr; + uint32_t frame_len; + undi_media_protocol protocol; + uint16_t media_header_len; + undi_frame_type type; + uint8_t reserved[7]; +} PACKED; +struct fptr { + void *func; + void *gp; +}; + +extern char __gp[]; + +/* Variables */ +static unsigned undi_ifnum; +static void *undi_entry_point; +static struct cdb cdb; +static char buffer[1024*1024]; + +/* SW UNDI callbacks */ +static void undi_udelay(uint64_t microseconds) +{ +#if 0 + printf("undi_udelay(%lx)\n", microseconds); +#endif + if (microseconds < 10) { + microseconds = 10; + } + if (microseconds > 1000) { + mdelay(microseconds/1000); + microseconds%=1000; + } + udelay(microseconds); +} +static struct fptr fptr_undi_udelay = { + .func = &undi_udelay, + .gp = &__gp, +}; +static void undi_block(uint32_t enable __unused) +{ +#if 0 + printf("undi_block(%x)\n", + enable); +#endif + return; +} +static struct fptr fptr_undi_block = { + .func = &undi_block, + .gp = &__gp, +}; +static void undi_virt2phys(uint64_t virtual, uint64_t *ptr) +{ +#if 0 + printf("undi_virt2phys(%lx, %lx)\n", + virtual, ptr); +#endif + *ptr = virt_to_phys((void *)virtual); +} +static struct fptr fptr_undi_virt2phys = { + .func = &undi_virt2phys, + .gp = &__gp, +}; +#define UNDI_IO_READ 0 +#define UNDI_IO_WRITE 1 +#define UNDI_MEM_READ 2 +#define UNDI_MEM_WRITE 3 +static void undi_mem_io(uint8_t read_write, uint8_t len, uint64_t port, uint64_t buf_addr) +{ + printf("undi_mem_io(%hhx, %hhx, %lx, %lx)\n", + read_write, len, port, buf_addr); +#if 0 + if ((read_write == UNDI_IO_READ) && (len == 1)) { + uint8_t *buf = (void *)buf_addr; + *buf = inb(port); + } + else if ((read_write == UNDI_IO_READ) && (len == 2)) { + uint16_t *buf = (void *)buf_addr; + *buf = inw(port); + } + else if ((read_write == UNDI_IO_READ) && (len == 4)) { + uint32_t *buf = (void *)buf_addr; + *buf = inl(port); + } + else if ((read_write == UNDI_IO_READ) && (len == 8)) { + uint64_t *buf = (void *)buf_addr; + *buf = inq(port); + } + else if ((read_write == UNDI_IO_WRITE) && (len == 1)) { + uint8_t *buf = (void *)buf_addr; + outb(*buf, port); + } + else if ((read_write == UNDI_IO_WRITE) && (len == 2)) { + uint16_t *buf = (void *)buf_addr; + outw(*buf, port); + } + else if ((read_write == UNDI_IO_WRITE) && (len == 4)) { + uint32_t *buf = (void *)buf_addr; + outl(*buf, port); + } + else if ((read_write == UNDI_IO_WRITE) && (len == 8)) { + uint64_t *buf = (void *)buf_addr; + outq(*buf, port); + } + else if ((read_write == UNDI_MEM_READ) && (len == 1)) { + uint8_t *buf = (void *)buf_addr; + *buf = readb(port); + } + else if ((read_write == UNDI_MEM_READ) && (len == 2)) { + uint16_t *buf = (void *)buf_addr; + *buf = readw(port); + } + else if ((read_write == UNDI_MEM_READ) && (len == 4)) { + uint32_t *buf = (void *)buf_addr; + *buf = readl(port); + } + else if ((read_write == UNDI_MEM_READ) && (len == 8)) { + uint64_t *buf = (void *)buf_addr; + *buf = readq(port); + } + else if ((read_write == UNDI_MEM_WRITE) && (len == 1)) { + uint8_t *buf = (void *)buf_addr; + writeb(*buf, port); + } + else if ((read_write == UNDI_MEM_WRITE) && (len == 2)) { + uint16_t *buf = (void *)buf_addr; + writew(*buf, port); + } + else if ((read_write == UNDI_MEM_WRITE) && (len == 4)) { + uint32_t *buf = (void *)buf_addr; + writel(*buf, port); + } + else if ((read_write == UNDI_MEM_WRITE) && (len == 8)) { + uint64_t *buf = (void *)buf_addr; + writeq(*buf, port); + } +#endif +} +static struct fptr fptr_undi_mem_io = { + .func = &undi_mem_io, + .gp = &__gp, +}; + +/* static void undi_memio(this, width, address, count, buffer);??? */ + + +/* Wrappers to call the undi functions */ +static int undi_call(struct cdb *cdb) +{ + int result = 1; + cdb->stat_code = CDB_STATCODE_INITIALIZE; + cdb->stat_flags = CDB_STATFLAGS_INITIALIZE; + cdb->ifnum = undi_ifnum; + cdb->control = CDB_CONTROL_LAST_CDB_IN_LIST; + __call(undi_entry_point, cdb); + /* Wait until the command executes... */ + while((cdb->stat_flags & CDB_STATFLAGS_STATUS_MASK) == 0) + ; + if ((cdb->stat_flags & CDB_STATFLAGS_STATUS_MASK) != + CDB_STATFLAGS_COMMAND_COMPLETE) + result = 0; + if (cdb->stat_code != CDB_STATCODE_SUCCESS) + result = 0; + return result; +} + +static int get_state(struct cdb *cdb) +{ + memset(cdb, 0, sizeof(*cdb)); + cdb->op_code = CDB_OP_GET_STATE; + cdb->op_flags = CDB_OPFLAGS_NOT_USED; + return undi_call(cdb); +} + +static int start(struct cdb *cdb) +{ + static struct cpb_start cpb; + memset(&cpb, 0, sizeof(cpb)); + cpb.delay = &fptr_undi_udelay; + cpb.block = &fptr_undi_block; + cpb.virt2phys = &fptr_undi_virt2phys; + cpb.mem_io = &fptr_undi_mem_io; + + memset(cdb, 0, sizeof(*cdb)); + cdb->op_code = CDB_OP_START; + cdb->op_flags = CDB_OPFLAGS_NOT_USED; + cdb->cpb_size = sizeof(cpb); + cdb->cpb_addr = virt_to_phys(&cpb); + + return undi_call(cdb); +} +static int stop(struct cdb *cdb) +{ + memset(cdb, 0, sizeof(*cdb)); + cdb->op_code = CDB_OP_STOP; + cdb->op_flags = CDB_OPFLAGS_NOT_USED; + return undi_call(cdb); +} +static int get_init_info(struct cdb *cdb, struct db_init_info *info) +{ + memset(info, 0, sizeof(*info)); + memset(cdb, 0, sizeof(*cdb)); + cdb->op_code = CDB_OP_GET_INIT_INFO; + cdb->op_flags = CDB_OPFLAGS_NOT_USED; + cdb->db_size = sizeof(*info); + cdb->db_addr = virt_to_phys(info); + return undi_call(cdb); +} + +#if 0 +/* get_config_info crashes broadcoms pxe driver */ +static int get_config_info(struct cdb *cdb, struct db_config_info *info) +{ + memset(info, 0, sizeof(*info)); + memset(cdb, 0, sizeof(*cdb)); + cdb->op_code = CDB_OP_GET_CONFIG_INFO; + cdb->op_flags = CDB_OPFLAGS_NOT_USED; + cdb->db_size = sizeof(*info); + cdb->db_addr = virt_to_phys(info); + return undi_call(cdb); +} +#endif +static int initialize(struct cdb *cdb, int media_detect, + struct cpb_initialize *cpb, struct db_initialize *db) +{ + memset(db, 0, sizeof(*db)); + memset(cdb, 0, sizeof(*cdb)); + cdb->op_code = CDB_OP_INITIALIZE; + cdb->op_flags = media_detect? + CDB_OPFLAGS_INIT_DETECT_CABLE:CDB_OPFLAGS_INIT_DO_NOT_DETECT_CABLE; + cdb->cpb_size = sizeof(*cpb); + cdb->cpb_addr = virt_to_phys(cpb); + cdb->db_size = sizeof(*db); + cdb->db_addr = virt_to_phys(db); + return undi_call(cdb); +} +static int shutdown(struct cdb *cdb) +{ + memset(cdb, 0, sizeof(*cdb)); + cdb->op_code = CDB_OP_SHUTDOWN; + cdb->op_flags = CDB_OPFLAGS_NOT_USED; + return undi_call(cdb); +} +static int station_address_read(struct cdb *cdb, struct db_station_address *db) +{ + memset(db, 0, sizeof(*db)); + memset(cdb, 0, sizeof(*cdb)); + cdb->op_code = CDB_OP_STATION_ADDRESS; + cdb->op_flags = CDB_OPFLAGS_STATION_ADDRESS_READ; + cdb->db_size = sizeof(*db); + cdb->db_addr = virt_to_phys(db); + return undi_call(cdb); +} +static int receive_filters(struct cdb *cdb, unsigned opflags) +{ + /* I currently do not support setting + * or returning the multicast filter list. + * So do not even attempt to pass them. + */ + memset(cdb, 0, sizeof(*cdb)); + cdb->op_code = CDB_OP_RECEIVE_FILTERS; + cdb->op_flags = opflags; + return undi_call(cdb); +} +static int get_transmitted_status(struct cdb *cdb, struct db_get_status *db) +{ + memset(db, 0, sizeof(*db)); + memset(cdb, 0, sizeof(*cdb)); + cdb->op_code = CDB_OP_GET_STATUS; + cdb->op_flags = CDB_OPFLAGS_GET_TRANSMITTED_BUFFERS; + cdb->db_size = sizeof(*db); + cdb->db_addr = virt_to_phys(db); + return undi_call(cdb); +} + +static int transmit(struct cdb *cdb, struct cpb_transmit *cpb) +{ + memset(cdb, 0, sizeof(*cdb)); + cdb->op_code = CDB_OP_TRANSMIT; + cdb->op_flags = CDB_OPFLAGS_TRANSMIT_WHOLE | CDB_OPFLAGS_TRANSMIT_DONT_BLOCK; + cdb->cpb_size = sizeof(*cpb); + cdb->cpb_addr = virt_to_phys(cpb); + return undi_call(cdb); +} + +static int receive(struct cdb *cdb, + struct cpb_receive *cpb, struct db_receive *db) +{ + memset(db, 0, sizeof(*db)); + memset(cdb, 0, sizeof(*cdb)); + cdb->op_code = CDB_OP_RECEIVE; + cdb->op_flags = CDB_OPFLAGS_NOT_USED; + cdb->cpb_size = sizeof(*cpb); + cdb->cpb_addr = virt_to_phys(cpb); + cdb->db_size = sizeof(*db); + cdb->db_addr = virt_to_phys(db); + return undi_call(cdb); +} + +/* The work horse functions */ +static int nic_poll(struct nic *nic ) +{ + int result; + struct cpb_receive cpb; + struct db_receive db; + + memset(&cpb, 0, sizeof(cpb)); + cpb.buffer_addr = virt_to_phys(nic->packet); + cpb.buffer_len = ETH_FRAME_LEN; + result = receive(&cdb, &cpb, &db); + if (result) { + nic->packetlen = db.frame_len; + return 1; + } + else if (cdb.stat_code != CDB_STATCODE_NO_DATA) { + printf("Receive failed: %lx\n", cdb.stat_code); + } + return 0; /* initially as this is called to flush the input */ +} + +static void nic_transmit(struct nic *nic, const char *dest, unsigned int type, + unsigned int size, const char *data) +{ + int result; + static struct { + uint8_t dst_addr[ETH_ALEN]; + uint8_t src_addr[ETH_ALEN]; + uint16_t type; + uint8_t data[ETH_MAX_MTU]; + } packet; + struct cpb_transmit cpb; + struct db_get_status db; + int done; + + /* Build the packet to transmit in my buffer */ + memcpy(&packet.dst_addr, dest, ETH_ALEN); + memcpy(&packet.src_addr, nic->node_addr, ETH_ALEN); + packet.type = htons(type); + memcpy(&packet.data, data, size); + + /* send the packet to destination */ + cpb.frame_addr = virt_to_phys(&packet); + cpb.data_len = ETH_HLEN + size; + cpb.media_header_len = ETH_HLEN; + result = transmit(&cdb, &cpb); + if (!result) { + printf("transmit failed: %lx\n", cdb.stat_code); + return; + } + /* Wait until the packet is actually transmitted, + * indicating it is safe to reuse my trasmit buffer. + */ + done = 0; + while(!done) { + int i; + result = get_transmitted_status(&cdb, &db); + for(i = 0; i < UNDI_MAX_XMIT_BUFFERS; i++) { + if (db.tx_buffer[i] == virt_to_phys(&packet)) { + done = 1; + } + } + } +} + +static void nic_disable(struct dev *dev) +{ + struct nic *nic = (struct nic *)dev; + int result; + result = shutdown(&cdb); + if (!result) { + printf("UNDI nic does not want to shutdown: %x\n", cdb.stat_code); + } + result = stop(&cdb); + if (!result) { + printf("UNDI nic does not want to stop: %x\n", cdb.stat_code); + } + undi_ifnum = 0; + undi_entry_point = 0; +} + +static uint8_t undi_checksum(struct sw_undi *undi) +{ + uint8_t undi_sum, *ptr; + int i; + ptr = (uint8_t *)undi; + undi_sum = 0; + for(i = 0; i < undi->len; i++) { + undi_sum += ((char *)undi)[i]; + } + return undi_sum; +} + +#if 0 +/* Debug functions */ +void print_nii(EFI_NETWORK_INTERFACE_IDENTIFIER_INTERFACE *nii) +{ + printf("NII Revision: %lx\n", nii->Revision); + printf("NII ID: %lx\n", nii->ID); + printf("NII ImageAddr: %lx\n", nii->ImageAddr); + printf("NII ImageSize: %x\n", nii->ImageSize); + printf("NII StringID: %c%c%c%c\n", + nii->StringId[0], nii->StringId[1], nii->StringId[2], nii->StringId[3]); + printf("NII Type: %hhx\n", nii->Type); + printf("NII Version: %d.%d\n", nii->MajorVer, nii->MinorVer); + printf("NII IfNum: %hhx\n", nii->IfNum); + printf("\n"); +} +void print_sw_undi(struct sw_undi *undi) +{ + int i; + printf("UNDI signature: %c%c%c%c\n", + undi->signature[0], undi->signature[1], undi->signature[2], undi->signature[3]); + printf("UNDI len: %hhx\n", undi->len); + printf("UNDI fudge: %hhx\n", undi->fudge); + printf("UNDI rev: %hhx\n", undi->rev); + printf("UNDI ifcnt: %hhx\n", undi->ifcnt); + printf("UNDI version: %d.%d\n", undi->major, undi->minor); + printf("UNDI implementation: %x\n", undi->implementation); + printf("UNDI entry point: %lx\n", undi->entry_point); + printf("UNDI bus type cnt: %d\n", undi->bus_type_cnt); + for(i = 0; i < undi->bus_type_cnt; i++) { + printf("UNDI bus type: %c%c%c%c\n", + ((undi->bus_type[i]) >> 0) & 0xff, + ((undi->bus_type[i]) >> 8) & 0xff, + ((undi->bus_type[i]) >> 16) & 0xff, + ((undi->bus_type[i]) >> 24) & 0xff); + } + printf("UNDI sum: %hhx\n", undi_checksum(undi)); + printf("\n"); +} +void print_init_info(struct db_init_info *info) +{ + printf("init_info.memory_required: %d\n", info->memory_required); + printf("init_info.frame_data_len: %d\n", info->frame_data_len); + printf("init_info.link_speeds: %d %d %d %d\n", + info->link_speeds[0], info->link_speeds[1], + info->link_speeds[2], info->link_speeds[3]); + printf("init_info.media_header_len: %d\n", info->media_header_len); + printf("init_info.hw_addr_len: %d\n", info->hw_addr_len); + printf("init_info.mcast_filter_cnt: %d\n", info->mcast_filter_cnt); + printf("init_info.tx_buf_cnt: %d\n", info->tx_buf_cnt); + printf("init_info.tx_buf_size: %d\n", info->tx_buf_size); + printf("init_info.rx_buf_cnt: %d\n", info->rx_buf_cnt); + printf("init_info.rx_buf_size: %d\n", info->rx_buf_size); + printf("init_info.if_type: %hhx\n", info->if_type); + printf("init_info.duplex: %hhx\n", info->duplex); + printf("init_info.loopback: %hhx\n", info->loopback); + printf("\n"); +} +void print_config_info(struct db_config_info *info) +{ + int i; + printf("config_info.bus_type: %c%c%c%c\n", + ((info->pci.bus_type) >> 0) & 0xff, + ((info->pci.bus_type) >> 8) & 0xff, + ((info->pci.bus_type) >> 16) & 0xff, + ((info->pci.bus_type) >> 24) & 0xff); + if (info->pci.bus_type != UNDI_BUS_TYPE_PCI) { + return; + } + printf("config_info.bus: %hx\n", info->pci.bus); + printf("config_info.device: %hhx\n", info->pci.device); + printf("config_info.function: %hhx\n", info->pci.function); + printf("config_info.config:\n"); + for(i = 0; i < 256; i++) { + if ((i & 0xf) == 0) { + printf("[%hhx]", i); + } + printf(" %hhx", info->pci.config[i]); + if ((i & 0xf) == 0xf) { + printf("\n"); + } + } + printf("\n"); + +} +void print_cdb(struct cdb *cdb) +{ + printf("\n"); + printf("cdb.op_code: %hx\n", cdb->op_code); + printf("cdb.op_flags: %hx\n", cdb->op_flags); + printf("cdb.cpb_size: %d\n", cdb->cpb_size); + printf("cdb.db_size: %d\n", cdb->db_size); + printf("cdb.cpb_addr: %lx\n", cdb->cpb_addr); + printf("cdb.db_addr: %lx\n", cdb->db_addr); + printf("cdb.stat_code: %lx\n", cdb->stat_code); + printf("cdb.stat_flags: %lx\n", cdb->stat_flags); + printf("cdb.ifnum %d\n", cdb->ifnum); + printf("cdb.control: %hx\n", cdb->control); + printf("\n"); +} +#endif +#define ARPHRD_ETHER 1 +static int nic_setup(struct dev *dev, + EFI_NETWORK_INTERFACE_IDENTIFIER_INTERFACE *nii) +{ + struct nic *nic = (struct nic *)dev; + int result; + struct sw_undi *undi; + struct db_init_info init_info; + struct cpb_initialize cpb_initialize; + struct db_initialize db_initialize; + struct db_station_address db_station_address; + int media_detect; + unsigned filter, no_filter; + int i; + + /* Fail if I I'm not passed a valid nii */ + if (!nii) + return 0; + + /* Fail if this nit a SW UNDI interface */ + if (nii->ID == 0) + return 0; + + undi = phys_to_virt(nii->ID); + + /* Verify the undi structure */ + + /* It must have a pxe signature */ + if (memcmp(undi->signature, "!PXE", 4) != 0) + return 0; + /* It must have a valid checksum */ + if (undi_checksum(undi) != 0) + return 0; + /* It must be software undi */ + if (undi->implementation & UNDI_IMP_HW_UNDI) + return 0; + + /* Setup to do undi calls */ + undi_ifnum = nii->IfNum; + undi_entry_point = (void *)undi->entry_point; + + /* Find the UNDI state... */ + result = get_state(&cdb); + if (!result) + return 0; + + /* See if the device is already initialized */ + if ((cdb.stat_flags & CDB_STATFLAGS_GET_STATE_MASK) != + CDB_STATFLAGS_GET_STATE_STOPPED) { + + /* If so attempt to stop it */ + if ((cdb.stat_flags & CDB_STATFLAGS_GET_STATE_MASK) == + CDB_STATFLAGS_GET_STATE_INITIALIZED) { + result = shutdown(&cdb); + result = stop(&cdb); + } + else if ((cdb.stat_flags & CDB_STATFLAGS_GET_STATE_MASK) == + CDB_STATFLAGS_GET_STATE_STARTED) { + result = stop(&cdb); + } + + /* See if it did stop */ + result = get_state(&cdb); + if (!result) + return 0; + + /* If it didn't stop give up */ + if ((cdb.stat_flags & CDB_STATFLAGS_GET_STATE_MASK) != + CDB_STATFLAGS_GET_STATE_STOPPED) + return 0; + + } + + result = start(&cdb); + if (!result) { + printf("Device would not start: %x\n", cdb.stat_code); + return 0; + } + result = get_init_info(&cdb, &init_info); + if (!result) { + printf("Device wount not give init info: %x\n", cdb.stat_code); + stop(&cdb); + return 0; + } + /* See if the NIC can detect the presence of a cable */ + media_detect = (cdb.stat_flags & CDB_STATFLAGS_CABLE_DETECT_MASK) == + CDB_STATFLAGS_CABLE_DETECT_SUPPORTED; + + if ((init_info.if_type != ARPHRD_ETHER) || + (init_info.hw_addr_len != ETH_ALEN)) { + printf("Not ethernet\n"); + stop(&cdb); + return 0; + } + if (init_info.memory_required > sizeof(buffer)) { + printf("NIC wants %d bytes I only have %ld bytes\n", + init_info.memory_required, sizeof(buffer)); + stop(&cdb); + return 0; + } + /* Initialize the device */ + memset(buffer, 0, sizeof(buffer)); + memset(&cpb_initialize, 0, sizeof(cpb_initialize)); + cpb_initialize.memory_addr = virt_to_phys(&buffer); + cpb_initialize.memory_length = init_info.memory_required; + cpb_initialize.link_speed = 0; /* auto detect */ + /* UNDI nics will not take suggestions :( + * So let them figure out an appropriate buffer stragety on their own. + */ + cpb_initialize.tx_buf_cnt = 0; + cpb_initialize.tx_buf_size = 0; + cpb_initialize.rx_buf_cnt = 0; + cpb_initialize.rx_buf_size = 0; + cpb_initialize.duplex = 0; + cpb_initialize.loopback = 0; + result = initialize(&cdb, media_detect, &cpb_initialize, &db_initialize); + if (!result) { + printf("Device would not initialize: %x\n", cdb.stat_code); + stop(&cdb); + return 0; + } +#if 0 + /* It appears the memory_used parameter is never set correctly, ignore it */ + if (db_initialize.memory_used > sizeof(buffer)) { + printf("NIC is using %d bytes I only have %ld bytes\n", + db_initialize.memory_used, sizeof(buffer)); + printf("tx_buf_cnt: %d\n", db_initialize.tx_buf_cnt); + printf("tx_buf_size: %d\n", db_initialize.tx_buf_size); + printf("rx_buf_cnt: %d\n", db_initialize.rx_buf_cnt); + printf("rx_buf_size: %d\n", db_initialize.rx_buf_size); + nic_disable(dev); + return 0; + } + printf("NIC is using %d bytes\n", + db_initialize.memory_used); +#endif + if (media_detect && ( + (cdb.stat_flags & ~CDB_STATFLAGS_STATUS_MASK) == + CDB_STATFLAGS_INITIALIZED_NO_MEDIA)) { + printf("No media present\n"); + nic_disable(dev); + return 0; + } + + /* Get the mac address */ + result = station_address_read(&cdb, &db_station_address); + if (!result) { + printf("Could not read station address: %x\n", + cdb.stat_code); + nic_disable(dev); + return 0; + } + for(i = 0; i < ETH_ALEN; i++) { + nic->node_addr[i] = db_station_address.station_address[i]; + } + printf("Ethernet addr: %!\n", nic->node_addr); + + filter = CDB_OPFLAGS_RECEIVE_FILTER_ENABLE | + CDB_OPFLAGS_RECEIVE_FILTER_UNICAST | + CDB_OPFLAGS_RECEIVE_FILTER_BROADCAST; + no_filter = CDB_OPFLAGS_RECEIVE_FILTER_DISABLE | + CDB_OPFLAGS_RECEIVE_FILTER_RESET_MCAST_LIST | + CDB_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST; + + if (undi->implementation & UNDI_IMP_PROMISCUOUS_MULTICAST_RX_SUPPORTED) { + filter |= CDB_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST; + no_filter |= CDB_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS; + } + else if (undi->implementation & UNDI_IMP_PROMISCUOUS_RX_SUPPORTED) { + filter |= CDB_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS; + } + + result = receive_filters(&cdb, no_filter); + if (!result) { + printf("Could not clear receive filters: %x\n", + cdb.stat_code); + nic_disable(dev); + return 0; + } + result = receive_filters(&cdb, filter); + if (!result) { + printf("Could not set receive filters: %x\n", + cdb.stat_code); + nic_disable(dev); + return 0; + } + + /* It would be nice to call get_config_info so I could pass + * the type of nic, but that crashes some efi drivers. + */ + /* Everything worked! */ + dev->disable = nic_disable; + nic->poll = nic_poll; + nic->transmit = nic_transmit; + + return 1; +} + +/************************************************************************** +PROBE - Look for an adapter, this routine's visible to the outside +***************************************************************************/ +static int nic_probe(struct dev *dev, unsigned short *dummy __unused) +{ + EFI_NETWORK_INTERFACE_IDENTIFIER_INTERFACE *nii; + int index; + int result; + + index = dev->index+ 1; + if (dev->how_probe == PROBE_AWAKE) { + index--; + } + for(result = 0; !result && (nii = lookup_efi_nic(index)); index++) { + result = nic_setup(dev, nii); + if (result) { + break; + } + } + dev->index = result ? index : -1; + return result; +} + + + + +static struct isa_driver nic_driver __isa_driver = { + .type = NIC_DRIVER, + .name = "undi_nii", + .probe = nic_probe, + .ioaddrs = 0, +}; diff --git a/src/arch/ia64/include/bits/byteswap.h b/src/arch/ia64/include/bits/byteswap.h new file mode 100644 index 00000000..a8a11585 --- /dev/null +++ b/src/arch/ia64/include/bits/byteswap.h @@ -0,0 +1,36 @@ +#ifndef ETHERBOOT_BITS_BYTESWAP_H +#define ETHERBOOT_BITS_BYTESWAP_H + +static inline uint64_t __ia64_bswap_64(uint64_t x) +{ + uint64_t result; + __asm__ volatile( + "mux1 %0=%1,@rev" : + "=r" (result) + : "r" (x)); + return result; +} + +#define __bswap_constant_16(x) \ + ((uint16_t)((((uint16_t)(x) & 0x00ff) << 8) | \ + (((uint16_t)(x) & 0xff00) >> 8))) + +#define __bswap_constant_32(x) \ + ((uint32_t)((((uint32_t)(x) & 0x000000ffU) << 24) | \ + (((uint32_t)(x) & 0x0000ff00U) << 8) | \ + (((uint32_t)(x) & 0x00ff0000U) >> 8) | \ + (((uint32_t)(x) & 0xff000000U) >> 24))) + +#define __bswap_16(x) \ + (__builtin_constant_p(x) ? \ + __bswap_constant_16(x) : \ + (__ia64_bswap_64(x) >> 48)) + + +#define __bswap_32(x) \ + (__builtin_constant_p(x) ? \ + __bswap_constant_32(x) : \ + (__ia64_bswap_64(x) >> 32)) + + +#endif /* ETHERBOOT_BITS_BYTESWAP_H */ diff --git a/src/arch/ia64/include/bits/cpu.h b/src/arch/ia64/include/bits/cpu.h new file mode 100644 index 00000000..d8fe1cbb --- /dev/null +++ b/src/arch/ia64/include/bits/cpu.h @@ -0,0 +1,6 @@ +#ifndef IA64_BITS_CPU_H +#define IA64_BITS_CPU_H + +#define cpu_setup() do {} while(0) + +#endif /* IA64_BITS_CPU_H */ diff --git a/src/arch/ia64/include/bits/elf.h b/src/arch/ia64/include/bits/elf.h new file mode 100644 index 00000000..c68f8456 --- /dev/null +++ b/src/arch/ia64/include/bits/elf.h @@ -0,0 +1,11 @@ +#ifndef IA64_BITS_ELF_H +#define IA64_BITS_ELF_H + +/* ELF Defines for the current architecture */ +#define EM_CURRENT EM_IA_64 +#define ELFDATA_CURRENT ELFDATA2LSB + +#define ELF_CHECK_ARCH(x) \ + ((x).e_machine == EM_CURRENT) + +#endif /* IA64_BITS_ELF_H */ diff --git a/src/arch/ia64/include/bits/endian.h b/src/arch/ia64/include/bits/endian.h new file mode 100644 index 00000000..413e702d --- /dev/null +++ b/src/arch/ia64/include/bits/endian.h @@ -0,0 +1,6 @@ +#ifndef ETHERBOOT_BITS_ENDIAN_H +#define ETHERBOOT_BITS_ENDIAN_H + +#define __BYTE_ORDER __LITTLE_ENDIAN + +#endif /* ETHERBOOT_BITS_ENDIAN_H */ diff --git a/src/arch/ia64/include/bits/string.h b/src/arch/ia64/include/bits/string.h new file mode 100644 index 00000000..31e94b7d --- /dev/null +++ b/src/arch/ia64/include/bits/string.h @@ -0,0 +1,6 @@ +#ifndef ETHERBOOT_BITS_STRING_H +#define ETHERBOOT_BITS_STRING_H + +/* define inline optimized string functions here */ + +#endif /* ETHERBOOT_BITS_STRING_H */ diff --git a/src/arch/ia64/include/hooks.h b/src/arch/ia64/include/hooks.h new file mode 100644 index 00000000..d8f1f06a --- /dev/null +++ b/src/arch/ia64/include/hooks.h @@ -0,0 +1,12 @@ +#ifndef ETHERBOOT_IA64_HOOKS_H +#define ETHERBOOT_IA64_HOOKS_H + +#include <stdarg.h> + +void arch_main(in_call_data_t *data, va_list params); +void arch_on_exit(int status); +void arch_relocate_to(unsigned long addr); +#define arch_relocated_from(old_addr) do {} while(0) + + +#endif /* ETHERBOOT_IA64_HOOKS_H */ diff --git a/src/arch/ia64/include/io.h b/src/arch/ia64/include/io.h new file mode 100644 index 00000000..be5a5ce1 --- /dev/null +++ b/src/arch/ia64/include/io.h @@ -0,0 +1,228 @@ +#ifndef ETHERBOOT_IO_H +#define ETHERBOOT_IO_H + +/* Don't require identity mapped physical memory, + * osloader.c is the only valid user at the moment. + */ +static inline unsigned long virt_to_phys(volatile const void *virt_addr) +{ + return ((unsigned long)virt_addr); +} + +static inline void *phys_to_virt(unsigned long phys_addr) +{ + return (void *)(phys_addr); +} + +/* virt_to_bus converts an addresss inside of etherboot [_start, _end] + * into a memory address cards can use. + */ +#define virt_to_bus virt_to_phys + + +/* bus_to_virt reverses virt_to_bus, the address must be output + * from virt_to_bus to be valid. This function does not work on + * all bus addresses. + */ +#define bus_to_virt phys_to_virt + +/* ioremap converts a random 32bit bus address into something + * etherboot can access. + */ +static inline void *ioremap(unsigned long bus_addr, unsigned long length __unused) +{ + return bus_to_virt(bus_addr); +} + +/* iounmap cleans up anything ioremap had to setup */ +static inline void iounmap(void *virt_addr __unused) +{ + return; +} + +/* In physical mode the offset of uncached pages */ +#define PHYS_BASE (0x8000000000000000UL) + +/* Memory mapped IO primitives, we avoid the cache... */ +static inline uint8_t readb(unsigned long addr) +{ + return *((volatile uint8_t *)(PHYS_BASE | addr)); +} + +static inline uint16_t readw(unsigned long addr) +{ + return *((volatile uint16_t *)(PHYS_BASE | addr)); +} + +static inline uint32_t readl(unsigned long addr) +{ + return *((volatile uint32_t *)(PHYS_BASE | addr)); +} + +static inline uint64_t readq(unsigned long addr) +{ + return *((volatile uint64_t *)(PHYS_BASE | addr)); +} + + +static inline void writeb(uint8_t val, unsigned long addr) +{ + *((volatile uint8_t *)(PHYS_BASE | addr)) = val; +} + +static inline void writew(uint16_t val, unsigned long addr) +{ + *((volatile uint16_t *)(PHYS_BASE | addr)) = val; +} + +static inline void writel(uint32_t val, unsigned long addr) +{ + *((volatile uint32_t *)(PHYS_BASE | addr)) = val; +} + +static inline void writeq(uint64_t val, unsigned long addr) +{ + *((volatile uint64_t *)(PHYS_BASE | addr)) = val; +} + + +static inline void memcpy_fromio(void *dest, unsigned long src, size_t n) +{ + size_t i; + uint8_t *dp = dest; + for(i = 0; i < n; i++) { + *dp = readb(src); + dp++; + src++; + } +} + +static inline void memcpy_toio(unsigned long dest , const void *src, size_t n) +{ + size_t i; + const uint8_t *sp = src; + for(i = 0; i < n; i++) { + writeb(*sp, dest); + sp++; + dest++; + } +} + +/* IO space IO primitives, Itanium has a strange architectural mapping... */ +extern unsigned long io_base; +#define __ia64_mf_a() __asm__ __volatile__ ("mf.a" ::: "memory") +#define __ia64_io_addr(port) ((void *)(PHYS_BASE | io_base | (((port) >> 2) << 12) | ((port) & 0xfff))) + +static inline uint8_t inb(unsigned long port) +{ + uint8_t result; + + result = *((volatile uint8_t *)__ia64_io_addr(port)); + __ia64_mf_a(); + return result; +} + +static inline uint16_t inw(unsigned long port) +{ + uint8_t result; + result = *((volatile uint16_t *)__ia64_io_addr(port)); + __ia64_mf_a(); + return result; +} + +static inline uint32_t inl(unsigned long port) +{ + uint32_t result; + result = *((volatile uint32_t *)__ia64_io_addr(port)); + __ia64_mf_a(); + return result; +} + +static inline void outb(uint8_t val, unsigned long port) +{ + *((volatile uint8_t *)__ia64_io_addr(port)) = val; + __ia64_mf_a(); +} + +static inline void outw(uint16_t val, unsigned long port) +{ + *((volatile uint16_t *)__ia64_io_addr(port)) = val; + __ia64_mf_a(); +} + +static inline void outl(uint32_t val, unsigned long port) +{ + *((volatile uint32_t *)__ia64_io_addr(port)) = val; + __ia64_mf_a(); +} + + + +static inline void insb(unsigned long port, void *dst, unsigned long count) +{ + volatile uint8_t *addr = __ia64_io_addr(port); + uint8_t *dp = dst; + __ia64_mf_a(); + while(count--) + *dp++ = *addr; + __ia64_mf_a(); +} + +static inline void insw(unsigned long port, void *dst, unsigned long count) +{ + volatile uint16_t *addr = __ia64_io_addr(port); + uint16_t *dp = dst; + __ia64_mf_a(); + while(count--) + *dp++ = *addr; + __ia64_mf_a(); +} + +static inline void insl(unsigned long port, void *dst, unsigned long count) +{ + volatile uint32_t *addr = __ia64_io_addr(port); + uint32_t *dp = dst; + __ia64_mf_a(); + while(count--) + *dp++ = *addr; + __ia64_mf_a(); +} + +static inline void outsb(unsigned long port, void *src, unsigned long count) +{ + const uint8_t *sp = src; + volatile uint8_t *addr = __ia64_io_addr(port); + + while (count--) + *addr = *sp++; + __ia64_mf_a(); +} + +static inline void outsw(unsigned long port, void *src, unsigned long count) +{ + const uint16_t *sp = src; + volatile uint16_t *addr = __ia64_io_addr(port); + + while (count--) + *addr = *sp++; + __ia64_mf_a(); +} + +static inline void outsl(unsigned long port, void *src, unsigned long count) +{ + const uint32_t *sp = src; + volatile uint32_t *addr = __ia64_io_addr(port); + + while (count--) + *addr = *sp++; + __ia64_mf_a(); +} + +static inline unsigned long ia64_get_kr0(void) +{ + unsigned long r; + asm volatile ("mov %0=ar.k0" : "=r"(r)); + return r; +} + +#endif /* ETHERBOOT_IO_H */ diff --git a/src/arch/ia64/include/latch.h b/src/arch/ia64/include/latch.h new file mode 100644 index 00000000..87195b42 --- /dev/null +++ b/src/arch/ia64/include/latch.h @@ -0,0 +1,11 @@ +#ifndef LATCH_H +#define LATCH_H + +#define TICKS_PER_SEC (1000UL) + +/* Fixed timer interval used for calibrating a more precise timer */ +#define LATCHES_PER_SEC 10 + +void sleep_latch(void); + +#endif /* LATCH_H */ diff --git a/src/arch/ia64/include/limits.h b/src/arch/ia64/include/limits.h new file mode 100644 index 00000000..0c6f21f9 --- /dev/null +++ b/src/arch/ia64/include/limits.h @@ -0,0 +1,57 @@ +#ifndef LIMITS_H +#define LIMITS_H 1 + +/* Number of bits in a `char' */ +#define CHAR_BIT 8 + +/* Minimum and maximum values a `signed char' can hold */ +#define SCHAR_MIN (-128) +#define SCHAR_MAX 127 + +/* Maximum value an `unsigned char' can hold. (Minimum is 0.) */ +#define UCHAR_MAX 255 + +/* Minimum and maximum values a `char' can hold */ +#define CHAR_MIN SCHAR_MIN +#define CHAR_MAX SCHAR_MAX + +/* Minimum and maximum values a `signed short int' can hold */ +#define SHRT_MIN (-32768) +#define SHRT_MAX 32767 + +/* Maximum value an `unsigned short' can hold. (Minimum is 0.) */ +#define USHRT_MAX 65535 + + +/* Minimum and maximum values a `signed int' can hold */ +#define INT_MIN (-INT_MAX - 1) +#define INT_MAX 2147483647 + +/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */ +#define UINT_MAX 4294967295U + + +/* Minimum and maximum values a `signed int' can hold */ +#define INT_MIN (-INT_MAX - 1) +#define INT_MAX 2147483647 + +/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */ +#define UINT_MAX 4294967295U + +/* Minimum and maximum values a `signed long' can hold */ +#define LONG_MAX 9223372036854775807L +#define LONG_MIN (-LONG_MAX - 1L) + +/* Maximum value an `unsigned long' can hold. (Minimum is 0.) */ +#define ULONG_MAX 18446744073709551615UL + +/* Minimum and maximum values a `signed long long' can hold */ +#define LLONG_MAX 9223372036854775807LL +#define LLONG_MIN (-LONG_MAX - 1LL) + + +/* Maximum value an `unsigned long long' can hold. (Minimum is 0.) */ +#define ULLONG_MAX 18446744073709551615ULL + + +#endif /* LIMITS_H */ diff --git a/src/arch/ia64/include/pal.h b/src/arch/ia64/include/pal.h new file mode 100644 index 00000000..6cda19d1 --- /dev/null +++ b/src/arch/ia64/include/pal.h @@ -0,0 +1,11 @@ +#ifndef IA64_PAL_H +#define IA64_PAL_H + +struct pal_freq_ratio { + unsigned long den : 32, num : 32; /* numerator & denominator */ +}; +extern long pal_freq_ratios(struct pal_freq_ratio *proc_ratio, + struct pal_freq_ratio *bus_ratio, struct pal_freq_ratio *itc_ratio); + + +#endif /* IA64_PAL_H */ diff --git a/src/arch/ia64/include/sal.h b/src/arch/ia64/include/sal.h new file mode 100644 index 00000000..7a1b57ec --- /dev/null +++ b/src/arch/ia64/include/sal.h @@ -0,0 +1,29 @@ +#ifndef IA64_SAL_H +#define IA64_SAL_H + +struct fptr { + unsigned long entry; + unsigned long gp; +}; +extern struct fptr sal_entry; +extern struct fptr pal_entry; +extern int parse_sal_system_table(void *table); + +#define SAL_FREQ_BASE_PLATFORM 0 +#define SAL_FREQ_BASE_INTERVAL_TIMER 1 +#define SAL_FREQ_BASE_REALTIME_CLOCK 2 + +long sal_freq_base (unsigned long which, unsigned long *ticks_per_second, + unsigned long *drift_info); + +#define PCI_SAL_ADDRESS(seg, bus, dev, fn, reg) \ + ((unsigned long)(seg << 24) | (unsigned long)(bus << 16) | \ + (unsigned long)(dev << 11) | (unsigned long)(fn << 8) | \ + (unsigned long)(reg)) + +long sal_pci_config_read ( + unsigned long pci_config_addr, unsigned long size, unsigned long *value); +long sal_pci_config_write ( + unsigned long pci_config_addr, unsigned long size, unsigned long value); + +#endif /* IA64_SAL_H */ diff --git a/src/arch/ia64/include/setjmp.h b/src/arch/ia64/include/setjmp.h new file mode 100644 index 00000000..a1fac2dc --- /dev/null +++ b/src/arch/ia64/include/setjmp.h @@ -0,0 +1,13 @@ +#ifndef ETHERBOOT_SETJMP_H +#define ETHERBOOT_SETJMP_H + + +/* Define a type for use by setjmp and longjmp */ +#define JBLEN 70 + +typedef long jmp_buf[JBLEN] __attribute__ ((aligned (16))); /* guarantees 128-bit alignment! */ + +extern int setjmp (jmp_buf env); +extern void longjmp (jmp_buf env, int val); + +#endif /* ETHERBOOT_SETJMP_H */ diff --git a/src/arch/ia64/include/stdint.h b/src/arch/ia64/include/stdint.h new file mode 100644 index 00000000..2f9c592c --- /dev/null +++ b/src/arch/ia64/include/stdint.h @@ -0,0 +1,16 @@ +#ifndef STDINT_H +#define STDINT_H + +typedef unsigned long size_t; + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long uint64_t; + +typedef signed char int8_t; +typedef signed short int16_t; +typedef signed int int32_t; +typedef signed long int64_t; + +#endif /* STDINT_H */ diff --git a/src/arch/ia64/prefix/apply_efi_prefix.pl b/src/arch/ia64/prefix/apply_efi_prefix.pl new file mode 100755 index 00000000..999c43b4 --- /dev/null +++ b/src/arch/ia64/prefix/apply_efi_prefix.pl @@ -0,0 +1,63 @@ +#!/usr/bin/perl -w +# +# Program to apply an efi header to an ia64 etherboot file. +# +# GPL Eric Biederman 2002 +# + +use strict; + +use bytes; + +main(@ARGV); + +sub usage +{ + my ($err) = @_; + print STDERR $err , "\n"; + die "Usage $0 prrefix file bss_size\n"; +} +sub main +{ + my ($prefix_name, $suffix_name, $bss_size) = @_; + usage("No prefix") unless (defined($prefix_name)); + usage("No suffix") unless (defined($suffix_name)); + usage("No bss size") unless (defined($bss_size)); + + open(PREFIX, "<$prefix_name") or die "Cannot open $prefix_name"; + open(SUFFIX, "<$suffix_name") or die "Cannot open $suffix_name"; + + $/ = undef; + my $prefix = <PREFIX>; close(PREFIX); + my $suffix = <SUFFIX>; close(SUFFIX); + + # Payload sizes. + my $payload_size = length($suffix); + my $payload_bss = $bss_size; + + # Update the image size + my $hdr_off = unpack("V",substr($prefix, 0x3c, 4)); + my $image_size_off = 0x050 + $hdr_off; + my $img_mem_size_off = 0x0c0 + $hdr_off; + my $img_size_off = 0x0c8 + $hdr_off; + + my $image_size = unpack("V", substr($prefix, $image_size_off, 4)); + my $img_mem_size = unpack("V", substr($prefix, $img_mem_size_off, 4)); + my $img_size = unpack("V", substr($prefix, $img_size_off, 4)); + + $image_size += $payload_size + $payload_bss; + $img_mem_size += $payload_size + $payload_bss; + $img_size += $payload_size; + + substr($prefix, $image_size_off, 4) = pack("V", $image_size); + substr($prefix, $img_mem_size_off, 4) = pack("V", $img_mem_size); + substr($prefix, $img_size_off, 4) = pack("V", $img_size); + + #print(STDERR "image_size: $image_size\n"); + #print(STDERR "img_mem_size: $img_mem_size\n"); + #print(STDERR "img_size: $img_size\n"); + + print $prefix; + print $suffix; +} + diff --git a/src/arch/ia64/prefix/apply_unnrv2b_prefix.pl b/src/arch/ia64/prefix/apply_unnrv2b_prefix.pl new file mode 100644 index 00000000..71b2b247 --- /dev/null +++ b/src/arch/ia64/prefix/apply_unnrv2b_prefix.pl @@ -0,0 +1,198 @@ +#!/usr/bin/perl -w +# +# Program to apply an unnrv2b decompressor header to an ia64 etherboot file. +# +# GPL Eric Biederman 2002 +# + +use strict; + +use bytes; + +main(@ARGV); + +sub usage +{ + my ($err) = @_; + print STDERR $err , "\n"; + die "Usage $0 prefix file\n"; +} + +sub getbits +{ + my ($bundle, $start, $size) = @_; + + # Compute the mask + my $mask = 0xffffffff; + $mask = $mask >> (32 - $size); + + # Compute the substring, and shift + my ($first, $end, $count, $shift); + $first = int($start / 8); + $end = int(($start + $size + 7)/8); + $count = $end - $first; + $shift = $start % 8; + + # Compute the unpack type + my $type; + if ($count == 1) { + $type = "C" + } + elsif ($count == 2) { + $type = "v"; + } + elsif (($count >= 3) && ($count <= 4)) { + $type = "V"; + } + else { + die "bad count $count"; + } + + # Now compute the value + my $val = (unpack($type, substr($bundle, $first, $count)) >> $shift) & $mask; + + # Now return the value + return $val; +} + +sub putbits +{ + my ($bundle, $start, $size, $val) = @_; + + + # Compute the mask + my $mask = 0xffffffff; + $mask >>= 32 - $size; + + # Compute the substring, and shift + my ($first, $end, $count, $shift); + $first = int($start / 8); + $end = int(($start + $size + 7)/8); + $count = $end - $first; + $shift = $start % 8; + + # Compute the unpack type + my $type; + if ($count == 1) { + $type = "C" + } + elsif ($count == 2) { + $type = "v"; + } + elsif (($count >= 3) && ($count <= 4)) { + $type = "V"; + } + else { + die "bad count $count"; + } + + # Adjust the mask + $mask <<= $shift; + + # Now set the value, preserving the untouched bits + substr($bundle, $first, $count) = + pack($type, + ((unpack($type, substr($bundle, $first, $count)) & ~$mask) | + (($val << $shift) & $mask))); + + # Now return the new value; + return $bundle; +} + +sub main +{ + my ($prefix_name, $suffix_name) = @_; + usage("No prefix") unless (defined($prefix_name)); + usage("No suffix") unless (defined($suffix_name)); + + open(PREFIX, "<$prefix_name") or die "Cannot open $prefix_name"; + open(SUFFIX, "<$suffix_name") or die "Cannot open $suffix_name"; + + $/ = undef; + my $prefix = <PREFIX>; close(PREFIX); + my $suffix = <SUFFIX>; close(SUFFIX); + + # Payload sizes + my $prefix_len = length($prefix); + my $suffix_len = length($suffix); + my $payload_size = $suffix_len; + my $uncompressed_offset = ($prefix_len + $suffix_len + 15) & ~15; + my $pad = $uncompressed_offset - ($prefix_len + $suffix_len); + + # Itaninum instruction bundle we will be updating + # 0 - 4 template == 5 + # 5 - 45 slot 0 M-Unit + # 46 - 86 slot 1 L-Unit + # 87 - 127 slot 2 X-Unit + # Itaninum instruction format + # 40 - 37 Major opcode + # ... + # + + # slot 1 + # 0 - 40 [41] imm-41 + # 10 - 40 [31] imm-41-hi + # 0 - 9 [10] imm-41-lo + + # slot 2 + # 0 - 5 [6] qp + # 6 - 12 [7] r1 + # 13 - 19 [7] imm-7b + # 20 [1] vc + # 21 [1] immc + # 22 - 26 [5] imm-5c + # 27 - 35 [9] imm-9d + # 36 [1] imm0 + # 37 - 40 [4] major opcode + # + + # major opcode should be 6 + + # Update the image size + my $uncompressed_offset_bundle_off = 16; + my $bundle = substr($prefix, $uncompressed_offset_bundle_off, 16); + + my $template = getbits($bundle, 0, 5); + my $op1_base = 46; + my $op2_base = 87; + my $major_opcode = getbits($bundle, 37 + $op2_base, 4); + + if (($template != 5) || + ($major_opcode != 6)) { + die "unknown second bundle cannot patch"; + } + + die "uncompressed_offset to big!\n" if ($uncompressed_offset > 0xffffffff); + my $immhi = 0; + my $immlo = $uncompressed_offset; + + my $imm0 = ($immhi >> 31) & ((1 << 1) - 1); + my $imm41_hi = ($immhi >> 0) & ((1 << 31) - 1); + + my $imm41_lo = ($immlo >> 22) & ((1 << 10) - 1); + my $immc = ($immlo >> 21) & ((1 << 1) - 1); + my $imm5c = ($immlo >> 16) & ((1 << 5) - 1); + my $imm9d = ($immlo >> 7) & ((1 << 9) - 1); + my $imm7b = ($immlo >> 0) & ((1 << 7) - 1); + + $bundle = putbits($bundle, 10 + $op1_base, 31, $imm41_hi); + $bundle = putbits($bundle, 0 + $op1_base, 10, $imm41_lo); + $bundle = putbits($bundle, 36 + $op2_base, 1 , $imm0); + $bundle = putbits($bundle, 27 + $op2_base, 9 , $imm9d); + $bundle = putbits($bundle, 22 + $op2_base, 5 , $imm5c); + $bundle = putbits($bundle, 21 + $op2_base, 1 , $immc); + $bundle = putbits($bundle, 13 + $op2_base, 7 , $imm7b); + + substr($prefix, $uncompressed_offset_bundle_off, 16) = $bundle; + + #print (STDERR "prefix: $prefix_len\n"); + #print (STDERR "suffix: $suffix_len\n"); + #print (STDERR "pad: $pad\n"); + #print (STDERR "uncompressed_offset: $uncompressed_offset\n"); + + print $prefix; + print $suffix; + # Pad the resulting image by a few extra bytes... + print pack("C", 0) x $pad; +} + diff --git a/src/arch/ia64/prefix/efi_prefix.S b/src/arch/ia64/prefix/efi_prefix.S new file mode 100644 index 00000000..1fb1501f --- /dev/null +++ b/src/arch/ia64/prefix/efi_prefix.S @@ -0,0 +1,195 @@ +#include "elf.h" + .explicit + + .section ".hdrs", "a" + /* First the DOS file header */ + + +dos_header: + .byte 'M', 'Z' /* Signature */ + .short dos_program_end - dos_program /* Length of image mod 512 bytes*/ + .short 0x0001 /* Length of image in 512 byte pages */ /* FIXME */ + .short 0x0000 /* Number of relocation items following header */ + .short (dos_header_end - dos_header)/16 /* Size of header in 16 byte paragraphs */ + .short 0x0001 /* Minimum number of paragraphs needed to run image */ + .short 0x0001 /* Maximum number of paragraphs program would like */ + .short 0x0000 /* Initial SS */ + + .short 0x00b8 /* Initial SP */ + .short 0x0000 /* Negative checksum of image */ + .short 0x0000 /* Initial IP */ + .short 0x0000 /* Initial CS */ + .short 0x0010 /* Offset in EXEC of first relocation item */ + .short 0x0000 /* Overlay number */ + + .balign 16 +dos_header_end: +dos_program: + .byte 0xdb /* retf */ + .balign 16 +dos_program_end: + .org 0x3c + .byte pe_signature - dos_header, 0x00, 0x00, 0x00 + .org 0x40 /* NOTE: set this to 0x80 for debugging, 0x40 otherwise */ +pe_signature: + .byte 'P', 'E', 0, 0 +coff_header: +#define IMAGE_MACHINE_IA64 0x0200 +coff_machine: .short IMAGE_MACHINE_IA64 +coff_nsections: .short (section_headers_end - section_headers)/40 +coff_timdat: .int 1038168747 /* Sun Nov 24 12:12:27 2002 */ +coff_symptr: .int 0x00000000 + +coff_nsyms: .int 0x00000000 +coff_opthdr: .short pe_end - pe_header +#define CF_RELOC_STRIPPED 0x0001 +#define CF_EXECUTABLE 0x0002 +#define CF_LINE_STRIPPED 0x0004 +#define CF_LOCAL_STRIPPED 0x0008 +#define CF_DEBUG_STRIPPED 0x0206 +coff_flags: .short CF_EXECUTABLE | CF_LINE_STRIPPED | CF_LOCAL_STRIPPED | CF_DEBUG_STRIPPED + + /* Option header */ +pe_header: +pe_magic: .short 0x020b /* 020b or 010b? */ +pe_linker: .byte 0x02, 0x38 +pe_text_size: .int _text_size +pe_data_size: .int _data_size +pe_bss_size: .int _bss_size +pe_entry: .int _start_plabel_rva +pe_text_base: .int _text_rva +pe_image_base: .quad _image_base +pe_sec_align: .int _sect_align +pe_file_align: .int _file_align +pe_os_major: .short 0 +pe_os_minor: .short 0 +pe_image_major: .short 0 +pe_image_minro: .short 0 +pe_sub_major: .short 0 +pe_sub_minor: .short 0 +pe_reserved: .int 0 +pe_image_size: .int _image_size +pe_hdrs_size: .int _hdrs_size +pe_checksum: .int 0 /* FIXME how do I compute the checksum, unnecessary */ +#define SUBSYS_EFI_APP 10 +#define SUBSYS_EFI_BOOT_SERVICE_DRIVER 11 +#define SUBSYS_EFI_RUNTIME_DRIVER 12 +pe_subsys: .short SUBSYS_EFI_APP +pe_dll_flags: .short 0 +pe_stack_res: .quad 0 +pe_stack_commit:.quad 0 +pe_heap_res: .quad 0 +pe_heap_commit: .quad 0 +pe_ld_flags: .int 0 +pe_rvas: .int (rvas_end - rvas_start)/8 + +rvas_start: +rva_0_rva: .int 0 +rva_0_size: .int 0 +rva_1_rva: .int 0 +rva_1_size: .int 0 +rva_2_rva: .int 0 +rva_2_size: .int 0 +rva_3_rva: .int 0 +rva_3_size: .int 0 +rva_4_rva: .int 0 +rva_4_size: .int 0 +rva_5_rva: .int _reloc_rva +rva_5_size: .int __reloc_size +rvas_end: +pe_end: + +section_headers: +#define SCN_CNT_CODE 0x00000020 +#define SCN_CNT_INITIALIZED_DATA 0x00000040 +#define SCN_CNT_UNINITIALIZED_DATA 0x00000080 +#define SCN_MEM_DISCARDABLE 0x02000000 +#define SCN_MEM_SHARED 0x10000000 +#define SCN_MEM_EXECUTE 0x20000000 +#define SCN_MEM_READ 0x40000000 +#define SCN_MEM_WRITE 0x80000000 + +sec1_name: .byte '.', 'i', 'm', 'g', 0 , 0, 0, 0 +sec1_virt_size: .int _img_mem_size +sec1_virt_addr: .int _img_rva +sec1_file_size: .int _img_size +sec1_file_off: .int _img_off +sec1_reloc_off: .int 0 +sec1_line_off: .int 0 +sec1_reloc_cnt: .short 0 +sec1_line_cnt: .short 0 +sec1_flags: .int SCN_CNT_CODE | SCN_CNT_INITIALIZED_DATA \ + | SCN_MEM_EXECUTE | SCN_MEM_READ | SCN_MEM_WRITE + +section_headers_end: + + .text + .psr abi64 + .psr lsb + .global _start +_start: + { + alloc r8=ar.pfs,2,0,0,0 + mov gp=ip /* Get the address of _start/_text/__gp */ + } + ;; + add r14=@gprel(n1_desc),gp + add r15=@gprel(n2_desc),gp + add r16=@gprel(bhdr),gp + ;; + st8 [r14]=in0 + st8 [r15]=in1 + ;; + mov ar.pfs=r8 + ;; + mov r32=r16 + ;; + br.sptk.few _payload_start + + .data + .global _start_plabel + .balign 16 +_start_plabel: + .quad _start + .quad 0 /* I don't need a gp value... */ + + /* hand-crafted bhdr and parameters */ + .balign 16 +bhdr: +b_signature: .int 0x0E1FB007 +b_size: .int bhdr_end - bhdr +b_checksum: .short 0 +b_records: .short 3 + /* A NOP note to 64bit align later data */ + .balign 4 +n0_namesz: .int 0 +n0_descsz: .int 0 +n0_type: .int EBN_NOP + .balign 4 +n1_namesz: .int 10 +n1_descsz: .int 8 +n1_type: .int EB_IA64_IMAGE_HANDLE +n1_name: .asciz "Etherboot" + .balign 4 +n1_desc: .quad 0 + .balign 4 +n2_namesz: .int 10 +n2_descsz: .int 8 +n2_type: .int EB_IA64_SYSTAB +n2_name: .asciz "Etherboot" + .balign 4 +n2_desc: .quad 0 +bhdr_end: + + + /* hand-craft a .reloc section for the plabel */ +#define IMAGE_REL_BASED_ABS 0 +#define IMAGE_REL_BASED_DIR64 10 + + .section ".reloc", "a" + .int _start_plabel_rva // PAGE RVA + .int 12 // Block Size (2*4+2*2) + .short (IMAGE_REL_BASED_DIR64<<12) + 0 // reloc for plabel's entry point + .short (IMAGE_REL_BASED_ABS <<12) + 0 // dummy reloc for good alignment + + diff --git a/src/arch/ia64/prefix/efi_prefix.lds b/src/arch/ia64/prefix/efi_prefix.lds new file mode 100644 index 00000000..aa71ea2e --- /dev/null +++ b/src/arch/ia64/prefix/efi_prefix.lds @@ -0,0 +1,91 @@ +/* OUTPUT_FORMAT("binary") */ +OUTPUT_FORMAT("elf64-ia64-little") + +OUTPUT_ARCH(ia64) + +ENTRY(_start_plabel) +_sect_align = 16; /* normally 512 */ +_file_align = 16; /* normally 4096 */ +/* Symbols for hardcoding the payload and _bss size, with apply_efi_prefix + * there is no need to set these, and in will get confused if these are not 0. + */ +_payload_size = 0; +_payload_bss = 0; +SECTIONS { + /* We can arbitrarily set image base to anything we want, + * but efi does not honor it, so it is a pointless exercise. + * So we just set the start address to 0. + */ + . = 0; + _link_base = . ; + _image_base = . ; + .hdrs : { + _hdrs = . ; + *(.hdrs) + . = ALIGN(_file_align) ; + _ehdrs = . ; + } + . = ALIGN(_sect_align); + .img : { + _img = . ; + _text = . ; + __gp = . ; + *(.text) + _etext = .; + . = ALIGN(16); + _data = . ; + *(.data) + _edata = .; + . = ALIGN(16); + _reloc = . ; + *(.reloc) + __ereloc = . ; + _ereloc = . ; + . = ALIGN(16); + /* . = ALIGN(_file_align) ; */ + } + _payload_start = . ; + . = . + _payload_size ; + _payload_end = . ; + _eimg = . ; + . = ALIGN(_sect_align) ; + _bss = . ; + .bss : { + *(.bss) + . = . + _payload_bss; + } + _ebss = . ; + _end = . ; + /DISCARD/ : { + *(*) + } + + _hdrs_size = _ehdrs - _hdrs; + _hdrs_off = 0; + + _text_size = _etext - _text ; + _text_rva = _text - _image_base ; + _text_off = _text - _link_base; + + _data_size = _edata - _data ; + _data_rva = _data - _image_base; + _data_off = _data - _link_base; + + __reloc_size = __ereloc - _reloc ; + _reloc_size = _ereloc - _reloc ; + _reloc_rva = _reloc - _image_base; + _reloc_off = _reloc - _link_base; + + _bss_size = _ebss - _bss; + _bss_rva = _bss - _image_base; + + _img_size = _eimg - _img ; + _img_rva = _img - _image_base; + _img_off = _img - _link_base; + + _img_mem_size = _ebss - _img; + + _image_size = _ebss - _link_base ; + + _start_plabel_rva = _start_plabel - _image_base; +} diff --git a/src/arch/ia64/prefix/unnrv2b.S b/src/arch/ia64/prefix/unnrv2b.S new file mode 100644 index 00000000..bd7013b2 --- /dev/null +++ b/src/arch/ia64/prefix/unnrv2b.S @@ -0,0 +1,196 @@ +/* + * Copyright (C) 1996-2002 Markus Franz Xaver Johannes Oberhumer + * Copyright (C) 2002 Eric Biederman + * + * This file 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. + * + * Originally this code was part of ucl the data compression library + * for upx the ``Ultimate Packer of eXecutables''. + * + * - Converted to gas assembly, and refitted to work with etherboot. + * Eric Biederman 20 Aug 2002 + * + * - Converted to functional ia64 assembly (Can this get smaller?) + * Eric Biederman 5 Dec 2002 + */ + .text + .globl _start +_start: + /* See where I am running, and compute gp */ + { + /* Do no call alloc here as I do not know how many argument + * registers are being passed through the decompressor, and if I report + * to few the unreported registers may get stomped. + * + * Instead just explicitly get the value of ar.pfs. + */ + mov r17=0 + mov r8=ar.pfs + mov gp = ip /* The linker scripts sets gp at _start */ + + } + {.mlx + movl r9=0x123456789abcdef0 /* Get uncompressed_offset into r9 */ + } + ;; + { + add r14 = @gprel(payload + 4),gp + add r15 = r9,gp + mov r16=1 /* last_m_off = 1 */ + } + { + mov r20 = 0xd00 + add r21 = r9,gp + br.sptk.few decompr_loop_n2b + } + +/* ------------- DECOMPRESSION ------------- + + Input: + r8 - ar.pfs + r14 - source + r15 - dest + r16 - 1 + r17 - (buffer) 0 + r20 - 0xd00 (constant) + r21 - start address + Usage: + r9 - scratch register for memory copies + r18 - scratch register for getbit + r19 - scratch register for loads and stores + Output: + r2 - 0 + r3 - 0 +*/ + +getbit: + add r18 = r17,r17 + ;; + cmp.ne p8,p0 = r0,r18 + cmp.leu p6,p7 = r18,r17 + ;; + mov r17 = r18 +(p8) br.cond.sptk.few getbit_end + /* Do a unaligned 64bit load */ + ;; + ld1 r17 = [r14],1 + ;; + ld1 r18 = [r14],1 + ;; + dep r17 = r18,r17,8,8 + ld1 r18 = [r14],1 + ;; + dep r17 = r18,r17,16,8 + ld1 r18 = [r14],1 + ;; + dep r17 = r18,r17,24,8 + ld1 r18 = [r14],1 + ;; + dep r17 = r18,r17,32,8 + ld1 r18 = [r14],1 + ;; + dep r17 = r18,r17,40,8 + ld1 r18 = [r14],1 + ;; + dep r17 = r18,r17,48,8 + ld1 r18 = [r14],1 + ;; + dep r17 = r18,r17,56,8 + ;; + add r18 = r17,r17,1 + ;; + cmp.leu p6,p7=r18,r17 + ;; + mov r17=r18 + ;; +getbit_end: + br.ret.sptk.few b6 + + +decompr_literals_n2b: + ld1 r19 = [r14],1 + ;; + st1 [r15] = r19,1 + ;; +decompr_loop_n2b: + br.call.sptk.few b6 = getbit + ;; +(p6) br.cond.sptk.few decompr_literals_n2b +(p7) add r2 = 1,r0 /* m_off = 1 */ + ;; +loop1_n2b: + br.call.sptk.few b6 = getbit + ;; +(p6) add r2 = r2,r2,1 /* m_off = m_off*2 + getbit() */ +(p7) add r2 = r2,r2 + br.call.sptk.few b6 = getbit + ;; +(p7) br.cond.sptk.few loop1_n2b /* while(!getbit()) */ + ;; + mov r3 = r0 + cmp.eq p6,p0 = 2,r2 + add r2 = -3,r2 +(p6) br.cond.sptk.few decompr_ebpeax_n2b /* if (m_off == 2) goto decompr_ebpeax_n2b ? */ + ;; + ld1 r19 = [r14],1 + shl r2 = r2,8 + ;; + dep r2 = r19,r2,0,8 /* m_off = (m_off - 3)*256 + src[ilen++] */ + ;; + cmp4.eq p6,p0 = -1,r2 /* if (m_off == 0xffffffff) goto decomp_end_n2b */ + ;; +(p6) br.cond.sptk.few decompr_end_n2b + mov r16 = r2 /* last_m_off = m_off */ + ;; +decompr_ebpeax_n2b: + br.call.sptk.few b6 = getbit + ;; +(p6) add r3 = r3,r3,1 /* m_len = getbit() */ +(p7) add r3 = r3,r3 + br.call.sptk.few b6 = getbit + ;; +(p6) add r3 = r3,r3,1 /* m_len = m_len*2 + getbit()) */ +(p7) add r3 = r3,r3 + ;; + cmp.ne p6,p0 = r0,r3 +(p6) br.cond.sptk.few decompr_got_mlen_n2b /* if (m_len == 0) goto decompr_got_mlen_n2b */ + add r3 = 1,r3 /* m_len++ */ + ;; +loop2_n2b: + br.call.sptk.few b6 = getbit + ;; +(p6) add r3 = r3,r3,1 /* m_len = m_len*2 + getbit() */ +(p7) add r3 = r3,r3 + br.call.sptk.few b6 = getbit + ;; +(p7) br.cond.sptk.few loop2_n2b /* while(!getbit()) */ + add r3 = 2, r3 /* m_len += 2 */ + ;; +decompr_got_mlen_n2b: + cmp.gtu p6,p7 = r16, r20 + ;; +(p6) add r3 = 2, r3 /* m_len = m_len + 1 + (last_m_off > 0xd00) */ +(p7) add r3 = 1, r3 + sub r9 = r15, r16,1 /* m_pos = dst + olen - last_m_off - 1 */ + ;; +1: + ld1 r19 = [r9],1 + add r3 = -1,r3 + ;; + st1 [r15] = r19,1 /* dst[olen++] = *m_pos++ while(m_len > 0) */ + cmp.ne p6,p0 = r0,r3 +(p6) br.cond.sptk.few 1b + ;; + br.cond.sptk.few decompr_loop_n2b +decompr_end_n2b: + /* Branch to the start address */ + mov ar.pfs=r8 + ;; + mov b6 = r21 + ;; + br.sptk.few b6 + +payload: diff --git a/src/arch/ia64/prefix/unnrv2b.lds b/src/arch/ia64/prefix/unnrv2b.lds new file mode 100644 index 00000000..728f7009 --- /dev/null +++ b/src/arch/ia64/prefix/unnrv2b.lds @@ -0,0 +1,28 @@ +OUTPUT_FORMAT("elf64-ia64-little") + +OUTPUT_ARCH(ia64) + +ENTRY(_start) +SECTIONS { + . = 0; + __gp = .; + _text = . ; + .text : { + *(.text) + } + /DISCARD/ : { + *(.comment) + *(.note) + *(.hash) + *(.data) + *(.sbss) + *(.bss) + *(.dynstr) + *(.dynsym) + *(.IA_64.unwind) + *(.IA_64.unwind_info) + *(.IA64_unwind) + *(.IA64_unwind_info) + *(.dynamic) + } +} |