From 9ea31ee45a04dfdd23462a9aa6bc0ffe2754817d Mon Sep 17 00:00:00 2001 From: "Ronald G. Minnich" Date: Sat, 23 Jun 2001 21:04:48 +0000 Subject: [PATCH] Support for etherboot. --- src/etherboot/Config | 3 + src/etherboot/cards.h | 173 +++++ src/etherboot/etherboot.h | 598 +++++++++++++++ src/etherboot/linux-asm-io.h | 187 +++++ src/etherboot/linux-asm-string.h | 253 +++++++ src/etherboot/netboot.c | 112 +++ src/etherboot/nic.h | 31 + src/etherboot/osdep.h | 121 +++ src/etherboot/pci.h | 178 +++++ src/etherboot/rarp.c | 734 ++++++++++++++++++ src/etherboot/sis900.c | 1073 +++++++++++++++++++++++++++ src/etherboot/sis900.h | 365 +++++++++ src/etherboot/sis900.txt | 91 +++ src/etherboot/timer.h | 64 ++ src/lib/linuxbiosmain.c | 50 +- src/lib/serial_subr.c | 32 + src/northsouthbridge/sis/630/Config | 2 +- src/northsouthbridge/sis/730/Config | 2 +- src/northsouthbridge/sis/730/ipl.S | 27 +- src/rom/Config | 1 + util/config/NLBConfig.py | 9 + 21 files changed, 4102 insertions(+), 4 deletions(-) create mode 100644 src/etherboot/Config create mode 100644 src/etherboot/cards.h create mode 100644 src/etherboot/etherboot.h create mode 100644 src/etherboot/linux-asm-io.h create mode 100644 src/etherboot/linux-asm-string.h create mode 100644 src/etherboot/netboot.c create mode 100644 src/etherboot/nic.h create mode 100644 src/etherboot/osdep.h create mode 100644 src/etherboot/pci.h create mode 100644 src/etherboot/rarp.c create mode 100644 src/etherboot/sis900.c create mode 100644 src/etherboot/sis900.h create mode 100644 src/etherboot/sis900.txt create mode 100644 src/etherboot/timer.h diff --git a/src/etherboot/Config b/src/etherboot/Config new file mode 100644 index 0000000000..faefd2f464 --- /dev/null +++ b/src/etherboot/Config @@ -0,0 +1,3 @@ +object netboot.o +object rarp.o +object sis900.o diff --git a/src/etherboot/cards.h b/src/etherboot/cards.h new file mode 100644 index 0000000000..ec1d1498ac --- /dev/null +++ b/src/etherboot/cards.h @@ -0,0 +1,173 @@ +#ifndef CARDS_H +#define CARDS_H + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, or (at + * your option) any later version. + */ + +#include "nic.h" + +/* OK, this is how the PCI support hack works: if pci.h is included before + * this file is included, assume that the driver supports PCI. This means that + * this file is usually included last. */ + +#ifdef PCI_H +#define PCI_ARG(x) ,x +#else +#define PCI_ARG(x) +#endif + +#ifdef INCLUDE_WD +extern struct nic *wd_probe(struct nic *, unsigned short * + PCI_ARG(struct pci_device *)); +#endif + +#ifdef INCLUDE_3C503 +extern struct nic *t503_probe(struct nic *, unsigned short * + PCI_ARG(struct pci_device *)); +#endif + +#ifdef INCLUDE_VIA_RHINE +extern struct nic *rhine_probe(struct nic *, unsigned short * + PCI_ARG(struct pci_device *)); +#endif + +#ifdef INCLUDE_NE +extern struct nic *ne_probe(struct nic *, unsigned short * + PCI_ARG(struct pci_device *)); +#endif + +#ifdef INCLUDE_NS8390 +extern struct nic *nepci_probe(struct nic *, unsigned short * + PCI_ARG(struct pci_device *)); +#endif + +#ifdef INCLUDE_3C509 +extern struct nic *t509_probe(struct nic *, unsigned short * + PCI_ARG(struct pci_device *)); +#endif + +#ifdef INCLUDE_3C529 +extern struct nic *t529_probe(struct nic *, unsigned short * + PCI_ARG(struct pci_device *)); +#endif + +#ifdef INCLUDE_3C595 +extern struct nic *t595_probe(struct nic *, unsigned short * + PCI_ARG(struct pci_device *)); +#endif + +#ifdef INCLUDE_3C90X +extern struct nic *a3c90x_probe(struct nic *, unsigned short * + PCI_ARG(struct pci_device *)); +#endif + +#ifdef INCLUDE_EEPRO +extern struct nic *eepro_probe(struct nic *, unsigned short * + PCI_ARG(struct pci_device *)); +#endif + +#ifdef INCLUDE_EEPRO100 +extern struct nic *eepro100_probe(struct nic *, unsigned short * + PCI_ARG(struct pci_device *)); +#endif + +#ifdef INCLUDE_EPIC100 +extern struct nic *epic100_probe(struct nic *, unsigned short * + PCI_ARG(struct pci_device *)); +#endif + +#ifdef INCLUDE_OTULIP +extern struct nic *otulip_probe(struct nic *, unsigned short * + PCI_ARG(struct pci_device *)); +#endif + +#ifdef INCLUDE_TULIP +extern struct nic *tulip_probe(struct nic *, unsigned short * + PCI_ARG(struct pci_device *)); +#endif + +#ifdef INCLUDE_DAVICOM +extern struct nic *davicom_probe(struct nic *, unsigned short * + PCI_ARG(struct pci_device *)); +#endif + +#ifdef INCLUDE_CS89X0 +extern struct nic *cs89x0_probe(struct nic *, unsigned short * + PCI_ARG(struct pci_device *)); +#endif + +#ifdef INCLUDE_LANCE +extern struct nic *lancepci_probe(struct nic *, unsigned short * + PCI_ARG(struct pci_device *)); +#endif + +#ifdef INCLUDE_NE2100 +extern struct nic *ne2100_probe(struct nic *, unsigned short * + PCI_ARG(struct pci_device *)); +#endif + +#ifdef INCLUDE_NI6510 +extern struct nic *ni6510_probe(struct nic *, unsigned short * + PCI_ARG(struct pci_device *)); +#endif + +#ifdef INCLUDE_SK_G16 +extern struct nic *SK_probe(struct nic *, unsigned short * + PCI_ARG(struct pci_device *)); +#endif + +#ifdef INCLUDE_3C507 +extern struct nic *t507_probe(struct nic *, unsigned short * + PCI_ARG(struct pci_device *)); +#endif + +#ifdef INCLUDE_NI5010 +extern struct nic *ni5010_probe(struct nic *, unsigned short * + PCI_ARG(struct pci_device *)); +#endif + +#ifdef INCLUDE_NI5210 +extern struct nic *ni5210_probe(struct nic *, unsigned short * + PCI_ARG(struct pci_device *)); +#endif + +#ifdef INCLUDE_EXOS205 +extern struct nic *exos205_probe(struct nic *, unsigned short * + PCI_ARG(struct pci_device *)); +#endif + +#ifdef INCLUDE_SMC9000 +extern struct nic *smc9000_probe(struct nic *, unsigned short * + PCI_ARG(struct pci_device *)); +#endif + +#ifdef INCLUDE_TIARA +extern struct nic *tiara_probe(struct nic *, unsigned short * + PCI_ARG(struct pci_device *)); +#endif + +#ifdef INCLUDE_DEPCA +extern struct nic *depca_probe(struct nic *, unsigned short * + PCI_ARG(struct pci_device *)); +#endif + +#ifdef INCLUDE_RTL8139 +extern struct nic *rtl8139_probe(struct nic *, unsigned short * + PCI_ARG(struct pci_device *)); +#endif + +#ifdef INCLUDE_W89C840 +extern struct nic *w89c840_probe(struct nic *, unsigned short * + PCI_ARG(struct pci_device *)); +#endif + +#ifdef INCLUDE_SIS900 +extern struct nic *sis900_probe(struct nic *, unsigned short * + PCI_ARG(struct pci_device *)); +#endif + +#endif /* CARDS_H */ diff --git a/src/etherboot/etherboot.h b/src/etherboot/etherboot.h new file mode 100644 index 0000000000..2661cb3c14 --- /dev/null +++ b/src/etherboot/etherboot.h @@ -0,0 +1,598 @@ +/************************************************************************** +Etherboot - BOOTP/TFTP Bootstrap Program + +Author: Martin Renters + Date: Dec/93 + +**************************************************************************/ + +#include "osdep.h" + +/* These could be customised for different languages perhaps */ +#define ASK_PROMPT "Boot from (N)etwork or from (L)ocal? " +#define ANS_NETWORK 'N' +#define ANS_LOCAL 'L' +#ifndef ANS_DEFAULT /* in case left out in Makefile */ +#define ANS_DEFAULT ANS_NETWORK +#endif + +#if !defined(TAGGED_IMAGE) && !defined(AOUT_IMAGE) && !defined(ELF_IMAGE) +#define TAGGED_IMAGE /* choose at least one */ +#endif + +#define ESC '\033' + +/* Edit this to change the path to hostspecific kernel image + kernel. in RARP boot */ +#ifndef DEFAULT_KERNELPATH +#define DEFAULT_KERNELPATH "/tftpboot/kernel.%I" +#endif + +/* Edit this to change the default fallback kernel image. + This is used if bootp/dhcp-server doesn't provide the kernel path */ +#ifndef DEFAULT_BOOTFILE +#define DEFAULT_BOOTFILE "/tftpboot/kernel" +#endif + +/* Clean up console settings... mainly CONSOLE_CRT and CONSOLE_SERIAL are used + * in the sources (except start.S and serial.S which cannot include + * etherboot.h). At least one of the CONSOLE_xxx has to be set, and + * CONSOLE_DUAL sets both CONSOLE_CRT and CONSOLE_SERIAL. If none is set, + * CONSOLE_CRT is assumed. */ +#ifdef CONSOLE_DUAL +#undef CONSOLE_CRT +#define CONSOLE_CRT +#undef CONSOLE_SERIAL +#define CONSOLE_SERIAL +#endif +#if defined(CONSOLE_CRT) && defined(CONSOLE_SERIAL) +#undef CONSOLE_DUAL +#define CONSOLE_DUAL +#endif +#if !defined(CONSOLE_CRT) && !defined(CONSOLE_SERIAL) +#define CONSOLE_CRT +#endif + +#ifndef DOWNLOAD_PROTO_NFS +#undef DOWNLOAD_PROTO_TFTP +#define DOWNLOAD_PROTO_TFTP /* default booting protocol */ +#endif + +#ifdef DOWNLOAD_PROTO_TFTP +#define download(fname,loader) tftp((fname),(loader)) +#endif +#ifdef DOWNLOAD_PROTO_NFS +#define download(fname,loader) nfs((fname),(loader)) +#endif + +#ifndef MAX_TFTP_RETRIES +#define MAX_TFTP_RETRIES 20 +#endif + +#ifndef MAX_BOOTP_RETRIES +#define MAX_BOOTP_RETRIES 20 +#endif + +#ifdef BOOTP_DATA_AT_0x93C00 +#define MAX_BOOTP_EXTLEN (1024-sizeof(struct bootp_t)) +#else +#define MAX_BOOTP_EXTLEN (ETH_FRAME_LEN-ETH_HLEN-sizeof(struct bootp_t)) +#endif + +#ifndef MAX_ARP_RETRIES +#define MAX_ARP_RETRIES 20 +#endif + +#ifndef MAX_RPC_RETRIES +#define MAX_RPC_RETRIES 20 +#endif + +#define TICKS_PER_SEC 18 + +/* Inter-packet retry in ticks */ +#define TIMEOUT (10*TICKS_PER_SEC) + +/* These settings have sense only if compiled with -DCONGESTED */ +/* total retransmission timeout in ticks */ +#define TFTP_TIMEOUT (30*TICKS_PER_SEC) +/* packet retransmission timeout in ticks */ +#define TFTP_REXMT (3*TICKS_PER_SEC) + +#ifndef NULL +#define NULL ((void *)0) +#endif + +/* + I'm moving towards the defined names in linux/if_ether.h for clarity. + The confusion between 60/64 and 1514/1518 arose because the NS8390 + counts the 4 byte frame checksum in the incoming packet, but not + in the outgoing packet. 60/1514 are the correct numbers for most + if not all of the other NIC controllers. I will be retiring the + 64/1518 defines in the lead-up to 5.0. +*/ + +#define ETH_ALEN 6 /* Size of Ethernet address */ +#define ETH_HLEN 14 /* Size of ethernet header */ +#define ETH_ZLEN 60 /* Minimum packet */ +/*#define ETH_MIN_PACKET 64*/ +#define ETH_FRAME_LEN 1514 /* Maximum packet */ +/*#define ETH_MAX_PACKET 1518*/ + +#define ARP_CLIENT 0 +#define ARP_SERVER 1 +#define ARP_GATEWAY 2 +#define ARP_ROOTSERVER 3 +#define ARP_SWAPSERVER 4 +#define MAX_ARP ARP_SWAPSERVER+1 + +#define RARP_REQUEST 3 +#define RARP_REPLY 4 + +#define IP 0x0800 +#define ARP 0x0806 +#define RARP 0x8035 + +#define BOOTP_SERVER 67 +#define BOOTP_CLIENT 68 +#define TFTP_PORT 69 +#define SUNRPC_PORT 111 + +#define IP_UDP 17 +/* Same after going through htonl */ +#define IP_BROADCAST 0xFFFFFFFF + +#define ARP_REQUEST 1 +#define ARP_REPLY 2 + +#define BOOTP_REQUEST 1 +#define BOOTP_REPLY 2 + +#define TAG_LEN(p) (*((p)+1)) +#define RFC1533_COOKIE 99, 130, 83, 99 +#define RFC1533_PAD 0 +#define RFC1533_NETMASK 1 +#define RFC1533_TIMEOFFSET 2 +#define RFC1533_GATEWAY 3 +#define RFC1533_TIMESERVER 4 +#define RFC1533_IEN116NS 5 +#define RFC1533_DNS 6 +#define RFC1533_LOGSERVER 7 +#define RFC1533_COOKIESERVER 8 +#define RFC1533_LPRSERVER 9 +#define RFC1533_IMPRESSSERVER 10 +#define RFC1533_RESOURCESERVER 11 +#define RFC1533_HOSTNAME 12 +#define RFC1533_BOOTFILESIZE 13 +#define RFC1533_MERITDUMPFILE 14 +#define RFC1533_DOMAINNAME 15 +#define RFC1533_SWAPSERVER 16 +#define RFC1533_ROOTPATH 17 +#define RFC1533_EXTENSIONPATH 18 +#define RFC1533_IPFORWARDING 19 +#define RFC1533_IPSOURCEROUTING 20 +#define RFC1533_IPPOLICYFILTER 21 +#define RFC1533_IPMAXREASSEMBLY 22 +#define RFC1533_IPTTL 23 +#define RFC1533_IPMTU 24 +#define RFC1533_IPMTUPLATEAU 25 +#define RFC1533_INTMTU 26 +#define RFC1533_INTLOCALSUBNETS 27 +#define RFC1533_INTBROADCAST 28 +#define RFC1533_INTICMPDISCOVER 29 +#define RFC1533_INTICMPRESPOND 30 +#define RFC1533_INTROUTEDISCOVER 31 +#define RFC1533_INTROUTESOLICIT 32 +#define RFC1533_INTSTATICROUTES 33 +#define RFC1533_LLTRAILERENCAP 34 +#define RFC1533_LLARPCACHETMO 35 +#define RFC1533_LLETHERNETENCAP 36 +#define RFC1533_TCPTTL 37 +#define RFC1533_TCPKEEPALIVETMO 38 +#define RFC1533_TCPKEEPALIVEGB 39 +#define RFC1533_NISDOMAIN 40 +#define RFC1533_NISSERVER 41 +#define RFC1533_NTPSERVER 42 +#define RFC1533_VENDOR 43 +#define RFC1533_NBNS 44 +#define RFC1533_NBDD 45 +#define RFC1533_NBNT 46 +#define RFC1533_NBSCOPE 47 +#define RFC1533_XFS 48 +#define RFC1533_XDM 49 +#ifndef NO_DHCP_SUPPORT +#define RFC2132_REQ_ADDR 50 +#define RFC2132_MSG_TYPE 53 +#define RFC2132_SRV_ID 54 +#define RFC2132_PARAM_LIST 55 +#define RFC2132_MAX_SIZE 57 +#define RFC2132_VENDOR_CLASS_ID 60 + +#define DHCPDISCOVER 1 +#define DHCPOFFER 2 +#define DHCPREQUEST 3 +#define DHCPACK 5 +#endif /* NO_DHCP_SUPPORT */ + +#define RFC1533_VENDOR_MAJOR 0 +#define RFC1533_VENDOR_MINOR 0 + +#define RFC1533_VENDOR_MAGIC 128 +#define RFC1533_VENDOR_ADDPARM 129 +#define RFC1533_VENDOR_ETHDEV 130 +#ifdef IMAGE_FREEBSD +#define RFC1533_VENDOR_HOWTO 132 +#endif +#define RFC1533_VENDOR_MNUOPTS 160 +#define RFC1533_VENDOR_SELECTION 176 +#define RFC1533_VENDOR_MOTD 184 +#define RFC1533_VENDOR_NUMOFMOTD 8 +#define RFC1533_VENDOR_IMG 192 +#define RFC1533_VENDOR_NUMOFIMG 16 + +#define RFC1533_END 255 + +#define BOOTP_VENDOR_LEN 64 +#ifndef NO_DHCP_SUPPORT +#define DHCP_OPT_LEN 312 +#endif /* NO_DHCP_SUPPORT */ + +#define TFTP_DEFAULTSIZE_PACKET 512 +#define TFTP_MAX_PACKET 1432 /* 512 */ + +#define TFTP_RRQ 1 +#define TFTP_WRQ 2 +#define TFTP_DATA 3 +#define TFTP_ACK 4 +#define TFTP_ERROR 5 +#define TFTP_OACK 6 + +#define TFTP_CODE_EOF 1 +#define TFTP_CODE_MORE 2 +#define TFTP_CODE_ERROR 3 +#define TFTP_CODE_BOOT 4 +#define TFTP_CODE_CFG 5 + +#define AWAIT_ARP 0 +#define AWAIT_BOOTP 1 +#define AWAIT_TFTP 2 +#define AWAIT_RARP 3 +#define AWAIT_RPC 4 +#define AWAIT_QDRAIN 5 /* drain queue, process ARP requests */ + +typedef struct { + unsigned long s_addr; +} in_addr; + +struct arptable_t { + in_addr ipaddr; + unsigned char node[6]; +}; + +/* + * A pity sipaddr and tipaddr are not longword aligned or we could use + * in_addr. No, I don't want to use #pragma packed. + */ +struct arprequest { + unsigned short hwtype; + unsigned short protocol; + char hwlen; + char protolen; + unsigned short opcode; + char shwaddr[6]; + char sipaddr[4]; + char thwaddr[6]; + char tipaddr[4]; +}; + +struct iphdr { + char verhdrlen; + char service; + unsigned short len; + unsigned short ident; + unsigned short frags; + char ttl; + char protocol; + unsigned short chksum; + in_addr src; + in_addr dest; +}; + +struct udphdr { + unsigned short src; + unsigned short dest; + unsigned short len; + unsigned short chksum; +}; + +/* Format of a bootp packet */ +struct bootp_t { + char bp_op; + char bp_htype; + char bp_hlen; + char bp_hops; + unsigned long bp_xid; + unsigned short bp_secs; + unsigned short unused; + in_addr bp_ciaddr; + in_addr bp_yiaddr; + in_addr bp_siaddr; + in_addr bp_giaddr; + char bp_hwaddr[16]; + char bp_sname[64]; + char bp_file[128]; +#ifdef NO_DHCP_SUPPORT + char bp_vend[BOOTP_VENDOR_LEN]; +#else + char bp_vend[DHCP_OPT_LEN]; +#endif /* NO_DHCP_SUPPORT */ +}; + +/* Format of a bootp IP packet */ +struct bootpip_t +{ + struct iphdr ip; + struct udphdr udp; + struct bootp_t bp; +}; + +/* Format of bootp packet with extensions */ +struct bootpd_t { + struct bootp_t bootp_reply; + unsigned char bootp_extension[MAX_BOOTP_EXTLEN]; +}; + +#define KERNEL_BUF (BOOTP_DATA_ADDR->bootp_reply.bp_file) + +struct tftp_t { + struct iphdr ip; + struct udphdr udp; + unsigned short opcode; + union { + char rrq[TFTP_DEFAULTSIZE_PACKET]; + struct { + unsigned short block; + char download[TFTP_MAX_PACKET]; + } data; + struct { + unsigned short block; + } ack; + struct { + unsigned short errcode; + char errmsg[TFTP_DEFAULTSIZE_PACKET]; + } err; + struct { + char data[TFTP_DEFAULTSIZE_PACKET+2]; + } oack; + } u; +}; + +/* define a smaller tftp packet solely for making requests to conserve stack + 512 bytes should be enough */ +struct tftpreq_t { + struct iphdr ip; + struct udphdr udp; + unsigned short opcode; + union { + char rrq[512]; + struct { + unsigned short block; + } ack; + struct { + unsigned short errcode; + char errmsg[512-2]; + } err; + } u; +}; + +#define TFTP_MIN_PACKET (sizeof(struct iphdr) + sizeof(struct udphdr) + 4) + +struct rpc_t { + struct iphdr ip; + struct udphdr udp; + union { + char data[300]; /* longest RPC call must fit!!!! */ + struct { + long id; + long type; + long rpcvers; + long prog; + long vers; + long proc; + long data[1]; + } call; + struct { + long id; + long type; + long rstatus; + long verifier; + long v2; + long astatus; + long data[1]; + } reply; + } u; +}; + +#define PROG_PORTMAP 100000 +#define PROG_NFS 100003 +#define PROG_MOUNT 100005 + +#define MSG_CALL 0 +#define MSG_REPLY 1 + +#define PORTMAP_GETPORT 3 + +#define MOUNT_ADDENTRY 1 +#define MOUNT_UMOUNTALL 4 + +#define NFS_LOOKUP 4 +#define NFS_READ 6 + +#define NFS_FHSIZE 32 + +#define NFSERR_PERM 1 +#define NFSERR_NOENT 2 +#define NFSERR_ACCES 13 + +/* Block size used for NFS read accesses. A RPC reply packet (including all + * headers) must fit within a single Ethernet frame to avoid fragmentation. + * Chosen to be a power of two, as most NFS servers are optimized for this. */ +#define NFS_READ_SIZE 1024 + +#define FLOPPY_BOOT_LOCATION 0x7c00 + +#define ROM_INFO_LOCATION 0x7dfa +/* at end of floppy boot block */ + +struct rom_info { + unsigned short rom_segment; + unsigned short rom_length; +}; + +extern inline int rom_address_ok(struct rom_info *rom, int assigned_rom_segment) +{ + return (assigned_rom_segment < 0xC000 + || assigned_rom_segment == rom->rom_segment); +} + +/* Define a type for use by setjmp and longjmp */ +typedef struct { + unsigned long buf[7]; +} jmpbuf[1]; + +/* Define a type for passing info to a loaded program */ +struct ebinfo { + unsigned char major, minor; /* Version */ + unsigned short flags; /* Bit flags */ +}; + +/*************************************************************************** +External prototypes +***************************************************************************/ +/* main.c */ +extern int tftp P((const char *name, int (*)(unsigned char *, int, int, int))); +extern int udp_transmit P((unsigned long destip, unsigned int srcsock, + unsigned int destsock, int len, const void *buf)); +extern int await_reply P((int type, int ival, void *ptr, int timeout)); +extern int decode_rfc1533 P((unsigned char *, int, int, int)); +extern void rfc951_sleep P((int)); +extern void cleanup P((void)); + +/* nfs.c */ +extern void rpc_init(void); +extern int nfs P((const char *name, int (*)(unsigned char *, int, int, int))); +extern void nfs_umountall P((int)); + +/* config.c */ +extern void print_config(void); +extern void eth_reset(void); +extern int eth_probe(void); +extern int eth_poll(void); +extern void eth_transmit(const char *d, unsigned int t, unsigned int s, const void *p); +extern void eth_disable(void); + +/* bootmenu.c */ +extern void bootmenu P((int)); +extern void show_motd P((void)); +extern void parse_menuopts P((char *,int)); +extern void selectImage P((char **)); + +/* osloader.c */ +#if defined(AOUT_IMAGE) || defined(ELF_IMAGE) +extern int howto; +#endif +extern int os_download P((unsigned int, unsigned char *,unsigned int)); + +/* misc.c */ +extern void twiddle P((void)); +extern void sleep P((int secs)); +extern int strcasecmp P((char *a, char *b)); +extern char *substr P((char *a, char *b)); +extern int getdec P((char **)); +extern void printf P((const char *, ...)); +extern int sprintf P((char *, const char *, ...)); +extern int inet_aton P((char *p, in_addr *i)); +extern void gateA20_set P((void)); +extern void gateA20_unset P((void)); +extern void putchar P((int)); +extern int getchar P((void)); +extern int iskey P((void)); + +/* start*.S */ +extern int console_getc P((void)); +extern void console_putc P((int)); +extern int console_ischar P((void)); +extern int getshift P((void)); +extern unsigned int memsize P((void)); +extern unsigned short basememsize P((void)); +extern void disk_init P((void)); +extern unsigned int disk_read P((int drv,int c,int h,int s,char *buf)); +extern void xstart P((unsigned long, unsigned long, char *)); +#ifdef IMAGE_MULTIBOOT +extern void xend P((void)); +#endif +extern unsigned long currticks P((void)); +extern int setjmp P((jmpbuf env)); +extern void longjmp P((jmpbuf env, int val)); +extern void exit P((int status)); + +/* serial.S */ +extern int serial_getc P((void)); +extern void serial_putc P((int)); +extern int serial_ischar P((void)); +extern int serial_init P((void)); + +/* ansiesc.c */ +extern void ansi_reset P((void)); +extern void enable_cursor P((int)); +extern void ansi_putc P((unsigned int)); + +/* md5.c */ +extern void md5_put P((unsigned int ch)); +extern void md5_done P((unsigned char *buf)); + +/* floppy.c */ +extern int bootdisk P((int dev,int part)); + +/*************************************************************************** +External variables +***************************************************************************/ +/* main.c */ +extern struct rom_info rom; +extern char *hostname; +extern int hostnamelen; +extern jmpbuf jmp_bootmenu; +extern struct arptable_t arptable[MAX_ARP]; +#ifdef IMAGE_MENU +extern int menutmo,menudefault; +extern unsigned char *defparams; +extern int defparams_max; +#endif +#ifdef MOTD +extern char *motd[RFC1533_VENDOR_NUMOFMOTD]; +#endif +#ifdef BOOTP_DATA_AT_0x93C00 +#define BOOTP_DATA_ADDR ((struct bootpd_t *)0x93C00) +#else +extern struct bootpd_t bootp_data; +#define BOOTP_DATA_ADDR (&bootp_data) +#endif +extern unsigned char *end_of_rfc1533; +#ifdef IMAGE_FREEBSD +extern int freebsd_howto; +#endif + +/* config.c */ +extern struct nic nic; + +/* bootmenu.c */ + +/* osloader.c */ + +/* created by linker */ +extern char _start[], edata[], end[]; + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/src/etherboot/linux-asm-io.h b/src/etherboot/linux-asm-io.h new file mode 100644 index 0000000000..4fe91fb634 --- /dev/null +++ b/src/etherboot/linux-asm-io.h @@ -0,0 +1,187 @@ +#ifndef _ASM_IO_H +#define _ASM_IO_H + +/* + * This file contains the definitions for the x86 IO instructions + * inb/inw/inl/outb/outw/outl and the "string versions" of the same + * (insb/insw/insl/outsb/outsw/outsl). You can also use "pausing" + * versions of the single-IO instructions (inb_p/inw_p/..). + * + * This file is not meant to be obfuscating: it's just complicated + * to (a) handle it all in a way that makes gcc able to optimize it + * as well as possible and (b) trying to avoid writing the same thing + * over and over again with slight variations and possibly making a + * mistake somewhere. + */ + +/* + * Thanks to James van Artsdalen for a better timing-fix than + * the two short jumps: using outb's to a nonexistent port seems + * to guarantee better timings even on fast machines. + * + * On the other hand, I'd like to be sure of a non-existent port: + * I feel a bit unsafe about using 0x80 (should be safe, though) + * + * Linus + */ + +#ifdef SLOW_IO_BY_JUMPING +#define __SLOW_DOWN_IO __asm__ __volatile__("jmp 1f\n1:\tjmp 1f\n1:") +#else +#define __SLOW_DOWN_IO __asm__ __volatile__("outb %al,$0x80") +#endif + +#ifdef REALLY_SLOW_IO +#define SLOW_DOWN_IO { __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; } +#else +#define SLOW_DOWN_IO __SLOW_DOWN_IO +#endif + +/* + * readX/writeX() are used to access memory mapped devices. On some + * architectures the memory mapped IO stuff needs to be accessed + * differently. On the x86 architecture, we just read/write the + * memory location directly. + */ +#define readb(addr) (*(volatile unsigned char *) (addr)) +#define readw(addr) (*(volatile unsigned short *) (addr)) +#define readl(addr) (*(volatile unsigned int *) (addr)) + +#define writeb(b,addr) ((*(volatile unsigned char *) (addr)) = (b)) +#define writew(b,addr) ((*(volatile unsigned short *) (addr)) = (b)) +#define writel(b,addr) ((*(volatile unsigned int *) (addr)) = (b)) + +#define memset_io(a,b,c) memset((void *)(a),(b),(c)) +#define memcpy_fromio(a,b,c) memcpy((a),(void *)(b),(c)) +#define memcpy_toio(a,b,c) memcpy((void *)(a),(b),(c)) + +/* + * Again, i386 does not require mem IO specific function. + */ + +#define eth_io_copy_and_sum(a,b,c,d) eth_copy_and_sum((a),(void *)(b),(c),(d)) + +/* + * Talk about misusing macros.. + */ + +#define __OUT1(s,x) \ +extern void __out##s(unsigned x value, unsigned short port); \ +extern inline void __out##s(unsigned x value, unsigned short port) { + +#define __OUT2(s,s1,s2) \ +__asm__ __volatile__ ("out" #s " %" s1 "0,%" s2 "1" + +#define __OUT(s,s1,x) \ +__OUT1(s,x) __OUT2(s,s1,"w") : : "a" (value), "d" (port)); } \ +__OUT1(s##c,x) __OUT2(s,s1,"") : : "a" (value), "id" (port)); } \ +__OUT1(s##_p,x) __OUT2(s,s1,"w") : : "a" (value), "d" (port)); SLOW_DOWN_IO; } \ +__OUT1(s##c_p,x) __OUT2(s,s1,"") : : "a" (value), "id" (port)); SLOW_DOWN_IO; } + +#define __IN1(s,x) \ +extern unsigned x __in##s(unsigned short port); \ +extern inline unsigned x __in##s(unsigned short port) { unsigned x _v; + +#define __IN2(s,s1,s2) \ +__asm__ __volatile__ ("in" #s " %" s2 "1,%" s1 "0" + +#define __IN(s,s1,x,i...) \ +__IN1(s,x) __IN2(s,s1,"w") : "=a" (_v) : "d" (port) ,##i ); return _v; } \ +__IN1(s##c,x) __IN2(s,s1,"") : "=a" (_v) : "id" (port) ,##i ); return _v; } \ +__IN1(s##_p,x) __IN2(s,s1,"w") : "=a" (_v) : "d" (port) ,##i ); SLOW_DOWN_IO; return _v; } \ +__IN1(s##c_p,x) __IN2(s,s1,"") : "=a" (_v) : "id" (port) ,##i ); SLOW_DOWN_IO; return _v; } + +#define __INS(s) \ +extern void ins##s(unsigned short port, void * addr, unsigned long count); \ +extern inline void ins##s(unsigned short port, void * addr, unsigned long count) \ +{ __asm__ __volatile__ ("cld ; rep ; ins" #s \ +: "=D" (addr), "=c" (count) : "d" (port),"0" (addr),"1" (count)); } + +#define __OUTS(s) \ +extern void outs##s(unsigned short port, const void * addr, unsigned long count); \ +extern inline void outs##s(unsigned short port, const void * addr, unsigned long count) \ +{ __asm__ __volatile__ ("cld ; rep ; outs" #s \ +: "=S" (addr), "=c" (count) : "d" (port),"0" (addr),"1" (count)); } + +__IN(b,"", char) +__IN(w,"",short) +__IN(l,"", long) + +__OUT(b,"b",char) +__OUT(w,"w",short) +__OUT(l,,int) + +__INS(b) +__INS(w) +__INS(l) + +__OUTS(b) +__OUTS(w) +__OUTS(l) + +/* + * Note that due to the way __builtin_constant_p() works, you + * - can't use it inside a inline function (it will never be true) + * - you don't have to worry about side effects within the __builtin.. + */ +#define outb(val,port) \ +((__builtin_constant_p((port)) && (port) < 256) ? \ + __outbc((val),(port)) : \ + __outb((val),(port))) + +#define inb(port) \ +((__builtin_constant_p((port)) && (port) < 256) ? \ + __inbc(port) : \ + __inb(port)) + +#define outb_p(val,port) \ +((__builtin_constant_p((port)) && (port) < 256) ? \ + __outbc_p((val),(port)) : \ + __outb_p((val),(port))) + +#define inb_p(port) \ +((__builtin_constant_p((port)) && (port) < 256) ? \ + __inbc_p(port) : \ + __inb_p(port)) + +#define outw(val,port) \ +((__builtin_constant_p((port)) && (port) < 256) ? \ + __outwc((val),(port)) : \ + __outw((val),(port))) + +#define inw(port) \ +((__builtin_constant_p((port)) && (port) < 256) ? \ + __inwc(port) : \ + __inw(port)) + +#define outw_p(val,port) \ +((__builtin_constant_p((port)) && (port) < 256) ? \ + __outwc_p((val),(port)) : \ + __outw_p((val),(port))) + +#define inw_p(port) \ +((__builtin_constant_p((port)) && (port) < 256) ? \ + __inwc_p(port) : \ + __inw_p(port)) + +#define outl(val,port) \ +((__builtin_constant_p((port)) && (port) < 256) ? \ + __outlc((val),(port)) : \ + __outl((val),(port))) + +#define inl(port) \ +((__builtin_constant_p((port)) && (port) < 256) ? \ + __inlc(port) : \ + __inl(port)) + +#define outl_p(val,port) \ +((__builtin_constant_p((port)) && (port) < 256) ? \ + __outlc_p((val),(port)) : \ + __outl_p((val),(port))) + +#define inl_p(port) \ +((__builtin_constant_p((port)) && (port) < 256) ? \ + __inlc_p(port) : \ + __inl_p(port)) + +#endif diff --git a/src/etherboot/linux-asm-string.h b/src/etherboot/linux-asm-string.h new file mode 100644 index 0000000000..f3fc1255d3 --- /dev/null +++ b/src/etherboot/linux-asm-string.h @@ -0,0 +1,253 @@ +/* + * Taken from Linux /usr/include/asm/string.h + * All except memcpy, memmove, memset and memcmp removed. + */ + +#ifndef _I386_STRING_H_ +#define _I386_STRING_H_ + +#include +/* + * This string-include defines all string functions as inline + * functions. Use gcc. It also assumes ds=es=data space, this should be + * normal. Most of the string-functions are rather heavily hand-optimized, + * see especially strtok,strstr,str[c]spn. They should work, but are not + * very easy to understand. Everything is done entirely within the register + * set, making the functions fast and clean. String instructions have been + * used through-out, making for "slightly" unclear code :-) + * + * NO Copyright (C) 1991, 1992 Linus Torvalds, + * consider these trivial functions to be PD. + */ + +extern void *__memcpy(void * to, const void * from, size_t n); +extern void *__constant_memcpy(void * to, const void * from, size_t n); +extern void *memmove(void * dest,const void * src, size_t n); +extern void *__memset_generic(void * s, char c,size_t count); +extern void *__constant_c_memset(void * s, unsigned long c, size_t count); +extern void *__constant_c_and_count_memset(void * s, unsigned long pattern, size_t count); + + +extern inline void * __memcpy(void * to, const void * from, size_t n) +{ +int d0, d1, d2; +__asm__ __volatile__( + "cld\n\t" + "rep ; movsl\n\t" + "testb $2,%b4\n\t" + "je 1f\n\t" + "movsw\n" + "1:\ttestb $1,%b4\n\t" + "je 2f\n\t" + "movsb\n" + "2:" + : "=&c" (d0), "=&D" (d1), "=&S" (d2) + :"0" (n/4), "q" (n),"1" ((long) to),"2" ((long) from) + : "memory"); +return (to); +} + +/* + * This looks horribly ugly, but the compiler can optimize it totally, + * as the count is constant. + */ +extern inline void * __constant_memcpy(void * to, const void * from, size_t n) +{ + switch (n) { + case 0: + return to; + case 1: + *(unsigned char *)to = *(const unsigned char *)from; + return to; + case 2: + *(unsigned short *)to = *(const unsigned short *)from; + return to; + case 3: + *(unsigned short *)to = *(const unsigned short *)from; + *(2+(unsigned char *)to) = *(2+(const unsigned char *)from); + return to; + case 4: + *(unsigned long *)to = *(const unsigned long *)from; + return to; + case 6: /* for Ethernet addresses */ + *(unsigned long *)to = *(const unsigned long *)from; + *(2+(unsigned short *)to) = *(2+(const unsigned short *)from); + return to; + case 8: + *(unsigned long *)to = *(const unsigned long *)from; + *(1+(unsigned long *)to) = *(1+(const unsigned long *)from); + return to; + case 12: + *(unsigned long *)to = *(const unsigned long *)from; + *(1+(unsigned long *)to) = *(1+(const unsigned long *)from); + *(2+(unsigned long *)to) = *(2+(const unsigned long *)from); + return to; + case 16: + *(unsigned long *)to = *(const unsigned long *)from; + *(1+(unsigned long *)to) = *(1+(const unsigned long *)from); + *(2+(unsigned long *)to) = *(2+(const unsigned long *)from); + *(3+(unsigned long *)to) = *(3+(const unsigned long *)from); + return to; + case 20: + *(unsigned long *)to = *(const unsigned long *)from; + *(1+(unsigned long *)to) = *(1+(const unsigned long *)from); + *(2+(unsigned long *)to) = *(2+(const unsigned long *)from); + *(3+(unsigned long *)to) = *(3+(const unsigned long *)from); + *(4+(unsigned long *)to) = *(4+(const unsigned long *)from); + return to; + } +#define COMMON(x) \ +__asm__ __volatile__( \ + "cld\n\t" \ + "rep ; movsl" \ + x \ + : "=&c" (d0), "=&D" (d1), "=&S" (d2) \ + : "0" (n/4),"1" ((long) to),"2" ((long) from) \ + : "memory"); +{ + int d0, d1, d2; + switch (n % 4) { + case 0: COMMON(""); return to; + case 1: COMMON("\n\tmovsb"); return to; + case 2: COMMON("\n\tmovsw"); return to; + default: COMMON("\n\tmovsw\n\tmovsb"); return to; + } +} + +#undef COMMON +} + +#define __HAVE_ARCH_MEMCPY +#define memcpy(t, f, n) \ +(__builtin_constant_p(n) ? \ + __constant_memcpy((t),(f),(n)) : \ + __memcpy((t),(f),(n))) + +#define __HAVE_ARCH_MEMMOVE +extern inline void * memmove(void * dest,const void * src, size_t n) +{ +int d0, d1, d2; +if (dest +#include +#include +#include +#include +#include + + +#include "nic.h" +#include "sis900.h" +#include "etherboot.h" + +struct nic nic; +unsigned char eth_addr[6]; + +int acpi_base; + +void init_HR_TIMER(void) +{ + int j; + + outl(0x80000840, 0x0cf8); + acpi_base = inb(0x0cfc) | 0x80; + outb(acpi_base, 0x0cfc); + + outl(0x80000874, 0x0cf8); + acpi_base = inw(0x0cfc); + + printk("init_HR_TIMER: acpi_base = %04x\n", acpi_base); + + j = inw(acpi_base + 0x56) | 0x02; // HR_TMR control + outw(j, acpi_base + 0x56); // activate HR_TMR +} + +inline unsigned long currticks(void) +{ + unsigned long int j=0; + + j = inl(acpi_base + 0x4c); + j /= 55555; // HR_TMR runs at 1MHz, etherboot drivers expect 18.2Hz, but this will be close enough. + + return(j); +} + +int test_display_tftp_callback(char *data, int block, int length, int eof) +{ + int i; + +#ifdef DEBUG + printk("RECD block %u, length = %u:\n",block, length); +#endif + for(i=0; i rom_info from main */ + unsigned char *node_addr; + char *packet; + unsigned int packetlen; + void *priv_data; /* driver can hang private data here */ +}; + +#endif /* NIC_H */ diff --git a/src/etherboot/osdep.h b/src/etherboot/osdep.h new file mode 100644 index 0000000000..34445fac57 --- /dev/null +++ b/src/etherboot/osdep.h @@ -0,0 +1,121 @@ +#ifndef __OSDEP_H__ +#define __OSDEP_H__ + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, or (at + * your option) any later version. + */ + +#define __LITTLE_ENDIAN /* x86 */ + +/* Taken from /usr/include/linux/hfs_sysdep.h */ +#if defined(__BIG_ENDIAN) +# if !defined(__constant_htonl) +# define __constant_htonl(x) (x) +# endif +# if !defined(__constant_htons) +# define __constant_htons(x) (x) +# endif +#elif defined(__LITTLE_ENDIAN) +# if !defined(__constant_htonl) +# define __constant_htonl(x) \ + ((unsigned long int)((((unsigned long int)(x) & 0x000000ffU) << 24) | \ + (((unsigned long int)(x) & 0x0000ff00U) << 8) | \ + (((unsigned long int)(x) & 0x00ff0000U) >> 8) | \ + (((unsigned long int)(x) & 0xff000000U) >> 24))) +# endif +# if !defined(__constant_htons) +# define __constant_htons(x) \ + ((unsigned short int)((((unsigned short int)(x) & 0x00ff) << 8) | \ + (((unsigned short int)(x) & 0xff00) >> 8))) +# endif +#else +# error "Don't know if bytes are big- or little-endian!" +#endif + +#define ntohl(x) \ +(__builtin_constant_p(x) ? \ + __constant_htonl((x)) : \ + __swap32(x)) +#define htonl(x) \ +(__builtin_constant_p(x) ? \ + __constant_htonl((x)) : \ + __swap32(x)) +#define ntohs(x) \ +(__builtin_constant_p(x) ? \ + __constant_htons((x)) : \ + __swap16(x)) +#define htons(x) \ +(__builtin_constant_p(x) ? \ + __constant_htons((x)) : \ + __swap16(x)) + +static inline unsigned long int __swap32(unsigned long int x) +{ + __asm__("xchgb %b0,%h0\n\t" + "rorl $16,%0\n\t" + "xchgb %b0,%h0" + : "=q" (x) + : "0" (x)); + return x; +} + +static inline unsigned short int __swap16(unsigned short int x) +{ + __asm__("xchgb %b0,%h0" + : "=q" (x) + : "0" (x)); + return x; +} + +/* Make routines available to all */ +#define swap32(x) __swap32(x) +#define swap16(x) __swap16(x) + +/* Taken from asm/string-486.h */ +#define __HAVE_ARCH_STRNCMP +extern inline int strncmp(const char * cs,const char * ct,int count) +{ +register int __res; +__asm__ __volatile__( + "\n1:\tdecl %3\n\t" + "js 2f\n\t" + "movb (%1),%b0\n\t" + "incl %1\n\t" + "cmpb %b0,(%2)\n\t" + "jne 3f\n\t" + "incl %2\n\t" + "testb %b0,%b0\n\t" + "jne 1b\n" + "2:\txorl %0,%0\n\t" + "jmp 4f\n" + "3:\tmovl $1,%0\n\t" + "jb 4f\n\t" + "negl %0\n" + "4:" + :"=q" (__res), "=r" (cs), "=r" (ct), "=r" (count) + :"1" (cs), "2" (ct), "3" (count)); +return __res; +} + +#include "linux-asm-string.h" +#include "linux-asm-io.h" + +typedef unsigned long Address; + +/* ANSI prototyping macro */ +#ifdef __STDC__ +#define P(x) x +#else +#define P(x) () +#endif + +#endif + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/src/etherboot/pci.h b/src/etherboot/pci.h new file mode 100644 index 0000000000..64ad47f1d8 --- /dev/null +++ b/src/etherboot/pci.h @@ -0,0 +1,178 @@ +#ifndef PCI_H +#define PCI_H + +/* +** Support for NE2000 PCI clones added David Monro June 1997 +** Generalised for other PCI NICs by Ken Yap July 1997 +** +** Most of this is taken from: +** +** /usr/src/linux/drivers/pci/pci.c +** /usr/src/linux/include/linux/pci.h +** /usr/src/linux/arch/i386/bios32.c +** /usr/src/linux/include/linux/bios32.h +** /usr/src/linux/drivers/net/ne.c +*/ + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, or (at + * your option) any later version. + */ + +#define PCI_COMMAND_IO 0x1 /* Enable response in I/O space */ +#define PCI_COMMAND_MASTER 0x4 /* Enable bus mastering */ +#define PCI_LATENCY_TIMER 0x0d /* 8 bits */ + +#define PCIBIOS_PCI_FUNCTION_ID 0xb1XX +#define PCIBIOS_PCI_BIOS_PRESENT 0xb101 +#define PCIBIOS_FIND_PCI_DEVICE 0xb102 +#define PCIBIOS_FIND_PCI_CLASS_CODE 0xb103 +#define PCIBIOS_GENERATE_SPECIAL_CYCLE 0xb106 +#define PCIBIOS_READ_CONFIG_BYTE 0xb108 +#define PCIBIOS_READ_CONFIG_WORD 0xb109 +#define PCIBIOS_READ_CONFIG_DWORD 0xb10a +#define PCIBIOS_WRITE_CONFIG_BYTE 0xb10b +#define PCIBIOS_WRITE_CONFIG_WORD 0xb10c +#define PCIBIOS_WRITE_CONFIG_DWORD 0xb10d + +#define PCI_VENDOR_ID 0x00 /* 16 bits */ +#define PCI_DEVICE_ID 0x02 /* 16 bits */ +#define PCI_COMMAND 0x04 /* 16 bits */ + +#define PCI_REVISION 0x08 /* 8 bits */ +#define PCI_CLASS_CODE 0x0b /* 8 bits */ +#define PCI_SUBCLASS_CODE 0x0a /* 8 bits */ +#define PCI_HEADER_TYPE 0x0e /* 8 bits */ + +#define PCI_BASE_ADDRESS_0 0x10 /* 32 bits */ +#define PCI_BASE_ADDRESS_1 0x14 /* 32 bits */ +#define PCI_BASE_ADDRESS_2 0x18 /* 32 bits */ +#define PCI_BASE_ADDRESS_3 0x1c /* 32 bits */ +#define PCI_BASE_ADDRESS_4 0x20 /* 32 bits */ +#define PCI_BASE_ADDRESS_5 0x24 /* 32 bits */ + +#ifndef PCI_BASE_ADDRESS_IO_MASK +#define PCI_BASE_ADDRESS_IO_MASK (~0x03) +#endif +#define PCI_BASE_ADDRESS_SPACE_IO 0x01 +#define PCI_ROM_ADDRESS 0x30 /* 32 bits */ +#define PCI_ROM_ADDRESS_ENABLE 0x01 /* Write 1 to enable ROM, + bits 31..11 are address, + 10..2 are reserved */ + +#define PCI_FUNC(devfn) ((devfn) & 0x07) + +#define BIOS32_SIGNATURE (('_' << 0) + ('3' << 8) + ('2' << 16) + ('_' << 24)) + +/* PCI signature: "PCI " */ +#define PCI_SIGNATURE (('P' << 0) + ('C' << 8) + ('I' << 16) + (' ' << 24)) + +/* PCI service signature: "$PCI" */ +#define PCI_SERVICE (('$' << 0) + ('P' << 8) + ('C' << 16) + ('I' << 24)) + +union bios32 { + struct { + unsigned long signature; /* _32_ */ + unsigned long entry; /* 32 bit physical address */ + unsigned char revision; /* Revision level, 0 */ + unsigned char length; /* Length in paragraphs should be 01 */ + unsigned char checksum; /* All bytes must add up to zero */ + unsigned char reserved[5]; /* Must be zero */ + } fields; + char chars[16]; +}; + +#define KERN_CODE_SEG 0x8 /* This _MUST_ match start.S */ + +/* Stuff for asm */ +#define save_flags(x) \ +__asm__ __volatile__("pushfl ; popl %0":"=g" (x): /* no input */ :"memory") + +#define cli() __asm__ __volatile__ ("cli": : :"memory") + +#define restore_flags(x) \ +__asm__ __volatile__("pushl %0 ; popfl": /* no output */ :"g" (x):"memory") + +#define PCI_VENDOR_ID_ADMTEK 0x1317 +#define PCI_DEVICE_ID_ADMTEK_0985 0x0985 +#define PCI_VENDOR_ID_REALTEK 0x10ec +#define PCI_DEVICE_ID_REALTEK_8029 0x8029 +#define PCI_DEVICE_ID_REALTEK_8139 0x8139 +#define PCI_VENDOR_ID_WINBOND2 0x1050 +#define PCI_DEVICE_ID_WINBOND2_89C940 0x0940 +#define PCI_DEVICE_ID_WINBOND2_89C840 0x0840 +#define PCI_VENDOR_ID_COMPEX 0x11f6 +#define PCI_DEVICE_ID_COMPEX_RL2000 0x1401 +#define PCI_DEVICE_ID_COMPEX_RL100ATX 0x2011 +#define PCI_VENDOR_ID_KTI 0x8e2e +#define PCI_DEVICE_ID_KTI_ET32P2 0x3000 +#define PCI_VENDOR_ID_NETVIN 0x4a14 +#define PCI_DEVICE_ID_NETVIN_NV5000SC 0x5000 +#define PCI_VENDOR_ID_3COM 0x10b7 +#define PCI_DEVICE_ID_3COM_3C590 0x5900 +#define PCI_DEVICE_ID_3COM_3C595 0x5950 +#define PCI_DEVICE_ID_3COM_3C595_1 0x5951 +#define PCI_DEVICE_ID_3COM_3C595_2 0x5952 +#define PCI_DEVICE_ID_3COM_3C900TPO 0x9000 +#define PCI_DEVICE_ID_3COM_3C900COMBO 0x9001 +#define PCI_DEVICE_ID_3COM_3C905TX 0x9050 +#define PCI_DEVICE_ID_3COM_3C905T4 0x9051 +#define PCI_DEVICE_ID_3COM_3C905B_TX 0x9055 +#define PCI_DEVICE_ID_3COM_3C905C_TXM 0x9200 +#define PCI_VENDOR_ID_INTEL 0x8086 +#define PCI_DEVICE_ID_INTEL_82557 0x1229 +#define PCI_DEVICE_ID_INTEL_82559ER 0x1209 +#define PCI_DEVICE_ID_INTEL_ID1029 0x1029 +#define PCI_DEVICE_ID_INTEL_ID1030 0x1030 +#define PCI_DEVICE_ID_INTEL_82562 0x2449 +#define PCI_VENDOR_ID_AMD 0x1022 +#define PCI_DEVICE_ID_AMD_LANCE 0x2000 +#define PCI_VENDOR_ID_AMD_HOMEPNA 0x1022 +#define PCI_DEVICE_ID_AMD_HOMEPNA 0x2001 +#define PCI_VENDOR_ID_SMC_1211 0x1113 +#define PCI_DEVICE_ID_SMC_1211 0x1211 +#define PCI_VENDOR_ID_DEC 0x1011 +#define PCI_DEVICE_ID_DEC_TULIP 0x0002 +#define PCI_DEVICE_ID_DEC_TULIP_FAST 0x0009 +#define PCI_DEVICE_ID_DEC_TULIP_PLUS 0x0014 +#define PCI_DEVICE_ID_DEC_21142 0x0019 +#define PCI_VENDOR_ID_SMC 0x10B8 +#ifndef PCI_DEVICE_ID_SMC_EPIC100 +# define PCI_DEVICE_ID_SMC_EPIC100 0x0005 +#endif +#define PCI_VENDOR_ID_MACRONIX 0x10d9 +#define PCI_DEVICE_ID_MX987x5 0x0531 +#define PCI_VENDOR_ID_LINKSYS 0x11AD +#define PCI_DEVICE_ID_LC82C115 0xC115 +#define PCI_VENDOR_ID_VIATEC 0x1106 +#define PCI_DEVICE_ID_VIA_RHINE_I 0x3043 +#define PCI_DEVICE_ID_VIA_VT6102 0x3065 +#define PCI_DEVICE_ID_VIA_86C100A 0x6100 +#define PCI_VENDOR_ID_DAVICOM 0x1282 +#define PCI_DEVICE_ID_DM9009 0x9009 +#define PCI_DEVICE_ID_DM9102 0x9102 +#define PCI_VENDOR_ID_SIS 0x1039 +#define PCI_DEVICE_ID_SIS900 0x0900 +#define PCI_DEVICE_ID_SIS7016 0x7016 +#define PCI_VENDOR_ID_DLINK 0x1186 +#define PCI_DEVICE_ID_DFE530TXP 0x1300 + +struct pci_device { + unsigned short vendor, dev_id; + const char *name; + unsigned int membase; + unsigned short ioaddr; + unsigned char devfn; + unsigned char bus; +}; + +extern void eth_pci_init(struct pci_device *); + +extern int pcibios_read_config_byte(unsigned int bus, unsigned int device_fn, unsigned int where, unsigned char *value); +extern int pcibios_write_config_byte (unsigned int bus, unsigned int device_fn, unsigned int where, unsigned char value); +extern int pcibios_read_config_word(unsigned int bus, unsigned int device_fn, unsigned int where, unsigned short *value); +extern int pcibios_write_config_word (unsigned int bus, unsigned int device_fn, unsigned int where, unsigned short value); +extern int pcibios_write_config_dword(unsigned int bus, unsigned int device_fn, unsigned int where, unsigned int value); +#endif /* PCI_H */ diff --git a/src/etherboot/rarp.c b/src/etherboot/rarp.c new file mode 100644 index 0000000000..03c09793dd --- /dev/null +++ b/src/etherboot/rarp.c @@ -0,0 +1,734 @@ +#include "etherboot.h" +#include "nic.h" + +static unsigned long xid; + +#define eth_poll() nic.poll(&nic) +#define eth_transmit(a,b,c,d) nic.transmit(&nic,a,b,c, (char *) d) + +int decode_rfc1533(unsigned char *p, int block, int len, int eof) +{ + return(-1); // don't support it yet, just define it! +} + +#define NO_DHCP_SUPPORT +#define EMERGENCYDISKBOOT + +#define getchar() ttys0_rx_char() + +static const unsigned char broadcast[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +static unsigned long netmask; + +struct arptable_t arptable[MAX_ARP]; + +struct bootpd_t bootp_data; + +/*************************************************************************** +getdec, a simple sort of atoi +***************************************************************************/ +int getdec(char **ptr) +{ + char *p = *ptr; + int ret=0; + if ((*p < '0') || (*p > '9')) return(-1); + while ((*p >= '0') && (*p <= '9')) { + ret = ret*10 + (*p - '0'); + p++; + } + *ptr = p; + return(ret); +} + +/************************************************************************** +STRCASECMP (not entirely correct, but this will do for our purposes) +**************************************************************************/ +int strcasecmp(char *a, char *b) +{ + while (*a && *b && (*a & ~0x20) == (*b & ~0x20)) {a++; b++; } + return((*a & ~0x20) - (*b & ~0x20)); +} + + +/************************************************************************** +DEFAULT_NETMASK - Return default netmask for IP address +**************************************************************************/ +static inline unsigned long default_netmask(void) +{ + int net = ntohl(arptable[ARP_CLIENT].ipaddr.s_addr) >> 24; + if (net <= 127) + return(htonl(0xff000000)); + else if (net < 192) + return(htonl(0xffff0000)); + else + return(htonl(0xffffff00)); +} + +/************************************************************************** +IPCHKSUM - Checksum IP Header +**************************************************************************/ +static unsigned short ipchksum(unsigned short *ip, int len) +{ + unsigned long sum = 0; + len >>= 1; + while (len--) { + sum += *(ip++); + if (sum > 0xFFFF) + sum -= 0xFFFF; + } + return((~sum) & 0x0000FFFF); +} + +/************************************************************************** +RFC951_SLEEP - sleep for expotentially longer times +**************************************************************************/ +void rfc951_sleep(int exp) +{ + static long seed = 0; + long q; + unsigned long tmo; + +#ifdef BACKOFF_LIMIT + if (exp > BACKOFF_LIMIT) + exp = BACKOFF_LIMIT; +#endif + if (!seed) /* Initialize linear congruential generator */ + seed = currticks() + *(long *)&arptable[ARP_CLIENT].node + + ((short *)arptable[ARP_CLIENT].node)[2]; + /* simplified version of the LCG given in Bruce Scheier's + "Applied Cryptography" */ + q = seed/53668; + if ((seed = 40014*(seed-53668*q) - 12211*q) < 0) seed += 2147483563l; + /* compute mask */ + for (tmo = 63; tmo <= 60*TICKS_PER_SEC && --exp > 0; tmo = 2*tmo+1); + /* sleep */ + printk("\n"); + for (tmo = (tmo&seed)+currticks(); currticks() < tmo; ) + return; +} + +/************************************************************************** +AWAIT_REPLY - Wait until we get a response for our request +**************************************************************************/ +int await_reply(int type, int ival, void *ptr, int timeout) +{ + unsigned long time; + struct iphdr *ip; + struct udphdr *udp; + struct arprequest *arpreply; + struct bootp_t *bootpreply; + struct rpc_t *rpc; + unsigned short ptype; + + unsigned int protohdrlen = ETH_HLEN + sizeof(struct iphdr) + + sizeof(struct udphdr); + time = timeout + currticks(); + /* The timeout check is done below. The timeout is only checked if + * there is no packet in the Rx queue. This assumes that eth_poll() + * needs a negligible amount of time. */ + for (;;) { + if (eth_poll()) { /* We have something! */ + /* Check for ARP - No IP hdr */ + if (nic.packetlen >= ETH_HLEN) { + ptype = ((unsigned short) nic.packet[12]) << 8 + | ((unsigned short) nic.packet[13]); + } else continue; /* what else could we do with it? */ + if ((nic.packetlen >= ETH_HLEN + + sizeof(struct arprequest)) && + (ptype == ARP) ) { + unsigned long tmp; + + arpreply = (struct arprequest *) + &nic.packet[ETH_HLEN]; + if ((arpreply->opcode == htons(ARP_REPLY)) && + !memcmp(arpreply->sipaddr, ptr, sizeof(in_addr)) && + (type == AWAIT_ARP)) { + memcpy(arptable[ival].node, arpreply->shwaddr, ETH_ALEN); + return(1); + } + memcpy(&tmp, arpreply->tipaddr, sizeof(in_addr)); + if ((arpreply->opcode == htons(ARP_REQUEST)) && + (tmp == arptable[ARP_CLIENT].ipaddr.s_addr)) { + arpreply->opcode = htons(ARP_REPLY); + memcpy(arpreply->tipaddr, arpreply->sipaddr, sizeof(in_addr)); + memcpy(arpreply->thwaddr, arpreply->shwaddr, ETH_ALEN); + memcpy(arpreply->sipaddr, &arptable[ARP_CLIENT].ipaddr, sizeof(in_addr)); + memcpy(arpreply->shwaddr, arptable[ARP_CLIENT].node, ETH_ALEN); + eth_transmit(arpreply->thwaddr, ARP, + sizeof(struct arprequest), + (char *) arpreply); +#ifdef MDEBUG + memcpy(&tmp, arpreply->tipaddr, sizeof(in_addr)); + printf("Sent ARP reply to: %I\n",tmp); +#endif /* MDEBUG */ + } + continue; + } + + if (type == AWAIT_QDRAIN) { + continue; + } + + /* Check for RARP - No IP hdr */ + if ((type == AWAIT_RARP) && + (nic.packetlen >= ETH_HLEN + + sizeof(struct arprequest)) && + (ptype == RARP)) { + arpreply = (struct arprequest *) + &nic.packet[ETH_HLEN]; + if ((arpreply->opcode == htons(RARP_REPLY)) && + !memcmp(arpreply->thwaddr, ptr, ETH_ALEN)) { + memcpy(arptable[ARP_SERVER].node, arpreply->shwaddr, ETH_ALEN); + memcpy(& arptable[ARP_SERVER].ipaddr, arpreply->sipaddr, sizeof(in_addr)); + memcpy(& arptable[ARP_CLIENT].ipaddr, arpreply->tipaddr, sizeof(in_addr)); + return(1); + } + continue; + } + + /* Anything else has IP header */ + if ((nic.packetlen < protohdrlen) || + (ptype != IP) ) continue; + ip = (struct iphdr *)&nic.packet[ETH_HLEN]; + if ((ip->verhdrlen != 0x45) || + ipchksum((unsigned short *)ip, sizeof(struct iphdr)) || + (ip->protocol != IP_UDP)) continue; + udp = (struct udphdr *)&nic.packet[ETH_HLEN + + sizeof(struct iphdr)]; + + /* BOOTP ? */ + bootpreply = (struct bootp_t *)&nic.packet[ETH_HLEN + sizeof(struct iphdr) + sizeof(struct udphdr)]; + if ((type == AWAIT_BOOTP) && + (nic.packetlen >= (ETH_HLEN + sizeof(struct iphdr) + sizeof(struct udphdr) + +#ifdef NO_DHCP_SUPPORT + sizeof(struct bootp_t))) && +#else + sizeof(struct bootp_t))-DHCP_OPT_LEN) && +#endif /* NO_DHCP_SUPPORT */ + (udp->dest == htons(BOOTP_CLIENT)) && + (bootpreply->bp_op == BOOTP_REPLY) && + (bootpreply->bp_xid == xid) && + (memcmp(broadcast, bootpreply->bp_hwaddr, ETH_ALEN) ==0 || + memcmp(arptable[ARP_CLIENT].node, bootpreply->bp_hwaddr, ETH_ALEN) == 0)) { + arptable[ARP_CLIENT].ipaddr.s_addr = + bootpreply->bp_yiaddr.s_addr; +#ifndef NO_DHCP_SUPPORT + dhcp_addr.s_addr = bootpreply->bp_yiaddr.s_addr; +#endif /* NO_DHCP_SUPPORT */ + netmask = default_netmask(); + arptable[ARP_SERVER].ipaddr.s_addr = + bootpreply->bp_siaddr.s_addr; + memset(arptable[ARP_SERVER].node, 0, ETH_ALEN); /* Kill arp */ + arptable[ARP_GATEWAY].ipaddr.s_addr = + bootpreply->bp_giaddr.s_addr; + memset(arptable[ARP_GATEWAY].node, 0, ETH_ALEN); /* Kill arp */ + /* bootpreply->bp_file will be copied to KERNEL_BUF in the memcpy */ + memcpy((char *)BOOTP_DATA_ADDR, (char *)bootpreply, sizeof(struct bootpd_t)); + decode_rfc1533(BOOTP_DATA_ADDR->bootp_reply.bp_vend, +#ifdef NO_DHCP_SUPPORT + 0, BOOTP_VENDOR_LEN + MAX_BOOTP_EXTLEN, 1); +#else + 0, DHCP_OPT_LEN + MAX_BOOTP_EXTLEN, 1); +#endif /* NO_DHCP_SUPPORT */ +#ifdef REQUIRE_VCI_ETHERBOOT + if (!vci_etherboot) + return (0); +#endif + + return(1); + } + +#ifdef DOWNLOAD_PROTO_TFTP + /* TFTP ? */ + if ((type == AWAIT_TFTP) && + (ntohs(udp->dest) == ival)) return(1); +#endif /* DOWNLOAD_PROTO_TFTP */ + +#ifdef DOWNLOAD_PROTO_NFS + /* RPC ? */ + rpc = (struct rpc_t *)&nic.packet[ETH_HLEN]; + if ((type == AWAIT_RPC) && + (ntohs(udp->dest) == ival) && + (*(unsigned long *)ptr == ntohl(rpc->u.reply.id)) && + (ntohl(rpc->u.reply.type) == MSG_REPLY)) { + return (1); + } +#endif /* DOWNLOAD_PROTO_NFS */ + + } else { + /* Check for abort key only if the Rx queue is empty - + * as long as we have something to process, don't + * assume that something failed. It is unlikely that + * we have no processing time left between packets. */ + if (iskey() && (getchar() == ESC)) +#ifdef EMERGENCYDISKBOOT + return(-1); +#else + longjmp(jmp_bootmenu, -1); +#endif + /* Do the timeout after at least a full queue walk. */ + if ((timeout == 0) || (currticks() > time)) { + break; + } + } + } + return(0); +} + + +/************************************************************************** +RARP - Get my IP address and load information +**************************************************************************/ +int rarp(void) +{ + int retry; + + /* arp and rarp requests share the same packet structure. */ + struct arprequest rarpreq; + + memset(&rarpreq, 0, sizeof(rarpreq)); + + rarpreq.hwtype = htons(1); + rarpreq.protocol = htons(IP); + rarpreq.hwlen = ETH_ALEN; + rarpreq.protolen = 4; + rarpreq.opcode = htons(RARP_REQUEST); + memcpy(&rarpreq.shwaddr, arptable[ARP_CLIENT].node, ETH_ALEN); + /* sipaddr is already zeroed out */ + memcpy(&rarpreq.thwaddr, arptable[ARP_CLIENT].node, ETH_ALEN); + /* tipaddr is already zeroed out */ + + for (retry = 0; retry < MAX_ARP_RETRIES; rfc951_sleep(++retry)) { + eth_transmit(broadcast, RARP, sizeof(rarpreq), &rarpreq); + + if (await_reply(AWAIT_RARP, 0, rarpreq.shwaddr, TIMEOUT)) + break; + } + + if (retry < MAX_ARP_RETRIES) { + (void)sprintf(KERNEL_BUF, DEFAULT_KERNELPATH, arptable[ARP_CLIENT].ipaddr); + + return (1); + } + return (0); +} + +/************************************************************************** +UDP_TRANSMIT - Send a UDP datagram +**************************************************************************/ +int udp_transmit(unsigned long destip, unsigned int srcsock, + unsigned int destsock, int len, const void *buf) +{ + struct iphdr *ip; + struct udphdr *udp; + struct arprequest arpreq; + int arpentry, i; + int retry; + + ip = (struct iphdr *)buf; + udp = (struct udphdr *)((char *)buf + sizeof(struct iphdr)); + ip->verhdrlen = 0x45; + ip->service = 0; + ip->len = htons(len); + ip->ident = 0; + ip->frags = 0; + ip->ttl = 60; + ip->protocol = IP_UDP; + ip->chksum = 0; + ip->src.s_addr = arptable[ARP_CLIENT].ipaddr.s_addr; + ip->dest.s_addr = destip; + ip->chksum = ipchksum((unsigned short *)buf, sizeof(struct iphdr)); + udp->src = htons(srcsock); + udp->dest = htons(destsock); + udp->len = htons(len - sizeof(struct iphdr)); + udp->chksum = 0; + if (destip == IP_BROADCAST) { + eth_transmit(broadcast, IP, len, buf); + } else { + if (((destip & netmask) != + (arptable[ARP_CLIENT].ipaddr.s_addr & netmask)) && + arptable[ARP_GATEWAY].ipaddr.s_addr) + destip = arptable[ARP_GATEWAY].ipaddr.s_addr; + for(arpentry = 0; arpentry\n"); +#endif + udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, + iport, oport, + TFTP_MIN_PACKET, &tp); + continue; + } +#endif + break; /* timeout */ + } + tr = (struct tftp_t *)&nic.packet[ETH_HLEN]; + if (tr->opcode == ntohs(TFTP_ERROR)) + { + printk("TFTP error %d (%s)\n", + ntohs(tr->u.err.errcode), + tr->u.err.errmsg); + break; + } + + if (tr->opcode == ntohs(TFTP_OACK)) { + char *p = tr->u.oack.data, *e; + + if (prevblock) /* shouldn't happen */ + continue; /* ignore it */ + len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 2; + if (len > TFTP_MAX_PACKET) + goto noak; + e = p + len; + while (*p != '\000' && p < e) { + if (!strcasecmp("blksize", p)) { + p += 8; + if ((packetsize = getdec(&p)) < + TFTP_DEFAULTSIZE_PACKET) + goto noak; + while (p < e && *p) p++; + if (p < e) + p++; + } + else { + noak: + tp.opcode = htons(TFTP_ERROR); + tp.u.err.errcode = 8; +/* + * Warning: the following assumes the layout of bootp_t. + * But that's fixed by the IP, UDP and BOOTP specs. + */ + len = sizeof(tp.ip) + sizeof(tp.udp) + sizeof(tp.opcode) + sizeof(tp.u.err.errcode) + + sprintf((char *)tp.u.err.errmsg, + "RFC1782 error") + 1; + udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, + iport, ntohs(tr->udp.src), + len, &tp); + return (0); + } + } + if (p > e) + goto noak; + block = tp.u.ack.block = 0; /* this ensures, that */ + /* the packet does not get */ + /* processed as data! */ + } + else if (tr->opcode == htons(TFTP_DATA)) { + len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 4; + if (len > packetsize) /* shouldn't happen */ + continue; /* ignore it */ + block = ntohs(tp.u.ack.block = tr->u.data.block); } + else /* neither TFTP_OACK nor TFTP_DATA */ + break; + + if ((block || bcounter) && (block != prevblock+1)) { + /* Block order should be continuous */ + tp.u.ack.block = htons(block = prevblock); + } + tp.opcode = htons(TFTP_ACK); + oport = ntohs(tr->udp.src); + udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, iport, + oport, TFTP_MIN_PACKET, &tp); /* ack */ + if ((unsigned short)(block-prevblock) != 1) { + /* Retransmission or OACK, don't process via callback + * and don't change the value of prevblock. */ + continue; + } + prevblock = block; + retry = 0; /* It's the right place to zero the timer? */ + if ((rc = fnc(tr->u.data.download, + ++bcounter, len, len < packetsize)) >= 0) + return(rc); + if (len < packetsize) /* End of data */ + return (1); + } + return (0); +} + +int retry = 0; +static unsigned short iport = 2000; +unsigned short oport; +unsigned short len, block = 0, prevblock = 0; +int bcounter = 0; +struct tftp_t *tr; +struct tftpreq_t tp; +int rc; +int packetsize = TFTP_DEFAULTSIZE_PACKET; + +int tftp_init(const char *name) +{ + retry = 0; + iport = 2000; + block = 0; + prevblock = 0; + bcounter = 0; + packetsize = TFTP_DEFAULTSIZE_PACKET; + + /* Clear out the Rx queue first. It contains nothing of interest, + * except possibly ARP requests from the DHCP/TFTP server. We use + * polling throughout Etherboot, so some time may have passed since we + * last polled the receive queue, which may now be filled with + * broadcast packets. This will cause the reply to the packets we are + * about to send to be lost immediately. Not very clever. */ + await_reply(AWAIT_QDRAIN, 0, NULL, 0); + + tp.opcode = htons(TFTP_RRQ); + /* Warning: the following assumes the layout of bootp_t. + But that's fixed by the IP, UDP and BOOTP specs. */ + len = sizeof(tp.ip) + sizeof(tp.udp) + sizeof(tp.opcode) + + sprintf((char *)tp.u.rrq, "%s%coctet%cblksize%c%d", + name, 0, 0, 0, TFTP_MAX_PACKET) + 1; + if (!udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, ++iport, + TFTP_PORT, len, &tp)) + return (-1); +#ifdef CONGESTED + while (!await_reply(AWAIT_TFTP, iport, NULL, (block ? TFTP_REXMT : TIMEOUT))) +#else + while (!await_reply(AWAIT_TFTP, iport, NULL, TIMEOUT)) +#endif + { + if (!block && retry++ < MAX_TFTP_RETRIES) + { /* maybe initial request was lost */ + rfc951_sleep(retry); + if (!udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, + ++iport, TFTP_PORT, len, &tp)) + return (-1); + continue; + } +#ifdef CONGESTED + if (block && ((retry += TFTP_REXMT) < TFTP_TIMEOUT)) + { /* we resend our last ack */ +#ifdef MDEBUG + printk("\n"); +#endif + udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, + iport, oport, + TFTP_MIN_PACKET, &tp); + continue; + } +#endif + break; /* timeout */ + } + +// we have recieved reply. see if it's any good. + + tr = (struct tftp_t *)&nic.packet[ETH_HLEN]; + if (tr->opcode == ntohs(TFTP_ERROR)) { + printk("TFTP error %d (%s)\n", + ntohs(tr->u.err.errcode), + tr->u.err.errmsg); + + return(-ntohs(tr->u.err.errcode)); + } + + if (tr->opcode == ntohs(TFTP_OACK)) { + char *p = tr->u.oack.data, *e; + + if (prevblock) /* shouldn't happen */ + return(-1); /* ignore it */ + len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 2; + if (len > TFTP_MAX_PACKET) + goto noak; + e = p + len; + while (*p != '\000' && p < e) { + if (!strcasecmp("blksize", p)) { + p += 8; + if ((packetsize = getdec(&p)) < + TFTP_DEFAULTSIZE_PACKET) + goto noak; + while (p < e && *p) p++; + if (p < e) + p++; + } + else { + noak: + tp.opcode = htons(TFTP_ERROR); + tp.u.err.errcode = 8; +/* + * Warning: the following assumes the layout of bootp_t. + * But that's fixed by the IP, UDP and BOOTP specs. + */ + len = sizeof(tp.ip) + sizeof(tp.udp) + sizeof(tp.opcode) + sizeof(tp.u.err.errcode) + + sprintf((char *)tp.u.err.errmsg, + "RFC1782 error") + 1; + udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, + iport, ntohs(tr->udp.src), + len, &tp); + return (-1); + } + } + if (p > e) + goto noak; + block = tp.u.ack.block = 0; /* this ensures, that */ + /* the packet does not get */ + /* processed as data! */ + } + return(0); +} +int tftp_fetchone(char *buffer) +{ + /* Clear out the Rx queue first. It contains nothing of interest, + * except possibly ARP requests from the DHCP/TFTP server. We use + * polling throughout Etherboot, so some time may have passed since we + * last polled the receive queue, which may now be filled with + * broadcast packets. This will cause the reply to the packets we are + * about to send to be lost immediately. Not very clever. */ +retry: + await_reply(AWAIT_QDRAIN, 0, NULL, 0); + + tp.opcode = htons(TFTP_ACK); + oport = ntohs(tr->udp.src); + udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, iport, + oport, TFTP_MIN_PACKET, &tp); /* ack */ + +#ifdef CONGESTED + while (!await_reply(AWAIT_TFTP, iport, NULL, (block ? TFTP_REXMT : TIMEOUT))) +#else + while (!await_reply(AWAIT_TFTP, iport, NULL, TIMEOUT)) +#endif + { + if (!block && retry++ < MAX_TFTP_RETRIES) + { /* maybe initial request was lost */ + rfc951_sleep(retry); + if (!udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, + ++iport, TFTP_PORT, len, &tp)) + return (-1); + continue; + } +#ifdef CONGESTED + if (block && ((retry += TFTP_REXMT) < TFTP_TIMEOUT)) + { /* we resend our last ack */ +#ifdef MDEBUG + printk("\n"); +#endif + udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, + iport, oport, + TFTP_MIN_PACKET, &tp); + continue; + } +#endif + break; /* timeout */ + } + + tr = (struct tftp_t *)&nic.packet[ETH_HLEN]; + if (tr->opcode == ntohs(TFTP_ERROR)) + { + printk("TFTP error %d (%s)\n", + ntohs(tr->u.err.errcode), + tr->u.err.errmsg); + return(-tr->u.err.errcode); + } + + if (tr->opcode == ntohs(TFTP_OACK)) + goto retry; // session is already started! try again. + + else if (tr->opcode == htons(TFTP_DATA)) { + len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 4; + if (len > packetsize) /* shouldn't happen */ + goto retry; /* bad thing, skip it and try again! */ + block = ntohs(tp.u.ack.block = tr->u.data.block); + + } else /* neither TFTP_OACK nor TFTP_DATA */ + goto retry; // whatever it is, it's invalid! try again. + + if ((block || bcounter) && (block != prevblock+1)) { + /* Block order should be continuous */ + tp.u.ack.block = htons(block = prevblock); + goto retry; + } + + prevblock = block; + retry = 0; /* It's the right place to zero the timer? */ + + + memcpy(buffer,tr->u.data.download, len); + return(len); +} diff --git a/src/etherboot/sis900.c b/src/etherboot/sis900.c new file mode 100644 index 0000000000..f2ca51e269 --- /dev/null +++ b/src/etherboot/sis900.c @@ -0,0 +1,1073 @@ +/* -*- Mode:C; c-basic-offset:4; -*- */ + +/* + sis900.c: An SiS 900/7016 PCI Fast Ethernet driver for Etherboot + Copyright (C) 2001 Entity Cyber, Inc. + + Revision: 1.0 March 1, 2001 + + Author: Marty Connor (mdc@thinguin.org) + + Adapted from a Linux driver which was written by Donald Becker + and modified by Ollie Lho and Chin-Shan Li of SiS Corporation. + Rewritten for Etherboot by Marty Connor. + + This software may be used and distributed according to the terms + of the GNU Public License (GPL), incorporated herein by reference. + + References: + SiS 7016 Fast Ethernet PCI Bus 10/100 Mbps LAN Controller with OnNow Support, + preliminary Rev. 1.0 Jan. 14, 1998 + SiS 900 Fast Ethernet PCI Bus 10/100 Mbps LAN Single Chip with OnNow Support, + preliminary Rev. 1.0 Nov. 10, 1998 + SiS 7014 Single Chip 100BASE-TX/10BASE-T Physical Layer Solution, + preliminary Rev. 1.0 Jan. 18, 1998 + http://www.sis.com.tw/support/databook.htm */ + +/* Revision History */ + +/* + 01 March 2001 mdc 1.0 + Initial Release. Tested with PCI based sis900 card and ThinkNIC + computer. + 20 March 2001 P.Koegel + added support for sis630e and PHY ICS1893 and RTL8201 + Testet with SIS730S chipset + ICS1893 +*/ + + +/* Includes */ + +#include +#include "etherboot.h" +#include "nic.h" +#include "pci.h" +#include "cards.h" + +#include "sis900.h" + +/* Globals */ + +static int sis900_debug = 0; + +static unsigned short vendor, dev_id; +static unsigned long ioaddr; + +static unsigned int cur_phy; + +static unsigned int cur_rx; + +static BufferDesc txd; +static BufferDesc rxd[NUM_RX_DESC]; + +#ifdef USE_LOWMEM_BUFFER +#define txb ((char *)0x10000 - TX_BUF_SIZE) +#define rxb ((char *)0x10000 - NUM_RX_DESC*RX_BUF_SIZE - TX_BUF_SIZE) +#else +static unsigned char txb[TX_BUF_SIZE]; +static unsigned char rxb[NUM_RX_DESC * RX_BUF_SIZE]; +#endif + +static struct mac_chip_info { + const char *name; + u16 vendor_id, device_id, flags; + int io_size; +} mac_chip_table[] = { + { "SiS 900 PCI Fast Ethernet", PCI_VENDOR_ID_SIS, PCI_DEVICE_ID_SIS900, + PCI_COMMAND_IO|PCI_COMMAND_MASTER, SIS900_TOTAL_SIZE}, + { "SiS 7016 PCI Fast Ethernet",PCI_VENDOR_ID_SIS, PCI_DEVICE_ID_SIS7016, + PCI_COMMAND_IO|PCI_COMMAND_MASTER, SIS900_TOTAL_SIZE}, + {0,0,0,0,0} /* 0 terminated list. */ +}; + +static void sis900_read_mode(struct nic *nic, int phy_addr, int *speed, int *duplex); +static void amd79c901_read_mode(struct nic *nic, int phy_addr, int *speed, int *duplex); +static void ics1893_read_mode(struct nic *nic, int phy_addr, int *speed, int *duplex); +static void rtl8201_read_mode(struct nic *nic, int phy_addr, int *speed, int *duplex); + +static struct mii_chip_info { + const char * name; + u16 phy_id0; + u16 phy_id1; + void (*read_mode) (struct nic *nic, int phy_addr, int *speed, int *duplex); +} mii_chip_table[] = { + {"SiS 900 Internal MII PHY", 0x001d, 0x8000, sis900_read_mode}, + {"SiS 7014 Physical Layer Solution", 0x0016, 0xf830,sis900_read_mode}, + {"AMD 79C901 10BASE-T PHY", 0x0000, 0x35b9, amd79c901_read_mode}, + {"AMD 79C901 HomePNA PHY", 0x0000, 0x35c8, amd79c901_read_mode}, + {"ICS 1893 Integrated PHYceiver" , 0x0015, 0xf441,ics1893_read_mode}, + {"RTL 8201 10/100Mbps Phyceiver" , 0x0000, 0x8201,rtl8201_read_mode}, + {0,0,0,0} +}; + +static struct mii_phy { + struct mii_phy * next; + struct mii_chip_info * chip_info; + int phy_addr; + u16 status; +} mii; + + +// PCI to ISA bridge for SIS640E access +static struct pci_device pci_isa_bridge_list[] = { + { 0x1039, 0x0008, + "SIS 85C503/5513 PCI to ISA bridge", 0, 0, 0, 0}, + {0, 0, NULL, 0, 0, 0, 0} +}; + +/* Function Prototypes */ + +struct nic *sis900_probe(struct nic *nic, unsigned short *io_addrs, struct pci_device *pci); + +static u16 sis900_read_eeprom(int location); +static void sis900_mdio_reset(long mdio_addr); +static void sis900_mdio_idle(long mdio_addr); +static u16 sis900_mdio_read(int phy_id, int location); +static void sis900_mdio_write(int phy_id, int location, int val); + +static void sis900_init(struct nic *nic); + +static void sis900_reset(struct nic *nic); + +static void sis900_init_rxfilter(struct nic *nic); +static void sis900_init_txd(struct nic *nic); +static void sis900_init_rxd(struct nic *nic); +static void sis900_set_rx_mode(struct nic *nic); +static void sis900_check_mode(struct nic *nic); + +static void sis900_transmit(struct nic *nic, const char *d, + unsigned int t, unsigned int s, const char *p); +static int sis900_poll(struct nic *nic); + +static void sis900_disable(struct nic *nic); + +/** + * sis900_get_mac_addr: - Get MAC address for stand alone SiS900 model + * @pci_dev: the sis900 pci device + * @net_dev: the net device to get address for + * + * Older SiS900 and friends, use EEPROM to store MAC address. + * MAC address is read from read_eeprom() into @net_dev->dev_addr. + */ + +static int sis900_get_mac_addr(struct pci_device * pci_dev , struct nic *nic) +{ + u16 signature; + int i; + + /* check to see if we have sane EEPROM */ + signature = (u16) sis900_read_eeprom( EEPROMSignature); + if (signature == 0xffff || signature == 0x0000) { + printk ("sis900_probe: Error EERPOM read %x\n", signature); + return 0; + } + + /* get MAC address from EEPROM */ + for (i = 0; i < 3; i++) + ((u16 *)(nic->node_addr))[i] = sis900_read_eeprom(i+EEPROMMACAddr); + return 1; +} + +/** + * sis630e_get_mac_addr: - Get MAC address for SiS630E model + * @pci_dev: the sis900 pci device + * @net_dev: the net device to get address for + * + * SiS630E model, use APC CMOS RAM to store MAC address. + * APC CMOS RAM is accessed through ISA bridge. + * MAC address is read into @net_dev->dev_addr. + */ + +#if 0 +static int sis630e_get_mac_addr(struct pci_device * pci_dev, struct nic *nic) +{ + u8 reg; + int i; + struct pci_device *p; + + // find PCI to ISA bridge + eth_pci_init(pci_isa_bridge_list); + + /* the firts entry in this list should contain bus/devfn */ + p = pci_isa_bridge_list; + + pcibios_read_config_byte(p->bus,p->devfn, 0x48, ®); + pcibios_write_config_byte(p->bus,p->devfn, 0x48, reg | 0x40); + + for (i = 0; i < 6; i++) + { + outb(0x09 + i, 0x70); + ((u8 *)(nic->node_addr))[i] = inb(0x71); + } + pcibios_write_config_byte(p->bus,p->devfn, 0x48, reg & ~0x40); + + return 1; +} + +#else + +static int sis630e_get_mac_addr(struct pci_device * pci_dev, struct nic *nic) +{ + u8 reg; + int i; + struct pci_device *p; + + // find PCI to ISA bridge +// eth_pci_init(pci_isa_bridge_list); + + /* the firts entry in this list should contain bus/devfn */ + // p = pci_isa_bridge_list; + + outl(0x80000848, 0xcf8); + outb(0x50,0x0cfc); +// pcibios_read_config_byte(p->bus,p->devfn, 0x48, ®); +// pcibios_write_config_byte(p->bus,p->devfn, 0x48, reg | 0x40); + + for (i = 0; i < 6; i++) + { + outb(0x09 + i, 0x70); + ((u8 *)(nic->node_addr))[i] = inb(0x71); + } +// pcibios_write_config_byte(p->bus,p->devfn, 0x48, reg & ~0x40); + + return 1; +} + +#endif + +/* + * Function: sis900_probe + * + * Description: initializes initializes the NIC, retrieves the + * MAC address of the card, and sets up some globals required by + * other routines. + * + * Side effects: + * leaves the ioaddress of the sis900 chip in the variable ioaddr. + * leaves the sis900 initialized, and ready to recieve packets. + * + * Returns: struct nic *: pointer to NIC data structure + */ + +struct nic *sis900_probe(struct nic *nic, unsigned short *io_addrs, struct pci_device *pci) +{ + int i; + int found=0; + int phy_addr; + u16 signature; + u8 revision; + int ret; + + if (io_addrs == 0 || *io_addrs == 0) + return NULL; + + ioaddr = *io_addrs & ~3; + vendor = pci->vendor; + dev_id = pci->dev_id; + + /* wakeup chip */ + pcibios_write_config_dword(pci->bus, pci->devfn, 0x40, 0x00000000); + + /* get MAC address */ + ret = 0; + pcibios_read_config_byte(pci->bus,pci->devfn, PCI_REVISION, &revision); + ret = sis630e_get_mac_addr(pci, nic); + +#if 0 + if (revision == SIS630E_900_REV || revision == SIS630EA1_900_REV) + ret = sis630e_get_mac_addr(pci, nic); + else if (revision == SIS630S_900_REV) + ret = sis630e_get_mac_addr(pci, nic); + else + ret = sis900_get_mac_addr(pci, nic); +#endif + + if (ret == 0) + { + printk ("sis900_probe: Error MAC address not found\n"); + return NULL; + } + + printk("\nsis900_probe: MAC addr %02x:%02x:%02x:%02x:%02x:%02x at ioaddr %04x\n", + nic->node_addr[0],nic->node_addr[1],nic->node_addr[2], + nic->node_addr[3],nic->node_addr[4],nic->node_addr[5], + ioaddr); + printk("sis900_probe: Vendor:%04x Device:%04x\n", vendor, dev_id); + + /* probe for mii transceiver */ + /* search for total of 32 possible mii phy addresses */ + + found = 0; + for (phy_addr = 0; phy_addr < 32; phy_addr++) { + u16 mii_status; + u16 phy_id0, phy_id1; + + mii_status = sis900_mdio_read(phy_addr, MII_STATUS); + if (mii_status == 0xffff || mii_status == 0x0000) + /* the mii is not accessable, try next one */ + continue; + + phy_id0 = sis900_mdio_read(phy_addr, MII_PHY_ID0); + phy_id1 = sis900_mdio_read(phy_addr, MII_PHY_ID1); + + /* search our mii table for the current mii */ + for (i = 0; mii_chip_table[i].phy_id1; i++) { + + if (phy_id0 == mii_chip_table[i].phy_id0) { + + printk("sis900_probe: %s transceiver found at address %d.\n", + mii_chip_table[i].name, phy_addr); + + mii.chip_info = &mii_chip_table[i]; + mii.phy_addr = phy_addr; + mii.status = sis900_mdio_read(phy_addr, MII_STATUS); + mii.next = NULL; + + found=1; + break; + } else { + printk("Found an MII, but can't recognize it. id= %u:%u\n", phy_id0, phy_id1); + } + } + } + + if (found == 0) { + printk("sis900_probe: No MII transceivers found!\n"); + return NULL; + } + + /* Arbitrarily select the last PHY found as current PHY */ + cur_phy = mii.phy_addr; + printk("sis900_probe: Using %s as default\n", mii.chip_info->name); + + /* initialize device */ + sis900_init(nic); + + nic->reset = sis900_init; + nic->poll = sis900_poll; + nic->transmit = sis900_transmit; + nic->disable = sis900_disable; + + return nic; +} + + +/* + * EEPROM Routines: These functions read and write to EEPROM for + * retrieving the MAC address and other configuration information about + * the card. + */ + +/* Delay between EEPROM clock transitions. */ +#define eeprom_delay() inl(ee_addr) + + +/* Function: sis900_read_eeprom + * + * Description: reads and returns a given location from EEPROM + * + * Arguments: int location: requested EEPROM location + * + * Returns: u16: contents of requested EEPROM location + * + */ + +/* Read Serial EEPROM through EEPROM Access Register, Note that location is + in word (16 bits) unit */ +static u16 sis900_read_eeprom(int location) +{ + int i; + u16 retval = 0; + long ee_addr = ioaddr + mear; + u32 read_cmd = location | EEread; + + outl(0, ee_addr); + eeprom_delay(); + outl(EECLK, ee_addr); + eeprom_delay(); + + /* Shift the read command (9) bits out. */ + for (i = 8; i >= 0; i--) { + u32 dataval = (read_cmd & (1 << i)) ? EEDI | EECS : EECS; + outl(dataval, ee_addr); + eeprom_delay(); + outl(dataval | EECLK, ee_addr); + eeprom_delay(); + } + outb(EECS, ee_addr); + eeprom_delay(); + + /* read the 16-bits data in */ + for (i = 16; i > 0; i--) { + outl(EECS, ee_addr); + eeprom_delay(); + outl(EECS | EECLK, ee_addr); + eeprom_delay(); + retval = (retval << 1) | ((inl(ee_addr) & EEDO) ? 1 : 0); + eeprom_delay(); + } + + /* Terminate the EEPROM access. */ + outl(0, ee_addr); + eeprom_delay(); + outl(EECLK, ee_addr); + + return (retval); +} + +#define sis900_mdio_delay() inl(mdio_addr) + + +/* + Read and write the MII management registers using software-generated + serial MDIO protocol. Note that the command bits and data bits are + send out seperately +*/ + +static void sis900_mdio_idle(long mdio_addr) +{ + outl(MDIO | MDDIR, mdio_addr); + sis900_mdio_delay(); + outl(MDIO | MDDIR | MDC, mdio_addr); +} + +/* Syncronize the MII management interface by shifting 32 one bits out. */ +static void sis900_mdio_reset(long mdio_addr) +{ + int i; + + for (i = 31; i >= 0; i--) { + outl(MDDIR | MDIO, mdio_addr); + sis900_mdio_delay(); + outl(MDDIR | MDIO | MDC, mdio_addr); + sis900_mdio_delay(); + } + return; +} + +static u16 sis900_mdio_read(int phy_id, int location) +{ + long mdio_addr = ioaddr + mear; + int mii_cmd = MIIread|(phy_id<= 0; i--) { + int dataval = (mii_cmd & (1 << i)) ? MDDIR | MDIO : MDDIR; + outl(dataval, mdio_addr); + sis900_mdio_delay(); + outl(dataval | MDC, mdio_addr); + sis900_mdio_delay(); + } + + /* Read the 16 data bits. */ + for (i = 16; i > 0; i--) { + outl(0, mdio_addr); + sis900_mdio_delay(); + retval = (retval << 1) | ((inl(mdio_addr) & MDIO) ? 1 : 0); + outl(MDC, mdio_addr); + sis900_mdio_delay(); + } + return retval; +} + +static void sis900_mdio_write(int phy_id, int location, int value) +{ + long mdio_addr = ioaddr + mear; + int mii_cmd = MIIwrite|(phy_id<= 0; i--) { + int dataval = (mii_cmd & (1 << i)) ? MDDIR | MDIO : MDDIR; + outb(dataval, mdio_addr); + sis900_mdio_delay(); + outb(dataval | MDC, mdio_addr); + sis900_mdio_delay(); + } + sis900_mdio_delay(); + + /* Shift the value bits out. */ + for (i = 15; i >= 0; i--) { + int dataval = (value & (1 << i)) ? MDDIR | MDIO : MDDIR; + outl(dataval, mdio_addr); + sis900_mdio_delay(); + outl(dataval | MDC, mdio_addr); + sis900_mdio_delay(); + } + sis900_mdio_delay(); + + /* Clear out extra bits. */ + for (i = 2; i > 0; i--) { + outb(0, mdio_addr); + sis900_mdio_delay(); + outb(MDC, mdio_addr); + sis900_mdio_delay(); + } + return; +} + + +/* Function: sis900_init + * + * Description: resets the ethernet controller chip and various + * data structures required for sending and receiving packets. + * + * Arguments: struct nic *nic: NIC data structure + * + * returns: void. + */ + +static void +sis900_init(struct nic *nic) +{ + /* Soft reset the chip. */ + sis900_reset(nic); + + sis900_init_rxfilter(nic); + + sis900_init_txd(nic); + sis900_init_rxd(nic); + + sis900_set_rx_mode(nic); + + sis900_check_mode(nic); + + outl(RxENA, ioaddr + cr); +} + + +/* + * Function: sis900_reset + * + * Description: disables interrupts and soft resets the controller chip + * + * Arguments: struct nic *nic: NIC data structure + * + * Returns: void. + */ + +static void +sis900_reset(struct nic *nic) +{ + int i = 0; + u32 status = TxRCMP | RxRCMP; + + outl(0, ioaddr + ier); + outl(0, ioaddr + imr); + outl(0, ioaddr + rfcr); + + outl(RxRESET | TxRESET | RESET, ioaddr + cr); + + /* Check that the chip has finished the reset. */ + while (status && (i++ < 1000)) { + status ^= (inl(isr + ioaddr) & status); + } + outl(PESEL, ioaddr + cfg); +} + + +/* Function: sis_init_rxfilter + * + * Description: sets receive filter address to our MAC address + * + * Arguments: struct nic *nic: NIC data structure + * + * returns: void. + */ + +static void +sis900_init_rxfilter(struct nic *nic) +{ + u32 rfcrSave; + int i; + + rfcrSave = inl(rfcr + ioaddr); + + /* disable packet filtering before setting filter */ + outl(rfcrSave & ~RFEN, rfcr); + + /* load MAC addr to filter data register */ + for (i = 0 ; i < 3 ; i++) { + u32 w; + + w = (u32) *((u16 *)(nic->node_addr)+i); + outl((i << RFADDR_shift), ioaddr + rfcr); + outl(w, ioaddr + rfdr); + + if (sis900_debug > 0) + printk("sis900_init_rxfilter: Receive Filter Addrss[%d]=%x\n", + i, inl(ioaddr + rfdr)); + } + + /* enable packet filitering */ + outl(rfcrSave | RFEN, rfcr + ioaddr); +} + + +/* + * Function: sis_init_txd + * + * Description: initializes the Tx descriptor + * + * Arguments: struct nic *nic: NIC data structure + * + * returns: void. + */ + +static void +sis900_init_txd(struct nic *nic) +{ + txd.link = (u32) 0; + txd.cmdsts = (u32) 0; + txd.bufptr = (u32) &txb[0]; + + /* load Transmit Descriptor Register */ + outl((u32) &txd, ioaddr + txdp); + if (sis900_debug > 0) + printk("sis900_init_txd: TX descriptor register loaded with: %X\n", + inl(ioaddr + txdp)); +} + + +/* Function: sis_init_rxd + * + * Description: initializes the Rx descriptor ring + * + * Arguments: struct nic *nic: NIC data structure + * + * Returns: void. + */ + +static void +sis900_init_rxd(struct nic *nic) +{ + int i; + + cur_rx = 0; + + /* init RX descriptor */ + for (i = 0; i < NUM_RX_DESC; i++) { + rxd[i].link = (i+1 < NUM_RX_DESC) ? (u32) &rxd[i+1] : (u32) &rxd[0]; + rxd[i].cmdsts = (u32) RX_BUF_SIZE; + rxd[i].bufptr = (u32) &rxb[i*RX_BUF_SIZE]; + if (sis900_debug > 0) + printk("sis900_init_rxd: rxd[%d]=%X link=%X cmdsts=%X bufptr=%X\n", + i, &rxd[i], rxd[i].link, rxd[i].cmdsts, rxd[i].bufptr); + } + + /* load Receive Descriptor Register */ + outl((u32) &rxd[0], ioaddr + rxdp); + + if (sis900_debug > 0) + printk("sis900_init_rxd: RX descriptor register loaded with: %X\n", + inl(ioaddr + rxdp)); + +} + + +/* Function: sis_init_rxd + * + * Description: + * sets the receive mode to accept all broadcast packets and packets + * with our MAC address, and reject all multicast packets. + * + * Arguments: struct nic *nic: NIC data structure + * + * Returns: void. + */ + +static void sis900_set_rx_mode(struct nic *nic) +{ + int i; + + /* Configure Multicast Hash Table in Receive Filter + to reject all MCAST packets */ + for (i = 0; i < 8; i++) { + /* why plus 0x04? That makes the correct value for hash table. */ + outl((u32)(0x00000004+i) << RFADDR_shift, ioaddr + rfcr); + outl((u32)(0x0), ioaddr + rfdr); + } + /* Accept Broadcast packets, destination addresses that match + our MAC address */ + outl(RFEN | RFAAB, ioaddr + rfcr); + + return; +} + + +/* Function: sis900_check_mode + * + * Description: checks the state of transmit and receive + * parameters on the NIC, and updates NIC registers to match + * + * Arguments: struct nic *nic: NIC data structure + * + * Returns: void. + */ + +static void +sis900_check_mode (struct nic *nic) +{ + int speed, duplex; + u32 tx_flags = 0, rx_flags = 0; + + mii.chip_info->read_mode(nic, cur_phy, &speed, &duplex); + + tx_flags = TxATP | (TX_DMA_BURST << TxMXDMA_shift) | (TX_FILL_THRESH << TxFILLT_shift); + rx_flags = RX_DMA_BURST << RxMXDMA_shift; + + if (speed == HW_SPEED_HOME || speed == HW_SPEED_10_MBPS) { + rx_flags |= (RxDRNT_10 << RxDRNT_shift); + tx_flags |= (TxDRNT_10 << TxDRNT_shift); + } + else { + rx_flags |= (RxDRNT_100 << RxDRNT_shift); + tx_flags |= (TxDRNT_100 << TxDRNT_shift); + } + + if (duplex == FDX_CAPABLE_FULL_SELECTED) { + tx_flags |= (TxCSI | TxHBI); + rx_flags |= RxATX; + } + + outl (tx_flags, ioaddr + txcfg); + outl (rx_flags, ioaddr + rxcfg); +} + + +/* Function: sis900_read_mode + * + * Description: retrieves and displays speed and duplex + * parameters from the NIC + * + * Arguments: struct nic *nic: NIC data structure + * + * Returns: void. + */ + +static void +sis900_read_mode(struct nic *nic, int phy_addr, int *speed, int *duplex) +{ + int i = 0; + u32 status; + + /* STSOUT register is Latched on Transition, read operation updates it */ + while (i++ < 2) + status = sis900_mdio_read(phy_addr, MII_STSOUT); + + if (status & MII_STSOUT_SPD) + *speed = HW_SPEED_100_MBPS; + else + *speed = HW_SPEED_10_MBPS; + + if (status & MII_STSOUT_DPLX) + *duplex = FDX_CAPABLE_FULL_SELECTED; + else + *duplex = FDX_CAPABLE_HALF_SELECTED; + + if (status & MII_STSOUT_LINK_FAIL) + printk("sis900_read_mode: Media Link Off\n"); + else + printk("sis900_read_mode: Media Link On %s %s-duplex \n", + *speed == HW_SPEED_100_MBPS ? + "100mbps" : "10mbps", + *duplex == FDX_CAPABLE_FULL_SELECTED ? + "full" : "half"); +} + + +/* Function: amd79c901_read_mode + * + * Description: retrieves and displays speed and duplex + * parameters from the NIC + * + * Arguments: struct nic *nic: NIC data structure + * + * Returns: void. + */ + +static void +amd79c901_read_mode(struct nic *nic, int phy_addr, int *speed, int *duplex) +{ + int i; + u16 status; + + for (i = 0; i < 2; i++) + status = sis900_mdio_read(phy_addr, MII_STATUS); + + if (status & MII_STAT_CAN_AUTO) { + /* 10BASE-T PHY */ + for (i = 0; i < 2; i++) + status = sis900_mdio_read(phy_addr, MII_STATUS_SUMMARY); + if (status & MII_STSSUM_SPD) + *speed = HW_SPEED_100_MBPS; + else + *speed = HW_SPEED_10_MBPS; + if (status & MII_STSSUM_DPLX) + *duplex = FDX_CAPABLE_FULL_SELECTED; + else + *duplex = FDX_CAPABLE_HALF_SELECTED; + + if (status & MII_STSSUM_LINK) + printk("amd79c901_read_mode: Media Link On %s %s-duplex \n", + *speed == HW_SPEED_100_MBPS ? + "100mbps" : "10mbps", + *duplex == FDX_CAPABLE_FULL_SELECTED ? + "full" : "half"); + else + printk("amd79c901_read_mode: Media Link Off\n"); + } + else { + /* HomePNA */ + *speed = HW_SPEED_HOME; + *duplex = FDX_CAPABLE_HALF_SELECTED; + if (status & MII_STAT_LINK) + printk("amd79c901_read_mode:Media Link On 1mbps half-duplex \n"); + else + printk("amd79c901_read_mode: Media Link Off\n"); + } +} + + +/** + * ics1893_read_mode: - read media mode for ICS1893 PHY + * @net_dev: the net device to read mode for + * @phy_addr: mii phy address + * @speed: the transmit speed to be determined + * @duplex: the duplex mode to be determined + * + * ICS1893 PHY use Quick Poll Detailed Status register + * to determine the speed and duplex mode for sis900 + */ + +static void ics1893_read_mode(struct nic *nic, int phy_addr, int *speed, int *duplex) +{ + int i = 0; + u32 status; + + /* MII_QPDSTS is Latched, read twice in succession will reflect the current state */ + for (i = 0; i < 2; i++) + status = sis900_mdio_read(phy_addr, MII_QPDSTS); + + if (status & MII_STSICS_SPD) + *speed = HW_SPEED_100_MBPS; + else + *speed = HW_SPEED_10_MBPS; + + if (status & MII_STSICS_DPLX) + *duplex = FDX_CAPABLE_FULL_SELECTED; + else + *duplex = FDX_CAPABLE_HALF_SELECTED; + + if (status & MII_STSICS_LINKSTS) + printk("ics1893_read_mode: Media Link On %s %s-duplex \n", + *speed == HW_SPEED_100_MBPS ? + "100mbps" : "10mbps", + *duplex == FDX_CAPABLE_FULL_SELECTED ? + "full" : "half"); + else + printk("ics1893_read_mode: Media Link Off\n"); +} + +/** + * rtl8201_read_mode: - read media mode for rtl8201 phy + * @nic: the net device to read mode for + * @phy_addr: mii phy address + * @speed: the transmit speed to be determined + * @duplex: the duplex mode to be determined + * + * read MII_STATUS register from rtl8201 phy + * to determine the speed and duplex mode for sis900 + */ + +static void rtl8201_read_mode(struct nic *nic, int phy_addr, int *speed, int *duplex) +{ + u32 status; + + status = sis900_mdio_read(phy_addr, MII_STATUS); + + if (status & MII_STAT_CAN_TX_FDX) { + *speed = HW_SPEED_100_MBPS; + *duplex = FDX_CAPABLE_FULL_SELECTED; + } + else if (status & MII_STAT_CAN_TX) { + *speed = HW_SPEED_100_MBPS; + *duplex = FDX_CAPABLE_HALF_SELECTED; + } + else if (status & MII_STAT_CAN_T_FDX) { + *speed = HW_SPEED_10_MBPS; + *duplex = FDX_CAPABLE_FULL_SELECTED; + } + else if (status & MII_STAT_CAN_T) { + *speed = HW_SPEED_10_MBPS; + *duplex = FDX_CAPABLE_HALF_SELECTED; + } + + if (status & MII_STAT_LINK) + printk("rtl8201_read_mode: Media Link On %s %s-duplex \n", + *speed == HW_SPEED_100_MBPS ? + "100mbps" : "10mbps", + *duplex == FDX_CAPABLE_FULL_SELECTED ? + "full" : "half"); + else + printk("rtl9201_read_config_mode: Media Link Off\n"); +} + +/* Function: sis900_transmit + * + * Description: transmits a packet and waits for completion or timeout. + * + * Arguments: char d[6]: destination ethernet address. + * unsigned short t: ethernet protocol type. + * unsigned short s: size of the data-part of the packet. + * char *p: the data for the packet. + * + * Returns: void. + */ + +static void +sis900_transmit(struct nic *nic, + const char *d, /* Destination */ + unsigned int t, /* Type */ + unsigned int s, /* size */ + const char *p) /* Packet */ +{ + u32 status, to, nstype; + u32 tx_status; + + /* Stop the transmitter */ + outl(TxDIS, ioaddr + cr); + + /* load Transmit Descriptor Register */ + outl((u32) &txd, ioaddr + txdp); + if (sis900_debug > 1) + printk("sis900_transmit: TX descriptor register loaded with: %X\n", + inl(ioaddr + txdp)); + + memcpy(txb, d, ETH_ALEN); + memcpy(txb + ETH_ALEN, nic->node_addr, ETH_ALEN); + nstype = htons(t); + memcpy(txb + 2 * ETH_ALEN, (char*)&nstype, 2); + memcpy(txb + ETH_HLEN, p, s); + + s += ETH_HLEN; + s &= DSIZE; + + if (sis900_debug > 1) + printk("sis900_transmit: sending %d bytes ethtype %x\n", (int) s, t); + + /* pad to minimum packet size */ + while (s < ETH_ZLEN) + txb[s++] = '\0'; + + /* set the transmit buffer descriptor and enable Transmit State Machine */ + txd.bufptr = (u32) &txb[0]; + txd.cmdsts = (u32) OWN | s; + + /* restart the transmitter */ + outl(TxENA, ioaddr + cr); + + if (sis900_debug > 1) + printk("sis900_transmit: Queued Tx packet size %d.\n", (int) s); + + to = currticks() + TX_TIMEOUT; + + while ((((volatile u32) tx_status=txd.cmdsts) & OWN) && (currticks() < to)) + /* wait */ ; + + if (currticks() >= to) { + printk("sis900_transmit: TX Timeout! Tx status %X.\n", tx_status); + } + + if (tx_status & (ABORT | UNDERRUN | OWCOLL)) { + /* packet unsuccessfully transmited */ + printk("sis900_transmit: Transmit error, Tx status %X.\n", tx_status); + } + /* Disable interrupts by clearing the interrupt mask. */ + outl(0, ioaddr + imr); +} + + +/* Function: sis900_poll + * + * Description: checks for a received packet and returns it if found. + * + * Arguments: struct nic *nic: NIC data structure + * + * Returns: 1 if a packet was recieved. + * 0 if no pacet was recieved. + * + * Side effects: + * Returns (copies) the packet to the array nic->packet. + * Returns the length of the packet in nic->packetlen. + */ + +static int +sis900_poll(struct nic *nic) +{ + u32 rx_status = rxd[cur_rx].cmdsts; + int retstat = 0; + + if (sis900_debug > 2) + printk("sis900_poll: cur_rx:%d, status:%X\n", cur_rx, rx_status); + + if (!(rx_status & OWN)) + return retstat; + + if (sis900_debug > 1) + printk("sis900_poll: got a packet: cur_rx:%d, status:%X\n", + cur_rx, rx_status); + + nic->packetlen = (rx_status & DSIZE) - CRC_SIZE; + + if (rx_status & (ABORT|OVERRUN|TOOLONG|RUNT|RXISERR|CRCERR|FAERR)) { + /* corrupted packet received */ + printk("sis900_poll: Corrupted packet received, buffer status = %X\n", + rx_status); + retstat = 0; + } else { + /* give packet to higher level routine */ + memcpy(nic->packet, (rxb + cur_rx*RX_BUF_SIZE), nic->packetlen); + retstat = 1; + } + + /* return the descriptor and buffer to receive ring */ + rxd[cur_rx].cmdsts = RX_BUF_SIZE; + rxd[cur_rx].bufptr = (u32) &rxb[cur_rx*RX_BUF_SIZE]; + + if (++cur_rx == NUM_RX_DESC) + cur_rx = 0; + + /* re-enable the potentially idle receive state machine */ + outl(RxENA , ioaddr + cr); + + return retstat; +} + + +/* Function: sis900_disable + * + * Description: Turns off interrupts and stops Tx and Rx engines + * + * Arguments: struct nic *nic: NIC data structure + * + * Returns: void. + */ + +static void +sis900_disable(struct nic *nic) +{ + /* Disable interrupts by clearing the interrupt mask. */ + outl(0, ioaddr + imr); + outl(0, ioaddr + ier); + + /* Stop the chip's Tx and Rx Status Machine */ + outl(RxDIS | TxDIS, ioaddr + cr); +} diff --git a/src/etherboot/sis900.h b/src/etherboot/sis900.h new file mode 100644 index 0000000000..b40032e9f4 --- /dev/null +++ b/src/etherboot/sis900.h @@ -0,0 +1,365 @@ +/* -*- Mode:C; c-basic-offset:4; -*- */ + +/* Definitions for SiS ethernet controllers including 7014/7016 and 900 + * References: + * SiS 7016 Fast Ethernet PCI Bus 10/100 Mbps LAN Controller with OnNow Support, + * preliminary Rev. 1.0 Jan. 14, 1998 + * SiS 900 Fast Ethernet PCI Bus 10/100 Mbps LAN Single Chip with OnNow Support, + * preliminary Rev. 1.0 Nov. 10, 1998 + * SiS 7014 Single Chip 100BASE-TX/10BASE-T Physical Layer Solution, + * preliminary Rev. 1.0 Jan. 18, 1998 + * http://www.sis.com.tw/support/databook.htm + */ + +/* MAC operationl registers of SiS 7016 and SiS 900 ethernet controller */ +/* The I/O extent, SiS 900 needs 256 bytes of io address */ +#define SIS900_TOTAL_SIZE 0x100 + +/* Symbolic offsets to registers. */ +enum sis900_registers { + cr=0x0, /* Command Register */ + cfg=0x4, /* Configuration Register */ + mear=0x8, /* EEPROM Access Register */ + ptscr=0xc, /* PCI Test Control Register */ + isr=0x10, /* Interrupt Status Register */ + imr=0x14, /* Interrupt Mask Register */ + ier=0x18, /* Interrupt Enable Register */ + epar=0x18, /* Enhanced PHY Access Register */ + txdp=0x20, /* Transmit Descriptor Pointer Register */ + txcfg=0x24, /* Transmit Configuration Register */ + rxdp=0x30, /* Receive Descriptor Pointer Register */ + rxcfg=0x34, /* Receive Configuration Register */ + flctrl=0x38, /* Flow Control Register */ + rxlen=0x3c, /* Receive Packet Length Register */ + rfcr=0x48, /* Receive Filter Control Register */ + rfdr=0x4C, /* Receive Filter Data Register */ + pmctrl=0xB0, /* Power Management Control Register */ + pmer=0xB4 /* Power Management Wake-up Event Register */ +}; + +/* Symbolic names for bits in various registers */ +enum sis900_command_register_bits { + RESET = 0x00000100, + SWI = 0x00000080, + RxRESET = 0x00000020, + TxRESET = 0x00000010, + RxDIS = 0x00000008, + RxENA = 0x00000004, + TxDIS = 0x00000002, + TxENA = 0x00000001 +}; + +enum sis900_configuration_register_bits { + DESCRFMT = 0x00000100, /* 7016 specific */ + REQALG = 0x00000080, + SB = 0x00000040, + POW = 0x00000020, + EXD = 0x00000010, + PESEL = 0x00000008, + LPM = 0x00000004, + BEM = 0x00000001 +}; + +enum sis900_eeprom_access_reigster_bits { + MDC = 0x00000040, + MDDIR = 0x00000020, + MDIO = 0x00000010, /* 7016 specific */ + EECS = 0x00000008, + EECLK = 0x00000004, + EEDO = 0x00000002, + EEDI = 0x00000001 +}; + +enum sis900_interrupt_register_bits { + WKEVT = 0x10000000, + TxPAUSEEND = 0x08000000, + TxPAUSE = 0x04000000, + TxRCMP = 0x02000000, + RxRCMP = 0x01000000, + DPERR = 0x00800000, + SSERR = 0x00400000, + RMABT = 0x00200000, + RTABT = 0x00100000, + RxSOVR = 0x00010000, + HIBERR = 0x00008000, + SWINT = 0x00001000, + MIBINT = 0x00000800, + TxURN = 0x00000400, + TxIDLE = 0x00000200, + TxERR = 0x00000100, + TxDESC = 0x00000080, + TxOK = 0x00000040, + RxORN = 0x00000020, + RxIDLE = 0x00000010, + RxEARLY = 0x00000008, + RxERR = 0x00000004, + RxDESC = 0x00000002, + RxOK = 0x00000001 +}; + +enum sis900_interrupt_enable_reigster_bits { + IE = 0x00000001 +}; + +/* maximum dma burst fro transmission and receive*/ +#define MAX_DMA_RANGE 7 /* actually 0 means MAXIMUM !! */ +#define TxMXDMA_shift 20 +#define RxMXDMA_shift 20 +#define TX_DMA_BURST 0 +#define RX_DMA_BURST 0 + +/* transmit FIFO threshholds */ +#define TX_FILL_THRESH 16 /* 1/4 FIFO size */ +#define TxFILLT_shift 8 +#define TxDRNT_shift 0 +#define TxDRNT_100 48 /* 3/4 FIFO size */ +#define TxDRNT_10 16 /* 1/2 FIFO size */ + +enum sis900_transmit_config_register_bits { + TxCSI = 0x80000000, + TxHBI = 0x40000000, + TxMLB = 0x20000000, + TxATP = 0x10000000, + TxIFG = 0x0C000000, + TxFILLT = 0x00003F00, + TxDRNT = 0x0000003F +}; + +/* recevie FIFO thresholds */ +#define RxDRNT_shift 1 +#define RxDRNT_100 16 /* 1/2 FIFO size */ +#define RxDRNT_10 24 /* 3/4 FIFO size */ + +enum sis900_reveive_config_register_bits { + RxAEP = 0x80000000, + RxARP = 0x40000000, + RxATX = 0x10000000, + RxAJAB = 0x08000000, + RxDRNT = 0x0000007F +}; + +#define RFAA_shift 28 +#define RFADDR_shift 16 + +enum sis900_receive_filter_control_register_bits { + RFEN = 0x80000000, + RFAAB = 0x40000000, + RFAAM = 0x20000000, + RFAAP = 0x10000000, + RFPromiscuous = (RFAAB|RFAAM|RFAAP) +}; + +enum sis900_reveive_filter_data_mask { + RFDAT = 0x0000FFFF +}; + +/* EEPROM Addresses */ +enum sis900_eeprom_address { + EEPROMSignature = 0x00, + EEPROMVendorID = 0x02, + EEPROMDeviceID = 0x03, + EEPROMMACAddr = 0x08, + EEPROMChecksum = 0x0b +}; + +/* The EEPROM commands include the alway-set leading bit. Refer to NM93Cxx datasheet */ +enum sis900_eeprom_command { + EEread = 0x0180, + EEwrite = 0x0140, + EEerase = 0x01C0, + EEwriteEnable = 0x0130, + EEwriteDisable = 0x0100, + EEeraseAll = 0x0120, + EEwriteAll = 0x0110, + EEaddrMask = 0x013F, +}; + +/* Manamgement Data I/O (mdio) frame */ +#define MIIread 0x6000 +#define MIIwrite 0x5002 +#define MIIpmdShift 7 +#define MIIregShift 2 +#define MIIcmdLen 16 +#define MIIcmdShift 16 + +/* Buffer Descriptor Status*/ +enum sis900_buffer_status { + OWN = 0x80000000, + MORE = 0x40000000, + INTR = 0x20000000, + SUPCRC = 0x10000000, + INCCRC = 0x10000000, + OK = 0x08000000, + DSIZE = 0x00000FFF +}; + +/* Status for TX Buffers */ +enum sis900_tx_buffer_status { + ABORT = 0x04000000, + UNDERRUN = 0x02000000, + NOCARRIER = 0x01000000, + DEFERD = 0x00800000, + EXCDEFER = 0x00400000, + OWCOLL = 0x00200000, + EXCCOLL = 0x00100000, + COLCNT = 0x000F0000 +}; + +enum sis900_rx_bufer_status { + OVERRUN = 0x02000000, + DEST = 0x00800000, + BCAST = 0x01800000, + MCAST = 0x01000000, + UNIMATCH = 0x00800000, + TOOLONG = 0x00400000, + RUNT = 0x00200000, + RXISERR = 0x00100000, + CRCERR = 0x00080000, + FAERR = 0x00040000, + LOOPBK = 0x00020000, + RXCOL = 0x00010000 +}; + +/* MII register offsets */ +enum mii_registers { + MII_CONTROL = 0x0000, + MII_STATUS = 0x0001, + MII_PHY_ID0 = 0x0002, + MII_PHY_ID1 = 0x0003, + MII_ANADV = 0x0004, + MII_ANLPAR = 0x0005, + MII_ANEXT = 0x0006 +}; + +/* mii registers specific to SiS 900 */ +enum sis_mii_registers { + MII_CONFIG1 = 0x0010, + MII_CONFIG2 = 0x0011, + MII_STSOUT = 0x0012, + MII_MASK = 0x0013 +}; + +/* mii registers specific to AMD 79C901 */ +enum amd_mii_registers { + MII_STATUS_SUMMARY = 0x0018 +}; + +/* mii registers specific to ICS 1893 */ +enum ics_mii_registers { + MII_EXTCTRL = 0x0010, MII_QPDSTS = 0x0011, MII_10BTOP = 0x0012, + MII_EXTCTRL2 = 0x0013 +}; + + + +/* MII Control register bit definitions. */ +enum mii_control_register_bits { + MII_CNTL_FDX = 0x0100, + MII_CNTL_RST_AUTO = 0x0200, + MII_CNTL_ISOLATE = 0x0400, + MII_CNTL_PWRDWN = 0x0800, + MII_CNTL_AUTO = 0x1000, + MII_CNTL_SPEED = 0x2000, + MII_CNTL_LPBK = 0x4000, + MII_CNTL_RESET = 0x8000 +}; + +/* MII Status register bit */ +enum mii_status_register_bits { + MII_STAT_EXT = 0x0001, + MII_STAT_JAB = 0x0002, + MII_STAT_LINK = 0x0004, + MII_STAT_CAN_AUTO = 0x0008, + MII_STAT_FAULT = 0x0010, + MII_STAT_AUTO_DONE = 0x0020, + MII_STAT_CAN_T = 0x0800, + MII_STAT_CAN_T_FDX = 0x1000, + MII_STAT_CAN_TX = 0x2000, + MII_STAT_CAN_TX_FDX = 0x4000, + MII_STAT_CAN_T4 = 0x8000 +}; + +#define MII_ID1_OUI_LO 0xFC00 /* low bits of OUI mask */ +#define MII_ID1_MODEL 0x03F0 /* model number */ +#define MII_ID1_REV 0x000F /* model number */ + +/* MII NWAY Register Bits ... + valid for the ANAR (Auto-Negotiation Advertisement) and + ANLPAR (Auto-Negotiation Link Partner) registers */ +enum mii_nway_register_bits { + MII_NWAY_NODE_SEL = 0x001f, + MII_NWAY_CSMA_CD = 0x0001, + MII_NWAY_T = 0x0020, + MII_NWAY_T_FDX = 0x0040, + MII_NWAY_TX = 0x0080, + MII_NWAY_TX_FDX = 0x0100, + MII_NWAY_T4 = 0x0200, + MII_NWAY_PAUSE = 0x0400, + MII_NWAY_RF = 0x2000, + MII_NWAY_ACK = 0x4000, + MII_NWAY_NP = 0x8000 +}; + +enum mii_stsout_register_bits { + MII_STSOUT_LINK_FAIL = 0x4000, + MII_STSOUT_SPD = 0x0080, + MII_STSOUT_DPLX = 0x0040 +}; + +enum mii_stsics_register_bits { + MII_STSICS_SPD = 0x8000, MII_STSICS_DPLX = 0x4000, + MII_STSICS_LINKSTS = 0x0001 +}; + +enum mii_stssum_register_bits { + MII_STSSUM_LINK = 0x0008, + MII_STSSUM_DPLX = 0x0004, + MII_STSSUM_AUTO = 0x0002, + MII_STSSUM_SPD = 0x0001 +}; + +enum sis900_revision_id { + SIS630A_900_REV = 0x80, SIS630E_900_REV = 0x81, + SIS630S_900_REV = 0x82, SIS630EA1_900_REV = 0x83 +}; + +enum sis630_revision_id { + SIS630A0 = 0x00, SIS630A1 = 0x01, + SIS630B0 = 0x10, SIS630B1 = 0x11 +}; + +#define FDX_CAPABLE_DUPLEX_UNKNOWN 0 +#define FDX_CAPABLE_HALF_SELECTED 1 +#define FDX_CAPABLE_FULL_SELECTED 2 + +#define HW_SPEED_UNCONFIG 0 +#define HW_SPEED_HOME 1 +#define HW_SPEED_10_MBPS 10 +#define HW_SPEED_100_MBPS 100 +#define HW_SPEED_DEFAULT (HW_SPEED_100_MBPS) + +#define CRC_SIZE 4 +#define MAC_HEADER_SIZE 14 + +#define TX_BUF_SIZE 1536 +#define RX_BUF_SIZE 1536 + +#define NUM_RX_DESC 4 /* Number of Rx descriptor registers. */ + +/* etherboot needs these defined. LinuxBIOS does it for us +typedef unsigned char u8; +typedef signed char s8; +typedef unsigned short u16; +typedef signed short s16; +typedef unsigned int u32; +typedef signed int s32; +*/ + +/* Time in ticks before concluding the transmitter is hung. */ +#define TX_TIMEOUT (4*TICKS_PER_SEC) + +typedef struct _BufferDesc { + u32 link; + volatile u32 cmdsts; + u32 bufptr; +} BufferDesc; diff --git a/src/etherboot/sis900.txt b/src/etherboot/sis900.txt new file mode 100644 index 0000000000..77419fad66 --- /dev/null +++ b/src/etherboot/sis900.txt @@ -0,0 +1,91 @@ +How I added the SIS900 card to Etherboot + +Author: Marty Connor (mdc@thinguin.org) + +Date: 25 Febrary 2001 + +Description: + +This file is intended to help people who want to write an Etherboot +driver or port another driver to Etherboot. It is a starting point. +Perhaps someday I may write a more detailed description of writing an +Etherboot driver. This text should help get people started, and +studying sis900.[ch] should help show the basic structure and +techniques involved in writing and Etherboot driver. + +*********************************************************************** + +0. Back up all the files I need to modify: + +cd etherboot-4.7.20/src +cp Makefile Makefile.orig +cp config.c config.c.orig +cp pci.h pci.h.orig +cp NIC NIC.orig +cp cards.h cards.h.orig + +1. Edit src/Makefile to add SIS900FLAGS to defines + +SIS900FLAGS= -DINCLUDE_SIS900 + +2. edit src/pci.h to add PCI signatures for card + +#define PCI_VENDOR_ID_SIS 0x1039 +#define PCI_DEVICE_ID_SIS900 0x0900 +#define PCI_DEVICE_ID_SIS7016 0x7016 + +3. Edit src/config.c to add the card to the card probe list + +#if defined(INCLUDE_NS8390) || defined(INCLUDE_EEPRO100) || + defined(INCLUDE_LANCE) || defined(INCLUDE_EPIC100) || + defined(INCLUDE_TULIP) || defined(INCLUDE_OTULIP) || + defined(INCLUDE_3C90X) || defined(INCLUDE_3C595) || + defined(INCLUDE_RTL8139) || defined(INCLUDE_VIA_RHINE) || + defined(INCLUDE_SIS900) || defined(INCLUDE_W89C840) + +... and ... + +#ifdef INCLUDE_SIS900 + { PCI_VENDOR_ID_SIS, PCI_DEVICE_ID_SIS900, + "SIS900", 0, 0, 0, 0}, + { PCI_VENDOR_ID_SIS, PCI_DEVICE_ID_SIS7016, + "SIS7016", 0, 0, 0, 0}, +#endif + +... and ... + +#ifdef INCLUDE_SIS900 + { "SIS900", sis900_probe, pci_ioaddrs }, +#endif + +4. Edit NIC to add sis900 and sis7016 to NIC list + +# SIS 900 and SIS 7016 +sis900 sis900 0x1039,0x0900 +sis7016 sis900 0x1039,0x7016 + +5. Edit cards.h to add sis900 probe routine declaration + +#ifdef INCLUDE_SIS900 +extern struct nic *sis900_probe(struct nic *, unsigned short * + PCI_ARG(struct pci_device *)); +#endif + +*********************************************************************** + +At this point, you can begin creating your driver source file. See +the "Writing and Etherboot Driver" section of the Etherboot +documentation for some hints. See the skel.c file for a starting +point. If there is a Linux driver for the card, you may be able to +use that. Copy and learn from existing Etherboot drivers (this is GPL +/ Open Source software!). + +Join the etherboot-developers and etherboot-users mailing lists +(information is on etherboot.sourceforge.net) for information and +assistance. We invite more developers to help improve Etherboot. + +Visit the http://etherboot.sourceforge.net, http://thinguin.org, +http://rom-o-matic.net, and http://ltsp.org sites for information and +assistance. + +Enjoy. diff --git a/src/etherboot/timer.h b/src/etherboot/timer.h new file mode 100644 index 0000000000..b44962a79d --- /dev/null +++ b/src/etherboot/timer.h @@ -0,0 +1,64 @@ +/* Defines for routines to implement a low-overhead timer for drivers */ + + /* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, or (at + * your option) any later version. + */ + +#ifndef TIMER_H +#define TIMER_H + +/* Ports for the 8254 timer chip */ +#define TIMER2_PORT 0x42 +#define TIMER_MODE_PORT 0x43 + +/* Meaning of the mode bits */ +#define TIMER0_SEL 0x00 +#define TIMER1_SEL 0x40 +#define TIMER2_SEL 0x80 +#define READBACK_SEL 0xC0 + +#define LATCH_COUNT 0x00 +#define LOBYTE_ACCESS 0x10 +#define HIBYTE_ACCESS 0x20 +#define WORD_ACCESS 0x30 + +#define MODE0 0x00 +#define MODE1 0x02 +#define MODE2 0x04 +#define MODE3 0x06 +#define MODE4 0x08 +#define MODE5 0x0A + +#define BINARY_COUNT 0x00 +#define BCD_COUNT 0x01 + +/* Timers tick over at this rate */ +#define TICKS_PER_MS 1193 + +/* Parallel Peripheral Controller Port B */ +#define PPC_PORTB 0x61 + +/* Meaning of the port bits */ +#define PPCB_T2OUT 0x20 /* Bit 5 */ +#define PPCB_SPKR 0x02 /* Bit 1 */ +#define PPCB_T2GATE 0x01 /* Bit 0 */ + +/* Ticks must be between 0 and 65535 (0 == 65536) + because it is a 16 bit counter */ +extern void load_timer2(unsigned int ticks); +extern inline int timer2_running(void) +{ + return ((inb(PPC_PORTB) & PPCB_T2OUT) == 0); +} + +extern inline void waiton_timer2(unsigned int ticks) +{ + load_timer2(ticks); + while ((inb(PPC_PORTB) & PPCB_T2OUT) == 0) + ; +} + +#endif /* TIMER_H */ diff --git a/src/lib/linuxbiosmain.c b/src/lib/linuxbiosmain.c index 8878801264..5898522116 100644 --- a/src/lib/linuxbiosmain.c +++ b/src/lib/linuxbiosmain.c @@ -28,7 +28,11 @@ #endif #include "do_inflate.h" - +#ifdef USE_TFTP +extern void netboot_init(void); +extern int tftp_init(const char *name); +extern int tftp_fetchone(char *buffer); +#endif int linuxbiosmain(unsigned long base, unsigned long totalram) { @@ -37,6 +41,11 @@ int linuxbiosmain(unsigned long base, unsigned long totalram) unsigned char *cmd_line; unsigned long initrd_start, initrd_size; +#ifdef USE_TFTP +char buffer[256]; +char *bufptr; +int buflen; +#endif #if USE_ELF_BOOT return elfboot(totalram); @@ -72,6 +81,16 @@ int linuxbiosmain(unsigned long base, unsigned long totalram) post_code(0xf1); +#ifdef PYRO_TEST1 +printk(KERN_NOTICE "LiLa loader, press a key to test netboot_init:"); +buflen = sizeof(buffer); +ttys0_rx_line(buffer, &buflen); +#endif + +#ifdef USE_TFTP + netboot_init(); + printk(KERN_NOTICE "\nnetboot_init test complete, all is well (I hope!)\n"); +#endif DBG("Gunzip setup\n"); gunzip_setup(); @@ -83,6 +102,35 @@ int linuxbiosmain(unsigned long base, unsigned long totalram) } post_code(0xf8); +#ifdef TFTP_INITRD + printk("Loading initrd now\n"); + + buflen = tftp_init("initrd"); + printk("TFTP init complete (%d)\n",buflen); + buflen = 512; // I know, not it's purpose, + // but it isn't being used at this point. + bufptr = initrd_start = 0x0400000; + while(buflen == 512) { + buflen = tftp_fetchone(bufptr); +#ifdef DEBUG_TFTP +printk("Got block, bufptr = %lu, size= %u\n",bufptr, buflen); +#endif + bufptr += buflen; + } + initrd_size = bufptr - initrd_start; + + printk("Initrd loaded\n"); + + if(tftp_init("cmdline") >=0) { + tftp_fetchone(buffer); + cmd_line=buffer; + } + + printk("Booting with command line: %s\n",cmd_line); + + +#endif + /* parameter passing to linux. You have to get the pointer to the * empty_zero_page, then fill it in. */ diff --git a/src/lib/serial_subr.c b/src/lib/serial_subr.c index c5ace84c72..9e22695eb4 100644 --- a/src/lib/serial_subr.c +++ b/src/lib/serial_subr.c @@ -36,6 +36,38 @@ static char rcsid[] = "$Id$"; #define TTYS0_DIV (115200/TTYS0_BAUD) +#ifdef PYRO_SERIAL + +/* experimental serial read stuffs */ +int iskey(void) { + if( inb(TTYS0_LSR) & 0x01) + return(1); + + return(0); +} + +char ttys0_rx_char(void) { + char result; + + while(!(inb(TTYS0_LSR) & 0x01)); + + result = inb(TTYS0_RBR); + return(result); +} + +void ttys0_rx_line(char *buffer, int *len) { + int pos=0; + char chargot=0; + + while(chargot != '\r' && chargot != '\n' && pos< *len) { + chargot = ttys0_rx_char(); + buffer[pos++] = chargot; + } + + *len = pos-1; +} + +#endif void ttys0_init(void) { /* disable interrupts */ diff --git a/src/northsouthbridge/sis/630/Config b/src/northsouthbridge/sis/630/Config index 3179e56284..e0f958d8db 100644 --- a/src/northsouthbridge/sis/630/Config +++ b/src/northsouthbridge/sis/630/Config @@ -1,5 +1,5 @@ option SIS630 -option USE_DOC_MIL +# option USE_DOC_MIL option SMA_SIZE=0xA0 docipl northsouthbridge/sis/630/ipl.S raminit northsouthbridge/sis/630/raminit.inc diff --git a/src/northsouthbridge/sis/730/Config b/src/northsouthbridge/sis/730/Config index c93eae7642..1cd74c8350 100644 --- a/src/northsouthbridge/sis/730/Config +++ b/src/northsouthbridge/sis/730/Config @@ -1,5 +1,5 @@ option SIS730 -option USE_DOC_MIL +# option USE_DOC_MIL option SMA_SIZE=0xA0 docipl northsouthbridge/sis/730/ipl.S raminit northsouthbridge/sis/730/raminit.inc diff --git a/src/northsouthbridge/sis/730/ipl.S b/src/northsouthbridge/sis/730/ipl.S index 8e519411d9..66868a5217 100644 --- a/src/northsouthbridge/sis/730/ipl.S +++ b/src/northsouthbridge/sis/730/ipl.S @@ -32,6 +32,9 @@ #include "ipl.h" .code16 +#ifdef STD_FLASH +.org 0xfe00 +#endif #define SIZE_ALL #define REALLY_COMPACT @@ -210,7 +213,20 @@ sis730ipl_start: movw %ax, %ss movw $SPL_RAM_SEG, %ax movw %ax, %es +#ifdef STD_FLASH + movw $0xf000, %ax + movw %ax, %ds + xorw %sp, %sp # clear %sp + xorw %si, %si + xorw %di, %di + movw $0xffff, %cx + rep + movsb + movw $DOC_WIN_SEG, %ax + movw %ax, %ds + +#else movw $sis950_init_table, %si # unlock SiS 950 LPC movw $0x05, %cx # select Clock Selection movw $0x2e, %dx # and Flash ROM I/F @@ -278,6 +294,7 @@ doc_delay: incw %bp # increse current page number cmpw $128, %bp # moved 63 KB ?? jl read_next_page # no, read next page +#endif /*else STD_FLASH */ sis730ipl_end: jmp spl_vector # jump to SPL vector @@ -395,10 +412,18 @@ pci_init_table: .word 0x0000 /* Null, End of table */ - .org 0x01f0 +#ifdef STD_FLASH + .org 0xfff0 +reset_vector: + .byte 0xea # jmp to fe00:0000, where IPL + .word 0xfe00, 0xf000 # starts in DoC + +#else + .org 0x1f0 reset_vector: .byte 0xea # jmp to fe00:0000, where IPL .word 0x0000, DOC_WIN_SEG # starts in DoC +#endif spl_vector: .byte 0xea # jmp to 8000:0000, where SPL diff --git a/src/rom/Config b/src/rom/Config index 7a2ab811f0..ccbd31483f 100644 --- a/src/rom/Config +++ b/src/rom/Config @@ -2,3 +2,4 @@ object fill_inbuf.o object rom_fill_inbuf.o object docmil_fill_inbuf.o object tsunami_tigbus_rom_fill_inbuf.o +object tftp_fill_inbuf.o diff --git a/util/config/NLBConfig.py b/util/config/NLBConfig.py index bad37e2782..4e4f953f38 100644 --- a/util/config/NLBConfig.py +++ b/util/config/NLBConfig.py @@ -128,6 +128,14 @@ def dir(base_dir, name): def mainboard(dir, mainboard_name): common_command_action(dir, 'mainboard', mainboard_name) +def etherboot(dir, net_name): + common_command_action(dir, 'etherboot', '') + option(dir,'OLD_KERNEL_HACK') + option(dir,'USE_TFTP') + option(dir,'TFTP_INITRD') + option(dir,'PYRO_SERIAL') + + # old legacy PC junk, which will be dying soon. def keyboard(dir, keyboard_name): if (debug): @@ -363,6 +371,7 @@ command_actions = { 'dir' : dir, 'keyboard' : keyboard, 'docipl' : docipl, + 'etherboot' : etherboot, 'makedefine' : makedefine, 'makerule' : makerule, 'addaction' : addaction,