diff options
Diffstat (limited to 'roms/SLOF/clients/net-snk/app')
47 files changed, 10901 insertions, 0 deletions
| diff --git a/roms/SLOF/clients/net-snk/app/Makefile b/roms/SLOF/clients/net-snk/app/Makefile new file mode 100644 index 00000000..8b0c08f1 --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/biosemu/Makefile b/roms/SLOF/clients/net-snk/app/biosemu/Makefile new file mode 100644 index 00000000..3a07ada3 --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/biosemu/biosemu.c b/roms/SLOF/clients/net-snk/app/biosemu/biosemu.c new file mode 100644 index 00000000..82a763ac --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/biosemu/biosemu.h b/roms/SLOF/clients/net-snk/app/biosemu/biosemu.h new file mode 100644 index 00000000..28b7ab88 --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/biosemu/debug.c b/roms/SLOF/clients/net-snk/app/biosemu/debug.c new file mode 100644 index 00000000..2fce2449 --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/biosemu/debug.h b/roms/SLOF/clients/net-snk/app/biosemu/debug.h new file mode 100644 index 00000000..46a026ae --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/biosemu/device.c b/roms/SLOF/clients/net-snk/app/biosemu/device.c new file mode 100644 index 00000000..514b87e6 --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/biosemu/device.h b/roms/SLOF/clients/net-snk/app/biosemu/device.h new file mode 100644 index 00000000..425dd3ca --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/biosemu/interrupt.c b/roms/SLOF/clients/net-snk/app/biosemu/interrupt.c new file mode 100644 index 00000000..ac3f5b43 --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/biosemu/interrupt.h b/roms/SLOF/clients/net-snk/app/biosemu/interrupt.h new file mode 100644 index 00000000..11755e10 --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/biosemu/io.c b/roms/SLOF/clients/net-snk/app/biosemu/io.c new file mode 100644 index 00000000..e9c08932 --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/biosemu/io.h b/roms/SLOF/clients/net-snk/app/biosemu/io.h new file mode 100644 index 00000000..5a0bb4b4 --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/biosemu/mem.c b/roms/SLOF/clients/net-snk/app/biosemu/mem.c new file mode 100644 index 00000000..1a620755 --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/biosemu/mem.h b/roms/SLOF/clients/net-snk/app/biosemu/mem.h new file mode 100644 index 00000000..f0fbad96 --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/biosemu/vbe.c b/roms/SLOF/clients/net-snk/app/biosemu/vbe.c new file mode 100644 index 00000000..957a1f2a --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/biosemu/vbe.h b/roms/SLOF/clients/net-snk/app/biosemu/vbe.h new file mode 100644 index 00000000..17e98263 --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/main.c b/roms/SLOF/clients/net-snk/app/main.c new file mode 100644 index 00000000..90e14b37 --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/netapps/Makefile b/roms/SLOF/clients/net-snk/app/netapps/Makefile new file mode 100644 index 00000000..70b990a7 --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/netapps/args.c b/roms/SLOF/clients/net-snk/app/netapps/args.c new file mode 100644 index 00000000..52215e6a --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/netapps/args.h b/roms/SLOF/clients/net-snk/app/netapps/args.h new file mode 100644 index 00000000..b80982a3 --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/netapps/netapps.h b/roms/SLOF/clients/net-snk/app/netapps/netapps.h new file mode 100644 index 00000000..18e607f5 --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/netapps/netboot.c b/roms/SLOF/clients/net-snk/app/netapps/netboot.c new file mode 100644 index 00000000..cf20b591 --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/netapps/ping.c b/roms/SLOF/clients/net-snk/app/netapps/ping.c new file mode 100644 index 00000000..2c7dadbd --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/netlib/Makefile b/roms/SLOF/clients/net-snk/app/netlib/Makefile new file mode 100644 index 00000000..df09bf82 --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/netlib/bootp.c b/roms/SLOF/clients/net-snk/app/netlib/bootp.c new file mode 100644 index 00000000..1bc6efe5 --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/netlib/dhcp.c b/roms/SLOF/clients/net-snk/app/netlib/dhcp.c new file mode 100644 index 00000000..5f26f3af --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/netlib/dhcp.h b/roms/SLOF/clients/net-snk/app/netlib/dhcp.h new file mode 100644 index 00000000..69dd49d4 --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/netlib/dhcpv6.c b/roms/SLOF/clients/net-snk/app/netlib/dhcpv6.c new file mode 100644 index 00000000..4deef30f --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/netlib/dhcpv6.h b/roms/SLOF/clients/net-snk/app/netlib/dhcpv6.h new file mode 100644 index 00000000..078a9f11 --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/netlib/dns.c b/roms/SLOF/clients/net-snk/app/netlib/dns.c new file mode 100644 index 00000000..0ab1346c --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/netlib/dns.h b/roms/SLOF/clients/net-snk/app/netlib/dns.h new file mode 100644 index 00000000..82eea4e4 --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/netlib/ethernet.c b/roms/SLOF/clients/net-snk/app/netlib/ethernet.c new file mode 100644 index 00000000..bbfd6d1c --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/netlib/ethernet.h b/roms/SLOF/clients/net-snk/app/netlib/ethernet.h new file mode 100644 index 00000000..e541c8f8 --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/netlib/icmpv6.c b/roms/SLOF/clients/net-snk/app/netlib/icmpv6.c new file mode 100644 index 00000000..be6cc110 --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/netlib/icmpv6.h b/roms/SLOF/clients/net-snk/app/netlib/icmpv6.h new file mode 100644 index 00000000..32797973 --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/netlib/ipv4.c b/roms/SLOF/clients/net-snk/app/netlib/ipv4.c new file mode 100644 index 00000000..8185de5e --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/netlib/ipv4.h b/roms/SLOF/clients/net-snk/app/netlib/ipv4.h new file mode 100644 index 00000000..eb719f8b --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/netlib/ipv6.c b/roms/SLOF/clients/net-snk/app/netlib/ipv6.c new file mode 100644 index 00000000..0cb0a2e7 --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/netlib/ipv6.h b/roms/SLOF/clients/net-snk/app/netlib/ipv6.h new file mode 100644 index 00000000..b496364f --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/netlib/ndp.c b/roms/SLOF/clients/net-snk/app/netlib/ndp.c new file mode 100644 index 00000000..ed9d61f4 --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/netlib/ndp.h b/roms/SLOF/clients/net-snk/app/netlib/ndp.h new file mode 100644 index 00000000..ee5235fe --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/netlib/tcp.c b/roms/SLOF/clients/net-snk/app/netlib/tcp.c new file mode 100644 index 00000000..5511aa00 --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/netlib/tcp.h b/roms/SLOF/clients/net-snk/app/netlib/tcp.h new file mode 100644 index 00000000..375afd77 --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/netlib/tftp.c b/roms/SLOF/clients/net-snk/app/netlib/tftp.c new file mode 100644 index 00000000..0a7c0ec6 --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/netlib/tftp.h b/roms/SLOF/clients/net-snk/app/netlib/tftp.h new file mode 100644 index 00000000..1cf1266f --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/netlib/udp.c b/roms/SLOF/clients/net-snk/app/netlib/udp.c new file mode 100644 index 00000000..db29bc90 --- /dev/null +++ b/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/roms/SLOF/clients/net-snk/app/netlib/udp.h b/roms/SLOF/clients/net-snk/app/netlib/udp.h new file mode 100644 index 00000000..1ba9332c --- /dev/null +++ b/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 | 
