diff options
Diffstat (limited to 'qemu/roms/SLOF/clients')
86 files changed, 13723 insertions, 0 deletions
diff --git a/qemu/roms/SLOF/clients/.gitignore b/qemu/roms/SLOF/clients/.gitignore new file mode 100644 index 000000000..c5c20bfab --- /dev/null +++ b/qemu/roms/SLOF/clients/.gitignore @@ -0,0 +1,3 @@ +*.client +*/client +*/client.unstripped diff --git a/qemu/roms/SLOF/clients/Makefile b/qemu/roms/SLOF/clients/Makefile new file mode 100644 index 000000000..d656f08ce --- /dev/null +++ b/qemu/roms/SLOF/clients/Makefile @@ -0,0 +1,29 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2008 IBM Corporation +# * All rights reserved. +# * This program and the accompanying materials +# * are made available under the terms of the BSD License +# * which accompanies this distribution, and is available at +# * http://www.opensource.org/licenses/bsd-license.php +# * +# * Contributors: +# * IBM Corporation - initial implementation +# ****************************************************************************/ + +include clients.mk + +all: + for dir in $(CLIENTS); do \ + if [ -r $$dir/Makefile ]; then \ + $(MAKE) -C $$dir "FLAG=$(FLAG)" || exit 1; \ + cp $$dir/client $$dir.client; \ + fi; \ + done + +clean distclean: + @for dir in $(CLIENTS); do \ + if [ -r $$dir/Makefile ]; then \ + $(MAKE) -C $$dir $@ || exit 1; \ + rm -f $$dir.client; \ + fi; \ + done diff --git a/qemu/roms/SLOF/clients/clients.mk b/qemu/roms/SLOF/clients/clients.mk new file mode 100644 index 000000000..ca741e420 --- /dev/null +++ b/qemu/roms/SLOF/clients/clients.mk @@ -0,0 +1,13 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2008 IBM Corporation +# * All rights reserved. +# * This program and the accompanying materials +# * are made available under the terms of the BSD License +# * which accompanies this distribution, and is available at +# * http://www.opensource.org/licenses/bsd-license.php +# * +# * Contributors: +# * IBM Corporation - initial implementation +# ****************************************************************************/ + +CLIENTS = net-snk diff --git a/qemu/roms/SLOF/clients/net-snk/Makefile b/qemu/roms/SLOF/clients/net-snk/Makefile new file mode 100644 index 000000000..c0bb73a52 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/Makefile @@ -0,0 +1,66 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2008 IBM Corporation +# * All rights reserved. +# * This program and the accompanying materials +# * are made available under the terms of the BSD License +# * which accompanies this distribution, and is available at +# * http://www.opensource.org/licenses/bsd-license.php +# * +# * Contributors: +# * IBM Corporation - initial implementation +# ****************************************************************************/ + +TOP=$(shell pwd) +export TOP +include $(TOP)/make.rules + +OBJS = kernel/kernel.o oflib/oflib.o libc/libc-glue.o app/app.o +.PHONY : subdirs clean depend mrproper + +CLIENTLIBS = $(LIBCMNDIR)/libelf.a $(LIBCMNDIR)/libc.a + +all: .depend subdirs + $(MAKE) client + +client : $(OBJS) $(CLIENTLIBS) + $(LD) $(LDFLAGS) -o $@ -Tclient.lds $(OBJS) $(CLIENTLIBS) + cp $@ $@.unstripped + $(STRIP) --strip-unneeded $@ + +client.dis: client + $(OBJDUMP) -DSsx client.unstripped > $@ + +sec-client : subdirs $(OBJS) $(LIBCMNDIR)/libc.a + $(LD) $(LDFLAGS) -o $@ -Tsec-client.lds $(OBJS) $(LIBCMNDIR)/libc.a + +subdirs : + @for dir in $(dir $(OBJS)); do \ + $(MAKE) -C $$dir || exit 1; \ + done + +$(LIBCMNDIR)/%.a: + $(MAKE) -C $(LIBCMNDIR) $(@:$(LIBCMNDIR)/%.a=%) + +clean: + @for dir in $(dir $(OBJS)); do \ + $(MAKE) -C $$dir clean; \ + done + rm -f $(OBJS) client diag netboot sec-client net-diag \ + *.dis client.unstripped fpga-client + +mrproper : clean + $(MAKE) -C app mrproper + $(MAKE) -C libc mrproper + $(MAKE) -C kernel mrproper + $(MAKE) -C oflib mrproper + find -name .*.bak | xargs rm -rf + $(RM) .depend + +distclean: mrproper + +depend .depend: + $(MAKE) -C app depend + $(MAKE) -C libc depend + $(MAKE) -C kernel depend + $(MAKE) -C oflib depend + touch .depend diff --git a/qemu/roms/SLOF/clients/net-snk/app/Makefile b/qemu/roms/SLOF/clients/net-snk/app/Makefile new file mode 100644 index 000000000..8b0c08f19 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/Makefile @@ -0,0 +1,51 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2011 IBM Corporation +# * All rights reserved. +# * This program and the accompanying materials +# * are made available under the terms of the BSD License +# * which accompanies this distribution, and is available at +# * http://www.opensource.org/licenses/bsd-license.php +# * +# * Contributors: +# * IBM Corporation - initial implementation +# ****************************************************************************/ + +ifndef TOP +TOP = $(shell while ! test -e make.rules; do cd .. ; done; pwd) +export TOP +endif +include $(TOP)/make.rules + +CFLAGS +=$(ADDCFLAGS) + +OBJS = main.o +OBJDIRS = netlib/netlib.o netapps/netboot.o +OBJDIRS += netapps/ping.o +OBJDIRS += netapps/args.o + +ifeq ($(SNK_BIOSEMU_APPS), 1) +OBJDIRS += biosemu/biosemu_app.o +CFLAGS += -DSNK_BIOSEMU_APPS +endif + +SUBDIRS = $(dir $(OBJDIRS)) + +all: subdirs + $(MAKE) app.o + +subdirs: + for dir in $(SUBDIRS); do \ + $(MAKE) -C $$dir DIRECTORY=$(DIRECTORY)$$dir || exit 1; \ + done + +app.o: $(OBJS) $(OBJDIRS) + $(LD) $(LDFLAGS) $(OBJDIRS) $(OBJS) -o $@ -r + +clean : + $(RM) -f *.o *.a *.i + for dir in $(SUBDIRS); do \ + $(CLEAN) ; \ + $(MAKE) -C $$dir DIRECTORY=$(DIRECTORY)$$dir clean; \ + done + +include $(TOP)/make.depend diff --git a/qemu/roms/SLOF/clients/net-snk/app/biosemu/Makefile b/qemu/roms/SLOF/clients/net-snk/app/biosemu/Makefile new file mode 100644 index 000000000..3a07ada31 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/biosemu/Makefile @@ -0,0 +1,38 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2008 IBM Corporation +# * All rights reserved. +# * This program and the accompanying materials +# * are made available under the terms of the BSD License +# * which accompanies this distribution, and is available at +# * http://www.opensource.org/licenses/bsd-license.php +# * +# * Contributors: +# * IBM Corporation - initial implementation +# ****************************************************************************/ + +ifndef TOP + TOP = $(shell while ! test -e make.rules; do cd .. ; done; pwd) + export TOP +endif +include $(TOP)/make.rules + +CFLAGS += -I$(ROOTDIR)/other-licence/x86emu -I$(ROOTDIR)/other-licence/x86emu/include + +OBJS = biosemu.o debug.o device.o mem.o io.o interrupt.o vbe.o +LIBX86EMU = $(ROOTDIR)/other-licence/x86emu/libx86emu.a + +.PHONY: $(LIBX86EMU) + +all: biosemu_app.o + +# special rule for libx86emu.a +$(LIBX86EMU): + $(MAKE) -C $(dir $@) + +biosemu_app.o: $(OBJS) $(LIBX86EMU) + $(LD) $(LDFLAGS) $^ -o $@ -r + +clean: + $(RM) -f *.o *.a *.i *.s + +include $(TOP)/make.depend diff --git a/qemu/roms/SLOF/clients/net-snk/app/biosemu/biosemu.c b/qemu/roms/SLOF/clients/net-snk/app/biosemu/biosemu.c new file mode 100644 index 000000000..82a763acf --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/biosemu/biosemu.c @@ -0,0 +1,345 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <stdint.h> +#include <cpu.h> + +#include "debug.h" + +#include <x86emu/x86emu.h> +#include <x86emu/regs.h> +#include <x86emu/prim_ops.h> // for push_word + +#include "biosemu.h" +#include "io.h" +#include "mem.h" +#include "interrupt.h" +#include "device.h" + +#include <rtas.h> + + +static X86EMU_memFuncs my_mem_funcs = { + my_rdb, my_rdw, my_rdl, + my_wrb, my_wrw, my_wrl +}; + +static X86EMU_pioFuncs my_pio_funcs = { + my_inb, my_inw, my_inl, + my_outb, my_outw, my_outl +}; + +void dump(uint8_t * addr, uint32_t len); + +uint32_t +biosemu(char argc, char **argv) +{ + uint8_t *rom_image; + int i = 0; + uint8_t *biosmem; + uint32_t biosmem_size; +#ifdef DEBUG + //debug_flags = DEBUG_PRINT_INT10 | DEBUG_PNP;// | DEBUG_PMM;// | DEBUG_INTR | DEBUG_CHECK_VMEM_ACCESS | DEBUG_MEM | DEBUG_IO;// | DEBUG_TRACE_X86EMU | DEBUG_JMP; +#endif + if (argc < 4) { + printf("Usage %s <vmem_base> <vmem_size> <device_path> [<debug_flags>]\n", argv[0]); + for (i = 0; i < argc; i++) { + printf("argv[%d]: %s\n", i, argv[i]); + } + return -1; + } + // argv[1] is address of virtual BIOS mem... + // argv[2] is the size + biosmem = (uint8_t *) strtoul(argv[1], 0, 16); + biosmem_size = strtoul(argv[2], 0, 16); + if (biosmem_size < MIN_REQUIRED_VMEM_SIZE) { + printf("Error: Not enough virtual memory: %x, required: %x!\n", + biosmem_size, MIN_REQUIRED_VMEM_SIZE); + return -1; + } + // argv[3] is the device to open and use... + if (dev_init(argv[3]) != 0) { + printf("Error initializing device!\n"); + return -1; + } + if (dev_check_exprom() != 0) { + printf("Error: Device Expansion ROM invalid!\n"); + return -1; + } + // argv[4] if set, is additional debug_flags + if (argc >= 5) { + debug_flags |= strtoul(argv[4], 0, 16); + printf("debug_flags: %x\n", debug_flags); + } + rom_image = (uint8_t *) bios_device.img_addr; + DEBUG_PRINTF("executing rom_image from %p\n", rom_image); + DEBUG_PRINTF("biosmem at %p\n", biosmem); + + DEBUG_PRINTF("Image Size: %d\n", bios_device.img_size); + + // in case we jump somewhere unexpected, or execution is finished, + // fill the biosmem with hlt instructions (0xf4) + memset(biosmem, 0xf4, biosmem_size); + + M.mem_base = (long) biosmem; + M.mem_size = biosmem_size; + DEBUG_PRINTF("membase set: %08x, size: %08x\n", (int) M.mem_base, + (int) M.mem_size); + + // copy expansion ROM image to segment OPTION_ROM_CODE_SEGMENT + // NOTE: this sometimes fails, some bytes are 0x00... so we compare + // after copying and do some retries... + uint8_t *mem_img = biosmem + (OPTION_ROM_CODE_SEGMENT << 4); + uint8_t copy_count = 0; + uint8_t cmp_result = 0; + do { +#if 0 + set_ci(); + memcpy(mem_img, rom_image, len); + clr_ci(); +#else + // memcpy fails... try copy byte-by-byte with set/clr_ci + uint8_t c; + for (i = 0; i < bios_device.img_size; i++) { + set_ci(); + c = *(rom_image + i); + if (c != *(rom_image + i)) { + clr_ci(); + printf("Copy failed at: %x/%x\n", i, + bios_device.img_size); + printf("rom_image(%x): %x, mem_img(%x): %x\n", + i, *(rom_image + i), i, *(mem_img + i)); + break; + } + clr_ci(); + *(mem_img + i) = c; + } +#endif + copy_count++; + set_ci(); + cmp_result = memcmp(mem_img, rom_image, bios_device.img_size); + clr_ci(); + } + while ((copy_count < 5) && (cmp_result != 0)); + if (cmp_result != 0) { + printf + ("\nCopying Expansion ROM Image to Memory failed after %d retries! (%x)\n", + copy_count, cmp_result); + dump(rom_image, 0x20); + dump(mem_img, 0x20); + return 0; + } + // setup default Interrupt Vectors + // some expansion ROMs seem to check for these addresses.. + // each handler is only an IRET (0xCF) instruction + // ROM BIOS Int 10 Handler F000:F065 + my_wrl(0x10 * 4, 0xf000f065); + my_wrb(0x000ff065, 0xcf); + // ROM BIOS Int 11 Handler F000:F84D + my_wrl(0x11 * 4, 0xf000f84d); + my_wrb(0x000ff84d, 0xcf); + // ROM BIOS Int 12 Handler F000:F841 + my_wrl(0x12 * 4, 0xf000f841); + my_wrb(0x000ff841, 0xcf); + // ROM BIOS Int 13 Handler F000:EC59 + my_wrl(0x13 * 4, 0xf000ec59); + my_wrb(0x000fec59, 0xcf); + // ROM BIOS Int 14 Handler F000:E739 + my_wrl(0x14 * 4, 0xf000e739); + my_wrb(0x000fe739, 0xcf); + // ROM BIOS Int 15 Handler F000:F859 + my_wrl(0x15 * 4, 0xf000f859); + my_wrb(0x000ff859, 0xcf); + // ROM BIOS Int 16 Handler F000:E82E + my_wrl(0x16 * 4, 0xf000e82e); + my_wrb(0x000fe82e, 0xcf); + // ROM BIOS Int 17 Handler F000:EFD2 + my_wrl(0x17 * 4, 0xf000efd2); + my_wrb(0x000fefd2, 0xcf); + // ROM BIOS Int 1A Handler F000:FE6E + my_wrl(0x1a * 4, 0xf000fe6e); + my_wrb(0x000ffe6e, 0xcf); + + // setup BIOS Data Area (0000:04xx, or 0040:00xx) + // we currently 0 this area, meaning "we dont have + // any hardware" :-) no serial/parallel ports, floppys, ... + memset(biosmem + 0x400, 0x0, 0x100); + + // at offset 13h in BDA is the memory size in kbytes + my_wrw(0x413, biosmem_size / 1024); + // at offset 0eh in BDA is the segment of the Extended BIOS Data Area + // see setup further down + my_wrw(0x40e, INITIAL_EBDA_SEGMENT); + // TODO: setup BDA Video Data ( offset 49h-66h) + // e.g. to store video mode, cursor position, ... + // in int10 (done) handler and VBE Functions + + // TODO: setup BDA Fixed Disk Data + // 74h: Fixed Disk Last Operation Status + // 75h: Fixed Disk Number of Disk Drives + + // TODO: check BDA for further needed data... + + //setup Extended BIOS Data Area + //we currently 0 this area + memset(biosmem + (INITIAL_EBDA_SEGMENT << 4), 0, INITIAL_EBDA_SIZE); + // at offset 0h in EBDA is the size of the EBDA in KB + my_wrw((INITIAL_EBDA_SEGMENT << 4) + 0x0, INITIAL_EBDA_SIZE / 1024); + //TODO: check for further needed EBDA data... + + // setup original ROM BIOS Area (F000:xxxx) + char *date = "06/11/99"; + for (i = 0; date[i]; i++) + my_wrb(0xffff5 + i, date[i]); + // set up eisa ident string + char *ident = "PCI_ISA"; + for (i = 0; ident[i]; i++) + my_wrb(0xfffd9 + i, ident[i]); + + // write system model id for IBM-AT + // according to "Ralf Browns Interrupt List" Int15 AH=C0 Table 515, + // model FC is the original AT and also used in all DOSEMU Versions. + my_wrb(0xFFFFE, 0xfc); + + //setup interrupt handler + X86EMU_intrFuncs intrFuncs[256]; + for (i = 0; i < 256; i++) + intrFuncs[i] = handleInterrupt; + X86EMU_setupIntrFuncs(intrFuncs); + X86EMU_setupPioFuncs(&my_pio_funcs); + X86EMU_setupMemFuncs(&my_mem_funcs); + + // setup the CPU + M.x86.R_AH = bios_device.bus; + M.x86.R_AL = bios_device.devfn; + M.x86.R_DX = 0x80; + M.x86.R_EIP = 3; + M.x86.R_CS = OPTION_ROM_CODE_SEGMENT; + + // Initialize stack and data segment + M.x86.R_SS = STACK_SEGMENT; + M.x86.R_SP = STACK_START_OFFSET; + M.x86.R_DS = DATA_SEGMENT; + + // push a HLT instruction and a pointer to it onto the stack + // any return will pop the pointer and jump to the HLT, thus + // exiting (more or less) cleanly + push_word(0xf4f4); //F4=HLT + push_word(M.x86.R_SS); + push_word(M.x86.R_SP + 2); + + CHECK_DBG(DEBUG_TRACE_X86EMU) { + X86EMU_trace_on(); + } else { +#ifdef DEBUG + M.x86.debug |= DEBUG_SAVE_IP_CS_F; + M.x86.debug |= DEBUG_DECODE_F; + M.x86.debug |= DEBUG_DECODE_NOPRINT_F; +#endif + } + CHECK_DBG(DEBUG_JMP) { + M.x86.debug |= DEBUG_TRACEJMP_F; + M.x86.debug |= DEBUG_TRACEJMP_REGS_F; + M.x86.debug |= DEBUG_TRACECALL_F; + M.x86.debug |= DEBUG_TRACECALL_REGS_F; + } + + DEBUG_PRINTF("Executing Initialization Vector...\n"); + X86EMU_exec(); + DEBUG_PRINTF("done\n"); + + // according to PNP BIOS Spec, Option ROMs should upon exit, return some boot device status in + // AX (see PNP BIOS Spec Section 3.3 + DEBUG_PRINTF_CS_IP("Option ROM Exit Status: %04x\n", M.x86.R_AX); +#ifdef DEBUG + DEBUG_PRINTF("Exit Status Decode:\n"); + if (M.x86.R_AX & 0x100) { // bit 8 + DEBUG_PRINTF + (" IPL Device supporting INT 13h Block Device Format:\n"); + switch (((M.x86.R_AX >> 4) & 0x3)) { // bits 5:4 + case 0: + DEBUG_PRINTF(" No IPL Device attached\n"); + break; + case 1: + DEBUG_PRINTF(" IPL Device status unknown\n"); + break; + case 2: + DEBUG_PRINTF(" IPL Device attached\n"); + break; + case 3: + DEBUG_PRINTF(" IPL Device status RESERVED!!\n"); + break; + } + } + if (M.x86.R_AX & 0x80) { // bit 7 + DEBUG_PRINTF + (" Output Device supporting INT 10h Character Output:\n"); + switch (((M.x86.R_AX >> 4) & 0x3)) { // bits 5:4 + case 0: + DEBUG_PRINTF(" No Display Device attached\n"); + break; + case 1: + DEBUG_PRINTF(" Display Device status unknown\n"); + break; + case 2: + DEBUG_PRINTF(" Display Device attached\n"); + break; + case 3: + DEBUG_PRINTF(" Display Device status RESERVED!!\n"); + break; + } + } + if (M.x86.R_AX & 0x40) { // bit 6 + DEBUG_PRINTF + (" Input Device supporting INT 9h Character Input:\n"); + switch (((M.x86.R_AX >> 4) & 0x3)) { // bits 5:4 + case 0: + DEBUG_PRINTF(" No Input Device attached\n"); + break; + case 1: + DEBUG_PRINTF(" Input Device status unknown\n"); + break; + case 2: + DEBUG_PRINTF(" Input Device attached\n"); + break; + case 3: + DEBUG_PRINTF(" Input Device status RESERVED!!\n"); + break; + } + } +#endif + // check wether the stack is "clean" i.e. containing the HLT instruction + // we pushed before executing, and pointing to the original stack address... + // indicating that the initialization probably was successful + if ((pop_word() == 0xf4f4) && (M.x86.R_SS == STACK_SEGMENT) + && (M.x86.R_SP == STACK_START_OFFSET)) { + DEBUG_PRINTF("Stack is clean, initialization successful!\n"); + } else { + DEBUG_PRINTF + ("Stack unclean, initialization probably NOT COMPLETE!!!\n"); + DEBUG_PRINTF("SS:SP = %04x:%04x, expected: %04x:%04x\n", + M.x86.R_SS, M.x86.R_SP, STACK_SEGMENT, + STACK_START_OFFSET); + } + + + // TODO: according to the BIOS Boot Spec initializations may be ended using INT18h and setting + // the status. + // We need to implement INT18 accordingly, pseudo code is in specsbbs101.pdf page 30 + // (also for Int19) + return 0; +} diff --git a/qemu/roms/SLOF/clients/net-snk/app/biosemu/biosemu.h b/qemu/roms/SLOF/clients/net-snk/app/biosemu/biosemu.h new file mode 100644 index 000000000..28b7ab881 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/biosemu/biosemu.h @@ -0,0 +1,42 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef _BIOSEMU_BIOSEMU_H_ +#define _BIOSEMU_BIOSEMU_H_ + +#define MIN_REQUIRED_VMEM_SIZE 0x100000 // 1MB + +//define default segments for different components +#define STACK_SEGMENT 0x1000 //1000:xxxx +#define STACK_START_OFFSET 0xfffe + +#define DATA_SEGMENT 0x2000 +#define VBE_SEGMENT 0x3000 + +#define PMM_CONV_SEGMENT 0x4000 // 4000:xxxx is PMM conventional memory area, extended memory area + // will be anything beyound MIN_REQUIRED_MEMORY_SIZE +#define PNP_DATA_SEGMENT 0x5000 + +#define OPTION_ROM_CODE_SEGMENT 0xc000 + +#define BIOS_DATA_SEGMENT 0xF000 +// both EBDA values are _initial_ values, they may (and will be) changed at runtime by option ROMs!! +#define INITIAL_EBDA_SEGMENT 0xF600 // segment of the Extended BIOS Data Area +#define INITIAL_EBDA_SIZE 0x400 // size of the EBDA (at least 1KB!! since size is stored in KB!) + +#define PMM_INT_NUM 0xFC // we misuse INT FC for PMM functionality, at the PMM Entry Point + // Address, there will only be a call to this INT and a RETF +#define PNP_INT_NUM 0xFD + +uint32_t biosemu(char argc, char **argv); + +#endif diff --git a/qemu/roms/SLOF/clients/net-snk/app/biosemu/debug.c b/qemu/roms/SLOF/clients/net-snk/app/biosemu/debug.c new file mode 100644 index 000000000..2fce24497 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/biosemu/debug.c @@ -0,0 +1,55 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <cpu.h> + +#include "debug.h" + +uint32_t debug_flags = 0; + +void +dump(uint8_t * addr, uint32_t len) +{ + printf("\n\r%s(%p, %x):\n", __FUNCTION__, addr, len); + while (len) { + unsigned int tmpCnt = len; + unsigned char x; + if (tmpCnt > 8) + tmpCnt = 8; + printf("\n\r%p: ", addr); + // print hex + while (tmpCnt--) { + set_ci(); + x = *addr++; + clr_ci(); + printf("%02x ", x); + } + tmpCnt = len; + if (tmpCnt > 8) + tmpCnt = 8; + len -= tmpCnt; + //reset addr ptr to print ascii + addr = addr - tmpCnt; + // print ascii + while (tmpCnt--) { + set_ci(); + x = *addr++; + clr_ci(); + if ((x < 32) || (x >= 127)) { + //non-printable char + x = '.'; + } + printf("%c", x); + } + } + printf("\n"); +} diff --git a/qemu/roms/SLOF/clients/net-snk/app/biosemu/debug.h b/qemu/roms/SLOF/clients/net-snk/app/biosemu/debug.h new file mode 100644 index 000000000..46a026ae6 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/biosemu/debug.h @@ -0,0 +1,74 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ +#ifndef _BIOSEMU_DEBUG_H_ +#define _BIOSEMU_DEBUG_H_ + +#include <stdio.h> +#include <stdint.h> + +extern uint32_t debug_flags; +// from x86emu...needed for debugging +extern void x86emu_dump_xregs(void); + +#define DEBUG_IO 0x1 +#define DEBUG_MEM 0x2 +// set this to print messages for certain virtual memory accesses (Interrupt Vectors, ...) +#define DEBUG_CHECK_VMEM_ACCESS 0x4 +#define DEBUG_INTR 0x8 +#define DEBUG_PRINT_INT10 0x10 // set to have the INT10 routine print characters +#define DEBUG_VBE 0x20 +#define DEBUG_PMM 0x40 +#define DEBUG_DISK 0x80 +#define DEBUG_PNP 0x100 + +#define DEBUG_TRACE_X86EMU 0x1000 +// set to enable tracing of JMPs in x86emu +#define DEBUG_JMP 0x2000 + +//#define DEBUG +#ifdef DEBUG + +#define CHECK_DBG(_flag) if (debug_flags & _flag) + +#define DEBUG_PRINTF(_x...) printf(_x); +// prints the CS:IP before the printout, NOTE: actually its CS:IP of the _next_ instruction +// to be executed, since the x86emu advances CS:IP _before_ actually executing an instruction +#define DEBUG_PRINTF_CS_IP(_x...) DEBUG_PRINTF("%x:%x ", M.x86.R_CS, M.x86.R_IP); DEBUG_PRINTF(_x); + +#define DEBUG_PRINTF_IO(_x...) CHECK_DBG(DEBUG_IO) { DEBUG_PRINTF_CS_IP(_x) } +#define DEBUG_PRINTF_MEM(_x...) CHECK_DBG(DEBUG_MEM) { DEBUG_PRINTF_CS_IP(_x) } +#define DEBUG_PRINTF_INTR(_x...) CHECK_DBG(DEBUG_INTR) { DEBUG_PRINTF_CS_IP(_x) } +#define DEBUG_PRINTF_VBE(_x...) CHECK_DBG(DEBUG_VBE) { DEBUG_PRINTF_CS_IP(_x) } +#define DEBUG_PRINTF_PMM(_x...) CHECK_DBG(DEBUG_PMM) { DEBUG_PRINTF_CS_IP(_x) } +#define DEBUG_PRINTF_DISK(_x...) CHECK_DBG(DEBUG_DISK) { DEBUG_PRINTF_CS_IP(_x) } +#define DEBUG_PRINTF_PNP(_x...) CHECK_DBG(DEBUG_PNP) { DEBUG_PRINTF_CS_IP(_x) } + +#else + +#define CHECK_DBG(_flag) if (0) + +#define DEBUG_PRINTF(_x...) +#define DEBUG_PRINTF_CS_IP(_x...) + +#define DEBUG_PRINTF_IO(_x...) +#define DEBUG_PRINTF_MEM(_x...) +#define DEBUG_PRINTF_INTR(_x...) +#define DEBUG_PRINTF_VBE(_x...) +#define DEBUG_PRINTF_PMM(_x...) +#define DEBUG_PRINTF_DISK(_x...) +#define DEBUG_PRINTF_PNP(_x...) + +#endif //DEBUG + +void dump(uint8_t * addr, uint32_t len); + +#endif diff --git a/qemu/roms/SLOF/clients/net-snk/app/biosemu/device.c b/qemu/roms/SLOF/clients/net-snk/app/biosemu/device.c new file mode 100644 index 000000000..514b87e62 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/biosemu/device.c @@ -0,0 +1,324 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + + +#include "device.h" +#include "rtas.h" +#include <stdio.h> +#include <string.h> +#include <of.h> // use translate_address_dev and get_puid from net-snk +#include "debug.h" + +typedef struct { + uint8_t info; + uint8_t bus; + uint8_t devfn; + uint8_t cfg_space_offset; + uint64_t address; + uint64_t size; +} __attribute__ ((__packed__)) assigned_address_t; + + +// scan all adresses assigned to the device ("assigned-addresses" and "reg") +// store in translate_address_array for faster translation using dev_translate_address +static void +dev_get_addr_info(void) +{ + // get bus/dev/fn from assigned-addresses + int32_t len; + //max. 6 BARs and 1 Exp.ROM plus CfgSpace and 3 legacy ranges + assigned_address_t buf[11]; + len = + of_getprop(bios_device.phandle, "assigned-addresses", buf, + sizeof(buf)); + bios_device.bus = buf[0].bus; + bios_device.devfn = buf[0].devfn; + DEBUG_PRINTF("bus: %x, devfn: %x\n", bios_device.bus, + bios_device.devfn); + //store address translations for all assigned-addresses and regs in + //translate_address_array for faster translation later on... + int i = 0; + // index to insert data into translate_address_array + int taa_index = 0; + uint64_t address_offset; + for (i = 0; i < (len / sizeof(assigned_address_t)); i++, taa_index++) { + //copy all info stored in assigned-addresses + translate_address_array[taa_index].info = buf[i].info; + translate_address_array[taa_index].bus = buf[i].bus; + translate_address_array[taa_index].devfn = buf[i].devfn; + translate_address_array[taa_index].cfg_space_offset = + buf[i].cfg_space_offset; + translate_address_array[taa_index].address = buf[i].address; + translate_address_array[taa_index].size = buf[i].size; + // translate first address and store it as address_offset + address_offset = buf[i].address; + translate_address_dev(&address_offset, bios_device.phandle); + translate_address_array[taa_index].address_offset = + address_offset - buf[i].address; + } + //get "reg" property + len = of_getprop(bios_device.phandle, "reg", buf, sizeof(buf)); + for (i = 0; i < (len / sizeof(assigned_address_t)); i++) { + if ((buf[i].size == 0) || (buf[i].cfg_space_offset != 0)) { + // we dont care for ranges with size 0 and + // BARs and Expansion ROM must be in assigned-addresses... so in reg + // we only look for those without config space offset set... + // i.e. the legacy ranges + continue; + } + //copy all info stored in assigned-addresses + translate_address_array[taa_index].info = buf[i].info; + translate_address_array[taa_index].bus = buf[i].bus; + translate_address_array[taa_index].devfn = buf[i].devfn; + translate_address_array[taa_index].cfg_space_offset = + buf[i].cfg_space_offset; + translate_address_array[taa_index].address = buf[i].address; + translate_address_array[taa_index].size = buf[i].size; + // translate first address and store it as address_offset + address_offset = buf[i].address; + translate_address_dev(&address_offset, bios_device.phandle); + translate_address_array[taa_index].address_offset = + address_offset - buf[i].address; + taa_index++; + } + // store last entry index of translate_address_array + taa_last_entry = taa_index - 1; +#ifdef DEBUG + //dump translate_address_array + printf("translate_address_array: \n"); + translate_address_t ta; + for (i = 0; i <= taa_last_entry; i++) { + ta = translate_address_array[i]; + printf + ("%d: %02x%02x%02x%02x\n\taddr: %016llx\n\toffs: %016llx\n\tsize: %016llx\n", + i, ta.info, ta.bus, ta.devfn, ta.cfg_space_offset, + ta.address, ta.address_offset, ta.size); + } +#endif +} + +// to simulate accesses to legacy VGA Memory (0xA0000-0xBFFFF) +// we look for the first prefetchable memory BAR, if no prefetchable BAR found, +// we use the first memory BAR +// dev_translate_addr will translate accesses to the legacy VGA Memory into the found vmem BAR +static void +dev_find_vmem_addr(void) +{ + int i = 0; + translate_address_t ta; + int8_t tai_np = -1, tai_p = -1; // translate_address_array index for non-prefetchable and prefetchable memory + //search backwards to find first entry + for (i = taa_last_entry; i >= 0; i--) { + ta = translate_address_array[i]; + if ((ta.cfg_space_offset >= 0x10) + && (ta.cfg_space_offset <= 0x24)) { + //only BARs + if ((ta.info & 0x03) >= 0x02) { + //32/64bit memory + tai_np = i; + if ((ta.info & 0x40) != 0) { + // prefetchable + tai_p = i; + } + } + } + } + if (tai_p != -1) { + ta = translate_address_array[tai_p]; + bios_device.vmem_addr = ta.address; + bios_device.vmem_size = ta.size; + DEBUG_PRINTF + ("%s: Found prefetchable Virtual Legacy Memory BAR: %llx, size: %llx\n", + __FUNCTION__, bios_device.vmem_addr, + bios_device.vmem_size); + } else if (tai_np != -1) { + ta = translate_address_array[tai_np]; + bios_device.vmem_addr = ta.address; + bios_device.vmem_size = ta.size; + DEBUG_PRINTF + ("%s: Found non-prefetchable Virtual Legacy Memory BAR: %llx, size: %llx", + __FUNCTION__, bios_device.vmem_addr, + bios_device.vmem_size); + } + // disable vmem + //bios_device.vmem_size = 0; +} + +static void +dev_get_puid(void) +{ + // get puid + bios_device.puid = get_puid(bios_device.phandle); + DEBUG_PRINTF("puid: 0x%llx\n", bios_device.puid); +} + +static void +dev_get_device_vendor_id(void) +{ + uint32_t pci_config_0 = + rtas_pci_config_read(bios_device.puid, 4, bios_device.bus, + bios_device.devfn, 0x0); + bios_device.pci_device_id = + (uint16_t) ((pci_config_0 & 0xFFFF0000) >> 16); + bios_device.pci_vendor_id = (uint16_t) (pci_config_0 & 0x0000FFFF); + DEBUG_PRINTF("PCI Device ID: %04x, PCI Vendor ID: %x\n", + bios_device.pci_device_id, bios_device.pci_vendor_id); +} + +/* check, wether the device has a valid Expansion ROM, also search the PCI Data Structure and + * any Expansion ROM Header (using dev_scan_exp_header()) for needed information */ +uint8_t +dev_check_exprom(void) +{ + int i = 0; + translate_address_t ta; + uint64_t rom_base_addr = 0; + uint16_t pci_ds_offset; + pci_data_struct_t pci_ds; + // check for ExpROM Address (Offset 30) in taa + for (i = 0; i <= taa_last_entry; i++) { + ta = translate_address_array[i]; + if (ta.cfg_space_offset == 0x30) { + rom_base_addr = ta.address + ta.address_offset; //translated address + break; + } + } + // in the ROM there could be multiple Expansion ROM Images... start searching + // them for a x86 image + do { + if (rom_base_addr == 0) { + printf("Error: no Expansion ROM address found!\n"); + return -1; + } + set_ci(); + uint16_t rom_signature = *((uint16_t *) rom_base_addr); + clr_ci(); + if (rom_signature != 0x55aa) { + printf + ("Error: invalid Expansion ROM signature: %02x!\n", + *((uint16_t *) rom_base_addr)); + return -1; + } + set_ci(); + // at offset 0x18 is the (16bit little-endian) pointer to the PCI Data Structure + pci_ds_offset = in16le((void *) (rom_base_addr + 0x18)); + //copy the PCI Data Structure + memcpy(&pci_ds, (void *) (rom_base_addr + pci_ds_offset), + sizeof(pci_ds)); + clr_ci(); +#ifdef DEBUG + DEBUG_PRINTF("PCI Data Structure @%llx:\n", + rom_base_addr + pci_ds_offset); + dump((void *) &pci_ds, sizeof(pci_ds)); +#endif + if (strncmp((const char *) pci_ds.signature, "PCIR", 4) != 0) { + printf("Invalid PCI Data Structure found!\n"); + break; + } + //little-endian conversion + pci_ds.vendor_id = in16le(&pci_ds.vendor_id); + pci_ds.device_id = in16le(&pci_ds.device_id); + pci_ds.img_length = in16le(&pci_ds.img_length); + pci_ds.pci_ds_length = in16le(&pci_ds.pci_ds_length); + if (pci_ds.vendor_id != bios_device.pci_vendor_id) { + printf + ("Image has invalid Vendor ID: %04x, expected: %04x\n", + pci_ds.vendor_id, bios_device.pci_vendor_id); + break; + } + if (pci_ds.device_id != bios_device.pci_device_id) { + printf + ("Image has invalid Device ID: %04x, expected: %04x\n", + pci_ds.device_id, bios_device.pci_device_id); + break; + } + //DEBUG_PRINTF("Image Length: %d\n", pci_ds.img_length * 512); + //DEBUG_PRINTF("Image Code Type: %d\n", pci_ds.code_type); + if (pci_ds.code_type == 0) { + //x86 image + //store image address and image length in bios_device struct + bios_device.img_addr = rom_base_addr; + bios_device.img_size = pci_ds.img_length * 512; + // we found the image, exit the loop + break; + } else { + // no x86 image, check next image (if any) + rom_base_addr += pci_ds.img_length * 512; + } + if ((pci_ds.indicator & 0x80) == 0x80) { + //last image found, exit the loop + DEBUG_PRINTF("Last PCI Expansion ROM Image found.\n"); + break; + } + } + while (bios_device.img_addr == 0); + // in case we did not find a valid x86 Expansion ROM Image + if (bios_device.img_addr == 0) { + printf("Error: no valid x86 Expansion ROM Image found!\n"); + return -1; + } + return 0; +} + +uint8_t +dev_init(char *device_name) +{ + uint8_t rval = 0; + //init bios_device struct + DEBUG_PRINTF("%s(%s)\n", __FUNCTION__, device_name); + memset(&bios_device, 0, sizeof(bios_device)); + bios_device.ihandle = of_open(device_name); + if (bios_device.ihandle == 0) { + DEBUG_PRINTF("%s is no valid device!\n", device_name); + return -1; + } + bios_device.phandle = of_finddevice(device_name); + dev_get_addr_info(); + dev_find_vmem_addr(); + dev_get_puid(); + dev_get_device_vendor_id(); + return rval; +} + +// translate address function using translate_address_array assembled +// by dev_get_addr_info... MUCH faster than calling translate_address_dev +// and accessing client interface for every translation... +// returns: 0 if addr not found in translate_address_array, 1 if found. +uint8_t +dev_translate_address(uint64_t * addr) +{ + int i = 0; + translate_address_t ta; + //check if it is an access to legacy VGA Mem... if it is, map the address + //to the vmem BAR and then translate it... + // (translation info provided by Ben Herrenschmidt) + // NOTE: the translation seems to only work for NVIDIA cards... but it is needed + // to make some NVIDIA cards work at all... + if ((bios_device.vmem_size > 0) + && ((*addr >= 0xA0000) && (*addr < 0xB8000))) { + *addr = (*addr - 0xA0000) * 4 + 2 + bios_device.vmem_addr; + } + if ((bios_device.vmem_size > 0) + && ((*addr >= 0xB8000) && (*addr < 0xC0000))) { + uint8_t shift = *addr & 1; + *addr &= 0xfffffffe; + *addr = (*addr - 0xB8000) * 4 + shift + bios_device.vmem_addr; + } + for (i = 0; i <= taa_last_entry; i++) { + ta = translate_address_array[i]; + if ((*addr >= ta.address) && (*addr <= (ta.address + ta.size))) { + *addr += ta.address_offset; + return 1; + } + } + return 0; +} diff --git a/qemu/roms/SLOF/clients/net-snk/app/biosemu/device.h b/qemu/roms/SLOF/clients/net-snk/app/biosemu/device.h new file mode 100644 index 000000000..425dd3caf --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/biosemu/device.h @@ -0,0 +1,157 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef DEVICE_LIB_H +#define DEVICE_LIB_H + +#include <stdint.h> +#include <cpu.h> +#include "of.h" +#include <stdio.h> + +// a Expansion Header Struct as defined in Plug and Play BIOS Spec 1.0a Chapter 3.2 +typedef struct { + char signature[4]; // signature + uint8_t structure_revision; + uint8_t length; // in 16 byte blocks + uint16_t next_header_offset; // offset to next Expansion Header as 16bit little-endian value, as offset from the start of the Expansion ROM + uint8_t reserved; + uint8_t checksum; // the sum of all bytes of the Expansion Header must be 0 + uint32_t device_id; // PnP Device ID as 32bit little-endian value + uint16_t p_manufacturer_string; //16bit little-endian offset from start of Expansion ROM + uint16_t p_product_string; //16bit little-endian offset from start of Expansion ROM + uint8_t device_base_type; + uint8_t device_sub_type; + uint8_t device_if_type; + uint8_t device_indicators; + // the following vectors are all 16bit little-endian offsets from start of Expansion ROM + uint16_t bcv; // Boot Connection Vector + uint16_t dv; // Disconnect Vector + uint16_t bev; // Bootstrap Entry Vector + uint16_t reserved_2; + uint16_t sriv; // Static Resource Information Vector +} __attribute__ ((__packed__)) exp_header_struct_t; + +// a PCI Data Struct as defined in PCI 2.3 Spec Chapter 6.3.1.2 +typedef struct { + uint8_t signature[4]; // signature, the String "PCIR" + uint16_t vendor_id; + uint16_t device_id; + uint16_t reserved; + uint16_t pci_ds_length; // PCI Data Structure Length, 16bit little-endian value + uint8_t pci_ds_revision; + uint8_t class_code[3]; + uint16_t img_length; // length of the Exp.ROM Image, 16bit little-endian value in 512 bytes + uint16_t img_revision; + uint8_t code_type; + uint8_t indicator; + uint16_t reserved_2; +} __attribute__ ((__packed__)) pci_data_struct_t; + +typedef struct { + uint8_t bus; + uint8_t devfn; + uint64_t puid; + phandle_t phandle; + ihandle_t ihandle; + // store the address of the BAR that is used to simulate + // legacy VGA memory accesses + uint64_t vmem_addr; + uint64_t vmem_size; + // used to buffer I/O Accesses, that do not access the I/O Range of the device... + // 64k might be overkill, but we can buffer all I/O accesses... + uint8_t io_buffer[64 * 1024]; + uint16_t pci_vendor_id; + uint16_t pci_device_id; + // translated address of the "PC-Compatible" Expansion ROM Image for this device + uint64_t img_addr; + uint32_t img_size; // size of the Expansion ROM Image (read from the PCI Data Structure) +} device_t; + +typedef struct { + uint8_t info; + uint8_t bus; + uint8_t devfn; + uint8_t cfg_space_offset; + uint64_t address; + uint64_t address_offset; + uint64_t size; +} __attribute__ ((__packed__)) translate_address_t; + +// array to store address translations for this +// device. Needed for faster address translation, so +// not every I/O or Memory Access needs to call translate_address_dev +// and access the device tree +// 6 BARs, 1 Exp. ROM, 1 Cfg.Space, and 3 Legacy +// translations are supported... this should be enough for +// most devices... for VGA it is enough anyways... +translate_address_t translate_address_array[11]; + +// index of last translate_address_array entry +// set by get_dev_addr_info function +uint8_t taa_last_entry; + +device_t bios_device; + +uint8_t dev_init(char *device_name); +// NOTE: for dev_check_exprom to work, dev_init MUST be called first! +uint8_t dev_check_exprom(void); + +uint8_t dev_translate_address(uint64_t * addr); + +/* endianness swap functions for 16 and 32 bit words + * copied from axon_pciconfig.c + */ +static inline void +out32le(void *addr, uint32_t val) +{ + asm volatile ("stwbrx %0, 0, %1"::"r" (val), "r"(addr)); +} + +static inline uint32_t +in32le(void *addr) +{ + uint32_t val; + const uint32_t *zaddr = addr; + asm volatile ("lwbrx %0, %y1" : "=r"(val) : "Z"(*zaddr)); + return val; +} + +static inline void +out16le(void *addr, uint16_t val) +{ + asm volatile ("sthbrx %0, 0, %1"::"r" (val), "r"(addr)); +} + +static inline uint16_t +in16le(void *addr) +{ + uint16_t val; + const uint16_t *zaddr = addr; + asm volatile ("lhbrx %0, %y1" : "=r"(val) : "Z"(*zaddr)); + return val; +} + +/* debug function, dumps HID1 and HID4 to detect wether caches are on/off */ +static inline void +dumpHID(void) +{ + uint64_t hid; + //HID1 = 1009 + __asm__ __volatile__("mfspr %0, 1009":"=r"(hid)); + printf("HID1: %016llx\n", hid); + //HID4 = 1012 + __asm__ __volatile__("mfspr %0, 1012":"=r"(hid)); + printf("HID4: %016llx\n", hid); +} + +#endif diff --git a/qemu/roms/SLOF/clients/net-snk/app/biosemu/interrupt.c b/qemu/roms/SLOF/clients/net-snk/app/biosemu/interrupt.c new file mode 100644 index 000000000..ac3f5b430 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/biosemu/interrupt.c @@ -0,0 +1,606 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdio.h> + +#include <rtas.h> + +#include "biosemu.h" +#include "mem.h" +#include "device.h" +#include "debug.h" +#include "interrupt.h" + +#include <x86emu/x86emu.h> +#include <x86emu/prim_ops.h> + + + +//setup to run the code at the address, that the Interrupt Vector points to... +static void +setupInt(int intNum) +{ + DEBUG_PRINTF_INTR("%s(%x): executing interrupt handler @%08x\n", + __FUNCTION__, intNum, my_rdl(intNum * 4)); + // push current R_FLG... will be popped by IRET + push_word((u16) M.x86.R_FLG); + CLEAR_FLAG(F_IF); + CLEAR_FLAG(F_TF); + // push current CS:IP to the stack, will be popped by IRET + push_word(M.x86.R_CS); + push_word(M.x86.R_IP); + // set CS:IP to the interrupt handler address... so the next executed instruction will + // be the interrupt handler + M.x86.R_CS = my_rdw(intNum * 4 + 2); + M.x86.R_IP = my_rdw(intNum * 4); +} + +// handle int10 (VGA BIOS Interrupt) +static void +handleInt10(void) +{ + // the data for INT10 is stored in BDA (0000:0400h) offset 49h-66h + // function number in AH + //DEBUG_PRINTF_CS_IP("%s:\n", __FUNCTION__); + //x86emu_dump_xregs(); + //if ((M.x86.R_IP == 0x32c2) && (M.x86.R_SI == 0x1ce2)){ + //X86EMU_trace_on(); + //M.x86.debug &= ~DEBUG_DECODE_NOPRINT_F; + //} + switch (M.x86.R_AH) { + case 0x00: + // set video mode + // BDA offset 49h is current video mode + my_wrb(0x449, M.x86.R_AL); + if (M.x86.R_AL > 7) + M.x86.R_AL = 0x20; + else if (M.x86.R_AL == 6) + M.x86.R_AL = 0x3f; + else + M.x86.R_AL = 0x30; + break; + case 0x01: + // set cursor shape + // ignore + break; + case 0x02: + // set cursor position + // BH: pagenumber, DX: cursor_pos (DH:row, DL:col) + // BDA offset 50h-60h are 8 cursor position words for + // eight possible video pages + my_wrw(0x450 + (M.x86.R_BH * 2), M.x86.R_DX); + break; + case 0x03: + //get cursor position + // BH: pagenumber + // BDA offset 50h-60h are 8 cursor position words for + // eight possible video pages + M.x86.R_AX = 0; + M.x86.R_CH = 0; // start scan line ??? + M.x86.R_CL = 0; // end scan line ??? + M.x86.R_DX = my_rdw(0x450 + (M.x86.R_BH * 2)); + break; + case 0x05: + // set active page + // BDA offset 62h is current page number + my_wrb(0x462, M.x86.R_AL); + break; + case 0x06: + //scroll up windows + break; + case 0x07: + //scroll down windows + break; + case 0x08: + //read character and attribute at position + M.x86.R_AH = 0x07; // white-on-black + M.x86.R_AL = 0x20; // a space... + break; + case 0x09: + // write character and attribute + //AL: char, BH: page number, BL: attribute, CX: number of times to write + //BDA offset 62h is current page number + CHECK_DBG(DEBUG_PRINT_INT10) { + uint32_t i = 0; + if (M.x86.R_BH == my_rdb(0x462)) { + for (i = 0; i < M.x86.R_CX; i++) + printf("%c", M.x86.R_AL); + } + } + break; + case 0x0a: + // write character + //AL: char, BH: page number, BL: attribute, CX: number of times to write + //BDA offset 62h is current page number + CHECK_DBG(DEBUG_PRINT_INT10) { + uint32_t i = 0; + if (M.x86.R_BH == my_rdb(0x462)) { + for (i = 0; i < M.x86.R_CX; i++) + printf("%c", M.x86.R_AL); + } + } + break; + case 0x0e: + // teletype output: write character and advance cursor... + //AL: char, BH: page number, BL: attribute + //BDA offset 62h is current page number + CHECK_DBG(DEBUG_PRINT_INT10) { + // we ignore the pagenumber on this call... + //if (M.x86.R_BH == my_rdb(0x462)) + { + printf("%c", M.x86.R_AL); + // for debugging, to read all lines + //if (M.x86.R_AL == 0xd) // carriage return + // printf("\n"); + } + } + break; + case 0x0f: + // get video mode + // BDA offset 49h is current video mode + // BDA offset 62h is current page number + // BDA offset 4ah is columns on screen + M.x86.R_AH = 80; //number of character columns... we hardcode it to 80 + M.x86.R_AL = my_rdb(0x449); + M.x86.R_BH = my_rdb(0x462); + break; + default: + printf("%s(): unknown function (%x) for int10 handler.\n", + __FUNCTION__, M.x86.R_AH); + DEBUG_PRINTF_INTR("AX=%04x BX=%04x CX=%04x DX=%04x\n", + M.x86.R_AX, M.x86.R_BX, M.x86.R_CX, + M.x86.R_DX); + HALT_SYS(); + break; + } +} + +// this table translates ASCII chars into their XT scan codes: +static uint8_t keycode_table[256] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0 - 7 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8 - 15 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 16 - 23 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 24 - 31 + 0x39, 0x02, 0x28, 0x04, 0x05, 0x06, 0x08, 0x28, // 32 - 39 + 0x0a, 0x0b, 0x09, 0x2b, 0x33, 0x0d, 0x34, 0x35, // 40 - 47 + 0x0b, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // 48 - 55 + 0x09, 0x0a, 0x27, 0x27, 0x33, 0x2b, 0x34, 0x35, // 56 - 63 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 64 - 71 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 72 - 79 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 80 - 87 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 88 - 95 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 96 - 103 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 104 - 111 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 112 - 119 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 120 - 127 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ... + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static void +translate_keycode(uint64_t * keycode) +{ + uint8_t scan_code = 0; + uint8_t char_code = 0; + if (*keycode < 256) { + scan_code = keycode_table[*keycode]; + char_code = (uint8_t) * keycode & 0xff; + } else { + switch (*keycode) { + case 0x1b50: + // F1 + scan_code = 0x3b; + char_code = 0x0; + break; + default: + printf("%s(): unknown multibyte keycode: %llx\n", + __FUNCTION__, *keycode); + break; + } + } + //assemble scan/char code in keycode + *keycode = (uint64_t) ((((uint16_t) scan_code) << 8) | char_code); +} + +// handle int16 (Keyboard BIOS Interrupt) +static void +handleInt16(void) +{ + // keyboard buffer is in BIOS Memory Area: + // offset 0x1a (WORD) pointer to next char in keybuffer + // offset 0x1c (WORD) pointer to next insert slot in keybuffer + // offset 0x1e-0x3e: 16 WORD Ring Buffer + // since we currently always read the char from the FW buffer, + // we misuse the ring buffer, we use it as pointer to a uint64_t that stores + // multi-byte keys (e.g. special keys in VT100 terminal) + // and as long as a key is available (not 0) we dont read further keys + uint64_t *keycode = (uint64_t *) (M.mem_base + 0x41e); + int8_t c; + // function number in AH + DEBUG_PRINTF_INTR("%s(): Keyboard Interrupt: function: %x.\n", + __FUNCTION__, M.x86.R_AH); + DEBUG_PRINTF_INTR("AX=%04x BX=%04x CX=%04x DX=%04x\n", M.x86.R_AX, + M.x86.R_BX, M.x86.R_CX, M.x86.R_DX); + switch (M.x86.R_AH) { + case 0x00: + // get keystroke + if (*keycode) { + M.x86.R_AX = (uint16_t) * keycode; + // clear keycode + *keycode = 0; + } else { + M.x86.R_AH = 0x61; // scancode for space key + M.x86.R_AL = 0x20; // a space + } + break; + case 0x01: + // check keystroke + // ZF set = no keystroke + // read first byte of key code + if (*keycode) { + // already read, but not yet taken + CLEAR_FLAG(F_ZF); + M.x86.R_AX = (uint16_t) * keycode; + } else { + c = getchar(); + if (c == -1) { + // no key available + SET_FLAG(F_ZF); + } else { + *keycode = c; + + // since after an ESC it may take a while to receive the next char, + // we send something that is not shown on the screen, and then try to get + // the next char + // TODO: only after ESC?? what about other multibyte keys + printf("tt%c%c", 0x08, 0x08); // 0x08 == Backspace + + while ((c = getchar()) != -1) { + *keycode = (*keycode << 8) | c; + DEBUG_PRINTF(" key read: %0llx\n", + *keycode); + } + translate_keycode(keycode); + DEBUG_PRINTF(" translated key: %0llx\n", + *keycode); + if (*keycode == 0) { + //not found + SET_FLAG(F_ZF); + } else { + CLEAR_FLAG(F_ZF); + M.x86.R_AX = (uint16_t) * keycode; + //X86EMU_trace_on(); + //M.x86.debug &= ~DEBUG_DECODE_NOPRINT_F; + } + } + } + break; + default: + printf("%s(): unknown function (%x) for int16 handler.\n", + __FUNCTION__, M.x86.R_AH); + DEBUG_PRINTF_INTR("AX=%04x BX=%04x CX=%04x DX=%04x\n", + M.x86.R_AX, M.x86.R_BX, M.x86.R_CX, + M.x86.R_DX); + HALT_SYS(); + break; + } +} + +// handle int1a (PCI BIOS Interrupt) +static void +handleInt1a(void) +{ + // function number in AX + uint8_t bus, devfn, offs; + switch (M.x86.R_AX) { + case 0xb101: + // Installation check + CLEAR_FLAG(F_CF); // clear CF + M.x86.R_EDX = 0x20494350; // " ICP" endian swapped "PCI " + M.x86.R_AL = 0x1; // Config Space Mechanism 1 supported + M.x86.R_BX = 0x0210; // PCI Interface Level Version 2.10 + M.x86.R_CL = 0xff; // number of last PCI Bus in system TODO: check! + break; + case 0xb102: + // Find PCI Device + // NOTE: we currently only allow the device to find itself... + // it SHOULD be all we ever need... + // device_id in CX, vendor_id in DX + // device index in SI (i.e. if multiple devices with same vendor/device id + // are connected). We currently only support device index 0 + DEBUG_PRINTF_INTR("%s(): function: %x: PCI Find Device\n", + __FUNCTION__, M.x86.R_AX); + if ((M.x86.R_CX == bios_device.pci_device_id) + && (M.x86.R_DX == bios_device.pci_vendor_id) + // device index must be 0 + && (M.x86.R_SI == 0)) { + CLEAR_FLAG(F_CF); + M.x86.R_AH = 0x00; // return code: success + M.x86.R_BH = bios_device.bus; + M.x86.R_BL = bios_device.devfn; + DEBUG_PRINTF_INTR + ("%s(): function %x: PCI Find Device --> 0x%04x\n", + __FUNCTION__, M.x86.R_AX, M.x86.R_BX); + } else { + DEBUG_PRINTF_INTR + ("%s(): function %x: invalid device/vendor/device index! (%04x/%04x/%02x expected: %04x/%04x/0) \n", + __FUNCTION__, M.x86.R_AX, M.x86.R_CX, M.x86.R_DX, + M.x86.R_SI, bios_device.pci_device_id, + bios_device.pci_vendor_id); + SET_FLAG(F_CF); + M.x86.R_AH = 0x86; // return code: device not found + } + break; + case 0xb108: //read configuration byte + case 0xb109: //read configuration word + case 0xb10a: //read configuration dword + bus = M.x86.R_BH; + devfn = M.x86.R_BL; + offs = M.x86.R_DI; + if ((bus != bios_device.bus) + || (devfn != bios_device.devfn)) { + // fail accesses to any device but ours... + printf + ("%s(): Config read access invalid! bus: %x (%x), devfn: %x (%x), offs: %x\n", + __FUNCTION__, bus, bios_device.bus, devfn, + bios_device.devfn, offs); + SET_FLAG(F_CF); + M.x86.R_AH = 0x87; //return code: bad pci register + HALT_SYS(); + return; + } else { + switch (M.x86.R_AX) { + case 0xb108: + M.x86.R_CL = + (uint8_t) rtas_pci_config_read(bios_device. + puid, 1, + bus, devfn, + offs); + DEBUG_PRINTF_INTR + ("%s(): function %x: PCI Config Read @%02x --> 0x%02x\n", + __FUNCTION__, M.x86.R_AX, offs, + M.x86.R_CL); + break; + case 0xb109: + M.x86.R_CX = + (uint16_t) rtas_pci_config_read(bios_device. + puid, 2, + bus, devfn, + offs); + DEBUG_PRINTF_INTR + ("%s(): function %x: PCI Config Read @%02x --> 0x%04x\n", + __FUNCTION__, M.x86.R_AX, offs, + M.x86.R_CX); + break; + case 0xb10a: + M.x86.R_ECX = + (uint32_t) rtas_pci_config_read(bios_device. + puid, 4, + bus, devfn, + offs); + DEBUG_PRINTF_INTR + ("%s(): function %x: PCI Config Read @%02x --> 0x%08x\n", + __FUNCTION__, M.x86.R_AX, offs, + M.x86.R_ECX); + break; + } + CLEAR_FLAG(F_CF); + M.x86.R_AH = 0x0; // return code: success + } + break; + case 0xb10b: //write configuration byte + case 0xb10c: //write configuration word + case 0xb10d: //write configuration dword + bus = M.x86.R_BH; + devfn = M.x86.R_BL; + offs = M.x86.R_DI; + if ((bus != bios_device.bus) + || (devfn != bios_device.devfn)) { + // fail accesses to any device but ours... + printf + ("%s(): Config read access invalid! bus: %x (%x), devfn: %x (%x), offs: %x\n", + __FUNCTION__, bus, bios_device.bus, devfn, + bios_device.devfn, offs); + SET_FLAG(F_CF); + M.x86.R_AH = 0x87; //return code: bad pci register + HALT_SYS(); + return; + } else { + switch (M.x86.R_AX) { + case 0xb10b: + rtas_pci_config_write(bios_device.puid, 1, bus, + devfn, offs, M.x86.R_CL); + DEBUG_PRINTF_INTR + ("%s(): function %x: PCI Config Write @%02x <-- 0x%02x\n", + __FUNCTION__, M.x86.R_AX, offs, + M.x86.R_CL); + break; + case 0xb10c: + rtas_pci_config_write(bios_device.puid, 2, bus, + devfn, offs, M.x86.R_CX); + DEBUG_PRINTF_INTR + ("%s(): function %x: PCI Config Write @%02x <-- 0x%04x\n", + __FUNCTION__, M.x86.R_AX, offs, + M.x86.R_CX); + break; + case 0xb10d: + rtas_pci_config_write(bios_device.puid, 4, bus, + devfn, offs, M.x86.R_ECX); + DEBUG_PRINTF_INTR + ("%s(): function %x: PCI Config Write @%02x <-- 0x%08x\n", + __FUNCTION__, M.x86.R_AX, offs, + M.x86.R_ECX); + break; + } + CLEAR_FLAG(F_CF); + M.x86.R_AH = 0x0; // return code: success + } + break; + default: + printf("%s(): unknown function (%x) for int1a handler.\n", + __FUNCTION__, M.x86.R_AX); + DEBUG_PRINTF_INTR("AX=%04x BX=%04x CX=%04x DX=%04x\n", + M.x86.R_AX, M.x86.R_BX, M.x86.R_CX, + M.x86.R_DX); + HALT_SYS(); + break; + } +} + +// main Interrupt Handler routine, should be registered as x86emu interrupt handler +void +handleInterrupt(int intNum) +{ + uint8_t int_handled = 0; +#ifndef DEBUG_PRINT_INT10 + // this printf makes output by int 10 unreadable... + // so we only enable it, if int10 print is disabled + DEBUG_PRINTF_INTR("%s(%x)\n", __FUNCTION__, intNum); +#endif + switch (intNum) { + case 0x10: //BIOS video interrupt + case 0x42: // INT 10h relocated by EGA/VGA BIOS + case 0x6d: // INT 10h relocated by VGA BIOS + // get interrupt vector from IDT (4 bytes per Interrupt starting at address 0 + if ((my_rdl(intNum * 4) == 0xF000F065) || //F000:F065 is default BIOS interrupt handler address + (my_rdl(intNum * 4) == 0xF4F4F4F4)) //invalid + { +#if 0 + // ignore interrupt... + DEBUG_PRINTF_INTR + ("%s(%x): invalid interrupt Vector (%08x) found, interrupt ignored...\n", + __FUNCTION__, intNum, my_rdl(intNum * 4)); + DEBUG_PRINTF_INTR("AX=%04x BX=%04x CX=%04x DX=%04x\n", + M.x86.R_AX, M.x86.R_BX, M.x86.R_CX, + M.x86.R_DX); + //HALT_SYS(); +#endif + handleInt10(); + int_handled = 1; + } + break; + case 0x16: + // Keyboard BIOS Interrupt + handleInt16(); + int_handled = 1; + break; + case 0x1a: + // PCI BIOS Interrupt + handleInt1a(); + int_handled = 1; + break; + default: + printf("Interrupt %#x (Vector: %x) not implemented\n", intNum, + my_rdl(intNum * 4)); + DEBUG_PRINTF_INTR("AX=%04x BX=%04x CX=%04x DX=%04x\n", + M.x86.R_AX, M.x86.R_BX, M.x86.R_CX, + M.x86.R_DX); + int_handled = 1; + HALT_SYS(); + break; + } + // if we did not handle the interrupt, jump to the interrupt vector... + if (!int_handled) { + setupInt(intNum); + } +} + +// prepare and execute Interrupt 10 (VGA Interrupt) +void +runInt10() +{ + // Initialize stack and data segment + M.x86.R_SS = STACK_SEGMENT; + M.x86.R_DS = DATA_SEGMENT; + M.x86.R_SP = STACK_START_OFFSET; + + // push a HLT instruction and a pointer to it onto the stack + // any return will pop the pointer and jump to the HLT, thus + // exiting (more or less) cleanly + push_word(0xf4f4); //F4=HLT + //push_word(M.x86.R_SS); + //push_word(M.x86.R_SP + 2); + + // setupInt will push the current CS and IP to the stack to return to it, + // but we want to halt, so set CS:IP to the HLT instruction we just pushed + // to the stack + M.x86.R_CS = M.x86.R_SS; + M.x86.R_IP = M.x86.R_SP; // + 4; + + CHECK_DBG(DEBUG_TRACE_X86EMU) { + X86EMU_trace_on(); + } + CHECK_DBG(DEBUG_JMP) { + M.x86.debug |= DEBUG_TRACEJMP_REGS_F; + M.x86.debug |= DEBUG_TRACEJMP_REGS_F; + M.x86.debug |= DEBUG_TRACECALL_F; + M.x86.debug |= DEBUG_TRACECALL_REGS_F; + } + setupInt(0x10); + DEBUG_PRINTF_INTR("%s(): starting execution of INT10...\n", + __FUNCTION__); + X86EMU_exec(); + DEBUG_PRINTF_INTR("%s(): execution finished\n", __FUNCTION__); +} + +// prepare and execute Interrupt 13 (Disk Interrupt) +void +runInt13(void) +{ + // Initialize stack and data segment + M.x86.R_SS = STACK_SEGMENT; + M.x86.R_DS = DATA_SEGMENT; + M.x86.R_SP = STACK_START_OFFSET; + + // push a HLT instruction and a pointer to it onto the stack + // any return will pop the pointer and jump to the HLT, thus + // exiting (more or less) cleanly + push_word(0xf4f4); //F4=HLT + //push_word(M.x86.R_SS); + //push_word(M.x86.R_SP + 2); + + // setupInt will push the current CS and IP to the stack to return to it, + // but we want to halt, so set CS:IP to the HLT instruction we just pushed + // to the stack + M.x86.R_CS = M.x86.R_SS; + M.x86.R_IP = M.x86.R_SP; + + CHECK_DBG(DEBUG_TRACE_X86EMU) { + X86EMU_trace_on(); + } + CHECK_DBG(DEBUG_JMP) { + M.x86.debug |= DEBUG_TRACEJMP_REGS_F; + M.x86.debug |= DEBUG_TRACEJMP_REGS_F; + M.x86.debug |= DEBUG_TRACECALL_F; + M.x86.debug |= DEBUG_TRACECALL_REGS_F; + } + + setupInt(0x13); + DEBUG_PRINTF_INTR("%s(): starting execution of INT13...\n", + __FUNCTION__); + X86EMU_exec(); + DEBUG_PRINTF_INTR("%s(): execution finished\n", __FUNCTION__); +} diff --git a/qemu/roms/SLOF/clients/net-snk/app/biosemu/interrupt.h b/qemu/roms/SLOF/clients/net-snk/app/biosemu/interrupt.h new file mode 100644 index 000000000..11755e102 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/biosemu/interrupt.h @@ -0,0 +1,21 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ +#ifndef _BIOSEMU_INTERRUPT_H_ +#define _BIOSEMU_INTERRUPT_H_ + +void handleInterrupt(int intNum); + +void runInt10(void); + +void runInt13(void); + +#endif diff --git a/qemu/roms/SLOF/clients/net-snk/app/biosemu/io.c b/qemu/roms/SLOF/clients/net-snk/app/biosemu/io.c new file mode 100644 index 000000000..e9c08932a --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/biosemu/io.c @@ -0,0 +1,382 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdio.h> +#include <cpu.h> +#include <pci.h> +#include "device.h" +#include "rtas.h" +#include "debug.h" +#include "device.h" +#include <stdint.h> +#include <x86emu/x86emu.h> +#include <time.h> +#include "io.h" + +//defined in net-snk/kernel/timer.c +extern uint64_t get_time(void); + +uint32_t pci_cfg_read(X86EMU_pioAddr addr, uint8_t size); +void pci_cfg_write(X86EMU_pioAddr addr, uint32_t val, uint8_t size); +uint8_t handle_port_61h(void); + +uint8_t +my_inb(X86EMU_pioAddr addr) +{ + uint8_t rval = 0xFF; + uint64_t translated_addr = addr; + uint8_t translated = dev_translate_address(&translated_addr); + if (translated != 0) { + //translation successful, access Device I/O (BAR or Legacy...) + DEBUG_PRINTF_IO("%s(%x): access to Device I/O\n", __FUNCTION__, + addr); + //DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __FUNCTION__, addr, translated_addr); + rval = read_io((void *)translated_addr, 1); + DEBUG_PRINTF_IO("%s(%04x) Device I/O --> %02x\n", __FUNCTION__, + addr, rval); + return rval; + } else { + switch (addr) { + case 0x61: + //8254 KB Controller / Timer Port + rval = handle_port_61h(); + //DEBUG_PRINTF_IO("%s(%04x) KB / Timer Port B --> %02x\n", __FUNCTION__, addr, rval); + return rval; + break; + case 0xCFC: + case 0xCFD: + case 0xCFE: + case 0xCFF: + // PCI Config Mechanism 1 Ports + return (uint8_t) pci_cfg_read(addr, 1); + break; + case 0x0a: + CHECK_DBG(DEBUG_INTR) { + X86EMU_trace_on(); + } + M.x86.debug &= ~DEBUG_DECODE_NOPRINT_F; + //HALT_SYS(); + // no break, intentional fall-through to default!! + default: + DEBUG_PRINTF_IO + ("%s(%04x) reading from bios_device.io_buffer\n", + __FUNCTION__, addr); + rval = *((uint8_t *) (bios_device.io_buffer + addr)); + DEBUG_PRINTF_IO("%s(%04x) I/O Buffer --> %02x\n", + __FUNCTION__, addr, rval); + return rval; + break; + } + } +} + +uint16_t +my_inw(X86EMU_pioAddr addr) +{ + uint64_t translated_addr = addr; + uint8_t translated = dev_translate_address(&translated_addr); + if (translated != 0) { + //translation successful, access Device I/O (BAR or Legacy...) + DEBUG_PRINTF_IO("%s(%x): access to Device I/O\n", __FUNCTION__, + addr); + //DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __FUNCTION__, addr, translated_addr); + uint16_t rval; + if ((translated_addr & (uint64_t) 0x1) == 0) { + // 16 bit aligned access... + uint16_t tempval = read_io((void *)translated_addr, 2); + //little endian conversion + rval = in16le((void *) &tempval); + } else { + // unaligned access, read single bytes, little-endian + rval = (read_io((void *)translated_addr, 1) << 8) + | (read_io((void *)(translated_addr + 1), 1)); + } + DEBUG_PRINTF_IO("%s(%04x) Device I/O --> %04x\n", __FUNCTION__, + addr, rval); + return rval; + } else { + switch (addr) { + case 0xCFC: + case 0xCFE: + //PCI Config Mechanism 1 + return (uint16_t) pci_cfg_read(addr, 2); + break; + default: + DEBUG_PRINTF_IO + ("%s(%04x) reading from bios_device.io_buffer\n", + __FUNCTION__, addr); + uint16_t rval = + in16le((void *) bios_device.io_buffer + addr); + DEBUG_PRINTF_IO("%s(%04x) I/O Buffer --> %04x\n", + __FUNCTION__, addr, rval); + return rval; + break; + } + } +} + +uint32_t +my_inl(X86EMU_pioAddr addr) +{ + uint64_t translated_addr = addr; + uint8_t translated = dev_translate_address(&translated_addr); + if (translated != 0) { + //translation successful, access Device I/O (BAR or Legacy...) + DEBUG_PRINTF_IO("%s(%x): access to Device I/O\n", __FUNCTION__, + addr); + //DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __FUNCTION__, addr, translated_addr); + uint32_t rval; + if ((translated_addr & (uint64_t) 0x3) == 0) { + // 32 bit aligned access... + uint32_t tempval = read_io((void *) translated_addr, 4); + //little endian conversion + rval = in32le((void *) &tempval); + } else { + // unaligned access, read single bytes, little-endian + rval = (read_io((void *)(translated_addr), 1) << 24) + | (read_io((void *)(translated_addr + 1), 1) << 16) + | (read_io((void *)(translated_addr + 2), 1) << 8) + | (read_io((void *)(translated_addr + 3), 1)); + } + DEBUG_PRINTF_IO("%s(%04x) Device I/O --> %08x\n", __FUNCTION__, + addr, rval); + return rval; + } else { + switch (addr) { + case 0xCFC: + //PCI Config Mechanism 1 + return pci_cfg_read(addr, 4); + break; + default: + DEBUG_PRINTF_IO + ("%s(%04x) reading from bios_device.io_buffer\n", + __FUNCTION__, addr); + uint32_t rval = + in32le((void *) bios_device.io_buffer + addr); + DEBUG_PRINTF_IO("%s(%04x) I/O Buffer --> %08x\n", + __FUNCTION__, addr, rval); + return rval; + break; + } + } +} + +void +my_outb(X86EMU_pioAddr addr, uint8_t val) +{ + uint64_t translated_addr = addr; + uint8_t translated = dev_translate_address(&translated_addr); + if (translated != 0) { + //translation successful, access Device I/O (BAR or Legacy...) + DEBUG_PRINTF_IO("%s(%x, %x): access to Device I/O\n", + __FUNCTION__, addr, val); + //DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __FUNCTION__, addr, translated_addr); + write_io((void *) translated_addr, val, 1); + DEBUG_PRINTF_IO("%s(%04x) Device I/O <-- %02x\n", __FUNCTION__, + addr, val); + } else { + switch (addr) { + case 0xCFC: + case 0xCFD: + case 0xCFE: + case 0xCFF: + // PCI Config Mechanism 1 Ports + pci_cfg_write(addr, val, 1); + break; + default: + DEBUG_PRINTF_IO + ("%s(%04x,%02x) writing to bios_device.io_buffer\n", + __FUNCTION__, addr, val); + *((uint8_t *) (bios_device.io_buffer + addr)) = val; + break; + } + } +} + +void +my_outw(X86EMU_pioAddr addr, uint16_t val) +{ + uint64_t translated_addr = addr; + uint8_t translated = dev_translate_address(&translated_addr); + if (translated != 0) { + //translation successful, access Device I/O (BAR or Legacy...) + DEBUG_PRINTF_IO("%s(%x, %x): access to Device I/O\n", + __FUNCTION__, addr, val); + //DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __FUNCTION__, addr, translated_addr); + if ((translated_addr & (uint64_t) 0x1) == 0) { + // little-endian conversion + uint16_t tempval = in16le((void *) &val); + // 16 bit aligned access... + write_io((void *) translated_addr, tempval, 2); + } else { + // unaligned access, write single bytes, little-endian + write_io(((void *) (translated_addr + 1)), + (uint8_t) ((val & 0xFF00) >> 8), 1); + write_io(((void *) translated_addr), + (uint8_t) (val & 0x00FF), 1); + } + DEBUG_PRINTF_IO("%s(%04x) Device I/O <-- %04x\n", __FUNCTION__, + addr, val); + } else { + switch (addr) { + case 0xCFC: + case 0xCFE: + // PCI Config Mechanism 1 Ports + pci_cfg_write(addr, val, 2); + break; + default: + DEBUG_PRINTF_IO + ("%s(%04x,%04x) writing to bios_device.io_buffer\n", + __FUNCTION__, addr, val); + out16le((void *) bios_device.io_buffer + addr, val); + break; + } + } +} + +void +my_outl(X86EMU_pioAddr addr, uint32_t val) +{ + uint64_t translated_addr = addr; + uint8_t translated = dev_translate_address(&translated_addr); + if (translated != 0) { + //translation successful, access Device I/O (BAR or Legacy...) + DEBUG_PRINTF_IO("%s(%x, %x): access to Device I/O\n", + __FUNCTION__, addr, val); + //DEBUG_PRINTF_IO("%s(%04x): translated_addr: %llx\n", __FUNCTION__, addr, translated_addr); + if ((translated_addr & (uint64_t) 0x3) == 0) { + // little-endian conversion + uint32_t tempval = in32le((void *) &val); + // 32 bit aligned access... + write_io((void *) translated_addr, tempval, 4); + } else { + // unaligned access, write single bytes, little-endian + write_io(((void *) translated_addr + 3), + (uint8_t) ((val & 0xFF000000) >> 24), 1); + write_io(((void *) translated_addr + 2), + (uint8_t) ((val & 0x00FF0000) >> 16), 1); + write_io(((void *) translated_addr + 1), + (uint8_t) ((val & 0x0000FF00) >> 8), 1); + write_io(((void *) translated_addr), + (uint8_t) (val & 0x000000FF), 1); + } + DEBUG_PRINTF_IO("%s(%04x) Device I/O <-- %08x\n", __FUNCTION__, + addr, val); + } else { + switch (addr) { + case 0xCFC: + // PCI Config Mechanism 1 Ports + pci_cfg_write(addr, val, 4); + break; + default: + DEBUG_PRINTF_IO + ("%s(%04x,%08x) writing to bios_device.io_buffer\n", + __FUNCTION__, addr, val); + out32le((void *) bios_device.io_buffer + addr, val); + break; + } + } +} + +uint32_t +pci_cfg_read(X86EMU_pioAddr addr, uint8_t size) +{ + uint32_t rval = 0xFFFFFFFF; + if ((addr >= 0xCFC) && ((addr + size) <= 0xCFF)) { + // PCI Configuration Mechanism 1 step 1 + // write to 0xCF8, sets bus, device, function and Config Space offset + // later read from 0xCFC-0xCFF returns the value... + uint8_t bus, devfn, offs; + uint32_t port_cf8_val = my_inl(0xCF8); + if ((port_cf8_val & 0x80000000) != 0) { + //highest bit enables config space mapping + bus = (port_cf8_val & 0x00FF0000) >> 16; + devfn = (port_cf8_val & 0x0000FF00) >> 8; + offs = (port_cf8_val & 0x000000FF); + offs += (addr - 0xCFC); // if addr is not 0xcfc, the offset is moved accordingly + if ((bus != bios_device.bus) + || (devfn != bios_device.devfn)) { + // fail accesses to any device but ours... + printf + ("Config access invalid! bus: %x, devfn: %x, offs: %x\n", + bus, devfn, offs); + HALT_SYS(); + } else { + rval = + (uint32_t) rtas_pci_config_read(bios_device. + puid, size, + bus, devfn, + offs); + DEBUG_PRINTF_IO + ("%s(%04x) PCI Config Read @%02x, size: %d --> 0x%08x\n", + __FUNCTION__, addr, offs, size, rval); + } + } + } + return rval; +} + +void +pci_cfg_write(X86EMU_pioAddr addr, uint32_t val, uint8_t size) +{ + if ((addr >= 0xCFC) && ((addr + size) <= 0xCFF)) { + // PCI Configuration Mechanism 1 step 1 + // write to 0xCF8, sets bus, device, function and Config Space offset + // later write to 0xCFC-0xCFF sets the value... + uint8_t bus, devfn, offs; + uint32_t port_cf8_val = my_inl(0xCF8); + if ((port_cf8_val & 0x80000000) != 0) { + //highest bit enables config space mapping + bus = (port_cf8_val & 0x00FF0000) >> 16; + devfn = (port_cf8_val & 0x0000FF00) >> 8; + offs = (port_cf8_val & 0x000000FF); + offs += (addr - 0xCFC); // if addr is not 0xcfc, the offset is moved accordingly + if ((bus != bios_device.bus) + || (devfn != bios_device.devfn)) { + // fail accesses to any device but ours... + printf + ("Config access invalid! bus: %x, devfn: %x, offs: %x\n", + bus, devfn, offs); + HALT_SYS(); + } else { + rtas_pci_config_write(bios_device.puid, + size, bus, devfn, offs, + val); + DEBUG_PRINTF_IO + ("%s(%04x) PCI Config Write @%02x, size: %d <-- 0x%08x\n", + __FUNCTION__, addr, offs, size, val); + } + } + } +} + +uint8_t +handle_port_61h(void) +{ + static uint64_t last_time = 0; + uint64_t curr_time = get_time(); + uint64_t time_diff; // time since last call + uint32_t period_ticks; // length of a period in ticks + uint32_t nr_periods; //number of periods passed since last call + // bit 4 should toggle with every (DRAM) refresh cycle... (66kHz??) + time_diff = curr_time - last_time; + // at 66kHz a period is ~ 15 ns long, converted to ticks: (tb_freq is ticks/second) + // TODO: as long as the frequency does not change, we should not calculate this every time + period_ticks = (15 * tb_freq) / 1000000; + nr_periods = time_diff / period_ticks; + // if the number if ticks passed since last call is odd, we toggle bit 4 + if ((nr_periods % 2) != 0) { + *((uint8_t *) (bios_device.io_buffer + 0x61)) ^= 0x10; + } + //finally read the value from the io_buffer + return *((uint8_t *) (bios_device.io_buffer + 0x61)); +} diff --git a/qemu/roms/SLOF/clients/net-snk/app/biosemu/io.h b/qemu/roms/SLOF/clients/net-snk/app/biosemu/io.h new file mode 100644 index 000000000..5a0bb4b4b --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/biosemu/io.h @@ -0,0 +1,30 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef _BIOSEMU_IO_H_ +#define _BIOSEMU_IO_H_ +#include <x86emu/x86emu.h> +#include <stdint.h> + +uint8_t my_inb(X86EMU_pioAddr addr); + +uint16_t my_inw(X86EMU_pioAddr addr); + +uint32_t my_inl(X86EMU_pioAddr addr); + +void my_outb(X86EMU_pioAddr addr, uint8_t val); + +void my_outw(X86EMU_pioAddr addr, uint16_t val); + +void my_outl(X86EMU_pioAddr addr, uint32_t val); + +#endif diff --git a/qemu/roms/SLOF/clients/net-snk/app/biosemu/mem.c b/qemu/roms/SLOF/clients/net-snk/app/biosemu/mem.c new file mode 100644 index 000000000..1a6207554 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/biosemu/mem.c @@ -0,0 +1,464 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdio.h> +#include <stdint.h> +#include <cpu.h> +#include "debug.h" +#include "device.h" +#include "x86emu/x86emu.h" +#include "biosemu.h" +#include <time.h> +#include "mem.h" + +// define a check for access to certain (virtual) memory regions (interrupt handlers, BIOS Data Area, ...) +#ifdef DEBUG +static uint8_t in_check = 0; // to avoid recursion... +uint16_t ebda_segment; +uint32_t ebda_size; + +//TODO: these macros have grown so large, that they should be changed to an inline function, +//just for the sake of readability... + +//declare prototypes of the functions to follow, for use in DEBUG_CHECK_VMEM_ACCESS +uint8_t my_rdb(uint32_t); +uint16_t my_rdw(uint32_t); +uint32_t my_rdl(uint32_t); + +#define DEBUG_CHECK_VMEM_READ(_addr, _rval) \ + if ((debug_flags & DEBUG_CHECK_VMEM_ACCESS) && (in_check == 0)) { \ + in_check = 1; \ + /* determine ebda_segment and size \ + * since we are using my_rdx calls, make sure, this is after setting in_check! */ \ + /* offset 03 in BDA is EBDA segment */ \ + ebda_segment = my_rdw(0x40e); \ + /* first value in ebda is size in KB */ \ + ebda_size = my_rdb(ebda_segment << 4) * 1024; \ + /* check Interrupt Vector Access (0000:0000h - 0000:0400h) */ \ + if (_addr < 0x400) { \ + DEBUG_PRINTF_CS_IP("%s: read from Interrupt Vector %x --> %x\n", \ + __FUNCTION__, _addr / 4, _rval); \ + } \ + /* access to BIOS Data Area (0000:0400h - 0000:0500h)*/ \ + else if ((_addr >= 0x400) && (addr < 0x500)) { \ + DEBUG_PRINTF_CS_IP("%s: read from BIOS Data Area: addr: %x --> %x\n", \ + __FUNCTION__, _addr, _rval); \ + /* dump registers */ \ + /* x86emu_dump_xregs(); */ \ + } \ + /* access to first 64k of memory... */ \ + else if (_addr < 0x10000) { \ + DEBUG_PRINTF_CS_IP("%s: read from segment 0000h: addr: %x --> %x\n", \ + __FUNCTION__, _addr, _rval); \ + /* dump registers */ \ + /* x86emu_dump_xregs(); */ \ + } \ + /* read from PMM_CONV_SEGMENT */ \ + else if ((_addr <= ((PMM_CONV_SEGMENT << 4) | 0xffff)) && (_addr >= (PMM_CONV_SEGMENT << 4))) { \ + DEBUG_PRINTF_CS_IP("%s: read from PMM Segment %04xh: addr: %x --> %x\n", \ + __FUNCTION__, PMM_CONV_SEGMENT, _addr, _rval); \ + /* HALT_SYS(); */ \ + /* dump registers */ \ + /* x86emu_dump_xregs(); */ \ + } \ + /* read from PNP_DATA_SEGMENT */ \ + else if ((_addr <= ((PNP_DATA_SEGMENT << 4) | 0xffff)) && (_addr >= (PNP_DATA_SEGMENT << 4))) { \ + DEBUG_PRINTF_CS_IP("%s: read from PnP Data Segment %04xh: addr: %x --> %x\n", \ + __FUNCTION__, PNP_DATA_SEGMENT, _addr, _rval); \ + /* HALT_SYS(); */ \ + /* dump registers */ \ + /* x86emu_dump_xregs(); */ \ + } \ + /* read from EBDA Segment */ \ + else if ((_addr <= ((ebda_segment << 4) | (ebda_size - 1))) && (_addr >= (ebda_segment << 4))) { \ + DEBUG_PRINTF_CS_IP("%s: read from Extended BIOS Data Area %04xh, size: %04x: addr: %x --> %x\n", \ + __FUNCTION__, ebda_segment, ebda_size, _addr, _rval); \ + } \ + /* read from BIOS_DATA_SEGMENT */ \ + else if ((_addr <= ((BIOS_DATA_SEGMENT << 4) | 0xffff)) && (_addr >= (BIOS_DATA_SEGMENT << 4))) { \ + DEBUG_PRINTF_CS_IP("%s: read from BIOS Data Segment %04xh: addr: %x --> %x\n", \ + __FUNCTION__, BIOS_DATA_SEGMENT, _addr, _rval); \ + /* for PMM debugging */ \ + /*if (_addr == BIOS_DATA_SEGMENT << 4) { \ + X86EMU_trace_on(); \ + M.x86.debug &= ~DEBUG_DECODE_NOPRINT_F; \ + }*/ \ + /* dump registers */ \ + /* x86emu_dump_xregs(); */ \ + } \ + in_check = 0; \ + } +#define DEBUG_CHECK_VMEM_WRITE(_addr, _val) \ + if ((debug_flags & DEBUG_CHECK_VMEM_ACCESS) && (in_check == 0)) { \ + in_check = 1; \ + /* determine ebda_segment and size \ + * since we are using my_rdx calls, make sure, this is after setting in_check! */ \ + /* offset 03 in BDA is EBDA segment */ \ + ebda_segment = my_rdw(0x40e); \ + /* first value in ebda is size in KB */ \ + ebda_size = my_rdb(ebda_segment << 4) * 1024; \ + /* check Interrupt Vector Access (0000:0000h - 0000:0400h) */ \ + if (_addr < 0x400) { \ + DEBUG_PRINTF_CS_IP("%s: write to Interrupt Vector %x <-- %x\n", \ + __FUNCTION__, _addr / 4, _val); \ + } \ + /* access to BIOS Data Area (0000:0400h - 0000:0500h)*/ \ + else if ((_addr >= 0x400) && (addr < 0x500)) { \ + DEBUG_PRINTF_CS_IP("%s: write to BIOS Data Area: addr: %x <-- %x\n", \ + __FUNCTION__, _addr, _val); \ + /* dump registers */ \ + /* x86emu_dump_xregs(); */ \ + } \ + /* access to first 64k of memory...*/ \ + else if (_addr < 0x10000) { \ + DEBUG_PRINTF_CS_IP("%s: write to segment 0000h: addr: %x <-- %x\n", \ + __FUNCTION__, _addr, _val); \ + /* dump registers */ \ + /* x86emu_dump_xregs(); */ \ + } \ + /* write to PMM_CONV_SEGMENT... */ \ + else if ((_addr <= ((PMM_CONV_SEGMENT << 4) | 0xffff)) && (_addr >= (PMM_CONV_SEGMENT << 4))) { \ + DEBUG_PRINTF_CS_IP("%s: write to PMM Segment %04xh: addr: %x <-- %x\n", \ + __FUNCTION__, PMM_CONV_SEGMENT, _addr, _val); \ + /* dump registers */ \ + /* x86emu_dump_xregs(); */ \ + } \ + /* write to PNP_DATA_SEGMENT... */ \ + else if ((_addr <= ((PNP_DATA_SEGMENT << 4) | 0xffff)) && (_addr >= (PNP_DATA_SEGMENT << 4))) { \ + DEBUG_PRINTF_CS_IP("%s: write to PnP Data Segment %04xh: addr: %x <-- %x\n", \ + __FUNCTION__, PNP_DATA_SEGMENT, _addr, _val); \ + /* dump registers */ \ + /* x86emu_dump_xregs(); */ \ + } \ + /* write to EBDA Segment... */ \ + else if ((_addr <= ((ebda_segment << 4) | (ebda_size - 1))) && (_addr >= (ebda_segment << 4))) { \ + DEBUG_PRINTF_CS_IP("%s: write to Extended BIOS Data Area %04xh, size: %04x: addr: %x <-- %x\n", \ + __FUNCTION__, ebda_segment, ebda_size, _addr, _val); \ + } \ + /* write to BIOS_DATA_SEGMENT... */ \ + else if ((_addr <= ((BIOS_DATA_SEGMENT << 4) | 0xffff)) && (_addr >= (BIOS_DATA_SEGMENT << 4))) { \ + DEBUG_PRINTF_CS_IP("%s: write to BIOS Data Segment %04xh: addr: %x <-- %x\n", \ + __FUNCTION__, BIOS_DATA_SEGMENT, _addr, _val); \ + /* dump registers */ \ + /* x86emu_dump_xregs(); */ \ + } \ + /* write to current CS segment... */ \ + else if ((_addr < ((M.x86.R_CS << 4) | 0xffff)) && (_addr > (M.x86.R_CS << 4))) { \ + DEBUG_PRINTF_CS_IP("%s: write to CS segment %04xh: addr: %x <-- %x\n", \ + __FUNCTION__, M.x86.R_CS, _addr, _val); \ + /* dump registers */ \ + /* x86emu_dump_xregs(); */ \ + } \ + in_check = 0; \ + } +#else +#define DEBUG_CHECK_VMEM_READ(_addr, _rval) +#define DEBUG_CHECK_VMEM_WRITE(_addr, _val) +#endif + +//defined in net-snk/kernel/timer.c +extern uint64_t get_time(void); + +void update_time(uint32_t); + +// read byte from memory +uint8_t +my_rdb(uint32_t addr) +{ + uint64_t translated_addr = addr; + uint8_t translated = dev_translate_address(&translated_addr); + uint8_t rval; + if (translated != 0) { + //translation successful, access VGA Memory (BAR or Legacy...) + DEBUG_PRINTF_MEM("%s(%08x): access to VGA Memory\n", + __FUNCTION__, addr); + //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __FUNCTION__, addr, translated_addr); + set_ci(); + rval = *((uint8_t *) translated_addr); + clr_ci(); + DEBUG_PRINTF_MEM("%s(%08x) VGA --> %02x\n", __FUNCTION__, addr, + rval); + return rval; + } else if (addr > M.mem_size) { + DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n", + __FUNCTION__, addr); + //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1); + HALT_SYS(); + } else { + /* read from virtual memory */ + rval = *((uint8_t *) (M.mem_base + addr)); + DEBUG_CHECK_VMEM_READ(addr, rval); + return rval; + } + return -1; +} + +//read word from memory +uint16_t +my_rdw(uint32_t addr) +{ + uint64_t translated_addr = addr; + uint8_t translated = dev_translate_address(&translated_addr); + uint16_t rval; + if (translated != 0) { + //translation successful, access VGA Memory (BAR or Legacy...) + DEBUG_PRINTF_MEM("%s(%08x): access to VGA Memory\n", + __FUNCTION__, addr); + //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __FUNCTION__, addr, translated_addr); + // check for legacy memory, because of the remapping to BARs, the reads must + // be byte reads... + if ((addr >= 0xa0000) && (addr < 0xc0000)) { + //read bytes a using my_rdb, because of the remapping to BARs + //words may not be contiguous in memory, so we need to translate + //every address... + rval = ((uint8_t) my_rdb(addr)) | + (((uint8_t) my_rdb(addr + 1)) << 8); + } else { + if ((translated_addr & (uint64_t) 0x1) == 0) { + // 16 bit aligned access... + set_ci(); + rval = in16le((void *) translated_addr); + clr_ci(); + } else { + // unaligned access, read single bytes + set_ci(); + rval = (*((uint8_t *) translated_addr)) | + (*((uint8_t *) translated_addr + 1) << 8); + clr_ci(); + } + } + DEBUG_PRINTF_MEM("%s(%08x) VGA --> %04x\n", __FUNCTION__, addr, + rval); + return rval; + } else if (addr > M.mem_size) { + DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n", + __FUNCTION__, addr); + //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1); + HALT_SYS(); + } else { + /* read from virtual memory */ + rval = in16le((void *) (M.mem_base + addr)); + DEBUG_CHECK_VMEM_READ(addr, rval); + return rval; + } + return -1; +} + +//read long from memory +uint32_t +my_rdl(uint32_t addr) +{ + uint64_t translated_addr = addr; + uint8_t translated = dev_translate_address(&translated_addr); + uint32_t rval; + if (translated != 0) { + //translation successful, access VGA Memory (BAR or Legacy...) + DEBUG_PRINTF_MEM("%s(%x): access to VGA Memory\n", + __FUNCTION__, addr); + //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __FUNCTION__, addr, translated_addr); + // check for legacy memory, because of the remapping to BARs, the reads must + // be byte reads... + if ((addr >= 0xa0000) && (addr < 0xc0000)) { + //read bytes a using my_rdb, because of the remapping to BARs + //dwords may not be contiguous in memory, so we need to translate + //every address... + rval = ((uint8_t) my_rdb(addr)) | + (((uint8_t) my_rdb(addr + 1)) << 8) | + (((uint8_t) my_rdb(addr + 2)) << 16) | + (((uint8_t) my_rdb(addr + 3)) << 24); + } else { + if ((translated_addr & (uint64_t) 0x3) == 0) { + // 32 bit aligned access... + set_ci(); + rval = in32le((void *) translated_addr); + clr_ci(); + } else { + // unaligned access, read single bytes + set_ci(); + rval = (*((uint8_t *) translated_addr)) | + (*((uint8_t *) translated_addr + 1) << 8) | + (*((uint8_t *) translated_addr + 2) << 16) | + (*((uint8_t *) translated_addr + 3) << 24); + clr_ci(); + } + } + DEBUG_PRINTF_MEM("%s(%08x) VGA --> %08x\n", __FUNCTION__, addr, + rval); + //HALT_SYS(); + return rval; + } else if (addr > M.mem_size) { + DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n", + __FUNCTION__, addr); + //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1); + HALT_SYS(); + } else { + /* read from virtual memory */ + rval = in32le((void *) (M.mem_base + addr)); + switch (addr) { + case 0x46c: + //BDA Time Data, update it, before reading + update_time(rval); + rval = in32le((void *) (M.mem_base + addr)); + break; + } + DEBUG_CHECK_VMEM_READ(addr, rval); + return rval; + } + return -1; +} + +//write byte to memory +void +my_wrb(uint32_t addr, uint8_t val) +{ + uint64_t translated_addr = addr; + uint8_t translated = dev_translate_address(&translated_addr); + if (translated != 0) { + //translation successful, access VGA Memory (BAR or Legacy...) + DEBUG_PRINTF_MEM("%s(%x, %x): access to VGA Memory\n", + __FUNCTION__, addr, val); + //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __FUNCTION__, addr, translated_addr); + set_ci(); + *((uint8_t *) translated_addr) = val; + clr_ci(); + } else if (addr > M.mem_size) { + DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n", + __FUNCTION__, addr); + //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1); + HALT_SYS(); + } else { + /* write to virtual memory */ + DEBUG_CHECK_VMEM_WRITE(addr, val); + *((uint8_t *) (M.mem_base + addr)) = val; + } +} + +void +my_wrw(uint32_t addr, uint16_t val) +{ + uint64_t translated_addr = addr; + uint8_t translated = dev_translate_address(&translated_addr); + if (translated != 0) { + //translation successful, access VGA Memory (BAR or Legacy...) + DEBUG_PRINTF_MEM("%s(%x, %x): access to VGA Memory\n", + __FUNCTION__, addr, val); + //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __FUNCTION__, addr, translated_addr); + // check for legacy memory, because of the remapping to BARs, the reads must + // be byte reads... + if ((addr >= 0xa0000) && (addr < 0xc0000)) { + //read bytes a using my_rdb, because of the remapping to BARs + //words may not be contiguous in memory, so we need to translate + //every address... + my_wrb(addr, (uint8_t) (val & 0x00FF)); + my_wrb(addr + 1, (uint8_t) ((val & 0xFF00) >> 8)); + } else { + if ((translated_addr & (uint64_t) 0x1) == 0) { + // 16 bit aligned access... + set_ci(); + out16le((void *) translated_addr, val); + clr_ci(); + } else { + // unaligned access, write single bytes + set_ci(); + *((uint8_t *) translated_addr) = + (uint8_t) (val & 0x00FF); + *((uint8_t *) translated_addr + 1) = + (uint8_t) ((val & 0xFF00) >> 8); + clr_ci(); + } + } + } else if (addr > M.mem_size) { + DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n", + __FUNCTION__, addr); + //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1); + HALT_SYS(); + } else { + /* write to virtual memory */ + DEBUG_CHECK_VMEM_WRITE(addr, val); + out16le((void *) (M.mem_base + addr), val); + } +} +void +my_wrl(uint32_t addr, uint32_t val) +{ + uint64_t translated_addr = addr; + uint8_t translated = dev_translate_address(&translated_addr); + if (translated != 0) { + //translation successful, access VGA Memory (BAR or Legacy...) + DEBUG_PRINTF_MEM("%s(%x, %x): access to VGA Memory\n", + __FUNCTION__, addr, val); + //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __FUNCTION__, addr, translated_addr); + // check for legacy memory, because of the remapping to BARs, the reads must + // be byte reads... + if ((addr >= 0xa0000) && (addr < 0xc0000)) { + //read bytes a using my_rdb, because of the remapping to BARs + //words may not be contiguous in memory, so we need to translate + //every address... + my_wrb(addr, (uint8_t) (val & 0x000000FF)); + my_wrb(addr + 1, (uint8_t) ((val & 0x0000FF00) >> 8)); + my_wrb(addr + 2, (uint8_t) ((val & 0x00FF0000) >> 16)); + my_wrb(addr + 3, (uint8_t) ((val & 0xFF000000) >> 24)); + } else { + if ((translated_addr & (uint64_t) 0x3) == 0) { + // 32 bit aligned access... + set_ci(); + out32le((void *) translated_addr, val); + clr_ci(); + } else { + // unaligned access, write single bytes + set_ci(); + *((uint8_t *) translated_addr) = + (uint8_t) (val & 0x000000FF); + *((uint8_t *) translated_addr + 1) = + (uint8_t) ((val & 0x0000FF00) >> 8); + *((uint8_t *) translated_addr + 2) = + (uint8_t) ((val & 0x00FF0000) >> 16); + *((uint8_t *) translated_addr + 3) = + (uint8_t) ((val & 0xFF000000) >> 24); + clr_ci(); + } + } + } else if (addr > M.mem_size) { + DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n", + __FUNCTION__, addr); + //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1); + HALT_SYS(); + } else { + /* write to virtual memory */ + DEBUG_CHECK_VMEM_WRITE(addr, val); + out32le((void *) (M.mem_base + addr), val); + } +} + +//update time in BIOS Data Area +//DWord at offset 0x6c is the timer ticks since midnight, timer is running at 18Hz +//byte at 0x70 is timer overflow (set if midnight passed since last call to interrupt 1a function 00 +//cur_val is the current value, of offset 6c... +void +update_time(uint32_t cur_val) +{ + //for convenience, we let the start of timebase be at midnight, we currently dont support + //real daytime anyway... + uint64_t ticks_per_day = tb_freq * 60 * 24; + // at 18Hz a period is ~55ms, converted to ticks (tb_freq is ticks/second) + uint32_t period_ticks = (55 * tb_freq) / 1000; + uint64_t curr_time = get_time(); + uint64_t ticks_since_midnight = curr_time % ticks_per_day; + uint32_t periods_since_midnight = ticks_since_midnight / period_ticks; + // if periods since midnight is smaller than last value, set overflow + // at BDA Offset 0x70 + if (periods_since_midnight < cur_val) { + my_wrb(0x470, 1); + } + // store periods since midnight at BDA offset 0x6c + my_wrl(0x46c, periods_since_midnight); +} diff --git a/qemu/roms/SLOF/clients/net-snk/app/biosemu/mem.h b/qemu/roms/SLOF/clients/net-snk/app/biosemu/mem.h new file mode 100644 index 000000000..f0fbad96c --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/biosemu/mem.h @@ -0,0 +1,36 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef _BIOSEMU_MEM_H_ +#define _BIOSEMU_MEM_H_ +#include <x86emu/x86emu.h> +#include <stdint.h> + +// read byte from memory +uint8_t my_rdb(uint32_t addr); + +//read word from memory +uint16_t my_rdw(uint32_t addr); + +//read long from memory +uint32_t my_rdl(uint32_t addr); + +//write byte to memory +void my_wrb(uint32_t addr, uint8_t val); + +//write word to memory +void my_wrw(uint32_t addr, uint16_t val); + +//write long to memory +void my_wrl(uint32_t addr, uint32_t val); + +#endif diff --git a/qemu/roms/SLOF/clients/net-snk/app/biosemu/vbe.c b/qemu/roms/SLOF/clients/net-snk/app/biosemu/vbe.c new file mode 100644 index 000000000..957a1f2a0 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/biosemu/vbe.c @@ -0,0 +1,780 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <stdint.h> +#include <cpu.h> + +#include "debug.h" + +#include <x86emu/x86emu.h> +#include <x86emu/regs.h> +#include <x86emu/prim_ops.h> // for push_word + +#include "biosemu.h" +#include "io.h" +#include "mem.h" +#include "interrupt.h" +#include "device.h" +#include "vbe.h" + +static X86EMU_memFuncs my_mem_funcs = { + my_rdb, my_rdw, my_rdl, + my_wrb, my_wrw, my_wrl +}; + +static X86EMU_pioFuncs my_pio_funcs = { + my_inb, my_inw, my_inl, + my_outb, my_outw, my_outl +}; + +// pointer to VBEInfoBuffer, set by vbe_prepare +uint8_t *vbe_info_buffer = 0; +// virtual BIOS Memory +uint8_t *biosmem; +uint32_t biosmem_size; + +// these structs are for input from and output to OF +typedef struct { + uint8_t display_type; // 0=NONE, 1= analog, 2=digital + uint16_t screen_width; + uint16_t screen_height; + uint16_t screen_linebytes; // bytes per line in framebuffer, may be more than screen_width + uint8_t color_depth; // color depth in bpp + uint32_t framebuffer_address; + uint8_t edid_block_zero[128]; +} __attribute__ ((__packed__)) screen_info_t; + +typedef struct { + uint8_t signature[4]; + uint16_t size_reserved; + uint8_t monitor_number; + uint16_t max_screen_width; + uint8_t color_depth; +} __attribute__ ((__packed__)) screen_info_input_t; + +// these structs only store a subset of the VBE defined fields +// only those needed. +typedef struct { + char signature[4]; + uint16_t version; + uint8_t *oem_string_ptr; + uint32_t capabilities; + uint16_t video_mode_list[256]; // lets hope we never have more than 256 video modes... + uint16_t total_memory; +} vbe_info_t; + +typedef struct { + uint16_t video_mode; + uint8_t mode_info_block[256]; + uint16_t attributes; + uint16_t linebytes; + uint16_t x_resolution; + uint16_t y_resolution; + uint8_t x_charsize; + uint8_t y_charsize; + uint8_t bits_per_pixel; + uint8_t memory_model; + uint32_t framebuffer_address; +} vbe_mode_info_t; + +typedef struct { + uint8_t port_number; // i.e. monitor number + uint8_t edid_transfer_time; + uint8_t ddc_level; + uint8_t edid_block_zero[128]; +} vbe_ddc_info_t; + +static inline uint8_t +vbe_prepare(void) +{ + vbe_info_buffer = biosmem + (VBE_SEGMENT << 4); // segment:offset off VBE Data Area + //clear buffer + memset(vbe_info_buffer, 0, 512); + //set VbeSignature to "VBE2" to indicate VBE 2.0+ request + vbe_info_buffer[0] = 'V'; + vbe_info_buffer[0] = 'B'; + vbe_info_buffer[0] = 'E'; + vbe_info_buffer[0] = '2'; + // ES:DI store pointer to buffer in virtual mem see vbe_info_buffer above... + M.x86.R_EDI = 0x0; + M.x86.R_ES = VBE_SEGMENT; + + return 0; // successful init +} + +// VBE Function 00h +static uint8_t +vbe_info(vbe_info_t * info) +{ + vbe_prepare(); + // call VBE function 00h (Info Function) + M.x86.R_EAX = 0x4f00; + + // enable trace + CHECK_DBG(DEBUG_TRACE_X86EMU) { + X86EMU_trace_on(); + } + // run VESA Interrupt + runInt10(); + + if (M.x86.R_AL != 0x4f) { + DEBUG_PRINTF_VBE("%s: VBE Info Function NOT supported! AL=%x\n", + __FUNCTION__, M.x86.R_AL); + return -1; + } + + if (M.x86.R_AH != 0x0) { + DEBUG_PRINTF_VBE + ("%s: VBE Info Function Return Code NOT OK! AH=%x\n", + __FUNCTION__, M.x86.R_AH); + return M.x86.R_AH; + } + //printf("VBE Info Dump:"); + //dump(vbe_info_buffer, 64); + + //offset 0: signature + info->signature[0] = vbe_info_buffer[0]; + info->signature[1] = vbe_info_buffer[1]; + info->signature[2] = vbe_info_buffer[2]; + info->signature[3] = vbe_info_buffer[3]; + + // offset 4: 16bit le containing VbeVersion + info->version = in16le(vbe_info_buffer + 4); + + // offset 6: 32bit le containg segment:offset of OEM String in virtual Mem. + info->oem_string_ptr = + biosmem + ((in16le(vbe_info_buffer + 8) << 4) + + in16le(vbe_info_buffer + 6)); + + // offset 10: 32bit le capabilities + info->capabilities = in32le(vbe_info_buffer + 10); + + // offset 14: 32 bit le containing segment:offset of supported video mode table + uint16_t *video_mode_ptr; + video_mode_ptr = + (uint16_t *) (biosmem + + ((in16le(vbe_info_buffer + 16) << 4) + + in16le(vbe_info_buffer + 14))); + uint32_t i = 0; + do { + info->video_mode_list[i] = in16le(video_mode_ptr + i); + i++; + } + while ((i < + (sizeof(info->video_mode_list) / + sizeof(info->video_mode_list[0]))) + && (info->video_mode_list[i - 1] != 0xFFFF)); + + //offset 18: 16bit le total memory in 64KB blocks + info->total_memory = in16le(vbe_info_buffer + 18); + + return 0; +} + +// VBE Function 01h +static uint8_t +vbe_get_mode_info(vbe_mode_info_t * mode_info) +{ + vbe_prepare(); + // call VBE function 01h (Return VBE Mode Info Function) + M.x86.R_EAX = 0x4f01; + M.x86.R_CX = mode_info->video_mode; + + // enable trace + CHECK_DBG(DEBUG_TRACE_X86EMU) { + X86EMU_trace_on(); + } + // run VESA Interrupt + runInt10(); + + if (M.x86.R_AL != 0x4f) { + DEBUG_PRINTF_VBE + ("%s: VBE Return Mode Info Function NOT supported! AL=%x\n", + __FUNCTION__, M.x86.R_AL); + return -1; + } + + if (M.x86.R_AH != 0x0) { + DEBUG_PRINTF_VBE + ("%s: VBE Return Mode Info (mode: %04x) Function Return Code NOT OK! AH=%02x\n", + __FUNCTION__, mode_info->video_mode, M.x86.R_AH); + return M.x86.R_AH; + } + //pointer to mode_info_block is in ES:DI + memcpy(mode_info->mode_info_block, + biosmem + ((M.x86.R_ES << 4) + M.x86.R_DI), + sizeof(mode_info->mode_info_block)); + + //printf("Mode Info Dump:"); + //dump(mode_info_block, 64); + + // offset 0: 16bit le mode attributes + mode_info->attributes = in16le(mode_info->mode_info_block); + + // offset 16: 16bit le bytes per scan line + mode_info->linebytes = in16le(mode_info->mode_info_block + 16); + + // offset 18: 16bit le x resolution + mode_info->x_resolution = in16le(mode_info->mode_info_block + 18); + + // offset 20: 16bit le y resolution + mode_info->y_resolution = in16le(mode_info->mode_info_block + 20); + + // offset 22: 8bit le x charsize + mode_info->x_charsize = *(mode_info->mode_info_block + 22); + + // offset 23: 8bit le y charsize + mode_info->y_charsize = *(mode_info->mode_info_block + 23); + + // offset 25: 8bit le bits per pixel + mode_info->bits_per_pixel = *(mode_info->mode_info_block + 25); + + // offset 27: 8bit le memory model + mode_info->memory_model = *(mode_info->mode_info_block + 27); + + // offset 40: 32bit le containg offset of frame buffer memory ptr + mode_info->framebuffer_address = + in32le(mode_info->mode_info_block + 40); + + return 0; +} + +// VBE Function 02h +static uint8_t +vbe_set_mode(vbe_mode_info_t * mode_info) +{ + vbe_prepare(); + // call VBE function 02h (Set VBE Mode Function) + M.x86.R_EAX = 0x4f02; + M.x86.R_BX = mode_info->video_mode; + M.x86.R_BX |= 0x4000; // set bit 14 to request linear framebuffer mode + M.x86.R_BX &= 0x7FFF; // clear bit 15 to request clearing of framebuffer + + DEBUG_PRINTF_VBE("%s: setting mode: 0x%04x\n", __FUNCTION__, + M.x86.R_BX); + + // enable trace + CHECK_DBG(DEBUG_TRACE_X86EMU) { + X86EMU_trace_on(); + } + // run VESA Interrupt + runInt10(); + + if (M.x86.R_AL != 0x4f) { + DEBUG_PRINTF_VBE + ("%s: VBE Set Mode Function NOT supported! AL=%x\n", + __FUNCTION__, M.x86.R_AL); + return -1; + } + + if (M.x86.R_AH != 0x0) { + DEBUG_PRINTF_VBE + ("%s: mode: %x VBE Set Mode Function Return Code NOT OK! AH=%x\n", + __FUNCTION__, mode_info->video_mode, M.x86.R_AH); + return M.x86.R_AH; + } + return 0; +} + +//VBE Function 08h +static uint8_t +vbe_set_palette_format(uint8_t format) +{ + vbe_prepare(); + // call VBE function 09h (Set/Get Palette Data Function) + M.x86.R_EAX = 0x4f08; + M.x86.R_BL = 0x00; // set format + M.x86.R_BH = format; + + DEBUG_PRINTF_VBE("%s: setting palette format: %d\n", __FUNCTION__, + format); + + // enable trace + CHECK_DBG(DEBUG_TRACE_X86EMU) { + X86EMU_trace_on(); + } + // run VESA Interrupt + runInt10(); + + if (M.x86.R_AL != 0x4f) { + DEBUG_PRINTF_VBE + ("%s: VBE Set Palette Format Function NOT supported! AL=%x\n", + __FUNCTION__, M.x86.R_AL); + return -1; + } + + if (M.x86.R_AH != 0x0) { + DEBUG_PRINTF_VBE + ("%s: VBE Set Palette Format Function Return Code NOT OK! AH=%x\n", + __FUNCTION__, M.x86.R_AH); + return M.x86.R_AH; + } + return 0; +} + +// VBE Function 09h +static uint8_t +vbe_set_color(uint16_t color_number, uint32_t color_value) +{ + vbe_prepare(); + // call VBE function 09h (Set/Get Palette Data Function) + M.x86.R_EAX = 0x4f09; + M.x86.R_BL = 0x00; // set color + M.x86.R_CX = 0x01; // set only one entry + M.x86.R_DX = color_number; + // ES:DI is address where color_value is stored, we store it at 2000:0000 + M.x86.R_ES = 0x2000; + M.x86.R_DI = 0x0; + + // store color value at ES:DI + out32le(biosmem + (M.x86.R_ES << 4) + M.x86.R_DI, color_value); + + DEBUG_PRINTF_VBE("%s: setting color #%x: 0x%04x\n", __FUNCTION__, + color_number, color_value); + + // enable trace + CHECK_DBG(DEBUG_TRACE_X86EMU) { + X86EMU_trace_on(); + } + // run VESA Interrupt + runInt10(); + + if (M.x86.R_AL != 0x4f) { + DEBUG_PRINTF_VBE + ("%s: VBE Set Palette Function NOT supported! AL=%x\n", + __FUNCTION__, M.x86.R_AL); + return -1; + } + + if (M.x86.R_AH != 0x0) { + DEBUG_PRINTF_VBE + ("%s: VBE Set Palette Function Return Code NOT OK! AH=%x\n", + __FUNCTION__, M.x86.R_AH); + return M.x86.R_AH; + } + return 0; +} + +#if 0 +static uint8_t +vbe_get_color(uint16_t color_number, uint32_t * color_value) +{ + vbe_prepare(); + // call VBE function 09h (Set/Get Palette Data Function) + M.x86.R_EAX = 0x4f09; + M.x86.R_BL = 0x00; // get color + M.x86.R_CX = 0x01; // get only one entry + M.x86.R_DX = color_number; + // ES:DI is address where color_value is stored, we store it at 2000:0000 + M.x86.R_ES = 0x2000; + M.x86.R_DI = 0x0; + + // enable trace + CHECK_DBG(DEBUG_TRACE_X86EMU) { + X86EMU_trace_on(); + } + // run VESA Interrupt + runInt10(); + + if (M.x86.R_AL != 0x4f) { + DEBUG_PRINTF_VBE + ("%s: VBE Set Palette Function NOT supported! AL=%x\n", + __FUNCTION__, M.x86.R_AL); + return -1; + } + + if (M.x86.R_AH != 0x0) { + DEBUG_PRINTF_VBE + ("%s: VBE Set Palette Function Return Code NOT OK! AH=%x\n", + __FUNCTION__, M.x86.R_AH); + return M.x86.R_AH; + } + // read color value from ES:DI + *color_value = in32le(biosmem + (M.x86.R_ES << 4) + M.x86.R_DI); + + DEBUG_PRINTF_VBE("%s: getting color #%x --> 0x%04x\n", __FUNCTION__, + color_number, *color_value); + + return 0; +} +#endif + +// VBE Function 15h +static uint8_t +vbe_get_ddc_info(vbe_ddc_info_t * ddc_info) +{ + vbe_prepare(); + // call VBE function 15h (DDC Info Function) + M.x86.R_EAX = 0x4f15; + M.x86.R_BL = 0x00; // get DDC Info + M.x86.R_CX = ddc_info->port_number; + M.x86.R_ES = 0x0; + M.x86.R_DI = 0x0; + + // enable trace + CHECK_DBG(DEBUG_TRACE_X86EMU) { + X86EMU_trace_on(); + } + // run VESA Interrupt + runInt10(); + + if (M.x86.R_AL != 0x4f) { + DEBUG_PRINTF_VBE + ("%s: VBE Get DDC Info Function NOT supported! AL=%x\n", + __FUNCTION__, M.x86.R_AL); + return -1; + } + + if (M.x86.R_AH != 0x0) { + DEBUG_PRINTF_VBE + ("%s: port: %x VBE Get DDC Info Function Return Code NOT OK! AH=%x\n", + __FUNCTION__, ddc_info->port_number, M.x86.R_AH); + return M.x86.R_AH; + } + // BH = approx. time in seconds to transfer one EDID block + ddc_info->edid_transfer_time = M.x86.R_BH; + // BL = DDC Level + ddc_info->ddc_level = M.x86.R_BL; + + vbe_prepare(); + // call VBE function 15h (DDC Info Function) + M.x86.R_EAX = 0x4f15; + M.x86.R_BL = 0x01; // read EDID + M.x86.R_CX = ddc_info->port_number; + M.x86.R_DX = 0x0; // block number + // ES:DI is address where EDID is stored, we store it at 2000:0000 + M.x86.R_ES = 0x2000; + M.x86.R_DI = 0x0; + + // enable trace + CHECK_DBG(DEBUG_TRACE_X86EMU) { + X86EMU_trace_on(); + } + // run VESA Interrupt + runInt10(); + + if (M.x86.R_AL != 0x4f) { + DEBUG_PRINTF_VBE + ("%s: VBE Read EDID Function NOT supported! AL=%x\n", + __FUNCTION__, M.x86.R_AL); + return -1; + } + + if (M.x86.R_AH != 0x0) { + DEBUG_PRINTF_VBE + ("%s: port: %x VBE Read EDID Function Return Code NOT OK! AH=%x\n", + __FUNCTION__, ddc_info->port_number, M.x86.R_AH); + return M.x86.R_AH; + } + + memcpy(ddc_info->edid_block_zero, + biosmem + (M.x86.R_ES << 4) + M.x86.R_DI, + sizeof(ddc_info->edid_block_zero)); + + return 0; +} + +uint32_t +vbe_get_info(uint8_t argc, char ** argv) +{ + uint8_t rval; + static const uint8_t valid_edid_sig[] = { + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 + }; + uint32_t i; + + if (argc < 4) { + printf + ("Usage %s <vmem_base> <device_path> <address of screen_info_t>\n", + argv[0]); + int i = 0; + for (i = 0; i < argc; i++) { + printf("argv[%d]: %s\n", i, argv[i]); + } + return -1; + } + // get a copy of input struct... + screen_info_input_t input = + *((screen_info_input_t *) strtoul((char *) argv[4], 0, 16)); + // output is pointer to the address passed as argv[4] + screen_info_t *output = + (screen_info_t *) strtoul((char *) argv[4], 0, 16); + // zero output + memset(output, 0, sizeof(screen_info_t)); + + // argv[1] is address of virtual BIOS mem... + // argv[2] is the size + biosmem = (uint8_t *) strtoul(argv[1], 0, 16); + biosmem_size = strtoul(argv[2], 0, 16);; + if (biosmem_size < MIN_REQUIRED_VMEM_SIZE) { + printf("Error: Not enough virtual memory: %x, required: %x!\n", + biosmem_size, MIN_REQUIRED_VMEM_SIZE); + return -1; + } + // argv[3] is the device to open and use... + if (dev_init((char *) argv[3]) != 0) { + printf("Error initializing device!\n"); + return -1; + } + //setup interrupt handler + X86EMU_intrFuncs intrFuncs[256]; + for (i = 0; i < 256; i++) + intrFuncs[i] = handleInterrupt; + X86EMU_setupIntrFuncs(intrFuncs); + X86EMU_setupPioFuncs(&my_pio_funcs); + X86EMU_setupMemFuncs(&my_mem_funcs); + + // set mem_base + M.mem_base = (long) biosmem; + M.mem_size = biosmem_size; + DEBUG_PRINTF_VBE("membase set: %08x, size: %08x\n", (int) M.mem_base, + (int) M.mem_size); + + vbe_info_t info; + rval = vbe_info(&info); + if (rval != 0) + return rval; + + DEBUG_PRINTF_VBE("VbeSignature: %s\n", info.signature); + DEBUG_PRINTF_VBE("VbeVersion: 0x%04x\n", info.version); + DEBUG_PRINTF_VBE("OemString: %s\n", info.oem_string_ptr); + DEBUG_PRINTF_VBE("Capabilities:\n"); + DEBUG_PRINTF_VBE("\tDAC: %s\n", + (info.capabilities & 0x1) == + 0 ? "fixed 6bit" : "switchable 6/8bit"); + DEBUG_PRINTF_VBE("\tVGA: %s\n", + (info.capabilities & 0x2) == + 0 ? "compatible" : "not compatible"); + DEBUG_PRINTF_VBE("\tRAMDAC: %s\n", + (info.capabilities & 0x4) == + 0 ? "normal" : "use blank bit in Function 09h"); + + // argv[4] may be a pointer with enough space to return screen_info_t + // as input, it must contain a screen_info_input_t with the following content: + // byte[0:3] = "DDC\0" (zero-terminated signature header) + // byte[4:5] = reserved space for the return struct... just in case we ever change + // the struct and dont have reserved enough memory (and let's hope the struct + // never gets larger than 64KB) + // byte[6] = monitor port number for DDC requests ("only" one byte... so lets hope we never have more than 255 monitors... + // byte[7:8] = max. screen width (OF may want to limit this) + // byte[9] = required color depth in bpp + if (strncmp((char *) input.signature, "DDC", 4) != 0) { + printf + ("%s: Invalid input signature! expected: %s, is: %s\n", + __FUNCTION__, "DDC", input.signature); + return -1; + } + if (input.size_reserved != sizeof(screen_info_t)) { + printf + ("%s: Size of return struct is wrong, required: %d, available: %d\n", + __FUNCTION__, (int) sizeof(screen_info_t), + input.size_reserved); + return -1; + } + + vbe_ddc_info_t ddc_info; + ddc_info.port_number = input.monitor_number; + vbe_get_ddc_info(&ddc_info); + +#if 0 + DEBUG_PRINTF_VBE("DDC: edid_tranfer_time: %d\n", + ddc_info.edid_transfer_time); + DEBUG_PRINTF_VBE("DDC: ddc_level: %x\n", ddc_info.ddc_level); + DEBUG_PRINTF_VBE("DDC: EDID: \n"); + CHECK_DBG(DEBUG_VBE) { + dump(ddc_info.edid_block_zero, + sizeof(ddc_info.edid_block_zero)); + } +#endif + if (memcmp(ddc_info.edid_block_zero, valid_edid_sig, 8) != 0) { + // invalid EDID signature... probably no monitor + output->display_type = 0x0; + return 0; + } else if ((ddc_info.edid_block_zero[20] & 0x80) != 0) { + // digital display + output->display_type = 2; + } else { + // analog + output->display_type = 1; + } + DEBUG_PRINTF_VBE("DDC: found display type %d\n", output->display_type); + memcpy(output->edid_block_zero, ddc_info.edid_block_zero, + sizeof(ddc_info.edid_block_zero)); + i = 0; + vbe_mode_info_t mode_info; + vbe_mode_info_t best_mode_info; + // initialize best_mode to 0 + memset(&best_mode_info, 0, sizeof(best_mode_info)); + while ((mode_info.video_mode = info.video_mode_list[i]) != 0xFFFF) { + //DEBUG_PRINTF_VBE("%x: Mode: %04x\n", i, mode_info.video_mode); + vbe_get_mode_info(&mode_info); +#if 0 + DEBUG_PRINTF_VBE("Video Mode 0x%04x available, %s\n", + mode_info.video_mode, + (mode_info.attributes & 0x1) == + 0 ? "not supported" : "supported"); + DEBUG_PRINTF_VBE("\tTTY: %s\n", + (mode_info.attributes & 0x4) == + 0 ? "no" : "yes"); + DEBUG_PRINTF_VBE("\tMode: %s %s\n", + (mode_info.attributes & 0x8) == + 0 ? "monochrome" : "color", + (mode_info.attributes & 0x10) == + 0 ? "text" : "graphics"); + DEBUG_PRINTF_VBE("\tVGA: %s\n", + (mode_info.attributes & 0x20) == + 0 ? "compatible" : "not compatible"); + DEBUG_PRINTF_VBE("\tWindowed Mode: %s\n", + (mode_info.attributes & 0x40) == + 0 ? "yes" : "no"); + DEBUG_PRINTF_VBE("\tFramebuffer: %s\n", + (mode_info.attributes & 0x80) == + 0 ? "no" : "yes"); + DEBUG_PRINTF_VBE("\tResolution: %dx%d\n", + mode_info.x_resolution, + mode_info.y_resolution); + DEBUG_PRINTF_VBE("\tChar Size: %dx%d\n", + mode_info.x_charsize, mode_info.y_charsize); + DEBUG_PRINTF_VBE("\tColor Depth: %dbpp\n", + mode_info.bits_per_pixel); + DEBUG_PRINTF_VBE("\tMemory Model: 0x%x\n", + mode_info.memory_model); + DEBUG_PRINTF_VBE("\tFramebuffer Offset: %08x\n", + mode_info.framebuffer_address); +#endif + if ((mode_info.bits_per_pixel == input.color_depth) + && (mode_info.x_resolution <= input.max_screen_width) + && ((mode_info.attributes & 0x80) != 0) // framebuffer mode + && ((mode_info.attributes & 0x10) != 0) // graphics + && ((mode_info.attributes & 0x8) != 0) // color + && (mode_info.x_resolution > best_mode_info.x_resolution)) // better than previous best_mode + { + // yiiiihaah... we found a new best mode + memcpy(&best_mode_info, &mode_info, sizeof(mode_info)); + } + i++; + } + + if (best_mode_info.video_mode != 0) { + DEBUG_PRINTF_VBE + ("Best Video Mode found: 0x%x, %dx%d, %dbpp, framebuffer_address: 0x%x\n", + best_mode_info.video_mode, + best_mode_info.x_resolution, + best_mode_info.y_resolution, + best_mode_info.bits_per_pixel, + best_mode_info.framebuffer_address); + + //printf("Mode Info Dump:"); + //dump(best_mode_info.mode_info_block, 64); + + // set the video mode + vbe_set_mode(&best_mode_info); + + if ((info.capabilities & 0x1) != 0) { + // switch to 8 bit palette format + vbe_set_palette_format(8); + } + // setup a palette: + // - first 216 colors are mixed colors for each component in 6 steps + // (6*6*6=216) + // - then 10 shades of the three primary colors + // - then 10 shades of grey + // ------- + // = 256 colors + // + // - finally black is color 0 and white color FF (because SLOF expects it + // this way...) + // this resembles the palette that the kernel/X Server seems to expect... + + uint8_t mixed_color_values[6] = + { 0xFF, 0xDA, 0xB3, 0x87, 0x54, 0x00 }; + uint8_t primary_color_values[10] = + { 0xF3, 0xE7, 0xCD, 0xC0, 0xA5, 0x96, 0x77, 0x66, 0x3F, + 0x27 + }; + uint8_t mc_size = sizeof(mixed_color_values); + uint8_t prim_size = sizeof(primary_color_values); + + uint8_t curr_color_index; + uint32_t curr_color; + + uint8_t r, g, b; + // 216 mixed colors + for (r = 0; r < mc_size; r++) { + for (g = 0; g < mc_size; g++) { + for (b = 0; b < mc_size; b++) { + curr_color_index = + (r * mc_size * mc_size) + + (g * mc_size) + b; + curr_color = 0; + curr_color |= ((uint32_t) mixed_color_values[r]) << 16; //red value + curr_color |= ((uint32_t) mixed_color_values[g]) << 8; //green value + curr_color |= (uint32_t) mixed_color_values[b]; //blue value + vbe_set_color(curr_color_index, + curr_color); + } + } + } + + // 10 shades of each primary color + // red + for (r = 0; r < prim_size; r++) { + curr_color_index = mc_size * mc_size * mc_size + r; + curr_color = ((uint32_t) primary_color_values[r]) << 16; + vbe_set_color(curr_color_index, curr_color); + } + //green + for (g = 0; g < prim_size; g++) { + curr_color_index = + mc_size * mc_size * mc_size + prim_size + g; + curr_color = ((uint32_t) primary_color_values[g]) << 8; + vbe_set_color(curr_color_index, curr_color); + } + //blue + for (b = 0; b < prim_size; b++) { + curr_color_index = + mc_size * mc_size * mc_size + prim_size * 2 + b; + curr_color = (uint32_t) primary_color_values[b]; + vbe_set_color(curr_color_index, curr_color); + } + // 10 shades of grey + for (i = 0; i < prim_size; i++) { + curr_color_index = + mc_size * mc_size * mc_size + prim_size * 3 + i; + curr_color = 0; + curr_color |= ((uint32_t) primary_color_values[i]) << 16; //red + curr_color |= ((uint32_t) primary_color_values[i]) << 8; //green + curr_color |= ((uint32_t) primary_color_values[i]); //blue + vbe_set_color(curr_color_index, curr_color); + } + + // SLOF is using color 0x0 (black) and 0xFF (white) to draw to the screen... + vbe_set_color(0x00, 0x00000000); + vbe_set_color(0xFF, 0x00FFFFFF); + + output->screen_width = best_mode_info.x_resolution; + output->screen_height = best_mode_info.y_resolution; + output->screen_linebytes = best_mode_info.linebytes; + output->color_depth = best_mode_info.bits_per_pixel; + output->framebuffer_address = + best_mode_info.framebuffer_address; + } else { + printf("%s: No suitable video mode found!\n", __FUNCTION__); + //unset display_type... + output->display_type = 0; + } + return 0; +} diff --git a/qemu/roms/SLOF/clients/net-snk/app/biosemu/vbe.h b/qemu/roms/SLOF/clients/net-snk/app/biosemu/vbe.h new file mode 100644 index 000000000..17e982632 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/biosemu/vbe.h @@ -0,0 +1,18 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef _BIOSEMU_VBE_H_ +#define _BIOSEMU_VBE_H_ + +uint32_t vbe_get_info(uint8_t argc, char ** argv); + +#endif diff --git a/qemu/roms/SLOF/clients/net-snk/app/main.c b/qemu/roms/SLOF/clients/net-snk/app/main.c new file mode 100644 index 000000000..90e14b370 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/main.c @@ -0,0 +1,77 @@ +/****************************************************************************** + * Copyright (c) 2004, 2011 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <string.h> +#include <stdio.h> +#include <of.h> +#include <netapps/netapps.h> +#include <libbootmsg.h> + +#ifdef SNK_BIOSEMU_APPS +#include "biosemu/biosemu.h" +#include "biosemu/vbe.h" +#endif + +extern void _callback_entry(void); +int callback(int argc, char *argv[]); + + +int +main(int argc, char *argv[]) +{ + int i; + of_set_callback((void *) &_callback_entry); + + if (strcmp(argv[0], "netboot") == 0 && argc >= 5) + return netboot(argc, argv); + if (strcmp(argv[0], "ping") == 0) + return ping(argc, argv); +#ifdef SNK_BIOSEMU_APPS + // BIOS Emulator applications + if (strcmp(argv[0], "biosemu") == 0) + return biosemu(argc, argv); + if (strcmp(argv[0], "get_vbe_info") == 0) + return vbe_get_info(argc, argv); +#endif + + printf("Unknown client application called\n"); + for (i = 0; i < argc; i++) + printf("argv[%d] %s\n", i, argv[i]); + + return -1; +} + +int +callback(int argc, char *argv[]) +{ + int i; + + printf("\n"); + + /* + * Register your application's callback handler here, similar to + * the way you would register an application. + * Please note that callback functions can be called safely only after + * your application has called of_yield(). If you return or exit() from + * your client application, the callback can no longer be used. + */ +#if 0 + if (strcmp(argv[0], "example") == 0) + return example(argc, argv); +#endif + + printf("No such callback function\n"); + for (i = 0; i < argc; i++) + printf("argv[%d] %s\n", i, argv[i]); + + return (-1); +} diff --git a/qemu/roms/SLOF/clients/net-snk/app/netapps/Makefile b/qemu/roms/SLOF/clients/net-snk/app/netapps/Makefile new file mode 100644 index 000000000..70b990a77 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/netapps/Makefile @@ -0,0 +1,28 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2008 IBM Corporation +# * All rights reserved. +# * This program and the accompanying materials +# * are made available under the terms of the BSD License +# * which accompanies this distribution, and is available at +# * http://www.opensource.org/licenses/bsd-license.php +# * +# * Contributors: +# * IBM Corporation - initial implementation +# ****************************************************************************/ + +ifndef TOP + TOP = $(shell while ! test -e make.rules; do cd .. ; done; pwd) + export TOP +endif +include $(TOP)/make.rules + +CFLAGS += -I../ -I../../../../lib/ -Wall -W + +OBJS = netboot.o ping.o args.o + +all: $(OBJS) + +clean: + $(RM) -f *.o *.a *.i + +include $(TOP)/make.depend diff --git a/qemu/roms/SLOF/clients/net-snk/app/netapps/args.c b/qemu/roms/SLOF/clients/net-snk/app/netapps/args.c new file mode 100644 index 000000000..52215e6a7 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/netapps/args.c @@ -0,0 +1,143 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdint.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include "args.h" + +/** + * Returns pointer of the n'th argument within a string. + * + * @param arg_str string with arguments, separated with ',' + * @param index index of the requested arguments within arg_str + * @return pointer of argument[index] on success + * NULL if index is out of range + */ +const char * +get_arg_ptr(const char *arg_str, unsigned int index) +{ + unsigned int i; + + for (i = 0; i < index; ++i) { + for (; *arg_str != ',' && *arg_str != 0; ++arg_str); + if (*arg_str == 0) + return 0; + ++arg_str; + } + return arg_str; +} + +/** + * Returns number of arguments within a string. + * + * @param arg_str string with arguments, separated with ',' + * @return number of arguments + */ +unsigned int +get_args_count(const char *arg_str) +{ + unsigned int count = 1; + + while ((arg_str = get_arg_ptr(arg_str, 1)) != 0) + ++count; + return count; +} + +/** + * Returns the length of the first argument. + * + * @param arg_str string with arguments, separated with ',' + * @return length of first argument + */ +unsigned int +get_arg_length(const char *arg_str) +{ + unsigned int i; + + for (i = 0; *arg_str != ',' && *arg_str != 0; ++i) + ++arg_str; + return i; +} + +/** + * Copy the n'th argument within a string into a buffer in respect + * to a limited buffer size + * + * @param arg_str string with arguments, separated with ',' + * @param index index of the requested arguments within arg_str + * @param buffer pointer to the buffer + * @param length size of the buffer + * @return pointer of buffer on success + * NULL if index is out of range. + */ +char * +argncpy(const char *arg_str, unsigned int index, char *buffer, + unsigned int length) +{ + const char *ptr = get_arg_ptr(arg_str, index); + unsigned int len; + + if (!ptr) + return 0; + len = get_arg_length(ptr); + if (!strncpy(buffer, ptr, length)) + return 0; + buffer[len] = 0; + return buffer; +} + +/** + * Converts "255.255.255.255" -> char[4] = { 0xff, 0xff, 0xff, 0xff } + * + * @param str string to be converted + * @param ip in case of SUCCESS - 32-bit long IP + in case of FAULT - zero + * @return TRUE - IP converted successfully; + * FALSE - error condition occurs (e.g. bad format) + */ +int +strtoip(const char *str, char ip[4]) +{ + char octet[10]; + int res; + unsigned int i = 0, len; + + while (*str != 0) { + if (i > 3 || !isdigit(*str)) + return 0; + if (strstr(str, ".") != NULL) { + len = (int16_t) (strstr(str, ".") - str); + if (len >= 10) + return 0; + strncpy(octet, str, len); + octet[len] = 0; + str += len; + } else { + strncpy(octet, str, 9); + octet[9] = 0; + str += strlen(octet); + } + res = strtol(octet, NULL, 10); + if ((res > 255) || (res < 0)) + return 0; + ip[i] = (char) res; + i++; + if (*str == '.') + str++; + } + + if (i != 4) + return 0; + return -1; +} diff --git a/qemu/roms/SLOF/clients/net-snk/app/netapps/args.h b/qemu/roms/SLOF/clients/net-snk/app/netapps/args.h new file mode 100644 index 000000000..b80982a39 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/netapps/args.h @@ -0,0 +1,22 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef _ARGS_H +#define _ARGS_H + +const char *get_arg_ptr(const char *, unsigned int); +unsigned int get_args_count(const char *); +unsigned int get_arg_length(const char *); +char *argncpy(const char *, unsigned int, char *, unsigned int); +int strtoip(const char *, char[4]); + +#endif /* _ARGS_H */ diff --git a/qemu/roms/SLOF/clients/net-snk/app/netapps/netapps.h b/qemu/roms/SLOF/clients/net-snk/app/netapps/netapps.h new file mode 100644 index 000000000..18e607f53 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/netapps/netapps.h @@ -0,0 +1,30 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef _NETAPPS_H_ +#define _NETAPPS_H_ + +#include <netlib/tftp.h> + +#define F_IPV4 4 +#define F_IPV6 6 + +int netboot(int argc, char *argv[]); +int netsave(int argc, char *argv[]); +int netflash(int argc, char *argv[]); +int bcmflash(int argc, char *argv[]); +int mac_sync(int argc, char *argv[]); +int net_eeprom_version( void ); +int ping(int argc, char *argv[]); +int dhcp(char *ret_buffer, filename_ip_t * fn_ip, unsigned int retries, int flags); + +#endif diff --git a/qemu/roms/SLOF/clients/net-snk/app/netapps/netboot.c b/qemu/roms/SLOF/clients/net-snk/app/netapps/netboot.c new file mode 100644 index 000000000..cf20b5915 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/netapps/netboot.c @@ -0,0 +1,832 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <netlib/tftp.h> +#include <netlib/ethernet.h> +#include <netlib/dhcp.h> +#include <netlib/dhcpv6.h> +#include <netlib/ipv4.h> +#include <netlib/ipv6.h> +#include <netlib/dns.h> +#include <string.h> +#include <stdio.h> +#include <time.h> +#include <stdlib.h> +#include <sys/socket.h> +#include <netapps/args.h> +#include <libbootmsg/libbootmsg.h> +#include <of.h> +#include "netapps.h" + +#define IP_INIT_DEFAULT 5 +#define IP_INIT_NONE 0 +#define IP_INIT_BOOTP 1 +#define IP_INIT_DHCP 2 +#define IP_INIT_DHCPV6_STATELESS 3 +#define IP_INIT_IPV6_MANUAL 4 + +#define DEFAULT_BOOT_RETRIES 10 +#define DEFAULT_TFTP_RETRIES 20 +static int ip_version = 4; + +typedef struct { + char filename[100]; + int ip_init; + char siaddr[4]; + ip6_addr_t si6addr; + char ciaddr[4]; + ip6_addr_t ci6addr; + char giaddr[4]; + ip6_addr_t gi6addr; + int bootp_retries; + int tftp_retries; +} obp_tftp_args_t; + + +/** + * Parses a argument string for IPv6 booting, extracts all + * parameters and fills a structure accordingly + * + * @param arg_str string with arguments, separated with ',' + * @param argc number of arguments + * @param obp_tftp_args structure which contains the result + * @return updated arg_str + */ +static const char * +parse_ipv6args (const char *arg_str, unsigned int argc, + obp_tftp_args_t *obp_tftp_args) +{ + char *ptr = NULL; + char arg_buf[100]; + + // find out siaddr + if (argc == 0) + memset(&obp_tftp_args->si6addr.addr, 0, 16); + else { + argncpy(arg_str, 0, arg_buf, 100); + if(str_to_ipv6(arg_buf, (uint8_t *) &(obp_tftp_args->si6addr.addr[0]))) { + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else if(arg_buf[0] == 0) { + memset(&obp_tftp_args->si6addr.addr, 0, 16); + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else + memset(&obp_tftp_args->si6addr.addr, 0, 16); + } + + // find out filename + if (argc == 0) + obp_tftp_args->filename[0] = 0; + else { + argncpy(arg_str, 0, obp_tftp_args->filename, 100); + for(ptr = obp_tftp_args->filename; *ptr != 0; ++ptr) + if(*ptr == '\\') { + *ptr = '/'; + } + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + + // find out ciaddr + if (argc == 0) + memset(&obp_tftp_args->ci6addr, 0, 16); + else { + argncpy(arg_str, 0, arg_buf, 100); + if (str_to_ipv6(arg_buf, (uint8_t *) &(obp_tftp_args->ci6addr.addr[0]))) { + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else if(arg_buf[0] == 0) { + memset(&obp_tftp_args->ci6addr.addr, 0, 16); + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else + memset(&obp_tftp_args->ci6addr.addr, 0, 16); + } + + // find out giaddr + if (argc == 0) + memset(&obp_tftp_args->gi6addr, 0, 16); + else { + argncpy(arg_str, 0, arg_buf, 100); + if (str_to_ipv6(arg_buf, (uint8_t *) &(obp_tftp_args->gi6addr.addr)) ) { + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else if(arg_buf[0] == 0) { + memset(&obp_tftp_args->gi6addr, 0, 16); + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else + memset(&obp_tftp_args->gi6addr.addr, 0, 16); + } + + return arg_str; +} + + +/** + * Parses a argument string for IPv4 booting, extracts all + * parameters and fills a structure accordingly + * + * @param arg_str string with arguments, separated with ',' + * @param argc number of arguments + * @param obp_tftp_args structure which contains the result + * @return updated arg_str + */ +static const char * +parse_ipv4args (const char *arg_str, unsigned int argc, + obp_tftp_args_t *obp_tftp_args) +{ + char *ptr = NULL; + char arg_buf[100]; + + // find out siaddr + if(argc==0) { + memset(obp_tftp_args->siaddr, 0, 4); + } else { + argncpy(arg_str, 0, arg_buf, 100); + if(strtoip(arg_buf, obp_tftp_args->siaddr)) { + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else if(arg_buf[0] == 0) { + memset(obp_tftp_args->siaddr, 0, 4); + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else + memset(obp_tftp_args->siaddr, 0, 4); + } + + // find out filename + if(argc==0) + obp_tftp_args->filename[0] = 0; + else { + argncpy(arg_str, 0, obp_tftp_args->filename, 100); + for(ptr = obp_tftp_args->filename; *ptr != 0; ++ptr) + if(*ptr == '\\') + *ptr = '/'; + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + + // find out ciaddr + if(argc==0) + memset(obp_tftp_args->ciaddr, 0, 4); + else { + argncpy(arg_str, 0, arg_buf, 100); + if(strtoip(arg_buf, obp_tftp_args->ciaddr)) { + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else if(arg_buf[0] == 0) { + memset(obp_tftp_args->ciaddr, 0, 4); + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else + memset(obp_tftp_args->ciaddr, 0, 4); + } + + // find out giaddr + if(argc==0) + memset(obp_tftp_args->giaddr, 0, 4); + else { + argncpy(arg_str, 0, arg_buf, 100); + if(strtoip(arg_buf, obp_tftp_args->giaddr)) { + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else if(arg_buf[0] == 0) { + memset(obp_tftp_args->giaddr, 0, 4); + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else + memset(obp_tftp_args->giaddr, 0, 4); + } + + return arg_str; +} + +/** + * Parses a argument string which is given by netload, extracts all + * parameters and fills a structure according to this + * + * Netload-Parameters: + * [bootp,]siaddr,filename,ciaddr,giaddr,bootp-retries,tftp-retries + * + * @param arg_str string with arguments, separated with ',' + * @param obp_tftp_args structure which contains the result + * @return none + */ +static void +parse_args(const char *arg_str, obp_tftp_args_t *obp_tftp_args) +{ + unsigned int argc; + char arg_buf[100]; + + memset(obp_tftp_args, 0, sizeof(*obp_tftp_args)); + + argc = get_args_count(arg_str); + + // find out if we should use BOOTP or DHCP + if(argc==0) + obp_tftp_args->ip_init = IP_INIT_DEFAULT; + else { + argncpy(arg_str, 0, arg_buf, 100); + if (strcasecmp(arg_buf, "bootp") == 0) { + obp_tftp_args->ip_init = IP_INIT_BOOTP; + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else if(strcasecmp(arg_buf, "dhcp") == 0) { + obp_tftp_args->ip_init = IP_INIT_DHCP; + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else if(strcasecmp(arg_buf, "ipv6") == 0) { + obp_tftp_args->ip_init = IP_INIT_DHCPV6_STATELESS; + arg_str = get_arg_ptr(arg_str, 1); + --argc; + ip_version = 6; + } + else + obp_tftp_args->ip_init = IP_INIT_DEFAULT; + } + + if (ip_version == 4) { + arg_str = parse_ipv4args (arg_str, argc, obp_tftp_args); + } + else if (ip_version == 6) { + arg_str = parse_ipv6args (arg_str, argc, obp_tftp_args); + } + + // find out bootp-retries + if (argc == 0) + obp_tftp_args->bootp_retries = DEFAULT_BOOT_RETRIES; + else { + argncpy(arg_str, 0, arg_buf, 100); + if(arg_buf[0] == 0) + obp_tftp_args->bootp_retries = DEFAULT_BOOT_RETRIES; + else { + obp_tftp_args->bootp_retries = strtol(arg_buf, 0, 10); + if(obp_tftp_args->bootp_retries < 0) + obp_tftp_args->bootp_retries = DEFAULT_BOOT_RETRIES; + } + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + + // find out tftp-retries + if (argc == 0) + obp_tftp_args->tftp_retries = DEFAULT_TFTP_RETRIES; + else { + argncpy(arg_str, 0, arg_buf, 100); + if(arg_buf[0] == 0) + obp_tftp_args->tftp_retries = DEFAULT_TFTP_RETRIES; + else { + obp_tftp_args->tftp_retries = strtol(arg_buf, 0, 10); + if(obp_tftp_args->tftp_retries < 0) + obp_tftp_args->tftp_retries = DEFAULT_TFTP_RETRIES; + } + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } +} + +/** + * DHCP: Wrapper for obtaining IP and configuration info from DHCP server + * for both IPv4 and IPv6. + * (makes several attempts). + * + * @param ret_buffer buffer for returning BOOTP-REPLY packet data + * @param fn_ip contains the following configuration information: + * client MAC, client IP, TFTP-server MAC, + * TFTP-server IP, Boot file name + * @param retries No. of DHCP attempts + * @param flags flags for specifying type of dhcp attempt (IPv4/IPv6) + * ZERO - attempt DHCPv4 followed by DHCPv6 + * F_IPV4 - attempt only DHCPv4 + * F_IPV6 - attempt only DHCPv6 + * @return ZERO - IP and configuration info obtained; + * NON ZERO - error condition occurs. + */ +int dhcp(char *ret_buffer, filename_ip_t * fn_ip, unsigned int retries, int flags) +{ + int i = (int) retries+1; + int rc = -1; + + printf(" "); + + do { + printf("\b\b\b%03d", i-1); + if (getchar() == 27) { + printf("\nAborted\n"); + return -1; + } + if (!--i) { + printf("\nGiving up after %d DHCP requests\n", retries); + return -1; + } + if (!flags || (flags == F_IPV4)) { + ip_version = 4; + rc = dhcpv4(ret_buffer, fn_ip); + } + if ((!flags && (rc == -1)) || (flags == F_IPV6)) { + ip_version = 6; + set_ipv6_address(fn_ip->fd, 0); + rc = dhcpv6(ret_buffer, fn_ip); + if (rc == 0) { + printf("\n"); + memcpy(&fn_ip->own_ip6, get_ipv6_address(), 16); + break; + } + + } + if (rc != -1) /* either success or non-dhcp failure */ + break; + } while (1); + printf("\b\b\b\b"); + + return rc; +} + +int +netboot(int argc, char *argv[]) +{ + char buf[256]; + int rc; + int len = strtol(argv[2], 0, 16); + char *buffer = (char *) strtol(argv[1], 0, 16); + char *ret_buffer = (char *) strtol(argv[3], 0, 16); + filename_ip_t fn_ip; + int fd_device; + tftp_err_t tftp_err; + obp_tftp_args_t obp_tftp_args; + char null_ip[4] = { 0x00, 0x00, 0x00, 0x00 }; + char null_ip6[16] = { 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }; + int huge_load = strtol(argv[4], 0, 10); + int32_t block_size = strtol(argv[5], 0, 10); + uint8_t own_mac[6]; + + printf("\n"); + printf(" Bootloader 1.6 \n"); + memset(&fn_ip, 0, sizeof(filename_ip_t)); + + /*********************************************************** + * + * Initialize network stuff and retrieve boot informations + * + ***********************************************************/ + + /* Wait for link up and get mac_addr from device */ + for(rc=0; rc<DEFAULT_BOOT_RETRIES; ++rc) { + if(rc > 0) { + set_timer(TICKS_SEC); + while (get_timer() > 0); + } + fd_device = socket(0, 0, 0, (char*) own_mac); + if(fd_device != -2) + break; + if(getchar() == 27) { + fd_device = -2; + break; + } + } + + if (fd_device == -1) { + strcpy(buf,"E3000: (net) Could not read MAC address"); + bootmsg_error(0x3000, &buf[7]); + + write_mm_log(buf, strlen(buf), 0x91); + return -100; + } + else if (fd_device == -2) { + strcpy(buf,"E3006: (net) Could not initialize network device"); + bootmsg_error(0x3006, &buf[7]); + + write_mm_log(buf, strlen(buf), 0x91); + return -101; + } + + fn_ip.fd = fd_device; + + printf(" Reading MAC address from device: " + "%02x:%02x:%02x:%02x:%02x:%02x\n", + own_mac[0], own_mac[1], own_mac[2], + own_mac[3], own_mac[4], own_mac[5]); + + // init ethernet layer + set_mac_address(own_mac); + + if (argc > 6) { + parse_args(argv[6], &obp_tftp_args); + if(obp_tftp_args.bootp_retries - rc < DEFAULT_BOOT_RETRIES) + obp_tftp_args.bootp_retries = DEFAULT_BOOT_RETRIES; + else + obp_tftp_args.bootp_retries -= rc; + } + else { + memset(&obp_tftp_args, 0, sizeof(obp_tftp_args_t)); + obp_tftp_args.ip_init = IP_INIT_DEFAULT; + obp_tftp_args.bootp_retries = DEFAULT_BOOT_RETRIES; + obp_tftp_args.tftp_retries = DEFAULT_TFTP_RETRIES; + } + memcpy(&fn_ip.own_ip, obp_tftp_args.ciaddr, 4); + + // reset of error code + rc = 0; + + /* if we still have got all necessary parameters, then we don't + need to perform an BOOTP/DHCP-Request */ + if (ip_version == 4) { + if (memcmp(obp_tftp_args.ciaddr, null_ip, 4) != 0 + && memcmp(obp_tftp_args.siaddr, null_ip, 4) != 0 + && obp_tftp_args.filename[0] != 0) { + + memcpy(&fn_ip.server_ip, &obp_tftp_args.siaddr, 4); + obp_tftp_args.ip_init = IP_INIT_NONE; + } + } + else if (ip_version == 6) { + if (memcmp(&obp_tftp_args.ci6addr, null_ip6, 16) != 0 + && memcmp(&obp_tftp_args.si6addr, null_ip6, 16) != 0 + && obp_tftp_args.filename[0] != 0) { + + memcpy(&fn_ip.server_ip6.addr[0], + &obp_tftp_args.si6addr.addr, 16); + obp_tftp_args.ip_init = IP_INIT_IPV6_MANUAL; + } + else { + obp_tftp_args.ip_init = IP_INIT_DHCPV6_STATELESS; + } + } + + // construction of fn_ip from parameter + switch(obp_tftp_args.ip_init) { + case IP_INIT_BOOTP: + printf(" Requesting IP address via BOOTP: "); + // if giaddr in not specified, then we have to identify + // the BOOTP server via broadcasts + if(memcmp(obp_tftp_args.giaddr, null_ip, 4) == 0) { + // don't do this, when using DHCP !!! + fn_ip.server_ip = 0xFFFFFFFF; + } + // if giaddr is specified, then we have to use this + // IP address as proxy to identify the BOOTP server + else { + memcpy(&fn_ip.server_ip, obp_tftp_args.giaddr, 4); + } + rc = bootp(ret_buffer, &fn_ip, obp_tftp_args.bootp_retries); + break; + case IP_INIT_DHCP: + printf(" Requesting IP address via DHCPv4: "); + rc = dhcp(ret_buffer, &fn_ip, obp_tftp_args.bootp_retries, F_IPV4); + break; + case IP_INIT_DHCPV6_STATELESS: + printf(" Requesting information via DHCPv6: "); + rc = dhcp(ret_buffer, &fn_ip, + obp_tftp_args.bootp_retries, F_IPV6); + break; + case IP_INIT_IPV6_MANUAL: + set_ipv6_address(fn_ip.fd, &obp_tftp_args.ci6addr); + break; + case IP_INIT_DEFAULT: + printf(" Requesting IP address via DHCP: "); + rc = dhcp(ret_buffer, &fn_ip, obp_tftp_args.bootp_retries, 0); + break; + case IP_INIT_NONE: + default: + break; + } + + if(rc >= 0 && ip_version == 4) { + if(memcmp(obp_tftp_args.ciaddr, null_ip, 4) != 0 + && memcmp(obp_tftp_args.ciaddr, &fn_ip.own_ip, 4) != 0) + memcpy(&fn_ip.own_ip, obp_tftp_args.ciaddr, 4); + + if(memcmp(obp_tftp_args.siaddr, null_ip, 4) != 0 + && memcmp(obp_tftp_args.siaddr, &fn_ip.server_ip, 4) != 0) + memcpy(&fn_ip.server_ip, obp_tftp_args.siaddr, 4); + + // init IPv4 layer + set_ipv4_address(fn_ip.own_ip); + } + else if (rc >= 0 && ip_version == 6) { + if(memcmp(&obp_tftp_args.ci6addr.addr, null_ip6, 16) != 0 + && memcmp(&obp_tftp_args.ci6addr.addr, &fn_ip.own_ip6, 16) != 0) + memcpy(&fn_ip.own_ip6, &obp_tftp_args.ci6addr.addr, 16); + + if(memcmp(&obp_tftp_args.si6addr.addr, null_ip6, 16) != 0 + && memcmp(&obp_tftp_args.si6addr.addr, &fn_ip.server_ip6.addr, 16) != 0) + memcpy(&fn_ip.server_ip6.addr, &obp_tftp_args.si6addr.addr, 16); + } + if (rc == -1) { + strcpy(buf,"E3001: (net) Could not get IP address"); + bootmsg_error(0x3001, &buf[7]); + + write_mm_log(buf, strlen(buf), 0x91); + return -101; + } + + if(ip_version == 4) + printf("%d.%d.%d.%d\n", + ((fn_ip.own_ip >> 24) & 0xFF), ((fn_ip.own_ip >> 16) & 0xFF), + ((fn_ip.own_ip >> 8) & 0xFF), ( fn_ip.own_ip & 0xFF)); + + if (rc == -2) { + sprintf(buf, + "E3002: (net) ARP request to TFTP server " + "(%d.%d.%d.%d) failed", + ((fn_ip.server_ip >> 24) & 0xFF), + ((fn_ip.server_ip >> 16) & 0xFF), + ((fn_ip.server_ip >> 8) & 0xFF), + ( fn_ip.server_ip & 0xFF)); + bootmsg_error(0x3002, &buf[7]); + + write_mm_log(buf, strlen(buf), 0x91); + return -102; + } + if (rc == -4 || rc == -3) { + strcpy(buf,"E3008: (net) Can't obtain TFTP server IP address"); + bootmsg_error(0x3008, &buf[7]); + + write_mm_log(buf, strlen(buf), 0x91); + return -107; + } + + + /*********************************************************** + * + * Load file via TFTP into buffer provided by OpenFirmware + * + ***********************************************************/ + + if (obp_tftp_args.filename[0] != 0) { + strncpy((char *) fn_ip.filename, obp_tftp_args.filename, sizeof(fn_ip.filename)-1); + fn_ip.filename[sizeof(fn_ip.filename)-1] = 0; + } + + if (ip_version == 4) { + printf(" Requesting file \"%s\" via TFTP from %d.%d.%d.%d\n", + fn_ip.filename, + ((fn_ip.server_ip >> 24) & 0xFF), + ((fn_ip.server_ip >> 16) & 0xFF), + ((fn_ip.server_ip >> 8) & 0xFF), + ( fn_ip.server_ip & 0xFF)); + } else if (ip_version == 6) { + char ip6_str[40]; + printf(" Requesting file \"%s\" via TFTP from ", fn_ip.filename); + ipv6_to_str(fn_ip.server_ip6.addr, ip6_str); + printf("%s\n", ip6_str); + } + + // accept at most 20 bad packets + // wait at most for 40 packets + rc = tftp(&fn_ip, (unsigned char *) buffer, + len, obp_tftp_args.tftp_retries, + &tftp_err, huge_load, block_size, ip_version); + + if(obp_tftp_args.ip_init == IP_INIT_DHCP) + dhcp_send_release(fn_ip.fd); + + if (rc > 0) { + printf(" TFTP: Received %s (%d KBytes)\n", fn_ip.filename, + rc / 1024); + } else if (rc == -1) { + bootmsg_error(0x3003, "(net) unknown TFTP error"); + return -103; + } else if (rc == -2) { + sprintf(buf, + "E3004: (net) TFTP buffer of %d bytes " + "is too small for %s", + len, fn_ip.filename); + bootmsg_error(0x3004, &buf[7]); + + write_mm_log(buf, strlen(buf), 0x91); + return -104; + } else if (rc == -3) { + sprintf(buf,"E3009: (net) file not found: %s", + fn_ip.filename); + bootmsg_error(0x3009, &buf[7]); + + write_mm_log(buf, strlen(buf), 0x91); + return -108; + } else if (rc == -4) { + strcpy(buf,"E3010: (net) TFTP access violation"); + bootmsg_error(0x3010, &buf[7]); + + write_mm_log(buf, strlen(buf), 0x91); + return -109; + } else if (rc == -5) { + strcpy(buf,"E3011: (net) illegal TFTP operation"); + bootmsg_error(0x3011, &buf[7]); + + write_mm_log(buf, strlen(buf), 0x91); + return -110; + } else if (rc == -6) { + strcpy(buf, "E3012: (net) unknown TFTP transfer ID"); + bootmsg_error(0x3012, &buf[7]); + + write_mm_log(buf, strlen(buf), 0x91); + return -111; + } else if (rc == -7) { + strcpy(buf, "E3013: (net) no such TFTP user"); + bootmsg_error(0x3013, &buf[7]); + + write_mm_log(buf, strlen(buf), 0x91); + return -112; + } else if (rc == -8) { + strcpy(buf, "E3017: (net) TFTP blocksize negotiation failed"); + bootmsg_error(0x3017, &buf[7]); + + write_mm_log(buf, strlen(buf), 0x91); + return -116; + } else if (rc == -9) { + strcpy(buf,"E3018: (net) file exceeds maximum TFTP transfer size"); + bootmsg_error(0x3018, &buf[7]); + + write_mm_log(buf, strlen(buf), 0x91); + return -117; + } else if (rc <= -10 && rc >= -15) { + sprintf(buf,"E3005: (net) ICMP ERROR \""); + switch (rc) { + case -ICMP_NET_UNREACHABLE - 10: + sprintf(buf+strlen(buf),"net unreachable"); + break; + case -ICMP_HOST_UNREACHABLE - 10: + sprintf(buf+strlen(buf),"host unreachable"); + break; + case -ICMP_PROTOCOL_UNREACHABLE - 10: + sprintf(buf+strlen(buf),"protocol unreachable"); + break; + case -ICMP_PORT_UNREACHABLE - 10: + sprintf(buf+strlen(buf),"port unreachable"); + break; + case -ICMP_FRAGMENTATION_NEEDED - 10: + sprintf(buf+strlen(buf),"fragmentation needed and DF set"); + break; + case -ICMP_SOURCE_ROUTE_FAILED - 10: + sprintf(buf+strlen(buf),"source route failed"); + break; + default: + sprintf(buf+strlen(buf)," UNKNOWN"); + break; + } + sprintf(buf+strlen(buf),"\""); + bootmsg_error(0x3005, &buf[7]); + + write_mm_log(buf, strlen(buf), 0x91); + return -105; + } else if (rc == -40) { + sprintf(buf, + "E3014: (net) TFTP error occurred after " + "%d bad packets received", + tftp_err.bad_tftp_packets); + bootmsg_error(0x3014, &buf[7]); + write_mm_log(buf, strlen(buf), 0x91); + return -113; + } else if (rc == -41) { + sprintf(buf, + "E3015: (net) TFTP error occurred after " + "missing %d responses", + tftp_err.no_packets); + bootmsg_error(0x3015, &buf[7]); + write_mm_log(buf, strlen(buf), 0x91); + return -114; + } else if (rc == -42) { + sprintf(buf, + "E3016: (net) TFTP error missing block %d, " + "expected block was %d", + tftp_err.blocks_missed, + tftp_err.blocks_received); + bootmsg_error(0x3016, &buf[7]); + write_mm_log(buf, strlen(buf), 0x91); + return -115; + } + return rc; +} + +/** + * Parses a tftp arguments, extracts all + * parameters and fills server ip according to this + * + * Parameters: + * @param buffer string with arguments, + * @param server_ip server ip as result + * @param filename default filename + * @param fd Socket descriptor + * @param len len of the buffer, + * @return 0 on SUCCESS and -1 on failure + */ +int parse_tftp_args(char buffer[], char *server_ip, char filename[], int fd, + int len) +{ + char *raw; + char *tmp, *tmp1; + int i, j = 0; + char domainname[256]; + uint8_t server_ip6[16]; + + raw = malloc(len); + if (raw == NULL) { + printf("\n unable to allocate memory, parsing failed\n"); + return -1; + } + strncpy(raw,(const char *)buffer,len); + /*tftp url contains tftp://[fd00:4f53:4444:90:214:5eff:fed9:b200]/testfile*/ + if(strncmp(raw,"tftp://",7)){ + printf("\n tftp missing in %s\n",raw); + free(raw); + return -1; + } + tmp = strchr(raw,'['); + if(tmp != NULL && *tmp == '[') { + /*check for valid ipv6 address*/ + tmp1 = strchr(tmp,']'); + if (tmp1 == NULL) { + printf("\n missing ] in %s\n",raw); + free(raw); + return -1; + } + i = tmp1 - tmp; + /*look for file name*/ + tmp1 = strchr(tmp,'/'); + if (tmp1 == NULL) { + printf("\n missing filename in %s\n",raw); + free(raw); + return -1; + } + tmp[i] = '\0'; + /*check for 16 byte ipv6 address */ + if (!str_to_ipv6((tmp+1), (uint8_t *)(server_ip))) { + printf("\n wrong format IPV6 address in %s\n",raw); + free(raw); + return -1;; + } + else { + /*found filename */ + strcpy(filename,(tmp1+1)); + free(raw); + return 0; + } + } + else { + /*here tftp://hostname/testfile from option request of dhcp*/ + /*look for dns server name */ + tmp1 = strchr(raw,'.'); + if(tmp1 == NULL) { + printf("\n missing . seperator in %s\n",raw); + free(raw); + return -1; + } + /*look for domain name beyond dns server name + * so ignore the current . and look for one more + */ + tmp = strchr((tmp1+1),'.'); + if(tmp == NULL) { + printf("\n missing domain in %s\n",raw); + free(raw); + return -1; + } + tmp1 = strchr(tmp1,'/'); + if (tmp1 == NULL) { + printf("\n missing filename in %s\n",raw); + free(raw); + return -1; + } + j = tmp1 - (raw + 7); + tmp = raw + 7; + tmp[j] = '\0'; + strcpy(domainname, tmp); + if (dns_get_ip(fd, (int8_t *)domainname, server_ip6, 6) == 0) { + printf("\n DNS failed for IPV6\n"); + return -1; + } + ipv6_to_str(server_ip6, server_ip); + + strcpy(filename,(tmp1+1)); + free(raw); + return 0; + } + +} diff --git a/qemu/roms/SLOF/clients/net-snk/app/netapps/ping.c b/qemu/roms/SLOF/clients/net-snk/app/netapps/ping.c new file mode 100644 index 000000000..2c7dadbde --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/netapps/ping.c @@ -0,0 +1,196 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <netlib/ipv4.h> +#include <netlib/dhcp.h> +#include <netlib/ethernet.h> +#include <sys/socket.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <netapps/args.h> +#include "netapps.h" + +struct ping_args { + union { + char string[4]; + unsigned int integer; + } server_ip; + union { + char string[4]; + unsigned int integer; + } client_ip; + union { + char string[4]; + unsigned int integer; + } gateway_ip; + unsigned int timeout; +}; + +static void +usage(void) +{ + printf + ("\nping device-path:[device-args,]server-ip,[client-ip],[gateway-ip][,timeout]\n"); + +} + +static int +parse_args(const char *args, struct ping_args *ping_args) +{ + unsigned int argc = get_args_count(args); + char buf[64]; + ping_args->timeout = 10; + if (argc == 0) + /* at least server-ip has to be specified */ + return -1; + if (argc == 1) { + /* probably only server ip is specified */ + argncpy(args, 0, buf, 64); + if (!strtoip(buf, ping_args->server_ip.string)) + return -1; + return 0; + } + /* get first option from list */ + argncpy(args, 0, buf, 64); + if (!strtoip(buf, ping_args->server_ip.string)) { + /* it is not an IP address + * therefore it has to be device-args + * device-args are not supported and just ignored */ + args = get_arg_ptr(args, 1); + argc--; + } + + argncpy(args, 0, buf, 64); + if (!strtoip(buf, ping_args->server_ip.string)) { + /* this should have been the server IP address */ + return -1; + } else { + args = get_arg_ptr(args, 1); + if (!--argc) + return 0; + } + + argncpy(args, 0, buf, 64); + if (!strtoip(buf, ping_args->client_ip.string)) { + /* this should have been the client (our) IP address */ + return -1; + } else { + args = get_arg_ptr(args, 1); + if (!--argc) + return 0; + } + argncpy(args, 0, buf, 64); + if (!strtoip(buf, ping_args->gateway_ip.string)) { + /* this should have been the gateway IP address */ + return -1; + } else { + args = get_arg_ptr(args, 1); + if (!--argc) + return 0; + } + argncpy(args, 0, buf, 64); + ping_args->timeout = strtol(args, 0, 10); + return 0; +} + +int +ping(int argc, char *argv[]) +{ + short arp_failed = 0; + filename_ip_t fn_ip; + int fd_device; + struct ping_args ping_args; + uint8_t own_mac[6]; + + memset(&ping_args, 0, sizeof(struct ping_args)); + + if (argc == 2) { + if (parse_args(argv[1], &ping_args)) { + usage(); + return -1; + } + } else { + usage(); + return -1; + } + + memset(&fn_ip, 0, sizeof(filename_ip_t)); + + /* Get mac_addr from device */ + printf("\n Reading MAC address from device: "); + fd_device = socket(0, 0, 0, (char *) own_mac); + if (fd_device == -1) { + printf("\nE3000: Could not read MAC address\n"); + return -100; + } else if (fd_device == -2) { + printf("\nE3006: Could not initialize network device\n"); + return -101; + } + + fn_ip.fd = fd_device; + + printf("%02x:%02x:%02x:%02x:%02x:%02x\n", + own_mac[0], own_mac[1], own_mac[2], + own_mac[3], own_mac[4], own_mac[5]); + + // init ethernet layer + set_mac_address(own_mac); + // identify the BOOTP/DHCP server via broadcasts + // don't do this, when using DHCP !!! + // fn_ip.server_ip = 0xFFFFFFFF; + // memset(fn_ip.server_mac, 0xff, 6); + + if (!ping_args.client_ip.integer) { + /* Get ip address for our mac address */ + printf(" Requesting IP address via DHCP: "); + arp_failed = dhcp(0, &fn_ip, 30, F_IPV4); + + if (arp_failed == -1) { + printf("\n DHCP: Could not get ip address\n"); + return -1; + } + + } else { + memcpy(&fn_ip.own_ip, &ping_args.client_ip.integer, 4); + arp_failed = 1; + printf(" Own IP address: "); + } + + // reinit network stack + set_ipv4_address(fn_ip.own_ip); + + printf("%d.%d.%d.%d\n", + ((fn_ip.own_ip >> 24) & 0xFF), ((fn_ip.own_ip >> 16) & 0xFF), + ((fn_ip.own_ip >> 8) & 0xFF), (fn_ip.own_ip & 0xFF)); + + memcpy(&fn_ip.server_ip, &ping_args.server_ip.integer, 4); + printf(" Ping to %d.%d.%d.%d ", ((fn_ip.server_ip >> 24) & 0xFF), + ((fn_ip.server_ip >> 16) & 0xFF), + ((fn_ip.server_ip >> 8) & 0xFF), (fn_ip.server_ip & 0xFF)); + + + ping_ipv4(fd_device, fn_ip.server_ip); + + set_timer(TICKS_SEC / 10 * ping_args.timeout); + while(get_timer() > 0) { + receive_ether(fd_device); + if(pong_ipv4() == 0) { + printf("success\n"); + return 0; + } + } + + printf("failed\n"); + return -1; +} diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/Makefile b/qemu/roms/SLOF/clients/net-snk/app/netlib/Makefile new file mode 100644 index 000000000..df09bf82e --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/Makefile @@ -0,0 +1,42 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2008 IBM Corporation +# * All rights reserved. +# * This program and the accompanying materials +# * are made available under the terms of the BSD License +# * which accompanies this distribution, and is available at +# * http://www.opensource.org/licenses/bsd-license.php +# * +# * Contributors: +# * IBM Corporation - initial implementation +# ****************************************************************************/ + +ifndef TOP + TOP = $(shell while ! test -e make.rules; do cd .. ; done; pwd) + export TOP +endif +include $(TOP)/make.rules + +CFLAGS += -I../ + +ifeq ($(SNK_USE_MTFTP), 1) +CFLAGS += -DUSE_MTFTP +endif + +OBJS = ethernet.o ipv4.o udp.o tcp.o dns.o bootp.o \ + dhcp.o ipv6.o dhcpv6.o icmpv6.o ndp.o + +ifeq ($(SNK_USE_MTFTP), 1) +OBJS += mtftp.o +else +OBJS += tftp.o +endif + +all: netlib.o + +netlib.o: $(OBJS) + $(LD) $(LDFLAGS) $^ -o $@ -r + +clean: + $(RM) -f *.o *.a *.i + +include $(TOP)/make.depend diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/bootp.c b/qemu/roms/SLOF/clients/net-snk/app/netlib/bootp.c new file mode 100644 index 000000000..1bc6efe5b --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/bootp.c @@ -0,0 +1,254 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + + +#include <stdio.h> +#include <string.h> +#include <sys/socket.h> +#include <time.h> + +#include <ethernet.h> +#include <ipv4.h> +#include <udp.h> +#include <dhcp.h> + +#define DEBUG 0 + +static char * response_buffer; + +#if DEBUG +static void +print_ip(char *ip) +{ + printf("%d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]); +} +#endif + +/* IP header checksum calculation */ +static unsigned short +checksum(unsigned short *packet, int words) +{ + unsigned long checksum; + for (checksum = 0; words > 0; words--) + checksum += *packet++; + checksum = (checksum >> 16) + (checksum & 0xffff); + checksum += (checksum >> 16); + return ~checksum; +} + + +static int +send_bootp(filename_ip_t * fn_ip) +{ +#if DEBUG + int i; +#endif + unsigned int packetsize = + sizeof(struct iphdr) + sizeof(struct ethhdr) + + sizeof(struct udphdr) + sizeof(struct btphdr); + unsigned char packet[packetsize]; + struct iphdr *iph; + struct udphdr *udph; + struct btphdr *btph; + + iph = (struct iphdr *) packet; + udph = (struct udphdr *) (iph + 1); + btph = (struct btphdr *) (udph + 1); + + memset(packet, 0, packetsize); + + fill_iphdr((uint8_t *) iph, htons(packetsize - sizeof(struct ethhdr)), + IPTYPE_UDP, 0, fn_ip->server_ip); + fill_udphdr((uint8_t *) udph, + htons(sizeof(struct udphdr) + sizeof(struct btphdr)), + htons(UDPPORT_BOOTPC), htons(UDPPORT_BOOTPS)); + btph->op = 1; + btph->htype = 1; + btph->hlen = 6; + strcpy((char *) btph->file, "bla"); + memcpy(btph->chaddr, get_mac_address(), 6); + +#if DEBUG + printf("Sending packet\n"); + printf("Packet is "); + for (i = 0; i < packetsize; i++) + printf(" %02x", packet[i]); + printf(".\n"); +#endif + + send_ipv4(fn_ip->fd, packet, iph->ip_len); +#if DEBUG + printf("%d bytes transmitted over socket.\n", i); +#endif + + return 0; +} + + +static int +receive_bootp(filename_ip_t * fn_ip) +{ + int len, old_sum; + unsigned int packetsize = 2000; + unsigned char packet[packetsize]; + struct iphdr *iph; + struct udphdr *udph; + struct btphdr *btph; + +#if DEBUG + struct ethhdr *ethh; + ethh = (struct ethhdr *) packet; +#endif + + iph = (struct iphdr *) (packet + sizeof(struct ethhdr)); + udph = (struct udphdr *) (iph + 1); + btph = (struct btphdr *) (udph + 1); + + memset(packet, 0, packetsize); + + /* setting up a timer with a timeout of one second */ + set_timer(TICKS_SEC); + + do { + + /* let's receive a packet */ + len = recv(fn_ip->fd, packet, packetsize, 0); + +#if DEBUG + int j; + printf("%d bytes received, %d expected \n", len, packetsize); + if (len == 346) { + printf("Rec packet\n"); + printf("Packet is "); + for (j = 0; j < len; j++) { + if (j % 16 == 0) + printf("\n"); + printf(" %02x", packet[j]); + } + printf(".\n"); + } +#endif + if (len == 0) + continue; + + /* check if the ip checksum is correct */ + old_sum = iph->ip_sum; + iph->ip_sum = 0x00; + if (old_sum != + checksum((unsigned short *) iph, sizeof(struct iphdr) >> 1)) + /* checksum failed */ + continue; + /* is it a udp packet */ + if (iph->ip_p != IPTYPE_UDP) + continue; + /* check if the source port and destination port and the packet + * say that it is a bootp answer */ + if (udph->uh_dport != htons(UDPPORT_BOOTPC) || udph->uh_sport != htons(UDPPORT_BOOTPS)) + continue; + /* check if it is a Boot Reply */ + if (btph->op != 2) + continue; + /* Comparing our mac address with the one in the bootp reply */ + if (memcmp(get_mac_address(), btph->chaddr, ETH_ALEN)) + continue; + + if(response_buffer) + memcpy(response_buffer, btph, 1720); + + fn_ip->own_ip = btph->yiaddr; + fn_ip->server_ip = btph->siaddr; + strcpy((char *) fn_ip->filename, (char *) btph->file); + +#if DEBUG + printf("\nThese are the details of the bootp reply:\n"); + printf("Our IP address: "); + print_ip((char*) &fn_ip->own_ip); + printf("Next server IP address: "); + print_ip((char*) &fn_ip->server_ip); + printf("Boot file name: %s\n", btph->file); + printf("Packet is: %s\n", btph->file); + for (j = 0; j < len; j++) { + if (j % 16 == 0) + printf("\n"); + printf(" %02x", packet[j]); + } + printf(".\n"); + printf("fn_ip->own_mac: %02x:%02x:%02x:%02x:%02x:%02x\n", + get_mac_address()[0], get_mac_address()[1], + get_mac_address()[2], get_mac_address()[3], + get_mac_address()[4], get_mac_address()[5]); + printf("Header ethh->dest_mac: %02x:%02x:%02x:%02x:%02x:%02x\n", + ethh->dest_mac[0], ethh->dest_mac[1], ethh->dest_mac[2], + ethh->dest_mac[3], ethh->dest_mac[4], ethh->dest_mac[5]); + printf("Header ethh->src_mac: %02x:%02x:%02x:%02x:%02x:%02x\n", + ethh->src_mac[0], ethh->src_mac[1], ethh->src_mac[2], + ethh->src_mac[3], ethh->src_mac[4], ethh->src_mac[5]); + printf("Header ethh->typ: %x\n",ethh->type); + printf("Header iph->ip_hlv: %x\n",iph->ip_hlv); + printf("Header iph->ip_len: %x\n",iph->ip_len); + printf("Header iph->ip_id: %x\n",iph->ip_id); + printf("Header iph->ip_off: %x\n",iph->ip_off); + printf("Header iph->ip_ttl: %x\n",iph->ip_ttl); + printf("Header iph->ip_p: %x\n",iph->ip_p); + printf("Header iph->ip_sum: %x\n",iph->ip_sum); + printf("Header iph->ip_src: %x\n",iph->ip_src); + printf("Header iph->ip_dst: %x\n",iph->ip_dst); + + printf("Header btph->op: %x\n",btph->op); + printf("Header btph->htype: %x\n",btph->htype); + printf("Header btph->hlen: %x\n",btph->hlen); + printf("Header btph->hops: %x\n",btph->hops); + printf("Header btph->xid: %x\n",btph->xid); + printf("Header btph->secs: %x\n",btph->secs); + printf("Header btph->ciaddr: %x\n",btph->ciaddr); + printf("Header btph->yiaddr: %x\n",btph->yiaddr); + printf("Header btph->siaddr: %x\n",btph->siaddr); + printf("Header btph->giaddr: %x\n",btph->giaddr); + + printf("Header btph->chaddr: %02x:%02x:%02x:%02x:%02x:%02x:\n", + btph->chaddr[0], btph->chaddr[1], btph->chaddr[2], + btph->chaddr[3], btph->chaddr[4], btph->chaddr[5]); +#endif + return 0; + + /* only do this for the time specified during set_timer() */ + } while (get_timer() > 0); + return -1; +} + + +int +bootp(char *ret_buffer, filename_ip_t * fn_ip, unsigned int retries) +{ + int i = (int) retries+1; + fn_ip->own_ip = 0; + + printf(" "); + + response_buffer = ret_buffer; + + do { + printf("\b\b%02d", i); + if (!i--) { + printf("\nGiving up after %d bootp requests\n", + retries+1); + return -1; + } + send_bootp(fn_ip); + /* if the timer in receive_bootp expired it will return + * -1 and we will just send another bootp request just + * in case the previous one was lost. And because we don't + * trust the network cable we keep on doing this 30 times */ + } while (receive_bootp(fn_ip) != 0); + printf("\b\b\b"); + return 0; +} diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/dhcp.c b/qemu/roms/SLOF/clients/net-snk/app/netlib/dhcp.c new file mode 100644 index 000000000..5f26f3afb --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/dhcp.c @@ -0,0 +1,998 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + + +/*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ALGORITHMS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + +/** \file dhcp.c <pre> + * **************** State-transition diagram for DHCP client ************* + * + * +---------+ Note: DHCP-server msg / DHCP-client msg + * | INIT | + * +---------+ + * | + * | - / Discover + * V + * +---------+ + * | SELECT | Timeout + * +---------+ | + * | | + * | Offer / Request | + * | | + * V V + * +---------+ NACK / - *********** + * | REQUEST | ----------------> * FAULT * + * +---------+ *********** + * | + * | ACK / - *********** + * +----------------------> * SUCCESS * + * *********** + * + * ************************************************************************ + * </pre> */ + + +/*>>>>>>>>>>>>>>>>>>>>> DEFINITIONS & DECLARATIONS <<<<<<<<<<<<<<<<<<<<<<*/ + +#include <dhcp.h> +#include <ethernet.h> +#include <ipv4.h> +#include <udp.h> +#include <dns.h> + +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <sys/socket.h> +#include <ctype.h> +#include <stdlib.h> + +/* DHCP Message Types */ +#define DHCPDISCOVER 1 +#define DHCPOFFER 2 +#define DHCPREQUEST 3 +#define DHCPDECLINE 4 +#define DHCPACK 5 +#define DHCPNACK 6 +#define DHCPRELEASE 7 +#define DHCPINFORM 8 + +/* DHCP Option Codes */ +#define DHCP_MASK 1 +#define DHCP_ROUTER 3 +#define DHCP_DNS 6 +#define DHCP_REQUESTED_IP 50 +#define DHCP_OVERLOAD 52 +#define DHCP_MSG_TYPE 53 +#define DHCP_SERVER_ID 54 +#define DHCP_REQUEST_LIST 55 +#define DHCP_TFTP_SERVER 66 +#define DHCP_BOOTFILE 67 +#define DHCP_CLIENT_ARCH 93 +#define DHCP_ENDOPT 0xFF +#define DHCP_PADOPT 0x00 + +/* "file/sname" overload option values */ +#define DHCP_OVERLOAD_FILE 1 +#define DHCP_OVERLOAD_SNAME 2 +#define DHCP_OVERLOAD_BOTH 3 + +/* DHCP states codes */ +#define DHCP_STATE_SELECT 1 +#define DHCP_STATE_REQUEST 2 +#define DHCP_STATE_SUCCESS 3 +#define DHCP_STATE_FAULT 4 + +/* DHCP Client Architecture */ +#ifndef DHCPARCH +#define USE_DHCPARCH 0 +#define DHCPARCH 0 +#else +#define USE_DHCPARCH 1 +#endif + +static uint8_t dhcp_magic[] = {0x63, 0x82, 0x53, 0x63}; +/**< DHCP_magic is a cookie, that identifies DHCP options (see RFC 2132) */ + +/** \struct dhcp_options_t + * This structure is used to fill options in DHCP-msg during transmitting + * or to retrieve options from DHCP-msg during receiving. + * <p> + * If flag[i] == TRUE then field for i-th option retains valid value and + * information from this field may retrived (in case of receiving) or will + * be transmitted (in case of transmitting). + * + */ +typedef struct { + uint8_t flag[256]; /**< Show if corresponding opt. is valid */ + uint8_t request_list[256]; /**< o.55 If i-th member is TRUE, then i-th + option will be requested from server */ + uint32_t server_ID; /**< o.54 Identifies DHCP-server */ + uint32_t requested_IP; /**< o.50 Must be filled in DHCP-Request */ + uint32_t dns_IP; /**< o. 6 DNS IP */ + uint32_t router_IP; /**< o. 3 Router IP */ + uint32_t subnet_mask; /**< o. 1 Subnet mask */ + uint8_t msg_type; /**< o.53 DHCP-message type */ + uint8_t overload; /**< o.52 Overload sname/file fields */ + int8_t tftp_server[256]; /**< o.66 TFTP server name */ + int8_t bootfile[256]; /**< o.67 Boot file name */ + uint16_t client_arch; /**< o.93 Client architecture type */ +} dhcp_options_t; + +/** Stores state of DHCP-client (refer to State-transition diagram) */ +static uint8_t dhcp_state; + + +/*>>>>>>>>>>>>>>>>>>>>>>>>>>>> PROTOTYPES <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + +static int32_t +dhcp_attempt(int fd); + +static int32_t +dhcp_encode_options(uint8_t * opt_field, dhcp_options_t * opt_struct); + +static int32_t +dhcp_decode_options(uint8_t opt_field[], uint32_t opt_len, + dhcp_options_t * opt_struct); + +static int8_t +dhcp_merge_options(uint8_t dst_options[], uint32_t * dst_len, + uint8_t src_options[], uint32_t src_len); + +static int8_t +dhcp_find_option(uint8_t options[], uint32_t len, + uint8_t op_code, uint32_t * op_offset); + +static void +dhcp_append_option(uint8_t dst_options[], uint32_t * dst_len, + uint8_t * new_option); + +static void +dhcp_combine_option(uint8_t dst_options[], uint32_t * dst_len, + uint32_t dst_offset, uint8_t * new_option); + +static void +dhcp_send_discover(int fd); + +static void +dhcp_send_request(int fd); + +static uint8_t +strtoip(int8_t * str, uint32_t * ip); + + +/*>>>>>>>>>>>>>>>>>>>>>>>>>>>> LOCAL VARIABLES <<<<<<<<<<<<<<<<<<<<<<<<<<*/ + +static uint8_t ether_packet[ETH_MTU_SIZE]; +static uint32_t dhcp_own_ip = 0; +static uint32_t dhcp_server_ip = 0; +static uint32_t dhcp_siaddr_ip = 0; +static int8_t dhcp_filename[256]; +static int8_t dhcp_tftp_name[256]; + +static char * response_buffer; + +/*>>>>>>>>>>>>>>>>>>>>>>>>>>>> IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + +int32_t +dhcpv4(char *ret_buffer, filename_ip_t * fn_ip) { + + uint32_t dhcp_tftp_ip = 0; + int fd = fn_ip->fd; + + strcpy((char *) dhcp_filename, ""); + strcpy((char *) dhcp_tftp_name, ""); + + response_buffer = ret_buffer; + + if (dhcp_attempt(fd) == 0) + return -1; + + if (fn_ip->own_ip) { + dhcp_own_ip = fn_ip->own_ip; + } + if (fn_ip->server_ip) { + dhcp_siaddr_ip = fn_ip->server_ip; + } + if(fn_ip->filename[0] != 0) { + strcpy((char *) dhcp_filename, (char *) fn_ip->filename); + } + + // TFTP SERVER + if (!strlen((char *) dhcp_tftp_name)) { + if (!dhcp_siaddr_ip) { + // ERROR: TFTP name is not presented + return -3; + } + + // take TFTP-ip from siaddr field + dhcp_tftp_ip = dhcp_siaddr_ip; + } + else { + // TFTP server defined by its name + if (!strtoip(dhcp_tftp_name, &(dhcp_tftp_ip))) { + if (!dns_get_ip(fd, dhcp_tftp_name, (uint8_t *)&(dhcp_tftp_ip), 4)) { + // DNS error - can't obtain TFTP-server name + // Use TFTP-ip from siaddr field, if presented + if (dhcp_siaddr_ip) { + dhcp_tftp_ip = dhcp_siaddr_ip; + } + else { + // ERROR: Can't obtain TFTP server IP + return -4; + } + } + } + } + + // Store configuration info into filename_ip strucutre + fn_ip -> own_ip = dhcp_own_ip; + fn_ip -> server_ip = dhcp_tftp_ip; + strcpy((char *) fn_ip -> filename, (char *) dhcp_filename); + + return 0; +} + +/** + * DHCP: Tries o obtain DHCP parameters, refer to state-transition diagram + */ +static int32_t +dhcp_attempt(int fd) { + int sec; + + // Send DISCOVER message and switch DHCP-client to SELECT state + dhcp_send_discover(fd); + + dhcp_state = DHCP_STATE_SELECT; + + // setting up a timer with a timeout of two seconds + for (sec = 0; sec < 2; sec++) { + set_timer(TICKS_SEC); + do { + receive_ether(fd); + + // Wait until client will switch to Final state or Timeout occurs + switch (dhcp_state) { + case DHCP_STATE_SUCCESS : + return 1; + case DHCP_STATE_FAULT : + return 0; + } + } while (get_timer() > 0); + } + + // timeout + return 0; +} + +/** + * DHCP: Supplements DHCP-message with options stored in structure. + * For more information about option coding see dhcp_options_t. + * + * @param opt_field Points to the "vend" field of DHCP-message + * (destination) + * @param opt_struct this structure stores info about the options which + * will be added to DHCP-message (source) + * @return TRUE - options packed; + * FALSE - error condition occurs. + * @see dhcp_options_t + */ +static int32_t +dhcp_encode_options(uint8_t * opt_field, dhcp_options_t * opt_struct) { + uint8_t * options = opt_field; + uint16_t i, sum; // used to define is any options set + + // magic + memcpy(options, dhcp_magic, 4); + options += 4; + + // fill message type + switch (opt_struct -> msg_type) { + case DHCPDISCOVER : + case DHCPREQUEST : + case DHCPDECLINE : + case DHCPINFORM : + case DHCPRELEASE : + options[0] = DHCP_MSG_TYPE; + options[1] = 1; + options[2] = opt_struct -> msg_type; + options += 3; + break; + default : + return 0; // Unsupported DHCP-message + } + + if (opt_struct -> overload) { + options[0] = DHCP_OVERLOAD; + options[1] = 0x01; + options[2] = opt_struct -> overload; + options +=3; + } + + if (opt_struct -> flag[DHCP_REQUESTED_IP]) { + options[0] = DHCP_REQUESTED_IP; + options[1] = 0x04; + * (uint32_t *) (options + 2) = htonl (opt_struct -> requested_IP); + options +=6; + } + + if (opt_struct -> flag[DHCP_SERVER_ID]) { + options[0] = DHCP_SERVER_ID; + options[1] = 0x04; + * (uint32_t *) (options + 2) = htonl (opt_struct -> server_ID); + options +=6; + } + + sum = 0; + for (i = 0; i < 256; i++) + sum += opt_struct -> request_list[i]; + + if (sum) { + options[0] = DHCP_REQUEST_LIST; + options[1] = sum; + options += 2; + for (i = 0; i < 256; i++) { + if (opt_struct -> request_list[i]) { + options[0] = i; options++; + } + } + } + + if (opt_struct -> flag[DHCP_TFTP_SERVER]) { + options[0] = DHCP_TFTP_SERVER; + options[1] = strlen((char *) opt_struct -> tftp_server) + 1; + memcpy(options + 2, opt_struct -> tftp_server, options[1]); + options += options[1] + 2; + } + + if (opt_struct -> flag[DHCP_BOOTFILE]) { + options[0] = DHCP_BOOTFILE; + options[1] = strlen((char *) opt_struct -> bootfile) + 1; + memcpy(options + 2, opt_struct -> bootfile, options[1]); + options += options[1] + 2; + } + + if (opt_struct -> flag[DHCP_CLIENT_ARCH]) { + options[0] = DHCP_CLIENT_ARCH; + options[1] = 2; + options[2] = (DHCPARCH >> 8); + options[3] = DHCPARCH & 0xff; + options += 4; + } + + // end options + options[0] = 0xFF; + options++; + + return 1; +} + +/** + * DHCP: Extracts encoded options from DHCP-message into the structure. + * For more information about option coding see dhcp_options_t. + * + * @param opt_field Points to the "options" field of DHCP-message + * (source). + * @param opt_len Length of "options" field. + * @param opt_struct this structure stores info about the options which + * was extracted from DHCP-message (destination). + * @return TRUE - options extracted; + * FALSE - error condition occurs. + * @see dhcp_options_t + */ +static int32_t +dhcp_decode_options(uint8_t opt_field[], uint32_t opt_len, + dhcp_options_t * opt_struct) { + int32_t offset = 0; + + memset(opt_struct, 0, sizeof(dhcp_options_t)); + + // magic + if (memcmp(opt_field, dhcp_magic, 4)) { + return 0; + } + + offset += 4; + while (offset < opt_len) { + opt_struct -> flag[opt_field[offset]] = 1; + switch(opt_field[offset]) { + case DHCP_OVERLOAD : + opt_struct -> overload = opt_field[offset + 2]; + offset += 2 + opt_field[offset + 1]; + break; + + case DHCP_REQUESTED_IP : + opt_struct -> requested_IP = htonl(* (uint32_t *) (opt_field + offset + 2)); + offset += 2 + opt_field[offset + 1]; + break; + + case DHCP_MASK : + opt_struct -> flag[DHCP_MASK] = 1; + opt_struct -> subnet_mask = htonl(* (uint32_t *) (opt_field + offset + 2)); + offset += 2 + opt_field[offset + 1]; + break; + + case DHCP_DNS : + opt_struct -> flag[DHCP_DNS] = 1; + opt_struct -> dns_IP = htonl(* (uint32_t *) (opt_field + offset + 2)); + offset += 2 + opt_field[offset + 1]; + break; + + case DHCP_ROUTER : + opt_struct -> flag[DHCP_ROUTER] = 1; + opt_struct -> router_IP = htonl(* (uint32_t *) (opt_field + offset + 2)); + offset += 2 + opt_field[offset + 1]; + break; + + case DHCP_MSG_TYPE : + if ((opt_field[offset + 2] > 0) && (opt_field[offset + 2] < 9)) + opt_struct -> msg_type = opt_field[offset + 2]; + else + return 0; + offset += 2 + opt_field[offset + 1]; + break; + + case DHCP_SERVER_ID : + opt_struct -> server_ID = htonl(* (uint32_t *) (opt_field + offset + 2)); + offset += 2 + opt_field[offset + 1]; + break; + + case DHCP_TFTP_SERVER : + memcpy(opt_struct -> tftp_server, opt_field + offset + 2, opt_field[offset + 1]); + (opt_struct -> tftp_server)[opt_field[offset + 1]] = 0; + offset += 2 + opt_field[offset + 1]; + break; + + case DHCP_BOOTFILE : + memcpy(opt_struct -> bootfile, opt_field + offset + 2, opt_field[offset + 1]); + (opt_struct -> bootfile)[opt_field[offset + 1]] = 0; + offset += 2 + opt_field[offset + 1]; + break; + + case DHCP_CLIENT_ARCH : + opt_struct -> client_arch = ((opt_field[offset + 2] << 8) & 0xFF00) | (opt_field[offset + 3] & 0xFF); + offset += 4; + break; + + case DHCP_PADOPT : + offset++; + break; + + case DHCP_ENDOPT : // End of options + return 1; + + default : + offset += 2 + opt_field[offset + 1]; // Unsupported opt. - do nothing + } + } + if (offset == opt_len) + return 1; // options finished without 0xFF + + return 0; +} + +/** + * DHCP: Appends information from source "options" into dest "options". + * This function is used to support "file/sname" overloading. + * + * @param dst_options destanation "options" field + * @param dst_len size of dst_options (modified by this function) + * @param src_options source "options" field + * @param src_len size of src_options + * @return TRUE - options merged; + * FALSE - error condition occurs. + */ +static int8_t dhcp_merge_options(uint8_t dst_options[], uint32_t * dst_len, + uint8_t src_options[], uint32_t src_len) { + int32_t dst_offset, src_offset = 0; + + // remove ENDOPT if presented + if (dhcp_find_option(dst_options, * dst_len, DHCP_ENDOPT, (uint32_t *) &dst_offset)) + * dst_len = dst_offset; + + while (src_offset < src_len) { + switch(src_options[src_offset]) { + case DHCP_PADOPT: + src_offset++; + break; + case DHCP_ENDOPT: + return 1; + default: + if (dhcp_find_option(dst_options, * dst_len, + src_options[src_offset], + (uint32_t *) &dst_offset)) { + dhcp_combine_option(dst_options, dst_len, + dst_offset, + (uint8_t *) src_options + + src_offset); + } + else { + dhcp_append_option(dst_options, dst_len, src_options + src_offset); + } + src_offset += 2 + src_options[src_offset + 1]; + } + } + + if (src_offset == src_len) + return 1; + return 0; +} + +/** + * DHCP: Finds given occurrence of the option with the given code (op_code) + * in "options" field of DHCP-message. + * + * @param options "options" field of DHCP-message + * @param len length of the "options" field + * @param op_code code of the option to find + * @param op_offset SUCCESS - offset to an option occurrence; + * FAULT - offset is set to zero. + * @return TRUE - option was find; + * FALSE - option wasn't find. + */ +static int8_t dhcp_find_option(uint8_t options[], uint32_t len, + uint8_t op_code, uint32_t * op_offset) { + uint32_t srch_offset = 0; + * op_offset = 0; + + while (srch_offset < len) { + if (options[srch_offset] == op_code) { + * op_offset = srch_offset; + return 1; + } + if (options[srch_offset] == DHCP_ENDOPT) + return 0; + + if (options[srch_offset] == DHCP_PADOPT) + srch_offset++; + else + srch_offset += 2 + options[srch_offset + 1]; + } + return 0; +} + +/** + * DHCP: Appends new option from one list (src) into the tail + * of another option list (dst) + * + * @param dst_options "options" field of DHCP-message + * @param dst_len length of the "options" field (modified) + * @param new_option points to an option in another list (src) + */ +static void +dhcp_append_option(uint8_t dst_options[], uint32_t * dst_len, + uint8_t * new_option) { + memcpy(dst_options + ( * dst_len), new_option, 2 + (* (new_option + 1))); + * dst_len += 2 + *(new_option + 1); +} + +/** + * DHCP: This function is used when options with the same code are + * presented in both merged lists. In this case information + * about the option from one list (src) is combined (complemented) + * with information about the option in another list (dst). + * + * @param dst_options "options" field of DHCP-message + * @param dst_len length of the "options" field (modified) + * @param dst_offset offset of the option from beginning of the list + * @param new_option points to an option in another list (src) + */ +static void +dhcp_combine_option(uint8_t dst_options[], uint32_t * dst_len, + uint32_t dst_offset, uint8_t * new_option) { + + uint8_t tmp_buffer[1024]; // use to provide safe memcpy + uint32_t tail_len; + + // move all subsequent options (allocate size for additional info) + tail_len = (* dst_len) - dst_offset - 2 - dst_options[dst_offset + 1]; + + memcpy(tmp_buffer, dst_options + (* dst_len) - tail_len, tail_len); + memcpy(dst_options + (* dst_len) - tail_len + (* (new_option + 1)), + tmp_buffer, tail_len); + + // add new_content to option + memcpy(dst_options + (* dst_len) - tail_len, new_option + 2, + * (new_option + 1)); + dst_options[dst_offset + 1] += * (new_option + 1); + + // correct dst_len + * dst_len += * (new_option + 1); +} + +/** + * DHCP: Sends DHCP-Discover message. Looks for DHCP servers. + */ +static void +dhcp_send_discover(int fd) { + uint32_t packetsize = sizeof(struct iphdr) + + sizeof(struct udphdr) + sizeof(struct btphdr); + struct btphdr *btph; + dhcp_options_t opt; + + memset(ether_packet, 0, packetsize); + + btph = (struct btphdr *) (ðer_packet[ + sizeof(struct iphdr) + sizeof(struct udphdr)]); + + btph -> op = 1; + btph -> htype = 1; + btph -> hlen = 6; + memcpy(btph -> chaddr, get_mac_address(), 6); + + memset(&opt, 0, sizeof(dhcp_options_t)); + + opt.msg_type = DHCPDISCOVER; + + opt.request_list[DHCP_MASK] = 1; + opt.request_list[DHCP_DNS] = 1; + opt.request_list[DHCP_ROUTER] = 1; + opt.request_list[DHCP_TFTP_SERVER] = 1; + opt.request_list[DHCP_BOOTFILE] = 1; + opt.request_list[DHCP_CLIENT_ARCH] = USE_DHCPARCH; + + dhcp_encode_options(btph -> vend, &opt); + + fill_udphdr(ðer_packet[sizeof(struct iphdr)], + sizeof(struct btphdr) + sizeof(struct udphdr), + UDPPORT_BOOTPC, UDPPORT_BOOTPS); + fill_iphdr(ether_packet, sizeof(struct btphdr) + + sizeof(struct udphdr) + sizeof(struct iphdr), + IPTYPE_UDP, dhcp_own_ip, 0xFFFFFFFF); + + send_ipv4(fd, ether_packet, packetsize); +} + +/** + * DHCP: Sends DHCP-Request message. Asks for acknowledgment to occupy IP. + */ +static void +dhcp_send_request(int fd) { + uint32_t packetsize = sizeof(struct iphdr) + + sizeof(struct udphdr) + sizeof(struct btphdr); + struct btphdr *btph; + dhcp_options_t opt; + + memset(ether_packet, 0, packetsize); + + btph = (struct btphdr *) (ðer_packet[ + sizeof(struct iphdr) + sizeof(struct udphdr)]); + + btph -> op = 1; + btph -> htype = 1; + btph -> hlen = 6; + memcpy(btph -> chaddr, get_mac_address(), 6); + + memset(&opt, 0, sizeof(dhcp_options_t)); + + opt.msg_type = DHCPREQUEST; + memcpy(&(opt.requested_IP), &dhcp_own_ip, 4); + opt.flag[DHCP_REQUESTED_IP] = 1; + memcpy(&(opt.server_ID), &dhcp_server_ip, 4); + opt.flag[DHCP_SERVER_ID] = 1; + + opt.request_list[DHCP_MASK] = 1; + opt.request_list[DHCP_DNS] = 1; + opt.request_list[DHCP_ROUTER] = 1; + opt.request_list[DHCP_TFTP_SERVER] = 1; + opt.request_list[DHCP_BOOTFILE] = 1; + opt.request_list[DHCP_CLIENT_ARCH] = USE_DHCPARCH; + opt.flag[DHCP_CLIENT_ARCH] = USE_DHCPARCH; + + dhcp_encode_options(btph -> vend, &opt); + + fill_udphdr(ðer_packet[sizeof(struct iphdr)], + sizeof(struct btphdr) + sizeof(struct udphdr), + UDPPORT_BOOTPC, UDPPORT_BOOTPS); + fill_iphdr(ether_packet, sizeof(struct btphdr) + + sizeof(struct udphdr) + sizeof(struct iphdr), + IPTYPE_UDP, 0, 0xFFFFFFFF); + + send_ipv4(fd, ether_packet, packetsize); +} + + +/** + * DHCP: Sends DHCP-Release message. Releases occupied IP. + */ +void dhcp_send_release(int fd) { + uint32_t packetsize = sizeof(struct iphdr) + + sizeof(struct udphdr) + sizeof(struct btphdr); + struct btphdr *btph; + dhcp_options_t opt; + + btph = (struct btphdr *) (ðer_packet[ + sizeof(struct iphdr) + sizeof(struct udphdr)]); + + memset(ether_packet, 0, packetsize); + + btph -> op = 1; + btph -> htype = 1; + btph -> hlen = 6; + strcpy((char *) btph -> file, ""); + memcpy(btph -> chaddr, get_mac_address(), 6); + btph -> ciaddr = htonl(dhcp_own_ip); + + memset(&opt, 0, sizeof(dhcp_options_t)); + + opt.msg_type = DHCPRELEASE; + opt.server_ID = dhcp_server_ip; + opt.flag[DHCP_SERVER_ID] = 1; + + dhcp_encode_options(btph -> vend, &opt); + + fill_udphdr(ðer_packet[sizeof(struct iphdr)], + sizeof(struct btphdr) + sizeof(struct udphdr), + UDPPORT_BOOTPC, UDPPORT_BOOTPS); + fill_iphdr(ether_packet, sizeof(struct btphdr) + + sizeof(struct udphdr) + sizeof(struct iphdr), IPTYPE_UDP, + dhcp_own_ip, dhcp_server_ip); + + send_ipv4(fd, ether_packet, packetsize); +} + +/** + * DHCP: Handles DHCP-messages according to Receive-handle diagram. + * Changes the state of DHCP-client. + * + * @param fd socket descriptor + * @param packet BootP/DHCP-packet to be handled + * @param packetsize length of the packet + * @return ZERO - packet handled successfully; + * NON ZERO - packet was not handled (e.g. bad format) + * @see receive_ether + * @see btphdr + */ + +int8_t +handle_dhcp(int fd, uint8_t * packet, int32_t packetsize) { + struct btphdr * btph; + struct iphdr * iph; + dhcp_options_t opt; + + memset(&opt, 0, sizeof(dhcp_options_t)); + btph = (struct btphdr *) packet; + iph = (struct iphdr *) packet - sizeof(struct udphdr) - + sizeof(struct iphdr); + if (btph -> op != 2) + return -1; // it is not Boot Reply + + if (memcmp(btph -> vend, dhcp_magic, 4)) { + // It is BootP - RFC 951 + dhcp_own_ip = htonl(btph -> yiaddr); + dhcp_siaddr_ip = htonl(btph -> siaddr); + dhcp_server_ip = htonl(iph -> ip_src); + + if (strlen((char *) btph -> sname) && !dhcp_siaddr_ip) { + strncpy((char *) dhcp_tftp_name, (char *) btph -> sname, + sizeof(btph -> sname)); + dhcp_tftp_name[sizeof(btph -> sname)] = 0; + } + + if (strlen((char *) btph -> file)) { + strncpy((char *) dhcp_filename, (char *) btph -> file, sizeof(btph -> file)); + dhcp_filename[sizeof(btph -> file)] = 0; + } + + dhcp_state = DHCP_STATE_SUCCESS; + return 0; + } + + + // decode options + if (!dhcp_decode_options(btph -> vend, packetsize - + sizeof(struct btphdr) + sizeof(btph -> vend), + &opt)) { + return -1; // can't decode options + } + + if (opt.overload) { + int16_t decode_res = 0; + uint8_t options[1024]; // buffer for merged options + uint32_t opt_len; + + // move 1-st part of options from vend field into buffer + opt_len = packetsize - sizeof(struct btphdr) + + sizeof(btph -> vend) - 4; + memcpy(options, btph -> vend, opt_len + 4); + + // add other parts + switch (opt.overload) { + case DHCP_OVERLOAD_FILE: + decode_res = dhcp_merge_options(options + 4, &opt_len, + btph -> file, + sizeof(btph -> file)); + break; + case DHCP_OVERLOAD_SNAME: + decode_res = dhcp_merge_options(options + 4, &opt_len, + btph -> sname, + sizeof(btph -> sname)); + break; + case DHCP_OVERLOAD_BOTH: + decode_res = dhcp_merge_options(options + 4, &opt_len, + btph -> file, + sizeof(btph -> file)); + if (!decode_res) + break; + decode_res = dhcp_merge_options(options + 4, &opt_len, + btph -> sname, + sizeof(btph -> sname)); + break; + } + + if (!decode_res) + return -1; // bad options in sname/file fields + + // decode merged options + if (!dhcp_decode_options(options, opt_len + 4, &opt)) { + return -1; // can't decode options + } + } + + if (!opt.msg_type) { + // It is BootP with Extensions - RFC 1497 + // retrieve conf. settings from BootP - reply + dhcp_own_ip = htonl(btph -> yiaddr); + dhcp_siaddr_ip = htonl(btph -> siaddr); + if (strlen((char *) btph -> sname) && !dhcp_siaddr_ip) { + strncpy((char *) dhcp_tftp_name, (char *) btph -> sname, sizeof(btph -> sname)); + dhcp_tftp_name[sizeof(btph -> sname)] = 0; + } + + if (strlen((char *) btph -> file)) { + strncpy((char *) dhcp_filename, (char *) btph -> file, sizeof(btph -> file)); + dhcp_filename[sizeof(btph -> file)] = 0; + } + + // retrieve DHCP-server IP from IP-header + dhcp_server_ip = iph -> htonl(ip_src); + + dhcp_state = DHCP_STATE_SUCCESS; + } + else { + // It is DHCP - RFC 2131 & RFC 2132 + // opt contains parameters from server + switch (dhcp_state) { + case DHCP_STATE_SELECT : + if (opt.msg_type == DHCPOFFER) { + dhcp_own_ip = htonl(btph -> yiaddr); + dhcp_server_ip = opt.server_ID; + dhcp_send_request(fd); + dhcp_state = DHCP_STATE_REQUEST; + } + return 0; + case DHCP_STATE_REQUEST : + switch (opt.msg_type) { + case DHCPNACK : + dhcp_own_ip = 0; + dhcp_server_ip = 0; + dhcp_state = DHCP_STATE_FAULT; + break; + case DHCPACK : + dhcp_own_ip = htonl(btph -> yiaddr); + dhcp_server_ip = opt.server_ID; + dhcp_siaddr_ip = htonl(btph -> siaddr); + if (opt.flag[DHCP_TFTP_SERVER]) { + strcpy((char *) dhcp_tftp_name, (char *) opt.tftp_server); + } + else { + strcpy((char *) dhcp_tftp_name, ""); + if ((opt.overload != DHCP_OVERLOAD_SNAME && + opt.overload != DHCP_OVERLOAD_BOTH) && + !dhcp_siaddr_ip) { + strncpy((char *) dhcp_tftp_name, + (char *) btph->sname, + sizeof(btph -> sname)); + dhcp_tftp_name[sizeof(btph->sname)] = 0; + } + } + + if (opt.flag[DHCP_BOOTFILE]) { + strcpy((char *) dhcp_filename, (char *) opt.bootfile); + } + else { + strcpy((char *) dhcp_filename, ""); + if (opt.overload != DHCP_OVERLOAD_FILE && + opt.overload != DHCP_OVERLOAD_BOTH && + strlen((char *) btph -> file)) { + strncpy((char *) dhcp_filename, + (char *) btph->file, + sizeof(btph->file)); + dhcp_filename[sizeof(btph -> file)] = 0; + } + } + + dhcp_state = DHCP_STATE_SUCCESS; + break; + default: + break; // Unused DHCP-message - do nothing + } + break; + default : + return -1; // Illegal DHCP-client state + } + } + + if (dhcp_state == DHCP_STATE_SUCCESS) { + + // initialize network entity with real own_ip + // to be able to answer for foreign requests + set_ipv4_address(dhcp_own_ip); + + if(response_buffer) { + if(packetsize <= 1720) + memcpy(response_buffer, packet, packetsize); + else + memcpy(response_buffer, packet, 1720); + } + + /* Subnet mask */ + if (opt.flag[DHCP_MASK]) { + /* Router */ + if (opt.flag[DHCP_ROUTER]) { + set_ipv4_router(opt.router_IP); + set_ipv4_netmask(opt.subnet_mask); + } + } + + /* DNS-server */ + if (opt.flag[DHCP_DNS]) { + dns_init(opt.dns_IP, 0, 4); + } + } + + return 0; +} + +/** + * DHCP: Converts "255.255.255.255" -> 32-bit long IP + * + * @param str string to be converted + * @param ip in case of SUCCESS - 32-bit long IP + in case of FAULT - zero + * @return TRUE - IP converted successfully; + * FALSE - error condition occurs (e.g. bad format) + */ +static uint8_t +strtoip(int8_t * str, uint32_t * ip) { + int8_t ** ptr = &str; + int16_t i = 0, res, len; + char octet[256]; + + * ip = 0; + + while (**ptr != 0) { + if (i > 3 || !isdigit(**ptr)) + return 0; + if (strstr((char *) * ptr, ".") != NULL) { + len = (int16_t) ((int8_t *) strstr((char *) * ptr, ".") - + (int8_t *) (* ptr)); + strncpy(octet, (char *) * ptr, len); octet[len] = 0; + * ptr += len; + } + else { + strcpy(octet, (char *) * ptr); + * ptr += strlen(octet); + } + res = strtol(octet, NULL, 10); + if ((res > 255) || (res < 0)) + return 0; + * ip = ((* ip) << 8) + res; + i++; + if (** ptr == '.') + (*ptr)++; + } + + if (i != 4) + return 0; + return 1; +} diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/dhcp.h b/qemu/roms/SLOF/clients/net-snk/app/netlib/dhcp.h new file mode 100644 index 000000000..69dd49d4a --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/dhcp.h @@ -0,0 +1,53 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef _DHCP_H_ +#define _DHCP_H_ + +#include <stdint.h> + +#ifdef USE_MTFTP +#include <netlib/mtftp.h> +#else +#include <netlib/tftp.h> +#endif + +/** \struct btphdr + * A header for BootP/DHCP-messages. + * For more information see RFC 951 / RFC 2131. + */ +struct btphdr { + uint8_t op; /**< Identifies is it request (1) or reply (2) */ + uint8_t htype; /**< HW address type (ethernet usually) */ + uint8_t hlen; /**< HW address length */ + uint8_t hops; /**< This info used by relay agents (not used) */ + uint32_t xid; /**< This ID is used to match queries and replies */ + uint16_t secs; /**< Unused */ + uint16_t unused; /**< Unused */ + uint32_t ciaddr; /**< Client IP address (if client knows it) */ + uint32_t yiaddr; /**< "Your" (client) IP address */ + uint32_t siaddr; /**< Next server IP address (TFTP server IP) */ + uint32_t giaddr; /**< Gateway IP address (used by relay agents) */ + uint8_t chaddr[16]; /**< Client HW address */ + uint8_t sname[64]; /**< Server host name (TFTP server name) */ + uint8_t file[128]; /**< Boot file name */ + uint8_t vend[64]; /**< Optional parameters field (DHCP-options) */ +}; + +int bootp(char *ret_buffer, filename_ip_t *, unsigned int); +int dhcpv4(char *ret_buffer, filename_ip_t *); +void dhcp_send_release(int fd); + +/* Handles DHCP-packets, which are detected by receive_ether. */ +extern int8_t handle_dhcp(int fd, uint8_t * packet, int32_t packetsize); + +#endif diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/dhcpv6.c b/qemu/roms/SLOF/clients/net-snk/app/netlib/dhcpv6.c new file mode 100644 index 000000000..4deef30f2 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/dhcpv6.c @@ -0,0 +1,216 @@ +/****************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <sys/socket.h> +#include <time.h> +#include <netlib/ethernet.h> +#include <netlib/ipv6.h> +#include <netlib/udp.h> +#include <netlib/dhcpv6.h> +#include <netlib/tftp.h> +#include <netlib/dns.h> + +static uint8_t tid[3]; +static uint32_t dhcpv6_state = -1; +static filename_ip_t *my_fn_ip; + +static void +generate_transaction_id(void) +{ + /* TODO: as per RFC 3315 transaction IDs should be generated randomly */ + tid[0] = 1; + tid[1] = 2; + tid[2] = 4; +} + +static void +send_info_request(int fd) +{ + uint8_t ether_packet[ETH_MTU_SIZE]; + uint32_t payload_length; + struct dhcp_message_header *dhcph; + + memset(ether_packet, 0, ETH_MTU_SIZE); + + generate_transaction_id(); + + /* Get an IPv6 packet */ + payload_length = sizeof(struct udphdr) + sizeof(struct dhcp_message_header); + fill_ip6hdr (ether_packet + sizeof(struct ethhdr), + payload_length, IPTYPE_UDP, + get_ipv6_address(), &(all_dhcpv6_ll.addr)); + fill_udphdr ( ether_packet + sizeof(struct ethhdr) + sizeof(struct ip6hdr), + payload_length, DHCP_CLIENT_PORT, DHCP_SERVER_PORT); + dhcph = (struct dhcp_message_header *) (ether_packet + + sizeof(struct ethhdr) + + sizeof(struct ip6hdr) + + sizeof(struct udphdr)); + + /* Fill in DHCPv6 data */ + dhcph->type = DHCP_INFORMATION_REQUEST; + memcpy( &(dhcph->transaction_id), &tid, 3); + dhcph->option.client_id.code = DHCPV6_OPTION_CLIENTID; + dhcph->option.client_id.length = 10; + dhcph->option.client_id.duid_type = DUID_LL; + dhcph->option.client_id.hardware_type = 1; + memcpy( &(dhcph->option.client_id.mac), + get_mac_address(), 6); + dhcph->option.el_time.code = DHCPV6_OPTION_ELAPSED_TIME; + dhcph->option.el_time.length = 2; + dhcph->option.el_time.time = 0x190; /* 4000 ms */ + dhcph->option.option_request_option.code = DHCPV6_OPTION_ORO; + dhcph->option.option_request_option.length= 6; + dhcph->option.option_request_option.option_code[0] = DHCPV6_OPTION_DNS_SERVERS; + dhcph->option.option_request_option.option_code[1] = DHCPV6_OPTION_DOMAIN_LIST; + dhcph->option.option_request_option.option_code[2] = DHCPV6_OPTION_BOOT_URL; + + + send_ipv6(fd, ether_packet + sizeof(struct ethhdr), + sizeof(struct ethhdr)+ sizeof(struct ip6hdr) + + sizeof(struct udphdr) + + sizeof( struct dhcp_message_header) ); +} + +static int32_t +dhcpv6_attempt(int fd) +{ + int sec; + + // Send information request + send_info_request(fd); + + dhcpv6_state = DHCPV6_STATE_SELECT; + + // setting up a timer with a timeout of two seconds + for (sec = 0; sec < 2; sec++) { + set_timer(TICKS_SEC); + do { + receive_ether(fd); + + // Wait until client will switch to Final state or Timeout occurs + switch (dhcpv6_state) { + case DHCP_STATUSCODE_SUCCESS: + return 1; + case DHCP_STATUSCODE_UNSPECFAIL: //FIXME + return 0; + } + } while (get_timer() > 0); + } + + // timeout + return 0; +} + +int32_t +dhcpv6 ( char *ret_buffer, void *fn_ip) +{ + int fd; + + my_fn_ip = (filename_ip_t *) fn_ip; + fd = my_fn_ip->fd; + + if( !dhcpv6_attempt(fd)) { + return -1; + } + + return 0; +} + +static struct dhcp6_received_options * +dhcp6_process_options (uint8_t *option, int32_t option_length) +{ + struct dhcp_boot_url *option_boot_url; + struct client_identifier *option_clientid; + struct server_identifier *option_serverid; + struct dhcp_dns *option_dns; + struct dhcp_dns_list *option_dns_list; + struct dhcp6_gen_option *option_gen; + struct dhcp6_received_options *received_options; + char buffer[256]; + + + received_options = malloc (sizeof(struct dhcp6_received_options)); + while (option_length > 0) { + switch ((uint16_t) *(option+1)) { + case DHCPV6_OPTION_CLIENTID: + option_clientid = (struct client_identifier *) option; + option = option + option_clientid->length + 4; + option_length = option_length - option_clientid->length - 4; + received_options->client_id = 1; + break; + case DHCPV6_OPTION_SERVERID: + option_serverid = (struct server_identifier *) option; + option = option + option_serverid->length + 4; + option_length = option_length - option_serverid->length - 4; + received_options->server_id = 1; + break; + case DHCPV6_OPTION_DNS_SERVERS: + option_dns = (struct dhcp_dns *) option; + option = option + option_dns->length + 4; + option_length = option_length - option_dns->length - 4; + memcpy( &(my_fn_ip->dns_ip6), + option_dns->p_ip6, + IPV6_ADDR_LENGTH); + dns_init(0, option_dns->p_ip6, 6); + break; + case DHCPV6_OPTION_DOMAIN_LIST: + option_dns_list = (struct dhcp_dns_list *) option; + option = option + option_dns_list->length + 4; + option_length = option_length - option_dns_list->length - 4; + break; + case DHCPV6_OPTION_BOOT_URL: + option_boot_url = (struct dhcp_boot_url *) option; + option = option + option_boot_url->length + 4; + option_length = option_length - option_boot_url->length - 4; + strncpy((char *)buffer, + (const char *)option_boot_url->url, + (size_t)option_boot_url->length); + buffer[option_boot_url->length] = 0; + if (parse_tftp_args(buffer, + (char *)my_fn_ip->server_ip6.addr, + (char *)my_fn_ip->filename, + (int)my_fn_ip->fd, + option_boot_url->length) == -1) + return NULL; + break; + default: + option_gen = (struct dhcp6_gen_option *) option; + option = option + option_gen->length + 4; + option_length = option_length - option_gen->length - 4; + } + } + + return received_options; +} + +uint32_t +handle_dhcpv6(uint8_t * packet, int32_t packetsize) +{ + + uint8_t *first_option; + int32_t option_length; + struct dhcp_message_reply *reply; + reply = (struct dhcp_message_reply *) packet; + + if (reply->type == 7) + dhcpv6_state = DHCP_STATUSCODE_SUCCESS; + + first_option = packet + 4; + option_length = packet + packetsize - first_option; + dhcp6_process_options(first_option, option_length); + + return 0; +} diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/dhcpv6.h b/qemu/roms/SLOF/clients/net-snk/app/netlib/dhcpv6.h new file mode 100644 index 000000000..078a9f11f --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/dhcpv6.h @@ -0,0 +1,157 @@ +/****************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef _DHCPV6_H_ +#define _DHCPV6_H_ + +#include <stdint.h> +#include <netlib/ethernet.h> + +#define DHCPV6_STATELESS 0 +#define DHCPV6_STATEFUL 1 + +/* DHCP port numbers */ +#define DHCP_CLIENT_PORT 546 +#define DHCP_SERVER_PORT 547 + +/* DHCPv6 message types */ +#define DHCP_SOLICIT 1 +#define DHCP_ADVERTISE 2 +#define DHCP_REQUEST 3 +#define DHCP_CONFIRM 4 +#define DHCP_RENEW 5 +#define DHCP_REBIND 6 +#define DHCP_REPLY 7 +#define DHCP_RELEASE 8 +#define DHCP_DECLINE 9 +#define DHCP_RECONFIGURE 10 +#define DHCP_INFORMATION_REQUEST 11 +#define RELAY_FORW 12 +#define RELAY_REPL 13 + +/* DHCPv6 option types */ +#define DHCPV6_OPTION_CLIENTID 0x0001 +#define DHCPV6_OPTION_SERVERID 0x0002 +#define DHCPV6_OPTION_IA_NA 3 +#define DHCPV6_OPTION_IA_TA 4 +#define DHCPV6_OPTION_IAADDR 5 +#define DHCPV6_OPTION_ORO 6 +#define DHCPV6_OPTION_PREFEREN 7 +#define DHCPV6_OPTION_ELAPSED_TIME 8 +#define DHCPV6_OPTION_RELAY_MS 9 +#define DHCPV6_OPTION_AUTH 11 +#define DHCPV6_OPTION_UNICAST 12 +#define DHCPV6_OPTION_STATUS_C 13 +#define DHCPV6_OPTION_RAPID_CO 14 +#define DHCPV6_OPTION_USER_CLA 15 +#define DHCPV6_OPTION_VENDOR_C 16 +#define DHCPV6_OPTION_VENDOR_O 17 +#define DHCPV6_OPTION_INTERFAC 18 +#define DHCPV6_OPTION_RECONF_M 19 +#define DHCPV6_OPTION_RECONF_A 20 +#define DHCPV6_OPTION_DNS_SERVERS 23 +#define DHCPV6_OPTION_DOMAIN_LIST 24 +#define DHCPV6_OPTION_BOOT_URL 59 + +/* DHCPv6 status codes */ +#define DHCP_STATUSCODE_SUCCESS 0 +#define DHCP_STATUSCODE_UNSPECFAIL 1 +#define DHCP_STATUSCODE_NOADDRAVAIL 2 +#define DHCP_STATUSCODE_NOBINDING 3 +#define DHCP_STATUSCODE_NOTONLINK 4 +#define DHCP_STATUSCODE_USEMULTICAST 5 +#define DHCPV6_STATE_SELECT 6 + +/* DUID types */ +#define DUID_LLT 1 /* DUID based on Link-layer Address Plus Time */ +#define DUID_EN 2 /* DUID based on Assigned by Vendor Based on Enterprise Number */ +#define DUID_LL 3 /* DUID based on Link-layer Address */ + +/* Prototypes */ +int32_t dhcpv6 ( char *ret_buffer, void *fn_ip); +uint32_t handle_dhcpv6(uint8_t * , int32_t); + +struct dhcp6_gen_option { + uint16_t code; + uint16_t length; +}; + +struct client_identifier { + uint16_t code; + uint16_t length; + uint16_t duid_type; + uint16_t hardware_type; + uint8_t mac[6]; +}; + +struct server_identifier { + uint16_t code; + uint16_t length; + uint16_t duid_type; + uint16_t hardware_type; + uint32_t time; + uint8_t mac[6]; +}; + +struct dhcp_info_request { + struct client_identifier client_id; + struct elapsed_time { + uint16_t code; + uint16_t length; + uint16_t time; + } el_time; + struct option_request { + uint16_t code; + uint16_t length; + uint16_t option_code[5]; + } option_request_option; +}; + +struct dhcp_message_header { + uint8_t type; /* Message type */ + uint8_t transaction_id[3]; /* Transaction id */ + struct dhcp_info_request option; +}; + +struct dhcp_dns { + uint16_t code; + uint16_t length; + uint8_t p_ip6[16]; + uint8_t s_ip6[16]; +}__attribute((packed)); + +struct dhcp_dns_list { + uint16_t code; + uint16_t length; + uint8_t domain[256]; +}__attribute((packed)); + +struct dhcp_boot_url { + uint16_t type; + uint16_t length; + uint8_t url[256]; +}; + +struct dhcp6_received_options { + uint8_t filename; + uint8_t ip; + uint8_t client_id; + uint8_t server_id; +}; +struct dhcp_message_reply { + uint8_t type; /* Message type */ + uint8_t transaction_id[3]; /* Transaction id */ + struct client_identifier client_id; + struct server_identifier server_id; +}; + +#endif diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/dns.c b/qemu/roms/SLOF/clients/net-snk/app/netlib/dns.c new file mode 100644 index 000000000..0ab1346c9 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/dns.c @@ -0,0 +1,527 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +/********************** DEFINITIONS & DECLARATIONS ***********************/ + +#include <dns.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <sys/socket.h> + +#include <ethernet.h> +#include <ipv4.h> +#include <ipv6.h> +#include <udp.h> + +#define DNS_FLAG_MSGTYPE 0xF800 /**< Message type mask (opcode) */ +#define DNS_FLAG_SQUERY 0x0000 /**< Standard query type */ +#define DNS_FLAG_SRESPONSE 0x8000 /**< Standard response type */ +#define DNS_FLAG_RD 0x0100 /**< Recursion desired flag */ +#define DNS_FLAG_RCODE 0x000F /**< Response code mask + (stores err.cond.) code */ +#define DNS_RCODE_NERROR 0 /**< "No errors" code */ + +#define DNS_QTYPE_A 1 /**< A 32-bit IP record type */ +#define DNS_QTYPE_AAAA 0x1c /**< 128-bit IPv6 record type */ +#define DNS_QTYPE_CNAME 5 /**< Canonical name record type */ + +#define DNS_QCLASS_IN 1 /**< Query class for internet msgs */ + +/** \struct dnshdr + * A header for DNS-messages (see RFC 1035, paragraph 4.1.1). + * <p> + * DNS-message consist of DNS-header and 4 optional sections, + * arranged in the following order:<ul> + * <li> DNS-header + * <li> question section + * <li> answer section + * <li> authority section + * <li> additional section + * </ul> + */ +struct dnshdr { + uint16_t id; /**< an identifier used to match up replies */ + uint16_t flags; /**< contains op_code, err_code, etc. */ + uint16_t qdcount; /**< specifies the number of entries in the + question section */ + uint16_t ancount; /**< specifies the number of entries in the + answer section */ + uint16_t nscount; /**< specifies the number of entries in the + authority section */ + uint16_t arcount; /**< specifies the number of entries in the + additional section */ +}; + + +/***************************** PROTOTYPES ********************************/ + +static void +dns_send_query(int fd, int8_t * domain_name, uint8_t ip_version); + +static void +fill_dnshdr(uint8_t * packet, int8_t * domain_name, uint8_t ip_version); + +static uint8_t * +dns_extract_name(uint8_t * dnsh, int8_t * head, int8_t * domain_name); + +static int8_t +urltohost(char * url, char * host_name); + +static int8_t +hosttodomain(char * host_name, char * domain_name); + +/**************************** LOCAL VARIABLES ****************************/ + +static uint8_t ether_packet[ETH_MTU_SIZE]; +static int32_t dns_server_ip = 0; +static uint8_t dns_server_ipv6[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static int32_t dns_result_ip = 0; +static uint8_t dns_result_ipv6[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static int8_t dns_error = 0; /**< Stores error code or 0 */ +static int8_t dns_domain_name[0x100]; /**< Raw domain name */ +static int8_t dns_domain_cname[0x100]; /**< Canonical domain name */ + +/**************************** IMPLEMENTATION *****************************/ + +/** + * DNS: Initialize the environment for DNS client. + * To perfrom DNS-queries use the function dns_get_ip. + * + * @param device_socket a socket number used to send and receive packets + * @param server_ip DNS-server IPv4 address (e.g. 127.0.0.1) + * @return TRUE in case of successful initialization; + * FALSE in case of fault (e.g. can't obtain MAC). + * @see dns_get_ip + */ +int8_t +dns_init(uint32_t _dns_server_ip, uint8_t _dns_server_ipv6[16], uint8_t ip_version) +{ + if(ip_version == 6) + memcpy(dns_server_ipv6, _dns_server_ipv6, 16); + else + dns_server_ip = _dns_server_ip; + return 0; +} + +/** + * DNS: For given URL retrieves IPv4/IPv6 from DNS-server. + * <p> + * URL can be given in one of the following form: <ul> + * <li> scheme with full path with (without) user and password + * <br>(e.g. "http://user:pass@www.host.org/url-path"); + * <li> host name with url-path + * <br>(e.g. "www.host.org/url-path"); + * <li> nothing but host name + * <br>(e.g. "www.host.org"); + * </ul> + * + * @param fd socket descriptor + * @param url the URL to be resolved + * @param domain_ip In case of SUCCESS stores extracted IP. + * In case of FAULT stores zeros (0.0.0.0). + * @return TRUE - IP successfuly retrieved; + * FALSE - error condition occurs. + */ +int8_t +dns_get_ip(int fd, int8_t * url, uint8_t * domain_ip, uint8_t ip_version) +{ + /* this counter is used so that we abort after 30 DNS request */ + int32_t i; + /* this buffer stores host name retrieved from url */ + static int8_t host_name[0x100]; + + (* domain_ip) = 0; + + // Retrieve host name from URL + if (!urltohost((char *) url, (char *) host_name)) { + printf("\nERROR:\t\t\tBad URL!\n"); + return 0; + } + + // Reformat host name into a series of labels + if (!hosttodomain((char *) host_name, (char *) dns_domain_name)) { + printf("\nERROR:\t\t\tBad host name!\n"); + return 0; + } + + // Check if DNS server is presented and accessible + if (dns_server_ip == 0) { + printf("\nERROR:\t\t\tCan't resolve domain name " + "(DNS server is not presented)!\n"); + return 0; + } + + // Use DNS-server to obtain IP + if (ip_version == 6) + memset(dns_result_ipv6, 0, 16); + else + dns_result_ip = 0; + dns_error = 0; + strcpy((char *) dns_domain_cname, ""); + + for(i = 0; i < 30; ++i) { + // Use canonical name in case we obtained it + if (strlen((char *) dns_domain_cname)) + dns_send_query(fd, dns_domain_cname, ip_version); + else + dns_send_query(fd, dns_domain_name, ip_version); + + // setting up a timer with a timeout of one seconds + set_timer(TICKS_SEC); + do { + receive_ether(fd); + if (dns_error) + return 0; // FALSE - error + if ((dns_result_ip != 0) && (ip_version == 4)) { + memcpy(domain_ip, &dns_result_ip, 4); + return 1; // TRUE - success (domain IP retrieved) + } + else if ((dns_result_ipv6[0] != 0) && (ip_version == 6)) { + memcpy(domain_ip, dns_result_ipv6, 16); + return 1; // TRUE - success (domain IP retrieved) + } + } while (get_timer() > 0); + } + + printf("\nGiving up after %d DNS requests\n", i); + return 0; // FALSE - domain name wasn't retrieved +} + +/** + * DNS: Handles DNS-messages according to Receive-handle diagram. + * Sets dns_result_ip for given dns_domain_name (see dns_get_ip) + * or signals error condition occurs during DNS-resolving process + * by setting dns_error flag. + * + * @param packet DNS-packet to be handled + * @param packetsize length of the packet + * @return ZERO - packet handled successfully; + * NON ZERO - packet was not handled (e.g. bad format) + * @see dns_get_ip + * @see receive_ether + * @see dnshdr + */ +int32_t +handle_dns(uint8_t * packet, int32_t packetsize) +{ + struct dnshdr * dnsh = (struct dnshdr *) packet; + uint8_t * resp_section = packet + sizeof(struct dnshdr); + /* This string stores domain name from DNS-packets */ + static int8_t handle_domain_name[0x100]; + int i; + + // verify ID - is it response for our query? + if (dnsh -> id != htons(0x1234)) + return 0; + + // Is it DNS response? + if ((dnsh -> flags & htons(DNS_FLAG_MSGTYPE)) != htons(DNS_FLAG_SRESPONSE)) + return 0; + + // Is error condition occurs? (check error field in incoming packet) + if ((dnsh -> flags & htons(DNS_FLAG_RCODE)) != DNS_RCODE_NERROR) { + dns_error = 1; + return 0; + } + + /* Pass all (qdcount) records in question section */ + + for (i = 0; i < htons(dnsh -> qdcount); i++) { + // pass QNAME + resp_section = dns_extract_name((uint8_t *) dnsh, (int8_t *) resp_section, + handle_domain_name); + if (resp_section == NULL) { + return -1; // incorrect domain name (bad packet) + } + // pass QTYPE & QCLASS + resp_section += 4; + } + + /* Handle all (ancount) records in answer section */ + + for (i = 0; i < htons(dnsh -> ancount); i++) { + // retrieve domain name from the packet + resp_section = dns_extract_name((uint8_t *) dnsh, (int8_t *) resp_section, + handle_domain_name); + + if (resp_section == NULL) { + return -1; // incorrect domain name (bad packet) + } + + // Check the class of the query (should be IN for Internet) + if (* (uint16_t *) (resp_section + 2) == htons(DNS_QCLASS_IN)) { + // check if retrieved name fit raw or canonical domain name + if (!strcmp((char *) handle_domain_name, (char *) dns_domain_name) || + !strcmp((char *) handle_domain_name, (char *) dns_domain_cname)) { + switch (htons(* (uint16_t *) resp_section)) { + + case DNS_QTYPE_A : + // rdata contains IP + dns_result_ip = htonl(* (uint32_t *) (resp_section + 10)); + return 0; // IP successfully obtained + + case DNS_QTYPE_CNAME : + // rdata contains canonical name, store it for further requests + if (dns_extract_name((uint8_t *) dnsh, (int8_t *) resp_section + 10, + dns_domain_cname) == NULL) { + // incorrect domain name (bad packet) + return -1; + } + break; + case DNS_QTYPE_AAAA : + memcpy(dns_result_ipv6, (resp_section + 10), 16); + return 0; // IP successfully obtained + break; + } + } + // continue with next record in answer section + resp_section += htons(* (uint16_t *) (resp_section + 8)) + 10; + } + } + return 0; // Packet successfully handled but IP wasn't obtained +} + +/** + * DNS: Sends a standard DNS-query (read request package) to a DNS-server. + * DNS-server respones with host IP or signals some error condition. + * Responses from the server are handled by handle_dns function. + * + * @param fd socket descriptor + * @param domain_name the domain name given as series of labels preceded + * with length(label) and terminated with 0 + * <br>(e.g. "\3,w,w,w,\4,h,o,s,t,\3,o,r,g,\0") + * @see handle_dns + */ +static void +dns_send_query(int fd, int8_t * domain_name, uint8_t ip_version) +{ + int qry_len = strlen((char *) domain_name) + 5; + int iphdr_len = (ip_version == 4) ? sizeof(struct iphdr) : sizeof(struct ip6hdr); + ip6_addr_t server_ipv6; + + uint32_t packetsize = iphdr_len + + sizeof(struct udphdr) + sizeof(struct dnshdr) + + qry_len; + + memset(ether_packet, 0, packetsize); + fill_dnshdr(ðer_packet[ + iphdr_len + sizeof(struct udphdr)], + domain_name, + ip_version); + fill_udphdr(ðer_packet[iphdr_len], + sizeof(struct dnshdr) + + sizeof(struct udphdr) + qry_len, + UDPPORT_DNSC, UDPPORT_DNSS); + if (ip_version == 4) { + fill_iphdr(ether_packet, + sizeof(struct dnshdr) + sizeof(struct udphdr) + + iphdr_len + qry_len, + IPTYPE_UDP, 0, dns_server_ip); + } else { + memcpy(server_ipv6.addr, dns_server_ipv6, 16); + fill_ip6hdr(ether_packet, + sizeof(struct dnshdr) + sizeof(struct udphdr) + qry_len, + IPTYPE_UDP, get_ipv6_address(), + &server_ipv6); + } + + send_ip(fd, ether_packet, packetsize); +} + +/** + * DNS: Creates standard DNS-query package. Places DNS-header + * and question section in a packet and fills it with + * corresponding information. + * <p> + * Use this function with similar functions for other network layers + * (fill_udphdr, fill_iphdr, fill_ethhdr). + * + * @param packet Points to the place where ARP-header must be placed. + * @param domain_name the domain name given as series of labels preceded + * with length(label) and terminated with 0 + * <br>(e.g. "\3,w,w,w,\4,h,o,s,t,\3,o,r,g,\0") + * @see fill_udphdr + * @see fill_iphdr + * @see fill_ethhdr + */ +static void +fill_dnshdr(uint8_t * packet, int8_t * domain_name, uint8_t ip_version) +{ + struct dnshdr * dnsh = (struct dnshdr *) packet; + uint8_t * qry_section = packet + sizeof(struct dnshdr); + + dnsh -> id = htons(0x1234); + dnsh -> flags = htons(DNS_FLAG_SQUERY) | htons(DNS_FLAG_RD); + dnsh -> qdcount = htons(1); + + strcpy((char *) qry_section, (char *) domain_name); + qry_section += strlen((char *) domain_name) + 1; + + // fill QTYPE (ask for IP) + if (ip_version == 4) + * (uint16_t *) qry_section = htons(DNS_QTYPE_A); + else + * (uint16_t *) qry_section = htons(DNS_QTYPE_AAAA); + qry_section += 2; + // fill QCLASS (IN is a standard class for Internet) + * (uint16_t *) qry_section = htons(DNS_QCLASS_IN); +} + +/** + * DNS: Extracts domain name from the question or answer section of + * the DNS-message. This function is need to support message + * compression requirement (see RFC 1035, paragraph 4.1.4). + * + * @param dnsh Points at the DNS-header. + * @param head Points at the beginning of the domain_name + * which has to be extracted. + * @param domain_name In case of SUCCESS this string stores extracted name. + * In case of FAULT this string is empty. + * @return NULL in case of FAULT (domain name > 255 octets); + * otherwise pointer to the data following the name. + * @see dnshdr + */ +static uint8_t * +dns_extract_name(uint8_t * dnsh, int8_t * head, int8_t * domain_name) +{ + int8_t * tail = domain_name; + int8_t * ptr = head; + int8_t * next_section = NULL; + + while (1) { + if ((ptr[0] & 0xC0) == 0xC0) { + // message compressed (reference is used) + next_section = ptr + 2; + ptr = (int8_t *) dnsh + (htons(* (uint16_t *) ptr) & 0x3FFF); + continue; + } + if (ptr[0] == 0) { + // message termination + tail[0] = 0; + ptr += 1; + break; + } + // maximum length for domain name is 255 octets w/o termination sym + if (tail - domain_name + ptr[0] + 1 > 255) { + strcpy((char *) domain_name, ""); + return NULL; + } + memcpy(tail, ptr, ptr[0] + 1); + tail += ptr[0] + 1; + ptr += ptr[0] + 1; + } + + if (next_section == NULL) + next_section = ptr; + + return (uint8_t *) next_section; +} + +/** + * DNS: Parses URL and returns host name. + * Input string can be given as: <ul> + * <li> scheme with full path with (without) user and password + * <br>(e.g. "http://user:pass@www.host.org/url-path"); + * <li> host name with url-path + * <br>(e.g. "www.host.org/url-path"); + * <li> nothing but host name + * <br>(e.g. "www.host.org"); + * </ul> + * + * @param url string that stores incoming URL + * @param host_name In case of SUCCESS this string stores the host name, + * In case of FAULT this string is empty. + * @return TRUE - host name retrieved, + * FALSE - host name > 255 octets or empty. + */ +static int8_t +urltohost(char * url, char * host_name) +{ + uint16_t length1; + uint16_t length2; + + strcpy(host_name, ""); + + if (strstr(url, "://") != NULL) + url = strstr(url, "//") + 2; // URL + + if (strstr(url, "@") != NULL) // truncate user & password + url = strstr(url, "@") + 1; + + if (strstr(url, "/") != NULL) // truncate url path + length1 = strstr(url, "/") - url; + else + length1 = strlen(url); + + if (strstr(url, ":") != NULL) // truncate port path + length2 = strstr(url, ":") - url; + else + length2 = strlen(url); + + if(length1 > length2) + length1 = length2; + + if (length1 == 0) + return 0; // string is empty + if(length1 >= 256) + return 0; // host name is too big + + strncpy(host_name, url, length1); + host_name[length1] = 0; + + return 1; // Host name is retrieved +} + +/** + * DNS: Transforms host name string into a series of labels + * each of them preceded with length(label). 0 is a terminator. + * "www.domain.dom" -> "\3,w,w,w,\6,d,o,m,a,i,n,\3,c,o,m,\0" + * <p> + * This format is used in DNS-messages. + * + * @param host_name incoming string with the host name + * @param domain_name resulting string with series of labels + * or empty string in case of FAULT + * @return TRUE - host name transformed, + * FALSE - host name > 255 octets or label > 63 octets. + */ +static int8_t +hosttodomain(char * host_name, char * domain_name) +{ + char * domain_iter = domain_name; + char * host_iter = host_name; + + strcpy(domain_name, ""); + + if(strlen(host_name) > 255) + return 0; // invalid host name (refer to RFC 1035) + + for(; 1; ++host_iter) { + if(*host_iter != '.' && *host_iter != 0) + continue; + *domain_iter = host_iter - host_name; + if (*domain_iter > 63) { + strcpy(domain_name, ""); + return 0; // invalid host name (refer to RFC 1035) + } + ++domain_iter; + strncpy(domain_iter, host_name, host_iter - host_name); + domain_iter += (host_iter - host_name); + if(*host_iter == 0) { + *domain_iter = 0; + break; + } + host_name = host_iter + 1; + } + return 1; // ok +} diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/dns.h b/qemu/roms/SLOF/clients/net-snk/app/netlib/dns.h new file mode 100644 index 000000000..82eea4e4d --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/dns.h @@ -0,0 +1,28 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + + +#ifndef _DNS_H_ +#define _DNS_H_ + +#include <stdint.h> + +/* Initialize the environment for DNS client. */ +extern int8_t dns_init(uint32_t _dns_server_ip, uint8_t _dns_server_ipv6[16], uint8_t ip_version); + +/* For given URL retrieves IPv4 from DNS-server. */ +extern int8_t dns_get_ip(int fd, int8_t * url, uint8_t * domain_ip, uint8_t ip_version); + +/* Handles DNS-packets, which are detected by receive_ether. */ +extern int32_t handle_dns(uint8_t * packet, int32_t packetsize); + +#endif diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/ethernet.c b/qemu/roms/SLOF/clients/net-snk/app/netlib/ethernet.c new file mode 100644 index 000000000..bbfd6d1c3 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/ethernet.c @@ -0,0 +1,187 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + + +/*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ALGORITHMS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + +/** \file netbase.c <pre> + * *********************** Receive-handle diagram ************************* + * + * Note: Every layer calls out required upper layer + * + * lower + * | MAC/LLC Receive packet (receive_ether) + * | | + * | NETWORK +-----------+---------+ + * | | | + * | IPv4 (handle_ipv4) IPv6 (handle_ipv4) + * | ARP (handle_arp) ICMP & NDP + * | ICMP | + * | | | + * | +---------+---------+ + * | | + * | TRANSPORT +---------+---------+ + * | | | + * | TCP (handle_tcp) UDP (handle_udp) + * | | + * | APPLICATION +----------------+-----------+ + * V | | + * upper DNS (handle_dns) BootP / DHCP (handle_bootp_client) + * + * ************************************************************************ + * </pre> */ + + +/*>>>>>>>>>>>>>>>>>>>>>>> DEFINITIONS & DECLARATIONS <<<<<<<<<<<<<<<<<<<<*/ + +#include <ethernet.h> +#include <string.h> +#include <sys/socket.h> +#include <ipv4.h> +#include <ipv6.h> + + +/*>>>>>>>>>>>>>>>>>>>>>>>>>>>>> LOCAL VARIABLES <<<<<<<<<<<<<<<<<<<<<<<<<*/ + +static uint8_t ether_packet[ETH_MTU_SIZE]; +static uint8_t own_mac[6] = {0, 0, 0, 0, 0, 0}; +static uint8_t multicast_mac[] = {0x01, 0x00, 0x5E}; +static const uint8_t broadcast_mac[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + +/*>>>>>>>>>>>>>>>>>>>>>>>>>>>>> IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<*/ + +/** + * Ethernet: Set the own MAC address to initializes ethernet layer. + * + * @param own_mac own hardware-address (MAC) + */ +void +set_mac_address(const uint8_t * _own_mac) { + if (_own_mac) + memcpy(own_mac, _own_mac, 6); + else + memset(own_mac, 0, 6); +} + +/** + * Ethernet: Set the own MAC address to initializes ethernet layer. + * + * @return own hardware-address (MAC) + */ +const uint8_t * +get_mac_address(void) { + return own_mac; +} + +/** + * Ethernet: Check if given multicast address is a multicast MAC address + * starting with 0x3333 + * + * @return true or false + */ +static uint8_t +is_multicast_mac(uint8_t * mac) { + + uint16_t mc = 0x3333; + if (memcmp(mac, &mc, 2) == 0) + return 1; + + return 0; +} + + +/** + * Ethernet: Receives an ethernet-packet and handles it according to + * Receive-handle diagram. + * + * @param fd socket fd + * @return ZERO - packet was handled or no packets received; + * NON ZERO - error condition occurs. + */ +int32_t +receive_ether(int fd) { + int32_t bytes_received; + struct ethhdr * ethh; + + memset(ether_packet, 0, ETH_MTU_SIZE); + bytes_received = recv(fd, ether_packet, ETH_MTU_SIZE, 0); + + if (!bytes_received) // No messages + return 0; + + if (bytes_received < sizeof(struct ethhdr)) + return -1; // packet is too small + + ethh = (struct ethhdr *) ether_packet; + + if(memcmp(ethh->dest_mac, broadcast_mac, 6) != 0 + && memcmp(ethh->dest_mac, multicast_mac, 3) != 0 + && memcmp(ethh->dest_mac, own_mac, 6 ) != 0 + && !is_multicast_mac(ethh->dest_mac)) + return -1; // packet is too small + + switch (htons(ethh -> type)) { + case ETHERTYPE_IP: + return handle_ipv4(fd, (uint8_t*) (ethh + 1), + bytes_received - sizeof(struct ethhdr)); + + case ETHERTYPE_IPv6: + return handle_ipv6(fd, ether_packet + sizeof(struct ethhdr), + bytes_received - sizeof(struct ethhdr)); + + case ETHERTYPE_ARP: + return handle_arp(fd, (uint8_t*) (ethh + 1), + bytes_received - sizeof(struct ethhdr)); + default: + break; + } + return -1; // unknown protocol +} + +/** + * Ethernet: Sends an ethernet frame via the initialized file descriptor. + * + * @return number of transmitted bytes + */ +int +send_ether(int fd, void* buffer, int len) +{ + return send(fd, buffer, len, 0); +} + +/** + * Ethernet: Creates Ethernet-packet. Places Ethernet-header in a packet and + * fills it with corresponding information. + * <p> + * Use this function with similar functions for other network layers + * (fill_arphdr, fill_iphdr, fill_udphdr, fill_dnshdr, fill_btphdr). + * + * @param packet Points to the place where eth-header must be placed. + * @param eth_type Type of the next level protocol (e.g. IP or ARP). + * @param src_mac Sender MAC address + * @param dest_mac Receiver MAC address + * @see ethhdr + * @see fill_arphdr + * @see fill_iphdr + * @see fill_udphdr + * @see fill_dnshdr + * @see fill_btphdr + */ +void +fill_ethhdr(uint8_t * packet, uint16_t eth_type, + const uint8_t * src_mac, const uint8_t * dest_mac) { + struct ethhdr * ethh = (struct ethhdr *) packet; + + ethh -> type = htons(eth_type); + memcpy(ethh -> src_mac, src_mac, 6); + memcpy(ethh -> dest_mac, dest_mac, 6); +} diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/ethernet.h b/qemu/roms/SLOF/clients/net-snk/app/netlib/ethernet.h new file mode 100644 index 000000000..e541c8f8e --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/ethernet.h @@ -0,0 +1,47 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef _ETHERNET_H +#define _ETHERNET_H + +#include <stdint.h> + +#define ETH_MTU_SIZE 1518 /**< Maximum Transfer Unit */ +#define ETH_ALEN 6 /**< HW address length */ +#define ETHERTYPE_IP 0x0800 +#define ETHERTYPE_IPv6 0x86DD +#define ETHERTYPE_ARP 0x0806 + +/** \struct ethhdr + * A header for Ethernet-packets. + */ +struct ethhdr { + uint8_t dest_mac[ETH_ALEN]; /**< Destination HW address */ + uint8_t src_mac[ETH_ALEN]; /**< Source HW address */ + uint16_t type; /**< Next level protocol type */ +}; + +/* Initializes ethernet layer */ +extern void set_mac_address(const uint8_t * own_mac); +extern const uint8_t * get_mac_address(void); + +/* Receives and handles packets, according to Receive-handle diagram */ +extern int32_t receive_ether(int fd); + +/* Sends an ethernet frame. */ +extern int send_ether(int fd, void* buffer, int len); + +/* fills ethernet header */ +extern void fill_ethhdr(uint8_t * packet, uint16_t eth_type, + const uint8_t * src_mac, const uint8_t * dest_mac); + +#endif diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/icmpv6.c b/qemu/roms/SLOF/clients/net-snk/app/netlib/icmpv6.c new file mode 100644 index 000000000..be6cc110f --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/icmpv6.c @@ -0,0 +1,391 @@ +/****************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/socket.h> +#include <netlib/ethernet.h> +#include <netlib/ipv6.h> +#include <netlib/icmpv6.h> +#include <netlib/ndp.h> +#include <netlib/dhcpv6.h> + +static int ra_received = 0; + +/** + * NET: + * @param fd socket fd + */ +void +send_router_solicitation (int fd) +{ + ip6_addr_t dest_addr; + uint8_t ether_packet[ETH_MTU_SIZE]; + struct packeth headers; + + headers.ip6h = (struct ip6hdr *) (ether_packet + sizeof(struct ethhdr)); + headers.icmp6h = (struct icmp6hdr *) (ether_packet + + sizeof(struct ethhdr) + + sizeof(struct ip6hdr)); + + /* Destination is "All routers multicast address" (link-local) */ + dest_addr.part.prefix = all_routers_ll.addr.part.prefix; + dest_addr.part.interface_id = all_routers_ll.addr.part.interface_id; + + + /* Fill IPv6 header */ + fill_ip6hdr (ether_packet + sizeof(struct ethhdr), + ICMPv6_HEADER_SIZE + sizeof(struct router_solicitation), + 0x3a, //ICMPV6 + get_ipv6_address(), &dest_addr); + + /* Fill ICMPv6 message */ + headers.icmp6h->type = ICMPV6_ROUTER_SOLICITATION; + headers.icmp6h->code = 0; + headers.icmp6h->icmp6body.router_solicit.lladdr.type = 1; + headers.icmp6h->icmp6body.router_solicit.lladdr.length = 1; + memcpy( &(headers.icmp6h->icmp6body.router_solicit.lladdr.mac), + get_mac_address(), 6); + + send_ip (fd, headers.ip6h, sizeof(struct ip6hdr) + + ICMPv6_HEADER_SIZE + sizeof(struct router_solicitation)); +} + +/** + * NET: Process prefix option in Router Advertisements + * + * @param ip6_packet pointer to an IPv6 packet + */ +static void +handle_prefixoption (uint8_t *option) +{ + ip6_addr_t prefix; + struct ip6addr_list_entry *new_address; + struct option_prefix *prefix_option; + struct prefix_info *prfx_info; + + prefix_option = (struct option_prefix *) option; + memcpy( &(prefix.addr), &(prefix_option->prefix.addr), IPV6_ADDR_LENGTH); + + /* Link-local adresses in RAs are nonsense */ + if ( (IPV6_LL_PREFIX & (prefix_option->prefix.part.prefix)) == IPV6_LL_PREFIX ) + return; + + if (prefix_option->preferred_lifetime > prefix_option->valid_lifetime) + return; + + /* Add address created from prefix to IPv6 address list */ + new_address = ip6_prefix2addr (prefix); + if (!new_address) + return; + + /* Process only prefixes we don't already have an adress from */ + if (!unknown_prefix (&new_address->addr)) { + return; + } + + /* Fill struct prefix_info from data in RA and store it in new_address */ + prfx_info = ip6_create_prefix_info(); + if (!prfx_info) + return; + memcpy (&(new_address->prfx_info), prfx_info, sizeof(struct prefix_info)); + + /* Add prefix received in RA to list of known prefixes */ + ip6addr_add (new_address); +} + +/** + * NET: Process source link layer addresses in Router Advertisements + * + * @param ip6_packet pointer to an IPv6 packet + */ +static void +handle_source_lladdr ( struct option_ll_address *option, struct router *rtr) +{ + memcpy (&(rtr->mac), &(option->mac), 6); +} + +/** + * NET: Process ICMPv6 options in Router Advertisements + * + * @param ip6_packet pointer to an IPv6 packet + */ +static void +process_ra_options (uint8_t *option, int32_t option_length, struct router *r) +{ + while (option_length > 0) { + switch (*option) { + case ND_OPTION_SOURCE_LL_ADDR: + handle_source_lladdr ((struct option_ll_address *) option, r); + break; + case ND_OPTION_PREFIX_INFO: + handle_prefixoption(option); + break; + default: + break; + } + //option+1 is the length field. length is in units of 8 bytes + option_length = option_length - (*(option+1) * 8); + option = option + (*(option+1) * 8); + } + + return; +} + +/** + * NET: Process Router Advertisements + * + * @param ip6_packet pointer to an IPv6 packet + */ +static void +handle_ra (struct icmp6hdr *icmp6h, uint8_t *ip6_packet) +{ + uint8_t *first_option; + int32_t option_length; + struct ip6hdr *ip6h; + struct router_advertisement *ra; + struct router *rtr; + ip6_addr_t *rtr_ip; + uint8_t rtr_mac[] = {0, 0, 0, 0, 0, 0}; + + ip6h = (struct ip6hdr *) ip6_packet; + ra = (struct router_advertisement *) &icmp6h->icmp6body.ra; + rtr_ip = (ip6_addr_t *) &ip6h->src; + + rtr = find_router (&(ip6h->src)); + if (!rtr) { + rtr = router_create (rtr_mac, rtr_ip); + router_add (rtr); + } + + /* store info from router advertisement in router struct */ + rtr->lifetime = ra->router_lifetime; + rtr->reachable_time = ra->reachable_time; + rtr->retrans_timer = ra->retrans_timer; + + /* save flags concerning address (auto-) configuration */ + ip6_state.managed_mode = ra->flags.managed; + ip6_state.other_config = ra->flags.other; + + /* Process ICMPv6 options in Router Advertisement */ + first_option = (uint8_t *) icmp6h + ICMPv6_HEADER_SIZE + 12; + option_length = (uint8_t *) icmp6h + ip6h->pl - first_option; + process_ra_options( (uint8_t *) first_option, option_length, rtr); + + ra_received = 1; +} + +int is_ra_received(void) +{ + return ra_received; +} + +/** + * NET: + * + * @param fd socket fd + * @param ip6_addr_t *dest_ip6 + */ +void +send_neighbour_solicitation (int fd, ip6_addr_t *dest_ip6) +{ + ip6_addr_t snma; + + uint8_t ether_packet[ETH_MTU_SIZE]; + struct packeth headers; + + memset(ether_packet, 0, ETH_MTU_SIZE); + headers.ethh = (struct ethhdr *) ether_packet; + headers.ip6h = (struct ip6hdr *) (ether_packet + sizeof(struct ethhdr)); + headers.icmp6h = (struct icmp6hdr *) (ether_packet + + sizeof(struct ethhdr) + + sizeof(struct ip6hdr)); + + /* Fill IPv6 header */ + snma.part.prefix = IPV6_SOLIC_NODE_PREFIX; + snma.part.interface_id = IPV6_SOLIC_NODE_IFACE_ID; + snma.addr[13] = dest_ip6->addr[13]; + snma.addr[14] = dest_ip6->addr[14]; + snma.addr[15] = dest_ip6->addr[15]; + fill_ip6hdr((uint8_t *) headers.ip6h, + ICMPv6_HEADER_SIZE + + sizeof(struct neighbour_solicitation), + 0x3a, //ICMPv6 + get_ipv6_address(), &snma); + + /* Fill ICMPv6 message */ + headers.icmp6h->type = ICMPV6_NEIGHBOUR_SOLICITATION; + headers.icmp6h->code = 0; + memcpy( &(headers.icmp6h->icmp6body.nghb_solicit.target), + dest_ip6, IPV6_ADDR_LENGTH ); + headers.icmp6h->icmp6body.nghb_solicit.lladdr.type = 1; + headers.icmp6h->icmp6body.nghb_solicit.lladdr.length = 1; + memcpy( &(headers.icmp6h->icmp6body.nghb_solicit.lladdr.mac), + get_mac_address(), 6); + + send_ip (fd, ether_packet + sizeof(struct ethhdr), + sizeof(struct ip6hdr) + ICMPv6_HEADER_SIZE + + sizeof(struct neighbour_solicitation)); +} + +/** + * NET: + * + * @param fd socket fd + * @param ip6_packet pointer to an IPv6 packet + * @param icmp6hdr pointer to the icmp6 header in ip6_packet + * @param na_flags Neighbour advertisment flags + */ +static void +send_neighbour_advertisement (int fd, struct neighbor *target) +{ + struct na_flags na_adv_flags; + uint8_t ether_packet[ETH_MTU_SIZE]; + struct packeth headers; + + + headers.ip6h = (struct ip6hdr *) (ether_packet + sizeof(struct ethhdr)); + headers.icmp6h = (struct icmp6hdr *) (ether_packet + + sizeof(struct ethhdr) + + sizeof(struct ip6hdr)); + + /* Fill IPv6 header */ + fill_ip6hdr(ether_packet + sizeof(struct ethhdr), + ICMPv6_HEADER_SIZE + + sizeof(struct neighbour_advertisement), + 0x3a, //ICMPv6 + get_ipv6_address(), (ip6_addr_t *) &(target->ip.addr)); + + /* Fill ICMPv6 message */ + memcpy( &(headers.icmp6h->icmp6body.nghb_adv.target), + &(target->ip.addr), IPV6_ADDR_LENGTH ); + headers.icmp6h->icmp6body.nghb_adv.lladdr.type = 1; + headers.icmp6h->icmp6body.nghb_adv.lladdr.length = 1; + memcpy( &(headers.icmp6h->icmp6body.nghb_adv.lladdr.mac), + get_mac_address(), 6); + + na_adv_flags.is_router = 0; + na_adv_flags.na_is_solicited = 1; + na_adv_flags.override = 1; + + headers.icmp6h->type = ICMPV6_NEIGHBOUR_ADVERTISEMENT; + headers.icmp6h->code = 0; + headers.icmp6h->icmp6body.nghb_adv.router = na_adv_flags.is_router; + + headers.icmp6h->icmp6body.nghb_adv.solicited = na_adv_flags.na_is_solicited; + headers.icmp6h->icmp6body.nghb_adv.override = na_adv_flags.override; + headers.icmp6h->icmp6body.nghb_adv.lladdr.type = 2; + headers.icmp6h->icmp6body.nghb_adv.lladdr.length = 1; + + memset( &(headers.icmp6h->icmp6body.nghb_adv.target), 0, + IPV6_ADDR_LENGTH ); + + if( na_adv_flags.na_is_solicited ) { + memcpy( &(headers.icmp6h->icmp6body.nghb_adv.target), + get_ipv6_address(), IPV6_ADDR_LENGTH); + } + + memcpy( &(headers.icmp6h->icmp6body.nghb_adv.lladdr.mac), + get_mac_address(), 6); + + send_ip (fd, ether_packet + sizeof(struct ethhdr), + sizeof(struct ip6hdr) + ICMPv6_HEADER_SIZE + + sizeof(struct neighbour_advertisement)); +} + +/** + * NET: + * + * @param fd socket fd + * @param ip6_packet pointer to an IPv6 packet + */ +static int8_t +handle_na (int fd, uint8_t *packet) +{ + struct neighbor *n = NULL; + struct packeth headers; + ip6_addr_t ip; + + headers.ethh = (struct ethhdr *) packet; + headers.ip6h = (struct ip6hdr *) ((unsigned char *) headers.ethh + + sizeof(struct ethhdr)); + headers.icmp6h = (struct icmp6hdr *) (packet + + sizeof(struct ethhdr) + + sizeof(struct ip6hdr)); + + memcpy(&(ip.addr), &(headers.ip6h->src), IPV6_ADDR_LENGTH); + + n = find_neighbor (&ip); + + if (!n) { + n= (struct neighbor *) + neighbor_create( packet, &headers ); + if (!n) + return 0; + if (!neighbor_add(n)) + return 0; + } else { + memcpy (&(n->mac), &(headers.ethh->src_mac[0]), 6); + + if (n->eth_len > 0) { + struct ethhdr * ethh = (struct ethhdr *) &(n->eth_frame); + memcpy(ethh->dest_mac, &(n->mac), 6); + send_ether (fd, &(n->eth_frame), n->eth_len + sizeof(struct ethhdr)); + n->eth_len = 0; + } + } + + return 1; +} + +/** + * NET: Handles ICMPv6 messages + * + * @param fd socket fd + * @param ip6_packet pointer to an IPv6 packet + * @param packetsize size of ipv6_packet + */ +int8_t +handle_icmpv6 (int fd, struct ethhdr *etherhdr, + uint8_t *ip6_packet) +{ + + struct icmp6hdr *received_icmp6 = NULL; + struct ip6hdr *received_ip6 = NULL; + struct neighbor target; + + received_ip6 = (struct ip6hdr *) ip6_packet; + received_icmp6 = (struct icmp6hdr *) (ip6_packet + + sizeof(struct ip6hdr)); + memcpy( &(target.ip.addr), &(received_ip6->src), + IPV6_ADDR_LENGTH ); + memcpy( &(target.mac), etherhdr->src_mac, 6); + + /* process ICMPv6 types */ + switch(received_icmp6->type) { + case ICMPV6_NEIGHBOUR_SOLICITATION: + send_neighbour_advertisement(fd, &target); + break; + case ICMPV6_NEIGHBOUR_ADVERTISEMENT: + handle_na(fd, (uint8_t *) ip6_packet - sizeof(struct ethhdr)); + break; + case ICMPV6_ROUTER_ADVERTISEMENT: + handle_ra(received_icmp6, (uint8_t *) received_ip6); + break; + default: + return -1; + } + + return 1; +} diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/icmpv6.h b/qemu/roms/SLOF/clients/net-snk/app/netlib/icmpv6.h new file mode 100644 index 000000000..32797973d --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/icmpv6.h @@ -0,0 +1,135 @@ +/****************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef _ICMPV6_H_ +#define _ICMPV6_H_ + +#include <stdint.h> +#include <netlib/ethernet.h> +#include <netlib/ipv6.h> + +#define __ICMPV6_DEBUG__ + +#ifdef __ICMPV6_DEBUG__ +#define ICMPV6_DEBUG_PRINT(format, ...) printf(format, ## __VA_ARGS__) +#else +#define ICMPV6_DEBUG_PRINT(format, ...) +#endif + +#define ICMPv6_HEADER_SIZE 4 /* Size of common fields */ +#define IPTYPE_ICMPV6 0x3a + +/* Error message types */ +#define ICMPV6_DEST_UNREACHABLE 1 /* Destination unreachable */ +#define ICMPV6_PACKET_TOO_BIG 2 /* Packet too big */ +#define ICMPV6_TIME_EXCEEDED 3 /* Time exceeded */ +#define ICMPV6_PARAM_PROBLEM 4 /* Parameter problem */ + +/* Informational message types */ +#define ICMPV6_ECHO_REQUEST 128 /* Echo request */ +#define ICMPV6_ECHO_REPLY 129 /* Echo reply */ +#define ICMPV6_MCAST_LISTENER_QUERY 130 /* Multicast listener query */ +#define ICMPV6_MCAST_LISTENER_REPORT 131 /* Multicast listener report */ +#define ICMPv6 MCAST_LISTENER_DONE 132 /* Multicast listener done */ +#define ICMPV6_ROUTER_SOLICITATION 133 /* Router solicitation */ +#define ICMPV6_ROUTER_ADVERTISEMENT 134 /* Router advertisement */ +#define ICMPV6_NEIGHBOUR_SOLICITATION 135 /* Neighbor solicitation */ +#define ICMPV6_NEIGHBOUR_ADVERTISEMENT 136 /* Neighbor advertisement */ +#define ICMPV6_REDIRECT_MSG 137 /* Redirect message */ + +/******** Functions *******************/ +int8_t handle_icmpv6 (int fd, struct ethhdr *etherhdr, uint8_t *ip6_packet); +void send_neighbour_solicitation(int fd, ip6_addr_t *target_ip6); +void send_router_solicitation(int fd); +int is_ra_received(void); + +/* Prefix information */ +struct option_prefix { + uint8_t type; + uint8_t length; + uint8_t prefix_length; + uint8_t onlink:1, + autom:1, + not_router:1, + not_site_prefix:1, + reserved:4; + uint32_t valid_lifetime; + uint32_t preferred_lifetime; + uint32_t reserved2; + ip6_addr_t prefix; +} __attribute((packed)); + +/* Neighbour advertisement/solicitation flags */ +struct na_flags { + uint8_t is_router:1, /* sender (we) is a router */ + na_is_solicited:1, /* this NA was solicited (asked for) */ + override:1, /* receiver shall override its cache entries */ + unused:5; +}__attribute((packed)); + +/* Source/Target Link-layer address */ +struct option_ll_address{ + uint8_t type; + uint8_t length; + uint8_t mac[ETH_ALEN]; +} __attribute((packed)); + +struct neighbour_solicitation { + uint32_t router:1, + solicited:1, + override:1, + reserved:29; + ip6_addr_t target; + struct option_ll_address lladdr; +} __attribute((packed)); + +struct neighbour_advertisement { + uint32_t router:1, + solicited:1, + override:1, + reserved:29; + ip6_addr_t target; + struct option_ll_address lladdr; +} __attribute((packed)); + +struct router_solicitation { + uint32_t reserved; + struct option_ll_address lladdr; +} __attribute((packed)); + +struct router_advertisement { + uint8_t curr_hop_limit; + struct raflags { + uint8_t managed:1, + other:1, + reserved:6; + } flags; + uint16_t router_lifetime; + uint32_t reachable_time; + uint32_t retrans_timer; + struct option_prefix prefix; + struct option_ll_address ll_addr; +} __attribute((packed)); + +struct icmp6hdr { + uint8_t type; + uint8_t code; + uint16_t checksum; + union { + struct neighbour_solicitation nghb_solicit; + struct neighbour_advertisement nghb_adv; + struct router_solicitation router_solicit; + struct router_advertisement ra; + } icmp6body; +} __attribute((packed)); + +#endif diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/ipv4.c b/qemu/roms/SLOF/clients/net-snk/app/netlib/ipv4.c new file mode 100644 index 000000000..8185de5e1 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/ipv4.c @@ -0,0 +1,904 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + + +/*>>>>>>>>>>>>>>>>>>>>> DEFINITIONS & DECLARATIONS <<<<<<<<<<<<<<<<<<<<<<*/ + +#include <ipv4.h> +#include <udp.h> +#include <tcp.h> +#include <ethernet.h> +#include <time.h> +#include <sys/socket.h> +#include <string.h> + +/* ARP Message types */ +#define ARP_REQUEST 1 +#define ARP_REPLY 2 + +/* ARP talbe size (+1) */ +#define ARP_ENTRIES 10 + +/* ICMP Message types */ +#define ICMP_ECHO_REPLY 0 +#define ICMP_DST_UNREACHABLE 3 +#define ICMP_SRC_QUENCH 4 +#define ICMP_REDIRECT 5 +#define ICMP_ECHO_REQUEST 8 +#define ICMP_TIME_EXCEEDED 11 +#define ICMP_PARAMETER_PROBLEM 12 +#define ICMP_TIMESTAMP_REQUEST 13 +#define ICMP_TIMESTAMP_REPLY 14 +#define ICMP_INFORMATION_REQUEST 15 +#define ICMP_INFORMATION_REPLY 16 + +/** \struct arp_entry + * A entry that describes a mapping between IPv4- and MAC-address. + */ +typedef struct arp_entry arp_entry_t; +struct arp_entry { + uint32_t ipv4_addr; + uint8_t mac_addr[6]; + uint8_t eth_frame[ETH_MTU_SIZE]; + int eth_len; + int pkt_pending; +}; + +/** \struct icmphdr + * ICMP packet + */ +struct icmphdr { + unsigned char type; + unsigned char code; + unsigned short int checksum; + union { + /* for type 3 "Destination Unreachable" */ + unsigned int unused; + /* for type 0 and 8 */ + struct echo { + unsigned short int id; + unsigned short int seq; + } echo; + } options; + union { + /* payload for destination unreachable */ + struct dun { + unsigned char iphdr[20]; + unsigned char data[64]; + } dun; + /* payload for echo or echo reply */ + /* maximum size supported is 84 */ + unsigned char data[84]; + } payload; +}; + +/*>>>>>>>>>>>>>>>>>>>>>>>>>>>>> PROTOTYPES <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + +static unsigned short +checksum(unsigned short *packet, int words); + +static void +arp_send_request(int fd, uint32_t dest_ip); + +static void +arp_send_reply(int fd, uint32_t src_ip, uint8_t * src_mac); + +static void +fill_arphdr(uint8_t * packet, uint8_t opcode, + const uint8_t * src_mac, uint32_t src_ip, + const uint8_t * dest_mac, uint32_t dest_ip); + +static arp_entry_t* +lookup_mac_addr(uint32_t ipv4_addr); + +static void +fill_udp_checksum(struct iphdr *ipv4_hdr); + +static int8_t +handle_icmp(int fd, struct iphdr * iph, uint8_t * packet, int32_t packetsize); + +/*>>>>>>>>>>>>>>>>>>>>>>>>>>>>> LOCAL VARIABLES <<<<<<<<<<<<<<<<<<<<<<<<<*/ + +/* Routing parameters */ +static uint32_t own_ip = 0; +static uint32_t multicast_ip = 0; +static uint32_t router_ip = 0; +static uint32_t subnet_mask = 0; + +/* helper variables */ +static uint32_t ping_dst_ip; +static const uint8_t null_mac_addr[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +static const uint8_t broadcast_mac[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; +static uint8_t multicast_mac[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + +/* There are only (ARP_ENTRIES-1) effective entries because + * the entry that is pointed by arp_producer is never used. + */ +static unsigned int arp_consumer = 0; +static unsigned int arp_producer = 0; +static arp_entry_t arp_table[ARP_ENTRIES]; +static arp_entry_t pending_pkt; + +/* Function pointer send_ip. Points either to send_ipv4() or send_ipv6() */ +int (*send_ip) (int fd, void *, int); + +/*>>>>>>>>>>>>>>>>>>>>>>>>>>>> IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + +/** + * IPv4: Initialize the environment for the IPv4 layer. + */ +static void +ipv4_init(void) +{ + int i; + + ping_dst_ip = 0; + + // clear ARP table + arp_consumer = 0; + arp_producer = 0; + for(i=0; i<ARP_ENTRIES; ++i) { + arp_table[i].ipv4_addr = 0; + memset(arp_table[i].mac_addr, 0, 6); + arp_table[i].eth_len = 0; + arp_table[i].pkt_pending = 0; + } + + /* Set IP send function to send_ipv4() */ + send_ip = &send_ipv4; +} + +/** + * IPv4: Set the own IPv4 address. + * + * @param _own_ip client IPv4 address (e.g. 127.0.0.1) + */ +void +set_ipv4_address(uint32_t _own_ip) +{ + own_ip = _own_ip; + ipv4_init(); +} + +/** + * IPv4: Get the own IPv4 address. + * + * @return client IPv4 address (e.g. 127.0.0.1) + */ +uint32_t +get_ipv4_address(void) +{ + return own_ip; +} + +/** + * IPv4: Set the IPv4 multicast address. + * + * @param _own_ip multicast IPv4 address (224.0.0.0 - 239.255.255.255) + */ +void +set_ipv4_multicast(uint32_t _multicast_ip) +{ + // is this IP Multicast out of range (224.0.0.0 - 239.255.255.255) + if((htonl(_multicast_ip) < 0xE0000000) + || (htonl(_multicast_ip) > 0xEFFFFFFF)) { + multicast_ip = 0; + memset(multicast_mac, 0xFF, 6); + return; + } + + multicast_ip = _multicast_ip; + multicast_mac[0] = 0x01; + multicast_mac[1] = 0x00; + multicast_mac[2] = 0x5E; + multicast_mac[3] = (uint8_t) 0x7F & (multicast_ip >> 16); + multicast_mac[4] = (uint8_t) 0xFF & (multicast_ip >> 8); + multicast_mac[5] = (uint8_t) 0xFF & (multicast_ip >> 0); +} + +/** + * IPv4: Get the IPv4 multicast address. + * + * @return multicast IPv4 address (224.0.0.0 - 239.255.255.255 or 0 if not set) + */ +uint32_t +get_ipv4_multicast(void) +{ + return multicast_ip; +} + +/** + * IPv4: Set the routers IPv4 address. + * + * @param _router_ip router IPv4 address + */ +void +set_ipv4_router(uint32_t _router_ip) +{ + router_ip = _router_ip; + ipv4_init(); +} + +/** + * IPv4: Get the routers IPv4 address. + * + * @return router IPv4 address + */ +uint32_t +get_ipv4_router(void) +{ + return router_ip; +} + +/** + * IPv4: Set the subnet mask. + * + * @param _subnet_mask netmask of the own IPv4 address + */ +void +set_ipv4_netmask(uint32_t _subnet_mask) +{ + subnet_mask = _subnet_mask; + ipv4_init(); +} + +/** + * IPv4: Get the subnet mask. + * + * @return netmask of the own IPv4 address + */ +uint32_t +get_ipv4_netmask(void) +{ + return subnet_mask; +} + +/** + * IPv4: Creates IP-packet. Places IP-header in a packet and fills it + * with corresponding information. + * <p> + * Use this function with similar functions for other network layers + * (fill_ethhdr, fill_udphdr, fill_dnshdr, fill_btphdr). + * + * @param packet Points to the place where IP-header must be placed. + * @param packetsize Size of the packet in bytes incl. this hdr and data. + * @param ip_proto Type of the next level protocol (e.g. UDP). + * @param ip_src Sender IP address + * @param ip_dst Receiver IP address + * @see iphdr + * @see fill_ethhdr + * @see fill_udphdr + * @see fill_dnshdr + * @see fill_btphdr + */ +void +fill_iphdr(uint8_t * packet, uint16_t packetsize, + uint8_t ip_proto, uint32_t ip_src, uint32_t ip_dst) { + struct iphdr * iph = (struct iphdr *) packet; + + iph -> ip_hlv = 0x45; + iph -> ip_tos = 0x10; + iph -> ip_len = htons(packetsize); + iph -> ip_id = htons(0); + iph -> ip_off = 0; + iph -> ip_ttl = 0xFF; + iph -> ip_p = ip_proto; + iph -> ip_src = htonl(ip_src); + iph -> ip_dst = htonl(ip_dst); + iph -> ip_sum = 0; +} + +/** + * IPv4: Handles IPv4-packets according to Receive-handle diagram. + * + * @param fd socket fd + * @param ip_packet IP-packet to be handled + * @param packetsize Length of the packet + * @return ZERO - packet handled successfully; + * NON ZERO - packet was not handled (e.g. bad format) + * @see receive_ether + * @see iphdr + */ +int8_t +handle_ipv4(int fd, uint8_t * ip_packet, int32_t packetsize) +{ + struct iphdr * iph; + int32_t old_sum; + static uint8_t ip_heap[65536 + ETH_MTU_SIZE]; + + if (packetsize < sizeof(struct iphdr)) + return -1; // packet is too small + + iph = (struct iphdr * ) ip_packet; + + /* Drop it if destination IPv4 address is no IPv4 Broadcast, no + * registered IPv4 Multicast and not our Unicast address + */ + if((multicast_ip == 0 && iph->ip_dst >= 0xE0000000 && iph->ip_dst <= 0xEFFFFFFF) + || (multicast_ip != iph->ip_dst && iph->ip_dst != 0xFFFFFFFF && + own_ip != 0 && iph->ip_dst != own_ip)) { + return -1; + } + + old_sum = iph -> ip_sum; + iph -> ip_sum = 0; + if (old_sum != checksum((uint16_t *) iph, sizeof (struct iphdr) >> 1)) + return -1; // Wrong IP checksum + + // is it the first fragment in a packet? + if (((iph -> ip_off) & 0x1FFF) == 0) { + // is it part of more fragments? + if (((iph -> ip_off) & 0x2000) == 0x2000) { + memcpy(ip_heap, ip_packet, iph->ip_len); + return 0; + } + } + // it's not the first fragment + else { + // get the first fragment + struct iphdr * iph_first = (struct iphdr * ) ip_heap; + + // is this fragment not part of the first one, then exit + if ((iph_first->ip_id != iph->ip_id ) || + (iph_first->ip_p != iph->ip_p ) || + (iph_first->ip_src != iph->ip_src) || + (iph_first->ip_dst != iph->ip_dst)) { + return 0; + } + + // this fragment is part of the first one! + memcpy(ip_heap + sizeof(struct iphdr) + + ((iph -> ip_off) & 0x1FFF) * 8, + ip_packet + sizeof(struct iphdr), + iph -> ip_len - sizeof(struct iphdr)); + + // is it part of more fragments? Then return. + if (((iph -> ip_off) & 0x2000) == 0x2000) { + return 0; + } + + // packet is completly reassambled now! + + // recalculate ip_len and set iph and ip_packet to the + iph_first->ip_len = iph->ip_len + ((iph->ip_off) & 0x1FFF) * 8; + + // set iph and ip_packet to the resulting packet. + ip_packet = ip_heap; + iph = (struct iphdr * ) ip_packet; + } + + switch (iph -> ip_p) { + case IPTYPE_ICMP: + return handle_icmp(fd, iph, ip_packet + sizeof(struct iphdr), + iph -> ip_len - sizeof(struct iphdr)); + case IPTYPE_UDP: + return handle_udp(fd, ip_packet + sizeof(struct iphdr), + iph -> ip_len - sizeof(struct iphdr)); + case IPTYPE_TCP: + return handle_tcp(ip_packet + sizeof(struct iphdr), + iph -> ip_len - sizeof(struct iphdr)); + default: + break; + } + return -1; // Unknown protocol +} + +/** + * IPv4: Send IPv4-packets. + * + * Before the packet is sent there are some patcches performed: + * - IPv4 source address is replaced by our unicast IPV4 address + * if it is set to 0 or 1 + * - IPv4 destination address is replaced by our multicast IPV4 address + * if it is set to 1 + * - IPv4 checksum is calculaded. + * - If payload type is UDP, then the UDP checksum is calculated also. + * + * We send an ARP request first, if this is the first packet sent to + * the declared IPv4 destination address. In this case we store the + * the packet and send it later if we receive the ARP response. + * If the MAC address is known already, then we send the packet immediately. + * If there is already an ARP request pending, then we drop this packet + * and send again an ARP request. + * + * @param fd socket fd + * @param ip_packet IP-packet to be handled + * @param packetsize Length of the packet + * @return -2 - packet dropped (MAC address not resolved - ARP request pending) + * -1 - packet dropped (bad format) + * 0 - packet stored (ARP request sent - packet will be sent if + * ARP response is received) + * >0 - packet send (number of transmitted bytes is returned) + * + * @see receive_ether + * @see iphdr + */ +int +send_ipv4(int fd, void* buffer, int len) +{ + arp_entry_t *arp_entry = 0; + struct iphdr *ip; + const uint8_t *mac_addr = 0; + uint32_t ip_dst = 0; + + if(len + sizeof(struct ethhdr) > ETH_MTU_SIZE) + return -1; + + ip = (struct iphdr *) buffer; + + /* Replace source IPv4 address with our own unicast IPv4 address + * if it's 0 (= own unicast source address not specified). + */ + if(ip->ip_src == 0) { + ip->ip_src = htonl( own_ip ); + } + /* Replace source IPv4 address with our unicast IPv4 address and + * replace destination IPv4 address with our multicast IPv4 address + * if source address is set to 1. + */ + else if(ip->ip_src == 1) { + ip->ip_src = htonl( own_ip ); + ip->ip_dst = htonl( multicast_ip ); + } + + // Calculate the IPv4 checksum + ip->ip_sum = 0; + ip->ip_sum = checksum((uint16_t *) ip, sizeof (struct iphdr) >> 1); + + // if payload type is UDP, then we need to calculate the + // UDP checksum that depends on the IP header + if(ip->ip_p == IPTYPE_UDP) { + fill_udp_checksum(ip); + } + + ip_dst = ip->ip_dst; + // Check if the MAC address is already cached + if(~ip->ip_dst == 0 + || ( ((~subnet_mask) & ip->ip_dst) == ~subnet_mask && + ( subnet_mask & ip->ip_dst) == (subnet_mask & own_ip))) { + arp_entry = &arp_table[arp_producer]; + mac_addr = broadcast_mac; + } + else if(ip->ip_dst == multicast_ip) { + arp_entry = &arp_table[arp_producer]; + mac_addr = multicast_mac; + } + else { + // Check if IP address is in the same subnet as we are + if((subnet_mask & own_ip) == (subnet_mask & ip->ip_dst)) + arp_entry = lookup_mac_addr(ip->ip_dst); + // if not then we need to know the router's IP address + else { + ip_dst = router_ip; + arp_entry = lookup_mac_addr(router_ip); + } + if(arp_entry && memcmp(arp_entry->mac_addr, null_mac_addr, 6) != 0) + mac_addr = arp_entry->mac_addr; + } + + // If we could not resolv the MAC address by our own... + if(!mac_addr) { + // send the ARP request + arp_send_request(fd, ip_dst); + + // drop the current packet if there is already a ARP request pending + if(arp_entry) + return -2; + + // take the next entry in the ARP table to prepare a the new ARP entry. + arp_entry = &arp_table[arp_producer]; + arp_producer = (arp_producer+1)%ARP_ENTRIES; + + // if ARP table is full then we must drop the oldes entry. + if(arp_consumer == arp_producer) + arp_consumer = (arp_consumer+1)%ARP_ENTRIES; + + // store the packet to be send if the ARP reply is received + arp_entry->pkt_pending = 1; + arp_entry->ipv4_addr = ip_dst; + memset(arp_entry->mac_addr, 0, 6); + pending_pkt.ipv4_addr = ip_dst; + memset(pending_pkt.mac_addr, 0, 6); + fill_ethhdr (pending_pkt.eth_frame, htons(ETHERTYPE_IP), + get_mac_address(), null_mac_addr); + memcpy(&pending_pkt.eth_frame[sizeof(struct ethhdr)], + buffer, len); + pending_pkt.eth_len = len + sizeof(struct ethhdr); + + set_timer(TICKS_SEC); + do { + receive_ether(fd); + if (!arp_entry->eth_len) + break; + } while (get_timer() > 0); + + return 0; + } + + // Send the packet with the known MAC address + fill_ethhdr(arp_entry->eth_frame, htons(ETHERTYPE_IP), + get_mac_address(), mac_addr); + memcpy(&arp_entry->eth_frame[sizeof(struct ethhdr)], buffer, len); + return send_ether(fd, arp_entry->eth_frame, len + sizeof(struct ethhdr)); +} + +/** + * IPv4: Calculate UDP checksum. Places the result into the UDP-header. + * <p> + * Use this function after filling the UDP payload. + * + * @param ipv4_hdr Points to the place where IPv4-header starts. + */ + +static void +fill_udp_checksum(struct iphdr *ipv4_hdr) +{ + int i; + unsigned long checksum = 0; + struct iphdr ip_hdr; + char *ptr; + udp_hdr_t *udp_hdr; + + udp_hdr = (udp_hdr_t *) (ipv4_hdr + 1); + udp_hdr->uh_sum = 0; + + memset(&ip_hdr, 0, sizeof(struct iphdr)); + ip_hdr.ip_src = ipv4_hdr->ip_src; + ip_hdr.ip_dst = ipv4_hdr->ip_dst; + ip_hdr.ip_len = udp_hdr->uh_ulen; + ip_hdr.ip_p = ipv4_hdr->ip_p; + + ptr = (char*) udp_hdr; + for (i = 0; i < udp_hdr->uh_ulen; i+=2) + checksum += *((uint16_t*) &ptr[i]); + + ptr = (char*) &ip_hdr; + for (i = 0; i < sizeof(struct iphdr); i+=2) + checksum += *((uint16_t*) &ptr[i]); + + checksum = (checksum >> 16) + (checksum & 0xffff); + checksum += (checksum >> 16); + udp_hdr->uh_sum = ~checksum; + + /* As per RFC 768, if the computed checksum is zero, + * it is transmitted as all ones (the equivalent in + * one's complement arithmetic). + */ + if (udp_hdr->uh_sum == 0) + udp_hdr->uh_sum = ~udp_hdr->uh_sum; +} + +/** + * IPv4: Calculates checksum for IP header. + * + * @param packet Points to the IP-header + * @param words Size of the packet in words incl. IP-header and data. + * @return Checksum + * @see iphdr + */ +static unsigned short +checksum(unsigned short * packet, int words) +{ + unsigned long checksum; + + for (checksum = 0; words > 0; words--) + checksum += *packet++; + checksum = (checksum >> 16) + (checksum & 0xffff); + checksum += (checksum >> 16); + + return ~checksum; +} + +static arp_entry_t* +lookup_mac_addr(uint32_t ipv4_addr) +{ + unsigned int i; + + for(i=arp_consumer; i != arp_producer; i = ((i+1)%ARP_ENTRIES) ) { + if(arp_table[i].ipv4_addr == ipv4_addr) + return &arp_table[i]; + } + return 0; +} + + +/** + * ARP: Sends an ARP-request package. + * For given IPv4 retrieves MAC via ARP (makes several attempts) + * + * @param fd socket fd + * @param dest_ip IP of the host which MAC should be obtained + */ +static void +arp_send_request(int fd, uint32_t dest_ip) +{ + arp_entry_t *arp_entry = &arp_table[arp_producer]; + + memset(arp_entry->eth_frame, 0, sizeof(struct ethhdr) + sizeof(struct arphdr)); + fill_arphdr(&arp_entry->eth_frame[sizeof(struct ethhdr)], ARP_REQUEST, + get_mac_address(), own_ip, broadcast_mac, dest_ip); + fill_ethhdr(arp_entry->eth_frame, ETHERTYPE_ARP, + get_mac_address(), broadcast_mac); + + send_ether(fd, arp_entry->eth_frame, + sizeof(struct ethhdr) + sizeof(struct arphdr)); +} + +/** + * ARP: Sends an ARP-reply package. + * This package is used to serve foreign requests (in case IP in + * foreign request matches our host IP). + * + * @param fd socket fd + * @param src_ip requester IP address (foreign IP) + * @param src_mac requester MAC address (foreign MAC) + */ +static void +arp_send_reply(int fd, uint32_t src_ip, uint8_t * src_mac) +{ + arp_entry_t *arp_entry = &arp_table[arp_producer]; + + memset(arp_entry->eth_frame, 0, sizeof(struct ethhdr) + sizeof(struct arphdr)); + fill_ethhdr(arp_entry->eth_frame, ETHERTYPE_ARP, + get_mac_address(), src_mac); + fill_arphdr(&arp_entry->eth_frame[sizeof(struct ethhdr)], ARP_REPLY, + get_mac_address(), own_ip, src_mac, src_ip); + + send_ether(fd, arp_entry->eth_frame, + sizeof(struct ethhdr) + sizeof(struct arphdr)); +} + +/** + * ARP: Creates ARP package. Places ARP-header in a packet and fills it + * with corresponding information. + * <p> + * Use this function with similar functions for other network layers + * (fill_ethhdr). + * + * @param packet Points to the place where ARP-header must be placed. + * @param opcode Identifies is it request (ARP_REQUEST) + * or reply (ARP_REPLY) package. + * @param src_mac sender MAC address + * @param src_ip sender IP address + * @param dest_mac receiver MAC address + * @param dest_ip receiver IP address + * @see arphdr + * @see fill_ethhdr + */ +static void +fill_arphdr(uint8_t * packet, uint8_t opcode, + const uint8_t * src_mac, uint32_t src_ip, + const uint8_t * dest_mac, uint32_t dest_ip) +{ + struct arphdr * arph = (struct arphdr *) packet; + + arph -> hw_type = htons(1); + arph -> proto_type = htons(ETHERTYPE_IP); + arph -> hw_len = 6; + arph -> proto_len = 4; + arph -> opcode = htons(opcode); + + memcpy(arph->src_mac, src_mac, 6); + arph->src_ip = htonl(src_ip); + memcpy(arph->dest_mac, dest_mac, 6); + arph->dest_ip = htonl(dest_ip); +} + +/** + * ARP: Handles ARP-messages according to Receive-handle diagram. + * Updates arp_table for outstanding ARP requests (see arp_getmac). + * + * @param fd socket fd + * @param packet ARP-packet to be handled + * @param packetsize length of the packet + * @return ZERO - packet handled successfully; + * NON ZERO - packet was not handled (e.g. bad format) + * @see arp_getmac + * @see receive_ether + * @see arphdr + */ +int8_t +handle_arp(int fd, uint8_t * packet, int32_t packetsize) +{ + struct arphdr * arph = (struct arphdr *) packet; + + if (packetsize < sizeof(struct arphdr)) + return -1; // Packet is too small + + if (arph -> hw_type != htons(1) || arph -> proto_type != htons(ETHERTYPE_IP)) + return -1; // Unknown hardware or unsupported protocol + + if (arph -> dest_ip != htonl(own_ip)) + return -1; // receiver IP doesn't match our IP + + switch(htons(arph -> opcode)) { + case ARP_REQUEST: + // foreign request + if(own_ip != 0) + arp_send_reply(fd, htonl(arph->src_ip), arph -> src_mac); + return 0; // no error + case ARP_REPLY: { + unsigned int i; + // if it is not for us -> return immediately + if(memcmp(get_mac_address(), arph->dest_mac, 6)) { + return 0; // no error + } + + if(arph->src_ip == 0) { + // we are not interested for a MAC address if + // the IPv4 address is 0.0.0.0 or ff.ff.ff.ff + return -1; + } + + // now let's find the corresponding entry in the ARP table + + for(i=arp_consumer; i != arp_producer; i = ((i+1)%ARP_ENTRIES) ) { + if(arp_table[i].ipv4_addr == arph->src_ip) + break; + } + if(i == arp_producer || memcmp(arp_table[i].mac_addr, null_mac_addr, 6) != 0) { + // we have not asked to resolve this IPv4 address ! + return -1; + } + + memcpy(arp_table[i].mac_addr, arph->src_mac, 6); + + // do we have something to send + if (arp_table[i].pkt_pending) { + struct ethhdr * ethh = (struct ethhdr *) pending_pkt.eth_frame; + memcpy(ethh -> dest_mac, arp_table[i].mac_addr, 6); + + send_ether(fd, pending_pkt.eth_frame, pending_pkt.eth_len); + pending_pkt.pkt_pending = 0; + arp_table[i].eth_len = 0; + } + return 0; // no error + } + default: + break; + } + return -1; // Invalid message type +} + +/** + * ICMP: Send an ICMP Echo request to destination IPv4 address. + * This function does also set a global variable to the + * destination IPv4 address. If there is an ICMP Echo Reply + * received later then the variable is set back to 0. + * In other words, reading a value of 0 form this variable + * means that an answer to the request has been arrived. + * + * @param fd socket descriptor + * @param _ping_dst_ip destination IPv4 address + */ +void +ping_ipv4(int fd, uint32_t _ping_dst_ip) +{ + unsigned char packet[sizeof(struct iphdr) + sizeof(struct icmphdr)]; + struct icmphdr *icmp; + + ping_dst_ip = _ping_dst_ip; + + if(ping_dst_ip == 0) + return; + + fill_iphdr(packet, sizeof(struct iphdr) + sizeof(struct icmphdr), IPTYPE_ICMP, + 0, ping_dst_ip); + icmp = (struct icmphdr *) (packet + sizeof(struct iphdr)); + icmp->type = ICMP_ECHO_REQUEST; + icmp->code = 0; + icmp->checksum = 0; + icmp->options.echo.id = 0xd476; + icmp->options.echo.seq = 1; + + memset(icmp->payload.data, '*', sizeof(icmp->payload.data)); + + icmp->checksum = + checksum((unsigned short *) icmp, sizeof(struct icmphdr) >> 1); + send_ipv4(fd, packet, sizeof(struct iphdr) + sizeof(struct icmphdr)); +} + +/** + * ICMP: Return host IPv4 address that we are waiting for a + * ICMP Echo reply message. If this value is 0 then we have + * received an reply. + * + * @return ping_dst_ip host IPv4 address + */ +uint32_t +pong_ipv4(void) +{ + return ping_dst_ip; +} + +/** + * ICMP: Handles ICMP-packets according to Receive-handle diagram. + * + * @param fd socket fd + * @param icmp_packet ICMP-packet to be handled + * @param packetsize Length of the packet + * @return ZERO - packet handled successfully; + * NON ZERO - packet was not handled (e.g. bad format) + * @see handle_ipv4 + */ +static int8_t +handle_icmp(int fd, struct iphdr * iph, uint8_t * packet, int32_t packetsize) +{ + struct icmphdr *icmp = (struct icmphdr *) packet; + + switch(icmp->type) { + case ICMP_ECHO_REPLY: + if (icmp->options.echo.id != 0xd476) + return -1; + if (icmp->options.echo.seq != 1) + return -1; + if(ping_dst_ip != iph->ip_src + || ping_dst_ip == 0) + return -1; + ping_dst_ip = 0; + break; + case ICMP_DST_UNREACHABLE: { + // We've got Destination Unreachable msg + // Inform corresponding upper network layers + struct iphdr * bad_iph = (struct iphdr * ) &icmp->payload; + + switch(bad_iph->ip_p) { + case IPTYPE_TCP: + handle_tcp_dun((uint8_t *) (bad_iph + 1), packetsize + - sizeof(struct icmphdr) + - sizeof(struct iphdr), icmp->code); + break; + case IPTYPE_UDP: + handle_udp_dun((uint8_t *) (bad_iph + 1), packetsize + - sizeof(struct icmphdr) + - sizeof(struct iphdr), icmp->code); + break; + } + break; + } + case ICMP_SRC_QUENCH: + break; + case ICMP_REDIRECT: + break; + case ICMP_ECHO_REQUEST: { + // We've got an Echo Request - answer with Echo Replay msg + unsigned char reply_packet[sizeof(struct iphdr) + packetsize]; + struct icmphdr *reply_icmph; + + fill_iphdr(reply_packet, sizeof(struct iphdr) + packetsize, + IPTYPE_ICMP, 0, iph->ip_src); + + reply_icmph = (struct icmphdr *) &reply_packet[sizeof(struct iphdr)]; + memcpy(reply_icmph, packet, packetsize); + reply_icmph -> type = ICMP_ECHO_REPLY; + reply_icmph -> checksum = 0; + reply_icmph->checksum = checksum((unsigned short *) reply_icmph, + sizeof(struct icmphdr) >> 1); + + send_ipv4(fd, reply_packet, sizeof(struct iphdr) + packetsize); + break; + } + case ICMP_TIME_EXCEEDED: + break; + case ICMP_PARAMETER_PROBLEM: + break; + case ICMP_TIMESTAMP_REQUEST: + break; + case ICMP_TIMESTAMP_REPLY: + break; + case ICMP_INFORMATION_REQUEST: + break; + case ICMP_INFORMATION_REPLY: + break; + } + return 0; +} diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/ipv4.h b/qemu/roms/SLOF/clients/net-snk/app/netlib/ipv4.h new file mode 100644 index 000000000..eb719f8b2 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/ipv4.h @@ -0,0 +1,96 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + + +#ifndef _IPV4_H_ +#define _IPV4_H_ + +#include <stdint.h> + +#define IPTYPE_ICMP 1 + +/** \struct iphdr + * A header for IP-packets. + * For more information see RFC 791. + */ +struct iphdr { + uint8_t ip_hlv; /**< Header length and version of the header */ + uint8_t ip_tos; /**< Type of Service */ + uint16_t ip_len; /**< Length in octets, inlc. this header and data */ + uint16_t ip_id; /**< ID is used to aid in assembling framents */ + uint16_t ip_off; /**< Info about fragmentation (control, offset) */ + uint8_t ip_ttl; /**< Time to Live */ + uint8_t ip_p; /**< Next level protocol type */ + uint16_t ip_sum; /**< Header checksum */ + uint32_t ip_src; /**< Source IP address */ + uint32_t ip_dst; /**< Destination IP address */ +}; +typedef struct iphdr ipv4_hdr_t; + +/* ICMP Error Codes */ +#define ICMP_NET_UNREACHABLE 0 +#define ICMP_HOST_UNREACHABLE 1 +#define ICMP_PROTOCOL_UNREACHABLE 2 +#define ICMP_PORT_UNREACHABLE 3 +#define ICMP_FRAGMENTATION_NEEDED 4 +#define ICMP_SOURCE_ROUTE_FAILED 5 + +/** \struct arphdr + * A header for ARP-messages, retains info about HW and proto addresses. + * For more information see RFC 826. + */ +struct arphdr { + uint16_t hw_type; /**< HW address space (1 for Ethernet) */ + uint16_t proto_type; /**< Protocol address space */ + uint8_t hw_len; /**< Byte length of each HW address */ + uint8_t proto_len; /**< Byte length of each proto address */ + uint16_t opcode; /**< Identifies is it request (1) or reply (2) */ + uint8_t src_mac[6]; /**< HW address of sender of this packet */ + uint32_t src_ip; /**< Proto address of sender of this packet */ + uint8_t dest_mac[6]; /**< HW address of target of this packet */ + uint32_t dest_ip; /**< Proto address of target of this packet */ +} __attribute((packed)); + +/*>>>>>>>>>>>>> Initialization of the IPv4 network layer. <<<<<<<<<<<<<*/ +extern void set_ipv4_address(uint32_t own_ip); +extern uint32_t get_ipv4_address(void); +extern void set_ipv4_multicast(uint32_t multicast_ip); +extern uint32_t get_ipv4_multicast(void); +extern void set_ipv4_router(uint32_t router_ip); +extern uint32_t get_ipv4_router(void); +extern void set_ipv4_netmask(uint32_t subnet_mask); +extern uint32_t get_ipv4_netmask(void); + +extern int (*send_ip) (int fd, void *, int); + +/* fills ip header */ +extern void fill_iphdr(uint8_t * packet, uint16_t packetsize, + uint8_t ip_proto, uint32_t ip_src, uint32_t ip_dst); + +/* Send a IPv4 packet. Adding the Ethernet-Header and resolving the + * MAC address is done transparent in the background if necessary. + */ +extern int send_ipv4(int fd, void* buffer, int len); + +/* Sends an ICMP Echo request to destination IPv4 address */ +extern void ping_ipv4(int fd, uint32_t _ping_dst_ip); + +/* Returns host IPv4 address that we are waiting for a response */ +extern uint32_t pong_ipv4(void); + +/* Handles IPv4-packets that are detected by receive_ether. */ +extern int8_t handle_ipv4(int fd, uint8_t * packet, int32_t packetsize); + +/* Handles ARP-packets that are detected by receive_ether. */ +extern int8_t handle_arp(int fd, uint8_t * packet, int32_t packetsize); + +#endif diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/ipv6.c b/qemu/roms/SLOF/clients/net-snk/app/netlib/ipv6.c new file mode 100644 index 000000000..0cb0a2e7b --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/ipv6.c @@ -0,0 +1,768 @@ +/****************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <time.h> +#include <ctype.h> +#include <sys/socket.h> +#include <netlib/ethernet.h> +#include <netlib/ipv6.h> +#include <netlib/icmpv6.h> +#include <netlib/ndp.h> +#include <netlib/udp.h> + +#undef IPV6_DEBUG +//#define IPV6_DEBUG +#ifdef IPV6_DEBUG +#define dprintf(_x ...) do { printf(_x); } while (0) +#else +#define dprintf(_x ...) +#endif + +/****************************** PROTOTYPES *******************************/ +int8_t ip6addr_add (struct ip6addr_list_entry *new_address); +static void ipv6_init(int fd); +static int ip6_is_multicast (ip6_addr_t * ip); + +/****************************** LOCAL VARIABLES **************************/ + +/* Own IPv6 address */ +static struct ip6addr_list_entry *own_ip6; + +/* Null IPv6 address */ +static ip6_addr_t null_ip6; + +/* helper variables */ +static uint8_t null_mac[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + +/****************************** IMPLEMENTATION ***************************/ + +/** + * IPv6: Set the own IPv6 address. + * + * @param fd Socket descriptor + * @param _own_ip client IPv6 address (e.g. ::1) + */ +void +set_ipv6_address (int fd, ip6_addr_t *_own_ip6) +{ + own_ip6 = malloc (sizeof(struct ip6addr_list_entry)); + + /* If no address was passed as a parameter generate a link-local + * address from our MAC address.*/ + if (_own_ip6 == NULL) + memcpy(&(own_ip6->addr.addr), + ip6_create_ll_address(get_mac_address()), + IPV6_ADDR_LENGTH); + else + memcpy (&(own_ip6->addr.addr), _own_ip6, 16); + + /* Add to our list of IPv6 addresses */ + ip6addr_add (own_ip6); + + ipv6_init(fd); +} + +/** + * IPv6: Get pointer to own IPv6 address. + * + * @return pointer to client IPv6 address (e.g. ::1) + */ +ip6_addr_t * +get_ipv6_address (void) +{ + return (ip6_addr_t *) &(own_ip6->addr); +} + +/** + * IPv6: Search for IPv6 address in list + * + * @return 0 - IPv6 address is not in list + * 1 - IPv6 address is in list + */ +static int8_t +find_ip6addr (ip6_addr_t *ip) +{ + struct ip6addr_list_entry *n = NULL; + + if (ip == NULL) + return 0; + + for (n = first_ip6; n != NULL ; n=n->next) + if (ip6_cmp (&(n->addr), ip)) + return 1; /* IPv6 address is in our list*/ + + return 0; /* not one of our IPv6 addresses*/ +} + +/** + * NET: Handles IPv6-packets + * + * @param fd - Socket descriptor + * @param ip6_packet - Pointer to IPv6 header + * @param packetsize - Size of Ipv6 packet + * @return ERROR - -1 if packet is too small or unknown protocol + * return value of handle_udp + * + * @see handle_udp + * @see ip6hdr + */ +int8_t +handle_ipv6 (int fd, uint8_t * ip6_packet, int32_t packetsize) +{ + + struct ip6hdr *ip6 = NULL; + ip6 = (struct ip6hdr *) ip6_packet; + + /* Only handle packets which are for us */ + if (! find_ip6addr(&(ip6->dst))) + return -1; + + if (packetsize < sizeof(struct ip6hdr)) + return -1; // packet is too small + + switch (ip6->nh) { + case IPTYPE_UDP: + return handle_udp (fd, ip6_packet + sizeof (struct ip6hdr), + ip6->pl); + case IPTYPE_ICMPV6: + return handle_icmpv6 (fd, (struct ethhdr *) ip6_packet - sizeof(struct ethhdr), + ip6_packet); + } + + return -1; // unknown protocol +} + + /** + * NET: Creates IPv6-packet. Places IPv6-header in a packet and fills it + * with corresponding information. + * <p> + * Use this function with similar functions for other network layers + * (fill_ethhdr, fill_udphdr, fill_dnshdr, fill_btphdr). + * + * @param packet Points to the place where IPv6-header must be placed. + * @param packetsize Size of payload (i.e. excluding ethhdr and ip6hdr) + * @param ip_proto Type of the next level protocol (e.g. UDP). + * @param ip6_src Sender IPv6 address + * @param ip6_dst Receiver IPv6 address + * @see ip6hdr + * @see fill_iphdr + * @see fill_ethhdr + * @see fill_udphdr + * @see fill_dnshdr + * @see fill_btphdr + */ +void +fill_ip6hdr (uint8_t * packet, uint16_t packetsize, + uint8_t ip_proto, ip6_addr_t *ip6_src, ip6_addr_t *ip6_dst) +{ + + struct ip6hdr * ip6h = (struct ip6hdr *) packet; + + ip6h->ver_tc_fl = 6 << 28; // set version to 6 + ip6h->pl = packetsize; // IPv6 payload size + ip6h->nh = ip_proto; + ip6h->hl = 255; + memcpy (&(ip6h->src), ip6_src, IPV6_ADDR_LENGTH); + memcpy (&(ip6h->dst), ip6_dst, IPV6_ADDR_LENGTH); +} + +/** + * NET: For a given MAC calculates EUI64-Identifier. + * See RFC 4291 "IP Version 6 Addressing Architecture" + * + */ +uint64_t +mac2eui64 (const uint8_t *mac) +{ + uint8_t eui64id[8]; + uint64_t retid; + + memcpy (eui64id, mac, 3); + memcpy (eui64id + 5, mac + 3, 3); + eui64id[3] = 0xff; + eui64id[4] = 0xfe; + + memcpy(&retid, eui64id, 8); + return retid; +} + +/** + * NET: create link-local IPv6 address + * + * @param own_mac MAC of NIC + * @return ll_addr pointer to newly created link-local address + */ +ip6_addr_t * +ip6_create_ll_address (const uint8_t *own_mac) +{ + ip6_addr_t *ll_addr; + + ll_addr = malloc (sizeof (struct ip6addr_list_entry)); + memset (ll_addr, 0, IPV6_ADDR_LENGTH); + ll_addr->part.prefix |= IPV6_LL_PREFIX; + ll_addr->part.interface_id |= mac2eui64((uint8_t *) own_mac); + + return ll_addr; +} + +/* + * NET: check if we already have an address with the same prefix. + * @param struct ip6_addr_list_entry *ip6 + * @return true or false + */ +int8_t +unknown_prefix (ip6_addr_t *ip) +{ + struct ip6addr_list_entry *node; + + for( node = first_ip6; node != NULL; node=node->next ) + if( node->addr.part.prefix == ip->part.prefix ) + return 0; /* address is one of ours */ + + return 1; /* prefix not yet in our list */ +} + +/* + * NET: Create empty element for prefix list and return a pointer to it; + * @return NULL - malloc failed + * ! NULL - pointer to new prefix_info + */ +struct prefix_info * +ip6_create_prefix_info () +{ + struct prefix_info *prfx_info; + + prfx_info = malloc (sizeof(struct prefix_info)); + if (!prfx_info) + return NULL; + + return prfx_info; +} + +/* + * NET: create a new IPv6 address with a given network prefix + * and add it to our IPv6 address list + * + * @param ip6_addr prefix (as received in RA) + * @return NULL - pointer to new ip6addr_list entry + */ +void * +ip6_prefix2addr (ip6_addr_t prefix) +{ + struct ip6addr_list_entry *new_address; + uint64_t interface_id; + + new_address = malloc (sizeof(struct ip6addr_list_entry)); + if( !new_address ) + return NULL; + + /* fill new addr struct */ + /* extract prefix from Router Advertisement */ + memcpy (&(new_address->addr.part.prefix), &prefix, 8 ); + + /* interface id is generated from MAC address */ + interface_id = mac2eui64 (get_mac_address()); + memcpy (&(new_address->addr.part.interface_id), &interface_id, 8); + + return new_address; +} + +/** + * NET: add new IPv6 adress to list + * + * @param ip6_addr *new_address + * @return 0 - passed pointer = NULL; + * 1 - ok + */ +int8_t +ip6addr_add (struct ip6addr_list_entry *new_address) +{ + struct ip6addr_list_entry *solicited_node; + + + if (new_address == NULL) + return 0; + + /* Don't add the same address twice */ + if (find_ip6addr (&(new_address->addr))) + return 0; + + /* If address is a unicast address, we also have to process packets + * for its solicited-node multicast address. + * See RFC 2373 - IP Version 6 Adressing Architecture */ + if (! ip6_is_multicast(&(new_address->addr))) { + + + solicited_node = malloc(sizeof(struct ip6addr_list_entry)); + if (! solicited_node) + return 0; + + solicited_node->addr.part.prefix = IPV6_SOLIC_NODE_PREFIX; + solicited_node->addr.part.interface_id = IPV6_SOLIC_NODE_IFACE_ID; + solicited_node->addr.addr[13] = new_address->addr.addr[13]; + solicited_node->addr.addr[14] = new_address->addr.addr[14]; + solicited_node->addr.addr[15] = new_address->addr.addr[15]; + ip6addr_add (solicited_node); + } + + if (NULL == first_ip6) + first_ip6 = new_address; + last_ip6->next = new_address; + last_ip6 = new_address; + last_ip6->next = NULL; + + return 1; /* no error */ +} + +/** + * NET: Initialize IPv6 + * + * @param fd socket fd + */ +static void +ipv6_init (int fd) +{ + int i = 0; + + send_ip = &send_ipv6; + + /* Address configuration parameters */ + ip6_state.managed_mode = 0; + + /* Null IPv6 address */ + null_ip6.part.prefix = 0; + null_ip6.part.interface_id = 0; + + /* Multicast addresses */ + all_nodes_ll.addr.part.prefix = 0xff02000000000000; + all_nodes_ll.addr.part.interface_id = 1; + all_dhcpv6_ll.addr.part.prefix = 0xff02000000000000ULL; + all_dhcpv6_ll.addr.part.interface_id = 0x10002ULL; + all_routers_ll.addr.part.prefix = 0xff02000000000000; + all_routers_ll.addr.part.interface_id = 2; + + ip6addr_add(&all_nodes_ll); + /* ... */ + + /* Router list */ + first_router = NULL; + last_router = first_router; + + /* Init Neighbour cache */ + first_neighbor = NULL; + last_neighbor = first_neighbor; + + send_router_solicitation (fd); + for(i=0; i < 4 && !is_ra_received(); i++) { + set_timer(TICKS_SEC); + do { + receive_ether(fd); + if (is_ra_received()) + break; + } while (get_timer() > 0); + } +} + +/** + * NET: compare IPv6 adresses + * + * @param ip6_addr ip_1 + * @param ip6_addr ip_2 + */ +int8_t +ip6_cmp (ip6_addr_t *ip_1, ip6_addr_t *ip_2) +{ + return ((int8_t) !memcmp( &(ip_1->addr[0]), &(ip_2->addr[0]), + IPV6_ADDR_LENGTH )); +} + +/** + * NET: Calculate checksum over IPv6 header and upper-layer protocol + * (e.g. UDP or ICMPv6) + * + * @param *ip - pointer to IPv6 address + * @return true or false + */ +int +ip6_is_multicast (ip6_addr_t * ip) +{ + uint8_t mc = 0xFF; + return ! memcmp(&ip->addr[0], &mc, 1); +} + +/** + * NET: Generate multicast MAC address from IPv6 address + * (e.g. UDP or ICMPv6) + * + * @param *ip - pointer to IPv6 address + * @return pointer to Multicast MAC address + */ +static uint8_t * +ip6_to_multicast_mac (ip6_addr_t * ip) +{ + uint8_t *mc_mac; + + mc_mac = malloc(ETH_ALEN); + if (!mc_mac) + return NULL; + + mc_mac[0] = 0x33; + mc_mac[1] = 0x33; + memcpy (mc_mac+2, (uint8_t *) &(ip->addr)+12, 4); + + return mc_mac; +} + +/** + * NET: calculate checksum over IPv6 header and upper-layer protocol + * (e.g. UDP or ICMPv6) + * + * @param struct ip6hdr *ip6h - pointer to IPv6 header + * @param unsigned short *packet - pointer to header of upper-layer + * protocol + * @param int words - number of words (as in 2 bytes) + * starting from *packet + * @return checksum + */ +static unsigned short +ip6_checksum (struct ip6hdr *ip6h, unsigned short *packet, int words) +{ + int i=0; + unsigned long checksum; + struct ip6hdr pseudo_ip6h; + unsigned short *pip6h; + + memcpy (&pseudo_ip6h, ip6h, sizeof(struct ip6hdr)); + pseudo_ip6h.hl = ip6h->nh; + pseudo_ip6h.ver_tc_fl = 0; + pseudo_ip6h.nh = 0; + pip6h = (unsigned short *) &pseudo_ip6h; + + for (checksum = 0; words > 0; words--) { + checksum += *packet++; + i++; + } + + for (i = 0; i < 20; i++) { + checksum += *pip6h++; + } + + checksum = (checksum >> 16) + (checksum & 0xffff); + checksum += (checksum >> 16); + + return ~checksum; +} + +/** + * NET: Handles IPv6-packets + * + * @param fd socket fd + * @param ip6_packet Pointer to IPv6 header in packet + * @param packetsize Size of IPv6 packet + * @return -1 == ERRROR + * return of handle_udp() or handle_icmp6() + * + * @see receive_ether + * @see ip6hdr + */ +int +send_ipv6 (int fd, void* buffer, int len) +{ + struct neighbor *n; + struct ip6hdr *ip6h; + struct udphdr *udph; + struct icmp6hdr *icmp6h; + ip6_addr_t ip_dst; + uint8_t *mac_addr, mac[6]; + + mac_addr = mac; + + ip6h = (struct ip6hdr *) buffer; + udph = (struct udphdr *) ((uint8_t *) ip6h + sizeof (struct ip6hdr)); + icmp6h = (struct icmp6hdr *) ((uint8_t *) ip6h + sizeof (struct ip6hdr)); + + memcpy(&ip_dst, &ip6h->dst, 16); + + if(len + sizeof(struct ethhdr) > 1500) + return -1; + + if ( ip6_cmp (&ip6h->src, &null_ip6)) + memcpy (&(ip6h->src), get_ipv6_address(), IPV6_ADDR_LENGTH); + + if (ip6h->nh == 17) {//UDP + udph->uh_sum = ip6_checksum (ip6h, (unsigned short *) udph , + ip6h->pl >> 1); + /* As per RFC 768, if the computed checksum is zero, + * it is transmitted as all ones (the equivalent in + * one's complement arithmetic). + */ + if (udph->uh_sum == 0) + udph->uh_sum = ~udph->uh_sum; + } + else if (ip6h->nh == 0x3a) //ICMPv6 + icmp6h->checksum = ip6_checksum (ip6h, + (unsigned short *) icmp6h, + ip6h->pl >> 1); + + n = find_neighbor (&ip_dst); + + // If packet is a neighbor solicitation + if (icmp6h->type == ICMPV6_NEIGHBOUR_SOLICITATION) { + mac_addr = ip6_to_multicast_mac (&ip_dst); + fill_ethhdr( buffer-sizeof(struct ethhdr), htons(ETHERTYPE_IPv6), + get_mac_address(), + mac_addr); + } + + // If address is a multicast address, create a proper mac address + else if (ip6_is_multicast (&ip_dst)) { + mac_addr = ip6_to_multicast_mac (&ip_dst); + } + else { + // Check if the MAC address is already cached + if (n) { + if (memcmp(n->mac, null_mac, ETH_ALEN) != 0) + memcpy (mac_addr, &(n->mac), ETH_ALEN); /* found it */ + } else { + mac_addr = null_mac; + n = malloc(sizeof(struct neighbor)); + memcpy(&(n->ip.addr[0]), &ip_dst, 16); + n->status = NB_PROBE; + n->times_asked += 1; + neighbor_add(n); + } + + if (! memcmp (mac_addr, &null_mac, 6)) { + if (n->eth_len == 0) { + send_neighbour_solicitation (fd, &ip_dst); + + // Store the packet until we know the MAC address + memset(n->eth_frame, 0, 1500); + fill_ethhdr (n->eth_frame, + htons(ETHERTYPE_IPv6), + get_mac_address(), + mac_addr); + memcpy (&(n->eth_frame[sizeof(struct ethhdr)]), + buffer, len); + n->eth_len = len; + set_timer(TICKS_SEC); + do { + receive_ether(fd); + } while (get_timer() > 0); + } + } + } + + fill_ethhdr (n->eth_frame, htons(ETHERTYPE_IPv6), get_mac_address(), + mac_addr); + memcpy (&(n->eth_frame[sizeof(struct ethhdr)]), buffer, len); + return send_ether (fd, n->eth_frame, len + sizeof(struct ethhdr)); +} + +static int +check_colons(const char *str) +{ + char *pch, *prv; + int col = 0; + int dcol = 0; + + dprintf("str : %s\n",str); + pch = strchr(str, ':'); + while(pch != NULL){ + prv = pch; + pch = strchr(pch+1, ':'); + if((pch-prv) != 1) { + col++; + } else { + col--; /* Its part of double colon */ + dcol++; + } + } + + dprintf("The number of col : %d \n",col); + dprintf("The number of dcol : %d \n",dcol); + + if((dcol > 1) || /* Cannot have 2 "::" */ + ((dcol == 1) && (col > 5)) || /* Too many ':'s */ + ((dcol == 0) && (col != 7)) ) { /* Too few ':'s */ + dprintf(" exiting for check_colons \n"); + return 0; + } + + return (col+dcol); +} + +static int +ipv6str_to_bytes(const char *str, char *ip) +{ + char block[5]; + int res; + char *pos; + uint32_t cnt = 0, len; + + dprintf("str : %s \n",str); + + while (*str != 0) { + if (cnt > 15 || !isxdigit(*str)){ + return 0; + } + if ((pos = strchr(str, ':')) != NULL) { + len = (int16_t) (pos - str); + dprintf("\t len is : %d \n",len); + if (len > 4) + return 0; + strncpy(block, str, len); + block[len] = 0; + dprintf("\t str : %s \n",str); + dprintf("\t block : %s \n",block); + str += len; + } else { + strncpy(block, str, 4); + block[4] = 0; + dprintf("\t str : %s \n",str); + dprintf("\t block : %s \n",block); + str += strlen(block); + } + res = strtol(block, NULL, 16); + dprintf("\t res : %x \n",res); + if ((res > 0xFFFF) || (res < 0)) + return 0; + ip[cnt++] = (res & 0xFF00) >> 8; + ip[cnt++] = (res & 0x00FF); + if (*str == ':'){ + str++; + } + } + + dprintf("cnt : %d\n",cnt); + return cnt; +} + +int str_to_ipv6(const char *str, uint8_t *ip) +{ + int i, k; + uint16_t len; + char *ptr; + char tmp[30], buf[16]; + + memset(ip,0,16); + + if(!check_colons(str)) + return 0; + + if ((ptr = strstr(str, "::")) != NULL) { + /* Handle the ::1 IPv6 loopback */ + if(!strcmp(str,"::1")) { + ip[15] = 1; + return 16; + } + len = (ptr-str); + dprintf(" len : %d \n",len); + if (len >= sizeof(tmp)) + return 0; + strncpy(tmp, str, len); + tmp[len] = 0; + ptr += 2; + + i = ipv6str_to_bytes(ptr, buf); + if(i == 0) + return i; + + #if defined(ARGS_DEBUG) + int j; + dprintf("=========== bottom part i : %d \n",i); + for(j=0; j<i; j++) + dprintf("%02x \t",buf[j]); + #endif + + /* Copy the bottom part i.e bytes following "::" */ + memcpy(ip+(16-i), buf, i); + + k = ipv6str_to_bytes(tmp, buf); + if(k == 0) + return k; + + #if defined(ARGS_DEBUG) + dprintf("=========== top part k : %d \n",k); + for(j=0; j<k; j++) + printf("%02x \t",buf[j]); + #endif + + /* Copy the top part i.e bytes before "::" */ + memcpy(ip, buf, k); + #if defined(ARGS_DEBUG) + dprintf("\n"); + for(j=0; j<16; j++) + dprintf("%02x \t",ip[j]); + #endif + + } else { + i = ipv6str_to_bytes(str, (char *)ip); + } + return i; +} + +void ipv6_to_str(const uint8_t *ip, char *str) +{ + int i, len; + uint8_t byte_even, byte_odd; + char *consec_zero, *strptr; + + *str = 0; + for (i = 0; i < 16; i+=2) { + byte_even = ip[i]; + byte_odd = ip[i+1]; + if (byte_even) + sprintf(str, "%s%x%02x", str, byte_even, byte_odd); + else if (byte_odd) + sprintf(str, "%s%x", str, byte_odd); + else + strcat(str, "0"); + if (i != 14) + strcat(str, ":"); + } + strptr = str; + do { + consec_zero = strstr(strptr, "0:0:"); + if (consec_zero) { + len = consec_zero - strptr; + if (!len) + break; + else if (strptr[len-1] == ':') + break; + else + strptr = consec_zero + 2; + } + } while (consec_zero); + if (consec_zero) { + len = consec_zero - str; + str[len] = 0; + if (len) + strcat(str, ":"); + else + strcat(str, "::"); + strptr = consec_zero + 4; + while (*strptr) { + if (!strncmp(strptr, "0:", 2)) + strptr += 2; + else + break; + } + strcat(str, strptr); + if (!strcmp(str, "::0")) + strcpy(str, "::"); + } +} diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/ipv6.h b/qemu/roms/SLOF/clients/net-snk/app/netlib/ipv6.h new file mode 100644 index 000000000..b496364f3 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/ipv6.h @@ -0,0 +1,196 @@ +/****************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef _IPV6_H_ +#define _IPV6_H_ + +#include <stdint.h> +#include <netlib/ethernet.h> + +#define __IPV6_DEBUG__ + +#ifdef __IPV6_DEBUG__ +#define IPV6_DEBUG_PRINT(format, ...) do { printf(format, ## __VA_ARGS__); } while (0) +#else +#define IPV6_DEBUG_PRINT(format, ...) +#endif + +#define IPV6_ADDR_LENGTH 16 /* Size of IPv6 adress in bytes */ +#define IPV6_LL_PREFIX 0xFE80000000000000ULL +#define IPV6_SOLIC_NODE_PREFIX 0xFF02000000000000ULL +#define IPV6_SOLIC_NODE_IFACE_ID 0x00000001FF000000ULL + +/** + * An IPv6 Address + */ +typedef union { + uint8_t addr[IPV6_ADDR_LENGTH]; + struct { + uint64_t prefix; + uint64_t interface_id; + } part; +} ip6_addr_t; + +typedef struct { + uint8_t type; + uint8_t pad[7]; + union { + ip6_addr_t v6; + char v4[4]; + } addr; +} netaddr_t; + +/** \struct prefix_info + * + * List of Prefixes we have adresses from + * Used for internal purposes, information derived from prefix option + * in Router Advertisements + * See RFC 4861 section 4.6.2 + */ +struct prefix_info { + uint64_t prefix; + uint8_t on_link:1, /* When set prefix can be used for on-link + * determination */ + autoconf:1, /* Prefix can be used for stateless address + * configuration */ + reserved1:6; + uint32_t valid_lifetime; /* Time until prefix expires */ + uint32_t preferred_lifetime; /* Time until prefix becomes deprecated */ + uint32_t start_time; /* Time when received */ + uint32_t reserved2; + struct prefix_info *next; +}; + + +/* List of IPv6 addresses */ +struct ip6addr_list_entry { + ip6_addr_t addr; + struct prefix_info prfx_info; + struct ip6addr_list_entry *next; +}; + +/** \struct ip6hdr + * A header for IPv6 packets. + * For more information see RFC 2460 + */ +struct ip6hdr { + uint32_t ver_tc_fl; /**< Version, Traffic class, Flow label */ + uint16_t pl; /**< Payload length */ + uint8_t nh; /**< Next header */ + uint8_t hl; /**< Hop limit */ + ip6_addr_t src; /**< IPv6 source address */ + ip6_addr_t dst; /**< IPv6 destination address */ +} __attribute((packed)); + +/** \struct packeth + * Struct with pointers to headers within a packet + */ +struct packeth { + struct ethhdr *ethh; + struct ip6hdr *ip6h; + struct icmp6hdr *icmp6h; + struct udphdr *udph; + /* ... */ +}; + +/** \struct parseip6_state + * Stores information about state of IPv6 address parser + */ +struct parseip6_state { + char *lookahead; + char *ptr; + const char *addr; + int state; + int s1ctr; + int s2ctr; + int blocknr; + int zeroblocks; + int i; + int done; + int errorcode; +}; + +/** \struct ip6_config + * Stores flags wheter we use Stateless- or Stateful Autoconfiguration or DHCPv6 + */ +struct ip6_config { + uint8_t managed_mode:1, + other_config:1, + reserved:6; +} ip6_state; + +/******************** VARIABLES **********************************************/ +/* Function pointer send_ip. Points either to send_ipv4() or send_ipv6() */ +extern int (*send_ip) (int fd, void *, int); + +/* IPv6 link-local multicast addresses */ +struct ip6addr_list_entry all_routers_ll; // Routers +struct ip6addr_list_entry all_dhcpv6_ll; // DHCPv6 servers +struct ip6addr_list_entry all_nodes_ll; // All IPv6 nodes + +/* List of Ipv6 Addresses */ +struct ip6addr_list_entry *first_ip6; +struct ip6addr_list_entry *last_ip6; + +/* Neighbor cache */ +struct neighbor *first_neighbor; +struct neighbor *last_neighbor; + +/* Router list */ +struct router *first_router; +struct router *last_router; + +/******************** FUNCTIONS *********************************************/ +/* Handles IPv6-packets that are detected by receive_ether. */ +int8_t handle_ipv6(int fd, uint8_t * ip6_packet, int32_t packetsize); + +/* Fill IPv6 header */ +void fill_ip6hdr(uint8_t * packet, uint16_t packetsize, + uint8_t ip_proto, ip6_addr_t *ip6_src, ip6_addr_t *ip6_dst); + +/* Set own IPv6 address */ +void set_ipv6_address(int fd, ip6_addr_t *own_ip6); + +/* Get own IPv6 address */ +ip6_addr_t *get_ipv6_address(void); + +/* Create link-local address from a given Mac Address */ +ip6_addr_t * ip6_create_ll_address (const uint8_t *own_mac); + +/* For a given MAC calculates EUI64-Identifier.*/ +uint64_t mac2eui64 (const uint8_t *mac); + +/* Create empty element for prefix list and return a pointer to it */ +struct prefix_info * ip6_create_prefix_info(void); + +/* Create a new IPv6 address with a given network prefix + * and add it to our IPv6 address list */ +void * ip6_prefix2addr (ip6_addr_t prefix); + +/* Compare IPv6 adresses */ +int8_t ip6_cmp( ip6_addr_t *ip_1, ip6_addr_t *ip_2 ); + +/* Check if prefix is already in our list */ +int8_t unknown_prefix (ip6_addr_t *ip); + +/* Send IPv6 packet */ +int send_ipv6 (int fd, void* buffer, int len); + +/* Add IPv6 address to list */ +int8_t ip6addr_add (struct ip6addr_list_entry *new_address); + +/* Parse an IPv6 address */ +int parseip6(const char *addr, uint8_t *parsedaddr); +int str_to_ipv6(const char *str, uint8_t *ip); +void ipv6_to_str(const uint8_t *ip, char *str); + +#endif diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/ndp.c b/qemu/roms/SLOF/clients/net-snk/app/netlib/ndp.c new file mode 100644 index 000000000..ed9d61f4a --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/ndp.c @@ -0,0 +1,147 @@ +/****************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <netlib/ipv6.h> +#include <netlib/icmpv6.h> +#include <netlib/ndp.h> + +/* + * NET: add new router to list + * @param struct router nghb - new router + * @return true or false + */ +int8_t +router_add (struct router *nghb ) +{ + if (nghb == NULL) + return -1; + + if (first_router == NULL) { + first_router= nghb; + last_router = first_router; + last_router->next = NULL; + } else { + last_router->next = nghb; + last_router = nghb; + last_router->next = NULL; + } + return 1; /* no error */ +} + +/* + * NET: create a new router + * @param uint8_t *packet - received packet (Ethernet/IPv6/ICMPv6/ND_NghSlct) + * @param struct packeth - pointers to headers in packet + * @return pointer to new router + */ +void * +router_create (uint8_t *mac, ip6_addr_t *ip) +{ + struct router *new_router; + + new_router = malloc (sizeof(struct router)); + if( !new_router) { + return 0; + } + memset (new_router, 0, sizeof(struct router)); + + /* fill neighbor struct */ + memcpy (new_router->mac, mac, 6); + memcpy (&(new_router->ip.addr[0]), &(ip->addr[0]), IPV6_ADDR_LENGTH); + + return new_router; +} + +struct router * +find_router( ip6_addr_t *ip ) +{ + struct router *n = NULL; + + for (n = first_router; n != NULL ; n=n->next) + if (ip6_cmp (&(n->ip), ip)) + return n; /* router is already in list*/ + + return NULL; /* router is unknown */ +} + +/* + * NET: add new neighbor to list + * @param struct neighbor nghb - new neighbor + * @return true or false + */ +int8_t +neighbor_add (struct neighbor *nghb) +{ + if (nghb == NULL) + return -1; + + if (first_neighbor == NULL) { + first_neighbor = nghb; + last_neighbor = first_neighbor; + last_neighbor->next = NULL; + } else { + last_neighbor->next = nghb; + last_neighbor = nghb; + last_neighbor->next = NULL; + } + + return 1; /* no error */ +} + +/* + * NET: create a new neighbor + * @param uint8_t *packet - received packet (Ethernet/IPv6/ICMPv6/ND_NghSlct) + * @param struct packeth - pointers to headers in packet + * @return pointer - pointer to new neighbor + * NULL - malloc failed + */ +void * +neighbor_create (uint8_t *packet, struct packeth *headers) +{ + struct neighbor *new_neighbor; + + new_neighbor = malloc (sizeof(struct neighbor)); + if( !new_neighbor ) + return NULL; + + /* fill neighbor struct */ + memcpy (&(new_neighbor->mac), + &(headers->ethh->src_mac[0]), 6); + memcpy (&(new_neighbor->ip.addr), &(headers->ip6h->src), IPV6_ADDR_LENGTH); + new_neighbor->status = NB_INCOMPLETE; + + return new_neighbor; +} + +/** + * NET: Find neighbor with given IPv6 address in Neighbor Cache + * + * @param ip - Pointer to IPv6 address + * @return pointer - pointer to client IPv6 address (e.g. ::1) + * NULL - Neighbor not found + */ +struct neighbor * +find_neighbor (ip6_addr_t *ip) +{ + struct neighbor *n = NULL; + + for (n = first_neighbor; n != NULL ; n=n->next) { + if (ip6_cmp (&(n->ip), ip)) { + return n; /* neighbor is already in cache */ + } + } + + return NULL; /* neighbor is unknown */ +} diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/ndp.h b/qemu/roms/SLOF/clients/net-snk/app/netlib/ndp.h new file mode 100644 index 000000000..ee5235fe1 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/ndp.h @@ -0,0 +1,70 @@ +/****************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef _NDP_H_ +#define _NDP_H_ + +#include <netlib/ipv6.h> + +#define __NDP_DEBUG__ + +#ifdef __NDP_DEBUG__ +#define NDP_DEBUG_PRINT(format, ...) do { printf(format, ## __VA_ARGS__); } while (0) +#else +#define NDP_DEBUG_PRINT(format, ...) +#endif + +#define ND_OPTION_SOURCE_LL_ADDR 1 +#define ND_OPTION_TARGET_LL_ADDR 2 +#define ND_OPTION_PREFIX_INFO 3 +#define ND_OPTION_REDIRECT_HDR 4 +#define ND_OPTION_MTU 5 + +/* Default Router List */ +struct router { + uint8_t mac[6]; + ip6_addr_t ip; + uint32_t lifetime; + uint32_t reachable_time; + uint32_t retrans_timer; + struct router *next; +}; + +/* Neighbor cache */ +struct neighbor { + uint8_t mac[6]; + ip6_addr_t ip; + uint8_t is_router; + uint8_t status; + uint8_t times_asked; + /* ... */ + struct neighbor *next; + uint8_t eth_frame[1500]; //FIXME + uint32_t eth_len; + +#define NB_INCOMPLETE 1 +#define NB_REACHABLE 2 +#define NB_STALE 3 +#define NB_DELAY 4 +#define NB_PROBE 5 +}; + +/******************** FUNCTIONS *********************************************/ +int8_t neighbor_add (struct neighbor *); +void * neighbor_create (uint8_t *packet, struct packeth *headers); +struct neighbor * find_neighbor (ip6_addr_t *); + +int8_t router_add(struct router*); +void * router_create(uint8_t *mac, ip6_addr_t *ip); +struct router * find_router(ip6_addr_t *); + +#endif //_NDP_H_ diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/tcp.c b/qemu/roms/SLOF/clients/net-snk/app/netlib/tcp.c new file mode 100644 index 000000000..5511aa00a --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/tcp.c @@ -0,0 +1,50 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +/*>>>>>>>>>>>>>>>>>>>>>>> DEFINITIONS & DECLARATIONS <<<<<<<<<<<<<<<<<<<<*/ + +#include <tcp.h> +#include <sys/socket.h> + + +/*>>>>>>>>>>>>>>>>>>>>>>>>>>>>> LOCAL VARIABLES <<<<<<<<<<<<<<<<<<<<<<<<<*/ + +/*>>>>>>>>>>>>>>>>>>>>>>>>>>>>> IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + +/** + * TCP: Handles TCP-packets according to Receive-handle diagram. + * + * @param tcp_packet TCP-packet to be handled + * @param packetsize Length of the packet + * @return ZERO - packet handled successfully; + * NON ZERO - packet was not handled (e.g. bad format) + */ +int8_t +handle_tcp(uint8_t * tcp_packet, int32_t packetsize) +{ + return -1; +} + + +/** + * NET: This function handles situation when "Destination unreachable" + * ICMP-error occurs during sending TCP-packet. + * + * @param err_code Error Code (e.g. "Host unreachable") + * @param packet original TCP-packet + * @param packetsize length of the packet + * @see handle_icmp + */ +void +handle_tcp_dun(uint8_t * tcp_packet, uint32_t packetsize, uint8_t err_code) { +} diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/tcp.h b/qemu/roms/SLOF/clients/net-snk/app/netlib/tcp.h new file mode 100644 index 000000000..375afd771 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/tcp.h @@ -0,0 +1,27 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef _TCP_H +#define _TCP_H + +#include <stdint.h> + +#define IPTYPE_TCP 6 + +/* Handles TCP-packets that are detected by any network layer. */ +extern int8_t handle_tcp(uint8_t * udp_packet, int32_t packetsize); + +/* Handles TCP related ICMP-Dest.Unreachable packets that are detected by + * the network layers. */ +extern void handle_tcp_dun(uint8_t * tcp_packet, uint32_t packetsize, uint8_t err_code); + +#endif diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/tftp.c b/qemu/roms/SLOF/clients/net-snk/app/netlib/tftp.c new file mode 100644 index 000000000..0a7c0ec63 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/tftp.c @@ -0,0 +1,597 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <tftp.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <sys/socket.h> + +#include <ethernet.h> +#include <ipv4.h> +#include <ipv6.h> +#include <udp.h> + +//#define __DEBUG__ + +#define MAX_BLOCKSIZE 1428 +#define BUFFER_LEN 256 + +#define ENOTFOUND 1 +#define EACCESS 2 +#define EBADOP 4 +#define EBADID 5 +#define ENOUSER 7 +//#define EUNDEF 0 +//#define ENOSPACE 3 +//#define EEXISTS 6 + +#define RRQ 1 +#define WRQ 2 +#define DATA 3 +#define ACK 4 +#define ERROR 5 +#define OACK 6 + +/* Local variables */ +static unsigned char packet[BUFFER_LEN]; +static unsigned char *buffer = NULL; +static unsigned short block = 0; +static unsigned short blocksize; +static char blocksize_str[6]; /* Blocksize string for read request */ +static int received_len = 0; +static int retries = 0; +static int huge_load; +static int len; +static int tftp_finished = 0; +static int lost_packets = 0; +static int tftp_errno = 0; +static int ip_version = 0; +static short port_number = -1; +static tftp_err_t *tftp_err; +static filename_ip_t *fn_ip; + +/** + * dump_package - Prints a package. + * + * @package: package which is to print + * @len: length of the package + */ +#ifdef __DEBUG__ + +static void +dump_package(unsigned char *buffer, unsigned int len) +{ + int i; + + for (i = 1; i <= len; i++) { + printf("%02x%02x ", buffer[i - 1], buffer[i]); + i++; + if ((i % 16) == 0) + printf("\n"); + } + printf("\n"); +} +#endif + +/** + * send_rrq - Sends a read request package. + * + * @fd: Socket Descriptor + */ +static void +send_rrq(int fd) +{ + int ip_len = 0; + int ip6_payload_len = 0; + unsigned short udp_len = 0; + unsigned char mode[] = "octet"; + char *ptr = NULL; + struct iphdr *ip = NULL; + struct ip6hdr *ip6 = NULL; + struct udphdr *udph = NULL; + struct tftphdr *tftp = NULL; + + memset(packet, 0, BUFFER_LEN); + + if (4 == ip_version) { + ip = (struct iphdr *) packet; + udph = (struct udphdr *) (ip + 1); + ip_len = sizeof(struct iphdr) + sizeof(struct udphdr) + + strlen((char *) fn_ip->filename) + strlen((char *) mode) + 4 + + strlen("blksize") + strlen(blocksize_str) + 2; + fill_iphdr ((uint8_t *) ip, ip_len, IPTYPE_UDP, 0, + fn_ip->server_ip); + } + else if (6 == ip_version) { + ip6 = (struct ip6hdr *) packet; + udph = (struct udphdr *) (ip6 + 1); + ip6_payload_len = sizeof(struct udphdr) + + strlen((char *) fn_ip->filename) + strlen((char *) mode) + 4 + + strlen("blksize") + strlen(blocksize_str) + 2; + ip_len = sizeof(struct ip6hdr) + ip6_payload_len; + fill_ip6hdr ((uint8_t *) ip6, ip6_payload_len, IPTYPE_UDP, get_ipv6_address(), + &(fn_ip->server_ip6)); + + } + udp_len = htons(sizeof(struct udphdr) + + strlen((char *) fn_ip->filename) + strlen((char *) mode) + 4 + + strlen("blksize") + strlen(blocksize_str) + 2); + fill_udphdr ((uint8_t *) udph, udp_len, htons(2001), htons(69)); + + tftp = (struct tftphdr *) (udph + 1); + tftp->th_opcode = htons(RRQ); + + ptr = (char *) &tftp->th_data; + memcpy(ptr, fn_ip->filename, strlen((char *) fn_ip->filename) + 1); + + ptr += strlen((char *) fn_ip->filename) + 1; + memcpy(ptr, mode, strlen((char *) mode) + 1); + + ptr += strlen((char *) mode) + 1; + memcpy(ptr, "blksize", strlen("blksize") + 1); + + ptr += strlen("blksize") + 1; + memcpy(ptr, blocksize_str, strlen(blocksize_str) + 1); + + send_ip (fd, packet, ip_len); + +#ifdef __DEBUG__ + printf("tftp RRQ with %d bytes transmitted.\n", ip_len); +#endif + return; +} + +/** + * send_ack - Sends a acknowlege package. + * + * @blckno: block number + * @dport: UDP destination port + */ +static void +send_ack(int fd, int blckno, unsigned short dport) +{ + int ip_len = 0; + int ip6_payload_len = 0; + unsigned short udp_len = 0; + struct iphdr *ip = NULL; + struct ip6hdr *ip6 = NULL; + struct udphdr *udph = NULL; + struct tftphdr *tftp = NULL; + + memset(packet, 0, BUFFER_LEN); + + if (4 == ip_version) { + ip = (struct iphdr *) packet; + udph = (struct udphdr *) (ip + 1); + ip_len = sizeof(struct iphdr) + sizeof(struct udphdr) + 4; + fill_iphdr ((uint8_t *) ip, ip_len, IPTYPE_UDP, 0, + fn_ip->server_ip); + } + else if (6 == ip_version) { + ip6 = (struct ip6hdr *) packet; + udph = (struct udphdr *) (ip6 + 1); + ip6_payload_len = sizeof(struct udphdr) + 4; + ip_len = sizeof(struct ethhdr) + sizeof(struct ip6hdr) + + ip6_payload_len; + fill_ip6hdr ((uint8_t *) ip6, ip6_payload_len, IPTYPE_UDP, get_ipv6_address(), + &(fn_ip->server_ip6)); + } + udp_len = htons(sizeof(struct udphdr) + 4); + fill_udphdr ((uint8_t *) udph, udp_len, htons(2001), htons(dport)); + + tftp = (struct tftphdr *) (udph + 1); + tftp->th_opcode = htons(ACK); + tftp->th_data = htons(blckno); + + send_ip(fd, packet, ip_len); + +#ifdef __DEBUG__ + printf("tftp ACK %d bytes transmitted.\n", ip_len); +#endif + + return; +} + +/** + * send_error - Sends an error package. + * + * @fd: Socket Descriptor + * @error_code: Used sub code for error packet + * @dport: UDP destination port + */ +static void +send_error(int fd, int error_code, unsigned short dport) +{ + int ip_len = 0; + int ip6_payload_len = 0; + unsigned short udp_len = 0; + struct ip6hdr *ip6 = NULL; + struct iphdr *ip = NULL; + struct udphdr *udph = NULL; + struct tftphdr *tftp = NULL; + + memset(packet, 0, BUFFER_LEN); + + if (4 == ip_version) { + ip = (struct iphdr *) packet; + udph = (struct udphdr *) (ip + 1); + ip_len = sizeof(struct iphdr) + sizeof(struct udphdr) + 5; + fill_iphdr ((uint8_t *) ip, ip_len, IPTYPE_UDP, 0, + fn_ip->server_ip); + } + else if (6 == ip_version) { + ip6 = (struct ip6hdr *) packet; + udph = (struct udphdr *) (ip6 + 1); + ip6_payload_len = sizeof(struct udphdr) + 5; + ip_len = sizeof(struct ethhdr) + sizeof(struct ip6hdr) + + ip6_payload_len; + fill_ip6hdr ((uint8_t *) ip6, ip6_payload_len, IPTYPE_UDP, get_ipv6_address(), + &(fn_ip->server_ip6)); + } + udp_len = htons(sizeof(struct udphdr) + 5); + fill_udphdr ((uint8_t *) udph, udp_len, htons(2001), htons(dport)); + + tftp = (struct tftphdr *) (udph + 1); + tftp->th_opcode = htons(ERROR); + tftp->th_data = htons(error_code); + ((char *) &tftp->th_data)[2] = 0; + + send_ip(fd, packet, ip_len); + +#ifdef __DEBUG__ + printf("tftp ERROR %d bytes transmitted.\n", ip_len); +#endif + + return; +} + +static void +print_progress(int urgent, int received_bytes) +{ + static unsigned int i = 1; + static int first = -1; + static int last_bytes = 0; + char buffer[100]; + char *ptr; + + // 1MB steps or 0x400 times or urgent + if(((received_bytes - last_bytes) >> 20) > 0 + || (i & 0x3FF) == 0 || urgent) { + if(!first) { + sprintf(buffer, "%d KBytes", (last_bytes >> 10)); + for(ptr = buffer; *ptr != 0; ++ptr) + *ptr = '\b'; + printf(buffer); + } + printf("%d KBytes", (received_bytes >> 10)); + i = 1; + first = 0; + last_bytes = received_bytes; + } + ++i; +} + +/** + * get_blksize tries to extract the blksize from the OACK package + * the TFTP returned. From RFC 1782 + * The OACK packet has the following format: + * + * +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ + * | opc | opt1 | 0 | value1 | 0 | optN | 0 | valueN | 0 | + * +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+ + * + * @param buffer the network packet + * @param len the length of the network packet + * @return the blocksize the server supports or 0 for error + */ +static int +get_blksize(unsigned char *buffer, unsigned int len) +{ + unsigned char *orig = buffer; + /* skip all headers until tftp has been reached */ + buffer += sizeof(struct udphdr); + /* skip opc */ + buffer += 2; + while (buffer < orig + len) { + if (!memcmp(buffer, "blksize", strlen("blksize") + 1)) + return (unsigned short) strtoul((char *) (buffer + + strlen("blksize") + 1), + (char **) NULL, 10); + else { + /* skip the option name */ + buffer = (unsigned char *) strchr((char *) buffer, 0); + if (!buffer) + return 0; + buffer++; + /* skip the option value */ + buffer = (unsigned char *) strchr((char *) buffer, 0); + if (!buffer) + return 0; + buffer++; + } + } + return 0; +} + +/** + * Handle incoming tftp packets after read request was sent + * + * this function also prints out some status characters + * \|-/ for each packet received + * A for an arp packet + * I for an ICMP packet + * #+* for different unexpected TFTP packets (not very good) + * + * @param fd socket descriptor + * @param packet points to the UDP header of the packet + * @param len the length of the network packet + * @return ZERO if packet was handled successfully + * ERRORCODE if error occurred + */ +int32_t +handle_tftp(int fd, uint8_t *pkt, int32_t packetsize) +{ + struct udphdr *udph; + struct tftphdr *tftp; + + /* buffer is only set if we are handling TFTP */ + if (buffer == NULL ) + return 0; + +#ifndef __DEBUG__ + print_progress(0, received_len); +#endif + udph = (struct udphdr *) pkt; + tftp = (struct tftphdr *) ((void *) udph + sizeof(struct udphdr)); + set_timer(TICKS_SEC); + +#ifdef __DEBUG__ + dump_package(pkt, packetsize); +#endif + + port_number = udph->uh_sport; + if (tftp->th_opcode == htons(OACK)) { + /* an OACK means that the server answers our blocksize request */ + blocksize = get_blksize(pkt, packetsize); + if (!blocksize || blocksize > MAX_BLOCKSIZE) { + send_error(fd, 8, port_number); + tftp_errno = -8; + goto error; + } + send_ack(fd, 0, port_number); + } else if (tftp->th_opcode == htons(ACK)) { + /* an ACK means that the server did not answers + * our blocksize request, therefore we will set the blocksize + * to the default value of 512 */ + blocksize = 512; + send_ack(fd, 0, port_number); + } else if ((unsigned char) tftp->th_opcode == ERROR) { +#ifdef __DEBUG__ + printf("tftp->th_opcode : %x\n", tftp->th_opcode); + printf("tftp->th_data : %x\n", tftp->th_data); +#endif + switch ( (uint8_t) tftp->th_data) { + case ENOTFOUND: + tftp_errno = -3; // ERROR: file not found + break; + case EACCESS: + tftp_errno = -4; // ERROR: access violation + break; + case EBADOP: + tftp_errno = -5; // ERROR: illegal TFTP operation + break; + case EBADID: + tftp_errno = -6; // ERROR: unknown transfer ID + break; + case ENOUSER: + tftp_errno = -7; // ERROR: no such user + break; + default: + tftp_errno = -1; // ERROR: unknown error + } + goto error; + } else if (tftp->th_opcode == DATA) { + /* DATA PACKAGE */ + if (block + 1 == tftp->th_data) { + ++block; + } + else if( block == 0xffff && huge_load != 0 + && (tftp->th_data == 0 || tftp->th_data == 1) ) { + block = tftp->th_data; + } + else if (tftp->th_data == block) { +#ifdef __DEBUG__ + printf + ("\nTFTP: Received block %x, expected block was %x\n", + tftp->th_data, block + 1); + printf("\b+ "); +#endif + send_ack(fd, tftp->th_data, port_number); + lost_packets++; + tftp_err->bad_tftp_packets++; + return 0; + } else if (tftp->th_data < block) { +#ifdef __DEBUG__ + printf + ("\nTFTP: Received block %x, expected block was %x\n", + tftp->th_data, block + 1); + printf("\b* "); +#endif + /* This means that an old data packet appears (again); + * this happens sometimes if we don't answer fast enough + * and a timeout is generated on the server side; + * as we already have this packet we just ignore it */ + tftp_err->bad_tftp_packets++; + return 0; + } else { + tftp_err->blocks_missed = block + 1; + tftp_err->blocks_received = tftp->th_data; + tftp_errno = -42; + goto error; + } + tftp_err->bad_tftp_packets = 0; + /* check if our buffer is large enough */ + if (received_len + udph->uh_ulen - 12 > len) { + tftp_errno = -2; + goto error; + } + memcpy(buffer + received_len, &tftp->th_data + 1, + udph->uh_ulen - 12); + send_ack(fd, tftp->th_data, port_number); + received_len += udph->uh_ulen - 12; + /* Last packet reached if the payload of the UDP packet + * is smaller than blocksize + 12 + * 12 = UDP header (8) + 4 bytes TFTP payload */ + if (udph->uh_ulen < blocksize + 12) { + tftp_finished = 1; + return 0; + } + /* 0xffff is the highest block number possible + * see the TFTP RFCs */ + + if (block >= 0xffff && huge_load == 0) { + tftp_errno = -9; + goto error; + } + } else { +#ifdef __DEBUG__ + printf("Unknown packet %x\n", tftp->th_opcode); + printf("\b# "); +#endif + tftp_err->bad_tftp_packets++; + return 0; + } + + return 0; + +error: +#ifdef __DEBUG__ + printf("\nTFTP errno: %d\n", tftp_errno); +#endif + tftp_finished = 1; + return tftp_errno; +} + +/** + * TFTP: This function handles situation when "Destination unreachable" + * ICMP-error occurs during sending TFTP-packet. + * + * @param err_code Error Code (e.g. "Host unreachable") + */ +void +handle_tftp_dun(uint8_t err_code) +{ + tftp_errno = - err_code - 10; + tftp_finished = 1; +} + +/** + * TFTP: Interface function to load files via TFTP. + * + * @param _fn_ip contains the following configuration information: + * client IP, TFTP-server IP, filename to be loaded + * @param _buffer destination buffer for the file + * @param _len size of destination buffer + * @param _retries max number of retries + * @param _tftp_err contains info about TFTP-errors (e.g. lost packets) + * @param _mode NON ZERO - multicast, ZERO - unicast + * @param _blocksize blocksize for DATA-packets + * @return ZERO - error condition occurs + * NON ZERO - size of received file + */ +int +tftp(filename_ip_t * _fn_ip, unsigned char *_buffer, int _len, + unsigned int _retries, tftp_err_t * _tftp_err, + int32_t _mode, int32_t _blocksize, int _ip_version) +{ + retries = _retries; + fn_ip = _fn_ip; + len = _len; + huge_load = _mode; + ip_version = _ip_version; + tftp_errno = 0; + tftp_err = _tftp_err; + tftp_err->bad_tftp_packets = 0; + tftp_err->no_packets = 0; + + /* Default blocksize must be 512 for TFTP servers + * which do not support the RRQ blocksize option */ + blocksize = 512; + + /* Preferred blocksize - used as option for the read request */ + if (_blocksize < 8) + _blocksize = 8; + else if (_blocksize > MAX_BLOCKSIZE) + _blocksize = MAX_BLOCKSIZE; + sprintf(blocksize_str, "%d", _blocksize); + + printf(" Receiving data: "); + print_progress(-1, 0); + + // Setting buffer to a non-zero address enabled handling of received TFTP packets. + buffer = _buffer; + + set_timer(TICKS_SEC); + send_rrq(fn_ip->fd); + + while (! tftp_finished) { + /* if timeout (no packet received) */ + if(get_timer() <= 0) { + /* the server doesn't seem to retry let's help out a bit */ + if (tftp_err->no_packets > 4 && port_number != -1 + && block > 1) { + send_ack(fn_ip->fd, block, port_number); + } + else if (port_number == -1 && block == 0 + && (tftp_err->no_packets&3) == 3) { + printf("\nRepeating TFTP read request...\n"); + send_rrq(fn_ip->fd); + } + tftp_err->no_packets++; + set_timer(TICKS_SEC); + } + + /* handle received packets */ + receive_ether(fn_ip->fd); + + /* bad_tftp_packets are counted whenever we receive a TFTP packet + * which was not expected; if this gets larger than 'retries' + * we just exit */ + if (tftp_err->bad_tftp_packets > retries) { + tftp_errno = -40; + break; + } + + /* no_packets counts the times we have returned from receive_ether() + * without any packet received; if this gets larger than 'retries' + * we also just exit */ + if (tftp_err->no_packets > retries) { + tftp_errno = -41; + break; + } + } + + // Setting buffer to NULL disables handling of received TFTP packets. + buffer = NULL; + + if (tftp_errno) + return tftp_errno; + + print_progress(-1, received_len); + printf("\n"); + if (lost_packets) + printf("Lost ACK packets: %d\n", lost_packets); + + return received_len; +} diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/tftp.h b/qemu/roms/SLOF/clients/net-snk/app/netlib/tftp.h new file mode 100644 index 000000000..1cf1266fb --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/tftp.h @@ -0,0 +1,51 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + + +#ifndef _TFTP_H_ +#define _TFTP_H_ + +#include <stdint.h> +#include <netlib/ipv6.h> + +struct tftphdr { + int16_t th_opcode; + uint16_t th_data; +}; + +typedef struct { + uint32_t own_ip; + ip6_addr_t own_ip6; + uint32_t server_ip; + ip6_addr_t server_ip6; + ip6_addr_t dns_ip6; + int8_t filename[256]; + int fd; +} __attribute__ ((packed)) filename_ip_t ; + +typedef struct { + uint32_t bad_tftp_packets; + uint32_t no_packets; + uint32_t blocks_missed; + uint32_t blocks_received; +} tftp_err_t; + +int tftp(filename_ip_t *, unsigned char *, int, unsigned int, + tftp_err_t *, int32_t mode, int32_t blocksize, int ip_version); +int tftp_netsave(filename_ip_t *, uint8_t * buffer, int len, + int use_ci, unsigned int retries, tftp_err_t * tftp_err); + +int32_t handle_tftp(int fd, uint8_t *, int32_t); +void handle_tftp_dun(uint8_t err_code); +int parse_tftp_args(char buffer[], char *server_ip, char filename[], int fd, int len); + +#endif diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/udp.c b/qemu/roms/SLOF/clients/net-snk/app/netlib/udp.c new file mode 100644 index 000000000..db29bc90f --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/udp.c @@ -0,0 +1,151 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +/*>>>>>>>>>>>>>>>>>>>>>>> DEFINITIONS & DECLARATIONS <<<<<<<<<<<<<<<<<<<<*/ + +#include <udp.h> +#include <sys/socket.h> +#include <dhcp.h> +#include <dhcpv6.h> +#include <dns.h> +#ifdef USE_MTFTP +#include <mtftp.h> +#else +#include <tftp.h> +#endif + + + +/*>>>>>>>>>>>>>>>>>>>>>>>>>>>>> LOCAL VARIABLES <<<<<<<<<<<<<<<<<<<<<<<<<*/ + + +#ifdef USE_MTFTP + +uint16_t net_tftp_uport; +uint16_t net_mtftp_uport; + +void net_set_tftp_port(uint16_t tftp_port) { + net_tftp_uport = tftp_port; +} + +void net_set_mtftp_port(uint16_t tftp_port) { + net_mtftp_uport = tftp_port; +} + +#endif + +/*>>>>>>>>>>>>>>>>>>>>>>>>>>>>> IMPLEMENTATION <<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + +/** + * NET: Handles UDP-packets according to Receive-handle diagram. + * + * @param udp_packet UDP-packet to be handled + * @param packetsize Length of the packet + * @return ZERO - packet handled successfully; + * NON ZERO - packet was not handled (e.g. bad format) + * @see receive_ether + * @see udphdr + */ +int8_t +handle_udp(int fd, uint8_t * udp_packet, int32_t packetsize) { + struct udphdr * udph = (struct udphdr *) udp_packet; + + if (packetsize < sizeof(struct udphdr)) + return -1; // packet is too small + + switch (htons(udph -> uh_dport)) { + case UDPPORT_BOOTPC: + if (udph -> uh_sport == htons(UDPPORT_BOOTPS)) + return handle_dhcp(fd, udp_packet + sizeof(struct udphdr), + packetsize - sizeof(struct udphdr)); + else + return -1; + case UDPPORT_DNSC: + if (udph -> uh_sport == htons(UDPPORT_DNSS)) + return handle_dns(udp_packet + sizeof(struct udphdr), + packetsize - sizeof(struct udphdr)); + else + return -1; + case UDPPORT_DHCPV6C: + return handle_dhcpv6(udp_packet+sizeof(struct udphdr), + packetsize - sizeof(struct udphdr)); + case UDPPORT_TFTPC: +#ifdef USE_MTFTP + return handle_tftp(fd, udp_packet + sizeof(struct udphdr), + packetsize - sizeof(struct udphdr)); +#else + return handle_tftp(fd, udp_packet, packetsize); +#endif + default: +#ifdef USE_MTFTP + if (htons(udph -> uh_dport) == net_tftp_uport) + return handle_tftp(fd, udp_packet + sizeof(struct udphdr), + packetsize - sizeof(struct udphdr)); + else if (htons(udph -> uh_dport) == net_mtftp_uport) + return handle_tftp(fd, udp_packet + sizeof(struct udphdr), + packetsize - sizeof(struct udphdr)); +#endif + return -1; + } +} + +/** + * NET: This function handles situation when "Destination unreachable" + * ICMP-error occurs during sending UDP-packet. + * + * @param err_code Error Code (e.g. "Host unreachable") + * @param packet original UDP-packet + * @param packetsize length of the packet + * @see handle_icmp + */ +void +handle_udp_dun(uint8_t * udp_packet, uint32_t packetsize, uint8_t err_code) { + struct udphdr * udph = (struct udphdr *) udp_packet; + + if (packetsize < sizeof(struct udphdr)) + return; // packet is too small + + switch (htons(udph -> uh_sport)) { + case UDPPORT_TFTPC: + handle_tftp_dun(err_code); + break; + } +} + +/** + * NET: Creates UDP-packet. Places UDP-header in a packet and fills it + * with corresponding information. + * <p> + * Use this function with similar functions for other network layers + * (fill_ethhdr, fill_iphdr, fill_dnshdr, fill_btphdr). + * + * @param packet Points to the place where UDP-header must be placed. + * @param packetsize Size of the packet in bytes incl. this hdr and data. + * @param src_port UDP source port + * @param dest_port UDP destination port + * @see udphdr + * @see fill_ethhdr + * @see fill_iphdr + * @see fill_dnshdr + * @see fill_btphdr + */ +void +fill_udphdr(uint8_t * packet, uint16_t packetsize, + uint16_t src_port, uint16_t dest_port) { + struct udphdr * udph = (struct udphdr *) packet; + + udph -> uh_sport = htons(src_port); + udph -> uh_dport = htons(dest_port); + udph -> uh_ulen = htons(packetsize); + udph -> uh_sum = htons(0); +} diff --git a/qemu/roms/SLOF/clients/net-snk/app/netlib/udp.h b/qemu/roms/SLOF/clients/net-snk/app/netlib/udp.h new file mode 100644 index 000000000..1ba9332ce --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/app/netlib/udp.h @@ -0,0 +1,58 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef _UDP_H +#define _UDP_H + +#include <stdint.h> + +#define IPTYPE_UDP 17 + +#define UDPPORT_BOOTPS 67 /**< UDP port of BootP/DHCP-server */ +#define UDPPORT_BOOTPC 68 /**< UDP port of BootP/DHCP-client */ +#define UDPPORT_DNSS 53 /**< UDP port of DNS-server */ +#define UDPPORT_DNSC 32769 /**< UDP port of DNS-client */ +#define UDPPORT_TFTPC 2001 /**< UDP port of TFTP-client */ +#define UDPPORT_DHCPV6C 546 /**< UDP port of DHCPv6-client */ + +/** \struct udphdr + * A header for UDP-packets. + * For more information see RFC 768. + */ +struct udphdr { + uint16_t uh_sport; /**< Source port */ + uint16_t uh_dport; /**< Destinantion port */ + uint16_t uh_ulen; /**< Length in octets, incl. this header and data */ + uint16_t uh_sum; /**< Checksum */ +}; +typedef struct udphdr udp_hdr_t; + +typedef int32_t *(*handle_upper_udp_t)(uint8_t *, int32_t); +typedef void *(*handle_upper_udp_dun_t)(uint8_t); + +/* Handles UDP-packets that are detected by any network layer. */ +extern int8_t handle_udp(int fd, uint8_t * udp_packet, int32_t packetsize); + +/* Handles UDP related ICMP-Dest.Unreachable packets that are detected by + * the network layers. */ +extern void handle_udp_dun(uint8_t * udp_packet, uint32_t packetsize, uint8_t err_code); + +/* fills udp header */ +extern void fill_udphdr(uint8_t *packet, uint16_t packetsize, + uint16_t src_port, uint16_t dest_port); + +#ifdef USE_MTFTP +extern void net_set_tftp_port(uint16_t tftp_port); +extern void net_set_mtftp_port(uint16_t tftp_port); +#endif + +#endif diff --git a/qemu/roms/SLOF/clients/net-snk/client.lds b/qemu/roms/SLOF/clients/net-snk/client.lds new file mode 100644 index 000000000..39d04594e --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/client.lds @@ -0,0 +1,85 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +OUTPUT_FORMAT("elf64-powerpc", "elf64-powerpc", "elf64-powerpc") +OUTPUT_ARCH(powerpc:common64) +ENTRY(_entry) + +SECTIONS { + . = 0xF000100; + .text : + { + __client_start = .; + *(.text* .stub .gnu.linkonce.t.*) + *(.sfpr .glink) + } + + . = ALIGN(0x100); + .rodata : + { + *(.rodata* .gnu.linkonce.r.*) + } + + . = ALIGN(0x10); + .data : + { + *(.data* .gnu.linkonce.d.*) + *(.force.data) + *(.toc1) + *(.branch_lt) + } + + . = ALIGN(0x10); + .opd : + { + *(.opd) + } + + . = ALIGN(0x10); + .got : + { + _got = .; + *(.got) + *(.toc) + _got_end = .; + } + + . = ALIGN(0x1000); + .bss : + { + *(*COM* .bss* .gnu.linkonce.b.*) + } + __client_end = .; + + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + . = ALIGN(0x10); + .dynamic : { + *(.dynamic) + } + . = ALIGN(0x10); + .rela.dyn : { + *(.rela*) + } + .hash : { *(.hash) } + + .comment : { + /* + * Discarding this section caused errors on binutils 2.23, + * this is fixed in 2.24. + */ + *(.comment) + } + /DISCARD/ : { + *(.interp) + } +} diff --git a/qemu/roms/SLOF/clients/net-snk/include/crt0.h b/qemu/roms/SLOF/clients/net-snk/include/crt0.h new file mode 100644 index 000000000..d8fce05c9 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/include/crt0.h @@ -0,0 +1,20 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + + +#ifndef _CRT0_H +#define _CRT0_H + +int gen_argv(const char *, int, char **); + + +#endif diff --git a/qemu/roms/SLOF/clients/net-snk/include/fcntl.h b/qemu/roms/SLOF/clients/net-snk/include/fcntl.h new file mode 100644 index 000000000..69de2cea3 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/include/fcntl.h @@ -0,0 +1,25 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + + +#ifndef _FCNTL_H +#define _FCNTL_H + +#define O_RDONLY 00 +#define O_WRONLY 01 +#define O_RDRW 02 + +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 + +#endif /* fcntl.h */ diff --git a/qemu/roms/SLOF/clients/net-snk/include/fileio.h b/qemu/roms/SLOF/clients/net-snk/include/fileio.h new file mode 100644 index 000000000..50f9650b2 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/include/fileio.h @@ -0,0 +1,31 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef FILEIO_H +#define FILEIO_H + +#include <of.h> + +#define FILEIO_TYPE_EMPTY 0 +#define FILEIO_TYPE_FILE 1 +#define FILEIO_TYPE_SOCKET 2 + +struct snk_fileio_type { + int type; + ihandle_t ih; +}; +typedef struct snk_fileio_type snk_fileio_t; + +#define FILEIO_MAX 32 +extern snk_fileio_t fd_array[FILEIO_MAX]; + +#endif diff --git a/qemu/roms/SLOF/clients/net-snk/include/kernel.h b/qemu/roms/SLOF/clients/net-snk/include/kernel.h new file mode 100644 index 000000000..4d6be2dc2 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/include/kernel.h @@ -0,0 +1,33 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef KERNEL_H +#define KERNEL_H + +#include <stddef.h> +#include <stdint.h> +#include <unistd.h> +#include <string.h> +#include <stdio.h> +#include <of.h> + +uint64_t get_time(void); +int getchar(void); + +void *malloc_aligned(size_t size, int align); + +int pre_open_ih(int fd, ihandle_t ih); + +void exception_forward(void); +void undo_exception(void); + +#endif diff --git a/qemu/roms/SLOF/clients/net-snk/include/of.h b/qemu/roms/SLOF/clients/net-snk/include/of.h new file mode 100644 index 000000000..22411ff98 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/include/of.h @@ -0,0 +1,70 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef OF_H +#define OF_H + +#include <stdint.h> +#include <stddef.h> + +#define p32 int +#define p32cast (int) (unsigned long) (void*) + +#define phandle_t p32 +#define ihandle_t p32 + +typedef struct +{ + unsigned int serv; + int nargs; + int nrets; + unsigned int args[16]; +} of_arg_t; + + +phandle_t of_finddevice (const char *); +phandle_t of_peer (phandle_t); +phandle_t of_child (phandle_t); +phandle_t of_parent (phandle_t); +phandle_t of_instance_to_package(ihandle_t ihandle); +int of_getprop (phandle_t, const char *, void *, int); +void * of_call_method_3 (const char *, ihandle_t, int); +int of_test(const char *name); +int of_interpret_1(void *s, void *ret); + +ihandle_t of_open (const char *); +void of_close(ihandle_t); +int of_read (ihandle_t , void*, int); +int of_write (ihandle_t, void*, int); +int of_seek (ihandle_t, int, int); + +void * of_claim(void *, unsigned int , unsigned int ); +void of_release(void *, unsigned int ); + +int of_yield(void); +void * of_set_callback(void *); + +unsigned int romfs_lookup(const char *, void **); +int vpd_read(unsigned int , unsigned int , char *); +int vpd_write(unsigned int , unsigned int , char *); +int write_mm_log(char *, unsigned int , unsigned short ); + +int of_get_mac(phandle_t device, char *mac); +uint64_t get_puid(phandle_t node); +void translate_address_dev(uint64_t *, phandle_t); +void translate_address(unsigned long *addr); + +int of_glue_init(unsigned int * timebase, + size_t _client_start, size_t _client_size); +void of_glue_release(void); + +#endif diff --git a/qemu/roms/SLOF/clients/net-snk/include/pci.h b/qemu/roms/SLOF/clients/net-snk/include/pci.h new file mode 100644 index 000000000..cf584c348 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/include/pci.h @@ -0,0 +1,20 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + + +#ifndef _PCI_H +#define _PCI_H + +unsigned int read_io(void *addr, size_t sz); +int write_io(void *addr, unsigned int value, size_t sz); + +#endif diff --git a/qemu/roms/SLOF/clients/net-snk/include/rtas.h b/qemu/roms/SLOF/clients/net-snk/include/rtas.h new file mode 100644 index 000000000..25cabf4d6 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/include/rtas.h @@ -0,0 +1,45 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + + +#ifndef RTAS_H +#define RTAS_H + +#include "of.h" + +typedef struct dtime { + unsigned int year; + unsigned int month; + unsigned int day; + unsigned int hour; + unsigned int minute; + unsigned int second; + unsigned int nano; +} dtime; + +typedef void (*thread_t) (int); + +int rtas_token(const char *); +int rtas_call(int, int, int, int *, ...); +void rtas_init(void); +int rtas_pci_config_read (long long, int, int, int, int); +int rtas_pci_config_write (long long, int, int, int, int, int); +int rtas_set_time_of_day(dtime *); +int rtas_get_time_of_day(dtime *); +int rtas_ibm_update_flash_64(long long, long long); +int rtas_ibm_update_flash_64_and_reboot(long long, long long); +int rtas_system_reboot(void); +int rtas_start_cpu (int, thread_t, int); +int rtas_stop_self (void); +int rtas_ibm_manage_flash(int); + +#endif diff --git a/qemu/roms/SLOF/clients/net-snk/include/sys/socket.h b/qemu/roms/SLOF/clients/net-snk/include/sys/socket.h new file mode 100644 index 000000000..e9175be37 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/include/sys/socket.h @@ -0,0 +1,53 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + + +#ifndef _SOCKET_H +#define _SOCKET_H +#include <stdint.h> + +#define AF_PACKET 0 +#define AF_INET 1 +#define AF_INET6 2 + +#define SOCK_RAW 0 +#define SOCK_PACKET 1 +#define SOCK_DGRAM 2 +#define SOCK_STREAM 3 + +#define INADDR_ANY 0xFFFFFFFF + +#define IPPROTO_UDP 1 + +#define ETH_ALEN 6 /**< HW address length */ + +struct sockaddr { + uint16_t tra_port; + + uint16_t ipv4_proto; + uint32_t ipv4_addr; + + // protocol field is only used by "connect"-handler + uint16_t llc_proto; + uint8_t mac_addr[ETH_ALEN]; +}; + +int socket(int, int, int, char *); +int sendto(int, const void *, int, int, const void *, int); +int send(int, const void *, int, int); +int recv(int, void *, int, int); + +#define htonl(x) x +#define htons(x) x + +#endif + diff --git a/qemu/roms/SLOF/clients/net-snk/include/time.h b/qemu/roms/SLOF/clients/net-snk/include/time.h new file mode 100644 index 000000000..14d1c4c57 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/include/time.h @@ -0,0 +1,36 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + + +#ifndef _TIME_H_ +#define _TIME_H_ + +typedef unsigned long clock_t; +typedef unsigned long time_t; + +time_t time(time_t *); + +extern unsigned long tb_freq; + +/* setup the timer to start counting from the given parameter */ +void set_timer(int); +/* read the current value from the decrementer */ +int get_timer(void); +/* get the number of ticks for which the decrementer needs 1 second */ +int get_sec_ticks(void); +/* get the number of ticks for which the decrementer needs 1 millisecond */ +int get_msec_ticks(void); + +#define TICKS_MSEC get_msec_ticks() +#define TICKS_SEC get_sec_ticks() + +#endif diff --git a/qemu/roms/SLOF/clients/net-snk/kernel/Makefile b/qemu/roms/SLOF/clients/net-snk/kernel/Makefile new file mode 100644 index 000000000..c4b8164ca --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/kernel/Makefile @@ -0,0 +1,31 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2008 IBM Corporation +# * All rights reserved. +# * This program and the accompanying materials +# * are made available under the terms of the BSD License +# * which accompanies this distribution, and is available at +# * http://www.opensource.org/licenses/bsd-license.php +# * +# * Contributors: +# * IBM Corporation - initial implementation +# ****************************************************************************/ + + +ifndef TOP + TOP = $(shell while ! test -e make.rules; do cd .. ; done; pwd) + export TOP +endif +include $(TOP)/make.rules + +OBJS = init.o systemcall.o crt0.o timer.o +OBJS2 = entry.o + +all: kernel.o + +kernel.o: $(OBJS) $(OBJS2) + $(LD) $(LDFLAGS) $(OBJS) $(OBJS2) -o $@ -r + +clean: + $(RM) -f *.o *.a *.i + +include $(TOP)/make.depend diff --git a/qemu/roms/SLOF/clients/net-snk/kernel/crt0.c b/qemu/roms/SLOF/clients/net-snk/kernel/crt0.c new file mode 100644 index 000000000..a292273b0 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/kernel/crt0.c @@ -0,0 +1,78 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdlib.h> +#include <string.h> + +extern int main (int, char**); +extern int callback (int, char **); + +int _start(char *arg_string, long len); +unsigned long callback_entry(void *base, unsigned long len); + + +#define MAX_ARGV 10 +static int +gen_argv(const char *arg_string, int len, char* argv[]) +{ + const char *str, *str_end, *arg_string_end = arg_string + len; + int i; + + str = arg_string; + for (i = 0; i < MAX_ARGV; i++) + { + str_end = str; + + while((*str_end++ != ' ') && (str_end <= arg_string_end)); + + argv[i] = malloc(str_end-str); + + memcpy (argv[i], str, str_end-str-1); + argv[i][str_end-str-1] = '\0'; + str = str_end-1; + while(*(++str) == ' '); + if (str >= arg_string_end) + break; + } + return i+1; +} + + + +int +_start(char * arg_string, long len) +{ + int rc; + int argc; + char* argv[MAX_ARGV]; + + argc = gen_argv(arg_string, len, argv); + + rc = main(argc, argv); + + return rc; +} + +/* + * Takes a Forth representation of a string and generates an argument array, + * then calls callback(). + */ +unsigned long +callback_entry(void *base, unsigned long len) { + char *argv[MAX_ARGV]; + int argc; + + argc = gen_argv(base, len, argv); + + return (callback(argc, argv)); +} + diff --git a/qemu/roms/SLOF/clients/net-snk/kernel/entry.S b/qemu/roms/SLOF/clients/net-snk/kernel/entry.S new file mode 100644 index 000000000..8849fb9d1 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/kernel/entry.S @@ -0,0 +1,127 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#define STACKSIZE 0x100000 +#include <macros.h> + + + .section ".toc","aw" # TOC entries are needed for relocation +.exception_stack_frame_toc: + .tc exception_stack_frame[TC],exception_stack_frame +.exit_sp_toc: + .tc _exit_sp[TC],_exit_sp +.prom_entry_toc: + .tc _prom_entry[TC],_prom_entry + + .previous + + +/* +Function: + Input: + r3: + r4: + r5: prom entry + Output: + +Decription: Main entry point, called from OF + +*/ +C_ENTRY(_entry) + mr r3, r6 # parm 0 passed in r6 + mr r4, r7 # parm 1 passed in r7 + mr r6, r1 # save stack pointer + mflr r7 # save link register + bcl 20,31,over # branch after pointer table +base: + .align 3 +.LCgot: .quad _got-base+0x8000 +.LCstack: .quad _stack+STACKSIZE-0x80-base +over: + mflr r8 # gpr 8 is the base + ld r1,.LCstack-base(r8) # load new stack pointer + add r1, r1, r8 # add base + std r2, 64(r1) # save got + std r7, 56(r1) # save link register + ld r2, .LCgot-base(r8) # load got pointer + add r2, r2, r8 # add base + std r6, 0(r1) # save stack pointer + + ld r6, .prom_entry_toc@toc(r2) + std r5, 0(r6) # Save prom handle + + ld r10, .exit_sp_toc@toc(r2) # save stack pointer for exit call + std r1, 0(r10) + + bl ._start_kernel # call kernel init code + +the_end: + ld r4, 56(r1) # Restore link register + mtlr r4 + ld r2, 64(r1) # restore got + ld r1, 0(r1) + + blr + +/* + * Function: _callback_entry + * Input: r6 start address of parameter string + * r7 length of parameter string. + * + * Description: If a client application wants to register a callback function, + * this function is registered w/ SLOF, not the application's function. SLOF + * passes the parameter string in Forth representation in R6 and R7. This + * function moves R6 to R3 and R7 to R4 and then calls callback_entry(). + * + */ +C_ENTRY(_callback_entry) + # Save the LR + mflr r0 + std r0, 16(r1) + + # Reserve stack space + stdu r1, -32(r1) + + # SLOF passes the parameters in Registers R6 and R7 but the target + # wants them in registers R3 and R4 + mr r3, r6 + mr r4, r7 + + # Branch to the callback_entry function + bl .callback_entry + + # Destroy stack frame + ld r1, 0(r1) + + # Restore LR + ld r0, 16(r1) + mtlr r0 + + # Return to caller + blr + + .section ".bss" + +_exit_sp: .quad 0 + +.global _prom_entry +_prom_entry: .quad 0 + + .section ".text" + +C_ENTRY(_exit) + ld r1, .exit_sp_toc@toc(r2) + ld r1, 0(r1) + b the_end + + + .lcomm _stack,STACKSIZE,16 diff --git a/qemu/roms/SLOF/clients/net-snk/kernel/init.c b/qemu/roms/SLOF/clients/net-snk/kernel/init.c new file mode 100644 index 000000000..1376b6474 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/kernel/init.c @@ -0,0 +1,67 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdint.h> +#include <string.h> +#include <stdlib.h> /* malloc */ +#include <of.h> +#include <pci.h> +#include <kernel.h> +#include <cpu.h> +#include <fileio.h> + +/* Application entry point .*/ +extern int _start(unsigned char *arg_string, long len); +extern int main(int, char**); +int _start_kernel(unsigned long p0, unsigned long p1); +void * malloc_aligned(size_t size, int align); + +unsigned long exception_stack_frame; + +snk_fileio_t fd_array[FILEIO_MAX]; + +extern uint64_t tb_freq; + +extern char __client_start; +extern char __client_end; + +void * malloc_aligned(size_t size, int align) +{ + unsigned long p = (unsigned long) malloc(size + align - 1); + p = p + align - 1; + p = p & ~(align - 1); + + return (void *) p; +} + +int _start_kernel(unsigned long p0, unsigned long p1) +{ + int rc; + unsigned int timebase; + + /* initialize all file descriptor by marking them as empty */ + for(rc=0; rc<FILEIO_MAX; ++rc) + fd_array[rc].type = FILEIO_TYPE_EMPTY; + + /* this is step is e.g. resposible to initialize file descriptor 0 and 1 for STDIO */ + rc = of_glue_init(&timebase, (size_t)(unsigned long)&__client_start, + (size_t)(unsigned long)&__client_end - (size_t)(unsigned long)&__client_start); + if(rc < 0) + return -1; + + tb_freq = (uint64_t) timebase; + rc = _start((unsigned char *) p0, p1); + + of_glue_release(); + return rc; +} + diff --git a/qemu/roms/SLOF/clients/net-snk/kernel/systemcall.c b/qemu/roms/SLOF/clients/net-snk/kernel/systemcall.c new file mode 100644 index 000000000..52c45cad7 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/kernel/systemcall.c @@ -0,0 +1,176 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdint.h> +#include <stdarg.h> +#include <string.h> +#include <fileio.h> +#include <kernel.h> +#include <of.h> +#include <sys/socket.h> + +extern int vsprintf(char *, const char *, va_list); +extern void _exit(int status); + +void exit(int status); + +int open(const char* name, int flags) +{ + int fd; + + /* search free file descriptor */ + for (fd=0; fd<FILEIO_MAX; ++fd) { + if(fd_array[fd].type == FILEIO_TYPE_EMPTY) { + break; + } + } + if (fd == FILEIO_MAX) { + printf("Can not open \"%s\" because file descriptor list is full\n", name); + /* there is no free file descriptor available */ + return -2; + } + + fd_array[fd].ih = of_open(name); + if (fd_array[fd].ih == 0) + return -1; + + fd_array[fd].type = FILEIO_TYPE_FILE; + + return fd; +} + +int pre_open_ih(int fd, ihandle_t ih) +{ + if (fd_array[fd].type != FILEIO_TYPE_EMPTY) + return -2; + fd_array[fd].ih = ih; + fd_array[fd].type = FILEIO_TYPE_FILE; + + return fd; +} + +int socket(int domain, int type, int proto, char *mac_addr) +{ + uint8_t tmpbuf[8]; + int fd; + phandle_t ph; + + /* search free file descriptor */ + for (fd=0; fd<FILEIO_MAX; ++fd) { + if(fd_array[fd].type == FILEIO_TYPE_EMPTY) { + break; + } + } + if (fd == FILEIO_MAX) { + printf("Can not open socket, file descriptor list is full\n"); + /* there is no free file descriptor available */ + return -2; + } + + fd_array[fd].ih = of_interpret_1("my-parent", tmpbuf); + if (fd_array[fd].ih == 0) { + printf("Can not open socket, no parent instance\n"); + return -1; + } + ph = of_instance_to_package(fd_array[fd].ih); + if (ph == -1) { + printf("Can not open socket, no parent package\n"); + return -1; + } + if (of_get_mac(ph, mac_addr) < 0) { + printf("Can not open socket, no MAC address\n"); + return -1; + } + fd_array[fd].type = FILEIO_TYPE_SOCKET; + + return fd; +} + +int close(int fd) +{ + if (fd < 0 || fd >= FILEIO_MAX || + fd_array[fd].type == FILEIO_TYPE_EMPTY) + return -1; + if (fd_array[fd].type == FILEIO_TYPE_FILE) + of_close(fd_array[fd].ih); + fd_array[fd].type = FILEIO_TYPE_EMPTY; + return 0; +} + +ssize_t read(int fd, void *buf, size_t len) +{ + if (fd < 0 || fd >= FILEIO_MAX || + fd_array[fd].type == FILEIO_TYPE_EMPTY) + return -1; + + return of_read(fd_array[fd].ih, buf, len); +} + +ssize_t write (int fd, const void *buf, size_t len) +{ + char dest_buf[512]; + char *dest_buf_ptr; + const char *dbuf = buf; + int i; + + if (fd == 1 || fd == 2) { + dest_buf_ptr = &dest_buf[0]; + for (i = 0; i < len && i < 256; i++) + { + *dest_buf_ptr++ = *dbuf++; + if (dbuf[-1] == '\n') + *dest_buf_ptr++ = '\r'; + } + len = dest_buf_ptr - &dest_buf[0]; + buf = &dest_buf[0]; + } + + if(fd < 0 || fd >= FILEIO_MAX || + fd_array[fd].type == FILEIO_TYPE_EMPTY) + return -1; + + return of_write(fd_array[fd].ih, (void *)buf, len); +} + +ssize_t lseek (int fd, long offset, int whence) +{ + return 0; // this syscall is unused !!! +#if 0 + if (whence != 0) + return -1; + + of_seek (fd_array[fd], (unsigned int) (offset>>32), (unsigned int) (offset & 0xffffffffULL)); + + return offset; +#endif +} + +int recv(int fd, void *packet, int packet_len, int flags) +{ + return read(fd, packet, packet_len); +} + +int send(int fd, const void *packet, int packet_len, int flags) +{ + return write(fd, packet, packet_len); +} + +int sendto(int fd, const void *packet, int packet_len, int flags, + const void *sock_addr, int sock_addr_len) +{ + return send(fd, packet, packet_len, flags); +} + +void exit(int status) +{ + _exit(status); +} diff --git a/qemu/roms/SLOF/clients/net-snk/kernel/timer.c b/qemu/roms/SLOF/clients/net-snk/kernel/timer.c new file mode 100644 index 000000000..2d8705895 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/kernel/timer.c @@ -0,0 +1,39 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdint.h> +#include "kernel.h" + +//******************************************************************* +// variable "tb_freq" contains the frequency in Hz +// and is read from the device tree (setup by LLFW) in "init.c" +uint64_t tb_freq; + +//------------------------------------------------------------------- +// Read the current timebase +uint64_t get_time(void) +{ + uint64_t act; + + __asm__ __volatile__( + "0: mftbu %0 ;\ + mftbl %%r0 ; \ + mftbu %%r4 ; \ + cmpw %0,%%r4 ; \ + bne 0b; \ + sldi %0,%0,32; \ + or %0,%0,%%r0" + : "=r"(act) + : /* no inputs */ + : "r0", "r4"); + return act; +} diff --git a/qemu/roms/SLOF/clients/net-snk/libc/Makefile b/qemu/roms/SLOF/clients/net-snk/libc/Makefile new file mode 100644 index 000000000..073f039aa --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/libc/Makefile @@ -0,0 +1,45 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2011 IBM Corporation +# * All rights reserved. +# * This program and the accompanying materials +# * are made available under the terms of the BSD License +# * which accompanies this distribution, and is available at +# * http://www.opensource.org/licenses/bsd-license.php +# * +# * Contributors: +# * IBM Corporation - initial implementation +# ****************************************************************************/ + + +ifndef TOP + TOP = $(shell while ! test -e make.rules; do cd .. ; done; pwd) + export TOP +endif +include $(TOP)/make.rules + + +OBJS = sbrk.o +OBJDIRS = time/time.o +SUBDIRS = $(filter-out ./,$(dir $(OBJDIRS))) + + +all: subdirs + $(MAKE) libc-glue.o + +libc-glue.o: $(OBJS) $(OBJDIRS) + $(LD) $(LDFLAGS) $(OBJS) $(OBJDIRS) -o $@ -r + + +subdirs : + for dir in $(SUBDIRS); do \ + $(MAKE) -C $$dir DIRECTORY=$(DIRECTORY)$$dir || exit 1; \ + done + +clean: + $(RM) -f *.a *.o + @for dir in $(SUBDIRS); do \ + $(CLEAN) ; \ + $(MAKE) -C $$dir DIRECTORY=$(DIRECTORY)$$dir clean; \ + done + +include $(TOP)/make.depend diff --git a/qemu/roms/SLOF/clients/net-snk/libc/sbrk.c b/qemu/roms/SLOF/clients/net-snk/libc/sbrk.c new file mode 100644 index 000000000..2ec1b5f12 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/libc/sbrk.c @@ -0,0 +1,39 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <unistd.h> + +#define HEAP_SIZE 0x200000 + + +static char heap[HEAP_SIZE]; +static char *actptr; + +void *sbrk(int increment) +{ + char *oldptr; + + /* Called for the first time? Then init the actual pointer */ + if (!actptr) { + actptr = heap; + } + + if (actptr + increment > heap + HEAP_SIZE) { + /* Out of memory */ + return (void *)-1; + } + + oldptr = actptr; + actptr += increment; + + return oldptr; +} diff --git a/qemu/roms/SLOF/clients/net-snk/libc/time/Makefile b/qemu/roms/SLOF/clients/net-snk/libc/time/Makefile new file mode 100644 index 000000000..5db779472 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/libc/time/Makefile @@ -0,0 +1,34 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2011 IBM Corporation +# * All rights reserved. +# * This program and the accompanying materials +# * are made available under the terms of the BSD License +# * which accompanies this distribution, and is available at +# * http://www.opensource.org/licenses/bsd-license.php +# * +# * Contributors: +# * IBM Corporation - initial implementation +# ****************************************************************************/ + +ifndef TOP + TOP = $(shell while ! test -e make.rules; do cd .. ; done; pwd) + export TOP + +endif +include $(TOP)/make.rules + + +OBJS = timer.o ftime.o + + +all: time.o + +time.o: $(OBJS) + $(LD) $(LDFLAGS) -r $< -o $@ + +clean: + $(RM) -f *.o *.i *.s + +distclean mrproper: clean + +include $(TOP)/make.depend diff --git a/qemu/roms/SLOF/clients/net-snk/libc/time/ftime.c b/qemu/roms/SLOF/clients/net-snk/libc/time/ftime.c new file mode 100644 index 000000000..e092ba55d --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/libc/time/ftime.c @@ -0,0 +1,38 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + + +#include <time.h> +#include <rtas.h> +#include <stdio.h> + +time_t +time(time_t *tod) +{ + dtime ts; + + rtas_get_time_of_day(&ts); + printf("debug!!\n"); + + printf("year : %d\n", ts.year); + printf("month : %d\n", ts.month); + printf("day : %d\n", ts.day); + printf("hour : %d\n", ts.hour); + printf("minute: %d\n", ts.minute); + printf("second: %d\n", ts.second); + printf("nano : %d\n", ts.nano); + printf("debug ende\n"); + +// if(tod) +// *tod = t; + return 0; +} diff --git a/qemu/roms/SLOF/clients/net-snk/libc/time/timer.c b/qemu/roms/SLOF/clients/net-snk/libc/time/timer.c new file mode 100644 index 000000000..b17737276 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/libc/time/timer.c @@ -0,0 +1,36 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <kernel.h> +#include "time.h" + +int get_msec_ticks() +{ + return tb_freq/1000; +} + +int get_sec_ticks() +{ + return tb_freq; +} + +void set_timer(int val) +{ + asm volatile ("mtdec %0"::"r" (val)); +} + +int get_timer() +{ + int val; + asm volatile ("mfdec %0":"=r" (val)); + return val; +} diff --git a/qemu/roms/SLOF/clients/net-snk/make.rules b/qemu/roms/SLOF/clients/net-snk/make.rules new file mode 100644 index 000000000..02160936f --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/make.rules @@ -0,0 +1,25 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2008 IBM Corporation +# * All rights reserved. +# * This program and the accompanying materials +# * are made available under the terms of the BSD License +# * which accompanies this distribution, and is available at +# * http://www.opensource.org/licenses/bsd-license.php +# * +# * Contributors: +# * IBM Corporation - initial implementation +# ****************************************************************************/ + +ROOTDIR ?= $(TOP)/../.. + +include $(ROOTDIR)/make.rules + +INCLCMNDIR ?= $(ROOTDIR)/include +LIBCMNDIR ?= $(ROOTDIR)/lib +CPPFLAGS = -I$(TOP)/include -I$(LIBCMNDIR)/libc/include -I$(INCLCMNDIR) \ + -I$(LIBCMNDIR)/libbootmsg -I$(INCLCMNDIR)/$(CPUARCH) -I. +CFLAGS += $(FLAG) + +LDFLAGS = -nostdlib -q -n +ASFLAGS = -I. -I$(TOP)/include -Wa,-mregnames -I$(INCLCMNDIR)/$(CPUARCH) +DD = dd diff --git a/qemu/roms/SLOF/clients/net-snk/oflib/Makefile b/qemu/roms/SLOF/clients/net-snk/oflib/Makefile new file mode 100644 index 000000000..aad3e89ff --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/oflib/Makefile @@ -0,0 +1,41 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2008 IBM Corporation +# * All rights reserved. +# * This program and the accompanying materials +# * are made available under the terms of the BSD License +# * which accompanies this distribution, and is available at +# * http://www.opensource.org/licenses/bsd-license.php +# * +# * Contributors: +# * IBM Corporation - initial implementation +# ****************************************************************************/ + + +ifndef TOP + TOP = $(shell while ! test -e make.rules; do cd .. ; done; pwd) + export TOP +endif +include $(TOP)/make.rules + +OBJS = rtas.o of.o pci.o +OBJS2 = entry.o + +all: oflib.o + + +oflib.o: $(OBJS) $(OBJS2) + $(LD) $(LDFLAGS) $^ -o oflib.o -r + +clean: + $(RM) -f $(OBJS) $(OBJS2) oflib.o + +include $(TOP)/make.depend + + +#mrproper : clean +# rm -rf .depend +# +#depend : +# @rm -rf .depend ; touch .depend ; \ +# makedepend -v -f.depend -- $(CFLAGS) -- $(OBJS:.o=.c) 2> /dev/null ; \ +# $(CC) -M $(CFLAGS) $(OBJS2:.o=.S) >> .depend diff --git a/qemu/roms/SLOF/clients/net-snk/oflib/entry.S b/qemu/roms/SLOF/clients/net-snk/oflib/entry.S new file mode 100644 index 000000000..f08026762 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/oflib/entry.S @@ -0,0 +1,38 @@ +/****************************************************************************** + * Copyright (c) 2004, 2011 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <macros.h> + + .section ".toc","aw" # TOC entries are needed for relocation +.prom_entry_toc: + .tc _prom_entry[TC],_prom_entry + + +C_ENTRY(call_client_interface) + ld r4, .prom_entry_toc@toc(r2) # Load prom entry point + mflr r0 + std r0, 16(r1) + ld r4, 0(r4) + stdu r1, -128(r1) + std r2,40(r1) + mtctr r4 + bctrl + ld r2,40(r1) + addi r1, r1, 128 + ld r0, 16(r1) + mtlr r0 + blr + + +C_ENTRY(rtas_call_entry) + mtctr r5 + bctr diff --git a/qemu/roms/SLOF/clients/net-snk/oflib/of.c b/qemu/roms/SLOF/clients/net-snk/oflib/of.c new file mode 100644 index 000000000..5c502ac60 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/oflib/of.c @@ -0,0 +1,715 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdint.h> +#include <of.h> +#include <rtas.h> +#include <string.h> +#include <libbootmsg.h> +#include <kernel.h> + +extern void call_client_interface(of_arg_t *); + +static int claim_rc = 0; +static void* client_start; +static size_t client_size; + +static inline int +of_0_1(const char *serv) +{ + of_arg_t arg = { + p32cast serv, + 0, 1, + { 0 } + }; + + call_client_interface(&arg); + + return arg.args[0]; +} + +static inline void +of_1_0(const char *serv, int arg0) +{ + of_arg_t arg = { + p32cast serv, + 1, 0, + {arg0, 0} + }; + + call_client_interface(&arg); +} + +static inline unsigned int +of_1_1(const char *serv, int arg0) +{ + of_arg_t arg = { + p32cast serv, + 1, 1, + {arg0, 0} + }; + + call_client_interface(&arg); + return arg.args[1]; +} + +static inline unsigned int +of_1_2(const char *serv, int arg0, int *ret0) +{ + of_arg_t arg = { + p32cast serv, + 1, 2, + {arg0, 0, 0} + }; + + call_client_interface(&arg); + *ret0 = arg.args[2]; + return arg.args[1]; +} + +static inline void +of_2_0(const char *serv, int arg0, int arg1) +{ + of_arg_t arg = { + p32cast serv, + 2, 0, + {arg0, arg1, 0} + }; + + call_client_interface(&arg); +} + +static inline unsigned int +of_2_1(const char *serv, int arg0, int arg1) +{ + of_arg_t arg = { + p32cast serv, + 2, 1, + {arg0, arg1, 0} + }; + + call_client_interface(&arg); + return arg.args[2]; +} + +static inline unsigned int +of_2_2(const char *serv, int arg0, int arg1, int *ret0) +{ + of_arg_t arg = { + p32cast serv, + 2, 2, + {arg0, arg1, 0, 0} + }; + + call_client_interface(&arg); + *ret0 = arg.args[3]; + return arg.args[2]; +} + +static inline unsigned int +of_2_3(const char *serv, int arg0, int arg1, int *ret0, int *ret1) +{ + of_arg_t arg = { + p32cast serv, + 2, 3, + {arg0, arg1, 0, 0, 0} + }; + + call_client_interface(&arg); + *ret0 = arg.args[3]; + *ret1 = arg.args[4]; + return arg.args[2]; +} + +static inline void +of_3_0(const char *serv, int arg0, int arg1, int arg2) +{ + of_arg_t arg = { + p32cast serv, + 3, 0, + {arg0, arg1, arg2, 0} + }; + + call_client_interface(&arg); + return; +} + +static inline unsigned int +of_3_1(const char *serv, int arg0, int arg1, int arg2) +{ + of_arg_t arg = { + p32cast serv, + 3, 1, + {arg0, arg1, arg2, 0} + }; + + call_client_interface(&arg); + return arg.args[3]; +} + +static inline unsigned int +of_3_2(const char *serv, int arg0, int arg1, int arg2, int *ret0) +{ + of_arg_t arg = { + p32cast serv, + 3, 2, + {arg0, arg1, arg2, 0, 0} + }; + + call_client_interface(&arg); + *ret0 = arg.args[4]; + return arg.args[3]; +} + +static inline unsigned int +of_3_3(const char *serv, int arg0, int arg1, int arg2, int *ret0, int *ret1) +{ + of_arg_t arg = { + p32cast serv, + 3, 3, + {arg0, arg1, arg2, 0, 0, 0} + }; + + call_client_interface(&arg); + *ret0 = arg.args[4]; + *ret1 = arg.args[5]; + return arg.args[3]; +} + +static inline unsigned int +of_4_1(const char *serv, int arg0, int arg1, int arg2, int arg3) +{ + of_arg_t arg = { + p32cast serv, + 4, 1, + {arg0, arg1, arg2, arg3, 0} + }; + + call_client_interface(&arg); + return arg.args[4]; +} + +int +of_test(const char *name) +{ + return (int) of_1_1("test", p32cast name); +} + +int +of_interpret_1(void *s, void *ret) +{ + return of_1_2("interpret", p32cast s, ret); +} + +void +of_close(ihandle_t ihandle) +{ + of_1_0("close", ihandle); +} + +int +of_write(ihandle_t ihandle, void *s, int len) +{ + return of_3_1("write", ihandle, p32cast s, len); +} + +int +of_read(ihandle_t ihandle, void *s, int len) +{ + return of_3_1("read", ihandle, p32cast s, len); +} + +int +of_seek(ihandle_t ihandle, int poshi, int poslo) +{ + return of_3_1("seek", ihandle, poshi, poslo); +} + +int +of_getprop(phandle_t phandle, const char *name, void *buf, int len) +{ + return of_4_1("getprop", phandle, p32cast name, p32cast buf, len); +} + +phandle_t +of_peer(phandle_t phandle) +{ + return (phandle_t) of_1_1("peer", phandle); +} + +phandle_t +of_child(phandle_t phandle) +{ + return (phandle_t) of_1_1("child", phandle); +} + +phandle_t +of_parent(phandle_t phandle) +{ + return (phandle_t) of_1_1("parent", phandle); +} + +phandle_t +of_instance_to_package(ihandle_t ihandle) +{ + return (phandle_t) of_1_1("instance-to-package", ihandle); +} + + +phandle_t +of_finddevice(const char *name) +{ + return (phandle_t) of_1_1("finddevice", p32cast name); +} + +ihandle_t +of_open(const char *name) +{ + return (ihandle_t) of_1_1("open", p32cast name); +} + +void * +of_claim(void *start, unsigned int size, unsigned int align) +{ + return(void *)(long)(size_t)of_3_1("claim", p32cast start, size, align); +} + +void +of_release(void *start, unsigned int size) +{ + (void) of_2_0("release", p32cast start, size); +} + +void * +of_call_method_3(const char *name, ihandle_t ihandle, int arg0) +{ + int entry, rc; + rc = of_3_2("call-method", p32cast name, ihandle, arg0, &entry); + return rc != 0 ? 0 : (void *) (long) entry; +} + +int +vpd_read(unsigned int offset, unsigned int length, char *data) +{ + int result; + long tmp = (long) data; + result = of_3_1("rtas-read-vpd", offset, length, (int) tmp); + return result; +} + +int +vpd_write(unsigned int offset, unsigned int length, char *data) +{ + int result; + long tmp = (long) data; + result = of_3_1("rtas-write-vpd", offset, length, (int) tmp); + return result; +} + +static void +ipmi_oem_led_set(int type, int instance, int state) +{ + return of_3_0("set-led", type, instance, state); +} + +int +write_mm_log(char *data, unsigned int length, unsigned short type) +{ + long tmp = (long) data; + + ipmi_oem_led_set(2, 0, 1); + return of_3_1("write-mm-log", (int) tmp, length, type); +} + +int +of_yield(void) +{ + return of_0_1("yield"); +} + +void * +of_set_callback(void *addr) +{ + return (void *) (long) (size_t) of_1_1("set-callback", p32cast addr); +} + +void +bootmsg_warning(short id, const char *str, short lvl) +{ + (void) of_3_0("bootmsg-warning", id, lvl, p32cast str); +} + +void +bootmsg_error(short id, const char *str) +{ + (void) of_2_0("bootmsg-error", id, p32cast str); +} + +/* +void +bootmsg_debugcp(short id, const char *str, short lvl) +{ + (void) of_3_0("bootmsg-debugcp", id, lvl, p32cast str); +} + +void +bootmsg_cp(short id) +{ + (void) of_1_0("bootmsg-cp", id); +} +*/ + +#define CONFIG_SPACE 0 +#define IO_SPACE 1 +#define MEM_SPACE 2 + +#define ASSIGNED_ADDRESS_PROPERTY 0 +#define REG_PROPERTY 1 + +#define DEBUG_TRANSLATE_ADDRESS 0 +#if DEBUG_TRANSLATE_ADDRESS != 0 +#define DEBUG_TR(str...) printf(str) +#else +#define DEBUG_TR(str...) +#endif + +/** + * pci_address_type tries to find the type for which a + * mapping should be done. This is PCI specific and is done by + * looking at the first 32bit of the phys-addr in + * assigned-addresses + * + * @param node the node of the device which requests + * translatation + * @param address the address which needs to be translated + * @param prop_type the type of the property to search in (either REG_PROPERTY or ASSIGNED_ADDRESS_PROPERTY) + * @return the corresponding type (config, i/o, mem) + */ +static int +pci_address_type(phandle_t node, uint64_t address, uint8_t prop_type) +{ + char *prop_name = "assigned-addresses"; + if (prop_type == REG_PROPERTY) + prop_name = "reg"; + /* #address-cells */ + const unsigned int nac = 3; //PCI + /* #size-cells */ + const unsigned int nsc = 2; //PCI + /* up to 11 pairs of (phys-addr(3) size(2)) */ + unsigned char buf[11 * (nac + nsc) * sizeof(int)]; + unsigned int *assigned_ptr; + int result = -1; + int len; + len = of_getprop(node, prop_name, buf, 11 * (nac + nsc) * sizeof(int)); + assigned_ptr = (unsigned int *) &buf[0]; + while (len > 0) { + if ((prop_type == REG_PROPERTY) + && ((assigned_ptr[0] & 0xFF) != 0)) { + //BARs and Expansion ROM must be in assigned-addresses... so in reg + // we only look for those without config space offset set... + assigned_ptr += (nac + nsc); + len -= (nac + nsc) * sizeof(int); + continue; + } + DEBUG_TR("%s %x size %x\n", prop_name, assigned_ptr[2], + assigned_ptr[4]); + if (address >= assigned_ptr[2] + && address <= assigned_ptr[2] + assigned_ptr[4]) { + DEBUG_TR("found a match\n"); + result = (assigned_ptr[0] & 0x03000000) >> 24; + break; + } + assigned_ptr += (nac + nsc); + len -= (nac + nsc) * sizeof(int); + } + /* this can only handle 32bit memory space and should be + * removed as soon as translations for 64bit are available */ + return (result == 3) ? MEM_SPACE : result; +} + +/** + * this is a hack which returns the lower 64 bit of any number of cells + * all the higher bits will silently discarded + * right now this works pretty good as long 64 bit addresses is all we want + * + * @param addr a pointer to the first address cell + * @param nc number of cells addr points to + * @return the lower 64 bit to which addr points + */ +static uint64_t +get_dt_address(uint32_t *addr, uint32_t nc) +{ + uint64_t result = 0; + while (nc--) + result = (result << 32) | *(addr++); + return result; +} + +/** + * this functions tries to find a mapping for the given address + * it assumes that if we have #address-cells == 3 that we are trying + * to do a PCI translation + * + * @param addr a pointer to the address that should be translated + * if a translation has been found the address will + * be modified + * @param type this is required for PCI devices to find the + * correct translation + * @param ranges this is one "range" containing the translation + * information (one range = nac + pnac + nsc) + * @param nac the OF property #address-cells + * @param nsc the OF property #size-cells + * @param pnac the OF property #address-cells from the parent node + * @return -1 if no translation was possible; else 0 + */ +static int +map_one_range(uint64_t *addr, int type, uint32_t *ranges, uint32_t nac, + uint32_t nsc, uint32_t pnac) +{ + long offset; + /* cm - child mapping */ + /* pm - parent mapping */ + uint64_t cm, size, pm; + /* only check for the type if nac == 3 (PCI) */ + DEBUG_TR("type %x, nac %x\n", ranges[0], nac); + if (((ranges[0] & 0x03000000) >> 24) != type && nac == 3) + return -1; + /* okay, it is the same type let's see if we find a mapping */ + size = get_dt_address(ranges + nac + pnac, nsc); + if (nac == 3) /* skip type if PCI */ + cm = get_dt_address(ranges + 1, nac - 1); + else + cm = get_dt_address(ranges, nac); + + DEBUG_TR("\t\tchild_mapping %lx\n", cm); + DEBUG_TR("\t\tsize %lx\n", size); + DEBUG_TR("\t\t*address %lx\n", (uint64_t) * addr); + if (cm + size <= (uint64_t) * addr || cm > (uint64_t) * addr) + /* it is not inside the mapping range */ + return -1; + /* get the offset */ + offset = *addr - cm; + /* and add the offset on the parent mapping */ + if (pnac == 3) /* skip type if PCI */ + pm = get_dt_address(ranges + nac + 1, pnac - 1); + else + pm = get_dt_address(ranges + nac, pnac); + DEBUG_TR("\t\tparent_mapping %lx\n", pm); + *addr = pm + offset; + DEBUG_TR("\t\t*address %lx\n", *addr); + return 0; +} + +/** + * translate_address_dev tries to translate the device specific address + * to a host specific address by walking up in the device tree + * + * @param address a pointer to a 64 bit value which will be + * translated + * @param current_node phandle of the device from which the + * translation will be started + */ +void +translate_address_dev(uint64_t *addr, phandle_t current_node) +{ + unsigned char buf[1024]; + phandle_t parent; + unsigned int pnac; + unsigned int nac; + unsigned int nsc; + int addr_type; + int len; + unsigned int *ranges; + unsigned int one_range; + DEBUG_TR("translate address %lx, node: %lx\n", *addr, current_node); + of_getprop(current_node, "name", buf, 400); + DEBUG_TR("current node: %s\n", buf); + addr_type = + pci_address_type(current_node, *addr, ASSIGNED_ADDRESS_PROPERTY); + if (addr_type == -1) { + // check in "reg" property if not found in "assigned-addresses" + addr_type = pci_address_type(current_node, *addr, REG_PROPERTY); + } + DEBUG_TR("address_type %x\n", addr_type); + current_node = of_parent(current_node); + while (1) { + parent = of_parent(current_node); + if (!parent) { + DEBUG_TR("reached root node...\n"); + break; + } + of_getprop(current_node, "#address-cells", &nac, 4); + of_getprop(current_node, "#size-cells", &nsc, 4); + of_getprop(parent, "#address-cells", &pnac, 4); + one_range = nac + pnac + nsc; + len = of_getprop(current_node, "ranges", buf, 400); + if (len < 0) { + DEBUG_TR("no 'ranges' property; not translatable\n"); + return; + } + ranges = (unsigned int *) &buf[0]; + while (len > 0) { + if (!map_one_range + ((uint64_t *) addr, addr_type, ranges, nac, nsc, + pnac)) + /* after a successful mapping we stop + * going through the ranges */ + break; + ranges += one_range; + len -= one_range * sizeof(int); + } + DEBUG_TR("address %lx\n", *addr); + of_getprop(current_node, "name", buf, 400); + DEBUG_TR("current node: %s\n", buf); + DEBUG_TR("\t#address-cells: %x\n", nac); + DEBUG_TR("\t#size-cells: %x\n", nsc); + of_getprop(parent, "name", buf, 400); + DEBUG_TR("parent node: %s\n", buf); + DEBUG_TR("\t#address-cells: %x\n", pnac); + current_node = parent; + } +} + +static phandle_t +get_boot_device(void) +{ + char buf[1024]; + phandle_t dev = of_finddevice("/chosen"); + + if (dev == -1) { + dev = of_finddevice("/aliases"); + if (dev == -1) + return dev; + of_getprop(dev, "net", buf, 1024); + } else + of_getprop(dev, "bootpath", buf, 1024); + + return of_finddevice(buf); +} + +/** + * translate_address tries to translate the device specific address + * of the boot device to a host specific address + * + * @param address a pointer to a 64 bit value which will be + * translated + */ +void +translate_address(unsigned long *addr) +{ + translate_address_dev((uint64_t*) addr, get_boot_device()); +} + +/** + * get_puid walks up in the device tree until it finds a parent + * node without a reg property. get_puid is assuming that if the + * parent node has no reg property it has found the pci host bridge + * + * this is not the correct way to find PHBs but it seems to work + * for all our systems + * + * @param node the device for which to find the puid + * + * @return the puid or 0 + */ +uint64_t +get_puid(phandle_t node) +{ + uint64_t puid = 0; + uint64_t tmp = 0; + phandle_t curr_node, last_node; + + curr_node = last_node = of_parent(node); + + while (curr_node) { + puid = tmp; + if (of_getprop(curr_node, "reg", &tmp, 8) < 8) { + /* if the found PHB is not directly under + * root we need to translate the found address */ + translate_address_dev(&puid, last_node); + return puid; + } + last_node = curr_node; + curr_node = of_parent(curr_node); + } + + return 0; +} + +int of_get_mac(phandle_t device, char *mac) +{ + uint8_t localmac[8]; + int len; + + len = of_getprop(device, "local-mac-address", localmac, 8); + if (len <= 0) + return -1; + + if (len == 8) { + /* Some bad FDT nodes like veth use a 8-byte wide + * property instead of 6-byte wide MACs... :-( */ + memcpy(mac, &localmac[2], 6); + } + else { + memcpy(mac, localmac, 6); + } + return 0; +} + +static void +get_timebase(unsigned int *timebase) +{ + phandle_t cpu; + phandle_t cpus = of_finddevice("/cpus"); + + if (cpus == -1) + return; + + cpu = of_child(cpus); + + if (cpu == -1) + return; + + of_getprop(cpu, "timebase-frequency", timebase, 4); +} + +int of_glue_init(unsigned int * timebase, + size_t _client_start, size_t _client_size) +{ + phandle_t chosen = of_finddevice("/chosen"); + ihandle_t stdin_ih, stdout_ih; + + client_start = (void *) (long) _client_start; + client_size = _client_size; + + if (chosen == -1) + return -1; + + of_getprop(chosen, "stdin", &stdin_ih, sizeof(ihandle_t)); + of_getprop(chosen, "stdout", &stdout_ih, sizeof(ihandle_t)); + pre_open_ih(0, stdin_ih); + pre_open_ih(1, stdout_ih); + pre_open_ih(2, stdout_ih); + get_timebase(timebase); + rtas_init(); + + claim_rc=(int)(long)of_claim(client_start, client_size, 0); + + return 0; +} + +void of_glue_release(void) +{ + if (claim_rc >= 0) { + of_release(client_start, client_size); + } +} diff --git a/qemu/roms/SLOF/clients/net-snk/oflib/pci.c b/qemu/roms/SLOF/clients/net-snk/oflib/pci.c new file mode 100644 index 000000000..89003ae3e --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/oflib/pci.c @@ -0,0 +1,60 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdint.h> +#include <rtas.h> +#include <of.h> +#include <pci.h> +#include <string.h> +#include <kernel.h> +#include <cpu.h> +#include <cache.h> + +unsigned int read_io(void *addr, size_t sz) +{ + unsigned int ret; + + switch (sz) { + case 1: + ret = ci_read_8(addr); + break; + case 2: + ret = ci_read_16(addr); + break; + case 4: + ret = ci_read_32(addr); + break; + default: + ret = 0; + } + + return ret; +} + +int write_io(void *addr, unsigned int value, size_t sz) +{ + switch (sz) { + case 1: + ci_write_8(addr, value); + break; + case 2: + ci_write_16(addr, value); + break; + case 4: + ci_write_32(addr, value); + break; + default: + return -1; + } + + return 0; +} diff --git a/qemu/roms/SLOF/clients/net-snk/oflib/rtas.c b/qemu/roms/SLOF/clients/net-snk/oflib/rtas.c new file mode 100644 index 000000000..c514c7079 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/oflib/rtas.c @@ -0,0 +1,226 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdint.h> +#include <stdarg.h> +#include <stdio.h> +#include <rtas.h> +#include <of.h> +#include <kernel.h> + +typedef int rtas_arg_t; + +typedef struct { + int token; + int nargs; + int nret; + rtas_arg_t args[16]; + rtas_arg_t *rets; /* Pointer to return values in args[]. */ +} rtas_args_t; + +rtas_args_t rtas_args; + +typedef struct { + void *rtas_start; + void *rtas_entry; + int rtas_size; + phandle_t dev; +} rtas_t; + +extern rtas_t _rtas; +static int instantiate_rtas(void); +void rtas_call_entry(rtas_args_t *, void *, void *); + +int +rtas_token(const char *service) +{ + int token; + int retVal; + if (_rtas.dev == 0) + instantiate_rtas(); + + retVal = of_getprop(_rtas.dev, service, &token, sizeof(token)); + if (retVal == -1) { + token = 0; + } + return token; +} + +int +rtas_call(int token, int nargs, int nret, int *outputs, ...) +{ + va_list list; + int i; + + rtas_args.token = token; + rtas_args.nargs = nargs; + rtas_args.nret = nret; + rtas_args.rets = (rtas_arg_t *) & (rtas_args.args[nargs]); + va_start(list, outputs); + for (i = 0; i < nargs; ++i) { + rtas_args.args[i] = (rtas_arg_t) (va_arg(list, unsigned int)); + } + va_end(list); + + for (i = 0; i < nret; ++i) + rtas_args.rets[i] = 0; + + rtas_call_entry(&rtas_args, _rtas.rtas_start, _rtas.rtas_entry); + if (nret > 0 && outputs != 0) + for (i = 0; i < nret; i++) + outputs[i] = rtas_args.rets[i]; +#if 0 + printf("rtas call %x %x %x args: %x %x %x %x %x %x %x %x\n", + token, nargs, nret, + rtas_args.args[0], + rtas_args.args[1], + rtas_args.args[2], + rtas_args.args[3], + rtas_args.args[4], rtas_args.args[5], outputs[0], outputs[1]); +#endif + return ((nret > 0) ? rtas_args.rets[0] : 0); +} + +rtas_t _rtas; + +static int +instantiate_rtas(void) +{ + long long *rtas_mem_space; + ihandle_t ihandle; + + _rtas.dev = of_finddevice("/rtas"); + if ((long) _rtas.dev < 0) { + printf("\nCould not open /rtas\n"); + return -1; + } + + of_getprop(_rtas.dev, "rtas-size", &_rtas.rtas_size, + sizeof(_rtas.rtas_size)); + + if (_rtas.rtas_size <= 0) { + printf("\nSize of rtas (%x) too small to make sense\n", + _rtas.rtas_size); + return -1; + } + + rtas_mem_space = (long long *) malloc_aligned(_rtas.rtas_size, 0x100); + + if (!rtas_mem_space) { + printf("\nFailed to allocated memory for RTAS\n"); + return -1; + } + + ihandle = of_open("/rtas"); + + if ((long) ihandle < 0) { + printf("Could not open /rtas\n"); + return -1; + } + + if ((long) (_rtas.rtas_entry = of_call_method_3("instantiate-rtas", + ihandle, + p32cast rtas_mem_space)) + > 0) { + _rtas.rtas_start = rtas_mem_space; + } else { + printf("instantiate-rtas failed\n"); + return -1; + } +#if 0 + printf("\ninstantiate-rtas at %x size %x entry %x\n", + _rtas.rtas_start, _rtas.rtas_size, _rtas.rtas_entry); +#endif + return 0; +} + +static int read_pci_config_token = 0; +static int write_pci_config_token = 0; +static int ibm_read_pci_config_token = 0; +static int ibm_write_pci_config_token = 0; +static int get_time_of_day_token = 0; + +void +rtas_init() +{ + int ret; + ret = instantiate_rtas(); + if (ret) + return; + read_pci_config_token = rtas_token("read-pci-config"); + ibm_read_pci_config_token = rtas_token("ibm,read-pci-config"); + write_pci_config_token = rtas_token("write-pci-config"); + ibm_write_pci_config_token = rtas_token("ibm,write-pci-config"); + get_time_of_day_token = rtas_token("get-time-of-day"); +} + + +int +rtas_pci_config_read(long long puid, int size, int bus, int devfn, int offset) +{ + int value[2]; + + if (ibm_read_pci_config_token && puid) { + rtas_call(ibm_read_pci_config_token, 4, 2, value, + bus << 16 | devfn << 8 | offset, + puid >> 32, puid & 0xffffffffULL, size); + } else if (read_pci_config_token) { + rtas_call(read_pci_config_token, 2, 2, value, + bus << 16 | devfn << 8 | offset, size); + } + + return value[1]; +} + +int +rtas_pci_config_write(long long puid, int size, int bus, int devfn, + int offset, int value) +{ + int rc; + + if (ibm_write_pci_config_token && puid) { + rtas_call(ibm_write_pci_config_token, 5, 1, &rc, + bus << 16 | devfn << 8 | offset, + puid >> 32, puid & 0xffffffffULL, size, value); + } else + rtas_call(write_pci_config_token, 3, 1, &rc, + bus << 16 | devfn << 8 | offset, size, value); + + return rc; +} + +int +rtas_get_time_of_day(dtime * get) +{ + int rc = -1; + unsigned int year; + unsigned int month; + unsigned int day; + unsigned int hour; + unsigned int minute; + unsigned int second; + unsigned int nano; + + if (get_time_of_day_token) + rtas_call(get_time_of_day_token, 0, 8, &rc, &year, &month, &day, + &hour, &minute, &second, &nano); + + get->year = year; + get->month = month; + get->day = day; + get->hour = hour; + get->minute = minute; + get->second = second; + get->nano = nano; + + return rc; +} diff --git a/qemu/roms/SLOF/clients/net-snk/sec-client.lds b/qemu/roms/SLOF/clients/net-snk/sec-client.lds new file mode 100644 index 000000000..0eefdda95 --- /dev/null +++ b/qemu/roms/SLOF/clients/net-snk/sec-client.lds @@ -0,0 +1,43 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +OUTPUT_FORMAT("elf64-powerpc", "elf64-powerpc", "elf64-powerpc") +OUTPUT_ARCH(powerpc:common64) +ENTRY(_entry) + +SECTIONS { + .client 0x200000: + { + __client_start = .; + *(.text .stub .text.* .gnu.linkonce.t.*) + *(.sfpr .glink) + *(.rodata .rodata.* .gnu.linkonce.r.*) + KEEP (*(.opd)) + . = ALIGN(256); + *(.data .data.* .gnu.linkonce.d.*) + . = ALIGN(256); +/* *(*COM* .bss .gnu.linkonce.b.*) */ + } =0x60000000 + + .got : + { + . = ALIGN(8); + _got = .; + *(.got .toc) + _got_end = .; + } + .bss : + { + *(*COM* .bss .gnu.linkonce.b.*) + __client_end = .; + } +} diff --git a/qemu/roms/SLOF/clients/takeover/Makefile b/qemu/roms/SLOF/clients/takeover/Makefile new file mode 100644 index 000000000..f71f7ecdf --- /dev/null +++ b/qemu/roms/SLOF/clients/takeover/Makefile @@ -0,0 +1,60 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2011 IBM Corporation +# * All rights reserved. +# * This program and the accompanying materials +# * are made available under the terms of the BSD License +# * which accompanies this distribution, and is available at +# * http://www.opensource.org/licenses/bsd-license.php +# * +# * Contributors: +# * IBM Corporation - initial implementation +# ****************************************************************************/ + +include $(TOPCMNDIR)/make.rules + +SNKDIR = $(TOPCMNDIR)/clients/net-snk + +CFLAGS += -fno-builtin -I$(LIBCMNDIR)/libc/include +CFLAGS += -I$(SNKDIR)/include -I. $(CPUARCHDEF) +CFLAGS += -I$(INCLBRDDIR) -I$(INCLCMNDIR) -I$(INCLCMNDIR)/$(CPUARCH) +CFLAGS += -O2 -msoft-float -Wa,-mregnames $(RELEASE) + +OBJS = $(SNKDIR)/kernel/kernel.o +OBJS += $(SNKDIR)/oflib/oflib.o +OBJS += $(SNKDIR)/libc/time/timer.o +OBJS += entry.o main.o of.elf takeover.o +OBJS += $(LIBCMNDIR)/libelf.a $(LIBCMNDIR)/libc.a + +%.o: %.S + $(CC) $(CFLAGS) -c $^ + +all: takeover.elf + +takeover.elf: ppc32wrap.o takeover.elf32 + @echo " ====== Building $@ ======" + $(LD) -N -melf32ppclinux -static -nostdlib \ + -Ttext=0x400000 -Tdata=0x400100 \ + $(LDFLAGS) $^ -o $@ + +takeover.elf64: entry.o main.o takeover.o $(SNKDIR)/libc/time/timer.o of.elf + $(MAKE) -C $(LIBCMNDIR) libc + $(MAKE) -C $(CLIENTSDIR) + $(LD) $(LDFLAGS) -o $@ -Tclient.lds $(OBJS) + +of.elf: ../../boot_rom.bin + $(OBJCOPY) --input-target=binary --binary-architecture=powerpc -O elf64-powerpc $< $@ + +takeover.elf32: takeover.elf64 + $(OBJCOPY) -O binary $^ takeover.tmp + $(OBJCOPY) --input-target=binary --binary-architecture=powerpc -O elf32-powerpc takeover.tmp $@ + +ppc32wrap.o: ppc32wrap.S + $(CROSS)gcc -m32 $(CFLAGS) -c $< -o $@ + +clean distclean: + $(MAKE) -C $(LIBCMNDIR) $@ + $(MAKE) -C $(CLIENTSDIR) $@ + $(RM) *.o *.bin *.elf + $(RM) takeover.elf32 takeover.elf64 takeover.tmp +%.o: %.oco + cp -f $< $@ diff --git a/qemu/roms/SLOF/clients/takeover/client.lds b/qemu/roms/SLOF/clients/takeover/client.lds new file mode 100644 index 000000000..2701d8e1e --- /dev/null +++ b/qemu/roms/SLOF/clients/takeover/client.lds @@ -0,0 +1,60 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +OUTPUT_FORMAT("elf64-powerpc", "elf64-powerpc", "elf64-powerpc") +OUTPUT_ARCH(powerpc:common64) +ENTRY(_entry) + +SECTIONS { + .client 0x400100: + { + __client_start = .; + *(.start) + *(.text .stub .text.* .gnu.linkonce.t.*) + *(.sfpr .glink) + *(.rodata .rodata.* .gnu.linkonce.r.*) + KEEP (*(.opd)) + . = ALIGN(256); + *(.data .data.* .gnu.linkonce.d.*) + . = ALIGN(256); + } =0x60000000 + + .diag_table : + { + _diag_init_start = .; + *(.diag_init) + _diag_init_end = .; + } + .lowmem : + { + _lowmem_start = .; + *(.lowmem) + _lowmem_end = .; + } + + .got : + { + . = ALIGN(8); + _got = .; + *(.got .toc) + _got_end = .; + } + .comment : { *(.comment) } + .branch_lt : { *(.branch_lt) } + .bss : + { + __bssStart = .; + *(*COM* .bss .gnu.linkonce.b.*) + __client_end = .; + __bssSize = . - __bssStart; + } +} diff --git a/qemu/roms/SLOF/clients/takeover/entry.S b/qemu/roms/SLOF/clients/takeover/entry.S new file mode 100644 index 000000000..a1030eb40 --- /dev/null +++ b/qemu/roms/SLOF/clients/takeover/entry.S @@ -0,0 +1,99 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <macros.h> +#include "takeover.h" + + .globl _wrapclient + .section ".start" + .align 3 + +_wrapclient: + bcl 20,31,over # branch after pointer table +base: + .align 3 +.LCgot: .quad _got-base+0x8000 +over: + mflr r8 # gpr 8 is the base + ld r2, .LCgot-base(r8) # load got pointer + add r2, r2, r8 # add base + li 14, 0 + oris 14, 14, __bssSize@h + ori 14, 14, __bssSize@l + addi 14,14,7 + srwi 14,14,3 + mtctr 14 + li 14, 0 + oris 14, 14, __bssStart@h + ori 14, 14, __bssStart@l + subi 14, 14, 8 + li 15, 0 +1: + stdu 15,8(14) + bdnz 1b + + bl ._entry + + + + .globl slaveLoopNoTakeover +slaveLoopNoTakeover: + mr 28,3 + + li 14,0 + oris 14, 14, slaveQuitt@h + ori 14, 14, slaveQuitt@l + + li 3,0 + std 3,0(14) +1: + ld 3,0(14) + cmpld 3,28 + bne 1b + + li 3,0 + std 3,0(14) + + LOAD64(r3, (TAKEOVERBASEADDRESS+0x150)) + mtctr r3 + bctr + + + .globl slaveLoop +slaveLoop: + mr 28,3 + li r3, 0x5124 + li r0, -1; .long 0x44000022 + + li 14,0 + oris 14, 14, slaveQuitt@h + ori 14, 14, slaveQuitt@l + li 3,0 + std 3,0(14) +1: + ld 3,0(14) + cmpld 3,28 + bne 1b + + li 3,0 + std 3,0(14) + + LOAD64(r3, (TAKEOVERBASEADDRESS+0x150)) + mtctr r3 + bctr + + +C_ENTRY(m_sync) + isync + sync + nop + blr diff --git a/qemu/roms/SLOF/clients/takeover/main.c b/qemu/roms/SLOF/clients/takeover/main.c new file mode 100644 index 000000000..360d8eaed --- /dev/null +++ b/qemu/roms/SLOF/clients/takeover/main.c @@ -0,0 +1,163 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <of.h> +#include <pci.h> +#include <cpu.h> +#include <ioctl.h> +#include <takeover.h> + +extern void call_client_interface(of_arg_t *); +extern void m_sync(void); + +int callback(int argc, char *argv[]); + +#define boot_rom_bin_start _binary_______boot_rom_bin_start +#define boot_rom_bin_end _binary_______boot_rom_bin_end + +extern char boot_rom_bin_start; +extern char boot_rom_bin_end; + +#if defined(__GNUC__) +# define UNUSED __attribute__((unused)) +#else +# define UNUSED +#endif + +void * +sbrk(int incr) +{ + return (void *) -1; +} + +static void +doWait(void) +{ + static const char *wheel = "|/-\\"; + static int i = 0; + volatile int dly = 0xf0000; + while (dly--) + asm volatile (" nop "); + printf("\b%c", wheel[i++]); + i &= 0x3; +} + +static void +quiesce(void) +{ + of_arg_t arg = { + p32cast "quiesce", + 0, 0, + }; + call_client_interface(&arg); +} + +static int +startCpu(int num, int addr, int reg) +{ + of_arg_t arg = { + p32cast "start-cpu", + 3, 0, + {num, addr, reg} + }; + call_client_interface(&arg); + return arg.args[3]; +} + +volatile unsigned long slaveQuitt; +int takeoverFlag; + +void +main(int argc, char *argv[]) +{ + phandle_t cpus; + phandle_t cpu; + unsigned long slaveMask; + extern int slaveLoop[]; + extern int slaveLoopNoTakeover[]; + int index = 0; + int delay = 100; + unsigned long reg; + unsigned long msr; + + asm volatile ("mfmsr %0":"=r" (msr)); + if (msr & 0x1000000000000000) + takeoverFlag = 0; + else + takeoverFlag = 1; + + cpus = of_finddevice("/cpus"); + cpu = of_child(cpus); + slaveMask = 0; + while (cpu) { + char devType[100]; + *devType = '\0'; + of_getprop(cpu, "device_type", devType, sizeof(devType)); + if (strcmp(devType, "cpu") == 0) { + of_getprop(cpu, "reg", ®, sizeof(reg)); + if (index) { + printf("\r\n takeover on cpu%d (%x, %lx) ", index, + cpu, reg); + slaveQuitt = -1; + if (takeoverFlag) + startCpu(cpu, (int)(unsigned long)slaveLoop, index); + else + startCpu(cpu, (int)(unsigned long)slaveLoopNoTakeover, + index); + slaveMask |= 0x1 << index; + delay = 100; + while (delay-- && slaveQuitt) + doWait(); + } + index++; + } + cpu = of_peer(cpu); + } + + + printf("\r\n takeover on master cpu "); + quiesce(); + + delay = 5; + while (delay--) + doWait(); + if (takeoverFlag) + takeover(); + + memcpy((void*)TAKEOVERBASEADDRESS, &boot_rom_bin_start, &boot_rom_bin_end - &boot_rom_bin_start); + flush_cache((void *)TAKEOVERBASEADDRESS, &boot_rom_bin_end - &boot_rom_bin_start); + index = 0; + + while (slaveMask) { + m_sync(); + unsigned long shifter = 0x1 << index; + if (shifter & slaveMask) { + slaveQuitt = index; + while (slaveQuitt) + m_sync(); + slaveMask &= ~shifter; + } + index++; + } + + asm volatile(" mtctr %0 ; bctr " : : "r" (TAKEOVERBASEADDRESS+0x180) ); +} + +int +callback(int argc, char *argv[]) +{ + /* Dummy, only for takeover */ + return (0); +} diff --git a/qemu/roms/SLOF/clients/takeover/ppc32wrap.S b/qemu/roms/SLOF/clients/takeover/ppc32wrap.S new file mode 100644 index 000000000..90afa268d --- /dev/null +++ b/qemu/roms/SLOF/clients/takeover/ppc32wrap.S @@ -0,0 +1,30 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + + .globl _start + .section ".text" + .align 3 + +_start: + nop + bl over + .llong 0x9000000000003000 +over: + li 14, 0 + oris 14, 14, _binary_takeover_tmp_start@h + ori 14, 14, _binary_takeover_tmp_start@l + mtsrr0 14 + mflr 15 + ld 14,0(15) + mtsrr1 14 + rfid + diff --git a/qemu/roms/SLOF/clients/takeover/takeover.h b/qemu/roms/SLOF/clients/takeover/takeover.h new file mode 100644 index 000000000..f348e2359 --- /dev/null +++ b/qemu/roms/SLOF/clients/takeover/takeover.h @@ -0,0 +1,23 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#if defined(CPU_CBEA) +#define TAKEOVERBASEADDRESS 0x0e000000 +#elif defined(CPU_PPC970) +#define TAKEOVERBASEADDRESS 0x00000000 +#else +#error no processor specified +#endif + +#ifndef __ASSEMBLER__ +int takeover(void); +#endif diff --git a/qemu/roms/SLOF/clients/takeover/takeover.oco b/qemu/roms/SLOF/clients/takeover/takeover.oco Binary files differnew file mode 100644 index 000000000..6e2a1a6ab --- /dev/null +++ b/qemu/roms/SLOF/clients/takeover/takeover.oco |