Initial revision

This commit is contained in:
Michael Brown
2005-03-08 18:53:11 +00:00
commit 3d6123e69a
373 changed files with 114041 additions and 0 deletions

5196
src/core/btext.c Normal file

File diff suppressed because it is too large Load Diff

161
src/core/config.c Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

365
src/core/osloader.c Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

103
src/core/relocate.c Normal file
View 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
View 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
View 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
View 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
View 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);
}