mirror of
https://github.com/ipxe/ipxe
synced 2025-12-11 22:11:08 +03:00
Initial revision
This commit is contained in:
5196
src/core/btext.c
Normal file
5196
src/core/btext.c
Normal file
File diff suppressed because it is too large
Load Diff
161
src/core/config.c
Normal file
161
src/core/config.c
Normal file
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* 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 "etherboot.h"
|
||||
#include "nic.h"
|
||||
#ifdef BUILD_SERIAL
|
||||
#include ".buildserial.h"
|
||||
#define xstr(s) str(s)
|
||||
#define str(s) #s
|
||||
#endif
|
||||
|
||||
void print_config(void)
|
||||
{
|
||||
printf( "Etherboot " VERSION
|
||||
#ifdef BUILD_SERIAL
|
||||
" [build "
|
||||
#ifdef BUILD_ID
|
||||
BUILD_ID " "
|
||||
#endif
|
||||
"#" xstr(BUILD_SERIAL_NUM) "]"
|
||||
#endif /* BUILD_SERIAL */
|
||||
" (GPL) http://etherboot.org\n"
|
||||
"Drivers: " );
|
||||
#ifdef CONFIG_PCI
|
||||
pci_enumerate();
|
||||
#endif
|
||||
#ifdef CONFIG_ISA
|
||||
isa_enumerate();
|
||||
#endif
|
||||
printf( " Images: "
|
||||
#ifdef TAGGED_IMAGE
|
||||
"NBI "
|
||||
#endif
|
||||
#ifdef ELF64_IMAGE
|
||||
"ELF64 "
|
||||
#endif
|
||||
#ifdef ELF_IMAGE
|
||||
"ELF "
|
||||
#endif
|
||||
#ifdef COFF_IMAGE
|
||||
"COFF "
|
||||
#endif
|
||||
#ifdef IMAGE_FREEBSD
|
||||
"FreeBSD "
|
||||
#endif
|
||||
#ifdef IMAGE_MULTIBOOT
|
||||
"Multiboot "
|
||||
#endif
|
||||
#ifdef AOUT_IMAGE
|
||||
"a.out "
|
||||
#endif
|
||||
#ifdef WINCE_IMAGE
|
||||
"WINCE "
|
||||
#endif
|
||||
#ifdef PXE_IMAGE
|
||||
"PXE "
|
||||
#endif
|
||||
#ifdef PXE_EXPORT /* All possible exports */
|
||||
" Exports: "
|
||||
#ifdef PXE_EXPORT
|
||||
"PXE "
|
||||
#endif
|
||||
#endif /* All possible exports */
|
||||
" "
|
||||
);
|
||||
#if (BOOTP_SERVER != 67) || (BOOTP_CLIENT != 68)
|
||||
printf( "[DHCP ports %d and %d] ",
|
||||
BOOTP_SERVER, BOOTP_CLIENT);
|
||||
#endif
|
||||
putchar('\n');
|
||||
printf( "Protocols: "
|
||||
#ifdef RARP_NOT_BOOTP
|
||||
"RARP "
|
||||
#else
|
||||
# ifndef NO_DHCP_SUPPORT
|
||||
"DHCP "
|
||||
# else
|
||||
"BOOTP "
|
||||
# endif
|
||||
#endif
|
||||
#ifdef DOWNLOAD_PROTO_TFTP
|
||||
"TFTP "
|
||||
#endif
|
||||
#ifdef DOWNLOAD_PROTO_NFS
|
||||
"NFS "
|
||||
#endif
|
||||
#ifdef DOWNLOAD_PROTO_SLAM
|
||||
"SLAM "
|
||||
#endif
|
||||
#ifdef DOWNLOAD_PROTO_TFTM
|
||||
"TFTM "
|
||||
#endif
|
||||
#ifdef DOWNLOAD_PROTO_HTTP
|
||||
"HTTP "
|
||||
#endif
|
||||
#ifdef PROTO_LACP
|
||||
"LACP "
|
||||
#endif
|
||||
#ifdef DNS_RESOLVER
|
||||
"DNS "
|
||||
#endif
|
||||
"\n");
|
||||
}
|
||||
|
||||
static const char *driver_name[] = {
|
||||
"nic",
|
||||
"disk",
|
||||
"floppy",
|
||||
};
|
||||
|
||||
int probe(struct dev *dev)
|
||||
{
|
||||
const char *type_name;
|
||||
type_name = "";
|
||||
if ((dev->type >= 0) &&
|
||||
((unsigned)dev->type < sizeof(driver_name)/sizeof(driver_name[0]))) {
|
||||
type_name = driver_name[dev->type];
|
||||
}
|
||||
if (dev->how_probe == PROBE_FIRST) {
|
||||
dev->to_probe = PROBE_PCI;
|
||||
memset(&dev->state, 0, sizeof(dev->state));
|
||||
}
|
||||
if (dev->to_probe == PROBE_PCI) {
|
||||
#ifdef CONFIG_PCI
|
||||
dev->how_probe = pci_probe(dev, type_name);
|
||||
#else
|
||||
dev->how_probe = PROBE_FAILED;
|
||||
#endif
|
||||
if (dev->how_probe == PROBE_FAILED) {
|
||||
dev->to_probe = PROBE_ISA;
|
||||
}
|
||||
}
|
||||
if (dev->to_probe == PROBE_ISA) {
|
||||
#ifdef CONFIG_ISA
|
||||
dev->how_probe = isa_probe(dev, type_name);
|
||||
#else
|
||||
dev->how_probe = PROBE_FAILED;
|
||||
#endif
|
||||
if (dev->how_probe == PROBE_FAILED) {
|
||||
dev->to_probe = PROBE_NONE;
|
||||
}
|
||||
}
|
||||
if ((dev->to_probe != PROBE_PCI) &&
|
||||
(dev->to_probe != PROBE_ISA)) {
|
||||
dev->how_probe = PROBE_FAILED;
|
||||
|
||||
}
|
||||
return dev->how_probe;
|
||||
}
|
||||
|
||||
void disable(struct dev *dev)
|
||||
{
|
||||
if (dev->disable) {
|
||||
dev->disable(dev);
|
||||
dev->disable = 0;
|
||||
}
|
||||
}
|
||||
283
src/core/disk.c
Normal file
283
src/core/disk.c
Normal file
@@ -0,0 +1,283 @@
|
||||
#include "etherboot.h"
|
||||
#include "disk.h"
|
||||
|
||||
#undef disk_disable
|
||||
|
||||
static int dummy(void *unused __unused)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
static unsigned char disk_buffer[DISK_BUFFER_SIZE];
|
||||
struct disk disk =
|
||||
{
|
||||
{
|
||||
0, /* dev.disable */
|
||||
{
|
||||
0,
|
||||
0,
|
||||
PCI_BUS_TYPE,
|
||||
}, /* dev.devid */
|
||||
0, /* index */
|
||||
0, /* type */
|
||||
PROBE_FIRST, /* how_probe */
|
||||
PROBE_NONE, /* to_probe */
|
||||
0, /* failsafe */
|
||||
0, /* type_index */
|
||||
{}, /* state */
|
||||
},
|
||||
(int (*)(struct disk *, sector_t ))dummy, /* read */
|
||||
0 - 1, /* drive */
|
||||
0, /* hw_sector_size */
|
||||
0, /* sectors_per_read */
|
||||
0, /* bytes */
|
||||
0, /* sectors */
|
||||
0, /* sector */
|
||||
disk_buffer, /* buffer */
|
||||
0, /* priv */
|
||||
|
||||
0, /* disk_offset */
|
||||
0, /* direction */
|
||||
};
|
||||
|
||||
|
||||
static int disk_read(
|
||||
struct disk *disk, unsigned char *buffer, sector_t sector)
|
||||
{
|
||||
int result;
|
||||
sector_t base_sector;
|
||||
|
||||
/* Note: I do not handle disk wrap around here! */
|
||||
|
||||
/* Compute the start of the track cache */
|
||||
base_sector = sector;
|
||||
/* Support sectors_per_read > 1 only on small disks */
|
||||
if ((sizeof(sector_t) > sizeof(unsigned long)) &&
|
||||
(disk->sectors_per_read > 1)) {
|
||||
unsigned long offset;
|
||||
offset = ((unsigned long)sector) % disk->sectors_per_read;
|
||||
base_sector -= offset;
|
||||
}
|
||||
|
||||
/* See if I need to update the track cache */
|
||||
if ((sector < disk->sector) ||
|
||||
sector >= disk->sector + (disk->bytes >> 9)) {
|
||||
twiddle();
|
||||
result = disk->read(disk, base_sector);
|
||||
if (result < 0)
|
||||
return result;
|
||||
}
|
||||
/* Service the request from the track cache */
|
||||
memcpy(buffer, disk->buffer + ((sector - base_sector)<<9), SECTOR_SIZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int disk_read_sectors(
|
||||
struct disk *disk,
|
||||
unsigned char *buffer,
|
||||
sector_t base_sector, unsigned int sectors)
|
||||
{
|
||||
sector_t sector = 0;
|
||||
unsigned long offset;
|
||||
int result = 0;
|
||||
|
||||
for(offset = 0; offset < sectors; offset++) {
|
||||
sector = base_sector + offset;
|
||||
if (sector >= disk->sectors) {
|
||||
sector -= disk->sectors;
|
||||
}
|
||||
result = disk_read(disk, buffer + (offset << 9), sector);
|
||||
if (result < 0)
|
||||
break;
|
||||
}
|
||||
if (result < 0) {
|
||||
printf("disk read error at 0x%lx\n", sector);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static os_download_t probe_buffer(unsigned char *buffer, unsigned int len,
|
||||
int increment, unsigned int offset, unsigned int *roffset)
|
||||
{
|
||||
os_download_t os_download;
|
||||
unsigned int end;
|
||||
end = 0;
|
||||
os_download = 0;
|
||||
if (increment > 0) {
|
||||
end = len - SECTOR_SIZE;
|
||||
}
|
||||
do {
|
||||
offset += increment;
|
||||
os_download = probe_image(buffer + offset, len - offset);
|
||||
} while(!os_download && (offset != end));
|
||||
*roffset = offset;
|
||||
return os_download;
|
||||
}
|
||||
|
||||
static int load_image(
|
||||
struct disk *disk,
|
||||
unsigned char *buffer, unsigned int buf_sectors,
|
||||
sector_t block, unsigned int offset,
|
||||
os_download_t os_download)
|
||||
{
|
||||
sector_t skip_sectors;
|
||||
|
||||
skip_sectors = 0;
|
||||
while(1) {
|
||||
skip_sectors = os_download(buffer + offset,
|
||||
(buf_sectors << 9) - offset, 0);
|
||||
|
||||
block += skip_sectors + buf_sectors;
|
||||
if (block >= disk->sectors) {
|
||||
block -= disk->sectors;
|
||||
}
|
||||
|
||||
offset = 0;
|
||||
buf_sectors = 1;
|
||||
if (disk_read_sectors(disk, buffer, block, 1) < 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int disk_probe(struct dev *dev)
|
||||
{
|
||||
struct disk *disk = (struct disk *)dev;
|
||||
if (dev->how_probe == PROBE_NEXT) {
|
||||
disk->drive += 1;
|
||||
}
|
||||
return probe(dev);
|
||||
}
|
||||
|
||||
|
||||
int disk_load_configuration(struct dev *dev)
|
||||
{
|
||||
/* Start with the very simplest possible disk configuration */
|
||||
struct disk *disk = (struct disk *)dev;
|
||||
disk->direction = (dev->failsafe)?-1:1;
|
||||
disk->disk_offset = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int disk_load(struct dev *dev)
|
||||
{
|
||||
struct disk *disk = (struct disk *)dev;
|
||||
/* 16K == 8K in either direction from the start of the disk */
|
||||
static unsigned char buffer[32*SECTOR_SIZE];
|
||||
os_download_t os_download;
|
||||
unsigned int offset;
|
||||
unsigned int len;
|
||||
unsigned int buf_sectors;
|
||||
volatile sector_t block;
|
||||
volatile int inc, increment;
|
||||
int i;
|
||||
int result;
|
||||
jmp_buf real_restart;
|
||||
|
||||
|
||||
printf("Searching for image...\n");
|
||||
result = 0;
|
||||
/* Only check for 16byte aligned images */
|
||||
increment = (disk->direction < 0)?-16:16;
|
||||
/* Load a buffer, and see if it contains the start of an image
|
||||
* we can boot from disk.
|
||||
*/
|
||||
len = sizeof(buffer);
|
||||
buf_sectors = sizeof(buffer) / SECTOR_SIZE;
|
||||
inc = increment;
|
||||
block = (disk->disk_offset) >> 9;
|
||||
if (buf_sectors/2 > block) {
|
||||
block = (disk->sectors - (buf_sectors/2)) + block;
|
||||
}
|
||||
/* let probe buffer assume offset always needs to be incremented */
|
||||
offset = (len/2 + ((disk->disk_offset) & 0x1ff)) - inc;
|
||||
|
||||
/* Catch longjmp so if this image fails to load, I start looking
|
||||
* for the next image where I left off looking for this image.
|
||||
*/
|
||||
memcpy(&real_restart, &restart_etherboot, sizeof(jmp_buf));
|
||||
i = setjmp(restart_etherboot);
|
||||
if ((i != 0) && (i != -2)) {
|
||||
memcpy(&restart_etherboot, &real_restart, sizeof(jmp_buf));
|
||||
longjmp(restart_etherboot, i);
|
||||
}
|
||||
/* Read the canidate sectors into the buffer */
|
||||
if (disk_read_sectors(disk, buffer, block, buf_sectors) < 0) {
|
||||
result = -1;
|
||||
goto out;
|
||||
}
|
||||
if (inc == increment) {
|
||||
os_download = probe_buffer(buffer, len, inc, offset, &offset);
|
||||
if (os_download)
|
||||
goto load_image;
|
||||
inc = -inc;
|
||||
}
|
||||
os_download = probe_buffer(buffer, len, inc, offset, &offset);
|
||||
if (!os_download) {
|
||||
result = -1;
|
||||
goto out;
|
||||
}
|
||||
load_image:
|
||||
printf("Loading image...\n");
|
||||
result = load_image(disk, buffer, buf_sectors, block, offset, os_download);
|
||||
out:
|
||||
memcpy(&restart_etherboot, &real_restart, sizeof(jmp_buf));
|
||||
return result;
|
||||
}
|
||||
|
||||
int url_file(const char *name,
|
||||
int (*fnc)(unsigned char *, unsigned int, unsigned int, int) __unused)
|
||||
{
|
||||
unsigned int drive;
|
||||
unsigned long disk_offset;
|
||||
int direction;
|
||||
int type;
|
||||
|
||||
disk_offset = 0;
|
||||
direction = 1;
|
||||
if (memcmp(name, "disk", 4) == 0) {
|
||||
type = DISK_DRIVER;
|
||||
name += 4;
|
||||
}
|
||||
else if (memcmp(name, "floppy", 6) == 0) {
|
||||
type = FLOPPY_DRIVER;
|
||||
name += 6;
|
||||
}
|
||||
else {
|
||||
printf("Unknown device type\n");
|
||||
return 0;
|
||||
}
|
||||
drive = strtoul(name, &name, 10);
|
||||
if ((name[0] == '+') || (name[0] == '-')) {
|
||||
direction = (name[0] == '-')? -1 : 1;
|
||||
name++;
|
||||
disk_offset = strtoul(name, &name, 10);
|
||||
}
|
||||
if (name[0]) {
|
||||
printf("Junk '%s' at end of disk url\n", name);
|
||||
return 0;
|
||||
}
|
||||
memset(&disk, 0, sizeof(disk));
|
||||
disk.buffer = disk_buffer;
|
||||
disk.drive = 0;
|
||||
disk.dev.how_probe = PROBE_FIRST;
|
||||
disk.dev.type = type;
|
||||
do {
|
||||
disk_disable();
|
||||
disk.dev.how_probe = disk_probe(&disk.dev);
|
||||
if (disk.dev.how_probe == PROBE_FAILED) {
|
||||
printf("Not that many drives\n");
|
||||
return 0;
|
||||
}
|
||||
} while(disk.drive < drive);
|
||||
disk.direction = direction;
|
||||
disk.disk_offset = disk_offset;
|
||||
|
||||
return disk_load(&disk.dev);
|
||||
}
|
||||
|
||||
void disk_disable(void)
|
||||
{
|
||||
disable(&disk.dev);
|
||||
}
|
||||
419
src/core/dns_resolver.c
Normal file
419
src/core/dns_resolver.c
Normal file
@@ -0,0 +1,419 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* dns_resolver.c: Etherboot support for resolution of host/domain
|
||||
* names in filename parameters
|
||||
* Written 2004 by Anselm M. Hoffmeister
|
||||
* <stockholm@users.sourceforge.net>
|
||||
*
|
||||
* 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 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* This code is using nuts and bolts from throughout etherboot.
|
||||
* It is a fresh implementation according to the DNS RFC, #1035
|
||||
*
|
||||
* $Revision$
|
||||
* $Author$
|
||||
* $Date$
|
||||
*
|
||||
* REVISION HISTORY:
|
||||
* ================
|
||||
* 2004-05-10 File created
|
||||
* 2004-05-19 First release to CVS
|
||||
* 2004-05-22 CNAME support first stage finished
|
||||
* 2004-05-24 First "stable" release to CVS
|
||||
* 2004-08-28 Improve readability, set recursion flag
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef DNS_RESOLVER
|
||||
#include "etherboot.h"
|
||||
#include "nic.h"
|
||||
#include "dns_resolver.h"
|
||||
|
||||
#define MAX_DNS_RETRIES 3
|
||||
#define MAX_CNAME_RECURSION 0x30
|
||||
#undef DNSDEBUG
|
||||
|
||||
int donameresolution ( char * hostname, int hnlength, char * deststring );
|
||||
|
||||
/*
|
||||
* dns_resolver
|
||||
* Function: Main function for name resolution - will be called by other
|
||||
* parts of etherboot
|
||||
* Param: string filename (not containing proto prefix like "tftp://")
|
||||
* Return: string filename, with hostname replaced by IP in dotted quad
|
||||
* or NULL for resolver error
|
||||
* In case no substitution is necessary, return will be = param
|
||||
* The returned string, if not == input, will be temporary and
|
||||
* probably be overwritten during the next call to dns_resolver
|
||||
* The returned string may (or may not) only contain an IP
|
||||
* address, or an IP followed by a ":" or "/", or be NULL(=error)
|
||||
*/
|
||||
char * dns_resolver ( char * filename ) {
|
||||
int i = 0, j, k;
|
||||
static char ipaddr[16] = { 0 };
|
||||
// Search for "end of hostname" (which might be either ":" or "/")
|
||||
for ( j = i; (filename[j] != ':') && (filename[j] != '/'); ++j ) {
|
||||
// If no hostname delimiter was found, assume no name present
|
||||
if ( filename[j] == 0 ) return filename;
|
||||
}
|
||||
// Check if the filename is an IP, in which case, leave unchanged
|
||||
k = j - i - 1;
|
||||
while ( ( '.' == filename[i+k] ) ||
|
||||
( ( '0' <= filename[i+k] ) && ( '9' >= filename[i+k] ) ) ) {
|
||||
--k;
|
||||
if ( k < 0 ) return filename; // Only had nums and dots->IP
|
||||
}
|
||||
// Now that we know it's a full hostname, attempt to resolve
|
||||
if ( donameresolution ( filename + i, j - i, ipaddr ) ) {
|
||||
return NULL; // Error in resolving - Fatal.
|
||||
}
|
||||
// Return the dotted-quad IP which resulted
|
||||
return ipaddr;
|
||||
}
|
||||
|
||||
/*
|
||||
* await_dns
|
||||
* Shall be called on any incoming packet during the resolution process
|
||||
* (as is the case with all the other await_ functions in etherboot)
|
||||
* Param: as any await functions
|
||||
* Return: see dns_resolver.h for constant return values + descriptions
|
||||
*/
|
||||
static int await_dns (int ival, void *ptr,
|
||||
unsigned short ptype __unused, struct iphdr *ip __unused,
|
||||
struct udphdr *udp, struct tcphdr *tcp __unused) {
|
||||
int i, j, k;
|
||||
unsigned char *p = (unsigned char *)udp + sizeof(struct udphdr);
|
||||
// p is set to the beginning of the payload
|
||||
unsigned char *q;
|
||||
unsigned int querytype = QUERYTYPE_A;
|
||||
if ( 0 == udp ) // Parser couldn't find UDP header
|
||||
return RET_PACK_GARBAG; // Not a UDP packet
|
||||
if (( UDP_PORT_DNS != ntohs (udp->src )) ||
|
||||
( UDP_PORT_DNS != ntohs (udp->dest)) )
|
||||
// Neither source nor destination port is "53"
|
||||
return RET_PACK_GARBAG; // UDP port wrong
|
||||
if (( p[QINDEX_ID ] != 0) ||
|
||||
( p[QINDEX_ID+1] != (ival & 0xff)))
|
||||
// Checking if this packet has set (inside payload)
|
||||
// the sequence identifier that we expect
|
||||
return RET_PACK_GARBAG; // Not matching our request ID
|
||||
if (( p[QINDEX_FLAGS ] & QUERYFLAGS_MASK ) != QUERYFLAGS_WANT )
|
||||
// We only accept responses to the query(ies) we sent
|
||||
return RET_PACK_GARBAG; // Is not response=opcode <0>
|
||||
querytype = (ival & 0xff00) >> 8;
|
||||
if (((p[QINDEX_NUMANSW+1] + (p[QINDEX_NUMANSW+1]<<8)) == 0 ) ||
|
||||
( ERR_NOSUCHNAME == (p[QINDEX_FLAGS+1] & 0x0f) ) ) {
|
||||
// Answer section has 0 entries, or "no such name" returned
|
||||
if ( QUERYTYPE_A == querytype) {
|
||||
// It was an A type query, so we should try if there's
|
||||
// an alternative "CNAME" record available
|
||||
return RET_RUN_CNAME_Q; // So try CNAME query next
|
||||
} else if ( QUERYTYPE_CNAME == querytype) {
|
||||
// There's no CNAME either, so give up
|
||||
return RET_NOSUCHNAME;
|
||||
} else {
|
||||
// Anything else? No idea what. Should not happen.
|
||||
return RET_NOSUCHNAME; // Bail out with error
|
||||
}
|
||||
}
|
||||
if ( 0 != ( p[QINDEX_FLAGS+1] & 0x0f ) )
|
||||
// The response packet's flag section tells us:
|
||||
return RET_NOSUCHNAME; // Another (unspecific) error occured
|
||||
// Now we have an answer packet in response to our query. Next thing
|
||||
// to do is to search the payload for the "answer section", as there is
|
||||
// a question section first usually that needs to be skipped
|
||||
// If question section was not repeated, that saves a lot of work :
|
||||
if ( 0 >= (i = ((p[QINDEX_NUMQUEST] << 8) + p[QINDEX_NUMQUEST+1]))) {
|
||||
q = p+ QINDEX_QUESTION; // No question section, just continue;
|
||||
} else if ( i >= 2 ) { // More than one query section? Error!
|
||||
return RET_NOSUCHNAME; // That's invalid for us anyway -
|
||||
// We only place one query at a time
|
||||
} else {
|
||||
// We have to skip through the question section first to
|
||||
// find the beginning of the answer section
|
||||
q = p + QINDEX_QUESTION;
|
||||
while ( 0 != q[0] )
|
||||
q += q[0] + 1; // Skip through
|
||||
q += 5; // Skip over end-\0 and query type section
|
||||
}
|
||||
// Now our pointer shows the beginning of the answer section
|
||||
// So now move it past the (repeated) query string, we only
|
||||
// want the answer
|
||||
while ( 0 != q[0] ) {
|
||||
if ( 0xc0 == ( q[0] & 0xc0 ) ) { // Pointer
|
||||
++q;
|
||||
break;
|
||||
}
|
||||
q += q[0] + 1;
|
||||
}
|
||||
++q;
|
||||
// Now check wether it's an INET host address (resp. CNAME)?
|
||||
// There seem to be nameservers out there (Bind 9, for example),
|
||||
// that return CNAMEs when no As are there, e.g. in
|
||||
// testname.test. IN CNAME othername.test.
|
||||
// case, bind 9 would return the CNAME record on an A query.
|
||||
// Accept this case as it saves a lot of work (an extra query)
|
||||
if (( QUERYTYPE_CNAME == q[1] ) &&
|
||||
( QUERYTYPE_A == querytype )) {
|
||||
// A query, but CNAME response.
|
||||
// Do simulate having done a CNAME query now and just assume
|
||||
// the answer we are working on here is that of a CNAME query
|
||||
// This works for single-depth CNAMEs fine.
|
||||
// For deeper recursion, we won't parse the answer
|
||||
// packet deeper but rely on a separate CNAME query
|
||||
querytype = QUERYTYPE_CNAME;
|
||||
}
|
||||
// Now check wether the answer packet is of the expected type
|
||||
// (remember, we just tweaked CNAME answers to A queries already)
|
||||
if ( (q[0] != 0) ||
|
||||
(q[1] != querytype) || // query type matches?
|
||||
(q[2] != ((QUERYCLASS_INET & 0xff00) >> 8)) ||
|
||||
(q[3] != (QUERYCLASS_INET & 0x00ff))) { // class IN response?
|
||||
return RET_DNSERROR; // Should not happen. DNS server bad?
|
||||
}
|
||||
q += 8; // skip querytype/-class/ttl which are uninteresting now
|
||||
if ( querytype == QUERYTYPE_A ) {
|
||||
// So what we sent was an A query - expect an IPv4 address
|
||||
// Check if datalength looks satisfactory
|
||||
if ( ( 0 != q[0] ) || ( 4 != q[1] ) ) {
|
||||
// Data length is not 4 bytes
|
||||
return RET_DNSERROR;
|
||||
}
|
||||
// Go to the IP address and copy it to the response buffer
|
||||
p = ptr + QINDEX_STORE_A;
|
||||
for ( i = 0; i < 4; ++i ) {
|
||||
p[i] = q[i+2];
|
||||
}
|
||||
return RET_GOT_ADDR; // OK! Address RETURNED! VERY FINE!
|
||||
} else if ( querytype == QUERYTYPE_CNAME ) { // CNAME QUERY
|
||||
// The pointer "q" now stands on the "payload length" of the
|
||||
// CNAME data [2 byte field]. We will have to check wether this
|
||||
// name is referenced in a following response, which would save
|
||||
// us further queries, or if it is standalone, which calls for
|
||||
// making a separate query
|
||||
// This statement above probably needs clarification. To help
|
||||
// the reader understand what's going on, imagine the DNS
|
||||
// answer to be 4-section: query(repeating what we sent to the
|
||||
// DNS server), answer(like the CNAME record we wanted),
|
||||
// additional (which might hold the A for the CNAME - esp.
|
||||
// Bind9 seems to provide us with this info when we didn't
|
||||
// query for it yet) and authoritative (the DNS server's info
|
||||
// on who is responsible for that data). For compression's
|
||||
// sake, instead of specifying the full hostname string all
|
||||
// the time, one can instead specify something like "same as on
|
||||
// payload offset 0x123" - if this happens here, we can check
|
||||
// if that payload offset given here by coincidence is that of
|
||||
// an additional "A" record which saves us sending a separate
|
||||
// query.
|
||||
// We will look if there's a/ two or more answer sections
|
||||
// AND b/ the next is a reference to us.
|
||||
p = (unsigned char *)udp + sizeof(struct udphdr);
|
||||
i = q + 2 - p;
|
||||
if ( p[7] > 1 ) { // More than one answer section
|
||||
// Check the next if it's a ref
|
||||
// For that, we have to locate the next. Do this with "q".
|
||||
p = q + (q[0] * 0x100) + q[1] + 2;
|
||||
if ( (p[0] == (0xc0 + ((i & 0xf00)/256))) &&
|
||||
(p[1] == (i & 0xff) ) &&
|
||||
(p[2] == ((QUERYTYPE_A & 0xff00) >> 8)) &&
|
||||
(p[3] == (QUERYTYPE_A & 0x00ff)) &&
|
||||
(p[4] == ((QUERYCLASS_INET & 0xff00) >> 8 )) &&
|
||||
(p[5] == (QUERYCLASS_INET & 0x00ff)) &&
|
||||
(p[10]== 0) && // Data length expected
|
||||
(p[11]== 4)) { // to be <4> (IP-addr)
|
||||
// Behind that sections: TTL, data length(4)
|
||||
for ( i = 0; i < 4; ++i ) {
|
||||
((unsigned char*)ptr)[QINDEX_STORE_A+i] =
|
||||
p[12+i];
|
||||
}
|
||||
return RET_GOT_ADDR;
|
||||
}
|
||||
}
|
||||
// Reference not found, next query (A) needed (because CNAME
|
||||
// queries usually return another hostname, not an IP address
|
||||
// as we need it - that's the point in CNAME, anyway :-)
|
||||
p = (unsigned char *)udp + sizeof(struct udphdr);
|
||||
#ifdef DNSDEBUG
|
||||
printf ( " ->[");
|
||||
#endif
|
||||
k = QINDEX_QUESTION;
|
||||
i = (q-p) + 2;
|
||||
// Compose the hostname that needs to be queried for
|
||||
// This looks complicated (and is a little bit) because
|
||||
// we might not be able to copy verbatim, but need to
|
||||
// check wether the CNAME looks like
|
||||
// "servername" "plus what offset 0x123 of the payload says"
|
||||
// (this saves transfer bandwidth, which is why DNS allows
|
||||
// this, but it makes programmers' lives more difficult)
|
||||
while (p[i] != 0) {
|
||||
if ( (((unsigned char *)p)[i] & 0xc0) != 0 ) {
|
||||
i = ((p[i] & 0x3f) * 0x100) + p[i+1];
|
||||
continue;
|
||||
}
|
||||
((unsigned char *)ptr)[k] = p[i];
|
||||
for ( j = i + 1; j <= i + p[i]; ++j ) {
|
||||
((unsigned char *)ptr)[k+j-i] = p[j];
|
||||
#ifdef DNSDEBUG
|
||||
printf ( "%c", p[j] );
|
||||
#endif
|
||||
}
|
||||
k += ((unsigned char *)ptr)[k] + 1;
|
||||
i += p[i] + 1;
|
||||
#ifdef DNSDEBUG
|
||||
printf ( (p[i] ? ".": "") );
|
||||
#endif
|
||||
}
|
||||
((unsigned char *)ptr)[k] = 0;
|
||||
#ifdef DNSDEBUG
|
||||
printf ( "].." );
|
||||
#endif
|
||||
// So we need to run another query, this time try to
|
||||
// get an "A" record for the hostname that the last CNAME
|
||||
// query pointed to
|
||||
return RET_RUN_NEXT_A;
|
||||
}
|
||||
// We only accept CNAME or A replies from the nameserver, every-
|
||||
// thing else is of no use for us.
|
||||
// Must be an invalid packet that the nameserver sent.
|
||||
return RET_DNSERROR;
|
||||
}
|
||||
|
||||
int chars_to_next_dot ( char * countfrom, int maxnum ) {
|
||||
// Count the number of characters of this part of a hostname
|
||||
int i;
|
||||
for ( i = 1; i < maxnum; ++i ) {
|
||||
if ( countfrom[i] == '.' ) return i;
|
||||
}
|
||||
return maxnum;
|
||||
}
|
||||
|
||||
/*
|
||||
* donameresolution
|
||||
* Function: Compose the initial query packet, handle answers until
|
||||
* a/ an IP address is retrieved
|
||||
* b/ too many CNAME references occured (max. MAX_DNS_RETRIES)
|
||||
* c/ No matching record for A or CNAME can be found
|
||||
* Param: string hostname, length (hostname needs no \0-end-marker),
|
||||
* string to which dotted-quad-IP shall be written
|
||||
* Return: 0 for success, >0 for failure
|
||||
*/
|
||||
int donameresolution ( char * hostname, int hnlength, char * deststring ) {
|
||||
unsigned char querybuf[260+sizeof(struct iphdr)+sizeof(struct udphdr)];
|
||||
// 256 for the DNS query payload, +4 for the result temporary
|
||||
unsigned char *query = &querybuf[sizeof(struct iphdr)+sizeof(struct udphdr)];
|
||||
// Pointer to the payload
|
||||
int i, h = hnlength;
|
||||
long timeout;
|
||||
int retry, recursion;
|
||||
// Setup the query data
|
||||
query[QINDEX_ID ] = (QUERYIDENTIFIER & 0xff00) >> 8;
|
||||
query[QINDEX_ID+1] = QUERYIDENTIFIER & 0xff;
|
||||
query[QINDEX_FLAGS ] = (QUERYFLAGS & 0xff00) >> 8;
|
||||
query[QINDEX_FLAGS+1] = QUERYFLAGS & 0xff;
|
||||
query[QINDEX_NUMQUEST ]= 0;
|
||||
query[QINDEX_NUMQUEST+1]= 1; // 1 standard query to be sent
|
||||
query[QINDEX_NUMANSW ] = 0;
|
||||
query[QINDEX_NUMANSW+1] = 0;
|
||||
query[QINDEX_NUMAUTH ] = 0;
|
||||
query[QINDEX_NUMAUTH+1] = 0;
|
||||
query[QINDEX_NUMADDIT ]= 0;
|
||||
query[QINDEX_NUMADDIT+1]= 0;
|
||||
query[QINDEX_QUESTION] = chars_to_next_dot(hostname,h);
|
||||
if ( h > 236 ) return 1; // Hostnames longer than 236 chars are refused.
|
||||
// This is an arbitrary decision, SHOULD check
|
||||
// what the RFCs say about that
|
||||
for ( i = 0; i < h; ++i ) {
|
||||
// Compose the query section's hostname - replacing dots (and
|
||||
// preceding the string) with one-byte substring-length values
|
||||
// (for the immediately following substring) - \0 terminated
|
||||
query[QINDEX_QUESTION+i+1] = hostname[i];
|
||||
if ( hostname[i] == '.' )
|
||||
query[QINDEX_QUESTION+i+1] = chars_to_next_dot(hostname + i + 1, h - i - 1);
|
||||
}
|
||||
query[QINDEX_QTYPE+h ] = (QUERYTYPE_A & 0xff00) >> 8;
|
||||
query[QINDEX_QTYPE+h+1] = QUERYTYPE_A & 0xff;
|
||||
// First try an A query, if that
|
||||
// won't work, try CNAME
|
||||
printf ( "Resolving hostname [" );
|
||||
for ( i = 0; i < hnlength; ++i ) { printf ( "%c", hostname[i] ); }
|
||||
printf ("]" );
|
||||
for ( recursion = MAX_CNAME_RECURSION; recursion > 0; --recursion ) {
|
||||
printf ( ".." );
|
||||
// Try MAX_CNAME_RECURSION CNAME queries maximally, if then no
|
||||
// A record is found, assume the hostname to not be resolvable
|
||||
query[QINDEX_QUESTION+h+1] = 0; // Marks the end of
|
||||
// the query string
|
||||
query[QINDEX_QCLASS+h ]= (QUERYCLASS_INET & 0xff00) >> 8;
|
||||
query[QINDEX_QCLASS+h+1]= QUERYCLASS_INET & 0xff;
|
||||
rx_qdrain(); // Clear NIC packet buffer -
|
||||
// there won't be anything of interest *now*.
|
||||
udp_transmit ( arptable[ARP_NAMESERVER].ipaddr.s_addr,
|
||||
UDP_PORT_DNS, UDP_PORT_DNS,
|
||||
hnlength + 18 + sizeof(struct iphdr) +
|
||||
sizeof(struct udphdr), querybuf );
|
||||
// If no answer comes in in a certain period of time, retry
|
||||
for (retry = 1; retry <= MAX_DNS_RETRIES; retry++) {
|
||||
timeout = rfc2131_sleep_interval(TIMEOUT, retry);
|
||||
i = await_reply ( await_dns,
|
||||
(query[QINDEX_QTYPE+h+1] << 8) +
|
||||
((unsigned char *)query)[QINDEX_ID+1],
|
||||
query, timeout);
|
||||
// The parameters given are
|
||||
// bits 15...8 of integer = Query type (low bits)
|
||||
// bits 7...0 of integer = Query index (low bits)
|
||||
// query + QINDEX_STORE_A points to the place
|
||||
// where A records shall be stored (4 bytes);
|
||||
// query + 12(QINDEX_QUESTION) points to where
|
||||
// a CNAME should go in case one is received
|
||||
if (i) break;
|
||||
}
|
||||
switch ( i ) {
|
||||
case RET_GOT_ADDR: // Address successfully retrieved
|
||||
sprintf( deststring, "%@",
|
||||
(long)query[QINDEX_STORE_A ] +
|
||||
( (long)query[QINDEX_STORE_A+1] * 256 ) +
|
||||
( (long)query[QINDEX_STORE_A+2] * 65536 )+
|
||||
( (long)query[QINDEX_STORE_A+3] * 16777216 ));
|
||||
printf ( " -> IP [%s]\n", deststring );
|
||||
return RET_DNS_OK;
|
||||
case RET_RUN_CNAME_Q: // No A record found, try CNAME
|
||||
query[QINDEX_QTYPE+h ]=(QUERYTYPE_CNAME & 0xff00)>> 8;
|
||||
query[QINDEX_QTYPE+h+1]= QUERYTYPE_CNAME & 0xff;
|
||||
break;
|
||||
case RET_RUN_NEXT_A:
|
||||
// Found a CNAME, now try A for the name it pointed to
|
||||
for ( i = 0; query[QINDEX_QUESTION+i] != 0;
|
||||
i += query[QINDEX_QUESTION+i] + 1 ) {;}
|
||||
h = i - 1;
|
||||
query[QINDEX_QTYPE+h ]=(QUERYTYPE_A & 0xff00)>> 8;
|
||||
query[QINDEX_QTYPE+h+1]= QUERYTYPE_A & 0xff;
|
||||
break;
|
||||
case RET_CNAME_FAIL:
|
||||
// Neither A nor CNAME gave a usable result
|
||||
printf ("Host name cannot be resolved\n");
|
||||
return RET_DNS_FAIL;
|
||||
case RET_NOSUCHNAME:
|
||||
default:
|
||||
printf ( "Name resolution failed\n" );
|
||||
return RET_DNS_FAIL;
|
||||
}
|
||||
query[QINDEX_ID ] = 0; // We will probably never have more
|
||||
// than 256 queries in one run
|
||||
query[QINDEX_ID+1]++;
|
||||
}
|
||||
// To deep recursion
|
||||
printf ( "CNAME recursion to deep - abort name resolver\n" );
|
||||
return RET_DNS_FAIL;
|
||||
}
|
||||
#endif /* DNS_RESOLVER */
|
||||
658
src/core/elf_loader.c
Normal file
658
src/core/elf_loader.c
Normal file
@@ -0,0 +1,658 @@
|
||||
#include "elf.h"
|
||||
|
||||
#ifndef ELF_CHECK_ARCH
|
||||
#error ELF_CHECK_ARCH not defined
|
||||
#endif
|
||||
|
||||
#define ELF_NOTES 1
|
||||
#define ELF_DEBUG 0
|
||||
|
||||
struct elf_state
|
||||
{
|
||||
union {
|
||||
Elf32_Ehdr elf32;
|
||||
Elf64_Ehdr elf64;
|
||||
} e;
|
||||
union {
|
||||
Elf32_Phdr phdr32[1];
|
||||
Elf64_Phdr phdr64[1];
|
||||
unsigned char dummy[1024];
|
||||
} p;
|
||||
unsigned long curaddr;
|
||||
int segment; /* current segment number, -1 for none */
|
||||
uint64_t loc; /* start offset of current block */
|
||||
uint64_t skip; /* padding to be skipped to current segment */
|
||||
unsigned long toread; /* remaining data to be read in the segment */
|
||||
#if ELF_NOTES
|
||||
int check_ip_checksum;
|
||||
uint16_t ip_checksum;
|
||||
unsigned long ip_checksum_offset;
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct elf_state estate;
|
||||
|
||||
static void elf_boot(unsigned long machine, unsigned long entry)
|
||||
{
|
||||
int result;
|
||||
struct Elf_Bhdr *hdr;
|
||||
multiboot_boot(entry);
|
||||
/* We cleanup unconditionally, and then reawaken the network
|
||||
* adapter after the longjmp.
|
||||
*/
|
||||
hdr = prepare_boot_params(&estate.e);
|
||||
result = elf_start(machine, entry, virt_to_phys(hdr));
|
||||
if (result == 0) {
|
||||
result = -1;
|
||||
}
|
||||
printf("Secondary program returned %d\n", result);
|
||||
longjmp(restart_etherboot, result);
|
||||
}
|
||||
|
||||
#if ELF_NOTES
|
||||
static int elf_prep_segment(
|
||||
unsigned long start __unused, unsigned long mid __unused, unsigned long end __unused,
|
||||
unsigned long istart, unsigned long iend)
|
||||
|
||||
{
|
||||
if (estate.check_ip_checksum) {
|
||||
if ((istart <= estate.ip_checksum_offset) &&
|
||||
(iend > estate.ip_checksum_offset)) {
|
||||
/* The checksum note is also loaded in a
|
||||
* PT_LOAD segment, so the computed checksum
|
||||
* should be 0.
|
||||
*/
|
||||
estate.ip_checksum = 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
#define elf_prep_segment(start, mid, end, istart, iend) (1)
|
||||
#endif
|
||||
|
||||
|
||||
#if ELF_NOTES
|
||||
static void process_elf_notes(unsigned char *header,
|
||||
unsigned long offset, unsigned long length)
|
||||
{
|
||||
unsigned char *note, *end;
|
||||
char *program, *version;
|
||||
|
||||
estate.check_ip_checksum = 0;
|
||||
note = header + offset;
|
||||
end = note + length;
|
||||
program = version = 0;
|
||||
while(note < end) {
|
||||
Elf_Nhdr *hdr;
|
||||
unsigned char *n_name, *n_desc, *next;
|
||||
hdr = (Elf_Nhdr *)note;
|
||||
n_name = note + sizeof(*hdr);
|
||||
n_desc = n_name + ((hdr->n_namesz + 3) & ~3);
|
||||
next = n_desc + ((hdr->n_descsz + 3) & ~3);
|
||||
if (next > end) {
|
||||
break;
|
||||
}
|
||||
if ((hdr->n_namesz == sizeof(ELF_NOTE_BOOT)) &&
|
||||
(memcmp(n_name, ELF_NOTE_BOOT, sizeof(ELF_NOTE_BOOT)) == 0)) {
|
||||
switch(hdr->n_type) {
|
||||
case EIN_PROGRAM_NAME:
|
||||
if (n_desc[hdr->n_descsz -1] == 0) {
|
||||
program = n_desc;
|
||||
}
|
||||
break;
|
||||
case EIN_PROGRAM_VERSION:
|
||||
if (n_desc[hdr->n_descsz -1] == 0) {
|
||||
version = n_desc;
|
||||
}
|
||||
break;
|
||||
case EIN_PROGRAM_CHECKSUM:
|
||||
estate.check_ip_checksum = 1;
|
||||
estate.ip_checksum = *((uint16_t *)n_desc);
|
||||
/* Remember where the segment is so
|
||||
* I can detect segment overlaps.
|
||||
*/
|
||||
estate.ip_checksum_offset = n_desc - header;
|
||||
#if ELF_DEBUG
|
||||
printf("Checksum: %hx\n", estate.ip_checksum);
|
||||
#endif
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
#if ELF_DEBUG
|
||||
printf("n_type: %x n_name(%d): %s n_desc(%d): %s\n",
|
||||
hdr->n_type,
|
||||
hdr->n_namesz, n_name,
|
||||
hdr->n_descsz, n_desc);
|
||||
#endif
|
||||
note = next;
|
||||
}
|
||||
if (program && version) {
|
||||
printf("\nLoading %s version: %s\n", program, version);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ELF_IMAGE
|
||||
static sector_t elf32_download(unsigned char *data, unsigned int len, int eof);
|
||||
static inline os_download_t elf32_probe(unsigned char *data, unsigned int len)
|
||||
{
|
||||
unsigned long phdr_size;
|
||||
if (len < sizeof(estate.e.elf32)) {
|
||||
return 0;
|
||||
}
|
||||
memcpy(&estate.e.elf32, data, sizeof(estate.e.elf32));
|
||||
if ((estate.e.elf32.e_ident[EI_MAG0] != ELFMAG0) ||
|
||||
(estate.e.elf32.e_ident[EI_MAG1] != ELFMAG1) ||
|
||||
(estate.e.elf32.e_ident[EI_MAG2] != ELFMAG2) ||
|
||||
(estate.e.elf32.e_ident[EI_MAG3] != ELFMAG3) ||
|
||||
(estate.e.elf32.e_ident[EI_CLASS] != ELFCLASS32) ||
|
||||
(estate.e.elf32.e_ident[EI_DATA] != ELFDATA_CURRENT) ||
|
||||
(estate.e.elf32.e_ident[EI_VERSION] != EV_CURRENT) ||
|
||||
( (estate.e.elf32.e_type != ET_EXEC) &&
|
||||
(estate.e.elf32.e_type != ET_DYN)) ||
|
||||
(estate.e.elf32.e_version != EV_CURRENT) ||
|
||||
(estate.e.elf32.e_ehsize != sizeof(Elf32_Ehdr)) ||
|
||||
(estate.e.elf32.e_phentsize != sizeof(Elf32_Phdr)) ||
|
||||
!ELF_CHECK_ARCH(estate.e.elf32)) {
|
||||
return 0;
|
||||
}
|
||||
printf("(ELF");
|
||||
elf_freebsd_probe();
|
||||
multiboot_probe(data, len);
|
||||
printf(")... ");
|
||||
phdr_size = estate.e.elf32.e_phnum * estate.e.elf32.e_phentsize;
|
||||
if (estate.e.elf32.e_phoff + phdr_size > len) {
|
||||
printf("ELF header outside first block\n");
|
||||
return dead_download;
|
||||
}
|
||||
if (phdr_size > sizeof(estate.p.dummy)) {
|
||||
printf("Program header to big\n");
|
||||
return dead_download;
|
||||
}
|
||||
memcpy(&estate.p.phdr32, data + estate.e.elf32.e_phoff, phdr_size);
|
||||
if (estate.e.elf32.e_type == ET_DYN) {
|
||||
Elf32_Addr min, max, base_addr, delta, align;
|
||||
min = -1;
|
||||
max = 0;
|
||||
align = 1;
|
||||
for(estate.segment = 0; estate.segment < estate.e.elf32.e_phnum; estate.segment++) {
|
||||
Elf32_Addr val;
|
||||
if (estate.p.phdr32[estate.segment].p_type != PT_LOAD)
|
||||
continue;
|
||||
val = estate.p.phdr32[estate.segment].p_paddr;
|
||||
if (val < min) {
|
||||
min = val;
|
||||
}
|
||||
val += estate.p.phdr32[estate.segment].p_memsz;
|
||||
if (val > max) {
|
||||
max = val;
|
||||
}
|
||||
if (estate.p.phdr32[estate.segment].p_align > align) {
|
||||
align = estate.p.phdr32[estate.segment].p_align;
|
||||
}
|
||||
}
|
||||
if (align & (align -1)) {
|
||||
printf("ELF base address alignment is not a power of 2\n");
|
||||
return dead_download;
|
||||
}
|
||||
base_addr = find_segment(max - min, align);
|
||||
if (base_addr == ULONG_MAX) {
|
||||
printf("ELF base address not available for size %ld\n", max - min);
|
||||
return dead_download;
|
||||
}
|
||||
/* Compute the change in base address and fix up the addresses */
|
||||
delta = base_addr - min;
|
||||
for(estate.segment = 0; estate.segment < estate.e.elf32.e_phnum; estate.segment++) {
|
||||
/* Change the base address of the object to load */
|
||||
estate.p.phdr32[estate.segment].p_paddr += delta;
|
||||
}
|
||||
estate.e.elf32.e_entry += delta;
|
||||
}
|
||||
#if ELF_NOTES
|
||||
/* Load ELF notes from the image */
|
||||
for(estate.segment = 0; estate.segment < estate.e.elf32.e_phnum; estate.segment++) {
|
||||
if (estate.p.phdr32[estate.segment].p_type != PT_NOTE)
|
||||
continue;
|
||||
if (estate.p.phdr32[estate.segment].p_offset + estate.p.phdr32[estate.segment].p_filesz > len) {
|
||||
/* Ignore ELF notes outside of the first block */
|
||||
continue;
|
||||
}
|
||||
process_elf_notes(data,
|
||||
estate.p.phdr32[estate.segment].p_offset, estate.p.phdr32[estate.segment].p_filesz);
|
||||
}
|
||||
#endif
|
||||
/* Check for Etherboot related limitations. Memory
|
||||
* between _text and _end is not allowed.
|
||||
* Reasons: the Etherboot code/data area.
|
||||
*/
|
||||
for (estate.segment = 0; estate.segment < estate.e.elf32.e_phnum; estate.segment++) {
|
||||
unsigned long start, mid, end, istart, iend;
|
||||
if (estate.p.phdr32[estate.segment].p_type != PT_LOAD)
|
||||
continue;
|
||||
|
||||
elf_freebsd_fixup_segment();
|
||||
|
||||
start = estate.p.phdr32[estate.segment].p_paddr;
|
||||
mid = start + estate.p.phdr32[estate.segment].p_filesz;
|
||||
end = start + estate.p.phdr32[estate.segment].p_memsz;
|
||||
istart = estate.p.phdr32[estate.segment].p_offset;
|
||||
iend = istart + estate.p.phdr32[estate.segment].p_filesz;
|
||||
if (!prep_segment(start, mid, end, istart, iend)) {
|
||||
return dead_download;
|
||||
}
|
||||
if (!elf_prep_segment(start, mid, end, istart, iend)) {
|
||||
return dead_download;
|
||||
}
|
||||
}
|
||||
estate.segment = -1;
|
||||
estate.loc = 0;
|
||||
estate.skip = 0;
|
||||
estate.toread = 0;
|
||||
return elf32_download;
|
||||
}
|
||||
|
||||
static sector_t elf32_download(unsigned char *data, unsigned int len, int eof)
|
||||
{
|
||||
unsigned long skip_sectors = 0;
|
||||
unsigned int offset; /* working offset in the current data block */
|
||||
int i;
|
||||
|
||||
offset = 0;
|
||||
do {
|
||||
if (estate.segment != -1) {
|
||||
if (estate.skip) {
|
||||
if (estate.skip >= len - offset) {
|
||||
estate.skip -= len - offset;
|
||||
break;
|
||||
}
|
||||
offset += estate.skip;
|
||||
estate.skip = 0;
|
||||
}
|
||||
|
||||
if (estate.toread) {
|
||||
unsigned int cplen;
|
||||
cplen = len - offset;
|
||||
if (cplen >= estate.toread) {
|
||||
cplen = estate.toread;
|
||||
}
|
||||
memcpy(phys_to_virt(estate.curaddr), data+offset, cplen);
|
||||
estate.curaddr += cplen;
|
||||
estate.toread -= cplen;
|
||||
offset += cplen;
|
||||
if (estate.toread)
|
||||
break;
|
||||
elf_freebsd_find_segment_end();
|
||||
}
|
||||
}
|
||||
|
||||
/* Data left, but current segment finished - look for the next
|
||||
* segment (in file offset order) that needs to be loaded.
|
||||
* We can only seek forward, so select the program headers,
|
||||
* in the correct order.
|
||||
*/
|
||||
estate.segment = -1;
|
||||
for (i = 0; i < estate.e.elf32.e_phnum; i++) {
|
||||
if (estate.p.phdr32[i].p_type != PT_LOAD)
|
||||
continue;
|
||||
if (estate.p.phdr32[i].p_filesz == 0)
|
||||
continue;
|
||||
if (estate.p.phdr32[i].p_offset < estate.loc + offset)
|
||||
continue; /* can't go backwards */
|
||||
if ((estate.segment != -1) &&
|
||||
(estate.p.phdr32[i].p_offset >= estate.p.phdr32[estate.segment].p_offset))
|
||||
continue; /* search minimum file offset */
|
||||
estate.segment = i;
|
||||
}
|
||||
if (estate.segment == -1) {
|
||||
if (elf_freebsd_debug_loader(offset)) {
|
||||
estate.segment = 0; /* -1 makes it not read anymore */
|
||||
continue;
|
||||
}
|
||||
/* No more segments to be loaded, so just start the
|
||||
* kernel. This saves a lot of network bandwidth if
|
||||
* debug info is in the kernel but not loaded. */
|
||||
goto elf_startkernel;
|
||||
break;
|
||||
}
|
||||
estate.curaddr = estate.p.phdr32[estate.segment].p_paddr;
|
||||
estate.skip = estate.p.phdr32[estate.segment].p_offset - (estate.loc + offset);
|
||||
estate.toread = estate.p.phdr32[estate.segment].p_filesz;
|
||||
#if ELF_DEBUG
|
||||
printf("PHDR %d, size %#lX, curaddr %#lX\n",
|
||||
estate.segment, estate.toread, estate.curaddr);
|
||||
#endif
|
||||
} while (offset < len);
|
||||
|
||||
estate.loc += len + (estate.skip & ~0x1ff);
|
||||
skip_sectors = estate.skip >> 9;
|
||||
estate.skip &= 0x1ff;
|
||||
|
||||
if (eof) {
|
||||
unsigned long entry;
|
||||
unsigned long machine;
|
||||
elf_startkernel:
|
||||
entry = estate.e.elf32.e_entry;
|
||||
machine = estate.e.elf32.e_machine;
|
||||
|
||||
#if ELF_NOTES
|
||||
if (estate.check_ip_checksum) {
|
||||
unsigned long bytes = 0;
|
||||
uint16_t sum, new_sum;
|
||||
|
||||
sum = ipchksum(&estate.e.elf32, sizeof(estate.e.elf32));
|
||||
bytes = sizeof(estate.e.elf32);
|
||||
#if ELF_DEBUG
|
||||
printf("Ehdr: %hx %hx sz: %lx bytes: %lx\n",
|
||||
sum, sum, bytes, bytes);
|
||||
#endif
|
||||
|
||||
new_sum = ipchksum(estate.p.phdr32, sizeof(estate.p.phdr32[0]) * estate.e.elf32.e_phnum);
|
||||
sum = add_ipchksums(bytes, sum, new_sum);
|
||||
bytes += sizeof(estate.p.phdr32[0]) * estate.e.elf32.e_phnum;
|
||||
#if ELF_DEBUG
|
||||
printf("Phdr: %hx %hx sz: %lx bytes: %lx\n",
|
||||
new_sum, sum,
|
||||
sizeof(estate.p.phdr32[0]) * estate.e.elf32.e_phnum, bytes);
|
||||
#endif
|
||||
|
||||
for(i = 0; i < estate.e.elf32.e_phnum; i++) {
|
||||
if (estate.p.phdr32[i].p_type != PT_LOAD)
|
||||
continue;
|
||||
new_sum = ipchksum(phys_to_virt(estate.p.phdr32[i].p_paddr),
|
||||
estate.p.phdr32[i].p_memsz);
|
||||
sum = add_ipchksums(bytes, sum, new_sum);
|
||||
bytes += estate.p.phdr32[i].p_memsz;
|
||||
#if ELF_DEBUG
|
||||
printf("seg%d: %hx %hx sz: %x bytes: %lx\n",
|
||||
i, new_sum, sum,
|
||||
estate.p.phdr32[i].p_memsz, bytes);
|
||||
#endif
|
||||
|
||||
}
|
||||
if (estate.ip_checksum != sum) {
|
||||
printf("\nImage checksum: %hx != computed checksum: %hx\n",
|
||||
estate.ip_checksum, sum);
|
||||
longjmp(restart_etherboot, -2);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
done(1);
|
||||
/* Fixup the offset to the program header so you can find the program headers from
|
||||
* the ELF header mknbi needs this.
|
||||
*/
|
||||
estate.e.elf32.e_phoff = (char *)&estate.p - (char *)&estate.e;
|
||||
elf_freebsd_boot(entry);
|
||||
elf_boot(machine,entry);
|
||||
}
|
||||
return skip_sectors;
|
||||
}
|
||||
#endif /* ELF_IMAGE */
|
||||
|
||||
#ifdef ELF64_IMAGE
|
||||
static sector_t elf64_download(unsigned char *data, unsigned int len, int eof);
|
||||
static inline os_download_t elf64_probe(unsigned char *data, unsigned int len)
|
||||
{
|
||||
unsigned long phdr_size;
|
||||
if (len < sizeof(estate.e.elf64)) {
|
||||
return 0;
|
||||
}
|
||||
memcpy(&estate.e.elf64, data, sizeof(estate.e.elf64));
|
||||
if ((estate.e.elf64.e_ident[EI_MAG0] != ELFMAG0) ||
|
||||
(estate.e.elf64.e_ident[EI_MAG1] != ELFMAG1) ||
|
||||
(estate.e.elf64.e_ident[EI_MAG2] != ELFMAG2) ||
|
||||
(estate.e.elf64.e_ident[EI_MAG3] != ELFMAG3) ||
|
||||
(estate.e.elf64.e_ident[EI_CLASS] != ELFCLASS64) ||
|
||||
(estate.e.elf64.e_ident[EI_DATA] != ELFDATA_CURRENT) ||
|
||||
(estate.e.elf64.e_ident[EI_VERSION] != EV_CURRENT) ||
|
||||
( (estate.e.elf64.e_type != ET_EXEC) &&
|
||||
(estate.e.elf64.e_type != ET_DYN)) ||
|
||||
(estate.e.elf64.e_version != EV_CURRENT) ||
|
||||
(estate.e.elf64.e_ehsize != sizeof(Elf64_Ehdr)) ||
|
||||
(estate.e.elf64.e_phentsize != sizeof(Elf64_Phdr)) ||
|
||||
!ELF_CHECK_ARCH(estate.e.elf64)) {
|
||||
return 0;
|
||||
}
|
||||
printf("(ELF64)... ");
|
||||
phdr_size = estate.e.elf64.e_phnum * estate.e.elf64.e_phentsize;
|
||||
if (estate.e.elf64.e_phoff + phdr_size > len) {
|
||||
printf("ELF header outside first block\n");
|
||||
return dead_download;
|
||||
}
|
||||
if (phdr_size > sizeof(estate.p.dummy)) {
|
||||
printf("Program header to big\n");
|
||||
return dead_download;
|
||||
}
|
||||
if (estate.e.elf64.e_entry > ULONG_MAX) {
|
||||
printf("ELF entry point exceeds address space\n");
|
||||
return dead_download;
|
||||
}
|
||||
memcpy(&estate.p.phdr64, data + estate.e.elf64.e_phoff, phdr_size);
|
||||
if (estate.e.elf64.e_type == ET_DYN) {
|
||||
Elf64_Addr min, max, base_addr, delta, align;
|
||||
min = -1;
|
||||
max = 0;
|
||||
align = 1;
|
||||
for(estate.segment = 0; estate.segment < estate.e.elf64.e_phnum; estate.segment++) {
|
||||
Elf64_Addr val;
|
||||
if (estate.p.phdr64[estate.segment].p_type != PT_LOAD)
|
||||
continue;
|
||||
val = estate.p.phdr64[estate.segment].p_paddr;
|
||||
if (val < min) {
|
||||
min = val;
|
||||
}
|
||||
val += estate.p.phdr64[estate.segment].p_memsz;
|
||||
if (val > max) {
|
||||
max = val;
|
||||
}
|
||||
if (estate.p.phdr64[estate.segment].p_align > align) {
|
||||
align = estate.p.phdr64[estate.segment].p_align;
|
||||
}
|
||||
}
|
||||
if (align > ULONG_MAX) {
|
||||
printf("ELF base address alignment exceeds address space\n");
|
||||
return dead_download;
|
||||
}
|
||||
if (align & (align -1)) {
|
||||
printf("ELF base address alignment is not a power of 2\n");
|
||||
return dead_download;
|
||||
}
|
||||
if ((max - min) > ULONG_MAX) {
|
||||
printf("ELF size exceeds address space\n");
|
||||
return dead_download;
|
||||
}
|
||||
base_addr = find_segment(max - min, align);
|
||||
if (base_addr == ULONG_MAX) {
|
||||
printf("ELF base address not available for size %ld\n", max - min);
|
||||
return dead_download;
|
||||
}
|
||||
/* Compute the change in base address and fix up the addresses */
|
||||
delta = base_addr - min;
|
||||
for(estate.segment = 0; estate.segment < estate.e.elf64.e_phnum; estate.segment++) {
|
||||
/* Change the base address of the object to load */
|
||||
estate.p.phdr64[estate.segment].p_paddr += delta;
|
||||
}
|
||||
estate.e.elf64.e_entry += delta;
|
||||
}
|
||||
#if ELF_NOTES
|
||||
/* Load ELF notes from the image */
|
||||
for(estate.segment = 0; estate.segment < estate.e.elf64.e_phnum; estate.segment++) {
|
||||
if (estate.p.phdr64[estate.segment].p_type != PT_NOTE)
|
||||
continue;
|
||||
if (estate.p.phdr64[estate.segment].p_offset + estate.p.phdr64[estate.segment].p_filesz > len) {
|
||||
/* Ignore ELF notes outside of the first block */
|
||||
continue;
|
||||
}
|
||||
process_elf_notes(data,
|
||||
estate.p.phdr64[estate.segment].p_offset, estate.p.phdr64[estate.segment].p_filesz);
|
||||
}
|
||||
#endif
|
||||
/* Check for Etherboot related limitations. Memory
|
||||
* between _text and _end is not allowed.
|
||||
* Reasons: the Etherboot code/data area.
|
||||
*/
|
||||
for (estate.segment = 0; estate.segment < estate.e.elf64.e_phnum; estate.segment++) {
|
||||
unsigned long start, mid, end, istart, iend;
|
||||
if (estate.p.phdr64[estate.segment].p_type != PT_LOAD)
|
||||
continue;
|
||||
if ((estate.p.phdr64[estate.segment].p_paddr > ULONG_MAX) ||
|
||||
((estate.p.phdr64[estate.segment].p_paddr + estate.p.phdr64[estate.segment].p_filesz) > ULONG_MAX) ||
|
||||
((estate.p.phdr64[estate.segment].p_paddr + estate.p.phdr64[estate.segment].p_memsz) > ULONG_MAX)) {
|
||||
printf("ELF segment exceeds address space\n");
|
||||
return dead_download;
|
||||
}
|
||||
start = estate.p.phdr64[estate.segment].p_paddr;
|
||||
mid = start + estate.p.phdr64[estate.segment].p_filesz;
|
||||
end = start + estate.p.phdr64[estate.segment].p_memsz;
|
||||
istart = iend = ULONG_MAX;
|
||||
if ((estate.p.phdr64[estate.segment].p_offset < ULONG_MAX) &&
|
||||
((estate.p.phdr64[estate.segment].p_offset + estate.p.phdr64[estate.segment].p_filesz) < ULONG_MAX))
|
||||
{
|
||||
istart = estate.p.phdr64[estate.segment].p_offset;
|
||||
iend = istart + estate.p.phdr64[estate.segment].p_filesz;
|
||||
}
|
||||
if (!prep_segment(start, mid, end, istart, iend)) {
|
||||
return dead_download;
|
||||
}
|
||||
if (!elf_prep_segment(start, mid, end, istart, iend)) {
|
||||
return dead_download;
|
||||
}
|
||||
}
|
||||
estate.segment = -1;
|
||||
estate.loc = 0;
|
||||
estate.skip = 0;
|
||||
estate.toread = 0;
|
||||
return elf64_download;
|
||||
}
|
||||
|
||||
static sector_t elf64_download(unsigned char *data, unsigned int len, int eof)
|
||||
{
|
||||
unsigned long skip_sectors = 0;
|
||||
unsigned int offset; /* working offset in the current data block */
|
||||
int i;
|
||||
|
||||
offset = 0;
|
||||
do {
|
||||
if (estate.segment != -1) {
|
||||
if (estate.skip) {
|
||||
if (estate.skip >= len - offset) {
|
||||
estate.skip -= len - offset;
|
||||
break;
|
||||
}
|
||||
offset += estate.skip;
|
||||
estate.skip = 0;
|
||||
}
|
||||
|
||||
if (estate.toread) {
|
||||
unsigned int cplen;
|
||||
cplen = len - offset;
|
||||
if (cplen >= estate.toread) {
|
||||
cplen = estate.toread;
|
||||
}
|
||||
memcpy(phys_to_virt(estate.curaddr), data+offset, cplen);
|
||||
estate.curaddr += cplen;
|
||||
estate.toread -= cplen;
|
||||
offset += cplen;
|
||||
if (estate.toread)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Data left, but current segment finished - look for the next
|
||||
* segment (in file offset order) that needs to be loaded.
|
||||
* We can only seek forward, so select the program headers,
|
||||
* in the correct order.
|
||||
*/
|
||||
estate.segment = -1;
|
||||
for (i = 0; i < estate.e.elf64.e_phnum; i++) {
|
||||
if (estate.p.phdr64[i].p_type != PT_LOAD)
|
||||
continue;
|
||||
if (estate.p.phdr64[i].p_filesz == 0)
|
||||
continue;
|
||||
if (estate.p.phdr64[i].p_offset < estate.loc + offset)
|
||||
continue; /* can't go backwards */
|
||||
if ((estate.segment != -1) &&
|
||||
(estate.p.phdr64[i].p_offset >= estate.p.phdr64[estate.segment].p_offset))
|
||||
continue; /* search minimum file offset */
|
||||
estate.segment = i;
|
||||
}
|
||||
if (estate.segment == -1) {
|
||||
/* No more segments to be loaded, so just start the
|
||||
* kernel. This saves a lot of network bandwidth if
|
||||
* debug info is in the kernel but not loaded. */
|
||||
goto elf_startkernel;
|
||||
break;
|
||||
}
|
||||
estate.curaddr = estate.p.phdr64[estate.segment].p_paddr;
|
||||
estate.skip = estate.p.phdr64[estate.segment].p_offset - (estate.loc + offset);
|
||||
estate.toread = estate.p.phdr64[estate.segment].p_filesz;
|
||||
#if ELF_DEBUG
|
||||
printf("PHDR %d, size %#lX, curaddr %#lX\n",
|
||||
estate.segment, estate.toread, estate.curaddr);
|
||||
#endif
|
||||
} while (offset < len);
|
||||
|
||||
estate.loc += len + (estate.skip & ~0x1ff);
|
||||
skip_sectors = estate.skip >> 9;
|
||||
estate.skip &= 0x1ff;
|
||||
|
||||
if (eof) {
|
||||
unsigned long entry;
|
||||
unsigned long machine;
|
||||
elf_startkernel:
|
||||
entry = estate.e.elf64.e_entry;
|
||||
machine = estate.e.elf64.e_machine;
|
||||
#if ELF_NOTES
|
||||
if (estate.check_ip_checksum) {
|
||||
unsigned long bytes = 0;
|
||||
uint16_t sum, new_sum;
|
||||
|
||||
sum = ipchksum(&estate.e.elf64, sizeof(estate.e.elf64));
|
||||
bytes = sizeof(estate.e.elf64);
|
||||
#if ELF_DEBUG
|
||||
printf("Ehdr: %hx %hx sz: %lx bytes: %lx\n",
|
||||
sum, sum, bytes, bytes);
|
||||
#endif
|
||||
|
||||
new_sum = ipchksum(estate.p.phdr64, sizeof(estate.p.phdr64[0]) * estate.e.elf64.e_phnum);
|
||||
sum = add_ipchksums(bytes, sum, new_sum);
|
||||
bytes += sizeof(estate.p.phdr64[0]) * estate.e.elf64.e_phnum;
|
||||
#if ELF_DEBUG
|
||||
printf("Phdr: %hx %hx sz: %lx bytes: %lx\n",
|
||||
new_sum, sum,
|
||||
sizeof(estate.p.phdr64[0]) * estate.e.elf64.e_phnum, bytes);
|
||||
#endif
|
||||
|
||||
for(i = 0; i < estate.e.elf64.e_phnum; i++) {
|
||||
if (estate.p.phdr64[i].p_type != PT_LOAD)
|
||||
continue;
|
||||
new_sum = ipchksum(phys_to_virt(estate.p.phdr64[i].p_paddr),
|
||||
estate.p.phdr64[i].p_memsz);
|
||||
sum = add_ipchksums(bytes, sum, new_sum);
|
||||
bytes += estate.p.phdr64[i].p_memsz;
|
||||
#if ELF_DEBUG
|
||||
printf("seg%d: %hx %hx sz: %x bytes: %lx\n",
|
||||
i, new_sum, sum,
|
||||
estate.p.phdr64[i].p_memsz, bytes);
|
||||
#endif
|
||||
|
||||
}
|
||||
if (estate.ip_checksum != sum) {
|
||||
printf("\nImage checksum: %hx != computed checksum: %hx\n",
|
||||
estate.ip_checksum, sum);
|
||||
longjmp(restart_etherboot, -2);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
done(1);
|
||||
/* Fixup the offset to the program header so you can find the program headers from
|
||||
* the ELF header mknbi needs this.
|
||||
*/
|
||||
estate.e.elf64.e_phoff = (char *)&estate.p - (char *)&estate.e;
|
||||
elf_boot(machine,entry);
|
||||
}
|
||||
return skip_sectors;
|
||||
}
|
||||
|
||||
#endif /* ELF64_IMAGE */
|
||||
168
src/core/heap.c
Normal file
168
src/core/heap.c
Normal file
@@ -0,0 +1,168 @@
|
||||
#include "etherboot.h"
|
||||
|
||||
size_t heap_ptr, heap_top, heap_bot;
|
||||
|
||||
void init_heap(void)
|
||||
{
|
||||
size_t size;
|
||||
size_t start, end;
|
||||
unsigned i;
|
||||
/* Find the largest contiguous area of memory that
|
||||
* I can use for the heap, which is organized as
|
||||
* a stack that grows backwards through memory.
|
||||
*/
|
||||
|
||||
/* If I have virtual address that do not equal physical addresses
|
||||
* there is a change I will try to use memory from both sides of
|
||||
* the virtual address space simultaneously, which can cause all kinds
|
||||
* of interesting problems.
|
||||
* Avoid it by logically extending etherboot. Once I know that relocation
|
||||
* works I can just start the virtual address space at 0, and this problem goes
|
||||
* away so that is probably a better solution.
|
||||
*/
|
||||
#if 0
|
||||
start = virt_to_phys(_text);
|
||||
#else
|
||||
/* segment wrap around is nasty don't chance it. */
|
||||
start = virt_to_phys(_virt_start);
|
||||
#endif
|
||||
end = virt_to_phys(_end);
|
||||
size = 0;
|
||||
for(i = 0; i < meminfo.map_count; i++) {
|
||||
unsigned long r_start, r_end;
|
||||
if (meminfo.map[i].type != E820_RAM)
|
||||
continue;
|
||||
if (meminfo.map[i].addr > ULONG_MAX)
|
||||
continue;
|
||||
if (meminfo.map[i].size > ULONG_MAX)
|
||||
continue;
|
||||
|
||||
r_start = meminfo.map[i].addr;
|
||||
r_end = r_start + meminfo.map[i].size;
|
||||
if (r_end < r_start) {
|
||||
r_end = ULONG_MAX;
|
||||
}
|
||||
/* Handle areas that overlap etherboot */
|
||||
if ((end > r_start) && (start < r_end)) {
|
||||
/* Etherboot completely covers the region */
|
||||
if ((start <= r_start) && (end >= r_end))
|
||||
continue;
|
||||
/* Etherboot is completely contained in the region */
|
||||
if ((start > r_start) && (end < r_end)) {
|
||||
/* keep the larger piece */
|
||||
if ((r_end - end) >= (r_start - start)) {
|
||||
r_start = end;
|
||||
}
|
||||
else {
|
||||
r_end = start;
|
||||
}
|
||||
}
|
||||
/* Etherboot covers one end of the region.
|
||||
* Shrink the region.
|
||||
*/
|
||||
else if (end >= r_end) {
|
||||
r_end = start;
|
||||
}
|
||||
else if (start <= r_start) {
|
||||
r_start = end;
|
||||
}
|
||||
}
|
||||
/* If two areas are the size prefer the greater address */
|
||||
if (((r_end - r_start) > size) ||
|
||||
(((r_end - r_start) == size) && (r_start > heap_top))) {
|
||||
size = r_end - r_start;
|
||||
heap_top = r_start;
|
||||
heap_bot = r_end;
|
||||
}
|
||||
}
|
||||
if (size == 0) {
|
||||
printf("init_heap: No heap found.\n");
|
||||
exit(1);
|
||||
}
|
||||
heap_ptr = heap_bot;
|
||||
}
|
||||
|
||||
void *allot(size_t size)
|
||||
{
|
||||
void *ptr;
|
||||
size_t *mark, addr;
|
||||
/* Get an 16 byte aligned chunk of memory off of the heap
|
||||
* An extra sizeof(size_t) bytes is allocated to track
|
||||
* the size of the object allocated on the heap.
|
||||
*/
|
||||
addr = (heap_ptr - (size + sizeof(size_t))) & ~15;
|
||||
if (addr < heap_top) {
|
||||
ptr = 0;
|
||||
} else {
|
||||
mark = phys_to_virt(addr);
|
||||
*mark = size;
|
||||
heap_ptr = addr;
|
||||
ptr = phys_to_virt(addr + sizeof(size_t));
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
//if mask = 0xf, it will be 16 byte aligned
|
||||
//if mask = 0xff, it will be 256 byte aligned
|
||||
//For DMA memory allocation, because it has more reqiurement on alignment
|
||||
void *allot2(size_t size, uint32_t mask)
|
||||
{
|
||||
void *ptr;
|
||||
size_t *mark, addr;
|
||||
uint32_t *mark1;
|
||||
|
||||
addr = ((heap_ptr - size ) & ~mask) - sizeof(size_t) - sizeof(uint32_t);
|
||||
if (addr < heap_top) {
|
||||
ptr = 0;
|
||||
} else {
|
||||
mark = phys_to_virt(addr);
|
||||
*mark = size;
|
||||
mark1 = phys_to_virt(addr+sizeof(size_t));
|
||||
*mark1 = mask;
|
||||
heap_ptr = addr;
|
||||
ptr = phys_to_virt(addr + sizeof(size_t) + sizeof(uint32_t));
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void forget(void *ptr)
|
||||
{
|
||||
size_t *mark, addr;
|
||||
size_t size;
|
||||
|
||||
if (!ptr) {
|
||||
return;
|
||||
}
|
||||
addr = virt_to_phys(ptr);
|
||||
mark = phys_to_virt(addr - sizeof(size_t));
|
||||
size = *mark;
|
||||
addr += (size + 15) & ~15;
|
||||
|
||||
if (addr > heap_bot) {
|
||||
addr = heap_bot;
|
||||
}
|
||||
heap_ptr = addr;
|
||||
}
|
||||
|
||||
void forget2(void *ptr)
|
||||
{
|
||||
size_t *mark, addr;
|
||||
size_t size;
|
||||
uint32_t mask;
|
||||
uint32_t *mark1;
|
||||
|
||||
if (!ptr) {
|
||||
return;
|
||||
}
|
||||
addr = virt_to_phys(ptr);
|
||||
mark = phys_to_virt(addr - sizeof(size_t) - sizeof(uint32_t));
|
||||
size = *mark;
|
||||
mark1 = phys_to_virt(addr - sizeof(uint32_t));
|
||||
mask = *mark1;
|
||||
addr += (size + mask) & ~mask;
|
||||
|
||||
if (addr > heap_bot) {
|
||||
addr = heap_bot;
|
||||
}
|
||||
heap_ptr = addr;
|
||||
}
|
||||
656
src/core/i82365.c
Normal file
656
src/core/i82365.c
Normal file
@@ -0,0 +1,656 @@
|
||||
#ifdef CONFIG_PCMCIA
|
||||
|
||||
/*
|
||||
* i82365.c
|
||||
* Support for i82365 and similar ISA-to-PCMCIA bridges
|
||||
*
|
||||
* Taken from Linux kernel sources, distributed under GPL2
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS
|
||||
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
* implied. See the License for the specific language governing
|
||||
* rights and limitations under the License.
|
||||
*
|
||||
* The initial developer of the original code is David A. Hinds
|
||||
* <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
|
||||
* are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
|
||||
*
|
||||
* Ported by: Anselm Martin Hoffmeister, Stockholm Projekt Computer-Service, Sankt Augustin/Bonn, GERMANY
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
*
|
||||
* ******************************
|
||||
* PLEASE DO NOT YET WORK ON THIS
|
||||
* ******************************
|
||||
*
|
||||
* I'm still fixing it up on every end, so we most probably would interfere
|
||||
* at some point. If there's anything obvious or better, not-so-obvious,
|
||||
* please contact me by e-mail: anselm (AT) hoffmeister (DOT) be *THANKS*
|
||||
*/
|
||||
#include "../include/pcmcia.h"
|
||||
#include "../include/pcmcia-opts.h"
|
||||
#include "../include/i82365.h"
|
||||
|
||||
#ifndef CONFIG_ISA
|
||||
#error PCMCIA_I82365 only works with ISA defined - set CONFIG_ISA
|
||||
#endif
|
||||
|
||||
typedef enum pcic_id {
|
||||
IS_I82365A, IS_I82365B, IS_I82365DF,
|
||||
IS_IBM, IS_RF5Cx96, IS_VLSI, IS_VG468, IS_VG469,
|
||||
IS_PD6710, IS_PD672X, IS_VT83C469,
|
||||
} pcic_id;
|
||||
|
||||
/* Flags for classifying groups of controllers */
|
||||
#define IS_VADEM 0x0001
|
||||
#define IS_CIRRUS 0x0002
|
||||
#define IS_TI 0x0004
|
||||
#define IS_O2MICRO 0x0008
|
||||
#define IS_VIA 0x0010
|
||||
#define IS_TOPIC 0x0020
|
||||
#define IS_RICOH 0x0040
|
||||
#define IS_UNKNOWN 0x0400
|
||||
#define IS_VG_PWR 0x0800
|
||||
#define IS_DF_PWR 0x1000
|
||||
#define IS_PCI 0x2000
|
||||
#define IS_ALIVE 0x8000
|
||||
|
||||
typedef struct pcic_t {
|
||||
char *name;
|
||||
u_short flags;
|
||||
} pcic_t;
|
||||
|
||||
static pcic_t pcic[] = {
|
||||
{ "Intel i82365sl A step", 0 },
|
||||
{ "Intel i82365sl B step", 0 },
|
||||
{ "Intel i82365sl DF", IS_DF_PWR },
|
||||
{ "IBM Clone", 0 },
|
||||
{ "Ricoh RF5C296/396", 0 },
|
||||
{ "VLSI 82C146", 0 },
|
||||
{ "Vadem VG-468", IS_VADEM },
|
||||
{ "Vadem VG-469", IS_VADEM|IS_VG_PWR },
|
||||
{ "Cirrus PD6710", IS_CIRRUS },
|
||||
{ "Cirrus PD672x", IS_CIRRUS },
|
||||
{ "VIA VT83C469", IS_CIRRUS|IS_VIA },
|
||||
};
|
||||
|
||||
typedef struct cirrus_state_t {
|
||||
u_char misc1, misc2;
|
||||
u_char timer[6];
|
||||
} cirrus_state_t;
|
||||
|
||||
typedef struct vg46x_state_t {
|
||||
u_char ctl, ema;
|
||||
} vg46x_state_t;
|
||||
|
||||
typedef struct socket_info_t {
|
||||
u_short type, flags;
|
||||
socket_cap_t cap;
|
||||
ioaddr_t ioaddr;
|
||||
u_short psock;
|
||||
u_char cs_irq, intr;
|
||||
void (*handler)(void *info, u_int events);
|
||||
void *info;
|
||||
union {
|
||||
cirrus_state_t cirrus;
|
||||
vg46x_state_t vg46x;
|
||||
} state;
|
||||
} socket_info_t;
|
||||
|
||||
//static socket_info_t socket[8];
|
||||
|
||||
int i365_base = 0x3e0; // Default in Linux kernel
|
||||
int cycle_time = 120; // External clock time in ns, 120ns =~ 8.33 MHz
|
||||
int mydriverid = 0;
|
||||
|
||||
void phex ( unsigned char c );
|
||||
/*static int to_cycles(int ns)
|
||||
{
|
||||
return ns/cycle_time;
|
||||
}
|
||||
*/
|
||||
/*static int to_ns(int cycles)
|
||||
{
|
||||
return cycle_time*cycles;
|
||||
}
|
||||
*/
|
||||
|
||||
static u_char i365_get(u_short sock, u_short reg)
|
||||
{
|
||||
//unsigned long flags;
|
||||
//spin_lock_irqsave(&bus_lock,flags);
|
||||
{
|
||||
ioaddr_t port = pccsock[sock].ioaddr;
|
||||
u_char val;
|
||||
reg = I365_REG(pccsock[sock].internalid, reg);
|
||||
outb(reg, port); val = inb(port+1);
|
||||
//spin_unlock_irqrestore(&bus_lock,flags);
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
static void i365_set(u_short sock, u_short reg, u_char data)
|
||||
{
|
||||
//unsigned long flags;
|
||||
//spin_lock_irqsave(&bus_lock,flags);
|
||||
{
|
||||
ioaddr_t port = pccsock[sock].ioaddr;
|
||||
u_char val = I365_REG(pccsock[sock].internalid, reg);
|
||||
outb(val, port); outb(data, port+1);
|
||||
//spin_unlock_irqrestore(&bus_lock,flags);
|
||||
}
|
||||
}
|
||||
|
||||
void add_socket_i365(u_short port, int psock, int type) {
|
||||
pccsock[pccsocks].ioaddr = port;
|
||||
pccsock[pccsocks].internalid = psock;
|
||||
pccsock[pccsocks].type = type;
|
||||
pccsock[pccsocks].flags = pcic[type].flags;
|
||||
pccsock[pccsocks].drivernum = mydriverid;
|
||||
pccsock[pccsocks].configoffset = -1;
|
||||
// Find out if a card in inside that socket
|
||||
pccsock[pccsocks].status = (( 12 == (i365_get(pccsocks,I365_STATUS)&12) ) ? HASCARD : EMPTY );
|
||||
// *TODO* check if that's all
|
||||
if ( 0 == (psock & 1) ) {
|
||||
printf ( "Found a PCMCIA controller (i82365) at io %x, type '%s'\n", port, pcic[type].name );
|
||||
// pccsock[pccsocks].status == HASCARD? "holds card":"empty" );
|
||||
}
|
||||
pccsocks++;
|
||||
return;
|
||||
}
|
||||
|
||||
void i365_bset(u_short sock, u_short reg, u_char mask) {
|
||||
u_char d = i365_get(sock, reg);
|
||||
d |= mask;
|
||||
i365_set(sock, reg, d);
|
||||
}
|
||||
|
||||
void i365_bclr(u_short sock, u_short reg, u_char mask) {
|
||||
u_char d = i365_get(sock, reg);
|
||||
d &= ~mask;
|
||||
i365_set(sock, reg, d);
|
||||
}
|
||||
|
||||
|
||||
/*static void i365_bflip(u_short sock, u_short reg, u_char mask, int b)
|
||||
{
|
||||
u_char d = i365_get(sock, reg);
|
||||
if (b)
|
||||
d |= mask;
|
||||
else
|
||||
d &= ~mask;
|
||||
i365_set(sock, reg, d);
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
static u_short i365_get_pair(u_short sock, u_short reg)
|
||||
{
|
||||
u_short a, b;
|
||||
a = i365_get(sock, reg);
|
||||
b = i365_get(sock, reg+1);
|
||||
return (a + (b<<8));
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
static void i365_set_pair(u_short sock, u_short reg, u_short data)
|
||||
{
|
||||
i365_set(sock, reg, data & 0xff);
|
||||
i365_set(sock, reg+1, data >> 8);
|
||||
}
|
||||
*/
|
||||
int identify_i365 ( u_short port, u_short sock ) {
|
||||
u_char val;
|
||||
int type = -1;
|
||||
/* Use the next free entry in the socket table */
|
||||
pccsock[pccsocks].ioaddr = port;
|
||||
pccsock[pccsocks].internalid = sock;
|
||||
// *TODO* wakeup a sleepy cirrus controller?
|
||||
|
||||
if ((val = i365_get(pccsocks, I365_IDENT)) & 0x70)
|
||||
return -1;
|
||||
switch (val) {
|
||||
case 0x82:
|
||||
type = IS_I82365A; break;
|
||||
case 0x83:
|
||||
type = IS_I82365B; break;
|
||||
case 0x84:
|
||||
type = IS_I82365DF; break;
|
||||
case 0x88: case 0x89: case 0x8a:
|
||||
type = IS_IBM; break;
|
||||
}
|
||||
/* Check for Vadem VG-468 chips */
|
||||
outb(0x0e, port);
|
||||
outb(0x37, port);
|
||||
i365_bset(pccsocks, VG468_MISC, VG468_MISC_VADEMREV);
|
||||
val = i365_get(pccsocks, I365_IDENT);
|
||||
if (val & I365_IDENT_VADEM) {
|
||||
i365_bclr(pccsocks, VG468_MISC, VG468_MISC_VADEMREV);
|
||||
type = ((val & 7) >= 4) ? IS_VG469 : IS_VG468;
|
||||
}
|
||||
|
||||
/* Check for Ricoh chips */
|
||||
val = i365_get(pccsocks, RF5C_CHIP_ID);
|
||||
if ((val == RF5C_CHIP_RF5C296) || (val == RF5C_CHIP_RF5C396)) type = IS_RF5Cx96;
|
||||
|
||||
/* Check for Cirrus CL-PD67xx chips */
|
||||
i365_set(pccsocks, PD67_CHIP_INFO, 0);
|
||||
val = i365_get(pccsocks, PD67_CHIP_INFO);
|
||||
if ((val & PD67_INFO_CHIP_ID) == PD67_INFO_CHIP_ID) {
|
||||
val = i365_get(pccsocks, PD67_CHIP_INFO);
|
||||
if ((val & PD67_INFO_CHIP_ID) == 0) {
|
||||
type = (val & PD67_INFO_SLOTS) ? IS_PD672X : IS_PD6710;
|
||||
i365_set(pccsocks, PD67_EXT_INDEX, 0xe5);
|
||||
if (i365_get(pccsocks, PD67_EXT_INDEX) != 0xe5) type = IS_VT83C469;
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
int init_i82365(void) {
|
||||
int i, j, sock, k, ns, id;
|
||||
//unsigned int ui,uj;
|
||||
//unsigned char * upc;
|
||||
ioaddr_t port;
|
||||
int i82365s = 0;
|
||||
// Change from kernel: No irq init, no check_region, no isapnp support
|
||||
// No ignore socket, no extra sockets to check (so it's easier here :-/)
|
||||
// Probably we don't need any of them; in case YOU do, SHOUT AT ME!
|
||||
id = identify_i365(i365_base, 0);
|
||||
if ((id == IS_I82365DF) && (identify_i365(i365_base, 1) != id)) {
|
||||
for (i = 0; i < 4; i++) {
|
||||
port = i365_base + ((i & 1) << 2) + ((i & 2) << 1);
|
||||
sock = (i & 1) << 1;
|
||||
if (identify_i365(port, sock) == IS_I82365DF) {
|
||||
add_socket_i365(port, sock, IS_VLSI);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < 4; i += 2) {
|
||||
port = i365_base + 2*(i>>2);
|
||||
sock = (i & 3);
|
||||
id = identify_i365(port, sock);
|
||||
if (id < 0) continue;
|
||||
|
||||
for (j = ns = 0; j < 2; j++) {
|
||||
/* Does the socket exist? */
|
||||
if (identify_i365(port, sock+j) < 0) continue;
|
||||
/* Check for bad socket decode */
|
||||
for (k = 0; k <= i82365s; k++)
|
||||
i365_set(k, I365_MEM(0)+I365_W_OFF, k);
|
||||
for (k = 0; k <= i82365s; k++)
|
||||
if (i365_get(k, I365_MEM(0)+I365_W_OFF) != k)
|
||||
break;
|
||||
if (k <= i82365s) break;
|
||||
add_socket_i365(port, sock+j, id); ns++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* printf ( "Selecting config 1: io 0x300 @byte 87*2.." );
|
||||
upc[(2*87)] = 2;
|
||||
i365_bclr(1, I365_ADDRWIN, 1 );
|
||||
i365_set(1,I365_INTCTL, 0x65 ); //no-reset, memory-card
|
||||
i365_set(1, I365_IO(0)+0, 0x20 );
|
||||
i365_set(1, I365_IO(0)+1, 0x03 );
|
||||
i365_set(1, I365_IO(0)+2, 0x3f );
|
||||
i365_set(1, I365_IO(0)+3, 0x03 );
|
||||
i365_set(1, 0x3a, 0x05 );
|
||||
i365_set(1, 0x3b, 0x05 );
|
||||
i365_set(1, 0x3c, 0x05 );
|
||||
i365_set(1, 0x3d, 0x05 );
|
||||
i365_set(1, 0x3e, 0x05 );
|
||||
i365_set(1, 0x3f, 0x05 );
|
||||
i365_set(1, 0x07, 0x0a );
|
||||
i365_set(1, I365_ADDRWIN, 0x40 ); // 0x40
|
||||
printf ( "!\n" ); getchar();
|
||||
printf ( "\n" );
|
||||
return 0; */
|
||||
}
|
||||
|
||||
void phex ( unsigned char c ) {
|
||||
unsigned char a = 0, b = 0;
|
||||
b = ( c & 0xf );
|
||||
if ( b > 9 ) b += ('a'-'9'-1);
|
||||
b += '0';
|
||||
a = ( c & 0xf0 ) >> 4;
|
||||
if ( a > 9 ) a += ('a'-'9'-1);
|
||||
a += '0';
|
||||
printf ( "%c%c ", a, b );
|
||||
return;
|
||||
}
|
||||
|
||||
int deinit_i82365(void) {
|
||||
printf("Deinitializing i82365\n" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*static int i365_get_status(u_short sock, u_int *value)
|
||||
{
|
||||
u_int status;
|
||||
|
||||
status = i365_get(sock, I365_STATUS);
|
||||
*value = ((status & I365_CS_DETECT) == I365_CS_DETECT)
|
||||
? SS_DETECT : 0;
|
||||
|
||||
if (i365_get(sock, I365_INTCTL) & I365_PC_IOCARD)
|
||||
*value |= (status & I365_CS_STSCHG) ? 0 : SS_STSCHG;
|
||||
else {
|
||||
*value |= (status & I365_CS_BVD1) ? 0 : SS_BATDEAD;
|
||||
*value |= (status & I365_CS_BVD2) ? 0 : SS_BATWARN;
|
||||
}
|
||||
*value |= (status & I365_CS_WRPROT) ? SS_WRPROT : 0;
|
||||
*value |= (status & I365_CS_READY) ? SS_READY : 0;
|
||||
*value |= (status & I365_CS_POWERON) ? SS_POWERON : 0;
|
||||
|
||||
#ifdef CONFIG_ISA
|
||||
if (pccsock[sock].type == IS_VG469) {
|
||||
status = i365_get(sock, VG469_VSENSE);
|
||||
if (pccsock[sock].internalid & 1) {
|
||||
*value |= (status & VG469_VSENSE_B_VS1) ? 0 : SS_3VCARD;
|
||||
*value |= (status & VG469_VSENSE_B_VS2) ? 0 : SS_XVCARD;
|
||||
} else {
|
||||
*value |= (status & VG469_VSENSE_A_VS1) ? 0 : SS_3VCARD;
|
||||
*value |= (status & VG469_VSENSE_A_VS2) ? 0 : SS_XVCARD;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
printf("i82365: GetStatus(%d) = %#4.4x\n", sock, *value);
|
||||
return 0;
|
||||
} //i365_get_status
|
||||
*/
|
||||
|
||||
/*static int i365_set_socket(u_short sock, socket_state_t *state)
|
||||
{
|
||||
socket_info_t *t = &socket[sock];
|
||||
u_char reg;
|
||||
|
||||
printf("i82365: SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
|
||||
"io_irq %d, csc_mask %#2.2x)\n", sock, state->flags,
|
||||
state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
|
||||
printf ("\nERROR:UNIMPLEMENTED\n" );
|
||||
return 0;
|
||||
// First set global controller options
|
||||
// set_bridge_state(sock); *TODO* check: need this here?
|
||||
|
||||
// IO card, RESET flag, IO interrupt
|
||||
reg = t->intr;
|
||||
if (state->io_irq != t->cap.pci_irq) reg |= state->io_irq;
|
||||
reg |= (state->flags & SS_RESET) ? 0 : I365_PC_RESET;
|
||||
reg |= (state->flags & SS_IOCARD) ? I365_PC_IOCARD : 0;
|
||||
i365_set(sock, I365_INTCTL, reg);
|
||||
|
||||
reg = I365_PWR_NORESET;
|
||||
if (state->flags & SS_PWR_AUTO) reg |= I365_PWR_AUTO;
|
||||
if (state->flags & SS_OUTPUT_ENA) reg |= I365_PWR_OUT;
|
||||
|
||||
if (t->flags & IS_CIRRUS) {
|
||||
if (state->Vpp != 0) {
|
||||
if (state->Vpp == 120)
|
||||
reg |= I365_VPP1_12V;
|
||||
else if (state->Vpp == state->Vcc)
|
||||
reg |= I365_VPP1_5V;
|
||||
else return -EINVAL;
|
||||
}
|
||||
if (state->Vcc != 0) {
|
||||
reg |= I365_VCC_5V;
|
||||
if (state->Vcc == 33)
|
||||
i365_bset(sock, PD67_MISC_CTL_1, PD67_MC1_VCC_3V);
|
||||
else if (state->Vcc == 50)
|
||||
i365_bclr(sock, PD67_MISC_CTL_1, PD67_MC1_VCC_3V);
|
||||
else return -EINVAL;
|
||||
}
|
||||
} else if (t->flags & IS_VG_PWR) {
|
||||
if (state->Vpp != 0) {
|
||||
if (state->Vpp == 120)
|
||||
reg |= I365_VPP1_12V;
|
||||
else if (state->Vpp == state->Vcc)
|
||||
reg |= I365_VPP1_5V;
|
||||
else return -EINVAL;
|
||||
}
|
||||
if (state->Vcc != 0) {
|
||||
reg |= I365_VCC_5V;
|
||||
if (state->Vcc == 33)
|
||||
i365_bset(sock, VG469_VSELECT, VG469_VSEL_VCC);
|
||||
else if (state->Vcc == 50)
|
||||
i365_bclr(sock, VG469_VSELECT, VG469_VSEL_VCC);
|
||||
else return -EINVAL;
|
||||
}
|
||||
} else if (t->flags & IS_DF_PWR) {
|
||||
switch (state->Vcc) {
|
||||
case 0: break;
|
||||
case 33: reg |= I365_VCC_3V; break;
|
||||
case 50: reg |= I365_VCC_5V; break;
|
||||
default: return -EINVAL;
|
||||
}
|
||||
switch (state->Vpp) {
|
||||
case 0: break;
|
||||
case 50: reg |= I365_VPP1_5V; break;
|
||||
case 120: reg |= I365_VPP1_12V; break;
|
||||
default: return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
switch (state->Vcc) {
|
||||
case 0: break;
|
||||
case 50: reg |= I365_VCC_5V; break;
|
||||
default: return -EINVAL;
|
||||
}
|
||||
switch (state->Vpp) {
|
||||
case 0: break;
|
||||
case 50: reg |= I365_VPP1_5V | I365_VPP2_5V; break;
|
||||
case 120: reg |= I365_VPP1_12V | I365_VPP2_12V; break;
|
||||
default: return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (reg != i365_get(sock, I365_POWER))
|
||||
i365_set(sock, I365_POWER, reg);
|
||||
|
||||
// Chipset-specific functions
|
||||
if (t->flags & IS_CIRRUS) {
|
||||
// Speaker control
|
||||
i365_bflip(sock, PD67_MISC_CTL_1, PD67_MC1_SPKR_ENA,
|
||||
state->flags & SS_SPKR_ENA);
|
||||
}
|
||||
|
||||
// Card status change interrupt mask
|
||||
reg = t->cs_irq << 4;
|
||||
if (state->csc_mask & SS_DETECT) reg |= I365_CSC_DETECT;
|
||||
if (state->flags & SS_IOCARD) {
|
||||
if (state->csc_mask & SS_STSCHG) reg |= I365_CSC_STSCHG;
|
||||
} else {
|
||||
if (state->csc_mask & SS_BATDEAD) reg |= I365_CSC_BVD1;
|
||||
if (state->csc_mask & SS_BATWARN) reg |= I365_CSC_BVD2;
|
||||
if (state->csc_mask & SS_READY) reg |= I365_CSC_READY;
|
||||
}
|
||||
i365_set(sock, I365_CSCINT, reg);
|
||||
i365_get(sock, I365_CSC);
|
||||
|
||||
return 0;
|
||||
} // i365_set_socket
|
||||
*/
|
||||
|
||||
/*static int i365_get_io_map(u_short sock, struct pccard_io_map *io)
|
||||
{
|
||||
u_char map, ioctl, addr;
|
||||
printf ( "GETIOMAP unimplemented\n" ); return 0;
|
||||
map = io->map;
|
||||
if (map > 1) return -EINVAL;
|
||||
io->start = i365_get_pair(sock, I365_IO(map)+I365_W_START);
|
||||
io->stop = i365_get_pair(sock, I365_IO(map)+I365_W_STOP);
|
||||
ioctl = i365_get(sock, I365_IOCTL);
|
||||
addr = i365_get(sock, I365_ADDRWIN);
|
||||
io->speed = to_ns(ioctl & I365_IOCTL_WAIT(map)) ? 1 : 0;
|
||||
io->flags = (addr & I365_ENA_IO(map)) ? MAP_ACTIVE : 0;
|
||||
io->flags |= (ioctl & I365_IOCTL_0WS(map)) ? MAP_0WS : 0;
|
||||
io->flags |= (ioctl & I365_IOCTL_16BIT(map)) ? MAP_16BIT : 0;
|
||||
io->flags |= (ioctl & I365_IOCTL_IOCS16(map)) ? MAP_AUTOSZ : 0;
|
||||
printf("i82365: GetIOMap(%d, %d) = %#2.2x, %d ns, "
|
||||
"%#4.4x-%#4.4x\n", sock, map, io->flags, io->speed,
|
||||
io->start, io->stop);
|
||||
return 0;
|
||||
} // i365_get_io_map
|
||||
*/
|
||||
|
||||
/*====================================================================*/
|
||||
|
||||
/*static int i365_set_io_map(u_short sock, struct pccard_io_map *io)
|
||||
{
|
||||
u_char map, ioctl;
|
||||
|
||||
printf("i82365: SetIOMap(%d, %d, %#2.2x, %d ns, "
|
||||
"%#4.4x-%#4.4x)\n", sock, io->map, io->flags,
|
||||
io->speed, io->start, io->stop);
|
||||
printf ( "UNIMPLEMENTED\n" );
|
||||
return 0;
|
||||
map = io->map;
|
||||
//if ((map > 1) || (io->start > 0xffff) || (io->stop > 0xffff) ||
|
||||
if ((map > 1) ||
|
||||
(io->stop < io->start)) return -EINVAL;
|
||||
// Turn off the window before changing anything
|
||||
if (i365_get(sock, I365_ADDRWIN) & I365_ENA_IO(map))
|
||||
i365_bclr(sock, I365_ADDRWIN, I365_ENA_IO(map));
|
||||
i365_set_pair(sock, I365_IO(map)+I365_W_START, io->start);
|
||||
i365_set_pair(sock, I365_IO(map)+I365_W_STOP, io->stop);
|
||||
ioctl = i365_get(sock, I365_IOCTL) & ~I365_IOCTL_MASK(map);
|
||||
if (io->speed) ioctl |= I365_IOCTL_WAIT(map);
|
||||
if (io->flags & MAP_0WS) ioctl |= I365_IOCTL_0WS(map);
|
||||
if (io->flags & MAP_16BIT) ioctl |= I365_IOCTL_16BIT(map);
|
||||
if (io->flags & MAP_AUTOSZ) ioctl |= I365_IOCTL_IOCS16(map);
|
||||
i365_set(sock, I365_IOCTL, ioctl);
|
||||
// Turn on the window if necessary
|
||||
if (io->flags & MAP_ACTIVE)
|
||||
i365_bset(sock, I365_ADDRWIN, I365_ENA_IO(map));
|
||||
return 0;
|
||||
} // i365_set_io_map
|
||||
*/
|
||||
|
||||
/*
|
||||
static int i365_set_mem_map(u_short sock, struct pccard_mem_map *mem)
|
||||
{
|
||||
u_short base, i;
|
||||
u_char map;
|
||||
|
||||
printf("i82365: SetMemMap(%d, %d, %#2.2x, %d ns, %#5.5lx-%#5.5"
|
||||
"lx, %#5.5x)\n", sock, mem->map, mem->flags, mem->speed,
|
||||
mem->sys_start, mem->sys_stop, mem->card_start);
|
||||
|
||||
printf ( "UNIMPLEMENTED\n" );
|
||||
return 0;
|
||||
map = mem->map;
|
||||
if ((map > 4) || (mem->card_start > 0x3ffffff) ||
|
||||
(mem->sys_start > mem->sys_stop) || (mem->speed > 1000))
|
||||
return -EINVAL;
|
||||
if (!(socket[sock].flags & IS_PCI) &&
|
||||
((mem->sys_start > 0xffffff) || (mem->sys_stop > 0xffffff)))
|
||||
return -EINVAL;
|
||||
|
||||
// Turn off the window before changing anything
|
||||
if (i365_get(sock, I365_ADDRWIN) & I365_ENA_MEM(map))
|
||||
i365_bclr(sock, I365_ADDRWIN, I365_ENA_MEM(map));
|
||||
|
||||
base = I365_MEM(map);
|
||||
i = (mem->sys_start >> 12) & 0x0fff;
|
||||
if (mem->flags & MAP_16BIT) i |= I365_MEM_16BIT;
|
||||
if (mem->flags & MAP_0WS) i |= I365_MEM_0WS;
|
||||
i365_set_pair(sock, base+I365_W_START, i);
|
||||
|
||||
i = (mem->sys_stop >> 12) & 0x0fff;
|
||||
switch (to_cycles(mem->speed)) {
|
||||
case 0: break;
|
||||
case 1: i |= I365_MEM_WS0; break;
|
||||
case 2: i |= I365_MEM_WS1; break;
|
||||
default: i |= I365_MEM_WS1 | I365_MEM_WS0; break;
|
||||
}
|
||||
i365_set_pair(sock, base+I365_W_STOP, i);
|
||||
|
||||
i = ((mem->card_start - mem->sys_start) >> 12) & 0x3fff;
|
||||
if (mem->flags & MAP_WRPROT) i |= I365_MEM_WRPROT;
|
||||
if (mem->flags & MAP_ATTRIB) i |= I365_MEM_REG;
|
||||
i365_set_pair(sock, base+I365_W_OFF, i);
|
||||
|
||||
// Turn on the window if necessary
|
||||
if (mem->flags & MAP_ACTIVE)
|
||||
i365_bset(sock, I365_ADDRWIN, I365_ENA_MEM(map));
|
||||
return 0;
|
||||
} // i365_set_mem_map
|
||||
*/
|
||||
|
||||
|
||||
int i82365_interfacer ( interface_func_t func, int sockno, int par1, int par2, void* par3 ) {
|
||||
//int i, j, k;
|
||||
//u_int ui;
|
||||
u_char *upc;
|
||||
struct pcc_config_t * pccc;
|
||||
switch ( func ) {
|
||||
case INIT:
|
||||
mydriverid = par1;
|
||||
return init_i82365();
|
||||
case SHUTDOWN:
|
||||
i365_set(sockno, I365_ADDRWIN, i365_get(sockno, I365_ADDRWIN) & 0x20 );
|
||||
i365_set(sockno, I365_INTCTL, 0x05 );
|
||||
sleepticks(2);
|
||||
i365_set(sockno,I365_INTCTL, 0x45 ); //no-reset, memory-card
|
||||
break;
|
||||
case MAPATTRMEM:
|
||||
i365_set(sockno,I365_POWER, 0xb1 );
|
||||
i365_set(sockno, I365_INTCTL, 0x05 );
|
||||
sleepticks(2);
|
||||
i365_set(sockno,I365_INTCTL, 0x45 ); //no-reset, memory-card
|
||||
i365_set(sockno, I365_ADDRWIN, i365_get(sockno, I365_ADDRWIN) & 0x20 );
|
||||
//i365_bclr(sockno, I365_ADDRWIN, 1 );
|
||||
i365_set(sockno, I365_MEM(0)+0, ( par1 >> 12 )& 0xff ); //start
|
||||
i365_set(sockno, I365_MEM(0)+1, ( par1 >> 20 ) & 0x0f );
|
||||
i365_set(sockno, I365_MEM(0)+2, ((par1 + par2 - 1 ) >> 12 ) & 0xff ); //end
|
||||
i365_set(sockno, I365_MEM(0)+3, (( par1 + par2 - 1 ) >> 20 ) & 0x0f );
|
||||
i365_set(sockno, I365_MEM(0)+4, ((0x4000000 - par1) >> 12) & 0xff ); //offset low
|
||||
i365_set(sockno, I365_MEM(0)+5, 0x40 | (((0x40000000 - par1) >> 12) & 0x3f));
|
||||
i365_bset(sockno, I365_ADDRWIN, 1 );
|
||||
if ( ! ( 1 & i365_get ( sockno, I365_ADDRWIN ) ) ) return 1;
|
||||
break;
|
||||
case UNMAPATTRMEM:
|
||||
i365_set(sockno, I365_ADDRWIN, i365_get(sockno, I365_ADDRWIN) & 0x20 );
|
||||
i365_set(sockno,I365_INTCTL, 0x45 ); //no-reset, memory-card
|
||||
break;
|
||||
case SELECTCONFIG: // Params: par1: config number; par3 config pointer pointer
|
||||
if ( 0 > pccsock[sockno].configoffset ) return 1;
|
||||
if ( NULL == (pccc = par3 ) ) return 2;
|
||||
// write config number to
|
||||
upc = ioremap ( MAP_ATTRMEM_TO, MAP_ATTRMEM_LEN );
|
||||
if ( pccsock[sockno].configoffset > MAP_ATTRMEM_LEN ) return 3;
|
||||
if ( ( par1 & 0x7fffffc0 ) ) return 4;
|
||||
if ( pccc->index != par1 ) return 5;
|
||||
upc[pccsock[sockno].configoffset] = ( upc[pccsock[sockno].configoffset] & 0xc0 ) | ( par1 & 0x3f );
|
||||
i365_set(sockno, I365_IOCTL, (i365_get(sockno, I365_IOCTL) & 0xfe) | 0x20 ); // 16bit autosize
|
||||
i365_set(sockno, I365_IO(0)+0, pccc->iowin & 0xff);
|
||||
i365_set(sockno, I365_IO(0)+1, (pccc->iowin >> 8) & 0xff);
|
||||
i365_set(sockno, I365_IO(0)+2, (pccc->iowin+pccc->iolen - 1) & 0xff);
|
||||
i365_set(sockno, I365_IO(0)+3, ((pccc->iowin+pccc->iolen- 1) >> 8) & 0xff);
|
||||
// Disable mem mapping
|
||||
i365_bclr(sockno, I365_ADDRWIN, 1);
|
||||
i365_set(sockno, I365_INTCTL, 0x65);
|
||||
i365_bset(sockno, I365_ADDRWIN,0x40);
|
||||
break;
|
||||
default:
|
||||
return -1; // ERROR: Unknown function called
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// get_mem_map[1320]
|
||||
// cirrus_get_state/set/opts...
|
||||
// vg46x_get_state/...
|
||||
// get_bridge_state/...
|
||||
|
||||
#endif /* CONFIG_PCMCIA */
|
||||
66
src/core/isa_probe.c
Normal file
66
src/core/isa_probe.c
Normal file
@@ -0,0 +1,66 @@
|
||||
#ifdef CONFIG_ISA
|
||||
/*
|
||||
* 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 "etherboot.h"
|
||||
#include "nic.h"
|
||||
#include "isa.h"
|
||||
|
||||
void isa_enumerate(void)
|
||||
{
|
||||
const struct isa_driver *driver;
|
||||
for(driver = isa_drivers; driver < isa_drivers_end; driver++) {
|
||||
printf("%s ", driver->name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int isa_probe(struct dev *dev, const char *type_name)
|
||||
{
|
||||
/*
|
||||
* NIC probing is in the order the drivers were linked togeter.
|
||||
* If for some reason you want to change the order,
|
||||
* just change the order you list the drivers in.
|
||||
*/
|
||||
struct isa_probe_state *state = &dev->state.isa;
|
||||
printf("Probing isa %s...\n", type_name);
|
||||
if (dev->how_probe == PROBE_FIRST) {
|
||||
state->advance = 0;
|
||||
state->driver = isa_drivers;
|
||||
dev->index = -1;
|
||||
}
|
||||
for(;;)
|
||||
{
|
||||
if ((dev->how_probe != PROBE_AWAKE) && state->advance) {
|
||||
state->driver++;
|
||||
dev->index = -1;
|
||||
}
|
||||
state->advance = 1;
|
||||
|
||||
if (state->driver >= isa_drivers_end)
|
||||
break;
|
||||
|
||||
if (state->driver->type != dev->type)
|
||||
continue;
|
||||
|
||||
if (dev->how_probe != PROBE_AWAKE) {
|
||||
dev->type_index++;
|
||||
}
|
||||
printf("[%s]", state->driver->name);
|
||||
dev->devid.bus_type = ISA_BUS_TYPE;
|
||||
/* FIXME how do I handle dev->index + PROBE_AGAIN?? */
|
||||
/* driver will fill in vendor and device IDs */
|
||||
if (state->driver->probe(dev, state->driver->ioaddrs)) {
|
||||
state->advance = (dev->index == -1);
|
||||
return PROBE_WORKED;
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
return PROBE_FAILED;
|
||||
}
|
||||
|
||||
#endif
|
||||
382
src/core/isapnp.c
Normal file
382
src/core/isapnp.c
Normal file
@@ -0,0 +1,382 @@
|
||||
#ifdef CONFIG_ISA
|
||||
/**************************************************************************
|
||||
*
|
||||
* isapnp.c -- Etherboot isapnp support for the 3Com 3c515
|
||||
* Written 2002-2003 by Timothy Legge <tlegge@rogers.com>
|
||||
*
|
||||
* 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 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* Portions of this code:
|
||||
* Copyright (C) 2001 P.J.H.Fox (fox@roestock.demon.co.uk)
|
||||
*
|
||||
*
|
||||
* REVISION HISTORY:
|
||||
* ================
|
||||
* Version 0.1 April 26, 2002 TJL
|
||||
* Version 0.2 01/08/2003 TJL Moved outside the 3c515.c driver file
|
||||
* Version 0.3 Sept 23, 2003 timlegge Change delay to currticks
|
||||
*
|
||||
*
|
||||
* Indent Options: indent -kr -i8
|
||||
***************************************************************************/
|
||||
|
||||
/* to get some global routines like printf */
|
||||
#include "etherboot.h"
|
||||
#include "timer.h"
|
||||
#include "isapnp.h"
|
||||
|
||||
static int pnp_card_csn = 0;
|
||||
|
||||
void isapnp_wait(unsigned int nticks)
|
||||
{
|
||||
unsigned int to = currticks() + nticks;
|
||||
while (currticks() < to)
|
||||
/* Wait */ ;
|
||||
}
|
||||
|
||||
/* The following code is the ISA PNP logic required to activate the 3c515 */
|
||||
/* PNP Defines */
|
||||
#define IDENT_LEN 9
|
||||
#define NUM_CARDS 128
|
||||
|
||||
/* PNP declares */
|
||||
static unsigned char serial_identifier[NUM_CARDS + 1][IDENT_LEN];
|
||||
static unsigned char isapnp_checksum_value;
|
||||
static char initdata[INIT_LENGTH] = INITDATA;
|
||||
int read_port = 0;
|
||||
|
||||
|
||||
/* PNP Prototypes */
|
||||
static int Isolate(void);
|
||||
static int do_isapnp_isolate(void);
|
||||
static int isapnp_build_device_list(void);
|
||||
static int isapnp_isolate_rdp_select(void);
|
||||
static int isapnp_next_rdp(void);
|
||||
static void isapnp_peek(unsigned char *data, int bytes);
|
||||
static void send_key(void);
|
||||
static unsigned char isapnp_checksum(unsigned char *data);
|
||||
int Config(int csn);
|
||||
|
||||
void config_pnp_device(void)
|
||||
{
|
||||
/* PNP Configuration */
|
||||
printf("Probing/Configuring ISAPNP devices\n");
|
||||
if (!read_port) {
|
||||
Isolate();
|
||||
if (pnp_card_csn)
|
||||
Config(pnp_card_csn);
|
||||
}
|
||||
}
|
||||
|
||||
/* Isolate all the PNP Boards on the ISA BUS */
|
||||
static int Isolate(void)
|
||||
{
|
||||
int cards = 0;
|
||||
if (read_port < 0x203 || read_port > 0x3ff) {
|
||||
cards = do_isapnp_isolate();
|
||||
if (cards < 0 || (read_port < 0x203 || read_port > 0x3ff)) {
|
||||
printf("No Plug & Play device found\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
isapnp_build_device_list();
|
||||
#ifdef EDEBUG
|
||||
printf("%d Plug & Play device found\n", cards);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_isapnp_isolate(void)
|
||||
{
|
||||
unsigned char checksum = 0x6a;
|
||||
unsigned char chksum = 0x00;
|
||||
unsigned char bit = 0x00;
|
||||
unsigned char c1, c2;
|
||||
int csn = 0;
|
||||
int i;
|
||||
int iteration = 1;
|
||||
|
||||
read_port = 0x213;
|
||||
if (isapnp_isolate_rdp_select() < 0)
|
||||
return -1;
|
||||
|
||||
while (1) {
|
||||
for (i = 1; i <= 64; i++) {
|
||||
c1 = READ_DATA;
|
||||
isapnp_wait(1);
|
||||
c2 = READ_DATA;
|
||||
isapnp_wait(1);
|
||||
if (c1 == 0x55) {
|
||||
if (c2 == 0xAA) {
|
||||
bit = 0x01;
|
||||
}
|
||||
}
|
||||
checksum =
|
||||
((((checksum ^ (checksum >> 1)) & 0x01) ^ bit)
|
||||
<< 7) | (checksum >> 1);
|
||||
bit = 0x00;
|
||||
}
|
||||
#ifdef EDEBUG
|
||||
printf("Calc checksum %d", checksum);
|
||||
#endif
|
||||
for (i = 65; i <= 72; i++) {
|
||||
c1 = READ_DATA;
|
||||
udelay(250);
|
||||
c2 = READ_DATA;
|
||||
udelay(250);
|
||||
if (c1 == 0x55) {
|
||||
if (c2 == 0xAA)
|
||||
chksum |= (1 << (i - 65));
|
||||
}
|
||||
}
|
||||
#ifdef EDEBUG
|
||||
printf("Actual checksum %d", chksum);
|
||||
#endif
|
||||
if (checksum != 0x00 && checksum == chksum) {
|
||||
csn++;
|
||||
serial_identifier[csn][iteration] >>= 1;
|
||||
serial_identifier[csn][iteration] |= bit;
|
||||
CARDSELECTNUMBER;
|
||||
#ifdef EDEBUG
|
||||
printf("Writing csn: %d", csn);
|
||||
#endif
|
||||
WRITE_DATA(csn);
|
||||
udelay(250);
|
||||
iteration++;
|
||||
/* Force all cards without a CSN into Isolation state */
|
||||
Wake(0);
|
||||
SetRdPort(read_port);
|
||||
udelay(1000);
|
||||
SERIALISOLATION;
|
||||
udelay(1000);
|
||||
goto __next;
|
||||
}
|
||||
if (iteration == 1) {
|
||||
read_port += READ_ADDR_STEP;
|
||||
if (isapnp_isolate_rdp_select() < 0)
|
||||
return -1;
|
||||
} else if (iteration > 1) {
|
||||
break;
|
||||
}
|
||||
__next:
|
||||
checksum = 0x6a;
|
||||
chksum = 0x00;
|
||||
bit = 0x00;
|
||||
}
|
||||
return csn;
|
||||
}
|
||||
|
||||
/*
|
||||
* Build device list for all present ISA PnP devices.
|
||||
*/
|
||||
static int isapnp_build_device_list(void)
|
||||
{
|
||||
int csn, device, vendor, serial;
|
||||
unsigned char header[9], checksum;
|
||||
for (csn = 1; csn <= 10; csn++) {
|
||||
Wake(csn);
|
||||
isapnp_peek(header, 9);
|
||||
checksum = isapnp_checksum(header);
|
||||
#ifdef EDEBUG
|
||||
printf
|
||||
("vendor: 0x%hX:0x%hX:0x%hX:0x%hX:0x%hX:0x%hX:0x%hX:0x%hX:0x%hX\n",
|
||||
header[0], header[1], header[2], header[3], header[4],
|
||||
header[5], header[6], header[7], header[8]);
|
||||
printf("checksum = 0xhX\n", checksum);
|
||||
#endif
|
||||
/* Don't be strict on the checksum, here !
|
||||
e.g. 'SCM SwapBox Plug and Play' has header[8]==0 (should be: b7) */
|
||||
if (header[8] == 0);
|
||||
else if (checksum == 0x00 || checksum != header[8]) /* not valid CSN */
|
||||
continue;
|
||||
|
||||
vendor = (header[1] << 8) | header[0];
|
||||
device = (header[3] << 8) | header[2];
|
||||
serial =
|
||||
(header[7] << 24) | (header[6] << 16) | (header[5] <<
|
||||
8) |
|
||||
header[4];
|
||||
if (vendor == 0x6D50)
|
||||
if (device == 0x5150) {
|
||||
printf
|
||||
("\nFound 3Com 3c515 PNP Card!\n Vendor ID: 0x%hX, Device ID: 0x%hX, Serial Num: 0x%hX\n",
|
||||
vendor, device, serial);
|
||||
pnp_card_csn = csn;
|
||||
}
|
||||
isapnp_checksum_value = 0x00;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Config(int csn)
|
||||
{
|
||||
#define TIMEOUT_PNP 100
|
||||
unsigned char id[IDENT_LEN];
|
||||
int i, x;
|
||||
Wake(csn);
|
||||
udelay(1000);
|
||||
for (i = 0; i < IDENT_LEN; i++) {
|
||||
for (x = 1; x < TIMEOUT_PNP; x++) {
|
||||
if (STATUS & 1)
|
||||
break;
|
||||
udelay(1000);
|
||||
}
|
||||
id[i] = RESOURCEDATA;
|
||||
#ifdef EDEBUG
|
||||
printf(" 0x%hX ", id[i]);
|
||||
#endif
|
||||
}
|
||||
#ifdef EDEBUG
|
||||
printf("Got The status bit\n");
|
||||
#endif
|
||||
/*Set Logical Device Register active */
|
||||
LOGICALDEVICENUMBER;
|
||||
/* Specify the first logical device */
|
||||
WRITE_DATA(0);
|
||||
|
||||
|
||||
/* Apparently just activating the card is enough
|
||||
for Etherboot to detect it. Why bother with the
|
||||
following code. Left in place in case it is
|
||||
later required */
|
||||
/*==========================================*/
|
||||
/* set DMA */
|
||||
/* ADDRESS(0x74 + 0);
|
||||
WRITE_DATA(7); */
|
||||
|
||||
/*Set IRQ */
|
||||
/* udelay(1000);
|
||||
ADDRESS(0x70 + (0 << 1));
|
||||
WRITE_DATA(9);
|
||||
udelay(1000); */
|
||||
/*=============================================*/
|
||||
/*Activate */
|
||||
ACTIVATE;
|
||||
WRITE_DATA(1);
|
||||
udelay(250);
|
||||
/* Ask for access to the Wait for Key command - ConfigControl register */
|
||||
CONFIGCONTROL;
|
||||
/* Write the Wait for Key Command to the ConfigControl Register */
|
||||
WRITE_DATA(CONFIG_WAIT_FOR_KEY);
|
||||
/* As per doc. Two Write cycles of 0x00 required befor the Initialization key is sent */
|
||||
ADDRESS(0);
|
||||
ADDRESS(0);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void send_key(void)
|
||||
{
|
||||
int i;
|
||||
/* Ask for access to the Wait for Key command - ConfigControl register */
|
||||
CONFIGCONTROL;
|
||||
/* Write the Wait for Key Command to the ConfigControl Register */
|
||||
WRITE_DATA(CONFIG_WAIT_FOR_KEY);
|
||||
/* As per doc. Two Write cycles of 0x00 required befor the Initialization key is sent */
|
||||
ADDRESS(0);
|
||||
ADDRESS(0);
|
||||
/* 32 writes of the initiation key to the card */
|
||||
for (i = 0; i < INIT_LENGTH; i++)
|
||||
ADDRESS(initdata[i]);
|
||||
}
|
||||
|
||||
static void isapnp_peek(unsigned char *data, int bytes)
|
||||
{
|
||||
int i, j;
|
||||
unsigned char d = 0;
|
||||
|
||||
for (i = 1; i <= bytes; i++) {
|
||||
for (j = 0; j < 20; j++) {
|
||||
d = STATUS;
|
||||
if (d & 1)
|
||||
break;
|
||||
udelay(100);
|
||||
}
|
||||
if (!(d & 1)) {
|
||||
if (data != NULL)
|
||||
*data++ = 0xff;
|
||||
continue;
|
||||
}
|
||||
d = RESOURCEDATA; /* PRESDI */
|
||||
isapnp_checksum_value += d;
|
||||
if (data != NULL)
|
||||
*data++ = d;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute ISA PnP checksum for first eight bytes.
|
||||
*/
|
||||
static unsigned char isapnp_checksum(unsigned char *data)
|
||||
{
|
||||
int i, j;
|
||||
unsigned char checksum = 0x6a, bit, b;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
b = data[i];
|
||||
for (j = 0; j < 8; j++) {
|
||||
bit = 0;
|
||||
if (b & (1 << j))
|
||||
bit = 1;
|
||||
checksum =
|
||||
((((checksum ^ (checksum >> 1)) & 0x01) ^ bit)
|
||||
<< 7) | (checksum >> 1);
|
||||
}
|
||||
}
|
||||
return checksum;
|
||||
}
|
||||
static int isapnp_next_rdp(void)
|
||||
{
|
||||
int rdp = read_port;
|
||||
while (rdp <= 0x3ff) {
|
||||
/*
|
||||
* We cannot use NE2000 probe spaces for ISAPnP or we
|
||||
* will lock up machines.
|
||||
*/
|
||||
if ((rdp < 0x280 || rdp > 0x380)) {
|
||||
read_port = rdp;
|
||||
return 0;
|
||||
}
|
||||
rdp += READ_ADDR_STEP;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int isapnp_isolate_rdp_select(void)
|
||||
{
|
||||
send_key();
|
||||
/* Control: reset CSN and conditionally everything else too */
|
||||
CONFIGCONTROL;
|
||||
WRITE_DATA((CONFIG_RESET_CSN | CONFIG_WAIT_FOR_KEY));
|
||||
mdelay(2);
|
||||
|
||||
send_key();
|
||||
Wake(0);
|
||||
|
||||
if (isapnp_next_rdp() < 0) {
|
||||
/* Ask for access to the Wait for Key command - ConfigControl register */
|
||||
CONFIGCONTROL;
|
||||
/* Write the Wait for Key Command to the ConfigControl Register */
|
||||
WRITE_DATA(CONFIG_WAIT_FOR_KEY);
|
||||
return -1;
|
||||
}
|
||||
|
||||
SetRdPort(read_port);
|
||||
udelay(1000);
|
||||
SERIALISOLATION;
|
||||
udelay(1000);
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_ISA */
|
||||
529
src/core/main.c
Normal file
529
src/core/main.c
Normal file
@@ -0,0 +1,529 @@
|
||||
/**************************************************************************
|
||||
Etherboot - Network Bootstrap Program
|
||||
|
||||
Literature dealing with the network protocols:
|
||||
ARP - RFC826
|
||||
RARP - RFC903
|
||||
UDP - RFC768
|
||||
BOOTP - RFC951, RFC2132 (vendor extensions)
|
||||
DHCP - RFC2131, RFC2132 (options)
|
||||
TFTP - RFC1350, RFC2347 (options), RFC2348 (blocksize), RFC2349 (tsize)
|
||||
RPC - RFC1831, RFC1832 (XDR), RFC1833 (rpcbind/portmapper)
|
||||
NFS - RFC1094, RFC1813 (v3, useful for clarifications, not implemented)
|
||||
IGMP - RFC1112
|
||||
|
||||
**************************************************************************/
|
||||
|
||||
/* #define MDEBUG */
|
||||
|
||||
#include "etherboot.h"
|
||||
#include "dev.h"
|
||||
#include "nic.h"
|
||||
#include "disk.h"
|
||||
#include "http.h"
|
||||
#include "timer.h"
|
||||
#include "cpu.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifdef CONSOLE_BTEXT
|
||||
#include "btext.h"
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_FILO
|
||||
#include <lib.h>
|
||||
#endif
|
||||
|
||||
jmp_buf restart_etherboot;
|
||||
int url_port;
|
||||
|
||||
char as_main_program = 1;
|
||||
|
||||
#ifdef IMAGE_FREEBSD
|
||||
int freebsd_howto = 0;
|
||||
char freebsd_kernel_env[FREEBSD_KERNEL_ENV_SIZE];
|
||||
#endif
|
||||
|
||||
/* in_call(): the entry point to Etherboot. Generally called from
|
||||
* arch_in_call(), which in turn will have been invoked from
|
||||
* platform-specific assembly code.
|
||||
*/
|
||||
int in_call ( in_call_data_t *data, uint32_t opcode, va_list params ) {
|
||||
int old_as_main_program = as_main_program;
|
||||
int ret = 0;
|
||||
|
||||
/* Set flat to indicate that we are not running as the main
|
||||
* program (i.e. we are something like a PXE stack).
|
||||
*/
|
||||
as_main_program = 0;
|
||||
|
||||
/* NOTE: params will cease to be valid if we relocate, since
|
||||
* it represents a virtual address
|
||||
*/
|
||||
switch ( EB_OPCODE(opcode) ) {
|
||||
|
||||
case EB_OPCODE_CHECK:
|
||||
/* Installation check
|
||||
*/
|
||||
ret = EB_CHECK_RESULT;
|
||||
break;
|
||||
case EB_OPCODE_MAIN:
|
||||
/* Start up Etherboot as a standalone program. */
|
||||
as_main_program = 1;
|
||||
ret = main ( data, params );
|
||||
break;
|
||||
#ifdef PXE_EXPORT
|
||||
case EB_OPCODE_PXE:
|
||||
/* !PXE API call */
|
||||
ret = pxe_in_call ( data, params );
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
printf ( "Unsupported API \"%c%c\"\n",
|
||||
EB_OPCODE(opcode) >> 8, EB_OPCODE(opcode) & 0xff );
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
as_main_program = old_as_main_program;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline unsigned long ask_boot(unsigned *index)
|
||||
{
|
||||
unsigned long order = DEFAULT_BOOT_ORDER;
|
||||
*index = DEFAULT_BOOT_INDEX;
|
||||
#ifdef LINUXBIOS
|
||||
order = get_boot_order(order, index);
|
||||
#endif
|
||||
#if defined(ASK_BOOT)
|
||||
#if ASK_BOOT >= 0
|
||||
while(1) {
|
||||
int c = 0;
|
||||
printf(ASK_PROMPT);
|
||||
#if ASK_BOOT > 0
|
||||
{
|
||||
unsigned long time;
|
||||
for ( time = currticks() + ASK_BOOT*TICKS_PER_SEC;
|
||||
!c && !iskey(); ) {
|
||||
if (currticks() > time) c = ANS_DEFAULT;
|
||||
}
|
||||
}
|
||||
#endif /* ASK_BOOT > 0 */
|
||||
if ( !c ) c = getchar();
|
||||
if ((c >= 'a') && (c <= 'z')) c &= 0x5F;
|
||||
if ((c >= ' ') && (c <= '~')) putchar(c);
|
||||
putchar('\n');
|
||||
|
||||
switch(c) {
|
||||
default:
|
||||
/* Nothing useful try again */
|
||||
continue;
|
||||
case ANS_QUIT:
|
||||
order = BOOT_NOTHING;
|
||||
*index = 0;
|
||||
break;
|
||||
case ANS_DEFAULT:
|
||||
/* Preserve the default boot order */
|
||||
break;
|
||||
case ANS_NETWORK:
|
||||
order = (BOOT_NIC << (0*BOOT_BITS)) |
|
||||
(BOOT_NOTHING << (1*BOOT_BITS));
|
||||
*index = 0;
|
||||
break;
|
||||
case ANS_DISK:
|
||||
order = (BOOT_DISK << (0*BOOT_BITS)) |
|
||||
(BOOT_NOTHING << (1*BOOT_BITS));
|
||||
*index = 0;
|
||||
break;
|
||||
case ANS_FLOPPY:
|
||||
order = (BOOT_FLOPPY << (0*BOOT_BITS)) |
|
||||
(BOOT_NOTHING << (1*BOOT_BITS));
|
||||
*index = 0;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
putchar('\n');
|
||||
#endif /* ASK_BOOT >= 0 */
|
||||
#endif /* defined(ASK_BOOT) */
|
||||
return order;
|
||||
}
|
||||
|
||||
static inline void try_floppy_first(void)
|
||||
{
|
||||
#if (TRY_FLOPPY_FIRST > 0)
|
||||
int i;
|
||||
printf("Trying floppy");
|
||||
disk_init();
|
||||
for (i = TRY_FLOPPY_FIRST; i-- > 0; ) {
|
||||
putchar('.');
|
||||
if (pcbios_disk_read(0, 0, 0, 0, ((char *)FLOPPY_BOOT_LOCATION)) != 0x8000) {
|
||||
printf("using floppy\n");
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
printf("no floppy\n");
|
||||
#endif /* TRY_FLOPPY_FIRST */
|
||||
}
|
||||
|
||||
void console_init(void)
|
||||
{
|
||||
#ifdef CONSOLE_SERIAL
|
||||
(void)serial_init();
|
||||
#endif
|
||||
#ifdef CONSOLE_DIRECT_VGA
|
||||
video_init();
|
||||
#endif
|
||||
#ifdef CONSOLE_BTEXT
|
||||
map_boot_text();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void console_fini(void)
|
||||
{
|
||||
#ifdef CONSOLE_SERIAL
|
||||
(void)serial_fini();
|
||||
#endif
|
||||
}
|
||||
|
||||
static struct class_operations {
|
||||
struct dev *dev;
|
||||
int (*probe)(struct dev *dev);
|
||||
int (*load_configuration)(struct dev *dev);
|
||||
int (*load)(struct dev *dev);
|
||||
}
|
||||
operations[] = {
|
||||
{ &nic.dev, eth_probe, eth_load_configuration, eth_load },
|
||||
{ &disk.dev, disk_probe, disk_load_configuration, disk_load },
|
||||
{ &disk.dev, disk_probe, disk_load_configuration, disk_load },
|
||||
};
|
||||
|
||||
|
||||
static int main_loop(int state);
|
||||
static int exit_ok;
|
||||
static int exit_status;
|
||||
static int initialized;
|
||||
|
||||
/**************************************************************************
|
||||
MAIN - Kick off routine
|
||||
**************************************************************************/
|
||||
int main(in_call_data_t *data, va_list params)
|
||||
{
|
||||
char *p;
|
||||
int state;
|
||||
|
||||
for (p = _bss; p < _ebss; p++)
|
||||
*p = 0; /* Zero BSS */
|
||||
|
||||
console_init();
|
||||
arch_main(data,params);
|
||||
|
||||
#if 0
|
||||
#ifdef CONSOLE_BTEXT
|
||||
btext_init();
|
||||
map_boot_text();
|
||||
btext_clearscreen();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if ( rom.rom_segment ) {
|
||||
printf ( "ROM segment %#hx length %#hx reloc %#x\n",
|
||||
rom.rom_segment, rom.rom_length, _text );
|
||||
}
|
||||
|
||||
cpu_setup();
|
||||
setup_timers();
|
||||
gateA20_set();
|
||||
print_config();
|
||||
get_memsizes();
|
||||
cleanup();
|
||||
|
||||
#ifdef CONFIG_PCMCIA
|
||||
pcmcia_init_all();
|
||||
#endif
|
||||
|
||||
/* -1: timeout or ESC
|
||||
-2: error return from loader
|
||||
-3: finish the current run.
|
||||
0: retry booting bootp and tftp
|
||||
1: retry tftp with possibly modified bootp reply
|
||||
2: retry bootp and tftp
|
||||
3: retry probe bootp and tftp
|
||||
4: start with the next device and retry from there...
|
||||
255: exit Etherboot
|
||||
256: retry after relocation
|
||||
*/
|
||||
state = setjmp(restart_etherboot);
|
||||
exit_ok = 1;
|
||||
for(;state != 255;) {
|
||||
state = main_loop(state);
|
||||
}
|
||||
arch_on_exit(exit_status);
|
||||
#ifdef CONFIG_PCMCIA
|
||||
pcmcia_shutdown_all();
|
||||
#endif
|
||||
return exit_status;
|
||||
}
|
||||
|
||||
void exit(int status)
|
||||
{
|
||||
while(!exit_ok)
|
||||
;
|
||||
exit_status = status;
|
||||
longjmp(restart_etherboot, 255);
|
||||
}
|
||||
|
||||
static int main_loop(int state)
|
||||
{
|
||||
/* Splitting main into 2 pieces makes the semantics of
|
||||
* which variables are preserved across a longjmp clean
|
||||
* and predictable.
|
||||
*/
|
||||
static unsigned long order;
|
||||
static unsigned boot_index;
|
||||
static struct dev * dev = 0;
|
||||
static struct class_operations *ops;
|
||||
static void *heap_base;
|
||||
static int type;
|
||||
static int i;
|
||||
|
||||
if (!initialized) {
|
||||
initialized = 1;
|
||||
console_init();
|
||||
if (dev && (state >= 1) && (state <= 2)) {
|
||||
dev->how_probe = PROBE_AWAKE;
|
||||
dev->how_probe = ops->probe(dev);
|
||||
if (dev->how_probe == PROBE_FAILED) {
|
||||
state = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
switch(state) {
|
||||
case 0:
|
||||
{
|
||||
static int firsttime = 1;
|
||||
/* First time through */
|
||||
if (firsttime) {
|
||||
relocate();
|
||||
cleanup();
|
||||
console_init();
|
||||
init_heap();
|
||||
#ifdef CONSOLE_BTEXT
|
||||
//I need to all allot
|
||||
btext_init();
|
||||
map_boot_text();
|
||||
btext_clearscreen();
|
||||
#else
|
||||
#ifdef CONFIG_FILO
|
||||
pci_init();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
firsttime = 0;
|
||||
}
|
||||
#ifdef EXIT_IF_NO_OFFER
|
||||
else {
|
||||
cleanup();
|
||||
exit(0);
|
||||
}
|
||||
#endif
|
||||
heap_base = allot(0);
|
||||
i = -1;
|
||||
state = 4;
|
||||
dev = 0;
|
||||
|
||||
/* We just called setjmp ... */
|
||||
order = ask_boot(&boot_index);
|
||||
try_floppy_first();
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
cleanup();
|
||||
console_init();
|
||||
forget(heap_base);
|
||||
/* Find a dev entry to probe with */
|
||||
if (!dev) {
|
||||
int boot;
|
||||
int failsafe;
|
||||
|
||||
/* Advance to the next device type */
|
||||
i++;
|
||||
boot = (order >> (i * BOOT_BITS)) & BOOT_MASK;
|
||||
type = boot & BOOT_TYPE_MASK;
|
||||
failsafe = (boot & BOOT_FAILSAFE) != 0;
|
||||
if (i >= MAX_BOOT_ENTRIES) {
|
||||
type = BOOT_NOTHING;
|
||||
}
|
||||
if ((i == 0) && (type == BOOT_NOTHING)) {
|
||||
/* Return to caller */
|
||||
exit(0);
|
||||
}
|
||||
if (type >= BOOT_NOTHING) {
|
||||
interruptible_sleep(2);
|
||||
state = 0;
|
||||
break;
|
||||
}
|
||||
ops = &operations[type];
|
||||
dev = ops->dev;
|
||||
dev->how_probe = PROBE_FIRST;
|
||||
dev->type = type;
|
||||
dev->failsafe = failsafe;
|
||||
dev->type_index = 0;
|
||||
} else {
|
||||
/* Advance to the next device of the same type */
|
||||
dev->how_probe = PROBE_NEXT;
|
||||
}
|
||||
state = 3;
|
||||
break;
|
||||
case 3:
|
||||
state = -1;
|
||||
heap_base = allot(0);
|
||||
dev->how_probe = ops->probe(dev);
|
||||
if (dev->how_probe == PROBE_FAILED) {
|
||||
dev = 0;
|
||||
state = 4;
|
||||
} else if (boot_index && (i == 0) && (boot_index != (unsigned)dev->type_index)) {
|
||||
printf("Wrong index\n");
|
||||
state = 4;
|
||||
}
|
||||
else {
|
||||
state = 2;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
state = -1;
|
||||
if (ops->load_configuration(dev) >= 0) {
|
||||
state = 1;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
/* Any return from load is a failure */
|
||||
ops->load(dev);
|
||||
state = -1;
|
||||
break;
|
||||
case 256:
|
||||
state = 0;
|
||||
break;
|
||||
case -3:
|
||||
i = MAX_BOOT_ENTRIES;
|
||||
type = BOOT_NOTHING;
|
||||
/* fall through */
|
||||
default:
|
||||
printf("<abort>\n");
|
||||
state = 4;
|
||||
/* At the end goto state 0 */
|
||||
if ((type >= BOOT_NOTHING) || (i >= MAX_BOOT_ENTRIES)) {
|
||||
state = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
LOADKERNEL - Try to load kernel image
|
||||
**************************************************************************/
|
||||
struct proto {
|
||||
char *name;
|
||||
int (*load)(const char *name,
|
||||
int (*fnc)(unsigned char *, unsigned int, unsigned int, int));
|
||||
};
|
||||
static const struct proto protos[] = {
|
||||
#ifdef DOWNLOAD_PROTO_TFTM
|
||||
{ "x-tftm", url_tftm },
|
||||
#endif
|
||||
#ifdef DOWNLOAD_PROTO_SLAM
|
||||
{ "x-slam", url_slam },
|
||||
#endif
|
||||
#ifdef DOWNLOAD_PROTO_NFS
|
||||
{ "nfs", nfs },
|
||||
#endif
|
||||
#ifdef DOWNLOAD_PROTO_DISK
|
||||
{ "file", url_file },
|
||||
#endif
|
||||
#ifdef DOWNLOAD_PROTO_TFTP
|
||||
{ "tftp", tftp },
|
||||
#endif
|
||||
#ifdef DOWNLOAD_PROTO_HTTP
|
||||
{ "http", http },
|
||||
#endif
|
||||
};
|
||||
|
||||
int loadkernel(const char *fname)
|
||||
{
|
||||
static const struct proto * const last_proto =
|
||||
&protos[sizeof(protos)/sizeof(protos[0])];
|
||||
const struct proto *proto;
|
||||
in_addr ip;
|
||||
int len;
|
||||
const char *name;
|
||||
#ifdef DNS_RESOLVER
|
||||
const char *resolvt;
|
||||
#endif
|
||||
ip.s_addr = arptable[ARP_SERVER].ipaddr.s_addr;
|
||||
name = fname;
|
||||
url_port = -1;
|
||||
len = 0;
|
||||
while(fname[len] && fname[len] != ':') {
|
||||
len++;
|
||||
}
|
||||
for(proto = &protos[0]; proto < last_proto; proto++) {
|
||||
if (memcmp(name, proto->name, len) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((proto < last_proto) && (memcmp(fname + len, "://", 3) == 0)) {
|
||||
name += len + 3;
|
||||
if (name[0] != '/') {
|
||||
#ifdef DNS_RESOLVER
|
||||
resolvt = dns_resolver ( name );
|
||||
if ( NULL != resolvt ) {
|
||||
//printf ("Resolved host name [%s] to [%s]\n",
|
||||
// name, resolvt );
|
||||
inet_aton(resolvt, &ip);
|
||||
while ( ( '/' != name[0] ) && ( 0 != name[0]))
|
||||
++name;
|
||||
} else
|
||||
#endif /* DNS_RESOLVER */
|
||||
name += inet_aton(name, &ip);
|
||||
if (name[0] == ':') {
|
||||
name++;
|
||||
url_port = strtoul(name, &name, 10);
|
||||
}
|
||||
}
|
||||
if (name[0] == '/') {
|
||||
arptable[ARP_SERVER].ipaddr.s_addr = ip.s_addr;
|
||||
printf( "Loading %s ", fname );
|
||||
return proto->load(name + 1, load_block);
|
||||
}
|
||||
}
|
||||
printf("Loading %@:%s ", arptable[ARP_SERVER].ipaddr, fname);
|
||||
#ifdef DEFAULT_PROTO_NFS
|
||||
return nfs(fname, load_block);
|
||||
#else
|
||||
return tftp(fname, load_block);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
CLEANUP - shut down networking and console so that the OS may be called
|
||||
**************************************************************************/
|
||||
void cleanup(void)
|
||||
{
|
||||
#ifdef DOWNLOAD_PROTO_NFS
|
||||
nfs_umountall(ARP_SERVER);
|
||||
#endif
|
||||
/* Stop receiving packets */
|
||||
eth_disable();
|
||||
disk_disable();
|
||||
console_fini();
|
||||
initialized = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-basic-offset: 8
|
||||
* End:
|
||||
*/
|
||||
415
src/core/misc.c
Normal file
415
src/core/misc.c
Normal file
@@ -0,0 +1,415 @@
|
||||
/**************************************************************************
|
||||
MISC Support Routines
|
||||
**************************************************************************/
|
||||
|
||||
#include "etherboot.h"
|
||||
#ifdef CONSOLE_BTEXT
|
||||
#include <btext.h>
|
||||
#endif
|
||||
#ifdef CONSOLE_PC_KBD
|
||||
#include <pc_kbd.h>
|
||||
#endif
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
IPCHKSUM - Checksum IP Header
|
||||
**************************************************************************/
|
||||
uint16_t ipchksum(const void *data, unsigned long length)
|
||||
{
|
||||
unsigned long sum;
|
||||
unsigned long i;
|
||||
const uint8_t *ptr;
|
||||
|
||||
/* In the most straight forward way possible,
|
||||
* compute an ip style checksum.
|
||||
*/
|
||||
sum = 0;
|
||||
ptr = data;
|
||||
for(i = 0; i < length; i++) {
|
||||
unsigned long value;
|
||||
value = ptr[i];
|
||||
if (i & 1) {
|
||||
value <<= 8;
|
||||
}
|
||||
/* Add the new value */
|
||||
sum += value;
|
||||
/* Wrap around the carry */
|
||||
if (sum > 0xFFFF) {
|
||||
sum = (sum + (sum >> 16)) & 0xFFFF;
|
||||
}
|
||||
}
|
||||
return (~cpu_to_le16(sum)) & 0xFFFF;
|
||||
}
|
||||
|
||||
uint16_t add_ipchksums(unsigned long offset, uint16_t sum, uint16_t new)
|
||||
{
|
||||
unsigned long checksum;
|
||||
sum = ~sum & 0xFFFF;
|
||||
new = ~new & 0xFFFF;
|
||||
if (offset & 1) {
|
||||
/* byte swap the sum if it came from an odd offset
|
||||
* since the computation is endian independant this
|
||||
* works.
|
||||
*/
|
||||
new = bswap_16(new);
|
||||
}
|
||||
checksum = sum + new;
|
||||
if (checksum > 0xFFFF) {
|
||||
checksum -= 0xFFFF;
|
||||
}
|
||||
return (~checksum) & 0xFFFF;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
RANDOM - compute a random number between 0 and 2147483647L or 2147483562?
|
||||
**************************************************************************/
|
||||
int32_t random(void)
|
||||
{
|
||||
static int32_t seed = 0;
|
||||
int32_t q;
|
||||
if (!seed) /* Initialize linear congruential generator */
|
||||
seed = currticks() + *(int32_t *)&arptable[ARP_CLIENT].node
|
||||
+ ((int16_t *)arptable[ARP_CLIENT].node)[2];
|
||||
/* simplified version of the LCG given in Bruce Schneier's
|
||||
"Applied Cryptography" */
|
||||
q = seed/53668;
|
||||
if ((seed = 40014*(seed-53668*q) - 12211*q) < 0) seed += 2147483563L;
|
||||
return seed;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
POLL INTERRUPTIONS
|
||||
**************************************************************************/
|
||||
void poll_interruptions(void)
|
||||
{
|
||||
int ch;
|
||||
if ( ! as_main_program ) return;
|
||||
/* If an interruption has occured restart etherboot */
|
||||
if (iskey() && (ch = getchar(), (ch == K_ESC) || (ch == K_EOF) || (ch == K_INTR))) {
|
||||
int state = (ch != K_INTR)? -1 : -3;
|
||||
longjmp(restart_etherboot, state);
|
||||
}
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
SLEEP
|
||||
**************************************************************************/
|
||||
void sleep(int secs)
|
||||
{
|
||||
unsigned long tmo;
|
||||
|
||||
for (tmo = currticks()+secs*TICKS_PER_SEC; currticks() < tmo; ) {
|
||||
poll_interruptions();
|
||||
}
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
INTERRUPTIBLE SLEEP
|
||||
**************************************************************************/
|
||||
void interruptible_sleep(int secs)
|
||||
{
|
||||
printf("<sleep>\n");
|
||||
return sleep(secs);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
TWIDDLE
|
||||
**************************************************************************/
|
||||
void twiddle(void)
|
||||
{
|
||||
#ifdef BAR_PROGRESS
|
||||
static int count=0;
|
||||
static const char tiddles[]="-\\|/";
|
||||
static unsigned long lastticks = 0;
|
||||
unsigned long ticks;
|
||||
#endif
|
||||
if ( ! as_main_program ) return;
|
||||
#ifdef BAR_PROGRESS
|
||||
/* Limit the maximum rate at which characters are printed */
|
||||
ticks = currticks();
|
||||
if ((lastticks + (TICKS_PER_SEC/18)) > ticks)
|
||||
return;
|
||||
lastticks = ticks;
|
||||
|
||||
putchar(tiddles[(count++)&3]);
|
||||
putchar('\b');
|
||||
#else
|
||||
putchar('.');
|
||||
#endif /* BAR_PROGRESS */
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
STRCASECMP (not entirely correct, but this will do for our purposes)
|
||||
**************************************************************************/
|
||||
int strcasecmp(const char *a, const char *b)
|
||||
{
|
||||
while (*a && *b && (*a & ~0x20) == (*b & ~0x20)) {a++; b++; }
|
||||
return((*a & ~0x20) - (*b & ~0x20));
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
INET_ATON - Convert an ascii x.x.x.x to binary form
|
||||
**************************************************************************/
|
||||
int inet_aton(const char *start, in_addr *i)
|
||||
{
|
||||
const char *p = start;
|
||||
const char *digits_start;
|
||||
unsigned long ip = 0;
|
||||
unsigned long val;
|
||||
int j;
|
||||
for(j = 0; j <= 3; j++) {
|
||||
digits_start = p;
|
||||
val = strtoul(p, &p, 10);
|
||||
if ((p == digits_start) || (val > 255)) return 0;
|
||||
if ( ( j < 3 ) && ( *(p++) != '.' ) ) return 0;
|
||||
ip = (ip << 8) | val;
|
||||
}
|
||||
i->s_addr = htonl(ip);
|
||||
return p - start;
|
||||
}
|
||||
|
||||
|
||||
unsigned long strtoul(const char *p, const char **endp, int base)
|
||||
{
|
||||
unsigned long ret = 0;
|
||||
if (base != 10) return 0;
|
||||
while((*p >= '0') && (*p <= '9')) {
|
||||
ret = ret*10 + (*p - '0');
|
||||
p++;
|
||||
}
|
||||
if (endp)
|
||||
*endp = p;
|
||||
return(ret);
|
||||
|
||||
}
|
||||
|
||||
#define K_RDWR 0x60 /* keyboard data & cmds (read/write) */
|
||||
#define K_STATUS 0x64 /* keyboard status */
|
||||
#define K_CMD 0x64 /* keybd ctlr command (write-only) */
|
||||
|
||||
#define K_OBUF_FUL 0x01 /* output buffer full */
|
||||
#define K_IBUF_FUL 0x02 /* input buffer full */
|
||||
|
||||
#define KC_CMD_WIN 0xd0 /* read output port */
|
||||
#define KC_CMD_WOUT 0xd1 /* write output port */
|
||||
#define KB_SET_A20 0xdf /* enable A20,
|
||||
enable output buffer full interrupt
|
||||
enable data line
|
||||
disable clock line */
|
||||
#define KB_UNSET_A20 0xdd /* enable A20,
|
||||
enable output buffer full interrupt
|
||||
enable data line
|
||||
disable clock line */
|
||||
|
||||
enum { Disable_A20 = 0x2400, Enable_A20 = 0x2401, Query_A20_Status = 0x2402,
|
||||
Query_A20_Support = 0x2403 };
|
||||
|
||||
#if defined(PCBIOS) && !defined(IBM_L40)
|
||||
static void empty_8042(void)
|
||||
{
|
||||
unsigned long time;
|
||||
char st;
|
||||
|
||||
time = currticks() + TICKS_PER_SEC; /* max wait of 1 second */
|
||||
while ((((st = inb(K_CMD)) & K_OBUF_FUL) ||
|
||||
(st & K_IBUF_FUL)) &&
|
||||
currticks() < time)
|
||||
inb(K_RDWR);
|
||||
}
|
||||
#endif /* IBM_L40 */
|
||||
|
||||
#if defined(PCBIOS)
|
||||
/*
|
||||
* Gate A20 for high memory
|
||||
*/
|
||||
void gateA20_set(void)
|
||||
{
|
||||
#warning "gateA20_set should test to see if it is already set"
|
||||
if (int15(Enable_A20) == 0) {
|
||||
return;
|
||||
}
|
||||
#ifdef IBM_L40
|
||||
outb(0x2, 0x92);
|
||||
#else /* IBM_L40 */
|
||||
empty_8042();
|
||||
outb(KC_CMD_WOUT, K_CMD);
|
||||
empty_8042();
|
||||
outb(KB_SET_A20, K_RDWR);
|
||||
empty_8042();
|
||||
#endif /* IBM_L40 */
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
int last_putchar; // From filo
|
||||
|
||||
void
|
||||
putchar(int c)
|
||||
{
|
||||
c &= 0xff;
|
||||
last_putchar = c;
|
||||
|
||||
if (c == '\n')
|
||||
putchar('\r');
|
||||
#ifdef CONSOLE_FIRMWARE
|
||||
console_putc(c);
|
||||
#endif
|
||||
#ifdef CONSOLE_DIRECT_VGA
|
||||
vga_putc(c);
|
||||
#endif
|
||||
#ifdef CONSOLE_BTEXT
|
||||
btext_putc(c);
|
||||
#endif
|
||||
#ifdef CONSOLE_SERIAL
|
||||
serial_putc(c);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
GETCHAR - Read the next character from input device WITHOUT ECHO
|
||||
**************************************************************************/
|
||||
int getchar(void)
|
||||
{
|
||||
int c = 256;
|
||||
|
||||
do {
|
||||
#if defined(PCBIOS) && defined(POWERSAVE)
|
||||
/* Doze for a while (until the next interrupt). This works
|
||||
* fine, because the keyboard is interrupt-driven, and the
|
||||
* timer interrupt (approx. every 50msec) takes care of the
|
||||
* serial port, which is read by polling. This reduces the
|
||||
* power dissipation of a modern CPU considerably, and also
|
||||
* makes Etherboot waiting for user interaction waste a lot
|
||||
* less CPU time in a VMware session. */
|
||||
cpu_nap();
|
||||
#endif /* POWERSAVE */
|
||||
#ifdef CONSOLE_FIRMWARE
|
||||
if (console_ischar())
|
||||
c = console_getc();
|
||||
#endif
|
||||
#ifdef CONSOLE_SERIAL
|
||||
if (serial_ischar())
|
||||
c = serial_getc();
|
||||
#endif
|
||||
#ifdef CONSOLE_PC_KBD
|
||||
if (kbd_ischar())
|
||||
c = kbd_getc();
|
||||
#endif
|
||||
} while (c==256);
|
||||
if (c == '\r')
|
||||
c = '\n';
|
||||
return c;
|
||||
}
|
||||
|
||||
int iskey(void)
|
||||
{
|
||||
#ifdef CONSOLE_FIRMWARE
|
||||
if (console_ischar())
|
||||
return 1;
|
||||
#endif
|
||||
#ifdef CONSOLE_SERIAL
|
||||
if (serial_ischar())
|
||||
return 1;
|
||||
#endif
|
||||
#ifdef CONSOLE_PC_KBD
|
||||
if (kbd_ischar())
|
||||
return 1;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if DEBUG_UTILS
|
||||
|
||||
void pause ( void ) {
|
||||
printf ( "\nPress a key" );
|
||||
getchar();
|
||||
printf ( "\r \r" );
|
||||
}
|
||||
|
||||
void more ( void ) {
|
||||
printf ( "---more---" );
|
||||
getchar();
|
||||
printf ( "\r \r" );
|
||||
}
|
||||
|
||||
/* Produce a paged hex dump of the specified data and length */
|
||||
void hex_dump ( const char *data, const unsigned int len ) {
|
||||
unsigned int index;
|
||||
for ( index = 0; index < len; index++ ) {
|
||||
if ( ( index % 16 ) == 0 ) {
|
||||
printf ( "\n" );
|
||||
}
|
||||
if ( ( index % 368 ) == 352 ) {
|
||||
more();
|
||||
}
|
||||
if ( ( index % 16 ) == 0 ) {
|
||||
printf ( "%X [%X] : %hX :", data + index,
|
||||
virt_to_phys ( data + index ), index );
|
||||
}
|
||||
printf ( " %hhX", data[index] );
|
||||
}
|
||||
printf ( "\n" );
|
||||
}
|
||||
|
||||
#define GUARD_SYMBOL ( ( 'M' << 24 ) | ( 'I' << 16 ) | ( 'N' << 8 ) | 'E' )
|
||||
/* Fill a region with guard markers. We use a 4-byte pattern to make
|
||||
* it less likely that check_region will find spurious 1-byte regions
|
||||
* of non-corruption.
|
||||
*/
|
||||
void guard_region ( void *region, size_t len ) {
|
||||
uint32_t offset = 0;
|
||||
|
||||
len &= ~0x03;
|
||||
for ( offset = 0; offset < len ; offset += 4 ) {
|
||||
*((uint32_t *)(region + offset)) = GUARD_SYMBOL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check a region that has been guarded with guard_region() for
|
||||
* corruption.
|
||||
*/
|
||||
int check_region ( void *region, size_t len ) {
|
||||
uint8_t corrupted = 0;
|
||||
uint8_t in_corruption = 0;
|
||||
uint32_t offset = 0;
|
||||
uint32_t test = 0;
|
||||
|
||||
len &= ~0x03;
|
||||
for ( offset = 0; offset < len ; offset += 4 ) {
|
||||
test = *((uint32_t *)(region + offset)) = GUARD_SYMBOL;
|
||||
if ( ( in_corruption == 0 ) &&
|
||||
( test != GUARD_SYMBOL ) ) {
|
||||
/* Start of corruption */
|
||||
if ( corrupted == 0 ) {
|
||||
corrupted = 1;
|
||||
printf ( "Region %#x-%#x (physical %#x-%#x) "
|
||||
"corrupted\n",
|
||||
region, region + len,
|
||||
virt_to_phys ( region ),
|
||||
virt_to_phys ( region + len ) );
|
||||
}
|
||||
in_corruption = 1;
|
||||
printf ( "--- offset %#x ", offset );
|
||||
} else if ( ( in_corruption != 0 ) &&
|
||||
( test == GUARD_SYMBOL ) ) {
|
||||
/* End of corruption */
|
||||
in_corruption = 0;
|
||||
printf ( "to offset %#x", offset );
|
||||
}
|
||||
|
||||
}
|
||||
if ( in_corruption != 0 ) {
|
||||
printf ( "to offset %#x (end of region)\n", len-1 );
|
||||
}
|
||||
return corrupted;
|
||||
}
|
||||
|
||||
#endif /* DEBUG_UTILS */
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-basic-offset: 8
|
||||
* End:
|
||||
*/
|
||||
610
src/core/nfs.c
Normal file
610
src/core/nfs.c
Normal file
@@ -0,0 +1,610 @@
|
||||
#ifdef DOWNLOAD_PROTO_NFS
|
||||
|
||||
#include "etherboot.h"
|
||||
#include "nic.h"
|
||||
|
||||
/* NOTE: the NFS code is heavily inspired by the NetBSD netboot code (read:
|
||||
* large portions are copied verbatim) as distributed in OSKit 0.97. A few
|
||||
* changes were necessary to adapt the code to Etherboot and to fix several
|
||||
* inconsistencies. Also the RPC message preparation is done "by hand" to
|
||||
* avoid adding netsprintf() which I find hard to understand and use. */
|
||||
|
||||
/* NOTE 2: Etherboot does not care about things beyond the kernel image, so
|
||||
* it loads the kernel image off the boot server (ARP_SERVER) and does not
|
||||
* access the client root disk (root-path in dhcpd.conf), which would use
|
||||
* ARP_ROOTSERVER. The root disk is something the operating system we are
|
||||
* about to load needs to use. This is different from the OSKit 0.97 logic. */
|
||||
|
||||
/* NOTE 3: Symlink handling introduced by Anselm M Hoffmeister, 2003-July-14
|
||||
* If a symlink is encountered, it is followed as far as possible (recursion
|
||||
* possible, maximum 16 steps). There is no clearing of ".."'s inside the
|
||||
* path, so please DON'T DO THAT. thx. */
|
||||
|
||||
#define START_OPORT 700 /* mountd usually insists on secure ports */
|
||||
#define OPORT_SWEEP 200 /* make sure we don't leave secure range */
|
||||
|
||||
static int oport = START_OPORT;
|
||||
static int mount_port = -1;
|
||||
static int nfs_port = -1;
|
||||
static int fs_mounted = 0;
|
||||
static unsigned long rpc_id;
|
||||
|
||||
/**************************************************************************
|
||||
RPC_INIT - set up the ID counter to something fairly random
|
||||
**************************************************************************/
|
||||
void rpc_init(void)
|
||||
{
|
||||
unsigned long t;
|
||||
|
||||
t = currticks();
|
||||
rpc_id = t ^ (t << 8) ^ (t << 16);
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
RPC_PRINTERROR - Print a low level RPC error message
|
||||
**************************************************************************/
|
||||
static void rpc_printerror(struct rpc_t *rpc)
|
||||
{
|
||||
if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
|
||||
rpc->u.reply.astatus) {
|
||||
/* rpc_printerror() is called for any RPC related error,
|
||||
* suppress output if no low level RPC error happened. */
|
||||
printf("RPC error: (%d,%d,%d)\n", ntohl(rpc->u.reply.rstatus),
|
||||
ntohl(rpc->u.reply.verifier),
|
||||
ntohl(rpc->u.reply.astatus));
|
||||
}
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
AWAIT_RPC - Wait for an rpc packet
|
||||
**************************************************************************/
|
||||
static int await_rpc(int ival, void *ptr,
|
||||
unsigned short ptype, struct iphdr *ip, struct udphdr *udp)
|
||||
{
|
||||
struct rpc_t *rpc;
|
||||
if (!udp)
|
||||
return 0;
|
||||
if (arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr)
|
||||
return 0;
|
||||
if (ntohs(udp->dest) != ival)
|
||||
return 0;
|
||||
if (nic.packetlen < ETH_HLEN + sizeof(struct iphdr) + sizeof(struct udphdr) + 8)
|
||||
return 0;
|
||||
rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
|
||||
if (*(unsigned long *)ptr != ntohl(rpc->u.reply.id))
|
||||
return 0;
|
||||
if (MSG_REPLY != ntohl(rpc->u.reply.type))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
RPC_LOOKUP - Lookup RPC Port numbers
|
||||
**************************************************************************/
|
||||
static int rpc_lookup(int addr, int prog, int ver, int sport)
|
||||
{
|
||||
struct rpc_t buf, *rpc;
|
||||
unsigned long id;
|
||||
int retries;
|
||||
long *p;
|
||||
|
||||
id = rpc_id++;
|
||||
buf.u.call.id = htonl(id);
|
||||
buf.u.call.type = htonl(MSG_CALL);
|
||||
buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
|
||||
buf.u.call.prog = htonl(PROG_PORTMAP);
|
||||
buf.u.call.vers = htonl(2); /* portmapper is version 2 */
|
||||
buf.u.call.proc = htonl(PORTMAP_GETPORT);
|
||||
p = (long *)buf.u.call.data;
|
||||
*p++ = 0; *p++ = 0; /* auth credential */
|
||||
*p++ = 0; *p++ = 0; /* auth verifier */
|
||||
*p++ = htonl(prog);
|
||||
*p++ = htonl(ver);
|
||||
*p++ = htonl(IP_UDP);
|
||||
*p++ = 0;
|
||||
for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
|
||||
long timeout;
|
||||
udp_transmit(arptable[addr].ipaddr.s_addr, sport, SUNRPC_PORT,
|
||||
(char *)p - (char *)&buf, &buf);
|
||||
timeout = rfc2131_sleep_interval(TIMEOUT, retries);
|
||||
if (await_reply(await_rpc, sport, &id, timeout)) {
|
||||
rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
|
||||
if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
|
||||
rpc->u.reply.astatus) {
|
||||
rpc_printerror(rpc);
|
||||
return -1;
|
||||
} else {
|
||||
return ntohl(rpc->u.reply.data[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
RPC_ADD_CREDENTIALS - Add RPC authentication/verifier entries
|
||||
**************************************************************************/
|
||||
static long *rpc_add_credentials(long *p)
|
||||
{
|
||||
int hl;
|
||||
|
||||
/* Here's the executive summary on authentication requirements of the
|
||||
* various NFS server implementations: Linux accepts both AUTH_NONE
|
||||
* and AUTH_UNIX authentication (also accepts an empty hostname field
|
||||
* in the AUTH_UNIX scheme). *BSD refuses AUTH_NONE, but accepts
|
||||
* AUTH_UNIX (also accepts an empty hostname field in the AUTH_UNIX
|
||||
* scheme). To be safe, use AUTH_UNIX and pass the hostname if we have
|
||||
* it (if the BOOTP/DHCP reply didn't give one, just use an empty
|
||||
* hostname). */
|
||||
|
||||
hl = (hostnamelen + 3) & ~3;
|
||||
|
||||
/* Provide an AUTH_UNIX credential. */
|
||||
*p++ = htonl(1); /* AUTH_UNIX */
|
||||
*p++ = htonl(hl+20); /* auth length */
|
||||
*p++ = htonl(0); /* stamp */
|
||||
*p++ = htonl(hostnamelen); /* hostname string */
|
||||
if (hostnamelen & 3) {
|
||||
*(p + hostnamelen / 4) = 0; /* add zero padding */
|
||||
}
|
||||
memcpy(p, hostname, hostnamelen);
|
||||
p += hl / 4;
|
||||
*p++ = 0; /* uid */
|
||||
*p++ = 0; /* gid */
|
||||
*p++ = 0; /* auxiliary gid list */
|
||||
|
||||
/* Provide an AUTH_NONE verifier. */
|
||||
*p++ = 0; /* AUTH_NONE */
|
||||
*p++ = 0; /* auth length */
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
NFS_PRINTERROR - Print a NFS error message
|
||||
**************************************************************************/
|
||||
static void nfs_printerror(int err)
|
||||
{
|
||||
switch (-err) {
|
||||
case NFSERR_PERM:
|
||||
printf("Not owner\n");
|
||||
break;
|
||||
case NFSERR_NOENT:
|
||||
printf("No such file or directory\n");
|
||||
break;
|
||||
case NFSERR_ACCES:
|
||||
printf("Permission denied\n");
|
||||
break;
|
||||
case NFSERR_ISDIR:
|
||||
printf("Directory given where filename expected\n");
|
||||
break;
|
||||
case NFSERR_INVAL:
|
||||
printf("Invalid filehandle\n");
|
||||
break; // INVAL is not defined in NFSv2, some NFS-servers
|
||||
// seem to use it in answers to v2 nevertheless.
|
||||
case 9998:
|
||||
printf("low-level RPC failure (parameter decoding problem?)\n");
|
||||
break;
|
||||
case 9999:
|
||||
printf("low-level RPC failure (authentication problem?)\n");
|
||||
break;
|
||||
default:
|
||||
printf("Unknown NFS error %d\n", -err);
|
||||
}
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
NFS_MOUNT - Mount an NFS Filesystem
|
||||
**************************************************************************/
|
||||
static int nfs_mount(int server, int port, char *path, char *fh, int sport)
|
||||
{
|
||||
struct rpc_t buf, *rpc;
|
||||
unsigned long id;
|
||||
int retries;
|
||||
long *p;
|
||||
int pathlen = strlen(path);
|
||||
|
||||
id = rpc_id++;
|
||||
buf.u.call.id = htonl(id);
|
||||
buf.u.call.type = htonl(MSG_CALL);
|
||||
buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
|
||||
buf.u.call.prog = htonl(PROG_MOUNT);
|
||||
buf.u.call.vers = htonl(1); /* mountd is version 1 */
|
||||
buf.u.call.proc = htonl(MOUNT_ADDENTRY);
|
||||
p = rpc_add_credentials((long *)buf.u.call.data);
|
||||
*p++ = htonl(pathlen);
|
||||
if (pathlen & 3) {
|
||||
*(p + pathlen / 4) = 0; /* add zero padding */
|
||||
}
|
||||
memcpy(p, path, pathlen);
|
||||
p += (pathlen + 3) / 4;
|
||||
for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
|
||||
long timeout;
|
||||
udp_transmit(arptable[server].ipaddr.s_addr, sport, port,
|
||||
(char *)p - (char *)&buf, &buf);
|
||||
timeout = rfc2131_sleep_interval(TIMEOUT, retries);
|
||||
if (await_reply(await_rpc, sport, &id, timeout)) {
|
||||
rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
|
||||
if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
|
||||
rpc->u.reply.astatus || rpc->u.reply.data[0]) {
|
||||
rpc_printerror(rpc);
|
||||
if (rpc->u.reply.rstatus) {
|
||||
/* RPC failed, no verifier, data[0] */
|
||||
return -9999;
|
||||
}
|
||||
if (rpc->u.reply.astatus) {
|
||||
/* RPC couldn't decode parameters */
|
||||
return -9998;
|
||||
}
|
||||
return -ntohl(rpc->u.reply.data[0]);
|
||||
} else {
|
||||
fs_mounted = 1;
|
||||
memcpy(fh, rpc->u.reply.data + 1, NFS_FHSIZE);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
NFS_UMOUNTALL - Unmount all our NFS Filesystems on the Server
|
||||
**************************************************************************/
|
||||
void nfs_umountall(int server)
|
||||
{
|
||||
struct rpc_t buf, *rpc;
|
||||
unsigned long id;
|
||||
int retries;
|
||||
long *p;
|
||||
|
||||
if (!arptable[server].ipaddr.s_addr) {
|
||||
/* Haven't sent a single UDP packet to this server */
|
||||
return;
|
||||
}
|
||||
if ((mount_port == -1) || (!fs_mounted)) {
|
||||
/* Nothing mounted, nothing to umount */
|
||||
return;
|
||||
}
|
||||
id = rpc_id++;
|
||||
buf.u.call.id = htonl(id);
|
||||
buf.u.call.type = htonl(MSG_CALL);
|
||||
buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
|
||||
buf.u.call.prog = htonl(PROG_MOUNT);
|
||||
buf.u.call.vers = htonl(1); /* mountd is version 1 */
|
||||
buf.u.call.proc = htonl(MOUNT_UMOUNTALL);
|
||||
p = rpc_add_credentials((long *)buf.u.call.data);
|
||||
for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
|
||||
long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
|
||||
udp_transmit(arptable[server].ipaddr.s_addr, oport, mount_port,
|
||||
(char *)p - (char *)&buf, &buf);
|
||||
if (await_reply(await_rpc, oport, &id, timeout)) {
|
||||
rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
|
||||
if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
|
||||
rpc->u.reply.astatus) {
|
||||
rpc_printerror(rpc);
|
||||
}
|
||||
fs_mounted = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
/***************************************************************************
|
||||
* NFS_READLINK (AH 2003-07-14)
|
||||
* This procedure is called when read of the first block fails -
|
||||
* this probably happens when it's a directory or a symlink
|
||||
* In case of successful readlink(), the dirname is manipulated,
|
||||
* so that inside the nfs() function a recursion can be done.
|
||||
**************************************************************************/
|
||||
static int nfs_readlink(int server, int port, char *fh, char *path, char *nfh,
|
||||
int sport)
|
||||
{
|
||||
struct rpc_t buf, *rpc;
|
||||
unsigned long id;
|
||||
long *p;
|
||||
int retries;
|
||||
int pathlen = strlen(path);
|
||||
|
||||
id = rpc_id++;
|
||||
buf.u.call.id = htonl(id);
|
||||
buf.u.call.type = htonl(MSG_CALL);
|
||||
buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
|
||||
buf.u.call.prog = htonl(PROG_NFS);
|
||||
buf.u.call.vers = htonl(2); /* nfsd is version 2 */
|
||||
buf.u.call.proc = htonl(NFS_READLINK);
|
||||
p = rpc_add_credentials((long *)buf.u.call.data);
|
||||
memcpy(p, nfh, NFS_FHSIZE);
|
||||
p += (NFS_FHSIZE / 4);
|
||||
for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
|
||||
long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
|
||||
udp_transmit(arptable[server].ipaddr.s_addr, sport, port,
|
||||
(char *)p - (char *)&buf, &buf);
|
||||
if (await_reply(await_rpc, sport, &id, timeout)) {
|
||||
rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
|
||||
if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
|
||||
rpc->u.reply.astatus || rpc->u.reply.data[0]) {
|
||||
rpc_printerror(rpc);
|
||||
if (rpc->u.reply.rstatus) {
|
||||
/* RPC failed, no verifier, data[0] */
|
||||
return -9999;
|
||||
}
|
||||
if (rpc->u.reply.astatus) {
|
||||
/* RPC couldn't decode parameters */
|
||||
return -9998;
|
||||
}
|
||||
return -ntohl(rpc->u.reply.data[0]);
|
||||
} else {
|
||||
// It *is* a link.
|
||||
// If it's a relative link, append everything to dirname, filename TOO!
|
||||
retries = strlen ( (char *)(&(rpc->u.reply.data[2]) ));
|
||||
if ( *((char *)(&(rpc->u.reply.data[2]))) != '/' ) {
|
||||
path[pathlen++] = '/';
|
||||
while ( ( retries + pathlen ) > 298 ) {
|
||||
retries--;
|
||||
}
|
||||
if ( retries > 0 ) {
|
||||
memcpy(path + pathlen, &(rpc->u.reply.data[2]), retries + 1);
|
||||
} else { retries = 0; }
|
||||
path[pathlen + retries] = 0;
|
||||
} else {
|
||||
// Else make it the only path.
|
||||
if ( retries > 298 ) { retries = 298; }
|
||||
memcpy ( path, &(rpc->u.reply.data[2]), retries + 1 );
|
||||
path[retries] = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
/**************************************************************************
|
||||
NFS_LOOKUP - Lookup Pathname
|
||||
**************************************************************************/
|
||||
static int nfs_lookup(int server, int port, char *fh, char *path, char *nfh,
|
||||
int sport)
|
||||
{
|
||||
struct rpc_t buf, *rpc;
|
||||
unsigned long id;
|
||||
long *p;
|
||||
int retries;
|
||||
int pathlen = strlen(path);
|
||||
|
||||
id = rpc_id++;
|
||||
buf.u.call.id = htonl(id);
|
||||
buf.u.call.type = htonl(MSG_CALL);
|
||||
buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
|
||||
buf.u.call.prog = htonl(PROG_NFS);
|
||||
buf.u.call.vers = htonl(2); /* nfsd is version 2 */
|
||||
buf.u.call.proc = htonl(NFS_LOOKUP);
|
||||
p = rpc_add_credentials((long *)buf.u.call.data);
|
||||
memcpy(p, fh, NFS_FHSIZE);
|
||||
p += (NFS_FHSIZE / 4);
|
||||
*p++ = htonl(pathlen);
|
||||
if (pathlen & 3) {
|
||||
*(p + pathlen / 4) = 0; /* add zero padding */
|
||||
}
|
||||
memcpy(p, path, pathlen);
|
||||
p += (pathlen + 3) / 4;
|
||||
for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
|
||||
long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
|
||||
udp_transmit(arptable[server].ipaddr.s_addr, sport, port,
|
||||
(char *)p - (char *)&buf, &buf);
|
||||
if (await_reply(await_rpc, sport, &id, timeout)) {
|
||||
rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
|
||||
if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
|
||||
rpc->u.reply.astatus || rpc->u.reply.data[0]) {
|
||||
rpc_printerror(rpc);
|
||||
if (rpc->u.reply.rstatus) {
|
||||
/* RPC failed, no verifier, data[0] */
|
||||
return -9999;
|
||||
}
|
||||
if (rpc->u.reply.astatus) {
|
||||
/* RPC couldn't decode parameters */
|
||||
return -9998;
|
||||
}
|
||||
return -ntohl(rpc->u.reply.data[0]);
|
||||
} else {
|
||||
memcpy(nfh, rpc->u.reply.data + 1, NFS_FHSIZE);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
NFS_READ - Read File on NFS Server
|
||||
**************************************************************************/
|
||||
static int nfs_read(int server, int port, char *fh, int offset, int len,
|
||||
int sport)
|
||||
{
|
||||
struct rpc_t buf, *rpc;
|
||||
unsigned long id;
|
||||
int retries;
|
||||
long *p;
|
||||
|
||||
static int tokens=0;
|
||||
/*
|
||||
* Try to implement something similar to a window protocol in
|
||||
* terms of response to losses. On successful receive, increment
|
||||
* the number of tokens by 1 (cap at 256). On failure, halve it.
|
||||
* When the number of tokens is >= 2, use a very short timeout.
|
||||
*/
|
||||
|
||||
id = rpc_id++;
|
||||
buf.u.call.id = htonl(id);
|
||||
buf.u.call.type = htonl(MSG_CALL);
|
||||
buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */
|
||||
buf.u.call.prog = htonl(PROG_NFS);
|
||||
buf.u.call.vers = htonl(2); /* nfsd is version 2 */
|
||||
buf.u.call.proc = htonl(NFS_READ);
|
||||
p = rpc_add_credentials((long *)buf.u.call.data);
|
||||
memcpy(p, fh, NFS_FHSIZE);
|
||||
p += NFS_FHSIZE / 4;
|
||||
*p++ = htonl(offset);
|
||||
*p++ = htonl(len);
|
||||
*p++ = 0; /* unused parameter */
|
||||
for (retries = 0; retries < MAX_RPC_RETRIES; retries++) {
|
||||
long timeout = rfc2131_sleep_interval(TIMEOUT, retries);
|
||||
if (tokens >= 2)
|
||||
timeout = TICKS_PER_SEC/2;
|
||||
|
||||
udp_transmit(arptable[server].ipaddr.s_addr, sport, port,
|
||||
(char *)p - (char *)&buf, &buf);
|
||||
if (await_reply(await_rpc, sport, &id, timeout)) {
|
||||
if (tokens < 256)
|
||||
tokens++;
|
||||
rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
|
||||
if (rpc->u.reply.rstatus || rpc->u.reply.verifier ||
|
||||
rpc->u.reply.astatus || rpc->u.reply.data[0]) {
|
||||
rpc_printerror(rpc);
|
||||
if (rpc->u.reply.rstatus) {
|
||||
/* RPC failed, no verifier, data[0] */
|
||||
return -9999;
|
||||
}
|
||||
if (rpc->u.reply.astatus) {
|
||||
/* RPC couldn't decode parameters */
|
||||
return -9998;
|
||||
}
|
||||
return -ntohl(rpc->u.reply.data[0]);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else
|
||||
tokens >>= 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
NFS - Download extended BOOTP data, or kernel image from NFS server
|
||||
**************************************************************************/
|
||||
int nfs(const char *name, int (*fnc)(unsigned char *, unsigned int, unsigned int, int))
|
||||
{
|
||||
static int recursion = 0;
|
||||
int sport;
|
||||
int err, namelen = strlen(name);
|
||||
char dirname[300], *fname;
|
||||
char dirfh[NFS_FHSIZE]; /* file handle of directory */
|
||||
char filefh[NFS_FHSIZE]; /* file handle of kernel image */
|
||||
unsigned int block;
|
||||
int rlen, size, offs, len;
|
||||
struct rpc_t *rpc;
|
||||
|
||||
rx_qdrain();
|
||||
|
||||
sport = oport++;
|
||||
if (oport > START_OPORT+OPORT_SWEEP) {
|
||||
oport = START_OPORT;
|
||||
}
|
||||
if ( name != dirname ) {
|
||||
memcpy(dirname, name, namelen + 1);
|
||||
}
|
||||
recursion = 0;
|
||||
nfssymlink:
|
||||
if ( recursion > NFS_MAXLINKDEPTH ) {
|
||||
printf ( "\nRecursion: More than %d symlinks followed. Abort.\n", NFS_MAXLINKDEPTH );
|
||||
return 0;
|
||||
}
|
||||
recursion++;
|
||||
fname = dirname + (namelen - 1);
|
||||
while (fname >= dirname) {
|
||||
if (*fname == '/') {
|
||||
*fname = '\0';
|
||||
fname++;
|
||||
break;
|
||||
}
|
||||
fname--;
|
||||
}
|
||||
if (fname < dirname) {
|
||||
printf("can't parse file name %s\n", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mount_port == -1) {
|
||||
mount_port = rpc_lookup(ARP_SERVER, PROG_MOUNT, 1, sport);
|
||||
}
|
||||
if (nfs_port == -1) {
|
||||
nfs_port = rpc_lookup(ARP_SERVER, PROG_NFS, 2, sport);
|
||||
}
|
||||
if (nfs_port == -1 || mount_port == -1) {
|
||||
printf("can't get nfs/mount ports from portmapper\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
err = nfs_mount(ARP_SERVER, mount_port, dirname, dirfh, sport);
|
||||
if (err) {
|
||||
printf("mounting %s: ", dirname);
|
||||
nfs_printerror(err);
|
||||
/* just to be sure... */
|
||||
nfs_umountall(ARP_SERVER);
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = nfs_lookup(ARP_SERVER, nfs_port, dirfh, fname, filefh, sport);
|
||||
if (err) {
|
||||
printf("looking up %s: ", fname);
|
||||
nfs_printerror(err);
|
||||
nfs_umountall(ARP_SERVER);
|
||||
return 0;
|
||||
}
|
||||
|
||||
offs = 0;
|
||||
block = 1; /* blocks are numbered starting from 1 */
|
||||
size = -1; /* will be set properly with the first reply */
|
||||
len = NFS_READ_SIZE; /* first request is always full size */
|
||||
do {
|
||||
err = nfs_read(ARP_SERVER, nfs_port, filefh, offs, len, sport);
|
||||
if ((err <= -NFSERR_ISDIR)&&(err >= -NFSERR_INVAL) && (offs == 0)) {
|
||||
// An error occured. NFS servers tend to sending
|
||||
// errors 21 / 22 when symlink instead of real file
|
||||
// is requested. So check if it's a symlink!
|
||||
block = nfs_readlink(ARP_SERVER, nfs_port, dirfh, dirname,
|
||||
filefh, sport);
|
||||
if ( 0 == block ) {
|
||||
printf("\nLoading symlink:%s ..",dirname);
|
||||
goto nfssymlink;
|
||||
}
|
||||
nfs_printerror(err);
|
||||
nfs_umountall(ARP_SERVER);
|
||||
return 0;
|
||||
}
|
||||
if (err) {
|
||||
printf("reading at offset %d: ", offs);
|
||||
nfs_printerror(err);
|
||||
nfs_umountall(ARP_SERVER);
|
||||
return 0;
|
||||
}
|
||||
|
||||
rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
|
||||
|
||||
/* size must be found out early to allow EOF detection */
|
||||
if (size == -1) {
|
||||
size = ntohl(rpc->u.reply.data[6]);
|
||||
}
|
||||
rlen = ntohl(rpc->u.reply.data[18]);
|
||||
if (rlen > len) {
|
||||
rlen = len; /* shouldn't happen... */
|
||||
}
|
||||
|
||||
err = fnc((char *)&rpc->u.reply.data[19], block, rlen,
|
||||
(offs+rlen == size));
|
||||
if (err <= 0) {
|
||||
nfs_umountall(ARP_SERVER);
|
||||
return err;
|
||||
}
|
||||
|
||||
block++;
|
||||
offs += rlen;
|
||||
/* last request is done with matching requested read size */
|
||||
if (size-offs < NFS_READ_SIZE) {
|
||||
len = size-offs;
|
||||
}
|
||||
} while (len != 0);
|
||||
/* len == 0 means that all the file has been read */
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif /* DOWNLOAD_PROTO_NFS */
|
||||
1778
src/core/nic.c
Normal file
1778
src/core/nic.c
Normal file
File diff suppressed because it is too large
Load Diff
365
src/core/osloader.c
Normal file
365
src/core/osloader.c
Normal file
@@ -0,0 +1,365 @@
|
||||
/**************************************************************************
|
||||
OS loader
|
||||
|
||||
Author: Markus Gutschke (gutschk@math.uni-muenster.de)
|
||||
Date: Sep/95
|
||||
Modifications: Ken Yap (for Etherboot/16)
|
||||
Doug Ambrisko (ELF and a.out support)
|
||||
Klaus Espenlaub (rewrote ELF and a.out (did it really work before?) support,
|
||||
added ELF Multiboot images). Someone should merge the ELF and a.out
|
||||
loaders, as most of the code is now identical. Maybe even NBI could be
|
||||
rewritten and merged into the generic loading framework. This should
|
||||
save quite a few bytes of code if you have selected more than one format.
|
||||
Ken Yap (Jan 2001)
|
||||
Added support for linear entry addresses in tagged images,
|
||||
which allows a more efficient protected mode call instead of
|
||||
going to real mode and back. Also means entry addresses > 1 MB can
|
||||
be called. Conditional on the LINEAR_EXEC_ADDR bit.
|
||||
Added support for Etherboot extension calls. Conditional on the
|
||||
TAGGED_PROGRAM_RETURNS bit. Implies LINEAR_EXEC_ADDR.
|
||||
Added support for non-MULTIBOOT ELF which also supports Etherboot
|
||||
extension calls. Conditional on the ELF_PROGRAM_RETURNS bit.
|
||||
|
||||
**************************************************************************/
|
||||
|
||||
/*
|
||||
* 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 "etherboot.h"
|
||||
|
||||
struct os_entry_regs os_regs;
|
||||
|
||||
static struct ebinfo loaderinfo = {
|
||||
VERSION_MAJOR, VERSION_MINOR,
|
||||
0
|
||||
};
|
||||
|
||||
#define LOAD_DEBUG 0
|
||||
|
||||
static int prep_segment(unsigned long start, unsigned long mid, unsigned long end,
|
||||
unsigned long istart, unsigned long iend);
|
||||
static unsigned long find_segment(unsigned long size, unsigned long align);
|
||||
static sector_t dead_download ( unsigned char *data, unsigned int len, int eof);
|
||||
static void done(int do_cleanup);
|
||||
|
||||
#if defined(IMAGE_FREEBSD) && defined(ELF_IMAGE)
|
||||
static void elf_freebsd_probe(void);
|
||||
static void elf_freebsd_fixup_segment(void);
|
||||
static void elf_freebsd_find_segment_end(void);
|
||||
static int elf_freebsd_debug_loader(unsigned int offset);
|
||||
static void elf_freebsd_boot(unsigned long entry);
|
||||
#else
|
||||
#define elf_freebsd_probe() do {} while(0)
|
||||
#define elf_freebsd_fixup_segment() do {} while(0)
|
||||
#define elf_freebsd_find_segment_end() do {} while(0)
|
||||
#define elf_freebsd_debug_loader(off) (0)
|
||||
#define elf_freebsd_boot(entry) do {} while(0)
|
||||
#endif
|
||||
#if defined(IMAGE_FREEBSD) && defined(AOUT_IMAGE)
|
||||
static void aout_freebsd_probe(void);
|
||||
static void aout_freebsd_boot(void);
|
||||
#else
|
||||
#define aout_freebsd_probe() do {} while(0)
|
||||
#define aout_freebsd_boot() do {} while(0)
|
||||
#endif
|
||||
|
||||
/**************************************************************************
|
||||
dead_download - Restart etherboot if probe image fails
|
||||
**************************************************************************/
|
||||
static sector_t dead_download ( unsigned char *data __unused, unsigned int len __unused, int eof __unused) {
|
||||
longjmp(restart_etherboot, -2);
|
||||
}
|
||||
|
||||
#ifdef IMAGE_MULTIBOOT
|
||||
#include "../arch/i386/core/multiboot_loader.c"
|
||||
#else
|
||||
#define multiboot_probe(data, len) do {} while(0)
|
||||
#define multiboot_boot(entry) do {} while(0)
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef WINCE_IMAGE
|
||||
#include "../arch/i386/core/wince_loader.c"
|
||||
#endif
|
||||
|
||||
#ifdef AOUT_IMAGE
|
||||
#include "../arch/i386/core/aout_loader.c"
|
||||
#endif
|
||||
|
||||
#ifdef TAGGED_IMAGE
|
||||
#include "../arch/i386/core/tagged_loader.c"
|
||||
#endif
|
||||
|
||||
#if defined(ELF_IMAGE) || defined(ELF64_IMAGE)
|
||||
#include "elf_loader.c"
|
||||
#endif
|
||||
|
||||
#if defined(COFF_IMAGE)
|
||||
#include "../arch/e1/core/coff_loader.c"
|
||||
#endif
|
||||
|
||||
#ifdef IMAGE_FREEBSD
|
||||
#include "../arch/i386/core/freebsd_loader.c"
|
||||
#endif
|
||||
|
||||
#ifdef PXE_IMAGE
|
||||
#include "../arch/i386/core/pxe_loader.c"
|
||||
#endif
|
||||
|
||||
#ifdef RAW_IMAGE
|
||||
#include "../arch/armnommu/core/raw_loader.c"
|
||||
#endif
|
||||
|
||||
static void done(int do_cleanup)
|
||||
{
|
||||
#ifdef SIZEINDICATOR
|
||||
printf("K ");
|
||||
#endif
|
||||
printf("done\n");
|
||||
/* We may not want to do the cleanup: when booting a PXE
|
||||
* image, for example, we need to leave the network card
|
||||
* enabled, and it helps debugging if the serial console
|
||||
* remains enabled. The call the cleanup() will be triggered
|
||||
* when the PXE stack is shut down.
|
||||
*/
|
||||
if ( do_cleanup ) {
|
||||
cleanup();
|
||||
arch_on_exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
static int prep_segment(unsigned long start, unsigned long mid, unsigned long end,
|
||||
unsigned long istart __unused, unsigned long iend __unused)
|
||||
{
|
||||
unsigned fit, i;
|
||||
|
||||
#if LOAD_DEBUG
|
||||
printf ( "\nAbout to prepare segment [%lX,%lX)\n", start, end );
|
||||
sleep ( 3 );
|
||||
#endif
|
||||
|
||||
if (mid > end) {
|
||||
printf("filesz > memsz\n");
|
||||
return 0;
|
||||
}
|
||||
if ((end > virt_to_phys(_text)) &&
|
||||
(start < virt_to_phys(_end))) {
|
||||
printf("segment [%lX, %lX) overlaps etherboot [%lX, %lX)\n",
|
||||
start, end,
|
||||
virt_to_phys(_text), virt_to_phys(_end)
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
if ((end > heap_ptr) && (start < heap_bot)) {
|
||||
printf("segment [%lX, %lX) overlaps heap [%lX, %lX)\n",
|
||||
start, end,
|
||||
heap_ptr, heap_bot
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
fit = 0;
|
||||
for(i = 0; i < meminfo.map_count; i++) {
|
||||
unsigned long long r_start, r_end;
|
||||
if (meminfo.map[i].type != E820_RAM)
|
||||
continue;
|
||||
r_start = meminfo.map[i].addr;
|
||||
r_end = r_start + meminfo.map[i].size;
|
||||
if ((start >= r_start) && (end <= r_end)) {
|
||||
fit = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!fit) {
|
||||
printf("\nsegment [%lX,%lX) does not fit in any memory region\n",
|
||||
start, end);
|
||||
#if LOAD_DEBUG
|
||||
printf("Memory regions(%d):\n", meminfo.map_count);
|
||||
for(i = 0; i < meminfo.map_count; i++) {
|
||||
unsigned long long r_start, r_end;
|
||||
if (meminfo.map[i].type != E820_RAM)
|
||||
continue;
|
||||
r_start = meminfo.map[i].addr;
|
||||
r_end = r_start + meminfo.map[i].size;
|
||||
printf("[%X%X, %X%X) type %d\n",
|
||||
(unsigned long)(r_start >> 32),
|
||||
(unsigned long)r_start,
|
||||
(unsigned long)(r_end >> 32),
|
||||
(unsigned long)r_end,
|
||||
meminfo.map[i].type);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
#if LOAD_DEBUG
|
||||
/* Zap the whole lot. Do this so that if we're treading on
|
||||
* anything, it shows up now, when the debug message is
|
||||
* visible, rather than when we're partway through downloading
|
||||
* the file.
|
||||
*
|
||||
* If you see an entire screen full of exclamation marks, then
|
||||
* you've almost certainly written all over the display RAM.
|
||||
* This is likely to happen if the status of the A20 line gets
|
||||
* screwed up. Of course, if this happens, it's a good bet
|
||||
* that you've also trashed the whole of low memory, so expect
|
||||
* interesting things to happen...
|
||||
*/
|
||||
memset(phys_to_virt(start), '!', mid - start);
|
||||
#endif
|
||||
/* Zero the bss */
|
||||
if (end > mid) {
|
||||
memset(phys_to_virt(mid), 0, end - mid);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static unsigned long find_segment(unsigned long size, unsigned long align)
|
||||
{
|
||||
unsigned i;
|
||||
/* Verify I have a power of 2 alignment */
|
||||
if (align & (align - 1)) {
|
||||
return ULONG_MAX;
|
||||
}
|
||||
for(i = 0; i < meminfo.map_count; i++) {
|
||||
unsigned long r_start, r_end;
|
||||
if (meminfo.map[i].type != E820_RAM)
|
||||
continue;
|
||||
if ((meminfo.map[i].addr + meminfo.map[i].size) > ULONG_MAX) {
|
||||
continue;
|
||||
}
|
||||
r_start = meminfo.map[i].addr;
|
||||
r_end = r_start + meminfo.map[i].size;
|
||||
/* Don't allow the segment to overlap etherboot */
|
||||
if ((r_end > virt_to_phys(_text)) && (r_start < virt_to_phys(_text))) {
|
||||
r_end = virt_to_phys(_text);
|
||||
}
|
||||
if ((r_start > virt_to_phys(_text)) && (r_start < virt_to_phys(_end))) {
|
||||
r_start = virt_to_phys(_end);
|
||||
}
|
||||
/* Don't allow the segment to overlap the heap */
|
||||
if ((r_end > heap_ptr) && (r_start < heap_ptr)) {
|
||||
r_end = heap_ptr;
|
||||
}
|
||||
if ((r_start > heap_ptr) && (r_start < heap_bot)) {
|
||||
r_start = heap_ptr;
|
||||
}
|
||||
r_start = (r_start + align - 1) & ~(align - 1);
|
||||
if ((r_end >= r_start) && ((r_end - r_start) >= size)) {
|
||||
return r_start;
|
||||
}
|
||||
}
|
||||
/* I did not find anything :( */
|
||||
return ULONG_MAX;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
PROBE_IMAGE - Detect image file type
|
||||
**************************************************************************/
|
||||
os_download_t probe_image(unsigned char *data, unsigned int len)
|
||||
{
|
||||
os_download_t os_download = 0;
|
||||
#ifdef AOUT_IMAGE
|
||||
if (!os_download) os_download = aout_probe(data, len);
|
||||
#endif
|
||||
#ifdef ELF_IMAGE
|
||||
if (!os_download) os_download = elf32_probe(data, len);
|
||||
#endif
|
||||
#ifdef ELF64_IMAGE
|
||||
if (!os_download) os_download = elf64_probe(data, len);
|
||||
#endif
|
||||
#ifdef COFF_IMAGE
|
||||
if (!os_download) os_download = coff_probe(data, len);
|
||||
#endif
|
||||
#ifdef WINCE_IMAGE
|
||||
if (!os_download) os_download = wince_probe(data, len);
|
||||
#endif
|
||||
#ifdef TAGGED_IMAGE
|
||||
if (!os_download) os_download = tagged_probe(data, len);
|
||||
#endif
|
||||
/* PXE_IMAGE must always be last */
|
||||
#ifdef PXE_IMAGE
|
||||
if (!os_download) os_download = pxe_probe(data, len);
|
||||
#endif
|
||||
#ifdef RAW_IMAGE
|
||||
if (!os_download) os_download = raw_probe(data, len);
|
||||
#endif
|
||||
return os_download;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
LOAD_BLOCK - Try to load file
|
||||
**************************************************************************/
|
||||
int load_block(unsigned char *data, unsigned int block, unsigned int len, int eof)
|
||||
{
|
||||
static os_download_t os_download;
|
||||
static sector_t skip_sectors;
|
||||
static unsigned int skip_bytes;
|
||||
#ifdef SIZEINDICATOR
|
||||
static int rlen = 0;
|
||||
|
||||
if (block == 1)
|
||||
{
|
||||
rlen=len;
|
||||
printf("XXXX");
|
||||
}
|
||||
if (!(block % 4) || eof) {
|
||||
int size;
|
||||
size = ((block-1) * rlen + len) / 1024;
|
||||
|
||||
putchar('\b');
|
||||
putchar('\b');
|
||||
putchar('\b');
|
||||
putchar('\b');
|
||||
|
||||
putchar('0' + (size/1000)%10);
|
||||
putchar('0' + (size/100)%10);
|
||||
putchar('0' + (size/10)%10);
|
||||
putchar('0' + (size/1)%10);
|
||||
}
|
||||
#endif
|
||||
if (block == 1)
|
||||
{
|
||||
skip_sectors = 0;
|
||||
skip_bytes = 0;
|
||||
os_download = probe_image(data, len);
|
||||
if (!os_download) {
|
||||
printf("error: not a valid image\n");
|
||||
#if 0
|
||||
printf("block: %d len: %d\n", block, len);
|
||||
printf("%hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx\n",
|
||||
data[0], data[1], data[2], data[3],
|
||||
data[4], data[5], data[6], data[7]);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
} /* end of block zero processing */
|
||||
|
||||
/* Either len is greater or the skip is greater */
|
||||
if ((skip_sectors > (len >> 9)) ||
|
||||
((skip_sectors == (len >> 9)) && (skip_bytes >= (len & 0x1ff)))) {
|
||||
/* If I don't have enough bytes borrow them from skip_sectors */
|
||||
if (skip_bytes < len) {
|
||||
skip_sectors -= (len - skip_bytes + 511) >> 9;
|
||||
skip_bytes += (len - skip_bytes + 511) & ~0x1ff;
|
||||
}
|
||||
skip_bytes -= len;
|
||||
}
|
||||
else {
|
||||
len -= (skip_sectors << 9) + skip_bytes;
|
||||
data += (skip_sectors << 9) + skip_bytes;
|
||||
}
|
||||
skip_sectors = os_download(data, len, eof);
|
||||
skip_bytes = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-basic-offset: 8
|
||||
* End:
|
||||
*/
|
||||
|
||||
108
src/core/pc_kbd.c
Normal file
108
src/core/pc_kbd.c
Normal file
@@ -0,0 +1,108 @@
|
||||
/* Minimal polling PC keyboard driver
|
||||
* - No interrupt
|
||||
* - No LED
|
||||
* - No special keys
|
||||
*
|
||||
* still Enough For Me to type a filename.
|
||||
*
|
||||
* 2003-07 by SONE Takesh
|
||||
* 2004-04 moved by LYH From filo to Etherboot
|
||||
* yhlu@tyan.com
|
||||
*/
|
||||
#ifdef CONSOLE_PC_KBD
|
||||
#include "etherboot.h"
|
||||
|
||||
static char key_map[][128] = {
|
||||
{
|
||||
"\0\x1b""1234567890-=\b\t"
|
||||
"qwertyuiop[]\r\0as"
|
||||
"dfghjkl;'`\0\\zxcv"
|
||||
"bnm,./\0*\0 \0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0""789-456+1"
|
||||
"230."
|
||||
},{
|
||||
"\0\x1b""!@#$%^&*()_+\b\t"
|
||||
"QWERTYUIOP{}\r\0AS"
|
||||
"DFGHJKL:\"~\0|ZXCV"
|
||||
"BNM<>?\0\0\0 \0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0""789-456+1"
|
||||
"230."
|
||||
}
|
||||
};
|
||||
|
||||
static int cur_scan;
|
||||
static unsigned int shift_state;
|
||||
#define SHIFT 1
|
||||
#define CONTROL 2
|
||||
#define CAPS 4
|
||||
|
||||
static int get_scancode(void)
|
||||
{
|
||||
int scan;
|
||||
|
||||
if ((inb(0x64) & 1) == 0)
|
||||
return 0;
|
||||
scan = inb(0x60);
|
||||
|
||||
switch (scan) {
|
||||
case 0x2a:
|
||||
case 0x36:
|
||||
shift_state |= SHIFT;
|
||||
break;
|
||||
case 0xaa:
|
||||
case 0xb6:
|
||||
shift_state &= ~SHIFT;
|
||||
break;
|
||||
case 0x1d:
|
||||
shift_state |= CONTROL;
|
||||
break;
|
||||
case 0x9d:
|
||||
shift_state &= ~CONTROL;
|
||||
break;
|
||||
case 0x3a:
|
||||
shift_state ^= CAPS;
|
||||
break;
|
||||
}
|
||||
|
||||
if (scan & 0x80)
|
||||
return 0; /* ignore break code or 0xe0 etc! */
|
||||
return scan;
|
||||
}
|
||||
|
||||
int kbd_havekey(void)
|
||||
{
|
||||
if (!cur_scan)
|
||||
cur_scan = get_scancode();
|
||||
return cur_scan != 0;
|
||||
}
|
||||
|
||||
int kbd_ischar(void)
|
||||
{
|
||||
if (!kbd_havekey())
|
||||
return 0;
|
||||
if (!key_map[shift_state & SHIFT][cur_scan]) {
|
||||
cur_scan = 0;
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int kbd_getc(void)
|
||||
{
|
||||
int c;
|
||||
|
||||
while (!kbd_ischar())
|
||||
;
|
||||
c = key_map[shift_state & SHIFT][cur_scan];
|
||||
if (shift_state & (CONTROL | CAPS)) {
|
||||
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
|
||||
if (shift_state & CONTROL)
|
||||
c &= 0x1f;
|
||||
else if (shift_state & CAPS)
|
||||
c ^= ('A' ^ 'a');
|
||||
}
|
||||
}
|
||||
cur_scan = 0;
|
||||
return c;
|
||||
}
|
||||
#endif
|
||||
337
src/core/pci.c
Normal file
337
src/core/pci.c
Normal file
@@ -0,0 +1,337 @@
|
||||
#ifdef CONFIG_PCI
|
||||
|
||||
/*
|
||||
* 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 "etherboot.h"
|
||||
#include "pci.h"
|
||||
|
||||
/*#define DEBUG 1*/
|
||||
|
||||
static void scan_drivers(
|
||||
int type,
|
||||
uint32_t class, uint16_t vendor, uint16_t device,
|
||||
const struct pci_driver *last_driver, struct pci_device *dev)
|
||||
{
|
||||
const struct pci_driver *skip_driver = last_driver;
|
||||
/* Assume there is only one match of the correct type */
|
||||
const struct pci_driver *driver;
|
||||
|
||||
for(driver = pci_drivers; driver < pci_drivers_end; driver++) {
|
||||
int i;
|
||||
if (driver->type != type)
|
||||
continue;
|
||||
if (skip_driver) {
|
||||
if (skip_driver == driver)
|
||||
skip_driver = 0;
|
||||
continue;
|
||||
}
|
||||
for(i = 0; i < driver->id_count; i++) {
|
||||
if ((vendor == driver->ids[i].vendor) &&
|
||||
(device == driver->ids[i].dev_id)) {
|
||||
|
||||
dev->driver = driver;
|
||||
dev->name = driver->ids[i].name;
|
||||
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!class) {
|
||||
goto out;
|
||||
}
|
||||
for(driver = pci_drivers; driver < pci_drivers_end; driver++) {
|
||||
if (driver->type != type)
|
||||
continue;
|
||||
if (skip_driver) {
|
||||
if (skip_driver == driver)
|
||||
skip_driver = 0;
|
||||
continue;
|
||||
}
|
||||
if (last_driver == driver)
|
||||
continue;
|
||||
if ((class >> 8) == driver->class) {
|
||||
dev->driver = driver;
|
||||
dev->name = driver->name;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
out:
|
||||
return;
|
||||
}
|
||||
|
||||
void scan_pci_bus(int type, struct pci_device *dev)
|
||||
{
|
||||
unsigned int first_bus, first_devfn;
|
||||
const struct pci_driver *first_driver;
|
||||
unsigned int devfn, bus, buses;
|
||||
unsigned char hdr_type = 0;
|
||||
uint32_t class;
|
||||
uint16_t vendor, device;
|
||||
uint32_t l, membase, ioaddr, romaddr;
|
||||
uint8_t irq;
|
||||
int reg;
|
||||
|
||||
first_bus = 0;
|
||||
first_devfn = 0;
|
||||
first_driver = 0;
|
||||
if (dev->driver || dev->use_specified) {
|
||||
first_driver = dev->driver;
|
||||
first_bus = dev->bus;
|
||||
first_devfn = dev->devfn;
|
||||
/* Re read the header type on a restart */
|
||||
pcibios_read_config_byte(first_bus, first_devfn & ~0x7,
|
||||
PCI_HEADER_TYPE, &hdr_type);
|
||||
dev->driver = 0;
|
||||
dev->bus = 0;
|
||||
dev->devfn = 0;
|
||||
}
|
||||
|
||||
/* Scan all PCI buses, until we find our card.
|
||||
* We could be smart only scan the required buses but that
|
||||
* is error prone, and tricky.
|
||||
* By scanning all possible pci buses in order we should find
|
||||
* our card eventually.
|
||||
*/
|
||||
buses=256;
|
||||
for (bus = first_bus; bus < buses; ++bus) {
|
||||
for (devfn = first_devfn; devfn < 0xff; ++devfn, first_driver = 0) {
|
||||
if (PCI_FUNC (devfn) == 0)
|
||||
pcibios_read_config_byte(bus, devfn, PCI_HEADER_TYPE, &hdr_type);
|
||||
else if (!(hdr_type & 0x80)) /* not a multi-function device */
|
||||
continue;
|
||||
pcibios_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l);
|
||||
/* some broken boards return 0 if a slot is empty: */
|
||||
if (l == 0xffffffff || l == 0x00000000) {
|
||||
continue;
|
||||
}
|
||||
vendor = l & 0xffff;
|
||||
device = (l >> 16) & 0xffff;
|
||||
|
||||
pcibios_read_config_dword(bus, devfn, PCI_REVISION, &l);
|
||||
class = (l >> 8) & 0xffffff;
|
||||
#if DEBUG
|
||||
{
|
||||
int i;
|
||||
printf("%hhx:%hhx.%hhx [%hX/%hX] Class %hX\n",
|
||||
bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
|
||||
vendor, device, class >> 8);
|
||||
#if DEBUG > 1
|
||||
for(i = 0; i < 256; i++) {
|
||||
unsigned char byte;
|
||||
if ((i & 0xf) == 0) {
|
||||
printf("%hhx: ", i);
|
||||
}
|
||||
pcibios_read_config_byte(bus, devfn, i, &byte);
|
||||
printf("%hhx ", byte);
|
||||
if ((i & 0xf) == 0xf) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
#endif
|
||||
scan_drivers(type, class, vendor, device, first_driver, dev);
|
||||
if (!dev->driver)
|
||||
continue;
|
||||
|
||||
dev->devfn = devfn;
|
||||
dev->bus = bus;
|
||||
dev->class = class;
|
||||
dev->vendor = vendor;
|
||||
dev->dev_id = device;
|
||||
|
||||
|
||||
/* Get the ROM base address */
|
||||
pcibios_read_config_dword(bus, devfn,
|
||||
PCI_ROM_ADDRESS, &romaddr);
|
||||
romaddr >>= 10;
|
||||
dev->romaddr = romaddr;
|
||||
|
||||
/* Get the ``membase'' */
|
||||
pcibios_read_config_dword(bus, devfn,
|
||||
PCI_BASE_ADDRESS_1, &membase);
|
||||
dev->membase = membase;
|
||||
|
||||
/* Get the ``ioaddr'' */
|
||||
for (reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4) {
|
||||
pcibios_read_config_dword(bus, devfn, reg, &ioaddr);
|
||||
if ((ioaddr & PCI_BASE_ADDRESS_IO_MASK) == 0 || (ioaddr & PCI_BASE_ADDRESS_SPACE_IO) == 0)
|
||||
continue;
|
||||
|
||||
|
||||
/* Strip the I/O address out of the returned value */
|
||||
ioaddr &= PCI_BASE_ADDRESS_IO_MASK;
|
||||
|
||||
/* Take the first one or the one that matches in boot ROM address */
|
||||
dev->ioaddr = ioaddr;
|
||||
}
|
||||
|
||||
/* Get the irq */
|
||||
pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &irq);
|
||||
if (irq) {
|
||||
pci_read_config_byte(dev, PCI_INTERRUPT_LINE,
|
||||
&irq);
|
||||
}
|
||||
dev->irq = irq;
|
||||
|
||||
#if DEBUG > 2
|
||||
printf("Found %s ROM address %#hx\n",
|
||||
dev->name, romaddr);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
first_devfn = 0;
|
||||
}
|
||||
first_bus = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Set device to be a busmaster in case BIOS neglected to do so.
|
||||
* Also adjust PCI latency timer to a reasonable value, 32.
|
||||
*/
|
||||
void adjust_pci_device(struct pci_device *p)
|
||||
{
|
||||
unsigned short new_command, pci_command;
|
||||
unsigned char pci_latency;
|
||||
|
||||
pcibios_read_config_word(p->bus, p->devfn, PCI_COMMAND, &pci_command);
|
||||
new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO;
|
||||
if (pci_command != new_command) {
|
||||
#if DEBUG > 0
|
||||
printf(
|
||||
"The PCI BIOS has not enabled this device!\n"
|
||||
"Updating PCI command %hX->%hX. pci_bus %hhX pci_device_fn %hhX\n",
|
||||
pci_command, new_command, p->bus, p->devfn);
|
||||
#endif
|
||||
pcibios_write_config_word(p->bus, p->devfn, PCI_COMMAND, new_command);
|
||||
}
|
||||
pcibios_read_config_byte(p->bus, p->devfn, PCI_LATENCY_TIMER, &pci_latency);
|
||||
if (pci_latency < 32) {
|
||||
#if DEBUG > 0
|
||||
printf("PCI latency timer (CFLT) is unreasonably low at %d. Setting to 32 clocks.\n",
|
||||
pci_latency);
|
||||
#endif
|
||||
pcibios_write_config_byte(p->bus, p->devfn, PCI_LATENCY_TIMER, 32);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the start of a pci resource.
|
||||
*/
|
||||
unsigned long pci_bar_start(struct pci_device *dev, unsigned int index)
|
||||
{
|
||||
uint32_t lo, hi;
|
||||
unsigned long bar;
|
||||
pci_read_config_dword(dev, index, &lo);
|
||||
if (lo & PCI_BASE_ADDRESS_SPACE_IO) {
|
||||
bar = lo & PCI_BASE_ADDRESS_IO_MASK;
|
||||
} else {
|
||||
bar = 0;
|
||||
if ((lo & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == PCI_BASE_ADDRESS_MEM_TYPE_64) {
|
||||
pci_read_config_dword(dev, index + 4, &hi);
|
||||
if (hi) {
|
||||
#if ULONG_MAX > 0xffffffff
|
||||
bar = hi;
|
||||
bar <<=32;
|
||||
#else
|
||||
printf("Unhandled 64bit BAR\n");
|
||||
return -1UL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
bar |= lo & PCI_BASE_ADDRESS_MEM_MASK;
|
||||
}
|
||||
return bar + pcibios_bus_base(dev->bus);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the size of a pci resource.
|
||||
*/
|
||||
unsigned long pci_bar_size(struct pci_device *dev, unsigned int bar)
|
||||
{
|
||||
uint32_t start, size;
|
||||
/* Save the original bar */
|
||||
pci_read_config_dword(dev, bar, &start);
|
||||
/* Compute which bits can be set */
|
||||
pci_write_config_dword(dev, bar, ~0);
|
||||
pci_read_config_dword(dev, bar, &size);
|
||||
/* Restore the original size */
|
||||
pci_write_config_dword(dev, bar, start);
|
||||
/* Find the significant bits */
|
||||
if (start & PCI_BASE_ADDRESS_SPACE_IO) {
|
||||
size &= PCI_BASE_ADDRESS_IO_MASK;
|
||||
} else {
|
||||
size &= PCI_BASE_ADDRESS_MEM_MASK;
|
||||
}
|
||||
/* Find the lowest bit set */
|
||||
size = size & ~(size - 1);
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_find_capability - query for devices' capabilities
|
||||
* @dev: PCI device to query
|
||||
* @cap: capability code
|
||||
*
|
||||
* Tell if a device supports a given PCI capability.
|
||||
* Returns the address of the requested capability structure within the
|
||||
* device's PCI configuration space or 0 in case the device does not
|
||||
* support it. Possible values for @cap:
|
||||
*
|
||||
* %PCI_CAP_ID_PM Power Management
|
||||
*
|
||||
* %PCI_CAP_ID_AGP Accelerated Graphics Port
|
||||
*
|
||||
* %PCI_CAP_ID_VPD Vital Product Data
|
||||
*
|
||||
* %PCI_CAP_ID_SLOTID Slot Identification
|
||||
*
|
||||
* %PCI_CAP_ID_MSI Message Signalled Interrupts
|
||||
*
|
||||
* %PCI_CAP_ID_CHSWP CompactPCI HotSwap
|
||||
*/
|
||||
int pci_find_capability(struct pci_device *dev, int cap)
|
||||
{
|
||||
uint16_t status;
|
||||
uint8_t pos, id;
|
||||
uint8_t hdr_type;
|
||||
int ttl = 48;
|
||||
|
||||
pci_read_config_word(dev, PCI_STATUS, &status);
|
||||
if (!(status & PCI_STATUS_CAP_LIST))
|
||||
return 0;
|
||||
pci_read_config_byte(dev, PCI_HEADER_TYPE, &hdr_type);
|
||||
switch (hdr_type & 0x7F) {
|
||||
case PCI_HEADER_TYPE_NORMAL:
|
||||
case PCI_HEADER_TYPE_BRIDGE:
|
||||
default:
|
||||
pci_read_config_byte(dev, PCI_CAPABILITY_LIST, &pos);
|
||||
break;
|
||||
case PCI_HEADER_TYPE_CARDBUS:
|
||||
pci_read_config_byte(dev, PCI_CB_CAPABILITY_LIST, &pos);
|
||||
break;
|
||||
}
|
||||
while (ttl-- && pos >= 0x40) {
|
||||
pos &= ~3;
|
||||
pci_read_config_byte(dev, pos + PCI_CAP_LIST_ID, &id);
|
||||
#if DEBUG > 0
|
||||
printf("Capability: %d\n", id);
|
||||
#endif
|
||||
if (id == 0xff)
|
||||
break;
|
||||
if (id == cap)
|
||||
return pos;
|
||||
pci_read_config_byte(dev, pos + PCI_CAP_LIST_NEXT, &pos);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PCI */
|
||||
70
src/core/pci_probe.c
Normal file
70
src/core/pci_probe.c
Normal file
@@ -0,0 +1,70 @@
|
||||
#ifdef CONFIG_PCI
|
||||
/*
|
||||
* 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 "etherboot.h"
|
||||
#include "nic.h"
|
||||
#include "pci.h"
|
||||
|
||||
void pci_enumerate(void)
|
||||
{
|
||||
const struct pci_driver *driver;
|
||||
for(driver = pci_drivers; driver < pci_drivers_end; driver++) {
|
||||
printf("%s ", driver->name);
|
||||
}
|
||||
}
|
||||
|
||||
int pci_probe(struct dev *dev, const char *type_name)
|
||||
{
|
||||
/*
|
||||
* NIC probing is in pci device order, followed by the
|
||||
* link order of the drivers. A driver that matches
|
||||
* on vendor and device id will supersede a driver
|
||||
* that matches on pci class.
|
||||
*
|
||||
* If you want to probe for another device behind the same pci
|
||||
* device just increment index. And the previous probe call
|
||||
* will be repeated.
|
||||
*/
|
||||
struct pci_probe_state *state = &dev->state.pci;
|
||||
printf("Probing pci %s...\n", type_name);
|
||||
if (dev->how_probe == PROBE_FIRST) {
|
||||
state->advance = 1;
|
||||
state->dev.driver = 0;
|
||||
state->dev.bus = 0;
|
||||
state->dev.devfn = 0;
|
||||
dev->index = -1;
|
||||
}
|
||||
for(;;) {
|
||||
if ((dev->how_probe != PROBE_AWAKE) && state->advance) {
|
||||
find_pci(dev->type, &state->dev);
|
||||
dev->index = -1;
|
||||
}
|
||||
state->advance = 1;
|
||||
|
||||
if (state->dev.driver == 0)
|
||||
break;
|
||||
|
||||
if (dev->how_probe != PROBE_AWAKE) {
|
||||
dev->type_index++;
|
||||
}
|
||||
dev->devid.bus_type = PCI_BUS_TYPE;
|
||||
dev->devid.vendor_id = htons(state->dev.vendor);
|
||||
dev->devid.device_id = htons(state->dev.dev_id);
|
||||
/* FIXME how do I handle dev->index + PROBE_AGAIN?? */
|
||||
|
||||
printf("[%s]", state->dev.name);
|
||||
if (state->dev.driver->probe(dev, &state->dev)) {
|
||||
state->advance = (dev->index == -1);
|
||||
return PROBE_WORKED;
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
return PROBE_FAILED;
|
||||
}
|
||||
|
||||
#endif
|
||||
269
src/core/pcmcia.c
Normal file
269
src/core/pcmcia.c
Normal file
@@ -0,0 +1,269 @@
|
||||
#ifdef CONFIG_PCMCIA
|
||||
|
||||
/*
|
||||
* pcmcia.c
|
||||
*
|
||||
* PCMCIA support routines for etherboot - generic stuff
|
||||
*
|
||||
* This code has partly be taken from the linux kernel sources, .../drivers/pcmcia/
|
||||
* Started & put together by
|
||||
* Anselm Martin Hoffmeister
|
||||
* Stockholm Projekt Computer-Service
|
||||
* Sankt Augustin / Bonn, Germany
|
||||
*
|
||||
* Distributed under GPL2
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
*
|
||||
* ******************************
|
||||
* PLEASE DO NOT YET WORK ON THIS
|
||||
* ******************************
|
||||
*
|
||||
* I'm still fixing it up on every end, so we most probably would interfere
|
||||
* at some point. If there's anything obvious or better, not-so-obvious,
|
||||
* please contact me by e-mail: anselm (AT) hoffmeister (DOT) be *THANKS*
|
||||
*/
|
||||
#include "../include/pcmcia.h"
|
||||
#include "../include/i82365.h"
|
||||
#define CODE_STATUS "alpha"
|
||||
#define CODE_VERSION "0.1.3"
|
||||
|
||||
#include "../include/pcmcia-opts.h"
|
||||
#include "../include/pcmcia.h"
|
||||
|
||||
int sockets; /* AHTODO: Phase this out! */
|
||||
u_int pccsocks;
|
||||
struct pccsock_t pccsock[MAXPCCSOCKS];
|
||||
int inited = -1;
|
||||
struct pcc_config_t pccconfig[MAXPCCCONFIGS];
|
||||
|
||||
struct driver_interact_t driver[] = {
|
||||
#ifdef SUPPORT_I82365
|
||||
{ I82365, i82365_interfacer, "Intel_82365" },
|
||||
#endif
|
||||
};
|
||||
|
||||
#define NUM_DRIVERS (sizeof(driver)/(sizeof(struct driver_interact_t)))
|
||||
|
||||
void sleepticks(int numticks ) {
|
||||
u_int tmo;
|
||||
for (tmo = currticks()+numticks; currticks() < tmo; ) {
|
||||
poll_interruptions();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int pcmcia_init_all(void) {
|
||||
u_int i, j, k, l, m, n, ui, configs = 0;
|
||||
u_int multicard[8];
|
||||
u_char *uc, upc;
|
||||
if ( PDEBUG > 0 ) printf("Initializing PCMCIA subsystem (code-status: " CODE_STATUS ", Version " CODE_VERSION ")\n");
|
||||
if ( PDEBUG > 2 ) {
|
||||
printf ( "Supporting %d driver(s): ", NUM_DRIVERS );
|
||||
for ( i = 0; i < NUM_DRIVERS; ++i ) {
|
||||
printf ( "[%s] ", driver[i].name );
|
||||
}
|
||||
printf ( "\n" );
|
||||
}
|
||||
pccsocks = 0;
|
||||
sockets = 0;
|
||||
// Init all drivers in the driver[] array:
|
||||
for ( i = 0; i < NUM_DRIVERS; ++i ) {
|
||||
driver[i].f(INIT,0,i,0,0); // init needs no params. It uses pccsocks and pccsock[].
|
||||
// Only i tells it which driver_id itself is.
|
||||
}
|
||||
for ( i = 0; i < pccsocks; ++i ) {
|
||||
printf ( "Socket %d: ", i );
|
||||
if ( pccsock[i].status != HASCARD ) {
|
||||
printf ( "is %s: skipping\n", pccsock[i].status == EMPTY? "empty":"[status unknown]" );
|
||||
continue;
|
||||
}
|
||||
if ( 0 != driver[pccsock[i].drivernum].f(MAPATTRMEM,pccsock[i].internalid,MAP_ATTRMEM_TO, MAP_ATTRMEM_LEN,0 ) ) {
|
||||
printf ("PCMCIA controller failed to map attribute memory.\n**** SEVERE ERROR CONDITION. Skipping controller.\n" );
|
||||
if ( PDEBUG > 2 ) {
|
||||
printf ( "<press key. THIS CONDITION SHOULD BE REPORTED!>\n" ); getchar();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// parse configuration information
|
||||
uc = ioremap ( MAP_ATTRMEM_TO, MAP_ATTRMEM_LEN );
|
||||
pccsock[i].stringoffset = pccsock[i].configoffset = pccsock[i].stringlength = 0;
|
||||
pccsock[i].type = 0xff;
|
||||
for ( l = 0; l < 8; ++l ) multicard[l] = 0;
|
||||
sleepticks(2);
|
||||
for ( l = ui = 0; ui < 0x800; ui += uc[(2*ui)+2] + 2 ) {
|
||||
if ( uc[(2*ui)] == 0xff ) {
|
||||
break;
|
||||
}
|
||||
// This loop is complete rubbish AFAICS.
|
||||
// But without it, my test system won't come up.
|
||||
// It's too bad to develop on broken hardware
|
||||
// - Anselm
|
||||
}
|
||||
sleepticks(2);
|
||||
configs = 0;
|
||||
inited = -1;
|
||||
for ( l = ui = 0; ui < 0x800; ui += uc[(2*ui)+2] + 2 ) {
|
||||
if ( uc[(2*ui)] == 0xff ) break;
|
||||
else if ( uc[2*ui] == 0x15 ) {
|
||||
for ( k = 2 * ( ui + 2 ); ( uc[k] <= ' ' ) && ( k < ( 2 * ( uc[2*(ui+1)] + ui + 2 ) ) ) ; k += 2 ) { ; }
|
||||
pccsock[i].stringoffset = k;
|
||||
pccsock[i].stringlength = ( 2 * ( ui + 2 + uc[(2*ui)+2] ) - k ) / 2;
|
||||
} else if ( uc[2*ui] == 0x21 ) {
|
||||
pccsock[i].type = uc[(2*ui)+4];
|
||||
} else if ( uc[2*ui] == 0x1a ) { // Configuration map
|
||||
printf ( "\nConfig map 0x1a found [" );
|
||||
for ( k = 0; k < uc[2*(ui+1)]; ++k ) {
|
||||
printf ( "%02x ", uc[2*(ui+k+2)] );
|
||||
}
|
||||
printf ( "]\nHighest config available is %d\n", uc[2*(ui+3)] );
|
||||
m = uc[2*(ui+2)];
|
||||
pccsock[i].configoffset = 0;
|
||||
for ( j = 0; j <= m & 3; ++j ) {
|
||||
pccsock[i].configoffset += uc[2*(ui+4+j)] << (8*j);
|
||||
}
|
||||
pccsock[i].rmask0 = 0;
|
||||
for ( j = 0; j <= ( ( ( m & 0x3c ) >> 2 ) & 3 ); ++j ) {
|
||||
pccsock[i].rmask0 += uc[2*(ui+5+(m&3)+j)] << (8*j);
|
||||
}
|
||||
j = pccsock[i].rmask0;
|
||||
printf ( "Config offset is %x, card has regs: < %s%s%s%s%s>\n", pccsock[i].configoffset,
|
||||
j & 1 ? "COR ":"", j & 2 ? "CCSR ":"", j & 4 ? "PRR ":"", j & 8 ? "SCR ":"", j & 16? "ESR ":"" );
|
||||
printf ( "COR + CCSR contents (si/du) %x %x/%x %x\n", uc[pccsock[i].configoffset+0],
|
||||
uc[pccsock[i].configoffset+2],uc[pccsock[i].configoffset*2],uc[(pccsock[i].configoffset*2)+2] );
|
||||
printf ( " " );
|
||||
} else if ( uc[2*ui] == 0x1b ) { // Configuration data entry
|
||||
//printf ( "Config data 0x1b found [\n" );getchar();
|
||||
for ( k = 0; k < uc[2*(ui+1)]; ++k ) {
|
||||
// printf ( "%02x ", uc[2*(ui+k+2)] );
|
||||
}
|
||||
// Parse this tuple into pccconfig[configs]
|
||||
// printf ( "]\n" );
|
||||
if ( configs == MAXPCCCONFIGS ) continue;
|
||||
k = 2*ui+4;
|
||||
pccconfig[configs].index = uc[k] & 0x3f;
|
||||
if ( uc[k] & 0x80 ) {
|
||||
// printf ( "Special config, unsupp. for now\n" );
|
||||
continue;
|
||||
}
|
||||
k+=2;
|
||||
// printf ( "Features: %2x\n", uc[k] );
|
||||
if ( uc[k] & 0x7 ) {
|
||||
// printf ( "Cannot work with Vcc/Timing configs right now\n" );
|
||||
continue;
|
||||
}
|
||||
pccconfig[configs].iowin = pccconfig[configs].iolen = 0;
|
||||
if ( 0 != ( uc[k] & 0x8 ) ) {
|
||||
k+=2;
|
||||
// printf ( "Reading IO config: " );
|
||||
if ( 0 == ( uc[k] & 0x80 ) ) {
|
||||
// printf ( "Cannot work with auto/io config\n" );
|
||||
continue;
|
||||
}
|
||||
k+=2;
|
||||
if ( 0 != ( uc[k] & 0x0f ) ) {
|
||||
// printf ( "Don't support more than 1 iowin right now\n" );
|
||||
continue;
|
||||
}
|
||||
j = (uc[k] & 0x30) >> 4;
|
||||
m = (uc[k] & 0xc0) >> 6;
|
||||
if ( 3 == j ) ++j;
|
||||
if ( 3 == m ) ++m;
|
||||
k += 2;
|
||||
pccconfig[configs].iowin = 0;
|
||||
pccconfig[configs].iolen = 1;
|
||||
for ( n = 0; n < j; ++n, k+=2 ) {
|
||||
pccconfig[configs].iowin += uc[k] << (n*8);
|
||||
}
|
||||
for ( n = 0; n < m; ++n, k+=2 ) {
|
||||
pccconfig[configs].iolen += uc[k] << (n*8);
|
||||
}
|
||||
// printf ( "io %x len %d (%d)\n", pccconfig[configs].iowin, pccconfig[configs].iolen,configs );
|
||||
}
|
||||
for ( j = 0; j < (uc[k] & 3); ++j ) {
|
||||
// pccconfig[configs].iowin += (uc[k+(2*j)+2]) << (8*j);
|
||||
}
|
||||
++configs;
|
||||
}
|
||||
}
|
||||
if ( pccsock[i].stringoffset > 0 ) { // If no identifier, it's not a valid CIS (as of documentation...)
|
||||
printf ( "[" );
|
||||
for ( k = 0; ( k < pccsock[i].stringlength ) && ( k < 64 ); ++k ) {
|
||||
j = uc[pccsock[i].stringoffset + 2 * k];
|
||||
printf ( "%c", (j>=' '? j:' ' ) );
|
||||
}
|
||||
printf ("]\n is type %d (", pccsock[i].type );
|
||||
switch ( pccsock[i].type ) {
|
||||
case 0x00:
|
||||
printf ( "MULTI" ); break;
|
||||
case 0x01:
|
||||
printf ( "Memory" ); break;
|
||||
case 0x02:
|
||||
printf ( "Serial" ); break;
|
||||
case 0x03:
|
||||
printf ( "Parallel" ); break;
|
||||
case 0x04:
|
||||
printf ( "Fixed" ); break;
|
||||
case 0x05:
|
||||
printf ( "Video" ); break;
|
||||
case 0x06:
|
||||
printf ( "Network" ); break;
|
||||
case 0x07:
|
||||
printf ( "AIMS" ); break;
|
||||
case 0x08:
|
||||
printf ( "SCSI" ); break;
|
||||
case 0x106: // Special / homebrew to say "Multi/network"
|
||||
printf ( "MULTI, with Network" ); break; // AHTODO find a card for this
|
||||
default:
|
||||
printf ( "UNSUPPORTED/UNKNOWN" );
|
||||
}
|
||||
printf ( ") with %d possible configuration(s)\n", configs );
|
||||
// Now set dependency: If it's Network or multi->network, accept
|
||||
if ( (inited <= 0 ) && (6 == (0xff & pccsock[i].type) ) && (0 < configs ) ) {
|
||||
printf ( "activating this device with ioport %x-%x (config #%d)\n",
|
||||
pccconfig[0].iowin, pccconfig[0].iowin+pccconfig[0].iolen-1, pccconfig[0].index );
|
||||
inited = i;
|
||||
// And unmap attrmem ourselves!
|
||||
printf ( "Activating config..." );
|
||||
if ( m=driver[pccsock[i].drivernum].f(SELECTCONFIG,pccsock[i].internalid,pccconfig[0].index,0,&pccconfig[0]) ) {
|
||||
printf ("Failure(%d)!",m); inited = -1;
|
||||
driver[pccsock[i].drivernum].f(UNMAPATTRMEM,pccsock[i].internalid,0,0,0);
|
||||
}
|
||||
printf ( "done!\n" );
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
printf ( "unsupported - no identifier string found in CIS\n" );
|
||||
}
|
||||
// unmap the PCMCIA device
|
||||
if ( i != inited ) {
|
||||
if ( 0 != driver[pccsock[i].drivernum].f(UNMAPATTRMEM,pccsock[i].internalid,0,0,0) ) {
|
||||
printf ("PCMCIA controller failed to unmap attribute memory.\n**** SEVERE ERROR CONDITION ****\n" );
|
||||
if ( PDEBUG > 2 ) {
|
||||
printf ( "<press key. THIS CONDITION SHOULD BE REPORTED!>\n" ); getchar();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( PDEBUG > 2 ) {
|
||||
printf ( "<press key to exit the pcmcia_init_all routine>\n" );
|
||||
getchar();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pcmcia_shutdown_all(void) {
|
||||
int i;
|
||||
//if ( PDEBUG > 2 ) {printf("<press key to continue>\n" ); getchar(); }
|
||||
for ( i = 0; i < pccsocks; ++i ) {
|
||||
driver[pccsock[i].drivernum].f(SHUTDOWN,pccsock[i].internalid,0,0,0);
|
||||
}
|
||||
printf("Shutdown of PCMCIA subsystem completed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PCMCIA */
|
||||
407
src/core/proto_eth_slow.c
Normal file
407
src/core/proto_eth_slow.c
Normal file
@@ -0,0 +1,407 @@
|
||||
/* Copyright 2004 Linux Networx */
|
||||
#ifdef PROTO_LACP
|
||||
#if 0
|
||||
#include "etherboot.h"
|
||||
#include "nic.h"
|
||||
#include "timer.h"
|
||||
#endif
|
||||
|
||||
#define LACP_DEBUG 0
|
||||
|
||||
/* Structure definitions originally taken from the linux bond_3ad driver */
|
||||
|
||||
#define SLOW_DST_MAC "\x01\x80\xc2\x00\x00\x02"
|
||||
static const char slow_dest[] = SLOW_DST_MAC;
|
||||
|
||||
|
||||
#define SLOW_SUBTYPE_LACP 1
|
||||
#define SLOW_SUBTYPE_MARKER 2
|
||||
|
||||
struct slow_header {
|
||||
uint8_t subtype;
|
||||
};
|
||||
|
||||
struct lacp_info {
|
||||
uint16_t system_priority;
|
||||
uint8_t system[ETH_ALEN];
|
||||
uint16_t key;
|
||||
uint16_t port_priority;
|
||||
uint16_t port;
|
||||
uint8_t state;
|
||||
uint8_t reserved[3];
|
||||
} PACKED;
|
||||
|
||||
#define LACP_CMP_LEN (2 + 6 + 2 + 2 + 2)
|
||||
#define LACP_CP_LEN (2 + 6 + 2 + 2 + 2 + 1)
|
||||
|
||||
/* Link Aggregation Control Protocol(LACP) data unit structure(43.4.2.2 in the 802.3ad standard) */
|
||||
struct slow_lacp {
|
||||
uint8_t subtype; /* = LACP(= 0x01) */
|
||||
uint8_t version_number;
|
||||
uint8_t tlv_type_actor_info; /* = actor information(type/length/value) */
|
||||
#define LACP_TLV_TERMINATOR 0
|
||||
#define LACP_TLV_ACTOR 1
|
||||
#define LACP_TLV_PARTNER 2
|
||||
#define LACP_TLV_COLLECTOR 3
|
||||
uint8_t actor_information_length; /* = 20 */
|
||||
struct lacp_info actor;
|
||||
uint8_t tlv_type_partner_info; /* = partner information */
|
||||
uint8_t partner_information_length; /* = 20 */
|
||||
struct lacp_info partner;
|
||||
uint8_t tlv_type_collector_info; /* = collector information */
|
||||
uint8_t collector_information_length; /* = 16 */
|
||||
uint16_t collector_max_delay;
|
||||
uint8_t reserved_12[12];
|
||||
uint8_t tlv_type_terminator; /* = terminator */
|
||||
uint8_t terminator_length; /* = 0 */
|
||||
uint8_t reserved_50[50]; /* = 0 */
|
||||
} PACKED;
|
||||
|
||||
/* Marker Protocol Data Unit(PDU) structure(43.5.3.2 in the 802.3ad standard) */
|
||||
struct slow_marker {
|
||||
uint8_t subtype; /* = 0x02 (marker PDU) */
|
||||
uint8_t version_number; /* = 0x01 */
|
||||
uint8_t tlv_type;
|
||||
#define MARKER_TLV_TERMINATOR 0 /* marker terminator */
|
||||
#define MARKER_TLV_INFO 1 /* marker information */
|
||||
#define MARKER_TLV_RESPONSE 2 /* marker response information */
|
||||
uint8_t marker_length; /* = 0x16 */
|
||||
uint16_t requester_port; /* The number assigned to the port by the requester */
|
||||
uint8_t requester_system[ETH_ALEN]; /* The requester's system id */
|
||||
uint32_t requester_transaction_id; /* The transaction id allocated by the requester, */
|
||||
uint16_t pad; /* = 0 */
|
||||
uint8_t tlv_type_terminator; /* = 0x00 */
|
||||
uint8_t terminator_length; /* = 0x00 */
|
||||
uint8_t reserved_90[90]; /* = 0 */
|
||||
} PACKED;
|
||||
|
||||
union slow_union {
|
||||
struct slow_header header;
|
||||
struct slow_lacp lacp;
|
||||
struct slow_marker marker;
|
||||
};
|
||||
|
||||
#define FAST_PERIODIC_TIME (1*TICKS_PER_SEC)
|
||||
#define SLOW_PERIODIC_TIME (30*TICKS_PER_SEC)
|
||||
#define SHORT_TIMEOUT_TIME (3*FAST_PERIODIC_TIME)
|
||||
#define LONG_TIMEOUT_TIME (3*SLOW_PERIODIC_TIME)
|
||||
#define CHURN_DETECTION_TIME (60*TICKS_PER_SEC)
|
||||
#define AGGREGATE_WAIT_TIME (2*TICKS_PER_SEC)
|
||||
|
||||
#define LACP_ACTIVITY (1 << 0)
|
||||
#define LACP_TIMEOUT (1 << 1)
|
||||
#define LACP_AGGREGATION (1 << 2)
|
||||
#define LACP_SYNCHRONIZATION (1 << 3)
|
||||
#define LACP_COLLECTING (1 << 4)
|
||||
#define LACP_DISTRIBUTING (1 << 5)
|
||||
#define LACP_DEFAULTED (1 << 6)
|
||||
#define LACP_EXPIRED (1 << 7)
|
||||
|
||||
#define UNSELECTED 0
|
||||
#define STANDBY 1
|
||||
#define SELECTED 2
|
||||
|
||||
|
||||
struct lacp_state {
|
||||
struct slow_lacp pkt;
|
||||
unsigned long current_while_timer; /* Time when the LACP information expires */
|
||||
unsigned long periodic_timer; /* Time when I need to send my partner an update */
|
||||
};
|
||||
|
||||
static struct lacp_state lacp;
|
||||
|
||||
|
||||
#if LACP_DEBUG > 0
|
||||
static void print_lacp_state(uint8_t state)
|
||||
{
|
||||
printf("%hhx", state);
|
||||
if (state & LACP_ACTIVITY) {
|
||||
printf(" Activity");
|
||||
}
|
||||
if (state & LACP_TIMEOUT) {
|
||||
printf(" Timeout");
|
||||
}
|
||||
if (state & LACP_AGGREGATION) {
|
||||
printf(" Aggregation");
|
||||
}
|
||||
if (state & LACP_SYNCHRONIZATION) {
|
||||
printf(" Syncronization");
|
||||
}
|
||||
if (state & LACP_COLLECTING) {
|
||||
printf(" Collecting");
|
||||
}
|
||||
if (state & LACP_DISTRIBUTING) {
|
||||
printf(" Distributing");
|
||||
}
|
||||
if (state & LACP_DEFAULTED) {
|
||||
printf(" Defaulted");
|
||||
}
|
||||
if (state & LACP_EXPIRED) {
|
||||
printf(" Expired");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static inline void print_lacpdu(struct slow_lacp *pkt)
|
||||
{
|
||||
printf("subtype version: %hhx %hhx\n",
|
||||
pkt->subtype, pkt->version_number);
|
||||
printf("actor_tlv %hhx", pkt->tlv_type_actor_info);
|
||||
printf(" len: %hhx (\n", pkt->actor_information_length);
|
||||
printf(" sys_pri: %hx", ntohs(pkt->actor.system_priority));
|
||||
printf(" mac: %!", pkt->actor.system);
|
||||
printf(" key: %hx", ntohs(pkt->actor.key));
|
||||
printf(" port_pri: %hx", ntohs(pkt->actor.port_priority));
|
||||
printf(" port: %hx\n", ntohs(pkt->actor.port));
|
||||
printf(" state: ");
|
||||
print_lacp_state(pkt->actor.state);
|
||||
#if LACP_DEBUG > 1
|
||||
printf(" reserved: %hhx %hhx %hhx\n",
|
||||
pkt->actor.reserved[0], pkt->actor.reserved[1], pkt->actor.reserved[2]);
|
||||
#endif
|
||||
printf(")\n");
|
||||
printf("partner_tlv: %hhx", pkt->tlv_type_partner_info);
|
||||
printf(" len: %hhx (\n", pkt->partner_information_length);
|
||||
printf(" sys_pri: %hx", ntohs(pkt->partner.system_priority));
|
||||
printf(" mac: %!", pkt->partner.system);
|
||||
printf(" key: %hx", ntohs(pkt->partner.key));
|
||||
printf(" port_pri: %hx", ntohs(pkt->partner.port_priority));
|
||||
printf(" port: %hx\n", ntohs(pkt->partner.port));
|
||||
printf(" state: ");
|
||||
print_lacp_state(pkt->partner.state);
|
||||
#if LACP_DEBUG > 1
|
||||
printf(" reserved: %hhx %hhx %hhx\n",
|
||||
pkt->partner.reserved[0], pkt->partner.reserved[1], pkt->partner.reserved[2]);
|
||||
#endif
|
||||
printf(")\n");
|
||||
printf("collector_tlv: %hhx ", pkt->tlv_type_collector_info);
|
||||
printf(" len: %hhx (", pkt->collector_information_length);
|
||||
printf(" max_delay: %hx", ntohs(pkt->collector_max_delay));
|
||||
#if LACP_DEBUG > 1
|
||||
printf("reserved_12: %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx\n",
|
||||
pkt->reserved_12[0], pkt->reserved_12[1], pkt->reserved_12[2],
|
||||
pkt->reserved_12[3], pkt->reserved_12[4], pkt->reserved_12[5],
|
||||
pkt->reserved_12[6], pkt->reserved_12[7], pkt->reserved_12[8],
|
||||
pkt->reserved_12[9], pkt->reserved_12[10], pkt->reserved_12[11]);
|
||||
#endif
|
||||
printf(" )\n");
|
||||
printf("terminator_tlv: %hhx", pkt->tlv_type_terminator);
|
||||
printf(" len: %hhx ()\n", pkt->terminator_length);
|
||||
}
|
||||
|
||||
static inline unsigned long lacp_timer_val(unsigned long now, unsigned long when)
|
||||
{
|
||||
return when?(when - now)/TICKS_PER_SEC : 0;
|
||||
}
|
||||
static void print_lacp(const char *which, struct slow_lacp *pkt, unsigned long now)
|
||||
{
|
||||
printf("%s\n", which);
|
||||
print_lacpdu(pkt);
|
||||
printf("timers: c %ds p %ds\n",
|
||||
lacp_timer_val(now, lacp.current_while_timer),
|
||||
lacp_timer_val(now, lacp.periodic_timer)
|
||||
);
|
||||
printf("\n");
|
||||
}
|
||||
#else /* LACP_DEBUG */
|
||||
#define print_lacp(which, pkt, now) do {} while(0)
|
||||
#endif /* LACP_DEBUG */
|
||||
|
||||
static void lacp_init_state(const uint8_t *mac)
|
||||
{
|
||||
memset(&lacp, 0, sizeof(lacp));
|
||||
|
||||
/* Initialize the packet constants */
|
||||
lacp.pkt.subtype = 1;
|
||||
lacp.pkt.version_number = 1;
|
||||
|
||||
|
||||
/* The default state of my interface */
|
||||
lacp.pkt.tlv_type_actor_info = LACP_TLV_ACTOR;
|
||||
lacp.pkt.actor_information_length = 0x14;
|
||||
lacp.pkt.actor.system_priority = htons(1);
|
||||
memcpy(lacp.pkt.actor.system, mac, ETH_ALEN);
|
||||
lacp.pkt.actor.key = htons(1);
|
||||
lacp.pkt.actor.port = htons(1);
|
||||
lacp.pkt.actor.port_priority = htons(1);
|
||||
lacp.pkt.actor.state =
|
||||
LACP_SYNCHRONIZATION |
|
||||
LACP_COLLECTING |
|
||||
LACP_DISTRIBUTING |
|
||||
LACP_DEFAULTED;
|
||||
|
||||
/* Set my partner defaults */
|
||||
lacp.pkt.tlv_type_partner_info = LACP_TLV_PARTNER;
|
||||
lacp.pkt.partner_information_length = 0x14;
|
||||
lacp.pkt.partner.system_priority = htons(1);
|
||||
/* memset(lacp.pkt.parnter_system, 0, ETH_ALEN); */
|
||||
lacp.pkt.partner.key = htons(1);
|
||||
lacp.pkt.partner.port = htons(1);
|
||||
lacp.pkt.partner.port_priority = htons(1);
|
||||
lacp.pkt.partner.state =
|
||||
LACP_ACTIVITY |
|
||||
LACP_SYNCHRONIZATION |
|
||||
LACP_COLLECTING |
|
||||
LACP_DISTRIBUTING |
|
||||
LACP_DEFAULTED;
|
||||
|
||||
lacp.pkt.tlv_type_collector_info = LACP_TLV_COLLECTOR;
|
||||
lacp.pkt.collector_information_length = 0x10;
|
||||
lacp.pkt.collector_max_delay = htons(0x8000); /* ???? */
|
||||
|
||||
lacp.pkt.tlv_type_terminator = LACP_TLV_TERMINATOR;
|
||||
lacp.pkt.terminator_length = 0;
|
||||
}
|
||||
|
||||
#define LACP_NTT_MASK (LACP_ACTIVITY | LACP_TIMEOUT | \
|
||||
LACP_SYNCHRONIZATION | LACP_AGGREGATION)
|
||||
|
||||
static inline int lacp_update_ntt(struct slow_lacp *pkt)
|
||||
{
|
||||
int ntt = 0;
|
||||
if ((memcmp(&pkt->partner, &lacp.pkt.actor, LACP_CMP_LEN) != 0) ||
|
||||
((pkt->partner.state & LACP_NTT_MASK) !=
|
||||
(lacp.pkt.actor.state & LACP_NTT_MASK)))
|
||||
{
|
||||
ntt = 1;
|
||||
}
|
||||
return ntt;
|
||||
}
|
||||
|
||||
static inline void lacp_record_pdu(struct slow_lacp *pkt)
|
||||
{
|
||||
memcpy(&lacp.pkt.partner, &pkt->actor, LACP_CP_LEN);
|
||||
|
||||
lacp.pkt.actor.state &= ~LACP_DEFAULTED;
|
||||
lacp.pkt.partner.state &= ~LACP_SYNCHRONIZATION;
|
||||
if ((memcmp(&pkt->partner, &lacp.pkt.actor, LACP_CMP_LEN) == 0) &&
|
||||
((pkt->partner.state & LACP_AGGREGATION) ==
|
||||
(lacp.pkt.actor.state & LACP_AGGREGATION)))
|
||||
{
|
||||
lacp.pkt.partner.state |= LACP_SYNCHRONIZATION;
|
||||
}
|
||||
if (!(pkt->actor.state & LACP_AGGREGATION)) {
|
||||
lacp.pkt.partner.state |= LACP_SYNCHRONIZATION;
|
||||
}
|
||||
|
||||
/* ACTIVITY? */
|
||||
}
|
||||
|
||||
static inline int lacp_timer_expired(unsigned long now, unsigned long when)
|
||||
{
|
||||
return when && (now > when);
|
||||
}
|
||||
|
||||
static inline void lacp_start_periodic_timer(unsigned long now)
|
||||
{
|
||||
if ((lacp.pkt.partner.state & LACP_ACTIVITY) ||
|
||||
(lacp.pkt.actor.state & LACP_ACTIVITY)) {
|
||||
lacp.periodic_timer = now +
|
||||
(((lacp.pkt.partner.state & LACP_TIMEOUT)?
|
||||
FAST_PERIODIC_TIME : SLOW_PERIODIC_TIME));
|
||||
}
|
||||
}
|
||||
|
||||
static inline void lacp_start_current_while_timer(unsigned long now)
|
||||
{
|
||||
lacp.current_while_timer = now +
|
||||
((lacp.pkt.actor.state & LACP_TIMEOUT) ?
|
||||
SHORT_TIMEOUT_TIME : LONG_TIMEOUT_TIME);
|
||||
|
||||
lacp.pkt.actor.state &= ~LACP_EXPIRED;
|
||||
}
|
||||
|
||||
static void send_lacp_reports(unsigned long now, int ntt)
|
||||
{
|
||||
if (memcmp(nic.node_addr, lacp.pkt.actor.system, ETH_ALEN) != 0) {
|
||||
lacp_init_state(nic.node_addr);
|
||||
}
|
||||
/* If the remote information has expired I need to take action */
|
||||
if (lacp_timer_expired(now, lacp.current_while_timer)) {
|
||||
if (!(lacp.pkt.actor.state & LACP_EXPIRED)) {
|
||||
lacp.pkt.partner.state &= ~LACP_SYNCHRONIZATION;
|
||||
lacp.pkt.partner.state |= LACP_TIMEOUT;
|
||||
lacp.pkt.actor.state |= LACP_EXPIRED;
|
||||
lacp.current_while_timer = now + SHORT_TIMEOUT_TIME;
|
||||
ntt = 1;
|
||||
}
|
||||
else {
|
||||
lacp_init_state(nic.node_addr);
|
||||
}
|
||||
}
|
||||
/* If the periodic timer has expired I need to transmit */
|
||||
if (lacp_timer_expired(now, lacp.periodic_timer)) {
|
||||
ntt = 1;
|
||||
/* Reset by lacp_start_periodic_timer */
|
||||
}
|
||||
if (ntt) {
|
||||
eth_transmit(slow_dest, ETH_P_SLOW, sizeof(lacp.pkt), &lacp.pkt);
|
||||
|
||||
/* Restart the periodic timer */
|
||||
lacp_start_periodic_timer(now);
|
||||
|
||||
print_lacp("Trasmitted", &lacp.pkt, now);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void send_eth_slow_reports(unsigned long now)
|
||||
{
|
||||
send_lacp_reports(now, 0);
|
||||
}
|
||||
|
||||
static inline void process_eth_slow(unsigned short ptype, unsigned long now)
|
||||
{
|
||||
union slow_union *pkt;
|
||||
if ((ptype != ETH_P_SLOW) ||
|
||||
(nic.packetlen < (ETH_HLEN + sizeof(pkt->header)))) {
|
||||
return;
|
||||
}
|
||||
pkt = (union slow_union *)&nic.packet[ETH_HLEN];
|
||||
if ((pkt->header.subtype == SLOW_SUBTYPE_LACP) &&
|
||||
(nic.packetlen >= ETH_HLEN + sizeof(pkt->lacp))) {
|
||||
int ntt;
|
||||
if (memcmp(nic.node_addr, lacp.pkt.actor.system, ETH_ALEN) != 0) {
|
||||
lacp_init_state(nic.node_addr);
|
||||
}
|
||||
/* As long as nic.packet is 2 byte aligned all is good */
|
||||
print_lacp("Received", &pkt->lacp, now);
|
||||
/* I don't actually implement the MUX or SELECT
|
||||
* machines.
|
||||
*
|
||||
* What logically happens when the client and I
|
||||
* disagree about an aggregator is the current
|
||||
* aggregtator is unselected. The MUX machine places
|
||||
* me in DETACHED. The SELECT machine runs and
|
||||
* reslects the same aggregator. If I go through
|
||||
* these steps fast enough an outside observer can not
|
||||
* notice this.
|
||||
*
|
||||
* Since the process will not generate any noticeable
|
||||
* effect it does not need an implmenetation. This
|
||||
* keeps the code simple and the code and binary
|
||||
* size down.
|
||||
*/
|
||||
/* lacp_update_selected(&pkt->lacp); */
|
||||
ntt = lacp_update_ntt(&pkt->lacp);
|
||||
lacp_record_pdu(&pkt->lacp);
|
||||
lacp_start_current_while_timer(now);
|
||||
send_lacp_reports(now, ntt);
|
||||
}
|
||||
/* If we receive a marker information packet return it */
|
||||
else if ((pkt->header.subtype == SLOW_SUBTYPE_MARKER) &&
|
||||
(nic.packetlen >= ETH_HLEN + sizeof(pkt->marker)) &&
|
||||
(pkt->marker.tlv_type == MARKER_TLV_INFO) &&
|
||||
(pkt->marker.marker_length == 0x16))
|
||||
{
|
||||
pkt->marker.tlv_type = MARKER_TLV_RESPONSE;
|
||||
eth_transmit(slow_dest, ETH_P_SLOW,
|
||||
sizeof(pkt->marker), &pkt->marker);
|
||||
}
|
||||
|
||||
}
|
||||
#else
|
||||
|
||||
#define send_eth_slow_reports(now) do {} while(0)
|
||||
#define process_eth_slow(ptype, now) do {} while(0)
|
||||
|
||||
#endif
|
||||
206
src/core/proto_http.c
Normal file
206
src/core/proto_http.c
Normal file
@@ -0,0 +1,206 @@
|
||||
#include "etherboot.h"
|
||||
#include "http.h"
|
||||
|
||||
#ifdef DOWNLOAD_PROTO_HTTP
|
||||
|
||||
/* The block size is currently chosen to be 512 bytes. This means, we can
|
||||
allocate the receive buffer on the stack, but it results in a noticeable
|
||||
performance penalty.
|
||||
This is what needs to be done in order to increase the block size:
|
||||
- size negotiation needs to be implemented in TCP
|
||||
- the buffer needs to be allocated on the heap
|
||||
- path MTU discovery needs to be implemented
|
||||
*/ /***/ /* FIXME */
|
||||
#define BLOCKSIZE TFTP_DEFAULTSIZE_PACKET
|
||||
|
||||
/**************************************************************************
|
||||
SEND_TCP_CALLBACK - Send data using TCP
|
||||
**************************************************************************/
|
||||
struct send_recv_state {
|
||||
int (*fnc)(unsigned char *data, int block, int len, int eof);
|
||||
char *send_buffer;
|
||||
char *recv_buffer;
|
||||
int send_length;
|
||||
int recv_length;
|
||||
int bytes_sent;
|
||||
int block;
|
||||
int bytes_received;
|
||||
enum { RESULT_CODE, HEADER, DATA, ERROR, MOVED } recv_state;
|
||||
int rc;
|
||||
char location[MAX_URL+1];
|
||||
};
|
||||
|
||||
static int send_tcp_request(int length, void *buffer, void *ptr) {
|
||||
struct send_recv_state *state = (struct send_recv_state *)ptr;
|
||||
|
||||
if (length > state->send_length - state->bytes_sent)
|
||||
length = state->send_length - state->bytes_sent;
|
||||
memcpy(buffer, state->send_buffer + state->bytes_sent, length);
|
||||
state->bytes_sent += length;
|
||||
return (length);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
RECV_TCP_CALLBACK - Receive data using TCP
|
||||
**************************************************************************/
|
||||
static int recv_tcp_request(int length, const void *buffer, void *ptr) {
|
||||
struct send_recv_state *state = (struct send_recv_state *)ptr;
|
||||
|
||||
/* Assume that the lines in an HTTP header do not straddle a packet */
|
||||
/* boundary. This is probably a reasonable assumption */
|
||||
if (state->recv_state == RESULT_CODE) {
|
||||
while (length > 0) {
|
||||
/* Find HTTP result code */
|
||||
if (*(const char *)buffer == ' ') {
|
||||
const char *ptr = ((const char *)buffer) + 1;
|
||||
int rc = strtoul(ptr, &ptr, 10);
|
||||
if (ptr >= (const char *)buffer + length) {
|
||||
state->recv_state = ERROR;
|
||||
return 0;
|
||||
}
|
||||
state->rc = rc;
|
||||
state->recv_state = HEADER;
|
||||
goto header;
|
||||
}
|
||||
++(const char *)buffer;
|
||||
length--;
|
||||
}
|
||||
state->recv_state = ERROR;
|
||||
return 0;
|
||||
}
|
||||
if (state->recv_state == HEADER) {
|
||||
header: while (length > 0) {
|
||||
/* Check for HTTP redirect */
|
||||
if (state->rc >= 300 && state->rc < 400 &&
|
||||
!memcmp(buffer, "Location: ", 10)) {
|
||||
char *ptr = state->location;
|
||||
int i;
|
||||
memcpy(ptr, buffer + 10, MAX_URL);
|
||||
for (i = 0; i < MAX_URL && *ptr > ' ';
|
||||
i++, ptr++);
|
||||
*ptr = '\000';
|
||||
state->recv_state = MOVED;
|
||||
return 1;
|
||||
}
|
||||
/* Find beginning of line */
|
||||
while (length > 0) {
|
||||
length--;
|
||||
if (*((const char *)buffer)++ == '\n')
|
||||
break;
|
||||
}
|
||||
/* Check for end of header */
|
||||
if (length >= 2 && !memcmp(buffer, "\r\n", 2)) {
|
||||
state->recv_state = DATA;
|
||||
buffer += 2;
|
||||
length -= 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (state->recv_state == DATA) {
|
||||
state->bytes_received += length;
|
||||
while (length > 0) {
|
||||
int copy_length = BLOCKSIZE - state->recv_length;
|
||||
if (copy_length > length)
|
||||
copy_length = length;
|
||||
memcpy(state->recv_buffer + state->recv_length,
|
||||
buffer, copy_length);
|
||||
if ((state->recv_length += copy_length) == BLOCKSIZE) {
|
||||
if (!state->fnc(state->recv_buffer,
|
||||
++state->block, BLOCKSIZE, 0))
|
||||
return 0;
|
||||
state->recv_length = 0;
|
||||
}
|
||||
length -= copy_length;
|
||||
buffer += copy_length;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
HTTP_GET - Get data using HTTP
|
||||
**************************************************************************/
|
||||
int http(const char *url,
|
||||
int (*fnc)(unsigned char *, unsigned int, unsigned int, int)) {
|
||||
static const char GET[] = "GET /%s HTTP/1.0\r\n\r\n";
|
||||
static char recv_buffer[BLOCKSIZE];
|
||||
in_addr destip;
|
||||
int port;
|
||||
int length;
|
||||
struct send_recv_state state;
|
||||
|
||||
state.fnc = fnc;
|
||||
state.rc = -1;
|
||||
state.block = 0;
|
||||
state.recv_buffer = recv_buffer;
|
||||
length = strlen(url);
|
||||
if (length <= MAX_URL) {
|
||||
memcpy(state.location, url, length+1);
|
||||
destip = arptable[ARP_SERVER].ipaddr;
|
||||
port = url_port;
|
||||
if (port == -1)
|
||||
port = 80;
|
||||
goto first_time;
|
||||
|
||||
do {
|
||||
state.rc = -1;
|
||||
state.block = 0;
|
||||
url = state.location;
|
||||
if (memcmp("http://", url, 7))
|
||||
break;
|
||||
url += 7;
|
||||
length = inet_aton(url, &destip);
|
||||
if (!length) {
|
||||
/* As we do not have support for DNS, assume*/
|
||||
/* that HTTP redirects always point to the */
|
||||
/* same machine */
|
||||
if (state.recv_state == MOVED) {
|
||||
while (*url &&
|
||||
*url != ':' && *url != '/') url++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (*(url += length) == ':') {
|
||||
port = strtoul(url, &url, 10);
|
||||
} else {
|
||||
port = 80;
|
||||
}
|
||||
if (!*url)
|
||||
url = "/";
|
||||
if (*url != '/')
|
||||
break;
|
||||
url++;
|
||||
|
||||
first_time:
|
||||
length = strlen(url);
|
||||
state.send_length = sizeof(GET) - 3 + length;
|
||||
|
||||
{ char buf[state.send_length + 1];
|
||||
sprintf(state.send_buffer = buf, GET, url);
|
||||
state.bytes_sent = 0;
|
||||
|
||||
state.bytes_received = 0;
|
||||
state.recv_state = RESULT_CODE;
|
||||
|
||||
state.recv_length = 0;
|
||||
tcp_transaction(destip.s_addr, 80, &state,
|
||||
send_tcp_request, recv_tcp_request);
|
||||
}
|
||||
} while (state.recv_state == MOVED);
|
||||
} else {
|
||||
memcpy(state.location, url, MAX_URL);
|
||||
state.location[MAX_URL] = '\000';
|
||||
}
|
||||
|
||||
if (state.rc == 200) {
|
||||
return fnc(recv_buffer, ++state.block, state.recv_length, 1);
|
||||
} else {
|
||||
printf("Failed to download %s (rc = %d)\n",
|
||||
state.location, state.rc);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* DOWNLOAD_PROTO_HTTP */
|
||||
541
src/core/proto_slam.c
Normal file
541
src/core/proto_slam.c
Normal file
@@ -0,0 +1,541 @@
|
||||
#ifdef DOWNLOAD_PROTO_SLAM
|
||||
#include "etherboot.h"
|
||||
#include "nic.h"
|
||||
|
||||
#define SLAM_PORT 10000
|
||||
#define SLAM_MULTICAST_IP ((239<<24)|(255<<16)|(1<<8)|(1<<0))
|
||||
#define SLAM_MULTICAST_PORT 10000
|
||||
#define SLAM_LOCAL_PORT 10000
|
||||
|
||||
/* Set the timeout intervals to at least 1 second so
|
||||
* on a 100Mbit ethernet can receive 10000 packets
|
||||
* in one second.
|
||||
*
|
||||
* The only case that is likely to trigger all of the nodes
|
||||
* firing a nack packet is a slow server. The odds of this
|
||||
* happening could be reduced being slightly smarter and utilizing
|
||||
* the multicast channels for nacks. But that only improves the odds
|
||||
* it doesn't improve the worst case. So unless this proves to be
|
||||
* a common case having the control data going unicast should increase
|
||||
* the odds of the data not being dropped.
|
||||
*
|
||||
* When doing exponential backoff we increase just the timeout
|
||||
* interval and not the base to optimize for throughput. This is only
|
||||
* expected to happen when the server is down. So having some nodes
|
||||
* pinging immediately should get the transmission restarted quickly after a
|
||||
* server restart. The host nic won't be to baddly swamped because of
|
||||
* the random distribution of the nodes.
|
||||
*
|
||||
*/
|
||||
#define SLAM_INITIAL_MIN_TIMEOUT (TICKS_PER_SEC/3)
|
||||
#define SLAM_INITIAL_TIMEOUT_INTERVAL (TICKS_PER_SEC)
|
||||
#define SLAM_BASE_MIN_TIMEOUT (2*TICKS_PER_SEC)
|
||||
#define SLAM_BASE_TIMEOUT_INTERVAL (4*TICKS_PER_SEC)
|
||||
#define SLAM_BACKOFF_LIMIT 5
|
||||
#define SLAM_MAX_RETRIES 20
|
||||
|
||||
/*** Packets Formats ***
|
||||
* Data Packet:
|
||||
* transaction
|
||||
* total bytes
|
||||
* block size
|
||||
* packet #
|
||||
* data
|
||||
*
|
||||
* Status Request Packet
|
||||
* transaction
|
||||
* total bytes
|
||||
* block size
|
||||
*
|
||||
* Status Packet
|
||||
* received packets
|
||||
* requested packets
|
||||
* received packets
|
||||
* requested packets
|
||||
* ...
|
||||
* received packets
|
||||
* requested packtes
|
||||
* 0
|
||||
*/
|
||||
|
||||
#define MAX_HDR (7 + 7 + 7) /* transaction, total size, block size */
|
||||
#define MIN_HDR (1 + 1 + 1) /* transactino, total size, block size */
|
||||
|
||||
#define MAX_SLAM_REQUEST MAX_HDR
|
||||
#define MIN_SLAM_REQUEST MIN_HDR
|
||||
|
||||
#define MIN_SLAM_DATA (MIN_HDR + 1)
|
||||
|
||||
static struct slam_nack {
|
||||
struct iphdr ip;
|
||||
struct udphdr udp;
|
||||
unsigned char data[ETH_MAX_MTU -
|
||||
(sizeof(struct iphdr) + sizeof(struct udphdr))];
|
||||
} nack;
|
||||
|
||||
struct slam_state {
|
||||
unsigned char hdr[MAX_HDR];
|
||||
unsigned long hdr_len;
|
||||
unsigned long block_size;
|
||||
unsigned long total_bytes;
|
||||
unsigned long total_packets;
|
||||
|
||||
unsigned long received_packets;
|
||||
|
||||
unsigned char *image;
|
||||
unsigned char *bitmap;
|
||||
} state;
|
||||
|
||||
|
||||
static void init_slam_state(void)
|
||||
{
|
||||
state.hdr_len = sizeof(state.hdr);
|
||||
memset(state.hdr, 0, state.hdr_len);
|
||||
state.block_size = 0;
|
||||
state.total_packets = 0;
|
||||
|
||||
state.received_packets = 0;
|
||||
|
||||
state.image = 0;
|
||||
state.bitmap = 0;
|
||||
}
|
||||
|
||||
struct slam_info {
|
||||
in_addr server_ip;
|
||||
in_addr multicast_ip;
|
||||
in_addr local_ip;
|
||||
uint16_t server_port;
|
||||
uint16_t multicast_port;
|
||||
uint16_t local_port;
|
||||
int (*fnc)(unsigned char *, unsigned int, unsigned int, int);
|
||||
int sent_nack;
|
||||
};
|
||||
|
||||
#define SLAM_TIMEOUT 0
|
||||
#define SLAM_REQUEST 1
|
||||
#define SLAM_DATA 2
|
||||
static int await_slam(int ival __unused, void *ptr,
|
||||
unsigned short ptype __unused, struct iphdr *ip, struct udphdr *udp)
|
||||
{
|
||||
struct slam_info *info = ptr;
|
||||
if (!udp) {
|
||||
return 0;
|
||||
}
|
||||
/* I can receive two kinds of packets here, a multicast data packet,
|
||||
* or a unicast request for information
|
||||
*/
|
||||
/* Check for a data request packet */
|
||||
if ((ip->dest.s_addr == arptable[ARP_CLIENT].ipaddr.s_addr) &&
|
||||
(ntohs(udp->dest) == info->local_port) &&
|
||||
(nic.packetlen >=
|
||||
ETH_HLEN +
|
||||
sizeof(struct iphdr) +
|
||||
sizeof(struct udphdr) +
|
||||
MIN_SLAM_REQUEST)) {
|
||||
return SLAM_REQUEST;
|
||||
}
|
||||
/* Check for a multicast data packet */
|
||||
if ((ip->dest.s_addr == info->multicast_ip.s_addr) &&
|
||||
(ntohs(udp->dest) == info->multicast_port) &&
|
||||
(nic.packetlen >=
|
||||
ETH_HLEN +
|
||||
sizeof(struct iphdr) +
|
||||
sizeof(struct udphdr) +
|
||||
MIN_SLAM_DATA)) {
|
||||
return SLAM_DATA;
|
||||
}
|
||||
#if 0
|
||||
printf("#");
|
||||
printf("dest: %@ port: %d len: %d\n",
|
||||
ip->dest.s_addr, ntohs(udp->dest), nic.packetlen);
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static int slam_encode(
|
||||
unsigned char **ptr, unsigned char *end, unsigned long value)
|
||||
{
|
||||
unsigned char *data = *ptr;
|
||||
int bytes;
|
||||
bytes = sizeof(value);
|
||||
while ((bytes > 0) && ((0xff & (value >> ((bytes -1)<<3))) == 0)) {
|
||||
bytes--;
|
||||
}
|
||||
if (bytes <= 0) {
|
||||
bytes = 1;
|
||||
}
|
||||
if (data + bytes >= end) {
|
||||
return -1;
|
||||
}
|
||||
if ((0xe0 & (value >> ((bytes -1)<<3))) == 0) {
|
||||
/* packed together */
|
||||
*data = (bytes << 5) | (value >> ((bytes -1)<<3));
|
||||
} else {
|
||||
bytes++;
|
||||
*data = (bytes << 5);
|
||||
}
|
||||
bytes--;
|
||||
data++;
|
||||
while(bytes) {
|
||||
*(data++) = 0xff & (value >> ((bytes -1)<<3));
|
||||
bytes--;
|
||||
}
|
||||
*ptr = data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int slam_skip(unsigned char **ptr, unsigned char *end)
|
||||
{
|
||||
int bytes;
|
||||
if (*ptr >= end) {
|
||||
return -1;
|
||||
}
|
||||
bytes = ((**ptr) >> 5) & 7;
|
||||
if (bytes == 0) {
|
||||
return -1;
|
||||
}
|
||||
if (*ptr + bytes >= end) {
|
||||
return -1;
|
||||
}
|
||||
(*ptr) += bytes;
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static unsigned long slam_decode(unsigned char **ptr, unsigned char *end, int *err)
|
||||
{
|
||||
unsigned long value;
|
||||
unsigned bytes;
|
||||
if (*ptr >= end) {
|
||||
*err = -1;
|
||||
}
|
||||
bytes = ((**ptr) >> 5) & 7;
|
||||
if ((bytes == 0) || (bytes > sizeof(unsigned long))) {
|
||||
*err = -1;
|
||||
return 0;
|
||||
}
|
||||
if ((*ptr) + bytes >= end) {
|
||||
*err = -1;
|
||||
}
|
||||
value = (**ptr) & 0x1f;
|
||||
bytes--;
|
||||
(*ptr)++;
|
||||
while(bytes) {
|
||||
value <<= 8;
|
||||
value |= **ptr;
|
||||
(*ptr)++;
|
||||
bytes--;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
static long slam_sleep_interval(int exp)
|
||||
{
|
||||
long range;
|
||||
long divisor;
|
||||
long interval;
|
||||
range = SLAM_BASE_TIMEOUT_INTERVAL;
|
||||
if (exp < 0) {
|
||||
divisor = RAND_MAX/SLAM_INITIAL_TIMEOUT_INTERVAL;
|
||||
} else {
|
||||
if (exp > SLAM_BACKOFF_LIMIT)
|
||||
exp = SLAM_BACKOFF_LIMIT;
|
||||
divisor = RAND_MAX/(range << exp);
|
||||
}
|
||||
interval = random()/divisor;
|
||||
if (exp < 0) {
|
||||
interval += SLAM_INITIAL_MIN_TIMEOUT;
|
||||
} else {
|
||||
interval += SLAM_BASE_MIN_TIMEOUT;
|
||||
}
|
||||
return interval;
|
||||
}
|
||||
|
||||
|
||||
static unsigned char *reinit_slam_state(
|
||||
unsigned char *header, unsigned char *end)
|
||||
{
|
||||
unsigned long total_bytes;
|
||||
unsigned long block_size;
|
||||
|
||||
unsigned long bitmap_len;
|
||||
unsigned long max_packet_len;
|
||||
unsigned char *data;
|
||||
int err;
|
||||
|
||||
#if 0
|
||||
printf("reinit\n");
|
||||
#endif
|
||||
data = header;
|
||||
|
||||
state.hdr_len = 0;
|
||||
err = slam_skip(&data, end); /* transaction id */
|
||||
total_bytes = slam_decode(&data, end, &err);
|
||||
block_size = slam_decode(&data, end, &err);
|
||||
if (err) {
|
||||
printf("ALERT: slam size out of range\n");
|
||||
return 0;
|
||||
}
|
||||
state.block_size = block_size;
|
||||
state.total_bytes = total_bytes;
|
||||
state.total_packets = (total_bytes + block_size - 1)/block_size;
|
||||
state.hdr_len = data - header;
|
||||
state.received_packets = 0;
|
||||
|
||||
data = state.hdr;
|
||||
slam_encode(&data, &state.hdr[sizeof(state.hdr)], state.total_packets);
|
||||
max_packet_len = data - state.hdr;
|
||||
memcpy(state.hdr, header, state.hdr_len);
|
||||
|
||||
#if 0
|
||||
printf("block_size: %ld\n", block_size);
|
||||
printf("total_bytes: %ld\n", total_bytes);
|
||||
printf("total_packets: %ld\n", state.total_packets);
|
||||
printf("hdr_len: %ld\n", state.hdr_len);
|
||||
printf("max_packet_len: %ld\n", max_packet_len);
|
||||
#endif
|
||||
|
||||
if (state.block_size > ETH_MAX_MTU - (
|
||||
sizeof(struct iphdr) + sizeof(struct udphdr) +
|
||||
state.hdr_len + max_packet_len)) {
|
||||
printf("ALERT: slam blocksize to large\n");
|
||||
return 0;
|
||||
}
|
||||
if (state.bitmap) {
|
||||
forget(state.bitmap);
|
||||
}
|
||||
bitmap_len = (state.total_packets + 1 + 7)/8;
|
||||
state.bitmap = allot(bitmap_len);
|
||||
state.image = allot(total_bytes);
|
||||
if ((unsigned long)state.image < 1024*1024) {
|
||||
printf("ALERT: slam filesize to large for available memory\n");
|
||||
return 0;
|
||||
}
|
||||
memset(state.bitmap, 0, bitmap_len);
|
||||
|
||||
return header + state.hdr_len;
|
||||
}
|
||||
|
||||
static int slam_recv_data(unsigned char *data)
|
||||
{
|
||||
unsigned long packet;
|
||||
unsigned long data_len;
|
||||
int err;
|
||||
struct udphdr *udp;
|
||||
udp = (struct udphdr *)&nic.packet[ETH_HLEN + sizeof(struct iphdr)];
|
||||
err = 0;
|
||||
packet = slam_decode(&data, &nic.packet[nic.packetlen], &err);
|
||||
if (err || (packet > state.total_packets)) {
|
||||
printf("ALERT: Invalid packet number\n");
|
||||
return 0;
|
||||
}
|
||||
/* Compute the expected data length */
|
||||
if (packet != state.total_packets -1) {
|
||||
data_len = state.block_size;
|
||||
} else {
|
||||
data_len = state.total_bytes % state.block_size;
|
||||
}
|
||||
/* If the packet size is wrong drop the packet and then continue */
|
||||
if (ntohs(udp->len) != (data_len + (data - (unsigned char*)udp))) {
|
||||
printf("ALERT: udp packet is not the correct size\n");
|
||||
return 1;
|
||||
}
|
||||
if (nic.packetlen < data_len + (data - nic.packet)) {
|
||||
printf("ALERT: Ethernet packet shorter than data_len\n");
|
||||
return 1;
|
||||
}
|
||||
if (data_len > state.block_size) {
|
||||
data_len = state.block_size;
|
||||
}
|
||||
if (((state.bitmap[packet >> 3] >> (packet & 7)) & 1) == 0) {
|
||||
/* Non duplicate packet */
|
||||
state.bitmap[packet >> 3] |= (1 << (packet & 7));
|
||||
memcpy(state.image + (packet*state.block_size), data, data_len);
|
||||
state.received_packets++;
|
||||
} else {
|
||||
#ifdef MDEBUG
|
||||
printf("<DUP>\n");
|
||||
#endif
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void transmit_nack(unsigned char *ptr, struct slam_info *info)
|
||||
{
|
||||
int nack_len;
|
||||
/* Ensure the packet is null terminated */
|
||||
*ptr++ = 0;
|
||||
nack_len = ptr - (unsigned char *)&nack;
|
||||
build_udp_hdr(info->server_ip.s_addr,
|
||||
info->local_port, info->server_port, 1, nack_len, &nack);
|
||||
ip_transmit(nack_len, &nack);
|
||||
#if defined(MDEBUG) && 0
|
||||
printf("Sent NACK to %@ bytes: %d have:%ld/%ld\n",
|
||||
info->server_ip, nack_len,
|
||||
state.received_packets, state.total_packets);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void slam_send_nack(struct slam_info *info)
|
||||
{
|
||||
unsigned char *ptr, *end;
|
||||
/* Either I timed out or I was explicitly
|
||||
* asked for a request packet
|
||||
*/
|
||||
ptr = &nack.data[0];
|
||||
/* Reserve space for the trailling null */
|
||||
end = &nack.data[sizeof(nack.data) -1];
|
||||
if (!state.bitmap) {
|
||||
slam_encode(&ptr, end, 0);
|
||||
slam_encode(&ptr, end, 1);
|
||||
}
|
||||
else {
|
||||
/* Walk the bitmap */
|
||||
unsigned long i;
|
||||
unsigned long len;
|
||||
unsigned long max;
|
||||
int value;
|
||||
int last;
|
||||
/* Compute the last bit and store an inverted trailer */
|
||||
max = state.total_packets;
|
||||
value = ((state.bitmap[(max -1) >> 3] >> ((max -1) & 7) ) & 1);
|
||||
value = !value;
|
||||
state.bitmap[max >> 3] &= ~(1 << (max & 7));
|
||||
state.bitmap[max >> 3] |= value << (max & 7);
|
||||
|
||||
len = 0;
|
||||
last = 1; /* Start with the received packets */
|
||||
for(i = 0; i <= max; i++) {
|
||||
value = (state.bitmap[i>>3] >> (i & 7)) & 1;
|
||||
if (value == last) {
|
||||
len++;
|
||||
} else {
|
||||
if (slam_encode(&ptr, end, len))
|
||||
break;
|
||||
last = value;
|
||||
len = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
info->sent_nack = 1;
|
||||
transmit_nack(ptr, info);
|
||||
}
|
||||
|
||||
static void slam_send_disconnect(struct slam_info *info)
|
||||
{
|
||||
if (info->sent_nack) {
|
||||
/* A disconnect is a packet with just the null terminator */
|
||||
transmit_nack(&nack.data[0], info);
|
||||
}
|
||||
info->sent_nack = 0;
|
||||
}
|
||||
|
||||
|
||||
static int proto_slam(struct slam_info *info)
|
||||
{
|
||||
int retry;
|
||||
long timeout;
|
||||
|
||||
init_slam_state();
|
||||
|
||||
retry = -1;
|
||||
rx_qdrain();
|
||||
/* Arp for my server */
|
||||
if (arptable[ARP_SERVER].ipaddr.s_addr != info->server_ip.s_addr) {
|
||||
arptable[ARP_SERVER].ipaddr.s_addr = info->server_ip.s_addr;
|
||||
memset(arptable[ARP_SERVER].node, 0, ETH_ALEN);
|
||||
}
|
||||
/* If I'm running over multicast join the multicast group */
|
||||
join_group(IGMP_SERVER, info->multicast_ip.s_addr);
|
||||
for(;;) {
|
||||
unsigned char *header;
|
||||
unsigned char *data;
|
||||
int type;
|
||||
header = data = 0;
|
||||
|
||||
timeout = slam_sleep_interval(retry);
|
||||
type = await_reply(await_slam, 0, info, timeout);
|
||||
/* Compute the timeout for next time */
|
||||
if (type == SLAM_TIMEOUT) {
|
||||
/* If I timeouted recompute the next timeout */
|
||||
if (retry++ > SLAM_MAX_RETRIES) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
retry = 0;
|
||||
}
|
||||
if ((type == SLAM_DATA) || (type == SLAM_REQUEST)) {
|
||||
/* Check the incomming packet and reinit the data
|
||||
* structures if necessary.
|
||||
*/
|
||||
header = &nic.packet[ETH_HLEN +
|
||||
sizeof(struct iphdr) + sizeof(struct udphdr)];
|
||||
data = header + state.hdr_len;
|
||||
if (memcmp(state.hdr, header, state.hdr_len) != 0) {
|
||||
/* Something is fishy reset the transaction */
|
||||
data = reinit_slam_state(header, &nic.packet[nic.packetlen]);
|
||||
if (!data) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (type == SLAM_DATA) {
|
||||
if (!slam_recv_data(data)) {
|
||||
return 0;
|
||||
}
|
||||
if (state.received_packets == state.total_packets) {
|
||||
/* We are done get out */
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((type == SLAM_TIMEOUT) || (type == SLAM_REQUEST)) {
|
||||
/* Either I timed out or I was explicitly
|
||||
* asked by a request packet
|
||||
*/
|
||||
slam_send_nack(info);
|
||||
}
|
||||
}
|
||||
slam_send_disconnect(info);
|
||||
|
||||
/* Leave the multicast group */
|
||||
leave_group(IGMP_SERVER);
|
||||
/* FIXME don't overwrite myself */
|
||||
/* load file to correct location */
|
||||
return info->fnc(state.image, 1, state.total_bytes, 1);
|
||||
}
|
||||
|
||||
|
||||
int url_slam(const char *name, int (*fnc)(unsigned char *, unsigned int, unsigned int, int))
|
||||
{
|
||||
struct slam_info info;
|
||||
/* Set the defaults */
|
||||
info.server_ip.s_addr = arptable[ARP_SERVER].ipaddr.s_addr;
|
||||
info.server_port = SLAM_PORT;
|
||||
info.multicast_ip.s_addr = htonl(SLAM_MULTICAST_IP);
|
||||
info.multicast_port = SLAM_MULTICAST_PORT;
|
||||
info.local_ip.s_addr = arptable[ARP_CLIENT].ipaddr.s_addr;
|
||||
info.local_port = SLAM_LOCAL_PORT;
|
||||
info.fnc = fnc;
|
||||
info.sent_nack = 0;
|
||||
/* Now parse the url */
|
||||
if (url_port != -1) {
|
||||
info.server_port = url_port;
|
||||
}
|
||||
if (name[0]) {
|
||||
/* multicast ip */
|
||||
name += inet_aton(name, &info.multicast_ip);
|
||||
if (name[0] == ':') {
|
||||
name++;
|
||||
info.multicast_port = strtoul(name, &name, 10);
|
||||
}
|
||||
}
|
||||
if (name[0]) {
|
||||
printf("\nBad url\n");
|
||||
return 0;
|
||||
}
|
||||
return proto_slam(&info);
|
||||
}
|
||||
|
||||
#endif /* DOWNLOAD_PROTO_SLAM */
|
||||
491
src/core/proto_tftm.c
Normal file
491
src/core/proto_tftm.c
Normal file
@@ -0,0 +1,491 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* proto_tftm.c -- Etherboot Multicast TFTP
|
||||
* Written 2003-2003 by Timothy Legge <tlegge@rogers.com>
|
||||
*
|
||||
* 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 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* This code is based on the DOWNLOAD_PROTO_TFTM section of
|
||||
* Etherboot 5.3 core/nic.c and:
|
||||
*
|
||||
* Anselm Martin Hoffmeister's previous proto_tftm.c multicast work
|
||||
* Eric Biederman's proto_slam.c
|
||||
*
|
||||
* $Revision$
|
||||
* $Author$
|
||||
* $Date$
|
||||
*
|
||||
* REVISION HISTORY:
|
||||
* ================
|
||||
* 09-07-2003 timlegge Release Version, Capable of Multicast Booting
|
||||
* 08-30-2003 timlegge Initial version, Assumes consecutive blocks
|
||||
*
|
||||
* Indent Options: indent -kr -i8
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef DOWNLOAD_PROTO_TFTM
|
||||
#include "etherboot.h"
|
||||
#include "nic.h"
|
||||
|
||||
//#define TFTM_DEBUG
|
||||
#ifdef TFTM_DEBUG
|
||||
#define debug(x) printf x
|
||||
#else
|
||||
#define debug(x)
|
||||
#endif
|
||||
struct tftm_info {
|
||||
in_addr server_ip;
|
||||
in_addr multicast_ip;
|
||||
in_addr local_ip;
|
||||
uint16_t server_port;
|
||||
uint16_t multicast_port;
|
||||
uint16_t local_port;
|
||||
int (*fnc) (unsigned char *, unsigned int, unsigned int, int);
|
||||
int sent_nack;
|
||||
const char *name; /* Filename */
|
||||
};
|
||||
|
||||
struct tftm_state {
|
||||
unsigned long block_size;
|
||||
unsigned long total_bytes;
|
||||
unsigned long total_packets;
|
||||
char ismaster;
|
||||
unsigned long received_packets;
|
||||
unsigned char *image;
|
||||
unsigned char *bitmap;
|
||||
char recvd_oack;
|
||||
} state;
|
||||
|
||||
#define TFTM_PORT 1758
|
||||
#define TFTM_MIN_PACKET 1024
|
||||
|
||||
|
||||
int opt_get_multicast(struct tftp_t *tr, unsigned short *len,
|
||||
unsigned long *filesize, struct tftm_info *info);
|
||||
|
||||
static int await_tftm(int ival, void *ptr, unsigned short ptype __unused,
|
||||
struct iphdr *ip, struct udphdr *udp)
|
||||
{
|
||||
struct tftm_info *info = ptr;
|
||||
|
||||
/* Check for Unicast data being received */
|
||||
if (ip->dest.s_addr == arptable[ARP_CLIENT].ipaddr.s_addr) {
|
||||
if (!udp) {
|
||||
return 0;
|
||||
}
|
||||
if (arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr)
|
||||
return 0;
|
||||
if (ntohs(udp->dest) != ival)
|
||||
return 0;
|
||||
|
||||
return 1; /* Unicast Data Received */
|
||||
}
|
||||
|
||||
/* Also check for Multicast data being received */
|
||||
if ((ip->dest.s_addr == info->multicast_ip.s_addr) &&
|
||||
(ntohs(udp->dest) == info->multicast_port) &&
|
||||
(nic.packetlen >= ETH_HLEN + sizeof(struct iphdr) +
|
||||
sizeof(struct udphdr))) {
|
||||
return 1; /* Multicast data received */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int proto_tftm(struct tftm_info *info)
|
||||
{
|
||||
int retry = 0;
|
||||
static unsigned short iport = 2000;
|
||||
unsigned short oport = 0;
|
||||
unsigned short len, block = 0, prevblock = 0;
|
||||
struct tftp_t *tr;
|
||||
struct tftpreq_t tp;
|
||||
unsigned long filesize = 0;
|
||||
|
||||
state.image = 0;
|
||||
state.bitmap = 0;
|
||||
|
||||
rx_qdrain();
|
||||
|
||||
/* Warning: the following assumes the layout of bootp_t.
|
||||
But that's fixed by the IP, UDP and BOOTP specs. */
|
||||
|
||||
/* Send a tftm-request to the server */
|
||||
tp.opcode = htons(TFTP_RRQ); /* Const for "\0x0" "\0x1" =^= ReadReQuest */
|
||||
len =
|
||||
sizeof(tp.ip) + sizeof(tp.udp) + sizeof(tp.opcode) +
|
||||
sprintf((char *) tp.u.rrq,
|
||||
"%s%coctet%cmulticast%c%cblksize%c%d%ctsize%c",
|
||||
info->name, 0, 0, 0, 0, 0, TFTM_MIN_PACKET, 0, 0) + 1;
|
||||
|
||||
if (!udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, ++iport,
|
||||
TFTM_PORT, len, &tp))
|
||||
return (0);
|
||||
|
||||
/* loop to listen for packets and to receive the file */
|
||||
for (;;) {
|
||||
long timeout;
|
||||
#ifdef CONGESTED
|
||||
timeout =
|
||||
rfc2131_sleep_interval(block ? TFTP_REXMT : TIMEOUT,
|
||||
retry);
|
||||
#else
|
||||
timeout = rfc2131_sleep_interval(TIMEOUT, retry);
|
||||
#endif
|
||||
/* Calls the await_reply function in nic.c which in turn calls
|
||||
await_tftm (1st parameter) as above */
|
||||
if (!await_reply(await_tftm, iport, info, timeout)) {
|
||||
if (!block && retry++ < MAX_TFTP_RETRIES) { /* maybe initial request was lost */
|
||||
if (!udp_transmit
|
||||
(arptable[ARP_SERVER].ipaddr.s_addr,
|
||||
++iport, TFTM_PORT, len, &tp))
|
||||
return (0);
|
||||
continue;
|
||||
}
|
||||
#ifdef CONGESTED
|
||||
if (block && ((retry += TFTP_REXMT) < TFTP_TIMEOUT)) { /* we resend our last ack */
|
||||
#ifdef MDEBUG
|
||||
printf("<REXMT>\n");
|
||||
#endif
|
||||
debug(("Timed out receiving file"));
|
||||
len =
|
||||
sizeof(tp.ip) + sizeof(tp.udp) +
|
||||
sizeof(tp.opcode) +
|
||||
sprintf((char *) tp.u.rrq,
|
||||
"%s%coctet%cmulticast%c%cblksize%c%d%ctsize%c",
|
||||
info->name, 0, 0, 0, 0, 0,
|
||||
TFTM_MIN_PACKET, 0, 0) + 1;
|
||||
|
||||
udp_transmit
|
||||
(arptable[ARP_SERVER].ipaddr.s_addr,
|
||||
++iport, TFTM_PORT, len, &tp);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
break; /* timeout */
|
||||
}
|
||||
|
||||
tr = (struct tftp_t *) &nic.packet[ETH_HLEN];
|
||||
|
||||
if (tr->opcode == ntohs(TFTP_ERROR)) {
|
||||
printf("TFTP error %d (%s)\n",
|
||||
ntohs(tr->u.err.errcode), tr->u.err.errmsg);
|
||||
break;
|
||||
}
|
||||
|
||||
if (tr->opcode == ntohs(TFTP_OACK)) {
|
||||
int i =
|
||||
opt_get_multicast(tr, &len, &filesize, info);
|
||||
|
||||
if (i == 0 || (i != 7 && !state.recvd_oack)) { /* Multicast unsupported */
|
||||
/* Transmit an error message to the server to end the transmission */
|
||||
printf
|
||||
("TFTM-Server doesn't understand options [blksize tsize multicast]\n");
|
||||
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) +
|
||||
/*
|
||||
* Normally bad form to omit the format string, but in this case
|
||||
* the string we are copying from is fixed. sprintf is just being
|
||||
* used as a strcpy and strlen.
|
||||
*/
|
||||
sprintf((char *) tp.u.err.errmsg,
|
||||
"RFC2090 error") + 1;
|
||||
udp_transmit(arptable[ARP_SERVER].ipaddr.
|
||||
s_addr, iport,
|
||||
ntohs(tr->udp.src), len, &tp);
|
||||
block = tp.u.ack.block = 0; /* this ensures, that */
|
||||
/* the packet does not get */
|
||||
/* processed as data! */
|
||||
return (0);
|
||||
} else {
|
||||
unsigned long bitmap_len;
|
||||
/* */
|
||||
if (!state.recvd_oack) {
|
||||
|
||||
state.total_packets =
|
||||
1 + (filesize -
|
||||
(filesize %
|
||||
state.block_size)) /
|
||||
state.block_size;
|
||||
bitmap_len =
|
||||
(state.total_packets + 7) / 8;
|
||||
if (!state.image) {
|
||||
state.bitmap =
|
||||
allot(bitmap_len);
|
||||
state.image =
|
||||
allot(filesize);
|
||||
|
||||
if ((unsigned long) state.
|
||||
image < 1024 * 1024) {
|
||||
printf
|
||||
("ALERT: tftp filesize to large for available memory\n");
|
||||
return 0;
|
||||
}
|
||||
memset(state.bitmap, 0,
|
||||
bitmap_len);
|
||||
}
|
||||
/* If I'm running over multicast join the multicast group */
|
||||
join_group(IGMP_SERVER,
|
||||
info->multicast_ip.
|
||||
s_addr);
|
||||
}
|
||||
state.recvd_oack = 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} else if (tr->opcode == htons(TFTP_DATA)) {
|
||||
unsigned long data_len;
|
||||
unsigned char *data;
|
||||
struct udphdr *udp;
|
||||
udp =
|
||||
(struct udphdr *) &nic.packet[ETH_HLEN +
|
||||
sizeof(struct
|
||||
iphdr)];
|
||||
len =
|
||||
ntohs(tr->udp.len) - sizeof(struct udphdr) - 4;
|
||||
data =
|
||||
nic.packet + ETH_HLEN + sizeof(struct iphdr) +
|
||||
sizeof(struct udphdr) + 4;
|
||||
|
||||
if (len > TFTM_MIN_PACKET) /* shouldn't happen */
|
||||
continue; /* ignore it */
|
||||
|
||||
block = ntohs(tp.u.ack.block = tr->u.data.block);
|
||||
|
||||
if (block > state.total_packets) {
|
||||
printf("ALERT: Invalid packet number\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Compute the expected data length */
|
||||
if (block != state.total_packets) {
|
||||
data_len = state.block_size;
|
||||
} else {
|
||||
data_len = filesize % state.block_size;
|
||||
}
|
||||
/* If the packet size is wrong drop the packet and then continue */
|
||||
if (ntohs(udp->len) !=
|
||||
(data_len + (data - (unsigned char *) udp))) {
|
||||
printf
|
||||
("ALERT: udp packet is not the correct size: %d\n",
|
||||
block);
|
||||
continue;
|
||||
}
|
||||
if (nic.packetlen < data_len + (data - nic.packet)) {
|
||||
printf
|
||||
("ALERT: Ethernet packet shorter than data_len: %d\n",
|
||||
block);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (data_len > state.block_size) {
|
||||
data_len = state.block_size;
|
||||
}
|
||||
if (((state.
|
||||
bitmap[block >> 3] >> (block & 7)) & 1) ==
|
||||
0) {
|
||||
/* Non duplicate packet */
|
||||
state.bitmap[block >> 3] |=
|
||||
(1 << (block & 7));
|
||||
memcpy(state.image +
|
||||
((block - 1) * state.block_size),
|
||||
data, data_len);
|
||||
state.received_packets++;
|
||||
} else {
|
||||
|
||||
/* printf("<DUP>\n"); */
|
||||
}
|
||||
}
|
||||
|
||||
else { /* neither TFTP_OACK, TFTP_DATA nor TFTP_ERROR */
|
||||
break;
|
||||
}
|
||||
|
||||
if (state.received_packets <= state.total_packets) {
|
||||
unsigned long b;
|
||||
unsigned long len;
|
||||
unsigned long max;
|
||||
int value;
|
||||
int last;
|
||||
|
||||
/* Compute the last bit and store an inverted trailer */
|
||||
max = state.total_packets + 1;
|
||||
value =
|
||||
((state.
|
||||
bitmap[(max - 1) >> 3] >> ((max -
|
||||
1) & 7)) & 1);
|
||||
value = !value;
|
||||
state.bitmap[max >> 3] &= ~(1 << (max & 7));
|
||||
state.bitmap[max >> 3] |= value << (max & 7);
|
||||
|
||||
len = 0;
|
||||
last = 0; /* Start with the received packets */
|
||||
for (b = 1; b <= max; b++) {
|
||||
value =
|
||||
(state.bitmap[b >> 3] >> (b & 7)) & 1;
|
||||
|
||||
if (value == 0) {
|
||||
tp.u.ack.block = htons(b - 1); /* Acknowledge the previous block */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (state.ismaster) {
|
||||
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 (state.received_packets == state.total_packets) {
|
||||
/* If the client is finished and not the master,
|
||||
* ack the last packet */
|
||||
if (!state.ismaster) {
|
||||
tp.opcode = htons(TFTP_ACK);
|
||||
/* Ack Last packet to end xfer */
|
||||
tp.u.ack.block = htons(state.total_packets);
|
||||
oport = ntohs(tr->udp.src);
|
||||
udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, iport, oport, TFTP_MIN_PACKET, &tp); /* ack */
|
||||
}
|
||||
/* We are done get out */
|
||||
forget(state.bitmap);
|
||||
break;
|
||||
}
|
||||
|
||||
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? */
|
||||
|
||||
}
|
||||
/* Leave the multicast group */
|
||||
leave_group(IGMP_SERVER);
|
||||
return info->fnc(state.image, 1, filesize, 1);
|
||||
}
|
||||
|
||||
int url_tftm(const char *name,
|
||||
int (*fnc) (unsigned char *, unsigned int, unsigned int, int))
|
||||
{
|
||||
|
||||
int ret;
|
||||
struct tftm_info info;
|
||||
|
||||
/* Set the defaults */
|
||||
info.server_ip.s_addr = arptable[ARP_SERVER].ipaddr.s_addr;
|
||||
info.server_port = TFTM_PORT;
|
||||
info.local_ip.s_addr = arptable[ARP_CLIENT].ipaddr.s_addr;
|
||||
info.local_port = TFTM_PORT; /* Does not matter. So take tftm port too. */
|
||||
info.multicast_ip.s_addr = info.local_ip.s_addr;
|
||||
info.multicast_port = TFTM_PORT;
|
||||
info.fnc = fnc;
|
||||
state.ismaster = 0;
|
||||
info.name = name;
|
||||
|
||||
state.block_size = 0;
|
||||
state.total_bytes = 0;
|
||||
state.total_packets = 0;
|
||||
state.received_packets = 0;
|
||||
state.image = 0;
|
||||
state.bitmap = 0;
|
||||
state.recvd_oack = 0;
|
||||
|
||||
if (name[0] != '/') {
|
||||
/* server ip given, so use it */
|
||||
name += inet_aton(info.name, &info.server_ip);
|
||||
/* No way to specify another port for now */
|
||||
}
|
||||
if (name[0] != '/') {
|
||||
printf("Bad tftm-URI: [%s]\n", info.name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = proto_tftm(&info);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/******************************
|
||||
* Parse the multicast options
|
||||
*******************************/
|
||||
int opt_get_multicast(struct tftp_t *tr, unsigned short *len,
|
||||
unsigned long *filesize, struct tftm_info *info)
|
||||
{
|
||||
const char *p = tr->u.oack.data, *e = 0;
|
||||
int i = 0;
|
||||
*len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 2;
|
||||
if (*len > TFTM_MIN_PACKET)
|
||||
return -1;
|
||||
e = p + *len;
|
||||
|
||||
while (*p != '\0' && p < e) {
|
||||
if (!strcasecmp("tsize", p)) {
|
||||
p += 6;
|
||||
if ((*filesize = strtoul(p, &p, 10)) > 0)
|
||||
i |= 4;
|
||||
debug(("\n"));
|
||||
debug(("tsize=%d\n", *filesize));
|
||||
while (p < e && *p)
|
||||
p++;
|
||||
if (p < e)
|
||||
p++;
|
||||
} else if (!strcasecmp("blksize", p)) {
|
||||
i |= 2;
|
||||
p += 8;
|
||||
state.block_size = strtoul(p, &p, 10);
|
||||
if (state.block_size != TFTM_MIN_PACKET) {
|
||||
printf
|
||||
("TFTM-Server rejected required transfer blocksize %d\n",
|
||||
TFTM_MIN_PACKET);
|
||||
return 0;
|
||||
}
|
||||
debug(("blksize=%d\n", state.block_size));
|
||||
while (p < e && *p)
|
||||
p++;
|
||||
if (p < e)
|
||||
p++;
|
||||
} else if (!strncmp(p, "multicast", 10)) {
|
||||
i |= 1;
|
||||
p += 10;
|
||||
debug(("multicast options: %s\n", p));
|
||||
p += 1 + inet_aton(p, &info->multicast_ip);
|
||||
debug(("multicast ip = %@\n", info->multicast_ip));
|
||||
info->multicast_port = strtoul(p, &p, 10);
|
||||
++p;
|
||||
debug(("multicast port = %d\n",
|
||||
info->multicast_port));
|
||||
state.ismaster = (*p == '1' ? 1 : 0);
|
||||
debug(("multicast ismaster = %d\n",
|
||||
state.ismaster));
|
||||
while (p < e && *p)
|
||||
p++;
|
||||
if (p < e)
|
||||
p++;
|
||||
}
|
||||
}
|
||||
if (p > e)
|
||||
return 0;
|
||||
return i;
|
||||
}
|
||||
#endif /* DOWNLOAD_PROTO_TFTP */
|
||||
1424
src/core/pxe_export.c
Normal file
1424
src/core/pxe_export.c
Normal file
File diff suppressed because it is too large
Load Diff
103
src/core/relocate.c
Normal file
103
src/core/relocate.c
Normal file
@@ -0,0 +1,103 @@
|
||||
#ifndef NORELOCATE
|
||||
|
||||
#include "etherboot.h"
|
||||
|
||||
/* by Eric Biederman */
|
||||
|
||||
/* On some platforms etherboot is compiled as a shared library, and we use
|
||||
* the ELF pic support to make it relocateable. This works very nicely
|
||||
* for code, but since no one has implemented PIC data yet pointer
|
||||
* values in variables are a a problem. Global variables are a
|
||||
* pain but the return addresses on the stack are the worst. On these
|
||||
* platforms relocate_to will restart etherboot, to ensure the stack
|
||||
* is reinitialize and hopefully get the global variables
|
||||
* appropriately reinitialized as well.
|
||||
*
|
||||
*/
|
||||
|
||||
void relocate(void)
|
||||
{
|
||||
unsigned long addr, eaddr, size;
|
||||
unsigned i;
|
||||
/* Walk through the memory map and find the highest address
|
||||
* below 4GB that etherboot will fit into. Ensure etherboot
|
||||
* lies entirely within a range with A20=0. This means that
|
||||
* even if something screws up the state of the A20 line, the
|
||||
* etherboot code is still visible and we have a chance to
|
||||
* diagnose the problem.
|
||||
*/
|
||||
/* First find the size of etherboot */
|
||||
addr = virt_to_phys(_text);
|
||||
eaddr = virt_to_phys(_end);
|
||||
size = (eaddr - addr + 0xf) & ~0xf;
|
||||
|
||||
/* If the current etherboot is beyond MAX_ADDR pretend it is
|
||||
* at the lowest possible address.
|
||||
*/
|
||||
if (eaddr > MAX_ADDR) {
|
||||
eaddr = 0;
|
||||
}
|
||||
|
||||
for(i = 0; i < meminfo.map_count; i++) {
|
||||
unsigned long r_start, r_end;
|
||||
if (meminfo.map[i].type != E820_RAM) {
|
||||
continue;
|
||||
}
|
||||
if (meminfo.map[i].addr > MAX_ADDR) {
|
||||
continue;
|
||||
}
|
||||
if (meminfo.map[i].size > MAX_ADDR) {
|
||||
continue;
|
||||
}
|
||||
r_start = meminfo.map[i].addr;
|
||||
r_end = r_start + meminfo.map[i].size;
|
||||
/* Make the addresses 16 byte (128 bit) aligned */
|
||||
r_start = (r_start + 15) & ~15;
|
||||
r_end = r_end & ~15;
|
||||
if (r_end < r_start) {
|
||||
r_end = MAX_ADDR;
|
||||
}
|
||||
if (r_end < size) {
|
||||
/* Avoid overflow weirdness when r_end - size < 0 */
|
||||
continue;
|
||||
}
|
||||
/* Shrink the range down to use only even megabytes
|
||||
* (i.e. A20=0).
|
||||
*/
|
||||
if ( r_end & 0x100000 ) {
|
||||
/* If r_end is in an odd megabyte, round down
|
||||
* r_end to the top of the next even megabyte.
|
||||
*/
|
||||
r_end = r_end & ~0xfffff;
|
||||
} else if ( ( r_end - size ) & 0x100000 ) {
|
||||
/* If r_end is in an even megabyte, but the
|
||||
* start of Etherboot would be in an odd
|
||||
* megabyte, round down to the top of the next
|
||||
* even megabyte.
|
||||
*/
|
||||
r_end = ( r_end - 0x100000 ) & ~0xfffff;
|
||||
}
|
||||
/* If we have rounded down r_end below r_ start, skip
|
||||
* this block.
|
||||
*/
|
||||
if ( r_end < r_start ) {
|
||||
continue;
|
||||
}
|
||||
if (eaddr < r_end - size) {
|
||||
addr = r_end - size;
|
||||
eaddr = r_end;
|
||||
}
|
||||
}
|
||||
if (addr != virt_to_phys(_text)) {
|
||||
unsigned long old_addr = virt_to_phys(_text);
|
||||
printf("Relocating _text from: [%lx,%lx) to [%lx,%lx)\n",
|
||||
old_addr, virt_to_phys(_end),
|
||||
addr, eaddr);
|
||||
arch_relocate_to(addr);
|
||||
cleanup();
|
||||
relocate_to(addr);
|
||||
arch_relocated_from(old_addr);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* NORELOCATE */
|
||||
236
src/core/serial.c
Normal file
236
src/core/serial.c
Normal file
@@ -0,0 +1,236 @@
|
||||
#include "etherboot.h"
|
||||
#include "timer.h"
|
||||
#ifdef CONSOLE_SERIAL
|
||||
|
||||
/*
|
||||
* The serial port interface routines implement a simple polled i/o
|
||||
* interface to a standard serial port. Due to the space restrictions
|
||||
* for the boot blocks, no BIOS support is used (since BIOS requires
|
||||
* expensive real/protected mode switches), instead the rudimentary
|
||||
* BIOS support is duplicated here.
|
||||
*
|
||||
* The base address and speed for the i/o port are passed from the
|
||||
* Makefile in the COMCONSOLE and CONSPEED preprocessor macros. The
|
||||
* line control parameters are currently hard-coded to 8 bits, no
|
||||
* parity, 1 stop bit (8N1). This can be changed in init_serial().
|
||||
*/
|
||||
|
||||
static int found = 0;
|
||||
|
||||
#if defined(COMCONSOLE)
|
||||
#undef UART_BASE
|
||||
#define UART_BASE COMCONSOLE
|
||||
#endif
|
||||
|
||||
#ifndef UART_BASE
|
||||
#error UART_BASE not defined
|
||||
#endif
|
||||
|
||||
#if defined(CONSPEED)
|
||||
#undef UART_BAUD
|
||||
#define UART_BAUD CONSPEED
|
||||
#endif
|
||||
|
||||
#ifndef UART_BAUD
|
||||
#define UART_BAUD 115200
|
||||
#endif
|
||||
|
||||
#if ((115200%UART_BAUD) != 0)
|
||||
#error Bad ttys0 baud rate
|
||||
#endif
|
||||
|
||||
#define COMBRD (115200/UART_BAUD)
|
||||
|
||||
/* Line Control Settings */
|
||||
#ifndef COMPARM
|
||||
/* Set 8bit, 1 stop bit, no parity */
|
||||
#define COMPARM 0x03
|
||||
#endif
|
||||
|
||||
#define UART_LCS COMPARM
|
||||
|
||||
/* Data */
|
||||
#define UART_RBR 0x00
|
||||
#define UART_TBR 0x00
|
||||
|
||||
/* Control */
|
||||
#define UART_IER 0x01
|
||||
#define UART_IIR 0x02
|
||||
#define UART_FCR 0x02
|
||||
#define UART_LCR 0x03
|
||||
#define UART_MCR 0x04
|
||||
#define UART_DLL 0x00
|
||||
#define UART_DLM 0x01
|
||||
|
||||
/* Status */
|
||||
#define UART_LSR 0x05
|
||||
#define UART_LSR_TEMPT 0x40 /* Transmitter empty */
|
||||
#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */
|
||||
#define UART_LSR_BI 0x10 /* Break interrupt indicator */
|
||||
#define UART_LSR_FE 0x08 /* Frame error indicator */
|
||||
#define UART_LSR_PE 0x04 /* Parity error indicator */
|
||||
#define UART_LSR_OE 0x02 /* Overrun error indicator */
|
||||
#define UART_LSR_DR 0x01 /* Receiver data ready */
|
||||
|
||||
#define UART_MSR 0x06
|
||||
#define UART_SCR 0x07
|
||||
|
||||
#if defined(UART_MEM)
|
||||
#define uart_readb(addr) readb((addr))
|
||||
#define uart_writeb(val,addr) writeb((val),(addr))
|
||||
#else
|
||||
#define uart_readb(addr) inb((addr))
|
||||
#define uart_writeb(val,addr) outb((val),(addr))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* void serial_putc(int ch);
|
||||
* Write character `ch' to port UART_BASE.
|
||||
*/
|
||||
void serial_putc(int ch)
|
||||
{
|
||||
int i;
|
||||
int status;
|
||||
if (!found) {
|
||||
/* no serial interface */
|
||||
return;
|
||||
}
|
||||
i = 1000; /* timeout */
|
||||
while(--i > 0) {
|
||||
status = uart_readb(UART_BASE + UART_LSR);
|
||||
if (status & UART_LSR_THRE) {
|
||||
/* TX buffer emtpy */
|
||||
uart_writeb(ch, UART_BASE + UART_TBR);
|
||||
break;
|
||||
}
|
||||
mdelay(2);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* int serial_getc(void);
|
||||
* Read a character from port UART_BASE.
|
||||
*/
|
||||
int serial_getc(void)
|
||||
{
|
||||
int status;
|
||||
int ch;
|
||||
do {
|
||||
status = uart_readb(UART_BASE + UART_LSR);
|
||||
} while((status & 1) == 0);
|
||||
ch = uart_readb(UART_BASE + UART_RBR); /* fetch (first) character */
|
||||
ch &= 0x7f; /* remove any parity bits we get */
|
||||
if (ch == 0x7f) { /* Make DEL... look like BS */
|
||||
ch = 0x08;
|
||||
}
|
||||
return ch;
|
||||
}
|
||||
|
||||
/*
|
||||
* int serial_ischar(void);
|
||||
* If there is a character in the input buffer of port UART_BASE,
|
||||
* return nonzero; otherwise return 0.
|
||||
*/
|
||||
int serial_ischar(void)
|
||||
{
|
||||
int status;
|
||||
if (!found)
|
||||
return 0;
|
||||
status = uart_readb(UART_BASE + UART_LSR); /* line status reg; */
|
||||
return status & 1; /* rx char available */
|
||||
}
|
||||
|
||||
/*
|
||||
* int serial_init(void);
|
||||
* Initialize port UART_BASE to speed CONSPEED, line settings 8N1.
|
||||
*/
|
||||
int serial_init(void)
|
||||
{
|
||||
int initialized = 0;
|
||||
int status;
|
||||
int divisor, lcs;
|
||||
|
||||
if (found)
|
||||
return 1;
|
||||
|
||||
divisor = COMBRD;
|
||||
lcs = UART_LCS;
|
||||
|
||||
|
||||
#ifdef COMPRESERVE
|
||||
lcs = uart_readb(UART_BASE + UART_LCR) & 0x7f;
|
||||
uart_writeb(0x80 | lcs, UART_BASE + UART_LCR);
|
||||
divisor = (uart_readb(UART_BASE + UART_DLM) << 8) | uart_readb(UART_BASE + UART_DLL);
|
||||
uart_writeb(lcs, UART_BASE + UART_LCR);
|
||||
#endif
|
||||
|
||||
/* Set Baud Rate Divisor to CONSPEED, and test to see if the
|
||||
* serial port appears to be present.
|
||||
*/
|
||||
uart_writeb(0x80 | lcs, UART_BASE + UART_LCR);
|
||||
uart_writeb(0xaa, UART_BASE + UART_DLL);
|
||||
if (uart_readb(UART_BASE + UART_DLL) != 0xaa)
|
||||
goto out;
|
||||
uart_writeb(0x55, UART_BASE + UART_DLL);
|
||||
if (uart_readb(UART_BASE + UART_DLL) != 0x55)
|
||||
goto out;
|
||||
uart_writeb(divisor & 0xff, UART_BASE + UART_DLL);
|
||||
if (uart_readb(UART_BASE + UART_DLL) != (divisor & 0xff))
|
||||
goto out;
|
||||
uart_writeb(0xaa, UART_BASE + UART_DLM);
|
||||
if (uart_readb(UART_BASE + UART_DLM) != 0xaa)
|
||||
goto out;
|
||||
uart_writeb(0x55, UART_BASE + UART_DLM);
|
||||
if (uart_readb(UART_BASE + UART_DLM) != 0x55)
|
||||
goto out;
|
||||
uart_writeb((divisor >> 8) & 0xff, UART_BASE + UART_DLM);
|
||||
if (uart_readb(UART_BASE + UART_DLM) != ((divisor >> 8) & 0xff))
|
||||
goto out;
|
||||
uart_writeb(lcs, UART_BASE + UART_LCR);
|
||||
|
||||
/* disable interrupts */
|
||||
uart_writeb(0x0, UART_BASE + UART_IER);
|
||||
|
||||
/* disable fifo's */
|
||||
uart_writeb(0x00, UART_BASE + UART_FCR);
|
||||
|
||||
/* Set clear to send, so flow control works... */
|
||||
uart_writeb((1<<1), UART_BASE + UART_MCR);
|
||||
|
||||
|
||||
/* Flush the input buffer. */
|
||||
do {
|
||||
/* rx buffer reg
|
||||
* throw away (unconditionally the first time)
|
||||
*/
|
||||
uart_readb(UART_BASE + UART_RBR);
|
||||
/* line status reg */
|
||||
status = uart_readb(UART_BASE + UART_LSR);
|
||||
} while(status & UART_LSR_DR);
|
||||
initialized = 1;
|
||||
out:
|
||||
found = initialized;
|
||||
return initialized;
|
||||
}
|
||||
|
||||
/*
|
||||
* void serial_fini(void);
|
||||
* Cleanup our use of the serial port, in particular flush the
|
||||
* output buffer so we don't accidentially loose characters.
|
||||
*/
|
||||
void serial_fini(void)
|
||||
{
|
||||
int i, status;
|
||||
if (!found) {
|
||||
/* no serial interface */
|
||||
return;
|
||||
}
|
||||
/* Flush the output buffer to avoid dropping characters,
|
||||
* if we are reinitializing the serial port.
|
||||
*/
|
||||
i = 10000; /* timeout */
|
||||
do {
|
||||
status = uart_readb(UART_BASE + UART_LSR);
|
||||
} while((--i > 0) && !(status & UART_LSR_TEMPT));
|
||||
}
|
||||
#endif
|
||||
540
src/core/string.c
Normal file
540
src/core/string.c
Normal file
@@ -0,0 +1,540 @@
|
||||
/*
|
||||
* Copyright (C) 1991, 1992 Linus Torvalds
|
||||
* Copyright (C) 2004 Tobias Lorenz
|
||||
*
|
||||
* string handling functions
|
||||
* based on linux/lib/string.c
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
/*
|
||||
* stupid library routines.. The optimized versions should generally be found
|
||||
* as inline code in <asm-xx/string.h>
|
||||
*
|
||||
* These are buggy as well..
|
||||
*
|
||||
* * Fri Jun 25 1999, Ingo Oeser <ioe@informatik.tu-chemnitz.de>
|
||||
* - Added strsep() which will replace strtok() soon (because strsep() is
|
||||
* reentrant and should be faster). Use only strsep() in new code, please.
|
||||
*/
|
||||
|
||||
#include "etherboot.h"
|
||||
|
||||
|
||||
/* *** FROM string.c *** */
|
||||
|
||||
#ifndef __HAVE_ARCH_STRNICMP
|
||||
/**
|
||||
* strnicmp - Case insensitive, length-limited string comparison
|
||||
* @s1: One string
|
||||
* @s2: The other string
|
||||
* @len: the maximum number of characters to compare
|
||||
*/
|
||||
int strnicmp(const char *s1, const char *s2, size_t len)
|
||||
{
|
||||
/* Yes, Virginia, it had better be unsigned */
|
||||
unsigned char c1, c2;
|
||||
|
||||
c1 = 0; c2 = 0;
|
||||
if (len) {
|
||||
do {
|
||||
c1 = *s1; c2 = *s2;
|
||||
s1++; s2++;
|
||||
if (!c1)
|
||||
break;
|
||||
if (!c2)
|
||||
break;
|
||||
if (c1 == c2)
|
||||
continue;
|
||||
c1 = tolower(c1);
|
||||
c2 = tolower(c2);
|
||||
if (c1 != c2)
|
||||
break;
|
||||
} while (--len);
|
||||
}
|
||||
return (int)c1 - (int)c2;
|
||||
}
|
||||
#endif
|
||||
|
||||
char * ___strtok;
|
||||
|
||||
#ifndef __HAVE_ARCH_STRCPY
|
||||
/**
|
||||
* strcpy - Copy a %NUL terminated string
|
||||
* @dest: Where to copy the string to
|
||||
* @src: Where to copy the string from
|
||||
*/
|
||||
char * strcpy(char * dest,const char *src)
|
||||
{
|
||||
char *tmp = dest;
|
||||
|
||||
while ((*dest++ = *src++) != '\0')
|
||||
/* nothing */;
|
||||
return tmp;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef __HAVE_ARCH_STRNCPY
|
||||
/**
|
||||
* strncpy - Copy a length-limited, %NUL-terminated string
|
||||
* @dest: Where to copy the string to
|
||||
* @src: Where to copy the string from
|
||||
* @count: The maximum number of bytes to copy
|
||||
*
|
||||
* Note that unlike userspace strncpy, this does not %NUL-pad the buffer.
|
||||
* However, the result is not %NUL-terminated if the source exceeds
|
||||
* @count bytes.
|
||||
*/
|
||||
char * strncpy(char * dest,const char *src,size_t count)
|
||||
{
|
||||
char *tmp = dest;
|
||||
|
||||
while (count-- && (*dest++ = *src++) != '\0')
|
||||
/* nothing */;
|
||||
|
||||
return tmp;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef __HAVE_ARCH_STRCAT
|
||||
/**
|
||||
* strcat - Append one %NUL-terminated string to another
|
||||
* @dest: The string to be appended to
|
||||
* @src: The string to append to it
|
||||
*/
|
||||
char * strcat(char * dest, const char * src)
|
||||
{
|
||||
char *tmp = dest;
|
||||
|
||||
while (*dest)
|
||||
dest++;
|
||||
while ((*dest++ = *src++) != '\0')
|
||||
;
|
||||
|
||||
return tmp;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef __HAVE_ARCH_STRNCAT
|
||||
/**
|
||||
* strncat - Append a length-limited, %NUL-terminated string to another
|
||||
* @dest: The string to be appended to
|
||||
* @src: The string to append to it
|
||||
* @count: The maximum numbers of bytes to copy
|
||||
*
|
||||
* Note that in contrast to strncpy, strncat ensures the result is
|
||||
* terminated.
|
||||
*/
|
||||
char * strncat(char *dest, const char *src, size_t count)
|
||||
{
|
||||
char *tmp = dest;
|
||||
|
||||
if (count) {
|
||||
while (*dest)
|
||||
dest++;
|
||||
while ((*dest++ = *src++)) {
|
||||
if (--count == 0) {
|
||||
*dest = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tmp;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef __HAVE_ARCH_STRCMP
|
||||
/**
|
||||
* strcmp - Compare two strings
|
||||
* @cs: One string
|
||||
* @ct: Another string
|
||||
*/
|
||||
int strcmp(const char * cs,const char * ct)
|
||||
{
|
||||
register signed char __res;
|
||||
|
||||
while (1) {
|
||||
if ((__res = *cs - *ct++) != 0 || !*cs++)
|
||||
break;
|
||||
}
|
||||
|
||||
return __res;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef __HAVE_ARCH_STRNCMP
|
||||
/**
|
||||
* strncmp - Compare two length-limited strings
|
||||
* @cs: One string
|
||||
* @ct: Another string
|
||||
* @count: The maximum number of bytes to compare
|
||||
*/
|
||||
int strncmp(const char * cs,const char * ct,size_t count)
|
||||
{
|
||||
register signed char __res = 0;
|
||||
|
||||
while (count) {
|
||||
if ((__res = *cs - *ct++) != 0 || !*cs++)
|
||||
break;
|
||||
count--;
|
||||
}
|
||||
|
||||
return __res;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef __HAVE_ARCH_STRCHR
|
||||
/**
|
||||
* strchr - Find the first occurrence of a character in a string
|
||||
* @s: The string to be searched
|
||||
* @c: The character to search for
|
||||
*/
|
||||
char * strchr(const char * s, int c)
|
||||
{
|
||||
for(; *s != (char) c; ++s)
|
||||
if (*s == '\0')
|
||||
return NULL;
|
||||
return (char *) s;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef __HAVE_ARCH_STRRCHR
|
||||
/**
|
||||
* strrchr - Find the last occurrence of a character in a string
|
||||
* @s: The string to be searched
|
||||
* @c: The character to search for
|
||||
*/
|
||||
char * strrchr(const char * s, int c)
|
||||
{
|
||||
const char *p = s + strlen(s);
|
||||
do {
|
||||
if (*p == (char)c)
|
||||
return (char *)p;
|
||||
} while (--p >= s);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef __HAVE_ARCH_STRLEN
|
||||
/**
|
||||
* strlen - Find the length of a string
|
||||
* @s: The string to be sized
|
||||
*/
|
||||
size_t strlen(const char * s)
|
||||
{
|
||||
const char *sc;
|
||||
|
||||
for (sc = s; *sc != '\0'; ++sc)
|
||||
/* nothing */;
|
||||
return sc - s;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef __HAVE_ARCH_STRNLEN
|
||||
/**
|
||||
* strnlen - Find the length of a length-limited string
|
||||
* @s: The string to be sized
|
||||
* @count: The maximum number of bytes to search
|
||||
*/
|
||||
size_t strnlen(const char * s, size_t count)
|
||||
{
|
||||
const char *sc;
|
||||
|
||||
for (sc = s; count-- && *sc != '\0'; ++sc)
|
||||
/* nothing */;
|
||||
return sc - s;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef __HAVE_ARCH_STRSPN
|
||||
/**
|
||||
* strspn - Calculate the length of the initial substring of @s which only
|
||||
* contain letters in @accept
|
||||
* @s: The string to be searched
|
||||
* @accept: The string to search for
|
||||
*/
|
||||
size_t strspn(const char *s, const char *accept)
|
||||
{
|
||||
const char *p;
|
||||
const char *a;
|
||||
size_t count = 0;
|
||||
|
||||
for (p = s; *p != '\0'; ++p) {
|
||||
for (a = accept; *a != '\0'; ++a) {
|
||||
if (*p == *a)
|
||||
break;
|
||||
}
|
||||
if (*a == '\0')
|
||||
return count;
|
||||
++count;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef __HAVE_ARCH_STRPBRK
|
||||
/**
|
||||
* strpbrk - Find the first occurrence of a set of characters
|
||||
* @cs: The string to be searched
|
||||
* @ct: The characters to search for
|
||||
*/
|
||||
char * strpbrk(const char * cs,const char * ct)
|
||||
{
|
||||
const char *sc1,*sc2;
|
||||
|
||||
for( sc1 = cs; *sc1 != '\0'; ++sc1) {
|
||||
for( sc2 = ct; *sc2 != '\0'; ++sc2) {
|
||||
if (*sc1 == *sc2)
|
||||
return (char *) sc1;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef __HAVE_ARCH_STRTOK
|
||||
/**
|
||||
* strtok - Split a string into tokens
|
||||
* @s: The string to be searched
|
||||
* @ct: The characters to search for
|
||||
*
|
||||
* WARNING: strtok is deprecated, use strsep instead.
|
||||
*/
|
||||
char * strtok(char * s,const char * ct)
|
||||
{
|
||||
char *sbegin, *send;
|
||||
|
||||
sbegin = s ? s : ___strtok;
|
||||
if (!sbegin) {
|
||||
return NULL;
|
||||
}
|
||||
sbegin += strspn(sbegin,ct);
|
||||
if (*sbegin == '\0') {
|
||||
___strtok = NULL;
|
||||
return( NULL );
|
||||
}
|
||||
send = strpbrk( sbegin, ct);
|
||||
if (send && *send != '\0')
|
||||
*send++ = '\0';
|
||||
___strtok = send;
|
||||
return (sbegin);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef __HAVE_ARCH_STRSEP
|
||||
/**
|
||||
* strsep - Split a string into tokens
|
||||
* @s: The string to be searched
|
||||
* @ct: The characters to search for
|
||||
*
|
||||
* strsep() updates @s to point after the token, ready for the next call.
|
||||
*
|
||||
* It returns empty tokens, too, behaving exactly like the libc function
|
||||
* of that name. In fact, it was stolen from glibc2 and de-fancy-fied.
|
||||
* Same semantics, slimmer shape. ;)
|
||||
*/
|
||||
char * strsep(char **s, const char *ct)
|
||||
{
|
||||
char *sbegin = *s, *end;
|
||||
|
||||
if (sbegin == NULL)
|
||||
return NULL;
|
||||
|
||||
end = strpbrk(sbegin, ct);
|
||||
if (end)
|
||||
*end++ = '\0';
|
||||
*s = end;
|
||||
|
||||
return sbegin;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef __HAVE_ARCH_MEMSET
|
||||
/**
|
||||
* memset - Fill a region of memory with the given value
|
||||
* @s: Pointer to the start of the area.
|
||||
* @c: The byte to fill the area with
|
||||
* @count: The size of the area.
|
||||
*
|
||||
* Do not use memset() to access IO space, use memset_io() instead.
|
||||
*/
|
||||
void * memset(void * s,int c,size_t count)
|
||||
{
|
||||
char *xs = (char *) s;
|
||||
|
||||
while (count--)
|
||||
*xs++ = c;
|
||||
|
||||
return s;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef __HAVE_ARCH_BCOPY
|
||||
/**
|
||||
* bcopy - Copy one area of memory to another
|
||||
* @src: Where to copy from
|
||||
* @dest: Where to copy to
|
||||
* @count: The size of the area.
|
||||
*
|
||||
* Note that this is the same as memcpy(), with the arguments reversed.
|
||||
* memcpy() is the standard, bcopy() is a legacy BSD function.
|
||||
*
|
||||
* You should not use this function to access IO space, use memcpy_toio()
|
||||
* or memcpy_fromio() instead.
|
||||
*/
|
||||
char * bcopy(const char * src, char * dest, int count)
|
||||
{
|
||||
char *tmp = dest;
|
||||
|
||||
while (count--)
|
||||
*tmp++ = *src++;
|
||||
|
||||
return dest;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef __HAVE_ARCH_MEMCPY
|
||||
/**
|
||||
* memcpy - Copy one area of memory to another
|
||||
* @dest: Where to copy to
|
||||
* @src: Where to copy from
|
||||
* @count: The size of the area.
|
||||
*
|
||||
* You should not use this function to access IO space, use memcpy_toio()
|
||||
* or memcpy_fromio() instead.
|
||||
*/
|
||||
void * memcpy(void * dest,const void *src,size_t count)
|
||||
{
|
||||
char *tmp = (char *) dest, *s = (char *) src;
|
||||
|
||||
while (count--)
|
||||
*tmp++ = *s++;
|
||||
|
||||
return dest;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef __HAVE_ARCH_MEMMOVE
|
||||
/**
|
||||
* memmove - Copy one area of memory to another
|
||||
* @dest: Where to copy to
|
||||
* @src: Where to copy from
|
||||
* @count: The size of the area.
|
||||
*
|
||||
* Unlike memcpy(), memmove() copes with overlapping areas.
|
||||
*/
|
||||
void * memmove(void * dest,const void *src,size_t count)
|
||||
{
|
||||
char *tmp, *s;
|
||||
|
||||
if (dest <= src) {
|
||||
tmp = (char *) dest;
|
||||
s = (char *) src;
|
||||
while (count--)
|
||||
*tmp++ = *s++;
|
||||
}
|
||||
else {
|
||||
tmp = (char *) dest + count;
|
||||
s = (char *) src + count;
|
||||
while (count--)
|
||||
*--tmp = *--s;
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef __HAVE_ARCH_MEMCMP
|
||||
/**
|
||||
* memcmp - Compare two areas of memory
|
||||
* @cs: One area of memory
|
||||
* @ct: Another area of memory
|
||||
* @count: The size of the area.
|
||||
*/
|
||||
int memcmp(const void * cs,const void * ct,size_t count)
|
||||
{
|
||||
const unsigned char *su1, *su2;
|
||||
int res = 0;
|
||||
|
||||
for( su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--)
|
||||
if ((res = *su1 - *su2) != 0)
|
||||
break;
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef __HAVE_ARCH_MEMSCAN
|
||||
/**
|
||||
* memscan - Find a character in an area of memory.
|
||||
* @addr: The memory area
|
||||
* @c: The byte to search for
|
||||
* @size: The size of the area.
|
||||
*
|
||||
* returns the address of the first occurrence of @c, or 1 byte past
|
||||
* the area if @c is not found
|
||||
*/
|
||||
void * memscan(void * addr, int c, size_t size)
|
||||
{
|
||||
unsigned char * p = (unsigned char *) addr;
|
||||
|
||||
while (size) {
|
||||
if (*p == c)
|
||||
return (void *) p;
|
||||
p++;
|
||||
size--;
|
||||
}
|
||||
return (void *) p;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef __HAVE_ARCH_STRSTR
|
||||
/**
|
||||
* strstr - Find the first substring in a %NUL terminated string
|
||||
* @s1: The string to be searched
|
||||
* @s2: The string to search for
|
||||
*/
|
||||
char * strstr(const char * s1,const char * s2)
|
||||
{
|
||||
int l1, l2;
|
||||
|
||||
l2 = strlen(s2);
|
||||
if (!l2)
|
||||
return (char *) s1;
|
||||
l1 = strlen(s1);
|
||||
while (l1 >= l2) {
|
||||
l1--;
|
||||
if (!memcmp(s1,s2,l2))
|
||||
return (char *) s1;
|
||||
s1++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef __HAVE_ARCH_MEMCHR
|
||||
/**
|
||||
* memchr - Find a character in an area of memory.
|
||||
* @s: The memory area
|
||||
* @c: The byte to search for
|
||||
* @n: The size of the area.
|
||||
*
|
||||
* returns the address of the first occurrence of @c, or %NULL
|
||||
* if @c is not found
|
||||
*/
|
||||
void * memchr(const void *s, int c, size_t n)
|
||||
{
|
||||
const unsigned char *p = s;
|
||||
while (n-- != 0) {
|
||||
if ((unsigned char)c == *p++) {
|
||||
return (void *)(p-1);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
30
src/core/timer.c
Normal file
30
src/core/timer.c
Normal file
@@ -0,0 +1,30 @@
|
||||
/* A couple of 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.
|
||||
*/
|
||||
|
||||
#include "etherboot.h"
|
||||
#include "timer.h"
|
||||
|
||||
/* Machine Independant timer helper functions */
|
||||
|
||||
void mdelay(unsigned int msecs)
|
||||
{
|
||||
unsigned int i;
|
||||
for(i = 0; i < msecs; i++) {
|
||||
udelay(1000);
|
||||
poll_interruptions();
|
||||
}
|
||||
}
|
||||
|
||||
void waiton_timer2(unsigned int ticks)
|
||||
{
|
||||
load_timer2(ticks);
|
||||
while(timer2_running()) {
|
||||
poll_interruptions();
|
||||
}
|
||||
}
|
||||
166
src/core/vsprintf.c
Normal file
166
src/core/vsprintf.c
Normal file
@@ -0,0 +1,166 @@
|
||||
#include "etherboot.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
#define LONG_SHIFT ((int)((sizeof(unsigned long)*CHAR_BIT) - 4))
|
||||
#define INT_SHIFT ((int)((sizeof(unsigned int)*CHAR_BIT) - 4))
|
||||
#define SHRT_SHIFT ((int)((sizeof(unsigned short)*CHAR_BIT) - 4))
|
||||
#define CHAR_SHIFT ((int)((sizeof(unsigned char)*CHAR_BIT) - 4))
|
||||
|
||||
/**************************************************************************
|
||||
PRINTF and friends
|
||||
|
||||
Formats:
|
||||
%[#]x - 4 bytes int (8 hex digits, lower case)
|
||||
%[#]X - 4 bytes int (8 hex digits, upper case)
|
||||
%[#]lx - 8 bytes long (16 hex digits, lower case)
|
||||
%[#]lX - 8 bytes long (16 hex digits, upper case)
|
||||
%[#]hx - 2 bytes int (4 hex digits, lower case)
|
||||
%[#]hX - 2 bytes int (4 hex digits, upper case)
|
||||
%[#]hhx - 1 byte int (2 hex digits, lower case)
|
||||
%[#]hhX - 1 byte int (2 hex digits, upper case)
|
||||
- optional # prefixes 0x or 0X
|
||||
%d - decimal int
|
||||
%c - char
|
||||
%s - string
|
||||
%@ - Internet address in ddd.ddd.ddd.ddd notation
|
||||
%! - Ethernet address in xx:xx:xx:xx:xx:xx notation
|
||||
Note: width specification ignored
|
||||
**************************************************************************/
|
||||
static int vsprintf(char *buf, const char *fmt, va_list args)
|
||||
{
|
||||
char *p, *s;
|
||||
s = buf;
|
||||
for ( ; *fmt != '\0'; ++fmt) {
|
||||
if (*fmt != '%') {
|
||||
buf ? *s++ = *fmt : putchar(*fmt);
|
||||
continue;
|
||||
}
|
||||
/* skip width specs */
|
||||
fmt++;
|
||||
while (*fmt >= '0' && *fmt <= '9')
|
||||
fmt++;
|
||||
if (*fmt == '.')
|
||||
fmt++;
|
||||
while (*fmt >= '0' && *fmt <= '9')
|
||||
fmt++;
|
||||
if (*fmt == 's') {
|
||||
for(p = va_arg(args, char *); *p != '\0'; p++)
|
||||
buf ? *s++ = *p : putchar(*p);
|
||||
}
|
||||
else { /* Length of item is bounded */
|
||||
char tmp[40], *q = tmp;
|
||||
int alt = 0;
|
||||
int shift = INT_SHIFT;
|
||||
if (*fmt == '#') {
|
||||
alt = 1;
|
||||
fmt++;
|
||||
}
|
||||
if (*fmt == 'l') {
|
||||
shift = LONG_SHIFT;
|
||||
fmt++;
|
||||
}
|
||||
else if (*fmt == 'h') {
|
||||
shift = SHRT_SHIFT;
|
||||
fmt++;
|
||||
if (*fmt == 'h') {
|
||||
shift = CHAR_SHIFT;
|
||||
fmt++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Before each format q points to tmp buffer
|
||||
* After each format q points past end of item
|
||||
*/
|
||||
if ((*fmt | 0x20) == 'x') {
|
||||
/* With x86 gcc, sizeof(long) == sizeof(int) */
|
||||
unsigned long h;
|
||||
int ncase;
|
||||
if (shift > INT_SHIFT) {
|
||||
h = va_arg(args, unsigned long);
|
||||
} else {
|
||||
h = va_arg(args, unsigned int);
|
||||
}
|
||||
ncase = (*fmt & 0x20);
|
||||
if (alt) {
|
||||
*q++ = '0';
|
||||
*q++ = 'X' | ncase;
|
||||
}
|
||||
for ( ; shift >= 0; shift -= 4)
|
||||
*q++ = "0123456789ABCDEF"[(h >> shift) & 0xF] | ncase;
|
||||
}
|
||||
else if (*fmt == 'd') {
|
||||
char *r;
|
||||
long i;
|
||||
if (shift > INT_SHIFT) {
|
||||
i = va_arg(args, long);
|
||||
} else {
|
||||
i = va_arg(args, int);
|
||||
}
|
||||
if (i < 0) {
|
||||
*q++ = '-';
|
||||
i = -i;
|
||||
}
|
||||
p = q; /* save beginning of digits */
|
||||
do {
|
||||
*q++ = '0' + (i % 10);
|
||||
i /= 10;
|
||||
} while (i);
|
||||
/* reverse digits, stop in middle */
|
||||
r = q; /* don't alter q */
|
||||
while (--r > p) {
|
||||
i = *r;
|
||||
*r = *p;
|
||||
*p++ = i;
|
||||
}
|
||||
}
|
||||
else if (*fmt == '@') {
|
||||
unsigned char *r;
|
||||
union {
|
||||
uint32_t l;
|
||||
unsigned char c[4];
|
||||
} u;
|
||||
u.l = va_arg(args, uint32_t);
|
||||
for (r = &u.c[0]; r < &u.c[4]; ++r)
|
||||
q += sprintf(q, "%d.", *r);
|
||||
--q;
|
||||
}
|
||||
else if (*fmt == '!') {
|
||||
char *r;
|
||||
p = va_arg(args, char *);
|
||||
for (r = p + ETH_ALEN; p < r; ++p)
|
||||
q += sprintf(q, "%hhX:", *p);
|
||||
--q;
|
||||
}
|
||||
else if (*fmt == 'c')
|
||||
*q++ = va_arg(args, int);
|
||||
else
|
||||
*q++ = *fmt;
|
||||
/* now output the saved string */
|
||||
for (p = tmp; p < q; ++p)
|
||||
buf ? *s++ = *p : putchar(*p);
|
||||
}
|
||||
}
|
||||
if (buf)
|
||||
*s = '\0';
|
||||
return (s - buf);
|
||||
}
|
||||
|
||||
int sprintf(char *buf, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int i;
|
||||
va_start(args, fmt);
|
||||
i=vsprintf(buf, fmt, args);
|
||||
va_end(args);
|
||||
return i;
|
||||
}
|
||||
|
||||
void printf(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int i;
|
||||
va_start(args, fmt);
|
||||
i=vsprintf(0, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
Reference in New Issue
Block a user