summaryrefslogtreecommitdiff
path: root/os/boot/pc
diff options
context:
space:
mode:
Diffstat (limited to 'os/boot/pc')
-rw-r--r--os/boot/pc/8250.c308
-rw-r--r--os/boot/pc/LICENCE237
-rw-r--r--os/boot/pc/NOTICE4
-rw-r--r--os/boot/pc/alarm.c123
-rw-r--r--os/boot/pc/apm.c16
-rw-r--r--os/boot/pc/bcom.c460
-rw-r--r--os/boot/pc/boot.c451
-rw-r--r--os/boot/pc/bootld.c108
-rw-r--r--os/boot/pc/bootp.c652
-rw-r--r--os/boot/pc/cga.c91
-rw-r--r--os/boot/pc/clock.c305
-rw-r--r--os/boot/pc/conf.c537
-rw-r--r--os/boot/pc/console.c236
-rw-r--r--os/boot/pc/dat.h212
-rw-r--r--os/boot/pc/devfloppy.c853
-rw-r--r--os/boot/pc/devfloppy.h196
-rw-r--r--os/boot/pc/devi82365.c1205
-rw-r--r--os/boot/pc/devpccard.c1957
-rw-r--r--os/boot/pc/devsd.c617
-rw-r--r--os/boot/pc/dma.c245
-rw-r--r--os/boot/pc/dosboot.c582
-rw-r--r--os/boot/pc/dosfs.h62
-rw-r--r--os/boot/pc/eipfmt.c145
-rw-r--r--os/boot/pc/error.h58
-rw-r--r--os/boot/pc/ether.c282
-rw-r--r--os/boot/pc/ether2000.c110
-rw-r--r--os/boot/pc/ether2114x.c1652
-rw-r--r--os/boot/pc/ether589.c211
-rw-r--r--os/boot/pc/ether79c970.c539
-rw-r--r--os/boot/pc/ether8003.c258
-rw-r--r--os/boot/pc/ether8139.c614
-rw-r--r--os/boot/pc/ether8169.c847
-rw-r--r--os/boot/pc/ether82557.c881
-rw-r--r--os/boot/pc/ether83815.c825
-rw-r--r--os/boot/pc/ether8390.c715
-rw-r--r--os/boot/pc/ether8390.h71
-rw-r--r--os/boot/pc/etherec2t.c155
-rw-r--r--os/boot/pc/etherelnk3.c1898
-rw-r--r--os/boot/pc/etherif.h46
-rw-r--r--os/boot/pc/etherigbe.c1706
-rw-r--r--os/boot/pc/ethermii.c224
-rw-r--r--os/boot/pc/ethermii.h116
-rw-r--r--os/boot/pc/etherrhine.c676
-rw-r--r--os/boot/pc/fns.h155
-rw-r--r--os/boot/pc/fs.c94
-rw-r--r--os/boot/pc/fs.h36
-rw-r--r--os/boot/pc/getcallerpc.c8
-rw-r--r--os/boot/pc/ilock.c24
-rw-r--r--os/boot/pc/inflate.c199
-rw-r--r--os/boot/pc/io.h201
-rw-r--r--os/boot/pc/ip.h100
-rw-r--r--os/boot/pc/kbd.c489
-rw-r--r--os/boot/pc/kfs.h57
-rw-r--r--os/boot/pc/kfsboot.c256
-rw-r--r--os/boot/pc/l.s1079
-rw-r--r--os/boot/pc/lib.h102
-rw-r--r--os/boot/pc/load.c563
-rw-r--r--os/boot/pc/mbr.s259
-rw-r--r--os/boot/pc/mem.h114
-rw-r--r--os/boot/pc/memory.c504
-rw-r--r--os/boot/pc/mkfile241
-rw-r--r--os/boot/pc/noether.c40
-rw-r--r--os/boot/pc/part.c344
-rw-r--r--os/boot/pc/pbs.s372
-rwxr-xr-xos/boot/pc/pbsdiskbin0 -> 509 bytes
-rw-r--r--os/boot/pc/pbsdisk.s344
-rwxr-xr-xos/boot/pc/pbsdisklbabin0 -> 509 bytes
-rw-r--r--os/boot/pc/pbsdisklba.s327
-rw-r--r--os/boot/pc/pbslba.s362
-rw-r--r--os/boot/pc/pci.c852
-rw-r--r--os/boot/pc/print.c31
-rw-r--r--os/boot/pc/queue.c44
-rw-r--r--os/boot/pc/sd.h127
-rw-r--r--os/boot/pc/sd53c8xx.c2120
-rw-r--r--os/boot/pc/sd53c8xx.i769
-rw-r--r--os/boot/pc/sdata.c1639
-rw-r--r--os/boot/pc/sdmylex.c1294
-rw-r--r--os/boot/pc/sdscsi.c376
-rw-r--r--os/boot/pc/trap.c331
-rw-r--r--os/boot/pc/ureg.h27
-rw-r--r--os/boot/pc/x16.h159
81 files changed, 35525 insertions, 0 deletions
diff --git a/os/boot/pc/8250.c b/os/boot/pc/8250.c
new file mode 100644
index 00000000..56d652f9
--- /dev/null
+++ b/os/boot/pc/8250.c
@@ -0,0 +1,308 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+/*
+ * INS8250 uart
+ */
+enum
+{
+ /*
+ * register numbers
+ */
+ Data= 0, /* xmit/rcv buffer */
+ Iena= 1, /* interrupt enable */
+ Ircv= (1<<0), /* for char rcv'd */
+ Ixmt= (1<<1), /* for xmit buffer empty */
+ Irstat=(1<<2), /* for change in rcv'er status */
+ Imstat=(1<<3), /* for change in modem status */
+ Istat= 2, /* interrupt flag (read) */
+ Tctl= 2, /* test control (write) */
+ Format= 3, /* byte format */
+ Bits8= (3<<0), /* 8 bits/byte */
+ Stop2= (1<<2), /* 2 stop bits */
+ Pena= (1<<3), /* generate parity */
+ Peven= (1<<4), /* even parity */
+ Pforce=(1<<5), /* force parity */
+ Break= (1<<6), /* generate a break */
+ Dra= (1<<7), /* address the divisor */
+ Mctl= 4, /* modem control */
+ Dtr= (1<<0), /* data terminal ready */
+ Rts= (1<<1), /* request to send */
+ Ri= (1<<2), /* ring */
+ Inton= (1<<3), /* turn on interrupts */
+ Loop= (1<<4), /* loop back */
+ Lstat= 5, /* line status */
+ Inready=(1<<0), /* receive buffer full */
+ Oerror=(1<<1), /* receiver overrun */
+ Perror=(1<<2), /* receiver parity error */
+ Ferror=(1<<3), /* rcv framing error */
+ Outready=(1<<5), /* output buffer empty */
+ Mstat= 6, /* modem status */
+ Ctsc= (1<<0), /* clear to send changed */
+ Dsrc= (1<<1), /* data set ready changed */
+ Rire= (1<<2), /* rising edge of ring indicator */
+ Dcdc= (1<<3), /* data carrier detect changed */
+ Cts= (1<<4), /* complement of clear to send line */
+ Dsr= (1<<5), /* complement of data set ready line */
+ Ring= (1<<6), /* complement of ring indicator line */
+ Dcd= (1<<7), /* complement of data carrier detect line */
+ Scratch=7, /* scratchpad */
+ Dlsb= 0, /* divisor lsb */
+ Dmsb= 1, /* divisor msb */
+
+ Serial= 0,
+ Modem= 1,
+};
+
+typedef struct Uart Uart;
+struct Uart
+{
+ int port;
+ uchar sticky[8]; /* sticky write register values */
+ uchar txbusy;
+
+ void (*rx)(int); /* routine to take a received character */
+ int (*tx)(void); /* routine to get a character to transmit */
+
+ ulong frame;
+ ulong overrun;
+};
+
+static Uart com[2];
+static Uart* uart;
+
+#define UartFREQ 1843200
+
+#define uartwrreg(u,r,v) outb((u)->port + r, (u)->sticky[r] | (v))
+#define uartrdreg(u,r) inb((u)->port + r)
+
+/*
+ * set the baud rate by calculating and setting the baudrate
+ * generator constant. This will work with fairly non-standard
+ * baud rates.
+ */
+static void
+uartsetbaud(Uart *up, int rate)
+{
+ ulong brconst;
+
+ brconst = (UartFREQ+8*rate-1)/(16*rate);
+
+ uartwrreg(up, Format, Dra);
+ outb(up->port+Dmsb, (brconst>>8) & 0xff);
+ outb(up->port+Dlsb, brconst & 0xff);
+ uartwrreg(up, Format, 0);
+}
+
+/*
+ * toggle DTR
+ */
+static void
+uartdtr(Uart *up, int n)
+{
+ if(n)
+ up->sticky[Mctl] |= Dtr;
+ else
+ up->sticky[Mctl] &= ~Dtr;
+ uartwrreg(up, Mctl, 0);
+}
+
+/*
+ * toggle RTS
+ */
+static void
+uartrts(Uart *up, int n)
+{
+ if(n)
+ up->sticky[Mctl] |= Rts;
+ else
+ up->sticky[Mctl] &= ~Rts;
+ uartwrreg(up, Mctl, 0);
+}
+
+static void
+uartintr(Ureg*, void *arg)
+{
+ Uart *up;
+ int ch;
+ int s, l, loops;
+
+ up = arg;
+ for(loops = 0; loops < 1024; loops++){
+ s = uartrdreg(up, Istat);
+ switch(s & 0x3F){
+ case 6: /* receiver line status */
+ l = uartrdreg(up, Lstat);
+ if(l & Ferror)
+ up->frame++;
+ if(l & Oerror)
+ up->overrun++;
+ break;
+
+ case 4: /* received data available */
+ case 12:
+ ch = inb(up->port+Data);
+ if(up->rx)
+ (*up->rx)(ch);
+ break;
+
+ case 2: /* transmitter empty */
+ ch = -1;
+ if(up->tx)
+ ch = (*up->tx)();
+ if(ch != -1)
+ outb(up->port+Data, ch);
+ else
+ up->txbusy = 0;
+ break;
+
+ case 0: /* modem status */
+ uartrdreg(up, Mstat);
+ break;
+
+ default:
+ if(s&1)
+ return;
+ print("weird modem interrupt #%2.2ux\n", s);
+ break;
+ }
+ }
+ panic("uartintr: 0x%2.2ux\n", uartrdreg(up, Istat));
+}
+
+/*
+ * turn on a port's interrupts. set DTR and RTS
+ */
+static void
+uartenable(Uart *up)
+{
+ /*
+ * turn on interrupts
+ */
+ up->sticky[Iena] = 0;
+ if(up->tx)
+ up->sticky[Iena] |= Ixmt;
+ if(up->rx)
+ up->sticky[Iena] |= Ircv|Irstat;
+ uartwrreg(up, Iena, 0);
+
+ /*
+ * turn on DTR and RTS
+ */
+ uartdtr(up, 1);
+ uartrts(up, 1);
+}
+
+static void
+uartdisable(Uart* up)
+{
+ /*
+ * Disable interrupts.
+ */
+ up->sticky[Iena] = 0;
+ uartwrreg(up, Iena, 0);
+ uartdtr(up, 0);
+ uartrts(up, 0);
+}
+
+void
+uartspecial(int port, void (*rx)(int), int (*tx)(void), int baud)
+{
+ Uart *up;
+ int vector;
+
+ switch(port){
+ case 0:
+ port = 0x3F8;
+ vector = VectorUART0;
+ up = &com[0];
+ break;
+ case 1:
+ port = 0x2F8;
+ vector = VectorUART1;
+ up = &com[1];
+ break;
+ default:
+ return;
+ }
+
+ if(uart != nil && uart != up)
+ uartdisable(uart);
+ uart = up;
+
+ if(up->port == 0){
+ up->port = port;
+ setvec(vector, uartintr, up);
+ }
+
+ /*
+ * set rate to 9600 baud.
+ * 8 bits/character.
+ * 1 stop bit.
+ * interrupts enabled.
+ */
+ uartsetbaud(up, 9600);
+ up->sticky[Format] = Bits8;
+ uartwrreg(up, Format, 0);
+ up->sticky[Mctl] |= Inton;
+ uartwrreg(up, Mctl, 0x0);
+
+ up->rx = rx;
+ up->tx = tx;
+ uartenable(up);
+ if(baud)
+ uartsetbaud(up, baud);
+}
+
+void
+uartputc(int c)
+{
+ int i;
+ Uart *up;
+
+ if((up = uart) == nil)
+ return;
+ for(i = 0; i < 100; i++){
+ if(uartrdreg(up, Lstat) & Outready)
+ break;
+ delay(1);
+ }
+ outb(up->port+Data, c);
+}
+
+void
+uartputs(IOQ *q, char *s, int n)
+{
+ Uart *up;
+ int c, x;
+
+ if((up = uart) == nil)
+ return;
+ while(n--){
+ if(*s == '\n')
+ q->putc(q, '\r');
+ q->putc(q, *s++);
+ }
+ x = splhi();
+ if(up->txbusy == 0 && (c = q->getc(q)) != -1){
+ uartputc(c & 0xFF);
+ up->txbusy = 1;
+ }
+ splx(x);
+}
+
+void
+uartdrain(void)
+{
+ Uart *up;
+ int timeo;
+
+ if((up = uart) == nil)
+ return;
+ for(timeo = 0; timeo < 10000 && up->txbusy; timeo++)
+ delay(1);
+}
diff --git a/os/boot/pc/LICENCE b/os/boot/pc/LICENCE
new file mode 100644
index 00000000..a4418218
--- /dev/null
+++ b/os/boot/pc/LICENCE
@@ -0,0 +1,237 @@
+Lucent Public License Version 1.02
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS PUBLIC
+LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE
+PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+"Contribution" means:
+
+ a. in the case of Lucent Technologies Inc. ("LUCENT"), the Original
+ Program, and
+ b. in the case of each Contributor,
+
+ i. changes to the Program, and
+ ii. additions to the Program;
+
+ where such changes and/or additions to the Program were added to the
+ Program by such Contributor itself or anyone acting on such
+ Contributor's behalf, and the Contributor explicitly consents, in
+ accordance with Section 3C, to characterization of the changes and/or
+ additions as Contributions.
+
+"Contributor" means LUCENT and any other entity that has Contributed a
+Contribution to the Program.
+
+"Distributor" means a Recipient that distributes the Program,
+modifications to the Program, or any part thereof.
+
+"Licensed Patents" mean patent claims licensable by a Contributor
+which are necessarily infringed by the use or sale of its Contribution
+alone or when combined with the Program.
+
+"Original Program" means the original version of the software
+accompanying this Agreement as released by LUCENT, including source
+code, object code and documentation, if any.
+
+"Program" means the Original Program and Contributions or any part
+thereof
+
+"Recipient" means anyone who receives the Program under this
+Agreement, including all Contributors.
+
+2. GRANT OF RIGHTS
+
+ a. Subject to the terms of this Agreement, each Contributor hereby
+ grants Recipient a non-exclusive, worldwide, royalty-free copyright
+ license to reproduce, prepare derivative works of, publicly display,
+ publicly perform, distribute and sublicense the Contribution of such
+ Contributor, if any, and such derivative works, in source code and
+ object code form.
+
+ b. Subject to the terms of this Agreement, each Contributor hereby
+ grants Recipient a non-exclusive, worldwide, royalty-free patent
+ license under Licensed Patents to make, use, sell, offer to sell,
+ import and otherwise transfer the Contribution of such Contributor, if
+ any, in source code and object code form. The patent license granted
+ by a Contributor shall also apply to the combination of the
+ Contribution of that Contributor and the Program if, at the time the
+ Contribution is added by the Contributor, such addition of the
+ Contribution causes such combination to be covered by the Licensed
+ Patents. The patent license granted by a Contributor shall not apply
+ to (i) any other combinations which include the Contribution, nor to
+ (ii) Contributions of other Contributors. No hardware per se is
+ licensed hereunder.
+
+ c. Recipient understands that although each Contributor grants the
+ licenses to its Contributions set forth herein, no assurances are
+ provided by any Contributor that the Program does not infringe the
+ patent or other intellectual property rights of any other entity. Each
+ Contributor disclaims any liability to Recipient for claims brought by
+ any other entity based on infringement of intellectual property rights
+ or otherwise. As a condition to exercising the rights and licenses
+ granted hereunder, each Recipient hereby assumes sole responsibility
+ to secure any other intellectual property rights needed, if any. For
+ example, if a third party patent license is required to allow
+ Recipient to distribute the Program, it is Recipient's responsibility
+ to acquire that license before distributing the Program.
+
+ d. Each Contributor represents that to its knowledge it has sufficient
+ copyright rights in its Contribution, if any, to grant the copyright
+ license set forth in this Agreement.
+
+3. REQUIREMENTS
+
+A. Distributor may choose to distribute the Program in any form under
+this Agreement or under its own license agreement, provided that:
+
+ a. it complies with the terms and conditions of this Agreement;
+
+ b. if the Program is distributed in source code or other tangible
+ form, a copy of this Agreement or Distributor's own license agreement
+ is included with each copy of the Program; and
+
+ c. if distributed under Distributor's own license agreement, such
+ license agreement:
+
+ i. effectively disclaims on behalf of all Contributors all warranties
+ and conditions, express and implied, including warranties or
+ conditions of title and non-infringement, and implied warranties or
+ conditions of merchantability and fitness for a particular purpose;
+ ii. effectively excludes on behalf of all Contributors all liability
+ for damages, including direct, indirect, special, incidental and
+ consequential damages, such as lost profits; and
+ iii. states that any provisions which differ from this Agreement are
+ offered by that Contributor alone and not by any other party.
+
+B. Each Distributor must include the following in a conspicuous
+ location in the Program:
+
+ Copyright (C) 2003, Lucent Technologies Inc. and others. All Rights
+ Reserved.
+
+C. In addition, each Contributor must identify itself as the
+originator of its Contribution in a manner that reasonably allows
+subsequent Recipients to identify the originator of the Contribution.
+Also, each Contributor must agree that the additions and/or changes
+are intended to be a Contribution. Once a Contribution is contributed,
+it may not thereafter be revoked.
+
+4. COMMERCIAL DISTRIBUTION
+
+Commercial distributors of software may accept certain
+responsibilities with respect to end users, business partners and the
+like. While this license is intended to facilitate the commercial use
+of the Program, the Distributor who includes the Program in a
+commercial product offering should do so in a manner which does not
+create potential liability for Contributors. Therefore, if a
+Distributor includes the Program in a commercial product offering,
+such Distributor ("Commercial Distributor") hereby agrees to defend
+and indemnify every Contributor ("Indemnified Contributor") against
+any losses, damages and costs (collectively"Losses") arising from
+claims, lawsuits and other legal actions brought by a third party
+against the Indemnified Contributor to the extent caused by the acts
+or omissions of such Commercial Distributor in connection with its
+distribution of the Program in a commercial product offering. The
+obligations in this section do not apply to any claims or Losses
+relating to any actual or alleged intellectual property infringement.
+In order to qualify, an Indemnified Contributor must: a) promptly
+notify the Commercial Distributor in writing of such claim, and b)
+allow the Commercial Distributor to control, and cooperate with the
+Commercial Distributor in, the defense and any related settlement
+negotiations. The Indemnified Contributor may participate in any such
+claim at its own expense.
+
+For example, a Distributor might include the Program in a commercial
+product offering, Product X. That Distributor is then a Commercial
+Distributor. If that Commercial Distributor then makes performance
+claims, or offers warranties related to Product X, those performance
+claims and warranties are such Commercial Distributor's responsibility
+alone. Under this section, the Commercial Distributor would have to
+defend claims against the Contributors related to those performance
+claims and warranties, and if a court requires any Contributor to pay
+any damages as a result, the Commercial Distributor must pay those
+damages.
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
+PROVIDED ON AN"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
+WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
+OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely
+responsible for determining the appropriateness of using and
+distributing the Program and assumes all risks associated with its
+exercise of rights under this Agreement, including but not limited to
+the risks and costs of program errors, compliance with applicable
+laws, damage to or loss of data, programs or equipment, and
+unavailability or interruption of operations.
+
+6. DISCLAIMER OF LIABILITY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR
+ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING
+WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR
+DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+7. EXPORT CONTROL
+
+Recipient agrees that Recipient alone is responsible for compliance
+with the United States export administration regulations (and the
+export control laws and regulation of any other countries).
+
+8. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under
+applicable law, it shall not affect the validity or enforceability of
+the remainder of the terms of this Agreement, and without further
+action by the parties hereto, such provision shall be reformed to the
+minimum extent necessary to make such provision valid and enforceable.
+
+If Recipient institutes patent litigation against a Contributor with
+respect to a patent applicable to software (including a cross-claim or
+counterclaim in a lawsuit), then any patent licenses granted by that
+Contributor to such Recipient under this Agreement shall terminate as
+of the date such litigation is filed. In addition, if Recipient
+institutes patent litigation against any entity (including a
+cross-claim or counterclaim in a lawsuit) alleging that the Program
+itself (excluding combinations of the Program with other software or
+hardware) infringes such Recipient's patent(s), then such Recipient's
+rights granted under Section 2(b) shall terminate as of the date such
+litigation is filed.
+
+All Recipient's rights under this Agreement shall terminate if it
+fails to comply with any of the material terms or conditions of this
+Agreement and does not cure such failure in a reasonable period of
+time after becoming aware of such noncompliance. If all Recipient's
+rights under this Agreement terminate, Recipient agrees to cease use
+and distribution of the Program as soon as reasonably practicable.
+However, Recipient's obligations under this Agreement and any licenses
+granted by Recipient relating to the Program shall continue and
+survive.
+
+LUCENT may publish new versions (including revisions) of this
+Agreement from time to time. Each new version of the Agreement will be
+given a distinguishing version number. The Program (including
+Contributions) may always be distributed subject to the version of the
+Agreement under which it was received. In addition, after a new
+version of the Agreement is published, Contributor may elect to
+distribute the Program (including its Contributions) under the new
+version. No one other than LUCENT has the right to modify this
+Agreement. Except as expressly stated in Sections 2(a) and 2(b) above,
+Recipient receives no rights or licenses to the intellectual property
+of any Contributor under this Agreement, whether expressly, by
+implication, estoppel or otherwise. All rights in the Program not
+expressly granted under this Agreement are reserved.
+
+This Agreement is governed by the laws of the State of New York and
+the intellectual property laws of the United States of America. No
+party to this Agreement will bring a legal action under this Agreement
+more than one year after the cause of action arose. Each party waives
+its rights to a jury trial in any resulting litigation.
+
diff --git a/os/boot/pc/NOTICE b/os/boot/pc/NOTICE
new file mode 100644
index 00000000..2982c51d
--- /dev/null
+++ b/os/boot/pc/NOTICE
@@ -0,0 +1,4 @@
+Copyright Ā© 1995-2006 Lucent Technologies Inc. and others. All rights reserved.
+Revisions for use with Inferno Ā© 2003-2006 Vita Nuova Holdings Limited.
+
+This software is provided under the terms of the Lucent Public License, Version 1.02.
diff --git a/os/boot/pc/alarm.c b/os/boot/pc/alarm.c
new file mode 100644
index 00000000..2aa4431a
--- /dev/null
+++ b/os/boot/pc/alarm.c
@@ -0,0 +1,123 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#define MAXALARM 10
+
+Alarm alarmtab[MAXALARM];
+
+/*
+ * Insert new into list after where
+ */
+void
+insert(List **head, List *where, List *new)
+{
+ if(where == 0){
+ new->next = *head;
+ *head = new;
+ }else{
+ new->next = where->next;
+ where->next = new;
+ }
+
+}
+
+/*
+ * Delete old from list. where->next is known to be old.
+ */
+void
+delete(List **head, List *where, List *old)
+{
+ if(where == 0){
+ *head = old->next;
+ return;
+ }
+ where->next = old->next;
+}
+
+Alarm*
+newalarm(void)
+{
+ int i;
+ Alarm *a;
+
+ for(i=0,a=alarmtab; i < nelem(alarmtab); i++,a++)
+ if(a->busy==0 && a->f==0){
+ a->f = 0;
+ a->arg = 0;
+ a->busy = 1;
+ return a;
+ }
+ panic("newalarm");
+ return 0; /* not reached */
+}
+
+Alarm*
+alarm(int ms, void (*f)(Alarm*), void *arg)
+{
+ Alarm *a, *w, *pw;
+ ulong s;
+
+ if(ms < 0)
+ ms = 0;
+ s = splhi();
+ a = newalarm();
+ a->dt = MS2TK(ms);
+ a->f = f;
+ a->arg = arg;
+ pw = 0;
+ for(w=m->alarm; w; pw=w, w=w->next){
+ if(w->dt <= a->dt){
+ a->dt -= w->dt;
+ continue;
+ }
+ w->dt -= a->dt;
+ break;
+ }
+ insert(&m->alarm, pw, a);
+ splx(s);
+ return a;
+}
+
+void
+cancel(Alarm *a)
+{
+ a->f = 0;
+}
+
+void
+alarminit(void)
+{
+}
+
+#define NA 10 /* alarms per clock tick */
+void
+checkalarms(void)
+{
+ int i, n, s;
+ Alarm *a;
+ void (*f)(Alarm*);
+ Alarm *alist[NA];
+
+ s = splhi();
+ a = m->alarm;
+ if(a){
+ for(n=0; a && a->dt<=0 && n<NA; n++){
+ alist[n] = a;
+ delete(&m->alarm, 0, a);
+ a = m->alarm;
+ }
+ if(a)
+ a->dt--;
+
+ for(i = 0; i < n; i++){
+ f = alist[i]->f; /* avoid race with cancel */
+ if(f)
+ (*f)(alist[i]);
+ alist[i]->busy = 0;
+ }
+ }
+ splx(s);
+}
diff --git a/os/boot/pc/apm.c b/os/boot/pc/apm.c
new file mode 100644
index 00000000..6c662d6c
--- /dev/null
+++ b/os/boot/pc/apm.c
@@ -0,0 +1,16 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+Apminfo apm;
+
+void
+apminit(void)
+{
+ if(getconf("apm0") && apm.haveinfo)
+ changeconf("apm0=ax=%x ebx=%x cx=%x dx=%x di=%x esi=%x\n",
+ apm.ax, apm.ebx, apm.cx, apm.dx, apm.di, apm.esi);
+}
diff --git a/os/boot/pc/bcom.c b/os/boot/pc/bcom.c
new file mode 100644
index 00000000..62a50a10
--- /dev/null
+++ b/os/boot/pc/bcom.c
@@ -0,0 +1,460 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "fs.h"
+
+Type types[] = {
+ { Tfloppy,
+ Fini|Ffs,
+ floppyinit, floppyinitdev,
+ floppygetfspart, 0, floppyboot,
+ },
+ { Tsd,
+ Fini|Ffs,
+ sdinit, sdinitdev,
+ sdgetfspart, sdaddconf, sdboot,
+ },
+ { Tnil,
+ 0,
+ 0, 0,
+ 0, 0, 0,
+ },
+};
+
+#include "sd.h"
+
+extern SDifc sdataifc;
+extern SDifc sdmylexifc;
+extern SDifc sd53c8xxifc;
+SDifc* sdifc[] = {
+ &sdataifc,
+// &sdmylexifc,
+// &sd53c8xxifc,
+ nil,
+};
+
+typedef struct Mode Mode;
+
+enum {
+ Maxdev = 7,
+ Dany = -1,
+ Nmedia = 16,
+ Nini = 10,
+};
+
+enum { /* mode */
+ Mauto = 0x00,
+ Mlocal = 0x01,
+ Manual = 0x02,
+ NMode = 0x03,
+};
+
+typedef struct Medium Medium;
+struct Medium {
+ Type* type;
+ int flag;
+ int dev;
+ char name[NAMELEN];
+ Fs* inifs;
+
+ Medium* next;
+};
+
+typedef struct Mode {
+ char* name;
+ int mode;
+} Mode;
+
+static Medium media[Nmedia];
+static Medium *curmedium = media;
+
+static Mode modes[NMode+1] = {
+ [Mauto] { "auto", Mauto, },
+ [Mlocal] { "local", Mlocal, },
+ [Manual] { "manual", Manual, },
+};
+
+char *defaultpartition = "new";
+
+static Medium*
+parse(char *line, char **file)
+{
+ char *p;
+ Type *tp;
+ Medium *mp;
+
+ if(p = strchr(line, '!')) {
+ *p++ = 0;
+ *file = p;
+ } else
+ *file = "";
+
+ for(tp = types; tp->type != Tnil; tp++)
+ for(mp = tp->media; mp; mp = mp->next)
+ if(strcmp(mp->name, line) == 0)
+ return mp;
+ return nil;
+}
+
+static int
+boot(Medium *mp, char *file)
+{
+ static Boot b;
+
+ memset(&b, 0, sizeof b);
+ b.state = INIT9LOAD;
+
+// sprint(BOOTLINE, "%s!%s", mp->name, file);
+ return (*mp->type->boot)(mp->dev, file, &b);
+}
+
+static Medium*
+allocm(Type *tp)
+{
+ Medium **l;
+
+ if(curmedium >= &media[Nmedia])
+ return 0;
+
+ for(l = &tp->media; *l; l = &(*l)->next)
+ ;
+ *l = curmedium++;
+ return *l;
+}
+
+char *parts[] = { "dos", "9fat", "fs", 0 };
+
+Medium*
+probe(int type, int flag, int dev)
+{
+ Type *tp;
+ int i;
+ Medium *mp;
+
+ for(tp = types; tp->type != Tnil; tp++){
+ if(type != Tany && type != tp->type)
+ continue;
+
+ if(flag != Fnone){
+ for(mp = tp->media; mp; mp = mp->next){
+ if((flag & mp->flag) && (dev == Dany || dev == mp->dev))
+ return mp;
+ }
+ }
+
+ if((tp->flag & Fprobe) == 0){
+ tp->flag |= Fprobe;
+ tp->mask = (*tp->init)();
+ }
+
+ for(i = 0; tp->mask; i++){
+ if((tp->mask & (1<<i)) == 0)
+ continue;
+ tp->mask &= ~(1<<i);
+
+ if((mp = allocm(tp)) == 0)
+ continue;
+
+ mp->dev = i;
+ mp->flag = tp->flag;
+ mp->type = tp;
+ (*tp->initdev)(i, mp->name);
+
+ if((flag & mp->flag) && (dev == Dany || dev == i))
+ return mp;
+ }
+ }
+
+ return 0;
+}
+
+extern int loopconst;
+void
+main(void)
+{
+ Medium *mp;
+ int flag;
+ char def[2*NAMELEN], line[80], *p, *file;
+ Type *tp;
+
+ i8042a20();
+ memset(m, 0, sizeof(Mach));
+ trapinit();
+ clockinit();
+ alarminit();
+ spllo();
+
+ kbdinit();
+
+ if((ulong)&end > (KZERO|(640*1024)))
+ panic("i'm too big");
+
+ /*
+ * If there were any arguments, MS-DOS leaves a character
+ * count followed by the arguments in the runtime header.
+ * Step over the leading space.
+ */
+ p = (char*)0x80080080;
+ if(p[0]){
+ p[p[0]+1] = 0;
+ p += 2;
+ }
+ else
+ p = 0;
+
+ /*
+ * Advance command line to first option, if any
+ */
+ if(p) {
+ while(*p==' ' || *p=='\t')
+ p++;
+ if(*p == 0)
+ p = nil;
+ }
+
+ /*
+ * Probe everything, to collect device names.
+ */
+ probe(Tany, Fnone, Dany);
+
+ if(p != 0) {
+ if((mp = parse(p, &file)) == nil) {
+ print("bad loadfile syntax: %s\n", p);
+ goto done;
+ }
+ boot(mp, file);
+ }
+
+done:
+ flag = 0;
+ for(tp = types; tp->type != Tnil; tp++){
+ for(mp = tp->media; mp; mp = mp->next){
+ if(flag == 0){
+ flag = 1;
+ print("Load devices:");
+ }
+ print(" %s", mp->name);
+ }
+ }
+ if(flag)
+ print("\n");
+
+ for(;;){
+ if(getstr("load from", line, sizeof(line), nil, 0) >= 0)
+ if(mp = parse(line, &file))
+ boot(mp, file);
+ def[0] = 0;
+ }
+}
+
+int
+getfields(char *lp, char **fields, int n, char sep)
+{
+ int i;
+
+ for(i = 0; lp && *lp && i < n; i++){
+ while(*lp == sep)
+ *lp++ = 0;
+ if(*lp == 0)
+ break;
+ fields[i] = lp;
+ while(*lp && *lp != sep){
+ if(*lp == '\\' && *(lp+1) == '\n')
+ *lp++ = ' ';
+ lp++;
+ }
+ }
+ return i;
+}
+
+int
+cistrcmp(char *a, char *b)
+{
+ int ac, bc;
+
+ for(;;){
+ ac = *a++;
+ bc = *b++;
+
+ if(ac >= 'A' && ac <= 'Z')
+ ac = 'a' + (ac - 'A');
+ if(bc >= 'A' && bc <= 'Z')
+ bc = 'a' + (bc - 'A');
+ ac -= bc;
+ if(ac)
+ return ac;
+ if(bc == 0)
+ break;
+ }
+ return 0;
+}
+
+int
+cistrncmp(char *a, char *b, int n)
+{
+ unsigned ac, bc;
+
+ while(n > 0){
+ ac = *a++;
+ bc = *b++;
+ n--;
+
+ if(ac >= 'A' && ac <= 'Z')
+ ac = 'a' + (ac - 'A');
+ if(bc >= 'A' && bc <= 'Z')
+ bc = 'a' + (bc - 'A');
+
+ ac -= bc;
+ if(ac)
+ return ac;
+ if(bc == 0)
+ break;
+ }
+
+ return 0;
+}
+
+void*
+ialloc(ulong n, int align)
+{
+
+ static ulong palloc;
+ ulong p;
+ int a;
+
+ if(palloc == 0)
+ palloc = 3*1024*1024;
+
+ p = palloc;
+ if(align <= 0)
+ align = 4;
+ if(a = n % align)
+ n += align - a;
+ if(a = p % align)
+ p += align - a;
+
+ palloc = p+n;
+
+ return memset((void*)(p|KZERO), 0, n);
+}
+
+void*
+xspanalloc(ulong size, int align, ulong span)
+{
+ ulong a, v;
+
+ a = (ulong)ialloc(size+align+span, 0);
+
+ if(span > 2)
+ v = (a + span) & ~(span-1);
+ else
+ v = a;
+
+ if(align > 1)
+ v = (v + align) & ~(align-1);
+
+ return (void*)v;
+}
+
+static Block *allocbp;
+
+Block*
+allocb(int size)
+{
+ Block *bp, **lbp;
+ ulong addr;
+
+ lbp = &allocbp;
+ for(bp = *lbp; bp; bp = bp->next){
+ if((bp->lim - bp->base) >= size){
+ *lbp = bp->next;
+ break;
+ }
+ lbp = &bp->next;
+ }
+ if(bp == 0){
+ bp = ialloc(sizeof(Block)+size+64, 0);
+ addr = (ulong)bp;
+ addr = ROUNDUP(addr + sizeof(Block), 8);
+ bp->base = (uchar*)addr;
+ bp->lim = ((uchar*)bp) + sizeof(Block)+size+64;
+ }
+
+ if(bp->flag)
+ panic("allocb reuse\n");
+
+ bp->rp = bp->base;
+ bp->wp = bp->rp;
+ bp->next = 0;
+ bp->flag = 1;
+
+ return bp;
+}
+
+void
+freeb(Block* bp)
+{
+ bp->next = allocbp;
+ allocbp = bp;
+
+ bp->flag = 0;
+}
+
+enum {
+ Paddr= 0x70, /* address port */
+ Pdata= 0x71, /* data port */
+};
+
+uchar
+nvramread(int offset)
+{
+ outb(Paddr, offset);
+ return inb(Pdata);
+}
+
+void (*etherdetach)(void);
+void (*floppydetach)(void);
+void (*sddetach)(void);
+
+void
+warp9(ulong entry)
+{
+ if(etherdetach)
+ etherdetach();
+ consdrain();
+ (*(void(*)(void))(PADDR(entry)))();
+}
+
+char*
+getconf(char*)
+{
+ return nil;
+}
+
+void
+addconf(char*, ...)
+{
+}
+
+void
+uartspecial(int, void(*)(int), int(*)(void), int)
+{
+}
+
+void
+uartputs(IOQ*, char*, int)
+{
+}
+
+void
+uartputc(int)
+{}
+
+void
+uartdrain(void)
+{
+}
diff --git a/os/boot/pc/boot.c b/os/boot/pc/boot.c
new file mode 100644
index 00000000..0659edbd
--- /dev/null
+++ b/os/boot/pc/boot.c
@@ -0,0 +1,451 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "../../../utils/libmach/elf.h"
+
+static uchar elfident[7] = {
+ '\177', 'E', 'L', 'F', '\1', '\1', '\1'
+};
+static Ehdr ehdr, rehdr;
+static Phdr *phdr;
+static int curphdr;
+static ulong curoff;
+static ulong elftotal;
+static long (*swal)(long);
+static ushort (*swab)(ushort);
+
+/*
+ * big-endian short
+ */
+ushort
+beswab(ushort s)
+{
+ uchar *p;
+
+ p = (uchar*)&s;
+ return (p[0]<<8) | p[1];
+}
+
+/*
+ * big-endian long
+ */
+long
+beswal(long l)
+{
+ uchar *p;
+
+ p = (uchar*)&l;
+ return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
+}
+
+/*
+ * little-endian short
+ */
+ushort
+leswab(ushort s)
+{
+ uchar *p;
+
+ p = (uchar*)&s;
+ return (p[1]<<8) | p[0];
+}
+
+/*
+ * little-endian long
+ */
+long
+leswal(long l)
+{
+ uchar *p;
+
+ p = (uchar*)&l;
+ return (p[3]<<24) | (p[2]<<16) | (p[1]<<8) | p[0];
+}
+
+/*
+ * Convert header to canonical form
+ */
+static void
+hswal(long *lp, int n, long (*swap) (long))
+{
+ while (n--) {
+ *lp = (*swap) (*lp);
+ lp++;
+ }
+}
+
+static int
+readehdr(Boot *b)
+{
+ int i;
+
+ /* bitswap the header according to the DATA format */
+ if(ehdr.ident[CLASS] != ELFCLASS32) {
+ print("bad ELF class - not 32 bit\n");
+ return 0;
+ }
+ if(ehdr.ident[DATA] == ELFDATA2LSB) {
+ swab = leswab;
+ swal = leswal;
+ } else if(ehdr.ident[DATA] == ELFDATA2MSB) {
+ swab = beswab;
+ swal = beswal;
+ } else {
+ print("bad ELF encoding - not big or little endian\n");
+ return 0;
+ }
+ memmove(&rehdr, &ehdr, sizeof(Ehdr));
+
+ ehdr.type = swab(ehdr.type);
+ ehdr.machine = swab(ehdr.machine);
+ ehdr.version = swal(ehdr.version);
+ ehdr.elfentry = swal(ehdr.elfentry);
+ ehdr.phoff = swal(ehdr.phoff);
+ ehdr.shoff = swal(ehdr.shoff);
+ ehdr.flags = swal(ehdr.flags);
+ ehdr.ehsize = swab(ehdr.ehsize);
+ ehdr.phentsize = swab(ehdr.phentsize);
+ ehdr.phnum = swab(ehdr.phnum);
+ ehdr.shentsize = swab(ehdr.shentsize);
+ ehdr.shnum = swab(ehdr.shnum);
+ ehdr.shstrndx = swab(ehdr.shstrndx);
+ if(ehdr.type != EXEC || ehdr.version != CURRENT)
+ return 0;
+ if(ehdr.phentsize != sizeof(Phdr))
+ return 0;
+
+ if(debug)
+ print("readehdr OK entry 0x%lux\n", ehdr.elfentry);
+
+ curoff = sizeof(Ehdr);
+ i = ehdr.phoff+ehdr.phentsize*ehdr.phnum - curoff;
+ b->state = READPHDR;
+ b->bp = (char*)malloc(i);
+ b->wp = b->bp;
+ b->ep = b->wp + i;
+ phdr = (Phdr*)(b->bp + ehdr.phoff-sizeof(Ehdr));
+ if(debug)
+ print("phdr...");
+
+ return 1;
+}
+
+static int
+nextphdr(Boot *b)
+{
+ Phdr *php;
+ ulong entry, offset;
+ char *paddr;
+
+ if(debug)
+ print("readedata %d\n", curphdr);
+
+ for(; curphdr < ehdr.phnum; curphdr++){
+ php = phdr+curphdr;
+ if(php->type != LOAD)
+ continue;
+ offset = php->offset;
+ paddr = (char*)PADDR(php->paddr);
+ if(offset < curoff){
+ /*
+ * Can't (be bothered to) rewind the
+ * input, it might be from tftp. If we
+ * did then we could boot FreeBSD kernels
+ * too maybe.
+ */
+ return 0;
+ }
+ if(php->offset > curoff){
+ b->state = READEPAD;
+ b->bp = (char*)malloc(offset - curoff);
+ b->wp = b->bp;
+ b->ep = b->wp + offset - curoff;
+ if(debug)
+ print("nextphdr %lud...\n", offset - curoff);
+ return 1;
+ }
+ b->state = READEDATA;
+ b->bp = paddr;
+ b->wp = b->bp;
+ b->ep = b->wp+php->filesz;
+ print("%ud+", php->filesz);
+ elftotal += php->filesz;
+ if(debug)
+ print("nextphdr %ud@0x%p\n", php->filesz, paddr);
+
+ return 1;
+ }
+
+ if(curphdr != 0){
+ print("=%lud\n", elftotal);
+ b->state = TRYBOOT;
+ entry = ehdr.elfentry & ~0xF0000000;
+ PLLONG(b->exec.entry, entry);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+readepad(Boot *b)
+{
+ Phdr *php;
+
+ php = phdr+curphdr;
+ if(debug)
+ print("readepad %d\n", curphdr);
+ curoff = php->offset;
+
+ return nextphdr(b);
+}
+
+static int
+readedata(Boot *b)
+{
+ Phdr *php;
+
+ php = phdr+curphdr;
+ if(debug)
+ print("readedata %d\n", curphdr);
+ if(php->filesz < php->memsz){
+ print("%lud", php->memsz-php->filesz);
+ elftotal += php->memsz-php->filesz;
+ memset((char*)(PADDR(php->paddr)+php->filesz), 0, php->memsz-php->filesz);
+ }
+ curoff = php->offset+php->filesz;
+ curphdr++;
+
+ return nextphdr(b);
+}
+
+static int
+readphdr(Boot *b)
+{
+ Phdr *php;
+
+ php = phdr;
+ hswal((long*)php, ehdr.phentsize*ehdr.phnum/sizeof(long), swal);
+ if(debug)
+ print("phdr curoff %lud vaddr 0x%lux paddr 0x%lux\n",
+ curoff, php->vaddr, php->paddr);
+
+ curoff = ehdr.phoff+ehdr.phentsize*ehdr.phnum;
+ curphdr = 0;
+
+ return nextphdr(b);
+}
+
+static int
+addbytes(char **dbuf, char *edbuf, char **sbuf, char *esbuf)
+{
+ int n;
+
+ n = edbuf - *dbuf;
+ if(n <= 0)
+ return 0;
+ if(n > esbuf - *sbuf)
+ n = esbuf - *sbuf;
+ if(n <= 0)
+ return -1;
+
+ memmove(*dbuf, *sbuf, n);
+ *sbuf += n;
+ *dbuf += n;
+ return edbuf - *dbuf;
+}
+
+int
+bootpass(Boot *b, void *vbuf, int nbuf)
+{
+ char *buf, *ebuf;
+ Exec *ep;
+ ulong entry, data, text, bss;
+
+ if(b->state == FAILED)
+ return FAIL;
+
+ if(nbuf == 0)
+ goto Endofinput;
+
+ buf = vbuf;
+ ebuf = buf+nbuf;
+ while(addbytes(&b->wp, b->ep, &buf, ebuf) == 0) {
+ switch(b->state) {
+ case INITKERNEL:
+ b->state = READEXEC;
+ b->bp = (char*)&b->exec;
+ b->wp = b->bp;
+ b->ep = b->bp+sizeof(Exec);
+ break;
+ case READEXEC:
+ ep = &b->exec;
+ if(GLLONG(ep->magic) == I_MAGIC) {
+ b->state = READ9TEXT;
+ b->bp = (char*)PADDR(GLLONG(ep->entry));
+ b->wp = b->bp;
+ b->ep = b->wp+GLLONG(ep->text);
+ print("%lud", GLLONG(ep->text));
+ break;
+ }
+
+ /* check for gzipped kernel */
+ if(b->bp[0] == 0x1F && (uchar)b->bp[1] == 0x8B && b->bp[2] == 0x08) {
+ b->state = READGZIP;
+ b->bp = (char*)malloc(1440*1024);
+ b->wp = b->bp;
+ b->ep = b->wp + 1440*1024;
+ memmove(b->bp, &b->exec, sizeof(Exec));
+ b->wp += sizeof(Exec);
+ print("gz...");
+ break;
+ }
+
+ /*
+ * Check for ELF.
+ */
+ if(memcmp(b->bp, elfident, 4) == 0){
+ b->state = READEHDR;
+ b->bp = (char*)&ehdr;
+ b->wp = b->bp;
+ b->ep = b->wp + sizeof(Ehdr);
+ memmove(b->bp, &b->exec, sizeof(Exec));
+ b->wp += sizeof(Exec);
+ print("elf...");
+ break;
+ }
+
+ print("bad kernel format\n");
+ b->state = FAILED;
+ return FAIL;
+
+ case READ9TEXT:
+ ep = &b->exec;
+ b->state = READ9DATA;
+ b->bp = (char*)PGROUND(PADDR(GLLONG(ep->entry))+GLLONG(ep->text));
+ b->wp = b->bp;
+ b->ep = b->wp + GLLONG(ep->data);
+ print("+%ld", GLLONG(ep->data));
+ break;
+
+ case READ9DATA:
+ ep = &b->exec;
+ bss = GLLONG(ep->bss);
+ print("+%ld=%ld\n",
+ bss, GLLONG(ep->text)+GLLONG(ep->data)+bss);
+ b->state = TRYBOOT;
+ return ENOUGH;
+
+ case READEHDR:
+ if(!readehdr(b)){
+ print("readehdr failed\n");
+ b->state = FAILED;
+ return FAIL;
+ }
+ break;
+
+ case READPHDR:
+ if(!readphdr(b)){
+ b->state = FAILED;
+ return FAIL;
+ }
+ break;
+
+ case READEPAD:
+ if(!readepad(b)){
+ b->state = FAILED;
+ return FAIL;
+ }
+ break;
+
+ case READEDATA:
+ if(!readedata(b)){
+ b->state = FAILED;
+ return FAIL;
+ }
+ if(b->state == TRYBOOT)
+ return ENOUGH;
+ break;
+
+ case TRYBOOT:
+ case READGZIP:
+ return ENOUGH;
+
+ case READ9LOAD:
+ case INIT9LOAD:
+ panic("9load");
+
+ default:
+ panic("bootstate");
+ }
+ }
+ return MORE;
+
+
+Endofinput:
+ /* end of input */
+ switch(b->state) {
+ case INITKERNEL:
+ case READEXEC:
+ case READ9TEXT:
+ case READ9DATA:
+ case READEHDR:
+ case READPHDR:
+ case READEPAD:
+ case READEDATA:
+ print("premature EOF\n");
+ b->state = FAILED;
+ return FAIL;
+
+ case TRYBOOT:
+ entry = GLLONG(b->exec.entry);
+ print("entry: 0x%lux\n", entry);
+ warp9(PADDR(entry));
+ b->state = FAILED;
+ return FAIL;
+
+ case READGZIP:
+ ep = &b->exec;
+ if(b->bp[0] != 0x1F || (uchar)b->bp[1] != 0x8B || b->bp[2] != 0x08)
+ print("lost magic\n");
+
+ print("%ld => ", b->wp - b->bp);
+ if(gunzip((uchar*)ep, sizeof(*ep), (uchar*)b->bp, b->wp - b->bp) < sizeof(*ep)) {
+ print("badly compressed kernel\n");
+ return FAIL;
+ }
+
+ entry = GLLONG(ep->entry);
+ text = GLLONG(ep->text);
+ data = GLLONG(ep->data);
+ bss = GLLONG(ep->bss);
+ print("%lud+%lud+%lud=%lud\n", text, data, bss, text+data+bss);
+
+ if(gunzip((uchar*)PADDR(entry)-sizeof(Exec), sizeof(Exec)+text+data,
+ (uchar*)b->bp, b->wp-b->bp) < sizeof(Exec)+text+data) {
+ print("error uncompressing kernel\n");
+ return FAIL;
+ }
+
+ /* relocate data to start at page boundary */
+ memmove((void*)PGROUND(PADDR(entry+text)), (void*)(PADDR(entry+text)), data);
+
+ print("entry: %lux\n", entry);
+ warp9(PADDR(entry));
+ b->state = FAILED;
+ return FAIL;
+
+ case INIT9LOAD:
+ case READ9LOAD:
+ panic("end 9load");
+
+ default:
+ panic("bootdone");
+ }
+ b->state = FAILED;
+ return FAIL;
+}
diff --git a/os/boot/pc/bootld.c b/os/boot/pc/bootld.c
new file mode 100644
index 00000000..e60b2389
--- /dev/null
+++ b/os/boot/pc/bootld.c
@@ -0,0 +1,108 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+static int
+addbytes(char **dbuf, char *edbuf, char **sbuf, char *esbuf)
+{
+ int n;
+
+ n = edbuf - *dbuf;
+ if(n <= 0)
+ return 0;
+ if(n > esbuf - *sbuf)
+ n = esbuf - *sbuf;
+ if(n <= 0)
+ return -1;
+
+ memmove(*dbuf, *sbuf, n);
+ *sbuf += n;
+ *dbuf += n;
+ return edbuf - *dbuf;
+}
+
+extern void origin(void);
+
+int
+bootpass(Boot *b, void *vbuf, int nbuf)
+{
+ char *buf, *ebuf, *p, *q;
+ ulong size;
+
+ if(b->state == FAILED)
+ return FAIL;
+
+ if(nbuf == 0)
+ goto Endofinput;
+
+ buf = vbuf;
+ ebuf = buf+nbuf;
+ while(addbytes(&b->wp, b->ep, &buf, ebuf) == 0) {
+ switch(b->state) {
+ case INIT9LOAD:
+ b->state = READ9LOAD;
+ b->bp = (char*)0x10000;
+ b->wp = b->bp;
+ b->ep = b->bp + 256*1024;
+ break;
+
+ case READ9LOAD:
+ return ENOUGH;
+
+ default:
+ panic("bootstate");
+ }
+ }
+ return MORE;
+
+
+Endofinput:
+ /* end of input */
+ print("\n");
+ switch(b->state) {
+ case INIT9LOAD:
+ print("premature EOF\n");
+ b->state = FAILED;
+ return FAIL;
+
+ case READ9LOAD:
+ size = b->wp - b->bp;
+ if(memcmp(b->bp, origin, 16) != 0) {
+ print("beginning of file does not look right\n");
+ b->state = FAILED;
+ return FAIL;
+ }
+ if(size < 32*1024 || size > 256*1024) {
+ print("got %lud byte loader; not likely\n", size);
+ b->state = FAILED;
+ return FAIL;
+ }
+
+ p = b->bp;
+ q = b->wp;
+ if(q - p > 10000) /* don't search much past l.s */
+ q = p+10000;
+
+ /*
+ * The string `JUMP' appears right before
+ * tokzero, which is where we want to jump.
+ */
+ for(; p<q; p++) {
+ if(strncmp(p, "JUMP", 4) == 0) {
+ p += 4;
+ warp9((ulong)p);
+ }
+ }
+ print("could not find jump destination\n");
+ b->state = FAILED;
+ return FAIL;
+
+ default:
+ panic("bootdone");
+ }
+ b->state = FAILED;
+ return FAIL;
+}
diff --git a/os/boot/pc/bootp.c b/os/boot/pc/bootp.c
new file mode 100644
index 00000000..64df7e14
--- /dev/null
+++ b/os/boot/pc/bootp.c
@@ -0,0 +1,652 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "ip.h"
+
+uchar broadcast[Eaddrlen] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+};
+
+static ushort tftpport = 5000;
+static int Id = 1;
+static Netaddr myaddr;
+static Netaddr server;
+
+typedef struct {
+ uchar header[4];
+ uchar data[Segsize];
+} Tftp;
+static Tftp tftpb;
+
+static void
+hnputs(uchar *ptr, ushort val)
+{
+ ptr[0] = val>>8;
+ ptr[1] = val;
+}
+
+static void
+hnputl(uchar *ptr, ulong val)
+{
+ ptr[0] = val>>24;
+ ptr[1] = val>>16;
+ ptr[2] = val>>8;
+ ptr[3] = val;
+}
+
+static ulong
+nhgetl(uchar *ptr)
+{
+ return ((ptr[0]<<24) | (ptr[1]<<16) | (ptr[2]<<8) | ptr[3]);
+}
+
+static ushort
+nhgets(uchar *ptr)
+{
+ return ((ptr[0]<<8) | ptr[1]);
+}
+
+static short endian = 1;
+static char* aendian = (char*)&endian;
+#define LITTLE *aendian
+
+static ushort
+ptcl_csum(void *a, int len)
+{
+ uchar *addr;
+ ulong t1, t2;
+ ulong losum, hisum, mdsum, x;
+
+ addr = a;
+ losum = 0;
+ hisum = 0;
+ mdsum = 0;
+
+ x = 0;
+ if((ulong)addr & 1) {
+ if(len) {
+ hisum += addr[0];
+ len--;
+ addr++;
+ }
+ x = 1;
+ }
+ while(len >= 16) {
+ t1 = *(ushort*)(addr+0);
+ t2 = *(ushort*)(addr+2); mdsum += t1;
+ t1 = *(ushort*)(addr+4); mdsum += t2;
+ t2 = *(ushort*)(addr+6); mdsum += t1;
+ t1 = *(ushort*)(addr+8); mdsum += t2;
+ t2 = *(ushort*)(addr+10); mdsum += t1;
+ t1 = *(ushort*)(addr+12); mdsum += t2;
+ t2 = *(ushort*)(addr+14); mdsum += t1;
+ mdsum += t2;
+ len -= 16;
+ addr += 16;
+ }
+ while(len >= 2) {
+ mdsum += *(ushort*)addr;
+ len -= 2;
+ addr += 2;
+ }
+ if(x) {
+ if(len)
+ losum += addr[0];
+ if(LITTLE)
+ losum += mdsum;
+ else
+ hisum += mdsum;
+ } else {
+ if(len)
+ hisum += addr[0];
+ if(LITTLE)
+ hisum += mdsum;
+ else
+ losum += mdsum;
+ }
+
+ losum += hisum >> 8;
+ losum += (hisum & 0xff) << 8;
+ while(hisum = losum>>16)
+ losum = hisum + (losum & 0xffff);
+
+ return ~losum;
+}
+
+static ushort
+ip_csum(uchar *addr)
+{
+ int len;
+ ulong sum = 0;
+
+ len = (addr[0]&0xf)<<2;
+
+ while(len > 0) {
+ sum += addr[0]<<8 | addr[1] ;
+ len -= 2;
+ addr += 2;
+ }
+
+ sum = (sum & 0xffff) + (sum >> 16);
+ sum = (sum & 0xffff) + (sum >> 16);
+ return (sum^0xffff);
+}
+
+enum {
+ /* this is only true of IPv4, but we're not doing v6 yet */
+ Min_udp_payload = ETHERMINTU - ETHERHDRSIZE - UDP_HDRSIZE,
+};
+
+static void
+udpsend(int ctlrno, Netaddr *a, void *data, int dlen)
+{
+ char payload[ETHERMAXTU];
+ Udphdr *uh;
+ Etherhdr *ip;
+ Etherpkt pkt;
+ int len, ptcllen;
+
+ /*
+ * if packet is too short, make it longer rather than relying
+ * on ethernet interface or lower layers to pad it.
+ */
+ if (dlen < Min_udp_payload) {
+ memmove(payload, data, dlen);
+ data = payload;
+ dlen = Min_udp_payload;
+ }
+
+ uh = (Udphdr*)&pkt;
+
+ memset(uh, 0, sizeof(Etherpkt));
+ memmove(uh->udpcksum+sizeof(uh->udpcksum), data, dlen);
+
+ /*
+ * UDP portion
+ */
+ ptcllen = dlen + (UDP_HDRSIZE-UDP_PHDRSIZE);
+ uh->ttl = 0;
+ uh->udpproto = IP_UDPPROTO;
+ uh->frag[0] = 0;
+ uh->frag[1] = 0;
+ hnputs(uh->udpplen, ptcllen);
+ hnputl(uh->udpsrc, myaddr.ip);
+ hnputs(uh->udpsport, myaddr.port);
+ hnputl(uh->udpdst, a->ip);
+ hnputs(uh->udpdport, a->port);
+ hnputs(uh->udplen, ptcllen);
+ uh->udpcksum[0] = 0;
+ uh->udpcksum[1] = 0;
+ dlen = (dlen+1)&~1;
+ hnputs(uh->udpcksum, ptcl_csum(&uh->ttl, dlen+UDP_HDRSIZE));
+
+ /*
+ * IP portion
+ */
+ ip = (Etherhdr*)&pkt;
+ len = UDP_EHSIZE+UDP_HDRSIZE+dlen; /* non-descriptive names */
+ ip->vihl = IP_VER|IP_HLEN;
+ ip->tos = 0;
+ ip->ttl = 255;
+ hnputs(ip->length, len-ETHER_HDR);
+ hnputs(ip->id, Id++);
+ ip->frag[0] = 0;
+ ip->frag[1] = 0;
+ ip->cksum[0] = 0;
+ ip->cksum[1] = 0;
+ hnputs(ip->cksum, ip_csum(&ip->vihl));
+
+ /*
+ * Ethernet MAC portion
+ */
+ hnputs(ip->type, ET_IP);
+ memmove(ip->d, a->ea, sizeof(ip->d));
+
+if(debug) {
+ print("udpsend ");
+}
+ ethertxpkt(ctlrno, &pkt, len, Timeout);
+}
+
+static void
+nak(int ctlrno, Netaddr *a, int code, char *msg, int report)
+{
+ int n;
+ char buf[128];
+
+ buf[0] = 0;
+ buf[1] = Tftp_ERROR;
+ buf[2] = 0;
+ buf[3] = code;
+ strcpy(buf+4, msg);
+ n = strlen(msg) + 4 + 1;
+ udpsend(ctlrno, a, buf, n);
+ if(report)
+ print("\ntftp: error(%d): %s\n", code, msg);
+}
+
+static int
+udprecv(int ctlrno, Netaddr *a, void *data, int dlen)
+{
+ int n, len;
+ ushort csm;
+ Udphdr *h;
+ ulong addr, timo;
+ Etherpkt pkt;
+ static int rxactive;
+
+ if(rxactive == 0)
+ timo = 1000;
+ else
+ timo = Timeout;
+ timo += TK2MS(m->ticks);
+ while(timo > TK2MS(m->ticks)){
+ n = etherrxpkt(ctlrno, &pkt, timo-TK2MS(m->ticks));
+ if(n <= 0)
+ continue;
+
+ h = (Udphdr*)&pkt;
+ if(debug)
+ print("udprecv %E to %E...\n", h->s, h->d);
+
+ if(nhgets(h->type) != ET_IP) {
+ if(debug)
+ print("not ip...");
+ continue;
+ }
+
+ if(ip_csum(&h->vihl)) {
+ print("ip chksum error\n");
+ continue;
+ }
+ if(h->vihl != (IP_VER|IP_HLEN)) {
+ print("ip bad vers/hlen\n");
+ continue;
+ }
+
+ if(h->udpproto != IP_UDPPROTO) {
+ if(debug)
+ print("not udp (%d)...", h->udpproto);
+ continue;
+ }
+
+ if(debug)
+ print("okay udp...");
+
+ h->ttl = 0;
+ len = nhgets(h->udplen);
+ hnputs(h->udpplen, len);
+
+ if(nhgets(h->udpcksum)) {
+ csm = ptcl_csum(&h->ttl, len+UDP_PHDRSIZE);
+ if(csm != 0) {
+ print("udp chksum error csum #%4ux len %d\n",
+ csm, n);
+ break;
+ }
+ }
+
+ if(a->port != 0 && nhgets(h->udpsport) != a->port) {
+ if(debug)
+ print("udpport %ux %ux\n",
+ nhgets(h->udpsport), a->port);
+ continue;
+ }
+
+ addr = nhgetl(h->udpsrc);
+ if(a->ip != Bcastip && addr != a->ip) {
+ if(debug)
+ print("bad ip\n");
+ continue;
+ }
+
+ len -= UDP_HDRSIZE-UDP_PHDRSIZE;
+ if(len > dlen) {
+ print("udp: packet too big: %d > %d; from addr %E\n",
+ len, dlen, h->udpsrc);
+ continue;
+ }
+
+ memmove(data, h->udpcksum+sizeof(h->udpcksum), len);
+ a->ip = addr;
+ a->port = nhgets(h->udpsport);
+ memmove(a->ea, pkt.s, sizeof(a->ea));
+
+ rxactive = 1;
+ return len;
+ }
+
+ return 0;
+}
+
+static int tftpblockno;
+
+/*
+ * format of a request packet, from the RFC:
+ *
+ 2 bytes string 1 byte string 1 byte
+ ------------------------------------------------
+ | Opcode | Filename | 0 | Mode | 0 |
+ ------------------------------------------------
+ */
+static int
+tftpopen(int ctlrno, Netaddr *a, char *name, Tftp *tftp)
+{
+ int i, len, rlen, oport;
+ char buf[Segsize+2];
+
+ buf[0] = 0;
+ buf[1] = Tftp_READ;
+ len = 2 + sprint(buf+2, "%s", name) + 1;
+ len += sprint(buf+len, "octet") + 1;
+
+ oport = a->port;
+ for(i = 0; i < 5; i++){
+ a->port = oport;
+ udpsend(ctlrno, a, buf, len);
+ a->port = 0;
+ if((rlen = udprecv(ctlrno, a, tftp, sizeof(Tftp))) < sizeof(tftp->header))
+ continue;
+
+ switch((tftp->header[0]<<8)|tftp->header[1]){
+
+ case Tftp_ERROR:
+ print("tftpopen: error (%d): %s\n",
+ (tftp->header[2]<<8)|tftp->header[3], (char*)tftp->data);
+ return -1;
+
+ case Tftp_DATA:
+ tftpblockno = 1;
+ len = (tftp->header[2]<<8)|tftp->header[3];
+ if(len != tftpblockno){
+ print("tftpopen: block error: %d\n", len);
+ nak(ctlrno, a, 1, "block error", 0);
+ return -1;
+ }
+ rlen -= sizeof(tftp->header);
+ if(rlen < Segsize){
+ /* ACK now, in case we don't later */
+ buf[0] = 0;
+ buf[1] = Tftp_ACK;
+ buf[2] = tftpblockno>>8;
+ buf[3] = tftpblockno;
+ udpsend(ctlrno, a, buf, sizeof(tftp->header));
+ }
+ return rlen;
+ }
+ }
+
+ print("tftpopen: failed to connect to server\n");
+ return -1;
+}
+
+static int
+tftpread(int ctlrno, Netaddr *a, Tftp *tftp, int dlen)
+{
+ uchar buf[4];
+ int try, blockno, len;
+
+ dlen += sizeof(tftp->header);
+
+ for(try = 0; try < 10; try++) {
+ buf[0] = 0;
+ buf[1] = Tftp_ACK;
+ buf[2] = tftpblockno>>8;
+ buf[3] = tftpblockno;
+
+ udpsend(ctlrno, a, buf, sizeof(buf));
+ len = udprecv(ctlrno, a, tftp, dlen);
+ if(len <= sizeof(tftp->header)){
+ if(debug)
+ print("tftpread: too short %d <= %d\n",
+ len, sizeof(tftp->header));
+ continue;
+ }
+ blockno = (tftp->header[2]<<8)|tftp->header[3];
+ if(blockno <= tftpblockno){
+ if(debug)
+ print("tftpread: blkno %d <= %d\n",
+ blockno, tftpblockno);
+ continue;
+ }
+
+ if(blockno == tftpblockno+1) {
+ tftpblockno++;
+ if(len < dlen) { /* last packet; send final ack */
+ tftpblockno++;
+ buf[0] = 0;
+ buf[1] = Tftp_ACK;
+ buf[2] = tftpblockno>>8;
+ buf[3] = tftpblockno;
+ udpsend(ctlrno, a, buf, sizeof(buf));
+ }
+ return len-sizeof(tftp->header);
+ }
+ print("tftpread: block error: %d, expected %d\n",
+ blockno, tftpblockno+1);
+ }
+
+ return -1;
+}
+
+static int
+bootpopen(int ctlrno, char *file, Bootp *rep, int dotftpopen)
+{
+ Bootp req;
+ int i, n;
+ uchar *ea;
+ char name[128], *filename, *sysname;
+
+ if((ea = etheraddr(ctlrno)) == 0){
+ print("invalid ctlrno %d\n", ctlrno);
+ return -1;
+ }
+
+ filename = 0;
+ sysname = 0;
+ if(file && *file){
+ strcpy(name, file);
+ if(filename = strchr(name, '!')){
+ sysname = name;
+ *filename++ = 0;
+ }
+ else
+ filename = name;
+ }
+
+ memset(&req, 0, sizeof(req));
+ req.op = Bootrequest;
+ req.htype = 1; /* ethernet */
+ req.hlen = Eaddrlen; /* ethernet */
+ memmove(req.chaddr, ea, Eaddrlen);
+ if(filename != nil)
+ strncpy(req.file, filename, sizeof(req.file));
+ if(sysname != nil)
+ strncpy(req.sname, sysname, sizeof(req.sname));
+
+ myaddr.ip = 0;
+ myaddr.port = BPportsrc;
+ memmove(myaddr.ea, ea, Eaddrlen);
+
+ etherrxflush(ctlrno);
+ for(i = 0; i < 10; i++) {
+ server.ip = Bcastip;
+ server.port = BPportdst;
+ memmove(server.ea, broadcast, sizeof(server.ea));
+ udpsend(ctlrno, &server, &req, sizeof(req));
+ if(udprecv(ctlrno, &server, rep, sizeof(*rep)) <= 0)
+ continue;
+ if(memcmp(req.chaddr, rep->chaddr, Eaddrlen))
+ continue;
+ if(rep->htype != 1 || rep->hlen != Eaddrlen)
+ continue;
+ if(sysname == 0 || strcmp(sysname, rep->sname) == 0)
+ break;
+ }
+ if(i >= 10) {
+ print("bootp timed out\n");
+ return -1;
+ }
+
+ if(!dotftpopen)
+ return 0;
+
+ if(filename == 0 || *filename == 0){
+ if(strcmp(rep->file, "/386/9pxeload") == 0)
+ return -1;
+ filename = rep->file;
+ }
+
+ if(rep->sname[0] != '\0')
+ print("%s ", rep->sname);
+ print("(%d.%d.%d.%d!%d): %s\n",
+ rep->siaddr[0],
+ rep->siaddr[1],
+ rep->siaddr[2],
+ rep->siaddr[3],
+ server.port,
+ filename);
+
+ myaddr.ip = nhgetl(rep->yiaddr);
+ myaddr.port = tftpport++;
+ server.ip = nhgetl(rep->siaddr);
+ server.port = TFTPport;
+
+ if((n = tftpopen(ctlrno, &server, filename, &tftpb)) < 0)
+ return -1;
+
+ return n;
+}
+
+int
+bootpboot(int ctlrno, char *file, Boot *b)
+{
+ int n;
+ Bootp rep;
+
+ if((n = bootpopen(ctlrno, file, &rep, 1)) < 0)
+ return -1;
+
+ while(bootpass(b, tftpb.data, n) == MORE){
+ n = tftpread(ctlrno, &server, &tftpb, sizeof(tftpb.data));
+ if(n < sizeof(tftpb.data))
+ break;
+ }
+
+ if(0 < n && n < sizeof(tftpb.data)) /* got to end of file */
+ bootpass(b, tftpb.data, n);
+ else
+ nak(ctlrno, &server, 3, "ok", 0); /* tftpclose to abort transfer */
+ bootpass(b, nil, 0); /* boot if possible */
+ return -1;
+}
+
+#include "fs.h"
+
+#define INIPATHLEN 64
+
+static struct {
+ Fs fs;
+ char ini[INIPATHLEN];
+} pxether[MaxEther];
+
+static vlong
+pxediskseek(Fs*, vlong)
+{
+ return -1LL;
+}
+
+static long
+pxediskread(Fs*, void*, long)
+{
+ return -1;
+}
+
+static long
+pxeread(File* f, void* va, long len)
+{
+ int n;
+ Bootp rep;
+ char *p, *v;
+
+ if((n = bootpopen(f->fs->dev, pxether[f->fs->dev].ini, &rep, 1)) < 0)
+ return -1;
+
+ p = v = va;
+ while(n > 0) {
+ if((p-v)+n > len)
+ n = len - (p-v);
+ memmove(p, tftpb.data, n);
+ p += n;
+ if(n != Segsize)
+ break;
+ if((n = tftpread(f->fs->dev, &server, &tftpb, sizeof(tftpb.data))) < 0)
+ return -1;
+ }
+ return p-v;
+}
+
+static int
+pxewalk(File* f, char* name)
+{
+ Bootp rep;
+ char *ini;
+
+ switch(f->walked){
+ default:
+ return -1;
+ case 0:
+ if(strcmp(name, "cfg") == 0){
+ f->walked = 1;
+ return 1;
+ }
+ break;
+ case 1:
+ if(strcmp(name, "pxe") == 0){
+ f->walked = 2;
+ return 1;
+ }
+ break;
+ case 2:
+ if(strcmp(name, "%E") != 0)
+ break;
+ f->walked = 3;
+
+ if(bootpopen(f->fs->dev, nil, &rep, 0) < 0)
+ return 0;
+
+ ini = pxether[f->fs->dev].ini;
+ snprint(ini, INIPATHLEN, "/cfg/pxe/%E", rep.chaddr);
+ f->path = ini;
+
+ return 1;
+ }
+ return 0;
+}
+
+void*
+pxegetfspart(int ctlrno, char* part, int)
+{
+ if(!pxe)
+ return nil;
+ if(strcmp(part, "*") != 0)
+ return nil;
+ if(ctlrno >= MaxEther)
+ return nil;
+
+ pxether[ctlrno].fs.dev = ctlrno;
+ pxether[ctlrno].fs.diskread = pxediskread;
+ pxether[ctlrno].fs.diskseek = pxediskseek;
+
+ pxether[ctlrno].fs.read = pxeread;
+ pxether[ctlrno].fs.walk = pxewalk;
+
+ pxether[ctlrno].fs.root.fs = &pxether[ctlrno].fs;
+ pxether[ctlrno].fs.root.walked = 0;
+
+ return &pxether[ctlrno].fs;
+}
diff --git a/os/boot/pc/cga.c b/os/boot/pc/cga.c
new file mode 100644
index 00000000..90e08aaf
--- /dev/null
+++ b/os/boot/pc/cga.c
@@ -0,0 +1,91 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+enum {
+ Width = 160,
+ Height = 25,
+
+ Attr = 7, /* white on black */
+};
+
+#define CGASCREENBASE ((uchar*)KADDR(0xB8000))
+
+static int pos;
+static int screeninitdone;
+
+static uchar
+cgaregr(int index)
+{
+ outb(0x3D4, index);
+ return inb(0x3D4+1) & 0xFF;
+}
+
+static void
+cgaregw(int index, int data)
+{
+ outb(0x3D4, index);
+ outb(0x3D4+1, data);
+}
+
+static void
+movecursor(void)
+{
+ cgaregw(0x0E, (pos/2>>8) & 0xFF);
+ cgaregw(0x0F, pos/2 & 0xFF);
+ CGASCREENBASE[pos+1] = Attr;
+}
+
+static void
+cgascreenputc(int c)
+{
+ int i;
+
+ if(c == '\n'){
+ pos = pos/Width;
+ pos = (pos+1)*Width;
+ }
+ else if(c == '\t'){
+ i = 8 - ((pos/2)&7);
+ while(i-->0)
+ cgascreenputc(' ');
+ }
+ else if(c == '\b'){
+ if(pos >= 2)
+ pos -= 2;
+ cgascreenputc(' ');
+ pos -= 2;
+ }
+ else{
+ CGASCREENBASE[pos++] = c;
+ CGASCREENBASE[pos++] = Attr;
+ }
+ if(pos >= Width*Height){
+ memmove(CGASCREENBASE, &CGASCREENBASE[Width], Width*(Height-1));
+ memset(&CGASCREENBASE[Width*(Height-1)], 0, Width);
+ pos = Width*(Height-1);
+ }
+ movecursor();
+}
+
+static void
+screeninit(void)
+{
+ if(screeninitdone == 0){
+ pos = cgaregr(0x0E)<<8;
+ pos |= cgaregr(0x0F);
+ pos *= 2;
+ screeninitdone = 1;
+ }
+}
+
+void
+cgascreenputs(char* s, int n)
+{
+ if(screeninitdone == 0)
+ screeninit();
+ while(n-- > 0)
+ cgascreenputc(*s++);
+}
diff --git a/os/boot/pc/clock.c b/os/boot/pc/clock.c
new file mode 100644
index 00000000..6c5694cd
--- /dev/null
+++ b/os/boot/pc/clock.c
@@ -0,0 +1,305 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+
+/*
+ * 8253 timer
+ */
+enum
+{
+ T0cntr= 0x40, /* counter ports */
+ T1cntr= 0x41, /* ... */
+ T2cntr= 0x42, /* ... */
+ Tmode= 0x43, /* mode port */
+
+ /* commands */
+ Latch0= 0x00, /* latch counter 0's value */
+ Load0= 0x30, /* load counter 0 with 2 bytes */
+
+ /* modes */
+ Square= 0x36, /* perioic square wave */
+
+ Freq= 1193182, /* Real clock frequency */
+};
+
+static uvlong cpuhz = 66000000;
+static int cpumhz = 66;
+static int loopconst = 100;
+int cpuidax, cpuiddx;
+int havetsc;
+
+extern void _cycles(uvlong*); /* in l.s */
+extern void wrmsr(int, vlong);
+
+static void
+clockintr(Ureg*, void*)
+{
+ m->ticks++;
+ checkalarms();
+}
+
+#define STEPPING(x) ((x)&0xf)
+#define X86MODEL(x) (((x)>>4)&0xf)
+#define X86FAMILY(x) (((x)>>8)&0xf)
+
+enum
+{
+ /* flags */
+ CpuidFPU = 0x001, /* on-chip floating point unit */
+ CpuidMCE = 0x080, /* machine check exception */
+ CpuidCX8 = 0x100, /* CMPXCHG8B instruction */
+};
+
+typedef struct
+{
+ int family;
+ int model;
+ int aalcycles;
+ char *name;
+} X86type;
+
+X86type x86intel[] =
+{
+ { 4, 0, 22, "486DX", }, /* known chips */
+ { 4, 1, 22, "486DX50", },
+ { 4, 2, 22, "486SX", },
+ { 4, 3, 22, "486DX2", },
+ { 4, 4, 22, "486SL", },
+ { 4, 5, 22, "486SX2", },
+ { 4, 7, 22, "DX2WB", }, /* P24D */
+ { 4, 8, 22, "DX4", }, /* P24C */
+ { 4, 9, 22, "DX4WB", }, /* P24CT */
+ { 5, 0, 23, "P5", },
+ { 5, 1, 23, "P5", },
+ { 5, 2, 23, "P54C", },
+ { 5, 3, 23, "P24T", },
+ { 5, 4, 23, "P55C MMX", },
+ { 5, 7, 23, "P54C VRT", },
+ { 6, 1, 16, "PentiumPro", },/* trial and error */
+ { 6, 3, 16, "PentiumII", },
+ { 6, 5, 16, "PentiumII/Xeon", },
+ { 6, 6, 16, "Celeron", },
+ { 6, 7, 16, "PentiumIII/Xeon", },
+ { 6, 8, 16, "PentiumIII/Xeon", },
+ { 6, 0xB, 16, "PentiumIII/Xeon", },
+ { 0xF, 1, 16, "P4", }, /* P4 */
+ { 0xF, 2, 16, "PentiumIV/Xeon", },
+
+ { 3, -1, 32, "386", }, /* family defaults */
+ { 4, -1, 22, "486", },
+ { 5, -1, 23, "P5", },
+ { 6, -1, 16, "P6", },
+ { 0xF, -1, 16, "P4", }, /* P4 */
+
+ { -1, -1, 16, "unknown", }, /* total default */
+};
+
+
+/*
+ * The AMD processors all implement the CPUID instruction.
+ * The later ones also return the processor name via functions
+ * 0x80000002, 0x80000003 and 0x80000004 in registers AX, BX, CX
+ * and DX:
+ * K5 "AMD-K5(tm) Processor"
+ * K6 "AMD-K6tm w/ multimedia extensions"
+ * K6 3D "AMD-K6(tm) 3D processor"
+ * K6 3D+ ?
+ */
+static X86type x86amd[] =
+{
+ { 5, 0, 23, "AMD-K5", }, /* guesswork */
+ { 5, 1, 23, "AMD-K5", }, /* guesswork */
+ { 5, 2, 23, "AMD-K5", }, /* guesswork */
+ { 5, 3, 23, "AMD-K5", }, /* guesswork */
+ { 5, 6, 11, "AMD-K6", }, /* trial and error */
+ { 5, 7, 11, "AMD-K6", }, /* trial and error */
+ { 5, 8, 11, "AMD-K6-2", }, /* trial and error */
+ { 5, 9, 11, "AMD-K6-III", },/* trial and error */
+
+ { 6, 1, 11, "AMD-Athlon", },/* trial and error */
+ { 6, 2, 11, "AMD-Athlon", },/* trial and error */
+
+ { 4, -1, 22, "Am486", }, /* guesswork */
+ { 5, -1, 23, "AMD-K5/K6", }, /* guesswork */
+ { 6, -1, 11, "AMD-Athlon", },/* guesswork */
+ { 0xF, -1, 11, "AMD64", }, /* guesswork */
+
+ { -1, -1, 11, "unknown", }, /* total default */
+};
+
+static X86type *cputype;
+
+
+void
+delay(int millisecs)
+{
+ millisecs *= loopconst;
+ if(millisecs <= 0)
+ millisecs = 1;
+ aamloop(millisecs);
+}
+
+void
+microdelay(int microsecs)
+{
+ microsecs *= loopconst;
+ microsecs /= 1000;
+ if(microsecs <= 0)
+ microsecs = 1;
+ aamloop(microsecs);
+}
+
+extern void cpuid(char*, int*, int*);
+
+X86type*
+cpuidentify(void)
+{
+ int family, model;
+ X86type *t;
+ char cpuidid[16];
+ int cpuidax, cpuiddx;
+
+ cpuid(cpuidid, &cpuidax, &cpuiddx);
+ if(strncmp(cpuidid, "AuthenticAMD", 12) == 0)
+ t = x86amd;
+ else
+ t = x86intel;
+ family = X86FAMILY(cpuidax);
+ model = X86MODEL(cpuidax);
+ if (0)
+ print("cpuidentify: cpuidax 0x%ux cpuiddx 0x%ux\n",
+ cpuidax, cpuiddx);
+ while(t->name){
+ if((t->family == family && t->model == model)
+ || (t->family == family && t->model == -1)
+ || (t->family == -1))
+ break;
+ t++;
+ }
+ if(t->name == nil)
+ panic("cpuidentify");
+
+ if(cpuiddx & 0x10){
+ havetsc = 1;
+ if(cpuiddx & 0x20)
+ wrmsr(0x10, 0);
+ }
+
+ return t;
+}
+
+void
+clockinit(void)
+{
+ uvlong a, b, cpufreq;
+ int loops, incr, x, y;
+ X86type *t;
+
+ /*
+ * set vector for clock interrupts
+ */
+ setvec(VectorCLOCK, clockintr, 0);
+
+ t = cpuidentify();
+
+ /*
+ * set clock for 1/HZ seconds
+ */
+ outb(Tmode, Load0|Square);
+ outb(T0cntr, (Freq/HZ)); /* low byte */
+ outb(T0cntr, (Freq/HZ)>>8); /* high byte */
+
+ /*
+ * Introduce a little delay to make sure the count is
+ * latched and the timer is counting down; with a fast
+ * enough processor this may not be the case.
+ * The i8254 (which this probably is) has a read-back
+ * command which can be used to make sure the counting
+ * register has been written into the counting element.
+ */
+ x = (Freq/HZ);
+ for(loops = 0; loops < 100000 && x >= (Freq/HZ); loops++){
+ outb(Tmode, Latch0);
+ x = inb(T0cntr);
+ x |= inb(T0cntr)<<8;
+ }
+
+ /* find biggest loop that doesn't wrap */
+ incr = 16000000/(t->aalcycles*HZ*2);
+ x = 2000;
+ for(loops = incr; loops < 64*1024; loops += incr) {
+
+ /*
+ * measure time for the loop
+ *
+ * MOVL loops,CX
+ * aaml1: AAM
+ * LOOP aaml1
+ *
+ * the time for the loop should be independent of external
+ * cache and memory system since it fits in the execution
+ * prefetch buffer.
+ *
+ */
+ outb(Tmode, Latch0);
+ if(havetsc)
+ _cycles(&a);
+ x = inb(T0cntr);
+ x |= inb(T0cntr)<<8;
+ aamloop(loops);
+ outb(Tmode, Latch0);
+ if(havetsc)
+ _cycles(&b);
+ y = inb(T0cntr);
+ y |= inb(T0cntr)<<8;
+ x -= y;
+
+ if(x < 0)
+ x += Freq/HZ;
+
+ if(x > Freq/(3*HZ))
+ break;
+ }
+
+ /*
+ * figure out clock frequency and a loop multiplier for delay().
+ * counter goes at twice the frequency, once per transition,
+ * i.e., twice per square wave
+ */
+ cpufreq = (vlong)loops*((t->aalcycles*2*Freq)/x);
+ loopconst = (cpufreq/1000)/t->aalcycles; /* AAM+LOOP's for 1 ms */
+
+ if(havetsc){
+ /* counter goes up by 2*Freq */
+ b = (b-a)<<1;
+ b *= Freq;
+ b /= x;
+
+ /*
+ * round to the nearest megahz
+ */
+ cpumhz = (b+500000)/1000000L;
+ cpuhz = b;
+ }
+ else{
+ /*
+ * add in possible .5% error and convert to MHz
+ */
+ cpumhz = (cpufreq + cpufreq/200)/1000000;
+ cpuhz = cpufreq;
+ }
+
+ if(debug){
+ int timeo;
+
+ print("%dMHz %s loop %d\n", cpumhz, t->name, loopconst);
+ print("tick...");
+ for(timeo = 0; timeo < 10; timeo++)
+ delay(1000);
+ print("tock...\n");
+ }
+}
diff --git a/os/boot/pc/conf.c b/os/boot/pc/conf.c
new file mode 100644
index 00000000..4109f9bb
--- /dev/null
+++ b/os/boot/pc/conf.c
@@ -0,0 +1,537 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "fs.h"
+
+/*
+ * Where configuration info is left for the loaded programme.
+ * This will turn into a structure as more is done by the boot loader
+ * (e.g. why parse the .ini file twice?).
+ * There are 3584 bytes available at CONFADDR.
+ *
+ * The low-level boot routines in l.s leave data for us at CONFADDR,
+ * which we pick up before reading the plan9.ini file.
+ */
+#define BOOTLINELEN 64
+#define BOOTARGS ((char*)(CONFADDR+BOOTLINELEN))
+#define BOOTARGSLEN (3584-0x200-BOOTLINELEN)
+#define MAXCONF 100
+
+static char *confname[MAXCONF];
+static char *confval[MAXCONF];
+static int nconf;
+
+extern char **ini;
+
+typedef struct {
+ char* name;
+ int start;
+ int end;
+} Mblock;
+
+typedef struct {
+ char* tag;
+ Mblock* mb;
+} Mitem;
+
+static Mblock mblock[MAXCONF];
+static int nmblock;
+static Mitem mitem[MAXCONF];
+static int nmitem;
+static char* mdefault;
+static char mdefaultbuf[10];
+static int mtimeout;
+
+static char*
+comma(char* line, char** residue)
+{
+ char *q, *r;
+
+ if((q = strchr(line, ',')) != nil){
+ *q++ = 0;
+ if(*q == ' ')
+ q++;
+ }
+ *residue = q;
+
+ if((r = strchr(line, ' ')) != nil)
+ *r = 0;
+
+ if(*line == ' ')
+ line++;
+ return line;
+}
+
+static Mblock*
+findblock(char* name, char** residue)
+{
+ int i;
+ char *p;
+
+ p = comma(name, residue);
+ for(i = 0; i < nmblock; i++){
+ if(strcmp(p, mblock[i].name) == 0)
+ return &mblock[i];
+ }
+ return nil;
+}
+
+static Mitem*
+finditem(char* name, char** residue)
+{
+ int i;
+ char *p;
+
+ p = comma(name, residue);
+ for(i = 0; i < nmitem; i++){
+ if(strcmp(p, mitem[i].mb->name) == 0)
+ return &mitem[i];
+ }
+ return nil;
+}
+
+static void
+parsemenu(char* str, char* scratch, int len)
+{
+ Mitem *mi;
+ Mblock *mb, *menu;
+ char buf[20], *p, *q, *line[MAXCONF];
+ int i, inblock, n, show;
+
+ inblock = 0;
+ menu = nil;
+ memmove(scratch, str, len);
+ n = getfields(scratch, line, MAXCONF, '\n');
+ if(n >= MAXCONF)
+ print("warning: possibly too many lines in plan9.ini\n");
+ for(i = 0; i < n; i++){
+ p = line[i];
+ if(inblock && *p == '['){
+ mblock[nmblock].end = i;
+ if(strcmp(mblock[nmblock].name, "menu") == 0)
+ menu = &mblock[nmblock];
+ nmblock++;
+ inblock = 0;
+ }
+ if(*p == '['){
+ if(nmblock == 0 && i != 0){
+ mblock[nmblock].name = "common";
+ mblock[nmblock].start = 0;
+ mblock[nmblock].end = i;
+ nmblock++;
+ }
+ q = strchr(p+1, ']');
+ if(q == nil || *(q+1) != 0){
+ print("malformed menu block header - %s\n", p);
+ return;
+ }
+ *q = 0;
+ mblock[nmblock].name = p+1;
+ mblock[nmblock].start = i+1;
+ inblock = 1;
+ }
+ }
+
+ if(inblock){
+ mblock[nmblock].end = i;
+ nmblock++;
+ }
+ if(menu == nil)
+ return;
+ if(nmblock < 2){
+ print("incomplete menu specification\n");
+ return;
+ }
+
+ for(i = menu->start; i < menu->end; i++){
+ p = line[i];
+ if(cistrncmp(p, "menuitem=", 9) == 0){
+ p += 9;
+ if((mb = findblock(p, &q)) == nil){
+ print("no block for menuitem %s\n", p);
+ return;
+ }
+ if(q != nil)
+ mitem[nmitem].tag = q;
+ else
+ mitem[nmitem].tag = mb->name;
+ mitem[nmitem].mb = mb;
+ nmitem++;
+ }
+ else if(cistrncmp(p, "menudefault=", 12) == 0){
+ p += 12;
+ if((mi = finditem(p, &q)) == nil){
+ print("no item for menudefault %s\n", p);
+ return;
+ }
+ if(q != nil)
+ mtimeout = strtol(q, 0, 0);
+ sprint(mdefaultbuf, "%ld", mi-mitem+1);
+ mdefault = mdefaultbuf;
+ }
+ else if(cistrncmp(p, "menuconsole=", 12) == 0){
+ p += 12;
+ p = comma(p, &q);
+ consinit(p, q);
+ }
+ else{
+ print("invalid line in [menu] block - %s\n", p);
+ return;
+ }
+ }
+
+again:
+ print("\nPlan 9 Startup Menu:\n====================\n");
+ for(i = 0; i < nmitem; i++)
+ print(" %d. %s\n", i+1, mitem[i].tag);
+ for(;;){
+ getstr("Selection", buf, sizeof(buf), mdefault, mtimeout);
+ mtimeout = 0;
+ i = strtol(buf, &p, 0)-1;
+ if(i < 0 || i >= nmitem)
+ goto again;
+ switch(*p){
+ case 'p':
+ case 'P':
+ show = 1;
+ print("\n");
+ break;
+ case 0:
+ show = 0;
+ break;
+ default:
+ continue;
+
+ }
+ mi = &mitem[i];
+
+ p = str;
+ p += sprint(p, "menuitem=%s\n", mi->mb->name);
+ for(i = 0; i < nmblock; i++){
+ mb = &mblock[i];
+ if(mi->mb != mb && cistrcmp(mb->name, "common") != 0)
+ continue;
+ for(n = mb->start; n < mb->end; n++)
+ p += sprint(p, "%s\n", line[n]);
+ }
+
+ if(show){
+ for(q = str; q < p; q += i){
+ if((i = print(q)) <= 0)
+ break;
+ }
+ goto again;
+ }
+ break;
+ }
+ print("\n");
+}
+
+/*
+static void
+msleep(int msec)
+{
+ ulong start;
+
+ for(start = m->ticks; TK2MS(m->ticks - start) < msec; )
+ ;
+}
+*/
+
+void
+readlsconf(void)
+{
+ uchar *p;
+
+ p = (uchar*)CONFADDR;
+ for(;;) {
+ if(strcmp((char*)p, "APM") == 0){
+ apm.haveinfo = 1;
+ apm.ax = *(ushort*)(p+4);
+ apm.cx = *(ushort*)(p+6);
+ apm.dx = *(ushort*)(p+8);
+ apm.di = *(ushort*)(p+10);
+ apm.ebx = *(ulong*)(p+12);
+ apm.esi = *(ulong*)(p+16);
+ print("apm ax=%x cx=%x dx=%x di=%x ebx=%x esi=%x\n",
+ apm.ax, apm.cx, apm.dx, apm.di, apm.ebx, apm.esi);
+ p += 20;
+ continue;
+ }
+ break;
+ }
+}
+
+char*
+getconf(char *name)
+{
+ int i, n, nmatch;
+ char buf[20];
+
+ nmatch = 0;
+ for(i = 0; i < nconf; i++)
+ if(cistrcmp(confname[i], name) == 0)
+ nmatch++;
+
+ switch(nmatch) {
+ default:
+ print("\n");
+ nmatch = 0;
+ for(i = 0; i < nconf; i++)
+ if(cistrcmp(confname[i], name) == 0)
+ print("%d. %s\n", ++nmatch, confval[i]);
+ print("%d. none of the above\n", ++nmatch);
+ do {
+ getstr(name, buf, sizeof(buf), nil, 0);
+ n = atoi(buf);
+ } while(n < 1 || n > nmatch);
+
+ for(i = 0; i < nconf; i++)
+ if(cistrcmp(confname[i], name) == 0)
+ if(--n == 0)
+ return confval[i];
+ break;
+
+ case 1:
+ for(i = 0; i < nconf; i++)
+ if(cistrcmp(confname[i], name) == 0)
+ return confval[i];
+ break;
+
+ case 0:
+ break;
+ }
+ return nil;
+}
+
+void
+addconf(char *fmt, ...)
+{
+ va_list arg;
+
+ va_start(arg, fmt);
+ vseprint(BOOTARGS+strlen(BOOTARGS), BOOTARGS+BOOTARGSLEN, fmt, arg);
+ va_end(arg);
+}
+
+void
+changeconf(char *fmt, ...)
+{
+ va_list arg;
+ char *p, *q, pref[20], buf[128];
+
+ va_start(arg, fmt);
+ vseprint(buf, buf+sizeof buf, fmt, arg);
+ va_end(arg);
+ strncpy(pref+1, buf, 19);
+ pref[19] = '\0';
+ if(p = strchr(pref, '='))
+ *(p+1) = '\0';
+ else
+ print("warning: did not change %s in plan9.ini\n", buf);
+
+ /* find old line by looking for \nwhat= */
+ pref[0] = '\n';
+ if(strncmp(BOOTARGS, pref+1, strlen(pref+1)) == 0)
+ p = BOOTARGS;
+ else if(p = strstr(BOOTARGS, pref))
+ p++;
+ else
+ p = nil;
+
+ /* move rest of args up, deleting what= line. */
+ if(p != nil && (q = strchr(p, '\n')) != nil)
+ memmove(p, q+1, strlen(q+1)+1);
+
+ /* add replacement to end */
+ addconf("%s", buf);
+}
+
+/*
+ * read configuration file
+ */
+static char inibuf[BOOTARGSLEN];
+static char id[8] = "ZORT 0\r\n";
+
+int
+dotini(Fs *fs)
+{
+ File rc;
+ int blankline, i, incomment, inspace, n;
+ char *cp, *p, *q, *line[MAXCONF];
+
+ if(fswalk(fs, *ini, &rc) <= 0)
+ return -1;
+
+ cp = inibuf;
+ *cp = 0;
+ n = fsread(&rc, cp, BOOTARGSLEN-1);
+ if(n <= 0)
+ return -1;
+
+ cp[n] = 0;
+
+ /*
+ * Strip out '\r', change '\t' -> ' '.
+ * Change runs of spaces into single spaces.
+ * Strip out trailing spaces, blank lines.
+ *
+ * We do this before we make the copy so that if we
+ * need to change the copy, it is already fairly clean.
+ * The main need is in the case when plan9.ini has been
+ * padded with lots of trailing spaces, as is the case
+ * for those created during a distribution install.
+ */
+ p = cp;
+ blankline = 1;
+ incomment = inspace = 0;
+ for(q = cp; *q; q++){
+ if(*q == '\r')
+ continue;
+ if(*q == '\t')
+ *q = ' ';
+ if(*q == ' '){
+ inspace = 1;
+ continue;
+ }
+ if(*q == '\n'){
+ if(!blankline){
+ if(!incomment)
+ *p++ = '\n';
+ blankline = 1;
+ }
+ incomment = inspace = 0;
+ continue;
+ }
+ if(inspace){
+ if(!blankline && !incomment)
+ *p++ = ' ';
+ inspace = 0;
+ }
+ if(blankline && *q == '#')
+ incomment = 1;
+ blankline = 0;
+ if(!incomment)
+ *p++ = *q;
+ }
+ if(p > cp && p[-1] != '\n')
+ *p++ = '\n';
+ *p++ = 0;
+ n = p-cp;
+
+ parsemenu(cp, BOOTARGS, n);
+
+ /*
+ * Keep a copy.
+ * We could change this to pass the parsed strings
+ * to the booted programme instead of the raw
+ * string, then it only gets done once.
+ */
+ if(strncmp(cp, id, sizeof(id))){
+ memmove(BOOTARGS, id, sizeof(id));
+ if(n+1+sizeof(id) >= BOOTARGSLEN)
+ n -= sizeof(id);
+ memmove(BOOTARGS+sizeof(id), cp, n+1);
+ }
+ else
+ memmove(BOOTARGS, cp, n+1);
+
+ n = getfields(cp, line, MAXCONF, '\n');
+ for(i = 0; i < n; i++){
+ cp = strchr(line[i], '=');
+ if(cp == 0)
+ continue;
+ *cp++ = 0;
+ if(cp - line[i] >= NAMELEN+1)
+ *(line[i]+NAMELEN-1) = 0;
+ confname[nconf] = line[i];
+ confval[nconf] = cp;
+ nconf++;
+ }
+ return 0;
+}
+
+static int
+parseether(uchar *to, char *from)
+{
+ char nip[4];
+ char *p;
+ int i;
+
+ p = from;
+ while(*p == ' ')
+ ++p;
+ for(i = 0; i < 6; i++){
+ if(*p == 0)
+ return -1;
+ nip[0] = *p++;
+ if(*p == 0)
+ return -1;
+ nip[1] = *p++;
+ nip[2] = 0;
+ to[i] = strtoul(nip, 0, 16);
+ if(*p == ':')
+ p++;
+ }
+ return 0;
+}
+
+int
+isaconfig(char *class, int ctlrno, ISAConf *isa)
+{
+ char cc[NAMELEN], *p, *q, *r;
+ int n;
+
+ sprint(cc, "%s%d", class, ctlrno);
+ for(n = 0; n < nconf; n++){
+ if(cistrncmp(confname[n], cc, NAMELEN))
+ continue;
+ isa->nopt = 0;
+ p = confval[n];
+ while(*p){
+ while(*p == ' ' || *p == '\t')
+ p++;
+ if(*p == '\0')
+ break;
+ if(cistrncmp(p, "type=", 5) == 0){
+ p += 5;
+ for(q = isa->type; q < &isa->type[NAMELEN-1]; q++){
+ if(*p == '\0' || *p == ' ' || *p == '\t')
+ break;
+ *q = *p++;
+ }
+ *q = '\0';
+ }
+ else if(cistrncmp(p, "port=", 5) == 0)
+ isa->port = strtoul(p+5, &p, 0);
+ else if(cistrncmp(p, "irq=", 4) == 0)
+ isa->irq = strtoul(p+4, &p, 0);
+ else if(cistrncmp(p, "mem=", 4) == 0)
+ isa->mem = strtoul(p+4, &p, 0);
+ else if(cistrncmp(p, "size=", 5) == 0)
+ isa->size = strtoul(p+5, &p, 0);
+ else if(cistrncmp(p, "ea=", 3) == 0){
+ if(parseether(isa->ea, p+3) == -1)
+ memset(isa->ea, 0, 6);
+ }
+ else if(isa->nopt < NISAOPT){
+ r = isa->opt[isa->nopt];
+ while(*p && *p != ' ' && *p != '\t'){
+ *r++ = *p++;
+ if(r-isa->opt[isa->nopt] >= ISAOPTLEN-1)
+ break;
+ }
+ *r = '\0';
+ isa->nopt++;
+ }
+ while(*p && *p != ' ' && *p != '\t')
+ p++;
+ }
+ return 1;
+ }
+ return 0;
+}
diff --git a/os/boot/pc/console.c b/os/boot/pc/console.c
new file mode 100644
index 00000000..9883728c
--- /dev/null
+++ b/os/boot/pc/console.c
@@ -0,0 +1,236 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+IOQ consiq;
+IOQ consoq;
+
+static int useuart;
+
+int debug = 0;
+
+void
+kbdchar(int c)
+{
+ c &= 0x7F;
+ if(c == 0x10)
+ warp86("\n^P\n", 0);
+ if(c == 0x12)
+ debug = !debug;
+ consiq.putc(&consiq, c);
+}
+
+static int
+consputc(void)
+{
+ return consoq.getc(&consoq);
+}
+
+void
+kbdinit(void)
+{
+ i8042init();
+ qinit(&consiq);
+}
+
+void
+consinit(char* name, char* speed)
+{
+ int baud, port;
+
+ if(name == nil || cistrcmp(name, "cga") == 0)
+ return;
+ port = strtoul(name, 0, 0);
+ if(port < 0 || port > 1)
+ return;
+ if(speed == nil || (baud = strtoul(speed, 0, 0)) == 0)
+ baud = 9600;
+
+ qinit(&consoq);
+
+ uartspecial(port, kbdchar, consputc, baud);
+ useuart = 1;
+ uartputs(&consoq, "\n", 1);
+}
+
+void
+consdrain(void)
+{
+ if(useuart)
+ uartdrain();
+}
+
+void
+consputs(char* s, int n)
+{
+ cgascreenputs(s, n);
+ if(useuart)
+ uartputs(&consoq, s, n);
+}
+
+void
+warp86(char* s, ulong)
+{
+ if(s != nil)
+ print(s);
+ spllo();
+ consdrain();
+
+ i8042reset();
+ print("Takes a licking and keeps on ticking...\n");
+ for(;;)
+ idle();
+}
+
+static int
+getline(char *buf, int size, int timeout)
+{
+ int c, i=0;
+ ulong start;
+ char echo;
+
+ for (;;) {
+ start = m->ticks;
+ do{
+ /* timeout seconds to first char */
+ if(timeout && ((m->ticks - start) > timeout*HZ))
+ return -2;
+ c = consiq.getc(&consiq);
+ }while(c == -1);
+ timeout = 0;
+
+ if(c == '\r')
+ c = '\n'; /* turn carriage return into newline */
+ if(c == '\177')
+ c = '\010'; /* turn delete into backspace */
+ if(c == '\025')
+ echo = '\n'; /* echo ^U as a newline */
+ else
+ echo = c;
+ consputs(&echo, 1);
+
+ if(c == '\010'){
+ if(i > 0)
+ i--; /* bs deletes last character */
+ continue;
+ }
+ /* a newline ends a line */
+ if (c == '\n')
+ break;
+ /* ^U wipes out the line */
+ if (c =='\025')
+ return -1;
+ if(i == size)
+ return size;
+ buf[i++] = c;
+ }
+ buf[i] = 0;
+ return i;
+}
+
+int
+getstr(char *prompt, char *buf, int size, char *def, int timeout)
+{
+ int len, isdefault;
+ char pbuf[PRINTSIZE];
+
+ buf[0] = 0;
+ isdefault = (def && *def);
+ if(isdefault == 0){
+ timeout = 0;
+ sprint(pbuf, "%s: ", prompt);
+ }
+ else if(timeout)
+ sprint(pbuf, "%s[default==%s (%ds timeout)]: ", prompt, def, timeout);
+ else
+ sprint(pbuf, "%s[default==%s]: ", prompt, def);
+ for (;;) {
+ print(pbuf);
+ len = getline(buf, size, timeout);
+ switch(len){
+ case 0:
+ /* RETURN */
+ if(isdefault)
+ break;
+ continue;
+ case -1:
+ /* ^U typed */
+ continue;
+ case -2:
+ /* timeout, use default */
+ consputs("\n", 1);
+ len = 0;
+ break;
+ default:
+ break;
+ }
+ if(len >= size){
+ print("line too long\n");
+ continue;
+ }
+ break;
+ }
+ if(len == 0 && isdefault)
+ strcpy(buf, def);
+ return 0;
+}
+
+int
+print(char *fmt, ...)
+{
+ int n;
+ va_list arg;
+ char buf[PRINTSIZE];
+
+ va_start(arg, fmt);
+ n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
+ va_end(arg);
+ consputs(buf, n);
+
+ return n;
+}
+
+int
+sprint(char *s, char *fmt, ...)
+{
+ int n;
+ va_list arg;
+
+ va_start(arg, fmt);
+ n = vseprint(s, s+PRINTSIZE, fmt, arg) - s;
+ va_end(arg);
+
+ return n;
+}
+
+void
+panic(char *fmt, ...)
+{
+ int n;
+ va_list arg;
+ char buf[PRINTSIZE];
+
+ strcpy(buf, "panic: ");
+ va_start(arg, fmt);
+ n = vseprint(buf+7, buf+sizeof(buf), fmt, arg) - buf;
+ va_end(arg);
+ buf[n] = '\n';
+ consputs(buf, n+1);
+
+//floppymemwrite();
+//splhi(); for(;;);
+ if(etherdetach)
+ etherdetach();
+ if(sddetach)
+ sddetach();
+
+ consputs("\nPress almost any key to reset...", 32);
+ spllo();
+ while(consiq.getc(&consiq) == -1)
+ ;
+
+ warp86(nil, 0);
+}
diff --git a/os/boot/pc/dat.h b/os/boot/pc/dat.h
new file mode 100644
index 00000000..f6d02d56
--- /dev/null
+++ b/os/boot/pc/dat.h
@@ -0,0 +1,212 @@
+typedef struct List {
+ void *next;
+} List;
+
+typedef struct Alarm Alarm;
+typedef struct Alarm {
+ List;
+ int busy;
+ long dt;
+ void (*f)(Alarm*);
+ void *arg;
+} Alarm;
+
+typedef struct Apminfo {
+ int haveinfo;
+ int ax;
+ int cx;
+ int dx;
+ int di;
+ int ebx;
+ int esi;
+} Apminfo;
+
+typedef struct Block Block;
+struct Block {
+ Block* next;
+ uchar* rp; /* first unconsumed byte */
+ uchar* wp; /* first empty byte */
+ uchar* lim; /* 1 past the end of the buffer */
+ uchar* base; /* start of the buffer */
+ ulong flag;
+};
+#define BLEN(s) ((s)->wp - (s)->rp)
+
+typedef struct IOQ IOQ;
+typedef struct IOQ {
+ uchar buf[4096];
+ uchar *in;
+ uchar *out;
+ int state;
+ int (*getc)(IOQ*);
+ int (*putc)(IOQ*, int);
+ void *ptr;
+};
+
+enum {
+ Eaddrlen = 6,
+ /* next two exclude 4-byte ether CRC */
+ ETHERMINTU = 60, /* minimum transmit size */
+ ETHERMAXTU = 1514, /* maximum transmit size */
+ ETHERHDRSIZE = 14, /* size of an ethernet header */
+
+ MaxEther = 6,
+};
+
+typedef struct {
+ uchar d[Eaddrlen];
+ uchar s[Eaddrlen];
+ uchar type[2];
+ uchar data[1500];
+ uchar crc[4];
+} Etherpkt;
+
+extern uchar broadcast[Eaddrlen];
+
+typedef struct Ureg Ureg;
+
+typedef struct Segdesc {
+ ulong d0;
+ ulong d1;
+} Segdesc;
+
+typedef struct Mach {
+ ulong ticks; /* of the clock since boot time */
+ void *alarm; /* alarms bound to this clock */
+} Mach;
+
+extern Mach *m;
+
+#define I_MAGIC ((((4*11)+0)*11)+7)
+
+typedef struct Exec Exec;
+struct Exec
+{
+ uchar magic[4]; /* magic number */
+ uchar text[4]; /* size of text segment */
+ uchar data[4]; /* size of initialized data */
+ uchar bss[4]; /* size of uninitialized data */
+ uchar syms[4]; /* size of symbol table */
+ uchar entry[4]; /* entry point */
+ uchar spsz[4]; /* size of sp/pc offset table */
+ uchar pcsz[4]; /* size of pc/line number table */
+};
+
+/*
+ * a parsed .ini line
+ */
+#define ISAOPTLEN 32
+#define NISAOPT 8
+
+typedef struct ISAConf {
+ char type[NAMELEN];
+ ulong port;
+ ulong irq;
+ ulong mem;
+ ulong size;
+ uchar ea[6];
+
+ int nopt;
+ char opt[NISAOPT][ISAOPTLEN];
+} ISAConf;
+
+typedef struct Pcidev Pcidev;
+typedef struct PCMmap PCMmap;
+
+#define BOOTLINE ((char*)CONFADDR)
+
+enum {
+ MB = (1024*1024),
+};
+#define ROUND(s, sz) (((s)+((sz)-1))&~((sz)-1))
+
+
+typedef struct Type Type;
+typedef struct Medium Medium;
+typedef struct Boot Boot;
+
+enum { /* type */
+ Tnil = 0x00,
+
+ Tfloppy = 0x01,
+ Tsd = 0x02,
+ Tether = 0x03,
+ Tcd = 0x04,
+
+ Tany = -1,
+};
+
+enum { /* name and flag */
+ Fnone = 0x00,
+
+ Nfs = 0x00,
+ Ffs = (1<<Nfs),
+ Nboot = 0x01,
+ Fboot = (1<<Nboot),
+ Nbootp = 0x02,
+ Fbootp = (1<<Nbootp),
+ NName = 3,
+
+ Fany = Fbootp|Fboot|Ffs,
+
+ Fini = 0x10,
+ Fprobe = 0x80,
+};
+
+typedef struct Type {
+ int type;
+ int flag;
+ int (*init)(void);
+ void (*initdev)(int, char*);
+ void* (*getfspart)(int, char*, int); /* actually returns Dos* */
+ void (*addconf)(int);
+ int (*boot)(int, char*, Boot*);
+ void (*printdevs)(int);
+ char** parts;
+ char** inis;
+ int mask;
+ Medium* media;
+} Type;
+
+extern void (*etherdetach)(void);
+extern void (*floppydetach)(void);
+extern void (*sddetach)(void);
+
+typedef struct Lock { /* for ilock, iunlock */
+ int locked;
+ int spl;
+} Lock;
+
+enum { /* returned by bootpass */
+ MORE, ENOUGH, FAIL
+};
+enum {
+ INITKERNEL,
+ READEXEC,
+ READ9TEXT,
+ READ9DATA,
+ READGZIP,
+ READEHDR,
+ READPHDR,
+ READEPAD,
+ READEDATA,
+ TRYBOOT,
+ INIT9LOAD,
+ READ9LOAD,
+ FAILED
+};
+
+struct Boot {
+ int state;
+
+ Exec exec;
+ char *bp; /* base ptr */
+ char *wp; /* write ptr */
+ char *ep; /* end ptr */
+};
+
+extern int debug;
+extern Apminfo apm;
+extern char *defaultpartition;
+extern int iniread;
+extern int pxe;
diff --git a/os/boot/pc/devfloppy.c b/os/boot/pc/devfloppy.c
new file mode 100644
index 00000000..06b7477f
--- /dev/null
+++ b/os/boot/pc/devfloppy.c
@@ -0,0 +1,853 @@
+#include <u.h>
+
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+
+#include "fs.h"
+#include "devfloppy.h"
+
+
+/* Intel 82077A (8272A compatible) floppy controller */
+
+/* This module expects the following functions to be defined
+ * elsewhere:
+ *
+ * inb()
+ * outb()
+ * floppyexec()
+ * floppyeject()
+ * floppysetup0()
+ * floppysetup1()
+ * dmainit()
+ * dmasetup()
+ * dmaend()
+ *
+ * On DMA systems, floppyexec() should be an empty function;
+ * on non-DMA systems, dmaend() should be an empty function;
+ * dmasetup() may enforce maximum transfer sizes.
+ */
+
+enum {
+ /* file types */
+ Qdir= 0,
+ Qdata= (1<<2),
+ Qctl= (2<<2),
+ Qmask= (3<<2),
+
+ DMAchan= 2, /* floppy dma channel */
+};
+
+#define DPRINT if(0)print
+
+FType floppytype[] =
+{
+ { "3½HD", T1440kb, 512, 18, 2, 1, 80, 0x1B, 0x54, 0, },
+ { "3½DD", T1440kb, 512, 9, 2, 1, 80, 0x1B, 0x54, 2, },
+ { "3½DD", T720kb, 512, 9, 2, 1, 80, 0x1B, 0x54, 2, },
+ { "5¼HD", T1200kb, 512, 15, 2, 1, 80, 0x2A, 0x50, 0, },
+ { "5¼DD", T1200kb, 512, 9, 2, 2, 40, 0x2A, 0x50, 1, },
+ { "ATT3B1", T1200kb, 512, 8, 2, 2, 48, 0x2A, 0x50, 1, },
+ { "5¼DD", T360kb, 512, 9, 2, 1, 40, 0x2A, 0x50, 2, },
+};
+
+/*
+ * bytes per sector encoding for the controller.
+ * - index for b2c is is (bytes per sector/128).
+ * - index for c2b is code from b2c
+ */
+static int b2c[] =
+{
+[1] 0,
+[2] 1,
+[4] 2,
+[8] 3,
+};
+static int c2b[] =
+{
+ 128,
+ 256,
+ 512,
+ 1024,
+};
+
+FController fl;
+
+#define MOTORBIT(i) (1<<((i)+4))
+
+/*
+ * predeclared
+ */
+static int cmddone(void*);
+static void floppyformat(FDrive*, char*);
+static void floppykproc(void*);
+static void floppypos(FDrive*,long);
+static int floppyrecal(FDrive*);
+static int floppyresult(void);
+static void floppyrevive(void);
+static vlong pcfloppyseek(FDrive*, vlong);
+static int floppysense(void);
+static void floppywait(int);
+static long floppyxfer(FDrive*, int, void*, long, long);
+
+static void
+fldump(void)
+{
+ DPRINT("sra %ux srb %ux dor %ux msr %ux dir %ux\n", inb(Psra), inb(Psrb),
+ inb(Pdor), inb(Pmsr), inb(Pdir));
+}
+
+static void
+floppyalarm(Alarm* a)
+{
+ FDrive *dp;
+
+ for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++){
+ if((fl.motor&MOTORBIT(dp->dev)) && TK2SEC(m->ticks - dp->lasttouched) > 5)
+ floppyoff(dp);
+ }
+
+ alarm(5*1000, floppyalarm, 0);
+ cancel(a);
+}
+
+/*
+ * set floppy drive to its default type
+ */
+static void
+floppysetdef(FDrive *dp)
+{
+ FType *t;
+
+ for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++)
+ if(dp->dt == t->dt){
+ dp->t = t;
+ break;
+ }
+}
+
+static void
+_floppydetach(void)
+{
+ /*
+ * stop the motors
+ */
+ fl.motor = 0;
+ delay(10);
+ outb(Pdor, fl.motor | Fintena | Fena);
+ delay(10);
+}
+
+int
+floppyinit(void)
+{
+ FDrive *dp;
+ FType *t;
+ ulong maxtsize;
+ int mask;
+
+ dmainit(DMAchan);
+
+ floppysetup0(&fl);
+
+ /*
+ * init dependent parameters
+ */
+ maxtsize = 0;
+ for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++){
+ t->cap = t->bytes * t->heads * t->sectors * t->tracks;
+ t->bcode = b2c[t->bytes/128];
+ t->tsize = t->bytes * t->sectors;
+ if(maxtsize < t->tsize)
+ maxtsize = t->tsize;
+ }
+
+ fl.selected = fl.d;
+
+ floppydetach = _floppydetach;
+ floppydetach();
+
+ /*
+ * init drives
+ */
+ mask = 0;
+ for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++){
+ dp->dev = dp - fl.d;
+ if(dp->dt == Tnone)
+ continue;
+ mask |= 1<<dp->dev;
+ floppysetdef(dp);
+ dp->cyl = -1; /* because we don't know */
+ dp->cache = (uchar*)xspanalloc(maxtsize, BY2PG, 64*1024);
+ dp->ccyl = -1;
+ dp->vers = 0;
+ dp->maxtries = 5;
+ }
+
+ /*
+ * first operation will recalibrate
+ */
+ fl.confused = 1;
+
+ floppysetup1(&fl);
+
+ /* to turn the motor off when inactive */
+ alarm(5*1000, floppyalarm, 0);
+
+ return mask;
+}
+
+void
+floppyinitdev(int i, char *name)
+{
+ if(i >= fl.ndrive)
+ panic("floppyinitdev");
+ sprint(name, "fd%d", i);
+}
+
+void
+floppyprintdevs(int i)
+{
+ if(i >= fl.ndrive)
+ panic("floppyprintdevs");
+ print(" fd%d", i);
+}
+
+int
+floppyboot(int dev, char *file, Boot *b)
+{
+ Fs *fs;
+
+ if(strncmp(file, "dos!", 4) == 0)
+ file += 4;
+ else if(strchr(file, '!') || strcmp(file, "")==0) {
+ print("syntax is fd0!file\n");
+ return -1;
+ }
+
+ fs = floppygetfspart(dev, "dos", 1);
+ if(fs == nil)
+ return -1;
+
+ return fsboot(fs, file, b);
+}
+
+void
+floppyprintbootdevs(int dev)
+{
+ print(" fd%d", dev);
+}
+
+/*
+ * check if the floppy has been replaced under foot. cause
+ * an error if it has.
+ *
+ * a seek and a read clears the condition. this was determined
+ * experimentally, there has to be a better way.
+ *
+ * if the read fails, cycle through the possible floppy
+ * density till one works or we've cycled through all
+ * possibilities for this drive.
+ */
+static int
+changed(FDrive *dp)
+{
+ FType *start;
+
+ /*
+ * if floppy has changed or first time through
+ */
+ if((inb(Pdir)&Fchange) || dp->vers == 0){
+ DPRINT("changed\n");
+ fldump();
+ dp->vers++;
+ floppysetdef(dp);
+ dp->maxtries = 3;
+ start = dp->t;
+
+ /* flopppyon fails if there's no drive */
+ dp->confused = 1; /* make floppyon recal */
+ if(floppyon(dp) < 0)
+ return -1;
+
+ pcfloppyseek(dp, dp->t->heads*dp->t->tsize);
+
+ while(floppyxfer(dp, Fread, dp->cache, 0, dp->t->tsize) <= 0){
+
+ /*
+ * if the xfer attempt doesn't clear the changed bit,
+ * there's no floppy in the drive
+ */
+ if(inb(Pdir)&Fchange)
+ return -1;
+
+ while(++dp->t){
+ if(dp->t == &floppytype[nelem(floppytype)])
+ dp->t = floppytype;
+ if(dp->dt == dp->t->dt)
+ break;
+ }
+
+ /* flopppyon fails if there's no drive */
+ if(floppyon(dp) < 0)
+ return -1;
+
+ DPRINT("changed: trying %s\n", dp->t->name);
+ fldump();
+ if(dp->t == start)
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int
+readtrack(FDrive *dp, int cyl, int head)
+{
+ int i, nn, sofar;
+ ulong pos;
+
+ nn = dp->t->tsize;
+ if(dp->ccyl==cyl && dp->chead==head)
+ return nn;
+ pos = (cyl*dp->t->heads+head) * nn;
+ for(sofar = 0; sofar < nn; sofar += i){
+ dp->ccyl = -1;
+ i = floppyxfer(dp, Fread, dp->cache + sofar, pos + sofar, nn - sofar);
+ if(i <= 0)
+ return -1;
+ }
+ dp->ccyl = cyl;
+ dp->chead = head;
+ return nn;
+}
+
+long
+floppyread(Fs *fs, void *a, long n)
+{
+ FDrive *dp;
+ long rv, offset;
+ int sec, head, cyl;
+ long len;
+ uchar *aa;
+
+ aa = a;
+ dp = &fl.d[fs->dev];
+
+ offset = dp->offset;
+ floppyon(dp);
+ if(changed(dp))
+ return -1;
+
+ for(rv = 0; rv < n; rv += len){
+ /*
+ * all xfers come out of the track cache
+ */
+ dp->len = n - rv;
+ floppypos(dp, offset+rv);
+ cyl = dp->tcyl;
+ head = dp->thead;
+ len = dp->len;
+ sec = dp->tsec;
+ if(readtrack(dp, cyl, head) < 0)
+ break;
+ memmove(aa+rv, dp->cache + (sec-1)*dp->t->bytes, len);
+ }
+ dp->offset = offset+rv;
+
+ return rv;
+}
+
+void*
+floppygetfspart(int i, char *name, int chatty)
+{
+ static Fs fs;
+
+ if(strcmp(name, "dos") != 0){
+ if(chatty)
+ print("unknown partition fd%d!%s (use fd%d!dos)\n", i, name, i);
+ return nil;
+ }
+
+ fs.dev = i;
+ fs.diskread = floppyread;
+ fs.diskseek = floppyseek;
+
+ /* sometimes we get spurious errors and doing it again works */
+ if(dosinit(&fs) < 0 && dosinit(&fs) < 0){
+ if(chatty)
+ print("fd%d!%s does not contain a FAT file system\n", i, name);
+ return nil;
+ }
+ return &fs;
+}
+
+static int
+return0(void*)
+{
+ return 0;
+}
+
+static void
+timedsleep(int (*f)(void*), void* arg, int ms)
+{
+ int s;
+ ulong end;
+
+ end = m->ticks + 1 + MS2TK(ms);
+ while(m->ticks < end && !(*f)(arg)){
+ s = spllo();
+ delay(10);
+ splx(s);
+ }
+}
+
+/*
+ * start a floppy drive's motor.
+ */
+static int
+floppyon(FDrive *dp)
+{
+ int alreadyon;
+ int tries;
+
+ if(fl.confused)
+ floppyrevive();
+
+ /* start motor and select drive */
+ dp->lasttouched = m->ticks;
+ alreadyon = fl.motor & MOTORBIT(dp->dev);
+ if(!alreadyon){
+ fl.motor |= MOTORBIT(dp->dev);
+ outb(Pdor, fl.motor | Fintena | Fena | dp->dev);
+ /* wait for drive to spin up */
+ timedsleep(return0, 0, 750);
+
+ /* clear any pending interrupts */
+ floppysense();
+ }
+
+ /* set transfer rate */
+ if(fl.rate != dp->t->rate){
+ fl.rate = dp->t->rate;
+ outb(Pdsr, fl.rate);
+ }
+
+ /* get drive to a known cylinder */
+ if(dp->confused)
+ for(tries = 0; tries < 4; tries++)
+ if(floppyrecal(dp) >= 0)
+ break;
+ dp->lasttouched = m->ticks;
+ fl.selected = dp;
+ if(dp->confused)
+ return -1;
+ return 0;
+}
+
+/*
+ * stop the floppy if it hasn't been used in 5 seconds
+ */
+static void
+floppyoff(FDrive *dp)
+{
+ fl.motor &= ~MOTORBIT(dp->dev);
+ outb(Pdor, fl.motor | Fintena | Fena | dp->dev);
+}
+
+/*
+ * send a command to the floppy
+ */
+static int
+floppycmd(void)
+{
+ int i;
+ int tries;
+
+ fl.nstat = 0;
+ for(i = 0; i < fl.ncmd; i++){
+ for(tries = 0; ; tries++){
+ if((inb(Pmsr)&(Ffrom|Fready)) == Fready)
+ break;
+ if(tries > 1000){
+ DPRINT("cmd %ux can't be sent (%d)\n", fl.cmd[0], i);
+ fldump();
+
+ /* empty fifo, might have been a bad command */
+ floppyresult();
+ return -1;
+ }
+ microdelay(1);
+ }
+ outb(Pfdata, fl.cmd[i]);
+ }
+ return 0;
+}
+
+/*
+ * get a command result from the floppy
+ *
+ * when the controller goes ready waiting for a command
+ * (instead of sending results), we're done
+ *
+ */
+static int
+floppyresult(void)
+{
+ int i, s;
+ int tries;
+
+ /* get the result of the operation */
+ for(i = 0; i < sizeof(fl.stat); i++){
+ /* wait for status byte */
+ for(tries = 0; ; tries++){
+ s = inb(Pmsr)&(Ffrom|Fready);
+ if(s == Fready){
+ fl.nstat = i;
+ return fl.nstat;
+ }
+ if(s == (Ffrom|Fready))
+ break;
+ if(tries > 1000){
+ DPRINT("floppyresult: %d stats\n", i);
+ fldump();
+ fl.confused = 1;
+ return -1;
+ }
+ microdelay(1);
+ }
+ fl.stat[i] = inb(Pfdata);
+ }
+ fl.nstat = sizeof(fl.stat);
+ return fl.nstat;
+}
+
+/*
+ * calculate physical address of a logical byte offset into the disk
+ *
+ * truncate dp->length if it crosses a track boundary
+ */
+static void
+floppypos(FDrive *dp, long off)
+{
+ int lsec;
+ int ltrack;
+ int end;
+
+ lsec = off/dp->t->bytes;
+ ltrack = lsec/dp->t->sectors;
+ dp->tcyl = ltrack/dp->t->heads;
+ dp->tsec = (lsec % dp->t->sectors) + 1;
+ dp->thead = (lsec/dp->t->sectors) % dp->t->heads;
+
+ /*
+ * can't read across track boundaries.
+ * if so, decrement the bytes to be read.
+ */
+ end = (ltrack+1)*dp->t->sectors*dp->t->bytes;
+ if(off+dp->len > end)
+ dp->len = end - off;
+}
+
+/*
+ * get the interrupt cause from the floppy.
+ */
+static int
+floppysense(void)
+{
+ fl.ncmd = 0;
+ fl.cmd[fl.ncmd++] = Fsense;
+ if(floppycmd() < 0)
+ return -1;
+ if(floppyresult() < 2){
+ DPRINT("can't read sense response\n");
+ fldump();
+ fl.confused = 1;
+ return -1;
+ }
+ return 0;
+}
+
+static int
+cmddone(void *a)
+{
+ USED(a);
+ return fl.ncmd == 0;
+}
+
+/*
+ * Wait for a floppy interrupt. If none occurs in 5 seconds, we
+ * may have missed one. This only happens on some portables which
+ * do power management behind our backs. Call the interrupt
+ * routine to try to clear any conditions.
+ */
+static void
+floppywait(int slow)
+{
+ timedsleep(cmddone, 0, slow ? 5000 : 1000);
+ if(!cmddone(0)){
+ floppyintr(0);
+ fl.confused = 1;
+ }
+}
+
+/*
+ * we've lost the floppy position, go to cylinder 0.
+ */
+static int
+floppyrecal(FDrive *dp)
+{
+ dp->ccyl = -1;
+ dp->cyl = -1;
+
+ fl.ncmd = 0;
+ fl.cmd[fl.ncmd++] = Frecal;
+ fl.cmd[fl.ncmd++] = dp->dev;
+ if(floppycmd() < 0)
+ return -1;
+ floppywait(1);
+ if(fl.nstat < 2){
+ DPRINT("recalibrate: confused %ux\n", inb(Pmsr));
+ fl.confused = 1;
+ return -1;
+ }
+ if((fl.stat[0] & (Codemask|Seekend)) != Seekend){
+ DPRINT("recalibrate: failed\n");
+ dp->confused = 1;
+ return -1;
+ }
+ dp->cyl = fl.stat[1];
+ if(dp->cyl != 0){
+ DPRINT("recalibrate: wrong cylinder %d\n", dp->cyl);
+ dp->cyl = -1;
+ dp->confused = 1;
+ return -1;
+ }
+
+ dp->confused = 0;
+ return 0;
+}
+
+/*
+ * if the controller or a specific drive is in a confused state,
+ * reset it and get back to a kown state
+ */
+static void
+floppyrevive(void)
+{
+ FDrive *dp;
+
+ /*
+ * reset the controller if it's confused
+ */
+ if(fl.confused){
+ DPRINT("floppyrevive in\n");
+ fldump();
+
+ /* reset controller and turn all motors off */
+ splhi();
+ fl.ncmd = 1;
+ fl.cmd[0] = 0;
+ outb(Pdor, 0);
+ delay(10);
+ outb(Pdor, Fintena|Fena);
+ delay(10);
+ spllo();
+ fl.motor = 0;
+ fl.confused = 0;
+ floppywait(0);
+
+ /* mark all drives in an unknown state */
+ for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++)
+ dp->confused = 1;
+
+ /* set rate to a known value */
+ outb(Pdsr, 0);
+ fl.rate = 0;
+
+ DPRINT("floppyrevive out\n");
+ fldump();
+ }
+}
+
+/*
+ * seek to the target cylinder
+ *
+ * interrupt, no results
+ */
+static vlong
+pcfloppyseek(FDrive *dp, vlong off)
+{
+ floppypos(dp, off);
+ if(dp->cyl == dp->tcyl){
+ dp->offset = off;
+ return off;
+ }
+ dp->cyl = -1;
+
+ fl.ncmd = 0;
+ fl.cmd[fl.ncmd++] = Fseek;
+ fl.cmd[fl.ncmd++] = (dp->thead<<2) | dp->dev;
+ fl.cmd[fl.ncmd++] = dp->tcyl * dp->t->steps;
+ if(floppycmd() < 0)
+ return -1;
+ floppywait(1);
+ if(fl.nstat < 2){
+ DPRINT("seek: confused\n");
+ fl.confused = 1;
+ return -1;
+ }
+ if((fl.stat[0] & (Codemask|Seekend)) != Seekend){
+ DPRINT("seek: failed\n");
+ dp->confused = 1;
+ return -1;
+ }
+
+ dp->cyl = dp->tcyl;
+ dp->offset = off;
+ DPRINT("seek to %d succeeded\n", dp->offset);
+
+ return dp->offset;
+}
+
+/*
+ * read or write to floppy. try up to three times.
+ */
+static long
+floppyxfer(FDrive *dp, int cmd, void *a, long off, long n)
+{
+ long offset;
+ int tries;
+
+ if(off >= dp->t->cap)
+ return 0;
+ if(off + n > dp->t->cap)
+ n = dp->t->cap - off;
+
+ /* retry on error (until it gets ridiculous) */
+ for(tries = 0; tries < dp->maxtries; tries++){
+
+ dp->len = n;
+ if(pcfloppyseek(dp, off) < 0){
+ DPRINT("xfer: seek failed\n");
+ dp->confused = 1;
+ continue;
+ }
+
+ /*
+ * set up the dma (dp->len may be trimmed)
+ */
+ dp->len = dmasetup(DMAchan, a, dp->len, cmd==Fread);
+ if(dp->len < 0){
+ buggery:
+ dmaend(DMAchan);
+ continue;
+ }
+
+ /*
+ * start operation
+ */
+ fl.ncmd = 0;
+ fl.cmd[fl.ncmd++] = cmd | (dp->t->heads > 1 ? Fmulti : 0);
+ fl.cmd[fl.ncmd++] = (dp->thead<<2) | dp->dev;
+ fl.cmd[fl.ncmd++] = dp->tcyl;
+ fl.cmd[fl.ncmd++] = dp->thead;
+ fl.cmd[fl.ncmd++] = dp->tsec;
+ fl.cmd[fl.ncmd++] = dp->t->bcode;
+ fl.cmd[fl.ncmd++] = dp->t->sectors;
+ fl.cmd[fl.ncmd++] = dp->t->gpl;
+ fl.cmd[fl.ncmd++] = 0xFF;
+ if(floppycmd() < 0)
+ goto buggery;
+
+ /*
+ * give bus to DMA, floppyintr() will read result
+ */
+ floppywait(0);
+ dmaend(DMAchan);
+
+ /*
+ * check for errors
+ */
+ if(fl.nstat < 7){
+ DPRINT("xfer: confused\n");
+ fl.confused = 1;
+ continue;
+ }
+ if((fl.stat[0] & Codemask)!=0 || fl.stat[1] || fl.stat[2]){
+ DPRINT("xfer: failed %ux %ux %ux\n", fl.stat[0],
+ fl.stat[1], fl.stat[2]);
+ DPRINT("offset %lud len %ld\n", off, dp->len);
+ if((fl.stat[0]&Codemask)==Cmdexec && fl.stat[1]==Overrun){
+ DPRINT("DMA overrun: retry\n");
+ } else
+ dp->confused = 1;
+ continue;
+ }
+
+ /*
+ * check for correct cylinder
+ */
+ offset = fl.stat[3] * dp->t->heads + fl.stat[4];
+ offset = offset*dp->t->sectors + fl.stat[5] - 1;
+ offset = offset * c2b[fl.stat[6]];
+ if(offset != off+dp->len){
+ DPRINT("xfer: ends on wrong cyl\n");
+ dp->confused = 1;
+ continue;
+ }
+
+ dp->lasttouched = m->ticks;
+ dp->maxtries = 20;
+ return dp->len;
+ }
+
+ return -1;
+}
+
+/*
+void
+floppymemwrite(void)
+{
+ int i;
+ int n;
+ uchar *a;
+ FDrive *dp;
+
+ dp = &fl.d[0];
+ a = (uchar*)0x80000000;
+ n = 0;
+ while(n < 1440*1024){
+ i = floppyxfer(dp, Fwrite, a+n, n, 1440*1024-n);
+ if(i <= 0)
+ break;
+ n += i;
+ }
+ print("floppymemwrite wrote %d bytes\n", n);
+splhi(); for(;;);
+}
+*/
+
+static void
+floppyintr(Ureg *ur)
+{
+ USED(ur);
+ switch(fl.cmd[0]&~Fmulti){
+ case Fread:
+ case Fwrite:
+ case Fformat:
+ case Fdumpreg:
+ floppyresult();
+ break;
+ case Fseek:
+ case Frecal:
+ default:
+ floppysense(); /* to clear interrupt */
+ break;
+ }
+ fl.ncmd = 0;
+}
diff --git a/os/boot/pc/devfloppy.h b/os/boot/pc/devfloppy.h
new file mode 100644
index 00000000..1d9a76a8
--- /dev/null
+++ b/os/boot/pc/devfloppy.h
@@ -0,0 +1,196 @@
+typedef struct FController FController;
+typedef struct FDrive FDrive;
+typedef struct FType FType;
+
+static void floppyintr(Ureg*);
+static int floppyon(FDrive*);
+static void floppyoff(FDrive*);
+static void floppysetdef(FDrive*);
+
+/*
+ * a floppy drive
+ */
+struct FDrive
+{
+ FType *t; /* floppy type */
+ int dt; /* drive type */
+ int dev;
+
+ ulong lasttouched; /* time last touched */
+ int cyl; /* current arm position */
+ int confused; /* needs to be recalibrated */
+ int offset; /* current offset */
+ int vers;
+ int maxtries;
+
+ int tcyl; /* target cylinder */
+ int thead; /* target head */
+ int tsec; /* target sector */
+ long len; /* size of xfer */
+
+ uchar *cache; /* track cache */
+ int ccyl;
+ int chead;
+
+// Rendez r; /* waiting here for motor to spin up */
+ void *aux;
+};
+
+/*
+ * controller for 4 floppys
+ */
+struct FController
+{
+// QLock; /* exclusive access to the contoller */
+
+ int ndrive;
+ FDrive *d; /* the floppy drives */
+ FDrive *selected;
+ int rate; /* current rate selected */
+ uchar cmd[14]; /* command */
+ int ncmd; /* # command bytes */
+ uchar stat[14]; /* command status */
+ int nstat; /* # status bytes */
+ int confused; /* controler needs to be reset */
+// Rendez r; /* wait here for command termination */
+ int motor; /* bit mask of spinning disks */
+// Rendez kr; /* for motor watcher */
+};
+
+/*
+ * floppy types (all MFM encoding)
+ */
+struct FType
+{
+ char *name;
+ int dt; /* compatible drive type */
+ int bytes; /* bytes/sector */
+ int sectors; /* sectors/track */
+ int heads; /* number of heads */
+ int steps; /* steps per cylinder */
+ int tracks; /* tracks/disk */
+ int gpl; /* intersector gap length for read/write */
+ int fgpl; /* intersector gap length for format */
+ int rate; /* rate code */
+
+ /*
+ * these depend on previous entries and are set filled in
+ * by floppyinit
+ */
+ int bcode; /* coded version of bytes for the controller */
+ long cap; /* drive capacity in bytes */
+ long tsize; /* track size in bytes */
+};
+/* bits in the registers */
+enum
+{
+ /* status registers a & b */
+ Psra= 0x3f0,
+ Psrb= 0x3f1,
+
+ /* digital output register */
+ Pdor= 0x3f2,
+ Fintena= 0x8, /* enable floppy interrupt */
+ Fena= 0x4, /* 0 == reset controller */
+
+ /* main status register */
+ Pmsr= 0x3f4,
+ Fready= 0x80, /* ready to be touched */
+ Ffrom= 0x40, /* data from controller */
+ Ffloppybusy= 0x10, /* operation not over */
+
+ /* data register */
+ Pfdata= 0x3f5,
+ Frecal= 0x07, /* recalibrate cmd */
+ Fseek= 0x0f, /* seek cmd */
+ Fsense= 0x08, /* sense cmd */
+ Fread= 0x66, /* read cmd */
+ Freadid= 0x4a, /* read track id */
+ Fspec= 0x03, /* set hold times */
+ Fwrite= 0x45, /* write cmd */
+ Fformat= 0x4d, /* format cmd */
+ Fmulti= 0x80, /* or'd with Fread or Fwrite for multi-head */
+ Fdumpreg= 0x0e, /* dump internal registers */
+
+ /* digital input register */
+ Pdir= 0x3F7, /* disk changed port (read only) */
+ Pdsr= 0x3F7, /* data rate select port (write only) */
+ Fchange= 0x80, /* disk has changed */
+
+ /* status 0 byte */
+ Drivemask= 3<<0,
+ Seekend= 1<<5,
+ Codemask= (3<<6)|(3<<3),
+ Cmdexec= 1<<6,
+
+ /* status 1 byte */
+ Overrun= 0x10,
+};
+
+/*
+ * types of drive (from PC equipment byte)
+ */
+enum
+{
+ Tnone= 0,
+ T360kb= 1,
+ T1200kb= 2,
+ T720kb= 3,
+ T1440kb= 4,
+};
+
+static void
+pcfloppyintr(Ureg *ur, void *a)
+{
+ USED(a);
+
+ floppyintr(ur);
+}
+
+void
+floppysetup0(FController *fl)
+{
+ uchar equip;
+
+ /*
+ * Read nvram for types of floppies 0 & 1.
+ * Always try floppy 0.
+ */
+ equip = nvramread(0x10);
+ fl->ndrive = 1;
+
+ if(equip & 0xf)
+ fl->ndrive++;
+
+ /*
+ * Allocate the drive storage.
+ * There's always one.
+ */
+ fl->d = xalloc(fl->ndrive*sizeof(FDrive));
+ fl->d[0].dt = (equip >> 4) & 0xf;
+ if(fl->d[0].dt == Tnone)
+ fl->d[0].dt = T1440kb;
+
+ if(fl->ndrive == 2)
+ fl->d[1].dt = equip & 0xf;
+}
+
+void
+floppysetup1(FController*)
+{
+// intrenable(VectorFLOPPY, pcfloppyintr, fl, BUSUNKNOWN);
+ setvec(VectorFLOPPY, pcfloppyintr, 0);
+}
+
+
+static vlong pcfloppyseek(FDrive*, vlong);
+FController fl;
+
+vlong
+floppyseek(Fs *fs, vlong off)
+{
+ FDrive *dp;
+
+ dp = &fl.d[fs->dev];
+ return pcfloppyseek(dp, off);
+}
diff --git a/os/boot/pc/devi82365.c b/os/boot/pc/devi82365.c
new file mode 100644
index 00000000..a4e09d2d
--- /dev/null
+++ b/os/boot/pc/devi82365.c
@@ -0,0 +1,1205 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "io.h"
+
+/*
+ * Support for up to 4 Slot card slots. Generalizing above that is hard
+ * since addressing is not obvious. - presotto
+ *
+ * WARNING: This has never been tried with more than one card slot.
+ */
+
+/*
+ * Intel 82365SL PCIC controller for the PCMCIA or
+ * Cirrus Logic PD6710/PD6720 which is mostly register compatible
+ */
+enum
+{
+ /*
+ * registers indices
+ */
+ Rid= 0x0, /* identification and revision */
+ Ris= 0x1, /* interface status */
+ Rpc= 0x2, /* power control */
+ Foutena= (1<<7), /* output enable */
+ Fautopower= (1<<5), /* automatic power switching */
+ Fcardena= (1<<4), /* PC card enable */
+ Rigc= 0x3, /* interrupt and general control */
+ Fiocard= (1<<5), /* I/O card (vs memory) */
+ Fnotreset= (1<<6), /* reset if not set */
+ FSMIena= (1<<4), /* enable change interrupt on SMI */
+ Rcsc= 0x4, /* card status change */
+ Rcscic= 0x5, /* card status change interrupt config */
+ Fchangeena= (1<<3), /* card changed */
+ Fbwarnena= (1<<1), /* card battery warning */
+ Fbdeadena= (1<<0), /* card battery dead */
+ Rwe= 0x6, /* address window enable */
+ Fmem16= (1<<5), /* use A23-A12 to decode address */
+ Rio= 0x7, /* I/O control */
+ Fwidth16= (1<<0), /* 16 bit data width */
+ Fiocs16= (1<<1), /* IOCS16 determines data width */
+ Fzerows= (1<<2), /* zero wait state */
+ Ftiming= (1<<3), /* timing register to use */
+ Riobtm0lo= 0x8, /* I/O address 0 start low byte */
+ Riobtm0hi= 0x9, /* I/O address 0 start high byte */
+ Riotop0lo= 0xa, /* I/O address 0 stop low byte */
+ Riotop0hi= 0xb, /* I/O address 0 stop high byte */
+ Riobtm1lo= 0xc, /* I/O address 1 start low byte */
+ Riobtm1hi= 0xd, /* I/O address 1 start high byte */
+ Riotop1lo= 0xe, /* I/O address 1 stop low byte */
+ Riotop1hi= 0xf, /* I/O address 1 stop high byte */
+ Rmap= 0x10, /* map 0 */
+
+ /*
+ * CL-PD67xx extension registers
+ */
+ Rmisc1= 0x16, /* misc control 1 */
+ F5Vdetect= (1<<0),
+ Fvcc3V= (1<<1),
+ Fpmint= (1<<2),
+ Fpsirq= (1<<3),
+ Fspeaker= (1<<4),
+ Finpack= (1<<7),
+ Rfifo= 0x17, /* fifo control */
+ Fflush= (1<<7), /* flush fifo */
+ Rmisc2= 0x1E, /* misc control 2 */
+ Flowpow= (1<<1), /* low power mode */
+ Rchipinfo= 0x1F, /* chip information */
+ Ratactl= 0x26, /* ATA control */
+
+ /*
+ * offsets into the system memory address maps
+ */
+ Mbtmlo= 0x0, /* System mem addr mapping start low byte */
+ Mbtmhi= 0x1, /* System mem addr mapping start high byte */
+ F16bit= (1<<7), /* 16-bit wide data path */
+ Mtoplo= 0x2, /* System mem addr mapping stop low byte */
+ Mtophi= 0x3, /* System mem addr mapping stop high byte */
+ Ftimer1= (1<<6), /* timer set 1 */
+ Mofflo= 0x4, /* Card memory offset address low byte */
+ Moffhi= 0x5, /* Card memory offset address high byte */
+ Fregactive= (1<<6), /* attribute memory */
+
+ Mbits= 13, /* msb of Mchunk */
+ Mchunk= 1<<Mbits, /* logical mapping granularity */
+ Nmap= 4, /* max number of maps to use */
+
+ /*
+ * configuration registers - they start at an offset in attribute
+ * memory found in the CIS.
+ */
+ Rconfig= 0,
+ Creset= (1<<7), /* reset device */
+ Clevel= (1<<6), /* level sensitive interrupt line */
+
+ Maxctab= 8, /* maximum configuration table entries */
+};
+
+static int pcmcia_pcmspecial(char *, ISAConf *);
+static void pcmcia_pcmspecialclose(int);
+
+#define MAP(x,o) (Rmap + (x)*0x8 + o)
+
+typedef struct I82365 I82365;
+typedef struct Slot Slot;
+typedef struct Conftab Conftab;
+typedef struct Cisdat Cisdat;
+/* a controller */
+enum
+{
+ Ti82365,
+ Tpd6710,
+ Tpd6720,
+ Tvg46x,
+};
+struct I82365
+{
+ int type;
+ int dev;
+ int nslot;
+ int xreg; /* index register address */
+ int dreg; /* data register address */
+ int irq;
+};
+static I82365 *controller[4];
+static int ncontroller;
+
+/* configuration table entry */
+struct Conftab
+{
+ int index;
+ ushort irqs; /* legal irqs */
+ uchar irqtype;
+ uchar bit16; /* true for 16 bit access */
+ struct {
+ ulong start;
+ ulong len;
+ } io[16];
+ int nio;
+ uchar vpp1;
+ uchar vpp2;
+ uchar memwait;
+ ulong maxwait;
+ ulong readywait;
+ ulong otherwait;
+};
+
+/* cis memory walking */
+struct Cisdat
+{
+ uchar *cisbase;
+ int cispos;
+ int cisskip;
+ int cislen;
+};
+
+/* a card slot */
+struct Slot
+{
+ Lock;
+ int ref;
+
+ I82365 *cp; /* controller for this slot */
+ long memlen; /* memory length */
+ uchar base; /* index register base */
+ uchar slotno; /* slot number */
+
+ /* status */
+ uchar special; /* in use for a special device */
+ uchar already; /* already inited */
+ uchar occupied;
+ uchar battery;
+ uchar wrprot;
+ uchar powered;
+ uchar configed;
+ uchar enabled;
+ uchar busy;
+
+ /* cis info */
+ char verstr[512]; /* version string */
+ uchar cpresent; /* config registers present */
+ ulong caddr; /* relative address of config registers */
+ int nctab; /* number of config table entries */
+ Conftab ctab[Maxctab];
+ Conftab *def; /* default conftab */
+
+ /* for walking through cis */
+ Cisdat;
+
+ /* memory maps */
+ Lock mlock; /* lock down the maps */
+ int time;
+ PCMmap mmap[Nmap]; /* maps, last is always for the kernel */
+};
+static Slot *slot;
+static Slot *lastslot;
+static nslot;
+
+static void cisread(Slot*);
+static void i82365intr(Ureg*, void*);
+static void i82365reset(void);
+static int pcmio(int, ISAConf*);
+static long pcmread(int, int, void*, long, vlong);
+static long pcmwrite(int, int, void*, long, vlong);
+
+static void i82365dump(Slot*);
+
+void
+devi82365link(void)
+{
+ static int already;
+
+ if(already)
+ return;
+ already = 1;
+
+ if (_pcmspecial)
+ return;
+
+ _pcmspecial = pcmcia_pcmspecial;
+ _pcmspecialclose = pcmcia_pcmspecialclose;
+}
+
+/*
+ * reading and writing card registers
+ */
+static uchar
+rdreg(Slot *pp, int index)
+{
+ outb(pp->cp->xreg, pp->base + index);
+ return inb(pp->cp->dreg);
+}
+static void
+wrreg(Slot *pp, int index, uchar val)
+{
+ outb(pp->cp->xreg, pp->base + index);
+ outb(pp->cp->dreg, val);
+}
+
+/*
+ * get info about card
+ */
+static void
+slotinfo(Slot *pp)
+{
+ uchar isr;
+
+ isr = rdreg(pp, Ris);
+ pp->occupied = (isr & (3<<2)) == (3<<2);
+ pp->powered = isr & (1<<6);
+ pp->battery = (isr & 3) == 3;
+ pp->wrprot = isr & (1<<4);
+ pp->busy = isr & (1<<5);
+}
+
+static int
+vcode(int volt)
+{
+ switch(volt){
+ case 5:
+ return 1;
+ case 12:
+ return 2;
+ default:
+ return 0;
+ }
+}
+
+/*
+ * enable the slot card
+ */
+static void
+slotena(Slot *pp)
+{
+ if(pp->enabled)
+ return;
+
+ /* power up and unreset, wait's are empirical (???) */
+ wrreg(pp, Rpc, Fautopower|Foutena|Fcardena);
+ delay(300);
+ wrreg(pp, Rigc, 0);
+ delay(100);
+ wrreg(pp, Rigc, Fnotreset);
+ delay(500);
+
+ /* get configuration */
+ slotinfo(pp);
+ if(pp->occupied){
+ cisread(pp);
+ pp->enabled = 1;
+ } else
+ wrreg(pp, Rpc, Fautopower);
+}
+
+/*
+ * disable the slot card
+ */
+static void
+slotdis(Slot *pp)
+{
+ wrreg(pp, Rpc, 0); /* turn off card power */
+ wrreg(pp, Rwe, 0); /* no windows */
+ pp->enabled = 0;
+}
+
+/*
+ * status change interrupt
+ */
+static void
+i82365intr(Ureg *, void *)
+{
+ uchar csc, was;
+ Slot *pp;
+
+ if(slot == 0)
+ return;
+
+ for(pp = slot; pp < lastslot; pp++){
+ csc = rdreg(pp, Rcsc);
+ was = pp->occupied;
+ slotinfo(pp);
+ if(csc & (1<<3) && was != pp->occupied){
+ if(!pp->occupied)
+ slotdis(pp);
+ }
+ }
+}
+
+enum
+{
+ Mshift= 12,
+ Mgran= (1<<Mshift), /* granularity of maps */
+ Mmask= ~(Mgran-1), /* mask for address bits important to the chip */
+};
+
+/*
+ * get a map for pc card region, return corrected len
+ */
+PCMmap*
+pcmmap(int slotno, ulong offset, int len, int attr)
+{
+ Slot *pp;
+ uchar we, bit;
+ PCMmap *m, *nm;
+ int i;
+ ulong e;
+
+ pp = slot + slotno;
+ lock(&pp->mlock);
+
+ /* convert offset to granularity */
+ if(len <= 0)
+ len = 1;
+ e = ROUND(offset+len, Mgran);
+ offset &= Mmask;
+ len = e - offset;
+
+ /* look for a map that covers the right area */
+ we = rdreg(pp, Rwe);
+ bit = 1;
+ nm = 0;
+ for(m = pp->mmap; m < &pp->mmap[Nmap]; m++){
+ if((we & bit))
+ if(m->attr == attr)
+ if(offset >= m->ca && e <= m->cea){
+
+ m->ref++;
+ unlock(&pp->mlock);
+ return m;
+ }
+ bit <<= 1;
+ if(nm == 0 && m->ref == 0)
+ nm = m;
+ }
+ m = nm;
+ if(m == 0){
+ unlock(&pp->mlock);
+ return 0;
+ }
+
+ /* if isa space isn't big enough, free it and get more */
+ if(m->len < len){
+ if(m->isa){
+ umbfree(m->isa, m->len);
+ m->len = 0;
+ }
+ m->isa = PADDR(umbmalloc(0, len, Mgran));
+ if(m->isa == 0){
+ print("pcmmap %d: out of isa space\n", len);
+ unlock(&pp->mlock);
+ return 0;
+ }
+ m->len = len;
+ }
+
+ /* set up new map */
+ m->ca = offset;
+ m->cea = m->ca + m->len;
+ m->attr = attr;
+ i = m-pp->mmap;
+ bit = 1<<i;
+ wrreg(pp, Rwe, we & ~bit); /* disable map before changing it */
+ wrreg(pp, MAP(i, Mbtmlo), m->isa>>Mshift);
+ wrreg(pp, MAP(i, Mbtmhi), (m->isa>>(Mshift+8)) | F16bit);
+ wrreg(pp, MAP(i, Mtoplo), (m->isa+m->len-1)>>Mshift);
+ wrreg(pp, MAP(i, Mtophi), ((m->isa+m->len-1)>>(Mshift+8)));
+ offset -= m->isa;
+ offset &= (1<<25)-1;
+ offset >>= Mshift;
+ wrreg(pp, MAP(i, Mofflo), offset);
+ wrreg(pp, MAP(i, Moffhi), (offset>>8) | (attr ? Fregactive : 0));
+ wrreg(pp, Rwe, we | bit); /* enable map */
+ m->ref = 1;
+
+ unlock(&pp->mlock);
+ return m;
+}
+
+void
+pcmunmap(int slotno, PCMmap* m)
+{
+ Slot *pp;
+
+ pp = slot + slotno;
+ lock(&pp->mlock);
+ m->ref--;
+ unlock(&pp->mlock);
+}
+
+static void
+increfp(Slot *pp)
+{
+ lock(pp);
+ if(pp->ref++ == 0)
+ slotena(pp);
+ unlock(pp);
+}
+
+static void
+decrefp(Slot *pp)
+{
+ lock(pp);
+ if(pp->ref-- == 1)
+ slotdis(pp);
+ unlock(pp);
+}
+
+/*
+ * look for a card whose version contains 'idstr'
+ */
+static int
+pcmcia_pcmspecial(char *idstr, ISAConf *isa)
+{
+ Slot *pp;
+ extern char *strstr(char*, char*);
+ int enabled;
+
+ i82365reset();
+ for(pp = slot; pp < lastslot; pp++){
+ if(pp->special)
+ continue; /* already taken */
+ enabled = 0;
+ /* make sure we don't power on cards when we already know what's
+ * in them. We'll reread every two minutes if necessary
+ */
+ if (pp->verstr[0] == '\0') {
+ increfp(pp);
+ enabled++;
+ }
+
+ if(pp->occupied) {
+ if(strstr(pp->verstr, idstr)) {
+ if (!enabled)
+ increfp(pp);
+ if(isa == 0 || pcmio(pp->slotno, isa) == 0){
+ pp->special = 1;
+ return pp->slotno;
+ }
+ }
+ } else
+ pp->special = 1;
+ if (enabled)
+ decrefp(pp);
+ }
+ return -1;
+}
+
+static void
+pcmcia_pcmspecialclose(int slotno)
+{
+ Slot *pp;
+
+ print("pcmspecialclose called\n");
+ if(slotno >= nslot)
+ panic("pcmspecialclose");
+ pp = slot + slotno;
+ pp->special = 0;
+ decrefp(pp);
+}
+
+static char *chipname[] =
+{
+[Ti82365] "Intel 82365SL",
+[Tpd6710] "Cirrus Logic PD6710",
+[Tpd6720] "Cirrus Logic PD6720",
+[Tvg46x] "Vadem VG-46x",
+};
+
+static I82365*
+i82365probe(int x, int d, int dev)
+{
+ uchar c, id;
+ I82365 *cp;
+ ISAConf isa;
+ int i, nslot;
+
+ outb(x, Rid + (dev<<7));
+ id = inb(d);
+ if((id & 0xf0) != 0x80)
+ return 0; /* not this family */
+
+ cp = xalloc(sizeof(I82365));
+ cp->xreg = x;
+ cp->dreg = d;
+ cp->dev = dev;
+ cp->type = Ti82365;
+ cp->nslot = 2;
+
+ switch(id){
+ case 0x82:
+ case 0x83:
+ case 0x84:
+ /* could be a cirrus */
+ outb(x, Rchipinfo + (dev<<7));
+ outb(d, 0);
+ c = inb(d);
+ if((c & 0xc0) != 0xc0)
+ break;
+ c = inb(d);
+ if((c & 0xc0) != 0x00)
+ break;
+ if(c & 0x20){
+ cp->type = Tpd6720;
+ } else {
+ cp->type = Tpd6710;
+ cp->nslot = 1;
+ }
+
+ /* low power mode */
+ outb(x, Rmisc2 + (dev<<7));
+ c = inb(d);
+ outb(d, c & ~Flowpow);
+ break;
+ }
+
+ if(cp->type == Ti82365){
+ outb(x, 0x0E + (dev<<7));
+ outb(x, 0x37 + (dev<<7));
+ outb(x, 0x3A + (dev<<7));
+ c = inb(d);
+ outb(d, c|0xC0);
+ outb(x, Rid + (dev<<7));
+ c = inb(d);
+ if(c != id && !(c & 0x08))
+ print("#y%d: id %uX changed to %uX\n", ncontroller, id, c);
+ if(c & 0x08)
+ cp->type = Tvg46x;
+ outb(x, 0x3A + (dev<<7));
+ c = inb(d);
+ outb(d, c & ~0xC0);
+ }
+
+ memset(&isa, 0, sizeof(ISAConf));
+ if(isaconfig("pcmcia", ncontroller, &isa) && isa.irq)
+ cp->irq = isa.irq;
+ else
+ cp->irq = VectorPCMCIA - VectorPIC;
+
+ for(i = 0; i < isa.nopt; i++){
+ if(cistrncmp(isa.opt[i], "nslot=", 6))
+ continue;
+ nslot = strtol(&isa.opt[i][6], nil, 0);
+ if(nslot > 0 && nslot <= 2)
+ cp->nslot = nslot;
+ }
+
+ controller[ncontroller++] = cp;
+ return cp;
+}
+
+static void
+i82365dump(Slot *pp)
+{
+ int i;
+
+ for(i = 0; i < 0x40; i++){
+ if((i&0x0F) == 0)
+ print("\n%2.2uX: ", i);
+ if(((i+1) & 0x0F) == 0x08)
+ print(" - ");
+ print("%2.2uX ", rdreg(pp, i));
+ }
+ print("\n");
+}
+
+/*
+ * set up for slot cards
+ */
+static void
+i82365reset(void)
+{
+ static int already;
+ int i, j;
+ I82365 *cp;
+ Slot *pp;
+
+ if(already)
+ return;
+ already = 1;
+
+
+ /* look for controllers */
+ i82365probe(0x3E0, 0x3E1, 0);
+ i82365probe(0x3E0, 0x3E1, 1);
+ i82365probe(0x3E2, 0x3E3, 0);
+ i82365probe(0x3E2, 0x3E3, 1);
+
+ for(i = 0; i < ncontroller; i++)
+ nslot += controller[i]->nslot;
+ slot = xalloc(nslot * sizeof(Slot));
+
+ /* if the card is there turn on 5V power to keep its battery alive */
+ lastslot = slot;
+ for(i = 0; i < ncontroller; i++){
+ cp = controller[i];
+ print("#y%d: %d slot %s: port 0x%uX irq %d\n",
+ i, cp->nslot, chipname[cp->type], cp->xreg, cp->irq);
+ for(j = 0; j < cp->nslot; j++){
+ pp = lastslot++;
+ pp->slotno = pp - slot;
+ pp->memlen = 64*MB;
+ pp->base = (cp->dev<<7) | (j<<6);
+ pp->cp = cp;
+ slotdis(pp);
+
+ /* interrupt on status change */
+ wrreg(pp, Rcscic, (cp->irq<<4) | Fchangeena);
+ rdreg(pp, Rcsc);
+ }
+
+ /* for card management interrupts */
+ setvec(cp->irq+VectorPIC, i82365intr, 0);
+ }
+}
+
+/*
+ * configure the Slot for IO. We assume very heavily that we can read
+ * configuration info from the CIS. If not, we won't set up correctly.
+ */
+static int
+pcmio(int slotno, ISAConf *isa)
+{
+ uchar we, x, *p;
+ Slot *pp;
+ Conftab *ct, *et, *t;
+ PCMmap *m;
+ int i, index, irq;
+ char *cp;
+
+ irq = isa->irq;
+ if(irq == 2)
+ irq = 9;
+
+ if(slotno > nslot)
+ return -1;
+ pp = slot + slotno;
+
+ if(!pp->occupied)
+ return -1;
+
+ et = &pp->ctab[pp->nctab];
+
+ ct = 0;
+ for(i = 0; i < isa->nopt; i++){
+ if(strncmp(isa->opt[i], "index=", 6))
+ continue;
+ index = strtol(&isa->opt[i][6], &cp, 0);
+ if(cp == &isa->opt[i][6] || index >= pp->nctab)
+ return -1;
+ ct = &pp->ctab[index];
+ }
+ if(ct == 0){
+
+ /* assume default is right */
+ if(pp->def)
+ ct = pp->def;
+ else
+ ct = pp->ctab;
+
+ /* try for best match */
+ if(ct->nio == 0
+ || ct->io[0].start != isa->port || ((1<<irq) & ct->irqs) == 0){
+ for(t = pp->ctab; t < et; t++)
+ if(t->nio
+ && t->io[0].start == isa->port
+ && ((1<<irq) & t->irqs)){
+ ct = t;
+ break;
+ }
+ }
+ if(ct->nio == 0 || ((1<<irq) & ct->irqs) == 0){
+ for(t = pp->ctab; t < et; t++)
+ if(t->nio && ((1<<irq) & t->irqs)){
+ ct = t;
+ break;
+ }
+ }
+ if(ct->nio == 0){
+ for(t = pp->ctab; t < et; t++)
+ if(t->nio){
+ ct = t;
+ break;
+ }
+ }
+ }
+
+ if(ct == et || ct->nio == 0)
+ return -1;
+ if(isa->port == 0 && ct->io[0].start == 0)
+ return -1;
+
+ /* route interrupts */
+ isa->irq = irq;
+ wrreg(pp, Rigc, irq | Fnotreset | Fiocard);
+
+ /* set power and enable device */
+ x = vcode(ct->vpp1);
+ wrreg(pp, Rpc, x|Fautopower|Foutena|Fcardena);
+
+ /* 16-bit data path */
+ if(ct->bit16)
+ x = Ftiming|Fiocs16|Fwidth16;
+ else
+ x = Ftiming;
+ if(ct->nio == 2 && ct->io[1].start)
+ x |= x<<4;
+ wrreg(pp, Rio, x);
+
+ /* enable io port map 0 */
+ if(isa->port == 0)
+ isa->port = ct->io[0].start;
+ we = rdreg(pp, Rwe);
+ wrreg(pp, Riobtm0lo, isa->port);
+ wrreg(pp, Riobtm0hi, isa->port>>8);
+ i = isa->port+ct->io[0].len-1;
+ wrreg(pp, Riotop0lo, i);
+ wrreg(pp, Riotop0hi, i>>8);
+ we |= 1<<6;
+ if(ct->nio == 2 && ct->io[1].start){
+ wrreg(pp, Riobtm1lo, ct->io[1].start);
+ wrreg(pp, Riobtm1hi, ct->io[1].start>>8);
+ i = ct->io[1].start+ct->io[1].len-1;
+ wrreg(pp, Riotop1lo, i);
+ wrreg(pp, Riotop1hi, i>>8);
+ we |= 1<<7;
+ }
+ wrreg(pp, Rwe, we);
+
+ /* only touch Rconfig if it is present */
+ if(pp->cpresent & (1<<Rconfig)){
+ /* Reset adapter */
+ m = pcmmap(slotno, pp->caddr + Rconfig, 1, 1);
+ p = KADDR(m->isa + pp->caddr + Rconfig - m->ca);
+
+ /* set configuration and interrupt type */
+ x = ct->index;
+ if((ct->irqtype & 0x20) && ((ct->irqtype & 0x40)==0 || isa->irq>7))
+ x |= Clevel;
+ *p = x;
+ delay(5);
+
+ pcmunmap(slotno, m);
+ }
+ return 0;
+}
+
+/*
+ * read and crack the card information structure enough to set
+ * important parameters like power
+ */
+static void tcfig(Slot*, Cisdat*, int);
+static void tentry(Slot*, Cisdat*, int);
+static void tvers1(Slot*, Cisdat*, int);
+
+struct {
+ int n;
+ void (*parse)(Slot*, Cisdat*, int);
+} cistab[] = {
+ 0x15, tvers1,
+ 0x1A, tcfig,
+ 0x1B, tentry,
+};
+
+static int
+readc(Cisdat *pp, uchar *x)
+{
+ if(pp->cispos >= pp->cislen)
+ return 0;
+ *x = pp->cisbase[pp->cisskip*pp->cispos];
+ pp->cispos++;
+ return 1;
+}
+
+static int
+xcistuple(int slotno, int tuple, void *v, int nv, int attr)
+{
+ PCMmap *m;
+ Cisdat cis;
+ int i, l;
+ uchar *p;
+ uchar type, link;
+ int this;
+
+ m = pcmmap(slotno, 0, 0, attr);
+ if(m == 0) {
+if(debug) print("could not map\n");
+ return -1;
+ }
+
+ cis.cisbase = KADDR(m->isa);
+ cis.cispos = 0;
+ cis.cisskip = attr ? 2 : 1;
+ cis.cislen = Mchunk;
+
+if(debug) print("cis %d %d #%lux srch %x...", attr, cis.cisskip, cis.cisbase, tuple);
+ /* loop through all the tuples */
+ for(i = 0; i < 1000; i++){
+ this = cis.cispos;
+ if(readc(&cis, &type) != 1)
+ break;
+if(debug) print("%2ux...", type);
+ if(type == 0xFF)
+ break;
+ if(readc(&cis, &link) != 1)
+ break;
+ if(link == 0xFF)
+ break;
+ if(type == tuple) {
+ p = v;
+ for(l=0; l<nv && l<link; l++)
+ if(readc(&cis, p++) != 1)
+ break;
+ pcmunmap(slotno, m);
+if(debug) print("pcm find %2.2ux %d %d\n", type, link, l);
+ return l;
+ }
+ cis.cispos = this + (2+link);
+ }
+ pcmunmap(slotno, m);
+ return -1;
+}
+
+int
+pcmcistuple(int slotno, int tuple, void *v, int nv)
+{
+ int n;
+
+ /* try attribute space, then memory */
+ if((n = xcistuple(slotno, tuple, v, nv, 1)) >= 0)
+ return n;
+ return xcistuple(slotno, tuple, v, nv, 0);
+}
+
+static void
+cisread(Slot *pp)
+{
+ uchar v[256];
+ int i, nv;
+ Cisdat cis;
+
+ memset(pp->ctab, 0, sizeof(pp->ctab));
+ pp->caddr = 0;
+ pp->cpresent = 0;
+ pp->configed = 0;
+ pp->nctab = 0;
+
+ for(i = 0; i < nelem(cistab); i++) {
+ if((nv = pcmcistuple(pp->slotno, cistab[i].n, v, sizeof(v))) >= 0) {
+ cis.cisbase = v;
+ cis.cispos = 0;
+ cis.cisskip = 1;
+ cis.cislen = nv;
+
+ (*cistab[i].parse)(pp, &cis, cistab[i].n);
+ }
+ }
+}
+
+static ulong
+getlong(Cisdat *cis, int size)
+{
+ uchar c;
+ int i;
+ ulong x;
+
+ x = 0;
+ for(i = 0; i < size; i++){
+ if(readc(cis, &c) != 1)
+ break;
+ x |= c<<(i*8);
+ }
+ return x;
+}
+
+static void
+tcfig(Slot *pp, Cisdat *cis, int )
+{
+ uchar size, rasize, rmsize;
+ uchar last;
+
+ if(readc(cis, &size) != 1)
+ return;
+ rasize = (size&0x3) + 1;
+ rmsize = ((size>>2)&0xf) + 1;
+ if(readc(cis, &last) != 1)
+ return;
+ pp->caddr = getlong(cis, rasize);
+ pp->cpresent = getlong(cis, rmsize);
+}
+
+static ulong vexp[8] =
+{
+ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000
+};
+static ulong vmant[16] =
+{
+ 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, 90,
+};
+
+static ulong
+microvolt(Cisdat *cis)
+{
+ uchar c;
+ ulong microvolts;
+ ulong exp;
+
+ if(readc(cis, &c) != 1)
+ return 0;
+ exp = vexp[c&0x7];
+ microvolts = vmant[(c>>3)&0xf]*exp;
+ while(c & 0x80){
+ if(readc(cis, &c) != 1)
+ return 0;
+ switch(c){
+ case 0x7d:
+ break; /* high impedence when sleeping */
+ case 0x7e:
+ case 0x7f:
+ microvolts = 0; /* no connection */
+ break;
+ default:
+ exp /= 10;
+ microvolts += exp*(c&0x7f);
+ }
+ }
+ return microvolts;
+}
+
+static ulong
+nanoamps(Cisdat *cis)
+{
+ uchar c;
+ ulong nanoamps;
+
+ if(readc(cis, &c) != 1)
+ return 0;
+ nanoamps = vexp[c&0x7]*vmant[(c>>3)&0xf];
+ while(c & 0x80){
+ if(readc(cis, &c) != 1)
+ return 0;
+ if(c == 0x7d || c == 0x7e || c == 0x7f)
+ nanoamps = 0;
+ }
+ return nanoamps;
+}
+
+/*
+ * only nominal voltage is important for config
+ */
+static ulong
+power(Cisdat *cis)
+{
+ uchar feature;
+ ulong mv;
+
+ mv = 0;
+ if(readc(cis, &feature) != 1)
+ return 0;
+ if(feature & 1)
+ mv = microvolt(cis);
+ if(feature & 2)
+ microvolt(cis);
+ if(feature & 4)
+ microvolt(cis);
+ if(feature & 8)
+ nanoamps(cis);
+ if(feature & 0x10)
+ nanoamps(cis);
+ if(feature & 0x20)
+ nanoamps(cis);
+ if(feature & 0x40)
+ nanoamps(cis);
+ return mv/1000000;
+}
+
+static ulong mantissa[16] =
+{ 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, };
+
+static ulong exponent[8] =
+{ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, };
+
+static ulong
+ttiming(Cisdat *cis, int scale)
+{
+ uchar unscaled;
+ ulong nanosecs;
+
+ if(readc(cis, &unscaled) != 1)
+ return 0;
+ nanosecs = (mantissa[(unscaled>>3)&0xf]*exponent[unscaled&7])/10;
+ nanosecs = nanosecs * vexp[scale];
+ return nanosecs;
+}
+
+static void
+timing(Cisdat *cis, Conftab *ct)
+{
+ uchar c, i;
+
+ if(readc(cis, &c) != 1)
+ return;
+ i = c&0x3;
+ if(i != 3)
+ ct->maxwait = ttiming(cis, i); /* max wait */
+ i = (c>>2)&0x7;
+ if(i != 7)
+ ct->readywait = ttiming(cis, i); /* max ready/busy wait */
+ i = (c>>5)&0x7;
+ if(i != 7)
+ ct->otherwait = ttiming(cis, i); /* reserved wait */
+}
+
+static void
+iospaces(Cisdat *cis, Conftab *ct)
+{
+ uchar c;
+ int i, nio;
+
+ ct->nio = 0;
+ if(readc(cis, &c) != 1)
+ return;
+
+ ct->bit16 = ((c>>5)&3) >= 2;
+ if(!(c & 0x80)){
+ ct->io[0].start = 0;
+ ct->io[0].len = 1<<(c&0x1f);
+ ct->nio = 1;
+ return;
+ }
+
+ if(readc(cis, &c) != 1)
+ return;
+
+ nio = (c&0xf)+1;
+ for(i = 0; i < nio; i++){
+ ct->io[i].start = getlong(cis, (c>>4)&0x3);
+ ct->io[i].len = getlong(cis, (c>>6)&0x3)+1;
+ }
+ ct->nio = nio;
+}
+
+static void
+irq(Cisdat *cis, Conftab *ct)
+{
+ uchar c;
+
+ if(readc(cis, &c) != 1)
+ return;
+ ct->irqtype = c & 0xe0;
+ if(c & 0x10)
+ ct->irqs = getlong(cis, 2);
+ else
+ ct->irqs = 1<<(c&0xf);
+ ct->irqs &= 0xDEB8; /* levels available to card */
+}
+
+static void
+memspace(Cisdat *cis, int asize, int lsize, int host)
+{
+ ulong haddress, address, len;
+
+ len = getlong(cis, lsize)*256;
+ address = getlong(cis, asize)*256;
+ USED(len, address);
+ if(host){
+ haddress = getlong(cis, asize)*256;
+ USED(haddress);
+ }
+}
+
+static void
+tentry(Slot *pp, Cisdat *cis, int )
+{
+ uchar c, i, feature;
+ Conftab *ct;
+
+ if(pp->nctab >= Maxctab)
+ return;
+ if(readc(cis, &c) != 1)
+ return;
+ ct = &pp->ctab[pp->nctab++];
+
+ /* copy from last default config */
+ if(pp->def)
+ *ct = *pp->def;
+
+ ct->index = c & 0x3f;
+
+ /* is this the new default? */
+ if(c & 0x40)
+ pp->def = ct;
+
+ /* memory wait specified? */
+ if(c & 0x80){
+ if(readc(cis, &i) != 1)
+ return;
+ if(i&0x80)
+ ct->memwait = 1;
+ }
+
+ if(readc(cis, &feature) != 1)
+ return;
+ switch(feature&0x3){
+ case 1:
+ ct->vpp1 = ct->vpp2 = power(cis);
+ break;
+ case 2:
+ power(cis);
+ ct->vpp1 = ct->vpp2 = power(cis);
+ break;
+ case 3:
+ power(cis);
+ ct->vpp1 = power(cis);
+ ct->vpp2 = power(cis);
+ break;
+ default:
+ break;
+ }
+ if(feature&0x4)
+ timing(cis, ct);
+ if(feature&0x8)
+ iospaces(cis, ct);
+ if(feature&0x10)
+ irq(cis, ct);
+ switch((feature>>5)&0x3){
+ case 1:
+ memspace(cis, 0, 2, 0);
+ break;
+ case 2:
+ memspace(cis, 2, 2, 0);
+ break;
+ case 3:
+ if(readc(cis, &c) != 1)
+ return;
+ for(i = 0; i <= (c&0x7); i++)
+ memspace(cis, (c>>5)&0x3, (c>>3)&0x3, c&0x80);
+ break;
+ }
+ pp->configed++;
+}
+
+static void
+tvers1(Slot *pp, Cisdat *cis, int )
+{
+ uchar c, major, minor;
+ int i;
+
+ if(readc(cis, &major) != 1)
+ return;
+ if(readc(cis, &minor) != 1)
+ return;
+ for(i = 0; i < sizeof(pp->verstr)-1; i++){
+ if(readc(cis, &c) != 1)
+ return;
+ if(c == 0)
+ c = '\n';
+ if(c == 0xff)
+ break;
+ pp->verstr[i] = c;
+ }
+ pp->verstr[i] = 0;
+}
diff --git a/os/boot/pc/devpccard.c b/os/boot/pc/devpccard.c
new file mode 100644
index 00000000..d5c96aa9
--- /dev/null
+++ b/os/boot/pc/devpccard.c
@@ -0,0 +1,1957 @@
+/*
+ cardbus and pcmcia (grmph) support.
+*/
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "io.h"
+
+#define ioalloc(addr, len, align, name) (addr)
+#define iofree(addr)
+extern int pciscan(int, Pcidev **);
+extern ulong pcibarsize(Pcidev *, int);
+
+int (*_pcmspecial)(char *, ISAConf *);
+void (*_pcmspecialclose)(int);
+
+int
+pcmspecial(char *idstr, ISAConf *isa)
+{
+ return (_pcmspecial != nil)? _pcmspecial(idstr, isa): -1;
+}
+
+void
+pcmspecialclose(int a)
+{
+ if (_pcmspecialclose != nil)
+ _pcmspecialclose(a);
+}
+
+static ulong
+ioreserve(ulong, int size, int align, char *)
+{
+ static ulong isaend = 0xfd00;
+ ulong ioaddr;
+
+ if (align)
+ isaend = ((isaend + align - 1) / align) * align;
+ ioaddr = isaend;
+ isaend += size;
+ return ioaddr;
+}
+
+#define MAP(x,o) (Rmap + (x)*0x8 + o)
+
+enum {
+ TI_vid = 0x104c,
+ TI_1131_did = 0xAC15,
+ TI_1250_did = 0xAC16,
+ TI_1450_did = 0xAC1B,
+ TI_1251A_did = 0xAC1D,
+
+ Ricoh_vid = 0x1180,
+ Ricoh_476_did = 0x0476,
+ Ricoh_478_did = 0x0478,
+
+ Nslots = 4, /* Maximum number of CardBus slots to use */
+
+ K = 1024,
+ M = K * K,
+
+ LegacyAddr = 0x3e0,
+ NUMEVENTS = 10,
+
+ TI1131xSC = 0x80, // system control
+ TI122X_SC_INTRTIE = 1 << 29,
+ TI12xxIM = 0x8c, //
+ TI1131xCC = 0x91, // card control
+ TI113X_CC_RIENB = 1 << 7,
+ TI113X_CC_ZVENABLE = 1 << 6,
+ TI113X_CC_PCI_IRQ_ENA = 1 << 5,
+ TI113X_CC_PCI_IREQ = 1 << 4,
+ TI113X_CC_PCI_CSC = 1 << 3,
+ TI113X_CC_SPKROUTEN = 1 << 1,
+ TI113X_CC_IFG = 1 << 0,
+ TI1131xDC = 0x92, // device control
+};
+
+typedef struct {
+ ushort r_vid;
+ ushort r_did;
+ char *r_name;
+} variant_t;
+
+static variant_t variant[] = {
+{ Ricoh_vid, Ricoh_476_did, "Ricoh 476 PCI/Cardbus bridge", },
+{ Ricoh_vid, Ricoh_478_did, "Ricoh 478 PCI/Cardbus bridge", },
+{ TI_vid, TI_1131_did, "TI PCI-1131 Cardbus Controller", },
+{ TI_vid, TI_1250_did, "TI PCI-1250 Cardbus Controller", },
+{ TI_vid, TI_1450_did, "TI PCI-1450 Cardbus Controller", },
+{ TI_vid, TI_1251A_did, "TI PCI-1251A Cardbus Controller", },
+};
+
+/* Cardbus registers */
+enum {
+ SocketEvent = 0,
+ SE_CCD = 3 << 1,
+ SE_POWER = 1 << 3,
+ SocketMask = 1,
+ SocketState = 2,
+ SS_CCD = 3 << 1,
+ SS_POWER = 1 << 3,
+ SS_PC16 = 1 << 4,
+ SS_CBC = 1 << 5,
+ SS_NOTCARD = 1 << 7,
+ SS_BADVCC = 1 << 9,
+ SS_5V = 1 << 10,
+ SS_3V = 1 << 11,
+ SocketForce = 3,
+ SocketControl = 4,
+ SC_5V = 0x22,
+ SC_3V = 0x33,
+};
+
+enum {
+ PciPCR_IO = 1 << 0,
+ PciPCR_MEM = 1 << 1,
+ PciPCR_Master = 1 << 2,
+
+ Nbars = 6,
+ Ncmd = 10,
+ CBIRQ = 9,
+
+ PC16,
+ PC32,
+};
+
+enum {
+ Ti82365,
+ Tpd6710,
+ Tpd6720,
+ Tvg46x,
+};
+
+static char *chipname[] = {
+[Ti82365] "Intel 82365SL",
+[Tpd6710] "Cirrus Logic PD6710",
+[Tpd6720] "Cirrus Logic PD6720",
+[Tvg46x] "Vadem VG-46x",
+};
+
+/*
+ * Intel 82365SL PCIC controller for the PCMCIA or
+ * Cirrus Logic PD6710/PD6720 which is mostly register compatible
+ */
+enum
+{
+ /*
+ * registers indices
+ */
+ Rid= 0x0, /* identification and revision */
+ Ris= 0x1, /* interface status */
+ Rpc= 0x2, /* power control */
+ Foutena= (1<<7), /* output enable */
+ Fautopower= (1<<5), /* automatic power switching */
+ Fcardena= (1<<4), /* PC card enable */
+ Rigc= 0x3, /* interrupt and general control */
+ Fiocard= (1<<5), /* I/O card (vs memory) */
+ Fnotreset= (1<<6), /* reset if not set */
+ FSMIena= (1<<4), /* enable change interrupt on SMI */
+ Rcsc= 0x4, /* card status change */
+ Rcscic= 0x5, /* card status change interrupt config */
+ Fchangeena= (1<<3), /* card changed */
+ Fbwarnena= (1<<1), /* card battery warning */
+ Fbdeadena= (1<<0), /* card battery dead */
+ Rwe= 0x6, /* address window enable */
+ Fmem16= (1<<5), /* use A23-A12 to decode address */
+ Rio= 0x7, /* I/O control */
+ Fwidth16= (1<<0), /* 16 bit data width */
+ Fiocs16= (1<<1), /* IOCS16 determines data width */
+ Fzerows= (1<<2), /* zero wait state */
+ Ftiming= (1<<3), /* timing register to use */
+ Riobtm0lo= 0x8, /* I/O address 0 start low byte */
+ Riobtm0hi= 0x9, /* I/O address 0 start high byte */
+ Riotop0lo= 0xa, /* I/O address 0 stop low byte */
+ Riotop0hi= 0xb, /* I/O address 0 stop high byte */
+ Riobtm1lo= 0xc, /* I/O address 1 start low byte */
+ Riobtm1hi= 0xd, /* I/O address 1 start high byte */
+ Riotop1lo= 0xe, /* I/O address 1 stop low byte */
+ Riotop1hi= 0xf, /* I/O address 1 stop high byte */
+ Rmap= 0x10, /* map 0 */
+
+ /*
+ * CL-PD67xx extension registers
+ */
+ Rmisc1= 0x16, /* misc control 1 */
+ F5Vdetect= (1<<0),
+ Fvcc3V= (1<<1),
+ Fpmint= (1<<2),
+ Fpsirq= (1<<3),
+ Fspeaker= (1<<4),
+ Finpack= (1<<7),
+ Rfifo= 0x17, /* fifo control */
+ Fflush= (1<<7), /* flush fifo */
+ Rmisc2= 0x1E, /* misc control 2 */
+ Flowpow= (1<<1), /* low power mode */
+ Rchipinfo= 0x1F, /* chip information */
+ Ratactl= 0x26, /* ATA control */
+
+ /*
+ * offsets into the system memory address maps
+ */
+ Mbtmlo= 0x0, /* System mem addr mapping start low byte */
+ Mbtmhi= 0x1, /* System mem addr mapping start high byte */
+ F16bit= (1<<7), /* 16-bit wide data path */
+ Mtoplo= 0x2, /* System mem addr mapping stop low byte */
+ Mtophi= 0x3, /* System mem addr mapping stop high byte */
+ Ftimer1= (1<<6), /* timer set 1 */
+ Mofflo= 0x4, /* Card memory offset address low byte */
+ Moffhi= 0x5, /* Card memory offset address high byte */
+ Fregactive= (1<<6), /* attribute memory */
+
+ /*
+ * configuration registers - they start at an offset in attribute
+ * memory found in the CIS.
+ */
+ Rconfig= 0,
+ Creset= (1<<7), /* reset device */
+ Clevel= (1<<6), /* level sensitive interrupt line */
+};
+
+/*
+ * read and crack the card information structure enough to set
+ * important parameters like power
+ */
+/* cis memory walking */
+typedef struct Cisdat {
+ uchar *cisbase;
+ int cispos;
+ int cisskip;
+ int cislen;
+} Cisdat;
+
+/* configuration table entry */
+typedef struct PCMconftab PCMconftab;
+struct PCMconftab
+{
+ int index;
+ ushort irqs; /* legal irqs */
+ uchar irqtype;
+ uchar bit16; /* true for 16 bit access */
+ struct {
+ ulong start;
+ ulong len;
+ } io[16];
+ int nio;
+ uchar vpp1;
+ uchar vpp2;
+ uchar memwait;
+ ulong maxwait;
+ ulong readywait;
+ ulong otherwait;
+};
+
+typedef struct {
+ char pi_verstr[512]; /* Version string */
+ PCMmap pi_mmap[4]; /* maps, last is always for the kernel */
+ ulong pi_conf_addr; /* Config address */
+ uchar pi_conf_present; /* Config register present */
+ int pi_nctab; /* In use configuration tables */
+ PCMconftab pi_ctab[8]; /* Configuration tables */
+ PCMconftab *pi_defctab; /* Default conftab */
+
+ int pi_port; /* Actual port usage */
+ int pi_irq; /* Actual IRQ usage */
+} pcminfo_t;
+
+#define qlock(i) {/* nothing to do */;}
+#define qunlock(i) {/* nothing to do */;}
+typedef struct QLock { int r; } QLock;
+
+typedef struct {
+ QLock;
+ variant_t *cb_variant; /* Which CardBus chipset */
+ Pcidev *cb_pci; /* The bridge itself */
+ ulong *cb_regs; /* Cardbus registers */
+ int cb_ltype; /* Legacy type */
+ int cb_lindex; /* Legacy port index address */
+ int cb_ldata; /* Legacy port data address */
+ int cb_lbase; /* Base register for this socket */
+
+ int cb_state; /* Current state of card */
+ int cb_type; /* Type of card */
+ pcminfo_t cb_linfo; /* PCMCIA slot info */
+
+ int cb_refs; /* Number of refs to slot */
+ QLock cb_refslock; /* inc/dev ref lock */
+} cb_t;
+
+static int managerstarted;
+
+enum {
+ Mshift= 12,
+ Mgran= (1<<Mshift), /* granularity of maps */
+ Mmask= ~(Mgran-1), /* mask for address bits important to the chip */
+};
+
+static cb_t cbslots[Nslots];
+static int nslots;
+
+static ulong exponent[8] = {
+ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000,
+};
+
+static ulong vmant[16] = {
+ 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, 90,
+};
+
+static ulong mantissa[16] = {
+ 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80,
+};
+
+static char Enocard[] = "No card in slot";
+
+static void cbint(Ureg *, void *);
+static int powerup(cb_t *);
+static void configure(cb_t *);
+static void managecard(cb_t *);
+static void cardmanager(void *);
+static void eject(cb_t *);
+static void interrupt(Ureg *, void *);
+static void powerdown(cb_t *cb);
+static void unconfigure(cb_t *cb);
+
+static void i82365probe(cb_t *cb, int lindex, int ldata);
+static void i82365configure(cb_t *cb);
+static PCMmap *isamap(cb_t *cb, ulong offset, int len, int attr);
+static void isaunmap(PCMmap* m);
+static uchar rdreg(cb_t *cb, int index);
+static void wrreg(cb_t *cb, int index, uchar val);
+static int readc(Cisdat *cis, uchar *x);
+static void tvers1(cb_t *cb, Cisdat *cis, int );
+static void tcfig(cb_t *cb, Cisdat *cis, int );
+static void tentry(cb_t *cb, Cisdat *cis, int );
+static int vcode(int volt);
+static int pccard_pcmspecial(char *idstr, ISAConf *isa);
+static void pccard_pcmspecialclose(int slotno);
+
+enum {
+ CardDetected,
+ CardPowered,
+ CardEjected,
+ CardConfigured,
+};
+
+static char *messages[] = {
+[CardDetected] "CardDetected",
+[CardPowered] "CardPowered",
+[CardEjected] "CardEjected",
+[CardConfigured] "CardConfigured",
+};
+
+enum {
+ SlotEmpty,
+ SlotFull,
+ SlotPowered,
+ SlotConfigured,
+};
+
+static char *states[] = {
+[SlotEmpty] "SlotEmpty",
+[SlotFull] "SlotFull",
+[SlotPowered] "SlotPowered",
+[SlotConfigured] "SlotConfigured",
+};
+
+static void
+engine(cb_t *cb, int message)
+{
+ // print("engine(%d): %s(%s)\n",
+ // (int)(cb - cbslots), states[cb->cb_state], messages[message]);
+ switch (cb->cb_state) {
+ case SlotEmpty:
+
+ switch (message) {
+ case CardDetected:
+ cb->cb_state = SlotFull;
+ powerup(cb);
+ break;
+ case CardEjected:
+ break;
+ default:
+ print("#Y%d: Invalid message %s in SlotEmpty state\n",
+ (int)(cb - cbslots), messages[message]);
+ break;
+ }
+ break;
+
+ case SlotFull:
+
+ switch (message) {
+ case CardPowered:
+ cb->cb_state = SlotPowered;
+ configure(cb);
+ break;
+ case CardEjected:
+ cb->cb_state = SlotEmpty;
+ powerdown(cb);
+ break;
+ default:
+ //print("#Y%d: Invalid message %s in SlotFull state\n",
+ // (int)(cb - cbslots), messages[message]);
+ break;
+ }
+ break;
+
+ case SlotPowered:
+
+ switch (message) {
+ case CardConfigured:
+ cb->cb_state = SlotConfigured;
+ break;
+ case CardEjected:
+ cb->cb_state = SlotEmpty;
+ unconfigure(cb);
+ powerdown(cb);
+ break;
+ default:
+ print("#Y%d: Invalid message %s in SlotPowered state\n",
+ (int)(cb - cbslots), messages[message]);
+ break;
+ }
+ break;
+
+ case SlotConfigured:
+
+ switch (message) {
+ case CardEjected:
+ cb->cb_state = SlotEmpty;
+ unconfigure(cb);
+ powerdown(cb);
+ break;
+ default:
+ print("#Y%d: Invalid message %s in SlotConfigured state\n",
+ (int)(cb - cbslots), messages[message]);
+ break;
+ }
+ break;
+ }
+}
+
+static void
+qengine(cb_t *cb, int message)
+{
+ qlock(cb);
+ engine(cb, message);
+ qunlock(cb);
+}
+
+typedef struct {
+ cb_t *e_cb;
+ int e_message;
+} events_t;
+
+static Lock levents;
+static events_t events[NUMEVENTS];
+// static Rendez revents;
+static int nevents;
+
+//static void
+//iengine(cb_t *cb, int message)
+//{
+// if (nevents >= NUMEVENTS) {
+// print("#Y: Too many events queued, discarding request\n");
+// return;
+// }
+// ilock(&levents);
+// events[nevents].e_cb = cb;
+// events[nevents].e_message = message;
+// nevents++;
+// iunlock(&levents);
+// wakeup(&revents);
+//}
+
+static int
+eventoccured(void)
+{
+ return nevents > 0;
+}
+
+// static void
+// processevents(void *)
+// {
+// while (1) {
+// int message;
+// cb_t *cb;
+//
+// sleep(&revents, (int (*)(void *))eventoccured, nil);
+//
+// cb = nil;
+// message = 0;
+// ilock(&levents);
+// if (nevents > 0) {
+// cb = events[0].e_cb;
+// message = events[0].e_message;
+// nevents--;
+// if (nevents > 0)
+// memmove(events, &events[1], nevents * sizeof(events_t));
+// }
+// iunlock(&levents);
+//
+// if (cb)
+// qengine(cb, message);
+// }
+// }
+
+// static void
+// interrupt(Ureg *, void *)
+// {
+// int i;
+//
+// for (i = 0; i != nslots; i++) {
+// cb_t *cb = &cbslots[i];
+// ulong event, state;
+//
+// event= cb->cb_regs[SocketEvent];
+// state = cb->cb_regs[SocketState];
+// rdreg(cb, Rcsc); /* Ack the interrupt */
+//
+// print("interrupt: slot %d, event %.8lX, state %.8lX, (%s)\n",
+// (int)(cb - cbslots), event, state, states[cb->cb_state]);
+//
+// if (event & SE_CCD) {
+// cb->cb_regs[SocketEvent] |= SE_CCD; /* Ack interrupt */
+// if (state & SE_CCD) {
+// if (cb->cb_state != SlotEmpty) {
+// print("#Y: take cardejected interrupt\n");
+// iengine(cb, CardEjected);
+// }
+// }
+// else
+// iengine(cb, CardDetected);
+// }
+//
+// if (event & SE_POWER) {
+// cb->cb_regs[SocketEvent] |= SE_POWER; /* Ack interrupt */
+// iengine(cb, CardPowered);
+// }
+// }
+// }
+
+void
+devpccardlink(void)
+{
+ static int initialized;
+ Pcidev *pci;
+ int i;
+// uchar intl;
+
+ if (initialized)
+ return;
+ initialized = 1;
+
+ if (!getconf("pccard0"))
+ return;
+
+ if (_pcmspecial) {
+ print("#Y: CardBus and PCMCIA at the same time?\n");
+ return;
+ }
+
+ _pcmspecial = pccard_pcmspecial;
+ _pcmspecialclose = pccard_pcmspecialclose;
+
+
+ /* Allocate legacy space */
+ if (ioalloc(LegacyAddr, 2, 0, "i82365.0") < 0)
+ print("#Y: WARNING: Cannot allocate legacy ports\n");
+
+ /* Find all CardBus controllers */
+ pci = nil;
+// intl = (uchar)-1;
+ while ((pci = pcimatch(pci, 0, 0)) != nil) {
+ ulong baddr;
+ uchar pin;
+ cb_t *cb;
+ int slot;
+
+ for (i = 0; i != nelem(variant); i++)
+ if (pci->vid == variant[i].r_vid && pci->did == variant[i].r_did)
+ break;
+ if (i == nelem(variant))
+ continue;
+
+ /* initialize this slot */
+ slot = nslots++;
+ cb = &cbslots[slot];
+
+ cb->cb_pci = pci;
+ cb->cb_variant = &variant[i];
+
+ // Don't you love standards!
+ if (pci->vid == TI_vid) {
+ if (pci->did <= TI_1131_did) {
+ uchar cc;
+
+ cc = pcicfgr8(pci, TI1131xCC);
+ cc &= ~(TI113X_CC_PCI_IRQ_ENA |
+ TI113X_CC_PCI_IREQ |
+ TI113X_CC_PCI_CSC |
+ TI113X_CC_ZVENABLE);
+ cc |= TI113X_CC_PCI_IRQ_ENA |
+ TI113X_CC_PCI_IREQ |
+ TI113X_CC_SPKROUTEN;
+ pcicfgw8(pci, TI1131xCC, cc);
+
+ // PCI interrupts only
+ pcicfgw8(pci, TI1131xDC,
+ pcicfgr8(pci, TI1131xDC) & ~6);
+
+ // CSC ints to PCI bus.
+ wrreg(cb, Rigc, rdreg(cb, Rigc) | 0x10);
+ }
+ else if (pci->did == TI_1250_did) {
+ print("No support yet for the TI_1250_did, prod pb\n");
+ }
+ }
+
+// if (intl != -1 && intl != pci->intl)
+// intrenable(pci->intl, interrupt, cb, pci->tbdf, "cardbus");
+// intl = pci->intl;
+
+ // Set up PCI bus numbers if needed.
+ if (pcicfgr8(pci, PciSBN) == 0) {
+ static int busbase = 0x20;
+
+ pcicfgw8(pci, PciSBN, busbase);
+ pcicfgw8(pci, PciUBN, busbase + 2);
+ busbase += 3;
+ }
+
+ // Patch up intl if needed.
+ if ((pin = pcicfgr8(pci, PciINTP)) != 0 &&
+ (pci->intl == 0xff || pci->intl == 0)) {
+ pci->intl = pciipin(nil, pin);
+ pcicfgw8(pci, PciINTL, pci->intl);
+
+ if (pci->intl == 0xff || pci->intl == 0)
+ print("#Y%d: No interrupt?\n", (int)(cb - cbslots));
+ }
+
+ if ((baddr = pcicfgr32(cb->cb_pci, PciBAR0)) == 0) {
+ int align = (pci->did == Ricoh_478_did)? 0x10000: 0x1000;
+
+ baddr = upamalloc(baddr, align, align);
+ pcicfgw32(cb->cb_pci, PciBAR0, baddr);
+ cb->cb_regs = (ulong *)KADDR(baddr);
+ }
+ else
+ cb->cb_regs = (ulong *)KADDR(upamalloc(baddr, 4096, 0));
+ cb->cb_state = SlotEmpty;
+
+ /* Don't really know what to do with this... */
+ i82365probe(cb, LegacyAddr, LegacyAddr + 1);
+
+ print("#Y%ld: %s, %.8ulX intl %d\n", cb - cbslots,
+ variant[i].r_name, baddr, pci->intl);
+ }
+
+ if (nslots == 0)
+ return;
+
+ for (i = 0; i != nslots; i++) {
+ cb_t *cb = &cbslots[i];
+
+ if ((cb->cb_regs[SocketState] & SE_CCD) == 0)
+ engine(cb, CardDetected);
+ }
+
+ delay(500); /* Allow time for power up */
+
+ for (i = 0; i != nslots; i++) {
+ cb_t *cb = &cbslots[i];
+
+ if (cb->cb_regs[SocketState] & SE_POWER)
+ engine(cb, CardPowered);
+
+ /* Enable interrupt on all events */
+// cb->cb_regs[SocketMask] |= 0xF;
+// wrreg(cb, Rcscic, 0xC);
+ }
+}
+
+static int
+powerup(cb_t *cb)
+{
+ ulong state;
+ ushort bcr;
+
+ if ((state = cb->cb_regs[SocketState]) & SS_PC16) {
+
+ // print("#Y%ld: Probed a PC16 card, powering up card\n", cb - cbslots);
+ cb->cb_type = PC16;
+ memset(&cb->cb_linfo, 0, sizeof(pcminfo_t));
+
+ /* power up and unreset, wait's are empirical (???) */
+ wrreg(cb, Rpc, Fautopower|Foutena|Fcardena);
+ delay(300);
+ wrreg(cb, Rigc, 0);
+ delay(100);
+ wrreg(cb, Rigc, Fnotreset);
+
+ return 1;
+ }
+
+ if (cb->cb_regs[SocketState] & SS_CCD)
+ return 0;
+
+ if ((state & SS_CBC) == 0 || (state & SS_NOTCARD)) {
+ print("#Y%ld: No cardbus card inserted\n", cb - cbslots);
+ return 0;
+ }
+
+ if (state & SS_BADVCC) {
+ print("#Y%ld: Bad VCC request to card, powering down card!\n",
+ cb - cbslots);
+ cb->cb_regs[SocketControl] = 0;
+ return 0;
+ }
+
+ if ((state & SS_3V) == 0 && (state & SS_5V) == 0) {
+ print("#Y%ld: Unsupported voltage, powering down card!\n",
+ cb - cbslots);
+ cb->cb_regs[SocketControl] = 0;
+ return 0;
+ }
+
+ print("#Y%ld: card %spowered at %d volt\n", cb - cbslots,
+ (state & SS_POWER)? "": "not ",
+ (state & SS_3V)? 3: (state & SS_5V)? 5: -1);
+
+ /* Power up the card
+ * and make sure the secondary bus is not in reset.
+ */
+ cb->cb_regs[SocketControl] = (state & SS_5V)? SC_5V: SC_3V;
+ delay(50);
+ bcr = pcicfgr16(cb->cb_pci, PciBCR);
+ bcr &= ~0x40;
+ pcicfgw16(cb->cb_pci, PciBCR, bcr);
+ delay(100);
+
+ cb->cb_type = PC32;
+
+ return 1;
+}
+
+static void
+powerdown(cb_t *cb)
+{
+ ushort bcr;
+
+ if (cb->cb_type == PC16) {
+
+ wrreg(cb, Rpc, 0); /* turn off card power */
+ wrreg(cb, Rwe, 0); /* no windows */
+
+ cb->cb_type = -1;
+ return;
+ }
+
+ bcr = pcicfgr16(cb->cb_pci, PciBCR);
+ bcr |= 0x40;
+ pcicfgw16(cb->cb_pci, PciBCR, bcr);
+ cb->cb_regs[SocketControl] = 0;
+ cb->cb_type = -1;
+}
+
+static void
+configure(cb_t *cb)
+{
+ int i;
+ Pcidev *pci;
+
+ // print("configuring slot %d (%s)\n", (int)(cb - cbslots), states[cb->cb_state]);
+ if (cb->cb_state == SlotConfigured)
+ return;
+ engine(cb, CardConfigured);
+
+ delay(50); /* Emperically established */
+
+ if (cb->cb_type == PC16) {
+ i82365configure(cb);
+ return;
+ }
+
+ /* Scan the CardBus for new PCI devices */
+ pciscan(pcicfgr8(cb->cb_pci, PciSBN), &cb->cb_pci->bridge);
+ pci = cb->cb_pci->bridge;
+ while (pci) {
+ ulong size, bar;
+ int memindex, ioindex;
+
+ /* Treat the found device as an ordinary PCI card. It seems that the
+ CIS is not always present in CardBus cards. XXX, need to support
+ multifunction cards */
+ memindex = ioindex = 0;
+ for (i = 0; i != Nbars; i++) {
+
+ if (pci->mem[i].size == 0) continue;
+ if (pci->mem[i].bar & 1) {
+
+ // Allocate I/O space
+ if (ioindex > 1) {
+ print("#Y%ld: WARNING: Can only configure 2 I/O slots\n", cb - cbslots);
+ continue;
+ }
+ bar = ioreserve(-1, pci->mem[i].size, 0, "cardbus");
+ pci->mem[i].bar = bar | 1;
+ pcicfgw32(pci, PciBAR0 + i * sizeof(ulong),
+ pci->mem[i].bar);
+ pcicfgw16(cb->cb_pci, PciCBIBR0 + ioindex * 8, bar);
+ pcicfgw16(cb->cb_pci, PciCBILR0 + ioindex * 8,
+ bar + pci->mem[i].size - 1);
+ //print("ioindex[%d] %.8uX (%d)\n",
+ // ioindex, bar, pci->mem[i].size);
+ ioindex++;
+ continue;
+ }
+
+ // Allocating memory space
+ if (memindex > 1) {
+ print("#Y%ld: WARNING: Can only configure 2 memory slots\n", cb - cbslots);
+ continue;
+ }
+
+ bar = upamalloc(0, pci->mem[i].size, BY2PG);
+ pci->mem[i].bar = bar | (pci->mem[i].bar & 0x80);
+ pcicfgw32(pci, PciBAR0 + i * sizeof(ulong), pci->mem[i].bar);
+ pcicfgw32(cb->cb_pci, PciCBMBR0 + memindex * 8, bar);
+ pcicfgw32(cb->cb_pci, PciCBMLR0 + memindex * 8,
+ bar + pci->mem[i].size - 1);
+
+ if (pci->mem[i].bar & 0x80)
+ /* Enable prefetch */
+ pcicfgw16(cb->cb_pci, PciBCR,
+ pcicfgr16(cb->cb_pci, PciBCR) |
+ (1 << (8 + memindex)));
+
+ //print("memindex[%d] %.8uX (%d)\n",
+ // memindex, bar, pci->mem[i].size);
+ memindex++;
+ }
+
+ if ((size = pcibarsize(pci, PciEBAR0)) > 0) {
+
+ if (memindex > 1)
+ print("#Y%ld: WARNING: Too many memory spaces, not mapping ROM space\n",
+ cb - cbslots);
+ else {
+ pci->rom.bar = upamalloc(0, size, BY2PG);
+ pci->rom.size = size;
+
+ pcicfgw32(pci, PciEBAR0, pci->rom.bar);
+ pcicfgw32(cb->cb_pci, PciCBMBR0 + memindex * 8,
+ pci->rom.bar);
+ pcicfgw32(cb->cb_pci, PciCBMLR0 + memindex * 8,
+ pci->rom.bar + pci->rom.size - 1);
+ }
+ }
+
+ /* Set the basic PCI registers for the device */
+ pcicfgw16(pci, PciPCR,
+ pcicfgr16(pci, PciPCR) |
+ PciPCR_IO|PciPCR_MEM|PciPCR_Master);
+ pcicfgw8(pci, PciCLS, 8);
+ pcicfgw8(pci, PciLTR, 64);
+
+ if (pcicfgr8(pci, PciINTP)) {
+ pci->intl = pcicfgr8(cb->cb_pci, PciINTL);
+ pcicfgw8(pci, PciINTL, pci->intl);
+
+ /* Route interrupts to INTA#/B# */
+ pcicfgw16(cb->cb_pci, PciBCR,
+ pcicfgr16(cb->cb_pci, PciBCR) & ~(1 << 7));
+ }
+
+ pci = pci->list;
+ }
+}
+
+static void
+unconfigure(cb_t *cb)
+{
+ Pcidev *pci;
+ int i, ioindex, memindex;
+
+ if (cb->cb_type == PC16) {
+ print("#Y%d: Don't know how to unconfigure a PC16 card\n",
+ (int)(cb - cbslots));
+
+ memset(&cb->cb_linfo, 0, sizeof(pcminfo_t));
+ return;
+ }
+
+ pci = cb->cb_pci->bridge;
+ if (pci == nil)
+ return; /* Not configured */
+ cb->cb_pci->bridge = nil;
+
+ memindex = ioindex = 0;
+ while (pci) {
+ Pcidev *_pci;
+
+ for (i = 0; i != Nbars; i++) {
+ if (pci->mem[i].size == 0) continue;
+ if (pci->mem[i].bar & 1) {
+ iofree(pci->mem[i].bar & ~1);
+ pcicfgw16(cb->cb_pci, PciCBIBR0 + ioindex * 8,
+ (ushort)-1);
+ pcicfgw16(cb->cb_pci, PciCBILR0 + ioindex * 8, 0);
+ ioindex++;
+ continue;
+ }
+
+ upafree(pci->mem[i].bar & ~0xF, pci->mem[i].size);
+ pcicfgw32(cb->cb_pci, PciCBMBR0 + memindex * 8,
+ (ulong)-1);
+ pcicfgw32(cb->cb_pci, PciCBMLR0 + memindex * 8, 0);
+ pcicfgw16(cb->cb_pci, PciBCR,
+ pcicfgr16(cb->cb_pci, PciBCR) &
+ ~(1 << (8 + memindex)));
+ memindex++;
+ }
+
+ if (pci->rom.bar && memindex < 2) {
+ upafree(pci->rom.bar & ~0xF, pci->rom.size);
+ pcicfgw32(cb->cb_pci, PciCBMBR0 + memindex * 8,
+ (ulong)-1);
+ pcicfgw32(cb->cb_pci, PciCBMLR0 + memindex * 8, 0);
+ memindex++;
+ }
+
+ _pci = pci->list;
+ free(_pci);
+ pci = _pci;
+ }
+}
+
+static void
+i82365configure(cb_t *cb)
+{
+ int this;
+ Cisdat cis;
+ PCMmap *m;
+ uchar type, link;
+
+ /*
+ * Read all tuples in attribute space.
+ */
+ m = isamap(cb, 0, 0, 1);
+ if(m == 0)
+ return;
+
+ cis.cisbase = KADDR(m->isa);
+ cis.cispos = 0;
+ cis.cisskip = 2;
+ cis.cislen = m->len;
+
+ /* loop through all the tuples */
+ for(;;){
+ this = cis.cispos;
+ if(readc(&cis, &type) != 1)
+ break;
+ if(type == 0xFF)
+ break;
+ if(readc(&cis, &link) != 1)
+ break;
+
+ switch(type){
+ default:
+ break;
+ case 0x15:
+ tvers1(cb, &cis, type);
+ break;
+ case 0x1A:
+ tcfig(cb, &cis, type);
+ break;
+ case 0x1B:
+ tentry(cb, &cis, type);
+ break;
+ }
+
+ if(link == 0xFF)
+ break;
+ cis.cispos = this + (2+link);
+ }
+ isaunmap(m);
+}
+
+/*
+ * look for a card whose version contains 'idstr'
+ */
+static int
+pccard_pcmspecial(char *idstr, ISAConf *isa)
+{
+ int i, irq;
+ PCMconftab *ct, *et;
+ pcminfo_t *pi;
+ cb_t *cb;
+ uchar x, we, *p;
+
+ cb = nil;
+ for (i = 0; i != nslots; i++) {
+ cb = &cbslots[i];
+
+ qlock(cb);
+ if (cb->cb_state == SlotConfigured &&
+ cb->cb_type == PC16 &&
+ strstr(cb->cb_linfo.pi_verstr, idstr))
+ break;
+ qunlock(cb);
+ }
+
+ if (i == nslots) {
+ // print("#Y: %s not found\n", idstr);
+ return -1;
+ }
+
+ pi = &cb->cb_linfo;
+
+ /*
+ * configure the PCMslot for IO. We assume very heavily that we can read
+ * configuration info from the CIS. If not, we won't set up correctly.
+ */
+ irq = isa->irq;
+ if(irq == 2)
+ irq = 9;
+
+ et = &pi->pi_ctab[pi->pi_nctab];
+ ct = nil;
+ for(i = 0; i < isa->nopt; i++){
+ int index;
+ char *cp;
+
+ if(strncmp(isa->opt[i], "index=", 6))
+ continue;
+ index = strtol(&isa->opt[i][6], &cp, 0);
+ if(cp == &isa->opt[i][6] || index >= pi->pi_nctab) {
+ qunlock(cb);
+ print("#Y%d: Cannot find index %d in conf table\n",
+ (int)(cb - cbslots), index);
+ return -1;
+ }
+ ct = &pi->pi_ctab[index];
+ }
+
+ if(ct == nil){
+ PCMconftab *t;
+
+ /* assume default is right */
+ if(pi->pi_defctab)
+ ct = pi->pi_defctab;
+ else
+ ct = pi->pi_ctab;
+
+ /* try for best match */
+ if(ct->nio == 0
+ || ct->io[0].start != isa->port || ((1<<irq) & ct->irqs) == 0){
+ for(t = pi->pi_ctab; t < et; t++)
+ if(t->nio
+ && t->io[0].start == isa->port
+ && ((1<<irq) & t->irqs)){
+ ct = t;
+ break;
+ }
+ }
+ if(ct->nio == 0 || ((1<<irq) & ct->irqs) == 0){
+ for(t = pi->pi_ctab; t < et; t++)
+ if(t->nio && ((1<<irq) & t->irqs)){
+ ct = t;
+ break;
+ }
+ }
+ if(ct->nio == 0){
+ for(t = pi->pi_ctab; t < et; t++)
+ if(t->nio){
+ ct = t;
+ break;
+ }
+ }
+ }
+
+ if(ct == et || ct->nio == 0) {
+ qunlock(cb);
+ print("#Y%d: No configuration?\n", (int)(cb - cbslots));
+ return -1;
+ }
+ if(isa->port == 0 && ct->io[0].start == 0) {
+ qunlock(cb);
+ print("#Y%d: No part or start address\n", (int)(cb - cbslots));
+ return -1;
+ }
+
+ /* route interrupts */
+ isa->irq = irq;
+ wrreg(cb, Rigc, irq | Fnotreset | Fiocard);
+
+ /* set power and enable device */
+ x = vcode(ct->vpp1);
+ wrreg(cb, Rpc, x|Fautopower|Foutena|Fcardena);
+
+ /* 16-bit data path */
+ if(ct->bit16)
+ x = Ftiming|Fiocs16|Fwidth16;
+ else
+ x = Ftiming;
+ if(ct->nio == 2 && ct->io[1].start)
+ x |= x<<4;
+ wrreg(cb, Rio, x);
+
+ /*
+ * enable io port map 0
+ * the 'top' register value includes the last valid address
+ */
+ if(isa->port == 0)
+ isa->port = ct->io[0].start;
+ we = rdreg(cb, Rwe);
+ wrreg(cb, Riobtm0lo, isa->port);
+ wrreg(cb, Riobtm0hi, isa->port>>8);
+ i = isa->port+ct->io[0].len-1;
+ wrreg(cb, Riotop0lo, i);
+ wrreg(cb, Riotop0hi, i>>8);
+ we |= 1<<6;
+ if(ct->nio == 2 && ct->io[1].start){
+ wrreg(cb, Riobtm1lo, ct->io[1].start);
+ wrreg(cb, Riobtm1hi, ct->io[1].start>>8);
+ i = ct->io[1].start+ct->io[1].len-1;
+ wrreg(cb, Riotop1lo, i);
+ wrreg(cb, Riotop1hi, i>>8);
+ we |= 1<<7;
+ }
+ wrreg(cb, Rwe, we);
+
+ /* only touch Rconfig if it is present */
+ if(pi->pi_conf_present & (1<<Rconfig)){
+ PCMmap *m;
+
+ /* Reset adapter */
+ m = isamap(cb, pi->pi_conf_addr + Rconfig, 1, 1);
+ p = KADDR(m->isa + pi->pi_conf_addr + Rconfig - m->ca);
+
+ /* set configuration and interrupt type */
+ x = ct->index;
+ if((ct->irqtype & 0x20) && ((ct->irqtype & 0x40)==0 || isa->irq>7))
+ x |= Clevel;
+ *p = x;
+ delay(5);
+
+ isaunmap(m);
+ }
+
+ pi->pi_port = isa->port;
+ pi->pi_irq = isa->irq;
+ qunlock(cb);
+
+ print("#Y%d: %s irq %ld, port %lX\n", (int)(cb - cbslots), pi->pi_verstr, isa->irq, isa->port);
+ return (int)(cb - cbslots);
+}
+
+static void
+pccard_pcmspecialclose(int slotno)
+{
+ cb_t *cb = &cbslots[slotno];
+
+ wrreg(cb, Rwe, 0); /* no windows */
+}
+
+static int
+xcistuple(int slotno, int tuple, int subtuple, void *v, int nv, int attr)
+{
+ PCMmap *m;
+ Cisdat cis;
+ int i, l;
+ uchar *p;
+ uchar type, link, n, c;
+ int this, subtype;
+ cb_t *cb = &cbslots[slotno];
+
+ m = isamap(cb, 0, 0, attr);
+ if(m == 0)
+ return -1;
+
+ cis.cisbase = KADDR(m->isa);
+ cis.cispos = 0;
+ cis.cisskip = attr ? 2 : 1;
+ cis.cislen = m->len;
+
+ /* loop through all the tuples */
+ for(i = 0; i < 1000; i++){
+ this = cis.cispos;
+ if(readc(&cis, &type) != 1)
+ break;
+ if(type == 0xFF)
+ break;
+ if(readc(&cis, &link) != 1)
+ break;
+ if(link == 0xFF)
+ break;
+
+ n = link;
+ if (link > 1 && subtuple != -1) {
+ if (readc(&cis, &c) != 1)
+ break;
+ subtype = c;
+ n--;
+ } else
+ subtype = -1;
+
+ if(type == tuple && subtype == subtuple) {
+ p = v;
+ for(l=0; l<nv && l<n; l++)
+ if(readc(&cis, p++) != 1)
+ break;
+ isaunmap(m);
+ return nv;
+ }
+ cis.cispos = this + (2+link);
+ }
+ isaunmap(m);
+ return -1;
+}
+
+// static Chan*
+// pccardattach(char *spec)
+// {
+// if (!managerstarted) {
+// managerstarted = 1;
+// kproc("cardbus", processevents, nil);
+// }
+// return devattach('Y', spec);
+// }
+//
+//enum
+//{
+// Qdir,
+// Qctl,
+//
+// Nents = 1,
+//};
+//
+//#define SLOTNO(c) ((ulong)((c->qid.path>>8)&0xff))
+//#define TYPE(c) ((ulong)(c->qid.path&0xff))
+//#define QID(s,t) (((s)<<8)|(t))
+//
+//static int
+//pccardgen(Chan *c, char*, Dirtab *, int , int i, Dir *dp)
+//{
+// int slotno;
+// Qid qid;
+// long len;
+// int entry;
+//
+// if(i == DEVDOTDOT){
+// mkqid(&qid, Qdir, 0, QTDIR);
+// devdir(c, qid, "#Y", 0, eve, 0555, dp);
+// return 1;
+// }
+//
+// len = 0;
+// if(i >= Nents * nslots) return -1;
+// slotno = i / Nents;
+// entry = i % Nents;
+// if (entry == 0) {
+// qid.path = QID(slotno, Qctl);
+// snprint(up->genbuf, sizeof up->genbuf, "cb%dctl", slotno);
+// }
+// else {
+// /* Entries for memory regions. I'll implement them when
+// needed. (pb) */
+// }
+// qid.vers = 0;
+// qid.type = QTFILE;
+// devdir(c, qid, up->genbuf, len, eve, 0660, dp);
+// return 1;
+//}
+//
+//static Walkqid*
+//pccardwalk(Chan *c, Chan *nc, char **name, int nname)
+//{
+// return devwalk(c, nc, name, nname, 0, 0, pccardgen);
+//}
+//
+//static int
+//pccardstat(Chan *c, uchar *db, int n)
+//{
+// return devstat(c, db, n, 0, 0, pccardgen);
+//}
+//
+//static void
+//increfp(cb_t *cb)
+//{
+// qlock(&cb->cb_refslock);
+// cb->cb_refs++;
+// qunlock(&cb->cb_refslock);
+//}
+//
+//static void
+//decrefp(cb_t *cb)
+//{
+// qlock(&cb->cb_refslock);
+// cb->cb_refs--;
+// qunlock(&cb->cb_refslock);
+//}
+//
+//static Chan*
+//pccardopen(Chan *c, int omode)
+//{
+// if (c->qid.type & QTDIR){
+// if(omode != OREAD)
+// error(Eperm);
+// } else
+// increfp(&cbslots[SLOTNO(c)]);
+// c->mode = openmode(omode);
+// c->flag |= COPEN;
+// c->offset = 0;
+// return c;
+//}
+//
+//static void
+//pccardclose(Chan *c)
+//{
+// if(c->flag & COPEN)
+// if((c->qid.type & QTDIR) == 0)
+// decrefp(&cbslots[SLOTNO(c)]);
+//}
+//
+//static long
+//pccardread(Chan *c, void *a, long n, vlong offset)
+//{
+// cb_t *cb;
+// char *buf, *p, *e;
+//
+// switch(TYPE(c)){
+// case Qdir:
+// return devdirread(c, a, n, 0, 0, pccardgen);
+//
+// case Qctl:
+// buf = p = malloc(READSTR);
+// buf[0] = 0;
+// e = p + READSTR;
+//
+// cb = &cbslots[SLOTNO(c)];
+// qlock(cb);
+// p = seprint(p, e, "slot %ld: %s; ", cb - cbslots, states[cb->cb_state]);
+//
+// switch (cb->cb_type) {
+// case -1:
+// seprint(p, e, "\n");
+// break;
+//
+// case PC32:
+// if (cb->cb_pci->bridge) {
+// Pcidev *pci = cb->cb_pci->bridge;
+// int i;
+//
+// while (pci) {
+// p = seprint(p, e, "%.4uX %.4uX; irq %d\n",
+// pci->vid, pci->did, pci->intl);
+// for (i = 0; i != Nbars; i++)
+// if (pci->mem[i].size)
+// p = seprint(p, e,
+// "\tmem[%d] %.8uX (%.8uX)\n",
+// i, pci->mem[i].bar,
+// pci->mem[i].size);
+// if (pci->rom.size)
+// p = seprint(p, e, "\tROM %.8uX (%.8uX)\n", i,
+// pci->rom.bar, pci->rom.size);
+// pci = pci->list;
+// }
+// }
+// break;
+//
+// case PC16:
+// if (cb->cb_state == SlotConfigured) {
+// pcminfo_t *pi = &cb->cb_linfo;
+//
+// p = seprint(p, e, "%s port %X; irq %d;\n",
+// pi->pi_verstr, pi->pi_port,
+// pi->pi_irq);
+// for (n = 0; n != pi->pi_nctab; n++) {
+// PCMconftab *ct;
+// int i;
+//
+// ct = &pi->pi_ctab[n];
+// p = seprint(p, e,
+// "\tconfiguration[%d] irqs %.4X; vpp %d, %d; %s\n",
+// n, ct->irqs, ct->vpp1, ct->vpp2,
+// (ct == pi->pi_defctab)? "(default);": "");
+// for (i = 0; i != ct->nio; i++)
+// if (ct->io[i].len > 0)
+// p = seprint(p, e, "\t\tio[%d] %.8lX %d\n",
+// i, ct->io[i].start, ct->io[i].len);
+// }
+// }
+// break;
+// }
+// qunlock(cb);
+//
+// n = readstr(offset, a, n, buf);
+// free(buf);
+// return n;
+// }
+// return 0;
+//}
+//
+//static long
+//pccardwrite(Chan *c, void *v, long n, vlong)
+//{
+// Rune r;
+// ulong n0;
+// int i, nf;
+// char buf[255], *field[Ncmd], *device;
+// cb_t *cb;
+//
+// n0 = n;
+// switch(TYPE(c)){
+// case Qctl:
+// cb = &cbslots[SLOTNO(c)];
+// if(n > sizeof(buf)-1) n = sizeof(buf)-1;
+// memmove(buf, v, n);
+// buf[n] = '\0';
+//
+// nf = getfields(buf, field, Ncmd, 1, " \t\n");
+// for (i = 0; i != nf; i++) {
+// if (!strcmp(field[i], "down")) {
+//
+// if (i + 1 < nf && *field[i + 1] == '#') {
+// device = field[++i];
+// device += chartorune(&r, device);
+// if ((n = devno(r, 1)) >= 0 && devtab[n]->config)
+// devtab[n]->config(0, device, nil);
+// }
+// qengine(cb, CardEjected);
+// }
+// else if (!strcmp(field[i], "power")) {
+// if ((cb->cb_regs[SocketState] & SS_CCD) == 0)
+// qengine(cb, CardDetected);
+// }
+// else
+// error(Ebadarg);
+// }
+// break;
+// }
+// return n0 - n;
+//}
+//
+//Dev pccarddevtab = {
+// 'Y',
+// "cardbus",
+//
+// devreset,
+// devinit,
+// pccardattach,
+// pccardwalk,
+// pccardstat,
+// pccardopen,
+// devcreate,
+// pccardclose,
+// pccardread,
+// devbread,
+// pccardwrite,
+// devbwrite,
+// devremove,
+// devwstat,
+//};
+
+static PCMmap *
+isamap(cb_t *cb, ulong offset, int len, int attr)
+{
+ uchar we, bit;
+ PCMmap *m, *nm;
+ pcminfo_t *pi;
+ int i;
+ ulong e;
+
+ pi = &cb->cb_linfo;
+
+ /* convert offset to granularity */
+ if(len <= 0)
+ len = 1;
+ e = ROUND(offset+len, Mgran);
+ offset &= Mmask;
+ len = e - offset;
+
+ /* look for a map that covers the right area */
+ we = rdreg(cb, Rwe);
+ bit = 1;
+ nm = 0;
+ for(m = pi->pi_mmap; m < &pi->pi_mmap[nelem(pi->pi_mmap)]; m++){
+ if((we & bit))
+ if(m->attr == attr)
+ if(offset >= m->ca && e <= m->cea){
+
+ m->ref++;
+ return m;
+ }
+ bit <<= 1;
+ if(nm == 0 && m->ref == 0)
+ nm = m;
+ }
+ m = nm;
+ if(m == 0)
+ return 0;
+
+ /* if isa space isn't big enough, free it and get more */
+ if(m->len < len){
+ if(m->isa){
+ umbfree(m->isa, m->len);
+ m->len = 0;
+ }
+ m->isa = PADDR(umbmalloc(0, len, Mgran));
+ if(m->isa == 0){
+ print("isamap: out of isa space\n");
+ return 0;
+ }
+ m->len = len;
+ }
+
+ /* set up new map */
+ m->ca = offset;
+ m->cea = m->ca + m->len;
+ m->attr = attr;
+ i = m - pi->pi_mmap;
+ bit = 1<<i;
+ wrreg(cb, Rwe, we & ~bit); /* disable map before changing it */
+ wrreg(cb, MAP(i, Mbtmlo), m->isa>>Mshift);
+ wrreg(cb, MAP(i, Mbtmhi), (m->isa>>(Mshift+8)) | F16bit);
+ wrreg(cb, MAP(i, Mtoplo), (m->isa+m->len-1)>>Mshift);
+ wrreg(cb, MAP(i, Mtophi), ((m->isa+m->len-1)>>(Mshift+8)));
+ offset -= m->isa;
+ offset &= (1<<25)-1;
+ offset >>= Mshift;
+ wrreg(cb, MAP(i, Mofflo), offset);
+ wrreg(cb, MAP(i, Moffhi), (offset>>8) | (attr ? Fregactive : 0));
+ wrreg(cb, Rwe, we | bit); /* enable map */
+ m->ref = 1;
+
+ return m;
+}
+
+static void
+isaunmap(PCMmap* m)
+{
+ m->ref--;
+}
+
+/*
+ * reading and writing card registers
+ */
+static uchar
+rdreg(cb_t *cb, int index)
+{
+ outb(cb->cb_lindex, cb->cb_lbase + index);
+ return inb(cb->cb_ldata);
+}
+
+static void
+wrreg(cb_t *cb, int index, uchar val)
+{
+ outb(cb->cb_lindex, cb->cb_lbase + index);
+ outb(cb->cb_ldata, val);
+}
+
+static int
+readc(Cisdat *cis, uchar *x)
+{
+ if(cis->cispos >= cis->cislen)
+ return 0;
+ *x = cis->cisbase[cis->cisskip*cis->cispos];
+ cis->cispos++;
+ return 1;
+}
+
+static ulong
+getlong(Cisdat *cis, int size)
+{
+ uchar c;
+ int i;
+ ulong x;
+
+ x = 0;
+ for(i = 0; i < size; i++){
+ if(readc(cis, &c) != 1)
+ break;
+ x |= c<<(i*8);
+ }
+ return x;
+}
+
+static void
+tcfig(cb_t *cb, Cisdat *cis, int )
+{
+ uchar size, rasize, rmsize;
+ uchar last;
+ pcminfo_t *pi;
+
+ if(readc(cis, &size) != 1)
+ return;
+ rasize = (size&0x3) + 1;
+ rmsize = ((size>>2)&0xf) + 1;
+ if(readc(cis, &last) != 1)
+ return;
+
+ pi = &cb->cb_linfo;
+ pi->pi_conf_addr = getlong(cis, rasize);
+ pi->pi_conf_present = getlong(cis, rmsize);
+}
+
+static void
+tvers1(cb_t *cb, Cisdat *cis, int )
+{
+ uchar c, major, minor, last;
+ int i;
+ pcminfo_t *pi;
+
+ pi = &cb->cb_linfo;
+ if(readc(cis, &major) != 1)
+ return;
+ if(readc(cis, &minor) != 1)
+ return;
+ last = 0;
+ for(i = 0; i < sizeof(pi->pi_verstr) - 1; i++){
+ if(readc(cis, &c) != 1)
+ return;
+ if(c == 0)
+ c = ';';
+ if(c == '\n')
+ c = ';';
+ if(c == 0xff)
+ break;
+ if(c == ';' && last == ';')
+ continue;
+ pi->pi_verstr[i] = c;
+ last = c;
+ }
+ pi->pi_verstr[i] = 0;
+}
+
+static ulong
+microvolt(Cisdat *cis)
+{
+ uchar c;
+ ulong microvolts;
+ ulong exp;
+
+ if(readc(cis, &c) != 1)
+ return 0;
+ exp = exponent[c&0x7];
+ microvolts = vmant[(c>>3)&0xf]*exp;
+ while(c & 0x80){
+ if(readc(cis, &c) != 1)
+ return 0;
+ switch(c){
+ case 0x7d:
+ break; /* high impedence when sleeping */
+ case 0x7e:
+ case 0x7f:
+ microvolts = 0; /* no connection */
+ break;
+ default:
+ exp /= 10;
+ microvolts += exp*(c&0x7f);
+ }
+ }
+ return microvolts;
+}
+
+static ulong
+nanoamps(Cisdat *cis)
+{
+ uchar c;
+ ulong nanoamps;
+
+ if(readc(cis, &c) != 1)
+ return 0;
+ nanoamps = exponent[c&0x7]*vmant[(c>>3)&0xf];
+ while(c & 0x80){
+ if(readc(cis, &c) != 1)
+ return 0;
+ if(c == 0x7d || c == 0x7e || c == 0x7f)
+ nanoamps = 0;
+ }
+ return nanoamps;
+}
+
+/*
+ * only nominal voltage (feature 1) is important for config,
+ * other features must read card to stay in sync.
+ */
+static ulong
+power(Cisdat *cis)
+{
+ uchar feature;
+ ulong mv;
+
+ mv = 0;
+ if(readc(cis, &feature) != 1)
+ return 0;
+ if(feature & 1)
+ mv = microvolt(cis);
+ if(feature & 2)
+ microvolt(cis);
+ if(feature & 4)
+ microvolt(cis);
+ if(feature & 8)
+ nanoamps(cis);
+ if(feature & 0x10)
+ nanoamps(cis);
+ if(feature & 0x20)
+ nanoamps(cis);
+ if(feature & 0x40)
+ nanoamps(cis);
+ return mv/1000000;
+}
+
+static ulong
+ttiming(Cisdat *cis, int scale)
+{
+ uchar unscaled;
+ ulong nanosecs;
+
+ if(readc(cis, &unscaled) != 1)
+ return 0;
+ nanosecs = (mantissa[(unscaled>>3)&0xf]*exponent[unscaled&7])/10;
+ nanosecs = nanosecs * exponent[scale];
+ return nanosecs;
+}
+
+static void
+timing(Cisdat *cis, PCMconftab *ct)
+{
+ uchar c, i;
+
+ if(readc(cis, &c) != 1)
+ return;
+ i = c&0x3;
+ if(i != 3)
+ ct->maxwait = ttiming(cis, i); /* max wait */
+ i = (c>>2)&0x7;
+ if(i != 7)
+ ct->readywait = ttiming(cis, i); /* max ready/busy wait */
+ i = (c>>5)&0x7;
+ if(i != 7)
+ ct->otherwait = ttiming(cis, i); /* reserved wait */
+}
+
+static void
+iospaces(Cisdat *cis, PCMconftab *ct)
+{
+ uchar c;
+ int i, nio;
+
+ ct->nio = 0;
+ if(readc(cis, &c) != 1)
+ return;
+
+ ct->bit16 = ((c>>5)&3) >= 2;
+ if(!(c & 0x80)){
+ ct->io[0].start = 0;
+ ct->io[0].len = 1<<(c&0x1f);
+ ct->nio = 1;
+ return;
+ }
+
+ if(readc(cis, &c) != 1)
+ return;
+
+ /*
+ * For each of the range descriptions read the
+ * start address and the length (value is length-1).
+ */
+ nio = (c&0xf)+1;
+ for(i = 0; i < nio; i++){
+ ct->io[i].start = getlong(cis, (c>>4)&0x3);
+ ct->io[i].len = getlong(cis, (c>>6)&0x3)+1;
+ }
+ ct->nio = nio;
+}
+
+static void
+irq(Cisdat *cis, PCMconftab *ct)
+{
+ uchar c;
+
+ if(readc(cis, &c) != 1)
+ return;
+ ct->irqtype = c & 0xe0;
+ if(c & 0x10)
+ ct->irqs = getlong(cis, 2);
+ else
+ ct->irqs = 1<<(c&0xf);
+ ct->irqs &= 0xDEB8; /* levels available to card */
+}
+
+static void
+memspace(Cisdat *cis, int asize, int lsize, int host)
+{
+ ulong haddress, address, len;
+
+ len = getlong(cis, lsize)*256;
+ address = getlong(cis, asize)*256;
+ USED(len, address);
+ if(host){
+ haddress = getlong(cis, asize)*256;
+ USED(haddress);
+ }
+}
+
+static void
+tentry(cb_t *cb, Cisdat *cis, int )
+{
+ uchar c, i, feature;
+ PCMconftab *ct;
+ pcminfo_t *pi;
+
+ pi = &cb->cb_linfo;
+ if(pi->pi_nctab >= nelem(pi->pi_ctab))
+ return;
+ if(readc(cis, &c) != 1)
+ return;
+ ct = &pi->pi_ctab[pi->pi_nctab++];
+
+ /* copy from last default config */
+ if(pi->pi_defctab)
+ *ct = *pi->pi_defctab;
+
+ ct->index = c & 0x3f;
+
+ /* is this the new default? */
+ if(c & 0x40)
+ pi->pi_defctab = ct;
+
+ /* memory wait specified? */
+ if(c & 0x80){
+ if(readc(cis, &i) != 1)
+ return;
+ if(i&0x80)
+ ct->memwait = 1;
+ }
+
+ if(readc(cis, &feature) != 1)
+ return;
+ switch(feature&0x3){
+ case 1:
+ ct->vpp1 = ct->vpp2 = power(cis);
+ break;
+ case 2:
+ power(cis);
+ ct->vpp1 = ct->vpp2 = power(cis);
+ break;
+ case 3:
+ power(cis);
+ ct->vpp1 = power(cis);
+ ct->vpp2 = power(cis);
+ break;
+ default:
+ break;
+ }
+ if(feature&0x4)
+ timing(cis, ct);
+ if(feature&0x8)
+ iospaces(cis, ct);
+ if(feature&0x10)
+ irq(cis, ct);
+ switch((feature>>5)&0x3){
+ case 1:
+ memspace(cis, 0, 2, 0);
+ break;
+ case 2:
+ memspace(cis, 2, 2, 0);
+ break;
+ case 3:
+ if(readc(cis, &c) != 1)
+ return;
+ for(i = 0; i <= (c&0x7); i++)
+ memspace(cis, (c>>5)&0x3, (c>>3)&0x3, c&0x80);
+ break;
+ }
+}
+
+static void
+i82365probe(cb_t *cb, int lindex, int ldata)
+{
+ uchar c, id;
+ int dev = 0; /* According to the Ricoh spec 00->3F _and_ 80->BF seem
+ to be the same socket A (ditto for B). */
+
+ outb(lindex, Rid + (dev<<7));
+ id = inb(ldata);
+ if((id & 0xf0) != 0x80)
+ return; /* not a memory & I/O card */
+ if((id & 0x0f) == 0x00)
+ return; /* no revision number, not possible */
+
+ cb->cb_lindex = lindex;
+ cb->cb_ldata = ldata;
+ cb->cb_ltype = Ti82365;
+ cb->cb_lbase = (int)(cb - cbslots) * 0x40;
+
+ switch(id){
+ case 0x82:
+ case 0x83:
+ case 0x84:
+ /* could be a cirrus */
+ outb(cb->cb_lindex, Rchipinfo + (dev<<7));
+ outb(cb->cb_ldata, 0);
+ c = inb(cb->cb_ldata);
+ if((c & 0xc0) != 0xc0)
+ break;
+ c = inb(cb->cb_ldata);
+ if((c & 0xc0) != 0x00)
+ break;
+ if(c & 0x20){
+ cb->cb_ltype = Tpd6720;
+ } else {
+ cb->cb_ltype = Tpd6710;
+ }
+ break;
+ }
+
+ /* if it's not a Cirrus, it could be a Vadem... */
+ if(cb->cb_ltype == Ti82365){
+ /* unlock the Vadem extended regs */
+ outb(cb->cb_lindex, 0x0E + (dev<<7));
+ outb(cb->cb_lindex, 0x37 + (dev<<7));
+
+ /* make the id register show the Vadem id */
+ outb(cb->cb_lindex, 0x3A + (dev<<7));
+ c = inb(cb->cb_ldata);
+ outb(cb->cb_ldata, c|0xC0);
+ outb(cb->cb_lindex, Rid + (dev<<7));
+ c = inb(cb->cb_ldata);
+ if(c & 0x08)
+ cb->cb_ltype = Tvg46x;
+
+ /* go back to Intel compatible id */
+ outb(cb->cb_lindex, 0x3A + (dev<<7));
+ c = inb(cb->cb_ldata);
+ outb(cb->cb_ldata, c & ~0xC0);
+ }
+}
+
+static int
+vcode(int volt)
+{
+ switch(volt){
+ case 5:
+ return 1;
+ case 12:
+ return 2;
+ default:
+ return 0;
+ }
+}
+
diff --git a/os/boot/pc/devsd.c b/os/boot/pc/devsd.c
new file mode 100644
index 00000000..54bfd835
--- /dev/null
+++ b/os/boot/pc/devsd.c
@@ -0,0 +1,617 @@
+/*
+ * Storage Device.
+ */
+#include "u.h"
+#include "mem.h"
+#include "lib.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "error.h"
+
+#include "sd.h"
+#include "fs.h"
+
+#define parttrace 0
+
+
+extern SDifc* sdifc[];
+
+static SDev* sdlist;
+static SDunit** sdunit;
+static int sdnunit;
+static int _sdmask;
+static int cdmask;
+static int sdmask;
+
+enum {
+ Rawcmd,
+ Rawdata,
+ Rawstatus,
+};
+
+void
+sdaddpart(SDunit* unit, char* name, ulong start, ulong end)
+{
+ SDpart *pp;
+ int i, partno;
+
+ if(parttrace)
+ print("add %d %s %s %ld %ld\n", unit->npart, unit->name, name, start, end);
+ /*
+ * Check name not already used
+ * and look for a free slot.
+ */
+ if(unit->part != nil){
+ partno = -1;
+ for(i = 0; i < SDnpart; i++){
+ pp = &unit->part[i];
+ if(!pp->valid){
+ if(partno == -1)
+ partno = i;
+ break;
+ }
+ if(strcmp(name, pp->name) == 0){
+ if(pp->start == start && pp->end == end){
+ if(parttrace)
+ print("already present\n");
+ return;
+ }
+ }
+ }
+ }else{
+ if((unit->part = malloc(sizeof(SDpart)*SDnpart)) == nil){
+ if(parttrace)
+ print("malloc failed\n");
+ return;
+ }
+ partno = 0;
+ }
+
+ /*
+ * Check there is a free slot and size and extent are valid.
+ */
+ if(partno == -1 || start > end || end > unit->sectors){
+ print("cannot add %s!%s [%lud,%lud) to disk [0,%lud): %s\n",
+ unit->name, name, start, end, unit->sectors,
+ partno==-1 ? "no free partitions" : "partition boundaries out of range");
+ return;
+ }
+ pp = &unit->part[partno];
+ pp->start = start;
+ pp->end = end;
+ strncpy(pp->name, name, NAMELEN);
+ pp->valid = 1;
+ unit->npart++;
+}
+
+void
+sddelpart(SDunit* unit, char* name)
+{
+ int i;
+ SDpart *pp;
+
+ if(parttrace)
+ print("del %d %s %s\n", unit->npart, unit->name, name);
+ /*
+ * Look for the partition to delete.
+ * Can't delete if someone still has it open.
+ * If it's the last valid partition zap the
+ * whole table.
+ */
+ pp = unit->part;
+ for(i = 0; i < SDnpart; i++){
+ if(strncmp(name, pp->name, NAMELEN) == 0)
+ break;
+ pp++;
+ }
+ if(i >= SDnpart)
+ return;
+ pp->valid = 0;
+
+ unit->npart--;
+ if(unit->npart == 0){
+ free(unit->part);
+ unit->part = nil;
+ }
+}
+
+static int
+sdinitpart(SDunit* unit)
+{
+ unit->sectors = unit->secsize = 0;
+ unit->npart = 0;
+ if(unit->part){
+ free(unit->part);
+ unit->part = nil;
+ }
+
+ if(unit->inquiry[0] & 0xC0)
+ return 0;
+ switch(unit->inquiry[0] & 0x1F){
+ case 0x00: /* DA */
+ case 0x04: /* WORM */
+ case 0x05: /* CD-ROM */
+ case 0x07: /* MO */
+ break;
+ default:
+ return 0;
+ }
+
+ if(unit->dev->ifc->online == nil || unit->dev->ifc->online(unit) == 0)
+ return 0;
+ sdaddpart(unit, "data", 0, unit->sectors);
+ return 1;
+}
+
+static SDunit*
+sdgetunit(SDev* sdev, int subno)
+{
+ int index;
+ SDunit *unit;
+
+ /*
+ * Associate a unit with a given device and sub-unit
+ * number on that device.
+ * The device will be probed if it has not already been
+ * successfully accessed.
+ */
+ qlock(&sdqlock);
+ index = sdev->index+subno;
+ unit = sdunit[index];
+ if(unit == nil){
+ if((unit = malloc(sizeof(SDunit))) == nil){
+ qunlock(&sdqlock);
+ return nil;
+ }
+
+ if(sdev->enabled == 0 && sdev->ifc->enable)
+ sdev->ifc->enable(sdev);
+ sdev->enabled = 1;
+
+ snprint(unit->name, NAMELEN, "sd%c%d", sdev->idno, subno);
+ unit->subno = subno;
+ unit->dev = sdev;
+
+ /*
+ * No need to lock anything here as this is only
+ * called before the unit is made available in the
+ * sdunit[] array.
+ */
+ if(unit->dev->ifc->verify(unit) == 0){
+ qunlock(&sdqlock);
+ free(unit);
+ return nil;
+ }
+ sdunit[index] = unit;
+ }
+ qunlock(&sdqlock);
+
+ return unit;
+}
+
+static SDunit*
+sdindex2unit(int index)
+{
+ SDev *sdev;
+
+ /*
+ * Associate a unit with a given index into the top-level
+ * device directory.
+ * The device will be probed if it has not already been
+ * successfully accessed.
+ */
+ for(sdev = sdlist; sdev != nil; sdev = sdev->next){
+ if(index >= sdev->index && index < sdev->index+sdev->nunit)
+ return sdgetunit(sdev, index-sdev->index);
+ }
+
+ return nil;
+}
+
+static void
+_sddetach(void)
+{
+ SDev *sdev;
+
+ for(sdev = sdlist; sdev != nil; sdev = sdev->next){
+ if(sdev->enabled == 0)
+ continue;
+ if(sdev->ifc->disable)
+ sdev->ifc->disable(sdev);
+ sdev->enabled = 0;
+ }
+}
+
+static int
+_sdinit(void)
+{
+ ulong m;
+ int i;
+ SDev *sdev, *tail;
+ SDunit *unit;
+
+ /*
+ * Probe all configured controllers and make a list
+ * of devices found, accumulating a possible maximum number
+ * of units attached and marking each device with an index
+ * into the linear top-level directory array of units.
+ */
+ tail = nil;
+ for(i = 0; sdifc[i] != nil; i++){
+ if((sdev = sdifc[i]->pnp()) == nil)
+ continue;
+ if(sdlist != nil)
+ tail->next = sdev;
+ else
+ sdlist = sdev;
+ for(tail = sdev; tail->next != nil; tail = tail->next){
+ sdev->index = sdnunit;
+ sdnunit += tail->nunit;
+ }
+ tail->index = sdnunit;
+ sdnunit += tail->nunit;
+ }
+
+ /*
+ * Legacy and option code goes here. This will be hard...
+ */
+
+ /*
+ * The maximum number of possible units is known, allocate
+ * placeholders for their datastructures; the units will be
+ * probed and structures allocated when attached.
+ * Allocate controller names for the different types.
+ */
+ if(sdnunit == 0)
+ return 0;
+ if((sdunit = malloc(sdnunit*sizeof(SDunit*))) == nil)
+ return 0;
+ sddetach = _sddetach;
+
+ for(i = 0; sdifc[i] != nil; i++){
+ if(sdifc[i]->id)
+ sdifc[i]->id(sdlist);
+ }
+
+ m = 0;
+ cdmask = sdmask = 0;
+ for(i=0; i<sdnunit && i < 32; i++) {
+ unit = sdindex2unit(i);
+ if(unit == nil)
+ continue;
+ sdinitpart(unit);
+ partition(unit);
+ if(unit->npart > 0){ /* BUG */
+ if((unit->inquiry[0] & 0x1F) == 0x05)
+ cdmask |= (1<<i);
+ else
+ sdmask |= (1<<i);
+ m |= (1<<i);
+ }
+ }
+
+//notesdinfo();
+ _sdmask = m;
+ return m;
+}
+
+int
+cdinit(void)
+{
+ if(sdnunit == 0)
+ _sdinit();
+ return cdmask;
+}
+
+int
+sdinit(void)
+{
+ if(sdnunit == 0)
+ _sdinit();
+ return sdmask;
+}
+
+void
+sdinitdev(int i, char *s)
+{
+ SDunit *unit;
+
+ unit = sdindex2unit(i);
+ strcpy(s, unit->name);
+}
+
+void
+sdprintdevs(int i)
+{
+ char *s;
+ SDunit *unit;
+
+ unit = sdindex2unit(i);
+ for(i=0; i<unit->npart; i++){
+ s = unit->part[i].name;
+ if(strncmp(s, "dos", 3) == 0
+ || strncmp(s, "9fat", 4) == 0
+ || strncmp(s, "fs", 2) == 0)
+ print(" %s!%s", unit->name, s);
+ }
+}
+
+SDpart*
+sdfindpart(SDunit *unit, char *name)
+{
+ int i;
+
+ if(parttrace)
+ print("findpart %d %s %s\t\n", unit->npart, unit->name, name);
+ for(i=0; i<unit->npart; i++) {
+ if(parttrace)
+ print("%s...", unit->part[i].name);
+ if(strcmp(unit->part[i].name, name) == 0){
+ if(parttrace)
+ print("\n");
+ return &unit->part[i];
+ }
+ }
+ if(parttrace)
+ print("not found\n");
+ return nil;
+}
+
+typedef struct Scsicrud Scsicrud;
+struct Scsicrud {
+ Fs fs;
+ vlong offset;
+ SDunit *unit;
+ SDpart *part;
+};
+
+long
+sdread(Fs *vcrud, void *v, long n)
+{
+ Scsicrud *crud;
+ long x;
+
+ crud = (Scsicrud*)vcrud;
+ x = sdbio(crud->unit, crud->part, v, n, crud->offset);
+ if(x > 0)
+ crud->offset += x;
+ return x;
+}
+
+vlong
+sdseek(Fs *vcrud, vlong seek)
+{
+ ((Scsicrud*)vcrud)->offset = seek;
+ return seek;
+}
+
+void*
+sdgetfspart(int i, char *s, int chatty)
+{
+ SDunit *unit;
+ SDpart *p;
+ Scsicrud *crud;
+
+ if(cdmask&(1<<i)){
+ if(strcmp(s, "cdboot") != 0)
+ return nil;
+ }else if(sdmask&(1<<i)){
+ if(strcmp(s, "cdboot") == 0)
+ return nil;
+ }
+
+ unit = sdindex2unit(i);
+ if((p = sdfindpart(unit, s)) == nil){
+ if(chatty)
+ print("unknown partition %s!%s\n", unit->name, s);
+ return nil;
+ }
+ if(p->crud == nil) {
+ crud = malloc(sizeof(Scsicrud));
+ crud->fs.dev = i;
+ crud->fs.diskread = sdread;
+ crud->fs.diskseek = sdseek;
+ // crud->start = 0;
+ crud->unit = unit;
+ crud->part = p;
+ if(dosinit(&crud->fs) < 0 && dosinit(&crud->fs) < 0 && kfsinit(&crud->fs) < 0){
+ if(chatty)
+ print("partition %s!%s does not contain a DOS or KFS file system\n",
+ unit->name, s);
+ return nil;
+ }
+ p->crud = crud;
+ }
+ return p->crud;
+}
+
+/*
+ * Leave partitions around for devsd to pick up.
+ * (Needed by boot process; more extensive
+ * partitioning is done by termrc or cpurc).
+ */
+void
+sdaddconf(int i)
+{
+ SDunit *unit;
+ SDpart *pp;
+
+ unit = sdindex2unit(i);
+
+ /*
+ * If there were no partitions (just data and partition), don't bother.
+ */
+ if(unit->npart<= 1 || (unit->npart==2 && strcmp(unit->part[1].name, "partition")==0))
+ return;
+
+ addconf("%spart=", unit->name);
+ for(i=1, pp=&unit->part[i]; i<unit->npart; i++, pp++) /* skip 0, which is "data" */
+ addconf("%s%s %ld %ld", i==1 ? "" : "/", pp->name,
+ pp->start, pp->end);
+ addconf("\n");
+}
+
+int
+sdboot(int dev, char *pname, Boot *b)
+{
+ char *file;
+ Fs *fs;
+
+ if((file = strchr(pname, '!')) == nil) {
+ print("syntax is sdC0!partition!file\n");
+ return -1;
+ }
+ *file++ = '\0';
+
+ fs = sdgetfspart(dev, pname, 1);
+ if(fs == nil)
+ return -1;
+
+ return fsboot(fs, file, b);
+}
+
+long
+sdbio(SDunit *unit, SDpart *pp, void* va, long len, vlong off)
+{
+ long l;
+ ulong bno, max, nb, offset;
+ static uchar *b;
+ char *a;
+ static ulong bsz;
+
+ a = va;
+memset(a, 0xDA, len);
+ qlock(&unit->ctl);
+ if(unit->changed){
+ qunlock(&unit->ctl);
+ return 0;
+ }
+
+ /*
+ * Check the request is within bounds.
+ * Removeable drives are locked throughout the I/O
+ * in case the media changes unexpectedly.
+ * Non-removeable drives are not locked during the I/O
+ * to allow the hardware to optimise if it can; this is
+ * a little fast and loose.
+ * It's assumed that non-removable media parameters
+ * (sectors, secsize) can't change once the drive has
+ * been brought online.
+ */
+ bno = (off/unit->secsize) + pp->start;
+ nb = ((off+len+unit->secsize-1)/unit->secsize) + pp->start - bno;
+ max = SDmaxio/unit->secsize;
+ if(nb > max)
+ nb = max;
+ if(bno+nb > pp->end)
+ nb = pp->end - bno;
+ if(bno >= pp->end || nb == 0){
+ qunlock(&unit->ctl);
+ return 0;
+ }
+ if(!(unit->inquiry[1] & 0x80))
+ qunlock(&unit->ctl);
+
+ if(bsz < nb*unit->secsize){
+ b = malloc(nb*unit->secsize);
+ bsz = nb*unit->secsize;
+ }
+// b = sdmalloc(nb*unit->secsize);
+// if(b == nil)
+// return 0;
+
+ offset = off%unit->secsize;
+ if((l = unit->dev->ifc->bio(unit, 0, 0, b, nb, bno)) < 0) {
+// sdfree(b);
+ return 0;
+ }
+
+ if(l < offset)
+ len = 0;
+ else if(len > l - offset)
+ len = l - offset;
+ if(len)
+ memmove(a, b+offset, len);
+// sdfree(b);
+
+ if(unit->inquiry[1] & 0x80)
+ qunlock(&unit->ctl);
+
+ return len;
+}
+
+#ifdef DMA
+long
+sdrio(SDreq *r, void* a, long n)
+{
+ if(n >= SDmaxio || n < 0)
+ return 0;
+
+ r->data = nil;
+ if(n){
+ if((r->data = malloc(n)) == nil)
+ return 0;
+ if(r->write)
+ memmove(r->data, a, n);
+ }
+ r->dlen = n;
+
+ if(r->unit->dev->ifc->rio(r) != SDok){
+// cgascreenputs("1", 1);
+ if(r->data != nil){
+ sdfree(r->data);
+ r->data = nil;
+ }
+ return 0;
+ }
+// cgascreenputs("2", 1);
+
+ if(!r->write && r->rlen > 0)
+ memmove(a, r->data, r->rlen);
+// cgascreenputs("3", 1);
+ if(r->data != nil){
+ sdfree(r->data);
+ r->data = nil;
+ }
+
+// cgascreenputs("4", 1);
+ return r->rlen;
+}
+#endif /* DMA */
+
+void
+sleep(void*, int (*fn)(void*), void *v)
+{
+ int x;
+ x = spllo();
+ while(!fn(v))
+ ;
+ splx(x);
+ return;
+}
+
+void
+tsleep(void*, int (*fn)(void*), void *v, int msec)
+{
+ int x;
+ ulong start;
+
+ x = spllo();
+ for(start = m->ticks; TK2MS(m->ticks - start) < msec
+ && !fn(v); )
+ ;
+ splx(x);
+ return;
+}
+
+void*
+sdmalloc(void *p, ulong sz)
+{
+ if(p != nil) {
+ memset(p, 0, sz);
+ return p;
+ }
+ return malloc(sz);
+}
diff --git a/os/boot/pc/dma.c b/os/boot/pc/dma.c
new file mode 100644
index 00000000..edaaa2fe
--- /dev/null
+++ b/os/boot/pc/dma.c
@@ -0,0 +1,245 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+typedef struct DMAport DMAport;
+typedef struct DMA DMA;
+typedef struct DMAxfer DMAxfer;
+
+enum
+{
+ /*
+ * the byte registers for DMA0 are all one byte apart
+ */
+ Dma0= 0x00,
+ Dma0status= Dma0+0x8, /* status port */
+ Dma0reset= Dma0+0xD, /* reset port */
+
+ /*
+ * the byte registers for DMA1 are all two bytes apart (why?)
+ */
+ Dma1= 0xC0,
+ Dma1status= Dma1+2*0x8, /* status port */
+ Dma1reset= Dma1+2*0xD, /* reset port */
+};
+
+/*
+ * state of a dma transfer
+ */
+struct DMAxfer
+{
+ ulong bpa; /* bounce buffer physical address */
+ void* bva; /* bounce buffer virtual address */
+ void* va; /* virtual address destination/src */
+ long len; /* bytes to be transferred */
+ int isread;
+};
+
+/*
+ * the dma controllers. the first half of this structure specifies
+ * the I/O ports used by the DMA controllers.
+ */
+struct DMAport
+{
+ uchar addr[4]; /* current address (4 channels) */
+ uchar count[4]; /* current count (4 channels) */
+ uchar page[4]; /* page registers (4 channels) */
+ uchar cmd; /* command status register */
+ uchar req; /* request registers */
+ uchar sbm; /* single bit mask register */
+ uchar mode; /* mode register */
+ uchar cbp; /* clear byte pointer */
+ uchar mc; /* master clear */
+ uchar cmask; /* clear mask register */
+ uchar wam; /* write all mask register bit */
+};
+
+struct DMA
+{
+ DMAport;
+ int shift;
+ Lock;
+ DMAxfer x[4];
+};
+
+DMA dma[2] = {
+ { 0x00, 0x02, 0x04, 0x06,
+ 0x01, 0x03, 0x05, 0x07,
+ 0x87, 0x83, 0x81, 0x82,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0 },
+
+ { 0xc0, 0xc4, 0xc8, 0xcc,
+ 0xc2, 0xc6, 0xca, 0xce,
+ 0x8f, 0x8b, 0x89, 0x8a,
+ 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
+ 1 },
+};
+
+/*
+ * DMA must be in the first 16MB. This gets called early by the
+ * initialisation routines of any devices which require DMA to ensure
+ * the allocated bounce buffers are below the 16MB limit.
+ */
+void
+dmainit(int chan)
+{
+ DMA *dp;
+ DMAxfer *xp;
+ ulong v;
+ static int once;
+
+ if(once == 0){
+// if(ioalloc(0x00, 0x10, 0, "dma") < 0
+// || ioalloc(0x80, 0x10, 0, "dma") < 0
+// || ioalloc(0xd0, 0x10, 0, "dma") < 0)
+// panic("dmainit");
+ outb(dma[0].mc, 0);
+ outb(dma[1].mc, 0);
+ outb(dma[0].cmask, 0);
+ outb(dma[1].cmask, 0);
+ outb(dma[1].mode, 0xC0);
+ once = 1;
+ }
+
+ dp = &dma[(chan>>2)&1];
+ chan = chan & 3;
+ xp = &dp->x[chan];
+ if(xp->bva != nil)
+ return;
+
+ v = (ulong)xalloc(BY2PG+BY2PG);
+ if(v == 0 || PADDR(v) >= 16*MB){
+ print("dmainit: chan %d: 0x%luX out of range\n", chan, v);
+ xfree((void*)v);
+ v = 0;
+ }
+ xp->bva = (void*)ROUND(v, BY2PG);
+ xp->bpa = PADDR(xp->bva);
+ xp->len = 0;
+ xp->isread = 0;
+}
+
+/*
+ * setup a dma transfer. if the destination is not in kernel
+ * memory, allocate a page for the transfer.
+ *
+ * we assume BIOS has set up the command register before we
+ * are booted.
+ *
+ * return the updated transfer length (we can't transfer across 64k
+ * boundaries)
+ */
+long
+dmasetup(int chan, void *va, long len, int isread)
+{
+ DMA *dp;
+ ulong pa;
+ uchar mode;
+ DMAxfer *xp;
+
+ dp = &dma[(chan>>2)&1];
+ chan = chan & 3;
+ xp = &dp->x[chan];
+
+ /*
+ * if this isn't kernel memory or crossing 64k boundary or above 16 meg
+ * use the allocated low memory page.
+ */
+ pa = PADDR(va);
+ if((((ulong)va)&0xF0000000) != KZERO
+ || (pa&0xFFFF0000) != ((pa+len)&0xFFFF0000)
+ || pa > 16*MB) {
+ if(xp->bva == nil)
+ return -1;
+ if(len > BY2PG)
+ len = BY2PG;
+ if(!isread)
+ memmove(xp->bva, va, len);
+ xp->va = va;
+ xp->len = len;
+ xp->isread = isread;
+ pa = xp->bpa;
+ }
+ else
+ xp->len = 0;
+
+ /*
+ * this setup must be atomic
+ */
+ ilock(dp);
+ mode = (isread ? 0x44 : 0x48) | chan;
+ outb(dp->mode, mode); /* single mode dma (give CPU a chance at mem) */
+ outb(dp->page[chan], pa>>16);
+ outb(dp->cbp, 0); /* set count & address to their first byte */
+ outb(dp->addr[chan], pa>>dp->shift); /* set address */
+ outb(dp->addr[chan], pa>>(8+dp->shift));
+ outb(dp->count[chan], (len>>dp->shift)-1); /* set count */
+ outb(dp->count[chan], ((len>>dp->shift)-1)>>8);
+ outb(dp->sbm, chan); /* enable the channel */
+ iunlock(dp);
+
+ return len;
+}
+
+int
+dmadone(int chan)
+{
+ DMA *dp;
+
+ dp = &dma[(chan>>2)&1];
+ chan = chan & 3;
+
+ return inb(dp->cmd) & (1<<chan);
+}
+
+/*
+ * this must be called after a dma has been completed.
+ *
+ * if a page has been allocated for the dma,
+ * copy the data into the actual destination
+ * and free the page.
+ */
+void
+dmaend(int chan)
+{
+ DMA *dp;
+ DMAxfer *xp;
+
+ dp = &dma[(chan>>2)&1];
+ chan = chan & 3;
+
+ /*
+ * disable the channel
+ */
+ ilock(dp);
+ outb(dp->sbm, 4|chan);
+ iunlock(dp);
+
+ xp = &dp->x[chan];
+ if(xp->len == 0 || !xp->isread)
+ return;
+
+ /*
+ * copy out of temporary page
+ */
+ memmove(xp->va, xp->bva, xp->len);
+ xp->len = 0;
+}
+
+/*
+int
+dmacount(int chan)
+{
+ int retval;
+ DMA *dp;
+
+ dp = &dma[(chan>>2)&1];
+ outb(dp->cbp, 0);
+ retval = inb(dp->count[chan]);
+ retval |= inb(dp->count[chan]) << 8;
+ return((retval<<dp->shift)+1);
+}
+ */
diff --git a/os/boot/pc/dosboot.c b/os/boot/pc/dosboot.c
new file mode 100644
index 00000000..9192a5c8
--- /dev/null
+++ b/os/boot/pc/dosboot.c
@@ -0,0 +1,582 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "fs.h"
+
+struct Dosboot{
+ uchar magic[3];
+ uchar version[8];
+ uchar sectsize[2];
+ uchar clustsize;
+ uchar nresrv[2];
+ uchar nfats;
+ uchar rootsize[2];
+ uchar volsize[2];
+ uchar mediadesc;
+ uchar fatsize[2];
+ uchar trksize[2];
+ uchar nheads[2];
+ uchar nhidden[4];
+ uchar bigvolsize[4];
+/* fat 32 */
+ uchar bigfatsize[4];
+ uchar extflags[2];
+ uchar fsversion[2];
+ uchar rootdirstartclust[4];
+ uchar fsinfosect[2];
+ uchar backupbootsect[2];
+/* ???
+ uchar driveno;
+ uchar reserved0;
+ uchar bootsig;
+ uchar volid[4];
+ uchar label[11];
+ uchar reserved1[8];
+*/
+};
+
+struct Dosdir{
+ uchar name[8];
+ uchar ext[3];
+ uchar attr;
+ uchar lowercase;
+ uchar hundredth;
+ uchar ctime[2];
+ uchar cdate[2];
+ uchar adate[2];
+ uchar highstart[2];
+ uchar mtime[2];
+ uchar mdate[2];
+ uchar start[2];
+ uchar length[4];
+};
+
+#define DOSRONLY 0x01
+#define DOSHIDDEN 0x02
+#define DOSSYSTEM 0x04
+#define DOSVLABEL 0x08
+#define DOSDIR 0x10
+#define DOSARCH 0x20
+
+/*
+ * predeclared
+ */
+static void bootdump(Dosboot*);
+static void setname(Dosfile*, char*);
+
+/*
+ * debugging
+ */
+#define chatty 0
+#define chat if(chatty)print
+
+/*
+ * block io buffers
+ */
+enum
+{
+ Nbio= 16,
+};
+typedef struct Clustbuf Clustbuf;
+struct Clustbuf
+{
+ int age;
+ long sector;
+ uchar *iobuf;
+ Dos *dos;
+ int size;
+};
+Clustbuf bio[Nbio];
+
+/*
+ * get an io block from an io buffer
+ */
+Clustbuf*
+getclust(Dos *dos, long sector)
+{
+ Fs *fs;
+ Clustbuf *p, *oldest;
+ int size;
+
+ chat("getclust @ %ld\n", sector);
+
+ /*
+ * if we have it, just return it
+ */
+ for(p = bio; p < &bio[Nbio]; p++){
+ if(sector == p->sector && dos == p->dos){
+ p->age = m->ticks;
+ chat("getclust %ld in cache\n", sector);
+ return p;
+ }
+ }
+
+ /*
+ * otherwise, reuse the oldest entry
+ */
+ oldest = bio;
+ for(p = &bio[1]; p < &bio[Nbio]; p++){
+ if(p->age <= oldest->age)
+ oldest = p;
+ }
+ p = oldest;
+
+ /*
+ * make sure the buffer is big enough
+ */
+ size = dos->clustsize*dos->sectsize;
+ if(p->iobuf==0 || p->size < size)
+ p->iobuf = ialloc(size, 0);
+ p->size = size;
+
+ /*
+ * read in the cluster
+ */
+ fs = (Fs*)dos;
+ chat("getclust addr %lud %p %p %p\n", (ulong)((sector+dos->start)*(vlong)dos->sectsize),
+ fs, fs->diskseek, fs->diskread);
+ if(fs->diskseek(fs, (sector+dos->start)*(vlong)dos->sectsize) < 0){
+ chat("can't seek block\n");
+ return 0;
+ }
+ if(fs->diskread(fs, p->iobuf, size) != size){
+ chat("can't read block\n");
+ return 0;
+ }
+
+ p->age = m->ticks;
+ p->dos = dos;
+ p->sector = sector;
+ chat("getclust %ld read\n", sector);
+ return p;
+}
+
+/*
+ * walk the fat one level ( n is a current cluster number ).
+ * return the new cluster number or -1 if no more.
+ */
+static long
+fatwalk(Dos *dos, int n)
+{
+ ulong k, sect;
+ Clustbuf *p;
+ int o;
+
+ chat("fatwalk %d\n", n);
+
+ if(n < 2 || n >= dos->fatclusters)
+ return -1;
+
+ switch(dos->fatbits){
+ case 12:
+ k = (3*n)/2; break;
+ case 16:
+ k = 2*n; break;
+ case 32:
+ k = 4*n; break;
+ default:
+ return -1;
+ }
+ if(k >= dos->fatsize*dos->sectsize)
+ panic("getfat");
+
+ sect = (k/(dos->sectsize*dos->clustsize))*dos->clustsize + dos->fataddr;
+ o = k%(dos->sectsize*dos->clustsize);
+ p = getclust(dos, sect);
+ k = p->iobuf[o++];
+ if(o >= dos->sectsize*dos->clustsize){
+ p = getclust(dos, sect+dos->clustsize);
+ o = 0;
+ }
+ k |= p->iobuf[o++]<<8;
+ if(dos->fatbits == 12){
+ if(n&1)
+ k >>= 4;
+ else
+ k &= 0xfff;
+ if(k >= 0xff8)
+ k = -1;
+ }
+ else if (dos->fatbits == 32){
+ if(o >= dos->sectsize*dos->clustsize){
+ p = getclust(dos, sect+dos->clustsize);
+ o = 0;
+ }
+ k |= p->iobuf[o++]<<16;
+ k |= p->iobuf[o]<<24;
+ if (k >= 0xfffffff8)
+ k = -1;
+ }
+ else
+ k = k < 0xfff8 ? k : -1;
+ chat("fatwalk %d -> %lud\n", n, k);
+ return k;
+}
+
+/*
+ * map a file's logical cluster address to a physical sector address
+ */
+static long
+fileaddr(Dosfile *fp, long ltarget)
+{
+ Dos *dos = fp->dos;
+ long l;
+ long p;
+
+ chat("fileaddr %8.8s %ld\n", fp->name, ltarget);
+ /*
+ * root directory is contiguous and easy (unless FAT32)
+ */
+ if(fp->pstart == 0 && dos->rootsize != 0) {
+ if(ltarget*dos->sectsize*dos->clustsize >= dos->rootsize*sizeof(Dosdir))
+ return -1;
+ l = dos->rootaddr + ltarget*dos->clustsize;
+ chat("fileaddr %ld -> %ld\n", ltarget, l);
+ return l;
+ }
+
+ /*
+ * anything else requires a walk through the fat
+ */
+ if(ltarget >= fp->lcurrent && fp->pcurrent){
+ /* start at the currrent point */
+ l = fp->lcurrent;
+ p = fp->pcurrent;
+ } else {
+ /* go back to the beginning */
+ l = 0;
+ p = fp->pstart;
+ }
+ while(l != ltarget){
+ /* walk the fat */
+ p = fatwalk(dos, p);
+ if(p < 0)
+ return -1;
+ l++;
+ }
+ fp->lcurrent = l;
+ fp->pcurrent = p;
+
+ /*
+ * clusters start at 2 instead of 0 (why? - presotto)
+ */
+ l = dos->dataaddr + (p-2)*dos->clustsize;
+ chat("fileaddr %ld -> %ld\n", ltarget, l);
+ return l;
+}
+
+/*
+ * read from a dos file
+ */
+long
+dosread(Dosfile *fp, void *a, long n)
+{
+ long addr;
+ long rv;
+ int i;
+ int off;
+ Clustbuf *p;
+ uchar *from, *to;
+
+ if((fp->attr & DOSDIR) == 0){
+ if(fp->offset >= fp->length)
+ return 0;
+ if(fp->offset+n > fp->length)
+ n = fp->length - fp->offset;
+ }
+
+ to = a;
+ for(rv = 0; rv < n; rv+=i){
+ /*
+ * read the cluster
+ */
+ addr = fileaddr(fp, fp->offset/fp->dos->clustbytes);
+ if(addr < 0)
+ return -1;
+ p = getclust(fp->dos, addr);
+ if(p == 0)
+ return -1;
+
+ /*
+ * copy the bytes we need
+ */
+ off = fp->offset % fp->dos->clustbytes;
+ from = &p->iobuf[off];
+ i = n - rv;
+ if(i > fp->dos->clustbytes - off)
+ i = fp->dos->clustbytes - off;
+ memmove(to, from, i);
+ to += i;
+ fp->offset += i;
+ }
+
+ return rv;
+}
+
+/*
+ * walk a directory returns
+ * -1 if something went wrong
+ * 0 if not found
+ * 1 if found
+ */
+int
+doswalk(File *f, char *name)
+{
+ Dosdir d;
+ long n;
+ Dosfile *file;
+
+ chat("doswalk %s\n", name);
+
+ file = &f->dos;
+
+ if((file->attr & DOSDIR) == 0){
+ chat("walking non-directory!\n");
+ return -1;
+ }
+
+ setname(file, name);
+
+ file->offset = 0; /* start at the beginning */
+ while((n = dosread(file, &d, sizeof(d))) == sizeof(d)){
+ chat("comparing to %8.8s.%3.3s\n", (char*)d.name, (char*)d.ext);
+ if(memcmp(file->name, d.name, sizeof(d.name)) != 0)
+ continue;
+ if(memcmp(file->ext, d.ext, sizeof(d.ext)) != 0)
+ continue;
+ if(d.attr & DOSVLABEL){
+ chat("%8.8s.%3.3s is a LABEL\n", (char*)d.name, (char*)d.ext);
+ continue;
+ }
+ file->attr = d.attr;
+ file->pstart = GSHORT(d.start);
+ if (file->dos->fatbits == 32)
+ file->pstart |= GSHORT(d.highstart) << 16;
+ file->length = GLONG(d.length);
+ file->pcurrent = 0;
+ file->lcurrent = 0;
+ file->offset = 0;
+ return 1;
+ }
+ return n >= 0 ? 0 : -1;
+}
+
+/*
+ * instructions that boot blocks can start with
+ */
+#define JMPSHORT 0xeb
+#define JMPNEAR 0xe9
+
+/*
+ * read in a segment
+ */
+long
+dosreadseg(File *f, void *va, long len)
+{
+ char *a;
+ long n, sofar;
+ Dosfile *fp;
+
+ fp = &f->dos;
+ a = va;
+ for(sofar = 0; sofar < len; sofar += n){
+ n = 8*1024;
+ if(len - sofar < n)
+ n = len - sofar;
+ n = dosread(fp, a + sofar, n);
+ if(n <= 0)
+ break;
+ print(".");
+ }
+ return sofar;
+}
+
+int
+dosinit(Fs *fs)
+{
+ Clustbuf *p;
+ Dosboot *b;
+ int i;
+ Dos *dos;
+ Dosfile *root;
+
+chat("dosinit0 %p %p %p\n", fs, fs->diskseek, fs->diskread);
+
+ dos = &fs->dos;
+ /* defaults till we know better */
+ dos->sectsize = 512;
+ dos->clustsize = 1;
+
+ /* get first sector */
+ p = getclust(dos, 0);
+ if(p == 0){
+ chat("can't read boot block\n");
+ return -1;
+ }
+
+chat("dosinit0a\n");
+
+ p->dos = 0;
+ b = (Dosboot *)p->iobuf;
+ if(b->magic[0] != JMPNEAR && (b->magic[0] != JMPSHORT || b->magic[2] != 0x90)){
+ chat("no dos file system %x %x %x %x\n",
+ b->magic[0], b->magic[1], b->magic[2], b->magic[3]);
+ return -1;
+ }
+
+ if(chatty)
+ bootdump(b);
+
+ if(b->clustsize == 0) {
+unreasonable:
+ if(chatty){
+ print("unreasonable FAT BPB: ");
+ for(i=0; i<3+8+2+1; i++)
+ print(" %.2ux", p->iobuf[i]);
+ print("\n");
+ }
+ return -1;
+ }
+
+chat("dosinit1\n");
+
+ /*
+ * Determine the systems' wondrous properties.
+ * There are heuristics here, but there's no real way
+ * of knowing if this is a reasonable FAT.
+ */
+ dos->fatbits = 0;
+ dos->sectsize = GSHORT(b->sectsize);
+ if(dos->sectsize & 0xFF)
+ goto unreasonable;
+ dos->clustsize = b->clustsize;
+ dos->clustbytes = dos->sectsize*dos->clustsize;
+ dos->nresrv = GSHORT(b->nresrv);
+ dos->nfats = b->nfats;
+ dos->fatsize = GSHORT(b->fatsize);
+ dos->rootsize = GSHORT(b->rootsize);
+ dos->volsize = GSHORT(b->volsize);
+ if(dos->volsize == 0)
+ dos->volsize = GLONG(b->bigvolsize);
+ dos->mediadesc = b->mediadesc;
+ if(dos->fatsize == 0) {
+ chat("fat32\n");
+ dos->rootsize = 0;
+ dos->fatsize = GLONG(b->bigfatsize);
+ dos->fatbits = 32;
+ }
+ dos->fataddr = dos->nresrv;
+ if (dos->rootsize == 0) {
+ dos->rootaddr = 0;
+ dos->rootclust = GLONG(b->rootdirstartclust);
+ dos->dataaddr = dos->fataddr + dos->nfats*dos->fatsize;
+ } else {
+ dos->rootaddr = dos->fataddr + dos->nfats*dos->fatsize;
+ i = dos->rootsize*sizeof(Dosdir) + dos->sectsize - 1;
+ i = i/dos->sectsize;
+ dos->dataaddr = dos->rootaddr + i;
+ }
+ dos->fatclusters = 2+(dos->volsize - dos->dataaddr)/dos->clustsize;
+ if(dos->fatbits != 32) {
+ if(dos->fatclusters < 4087)
+ dos->fatbits = 12;
+ else
+ dos->fatbits = 16;
+ }
+ dos->freeptr = 2;
+
+ if(dos->clustbytes < 512 || dos->clustbytes > 64*1024)
+ goto unreasonable;
+
+chat("dosinit2\n");
+
+ /*
+ * set up the root
+ */
+
+ fs->root.fs = fs;
+ root = &fs->root.dos;
+ root->dos = dos;
+ root->pstart = dos->rootsize == 0 ? dos->rootclust : 0;
+ root->pcurrent = root->lcurrent = 0;
+ root->offset = 0;
+ root->attr = DOSDIR;
+ root->length = dos->rootsize*sizeof(Dosdir);
+
+chat("dosinit3\n");
+
+ fs->read = dosreadseg;
+ fs->walk = doswalk;
+ return 0;
+}
+
+static void
+bootdump(Dosboot *b)
+{
+ if(chatty == 0)
+ return;
+ print("magic: 0x%2.2x 0x%2.2x 0x%2.2x ",
+ b->magic[0], b->magic[1], b->magic[2]);
+ print("version: \"%8.8s\"\n", (char*)b->version);
+ print("sectsize: %d ", GSHORT(b->sectsize));
+ print("allocsize: %d ", b->clustsize);
+ print("nresrv: %d ", GSHORT(b->nresrv));
+ print("nfats: %d\n", b->nfats);
+ print("rootsize: %d ", GSHORT(b->rootsize));
+ print("volsize: %d ", GSHORT(b->volsize));
+ print("mediadesc: 0x%2.2x\n", b->mediadesc);
+ print("fatsize: %d ", GSHORT(b->fatsize));
+ print("trksize: %d ", GSHORT(b->trksize));
+ print("nheads: %d ", GSHORT(b->nheads));
+ print("nhidden: %d ", GLONG(b->nhidden));
+ print("bigvolsize: %d\n", GLONG(b->bigvolsize));
+/*
+ print("driveno: %d\n", b->driveno);
+ print("reserved0: 0x%2.2x\n", b->reserved0);
+ print("bootsig: 0x%2.2x\n", b->bootsig);
+ print("volid: 0x%8.8x\n", GLONG(b->volid));
+ print("label: \"%11.11s\"\n", b->label);
+*/
+}
+
+
+/*
+ * set up a dos file name
+ */
+static void
+setname(Dosfile *fp, char *from)
+{
+ char *to;
+
+ to = fp->name;
+ for(; *from && to-fp->name < 8; from++, to++){
+ if(*from == '.'){
+ from++;
+ break;
+ }
+ if(*from >= 'a' && *from <= 'z')
+ *to = *from + 'A' - 'a';
+ else
+ *to = *from;
+ }
+ while(to - fp->name < 8)
+ *to++ = ' ';
+
+ /* from might be 12345678.123: don't save the '.' in ext */
+ if(*from == '.')
+ from++;
+
+ to = fp->ext;
+ for(; *from && to-fp->ext < 3; from++, to++){
+ if(*from >= 'a' && *from <= 'z')
+ *to = *from + 'A' - 'a';
+ else
+ *to = *from;
+ }
+ while(to-fp->ext < 3)
+ *to++ = ' ';
+
+ chat("name is %8.8s.%3.3s\n", fp->name, fp->ext);
+}
diff --git a/os/boot/pc/dosfs.h b/os/boot/pc/dosfs.h
new file mode 100644
index 00000000..1107c4ce
--- /dev/null
+++ b/os/boot/pc/dosfs.h
@@ -0,0 +1,62 @@
+typedef struct Dosboot Dosboot;
+typedef struct Dos Dos;
+typedef struct Dosdir Dosdir;
+typedef struct Dosfile Dosfile;
+typedef struct Dospart Dospart;
+
+struct Dospart
+{
+ uchar flag; /* active flag */
+ uchar shead; /* starting head */
+ uchar scs[2]; /* starting cylinder/sector */
+ uchar type; /* partition type */
+ uchar ehead; /* ending head */
+ uchar ecs[2]; /* ending cylinder/sector */
+ uchar start[4]; /* starting sector */
+ uchar len[4]; /* length in sectors */
+};
+
+#define FAT12 0x01
+#define FAT16 0x04
+#define EXTEND 0x05
+#define FATHUGE 0x06
+#define FAT32 0x0b
+#define FAT32X 0x0c
+#define EXTHUGE 0x0f
+#define DMDDO 0x54
+#define PLAN9 0x39
+#define LEXTEND 0x85
+
+struct Dosfile{
+ Dos *dos; /* owning dos file system */
+ char name[8];
+ char ext[3];
+ uchar attr;
+ long length;
+ long pstart; /* physical start cluster address */
+ long pcurrent; /* physical current cluster address */
+ long lcurrent; /* logical current cluster address */
+ long offset;
+};
+
+struct Dos{
+ long start; /* start of file system */
+ int sectsize; /* in bytes */
+ int clustsize; /* in sectors */
+ int clustbytes; /* in bytes */
+ int nresrv; /* sectors */
+ int nfats; /* usually 2 */
+ int rootsize; /* number of entries */
+ int volsize; /* in sectors */
+ int mediadesc;
+ int fatsize; /* in sectors */
+ int fatclusters;
+ int fatbits; /* 12 or 16 */
+ long fataddr; /* sector number */
+ long rootaddr;
+ long rootclust;
+ long dataaddr;
+ long freeptr;
+};
+
+extern int dosinit(Fs*);
diff --git a/os/boot/pc/eipfmt.c b/os/boot/pc/eipfmt.c
new file mode 100644
index 00000000..9d3b38fb
--- /dev/null
+++ b/os/boot/pc/eipfmt.c
@@ -0,0 +1,145 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "ip.h"
+
+
+enum
+{
+ IPaddrlen= 16,
+ IPv4addrlen= 4,
+ IPv4off= 12,
+ IPllen= 4,
+};
+extern int fmtstrcpy(Fmt*, char*);
+
+
+/*
+ * prefix of all v4 addresses
+ */
+uchar v4prefix[IPaddrlen] = {
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0xff, 0xff,
+ 0, 0, 0, 0
+};
+
+enum
+{
+ Isprefix= 16,
+};
+
+uchar prefixvals[256] =
+{
+[0x00] 0 | Isprefix,
+[0x80] 1 | Isprefix,
+[0xC0] 2 | Isprefix,
+[0xE0] 3 | Isprefix,
+[0xF0] 4 | Isprefix,
+[0xF8] 5 | Isprefix,
+[0xFC] 6 | Isprefix,
+[0xFE] 7 | Isprefix,
+[0xFF] 8 | Isprefix,
+};
+
+void
+hnputl(void *p, uint v)
+{
+ uchar *a;
+
+ a = p;
+ a[0] = v>>24;
+ a[1] = v>>16;
+ a[2] = v>>8;
+ a[3] = v;
+}
+
+int
+eipfmt(Fmt *f)
+{
+ char buf[5*8];
+ static char *efmt = "%.2lux%.2lux%.2lux%.2lux%.2lux%.2lux";
+ static char *ifmt = "%d.%d.%d.%d";
+ uchar *p, ip[16];
+ ulong *lp;
+ ushort s;
+ int i, j, n, eln, eli;
+
+ switch(f->r) {
+ case 'E': /* Ethernet address */
+ p = va_arg(f->args, uchar*);
+ snprint(buf, sizeof buf, efmt, p[0], p[1], p[2], p[3], p[4], p[5]);
+ return fmtstrcpy(f, buf);
+
+ case 'I': /* Ip address */
+ p = va_arg(f->args, uchar*);
+common:
+ if(memcmp(p, v4prefix, 12) == 0){
+ snprint(buf, sizeof buf, ifmt, p[12], p[13], p[14], p[15]);
+ return fmtstrcpy(f, buf);
+ }
+
+ /* find longest elision */
+ eln = eli = -1;
+ for(i = 0; i < 16; i += 2){
+ for(j = i; j < 16; j += 2)
+ if(p[j] != 0 || p[j+1] != 0)
+ break;
+ if(j > i && j - i > eln){
+ eli = i;
+ eln = j - i;
+ }
+ }
+
+ /* print with possible elision */
+ n = 0;
+ for(i = 0; i < 16; i += 2){
+ if(i == eli){
+ n += sprint(buf+n, "::");
+ i += eln;
+ if(i >= 16)
+ break;
+ } else if(i != 0)
+ n += sprint(buf+n, ":");
+ s = (p[i]<<8) + p[i+1];
+ n += sprint(buf+n, "%ux", s);
+ }
+ return fmtstrcpy(f, buf);
+
+ case 'i': /* v6 address as 4 longs */
+ lp = va_arg(f->args, ulong*);
+ for(i = 0; i < 4; i++)
+ hnputl(ip+4*i, *lp++);
+ p = ip;
+ goto common;
+
+ case 'V': /* v4 ip address */
+ p = va_arg(f->args, uchar*);
+ snprint(buf, sizeof buf, ifmt, p[0], p[1], p[2], p[3]);
+ return fmtstrcpy(f, buf);
+
+ case 'M': /* ip mask */
+ p = va_arg(f->args, uchar*);
+
+ /* look for a prefix mask */
+ for(i = 0; i < 16; i++)
+ if(p[i] != 0xff)
+ break;
+ if(i < 16){
+ if((prefixvals[p[i]] & Isprefix) == 0)
+ goto common;
+ for(j = i+1; j < 16; j++)
+ if(p[j] != 0)
+ goto common;
+ n = 8*i + (prefixvals[p[i]] & ~Isprefix);
+ } else
+ n = 8*16;
+
+ /* got one, use /xx format */
+ snprint(buf, sizeof buf, "/%d", n);
+ return fmtstrcpy(f, buf);
+ }
+ return fmtstrcpy(f, "(eipfmt)");
+}
diff --git a/os/boot/pc/error.h b/os/boot/pc/error.h
new file mode 100644
index 00000000..98277d63
--- /dev/null
+++ b/os/boot/pc/error.h
@@ -0,0 +1,58 @@
+extern char Enoerror[]; /* no error */
+extern char Emount[]; /* inconsistent mount */
+extern char Eunmount[]; /* not mounted */
+extern char Eunion[]; /* not in union */
+extern char Emountrpc[]; /* mount rpc error */
+extern char Eshutdown[]; /* mounted device shut down */
+extern char Enocreate[]; /* mounted directory forbids creation */
+extern char Enonexist[]; /* file does not exist */
+extern char Eexist[]; /* file already exists */
+extern char Ebadsharp[]; /* unknown device in # filename */
+extern char Enotdir[]; /* not a directory */
+extern char Eisdir[]; /* file is a directory */
+extern char Ebadchar[]; /* bad character in file name */
+extern char Efilename[]; /* file name syntax */
+extern char Eperm[]; /* permission denied */
+extern char Ebadusefd[]; /* inappropriate use of fd */
+extern char Ebadarg[]; /* bad arg in system call */
+extern char Einuse[]; /* device or object already in use */
+extern char Eio[]; /* i/o error */
+extern char Etoobig[]; /* read or write too large */
+extern char Etoosmall[]; /* read or write too small */
+extern char Enetaddr[]; /* bad network address */
+extern char Emsgsize[]; /* message is too big for protocol */
+extern char Enetbusy[]; /* network device is busy or allocated */
+extern char Enoproto[]; /* network protocol not supported */
+extern char Enoport[]; /* network port not available */
+extern char Enoifc[]; /* bad interface or no free interface slots */
+extern char Enolisten[]; /* not announced */
+extern char Ehungup[]; /* write to hungup channel */
+extern char Ebadctl[]; /* bad process or channel control request */
+extern char Enodev[]; /* no free devices */
+extern char Enoenv[]; /* no free environment resources */
+extern char Emuxshutdown[]; /* mux server shut down */
+extern char Emuxbusy[]; /* all mux channels busy */
+extern char Emuxmsg[]; /* bad mux message format or mismatch */
+extern char Eprocdied[]; /* process exited */
+extern char Enochild[]; /* no living children */
+extern char Eioload[]; /* i/o error in demand load */
+extern char Enovmem[]; /* virtual memory allocation failed */
+extern char Ebadld[]; /* illegal line discipline */
+extern char Ebadfd[]; /* fd out of range or not open */
+extern char Eisstream[]; /* seek on a stream */
+extern char Ebadexec[]; /* exec header invalid */
+extern char Etimedout[]; /* connection timed out */
+extern char Econrefused[]; /* connection refused */
+extern char Enetunreach[]; /* network unreachable */
+extern char Eintr[]; /* interrupted */
+extern char Eneedservice[]; /* service required for tcp/udp/il calls */
+extern char Enomem[]; /* kernel allocate failed */
+extern char Enoswap[]; /* swap space full */
+extern char Esfnotcached[]; /* subfont not cached */
+extern char Esoverlap[]; /* segments overlap */
+extern char Emouseset[]; /* mouse type already set */
+extern char Erecover[]; /* failed to recover fd */
+extern char Eshort[]; /* i/o count too small */
+extern char Egreg[]; /* ken scheduled it */
+extern char Ebadspec[]; /* bad attach specifier */
+extern char Enoreg[]; /* process has no saved registers */
diff --git a/os/boot/pc/ether.c b/os/boot/pc/ether.c
new file mode 100644
index 00000000..c8a4b7de
--- /dev/null
+++ b/os/boot/pc/ether.c
@@ -0,0 +1,282 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ip.h"
+
+#include "etherif.h"
+
+static Ether ether[MaxEther];
+
+extern int ether2114xreset(Ether*);
+extern int elnk3reset(Ether*);
+extern int i82557reset(Ether*);
+extern int igbepnp(Ether *);
+extern int elnk3reset(Ether*);
+extern int ether589reset(Ether*);
+extern int ne2000reset(Ether*);
+extern int wd8003reset(Ether*);
+extern int ec2treset(Ether*);
+extern int amd79c970reset(Ether*);
+extern int rtl8139pnp(Ether*);
+extern int rtl8169pnp(Ether*);
+extern int ether83815reset(Ether*);
+extern int rhinepnp(Ether*);
+
+struct {
+ char *type;
+ int (*reset)(Ether*);
+ int noprobe;
+} ethercards[] = {
+ { "21140", ether2114xreset, 0, },
+ { "2114x", ether2114xreset, 0, },
+ { "i82557", i82557reset, 0, },
+ { "igbe", igbepnp, 0, },
+ { "elnk3", elnk3reset, 0, },
+ { "3C509", elnk3reset, 0, },
+ { "3C575", elnk3reset, 0, },
+ { "3C589", ether589reset, 1, },
+ { "3C562", ether589reset, 1, },
+ { "589E", ether589reset, 1, },
+ { "NE2000", ne2000reset, 0, },
+ { "WD8003", wd8003reset, 1, },
+ { "EC2T", ec2treset, 0, },
+ { "AMD79C970", amd79c970reset, 0, },
+ { "RTL8139", rtl8139pnp, 0, },
+ { "RTL8169", rtl8169pnp, 0, },
+ { "83815", ether83815reset, 0, },
+ { "rhine", rhinepnp, 0, },
+
+ { 0, }
+};
+
+static void xetherdetach(void);
+
+int
+etherinit(void)
+{
+ Ether *ctlr;
+ int ctlrno, i, mask, n, x;
+
+ fmtinstall('E', eipfmt);
+
+ etherdetach = xetherdetach;
+ mask = 0;
+ for(ctlrno = 0; ctlrno < MaxEther; ctlrno++){
+ ctlr = &ether[ctlrno];
+ memset(ctlr, 0, sizeof(Ether));
+ if(iniread && isaconfig("ether", ctlrno, ctlr) == 0)
+ continue;
+
+ for(n = 0; ethercards[n].type; n++){
+ if(!iniread){
+ if(ethercards[n].noprobe)
+ continue;
+ memset(ctlr, 0, sizeof(Ether));
+ strcpy(ctlr->type, ethercards[n].type);
+ }
+ else if(cistrcmp(ethercards[n].type, ctlr->type))
+ continue;
+ ctlr->ctlrno = ctlrno;
+
+ x = splhi();
+ if((*ethercards[n].reset)(ctlr)){
+ splx(x);
+ if(iniread)
+ break;
+ else
+ continue;
+ }
+
+ ctlr->state = 1;
+ mask |= 1<<ctlrno;
+ if(ctlr->irq == 2)
+ ctlr->irq = 9;
+ setvec(VectorPIC + ctlr->irq, ctlr->interrupt, ctlr);
+
+ print("ether#%d: %s: port 0x%luX irq %lud",
+ ctlr->ctlrno, ctlr->type, ctlr->port, ctlr->irq);
+ if(ctlr->mem)
+ print(" addr 0x%luX", ctlr->mem & ~KZERO);
+ if(ctlr->size)
+ print(" size 0x%luX", ctlr->size);
+ print(": %E\n", ctlr->ea);
+
+ if(ctlr->nrb == 0)
+ ctlr->nrb = Nrb;
+ ctlr->rb = ialloc(sizeof(RingBuf)*ctlr->nrb, 0);
+ if(ctlr->ntb == 0)
+ ctlr->ntb = Ntb;
+ ctlr->tb = ialloc(sizeof(RingBuf)*ctlr->ntb, 0);
+
+ ctlr->rh = 0;
+ ctlr->ri = 0;
+ for(i = 0; i < ctlr->nrb; i++)
+ ctlr->rb[i].owner = Interface;
+
+ ctlr->th = 0;
+ ctlr->ti = 0;
+ for(i = 0; i < ctlr->ntb; i++)
+ ctlr->tb[i].owner = Host;
+
+ splx(x);
+ break;
+ }
+ }
+
+ return mask;
+}
+
+void
+etherinitdev(int i, char *s)
+{
+ sprint(s, "ether%d", i);
+}
+
+void
+etherprintdevs(int i)
+{
+ print(" ether%d", i);
+}
+
+static Ether*
+attach(int ctlrno)
+{
+ Ether *ctlr;
+
+ if(ctlrno >= MaxEther || ether[ctlrno].state == 0)
+ return 0;
+
+ ctlr = &ether[ctlrno];
+ if(ctlr->state == 1){
+ ctlr->state = 2;
+ (*ctlr->attach)(ctlr);
+ }
+
+ return ctlr;
+}
+
+static void
+xetherdetach(void)
+{
+ Ether *ctlr;
+ int ctlrno, x;
+
+ x = splhi();
+ for(ctlrno = 0; ctlrno < MaxEther; ctlrno++){
+ ctlr = &ether[ctlrno];
+ if(ctlr->detach && ctlr->state != 0)
+ ctlr->detach(ctlr);
+ }
+ splx(x);
+}
+
+uchar*
+etheraddr(int ctlrno)
+{
+ Ether *ctlr;
+
+ if((ctlr = attach(ctlrno)) == 0)
+ return 0;
+
+ return ctlr->ea;
+}
+
+static int
+wait(RingBuf* ring, uchar owner, int timo)
+{
+ ulong start;
+
+ start = m->ticks;
+ while(TK2MS(m->ticks - start) < timo){
+ if(ring->owner != owner)
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+etherrxpkt(int ctlrno, Etherpkt* pkt, int timo)
+{
+ int n;
+ Ether *ctlr;
+ RingBuf *ring;
+
+ if((ctlr = attach(ctlrno)) == 0)
+ return 0;
+
+ ring = &ctlr->rb[ctlr->rh];
+ if(wait(ring, Interface, timo) == 0){
+ if(debug)
+ print("ether%d: rx timeout\n", ctlrno);
+ return 0;
+ }
+
+ n = ring->len;
+ memmove(pkt, ring->pkt, n);
+ ring->owner = Interface;
+ ctlr->rh = NEXT(ctlr->rh, ctlr->nrb);
+
+ return n;
+}
+
+int
+etherrxflush(int ctlrno)
+{
+ int n;
+ Ether *ctlr;
+ RingBuf *ring;
+
+ if((ctlr = attach(ctlrno)) == 0)
+ return 0;
+
+ n = 0;
+ for(;;){
+ ring = &ctlr->rb[ctlr->rh];
+ if(wait(ring, Interface, 100) == 0)
+ break;
+
+ ring->owner = Interface;
+ ctlr->rh = NEXT(ctlr->rh, ctlr->nrb);
+ n++;
+ }
+
+ return n;
+}
+
+int
+ethertxpkt(int ctlrno, Etherpkt* pkt, int len, int)
+{
+ Ether *ctlr;
+ RingBuf *ring;
+ int s;
+
+ if((ctlr = attach(ctlrno)) == 0)
+ return 0;
+
+ ring = &ctlr->tb[ctlr->th];
+ if(wait(ring, Interface, 1000) == 0){
+ print("ether%d: tx buffer timeout\n", ctlrno);
+ return 0;
+ }
+
+ memmove(pkt->s, ctlr->ea, Eaddrlen);
+ if(debug)
+ print("%E to %E...\n", pkt->s, pkt->d);
+ memmove(ring->pkt, pkt, len);
+ if(len < ETHERMINTU){
+ memset(ring->pkt+len, 0, ETHERMINTU-len);
+ len = ETHERMINTU;
+ }
+ ring->len = len;
+ ring->owner = Interface;
+ ctlr->th = NEXT(ctlr->th, ctlr->ntb);
+ s = splhi();
+ (*ctlr->transmit)(ctlr);
+ splx(s);
+
+ return 1;
+}
diff --git a/os/boot/pc/ether2000.c b/os/boot/pc/ether2000.c
new file mode 100644
index 00000000..4c329978
--- /dev/null
+++ b/os/boot/pc/ether2000.c
@@ -0,0 +1,110 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "etherif.h"
+#include "ether8390.h"
+
+/*
+ * Driver written for the 'Notebook Computer Ethernet LAN Adapter',
+ * a plug-in to the bus-slot on the rear of the Gateway NOMAD 425DXL
+ * laptop. The manual says NE2000 compatible.
+ * The interface appears to be pretty well described in the National
+ * Semiconductor Local Area Network Databook (1992) as one of the
+ * AT evaluation cards.
+ *
+ * The NE2000 is really just a DP8390[12] plus a data port
+ * and a reset port.
+ */
+enum {
+ Data = 0x10, /* offset from I/O base of data port */
+ Reset = 0x1F, /* offset from I/O base of reset port */
+};
+
+int
+ne2000reset(Ether* ether)
+{
+ ushort buf[16];
+ ulong port;
+ Dp8390 *ctlr;
+ int i;
+ uchar ea[Eaddrlen];
+
+ /*
+ * Set up the software configuration.
+ * Use defaults for port, irq, mem and size
+ * if not specified.
+ */
+ if(ether->port == 0)
+ ether->port = 0x300;
+ if(ether->irq == 0)
+ ether->irq = 2;
+ if(ether->mem == 0)
+ ether->mem = 0x4000;
+ if(ether->size == 0)
+ ether->size = 16*1024;
+ port = ether->port;
+
+ ether->ctlr = malloc(sizeof(Dp8390));
+ ctlr = ether->ctlr;
+ ctlr->width = 2;
+ ctlr->ram = 0;
+
+ ctlr->port = port;
+ ctlr->data = port+Data;
+
+ ctlr->tstart = HOWMANY(ether->mem, Dp8390BufSz);
+ ctlr->pstart = ctlr->tstart + HOWMANY(sizeof(Etherpkt), Dp8390BufSz);
+ ctlr->pstop = ctlr->tstart + HOWMANY(ether->size, Dp8390BufSz);
+
+ ctlr->dummyrr = 1;
+ for(i = 0; i < ether->nopt; i++){
+ if(strcmp(ether->opt[i], "nodummyrr"))
+ continue;
+ ctlr->dummyrr = 0;
+ break;
+ }
+
+ /*
+ * Reset the board. This is done by doing a read
+ * followed by a write to the Reset address.
+ */
+ buf[0] = inb(port+Reset);
+ delay(2);
+ outb(port+Reset, buf[0]);
+ delay(2);
+
+ /*
+ * Init the (possible) chip, then use the (possible)
+ * chip to read the (possible) PROM for ethernet address
+ * and a marker byte.
+ * Could just look at the DP8390 command register after
+ * initialisation has been tried, but that wouldn't be
+ * enough, there are other ethernet boards which could
+ * match.
+ */
+ dp8390reset(ether);
+ memset(buf, 0, sizeof(buf));
+ dp8390read(ctlr, buf, 0, sizeof(buf));
+ if((buf[0x0E] & 0xFF) != 0x57 || (buf[0x0F] & 0xFF) != 0x57){
+ free(ether->ctlr);
+ return -1;
+ }
+
+ /*
+ * Stupid machine. Shorts were asked for,
+ * shorts were delivered, although the PROM is a byte array.
+ * Set the ethernet address.
+ */
+ memset(ea, 0, Eaddrlen);
+ if(memcmp(ea, ether->ea, Eaddrlen) == 0){
+ for(i = 0; i < sizeof(ether->ea); i++)
+ ether->ea[i] = buf[i];
+ }
+ dp8390setea(ether);
+
+ return 0;
+}
diff --git a/os/boot/pc/ether2114x.c b/os/boot/pc/ether2114x.c
new file mode 100644
index 00000000..d6f12b52
--- /dev/null
+++ b/os/boot/pc/ether2114x.c
@@ -0,0 +1,1652 @@
+/*
+ * Digital Semiconductor DECchip 21140 PCI Fast Ethernet LAN Controller
+ * as found on the Digital Fast EtherWORKS PCI 10/100 adapter (DE-500-X).
+ * To do:
+ * thresholds;
+ * ring sizing;
+ * handle more error conditions;
+ * all the rest of it...
+ */
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "etherif.h"
+
+#define DEBUG (0)
+#define debug if(DEBUG)print
+
+enum {
+ Nrde = 32,
+ Ntde = 4,
+};
+
+#define Rbsz ROUNDUP(sizeof(Etherpkt)+4, 4)
+
+enum { /* CRS0 - Bus Mode */
+ Swr = 0x00000001, /* Software Reset */
+ Bar = 0x00000002, /* Bus Arbitration */
+ Dsl = 0x0000007C, /* Descriptor Skip Length (field) */
+ Ble = 0x00000080, /* Big/Little Endian */
+ Pbl = 0x00003F00, /* Programmable Burst Length (field) */
+ Cal = 0x0000C000, /* Cache Alignment (field) */
+ Cal8 = 0x00004000, /* 8 longword boundary alignment */
+ Cal16 = 0x00008000, /* 16 longword boundary alignment */
+ Cal32 = 0x0000C000, /* 32 longword boundary alignment */
+ Tap = 0x000E0000, /* Transmit Automatic Polling (field) */
+ Dbo = 0x00100000, /* Descriptor Byte Ordering Mode */
+ Rml = 0x00200000, /* Read Multiple */
+};
+
+enum { /* CSR[57] - Status and Interrupt Enable */
+ Ti = 0x00000001, /* Transmit Interrupt */
+ Tps = 0x00000002, /* Transmit Process Stopped */
+ Tu = 0x00000004, /* Transmit buffer Unavailable */
+ Tjt = 0x00000008, /* Transmit Jabber Timeout */
+ Unf = 0x00000020, /* transmit UNderFlow */
+ Ri = 0x00000040, /* Receive Interrupt */
+ Ru = 0x00000080, /* Receive buffer Unavailable */
+ Rps = 0x00000100, /* Receive Process Stopped */
+ Rwt = 0x00000200, /* Receive Watchdog Timeout */
+ Eti = 0x00000400, /* Early Transmit Interrupt */
+ Gte = 0x00000800, /* General purpose Timer Expired */
+ Fbe = 0x00002000, /* Fatal Bit Error */
+ Ais = 0x00008000, /* Abnormal Interrupt Summary */
+ Nis = 0x00010000, /* Normal Interrupt Summary */
+ Rs = 0x000E0000, /* Receive process State (field) */
+ Ts = 0x00700000, /* Transmit process State (field) */
+ Eb = 0x03800000, /* Error bits */
+};
+
+enum { /* CSR6 - Operating Mode */
+ Hp = 0x00000001, /* Hash/Perfect receive filtering mode */
+ Sr = 0x00000002, /* Start/stop Receive */
+ Ho = 0x00000004, /* Hash-Only filtering mode */
+ Pb = 0x00000008, /* Pass Bad frames */
+ If = 0x00000010, /* Inverse Filtering */
+ Sb = 0x00000020, /* Start/stop Backoff counter */
+ Pr = 0x00000040, /* Promiscuous Mode */
+ Pm = 0x00000080, /* Pass all Multicast */
+ Fd = 0x00000200, /* Full Duplex mode */
+ Om = 0x00000C00, /* Operating Mode (field) */
+ Fc = 0x00001000, /* Force Collision */
+ St = 0x00002000, /* Start/stop Transmission Command */
+ Tr = 0x0000C000, /* ThReshold control bits (field) */
+ Tr128 = 0x00000000,
+ Tr256 = 0x00004000,
+ Tr512 = 0x00008000,
+ Tr1024 = 0x0000C000,
+ Ca = 0x00020000, /* CApture effect enable */
+ Ps = 0x00040000, /* Port Select */
+ Hbd = 0x00080000, /* HeartBeat Disable */
+ Imm = 0x00100000, /* IMMediate mode */
+ Sf = 0x00200000, /* Store and Forward */
+ Ttm = 0x00400000, /* Transmit Threshold Mode */
+ Pcs = 0x00800000, /* PCS function */
+ Scr = 0x01000000, /* SCRambler mode */
+ Mbo = 0x02000000, /* Must Be One */
+ Ra = 0x40000000, /* Receive All */
+ Sc = 0x80000000, /* Special Capture effect enable */
+
+ TrMODE = Tr512, /* default transmission threshold */
+};
+
+enum { /* CSR9 - ROM and MII Management */
+ Scs = 0x00000001, /* serial ROM chip select */
+ Sclk = 0x00000002, /* serial ROM clock */
+ Sdi = 0x00000004, /* serial ROM data in */
+ Sdo = 0x00000008, /* serial ROM data out */
+ Ss = 0x00000800, /* serial ROM select */
+ Wr = 0x00002000, /* write */
+ Rd = 0x00004000, /* read */
+
+ Mdc = 0x00010000, /* MII management clock */
+ Mdo = 0x00020000, /* MII management write data */
+ Mii = 0x00040000, /* MII management operation mode (W) */
+ Mdi = 0x00080000, /* MII management data in */
+};
+
+enum { /* CSR12 - General-Purpose Port */
+ Gpc = 0x00000100, /* General Purpose Control */
+};
+
+typedef struct Des {
+ int status;
+ int control;
+ ulong addr;
+ void* bp;
+} Des;
+
+enum { /* status */
+ Of = 0x00000001, /* Rx: OverFlow */
+ Ce = 0x00000002, /* Rx: CRC Error */
+ Db = 0x00000004, /* Rx: Dribbling Bit */
+ Re = 0x00000008, /* Rx: Report on MII Error */
+ Rw = 0x00000010, /* Rx: Receive Watchdog */
+ Ft = 0x00000020, /* Rx: Frame Type */
+ Cs = 0x00000040, /* Rx: Collision Seen */
+ Tl = 0x00000080, /* Rx: Frame too Long */
+ Ls = 0x00000100, /* Rx: Last deScriptor */
+ Fs = 0x00000200, /* Rx: First deScriptor */
+ Mf = 0x00000400, /* Rx: Multicast Frame */
+ Rf = 0x00000800, /* Rx: Runt Frame */
+ Dt = 0x00003000, /* Rx: Data Type (field) */
+ De = 0x00004000, /* Rx: Descriptor Error */
+ Fl = 0x3FFF0000, /* Rx: Frame Length (field) */
+ Ff = 0x40000000, /* Rx: Filtering Fail */
+
+ Def = 0x00000001, /* Tx: DEFerred */
+ Uf = 0x00000002, /* Tx: UnderFlow error */
+ Lf = 0x00000004, /* Tx: Link Fail report */
+ Cc = 0x00000078, /* Tx: Collision Count (field) */
+ Hf = 0x00000080, /* Tx: Heartbeat Fail */
+ Ec = 0x00000100, /* Tx: Excessive Collisions */
+ Lc = 0x00000200, /* Tx: Late Collision */
+ Nc = 0x00000400, /* Tx: No Carrier */
+ Lo = 0x00000800, /* Tx: LOss of carrier */
+ To = 0x00004000, /* Tx: Transmission jabber timeOut */
+
+ Es = 0x00008000, /* [RT]x: Error Summary */
+ Own = 0x80000000, /* [RT]x: OWN bit */
+};
+
+enum { /* control */
+ Bs1 = 0x000007FF, /* [RT]x: Buffer 1 Size */
+ Bs2 = 0x003FF800, /* [RT]x: Buffer 2 Size */
+
+ Ch = 0x01000000, /* [RT]x: second address CHained */
+ Er = 0x02000000, /* [RT]x: End of Ring */
+
+ Ft0 = 0x00400000, /* Tx: Filtering Type 0 */
+ Dpd = 0x00800000, /* Tx: Disabled PaDding */
+ Ac = 0x04000000, /* Tx: Add CRC disable */
+ Set = 0x08000000, /* Tx: SETup packet */
+ Ft1 = 0x10000000, /* Tx: Filtering Type 1 */
+ Fseg = 0x20000000, /* Tx: First SEGment */
+ Lseg = 0x40000000, /* Tx: Last SEGment */
+ Ic = 0x80000000, /* Tx: Interrupt on Completion */
+};
+
+enum { /* PHY registers */
+ Bmcr = 0, /* Basic Mode Control */
+ Bmsr = 1, /* Basic Mode Status */
+ Phyidr1 = 2, /* PHY Identifier #1 */
+ Phyidr2 = 3, /* PHY Identifier #2 */
+ Anar = 4, /* Auto-Negotiation Advertisment */
+ Anlpar = 5, /* Auto-Negotiation Link Partner Ability */
+ Aner = 6, /* Auto-Negotiation Expansion */
+};
+
+enum { /* Variants */
+ Tulip0 = (0x0009<<16)|0x1011,
+ Tulip1 = (0x0014<<16)|0x1011,
+ Tulip3 = (0x0019<<16)|0x1011,
+ Pnic = (0x0002<<16)|0x11AD,
+ Pnic2 = (0xC115<<16)|0x11AD,
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Ctlr {
+ int port;
+ Pcidev* pcidev;
+ Ctlr* next;
+ int active;
+ int id; /* (pcidev->did<<16)|pcidev->vid */
+
+ uchar *srom;
+ int sromsz;
+ uchar* sromea; /* MAC address */
+ uchar* leaf;
+ int sct; /* selected connection type */
+ int k; /* info block count */
+ uchar* infoblock[16];
+ int sctk; /* sct block index */
+ int curk; /* current block index */
+ uchar* type5block;
+
+ int phy[32]; /* logical to physical map */
+ int phyreset; /* reset bitmap */
+ int curphyad;
+ int fdx;
+ int ttm;
+
+ uchar fd; /* option */
+ int medium; /* option */
+
+ int csr6; /* CSR6 - operating mode */
+ int mask; /* CSR[57] - interrupt mask */
+ int mbps;
+
+ Des* rdr; /* receive descriptor ring */
+ int nrdr; /* size of rdr */
+ int rdrx; /* index into rdr */
+
+ Des* tdr; /* transmit descriptor ring */
+ int ntdr; /* size of tdr */
+ int tdrh; /* host index into tdr */
+ int tdri; /* interface index into tdr */
+ int ntq; /* descriptors active */
+ Block* setupbp;
+
+ ulong of; /* receive statistics */
+ ulong ce;
+ ulong cs;
+ ulong tl;
+ ulong rf;
+ ulong de;
+
+ ulong uf; /* transmit statistics */
+ ulong ec;
+ ulong lc;
+ ulong nc;
+ ulong lo;
+ ulong to;
+
+} Ctlr;
+
+static Ctlr* ctlrhead;
+static Ctlr* ctlrtail;
+
+#define csr32r(c, r) (inl((c)->port+((r)*8)))
+#define csr32w(c, r, l) (outl((c)->port+((r)*8), (ulong)(l)))
+
+static void
+attach(Ether* ether)
+{
+ Ctlr *ctlr;
+
+ ctlr = ether->ctlr;
+ if(!(ctlr->csr6 & Sr)){
+ ctlr->csr6 |= Sr;
+ csr32w(ctlr, 6, ctlr->csr6);
+ }
+}
+
+static void
+transmit(Ether* ether)
+{
+ Ctlr *ctlr;
+ Block *bp;
+ Des *des;
+ int control;
+ RingBuf *tb;
+
+ ctlr = ether->ctlr;
+ while(ctlr->ntq < (ctlr->ntdr-1)){
+ if(ctlr->setupbp){
+ bp = ctlr->setupbp;
+ ctlr->setupbp = 0;
+ control = Ic|Set|BLEN(bp);
+ }
+ else{
+ if(ether->ntb == 0)
+ break;
+ tb = &ether->tb[ether->ti];
+ if(tb->owner != Interface)
+ break;
+ bp = allocb(tb->len);
+ memmove(bp->wp, tb->pkt, tb->len);
+ memmove(bp->wp+Eaddrlen, ether->ea, Eaddrlen);
+ bp->wp += tb->len;
+
+ tb->owner = Host;
+ ether->ti = NEXT(ether->ti, ether->ntb);
+
+ control = Ic|Lseg|Fseg|BLEN(bp);
+ }
+
+ ctlr->tdr[PREV(ctlr->tdrh, ctlr->ntdr)].control &= ~Ic;
+ des = &ctlr->tdr[ctlr->tdrh];
+ des->bp = bp;
+ des->addr = PADDR(bp->rp);
+ des->control |= control;
+ ctlr->ntq++;
+ //coherence();
+ des->status = Own;
+ csr32w(ctlr, 1, 0);
+ ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdr);
+ }
+}
+
+static void
+interrupt(Ureg*, void* arg)
+{
+ Ctlr *ctlr;
+ Ether *ether;
+ int len, status;
+ Des *des;
+ RingBuf *ring;
+
+ ether = arg;
+ ctlr = ether->ctlr;
+
+ while((status = csr32r(ctlr, 5)) & (Nis|Ais)){
+ /*
+ * Acknowledge the interrupts and mask-out
+ * the ones that are implicitly handled.
+ */
+ csr32w(ctlr, 5, status);
+ status &= (ctlr->mask & ~(Nis|Ais|Ti));
+
+ /*
+ * Received packets.
+ */
+ if(status & Ri){
+ des = &ctlr->rdr[ctlr->rdrx];
+ while((des->status & Own) == 0){
+ len = ((des->status & Fl)>>16)-4;
+ if(des->status & Es){
+ if(des->status & Of)
+ ctlr->of++;
+ if(des->status & Ce)
+ ctlr->ce++;
+ if(des->status & Cs)
+ ctlr->cs++;
+ if(des->status & Tl)
+ ctlr->tl++;
+ if(des->status & Rf)
+ ctlr->rf++;
+ if(des->status & De)
+ ctlr->de++;
+ }
+ else{
+ ring = &ether->rb[ether->ri];
+ if(ring->owner == Interface){
+ ring->owner = Host;
+ ring->len = len;
+ memmove(ring->pkt, des->bp, len);
+ ether->ri = NEXT(ether->ri, ether->nrb);
+ }
+ }
+
+ des->control &= Er;
+ des->control |= Rbsz;
+ des->status = Own;
+
+ ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdr);
+ des = &ctlr->rdr[ctlr->rdrx];
+ }
+ status &= ~Ri;
+ }
+
+ /*
+ * Check the transmit side:
+ * check for Transmit Underflow and Adjust
+ * the threshold upwards;
+ * free any transmitted buffers and try to
+ * top-up the ring.
+ */
+ if(status & Unf){
+ csr32w(ctlr, 6, ctlr->csr6 & ~St);
+ switch(ctlr->csr6 & Tr){
+ case Tr128:
+ len = Tr256;
+ break;
+ case Tr256:
+ len = Tr512;
+ break;
+ case Tr512:
+ len = Tr1024;
+ break;
+ default:
+ case Tr1024:
+ len = Sf;
+ break;
+ }
+ ctlr->csr6 = (ctlr->csr6 & ~Tr)|len;
+ csr32w(ctlr, 6, ctlr->csr6);
+ csr32w(ctlr, 5, Tps);
+ status &= ~(Unf|Tps);
+ }
+
+ while(ctlr->ntq){
+ des = &ctlr->tdr[ctlr->tdri];
+ if(des->status & Own)
+ break;
+
+ if(des->status & Es){
+ if(des->status & Uf)
+ ctlr->uf++;
+ if(des->status & Ec)
+ ctlr->ec++;
+ if(des->status & Lc)
+ ctlr->lc++;
+ if(des->status & Nc)
+ ctlr->nc++;
+ if(des->status & Lo)
+ ctlr->lo++;
+ if(des->status & To)
+ ctlr->to++;
+ }
+
+ freeb(des->bp);
+ des->control &= Er;
+
+ ctlr->ntq--;
+ ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdr);
+ }
+ transmit(ether);
+
+ /*
+ * Anything left not catered for?
+ */
+ if(status)
+ panic("#l%d: status %8.8uX\n", ether->ctlrno, status);
+ }
+}
+
+static void
+ctlrinit(Ether* ether)
+{
+ Ctlr *ctlr;
+ Des *des;
+ Block *bp;
+ int i;
+ uchar bi[Eaddrlen*2];
+
+ ctlr = ether->ctlr;
+
+ /*
+ * Allocate and initialise the receive ring;
+ * allocate and initialise the transmit ring;
+ * unmask interrupts and start the transmit side;
+ * create and post a setup packet to initialise
+ * the physical ethernet address.
+ */
+ ctlr->rdr = malloc(ctlr->nrdr*sizeof(Des));
+ for(des = ctlr->rdr; des < &ctlr->rdr[ctlr->nrdr]; des++){
+ des->bp = malloc(Rbsz);
+ des->status = Own;
+ des->control = Rbsz;
+ des->addr = PADDR(des->bp);
+ }
+ ctlr->rdr[ctlr->nrdr-1].control |= Er;
+ ctlr->rdrx = 0;
+ csr32w(ctlr, 3, PADDR(ctlr->rdr));
+
+ ctlr->tdr = ialloc(ctlr->ntdr*sizeof(Des), 32);
+ ctlr->tdr[ctlr->ntdr-1].control |= Er;
+ ctlr->tdrh = 0;
+ ctlr->tdri = 0;
+ csr32w(ctlr, 4, PADDR(ctlr->tdr));
+
+ /*
+ * Clear any bits in the Status Register (CSR5) as
+ * the PNIC has a different reset value from a true 2114x.
+ */
+ ctlr->mask = Nis|Ais|Fbe|Rwt|Rps|Ru|Ri|Unf|Tjt|Tps|Ti;
+ csr32w(ctlr, 5, ctlr->mask);
+ csr32w(ctlr, 7, ctlr->mask);
+ ctlr->csr6 |= St;
+ csr32w(ctlr, 6, ctlr->csr6);
+
+ for(i = 0; i < Eaddrlen/2; i++){
+ bi[i*4] = ether->ea[i*2];
+ bi[i*4+1] = ether->ea[i*2+1];
+ bi[i*4+2] = ether->ea[i*2+1];
+ bi[i*4+3] = ether->ea[i*2];
+ }
+ bp = allocb(Eaddrlen*2*16);
+ memset(bp->rp, 0xFF, sizeof(bi));
+ for(i = sizeof(bi); i < sizeof(bi)*16; i += sizeof(bi))
+ memmove(bp->rp+i, bi, sizeof(bi));
+ bp->wp += sizeof(bi)*16;
+
+ ctlr->setupbp = bp;
+ transmit(ether);
+}
+
+static void
+csr9w(Ctlr* ctlr, int data)
+{
+ csr32w(ctlr, 9, data);
+ microdelay(1);
+}
+
+static int
+miimdi(Ctlr* ctlr, int n)
+{
+ int data, i;
+
+ /*
+ * Read n bits from the MII Management Register.
+ */
+ data = 0;
+ for(i = n-1; i >= 0; i--){
+ if(csr32r(ctlr, 9) & Mdi)
+ data |= (1<<i);
+ csr9w(ctlr, Mii|Mdc);
+ csr9w(ctlr, Mii);
+ }
+ csr9w(ctlr, 0);
+
+ return data;
+}
+
+static void
+miimdo(Ctlr* ctlr, int bits, int n)
+{
+ int i, mdo;
+
+ /*
+ * Write n bits to the MII Management Register.
+ */
+ for(i = n-1; i >= 0; i--){
+ if(bits & (1<<i))
+ mdo = Mdo;
+ else
+ mdo = 0;
+ csr9w(ctlr, mdo);
+ csr9w(ctlr, mdo|Mdc);
+ csr9w(ctlr, mdo);
+ }
+}
+
+static int
+miir(Ctlr* ctlr, int phyad, int regad)
+{
+ int data, i;
+
+ if(ctlr->id == Pnic){
+ i = 1000;
+ csr32w(ctlr, 20, 0x60020000|(phyad<<23)|(regad<<18));
+ do{
+ microdelay(1);
+ data = csr32r(ctlr, 20);
+ }while((data & 0x80000000) && --i);
+
+ if(i == 0)
+ return -1;
+ return data & 0xFFFF;
+ }
+
+ /*
+ * Preamble;
+ * ST+OP+PHYAD+REGAD;
+ * TA + 16 data bits.
+ */
+ miimdo(ctlr, 0xFFFFFFFF, 32);
+ miimdo(ctlr, 0x1800|(phyad<<5)|regad, 14);
+ data = miimdi(ctlr, 18);
+
+ if(data & 0x10000)
+ return -1;
+
+ return data & 0xFFFF;
+}
+
+static void
+miiw(Ctlr* ctlr, int phyad, int regad, int data)
+{
+ /*
+ * Preamble;
+ * ST+OP+PHYAD+REGAD+TA + 16 data bits;
+ * Z.
+ */
+ miimdo(ctlr, 0xFFFFFFFF, 32);
+ data &= 0xFFFF;
+ data |= (0x05<<(5+5+2+16))|(phyad<<(5+2+16))|(regad<<(2+16))|(0x02<<16);
+ miimdo(ctlr, data, 32);
+ csr9w(ctlr, Mdc);
+ csr9w(ctlr, 0);
+}
+
+static int
+sromr(Ctlr* ctlr, int r)
+{
+ int i, op, data, size;
+
+ if(ctlr->id == Pnic){
+ i = 1000;
+ csr32w(ctlr, 19, 0x600|r);
+ do{
+ microdelay(1);
+ data = csr32r(ctlr, 19);
+ }while((data & 0x80000000) && --i);
+
+ if(ctlr->sromsz == 0)
+ ctlr->sromsz = 6;
+
+ return csr32r(ctlr, 9) & 0xFFFF;
+ }
+
+ /*
+ * This sequence for reading a 16-bit register 'r'
+ * in the EEPROM is taken (pretty much) straight from Section
+ * 7.4 of the 21140 Hardware Reference Manual.
+ */
+reread:
+ csr9w(ctlr, Rd|Ss);
+ csr9w(ctlr, Rd|Ss|Scs);
+ csr9w(ctlr, Rd|Ss|Sclk|Scs);
+ csr9w(ctlr, Rd|Ss);
+
+ op = 0x06;
+ for(i = 3-1; i >= 0; i--){
+ data = Rd|Ss|(((op>>i) & 0x01)<<2)|Scs;
+ csr9w(ctlr, data);
+ csr9w(ctlr, data|Sclk);
+ csr9w(ctlr, data);
+ }
+
+ /*
+ * First time through must work out the EEPROM size.
+ * This doesn't seem to work on the 21041 as implemented
+ * in Virtual PC for the Mac, so wire any 21041 to 6,
+ * it's the only 21041 this code will ever likely see.
+ */
+ if((size = ctlr->sromsz) == 0){
+ if(ctlr->id == Tulip1)
+ ctlr->sromsz = size = 6;
+ else
+ size = 8;
+ }
+
+ for(size = size-1; size >= 0; size--){
+ data = Rd|Ss|(((r>>size) & 0x01)<<2)|Scs;
+ csr9w(ctlr, data);
+ csr9w(ctlr, data|Sclk);
+ csr9w(ctlr, data);
+ microdelay(1);
+ if(ctlr->sromsz == 0 && !(csr32r(ctlr, 9) & Sdo))
+ break;
+ }
+
+ data = 0;
+ for(i = 16-1; i >= 0; i--){
+ csr9w(ctlr, Rd|Ss|Sclk|Scs);
+ if(csr32r(ctlr, 9) & Sdo)
+ data |= (1<<i);
+ csr9w(ctlr, Rd|Ss|Scs);
+ }
+
+ csr9w(ctlr, 0);
+
+ if(ctlr->sromsz == 0){
+ ctlr->sromsz = 8-size;
+ goto reread;
+ }
+
+ return data & 0xFFFF;
+}
+
+static void
+softreset(Ctlr* ctlr)
+{
+ /*
+ * Soft-reset the controller and initialise bus mode.
+ * Delay should be >= 50 PCI cycles (2ƗS @ 25MHz).
+ */
+ csr32w(ctlr, 0, Swr);
+ microdelay(10);
+ csr32w(ctlr, 0, Rml|Cal16);
+ delay(1);
+}
+
+static int
+type5block(Ctlr* ctlr, uchar* block)
+{
+ int csr15, i, len;
+
+ /*
+ * Reset or GPR sequence. Reset should be once only,
+ * before the GPR sequence.
+ * Note 'block' is not a pointer to the block head but
+ * a pointer to the data in the block starting at the
+ * reset length value so type5block can be used for the
+ * sequences contained in type 1 and type 3 blocks.
+ * The SROM docs state the 21140 type 5 block is the
+ * same as that for the 21143, but the two controllers
+ * use different registers and sequence-element lengths
+ * so the 21140 code here is a guess for a real type 5
+ * sequence.
+ */
+ len = *block++;
+ if(ctlr->id != Tulip3){
+ for(i = 0; i < len; i++){
+ csr32w(ctlr, 12, *block);
+ block++;
+ }
+ return len;
+ }
+
+ for(i = 0; i < len; i++){
+ csr15 = *block++<<16;
+ csr15 |= *block++<<24;
+ csr32w(ctlr, 15, csr15);
+ debug("%8.8uX ", csr15);
+ }
+ return 2*len;
+}
+
+static int
+typephylink(Ctlr* ctlr, uchar*)
+{
+ int an, bmcr, bmsr, csr6, x;
+
+ /*
+ * Fail if
+ * auto-negotiataion enabled but not complete;
+ * no valid link established.
+ */
+ bmcr = miir(ctlr, ctlr->curphyad, Bmcr);
+ miir(ctlr, ctlr->curphyad, Bmsr);
+ bmsr = miir(ctlr, ctlr->curphyad, Bmsr);
+ debug("bmcr 0x%2.2uX bmsr 0x%2.2uX\n", bmcr, bmsr);
+ if(((bmcr & 0x1000) && !(bmsr & 0x0020)) || !(bmsr & 0x0004))
+ return 0;
+
+ if(bmcr & 0x1000){
+ an = miir(ctlr, ctlr->curphyad, Anar);
+ an &= miir(ctlr, ctlr->curphyad, Anlpar) & 0x3E0;
+ debug("an 0x%2.uX 0x%2.2uX 0x%2.2uX\n",
+ miir(ctlr, ctlr->curphyad, Anar),
+ miir(ctlr, ctlr->curphyad, Anlpar),
+ an);
+
+ if(an & 0x0100)
+ x = 0x4000;
+ else if(an & 0x0080)
+ x = 0x2000;
+ else if(an & 0x0040)
+ x = 0x1000;
+ else if(an & 0x0020)
+ x = 0x0800;
+ else
+ x = 0;
+ }
+ else if((bmcr & 0x2100) == 0x2100)
+ x = 0x4000;
+ else if(bmcr & 0x2000){
+ /*
+ * If FD capable, force it if necessary.
+ */
+ if((bmsr & 0x4000) && ctlr->fd){
+ miiw(ctlr, ctlr->curphyad, Bmcr, 0x2100);
+ x = 0x4000;
+ }
+ else
+ x = 0x2000;
+ }
+ else if(bmcr & 0x0100)
+ x = 0x1000;
+ else
+ x = 0x0800;
+
+ csr6 = Sc|Mbo|Hbd|Ps|Ca|TrMODE|Sb;
+ if(ctlr->fdx & x)
+ csr6 |= Fd;
+ if(ctlr->ttm & x)
+ csr6 |= Ttm;
+ debug("csr6 0x%8.8uX 0x%8.8uX 0x%8.8luX\n",
+ csr6, ctlr->csr6, csr32r(ctlr, 6));
+ if(csr6 != ctlr->csr6){
+ ctlr->csr6 = csr6;
+ csr32w(ctlr, 6, csr6);
+ }
+
+ return 1;
+}
+
+static int
+typephymode(Ctlr* ctlr, uchar* block, int wait)
+{
+ uchar *p;
+ int len, mc, nway, phyx, timeo;
+
+ if(DEBUG){
+ int i;
+
+ len = (block[0] & ~0x80)+1;
+ for(i = 0; i < len; i++)
+ debug("%2.2uX ", block[i]);
+ debug("\n");
+ }
+
+ if(block[1] == 1)
+ len = 1;
+ else if(block[1] == 3)
+ len = 2;
+ else
+ return -1;
+
+ /*
+ * Snarf the media capabilities, nway advertisment,
+ * FDX and TTM bitmaps.
+ */
+ p = &block[5+len*block[3]+len*block[4+len*block[3]]];
+ mc = *p++;
+ mc |= *p++<<8;
+ nway = *p++;
+ nway |= *p++<<8;
+ ctlr->fdx = *p++;
+ ctlr->fdx |= *p++<<8;
+ ctlr->ttm = *p++;
+ ctlr->ttm |= *p<<8;
+ debug("mc %4.4uX nway %4.4uX fdx %4.4uX ttm %4.4uX\n",
+ mc, nway, ctlr->fdx, ctlr->ttm);
+ USED(mc);
+
+ phyx = block[2];
+ ctlr->curphyad = ctlr->phy[phyx];
+
+ ctlr->csr6 = 0;//Sc|Mbo|Hbd|Ps|Ca|TrMODE|Sb;
+ //csr32w(ctlr, 6, ctlr->csr6);
+ if(typephylink(ctlr, block))
+ return 0;
+
+ if(!(ctlr->phyreset & (1<<phyx))){
+ debug("reset seq: len %d: ", block[3]);
+ if(ctlr->type5block)
+ type5block(ctlr, &ctlr->type5block[2]);
+ else
+ type5block(ctlr, &block[4+len*block[3]]);
+ debug("\n");
+ ctlr->phyreset |= (1<<phyx);
+ }
+
+ /*
+ * GPR sequence.
+ */
+ debug("gpr seq: len %d: ", block[3]);
+ type5block(ctlr, &block[3]);
+ debug("\n");
+
+ ctlr->csr6 = 0;//Sc|Mbo|Hbd|Ps|Ca|TrMODE|Sb;
+ //csr32w(ctlr, 6, ctlr->csr6);
+ if(typephylink(ctlr, block))
+ return 0;
+
+ /*
+ * Turn off auto-negotiation, set the auto-negotiation
+ * advertisment register then start the auto-negotiation
+ * process again.
+ */
+ miiw(ctlr, ctlr->curphyad, Bmcr, 0);
+ miiw(ctlr, ctlr->curphyad, Anar, nway|1);
+ miiw(ctlr, ctlr->curphyad, Bmcr, 0x1000);
+
+ if(!wait)
+ return 0;
+
+ for(timeo = 0; timeo < 30; timeo++){
+ if(typephylink(ctlr, block))
+ return 0;
+ delay(100);
+ }
+
+ return -1;
+}
+
+static int
+typesymmode(Ctlr *ctlr, uchar *block, int wait)
+{
+ uint gpmode, gpdata, command;
+
+ USED(wait);
+ gpmode = block[3] | ((uint) block[4] << 8);
+ gpdata = block[5] | ((uint) block[6] << 8);
+ command = (block[7] | ((uint) block[8] << 8)) & 0x71;
+ if (command & 0x8000) {
+ print("ether2114x.c: FIXME: handle type 4 mode blocks where cmd.active_invalid != 0\n");
+ return -1;
+ }
+ csr32w(ctlr, 15, gpmode);
+ csr32w(ctlr, 15, gpdata);
+ ctlr->csr6 = (command & 0x71) << 18;
+ csr32w(ctlr, 6, ctlr->csr6);
+ return 0;
+}
+
+static int
+type2mode(Ctlr* ctlr, uchar* block, int)
+{
+ uchar *p;
+ int csr6, csr13, csr14, csr15, gpc, gpd;
+
+ csr6 = Sc|Mbo|Ca|TrMODE|Sb;
+ debug("type2mode: medium 0x%2.2uX\n", block[2]);
+
+ /*
+ * Don't attempt full-duplex
+ * unless explicitly requested.
+ */
+ if((block[2] & 0x3F) == 0x04){ /* 10BASE-TFD */
+ if(!ctlr->fd)
+ return -1;
+ csr6 |= Fd;
+ }
+
+ /*
+ * Operating mode programming values from the datasheet
+ * unless media specific data is explicitly given.
+ */
+ p = &block[3];
+ if(block[2] & 0x40){
+ csr13 = (block[4]<<8)|block[3];
+ csr14 = (block[6]<<8)|block[5];
+ csr15 = (block[8]<<8)|block[7];
+ p += 6;
+ }
+ else switch(block[2] & 0x3F){
+ default:
+ return -1;
+ case 0x00: /* 10BASE-T */
+ csr13 = 0x00000001;
+ csr14 = 0x00007F3F;
+ csr15 = 0x00000008;
+ break;
+ case 0x01: /* 10BASE-2 */
+ csr13 = 0x00000009;
+ csr14 = 0x00000705;
+ csr15 = 0x00000006;
+ break;
+ case 0x02: /* 10BASE-5 (AUI) */
+ csr13 = 0x00000009;
+ csr14 = 0x00000705;
+ csr15 = 0x0000000E;
+ break;
+ case 0x04: /* 10BASE-TFD */
+ csr13 = 0x00000001;
+ csr14 = 0x00007F3D;
+ csr15 = 0x00000008;
+ break;
+ }
+ gpc = *p++<<16;
+ gpc |= *p++<<24;
+ gpd = *p++<<16;
+ gpd |= *p<<24;
+
+ csr32w(ctlr, 13, 0);
+ csr32w(ctlr, 14, csr14);
+ csr32w(ctlr, 15, gpc|csr15);
+ delay(10);
+ csr32w(ctlr, 15, gpd|csr15);
+ csr32w(ctlr, 13, csr13);
+
+ ctlr->csr6 = csr6;
+ csr32w(ctlr, 6, ctlr->csr6);
+
+ debug("type2mode: csr13 %8.8uX csr14 %8.8uX csr15 %8.8uX\n",
+ csr13, csr14, csr15);
+ debug("type2mode: gpc %8.8uX gpd %8.8uX csr6 %8.8uX\n",
+ gpc, gpd, csr6);
+
+ return 0;
+}
+
+static int
+type0link(Ctlr* ctlr, uchar* block)
+{
+ int m, polarity, sense;
+
+ m = (block[3]<<8)|block[2];
+ sense = 1<<((m & 0x000E)>>1);
+ if(m & 0x0080)
+ polarity = sense;
+ else
+ polarity = 0;
+
+ return (csr32r(ctlr, 12) & sense)^polarity;
+}
+
+static int
+type0mode(Ctlr* ctlr, uchar* block, int wait)
+{
+ int csr6, m, timeo;
+
+ csr6 = Sc|Mbo|Hbd|Ca|TrMODE|Sb;
+debug("type0: medium 0x%uX, fd %d: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n",
+ ctlr->medium, ctlr->fd, block[0], block[1], block[2], block[3]);
+ switch(block[0]){
+ default:
+ break;
+
+ case 0x04: /* 10BASE-TFD */
+ case 0x05: /* 100BASE-TXFD */
+ case 0x08: /* 100BASE-FXFD */
+ /*
+ * Don't attempt full-duplex
+ * unless explicitly requested.
+ */
+ if(!ctlr->fd)
+ return -1;
+ csr6 |= Fd;
+ break;
+ }
+
+ m = (block[3]<<8)|block[2];
+ if(m & 0x0001)
+ csr6 |= Ps;
+ if(m & 0x0010)
+ csr6 |= Ttm;
+ if(m & 0x0020)
+ csr6 |= Pcs;
+ if(m & 0x0040)
+ csr6 |= Scr;
+
+ csr32w(ctlr, 12, block[1]);
+ microdelay(10);
+ csr32w(ctlr, 6, csr6);
+ ctlr->csr6 = csr6;
+
+ if(!wait)
+ return 0;
+
+ for(timeo = 0; timeo < 30; timeo++){
+ if(type0link(ctlr, block))
+ return 0;
+ delay(100);
+ }
+
+ return -1;
+}
+
+static int
+media21041(Ether* ether, int wait)
+{
+ Ctlr* ctlr;
+ uchar *block;
+ int csr6, csr13, csr14, csr15, medium, timeo;
+
+ ctlr = ether->ctlr;
+ block = ctlr->infoblock[ctlr->curk];
+ debug("media21041: block[0] %2.2uX, medium %4.4uX sct %4.4uX\n",
+ block[0], ctlr->medium, ctlr->sct);
+
+ medium = block[0] & 0x3F;
+ if(ctlr->medium >= 0 && medium != ctlr->medium)
+ return 0;
+ if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != medium)
+ return 0;
+
+ csr6 = Sc|Mbo|Ca|TrMODE|Sb;
+ if(block[0] & 0x40){
+ csr13 = (block[2]<<8)|block[1];
+ csr14 = (block[4]<<8)|block[3];
+ csr15 = (block[6]<<8)|block[5];
+ }
+ else switch(medium){
+ default:
+ return -1;
+ case 0x00: /* 10BASE-T */
+ csr13 = 0xEF01;
+ csr14 = 0xFF3F;
+ csr15 = 0x0008;
+ break;
+ case 0x01: /* 10BASE-2 */
+ csr13 = 0xEF09;
+ csr14 = 0xF73D;
+ csr15 = 0x0006;
+ break;
+ case 0x02: /* 10BASE-5 */
+ csr13 = 0xEF09;
+ csr14 = 0xF73D;
+ csr15 = 0x000E;
+ break;
+ case 0x04: /* 10BASE-TFD */
+ csr13 = 0xEF01;
+ csr14 = 0xFF3D;
+ csr15 = 0x0008;
+ break;
+ }
+
+ csr32w(ctlr, 13, 0);
+ csr32w(ctlr, 14, csr14);
+ csr32w(ctlr, 15, csr15);
+ csr32w(ctlr, 13, csr13);
+ delay(10);
+
+ if(medium == 0x04)
+ csr6 |= Fd;
+ ctlr->csr6 = csr6;
+ csr32w(ctlr, 6, ctlr->csr6);
+
+ debug("media21041: csr6 %8.8uX csr13 %4.4uX csr14 %4.4uX csr15 %4.4uX\n",
+ csr6, csr13, csr14, csr15);
+
+ if(!wait)
+ return 0;
+
+ for(timeo = 0; timeo < 30; timeo++){
+ if(!(csr32r(ctlr, 12) & 0x0002)){
+ debug("media21041: ok: csr12 %4.4luX timeo %d\n",
+ csr32r(ctlr, 12), timeo);
+ return 10;
+ }
+ delay(100);
+ }
+ debug("media21041: !ok: csr12 %4.4luX\n", csr32r(ctlr, 12));
+
+ return -1;
+}
+
+static int
+mediaxx(Ether* ether, int wait)
+{
+ Ctlr* ctlr;
+ uchar *block;
+
+ ctlr = ether->ctlr;
+ block = ctlr->infoblock[ctlr->curk];
+ if(block[0] & 0x80){
+ switch(block[1]){
+ default:
+ return -1;
+ case 0:
+ if(ctlr->medium >= 0 && block[2] != ctlr->medium)
+ return 0;
+/* need this test? */ if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != block[2])
+ return 0;
+ if(type0mode(ctlr, block+2, wait))
+ return 0;
+ break;
+ case 1:
+ if(typephymode(ctlr, block, wait))
+ return 0;
+ break;
+ case 2:
+ debug("type2: medium %d block[2] %d\n",
+ ctlr->medium, block[2]);
+ if(ctlr->medium >= 0 && ((block[2] & 0x3F) != ctlr->medium))
+ return 0;
+ if(type2mode(ctlr, block, wait))
+ return 0;
+ break;
+ case 3:
+ if(typephymode(ctlr, block, wait))
+ return 0;
+ break;
+ case 4:
+ debug("type4: medium %d block[2] %d\n",
+ ctlr->medium, block[2]);
+ if(ctlr->medium >= 0 && ((block[2] & 0x3F) != ctlr->medium))
+ return 0;
+ if(typesymmode(ctlr, block, wait))
+ return 0;
+ break;
+ }
+ }
+ else{
+ if(ctlr->medium >= 0 && block[0] != ctlr->medium)
+ return 0;
+/* need this test? */if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != block[0])
+ return 0;
+ if(type0mode(ctlr, block, wait))
+ return 0;
+ }
+
+ if(ctlr->csr6){
+ if(!(ctlr->csr6 & Ps) || (ctlr->csr6 & Ttm))
+ return 10;
+ return 100;
+ }
+
+ return 0;
+}
+
+static int
+media(Ether* ether, int wait)
+{
+ Ctlr* ctlr;
+ int k, mbps;
+
+ ctlr = ether->ctlr;
+ for(k = 0; k < ctlr->k; k++){
+ switch(ctlr->id){
+ default:
+ mbps = mediaxx(ether, wait);
+ break;
+ case Tulip1: /* 21041 */
+ mbps = media21041(ether, wait);
+ break;
+ }
+ if(mbps > 0)
+ return mbps;
+ if(ctlr->curk == 0)
+ ctlr->curk = ctlr->k-1;
+ else
+ ctlr->curk--;
+ }
+
+ return 0;
+}
+
+static char* mediatable[9] = {
+ "10BASE-T", /* TP */
+ "10BASE-2", /* BNC */
+ "10BASE-5", /* AUI */
+ "100BASE-TX",
+ "10BASE-TFD",
+ "100BASE-TXFD",
+ "100BASE-T4",
+ "100BASE-FX",
+ "100BASE-FXFD",
+};
+
+static uchar en1207[] = { /* Accton EN1207-COMBO */
+ 0x00, 0x00, 0xE8, /* [0] vendor ethernet code */
+ 0x00, /* [3] spare */
+
+ 0x00, 0x08, /* [4] connection (LSB+MSB = 0x0800) */
+ 0x1F, /* [6] general purpose control */
+ 2, /* [7] block count */
+
+ 0x00, /* [8] media code (10BASE-TX) */
+ 0x0B, /* [9] general purpose port data */
+ 0x9E, 0x00, /* [10] command (LSB+MSB = 0x009E) */
+
+ 0x03, /* [8] media code (100BASE-TX) */
+ 0x1B, /* [9] general purpose port data */
+ 0x6D, 0x00, /* [10] command (LSB+MSB = 0x006D) */
+
+ /* There is 10BASE-2 as well, but... */
+};
+
+static uchar ana6910fx[] = { /* Adaptec (Cogent) ANA-6910FX */
+ 0x00, 0x00, 0x92, /* [0] vendor ethernet code */
+ 0x00, /* [3] spare */
+
+ 0x00, 0x08, /* [4] connection (LSB+MSB = 0x0800) */
+ 0x3F, /* [6] general purpose control */
+ 1, /* [7] block count */
+
+ 0x07, /* [8] media code (100BASE-FX) */
+ 0x03, /* [9] general purpose port data */
+ 0x2D, 0x00 /* [10] command (LSB+MSB = 0x000D) */
+};
+
+static uchar smc9332[] = { /* SMC 9332 */
+ 0x00, 0x00, 0xC0, /* [0] vendor ethernet code */
+ 0x00, /* [3] spare */
+
+ 0x00, 0x08, /* [4] connection (LSB+MSB = 0x0800) */
+ 0x1F, /* [6] general purpose control */
+ 2, /* [7] block count */
+
+ 0x00, /* [8] media code (10BASE-TX) */
+ 0x00, /* [9] general purpose port data */
+ 0x9E, 0x00, /* [10] command (LSB+MSB = 0x009E) */
+
+ 0x03, /* [8] media code (100BASE-TX) */
+ 0x09, /* [9] general purpose port data */
+ 0x6D, 0x00, /* [10] command (LSB+MSB = 0x006D) */
+};
+
+static uchar* leaf21140[] = {
+ en1207, /* Accton EN1207-COMBO */
+ ana6910fx, /* Adaptec (Cogent) ANA-6910FX */
+ smc9332, /* SMC 9332 */
+ 0,
+};
+
+/*
+ * Copied to ctlr->srom at offset 20.
+ */
+static uchar leafpnic[] = {
+ 0x00, 0x00, 0x00, 0x00, /* MAC address */
+ 0x00, 0x00,
+ 0x00, /* controller 0 device number */
+ 0x1E, 0x00, /* controller 0 info leaf offset */
+ 0x00, /* reserved */
+ 0x00, 0x08, /* selected connection type */
+ 0x00, /* general purpose control */
+ 0x01, /* block count */
+
+ 0x8C, /* format indicator and count */
+ 0x01, /* block type */
+ 0x00, /* PHY number */
+ 0x00, /* GPR sequence length */
+ 0x00, /* reset sequence length */
+ 0x00, 0x78, /* media capabilities */
+ 0xE0, 0x01, /* Nway advertisment */
+ 0x00, 0x50, /* FDX bitmap */
+ 0x00, 0x18, /* TTM bitmap */
+};
+
+static int
+srom(Ctlr* ctlr)
+{
+ int i, k, oui, phy, x;
+ uchar *p;
+
+ /*
+ * This is a partial decoding of the SROM format described in
+ * 'Digital Semiconductor 21X4 Serial ROM Format, Version 4.05,
+ * 2-Mar-98'. Only the 2114[03] are handled, support for other
+ * controllers can be added as needed.
+ */
+ sromr(ctlr, 0);
+ if(ctlr->srom == nil)
+ ctlr->srom = malloc((1<<ctlr->sromsz)*sizeof(ushort));
+ for(i = 0; i < (1<<ctlr->sromsz); i++){
+ x = sromr(ctlr, i);
+ ctlr->srom[2*i] = x;
+ ctlr->srom[2*i+1] = x>>8;
+ }
+
+ if(DEBUG){
+ print("srom:");
+ for(i = 0; i < ((1<<ctlr->sromsz)*sizeof(ushort)); i++){
+ if(i && ((i & 0x0F) == 0))
+ print("\n ");
+ print(" %2.2uX", ctlr->srom[i]);
+ }
+ print("\n");
+ }
+
+ /*
+ * There are 2 SROM layouts:
+ * e.g. Digital EtherWORKS station address at offset 20;
+ * this complies with the 21140A SROM
+ * application note from Digital;
+ * e.g. SMC9332 station address at offset 0 followed by
+ * 2 additional bytes, repeated at offset
+ * 6; the 8 bytes are also repeated in
+ * reverse order at offset 8.
+ * To check which it is, read the SROM and check for the repeating
+ * patterns of the non-compliant cards; if that fails use the one at
+ * offset 20.
+ */
+ ctlr->sromea = ctlr->srom;
+ for(i = 0; i < 8; i++){
+ x = ctlr->srom[i];
+ if(x != ctlr->srom[15-i] || x != ctlr->srom[16+i]){
+ ctlr->sromea = &ctlr->srom[20];
+ break;
+ }
+ }
+
+ /*
+ * Fake up the SROM for the PNIC.
+ * It looks like a 21140 with a PHY.
+ * The MAC address is byte-swapped in the orginal SROM data.
+ */
+ if(ctlr->id == Pnic){
+ memmove(&ctlr->srom[20], leafpnic, sizeof(leafpnic));
+ for(i = 0; i < Eaddrlen; i += 2){
+ ctlr->srom[20+i] = ctlr->srom[i+1];
+ ctlr->srom[20+i+1] = ctlr->srom[i];
+ }
+ }
+
+ /*
+ * Next, try to find the info leaf in the SROM for media detection.
+ * If it's a non-conforming card try to match the vendor ethernet code
+ * and point p at a fake info leaf with compact 21140 entries.
+ */
+ if(ctlr->sromea == ctlr->srom){
+ p = nil;
+ for(i = 0; leaf21140[i] != nil; i++){
+ if(memcmp(leaf21140[i], ctlr->sromea, 3) == 0){
+ p = &leaf21140[i][4];
+ break;
+ }
+ }
+ if(p == nil)
+ return -1;
+ }
+ else
+ p = &ctlr->srom[(ctlr->srom[28]<<8)|ctlr->srom[27]];
+
+ /*
+ * Set up the info needed for later media detection.
+ * For the 21140, set the general-purpose mask in CSR12.
+ * The info block entries are stored in order of increasing
+ * precedence, so detection will work backwards through the
+ * stored indexes into ctlr->srom.
+ * If an entry is found which matches the selected connection
+ * type, save the index. Otherwise, start at the last entry.
+ * If any MII entries are found (type 1 and 3 blocks), scan
+ * for PHYs.
+ */
+ ctlr->leaf = p;
+ ctlr->sct = *p++;
+ ctlr->sct |= *p++<<8;
+ if(ctlr->id != Tulip3 && ctlr->id != Tulip1){
+ csr32w(ctlr, 12, Gpc|*p++);
+ delay(200);
+ }
+ ctlr->k = *p++;
+ if(ctlr->k >= nelem(ctlr->infoblock))
+ ctlr->k = nelem(ctlr->infoblock)-1;
+ ctlr->sctk = ctlr->k-1;
+ phy = 0;
+ for(k = 0; k < ctlr->k; k++){
+ ctlr->infoblock[k] = p;
+ if(ctlr->id == Tulip1){
+ debug("type21041: 0x%2.2uX\n", p[0]);
+ if(ctlr->sct != 0x0800 && *p == (ctlr->sct & 0xFF))
+ ctlr->sctk = k;
+ if(*p & 0x40)
+ p += 7;
+ else
+ p += 1;
+ }
+ /*
+ * The RAMIX PMC665 has a badly-coded SROM,
+ * hence the test for 21143 and type 3.
+ */
+ else if((*p & 0x80) || (ctlr->id == Tulip3 && *(p+1) == 3)){
+ *p |= 0x80;
+ if(*(p+1) == 1 || *(p+1) == 3)
+ phy = 1;
+ if(*(p+1) == 5)
+ ctlr->type5block = p;
+ p += (*p & ~0x80)+1;
+ }
+ else{
+ debug("type0: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n",
+ p[0], p[1], p[2], p[3]);
+ if(ctlr->sct != 0x0800 && *p == (ctlr->sct & 0xFF))
+ ctlr->sctk = k;
+ p += 4;
+ }
+ }
+ ctlr->curk = ctlr->sctk;
+ debug("sct 0x%uX medium 0x%uX k %d curk %d phy %d\n",
+ ctlr->sct, ctlr->medium, ctlr->k, ctlr->curk, phy);
+
+ if(phy){
+ x = 0;
+ for(k = 0; k < nelem(ctlr->phy); k++){
+ if((oui = miir(ctlr, k, 2)) == -1 || oui == 0)
+ continue;
+ if(DEBUG){
+ oui = (oui & 0x3FF)<<6;
+ oui |= miir(ctlr, k, 3)>>10;
+ miir(ctlr, k, 1);
+ debug("phy%d: index %d oui %uX reg1 %uX\n",
+ x, k, oui, miir(ctlr, k, 1));
+ USED(oui);
+ }
+ ctlr->phy[x] = k;
+ }
+ }
+
+ ctlr->fd = 0;
+ ctlr->medium = -1;
+
+ return 0;
+}
+
+static void
+dec2114xpci(void)
+{
+ Ctlr *ctlr;
+ Pcidev *p;
+ int x;
+
+ p = nil;
+ while(p = pcimatch(p, 0, 0)){
+ if(p->ccrb != 0x02 || p->ccru != 0)
+ continue;
+ switch((p->did<<16)|p->vid){
+ default:
+ continue;
+
+ case Tulip3: /* 21143 */
+ /*
+ * Exit sleep mode.
+ */
+ x = pcicfgr32(p, 0x40);
+ x &= ~0xC0000000;
+ pcicfgw32(p, 0x40, x);
+ /*FALLTHROUGH*/
+
+ case Pnic: /* PNIC */
+ case Pnic2: /* PNIC-II */
+ case Tulip0: /* 21140 */
+ case Tulip1: /* 21041 */
+ break;
+ }
+
+ /*
+ * bar[0] is the I/O port register address and
+ * bar[1] is the memory-mapped register address.
+ */
+ ctlr = malloc(sizeof(Ctlr));
+ ctlr->port = p->mem[0].bar & ~0x01;
+ ctlr->pcidev = p;
+ ctlr->id = (p->did<<16)|p->vid;
+ debug("2114x: type 0x%8.8uX rev 0x%4.4uX at port 0x%4.4uX\n",
+ ctlr->id, p->rid, ctlr->port);
+
+ /*
+ * Some cards (e.g. ANA-6910FX) seem to need the Ps bit
+ * set or they don't always work right after a hardware
+ * reset.
+ */
+ csr32w(ctlr, 6, Mbo|Ps);
+ softreset(ctlr);
+
+ if(srom(ctlr)){
+ free(ctlr);
+ break;
+ }
+
+ switch(ctlr->id){
+ default:
+ break;
+
+ case Pnic: /* PNIC */
+ /*
+ * Turn off the jabber timer.
+ */
+ csr32w(ctlr, 15, 0x00000001);
+ break;
+ }
+
+ if(ctlrhead != nil)
+ ctlrtail->next = ctlr;
+ else
+ ctlrhead = ctlr;
+ ctlrtail = ctlr;
+ }
+}
+
+static void
+detach(Ether* ether)
+{
+ softreset(ether->ctlr);
+}
+
+int
+ether2114xreset(Ether* ether)
+{
+ Ctlr *ctlr;
+ int i, x;
+ uchar ea[Eaddrlen];
+ static int scandone;
+
+ if(scandone == 0){
+ dec2114xpci();
+ scandone = 1;
+ }
+
+ /*
+ * Any adapter matches if no ether->port is supplied,
+ * otherwise the ports must match.
+ */
+ for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
+ if(ctlr->active)
+ continue;
+ if(ether->port == 0 || ether->port == ctlr->port){
+ ctlr->active = 1;
+ break;
+ }
+ }
+ if(ctlr == nil)
+ return -1;
+
+ ether->ctlr = ctlr;
+ ether->port = ctlr->port;
+ ether->irq = ctlr->pcidev->intl;
+ ether->tbdf = ctlr->pcidev->tbdf;
+
+ /*
+ * Check if the adapter's station address is to be overridden.
+ * If not, read it from the EEPROM and set in ether->ea prior to
+ * loading the station address in the hardware.
+ */
+ memset(ea, 0, Eaddrlen);
+ if(memcmp(ea, ether->ea, Eaddrlen) == 0)
+ memmove(ether->ea, ctlr->sromea, Eaddrlen);
+
+ /*
+ * Look for a medium override in case there's no autonegotiation
+ * (no MII) or the autonegotiation fails.
+ */
+ for(i = 0; i < ether->nopt; i++){
+ if(cistrcmp(ether->opt[i], "FD") == 0){
+ ctlr->fd = 1;
+ continue;
+ }
+ for(x = 0; x < nelem(mediatable); x++){
+ debug("compare <%s> <%s>\n", mediatable[x],
+ ether->opt[i]);
+ if(cistrcmp(mediatable[x], ether->opt[i]))
+ continue;
+ ctlr->medium = x;
+
+ switch(ctlr->medium){
+ default:
+ ctlr->fd = 0;
+ break;
+
+ case 0x04: /* 10BASE-TFD */
+ case 0x05: /* 100BASE-TXFD */
+ case 0x08: /* 100BASE-FXFD */
+ ctlr->fd = 1;
+ break;
+ }
+ break;
+ }
+ }
+
+ /*
+ * Determine media.
+ */
+ ctlr->mbps = media(ether, 1);
+
+ /*
+ * Initialise descriptor rings, ethernet address.
+ */
+ ctlr->nrdr = Nrde;
+ ctlr->ntdr = Ntde;
+ pcisetbme(ctlr->pcidev);
+ ctlrinit(ether);
+
+ /*
+ * Linkage to the generic ethernet driver.
+ */
+ ether->attach = attach;
+ ether->transmit = transmit;
+ ether->interrupt = interrupt;
+ ether->detach = detach;
+
+ return 0;
+}
diff --git a/os/boot/pc/ether589.c b/os/boot/pc/ether589.c
new file mode 100644
index 00000000..fc86f463
--- /dev/null
+++ b/os/boot/pc/ether589.c
@@ -0,0 +1,211 @@
+/*
+ * 3C589 and 3C562.
+ * To do:
+ * check xcvr10Base2 still works (is GlobalReset necessary?).
+ */
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "error.h"
+
+#include "etherif.h"
+
+enum { /* all windows */
+ CommandR = 0x000E,
+ IntStatusR = 0x000E,
+};
+
+enum { /* Commands */
+ GlobalReset = 0x0000,
+ SelectRegisterWindow = 0x0001,
+ RxReset = 0x0005,
+ TxReset = 0x000B,
+ AcknowledgeInterrupt = 0x000D,
+};
+
+enum { /* IntStatus bits */
+ commandInProgress = 0x1000,
+};
+
+#define COMMAND(port, cmd, a) outs((port)+CommandR, ((cmd)<<11)|(a))
+#define STATUS(port) ins((port)+IntStatusR)
+
+enum { /* Window 0 - setup */
+ Wsetup = 0x0000,
+ /* registers */
+ ManufacturerID = 0x0000, /* 3C5[08]*, 3C59[27] */
+ ProductID = 0x0002, /* 3C5[08]*, 3C59[27] */
+ ConfigControl = 0x0004, /* 3C5[08]*, 3C59[27] */
+ AddressConfig = 0x0006, /* 3C5[08]*, 3C59[27] */
+ ResourceConfig = 0x0008, /* 3C5[08]*, 3C59[27] */
+ EepromCommand = 0x000A,
+ EepromData = 0x000C,
+ /* AddressConfig Bits */
+ autoSelect9 = 0x0080,
+ xcvrMask9 = 0xC000,
+ /* ConfigControl bits */
+ Ena = 0x0001,
+ base10TAvailable9 = 0x0200,
+ coaxAvailable9 = 0x1000,
+ auiAvailable9 = 0x2000,
+ /* EepromCommand bits */
+ EepromReadRegister = 0x0080,
+ EepromBusy = 0x8000,
+};
+
+enum { /* Window 1 - operating set */
+ Wop = 0x0001,
+};
+
+enum { /* Window 3 - FIFO management */
+ Wfifo = 0x0003,
+ /* registers */
+ InternalConfig = 0x0000, /* 3C509B, 3C589, 3C59[0257] */
+ /* InternalConfig bits */
+ xcvr10BaseT = 0x00000000,
+ xcvr10Base2 = 0x00300000,
+};
+
+enum { /* Window 4 - diagnostic */
+ Wdiagnostic = 0x0004,
+ /* registers */
+ MediaStatus = 0x000A,
+ /* MediaStatus bits */
+ linkBeatDetect = 0x0800,
+};
+
+extern int elnk3reset(Ether*);
+
+static char *tcmpcmcia[] = {
+ "3C589", /* 3COM 589[ABCD] */
+ "3C562", /* 3COM 562 */
+ "589E", /* 3COM Megahertz 589E */
+ nil,
+};
+
+static int
+configASIC(Ether* ether, int port, int xcvr)
+{
+ int x;
+
+ /* set Window 0 configuration registers */
+ COMMAND(port, SelectRegisterWindow, Wsetup);
+ outs(port+ConfigControl, Ena);
+
+ /* IRQ must be 3 on 3C589/3C562 */
+ outs(port + ResourceConfig, 0x3F00);
+
+ x = ins(port+AddressConfig) & ~xcvrMask9;
+ x |= (xcvr>>20)<<14;
+ outs(port+AddressConfig, x);
+
+ COMMAND(port, TxReset, 0);
+ while(STATUS(port) & commandInProgress)
+ ;
+ COMMAND(port, RxReset, 0);
+ while(STATUS(port) & commandInProgress)
+ ;
+
+ return elnk3reset(ether);
+}
+
+int
+ether589reset(Ether* ether)
+{
+ int i, t, slot;
+ char *type;
+ int port;
+ enum { WantAny, Want10BT, Want10B2 };
+ int want;
+ uchar ea[6];
+ char *p;
+
+ if(ether->irq == 0)
+ ether->irq = 10;
+ if(ether->port == 0)
+ ether->port = 0x240;
+ port = ether->port;
+
+// if(ioalloc(port, 0x10, 0, "3C589") < 0)
+// return -1;
+
+ type = nil;
+ slot = -1;
+ for(i = 0; tcmpcmcia[i] != nil; i++){
+ type = tcmpcmcia[i];
+if(debug) print("try %s...", type);
+ if((slot = pcmspecial(type, ether)) >= 0)
+ break;
+ }
+ if(slot < 0){
+if(debug) print("none found\n");
+// iofree(port);
+ return -1;
+ }
+
+ /*
+ * Read Ethernet address from card memory
+ * on 3C562, but only if the user has not
+ * overridden it.
+ */
+ memset(ea, 0, sizeof ea);
+ if(memcmp(ea, ether->ea, 6) == 0 && strcmp(type, "3C562") == 0) {
+ if(debug)
+ print("read 562...");
+ if(pcmcistuple(slot, 0x88, ea, 6) == 6) {
+ for(i = 0; i < 6; i += 2){
+ t = ea[i];
+ ea[i] = ea[i+1];
+ ea[i+1] = t;
+ }
+ memmove(ether->ea, ea, 6);
+ if(debug)
+ print("ea %E", ea);
+ }
+ }
+ /*
+ * Allow user to specify desired media in plan9.ini
+ */
+ want = WantAny;
+ for(i = 0; i < ether->nopt; i++){
+ if(cistrncmp(ether->opt[i], "media=", 6) != 0)
+ continue;
+ p = ether->opt[i]+6;
+ if(cistrcmp(p, "10base2") == 0)
+ want = Want10B2;
+ else if(cistrcmp(p, "10baseT") == 0)
+ want = Want10BT;
+ }
+
+ /* try configuring as a 10BaseT */
+ if(want==WantAny || want==Want10BT){
+ if(configASIC(ether, port, xcvr10BaseT) < 0){
+ pcmspecialclose(slot);
+// iofree(port);
+ return -1;
+ }
+ delay(100);
+ COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+ if((ins(port+MediaStatus)&linkBeatDetect) || want==Want10BT){
+ COMMAND(port, SelectRegisterWindow, Wop);
+ print("#l%d: xcvr10BaseT %s\n", ether->ctlrno, type);
+ return 0;
+ }
+ }
+
+ /* try configuring as a 10base2 */
+ if(want==WantAny || want==Want10B2){
+ COMMAND(port, GlobalReset, 0);
+ if(configASIC(ether, port, xcvr10Base2) < 0){
+ pcmspecialclose(slot);
+// iofree(port);
+ return -1;
+ }
+ print("#l%d: xcvr10Base2 %s\n", ether->ctlrno, type);
+ return 0;
+ }
+ return -1; /* not reached */
+}
diff --git a/os/boot/pc/ether79c970.c b/os/boot/pc/ether79c970.c
new file mode 100644
index 00000000..6a171b24
--- /dev/null
+++ b/os/boot/pc/ether79c970.c
@@ -0,0 +1,539 @@
+/*
+ * AMD79C970
+ * PCnet-PCI Single-Chip Ethernet Controller for PCI Local Bus
+ * To do:
+ * finish this rewrite
+ */
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "etherif.h"
+
+enum {
+ Lognrdre = 6,
+ Nrdre = (1<<Lognrdre),/* receive descriptor ring entries */
+ Logntdre = 4,
+ Ntdre = (1<<Logntdre),/* transmit descriptor ring entries */
+
+ Rbsize = ETHERMAXTU+4, /* ring buffer size (+4 for CRC) */
+};
+
+enum { /* DWIO I/O resource map */
+ Aprom = 0x0000, /* physical address */
+ Rdp = 0x0010, /* register data port */
+ Rap = 0x0014, /* register address port */
+ Sreset = 0x0018, /* software reset */
+ Bdp = 0x001C, /* bus configuration register data port */
+};
+
+enum { /* CSR0 */
+ Init = 0x0001, /* begin initialisation */
+ Strt = 0x0002, /* enable chip */
+ Stop = 0x0004, /* disable chip */
+ Tdmd = 0x0008, /* transmit demand */
+ Txon = 0x0010, /* transmitter on */
+ Rxon = 0x0020, /* receiver on */
+ Iena = 0x0040, /* interrupt enable */
+ Intr = 0x0080, /* interrupt flag */
+ Idon = 0x0100, /* initialisation done */
+ Tint = 0x0200, /* transmit interrupt */
+ Rint = 0x0400, /* receive interrupt */
+ Merr = 0x0800, /* memory error */
+ Miss = 0x1000, /* missed frame */
+ Cerr = 0x2000, /* collision */
+ Babl = 0x4000, /* transmitter timeout */
+ Err = 0x8000, /* Babl|Cerr|Miss|Merr */
+};
+
+enum { /* CSR3 */
+ Bswp = 0x0004, /* byte swap */
+ Emba = 0x0008, /* enable modified back-off algorithm */
+ Dxmt2pd = 0x0010, /* disable transmit two part deferral */
+ Lappen = 0x0020, /* look-ahead packet processing enable */
+};
+
+enum { /* CSR4 */
+ ApadXmt = 0x0800, /* auto pad transmit */
+};
+
+enum { /* CSR15 */
+ Prom = 0x8000, /* promiscuous mode */
+};
+
+typedef struct { /* Initialisation Block */
+ ushort mode;
+ uchar rlen; /* upper 4 bits */
+ uchar tlen; /* upper 4 bits */
+ uchar padr[6];
+ uchar res[2];
+ uchar ladr[8];
+ ulong rdra;
+ ulong tdra;
+} Iblock;
+
+typedef struct { /* descriptor ring entry */
+ ulong addr;
+ ulong md1; /* status|bcnt */
+ ulong md2; /* rcc|rpc|mcnt */
+ void* data;
+} Dre;
+
+enum { /* md1 */
+ Enp = 0x01000000, /* end of packet */
+ Stp = 0x02000000, /* start of packet */
+ RxBuff = 0x04000000, /* buffer error */
+ Def = 0x04000000, /* deferred */
+ Crc = 0x08000000, /* CRC error */
+ One = 0x08000000, /* one retry needed */
+ Oflo = 0x10000000, /* overflow error */
+ More = 0x10000000, /* more than one retry needed */
+ Fram = 0x20000000, /* framing error */
+ RxErr = 0x40000000, /* Fram|Oflo|Crc|RxBuff */
+ TxErr = 0x40000000, /* Uflo|Lcol|Lcar|Rtry */
+ Own = 0x80000000,
+};
+
+enum { /* md2 */
+ Rtry = 0x04000000, /* failed after repeated retries */
+ Lcar = 0x08000000, /* loss of carrier */
+ Lcol = 0x10000000, /* late collision */
+ Uflo = 0x40000000, /* underflow error */
+ TxBuff = 0x80000000, /* buffer error */
+};
+
+typedef struct Ctlr Ctlr;
+struct Ctlr {
+ Lock;
+ int port;
+ Pcidev* pcidev;
+ Ctlr* next;
+ int active;
+
+ int init; /* initialisation in progress */
+ Iblock iblock;
+
+ Dre* rdr; /* receive descriptor ring */
+ int rdrx;
+
+ Dre* tdr; /* transmit descriptor ring */
+ int tdrh; /* host index into tdr */
+ int tdri; /* interface index into tdr */
+ int ntq; /* descriptors active */
+
+ ulong rxbuff; /* receive statistics */
+ ulong crc;
+ ulong oflo;
+ ulong fram;
+
+ ulong rtry; /* transmit statistics */
+ ulong lcar;
+ ulong lcol;
+ ulong uflo;
+ ulong txbuff;
+
+ ulong merr; /* bobf is such a whiner */
+ ulong miss;
+ ulong babl;
+
+ int (*ior)(Ctlr*, int);
+ void (*iow)(Ctlr*, int, int);
+};
+
+static Ctlr* ctlrhead;
+static Ctlr* ctlrtail;
+
+/*
+ * The Rdp, Rap, Sreset, Bdp ports are 32-bit port offset in the enumeration above.
+ * To get to 16-bit offsets, scale down with 0x10 staying the same.
+ */
+static int
+io16r(Ctlr* c, int r)
+{
+ if(r >= Rdp)
+ r = (r-Rdp)/2+Rdp;
+ return ins(c->port+r);
+}
+
+static void
+io16w(Ctlr* c, int r, int v)
+{
+ if(r >= Rdp)
+ r = (r-Rdp)/2+Rdp;
+ outs(c->port+r, v);
+}
+
+static int
+io32r(Ctlr* c, int r)
+{
+ return inl(c->port+r);
+}
+
+static void
+io32w(Ctlr* c, int r, int v)
+{
+ outl(c->port+r, v);
+}
+
+static void
+attach(Ether*)
+{
+}
+
+static void
+detach(Ether* ether)
+{
+ Ctlr *ctlr;
+
+ ctlr = ether->ctlr;
+ ctlr->iow(ctlr, Rdp, Iena|Stop);
+}
+
+static void
+ringinit(Ctlr* ctlr)
+{
+ Dre *dre;
+
+ /*
+ * Initialise the receive and transmit buffer rings.
+ * The ring entries must be aligned on 16-byte boundaries.
+ *
+ * This routine is protected by ctlr->init.
+ */
+ if(ctlr->rdr == 0){
+ ctlr->rdr = ialloc(Nrdre*sizeof(Dre), 0x10);
+ for(dre = ctlr->rdr; dre < &ctlr->rdr[Nrdre]; dre++){
+ dre->data = malloc(Rbsize);
+ dre->addr = PADDR(dre->data);
+ dre->md2 = 0;
+ dre->md1 = Own|(-Rbsize & 0xFFFF);
+ }
+ }
+ ctlr->rdrx = 0;
+
+ if(ctlr->tdr == 0)
+ ctlr->tdr = ialloc(Ntdre*sizeof(Dre), 0x10);
+ memset(ctlr->tdr, 0, Ntdre*sizeof(Dre));
+ ctlr->tdrh = ctlr->tdri = 0;
+}
+
+static void
+transmit(Ether* ether)
+{
+ Ctlr *ctlr;
+ Block *bp;
+ Dre *dre;
+ RingBuf *tb;
+
+ ctlr = ether->ctlr;
+
+ if(ctlr->init)
+ return;
+
+ while(ctlr->ntq < (Ntdre-1)){
+ tb = &ether->tb[ether->ti];
+ if(tb->owner != Interface)
+ break;
+
+ bp = allocb(tb->len);
+ memmove(bp->wp, tb->pkt, tb->len);
+ memmove(bp->wp+Eaddrlen, ether->ea, Eaddrlen);
+ bp->wp += tb->len;
+
+ /*
+ * Give ownership of the descriptor to the chip,
+ * increment the software ring descriptor pointer
+ * and tell the chip to poll.
+ * There's no need to pad to ETHERMINTU
+ * here as ApadXmt is set in CSR4.
+ */
+ dre = &ctlr->tdr[ctlr->tdrh];
+ dre->data = bp;
+ dre->addr = PADDR(bp->rp);
+ dre->md2 = 0;
+ dre->md1 = Own|Stp|Enp|Oflo|(-BLEN(bp) & 0xFFFF);
+ ctlr->ntq++;
+ ctlr->iow(ctlr, Rap, 0);
+ ctlr->iow(ctlr, Rdp, Iena|Tdmd);
+ ctlr->tdrh = NEXT(ctlr->tdrh, Ntdre);
+
+ tb->owner = Host;
+ ether->ti = NEXT(ether->ti, ether->ntb);
+ }
+}
+
+static void
+interrupt(Ureg*, void* arg)
+{
+ Ctlr *ctlr;
+ Ether *ether;
+ int csr0;
+ Dre *dre;
+ RingBuf *rb;
+
+ ether = arg;
+ ctlr = ether->ctlr;
+
+ /*
+ * Acknowledge all interrupts and whine about those that shouldn't
+ * happen.
+ */
+intrloop:
+ csr0 = ctlr->ior(ctlr, Rdp) & 0xFFFF;
+ ctlr->iow(ctlr, Rdp, Babl|Cerr|Miss|Merr|Rint|Tint|Iena);
+ if(csr0 & Merr)
+ ctlr->merr++;
+ if(csr0 & Miss)
+ ctlr->miss++;
+ if(csr0 & Babl)
+ ctlr->babl++;
+ //if(csr0 & (Babl|Miss|Merr))
+ // print("#l%d: csr0 = 0x%uX\n", ether->ctlrno, csr0);
+ if(!(csr0 & (Rint|Tint)))
+ return;
+
+ /*
+ * Receiver interrupt: run round the descriptor ring logging
+ * errors and passing valid receive data up to the higher levels
+ * until a descriptor is encountered still owned by the chip.
+ */
+ if(csr0 & Rint){
+ dre = &ctlr->rdr[ctlr->rdrx];
+ while(!(dre->md1 & Own)){
+ rb = &ether->rb[ether->ri];
+ if(dre->md1 & RxErr){
+ if(dre->md1 & RxBuff)
+ ctlr->rxbuff++;
+ if(dre->md1 & Crc)
+ ctlr->crc++;
+ if(dre->md1 & Oflo)
+ ctlr->oflo++;
+ if(dre->md1 & Fram)
+ ctlr->fram++;
+ }
+ else if(rb->owner == Interface){
+ rb->owner = Host;
+ rb->len = (dre->md2 & 0x0FFF)-4;
+ memmove(rb->pkt, dre->data, rb->len);
+ ether->ri = NEXT(ether->ri, ether->nrb);
+ }
+
+ /*
+ * Finished with this descriptor, reinitialise it,
+ * give it back to the chip, then on to the next...
+ */
+ dre->md2 = 0;
+ dre->md1 = Own|(-Rbsize & 0xFFFF);
+
+ ctlr->rdrx = NEXT(ctlr->rdrx, Nrdre);
+ dre = &ctlr->rdr[ctlr->rdrx];
+ }
+ }
+
+ /*
+ * Transmitter interrupt: wakeup anyone waiting for a free descriptor.
+ */
+ if(csr0 & Tint){
+ lock(ctlr);
+ while(ctlr->ntq){
+ dre = &ctlr->tdr[ctlr->tdri];
+ if(dre->md1 & Own)
+ break;
+
+ if(dre->md1 & TxErr){
+ if(dre->md2 & Rtry)
+ ctlr->rtry++;
+ if(dre->md2 & Lcar)
+ ctlr->lcar++;
+ if(dre->md2 & Lcol)
+ ctlr->lcol++;
+ if(dre->md2 & Uflo)
+ ctlr->uflo++;
+ if(dre->md2 & TxBuff)
+ ctlr->txbuff++;
+ }
+
+ freeb(dre->data);
+
+ ctlr->ntq--;
+ ctlr->tdri = NEXT(ctlr->tdri, Ntdre);
+ }
+ transmit(ether);
+ unlock(ctlr);
+ }
+ goto intrloop;
+}
+
+static void
+amd79c970pci(void)
+{
+ Ctlr *ctlr;
+ Pcidev *p;
+
+ p = nil;
+ while(p = pcimatch(p, 0x1022, 0x2000)){
+ ctlr = malloc(sizeof(Ctlr));
+ ctlr->port = p->mem[0].bar & ~0x01;
+ ctlr->pcidev = p;
+
+ if(ctlrhead != nil)
+ ctlrtail->next = ctlr;
+ else
+ ctlrhead = ctlr;
+ ctlrtail = ctlr;
+ }
+}
+
+int
+amd79c970reset(Ether* ether)
+{
+ int x;
+ uchar ea[Eaddrlen];
+ Ctlr *ctlr;
+
+ if(ctlrhead == nil)
+ amd79c970pci();
+
+ /*
+ * Any adapter matches if no port is supplied,
+ * otherwise the ports must match.
+ */
+ for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
+ if(ctlr->active)
+ continue;
+ if(ether->port == 0 || ether->port == ctlr->port){
+ ctlr->active = 1;
+ break;
+ }
+ }
+ if(ctlr == nil)
+ return -1;
+
+ /*
+ * Allocate a controller structure and start to initialise it.
+ */
+ ether->ctlr = ctlr;
+ ether->port = ctlr->port;
+ ether->irq = ctlr->pcidev->intl;
+ ether->tbdf = ctlr->pcidev->tbdf;
+ pcisetbme(ctlr->pcidev);
+ ilock(ctlr);
+ ctlr->init = 1;
+
+ io32r(ctlr, Sreset);
+ io16r(ctlr, Sreset);
+
+ if(io16w(ctlr, Rap, 0), io16r(ctlr, Rdp) == 4){
+ ctlr->ior = io16r;
+ ctlr->iow = io16w;
+ }else if(io32w(ctlr, Rap, 0), io32r(ctlr, Rdp) == 4){
+ ctlr->ior = io32r;
+ ctlr->iow = io32w;
+ }else{
+ print("#l%d: card doesn't talk right\n", ether->ctlrno);
+ iunlock(ctlr);
+ return -1;
+ }
+
+ ctlr->iow(ctlr, Rap, 88);
+ x = ctlr->ior(ctlr, Rdp);
+ ctlr->iow(ctlr, Rap, 89);
+ x |= ctlr->ior(ctlr, Rdp)<<16;
+
+ switch(x&0xFFFFFFF){
+ case 0x2420003: /* PCnet/PCI 79C970 */
+ case 0x2621003: /* PCnet/PCI II 79C970A */
+ break;
+ default:
+ print("unknown PCnet card version %.7ux\n", x&0xFFFFFFF);
+ iunlock(ctlr);
+ return -1;
+ }
+
+ /*
+ * Set the software style in BCR20 to be PCnet-PCI to ensure 32-bit access.
+ * Set the auto pad transmit in CSR4.
+ */
+ ctlr->iow(ctlr, Rap, 20);
+ ctlr->iow(ctlr, Bdp, 0x0002);
+
+ ctlr->iow(ctlr, Rap, 4);
+ x = ctlr->ior(ctlr, Rdp) & 0xFFFF;
+ ctlr->iow(ctlr, Rdp, ApadXmt|x);
+
+ ctlr->iow(ctlr, Rap, 0);
+
+ /*
+ * Check if the adapter's station address is to be overridden.
+ * If not, read it from the I/O-space and set in ether->ea prior to
+ * loading the station address in the initialisation block.
+ */
+ memset(ea, 0, Eaddrlen);
+ if(!memcmp(ea, ether->ea, Eaddrlen)){
+ x = ctlr->ior(ctlr, Aprom);
+ ether->ea[0] = x;
+ ether->ea[1] = x>>8;
+ if(ctlr->ior == io16r)
+ x = ctlr->ior(ctlr, Aprom+2);
+ else
+ x >>= 16;
+ ether->ea[2] = x;
+ ether->ea[3] = x>>8;
+ x = ctlr->ior(ctlr, Aprom+4);
+ ether->ea[4] = x;
+ ether->ea[5] = x>>8;
+ }
+
+ /*
+ * Start to fill in the initialisation block
+ * (must be DWORD aligned).
+ */
+ ctlr->iblock.rlen = Lognrdre<<4;
+ ctlr->iblock.tlen = Logntdre<<4;
+ memmove(ctlr->iblock.padr, ether->ea, sizeof(ctlr->iblock.padr));
+
+ ringinit(ctlr);
+ ctlr->iblock.rdra = PADDR(ctlr->rdr);
+ ctlr->iblock.tdra = PADDR(ctlr->tdr);
+
+ /*
+ * Point the chip at the initialisation block and tell it to go.
+ * Mask the Idon interrupt and poll for completion. Strt and interrupt
+ * enables will be set later when attaching to the network.
+ */
+ x = PADDR(&ctlr->iblock);
+ ctlr->iow(ctlr, Rap, 1);
+ ctlr->iow(ctlr, Rdp, x & 0xFFFF);
+ ctlr->iow(ctlr, Rap, 2);
+ ctlr->iow(ctlr, Rdp, (x>>16) & 0xFFFF);
+ ctlr->iow(ctlr, Rap, 3);
+ ctlr->iow(ctlr, Rdp, Idon);
+ ctlr->iow(ctlr, Rap, 0);
+ ctlr->iow(ctlr, Rdp, Init);
+
+ while(!(ctlr->ior(ctlr, Rdp) & Idon))
+ ;
+
+ /*
+ * We used to set CSR0 to Idon|Stop here, and then
+ * in attach change it to Iena|Strt. Apparently the simulated
+ * 79C970 in VMware never enables after a write of Idon|Stop,
+ * so we enable the device here now.
+ */
+ ctlr->iow(ctlr, Rdp, Iena|Strt);
+ ctlr->init = 0;
+ iunlock(ctlr);
+
+ /*
+ * Linkage to the generic ethernet driver.
+ */
+ ether->attach = attach;
+ ether->transmit = transmit;
+ ether->interrupt = interrupt;
+ ether->detach = detach;
+
+ return 0;
+}
diff --git a/os/boot/pc/ether8003.c b/os/boot/pc/ether8003.c
new file mode 100644
index 00000000..1ea9be21
--- /dev/null
+++ b/os/boot/pc/ether8003.c
@@ -0,0 +1,258 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "etherif.h"
+#include "ether8390.h"
+
+/*
+ * Western Digital/Standard Microsystems Corporation cards (WD80[01]3).
+ * Also handles 8216 cards (Elite Ultra).
+ * Configuration code based on that provided by SMC a long time ago.
+ */
+enum { /* 83C584 Bus Interface Controller */
+ Msr = 0x00, /* Memory Select Register */
+ Icr = 0x01, /* Interface Configuration Register */
+ Iar = 0x02, /* I/O Address Register */
+ Bio = 0x03, /* BIOS ROM Address Register */
+ Ear = 0x03, /* EEROM Address Register (shared with Bio) */
+ Irr = 0x04, /* Interrupt Request Register */
+ Hcr = 0x04, /* 8216 hardware control */
+ Laar = 0x05, /* LA Address Register */
+ Ijr = 0x06, /* Initialisation Jumpers */
+ Gp2 = 0x07, /* General Purpose Data Register */
+ Lar = 0x08, /* LAN Address Registers */
+ Id = 0x0E, /* Card ID byte */
+ Cksum = 0x0F, /* Checksum */
+};
+
+enum { /* Msr */
+ Rst = 0x80, /* software reset */
+ Menb = 0x40, /* memory enable */
+};
+
+enum { /* Icr */
+ Bit16 = 0x01, /* 16-bit bus */
+ Other = 0x02, /* other register access */
+ Ir2 = 0x04, /* IR2 */
+ Msz = 0x08, /* SRAM size */
+ Rla = 0x10, /* recall LAN address */
+ Rx7 = 0x20, /* recall all but I/O and LAN address */
+ Rio = 0x40, /* recall I/O address from EEROM */
+ Sto = 0x80, /* non-volatile EEROM store */
+};
+
+enum { /* Laar */
+ ZeroWS16 = 0x20, /* zero wait states for 16-bit ops */
+ L16en = 0x40, /* enable 16-bit LAN operation */
+ M16en = 0x80, /* enable 16-bit memory access */
+};
+
+enum { /* Ijr */
+ Ienable = 0x01, /* 8216 interrupt enable */
+};
+
+/*
+ * Mapping from configuration bits to interrupt level.
+ */
+static int irq8003[8] = {
+ 9, 3, 5, 7, 10, 11, 15, 4,
+};
+
+static int irq8216[8] = {
+ 0, 9, 3, 5, 7, 10, 11, 15,
+};
+
+static void
+reset8003(Ether* ether, uchar ea[Eaddrlen], uchar ic[8])
+{
+ Dp8390 *ctlr;
+ ulong port;
+
+ ctlr = ether->ctlr;
+ port = ether->port;
+
+ /*
+ * Check for old, dumb 8003E, which doesn't have an interface
+ * chip. Only Msr exists out of the 1st eight registers, reads
+ * of the others just alias the 2nd eight registers, the LAN
+ * address ROM. Can check Icr, Irr and Laar against the ethernet
+ * address read above and if they match it's an 8003E (or an
+ * 8003EBT, 8003S, 8003SH or 8003WT, doesn't matter), in which
+ * case the default irq gets used.
+ */
+ if(memcmp(&ea[1], &ic[1], 5) == 0){
+ memset(ic, 0, sizeof(ic));
+ ic[Msr] = (((ulong)ether->mem)>>13) & 0x3F;
+ }
+ else{
+ /*
+ * As a final sanity check for the 8013EBT, which doesn't have
+ * the 83C584 interface chip, but has 2 real registers, write Gp2
+ * and if it reads back the same, it's not an 8013EBT.
+ */
+ outb(port+Gp2, 0xAA);
+ inb(port+Msr); /* wiggle bus */
+ if(inb(port+Gp2) != 0xAA){
+ memset(ic, 0, sizeof(ic));
+ ic[Msr] = (((ulong)ether->mem)>>13) & 0x3F;
+ }
+ else
+ ether->irq = irq8003[((ic[Irr]>>5) & 0x3)|(ic[Icr] & 0x4)];
+
+ /*
+ * Check if 16-bit card.
+ * If Bit16 is read/write, then it's an 8-bit card.
+ * If Bit16 is set, it's in a 16-bit slot.
+ */
+ outb(port+Icr, ic[Icr]^Bit16);
+ inb(port+Msr); /* wiggle bus */
+ if((inb(port+Icr) & Bit16) == (ic[Icr] & Bit16)){
+ ctlr->width = 2;
+ ic[Icr] &= ~Bit16;
+ }
+ outb(port+Icr, ic[Icr]);
+
+ if(ctlr->width == 2 && (inb(port+Icr) & Bit16) == 0)
+ ctlr->width = 1;
+ }
+
+ ether->mem = (ulong)KADDR((ic[Msr] & 0x3F)<<13);
+ if(ctlr->width == 2)
+ ether->mem |= (ic[Laar] & 0x1F)<<19;
+ else
+ ether->mem |= 0x80000;
+
+ if(ic[Icr] & (1<<3))
+ ether->size = 32*1024;
+ if(ctlr->width == 2)
+ ether->size <<= 1;
+
+ /*
+ * Enable interface RAM, set interface width.
+ */
+ outb(port+Msr, ic[Msr]|Menb);
+ if(ctlr->width == 2)
+ outb(port+Laar, ic[Laar]|L16en|M16en|ZeroWS16);
+}
+
+static void
+reset8216(Ether* ether, uchar[8])
+{
+ uchar hcr, irq, x;
+ ulong addr, port;
+ Dp8390 *ctlr;
+
+ ctlr = ether->ctlr;
+ port = ether->port;
+
+ ctlr->width = 2;
+
+ /*
+ * Switch to the alternate register set and retrieve the memory
+ * and irq information.
+ */
+ hcr = inb(port+Hcr);
+ outb(port+Hcr, 0x80|hcr);
+ addr = inb(port+0x0B) & 0xFF;
+ irq = inb(port+0x0D);
+ outb(port+Hcr, hcr);
+
+ ether->mem = (ulong)KADDR(0xC0000+((((addr>>2) & 0x30)|(addr & 0x0F))<<13));
+ ether->size = 8192*(1<<((addr>>4) & 0x03));
+ ether->irq = irq8216[((irq>>4) & 0x04)|((irq>>2) & 0x03)];
+
+ /*
+ * Enable interface RAM, set interface width, and enable interrupts.
+ */
+ x = inb(port+Msr) & ~Rst;
+ outb(port+Msr, Menb|x);
+ x = inb(port+Laar);
+ outb(port+Laar, M16en|x);
+ outb(port+Ijr, Ienable);
+}
+
+/*
+ * Get configuration parameters, enable memory.
+ * There are opportunities here for buckets of code, try to resist.
+ */
+int
+wd8003reset(Ether* ether)
+{
+ int i;
+ uchar ea[Eaddrlen], ic[8], id, nullea[Eaddrlen], sum;
+ ulong port;
+ Dp8390 *ctlr;
+
+ /*
+ * Set up the software configuration.
+ * Use defaults for port, irq, mem and size if not specified.
+ * Defaults are set for the dumb 8003E which can't be
+ * autoconfigured.
+ */
+ if(ether->port == 0)
+ ether->port = 0x280;
+ if(ether->irq == 0)
+ ether->irq = 3;
+ if(ether->mem == 0)
+ ether->mem = 0xD0000;
+ if(ether->size == 0)
+ ether->size = 8*1024;
+
+ /*
+ * Look for the interface. Read the LAN address ROM
+ * and validate the checksum - the sum of all 8 bytes
+ * should be 0xFF.
+ * At the same time, get the (possible) interface chip
+ * registers, they'll be used later to check for aliasing.
+ */
+ port = ether->port;
+ sum = 0;
+ for(i = 0; i < sizeof(ea); i++){
+ ea[i] = inb(port+Lar+i);
+ sum += ea[i];
+ ic[i] = inb(port+i);
+ }
+ id = inb(port+Id);
+ sum += id;
+ sum += inb(port+Cksum);
+ if(sum != 0xFF)
+ return -1;
+
+ ether->ctlr = malloc(sizeof(Dp8390));
+ ctlr = ether->ctlr;
+ ctlr->ram = 1;
+
+ if((id & 0xFE) == 0x2A)
+ reset8216(ether, ic);
+ else
+ reset8003(ether, ea, ic);
+
+ /*
+ * Set the DP8390 ring addresses.
+ */
+ ctlr->port = port+0x10;
+ ctlr->tstart = 0;
+ ctlr->pstart = HOWMANY(sizeof(Etherpkt), Dp8390BufSz);
+ ctlr->pstop = HOWMANY(ether->size, Dp8390BufSz);
+
+ /*
+ * Finally, init the 8390, set the ethernet address
+ * and claim the memory used.
+ */
+ dp8390reset(ether);
+ memset(nullea, 0, Eaddrlen);
+ if(memcmp(nullea, ether->ea, Eaddrlen) == 0){
+ for(i = 0; i < sizeof(ether->ea); i++)
+ ether->ea[i] = ea[i];
+ }
+ dp8390setea(ether);
+
+ if(umbrwmalloc(PADDR(ether->mem), ether->size, 0) == 0)
+ print("ether8003: warning - 0x%luX unavailable", PADDR(ether->mem));
+
+ return 0;
+}
diff --git a/os/boot/pc/ether8139.c b/os/boot/pc/ether8139.c
new file mode 100644
index 00000000..650df1b3
--- /dev/null
+++ b/os/boot/pc/ether8139.c
@@ -0,0 +1,614 @@
+/*
+ * Realtek 8139 (but not the 8129).
+ * Error recovery for the various over/under -flow conditions
+ * may need work.
+ */
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "etherif.h"
+
+enum { /* registers */
+ Idr0 = 0x0000, /* MAC address */
+ Mar0 = 0x0008, /* Multicast address */
+ Tsd0 = 0x0010, /* Transmit Status Descriptor0 */
+ Tsad0 = 0x0020, /* Transmit Start Address Descriptor0 */
+ Rbstart = 0x0030, /* Receive Buffer Start Address */
+ Erbcr = 0x0034, /* Early Receive Byte Count */
+ Ersr = 0x0036, /* Early Receive Status */
+ Cr = 0x0037, /* Command Register */
+ Capr = 0x0038, /* Current Address of Packet Read */
+ Cbr = 0x003A, /* Current Buffer Address */
+ Imr = 0x003C, /* Interrupt Mask */
+ Isr = 0x003E, /* Interrupt Status */
+ Tcr = 0x0040, /* Transmit Configuration */
+ Rcr = 0x0044, /* Receive Configuration */
+ Tctr = 0x0048, /* Timer Count */
+ Mpc = 0x004C, /* Missed Packet Counter */
+ Cr9346 = 0x0050, /* 9346 Command Register */
+ Config0 = 0x0051, /* Configuration Register 0 */
+ Config1 = 0x0052, /* Configuration Register 1 */
+ TimerInt = 0x0054, /* Timer Interrupt */
+ Msr = 0x0058, /* Media Status */
+ Config3 = 0x0059, /* Configuration Register 3 */
+ Config4 = 0x005A, /* Configuration Register 4 */
+ Mulint = 0x005C, /* Multiple Interrupt Select */
+ RerID = 0x005E, /* PCI Revision ID */
+ Tsad = 0x0060, /* Transmit Status of all Descriptors */
+
+ Bmcr = 0x0062, /* Basic Mode Control */
+ Bmsr = 0x0064, /* Basic Mode Status */
+ Anar = 0x0066, /* Auto-Negotiation Advertisment */
+ Anlpar = 0x0068, /* Auto-Negotiation Link Partner */
+ Aner = 0x006A, /* Auto-Negotiation Expansion */
+ Dis = 0x006C, /* Disconnect Counter */
+ Fcsc = 0x006E, /* False Carrier Sense Counter */
+ Nwaytr = 0x0070, /* N-way Test */
+ Rec = 0x0072, /* RX_ER Counter */
+ Cscr = 0x0074, /* CS Configuration */
+ Phy1parm = 0x0078, /* PHY Parameter 1 */
+ Twparm = 0x007C, /* Twister Parameter */
+ Phy2parm = 0x0080, /* PHY Parameter 2 */
+};
+
+enum { /* Cr */
+ Bufe = 0x01, /* Rx Buffer Empty */
+ Te = 0x04, /* Transmitter Enable */
+ Re = 0x08, /* Receiver Enable */
+ Rst = 0x10, /* Software Reset */
+};
+
+enum { /* Imr/Isr */
+ Rok = 0x0001, /* Receive OK */
+ Rer = 0x0002, /* Receive Error */
+ Tok = 0x0004, /* Transmit OK */
+ Ter = 0x0008, /* Transmit Error */
+ Rxovw = 0x0010, /* Receive Buffer Overflow */
+ PunLc = 0x0020, /* Packet Underrun or Link Change */
+ Fovw = 0x0040, /* Receive FIFO Overflow */
+ Clc = 0x2000, /* Cable Length Change */
+ Timer = 0x4000, /* Timer */
+ Serr = 0x8000, /* System Error */
+};
+
+enum { /* Tcr */
+ Clrabt = 0x00000001, /* Clear Abort */
+ TxrrSHIFT = 4, /* Transmit Retry Count */
+ TxrrMASK = 0x000000F0,
+ MtxdmaSHIFT = 8, /* Max. DMA Burst Size */
+ MtxdmaMASK = 0x00000700,
+ Mtxdma2048 = 0x00000700,
+ Acrc = 0x00010000, /* Append CRC (not) */
+ LbkSHIFT = 17, /* Loopback Test */
+ LbkMASK = 0x00060000,
+ IfgSHIFT = 24, /* Interframe Gap */
+ IfgMASK = 0x03000000,
+ HwveridSHIFT = 22, /* Hardware Version ID */
+ HwveridMASK = 0x7CC00000,
+};
+
+enum { /* Rcr */
+ Aap = 0x00000001, /* Accept All Packets */
+ Apm = 0x00000002, /* Accept Physical Match */
+ Am = 0x00000004, /* Accept Multicast */
+ Ab = 0x00000008, /* Accept Broadcast */
+ Ar = 0x00000010, /* Accept Runt */
+ Aer = 0x00000020, /* Accept Error */
+ Sel9356 = 0x00000040, /* 9356 EEPROM used */
+ Wrap = 0x00000080, /* Rx Buffer Wrap Control */
+ MrxdmaSHIFT = 8, /* Max. DMA Burst Size */
+ MrxdmaMASK = 0x00000700,
+ Mrxdmaunlimited = 0x00000700,
+ RblenSHIFT = 11, /* Receive Buffer Length */
+ RblenMASK = 0x00001800,
+ Rblen8K = 0x00000000, /* 8KB+16 */
+ Rblen16K = 0x00000800, /* 16KB+16 */
+ Rblen32K = 0x00001000, /* 32KB+16 */
+ Rblen64K = 0x00001800, /* 64KB+16 */
+ RxfthSHIFT = 13, /* Receive Buffer Length */
+ RxfthMASK = 0x0000E000,
+ Rxfth256 = 0x00008000,
+ Rxfthnone = 0x0000E000,
+ Rer8 = 0x00010000, /* Accept Error Packets > 8 bytes */
+ MulERINT = 0x00020000, /* Multiple Early Interrupt Select */
+ ErxthSHIFT = 24, /* Early Rx Threshold */
+ ErxthMASK = 0x0F000000,
+ Erxthnone = 0x00000000,
+};
+
+enum { /* Received Packet Status */
+ Rcok = 0x0001, /* Receive Completed OK */
+ Fae = 0x0002, /* Frame Alignment Error */
+ Crc = 0x0004, /* CRC Error */
+ Long = 0x0008, /* Long Packet */
+ Runt = 0x0010, /* Runt Packet Received */
+ Ise = 0x0020, /* Invalid Symbol Error */
+ Bar = 0x2000, /* Broadcast Address Received */
+ Pam = 0x4000, /* Physical Address Matched */
+ Mar = 0x8000, /* Multicast Address Received */
+};
+
+enum { /* Media Status Register */
+ Rxpf = 0x01, /* Pause Flag */
+ Txpf = 0x02, /* Pause Flag */
+ Linkb = 0x04, /* Inverse of Link Status */
+ Speed10 = 0x08, /* 10Mbps */
+ Auxstatus = 0x10, /* Aux. Power Present Status */
+ Rxfce = 0x40, /* Receive Flow Control Enable */
+ Txfce = 0x80, /* Transmit Flow Control Enable */
+};
+
+typedef struct { /* Soft Transmit Descriptor */
+ int tsd;
+ int tsad;
+ uchar* data;
+} Td;
+
+enum { /* Tsd0 */
+ SizeSHIFT = 0, /* Descriptor Size */
+ SizeMASK = 0x00001FFF,
+ Own = 0x00002000,
+ Tun = 0x00004000, /* Transmit FIFO Underrun */
+ Tcok = 0x00008000, /* Transmit COmpleted OK */
+ EtxthSHIFT = 16, /* Early Tx Threshold */
+ EtxthMASK = 0x001F0000,
+ NccSHIFT = 24, /* Number of Collisions Count */
+ NccMASK = 0x0F000000,
+ Cdh = 0x10000000, /* CD Heartbeat */
+ Owc = 0x20000000, /* Out of Window Collision */
+ Tabt = 0x40000000, /* Transmit Abort */
+ Crs = 0x80000000, /* Carrier Sense Lost */
+};
+
+enum {
+ Rblen = Rblen64K, /* Receive Buffer Length */
+ Ntd = 4, /* Number of Transmit Descriptors */
+ Tdbsz = ROUNDUP(sizeof(Etherpkt), 4),
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Ctlr {
+ int port;
+ Pcidev* pcidev;
+ Ctlr* next;
+ int active;
+ int id;
+
+ Lock ilock; /* init */
+ void* alloc; /* base of per-Ctlr allocated data */
+
+ int rcr; /* receive configuration register */
+ uchar* rbstart; /* receive buffer */
+ int rblen; /* receive buffer length */
+ int ierrs; /* receive errors */
+
+ Lock tlock; /* transmit */
+ Td td[Ntd];
+ int ntd; /* descriptors active */
+ int tdh; /* host index into td */
+ int tdi; /* interface index into td */
+ int etxth; /* early transmit threshold */
+ int taligned; /* packet required no alignment */
+ int tunaligned; /* packet required alignment */
+
+ int dis; /* disconnect counter */
+ int fcsc; /* false carrier sense counter */
+ int rec; /* RX_ER counter */
+} Ctlr;
+
+static Ctlr* ctlrhead;
+static Ctlr* ctlrtail;
+
+#define csr8r(c, r) (inb((c)->port+(r)))
+#define csr16r(c, r) (ins((c)->port+(r)))
+#define csr32r(c, r) (inl((c)->port+(r)))
+#define csr8w(c, r, b) (outb((c)->port+(r), (int)(b)))
+#define csr16w(c, r, w) (outs((c)->port+(r), (ushort)(w)))
+#define csr32w(c, r, l) (outl((c)->port+(r), (ulong)(l)))
+
+static int
+rtl8139reset(Ctlr* ctlr)
+{
+ /*
+ * Soft reset the controller.
+ */
+ csr8w(ctlr, Cr, Rst);
+ while(csr8r(ctlr, Cr) & Rst)
+ ;
+
+ return 0;
+}
+
+static void
+rtl8139detach(Ether* edev)
+{
+ rtl8139reset(edev->ctlr);
+}
+
+static void
+rtl8139halt(Ctlr* ctlr)
+{
+ csr8w(ctlr, Cr, 0);
+ csr16w(ctlr, Imr, 0);
+ csr16w(ctlr, Isr, ~0);
+}
+
+static void
+rtl8139init(Ether* edev)
+{
+ int i;
+ ulong r;
+ Ctlr *ctlr;
+ uchar *alloc;
+
+ ctlr = edev->ctlr;
+ ilock(&ctlr->ilock);
+
+ rtl8139halt(ctlr);
+
+ /*
+ * MAC Address.
+ */
+ r = (edev->ea[3]<<24)|(edev->ea[2]<<16)|(edev->ea[1]<<8)|edev->ea[0];
+ csr32w(ctlr, Idr0, r);
+ r = (edev->ea[5]<<8)|edev->ea[4];
+ csr32w(ctlr, Idr0+4, r);
+
+ /*
+ * Receiver
+ */
+ alloc = (uchar*)ROUNDUP((ulong)ctlr->alloc, 32);
+ ctlr->rbstart = alloc;
+ alloc += ctlr->rblen+16;
+ memset(ctlr->rbstart, 0, ctlr->rblen+16);
+ csr32w(ctlr, Rbstart, PADDR(ctlr->rbstart));
+ ctlr->rcr = Rxfth256|Rblen|Mrxdmaunlimited|Ab|Apm;
+
+ /*
+ * Transmitter.
+ */
+ for(i = 0; i < Ntd; i++){
+ ctlr->td[i].tsd = Tsd0+i*4;
+ ctlr->td[i].tsad = Tsad0+i*4;
+ ctlr->td[i].data = alloc;
+ alloc += Tdbsz;
+ }
+ ctlr->ntd = ctlr->tdh = ctlr->tdi = 0;
+ ctlr->etxth = 128/32;
+
+ /*
+ * Interrupts.
+ */
+ csr32w(ctlr, TimerInt, 0);
+ csr16w(ctlr, Imr, Serr|Timer|Fovw|PunLc|Rxovw|Ter|Tok|Rer|Rok);
+ csr32w(ctlr, Mpc, 0);
+
+ /*
+ * Enable receiver/transmitter.
+ * Need to enable before writing the Rcr or it won't take.
+ */
+ csr8w(ctlr, Cr, Te|Re);
+ csr32w(ctlr, Tcr, Mtxdma2048);
+ csr32w(ctlr, Rcr, ctlr->rcr);
+
+ iunlock(&ctlr->ilock);
+}
+
+static void
+rtl8139attach(Ether* edev)
+{
+ Ctlr *ctlr;
+
+ ctlr = edev->ctlr;
+ if(ctlr->alloc == nil){
+ ctlr->rblen = 1<<((Rblen>>RblenSHIFT)+13);
+ ctlr->alloc = mallocz(ctlr->rblen+16 + Ntd*Tdbsz + 32, 0);
+ rtl8139init(edev);
+ }
+}
+
+static void
+rtl8139txstart(Ether* edev)
+{
+ Td *td;
+ Ctlr *ctlr;
+ RingBuf *tb;
+
+ ctlr = edev->ctlr;
+ while(ctlr->ntd < Ntd){
+ tb = &edev->tb[edev->ti];
+ if(tb->owner != Interface)
+ break;
+
+ td = &ctlr->td[ctlr->tdh];
+ memmove(td->data, tb->pkt, tb->len);
+ csr32w(ctlr, td->tsad, PADDR(tb->pkt));
+ csr32w(ctlr, td->tsd, (ctlr->etxth<<EtxthSHIFT)|tb->len);
+
+ ctlr->ntd++;
+ ctlr->tdh = NEXT(ctlr->tdh, Ntd);
+ tb->owner = Host;
+ edev->ti = NEXT(edev->ti, edev->ntb);
+ }
+}
+
+static void
+rtl8139transmit(Ether* edev)
+{
+ Ctlr *ctlr;
+
+ ctlr = edev->ctlr;
+ ilock(&ctlr->tlock);
+ rtl8139txstart(edev);
+ iunlock(&ctlr->tlock);
+}
+
+static void
+rtl8139receive(Ether* edev)
+{
+ Ctlr *ctlr;
+ RingBuf *rb;
+ ushort capr;
+ uchar cr, *p;
+ int l, length, status;
+
+ ctlr = edev->ctlr;
+
+ /*
+ * Capr is where the host is reading from,
+ * Cbr is where the NIC is currently writing.
+ */
+ capr = (csr16r(ctlr, Capr)+16) % ctlr->rblen;
+ while(!(csr8r(ctlr, Cr) & Bufe)){
+ p = ctlr->rbstart+capr;
+
+ /*
+ * Apparently the packet length may be 0xFFF0 if
+ * the NIC is still copying the packet into memory.
+ */
+ length = (*(p+3)<<8)|*(p+2);
+ if(length == 0xFFF0)
+ break;
+ status = (*(p+1)<<8)|*p;
+
+ if(!(status & Rcok)){
+ /*
+ * Reset the receiver.
+ * Also may have to restore the multicast list
+ * here too if it ever gets used.
+ */
+ cr = csr8r(ctlr, Cr);
+ csr8w(ctlr, Cr, cr & ~Re);
+ csr32w(ctlr, Rbstart, PADDR(ctlr->rbstart));
+ csr8w(ctlr, Cr, cr);
+ csr32w(ctlr, Rcr, ctlr->rcr);
+
+ continue;
+ }
+
+ /*
+ * Receive Completed OK.
+ * Very simplistic; there are ways this could be done
+ * without copying, but the juice probably isn't worth
+ * the squeeze.
+ * The packet length includes a 4 byte CRC on the end.
+ */
+ capr = (capr+4) % ctlr->rblen;
+ p = ctlr->rbstart+capr;
+ capr = (capr+length) % ctlr->rblen;
+
+ rb = &edev->rb[edev->ri];
+ l = 0;
+ if(p+length >= ctlr->rbstart+ctlr->rblen){
+ l = ctlr->rbstart+ctlr->rblen - p;
+ if(rb->owner == Interface)
+ memmove(rb->pkt, p, l);
+ length -= l;
+ p = ctlr->rbstart;
+ }
+ if(length > 0 && rb->owner == Interface){
+ memmove(rb->pkt+l, p, length);
+ l += length;
+ }
+ if(rb->owner == Interface){
+ rb->owner = Host;
+ rb->len = l-4;
+ edev->ri = NEXT(edev->ri, edev->nrb);
+ }
+
+ capr = ROUNDUP(capr, 4);
+ csr16w(ctlr, Capr, capr-16);
+ }
+}
+
+static void
+rtl8139interrupt(Ureg*, void* arg)
+{
+ Td *td;
+ Ctlr *ctlr;
+ Ether *edev;
+ int isr, tsd;
+
+ edev = arg;
+ ctlr = edev->ctlr;
+
+ while((isr = csr16r(ctlr, Isr)) != 0){
+ csr16w(ctlr, Isr, isr);
+ if(isr & (Fovw|PunLc|Rxovw|Rer|Rok)){
+ rtl8139receive(edev);
+ if(!(isr & Rok))
+ ctlr->ierrs++;
+ isr &= ~(Fovw|Rxovw|Rer|Rok);
+ }
+
+ if(isr & (Ter|Tok)){
+ ilock(&ctlr->tlock);
+ while(ctlr->ntd){
+ td = &ctlr->td[ctlr->tdi];
+ tsd = csr32r(ctlr, td->tsd);
+ if(!(tsd & (Tabt|Tun|Tcok)))
+ break;
+
+ if(!(tsd & Tcok)){
+ if(tsd & Tun){
+ if(ctlr->etxth < ETHERMAXTU/32)
+ ctlr->etxth++;
+ }
+ }
+
+ ctlr->ntd--;
+ ctlr->tdi = NEXT(ctlr->tdi, Ntd);
+ }
+ rtl8139txstart(edev);
+ iunlock(&ctlr->tlock);
+ isr &= ~(Ter|Tok);
+ }
+
+ if(isr & PunLc)
+ isr &= ~(Clc|PunLc);
+
+ /*
+ * Only Serr|Timer should be left by now.
+ * Should anything be done to tidy up? TimerInt isn't
+ * used so that can be cleared. A PCI bus error is indicated
+ * by Serr, that's pretty serious; is there anyhing to do
+ * other than try to reinitialise the chip?
+ */
+ if((isr & (Serr|Timer)) != 0){
+ print("rtl8139interrupt: imr %4.4uX isr %4.4uX\n",
+ csr16r(ctlr, Imr), isr);
+ if(isr & Timer)
+ csr32w(ctlr, TimerInt, 0);
+ if(isr & Serr)
+ rtl8139init(edev);
+ }
+ }
+}
+
+static Ctlr*
+rtl8139match(Ether* edev, int id)
+{
+ int port;
+ Pcidev *p;
+ Ctlr *ctlr;
+
+ /*
+ * Any adapter matches if no edev->port is supplied,
+ * otherwise the ports must match.
+ */
+ for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
+ if(ctlr->active)
+ continue;
+ p = ctlr->pcidev;
+ if(((p->did<<16)|p->vid) != id)
+ continue;
+ port = p->mem[0].bar & ~0x01;
+ if(edev->port != 0 && edev->port != port)
+ continue;
+
+ ctlr->port = port;
+ if(rtl8139reset(ctlr))
+ continue;
+ pcisetbme(p);
+
+ ctlr->active = 1;
+ return ctlr;
+ }
+ return nil;
+}
+
+static struct {
+ char* name;
+ int id;
+} rtl8139pci[] = {
+ { "rtl8139", (0x8139<<16)|0x10EC, }, /* generic */
+ { "smc1211", (0x1211<<16)|0x1113, }, /* SMC EZ-Card */
+ { "dfe-538tx", (0x1300<<16)|0x1186, }, /* D-Link DFE-538TX */
+ { "dfe-560txd", (0x1340<<16)|0x1186, }, /* D-Link DFE-560TXD */
+ { nil },
+};
+
+int
+rtl8139pnp(Ether* edev)
+{
+ int i, id;
+ Pcidev *p;
+ Ctlr *ctlr;
+ uchar ea[Eaddrlen];
+
+ /*
+ * Make a list of all ethernet controllers
+ * if not already done.
+ */
+ if(ctlrhead == nil){
+ p = nil;
+ while(p = pcimatch(p, 0, 0)){
+ if(p->ccrb != 0x02 || p->ccru != 0)
+ continue;
+ ctlr = malloc(sizeof(Ctlr));
+ ctlr->pcidev = p;
+ ctlr->id = (p->did<<16)|p->vid;
+
+ if(ctlrhead != nil)
+ ctlrtail->next = ctlr;
+ else
+ ctlrhead = ctlr;
+ ctlrtail = ctlr;
+ }
+ }
+
+ /*
+ * Is it an RTL8139 under a different name?
+ * Normally a search is made through all the found controllers
+ * for one which matches any of the known vid+did pairs.
+ * If a vid+did pair is specified a search is made for that
+ * specific controller only.
+ */
+ id = 0;
+ for(i = 0; i < edev->nopt; i++){
+ if(cistrncmp(edev->opt[i], "id=", 3) == 0)
+ id = strtol(&edev->opt[i][3], nil, 0);
+ }
+
+ ctlr = nil;
+ if(id != 0)
+ ctlr = rtl8139match(edev, id);
+ else for(i = 0; rtl8139pci[i].name; i++){
+ if((ctlr = rtl8139match(edev, rtl8139pci[i].id)) != nil)
+ break;
+ }
+ if(ctlr == nil)
+ return -1;
+
+ edev->ctlr = ctlr;
+ edev->port = ctlr->port;
+ edev->irq = ctlr->pcidev->intl;
+ edev->tbdf = ctlr->pcidev->tbdf;
+
+ /*
+ * Check if the adapter's station address is to be overridden.
+ * If not, read it from the device and set in edev->ea.
+ */
+ memset(ea, 0, Eaddrlen);
+ if(memcmp(ea, edev->ea, Eaddrlen) == 0){
+ i = csr32r(ctlr, Idr0);
+ edev->ea[0] = i;
+ edev->ea[1] = i>>8;
+ edev->ea[2] = i>>16;
+ edev->ea[3] = i>>24;
+ i = csr32r(ctlr, Idr0+4);
+ edev->ea[4] = i;
+ edev->ea[5] = i>>8;
+ }
+
+ edev->attach = rtl8139attach;
+ edev->transmit = rtl8139transmit;
+ edev->interrupt = rtl8139interrupt;
+ edev->detach = rtl8139detach;
+
+ return 0;
+}
diff --git a/os/boot/pc/ether8169.c b/os/boot/pc/ether8169.c
new file mode 100644
index 00000000..5b36f7c3
--- /dev/null
+++ b/os/boot/pc/ether8169.c
@@ -0,0 +1,847 @@
+/*
+ * Realtek RTL8110S/8169S.
+ * Mostly there. There are some magic register values used
+ * which are not described in any datasheet or driver but seem
+ * to be necessary.
+ * Why is the Fovf descriptor bit set for every received packet?
+ * Occasionally the hardware indicates an input TCP checksum error
+ * although the higher-level software seems to check the packet OK?
+ * No tuning has been done. Only tested on an RTL8110S, there
+ * are slight differences between the chips in the series so some
+ * tweaks may be needed.
+ */
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+
+typedef struct QLock { int r; } QLock;
+#define qlock(i) while(0)
+#define qunlock(i) while(0)
+#define iallocb allocb
+extern void mb386(void);
+#define coherence() mb386()
+#define iprint print
+#define mallocalign(n, a, o, s) ialloc((n), (a))
+
+#include "etherif.h"
+#include "ethermii.h"
+
+enum { /* registers */
+ Idr0 = 0x00, /* MAC address */
+ Mar0 = 0x08, /* Multicast address */
+ Dtccr = 0x10, /* Dump Tally Counter Command */
+ Tnpds = 0x20, /* Transmit Normal Priority Descriptors */
+ Thpds = 0x28, /* Transmit High Priority Descriptors */
+ Flash = 0x30, /* Flash Memory Read/Write */
+ Erbcr = 0x34, /* Early Receive Byte Count */
+ Ersr = 0x36, /* Early Receive Status */
+ Cr = 0x37, /* Command Register */
+ Tppoll = 0x38, /* Transmit Priority Polling */
+ Imr = 0x3C, /* Interrupt Mask */
+ Isr = 0x3E, /* Interrupt Status */
+ Tcr = 0x40, /* Transmit Configuration */
+ Rcr = 0x44, /* Receive Configuration */
+ Tctr = 0x48, /* Timer Count */
+ Mpc = 0x4C, /* Missed Packet Counter */
+ Cr9346 = 0x50, /* 9346 Command Register */
+ Config0 = 0x51, /* Configuration Register 0 */
+ Config1 = 0x52, /* Configuration Register 1 */
+ Config2 = 0x53, /* Configuration Register 2 */
+ Config3 = 0x54, /* Configuration Register 3 */
+ Config4 = 0x55, /* Configuration Register 4 */
+ Config5 = 0x56, /* Configuration Register 5 */
+ Timerint = 0x58, /* Timer Interrupt */
+ Mulint = 0x5C, /* Multiple Interrupt Select */
+ Phyar = 0x60, /* PHY Access */
+ Tbicsr0 = 0x64, /* TBI Control and Status */
+ Tbianar = 0x68, /* TBI Auto-Negotiation Advertisment */
+ Tbilpar = 0x6A, /* TBI Auto-Negotiation Link Partner */
+ Phystatus = 0x6C, /* PHY Status */
+
+ Rms = 0xDA, /* Receive Packet Maximum Size */
+ Cplusc = 0xE0, /* C+ Command */
+ Rdsar = 0xE4, /* Receive Descriptor Start Address */
+ Mtps = 0xEC, /* Max. Transmit Packet Size */
+};
+
+enum { /* Dtccr */
+ Cmd = 0x00000008, /* Command */
+};
+
+enum { /* Cr */
+ Te = 0x04, /* Transmitter Enable */
+ Re = 0x08, /* Receiver Enable */
+ Rst = 0x10, /* Software Reset */
+};
+
+enum { /* Tppoll */
+ Fswint = 0x01, /* Forced Software Interrupt */
+ Npq = 0x40, /* Normal Priority Queue polling */
+ Hpq = 0x80, /* High Priority Queue polling */
+};
+
+enum { /* Imr/Isr */
+ Rok = 0x0001, /* Receive OK */
+ Rer = 0x0002, /* Receive Error */
+ Tok = 0x0004, /* Transmit OK */
+ Ter = 0x0008, /* Transmit Error */
+ Rdu = 0x0010, /* Receive Descriptor Unavailable */
+ Punlc = 0x0020, /* Packet Underrun or Link Change */
+ Fovw = 0x0040, /* Receive FIFO Overflow */
+ Tdu = 0x0080, /* Transmit Descriptor Unavailable */
+ Swint = 0x0100, /* Software Interrupt */
+ Timeout = 0x4000, /* Timer */
+ Serr = 0x8000, /* System Error */
+};
+
+enum { /* Tcr */
+ MtxdmaSHIFT = 8, /* Max. DMA Burst Size */
+ MtxdmaMASK = 0x00000700,
+ Mtxdmaunlimited = 0x00000700,
+ Acrc = 0x00010000, /* Append CRC (not) */
+ Lbk0 = 0x00020000, /* Loopback Test 0 */
+ Lbk1 = 0x00040000, /* Loopback Test 1 */
+ Ifg2 = 0x00080000, /* Interframe Gap 2 */
+ HwveridSHIFT = 23, /* Hardware Version ID */
+ HwveridMASK = 0x7C800000,
+ Ifg0 = 0x01000000, /* Interframe Gap 0 */
+ Ifg1 = 0x02000000, /* Interframe Gap 1 */
+};
+
+enum { /* Rcr */
+ Aap = 0x00000001, /* Accept All Packets */
+ Apm = 0x00000002, /* Accept Physical Match */
+ Am = 0x00000004, /* Accept Multicast */
+ Ab = 0x00000008, /* Accept Broadcast */
+ Ar = 0x00000010, /* Accept Runt */
+ Aer = 0x00000020, /* Accept Error */
+ Sel9356 = 0x00000040, /* 9356 EEPROM used */
+ MrxdmaSHIFT = 8, /* Max. DMA Burst Size */
+ MrxdmaMASK = 0x00000700,
+ Mrxdmaunlimited = 0x00000700,
+ RxfthSHIFT = 13, /* Receive Buffer Length */
+ RxfthMASK = 0x0000E000,
+ Rxfth256 = 0x00008000,
+ Rxfthnone = 0x0000E000,
+ Rer8 = 0x00010000, /* Accept Error Packets > 8 bytes */
+ MulERINT = 0x01000000, /* Multiple Early Interrupt Select */
+};
+
+enum { /* Cr9346 */
+ Eedo = 0x01, /* */
+ Eedi = 0x02, /* */
+ Eesk = 0x04, /* */
+ Eecs = 0x08, /* */
+ Eem0 = 0x40, /* Operating Mode */
+ Eem1 = 0x80,
+};
+
+enum { /* Phyar */
+ DataMASK = 0x0000FFFF, /* 16-bit GMII/MII Register Data */
+ DataSHIFT = 0,
+ RegaddrMASK = 0x001F0000, /* 5-bit GMII/MII Register Address */
+ RegaddrSHIFT = 16,
+ Flag = 0x80000000, /* */
+};
+
+enum { /* Phystatus */
+ Fd = 0x01, /* Full Duplex */
+ Linksts = 0x02, /* Link Status */
+ Speed10 = 0x04, /* */
+ Speed100 = 0x08, /* */
+ Speed1000 = 0x10, /* */
+ Rxflow = 0x20, /* */
+ Txflow = 0x40, /* */
+ Entbi = 0x80, /* */
+};
+
+enum { /* Cplusc */
+ Mulrw = 0x0008, /* PCI Multiple R/W Enable */
+ Dac = 0x0010, /* PCI Dual Address Cycle Enable */
+ Rxchksum = 0x0020, /* Receive Checksum Offload Enable */
+ Rxvlan = 0x0040, /* Receive VLAN De-tagging Enable */
+ Endian = 0x0200, /* Endian Mode */
+};
+
+typedef struct D D; /* Transmit/Receive Descriptor */
+struct D {
+ u32int control;
+ u32int vlan;
+ u32int addrlo;
+ u32int addrhi;
+};
+
+enum { /* Transmit Descriptor control */
+ TxflMASK = 0x0000FFFF, /* Transmit Frame Length */
+ TxflSHIFT = 0,
+ Tcps = 0x00010000, /* TCP Checksum Offload */
+ Udpcs = 0x00020000, /* UDP Checksum Offload */
+ Ipcs = 0x00040000, /* IP Checksum Offload */
+ Lgsen = 0x08000000, /* Large Send */
+};
+
+enum { /* Receive Descriptor control */
+ RxflMASK = 0x00003FFF, /* Receive Frame Length */
+ RxflSHIFT = 0,
+ Tcpf = 0x00004000, /* TCP Checksum Failure */
+ Udpf = 0x00008000, /* UDP Checksum Failure */
+ Ipf = 0x00010000, /* IP Checksum Failure */
+ Pid0 = 0x00020000, /* Protocol ID0 */
+ Pid1 = 0x00040000, /* Protocol ID1 */
+ Crce = 0x00080000, /* CRC Error */
+ Runt = 0x00100000, /* Runt Packet */
+ Res = 0x00200000, /* Receive Error Summary */
+ Rwt = 0x00400000, /* Receive Watchdog Timer Expired */
+ Fovf = 0x00800000, /* FIFO Overflow */
+ Bovf = 0x01000000, /* Buffer Overflow */
+ Bar = 0x02000000, /* Broadcast Address Received */
+ Pam = 0x04000000, /* Physical Address Matched */
+ Mar = 0x08000000, /* Multicast Address Received */
+};
+
+enum { /* General Descriptor control */
+ Ls = 0x10000000, /* Last Segment Descriptor */
+ Fs = 0x20000000, /* First Segment Descriptor */
+ Eor = 0x40000000, /* End of Descriptor Ring */
+ Own = 0x80000000, /* Ownership */
+};
+
+/*
+ */
+enum { /* Ring sizes (<= 1024) */
+ Ntd = 8, /* Transmit Ring */
+ Nrd = 32, /* Receive Ring */
+
+ Mps = ROUNDUP(ETHERMAXTU+4, 128),
+};
+
+typedef struct Dtcc Dtcc;
+struct Dtcc {
+ u64int txok;
+ u64int rxok;
+ u64int txer;
+ u32int rxer;
+ u16int misspkt;
+ u16int fae;
+ u32int tx1col;
+ u32int txmcol;
+ u64int rxokph;
+ u64int rxokbrd;
+ u32int rxokmu;
+ u16int txabt;
+ u16int txundrn;
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Ctlr {
+ int port;
+ Pcidev* pcidev;
+ Ctlr* next;
+ int active;
+ uint id;
+
+ QLock alock; /* attach */
+ Lock ilock; /* init */
+ int init; /* */
+
+ Mii* mii;
+
+ Lock tlock; /* transmit */
+ D* td; /* descriptor ring */
+ Block** tb; /* transmit buffers */
+ int ntd;
+
+ int tdh; /* head - producer index (host) */
+ int tdt; /* tail - consumer index (NIC) */
+ int ntdfree;
+ int ntq;
+
+ int mtps; /* Max. Transmit Packet Size */
+
+ Lock rlock; /* receive */
+ D* rd; /* descriptor ring */
+ void** rb; /* receive buffers */
+ int nrd;
+
+ int rdh; /* head - producer index (NIC) */
+ int rdt; /* tail - consumer index (host) */
+ int nrdfree;
+
+ int rcr; /* receive configuration register */
+
+ QLock slock; /* statistics */
+ Dtcc* dtcc;
+ uint txdu;
+ uint tcpf;
+ uint udpf;
+ uint ipf;
+ uint fovf;
+ uint ierrs;
+ uint rer;
+ uint rdu;
+ uint punlc;
+ uint fovw;
+} Ctlr;
+
+static Ctlr* ctlrhead;
+static Ctlr* ctlrtail;
+
+#define csr8r(c, r) (inb((c)->port+(r)))
+#define csr16r(c, r) (ins((c)->port+(r)))
+#define csr32r(c, r) (inl((c)->port+(r)))
+#define csr8w(c, r, b) (outb((c)->port+(r), (int)(b)))
+#define csr16w(c, r, w) (outs((c)->port+(r), (ushort)(w)))
+#define csr32w(c, r, l) (outl((c)->port+(r), (ulong)(l)))
+
+static int
+rtl8169miimir(Mii* mii, int pa, int ra)
+{
+ uint r;
+ int timeo;
+ Ctlr *ctlr;
+
+ if(pa != 1)
+ return -1;
+ ctlr = mii->ctlr;
+
+ r = (ra<<16) & RegaddrMASK;
+ csr32w(ctlr, Phyar, r);
+ delay(1);
+ for(timeo = 0; timeo < 2000; timeo++){
+ if((r = csr32r(ctlr, Phyar)) & Flag)
+ break;
+ microdelay(100);
+ }
+ if(!(r & Flag))
+ return -1;
+
+ return (r & DataMASK)>>DataSHIFT;
+}
+
+static int
+rtl8169miimiw(Mii* mii, int pa, int ra, int data)
+{
+ uint r;
+ int timeo;
+ Ctlr *ctlr;
+
+ if(pa != 1)
+ return -1;
+ ctlr = mii->ctlr;
+
+ r = Flag|((ra<<16) & RegaddrMASK)|((data<<DataSHIFT) & DataMASK);
+ csr32w(ctlr, Phyar, r);
+ delay(1);
+ for(timeo = 0; timeo < 2000; timeo++){
+ if(!((r = csr32r(ctlr, Phyar)) & Flag))
+ break;
+ microdelay(100);
+ }
+ if(r & Flag)
+ return -1;
+
+ return 0;
+}
+
+static int
+rtl8169mii(Ctlr* ctlr)
+{
+ MiiPhy *phy;
+
+ /*
+ * Link management.
+ */
+ if((ctlr->mii = malloc(sizeof(Mii))) == nil)
+ return -1;
+ ctlr->mii->mir = rtl8169miimir;
+ ctlr->mii->miw = rtl8169miimiw;
+ ctlr->mii->ctlr = ctlr;
+ rtl8169miimiw(ctlr->mii, 1, 0x0B, 0x0000); /* magic */
+
+ if(mii(ctlr->mii, ~0) == 0 || (phy = ctlr->mii->curphy) == nil){
+ free(ctlr->mii);
+ ctlr->mii = nil;
+ return -1;
+ }
+ print("oui %X phyno %d\n", phy->oui, phy->phyno);
+
+ miiane(ctlr->mii, ~0, ~0, ~0);
+
+ return 0;
+}
+
+static int
+rtl8169reset(Ctlr* ctlr)
+{
+ int timeo;
+
+ /*
+ * Soft reset the controller.
+ */
+ csr8w(ctlr, Cr, Rst);
+ for(timeo = 0; timeo < 1000; timeo++){
+ if(!(csr8r(ctlr, Cr) & Rst))
+ return 0;
+ delay(1);
+ }
+
+ return -1;
+}
+
+static void
+rtl8169detach(Ether* edev)
+{
+ rtl8169reset(edev->ctlr);
+}
+
+static void
+rtl8169halt(Ctlr* ctlr)
+{
+ csr8w(ctlr, Cr, 0);
+ csr16w(ctlr, Imr, 0);
+ csr16w(ctlr, Isr, ~0);
+}
+
+static void
+rtl8169replenish(Ctlr* ctlr)
+{
+ D *d;
+ int rdt;
+ void *bp;
+
+ rdt = ctlr->rdt;
+ while(NEXT(rdt, ctlr->nrd) != ctlr->rdh){
+ d = &ctlr->rd[rdt];
+ if(ctlr->rb[rdt] == nil){
+ /*
+ * simple allocation for now
+ */
+ bp = malloc(Mps);
+ ctlr->rb[rdt] = bp;
+ d->addrlo = PCIWADDR(bp);
+ d->addrhi = 0;
+ }
+ coherence();
+ d->control |= Own|Mps;
+ rdt = NEXT(rdt, ctlr->nrd);
+ ctlr->nrdfree++;
+ }
+ ctlr->rdt = rdt;
+}
+
+static void
+rtl8169init(Ether* edev)
+{
+ uint r;
+ Ctlr *ctlr;
+
+ ctlr = edev->ctlr;
+ ilock(&ctlr->ilock);
+
+ rtl8169halt(ctlr);
+
+ /*
+ * Setting Mulrw in Cplusc disables the Tx/Rx DMA burst settings
+ * in Tcr/Rcr.
+ */
+ csr16w(ctlr, Cplusc, (1<<14)|Rxchksum|Mulrw); /* magic (1<<14) */
+
+ /*
+ * MAC Address.
+ * Must put chip into config register write enable mode.
+ */
+ csr8w(ctlr, Cr9346, Eem1|Eem0);
+ r = (edev->ea[3]<<24)|(edev->ea[2]<<16)|(edev->ea[1]<<8)|edev->ea[0];
+ csr32w(ctlr, Idr0, r);
+ r = (edev->ea[5]<<8)|edev->ea[4];
+ csr32w(ctlr, Idr0+4, r);
+
+ /*
+ * Enable receiver/transmitter.
+ * Need to do this first or some of the settings below
+ * won't take.
+ */
+ csr8w(ctlr, Cr, Te|Re);
+
+ /*
+ * Transmitter.
+ * Mtps is in units of 128.
+ */
+ memset(ctlr->td, 0, sizeof(D)*ctlr->ntd);
+ ctlr->tdh = ctlr->tdt = 0;
+ ctlr->td[ctlr->ntd-1].control = Eor;
+ ctlr->mtps = HOWMANY(Mps, 128);
+
+ /*
+ * Receiver.
+ */
+ memset(ctlr->rd, 0, sizeof(D)*ctlr->nrd);
+ ctlr->rdh = ctlr->rdt = 0;
+ ctlr->rd[ctlr->nrd-1].control = Eor;
+
+ //for(i = 0; i < ctlr->nrd; i++){
+ // if((bp = ctlr->rb[i]) != nil){
+ // ctlr->rb[i] = nil;
+ // freeb(bp);
+ // }
+ //}
+ rtl8169replenish(ctlr);
+ ctlr->rcr = Rxfth256|Mrxdmaunlimited|Ab|Apm;
+
+ /*
+ * Interrupts.
+ * Disable Tdu|Tok for now, the transmit routine will tidy.
+ * Tdu means the NIC ran out of descritors to send, so it
+ * doesn't really need to ever be on.
+ */
+ csr32w(ctlr, Timerint, 0);
+ csr16w(ctlr, Imr, Serr|Timeout/*|Tdu*/|Fovw|Punlc|Rdu|Ter/*|Tok*/|Rer|Rok);
+
+ /*
+ * Clear missed-packet counter;
+ * initial early transmit threshold value;
+ * set the descriptor ring base addresses;
+ * set the maximum receive packet size;
+ * no early-receive interrupts.
+ */
+ csr32w(ctlr, Mpc, 0);
+ csr8w(ctlr, Mtps, ctlr->mtps);
+ csr32w(ctlr, Tnpds+4, 0);
+ csr32w(ctlr, Tnpds, PCIWADDR(ctlr->td));
+ csr32w(ctlr, Rdsar+4, 0);
+ csr32w(ctlr, Rdsar, PCIWADDR(ctlr->rd));
+ csr16w(ctlr, Rms, Mps);
+ csr16w(ctlr, Mulint, 0);
+
+ /*
+ * Set configuration.
+ */
+ csr32w(ctlr, Tcr, Ifg1|Ifg0|Mtxdmaunlimited);
+ csr32w(ctlr, Rcr, ctlr->rcr);
+ csr16w(ctlr, 0xE2, 0); /* magic */
+
+ csr8w(ctlr, Cr9346, 0);
+
+ iunlock(&ctlr->ilock);
+
+ //rtl8169mii(ctlr);
+}
+
+static void
+rtl8169attach(Ether* edev)
+{
+ int timeo;
+ Ctlr *ctlr;
+
+ ctlr = edev->ctlr;
+ qlock(&ctlr->alock);
+ if(ctlr->init == 0){
+ /*
+ * Handle allocation/init errors here.
+ */
+ ctlr->td = xspanalloc(sizeof(D)*Ntd, 256, 0);
+ ctlr->tb = malloc(Ntd*sizeof(Block*));
+ ctlr->ntd = Ntd;
+ ctlr->rd = xspanalloc(sizeof(D)*Nrd, 256, 0);
+ ctlr->rb = malloc(Nrd*sizeof(Block*));
+ ctlr->nrd = Nrd;
+ ctlr->dtcc = xspanalloc(sizeof(Dtcc), 64, 0);
+ rtl8169init(edev);
+ ctlr->init = 1;
+ }
+ qunlock(&ctlr->alock);
+
+ for(timeo = 0; timeo < 3500; timeo++){
+ if(miistatus(ctlr->mii) == 0)
+ break;
+ delay(10);
+ }
+}
+
+static void
+rtl8169transmit(Ether* edev)
+{
+ D *d;
+ Block *bp;
+ Ctlr *ctlr;
+ int control, x;
+ RingBuf *tb;
+
+ ctlr = edev->ctlr;
+
+ ilock(&ctlr->tlock);
+ for(x = ctlr->tdh; ctlr->ntq > 0; x = NEXT(x, ctlr->ntd)){
+ d = &ctlr->td[x];
+ if((control = d->control) & Own)
+ break;
+
+ /*
+ * Check errors and log here.
+ */
+ USED(control);
+
+ /*
+ * Free it up.
+ * Need to clean the descriptor here? Not really.
+ * Simple freeb for now (no chain and freeblist).
+ * Use ntq count for now.
+ */
+ freeb(ctlr->tb[x]);
+ ctlr->tb[x] = nil;
+ d->control &= Eor;
+
+ ctlr->ntq--;
+ }
+ ctlr->tdh = x;
+
+ x = ctlr->tdt;
+ while(ctlr->ntq < (ctlr->ntd-1)){
+ tb = &edev->tb[edev->ti];
+ if(tb->owner != Interface)
+ break;
+
+ bp = allocb(tb->len);
+ memmove(bp->wp, tb->pkt, tb->len);
+ memmove(bp->wp+Eaddrlen, edev->ea, Eaddrlen);
+ bp->wp += tb->len;
+
+ tb->owner = Host;
+ edev->ti = NEXT(edev->ti, edev->ntb);
+
+ d = &ctlr->td[x];
+ d->addrlo = PCIWADDR(bp->rp);
+ d->addrhi = 0;
+ ctlr->tb[x] = bp;
+ coherence();
+ d->control |= Own|Fs|Ls|((BLEN(bp)<<TxflSHIFT) & TxflMASK);
+
+ x = NEXT(x, ctlr->ntd);
+ ctlr->ntq++;
+ }
+ if(x != ctlr->tdt){
+ ctlr->tdt = x;
+ csr8w(ctlr, Tppoll, Npq);
+ }
+ else if(ctlr->ntq >= (ctlr->ntd-1))
+ ctlr->txdu++;
+
+ iunlock(&ctlr->tlock);
+}
+
+static void
+rtl8169receive(Ether* edev)
+{
+ D *d;
+ int len, rdh;
+ Ctlr *ctlr;
+ u32int control;
+ RingBuf *ring;
+
+ ctlr = edev->ctlr;
+
+ rdh = ctlr->rdh;
+ for(;;){
+ d = &ctlr->rd[rdh];
+
+ if(d->control & Own)
+ break;
+
+ control = d->control;
+ if((control & (Fs|Ls|Res)) == (Fs|Ls)){
+ len = ((control & RxflMASK)>>RxflSHIFT) - 4;
+
+ ring = &edev->rb[edev->ri];
+ if(ring->owner == Interface){
+ ring->owner = Host;
+ ring->len = len;
+ memmove(ring->pkt, ctlr->rb[rdh], len);
+ edev->ri = NEXT(edev->ri, edev->nrb);
+ }
+ }
+ else{
+ /*
+ * Error stuff here.
+ print("control %8.8uX\n", control);
+ */
+ }
+ d->control &= Eor;
+ ctlr->nrdfree--;
+ rdh = NEXT(rdh, ctlr->nrd);
+ }
+ ctlr->rdh = rdh;
+
+ if(ctlr->nrdfree < ctlr->nrd/2)
+ rtl8169replenish(ctlr);
+}
+
+static void
+rtl8169interrupt(Ureg*, void* arg)
+{
+ Ctlr *ctlr;
+ Ether *edev;
+ u32int isr;
+
+ edev = arg;
+ ctlr = edev->ctlr;
+
+ while((isr = csr16r(ctlr, Isr)) != 0){
+ csr16w(ctlr, Isr, isr);
+ if(isr & (Fovw|Punlc|Rdu|Rer|Rok)){
+ rtl8169receive(edev);
+ if(!(isr & (Punlc|Rok)))
+ ctlr->ierrs++;
+ if(isr & Rer)
+ ctlr->rer++;
+ if(isr & Rdu)
+ ctlr->rdu++;
+ if(isr & Punlc)
+ ctlr->punlc++;
+ if(isr & Fovw)
+ ctlr->fovw++;
+ isr &= ~(Fovw|Rdu|Rer|Rok);
+ }
+
+ if(isr & (Tdu|Ter|Tok)){
+ rtl8169transmit(edev);
+ isr &= ~(Tdu|Ter|Tok);
+ }
+
+ if(isr & Punlc){
+ //rtl8169link(edev);
+ isr &= ~Punlc;
+ }
+
+ /*
+ * Some of the reserved bits get set sometimes...
+ */
+ if(isr & (Serr|Timeout|Tdu|Fovw|Punlc|Rdu|Ter|Tok|Rer|Rok))
+ panic("rtl8169interrupt: imr %4.4uX isr %4.4uX\n",
+ csr16r(ctlr, Imr), isr);
+ }
+}
+
+static Ctlr*
+rtl8169match(Ether* edev, int id)
+{
+ Pcidev *p;
+ Ctlr *ctlr;
+ int port;
+
+ /*
+ * Any adapter matches if no edev->port is supplied,
+ * otherwise the ports must match.
+ */
+ for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
+ if(ctlr->active)
+ continue;
+ p = ctlr->pcidev;
+ if(((p->did<<16)|p->vid) != id)
+ continue;
+ port = p->mem[0].bar & ~0x01;
+ if(edev->port != 0 && edev->port != port)
+ continue;
+
+ ctlr->port = port;
+ if(rtl8169reset(ctlr))
+ continue;
+
+ csr8w(ctlr, 0x82, 1); /* magic */
+
+ rtl8169mii(ctlr);
+
+ pcisetbme(p);
+ ctlr->active = 1;
+ return ctlr;
+ }
+ return nil;
+}
+
+static struct {
+ char* name;
+ int id;
+} rtl8169pci[] = {
+ { "rtl8169", (0x8169<<16)|0x10EC, }, /* generic */
+ { "CG-LAPCIGT", (0xC107<<16)|0x1259, }, /* Corega CG-LAPCIGT */
+ { nil },
+};
+
+int
+rtl8169pnp(Ether* edev)
+{
+ Pcidev *p;
+ Ctlr *ctlr;
+ int i, id;
+ uchar ea[Eaddrlen];
+
+ /*
+ * Make a list of all ethernet controllers
+ * if not already done.
+ */
+ if(ctlrhead == nil){
+ p = nil;
+ while(p = pcimatch(p, 0, 0)){
+ if(p->ccrb != 0x02 || p->ccru != 0)
+ continue;
+ ctlr = malloc(sizeof(Ctlr));
+ ctlr->pcidev = p;
+ ctlr->id = (p->did<<16)|p->vid;
+
+ if(ctlrhead != nil)
+ ctlrtail->next = ctlr;
+ else
+ ctlrhead = ctlr;
+ ctlrtail = ctlr;
+ }
+ }
+
+ /*
+ * Is it an RTL8169 under a different name?
+ * Normally a search is made through all the found controllers
+ * for one which matches any of the known vid+did pairs.
+ * If a vid+did pair is specified a search is made for that
+ * specific controller only.
+ */
+ id = 0;
+ for(i = 0; i < edev->nopt; i++){
+ if(cistrncmp(edev->opt[i], "id=", 3) == 0)
+ id = strtol(&edev->opt[i][3], nil, 0);
+ }
+
+ ctlr = nil;
+ if(id != 0)
+ ctlr = rtl8169match(edev, id);
+ else for(i = 0; rtl8169pci[i].name; i++){
+ if((ctlr = rtl8169match(edev, rtl8169pci[i].id)) != nil)
+ break;
+ }
+ if(ctlr == nil)
+ return -1;
+
+ edev->ctlr = ctlr;
+ edev->port = ctlr->port;
+ edev->irq = ctlr->pcidev->intl;
+ edev->tbdf = ctlr->pcidev->tbdf;
+
+ /*
+ */
+ memset(ea, 0, Eaddrlen);
+ i = csr32r(ctlr, Idr0);
+ edev->ea[0] = i;
+ edev->ea[1] = i>>8;
+ edev->ea[2] = i>>16;
+ edev->ea[3] = i>>24;
+ i = csr32r(ctlr, Idr0+4);
+ edev->ea[4] = i;
+ edev->ea[5] = i>>8;
+
+ edev->attach = rtl8169attach;
+ edev->transmit = rtl8169transmit;
+ edev->interrupt = rtl8169interrupt;
+ edev->detach = rtl8169detach;
+
+ return 0;
+}
diff --git a/os/boot/pc/ether82557.c b/os/boot/pc/ether82557.c
new file mode 100644
index 00000000..1876692c
--- /dev/null
+++ b/os/boot/pc/ether82557.c
@@ -0,0 +1,881 @@
+/*
+ * Intel 82557 Fast Ethernet PCI Bus LAN Controller
+ * as found on the Intel EtherExpress PRO/100B. This chip is full
+ * of smarts, unfortunately none of them are in the right place.
+ * To do:
+ * the PCI scanning code could be made common to other adapters;
+ * PCI code needs rewritten to handle byte, word, dword accesses
+ * and using the devno as a bus+dev+function triplet.
+ */
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "etherif.h"
+
+enum {
+ Nrfd = 4, /* receive frame area */
+
+ NullPointer = 0xFFFFFFFF, /* 82557 NULL pointer */
+};
+
+enum { /* CSR */
+ Status = 0x00, /* byte or word (word includes Ack) */
+ Ack = 0x01, /* byte */
+ CommandR = 0x02, /* byte or word (word includes Interrupt) */
+ Interrupt = 0x03, /* byte */
+ Pointer = 0x04, /* dword */
+ Port = 0x08, /* dword */
+ Fcr = 0x0C, /* Flash control register */
+ Ecr = 0x0E, /* EEPROM control register */
+ Mcr = 0x10, /* MDI control register */
+};
+
+enum { /* Status */
+ RUidle = 0x0000,
+ RUsuspended = 0x0004,
+ RUnoresources = 0x0008,
+ RUready = 0x0010,
+ RUrbd = 0x0020, /* bit */
+ RUstatus = 0x003F, /* mask */
+
+ CUidle = 0x0000,
+ CUsuspended = 0x0040,
+ CUactive = 0x0080,
+ CUstatus = 0x00C0, /* mask */
+
+ StatSWI = 0x0400, /* SoftWare generated Interrupt */
+ StatMDI = 0x0800, /* MDI r/w done */
+ StatRNR = 0x1000, /* Receive unit Not Ready */
+ StatCNA = 0x2000, /* Command unit Not Active (Active->Idle) */
+ StatFR = 0x4000, /* Finished Receiving */
+ StatCX = 0x8000, /* Command eXecuted */
+ StatTNO = 0x8000, /* Transmit NOT OK */
+};
+
+enum { /* Command (byte) */
+ CUnop = 0x00,
+ CUstart = 0x10,
+ CUresume = 0x20,
+ LoadDCA = 0x40, /* Load Dump Counters Address */
+ DumpSC = 0x50, /* Dump Statistical Counters */
+ LoadCUB = 0x60, /* Load CU Base */
+ ResetSA = 0x70, /* Dump and Reset Statistical Counters */
+
+ RUstart = 0x01,
+ RUresume = 0x02,
+ RUabort = 0x04,
+ LoadHDS = 0x05, /* Load Header Data Size */
+ LoadRUB = 0x06, /* Load RU Base */
+ RBDresume = 0x07, /* Resume frame reception */
+};
+
+enum { /* Interrupt (byte) */
+ InterruptM = 0x01, /* interrupt Mask */
+ InterruptSI = 0x02, /* Software generated Interrupt */
+};
+
+enum { /* Ecr */
+ EEsk = 0x01, /* serial clock */
+ EEcs = 0x02, /* chip select */
+ EEdi = 0x04, /* serial data in */
+ EEdo = 0x08, /* serial data out */
+
+ EEstart = 0x04, /* start bit */
+ EEread = 0x02, /* read opcode */
+};
+
+enum { /* Mcr */
+ MDIread = 0x08000000, /* read opcode */
+ MDIwrite = 0x04000000, /* write opcode */
+ MDIready = 0x10000000, /* ready bit */
+ MDIie = 0x20000000, /* interrupt enable */
+};
+
+typedef struct Rfd {
+ int field;
+ ulong link;
+ ulong rbd;
+ ushort count;
+ ushort size;
+
+ Etherpkt;
+} Rfd;
+
+enum { /* field */
+ RfdCollision = 0x00000001,
+ RfdIA = 0x00000002, /* IA match */
+ RfdRxerr = 0x00000010, /* PHY character error */
+ RfdType = 0x00000020, /* Type frame */
+ RfdRunt = 0x00000080,
+ RfdOverrun = 0x00000100,
+ RfdBuffer = 0x00000200,
+ RfdAlignment = 0x00000400,
+ RfdCRC = 0x00000800,
+
+ RfdOK = 0x00002000, /* frame received OK */
+ RfdC = 0x00008000, /* reception Complete */
+ RfdSF = 0x00080000, /* Simplified or Flexible (1) Rfd */
+ RfdH = 0x00100000, /* Header RFD */
+
+ RfdI = 0x20000000, /* Interrupt after completion */
+ RfdS = 0x40000000, /* Suspend after completion */
+ RfdEL = 0x80000000, /* End of List */
+};
+
+enum { /* count */
+ RfdF = 0x00004000,
+ RfdEOF = 0x00008000,
+};
+
+typedef struct Cb {
+ int command;
+ ulong link;
+ uchar data[24]; /* CbIAS + CbConfigure */
+} Cb;
+
+typedef struct TxCB {
+ int command;
+ ulong link;
+ ulong tbd;
+ ushort count;
+ uchar threshold;
+ uchar number;
+} TxCB;
+
+enum { /* action command */
+ CbOK = 0x00002000, /* DMA completed OK */
+ CbC = 0x00008000, /* execution Complete */
+
+ CbNOP = 0x00000000,
+ CbIAS = 0x00010000, /* Indvidual Address Setup */
+ CbConfigure = 0x00020000,
+ CbMAS = 0x00030000, /* Multicast Address Setup */
+ CbTransmit = 0x00040000,
+ CbDump = 0x00060000,
+ CbDiagnose = 0x00070000,
+ CbCommand = 0x00070000, /* mask */
+
+ CbSF = 0x00080000, /* CbTransmit */
+
+ CbI = 0x20000000, /* Interrupt after completion */
+ CbS = 0x40000000, /* Suspend after completion */
+ CbEL = 0x80000000, /* End of List */
+};
+
+enum { /* CbTransmit count */
+ CbEOF = 0x00008000,
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Ctlr {
+ int port;
+ Pcidev* pcidev;
+ Ctlr* next;
+ int active;
+
+ int eepromsz; /* address size in bits */
+ ushort* eeprom;
+
+ int ctlrno;
+ char* type;
+
+ uchar configdata[24];
+
+ Rfd rfd[Nrfd];
+ int rfdl;
+ int rfdx;
+
+ Block* cbqhead;
+ Block* cbqtail;
+ int cbqbusy;
+} Ctlr;
+
+static Ctlr* ctlrhead;
+static Ctlr* ctlrtail;
+
+static uchar configdata[24] = {
+ 0x16, /* byte count */
+ 0x44, /* Rx/Tx FIFO limit */
+ 0x00, /* adaptive IFS */
+ 0x00,
+ 0x04, /* Rx DMA maximum byte count */
+ 0x84, /* Tx DMA maximum byte count */
+ 0x33, /* late SCB, CNA interrupts */
+ 0x01, /* discard short Rx frames */
+ 0x00, /* 503/MII */
+
+ 0x00,
+ 0x2E, /* normal operation, NSAI */
+ 0x00, /* linear priority */
+ 0x60, /* inter-frame spacing */
+ 0x00,
+ 0xF2,
+ 0x48, /* promiscuous mode off */
+ 0x00,
+ 0x40,
+ 0xF2, /* transmit padding enable */
+ 0x80, /* full duplex pin enable */
+ 0x3F, /* no Multi IA */
+ 0x05, /* no Multi Cast ALL */
+};
+
+#define csr8r(c, r) (inb((c)->port+(r)))
+#define csr16r(c, r) (ins((c)->port+(r)))
+#define csr32r(c, r) (inl((c)->port+(r)))
+#define csr8w(c, r, b) (outb((c)->port+(r), (int)(b)))
+#define csr16w(c, r, w) (outs((c)->port+(r), (ushort)(w)))
+#define csr32w(c, r, l) (outl((c)->port+(r), (ulong)(l)))
+
+static void
+custart(Ctlr* ctlr)
+{
+ if(ctlr->cbqhead == 0){
+ ctlr->cbqbusy = 0;
+ return;
+ }
+ ctlr->cbqbusy = 1;
+
+ csr32w(ctlr, Pointer, PADDR(ctlr->cbqhead->rp));
+ while(csr8r(ctlr, CommandR))
+ ;
+ csr8w(ctlr, CommandR, CUstart);
+}
+
+static void
+action(Ctlr* ctlr, Block* bp)
+{
+ Cb *cb;
+
+ cb = (Cb*)bp->rp;
+ cb->command |= CbEL;
+
+ if(ctlr->cbqhead){
+ ctlr->cbqtail->next = bp;
+ cb = (Cb*)ctlr->cbqtail->rp;
+ cb->link = PADDR(bp->rp);
+ cb->command &= ~CbEL;
+ }
+ else
+ ctlr->cbqhead = bp;
+ ctlr->cbqtail = bp;
+
+ if(ctlr->cbqbusy == 0)
+ custart(ctlr);
+}
+
+static void
+attach(Ether* ether)
+{
+ int status;
+ Ctlr *ctlr;
+
+ ctlr = ether->ctlr;
+ status = csr16r(ctlr, Status);
+ if((status & RUstatus) == RUidle){
+ csr32w(ctlr, Pointer, PADDR(&ctlr->rfd[ctlr->rfdx]));
+ while(csr8r(ctlr, CommandR))
+ ;
+ csr8w(ctlr, CommandR, RUstart);
+ }
+}
+
+static void
+configure(void* arg, int promiscuous)
+{
+ Ctlr *ctlr;
+ Block *bp;
+ Cb *cb;
+
+ ctlr = ((Ether*)arg)->ctlr;
+
+ bp = allocb(sizeof(Cb));
+ cb = (Cb*)bp->rp;
+ bp->wp += sizeof(Cb);
+
+ cb->command = CbConfigure;
+ cb->link = NullPointer;
+ memmove(cb->data, ctlr->configdata, sizeof(ctlr->configdata));
+ if(promiscuous)
+ cb->data[15] |= 0x01;
+ action(ctlr, bp);
+}
+
+static void
+transmit(Ether* ether)
+{
+ Block *bp;
+ TxCB *txcb;
+ RingBuf *tb;
+
+ for(tb = &ether->tb[ether->ti]; tb->owner == Interface; tb = &ether->tb[ether->ti]){
+ bp = allocb(tb->len+sizeof(TxCB));
+ txcb = (TxCB*)bp->wp;
+ bp->wp += sizeof(TxCB);
+
+ txcb->command = CbTransmit;
+ txcb->link = NullPointer;
+ txcb->tbd = NullPointer;
+ txcb->count = CbEOF|tb->len;
+ txcb->threshold = 2;
+ txcb->number = 0;
+
+ memmove(bp->wp, tb->pkt, tb->len);
+ memmove(bp->wp+Eaddrlen, ether->ea, Eaddrlen);
+ bp->wp += tb->len;
+
+ action(ether->ctlr, bp);
+
+ tb->owner = Host;
+ ether->ti = NEXT(ether->ti, ether->ntb);
+ }
+}
+
+static void
+interrupt(Ureg*, void* arg)
+{
+ Rfd *rfd;
+ Block *bp;
+ Ctlr *ctlr;
+ Ether *ether;
+ int status;
+ RingBuf *rb;
+
+ ether = arg;
+ ctlr = ether->ctlr;
+
+ for(;;){
+ status = csr16r(ctlr, Status);
+ csr8w(ctlr, Ack, (status>>8) & 0xFF);
+
+ if((status & (StatCX|StatFR|StatCNA|StatRNR)) == 0)
+ return;
+
+ if(status & StatFR){
+ rfd = &ctlr->rfd[ctlr->rfdx];
+ while(rfd->field & RfdC){
+ rb = &ether->rb[ether->ri];
+ if(rb->owner == Interface){
+ rb->owner = Host;
+ rb->len = rfd->count & 0x3FFF;
+ memmove(rb->pkt, rfd->d, rfd->count & 0x3FFF);
+ ether->ri = NEXT(ether->ri, ether->nrb);
+ }
+
+ /*
+ * Reinitialise the frame for reception and bump
+ * the receive frame processing index;
+ * bump the sentinel index, mark the new sentinel
+ * and clear the old sentinel suspend bit;
+ * set bp and rfd for the next receive frame to
+ * process.
+ */
+ rfd->field = 0;
+ rfd->count = 0;
+ ctlr->rfdx = NEXT(ctlr->rfdx, Nrfd);
+
+ rfd = &ctlr->rfd[ctlr->rfdl];
+ ctlr->rfdl = NEXT(ctlr->rfdl, Nrfd);
+ ctlr->rfd[ctlr->rfdl].field |= RfdS;
+ rfd->field &= ~RfdS;
+
+ rfd = &ctlr->rfd[ctlr->rfdx];
+ }
+ status &= ~StatFR;
+ }
+
+ if(status & StatRNR){
+ while(csr8r(ctlr, CommandR))
+ ;
+ csr8w(ctlr, CommandR, RUresume);
+
+ status &= ~StatRNR;
+ }
+
+ if(status & StatCNA){
+ while(bp = ctlr->cbqhead){
+ if((((Cb*)bp->rp)->command & CbC) == 0)
+ break;
+ ctlr->cbqhead = bp->next;
+ freeb(bp);
+ }
+ custart(ctlr);
+
+ status &= ~StatCNA;
+ }
+
+ if(status & (StatCX|StatFR|StatCNA|StatRNR|StatMDI|StatSWI))
+ panic("%s#%d: status %uX\n", ctlr->type, ctlr->ctlrno, status);
+ }
+}
+
+static void
+ctlrinit(Ctlr* ctlr)
+{
+ int i;
+ Rfd *rfd;
+ ulong link;
+
+ link = NullPointer;
+ for(i = Nrfd-1; i >= 0; i--){
+ rfd = &ctlr->rfd[i];
+
+ rfd->field = 0;
+ rfd->link = link;
+ link = PADDR(rfd);
+ rfd->rbd = NullPointer;
+ rfd->count = 0;
+ rfd->size = sizeof(Etherpkt);
+ }
+ ctlr->rfd[Nrfd-1].link = PADDR(&ctlr->rfd[0]);
+
+ ctlr->rfdl = 0;
+ ctlr->rfd[0].field |= RfdS;
+ ctlr->rfdx = 2;
+
+ memmove(ctlr->configdata, configdata, sizeof(configdata));
+}
+
+static int
+miir(Ctlr* ctlr, int phyadd, int regadd)
+{
+ int mcr, timo;
+
+ csr32w(ctlr, Mcr, MDIread|(phyadd<<21)|(regadd<<16));
+ mcr = 0;
+ for(timo = 64; timo; timo--){
+ mcr = csr32r(ctlr, Mcr);
+ if(mcr & MDIready)
+ break;
+ microdelay(1);
+ }
+
+ if(mcr & MDIready)
+ return mcr & 0xFFFF;
+
+ return -1;
+}
+
+static int
+miiw(Ctlr* ctlr, int phyadd, int regadd, int data)
+{
+ int mcr, timo;
+
+ csr32w(ctlr, Mcr, MDIwrite|(phyadd<<21)|(regadd<<16)|(data & 0xFFFF));
+ mcr = 0;
+ for(timo = 64; timo; timo--){
+ mcr = csr32r(ctlr, Mcr);
+ if(mcr & MDIready)
+ break;
+ microdelay(1);
+ }
+
+ if(mcr & MDIready)
+ return 0;
+
+ return -1;
+}
+
+static int
+hy93c46r(Ctlr* ctlr, int r)
+{
+ int data, i, op, size;
+
+ /*
+ * Hyundai HY93C46 or equivalent serial EEPROM.
+ * This sequence for reading a 16-bit register 'r'
+ * in the EEPROM is taken straight from Section
+ * 3.3.4.2 of the Intel 82557 User's Guide.
+ */
+reread:
+ csr16w(ctlr, Ecr, EEcs);
+ op = EEstart|EEread;
+ for(i = 2; i >= 0; i--){
+ data = (((op>>i) & 0x01)<<2)|EEcs;
+ csr16w(ctlr, Ecr, data);
+ csr16w(ctlr, Ecr, data|EEsk);
+ microdelay(1);
+ csr16w(ctlr, Ecr, data);
+ microdelay(1);
+ }
+
+ /*
+ * First time through must work out the EEPROM size.
+ */
+ if((size = ctlr->eepromsz) == 0)
+ size = 8;
+
+ for(size = size-1; size >= 0; size--){
+ data = (((r>>size) & 0x01)<<2)|EEcs;
+ csr16w(ctlr, Ecr, data);
+ csr16w(ctlr, Ecr, data|EEsk);
+ delay(1);
+ csr16w(ctlr, Ecr, data);
+ microdelay(1);
+ if(!(csr16r(ctlr, Ecr) & EEdo))
+ break;
+ }
+
+ data = 0;
+ for(i = 15; i >= 0; i--){
+ csr16w(ctlr, Ecr, EEcs|EEsk);
+ microdelay(1);
+ if(csr16r(ctlr, Ecr) & EEdo)
+ data |= (1<<i);
+ csr16w(ctlr, Ecr, EEcs);
+ microdelay(1);
+ }
+
+ csr16w(ctlr, Ecr, 0);
+
+ if(ctlr->eepromsz == 0){
+ ctlr->eepromsz = 8-size;
+ ctlr->eeprom = malloc((1<<ctlr->eepromsz)*sizeof(ushort));
+ goto reread;
+ }
+
+ return data;
+}
+
+static void
+i82557pci(void)
+{
+ Pcidev *p;
+ Ctlr *ctlr;
+
+ p = nil;
+ while(p = pcimatch(p, 0x8086, 0)){
+ switch(p->did){
+ default:
+ continue;
+ case 0x1031: /* Intel 82562EM */
+ case 0x1050: /* Intel 82562EZ */
+ case 0x2449: /* Intel 82562ET */
+ case 0x1209: /* Intel 82559ER */
+ case 0x1229: /* Intel 8255[789] */
+ case 0x1030: /* Intel 82559 InBusiness 10/100 */
+ case 0x1039: /* Intel 82801BD PRO/100 VE */
+ case 0x103A: /* Intel 82562 PRO/100 VE */
+ break;
+ }
+
+ /*
+ * bar[0] is the memory-mapped register address (4KB),
+ * bar[1] is the I/O port register address (32 bytes) and
+ * bar[2] is for the flash ROM (1MB).
+ */
+ ctlr = malloc(sizeof(Ctlr));
+ ctlr->port = p->mem[1].bar & ~0x01;
+ ctlr->pcidev = p;
+
+ if(ctlrhead != nil)
+ ctlrtail->next = ctlr;
+ else
+ ctlrhead = ctlr;
+ ctlrtail = ctlr;
+
+ pcisetbme(p);
+ }
+}
+
+static void
+detach(Ether* ether)
+{
+ Ctlr *ctlr;
+
+ ctlr = ether->ctlr;
+
+ csr32w(ctlr, Port, 0);
+ delay(1);
+
+ while(csr8r(ctlr, CommandR))
+ ;
+}
+
+static int
+scanphy(Ctlr* ctlr)
+{
+ int i, oui, x;
+
+ for(i = 0; i < 32; i++){
+ if((oui = miir(ctlr, i, 2)) == -1 || oui == 0 || oui == 0xFFFF)
+ continue;
+ oui <<= 6;
+ x = miir(ctlr, i, 3);
+ oui |= x>>10;
+ //print("phy%d: oui %uX reg1 %uX\n", i, oui, miir(ctlr, i, 1));
+
+ if(oui == 0xAA00)
+ ctlr->eeprom[6] = 0x07<<8;
+ else if(oui == 0x80017){
+ if(x & 0x01)
+ ctlr->eeprom[6] = 0x0A<<8;
+ else
+ ctlr->eeprom[6] = 0x04<<8;
+ }
+ return i;
+ }
+ return -1;
+}
+
+int
+i82557reset(Ether* ether)
+{
+ int anar, anlpar, bmcr, bmsr, force, i, phyaddr, x;
+ unsigned short sum;
+ Block *bp;
+ uchar ea[Eaddrlen];
+ Ctlr *ctlr;
+ Cb *cb;
+
+
+ if(ctlrhead == nil)
+ i82557pci();
+
+ /*
+ * Any adapter matches if no ether->port is supplied,
+ * otherwise the ports must match.
+ */
+ for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
+ if(ctlr->active)
+ continue;
+ if(ether->port == 0 || ether->port == ctlr->port){
+ ctlr->active = 1;
+ break;
+ }
+ }
+ if(ctlr == nil)
+ return -1;
+
+ /*
+ * Initialise the Ctlr structure.
+ * Perform a software reset after which need to ensure busmastering
+ * is still enabled. The EtherExpress PRO/100B appears to leave
+ * the PCI configuration alone (see the 'To do' list above) so punt
+ * for now.
+ * Load the RUB and CUB registers for linear addressing (0).
+ */
+ ether->ctlr = ctlr;
+ ether->port = ctlr->port;
+ ether->irq = ctlr->pcidev->intl;
+ ether->tbdf = ctlr->pcidev->tbdf;
+ ctlr->ctlrno = ether->ctlrno;
+ ctlr->type = ether->type;
+
+ csr32w(ctlr, Port, 0);
+ delay(1);
+
+ while(csr8r(ctlr, CommandR))
+ ;
+ csr32w(ctlr, Pointer, 0);
+ csr8w(ctlr, CommandR, LoadRUB);
+ while(csr8r(ctlr, CommandR))
+ ;
+ csr8w(ctlr, CommandR, LoadCUB);
+
+ /*
+ * Initialise the action and receive frame areas.
+ */
+ ctlrinit(ctlr);
+
+ /*
+ * Read the EEPROM.
+ * Do a dummy read first to get the size
+ * and allocate ctlr->eeprom.
+ */
+ hy93c46r(ctlr, 0);
+ sum = 0;
+ for(i = 0; i < (1<<ctlr->eepromsz); i++){
+ x = hy93c46r(ctlr, i);
+ ctlr->eeprom[i] = x;
+ sum += x;
+ }
+ if(sum != 0xBABA)
+ print("#l%d: EEPROM checksum - 0x%4.4uX\n", ether->ctlrno, sum);
+
+ /*
+ * Eeprom[6] indicates whether there is a PHY and whether
+ * it's not 10Mb-only, in which case use the given PHY address
+ * to set any PHY specific options and determine the speed.
+ * Unfortunately, sometimes the EEPROM is blank except for
+ * the ether address and checksum; in this case look at the
+ * controller type and if it's am 82558 or 82559 it has an
+ * embedded PHY so scan for that.
+ * If no PHY, assume 82503 (serial) operation.
+ */
+ if((ctlr->eeprom[6] & 0x1F00) && !(ctlr->eeprom[6] & 0x8000))
+ phyaddr = ctlr->eeprom[6] & 0x00FF;
+ else
+ switch(ctlr->pcidev->rid){
+ case 0x01: /* 82557 A-step */
+ case 0x02: /* 82557 B-step */
+ case 0x03: /* 82557 C-step */
+ default:
+ phyaddr = -1;
+ break;
+ case 0x04: /* 82558 A-step */
+ case 0x05: /* 82558 B-step */
+ case 0x06: /* 82559 A-step */
+ case 0x07: /* 82559 B-step */
+ case 0x08: /* 82559 C-step */
+ case 0x09: /* 82559ER A-step */
+ phyaddr = scanphy(ctlr);
+ break;
+ }
+ if(phyaddr >= 0){
+ /*
+ * Resolve the highest common ability of the two
+ * link partners. In descending order:
+ * 0x0100 100BASE-TX Full Duplex
+ * 0x0200 100BASE-T4
+ * 0x0080 100BASE-TX
+ * 0x0040 10BASE-T Full Duplex
+ * 0x0020 10BASE-T
+ */
+ anar = miir(ctlr, phyaddr, 0x04);
+ anlpar = miir(ctlr, phyaddr, 0x05) & 0x03E0;
+ anar &= anlpar;
+ bmcr = 0;
+ if(anar & 0x380)
+ bmcr = 0x2000;
+ if(anar & 0x0140)
+ bmcr |= 0x0100;
+
+ switch((ctlr->eeprom[6]>>8) & 0x001F){
+
+ case 0x04: /* DP83840 */
+ case 0x0A: /* DP83840A */
+ /*
+ * The DP83840[A] requires some tweaking for
+ * reliable operation.
+ * The manual says bit 10 should be unconditionally
+ * set although it supposedly only affects full-duplex
+ * operation (an & 0x0140).
+ */
+ x = miir(ctlr, phyaddr, 0x17) & ~0x0520;
+ x |= 0x0420;
+ for(i = 0; i < ether->nopt; i++){
+ if(cistrcmp(ether->opt[i], "congestioncontrol"))
+ continue;
+ x |= 0x0100;
+ break;
+ }
+ miiw(ctlr, phyaddr, 0x17, x);
+
+ /*
+ * If the link partner can't autonegotiate, determine
+ * the speed from elsewhere.
+ */
+ if(anlpar == 0){
+ miir(ctlr, phyaddr, 0x01);
+ bmsr = miir(ctlr, phyaddr, 0x01);
+ x = miir(ctlr, phyaddr, 0x19);
+ if((bmsr & 0x0004) && !(x & 0x0040))
+ bmcr = 0x2000;
+ }
+ break;
+
+ case 0x07: /* Intel 82555 */
+ /*
+ * Auto-negotiation may fail if the other end is
+ * a DP83840A and the cable is short.
+ */
+ bmsr = miir(ctlr, phyaddr, 0x01);
+ if((miir(ctlr, phyaddr, 0) & 0x1000) && !(bmsr & 0x0020)){
+ miiw(ctlr, phyaddr, 0x1A, 0x2010);
+ x = miir(ctlr, phyaddr, 0);
+ miiw(ctlr, phyaddr, 0, 0x0200|x);
+ for(i = 0; i < 3000; i++){
+ delay(1);
+ if(miir(ctlr, phyaddr, 0x01) & 0x0020)
+ break;
+ }
+ miiw(ctlr, phyaddr, 0x1A, 0x2000);
+
+ anar = miir(ctlr, phyaddr, 0x04);
+ anlpar = miir(ctlr, phyaddr, 0x05) & 0x03E0;
+ anar &= anlpar;
+ bmcr = 0;
+ if(anar & 0x380)
+ bmcr = 0x2000;
+ if(anar & 0x0140)
+ bmcr |= 0x0100;
+ }
+ break;
+ }
+
+ /*
+ * Force speed and duplex if no auto-negotiation.
+ */
+ if(anlpar == 0){
+ force = 0;
+ for(i = 0; i < ether->nopt; i++){
+ if(cistrcmp(ether->opt[i], "fullduplex") == 0){
+ force = 1;
+ bmcr |= 0x0100;
+ ctlr->configdata[19] |= 0x40;
+ }
+ else if(cistrcmp(ether->opt[i], "speed") == 0){
+ force = 1;
+ x = strtol(&ether->opt[i][6], 0, 0);
+ if(x == 10)
+ bmcr &= ~0x2000;
+ else if(x == 100)
+ bmcr |= 0x2000;
+ else
+ force = 0;
+ }
+ }
+ if(force)
+ miiw(ctlr, phyaddr, 0x00, bmcr);
+ }
+
+ ctlr->configdata[8] = 1;
+ ctlr->configdata[15] &= ~0x80;
+ }
+ else{
+ ctlr->configdata[8] = 0;
+ ctlr->configdata[15] |= 0x80;
+ }
+
+ /*
+ * Load the chip configuration
+ */
+ configure(ether, 0);
+
+ /*
+ * Check if the adapter's station address is to be overridden.
+ * If not, read it from the EEPROM and set in ether->ea prior to loading
+ * the station address with the Individual Address Setup command.
+ */
+ memset(ea, 0, Eaddrlen);
+ if(memcmp(ea, ether->ea, Eaddrlen) == 0){
+ for(i = 0; i < Eaddrlen/2; i++){
+ x = ctlr->eeprom[i];
+ ether->ea[2*i] = x & 0xFF;
+ ether->ea[2*i+1] = (x>>8) & 0xFF;
+ }
+ }
+
+ bp = allocb(sizeof(Cb));
+ cb = (Cb*)bp->rp;
+ bp->wp += sizeof(Cb);
+
+ cb->command = CbIAS;
+ cb->link = NullPointer;
+ memmove(cb->data, ether->ea, Eaddrlen);
+ action(ctlr, bp);
+
+ /*
+ * Linkage to the generic ethernet driver.
+ */
+ ether->attach = attach;
+ ether->transmit = transmit;
+ ether->interrupt = interrupt;
+ ether->detach = detach;
+
+ return 0;
+}
diff --git a/os/boot/pc/ether83815.c b/os/boot/pc/ether83815.c
new file mode 100644
index 00000000..71a1c24d
--- /dev/null
+++ b/os/boot/pc/ether83815.c
@@ -0,0 +1,825 @@
+/*
+ * National Semiconductor DP83815
+ *
+ * Supports only internal PHY and has been tested on:
+ * Netgear FA311TX (using Netgear DS108 10/100 hub)
+ * To do:
+ * check Ethernet address;
+ * test autonegotiation on 10 Mbit, and 100 Mbit full duplex;
+ * external PHY via MII (should be common code for MII);
+ * thresholds;
+ * ring sizing;
+ * physical link changes/disconnect;
+ * push initialisation back to attach.
+ *
+ * C H Forsyth, forsyth@vitanuova.com, 18th June 2001.
+ */
+
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "etherif.h"
+
+#define DEBUG (1)
+#define debug if(DEBUG)print
+
+enum {
+ Nrde = 8,
+ Ntde = 8,
+};
+
+#define Rbsz ROUNDUP(sizeof(Etherpkt)+4, 4)
+
+typedef struct Des {
+ ulong next;
+ int cmdsts;
+ ulong addr;
+ Block* bp;
+} Des;
+
+enum { /* cmdsts */
+ Own = 1<<31, /* set by data producer to hand to consumer */
+ More = 1<<30, /* more of packet in next descriptor */
+ Intr = 1<<29, /* interrupt when device is done with it */
+ Supcrc = 1<<28, /* suppress crc on transmit */
+ Inccrc = 1<<28, /* crc included on receive (always) */
+ Ok = 1<<27, /* packet ok */
+ Size = 0xFFF, /* packet size in bytes */
+
+ /* transmit */
+ Txa = 1<<26, /* transmission aborted */
+ Tfu = 1<<25, /* transmit fifo underrun */
+ Crs = 1<<24, /* carrier sense lost */
+ Td = 1<<23, /* transmission deferred */
+ Ed = 1<<22, /* excessive deferral */
+ Owc = 1<<21, /* out of window collision */
+ Ec = 1<<20, /* excessive collisions */
+ /* 19-16 collision count */
+
+ /* receive */
+ Rxa = 1<<26, /* receive aborted (same as Rxo) */
+ Rxo = 1<<25, /* receive overrun */
+ Dest = 3<<23, /* destination class */
+ Drej= 0<<23, /* packet was rejected */
+ Duni= 1<<23, /* unicast */
+ Dmulti= 2<<23, /* multicast */
+ Dbroad= 3<<23, /* broadcast */
+ Long = 1<<22, /* too long packet received */
+ Runt = 1<<21, /* packet less than 64 bytes */
+ Ise = 1<<20, /* invalid symbol */
+ Crce = 1<<19, /* invalid crc */
+ Fae = 1<<18, /* frame alignment error */
+ Lbp = 1<<17, /* loopback packet */
+ Col = 1<<16, /* collision during receive */
+};
+
+enum { /* Variants */
+ Nat83815 = (0x0020<<16)|0x100B,
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Ctlr {
+ int port;
+ Pcidev* pcidev;
+ Ctlr* next;
+ int active;
+ int id; /* (pcidev->did<<16)|pcidev->vid */
+
+ ushort srom[0xB+1];
+ uchar sromea[Eaddrlen]; /* MAC address */
+
+ uchar fd; /* option or auto negotiation */
+
+ int mbps;
+
+ Lock ilock;
+
+ Des* rdr; /* receive descriptor ring */
+ int nrdr; /* size of rdr */
+ int rdrx; /* index into rdr */
+
+ Lock tlock;
+ Des* tdr; /* transmit descriptor ring */
+ int ntdr; /* size of tdr */
+ int tdrh; /* host index into tdr */
+ int tdri; /* interface index into tdr */
+ int ntq; /* descriptors active */
+ int ntqmax;
+ Block* bqhead; /* transmission queue */
+ Block* bqtail;
+
+ ulong rxa; /* receive statistics */
+ ulong rxo;
+ ulong rlong;
+ ulong runt;
+ ulong ise;
+ ulong crce;
+ ulong fae;
+ ulong lbp;
+ ulong col;
+ ulong rxsovr;
+ ulong rxorn;
+
+ ulong txa; /* transmit statistics */
+ ulong tfu;
+ ulong crs;
+ ulong td;
+ ulong ed;
+ ulong owc;
+ ulong ec;
+ ulong txurn;
+
+ ulong dperr; /* system errors */
+ ulong rmabt;
+ ulong rtabt;
+ ulong sserr;
+ ulong rxsover;
+} Ctlr;
+
+static Ctlr* ctlrhead;
+static Ctlr* ctlrtail;
+
+enum {
+ /* registers (could memory map) */
+ Rcr= 0x00, /* command register */
+ Rst= 1<<8,
+ Rxr= 1<<5, /* receiver reset */
+ Txr= 1<<4, /* transmitter reset */
+ Rxd= 1<<3, /* receiver disable */
+ Rxe= 1<<2, /* receiver enable */
+ Txd= 1<<1, /* transmitter disable */
+ Txe= 1<<0, /* transmitter enable */
+ Rcfg= 0x04, /* configuration */
+ Lnksts= 1<<31, /* link good */
+ Speed100= 1<<30, /* 100 Mb/s link */
+ Fdup= 1<<29, /* full duplex */
+ Pol= 1<<28, /* polarity reversal (10baseT) */
+ Aneg_dn= 1<<27, /* autonegotiation done */
+ Pint_acen= 1<<17, /* PHY interrupt auto clear enable */
+ Pause_adv= 1<<16, /* advertise pause during auto neg */
+ Paneg_ena= 1<<13, /* auto negotiation enable */
+ Paneg_all= 7<<13, /* auto negotiation enable 10/100 half & full */
+ Ext_phy= 1<<12, /* enable MII for external PHY */
+ Phy_rst= 1<<10, /* reset internal PHY */
+ Phy_dis= 1<<9, /* disable internal PHY (eg, low power) */
+ Req_alg= 1<<7, /* PCI bus request: set means less aggressive */
+ Sb= 1<<6, /* single slot back-off not random */
+ Pow= 1<<5, /* out of window timer selection */
+ Exd= 1<<4, /* disable excessive deferral timer */
+ Pesel= 1<<3, /* parity error algorithm selection */
+ Brom_dis= 1<<2, /* disable boot rom interface */
+ Bem= 1<<0, /* big-endian mode */
+ Rmear= 0x08, /* eeprom access */
+ Mdc= 1<<6, /* MII mangement check */
+ Mddir= 1<<5, /* MII management direction */
+ Mdio= 1<<4, /* MII mangement data */
+ Eesel= 1<<3, /* EEPROM chip select */
+ Eeclk= 1<<2, /* EEPROM clock */
+ Eedo= 1<<1, /* EEPROM data out (from chip) */
+ Eedi= 1<<0, /* EEPROM data in (to chip) */
+ Rptscr= 0x0C, /* pci test control */
+ Risr= 0x10, /* interrupt status */
+ Txrcmp= 1<<25, /* transmit reset complete */
+ Rxrcmp= 1<<24, /* receiver reset complete */
+ Dperr= 1<<23, /* detected parity error */
+ Sserr= 1<<22, /* signalled system error */
+ Rmabt= 1<<21, /* received master abort */
+ Rtabt= 1<<20, /* received target abort */
+ Rxsovr= 1<<16, /* RX status FIFO overrun */
+ Hiberr= 1<<15, /* high bits error set (OR of 25-16) */
+ Phy= 1<<14, /* PHY interrupt */
+ Pme= 1<<13, /* power management event (wake online) */
+ Swi= 1<<12, /* software interrupt */
+ Mib= 1<<11, /* MIB service */
+ Txurn= 1<<10, /* TX underrun */
+ Txidle= 1<<9, /* TX idle */
+ Txerr= 1<<8, /* TX packet error */
+ Txdesc= 1<<7, /* TX descriptor (with Intr bit done) */
+ Txok= 1<<6, /* TX ok */
+ Rxorn= 1<<5, /* RX overrun */
+ Rxidle= 1<<4, /* RX idle */
+ Rxearly= 1<<3, /* RX early threshold */
+ Rxerr= 1<<2, /* RX packet error */
+ Rxdesc= 1<<1, /* RX descriptor (with Intr bit done) */
+ Rxok= 1<<0, /* RX ok */
+ Rimr= 0x14, /* interrupt mask */
+ Rier= 0x18, /* interrupt enable */
+ Ie= 1<<0, /* interrupt enable */
+ Rtxdp= 0x20, /* transmit descriptor pointer */
+ Rtxcfg= 0x24, /* transmit configuration */
+ Csi= 1<<31, /* carrier sense ignore (needed for full duplex) */
+ Hbi= 1<<30, /* heartbeat ignore (needed for full duplex) */
+ Atp= 1<<28, /* automatic padding of runt packets */
+ Mxdma= 7<<20, /* maximum dma transfer field */
+ Mxdma32= 4<<20, /* 4x32-bit words (32 bytes) */
+ Mxdma64= 5<<20, /* 8x32-bit words (64 bytes) */
+ Flth= 0x3F<<8, /* Tx fill threshold, units of 32 bytes (must be > Mxdma) */
+ Drth= 0x3F<<0, /* Tx drain threshold (units of 32 bytes) */
+ Flth128= 4<<8, /* fill at 128 bytes */
+ Drth512= 16<<0, /* drain at 512 bytes */
+ Rrxdp= 0x30, /* receive descriptor pointer */
+ Rrxcfg= 0x34, /* receive configuration */
+ Atx= 1<<28, /* accept transmit packets (needed for full duplex) */
+ Rdrth= 0x1F<<1, /* Rx drain threshold (units of 32 bytes) */
+ Rdrth64= 2<<1, /* drain at 64 bytes */
+ Rccsr= 0x3C, /* CLKRUN control/status */
+ Pmests= 1<<15, /* PME status */
+ Rwcsr= 0x40, /* wake on lan control/status */
+ Rpcr= 0x44, /* pause control/status */
+ Rrfcr= 0x48, /* receive filter/match control */
+ Rfen= 1<<31, /* receive filter enable */
+ Aab= 1<<30, /* accept all broadcast */
+ Aam= 1<<29, /* accept all multicast */
+ Aau= 1<<28, /* accept all unicast */
+ Apm= 1<<27, /* accept on perfect match */
+ Apat= 0xF<<23, /* accept on pattern match */
+ Aarp= 1<<22, /* accept ARP */
+ Mhen= 1<<21, /* multicast hash enable */
+ Uhen= 1<<20, /* unicast hash enable */
+ Ulm= 1<<19, /* U/L bit mask */
+ /* bits 0-9 are rfaddr */
+ Rrfdr= 0x4C, /* receive filter/match data */
+ Rbrar= 0x50, /* boot rom address */
+ Rbrdr= 0x54, /* boot rom data */
+ Rsrr= 0x58, /* silicon revision */
+ Rmibc= 0x5C, /* MIB control */
+ /* 60-78 MIB data */
+
+ /* PHY registers */
+ Rbmcr= 0x80, /* basic mode configuration */
+ Reset= 1<<15,
+ Sel100= 1<<13, /* select 100Mb/sec if no auto neg */
+ Anena= 1<<12, /* auto negotiation enable */
+ Anrestart= 1<<9, /* restart auto negotiation */
+ Selfdx= 1<<8, /* select full duplex if no auto neg */
+ Rbmsr= 0x84, /* basic mode status */
+ Ancomp= 1<<5, /* autonegotiation complete */
+ Rphyidr1= 0x88,
+ Rphyidr2= 0x8C,
+ Ranar= 0x90, /* autonegotiation advertisement */
+ Ranlpar= 0x94, /* autonegotiation link partner ability */
+ Raner= 0x98, /* autonegotiation expansion */
+ Rannptr= 0x9C, /* autonegotiation next page TX */
+ Rphysts= 0xC0, /* PHY status */
+ Rmicr= 0xC4, /* MII control */
+ Inten= 1<<1, /* PHY interrupt enable */
+ Rmisr= 0xC8, /* MII status */
+ Rfcscr= 0xD0, /* false carrier sense counter */
+ Rrecr= 0xD4, /* receive error counter */
+ Rpcsr= 0xD8, /* 100Mb config/status */
+ Rphycr= 0xE4, /* PHY control */
+ Rtbscr= 0xE8, /* 10BaseT status/control */
+};
+
+/*
+ * eeprom addresses
+ * 7 to 9 (16 bit words): mac address, shifted and reversed
+ */
+
+#define csr32r(c, r) (inl((c)->port+(r)))
+#define csr32w(c, r, l) (outl((c)->port+(r), (ulong)(l)))
+#define csr16r(c, r) (ins((c)->port+(r)))
+#define csr16w(c, r, l) (outs((c)->port+(r), (ulong)(l)))
+
+static void
+coherence(void)
+{
+}
+
+static void
+dumpcregs(Ctlr *ctlr)
+{
+ int i;
+
+ for(i=0; i<=0x5C; i+=4)
+ print("%2.2ux %8.8lux\n", i, csr32r(ctlr, i));
+}
+
+static void
+attach(Ether* ether)
+{
+ Ctlr *ctlr;
+
+ ctlr = ether->ctlr;
+ ilock(&ctlr->ilock);
+ if(0)
+ dumpcregs(ctlr);
+ csr32w(ctlr, Rcr, Rxe);
+ iunlock(&ctlr->ilock);
+}
+
+static void
+detach(Ether* ether)
+{
+ Ctlr *ctlr;
+
+ ctlr = ether->ctlr;
+ csr32w(ctlr, Rcr, 0);
+ delay(1);
+}
+
+static void
+txstart(Ether* ether)
+{
+ Ctlr *ctlr;
+ Block *bp;
+ Des *des;
+ int started;
+
+ ctlr = ether->ctlr;
+ started = 0;
+ while(ctlr->ntq < ctlr->ntdr-1){
+ bp = ctlr->bqhead;
+ if(bp == nil)
+ break;
+ ctlr->bqhead = bp->next;
+ des = &ctlr->tdr[ctlr->tdrh];
+ des->bp = bp;
+ des->addr = PADDR(bp->rp);
+ ctlr->ntq++;
+ coherence();
+ des->cmdsts = Own | BLEN(bp);
+ ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdr);
+ started = 1;
+ }
+ if(started){
+ coherence();
+ csr32w(ctlr, Rcr, Txe); /* prompt */
+ }
+
+ if(ctlr->ntq > ctlr->ntqmax)
+ ctlr->ntqmax = ctlr->ntq;
+}
+
+static void
+transmit(Ether* ether)
+{
+ Ctlr *ctlr;
+ Block *bp;
+ RingBuf *tb;
+
+ ctlr = ether->ctlr;
+ ilock(&ctlr->tlock);
+ while((tb = &ether->tb[ether->ti])->owner == Interface){
+ bp = allocb(tb->len);
+ memmove(bp->wp, tb->pkt, tb->len);
+ memmove(bp->wp+Eaddrlen, ether->ea, Eaddrlen);
+ bp->wp += tb->len;
+ if(ctlr->bqhead)
+ ctlr->bqtail->next = bp;
+ else
+ ctlr->bqhead = bp;
+ ctlr->bqtail = bp;
+ txstart(ether);
+ tb->owner = Host;
+ ether->ti = NEXT(ether->ti, ether->ntb);
+ }
+ iunlock(&ctlr->tlock);
+}
+
+static void
+txrxcfg(Ctlr *ctlr, int txdrth)
+{
+ ulong rx, tx;
+
+ rx = csr32r(ctlr, Rrxcfg);
+ tx = csr32r(ctlr, Rtxcfg);
+ if(ctlr->fd){
+ rx |= Atx;
+ tx |= Csi | Hbi;
+ }else{
+ rx &= ~Atx;
+ tx &= ~(Csi | Hbi);
+ }
+ tx &= ~(Mxdma|Drth|Flth);
+ tx |= Mxdma64 | Flth128 | txdrth;
+ csr32w(ctlr, Rtxcfg, tx);
+ rx &= ~(Mxdma|Rdrth);
+ rx |= Mxdma64 | Rdrth64;
+ csr32w(ctlr, Rrxcfg, rx);
+}
+
+static void
+interrupt(Ureg*, void* arg)
+{
+ Ctlr *ctlr;
+ Ether *ether;
+ int status, cmdsts;
+ Des *des;
+ RingBuf *rb;
+
+ ether = arg;
+ ctlr = ether->ctlr;
+
+ while((status = csr32r(ctlr, Risr)) != 0){
+
+ status &= ~(Pme|Mib);
+ status &= ~(Hiberr|Txrcmp|Rxrcmp|Rxsovr|Dperr|Sserr|Rmabt|Rtabt);
+
+ /*
+ * Received packets.
+ */
+ if(status & (Rxdesc|Rxok|Rxerr|Rxearly|Rxorn)){
+ des = &ctlr->rdr[ctlr->rdrx];
+ while((cmdsts = des->cmdsts) & Own){
+ rb = &ether->rb[ether->ri];
+ if(rb->owner == Interface && (cmdsts&Ok)){
+ rb->len = (cmdsts&Size)-4;
+ memmove(rb->pkt, des->bp->rp, rb->len);
+ rb->owner = Host;
+ ether->ri = NEXT(ether->ri, ether->nrb);
+ }
+
+ des->cmdsts = Rbsz;
+ coherence();
+
+ ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdr);
+ des = &ctlr->rdr[ctlr->rdrx];
+ }
+ status &= ~(Rxdesc|Rxok|Rxerr|Rxearly|Rxorn);
+ }
+
+ /*
+ * Check the transmit side:
+ * check for Transmit Underflow and Adjust
+ * the threshold upwards;
+ * free any transmitted buffers and try to
+ * top-up the ring.
+ */
+ if(status & Txurn){
+ ctlr->txurn++;
+ ilock(&ctlr->ilock);
+ /* change threshold */
+ iunlock(&ctlr->ilock);
+ status &= ~(Txurn);
+ }
+
+ ilock(&ctlr->tlock);
+ while(ctlr->ntq){
+ des = &ctlr->tdr[ctlr->tdri];
+ cmdsts = des->cmdsts;
+ if(cmdsts & Own)
+ break;
+
+ freeb(des->bp);
+ des->bp = nil;
+ des->cmdsts = 0;
+
+ ctlr->ntq--;
+ ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdr);
+ }
+ txstart(ether);
+ iunlock(&ctlr->tlock);
+
+ status &= ~(Txurn|Txidle|Txerr|Txdesc|Txok);
+
+ /*
+ * Anything left not catered for?
+ */
+ if(status)
+ print("#l%d: status %8.8uX\n", ether->ctlrno, status);
+ }
+}
+
+static void
+ctlrinit(Ether* ether)
+{
+ Ctlr *ctlr;
+ Des *des, *last;
+
+ ctlr = ether->ctlr;
+
+ /*
+ * Allocate and initialise the receive ring;
+ * allocate and initialise the transmit ring;
+ * unmask interrupts and start the transmit side
+ */
+ ctlr->rdr = malloc(ctlr->nrdr*sizeof(Des));
+ last = nil;
+ for(des = ctlr->rdr; des < &ctlr->rdr[ctlr->nrdr]; des++){
+ des->bp = allocb(Rbsz);
+ des->cmdsts = Rbsz;
+ des->addr = PADDR(des->bp->rp);
+ if(last != nil)
+ last->next = PADDR(des);
+ last = des;
+ }
+ ctlr->rdr[ctlr->nrdr-1].next = PADDR(ctlr->rdr);
+ ctlr->rdrx = 0;
+ csr32w(ctlr, Rrxdp, PADDR(ctlr->rdr));
+
+ ctlr->tdr = xspanalloc(ctlr->ntdr*sizeof(Des), 8*sizeof(ulong), 0);
+ last = nil;
+ for(des = ctlr->tdr; des < &ctlr->tdr[ctlr->ntdr]; des++){
+ des->cmdsts = 0;
+ des->bp = nil;
+ des->addr = ~0;
+ if(last != nil)
+ last->next = PADDR(des);
+ last = des;
+ }
+ ctlr->tdr[ctlr->ntdr-1].next = PADDR(ctlr->tdr);
+ ctlr->tdrh = 0;
+ ctlr->tdri = 0;
+ csr32w(ctlr, Rtxdp, PADDR(ctlr->tdr));
+
+ txrxcfg(ctlr, Drth512);
+
+ csr32w(ctlr, Rimr, Dperr|Sserr|Rmabt|Rtabt|Rxsovr|Hiberr|Txurn|Txerr|Txdesc|Txok|Rxorn|Rxerr|Rxdesc|Rxok); /* Phy|Pme|Mib */
+ csr32r(ctlr, Risr); /* clear status */
+ csr32w(ctlr, Rier, Ie);
+}
+
+static void
+eeclk(Ctlr *ctlr, int clk)
+{
+ csr32w(ctlr, Rmear, Eesel | clk);
+ microdelay(2);
+}
+
+static void
+eeidle(Ctlr *ctlr)
+{
+ int i;
+
+ eeclk(ctlr, 0);
+ eeclk(ctlr, Eeclk);
+ for(i=0; i<25; i++){
+ eeclk(ctlr, 0);
+ eeclk(ctlr, Eeclk);
+ }
+ eeclk(ctlr, 0);
+ csr32w(ctlr, Rmear, 0);
+ microdelay(2);
+}
+
+static int
+eegetw(Ctlr *ctlr, int a)
+{
+ int d, i, w, v;
+
+ eeidle(ctlr);
+ eeclk(ctlr, 0);
+ eeclk(ctlr, Eeclk);
+ d = 0x180 | a;
+ for(i=0x400; i; i>>=1){
+ v = (d & i) ? Eedi : 0;
+ eeclk(ctlr, v);
+ eeclk(ctlr, Eeclk|v);
+ }
+ eeclk(ctlr, 0);
+
+ w = 0;
+ for(i=0x8000; i; i >>= 1){
+ eeclk(ctlr, Eeclk);
+ if(csr32r(ctlr, Rmear) & Eedo)
+ w |= i;
+ microdelay(2);
+ eeclk(ctlr, 0);
+ }
+ eeidle(ctlr);
+ return w;
+}
+
+static void
+softreset(Ctlr* ctlr, int resetphys)
+{
+ int i, w;
+
+ /*
+ * Soft-reset the controller
+ */
+ csr32w(ctlr, Rcr, Rst);
+ for(i=0;; i++){
+ if(i > 100)
+ panic("ns83815: soft reset did not complete");
+ microdelay(250);
+ if((csr32r(ctlr, Rcr) & Rst) == 0)
+ break;
+ delay(1);
+ }
+
+ csr32w(ctlr, Rccsr, Pmests);
+ csr32w(ctlr, Rccsr, 0);
+ csr32w(ctlr, Rcfg, csr32r(ctlr, Rcfg) | Pint_acen);
+
+ if(resetphys){
+ /*
+ * Soft-reset the PHY
+ */
+ csr32w(ctlr, Rbmcr, Reset);
+ for(i=0;; i++){
+ if(i > 100)
+ panic("ns83815: PHY soft reset time out");
+ if((csr32r(ctlr, Rbmcr) & Reset) == 0)
+ break;
+ delay(1);
+ }
+ }
+
+ /*
+ * Initialisation values, in sequence (see 4.4 Recommended Registers Configuration)
+ */
+ csr16w(ctlr, 0xCC, 0x0001); /* PGSEL */
+ csr16w(ctlr, 0xE4, 0x189C); /* PMCCSR */
+ csr16w(ctlr, 0xFC, 0x0000); /* TSTDAT */
+ csr16w(ctlr, 0xF4, 0x5040); /* DSPCFG */
+ csr16w(ctlr, 0xF8, 0x008C); /* SDCFG */
+
+ /*
+ * Auto negotiate
+ */
+ w = csr16r(ctlr, Rbmsr); /* clear latched bits */
+ debug("anar: %4.4ux\n", csr16r(ctlr, Ranar));
+ csr16w(ctlr, Rbmcr, Anena);
+ if(csr16r(ctlr, Ranar) == 0 || (csr32r(ctlr, Rcfg) & Aneg_dn) == 0){
+ csr16w(ctlr, Rbmcr, Anena|Anrestart);
+ for(i=0;; i++){
+ if(i > 6000){
+ print("ns83815: auto neg timed out\n");
+ break;
+ }
+ if((w = csr16r(ctlr, Rbmsr)) & Ancomp)
+ break;
+ delay(1);
+ }
+ debug("%d ms\n", i);
+ w &= 0xFFFF;
+ debug("bmsr: %4.4ux\n", w);
+ }
+ debug("anar: %4.4ux\n", csr16r(ctlr, Ranar));
+ debug("anlpar: %4.4ux\n", csr16r(ctlr, Ranlpar));
+ debug("aner: %4.4ux\n", csr16r(ctlr, Raner));
+ debug("physts: %4.4ux\n", csr16r(ctlr, Rphysts));
+ debug("tbscr: %4.4ux\n", csr16r(ctlr, Rtbscr));
+}
+
+static char* mediatable[9] = {
+ "10BASE-T", /* TP */
+ "10BASE-2", /* BNC */
+ "10BASE-5", /* AUI */
+ "100BASE-TX",
+ "10BASE-TFD",
+ "100BASE-TXFD",
+ "100BASE-T4",
+ "100BASE-FX",
+ "100BASE-FXFD",
+};
+
+static void
+srom(Ctlr* ctlr)
+{
+ int i, j;
+
+ for(i = 0; i < nelem(ctlr->srom); i++)
+ ctlr->srom[i] = eegetw(ctlr, i);
+
+ /*
+ * the MAC address is reversed, straddling word boundaries
+ */
+ memset(ctlr->sromea, 0, sizeof(ctlr->sromea));
+ j = 6*16 + 15;
+ for(i=0; i<48; i++){
+ ctlr->sromea[i>>3] |= ((ctlr->srom[j>>4] >> (15-(j&0xF))) & 1) << (i&7);
+ j++;
+ }
+}
+
+static void
+scanpci83815(void)
+{
+ Ctlr *ctlr;
+ Pcidev *p;
+
+ p = nil;
+ while(p = pcimatch(p, 0, 0)){
+ if(p->ccrb != 0x02 || p->ccru != 0)
+ continue;
+ switch((p->did<<16)|p->vid){
+ default:
+ continue;
+
+ case Nat83815:
+ break;
+ }
+
+ /*
+ * bar[0] is the I/O port register address and
+ * bar[1] is the memory-mapped register address.
+ */
+ ctlr = malloc(sizeof(Ctlr));
+ ctlr->port = p->mem[0].bar & ~0x01;
+ ctlr->pcidev = p;
+ ctlr->id = (p->did<<16)|p->vid;
+
+ softreset(ctlr, 0);
+ srom(ctlr);
+
+ if(ctlrhead != nil)
+ ctlrtail->next = ctlr;
+ else
+ ctlrhead = ctlr;
+ ctlrtail = ctlr;
+ }
+}
+
+int
+ether83815reset(Ether* ether)
+{
+ Ctlr *ctlr;
+ int i, x;
+ uchar ea[Eaddrlen];
+ static int scandone;
+
+ if(scandone == 0){
+ scanpci83815();
+ scandone = 1;
+ }
+
+ /*
+ * Any adapter matches if no ether->port is supplied,
+ * otherwise the ports must match.
+ */
+ for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
+ if(ctlr->active)
+ continue;
+ if(ether->port == 0 || ether->port == ctlr->port){
+ ctlr->active = 1;
+ break;
+ }
+ }
+ if(ctlr == nil)
+ return -1;
+
+ ether->ctlr = ctlr;
+ ether->port = ctlr->port;
+ ether->irq = ctlr->pcidev->intl;
+ ether->tbdf = ctlr->pcidev->tbdf;
+
+ /*
+ * Check if the adapter's station address is to be overridden.
+ * If not, read it from the EEPROM and set in ether->ea prior to
+ * loading the station address in the hardware.
+ */
+ memset(ea, 0, Eaddrlen);
+ if(memcmp(ea, ether->ea, Eaddrlen) == 0)
+ memmove(ether->ea, ctlr->sromea, Eaddrlen);
+ for(i=0; i<Eaddrlen; i+=2){
+ x = ether->ea[i] | (ether->ea[i+1]<<8);
+ csr32w(ctlr, Rrfcr, i);
+ csr32w(ctlr, Rrfdr, x);
+ }
+ csr32w(ctlr, Rrfcr, Rfen|Apm|Aab|Aam);
+
+ /*
+ * Look for a medium override in case there's no autonegotiation
+ * the autonegotiation fails.
+ */
+
+ for(i = 0; i < ether->nopt; i++){
+ if(cistrcmp(ether->opt[i], "FD") == 0){
+ ctlr->fd = 1;
+ continue;
+ }
+ for(x = 0; x < nelem(mediatable); x++){
+ debug("compare <%s> <%s>\n", mediatable[x],
+ ether->opt[i]);
+ if(cistrcmp(mediatable[x], ether->opt[i]) == 0){
+ switch(x){
+ default:
+ ctlr->fd = 0;
+ break;
+
+ case 0x04: /* 10BASE-TFD */
+ case 0x05: /* 100BASE-TXFD */
+ case 0x08: /* 100BASE-FXFD */
+ ctlr->fd = 1;
+ break;
+ }
+ break;
+ }
+ }
+ }
+
+ /*
+ * Initialise descriptor rings, ethernet address.
+ */
+ ctlr->nrdr = Nrde;
+ ctlr->ntdr = Ntde;
+ pcisetbme(ctlr->pcidev);
+ ctlrinit(ether);
+
+ /*
+ * Linkage to the generic ethernet driver.
+ */
+ ether->attach = attach;
+ ether->transmit = transmit;
+ ether->interrupt = interrupt;
+ ether->detach = detach;
+
+ return 0;
+}
diff --git a/os/boot/pc/ether8390.c b/os/boot/pc/ether8390.c
new file mode 100644
index 00000000..ea4fedab
--- /dev/null
+++ b/os/boot/pc/ether8390.c
@@ -0,0 +1,715 @@
+/*
+ * National Semiconductor DP8390 and clone
+ * Network Interface Controller.
+ */
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "etherif.h"
+#include "ether8390.h"
+
+enum { /* NIC core registers */
+ Cr = 0x00, /* command register, all pages */
+
+ /* Page 0, read */
+ Clda0 = 0x01, /* current local DMA address 0 */
+ Clda1 = 0x02, /* current local DMA address 1 */
+ Bnry = 0x03, /* boundary pointer (R/W) */
+ Tsr = 0x04, /* transmit status register */
+ Ncr = 0x05, /* number of collisions register */
+ Fifo = 0x06, /* FIFO */
+ Isr = 0x07, /* interrupt status register (R/W) */
+ Crda0 = 0x08, /* current remote DMA address 0 */
+ Crda1 = 0x09, /* current remote DMA address 1 */
+ Rsr = 0x0C, /* receive status register */
+ Cntr0 = 0x0D, /* frame alignment errors */
+ Cntr1 = 0x0E, /* CRC errors */
+ Cntr2 = 0x0F, /* missed packet errors */
+
+ /* Page 0, write */
+ Pstart = 0x01, /* page start register */
+ Pstop = 0x02, /* page stop register */
+ Tpsr = 0x04, /* transmit page start address */
+ Tbcr0 = 0x05, /* transmit byte count register 0 */
+ Tbcr1 = 0x06, /* transmit byte count register 1 */
+ Rsar0 = 0x08, /* remote start address register 0 */
+ Rsar1 = 0x09, /* remote start address register 1 */
+ Rbcr0 = 0x0A, /* remote byte count register 0 */
+ Rbcr1 = 0x0B, /* remote byte count register 1 */
+ Rcr = 0x0C, /* receive configuration register */
+ Tcr = 0x0D, /* transmit configuration register */
+ Dcr = 0x0E, /* data configuration register */
+ Imr = 0x0F, /* interrupt mask */
+
+ /* Page 1, read/write */
+ Par0 = 0x01, /* physical address register 0 */
+ Curr = 0x07, /* current page register */
+ Mar0 = 0x08, /* multicast address register 0 */
+};
+
+enum { /* Cr */
+ Stp = 0x01, /* stop */
+ Sta = 0x02, /* start */
+ Txp = 0x04, /* transmit packet */
+ Rd0 = 0x08, /* remote DMA command */
+ Rd1 = 0x10,
+ Rd2 = 0x20,
+ RdREAD = Rd0, /* remote read */
+ RdWRITE = Rd1, /* remote write */
+ RdSEND = Rd1|Rd0, /* send packet */
+ RdABORT = Rd2, /* abort/complete remote DMA */
+ Ps0 = 0x40, /* page select */
+ Ps1 = 0x80,
+ Page0 = 0x00,
+ Page1 = Ps0,
+ Page2 = Ps1,
+};
+
+enum { /* Isr/Imr */
+ Prx = 0x01, /* packet received */
+ Ptx = 0x02, /* packet transmitted */
+ Rxe = 0x04, /* receive error */
+ Txe = 0x08, /* transmit error */
+ Ovw = 0x10, /* overwrite warning */
+ Cnt = 0x20, /* counter overflow */
+ Rdc = 0x40, /* remote DMA complete */
+ Rst = 0x80, /* reset status */
+};
+
+enum { /* Dcr */
+ Wts = 0x01, /* word transfer select */
+ Bos = 0x02, /* byte order select */
+ Las = 0x04, /* long address select */
+ Ls = 0x08, /* loopback select */
+ Arm = 0x10, /* auto-initialise remote */
+ Ft0 = 0x20, /* FIFO threshold select */
+ Ft1 = 0x40,
+ Ft1WORD = 0x00,
+ Ft2WORD = Ft0,
+ Ft4WORD = Ft1,
+ Ft6WORD = Ft1|Ft0,
+};
+
+enum { /* Tcr */
+ Crc = 0x01, /* inhibit CRC */
+ Lb0 = 0x02, /* encoded loopback control */
+ Lb1 = 0x04,
+ LpbkNORMAL = 0x00, /* normal operation */
+ LpbkNIC = Lb0, /* internal NIC module loopback */
+ LpbkENDEC = Lb1, /* internal ENDEC module loopback */
+ LpbkEXTERNAL = Lb1|Lb0, /* external loopback */
+ Atd = 0x08, /* auto transmit disable */
+ Ofst = 0x10, /* collision offset enable */
+};
+
+enum { /* Tsr */
+ Ptxok = 0x01, /* packet transmitted */
+ Col = 0x04, /* transmit collided */
+ Abt = 0x08, /* tranmit aborted */
+ Crs = 0x10, /* carrier sense lost */
+ Fu = 0x20, /* FIFO underrun */
+ Cdh = 0x40, /* CD heartbeat */
+ Owc = 0x80, /* out of window collision */
+};
+
+enum { /* Rcr */
+ Sep = 0x01, /* save errored packets */
+ Ar = 0x02, /* accept runt packets */
+ Ab = 0x04, /* accept broadcast */
+ Am = 0x08, /* accept multicast */
+ Pro = 0x10, /* promiscuous physical */
+ Mon = 0x20, /* monitor mode */
+};
+
+enum { /* Rsr */
+ Prxok = 0x01, /* packet received intact */
+ Crce = 0x02, /* CRC error */
+ Fae = 0x04, /* frame alignment error */
+ Fo = 0x08, /* FIFO overrun */
+ Mpa = 0x10, /* missed packet */
+ Phy = 0x20, /* physical/multicast address */
+ Dis = 0x40, /* receiver disabled */
+ Dfr = 0x80, /* deferring */
+};
+
+typedef struct {
+ uchar status;
+ uchar next;
+ uchar len0;
+ uchar len1;
+} Hdr;
+
+void
+dp8390getea(Ether* ether, uchar* ea)
+{
+ Dp8390 *ctlr;
+ uchar cr;
+ int i;
+
+ ctlr = ether->ctlr;
+
+ /*
+ * Get the ethernet address from the chip.
+ * Take care to restore the command register
+ * afterwards.
+ */
+ ilock(ctlr);
+ cr = regr(ctlr, Cr) & ~Txp;
+ regw(ctlr, Cr, Page1|(~(Ps1|Ps0) & cr));
+ for(i = 0; i < Eaddrlen; i++)
+ ea[i] = regr(ctlr, Par0+i);
+ regw(ctlr, Cr, cr);
+ iunlock(ctlr);
+}
+
+void
+dp8390setea(Ether* ether)
+{
+ int i;
+ uchar cr;
+ Dp8390 *ctlr;
+
+ ctlr = ether->ctlr;
+
+ /*
+ * Set the ethernet address into the chip.
+ * Take care to restore the command register
+ * afterwards. Don't care about multicast
+ * addresses as multicast is never enabled
+ * (currently).
+ */
+ ilock(ctlr);
+ cr = regr(ctlr, Cr) & ~Txp;
+ regw(ctlr, Cr, Page1|(~(Ps1|Ps0) & cr));
+ for(i = 0; i < Eaddrlen; i++)
+ regw(ctlr, Par0+i, ether->ea[i]);
+ regw(ctlr, Cr, cr);
+ iunlock(ctlr);
+}
+
+static void*
+_dp8390read(Dp8390* ctlr, void* to, ulong from, ulong len)
+{
+ uchar cr;
+ int timo;
+
+ /*
+ * Read some data at offset 'from' in the card's memory
+ * using the DP8390 remote DMA facility, and place it at
+ * 'to' in main memory, via the I/O data port.
+ */
+ cr = regr(ctlr, Cr) & ~Txp;
+ regw(ctlr, Cr, Page0|RdABORT|Sta);
+ regw(ctlr, Isr, Rdc);
+
+ /*
+ * Set up the remote DMA address and count.
+ */
+ len = ROUNDUP(len, ctlr->width);
+ regw(ctlr, Rbcr0, len & 0xFF);
+ regw(ctlr, Rbcr1, (len>>8) & 0xFF);
+ regw(ctlr, Rsar0, from & 0xFF);
+ regw(ctlr, Rsar1, (from>>8) & 0xFF);
+
+ /*
+ * Start the remote DMA read and suck the data
+ * out of the I/O port.
+ */
+ regw(ctlr, Cr, Page0|RdREAD|Sta);
+ rdread(ctlr, to, len);
+
+ /*
+ * Wait for the remote DMA to complete. The timeout
+ * is necessary because this routine may be called on
+ * a non-existent chip during initialisation and, due
+ * to the miracles of the bus, it's possible to get this
+ * far and still be talking to a slot full of nothing.
+ */
+ for(timo = 10000; (regr(ctlr, Isr) & Rdc) == 0 && timo; timo--)
+ ;
+
+ regw(ctlr, Isr, Rdc);
+ regw(ctlr, Cr, cr);
+
+ return to;
+}
+
+void*
+dp8390read(Dp8390* ctlr, void* to, ulong from, ulong len)
+{
+ void *v;
+
+ ilock(ctlr);
+ v = _dp8390read(ctlr, to, from, len);
+ iunlock(ctlr);
+
+ return v;
+}
+
+static void*
+dp8390write(Dp8390* ctlr, ulong to, void* from, ulong len)
+{
+ ulong crda;
+ uchar cr;
+ int timo, width;
+
+top:
+ /*
+ * Write some data to offset 'to' in the card's memory
+ * using the DP8390 remote DMA facility, reading it at
+ * 'from' in main memory, via the I/O data port.
+ */
+ cr = regr(ctlr, Cr) & ~Txp;
+ regw(ctlr, Cr, Page0|RdABORT|Sta);
+ regw(ctlr, Isr, Rdc);
+
+ len = ROUNDUP(len, ctlr->width);
+
+ /*
+ * Set up the remote DMA address and count.
+ * This is straight from the DP8390[12D] datasheet,
+ * hence the initial set up for read.
+ * Assumption here that the A7000 EtherV card will
+ * never need a dummyrr.
+ */
+ if(ctlr->dummyrr && (ctlr->width == 1 || ctlr->width == 2)){
+ if(ctlr->width == 2)
+ width = 1;
+ else
+ width = 0;
+ crda = to-1-width;
+ regw(ctlr, Rbcr0, (len+1+width) & 0xFF);
+ regw(ctlr, Rbcr1, ((len+1+width)>>8) & 0xFF);
+ regw(ctlr, Rsar0, crda & 0xFF);
+ regw(ctlr, Rsar1, (crda>>8) & 0xFF);
+ regw(ctlr, Cr, Page0|RdREAD|Sta);
+
+ for(timo=0;; timo++){
+ if(timo > 10000){
+ print("ether8390: dummyrr timeout; assuming nodummyrr\n");
+ ctlr->dummyrr = 0;
+ goto top;
+ }
+ crda = regr(ctlr, Crda0);
+ crda |= regr(ctlr, Crda1)<<8;
+ if(crda == to){
+ /*
+ * Start the remote DMA write and make sure
+ * the registers are correct.
+ */
+ regw(ctlr, Cr, Page0|RdWRITE|Sta);
+
+ crda = regr(ctlr, Crda0);
+ crda |= regr(ctlr, Crda1)<<8;
+ if(crda != to)
+ panic("crda write %d to %d\n", crda, to);
+
+ break;
+ }
+ }
+ }
+ else{
+ regw(ctlr, Rsar0, to & 0xFF);
+ regw(ctlr, Rsar1, (to>>8) & 0xFF);
+ regw(ctlr, Rbcr0, len & 0xFF);
+ regw(ctlr, Rbcr1, (len>>8) & 0xFF);
+ regw(ctlr, Cr, Page0|RdWRITE|Sta);
+ }
+
+ /*
+ * Pump the data into the I/O port
+ * then wait for the remote DMA to finish.
+ */
+ rdwrite(ctlr, from, len);
+ for(timo = 10000; (regr(ctlr, Isr) & Rdc) == 0 && timo; timo--)
+ ;
+
+ regw(ctlr, Isr, Rdc);
+ regw(ctlr, Cr, cr);
+
+ return (void*)to;
+}
+
+static void
+ringinit(Dp8390* ctlr)
+{
+ regw(ctlr, Pstart, ctlr->pstart);
+ regw(ctlr, Pstop, ctlr->pstop);
+ regw(ctlr, Bnry, ctlr->pstop-1);
+
+ regw(ctlr, Cr, Page1|RdABORT|Stp);
+ regw(ctlr, Curr, ctlr->pstart);
+ regw(ctlr, Cr, Page0|RdABORT|Stp);
+
+ ctlr->nxtpkt = ctlr->pstart;
+}
+
+static uchar
+getcurr(Dp8390* ctlr)
+{
+ uchar cr, curr;
+
+ cr = regr(ctlr, Cr) & ~Txp;
+ regw(ctlr, Cr, Page1|(~(Ps1|Ps0) & cr));
+ curr = regr(ctlr, Curr);
+ regw(ctlr, Cr, cr);
+
+ return curr;
+}
+
+static void
+receive(Ether* ether)
+{
+ Dp8390 *ctlr;
+ uchar curr, *p;
+ Hdr hdr;
+ ulong count, data, len;
+ RingBuf *ring;
+
+ ctlr = ether->ctlr;
+ for(curr = getcurr(ctlr); ctlr->nxtpkt != curr; curr = getcurr(ctlr)){
+ data = ctlr->nxtpkt*Dp8390BufSz;
+ if(ctlr->ram)
+ memmove(&hdr, (void*)(ether->mem+data), sizeof(Hdr));
+ else
+ _dp8390read(ctlr, &hdr, data, sizeof(Hdr));
+
+ /*
+ * Don't believe the upper byte count, work it
+ * out from the software next-page pointer and
+ * the current next-page pointer.
+ */
+ if(hdr.next > ctlr->nxtpkt)
+ len = hdr.next - ctlr->nxtpkt - 1;
+ else
+ len = (ctlr->pstop-ctlr->nxtpkt) + (hdr.next-ctlr->pstart) - 1;
+ if(hdr.len0 > (Dp8390BufSz-sizeof(Hdr)))
+ len--;
+
+ len = ((len<<8)|hdr.len0)-4;
+
+ /*
+ * Chip is badly scrogged, reinitialise the ring.
+ */
+ if(hdr.next < ctlr->pstart || hdr.next >= ctlr->pstop
+ || len < 60 || len > sizeof(Etherpkt)){
+ print("dp8390: H#%2.2ux#%2.2ux#%2.2ux#%2.2ux,%lud\n",
+ hdr.status, hdr.next, hdr.len0, hdr.len1, len);
+ regw(ctlr, Cr, Page0|RdABORT|Stp);
+ ringinit(ctlr);
+ regw(ctlr, Cr, Page0|RdABORT|Sta);
+
+ return;
+ }
+
+ /*
+ * If it's a good packet read it in to the software buffer.
+ * If the packet wraps round the hardware ring, read it in
+ * two pieces.
+ */
+ ring = &ether->rb[ether->ri];
+ if((hdr.status & (Fo|Fae|Crce|Prxok)) == Prxok && ring->owner == Interface){
+ p = ring->pkt;
+ ring->len = len;
+ data += sizeof(Hdr);
+
+ if((data+len) >= ctlr->pstop*Dp8390BufSz){
+ count = ctlr->pstop*Dp8390BufSz - data;
+ if(ctlr->ram)
+ memmove(p, (void*)(ether->mem+data), count);
+ else
+ _dp8390read(ctlr, p, data, count);
+ p += count;
+ data = ctlr->pstart*Dp8390BufSz;
+ len -= count;
+ }
+ if(len){
+ if(ctlr->ram)
+ memmove(p, (void*)(ether->mem+data), len);
+ else
+ _dp8390read(ctlr, p, data, len);
+ }
+
+ /*
+ * Copy the packet to whoever wants it.
+ */
+ ring->owner = Host;
+ ether->ri = NEXT(ether->ri, ether->nrb);
+ }
+
+ /*
+ * Finished with this packet, update the
+ * hardware and software ring pointers.
+ */
+ ctlr->nxtpkt = hdr.next;
+
+ hdr.next--;
+ if(hdr.next < ctlr->pstart)
+ hdr.next = ctlr->pstop-1;
+ regw(ctlr, Bnry, hdr.next);
+ }
+}
+
+static void
+txstart(Ether* ether)
+{
+ int len;
+ Dp8390 *ctlr;
+ RingBuf *ring;
+ uchar minpkt[ETHERMINTU], *rp;
+
+ ctlr = ether->ctlr;
+
+ /*
+ * This routine is called both from the top level and from interrupt
+ * level and expects to be called with ctlr already locked.
+ */
+ if(ether->tbusy)
+ return;
+ ring = &ether->tb[ether->ti];
+ if(ring->owner != Interface)
+ return;
+
+ /*
+ * Make sure the packet is of minimum length;
+ * copy it to the card's memory by the appropriate means;
+ * start the transmission.
+ */
+ len = ring->len;
+ rp = ring->pkt;
+ if(len < ETHERMINTU){
+ rp = minpkt;
+ memmove(rp, ring->pkt, len);
+ memset(rp+len, 0, ETHERMINTU-len);
+ len = ETHERMINTU;
+ }
+
+ if(ctlr->ram)
+ memmove((void*)(ether->mem+ctlr->tstart*Dp8390BufSz), rp, len);
+ else
+ dp8390write(ctlr, ctlr->tstart*Dp8390BufSz, rp, len);
+
+ regw(ctlr, Tbcr0, len & 0xFF);
+ regw(ctlr, Tbcr1, (len>>8) & 0xFF);
+ regw(ctlr, Cr, Page0|RdABORT|Txp|Sta);
+
+ ether->tbusy = 1;
+}
+
+static void
+transmit(Ether* ether)
+{
+ Dp8390 *ctlr;
+
+ ctlr = ether->ctlr;
+
+ ilock(ctlr);
+ txstart(ether);
+ iunlock(ctlr);
+}
+
+static void
+overflow(Ether *ether)
+{
+ Dp8390 *ctlr;
+ uchar txp;
+ int resend;
+
+ ctlr = ether->ctlr;
+
+ /*
+ * The following procedure is taken from the DP8390[12D] datasheet,
+ * it seems pretty adamant that this is what has to be done.
+ */
+ txp = regr(ctlr, Cr) & Txp;
+ regw(ctlr, Cr, Page0|RdABORT|Stp);
+ delay(2);
+ regw(ctlr, Rbcr0, 0);
+ regw(ctlr, Rbcr1, 0);
+
+ resend = 0;
+ if(txp && (regr(ctlr, Isr) & (Txe|Ptx)) == 0)
+ resend = 1;
+
+ regw(ctlr, Tcr, LpbkNIC);
+ regw(ctlr, Cr, Page0|RdABORT|Sta);
+ receive(ether);
+ regw(ctlr, Isr, Ovw);
+ regw(ctlr, Tcr, LpbkNORMAL);
+
+ if(resend)
+ regw(ctlr, Cr, Page0|RdABORT|Txp|Sta);
+}
+
+static void
+interrupt(Ureg*, void* arg)
+{
+ Ether *ether;
+ Dp8390 *ctlr;
+ RingBuf *ring;
+ uchar isr, r;
+
+ ether = arg;
+ ctlr = ether->ctlr;
+
+ /*
+ * While there is something of interest,
+ * clear all the interrupts and process.
+ */
+ ilock(ctlr);
+ regw(ctlr, Imr, 0x00);
+ while(isr = (regr(ctlr, Isr) & (Cnt|Ovw|Txe|Rxe|Ptx|Prx))){
+ if(isr & Ovw){
+ overflow(ether);
+ regw(ctlr, Isr, Ovw);
+ }
+
+ /*
+ * Packets have been received.
+ * Take a spin round the ring.
+ */
+ if(isr & (Rxe|Prx)){
+ receive(ether);
+ regw(ctlr, Isr, Rxe|Prx);
+ }
+
+ /*
+ * A packet completed transmission, successfully or
+ * not. Start transmission on the next buffered packet,
+ * and wake the output routine.
+ */
+ if(isr & (Txe|Ptx)){
+ r = regr(ctlr, Tsr);
+ if((isr & Txe) && (r & (Cdh|Fu|Crs|Abt))){
+ print("dp8390: Tsr#%2.2ux|", r);
+ }
+
+ regw(ctlr, Isr, Txe|Ptx);
+
+ ring = &ether->tb[ether->ti];
+ ring->owner = Host;
+ ether->ti = NEXT(ether->ti, ether->ntb);
+ ether->tbusy = 0;
+ txstart(ether);
+ }
+
+ if(isr & Cnt){
+ regr(ctlr, Cntr0);
+ regr(ctlr, Cntr1);
+ regr(ctlr, Cntr2);
+ regw(ctlr, Isr, Cnt);
+ }
+ }
+ regw(ctlr, Imr, Cnt|Ovw|Txe|Rxe|Ptx|Prx);
+ iunlock(ctlr);
+}
+
+static void
+attach(Ether* ether)
+{
+ Dp8390 *ctlr;
+ uchar r;
+
+ ctlr = ether->ctlr;
+
+ /*
+ * Enable the chip for transmit/receive.
+ * The init routine leaves the chip in monitor
+ * mode. Clear the missed-packet counter, it
+ * increments while in monitor mode.
+ * Sometimes there's an interrupt pending at this
+ * point but there's nothing in the Isr, so
+ * any pending interrupts are cleared and the
+ * mask of acceptable interrupts is enabled here.
+ */
+ r = Ab;
+ ilock(ctlr);
+ regw(ctlr, Isr, 0xFF);
+ regw(ctlr, Imr, Cnt|Ovw|Txe|Rxe|Ptx|Prx);
+ regw(ctlr, Rcr, r);
+ r = regr(ctlr, Cntr2);
+ regw(ctlr, Tcr, LpbkNORMAL);
+ iunlock(ctlr);
+ USED(r);
+}
+
+static void
+detach(Ether* ether)
+{
+ int timo;
+ Dp8390 *ctlr;
+
+ /*
+ * Stop the chip. Set the Stp bit and wait for the chip
+ * to finish whatever was on its tiny mind before it sets
+ * the Rst bit.
+ * The timeout is needed because there may not be a real
+ * chip there if this is called when probing for a device
+ * at boot.
+ */
+ ctlr = ether->ctlr;
+ regw(ctlr, Cr, Page0|RdABORT|Stp);
+ regw(ctlr, Rbcr0, 0);
+ regw(ctlr, Rbcr1, 0);
+ for(timo = 10000; (regr(ctlr, Isr) & Rst) == 0 && timo; timo--)
+ ;
+}
+
+int
+dp8390reset(Ether* ether)
+{
+ Dp8390 *ctlr;
+
+ ctlr = ether->ctlr;
+
+ /*
+ * This is the initialisation procedure described
+ * as 'mandatory' in the datasheet, with references
+ * to the 3C503 technical reference manual.
+ */
+ detach(ether);
+ if(ctlr->width != 1)
+ regw(ctlr, Dcr, Ft4WORD|Ls|Wts);
+ else
+ regw(ctlr, Dcr, Ft4WORD|Ls);
+
+ regw(ctlr, Rbcr0, 0);
+ regw(ctlr, Rbcr1, 0);
+
+ regw(ctlr, Tcr, LpbkNIC);
+ regw(ctlr, Rcr, Mon);
+
+ /*
+ * Init the ring hardware and software ring pointers.
+ * Can't initialise ethernet address as it may not be
+ * known yet.
+ */
+ ringinit(ctlr);
+ regw(ctlr, Tpsr, ctlr->tstart);
+
+ /*
+ * Clear any pending interrupts and mask then all off.
+ */
+ regw(ctlr, Isr, 0xFF);
+ regw(ctlr, Imr, 0);
+
+ /*
+ * Leave the chip initialised,
+ * but in monitor mode.
+ */
+ regw(ctlr, Cr, Page0|RdABORT|Sta);
+
+ /*
+ * Set up the software configuration.
+ */
+ ether->attach = attach;
+ ether->transmit = transmit;
+ ether->interrupt = interrupt;
+ ether->detach = detach;
+
+ return 0;
+}
diff --git a/os/boot/pc/ether8390.h b/os/boot/pc/ether8390.h
new file mode 100644
index 00000000..1c464110
--- /dev/null
+++ b/os/boot/pc/ether8390.h
@@ -0,0 +1,71 @@
+/*
+ * Ctlr for the boards using the National Semiconductor DP8390
+ * and SMC 83C90 Network Interface Controller.
+ * Common code is in ether8390.c.
+ */
+typedef struct {
+ Lock;
+
+ ulong port; /* I/O address of 8390 */
+ ulong data; /* I/O data port if no shared memory */
+
+ uchar width; /* data transfer width in bytes */
+ uchar ram; /* true if card has shared memory */
+ uchar dummyrr; /* do dummy remote read */
+
+ uchar nxtpkt; /* receive: software bndry */
+ uchar pstart;
+ uchar pstop;
+
+ int txbusy; /* transmit */
+ uchar tstart; /* 8390 ring addresses */
+} Dp8390;
+
+#define Dp8390BufSz 256
+
+extern int dp8390reset(Ether*);
+extern void *dp8390read(Dp8390*, void*, ulong, ulong);
+extern void dp8390getea(Ether*, uchar*);
+extern void dp8390setea(Ether*);
+
+/*
+ * x86-specific code.
+ */
+#define regr(c, r) inb((c)->port+(r))
+#define regw(c, r, v) outb((c)->port+(r), (v))
+
+static void
+rdread(Dp8390* ctlr, void* to, int len)
+{
+ switch(ctlr->width){
+ default:
+ panic("dp8390 rdread: width %d\n", ctlr->width);
+ break;
+
+ case 2:
+ inss(ctlr->data, to, len/2);
+ break;
+
+ case 1:
+ insb(ctlr->data, to, len);
+ break;
+ }
+}
+
+static void
+rdwrite(Dp8390* ctlr, void* from, int len)
+{
+ switch(ctlr->width){
+ default:
+ panic("dp8390 rdwrite: width %d\n", ctlr->width);
+ break;
+
+ case 2:
+ outss(ctlr->data, from, len/2);
+ break;
+
+ case 1:
+ outsb(ctlr->data, from, len);
+ break;
+ }
+}
diff --git a/os/boot/pc/etherec2t.c b/os/boot/pc/etherec2t.c
new file mode 100644
index 00000000..4d91cd25
--- /dev/null
+++ b/os/boot/pc/etherec2t.c
@@ -0,0 +1,155 @@
+/*
+ * Supposed NE2000 PCMCIA clones, see the comments in ether2000.c
+ */
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "etherif.h"
+#include "ether8390.h"
+
+enum {
+ Data = 0x10, /* offset from I/O base of data port */
+ Reset = 0x1F, /* offset from I/O base of reset port */
+};
+
+static char* ec2tpcmcia[] = {
+ "EC2T", /* Linksys Combo PCMCIA EthernetCard */
+ "PCMPC100", /* EtherFast 10/100 PC Card */
+ "EN2216", /* Accton EtherPair-PCMCIA */
+ "FA410TX", /* Netgear FA410TX */
+ "Network Everywhere", /* Linksys NP10T 10BaseT Card */
+ nil,
+};
+
+int
+ec2treset(Ether* ether)
+{
+ ushort buf[16];
+ ulong port;
+ Dp8390 *ctlr;
+ int i, slot;
+ uchar ea[Eaddrlen], sum, x;
+ char *type;
+
+ /*
+ * Set up the software configuration.
+ * Use defaults for port, irq, mem and size
+ * if not specified.
+ * The manual says 16KB memory, the box
+ * says 32KB. The manual seems to be correct.
+ */
+ if(ether->port == 0)
+ ether->port = 0x300;
+ if(ether->irq == 0)
+ ether->irq = 9;
+ if(ether->mem == 0)
+ ether->mem = 0x4000;
+ if(ether->size == 0)
+ ether->size = 16*1024;
+ port = ether->port;
+
+ //if(ioalloc(ether->port, 0x20, 0, "ec2t") < 0)
+ // return -1;
+ slot = -1;
+ type = nil;
+ for(i = 0; ec2tpcmcia[i] != nil; i++){
+ type = ec2tpcmcia[i];
+ if((slot = pcmspecial(type, ether)) >= 0)
+ break;
+ }
+ if(ec2tpcmcia[i] == nil){
+ for(i = 0; i < ether->nopt; i++){
+ if(cistrncmp(ether->opt[i], "id=", 3))
+ continue;
+ type = &ether->opt[i][3];
+ if((slot = pcmspecial(type, ether)) >= 0)
+ break;
+ }
+ }
+ if(slot < 0){
+ // iofree(port);
+ return -1;
+ }
+
+ ether->ctlr = malloc(sizeof(Dp8390));
+ ctlr = ether->ctlr;
+ ctlr->width = 2;
+ ctlr->ram = 0;
+
+ ctlr->port = port;
+ ctlr->data = port+Data;
+
+ ctlr->tstart = HOWMANY(ether->mem, Dp8390BufSz);
+ ctlr->pstart = ctlr->tstart + HOWMANY(sizeof(Etherpkt), Dp8390BufSz);
+ ctlr->pstop = ctlr->tstart + HOWMANY(ether->size, Dp8390BufSz);
+
+ ctlr->dummyrr = 0;
+ for(i = 0; i < ether->nopt; i++){
+ if(cistrcmp(ether->opt[i], "nodummyrr") == 0)
+ ctlr->dummyrr = 0;
+ else if(cistrncmp(ether->opt[i], "dummyrr=", 8) == 0)
+ ctlr->dummyrr = strtol(&ether->opt[i][8], nil, 0);
+ }
+
+ /*
+ * Reset the board. This is done by doing a read
+ * followed by a write to the Reset address.
+ */
+ buf[0] = inb(port+Reset);
+ delay(2);
+ outb(port+Reset, buf[0]);
+ delay(2);
+
+ /*
+ * Init the (possible) chip, then use the (possible)
+ * chip to read the (possible) PROM for ethernet address
+ * and a marker byte.
+ * Could just look at the DP8390 command register after
+ * initialisation has been tried, but that wouldn't be
+ * enough, there are other ethernet boards which could
+ * match.
+ */
+ dp8390reset(ether);
+ sum = 0;
+ if(cistrcmp(type, "PCMPC100") == 0 || cistrcmp(type, "FA410TX") == 0){
+ /*
+ * The PCMPC100 has the ethernet address in I/O space.
+ * There's a checksum over 8 bytes which sums to 0xFF.
+ */
+ for(i = 0; i < 8; i++){
+ x = inb(port+0x14+i);
+ sum += x;
+ buf[i] = (x<<8)|x;
+ }
+ }
+ else{
+ memset(buf, 0, sizeof(buf));
+ dp8390read(ctlr, buf, 0, sizeof(buf));
+ if((buf[0x0E] & 0xFF) == 0x57 && (buf[0x0F] & 0xFF) == 0x57)
+ sum = 0xFF;
+ }
+ if(sum != 0xFF){
+ pcmspecialclose(slot);
+ //iofree(ether->port);
+ free(ether->ctlr);
+ return -1;
+ }
+
+ /*
+ * Stupid machine. Shorts were asked for,
+ * shorts were delivered, although the PROM is a byte array.
+ * Set the ethernet address.
+ */
+ memset(ea, 0, Eaddrlen);
+ if(memcmp(ea, ether->ea, Eaddrlen) == 0){
+ for(i = 0; i < sizeof(ether->ea); i++)
+ ether->ea[i] = buf[i];
+ }
+ dp8390setea(ether);
+
+ return 0;
+}
diff --git a/os/boot/pc/etherelnk3.c b/os/boot/pc/etherelnk3.c
new file mode 100644
index 00000000..618f7a1b
--- /dev/null
+++ b/os/boot/pc/etherelnk3.c
@@ -0,0 +1,1898 @@
+/*
+ * Etherlink III, Fast EtherLink and Fast EtherLink XL adapters.
+ * To do:
+ * check robustness in the face of errors (e.g. busmaster & rxUnderrun);
+ * RxEarly and busmaster;
+ * autoSelect;
+ * PCI latency timer and master enable;
+ * errata list;
+ * rewrite all initialisation.
+ *
+ * Product ID:
+ * 9150 ISA 3C509[B]
+ * 9050 ISA 3C509[B]-TP
+ * 9450 ISA 3C509[B]-COMBO
+ * 9550 ISA 3C509[B]-TPO
+ *
+ * 9350 EISA 3C579
+ * 9250 EISA 3C579-TP
+ *
+ * 5920 EISA 3C592-[TP|COMBO|TPO]
+ * 5970 EISA 3C597-TX Fast Etherlink 10BASE-T/100BASE-TX
+ * 5971 EISA 3C597-T4 Fast Etherlink 10BASE-T/100BASE-T4
+ * 5972 EISA 3C597-MII Fast Etherlink 10BASE-T/MII
+ *
+ * 5900 PCI 3C590-[TP|COMBO|TPO]
+ * 5950 PCI 3C595-TX Fast Etherlink Shared 10BASE-T/100BASE-TX
+ * 5951 PCI 3C595-T4 Fast Etherlink Shared 10BASE-T/100BASE-T4
+ * 5952 PCI 3C595-MII Fast Etherlink 10BASE-T/MII
+ *
+ * 9000 PCI 3C900-TPO Etherlink III XL PCI 10BASE-T
+ * 9001 PCI 3C900-COMBO Etherlink III XL PCI 10BASE-T/10BASE-2/AUI
+ * 9005 PCI 3C900B-COMBO Etherlink III XL PCI 10BASE-T/10BASE-2/AUI
+ * 9050 PCI 3C905-TX Fast Etherlink XL Shared 10BASE-T/100BASE-TX
+ * 9051 PCI 3C905-T4 Fast Etherlink Shared 10BASE-T/100BASE-T4
+ * 9055 PCI 3C905B-TX Fast Etherlink Shared 10BASE-T/100BASE-TX
+ * 9200 PCI 3C905C-TX Fast Etherlink Shared 10BASE-T/100BASE-TX
+ *
+ * 9058 PCMCIA 3C589[B]-[TP|COMBO]
+ *
+ * 627C MCA 3C529
+ * 627D MCA 3C529-TP
+ */
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "etherif.h"
+
+#define coherence()
+#define XCVRDEBUG if(0)print
+
+enum {
+ IDport = 0x0110, /* anywhere between 0x0100 and 0x01F0 */
+};
+
+enum { /* all windows */
+ CommandR = 0x000E,
+ IntStatusR = 0x000E,
+};
+
+enum { /* Commands */
+ GlobalReset = 0x0000,
+ SelectRegisterWindow = 0x0001,
+ EnableDcConverter = 0x0002,
+ RxDisable = 0x0003,
+ RxEnable = 0x0004,
+ RxReset = 0x0005,
+ Stall = 0x0006, /* 3C90x */
+ TxDone = 0x0007,
+ RxDiscard = 0x0008,
+ TxEnable = 0x0009,
+ TxDisable = 0x000A,
+ TxReset = 0x000B,
+ RequestInterrupt = 0x000C,
+ AcknowledgeInterrupt = 0x000D,
+ SetInterruptEnable = 0x000E,
+ SetIndicationEnable = 0x000F, /* SetReadZeroMask */
+ SetRxFilter = 0x0010,
+ SetRxEarlyThresh = 0x0011,
+ SetTxAvailableThresh = 0x0012,
+ SetTxStartThresh = 0x0013,
+ StartDma = 0x0014, /* initiate busmaster operation */
+ StatisticsEnable = 0x0015,
+ StatisticsDisable = 0x0016,
+ DisableDcConverter = 0x0017,
+ SetTxReclaimThresh = 0x0018, /* PIO-only adapters */
+ PowerUp = 0x001B, /* not all adapters */
+ PowerDownFull = 0x001C, /* not all adapters */
+ PowerAuto = 0x001D, /* not all adapters */
+};
+
+enum { /* (Global|Rx|Tx)Reset command bits */
+ tpAuiReset = 0x0001, /* 10BaseT and AUI transceivers */
+ endecReset = 0x0002, /* internal Ethernet encoder/decoder */
+ networkReset = 0x0004, /* network interface logic */
+ fifoReset = 0x0008, /* FIFO control logic */
+ aismReset = 0x0010, /* autoinitialise state-machine logic */
+ hostReset = 0x0020, /* bus interface logic */
+ dmaReset = 0x0040, /* bus master logic */
+ vcoReset = 0x0080, /* on-board 10Mbps VCO */
+ updnReset = 0x0100, /* upload/download (Rx/TX) logic */
+
+ resetMask = 0x01FF,
+};
+
+enum { /* Stall command bits */
+ upStall = 0x0000,
+ upUnStall = 0x0001,
+ dnStall = 0x0002,
+ dnUnStall = 0x0003,
+};
+
+enum { /* SetRxFilter command bits */
+ receiveIndividual = 0x0001, /* match station address */
+ receiveMulticast = 0x0002,
+ receiveBroadcast = 0x0004,
+ receiveAllFrames = 0x0008, /* promiscuous */
+};
+
+enum { /* StartDma command bits */
+ Upload = 0x0000, /* transfer data from adapter to memory */
+ Download = 0x0001, /* transfer data from memory to adapter */
+};
+
+enum { /* IntStatus bits */
+ interruptLatch = 0x0001,
+ hostError = 0x0002, /* Adapter Failure */
+ txComplete = 0x0004,
+ txAvailable = 0x0008,
+ rxComplete = 0x0010,
+ rxEarly = 0x0020,
+ intRequested = 0x0040,
+ updateStats = 0x0080,
+ transferInt = 0x0100, /* Bus Master Transfer Complete */
+ dnComplete = 0x0200,
+ upComplete = 0x0400,
+ busMasterInProgress = 0x0800,
+ commandInProgress = 0x1000,
+
+ interruptMask = 0x07FE,
+};
+
+#define COMMAND(port, cmd, a) outs((port)+CommandR, ((cmd)<<11)|(a))
+#define STATUS(port) ins((port)+IntStatusR)
+
+enum { /* Window 0 - setup */
+ Wsetup = 0x0000,
+ /* registers */
+ ManufacturerID = 0x0000, /* 3C5[08]*, 3C59[27] */
+ ProductID = 0x0002, /* 3C5[08]*, 3C59[27] */
+ ConfigControl = 0x0004, /* 3C5[08]*, 3C59[27] */
+ AddressConfig = 0x0006, /* 3C5[08]*, 3C59[27] */
+ ResourceConfig = 0x0008, /* 3C5[08]*, 3C59[27] */
+ EepromCommand = 0x000A,
+ EepromData = 0x000C,
+ /* AddressConfig Bits */
+ autoSelect9 = 0x0080,
+ xcvrMask9 = 0xC000,
+ /* ConfigControl bits */
+ Ena = 0x0001,
+ base10TAvailable9 = 0x0200,
+ coaxAvailable9 = 0x1000,
+ auiAvailable9 = 0x2000,
+ /* EepromCommand bits */
+ _EepromReadRegister = 0x0080,
+ _EepromRead8bRegister = 0x0230,
+ EepromBusy = 0x8000,
+};
+
+static int EepromReadRegister = _EepromReadRegister;
+
+#define EEPROMCMD(port, cmd, a) outs((port)+EepromCommand, (cmd)|(a))
+#define EEPROMBUSY(port) (ins((port)+EepromCommand) & EepromBusy)
+#define EEPROMDATA(port) ins((port)+EepromData)
+
+enum { /* Window 1 - operating set */
+ Wop = 0x0001,
+ /* registers */
+ Fifo = 0x0000,
+ RxError = 0x0004, /* 3C59[0257] only */
+ RxStatus = 0x0008,
+ Timer = 0x000A,
+ TxStatus = 0x000B,
+ TxFree = 0x000C,
+ /* RxError bits */
+ rxOverrun = 0x0001,
+ runtFrame = 0x0002,
+ alignmentError = 0x0004, /* Framing */
+ crcError = 0x0008,
+ oversizedFrame = 0x0010,
+ dribbleBits = 0x0080,
+ /* RxStatus bits */
+ rxBytes = 0x1FFF, /* 3C59[0257] mask */
+ rxBytes9 = 0x07FF, /* 3C5[078]9 mask */
+ rxError9 = 0x3800, /* 3C5[078]9 error mask */
+ rxOverrun9 = 0x0000,
+ oversizedFrame9 = 0x0800,
+ dribbleBits9 = 0x1000,
+ runtFrame9 = 0x1800,
+ alignmentError9 = 0x2000, /* Framing */
+ crcError9 = 0x2800,
+ rxError = 0x4000,
+ rxIncomplete = 0x8000,
+ /* TxStatus Bits */
+ txStatusOverflow = 0x0004,
+ maxCollisions = 0x0008,
+ txUnderrun = 0x0010,
+ txJabber = 0x0020,
+ interruptRequested = 0x0040,
+ txStatusComplete = 0x0080,
+};
+
+enum { /* Window 2 - station address */
+ Wstation = 0x0002,
+
+ ResetOp905B = 0x000C,
+};
+
+enum { /* Window 3 - FIFO management */
+ Wfifo = 0x0003,
+ /* registers */
+ InternalConfig = 0x0000, /* 3C509B, 3C589, 3C59[0257] */
+ OtherInt = 0x0004, /* 3C59[0257] */
+ RomControl = 0x0006, /* 3C509B, 3C59[27] */
+ MacControl = 0x0006, /* 3C59[0257] */
+ ResetOptions = 0x0008, /* 3C59[0257] */
+ MediaOptions = 0x0008, /* 3C905B */
+ RxFree = 0x000A,
+ /* InternalConfig bits */
+ disableBadSsdDetect = 0x00000100,
+ ramLocation = 0x00000200, /* 0 external, 1 internal */
+ ramPartition5to3 = 0x00000000,
+ ramPartition3to1 = 0x00010000,
+ ramPartition1to1 = 0x00020000,
+ ramPartition3to5 = 0x00030000,
+ ramPartitionMask = 0x00030000,
+ xcvr10BaseT = 0x00000000,
+ xcvrAui = 0x00100000, /* 10BASE5 */
+ xcvr10Base2 = 0x00300000,
+ xcvr100BaseTX = 0x00400000,
+ xcvr100BaseFX = 0x00500000,
+ xcvrMii = 0x00600000,
+ xcvrMask = 0x00700000,
+ autoSelect = 0x01000000,
+ /* MacControl bits */
+ deferExtendEnable = 0x0001,
+ deferTimerSelect = 0x001E, /* mask */
+ fullDuplexEnable = 0x0020,
+ allowLargePackets = 0x0040,
+ extendAfterCollision = 0x0080, /* 3C90xB */
+ flowControlEnable = 0x0100, /* 3C90xB */
+ vltEnable = 0x0200, /* 3C90xB */
+ /* ResetOptions bits */
+ baseT4Available = 0x0001,
+ baseTXAvailable = 0x0002,
+ baseFXAvailable = 0x0004,
+ base10TAvailable = 0x0008,
+ coaxAvailable = 0x0010,
+ auiAvailable = 0x0020,
+ miiConnector = 0x0040,
+};
+
+enum { /* Window 4 - diagnostic */
+ Wdiagnostic = 0x0004,
+ /* registers */
+ VcoDiagnostic = 0x0002,
+ FifoDiagnostic = 0x0004,
+ NetworkDiagnostic = 0x0006,
+ PhysicalMgmt = 0x0008,
+ MediaStatus = 0x000A,
+ BadSSD = 0x000C,
+ UpperBytesOk = 0x000D,
+ /* FifoDiagnostic bits */
+ txOverrun = 0x0400,
+ rxUnderrun = 0x2000,
+ receiving = 0x8000,
+ /* PhysicalMgmt bits */
+ mgmtClk = 0x0001,
+ mgmtData = 0x0002,
+ mgmtDir = 0x0004,
+ cat5LinkTestDefeat = 0x8000,
+ /* MediaStatus bits */
+ dataRate100 = 0x0002,
+ crcStripDisable = 0x0004,
+ enableSqeStats = 0x0008,
+ collisionDetect = 0x0010,
+ carrierSense = 0x0020,
+ jabberGuardEnable = 0x0040,
+ linkBeatEnable = 0x0080,
+ jabberDetect = 0x0200,
+ polarityReversed = 0x0400,
+ linkBeatDetect = 0x0800,
+ txInProg = 0x1000,
+ dcConverterEnabled = 0x4000,
+ auiDisable = 0x8000, /* 10BaseT transceiver selected */
+};
+
+enum { /* Window 5 - internal state */
+ Wstate = 0x0005,
+ /* registers */
+ TxStartThresh = 0x0000,
+ TxAvailableThresh = 0x0002,
+ RxEarlyThresh = 0x0006,
+ RxFilter = 0x0008,
+ InterruptEnable = 0x000A,
+ IndicationEnable = 0x000C,
+};
+
+enum { /* Window 6 - statistics */
+ Wstatistics = 0x0006,
+ /* registers */
+ CarrierLost = 0x0000,
+ SqeErrors = 0x0001,
+ MultipleColls = 0x0002,
+ SingleCollFrames = 0x0003,
+ LateCollisions = 0x0004,
+ RxOverruns = 0x0005,
+ FramesXmittedOk = 0x0006,
+ FramesRcvdOk = 0x0007,
+ FramesDeferred = 0x0008,
+ UpperFramesOk = 0x0009,
+ BytesRcvdOk = 0x000A,
+ BytesXmittedOk = 0x000C,
+};
+
+enum { /* Window 7 - bus master operations */
+ Wmaster = 0x0007,
+ /* registers */
+ MasterAddress = 0x0000,
+ MasterLen = 0x0006,
+ MasterStatus = 0x000C,
+ /* MasterStatus bits */
+ masterAbort = 0x0001,
+ targetAbort = 0x0002,
+ targetRetry = 0x0004,
+ targetDisc = 0x0008,
+ masterDownload = 0x1000,
+ masterUpload = 0x4000,
+ masterInProgress = 0x8000,
+
+ masterMask = 0xD00F,
+};
+
+enum { /* 3C90x extended register set */
+ Timer905 = 0x001A, /* 8-bits */
+ TxStatus905 = 0x001B, /* 8-bits */
+ PktStatus = 0x0020, /* 32-bits */
+ DnListPtr = 0x0024, /* 32-bits, 8-byte aligned */
+ FragAddr = 0x0028, /* 32-bits */
+ FragLen = 0x002C, /* 16-bits */
+ ListOffset = 0x002E, /* 8-bits */
+ TxFreeThresh = 0x002F, /* 8-bits */
+ UpPktStatus = 0x0030, /* 32-bits */
+ FreeTimer = 0x0034, /* 16-bits */
+ UpListPtr = 0x0038, /* 32-bits, 8-byte aligned */
+
+ /* PktStatus bits */
+ fragLast = 0x00000001,
+ dnCmplReq = 0x00000002,
+ dnStalled = 0x00000004,
+ upCompleteX = 0x00000008,
+ dnCompleteX = 0x00000010,
+ upRxEarlyEnable = 0x00000020,
+ armCountdown = 0x00000040,
+ dnInProg = 0x00000080,
+ counterSpeed = 0x00000010, /* 0 3.2uS, 1 320nS */
+ countdownMode = 0x00000020,
+ /* UpPktStatus bits (dpd->control) */
+ upPktLenMask = 0x00001FFF,
+ upStalled = 0x00002000,
+ upError = 0x00004000,
+ upPktComplete = 0x00008000,
+ upOverrun = 0x00010000, /* RxError<<16 */
+ upRuntFrame = 0x00020000,
+ upAlignmentError = 0x00040000,
+ upCRCError = 0x00080000,
+ upOversizedFrame = 0x00100000,
+ upDribbleBits = 0x00800000,
+ upOverflow = 0x01000000,
+
+ dnIndicate = 0x80000000, /* FrameStartHeader (dpd->control) */
+
+ updnLastFrag = 0x80000000, /* (dpd->len) */
+
+ Nup = 32,
+ Ndn = 64,
+};
+
+/*
+ * Up/Dn Packet Descriptors.
+ * The hardware info (np, control, addr, len) must be 8-byte aligned
+ * and this structure size must be a multiple of 8.
+ */
+typedef struct Pd Pd;
+typedef struct Pd {
+ ulong np; /* next pointer */
+ ulong control; /* FSH or UpPktStatus */
+ ulong addr;
+ ulong len;
+
+ Pd* next;
+ void *vaddr;
+} Pd;
+
+typedef struct {
+ Lock wlock; /* window access */
+
+ int attached;
+ int busmaster;
+ Block* rbp; /* receive buffer */
+
+ Block* txbp; /* FIFO -based transmission */
+ int txthreshold;
+ int txbusy;
+
+ int nup; /* full-busmaster -based reception */
+ void* upbase;
+ Pd* upr;
+ Pd* uphead;
+
+ int ndn; /* full-busmaster -based transmission */
+ void* dnbase;
+ Pd* dnr;
+ Pd* dnhead;
+ Pd* dntail;
+ int dnq;
+
+ long interrupts; /* statistics */
+ long timer[2];
+ long stats[BytesRcvdOk+3];
+
+ int upqmax;
+ int upqmaxhw;
+ ulong upinterrupts;
+ ulong upqueued;
+ ulong upstalls;
+ int dnqmax;
+ int dnqmaxhw;
+ ulong dninterrupts;
+ ulong dnqueued;
+
+ int xcvr; /* transceiver type */
+ int rxstatus9; /* old-style RxStatus register */
+ int rxearly; /* RxEarlyThreshold */
+ int ts; /* threshold shift */
+ int upenabled;
+ int dnenabled;
+} Ctlr;
+
+static void
+init905(Ctlr* ctlr)
+{
+ Pd *pd, *prev;
+ uchar *vaddr;
+
+ /*
+ * Create rings for the receive and transmit sides.
+ * Take care with alignment:
+ * make sure ring base is 8-byte aligned;
+ * make sure each entry is 8-byte aligned.
+ */
+ ctlr->upbase = malloc((ctlr->nup+1)*sizeof(Pd));
+ ctlr->upr = (Pd*)ROUNDUP((ulong)ctlr->upbase, 8);
+ vaddr = ialloc((ctlr->nup+1)*ROUNDUP(sizeof(Etherpkt)+4, 8), 8);
+
+ prev = ctlr->upr;
+ for(pd = &ctlr->upr[ctlr->nup-1]; pd >= ctlr->upr; pd--){
+ pd->np = PADDR(&prev->np);
+ pd->control = 0;
+ pd->vaddr = vaddr;
+ pd->addr = PADDR(vaddr);
+ vaddr += ROUNDUP(sizeof(Etherpkt)+4, 8);
+ pd->len = updnLastFrag|sizeof(Etherpkt);
+
+ pd->next = prev;
+ prev = pd;
+ }
+ ctlr->uphead = ctlr->upr;
+
+ ctlr->dnbase = malloc((ctlr->ndn+1)*sizeof(Pd));
+ ctlr->dnr = (Pd*)ROUNDUP((ulong)ctlr->dnbase, 8);
+ vaddr = ialloc((ctlr->ndn+1)*ROUNDUP(sizeof(Etherpkt)+4, 8), 8);
+
+ prev = ctlr->dnr;
+ for(pd = &ctlr->dnr[ctlr->ndn-1]; pd >= ctlr->dnr; pd--){
+ pd->next = prev;
+ pd->vaddr = vaddr;
+ pd->addr = PADDR(vaddr);
+ vaddr += ROUNDUP(sizeof(Etherpkt)+4, 8);
+ prev = pd;
+ }
+ ctlr->dnhead = ctlr->dnr;
+ ctlr->dntail = ctlr->dnr;
+ ctlr->dnq = 0;
+}
+
+static Block*
+rbpalloc(Block* (*f)(int))
+{
+ Block *bp;
+ ulong addr;
+
+ /*
+ * The receive buffers must be on a 32-byte
+ * boundary for EISA busmastering.
+ */
+ if(bp = f(ROUNDUP(sizeof(Etherpkt), 4) + 31)){
+ addr = (ulong)bp->base;
+ addr = ROUNDUP(addr, 32);
+ bp->rp = (uchar*)addr;
+ }
+
+ return bp;
+}
+
+static uchar*
+startdma(Ether* ether, ulong address)
+{
+ int port, status, w;
+ uchar *wp;
+
+ port = ether->port;
+
+ w = (STATUS(port)>>13) & 0x07;
+ COMMAND(port, SelectRegisterWindow, Wmaster);
+
+ wp = KADDR(inl(port+MasterAddress));
+ status = ins(port+MasterStatus);
+ if(status & (masterInProgress|targetAbort|masterAbort))
+ print("#l%d: BM status 0x%uX\n", ether->ctlrno, status);
+ outs(port+MasterStatus, masterMask);
+ outl(port+MasterAddress, address);
+ outs(port+MasterLen, sizeof(Etherpkt));
+ COMMAND(port, StartDma, Upload);
+
+ COMMAND(port, SelectRegisterWindow, w);
+ return wp;
+}
+
+/* On the 575B and C, interrupts need to be acknowledged in CardBus memory space */
+static void
+intrack3c575(ulong *cbfns)
+{
+ cbfns[1] = 0x8000;
+}
+
+static void
+attach(Ether* ether)
+{
+ int port, x;
+ Ctlr *ctlr;
+
+ ctlr = ether->ctlr;
+ ilock(&ctlr->wlock);
+ if(ctlr->attached){
+ iunlock(&ctlr->wlock);
+ return;
+ }
+
+ port = ether->port;
+
+ COMMAND(port, SetRxFilter, receiveIndividual|receiveBroadcast);
+ x = interruptMask;
+ if(ctlr->busmaster == 1)
+ x &= ~(rxEarly|rxComplete);
+ else{
+ if(ctlr->dnenabled)
+ x &= ~transferInt;
+ if(ctlr->upenabled)
+ x &= ~(rxEarly|rxComplete);
+ }
+ COMMAND(port, SetIndicationEnable, x);
+ COMMAND(port, SetInterruptEnable, x);
+ COMMAND(port, RxEnable, 0);
+ COMMAND(port, TxEnable, 0);
+
+ if (ether->mem)
+ /* This must be a cardbus card. Acknowledge the interrupt */
+ intrack3c575(KADDR(ether->mem));
+
+ /*
+ * Prime the busmaster channel for receiving directly into a
+ * receive packet buffer if necessary.
+ */
+ if(ctlr->busmaster == 1)
+ startdma(ether, PADDR(ctlr->rbp->rp));
+ else{
+ if(ctlr->upenabled)
+ outl(port+UpListPtr, PADDR(&ctlr->uphead->np));
+ }
+
+ ctlr->attached = 1;
+ iunlock(&ctlr->wlock);
+
+}
+
+static void
+statistics(Ether* ether)
+{
+ int port, i, u, w;
+ Ctlr *ctlr;
+
+ port = ether->port;
+ ctlr = ether->ctlr;
+
+ /*
+ * 3C59[27] require a read between a PIO write and
+ * reading a statistics register.
+ */
+ w = (STATUS(port)>>13) & 0x07;
+ COMMAND(port, SelectRegisterWindow, Wstatistics);
+ STATUS(port);
+
+ for(i = 0; i < UpperFramesOk; i++)
+ ctlr->stats[i] += inb(port+i) & 0xFF;
+ u = inb(port+UpperFramesOk) & 0xFF;
+ ctlr->stats[FramesXmittedOk] += (u & 0x30)<<4;
+ ctlr->stats[FramesRcvdOk] += (u & 0x03)<<8;
+ ctlr->stats[BytesRcvdOk] += ins(port+BytesRcvdOk) & 0xFFFF;
+ ctlr->stats[BytesRcvdOk+1] += ins(port+BytesXmittedOk) & 0xFFFF;
+
+ switch(ctlr->xcvr){
+
+ case xcvrMii:
+ case xcvr100BaseTX:
+ case xcvr100BaseFX:
+ COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+ STATUS(port);
+ ctlr->stats[BytesRcvdOk+2] += inb(port+BadSSD);
+ break;
+ }
+
+ COMMAND(port, SelectRegisterWindow, w);
+}
+
+static void
+txstart(Ether* ether)
+{
+ int port, len;
+ Ctlr *ctlr;
+ RingBuf *tb;
+
+ port = ether->port;
+ ctlr = ether->ctlr;
+
+ /*
+ * Attempt to top-up the transmit FIFO. If there's room simply
+ * stuff in the packet length (unpadded to a dword boundary), the
+ * packet data (padded) and remove the packet from the queue.
+ * If there's no room post an interrupt for when there is.
+ * This routine is called both from the top level and from interrupt
+ * level and expects to be called with ctlr->wlock already locked
+ * and the correct register window (Wop) in place.
+ */
+ for(tb = &ether->tb[ether->ti]; tb->owner == Interface; tb = &ether->tb[ether->ti]){
+ len = ROUNDUP(tb->len, 4);
+ if(len+4 <= ins(port+TxFree)){
+ outl(port+Fifo, tb->len);
+ outsl(port+Fifo, tb->pkt, len/4);
+ tb->owner = Host;
+ ether->ti = NEXT(ether->ti, ether->ntb);
+ }
+ else{
+ if(ctlr->txbusy == 0){
+ ctlr->txbusy = 1;
+ COMMAND(port, SetTxAvailableThresh, len>>ctlr->ts);
+ }
+ break;
+ }
+ }
+}
+
+static void
+txstart905(Ether* ether)
+{
+ Ctlr *ctlr;
+ int port, stalled, timeo;
+ RingBuf *tb;
+ Pd *pd;
+
+ ctlr = ether->ctlr;
+ port = ether->port;
+
+ /*
+ * Free any completed packets.
+ */
+ pd = ctlr->dntail;
+ while(ctlr->dnq){
+ if(PADDR(&pd->np) == inl(port+DnListPtr))
+ break;
+ ctlr->dnq--;
+ pd = pd->next;
+ }
+ ctlr->dntail = pd;
+
+ stalled = 0;
+ while(ctlr->dnq < (ctlr->ndn-1)){
+ tb = &ether->tb[ether->ti];
+ if(tb->owner != Interface)
+ break;
+
+ pd = ctlr->dnhead->next;
+ pd->np = 0;
+ pd->control = dnIndicate|tb->len;
+ memmove(pd->vaddr, tb->pkt, tb->len);
+ pd->len = updnLastFrag|tb->len;
+
+ tb->owner = Host;
+ ether->ti = NEXT(ether->ti, ether->ntb);
+
+ if(stalled == 0 && ctlr->dnq && inl(port+DnListPtr)){
+ COMMAND(port, Stall, dnStall);
+ for(timeo = 100; (STATUS(port) & commandInProgress) && timeo; timeo--)
+ ;
+ if(timeo == 0)
+ print("#l%d: dnstall %d\n", ether->ctlrno, timeo);
+ stalled = 1;
+ }
+
+ coherence();
+ ctlr->dnhead->np = PADDR(&pd->np);
+ ctlr->dnhead->control &= ~dnIndicate;
+ ctlr->dnhead = pd;
+ if(ctlr->dnq == 0)
+ ctlr->dntail = pd;
+ ctlr->dnq++;
+
+ ctlr->dnqueued++;
+ }
+
+ if(ctlr->dnq > ctlr->dnqmax)
+ ctlr->dnqmax = ctlr->dnq;
+
+ /*
+ * If the adapter is not currently processing anything
+ * and there is something on the queue, start it processing.
+ */
+ if(inl(port+DnListPtr) == 0 && ctlr->dnq)
+ outl(port+DnListPtr, PADDR(&ctlr->dnhead->np));
+ if(stalled)
+ COMMAND(port, Stall, dnUnStall);
+}
+
+static void
+transmit(Ether* ether)
+{
+ Ctlr *ctlr;
+ int port, w;
+
+ port = ether->port;
+ ctlr = ether->ctlr;
+
+ ilock(&ctlr->wlock);
+ if(ctlr->dnenabled)
+ txstart905(ether);
+ else{
+ w = (STATUS(port)>>13) & 0x07;
+ COMMAND(port, SelectRegisterWindow, Wop);
+ txstart(ether);
+ COMMAND(port, SelectRegisterWindow, w);
+ }
+ iunlock(&ctlr->wlock);
+}
+
+static void
+receive905(Ether* ether)
+{
+ Ctlr *ctlr;
+ int len, port, q;
+ Pd *pd;
+ RingBuf *rb;
+
+ ctlr = ether->ctlr;
+ port = ether->port;
+
+ if(inl(port+UpPktStatus) & upStalled)
+ ctlr->upstalls++;
+ q = 0;
+ for(pd = ctlr->uphead; pd->control & upPktComplete; pd = pd->next){
+ if(!(pd->control & upError)){
+ rb = &ether->rb[ether->ri];
+ if (rb->owner == Interface) {
+ len = pd->control & rxBytes;
+ rb->len = len;
+ memmove(rb->pkt, pd->vaddr, len);
+ rb->owner = Host;
+ ether->ri = NEXT(ether->ri, ether->nrb);
+ }
+ }
+
+ pd->control = 0;
+ COMMAND(port, Stall, upUnStall);
+
+ q++;
+ }
+ ctlr->uphead = pd;
+
+ ctlr->upqueued += q;
+ if(q > ctlr->upqmax)
+ ctlr->upqmax = q;
+}
+
+static void
+receive(Ether* ether)
+{
+ int len, port, rxstatus;
+ RingBuf *rb;
+ Ctlr *ctlr;
+
+ port = ether->port;
+ ctlr = ether->ctlr;
+
+ while(((rxstatus = ins(port+RxStatus)) & rxIncomplete) == 0){
+ if(ctlr->busmaster == 1 && (STATUS(port) & busMasterInProgress))
+ break;
+
+ /*
+ * If there was an error, log it and continue.
+ * Unfortunately the 3C5[078]9 has the error info in the status register
+ * and the 3C59[0257] implement a separate RxError register.
+ */
+ if((rxstatus & rxError) == 0){
+ /*
+ * Packet received. Read it into the next free
+ * ring buffer, if any. Must read len bytes padded
+ * to a doubleword, can be picked out 32-bits at
+ * a time. The CRC is already stripped off.
+ */
+ rb = &ether->rb[ether->ri];
+ if(rb->owner == Interface){
+ len = (rxstatus & rxBytes9);
+ rb->len = len;
+ insl(port+Fifo, rb->pkt, HOWMANY(len, 4));
+
+ rb->owner = Host;
+ ether->ri = NEXT(ether->ri, ether->nrb);
+ }else
+if(debug) print("toss...");
+ }
+else
+if(debug) print("error...");
+
+ /*
+ * All done, discard the packet.
+ */
+ COMMAND(port, RxDiscard, 0);
+ while(STATUS(port) & commandInProgress)
+ ;
+ }
+}
+
+static void
+interrupt(Ureg*, void* arg)
+{
+ Ether *ether;
+ int port, status, s, txstatus, w, x;
+ Ctlr *ctlr;
+
+ ether = arg;
+ port = ether->port;
+ ctlr = ether->ctlr;
+
+ ilock(&ctlr->wlock);
+ status = STATUS(port);
+ if(!(status & (interruptMask|interruptLatch))){
+ iunlock(&ctlr->wlock);
+ return;
+ }
+ w = (status>>13) & 0x07;
+ COMMAND(port, SelectRegisterWindow, Wop);
+
+ ctlr->interrupts++;
+ if(ctlr->busmaster == 2)
+ ctlr->timer[0] += inb(port+Timer905) & 0xFF;
+ else
+ ctlr->timer[0] += inb(port+Timer) & 0xFF;
+
+ do{
+ if(status & hostError){
+ /*
+ * Adapter failure, try to find out why, reset if
+ * necessary. What happens if Tx is active and a reset
+ * occurs, need to retransmit? This probably isn't right.
+ */
+ COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+ x = ins(port+FifoDiagnostic);
+ COMMAND(port, SelectRegisterWindow, Wop);
+ print("#l%d: status 0x%uX, diag 0x%uX\n",
+ ether->ctlrno, status, x);
+
+ if(x & txOverrun){
+ if(ctlr->busmaster == 0)
+ COMMAND(port, TxReset, 0);
+ else
+ COMMAND(port, TxReset, (updnReset|dmaReset));
+ COMMAND(port, TxEnable, 0);
+ }
+
+ if(x & rxUnderrun){
+ /*
+ * This shouldn't happen...
+ * Reset the receiver and restore the filter and RxEarly
+ * threshold before re-enabling.
+ * Need to restart any busmastering?
+ */
+ COMMAND(port, SelectRegisterWindow, Wstate);
+ s = (port+RxFilter) & 0x000F;
+ COMMAND(port, SelectRegisterWindow, Wop);
+ COMMAND(port, RxReset, 0);
+ while(STATUS(port) & commandInProgress)
+ ;
+ COMMAND(port, SetRxFilter, s);
+ COMMAND(port, SetRxEarlyThresh, ctlr->rxearly>>ctlr->ts);
+ COMMAND(port, RxEnable, 0);
+ }
+
+ status &= ~hostError;
+ }
+
+ if(status & (transferInt|rxComplete)){
+ receive(ether);
+ status &= ~(transferInt|rxComplete);
+ }
+
+ if(status & (upComplete)){
+ COMMAND(port, AcknowledgeInterrupt, upComplete);
+ receive905(ether);
+ status &= ~upComplete;
+ ctlr->upinterrupts++;
+ }
+
+ if(status & txComplete){
+ /*
+ * Pop the TxStatus stack, accumulating errors.
+ * Adjust the TX start threshold if there was an underrun.
+ * If there was a Jabber or Underrun error, reset
+ * the transmitter, taking care not to reset the dma logic
+ * as a busmaster receive may be in progress.
+ * For all conditions enable the transmitter.
+ */
+ if(ctlr->busmaster == 2)
+ txstatus = port+TxStatus905;
+ else
+ txstatus = port+TxStatus;
+ s = 0;
+ do{
+ if(x = inb(txstatus))
+ outb(txstatus, 0);
+ s |= x;
+ }while(STATUS(port) & txComplete);
+
+ if(s & txUnderrun){
+ if(ctlr->dnenabled){
+ while(inl(port+PktStatus) & dnInProg)
+ ;
+ }
+ COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+ while(ins(port+MediaStatus) & txInProg)
+ ;
+ COMMAND(port, SelectRegisterWindow, Wop);
+ if(ctlr->txthreshold < ETHERMAXTU)
+ ctlr->txthreshold += ETHERMINTU;
+ }
+
+ /*
+ * According to the manual, maxCollisions does not require
+ * a TxReset, merely a TxEnable. However, evidence points to
+ * it being necessary on the 3C905. The jury is still out.
+ * On busy or badly configured networks maxCollisions can
+ * happen frequently enough for messages to be annoying so
+ * keep quiet about them by popular request.
+ */
+ if(s & (txJabber|txUnderrun|maxCollisions)){
+ if(ctlr->busmaster == 0)
+ COMMAND(port, TxReset, 0);
+ else
+ COMMAND(port, TxReset, (updnReset|dmaReset));
+ while(STATUS(port) & commandInProgress)
+ ;
+ COMMAND(port, SetTxStartThresh, ctlr->txthreshold>>ctlr->ts);
+ if(ctlr->busmaster == 2)
+ outl(port+TxFreeThresh, HOWMANY(ETHERMAXTU, 256));
+ if(ctlr->dnenabled)
+ status |= dnComplete;
+ }
+
+ if(s & ~(txStatusComplete|maxCollisions))
+ print("#l%d: txstatus 0x%uX, threshold %d\n",
+ ether->ctlrno, s, ctlr->txthreshold);
+ COMMAND(port, TxEnable, 0);
+ status &= ~txComplete;
+ status |= txAvailable;
+ }
+
+ if(status & txAvailable){
+ COMMAND(port, AcknowledgeInterrupt, txAvailable);
+ ctlr->txbusy = 0;
+ txstart(ether);
+ status &= ~txAvailable;
+ }
+
+ if(status & dnComplete){
+ COMMAND(port, AcknowledgeInterrupt, dnComplete);
+ txstart905(ether);
+ status &= ~dnComplete;
+ ctlr->dninterrupts++;
+ }
+
+ if(status & updateStats){
+ statistics(ether);
+ status &= ~updateStats;
+ }
+
+ /*
+ * Currently, this shouldn't happen.
+ */
+ if(status & rxEarly){
+ COMMAND(port, AcknowledgeInterrupt, rxEarly);
+ status &= ~rxEarly;
+ }
+
+ /*
+ * Panic if there are any interrupts not dealt with.
+ */
+ if(status & interruptMask)
+ panic("#l%d: interrupt mask 0x%uX\n", ether->ctlrno, status);
+
+ COMMAND(port, AcknowledgeInterrupt, interruptLatch);
+ if (ether->mem)
+ intrack3c575((ulong *)KADDR(ether->mem));
+
+ }while((status = STATUS(port)) & (interruptMask|interruptLatch));
+
+ if(ctlr->busmaster == 2)
+ ctlr->timer[1] += inb(port+Timer905) & 0xFF;
+ else
+ ctlr->timer[1] += inb(port+Timer) & 0xFF;
+
+ COMMAND(port, SelectRegisterWindow, w);
+ iunlock(&ctlr->wlock);
+}
+
+static void
+txrxreset(int port)
+{
+ COMMAND(port, TxReset, 0);
+ while(STATUS(port) & commandInProgress)
+ ;
+ COMMAND(port, RxReset, 0);
+ while(STATUS(port) & commandInProgress)
+ ;
+}
+
+typedef struct Adapter {
+ int port;
+ int irq;
+ int tbdf;
+ ulong cbfns;
+} Adapter;
+static Block* adapter;
+
+static void
+tcmadapter(int port, int irq, int tbdf, ulong cbfns)
+{
+ Block *bp;
+ Adapter *ap;
+
+ bp = allocb(sizeof(Adapter));
+ ap = (Adapter*)bp->rp;
+ ap->port = port;
+ ap->irq = irq;
+ ap->tbdf = tbdf;
+ ap->cbfns = cbfns;
+
+ bp->next = adapter;
+ adapter = bp;
+}
+
+/*
+ * Write two 0 bytes to identify the IDport and then reset the
+ * ID sequence. Then send the ID sequence to the card to get
+ * the card into command state.
+ */
+static void
+idseq(void)
+{
+ int i;
+ uchar al;
+ static int reset, untag;
+
+ /*
+ * One time only:
+ * reset any adapters listening
+ */
+ if(reset == 0){
+ outb(IDport, 0);
+ outb(IDport, 0);
+ outb(IDport, 0xC0);
+ delay(20);
+ reset = 1;
+ }
+
+ outb(IDport, 0);
+ outb(IDport, 0);
+ for(al = 0xFF, i = 0; i < 255; i++){
+ outb(IDport, al);
+ if(al & 0x80){
+ al <<= 1;
+ al ^= 0xCF;
+ }
+ else
+ al <<= 1;
+ }
+
+ /*
+ * One time only:
+ * write ID sequence to get the attention of all adapters;
+ * untag all adapters.
+ * If a global reset is done here on all adapters it will confuse
+ * any ISA cards configured for EISA mode.
+ */
+ if(untag == 0){
+ outb(IDport, 0xD0);
+ untag = 1;
+ }
+}
+
+static ulong
+activate(void)
+{
+ int i;
+ ushort x, acr;
+
+ /*
+ * Do the little configuration dance:
+ *
+ * 2. write the ID sequence to get to command state.
+ */
+ idseq();
+
+ /*
+ * 3. Read the Manufacturer ID from the EEPROM.
+ * This is done by writing the IDPort with 0x87 (0x80
+ * is the 'read EEPROM' command, 0x07 is the offset of
+ * the Manufacturer ID field in the EEPROM).
+ * The data comes back 1 bit at a time.
+ * A delay seems necessary between reading the bits.
+ *
+ * If the ID doesn't match, there are no more adapters.
+ */
+ outb(IDport, 0x87);
+ delay(20);
+ for(x = 0, i = 0; i < 16; i++){
+ delay(20);
+ x <<= 1;
+ x |= inb(IDport) & 0x01;
+ }
+ if(x != 0x6D50)
+ return 0;
+
+ /*
+ * 3. Read the Address Configuration from the EEPROM.
+ * The Address Configuration field is at offset 0x08 in the EEPROM).
+ */
+ outb(IDport, 0x88);
+ for(acr = 0, i = 0; i < 16; i++){
+ delay(20);
+ acr <<= 1;
+ acr |= inb(IDport) & 0x01;
+ }
+
+ return (acr & 0x1F)*0x10 + 0x200;
+}
+
+static void
+tcm509isa(void)
+{
+ int irq, port;
+
+ /*
+ * Attempt to activate all adapters. If adapter is set for
+ * EISA mode (0x3F0), tag it and ignore. Otherwise, activate
+ * it fully.
+ */
+ while(port = activate()){
+ /*
+ * 6. Tag the adapter so it won't respond in future.
+ */
+ outb(IDport, 0xD1);
+ if(port == 0x3F0)
+ continue;
+
+ /*
+ * 6. Activate the adapter by writing the Activate command
+ * (0xFF).
+ */
+ outb(IDport, 0xFF);
+ delay(20);
+
+ /*
+ * 8. Can now talk to the adapter's I/O base addresses.
+ * Use the I/O base address from the acr just read.
+ *
+ * Enable the adapter and clear out any lingering status
+ * and interrupts.
+ */
+ while(STATUS(port) & commandInProgress)
+ ;
+ COMMAND(port, SelectRegisterWindow, Wsetup);
+ outs(port+ConfigControl, Ena);
+
+ txrxreset(port);
+ COMMAND(port, AcknowledgeInterrupt, 0xFF);
+
+ irq = (ins(port+ResourceConfig)>>12) & 0x0F;
+ tcmadapter(port, irq, BUSUNKNOWN, 0);
+ }
+}
+
+static void
+tcm5XXeisa(void)
+{
+ ushort x;
+ int irq, port, slot;
+
+ /*
+ * Check if this is an EISA machine.
+ * If not, nothing to do.
+ */
+ if(strncmp((char*)KADDR(0xFFFD9), "EISA", 4))
+ return;
+
+ /*
+ * Continue through the EISA slots looking for a match on both
+ * 3COM as the manufacturer and 3C579-* or 3C59[27]-* as the product.
+ * If an adapter is found, select window 0, enable it and clear
+ * out any lingering status and interrupts.
+ */
+ for(slot = 1; slot < MaxEISA; slot++){
+ port = slot*0x1000;
+ if(ins(port+0xC80+ManufacturerID) != 0x6D50)
+ continue;
+ x = ins(port+0xC80+ProductID);
+ if((x & 0xF0FF) != 0x9050 && (x & 0xFF00) != 0x5900)
+ continue;
+
+ COMMAND(port, SelectRegisterWindow, Wsetup);
+ outs(port+ConfigControl, Ena);
+
+ txrxreset(port);
+ COMMAND(port, AcknowledgeInterrupt, 0xFF);
+
+ irq = (ins(port+ResourceConfig)>>12) & 0x0F;
+ tcmadapter(port, irq, BUSUNKNOWN, 0);
+ }
+}
+
+static void
+tcm59Xpci(Ether *ether)
+{
+ Pcidev *p;
+ int irq, port;
+ ulong bar;
+
+ p = nil;
+ while(p = pcimatch(p, 0x10B7, 0)){
+ if (p->did == 0x5157) {
+ EepromReadRegister = _EepromRead8bRegister;
+
+ /* Map the CardBus functions */
+ bar = pcicfgr32(p, PciBAR2);
+ print("ether#%d: CardBus functions at %.8luX\n", ether->ctlrno, bar & ~KZERO);
+ }
+ else
+ bar = 0;
+
+ /*
+ * Not prepared to deal with memory-mapped
+ * devices yet.
+ */
+ if(!(p->mem[0].bar & 0x01))
+ continue;
+ port = p->mem[0].bar & ~0x01;
+ irq = p->intl;
+ COMMAND(port, GlobalReset, 0);
+ while(STATUS(port) & commandInProgress)
+ ;
+
+ tcmadapter(port, irq, p->tbdf, bar);
+ pcisetbme(p);
+ }
+}
+
+static char* tcmpcmcia[] = {
+ "3C589", /* 3COM 589[ABCD] */
+ "3C562", /* 3COM 562 */
+ "589E", /* 3COM Megahertz 589E */
+ nil,
+};
+
+static int
+tcm5XXpcmcia(Ether* ether)
+{
+ int i;
+
+ for(i = 0; tcmpcmcia[i] != nil; i++){
+ if(!cistrcmp(ether->type, tcmpcmcia[i])){
+ /*
+ * No need for an ioalloc here, the 589 reset
+ * code deals with it.
+ if(ioalloc(ether->port, 0x10, 0, "tcm5XXpcmcia") < 0)
+ return 0;
+ */
+ return ether->port;
+ }
+ }
+
+ return 0;
+}
+
+static void
+setxcvr(int port, int xcvr, int is9)
+{
+ int x;
+
+ if(is9){
+ COMMAND(port, SelectRegisterWindow, Wsetup);
+ x = ins(port+AddressConfig) & ~xcvrMask9;
+ x |= (xcvr>>20)<<14;
+ outs(port+AddressConfig, x);
+ }
+ else{
+ COMMAND(port, SelectRegisterWindow, Wfifo);
+ x = inl(port+InternalConfig) & ~xcvrMask;
+ x |= xcvr;
+ outl(port+InternalConfig, x);
+ }
+
+ txrxreset(port);
+}
+
+static void
+setfullduplex(int port)
+{
+ int x;
+
+ COMMAND(port, SelectRegisterWindow, Wfifo);
+ x = ins(port+MacControl);
+ outs(port+MacControl, fullDuplexEnable|x);
+
+ txrxreset(port);
+}
+
+static int
+miimdi(int port, int n)
+{
+ int data, i;
+
+ /*
+ * Read n bits from the MII Management Register.
+ */
+ data = 0;
+ for(i = n-1; i >= 0; i--){
+ if(ins(port) & mgmtData)
+ data |= (1<<i);
+ microdelay(1);
+ outs(port, mgmtClk);
+ microdelay(1);
+ outs(port, 0);
+ microdelay(1);
+ }
+
+ return data;
+}
+
+static void
+miimdo(int port, int bits, int n)
+{
+ int i, mdo;
+
+ /*
+ * Write n bits to the MII Management Register.
+ */
+ for(i = n-1; i >= 0; i--){
+ if(bits & (1<<i))
+ mdo = mgmtDir|mgmtData;
+ else
+ mdo = mgmtDir;
+ outs(port, mdo);
+ microdelay(1);
+ outs(port, mdo|mgmtClk);
+ microdelay(1);
+ outs(port, mdo);
+ microdelay(1);
+ }
+}
+
+static int
+miir(int port, int phyad, int regad)
+{
+ int data, w;
+
+ w = (STATUS(port)>>13) & 0x07;
+ COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+ port += PhysicalMgmt;
+
+ /*
+ * Preamble;
+ * ST+OP+PHYAD+REGAD;
+ * TA + 16 data bits.
+ */
+ miimdo(port, 0xFFFFFFFF, 32);
+ miimdo(port, 0x1800|(phyad<<5)|regad, 14);
+ data = miimdi(port, 18);
+
+ port -= PhysicalMgmt;
+ COMMAND(port, SelectRegisterWindow, w);
+
+ if(data & 0x10000)
+ return -1;
+
+ return data & 0xFFFF;
+}
+
+static void
+scanphy(int port)
+{
+ int i, x;
+
+ for(i = 0; i < 32; i++){
+ if((x = miir(port, i, 2)) == -1 || x == 0)
+ continue;
+ x <<= 6;
+ x |= miir(port, i, 3)>>10;
+ XCVRDEBUG("phy%d: oui %uX reg1 %uX\n", i, x, miir(port, i, 1));
+ USED(x);
+ }
+}
+
+#ifdef notdef
+static struct xxx {
+ int available;
+ int next;
+} xxx[8] = {
+ { base10TAvailable, 1, }, /* xcvr10BaseT -> xcvrAui */
+ { auiAvailable, 3, }, /* xcvrAui -> xcvr10Base2 */
+ { 0, -1, },
+ { coaxAvailable, -1, }, /* xcvr10Base2 -> nowhere */
+ { baseTXAvailable, 5, }, /* xcvr100BaseTX-> xcvr100BaseFX */
+ { baseFXAvailable, -1, }, /* xcvr100BaseFX-> nowhere */
+ { miiConnector, -1, }, /* xcvrMii -> nowhere */
+ { 0, -1, },
+};
+#endif /* notdef */
+
+static struct {
+ char *name;
+ int avail;
+ int xcvr;
+} media[] = {
+ "10BaseT", base10TAvailable, xcvr10BaseT,
+ "10Base2", coaxAvailable, xcvr10Base2,
+ "100BaseTX", baseTXAvailable, xcvr100BaseTX,
+ "100BaseFX", baseFXAvailable, xcvr100BaseFX,
+ "aui", auiAvailable, xcvrAui,
+ "mii", miiConnector, xcvrMii
+};
+
+static int
+autoselect(int port, int xcvr, int is9)
+{
+ int media, x;
+ USED(xcvr);
+
+ /*
+ * Pathetic attempt at automatic media selection.
+ * Really just to get the Fast Etherlink 10BASE-T/100BASE-TX
+ * cards operational.
+ * It's a bonus if it works for anything else.
+ */
+ if(is9){
+ COMMAND(port, SelectRegisterWindow, Wsetup);
+ x = ins(port+ConfigControl);
+ media = 0;
+ if(x & base10TAvailable9)
+ media |= base10TAvailable;
+ if(x & coaxAvailable9)
+ media |= coaxAvailable;
+ if(x & auiAvailable9)
+ media |= auiAvailable;
+ }
+ else{
+ COMMAND(port, SelectRegisterWindow, Wfifo);
+ media = ins(port+ResetOptions);
+ }
+ XCVRDEBUG("autoselect: media %uX\n", media);
+
+ if(media & miiConnector)
+ return xcvrMii;
+
+ COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+ XCVRDEBUG("autoselect: media status %uX\n", ins(port+MediaStatus));
+
+ if(media & baseTXAvailable){
+ /*
+ * Must have InternalConfig register.
+ */
+ setxcvr(port, xcvr100BaseTX, is9);
+
+ COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+ x = ins(port+MediaStatus) & ~(dcConverterEnabled|jabberGuardEnable);
+ outs(port+MediaStatus, linkBeatEnable|x);
+ delay(10);
+
+ if(ins(port+MediaStatus) & linkBeatDetect)
+ return xcvr100BaseTX;
+ outs(port+MediaStatus, x);
+ }
+
+ if(media & base10TAvailable){
+ setxcvr(port, xcvr10BaseT, is9);
+
+ COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+ x = ins(port+MediaStatus) & ~dcConverterEnabled;
+ outs(port+MediaStatus, linkBeatEnable|jabberGuardEnable|x);
+ delay(100);
+
+ XCVRDEBUG("autoselect: 10BaseT media status %uX\n", ins(port+MediaStatus));
+ if(ins(port+MediaStatus) & linkBeatDetect)
+ return xcvr10BaseT;
+ outs(port+MediaStatus, x);
+ }
+
+ /*
+ * Botch.
+ */
+ return autoSelect;
+}
+
+static int
+eepromdata(int port, int offset)
+{
+ COMMAND(port, SelectRegisterWindow, Wsetup);
+ while(EEPROMBUSY(port))
+ ;
+ EEPROMCMD(port, EepromReadRegister, offset);
+ while(EEPROMBUSY(port))
+ ;
+ return EEPROMDATA(port);
+}
+
+int
+elnk3reset(Ether* ether)
+{
+ int anar, anlpar, phyaddr, phystat, timeo, xcvr;
+ int busmaster, did, i, j, port, rxearly, rxstatus9, x;
+ Block *bp, **bpp;
+ Adapter *ap;
+ uchar ea[Eaddrlen];
+ Ctlr *ctlr;
+ static int scandone;
+ char *p;
+
+ /*
+ * Scan for adapter on PCI, EISA and finally
+ * using the little ISA configuration dance.
+ */
+ if(scandone == 0){
+ tcm59Xpci(ether);
+ tcm5XXeisa();
+ tcm509isa();
+ scandone = 1;
+ }
+
+ /*
+ * Any adapter matches if no ether->port is supplied,
+ * otherwise the ports must match.
+ */
+ port = 0;
+ bpp = &adapter;
+ for(bp = *bpp; bp; bp = bp->next){
+ ap = (Adapter*)bp->rp;
+ if(ether->port == 0 || ether->port == ap->port){
+ port = ap->port;
+ ether->irq = ap->irq;
+ ether->tbdf = ap->tbdf;
+ ether->mem = ap->cbfns; /* Misuse the mem ref for the cardbus functions */
+ *bpp = bp->next;
+ freeb(bp);
+ break;
+ }
+ bpp = &bp->next;
+ }
+ if(port == 0 && (port = tcm5XXpcmcia(ether)) == 0)
+ return -1;
+
+ /*
+ * Read the DeviceID from the EEPROM, it's at offset 0x03,
+ * and do something depending on capabilities.
+ */
+ switch(did = eepromdata(port, 0x03)){
+
+ case 0x5157: /* 3C575 Cyclone */
+ case 0x4500: /* 3C450 HomePNA Tornado */
+ case 0x6056:
+ case 0x7646: /* 3CSOHO100-TX */
+ case 0x9055: /* 3C905B-TX */
+ case 0x9200: /* 3C905C-TX */
+ /*FALLTHROUGH*/
+ case 0x9000: /* 3C900-TPO */
+ case 0x9001: /* 3C900-COMBO */
+ case 0x9005: /* 3C900B-COMBO */
+ case 0x9050: /* 3C905-TX */
+ case 0x9051: /* 3C905-T4 */
+ if(BUSTYPE(ether->tbdf) != BusPCI)
+ goto buggery;
+ busmaster = 2;
+ goto vortex;
+
+ case 0x5900: /* 3C590-[TP|COMBO|TPO] */
+ case 0x5920: /* 3C592-[TP|COMBO|TPO] */
+ case 0x5950: /* 3C595-TX */
+ case 0x5951: /* 3C595-T4 */
+ case 0x5952: /* 3C595-MII */
+ case 0x5970: /* 3C597-TX */
+ case 0x5971: /* 3C597-T4 */
+ case 0x5972: /* 3C597-MII */
+ busmaster = 1;
+ vortex:
+ COMMAND(port, SelectRegisterWindow, Wfifo);
+ xcvr = inl(port+InternalConfig) & (autoSelect|xcvrMask);
+ rxearly = 8188;
+ rxstatus9 = 0;
+ break;
+
+ buggery:
+ default:
+ busmaster = 0;
+ COMMAND(port, SelectRegisterWindow, Wsetup);
+ x = ins(port+AddressConfig);
+ xcvr = ((x & xcvrMask9)>>14)<<20;
+ if(x & autoSelect9)
+ xcvr |= autoSelect;
+ rxearly = 2044;
+ rxstatus9 = 1;
+ break;
+ }
+
+ /*
+ * Check if the adapter's station address is to be overridden.
+ * If not, read it from the EEPROM and set in ether->ea prior to
+ * loading the station address in Wstation.
+ * The EEPROM returns 16-bits at a time.
+ */
+ memset(ea, 0, Eaddrlen);
+ if(memcmp(ea, ether->ea, Eaddrlen) == 0){
+ for(i = 0; i < Eaddrlen/2; i++){
+ x = eepromdata(port, i);
+ ether->ea[2*i] = x>>8;
+ ether->ea[2*i+1] = x;
+ }
+ }
+
+ COMMAND(port, SelectRegisterWindow, Wstation);
+ for(i = 0; i < Eaddrlen; i++)
+ outb(port+i, ether->ea[i]);
+
+ /*
+ * Enable the transceiver if necessary and determine whether
+ * busmastering can be used. Due to bugs in the first revision
+ * of the 3C59[05], don't use busmastering at 10Mbps.
+ */
+ XCVRDEBUG("reset: xcvr %uX\n", xcvr);
+
+ /*
+ * Allow user to specify desired media in plan9.ini
+ */
+ for(i = 0; i < ether->nopt; i++){
+ if(cistrncmp(ether->opt[i], "media=", 6) != 0)
+ continue;
+ p = ether->opt[i]+6;
+ for(j = 0; j < nelem(media); j++)
+ if(cistrcmp(p, media[j].name) == 0)
+ xcvr = media[j].xcvr;
+ }
+
+ /*
+ * forgive me, but i am weak
+ */
+ switch(did){
+ default:
+ if(xcvr & autoSelect)
+ xcvr = autoselect(port, xcvr, rxstatus9);
+ break;
+ case 0x4500:
+ case 0x5157:
+ case 0x6056:
+ case 0x7646:
+ case 0x9055:
+ case 0x9200:
+ xcvr = xcvrMii;
+ txrxreset(port);
+ XCVRDEBUG("905[BC] reset ops 0x%uX\n", ins(port+ResetOp905B));
+
+ if (did == 0x5157) {
+ ushort reset_opts;
+
+ COMMAND(port, SelectRegisterWindow, Wstation);
+ reset_opts = ins(port + ResetOp905B);
+ reset_opts |= 0x0010; /* Invert LED */
+ outs(port + ResetOp905B, reset_opts);
+ }
+ break;
+ }
+ XCVRDEBUG("autoselect returns: xcvr %uX, did 0x%uX\n", xcvr, did);
+
+ switch(xcvr){
+
+ case xcvrMii:
+ /*
+ * Quick hack.
+ scanphy(port);
+ */
+ phyaddr = (did == 0x5157)? 0: 24;
+ for(i = 0; i < 7; i++)
+ XCVRDEBUG(" %2.2uX", miir(port, phyaddr, i));
+ XCVRDEBUG("\n");
+
+ for(timeo = 0; timeo < 30; timeo++){
+ phystat = miir(port, phyaddr, 0x01);
+ if(phystat & 0x20)
+ break;
+ XCVRDEBUG(" %2.2uX", phystat);
+ delay(100);
+ }
+ XCVRDEBUG(" %2.2uX", miir(port, phyaddr, 0x01));
+ XCVRDEBUG("\n");
+
+ anar = miir(port, phyaddr, 0x04);
+ anlpar = miir(port, phyaddr, 0x05) & 0x03E0;
+ anar &= anlpar;
+ miir(port, phyaddr, 0x00);
+ XCVRDEBUG("mii an: %uX anlp: %uX r0:%uX r1:%uX\n",
+ anar, anlpar, miir(port, phyaddr, 0x00),
+ miir(port, phyaddr, 0x01));
+ for(i = 0; i < ether->nopt; i++){
+ if(cistrcmp(ether->opt[i], "fullduplex") == 0)
+ anar |= 0x0100;
+ else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0)
+ anar |= 0x0100;
+ else if(cistrcmp(ether->opt[i], "force100") == 0)
+ anar |= 0x0080;
+ }
+ XCVRDEBUG("mii anar: %uX\n", anar);
+ if(anar & 0x0100){ /* 100BASE-TXFD */
+ setfullduplex(port);
+ }
+ else if(anar & 0x0200){ /* 100BASE-T4 */
+ /* nothing to do */
+ }
+ else if(anar & 0x0080){ /* 100BASE-TX */
+ /* nothing to do */;
+ }
+ else if(anar & 0x0040) /* 10BASE-TFD */
+ setfullduplex(port);
+ else{ /* 10BASE-T */
+ /* nothing to do */;
+ }
+ break;
+
+ case xcvr100BaseTX:
+ case xcvr100BaseFX:
+ COMMAND(port, SelectRegisterWindow, Wfifo);
+ x = inl(port+InternalConfig) & ~ramPartitionMask;
+ outl(port+InternalConfig, x|ramPartition1to1);
+
+ COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+ x = ins(port+MediaStatus) & ~(dcConverterEnabled|jabberGuardEnable);
+ x |= linkBeatEnable;
+ outs(port+MediaStatus, x);
+ break;
+
+ case xcvr10BaseT:
+ /*
+ * Enable Link Beat and Jabber to start the
+ * transceiver.
+ */
+ COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+ x = ins(port+MediaStatus) & ~dcConverterEnabled;
+ x |= linkBeatEnable|jabberGuardEnable;
+ outs(port+MediaStatus, x);
+
+ if((did & 0xFF00) == 0x5900)
+ busmaster = 0;
+ break;
+
+ case xcvr10Base2:
+ COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+ x = ins(port+MediaStatus) & ~(linkBeatEnable|jabberGuardEnable);
+ outs(port+MediaStatus, x);
+
+ /*
+ * Start the DC-DC converter.
+ * Wait > 800 microseconds.
+ */
+ COMMAND(port, EnableDcConverter, 0);
+ delay(1);
+ break;
+ }
+
+ /*
+ * Wop is the normal operating register set.
+ * The 3C59[0257] adapters allow access to more than one register window
+ * at a time, but there are situations where switching still needs to be
+ * done, so just do it.
+ * Clear out any lingering Tx status.
+ */
+ COMMAND(port, SelectRegisterWindow, Wop);
+ if(busmaster == 2)
+ x = port+TxStatus905;
+ else
+ x = port+TxStatus;
+ while(inb(x))
+ outb(x, 0);
+
+ /*
+ * Allocate a controller structure, clear out the
+ * adapter statistics, clear the statistics logged into ctlr
+ * and enable statistics collection. Xcvr is needed in order
+ * to collect the BadSSD statistics.
+ */
+ ether->ctlr = malloc(sizeof(Ctlr));
+ ctlr = ether->ctlr;
+
+ ilock(&ctlr->wlock);
+ ctlr->xcvr = xcvr;
+ statistics(ether);
+ memset(ctlr->stats, 0, sizeof(ctlr->stats));
+
+ ctlr->busmaster = busmaster;
+ ctlr->xcvr = xcvr;
+ ctlr->rxstatus9 = rxstatus9;
+ ctlr->rxearly = rxearly;
+ if(rxearly >= 2048)
+ ctlr->ts = 2;
+
+ COMMAND(port, StatisticsEnable, 0);
+
+ /*
+ * Allocate any receive buffers.
+ */
+ if (ctlr->busmaster == 2) {
+ ctlr->dnenabled = 1;
+
+ /*
+ * 10MUpldBug.
+ * Disabling is too severe, can use receive busmastering at
+ * 100Mbps OK, but how to tell which rate is actually being used -
+ * the 3c905 always seems to have dataRate100 set?
+ * Believe the bug doesn't apply if upRxEarlyEnable is set
+ * and the threshold is set such that uploads won't start
+ * until the whole packet has been received.
+ */
+ ctlr->upenabled = 1;
+ x = eepromdata(port, 0x0F);
+ if(!(x & 0x01))
+ outl(port+PktStatus, upRxEarlyEnable);
+
+ ctlr->nup = Nup;
+ ctlr->ndn = Ndn;
+ init905(ctlr);
+ outl(port+TxFreeThresh, HOWMANY(ETHERMAXTU, 256));
+ }
+
+ /*
+ * Set a base TxStartThresh which will be incremented
+ * if any txUnderrun errors occur and ensure no RxEarly
+ * interrupts happen.
+ */
+ ctlr->txthreshold = ETHERMAXTU/2;
+ COMMAND(port, SetTxStartThresh, ctlr->txthreshold>>ctlr->ts);
+ COMMAND(port, SetRxEarlyThresh, rxearly>>ctlr->ts);
+
+ iunlock(&ctlr->wlock);
+
+ /*
+ * Linkage to the generic ethernet driver.
+ */
+ ether->port = port;
+ ether->attach = attach;
+ ether->transmit = transmit;
+ ether->interrupt = interrupt;
+
+ return 0;
+}
diff --git a/os/boot/pc/etherif.h b/os/boot/pc/etherif.h
new file mode 100644
index 00000000..522fd9b0
--- /dev/null
+++ b/os/boot/pc/etherif.h
@@ -0,0 +1,46 @@
+typedef struct RingBuf {
+ uchar owner;
+ uchar unused;
+ ushort len;
+ uchar pkt[sizeof(Etherpkt)];
+} RingBuf;
+
+enum {
+ Host = 0, /* buffer owned by host */
+ Interface = 1, /* buffer owned by card */
+
+ Nrb = 32, /* default number of receive buffers */
+ Ntb = 8, /* default number of transmit buffers */
+};
+
+typedef struct Ether Ether;
+struct Ether {
+ ISAConf; /* hardware info */
+ int ctlrno;
+ int state;
+ int tbdf;
+
+ void (*attach)(Ether*); /* filled in by reset routine */
+ void (*transmit)(Ether*);
+ void (*interrupt)(Ureg*, void*);
+ void (*detach)(Ether*);
+ void *ctlr;
+
+ ushort nrb; /* number of software receive buffers */
+ ushort ntb; /* number of software transmit buffers */
+ RingBuf *rb; /* software receive buffers */
+ RingBuf *tb; /* software transmit buffers */
+
+ ushort rh; /* first receive buffer belonging to host */
+ ushort ri; /* first receive buffer belonging to card */
+
+ ushort th; /* first transmit buffer belonging to host */
+ ushort ti; /* first transmit buffer belonging to card */
+ int tbusy; /* transmitter is busy */
+};
+
+extern void etherrloop(Ether*, Etherpkt*, long);
+extern void addethercard(char*, int(*)(Ether*));
+
+#define NEXT(x, l) (((x)+1)%(l))
+#define PREV(x, l) (((x) == 0) ? (l)-1: (x)-1)
diff --git a/os/boot/pc/etherigbe.c b/os/boot/pc/etherigbe.c
new file mode 100644
index 00000000..f65ea7ba
--- /dev/null
+++ b/os/boot/pc/etherigbe.c
@@ -0,0 +1,1706 @@
+/*
+ * bootstrap driver for
+ * Intel RS-82543GC Gigabit Ethernet Controller
+ * as found on the Intel PRO/1000[FT] Server Adapter.
+ * The older non-[FT] cards use the 82542 (LSI L2A1157) chip; no attempt
+ * is made to handle the older chip although it should be possible.
+ *
+ * updated just enough to cope with the
+ * Intel 8254[0347]NN Gigabit Ethernet Controller
+ * as found on the Intel PRO/1000 series of adapters:
+ * 82540EM Intel PRO/1000 MT
+ * 82543GC Intel PRO/1000 T
+ * 82544EI Intel PRO/1000 XT
+ * 82547EI built-in
+ *
+ * The datasheet is not very clear about running on a big-endian system
+ * and this driver assumes little-endian throughout.
+ * To do:
+ * GMII/MII
+ * check recovery from receive no buffers condition
+ * automatic ett adjustment
+ */
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "etherif.h"
+#include "ethermii.h"
+
+enum {
+ i82542 = (0x1000<<16)|0x8086,
+ i82543gc = (0x1004<<16)|0x8086,
+ i82544ei = (0x1008<<16)|0x8086,
+ i82547ei = (0x1019<<16)|0x8086,
+ i82540em = (0x100E<<16)|0x8086,
+ i82540eplp = (0x101E<<16)|0x8086,
+ i82547gi = (0x1075<<16)|0x8086,
+ i82541gi = (0x1076<<16)|0x8086,
+ i82546gb = (0x1079<<16)|0x8086,
+ i82541pi = (0x107c<<16)|0x8086,
+ i82546eb = (0x1010<<16)|0x8086,
+};
+
+/* compatibility with cpu kernels */
+#define iallocb allocb
+#ifndef CACHELINESZ
+#define CACHELINESZ 32 /* pentium & later */
+#endif
+
+/* from pci.c */
+enum
+{ /* command register (pcidev->pcr) */
+ IOen = (1<<0),
+ MEMen = (1<<1),
+ MASen = (1<<2),
+ MemWrInv = (1<<4),
+ PErrEn = (1<<6),
+ SErrEn = (1<<8),
+};
+enum {
+ Ctrl = 0x00000000, /* Device Control */
+ Status = 0x00000008, /* Device Status */
+ Eecd = 0x00000010, /* EEPROM/Flash Control/Data */
+ Ctrlext = 0x00000018, /* Extended Device Control */
+ Mdic = 0x00000020, /* MDI Control */
+ Fcal = 0x00000028, /* Flow Control Address Low */
+ Fcah = 0x0000002C, /* Flow Control Address High */
+ Fct = 0x00000030, /* Flow Control Type */
+ Icr = 0x000000C0, /* Interrupt Cause Read */
+ Ics = 0x000000C8, /* Interrupt Cause Set */
+ Ims = 0x000000D0, /* Interrupt Mask Set/Read */
+ Imc = 0x000000D8, /* Interrupt mask Clear */
+ Rctl = 0x00000100, /* Receive Control */
+ Fcttv = 0x00000170, /* Flow Control Transmit Timer Value */
+ Txcw = 0x00000178, /* Transmit Configuration Word */
+ Tctl = 0x00000400, /* Transmit Control */
+ Tipg = 0x00000410, /* Transmit IPG */
+ Tbt = 0x00000448, /* Transmit Burst Timer */
+ Ait = 0x00000458, /* Adaptive IFS Throttle */
+ Fcrtl = 0x00002160, /* Flow Control RX Threshold Low */
+ Fcrth = 0x00002168, /* Flow Control Rx Threshold High */
+ Rdbal = 0x00002800, /* Rdesc Base Address Low */
+ Rdbah = 0x00002804, /* Rdesc Base Address High */
+ Rdlen = 0x00002808, /* Receive Descriptor Length */
+ Rdh = 0x00002810, /* Receive Descriptor Head */
+ Rdt = 0x00002818, /* Receive Descriptor Tail */
+ Rdtr = 0x00002820, /* Receive Descriptor Timer Ring */
+ Rxdctl = 0x00002828, /* Receive Descriptor Control */
+ Radv = 0x0000282C, /* Receive Interrupt Absolute Delay Timer */
+ Txdmac = 0x00003000, /* Transfer DMA Control */
+ Ett = 0x00003008, /* Early Transmit Control */
+ Tdbal = 0x00003800, /* Tdesc Base Address Low */
+ Tdbah = 0x00003804, /* Tdesc Base Address High */
+ Tdlen = 0x00003808, /* Transmit Descriptor Length */
+ Tdh = 0x00003810, /* Transmit Descriptor Head */
+ Tdt = 0x00003818, /* Transmit Descriptor Tail */
+ Tidv = 0x00003820, /* Transmit Interrupt Delay Value */
+ Txdctl = 0x00003828, /* Transmit Descriptor Control */
+ Tadv = 0x0000382C, /* Transmit Interrupt Absolute Delay Timer */
+
+ Statistics = 0x00004000, /* Start of Statistics Area */
+ Gorcl = 0x88/4, /* Good Octets Received Count */
+ Gotcl = 0x90/4, /* Good Octets Transmitted Count */
+ Torl = 0xC0/4, /* Total Octets Received */
+ Totl = 0xC8/4, /* Total Octets Transmitted */
+ Nstatistics = 64,
+
+ Rxcsum = 0x00005000, /* Receive Checksum Control */
+ Mta = 0x00005200, /* Multicast Table Array */
+ Ral = 0x00005400, /* Receive Address Low */
+ Rah = 0x00005404, /* Receive Address High */
+ Manc = 0x00005820, /* Management Control */
+};
+
+enum { /* Ctrl */
+ Bem = 0x00000002, /* Big Endian Mode */
+ Prior = 0x00000004, /* Priority on the PCI bus */
+ Lrst = 0x00000008, /* Link Reset */
+ Asde = 0x00000020, /* Auto-Speed Detection Enable */
+ Slu = 0x00000040, /* Set Link Up */
+ Ilos = 0x00000080, /* Invert Loss of Signal (LOS) */
+ SspeedMASK = 0x00000300, /* Speed Selection */
+ SspeedSHIFT = 8,
+ Sspeed10 = 0x00000000, /* 10Mb/s */
+ Sspeed100 = 0x00000100, /* 100Mb/s */
+ Sspeed1000 = 0x00000200, /* 1000Mb/s */
+ Frcspd = 0x00000800, /* Force Speed */
+ Frcdplx = 0x00001000, /* Force Duplex */
+ SwdpinsloMASK = 0x003C0000, /* Software Defined Pins - lo nibble */
+ SwdpinsloSHIFT = 18,
+ SwdpioloMASK = 0x03C00000, /* Software Defined Pins - I or O */
+ SwdpioloSHIFT = 22,
+ Devrst = 0x04000000, /* Device Reset */
+ Rfce = 0x08000000, /* Receive Flow Control Enable */
+ Tfce = 0x10000000, /* Transmit Flow Control Enable */
+ Vme = 0x40000000, /* VLAN Mode Enable */
+};
+
+enum { /* Status */
+ Lu = 0x00000002, /* Link Up */
+ Tckok = 0x00000004, /* Transmit clock is running */
+ Rbcok = 0x00000008, /* Receive clock is running */
+ Txoff = 0x00000010, /* Transmission Paused */
+ Tbimode = 0x00000020, /* TBI Mode Indication */
+ SpeedMASK = 0x000000C0,
+ Speed10 = 0x00000000, /* 10Mb/s */
+ Speed100 = 0x00000040, /* 100Mb/s */
+ Speed1000 = 0x00000080, /* 1000Mb/s */
+ Mtxckok = 0x00000400, /* MTX clock is running */
+ Pci66 = 0x00000800, /* PCI Bus speed indication */
+ Bus64 = 0x00001000, /* PCI Bus width indication */
+};
+
+enum { /* Ctrl and Status */
+ Fd = 0x00000001, /* Full-Duplex */
+ AsdvMASK = 0x00000300,
+ Asdv10 = 0x00000000, /* 10Mb/s */
+ Asdv100 = 0x00000100, /* 100Mb/s */
+ Asdv1000 = 0x00000200, /* 1000Mb/s */
+};
+
+enum { /* Eecd */
+ Sk = 0x00000001, /* Clock input to the EEPROM */
+ Cs = 0x00000002, /* Chip Select */
+ Di = 0x00000004, /* Data Input to the EEPROM */
+ Do = 0x00000008, /* Data Output from the EEPROM */
+ Areq = 0x00000040, /* EEPROM Access Request */
+ Agnt = 0x00000080, /* EEPROM Access Grant */
+ Eesz256 = 0x00000200, /* EEPROM is 256 words not 64 */
+ Spi = 0x00002000, /* EEPROM is SPI not Microwire */
+};
+
+enum { /* Ctrlext */
+ Gpien = 0x0000000F, /* General Purpose Interrupt Enables */
+ SwdpinshiMASK = 0x000000F0, /* Software Defined Pins - hi nibble */
+ SwdpinshiSHIFT = 4,
+ SwdpiohiMASK = 0x00000F00, /* Software Defined Pins - I or O */
+ SwdpiohiSHIFT = 8,
+ Asdchk = 0x00001000, /* ASD Check */
+ Eerst = 0x00002000, /* EEPROM Reset */
+ Ips = 0x00004000, /* Invert Power State */
+ Spdbyps = 0x00008000, /* Speed Select Bypass */
+};
+
+enum { /* EEPROM content offsets */
+ Ea = 0x00, /* Ethernet Address */
+ Cf = 0x03, /* Compatibility Field */
+ Pba = 0x08, /* Printed Board Assembly number */
+ Icw1 = 0x0A, /* Initialization Control Word 1 */
+ Sid = 0x0B, /* Subsystem ID */
+ Svid = 0x0C, /* Subsystem Vendor ID */
+ Did = 0x0D, /* Device ID */
+ Vid = 0x0E, /* Vendor ID */
+ Icw2 = 0x0F, /* Initialization Control Word 2 */
+};
+
+enum { /* Mdic */
+ MDIdMASK = 0x0000FFFF, /* Data */
+ MDIdSHIFT = 0,
+ MDIrMASK = 0x001F0000, /* PHY Register Address */
+ MDIrSHIFT = 16,
+ MDIpMASK = 0x03E00000, /* PHY Address */
+ MDIpSHIFT = 21,
+ MDIwop = 0x04000000, /* Write Operation */
+ MDIrop = 0x08000000, /* Read Operation */
+ MDIready = 0x10000000, /* End of Transaction */
+ MDIie = 0x20000000, /* Interrupt Enable */
+ MDIe = 0x40000000, /* Error */
+};
+
+enum { /* Icr, Ics, Ims, Imc */
+ Txdw = 0x00000001, /* Transmit Descriptor Written Back */
+ Txqe = 0x00000002, /* Transmit Queue Empty */
+ Lsc = 0x00000004, /* Link Status Change */
+ Rxseq = 0x00000008, /* Receive Sequence Error */
+ Rxdmt0 = 0x00000010, /* Rdesc Minimum Threshold Reached */
+ Rxo = 0x00000040, /* Receiver Overrun */
+ Rxt0 = 0x00000080, /* Receiver Timer Interrupt */
+ Mdac = 0x00000200, /* MDIO Access Completed */
+ Rxcfg = 0x00000400, /* Receiving /C/ ordered sets */
+ Gpi0 = 0x00000800, /* General Purpose Interrupts */
+ Gpi1 = 0x00001000,
+ Gpi2 = 0x00002000,
+ Gpi3 = 0x00004000,
+};
+
+/*
+ * The Mdic register isn't implemented on the 82543GC,
+ * the software defined pins are used instead.
+ * These definitions work for the Intel PRO/1000 T Server Adapter.
+ * The direction pin bits are read from the EEPROM.
+ */
+enum {
+ Mdd = ((1<<2)<<SwdpinsloSHIFT), /* data */
+ Mddo = ((1<<2)<<SwdpioloSHIFT), /* pin direction */
+ Mdc = ((1<<3)<<SwdpinsloSHIFT), /* clock */
+ Mdco = ((1<<3)<<SwdpioloSHIFT), /* pin direction */
+ Mdr = ((1<<0)<<SwdpinshiSHIFT), /* reset */
+ Mdro = ((1<<0)<<SwdpiohiSHIFT), /* pin direction */
+};
+
+enum { /* Txcw */
+ TxcwFd = 0x00000020, /* Full Duplex */
+ TxcwHd = 0x00000040, /* Half Duplex */
+ TxcwPauseMASK = 0x00000180, /* Pause */
+ TxcwPauseSHIFT = 7,
+ TxcwPs = (1<<TxcwPauseSHIFT), /* Pause Supported */
+ TxcwAs = (2<<TxcwPauseSHIFT), /* Asymmetric FC desired */
+ TxcwRfiMASK = 0x00003000, /* Remote Fault Indication */
+ TxcwRfiSHIFT = 12,
+ TxcwNpr = 0x00008000, /* Next Page Request */
+ TxcwConfig = 0x40000000, /* Transmit COnfig Control */
+ TxcwAne = 0x80000000, /* Auto-Negotiation Enable */
+};
+
+enum { /* Rctl */
+ Rrst = 0x00000001, /* Receiver Software Reset */
+ Ren = 0x00000002, /* Receiver Enable */
+ Sbp = 0x00000004, /* Store Bad Packets */
+ Upe = 0x00000008, /* Unicast Promiscuous Enable */
+ Mpe = 0x00000010, /* Multicast Promiscuous Enable */
+ Lpe = 0x00000020, /* Long Packet Reception Enable */
+ LbmMASK = 0x000000C0, /* Loopback Mode */
+ LbmOFF = 0x00000000, /* No Loopback */
+ LbmTBI = 0x00000040, /* TBI Loopback */
+ LbmMII = 0x00000080, /* GMII/MII Loopback */
+ LbmXCVR = 0x000000C0, /* Transceiver Loopback */
+ RdtmsMASK = 0x00000300, /* Rdesc Minimum Threshold Size */
+ RdtmsHALF = 0x00000000, /* Threshold is 1/2 Rdlen */
+ RdtmsQUARTER = 0x00000100, /* Threshold is 1/4 Rdlen */
+ RdtmsEIGHTH = 0x00000200, /* Threshold is 1/8 Rdlen */
+ MoMASK = 0x00003000, /* Multicast Offset */
+ Bam = 0x00008000, /* Broadcast Accept Mode */
+ BsizeMASK = 0x00030000, /* Receive Buffer Size */
+ Bsize2048 = 0x00000000,
+ Bsize1024 = 0x00010000,
+ Bsize512 = 0x00020000,
+ Bsize256 = 0x00030000,
+ Vfe = 0x00040000, /* VLAN Filter Enable */
+ Cfien = 0x00080000, /* Canonical Form Indicator Enable */
+ Cfi = 0x00100000, /* Canonical Form Indicator value */
+ Dpf = 0x00400000, /* Discard Pause Frames */
+ Pmcf = 0x00800000, /* Pass MAC Control Frames */
+ Bsex = 0x02000000, /* Buffer Size Extension */
+ Secrc = 0x04000000, /* Strip CRC from incoming packet */
+};
+
+enum { /* Tctl */
+ Trst = 0x00000001, /* Transmitter Software Reset */
+ Ten = 0x00000002, /* Transmit Enable */
+ Psp = 0x00000008, /* Pad Short Packets */
+ CtMASK = 0x00000FF0, /* Collision Threshold */
+ CtSHIFT = 4,
+ ColdMASK = 0x003FF000, /* Collision Distance */
+ ColdSHIFT = 12,
+ Swxoff = 0x00400000, /* Sofware XOFF Transmission */
+ Pbe = 0x00800000, /* Packet Burst Enable */
+ Rtlc = 0x01000000, /* Re-transmit on Late Collision */
+ Nrtu = 0x02000000, /* No Re-transmit on Underrrun */
+};
+
+enum { /* [RT]xdctl */
+ PthreshMASK = 0x0000003F, /* Prefetch Threshold */
+ PthreshSHIFT = 0,
+ HthreshMASK = 0x00003F00, /* Host Threshold */
+ HthreshSHIFT = 8,
+ WthreshMASK = 0x003F0000, /* Writebacj Threshold */
+ WthreshSHIFT = 16,
+ Gran = 0x01000000, /* Granularity */
+};
+
+enum { /* Rxcsum */
+ PcssMASK = 0x000000FF, /* Packet Checksum Start */
+ PcssSHIFT = 0,
+ Ipofl = 0x00000100, /* IP Checksum Off-load Enable */
+ Tuofl = 0x00000200, /* TCP/UDP Checksum Off-load Enable */
+};
+
+enum { /* Manc */
+ Arpen = 0x00002000, /* Enable ARP Request Filtering */
+};
+
+typedef struct Rdesc { /* Receive Descriptor */
+ uint addr[2];
+ ushort length;
+ ushort checksum;
+ uchar status;
+ uchar errors;
+ ushort special;
+} Rdesc;
+
+enum { /* Rdesc status */
+ Rdd = 0x01, /* Descriptor Done */
+ Reop = 0x02, /* End of Packet */
+ Ixsm = 0x04, /* Ignore Checksum Indication */
+ Vp = 0x08, /* Packet is 802.1Q (matched VET) */
+ Tcpcs = 0x20, /* TCP Checksum Calculated on Packet */
+ Ipcs = 0x40, /* IP Checksum Calculated on Packet */
+ Pif = 0x80, /* Passed in-exact filter */
+};
+
+enum { /* Rdesc errors */
+ Ce = 0x01, /* CRC Error or Alignment Error */
+ Se = 0x02, /* Symbol Error */
+ Seq = 0x04, /* Sequence Error */
+ Cxe = 0x10, /* Carrier Extension Error */
+ Tcpe = 0x20, /* TCP/UDP Checksum Error */
+ Ipe = 0x40, /* IP Checksum Error */
+ Rxe = 0x80, /* RX Data Error */
+};
+
+typedef struct Tdesc { /* Legacy+Normal Transmit Descriptor */
+ uint addr[2];
+ uint control; /* varies with descriptor type */
+ uint status; /* varies with descriptor type */
+} Tdesc;
+
+enum { /* Tdesc control */
+ LenMASK = 0x000FFFFF, /* Data/Packet Length Field */
+ LenSHIFT = 0,
+ DtypeCD = 0x00000000, /* Data Type 'Context Descriptor' */
+ DtypeDD = 0x00100000, /* Data Type 'Data Descriptor' */
+ PtypeTCP = 0x01000000, /* TCP/UDP Packet Type (CD) */
+ Teop = 0x01000000, /* End of Packet (DD) */
+ PtypeIP = 0x02000000, /* IP Packet Type (CD) */
+ Ifcs = 0x02000000, /* Insert FCS (DD) */
+ Tse = 0x04000000, /* TCP Segmentation Enable */
+ Rs = 0x08000000, /* Report Status */
+ Rps = 0x10000000, /* Report Status Sent */
+ Dext = 0x20000000, /* Descriptor Extension */
+ Vle = 0x40000000, /* VLAN Packet Enable */
+ Ide = 0x80000000, /* Interrupt Delay Enable */
+};
+
+enum { /* Tdesc status */
+ Tdd = 0x00000001, /* Descriptor Done */
+ Ec = 0x00000002, /* Excess Collisions */
+ Lc = 0x00000004, /* Late Collision */
+ Tu = 0x00000008, /* Transmit Underrun */
+ CssMASK = 0x0000FF00, /* Checksum Start Field */
+ CssSHIFT = 8,
+};
+
+enum {
+ Nrdesc = 128, /* multiple of 8 */
+ Ntdesc = 128, /* multiple of 8 */
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Ctlr {
+ int port;
+ Pcidev* pcidev;
+ Ctlr* next;
+ int active;
+ int id;
+ int cls;
+ ushort eeprom[0x40];
+
+ int* nic;
+ Lock imlock;
+ int im; /* interrupt mask */
+
+ Mii* mii;
+
+ Lock slock;
+ uint statistics[Nstatistics];
+
+ uchar ra[Eaddrlen]; /* receive address */
+ ulong mta[128]; /* multicast table array */
+
+ Rdesc* rdba; /* receive descriptor base address */
+ Block** rb; /* receive buffers */
+ int rdh; /* receive descriptor head */
+ int rdt; /* receive descriptor tail */
+
+ Tdesc* tdba; /* transmit descriptor base address */
+ Lock tdlock;
+ Block** tb; /* transmit buffers */
+ int tdh; /* transmit descriptor head */
+ int tdt; /* transmit descriptor tail */
+ int ett; /* early transmit threshold */
+
+ int txcw;
+ int fcrtl;
+ int fcrth;
+
+ /* bootstrap goo */
+ Block* bqhead; /* transmission queue */
+ Block* bqtail;
+} Ctlr;
+
+static Ctlr* ctlrhead;
+static Ctlr* ctlrtail;
+
+#define csr32r(c, r) (*((c)->nic+((r)/4)))
+#define csr32w(c, r, v) (*((c)->nic+((r)/4)) = (v))
+
+static void
+igbeim(Ctlr* ctlr, int im)
+{
+ ilock(&ctlr->imlock);
+ ctlr->im |= im;
+ csr32w(ctlr, Ims, ctlr->im);
+ iunlock(&ctlr->imlock);
+}
+
+static void
+igbeattach(Ether* edev)
+{
+ int ctl;
+ Ctlr *ctlr;
+
+ /*
+ * To do here:
+ * one-time stuff;
+ * start off a kproc for link status change:
+ * adjust queue length depending on speed;
+ * flow control.
+ * more needed here...
+ */
+ ctlr = edev->ctlr;
+ igbeim(ctlr, 0);
+ ctl = csr32r(ctlr, Rctl)|Ren;
+ csr32w(ctlr, Rctl, ctl);
+ ctl = csr32r(ctlr, Tctl)|Ten;
+ csr32w(ctlr, Tctl, ctl);
+}
+
+static char* statistics[Nstatistics] = {
+ "CRC Error",
+ "Alignment Error",
+ "Symbol Error",
+ "RX Error",
+ "Missed Packets",
+ "Single Collision",
+ "Excessive Collisions",
+ "Multiple Collision",
+ "Late Collisions",
+ nil,
+ "Collision",
+ "Transmit Underrun",
+ "Defer",
+ "Transmit - No CRS",
+ "Sequence Error",
+ "Carrier Extension Error",
+ "Receive Error Length",
+ nil,
+ "XON Received",
+ "XON Transmitted",
+ "XOFF Received",
+ "XOFF Transmitted",
+ "FC Received Unsupported",
+ "Packets Received (64 Bytes)",
+ "Packets Received (65-127 Bytes)",
+ "Packets Received (128-255 Bytes)",
+ "Packets Received (256-511 Bytes)",
+ "Packets Received (512-1023 Bytes)",
+ "Packets Received (1024-1522 Bytes)",
+ "Good Packets Received",
+ "Broadcast Packets Received",
+ "Multicast Packets Received",
+ "Good Packets Transmitted",
+ nil,
+ "Good Octets Received",
+ nil,
+ "Good Octets Transmitted",
+ nil,
+ nil,
+ nil,
+ "Receive No Buffers",
+ "Receive Undersize",
+ "Receive Fragment",
+ "Receive Oversize",
+ "Receive Jabber",
+ nil,
+ nil,
+ nil,
+ "Total Octets Received",
+ nil,
+ "Total Octets Transmitted",
+ nil,
+ "Total Packets Received",
+ "Total Packets Transmitted",
+ "Packets Transmitted (64 Bytes)",
+ "Packets Transmitted (65-127 Bytes)",
+ "Packets Transmitted (128-255 Bytes)",
+ "Packets Transmitted (256-511 Bytes)",
+ "Packets Transmitted (512-1023 Bytes)",
+ "Packets Transmitted (1024-1522 Bytes)",
+ "Multicast Packets Transmitted",
+ "Broadcast Packets Transmitted",
+ "TCP Segmentation Context Transmitted",
+ "TCP Segmentation Context Fail",
+};
+
+static void
+txstart(Ether *edev)
+{
+ int tdh, tdt, len, olen;
+ Ctlr *ctlr = edev->ctlr;
+ Block *bp;
+ Tdesc *tdesc;
+
+ /*
+ * Try to fill the ring back up, moving buffers from the transmit q.
+ */
+ tdh = PREV(ctlr->tdh, Ntdesc);
+ for(tdt = ctlr->tdt; tdt != tdh; tdt = NEXT(tdt, Ntdesc)){
+ /* pull off the head of the transmission queue */
+ if((bp = ctlr->bqhead) == nil) /* was qget(edev->oq) */
+ break;
+ ctlr->bqhead = bp->next;
+ if (ctlr->bqtail == bp)
+ ctlr->bqtail = nil;
+ len = olen = BLEN(bp);
+
+ /*
+ * if packet is too short, make it longer rather than relying
+ * on ethernet interface to pad it and complain so the caller
+ * will get fixed. I don't think Psp is working right, or it's
+ * getting cleared.
+ */
+ if (len < ETHERMINTU) {
+ if (bp->rp + ETHERMINTU <= bp->lim)
+ bp->wp = bp->rp + ETHERMINTU;
+ else
+ bp->wp = bp->lim;
+ len = BLEN(bp);
+ print("txstart: extended short pkt %d -> %d bytes\n",
+ olen, len);
+ }
+
+ /* set up a descriptor for it */
+ tdesc = &ctlr->tdba[tdt];
+ tdesc->addr[0] = PCIWADDR(bp->rp);
+ tdesc->addr[1] = 0;
+ tdesc->control = /* Ide| */ Rs|Dext|Ifcs|Teop|DtypeDD|len;
+ tdesc->status = 0;
+
+ ctlr->tb[tdt] = bp;
+ }
+ ctlr->tdt = tdt;
+ csr32w(ctlr, Tdt, tdt);
+ igbeim(ctlr, Txdw);
+}
+
+static Block *
+fromringbuf(Ether *ether)
+{
+ RingBuf *tb = &ether->tb[ether->ti];
+ Block *bp = allocb(tb->len);
+
+ memmove(bp->wp, tb->pkt, tb->len);
+ memmove(bp->wp+Eaddrlen, ether->ea, Eaddrlen);
+ bp->wp += tb->len;
+ return bp;
+}
+
+static void
+igbetransmit(Ether* edev)
+{
+ Block *bp;
+ Ctlr *ctlr;
+ Tdesc *tdesc;
+ RingBuf *tb;
+ int tdh;
+
+ /*
+ * For now there are no smarts here. Tuning comes later.
+ */
+ ctlr = edev->ctlr;
+ ilock(&ctlr->tdlock);
+
+ /*
+ * Free any completed packets
+ * - try to get the soft tdh to catch the tdt;
+ * - if the packet had an underrun bump the threshold
+ * - the Tu bit doesn't seem to ever be set, perhaps
+ * because Rs mode is used?
+ */
+ tdh = ctlr->tdh;
+ for(;;){
+ tdesc = &ctlr->tdba[tdh];
+ if(!(tdesc->status & Tdd))
+ break;
+ if(tdesc->status & Tu){
+ ctlr->ett++;
+ csr32w(ctlr, Ett, ctlr->ett);
+ }
+ tdesc->status = 0;
+ if(ctlr->tb[tdh] != nil){
+ freeb(ctlr->tb[tdh]);
+ ctlr->tb[tdh] = nil;
+ }
+ tdh = NEXT(tdh, Ntdesc);
+ }
+ ctlr->tdh = tdh;
+
+ /* copy packets from the software RingBuf to the transmission q */
+ /* from boot ether83815.c */
+ while((tb = &edev->tb[edev->ti])->owner == Interface){
+ bp = fromringbuf(edev);
+
+ /* put the buffer on the transmit queue */
+ if(ctlr->bqhead)
+ ctlr->bqtail->next = bp;
+ else
+ ctlr->bqhead = bp;
+ ctlr->bqtail = bp;
+
+ txstart(edev); /* kick transmitter */
+ tb->owner = Host; /* give descriptor back */
+
+ edev->ti = NEXT(edev->ti, edev->ntb);
+ }
+
+ iunlock(&ctlr->tdlock);
+}
+
+static void
+igbereplenish(Ctlr* ctlr)
+{
+ int rdt;
+ Block *bp;
+ Rdesc *rdesc;
+
+ rdt = ctlr->rdt;
+ while(NEXT(rdt, Nrdesc) != ctlr->rdh){
+ rdesc = &ctlr->rdba[rdt];
+ if(ctlr->rb[rdt] != nil){
+ /* nothing to do */
+ }
+ else if((bp = iallocb(2048)) != nil){
+ ctlr->rb[rdt] = bp;
+ rdesc->addr[0] = PCIWADDR(bp->rp);
+ rdesc->addr[1] = 0;
+ }
+ else
+ break;
+ rdesc->status = 0;
+
+ rdt = NEXT(rdt, Nrdesc);
+ }
+ ctlr->rdt = rdt;
+ csr32w(ctlr, Rdt, rdt);
+}
+
+static void
+toringbuf(Ether *ether, Block *bp)
+{
+ RingBuf *rb = &ether->rb[ether->ri];
+
+ if (rb->owner == Interface) {
+ rb->len = BLEN(bp);
+ memmove(rb->pkt, bp->rp, rb->len);
+ rb->owner = Host;
+ ether->ri = NEXT(ether->ri, ether->nrb);
+ }
+ /* else no one is expecting packets from the network */
+}
+
+static void
+igbeinterrupt(Ureg*, void* arg)
+{
+ Block *bp;
+ Ctlr *ctlr;
+ Ether *edev;
+ Rdesc *rdesc;
+ int icr, im, rdh, txdw = 0;
+
+ edev = arg;
+ ctlr = edev->ctlr;
+
+ ilock(&ctlr->imlock);
+ csr32w(ctlr, Imc, ~0);
+ im = ctlr->im;
+
+ for(icr = csr32r(ctlr, Icr); icr & ctlr->im; icr = csr32r(ctlr, Icr)){
+ /*
+ * Link status changed.
+ */
+ if(icr & (Rxseq|Lsc)){
+ /*
+ * More here...
+ */
+ }
+
+ /*
+ * Process any received packets.
+ */
+ rdh = ctlr->rdh;
+ for(;;){
+ rdesc = &ctlr->rdba[rdh];
+ if(!(rdesc->status & Rdd))
+ break;
+ if ((rdesc->status & Reop) && rdesc->errors == 0) {
+ bp = ctlr->rb[rdh];
+ ctlr->rb[rdh] = nil;
+ /*
+ * it appears that the original 82543 needed
+ * to have the Ethernet CRC excluded, but that
+ * the newer chips do not?
+ */
+ bp->wp += rdesc->length /* -4 */;
+ toringbuf(edev, bp);
+ freeb(bp);
+ } else if ((rdesc->status & Reop) && rdesc->errors)
+ print("igbe: input packet error 0x%ux\n",
+ rdesc->errors);
+ rdesc->status = 0;
+ rdh = NEXT(rdh, Nrdesc);
+ }
+ ctlr->rdh = rdh;
+
+ if(icr & Rxdmt0)
+ igbereplenish(ctlr);
+ if(icr & Txdw){
+ im &= ~Txdw;
+ txdw++;
+ }
+ }
+
+ ctlr->im = im;
+ csr32w(ctlr, Ims, im);
+ iunlock(&ctlr->imlock);
+
+ if(txdw)
+ igbetransmit(edev);
+}
+
+static int
+igbeinit(Ether* edev)
+{
+ int csr, i, r, ctrl;
+ MiiPhy *phy;
+ Ctlr *ctlr;
+
+ ctlr = edev->ctlr;
+
+ /*
+ * Set up the receive addresses.
+ * There are 16 addresses. The first should be the MAC address.
+ * The others are cleared and not marked valid (MS bit of Rah).
+ */
+ csr = (edev->ea[3]<<24)|(edev->ea[2]<<16)|(edev->ea[1]<<8)|edev->ea[0];
+ csr32w(ctlr, Ral, csr);
+ csr = 0x80000000|(edev->ea[5]<<8)|edev->ea[4];
+ csr32w(ctlr, Rah, csr);
+ for(i = 1; i < 16; i++){
+ csr32w(ctlr, Ral+i*8, 0);
+ csr32w(ctlr, Rah+i*8, 0);
+ }
+
+ /*
+ * Clear the Multicast Table Array.
+ * It's a 4096 bit vector accessed as 128 32-bit registers.
+ */
+ for(i = 0; i < 128; i++)
+ csr32w(ctlr, Mta+i*4, 0);
+
+ /*
+ * Receive initialisation.
+ * Mostly defaults from the datasheet, will
+ * need some tuning for performance:
+ * Rctl descriptor mimimum threshold size
+ * discard pause frames
+ * strip CRC
+ * Rdtr interrupt delay
+ * Rxdctl all the thresholds
+ */
+ csr32w(ctlr, Rctl, 0);
+
+ /*
+ * Allocate the descriptor ring and load its
+ * address and length into the NIC.
+ */
+ ctlr->rdba = xspanalloc(Nrdesc*sizeof(Rdesc), 128 /* was 16 */, 0);
+ csr32w(ctlr, Rdbal, PCIWADDR(ctlr->rdba));
+ csr32w(ctlr, Rdbah, 0);
+ csr32w(ctlr, Rdlen, Nrdesc*sizeof(Rdesc));
+
+ /*
+ * Initialise the ring head and tail pointers and
+ * populate the ring with Blocks.
+ * The datasheet says the tail pointer is set to beyond the last
+ * descriptor hardware can process, which implies the initial
+ * condition is Rdh == Rdt. However, experience shows Rdt must
+ * always be 'behind' Rdh; the replenish routine ensures this.
+ */
+ ctlr->rdh = 0;
+ csr32w(ctlr, Rdh, ctlr->rdh);
+ ctlr->rdt = 0;
+ csr32w(ctlr, Rdt, ctlr->rdt);
+ ctlr->rb = malloc(sizeof(Block*)*Nrdesc);
+ igbereplenish(ctlr);
+
+ /*
+ * Set up Rctl but don't enable receiver (yet).
+ */
+ csr32w(ctlr, Rdtr, 0);
+ switch(ctlr->id){
+ case i82540em:
+ case i82540eplp:
+ case i82541gi:
+ case i82546gb:
+ case i82546eb:
+ case i82547gi:
+ case i82541pi:
+ csr32w(ctlr, Radv, 64);
+ break;
+ }
+ csr32w(ctlr, Rxdctl, (8<<WthreshSHIFT)|(8<<HthreshSHIFT)|4);
+ /*
+ * Enable checksum offload.
+ */
+ csr32w(ctlr, Rxcsum, Tuofl|Ipofl|(ETHERHDRSIZE<<PcssSHIFT));
+
+ csr32w(ctlr, Rctl, Dpf|Bsize2048|Bam|RdtmsHALF);
+ igbeim(ctlr, Rxt0|Rxo|Rxdmt0|Rxseq);
+
+ /*
+ * Transmit initialisation.
+ * Mostly defaults from the datasheet, will
+ * need some tuning for performance. The normal mode will
+ * be full-duplex and things to tune for half-duplex are
+ * Tctl re-transmit on late collision
+ * Tipg all IPG times
+ * Tbt burst timer
+ * Ait adaptive IFS throttle
+ * and in general
+ * Txdmac packet prefetching
+ * Ett transmit early threshold
+ * Tidv interrupt delay value
+ * Txdctl all the thresholds
+ */
+ csr32w(ctlr, Tctl, (0x0F<<CtSHIFT)|Psp|(66<<ColdSHIFT)); /* Fd */
+ switch(ctlr->id){
+ default:
+ r = 6;
+ break;
+ case i82543gc:
+ case i82544ei:
+ case i82547ei:
+ case i82540em:
+ case i82540eplp:
+ case i82541gi:
+ case i82541pi:
+ case i82546gb:
+ case i82546eb:
+ case i82547gi:
+ r = 8;
+ break;
+ }
+ csr32w(ctlr, Tipg, (6<<20)|(8<<10)|r);
+ csr32w(ctlr, Ait, 0);
+ csr32w(ctlr, Txdmac, 0);
+ csr32w(ctlr, Tidv, 128);
+
+ /*
+ * Allocate the descriptor ring and load its
+ * address and length into the NIC.
+ */
+ ctlr->tdba = xspanalloc(Ntdesc*sizeof(Tdesc), 128 /* was 16 */, 0);
+ csr32w(ctlr, Tdbal, PCIWADDR(ctlr->tdba));
+ csr32w(ctlr, Tdbah, 0);
+ csr32w(ctlr, Tdlen, Ntdesc*sizeof(Tdesc));
+
+ /*
+ * Initialise the ring head and tail pointers.
+ */
+ ctlr->tdh = 0;
+ csr32w(ctlr, Tdh, ctlr->tdh);
+ ctlr->tdt = 0;
+ csr32w(ctlr, Tdt, ctlr->tdt);
+ ctlr->tb = malloc(sizeof(Block*)*Ntdesc);
+// ctlr->im |= Txqe|Txdw;
+
+ r = (4<<WthreshSHIFT)|(4<<HthreshSHIFT)|(8<<PthreshSHIFT);
+ switch(ctlr->id){
+ default:
+ break;
+ case i82540em:
+ case i82540eplp:
+ case i82547gi:
+ case i82546gb:
+ case i82546eb:
+ case i82541gi:
+ case i82541pi:
+ r = csr32r(ctlr, Txdctl);
+ r &= ~WthreshMASK;
+ r |= Gran|(4<<WthreshSHIFT);
+
+ csr32w(ctlr, Tadv, 64);
+ break;
+ }
+ csr32w(ctlr, Txdctl, r);
+
+ r = csr32r(ctlr, Tctl);
+ r |= Ten;
+ csr32w(ctlr, Tctl, r);
+
+ if(ctlr->mii == nil || ctlr->mii->curphy == nil) {
+ print("igbe: no mii (yet)\n");
+ return 0;
+ }
+ /* wait for the link to come up */
+ if (miistatus(ctlr->mii) < 0)
+ return -1;
+ print("igbe: phy: ");
+ phy = ctlr->mii->curphy;
+ if (phy->fd)
+ print("full duplex");
+ else
+ print("half duplex");
+ print(", %d Mb/s\n", phy->speed);
+
+ /*
+ * Flow control.
+ */
+ ctrl = csr32r(ctlr, Ctrl);
+ if(phy->rfc)
+ ctrl |= Rfce;
+ if(phy->tfc)
+ ctrl |= Tfce;
+ csr32w(ctlr, Ctrl, ctrl);
+
+ return 0;
+}
+
+static int
+i82543mdior(Ctlr* ctlr, int n)
+{
+ int ctrl, data, i, r;
+
+ /*
+ * Read n bits from the Management Data I/O Interface.
+ */
+ ctrl = csr32r(ctlr, Ctrl);
+ r = (ctrl & ~Mddo)|Mdco;
+ data = 0;
+ for(i = n-1; i >= 0; i--){
+ if(csr32r(ctlr, Ctrl) & Mdd)
+ data |= (1<<i);
+ csr32w(ctlr, Ctrl, Mdc|r);
+ csr32w(ctlr, Ctrl, r);
+ }
+ csr32w(ctlr, Ctrl, ctrl);
+
+ return data;
+}
+
+static int
+i82543mdiow(Ctlr* ctlr, int bits, int n)
+{
+ int ctrl, i, r;
+
+ /*
+ * Write n bits to the Management Data I/O Interface.
+ */
+ ctrl = csr32r(ctlr, Ctrl);
+ r = Mdco|Mddo|ctrl;
+ for(i = n-1; i >= 0; i--){
+ if(bits & (1<<i))
+ r |= Mdd;
+ else
+ r &= ~Mdd;
+ csr32w(ctlr, Ctrl, Mdc|r);
+ csr32w(ctlr, Ctrl, r);
+ }
+ csr32w(ctlr, Ctrl, ctrl);
+
+ return 0;
+}
+
+static int
+i82543miimir(Mii* mii, int pa, int ra)
+{
+ int data;
+ Ctlr *ctlr;
+
+ ctlr = mii->ctlr;
+
+ /*
+ * MII Management Interface Read.
+ *
+ * Preamble;
+ * ST+OP+PHYAD+REGAD;
+ * TA + 16 data bits.
+ */
+ i82543mdiow(ctlr, 0xFFFFFFFF, 32);
+ i82543mdiow(ctlr, 0x1800|(pa<<5)|ra, 14);
+ data = i82543mdior(ctlr, 18);
+
+ if(data & 0x10000)
+ return -1;
+
+ return data & 0xFFFF;
+}
+
+static int
+i82543miimiw(Mii* mii, int pa, int ra, int data)
+{
+ Ctlr *ctlr;
+
+ ctlr = mii->ctlr;
+
+ /*
+ * MII Management Interface Write.
+ *
+ * Preamble;
+ * ST+OP+PHYAD+REGAD+TA + 16 data bits;
+ * Z.
+ */
+ i82543mdiow(ctlr, 0xFFFFFFFF, 32);
+ data &= 0xFFFF;
+ data |= (0x05<<(5+5+2+16))|(pa<<(5+2+16))|(ra<<(2+16))|(0x02<<16);
+ i82543mdiow(ctlr, data, 32);
+
+ return 0;
+}
+
+static int
+igbemiimir(Mii* mii, int pa, int ra)
+{
+ Ctlr *ctlr;
+ int mdic, timo;
+
+ ctlr = mii->ctlr;
+
+ csr32w(ctlr, Mdic, MDIrop|(pa<<MDIpSHIFT)|(ra<<MDIrSHIFT));
+ mdic = 0;
+ for(timo = 64; timo; timo--){
+ mdic = csr32r(ctlr, Mdic);
+ if(mdic & (MDIe|MDIready))
+ break;
+ microdelay(1);
+ }
+
+ if((mdic & (MDIe|MDIready)) == MDIready)
+ return mdic & 0xFFFF;
+ return -1;
+}
+
+static int
+igbemiimiw(Mii* mii, int pa, int ra, int data)
+{
+ Ctlr *ctlr;
+ int mdic, timo;
+
+ ctlr = mii->ctlr;
+
+ data &= MDIdMASK;
+ csr32w(ctlr, Mdic, MDIwop|(pa<<MDIpSHIFT)|(ra<<MDIrSHIFT)|data);
+ mdic = 0;
+ for(timo = 64; timo; timo--){
+ mdic = csr32r(ctlr, Mdic);
+ if(mdic & (MDIe|MDIready))
+ break;
+ microdelay(1);
+ }
+ if((mdic & (MDIe|MDIready)) == MDIready)
+ return 0;
+ return -1;
+}
+
+static int
+igbemii(Ctlr* ctlr)
+{
+ MiiPhy *phy = (MiiPhy *)1;
+ int ctrl, p, r;
+
+ USED(phy);
+ r = csr32r(ctlr, Status);
+ if(r & Tbimode)
+ return -1;
+ if((ctlr->mii = malloc(sizeof(Mii))) == nil)
+ return -1;
+ ctlr->mii->ctlr = ctlr;
+
+ ctrl = csr32r(ctlr, Ctrl);
+ ctrl |= Slu;
+
+ switch(ctlr->id){
+ case i82543gc:
+ ctrl |= Frcdplx|Frcspd;
+ csr32w(ctlr, Ctrl, ctrl);
+
+ /*
+ * The reset pin direction (Mdro) should already
+ * be set from the EEPROM load.
+ * If it's not set this configuration is unexpected
+ * so bail.
+ */
+ r = csr32r(ctlr, Ctrlext);
+ if(!(r & Mdro))
+ return -1;
+ csr32w(ctlr, Ctrlext, r);
+ delay(20);
+ r = csr32r(ctlr, Ctrlext);
+ r &= ~Mdr;
+ csr32w(ctlr, Ctrlext, r);
+ delay(20);
+ r = csr32r(ctlr, Ctrlext);
+ r |= Mdr;
+ csr32w(ctlr, Ctrlext, r);
+ delay(20);
+
+ ctlr->mii->mir = i82543miimir;
+ ctlr->mii->miw = i82543miimiw;
+ break;
+ case i82544ei:
+ case i82547ei:
+ case i82540em:
+ case i82540eplp:
+ case i82547gi:
+ case i82541gi:
+ case i82541pi:
+ case i82546gb:
+ case i82546eb:
+ ctrl &= ~(Frcdplx|Frcspd);
+ csr32w(ctlr, Ctrl, ctrl);
+ ctlr->mii->mir = igbemiimir;
+ ctlr->mii->miw = igbemiimiw;
+ break;
+ default:
+ free(ctlr->mii);
+ ctlr->mii = nil;
+ return -1;
+ }
+
+ if(mii(ctlr->mii, ~0) == 0 || (phy = ctlr->mii->curphy) == nil){
+ if (0)
+ print("phy trouble: phy = 0x%lux\n", (ulong)phy);
+ free(ctlr->mii);
+ ctlr->mii = nil;
+ return -1;
+ }
+ print("oui %X phyno %d\n", phy->oui, phy->phyno);
+
+ /*
+ * 8254X-specific PHY registers not in 802.3:
+ * 0x10 PHY specific control
+ * 0x14 extended PHY specific control
+ * Set appropriate values then reset the PHY to have
+ * changes noted.
+ */
+ switch(ctlr->id){
+ case i82547gi:
+ case i82541gi:
+ case i82541pi:
+ case i82546gb:
+ case i82546eb:
+ break;
+ default:
+ r = miimir(ctlr->mii, 16);
+ r |= 0x0800; /* assert CRS on Tx */
+ r |= 0x0060; /* auto-crossover all speeds */
+ r |= 0x0002; /* polarity reversal enabled */
+ miimiw(ctlr->mii, 16, r);
+
+ r = miimir(ctlr->mii, 20);
+ r |= 0x0070; /* +25MHz clock */
+ r &= ~0x0F00;
+ r |= 0x0100; /* 1x downshift */
+ miimiw(ctlr->mii, 20, r);
+
+ miireset(ctlr->mii);
+ break;
+ }
+ p = 0;
+ if(ctlr->txcw & TxcwPs)
+ p |= AnaP;
+ if(ctlr->txcw & TxcwAs)
+ p |= AnaAP;
+ miiane(ctlr->mii, ~0, p, ~0);
+
+ return 0;
+}
+
+static int
+at93c46io(Ctlr* ctlr, char* op, int data)
+{
+ char *lp, *p;
+ int i, loop, eecd, r;
+
+ eecd = csr32r(ctlr, Eecd);
+
+ r = 0;
+ loop = -1;
+ lp = nil;
+ for(p = op; *p != '\0'; p++){
+ switch(*p){
+ default:
+ return -1;
+ case ' ':
+ continue;
+ case ':': /* start of loop */
+ loop = strtol(p+1, &lp, 0)-1;
+ lp--;
+ if(p == lp)
+ loop = 7;
+ p = lp;
+ continue;
+ case ';': /* end of loop */
+ if(lp == nil)
+ return -1;
+ loop--;
+ if(loop >= 0)
+ p = lp;
+ else
+ lp = nil;
+ continue;
+ case 'C': /* assert clock */
+ eecd |= Sk;
+ break;
+ case 'c': /* deassert clock */
+ eecd &= ~Sk;
+ break;
+ case 'D': /* next bit in 'data' byte */
+ if(loop < 0)
+ return -1;
+ if(data & (1<<loop))
+ eecd |= Di;
+ else
+ eecd &= ~Di;
+ break;
+ case 'O': /* collect data output */
+ i = (csr32r(ctlr, Eecd) & Do) != 0;
+ if(loop >= 0)
+ r |= (i<<loop);
+ else
+ r = i;
+ continue;
+ case 'I': /* assert data input */
+ eecd |= Di;
+ break;
+ case 'i': /* deassert data input */
+ eecd &= ~Di;
+ break;
+ case 'S': /* enable chip select */
+ eecd |= Cs;
+ break;
+ case 's': /* disable chip select */
+ eecd &= ~Cs;
+ break;
+ }
+ csr32w(ctlr, Eecd, eecd);
+ microdelay(1);
+ }
+ if(loop >= 0)
+ return -1;
+ return r;
+}
+
+static int
+at93c46r(Ctlr* ctlr)
+{
+ ushort sum;
+ char rop[20];
+ int addr, areq, bits, data, eecd, i;
+
+ eecd = csr32r(ctlr, Eecd);
+ if(eecd & Spi){
+ print("igbe: SPI EEPROM access not implemented\n");
+ return 0;
+ }
+ if(eecd & Eesz256)
+ bits = 8;
+ else
+ bits = 6;
+ snprint(rop, sizeof(rop), "S :%dDCc;", bits+3);
+
+ sum = 0;
+
+ switch(ctlr->id){
+ default:
+ areq = 0;
+ break;
+ case i82540em:
+ case i82540eplp:
+ case i82541gi:
+ case i82541pi:
+ case i82547gi:
+ case i82546gb:
+ case i82546eb:
+ areq = 1;
+ csr32w(ctlr, Eecd, eecd|Areq);
+ for(i = 0; i < 1000; i++){
+ if((eecd = csr32r(ctlr, Eecd)) & Agnt)
+ break;
+ microdelay(5);
+ }
+ if(!(eecd & Agnt)){
+ print("igbe: not granted EEPROM access\n");
+ goto release;
+ }
+ break;
+ }
+
+ for(addr = 0; addr < 0x40; addr++){
+ /*
+ * Read a word at address 'addr' from the Atmel AT93C46
+ * 3-Wire Serial EEPROM or compatible. The EEPROM access is
+ * controlled by 4 bits in Eecd. See the AT93C46 datasheet
+ * for protocol details.
+ */
+ if(at93c46io(ctlr, rop, (0x06<<bits)|addr) != 0){
+ print("igbe: can't set EEPROM address 0x%2.2X\n", addr);
+ goto release;
+ }
+ data = at93c46io(ctlr, ":16COc;", 0);
+ at93c46io(ctlr, "sic", 0);
+ ctlr->eeprom[addr] = data;
+ sum += data;
+ }
+
+release:
+ if(areq)
+ csr32w(ctlr, Eecd, eecd & ~Areq);
+ return sum;
+}
+
+static void
+detach(Ctlr *ctlr)
+{
+ int r;
+
+ /*
+ * Perform a device reset to get the chip back to the
+ * power-on state, followed by an EEPROM reset to read
+ * the defaults for some internal registers.
+ */
+ csr32w(ctlr, Imc, ~0);
+ csr32w(ctlr, Rctl, 0);
+ csr32w(ctlr, Tctl, 0);
+
+ delay(10);
+
+ csr32w(ctlr, Ctrl, Devrst);
+ /* apparently needed on multi-GHz processors to avoid infinite loops */
+ delay(1);
+ while(csr32r(ctlr, Ctrl) & Devrst)
+ ;
+
+ csr32w(ctlr, Ctrlext, Eerst | csr32r(ctlr, Ctrlext));
+ delay(1);
+ while(csr32r(ctlr, Ctrlext) & Eerst)
+ ;
+
+ switch(ctlr->id){
+ default:
+ break;
+ case i82540em:
+ case i82540eplp:
+ case i82541gi:
+ case i82541pi:
+ case i82547gi:
+ case i82546gb:
+ case i82546eb:
+ r = csr32r(ctlr, Manc);
+ r &= ~Arpen;
+ csr32w(ctlr, Manc, r);
+ break;
+ }
+
+ csr32w(ctlr, Imc, ~0);
+ delay(1);
+ while(csr32r(ctlr, Icr))
+ ;
+}
+
+static void
+igbedetach(Ether *edev)
+{
+ detach(edev->ctlr);
+}
+
+static void
+igbeshutdown(Ether* ether)
+{
+print("igbeshutdown\n");
+ igbedetach(ether);
+}
+
+static int
+igbereset(Ctlr* ctlr)
+{
+ int ctrl, i, pause, r, swdpio, txcw;
+
+ detach(ctlr);
+
+ /*
+ * Read the EEPROM, validate the checksum
+ * then get the device back to a power-on state.
+ */
+ r = at93c46r(ctlr);
+ /* zero return means no SPI EEPROM access */
+ if (r != 0 && r != 0xBABA){
+ print("igbe: bad EEPROM checksum - 0x%4.4uX\n", r);
+ return -1;
+ }
+
+ /*
+ * Snarf and set up the receive addresses.
+ * There are 16 addresses. The first should be the MAC address.
+ * The others are cleared and not marked valid (MS bit of Rah).
+ */
+ if ((ctlr->id == i82546gb || ctlr->id == i82546eb) && BUSFNO(ctlr->pcidev->tbdf) == 1)
+ ctlr->eeprom[Ea+2] += 0x100; // second interface
+ for(i = Ea; i < Eaddrlen/2; i++){
+ ctlr->ra[2*i] = ctlr->eeprom[i];
+ ctlr->ra[2*i+1] = ctlr->eeprom[i]>>8;
+ }
+ r = (ctlr->ra[3]<<24)|(ctlr->ra[2]<<16)|(ctlr->ra[1]<<8)|ctlr->ra[0];
+ csr32w(ctlr, Ral, r);
+ r = 0x80000000|(ctlr->ra[5]<<8)|ctlr->ra[4];
+ csr32w(ctlr, Rah, r);
+ for(i = 1; i < 16; i++){
+ csr32w(ctlr, Ral+i*8, 0);
+ csr32w(ctlr, Rah+i*8, 0);
+ }
+
+ /*
+ * Clear the Multicast Table Array.
+ * It's a 4096 bit vector accessed as 128 32-bit registers.
+ */
+ memset(ctlr->mta, 0, sizeof(ctlr->mta));
+ for(i = 0; i < 128; i++)
+ csr32w(ctlr, Mta+i*4, 0);
+
+ /*
+ * Just in case the Eerst didn't load the defaults
+ * (doesn't appear to fully on the 8243GC), do it manually.
+ */
+ if (ctlr->id == i82543gc) {
+ txcw = csr32r(ctlr, Txcw);
+ txcw &= ~(TxcwAne|TxcwPauseMASK|TxcwFd);
+ ctrl = csr32r(ctlr, Ctrl);
+ ctrl &= ~(SwdpioloMASK|Frcspd|Ilos|Lrst|Fd);
+
+ if(ctlr->eeprom[Icw1] & 0x0400){
+ ctrl |= Fd;
+ txcw |= TxcwFd;
+ }
+ if(ctlr->eeprom[Icw1] & 0x0200)
+ ctrl |= Lrst;
+ if(ctlr->eeprom[Icw1] & 0x0010)
+ ctrl |= Ilos;
+ if(ctlr->eeprom[Icw1] & 0x0800)
+ ctrl |= Frcspd;
+ swdpio = (ctlr->eeprom[Icw1] & 0x01E0)>>5;
+ ctrl |= swdpio<<SwdpioloSHIFT;
+ csr32w(ctlr, Ctrl, ctrl);
+
+ ctrl = csr32r(ctlr, Ctrlext);
+ ctrl &= ~(Ips|SwdpiohiMASK);
+ swdpio = (ctlr->eeprom[Icw2] & 0x00F0)>>4;
+ if(ctlr->eeprom[Icw1] & 0x1000)
+ ctrl |= Ips;
+ ctrl |= swdpio<<SwdpiohiSHIFT;
+ csr32w(ctlr, Ctrlext, ctrl);
+
+ if(ctlr->eeprom[Icw2] & 0x0800)
+ txcw |= TxcwAne;
+ pause = (ctlr->eeprom[Icw2] & 0x3000)>>12;
+ txcw |= pause<<TxcwPauseSHIFT;
+ switch(pause){
+ default:
+ ctlr->fcrtl = 0x00002000;
+ ctlr->fcrth = 0x00004000;
+ txcw |= TxcwAs|TxcwPs;
+ break;
+ case 0:
+ ctlr->fcrtl = 0x00002000;
+ ctlr->fcrth = 0x00004000;
+ break;
+ case 2:
+ ctlr->fcrtl = 0;
+ ctlr->fcrth = 0;
+ txcw |= TxcwAs;
+ break;
+ }
+ ctlr->txcw = txcw;
+ csr32w(ctlr, Txcw, txcw);
+ }
+ /*
+ * Flow control - values from the datasheet.
+ */
+ csr32w(ctlr, Fcal, 0x00C28001);
+ csr32w(ctlr, Fcah, 0x00000100);
+ csr32w(ctlr, Fct, 0x00008808);
+ csr32w(ctlr, Fcttv, 0x00000100);
+
+ csr32w(ctlr, Fcrtl, ctlr->fcrtl);
+ csr32w(ctlr, Fcrth, ctlr->fcrth);
+
+ ilock(&ctlr->imlock);
+ csr32w(ctlr, Imc, ~0);
+ ctlr->im = Lsc;
+ csr32w(ctlr, Ims, ctlr->im);
+ iunlock(&ctlr->imlock);
+
+ if(!(csr32r(ctlr, Status) & Tbimode) && igbemii(ctlr) < 0) {
+ print("igbe: igbemii failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+igbepci(void)
+{
+ int port, cls;
+ Pcidev *p;
+ Ctlr *ctlr;
+ static int first = 1;
+
+ if (first)
+ first = 0;
+ else
+ return;
+
+ p = nil;
+ while(p = pcimatch(p, 0, 0)){
+ if(p->ccrb != 0x02 || p->ccru != 0)
+ continue;
+
+ switch((p->did<<16)|p->vid){
+ case i82542:
+ default:
+ continue;
+
+ case (0x1001<<16)|0x8086: /* Intel PRO/1000 F */
+ break;
+ case i82543gc:
+ case i82544ei:
+ case i82547ei:
+ case i82540em:
+ case i82540eplp:
+ case i82547gi:
+ case i82541gi:
+ case i82541pi:
+ case i82546gb:
+ case i82546eb:
+ break;
+ }
+
+ /* the 82547EI is on the CSA bus, whatever that is */
+ port = upamalloc(p->mem[0].bar & ~0x0F, p->mem[0].size, 0);
+ if(port == 0){
+ print("igbe: can't map %d @ 0x%8.8luX\n",
+ p->mem[0].size, p->mem[0].bar);
+ continue;
+ }
+
+ /*
+ * from etherga620.c:
+ * If PCI Write-and-Invalidate is enabled set the max write DMA
+ * value to the host cache-line size (32 on Pentium or later).
+ */
+ if(p->pcr & MemWrInv){
+ cls = pcicfgr8(p, PciCLS) * 4;
+ if(cls != CACHELINESZ)
+ pcicfgw8(p, PciCLS, CACHELINESZ/4);
+ }
+
+ cls = pcicfgr8(p, PciCLS);
+ switch(cls){
+ default:
+ print("igbe: unexpected CLS - %d bytes\n",
+ cls*sizeof(long));
+ break;
+ case 0x00:
+ case 0xFF:
+ /* alphapc 164lx returns 0 */
+ print("igbe: unusable PciCLS: %d, using %d longs\n",
+ cls, CACHELINESZ/sizeof(long));
+ cls = CACHELINESZ/sizeof(long);
+ pcicfgw8(p, PciCLS, cls);
+ break;
+ case 0x08:
+ case 0x10:
+ break;
+ }
+
+ ctlr = malloc(sizeof(Ctlr));
+ ctlr->port = port;
+ ctlr->pcidev = p;
+ ctlr->id = (p->did<<16)|p->vid;
+ ctlr->cls = cls*4;
+ ctlr->nic = KADDR(ctlr->port);
+print("status0 %8.8uX\n", csr32r(ctlr, Status));
+ if(igbereset(ctlr)){
+ free(ctlr);
+ continue;
+ }
+print("status1 %8.8uX\n", csr32r(ctlr, Status));
+ pcisetbme(p);
+
+ if(ctlrhead != nil)
+ ctlrtail->next = ctlr;
+ else
+ ctlrhead = ctlr;
+ ctlrtail = ctlr;
+ }
+}
+
+int
+igbepnp(Ether* edev)
+{
+ int i;
+ Ctlr *ctlr;
+ uchar ea[Eaddrlen];
+
+ if(ctlrhead == nil)
+ igbepci();
+
+ /*
+ * Any adapter matches if no edev->port is supplied,
+ * otherwise the ports must match.
+ */
+ for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
+ if(ctlr->active)
+ continue;
+ if(edev->port == 0 || edev->port == ctlr->port){
+ ctlr->active = 1;
+ break;
+ }
+ }
+ if(ctlr == nil)
+ return -1;
+
+ edev->ctlr = ctlr;
+ edev->port = ctlr->port;
+ edev->irq = ctlr->pcidev->intl;
+ edev->tbdf = ctlr->pcidev->tbdf;
+// edev->mbps = 1000;
+
+ /*
+ * Check if the adapter's station address is to be overridden.
+ * If not, read it from the EEPROM and set in ether->ea prior to
+ * loading the station address in the hardware.
+ */
+ memset(ea, 0, Eaddrlen);
+ if(memcmp(ea, edev->ea, Eaddrlen) == 0){
+ for(i = 0; i < Eaddrlen/2; i++){
+ edev->ea[2*i] = ctlr->eeprom[i];
+ edev->ea[2*i+1] = ctlr->eeprom[i]>>8;
+ }
+ }
+ igbeinit(edev);
+
+ /*
+ * Linkage to the generic ethernet driver.
+ */
+ edev->attach = igbeattach;
+ edev->transmit = igbetransmit;
+ edev->interrupt = igbeinterrupt;
+ edev->detach = igbedetach;
+
+ return 0;
+}
diff --git a/os/boot/pc/ethermii.c b/os/boot/pc/ethermii.c
new file mode 100644
index 00000000..160e4fe0
--- /dev/null
+++ b/os/boot/pc/ethermii.c
@@ -0,0 +1,224 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "etherif.h"
+#include "ethermii.h"
+
+int
+mii(Mii* mii, int mask)
+{
+ MiiPhy *miiphy;
+ int bit, phyno, r, rmask;
+
+ /*
+ * Probe through mii for PHYs in mask;
+ * return the mask of those found in the current probe.
+ * If the PHY has not already been probed, update
+ * the Mii information.
+ */
+ rmask = 0;
+ for(phyno = 0; phyno < NMiiPhy; phyno++){
+ bit = 1<<phyno;
+ if(!(mask & bit))
+ continue;
+ if(mii->mask & bit){
+ rmask |= bit;
+ continue;
+ }
+ if(mii->mir(mii, phyno, Bmsr) == -1)
+ continue;
+ if((miiphy = malloc(sizeof(MiiPhy))) == nil)
+ continue;
+
+ miiphy->mii = mii;
+ r = mii->mir(mii, phyno, Phyidr1);
+ miiphy->oui = (r & 0x3FFF)<<6;
+ r = mii->mir(mii, phyno, Phyidr2);
+ miiphy->oui |= r>>10;
+ miiphy->phyno = phyno;
+
+ miiphy->anar = ~0;
+ miiphy->fc = ~0;
+ miiphy->mscr = ~0;
+
+ mii->phy[phyno] = miiphy;
+ if(mii->curphy == nil)
+ mii->curphy = miiphy;
+ mii->mask |= bit;
+ mii->nphy++;
+
+ rmask |= bit;
+ }
+ return rmask;
+}
+
+int
+miimir(Mii* mii, int r)
+{
+ if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+ return -1;
+ return mii->mir(mii, mii->curphy->phyno, r);
+}
+
+int
+miimiw(Mii* mii, int r, int data)
+{
+ if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+ return -1;
+ return mii->miw(mii, mii->curphy->phyno, r, data);
+}
+
+int
+miireset(Mii* mii)
+{
+ int bmcr;
+
+ if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+ return -1;
+ bmcr = mii->mir(mii, mii->curphy->phyno, Bmcr);
+ bmcr |= BmcrR;
+ mii->miw(mii, mii->curphy->phyno, Bmcr, bmcr);
+ microdelay(1);
+
+ return 0;
+}
+
+int
+miiane(Mii* mii, int a, int p, int e)
+{
+ int anar, bmsr, mscr, r, phyno;
+
+ if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+ return -1;
+ phyno = mii->curphy->phyno;
+
+ bmsr = mii->mir(mii, phyno, Bmsr);
+ if(!(bmsr & BmsrAna))
+ return -1;
+
+ if(a != ~0)
+ anar = (AnaTXFD|AnaTXHD|Ana10FD|Ana10HD) & a;
+ else if(mii->curphy->anar != ~0)
+ anar = mii->curphy->anar;
+ else{
+ anar = mii->mir(mii, phyno, Anar);
+ anar &= ~(AnaAP|AnaP|AnaT4|AnaTXFD|AnaTXHD|Ana10FD|Ana10HD);
+ if(bmsr & Bmsr10THD)
+ anar |= Ana10HD;
+ if(bmsr & Bmsr10TFD)
+ anar |= Ana10FD;
+ if(bmsr & Bmsr100TXHD)
+ anar |= AnaTXHD;
+ if(bmsr & Bmsr100TXFD)
+ anar |= AnaTXFD;
+ }
+ mii->curphy->anar = anar;
+
+ if(p != ~0)
+ anar |= (AnaAP|AnaP) & p;
+ else if(mii->curphy->fc != ~0)
+ anar |= mii->curphy->fc;
+ mii->curphy->fc = (AnaAP|AnaP) & anar;
+
+ if(bmsr & BmsrEs){
+ mscr = mii->mir(mii, phyno, Mscr);
+ mscr &= ~(Mscr1000TFD|Mscr1000THD);
+ if(e != ~0)
+ mscr |= (Mscr1000TFD|Mscr1000THD) & e;
+ else if(mii->curphy->mscr != ~0)
+ mscr = mii->curphy->mscr;
+ else{
+ r = mii->mir(mii, phyno, Esr);
+ if(r & Esr1000THD)
+ mscr |= Mscr1000THD;
+ if(r & Esr1000TFD)
+ mscr |= Mscr1000TFD;
+ }
+ mii->curphy->mscr = mscr;
+ mii->miw(mii, phyno, Mscr, mscr);
+ }
+ mii->miw(mii, phyno, Anar, anar);
+
+ r = mii->mir(mii, phyno, Bmcr);
+ if(!(r & BmcrR)){
+ r |= BmcrAne|BmcrRan;
+ mii->miw(mii, phyno, Bmcr, r);
+ }
+
+ return 0;
+}
+
+int
+miistatus(Mii* mii)
+{
+ MiiPhy *phy;
+ int anlpar, bmsr, p, r, phyno;
+
+ if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+ return -1;
+ phy = mii->curphy;
+ phyno = phy->phyno;
+
+ /*
+ * Check Auto-Negotiation is complete and link is up.
+ * (Read status twice as the Ls bit is sticky).
+ */
+ bmsr = mii->mir(mii, phyno, Bmsr);
+ if(!(bmsr & (BmsrAnc|BmsrAna)))
+ return -1;
+
+ bmsr = mii->mir(mii, phyno, Bmsr);
+ if(!(bmsr & BmsrLs)){
+ phy->link = 0;
+ return -1;
+ }
+
+ phy->speed = phy->fd = phy->rfc = phy->tfc = 0;
+ if(phy->mscr){
+ r = mii->mir(mii, phyno, Mssr);
+ if((phy->mscr & Mscr1000TFD) && (r & Mssr1000TFD)){
+ phy->speed = 1000;
+ phy->fd = 1;
+ }
+ else if((phy->mscr & Mscr1000THD) && (r & Mssr1000THD))
+ phy->speed = 1000;
+ }
+
+ anlpar = mii->mir(mii, phyno, Anlpar);
+ if(phy->speed == 0){
+ r = phy->anar & anlpar;
+ if(r & AnaTXFD){
+ phy->speed = 100;
+ phy->fd = 1;
+ }
+ else if(r & AnaTXHD)
+ phy->speed = 100;
+ else if(r & Ana10FD){
+ phy->speed = 10;
+ phy->fd = 1;
+ }
+ else if(r & Ana10HD)
+ phy->speed = 10;
+ }
+ if(phy->speed == 0)
+ return -1;
+
+ if(phy->fd){
+ p = phy->fc;
+ r = anlpar & (AnaAP|AnaP);
+ if(p == AnaAP && r == (AnaAP|AnaP))
+ phy->tfc = 1;
+ else if(p == (AnaAP|AnaP) && r == AnaAP)
+ phy->rfc = 1;
+ else if((p & AnaP) && (r & AnaP))
+ phy->rfc = phy->tfc = 1;
+ }
+
+ phy->link = 1;
+
+ return 0;
+}
diff --git a/os/boot/pc/ethermii.h b/os/boot/pc/ethermii.h
new file mode 100644
index 00000000..80c4ed06
--- /dev/null
+++ b/os/boot/pc/ethermii.h
@@ -0,0 +1,116 @@
+typedef struct Mii Mii;
+typedef struct MiiPhy MiiPhy;
+
+enum { /* registers */
+ Bmcr = 0x00, /* Basic Mode Control */
+ Bmsr = 0x01, /* Basic Mode Status */
+ Phyidr1 = 0x02, /* PHY Identifier #1 */
+ Phyidr2 = 0x03, /* PHY Identifier #2 */
+ Anar = 0x04, /* Auto-Negotiation Advertisement */
+ Anlpar = 0x05, /* AN Link Partner Ability */
+ Aner = 0x06, /* AN Expansion */
+ Annptr = 0x07, /* AN Next Page TX */
+ Annprr = 0x08, /* AN Next Page RX */
+ Mscr = 0x09, /* MASTER-SLAVE Control */
+ Mssr = 0x0A, /* MASTER-SLAVE Status */
+ Esr = 0x0F, /* Extended Status */
+
+ NMiiPhyr = 32,
+ NMiiPhy = 32,
+};
+
+enum { /* Bmcr */
+ BmcrSs1 = 0x0040, /* Speed Select[1] */
+ BmcrCte = 0x0080, /* Collision Test Enable */
+ BmcrDm = 0x0100, /* Duplex Mode */
+ BmcrRan = 0x0200, /* Restart Auto-Negotiation */
+ BmcrI = 0x0400, /* Isolate */
+ BmcrPd = 0x0800, /* Power Down */
+ BmcrAne = 0x1000, /* Auto-Negotiation Enable */
+ BmcrSs0 = 0x2000, /* Speed Select[0] */
+ BmcrLe = 0x4000, /* Loopback Enable */
+ BmcrR = 0x8000, /* Reset */
+};
+
+enum { /* Bmsr */
+ BmsrEc = 0x0001, /* Extended Capability */
+ BmsrJd = 0x0002, /* Jabber Detect */
+ BmsrLs = 0x0004, /* Link Status */
+ BmsrAna = 0x0008, /* Auto-Negotiation Ability */
+ BmsrRf = 0x0010, /* Remote Fault */
+ BmsrAnc = 0x0020, /* Auto-Negotiation Complete */
+ BmsrPs = 0x0040, /* Preamble Suppression Capable */
+ BmsrEs = 0x0100, /* Extended Status */
+ Bmsr100T2HD = 0x0200, /* 100BASE-T2 HD Capable */
+ Bmsr100T2FD = 0x0400, /* 100BASE-T2 FD Capable */
+ Bmsr10THD = 0x0800, /* 100BASE-T HD Capable */
+ Bmsr10TFD = 0x1000, /* 10BASE-T FD Capable */
+ Bmsr100TXHD = 0x2000, /* 100BASE-TX HD Capable */
+ Bmsr100TXFD = 0x4000, /* 100BASE-TX FD Capable */
+ Bmsr100T4 = 0x8000, /* 100BASE-T4 Capable */
+};
+
+enum { /* Anar/Anlpar */
+ Ana10HD = 0x0020, /* Advertise 10BASE-T */
+ Ana10FD = 0x0040, /* Advertise 10BASE-T FD */
+ AnaTXHD = 0x0080, /* Advertise 100BASE-TX */
+ AnaTXFD = 0x0100, /* Advertise 100BASE-TX FD */
+ AnaT4 = 0x0200, /* Advertise 100BASE-T4 */
+ AnaP = 0x0400, /* Pause */
+ AnaAP = 0x0800, /* Asymmetrical Pause */
+ AnaRf = 0x2000, /* Remote Fault */
+ AnaAck = 0x4000, /* Acknowledge */
+ AnaNp = 0x8000, /* Next Page Indication */
+};
+
+enum { /* Mscr */
+ Mscr1000THD = 0x0100, /* Advertise 1000BASE-T HD */
+ Mscr1000TFD = 0x0200, /* Advertise 1000BASE-T FD */
+};
+
+enum { /* Mssr */
+ Mssr1000THD = 0x0400, /* Link Partner 1000BASE-T HD able */
+ Mssr1000TFD = 0x0800, /* Link Partner 1000BASE-T FD able */
+};
+
+enum { /* Esr */
+ Esr1000THD = 0x1000, /* 1000BASE-T HD Capable */
+ Esr1000TFD = 0x2000, /* 1000BASE-T FD Capable */
+ Esr1000XHD = 0x4000, /* 1000BASE-X HD Capable */
+ Esr1000XFD = 0x8000, /* 1000BASE-X FD Capable */
+};
+
+typedef struct Mii {
+ Lock;
+ int nphy;
+ int mask;
+ MiiPhy* phy[NMiiPhy];
+ MiiPhy* curphy;
+
+ void* ctlr;
+ int (*mir)(Mii*, int, int);
+ int (*miw)(Mii*, int, int, int);
+} Mii;
+
+typedef struct MiiPhy {
+ Mii* mii;
+ int oui;
+ int phyno;
+
+ int anar;
+ int fc;
+ int mscr;
+
+ int link;
+ int speed;
+ int fd;
+ int rfc;
+ int tfc;
+};
+
+extern int mii(Mii*, int);
+extern int miiane(Mii*, int, int, int);
+extern int miimir(Mii*, int);
+extern int miimiw(Mii*, int, int);
+extern int miireset(Mii*);
+extern int miistatus(Mii*);
diff --git a/os/boot/pc/etherrhine.c b/os/boot/pc/etherrhine.c
new file mode 100644
index 00000000..b4d37bf5
--- /dev/null
+++ b/os/boot/pc/etherrhine.c
@@ -0,0 +1,676 @@
+ /*
+ Via Rhine driver, written for VT6102.
+ Uses the ethermii to control PHY.
+
+ Currently always copies on both, tx and rx.
+ rx side could be copy-free, and tx-side might be made
+ (almost) copy-free by using (possibly) two descriptors (if it allows
+ arbitrary tx lengths, which it should..): first for alignment and
+ second for rest of the frame. Rx-part should be worth doing.
+*/
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+typedef struct QLock { int r; } QLock;
+#define qlock(i) while(0)
+#define qunlock(i) while(0)
+#define coherence()
+#define iprint print
+
+#include "etherif.h"
+#include "ethermii.h"
+
+typedef struct Desc Desc;
+typedef struct Ctlr Ctlr;
+
+enum {
+ Ntxd = 4,
+ Nrxd = 4,
+ Nwait = 50,
+ Ntxstats = 9,
+ Nrxstats = 8,
+ BIGSTR = 8192,
+};
+
+struct Desc {
+ ulong stat;
+ ulong size;
+ ulong addr;
+ ulong next;
+ char *buf;
+ ulong pad[3];
+};
+
+struct Ctlr {
+ Pcidev *pci;
+ int attached;
+ int txused;
+ int txhead;
+ int txtail;
+ int rxtail;
+ ulong port;
+
+ Mii mii;
+
+ ulong txstats[Ntxstats];
+ ulong rxstats[Nrxstats];
+
+ Desc *txd; /* wants to be aligned on 16-byte boundary */
+ Desc *rxd;
+
+ QLock attachlck;
+ Lock tlock;
+};
+
+#define ior8(c, r) (inb((c)->port+(r)))
+#define ior16(c, r) (ins((c)->port+(r)))
+#define ior32(c, r) (inl((c)->port+(r)))
+#define iow8(c, r, b) (outb((c)->port+(r), (int)(b)))
+#define iow16(c, r, w) (outs((c)->port+(r), (ushort)(w)))
+#define iow32(c, r, l) (outl((c)->port+(r), (ulong)(l)))
+
+enum Regs {
+ Eaddr = 0x0,
+ Rcr = 0x6,
+ Tcr = 0x7,
+ Cr = 0x8,
+ Isr = 0xc,
+ Imr = 0xe,
+ McastAddr = 0x10,
+ RxdAddr = 0x18,
+ TxdAddr = 0x1C,
+ Bcr = 0x6e,
+ RhineMiiPhy = 0x6C,
+ RhineMiiSr = 0x6D,
+ RhineMiiCr = 0x70,
+ RhineMiiAddr = 0x71,
+ RhineMiiData = 0x72,
+ Eecsr = 0x74,
+ ConfigB = 0x79,
+ ConfigD = 0x7B,
+ MiscCr = 0x80,
+ HwSticky = 0x83,
+ MiscIsr = 0x84,
+ MiscImr = 0x86,
+ WolCrSet = 0xA0,
+ WolCfgSet = 0xA1,
+ WolCgSet = 0xA3,
+ WolCrClr = 0xA4,
+ PwrCfgClr = 0xA5,
+ WolCgClr = 0xA7,
+};
+
+enum Rcrbits {
+ RxErrX = 1<<0,
+ RxSmall = 1<<1,
+ RxMcast = 1<<2,
+ RxBcast = 1<<3,
+ RxProm = 1<<4,
+ RxFifo64 = 0<<5, RxFifo32 = 1<<5, RxFifo128 = 2<<5, RxFifo256 = 3<<5,
+ RxFifo512 = 4<<5, RxFifo768 = 5<<5, RxFifo1024 = 6<<5,
+ RxFifoStoreForward = 7<<5,
+};
+
+enum Tcrbits {
+ TxLoopback0 = 1<<1,
+ TxLoopback1 = 1<<2,
+ TxBackoff = 1<<3,
+ TxFifo128 = 0<<5, TxFifo256 = 1<<5, TxFifo512 = 2<<5, TxFifo1024 = 3<<5,
+ TxFifoStoreForward = 7<<5,
+};
+
+enum Crbits {
+ Init = 1<<0,
+ Start = 1<<1,
+ Stop = 1<<2,
+ RxOn = 1<<3,
+ TxOn = 1<<4,
+ Tdmd = 1<<5,
+ Rdmd = 1<<6,
+ EarlyRx = 1<<8,
+ Reserved0 = 1<<9,
+ FullDuplex = 1<<10,
+ NoAutoPoll = 1<<11,
+ Reserved1 = 1<<12,
+ Tdmd1 = 1<<13,
+ Rdmd1 = 1<<14,
+ Reset = 1<<15,
+};
+
+enum Isrbits {
+ RxOk = 1<<0,
+ TxOk = 1<<1,
+ RxErr = 1<<2,
+ TxErr = 1<<3,
+ TxBufUdf = 1<<4,
+ RxBufLinkErr = 1<<5,
+ BusErr = 1<<6,
+ CrcOvf = 1<<7,
+ EarlyRxInt = 1<<8,
+ TxFifoUdf = 1<<9,
+ RxFifoOvf = 1<<10,
+ TxPktRace = 1<<11,
+ NoRxbuf = 1<<12,
+ TxCollision = 1<<13,
+ PortCh = 1<<14,
+ GPInt = 1<<15
+};
+
+enum Bcrbits {
+ Dma32 = 0<<0, Dma64 = 1<<0, Dma128 = 2<<0,
+ Dma256 = 3<<0, Dma512 = 4<<0, Dma1024 = 5<<0,
+ DmaStoreForward = 7<<0,
+ DupRxFifo0 = 1<<3, DupRxFifo1 = 1<<4, DupRxFifo2 = 1<<5,
+ ExtraLed = 1<<6,
+ MediumSelect = 1<<7,
+ PollTimer0 = 1<<8, PollTimer1 = 1<<9, PollTimer2 = 1<<10,
+ DupTxFifo0 = 1<<11, DupTxFifo1 = 1<<12, DupTxFifo2 = 1<<13,
+};
+
+enum Eecsrbits {
+ EeAutoLoad = 1<<5,
+};
+
+enum MiscCrbits {
+ Timer0Enable= 1<<0,
+ Timer0Suspend = 1<<1,
+ HalfDuplexFlowControl = 1<<2,
+ FullDuplexFlowControl = 1<<3,
+ Timer1Enable = 1<<8,
+ ForceSoftReset = 1<<14,
+};
+
+enum HwStickybits {
+ StickyDS0 = 1<<0,
+ StickyDS1 = 1<<1,
+ WOLEna = 1<<2,
+ WOLStat = 1<<3,
+};
+
+enum WolCgbits {
+ PmeOvr = 1<<7,
+};
+
+enum Descbits {
+ OwnNic = 1<<31, /* stat */
+ TxAbort = 1<<8, /* stat */
+ TxError = 1<<15, /* stat */
+ RxChainbuf = 1<<10, /* stat */
+ RxChainStart = 1<<9, /* stat */
+ RxChainEnd = 1<<8, /* stat */
+ Chainbuf = 1<<15, /* size rx & tx*/
+ TxDisableCrc = 1<<16, /* size */
+ TxChainStart = 1<<21, /* size */
+ TxChainEnd = 1<<22, /* size */
+ TxInt = 1<<23, /* size */
+};
+
+enum ConfigDbits {
+ BackoffOptional = 1<<0,
+ BackoffAMD = 1<<1,
+ BackoffDEC = 1<<2,
+ BackoffRandom = 1<<3,
+ PmccTestMode = 1<<4,
+ PciReadlineCap = 1<<5,
+ DiagMode = 1<<6,
+ MmioEnable = 1<<7,
+};
+
+enum ConfigBbits {
+ LatencyTimer = 1<<0,
+ WriteWaitState = 1<<1,
+ ReadWaitState = 1<<2,
+ RxArbit = 1<<3,
+ TxArbit = 1<<4,
+ NoMemReadline = 1<<5,
+ NoParity = 1<<6,
+ NoTxQueuing = 1<<7,
+};
+
+enum RhineMiiCrbits {
+ Mdc = 1<<0,
+ Mdi = 1<<1,
+ Mdo = 1<<2,
+ Mdout = 1<<3,
+ Mdpm = 1<<4,
+ Wcmd = 1<<5,
+ Rcmd = 1<<6,
+ Mauto = 1<<7,
+};
+
+enum RhineMiiSrbits {
+ Speed10M = 1<<0,
+ LinkFail = 1<<1,
+ PhyError = 1<<3,
+ DefaultPhy = 1<<4,
+ ResetPhy = 1<<7,
+};
+
+enum RhineMiiAddrbits {
+ Mdone = 1<<5,
+ Msrcen = 1<<6,
+ Midle = 1<<7,
+};
+
+static char *
+txstatnames[Ntxstats] = {
+ "aborts (excess collisions)",
+ "out of window collisions",
+ "carrier sense losses",
+ "fifo underflows",
+ "invalid descriptor format or underflows",
+ "system errors",
+ "reserved",
+ "transmit errors",
+ "collisions",
+};
+
+static char *
+rxstatnames[Nrxstats] = {
+ "receiver errors",
+ "crc errors",
+ "frame alignment errors",
+ "fifo overflows",
+ "long packets",
+ "run packets",
+ "system errors",
+ "buffer underflows",
+};
+
+static void
+attach(Ether *edev)
+{
+ Ctlr *ctlr;
+ Desc *txd, *rxd, *td, *rd;
+ Mii *mi;
+ MiiPhy *phy;
+ int i, s;
+
+ ctlr = edev->ctlr;
+ qlock(&ctlr->attachlck);
+ if (ctlr->attached == 0) {
+ txd = ctlr->txd;
+ rxd = ctlr->rxd;
+ for (i = 0; i < Ntxd; ++i) {
+ td = &txd[i];
+ td->next = PCIWADDR(&txd[(i+1) % Ntxd]);
+ td->buf = xspanalloc(sizeof(Etherpkt)+4, 4, 0);
+ td->addr = PCIWADDR(td->buf);
+ td->size = 0;
+ coherence();
+ td->stat = 0;
+ }
+ for (i = 0; i < Nrxd; ++i) {
+ rd = &rxd[i];
+ rd->next = PCIWADDR(&rxd[(i+1) % Nrxd]);
+ rd->buf = xspanalloc(sizeof(Etherpkt)+4, 4, 0);
+ rd->addr = PCIWADDR(rd->buf);
+ rd->size = sizeof(Etherpkt)+4;
+ coherence();
+ rd->stat = OwnNic;
+ }
+
+ ctlr->txhead = ctlr->txtail = ctlr->rxtail = 0;
+ mi = &ctlr->mii;
+ miistatus(mi);
+ phy = mi->curphy;
+ s = splhi();
+ iow32(ctlr, TxdAddr, PCIWADDR(&txd[0]));
+ iow32(ctlr, RxdAddr, PCIWADDR(&rxd[0]));
+ iow16(ctlr, Cr, (phy->fd ? FullDuplex : 0) | NoAutoPoll | TxOn | RxOn | Start | Rdmd);
+ iow16(ctlr, Isr, 0xFFFF);
+ iow16(ctlr, Imr, 0xFFFF);
+ iow8(ctlr, MiscIsr, 0xFF);
+ iow8(ctlr, MiscImr, ~(3<<5));
+ splx(s);
+ }
+ ctlr->attached++;
+ qunlock(&ctlr->attachlck);
+}
+
+static void
+txstart(Ether *edev)
+{
+ Ctlr *ctlr;
+ Desc *txd, *td;
+ int i, txused, n;
+ RingBuf *tb;
+
+ ctlr = edev->ctlr;
+
+ txd = ctlr->txd;
+ i = ctlr->txhead;
+ txused = ctlr->txused;
+ n = 0;
+ while (txused < Ntxd) {
+ tb = &edev->tb[edev->ti];
+ if(tb->owner != Interface)
+ break;
+
+ td = &txd[i];
+ memmove(td->buf, tb->pkt, tb->len);
+ td->size = tb->len | TxChainStart | TxChainEnd | TxInt; /* could reduce number of ints here */
+ coherence();
+ td->stat = OwnNic;
+ i = (i + 1) % Ntxd;
+ txused++;
+ n++;
+
+ tb->owner = Host;
+ edev->ti = NEXT(edev->ti, edev->ntb);
+ }
+ if (n)
+ iow16(ctlr, Cr, ior16(ctlr, Cr) | Tdmd);
+
+ ctlr->txhead = i;
+ ctlr->txused = txused;
+}
+
+static void
+transmit(Ether *edev)
+{
+ Ctlr *ctlr;
+ ctlr = edev->ctlr;
+ ilock(&ctlr->tlock);
+ txstart(edev);
+ iunlock(&ctlr->tlock);
+}
+
+static void
+txcomplete(Ether *edev)
+{
+ Ctlr *ctlr;
+ Desc *txd, *td;
+ int i, txused, j;
+ ulong stat;
+
+ ctlr = edev->ctlr;
+ txd = ctlr->txd;
+ txused = ctlr->txused;
+ i = ctlr->txtail;
+ while (txused > 0) {
+ td = &txd[i];
+ stat = td->stat;
+
+ if (stat & OwnNic)
+ break;
+
+ ctlr->txstats[Ntxstats-1] += stat & 0xF;
+ for (j = 0; j < Ntxstats-1; ++j)
+ if (stat & (1<<(j+8)))
+ ctlr->txstats[j]++;
+
+ i = (i + 1) % Ntxd;
+ txused--;
+ }
+ ctlr->txused = txused;
+ ctlr->txtail = i;
+
+ if (txused <= Ntxd/2)
+ txstart(edev);
+}
+
+static void
+interrupt(Ureg *, void *arg)
+{
+ Ether *edev;
+ Ctlr *ctlr;
+ RingBuf *rb;
+ ushort isr, misr;
+ ulong stat;
+ Desc *rxd, *rd;
+ int i, n, j, size;
+
+ edev = (Ether*)arg;
+ ctlr = edev->ctlr;
+ iow16(ctlr, Imr, 0);
+ isr = ior16(ctlr, Isr);
+ iow16(ctlr, Isr, 0xFFFF);
+ misr = ior16(ctlr, MiscIsr) & ~(3<<5); /* don't care about used defined ints */
+
+ if (isr & RxOk) {
+ rxd = ctlr->rxd;
+ i = ctlr->rxtail;
+
+ n = 0;
+ while ((rxd[i].stat & OwnNic) == 0) {
+ rd = &rxd[i];
+ stat = rd->stat;
+ for (j = 0; j < Nrxstats; ++j)
+ if (stat & (1<<j))
+ ctlr->rxstats[j]++;
+
+ if (stat & 0xFF)
+ iprint("rx: %lux\n", stat & 0xFF);
+
+ size = ((rd->stat>>16) & 2047) - 4;
+
+ rb = &edev->rb[edev->ri];
+ if(rb->owner == Interface){
+ rb->owner = Host;
+ rb->len = size;
+ memmove(rb->pkt, rd->buf, size);
+ edev->ri = NEXT(edev->ri, edev->nrb);
+ }
+
+ rd->size = sizeof(Etherpkt)+4;
+ coherence();
+ rd->stat = OwnNic;
+ i = (i + 1) % Nrxd;
+ n++;
+ }
+ if (n)
+ iow16(ctlr, Cr, ior16(ctlr, Cr) | Rdmd);
+ ctlr->rxtail = i;
+ isr &= ~RxOk;
+ }
+ if (isr & TxOk) {
+ txcomplete(edev);
+ isr &= ~TxOk;
+ }
+ if (isr | misr)
+ iprint("etherrhine: unhandled irq(s). isr:%x misr:%x\n", isr, misr);
+
+ iow16(ctlr, Imr, 0xFFFF);
+}
+
+static void
+promiscuous(void *arg, int enable)
+{
+ Ether *edev;
+ Ctlr *ctlr;
+
+ edev = arg;
+ ctlr = edev->ctlr;
+ ilock(&ctlr->tlock);
+ iow8(ctlr, Rcr, ior8(ctlr, Rcr) | (enable ? RxProm : RxBcast));
+ iunlock(&ctlr->tlock);
+}
+
+static int
+miiread(Mii *mii, int phy, int reg)
+{
+ Ctlr *ctlr;
+ int n;
+
+ ctlr = mii->ctlr;
+
+ n = Nwait;
+ while (n-- && ior8(ctlr, RhineMiiCr) & (Rcmd | Wcmd))
+ microdelay(1);
+ if (n == Nwait)
+ iprint("etherrhine: miiread: timeout\n");
+
+ iow8(ctlr, RhineMiiCr, 0);
+ iow8(ctlr, RhineMiiPhy, phy);
+ iow8(ctlr, RhineMiiAddr, reg);
+ iow8(ctlr, RhineMiiCr, Rcmd);
+
+ n = Nwait;
+ while (n-- && ior8(ctlr, RhineMiiCr) & Rcmd)
+ microdelay(1);
+ if (n == Nwait)
+ iprint("etherrhine: miiread: timeout\n");
+
+ n = ior16(ctlr, RhineMiiData);
+
+ return n;
+}
+
+static int
+miiwrite(Mii *mii, int phy, int reg, int data)
+{
+ int n;
+ Ctlr *ctlr;
+
+ ctlr = mii->ctlr;
+
+ n = Nwait;
+ while (n-- && ior8(ctlr, RhineMiiCr) & (Rcmd | Wcmd))
+ microdelay(1);
+ if (n == Nwait)
+ iprint("etherrhine: miiwrite: timeout\n");
+
+ iow8(ctlr, RhineMiiCr, 0);
+ iow8(ctlr, RhineMiiPhy, phy);
+ iow8(ctlr, RhineMiiAddr, reg);
+ iow16(ctlr, RhineMiiData, data);
+ iow8(ctlr, RhineMiiCr, Wcmd);
+
+ n = Nwait;
+ while (n-- && ior8(ctlr, RhineMiiCr) & Wcmd)
+ microdelay(1);
+ if (n == Nwait)
+ iprint("etherrhine: miiwrite: timeout\n");
+
+ return 0;
+}
+
+static void
+reset(Ctlr* ctlr)
+{
+ int i;
+
+ iow16(ctlr, Cr, ior16(ctlr, Cr) | Stop);
+ iow16(ctlr, Cr, ior16(ctlr, Cr) | Reset);
+
+ for (i = 0; i < Nwait; ++i) {
+ if ((ior16(ctlr, Cr) & Reset) == 0)
+ return;
+ delay(5);
+ }
+ iprint("etherrhine: reset timeout\n");
+}
+
+static void
+detach(Ether* edev)
+{
+ reset(edev->ctlr);
+}
+
+static void
+init(Ether *edev)
+{
+ Ctlr *ctlr;
+ int i;
+
+ ctlr = edev->ctlr;
+
+ ilock(&ctlr->tlock);
+
+ pcisetbme(ctlr->pci);
+
+ reset(ctlr);
+
+ iow8(ctlr, Eecsr, ior8(ctlr, Eecsr) | EeAutoLoad);
+ for (i = 0; i < Nwait; ++i) {
+ if ((ior8(ctlr, Eecsr) & EeAutoLoad) == 0)
+ break;
+ delay(5);
+ }
+ if (i == Nwait)
+ iprint("etherrhine: eeprom autoload timeout\n");
+
+ for (i = 0; i < Eaddrlen; ++i)
+ edev->ea[i] = ior8(ctlr, Eaddr + i);
+
+ ctlr->mii.mir = miiread;
+ ctlr->mii.miw = miiwrite;
+ ctlr->mii.ctlr = ctlr;
+
+ if(mii(&ctlr->mii, ~0) == 0 || ctlr->mii.curphy == nil){
+ iprint("etherrhine: init mii failure\n");
+ return;
+ }
+ for (i = 0; i < NMiiPhy; ++i)
+ if (ctlr->mii.phy[i])
+ if (ctlr->mii.phy[i]->oui != 0xFFFFF)
+ ctlr->mii.curphy = ctlr->mii.phy[i];
+
+ miistatus(&ctlr->mii);
+
+ iow16(ctlr, Imr, 0);
+ iow16(ctlr, Cr, ior16(ctlr, Cr) | Stop);
+
+ iunlock(&ctlr->tlock);
+}
+
+static Pcidev *
+rhinematch(ulong)
+{
+ static int nrhines = 0;
+ int nfound = 0;
+ Pcidev *p = nil;
+
+ while (p = pcimatch(p, 0x1106, 0))
+ if (p->did == 0x3065)
+ if (++nfound > nrhines) {
+ nrhines++;
+ break;
+ }
+ return p;
+}
+
+int
+rhinepnp(Ether *edev)
+{
+ Pcidev *p;
+ Ctlr *ctlr;
+ ulong port;
+
+ p = rhinematch(edev->port);
+ if (p == nil)
+ return -1;
+
+ port = p->mem[0].bar & ~1;
+
+ if ((ctlr = malloc(sizeof(Ctlr))) == nil) {
+ print("etherrhine: couldn't allocate memory for ctlr\n");
+ return -1;
+ }
+ memset(ctlr, 0, sizeof(Ctlr));
+ ctlr->txd = xspanalloc(sizeof(Desc) * Ntxd, 16, 0);
+ ctlr->rxd = xspanalloc(sizeof(Desc) * Nrxd, 16, 0);
+
+ ctlr->pci = p;
+ ctlr->port = port;
+
+ edev->ctlr = ctlr;
+ edev->port = ctlr->port;
+ edev->irq = p->intl;
+ edev->tbdf = p->tbdf;
+
+ init(edev);
+
+
+ edev->attach = attach;
+ edev->transmit = transmit;
+ edev->interrupt = interrupt;
+ edev->detach = detach;
+
+ return 0;
+}
diff --git a/os/boot/pc/fns.h b/os/boot/pc/fns.h
new file mode 100644
index 00000000..794fe592
--- /dev/null
+++ b/os/boot/pc/fns.h
@@ -0,0 +1,155 @@
+void aamloop(int);
+void addconf(char*, ...);
+Alarm* alarm(int, void (*)(Alarm*), void*);
+void alarminit(void);
+Block* allocb(int);
+void apminit(void);
+int bootpboot(int, char*, Boot*);
+int bootpass(Boot*, void*, int);
+void cancel(Alarm*);
+int cdinit(void);
+void check(char*);
+void cgascreenputs(char*, int);
+int cistrcmp(char*, char*);
+int cistrncmp(char*, char*, int);
+void changeconf(char*, ...);
+void checkalarms(void);
+void clockinit(void);
+void consdrain(void);
+void consinit(char*, char*);
+void consputs(char*, int);
+void delay(int);
+uchar* etheraddr(int);
+int etherinit(void);
+void etherinitdev(int, char*);
+void etherprintdevs(int);
+int etherrxflush(int);
+int etherrxpkt(int, Etherpkt*, int);
+int ethertxpkt(int, Etherpkt*, int, int);
+#define evenaddr(x) /* 386 doesn't care */
+int floppyboot(int, char*, Boot*);
+int floppyinit(void);
+void floppyinitdev(int, char*);
+void floppyprintdevs(int);
+void* floppygetfspart(int, char*, int);
+void freeb(Block*);
+char* getconf(char*);
+ulong getcr0(void);
+ulong getcr2(void);
+ulong getcr3(void);
+ulong getcr4(void);
+int getfields(char*, char**, int, char);
+int getstr(char*, char*, int, char*, int);
+int gunzip(uchar*, int, uchar*, int);
+void i8042a20(void);
+void i8042init(void);
+void i8042reset(void);
+void* ialloc(ulong, int);
+void idle(void);
+void ilock(Lock*);
+int inb(int);
+ushort ins(int);
+ulong inl(int);
+void insb(int, void*, int);
+void inss(int, void*, int);
+void insl(int, void*, int);
+void iunlock(Lock*);
+int isaconfig(char*, int, ISAConf*);
+void kbdinit(void);
+void kbdchar(int);
+void machinit(void);
+void meminit(ulong);
+void microdelay(int);
+void mmuinit(void);
+#define nelem(x) (sizeof(x)/sizeof(x[0]))
+char* nextelem(char*, char*);
+uchar nvramread(int);
+void outb(int, int);
+void outs(int, ushort);
+void outl(int, ulong);
+void outsb(int, void*, int);
+void outss(int, void*, int);
+void outsl(int, void*, int);
+void panic(char*, ...);
+int pcicfgr8(Pcidev*, int);
+int pcicfgr16(Pcidev*, int);
+int pcicfgr32(Pcidev*, int);
+void pcicfgw8(Pcidev*, int, int);
+void pcicfgw16(Pcidev*, int, int);
+void pcicfgw32(Pcidev*, int, int);
+void pcihinv(Pcidev*);
+Pcidev* pcimatch(Pcidev*, int, int);
+uchar pciintl(Pcidev *);
+uchar pciipin(Pcidev *, uchar);
+void pcireset(void);
+void pcisetbme(Pcidev*);
+int pcmcistuple(int, int, void*, int);
+int pcmspecial(char*, ISAConf*);
+void pcmspecialclose(int);
+void pcmunmap(int, PCMmap*);
+void ptcheck(char*);
+void putcr3(ulong);
+void putidt(Segdesc*, int);
+void* pxegetfspart(int, char*, int);
+void qinit(IOQ*);
+void readlsconf(void);
+void sdaddconf(int);
+int sdboot(int, char*, Boot*);
+void sdcheck(char*);
+void* sdgetfspart(int, char*, int);
+int sdinit(void);
+void sdinitdev(int, char*);
+void sdprintdevs(int);
+int sdsetpart(int, char*);
+void setvec(int, void (*)(Ureg*, void*), void*);
+int splhi(void);
+int spllo(void);
+void splx(int);
+void trapinit(void);
+void trapdisable(void);
+void trapenable(void);
+void uartdrain(void);
+void uartspecial(int, void (*)(int), int (*)(void), int);
+void uartputs(IOQ*, char*, int);
+ulong umbmalloc(ulong, int, int);
+void umbfree(ulong, int);
+ulong umbrwmalloc(ulong, int, int);
+void upafree(ulong, int);
+ulong upamalloc(ulong, int, int);
+void warp86(char*, ulong);
+void warp9(ulong);
+int x86cpuid(int*, int*);
+void* xspanalloc(ulong, int, ulong);
+
+#define malloc(n) ialloc(n, 0)
+#define mallocz(n, c) ialloc(n, 0)
+#define free(v) while(0)
+
+#define GSHORT(p) (((p)[1]<<8)|(p)[0])
+#define GLONG(p) ((GSHORT(p+2)<<16)|GSHORT(p))
+#define GLSHORT(p) (((p)[0]<<8)|(p)[1])
+#define GLLONG(p) (((ulong)GLSHORT(p)<<16)|GLSHORT(p+2))
+#define PLLONG(p,v) (p)[3]=(v);(p)[2]=(v)>>8;(p)[1]=(v)>>16;(p)[0]=(v)>>24
+
+#define KADDR(a) ((void*)((ulong)(a)|KZERO))
+#define PADDR(a) ((ulong)(a)&~0xF0000000)
+
+#define HOWMANY(x, y) (((x)+((y)-1))/(y))
+#define ROUNDUP(x, y) (HOWMANY((x), (y))*(y))
+
+
+#define xalloc(n) ialloc(n, 0)
+#define xfree(v) while(0)
+#define lock(l) if(l){/* nothing to do */;}else{/* nothing to do */;}
+#define unlock(l) if(l){/* nothing to do */;}else{/* nothing to do */;}
+
+int dmacount(int);
+int dmadone(int);
+void dmaend(int);
+void dmainit(int);
+long dmasetup(int, void*, long, int);
+
+extern int (*_pcmspecial)(char *, ISAConf *);
+extern void (*_pcmspecialclose)(int);
+extern void devi82365link(void);
+extern void devpccardlink(void);
diff --git a/os/boot/pc/fs.c b/os/boot/pc/fs.c
new file mode 100644
index 00000000..24b33c09
--- /dev/null
+++ b/os/boot/pc/fs.c
@@ -0,0 +1,94 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "fs.h"
+
+/*
+ * grab next element from a path, return the pointer to unprocessed portion of
+ * path.
+ */
+char *
+nextelem(char *path, char *elem)
+{
+ int i;
+
+ while(*path == '/')
+ path++;
+ if(*path==0 || *path==' ')
+ return 0;
+ for(i=0; *path!='\0' && *path!='/' && *path!=' '; i++){
+ if(i==NAMELEN){
+ print("name component too long\n");
+ return 0;
+ }
+ *elem++ = *path++;
+ }
+ *elem = '\0';
+ return path;
+}
+
+int
+fswalk(Fs *fs, char *path, File *f)
+{
+ char element[NAMELEN];
+
+ *f = fs->root;
+ if(BADPTR(fs->walk))
+ panic("fswalk bad pointer fs->walk");
+
+ f->path = path;
+ while(path = nextelem(path, element)){
+ switch(fs->walk(f, element)){
+ case -1:
+ return -1;
+ case 0:
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/*
+ * boot
+ */
+int
+fsboot(Fs *fs, char *path, Boot *b)
+{
+ File file;
+ long n;
+ static char buf[8192];
+
+ switch(fswalk(fs, path, &file)){
+ case -1:
+ print("error walking to %s\n", path);
+ return -1;
+ case 0:
+ print("%s not found\n", path);
+ return -1;
+ case 1:
+ print("found %s\n", path);
+ break;
+ }
+
+ while((n = fsread(&file, buf, sizeof buf)) > 0) {
+ if(bootpass(b, buf, n) != MORE)
+ break;
+ }
+
+ bootpass(b, nil, 0); /* tries boot */
+ return -1;
+}
+
+int
+fsread(File *file, void *a, long n)
+{
+ if(BADPTR(file->fs))
+ panic("bad pointer file->fs in fsread");
+ if(BADPTR(file->fs->read))
+ panic("bad pointer file->fs->read in fsread");
+ return file->fs->read(file, a, n);
+}
diff --git a/os/boot/pc/fs.h b/os/boot/pc/fs.h
new file mode 100644
index 00000000..1b53e929
--- /dev/null
+++ b/os/boot/pc/fs.h
@@ -0,0 +1,36 @@
+typedef struct File File;
+typedef struct Fs Fs;
+
+#include "dosfs.h"
+#include "kfs.h"
+
+struct File{
+ union{
+ Dosfile dos;
+ Kfsfile kfs;
+ int walked;
+ };
+ Fs *fs;
+ char *path;
+};
+
+struct Fs{
+ union {
+ Dos dos;
+ Kfs kfs;
+ };
+ int dev; /* device id */
+ long (*diskread)(Fs*, void*, long); /* disk read routine */
+ vlong (*diskseek)(Fs*, vlong); /* disk seek routine */
+ long (*read)(File*, void*, long);
+ int (*walk)(File*, char*);
+ File root;
+};
+
+extern int chatty;
+extern int dotini(Fs*);
+extern int fswalk(Fs*, char*, File*);
+extern int fsread(File*, void*, long);
+extern int fsboot(Fs*, char*, Boot*);
+
+#define BADPTR(x) ((ulong)x < 0x80000000)
diff --git a/os/boot/pc/getcallerpc.c b/os/boot/pc/getcallerpc.c
new file mode 100644
index 00000000..c14a2449
--- /dev/null
+++ b/os/boot/pc/getcallerpc.c
@@ -0,0 +1,8 @@
+#include "u.h"
+#include "lib.h"
+
+ulong
+getcallerpc(void *x)
+{
+ return (((ulong*)(x))[-1]);
+}
diff --git a/os/boot/pc/ilock.c b/os/boot/pc/ilock.c
new file mode 100644
index 00000000..977b9ad2
--- /dev/null
+++ b/os/boot/pc/ilock.c
@@ -0,0 +1,24 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+void
+ilock(Lock *lk)
+{
+ if(lk->locked != 0)
+ panic("ilock");
+ lk->spl = splhi();
+ lk->locked = 1;
+}
+
+void
+iunlock(Lock *lk)
+{
+ if(lk->locked != 1)
+ panic("iunlock");
+ lk->locked = 0;
+ splx(lk->spl);
+}
diff --git a/os/boot/pc/inflate.c b/os/boot/pc/inflate.c
new file mode 100644
index 00000000..262511a0
--- /dev/null
+++ b/os/boot/pc/inflate.c
@@ -0,0 +1,199 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include <flate.h>
+
+typedef struct Biobuf Biobuf;
+
+struct Biobuf
+{
+ uchar *bp;
+ uchar *p;
+ uchar *ep;
+};
+
+static int header(Biobuf*);
+static int trailer(Biobuf*, Biobuf*);
+static int getc(void*);
+static ulong offset(Biobuf*);
+static int crcwrite(void *out, void *buf, int n);
+static ulong get4(Biobuf *b);
+static ulong Boffset(Biobuf *bp);
+
+/* GZIP flags */
+enum {
+ Ftext= (1<<0),
+ Fhcrc= (1<<1),
+ Fextra= (1<<2),
+ Fname= (1<<3),
+ Fcomment= (1<<4),
+
+ GZCRCPOLY = 0xedb88320UL,
+};
+
+static ulong *crctab;
+static ulong crc;
+
+extern void diff(char*); //XXX
+int
+gunzip(uchar *out, int outn, uchar *in, int inn)
+{
+ Biobuf bin, bout;
+ int err;
+
+ crc = 0;
+ crctab = mkcrctab(GZCRCPOLY);
+ err = inflateinit();
+ if(err != FlateOk)
+ print("inflateinit failed: %s\n", flateerr(err));
+
+ bin.bp = bin.p = in;
+ bin.ep = in+inn;
+ bout.bp = bout.p = out;
+ bout.ep = out+outn;
+
+ err = header(&bin);
+ if(err != FlateOk)
+ return err;
+
+ err = inflate(&bout, crcwrite, &bin, getc);
+ if(err != FlateOk)
+ print("inflate failed: %s\n", flateerr(err));
+
+ err = trailer(&bout, &bin);
+ if(err != FlateOk)
+ return err;
+
+ return Boffset(&bout);
+}
+
+static int
+header(Biobuf *bin)
+{
+ int i, flag;
+
+ if(getc(bin) != 0x1f || getc(bin) != 0x8b){
+ print("bad magic\n");
+ return FlateCorrupted;
+ }
+ if(getc(bin) != 8){
+ print("unknown compression type\n");
+ return FlateCorrupted;
+ }
+
+ flag = getc(bin);
+
+ /* mod time */
+ get4(bin);
+
+ /* extra flags */
+ getc(bin);
+
+ /* OS type */
+ getc(bin);
+
+ if(flag & Fextra)
+ for(i=getc(bin); i>0; i--)
+ getc(bin);
+
+ /* name */
+ if(flag&Fname)
+ while(getc(bin) != 0)
+ ;
+
+ /* comment */
+ if(flag&Fcomment)
+ while(getc(bin) != 0)
+ ;
+
+ /* crc16 */
+ if(flag&Fhcrc) {
+ getc(bin);
+ getc(bin);
+ }
+
+ return FlateOk;
+}
+
+static int
+trailer(Biobuf *bout, Biobuf *bin)
+{
+ /* crc32 */
+ if(crc != get4(bin)){
+ print("crc mismatch\n");
+ return FlateCorrupted;
+ }
+
+ /* length */
+ if(get4(bin) != Boffset(bout)){
+ print("bad output len\n");
+ return FlateCorrupted;
+ }
+ return FlateOk;
+}
+
+static ulong
+get4(Biobuf *b)
+{
+ ulong v;
+ int i, c;
+
+ v = 0;
+ for(i = 0; i < 4; i++){
+ c = getc(b);
+ v |= c << (i * 8);
+ }
+ return v;
+}
+
+static int
+getc(void *in)
+{
+ Biobuf *bp = in;
+
+// if((bp->p - bp->bp) % 10000 == 0)
+// print(".");
+ if(bp->p >= bp->ep)
+ return -1;
+ return *bp->p++;
+}
+
+static ulong
+Boffset(Biobuf *bp)
+{
+ return bp->p - bp->bp;
+}
+
+static int
+crcwrite(void *out, void *buf, int n)
+{
+ Biobuf *bp;
+ int nn;
+
+ crc = blockcrc(crctab, crc, buf, n);
+ bp = out;
+ nn = n;
+ if(nn > bp->ep-bp->p)
+ nn = bp->ep-bp->p;
+ if(nn > 0)
+ memmove(bp->p, buf, nn);
+ bp->p += n;
+ return n;
+}
+
+#undef malloc
+#undef free
+
+void *
+malloc(ulong n)
+{
+ return ialloc(n, 8);
+}
+
+void
+free(void *)
+{
+}
diff --git a/os/boot/pc/io.h b/os/boot/pc/io.h
new file mode 100644
index 00000000..67219ddd
--- /dev/null
+++ b/os/boot/pc/io.h
@@ -0,0 +1,201 @@
+/*
+ * programmable interrupt vectors (for the 8259's)
+ */
+enum
+{
+ Bptvec= 3, /* breakpoints */
+ Mathemuvec= 7, /* math coprocessor emulation interrupt */
+ Mathovervec= 9, /* math coprocessor overrun interrupt */
+ Matherr1vec= 16, /* math coprocessor error interrupt */
+ Faultvec= 14, /* page fault */
+
+ Syscallvec= 64,
+
+ VectorPIC = 24, /* external [A]PIC interrupts */
+ VectorCLOCK = VectorPIC+0,
+ VectorKBD = VectorPIC+1,
+ VectorUART1 = VectorPIC+3,
+ VectorUART0 = VectorPIC+4,
+ VectorPCMCIA = VectorPIC+5,
+ VectorFLOPPY = VectorPIC+6,
+ VectorLPT = VectorPIC+7,
+ VectorIRQ7 = VectorPIC+7,
+ VectorAUX = VectorPIC+12, /* PS/2 port */
+ VectorIRQ13 = VectorPIC+13, /* coprocessor on x386 */
+ VectorATA0 = VectorPIC+14,
+ VectorATA1 = VectorPIC+15,
+ MaxVectorPIC = VectorPIC+15,
+};
+
+enum {
+ BusCBUS = 0, /* Corollary CBUS */
+ BusCBUSII, /* Corollary CBUS II */
+ BusEISA, /* Extended ISA */
+ BusFUTURE, /* IEEE Futurebus */
+ BusINTERN, /* Internal bus */
+ BusISA, /* Industry Standard Architecture */
+ BusMBI, /* Multibus I */
+ BusMBII, /* Multibus II */
+ BusMCA, /* Micro Channel Architecture */
+ BusMPI, /* MPI */
+ BusMPSA, /* MPSA */
+ BusNUBUS, /* Apple Macintosh NuBus */
+ BusPCI, /* Peripheral Component Interconnect */
+ BusPCMCIA, /* PC Memory Card International Association */
+ BusTC, /* DEC TurboChannel */
+ BusVL, /* VESA Local bus */
+ BusVME, /* VMEbus */
+ BusXPRESS, /* Express System Bus */
+};
+
+#define MKBUS(t,b,d,f) (((t)<<24)|(((b)&0xFF)<<16)|(((d)&0x1F)<<11)|(((f)&0x07)<<8))
+#define BUSFNO(tbdf) (((tbdf)>>8)&0x07)
+#define BUSDNO(tbdf) (((tbdf)>>11)&0x1F)
+#define BUSBNO(tbdf) (((tbdf)>>16)&0xFF)
+#define BUSTYPE(tbdf) ((tbdf)>>24)
+#define BUSBDF(tbdf) ((tbdf)&0x00FFFF00)
+#define BUSUNKNOWN (-1)
+
+enum {
+ MaxEISA = 16,
+ CfgEISA = 0xC80,
+};
+
+/*
+ * PCI support code.
+ */
+enum { /* type 0 and type 1 pre-defined header */
+ PciVID = 0x00, /* vendor ID */
+ PciDID = 0x02, /* device ID */
+ PciPCR = 0x04, /* command */
+ PciPSR = 0x06, /* status */
+ PciRID = 0x08, /* revision ID */
+ PciCCRp = 0x09, /* programming interface class code */
+ PciCCRu = 0x0A, /* sub-class code */
+ PciCCRb = 0x0B, /* base class code */
+ PciCLS = 0x0C, /* cache line size */
+ PciLTR = 0x0D, /* latency timer */
+ PciHDT = 0x0E, /* header type */
+ PciBST = 0x0F, /* BIST */
+
+ PciBAR0 = 0x10, /* base address */
+ PciBAR1 = 0x14,
+
+ PciINTL = 0x3C, /* interrupt line */
+ PciINTP = 0x3D, /* interrupt pin */
+};
+
+enum { /* type 0 pre-defined header */
+ PciBAR2 = 0x18,
+ PciBAR3 = 0x1C,
+ PciBAR4 = 0x20,
+ PciBAR5 = 0x24,
+ PciCIS = 0x28, /* cardbus CIS pointer */
+ PciSVID = 0x2C, /* subsystem vendor ID */
+ PciSID = 0x2E, /* cardbus CIS pointer */
+ PciEBAR0 = 0x30, /* expansion ROM base address */
+ PciMGNT = 0x3E, /* burst period length */
+ PciMLT = 0x3F, /* maximum latency between bursts */
+};
+
+enum { /* type 1 pre-defined header */
+ PciPBN = 0x18, /* primary bus number */
+ PciSBN = 0x19, /* secondary bus number */
+ PciUBN = 0x1A, /* subordinate bus number */
+ PciSLTR = 0x1B, /* secondary latency timer */
+ PciIBR = 0x1C, /* I/O base */
+ PciILR = 0x1D, /* I/O limit */
+ PciSPSR = 0x1E, /* secondary status */
+ PciMBR = 0x20, /* memory base */
+ PciMLR = 0x22, /* memory limit */
+ PciPMBR = 0x24, /* prefetchable memory base */
+ PciPMLR = 0x26, /* prefetchable memory limit */
+ PciPUBR = 0x28, /* prefetchable base upper 32 bits */
+ PciPULR = 0x2C, /* prefetchable limit upper 32 bits */
+ PciIUBR = 0x30, /* I/O base upper 16 bits */
+ PciIULR = 0x32, /* I/O limit upper 16 bits */
+ PciEBAR1 = 0x28, /* expansion ROM base address */
+ PciBCR = 0x3E, /* bridge control register */
+};
+
+enum { /* type 2 pre-defined header */
+ PciCBExCA = 0x10,
+ PciCBSPSR = 0x16,
+ PciCBPBN = 0x18, /* primary bus number */
+ PciCBSBN = 0x19, /* secondary bus number */
+ PciCBUBN = 0x1A, /* subordinate bus number */
+ PciCBSLTR = 0x1B, /* secondary latency timer */
+ PciCBMBR0 = 0x1C,
+ PciCBMLR0 = 0x20,
+ PciCBMBR1 = 0x24,
+ PciCBMLR1 = 0x28,
+ PciCBIBR0 = 0x2C, /* I/O base */
+ PciCBILR0 = 0x30, /* I/O limit */
+ PciCBIBR1 = 0x34, /* I/O base */
+ PciCBILR1 = 0x38, /* I/O limit */
+ PciCBBCTL = 0x3E, /* Bridhe control */
+ PciCBSVID = 0x40, /* subsystem vendor ID */
+ PciCBSID = 0x42, /* subsystem ID */
+ PciCBLMBAR = 0x44, /* legacy mode base address */
+};
+
+typedef struct Pcisiz Pcisiz;
+struct Pcisiz
+{
+ Pcidev* dev;
+ int siz;
+ int bar;
+};
+
+typedef struct Pcidev Pcidev;
+typedef struct Pcidev {
+ int tbdf; /* type+bus+device+function */
+ ushort vid; /* vendor ID */
+ ushort did; /* device ID */
+
+ uchar rid;
+ uchar ccrp;
+ uchar ccru;
+ uchar ccrb;
+
+ struct {
+ ulong bar; /* base address */
+ int size;
+ } mem[6];
+
+ struct {
+ ulong bar;
+ int size;
+ } rom;
+ uchar intl; /* interrupt line */
+
+ Pcidev* list;
+ Pcidev* bridge; /* down a bus */
+ Pcidev* link; /* next device on this bno */
+ struct {
+ ulong bar;
+ int size;
+ } ioa, mema;
+
+ ulong pcr;
+};
+
+#define PCIWINDOW 0
+#define PCIWADDR(va) (PADDR(va)+PCIWINDOW)
+#define ISAWINDOW 0
+#define ISAWADDR(va) (PADDR(va)+ISAWINDOW)
+
+/*
+ * PCMCIA support code.
+ */
+/*
+ * Map between ISA memory space and PCMCIA card memory space.
+ */
+struct PCMmap {
+ ulong ca; /* card address */
+ ulong cea; /* card end address */
+ ulong isa; /* ISA address */
+ int len; /* length of the ISA area */
+ int attr; /* attribute memory */
+ int ref;
+};
diff --git a/os/boot/pc/ip.h b/os/boot/pc/ip.h
new file mode 100644
index 00000000..6dfb7cdd
--- /dev/null
+++ b/os/boot/pc/ip.h
@@ -0,0 +1,100 @@
+typedef struct Udphdr Udphdr;
+struct Udphdr
+{
+ uchar d[6]; /* Ethernet destination */
+ uchar s[6]; /* Ethernet source */
+ uchar type[2]; /* Ethernet packet type */
+
+ uchar vihl; /* Version and header length */
+ uchar tos; /* Type of service */
+ uchar length[2]; /* packet length */
+ uchar id[2]; /* Identification */
+ uchar frag[2]; /* Fragment information */
+
+ /* Udp pseudo ip really starts here */
+ uchar ttl;
+ uchar udpproto; /* Protocol */
+ uchar udpplen[2]; /* Header plus data length */
+ uchar udpsrc[4]; /* Ip source */
+ uchar udpdst[4]; /* Ip destination */
+ uchar udpsport[2]; /* Source port */
+ uchar udpdport[2]; /* Destination port */
+ uchar udplen[2]; /* data length */
+ uchar udpcksum[2]; /* Checksum */
+};
+
+typedef struct Etherhdr Etherhdr;
+struct Etherhdr
+{
+ uchar d[6];
+ uchar s[6];
+ uchar type[2];
+
+ /* Now we have the ip fields */
+ uchar vihl; /* Version and header length */
+ uchar tos; /* Type of service */
+ uchar length[2]; /* packet length */
+ uchar id[2]; /* Identification */
+ uchar frag[2]; /* Fragment information */
+ uchar ttl; /* Time to live */
+ uchar proto; /* Protocol */
+ uchar cksum[2]; /* Header checksum */
+ uchar src[4]; /* Ip source */
+ uchar dst[4]; /* Ip destination */
+};
+
+enum
+{
+ IP_VER = 0x40,
+ IP_HLEN = 0x05,
+ UDP_EHSIZE = 22,
+ UDP_PHDRSIZE = 12,
+ UDP_HDRSIZE = 20,
+ ETHER_HDR = 14,
+ IP_UDPPROTO = 17,
+ ET_IP = 0x800,
+ Bcastip = 0xffffffff,
+ BPportsrc = 68,
+ BPportdst = 67,
+ TFTPport = 69,
+ Timeout = 5000, /* milliseconds */
+ Bootrequest = 1,
+ Bootreply = 2,
+ Tftp_READ = 1,
+ Tftp_WRITE = 2,
+ Tftp_DATA = 3,
+ Tftp_ACK = 4,
+ Tftp_ERROR = 5,
+ Segsize = 512,
+ TFTPSZ = Segsize+10,
+};
+
+typedef struct Bootp Bootp;
+struct Bootp
+{
+ uchar op; /* opcode */
+ uchar htype; /* hardware type */
+ uchar hlen; /* hardware address len */
+ uchar hops; /* hops */
+ uchar xid[4]; /* a random number */
+ uchar secs[2]; /* elapsed since client started booting */
+ uchar pad[2];
+ uchar ciaddr[4]; /* client IP address (client tells server) */
+ uchar yiaddr[4]; /* client IP address (server tells client) */
+ uchar siaddr[4]; /* server IP address */
+ uchar giaddr[4]; /* gateway IP address */
+ uchar chaddr[16]; /* client hardware address */
+ char sname[64]; /* server host name (optional) */
+ char file[128]; /* boot file name */
+ char vend[128]; /* vendor-specific goo */
+};
+
+typedef struct Netaddr Netaddr;
+struct Netaddr
+{
+ ulong ip;
+ ushort port;
+ char ea[Eaddrlen];
+};
+
+extern int eipfmt(Fmt*);
diff --git a/os/boot/pc/kbd.c b/os/boot/pc/kbd.c
new file mode 100644
index 00000000..a572866f
--- /dev/null
+++ b/os/boot/pc/kbd.c
@@ -0,0 +1,489 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+enum {
+ Data= 0x60, /* data port */
+
+ Status= 0x64, /* status port */
+ Inready= 0x01, /* input character ready */
+ Outbusy= 0x02, /* output busy */
+ Sysflag= 0x04, /* system flag */
+ Cmddata= 0x08, /* cmd==0, data==1 */
+ Inhibit= 0x10, /* keyboard/mouse inhibited */
+ Minready= 0x20, /* mouse character ready */
+ Rtimeout= 0x40, /* general timeout */
+ Parity= 0x80,
+
+ Cmd= 0x64, /* command port (write only) */
+
+ Spec= 0x80,
+
+ PF= Spec|0x20, /* num pad function key */
+ View= Spec|0x00, /* view (shift window up) */
+ KF= Spec|0x40, /* function key */
+ Shift= Spec|0x60,
+ Break= Spec|0x61,
+ Ctrl= Spec|0x62,
+ Latin= Spec|0x63,
+ Caps= Spec|0x64,
+ Num= Spec|0x65,
+ No= Spec|0x7F, /* no mapping */
+
+ Home= KF|13,
+ Up= KF|14,
+ Pgup= KF|15,
+ Print= KF|16,
+ Left= View,
+ Right= View,
+ End= '\r',
+ Down= View,
+ Pgdown= View,
+ Ins= KF|20,
+ Del= 0x7F,
+};
+
+uchar kbtab[] =
+{
+[0x00] No, 0x1b, '1', '2', '3', '4', '5', '6',
+[0x08] '7', '8', '9', '0', '-', '=', '\b', '\t',
+[0x10] 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
+[0x18] 'o', 'p', '[', ']', '\n', Ctrl, 'a', 's',
+[0x20] 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';',
+[0x28] '\'', '`', Shift, '\\', 'z', 'x', 'c', 'v',
+[0x30] 'b', 'n', 'm', ',', '.', '/', Shift, No,
+[0x38] Latin, ' ', Caps, KF|1, KF|2, KF|3, KF|4, KF|5,
+[0x40] KF|6, KF|7, KF|8, KF|9, KF|10, Num, KF|12, Home,
+[0x48] No, No, No, No, No, No, No, No,
+[0x50] No, No, No, No, No, No, No, KF|11,
+[0x58] KF|12, No, No, No, No, No, No, No,
+};
+
+uchar kbtabshift[] =
+{
+[0x00] No, 0x1b, '!', '@', '#', '$', '%', '^',
+[0x08] '&', '*', '(', ')', '_', '+', '\b', '\t',
+[0x10] 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I',
+[0x18] 'O', 'P', '{', '}', '\n', Ctrl, 'A', 'S',
+[0x20] 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':',
+[0x28] '"', '~', Shift, '|', 'Z', 'X', 'C', 'V',
+[0x30] 'B', 'N', 'M', '<', '>', '?', Shift, No,
+[0x38] Latin, ' ', Caps, KF|1, KF|2, KF|3, KF|4, KF|5,
+[0x40] KF|6, KF|7, KF|8, KF|9, KF|10, Num, KF|12, Home,
+[0x48] No, No, No, No, No, No, No, No,
+[0x50] No, No, No, No, No, No, No, KF|11,
+[0x58] KF|12, No, No, No, No, No, No, No,
+};
+
+uchar kbtabesc1[] =
+{
+[0x00] No, No, No, No, No, No, No, No,
+[0x08] No, No, No, No, No, No, No, No,
+[0x10] No, No, No, No, No, No, No, No,
+[0x18] No, No, No, No, No, Ctrl, No, No,
+[0x20] No, No, No, No, No, No, No, No,
+[0x28] No, No, No, No, No, No, No, No,
+[0x30] No, No, No, No, No, No, No, Print,
+[0x38] Latin, No, No, No, No, No, No, No,
+[0x40] No, No, No, No, No, No, Break, Home,
+[0x48] Up, Pgup, No, Down, No, Right, No, End,
+[0x50] Left, Pgdown, Ins, Del, No, No, No, No,
+[0x58] No, No, No, No, No, No, No, No,
+};
+
+struct latin
+{
+ uchar l;
+ char c[2];
+}latintab[] = {
+ L'Ā”', "!!", /* spanish initial ! */
+ L'Ā¢', "c|", /* cent */
+ L'Ā¢', "c$", /* cent */
+ L'Ā£', "l$", /* pound sterling */
+ L'¤', "g$", /* general currency */
+ L'Ā„', "y$", /* yen */
+ L'Ā„', "j$", /* yen */
+ L'¦', "||", /* broken vertical bar */
+ L'§', "SS", /* section symbol */
+ L'ĀØ', "\"\"", /* dieresis */
+ L'Ā©', "cr", /* copyright */
+ L'Ā©', "cO", /* copyright */
+ L'ĀŖ', "sa", /* super a, feminine ordinal */
+ L'Ā«', "<<", /* left angle quotation */
+ L'¬', "no", /* not sign, hooked overbar */
+ L'Ā­', "--", /* soft hyphen */
+ L'Ā®', "rg", /* registered trademark */
+ L'ĀÆ', "__", /* macron */
+ L'°', "s0", /* degree (sup o) */
+ L'±', "+-", /* plus-minus */
+ L'²', "s2", /* sup 2 */
+ L'³', "s3", /* sup 3 */
+ L'Ā“', "''", /* grave accent */
+ L'µ', "mu", /* mu */
+ L'¶', "pg", /* paragraph (pilcrow) */
+ L'Ā·', "..", /* centered . */
+ L'Āø', ",,", /* cedilla */
+ L'¹', "s1", /* sup 1 */
+ L'Āŗ', "so", /* sup o */
+ L'Ā»', ">>", /* right angle quotation */
+ L'¼', "14", /* 1/4 */
+ L'½', "12", /* 1/2 */
+ L'¾', "34", /* 3/4 */
+ L'Āæ', "??", /* spanish initial ? */
+ L'ƀ', "A`", /* A grave */
+ L'Ɓ', "A'", /* A acute */
+ L'Ƃ', "A^", /* A circumflex */
+ L'ƃ', "A~", /* A tilde */
+ L'Ƅ', "A\"", /* A dieresis */
+ L'Ƅ', "A:", /* A dieresis */
+ L'ƅ', "Ao", /* A circle */
+ L'ƅ', "AO", /* A circle */
+ L'Ɔ', "Ae", /* AE ligature */
+ L'Ɔ', "AE", /* AE ligature */
+ L'Ƈ', "C,", /* C cedilla */
+ L'ƈ', "E`", /* E grave */
+ L'Ɖ', "E'", /* E acute */
+ L'Ê', "E^", /* E circumflex */
+ L'Ƌ', "E\"", /* E dieresis */
+ L'Ƌ', "E:", /* E dieresis */
+ L'Ì', "I`", /* I grave */
+ L'ƍ', "I'", /* I acute */
+ L'Ǝ', "I^", /* I circumflex */
+ L'Ə', "I\"", /* I dieresis */
+ L'Ə', "I:", /* I dieresis */
+ L'Ɛ', "D-", /* Eth */
+ L'Ƒ', "N~", /* N tilde */
+ L'ƒ', "O`", /* O grave */
+ L'Ɠ', "O'", /* O acute */
+ L'Ɣ', "O^", /* O circumflex */
+ L'ƕ', "O~", /* O tilde */
+ L'Ɩ', "O\"", /* O dieresis */
+ L'Ɩ', "O:", /* O dieresis */
+ L'Ɩ', "OE", /* O dieresis */
+ L'Ɩ', "Oe", /* O dieresis */
+ L'Ɨ', "xx", /* times sign */
+ L'Ƙ', "O/", /* O slash */
+ L'ƙ', "U`", /* U grave */
+ L'Ú', "U'", /* U acute */
+ L'ƛ', "U^", /* U circumflex */
+ L'Ü', "U\"", /* U dieresis */
+ L'Ü', "U:", /* U dieresis */
+ L'Ü', "UE", /* U dieresis */
+ L'Ü', "Ue", /* U dieresis */
+ L'Ɲ', "Y'", /* Y acute */
+ L'ƞ', "P|", /* Thorn */
+ L'ƞ', "Th", /* Thorn */
+ L'ƞ', "TH", /* Thorn */
+ L'ß', "ss", /* sharp s */
+ L'Ć ', "a`", /* a grave */
+ L'Ć”', "a'", /* a acute */
+ L'Ć¢', "a^", /* a circumflex */
+ L'Ć£', "a~", /* a tilde */
+ L'Ƥ', "a\"", /* a dieresis */
+ L'Ƥ', "a:", /* a dieresis */
+ L'Ć„', "ao", /* a circle */
+ L'Ʀ', "ae", /* ae ligature */
+ L'Ƨ', "c,", /* c cedilla */
+ L'ĆØ', "e`", /* e grave */
+ L'Ć©', "e'", /* e acute */
+ L'ĆŖ', "e^", /* e circumflex */
+ L'Ć«', "e\"", /* e dieresis */
+ L'Ć«', "e:", /* e dieresis */
+ L'Ƭ', "i`", /* i grave */
+ L'Ć­', "i'", /* i acute */
+ L'Ć®', "i^", /* i circumflex */
+ L'ĆÆ', "i\"", /* i dieresis */
+ L'ĆÆ', "i:", /* i dieresis */
+ L'ư', "d-", /* eth */
+ L'Ʊ', "n~", /* n tilde */
+ L'ò', "o`", /* o grave */
+ L'ó', "o'", /* o acute */
+ L'Ć“', "o^", /* o circumflex */
+ L'Ƶ', "o~", /* o tilde */
+ L'ƶ', "o\"", /* o dieresis */
+ L'ƶ', "o:", /* o dieresis */
+ L'ƶ', "oe", /* o dieresis */
+ L'Ć·', "-:", /* divide sign */
+ L'Ćø', "o/", /* o slash */
+ L'ù', "u`", /* u grave */
+ L'Ćŗ', "u'", /* u acute */
+ L'Ć»', "u^", /* u circumflex */
+ L'ü', "u\"", /* u dieresis */
+ L'ü', "u:", /* u dieresis */
+ L'ü', "ue", /* u dieresis */
+ L'ý', "y'", /* y acute */
+ L'þ', "th", /* thorn */
+ L'þ', "p|", /* thorn */
+ L'Ćæ', "y\"", /* y dieresis */
+ L'Ćæ', "y:", /* y dieresis */
+ 0, 0,
+};
+
+enum
+{
+ /* controller command byte */
+ Cscs1= (1<<6), /* scan code set 1 */
+ Cmousedis= (1<<5), /* mouse disable */
+ Ckbddis= (1<<4), /* kbd disable */
+ Csf= (1<<2), /* system flag */
+ Cmouseint= (1<<1), /* mouse interrupt enable */
+ Ckbdint= (1<<0), /* kbd interrupt enable */
+};
+
+static uchar ccc;
+
+int
+latin1(int k1, int k2)
+{
+ struct latin *l;
+
+ for(l=latintab; l->l; l++)
+ if(k1==l->c[0] && k2==l->c[1])
+ return l->l;
+ return 0;
+}
+
+/*
+ * wait for output no longer busy
+ */
+static int
+outready(void)
+{
+ int tries;
+
+ for(tries = 0; (inb(Status) & Outbusy); tries++){
+ if(tries > 500)
+ return -1;
+ delay(2);
+ }
+ return 0;
+}
+
+/*
+ * wait for input
+ */
+static int
+inready(void)
+{
+ int tries;
+
+ for(tries = 0; !(inb(Status) & Inready); tries++){
+ if(tries > 500)
+ return -1;
+ delay(2);
+ }
+ return 0;
+}
+
+/*
+ * ask 8042 to enable the use of address bit 20
+ */
+void
+i8042a20(void)
+{
+ outready();
+ outb(Cmd, 0xD1);
+ outready();
+ outb(Data, 0xDF);
+ outready();
+}
+
+/*
+ * ask 8042 to reset the machine
+ */
+void
+i8042reset(void)
+{
+ int i, x;
+#ifdef notdef
+ ushort *s = (ushort*)(KZERO|0x472);
+
+ *s = 0x1234; /* BIOS warm-boot flag */
+#endif /* notdef */
+
+ outready();
+ outb(Cmd, 0xFE); /* pulse reset line (means resend on AT&T machines) */
+ outready();
+
+ /*
+ * Pulse it by hand (old somewhat reliable)
+ */
+ x = 0xDF;
+ for(i = 0; i < 5; i++){
+ x ^= 1;
+ outready();
+ outb(Cmd, 0xD1);
+ outready();
+ outb(Data, x); /* toggle reset */
+ delay(100);
+ }
+}
+
+/*
+ * keyboard interrupt
+ */
+static void
+i8042intr(Ureg*, void*)
+{
+ int s, c;
+ static int esc1, esc2;
+ static int alt, caps, ctl, num, shift;
+ static int lstate, k1, k2;
+ int keyup;
+
+ /*
+ * get status
+ */
+ s = inb(Status);
+ if(!(s&Inready))
+ return;
+
+ /*
+ * get the character
+ */
+ c = inb(Data);
+
+ /*
+ * if it's the aux port...
+ */
+ if(s & Minready)
+ return;
+
+ /*
+ * e0's is the first of a 2 character sequence
+ */
+ if(c == 0xe0){
+ esc1 = 1;
+ return;
+ } else if(c == 0xe1){
+ esc2 = 2;
+ return;
+ }
+
+ keyup = c&0x80;
+ c &= 0x7f;
+ if(c > sizeof kbtab){
+ c |= keyup;
+ if(c != 0xFF) /* these come fairly often: CAPSLOCK U Y */
+ print("unknown key %ux\n", c);
+ return;
+ }
+
+ if(esc1){
+ c = kbtabesc1[c];
+ esc1 = 0;
+ } else if(esc2){
+ esc2--;
+ return;
+ } else if(shift)
+ c = kbtabshift[c];
+ else
+ c = kbtab[c];
+
+ if(caps && c<='z' && c>='a')
+ c += 'A' - 'a';
+
+ /*
+ * keyup only important for shifts
+ */
+ if(keyup){
+ switch(c){
+ case Latin:
+ alt = 0;
+ break;
+ case Shift:
+ shift = 0;
+ break;
+ case Ctrl:
+ ctl = 0;
+ break;
+ }
+ return;
+ }
+
+ /*
+ * normal character
+ */
+ if(!(c & Spec)){
+ if(ctl){
+ if(alt && c == Del)
+ warp86("\nCtrl-Alt-Del\n", 0);
+ c &= 0x1f;
+ }
+ switch(lstate){
+ case 1:
+ k1 = c;
+ lstate = 2;
+ return;
+ case 2:
+ k2 = c;
+ lstate = 0;
+ c = latin1(k1, k2);
+ if(c == 0){
+ kbdchar(k1);
+ c = k2;
+ }
+ /* fall through */
+ default:
+ break;
+ }
+ } else {
+ switch(c){
+ case Caps:
+ caps ^= 1;
+ return;
+ case Num:
+ num ^= 1;
+ return;
+ case Shift:
+ shift = 1;
+ return;
+ case Latin:
+ alt = 1;
+ lstate = 1;
+ return;
+ case Ctrl:
+ ctl = 1;
+ return;
+ }
+ }
+ kbdchar(c);
+}
+
+static char *initfailed = "kbd init failed\n";
+
+void
+i8042init(void)
+{
+ int c;
+
+ /* wait for a quiescent controller */
+ while((c = inb(Status)) & (Outbusy | Inready))
+ if(c & Inready)
+ inb(Data);
+
+ /* get current controller command byte */
+ outb(Cmd, 0x20);
+ if(inready() < 0){
+ print("kbdinit: can't read ccc\n");
+ ccc = 0;
+ } else
+ ccc = inb(Data);
+
+ /* enable kbd xfers and interrupts */
+ ccc &= ~Ckbddis;
+ ccc |= Csf | Ckbdint | Cscs1;
+ if(outready() < 0)
+ print(initfailed);
+ outb(Cmd, 0x60);
+ if(outready() < 0)
+ print(initfailed);
+ outb(Data, ccc);
+ if(outready() < 0)
+ print(initfailed);
+
+ setvec(VectorKBD, i8042intr, 0);
+}
diff --git a/os/boot/pc/kfs.h b/os/boot/pc/kfs.h
new file mode 100644
index 00000000..a08ad37a
--- /dev/null
+++ b/os/boot/pc/kfs.h
@@ -0,0 +1,57 @@
+typedef struct Qid9p1 Qid9p1;
+typedef struct Dentry Dentry;
+typedef struct Kfsfile Kfsfile;
+typedef struct Kfs Kfs;
+
+/* DONT TOUCH, this is the disk structure */
+struct Qid9p1
+{
+ long path;
+ long version;
+};
+
+//#define NAMELEN 28 /* size of names */
+#define NDBLOCK 6 /* number of direct blocks in Dentry */
+
+/* DONT TOUCH, this is the disk structure */
+struct Dentry
+{
+ char name[NAMELEN];
+ short uid;
+ short gid;
+ ushort mode;
+/*
+ #define DALLOC 0x8000
+ #define DDIR 0x4000
+ #define DAPND 0x2000
+ #define DLOCK 0x1000
+ #define DREAD 0x4
+ #define DWRITE 0x2
+ #define DEXEC 0x1
+*/
+ Qid9p1 qid;
+ long size;
+ long dblock[NDBLOCK];
+ long iblock;
+ long diblock;
+ long atime;
+ long mtime;
+};
+
+struct Kfsfile
+{
+ Dentry;
+ long off;
+};
+
+struct Kfs
+{
+ int RBUFSIZE;
+ int BUFSIZE;
+ int DIRPERBUF;
+ int INDPERBUF;
+ int INDPERBUF2;
+};
+
+extern int kfsinit(Fs*);
+
diff --git a/os/boot/pc/kfsboot.c b/os/boot/pc/kfsboot.c
new file mode 100644
index 00000000..a99d226d
--- /dev/null
+++ b/os/boot/pc/kfsboot.c
@@ -0,0 +1,256 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "fs.h"
+
+typedef struct Tag Tag;
+
+/*
+ * tags on block
+ */
+enum
+{
+ Tnone = 0,
+ Tsuper, /* the super block */
+ Tdir, /* directory contents */
+ Tind1, /* points to blocks */
+ Tind2, /* points to Tind1 */
+ Tfile, /* file contents */
+ Tfree, /* in free list */
+ Tbuck, /* cache fs bucket */
+ Tvirgo, /* fake worm virgin bits */
+ Tcache, /* cw cache things */
+ MAXTAG
+};
+
+#define QPDIR 0x80000000L
+#define QPNONE 0
+#define QPROOT 1
+#define QPSUPER 2
+
+/* DONT TOUCH, this is the disk structure */
+struct Tag
+{
+ short pad;
+ short tag;
+ long path;
+};
+
+static int thisblock = -1;
+static Fs *thisfs;
+static uchar *block;
+
+/*
+ * we end up reading 2x or 3x the number of blocks we need to read.
+ * this is okay because we need to read so few. if it wasn't okay, we could
+ * have getblock return a pointer to a block, and keep a cache of the last
+ * three read blocks. that would get us down to the minimum.
+ * but this is fine.
+ */
+static int
+getblock(Fs *fs, ulong n)
+{
+ if(!block)
+ block = malloc(16384);
+
+ if(thisblock == n && thisfs == fs)
+ return 0;
+ thisblock = -1;
+ if(fs->diskseek(fs, (vlong)n*fs->kfs.RBUFSIZE) < 0)
+ return -1;
+ if(fs->diskread(fs, block, fs->kfs.RBUFSIZE) != fs->kfs.RBUFSIZE)
+ return -1;
+ thisblock = n;
+ thisfs = fs;
+
+ return 1;
+}
+
+static int
+checktag(Fs *fs, uchar *block, int tag, long qpath)
+{
+ Tag *t;
+
+ t = (Tag*)(block+fs->kfs.BUFSIZE);
+ if(t->tag != tag)
+ return -1;
+ if(qpath != QPNONE && (qpath&~QPDIR) != t->path)
+ return -1;
+ return 1;
+}
+
+static int
+getblocktag(Fs *fs, ulong n, int tag, long qpath)
+{
+ if(getblock(fs, n) < 0 || checktag(fs, block, tag, qpath) < 0)
+ return -1;
+ return 1;
+}
+
+static int
+readinfo(Fs *fs)
+{
+ fs->kfs.RBUFSIZE = 512;
+ if(getblock(fs, 0) < 0)
+ return -1;
+
+ if(memcmp(block+256, "kfs wren device\n", 16) != 0)
+ return -1;
+
+ fs->kfs.RBUFSIZE = atoi((char*)block+256+16);
+ if(!fs->kfs.RBUFSIZE || (fs->kfs.RBUFSIZE&(fs->kfs.RBUFSIZE-1)))
+ return -1;
+
+ fs->kfs.BUFSIZE = fs->kfs.RBUFSIZE - sizeof(Tag);
+ fs->kfs.DIRPERBUF = fs->kfs.BUFSIZE / sizeof(Dentry);
+ fs->kfs.INDPERBUF = fs->kfs.BUFSIZE / sizeof(long);
+ fs->kfs.INDPERBUF2 = fs->kfs.INDPERBUF * fs->kfs.INDPERBUF;
+
+ return 1;
+}
+
+static int
+readroot(Fs *fs, Dentry *d)
+{
+ Dentry *d2;
+
+ if(getblocktag(fs, 2, Tdir, QPROOT) < 0)
+ return -1;
+ d2 = (Dentry*)block;
+ if(strcmp(d2->name, "/") != 0)
+ return -1;
+ *d = *(Dentry*)block;
+ return 1;
+}
+
+static long
+indfetch(Fs *fs, long addr, long off, int tag, long path)
+{
+ if(getblocktag(fs, addr, tag, path) < 0)
+ return -1;
+ return ((long*)block)[off];
+}
+
+static long
+rel2abs(Fs *fs, Dentry *d, long a)
+{
+ long addr;
+
+ if(a < NDBLOCK)
+ return d->dblock[a];
+ a -= NDBLOCK;
+ if(a < fs->kfs.INDPERBUF){
+ if(d->iblock == 0)
+ return 0;
+ addr = indfetch(fs, d->iblock, a, Tind1, d->qid.path);
+ if(addr == 0)
+ print("rel2abs indfetch 0 %s %ld\n", d->name, a);
+ return addr;
+ }
+ a -= fs->kfs.INDPERBUF;
+ if(a < fs->kfs.INDPERBUF2){
+ if(d->diblock == 0)
+ return 0;
+ addr = indfetch(fs, d->diblock, a/fs->kfs.INDPERBUF, Tind2, d->qid.path);
+ if(addr == 0){
+ print("rel2abs indfetch 0 %s %ld\n", d->name, a/fs->kfs.INDPERBUF);
+ return 0;
+ }
+ addr = indfetch(fs, addr, a%fs->kfs.INDPERBUF, Tind1, d->qid.path);
+ return addr;
+ }
+ print("rel2abs trip ind %s %ld\n", d->name, a);
+ return -1;
+}
+
+static int
+readdentry(Fs *fs, Dentry *d, int n, Dentry *e)
+{
+ long addr, m;
+
+ m = n/fs->kfs.DIRPERBUF;
+ if((addr = rel2abs(fs, d, m)) <= 0)
+ return addr;
+ if(getblocktag(fs, addr, Tdir, d->qid.path) < 0)
+ return -1;
+ *e = *(Dentry*)(block+(n%fs->kfs.DIRPERBUF)*sizeof(Dentry));
+ return 1;
+}
+
+static int
+getdatablock(Fs *fs, Dentry *d, long a)
+{
+ long addr;
+
+ if((addr = rel2abs(fs, d, a)) == 0)
+ return -1;
+ return getblocktag(fs, addr, Tfile, QPNONE);
+}
+
+static int
+walk(Fs *fs, Dentry *d, char *name, Dentry *e)
+{
+ int i, n;
+ Dentry x;
+
+ for(i=0;; i++){
+ if((n=readdentry(fs, d, i, &x)) <= 0)
+ return n;
+ if(strcmp(x.name, name) == 0){
+ *e = x;
+ return 1;
+ }
+ }
+}
+
+static long
+kfsread(File *f, void *va, long len)
+{
+ uchar *a;
+ long tot, off, o, n;
+ Fs *fs;
+
+ a = va;
+ fs = f->fs;
+ off = f->kfs.off;
+ tot = 0;
+ while(tot < len){
+ if(getdatablock(fs, &f->kfs, off/fs->kfs.BUFSIZE) < 0)
+ return -1;
+ o = off%fs->kfs.BUFSIZE;
+ n = fs->kfs.BUFSIZE - o;
+ if(n > len-tot)
+ n = len-tot;
+ memmove(a+tot, block+o, n);
+ off += n;
+ tot += n;
+ }
+ f->kfs.off = off;
+ return tot;
+}
+
+static int
+kfswalk(File *f, char *name)
+{
+ int n;
+
+ n = walk(f->fs, &f->kfs, name, &f->kfs);
+ if(n < 0)
+ return -1;
+ f->kfs.off = 0;
+ return 1;
+}
+
+int
+kfsinit(Fs *fs)
+{
+ if(readinfo(fs) < 0 || readroot(fs, &fs->root.kfs) < 0)
+ return -1;
+
+ fs->root.fs = fs;
+ fs->read = kfsread;
+ fs->walk = kfswalk;
+ return 0;
+}
diff --git a/os/boot/pc/l.s b/os/boot/pc/l.s
new file mode 100644
index 00000000..0269e92e
--- /dev/null
+++ b/os/boot/pc/l.s
@@ -0,0 +1,1079 @@
+#include "x16.h"
+#include "mem.h"
+
+#define WRMSR BYTE $0x0F; BYTE $0x30 /* WRMSR, argument in AX/DX (lo/hi) */
+#define RDTSC BYTE $0x0F; BYTE $0x31 /* RDTSC, result in AX/DX (lo/hi) */
+#define RDMSR BYTE $0x0F; BYTE $0x32 /* RDMSR, result in AX/DX (lo/hi) */
+
+#ifdef PXE
+#define PDB 0x90000 /* temporary page tables (24KB) */
+#else
+#define PDB 0x08000
+#endif PXE
+
+#define NoScreenBlank 1
+/*#define ResetDiscs 1*/
+
+TEXT origin(SB), $0
+ /*
+ * This part of l.s is used only in the boot kernel.
+ * It assumes that we are in real address mode, i.e.,
+ * that we look like an 8086.
+ *
+ * Make sure the segments are reasonable.
+ * If we were started directly from the BIOS
+ * (i.e. no MS-DOS) then DS may not be
+ * right.
+ */
+ MOVW CS, AX
+ MOVW AX, DS
+
+#ifdef NoScreenBlank
+ /*
+ * Get the current video mode. If it isn't mode 3,
+ * set text mode 3.
+ * Well, no. Windows95 won't co-operate here so we have
+ * to explicitly set mode 3.
+ */
+ XORL AX, AX
+ MOVB $0x0F, AH
+ INT $0x10 /* get current video mode in AL */
+ CMPB AL, $03
+ JEQ sayhello
+#endif /* NoScreenBlank */
+ XORL AX, AX
+ MOVB $0x03, AL
+ INT $0x10 /* set video mode in AL */
+
+sayhello:
+ LWI(hello(SB), rSI)
+ CALL16(biosputs(SB))
+
+#ifdef ResetDiscs
+ XORL AX, AX /* reset disc system */
+ XORL DX, DX
+ MOVB $0x80, DL
+ INT $0x13
+#endif /* ResetDiscs */
+
+#ifdef DOTCOM
+/*
+ * relocate everything to a half meg and jump there
+ * - looks weird because it is being assembled by a 32 bit
+ * assembler for a 16 bit world
+ *
+ * only b.com does this - not 9load
+ */
+ MOVL $0,BX
+ INCL BX
+ SHLL $15,BX
+ MOVL BX,CX
+ MOVW BX,ES
+ MOVL $0,SI
+ MOVL SI,DI
+ CLD
+ REP
+ MOVSL
+
+ /*
+ * Jump to the copied image;
+ * fix up the DS for the new location.
+ */
+ FARJUMP16(0x8000, _start8000(SB))
+
+TEXT _start8000(SB), $0
+ MFSR(rCS, rAX) /* fix up DS, ES (0x8000) */
+ MTSR(rAX, rDS)
+ MTSR(rAX, rES)
+
+ /*
+ * If we are already in protected mode, have to get back
+ * to real mode before trying any privileged operations
+ * (like going into protected mode...).
+ * Try to reset with a restart vector.
+ */
+ MFCR(rCR0, rAX) /* are we in protected mode? */
+ ANDI(0x0001, rAX)
+ JEQ _real
+
+ CLR(rBX)
+ MTSR(rBX, rES)
+
+ LWI(0x0467, rBX) /* reset entry point */
+ LWI(_start8000(SB), rAX) /* offset within segment */
+ BYTE $0x26
+ BYTE $0x89
+ BYTE $0x07 /* MOVW AX, ES:[BX] */
+ LBI(0x69, rBL)
+ MFSR(rCS, rAX) /* segment */
+ BYTE $0x26
+ BYTE $0x89
+ BYTE $0x07 /* MOVW AX, ES:[BX] */
+
+ CLR(rDX)
+ OUTPORTB(0x70, 0x8F)
+ OUTPORTB(0x71, 0x0A)
+
+ FARJUMP16(0xFFFF, 0x0000) /* reset */
+#endif /* DOTCOM */
+
+_real:
+
+/*
+ * do things that need to be done in real mode.
+ * the results get written to CONFADDR (0x1200)
+ * in a series of <4-byte-magic-number><block-of-data>
+ * the data length is dependent on the magic number.
+ *
+ * this gets parsed by conf.c:/^readlsconf
+ *
+ * N.B. CALL16 kills rDI, so we can't call anything.
+ */
+ LWI(0x0000, rAX)
+ MTSR(rAX, rES)
+ LWI(0x1200, rDI)
+
+/*
+ * turn off interrupts
+ */
+ CLI
+
+/*
+ * detect APM1.2 bios support
+ */
+ /* save DI */
+ SW(rDI, rock(SB))
+
+ /* disconnect anyone else */
+ LWI(0x5304, rAX)
+ LWI(0x0000, rBX)
+ INT $0x15
+
+ /* connect */
+ CLC
+ LWI(0x5303, rAX)
+ LWI(0x0000, rBX)
+ INT $0x15
+ CLI /* apm put interrupts back? */
+
+ JC noapm
+
+ OPSIZE; PUSHR(rSI)
+ OPSIZE; PUSHR(rBX)
+ PUSHR(rDI)
+ PUSHR(rDX)
+ PUSHR(rCX)
+ PUSHR(rAX)
+
+ /* put DI, ES back */
+ LW(rock(SB), rDI)
+ LWI(0x0000, rAX)
+ MTSR(rAX, rES)
+
+ /*
+ * write APM data. first four bytes are APM\0.
+ */
+ LWI(0x5041, rAX)
+ STOSW
+
+ LWI(0x004d, rAX)
+ STOSW
+
+ LWI(8, rCX)
+apmmove:
+ POPR(rAX)
+ STOSW
+ LOOP apmmove
+
+noapm:
+
+/*
+ * end of real mode hacks: write terminator, put ES back.
+ */
+ LWI(0x0000, rAX)
+ STOSW
+ STOSW
+
+ MFSR(rCS, rAX) /* fix up ES (0x8000) */
+ MTSR(rAX, rES)
+
+/*
+ * goto protected mode
+ */
+/* MOVL tgdtptr(SB),GDTR /**/
+ BYTE $0x0f
+ BYTE $0x01
+ BYTE $0x16
+ WORD $tgdtptr(SB)
+
+ LWI(1, rAX)
+ /* MOV AX,MSW */
+ BYTE $0x0F; BYTE $0x01; BYTE $0xF0
+
+/*
+ * clear prefetch queue (weird code to avoid optimizations)
+ */
+ /* JMP .+2 */
+ BYTE $0xEB
+ BYTE $0x00
+
+/*
+ * set all segs
+ */
+/* MOVW $SELECTOR(1, SELGDT, 0),AX /**/
+ BYTE $0xc7
+ BYTE $0xc0
+ WORD $SELECTOR(1, SELGDT, 0)
+ MOVW AX,DS
+ MOVW AX,SS
+ MOVW AX,ES
+ MOVW AX,FS
+ MOVW AX,GS
+
+/* JMPFAR SELECTOR(2, SELGDT, 0):$mode32bit(SB) /**/
+ BYTE $0x66
+ BYTE $0xEA
+ LONG $mode32bit-KZERO(SB)
+ WORD $SELECTOR(2, SELGDT, 0)
+
+TEXT mode32bit(SB),$0
+ /*
+ * make a bottom level page table page that maps the first
+ * 16 meg of physical memory
+ */
+ MOVL $PDB, DI /* clear 6 pages for the tables etc. */
+ XORL AX, AX
+ MOVL $(6*BY2PG), CX
+ SHRL $2, CX
+
+ CLD
+ REP; STOSL
+
+ MOVL $PDB, AX /* phys addr of temporary page table */
+ MOVL $(4*1024),CX /* pte's per page */
+ MOVL $((((4*1024)-1)<<PGSHIFT)|PTEVALID|PTEKERNEL|PTEWRITE),BX
+setpte:
+ MOVL BX,-4(AX)(CX*4)
+ SUBL $(1<<PGSHIFT),BX
+ LOOP setpte
+
+ /*
+ * make a top level page table page that maps the first
+ * 16 meg of memory to 0 thru 16meg and to KZERO thru KZERO+16meg
+ */
+ MOVL AX,BX
+ ADDL $(4*BY2PG),AX
+ ADDL $(PTEVALID|PTEKERNEL|PTEWRITE),BX
+ MOVL BX,0(AX)
+ MOVL BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+0)(AX)
+ ADDL $BY2PG,BX
+ MOVL BX,4(AX)
+ MOVL BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+4)(AX)
+ ADDL $BY2PG,BX
+ MOVL BX,8(AX)
+ MOVL BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+8)(AX)
+ ADDL $BY2PG,BX
+ MOVL BX,12(AX)
+ MOVL BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+12)(AX)
+
+ /*
+ * point processor to top level page & turn on paging
+ *
+ * this produces the apparently harmless "VMX|F(125):468 Dis 0x0:0x0"
+ * message in the VMware log.
+ */
+ MOVL AX,CR3
+ MOVL CR0,AX
+ ORL $0X80000000,AX
+ MOVL AX,CR0
+
+ /*
+ * use a jump to an absolute location to get the PC into
+ * KZERO.
+ */
+ LEAL tokzero(SB),AX
+ JMP* AX
+
+/*
+ * When we load 9load from DOS, the bootstrap jumps
+ * to the instruction right after `JUMP', which gets
+ * us into kzero.
+ *
+ * The name prevents it from being optimized away.
+ */
+TEXT jumplabel(SB), $0
+ BYTE $'J'; BYTE $'U'; BYTE $'M'; BYTE $'P'
+
+ LEAL tokzero(SB),AX
+ JMP* AX
+
+TEXT tokzero(SB),$0
+ /*
+ * Clear BSS
+ */
+ LEAL edata(SB),SI
+ MOVL SI,DI
+ ADDL $4,DI
+ MOVL $0,AX
+ MOVL AX,(SI)
+ LEAL end(SB),CX
+ SUBL DI,CX
+ SHRL $2,CX
+ CLD
+ REP
+ MOVSL
+
+ /*
+ * stack and mach
+ */
+ MOVL $mach0(SB),SP
+ MOVL SP,m(SB)
+ MOVL $0,0(SP)
+ ADDL $(MACHSIZE-4),SP /* start stack above machine struct */
+
+ CALL main(SB)
+
+loop:
+ JMP loop
+
+GLOBL mach0+0(SB), $MACHSIZE
+GLOBL m(SB), $4
+
+/*
+ * gdt to get us to 32-bit/segmented/unpaged mode
+ */
+TEXT tgdt(SB),$0
+
+ /* null descriptor */
+ LONG $0
+ LONG $0
+
+ /* data segment descriptor for 4 gigabytes (PL 0) */
+ LONG $(0xFFFF)
+ LONG $(SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(0)|SEGDATA|SEGW)
+
+ /* exec segment descriptor for 4 gigabytes (PL 0) */
+ LONG $(0xFFFF)
+ LONG $(SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR)
+
+ /* exec segment descriptor for 4 gigabytes (PL 0) 16-bit */
+ LONG $(0xFFFF)
+ LONG $(SEGG|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR)
+
+/*
+ * pointer to initial gdt
+ */
+TEXT tgdtptr(SB),$0
+ WORD $(4*8)
+ LONG $tgdt-KZERO(SB)
+
+/*
+ * Output a string to the display.
+ * String argument is in rSI.
+ */
+TEXT biosputs(SB), $0
+ PUSHA
+ CLR(rBX)
+_BIOSputs:
+ LODSB
+ ORB(rAL, rAL)
+ JEQ _BIOSputsret
+
+ LBI(0x0E, rAH)
+ BIOSCALL(0x10)
+ JMP _BIOSputs
+
+_BIOSputsret:
+ POPA
+ RET
+
+/*
+ * input a byte
+ */
+TEXT inb(SB),$0
+
+ MOVL p+0(FP),DX
+ XORL AX,AX
+ INB
+ RET
+
+/*
+ * input a short from a port
+ */
+TEXT ins(SB), $0
+
+ MOVL p+0(FP), DX
+ XORL AX, AX
+ OPSIZE; INL
+ RET
+
+/*
+ * input a long from a port
+ */
+TEXT inl(SB), $0
+
+ MOVL p+0(FP), DX
+ XORL AX, AX
+ INL
+ RET
+
+/*
+ * output a byte
+ */
+TEXT outb(SB),$0
+
+ MOVL p+0(FP),DX
+ MOVL b+4(FP),AX
+ OUTB
+ RET
+
+/*
+ * output a short to a port
+ */
+TEXT outs(SB), $0
+ MOVL p+0(FP), DX
+ MOVL s+4(FP), AX
+ OPSIZE; OUTL
+ RET
+
+/*
+ * output a long to a port
+ */
+TEXT outl(SB), $0
+ MOVL p+0(FP), DX
+ MOVL s+4(FP), AX
+ OUTL
+ RET
+
+/*
+ * input a string of bytes from a port
+ */
+TEXT insb(SB),$0
+
+ MOVL p+0(FP),DX
+ MOVL a+4(FP),DI
+ MOVL c+8(FP),CX
+ CLD; REP; INSB
+ RET
+
+/*
+ * input a string of shorts from a port
+ */
+TEXT inss(SB),$0
+ MOVL p+0(FP),DX
+ MOVL a+4(FP),DI
+ MOVL c+8(FP),CX
+ CLD
+ REP; OPSIZE; INSL
+ RET
+
+/*
+ * output a string of bytes to a port
+ */
+TEXT outsb(SB),$0
+
+ MOVL p+0(FP),DX
+ MOVL a+4(FP),SI
+ MOVL c+8(FP),CX
+ CLD; REP; OUTSB
+ RET
+
+/*
+ * output a string of shorts to a port
+ */
+TEXT outss(SB),$0
+ MOVL p+0(FP),DX
+ MOVL a+4(FP),SI
+ MOVL c+8(FP),CX
+ CLD
+ REP; OPSIZE; OUTSL
+ RET
+
+/*
+ * input a string of longs from a port
+ */
+TEXT insl(SB),$0
+
+ MOVL p+0(FP),DX
+ MOVL a+4(FP),DI
+ MOVL c+8(FP),CX
+ CLD; REP; INSL
+ RET
+
+/*
+ * output a string of longs to a port
+ */
+TEXT outsl(SB),$0
+
+ MOVL p+0(FP),DX
+ MOVL a+4(FP),SI
+ MOVL c+8(FP),CX
+ CLD; REP; OUTSL
+ RET
+
+/*
+ * routines to load/read various system registers
+ */
+GLOBL idtptr(SB),$6
+TEXT putidt(SB),$0 /* interrupt descriptor table */
+ MOVL t+0(FP),AX
+ MOVL AX,idtptr+2(SB)
+ MOVL l+4(FP),AX
+ MOVW AX,idtptr(SB)
+ MOVL idtptr(SB),IDTR
+ RET
+
+TEXT putcr3(SB),$0 /* top level page table pointer */
+ MOVL t+0(FP),AX
+ MOVL AX,CR3
+ RET
+
+TEXT getcr0(SB),$0 /* coprocessor bits */
+ MOVL CR0,AX
+ RET
+
+TEXT getcr2(SB),$0 /* fault address */
+ MOVL CR2,AX
+ RET
+
+TEXT getcr3(SB),$0 /* page directory base */
+ MOVL CR3,AX
+ RET
+
+TEXT getcr4(SB), $0 /* CR4 - extensions */
+ MOVL CR4, AX
+ RET
+
+TEXT _cycles(SB), $0 /* time stamp counter */
+ RDTSC
+ MOVL vlong+0(FP), CX /* &vlong */
+ MOVL AX, 0(CX) /* lo */
+ MOVL DX, 4(CX) /* hi */
+ RET
+
+TEXT rdmsr(SB), $0 /* model-specific register */
+ MOVL index+0(FP), CX
+ RDMSR
+ MOVL vlong+4(FP), CX /* &vlong */
+ MOVL AX, 0(CX) /* lo */
+ MOVL DX, 4(CX) /* hi */
+ RET
+
+TEXT wrmsr(SB), $0
+ MOVL index+0(FP), CX
+ MOVL lo+4(FP), AX
+ MOVL hi+8(FP), DX
+ WRMSR
+ RET
+
+TEXT mb386(SB), $0
+ POPL AX /* return PC */
+ PUSHFL
+ PUSHL CS
+ PUSHL AX
+ IRETL
+
+/*
+ * special traps
+ */
+TEXT intr0(SB),$0
+ PUSHL $0
+ PUSHL $0
+ JMP intrcommon
+TEXT intr1(SB),$0
+ PUSHL $0
+ PUSHL $1
+ JMP intrcommon
+TEXT intr2(SB),$0
+ PUSHL $0
+ PUSHL $2
+ JMP intrcommon
+TEXT intr3(SB),$0
+ PUSHL $0
+ PUSHL $3
+ JMP intrcommon
+TEXT intr4(SB),$0
+ PUSHL $0
+ PUSHL $4
+ JMP intrcommon
+TEXT intr5(SB),$0
+ PUSHL $0
+ PUSHL $5
+ JMP intrcommon
+TEXT intr6(SB),$0
+ PUSHL $0
+ PUSHL $6
+ JMP intrcommon
+TEXT intr7(SB),$0
+ PUSHL $0
+ PUSHL $7
+ JMP intrcommon
+TEXT intr8(SB),$0
+ PUSHL $8
+ JMP intrcommon
+TEXT intr9(SB),$0
+ PUSHL $0
+ PUSHL $9
+ JMP intrcommon
+TEXT intr10(SB),$0
+ PUSHL $10
+ JMP intrcommon
+TEXT intr11(SB),$0
+ PUSHL $11
+ JMP intrcommon
+TEXT intr12(SB),$0
+ PUSHL $12
+ JMP intrcommon
+TEXT intr13(SB),$0
+ PUSHL $13
+ JMP intrcommon
+TEXT intr14(SB),$0
+ PUSHL $14
+ JMP intrcommon
+TEXT intr15(SB),$0
+ PUSHL $0
+ PUSHL $15
+ JMP intrcommon
+TEXT intr16(SB),$0
+ PUSHL $0
+ PUSHL $16
+ JMP intrcommon
+TEXT intr24(SB),$0
+ PUSHL $0
+ PUSHL $24
+ JMP intrcommon
+TEXT intr25(SB),$0
+ PUSHL $0
+ PUSHL $25
+ JMP intrcommon
+TEXT intr26(SB),$0
+ PUSHL $0
+ PUSHL $26
+ JMP intrcommon
+TEXT intr27(SB),$0
+ PUSHL $0
+ PUSHL $27
+ JMP intrcommon
+TEXT intr28(SB),$0
+ PUSHL $0
+ PUSHL $28
+ JMP intrcommon
+TEXT intr29(SB),$0
+ PUSHL $0
+ PUSHL $29
+ JMP intrcommon
+TEXT intr30(SB),$0
+ PUSHL $0
+ PUSHL $30
+ JMP intrcommon
+TEXT intr31(SB),$0
+ PUSHL $0
+ PUSHL $31
+ JMP intrcommon
+TEXT intr32(SB),$0
+ PUSHL $0
+ PUSHL $32
+ JMP intrcommon
+TEXT intr33(SB),$0
+ PUSHL $0
+ PUSHL $33
+ JMP intrcommon
+TEXT intr34(SB),$0
+ PUSHL $0
+ PUSHL $34
+ JMP intrcommon
+TEXT intr35(SB),$0
+ PUSHL $0
+ PUSHL $35
+ JMP intrcommon
+TEXT intr36(SB),$0
+ PUSHL $0
+ PUSHL $36
+ JMP intrcommon
+TEXT intr37(SB),$0
+ PUSHL $0
+ PUSHL $37
+ JMP intrcommon
+TEXT intr38(SB),$0
+ PUSHL $0
+ PUSHL $38
+ JMP intrcommon
+TEXT intr39(SB),$0
+ PUSHL $0
+ PUSHL $39
+ JMP intrcommon
+TEXT intr64(SB),$0
+ PUSHL $0
+ PUSHL $64
+ JMP intrcommon
+TEXT intrbad(SB),$0
+ PUSHL $0
+ PUSHL $0x1ff
+ JMP intrcommon
+
+intrcommon:
+ PUSHL DS
+ PUSHL ES
+ PUSHL FS
+ PUSHL GS
+ PUSHAL
+ MOVL $(KDSEL),AX
+ MOVW AX,DS
+ MOVW AX,ES
+ LEAL 0(SP),AX
+ PUSHL AX
+ CALL trap(SB)
+ POPL AX
+ POPAL
+ POPL GS
+ POPL FS
+ POPL ES
+ POPL DS
+ ADDL $8,SP /* error code and trap type */
+ IRETL
+
+
+/*
+ * interrupt level is interrupts on or off
+ */
+TEXT spllo(SB),$0
+ PUSHFL
+ POPL AX
+ STI
+ RET
+
+TEXT splhi(SB),$0
+ PUSHFL
+ POPL AX
+ CLI
+ RET
+
+TEXT splx(SB),$0
+ MOVL s+0(FP),AX
+ PUSHL AX
+ POPFL
+ RET
+
+/*
+ * do nothing whatsoever till interrupt happens
+ */
+TEXT idle(SB),$0
+ HLT
+ RET
+
+/*
+ * Try to determine the CPU type which requires fiddling with EFLAGS.
+ * If the Id bit can be toggled then the CPUID instruciton can be used
+ * to determine CPU identity and features. First have to check if it's
+ * a 386 (Ac bit can't be set). If it's not a 386 and the Id bit can't be
+ * toggled then it's an older 486 of some kind.
+ *
+ * cpuid(id[], &ax, &dx);
+ */
+#define CPUID BYTE $0x0F; BYTE $0xA2 /* CPUID, argument in AX */
+TEXT cpuid(SB), $0
+ MOVL $0x240000, AX
+ PUSHL AX
+ POPFL /* set Id|Ac */
+
+ PUSHFL
+ POPL BX /* retrieve value */
+
+ MOVL $0, AX
+ PUSHL AX
+ POPFL /* clear Id|Ac, EFLAGS initialised */
+
+ PUSHFL
+ POPL AX /* retrieve value */
+ XORL BX, AX
+ TESTL $0x040000, AX /* Ac */
+ JZ _cpu386 /* can't set this bit on 386 */
+ TESTL $0x200000, AX /* Id */
+ JZ _cpu486 /* can't toggle this bit on some 486 */
+
+ MOVL $0, AX
+ CPUID
+ MOVL id+0(FP), BP
+ MOVL BX, 0(BP) /* "Genu" "Auth" "Cyri" */
+ MOVL DX, 4(BP) /* "ineI" "enti" "xIns" */
+ MOVL CX, 8(BP) /* "ntel" "cAMD" "tead" */
+
+ MOVL $1, AX
+ CPUID
+ JMP _cpuid
+
+_cpu486:
+ MOVL $0x400, AX
+ MOVL $0, DX
+ JMP _cpuid
+
+_cpu386:
+ MOVL $0x300, AX
+ MOVL $0, DX
+
+_cpuid:
+ MOVL ax+4(FP), BP
+ MOVL AX, 0(BP)
+ MOVL dx+8(FP), BP
+ MOVL DX, 0(BP)
+ RET
+
+
+/*
+ * basic timing loop to determine CPU frequency
+ */
+TEXT aamloop(SB),$0
+
+ MOVL c+0(FP),CX
+aaml1:
+ AAM
+ LOOP aaml1
+ RET
+
+TEXT hello(SB), $0
+ BYTE $'P'; BYTE $'l'; BYTE $'a'; BYTE $'n';
+ BYTE $' '; BYTE $'9'; BYTE $' '; BYTE $'f';
+ BYTE $'r'; BYTE $'o'; BYTE $'m'; BYTE $' ';
+ BYTE $'B'; BYTE $'e'; BYTE $'l'; BYTE $'l';
+ BYTE $' '; BYTE $'L'; BYTE $'a'; BYTE $'b';
+ BYTE $'s';
+ BYTE $'\r';
+ BYTE $'\n';
+ BYTE $'\z';
+
+TEXT rock(SB), $0
+ BYTE $0; BYTE $0; BYTE $0; BYTE $0;
+
+GLOBL pxe(SB), $4
+#ifdef PXE
+DATA pxe+0(SB)/4, $1
+#else
+DATA pxe+0(SB)/4, $0
+#endif /* PXE */
+
+/*
+ * Save registers.
+ */
+TEXT saveregs(SB), $0
+ /* appease 8l */
+ SUBL $32, SP
+ POPL AX
+ POPL AX
+ POPL AX
+ POPL AX
+ POPL AX
+ POPL AX
+ POPL AX
+ POPL AX
+
+ PUSHL AX
+ PUSHL BX
+ PUSHL CX
+ PUSHL DX
+ PUSHL BP
+ PUSHL DI
+ PUSHL SI
+ PUSHFL
+
+ XCHGL 32(SP), AX /* swap return PC and saved flags */
+ XCHGL 0(SP), AX
+ XCHGL 32(SP), AX
+ RET
+
+TEXT restoreregs(SB), $0
+ /* appease 8l */
+ PUSHL AX
+ PUSHL AX
+ PUSHL AX
+ PUSHL AX
+ PUSHL AX
+ PUSHL AX
+ PUSHL AX
+ PUSHL AX
+ ADDL $32, SP
+
+ XCHGL 32(SP), AX /* swap return PC and saved flags */
+ XCHGL 0(SP), AX
+ XCHGL 32(SP), AX
+
+ POPFL
+ POPL SI
+ POPL DI
+ POPL BP
+ POPL DX
+ POPL CX
+ POPL BX
+ POPL AX
+ RET
+
+/*
+ * Assumed to be in protected mode at time of call.
+ * Switch to real mode, execute an interrupt, and
+ * then switch back to protected mode.
+ *
+ * Assumes:
+ *
+ * - no device interrupts are going to come in
+ * - 0-16MB is identity mapped in page tables
+ * - can use code segment 0x1000 in real mode
+ * to get at l.s code
+ */
+TEXT realmodeidtptr(SB), $0
+ WORD $(4*256-1)
+ LONG $0
+
+TEXT realmode0(SB), $0
+ CALL saveregs(SB)
+
+ /* switch to low code address */
+ LEAL physcode-KZERO(SB), AX
+ JMP *AX
+
+TEXT physcode(SB), $0
+
+ /* switch to low stack */
+ MOVL SP, AX
+ MOVL $0x7C00, SP
+ PUSHL AX
+
+ /* load IDT with real-mode version; GDT already fine */
+ MOVL realmodeidtptr(SB), IDTR
+
+ /* edit INT $0x00 instruction below */
+ MOVL realmodeintr(SB), AX
+ MOVB AX, realmodeintrinst+1(SB)
+
+ /* disable paging */
+ MOVL CR0, AX
+ ANDL $0x7FFFFFFF, AX
+ MOVL AX, CR0
+ /* JMP .+2 to clear prefetch queue*/
+ BYTE $0xEB; BYTE $0x00
+
+ /* jump to 16-bit code segment */
+/* JMPFAR SELECTOR(3, SELGDT, 0):$again16bit(SB) /**/
+ BYTE $0xEA
+ LONG $again16bit-KZERO(SB)
+ WORD $SELECTOR(3, SELGDT, 0)
+
+TEXT again16bit(SB), $0
+ /*
+ * Now in 16-bit compatibility mode.
+ * These are 32-bit instructions being interpreted
+ * as 16-bit instructions. I'm being lazy and
+ * not using the macros because I know when
+ * the 16- and 32-bit instructions look the same
+ * or close enough.
+ */
+
+ /* disable protected mode and jump to real mode cs */
+ OPSIZE; MOVL CR0, AX
+ OPSIZE; XORL BX, BX
+ OPSIZE; INCL BX
+ OPSIZE; XORL BX, AX
+ OPSIZE; MOVL AX, CR0
+
+ /* JMPFAR 0x1000:now16real */
+ BYTE $0xEA
+ WORD $now16real-KZERO(SB)
+ WORD $0x1000
+
+TEXT now16real(SB), $0
+ /* copy the registers for the bios call */
+ LWI(0x1000, rAX)
+ MOVW AX,SS
+ LWI(realmoderegs(SB), rBP)
+
+ /* offsets are in Ureg */
+ LXW(44, xBP, rAX)
+ MOVW AX, DS
+ LXW(40, xBP, rAX)
+ MOVW AX, ES
+
+ OPSIZE; LXW(0, xBP, rDI)
+ OPSIZE; LXW(4, xBP, rSI)
+ OPSIZE; LXW(16, xBP, rBX)
+ OPSIZE; LXW(20, xBP, rDX)
+ OPSIZE; LXW(24, xBP, rCX)
+ OPSIZE; LXW(28, xBP, rAX)
+
+ CLC
+
+TEXT realmodeintrinst(SB), $0
+ INT $0x00
+
+ /* save the registers after the call */
+
+ LWI(0x7bfc, rSP)
+ OPSIZE; PUSHFL
+ OPSIZE; PUSHL AX
+
+ LWI(0x1000, rAX)
+ MOVW AX,SS
+ LWI(realmoderegs(SB), rBP)
+
+ OPSIZE; SXW(rDI, 0, xBP)
+ OPSIZE; SXW(rSI, 4, xBP)
+ OPSIZE; SXW(rBX, 16, xBP)
+ OPSIZE; SXW(rDX, 20, xBP)
+ OPSIZE; SXW(rCX, 24, xBP)
+ OPSIZE; POPL AX
+ OPSIZE; SXW(rAX, 28, xBP)
+
+ MOVW DS, AX
+ OPSIZE; SXW(rAX, 44, xBP)
+ MOVW ES, AX
+ OPSIZE; SXW(rAX, 40, xBP)
+
+ OPSIZE; POPL AX
+ OPSIZE; SXW(rAX, 64, xBP) /* flags */
+
+ /* re-enter protected mode and jump to 32-bit code */
+ OPSIZE; MOVL $1, AX
+ OPSIZE; MOVL AX, CR0
+
+/* JMPFAR SELECTOR(2, SELGDT, 0):$again32bit(SB) /**/
+ OPSIZE
+ BYTE $0xEA
+ LONG $again32bit-KZERO(SB)
+ WORD $SELECTOR(2, SELGDT, 0)
+
+TEXT again32bit(SB), $0
+ MOVW $SELECTOR(1, SELGDT, 0),AX
+ MOVW AX,DS
+ MOVW AX,SS
+ MOVW AX,ES
+ MOVW AX,FS
+ MOVW AX,GS
+
+ /* enable paging and jump to kzero-address code */
+ MOVL CR0, AX
+ ORL $0x80000000, AX
+ MOVL AX, CR0
+ LEAL again32kzero(SB), AX
+ JMP* AX
+
+TEXT again32kzero(SB), $0
+ /* breathe a sigh of relief - back in 32-bit protected mode */
+
+ /* switch to old stack */
+ PUSHL AX /* match popl below for 8l */
+ MOVL $0x7BFC, SP
+ POPL SP
+
+ /* restore idt */
+ MOVL idtptr(SB),IDTR
+
+ CALL restoreregs(SB)
+ RET
+
+TEXT realmoderegs(SB), $0
+ LONG $0; LONG $0; LONG $0; LONG $0
+ LONG $0; LONG $0; LONG $0; LONG $0
+ LONG $0; LONG $0; LONG $0; LONG $0
+ LONG $0; LONG $0; LONG $0; LONG $0
+ LONG $0; LONG $0; LONG $0; LONG $0
+
+TEXT realmodeintr(SB), $0
+ LONG $0
+
diff --git a/os/boot/pc/lib.h b/os/boot/pc/lib.h
new file mode 100644
index 00000000..a011b5e4
--- /dev/null
+++ b/os/boot/pc/lib.h
@@ -0,0 +1,102 @@
+/*
+ * functions (possibly) linked in, complete, from libc.
+ */
+
+/*
+ * mem routines
+ */
+extern void* memccpy(void*, void*, int, long);
+extern void* memset(void*, int, long);
+extern int memcmp(void*, void*, long);
+extern void* memmove(void*, void*, long);
+extern void* memchr(void*, int, long);
+
+/*
+ * string routines
+ */
+extern char* strcat(char*, char*);
+extern char* strchr(char*, char);
+extern int strcmp(char*, char*);
+extern char* strcpy(char*, char*);
+extern char* strncat(char*, char*, long);
+extern char* strncpy(char*, char*, long);
+extern int strncmp(char*, char*, long);
+extern long strlen(char*);
+extern char* strrchr(char*, char);
+extern char* strstr(char*, char*);
+
+
+/*
+ * print routines
+ */
+typedef struct Fmt Fmt;
+typedef int (*Fmts)(Fmt*);
+struct Fmt{
+ uchar runes; /* output buffer is runes or chars? */
+ void *start; /* of buffer */
+ void *to; /* current place in the buffer */
+ void *stop; /* end of the buffer; overwritten if flush fails */
+ int (*flush)(Fmt *); /* called when to == stop */
+ void *farg; /* to make flush a closure */
+ int nfmt; /* num chars formatted so far */
+ va_list args; /* args passed to dofmt */
+ int r; /* % format Rune */
+ int width;
+ int prec;
+ ulong flags;
+};
+extern int print(char*, ...);
+extern char* vseprint(char*, char*, char*, va_list);
+extern int sprint(char*, char*, ...);
+extern int snprint(char*, int, char*, ...);
+extern int fmtinstall(int, int (*)(Fmt*));
+
+#pragma varargck argpos fmtprint 2
+#pragma varargck argpos print 1
+#pragma varargck argpos seprint 3
+#pragma varargck argpos snprint 3
+#pragma varargck argpos sprint 2
+#pragma varargck type "H" void*
+
+#pragma varargck type "lld" vlong
+#pragma varargck type "llx" vlong
+#pragma varargck type "lld" uvlong
+#pragma varargck type "llx" uvlong
+#pragma varargck type "ld" long
+#pragma varargck type "lx" long
+#pragma varargck type "ld" ulong
+#pragma varargck type "lx" ulong
+#pragma varargck type "d" int
+#pragma varargck type "x" int
+#pragma varargck type "c" int
+#pragma varargck type "C" int
+#pragma varargck type "d" uint
+#pragma varargck type "x" uint
+#pragma varargck type "c" uint
+#pragma varargck type "C" uint
+#pragma varargck type "f" double
+#pragma varargck type "e" double
+#pragma varargck type "g" double
+#pragma varargck type "s" char*
+#pragma varargck type "q" char*
+#pragma varargck type "S" Rune*
+#pragma varargck type "Q" Rune*
+#pragma varargck type "r" void
+#pragma varargck type "%" void
+#pragma varargck type "|" int
+#pragma varargck type "p" void*
+#pragma varargck type "lux" void*
+#pragma varargck type "E" uchar*
+
+#define PRINTSIZE 256
+
+/*
+ * one-of-a-kind
+ */
+extern int atoi(char*);
+extern ulong getcallerpc(void*);
+extern long strtol(char*, char**, int);
+extern ulong strtoul(char*, char**, int);
+extern long end;
+
+#define NAMELEN 28
diff --git a/os/boot/pc/load.c b/os/boot/pc/load.c
new file mode 100644
index 00000000..575c1205
--- /dev/null
+++ b/os/boot/pc/load.c
@@ -0,0 +1,563 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "fs.h"
+
+static char *diskparts[] = { "dos", "9fat", "fs", "data", "cdboot", 0 };
+static char *etherparts[] = { "*", 0 };
+
+static char *diskinis[] = {
+ "plan9/plan9.ini",
+ "plan9.ini",
+ 0
+};
+static char *etherinis[] = {
+ "/cfg/pxe/%E",
+ 0
+};
+
+Type types[] = {
+ { Tfloppy,
+ Fini|Ffs,
+ floppyinit, floppyinitdev,
+ floppygetfspart, 0, floppyboot,
+ floppyprintdevs,
+ diskparts,
+ diskinis,
+ },
+ { Tcd,
+ Fini|Ffs,
+ cdinit, sdinitdev,
+ sdgetfspart, sdaddconf, sdboot,
+ sdprintdevs,
+ diskparts,
+ diskinis,
+ },
+ { Tether,
+ Fini|Fbootp,
+ etherinit, etherinitdev,
+ pxegetfspart, 0, bootpboot,
+ etherprintdevs,
+ etherparts,
+ etherinis,
+ },
+ { Tsd,
+ Fini|Ffs,
+ sdinit, sdinitdev,
+ sdgetfspart, sdaddconf, sdboot,
+ sdprintdevs,
+ diskparts,
+ diskinis,
+ },
+ { Tnil,
+ 0,
+ nil, nil, nil, nil, nil, nil,
+ nil,
+ nil,
+ 0,
+ nil,
+ },
+};
+
+#include "sd.h"
+
+extern SDifc sdataifc;
+
+#ifdef NOSCSI
+
+SDifc* sdifc[] = {
+ &sdataifc,
+ nil,
+};
+
+#else
+
+extern SDifc sdmylexifc;
+extern SDifc sd53c8xxifc;
+SDifc* sdifc[] = {
+ &sdataifc,
+ &sdmylexifc,
+ &sd53c8xxifc,
+ nil,
+};
+
+#endif NOSCSI
+
+typedef struct Mode Mode;
+
+enum {
+ Maxdev = 7,
+ Dany = -1,
+ Nmedia = 16,
+ Nini = 10,
+};
+
+enum { /* mode */
+ Mauto = 0x00,
+ Mlocal = 0x01,
+ Manual = 0x02,
+ NMode = 0x03,
+};
+
+typedef struct Medium Medium;
+struct Medium {
+ Type* type;
+ int flag;
+ int dev;
+ char name[NAMELEN];
+
+ Fs *inifs;
+ char *part;
+ char *ini;
+
+ Medium* next;
+};
+
+typedef struct Mode {
+ char* name;
+ int mode;
+} Mode;
+
+static Medium media[Nmedia];
+static Medium *curmedium = media;
+
+static Mode modes[NMode+1] = {
+ [Mauto] { "auto", Mauto, },
+ [Mlocal] { "local", Mlocal, },
+ [Manual] { "manual", Manual, },
+};
+
+char **ini;
+
+int scsi0port;
+char *defaultpartition;
+int iniread;
+
+static Medium*
+parse(char *line, char **file)
+{
+ char *p;
+ Type *tp;
+ Medium *mp;
+
+ if(p = strchr(line, '!')) {
+ *p++ = 0;
+ *file = p;
+ } else
+ *file = "";
+
+ for(tp = types; tp->type != Tnil; tp++)
+ for(mp = tp->media; mp; mp = mp->next)
+ if(strcmp(mp->name, line) == 0)
+ return mp;
+ if(p)
+ *--p = '!';
+ return nil;
+}
+
+static int
+boot(Medium *mp, char *file)
+{
+ Type *tp;
+ Medium *xmp;
+ static int didaddconf;
+ Boot b;
+
+ memset(&b, 0, sizeof b);
+ b.state = INITKERNEL;
+
+ if(didaddconf == 0) {
+ didaddconf = 1;
+ for(tp = types; tp->type != Tnil; tp++)
+ if(tp->addconf)
+ for(xmp = tp->media; xmp; xmp = xmp->next)
+ (*tp->addconf)(xmp->dev);
+ }
+
+ sprint(BOOTLINE, "%s!%s", mp->name, file);
+ return (*mp->type->boot)(mp->dev, file, &b);
+}
+
+static Medium*
+allocm(Type *tp)
+{
+ Medium **l;
+
+ if(curmedium >= &media[Nmedia])
+ return 0;
+
+ for(l = &tp->media; *l; l = &(*l)->next)
+ ;
+ *l = curmedium++;
+ return *l;
+}
+
+Medium*
+probe(int type, int flag, int dev)
+{
+ Type *tp;
+ int i;
+ Medium *mp;
+ File f;
+ Fs *fs;
+ char **partp;
+
+ for(tp = types; tp->type != Tnil; tp++){
+ if(type != Tany && type != tp->type)
+ continue;
+
+ if(flag != Fnone){
+ for(mp = tp->media; mp; mp = mp->next){
+ if((flag & mp->flag) && (dev == Dany || dev == mp->dev))
+ return mp;
+ }
+ }
+
+ if((tp->flag & Fprobe) == 0){
+ tp->flag |= Fprobe;
+ tp->mask = (*tp->init)();
+ }
+
+ for(i = 0; tp->mask; i++){
+ if((tp->mask & (1<<i)) == 0)
+ continue;
+ tp->mask &= ~(1<<i);
+
+ if((mp = allocm(tp)) == 0)
+ continue;
+
+ mp->dev = i;
+ mp->flag = tp->flag;
+ mp->type = tp;
+ (*tp->initdev)(i, mp->name);
+
+ if(mp->flag & Fini){
+ mp->flag &= ~Fini;
+ for(partp = tp->parts; *partp; partp++){
+ if((fs = (*tp->getfspart)(i, *partp, 0)) == nil)
+ continue;
+
+ for(ini = tp->inis; *ini; ini++){
+ if(fswalk(fs, *ini, &f) > 0){
+ mp->inifs = fs;
+ mp->part = *partp;
+ mp->ini = f.path;
+ mp->flag |= Fini;
+ goto Break2;
+ }
+ }
+ }
+ }
+ Break2:
+ if((flag & mp->flag) && (dev == Dany || dev == i))
+ return mp;
+ }
+ }
+
+ return 0;
+}
+
+void
+main(void)
+{
+ Medium *mp;
+ int flag, i, mode, tried;
+ char def[2*NAMELEN], line[80], *p, *file;
+ Type *tp;
+
+ i8042a20();
+ memset(m, 0, sizeof(Mach));
+ trapinit();
+ clockinit();
+ alarminit();
+ meminit(0);
+ spllo();
+ kbdinit();
+
+ if((ulong)&end > (KZERO|(640*1024)))
+ panic("i'm too big\n");
+
+ readlsconf();
+ for(tp = types; tp->type != Tnil; tp++){
+ //if(tp->type == Tether)
+ // continue;
+ if((mp = probe(tp->type, Fini, Dany)) && (mp->flag & Fini)){
+ print("using %s!%s!%s\n", mp->name, mp->part, mp->ini);
+ iniread = !dotini(mp->inifs);
+ break;
+ }
+ }
+ apminit();
+
+ if((p = getconf("console")) != nil)
+ consinit(p, getconf("baud"));
+ devpccardlink();
+ devi82365link();
+
+ /*
+ * Even after we find the ini file, we keep probing disks,
+ * because we have to collect the partition tables and
+ * have boot devices for parse.
+ */
+ probe(Tany, Fnone, Dany);
+ tried = 0;
+ mode = Mauto;
+
+ p = getconf("bootfile");
+
+ if(p != 0) {
+ mode = Manual;
+ for(i = 0; i < NMode; i++){
+ if(strcmp(p, modes[i].name) == 0){
+ mode = modes[i].mode;
+ goto done;
+ }
+ }
+ if((mp = parse(p, &file)) == nil) {
+ print("Unknown boot device: %s\n", p);
+ goto done;
+ }
+ tried = boot(mp, file);
+ }
+done:
+ if(tried == 0 && mode != Manual){
+ flag = Fany;
+ if(mode == Mlocal)
+ flag &= ~Fbootp;
+ if((mp = probe(Tany, flag, Dany)) && mp->type->type != Tfloppy)
+ boot(mp, "");
+ }
+
+ def[0] = 0;
+ probe(Tany, Fnone, Dany);
+ if(p = getconf("bootdef"))
+ strcpy(def, p);
+
+ flag = 0;
+ for(tp = types; tp->type != Tnil; tp++){
+ for(mp = tp->media; mp; mp = mp->next){
+ if(flag == 0){
+ flag = 1;
+ print("Boot devices:");
+ }
+ (*tp->printdevs)(mp->dev);
+ }
+ }
+ if(flag)
+ print("\n");
+
+ for(;;){
+ if(getstr("boot from", line, sizeof(line), def, (mode != Manual)*15) >= 0)
+ if(mp = parse(line, &file))
+ boot(mp, file);
+ def[0] = 0;
+ }
+}
+
+int
+getfields(char *lp, char **fields, int n, char sep)
+{
+ int i;
+
+ for(i = 0; lp && *lp && i < n; i++){
+ while(*lp == sep)
+ *lp++ = 0;
+ if(*lp == 0)
+ break;
+ fields[i] = lp;
+ while(*lp && *lp != sep){
+ if(*lp == '\\' && *(lp+1) == '\n')
+ *lp++ = ' ';
+ lp++;
+ }
+ }
+ return i;
+}
+
+int
+cistrcmp(char *a, char *b)
+{
+ int ac, bc;
+
+ for(;;){
+ ac = *a++;
+ bc = *b++;
+
+ if(ac >= 'A' && ac <= 'Z')
+ ac = 'a' + (ac - 'A');
+ if(bc >= 'A' && bc <= 'Z')
+ bc = 'a' + (bc - 'A');
+ ac -= bc;
+ if(ac)
+ return ac;
+ if(bc == 0)
+ break;
+ }
+ return 0;
+}
+
+int
+cistrncmp(char *a, char *b, int n)
+{
+ unsigned ac, bc;
+
+ while(n > 0){
+ ac = *a++;
+ bc = *b++;
+ n--;
+
+ if(ac >= 'A' && ac <= 'Z')
+ ac = 'a' + (ac - 'A');
+ if(bc >= 'A' && bc <= 'Z')
+ bc = 'a' + (bc - 'A');
+
+ ac -= bc;
+ if(ac)
+ return ac;
+ if(bc == 0)
+ break;
+ }
+
+ return 0;
+}
+
+#define PSTART (12*1024*1024)
+#define PEND (16*1024*1024)
+
+ulong palloc = PSTART;
+
+void*
+ialloc(ulong n, int align)
+{
+ ulong p;
+ int a;
+
+ p = palloc;
+ if(align <= 0)
+ align = 4;
+ if(a = n % align)
+ n += align - a;
+ if(a = p % align)
+ p += align - a;
+
+
+ palloc = p+n;
+ if(palloc > PEND)
+ panic("ialloc(%lud, %d) called from 0x%lux\n",
+ n, align, getcallerpc(&n));
+ return memset((void*)(p|KZERO), 0, n);
+}
+
+void*
+xspanalloc(ulong size, int align, ulong span)
+{
+ ulong a, v;
+
+ if((palloc + (size+align+span)) > PEND)
+ panic("xspanalloc(%lud, %d, 0x%lux) called from 0x%lux\n",
+ size, align, span, getcallerpc(&size));
+
+ a = (ulong)ialloc(size+align+span, 0);
+
+ if(span > 2)
+ v = (a + span) & ~(span-1);
+ else
+ v = a;
+
+ if(align > 1)
+ v = (v + align) & ~(align-1);
+
+ return (void*)v;
+}
+
+static Block *allocbp;
+
+Block*
+allocb(int size)
+{
+ Block *bp, **lbp;
+ ulong addr;
+
+ lbp = &allocbp;
+ for(bp = *lbp; bp; bp = bp->next){
+ if((bp->lim - bp->base) >= size){
+ *lbp = bp->next;
+ break;
+ }
+ lbp = &bp->next;
+ }
+ if(bp == 0){
+ if((palloc + (sizeof(Block)+size+64)) > PEND)
+ panic("allocb(%d) called from 0x%lux\n",
+ size, getcallerpc(&size));
+ bp = ialloc(sizeof(Block)+size+64, 0);
+ addr = (ulong)bp;
+ addr = ROUNDUP(addr + sizeof(Block), 8);
+ bp->base = (uchar*)addr;
+ bp->lim = ((uchar*)bp) + sizeof(Block)+size+64;
+ }
+
+ if(bp->flag)
+ panic("allocb reuse\n");
+
+ bp->rp = bp->base;
+ bp->wp = bp->rp;
+ bp->next = 0;
+ bp->flag = 1;
+
+ return bp;
+}
+
+void
+freeb(Block* bp)
+{
+ bp->next = allocbp;
+ allocbp = bp;
+
+ bp->flag = 0;
+}
+
+enum {
+ Paddr= 0x70, /* address port */
+ Pdata= 0x71, /* data port */
+};
+
+uchar
+nvramread(int offset)
+{
+ outb(Paddr, offset);
+ return inb(Pdata);
+}
+
+void (*etherdetach)(void);
+void (*floppydetach)(void);
+void (*sddetach)(void);
+
+void
+warp9(ulong entry)
+{
+ if(etherdetach)
+ etherdetach();
+ if(floppydetach)
+ floppydetach();
+ if(sddetach)
+ sddetach();
+
+ consdrain();
+
+ splhi();
+ trapdisable();
+
+ /*
+ * This is where to push things on the stack to
+ * boot *BSD systems, e.g.
+ (*(void(*)(void*, void*, void*, void*, ulong, ulong))(PADDR(entry)))(0, 0, 0, 0, 8196, 640);
+ * will enable NetBSD boot (the real memory size needs to
+ * go in the 5th argument).
+ */
+ (*(void(*)(void))(PADDR(entry)))();
+}
diff --git a/os/boot/pc/mbr.s b/os/boot/pc/mbr.s
new file mode 100644
index 00000000..fb299584
--- /dev/null
+++ b/os/boot/pc/mbr.s
@@ -0,0 +1,259 @@
+/*
+ * Hard disc boot block. Loaded at 0x7C00, relocates to 0x0600:
+ * 8a mbr.s; 8l -o mbr -l -H3 -T0x0600 mbr.8
+ */
+#include "x16.h"
+#include "mem.h"
+
+/*#define FLOPPY 1 /* test on a floppy */
+#define TRACE(C) PUSHA;\
+ CLR(rBX);\
+ MOVB $C, AL;\
+ LBI(0x0E, rAH);\
+ BIOSCALL(0x10);\
+ POPA
+
+/*
+ * We keep data on the stack, indexed by BP.
+ */
+#define Xdap 0x00 /* disc address packet */
+#define Xtable 0x10 /* partition table entry */
+#define Xdrive 0x12 /* starting disc */
+#define Xtotal 0x14 /* sum of allocated data above */
+
+/*
+ * Start: loaded at 0000:7C00, relocate to 0000:0600.
+ * Boot drive is in rDL.
+ */
+TEXT _start(SB), $0
+ CLI
+ CLR(rAX)
+ MTSR(rAX, rSS) /* 0000 -> rSS */
+ LWI((0x7C00-Xtotal), rSP) /* 7Bxx -> rSP */
+ MW(rSP, rBP) /* set the indexed-data pointer */
+
+ MTSR(rAX, rDS) /* 0000 -> rDS, source segment */
+ LWI(0x7C00, rSI) /* 7C00 -> rSI, source offset */
+ MTSR(rAX, rES) /* 0000 -> rES, destination segment */
+ LWI(0x600, rDI) /* 0600 -> rDI, destination offset */
+ LWI(0x100, rCX) /* 0100 -> rCX, loop count (words) */
+
+ CLD
+ REP; MOVSL /* MOV DS:[(E)SI] -> ES:[(E)DI] */
+
+ FARJUMP16(0x0000, _start0600(SB))
+
+TEXT _start0600(SB), $0
+#ifdef FLOPPY
+ LBI(0x80, rDL)
+#else
+ CLRB(rAL) /* some systems pass 0 */
+ CMPBR(rAL, rDL)
+ JNE _save
+ LBI(0x80, rDL)
+#endif /* FLOPPY */
+_save:
+ SXB(rDL, Xdrive, xBP) /* save disc */
+
+ LWI(confidence(SB), rSI) /* for that warm, fuzzy feeling */
+ CALL16(BIOSputs(SB))
+
+ LWI(_start+0x01BE(SB), rSI) /* address of partition table */
+ LWI(0x04, rCX) /* 4 entries in table */
+ LBI(0x80, rAH) /* active entry value */
+ CLRB(rAL) /* inactive entry value */
+
+_activeloop0:
+ LXB(0x00, xSI, rBL) /* get active entry from table */
+ CMPBR(rBL, rAH) /* is this an active entry? */
+ JEQ _active
+
+ CMPBR(rBL, rAL) /* if not active it should be 0 */
+ JNE _invalidMBR
+
+ ADDI(0x10, rSI) /* next table entry */
+ DEC(rCX)
+ JNE _activeloop0
+
+ LWI(noentry(SB), rSI)
+ CALL16(buggery(SB))
+
+_active:
+ MW(rSI, rDI) /* save table address */
+
+_activeloop1:
+ ADDI(0x10, rSI) /* next table entry */
+ DEC(rCX)
+ JEQ _readsector
+
+ LXB(0x00, xSI, rBL) /* get active entry from table */
+ CMPBR(rBL, rAH) /* is this an active entry? */
+ JNE _activeloop1 /* should only be one active */
+
+_invalidMBR:
+ LWI(invalidMBR(SB), rSI)
+ CALL16(buggery(SB))
+
+_readsector:
+ LBI(0x41, rAH) /* check extensions present */
+ LWI(0x55AA, rBX)
+ LXB(Xdrive, xBP, rDL) /* drive */
+ BIOSCALL(0x13) /* CF set on failure */
+ JCS _readsector2
+ CMPI(0xAA55, rBX)
+ JNE _readsector2
+ ANDI(0x0001, rCX)
+ JEQ _readsector2
+
+_readsector42:
+ SBPBI(0x10, Xdap+0) /* packet size */
+ SBPBI(0x00, Xdap+1) /* reserved */
+ SBPBI(0x01, Xdap+2) /* number of blocks to transfer */
+ SBPBI(0x00, Xdap+3) /* reserved */
+ SBPWI(0x7C00, Xdap+4) /* transfer buffer :offset */
+ SBPWI(0x0000, Xdap+6) /* transfer buffer seg: */
+ LXW(0x08, xDI, rAX) /* LBA (64-bits) */
+ SBPW(rAX, Xdap+8)
+ LXW(0x0A, xDI, rAX)
+ SBPW(rAX, Xdap+10)
+ SBPWI(0x0000, Xdap+12)
+ SBPWI(0x0000, Xdap+14)
+
+ MW(rBP, rSI) /* disk address packet */
+ LBI(0x42, rAH) /* extended read */
+ BIOSCALL(0x13) /* CF set on failure */
+ JCC _readsectorok
+
+ LWI(ioerror(SB), rSI)
+ CALL16(buggery(SB))
+
+/*
+ * Read a sector from a disc using the traditional BIOS call.
+ * For BIOSCALL(0x13/AH=0x02):
+ * rAH 0x02
+ * rAL number of sectors to read (1)
+ * rCH low 8 bits of cylinder
+ * rCL high 2 bits of cylinder (7-6), sector (5-0)
+ * rDH head
+ * rDL drive
+ * rES:rBX buffer address
+ */
+_readsector2:
+ LXB(0x01, xDI, rDH) /* head */
+ LXW(0x02, xDI, rCX) /* save active cylinder/sector */
+
+ LWI(0x0201, rAX) /* read one sector */
+ LXB(Xdrive, xBP, rDL) /* drive */
+ LWI(0x7C00, rBX) /* buffer address (rES already OK) */
+ BIOSCALL(0x13) /* CF set on failure */
+ JCC _readsectorok
+
+ LWI(ioerror(SB), rSI)
+ CALL16(buggery(SB))
+
+_readsectorok:
+ LWI(0x7C00, rBX) /* buffer address (rES already OK) */
+ LXW(0x1FE, xBX, rAX)
+ CMPI(0xAA55, rAX)
+ JNE _bbnotok
+
+ /*
+ * Jump to the loaded PBS.
+ * rDL and rSI should still contain the drive
+ * and partition table pointer respectively.
+ */
+ MW(rDI, rSI)
+ FARJUMP16(0x0000, 0x7C00)
+
+_bbnotok:
+ LWI(invalidPBS(SB), rSI)
+
+TEXT buggery(SB), $0
+ CALL16(BIOSputs(SB))
+ LWI(reboot(SB), rSI)
+ CALL16(BIOSputs(SB))
+
+_wait:
+ CLR(rAX) /* wait for any key */
+ BIOSCALL(0x16)
+
+_reset:
+ CLR(rBX) /* set ES segment for BIOS area */
+ MTSR(rBX, rES)
+
+ LWI(0x0472, rBX) /* warm-start code address */
+ LWI(0x1234, rAX) /* warm-start code */
+ POKEW /* MOVW AX, ES:[BX] */
+
+ FARJUMP16(0xFFFF, 0x0000) /* reset */
+
+/*
+ * Output a string to the display.
+ * String argument is in rSI.
+ */
+TEXT BIOSputs(SB), $0
+ PUSHA
+ CLR(rBX)
+_BIOSputs:
+ LODSB
+ ORB(rAL, rAL)
+ JEQ _BIOSputsret
+
+ LBI(0x0E, rAH)
+ BIOSCALL(0x10)
+ JMP _BIOSputs
+
+_BIOSputsret:
+ POPA
+ RET
+
+/* "No active entry in MBR" */
+TEXT noentry(SB), $0
+ BYTE $'N'; BYTE $'o'; BYTE $' '; BYTE $'a';
+ BYTE $'c'; BYTE $'t'; BYTE $'i'; BYTE $'v';
+ BYTE $'e'; BYTE $' '; BYTE $'e'; BYTE $'n';
+ BYTE $'t'; BYTE $'r'; BYTE $'y'; BYTE $' ';
+ BYTE $'i'; BYTE $'n'; BYTE $' '; BYTE $'M';
+ BYTE $'B'; BYTE $'R';
+ BYTE $'\z';
+
+/* "Invalid MBR" */
+TEXT invalidMBR(SB), $0
+ BYTE $'I'; BYTE $'n'; BYTE $'v'; BYTE $'a';
+ BYTE $'l'; BYTE $'i'; BYTE $'d'; BYTE $' ';
+ BYTE $'M'; BYTE $'B'; BYTE $'R';
+ BYTE $'\z';
+
+/* "I/O error" */
+TEXT ioerror(SB), $0
+ BYTE $'I'; BYTE $'/'; BYTE $'O'; BYTE $' ';
+ BYTE $'e'; BYTE $'r'; BYTE $'r'; BYTE $'o';
+ BYTE $'r';
+ BYTE $'\z';
+
+/* "Invalid PBS" */
+TEXT invalidPBS(SB), $0
+ BYTE $'I'; BYTE $'n'; BYTE $'v'; BYTE $'a';
+ BYTE $'l'; BYTE $'i'; BYTE $'d'; BYTE $' ';
+ BYTE $'P'; BYTE $'B'; BYTE $'S';
+ BYTE $'\z';
+
+/* "\r\nPress almost any key to reboot..." */
+TEXT reboot(SB), $0
+ BYTE $'\r';BYTE $'\n';
+ BYTE $'P'; BYTE $'r'; BYTE $'e'; BYTE $'s';
+ BYTE $'s'; BYTE $' '; BYTE $'a'; BYTE $'l';
+ BYTE $'m'; BYTE $'o'; BYTE $'s'; BYTE $'t';
+ BYTE $' '; BYTE $'a'; BYTE $'n'; BYTE $'y';
+ BYTE $' '; BYTE $'k'; BYTE $'e'; BYTE $'y';
+ BYTE $' '; BYTE $'t'; BYTE $'o'; BYTE $' ';
+ BYTE $'r'; BYTE $'e'; BYTE $'b'; BYTE $'o';
+ BYTE $'o'; BYTE $'t'; BYTE $'.'; BYTE $'.';
+ BYTE $'.';
+ BYTE $'\z';
+
+/* "MBR..." */
+TEXT confidence(SB), $0
+ BYTE $'M'; BYTE $'B'; BYTE $'R'; BYTE $'.';
+ BYTE $'.'; BYTE $'.';
+ BYTE $'\z';
diff --git a/os/boot/pc/mem.h b/os/boot/pc/mem.h
new file mode 100644
index 00000000..d33fee0e
--- /dev/null
+++ b/os/boot/pc/mem.h
@@ -0,0 +1,114 @@
+/*
+ * Memory and machine-specific definitions. Used in C and assembler.
+ */
+
+/*
+ * Sizes
+ */
+#define BI2BY 8 /* bits per byte */
+#define BI2WD 32 /* bits per word */
+#define BY2WD 4 /* bytes per word */
+#define BY2PG 4096 /* bytes per page */
+#define WD2PG (BY2PG/BY2WD) /* words per page */
+#define PGSHIFT 12 /* log(BY2PG) */
+#define PGROUND(s) (((s)+(BY2PG-1))&~(BY2PG-1))
+
+#define MAXMACH 1 /* max # cpus system can run */
+
+/*
+ * Time
+ */
+#define HZ (100) /* clock frequency */
+#define MS2HZ (1000/HZ) /* millisec per clock tick */
+#define TK2SEC(t) ((t)/HZ) /* ticks to seconds */
+#define TK2MS(x) ((x)*(1000/HZ))
+#define MS2TK(t) ((((ulong)(t))*HZ)/1000) /* milliseconds to ticks */
+
+/*
+ * Fundamental addresses
+ */
+#define IDTADDR 0x80000800 /* idt */
+#define APBOOTSTRAP 0x80001000 /* AP bootstrap code */
+#define CONFADDR 0x80001200 /* info passed from boot loader */
+#define CPU0PDB 0x80002000 /* bootstrap processor PDB */
+#define CPU0PTE 0x80003000 /* bootstrap processor PTE's for 0-4MB */
+#define MACHADDR 0x80004000 /* as seen by current processor */
+#define CPU0MACH 0x80005000 /* Mach for bootstrap processor */
+#define MACHSIZE (BY2PG*8) /* stack size */
+
+
+/*
+ * Address spaces
+ *
+ * Kernel is at 2GB-4GB
+ */
+#define KZERO 0x80000000 /* base of kernel address space */
+#define KTZERO KZERO /* first address in kernel text */
+#define ROMBIOS (KZERO|0xF0000)
+
+/*
+ * known 80386 segments (in GDT) and their selectors
+ */
+#define NULLSEG 0 /* null segment */
+#define KDSEG 1 /* kernel data/stack */
+#define KESEG 2 /* kernel executable */
+#define UDSEG 3 /* user data/stack */
+#define UESEG 4 /* user executable */
+#define SYSGATE 5 /* system call gate */
+#define TSSSEG 6 /* task segment */
+
+#define SELGDT (0<<3) /* selector is in gdt */
+#define SELLDT (1<<3) /* selector is in ldt */
+
+#define SELECTOR(i, t, p) (((i)<<3) | (t) | (p))
+
+#define NULLSEL SELECTOR(NULLSEG, SELGDT, 0)
+#define KESEL SELECTOR(KESEG, SELGDT, 0)
+#define KDSEL SELECTOR(KDSEG, SELGDT, 0)
+#define UESEL SELECTOR(UESEG, SELGDT, 3)
+#define UDSEL SELECTOR(UDSEG, SELGDT, 3)
+#define TSSSEL SELECTOR(TSSSEG, SELGDT, 0)
+
+/*
+ * fields in segment descriptors
+ */
+#define SEGDATA (0x10<<8) /* data/stack segment */
+#define SEGEXEC (0x18<<8) /* executable segment */
+#define SEGTSS (0x9<<8) /* TSS segment */
+#define SEGCG (0x0C<<8) /* call gate */
+#define SEGIG (0x0E<<8) /* interrupt gate */
+#define SEGTG (0x0F<<8) /* task gate */
+#define SEGTYPE (0x1F<<8)
+
+#define SEGP (1<<15) /* segment present */
+#define SEGPL(x) ((x)<<13) /* priority level */
+#define SEGB (1<<22) /* granularity 1==4k (for expand-down) */
+#define SEGG (1<<23) /* granularity 1==4k (for other) */
+#define SEGE (1<<10) /* expand down */
+#define SEGW (1<<9) /* writable (for data/stack) */
+#define SEGR (1<<9) /* readable (for code) */
+#define SEGD (1<<22) /* default 1==32bit (for code) */
+
+/*
+ * virtual MMU
+ */
+#define PTEMAPMEM (1024*1024) /* ??? */
+#define SEGMAPSIZE 16 /* ??? */
+#define PTEPERTAB (PTEMAPMEM/BY2PG) /* ??? */
+#define PPN(x) ((x)&~(BY2PG-1))
+
+/*
+ * physical MMU
+ */
+#define PTEVALID (1<<0)
+#define PTEUNCACHED 0 /* everything is uncached */
+#define PTEWRITE (1<<1)
+#define PTERONLY (0<<1)
+#define PTEKERNEL (0<<2)
+#define PTEUSER (1<<2)
+#define PTESIZE (1<<7)
+
+/*
+ * flag register bits that we care about
+ */
+#define IFLAG 0x200
diff --git a/os/boot/pc/memory.c b/os/boot/pc/memory.c
new file mode 100644
index 00000000..82da8b2e
--- /dev/null
+++ b/os/boot/pc/memory.c
@@ -0,0 +1,504 @@
+/*
+ * Size memory and create the kernel page-tables on the fly while doing so.
+ * Called from main(), this code should only be run by the bootstrap processor.
+ */
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#define MEMDEBUG 0
+
+#define PDX(va) ((((ulong)(va))>>22) & 0x03FF)
+#define PTX(va) ((((ulong)(va))>>12) & 0x03FF)
+
+enum {
+ MemUPA = 0, /* unbacked physical address */
+ MemRAM = 1, /* physical memory */
+ MemUMB = 2, /* upper memory block (<16MB) */
+ NMemType = 3,
+
+ KB = 1024,
+
+ MemMinMB = 4, /* minimum physical memory (<=4MB) */
+ MemMaxMB = 768, /* maximum physical memory to check */
+
+ NMemBase = 10,
+};
+
+typedef struct {
+ int size;
+ ulong addr;
+} Map;
+
+typedef struct {
+ char* name;
+ Map* map;
+ Map* mapend;
+
+ Lock;
+} RMap;
+
+static Map mapupa[8];
+static RMap rmapupa = {
+ "unallocated unbacked physical memory",
+ mapupa,
+ &mapupa[7],
+};
+
+static Map xmapupa[8];
+static RMap xrmapupa = {
+ "unbacked physical memory",
+ xmapupa,
+ &xmapupa[7],
+};
+
+static Map mapram[8];
+static RMap rmapram = {
+ "physical memory",
+ mapram,
+ &mapram[7],
+};
+
+static Map mapumb[64];
+static RMap rmapumb = {
+ "upper memory block",
+ mapumb,
+ &mapumb[63],
+};
+
+static Map mapumbrw[8];
+static RMap rmapumbrw = {
+ "UMB device memory",
+ mapumbrw,
+ &mapumbrw[7],
+};
+
+void
+memdebug(void)
+{
+ Map *mp;
+ ulong maxpa, maxpa1, maxpa2;
+
+ if(MEMDEBUG == 0)
+ return;
+
+ maxpa = (nvramread(0x18)<<8)|nvramread(0x17);
+ maxpa1 = (nvramread(0x31)<<8)|nvramread(0x30);
+ maxpa2 = (nvramread(0x16)<<8)|nvramread(0x15);
+ print("maxpa = %luX -> %luX, maxpa1 = %luX maxpa2 = %luX\n",
+ maxpa, MB+maxpa*KB, maxpa1, maxpa2);
+
+ for(mp = rmapram.map; mp->size; mp++)
+ print("%8.8luX %8.8luX %8.8luX\n", mp->addr, (ulong)mp->size, mp->addr+mp->size);
+ for(mp = rmapumb.map; mp->size; mp++)
+ print("%8.8luX %8.8luX %8.8luX\n", mp->addr, (ulong)mp->size, mp->addr+mp->size);
+ for(mp = rmapumbrw.map; mp->size; mp++)
+ print("%8.8luX %8.8luX %8.8luX\n", mp->addr, (ulong)mp->size, mp->addr+mp->size);
+ for(mp = rmapupa.map; mp->size; mp++)
+ print("%8.8luX %8.8luX %8.8luX\n", mp->addr, (ulong)mp->size, mp->addr+mp->size);
+}
+
+void
+mapfree(RMap* rmap, ulong addr, ulong size)
+{
+ Map *mp;
+ ulong t;
+
+ if(size == 0)
+ return;
+
+ lock(rmap);
+ for(mp = rmap->map; mp->addr <= addr && mp->size; mp++)
+ ;
+
+ if(mp > rmap->map && (mp-1)->addr+(mp-1)->size == addr){
+ (mp-1)->size += size;
+ if(addr+size == mp->addr){
+ (mp-1)->size += mp->size;
+ while(mp->size){
+ mp++;
+ (mp-1)->addr = mp->addr;
+ (mp-1)->size = mp->size;
+ }
+ }
+ }
+ else{
+ if(addr+size == mp->addr && mp->size){
+ mp->addr -= size;
+ mp->size += size;
+ }
+ else do{
+ if(mp >= rmap->mapend){
+ print("mapfree: %s: losing 0x%luX, %lud\n",
+ rmap->name, addr, size);
+ break;
+ }
+ t = mp->addr;
+ mp->addr = addr;
+ addr = t;
+ t = mp->size;
+ mp->size = size;
+ mp++;
+ }while(size = t);
+ }
+ unlock(rmap);
+}
+
+ulong
+mapalloc(RMap* rmap, ulong addr, int size, int align)
+{
+ Map *mp;
+ ulong maddr, oaddr;
+
+ lock(rmap);
+ for(mp = rmap->map; mp->size; mp++){
+ maddr = mp->addr;
+
+ if(addr){
+ /*
+ * A specific address range has been given:
+ * if the current map entry is greater then
+ * the address is not in the map;
+ * if the current map entry does not overlap
+ * the beginning of the requested range then
+ * continue on to the next map entry;
+ * if the current map entry does not entirely
+ * contain the requested range then the range
+ * is not in the map.
+ */
+ if(maddr > addr)
+ break;
+ if(mp->size < addr - maddr) /* maddr+mp->size < addr, but no overflow */
+ continue;
+ if(addr - maddr > mp->size - size) /* addr+size > maddr+mp->size, but no overflow */
+ break;
+ maddr = addr;
+ }
+
+ if(align > 0)
+ maddr = ((maddr+align-1)/align)*align;
+ if(mp->addr+mp->size-maddr < size)
+ continue;
+
+ oaddr = mp->addr;
+ mp->addr = maddr+size;
+ mp->size -= maddr-oaddr+size;
+ if(mp->size == 0){
+ do{
+ mp++;
+ (mp-1)->addr = mp->addr;
+ }while((mp-1)->size = mp->size);
+ }
+
+ unlock(rmap);
+ if(oaddr != maddr)
+ mapfree(rmap, oaddr, maddr-oaddr);
+
+ return maddr;
+ }
+ unlock(rmap);
+
+ return 0;
+}
+
+static void
+umbscan(void)
+{
+ uchar *p;
+
+ /*
+ * Scan the Upper Memory Blocks (0xA0000->0xF0000) for pieces
+ * which aren't used; they can be used later for devices which
+ * want to allocate some virtual address space.
+ * Check for two things:
+ * 1) device BIOS ROM. This should start with a two-byte header
+ * of 0x55 0xAA, followed by a byte giving the size of the ROM
+ * in 512-byte chunks. These ROM's must start on a 2KB boundary.
+ * 2) device memory. This is read-write.
+ * There are some assumptions: there's VGA memory at 0xA0000 and
+ * the VGA BIOS ROM is at 0xC0000. Also, if there's no ROM signature
+ * at 0xE0000 then the whole 64KB up to 0xF0000 is theoretically up
+ * for grabs; check anyway.
+ */
+ p = KADDR(0xD0000); /*RSC: changed from 0xC0000 */
+ while(p < (uchar*)KADDR(0xE0000)){
+ if (p[0] == 0x55 && p[1] == 0xAA) {
+ /* Skip p[2] chunks of 512 bytes. Test for 0x55 AA before
+ poking obtrusively, or else the Thinkpad X20 dies when
+ setting up the cardbus (PB) */
+ p += p[2] * 512;
+ continue;
+ }
+
+ p[0] = 0xCC;
+ p[2*KB-1] = 0xCC;
+ if(p[0] != 0xCC || p[2*KB-1] != 0xCC){
+ p[0] = 0x55;
+ p[1] = 0xAA;
+ p[2] = 4;
+ if(p[0] == 0x55 && p[1] == 0xAA){
+ p += p[2]*512;
+ continue;
+ }
+ if(p[0] == 0xFF && p[1] == 0xFF)
+ mapfree(&rmapumb, PADDR(p), 2*KB);
+ }
+ else
+ mapfree(&rmapumbrw, PADDR(p), 2*KB);
+ p += 2*KB;
+ }
+
+ p = KADDR(0xE0000);
+ if(p[0] != 0x55 || p[1] != 0xAA){
+ p[0] = 0xCC;
+ p[64*KB-1] = 0xCC;
+ if(p[0] != 0xCC && p[64*KB-1] != 0xCC)
+ mapfree(&rmapumb, PADDR(p), 64*KB);
+ }
+}
+
+
+void
+meminit(ulong)
+{
+ /* A hack to initialize unbacked physical memory. It's assumed PCI space is assigned by
+ the BIOS in the 0xF0000000 range and 9load never needs more than 0x2000... to run. These
+ values leave ample space for memory allocations for uninitialized PCI cards (e.g. cardbus
+ cards). (pb) */
+ ulong maxmem = 0x40000000;
+
+ umbscan();
+ mapfree(&rmapupa, maxmem, 0x00000000-maxmem);
+ if(MEMDEBUG)
+ memdebug();
+}
+
+ulong
+umbmalloc(ulong addr, int size, int align)
+{
+ ulong a;
+
+ if(a = mapalloc(&rmapumb, addr, size, align))
+ return (ulong)KADDR(a);
+
+ return 0;
+}
+
+void
+umbfree(ulong addr, int size)
+{
+ mapfree(&rmapumb, PADDR(addr), size);
+}
+
+ulong
+umbrwmalloc(ulong addr, int size, int align)
+{
+ ulong a;
+ uchar *p;
+
+ if(a = mapalloc(&rmapumbrw, addr, size, align))
+ return(ulong)KADDR(a);
+
+ /*
+ * Perhaps the memory wasn't visible before
+ * the interface is initialised, so try again.
+ */
+ if((a = umbmalloc(addr, size, align)) == 0)
+ return 0;
+ p = (uchar*)a;
+ p[0] = 0xCC;
+ p[size-1] = 0xCC;
+ if(p[0] == 0xCC && p[size-1] == 0xCC)
+ return a;
+ umbfree(a, size);
+
+ return 0;
+}
+
+void
+umbrwfree(ulong addr, int size)
+{
+ mapfree(&rmapumbrw, PADDR(addr), size);
+}
+
+ulong*
+mmuwalk(ulong* pdb, ulong va, int level, int create)
+{
+ ulong pa, *table;
+
+ /*
+ * Walk the page-table pointed to by pdb and return a pointer
+ * to the entry for virtual address va at the requested level.
+ * If the entry is invalid and create isn't requested then bail
+ * out early. Otherwise, for the 2nd level walk, allocate a new
+ * page-table page and register it in the 1st level.
+ */
+ table = &pdb[PDX(va)];
+ if(!(*table & PTEVALID) && create == 0)
+ return 0;
+
+ switch(level){
+
+ default:
+ return 0;
+
+ case 1:
+ return table;
+
+ case 2:
+ if(*table & PTESIZE)
+ panic("mmuwalk2: va 0x%ux entry 0x%ux\n", va, *table);
+ if(!(*table & PTEVALID)){
+ pa = PADDR(ialloc(BY2PG, BY2PG));
+ *table = pa|PTEWRITE|PTEVALID;
+ }
+ table = KADDR(PPN(*table));
+
+ return &table[PTX(va)];
+ }
+}
+
+static Lock mmukmaplock;
+
+ulong
+mmukmap(ulong pa, ulong va, int size)
+{
+ ulong pae, *table, *pdb, pgsz, *pte, x;
+ int pse, sync;
+ extern int cpuidax, cpuiddx;
+
+ pdb = KADDR(getcr3());
+ if((cpuiddx & 0x08) && (getcr4() & 0x10))
+ pse = 1;
+ else
+ pse = 0;
+ sync = 0;
+
+ pa = PPN(pa);
+ if(va == 0)
+ va = (ulong)KADDR(pa);
+ else
+ va = PPN(va);
+
+ pae = pa + size;
+ lock(&mmukmaplock);
+ while(pa < pae){
+ table = &pdb[PDX(va)];
+ /*
+ * Possibly already mapped.
+ */
+ if(*table & PTEVALID){
+ if(*table & PTESIZE){
+ /*
+ * Big page. Does it fit within?
+ * If it does, adjust pgsz so the correct end can be
+ * returned and get out.
+ * If not, adjust pgsz up to the next 4MB boundary
+ * and continue.
+ */
+ x = PPN(*table);
+ if(x != pa)
+ panic("mmukmap1: pa 0x%ux entry 0x%ux\n",
+ pa, *table);
+ x += 4*MB;
+ if(pae <= x){
+ pa = pae;
+ break;
+ }
+ pgsz = x - pa;
+ pa += pgsz;
+ va += pgsz;
+
+ continue;
+ }
+ else{
+ /*
+ * Little page. Walk to the entry.
+ * If the entry is valid, set pgsz and continue.
+ * If not, make it so, set pgsz, sync and continue.
+ */
+ pte = mmuwalk(pdb, va, 2, 0);
+ if(pte && *pte & PTEVALID){
+ x = PPN(*pte);
+ if(x != pa)
+ panic("mmukmap2: pa 0x%ux entry 0x%ux\n",
+ pa, *pte);
+ pgsz = BY2PG;
+ pa += pgsz;
+ va += pgsz;
+ sync++;
+
+ continue;
+ }
+ }
+ }
+
+ /*
+ * Not mapped. Check if it can be mapped using a big page -
+ * starts on a 4MB boundary, size >= 4MB and processor can do it.
+ * If not a big page, walk the walk, talk the talk.
+ * Sync is set.
+ */
+ if(pse && (pa % (4*MB)) == 0 && (pae >= pa+4*MB)){
+ *table = pa|PTESIZE|PTEWRITE|PTEUNCACHED|PTEVALID;
+ pgsz = 4*MB;
+ }
+ else{
+ pte = mmuwalk(pdb, va, 2, 1);
+ *pte = pa|PTEWRITE|PTEUNCACHED|PTEVALID;
+ pgsz = BY2PG;
+ }
+ pa += pgsz;
+ va += pgsz;
+ sync++;
+ }
+ unlock(&mmukmaplock);
+
+ /*
+ * If something was added
+ * then need to sync up.
+ */
+ if(sync)
+ putcr3(PADDR(pdb));
+
+ return pa;
+}
+
+ulong
+upamalloc(ulong addr, int size, int align)
+{
+ ulong ae, a;
+
+ USED(align);
+
+ if((a = mapalloc(&rmapupa, addr, size, align)) == 0){
+ memdebug();
+ return 0;
+ }
+
+ /*
+ * This is a travesty, but they all are.
+ */
+ ae = mmukmap(a, 0, size);
+
+ /*
+ * Should check here that it was all delivered
+ * and put it back and barf if not.
+ */
+ USED(ae);
+
+ /*
+ * Be very careful this returns a PHYSICAL address.
+ */
+ return a;
+}
+
+void
+upafree(ulong pa, int size)
+{
+ USED(pa, size);
+}
+
diff --git a/os/boot/pc/mkfile b/os/boot/pc/mkfile
new file mode 100644
index 00000000..57eef41e
--- /dev/null
+++ b/os/boot/pc/mkfile
@@ -0,0 +1,241 @@
+<../../../mkconfig
+objtype=386
+SYSTARG=$OSTARG
+OBJTYPE=386
+BIN=$ROOT/Inferno/$OBJTYPE
+LIBDIR=$ROOT/Inferno/$OBJTYPE/lib
+LIBDIRS=../libflate $ROOT/libkern
+LIBS=\
+ libflate\
+ libkern\
+
+LIBFILES=${LIBS:%=$LIBDIR/%.a}
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE
+
+BIN=$ROOT/Inferno/$OBJTYPE
+
+TARG=\
+ 9load\
+ 9pxeload\
+ 9loadlite\
+ 9loaddebug\
+ 9loadlitedebug\
+ ld.com\
+ mbr\
+ pbs\
+ pbslba\
+
+CORE=\
+ alarm.$O\
+ cga.$O\
+ clock.$O\
+ console.$O\
+ dosboot.$O\
+ devfloppy.$O\
+ dma.$O\
+ fs.$O\
+ ilock.$O\
+ kbd.$O\
+ kfsboot.$O\
+ print.$O\
+ queue.$O\
+ trap.$O\
+ getcallerpc.$O\
+
+LOAD=\
+ 8250.$O\
+ apm.$O\
+ boot.$O\
+ devpccard.$O\
+ conf.$O\
+ devi82365.$O\
+ devsd.$O\
+ inflate.$O\
+ load.$O\
+ memory.$O\
+ part.$O\
+ pci.$O\
+ sdata.$O\
+ sdmylex.$O\
+ sd53c8xx.$O\
+ sdscsi.$O\
+
+ETHER=\
+ bootp.$O\
+ eipfmt.$O\
+ ether.$O\
+ ether2114x.$O\
+ ether2000.$O\
+ ether589.$O\
+ ether79c970.$O\
+ ether8003.$O\
+ ether8139.$O\
+ ether8169.$O\
+ ether82557.$O\
+ ether83815.$O\
+ ether8390.$O\
+ etherec2t.$O\
+ etherelnk3.$O\
+ etherigbe.$O\
+ ethermii.$O\
+ etherrhine.$O\
+
+BCOM=\
+ bcom.$O\
+ bootld.$O\
+ devsd.$O\
+ memory.$O\
+ part.$O\
+ pci.$O\
+ sdata.$O\
+ sdscsi.$O\
+
+HFILES=\
+ lib.h\
+ mem.h\
+ dat.h\
+ fns.h\
+ io.h\
+
+CFLAGS=-FVw -I. -I$ROOT/Inferno/$OBJTYPE/include -I$ROOT/include
+
+all:V: $TARG
+
+9load: l.$O $CORE $LOAD $ETHER $LIBFILES
+ $LD -o $target -H3 -T0x80010000 -l $prereq
+ ls -l $target
+
+9pxeload: l.$O $CORE $LOAD $ETHER $LIBFILES
+ $LD -o $target -H3 -T0x80007C00 -l $prereq
+ ls -l $target
+
+9loaddebug: l.$O $CORE $LOAD $ETHER $LIBFILES
+ $LD -o $target -T0x80010000 -l $prereq
+ ls -l $target
+ # acid $target
+ # map({"text", 0x80010000, 0x80090000, 0x00000020})
+
+9loadlite: l.$O $CORE $LOAD noether.$O $LIBFILES
+ $LD -o $target -H3 -T0x80010000 -l $prereq
+ ls -l $target
+
+9loadlitedebug: l.$O $CORE $LOAD noether.$O $LIBFILES
+ $LD -o $target -T0x80010000 -l $prereq
+ ls -l $target
+ # acid $target
+ # map({"text", 0x80010000, 0x80090000, 0x00000020})
+
+ld.com: ld.$O $CORE $BCOM $LIBFILES
+ $LD -o $target -H3 -T0x80080100 -l $prereq
+ ls -l $target
+
+lddebug: ld.$O $CORE $BCOM $LIBFILES
+ $LD -o $target -T0x80080100 -l $prereq
+ ls -l $target
+ # acid $target
+ # map({"text", 0x80080100, 0x800B0000, 0x00000020})
+
+ld.$O: l.s
+ $AS -DDOTCOM -o $target l.s
+
+lpxe.$O: l.s
+ $AS -DPXE -o $target l.s
+
+%.$O: %.s
+ $AS $stem.s
+
+%.$O: %.c
+ $CC $CFLAGS $stem.c
+
+%.$O: $HFILES
+
+l.$O pbs.$O pbslba.$O mbr.$O: x16.h
+
+clock.$O floppy.$O trap.$O: ureg.h
+bcom.$O conf.$O devfloppy.$O devsd.$O dosboot.$O fs.$O \
+ kfsboot.$O load.$O part.$O: dosfs.h fs.h kfs.h
+ether.$O etherelnk3.$O: etherif.h
+devsd.$O part.$O sdata.$O sdscsi.$O: sd.h
+bootp.$O: ip.h
+
+mbr: mbr.$O
+ $LD -o $target -H3 -T0x0600 -l $prereq
+ ls -l $target
+
+pbs&: pbs%.$O
+ $LD -o $target -H3 -T0x7C00 -l $prereq
+ ls -l $target
+
+pbs&.debug: pbs%.$O
+ $LD -o $target -T0x7C00 -l $prereq
+ ls -l $target
+ # acid $target
+ # map({"text", 0x7C00, 0x7E00, 0x00000020})
+
+# added to cause libflate to be made automatically:
+
+$ROOT/Inferno/$OBJTYPE/lib/lib%.a:Q: all-$SHELLTYPE
+ #
+
+rc-lib%.a nt-lib%.a:VQ:
+ echo '@{builtin cd ' $ROOT/lib$stem ';mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install}'
+ @{builtin cd $ROOT/lib$stem ;mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE install}
+
+sh-lib%.a:VQ:
+ echo "(cd $ROOT/lib$stem ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install)"
+ (cd $ROOT/lib$stem ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE install)
+
+clean:
+ rm -f *.[$OS] [$OS].out y.tab.? y.debug y.output $TARG 9loaddebug lddebug
+
+install:V:
+ for (i in $TARG)
+ mk $MKFLAGS $i.install
+
+%.install:V: $BIN/%
+
+$BIN/%: %
+ cp $stem $BIN/$stem
+
+UPDATE=\
+ mkfile\
+ ${CORE:%.$O=%.c}\
+ ${LOAD:%.$O=%.c}\
+ ${BCOM:%.$O=%.c}\
+ ${ETHER:%.$O=%.c}\
+ $HFILES\
+ l.s\
+ noether.c\
+ pbs.s\
+ pbslba.s\
+ mbr.s\
+ x16.h\
+ ureg.h\
+ dosfs.h\
+ fs.h\
+ kfs.h\
+ etherif.h\
+ sd.h\
+ ip.h\
+ devfloppy.h\
+ ${TARG:%=/386/%}\
+
+update:V:
+ update $UPDATEFLAGS $UPDATE
+
+
+%-sh:QV:
+ for i in $LIBDIRS
+ do
+ echo "(cd $i ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE $stem)"
+ (cd $i; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE $stem)
+ done
+
+%-rc %-nt:QV:
+ for (i in $LIBDIRS)
+ {
+ echo '@{cd $i ; mk SHELLTYPE=$SHELLTYPE SYSTARG=$SYSTARG OBJTYPE=$OBJTYPE $stem}'
+ @{cd $i; mk 'SHELLTYPE='$SHELLTYPE 'SYSTARG='$SYSTARG 'OBJTYPE='$OBJTYPE $stem}
+ }
+
+nuke:V: clean nuke-$SHELLTYPE
diff --git a/os/boot/pc/noether.c b/os/boot/pc/noether.c
new file mode 100644
index 00000000..2162d1d4
--- /dev/null
+++ b/os/boot/pc/noether.c
@@ -0,0 +1,40 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+int
+etherinit(void)
+{
+ return -1;
+}
+
+void
+etherinitdev(int, char*)
+{
+}
+
+void
+etherprintdevs(int)
+{
+}
+
+int
+etherrxpkt(int, Etherpkt*, int)
+{
+ return -1;
+}
+
+int
+bootpboot(int, char*, Boot*)
+{
+ return -1;
+}
+
+void*
+pxegetfspart(int, char*, int)
+{
+ return nil;
+}
diff --git a/os/boot/pc/part.c b/os/boot/pc/part.c
new file mode 100644
index 00000000..8c703ae8
--- /dev/null
+++ b/os/boot/pc/part.c
@@ -0,0 +1,344 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+#include "sd.h"
+#include "fs.h"
+
+enum {
+ Npart = 32
+};
+
+uchar *mbrbuf, *partbuf;
+int nbuf;
+#define trace 0
+
+int
+tsdbio(SDunit *unit, SDpart *part, void *a, vlong off, int mbr)
+{
+ uchar *b;
+
+ if(sdbio(unit, part, a, unit->secsize, off) != unit->secsize){
+ if(trace)
+ print("%s: read %lud at %lld failed\n", unit->name,
+ unit->secsize, (vlong)part->start*unit->secsize+off);
+ return -1;
+ }
+ b = a;
+ if(mbr && (b[0x1FE] != 0x55 || b[0x1FF] != 0xAA)){
+ if(trace)
+ print("%s: bad magic %.2ux %.2ux at %lld\n",
+ unit->name, b[0x1FE], b[0x1FF],
+ (vlong)part->start*unit->secsize+off);
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * read partition table. The partition table is just ascii strings.
+ */
+#define MAGIC "plan9 partitions"
+static void
+oldp9part(SDunit *unit)
+{
+ SDpart *pp;
+ char *field[3], *line[Npart+1];
+ ulong n, start, end;
+ int i;
+
+ /*
+ * We have some partitions already.
+ */
+ pp = &unit->part[unit->npart];
+
+ /*
+ * We prefer partition tables on the second to last sector,
+ * but some old disks use the last sector instead.
+ */
+ strcpy(pp->name, "partition");
+ pp->start = unit->sectors - 2;
+ pp->end = unit->sectors - 1;
+
+ if(tsdbio(unit, pp, partbuf, 0, 0) < 0)
+ return;
+
+ if(strncmp((char*)partbuf, MAGIC, sizeof(MAGIC)-1) != 0) {
+ /* not found on 2nd last sector; look on last sector */
+ pp->start++;
+ pp->end++;
+ if(tsdbio(unit, pp, partbuf, 0, 0) < 0)
+ return;
+ if(strncmp((char*)partbuf, MAGIC, sizeof(MAGIC)-1) != 0)
+ return;
+ print("%s: using old plan9 partition table on last sector\n", unit->name);
+ }else
+ print("%s: using old plan9 partition table on 2nd-to-last sector\n", unit->name);
+
+ /* we found a partition table, so add a partition partition */
+ unit->npart++;
+ partbuf[unit->secsize-1] = '\0';
+
+ /*
+ * parse partition table
+ */
+ n = getfields((char*)partbuf, line, Npart+1, '\n');
+ if(n && strncmp(line[0], MAGIC, sizeof(MAGIC)-1) == 0){
+ for(i = 1; i < n && unit->npart < SDnpart; i++){
+ if(getfields(line[i], field, 3, ' ') != 3)
+ break;
+ start = strtoul(field[1], 0, 0);
+ end = strtoul(field[2], 0, 0);
+ if(start >= end || end > unit->sectors)
+ break;
+ sdaddpart(unit, field[0], start, end);
+ }
+ }
+}
+
+static void
+p9part(SDunit *unit, char *name)
+{
+ SDpart *p;
+ char *field[4], *line[Npart+1];
+ ulong start, end;
+ int i, n;
+
+ p = sdfindpart(unit, name);
+ if(p == nil)
+ return;
+
+ if(tsdbio(unit, p, partbuf, unit->secsize, 0) < 0)
+ return;
+ partbuf[unit->secsize-1] = '\0';
+
+ if(strncmp((char*)partbuf, "part ", 5) != 0)
+ return;
+
+ n = getfields((char*)partbuf, line, Npart+1, '\n');
+ if(n == 0)
+ return;
+ for(i = 0; i < n && unit->npart < SDnpart; i++){
+ if(strncmp(line[i], "part ", 5) != 0)
+ break;
+ if(getfields(line[i], field, 4, ' ') != 4)
+ break;
+ start = strtoul(field[2], 0, 0);
+ end = strtoul(field[3], 0, 0);
+ if(start >= end || end > unit->sectors)
+ break;
+ sdaddpart(unit, field[1], p->start+start, p->start+end);
+ }
+}
+
+int
+isdos(int t)
+{
+ return t==FAT12 || t==FAT16 || t==FATHUGE || t==FAT32 || t==FAT32X;
+}
+
+int
+isextend(int t)
+{
+ return t==EXTEND || t==EXTHUGE || t==LEXTEND;
+}
+
+/*
+ * Fetch the first dos and all plan9 partitions out of the MBR partition table.
+ * We return -1 if we did not find a plan9 partition.
+ */
+static int
+mbrpart(SDunit *unit)
+{
+ Dospart *dp;
+ ulong taboffset, start, end;
+ ulong firstxpart, nxtxpart;
+ int havedos, i, nplan9;
+ char name[10];
+
+ taboffset = 0;
+ dp = (Dospart*)&mbrbuf[0x1BE];
+ if(1) {
+ /* get the MBR (allowing for DMDDO) */
+ if(tsdbio(unit, &unit->part[0], mbrbuf, (vlong)taboffset*unit->secsize, 1) < 0)
+ return -1;
+ for(i=0; i<4; i++)
+ if(dp[i].type == DMDDO) {
+ if(trace)
+ print("DMDDO partition found\n");
+ taboffset = 63;
+ if(tsdbio(unit, &unit->part[0], mbrbuf, (vlong)taboffset*unit->secsize, 1) < 0)
+ return -1;
+ i = -1; /* start over */
+ }
+ }
+
+ /*
+ * Read the partitions, first from the MBR and then
+ * from successive extended partition tables.
+ */
+ nplan9 = 0;
+ havedos = 0;
+ firstxpart = 0;
+ for(;;) {
+ if(tsdbio(unit, &unit->part[0], mbrbuf, (vlong)taboffset*unit->secsize, 1) < 0)
+ return -1;
+ if(trace) {
+ if(firstxpart)
+ print("%s ext %lud ", unit->name, taboffset);
+ else
+ print("%s mbr ", unit->name);
+ }
+ nxtxpart = 0;
+ for(i=0; i<4; i++) {
+ if(trace)
+ print("dp %d...", dp[i].type);
+ start = taboffset+GLONG(dp[i].start);
+ end = start+GLONG(dp[i].len);
+
+ if(dp[i].type == PLAN9) {
+ if(nplan9 == 0)
+ strcpy(name, "plan9");
+ else
+ sprint(name, "plan9.%d", nplan9);
+ sdaddpart(unit, name, start, end);
+ p9part(unit, name);
+ nplan9++;
+ }
+
+ /*
+ * We used to take the active partition (and then the first
+ * when none are active). We have to take the first here,
+ * so that the partition we call ``dos'' agrees with the
+ * partition disk/fdisk calls ``dos''.
+ */
+ if(havedos==0 && isdos(dp[i].type)){
+ havedos = 1;
+ sdaddpart(unit, "dos", start, end);
+ }
+
+ /* nxtxpart is relative to firstxpart (or 0), not taboffset */
+ if(isextend(dp[i].type)){
+ nxtxpart = start-taboffset+firstxpart;
+ if(trace)
+ print("link %lud...", nxtxpart);
+ }
+ }
+ if(trace)
+ print("\n");
+
+ if(!nxtxpart)
+ break;
+ if(!firstxpart)
+ firstxpart = nxtxpart;
+ taboffset = nxtxpart;
+ }
+ return nplan9 ? 0 : -1;
+}
+
+/*
+ * To facilitate booting from CDs, we create a partition for
+ * the boot floppy image embedded in a bootable CD.
+ */
+static int
+part9660(SDunit *unit)
+{
+ uchar buf[2048];
+ ulong a, n;
+ uchar *p;
+
+ if(unit->secsize != 2048)
+ return -1;
+
+ if(sdbio(unit, &unit->part[0], buf, 2048, 17*2048) < 0)
+ return -1;
+
+ if(buf[0] || strcmp((char*)buf+1, "CD001\x01EL TORITO SPECIFICATION") != 0)
+ return -1;
+
+
+ p = buf+0x47;
+ a = p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
+
+ if(sdbio(unit, &unit->part[0], buf, 2048, a*2048) < 0)
+ return -1;
+
+ if(memcmp(buf, "\x01\x00\x00\x00", 4) != 0
+ || memcmp(buf+30, "\x55\xAA", 2) != 0
+ || buf[0x20] != 0x88)
+ return -1;
+
+ p = buf+0x28;
+ a = p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
+
+ switch(buf[0x21]){
+ case 0x01:
+ n = 1200*1024;
+ break;
+ case 0x02:
+ n = 1440*1024;
+ break;
+ case 0x03:
+ n = 2880*1024;
+ break;
+ default:
+ return -1;
+ }
+ n /= 2048;
+
+ print("found partition %s!cdboot; %lud+%lud\n", unit->name, a, n);
+ sdaddpart(unit, "cdboot", a, a+n);
+ return 0;
+}
+
+enum {
+ NEW = 1<<0,
+ OLD = 1<<1
+};
+
+void
+partition(SDunit *unit)
+{
+ int type;
+ char *p;
+
+ if(unit->part == 0)
+ return;
+
+ if(part9660(unit) == 0)
+ return;
+
+ p = getconf("partition");
+ if(p == nil)
+ p = defaultpartition;
+
+ if(p != nil && strncmp(p, "new", 3) == 0)
+ type = NEW;
+ else if(p != nil && strncmp(p, "old", 3) == 0)
+ type = OLD;
+ else
+ type = NEW|OLD;
+
+ if(nbuf < unit->secsize) {
+ free(mbrbuf);
+ free(partbuf);
+ mbrbuf = malloc(unit->secsize);
+ partbuf = malloc(unit->secsize);
+ if(mbrbuf==nil || partbuf==nil) {
+ free(mbrbuf);
+ free(partbuf);
+ partbuf = mbrbuf = nil;
+ nbuf = 0;
+ return;
+ }
+ nbuf = unit->secsize;
+ }
+
+ if((type & NEW) && mbrpart(unit) >= 0){
+ /* nothing to do */;
+ }
+ else if(type & OLD)
+ oldp9part(unit);
+}
diff --git a/os/boot/pc/pbs.s b/os/boot/pc/pbs.s
new file mode 100644
index 00000000..fde5ec39
--- /dev/null
+++ b/os/boot/pc/pbs.s
@@ -0,0 +1,372 @@
+/*
+ * FAT Partition Boot Sector. Loaded at 0x7C00:
+ * 8a pbs.s; 8l -o pbs -l -H3 -T0x7C00 pbs.8
+ * Will load the target at LOADSEG*16+LOADOFF, so the target
+ * should be probably be loaded with LOADOFF added to the
+ * -Taddress.
+ * If LOADSEG is a multiple of 64KB and LOADOFF is 0 then
+ * targets larger than 64KB can be loaded.
+ *
+ * This code uses the traditional INT13 BIOS interface and can
+ * therefore only access the first 8.4GB of the disc.
+ *
+ * It relies on the _volid field in the FAT header containing
+ * the LBA of the root directory.
+ */
+#include "x16.h"
+#include "mem.h"
+
+#define LOADSEG (0x10000/16) /* where to load code (64KB) */
+#define LOADOFF 0
+#define DIROFF 0x0200 /* where to read the root directory */
+
+/*
+ * FAT directory entry.
+ */
+#define Dname 0x00
+#define Dext 0x08
+#define Dattr 0x0B
+#define Dtime 0x16
+#define Ddate 0x18
+#define Dstart 0x1A
+#define Dlengthlo 0x1C
+#define Dlengthhi 0x1E
+
+#define Dirsz 0x20
+
+/*
+ * Data is kept on the stack, indexed by rBP.
+ */
+#define Xdap 0x00 /* disc address packet */
+#define Xrootsz 0x10 /* file data area */
+#define Xdrive 0x12 /* boot drive, passed by BIOS or MBR */
+#define Xtotal 0x14 /* sum of allocated data above */
+
+TEXT _magic(SB), $0
+ BYTE $0xEB; BYTE $0x3C; /* jmp .+ 0x3C (_start0x3E) */
+ BYTE $0x90 /* nop */
+TEXT _version(SB), $0
+ BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+ BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00
+TEXT _sectsize(SB), $0
+ BYTE $0x00; BYTE $0x00
+TEXT _clustsize(SB), $0
+ BYTE $0x00
+TEXT _nresrv(SB), $0
+ BYTE $0x00; BYTE $0x00
+TEXT _nfats(SB), $0
+ BYTE $0x00
+TEXT _rootsize(SB), $0
+ BYTE $0x00; BYTE $0x00
+TEXT _volsize(SB), $0
+ BYTE $0x00; BYTE $0x00
+TEXT _mediadesc(SB), $0
+ BYTE $0x00
+TEXT _fatsize(SB), $0
+ BYTE $0x00; BYTE $0x00
+TEXT _trksize(SB), $0
+ BYTE $0x00; BYTE $0x00
+TEXT _nheads(SB), $0
+ BYTE $0x00; BYTE $0x00
+TEXT _nhiddenlo(SB), $0
+ BYTE $0x00; BYTE $0x00
+TEXT _nhiddenhi(SB), $0
+ BYTE $0x00; BYTE $0x00;
+TEXT _bigvolsize(SB), $0
+ BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+TEXT _driveno(SB), $0
+ BYTE $0x00
+TEXT _reserved0(SB), $0
+ BYTE $0x00
+TEXT _bootsig(SB), $0
+ BYTE $0x00
+TEXT _volid(SB), $0
+ BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+TEXT _label(SB), $0
+ BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+ BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00
+ BYTE $0x00; BYTE $0x00; BYTE $0x00
+TEXT _type(SB), $0
+ BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+ BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+
+_start0x3E:
+ CLI
+ CLR(rAX)
+ MTSR(rAX, rSS) /* 0000 -> rSS */
+ MTSR(rAX, rDS) /* 0000 -> rDS, source segment */
+ MTSR(rAX, rES)
+ LWI(_magic-Xtotal(SB), rSP)
+ MW(rSP, rBP) /* set the indexed-data pointer */
+
+ SBPB(rDL, Xdrive) /* save the boot drive */
+
+ /* booting from a CD starts us at 7C0:0. Move to 0:7C00 */
+ PUSHR(rAX)
+ LWI(_nxt(SB), rAX)
+ PUSHR(rAX)
+ BYTE $0xCB /* FAR RET */
+
+TEXT _nxt(SB), $0
+ STI
+
+ LWI(confidence(SB), rSI) /* for that warm, fuzzy feeling */
+ CALL16(BIOSputs(SB))
+
+ CALL16(dreset(SB))
+
+_jmp00:
+ LW(_volid(SB), rAX) /* Xrootlo */
+ LW(_volid+2(SB), rDX) /* Xroothi */
+
+ LWI(_magic+DIROFF(SB), rBX)
+ CALL16(BIOSread(SB)) /* read the root directory */
+
+ LWI((512/Dirsz), rBX)
+
+ LWI(_magic+DIROFF(SB), rDI) /* compare first directory entry */
+
+_cmp00:
+ PUSHR(rDI) /* save for later if it matches */
+ LWI(bootfile(SB), rSI)
+ LWI(Dattr, rCX)
+ REP
+ CMPSB
+ POPR(rDI)
+ JEQ _jmp02
+
+ DEC(rBX)
+ JEQ _jmp01
+
+ ADDI(Dirsz, rDI)
+ JMP _cmp00
+_jmp01:
+ CALL16(buggery(SB))
+
+_jmp02:
+ CLR(rBX) /* a handy value */
+ LW(_rootsize(SB), rAX) /* calculate and save Xrootsz */
+ LWI(Dirsz, rCX)
+ MUL(rCX)
+ LW(_sectsize(SB), rCX)
+ PUSHR(rCX)
+ DEC(rCX)
+ ADD(rCX, rAX)
+ ADC(rBX, rDX)
+ POPR(rCX) /* _sectsize(SB) */
+ DIV(rCX)
+ PUSHR(rAX) /* Xrootsz */
+
+ /*
+ * rDI points to the matching directory entry.
+ */
+ LXW(Dstart, xDI, rAX) /* starting sector address */
+ DEC(rAX) /* that's just the way it is */
+ DEC(rAX)
+ LB(_clustsize(SB), rCL)
+ CLRB(rCH)
+ MUL(rCX)
+ LW(_volid(SB), rCX) /* Xrootlo */
+ ADD(rCX, rAX)
+ LW(_volid+2(SB), rCX) /* Xroothi */
+ ADC(rCX, rDX)
+ POPR(rCX) /* Xrootsz */
+ ADD(rCX, rAX)
+ ADC(rBX, rDX)
+
+ PUSHR(rAX) /* calculate how many sectors to read */
+ PUSHR(rDX)
+ LXW(Dlengthlo, xDI, rAX)
+ LXW(Dlengthhi, xDI, rDX)
+ LW(_sectsize(SB), rCX)
+ PUSHR(rCX)
+ DEC(rCX)
+ ADD(rCX, rAX)
+ ADC(rBX, rDX)
+ POPR(rCX) /* _sectsize(SB) */
+ DIV(rCX)
+ MW(rAX, rCX)
+ POPR(rDX)
+ POPR(rAX)
+
+ LWI(LOADSEG, rBX) /* address to load into (seg+offset) */
+ MTSR(rBX, rES) /* seg */
+ LWI(LOADOFF, rBX) /* offset */
+
+_readboot:
+ CALL16(BIOSread(SB)) /* read the sector */
+
+ LW(_sectsize(SB), rDI) /* bump addresses/counts */
+ ADD(rDI, rBX)
+ JCC _incsecno
+
+ MFSR(rES, rDI) /* next 64KB segment */
+ ADDI(0x1000, rDI)
+ MTSR(rDI, rES)
+
+_incsecno:
+ CLR(rDI)
+ INC(rAX)
+ ADC(rDI, rDX)
+ LOOP _readboot
+
+ LWI(LOADSEG, rDI) /* set rDS for loaded code */
+ MTSR(rDI, rDS)
+ FARJUMP16(LOADSEG, LOADOFF) /* no deposit, no return */
+
+TEXT buggery(SB), $0
+ LWI(error(SB), rSI)
+ CALL16(BIOSputs(SB))
+
+_wait:
+ CLR(rAX) /* wait for almost any key */
+ BIOSCALL(0x16)
+
+_reset:
+ CLR(rBX) /* set ES segment for BIOS area */
+ MTSR(rBX, rES)
+
+ LWI(0x0472, rBX) /* warm-start code address */
+ LWI(0x1234, rAX) /* warm-start code */
+ POKEW /* MOVW AX, ES:[BX] */
+
+ FARJUMP16(0xFFFF, 0x0000) /* reset */
+
+/*
+ * Read a sector from a disc. On entry:
+ * rDX:rAX sector number
+ * rES:rBX buffer address
+ * For BIOSCALL(0x13):
+ * rAH 0x02
+ * rAL number of sectors to read (1)
+ * rCH low 8 bits of cylinder
+ * rCL high 2 bits of cylinder (7-6), sector (5-0)
+ * rDH head
+ * rDL drive
+ * rES:rBX buffer address
+ */
+TEXT BIOSread(SB), $0
+ LWI(5, rDI) /* retry count (ATAPI ZIPs suck) */
+_retry:
+ PUSHA /* may be trashed by BIOSCALL */
+ PUSHR(rBX)
+
+ LW(_trksize(SB), rBX)
+ LW(_nheads(SB), rDI)
+ IMUL(rDI, rBX)
+ OR(rBX, rBX)
+ JZ _ioerror
+
+_okay:
+ DIV(rBX) /* cylinder -> rAX, track,sector -> rDX */
+
+ MW(rAX, rCX) /* save cylinder */
+ ROLI(0x08, rCX) /* swap rC[HL] */
+ SHLBI(0x06, rCL) /* move high bits up */
+
+ MW(rDX, rAX)
+ CLR(rDX)
+ LW(_trksize(SB), rBX)
+
+ DIV(rBX) /* head -> rAX, sector -> rDX */
+
+ INC(rDX) /* sector numbers are 1-based */
+ ANDI(0x003F, rDX) /* should not be necessary */
+ OR(rDX, rCX)
+
+ MW(rAX, rDX)
+ SHLI(0x08, rDX) /* form head */
+ LBPB(Xdrive, rDL) /* form drive */
+
+ POPR(rBX)
+ LWI(0x0201, rAX) /* form command and sectors */
+ BIOSCALL(0x13) /* CF set on failure */
+ JCC _BIOSreadret
+
+ POPA
+ DEC(rDI) /* too many retries? */
+ JEQ _ioerror
+
+ CALL16(dreset(SB))
+ JMP _retry
+
+_ioerror:
+ LWI(ioerror(SB), rSI)
+ CALL16(BIOSputs(SB))
+ JMP _wait
+
+_BIOSreadret:
+ POPA
+ RET
+
+TEXT dreset(SB), $0
+ PUSHA
+ CLR(rAX) /* rAH == 0 == reset disc system */
+ LBPB(Xdrive, rDL)
+ BIOSCALL(0x13)
+ ORB(rAH, rAH) /* status (0 == success) */
+ POPA
+ JNE _ioerror
+ RET
+
+/*
+ * Output a string to the display.
+ * String argument is in rSI.
+ */
+TEXT BIOSputs(SB), $0
+ PUSHA
+ CLR(rBX)
+_BIOSputs:
+ LODSB
+ ORB(rAL, rAL)
+ JEQ _BIOSputsret
+
+ LBI(0x0E, rAH)
+ BIOSCALL(0x10)
+ JMP _BIOSputs
+
+_BIOSputsret:
+ POPA
+ RET
+
+/* "Bad format or I/O error\r\nPress almost any key to reboot..."*/
+TEXT error(SB), $0
+ BYTE $'B'; BYTE $'a'; BYTE $'d'; BYTE $' ';
+ BYTE $'f'; BYTE $'o'; BYTE $'r'; BYTE $'m';
+ BYTE $'a'; BYTE $'t'; BYTE $' '; BYTE $'o';
+ BYTE $'r'; BYTE $' ';
+/* "I/O error\r\nPress almost any key to reboot..." */
+TEXT ioerror(SB), $0
+ BYTE $'I'; BYTE $'/'; BYTE $'O'; BYTE $' ';
+ BYTE $'e'; BYTE $'r'; BYTE $'r'; BYTE $'o';
+ BYTE $'r'; BYTE $'\r';BYTE $'\n';
+ BYTE $'P'; BYTE $'r'; BYTE $'e'; BYTE $'s';
+ BYTE $'s'; BYTE $' '; BYTE $'a'; BYTE $' ';
+ BYTE $'k'; BYTE $'e'; BYTE $'y';
+ BYTE $' '; BYTE $'t'; BYTE $'o'; BYTE $' ';
+ BYTE $'r'; BYTE $'e'; BYTE $'b'; BYTE $'o';
+ BYTE $'o'; BYTE $'t';
+ BYTE $'.'; BYTE $'.'; BYTE $'.';
+ BYTE $'\z';
+
+#ifdef USEBCOM
+/* "B COM" */
+TEXT bootfile(SB), $0
+ BYTE $'B'; BYTE $' '; BYTE $' '; BYTE $' ';
+ BYTE $' '; BYTE $' '; BYTE $' '; BYTE $' ';
+ BYTE $'C'; BYTE $'O'; BYTE $'M';
+ BYTE $'\z';
+#else
+/* "9LOAD " */
+TEXT bootfile(SB), $0
+ BYTE $'9'; BYTE $'L'; BYTE $'O'; BYTE $'A';
+ BYTE $'D'; BYTE $' '; BYTE $' '; BYTE $' ';
+ BYTE $' '; BYTE $' '; BYTE $' ';
+ BYTE $'\z';
+#endif /* USEBCOM */
+
+/* "PBS..." */
+TEXT confidence(SB), $0
+ BYTE $'P'; BYTE $'B'; BYTE $'S'; BYTE $'.';
+ BYTE $'.'; BYTE $'.';
+ BYTE $'\z';
diff --git a/os/boot/pc/pbsdisk b/os/boot/pc/pbsdisk
new file mode 100755
index 00000000..cae49ba7
--- /dev/null
+++ b/os/boot/pc/pbsdisk
Binary files differ
diff --git a/os/boot/pc/pbsdisk.s b/os/boot/pc/pbsdisk.s
new file mode 100644
index 00000000..f879e79e
--- /dev/null
+++ b/os/boot/pc/pbsdisk.s
@@ -0,0 +1,344 @@
+/*
+ * Debugging boot sector. Reads the first directory
+ * sector from disk and displays it.
+ *
+ * It relies on the _volid field in the FAT header containing
+ * the LBA of the root directory.
+ */
+#include "x16.h"
+
+#define DIROFF 0x00200 /* where to read the root directory (offset) */
+#define LOADSEG (0x10000/16) /* where to load code (64KB) */
+#define LOADOFF 0
+
+/*
+ * FAT directory entry.
+ */
+#define Dname 0x00
+#define Dext 0x08
+#define Dattr 0x0B
+#define Dtime 0x16
+#define Ddate 0x18
+#define Dstart 0x1A
+#define Dlengthlo 0x1C
+#define Dlengthhi 0x1E
+
+#define Dirsz 0x20
+
+/*
+ * We keep data on the stack, indexed by rBP.
+ */
+#define Xdrive 0x00 /* boot drive, passed by BIOS in rDL */
+#define Xrootlo 0x02 /* offset of root directory */
+#define Xroothi 0x04
+#define Xrootsz 0x06 /* file data area */
+#define Xtotal 0x08 /* sum of allocated data above */
+#define Xdap 0x00 /* disc address packet */
+
+TEXT _magic(SB), $0
+ BYTE $0xEB; BYTE $0x3C; /* jmp .+ 0x3C (_start0x3E) */
+ BYTE $0x90 /* nop */
+TEXT _version(SB), $0
+ BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+ BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00
+TEXT _sectsize(SB), $0
+ BYTE $0x00; BYTE $0x00
+TEXT _clustsize(SB), $0
+ BYTE $0x00
+TEXT _nresrv(SB), $0
+ BYTE $0x00; BYTE $0x00
+TEXT _nfats(SB), $0
+ BYTE $0x00
+TEXT _rootsize(SB), $0
+ BYTE $0x00; BYTE $0x00
+TEXT _volsize(SB), $0
+ BYTE $0x00; BYTE $0x00
+TEXT _mediadesc(SB), $0
+ BYTE $0x00
+TEXT _fatsize(SB), $0
+ BYTE $0x00; BYTE $0x00
+TEXT _trksize(SB), $0
+ BYTE $0x00; BYTE $0x00
+TEXT _nheads(SB), $0
+ BYTE $0x00; BYTE $0x00
+TEXT _nhiddenlo(SB), $0
+ BYTE $0x00; BYTE $0x00
+TEXT _nhiddenhi(SB), $0
+ BYTE $0x00; BYTE $0x00;
+TEXT _bigvolsize(SB), $0
+ BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+TEXT _driveno(SB), $0
+ BYTE $0x00
+TEXT _reserved0(SB), $0
+ BYTE $0x00
+TEXT _bootsig(SB), $0
+ BYTE $0x00
+TEXT _volid(SB), $0
+ BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+TEXT _label(SB), $0
+ BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+ BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00
+ BYTE $0x00; BYTE $0x00; BYTE $0x00
+TEXT _type(SB), $0
+ BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+ BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+
+_start0x3E:
+ CLI
+ CLR(rAX)
+ MTSR(rAX, rSS) /* 0000 -> rSS */
+ MTSR(rAX, rDS) /* 0000 -> rDS, source segment */
+ MTSR(rAX, rES)
+ LWI(_magic-Xtotal(SB), rSP)
+ MW(rSP, rBP) /* set the indexed-data pointer */
+ SBPB(rDL, Xdrive) /* save the boot drive */
+
+ /* VMware starts us at 7C0:0. Move to 0:7C00 */
+ PUSHR(rAX)
+ LWI(_nxt(SB), rAX)
+ PUSHR(rAX)
+ BYTE $0xCB /* FAR RET */
+
+TEXT _nxt(SB), $0
+ STI
+ LWI(confidence(SB), rSI) /* for that warm, fuzzy feeling */
+ CALL16(BIOSputs(SB))
+
+ CALL16(dreset(SB))
+
+_jmp00:
+ LW(_volid(SB), rAX) /* Xrootlo */
+ LW(_volid+2(SB), rDX) /* Xroothi */
+
+ LWI(_magic+DIROFF(SB), rBX)
+ CALL16(BIOSread(SB)) /* read the root directory */
+
+ CALL16(printnl(SB))
+ LWI(_magic+DIROFF(SB), rBX)
+ LWI((512/2), rCX)
+ CALL16(printbuf(SB))
+
+xloop:
+ JMP xloop
+
+
+TEXT buggery(SB), $0
+ LWI(error(SB), rSI)
+ CALL16(BIOSputs(SB))
+
+TEXT quietbuggery(SB), $0
+xbuggery:
+ JMP xbuggery
+
+/*
+ * Read a sector from a disc. On entry:
+ * rDX:rAX sector number
+ * rES:rBX buffer address
+ * For BIOSCALL(0x13):
+ * rAH 0x02
+ * rAL number of sectors to read (1)
+ * rCH low 8 bits of cylinder
+ * rCL high 2 bits of cylinder (7-6), sector (5-0)
+ * rDH head
+ * rDL drive
+ * rES:rBX buffer address
+ */
+TEXT BIOSread(SB), $0
+ LWI(5, rDI) /* retry count (ATAPI ZIPs suck) */
+_retry:
+ PUSHA /* may be trashed by BIOSCALL */
+ PUSHR(rBX)
+
+ LW(_trksize(SB), rBX)
+ LW(_nheads(SB), rDI)
+ IMUL(rDI, rBX)
+ OR(rBX, rBX)
+ JZ _ioerror
+
+_okay:
+ DIV(rBX) /* cylinder -> rAX, track,sector -> rDX */
+
+ MW(rAX, rCX) /* save cylinder */
+ ROLI(0x08, rCX) /* swap rC[HL] */
+ SHLBI(0x06, rCL) /* move high bits up */
+
+ MW(rDX, rAX)
+ CLR(rDX)
+ LW(_trksize(SB), rBX)
+
+ DIV(rBX) /* head -> rAX, sector -> rDX */
+
+ INC(rDX) /* sector numbers are 1-based */
+ ANDI(0x003F, rDX) /* should not be necessary */
+ OR(rDX, rCX)
+
+ MW(rAX, rDX)
+ SHLI(0x08, rDX) /* form head */
+ LBPB(Xdrive, rDL) /* form drive */
+
+ POPR(rBX)
+ LWI(0x0201, rAX) /* form command and sectors */
+ BIOSCALL(0x13) /* CF set on failure */
+ JCC _BIOSreadret
+
+ POPA
+ DEC(rDI) /* too many retries? */
+ JEQ _ioerror
+
+ CALL16(dreset(SB))
+ JMP _retry
+
+_ioerror:
+ LWI(ioerror(SB), rSI)
+ CALL16(BIOSputs(SB))
+ JMP xbuggery
+
+_BIOSreadret:
+ POPA
+ RET
+
+TEXT dreset(SB), $0
+ PUSHA
+ CLR(rAX) /* rAH == 0 == reset disc system */
+ LBPB(Xdrive, rDL)
+ BIOSCALL(0x13)
+ ORB(rAH, rAH) /* status (0 == success) */
+ POPA
+ JNE _ioerror
+ RET
+
+TEXT printsharp(SB), $0
+ LWI(sharp(SB), rSI)
+_doprint:
+ CALL16(BIOSputs(SB))
+ RET
+
+TEXT printspace(SB), $0
+ LWI(space(SB), rSI)
+ JMP _doprint
+
+TEXT printnl(SB), $0
+ LWI(nl(SB), rSI)
+ JMP _doprint
+
+/*
+ * Output a string to the display.
+ * String argument is in rSI.
+ */
+TEXT BIOSputs(SB), $0
+ PUSHA
+ CLR(rBX)
+_BIOSputs:
+ LODSB
+ ORB(rAL, rAL)
+ JEQ _BIOSputsret
+
+ LBI(0x0E, rAH)
+ BIOSCALL(0x10)
+ JMP _BIOSputs
+
+_BIOSputsret:
+ POPA
+ RET
+
+/*
+ * Output a register to the display.
+ */
+TEXT printAX(SB), $0
+ PUSHW(rAX)
+ PUSHW(rBX)
+ PUSHW(rCX)
+ PUSHW(rDI)
+
+ LWI(4, rCX)
+ LWI(numbuf+4(SB), rSI)
+
+_nextchar:
+ DEC(rSI)
+ MW(rAX, rBX)
+ ANDI(0x000F, rBX)
+ ADDI(0x30, rBX) /* 0x30 = '0' */
+ CMPI(0x39, rBX) /* 0x39 = '9' */
+ JLE _dowrite
+ ADDI(0x07, rBX) /* 0x07 = 'A'-(1+'9')*/
+
+_dowrite:
+ SXB(rBL, 0, xSI)
+ SHRI(4, rAX)
+
+ DEC(rCX)
+ JNE _nextchar
+
+ LWI(numbuf(SB), rSI)
+ CALL16(BIOSputs(SB))
+
+ POPW(rDI)
+ POPW(rCX)
+ POPW(rBX)
+ POPW(rAX)
+
+ CALL16(printspace(SB))
+ RET
+
+TEXT printDXAX(SB), $0
+ PUSHW(rAX)
+ MW(rDX, rAX)
+ CALL16(printAX(SB))
+ POPW(rAX)
+ CALL16(printAX(SB))
+ RET
+
+TEXT printBX(SB), $0
+ PUSHW(rAX)
+ MW(rBX, rAX)
+ CALL16(printAX(SB))
+ POPW(rAX)
+ RET
+
+/*
+ * Output some number of words to the display
+ * rDS:rDI - buffer
+ * rCX: number of words
+ */
+TEXT printbuf(SB), $0
+ PUSHW(rAX)
+ PUSHW(rBX)
+ PUSHW(rCX)
+
+_nextword:
+ LXW(0, xBX, rAX)
+ CALL16(printAX(SB))
+ INC(rBX)
+ INC(rBX)
+ DEC(rCX)
+ JNE _nextword
+
+ POPW(rCX)
+ POPW(rBX)
+ POPW(rAX)
+ RET
+
+TEXT error(SB), $0
+ BYTE $'E';
+
+TEXT ioerror(SB), $0
+ BYTE $'I';
+
+TEXT nl(SB), $0
+ BYTE $'\r';
+ BYTE $'\n';
+ BYTE $'\z';
+
+TEXT numbuf(SB), $0
+ BYTE $'X'; BYTE $'X'; BYTE $'X'; BYTE $'X';
+ BYTE $'\z';
+
+TEXT space(SB), $0
+ BYTE $' ';
+ BYTE $'\z';
+
+TEXT sharp(SB), $0
+ BYTE $'#'; BYTE $'\z';
+
+TEXT confidence(SB), $0
+ BYTE $'P'; BYTE $'\z'
diff --git a/os/boot/pc/pbsdisklba b/os/boot/pc/pbsdisklba
new file mode 100755
index 00000000..45c2ab81
--- /dev/null
+++ b/os/boot/pc/pbsdisklba
Binary files differ
diff --git a/os/boot/pc/pbsdisklba.s b/os/boot/pc/pbsdisklba.s
new file mode 100644
index 00000000..357abd00
--- /dev/null
+++ b/os/boot/pc/pbsdisklba.s
@@ -0,0 +1,327 @@
+/*
+ * Debugging boot sector. Reads the first directory
+ * sector from disk and displays it.
+ *
+ * It relies on the _volid field in the FAT header containing
+ * the LBA of the root directory.
+ */
+#include "x16.h"
+
+#define DIROFF 0x00200 /* where to read the root directory (offset) */
+#define LOADSEG (0x10000/16) /* where to load code (64KB) */
+#define LOADOFF 0
+
+/*
+ * FAT directory entry.
+ */
+#define Dname 0x00
+#define Dext 0x08
+#define Dattr 0x0B
+#define Dtime 0x16
+#define Ddate 0x18
+#define Dstart 0x1A
+#define Dlengthlo 0x1C
+#define Dlengthhi 0x1E
+
+#define Dirsz 0x20
+
+/*
+ * We keep data on the stack, indexed by rBP.
+ */
+#define Xdrive 0x00 /* boot drive, passed by BIOS in rDL */
+#define Xrootlo 0x02 /* offset of root directory */
+#define Xroothi 0x04
+#define Xrootsz 0x06 /* file data area */
+#define Xtotal 0x08 /* sum of allocated data above */
+#define Xdap 0x00 /* disc address packet */
+
+TEXT _magic(SB), $0
+ BYTE $0xEB; BYTE $0x3C; /* jmp .+ 0x3C (_start0x3E) */
+ BYTE $0x90 /* nop */
+TEXT _version(SB), $0
+ BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+ BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00
+TEXT _sectsize(SB), $0
+ BYTE $0x00; BYTE $0x00
+TEXT _clustsize(SB), $0
+ BYTE $0x00
+TEXT _nresrv(SB), $0
+ BYTE $0x00; BYTE $0x00
+TEXT _nfats(SB), $0
+ BYTE $0x00
+TEXT _rootsize(SB), $0
+ BYTE $0x00; BYTE $0x00
+TEXT _volsize(SB), $0
+ BYTE $0x00; BYTE $0x00
+TEXT _mediadesc(SB), $0
+ BYTE $0x00
+TEXT _fatsize(SB), $0
+ BYTE $0x00; BYTE $0x00
+TEXT _trksize(SB), $0
+ BYTE $0x00; BYTE $0x00
+TEXT _nheads(SB), $0
+ BYTE $0x00; BYTE $0x00
+TEXT _nhiddenlo(SB), $0
+ BYTE $0x00; BYTE $0x00
+TEXT _nhiddenhi(SB), $0
+ BYTE $0x00; BYTE $0x00;
+TEXT _bigvolsize(SB), $0
+ BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+TEXT _driveno(SB), $0
+ BYTE $0x00
+TEXT _reserved0(SB), $0
+ BYTE $0x00
+TEXT _bootsig(SB), $0
+ BYTE $0x00
+TEXT _volid(SB), $0
+ BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+TEXT _label(SB), $0
+ BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+ BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00
+ BYTE $0x00; BYTE $0x00; BYTE $0x00
+TEXT _type(SB), $0
+ BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+ BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+
+_start0x3E:
+ CLI
+ CLR(rAX)
+ MTSR(rAX, rSS) /* 0000 -> rSS */
+ MTSR(rAX, rDS) /* 0000 -> rDS, source segment */
+ MTSR(rAX, rES)
+ LWI(_magic-Xtotal(SB), rSP)
+ MW(rSP, rBP) /* set the indexed-data pointer */
+
+ SBPB(rDL, Xdrive) /* save the boot drive */
+
+ STI
+
+ LWI(confidence(SB), rSI) /* for that warm, fuzzy feeling */
+ CALL(BIOSputs(SB))
+
+ LBI(0x41, rAH) /* check extensions present */
+ LWI(0x55AA, rBX)
+ LXB(Xdrive, xBP, rDL) /* drive */
+ SYSCALL(0x13) /* CF set on failure */
+ JCS _jmp01
+ CMPI(0xAA55, rBX)
+ JNE _jmp01
+ ANDI(0x0001, rCX)
+ JEQ _jmp01
+
+ /* rCX contains 0x0001 */
+ SBPWI(0x0010, Xdap+0) /* reserved + packet size */
+ SBPWI(rCX, Xdap+2) /* reserved + # of blocks to transfer */
+
+ DEC(rCX)
+ SBPW(rCX, Xdap+12)
+ SBPW(rCX, Xdap+14)
+
+/* BIOSread will do this CALL(dreset(SB)) */
+
+_jmp00:
+ LW(_volid(SB), rAX) /* Xrootlo */
+ LW(_volid+2(SB), rDX) /* Xroothi */
+
+ LWI(_magic+DIROFF(SB), rBX)
+ CALL(BIOSread(SB)) /* read the root directory */
+
+ CALL(printnl(SB))
+ LWI(_magic+DIROFF(SB), rBX)
+ LWI((512/2), rCX)
+ CALL(printbuf(SB))
+
+xloop:
+ JMP xloop
+
+
+_jmp01:
+
+TEXT buggery(SB), $0
+ LWI(error(SB), rSI)
+ CALL(BIOSputs(SB))
+
+xbuggery:
+ JMP xbuggery
+
+/*
+ * Read a sector from a disc. On entry:
+ * rDX:rAX sector number
+ * rES:rBX buffer address
+ */
+TEXT BIOSread(SB), $0
+ LWI(5, rDI) /* retry count (ATAPI ZIPs suck) */
+_retry:
+ PUSHA /* may be trashed by SYSCALL */
+
+ SBPW(rBX, Xdap+4) /* transfer buffer :offset */
+ MFSR(rES, rDI) /* transfer buffer seg: */
+ SBPW(rDI, Xdap+6)
+ SBPW(rAX, Xdap+8) /* LBA (64-bits) */
+ SBPW(rDX, Xdap+10)
+
+ MW(rBP, rSI) /* disk address packet */
+ LBI(0x42, rAH) /* extended read */
+ LBPB(Xdrive, rDL) /* form drive */
+ SYSCALL(0x13) /* CF set on failure */
+ JCC _BIOSreadret
+
+ POPA
+ DEC(rDI) /* too many retries? */
+ JEQ _ioerror
+
+ CALL(dreset(SB))
+ JMP _retry
+
+_ioerror:
+ LWI(ioerror(SB), rSI)
+ CALL(BIOSputs(SB))
+ JMP xbuggery
+
+_BIOSreadret:
+ POPA
+ RET
+
+TEXT dreset(SB), $0
+ PUSHA
+ CLR(rAX) /* rAH == 0 == reset disc system */
+ LBPB(Xdrive, rDL)
+ SYSCALL(0x13)
+ ORB(rAH, rAH) /* status (0 == success) */
+ POPA
+ JNE _ioerror
+ RET
+
+
+TEXT printsharp(SB), $0
+ LWI(sharp(SB), rSI)
+_doprint:
+ CALL(BIOSputs(SB))
+ RET
+
+TEXT printspace(SB), $0
+ LWI(space(SB), rSI)
+ JMP _doprint
+
+TEXT printnl(SB), $0
+ LWI(nl(SB), rSI)
+ JMP _doprint
+
+/*
+ * Output a string to the display.
+ * String argument is in rSI.
+ */
+TEXT BIOSputs(SB), $0
+ PUSHA
+ CLR(rBX)
+_BIOSputs:
+ LODSB
+ ORB(rAL, rAL)
+ JEQ _BIOSputsret
+
+ LBI(0x0E, rAH)
+ SYSCALL(0x10)
+ JMP _BIOSputs
+
+_BIOSputsret:
+ POPA
+ RET
+
+/*
+ * Output a register to the display.
+ */
+TEXT printAX(SB), $0
+ PUSHW(rAX)
+ PUSHW(rBX)
+ PUSHW(rCX)
+ PUSHW(rDI)
+
+ LWI(4, rCX)
+ LWI(numbuf+4(SB), rSI)
+
+_nextchar:
+ DEC(rSI)
+ MW(rAX, rBX)
+ ANDI(0x000F, rBX)
+ ADDI(0x30, rBX) /* 0x30 = '0' */
+ CMPI(0x39, rBX) /* 0x39 = '9' */
+ JLE _dowrite
+ ADDI(0x07, rBX) /* 0x07 = 'A'-(1+'9')*/
+
+_dowrite:
+ SXB(rBL, 0, xSI)
+ SHRI(4, rAX)
+
+ DEC(rCX)
+ JNE _nextchar
+
+ LWI(numbuf(SB), rSI)
+ CALL(BIOSputs(SB))
+
+ POPW(rDI)
+ POPW(rCX)
+ POPW(rBX)
+ POPW(rAX)
+
+ CALL(printspace(SB))
+ RET
+
+TEXT printDXAX(SB), $0
+ PUSHW(rAX)
+ MW(rDX, rAX)
+ CALL(printAX(SB))
+ POPW(rAX)
+ CALL(printAX(SB))
+ RET
+
+TEXT printBX(SB), $0
+ PUSHW(rAX)
+ MW(rBX, rAX)
+ CALL(printAX(SB))
+ POPW(rAX)
+ RET
+
+/*
+ * Output some number of words to the display
+ * rDS:rDI - buffer
+ * rCX: number of words
+ */
+TEXT printbuf(SB), $0
+ PUSHW(rAX)
+ PUSHW(rBX)
+ PUSHW(rCX)
+
+_nextword:
+ LXW(0, xBX, rAX)
+ CALL(printAX(SB))
+ INC(rBX)
+ INC(rBX)
+ DEC(rCX)
+ JNE _nextword
+
+ POPW(rCX)
+ POPW(rBX)
+ POPW(rAX)
+ RET
+
+TEXT error(SB), $0
+ BYTE $'E';
+
+TEXT ioerror(SB), $0
+ BYTE $'I';
+
+TEXT nl(SB), $0
+ BYTE $'\r';
+ BYTE $'\n';
+ BYTE $'\z';
+
+TEXT numbuf(SB), $0
+ BYTE $'X'; BYTE $'X'; BYTE $'X'; BYTE $'X';
+ BYTE $'\z';
+
+TEXT space(SB), $0
+ BYTE $' ';
+ BYTE $'\z';
+
+TEXT sharp(SB), $0
+ BYTE $'#'; BYTE $'\z';
diff --git a/os/boot/pc/pbslba.s b/os/boot/pc/pbslba.s
new file mode 100644
index 00000000..56388868
--- /dev/null
+++ b/os/boot/pc/pbslba.s
@@ -0,0 +1,362 @@
+/*
+ * FAT Partition Boot Sector. Loaded at 0x7C00:
+ * 8a pbslba.s; 8l -o pbslba -l -H3 -T0x7C00 pbslba.8
+ * Will load the target at LOADSEG*16+LOADOFF, so the target
+ * should be probably be loaded with LOADOFF added to the
+ * -Taddress.
+ * If LOADSEG is a multiple of 64KB and LOADOFF is 0 then
+ * targets larger than 64KB can be loaded.
+ *
+ * This code is uses Enhanced BIOS Services for Disc Drives and
+ * can be used with discs up to 137GB in capacity.
+ *
+ * It relies on the _volid field in the FAT header containing
+ * the LBA of the root directory.
+ */
+#include "x16.h"
+#include "mem.h"
+
+#define LOADSEG (0x10000/16) /* where to load code (64KB) */
+#define LOADOFF 0
+#define DIROFF 0x0200 /* where to read the root directory */
+
+/*
+ * FAT directory entry.
+ */
+#define Dname 0x00
+#define Dext 0x08
+#define Dattr 0x0B
+#define Dtime 0x16
+#define Ddate 0x18
+#define Dstart 0x1A
+#define Dlengthlo 0x1C
+#define Dlengthhi 0x1E
+
+#define Dirsz 0x20
+
+/*
+ * Data is kept on the stack, indexed by rBP.
+ */
+#define Xdap 0x00 /* disc address packet */
+#define Xrootsz 0x10 /* file data area */
+#define Xdrive 0x12 /* boot drive, passed by BIOS or MBR */
+#define Xtotal 0x14 /* sum of allocated data above */
+
+TEXT _magic(SB), $0
+ BYTE $0xEB; BYTE $0x3C; /* jmp .+ 0x3C (_start0x3E) */
+ BYTE $0x90 /* nop */
+TEXT _version(SB), $0
+ BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+ BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00
+TEXT _sectsize(SB), $0
+ BYTE $0x00; BYTE $0x00
+TEXT _clustsize(SB), $0
+ BYTE $0x00
+TEXT _nresrv(SB), $0
+ BYTE $0x00; BYTE $0x00
+TEXT _nfats(SB), $0
+ BYTE $0x00
+TEXT _rootsize(SB), $0
+ BYTE $0x00; BYTE $0x00
+TEXT _volsize(SB), $0
+ BYTE $0x00; BYTE $0x00
+TEXT _mediadesc(SB), $0
+ BYTE $0x00
+TEXT _fatsize(SB), $0
+ BYTE $0x00; BYTE $0x00
+TEXT _trksize(SB), $0
+ BYTE $0x00; BYTE $0x00
+TEXT _nheads(SB), $0
+ BYTE $0x00; BYTE $0x00
+TEXT _nhiddenlo(SB), $0
+ BYTE $0x00; BYTE $0x00
+TEXT _nhiddenhi(SB), $0
+ BYTE $0x00; BYTE $0x00;
+TEXT _bigvolsize(SB), $0
+ BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+TEXT _driveno(SB), $0
+ BYTE $0x00
+TEXT _reserved0(SB), $0
+ BYTE $0x00
+TEXT _bootsig(SB), $0
+ BYTE $0x00
+TEXT _volid(SB), $0
+ BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+TEXT _label(SB), $0
+ BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+ BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00
+ BYTE $0x00; BYTE $0x00; BYTE $0x00
+TEXT _type(SB), $0
+ BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+ BYTE $0x00; BYTE $0x00; BYTE $0x00; BYTE $0x00;
+
+_start0x3E:
+ CLI
+ CLR(rAX)
+ MTSR(rAX, rSS) /* 0000 -> rSS */
+ MTSR(rAX, rDS) /* 0000 -> rDS, source segment */
+ MTSR(rAX, rES)
+ LWI(_magic-Xtotal(SB), rSP)
+ MW(rSP, rBP) /* set the indexed-data pointer */
+
+ SBPB(rDL, Xdrive) /* save the boot drive */
+
+ /* booting from a CD starts us at 7C0:0. Move to 0:7C00 */
+ PUSHR(rAX)
+ LWI(_nxt(SB), rAX)
+ PUSHR(rAX)
+ BYTE $0xCB /* FAR RET */
+
+TEXT _nxt(SB), $0
+ STI
+
+ LWI(confidence(SB), rSI) /* for that warm, fuzzy feeling */
+ CALL16(BIOSputs(SB))
+
+ LBI(0x41, rAH) /* check extensions present */
+ LWI(0x55AA, rBX)
+ LXB(Xdrive, xBP, rDL) /* drive */
+ BIOSCALL(0x13) /* CF set on failure */
+ JCS _jmp01
+ CMPI(0xAA55, rBX)
+ JNE _jmp01
+ ANDI(0x0001, rCX)
+ JEQ _jmp01
+
+ /* rCX contains 0x0001 */
+ SBPWI(0x0010, Xdap+0) /* reserved + packet size */
+ SBPWI(rCX, Xdap+2) /* reserved + # of blocks to transfer */
+
+ DEC(rCX)
+ SBPW(rCX, Xdap+12)
+ SBPW(rCX, Xdap+14)
+
+ CALL16(dreset(SB))
+
+_jmp00:
+ LW(_volid(SB), rAX) /* Xrootlo */
+ LW(_volid+2(SB), rDX) /* Xroothi */
+
+ LWI(_magic+DIROFF(SB), rBX)
+ CALL16(BIOSread(SB)) /* read the root directory */
+
+ LWI((512/Dirsz), rBX)
+
+ LWI(_magic+DIROFF(SB), rDI) /* compare first directory entry */
+
+_cmp00:
+ PUSHR(rDI) /* save for later if it matches */
+ LWI(bootfile(SB), rSI)
+ LWI(Dattr, rCX)
+ REP
+ CMPSB
+ POPR(rDI)
+ JEQ _jmp02
+
+ DEC(rBX)
+ JEQ _jmp01
+
+ ADDI(Dirsz, rDI)
+ JMP _cmp00
+_jmp01:
+ CALL16(buggery(SB))
+
+_jmp02:
+ CLR(rBX) /* a handy value */
+ LW(_rootsize(SB), rAX) /* calculate and save Xrootsz */
+ LWI(Dirsz, rCX)
+ MUL(rCX)
+ LW(_sectsize(SB), rCX)
+ PUSHR(rCX)
+ DEC(rCX)
+ ADD(rCX, rAX)
+ ADC(rBX, rDX)
+ POPR(rCX) /* _sectsize(SB) */
+ DIV(rCX)
+ PUSHR(rAX) /* Xrootsz */
+
+ /*
+ * rDI points to the matching directory entry.
+ */
+ LXW(Dstart, xDI, rAX) /* starting sector address */
+ DEC(rAX) /* that's just the way it is */
+ DEC(rAX)
+ LB(_clustsize(SB), rCL)
+ CLRB(rCH)
+ MUL(rCX)
+ LW(_volid(SB), rCX) /* Xrootlo */
+ ADD(rCX, rAX)
+ LW(_volid+2(SB), rCX) /* Xroothi */
+ ADC(rCX, rDX)
+ POPR(rCX) /* Xrootsz */
+ ADD(rCX, rAX)
+ ADC(rBX, rDX)
+
+ PUSHR(rAX) /* calculate how many sectors to read */
+ PUSHR(rDX)
+ LXW(Dlengthlo, xDI, rAX)
+ LXW(Dlengthhi, xDI, rDX)
+ LW(_sectsize(SB), rCX)
+ PUSHR(rCX)
+ DEC(rCX)
+ ADD(rCX, rAX)
+ ADC(rBX, rDX)
+ POPR(rCX) /* _sectsize(SB) */
+ DIV(rCX)
+ MW(rAX, rCX)
+ POPR(rDX)
+ POPR(rAX)
+
+ LWI(LOADSEG, rBX) /* address to load into (seg+offset) */
+ MTSR(rBX, rES) /* seg */
+ LWI(LOADOFF, rBX) /* offset */
+
+_readboot:
+ CALL16(BIOSread(SB)) /* read the sector */
+
+ LW(_sectsize(SB), rDI) /* bump addresses/counts */
+ ADD(rDI, rBX)
+ JCC _incsecno
+
+ MFSR(rES, rDI) /* next 64KB segment */
+ ADDI(0x1000, rDI)
+ MTSR(rDI, rES)
+
+_incsecno:
+ CLR(rDI)
+ INC(rAX)
+ ADC(rDI, rDX)
+ LOOP _readboot
+
+ LWI(LOADSEG, rDI) /* set rDS for loaded code */
+ MTSR(rDI, rDS)
+ FARJUMP16(LOADSEG, LOADOFF) /* no deposit, no return */
+
+TEXT buggery(SB), $0
+ LWI(error(SB), rSI)
+ CALL16(BIOSputs(SB))
+
+_wait:
+ CLR(rAX) /* wait for almost any key */
+ BIOSCALL(0x16)
+
+_reset:
+ CLR(rBX) /* set ES segment for BIOS area */
+ MTSR(rBX, rES)
+
+ LWI(0x0472, rBX) /* warm-start code address */
+ LWI(0x1234, rAX) /* warm-start code */
+ POKEW /* MOVW AX, ES:[BX] */
+
+ FARJUMP16(0xFFFF, 0x0000) /* reset */
+
+
+/*
+ * Read a sector from a disc. On entry:
+ * rDX:rAX sector number
+ * rES:rBX buffer address
+ */
+TEXT BIOSread(SB), $0
+ LWI(5, rDI) /* retry count (ATAPI ZIPs suck) */
+_retry:
+ PUSHA /* may be trashed by BIOSCALL */
+
+ SBPW(rBX, Xdap+4) /* transfer buffer :offset */
+ MFSR(rES, rDI) /* transfer buffer seg: */
+ SBPW(rDI, Xdap+6)
+ SBPW(rAX, Xdap+8) /* LBA (64-bits) */
+ SBPW(rDX, Xdap+10)
+
+ MW(rBP, rSI) /* disk address packet */
+ LBI(0x42, rAH) /* extended read */
+ LBPB(Xdrive, rDL) /* form drive */
+ BIOSCALL(0x13) /* CF set on failure */
+ JCC _BIOSreadret
+
+ POPA
+ DEC(rDI) /* too many retries? */
+ JEQ _ioerror
+
+ CALL16(dreset(SB))
+ JMP _retry
+
+_ioerror:
+ LWI(ioerror(SB), rSI)
+ CALL16(BIOSputs(SB))
+ JMP _wait
+
+_BIOSreadret:
+ POPA
+ RET
+
+TEXT dreset(SB), $0
+ PUSHA
+ CLR(rAX) /* rAH == 0 == reset disc system */
+ LBPB(Xdrive, rDL)
+ BIOSCALL(0x13)
+ ORB(rAH, rAH) /* status (0 == success) */
+ POPA
+ JNE _ioerror
+ RET
+
+/*
+ * Output a string to the display.
+ * String argument is in rSI.
+ */
+TEXT BIOSputs(SB), $0
+ PUSHA
+ CLR(rBX)
+_BIOSputs:
+ LODSB
+ ORB(rAL, rAL)
+ JEQ _BIOSputsret
+
+ LBI(0x0E, rAH)
+ BIOSCALL(0x10)
+ JMP _BIOSputs
+
+_BIOSputsret:
+ POPA
+ RET
+
+/* "Bad format or I/O error\r\nPress almost any key to reboot..." */
+TEXT error(SB), $0
+ BYTE $'B'; BYTE $'a'; BYTE $'d'; BYTE $' ';
+ BYTE $'f'; BYTE $'o'; BYTE $'r'; BYTE $'m';
+ BYTE $'a'; BYTE $'t'; BYTE $' '; BYTE $'o';
+ BYTE $'r'; BYTE $' ';
+/* "I/O error\r\nPress almost any key to reboot..." */
+TEXT ioerror(SB), $0
+ BYTE $'I'; BYTE $'/'; BYTE $'O'; BYTE $' ';
+ BYTE $'e'; BYTE $'r'; BYTE $'r'; BYTE $'o';
+ BYTE $'r'; BYTE $'\r';BYTE $'\n';
+ BYTE $'P'; BYTE $'r'; BYTE $'e'; BYTE $'s';
+ BYTE $'s'; BYTE $' '; BYTE $'a'; BYTE $' ';
+ BYTE $'k'; BYTE $'e'; BYTE $'y';
+ BYTE $' '; BYTE $'t'; BYTE $'o'; BYTE $' ';
+ BYTE $'r'; BYTE $'e'; BYTE $'b'; BYTE $'o';
+ BYTE $'o'; BYTE $'t';
+ BYTE $'.'; BYTE $'.'; BYTE $'.';
+ BYTE $'\z';
+
+#ifdef USEBCOM
+/* "B COM" */
+TEXT bootfile(SB), $0
+ BYTE $'B'; BYTE $' '; BYTE $' '; BYTE $' ';
+ BYTE $' '; BYTE $' '; BYTE $' '; BYTE $' ';
+ BYTE $'C'; BYTE $'O'; BYTE $'M';
+ BYTE $'\z';
+#else
+/* "9LOAD " */
+TEXT bootfile(SB), $0
+ BYTE $'9'; BYTE $'L'; BYTE $'O'; BYTE $'A';
+ BYTE $'D'; BYTE $' '; BYTE $' '; BYTE $' ';
+ BYTE $' '; BYTE $' '; BYTE $' ';
+ BYTE $'\z';
+#endif /* USEBCOM */
+
+/* "PBS..." */
+TEXT confidence(SB), $0
+ BYTE $'P'; BYTE $'B'; BYTE $'S'; BYTE $'.';
+ BYTE $'.'; BYTE $'.';
+ BYTE $'\z';
diff --git a/os/boot/pc/pci.c b/os/boot/pc/pci.c
new file mode 100644
index 00000000..c7b9eda5
--- /dev/null
+++ b/os/boot/pc/pci.c
@@ -0,0 +1,852 @@
+/*
+ * PCI support code.
+ * To do:
+ * initialise bridge mappings if the PCI BIOS didn't.
+ */
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "error.h"
+
+enum { /* configuration mechanism #1 */
+ PciADDR = 0xCF8, /* CONFIG_ADDRESS */
+ PciDATA = 0xCFC, /* CONFIG_DATA */
+
+ /* configuration mechanism #2 */
+ PciCSE = 0xCF8, /* configuration space enable */
+ PciFORWARD = 0xCFA, /* which bus */
+
+ MaxFNO = 7,
+ MaxUBN = 255,
+};
+
+static Lock pcicfglock;
+static Lock pcicfginitlock;
+static int pcicfgmode = -1;
+static int pcimaxbno = 7;
+static int pcimaxdno;
+static Pcidev* pciroot;
+static Pcidev* pcilist;
+static Pcidev* pcitail;
+
+static int pcicfgrw32(int, int, int, int);
+static int pcicfgrw8(int, int, int, int);
+
+ulong
+pcibarsize(Pcidev *p, int rno)
+{
+ ulong v, size;
+
+ v = pcicfgrw32(p->tbdf, rno, 0, 1);
+ pcicfgrw32(p->tbdf, rno, 0xFFFFFFF0, 0);
+ size = pcicfgrw32(p->tbdf, rno, 0, 1);
+ if(v & 1)
+ size |= 0xFFFF0000;
+ pcicfgrw32(p->tbdf, rno, v, 0);
+
+ return -(size & ~0x0F);
+}
+
+int
+pciscan(int bno, Pcidev** list)
+{
+ Pcidev *p, *head, *tail;
+ int dno, fno, i, hdt, l, maxfno, maxubn, rno, sbn, tbdf, ubn;
+
+ maxubn = bno;
+ head = nil;
+ tail = nil;
+ for(dno = 0; dno <= pcimaxdno; dno++){
+ maxfno = 0;
+ for(fno = 0; fno <= maxfno; fno++){
+ /*
+ * For this possible device, form the
+ * bus+device+function triplet needed to address it
+ * and try to read the vendor and device ID.
+ * If successful, allocate a device struct and
+ * start to fill it in with some useful information
+ * from the device's configuration space.
+ */
+ tbdf = MKBUS(BusPCI, bno, dno, fno);
+ l = pcicfgrw32(tbdf, PciVID, 0, 1);
+ if(l == 0xFFFFFFFF || l == 0)
+ continue;
+ p = malloc(sizeof(*p));
+ p->tbdf = tbdf;
+ p->vid = l;
+ p->did = l>>16;
+
+ if(pcilist != nil)
+ pcitail->list = p;
+ else
+ pcilist = p;
+ pcitail = p;
+
+ p->rid = pcicfgr8(p, PciRID);
+ p->ccrp = pcicfgr8(p, PciCCRp);
+ p->ccru = pcicfgr8(p, PciCCRu);
+ p->ccrb = pcicfgr8(p, PciCCRb);
+ p->pcr = pcicfgr32(p, PciPCR);
+
+ p->intl = pcicfgr8(p, PciINTL);
+
+ /*
+ * If the device is a multi-function device adjust the
+ * loop count so all possible functions are checked.
+ */
+ hdt = pcicfgr8(p, PciHDT);
+ if(hdt & 0x80)
+ maxfno = MaxFNO;
+
+ /*
+ * If appropriate, read the base address registers
+ * and work out the sizes.
+ */
+ switch(p->ccrb){
+
+ case 0x01: /* mass storage controller */
+ case 0x02: /* network controller */
+ case 0x03: /* display controller */
+ case 0x04: /* multimedia device */
+ case 0x07: /* simple comm. controllers */
+ case 0x08: /* base system peripherals */
+ case 0x09: /* input devices */
+ case 0x0A: /* docking stations */
+ case 0x0B: /* processors */
+ case 0x0C: /* serial bus controllers */
+ if((hdt & 0x7F) != 0)
+ break;
+ rno = PciBAR0 - 4;
+ for(i = 0; i < nelem(p->mem); i++){
+ rno += 4;
+ p->mem[i].bar = pcicfgr32(p, rno);
+ p->mem[i].size = pcibarsize(p, rno);
+ }
+ break;
+
+ case 0x00:
+ case 0x05: /* memory controller */
+ case 0x06: /* bridge device */
+ default:
+ break;
+ }
+
+ if(head != nil)
+ tail->link = p;
+ else
+ head = p;
+ tail = p;
+ }
+ }
+
+ *list = head;
+ for(p = head; p != nil; p = p->link){
+ /*
+ * Find PCI-PCI and PCI-Cardbus bridges and recursively descend the tree.
+ */
+ if(p->ccrb != 0x06 || p->ccru != 0x04)
+ continue;
+
+ /*
+ * If the secondary or subordinate bus number is not
+ * initialised try to do what the PCI BIOS should have
+ * done and fill in the numbers as the tree is descended.
+ * On the way down the subordinate bus number is set to
+ * the maximum as it's not known how many buses are behind
+ * this one; the final value is set on the way back up.
+ */
+ ubn = pcicfgr8(p, PciUBN);
+ sbn = pcicfgr8(p, PciSBN);
+
+ if(sbn == 0 || ubn == 0){
+ sbn = maxubn+1;
+ /*
+ * Make sure memory, I/O and master enables are
+ * off, set the primary, secondary and subordinate
+ * bus numbers and clear the secondary status before
+ * attempting to scan the secondary bus.
+ *
+ * Initialisation of the bridge should be done here.
+ */
+ pcicfgw32(p, PciPCR, 0xFFFF0000);
+ l = (MaxUBN<<16)|(sbn<<8)|bno;
+ pcicfgw32(p, PciPBN, l);
+ pcicfgw16(p, PciSPSR, 0xFFFF);
+ maxubn = pciscan(sbn, &p->bridge);
+ l = (maxubn<<16)|(sbn<<8)|bno;
+
+ pcicfgw32(p, PciPBN, l);
+ }
+ else{
+ maxubn = ubn;
+ pciscan(sbn, &p->bridge);
+ }
+ }
+
+ return maxubn;
+}
+
+static uchar
+pIIx_link(Pcidev *router, uchar link)
+{
+ uchar pirq;
+
+ /* link should be 0x60, 0x61, 0x62, 0x63 */
+ pirq = pcicfgr8(router, link);
+ return (pirq < 16)? pirq: 0;
+}
+
+static void
+pIIx_init(Pcidev *router, uchar link, uchar irq)
+{
+ pcicfgw8(router, link, irq);
+}
+
+static uchar
+via_link(Pcidev *router, uchar link)
+{
+ uchar pirq;
+
+ /* link should be 1, 2, 3, 5 */
+ pirq = (link < 6)? pcicfgr8(router, 0x55 + (link>>1)): 0;
+
+ return (link & 1)? (pirq >> 4): (pirq & 15);
+}
+
+static void
+via_init(Pcidev *router, uchar link, uchar irq)
+{
+ uchar pirq;
+
+ pirq = pcicfgr8(router, 0x55 + (link >> 1));
+ pirq &= (link & 1)? 0x0f: 0xf0;
+ pirq |= (link & 1)? (irq << 4): (irq & 15);
+ pcicfgw8(router, 0x55 + (link>>1), pirq);
+}
+
+static uchar
+opti_link(Pcidev *router, uchar link)
+{
+ uchar pirq = 0;
+
+ /* link should be 0x02, 0x12, 0x22, 0x32 */
+ if ((link & 0xcf) == 0x02)
+ pirq = pcicfgr8(router, 0xb8 + (link >> 5));
+ return (link & 0x10)? (pirq >> 4): (pirq & 15);
+}
+
+static void
+opti_init(Pcidev *router, uchar link, uchar irq)
+{
+ uchar pirq;
+
+ pirq = pcicfgr8(router, 0xb8 + (link >> 5));
+ pirq &= (link & 0x10)? 0x0f : 0xf0;
+ pirq |= (link & 0x10)? (irq << 4): (irq & 15);
+ pcicfgw8(router, 0xb8 + (link >> 5), pirq);
+}
+
+static uchar
+ali_link(Pcidev *router, uchar link)
+{
+ /* No, you're not dreaming */
+ static const uchar map[] = { 0, 9, 3, 10, 4, 5, 7, 6, 1, 11, 0, 12, 0, 14, 0, 15 };
+ uchar pirq;
+
+ /* link should be 0x01..0x08 */
+ pirq = pcicfgr8(router, 0x48 + ((link-1)>>1));
+ return (link & 1)? map[pirq&15]: map[pirq>>4];
+}
+
+static void
+ali_init(Pcidev *router, uchar link, uchar irq)
+{
+ /* Inverse of map in ali_link */
+ static const uchar map[] = { 0, 8, 0, 2, 4, 5, 7, 6, 0, 1, 3, 9, 11, 0, 13, 15 };
+ uchar pirq;
+
+ pirq = pcicfgr8(router, 0x48 + ((link-1)>>1));
+ pirq &= (link & 1)? 0x0f: 0xf0;
+ pirq |= (link & 1)? (map[irq] << 4): (map[irq] & 15);
+ pcicfgw8(router, 0x48 + ((link-1)>>1), pirq);
+}
+
+static uchar
+cyrix_link(Pcidev *router, uchar link)
+{
+ uchar pirq;
+
+ /* link should be 1, 2, 3, 4 */
+ pirq = pcicfgr8(router, 0x5c + ((link-1)>>1));
+ return ((link & 1)? pirq >> 4: pirq & 15);
+}
+
+static void
+cyrix_init(Pcidev *router, uchar link, uchar irq)
+{
+ uchar pirq;
+
+ pirq = pcicfgr8(router, 0x5c + (link>>1));
+ pirq &= (link & 1)? 0x0f: 0xf0;
+ pirq |= (link & 1)? (irq << 4): (irq & 15);
+ pcicfgw8(router, 0x5c + (link>>1), pirq);
+}
+
+enum {
+ Intel = 0x8086,
+ Intel_82371FB_0 = 0x122e,
+ Intel_82371MX_0 = 0x1234,
+ Intel_82371SB_0 = 0x7000,
+ Intel_82371AB_0 = 0x7110,
+ Intel_82443MX_1 = 0x7198,
+ Intel_82801AA_0 = 0x2410,
+ Intel_82801AB_0 = 0x2420,
+ Intel_82801BA_0 = 0x2440,
+ Intel_82801BAM_0 = 0x244c,
+ Intel_82801CAM_0 = 0x248c,
+ Intel_82801DBM_0 = 0x24cc,
+ Intel_82801EB_0 = 0x24d0,
+ Intel_82801FB_0 = 0x2640,
+ Viatech = 0x1106,
+ Via_82C586_0 = 0x0586,
+ Via_82C596 = 0x0596,
+ Via_82C686 = 0x0686,
+ Opti = 0x1045,
+ Opti_82C700 = 0xc700,
+ Al = 0x10b9,
+ Al_M1533 = 0x1533,
+ SI = 0x1039,
+ SI_503 = 0x0008,
+ SI_496 = 0x0496,
+ Cyrix = 0x1078,
+ Cyrix_5530_Legacy = 0x0100,
+};
+
+typedef struct {
+ ushort sb_vid, sb_did;
+ uchar (*sb_translate)(Pcidev *, uchar);
+ void (*sb_initialize)(Pcidev *, uchar, uchar);
+} bridge_t;
+
+static bridge_t southbridges[] = {
+{ Intel, Intel_82371FB_0, pIIx_link, pIIx_init },
+{ Intel, Intel_82371MX_0, pIIx_link, pIIx_init },
+{ Intel, Intel_82371SB_0, pIIx_link, pIIx_init },
+{ Intel, Intel_82371AB_0, pIIx_link, pIIx_init },
+{ Intel, Intel_82443MX_1, pIIx_link, pIIx_init },
+{ Intel, Intel_82801AA_0, pIIx_link, pIIx_init },
+{ Intel, Intel_82801AB_0, pIIx_link, pIIx_init },
+{ Intel, Intel_82801BA_0, pIIx_link, pIIx_init },
+{ Intel, Intel_82801BAM_0, pIIx_link, pIIx_init },
+{ Intel, Intel_82801CAM_0, pIIx_link, pIIx_init },
+{ Intel, Intel_82801DBM_0, pIIx_link, pIIx_init },
+{ Intel, Intel_82801EB_0, pIIx_link, pIIx_init },
+{ Intel, Intel_82801FB_0, pIIx_link, pIIx_init },
+{ Viatech, Via_82C586_0, via_link, via_init },
+{ Viatech, Via_82C596, via_link, via_init },
+{ Viatech, Via_82C686, via_link, via_init },
+{ Opti, Opti_82C700, opti_link, opti_init },
+{ Al, Al_M1533, ali_link, ali_init },
+{ SI, SI_503, pIIx_link, pIIx_init },
+{ SI, SI_496, pIIx_link, pIIx_init },
+{ Cyrix, Cyrix_5530_Legacy, cyrix_link, cyrix_init }
+};
+
+typedef struct {
+ uchar e_bus; // Pci bus number
+ uchar e_dev; // Pci device number
+ uchar e_maps[12]; // Avoid structs! Link and mask.
+ uchar e_slot; // Add-in/built-in slot
+ uchar e_reserved;
+} slot_t;
+
+typedef struct {
+ uchar rt_signature[4]; // Routing table signature
+ uchar rt_version[2]; // Version number
+ uchar rt_size[2]; // Total table size
+ uchar rt_bus; // Interrupt router bus number
+ uchar rt_devfn; // Router's devfunc
+ uchar rt_pciirqs[2]; // Exclusive PCI irqs
+ uchar rt_compat[4]; // Compatible PCI interrupt router
+ uchar rt_miniport[4]; // Miniport data
+ uchar rt_reserved[11];
+ uchar rt_checksum;
+} router_t;
+
+static ushort pciirqs; // Exclusive PCI irqs
+static bridge_t *southbridge; // Which southbridge to use.
+
+static void
+pcirouting(void)
+{
+ uchar *p, pin, irq;
+ ulong tbdf, vdid;
+ ushort vid, did;
+ router_t *r;
+ slot_t *e;
+ int size, i, fn;
+ Pcidev *sbpci, *pci;
+
+ // Peek in the BIOS
+ for (p = (uchar *)KADDR(0xf0000); p < (uchar *)KADDR(0xfffff); p += 16)
+ if (p[0] == '$' && p[1] == 'P' && p[2] == 'I' && p[3] == 'R')
+ break;
+
+ if (p >= (uchar *)KADDR(0xfffff))
+ return;
+
+ r = (router_t *)p;
+
+ // print("PCI interrupt routing table version %d.%d at %.6uX\n",
+ // r->rt_version[0], r->rt_version[1], (ulong)r & 0xfffff);
+
+ tbdf = (BusPCI << 24)|(r->rt_bus << 16)|(r->rt_devfn << 8);
+ vdid = pcicfgrw32(tbdf, PciVID, 0, 1);
+ vid = vdid;
+ did = vdid >> 16;
+
+ for (i = 0; i != nelem(southbridges); i++)
+ if (vid == southbridges[i].sb_vid && did == southbridges[i].sb_did)
+ break;
+
+ if (i == nelem(southbridges)) {
+ print("pcirouting: South bridge %.4uX, %.4uX not found\n", vid, did);
+ return;
+ }
+ southbridge = &southbridges[i];
+
+ if ((sbpci = pcimatch(nil, vid, did)) == nil) {
+ print("pcirouting: Cannot match south bridge %.4uX, %.4uX\n",
+ vid, did);
+ return;
+ }
+
+ pciirqs = (r->rt_pciirqs[1] << 8)|r->rt_pciirqs[0];
+
+ size = (r->rt_size[1] << 8)|r->rt_size[0];
+ for (e = (slot_t *)&r[1]; (uchar *)e < p + size; e++) {
+ // print("%.2uX/%.2uX %.2uX: ", e->e_bus, e->e_dev, e->e_slot);
+ // for (i = 0; i != 4; i++) {
+ // uchar *m = &e->e_maps[i * 3];
+ // print("[%d] %.2uX %.4uX ",
+ // i, m[0], (m[2] << 8)|m[1]);
+ // }
+ // print("\n");
+
+ for (fn = 0; fn != 8; fn++) {
+ uchar *m;
+
+ // Retrieve the did and vid through the devfn before
+ // obtaining the Pcidev structure.
+ tbdf = (BusPCI << 24)|(e->e_bus << 16)|((e->e_dev | fn) << 8);
+ vdid = pcicfgrw32(tbdf, PciVID, 0, 1);
+ if (vdid == 0xFFFFFFFF || vdid == 0)
+ continue;
+
+ vid = vdid;
+ did = vdid >> 16;
+
+ pci = nil;
+ while ((pci = pcimatch(pci, vid, did)) != nil) {
+
+ if (pci->intl != 0 && pci->intl != 0xFF)
+ continue;
+
+ pin = pcicfgr8(pci, PciINTP);
+ if (pin == 0 || pin == 0xff)
+ continue;
+
+ m = &e->e_maps[(pin - 1) * 3];
+ irq = southbridge->sb_translate(sbpci, m[0]);
+ if (irq) {
+ print("pcirouting: %.4uX/%.4uX at pin %d irq %d\n",
+ vid, did, pin, irq);
+ pcicfgw8(pci, PciINTL, irq);
+ pci->intl = irq;
+ }
+ }
+ }
+ }
+}
+
+static void
+pcicfginit(void)
+{
+ char *p;
+ int bno, n;
+ Pcidev **list;
+
+ lock(&pcicfginitlock);
+ if(pcicfgmode != -1)
+ goto out;
+
+ /*
+ * Try to determine which PCI configuration mode is implemented.
+ * Mode2 uses a byte at 0xCF8 and another at 0xCFA; Mode1 uses
+ * a DWORD at 0xCF8 and another at 0xCFC and will pass through
+ * any non-DWORD accesses as normal I/O cycles. There shouldn't be
+ * a device behind these addresses so if Mode1 accesses fail try
+ * for Mode2 (Mode2 is deprecated).
+ */
+
+ /*
+ * Bits [30:24] of PciADDR must be 0,
+ * according to the spec.
+ */
+ n = inl(PciADDR);
+ if(!(n & 0x7FF00000)){
+ outl(PciADDR, 0x80000000);
+ outb(PciADDR+3, 0);
+ if(inl(PciADDR) & 0x80000000){
+ pcicfgmode = 1;
+ pcimaxdno = 31;
+ }
+ }
+ outl(PciADDR, n);
+
+ if(pcicfgmode < 0){
+ /*
+ * The 'key' part of PciCSE should be 0.
+ */
+ n = inb(PciCSE);
+ if(!(n & 0xF0)){
+ outb(PciCSE, 0x0E);
+ if(inb(PciCSE) == 0x0E){
+ pcicfgmode = 2;
+ pcimaxdno = 15;
+ }
+ }
+ outb(PciCSE, n);
+ }
+
+ if(pcicfgmode < 0)
+ goto out;
+
+
+ if(p = getconf("*pcimaxbno"))
+ pcimaxbno = strtoul(p, 0, 0);
+ if(p = getconf("*pcimaxdno"))
+ pcimaxdno = strtoul(p, 0, 0);
+
+ list = &pciroot;
+ for(bno = 0; bno <= pcimaxbno; bno++) {
+ bno = pciscan(bno, list);
+ while(*list)
+ list = &(*list)->link;
+ }
+
+ pcirouting();
+
+out:
+ unlock(&pcicfginitlock);
+
+ if(getconf("*pcihinv"))
+ pcihinv(nil);
+}
+
+
+static int
+pcicfgrw8(int tbdf, int rno, int data, int read)
+{
+ int o, type, x;
+
+ if(pcicfgmode == -1)
+ pcicfginit();
+
+ if(BUSBNO(tbdf))
+ type = 0x01;
+ else
+ type = 0x00;
+ x = -1;
+ if(BUSDNO(tbdf) > pcimaxdno)
+ return x;
+
+ lock(&pcicfglock);
+ switch(pcicfgmode){
+
+ case 1:
+ o = rno & 0x03;
+ rno &= ~0x03;
+ outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type);
+ if(read)
+ x = inb(PciDATA+o);
+ else
+ outb(PciDATA+o, data);
+ outl(PciADDR, 0);
+ break;
+
+ case 2:
+ outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1));
+ outb(PciFORWARD, BUSBNO(tbdf));
+ if(read)
+ x = inb((0xC000|(BUSDNO(tbdf)<<8)) + rno);
+ else
+ outb((0xC000|(BUSDNO(tbdf)<<8)) + rno, data);
+ outb(PciCSE, 0);
+ break;
+ }
+ unlock(&pcicfglock);
+
+ return x;
+}
+
+int
+pcicfgr8(Pcidev* pcidev, int rno)
+{
+ return pcicfgrw8(pcidev->tbdf, rno, 0, 1);
+}
+
+void
+pcicfgw8(Pcidev* pcidev, int rno, int data)
+{
+ pcicfgrw8(pcidev->tbdf, rno, data, 0);
+}
+
+static int
+pcicfgrw16(int tbdf, int rno, int data, int read)
+{
+ int o, type, x;
+
+ if(pcicfgmode == -1)
+ pcicfginit();
+
+ if(BUSBNO(tbdf))
+ type = 0x01;
+ else
+ type = 0x00;
+ x = -1;
+ if(BUSDNO(tbdf) > pcimaxdno)
+ return x;
+
+ lock(&pcicfglock);
+ switch(pcicfgmode){
+
+ case 1:
+ o = rno & 0x02;
+ rno &= ~0x03;
+ outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type);
+ if(read)
+ x = ins(PciDATA+o);
+ else
+ outs(PciDATA+o, data);
+ outl(PciADDR, 0);
+ break;
+
+ case 2:
+ outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1));
+ outb(PciFORWARD, BUSBNO(tbdf));
+ if(read)
+ x = ins((0xC000|(BUSDNO(tbdf)<<8)) + rno);
+ else
+ outs((0xC000|(BUSDNO(tbdf)<<8)) + rno, data);
+ outb(PciCSE, 0);
+ break;
+ }
+ unlock(&pcicfglock);
+
+ return x;
+}
+
+int
+pcicfgr16(Pcidev* pcidev, int rno)
+{
+ return pcicfgrw16(pcidev->tbdf, rno, 0, 1);
+}
+
+void
+pcicfgw16(Pcidev* pcidev, int rno, int data)
+{
+ pcicfgrw16(pcidev->tbdf, rno, data, 0);
+}
+
+static int
+pcicfgrw32(int tbdf, int rno, int data, int read)
+{
+ int type, x;
+
+ if(pcicfgmode == -1)
+ pcicfginit();
+
+ if(BUSBNO(tbdf))
+ type = 0x01;
+ else
+ type = 0x00;
+ x = -1;
+ if(BUSDNO(tbdf) > pcimaxdno)
+ return x;
+
+ lock(&pcicfglock);
+ switch(pcicfgmode){
+
+ case 1:
+ rno &= ~0x03;
+ outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type);
+ if(read)
+ x = inl(PciDATA);
+ else
+ outl(PciDATA, data);
+ outl(PciADDR, 0);
+ break;
+
+ case 2:
+ outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1));
+ outb(PciFORWARD, BUSBNO(tbdf));
+ if(read)
+ x = inl((0xC000|(BUSDNO(tbdf)<<8)) + rno);
+ else
+ outl((0xC000|(BUSDNO(tbdf)<<8)) + rno, data);
+ outb(PciCSE, 0);
+ break;
+ }
+ unlock(&pcicfglock);
+
+ return x;
+}
+
+int
+pcicfgr32(Pcidev* pcidev, int rno)
+{
+ return pcicfgrw32(pcidev->tbdf, rno, 0, 1);
+}
+
+void
+pcicfgw32(Pcidev* pcidev, int rno, int data)
+{
+ pcicfgrw32(pcidev->tbdf, rno, data, 0);
+}
+
+Pcidev*
+pcimatch(Pcidev* prev, int vid, int did)
+{
+ if(pcicfgmode == -1)
+ pcicfginit();
+
+ if(prev == nil)
+ prev = pcilist;
+ else
+ prev = prev->list;
+
+ while(prev != nil) {
+ if((vid == 0 || prev->vid == vid)
+ && (did == 0 || prev->did == did))
+ break;
+ prev = prev->list;
+ }
+ return prev;
+}
+
+uchar
+pciipin(Pcidev *pci, uchar pin)
+{
+ if (pci == nil)
+ pci = pcilist;
+
+ while (pci) {
+ uchar intl;
+
+ if (pcicfgr8(pci, PciINTP) == pin && pci->intl != 0 && pci->intl != 0xff)
+ return pci->intl;
+
+ if (pci->bridge && (intl = pciipin(pci->bridge, pin)) != 0)
+ return intl;
+
+ pci = pci->list;
+ }
+ return 0;
+}
+
+static ushort
+pciimask(Pcidev *pci)
+{
+ ushort imask;
+
+ imask = 0;
+ while (pci) {
+ if (pcicfgr8(pci, PciINTP) && pci->intl < 16)
+ imask |= 1 << pci->intl;
+
+ if (pci->bridge)
+ imask |= pciimask(pci->bridge);
+
+ pci = pci->list;
+ }
+ return imask;
+}
+
+uchar
+pciintl(Pcidev *pci)
+{
+ ushort imask;
+ int i;
+
+ if (pci == nil)
+ pci = pcilist;
+
+ imask = pciimask(pci) | 1;
+ for (i = 0; i != 16; i++)
+ if ((imask & (1 << i)) == 0)
+ return i;
+ return 0;
+}
+
+void
+pcihinv(Pcidev* p)
+{
+ int i;
+ Pcidev *t;
+
+ if(pcicfgmode == -1)
+ pcicfginit();
+
+ if(p == nil) {
+ p = pciroot;
+ print("bus dev type vid did intl memory\n");
+ }
+ for(t = p; t != nil; t = t->link) {
+ print("%d %2d/%d %.2ux %.2ux %.2ux %.4ux %.4ux %3d ",
+ BUSBNO(t->tbdf), BUSDNO(t->tbdf), BUSFNO(t->tbdf),
+ t->ccrb, t->ccru, t->ccrp, t->vid, t->did, t->intl);
+
+ for(i = 0; i < nelem(p->mem); i++) {
+ if(t->mem[i].size == 0)
+ continue;
+ print("%d:%.8lux %d ", i,
+ t->mem[i].bar, t->mem[i].size);
+ }
+ print("\n");
+ }
+ while(p != nil) {
+ if(p->bridge != nil)
+ pcihinv(p->bridge);
+ p = p->link;
+ }
+}
+
+void
+pcireset(void)
+{
+ Pcidev *p;
+ int pcr;
+
+ if(pcicfgmode == -1)
+ pcicfginit();
+
+ for(p = pcilist; p != nil; p = p->list){
+ pcr = pcicfgr16(p, PciPSR);
+ pcicfgw16(p, PciPSR, pcr & ~0x04);
+ }
+}
+
+void
+pcisetbme(Pcidev* p)
+{
+ int pcr;
+
+ pcr = pcicfgr16(p, PciPCR);
+ pcr |= 0x0004;
+ pcicfgw16(p, PciPCR, pcr);
+}
+
diff --git a/os/boot/pc/print.c b/os/boot/pc/print.c
new file mode 100644
index 00000000..f3137f55
--- /dev/null
+++ b/os/boot/pc/print.c
@@ -0,0 +1,31 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+static Lock fmtl;
+
+void
+_fmtlock(void)
+{
+ lock(&fmtl);
+}
+
+void
+_fmtunlock(void)
+{
+ unlock(&fmtl);
+}
+
+int
+_efgfmt(Fmt*)
+{
+ return -1;
+}
+
+int
+errfmt(Fmt*)
+{
+ return -1;
+}
diff --git a/os/boot/pc/queue.c b/os/boot/pc/queue.c
new file mode 100644
index 00000000..d2a529cd
--- /dev/null
+++ b/os/boot/pc/queue.c
@@ -0,0 +1,44 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+int
+qgetc(IOQ *q)
+{
+ int c;
+
+ if(q->in == q->out)
+ return -1;
+ c = *q->out;
+ if(q->out == q->buf+sizeof(q->buf)-1)
+ q->out = q->buf;
+ else
+ q->out++;
+ return c;
+}
+
+static int
+qputc(IOQ *q, int c)
+{
+ uchar *nextin;
+ if(q->in >= &q->buf[sizeof(q->buf)-1])
+ nextin = q->buf;
+ else
+ nextin = q->in+1;
+ if(nextin == q->out)
+ return -1;
+ *q->in = c;
+ q->in = nextin;
+ return 0;
+}
+
+void
+qinit(IOQ *q)
+{
+ q->in = q->out = q->buf;
+ q->getc = qgetc;
+ q->putc = qputc;
+}
diff --git a/os/boot/pc/sd.h b/os/boot/pc/sd.h
new file mode 100644
index 00000000..a19ac689
--- /dev/null
+++ b/os/boot/pc/sd.h
@@ -0,0 +1,127 @@
+/*
+ * Storage Device.
+ */
+typedef struct SDev SDev;
+typedef struct SDifc SDifc;
+typedef struct SDpart SDpart;
+typedef struct SDreq SDreq;
+typedef struct SDunit SDunit;
+
+typedef struct SDpart {
+ ulong start;
+ ulong end;
+ char name[NAMELEN];
+ char user[NAMELEN];
+ ulong perm;
+ int valid;
+ void *crud;
+} SDpart;
+
+typedef struct SDunit {
+ SDev* dev;
+ int subno;
+ uchar inquiry[256]; /* format follows SCSI spec */
+ char name[NAMELEN];
+// Rendez rendez;
+
+// QLock ctl;
+ ulong sectors;
+ ulong secsize;
+ SDpart* part;
+ int npart; /* of valid partitions */
+ int changed;
+
+// QLock raw;
+ int state;
+ ulong pid;
+ SDreq* req;
+} SDunit;
+
+typedef struct SDev {
+ SDifc* ifc; /* pnp/legacy */
+ void *ctlr;
+ int idno;
+ int index; /* into unit space */
+ int nunit;
+ SDev* next;
+
+// QLock; /* enable/disable */
+ int enabled;
+} SDev;
+
+typedef struct SDifc {
+ char* name;
+
+ SDev* (*pnp)(void);
+ SDev* (*legacy)(int, int);
+ SDev* (*id)(SDev*);
+ int (*enable)(SDev*);
+ int (*disable)(SDev*);
+
+ int (*verify)(SDunit*);
+ int (*online)(SDunit*);
+ int (*rio)(SDreq*);
+ int (*rctl)(SDunit*, char*, int);
+ int (*wctl)(SDunit*, void*);
+
+ long (*bio)(SDunit*, int, int, void*, long, long);
+} SDifc;
+
+typedef struct SDreq {
+ SDunit* unit;
+ int lun;
+ int write;
+ uchar cmd[16];
+ int clen;
+ void* data;
+ int dlen;
+
+ int flags;
+
+ int status;
+ long rlen;
+ uchar sense[256];
+} SDreq;
+
+enum {
+ SDnosense = 0x00000001,
+ SDvalidsense = 0x00010000,
+};
+
+enum {
+ SDmalloc = -4,
+ SDeio = -3,
+ SDtimeout = -2,
+ SDnostatus = -1,
+
+ SDok = 0,
+
+ SDcheck = 0x02, /* check condition */
+ SDbusy = 0x08, /* busy */
+
+ SDmaxio = 2048*1024,
+ SDnpart = 16,
+};
+
+/* sdscsi.c */
+extern int scsiverify(SDunit*);
+extern int scsionline(SDunit*);
+extern long scsibio(SDunit*, int, int, void*, long, long);
+extern SDev* scsiid(SDev*, SDifc*);
+
+#define IrqATA0 14
+#define IrqATA1 15
+#define qlock(i) while(0)
+#define qunlock(i) while(0)
+
+#define putstrn consputs
+
+void sleep(void*, int(*)(void*), void*);
+void tsleep(void*, int(*)(void*), void*, int);
+#define wakeup(x) while(0)
+extern long sdbio(SDunit *unit, SDpart *pp, void *a, long len, vlong off);
+void partition(SDunit*);
+void addpartconf(SDunit*);
+SDpart* sdfindpart(SDunit*, char*);
+void sdaddpart(SDunit*, char*, ulong, ulong);
+void* sdmalloc(void*, ulong);
diff --git a/os/boot/pc/sd53c8xx.c b/os/boot/pc/sd53c8xx.c
new file mode 100644
index 00000000..b9dc3e9a
--- /dev/null
+++ b/os/boot/pc/sd53c8xx.c
@@ -0,0 +1,2120 @@
+/*
+ * NCR 53c8xx driver for Plan 9
+ * Nigel Roles (ngr@cotswold.demon.co.uk)
+ *
+ * 08/07/99 Ultra2 fixed. Brazil #ifdefs added. Fixed script error 6 diagnostics.
+ *
+ * 09/06/99 Enhancements to support 895 and 896 correctly. Attempt at Ultra 2 negotiation,
+ * though no device to test with yet.
+ * Variant now contains the number of valid chip registers to assist
+ * dumpncrregs()
+ *
+ * 06/10/98 Various bug fixes and Brazil compiler inspired changes from jmk
+ *
+ * 05/10/98 Small fix to handle command length being greater than expected by device
+ *
+ * 04/08/98 Added missing locks to interrupt handler. Marked places where
+ * multiple controller extensions could go
+ *
+ * 18/05/97 Fixed overestimate in size of local SCRIPT RAM
+ *
+ * 17/05/97 Bug fix to return status
+ *
+ * 06/10/96 Enhanced list of chip IDs. 875 revision 1 has no clock doubler, so assume it
+ * is shipped with 80MHz crystal. Use bit 3 of the GPREG to recognise differential
+ * boards. This is Symbios specific, but since they are about the only suppliers of
+ * differential cards.
+ *
+ * 23/9/96 Wide and Ultra supported. 825A and 860 added to variants. Dual compiling
+ * version for fileserver and cpu. 80MHz default clock for 860
+ *
+ * 5/8/96 Waits for an Inquiry message before initiating synchronous negotiation
+ * in case capabilities byte [7] indicates device does not support it. Devices
+ * which do target initiated negotiation will typically get in first; a few
+ * bugs in handling this have been fixed
+ *
+ * 3/8/96 Added differential support (put scsi0=diff in plan9.ini)
+ * Split exec() into exec() and io(). Exec() is small, and Io() does not
+ * use any Plan 9 specific data structures, so alternate exec() functions
+ * may be done for other environments, such as the fileserver
+ *
+ * GENERAL
+ *
+ * Works on 810 and 875
+ * Should work on 815, 825, 810A, 825A, 860A
+ * Uses local RAM, large FIFO, prefetch, burst opcode fetch, and 16 byte synch. offset
+ * where applicable
+ * Supports multi-target, wide, Ultra
+ * Differential mode can be enabled by putting scsi0=diff in plan9.ini
+ * NO SUPPORT FOR tagged queuing (yet)
+ *
+ * Known problems
+ */
+
+#define MAXTARGET 16 /* can be 8 or 16 */
+
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "error.h"
+
+#include "sd.h"
+extern SDifc sd53c8xxifc;
+
+#define waserror() (0)
+#define poperror()
+typedef struct QLock{ int r; } QLock;
+typedef struct Rendez{ int r; } Rendez;
+#define intrenable(irq, f, c, tbdf, name) setvec(VectorPIC+(irq), f, c);
+
+/**********************************/
+/* Portable configuration macros */
+/**********************************/
+
+//#define BOOTDEBUG
+//#define ASYNC_ONLY
+//#define INTERNAL_SCLK
+//#define ALWAYS_DO_WDTR
+#define WMR_DEBUG
+
+/**********************************/
+/* CPU specific macros */
+/**********************************/
+
+#ifdef BOOTDEBUG
+
+#define KPRINT oprint
+#define IPRINT intrprint
+#define DEBUG(n) 0
+#define IFLUSH() iflush()
+
+#else
+
+#define KPRINT if(0)print
+#define IPRINT if(0)print
+#define DEBUG(n) (0)
+#define IFLUSH()
+
+#endif /* BOOTDEBUG */
+
+/*******************************/
+/* General */
+/*******************************/
+
+#ifndef DMASEG
+#define DMASEG(x) PADDR(x)
+#define legetl(x) (*(ulong*)(x))
+#define lesetl(x,v) (*(ulong*)(x) = (v))
+#define swabl(a,b,c)
+#else
+#endif /*DMASEG */
+#define DMASEG_TO_KADDR(x) KADDR(PADDR(x))
+#define KPTR(x) ((x) == 0 ? 0 : DMASEG_TO_KADDR(x))
+
+#define MEGA 1000000L
+#ifdef INTERNAL_SCLK
+#define SCLK (33 * MEGA)
+#else
+#define SCLK (40 * MEGA)
+#endif /* INTERNAL_SCLK */
+#define ULTRA_NOCLOCKDOUBLE_SCLK (80 * MEGA)
+
+#define MAXSYNCSCSIRATE (5 * MEGA)
+#define MAXFASTSYNCSCSIRATE (10 * MEGA)
+#define MAXULTRASYNCSCSIRATE (20 * MEGA)
+#define MAXULTRA2SYNCSCSIRATE (40 * MEGA)
+#define MAXASYNCCORERATE (25 * MEGA)
+#define MAXSYNCCORERATE (25 * MEGA)
+#define MAXFASTSYNCCORERATE (50 * MEGA)
+#define MAXULTRASYNCCORERATE (80 * MEGA)
+#define MAXULTRA2SYNCCORERATE (160 * MEGA)
+
+
+#define X_MSG 1
+#define X_MSG_SDTR 1
+#define X_MSG_WDTR 3
+
+struct na_patch {
+ unsigned lwoff;
+ unsigned char type;
+};
+
+typedef struct Ncr {
+ uchar scntl0; /* 00 */
+ uchar scntl1;
+ uchar scntl2;
+ uchar scntl3;
+
+ uchar scid; /* 04 */
+ uchar sxfer;
+ uchar sdid;
+ uchar gpreg;
+
+ uchar sfbr; /* 08 */
+ uchar socl;
+ uchar ssid;
+ uchar sbcl;
+
+ uchar dstat; /* 0c */
+ uchar sstat0;
+ uchar sstat1;
+ uchar sstat2;
+
+ uchar dsa[4]; /* 10 */
+
+ uchar istat; /* 14 */
+ uchar istatpad[3];
+
+ uchar ctest0; /* 18 */
+ uchar ctest1;
+ uchar ctest2;
+ uchar ctest3;
+
+ uchar temp[4]; /* 1c */
+
+ uchar dfifo; /* 20 */
+ uchar ctest4;
+ uchar ctest5;
+ uchar ctest6;
+
+ uchar dbc[3]; /* 24 */
+ uchar dcmd; /* 27 */
+
+ uchar dnad[4]; /* 28 */
+ uchar dsp[4]; /* 2c */
+ uchar dsps[4]; /* 30 */
+
+ uchar scratcha[4]; /* 34 */
+
+ uchar dmode; /* 38 */
+ uchar dien;
+ uchar dwt;
+ uchar dcntl;
+
+ uchar adder[4]; /* 3c */
+
+ uchar sien0; /* 40 */
+ uchar sien1;
+ uchar sist0;
+ uchar sist1;
+
+ uchar slpar; /* 44 */
+ uchar slparpad0;
+ uchar macntl;
+ uchar gpcntl;
+
+ uchar stime0; /* 48 */
+ uchar stime1;
+ uchar respid;
+ uchar respidpad0;
+
+ uchar stest0; /* 4c */
+ uchar stest1;
+ uchar stest2;
+ uchar stest3;
+
+ uchar sidl; /* 50 */
+ uchar sidlpad[3];
+
+ uchar sodl; /* 54 */
+ uchar sodlpad[3];
+
+ uchar sbdl; /* 58 */
+ uchar sbdlpad[3];
+
+ uchar scratchb[4]; /* 5c */
+} Ncr;
+
+typedef struct Movedata {
+ uchar dbc[4];
+ uchar pa[4];
+} Movedata;
+
+typedef enum NegoState {
+ NeitherDone, WideInit, WideResponse, WideDone,
+ SyncInit, SyncResponse, BothDone
+} NegoState;
+
+typedef enum State {
+ Allocated, Queued, Active, Done
+} State;
+
+typedef struct Dsa {
+ union {
+ uchar state[4];
+ struct {
+ uchar stateb;
+ uchar result;
+ uchar dmablks;
+ uchar flag; /* setbyte(state,3,...) */
+ };
+ };
+
+ union {
+ ulong dmancr; /* For block transfer: NCR order (little-endian) */
+ uchar dmaaddr[4];
+ };
+
+ uchar target; /* Target */
+ uchar pad0[3];
+
+ uchar lun; /* Logical Unit Number */
+ uchar pad1[3];
+
+ uchar scntl3;
+ uchar sxfer;
+ uchar pad2[2];
+
+ uchar next[4]; /* chaining for SCRIPT (NCR byte order) */
+ struct Dsa *freechain; /* chaining for freelist */
+ Rendez;
+ uchar scsi_id_buf[4];
+ Movedata msg_out_buf;
+ Movedata cmd_buf;
+ Movedata data_buf;
+ Movedata status_buf;
+ uchar msg_out[10]; /* enough to include SDTR */
+ uchar status;
+ int p9status;
+ uchar parityerror;
+} Dsa;
+
+typedef enum Feature {
+ BigFifo = 1, /* 536 byte fifo */
+ BurstOpCodeFetch = 2, /* burst fetch opcodes */
+ Prefetch = 4, /* prefetch 8 longwords */
+ LocalRAM = 8, /* 4K longwords of local RAM */
+ Differential = 16, /* Differential support */
+ Wide = 32, /* Wide capable */
+ Ultra = 64, /* Ultra capable */
+ ClockDouble = 128, /* Has clock doubler */
+ ClockQuad = 256, /* Has clock quadrupler (same as Ultra2) */
+ Ultra2 = 256,
+} Feature;
+
+typedef enum Burst {
+ Burst2 = 0,
+ Burst4 = 1,
+ Burst8 = 2,
+ Burst16 = 3,
+ Burst32 = 4,
+ Burst64 = 5,
+ Burst128 = 6
+} Burst;
+
+typedef struct Variant {
+ ushort did;
+ uchar maxrid; /* maximum allowed revision ID */
+ char *name;
+ Burst burst; /* codings for max burst */
+ uchar maxsyncoff; /* max synchronous offset */
+ uchar registers; /* number of 32 bit registers */
+ unsigned feature;
+} Variant;
+
+static unsigned char cf2[] = { 6, 2, 3, 4, 6, 8, 12, 16 };
+#define NULTRA2SCF (sizeof(cf2)/sizeof(cf2[0]))
+#define NULTRASCF (NULTRA2SCF - 2)
+#define NSCF (NULTRASCF - 1)
+
+typedef struct Controller {
+ Lock;
+ struct {
+ uchar scntl3;
+ uchar stest2;
+ } bios;
+ uchar synctab[NULTRA2SCF - 1][8];/* table of legal tpfs */
+ NegoState s[MAXTARGET];
+ uchar scntl3[MAXTARGET];
+ uchar sxfer[MAXTARGET];
+ uchar cap[MAXTARGET]; /* capabilities byte from Identify */
+ ushort capvalid; /* bit per target for validity of cap[] */
+ ushort wide; /* bit per target set if wide negotiated */
+ ulong sclk; /* clock speed of controller */
+ uchar clockmult; /* set by synctabinit */
+ uchar ccf; /* CCF bits */
+ uchar tpf; /* best tpf value for this controller */
+ uchar feature; /* requested features */
+ int running; /* is the script processor running? */
+ int ssm; /* single step mode */
+ Ncr *n; /* pointer to registers */
+ Variant *v; /* pointer to variant type */
+ ulong *script; /* where the real script is */
+ ulong scriptpa; /* where the real script is */
+ Pcidev* pcidev;
+ SDev* sdev;
+
+ struct {
+ Lock;
+ uchar head[4]; /* head of free list (NCR byte order) */
+ Dsa *tail;
+ Dsa *freechain;
+ } dsalist;
+
+ QLock q[MAXTARGET]; /* queues for each target */
+} Controller;
+
+static Controller controller;
+
+/* ISTAT */
+enum { Abrt = 0x80, Srst = 0x40, Sigp = 0x20, Sem = 0x10, Con = 0x08, Intf = 0x04, Sip = 0x02, Dip = 0x01 };
+
+/* DSTAT */
+enum { Dfe = 0x80, Mdpe = 0x40, Bf = 0x20, Abrted = 0x10, Ssi = 0x08, Sir = 0x04, Iid = 0x01 };
+
+/* SSTAT */
+enum { DataOut, DataIn, Cmd, Status, ReservedOut, ReservedIn, MessageOut, MessageIn };
+
+static void setmovedata(Movedata*, ulong, ulong);
+static void advancedata(Movedata*, long);
+static int bios_set_differential(Controller *c);
+
+static char *phase[] = {
+ "data out", "data in", "command", "status",
+ "reserved out", "reserved in", "message out", "message in"
+};
+
+#ifdef BOOTDEBUG
+#define DEBUGSIZE 10240
+char debugbuf[DEBUGSIZE];
+char *debuglast;
+
+static void
+intrprint(char *format, ...)
+{
+ if (debuglast == 0)
+ debuglast = debugbuf;
+ debuglast = doprint(debuglast, debugbuf + (DEBUGSIZE - 1), format, (&format + 1));
+}
+
+static void
+iflush()
+{
+ int s;
+ char *endp;
+ s = splhi();
+ if (debuglast == 0)
+ debuglast = debugbuf;
+ if (debuglast == debugbuf) {
+ splx(s);
+ return;
+ }
+ endp = debuglast;
+ splx(s);
+ screenputs(debugbuf, endp - debugbuf);
+ s = splhi();
+ memmove(debugbuf, endp, debuglast - endp);
+ debuglast -= endp - debugbuf;
+ splx(s);
+}
+
+static void
+oprint(char *format, ...)
+{
+ int s;
+
+ iflush();
+ s = splhi();
+ if (debuglast == 0)
+ debuglast = debugbuf;
+ debuglast = doprint(debuglast, debugbuf + (DEBUGSIZE - 1), format, (&format + 1));
+ splx(s);
+ iflush();
+}
+#endif
+
+#include "sd53c8xx.i"
+
+static Dsa *
+dsaalloc(Controller *c, int target, int lun)
+{
+ Dsa *d;
+
+ ilock(&c->dsalist);
+ if ((d = c->dsalist.freechain) == 0) {
+ d = xalloc(sizeof(*d));
+ if (DEBUG(1))
+ KPRINT("sd53c8xx: %d/%d: allocated new dsa %lux\n", target, lun, d);
+ lesetl(d->next, 0);
+ lesetl(d->state, A_STATE_ALLOCATED);
+ if (legetl(c->dsalist.head) == 0)
+ lesetl(c->dsalist.head, DMASEG(d)); /* ATOMIC?!? */
+ else
+ lesetl(c->dsalist.tail->next, DMASEG(d)); /* ATOMIC?!? */
+ c->dsalist.tail = d;
+ }
+ else {
+ if (DEBUG(1))
+ KPRINT("sd53c8xx: %d/%d: reused dsa %lux\n", target, lun, d);
+ c->dsalist.freechain = d->freechain;
+ lesetl(d->state, A_STATE_ALLOCATED);
+ }
+ iunlock(&c->dsalist);
+ d->target = target;
+ d->lun = lun;
+ return d;
+}
+
+static void
+dsafree(Controller *c, Dsa *d)
+{
+ ilock(&c->dsalist);
+ d->freechain = c->dsalist.freechain;
+ c->dsalist.freechain = d;
+ lesetl(d->state, A_STATE_FREE);
+ iunlock(&c->dsalist);
+}
+
+static Dsa *
+dsafind(Controller *c, uchar target, uchar lun, uchar state)
+{
+ Dsa *d;
+ for (d = KPTR(legetl(c->dsalist.head)); d; d = KPTR(legetl(d->next))) {
+ if (d->target != 0xff && d->target != target)
+ continue;
+ if (lun != 0xff && d->lun != lun)
+ continue;
+ if (state != 0xff && d->stateb != state)
+ continue;
+ break;
+ }
+ return d;
+}
+
+static void
+dumpncrregs(Controller *c, int intr)
+{
+ int i;
+ Ncr *n = c->n;
+ int depth = c->v->registers / 4;
+
+ KPRINT("sa = %.8lux\n", c->scriptpa);
+ for (i = 0; i < depth; i++) {
+ int j;
+ for (j = 0; j < 4; j++) {
+ int k = j * depth + i;
+ uchar *p;
+
+ /* display little-endian to make 32-bit values readable */
+ p = (uchar*)n+k*4;
+ if (intr)
+ IPRINT(" %.2x%.2x%.2x%.2x %.2x %.2x", p[3], p[2], p[1], p[0], k * 4, (k * 4) + 0x80);
+ else
+ KPRINT(" %.2x%.2x%.2x%.2x %.2x %.2x", p[3], p[2], p[1], p[0], k * 4, (k * 4) + 0x80);
+ USED(p);
+ }
+ if (intr)
+ IPRINT("\n");
+ else
+ KPRINT("\n");
+ }
+}
+
+static int
+chooserate(Controller *c, int tpf, int *scfp, int *xferpp)
+{
+ /* find lowest entry >= tpf */
+ int besttpf = 1000;
+ int bestscfi = 0;
+ int bestxferp = 0;
+ int scf, xferp;
+ int maxscf;
+
+ if (c->v->feature & Ultra2)
+ maxscf = NULTRA2SCF;
+ else if (c->v->feature & Ultra)
+ maxscf = NULTRASCF;
+ else
+ maxscf = NSCF;
+
+ /*
+ * search large clock factors first since this should
+ * result in more reliable transfers
+ */
+ for (scf = maxscf; scf >= 1; scf--) {
+ for (xferp = 0; xferp < 8; xferp++) {
+ unsigned char v = c->synctab[scf - 1][xferp];
+ if (v == 0)
+ continue;
+ if (v >= tpf && v < besttpf) {
+ besttpf = v;
+ bestscfi = scf;
+ bestxferp = xferp;
+ }
+ }
+ }
+ if (besttpf == 1000)
+ return 0;
+ if (scfp)
+ *scfp = bestscfi;
+ if (xferpp)
+ *xferpp = bestxferp;
+ return besttpf;
+}
+
+static void
+synctabinit(Controller *c)
+{
+ int scf;
+ unsigned long scsilimit;
+ int xferp;
+ unsigned long cr, sr;
+ int tpf;
+ int fast;
+ int maxscf;
+
+ if (c->v->feature & Ultra2)
+ maxscf = NULTRA2SCF;
+ else if (c->v->feature & Ultra)
+ maxscf = NULTRASCF;
+ else
+ maxscf = NSCF;
+
+ /*
+ * for chips with no clock doubler, but Ultra capable (e.g. 860, or interestingly the
+ * first spin of the 875), assume 80MHz
+ * otherwise use the internal (33 Mhz) or external (40MHz) default
+ */
+
+ if ((c->v->feature & Ultra) != 0 && (c->v->feature & (ClockDouble | ClockQuad)) == 0)
+ c->sclk = ULTRA_NOCLOCKDOUBLE_SCLK;
+ else
+ c->sclk = SCLK;
+
+ /*
+ * otherwise, if the chip is Ultra capable, but has a slow(ish) clock,
+ * invoke the doubler
+ */
+
+ if (SCLK <= 40000000) {
+ if (c->v->feature & ClockDouble) {
+ c->sclk *= 2;
+ c->clockmult = 1;
+ }
+ else if (c->v->feature & ClockQuad) {
+ c->sclk *= 4;
+ c->clockmult = 1;
+ }
+ else
+ c->clockmult = 0;
+ }
+ else
+ c->clockmult = 0;
+
+ /* derive CCF from sclk */
+ /* woebetide anyone with SCLK < 16.7 or > 80MHz */
+ if (c->sclk <= 25 * MEGA)
+ c->ccf = 1;
+ else if (c->sclk <= 3750000)
+ c->ccf = 2;
+ else if (c->sclk <= 50 * MEGA)
+ c->ccf = 3;
+ else if (c->sclk <= 75 * MEGA)
+ c->ccf = 4;
+ else if ((c->v->feature & ClockDouble) && c->sclk <= 80 * MEGA)
+ c->ccf = 5;
+ else if ((c->v->feature & ClockQuad) && c->sclk <= 120 * MEGA)
+ c->ccf = 6;
+ else if ((c->v->feature & ClockQuad) && c->sclk <= 160 * MEGA)
+ c->ccf = 7;
+
+ for (scf = 1; scf < maxscf; scf++) {
+ /* check for legal core rate */
+ /* round up so we run slower for safety */
+ cr = (c->sclk * 2 + cf2[scf] - 1) / cf2[scf];
+ if (cr <= MAXSYNCCORERATE) {
+ scsilimit = MAXSYNCSCSIRATE;
+ fast = 0;
+ }
+ else if (cr <= MAXFASTSYNCCORERATE) {
+ scsilimit = MAXFASTSYNCSCSIRATE;
+ fast = 1;
+ }
+ else if ((c->v->feature & Ultra) && cr <= MAXULTRASYNCCORERATE) {
+ scsilimit = MAXULTRASYNCSCSIRATE;
+ fast = 2;
+ }
+ else if ((c->v->feature & Ultra2) && cr <= MAXULTRA2SYNCCORERATE) {
+ scsilimit = MAXULTRA2SYNCSCSIRATE;
+ fast = 3;
+ }
+ else
+ continue;
+ for (xferp = 11; xferp >= 4; xferp--) {
+ int ok;
+ int tp;
+ /* calculate scsi rate - round up again */
+ /* start from sclk for accuracy */
+ int totaldivide = xferp * cf2[scf];
+ sr = (c->sclk * 2 + totaldivide - 1) / totaldivide;
+ if (sr > scsilimit)
+ break;
+ /*
+ * now work out transfer period
+ * round down now so that period is pessimistic
+ */
+ tp = (MEGA * 1000) / sr;
+ /*
+ * bounds check it
+ */
+ if (tp < 25 || tp > 255 * 4)
+ continue;
+ /*
+ * spot stupid special case for Ultra or Ultra2
+ * while working out factor
+ */
+ if (tp == 25)
+ tpf = 10;
+ else if (tp == 50)
+ tpf = 12;
+ else if (tp < 52)
+ continue;
+ else
+ tpf = tp / 4;
+ /*
+ * now check tpf looks sensible
+ * given core rate
+ */
+ switch (fast) {
+ case 0:
+ /* scf must be ccf for SCSI 1 */
+ ok = tpf >= 50 && scf == c->ccf;
+ break;
+ case 1:
+ ok = tpf >= 25 && tpf < 50;
+ break;
+ case 2:
+ /*
+ * must use xferp of 4, or 5 at a pinch
+ * for an Ultra transfer
+ */
+ ok = xferp <= 5 && tpf >= 12 && tpf < 25;
+ break;
+ case 3:
+ ok = xferp == 4 && (tpf == 10 || tpf == 11);
+ break;
+ default:
+ ok = 0;
+ }
+ if (!ok)
+ continue;
+ c->synctab[scf - 1][xferp - 4] = tpf;
+ }
+ }
+
+#ifndef NO_ULTRA2
+ if (c->v->feature & Ultra2)
+ tpf = 10;
+ else
+#endif
+ if (c->v->feature & Ultra)
+ tpf = 12;
+ else
+ tpf = 25;
+ for (; tpf < 256; tpf++) {
+ if (chooserate(c, tpf, &scf, &xferp) == tpf) {
+ unsigned tp = tpf == 10 ? 25 : (tpf == 12 ? 50 : tpf * 4);
+ unsigned long khz = (MEGA + tp - 1) / (tp);
+ KPRINT("sd53c8xx: tpf=%d scf=%d.%.1d xferp=%d mhz=%ld.%.3ld\n",
+ tpf, cf2[scf] / 2, (cf2[scf] & 1) ? 5 : 0,
+ xferp + 4, khz / 1000, khz % 1000);
+ USED(khz);
+ if (c->tpf == 0)
+ c->tpf = tpf; /* note lowest value for controller */
+ }
+ }
+}
+
+static void
+synctodsa(Dsa *dsa, Controller *c)
+{
+/*
+ KPRINT("synctodsa(dsa=%lux, target=%d, scntl3=%.2lx sxfer=%.2x)\n",
+ dsa, dsa->target, c->scntl3[dsa->target], c->sxfer[dsa->target]);
+*/
+ dsa->scntl3 = c->scntl3[dsa->target];
+ dsa->sxfer = c->sxfer[dsa->target];
+}
+
+static void
+setsync(Dsa *dsa, Controller *c, int target, uchar ultra, uchar scf, uchar xferp, uchar reqack)
+{
+ c->scntl3[target] =
+ (c->scntl3[target] & 0x08) | (((scf << 4) | c->ccf | (ultra << 7)) & ~0x08);
+ c->sxfer[target] = (xferp << 5) | reqack;
+ c->s[target] = BothDone;
+ if (dsa) {
+ synctodsa(dsa, c);
+ c->n->scntl3 = c->scntl3[target];
+ c->n->sxfer = c->sxfer[target];
+ }
+}
+
+static void
+setasync(Dsa *dsa, Controller *c, int target)
+{
+ setsync(dsa, c, target, 0, c->ccf, 0, 0);
+}
+
+static void
+setwide(Dsa *dsa, Controller *c, int target, uchar wide)
+{
+ c->scntl3[target] = wide ? (1 << 3) : 0;
+ setasync(dsa, c, target);
+ c->s[target] = WideDone;
+}
+
+static int
+buildsdtrmsg(uchar *buf, uchar tpf, uchar offset)
+{
+ *buf++ = X_MSG;
+ *buf++ = 3;
+ *buf++ = X_MSG_SDTR;
+ *buf++ = tpf;
+ *buf = offset;
+ return 5;
+}
+
+static int
+buildwdtrmsg(uchar *buf, uchar expo)
+{
+ *buf++ = X_MSG;
+ *buf++ = 2;
+ *buf++ = X_MSG_WDTR;
+ *buf = expo;
+ return 4;
+}
+
+static void
+start(Controller *c, long entry)
+{
+ ulong p;
+
+ if (c->running)
+ panic("sd53c8xx: start called while running");
+ c->running = 1;
+ p = c->scriptpa + entry;
+ lesetl(c->n->dsp, p);
+ if (c->ssm)
+ c->n->dcntl |= 0x4; /* start DMA in SSI mode */
+}
+
+static void
+ncrcontinue(Controller *c)
+{
+ if (c->running)
+ panic("sd53c8xx: ncrcontinue called while running");
+ /* set the start DMA bit to continue execution */
+ c->running = 1;
+ c->n->dcntl |= 0x4;
+}
+
+static void
+softreset(Controller *c)
+{
+ Ncr *n = c->n;
+
+ n->istat = Srst; /* software reset */
+ n->istat = 0;
+ /* general initialisation */
+ n->scid = (1 << 6) | 7; /* respond to reselect, ID 7 */
+ n->respid = 1 << 7; /* response ID = 7 */
+
+#ifdef INTERNAL_SCLK
+ n->stest1 = 0x80; /* disable external scsi clock */
+#else
+ n->stest1 = 0x00;
+#endif
+
+ n->stime0 = 0xdd; /* about 0.5 second timeout on each device */
+ n->scntl0 |= 0x8; /* Enable parity checking */
+
+ /* continued setup */
+ n->sien0 = 0x8f;
+ n->sien1 = 0x04;
+ n->dien = 0x7d;
+ n->stest3 = 0x80; /* TolerANT enable */
+ c->running = 0;
+
+ if (c->v->feature & BigFifo)
+ n->ctest5 = (1 << 5);
+ n->dmode = c->v->burst << 6; /* set burst length bits */
+ if (c->v->burst & 4)
+ n->ctest5 |= (1 << 2); /* including overflow into ctest5 bit 2 */
+ if (c->v->feature & Prefetch)
+ n->dcntl |= (1 << 5); /* prefetch enable */
+ else if (c->v->feature & BurstOpCodeFetch)
+ n->dmode |= (1 << 1); /* burst opcode fetch */
+ if (c->v->feature & Differential) {
+ /* chip capable */
+ if ((c->feature & Differential) || bios_set_differential(c)) {
+ /* user enabled, or some evidence bios set differential */
+ if (n->sstat2 & (1 << 2))
+ print("sd53c8xx: can't go differential; wrong cable\n");
+ else {
+ n->stest2 = (1 << 5);
+ print("sd53c8xx: differential mode set\n");
+ }
+ }
+ }
+ if (c->clockmult) {
+ n->stest1 |= (1 << 3); /* power up doubler */
+ delay(2);
+ n->stest3 |= (1 << 5); /* stop clock */
+ n->stest1 |= (1 << 2); /* enable doubler */
+ n->stest3 &= ~(1 << 5); /* start clock */
+ /* pray */
+ }
+}
+
+static void
+msgsm(Dsa *dsa, Controller *c, int msg, int *cont, int *wakeme)
+{
+ uchar histpf, hisreqack;
+ int tpf;
+ int scf, xferp;
+ int len;
+
+ Ncr *n = c->n;
+
+ switch (c->s[dsa->target]) {
+ case SyncInit:
+ switch (msg) {
+ case A_SIR_MSG_SDTR:
+ /* reply to my SDTR */
+ histpf = n->scratcha[2];
+ hisreqack = n->scratcha[3];
+ KPRINT("sd53c8xx: %d: SDTN response %d %d\n",
+ dsa->target, histpf, hisreqack);
+
+ if (hisreqack == 0)
+ setasync(dsa, c, dsa->target);
+ else {
+ /* hisreqack should be <= c->v->maxsyncoff */
+ tpf = chooserate(c, histpf, &scf, &xferp);
+ KPRINT("sd53c8xx: %d: SDTN: using %d %d\n",
+ dsa->target, tpf, hisreqack);
+ setsync(dsa, c, dsa->target, tpf < 25, scf, xferp, hisreqack);
+ }
+ *cont = -2;
+ return;
+ case A_SIR_EV_PHASE_SWITCH_AFTER_ID:
+ /* target ignored ATN for message after IDENTIFY - not SCSI-II */
+ KPRINT("sd53c8xx: %d: illegal phase switch after ID message - SCSI-1 device?\n", dsa->target);
+ KPRINT("sd53c8xx: %d: SDTN: async\n", dsa->target);
+ setasync(dsa, c, dsa->target);
+ *cont = E_to_decisions;
+ return;
+ case A_SIR_MSG_REJECT:
+ /* rejection of my SDTR */
+ KPRINT("sd53c8xx: %d: SDTN: rejected SDTR\n", dsa->target);
+ //async:
+ KPRINT("sd53c8xx: %d: SDTN: async\n", dsa->target);
+ setasync(dsa, c, dsa->target);
+ *cont = -2;
+ return;
+ }
+ break;
+ case WideInit:
+ switch (msg) {
+ case A_SIR_MSG_WDTR:
+ /* reply to my WDTR */
+ KPRINT("sd53c8xx: %d: WDTN: response %d\n",
+ dsa->target, n->scratcha[2]);
+ setwide(dsa, c, dsa->target, n->scratcha[2]);
+ *cont = -2;
+ return;
+ case A_SIR_EV_PHASE_SWITCH_AFTER_ID:
+ /* target ignored ATN for message after IDENTIFY - not SCSI-II */
+ KPRINT("sd53c8xx: %d: illegal phase switch after ID message - SCSI-1 device?\n", dsa->target);
+ setwide(dsa, c, dsa->target, 0);
+ *cont = E_to_decisions;
+ return;
+ case A_SIR_MSG_REJECT:
+ /* rejection of my SDTR */
+ KPRINT("sd53c8xx: %d: WDTN: rejected WDTR\n", dsa->target);
+ setwide(dsa, c, dsa->target, 0);
+ *cont = -2;
+ return;
+ }
+ break;
+
+ case NeitherDone:
+ case WideDone:
+ case BothDone:
+ switch (msg) {
+ case A_SIR_MSG_WDTR: {
+ uchar hiswide, mywide;
+ hiswide = n->scratcha[2];
+ mywide = (c->v->feature & Wide) != 0;
+ KPRINT("sd53c8xx: %d: WDTN: target init %d\n",
+ dsa->target, hiswide);
+ if (hiswide < mywide)
+ mywide = hiswide;
+ KPRINT("sd53c8xx: %d: WDTN: responding %d\n",
+ dsa->target, mywide);
+ setwide(dsa, c, dsa->target, mywide);
+ len = buildwdtrmsg(dsa->msg_out, mywide);
+ setmovedata(&dsa->msg_out_buf, DMASEG(dsa->msg_out), len);
+ *cont = E_response;
+ c->s[dsa->target] = WideResponse;
+ return;
+ }
+ case A_SIR_MSG_SDTR:
+#ifdef ASYNC_ONLY
+ *cont = E_reject;
+ return;
+#else
+ /* target decides to renegotiate */
+ histpf = n->scratcha[2];
+ hisreqack = n->scratcha[3];
+ KPRINT("sd53c8xx: %d: SDTN: target init %d %d\n",
+ dsa->target, histpf, hisreqack);
+ if (hisreqack == 0) {
+ /* he wants asynchronous */
+ setasync(dsa, c, dsa->target);
+ tpf = 0;
+ }
+ else {
+ /* he wants synchronous */
+ tpf = chooserate(c, histpf, &scf, &xferp);
+ if (hisreqack > c->v->maxsyncoff)
+ hisreqack = c->v->maxsyncoff;
+ KPRINT("sd53c8xx: %d: using %d %d\n",
+ dsa->target, tpf, hisreqack);
+ setsync(dsa, c, dsa->target, tpf < 25, scf, xferp, hisreqack);
+ }
+ /* build my SDTR message */
+ len = buildsdtrmsg(dsa->msg_out, tpf, hisreqack);
+ setmovedata(&dsa->msg_out_buf, DMASEG(dsa->msg_out), len);
+ *cont = E_response;
+ c->s[dsa->target] = SyncResponse;
+ return;
+#endif
+ }
+ break;
+ case WideResponse:
+ switch (msg) {
+ case A_SIR_EV_RESPONSE_OK:
+ c->s[dsa->target] = WideDone;
+ KPRINT("sd53c8xx: %d: WDTN: response accepted\n", dsa->target);
+ *cont = -2;
+ return;
+ case A_SIR_MSG_REJECT:
+ setwide(dsa, c, dsa->target, 0);
+ KPRINT("sd53c8xx: %d: WDTN: response REJECTed\n", dsa->target);
+ *cont = -2;
+ return;
+ }
+ break;
+ case SyncResponse:
+ switch (msg) {
+ case A_SIR_EV_RESPONSE_OK:
+ c->s[dsa->target] = BothDone;
+ KPRINT("sd53c8xx: %d: SDTN: response accepted (%s)\n",
+ dsa->target, phase[n->sstat1 & 7]);
+ *cont = -2;
+ return; /* chf */
+ case A_SIR_MSG_REJECT:
+ setasync(dsa, c, dsa->target);
+ KPRINT("sd53c8xx: %d: SDTN: response REJECTed\n", dsa->target);
+ *cont = -2;
+ return;
+ }
+ break;
+ }
+ KPRINT("sd53c8xx: %d: msgsm: state %d msg %d\n",
+ dsa->target, c->s[dsa->target], msg);
+ *wakeme = 1;
+ return;
+}
+
+static void
+calcblockdma(Dsa *d, ulong base, ulong count)
+{
+ ulong blocks;
+ if (DEBUG(3))
+ blocks = 0;
+ else {
+ blocks = count / A_BSIZE;
+ if (blocks > 255)
+ blocks = 255;
+ }
+ d->dmablks = blocks;
+ d->dmaaddr[0] = base;
+ d->dmaaddr[1] = base >> 8;
+ d->dmaaddr[2] = base >> 16;
+ d->dmaaddr[3] = base >> 24;
+ setmovedata(&d->data_buf, base + blocks * A_BSIZE, count - blocks * A_BSIZE);
+ if (legetl(d->data_buf.dbc) == 0)
+ d->flag = 1;
+}
+
+static ulong
+read_mismatch_recover(Controller *c, Ncr *n, Dsa *dsa)
+{
+ ulong dbc;
+ uchar dfifo = n->dfifo;
+ int inchip;
+
+ dbc = (n->dbc[2]<<16)|(n->dbc[1]<<8)|n->dbc[0];
+ if (n->ctest5 & (1 << 5))
+ inchip = ((dfifo | ((n->ctest5 & 3) << 8)) - (dbc & 0x3ff)) & 0x3ff;
+ else
+ inchip = ((dfifo & 0x7f) - (dbc & 0x7f)) & 0x7f;
+ if (inchip) {
+ IPRINT("sd53c8xx: %d/%d: read_mismatch_recover: DMA FIFO = %d\n",
+ dsa->target, dsa->lun, inchip);
+ }
+ if (n->sxfer & 0xf) {
+ /* SCSI FIFO */
+ uchar fifo = n->sstat1 >> 4;
+ if (c->v->maxsyncoff > 8)
+ fifo |= (n->sstat2 & (1 << 4));
+ if (fifo) {
+ inchip += fifo;
+ IPRINT("sd53c8xx: %d/%d: read_mismatch_recover: SCSI FIFO = %d\n",
+ dsa->target, dsa->lun, fifo);
+ }
+ }
+ else {
+ if (n->sstat0 & (1 << 7)) {
+ inchip++;
+ IPRINT("sd53c8xx: %d/%d: read_mismatch_recover: SIDL full\n",
+ dsa->target, dsa->lun);
+ }
+ if (n->sstat2 & (1 << 7)) {
+ inchip++;
+ IPRINT("sd53c8xx: %d/%d: read_mismatch_recover: SIDL msb full\n",
+ dsa->target, dsa->lun);
+ }
+ }
+ USED(inchip);
+ return dbc;
+}
+
+static ulong
+write_mismatch_recover(Ncr *n, Dsa *dsa)
+{
+ ulong dbc;
+ uchar dfifo = n->dfifo;
+ int inchip;
+
+ dbc = (n->dbc[2]<<16)|(n->dbc[1]<<8)|n->dbc[0];
+ USED(dsa);
+ if (n->ctest5 & (1 << 5))
+ inchip = ((dfifo | ((n->ctest5 & 3) << 8)) - (dbc & 0x3ff)) & 0x3ff;
+ else
+ inchip = ((dfifo & 0x7f) - (dbc & 0x7f)) & 0x7f;
+#ifdef WMR_DEBUG
+ if (inchip) {
+ IPRINT("sd53c8xx: %d/%d: write_mismatch_recover: DMA FIFO = %d\n",
+ dsa->target, dsa->lun, inchip);
+ }
+#endif
+ if (n->sstat0 & (1 << 5)) {
+ inchip++;
+#ifdef WMR_DEBUG
+ IPRINT("sd53c8xx: %d/%d: write_mismatch_recover: SODL full\n", dsa->target, dsa->lun);
+#endif
+ }
+ if (n->sstat2 & (1 << 5)) {
+ inchip++;
+#ifdef WMR_DEBUG
+ IPRINT("sd53c8xx: %d/%d: write_mismatch_recover: SODL msb full\n", dsa->target, dsa->lun);
+#endif
+ }
+ if (n->sxfer & 0xf) {
+ /* synchronous SODR */
+ if (n->sstat0 & (1 << 6)) {
+ inchip++;
+#ifdef WMR_DEBUG
+ IPRINT("sd53c8xx: %d/%d: write_mismatch_recover: SODR full\n",
+ dsa->target, dsa->lun);
+#endif
+ }
+ if (n->sstat2 & (1 << 6)) {
+ inchip++;
+#ifdef WMR_DEBUG
+ IPRINT("sd53c8xx: %d/%d: write_mismatch_recover: SODR msb full\n",
+ dsa->target, dsa->lun);
+#endif
+ }
+ }
+ /* clear the dma fifo */
+ n->ctest3 |= (1 << 2);
+ /* wait till done */
+ while ((n->dstat & Dfe) == 0)
+ ;
+ return dbc + inchip;
+}
+
+static void
+interrupt(Ureg *ur, void *a)
+{
+ uchar istat;
+ ushort sist;
+ uchar dstat;
+ int wakeme = 0;
+ int cont = -1;
+ Dsa *dsa;
+ Controller *c = a;
+ Ncr *n = c->n;
+
+ USED(ur);
+ if (DEBUG(1))
+ IPRINT("sd53c8xx: int\n");
+ ilock(c);
+ istat = n->istat;
+ if (istat & Intf) {
+ Dsa *d;
+ int wokesomething = 0;
+ if (DEBUG(1))
+ IPRINT("sd53c8xx: Intfly\n");
+ n->istat = Intf;
+ /* search for structures in A_STATE_DONE */
+ for (d = KPTR(legetl(c->dsalist.head)); d; d = KPTR(legetl(d->next))) {
+ if (d->stateb == A_STATE_DONE) {
+ d->p9status = d->status;
+ if (DEBUG(1))
+ IPRINT("sd53c8xx: waking up dsa %lux\n", d);
+ wakeup(d);
+ wokesomething = 1;
+ }
+ }
+ if (!wokesomething)
+ IPRINT("sd53c8xx: nothing to wake up\n");
+ }
+
+ if ((istat & (Sip | Dip)) == 0) {
+ if (DEBUG(1))
+ IPRINT("sd53c8xx: int end %x\n", istat);
+ iunlock(c);
+ return;
+ }
+
+ sist = (n->sist1<<8)|n->sist0; /* BUG? can two-byte read be inconsistent? */
+ dstat = n->dstat;
+ dsa = (Dsa *)DMASEG_TO_KADDR(legetl(n->dsa));
+ c->running = 0;
+ if (istat & Sip) {
+ if (DEBUG(1))
+ IPRINT("sist = %.4x\n", sist);
+ if (sist & 0x80) {
+ ulong addr;
+ ulong sa;
+ ulong dbc;
+ ulong tbc;
+ int dmablks;
+ ulong dmaaddr;
+
+ addr = legetl(n->dsp);
+ sa = addr - c->scriptpa;
+ if (DEBUG(1) || DEBUG(2))
+ IPRINT("sd53c8xx: %d/%d: Phase Mismatch sa=%.8lux\n",
+ dsa->target, dsa->lun, sa);
+ /*
+ * now recover
+ */
+ if (sa == E_data_in_mismatch) {
+ dbc = read_mismatch_recover(c, n, dsa);
+ tbc = legetl(dsa->data_buf.dbc) - dbc;
+ advancedata(&dsa->data_buf, tbc);
+ if (DEBUG(1) || DEBUG(2))
+ IPRINT("sd53c8xx: %d/%d: transferred = %ld residue = %ld\n",
+ dsa->target, dsa->lun, tbc, legetl(dsa->data_buf.dbc));
+ cont = E_to_decisions;
+ }
+ else if (sa == E_data_in_block_mismatch) {
+ dbc = read_mismatch_recover(c, n, dsa);
+ tbc = A_BSIZE - dbc;
+ /* recover current state from registers */
+ dmablks = n->scratcha[2];
+ dmaaddr = legetl(n->scratchb);
+ /* we have got to dmaaddr + tbc */
+ /* we have dmablks * A_BSIZE - tbc + residue left to do */
+ /* so remaining transfer is */
+ IPRINT("in_block_mismatch: dmaaddr = 0x%lux tbc=%lud dmablks=%d\n",
+ dmaaddr, tbc, dmablks);
+ calcblockdma(dsa, dmaaddr + tbc,
+ dmablks * A_BSIZE - tbc + legetl(dsa->data_buf.dbc));
+ /* copy changes into scratch registers */
+ IPRINT("recalc: dmablks %d dmaaddr 0x%lx pa 0x%lx dbc %ld\n",
+ dsa->dmablks, legetl(dsa->dmaaddr),
+ legetl(dsa->data_buf.pa), legetl(dsa->data_buf.dbc));
+ n->scratcha[2] = dsa->dmablks;
+ lesetl(n->scratchb, dsa->dmancr);
+ cont = E_data_block_mismatch_recover;
+ }
+ else if (sa == E_data_out_mismatch) {
+ dbc = write_mismatch_recover(n, dsa);
+ tbc = legetl(dsa->data_buf.dbc) - dbc;
+ advancedata(&dsa->data_buf, tbc);
+ if (DEBUG(1) || DEBUG(2))
+ IPRINT("sd53c8xx: %d/%d: transferred = %ld residue = %ld\n",
+ dsa->target, dsa->lun, tbc, legetl(dsa->data_buf.dbc));
+ cont = E_to_decisions;
+ }
+ else if (sa == E_data_out_block_mismatch) {
+ dbc = write_mismatch_recover(n, dsa);
+ tbc = legetl(dsa->data_buf.dbc) - dbc;
+ /* recover current state from registers */
+ dmablks = n->scratcha[2];
+ dmaaddr = legetl(n->scratchb);
+ /* we have got to dmaaddr + tbc */
+ /* we have dmablks blocks - tbc + residue left to do */
+ /* so remaining transfer is */
+ IPRINT("out_block_mismatch: dmaaddr = %lux tbc=%lud dmablks=%d\n",
+ dmaaddr, tbc, dmablks);
+ calcblockdma(dsa, dmaaddr + tbc,
+ dmablks * A_BSIZE - tbc + legetl(dsa->data_buf.dbc));
+ /* copy changes into scratch registers */
+ n->scratcha[2] = dsa->dmablks;
+ lesetl(n->scratchb, dsa->dmancr);
+ cont = E_data_block_mismatch_recover;
+ }
+ else if (sa == E_id_out_mismatch) {
+ /*
+ * target switched phases while attention held during
+ * message out. The possibilities are:
+ * 1. It didn't like the last message. This is indicated
+ * by the new phase being message_in. Use script to recover
+ *
+ * 2. It's not SCSI-II compliant. The new phase will be other
+ * than message_in. We should also indicate that the device
+ * is asynchronous, if it's the SDTR that got ignored
+ *
+ * For now, if the phase switch is not to message_in, and
+ * and it happens after IDENTIFY and before SDTR, we
+ * notify the negotiation state machine.
+ */
+ ulong lim = legetl(dsa->msg_out_buf.dbc);
+ uchar p = n->sstat1 & 7;
+ dbc = write_mismatch_recover(n, dsa);
+ tbc = lim - dbc;
+ IPRINT("sd53c8xx: %d/%d: msg_out_mismatch: %lud/%lud sent, phase %s\n",
+ dsa->target, dsa->lun, tbc, lim, phase[p]);
+ if (p != MessageIn && tbc == 1) {
+ msgsm(dsa, c, A_SIR_EV_PHASE_SWITCH_AFTER_ID, &cont, &wakeme);
+ }
+ else
+ cont = E_id_out_mismatch_recover;
+ }
+ else if (sa == E_cmd_out_mismatch) {
+ /*
+ * probably the command count is longer than the device wants ...
+ */
+ ulong lim = legetl(dsa->cmd_buf.dbc);
+ uchar p = n->sstat1 & 7;
+ dbc = write_mismatch_recover(n, dsa);
+ tbc = lim - dbc;
+ IPRINT("sd53c8xx: %d/%d: cmd_out_mismatch: %lud/%lud sent, phase %s\n",
+ dsa->target, dsa->lun, tbc, lim, phase[p]);
+ USED(p, tbc);
+ cont = E_to_decisions;
+ }
+ else {
+ IPRINT("sd53c8xx: %d/%d: ma sa=%.8lux wanted=%s got=%s\n",
+ dsa->target, dsa->lun, sa,
+ phase[n->dcmd & 7],
+ phase[n->sstat1 & 7]);
+ dumpncrregs(c, 1);
+ dsa->p9status = SDeio; /* chf */
+ wakeme = 1;
+ }
+ }
+ /*else*/ if (sist & 0x400) {
+ if (DEBUG(0))
+ IPRINT("sd53c8xx: %d/%d Sto\n", dsa->target, dsa->lun);
+ dsa->p9status = SDtimeout;
+ dsa->stateb = A_STATE_DONE;
+ softreset(c);
+ cont = E_issue_check;
+ wakeme = 1;
+ }
+ if (sist & 0x1) {
+ IPRINT("sd53c8xx: %d/%d: parity error\n", dsa->target, dsa->lun);
+ dsa->parityerror = 1;
+ }
+ if (sist & 0x4) {
+ IPRINT("sd53c8xx: %d/%d: unexpected disconnect\n",
+ dsa->target, dsa->lun);
+ dumpncrregs(c, 1);
+ //wakeme = 1;
+ dsa->p9status = SDeio;
+ }
+ }
+ if (istat & Dip) {
+ if (DEBUG(1))
+ IPRINT("dstat = %.2x\n", dstat);
+ /*else*/ if (dstat & Ssi) {
+ ulong *p = DMASEG_TO_KADDR(legetl(n->dsp));
+ ulong w = (uchar *)p - (uchar *)c->script;
+ IPRINT("[%lux]", w);
+ USED(w);
+ cont = -2; /* restart */
+ }
+ if (dstat & Sir) {
+ switch (legetl(n->dsps)) {
+ case A_SIR_MSG_IO_COMPLETE:
+ dsa->p9status = dsa->status;
+ wakeme = 1;
+ break;
+ case A_SIR_MSG_SDTR:
+ case A_SIR_MSG_WDTR:
+ case A_SIR_MSG_REJECT:
+ case A_SIR_EV_RESPONSE_OK:
+ msgsm(dsa, c, legetl(n->dsps), &cont, &wakeme);
+ break;
+ case A_SIR_MSG_IGNORE_WIDE_RESIDUE:
+ /* back up one in the data transfer */
+ IPRINT("sd53c8xx: %d/%d: ignore wide residue %d, WSR = %d\n",
+ dsa->target, dsa->lun, n->scratcha[1], n->scntl2 & 1);
+ if (dsa->dmablks == 0 && dsa->flag)
+ IPRINT("sd53c8xx: %d/%d: transfer over; residue ignored\n",
+ dsa->target, dsa->lun);
+ else
+ calcblockdma(dsa, legetl(dsa->dmaaddr) - 1,
+ dsa->dmablks * A_BSIZE + legetl(dsa->data_buf.dbc) + 1);
+ cont = -2;
+ break;
+ case A_SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT:
+ IPRINT("sd53c8xx: %d: not msg_in after reselect (%s)",
+ n->ssid & 7, phase[n->sstat1 & 7]);
+ dsa = dsafind(c, n->ssid & 7, -1, A_STATE_DISCONNECTED);
+ dumpncrregs(c, 1);
+ wakeme = 1;
+ break;
+ case A_SIR_NOTIFY_MSG_IN:
+ IPRINT("sd53c8xx: %d/%d: msg_in %d\n",
+ dsa->target, dsa->lun, n->sfbr);
+ cont = -2;
+ break;
+ case A_SIR_NOTIFY_DISC:
+ IPRINT("sd53c8xx: %d/%d: disconnect:", dsa->target, dsa->lun);
+ goto dsadump;
+ case A_SIR_NOTIFY_STATUS:
+ IPRINT("sd53c8xx: %d/%d: status\n", dsa->target, dsa->lun);
+ cont = -2;
+ break;
+ case A_SIR_NOTIFY_COMMAND:
+ IPRINT("sd53c8xx: %d/%d: commands\n", dsa->target, dsa->lun);
+ cont = -2;
+ break;
+ case A_SIR_NOTIFY_DATA_IN:
+ IPRINT("sd53c8xx: %d/%d: data in a %lx b %lx\n",
+ dsa->target, dsa->lun, legetl(n->scratcha), legetl(n->scratchb));
+ cont = -2;
+ break;
+ case A_SIR_NOTIFY_BLOCK_DATA_IN:
+ IPRINT("sd53c8xx: %d/%d: block data in: a2 %x b %lx\n",
+ dsa->target, dsa->lun, n->scratcha[2], legetl(n->scratchb));
+ cont = -2;
+ break;
+ case A_SIR_NOTIFY_DATA_OUT:
+ IPRINT("sd53c8xx: %d/%d: data out\n", dsa->target, dsa->lun);
+ cont = -2;
+ break;
+ case A_SIR_NOTIFY_DUMP:
+ IPRINT("sd53c8xx: %d/%d: dump\n", dsa->target, dsa->lun);
+ dumpncrregs(c, 1);
+ cont = -2;
+ break;
+ case A_SIR_NOTIFY_DUMP2:
+ IPRINT("sd53c8xx: %d/%d: dump2:", dsa->target, dsa->lun);
+ IPRINT(" sa %lux", legetl(n->dsp) - c->scriptpa);
+ IPRINT(" dsa %lux", legetl(n->dsa));
+ IPRINT(" sfbr %ux", n->sfbr);
+ IPRINT(" a %lux", n->scratcha);
+ IPRINT(" b %lux", legetl(n->scratchb));
+ IPRINT(" ssid %ux", n->ssid);
+ IPRINT("\n");
+ cont = -2;
+ break;
+ case A_SIR_NOTIFY_WAIT_RESELECT:
+ IPRINT("sd53c8xx: wait reselect\n");
+ cont = -2;
+ break;
+ case A_SIR_NOTIFY_RESELECT:
+ IPRINT("sd53c8xx: reselect: ssid %.2x sfbr %.2x at %ld\n",
+ n->ssid, n->sfbr, TK2MS(m->ticks));
+ cont = -2;
+ break;
+ case A_SIR_NOTIFY_ISSUE:
+ IPRINT("sd53c8xx: %d/%d: issue:", dsa->target, dsa->lun);
+ dsadump:
+ IPRINT(" tgt=%d", dsa->target);
+ IPRINT(" time=%ld", TK2MS(m->ticks));
+ IPRINT("\n");
+ cont = -2;
+ break;
+ case A_SIR_NOTIFY_ISSUE_CHECK:
+ IPRINT("sd53c8xx: issue check\n");
+ cont = -2;
+ break;
+ case A_SIR_NOTIFY_SIGP:
+ IPRINT("sd53c8xx: responded to SIGP\n");
+ cont = -2;
+ break;
+ case A_SIR_NOTIFY_DUMP_NEXT_CODE: {
+ ulong *dsp = DMASEG_TO_KADDR(legetl(n->dsp));
+ int x;
+ IPRINT("sd53c8xx: code at %lux", dsp - c->script);
+ for (x = 0; x < 6; x++)
+ IPRINT(" %.8lux", dsp[x]);
+ IPRINT("\n");
+ USED(dsp);
+ cont = -2;
+ break;
+ }
+ case A_SIR_NOTIFY_WSR:
+ IPRINT("sd53c8xx: %d/%d: WSR set\n", dsa->target, dsa->lun);
+ cont = -2;
+ break;
+ case A_SIR_NOTIFY_LOAD_SYNC:
+ IPRINT("sd53c8xx: %d/%d: scntl=%.2x sxfer=%.2x\n",
+ dsa->target, dsa->lun, n->scntl3, n->sxfer);
+ cont = -2;
+ break;
+ case A_SIR_NOTIFY_RESELECTED_ON_SELECT:
+ IPRINT("sd53c8xx: %d/%d: reselected during select\n",
+ dsa->target, dsa->lun);
+ cont = -2;
+ break;
+ default:
+ IPRINT("sd53c8xx: %d/%d: script error %ld\n",
+ dsa->target, dsa->lun, legetl(n->dsps));
+ dumpncrregs(c, 1);
+ wakeme = 1;
+ }
+ }
+ /*else*/ if (dstat & Iid) {
+ ulong addr = legetl(n->dsp);
+ ulong dbc = (n->dbc[2]<<16)|(n->dbc[1]<<8)|n->dbc[0];
+ IPRINT("sd53c8xx: %d/%d: Iid pa=%.8lux sa=%.8lux dbc=%lux\n",
+ dsa->target, dsa->lun,
+ addr, addr - c->scriptpa, dbc);
+ addr = (ulong)DMASEG_TO_KADDR(addr);
+ IPRINT("%.8lux %.8lux %.8lux\n",
+ *(ulong *)(addr - 12), *(ulong *)(addr - 8), *(ulong *)(addr - 4));
+ USED(addr, dbc);
+ dsa->p9status = SDeio;
+ wakeme = 1;
+ }
+ /*else*/ if (dstat & Bf) {
+ IPRINT("sd53c8xx: %d/%d: Bus Fault\n", dsa->target, dsa->lun);
+ dumpncrregs(c, 1);
+ dsa->p9status = SDeio;
+ wakeme = 1;
+ }
+ }
+ if (cont == -2)
+ ncrcontinue(c);
+ else if (cont >= 0)
+ start(c, cont);
+ if (wakeme){
+ if(dsa->p9status == SDnostatus)
+ dsa->p9status = SDeio;
+ wakeup(dsa);
+ }
+ iunlock(c);
+ if (DEBUG(1)) {
+ IPRINT("sd53c8xx: int end 1\n");
+ }
+}
+
+static int
+done(void *arg)
+{
+ return ((Dsa *)arg)->p9status != SDnostatus;
+}
+
+static void
+setmovedata(Movedata *d, ulong pa, ulong bc)
+{
+ d->pa[0] = pa;
+ d->pa[1] = pa>>8;
+ d->pa[2] = pa>>16;
+ d->pa[3] = pa>>24;
+ d->dbc[0] = bc;
+ d->dbc[1] = bc>>8;
+ d->dbc[2] = bc>>16;
+ d->dbc[3] = bc>>24;
+}
+
+static void
+advancedata(Movedata *d, long v)
+{
+ lesetl(d->pa, legetl(d->pa) + v);
+ lesetl(d->dbc, legetl(d->dbc) - v);
+}
+
+static void
+dumpwritedata(uchar *data, int datalen)
+{
+ int i;
+ uchar *bp;
+ if (!DEBUG(0)){
+ USED(data, datalen);
+ return;
+ }
+
+ if (datalen) {
+ KPRINT("sd53c8xx:write:");
+ for (i = 0, bp = data; i < 50 && i < datalen; i++, bp++)
+ KPRINT("%.2ux", *bp);
+ if (i < datalen) {
+ KPRINT("...");
+ }
+ KPRINT("\n");
+ }
+}
+
+static void
+dumpreaddata(uchar *data, int datalen)
+{
+ int i;
+ uchar *bp;
+ if (!DEBUG(0)){
+ USED(data, datalen);
+ return;
+ }
+
+ if (datalen) {
+ KPRINT("sd53c8xx:read:");
+ for (i = 0, bp = data; i < 50 && i < datalen; i++, bp++)
+ KPRINT("%.2ux", *bp);
+ if (i < datalen) {
+ KPRINT("...");
+ }
+ KPRINT("\n");
+ }
+}
+
+static void
+busreset(Controller *c)
+{
+ int x, ntarget;
+
+ /* bus reset */
+ c->n->scntl1 |= (1 << 3);
+ delay(500);
+ c->n->scntl1 &= ~(1 << 3);
+ if(!(c->v->feature & Wide))
+ ntarget = 8;
+ else
+ ntarget = MAXTARGET;
+ for (x = 0; x < ntarget; x++) {
+ setwide(0, c, x, 0);
+#ifndef ASYNC_ONLY
+ c->s[x] = NeitherDone;
+#endif
+ }
+ c->capvalid = 0;
+}
+
+static void
+reset(Controller *c)
+{
+ /* should wakeup all pending tasks */
+ softreset(c);
+ busreset(c);
+}
+
+static int
+symrio(SDreq* r)
+{
+ Dsa *d;
+ uchar *bp;
+ Controller *c;
+ uchar target_expo, my_expo;
+ int bc, check, status, target;
+
+ if((target = r->unit->subno) == 0x07)
+ return r->status = SDtimeout; /* assign */
+ c = r->unit->dev->ctlr;
+
+ check = 0;
+ d = dsaalloc(c, target, r->lun);
+
+ qlock(&c->q[target]); /* obtain access to target */
+docheck:
+ /* load the transfer control stuff */
+ d->scsi_id_buf[0] = 0;
+ d->scsi_id_buf[1] = c->sxfer[target];
+ d->scsi_id_buf[2] = target;
+ d->scsi_id_buf[3] = c->scntl3[target];
+ synctodsa(d, c);
+
+ bc = 0;
+
+ d->msg_out[bc] = 0x80 | r->lun;
+
+#ifndef NO_DISCONNECT
+ d->msg_out[bc] |= (1 << 6);
+#endif
+ bc++;
+
+ /* work out what to do about negotiation */
+ switch (c->s[target]) {
+ default:
+ KPRINT("sd53c8xx: %d: strange nego state %d\n", target, c->s[target]);
+ c->s[target] = NeitherDone;
+ /* fall through */
+ case NeitherDone:
+ if ((c->capvalid & (1 << target)) == 0)
+ break;
+ target_expo = (c->cap[target] >> 5) & 3;
+ my_expo = (c->v->feature & Wide) != 0;
+ if (target_expo < my_expo)
+ my_expo = target_expo;
+#ifdef ALWAYS_DO_WDTR
+ bc += buildwdtrmsg(d->msg_out + bc, my_expo);
+ KPRINT("sd53c8xx: %d: WDTN: initiating expo %d\n", target, my_expo);
+ c->s[target] = WideInit;
+ break;
+#else
+ if (my_expo) {
+ bc += buildwdtrmsg(d->msg_out + bc, (c->v->feature & Wide) ? 1 : 0);
+ KPRINT("sd53c8xx: %d: WDTN: initiating expo %d\n", target, my_expo);
+ c->s[target] = WideInit;
+ break;
+ }
+ KPRINT("sd53c8xx: %d: WDTN: narrow\n", target);
+ /* fall through */
+#endif
+ case WideDone:
+ if (c->cap[target] & (1 << 4)) {
+ KPRINT("sd53c8xx: %d: SDTN: initiating %d %d\n", target, c->tpf, c->v->maxsyncoff);
+ bc += buildsdtrmsg(d->msg_out + bc, c->tpf, c->v->maxsyncoff);
+ c->s[target] = SyncInit;
+ break;
+ }
+ KPRINT("sd53c8xx: %d: SDTN: async only\n", target);
+ c->s[target] = BothDone;
+ break;
+
+ case BothDone:
+ break;
+ }
+
+ setmovedata(&d->msg_out_buf, DMASEG(d->msg_out), bc);
+ setmovedata(&d->cmd_buf, DMASEG(r->cmd), r->clen);
+ calcblockdma(d, DMASEG(r->data), r->dlen);
+
+ if (DEBUG(0)) {
+ KPRINT("sd53c8xx: %d/%d: exec: ", target, r->lun);
+ for (bp = r->cmd; bp < &r->cmd[r->clen]; bp++)
+ KPRINT("%.2ux", *bp);
+ KPRINT("\n");
+ if (!r->write)
+ KPRINT("sd53c8xx: %d/%d: exec: limit=(%d)%ld\n",
+ target, r->lun, d->dmablks, legetl(d->data_buf.dbc));
+ else
+ dumpwritedata(r->data, r->dlen);
+ }
+
+ setmovedata(&d->status_buf, DMASEG(&d->status), 1);
+
+ d->p9status = SDnostatus;
+ d->parityerror = 0;
+
+ d->stateb = A_STATE_ISSUE; /* start operation */
+
+ ilock(c);
+ if (c->ssm)
+ c->n->dcntl |= 0x10; /* SSI */
+ if (c->running) {
+ c->n->istat |= Sigp;
+ }
+ else {
+ start(c, E_issue_check);
+ }
+ iunlock(c);
+
+ while(waserror())
+ ;
+ tsleep(d, done, d, 30 * 1000);
+ poperror();
+
+ if (!done(d)) {
+ KPRINT("sd53c8xx: %d/%d: exec: Timed out\n", target, r->lun);
+ dumpncrregs(c, 0);
+ dsafree(c, d);
+ reset(c);
+ qunlock(&c->q[target]);
+ r->status = SDtimeout;
+ return r->status = SDtimeout; /* assign */
+ }
+
+ if((status = d->p9status) == SDeio)
+ c->s[target] = NeitherDone;
+ if (d->parityerror) {
+ status = SDeio;
+ }
+
+ /*
+ * adjust datalen
+ */
+ r->rlen = r->dlen;
+ if (d->dmablks > 0)
+ r->rlen -= d->dmablks * A_BSIZE;
+ else if (d->flag == 0)
+ r->rlen -= legetl(d->data_buf.dbc);
+ if(!r->write)
+ dumpreaddata(r->data, r->rlen);
+ if (DEBUG(0))
+ KPRINT("53c8xx: %d/%d: exec: p9status=%d status %d rlen %ld\n",
+ target, r->lun, d->p9status, status, r->rlen);
+ /*
+ * spot the identify
+ */
+ if ((c->capvalid & (1 << target)) == 0
+ && (status == SDok || status == SDcheck)
+ && r->cmd[0] == 0x12 && r->dlen >= 8) {
+ c->capvalid |= 1 << target;
+ bp = r->data;
+ c->cap[target] = bp[7];
+ KPRINT("sd53c8xx: %d: capabilities %.2x\n", target, bp[7]);
+ }
+ if(!check && status == SDcheck && !(r->flags & SDnosense)){
+ check = 1;
+ r->write = 0;
+ memset(r->cmd, 0, sizeof(r->cmd));
+ r->cmd[0] = 0x03;
+ r->cmd[1] = r->lun<<5;
+ r->cmd[4] = sizeof(r->sense)-1;
+ r->clen = 6;
+ r->data = r->sense;
+ r->dlen = sizeof(r->sense)-1;
+ /*
+ * Clear out the microcode state
+ * so the Dsa can be re-used.
+ */
+ lesetl(d->state, A_STATE_ALLOCATED);
+ goto docheck;
+ }
+ qunlock(&c->q[target]);
+ dsafree(c, d);
+
+ if(status == SDok && check){
+ status = SDcheck;
+ r->flags |= SDvalidsense;
+ }
+ KPRINT("sd53c8xx: %d: r flags %8.8uX status %d rlen %ld\n",
+ target, r->flags, status, r->rlen);
+ return r->status = status;
+}
+
+static void
+cribbios(Controller *c)
+{
+ c->bios.scntl3 = c->n->scntl3;
+ c->bios.stest2 = c->n->stest2;
+ print("sd53c8xx: bios scntl3(%.2x) stest2(%.2x)\n", c->bios.scntl3, c->bios.stest2);
+}
+
+static int
+bios_set_differential(Controller *c)
+{
+ /* Concept lifted from FreeBSD - thanks Gerard */
+ /* basically, if clock conversion factors are set, then there is
+ * evidence the bios had a go at the chip, and if so, it would
+ * have set the differential enable bit in stest2
+ */
+ return (c->bios.scntl3 & 7) != 0 && (c->bios.stest2 & 0x20) != 0;
+}
+
+#define NCR_VID 0x1000
+#define NCR_810_DID 0x0001
+#define NCR_820_DID 0x0002 /* don't know enough about this one to support it */
+#define NCR_825_DID 0x0003
+#define NCR_815_DID 0x0004
+#define SYM_810AP_DID 0x0005
+#define SYM_860_DID 0x0006
+#define SYM_896_DID 0x000b
+#define SYM_895_DID 0x000c
+#define SYM_885_DID 0x000d /* ditto */
+#define SYM_875_DID 0x000f /* ditto */
+#define SYM_1010_DID 0x0020
+#define SYM_1011_DID 0x0021
+#define SYM_875J_DID 0x008f
+
+static Variant variant[] = {
+{ NCR_810_DID, 0x0f, "NCR53C810", Burst16, 8, 24, 0 },
+{ NCR_810_DID, 0x1f, "SYM53C810ALV", Burst16, 8, 24, Prefetch },
+{ NCR_810_DID, 0xff, "SYM53C810A", Burst16, 8, 24, Prefetch },
+{ SYM_810AP_DID, 0xff, "SYM53C810AP", Burst16, 8, 24, Prefetch },
+{ NCR_815_DID, 0xff, "NCR53C815", Burst16, 8, 24, BurstOpCodeFetch },
+{ NCR_825_DID, 0x0f, "NCR53C825", Burst16, 8, 24, Wide|BurstOpCodeFetch|Differential },
+{ NCR_825_DID, 0xff, "SYM53C825A", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide },
+{ SYM_860_DID, 0x0f, "SYM53C860", Burst16, 8, 24, Prefetch|Ultra },
+{ SYM_860_DID, 0xff, "SYM53C860LV", Burst16, 8, 24, Prefetch|Ultra },
+{ SYM_875_DID, 0x01, "SYM53C875r1", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide|Ultra },
+{ SYM_875_DID, 0xff, "SYM53C875", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide|Ultra|ClockDouble },
+{ SYM_875J_DID, 0xff, "SYM53C875j", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Differential|Wide|Ultra|ClockDouble },
+{ SYM_885_DID, 0xff, "SYM53C885", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Wide|Ultra|ClockDouble },
+{ SYM_895_DID, 0xff, "SYM53C895", Burst128, 16, 24, Prefetch|LocalRAM|BigFifo|Wide|Ultra|Ultra2 },
+{ SYM_896_DID, 0xff, "SYM53C896", Burst128, 16, 64, Prefetch|LocalRAM|BigFifo|Wide|Ultra|Ultra2 },
+{ SYM_1010_DID, 0xff, "SYM53C1010", Burst128, 16, 64, Prefetch|LocalRAM|BigFifo|Wide|Ultra|Ultra2 },
+{ SYM_1011_DID, 0xff, "SYM53C1010", Burst128, 16, 64, Prefetch|LocalRAM|BigFifo|Wide|Ultra|Ultra2 },
+};
+
+#define offsetof(s, t) ((ulong)&((s *)0)->t)
+
+static int
+xfunc(Controller *c, enum na_external x, unsigned long *v)
+{
+ switch (x)
+ {
+ case X_scsi_id_buf:
+ *v = offsetof(Dsa, scsi_id_buf[0]); return 1;
+ case X_msg_out_buf:
+ *v = offsetof(Dsa, msg_out_buf); return 1;
+ case X_cmd_buf:
+ *v = offsetof(Dsa, cmd_buf); return 1;
+ case X_data_buf:
+ *v = offsetof(Dsa, data_buf); return 1;
+ case X_status_buf:
+ *v = offsetof(Dsa, status_buf); return 1;
+ case X_dsa_head:
+ *v = DMASEG(&c->dsalist.head[0]); return 1;
+ default:
+ print("xfunc: can't find external %d\n", x);
+ return 0;
+ }
+ return 1;
+}
+
+static int
+na_fixup(Controller *c, ulong pa_reg,
+ struct na_patch *patch, int patches,
+ int (*externval)(Controller*, int, ulong*))
+{
+ int p;
+ int v;
+ ulong *script, pa_script;
+ unsigned long lw, lv;
+
+ script = c->script;
+ pa_script = c->scriptpa;
+ for (p = 0; p < patches; p++) {
+ switch (patch[p].type) {
+ case 1:
+ /* script relative */
+ script[patch[p].lwoff] += pa_script;
+ break;
+ case 2:
+ /* register i/o relative */
+ script[patch[p].lwoff] += pa_reg;
+ break;
+ case 3:
+ /* data external */
+ lw = script[patch[p].lwoff];
+ v = (lw >> 8) & 0xff;
+ if (!(*externval)(c, v, &lv))
+ return 0;
+ v = lv & 0xff;
+ script[patch[p].lwoff] = (lw & 0xffff00ffL) | (v << 8);
+ break;
+ case 4:
+ /* 32 bit external */
+ lw = script[patch[p].lwoff];
+ if (!(*externval)(c, lw, &lv))
+ return 0;
+ script[patch[p].lwoff] = lv;
+ break;
+ case 5:
+ /* 24 bit external */
+ lw = script[patch[p].lwoff];
+ if (!(*externval)(c, lw & 0xffffff, &lv))
+ return 0;
+ script[patch[p].lwoff] = (lw & 0xff000000L) | (lv & 0xffffffL);
+ break;
+ }
+ }
+ return 1;
+}
+
+static SDev*
+sympnp(void)
+{
+ int ba;
+ Pcidev *p;
+ Variant *v;
+ void *scriptma;
+ Controller *ctlr;
+ SDev *sdev, *head, *tail;
+ ulong regpa, *script, scriptpa;
+
+ p = nil;
+ head = tail = nil;
+ while(p = pcimatch(p, NCR_VID, 0)){
+ for(v = variant; v < &variant[nelem(variant)]; v++){
+ if(p->did == v->did && p->rid <= v->maxrid)
+ break;
+ }
+ if(v >= &variant[nelem(variant)])
+ continue;
+ print("sd53c8xx: %s rev. 0x%2.2x intr=%d command=%4.4luX\n",
+ v->name, p->rid, p->intl, p->pcr);
+
+ regpa = p->mem[1].bar;
+ ba = 2;
+ if(regpa & 0x04){
+ if(p->mem[2].bar)
+ continue;
+ ba++;
+ }
+ regpa = upamalloc(regpa & ~0x0F, p->mem[1].size, 0);
+ if(regpa == 0)
+ continue;
+
+ script = nil;
+ scriptpa = 0;
+ scriptma = nil;
+ if((v->feature & LocalRAM) && sizeof(na_script) <= 4096){
+ scriptpa = p->mem[ba].bar;
+ if((scriptpa & 0x04) && p->mem[ba+1].bar){
+ upafree(regpa, p->mem[1].size);
+ continue;
+ }
+ scriptpa = upamalloc(scriptpa & ~0x0F,
+ p->mem[ba].size, 0);
+ if(scriptpa)
+ script = KADDR(scriptpa);
+ }
+ if(scriptpa == 0){
+ /*
+ * Either the map failed, or this chip does not have
+ * local RAM. It will need a copy of the microcode.
+ */
+ scriptma = malloc(sizeof(na_script));
+ if(scriptma == nil){
+ upafree(regpa, p->mem[1].size);
+ continue;
+ }
+ scriptpa = DMASEG(scriptma);
+ script = scriptma;
+ }
+
+ ctlr = malloc(sizeof(Controller));
+ sdev = malloc(sizeof(SDev));
+ if(ctlr == nil || sdev == nil){
+buggery:
+ if(ctlr)
+ free(ctlr);
+ if(sdev)
+ free(sdev);
+ if(scriptma)
+ free(scriptma);
+ else
+ upafree(scriptpa, p->mem[ba].size);
+ upafree(regpa, p->mem[1].size);
+ continue;
+ }
+
+ ctlr->n = KADDR(regpa);
+ ctlr->v = v;
+ ctlr->script = script;
+ memmove(ctlr->script, na_script, sizeof(na_script));
+ ctlr->scriptpa = scriptpa;
+ if(!na_fixup(ctlr, regpa, na_patches, NA_PATCHES, xfunc)){
+ print("script fixup failed\n");
+ goto buggery;
+ }
+ swabl(ctlr->script, ctlr->script, sizeof(na_script));
+
+ ctlr->dsalist.freechain = 0;
+ lesetl(ctlr->dsalist.head, 0);
+
+ ctlr->pcidev = p;
+
+ sdev->ifc = &sd53c8xxifc;
+ sdev->ctlr = ctlr;
+ if(!(v->feature & Wide))
+ sdev->nunit = 8;
+ else
+ sdev->nunit = MAXTARGET;
+ ctlr->sdev = sdev;
+
+ if(head != nil)
+ tail->next = sdev;
+ else
+ head = sdev;
+ tail = sdev;
+ }
+
+ return head;
+}
+
+static SDev*
+symid(SDev* sdev)
+{
+ return scsiid(sdev, &sd53c8xxifc);
+}
+
+static int
+symenable(SDev* sdev)
+{
+ Pcidev *pcidev;
+ Controller *ctlr;
+ //char name[NAMELEN];
+
+ ctlr = sdev->ctlr;
+ pcidev = ctlr->pcidev;
+
+ pcisetbme(pcidev);
+ //snprint(name, NAMELEN, "%s (%s)", sdev->name, sdev->ifc->name);
+ intrenable(pcidev->intl, interrupt, ctlr, pcidev->tbdf, name);
+
+ ilock(ctlr);
+ synctabinit(ctlr);
+ cribbios(ctlr);
+ reset(ctlr);
+ iunlock(ctlr);
+
+ return 1;
+}
+
+static int
+symdisable(SDev* sdev)
+{
+ Ncr *n;
+ Controller *ctlr;
+
+ ctlr = sdev->ctlr;
+ n = ctlr->n;
+
+ n->istat = Srst; /* software reset */
+ microdelay(1);
+ n->istat = 0;
+
+ n->scntl1 |= (1 << 3); /* bus reset */
+ delay(500);
+ n->scntl1 &= ~(1 << 3);
+
+ return 1;
+}
+
+SDifc sd53c8xxifc = {
+ "53c8xx", /* name */
+
+ sympnp, /* pnp */
+ nil, /* legacy */
+ symid, /* id */
+ symenable, /* enable */
+ symdisable, /* disable */
+
+ scsiverify, /* verify */
+ scsionline, /* online */
+ symrio, /* rio */
+ nil, /* rctl */
+ nil, /* wctl */
+
+ scsibio, /* bio */
+};
diff --git a/os/boot/pc/sd53c8xx.i b/os/boot/pc/sd53c8xx.i
new file mode 100644
index 00000000..50322366
--- /dev/null
+++ b/os/boot/pc/sd53c8xx.i
@@ -0,0 +1,769 @@
+unsigned long na_script[] = {
+ /* extern scsi_id_buf */
+ /* extern msg_out_buf */
+ /* extern cmd_buf */
+ /* extern data_buf */
+ /* extern status_buf */
+ /* extern msgin_buf */
+ /* extern dsa_0 */
+ /* extern dsa_1 */
+ /* extern dsa_head */
+ /* SIR_MSG_IO_COMPLETE = 0 */
+ /* error_not_cmd_complete = 1 */
+ /* error_disconnected = 2 */
+ /* error_reselected = 3 */
+ /* error_unexpected_phase = 4 */
+ /* error_weird_message = 5 */
+ /* SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT = 6 */
+ /* error_not_identify_after_reselect = 7 */
+ /* error_too_much_data = 8 */
+ /* error_too_little_data = 9 */
+ /* SIR_MSG_REJECT = 10 */
+ /* SIR_MSG_SDTR = 11 */
+ /* SIR_EV_RESPONSE_OK = 12 */
+ /* error_sigp_set = 13 */
+ /* SIR_EV_PHASE_SWITCH_AFTER_ID = 14 */
+ /* SIR_MSG_WDTR = 15 */
+ /* SIR_MSG_IGNORE_WIDE_RESIDUE = 16 */
+ /* SIR_NOTIFY_DISC = 100 */
+ /* SIR_NOTIFY_RESELECT = 101 */
+ /* SIR_NOTIFY_MSG_IN = 102 */
+ /* SIR_NOTIFY_STATUS = 103 */
+ /* SIR_NOTIFY_DUMP = 104 */
+ /* SIR_NOTIFY_DUMP2 = 105 */
+ /* SIR_NOTIFY_SIGP = 106 */
+ /* SIR_NOTIFY_ISSUE = 107 */
+ /* SIR_NOTIFY_WAIT_RESELECT = 108 */
+ /* SIR_NOTIFY_ISSUE_CHECK = 109 */
+ /* SIR_NOTIFY_DUMP_NEXT_CODE = 110 */
+ /* SIR_NOTIFY_COMMAND = 111 */
+ /* SIR_NOTIFY_DATA_IN = 112 */
+ /* SIR_NOTIFY_DATA_OUT = 113 */
+ /* SIR_NOTIFY_BLOCK_DATA_IN = 114 */
+ /* SIR_NOTIFY_WSR = 115 */
+ /* SIR_NOTIFY_LOAD_SYNC = 116 */
+ /* SIR_NOTIFY_RESELECTED_ON_SELECT = 117 */
+ /* STATE_FREE = 0 */
+ /* STATE_ALLOCATED = 1 */
+ /* STATE_ISSUE = 2 */
+ /* STATE_DISCONNECTED = 3 */
+ /* STATE_DONE = 4 */
+ /* RESULT_OK = 0 */
+ /* MSG_IDENTIFY = 0x80 */
+ /* MSG_DISCONNECT = 0x04 */
+ /* MSG_SAVE_DATA_POINTER = 0x02 */
+ /* MSG_RESTORE_POINTERS = 0x03 */
+ /* MSG_IGNORE_WIDE_RESIDUE = 0x23 */
+ /* X_MSG = 0x01 */
+ /* X_MSG_SDTR = 0x01 */
+ /* X_MSG_WDTR = 0x03 */
+ /* MSG_REJECT = 0x07 */
+ /* BSIZE = 512 */
+/* 0000 */ 0x80880000L, /* jump wait_for_reselection */
+/* 0004 */ 0x00000514L,
+/* 0008 */ 0x88880000L, /* call load_sync */
+/* 000c */ 0x0000074cL,
+/* 0010 */ 0x60000200L, /* clear target */
+/* 0014 */ 0x00000000L,
+/* 0018 */ 0x47000000L, /* select atn from scsi_id_buf, reselected_on_select */
+/* 001c */ 0x000004ecL,
+/* 0020 */ 0x878b0000L, /* jump start1, when msg_in */
+/* 0024 */ 0x00000000L,
+/* 0028 */ 0x1e000000L, /* move from msg_out_buf, when msg_out */
+/* 002c */ 0x00000001L,
+/* 0030 */ 0x868b0000L, /* jump start1, when msg_out */
+/* 0034 */ 0x00fffff0L,
+/* 0038 */ 0x82830000L, /* jump to_decisions, when not cmd */
+/* 003c */ 0x000005f0L,
+/* 0040 */ 0x60000008L, /* clear atn */
+/* 0044 */ 0x00000000L,
+/* 0048 */ 0x1a000000L, /* move from cmd_buf, when cmd */
+/* 004c */ 0x00000002L,
+/* 0050 */ 0x81830000L, /* jump to_decisions, when not data_in */
+/* 0054 */ 0x000005d8L,
+/* 0058 */ 0xc0000004L, /* move memory 4, state, scratcha */
+/* 005c */ 0x00000678L,
+/* 0060 */ 0x00000034L,
+/* 0064 */ 0xc0000004L, /* move memory 4, dmaaddr, scratchb */
+/* 0068 */ 0x0000067cL,
+/* 006c */ 0x0000005cL,
+/* 0070 */ 0x72360000L, /* move scratcha2 to sfbr */
+/* 0074 */ 0x00000000L,
+/* 0078 */ 0x808c0000L, /* jump data_in_normal, if 0 */
+/* 007c */ 0x00000078L,
+/* 0080 */ 0x29000200L, /* move BSIZE, ptr dmaaddr, when data_in */
+/* 0084 */ 0x0000067cL,
+/* 0088 */ 0x7e5d0200L, /* move scratchb1 + BSIZE / 256 to scratchb1 */
+/* 008c */ 0x00000000L,
+/* 0090 */ 0x7f5e0000L, /* move scratchb2 + 0 to scratchb2 with carry */
+/* 0094 */ 0x00000000L,
+/* 0098 */ 0x7f5f0000L, /* move scratchb3 + 0 to scratchb3 with carry */
+/* 009c */ 0x00000000L,
+/* 00a0 */ 0x7e36ff00L, /* move scratcha2 + 255 to scratcha2 */
+/* 00a4 */ 0x00000000L,
+/* 00a8 */ 0xc0000004L, /* move memory 4, scratchb, dmaaddr */
+/* 00ac */ 0x0000005cL,
+/* 00b0 */ 0x0000067cL,
+/* 00b4 */ 0x818b0000L, /* jump data_in_block_loop, when data_in */
+/* 00b8 */ 0x00ffffb4L,
+/* 00bc */ 0xc0000004L, /* move memory 4, scratcha, state */
+/* 00c0 */ 0x00000034L,
+/* 00c4 */ 0x00000678L,
+/* 00c8 */ 0x88880000L, /* call save_state */
+/* 00cc */ 0x000005e0L,
+/* 00d0 */ 0x80880000L, /* jump to_decisions */
+/* 00d4 */ 0x00000558L,
+/* 00d8 */ 0xc0000004L, /* move memory 4, scratchb, dmaaddr */
+/* 00dc */ 0x0000005cL,
+/* 00e0 */ 0x0000067cL,
+/* 00e4 */ 0xc0000004L, /* move memory 4, scratcha, state */
+/* 00e8 */ 0x00000034L,
+/* 00ec */ 0x00000678L,
+/* 00f0 */ 0x80880000L, /* jump to_decisions */
+/* 00f4 */ 0x00000538L,
+/* 00f8 */ 0x72370000L, /* move scratcha3 to sfbr */
+/* 00fc */ 0x00000000L,
+/* 0100 */ 0x98040000L, /* int error_too_much_data, if not 0 */
+/* 0104 */ 0x00000008L,
+/* 0108 */ 0x19000000L, /* move from data_buf, when data_in */
+/* 010c */ 0x00000003L,
+/* 0110 */ 0x78370100L, /* move 1 to scratcha3 */
+/* 0114 */ 0x00000000L,
+/* 0118 */ 0xc0000004L, /* move memory 4, scratcha, state */
+/* 011c */ 0x00000034L,
+/* 0120 */ 0x00000678L,
+/* 0124 */ 0x88880000L, /* call save_state */
+/* 0128 */ 0x00000584L,
+/* 012c */ 0x80880000L, /* jump post_data_to_decisions */
+/* 0130 */ 0x0000052cL,
+/* 0134 */ 0xc0000004L, /* move memory 4, state, scratcha */
+/* 0138 */ 0x00000678L,
+/* 013c */ 0x00000034L,
+/* 0140 */ 0xc0000004L, /* move memory 4, dmaaddr, scratchb */
+/* 0144 */ 0x0000067cL,
+/* 0148 */ 0x0000005cL,
+/* 014c */ 0x72360000L, /* move scratcha2 to sfbr */
+/* 0150 */ 0x00000000L,
+/* 0154 */ 0x808c0000L, /* jump data_out_normal, if 0 */
+/* 0158 */ 0x0000005cL,
+/* 015c */ 0xc0000004L, /* move memory 4, dmaaddr, scratchb */
+/* 0160 */ 0x0000067cL,
+/* 0164 */ 0x0000005cL,
+/* 0168 */ 0x28000200L, /* move BSIZE, ptr dmaaddr, when data_out */
+/* 016c */ 0x0000067cL,
+/* 0170 */ 0x7e5d0200L, /* move scratchb1 + BSIZE / 256 to scratchb1 */
+/* 0174 */ 0x00000000L,
+/* 0178 */ 0x7f5e0000L, /* move scratchb2 + 0 to scratchb2 with carry */
+/* 017c */ 0x00000000L,
+/* 0180 */ 0x7f5f0000L, /* move scratchb3 + 0 to scratchb3 with carry */
+/* 0184 */ 0x00000000L,
+/* 0188 */ 0x7e36ff00L, /* move scratcha2 + 255 to scratcha2 */
+/* 018c */ 0x00000000L,
+/* 0190 */ 0xc0000004L, /* move memory 4, scratchb, dmaaddr */
+/* 0194 */ 0x0000005cL,
+/* 0198 */ 0x0000067cL,
+/* 019c */ 0x808b0000L, /* jump data_out_block_loop, when data_out */
+/* 01a0 */ 0x00ffffa8L,
+/* 01a4 */ 0xc0000004L, /* move memory 4, scratcha, state */
+/* 01a8 */ 0x00000034L,
+/* 01ac */ 0x00000678L,
+/* 01b0 */ 0x80880000L, /* jump to_decisions */
+/* 01b4 */ 0x00000478L,
+/* 01b8 */ 0x72370000L, /* move scratcha3 to sfbr */
+/* 01bc */ 0x00000000L,
+/* 01c0 */ 0x98040000L, /* int error_too_little_data, if not 0 */
+/* 01c4 */ 0x00000009L,
+/* 01c8 */ 0x18000000L, /* move from data_buf, when data_out */
+/* 01cc */ 0x00000003L,
+/* 01d0 */ 0x78370100L, /* move 1 to scratcha3 */
+/* 01d4 */ 0x00000000L,
+/* 01d8 */ 0xc0000004L, /* move memory 4, scratcha, state */
+/* 01dc */ 0x00000034L,
+/* 01e0 */ 0x00000678L,
+/* 01e4 */ 0x88880000L, /* call save_state */
+/* 01e8 */ 0x000004c4L,
+/* 01ec */ 0x80880000L, /* jump post_data_to_decisions */
+/* 01f0 */ 0x0000046cL,
+/* 01f4 */ 0x1b000000L, /* move from status_buf, when status */
+/* 01f8 */ 0x00000004L,
+/* 01fc */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */
+/* 0200 */ 0x00000004L,
+/* 0204 */ 0x0f000001L, /* move 1, scratcha, when msg_in */
+/* 0208 */ 0x00000034L,
+/* 020c */ 0x808c0007L, /* jump rejected, if MSG_REJECT */
+/* 0210 */ 0x00000088L,
+/* 0214 */ 0x808c0004L, /* jump disconnected, if MSG_DISCONNECT */
+/* 0218 */ 0x00000298L,
+/* 021c */ 0x808c0002L, /* jump msg_in_skip, if MSG_SAVE_DATA_POINTER */
+/* 0220 */ 0x00000090L,
+/* 0224 */ 0x808c0003L, /* jump msg_in_skip, if MSG_RESTORE_POINTERS */
+/* 0228 */ 0x00000088L,
+/* 022c */ 0x808c0023L, /* jump ignore_wide, if MSG_IGNORE_WIDE_RESIDUE */
+/* 0230 */ 0x000001f0L,
+/* 0234 */ 0x808c0001L, /* jump extended, if X_MSG */
+/* 0238 */ 0x00000088L,
+/* 023c */ 0x98040000L, /* int error_not_cmd_complete, if not 0 */
+/* 0240 */ 0x00000001L,
+/* 0244 */ 0x7c027e00L, /* move scntl2&0x7e to scntl2 */
+/* 0248 */ 0x00000000L,
+/* 024c */ 0x60000040L, /* clear ack */
+/* 0250 */ 0x00000000L,
+/* 0254 */ 0x48000000L, /* wait disconnect */
+/* 0258 */ 0x00000000L,
+/* 025c */ 0xc0000004L, /* move memory 4, state, scratcha */
+/* 0260 */ 0x00000678L,
+/* 0264 */ 0x00000034L,
+/* 0268 */ 0x78340400L, /* move STATE_DONE to scratcha0 */
+/* 026c */ 0x00000000L,
+/* 0270 */ 0x78350000L, /* move RESULT_OK to scratcha1 */
+/* 0274 */ 0x00000000L,
+/* 0278 */ 0xc0000004L, /* move memory 4, scratcha, state */
+/* 027c */ 0x00000034L,
+/* 0280 */ 0x00000678L,
+/* 0284 */ 0x88880000L, /* call save_state */
+/* 0288 */ 0x00000424L,
+/* 028c */ 0x98180000L, /* intfly 0 */
+/* 0290 */ 0x00000000L,
+/* 0294 */ 0x80880000L, /* jump issue_check */
+/* 0298 */ 0x0000043cL,
+/* 029c */ 0x98080000L, /* int SIR_MSG_REJECT */
+/* 02a0 */ 0x0000000aL,
+/* 02a4 */ 0x60000040L, /* clear ack */
+/* 02a8 */ 0x00000000L,
+/* 02ac */ 0x80880000L, /* jump to_decisions */
+/* 02b0 */ 0x0000037cL,
+/* 02b4 */ 0x60000040L, /* clear ack */
+/* 02b8 */ 0x00000000L,
+/* 02bc */ 0x80880000L, /* jump to_decisions */
+/* 02c0 */ 0x0000036cL,
+/* 02c4 */ 0x60000040L, /* clear ack */
+/* 02c8 */ 0x00000000L,
+/* 02cc */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */
+/* 02d0 */ 0x00000004L,
+/* 02d4 */ 0x0f000001L, /* move 1, scratcha1, when msg_in */
+/* 02d8 */ 0x00000035L,
+/* 02dc */ 0x808c0003L, /* jump ext_3, if 3 */
+/* 02e0 */ 0x00000030L,
+/* 02e4 */ 0x808c0002L, /* jump ext_2, if 2 */
+/* 02e8 */ 0x00000098L,
+/* 02ec */ 0x98040001L, /* int error_weird_message, if not 1 */
+/* 02f0 */ 0x00000005L,
+/* 02f4 */ 0x60000040L, /* clear ack */
+/* 02f8 */ 0x00000000L,
+/* 02fc */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */
+/* 0300 */ 0x00000004L,
+/* 0304 */ 0x0f000001L, /* move 1, scratcha1, when msg_in */
+/* 0308 */ 0x00000035L,
+/* 030c */ 0x80880000L, /* jump ext_done */
+/* 0310 */ 0x000000c8L,
+/* 0314 */ 0x60000040L, /* ext_3: clear ack */
+/* 0318 */ 0x00000000L,
+/* 031c */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */
+/* 0320 */ 0x00000004L,
+/* 0324 */ 0x0f000001L, /* move 1, scratcha1, when msg_in */
+/* 0328 */ 0x00000035L,
+/* 032c */ 0x60000040L, /* clear ack */
+/* 0330 */ 0x00000000L,
+/* 0334 */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */
+/* 0338 */ 0x00000004L,
+/* 033c */ 0x0f000001L, /* move 1, scratcha2, when msg_in */
+/* 0340 */ 0x00000036L,
+/* 0344 */ 0x60000040L, /* clear ack */
+/* 0348 */ 0x00000000L,
+/* 034c */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */
+/* 0350 */ 0x00000004L,
+/* 0354 */ 0x0f000001L, /* move 1, scratcha3, when msg_in */
+/* 0358 */ 0x00000037L,
+/* 035c */ 0x72350000L, /* move scratcha1 to sfbr */
+/* 0360 */ 0x00000000L,
+/* 0364 */ 0x80840001L, /* jump ext_done, if not X_MSG_SDTR */
+/* 0368 */ 0x00000070L,
+/* 036c */ 0x98080000L, /* sdtr: int SIR_MSG_SDTR */
+/* 0370 */ 0x0000000bL,
+/* 0374 */ 0x60000040L, /* clear ack */
+/* 0378 */ 0x00000000L,
+/* 037c */ 0x80880000L, /* jump to_decisions */
+/* 0380 */ 0x000002acL,
+/* 0384 */ 0x60000040L, /* ext_2: clear ack */
+/* 0388 */ 0x00000000L,
+/* 038c */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */
+/* 0390 */ 0x00000004L,
+/* 0394 */ 0x0f000001L, /* move 1, scratcha1, when msg_in */
+/* 0398 */ 0x00000035L,
+/* 039c */ 0x60000040L, /* clear ack */
+/* 03a0 */ 0x00000000L,
+/* 03a4 */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */
+/* 03a8 */ 0x00000004L,
+/* 03ac */ 0x0f000001L, /* move 1, scratcha2, when msg_in */
+/* 03b0 */ 0x00000036L,
+/* 03b4 */ 0x72350000L, /* move scratcha1 to sfbr */
+/* 03b8 */ 0x00000000L,
+/* 03bc */ 0x80840003L, /* jump ext_done, if not X_MSG_WDTR */
+/* 03c0 */ 0x00000018L,
+/* 03c4 */ 0x98080000L, /* wdtr: int SIR_MSG_WDTR */
+/* 03c8 */ 0x0000000fL,
+/* 03cc */ 0x60000040L, /* clear ack */
+/* 03d0 */ 0x00000000L,
+/* 03d4 */ 0x80880000L, /* jump to_decisions */
+/* 03d8 */ 0x00000254L,
+/* 03dc */ 0x58000008L, /* set atn */
+/* 03e0 */ 0x00000000L,
+/* 03e4 */ 0x60000040L, /* clear ack */
+/* 03e8 */ 0x00000000L,
+/* 03ec */ 0x78340700L, /* move MSG_REJECT to scratcha */
+/* 03f0 */ 0x00000000L,
+/* 03f4 */ 0x9e030000L, /* int error_unexpected_phase, when not msg_out */
+/* 03f8 */ 0x00000004L,
+/* 03fc */ 0x60000008L, /* clear atn */
+/* 0400 */ 0x00000000L,
+/* 0404 */ 0x0e000001L, /* move 1, scratcha, when msg_out */
+/* 0408 */ 0x00000034L,
+/* 040c */ 0x60000040L, /* clear ack */
+/* 0410 */ 0x00000000L,
+/* 0414 */ 0x868b0000L, /* jump reject, when msg_out */
+/* 0418 */ 0x00ffffc0L,
+/* 041c */ 0x80880000L, /* jump to_decisions */
+/* 0420 */ 0x0000020cL,
+/* 0424 */ 0x60000040L, /* clear ack */
+/* 0428 */ 0x00000000L,
+/* 042c */ 0x9f030000L, /* int error_unexpected_phase, when not msg_in */
+/* 0430 */ 0x00000004L,
+/* 0434 */ 0x0f000001L, /* move 1, scratcha1, when msg_in */
+/* 0438 */ 0x00000035L,
+/* 043c */ 0x98080000L, /* int SIR_MSG_IGNORE_WIDE_RESIDUE */
+/* 0440 */ 0x00000010L,
+/* 0444 */ 0x60000040L, /* clear ack */
+/* 0448 */ 0x00000000L,
+/* 044c */ 0x80880000L, /* jump to_decisions */
+/* 0450 */ 0x000001dcL,
+/* 0454 */ 0x58000008L, /* set atn */
+/* 0458 */ 0x00000000L,
+/* 045c */ 0x60000040L, /* clear ack */
+/* 0460 */ 0x00000000L,
+/* 0464 */ 0x9e030000L, /* int error_unexpected_phase, when not msg_out */
+/* 0468 */ 0x00000004L,
+/* 046c */ 0x1e000000L, /* move from msg_out_buf, when msg_out */
+/* 0470 */ 0x00000001L,
+/* 0474 */ 0x868b0000L, /* jump response_repeat, when msg_out */
+/* 0478 */ 0x00fffff0L,
+/* 047c */ 0x878b0000L, /* jump response_msg_in, when msg_in */
+/* 0480 */ 0x00000010L,
+/* 0484 */ 0x98080000L, /* int SIR_EV_RESPONSE_OK */
+/* 0488 */ 0x0000000cL,
+/* 048c */ 0x80880000L, /* jump to_decisions */
+/* 0490 */ 0x0000019cL,
+/* 0494 */ 0x0f000001L, /* move 1, scratcha, when msg_in */
+/* 0498 */ 0x00000034L,
+/* 049c */ 0x808c0007L, /* jump rejected, if MSG_REJECT */
+/* 04a0 */ 0x00fffdf8L,
+/* 04a4 */ 0x98080000L, /* int SIR_EV_RESPONSE_OK */
+/* 04a8 */ 0x0000000cL,
+/* 04ac */ 0x80880000L, /* jump msg_in_not_reject */
+/* 04b0 */ 0x00fffd60L,
+/* 04b4 */ 0x7c027e00L, /* move scntl2&0x7e to scntl2 */
+/* 04b8 */ 0x00000000L,
+/* 04bc */ 0x60000040L, /* clear ack */
+/* 04c0 */ 0x00000000L,
+/* 04c4 */ 0x48000000L, /* wait disconnect */
+/* 04c8 */ 0x00000000L,
+/* 04cc */ 0xc0000004L, /* move memory 4, state, scratcha */
+/* 04d0 */ 0x00000678L,
+/* 04d4 */ 0x00000034L,
+/* 04d8 */ 0x78340300L, /* move STATE_DISCONNECTED to scratcha0 */
+/* 04dc */ 0x00000000L,
+/* 04e0 */ 0xc0000004L, /* move memory 4, scratcha, state */
+/* 04e4 */ 0x00000034L,
+/* 04e8 */ 0x00000678L,
+/* 04ec */ 0x88880000L, /* call save_state */
+/* 04f0 */ 0x000001bcL,
+/* 04f4 */ 0x74020100L, /* move scntl2&0x01 to sfbr */
+/* 04f8 */ 0x00000000L,
+/* 04fc */ 0x98040000L, /* int SIR_NOTIFY_WSR, if not 0 */
+/* 0500 */ 0x00000073L,
+/* 0504 */ 0x80880000L, /* jump issue_check */
+/* 0508 */ 0x000001ccL,
+/* 050c */ 0x98080000L, /* int SIR_NOTIFY_RESELECTED_ON_SELECT */
+/* 0510 */ 0x00000075L,
+/* 0514 */ 0x80880000L, /* jump reselected */
+/* 0518 */ 0x00000008L,
+/* 051c */ 0x54000000L, /* wait reselect sigp_set */
+/* 0520 */ 0x000001acL,
+/* 0524 */ 0x60000200L, /* clear target */
+/* 0528 */ 0x00000000L,
+/* 052c */ 0x9f030000L, /* int SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT, when not msg_in */
+/* 0530 */ 0x00000006L,
+/* 0534 */ 0x0f000001L, /* move 1, scratchb, when msg_in */
+/* 0538 */ 0x0000005cL,
+/* 053c */ 0x98041f80L, /* int error_not_identify_after_reselect, if not MSG_IDENTIFY and mask 0x1f */
+/* 0540 */ 0x00000007L,
+/* 0544 */ 0xc0000004L, /* move memory 4, dsa_head, dsa */
+/* 0548 */ 0x00000008L,
+/* 054c */ 0x00000010L,
+/* 0550 */ 0x72100000L, /* move dsa0 to sfbr */
+/* 0554 */ 0x00000000L,
+/* 0558 */ 0x80840000L, /* jump find_dsa_1, if not 0 */
+/* 055c */ 0x00000030L,
+/* 0560 */ 0x72110000L, /* move dsa1 to sfbr */
+/* 0564 */ 0x00000000L,
+/* 0568 */ 0x80840000L, /* jump find_dsa_1, if not 0 */
+/* 056c */ 0x00000020L,
+/* 0570 */ 0x72120000L, /* move dsa2 to sfbr */
+/* 0574 */ 0x00000000L,
+/* 0578 */ 0x80840000L, /* jump find_dsa_1, if not 0 */
+/* 057c */ 0x00000010L,
+/* 0580 */ 0x72130000L, /* move dsa3 to sfbr */
+/* 0584 */ 0x00000000L,
+/* 0588 */ 0x980c0000L, /* int error_reselected, if 0 */
+/* 058c */ 0x00000003L,
+/* 0590 */ 0x88880000L, /* call load_state */
+/* 0594 */ 0x000000f8L,
+/* 0598 */ 0xc0000004L, /* move memory 4, state, scratcha */
+/* 059c */ 0x00000678L,
+/* 05a0 */ 0x00000034L,
+/* 05a4 */ 0x72340000L, /* move scratcha0 to sfbr */
+/* 05a8 */ 0x00000000L,
+/* 05ac */ 0x80840003L, /* jump find_dsa_next, if not STATE_DISCONNECTED */
+/* 05b0 */ 0x00000038L,
+/* 05b4 */ 0x740a0f00L, /* move ssid & 15 to sfbr */
+/* 05b8 */ 0x00000000L,
+/* 05bc */ 0xc0000001L, /* move memory 1, targ, find_dsa_smc1 */
+/* 05c0 */ 0x00000680L,
+/* 05c4 */ 0x000005c8L,
+/* 05c8 */ 0x808400ffL, /* jump find_dsa_next, if not 255 */
+/* 05cc */ 0x0000001cL,
+/* 05d0 */ 0xc0000001L, /* move memory 1, lun, find_dsa_smc2 */
+/* 05d4 */ 0x00000684L,
+/* 05d8 */ 0x000005e4L,
+/* 05dc */ 0x725c0000L, /* move scratchb0 to sfbr */
+/* 05e0 */ 0x00000000L,
+/* 05e4 */ 0x808cf8ffL, /* jump reload_sync, if 255 and mask ~7 */
+/* 05e8 */ 0x00000034L,
+/* 05ec */ 0xc0000004L, /* move memory 4, next, dsa */
+/* 05f0 */ 0x0000068cL,
+/* 05f4 */ 0x00000010L,
+/* 05f8 */ 0x80880000L, /* jump find_dsa_loop */
+/* 05fc */ 0x00ffff50L,
+/* 0600 */ 0x60000008L, /* clear atn */
+/* 0604 */ 0x00000000L,
+/* 0608 */ 0x878b0000L, /* jump msg_in_phase, when msg_in */
+/* 060c */ 0x00fffbf4L,
+/* 0610 */ 0x98080000L, /* int SIR_MSG_REJECT */
+/* 0614 */ 0x0000000aL,
+/* 0618 */ 0x80880000L, /* jump to_decisions */
+/* 061c */ 0x00000010L,
+/* 0620 */ 0x88880000L, /* call load_sync */
+/* 0624 */ 0x00000134L,
+/* 0628 */ 0x60000040L, /* clear ack */
+/* 062c */ 0x00000000L,
+/* 0630 */ 0x818b0000L, /* jump data_in_phase, when data_in */
+/* 0634 */ 0x00fffa20L,
+/* 0638 */ 0x828a0000L, /* jump cmd_phase, if cmd */
+/* 063c */ 0x00fffa00L,
+/* 0640 */ 0x808a0000L, /* jump data_out_phase, if data_out */
+/* 0644 */ 0x00fffaecL,
+/* 0648 */ 0x838a0000L, /* jump status_phase, if status */
+/* 064c */ 0x00fffba4L,
+/* 0650 */ 0x878a0000L, /* jump msg_in_phase, if msg_in */
+/* 0654 */ 0x00fffbacL,
+/* 0658 */ 0x98080000L, /* int error_unexpected_phase */
+/* 065c */ 0x00000004L,
+/* 0660 */ 0x838b0000L, /* jump status_phase, when status */
+/* 0664 */ 0x00fffb8cL,
+/* 0668 */ 0x878a0000L, /* jump msg_in_phase, if msg_in */
+/* 066c */ 0x00fffb94L,
+/* 0670 */ 0x98080000L, /* int error_unexpected_phase */
+/* 0674 */ 0x00000004L,
+/* 0678 */ 0x00000000L, /* state: defw 0 */
+/* 067c */ 0x00000000L, /* dmaaddr: defw 0 */
+/* 0680 */ 0x00000000L, /* targ: defw 0 */
+/* 0684 */ 0x00000000L, /* lun: defw 0 */
+/* 0688 */ 0x00000000L, /* sync: defw 0 */
+/* 068c */ 0x00000000L, /* next: defw 0 */
+ /* dsa_load_len = dsa_load_end - dsa_copy */
+ /* dsa_save_len = dsa_save_end - dsa_copy */
+/* 0690 */ 0xc0000004L, /* move memory 4, dsa, load_state_smc0 + 4 */
+/* 0694 */ 0x00000010L,
+/* 0698 */ 0x000006a0L,
+/* 069c */ 0xc0000018L, /* move memory dsa_load_len, 0, dsa_copy */
+/* 06a0 */ 0x00000000L,
+/* 06a4 */ 0x00000678L,
+/* 06a8 */ 0x90080000L, /* return */
+/* 06ac */ 0x00000000L,
+/* 06b0 */ 0xc0000004L, /* move memory 4, dsa, save_state_smc0 + 8 */
+/* 06b4 */ 0x00000010L,
+/* 06b8 */ 0x000006c4L,
+/* 06bc */ 0xc0000008L, /* move memory dsa_save_len, dsa_copy, 0 */
+/* 06c0 */ 0x00000678L,
+/* 06c4 */ 0x00000000L,
+/* 06c8 */ 0x90080000L, /* return */
+/* 06cc */ 0x00000000L,
+/* 06d0 */ 0x721a0000L, /* move ctest2 to sfbr */
+/* 06d4 */ 0x00000000L,
+/* 06d8 */ 0xc0000004L, /* move memory 4, dsa_head, dsa */
+/* 06dc */ 0x00000008L,
+/* 06e0 */ 0x00000010L,
+/* 06e4 */ 0x72100000L, /* move dsa0 to sfbr */
+/* 06e8 */ 0x00000000L,
+/* 06ec */ 0x80840000L, /* jump issue_check_1, if not 0 */
+/* 06f0 */ 0x00000030L,
+/* 06f4 */ 0x72110000L, /* move dsa1 to sfbr */
+/* 06f8 */ 0x00000000L,
+/* 06fc */ 0x80840000L, /* jump issue_check_1, if not 0 */
+/* 0700 */ 0x00000020L,
+/* 0704 */ 0x72120000L, /* move dsa2 to sfbr */
+/* 0708 */ 0x00000000L,
+/* 070c */ 0x80840000L, /* jump issue_check_1, if not 0 */
+/* 0710 */ 0x00000010L,
+/* 0714 */ 0x72130000L, /* move dsa3 to sfbr */
+/* 0718 */ 0x00000000L,
+/* 071c */ 0x808c0000L, /* jump wait_for_reselection, if 0 */
+/* 0720 */ 0x00fffdf8L,
+/* 0724 */ 0x88880000L, /* call load_state */
+/* 0728 */ 0x00ffff64L,
+/* 072c */ 0xc0000004L, /* move memory 4, state, scratcha */
+/* 0730 */ 0x00000678L,
+/* 0734 */ 0x00000034L,
+/* 0738 */ 0x72340000L, /* move scratcha0 to sfbr */
+/* 073c */ 0x00000000L,
+/* 0740 */ 0x808c0002L, /* jump start, if STATE_ISSUE */
+/* 0744 */ 0x00fff8c0L,
+/* 0748 */ 0xc0000004L, /* move memory 4, next, dsa */
+/* 074c */ 0x0000068cL,
+/* 0750 */ 0x00000010L,
+/* 0754 */ 0x80880000L, /* jump issue_check_loop */
+/* 0758 */ 0x00ffff88L,
+/* 075c */ 0xc0000004L, /* move memory 4, sync, scratcha */
+/* 0760 */ 0x00000688L,
+/* 0764 */ 0x00000034L,
+/* 0768 */ 0x72340000L, /* move scratcha0 to sfbr */
+/* 076c */ 0x00000000L,
+/* 0770 */ 0x6a030000L, /* move sfbr to scntl3 */
+/* 0774 */ 0x00000000L,
+/* 0778 */ 0x72350000L, /* move scratcha1 to sfbr */
+/* 077c */ 0x00000000L,
+/* 0780 */ 0x6a050000L, /* move sfbr to sxfer */
+/* 0784 */ 0x00000000L,
+/* 0788 */ 0x90080000L, /* return */
+/* 078c */ 0x00000000L,
+};
+
+#define NA_SCRIPT_SIZE 484
+
+struct na_patch na_patches[] = {
+ { 0x0006, 5 }, /* 00000018 */
+ { 0x000b, 4 }, /* 0000002c */
+ { 0x0013, 4 }, /* 0000004c */
+ { 0x0017, 1 }, /* 0000005c */
+ { 0x0018, 2 }, /* 00000060 */
+ { 0x001a, 1 }, /* 00000068 */
+ { 0x001b, 2 }, /* 0000006c */
+ { 0x0021, 1 }, /* 00000084 */
+ { 0x002b, 2 }, /* 000000ac */
+ { 0x002c, 1 }, /* 000000b0 */
+ { 0x0030, 2 }, /* 000000c0 */
+ { 0x0031, 1 }, /* 000000c4 */
+ { 0x0037, 2 }, /* 000000dc */
+ { 0x0038, 1 }, /* 000000e0 */
+ { 0x003a, 2 }, /* 000000e8 */
+ { 0x003b, 1 }, /* 000000ec */
+ { 0x0043, 4 }, /* 0000010c */
+ { 0x0047, 2 }, /* 0000011c */
+ { 0x0048, 1 }, /* 00000120 */
+ { 0x004e, 1 }, /* 00000138 */
+ { 0x004f, 2 }, /* 0000013c */
+ { 0x0051, 1 }, /* 00000144 */
+ { 0x0052, 2 }, /* 00000148 */
+ { 0x0058, 1 }, /* 00000160 */
+ { 0x0059, 2 }, /* 00000164 */
+ { 0x005b, 1 }, /* 0000016c */
+ { 0x0065, 2 }, /* 00000194 */
+ { 0x0066, 1 }, /* 00000198 */
+ { 0x006a, 2 }, /* 000001a8 */
+ { 0x006b, 1 }, /* 000001ac */
+ { 0x0073, 4 }, /* 000001cc */
+ { 0x0077, 2 }, /* 000001dc */
+ { 0x0078, 1 }, /* 000001e0 */
+ { 0x007e, 4 }, /* 000001f8 */
+ { 0x0082, 2 }, /* 00000208 */
+ { 0x0098, 1 }, /* 00000260 */
+ { 0x0099, 2 }, /* 00000264 */
+ { 0x009f, 2 }, /* 0000027c */
+ { 0x00a0, 1 }, /* 00000280 */
+ { 0x00b6, 2 }, /* 000002d8 */
+ { 0x00c2, 2 }, /* 00000308 */
+ { 0x00ca, 2 }, /* 00000328 */
+ { 0x00d0, 2 }, /* 00000340 */
+ { 0x00d6, 2 }, /* 00000358 */
+ { 0x00e6, 2 }, /* 00000398 */
+ { 0x00ec, 2 }, /* 000003b0 */
+ { 0x0102, 2 }, /* 00000408 */
+ { 0x010e, 2 }, /* 00000438 */
+ { 0x011c, 4 }, /* 00000470 */
+ { 0x0126, 2 }, /* 00000498 */
+ { 0x0134, 1 }, /* 000004d0 */
+ { 0x0135, 2 }, /* 000004d4 */
+ { 0x0139, 2 }, /* 000004e4 */
+ { 0x013a, 1 }, /* 000004e8 */
+ { 0x014e, 2 }, /* 00000538 */
+ { 0x0152, 4 }, /* 00000548 */
+ { 0x0153, 2 }, /* 0000054c */
+ { 0x0167, 1 }, /* 0000059c */
+ { 0x0168, 2 }, /* 000005a0 */
+ { 0x0170, 1 }, /* 000005c0 */
+ { 0x0171, 1 }, /* 000005c4 */
+ { 0x0175, 1 }, /* 000005d4 */
+ { 0x0176, 1 }, /* 000005d8 */
+ { 0x017c, 1 }, /* 000005f0 */
+ { 0x017d, 2 }, /* 000005f4 */
+ { 0x01a5, 2 }, /* 00000694 */
+ { 0x01a6, 1 }, /* 00000698 */
+ { 0x01a9, 1 }, /* 000006a4 */
+ { 0x01ad, 2 }, /* 000006b4 */
+ { 0x01ae, 1 }, /* 000006b8 */
+ { 0x01b0, 1 }, /* 000006c0 */
+ { 0x01b7, 4 }, /* 000006dc */
+ { 0x01b8, 2 }, /* 000006e0 */
+ { 0x01cc, 1 }, /* 00000730 */
+ { 0x01cd, 2 }, /* 00000734 */
+ { 0x01d3, 1 }, /* 0000074c */
+ { 0x01d4, 2 }, /* 00000750 */
+ { 0x01d8, 1 }, /* 00000760 */
+ { 0x01d9, 2 }, /* 00000764 */
+};
+#define NA_PATCHES 79
+
+enum na_external {
+ X_scsi_id_buf,
+ X_msg_out_buf,
+ X_cmd_buf,
+ X_data_buf,
+ X_status_buf,
+ X_msgin_buf,
+ X_dsa_0,
+ X_dsa_1,
+ X_dsa_head,
+};
+
+enum {
+ E_issue_check_next = 1864,
+ E_issue_check_1 = 1828,
+ E_issue_check_loop = 1764,
+ E_save_state_smc0 = 1724,
+ E_load_state_smc0 = 1692,
+ E_dsa_load_end = 1680,
+ E_sync = 1672,
+ E_dsa_save_end = 1664,
+ E_dsa_copy = 1656,
+ E_id_out_mismatch_recover = 1536,
+ E_next = 1676,
+ E_reload_sync = 1568,
+ E_find_dsa_smc2 = 1508,
+ E_lun = 1668,
+ E_find_dsa_smc1 = 1480,
+ E_targ = 1664,
+ E_find_dsa_next = 1516,
+ E_load_state = 1680,
+ E_find_dsa_1 = 1424,
+ E_find_dsa_loop = 1360,
+ E_find_dsa = 1348,
+ E_sigp_set = 1744,
+ E_reselected = 1316,
+ E_wsr_check = 1268,
+ E_response_msg_in = 1172,
+ E_response_repeat = 1132,
+ E_response = 1108,
+ E_reject = 988,
+ E_wdtr = 964,
+ E_sdtr = 876,
+ E_ext_done = 988,
+ E_ext_1 = 756,
+ E_ext_2 = 900,
+ E_ext_3 = 788,
+ E_issue_check = 1752,
+ E_extended = 708,
+ E_ignore_wide = 1060,
+ E_msg_in_skip = 692,
+ E_disconnected = 1204,
+ E_msg_in_not_reject = 532,
+ E_rejected = 668,
+ E_msg_in_phase = 516,
+ E_status_phase = 500,
+ E_data_out_mismatch = 464,
+ E_data_out_block_mismatch = 368,
+ E_data_out_normal = 440,
+ E_data_out_block_loop = 332,
+ E_data_out_phase = 308,
+ E_post_data_to_decisions = 1632,
+ E_data_in_mismatch = 272,
+ E_data_block_mismatch_recover = 216,
+ E_save_state = 1712,
+ E_data_in_block_mismatch = 136,
+ E_data_in_normal = 248,
+ E_data_in_block_loop = 112,
+ E_dmaaddr = 1660,
+ E_state = 1656,
+ E_data_in_phase = 88,
+ E_cmd_out_mismatch = 80,
+ E_cmd_phase = 64,
+ E_to_decisions = 1584,
+ E_id_out_mismatch = 48,
+ E_start1 = 40,
+ E_reselected_on_select = 1292,
+ E_load_sync = 1884,
+ E_start = 8,
+ E_wait_for_reselection = 1308,
+ E_idle = 0,
+};
+#define A_dsa_save_len 8
+#define A_dsa_load_len 24
+#define A_BSIZE 512
+#define A_MSG_REJECT 7
+#define A_X_MSG_WDTR 3
+#define A_X_MSG_SDTR 1
+#define A_X_MSG 1
+#define A_MSG_IGNORE_WIDE_RESIDUE 35
+#define A_MSG_RESTORE_POINTERS 3
+#define A_MSG_SAVE_DATA_POINTER 2
+#define A_MSG_DISCONNECT 4
+#define A_MSG_IDENTIFY 128
+#define A_RESULT_OK 0
+#define A_STATE_DONE 4
+#define A_STATE_DISCONNECTED 3
+#define A_STATE_ISSUE 2
+#define A_STATE_ALLOCATED 1
+#define A_STATE_FREE 0
+#define A_SIR_NOTIFY_RESELECTED_ON_SELECT 117
+#define A_SIR_NOTIFY_LOAD_SYNC 116
+#define A_SIR_NOTIFY_WSR 115
+#define A_SIR_NOTIFY_BLOCK_DATA_IN 114
+#define A_SIR_NOTIFY_DATA_OUT 113
+#define A_SIR_NOTIFY_DATA_IN 112
+#define A_SIR_NOTIFY_COMMAND 111
+#define A_SIR_NOTIFY_DUMP_NEXT_CODE 110
+#define A_SIR_NOTIFY_ISSUE_CHECK 109
+#define A_SIR_NOTIFY_WAIT_RESELECT 108
+#define A_SIR_NOTIFY_ISSUE 107
+#define A_SIR_NOTIFY_SIGP 106
+#define A_SIR_NOTIFY_DUMP2 105
+#define A_SIR_NOTIFY_DUMP 104
+#define A_SIR_NOTIFY_STATUS 103
+#define A_SIR_NOTIFY_MSG_IN 102
+#define A_SIR_NOTIFY_RESELECT 101
+#define A_SIR_NOTIFY_DISC 100
+#define A_SIR_MSG_IGNORE_WIDE_RESIDUE 16
+#define A_SIR_MSG_WDTR 15
+#define A_SIR_EV_PHASE_SWITCH_AFTER_ID 14
+#define A_error_sigp_set 13
+#define A_SIR_EV_RESPONSE_OK 12
+#define A_SIR_MSG_SDTR 11
+#define A_SIR_MSG_REJECT 10
+#define A_error_too_little_data 9
+#define A_error_too_much_data 8
+#define A_error_not_identify_after_reselect 7
+#define A_SIR_ERROR_NOT_MSG_IN_AFTER_RESELECT 6
+#define A_error_weird_message 5
+#define A_error_unexpected_phase 4
+#define A_error_reselected 3
+#define A_error_disconnected 2
+#define A_error_not_cmd_complete 1
+#define A_SIR_MSG_IO_COMPLETE 0
diff --git a/os/boot/pc/sdata.c b/os/boot/pc/sdata.c
new file mode 100644
index 00000000..d035dc9c
--- /dev/null
+++ b/os/boot/pc/sdata.c
@@ -0,0 +1,1639 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "error.h"
+
+#include "sd.h"
+
+extern SDifc sdataifc;
+
+enum {
+ DbgCONFIG = 0x0001, /* detected drive config info */
+ DbgIDENTIFY = 0x0002, /* detected drive identify info */
+ DbgSTATE = 0x0004, /* dump state on panic */
+ DbgPROBE = 0x0008, /* trace device probing */
+ DbgDEBUG = 0x0080, /* the current problem... */
+ DbgINL = 0x0100, /* That Inil20+ message we hate */
+ Dbg48BIT = 0x0200, /* 48-bit LBA */
+ DbgBsy = 0x0400, /* interrupt but Bsy (shared IRQ) */
+};
+#define DEBUG (DbgDEBUG|DbgCONFIG)
+
+enum { /* I/O ports */
+ Data = 0,
+ Error = 1, /* (read) */
+ Features = 1, /* (write) */
+ Count = 2, /* sector count<7-0>, sector count<15-8> */
+ Ir = 2, /* interrupt reason (PACKET) */
+ Sector = 3, /* sector number */
+ Lbalo = 3, /* LBA<7-0>, LBA<31-24> */
+ Cyllo = 4, /* cylinder low */
+ Bytelo = 4, /* byte count low (PACKET) */
+ Lbamid = 4, /* LBA<15-8>, LBA<39-32> */
+ Cylhi = 5, /* cylinder high */
+ Bytehi = 5, /* byte count hi (PACKET) */
+ Lbahi = 5, /* LBA<23-16>, LBA<47-40> */
+ Dh = 6, /* Device/Head, LBA<32-14> */
+ Status = 7, /* (read) */
+ Command = 7, /* (write) */
+
+ As = 2, /* Alternate Status (read) */
+ Dc = 2, /* Device Control (write) */
+};
+
+enum { /* Error */
+ Med = 0x01, /* Media error */
+ Ili = 0x01, /* command set specific (PACKET) */
+ Nm = 0x02, /* No Media */
+ Eom = 0x02, /* command set specific (PACKET) */
+ Abrt = 0x04, /* Aborted command */
+ Mcr = 0x08, /* Media Change Request */
+ Idnf = 0x10, /* no user-accessible address */
+ Mc = 0x20, /* Media Change */
+ Unc = 0x40, /* Uncorrectable data error */
+ Wp = 0x40, /* Write Protect */
+ Icrc = 0x80, /* Interface CRC error */
+};
+
+enum { /* Features */
+ Dma = 0x01, /* data transfer via DMA (PACKET) */
+ Ovl = 0x02, /* command overlapped (PACKET) */
+};
+
+enum { /* Interrupt Reason */
+ Cd = 0x01, /* Command/Data */
+ Io = 0x02, /* I/O direction */
+ Rel = 0x04, /* Bus Release */
+};
+
+enum { /* Device/Head */
+ Dev0 = 0xA0, /* Master */
+ Dev1 = 0xB0, /* Slave */
+ Lba = 0x40, /* LBA mode */
+};
+
+enum { /* Status, Alternate Status */
+ Err = 0x01, /* Error */
+ Chk = 0x01, /* Check error (PACKET) */
+ Drq = 0x08, /* Data Request */
+ Dsc = 0x10, /* Device Seek Complete */
+ Serv = 0x10, /* Service */
+ Df = 0x20, /* Device Fault */
+ Dmrd = 0x20, /* DMA ready (PACKET) */
+ Drdy = 0x40, /* Device Ready */
+ Bsy = 0x80, /* Busy */
+};
+
+enum { /* Command */
+ Cnop = 0x00, /* NOP */
+ Cdr = 0x08, /* Device Reset */
+ Crs = 0x20, /* Read Sectors */
+ Crs48 = 0x24, /* Read Sectors Ext */
+ Crd48 = 0x25, /* Read w/ DMA Ext */
+ Crdq48 = 0x26, /* Read w/ DMA Queued Ext */
+ Crsm48 = 0x29, /* Read Multiple Ext */
+ Cws = 0x30, /* Write Sectors */
+ Cws48 = 0x34, /* Write Sectors Ext */
+ Cwd48 = 0x35, /* Write w/ DMA Ext */
+ Cwdq48 = 0x36, /* Write w/ DMA Queued Ext */
+ Cwsm48 = 0x39, /* Write Multiple Ext */
+ Cedd = 0x90, /* Execute Device Diagnostics */
+ Cpkt = 0xA0, /* Packet */
+ Cidpkt = 0xA1, /* Identify Packet Device */
+ Crsm = 0xC4, /* Read Multiple */
+ Cwsm = 0xC5, /* Write Multiple */
+ Csm = 0xC6, /* Set Multiple */
+ Crdq = 0xC7, /* Read DMA queued */
+ Crd = 0xC8, /* Read DMA */
+ Cwd = 0xCA, /* Write DMA */
+ Cwdq = 0xCC, /* Write DMA queued */
+ Cstandby = 0xE2, /* Standby */
+ Cid = 0xEC, /* Identify Device */
+ Csf = 0xEF, /* Set Features */
+};
+
+enum { /* Device Control */
+ Nien = 0x02, /* (not) Interrupt Enable */
+ Srst = 0x04, /* Software Reset */
+};
+
+enum { /* PCI Configuration Registers */
+ Bmiba = 0x20, /* Bus Master Interface Base Address */
+ Idetim = 0x40, /* IE Timing */
+ Sidetim = 0x44, /* Slave IE Timing */
+ Udmactl = 0x48, /* Ultra DMA/33 Control */
+ Udmatim = 0x4A, /* Ultra DMA/33 Timing */
+};
+
+enum { /* Bus Master IDE I/O Ports */
+ Bmicx = 0, /* Command */
+ Bmisx = 2, /* Status */
+ Bmidtpx = 4, /* Descriptor Table Pointer */
+};
+
+enum { /* Bmicx */
+ Ssbm = 0x01, /* Start/Stop Bus Master */
+ Rwcon = 0x08, /* Read/Write Control */
+};
+
+enum { /* Bmisx */
+ Bmidea = 0x01, /* Bus Master IDE Active */
+ Idedmae = 0x02, /* IDE DMA Error (R/WC) */
+ Ideints = 0x04, /* IDE Interrupt Status (R/WC) */
+ Dma0cap = 0x20, /* Drive 0 DMA Capable */
+ Dma1cap = 0x40, /* Drive 0 DMA Capable */
+};
+enum { /* Physical Region Descriptor */
+ PrdEOT = 0x80000000, /* Bus Master IDE Active */
+};
+
+enum { /* offsets into the identify info. */
+ Iconfig = 0, /* general configuration */
+ Ilcyl = 1, /* logical cylinders */
+ Ilhead = 3, /* logical heads */
+ Ilsec = 6, /* logical sectors per logical track */
+ Iserial = 10, /* serial number */
+ Ifirmware = 23, /* firmware revision */
+ Imodel = 27, /* model number */
+ Imaxrwm = 47, /* max. read/write multiple sectors */
+ Icapabilities = 49, /* capabilities */
+ Istandby = 50, /* device specific standby timer */
+ Ipiomode = 51, /* PIO data transfer mode number */
+ Ivalid = 53,
+ Iccyl = 54, /* cylinders if (valid&0x01) */
+ Ichead = 55, /* heads if (valid&0x01) */
+ Icsec = 56, /* sectors if (valid&0x01) */
+ Iccap = 57, /* capacity if (valid&0x01) */
+ Irwm = 59, /* read/write multiple */
+ Ilba = 60, /* LBA size */
+ Imwdma = 63, /* multiword DMA mode */
+ Iapiomode = 64, /* advanced PIO modes supported */
+ Iminmwdma = 65, /* min. multiword DMA cycle time */
+ Irecmwdma = 66, /* rec. multiword DMA cycle time */
+ Iminpio = 67, /* min. PIO cycle w/o flow control */
+ Iminiordy = 68, /* min. PIO cycle with IORDY */
+ Ipcktbr = 71, /* time from PACKET to bus release */
+ Iserbsy = 72, /* time from SERVICE to !Bsy */
+ Iqdepth = 75, /* max. queue depth */
+ Imajor = 80, /* major version number */
+ Iminor = 81, /* minor version number */
+ Icsfs = 82, /* command set/feature supported */
+ Icsfe = 85, /* command set/feature enabled */
+ Iudma = 88, /* ultra DMA mode */
+ Ierase = 89, /* time for security erase */
+ Ieerase = 90, /* time for enhanced security erase */
+ Ipower = 91, /* current advanced power management */
+ Ilba48 = 100, /* 48-bit LBA size (64 bits in 100-103) */
+ Irmsn = 127, /* removable status notification */
+ Isecstat = 128, /* security status */
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Drive Drive;
+
+typedef struct Prd {
+ ulong pa; /* Physical Base Address */
+ int count;
+} Prd;
+
+enum {
+ Nprd = SDmaxio/(64*1024)+2,
+};
+
+typedef struct Ctlr {
+ int cmdport;
+ int ctlport;
+ int irq;
+ int tbdf;
+
+ Pcidev* pcidev;
+ void (*ienable)(Ctlr*);
+ SDev* sdev;
+
+ Drive* drive[2];
+
+ Prd* prdt; /* physical region descriptor table */
+
+// QLock; /* current command */
+ Drive* curdrive;
+ int command; /* last command issued (debugging) */
+// Rendez;
+ int done;
+
+ Lock; /* register access */
+} Ctlr;
+
+typedef struct Drive {
+ Ctlr* ctlr;
+
+ int dev;
+ ushort info[256];
+ int c; /* cylinder */
+ int h; /* head */
+ int s; /* sector */
+ vlong sectors; /* total */
+ int secsize; /* sector size */
+
+// int dma; /* DMA R/W possible */
+// int dmactl;
+// int rwm; /* read/write multiple possible */
+// int rwmctl;
+
+ int pkt; /* PACKET device, length of pktcmd */
+ uchar pktcmd[16];
+// int pktdma; /* this PACKET command using dma */
+
+ uchar sense[18];
+ uchar inquiry[48];
+
+// QLock; /* drive access */
+ int command; /* current command */
+ int write;
+ uchar* data;
+ int dlen;
+ uchar* limit;
+ int count; /* sectors */
+ int block; /* R/W bytes per block */
+ int status;
+ int error;
+ int flags; /* internal flags */
+} Drive;
+
+enum { /* internal flags */
+ Lba48 = 0x1, /* LBA48 mode */
+ Lba48always = 0x2, /* ... */
+};
+
+static void
+pc87415ienable(Ctlr* ctlr)
+{
+ Pcidev *p;
+ int x;
+
+ p = ctlr->pcidev;
+ if(p == nil)
+ return;
+
+ x = pcicfgr32(p, 0x40);
+ if(ctlr->cmdport == p->mem[0].bar)
+ x &= ~0x00000100;
+ else
+ x &= ~0x00000200;
+ pcicfgw32(p, 0x40, x);
+}
+
+static int
+atadebug(int cmdport, int ctlport, char* fmt, ...)
+{
+ int i, n;
+ va_list arg;
+ char buf[PRINTSIZE];
+
+ if(!(DEBUG & DbgPROBE)){
+ USED(cmdport, ctlport, fmt);
+ return 0;
+ }
+
+ va_start(arg, fmt);
+ n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
+ va_end(arg);
+
+ if(cmdport){
+ if(buf[n-1] == '\n')
+ n--;
+ n += snprint(buf+n, PRINTSIZE-n, " ataregs 0x%uX:",
+ cmdport);
+ for(i = Features; i < Command; i++)
+ n += snprint(buf+n, PRINTSIZE-n, " 0x%2.2uX",
+ inb(cmdport+i));
+ if(ctlport)
+ n += snprint(buf+n, PRINTSIZE-n, " 0x%2.2uX",
+ inb(ctlport+As));
+ n += snprint(buf+n, PRINTSIZE-n, "\n");
+ }
+ putstrn(buf, n);
+
+ return n;
+}
+
+static int
+ataready(int cmdport, int ctlport, int dev, int reset, int ready, int micro)
+{
+ int as;
+
+ atadebug(cmdport, ctlport, "ataready: dev %uX reset %uX ready %uX",
+ dev, reset, ready);
+
+ for(;;){
+ /*
+ * Wait for the controller to become not busy and
+ * possibly for a status bit to become true (usually
+ * Drdy). Must change to the appropriate device
+ * register set if necessary before testing for ready.
+ * Always run through the loop at least once so it
+ * can be used as a test for !Bsy.
+ */
+ as = inb(ctlport+As);
+ if(as & reset){
+ /* nothing to do */;
+ }
+ else if(dev){
+ outb(cmdport+Dh, dev);
+ dev = 0;
+ }
+ else if(ready == 0 || (as & ready)){
+ atadebug(0, 0, "ataready: %d 0x%2.2uX\n", micro, as);
+ return as;
+ }
+
+ if(micro-- <= 0){
+ atadebug(0, 0, "ataready: %d 0x%2.2uX\n", micro, as);
+ break;
+ }
+ microdelay(1);
+ }
+ atadebug(cmdport, ctlport, "ataready: timeout");
+
+ return -1;
+}
+
+static int
+atacsfenabled(Drive* drive, vlong csf)
+{
+ int cmdset, i, x;
+
+ for(i = 0; i < 3; i++){
+ x = (csf>>(16*i)) & 0xFFFF;
+ if(x == 0)
+ continue;
+ cmdset = drive->info[Icsfe+i];
+ if(cmdset == 0 || cmdset == 0xFFFF)
+ return 0;
+ return cmdset & x;
+ }
+
+ return 0;
+}
+
+/*
+static int
+atasf(int cmdport, int ctlport, int dev, uchar* command)
+{
+ int as, i;
+
+ if(ataready(cmdport, ctlport, dev, Bsy|Drq, Drdy, 108*1000) < 0)
+ return -1;
+
+ for(i = Features; i < Dh; i++)
+ outb(cmdport+i, command[i]);
+ outb(cmdport+Command, Csf);
+ microdelay(100);
+ as = ataready(cmdport, ctlport, 0, Bsy, Drdy|Df|Err, 109*1000);
+ if(as < 0 || (as & (Df|Err)))
+ return -1;
+ return 0;
+}
+ */
+
+static int
+ataidentify(int cmdport, int ctlport, int dev, int pkt, void* info)
+{
+ int as, command, drdy;
+
+ if(pkt){
+ command = Cidpkt;
+ drdy = 0;
+ }
+ else{
+ command = Cid;
+ drdy = Drdy;
+ }
+ as = ataready(cmdport, ctlport, dev, Bsy|Drq, drdy, 103*1000);
+ if(as < 0)
+ return as;
+ outb(cmdport+Command, command);
+ microdelay(1);
+
+ as = ataready(cmdport, ctlport, 0, Bsy, Drq|Err, 400*1000);
+ if(as < 0)
+ return -1;
+ if(as & Err)
+ return as;
+
+ memset(info, 0, 512);
+ inss(cmdport+Data, info, 256);
+ inb(cmdport+Status);
+
+ if(DEBUG & DbgIDENTIFY){
+ int i;
+ ushort *sp;
+
+ sp = (ushort*)info;
+ for(i = 0; i < 256; i++){
+ if(i && (i%16) == 0)
+ print("\n");
+ print(" %4.4uX ", *sp);
+ sp++;
+ }
+ print("\n");
+ }
+
+ return 0;
+}
+
+static Drive*
+atadrive(int cmdport, int ctlport, int dev)
+{
+ Drive *drive;
+ int as, i, pkt;
+ uchar buf[512], *p;
+ ushort iconfig, *sp;
+
+ atadebug(0, 0, "identify: port 0x%uX dev 0x%2.2uX\n", cmdport, dev);
+ pkt = 1;
+retry:
+ as = ataidentify(cmdport, ctlport, dev, pkt, buf);
+ if(as < 0)
+ return nil;
+ if(as & Err){
+ if(pkt == 0)
+ return nil;
+ pkt = 0;
+ goto retry;
+ }
+
+ if((drive = malloc(sizeof(Drive))) == nil)
+ return nil;
+ drive->dev = dev;
+ memmove(drive->info, buf, sizeof(drive->info));
+ drive->sense[0] = 0x70;
+ drive->sense[7] = sizeof(drive->sense)-7;
+
+ drive->inquiry[2] = 2;
+ drive->inquiry[3] = 2;
+ drive->inquiry[4] = sizeof(drive->inquiry)-4;
+ p = &drive->inquiry[8];
+ sp = &drive->info[Imodel];
+ for(i = 0; i < 20; i++){
+ *p++ = *sp>>8;
+ *p++ = *sp++;
+ }
+
+ drive->secsize = 512;
+
+ /*
+ * Beware the CompactFlash Association feature set.
+ * Now, why this value in Iconfig just walks all over the bit
+ * definitions used in the other parts of the ATA/ATAPI standards
+ * is a mystery and a sign of true stupidity on someone's part.
+ * Anyway, the standard says if this value is 0x848A then it's
+ * CompactFlash and it's NOT a packet device.
+ */
+ iconfig = drive->info[Iconfig];
+ if(iconfig != 0x848A && (iconfig & 0xC000) == 0x8000){
+ if(iconfig & 0x01)
+ drive->pkt = 16;
+ else
+ drive->pkt = 12;
+ }
+ else{
+ if(drive->info[Ivalid] & 0x0001){
+ drive->c = drive->info[Iccyl];
+ drive->h = drive->info[Ichead];
+ drive->s = drive->info[Icsec];
+ }
+ else{
+ drive->c = drive->info[Ilcyl];
+ drive->h = drive->info[Ilhead];
+ drive->s = drive->info[Ilsec];
+ }
+ if(drive->info[Icapabilities] & 0x0200){
+ if(drive->info[Icsfs+1] & 0x0400){
+ drive->sectors = drive->info[Ilba48]
+ |(drive->info[Ilba48+1]<<16)
+ |((vlong)drive->info[Ilba48+2]<<32);
+ drive->flags |= Lba48;
+ }
+ else{
+ drive->sectors = (drive->info[Ilba+1]<<16)
+ |drive->info[Ilba];
+ }
+ drive->dev |= Lba;
+ }
+ else
+ drive->sectors = drive->c*drive->h*drive->s;
+ // atarwmmode(drive, cmdport, ctlport, dev);
+ }
+// atadmamode(drive);
+
+ if(DEBUG & DbgCONFIG){
+ print("dev %2.2uX port %uX config %4.4uX capabilities %4.4uX",
+ dev, cmdport, iconfig, drive->info[Icapabilities]);
+ print(" mwdma %4.4uX", drive->info[Imwdma]);
+ if(drive->info[Ivalid] & 0x04)
+ print(" udma %4.4uX", drive->info[Iudma]);
+// print(" dma %8.8uX rwm %ud", drive->dma, drive->rwm);
+ if(drive->flags&Lba48)
+ print("\tLLBA sectors %lld", drive->sectors);
+ print("\n");
+ }
+
+ return drive;
+}
+
+static void
+atasrst(int ctlport)
+{
+ /*
+ * Srst is a big stick and may cause problems if further
+ * commands are tried before the drives become ready again.
+ * Also, there will be problems here if overlapped commands
+ * are ever supported.
+ */
+ microdelay(5);
+ outb(ctlport+Dc, Srst);
+ microdelay(5);
+ outb(ctlport+Dc, 0);
+ microdelay(2*1000);
+}
+
+static SDev*
+ataprobe(int cmdport, int ctlport, int irq)
+{
+ Ctlr* ctlr;
+ SDev *sdev;
+ Drive *drive;
+ int dev, error, rhi, rlo;
+
+// if(ioalloc(cmdport, 8, 0, "atacmd") < 0)
+// return nil;
+// if(ioalloc(ctlport+As, 1, 0, "atactl") < 0){
+// iofree(cmdport);
+// return nil;
+// }
+
+ /*
+ * Try to detect a floating bus.
+ * Bsy should be cleared. If not, see if the cylinder registers
+ * are read/write capable.
+ * If the master fails, try the slave to catch slave-only
+ * configurations.
+ * There's no need to restore the tested registers as they will
+ * be reset on any detected drives by the Cedd command.
+ * All this indicates is that there is at least one drive on the
+ * controller; when the non-existent drive is selected in a
+ * single-drive configuration the registers of the existing drive
+ * are often seen, only command execution fails.
+ */
+ dev = Dev0;
+ if(inb(ctlport+As) & Bsy){
+ outb(cmdport+Dh, dev);
+ microdelay(1);
+trydev1:
+ atadebug(cmdport, ctlport, "ataprobe bsy");
+ outb(cmdport+Cyllo, 0xAA);
+ outb(cmdport+Cylhi, 0x55);
+ outb(cmdport+Sector, 0xFF);
+ rlo = inb(cmdport+Cyllo);
+ rhi = inb(cmdport+Cylhi);
+ if(rlo != 0xAA && (rlo == 0xFF || rhi != 0x55)){
+ if(dev == Dev1){
+release:
+ // iofree(cmdport);
+ // iofree(ctlport+As);
+ return nil;
+ }
+ dev = Dev1;
+ if(ataready(cmdport, ctlport, dev, Bsy, 0, 20*1000) < 0)
+ goto trydev1;
+ }
+ }
+
+ /*
+ * Disable interrupts on any detected controllers.
+ */
+ outb(ctlport+Dc, Nien);
+tryedd1:
+ if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 105*1000) < 0){
+ /*
+ * There's something there, but it didn't come up clean,
+ * so try hitting it with a big stick. The timing here is
+ * wrong but this is a last-ditch effort and it sometimes
+ * gets some marginal hardware back online.
+ */
+ atasrst(ctlport);
+ if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 106*1000) < 0)
+ goto release;
+ }
+
+ /*
+ * Can only get here if controller is not busy.
+ * If there are drives Bsy will be set within 400nS,
+ * must wait 2mS before testing Status.
+ * Wait for the command to complete (6 seconds max).
+ */
+ outb(cmdport+Command, Cedd);
+ delay(2);
+ if(ataready(cmdport, ctlport, dev, Bsy|Drq, 0, 6*1000*1000) < 0)
+ goto release;
+
+ /*
+ * If bit 0 of the error register is set then the selected drive
+ * exists. This is enough to detect single-drive configurations.
+ * However, if the master exists there is no way short of executing
+ * a command to determine if a slave is present.
+ * It appears possible to get here testing Dev0 although it doesn't
+ * exist and the EDD won't take, so try again with Dev1.
+ */
+ error = inb(cmdport+Error);
+ atadebug(cmdport, ctlport, "ataprobe: dev %uX", dev);
+ if((error & ~0x80) != 0x01){
+ if(dev == Dev1)
+ goto release;
+ dev = Dev1;
+ goto tryedd1;
+ }
+
+ /*
+ * At least one drive is known to exist, try to
+ * identify it. If that fails, don't bother checking
+ * any further.
+ * If the one drive found is Dev0 and the EDD command
+ * didn't indicate Dev1 doesn't exist, check for it.
+ */
+ if((drive = atadrive(cmdport, ctlport, dev)) == nil)
+ goto release;
+ if((ctlr = malloc(sizeof(Ctlr))) == nil){
+ free(drive);
+ goto release;
+ }
+ if((sdev = malloc(sizeof(SDev))) == nil){
+ free(ctlr);
+ free(drive);
+ goto release;
+ }
+ drive->ctlr = ctlr;
+ if(dev == Dev0){
+ ctlr->drive[0] = drive;
+ if(!(error & 0x80)){
+ /*
+ * Always leave Dh pointing to a valid drive,
+ * otherwise a subsequent call to ataready on
+ * this controller may try to test a bogus Status.
+ * Ataprobe is the only place possibly invalid
+ * drives should be selected.
+ */
+ drive = atadrive(cmdport, ctlport, Dev1);
+ if(drive != nil){
+ drive->ctlr = ctlr;
+ ctlr->drive[1] = drive;
+ }
+ else{
+ outb(cmdport+Dh, Dev0);
+ microdelay(1);
+ }
+ }
+ }
+ else
+ ctlr->drive[1] = drive;
+
+ ctlr->cmdport = cmdport;
+ ctlr->ctlport = ctlport;
+ ctlr->irq = irq;
+ ctlr->tbdf = BUSUNKNOWN;
+ ctlr->command = Cedd; /* debugging */
+
+ sdev->ifc = &sdataifc;
+ sdev->ctlr = ctlr;
+ sdev->nunit = 2;
+ ctlr->sdev = sdev;
+
+ return sdev;
+}
+
+static int
+atasetsense(Drive* drive, int status, int key, int asc, int ascq)
+{
+ drive->sense[2] = key;
+ drive->sense[12] = asc;
+ drive->sense[13] = ascq;
+
+ return status;
+}
+
+static int
+atamodesense(Drive* drive, uchar* cmd)
+{
+ int len;
+
+ /*
+ * Fake a vendor-specific request with page code 0,
+ * return the drive info.
+ */
+ if((cmd[2] & 0x3F) != 0 && (cmd[2] & 0x3F) != 0x3F)
+ return atasetsense(drive, SDcheck, 0x05, 0x24, 0);
+ len = (cmd[7]<<8)|cmd[8];
+ if(len == 0)
+ return SDok;
+ if(len < 8+sizeof(drive->info))
+ return atasetsense(drive, SDcheck, 0x05, 0x1A, 0);
+ if(drive->data == nil || drive->dlen < len)
+ return atasetsense(drive, SDcheck, 0x05, 0x20, 1);
+ memset(drive->data, 0, 8);
+ drive->data[0] = sizeof(drive->info)>>8;
+ drive->data[1] = sizeof(drive->info);
+ memmove(drive->data+8, drive->info, sizeof(drive->info));
+ drive->data += 8+sizeof(drive->info);
+
+ return SDok;
+}
+
+static void
+atanop(Drive* drive, int subcommand)
+{
+ Ctlr* ctlr;
+ int as, cmdport, ctlport, timeo;
+
+ /*
+ * Attempt to abort a command by using NOP.
+ * In response, the drive is supposed to set Abrt
+ * in the Error register, set (Drdy|Err) in Status
+ * and clear Bsy when done. However, some drives
+ * (e.g. ATAPI Zip) just go Bsy then clear Status
+ * when done, hence the timeout loop only on Bsy
+ * and the forced setting of drive->error.
+ */
+ ctlr = drive->ctlr;
+ cmdport = ctlr->cmdport;
+ outb(cmdport+Features, subcommand);
+ outb(cmdport+Dh, drive->dev);
+ ctlr->command = Cnop; /* debugging */
+ outb(cmdport+Command, Cnop);
+
+ microdelay(1);
+ ctlport = ctlr->ctlport;
+ for(timeo = 0; timeo < 1000; timeo++){
+ as = inb(ctlport+As);
+ if(!(as & Bsy))
+ break;
+ microdelay(1);
+ }
+ drive->error |= Abrt;
+}
+
+static void
+ataabort(Drive* drive, int dolock)
+{
+ /*
+ * If NOP is available (packet commands) use it otherwise
+ * must try a software reset.
+ */
+ if(dolock)
+ ilock(drive->ctlr);
+ if(atacsfenabled(drive, 0x0000000000004000LL))
+ atanop(drive, 0);
+ else{
+ atasrst(drive->ctlr->ctlport);
+ drive->error |= Abrt;
+ }
+ if(dolock)
+ iunlock(drive->ctlr);
+}
+
+static int
+atapktiodone(void* arg)
+{
+ return ((Ctlr*)arg)->done;
+}
+
+static void
+atapktinterrupt(Drive* drive)
+{
+ Ctlr* ctlr;
+ int cmdport, len;
+
+ ctlr = drive->ctlr;
+ cmdport = ctlr->cmdport;
+ switch(inb(cmdport+Ir) & (/*Rel|*/Io|Cd)){
+ case Cd:
+ outss(cmdport+Data, drive->pktcmd, drive->pkt/2);
+ break;
+
+ case 0:
+ len = (inb(cmdport+Bytehi)<<8)|inb(cmdport+Bytelo);
+ if(drive->data+len > drive->limit){
+ atanop(drive, 0);
+ break;
+ }
+ outss(cmdport+Data, drive->data, len/2);
+ drive->data += len;
+ break;
+
+ case Io:
+ len = (inb(cmdport+Bytehi)<<8)|inb(cmdport+Bytelo);
+ if(drive->data+len > drive->limit){
+ atanop(drive, 0);
+ break;
+ }
+ inss(cmdport+Data, drive->data, len/2);
+ drive->data += len;
+ break;
+
+ case Io|Cd:
+ // if(drive->pktdma)
+ // atadmainterrupt(drive, drive->dlen);
+ // else
+ ctlr->done = 1;
+ break;
+ }
+}
+
+static int
+atapktio(Drive* drive, uchar* cmd, int clen)
+{
+ Ctlr *ctlr;
+ int as, cmdport, ctlport, len, r;
+
+ if(cmd[0] == 0x5A && (cmd[2] & 0x3F) == 0)
+ return atamodesense(drive, cmd);
+
+ r = SDok;
+
+ drive->command = Cpkt;
+ memmove(drive->pktcmd, cmd, clen);
+ memset(drive->pktcmd+clen, 0, drive->pkt-clen);
+ drive->limit = drive->data+drive->dlen;
+
+ ctlr = drive->ctlr;
+ cmdport = ctlr->cmdport;
+ ctlport = ctlr->ctlport;
+
+ qlock(ctlr);
+
+ as = ataready(cmdport, ctlport, drive->dev, Bsy|Drq, 0, 107*1000);
+ if(as < 0 || (as&Chk)){
+ qunlock(ctlr);
+ return -1;
+ }
+
+ ilock(ctlr);
+// if(drive->dlen && drive->dmactl && !atadmasetup(drive, drive->dlen))
+// drive->pktdma = Dma;
+// else
+// drive->pktdma = 0;
+
+ outb(cmdport+Features, 0/*drive->pktdma*/);
+ outb(cmdport+Count, 0);
+ outb(cmdport+Sector, 0);
+ len = 16*drive->secsize;
+ outb(cmdport+Bytelo, len);
+ outb(cmdport+Bytehi, len>>8);
+ outb(cmdport+Dh, drive->dev);
+ ctlr->done = 0;
+ ctlr->curdrive = drive;
+ ctlr->command = Cpkt; /* debugging */
+// if(drive->pktdma)
+// atadmastart(ctlr, drive->write);
+ outb(cmdport+Command, Cpkt);
+
+ if((drive->info[Iconfig] & 0x0060) != 0x0020){
+ microdelay(1);
+ as = ataready(cmdport, ctlport, 0, Bsy, Drq|Chk, 4*1000);
+ if(as < 0 || (as & (Bsy|Chk))){
+ drive->status = as<0 ? 0 : as;
+ ctlr->curdrive = nil;
+ ctlr->done = 1;
+ r = SDtimeout;
+ }else
+ atapktinterrupt(drive);
+ }
+ iunlock(ctlr);
+
+ sleep(ctlr, atapktiodone, ctlr);
+
+ qunlock(ctlr);
+
+ if(drive->status & Chk)
+ r = SDcheck;
+
+ return r;
+}
+
+static int
+atageniodone(void* arg)
+{
+ return ((Ctlr*)arg)->done;
+}
+
+static uchar cmd48[256] = {
+ [Crs] Crs48,
+ [Crd] Crd48,
+ [Crdq] Crdq48,
+ [Crsm] Crsm48,
+ [Cws] Cws48,
+ [Cwd] Cwd48,
+ [Cwdq] Cwdq48,
+ [Cwsm] Cwsm48,
+};
+
+static int
+atageniostart(Drive* drive, vlong lba)
+{
+ Ctlr *ctlr;
+ uchar cmd;
+ int as, c, cmdport, ctlport, h, len, s, use48;
+
+ use48 = 0;
+ if((drive->flags&Lba48always) || (lba>>28) || drive->count > 256){
+ if(!(drive->flags & Lba48))
+ return -1;
+ use48 = 1;
+ c = h = s = 0;
+ }else if(drive->dev & Lba){
+ c = (lba>>8) & 0xFFFF;
+ h = (lba>>24) & 0x0F;
+ s = lba & 0xFF;
+ }
+ else{
+ c = lba/(drive->s*drive->h);
+ h = ((lba/drive->s) % drive->h);
+ s = (lba % drive->s) + 1;
+ }
+
+ ctlr = drive->ctlr;
+ cmdport = ctlr->cmdport;
+ ctlport = ctlr->ctlport;
+ if(ataready(cmdport, ctlport, drive->dev, Bsy|Drq, 0, 101*1000) < 0)
+ return -1;
+
+ ilock(ctlr);
+
+ drive->block = drive->secsize;
+ if(drive->write)
+ drive->command = Cws;
+ else
+ drive->command = Crs;
+
+ drive->limit = drive->data + drive->count*drive->secsize;
+ cmd = drive->command;
+ if(use48){
+ outb(cmdport+Count, (drive->count>>8) & 0xFF);
+ outb(cmdport+Count, drive->count & 0XFF);
+ outb(cmdport+Lbalo, (lba>>24) & 0xFF);
+ outb(cmdport+Lbalo, lba & 0xFF);
+ outb(cmdport+Lbamid, (lba>>32) & 0xFF);
+ outb(cmdport+Lbamid, (lba>>8) & 0xFF);
+ outb(cmdport+Lbahi, (lba>>40) & 0xFF);
+ outb(cmdport+Lbahi, (lba>>16) & 0xFF);
+ outb(cmdport+Dh, drive->dev|Lba);
+ cmd = cmd48[cmd];
+
+ if(DEBUG & Dbg48BIT)
+ print("using 48-bit commands\n");
+ }else{
+ outb(cmdport+Count, drive->count);
+ outb(cmdport+Sector, s);
+ outb(cmdport+Cyllo, c);
+ outb(cmdport+Cylhi, c>>8);
+ outb(cmdport+Dh, drive->dev|h);
+ }
+ ctlr->done = 0;
+ ctlr->curdrive = drive;
+ ctlr->command = drive->command; /* debugging */
+ outb(cmdport+Command, cmd);
+
+ switch(drive->command){
+ case Cws:
+ case Cwsm:
+ microdelay(1);
+ as = ataready(cmdport, ctlport, 0, Bsy, Drq|Err, 1000);
+ if(as < 0 || (as & Err)){
+ iunlock(ctlr);
+ return -1;
+ }
+ len = drive->block;
+ if(drive->data+len > drive->limit)
+ len = drive->limit-drive->data;
+ outss(cmdport+Data, drive->data, len/2);
+ break;
+
+ case Crd:
+ case Cwd:
+ // atadmastart(ctlr, drive->write);
+ break;
+ }
+ iunlock(ctlr);
+
+ return 0;
+}
+
+static int
+atagenioretry(Drive* drive)
+{
+ return atasetsense(drive, SDcheck, 4, 8, drive->error);
+}
+
+static int
+atagenio(Drive* drive, uchar* cmd, int)
+{
+ uchar *p;
+ Ctlr *ctlr;
+ int count, max;
+ vlong lba, len;
+
+ /*
+ * Map SCSI commands into ATA commands for discs.
+ * Fail any command with a LUN except INQUIRY which
+ * will return 'logical unit not supported'.
+ */
+ if((cmd[1]>>5) && cmd[0] != 0x12)
+ return atasetsense(drive, SDcheck, 0x05, 0x25, 0);
+
+ switch(cmd[0]){
+ default:
+ return atasetsense(drive, SDcheck, 0x05, 0x20, 0);
+
+ case 0x00: /* test unit ready */
+ return SDok;
+
+ case 0x03: /* request sense */
+ if(cmd[4] < sizeof(drive->sense))
+ len = cmd[4];
+ else
+ len = sizeof(drive->sense);
+ if(drive->data && drive->dlen >= len){
+ memmove(drive->data, drive->sense, len);
+ drive->data += len;
+ }
+ return SDok;
+
+ case 0x12: /* inquiry */
+ if(cmd[4] < sizeof(drive->inquiry))
+ len = cmd[4];
+ else
+ len = sizeof(drive->inquiry);
+ if(drive->data && drive->dlen >= len){
+ memmove(drive->data, drive->inquiry, len);
+ drive->data += len;
+ }
+ return SDok;
+
+ case 0x1B: /* start/stop unit */
+ /*
+ * NOP for now, can use the power management feature
+ * set later.
+ */
+ return SDok;
+
+ case 0x25: /* read capacity */
+ if((cmd[1] & 0x01) || cmd[2] || cmd[3])
+ return atasetsense(drive, SDcheck, 0x05, 0x24, 0);
+ if(drive->data == nil || drive->dlen < 8)
+ return atasetsense(drive, SDcheck, 0x05, 0x20, 1);
+ /*
+ * Read capacity returns the LBA of the last sector.
+ */
+ len = drive->sectors-1;
+ p = drive->data;
+ *p++ = len>>24;
+ *p++ = len>>16;
+ *p++ = len>>8;
+ *p++ = len;
+ len = drive->secsize;
+ *p++ = len>>24;
+ *p++ = len>>16;
+ *p++ = len>>8;
+ *p = len;
+ drive->data += 8;
+ return SDok;
+
+ case 0x9E: /* long read capacity */
+ if((cmd[1] & 0x01) || cmd[2] || cmd[3])
+ return atasetsense(drive, SDcheck, 0x05, 0x24, 0);
+ if(drive->data == nil || drive->dlen < 8)
+ return atasetsense(drive, SDcheck, 0x05, 0x20, 1);
+ /*
+ * Read capacity returns the LBA of the last sector.
+ */
+ len = drive->sectors-1;
+ p = drive->data;
+ *p++ = len>>56;
+ *p++ = len>>48;
+ *p++ = len>>40;
+ *p++ = len>>32;
+ *p++ = len>>24;
+ *p++ = len>>16;
+ *p++ = len>>8;
+ *p++ = len;
+ len = drive->secsize;
+ *p++ = len>>24;
+ *p++ = len>>16;
+ *p++ = len>>8;
+ *p = len;
+ drive->data += 8;
+ return SDok;
+
+ case 0x28: /* read */
+ case 0x2A: /* write */
+ break;
+
+ case 0x5A:
+ return atamodesense(drive, cmd);
+ }
+
+ ctlr = drive->ctlr;
+ lba = (cmd[2]<<24)|(cmd[3]<<16)|(cmd[4]<<8)|cmd[5];
+ count = (cmd[7]<<8)|cmd[8];
+ if(drive->data == nil)
+ return SDok;
+ if(drive->dlen < count*drive->secsize)
+ count = drive->dlen/drive->secsize;
+ qlock(ctlr);
+ while(count){
+ max = (drive->flags&Lba48) ? 65536 : 256;
+ if(count > max)
+ drive->count = max;
+ else
+ drive->count = count;
+ if(atageniostart(drive, lba)){
+ ilock(ctlr);
+ atanop(drive, 0);
+ iunlock(ctlr);
+ qunlock(ctlr);
+ return atagenioretry(drive);
+ }
+
+ tsleep(ctlr, atageniodone, ctlr, 10*1000);
+ if(!ctlr->done){
+ /*
+ * What should the above timeout be? In
+ * standby and sleep modes it could take as
+ * long as 30 seconds for a drive to respond.
+ * Very hard to get out of this cleanly.
+ */
+ // atadumpstate(drive, cmd, lba, count);
+ ataabort(drive, 1);
+ return atagenioretry(drive);
+ }
+
+ if(drive->status & Err){
+ qunlock(ctlr);
+ return atasetsense(drive, SDcheck, 4, 8, drive->error);
+ }
+ count -= drive->count;
+ lba += drive->count;
+ }
+ qunlock(ctlr);
+
+ return SDok;
+}
+
+static int
+atario(SDreq* r)
+{
+ Ctlr *ctlr;
+ Drive *drive;
+ SDunit *unit;
+ uchar cmd10[10], *cmdp, *p;
+ int clen, reqstatus, status;
+
+ unit = r->unit;
+ if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil){
+ r->status = SDtimeout;
+ return SDtimeout;
+ }
+ drive = ctlr->drive[unit->subno];
+
+ /*
+ * Most SCSI commands can be passed unchanged except for
+ * the padding on the end. The few which require munging
+ * are not used internally. Mode select/sense(6) could be
+ * converted to the 10-byte form but it's not worth the
+ * effort. Read/write(6) are easy.
+ */
+ switch(r->cmd[0]){
+ case 0x08: /* read */
+ case 0x0A: /* write */
+ cmdp = cmd10;
+ memset(cmdp, 0, sizeof(cmd10));
+ cmdp[0] = r->cmd[0]|0x20;
+ cmdp[1] = r->cmd[1] & 0xE0;
+ cmdp[5] = r->cmd[3];
+ cmdp[4] = r->cmd[2];
+ cmdp[3] = r->cmd[1] & 0x0F;
+ cmdp[8] = r->cmd[4];
+ clen = sizeof(cmd10);
+ break;
+
+ default:
+ cmdp = r->cmd;
+ clen = r->clen;
+ break;
+ }
+
+ qlock(drive);
+ drive->write = r->write;
+ drive->data = r->data;
+ drive->dlen = r->dlen;
+
+ drive->status = 0;
+ drive->error = 0;
+ if(drive->pkt)
+ status = atapktio(drive, cmdp, clen);
+ else
+ status = atagenio(drive, cmdp, clen);
+ if(status == SDok){
+ atasetsense(drive, SDok, 0, 0, 0);
+ if(drive->data){
+ p = r->data;
+ r->rlen = drive->data - p;
+ }
+ else
+ r->rlen = 0;
+ }
+ else if(status == SDcheck && !(r->flags & SDnosense)){
+ drive->write = 0;
+ memset(cmd10, 0, sizeof(cmd10));
+ cmd10[0] = 0x03;
+ cmd10[1] = r->lun<<5;
+ cmd10[4] = sizeof(r->sense)-1;
+ drive->data = r->sense;
+ drive->dlen = sizeof(r->sense)-1;
+ drive->status = 0;
+ drive->error = 0;
+ if(drive->pkt)
+ reqstatus = atapktio(drive, cmd10, 6);
+ else
+ reqstatus = atagenio(drive, cmd10, 6);
+ if(reqstatus == SDok){
+ r->flags |= SDvalidsense;
+ atasetsense(drive, SDok, 0, 0, 0);
+ }
+ }
+ qunlock(drive);
+ r->status = status;
+ if(status != SDok)
+ return status;
+
+ /*
+ * Fix up any results.
+ * Many ATAPI CD-ROMs ignore the LUN field completely and
+ * return valid INQUIRY data. Patch the response to indicate
+ * 'logical unit not supported' if the LUN is non-zero.
+ */
+ switch(cmdp[0]){
+ case 0x12: /* inquiry */
+ if((p = r->data) == nil)
+ break;
+ if((cmdp[1]>>5) && (!drive->pkt || (p[0] & 0x1F) == 0x05))
+ p[0] = 0x7F;
+ /*FALLTHROUGH*/
+ default:
+ break;
+ }
+
+ return SDok;
+}
+
+static void
+atainterrupt(Ureg*, void* arg)
+{
+ Ctlr *ctlr;
+ Drive *drive;
+ int cmdport, len, status;
+
+ ctlr = arg;
+
+ ilock(ctlr);
+ if(inb(ctlr->ctlport+As) & Bsy){
+ iunlock(ctlr);
+ if(DEBUG & DbgBsy)
+ print("IBsy+");
+ return;
+ }
+ cmdport = ctlr->cmdport;
+ status = inb(cmdport+Status);
+ if((drive = ctlr->curdrive) == nil){
+ iunlock(ctlr);
+ if((DEBUG & DbgINL) && ctlr->command != Cedd)
+ print("Inil%2.2uX+", ctlr->command);
+ return;
+ }
+
+ if(status & Err)
+ drive->error = inb(cmdport+Error);
+ else switch(drive->command){
+ default:
+ drive->error = Abrt;
+ break;
+
+ case Crs:
+ case Crsm:
+ if(!(status & Drq)){
+ drive->error = Abrt;
+ break;
+ }
+ len = drive->block;
+ if(drive->data+len > drive->limit)
+ len = drive->limit-drive->data;
+ inss(cmdport+Data, drive->data, len/2);
+ drive->data += len;
+ if(drive->data >= drive->limit)
+ ctlr->done = 1;
+ break;
+
+ case Cws:
+ case Cwsm:
+ len = drive->block;
+ if(drive->data+len > drive->limit)
+ len = drive->limit-drive->data;
+ drive->data += len;
+ if(drive->data >= drive->limit){
+ ctlr->done = 1;
+ break;
+ }
+ if(!(status & Drq)){
+ drive->error = Abrt;
+ break;
+ }
+ len = drive->block;
+ if(drive->data+len > drive->limit)
+ len = drive->limit-drive->data;
+ outss(cmdport+Data, drive->data, len/2);
+ break;
+
+ case Cpkt:
+ atapktinterrupt(drive);
+ break;
+
+ case Crd:
+ case Cwd:
+ // atadmainterrupt(drive, drive->count*drive->secsize);
+ break;
+ }
+ iunlock(ctlr);
+
+ if(drive->error){
+ status |= Err;
+ ctlr->done = 1;
+ }
+
+ if(ctlr->done){
+ ctlr->curdrive = nil;
+ drive->status = status;
+ wakeup(ctlr);
+ }
+}
+
+static SDev*
+atapnp(void)
+{
+ Ctlr *ctlr;
+ Pcidev *p;
+ int channel, ispc87415, pi, r;
+ SDev *legacy[2], *sdev, *head, *tail;
+
+ legacy[0] = legacy[1] = head = tail = nil;
+ if(sdev = ataprobe(0x1F0, 0x3F4, IrqATA0)){
+ head = tail = sdev;
+ legacy[0] = sdev;
+ }
+ if(sdev = ataprobe(0x170, 0x374, IrqATA1)){
+ if(head != nil)
+ tail->next = sdev;
+ else
+ head = sdev;
+ tail = sdev;
+ legacy[1] = sdev;
+ }
+
+ p = nil;
+ while(p = pcimatch(p, 0, 0)){
+ /*
+ * Look for devices with the correct class and sub-class
+ * code and known device and vendor ID; add native-mode
+ * channels to the list to be probed, save info for the
+ * compatibility mode channels.
+ * Note that the legacy devices should not be considered
+ * PCI devices by the interrupt controller.
+ * For both native and legacy, save info for busmastering
+ * if capable.
+ * Promise Ultra ATA/66 (PDC20262) appears to
+ * 1) give a sub-class of 'other mass storage controller'
+ * instead of 'IDE controller', regardless of whether it's
+ * the only controller or not;
+ * 2) put 0 in the programming interface byte (probably
+ * as a consequence of 1) above).
+ * Sub-class code 0x04 is 'RAID controller', e.g. VIA VT8237.
+ */
+ if(p->ccrb != 0x01)
+ continue;
+ if(p->ccru != 0x01 && p->ccru != 0x04 && p->ccru != 0x80)
+ continue;
+ pi = p->ccrp;
+ ispc87415 = 0;
+
+ switch((p->did<<16)|p->vid){
+ default:
+ continue;
+
+ case (0x0002<<16)|0x100B: /* NS PC87415 */
+ /*
+ * Disable interrupts on both channels until
+ * after they are probed for drives.
+ * This must be called before interrupts are
+ * enabled because the IRQ may be shared.
+ */
+ ispc87415 = 1;
+ pcicfgw32(p, 0x40, 0x00000300);
+ break;
+ case (0x1000<<16)|0x1042: /* PC-Tech RZ1000 */
+ /*
+ * Turn off prefetch. Overkill, but cheap.
+ */
+ r = pcicfgr32(p, 0x40);
+ r &= ~0x2000;
+ pcicfgw32(p, 0x40, r);
+ break;
+ case (0x4D38<<16)|0x105A: /* Promise PDC20262 */
+ case (0x4D30<<16)|0x105A: /* Promise PDC202xx */
+ case (0x4D68<<16)|0x105A: /* Promise PDC20268 */
+ case (0x4D69<<16)|0x105A: /* Promise Ultra/133 TX2 */
+ case (0x3373<<16)|0x105A: /* Promise 20378 RAID */
+ case (0x3149<<16)|0x1106: /* VIA VT8237 SATA/RAID */
+ case (0x3112<<16)|0x1095: /* SiL 3112 SATA (DMA busted?) */
+ case (0x3114<<16)|0x1095: /* SiL 3114 SATA/RAID */
+ pi = 0x85;
+ break;
+ case (0x0004<<16)|0x1103: /* HighPoint HPT-370 */
+ pi = 0x85;
+ /*
+ * Turn off fast interrupt prediction.
+ */
+ if((r = pcicfgr8(p, 0x51)) & 0x80)
+ pcicfgw8(p, 0x51, r & ~0x80);
+ if((r = pcicfgr8(p, 0x55)) & 0x80)
+ pcicfgw8(p, 0x55, r & ~0x80);
+ break;
+ case (0x0640<<16)|0x1095: /* CMD 640B */
+ /*
+ * Bugfix code here...
+ */
+ break;
+ case (0x7441<<16)|0x1022: /* AMD 768 */
+ /*
+ * Set:
+ * 0x41 prefetch, postwrite;
+ * 0x43 FIFO configuration 1/2 and 1/2;
+ * 0x44 status register read retry;
+ * 0x46 DMA read and end of sector flush.
+ */
+ r = pcicfgr8(p, 0x41);
+ pcicfgw8(p, 0x41, r|0xF0);
+ r = pcicfgr8(p, 0x43);
+ pcicfgw8(p, 0x43, (r & 0x90)|0x2A);
+ r = pcicfgr8(p, 0x44);
+ pcicfgw8(p, 0x44, r|0x08);
+ r = pcicfgr8(p, 0x46);
+ pcicfgw8(p, 0x46, (r & 0x0C)|0xF0);
+ /*FALLTHROUGH*/
+ case (0x7469<<16)|0x1022: /* AMD 3111 */
+ /*
+ * This can probably be lumped in with the 768 above.
+ */
+ /*FALLTHROUGH*/
+ case (0x00D5<<16)|0x10DE: /* nVidia nForce3 */
+ /*
+ * Ditto, although it may have a different base
+ * address for the registers (0x50?).
+ */
+ break;
+ case (0x0646<<16)|0x1095: /* CMD 646 */
+ case (0x0571<<16)|0x1106: /* VIA 82C686 */
+ case (0x0211<<16)|0x1166: /* ServerWorks IB6566 */
+ case (0x1230<<16)|0x8086: /* 82371FB (PIIX) */
+ case (0x7010<<16)|0x8086: /* 82371SB (PIIX3) */
+ case (0x7111<<16)|0x8086: /* 82371[AE]B (PIIX4[E]) */
+ case (0x2411<<16)|0x8086: /* 82801AA (ICH) */
+ case (0x2421<<16)|0x8086: /* 82801AB (ICH0) */
+ case (0x244A<<16)|0x8086: /* 82801BA (ICH2, Mobile) */
+ case (0x244B<<16)|0x8086: /* 82801BA (ICH2, High-End) */
+ case (0x248A<<16)|0x8086: /* 82801CA (ICH3, Mobile) */
+ case (0x248B<<16)|0x8086: /* 82801CA (ICH3, High-End) */
+ case (0x24CA<<16)|0x8086: /* 82801DBM (ICH4, Mobile) */
+ case (0x24CB<<16)|0x8086: /* 82801DB (ICH4, High-End) */
+ case (0x24DB<<16)|0x8086: /* 82801EB (ICH5) */
+ break;
+ }
+
+ for(channel = 0; channel < 2; channel++){
+ if(pi & (1<<(2*channel))){
+ sdev = ataprobe(p->mem[0+2*channel].bar & ~0x01,
+ p->mem[1+2*channel].bar & ~0x01,
+ p->intl);
+ if(sdev == nil)
+ continue;
+
+ ctlr = sdev->ctlr;
+ if(ispc87415)
+ ctlr->ienable = pc87415ienable;
+
+ if(head != nil)
+ tail->next = sdev;
+ else
+ head = sdev;
+ tail = sdev;
+ ctlr->tbdf = p->tbdf;
+ }
+ else if((sdev = legacy[channel]) == nil)
+ continue;
+ else
+ ctlr = sdev->ctlr;
+
+ ctlr->pcidev = p;
+ }
+ }
+
+ return head;
+}
+
+static SDev*
+atalegacy(int port, int irq)
+{
+ return ataprobe(port, port+0x204, irq);
+}
+
+static SDev*
+ataid(SDev* sdev)
+{
+ int i;
+ Ctlr *ctlr;
+
+ /*
+ * Legacy controllers are always 'C' and 'D' and if
+ * they exist and have drives will be first in the list.
+ * If there are no active legacy controllers, native
+ * controllers start at 'C'.
+ */
+ if(sdev == nil)
+ return nil;
+ ctlr = sdev->ctlr;
+ if(ctlr->cmdport == 0x1F0 || ctlr->cmdport == 0x170)
+ i = 2;
+ else
+ i = 0;
+ while(sdev){
+ if(sdev->ifc == &sdataifc){
+ ctlr = sdev->ctlr;
+ if(ctlr->cmdport == 0x1F0)
+ sdev->idno = 'C';
+ else if(ctlr->cmdport == 0x170)
+ sdev->idno = 'D';
+ else{
+ sdev->idno = 'C'+i;
+ i++;
+ }
+ // snprint(sdev->name, NAMELEN, "sd%c", sdev->idno);
+ }
+ sdev = sdev->next;
+ }
+
+ return nil;
+}
+
+static int
+ataenable(SDev* sdev)
+{
+ Ctlr *ctlr;
+
+ ctlr = sdev->ctlr;
+
+ setvec(ctlr->irq+VectorPIC, atainterrupt, ctlr);
+ outb(ctlr->ctlport+Dc, 0);
+ if(ctlr->ienable)
+ ctlr->ienable(ctlr);
+
+ return 1;
+}
+
+SDifc sdataifc = {
+ "ata", /* name */
+
+ atapnp, /* pnp */
+ atalegacy, /* legacy */
+ ataid, /* id */
+ ataenable, /* enable */
+ nil, /* disable */
+
+ scsiverify, /* verify */
+ scsionline, /* online */
+ atario, /* rio */
+ nil, /* rctl */
+ nil, /* wctl */
+
+ scsibio, /* bio */
+};
diff --git a/os/boot/pc/sdmylex.c b/os/boot/pc/sdmylex.c
new file mode 100644
index 00000000..bca23489
--- /dev/null
+++ b/os/boot/pc/sdmylex.c
@@ -0,0 +1,1294 @@
+/*
+ * Mylex MultiMaster (Buslogic BT-*) SCSI Host Adapter
+ * in both 24-bit and 32-bit mode.
+ * 24-bit mode works for Adaptec AHA-154xx series too.
+ *
+ * To do:
+ * allocate more Ccb's as needed, up to NMbox-1;
+ * add nmbox and nccb to Ctlr struct for the above;
+ * 64-bit LUN/explicit wide support necessary?
+ *
+ */
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "error.h"
+
+#include "sd.h"
+
+#define waserror() (0)
+#define poperror()
+typedef struct QLock{ int r; } QLock;
+typedef struct Rendez{ int r; } Rendez;
+#define intrenable(irq, f, c, tbdf, name) setvec(VectorPIC+(irq), f, c);\
+ USED(tbdf);
+#define ioalloc(p, b, c, d) (1)
+#define iofree(p)
+
+#define K2BPA(va, tbdf) PADDR(va)
+#define BPA2K(pa, tbdf) KADDR(pa)
+
+extern SDifc sdmylexifc;
+
+enum { /* registers */
+ Rcontrol = 0x00, /* WO: control register */
+ Rstatus = 0x00, /* RO: status register */
+ Rcpr = 0x01, /* WO: command/parameter register */
+ Rdatain = 0x01, /* RO: data-in register */
+ Rinterrupt = 0x02, /* RO: interrupt register */
+};
+
+enum { /* Rcontrol */
+ Rsbus = 0x10, /* SCSI Bus Reset */
+ Rint = 0x20, /* Interrupt Reset */
+ Rsoft = 0x40, /* Soft Reset */
+ Rhard = 0x80, /* Hard Reset */
+};
+
+enum { /* Rstatus */
+ Cmdinv = 0x01, /* Command Invalid */
+ Dirrdy = 0x04, /* Data In Register Ready */
+ Cprbsy = 0x08, /* Command/Parameter Register Busy */
+ Hardy = 0x10, /* Host Adapter Ready */
+ Inreq = 0x20, /* Initialisation Required */
+ Dfail = 0x40, /* Diagnostic Failure */
+ Dact = 0x80, /* Diagnostic Active */
+};
+
+enum { /* Rcpr */
+ Cinitialise = 0x01, /* Initialise Mailbox */
+ Cstart = 0x02, /* Start Mailbox Command */
+ Cinquiry = 0x04, /* Adapter Inquiry */
+ Ceombri = 0x05, /* Enable OMBR Interrupt */
+ Cinquire = 0x0B, /* Inquire Configuration */
+ Cextbios = 0x28, /* AHA-1542: extended BIOS info. */
+ Cmbienable = 0x29, /* AHA-1542: Mailbox interface enable */
+ Ciem = 0x81, /* Initialise Extended Mailbox */
+ Ciesi = 0x8D, /* Inquire Extended Setup Information */
+ Cerrm = 0x8F, /* Enable strict round-robin mode */
+ Cwide = 0x96, /* Wide CCB */
+};
+
+enum { /* Rinterrupt */
+ Imbl = 0x01, /* Incoming Mailbox Loaded */
+ Mbor = 0x02, /* Mailbox Out Ready */
+ Cmdc = 0x04, /* Command Complete */
+ Rsts = 0x08, /* SCSI Reset State */
+ Intv = 0x80, /* Interrupt Valid */
+};
+
+typedef struct {
+ uchar code; /* action/completion code */
+ uchar ccb[3]; /* CCB pointer (MSB, ..., LSB) */
+} Mbox24;
+
+typedef struct {
+ uchar ccb[4]; /* CCB pointer (LSB, ..., MSB) */
+ uchar btstat; /* BT-7[45]7[SD] status */
+ uchar sdstat; /* SCSI device status */
+ uchar pad;
+ uchar code; /* action/completion code */
+} Mbox32;
+
+enum { /* mailbox commands */
+ Mbfree = 0x00, /* Mailbox not in use */
+
+ Mbostart = 0x01, /* Start a mailbox command */
+ Mboabort = 0x02, /* Abort a mailbox command */
+
+ Mbiok = 0x01, /* CCB completed without error */
+ Mbiabort = 0x02, /* CCB aborted at request of host */
+ Mbinx = 0x03, /* Aborted CCB not found */
+ Mbierror = 0x04, /* CCB completed with error */
+};
+
+typedef struct Ccb24 Ccb24;
+typedef struct Ccb32 Ccb32;
+typedef union Ccb Ccb;
+
+typedef struct Ccb24 {
+ uchar opcode; /* Operation code */
+ uchar datadir; /* Data direction control */
+ uchar cdblen; /* Length of CDB */
+ uchar senselen; /* Length of sense area */
+ uchar datalen[3]; /* Data length (MSB, ..., LSB) */
+ uchar dataptr[3]; /* Data pointer (MSB, ..., LSB) */
+ uchar linkptr[3]; /* Link pointer (MSB, ..., LSB) */
+ uchar linkid; /* command linking identifier */
+ uchar btstat; /* BT-* adapter status */
+ uchar sdstat; /* SCSI device status */
+ uchar reserved[2]; /* */
+ uchar cs[12+0xFF]; /* Command descriptor block + Sense */
+
+ void* data; /* buffer if address > 24-bits */
+
+ Rendez;
+ int done; /* command completed */
+
+ Ccb* ccb; /* link on free list */
+} Ccb24;
+
+
+typedef struct Ccb32 {
+ uchar opcode; /* Operation code */
+ uchar datadir; /* Data direction control */
+ uchar cdblen; /* Length of CDB */
+ uchar senselen; /* Length of sense area */
+ uchar datalen[4]; /* Data length (LSB, ..., MSB) */
+ uchar dataptr[4]; /* Data pointer (LSB, ..., MSB) */
+ uchar reserved[2];
+ uchar btstat; /* BT-* adapter status */
+ uchar sdstat; /* SCSI device status */
+ uchar targetid; /* Target ID */
+ uchar luntag; /* LUN & tag */
+ uchar cdb[12]; /* Command descriptor block */
+ uchar ccbctl; /* CCB control */
+ uchar linkid; /* command linking identifier */
+ uchar linkptr[4]; /* Link pointer (LSB, ..., MSB) */
+ uchar senseptr[4]; /* Sense pointer (LSB, ..., MSB) */
+ uchar sense[0xFF]; /* Sense bytes */
+
+ Rendez;
+ int done; /* command completed */
+
+ Ccb* ccb; /* link on free list */
+} Ccb32;
+
+typedef union Ccb {
+ Ccb24;
+ Ccb32;
+} Ccb;
+
+enum { /* opcode */
+ OInitiator = 0x00, /* initiator CCB */
+ Ordl = 0x03, /* initiator CCB with
+ * residual data length returned
+ */
+};
+
+enum { /* datadir */
+ CCBdatain = 0x08, /* inbound, length is checked */
+ CCBdataout = 0x10, /* outbound, length is checked */
+};
+
+enum { /* btstat */
+ Eok = 0x00, /* normal completion with no errors */
+};
+
+enum { /* luntag */
+ TagEnable = 0x20, /* Tag enable */
+ SQTag = 0x00, /* Simple Queue Tag */
+ HQTag = 0x40, /* Head of Queue Tag */
+ OQTag = 0x80, /* Ordered Queue Tag */
+};
+
+enum { /* CCB control */
+ NoDisc = 0x08, /* No disconnect */
+ NoUnd = 0x10, /* No underrrun error report */
+ NoData = 0x20, /* No data transfer */
+ NoStat = 0x40, /* No CCB status if zero */
+ NoIntr = 0x80, /* No Interrupts */
+};
+
+typedef struct {
+ int port; /* I/O port */
+ int id; /* adapter SCSI id */
+ int bus; /* 24 or 32 -bit */
+ int irq;
+ int wide;
+ Pcidev* pcidev;
+ SDev* sdev;
+ int spurious;
+
+ Lock issuelock;
+
+ Lock ccblock;
+ QLock ccbq;
+ Rendez ccbr;
+
+ Lock mboxlock;
+ void* mb; /* mailbox out + mailbox in */
+ int mbox; /* current mailbox out index into mb */
+ int mbix; /* current mailbox in index into mb */
+
+ Lock cachelock;
+ Ccb* ccb; /* list of free Ccb's */
+ Ccb** cache; /* last completed Ccb */
+} Ctlr;
+
+/*
+ * The number of mailboxes should be a multiple of 8 (4 for Mbox32)
+ * to ensure the boundary between the out and in mailboxes doesn't
+ * straddle a cache-line boundary.
+ * The number of Ccb's should be less than the number of mailboxes to
+ * ensure no queueing is necessary on mailbox allocation.
+ */
+enum {
+ NMbox = 8*8, /* number of Mbox's */
+ NCcb = NMbox-1, /* number of Ccb's */
+};
+
+#define PADDR24(a, n) ((PADDR(a)+(n)) <= (1<<24))
+
+static void
+ccbfree(Ctlr* ctlr, Ccb* ccb)
+{
+ lock(&ctlr->ccblock);
+ if(ctlr->bus == 24)
+ ((Ccb24*)ccb)->ccb = ctlr->ccb;
+ else
+ ((Ccb32*)ccb)->ccb = ctlr->ccb;
+ if(ctlr->ccb == nil)
+ wakeup(&ctlr->ccbr);
+ ctlr->ccb = ccb;
+ unlock(&ctlr->ccblock);
+}
+
+static int
+ccbavailable(void* a)
+{
+ return ((Ctlr*)a)->ccb != nil;
+}
+
+static Ccb*
+ccballoc(Ctlr* ctlr)
+{
+ Ccb *ccb;
+
+ for(;;){
+ lock(&ctlr->ccblock);
+ if((ccb = ctlr->ccb) != nil){
+ if(ctlr->bus == 24)
+ ctlr->ccb = ((Ccb24*)ccb)->ccb;
+ else
+ ctlr->ccb = ((Ccb32*)ccb)->ccb;
+ unlock(&ctlr->ccblock);
+ break;
+ }
+
+ unlock(&ctlr->ccblock);
+ qlock(&ctlr->ccbq);
+ if(waserror()){
+ qunlock(&ctlr->ccbq);
+ continue;
+ }
+ sleep(&ctlr->ccbr, ccbavailable, ctlr);
+ qunlock(&ctlr->ccbq);
+ poperror();
+ }
+
+ return ccb;
+}
+
+static int
+done24(void* arg)
+{
+ return ((Ccb24*)arg)->done;
+}
+
+static int
+mylex24rio(SDreq* r)
+{
+ ulong p;
+ Ctlr *ctlr;
+ Ccb24 *ccb;
+ Mbox24 *mb;
+ uchar *data, lun, *sense;
+ int d, n, btstat, sdstat, target;
+
+ ctlr = r->unit->dev->ctlr;
+ target = r->unit->subno;
+ lun = (r->cmd[1]>>5) & 0x07;
+
+ /*
+ * Ctlr->cache holds the last completed Ccb for this target if it
+ * returned 'check condition'.
+ * If this command is a request-sense and there is valid sense data
+ * from the last completed Ccb, return it immediately.
+ */
+ lock(&ctlr->cachelock);
+ if((ccb = ctlr->cache[target]) != nil){
+ ctlr->cache[target] = nil;
+ if(r->cmd[0] == 0x03
+ && ccb->sdstat == SDcheck && lun == ((ccb->cs[1]>>5) & 0x07)){
+ unlock(&ctlr->cachelock);
+ if(r->dlen){
+ sense = &ccb->cs[ccb->cdblen];
+ n = 8+sense[7];
+ if(n > r->dlen)
+ n = r->dlen;
+ memmove(r->data, sense, n);
+ r->rlen = n;
+ }
+ ccbfree(ctlr, (Ccb*)ccb);
+ return SDok;
+ }
+ }
+ unlock(&ctlr->cachelock);
+ if(ccb == nil)
+ ccb = ccballoc(ctlr);
+
+ /*
+ * Check if the transfer is to memory above the 24-bit limit the
+ * controller can address. If it is, try to allocate a temporary
+ * buffer as a staging area.
+ */
+ n = r->dlen;
+ if(n && !PADDR24(r->data, n)){
+ data = mallocz(n, 0);
+ if(data == nil || !PADDR24(data, n)){
+ if(data != nil){
+ free(data);
+ ccb->data = nil;
+ }
+ ccbfree(ctlr, (Ccb*)ccb);
+ return SDmalloc;
+ }
+ if(r->write)
+ memmove(data, r->data, n);
+ ccb->data = r->data;
+ }
+ else
+ data = r->data;
+
+ /*
+ * Fill in the ccb.
+ */
+ ccb->opcode = Ordl;
+
+ ccb->datadir = (target<<5)|lun;
+ if(n == 0)
+ ccb->datadir |= CCBdataout|CCBdatain;
+ else if(!r->write)
+ ccb->datadir |= CCBdatain;
+ else
+ ccb->datadir |= CCBdataout;
+
+ ccb->cdblen = r->clen;
+ ccb->senselen = 0xFF;
+
+ ccb->datalen[0] = n>>16;
+ ccb->datalen[1] = n>>8;
+ ccb->datalen[2] = n;
+ p = PADDR(data);
+ ccb->dataptr[0] = p>>16;
+ ccb->dataptr[1] = p>>8;
+ ccb->dataptr[2] = p;
+
+ ccb->linkptr[0] = ccb->linkptr[1] = ccb->linkptr[2] = 0;
+ ccb->linkid = 0;
+ ccb->btstat = ccb->sdstat = 0;
+ ccb->reserved[0] = ccb->reserved[1] = 0;
+
+ memmove(ccb->cs, r->cmd, r->clen);
+
+ /*
+ * There's one more mbox than there there is
+ * ccb so there is always one free.
+ */
+ lock(&ctlr->mboxlock);
+ mb = ctlr->mb;
+ mb += ctlr->mbox;
+ p = PADDR(ccb);
+ mb->ccb[0] = p>>16;
+ mb->ccb[1] = p>>8;
+ mb->ccb[2] = p;
+ mb->code = Mbostart;
+ ctlr->mbox++;
+ if(ctlr->mbox >= NMbox)
+ ctlr->mbox = 0;
+
+ /*
+ * This command does not require Hardy
+ * and doesn't generate a Cmdc interrupt.
+ */
+ ccb->done = 0;
+ outb(ctlr->port+Rcpr, Cstart);
+ unlock(&ctlr->mboxlock);
+
+ /*
+ * Wait for the request to complete and return the status.
+ * Since the buffer is not reference counted cannot return
+ * until the DMA is done writing into the buffer so the caller
+ * cannot free the buffer prematurely.
+ */
+ while(waserror())
+ ;
+ tsleep(ccb, done24, ccb, 30*1000);
+ poperror();
+
+ if(!done24(ccb)){
+ print("%s: %d/%d: sd24rio timeout\n",
+ "sdmylex"/*ctlr->sdev->name*/, target, r->lun);
+ if(ccb->data != nil){
+ free(data);
+ ccb->data = nil;
+ }
+ ccbfree(ctlr, (Ccb*)ccb);
+
+ return SDtimeout;
+ }
+
+ /*
+ * Save the status and patch up the number of
+ * bytes actually transferred.
+ * There's a firmware bug on some 956C controllers
+ * which causes the return count from a successful
+ * READ CAPACITY not be updated, so fix it here.
+ */
+ sdstat = ccb->sdstat;
+ btstat = ccb->btstat;
+
+ d = ccb->datalen[0]<<16;
+ d |= ccb->datalen[1]<<8;
+ d |= ccb->datalen[2];
+ if(ccb->cs[0] == 0x25 && sdstat == SDok)
+ d = 0;
+ n -= d;
+ r->rlen = n;
+
+ /*
+ * Tidy things up if a staging area was used for the data,
+ */
+ if(ccb->data != nil){
+ if(sdstat == SDok && btstat == 0 && !r->write)
+ memmove(ccb->data, data, n);
+ free(data);
+ ccb->data = nil;
+ }
+
+ /*
+ * If there was a check-condition, save the
+ * ccb for a possible request-sense command.
+ */
+ if(sdstat == SDcheck){
+ if(r->flags & SDnosense){
+ lock(&ctlr->cachelock);
+ if(ctlr->cache[target])
+ ccbfree(ctlr, ctlr->cache[target]);
+ ctlr->cache[target] = (Ccb*)ccb;
+ unlock(&ctlr->cachelock);
+ return SDcheck;
+ }
+ sense = &ccb->cs[ccb->cdblen];
+ n = 8+sense[7];
+ if(n > sizeof(r->sense)-1)
+ n = sizeof(r->sense)-1;
+ memmove(r->sense, sense, n);
+ r->flags |= SDvalidsense;
+ }
+ ccbfree(ctlr, (Ccb*)ccb);
+
+ if(btstat){
+ if(btstat == 0x11)
+ return SDtimeout;
+ return SDeio;
+ }
+ return sdstat;
+}
+
+static void
+mylex24interrupt(Ureg*, void* arg)
+{
+ ulong pa;
+ Ctlr *ctlr;
+ Ccb24 *ccb;
+ Mbox24 *mb, *mbox;
+ int port, rinterrupt, rstatus;
+
+ ctlr = arg;
+ port = ctlr->port;
+
+ /*
+ * Save and clear the interrupt(s). The only
+ * interrupts expected are Cmdc, which is ignored,
+ * and Imbl which means something completed.
+ * There's one spurious interrupt left over from
+ * initialisation, ignore it.
+ */
+ rinterrupt = inb(port+Rinterrupt);
+ rstatus = inb(port+Rstatus);
+ outb(port+Rcontrol, Rint);
+ if((rinterrupt & ~(Cmdc|Imbl)) != Intv && ctlr->spurious++)
+ print("%s: interrupt 0x%2.2ux\n",
+ "sdmylex"/*ctlr->sdev->name*/, rinterrupt);
+ if((rinterrupt & Cmdc) && (rstatus & Cmdinv))
+ print("%s: command invalid\n", "sdmylex"/*ctlr->sdev->name*/);
+
+ /*
+ * Look for something in the mail.
+ * If there is, save the status, free the mailbox
+ * and wakeup whoever.
+ */
+ mb = ctlr->mb;
+ for(mbox = &mb[ctlr->mbix]; mbox->code; mbox = &mb[ctlr->mbix]){
+ pa = (mbox->ccb[0]<<16)|(mbox->ccb[1]<<8)|mbox->ccb[2];
+ ccb = BPA2K(pa, BUSUNKNOWN);
+ mbox->code = 0;
+ ccb->done = 1;
+ wakeup(ccb);
+
+ ctlr->mbix++;
+ if(ctlr->mbix >= NMbox+NMbox)
+ ctlr->mbix = NMbox;
+ }
+}
+
+static int
+done32(void* arg)
+{
+ return ((Ccb32*)arg)->done;
+}
+
+static int
+mylex32rio(SDreq* r)
+{
+ ulong p;
+ uchar lun;
+ Ctlr *ctlr;
+ Ccb32 *ccb;
+ Mbox32 *mb;
+ int d, n, btstat, sdstat, target;
+
+ ctlr = r->unit->dev->ctlr;
+ target = r->unit->subno;
+ lun = (r->cmd[1]>>5) & 0x07;
+
+ /*
+ * Ctlr->cache holds the last completed Ccb for this target if it
+ * returned 'check condition'.
+ * If this command is a request-sense and there is valid sense data
+ * from the last completed Ccb, return it immediately.
+ */
+ lock(&ctlr->cachelock);
+ if((ccb = ctlr->cache[target]) != nil){
+ ctlr->cache[target] = nil;
+ if(r->cmd[0] == 0x03
+ && ccb->sdstat == SDcheck && lun == (ccb->luntag & 0x07)){
+ unlock(&ctlr->cachelock);
+ if(r->dlen){
+ n = 8+ccb->sense[7];
+ if(n > r->dlen)
+ n = r->dlen;
+ memmove(r->data, ccb->sense, n);
+ r->rlen = n;
+ }
+ ccbfree(ctlr, (Ccb*)ccb);
+ return SDok;
+ }
+ }
+ unlock(&ctlr->cachelock);
+ if(ccb == nil)
+ ccb = ccballoc(ctlr);
+
+ /*
+ * Fill in the ccb.
+ */
+ ccb->opcode = Ordl;
+
+ n = r->dlen;
+ if(n == 0)
+ ccb->datadir = CCBdataout|CCBdatain;
+ else if(!r->write)
+ ccb->datadir = CCBdatain;
+ else
+ ccb->datadir = CCBdataout;
+
+ ccb->cdblen = r->clen;
+
+ ccb->datalen[0] = n;
+ ccb->datalen[1] = n>>8;
+ ccb->datalen[2] = n>>16;
+ ccb->datalen[3] = n>>24;
+ p = PADDR(r->data);
+ ccb->dataptr[0] = p;
+ ccb->dataptr[1] = p>>8;
+ ccb->dataptr[2] = p>>16;
+ ccb->dataptr[3] = p>>24;
+
+ ccb->targetid = target;
+ ccb->luntag = lun;
+ if(r->unit->inquiry[7] & 0x02)
+ ccb->luntag |= SQTag|TagEnable;
+ memmove(ccb->cdb, r->cmd, r->clen);
+ ccb->btstat = ccb->sdstat = 0;
+ ccb->ccbctl = 0;
+
+ /*
+ * There's one more mbox than there there is
+ * ccb so there is always one free.
+ */
+ lock(&ctlr->mboxlock);
+ mb = ctlr->mb;
+ mb += ctlr->mbox;
+ p = PADDR(ccb);
+ mb->ccb[0] = p;
+ mb->ccb[1] = p>>8;
+ mb->ccb[2] = p>>16;
+ mb->ccb[3] = p>>24;
+ mb->code = Mbostart;
+ ctlr->mbox++;
+ if(ctlr->mbox >= NMbox)
+ ctlr->mbox = 0;
+
+ /*
+ * This command does not require Hardy
+ * and doesn't generate a Cmdc interrupt.
+ */
+ ccb->done = 0;
+ outb(ctlr->port+Rcpr, Cstart);
+ unlock(&ctlr->mboxlock);
+
+ /*
+ * Wait for the request to complete and return the status.
+ * Since the buffer is not reference counted cannot return
+ * until the DMA is done writing into the buffer so the caller
+ * cannot free the buffer prematurely.
+ */
+ while(waserror())
+ ;
+ tsleep(ccb, done32, ccb, 30*1000);
+ poperror();
+
+ if(!done32(ccb)){
+ print("%s: %d/%d: sd32rio timeout\n",
+ "sdmylex"/*ctlr->sdev->name*/, target, r->lun);
+ ccbfree(ctlr, (Ccb*)ccb);
+
+ return SDtimeout;
+ }
+
+ /*
+ * Save the status and patch up the number of
+ * bytes actually transferred.
+ * There's a firmware bug on some 956C controllers
+ * which causes the return count from a successful
+ * READ CAPACITY not to be updated, so fix it here.
+ */
+ sdstat = ccb->sdstat;
+ btstat = ccb->btstat;
+
+ d = ccb->datalen[0];
+ d |= (ccb->datalen[1]<<8);
+ d |= (ccb->datalen[2]<<16);
+ d |= (ccb->datalen[3]<<24);
+ if(ccb->cdb[0] == 0x25 && sdstat == SDok)
+ d = 0;
+ n -= d;
+ r->rlen = n;
+
+ /*
+ * If there was a check-condition, save the
+ * ccb for a possible request-sense command.
+ */
+ if(sdstat == SDcheck){
+ if(r->flags & SDnosense){
+ lock(&ctlr->cachelock);
+ if(ctlr->cache[target])
+ ccbfree(ctlr, ctlr->cache[target]);
+ ctlr->cache[target] = (Ccb*)ccb;
+ unlock(&ctlr->cachelock);
+ return SDcheck;
+ }
+ n = 8+ccb->sense[7];
+ if(n > sizeof(r->sense)-1)
+ n = sizeof(r->sense)-1;
+ memmove(r->sense, ccb->sense, n);
+ r->flags |= SDvalidsense;
+ }
+ ccbfree(ctlr, (Ccb*)ccb);
+
+ if(btstat){
+ if(btstat == 0x11)
+ return SDtimeout;
+ return SDeio;
+ }
+ return sdstat;
+}
+
+static void
+mylex32interrupt(Ureg*, void* arg)
+{
+ ulong pa;
+ Ctlr *ctlr;
+ Ccb32 *ccb;
+ Mbox32 *mb, *mbox;
+ int port, rinterrupt, rstatus;
+
+ ctlr = arg;
+ port = ctlr->port;
+
+ /*
+ * Save and clear the interrupt(s). The only
+ * interrupts expected are Cmdc, which is ignored,
+ * and Imbl which means something completed.
+ * There's one spurious interrupt left over from
+ * initialisation, ignore it.
+ */
+ rinterrupt = inb(port+Rinterrupt);
+ rstatus = inb(port+Rstatus);
+ outb(port+Rcontrol, Rint);
+ if((rinterrupt & ~(Cmdc|Imbl)) != Intv && ctlr->spurious++)
+ print("%s: interrupt 0x%2.2ux\n",
+ "sdmylex"/*ctlr->sdev->name*/, rinterrupt);
+ if((rinterrupt & Cmdc) && (rstatus & Cmdinv))
+ print("%s: command invalid\n", "sdmylex"/*ctlr->sdev->name*/);
+
+ /*
+ * Look for something in the mail.
+ * If there is, free the mailbox and wakeup whoever.
+ */
+ mb = ctlr->mb;
+ for(mbox = &mb[ctlr->mbix]; mbox->code; mbox = &mb[ctlr->mbix]){
+ pa = (mbox->ccb[3]<<24)
+ |(mbox->ccb[2]<<16)
+ |(mbox->ccb[1]<<8)
+ |mbox->ccb[0];
+ if(ctlr->pcidev)
+ ccb = BPA2K(pa, ctlr->pcidev->tbdf);
+ else
+ ccb = BPA2K(pa, BUSUNKNOWN);
+ mbox->code = 0;
+ ccb->done = 1;
+ wakeup(ccb);
+
+ ctlr->mbix++;
+ if(ctlr->mbix >= NMbox+NMbox)
+ ctlr->mbix = NMbox;
+ }
+}
+
+static int
+mylexrio(SDreq* r)
+{
+ int subno;
+ Ctlr *ctlr;
+
+ subno = r->unit->subno;
+ ctlr = r->unit->dev->ctlr;
+ if(subno == ctlr->id || (!ctlr->wide && subno >= 8))
+ r->status = SDtimeout;
+ else if(ctlr->bus == 24)
+ r->status = mylex24rio(r);
+ else
+ r->status = mylex32rio(r);
+ return r->status;
+}
+
+/*
+ * Issue a command to a controller. The command and its length is
+ * contained in cmd and cmdlen. If any data is to be
+ * returned, datalen should be non-zero, and the returned data
+ * will be placed in data.
+ * If Cmdc is set, bail out, the invalid command will be handled
+ * when the interrupt is processed.
+ */
+static void
+issueio(int port, uchar* cmd, int cmdlen, uchar* data, int datalen)
+{
+ int len;
+
+ if(cmd[0] != Cstart && cmd[0] != Ceombri){
+ while(!(inb(port+Rstatus) & Hardy))
+ ;
+ }
+ outb(port+Rcpr, cmd[0]);
+
+ len = 1;
+ while(len < cmdlen){
+ if(!(inb(port+Rstatus) & Cprbsy)){
+ outb(port+Rcpr, cmd[len]);
+ len++;
+ }
+ if(inb(port+Rinterrupt) & Cmdc)
+ return;
+ }
+
+ if(datalen){
+ len = 0;
+ while(len < datalen){
+ if(inb(port+Rstatus) & Dirrdy){
+ data[len] = inb(port+Rdatain);
+ len++;
+ }
+ if(inb(port+Rinterrupt) & Cmdc)
+ return;
+ }
+ }
+}
+
+/*
+ * Issue a command to a controller, wait for it to complete then
+ * try to reset the interrupt. Should only be called at initialisation.
+ */
+static int
+issue(Ctlr* ctlr, uchar* cmd, int cmdlen, uchar* data, int datalen)
+{
+ int port;
+ uchar rinterrupt, rstatus;
+ static Lock mylexissuelock;
+
+ port = ctlr->port;
+
+ ilock(&ctlr->issuelock);
+ issueio(port, cmd, cmdlen, data, datalen);
+
+ while(!((rinterrupt = inb(port+Rinterrupt)) & Cmdc))
+ ;
+
+ rstatus = inb(port+Rstatus);
+ outb(port+Rcontrol, Rint);
+ iunlock(&ctlr->issuelock);
+
+ if((rinterrupt & Cmdc) && (rstatus & Cmdinv))
+ return 0;
+ return 1;
+}
+
+static SDev*
+mylexprobe(int port, int irq)
+{
+ SDev *sdev;
+ Ctlr *ctlr;
+ uchar cmd[6], data[256];
+ int clen, dlen, timeo;
+
+ if(ioalloc(port, 0x3, 0, "mylex") < 0)
+ return nil;
+ ctlr = nil;
+
+ /*
+ * Attempt to hard-reset the board and reset
+ * the SCSI bus. If the board state doesn't settle to
+ * idle with mailbox initialisation required, either
+ * it isn't a compatible board or it's broken.
+ * If the controller has SCAM set this can take a while.
+ */
+ if(getconf("*noscsireset") != nil)
+ outb(port+Rcontrol, Rhard);
+ else
+ outb(port+Rcontrol, Rhard|Rsbus);
+ for(timeo = 0; timeo < 100; timeo++){
+ if(inb(port+Rstatus) == (Inreq|Hardy))
+ break;
+ delay(100);
+ }
+ if(inb(port+Rstatus) != (Inreq|Hardy)){
+buggery:
+ if(ctlr != nil)
+ free(ctlr);
+ iofree(port);
+ return nil;
+ }
+
+ if((ctlr = malloc(sizeof(Ctlr))) == nil)
+ goto buggery;
+ ctlr->port = port;
+ ctlr->irq = irq;
+ ctlr->bus = 24;
+ ctlr->wide = 0;
+
+ /*
+ * Try to determine if this is a 32-bit MultiMaster controller
+ * by attempting to obtain the extended inquiry information;
+ * this command is not implemented on Adaptec 154xx
+ * controllers. If successful, the first byte of the returned
+ * data is the host adapter bus type, 'E' for 32-bit EISA,
+ * PCI and VLB buses.
+ */
+ cmd[0] = Ciesi;
+ cmd[1] = 4;
+ clen = 2;
+ dlen = 256;
+ if(issue(ctlr, cmd, clen, data, dlen)){
+ if(data[0] == 'E')
+ ctlr->bus = 32;
+ ctlr->wide = data[0x0D] & 0x01;
+ }
+ else{
+ /*
+ * Inconceivable though it may seem, a hard controller reset
+ * is necessary here to clear out the command queue. Every
+ * board seems to lock-up in a different way if you give an
+ * invalid command and then try to clear out the
+ * command/parameter and/or data-in register.
+ * Soft reset doesn't do the job either. Fortunately no
+ * serious initialisation has been done yet so there's nothing
+ * to tidy up.
+ */
+ outb(port+Rcontrol, Rhard);
+ for(timeo = 0; timeo < 100; timeo++){
+ if(inb(port+Rstatus) == (Inreq|Hardy))
+ break;
+ delay(100);
+ }
+ if(inb(port+Rstatus) != (Inreq|Hardy))
+ goto buggery;
+ }
+
+ /*
+ * If the BIOS is enabled on the AHA-1542C/CF and BIOS options for
+ * support of drives > 1Gb, dynamic scanning of the SCSI bus or more
+ * than 2 drives under DOS 5.0 are enabled, the BIOS disables
+ * accepting Cmbinit to protect against running with drivers which
+ * don't support those options. In order to unlock the interface it
+ * is necessary to read a lock-code using Cextbios and write it back
+ * using Cmbienable; the lock-code is non-zero.
+ */
+ cmd[0] = Cinquiry;
+ clen = 1;
+ dlen = 4;
+ if(issue(ctlr, cmd, clen, data, dlen) == 0)
+ goto buggery;
+ if(data[0] >= 0x43){
+ cmd[0] = Cextbios;
+ clen = 1;
+ dlen = 2;
+ if(issue(ctlr, cmd, clen, data, dlen) == 0)
+ goto buggery;
+
+ /*
+ * Lock-code returned in data[1]. If it's non-zero write
+ * it back along with bit 0 of byte 0 cleared to enable
+ * mailbox initialisation.
+ */
+ if(data[1]){
+ cmd[0] = Cmbienable;
+ cmd[1] = 0;
+ cmd[2] = data[1];
+ clen = 3;
+ if(issue(ctlr, cmd, clen, 0, 0) == 0)
+ goto buggery;
+ }
+ }
+
+ /*
+ * Get the id, DMA and IRQ info from the board. This will
+ * cause an interrupt which will hopefully not cause any
+ * trouble because the interrupt number isn't known yet.
+ * This is necessary as the DMA won't be set up if the
+ * board has the BIOS disabled.
+ *
+ * If the IRQ is already known, this must be a 32-bit PCI
+ * or EISA card, in which case the returned DMA and IRQ can
+ * be ignored.
+ */
+ cmd[0] = Cinquire;
+ clen = 1;
+ dlen = 3;
+ if(issue(ctlr, cmd, clen, data, dlen) == 0)
+ goto buggery;
+
+ ctlr->id = data[2] & 0x07;
+ if(ctlr->irq < 0){
+ switch(data[0]){ /* DMA Arbitration Priority */
+ case 0x80: /* Channel 7 */
+ outb(0xD6, 0xC3);
+ outb(0xD4, 0x03);
+ break;
+ case 0x40: /* Channel 6 */
+ outb(0xD6, 0xC2);
+ outb(0xD4, 0x02);
+ break;
+ case 0x20: /* Channel 5 */
+ outb(0xD6, 0xC1);
+ outb(0xD4, 0x01);
+ break;
+ case 0x01: /* Channel 0 */
+ outb(0x0B, 0xC0);
+ outb(0x0A, 0x00);
+ break;
+ default:
+ if(ctlr->bus == 24)
+ goto buggery;
+ break;
+ }
+
+ switch(data[1]){ /* Interrupt Channel */
+ case 0x40:
+ ctlr->irq = 15;
+ break;
+ case 0x20:
+ ctlr->irq = 14;
+ break;
+ case 0x08:
+ ctlr->irq = 12;
+ break;
+ case 0x04:
+ ctlr->irq = 11;
+ break;
+ case 0x02:
+ ctlr->irq = 10;
+ break;
+ case 0x01:
+ ctlr->irq = 9;
+ break;
+ default:
+ goto buggery;
+ }
+ }
+
+ if((sdev = malloc(sizeof(SDev))) == nil)
+ goto buggery;
+ sdev->ifc = &sdmylexifc;
+ sdev->ctlr = ctlr;
+ ctlr->sdev = sdev;
+ if(!ctlr->wide)
+ sdev->nunit = 8;
+ else
+ sdev->nunit = 16;
+
+ return sdev;
+}
+
+static int mylexport[8] = {
+ 0x330, 0x334, 0x230, 0x234, 0x130, 0x134, 0x000, 0x000,
+};
+
+static SDev*
+mylexpnp(void)
+{
+ Pcidev *p;
+ Ctlr *ctlr;
+ ISAConf isa;
+ int cfg, ctlrno, i, x;
+ SDev *sdev, *head, *tail;
+
+ p = nil;
+ head = tail = nil;
+ while(p = pcimatch(p, 0x104B, 0)){
+ if((sdev = mylexprobe(p->mem[0].bar & ~0x01, p->intl)) == nil)
+ continue;
+
+ ctlr = sdev->ctlr;
+ ctlr->pcidev = p;
+
+ if(head != nil)
+ tail->next = sdev;
+ else
+ head = sdev;
+ tail = sdev;
+ }
+
+ if(strncmp(KADDR(0xFFFD9), "EISA", 4) == 0){
+ for(cfg = 0x1000; cfg < MaxEISA*0x1000; cfg += 0x1000){
+ x = 0;
+ for(i = 0; i < 4; i++)
+ x |= inb(cfg+CfgEISA+i)<<(i*8);
+ if(x != 0x0142B30A && x != 0x0242B30A)
+ continue;
+
+ x = inb(cfg+0xC8C);
+ if((sdev = mylexprobe(mylexport[x & 0x07], -1)) == nil)
+ continue;
+
+ if(head != nil)
+ tail->next = sdev;
+ else
+ head = sdev;
+ tail = sdev;
+ }
+ }
+
+ for(ctlrno = 0; ctlrno < 4; ctlrno++){
+ memset(&isa, 0, sizeof(isa));
+ if(!isaconfig("scsi", ctlrno, &isa))
+ continue;
+ if(strcmp(isa.type, "aha1542"))
+ continue;
+ if((sdev = mylexprobe(isa.port, -1)) == nil)
+ continue;
+
+ if(head != nil)
+ tail->next = sdev;
+ else
+ head = sdev;
+ tail = sdev;
+ }
+
+ return head;
+}
+
+static SDev*
+mylexid(SDev* sdev)
+{
+ return scsiid(sdev, &sdmylexifc);
+}
+
+static int
+mylex24enable(Ctlr* ctlr)
+{
+ ulong p;
+ Ccb24 *ccb, *ccbp;
+ uchar cmd[6], *v;
+ int len;
+
+ len = (sizeof(Mbox24)*NMbox*2)+(sizeof(Ccb24)*NCcb);
+ v = xspanalloc(len, 32, 0);
+
+ if(!PADDR24(ctlr, sizeof(Ctlr)) || !PADDR24(v, len))
+ return 0;
+
+ ctlr->mb = v;
+ v += sizeof(Mbox24)*NMbox*2;
+
+ ccb = (Ccb24*)v;
+ for(ccbp = ccb; ccbp < &ccb[NCcb]; ccbp++){
+ ccbp->ccb = ctlr->ccb;
+ ctlr->ccb = (Ccb*)ccbp;
+ }
+
+ /*
+ * Initialise the software controller and
+ * set the board scanning the mailboxes.
+ */
+ ctlr->mbix = NMbox;
+
+ cmd[0] = Cinitialise;
+ cmd[1] = NMbox;
+ p = K2BPA(ctlr->mb, BUSUNKNOWN);
+ cmd[2] = p>>16;
+ cmd[3] = p>>8;
+ cmd[4] = p;
+
+ return issue(ctlr, cmd, 5, 0, 0);
+}
+
+static int
+mylex32enable(Ctlr* ctlr)
+{
+ ulong p;
+ Ccb32 *ccb, *ccbp;
+ uchar cmd[6], *v;
+
+ v = xspanalloc((sizeof(Mbox32)*NMbox*2)+(sizeof(Ccb32)*NCcb), 32, 0);
+
+ ctlr->mb = v;
+ v += sizeof(Mbox32)*NMbox*2;
+
+ ccb = (Ccb32*)v;
+ for(ccbp = ccb; ccbp < &ccb[NCcb]; ccbp++){
+ /*
+ * Fill in some stuff that doesn't change.
+ */
+ ccbp->senselen = sizeof(ccbp->sense);
+ p = PADDR(ccbp->sense);
+ ccbp->senseptr[0] = p;
+ ccbp->senseptr[1] = p>>8;
+ ccbp->senseptr[2] = p>>16;
+ ccbp->senseptr[3] = p>>24;
+
+ ccbp->ccb = ctlr->ccb;
+ ctlr->ccb = (Ccb*)ccbp;
+ }
+
+ /*
+ * Attempt wide mode setup.
+ */
+ if(ctlr->wide){
+ cmd[0] = Cwide;
+ cmd[1] = 1;
+ if(!issue(ctlr, cmd, 2, 0, 0))
+ ctlr->wide = 0;
+ }
+
+ /*
+ * Initialise the software controller and
+ * set the board scanning the mailboxes.
+ */
+ ctlr->mbix = NMbox;
+
+ cmd[0] = Ciem;
+ cmd[1] = NMbox;
+ if(ctlr->pcidev)
+ p = K2BPA(ctlr->mb, ctlr->tbdf);
+ else
+ p = K2BPA(ctlr->mb, BUSUNKNOWN);
+ cmd[2] = p;
+ cmd[3] = p>>8;
+ cmd[4] = p>>16;
+ cmd[5] = p>>24;
+
+ return issue(ctlr, cmd, 6, 0, 0);
+}
+
+static int
+mylexenable(SDev* sdev)
+{
+ int tbdf;
+ Ctlr *ctlr;
+ void (*interrupt)(Ureg*, void*);
+ char name[NAMELEN];
+
+ ctlr = sdev->ctlr;
+ if(ctlr->cache == nil){
+ if((ctlr->cache = malloc(sdev->nunit*sizeof(Ccb*))) == nil)
+ return 0;
+ }
+
+ tbdf = BUSUNKNOWN;
+ if(ctlr->bus == 32){
+ if(ctlr->pcidev){
+ tbdf = ctlr->pcidev->tbdf;
+ pcisetbme(ctlr->pcidev);
+ }
+ if(!mylex32enable(ctlr))
+ return 0;
+ interrupt = mylex32interrupt;
+ }
+ else if(mylex24enable(ctlr))
+ interrupt = mylex24interrupt;
+ else
+ return 0;
+
+ snprint(name, NAMELEN, "sd%c (%s)", sdev->idno, sdev->ifc->name);
+ intrenable(ctlr->irq, interrupt, ctlr, tbdf, name);
+
+ return 1;
+}
+
+static int
+mylexdisable(SDev* sdev)
+{
+ Ctlr *ctlr;
+ int port, timeo;
+
+ ctlr = sdev->ctlr;
+ port = ctlr->port;
+
+ if(getconf("*noscsireset") != nil)
+ outb(port+Rcontrol, Rhard);
+ else
+ outb(port+Rcontrol, Rhard|Rsbus);
+ for(timeo = 0; timeo < 100; timeo++){
+ if(inb(port+Rstatus) == (Inreq|Hardy))
+ break;
+ delay(100);
+ }
+ if(inb(port+Rstatus) != (Inreq|Hardy))
+ return 0;
+
+ return 1;
+}
+
+SDifc sdmylexifc = {
+ "mylex", /* name */
+
+ mylexpnp, /* pnp */
+ nil, /* legacy */
+ mylexid, /* id */
+ mylexenable, /* enable */
+ mylexdisable, /* disable */
+
+ scsiverify, /* verify */
+ scsionline, /* online */
+ mylexrio, /* rio */
+ nil, /* rctl */
+ nil, /* wctl */
+
+ scsibio, /* bio */
+};
diff --git a/os/boot/pc/sdscsi.c b/os/boot/pc/sdscsi.c
new file mode 100644
index 00000000..ff8668fe
--- /dev/null
+++ b/os/boot/pc/sdscsi.c
@@ -0,0 +1,376 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "error.h"
+
+#include "sd.h"
+
+static int
+scsitest(SDreq* r)
+{
+ r->write = 0;
+ memset(r->cmd, 0, sizeof(r->cmd));
+ r->cmd[1] = r->lun<<5;
+ r->clen = 6;
+ r->data = nil;
+ r->dlen = 0;
+ r->flags = 0;
+
+ r->status = ~0;
+
+// cgascreenputs("A", 1);
+ return r->unit->dev->ifc->rio(r);
+}
+
+int
+scsiverify(SDunit* unit)
+{
+ static SDreq *r;
+ int i, status;
+ static uchar *inquiry;
+
+ if((r = sdmalloc(r, sizeof(SDreq))) == nil)
+ return 0;
+
+ if((inquiry = sdmalloc(inquiry, sizeof(unit->inquiry))) == nil)
+ return 0;
+
+ r->unit = unit;
+ r->lun = 0; /* ??? */
+
+ memset(unit->inquiry, 0, sizeof(unit->inquiry));
+ r->write = 0;
+ r->cmd[0] = 0x12;
+ r->cmd[1] = r->lun<<5;
+ r->cmd[4] = sizeof(unit->inquiry)-1;
+ r->clen = 6;
+ r->data = inquiry;
+ r->dlen = sizeof(unit->inquiry)-1;
+ r->flags = 0;
+
+ r->status = ~0;
+// cgascreenputs("B", 1);
+ if(unit->dev->ifc->rio(r) != SDok){
+ return 0;
+ }
+ memmove(unit->inquiry, inquiry, r->dlen);
+
+ SET(status);
+ for(i = 0; i < 3; i++){
+ while((status = scsitest(r)) == SDbusy)
+ ;
+ if(status == SDok || status != SDcheck)
+ break;
+ if(!(r->flags & SDvalidsense))
+ break;
+ if((r->sense[2] & 0x0F) != 0x02)
+ continue;
+
+ /*
+ * Unit is 'not ready'.
+ * If it is in the process of becoming ready or needs
+ * an initialising command, set status so it will be spun-up
+ * below.
+ * If there's no medium, that's OK too, but don't
+ * try to spin it up.
+ */
+ if(r->sense[12] == 0x04){
+ if(r->sense[13] == 0x02 || r->sense[13] == 0x01){
+ status = SDok;
+ break;
+ }
+ }
+ if(r->sense[12] == 0x3A)
+ break;
+ }
+
+ if(status == SDok){
+ /*
+ * Try to ensure a direct-access device is spinning.
+ * Ignore the result.
+ */
+ if((unit->inquiry[0] & 0x1F) == 0){
+ memset(r->cmd, 0, sizeof(r->cmd));
+ r->write = 0;
+ r->cmd[0] = 0x1B;
+ r->cmd[1] = r->lun<<5;
+ r->cmd[4] = 1;
+ r->clen = 6;
+ r->data = nil;
+ r->dlen = 0;
+ r->flags = 0;
+
+ r->status = ~0;
+ unit->dev->ifc->rio(r);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+int
+return0(void*)
+{
+ return 0;
+}
+
+static int
+scsirio(SDreq* r)
+{
+ /*
+ * Perform an I/O request, returning
+ * -1 failure
+ * 0 ok
+ * 2 retry
+ * The contents of r may be altered so the
+ * caller should re-initialise if necesary.
+ */
+ r->status = ~0;
+// cgascreenputs("C", 1);
+ switch(r->unit->dev->ifc->rio(r)){
+ default:
+ return -1;
+ case SDcheck:
+ if(!(r->flags & SDvalidsense))
+ return -1;
+ switch(r->sense[2] & 0x0F){
+ case 0x00: /* no sense */
+ case 0x01: /* recovered error */
+ return 2;
+ case 0x06: /* check condition */
+ /*
+ * 0x28 - not ready to ready transition,
+ * medium may have changed.
+ * 0x29 - power on or some type of reset.
+ */
+ if(r->sense[12] == 0x28 && r->sense[13] == 0)
+ return 2;
+ if(r->sense[12] == 0x29)
+ return 2;
+ return -1;
+ case 0x02: /* not ready */
+ /*
+ * If no medium present, bail out.
+ * If unit is becoming ready, rather than not
+ * not ready, wait a little then poke it again. */
+ if(r->sense[12] == 0x3A)
+ return -1;
+ if(r->sense[12] != 0x04 || r->sense[13] != 0x01)
+ return -1;
+
+ tsleep(nil, return0, 0, 500);
+ scsitest(r);
+ return 2;
+ default:
+ return -1;
+ }
+ return -1;
+ case SDok:
+ return 0;
+ }
+ return -1;
+}
+
+int
+scsionline(SDunit* unit)
+{
+ int ok;
+ static SDreq *r;
+ static uchar *p;
+
+ if((r = sdmalloc(r, sizeof(SDreq))) == nil)
+ return 0;
+
+ if((p = sdmalloc(p, 8)) == nil)
+ return 0;
+
+ ok = 0;
+
+ r->unit = unit;
+ r->lun = 0; /* ??? */
+ for(;;){
+ /*
+ * Read-capacity is mandatory for DA, WORM, CD-ROM and
+ * MO. It may return 'not ready' if type DA is not
+ * spun up, type MO or type CD-ROM are not loaded or just
+ * plain slow getting their act together after a reset.
+ */
+ r->write = 0;
+ memset(r->cmd, 0, sizeof(r->cmd));
+ r->cmd[0] = 0x25;
+ r->cmd[1] = r->lun<<5;
+ r->clen = 10;
+ r->data = p;
+ r->dlen = 8;
+ r->flags = 0;
+
+ r->status = ~0;
+// cgascreenputs("F", 1);
+ switch(scsirio(r)){
+ default:
+ break;
+ case 0:
+ unit->sectors = (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3];
+ /*
+ * Read-capacity returns the LBA of the last sector,
+ * therefore the number of sectors must be incremented.
+ */
+ unit->sectors++;
+ unit->secsize = (p[4]<<24)|(p[5]<<16)|(p[6]<<8)|p[7];
+ ok = 1;
+ break;
+ case 2:
+ continue;
+ }
+ break;
+ }
+
+ return ok;
+}
+
+int
+scsiexec(SDunit* unit, int write, uchar* cmd, int clen, void* data, int* dlen)
+{
+ static SDreq *r;
+ int status;
+
+ if((r = sdmalloc(r, sizeof(SDreq))) == nil)
+ return SDmalloc;
+
+ r->unit = unit;
+ r->lun = cmd[1]>>5; /* ??? */
+ r->write = write;
+ memmove(r->cmd, cmd, clen);
+ r->clen = clen;
+ r->data = data;
+ if(dlen)
+ r->dlen = *dlen;
+ r->flags = 0;
+
+ r->status = ~0;
+
+ /*
+ * Call the device-specific I/O routine.
+ * There should be no calls to 'error()' below this
+ * which percolate back up.
+ */
+// cgascreenputs("D", 1);
+ switch(status = unit->dev->ifc->rio(r)){
+ case SDok:
+ if(dlen)
+ *dlen = r->rlen;
+ /*FALLTHROUGH*/
+ case SDcheck:
+ /*FALLTHROUGH*/
+ default:
+ /*
+ * It's more complicated than this. There are conditions
+ * which are 'ok' but for which the returned status code
+ * is not 'SDok'.
+ * Also, not all conditions require a reqsense, might
+ * need to do a reqsense here and make it available to the
+ * caller somehow.
+ *
+ * MaƱana.
+ */
+ break;
+ }
+
+ return status;
+}
+
+long
+scsibio(SDunit* unit, int lun, int write, void* data, long nb, long bno)
+{
+ static SDreq *r;
+ long rlen;
+
+ if((r = sdmalloc(r, sizeof(SDreq))) == nil)
+ return SDmalloc;
+
+ r->unit = unit;
+ r->lun = lun;
+again:
+ r->write = write;
+ if(write == 0)
+ r->cmd[0] = 0x28;
+ else
+ r->cmd[0] = 0x2A;
+ r->cmd[1] = (lun<<5);
+ r->cmd[2] = bno>>24;
+ r->cmd[3] = bno>>16;
+ r->cmd[4] = bno>>8;
+ r->cmd[5] = bno;
+ r->cmd[6] = 0;
+ r->cmd[7] = nb>>8;
+ r->cmd[8] = nb;
+ r->cmd[9] = 0;
+ r->clen = 10;
+ r->data = data;
+ r->dlen = nb*unit->secsize;
+ r->flags = 0;
+
+ r->status = ~0;
+// cgascreenputs("E", 1);
+ switch(scsirio(r)){
+ default:
+ rlen = -1;
+ break;
+ case 0:
+ rlen = r->rlen;
+ break;
+ case 2:
+ rlen = -1;
+ if(!(r->flags & SDvalidsense))
+ break;
+ switch(r->sense[2] & 0x0F){
+ default:
+ break;
+ case 0x06: /* check condition */
+ /*
+ * Check for a removeable media change.
+ * If so, mark it and zap the geometry info
+ * to force an online request.
+ */
+ if(r->sense[12] != 0x28 || r->sense[13] != 0)
+ break;
+ if(unit->inquiry[1] & 0x80){
+ unit->sectors = 0;
+ }
+ break;
+ case 0x02: /* not ready */
+ /*
+ * If unit is becoming ready,
+ * rather than not not ready, try again.
+ */
+ if(r->sense[12] == 0x04 && r->sense[13] == 0x01)
+ goto again;
+ break;
+ }
+ break;
+ }
+
+ return rlen;
+}
+
+SDev*
+scsiid(SDev* sdev, SDifc* ifc)
+{
+ static char idno[16] = "0123456789abcdef";
+ static char *p = idno;
+
+ while(sdev){
+ if(sdev->ifc == ifc){
+ sdev->idno = *p++;
+ if(p >= &idno[sizeof(idno)])
+ break;
+ }
+ sdev = sdev->next;
+ }
+
+ return nil;
+}
diff --git a/os/boot/pc/trap.c b/os/boot/pc/trap.c
new file mode 100644
index 00000000..503e03f4
--- /dev/null
+++ b/os/boot/pc/trap.c
@@ -0,0 +1,331 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+
+void intr0(void), intr1(void), intr2(void), intr3(void);
+void intr4(void), intr5(void), intr6(void), intr7(void);
+void intr8(void), intr9(void), intr10(void), intr11(void);
+void intr12(void), intr13(void), intr14(void), intr15(void);
+void intr16(void);
+void intr24(void), intr25(void), intr26(void), intr27(void);
+void intr28(void), intr29(void), intr30(void), intr31(void);
+void intr32(void), intr33(void), intr34(void), intr35(void);
+void intr36(void), intr37(void), intr38(void), intr39(void);
+void intr64(void);
+void intrbad(void);
+
+/*
+ * 8259 interrupt controllers
+ */
+enum
+{
+ Int0ctl= 0x20, /* control port (ICW1, OCW2, OCW3) */
+ Int0aux= 0x21, /* everything else (ICW2, ICW3, ICW4, OCW1) */
+ Int1ctl= 0xA0, /* control port */
+ Int1aux= 0xA1, /* everything else (ICW2, ICW3, ICW4, OCW1) */
+
+ Icw1= 0x10, /* select bit in ctl register */
+ Ocw2= 0x00,
+ Ocw3= 0x08,
+
+ EOI= 0x20, /* non-specific end of interrupt */
+
+ Elcr1= 0x4D0, /* Edge/Level Triggered Register */
+ Elcr2= 0x4D1,
+};
+
+int int0mask = 0xff; /* interrupts enabled for first 8259 */
+int int1mask = 0xff; /* interrupts enabled for second 8259 */
+int i8259elcr; /* mask of level-triggered interrupts */
+
+/*
+ * trap/interrupt gates
+ */
+Segdesc ilt[256];
+
+enum
+{
+ Maxhandler= 32, /* max number of interrupt handlers */
+};
+
+typedef struct Handler Handler;
+struct Handler
+{
+ void (*r)(Ureg*, void*);
+ void *arg;
+ Handler *next;
+};
+
+struct
+{
+ Handler *ivec[256];
+ Handler h[Maxhandler];
+ int nextfree;
+} halloc;
+
+void
+sethvec(int v, void (*r)(void), int type, int pri)
+{
+ ilt[v].d0 = ((ulong)r)&0xFFFF|(KESEL<<16);
+ ilt[v].d1 = ((ulong)r)&0xFFFF0000|SEGP|SEGPL(pri)|type;
+}
+
+void
+setvec(int v, void (*r)(Ureg*, void*), void *arg)
+{
+ Handler *h;
+
+ if(halloc.nextfree >= Maxhandler)
+ panic("out of interrupt handlers");
+ h = &halloc.h[halloc.nextfree++];
+ h->next = halloc.ivec[v];
+ h->r = r;
+ h->arg = arg;
+ halloc.ivec[v] = h;
+
+ /*
+ * enable corresponding interrupt in 8259
+ */
+ if((v&~0x7) == VectorPIC){
+ int0mask &= ~(1<<(v&7));
+ outb(Int0aux, int0mask);
+ } else if((v&~0x7) == VectorPIC+8){
+ int1mask &= ~(1<<(v&7));
+ outb(Int1aux, int1mask);
+ }
+}
+
+void
+trapdisable(void)
+{
+ outb(Int0aux, 0xFF);
+ outb(Int1aux, 0xFF);
+}
+
+void
+trapenable(void)
+{
+ outb(Int0aux, int0mask);
+ outb(Int1aux, int1mask);
+}
+
+
+/*
+ * set up the interrupt/trap gates
+ */
+void
+trapinit(void)
+{
+ int i, x;
+
+ /*
+ * set all interrupts to panics
+ */
+ for(i = 0; i < 256; i++)
+ sethvec(i, intrbad, SEGTG, 0);
+
+ /*
+ * 80386 processor (and coprocessor) traps
+ */
+ sethvec(0, intr0, SEGTG, 0);
+ sethvec(1, intr1, SEGTG, 0);
+ sethvec(2, intr2, SEGTG, 0);
+ sethvec(3, intr3, SEGTG, 0);
+ sethvec(4, intr4, SEGTG, 0);
+ sethvec(5, intr5, SEGTG, 0);
+ sethvec(6, intr6, SEGTG, 0);
+ sethvec(7, intr7, SEGTG, 0);
+ sethvec(8, intr8, SEGTG, 0);
+ sethvec(9, intr9, SEGTG, 0);
+ sethvec(10, intr10, SEGTG, 0);
+ sethvec(11, intr11, SEGTG, 0);
+ sethvec(12, intr12, SEGTG, 0);
+ sethvec(13, intr13, SEGTG, 0);
+ sethvec(14, intr14, SEGTG, 0);
+ sethvec(15, intr15, SEGTG, 0);
+ sethvec(16, intr16, SEGTG, 0);
+
+ /*
+ * device interrupts
+ */
+ sethvec(24, intr24, SEGIG, 0);
+ sethvec(25, intr25, SEGIG, 0);
+ sethvec(26, intr26, SEGIG, 0);
+ sethvec(27, intr27, SEGIG, 0);
+ sethvec(28, intr28, SEGIG, 0);
+ sethvec(29, intr29, SEGIG, 0);
+ sethvec(30, intr30, SEGIG, 0);
+ sethvec(31, intr31, SEGIG, 0);
+ sethvec(32, intr32, SEGIG, 0);
+ sethvec(33, intr33, SEGIG, 0);
+ sethvec(34, intr34, SEGIG, 0);
+ sethvec(35, intr35, SEGIG, 0);
+ sethvec(36, intr36, SEGIG, 0);
+ sethvec(37, intr37, SEGIG, 0);
+ sethvec(38, intr38, SEGIG, 0);
+ sethvec(39, intr39, SEGIG, 0);
+
+ /*
+ * tell the hardware where the table is (and how long)
+ */
+ putidt(ilt, sizeof(ilt)-1);
+
+ /*
+ * Set up the first 8259 interrupt processor.
+ * Make 8259 interrupts start at CPU vector VectorPIC.
+ * Set the 8259 as master with edge triggered
+ * input with fully nested interrupts.
+ */
+ outb(Int0ctl, Icw1|0x01); /* ICW1 - edge triggered, master,
+ ICW4 will be sent */
+ outb(Int0aux, VectorPIC); /* ICW2 - interrupt vector offset */
+ outb(Int0aux, 0x04); /* ICW3 - have slave on level 2 */
+ outb(Int0aux, 0x01); /* ICW4 - 8086 mode, not buffered */
+
+ /*
+ * Set up the second 8259 interrupt processor.
+ * Make 8259 interrupts start at CPU vector VectorPIC+8.
+ * Set the 8259 as master with edge triggered
+ * input with fully nested interrupts.
+ */
+ outb(Int1ctl, Icw1|0x01); /* ICW1 - edge triggered, master,
+ ICW4 will be sent */
+ outb(Int1aux, VectorPIC+8); /* ICW2 - interrupt vector offset */
+ outb(Int1aux, 0x02); /* ICW3 - I am a slave on level 2 */
+ outb(Int1aux, 0x01); /* ICW4 - 8086 mode, not buffered */
+ outb(Int1aux, int1mask);
+
+ /*
+ * pass #2 8259 interrupts to #1
+ */
+ int0mask &= ~0x04;
+ outb(Int0aux, int0mask);
+
+ /*
+ * Set Ocw3 to return the ISR when ctl read.
+ */
+ outb(Int0ctl, Ocw3|0x03);
+ outb(Int1ctl, Ocw3|0x03);
+
+ /*
+ * Check for Edge/Level register.
+ * This check may not work for all chipsets.
+ * First try a non-intrusive test - the bits for
+ * IRQs 13, 8, 2, 1 and 0 must be edge (0). If
+ * that's OK try a R/W test.
+ */
+ x = (inb(Elcr2)<<8)|inb(Elcr1);
+ if(!(x & 0x2107)){
+ outb(Elcr1, 0);
+ if(inb(Elcr1) == 0){
+ outb(Elcr1, 0x20);
+ if(inb(Elcr1) == 0x20)
+ i8259elcr = x;
+ outb(Elcr1, x & 0xFF);
+ print("ELCR: %4.4uX\n", i8259elcr);
+ }
+ }
+}
+
+/*
+ * dump registers
+ */
+static void
+dumpregs(Ureg *ur)
+{
+ print("FLAGS=%lux TRAP=%lux ECODE=%lux PC=%lux\n",
+ ur->flags, ur->trap, ur->ecode, ur->pc);
+ print(" AX %8.8lux BX %8.8lux CX %8.8lux DX %8.8lux\n",
+ ur->ax, ur->bx, ur->cx, ur->dx);
+ print(" SI %8.8lux DI %8.8lux BP %8.8lux\n",
+ ur->si, ur->di, ur->bp);
+ print(" CS %4.4lux DS %4.4lux ES %4.4lux FS %4.4lux GS %4.4lux\n",
+ ur->cs & 0xFF, ur->ds & 0xFFFF, ur->es & 0xFFFF, ur->fs & 0xFFFF, ur->gs & 0xFFFF);
+ print(" CR0 %8.8lux CR2 %8.8lux CR3 %8.8lux\n",
+ getcr0(), getcr2(), getcr3());
+}
+
+/*
+ * All traps
+ */
+void
+trap(Ureg *ur)
+{
+ int v;
+ int c;
+ Handler *h;
+ ushort isr;
+
+ v = ur->trap;
+ /*
+ * tell the 8259 that we're done with the
+ * highest level interrupt (interrupts are still
+ * off at this point)
+ */
+ c = v&~0x7;
+ isr = 0;
+ if(c==VectorPIC || c==VectorPIC+8){
+ isr = inb(Int0ctl);
+ outb(Int0ctl, EOI);
+ if(c == VectorPIC+8){
+ isr |= inb(Int1ctl)<<8;
+ outb(Int1ctl, EOI);
+ }
+ }
+
+ if(v>=256 || (h = halloc.ivec[v]) == 0){
+ if(v >= VectorPIC && v < VectorPIC+16){
+ v -= VectorPIC;
+ /*
+ * Check for a default IRQ7. This can happen when
+ * the IRQ input goes away before the acknowledge.
+ * In this case, a 'default IRQ7' is generated, but
+ * the corresponding bit in the ISR isn't set.
+ * In fact, just ignore all such interrupts.
+ */
+ if(isr & (1<<v))
+ print("unknown interrupt %d pc=0x%lux\n", v, ur->pc);
+ return;
+ }
+
+ switch(v){
+
+ case 0x02: /* NMI */
+ print("NMI: nmisc=0x%2.2ux, nmiertc=0x%2.2ux, nmiesc=0x%2.2ux\n",
+ inb(0x61), inb(0x70), inb(0x461));
+ return;
+
+ default:
+ dumpregs(ur);
+ panic("exception/interrupt %d", v);
+ return;
+ }
+ }
+
+ /*
+ * call the trap routines
+ */
+ do {
+ (*h->r)(ur, h->arg);
+ h = h->next;
+ } while(h);
+}
+
+void
+realmode(int intr, Ureg *ureg)
+{
+ extern void realmode0(void); /* in l.s */
+ extern int realmodeintr;
+ extern Ureg realmoderegs;
+
+ realmoderegs = *ureg;
+ realmodeintr = intr;
+ trapdisable();
+ realmode0();
+ trapenable();
+ *ureg = realmoderegs;
+}
diff --git a/os/boot/pc/ureg.h b/os/boot/pc/ureg.h
new file mode 100644
index 00000000..0d0d43f0
--- /dev/null
+++ b/os/boot/pc/ureg.h
@@ -0,0 +1,27 @@
+typedef struct Ureg Ureg;
+
+struct Ureg
+{
+ ulong di; /* general registers */
+ ulong si; /* ... */
+ ulong bp; /* ... */
+ ulong nsp;
+ ulong bx; /* ... */
+ ulong dx; /* ... */
+ ulong cx; /* ... */
+ ulong ax; /* ... */
+ ulong gs; /* data segments */
+ ulong fs; /* ... */
+ ulong es; /* ... */
+ ulong ds; /* ... */
+ ulong trap; /* trap type */
+ ulong ecode; /* error code (or zero) */
+ ulong pc; /* pc */
+ ulong cs; /* old context */
+ ulong flags; /* old flags */
+ union {
+ ulong usp;
+ ulong sp;
+ };
+ ulong ss; /* old stack segment */
+};
diff --git a/os/boot/pc/x16.h b/os/boot/pc/x16.h
new file mode 100644
index 00000000..9c204328
--- /dev/null
+++ b/os/boot/pc/x16.h
@@ -0,0 +1,159 @@
+/*
+ * Can't write 16-bit code for 8a without getting into
+ * lots of bother, so define some simple commands and
+ * output the code directly.
+ *
+ * N.B. CALL16(x) kills DI, so don't expect it to be
+ * saved across calls.
+ */
+#define rAX 0 /* rX */
+#define rCX 1
+#define rDX 2
+#define rBX 3
+#define rSP 4 /* SP */
+#define rBP 5 /* BP */
+#define rSI 6 /* SI */
+#define rDI 7 /* DI */
+
+#define rAL 0 /* rL */
+#define rCL 1
+#define rDL 2
+#define rBL 3
+#define rAH 4 /* rH */
+#define rCH 5
+#define rDH 6
+#define rBH 7
+
+#define rES 0 /* rS */
+#define rCS 1
+#define rSS 2
+#define rDS 3
+#define rFS 4
+#define rGS 5
+
+#define xSI 4 /* rI (index) */
+#define xDI 5
+#define xBP 6
+#define xBX 7
+
+#define rCR0 0 /* rC */
+#define rCR2 2
+#define rCR3 3
+#define rCR4 4
+
+#define OP(o, m, ro, rm) BYTE $o; /* op + modr/m byte */ \
+ BYTE $(((m)<<6)|((ro)<<3)|(rm))
+#define OPrm(o, r, m) OP(o, 0x00, r, 0x06); /* general r <-> m */ \
+ WORD $m;
+#define OPrr(o, r0, r1) OP(o, 0x03, r0, r1); /* general r -> r */
+
+#define LW(m, rX) OPrm(0x8B, rX, m) /* m -> rX */
+#define LXW(x, rI, r) OP(0x8B, 0x02, r, rI); /* x(rI) -> r */ \
+ WORD $x
+#define LBPW(x, r) OP(0x8B, 0x02, r, xBP); /* x(rBP) -> r */ \
+ WORD $x
+#define LB(m, rB) OPrm(0x8A, rB, m) /* m -> r[HL] */
+#define LXB(x, rI, r) OP(0x8A, 0x01, r, rI); /* x(rI) -> r */ \
+ BYTE $x
+#define LBPB(x, r) OP(0x8A, 0x01, r, xBP); /* x(rBP) -> r */ \
+ BYTE $x
+#define SW(rX, m) OPrm(0x89, rX, m) /* rX -> m */
+#define SXW(r, x, rI) OP(0x89, 0x02, r, rI); /* r -> x(rI) */ \
+ WORD $x
+#define SBPW(r, x) OP(0x89, 0x02, r, xBP); /* r -> x(rBP) */ \
+ WORD $(x)
+#define SBPWI(i, x) OP(0xC7, 0x01, 0, xBP); /* i -> x(rBP) */ \
+ BYTE $(x); WORD $(i)
+#define STB(rB, m) OPrm(0x88, rB, m) /* rB -> m */
+#define SXB(r, x, rI) OP(0x88, 0x01, r, rI); /* rB -> x(rI) */ \
+ BYTE $x
+#define SBPB(r, x) OP(0x88, 0x01, r, xBP); /* r -> x(rBP) */ \
+ BYTE $x
+#define SBPBI(i, x) OP(0xC6, 0x01, 0, xBP); /* i -> x(rBP) */ \
+ BYTE $(x); BYTE $(i)
+#define LWI(i, rX) BYTE $(0xB8+rX); /* i -> rX */ \
+ WORD $i;
+#define LBI(i, rB) BYTE $(0xB0+rB); /* i -> r[HL] */ \
+ BYTE $i
+
+#define MW(r0, r1) OPrr(0x89, r0, r1) /* r0 -> r1 */
+#define MFSR(rS, rX) OPrr(0x8C, rS, rX) /* rS -> rX */
+#define MTSR(rX, rS) OPrr(0x8E, rS, rX) /* rX -> rS */
+#define MFCR(rC, rX) BYTE $0x0F; /* rC -> rX */ \
+ OP(0x20, 0x03, rC, rX)
+#define MTCR(rX, rC) BYTE $0x0F; /* rX -> rC */ \
+ OP(0x22, 0x03, rC, rX)
+
+#define ADC(r0, r1) OPrr(0x11, r0, r1) /* r0 + r1 -> r1 */
+#define ADD(r0, r1) OPrr(0x01, r0, r1) /* r0 + r1 -> r1 */
+#define ADDI(i, r) OP(0x81, 0x03, 0x00, r);/* i+r -> r */ \
+ WORD $i;
+#define AND(r0, r1) OPrr(0x21, r0, r1) /* r0&r1 -> r1 */
+#define ANDI(i, r) OP(0x81, 0x03, 0x04, r);/* i&r -> r */ \
+ WORD $i;
+#define CLR(r) OPrr(0x31, r, r) /* r^r -> r */
+#define CLRB(r) OPrr(0x30, r, r) /* r^r -> r */
+#define CMP(r0, r1) OPrr(0x39, r0, r1) /* r1-r0 -> flags */
+#define CMPI(i, r) OP(0x81, 0x03, 0x07, r);/* r-i -> flags */ \
+ WORD $i;
+#define CMPBR(r0, r1) OPrr(0x38, r0, r1) /* r1-r0 -> flags */
+#define DEC(r) BYTE $(0x48|r) /* r-1 -> r */
+#define DIV(r) OPrr(0xF7, 0x06, r) /* rDX:rAX/r -> rAX, rDX:rAX%r -> rDX */
+#define INC(r) BYTE $(0x40|r) /* r+1 -> r */
+#define MUL(r) OPrr(0xF7, 0x04, r) /* r*rAX -> rDX:rAX */
+#define IMUL(r0, r1) BYTE $0x0F; /* r0*r1 -> r1 */ \
+ OPrr(0xAF, r1, r0) /* (signed) */
+#define OR(r0, r1) OPrr(0x09, r0, r1) /* r0|r1 -> r1 */
+#define ORB(r0, r1) OPrr(0x08, r0, r1) /* r0|r1 -> r1 */
+#define ORI(i, r) OP(0x81, 0x03, 0x01, r);/* i|r -> r */ \
+ WORD $i;
+#define ROLI(i, r) OPrr(0xC1, 0x00, r); /* r<<>>i -> r */ \
+ BYTE $i;
+#define SHLI(i, r) OPrr(0xC1, 0x04, r); /* r<<i -> r */ \
+ BYTE $i;
+#define SHLBI(i, r) OPrr(0xC0, 0x04, r); /* r<<i -> r */ \
+ BYTE $i;
+#define SHRI(i, r) OPrr(0xC1, 0x05, r); /* r>>i -> r */ \
+ BYTE $i;
+#define SHRBI(i, r) OPrr(0xC0, 0x05, r); /* r>>i -> r */ \
+ BYTE $i;
+#define SUB(r0, r1) OPrr(0x29, r0, r1) /* r1-r0 -> r1 */
+#define SUBI(i, r) OP(0x81, 0x03, 0x05, r);/* r-i -> r */ \
+ WORD $i;
+
+#define STOSW STOSL
+
+#define CALL16(f) LWI(f, rDI); /* &f -> rDI */ \
+ BYTE $0xFF; /* (*rDI) */ \
+ BYTE $0xD7;
+#define FARJUMP16(s, o) BYTE $0xEA; /* jump to ptr16:16 */ \
+ WORD $o; WORD $s
+#define FARJUMP32(s, o) BYTE $0x66; /* jump to ptr32:16 */ \
+ BYTE $0xEA; LONG $o; WORD $s
+#define DELAY BYTE $0xEB; /* jmp .+2 */ \
+ BYTE $0x00
+#define BIOSCALL(b) INT $b /* INT $b */
+
+#define PEEKW BYTE $0x26; /* MOVW rES:[rBX], rAX */ \
+ BYTE $0x8B; BYTE $0x07
+#define POKEW BYTE $0x26; /* MOVW rAX, rES:[rBX] */ \
+ BYTE $0x89; BYTE $0x07
+#define OUTPORTB(p, d) LBI(d, rAL); /* d -> I/O port p */ \
+ BYTE $0xE6; \
+ BYTE $p; DELAY
+#define PUSHA BYTE $0x60
+#define PUSHR(r) BYTE $(0x50|r) /* r -> (--rSP) */
+#define PUSHS(rS) BYTE $(0x06|((rS)<<3)) /* rS -> (--rSP) */
+#define PUSHI(i) BYTE $0x68; WORD $i; /* i -> --(rSP) */
+#define POPA BYTE $0x61
+#define POPR(r) BYTE $(0x58|r) /* (rSP++) -> r */
+#define POPS(rS) BYTE $$(0x07|((rS)<<3)) /* (rSP++) -> r */
+#define NOP BYTE $0x90 /* nop */
+
+#define LGDT(gdtptr) BYTE $0x0F; /* LGDT */ \
+ BYTE $0x01; BYTE $0x16; \
+ WORD $gdtptr
+
+/* operand size switch. */
+#define OPSIZE BYTE $0x66
+