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/ia64 | |
| download | ipxe-3d6123e69ab879c72ff489afc5bf93ef0b7a94ce.tar.gz ipxe-3d6123e69ab879c72ff489afc5bf93ef0b7a94ce.tar.xz ipxe-3d6123e69ab879c72ff489afc5bf93ef0b7a94ce.zip | |
Initial revision
Diffstat (limited to 'src/arch/ia64')
38 files changed, 5276 insertions, 0 deletions
diff --git a/src/arch/ia64/Config b/src/arch/ia64/Config new file mode 100644 index 000000000..6b7405f18 --- /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 000000000..42e0b47be --- /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 000000000..a11b82644 --- /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 000000000..97e585fc8 --- /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 000000000..216cce92a --- /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 000000000..3115714ab --- /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 000000000..283eff0e9 --- /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 000000000..d989a2f59 --- /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 000000000..d5c8e5ab5 --- /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 000000000..63e093d9c --- /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 000000000..a6cc40fcf --- /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 000000000..cfb518d09 --- /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 000000000..f8069bb91 --- /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 000000000..31689bfbb --- /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 000000000..37b5c9f86 --- /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 000000000..aa040f807 --- /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 000000000..f3e576789 --- /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 000000000..72b94ce19 --- /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 000000000..c1aabf001 --- /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 000000000..a8a115850 --- /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 000000000..d8fe1cbbe --- /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 000000000..c68f8456f --- /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 000000000..413e702db --- /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 000000000..31e94b7d3 --- /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 000000000..d8f1f06a1 --- /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 000000000..be5a5ce16 --- /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 000000000..87195b427 --- /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 000000000..0c6f21f9e --- /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 000000000..6cda19d18 --- /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 000000000..7a1b57ece --- /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 000000000..a1fac2dcb --- /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 000000000..2f9c592c3 --- /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 000000000..999c43b43 --- /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 000000000..71b2b247e --- /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 000000000..1fb1501f2 --- /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 000000000..aa71ea2e7 --- /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 000000000..bd7013b2b --- /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 000000000..728f70097 --- /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) + } +} |
