M2DIR=../..
include $(M2DIR)/software/common.mak
-OBJECTS=crt0.o isr.o sdram.o main.o microudp.o tftp.o boot-helper.o boot.o dataflow.o
+OBJECTS=crt0.o isr.o sdram.o main.o boot-helper.o boot.o dataflow.o
all: bios.bin
%.elf:
$(LD) $(LDFLAGS) -T $< -N -o $@ $(OBJECTS) \
+ -L$(M2DIR)/software/libnet \
-L$(M2DIR)/software/libbase \
-L$(M2DIR)/software/libcompiler-rt \
- -lbase -lcompiler-rt
+ -lnet -lbase -lcompiler-rt
chmod -x $@
main.o: main.c
libs:
make -C $(M2DIR)/software/libcompiler-rt
make -C $(M2DIR)/software/libbase
+ make -C $(M2DIR)/software/libnet
flash: bios.bin
m1nor bios.bin
#include <hw/mem.h>
-#include "microudp.h"
-#include "tftp.h"
+#include <net/microudp.h>
+#include <net/tftp.h>
#include "boot.h"
extern int rescue;
#include <timer.h>
#include <hw/mem.h>
+#include <net/microudp.h>
#include "sdram.h"
#include "dataflow.h"
#include "boot.h"
-#include "microudp.h"
enum {
CSR_IE = 1, CSR_IM, CSR_IP, CSR_ICC, CSR_DCC, CSR_CC, CSR_CFG, CSR_EBA,
+++ /dev/null
-#include <stdio.h>
-#include <system.h>
-#include <crc.h>
-#include <timer.h>
-#include <hw/csr.h>
-#include <hw/flags.h>
-#include <hw/mem.h>
-
-#include "microudp.h"
-
-#define ETHERTYPE_ARP 0x0806
-#define ETHERTYPE_IP 0x0800
-
-struct ethernet_header {
- unsigned char preamble[8];
- unsigned char destmac[6];
- unsigned char srcmac[6];
- unsigned short ethertype;
-} __attribute__((packed));
-
-static void fill_eth_header(struct ethernet_header *h, const unsigned char *destmac, const unsigned char *srcmac, unsigned short ethertype)
-{
- int i;
-
- for(i=0;i<7;i++)
- h->preamble[i] = 0x55;
- h->preamble[7] = 0xd5;
- for(i=0;i<6;i++)
- h->destmac[i] = destmac[i];
- for(i=0;i<6;i++)
- h->srcmac[i] = srcmac[i];
- h->ethertype = ethertype;
-}
-
-#define ARP_HWTYPE_ETHERNET 0x0001
-#define ARP_PROTO_IP 0x0800
-
-#define ARP_OPCODE_REQUEST 0x0001
-#define ARP_OPCODE_REPLY 0x0002
-
-struct arp_frame {
- unsigned short hwtype;
- unsigned short proto;
- unsigned char hwsize;
- unsigned char protosize;
- unsigned short opcode;
- unsigned char sender_mac[6];
- unsigned int sender_ip;
- unsigned char target_mac[6];
- unsigned int target_ip;
- unsigned char padding[18];
-} __attribute__((packed));
-
-#define IP_IPV4 0x45
-#define IP_DONT_FRAGMENT 0x4000
-#define IP_TTL 64
-#define IP_PROTO_UDP 0x11
-
-struct ip_header {
- unsigned char version;
- unsigned char diff_services;
- unsigned short total_length;
- unsigned short identification;
- unsigned short fragment_offset;
- unsigned char ttl;
- unsigned char proto;
- unsigned short checksum;
- unsigned int src_ip;
- unsigned int dst_ip;
-} __attribute__((packed));
-
-struct udp_header {
- unsigned short src_port;
- unsigned short dst_port;
- unsigned short length;
- unsigned short checksum;
-} __attribute__((packed));
-
-struct udp_frame {
- struct ip_header ip;
- struct udp_header udp;
- char payload[];
-} __attribute__((packed));
-
-struct ethernet_frame {
- struct ethernet_header eth_header;
- union {
- struct arp_frame arp;
- struct udp_frame udp;
- } contents;
-} __attribute__((packed));
-
-typedef union {
- struct ethernet_frame frame;
- unsigned char raw[1532];
-} ethernet_buffer;
-
-
-static unsigned int rxlen;
-static ethernet_buffer *rxbuffer;
-static ethernet_buffer *rxbuffer0;
-static ethernet_buffer *rxbuffer1;
-static unsigned int txlen;
-static ethernet_buffer *txbuffer;
-
-static void send_packet(void)
-{
- unsigned int crc;
-
- crc = crc32(&txbuffer->raw[8], txlen-8);
- txbuffer->raw[txlen ] = (crc & 0xff);
- txbuffer->raw[txlen+1] = (crc & 0xff00) >> 8;
- txbuffer->raw[txlen+2] = (crc & 0xff0000) >> 16;
- txbuffer->raw[txlen+3] = (crc & 0xff000000) >> 24;
- txlen += 4;
- minimac_tx_count_write(txlen);
- minimac_tx_start_write(1);
- while(!(minimac_ev_pending_read() & MINIMAC_EV_TX));
- minimac_ev_pending_write(MINIMAC_EV_TX);
-}
-
-static unsigned char my_mac[6];
-static unsigned int my_ip;
-
-/* ARP cache - one entry only */
-static unsigned char cached_mac[6];
-static unsigned int cached_ip;
-
-static void process_arp(void)
-{
- const struct arp_frame *rx_arp = &rxbuffer->frame.contents.arp;
- struct arp_frame *tx_arp = &txbuffer->frame.contents.arp;
-
- if(rxlen < 68) return;
- if(rx_arp->hwtype != ARP_HWTYPE_ETHERNET) return;
- if(rx_arp->proto != ARP_PROTO_IP) return;
- if(rx_arp->hwsize != 6) return;
- if(rx_arp->protosize != 4) return;
- if(rx_arp->opcode == ARP_OPCODE_REPLY) {
- if(rx_arp->sender_ip == cached_ip) {
- int i;
- for(i=0;i<6;i++)
- cached_mac[i] = rx_arp->sender_mac[i];
- }
- return;
- }
- if(rx_arp->opcode == ARP_OPCODE_REQUEST) {
- if(rx_arp->target_ip == my_ip) {
- int i;
-
- fill_eth_header(&txbuffer->frame.eth_header,
- rx_arp->sender_mac,
- my_mac,
- ETHERTYPE_ARP);
- txlen = 68;
- tx_arp->hwtype = ARP_HWTYPE_ETHERNET;
- tx_arp->proto = ARP_PROTO_IP;
- tx_arp->hwsize = 6;
- tx_arp->protosize = 4;
- tx_arp->opcode = ARP_OPCODE_REPLY;
- tx_arp->sender_ip = my_ip;
- for(i=0;i<6;i++)
- tx_arp->sender_mac[i] = my_mac[i];
- tx_arp->target_ip = rx_arp->sender_ip;
- for(i=0;i<6;i++)
- tx_arp->target_mac[i] = rx_arp->sender_mac[i];
- send_packet();
- }
- return;
- }
-}
-
-static const unsigned char broadcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
-
-int microudp_arp_resolve(unsigned int ip)
-{
- struct arp_frame *arp = &txbuffer->frame.contents.arp;
- int i;
- int tries;
- int timeout;
-
- if(cached_ip == ip) {
- for(i=0;i<6;i++)
- if(cached_mac[i]) return 1;
- }
- cached_ip = ip;
- for(i=0;i<6;i++)
- cached_mac[i] = 0;
-
- for(tries=0;tries<5;tries++) {
- /* Send an ARP request */
- fill_eth_header(&txbuffer->frame.eth_header,
- broadcast,
- my_mac,
- ETHERTYPE_ARP);
- txlen = 68;
- arp->hwtype = ARP_HWTYPE_ETHERNET;
- arp->proto = ARP_PROTO_IP;
- arp->hwsize = 6;
- arp->protosize = 4;
- arp->opcode = ARP_OPCODE_REQUEST;
- arp->sender_ip = my_ip;
- for(i=0;i<6;i++)
- arp->sender_mac[i] = my_mac[i];
- arp->target_ip = ip;
- for(i=0;i<6;i++)
- arp->target_mac[i] = 0;
- send_packet();
-
- /* Do we get a reply ? */
- for(timeout=0;timeout<2000000;timeout++) {
- microudp_service();
- for(i=0;i<6;i++)
- if(cached_mac[i]) return 1;
- }
- }
-
- return 0;
-}
-
-static unsigned short ip_checksum(unsigned int r, void *buffer, unsigned int length, int complete)
-{
- unsigned char *ptr;
- unsigned int i;
-
- ptr = (unsigned char *)buffer;
- length >>= 1;
-
- for(i=0;i<length;i++)
- r += ((unsigned int)(ptr[2*i]) << 8)|(unsigned int)(ptr[2*i+1]) ;
-
- /* Add overflows */
- while(r >> 16)
- r = (r & 0xffff) + (r >> 16);
-
- if(complete) {
- r = ~r;
- r &= 0xffff;
- if(r == 0) r = 0xffff;
- }
- return r;
-}
-
-void *microudp_get_tx_buffer(void)
-{
- return txbuffer->frame.contents.udp.payload;
-}
-
-struct pseudo_header {
- unsigned int src_ip;
- unsigned int dst_ip;
- unsigned char zero;
- unsigned char proto;
- unsigned short length;
-} __attribute__((packed));
-
-int microudp_send(unsigned short src_port, unsigned short dst_port, unsigned int length)
-{
- struct pseudo_header h;
- unsigned int r;
-
- if((cached_mac[0] == 0) && (cached_mac[1] == 0) && (cached_mac[2] == 0)
- && (cached_mac[3] == 0) && (cached_mac[4] == 0) && (cached_mac[5] == 0))
- return 0;
-
- txlen = length + sizeof(struct ethernet_header) + sizeof(struct udp_frame) + 8;
- if(txlen < 72) txlen = 72;
-
- fill_eth_header(&txbuffer->frame.eth_header,
- cached_mac,
- my_mac,
- ETHERTYPE_IP);
-
- txbuffer->frame.contents.udp.ip.version = IP_IPV4;
- txbuffer->frame.contents.udp.ip.diff_services = 0;
- txbuffer->frame.contents.udp.ip.total_length = length + sizeof(struct udp_frame);
- txbuffer->frame.contents.udp.ip.identification = 0;
- txbuffer->frame.contents.udp.ip.fragment_offset = IP_DONT_FRAGMENT;
- txbuffer->frame.contents.udp.ip.ttl = IP_TTL;
- h.proto = txbuffer->frame.contents.udp.ip.proto = IP_PROTO_UDP;
- txbuffer->frame.contents.udp.ip.checksum = 0;
- h.src_ip = txbuffer->frame.contents.udp.ip.src_ip = my_ip;
- h.dst_ip = txbuffer->frame.contents.udp.ip.dst_ip = cached_ip;
- txbuffer->frame.contents.udp.ip.checksum = ip_checksum(0, &txbuffer->frame.contents.udp.ip,
- sizeof(struct ip_header), 1);
-
- txbuffer->frame.contents.udp.udp.src_port = src_port;
- txbuffer->frame.contents.udp.udp.dst_port = dst_port;
- h.length = txbuffer->frame.contents.udp.udp.length = length + sizeof(struct udp_header);
- txbuffer->frame.contents.udp.udp.checksum = 0;
-
- h.zero = 0;
- r = ip_checksum(0, &h, sizeof(struct pseudo_header), 0);
- if(length & 1) {
- txbuffer->frame.contents.udp.payload[length] = 0;
- length++;
- }
- r = ip_checksum(r, &txbuffer->frame.contents.udp.udp,
- sizeof(struct udp_header)+length, 1);
- txbuffer->frame.contents.udp.udp.checksum = r;
-
- send_packet();
-
- return 1;
-}
-
-static udp_callback rx_callback;
-
-static void process_ip(void)
-{
- if(rxlen < (sizeof(struct ethernet_header)+sizeof(struct udp_frame))) return;
- /* We don't verify UDP and IP checksums and rely on the Ethernet checksum solely */
- if(rxbuffer->frame.contents.udp.ip.version != IP_IPV4) return;
- // check disabled for QEMU compatibility
- //if(rxbuffer->frame.contents.udp.ip.diff_services != 0) return;
- if(rxbuffer->frame.contents.udp.ip.total_length < sizeof(struct udp_frame)) return;
- // check disabled for QEMU compatibility
- //if(rxbuffer->frame.contents.udp.ip.fragment_offset != IP_DONT_FRAGMENT) return;
- if(rxbuffer->frame.contents.udp.ip.proto != IP_PROTO_UDP) return;
- if(rxbuffer->frame.contents.udp.ip.dst_ip != my_ip) return;
- if(rxbuffer->frame.contents.udp.udp.length < sizeof(struct udp_header)) return;
-
- if(rx_callback)
- rx_callback(rxbuffer->frame.contents.udp.ip.src_ip, rxbuffer->frame.contents.udp.udp.src_port, rxbuffer->frame.contents.udp.udp.dst_port, rxbuffer->frame.contents.udp.payload, rxbuffer->frame.contents.udp.udp.length-sizeof(struct udp_header));
-}
-
-void microudp_set_callback(udp_callback callback)
-{
- rx_callback = callback;
-}
-
-static void process_frame(void)
-{
- int i;
- unsigned int received_crc;
- unsigned int computed_crc;
-
- flush_cpu_dcache();
- for(i=0;i<7;i++)
- if(rxbuffer->frame.eth_header.preamble[i] != 0x55) return;
- if(rxbuffer->frame.eth_header.preamble[7] != 0xd5) return;
- received_crc = ((unsigned int)rxbuffer->raw[rxlen-1] << 24)
- |((unsigned int)rxbuffer->raw[rxlen-2] << 16)
- |((unsigned int)rxbuffer->raw[rxlen-3] << 8)
- |((unsigned int)rxbuffer->raw[rxlen-4]);
- computed_crc = crc32(&rxbuffer->raw[8], rxlen-12);
- if(received_crc != computed_crc) return;
-
- rxlen -= 4; /* strip CRC here to be consistent with TX */
- if(rxbuffer->frame.eth_header.ethertype == ETHERTYPE_ARP) process_arp();
- else if(rxbuffer->frame.eth_header.ethertype == ETHERTYPE_IP) process_ip();
-}
-
-void microudp_start(unsigned char *macaddr, unsigned int ip)
-{
- int i;
-
- minimac_ev_pending_write(MINIMAC_EV_RX0 | MINIMAC_EV_RX1 | MINIMAC_EV_TX);
-
- rxbuffer0 = (ethernet_buffer *)MINIMAC_RX0_BASE;
- rxbuffer1 = (ethernet_buffer *)MINIMAC_RX1_BASE;
- txbuffer = (ethernet_buffer *)MINIMAC_TX_BASE;
-
- for(i=0;i<6;i++)
- my_mac[i] = macaddr[i];
- my_ip = ip;
-
- cached_ip = 0;
- for(i=0;i<6;i++)
- cached_mac[i] = 0;
-
- rx_callback = (udp_callback)0;
-}
-
-void microudp_service(void)
-{
- if(minimac_ev_pending_read() & MINIMAC_EV_RX0) {
- rxlen = minimac_rx_count_0_read();
- rxbuffer = rxbuffer0;
- process_frame();
- minimac_ev_pending_write(MINIMAC_EV_RX0);
- }
- if(minimac_ev_pending_read() & MINIMAC_EV_RX1) {
- rxlen = minimac_rx_count_1_read();
- rxbuffer = rxbuffer1;
- process_frame();
- minimac_ev_pending_write(MINIMAC_EV_RX1);
- }
-}
-
-void ethreset(void)
-{
- minimac_phy_reset_write(0);
- busy_wait(2);
- /* that pesky ethernet PHY needs two resets at times... */
- minimac_phy_reset_write(1);
- busy_wait(2);
- minimac_phy_reset_write(0);
- busy_wait(2);
-}
+++ /dev/null
-#ifndef __MICROUDP_H
-#define __MICROUDP_H
-
-#define IPTOINT(a, b, c, d) ((a << 24)|(b << 16)|(c << 8)|d)
-
-#define MICROUDP_BUFSIZE (5*1532)
-
-typedef void (*udp_callback)(unsigned int src_ip, unsigned short src_port, unsigned short dst_port, void *data, unsigned int length);
-
-void microudp_start(unsigned char *macaddr, unsigned int ip);
-int microudp_arp_resolve(unsigned int ip);
-void *microudp_get_tx_buffer(void);
-int microudp_send(unsigned short src_port, unsigned short dst_port, unsigned int length);
-void microudp_set_callback(udp_callback callback);
-void microudp_service(void);
-
-void ethreset(void);
-
-#endif /* __MICROUDP_H */
+++ /dev/null
-#include <stdint.h>
-#include <string.h>
-
-#include "microudp.h"
-#include "tftp.h"
-
-#define PORT_OUT 69
-#define PORT_IN 7642
-
-enum {
- TFTP_RRQ = 1, /* Read request */
- TFTP_WRQ = 2, /* Write request */
- TFTP_DATA = 3, /* Data */
- TFTP_ACK = 4, /* Acknowledgment */
- TFTP_ERROR = 5, /* Error */
-};
-
-#define BLOCK_SIZE 512 /* block size in bytes */
-
-
-static int format_request(uint8_t *buf, uint16_t op, const char *filename)
-{
- int len = strlen(filename);
-
- *buf++ = op >> 8; /* Opcode */
- *buf++ = op;
- memcpy(buf, filename, len);
- buf += len;
- *buf++ = 0x00;
- *buf++ = 'o';
- *buf++ = 'c';
- *buf++ = 't';
- *buf++ = 'e';
- *buf++ = 't';
- *buf++ = 0x00;
- return 9+strlen(filename);
-}
-
-static int format_ack(uint8_t *buf, uint16_t block)
-{
- *buf++ = 0x00; /* Opcode: Ack */
- *buf++ = TFTP_ACK;
- *buf++ = (block & 0xff00) >> 8;
- *buf++ = (block & 0x00ff);
- return 4;
-}
-
-static int format_data(uint8_t *buf, uint16_t block, const void *data, int len)
-{
- *buf++ = 0x00; /* Opcode: Data*/
- *buf++ = TFTP_DATA;
- *buf++ = (block & 0xff00) >> 8;
- *buf++ = (block & 0x00ff);
- memcpy(buf, data, len);
- return len+4;
-}
-
-static uint8_t *packet_data;
-static int total_length;
-static int transfer_finished;
-static uint8_t *dst_buffer;
-static int last_ack; /* signed, so we can use -1 */
-static uint16_t data_port;
-
-static void rx_callback(uint32_t src_ip, uint16_t src_port,
- uint16_t dst_port, void *_data, unsigned int length)
-{
- uint8_t *data = _data;
- uint16_t opcode;
- uint16_t block;
- int i;
- int offset;
-
- if(length < 4) return;
- if(dst_port != PORT_IN) return;
- opcode = data[0] << 8 | data[1];
- block = data[2] << 8 | data[3];
- if(opcode == TFTP_ACK) { /* Acknowledgement */
- data_port = src_port;
- last_ack = block;
- return;
- }
- if(block < 1) return;
- if(opcode == TFTP_DATA) { /* Data */
- length -= 4;
- offset = (block-1)*BLOCK_SIZE;
- for(i=0;i<length;i++)
- dst_buffer[offset+i] = data[i+4];
- total_length += length;
- if(length < BLOCK_SIZE)
- transfer_finished = 1;
-
- length = format_ack(packet_data, block);
- microudp_send(PORT_IN, src_port, length);
- }
- if(opcode == TFTP_ERROR) { /* Error */
- total_length = -1;
- transfer_finished = 1;
- }
-}
-
-int tftp_get(uint32_t ip, const char *filename, void *buffer)
-{
- int len;
- int tries;
- int i;
- int length_before;
-
- if(!microudp_arp_resolve(ip))
- return -1;
-
- microudp_set_callback(rx_callback);
-
- packet_data = microudp_get_tx_buffer();
- dst_buffer = buffer;
-
- total_length = 0;
- transfer_finished = 0;
- tries = 5;
- while(1) {
- len = format_request(packet_data, TFTP_RRQ, filename);
- microudp_send(PORT_IN, PORT_OUT, len);
- for(i=0;i<2000000;i++) {
- microudp_service();
- if((total_length > 0) || transfer_finished) break;
- }
- if((total_length > 0) || transfer_finished) break;
- tries--;
- if(tries == 0) {
- microudp_set_callback(NULL);
- return -1;
- }
- }
-
- length_before = total_length;
- while(!transfer_finished) {
- if(length_before != total_length) {
- i = 12000000;
- length_before = total_length;
- }
- if(i-- == 0) {
- microudp_set_callback(NULL);
- return -1;
- }
- microudp_service();
- }
-
- microudp_set_callback(NULL);
-
- return total_length;
-}
-
-int tftp_put(uint32_t ip, const char *filename, const void *buffer, int size)
-{
- int len, send;
- int tries;
- int i;
- int block = 0, sent = 0;
-
- if(!microudp_arp_resolve(ip))
- return -1;
-
- microudp_set_callback(rx_callback);
-
- packet_data = microudp_get_tx_buffer();
-
- total_length = 0;
- transfer_finished = 0;
- tries = 5;
- while(1) {
- len = format_request(packet_data, TFTP_WRQ, filename);
- microudp_send(PORT_IN, PORT_OUT, len);
- for(i=0;i<2000000;i++) {
- last_ack = -1;
- microudp_service();
- if(last_ack == block)
- goto send_data;
- if(transfer_finished)
- goto fail;
- }
- tries--;
- if(tries == 0)
- goto fail;
- }
-
-send_data:
- do {
- block++;
- send = sent+BLOCK_SIZE > size ? size-sent : BLOCK_SIZE;
- tries = 5;
- while(1) {
- len = format_data(packet_data, block, buffer, send);
- microudp_send(PORT_IN, data_port, len);
- for(i=0;i<12000000;i++) {
- microudp_service();
- if(transfer_finished)
- goto fail;
- if(last_ack == block)
- goto next;
- }
- if (!--tries)
- goto fail;
- }
-next:
- sent += send;
- buffer += send;
- } while (send == BLOCK_SIZE);
-
- microudp_set_callback(NULL);
-
- return sent;
-
-fail:
- microudp_set_callback(NULL);
- return -1;
-}
+++ /dev/null
-#ifndef __TFTP_H
-#define __TFTP_H
-
-#include <stdint.h>
-
-int tftp_get(uint32_t ip, const char *filename, void *buffer);
-int tftp_put(uint32_t ip, const char *filename, const void *buffer, int size);
-
-#endif /* __TFTP_H */
-
--- /dev/null
+#ifndef __MICROUDP_H
+#define __MICROUDP_H
+
+#define IPTOINT(a, b, c, d) ((a << 24)|(b << 16)|(c << 8)|d)
+
+#define MICROUDP_BUFSIZE (5*1532)
+
+typedef void (*udp_callback)(unsigned int src_ip, unsigned short src_port, unsigned short dst_port, void *data, unsigned int length);
+
+void microudp_start(unsigned char *macaddr, unsigned int ip);
+int microudp_arp_resolve(unsigned int ip);
+void *microudp_get_tx_buffer(void);
+int microudp_send(unsigned short src_port, unsigned short dst_port, unsigned int length);
+void microudp_set_callback(udp_callback callback);
+void microudp_service(void);
+
+void ethreset(void);
+
+#endif /* __MICROUDP_H */
--- /dev/null
+#ifndef __TFTP_H
+#define __TFTP_H
+
+#include <stdint.h>
+
+int tftp_get(uint32_t ip, const char *filename, void *buffer);
+int tftp_put(uint32_t ip, const char *filename, const void *buffer, int size);
+
+#endif /* __TFTP_H */
+
--- /dev/null
+M2DIR=../..
+include $(M2DIR)/software/common.mak
+
+OBJECTS=microudp.o tftp.o
+
+all: libnet.a
+
+# pull in dependency info for *existing* .o files
+-include $(OBJECTS:.o=.d)
+
+libnet.a: $(OBJECTS)
+ $(AR) clr libnet.a $(OBJECTS)
+ $(RANLIB) libnet.a
+
+%.o: %.c
+ $(compile-dep)
+
+%.o: %.S
+ $(assemble)
+
+.PHONY: clean
+
+clean:
+ rm -f $(OBJECTS) $(OBJECTS:.o=.ts) $(OBJECTS:.o=.d) libnet.a .*~ *~
--- /dev/null
+#include <stdio.h>
+#include <system.h>
+#include <crc.h>
+#include <timer.h>
+#include <hw/csr.h>
+#include <hw/flags.h>
+#include <hw/mem.h>
+
+#include <net/microudp.h>
+
+#define ETHERTYPE_ARP 0x0806
+#define ETHERTYPE_IP 0x0800
+
+struct ethernet_header {
+ unsigned char preamble[8];
+ unsigned char destmac[6];
+ unsigned char srcmac[6];
+ unsigned short ethertype;
+} __attribute__((packed));
+
+static void fill_eth_header(struct ethernet_header *h, const unsigned char *destmac, const unsigned char *srcmac, unsigned short ethertype)
+{
+ int i;
+
+ for(i=0;i<7;i++)
+ h->preamble[i] = 0x55;
+ h->preamble[7] = 0xd5;
+ for(i=0;i<6;i++)
+ h->destmac[i] = destmac[i];
+ for(i=0;i<6;i++)
+ h->srcmac[i] = srcmac[i];
+ h->ethertype = ethertype;
+}
+
+#define ARP_HWTYPE_ETHERNET 0x0001
+#define ARP_PROTO_IP 0x0800
+
+#define ARP_OPCODE_REQUEST 0x0001
+#define ARP_OPCODE_REPLY 0x0002
+
+struct arp_frame {
+ unsigned short hwtype;
+ unsigned short proto;
+ unsigned char hwsize;
+ unsigned char protosize;
+ unsigned short opcode;
+ unsigned char sender_mac[6];
+ unsigned int sender_ip;
+ unsigned char target_mac[6];
+ unsigned int target_ip;
+ unsigned char padding[18];
+} __attribute__((packed));
+
+#define IP_IPV4 0x45
+#define IP_DONT_FRAGMENT 0x4000
+#define IP_TTL 64
+#define IP_PROTO_UDP 0x11
+
+struct ip_header {
+ unsigned char version;
+ unsigned char diff_services;
+ unsigned short total_length;
+ unsigned short identification;
+ unsigned short fragment_offset;
+ unsigned char ttl;
+ unsigned char proto;
+ unsigned short checksum;
+ unsigned int src_ip;
+ unsigned int dst_ip;
+} __attribute__((packed));
+
+struct udp_header {
+ unsigned short src_port;
+ unsigned short dst_port;
+ unsigned short length;
+ unsigned short checksum;
+} __attribute__((packed));
+
+struct udp_frame {
+ struct ip_header ip;
+ struct udp_header udp;
+ char payload[];
+} __attribute__((packed));
+
+struct ethernet_frame {
+ struct ethernet_header eth_header;
+ union {
+ struct arp_frame arp;
+ struct udp_frame udp;
+ } contents;
+} __attribute__((packed));
+
+typedef union {
+ struct ethernet_frame frame;
+ unsigned char raw[1532];
+} ethernet_buffer;
+
+
+static unsigned int rxlen;
+static ethernet_buffer *rxbuffer;
+static ethernet_buffer *rxbuffer0;
+static ethernet_buffer *rxbuffer1;
+static unsigned int txlen;
+static ethernet_buffer *txbuffer;
+
+static void send_packet(void)
+{
+ unsigned int crc;
+
+ crc = crc32(&txbuffer->raw[8], txlen-8);
+ txbuffer->raw[txlen ] = (crc & 0xff);
+ txbuffer->raw[txlen+1] = (crc & 0xff00) >> 8;
+ txbuffer->raw[txlen+2] = (crc & 0xff0000) >> 16;
+ txbuffer->raw[txlen+3] = (crc & 0xff000000) >> 24;
+ txlen += 4;
+ minimac_tx_count_write(txlen);
+ minimac_tx_start_write(1);
+ while(!(minimac_ev_pending_read() & MINIMAC_EV_TX));
+ minimac_ev_pending_write(MINIMAC_EV_TX);
+}
+
+static unsigned char my_mac[6];
+static unsigned int my_ip;
+
+/* ARP cache - one entry only */
+static unsigned char cached_mac[6];
+static unsigned int cached_ip;
+
+static void process_arp(void)
+{
+ const struct arp_frame *rx_arp = &rxbuffer->frame.contents.arp;
+ struct arp_frame *tx_arp = &txbuffer->frame.contents.arp;
+
+ if(rxlen < 68) return;
+ if(rx_arp->hwtype != ARP_HWTYPE_ETHERNET) return;
+ if(rx_arp->proto != ARP_PROTO_IP) return;
+ if(rx_arp->hwsize != 6) return;
+ if(rx_arp->protosize != 4) return;
+ if(rx_arp->opcode == ARP_OPCODE_REPLY) {
+ if(rx_arp->sender_ip == cached_ip) {
+ int i;
+ for(i=0;i<6;i++)
+ cached_mac[i] = rx_arp->sender_mac[i];
+ }
+ return;
+ }
+ if(rx_arp->opcode == ARP_OPCODE_REQUEST) {
+ if(rx_arp->target_ip == my_ip) {
+ int i;
+
+ fill_eth_header(&txbuffer->frame.eth_header,
+ rx_arp->sender_mac,
+ my_mac,
+ ETHERTYPE_ARP);
+ txlen = 68;
+ tx_arp->hwtype = ARP_HWTYPE_ETHERNET;
+ tx_arp->proto = ARP_PROTO_IP;
+ tx_arp->hwsize = 6;
+ tx_arp->protosize = 4;
+ tx_arp->opcode = ARP_OPCODE_REPLY;
+ tx_arp->sender_ip = my_ip;
+ for(i=0;i<6;i++)
+ tx_arp->sender_mac[i] = my_mac[i];
+ tx_arp->target_ip = rx_arp->sender_ip;
+ for(i=0;i<6;i++)
+ tx_arp->target_mac[i] = rx_arp->sender_mac[i];
+ send_packet();
+ }
+ return;
+ }
+}
+
+static const unsigned char broadcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+int microudp_arp_resolve(unsigned int ip)
+{
+ struct arp_frame *arp = &txbuffer->frame.contents.arp;
+ int i;
+ int tries;
+ int timeout;
+
+ if(cached_ip == ip) {
+ for(i=0;i<6;i++)
+ if(cached_mac[i]) return 1;
+ }
+ cached_ip = ip;
+ for(i=0;i<6;i++)
+ cached_mac[i] = 0;
+
+ for(tries=0;tries<5;tries++) {
+ /* Send an ARP request */
+ fill_eth_header(&txbuffer->frame.eth_header,
+ broadcast,
+ my_mac,
+ ETHERTYPE_ARP);
+ txlen = 68;
+ arp->hwtype = ARP_HWTYPE_ETHERNET;
+ arp->proto = ARP_PROTO_IP;
+ arp->hwsize = 6;
+ arp->protosize = 4;
+ arp->opcode = ARP_OPCODE_REQUEST;
+ arp->sender_ip = my_ip;
+ for(i=0;i<6;i++)
+ arp->sender_mac[i] = my_mac[i];
+ arp->target_ip = ip;
+ for(i=0;i<6;i++)
+ arp->target_mac[i] = 0;
+ send_packet();
+
+ /* Do we get a reply ? */
+ for(timeout=0;timeout<2000000;timeout++) {
+ microudp_service();
+ for(i=0;i<6;i++)
+ if(cached_mac[i]) return 1;
+ }
+ }
+
+ return 0;
+}
+
+static unsigned short ip_checksum(unsigned int r, void *buffer, unsigned int length, int complete)
+{
+ unsigned char *ptr;
+ unsigned int i;
+
+ ptr = (unsigned char *)buffer;
+ length >>= 1;
+
+ for(i=0;i<length;i++)
+ r += ((unsigned int)(ptr[2*i]) << 8)|(unsigned int)(ptr[2*i+1]) ;
+
+ /* Add overflows */
+ while(r >> 16)
+ r = (r & 0xffff) + (r >> 16);
+
+ if(complete) {
+ r = ~r;
+ r &= 0xffff;
+ if(r == 0) r = 0xffff;
+ }
+ return r;
+}
+
+void *microudp_get_tx_buffer(void)
+{
+ return txbuffer->frame.contents.udp.payload;
+}
+
+struct pseudo_header {
+ unsigned int src_ip;
+ unsigned int dst_ip;
+ unsigned char zero;
+ unsigned char proto;
+ unsigned short length;
+} __attribute__((packed));
+
+int microudp_send(unsigned short src_port, unsigned short dst_port, unsigned int length)
+{
+ struct pseudo_header h;
+ unsigned int r;
+
+ if((cached_mac[0] == 0) && (cached_mac[1] == 0) && (cached_mac[2] == 0)
+ && (cached_mac[3] == 0) && (cached_mac[4] == 0) && (cached_mac[5] == 0))
+ return 0;
+
+ txlen = length + sizeof(struct ethernet_header) + sizeof(struct udp_frame) + 8;
+ if(txlen < 72) txlen = 72;
+
+ fill_eth_header(&txbuffer->frame.eth_header,
+ cached_mac,
+ my_mac,
+ ETHERTYPE_IP);
+
+ txbuffer->frame.contents.udp.ip.version = IP_IPV4;
+ txbuffer->frame.contents.udp.ip.diff_services = 0;
+ txbuffer->frame.contents.udp.ip.total_length = length + sizeof(struct udp_frame);
+ txbuffer->frame.contents.udp.ip.identification = 0;
+ txbuffer->frame.contents.udp.ip.fragment_offset = IP_DONT_FRAGMENT;
+ txbuffer->frame.contents.udp.ip.ttl = IP_TTL;
+ h.proto = txbuffer->frame.contents.udp.ip.proto = IP_PROTO_UDP;
+ txbuffer->frame.contents.udp.ip.checksum = 0;
+ h.src_ip = txbuffer->frame.contents.udp.ip.src_ip = my_ip;
+ h.dst_ip = txbuffer->frame.contents.udp.ip.dst_ip = cached_ip;
+ txbuffer->frame.contents.udp.ip.checksum = ip_checksum(0, &txbuffer->frame.contents.udp.ip,
+ sizeof(struct ip_header), 1);
+
+ txbuffer->frame.contents.udp.udp.src_port = src_port;
+ txbuffer->frame.contents.udp.udp.dst_port = dst_port;
+ h.length = txbuffer->frame.contents.udp.udp.length = length + sizeof(struct udp_header);
+ txbuffer->frame.contents.udp.udp.checksum = 0;
+
+ h.zero = 0;
+ r = ip_checksum(0, &h, sizeof(struct pseudo_header), 0);
+ if(length & 1) {
+ txbuffer->frame.contents.udp.payload[length] = 0;
+ length++;
+ }
+ r = ip_checksum(r, &txbuffer->frame.contents.udp.udp,
+ sizeof(struct udp_header)+length, 1);
+ txbuffer->frame.contents.udp.udp.checksum = r;
+
+ send_packet();
+
+ return 1;
+}
+
+static udp_callback rx_callback;
+
+static void process_ip(void)
+{
+ if(rxlen < (sizeof(struct ethernet_header)+sizeof(struct udp_frame))) return;
+ /* We don't verify UDP and IP checksums and rely on the Ethernet checksum solely */
+ if(rxbuffer->frame.contents.udp.ip.version != IP_IPV4) return;
+ // check disabled for QEMU compatibility
+ //if(rxbuffer->frame.contents.udp.ip.diff_services != 0) return;
+ if(rxbuffer->frame.contents.udp.ip.total_length < sizeof(struct udp_frame)) return;
+ // check disabled for QEMU compatibility
+ //if(rxbuffer->frame.contents.udp.ip.fragment_offset != IP_DONT_FRAGMENT) return;
+ if(rxbuffer->frame.contents.udp.ip.proto != IP_PROTO_UDP) return;
+ if(rxbuffer->frame.contents.udp.ip.dst_ip != my_ip) return;
+ if(rxbuffer->frame.contents.udp.udp.length < sizeof(struct udp_header)) return;
+
+ if(rx_callback)
+ rx_callback(rxbuffer->frame.contents.udp.ip.src_ip, rxbuffer->frame.contents.udp.udp.src_port, rxbuffer->frame.contents.udp.udp.dst_port, rxbuffer->frame.contents.udp.payload, rxbuffer->frame.contents.udp.udp.length-sizeof(struct udp_header));
+}
+
+void microudp_set_callback(udp_callback callback)
+{
+ rx_callback = callback;
+}
+
+static void process_frame(void)
+{
+ int i;
+ unsigned int received_crc;
+ unsigned int computed_crc;
+
+ flush_cpu_dcache();
+ for(i=0;i<7;i++)
+ if(rxbuffer->frame.eth_header.preamble[i] != 0x55) return;
+ if(rxbuffer->frame.eth_header.preamble[7] != 0xd5) return;
+ received_crc = ((unsigned int)rxbuffer->raw[rxlen-1] << 24)
+ |((unsigned int)rxbuffer->raw[rxlen-2] << 16)
+ |((unsigned int)rxbuffer->raw[rxlen-3] << 8)
+ |((unsigned int)rxbuffer->raw[rxlen-4]);
+ computed_crc = crc32(&rxbuffer->raw[8], rxlen-12);
+ if(received_crc != computed_crc) return;
+
+ rxlen -= 4; /* strip CRC here to be consistent with TX */
+ if(rxbuffer->frame.eth_header.ethertype == ETHERTYPE_ARP) process_arp();
+ else if(rxbuffer->frame.eth_header.ethertype == ETHERTYPE_IP) process_ip();
+}
+
+void microudp_start(unsigned char *macaddr, unsigned int ip)
+{
+ int i;
+
+ minimac_ev_pending_write(MINIMAC_EV_RX0 | MINIMAC_EV_RX1 | MINIMAC_EV_TX);
+
+ rxbuffer0 = (ethernet_buffer *)MINIMAC_RX0_BASE;
+ rxbuffer1 = (ethernet_buffer *)MINIMAC_RX1_BASE;
+ txbuffer = (ethernet_buffer *)MINIMAC_TX_BASE;
+
+ for(i=0;i<6;i++)
+ my_mac[i] = macaddr[i];
+ my_ip = ip;
+
+ cached_ip = 0;
+ for(i=0;i<6;i++)
+ cached_mac[i] = 0;
+
+ rx_callback = (udp_callback)0;
+}
+
+void microudp_service(void)
+{
+ if(minimac_ev_pending_read() & MINIMAC_EV_RX0) {
+ rxlen = minimac_rx_count_0_read();
+ rxbuffer = rxbuffer0;
+ process_frame();
+ minimac_ev_pending_write(MINIMAC_EV_RX0);
+ }
+ if(minimac_ev_pending_read() & MINIMAC_EV_RX1) {
+ rxlen = minimac_rx_count_1_read();
+ rxbuffer = rxbuffer1;
+ process_frame();
+ minimac_ev_pending_write(MINIMAC_EV_RX1);
+ }
+}
+
+void ethreset(void)
+{
+ minimac_phy_reset_write(0);
+ busy_wait(2);
+ /* that pesky ethernet PHY needs two resets at times... */
+ minimac_phy_reset_write(1);
+ busy_wait(2);
+ minimac_phy_reset_write(0);
+ busy_wait(2);
+}
--- /dev/null
+#include <stdint.h>
+#include <string.h>
+
+#include <net/microudp.h>
+#include <net/tftp.h>
+
+#define PORT_OUT 69
+#define PORT_IN 7642
+
+enum {
+ TFTP_RRQ = 1, /* Read request */
+ TFTP_WRQ = 2, /* Write request */
+ TFTP_DATA = 3, /* Data */
+ TFTP_ACK = 4, /* Acknowledgment */
+ TFTP_ERROR = 5, /* Error */
+};
+
+#define BLOCK_SIZE 512 /* block size in bytes */
+
+
+static int format_request(uint8_t *buf, uint16_t op, const char *filename)
+{
+ int len = strlen(filename);
+
+ *buf++ = op >> 8; /* Opcode */
+ *buf++ = op;
+ memcpy(buf, filename, len);
+ buf += len;
+ *buf++ = 0x00;
+ *buf++ = 'o';
+ *buf++ = 'c';
+ *buf++ = 't';
+ *buf++ = 'e';
+ *buf++ = 't';
+ *buf++ = 0x00;
+ return 9+strlen(filename);
+}
+
+static int format_ack(uint8_t *buf, uint16_t block)
+{
+ *buf++ = 0x00; /* Opcode: Ack */
+ *buf++ = TFTP_ACK;
+ *buf++ = (block & 0xff00) >> 8;
+ *buf++ = (block & 0x00ff);
+ return 4;
+}
+
+static int format_data(uint8_t *buf, uint16_t block, const void *data, int len)
+{
+ *buf++ = 0x00; /* Opcode: Data*/
+ *buf++ = TFTP_DATA;
+ *buf++ = (block & 0xff00) >> 8;
+ *buf++ = (block & 0x00ff);
+ memcpy(buf, data, len);
+ return len+4;
+}
+
+static uint8_t *packet_data;
+static int total_length;
+static int transfer_finished;
+static uint8_t *dst_buffer;
+static int last_ack; /* signed, so we can use -1 */
+static uint16_t data_port;
+
+static void rx_callback(uint32_t src_ip, uint16_t src_port,
+ uint16_t dst_port, void *_data, unsigned int length)
+{
+ uint8_t *data = _data;
+ uint16_t opcode;
+ uint16_t block;
+ int i;
+ int offset;
+
+ if(length < 4) return;
+ if(dst_port != PORT_IN) return;
+ opcode = data[0] << 8 | data[1];
+ block = data[2] << 8 | data[3];
+ if(opcode == TFTP_ACK) { /* Acknowledgement */
+ data_port = src_port;
+ last_ack = block;
+ return;
+ }
+ if(block < 1) return;
+ if(opcode == TFTP_DATA) { /* Data */
+ length -= 4;
+ offset = (block-1)*BLOCK_SIZE;
+ for(i=0;i<length;i++)
+ dst_buffer[offset+i] = data[i+4];
+ total_length += length;
+ if(length < BLOCK_SIZE)
+ transfer_finished = 1;
+
+ length = format_ack(packet_data, block);
+ microudp_send(PORT_IN, src_port, length);
+ }
+ if(opcode == TFTP_ERROR) { /* Error */
+ total_length = -1;
+ transfer_finished = 1;
+ }
+}
+
+int tftp_get(uint32_t ip, const char *filename, void *buffer)
+{
+ int len;
+ int tries;
+ int i;
+ int length_before;
+
+ if(!microudp_arp_resolve(ip))
+ return -1;
+
+ microudp_set_callback(rx_callback);
+
+ packet_data = microudp_get_tx_buffer();
+ dst_buffer = buffer;
+
+ total_length = 0;
+ transfer_finished = 0;
+ tries = 5;
+ while(1) {
+ len = format_request(packet_data, TFTP_RRQ, filename);
+ microudp_send(PORT_IN, PORT_OUT, len);
+ for(i=0;i<2000000;i++) {
+ microudp_service();
+ if((total_length > 0) || transfer_finished) break;
+ }
+ if((total_length > 0) || transfer_finished) break;
+ tries--;
+ if(tries == 0) {
+ microudp_set_callback(NULL);
+ return -1;
+ }
+ }
+
+ length_before = total_length;
+ while(!transfer_finished) {
+ if(length_before != total_length) {
+ i = 12000000;
+ length_before = total_length;
+ }
+ if(i-- == 0) {
+ microudp_set_callback(NULL);
+ return -1;
+ }
+ microudp_service();
+ }
+
+ microudp_set_callback(NULL);
+
+ return total_length;
+}
+
+int tftp_put(uint32_t ip, const char *filename, const void *buffer, int size)
+{
+ int len, send;
+ int tries;
+ int i;
+ int block = 0, sent = 0;
+
+ if(!microudp_arp_resolve(ip))
+ return -1;
+
+ microudp_set_callback(rx_callback);
+
+ packet_data = microudp_get_tx_buffer();
+
+ total_length = 0;
+ transfer_finished = 0;
+ tries = 5;
+ while(1) {
+ len = format_request(packet_data, TFTP_WRQ, filename);
+ microudp_send(PORT_IN, PORT_OUT, len);
+ for(i=0;i<2000000;i++) {
+ last_ack = -1;
+ microudp_service();
+ if(last_ack == block)
+ goto send_data;
+ if(transfer_finished)
+ goto fail;
+ }
+ tries--;
+ if(tries == 0)
+ goto fail;
+ }
+
+send_data:
+ do {
+ block++;
+ send = sent+BLOCK_SIZE > size ? size-sent : BLOCK_SIZE;
+ tries = 5;
+ while(1) {
+ len = format_data(packet_data, block, buffer, send);
+ microudp_send(PORT_IN, data_port, len);
+ for(i=0;i<12000000;i++) {
+ microudp_service();
+ if(transfer_finished)
+ goto fail;
+ if(last_ack == block)
+ goto next;
+ }
+ if (!--tries)
+ goto fail;
+ }
+next:
+ sent += send;
+ buffer += send;
+ } while (send == BLOCK_SIZE);
+
+ microudp_set_callback(NULL);
+
+ return sent;
+
+fail:
+ microudp_set_callback(NULL);
+ return -1;
+}