summaryrefslogtreecommitdiff
path: root/sys/src/boot
diff options
context:
space:
mode:
authoraiju <aiju@phicode.de>2014-12-24 10:21:51 +0100
committeraiju <aiju@phicode.de>2014-12-24 10:21:51 +0100
commit7a3f0998a0ce7470d70c1a13bc4646abafdcc236 (patch)
tree9cea39d933719c0b82eb242ec286c25eed0d728c /sys/src/boot
parent6dafa424805d128fcd08c71dca3e3abdc40aa6c6 (diff)
added zynq kernel
Diffstat (limited to 'sys/src/boot')
-rw-r--r--sys/src/boot/zynq/boothead.c71
-rw-r--r--sys/src/boot/zynq/dat.h11
-rw-r--r--sys/src/boot/zynq/ddr.s258
-rw-r--r--sys/src/boot/zynq/fns.h13
-rw-r--r--sys/src/boot/zynq/fsbl.s310
-rw-r--r--sys/src/boot/zynq/main.c184
-rw-r--r--sys/src/boot/zynq/mem.h107
-rw-r--r--sys/src/boot/zynq/mkfile34
-rw-r--r--sys/src/boot/zynq/mkfile.boothead9
-rw-r--r--sys/src/boot/zynq/net.c591
-rw-r--r--sys/src/boot/zynq/qspi.c45
11 files changed, 1633 insertions, 0 deletions
diff --git a/sys/src/boot/zynq/boothead.c b/sys/src/boot/zynq/boothead.c
new file mode 100644
index 000000000..98aed00c7
--- /dev/null
+++ b/sys/src/boot/zynq/boothead.c
@@ -0,0 +1,71 @@
+#include <u.h>
+#include <libc.h>
+
+char *data;
+uchar head[0x8c0];
+
+void
+usage(void)
+{
+ fprint(2, "usage: %s file\n", argv0);
+ exits("usage");
+}
+
+void
+u32(int n, u32int p)
+{
+ head[n] = p;
+ head[n+1] = p >> 8;
+ head[n+2] = p >> 16;
+ head[n+3] = p >> 24;
+}
+
+u32int
+gu32(int n)
+{
+ return head[n] | head[n+1] << 8 | head[n+2] << 16 | head[n+3] << 24;
+}
+
+void
+main(int argc, char **argv)
+{
+ int fd, sz, i;
+ u32int ck;
+
+ ARGBEGIN {
+ default:
+ usage();
+ } ARGEND;
+
+ if(argc != 1)
+ usage();
+ fd = open(argv[0], OREAD);
+ if(fd < 0)
+ sysfatal("open: %r");
+ sz = seek(fd, 0, 2);
+ if(sz < 0)
+ sysfatal("seek: %r");
+ data = malloc(sz);
+ if(data == nil)
+ sysfatal("malloc: %r");
+ seek(fd, 0, 0);
+ if(readn(fd, data, sz) < sz)
+ sysfatal("read: %r");
+ close(fd);
+ memset(head, 0, sizeof(head));
+
+ u32(0x20, 0xaa995566);
+ u32(0x24, 0x584C4E58);
+ u32(0x30, sizeof(head));
+ u32(0x34, sz);
+ u32(0x40, sz);
+ ck = 0;
+ for(i = 0x20; i < 0x48; i += 4)
+ ck += gu32(i);
+ u32(0x48, ~ck);
+ u32(0xa0, -1);
+
+ write(1, head, sizeof(head));
+ write(1, data, sz);
+ exits(nil);
+}
diff --git a/sys/src/boot/zynq/dat.h b/sys/src/boot/zynq/dat.h
new file mode 100644
index 000000000..81cb90749
--- /dev/null
+++ b/sys/src/boot/zynq/dat.h
@@ -0,0 +1,11 @@
+enum {
+ DHCPTIMEOUT = 2000,
+ ARPTIMEOUT = 1000,
+ TFTPTIMEOUT = 10000,
+
+ TZERO = 0x80000,
+ CONFSIZE = 65536,
+ CONF = TZERO - CONFSIZE,
+};
+
+#define nelem(x) (sizeof(x)/sizeof(*(x)))
diff --git a/sys/src/boot/zynq/ddr.s b/sys/src/boot/zynq/ddr.s
new file mode 100644
index 000000000..28686f702
--- /dev/null
+++ b/sys/src/boot/zynq/ddr.s
@@ -0,0 +1,258 @@
+#define OUTPUT_EN (3<<9)
+#define DCI_EN (7<<4)
+#define INP_VREF (1<<1)
+#define INP_DIFF (2<<1)
+
+TEXT ddriob(SB), $-4
+ WORD $(OUTPUT_EN) // DDRIOB_ADDR0
+ WORD $(OUTPUT_EN) // DDRIOB_ADDR1
+ WORD $(OUTPUT_EN | DCI_EN | INP_VREF) // DDRIOB_DATA0
+ WORD $(OUTPUT_EN | DCI_EN | INP_VREF) // DDRIOB_DATA1
+ WORD $(OUTPUT_EN | DCI_EN | INP_DIFF) // DDRIOB_DIFF0
+ WORD $(OUTPUT_EN | DCI_EN | INP_DIFF) // DDRIOB_DIFF1
+ WORD $(OUTPUT_EN) // DDRIOB_CLOCK
+ WORD $0x0018C61C // DDRIOB_DRIVE_SLEW_ADDR
+ WORD $0x00F9861C // DDRIOB_DRIVE_SLEW_DATA
+ WORD $0x00F9861C // DDRIOB_DRIVE_SLEW_DIFF
+ WORD $0x00F9861C // DDRIOB_DRIVE_SLEW_CLOCK
+ WORD $0xE60 // DDRIOB_DDR_CTRL
+
+TEXT ddrdata(SB), $-4
+ WORD $0XF8006000
+ WORD $0x0001FFFF
+ WORD $0x00000080
+ WORD $0XF8006004
+ WORD $0x1FFFFFFF
+ WORD $0x00081081
+ WORD $0XF8006008
+ WORD $0x03FFFFFF
+ WORD $0x03C0780F
+ WORD $0XF800600C
+ WORD $0x03FFFFFF
+ WORD $0x02001001
+ WORD $0XF8006010
+ WORD $0x03FFFFFF
+ WORD $0x00014001
+ WORD $0XF8006014
+ WORD $0x001FFFFF
+ WORD $0x0004281A
+ WORD $0XF8006018
+ WORD $0xF7FFFFFF
+ WORD $0x44E458D2
+ WORD $0XF800601C
+ WORD $0xFFFFFFFF
+ WORD $0x82023965
+ WORD $0XF8006020
+ WORD $0xFFFFFFFC
+ WORD $0x2B288290
+ WORD $0XF8006024
+ WORD $0x0FFFFFFF
+ WORD $0x0000003C
+ WORD $0XF8006028
+ WORD $0x00003FFF
+ WORD $0x00002007
+ WORD $0XF800602C
+ WORD $0xFFFFFFFF
+ WORD $0x00000008
+ WORD $0XF8006030
+ WORD $0xFFFFFFFF
+ WORD $0x00040970
+ WORD $0XF8006034
+ WORD $0x13FF3FFF
+ WORD $0x00011054
+ WORD $0XF8006038
+ WORD $0x00001FC3
+ WORD $0x00000000
+ WORD $0XF800603C
+ WORD $0x000FFFFF
+ WORD $0x00000777
+ WORD $0XF8006040
+ WORD $0xFFFFFFFF
+ WORD $0xFFF00000
+ WORD $0XF8006044
+ WORD $0x0FFFFFFF
+ WORD $0x0F666666
+ WORD $0XF8006048
+ WORD $0x3FFFFFFF
+ WORD $0x0003C248
+ WORD $0XF8006050
+ WORD $0xFF0F8FFF
+ WORD $0x77010800
+ WORD $0XF8006058
+ WORD $0x0001FFFF
+ WORD $0x00000101
+ WORD $0XF800605C
+ WORD $0x0000FFFF
+ WORD $0x00005003
+ WORD $0XF8006060
+ WORD $0x000017FF
+ WORD $0x0000003E
+ WORD $0XF8006064
+ WORD $0x00021FE0
+ WORD $0x00020000
+ WORD $0XF8006068
+ WORD $0x03FFFFFF
+ WORD $0x00284545
+ WORD $0XF800606C
+ WORD $0x0000FFFF
+ WORD $0x00001610
+ WORD $0XF80060A0
+ WORD $0x00FFFFFF
+ WORD $0x00008000
+ WORD $0XF80060A4
+ WORD $0xFFFFFFFF
+ WORD $0x10200802
+ WORD $0XF80060A8
+ WORD $0x0FFFFFFF
+ WORD $0x0690CB73
+ WORD $0XF80060AC
+ WORD $0x000001FF
+ WORD $0x000001FE
+ WORD $0XF80060B0
+ WORD $0x1FFFFFFF
+ WORD $0x04FFFFFF
+ WORD $0XF80060B4
+ WORD $0x000007FF
+ WORD $0x00000200
+ WORD $0XF80060B8
+ WORD $0x01FFFFFF
+ WORD $0x0020006A
+ WORD $0XF80060C4
+ WORD $0x00000003
+ WORD $0x00000003
+ WORD $0XF80060C4
+ WORD $0x00000003
+ WORD $0x00000000
+ WORD $0XF80060C8
+ WORD $0x000000FF
+ WORD $0x00000000
+ WORD $0XF80060DC
+ WORD $0x00000001
+ WORD $0x00000000
+ WORD $0XF80060F0
+ WORD $0x0000FFFF
+ WORD $0x00000000
+ WORD $0XF80060F4
+ WORD $0x0000000F
+ WORD $0x00000008
+ WORD $0XF8006114
+ WORD $0x000000FF
+ WORD $0x00000000
+ WORD $0XF8006118
+ WORD $0x7FFFFFFF
+ WORD $0x40000001
+ WORD $0XF800611C
+ WORD $0x7FFFFFFF
+ WORD $0x40000001
+ WORD $0XF8006120
+ WORD $0x7FFFFFFF
+ WORD $0x40000001
+ WORD $0XF8006124
+ WORD $0x7FFFFFFF
+ WORD $0x40000001
+ WORD $0XF800612C
+ WORD $0x000FFFFF
+ WORD $0x00000000
+ WORD $0XF8006130
+ WORD $0x000FFFFF
+ WORD $0x00000000
+ WORD $0XF8006134
+ WORD $0x000FFFFF
+ WORD $0x00000000
+ WORD $0XF8006138
+ WORD $0x000FFFFF
+ WORD $0x00000000
+ WORD $0XF8006140
+ WORD $0x000FFFFF
+ WORD $0x00000035
+ WORD $0XF8006144
+ WORD $0x000FFFFF
+ WORD $0x00000035
+ WORD $0XF8006148
+ WORD $0x000FFFFF
+ WORD $0x00000035
+ WORD $0XF800614C
+ WORD $0x000FFFFF
+ WORD $0x00000035
+ WORD $0XF8006154
+ WORD $0x000FFFFF
+ WORD $0x00000080
+ WORD $0XF8006158
+ WORD $0x000FFFFF
+ WORD $0x00000080
+ WORD $0XF800615C
+ WORD $0x000FFFFF
+ WORD $0x00000080
+ WORD $0XF8006160
+ WORD $0x000FFFFF
+ WORD $0x00000075
+ WORD $0XF8006168
+ WORD $0x001FFFFF
+ WORD $0x000000EE
+ WORD $0XF800616C
+ WORD $0x001FFFFF
+ WORD $0x000000E4
+ WORD $0XF8006170
+ WORD $0x001FFFFF
+ WORD $0x000000FC
+ WORD $0XF8006174
+ WORD $0x001FFFFF
+ WORD $0x000000F4
+ WORD $0XF800617C
+ WORD $0x000FFFFF
+ WORD $0x000000C0
+ WORD $0XF8006180
+ WORD $0x000FFFFF
+ WORD $0x000000C0
+ WORD $0XF8006184
+ WORD $0x000FFFFF
+ WORD $0x000000C0
+ WORD $0XF8006188
+ WORD $0x000FFFFF
+ WORD $0x000000B5
+ WORD $0XF8006190
+ WORD $0xFFFFFFFF
+ WORD $0x10040080
+ WORD $0XF8006194
+ WORD $0x000FFFFF
+ WORD $0x00007D02
+ WORD $0XF8006204
+ WORD $0xFFFFFFFF
+ WORD $0x00000000
+ WORD $0XF8006208
+ WORD $0x000F03FF
+ WORD $0x000803FF
+ WORD $0XF800620C
+ WORD $0x000F03FF
+ WORD $0x000803FF
+ WORD $0XF8006210
+ WORD $0x000F03FF
+ WORD $0x000803FF
+ WORD $0XF8006214
+ WORD $0x000F03FF
+ WORD $0x000803FF
+ WORD $0XF8006218
+ WORD $0x000F03FF
+ WORD $0x000003FF
+ WORD $0XF800621C
+ WORD $0x000F03FF
+ WORD $0x000003FF
+ WORD $0XF8006220
+ WORD $0x000F03FF
+ WORD $0x000003FF
+ WORD $0XF8006224
+ WORD $0x000F03FF
+ WORD $0x000003FF
+ WORD $0XF80062A8
+ WORD $0x00000FF7
+ WORD $0x00000000
+ WORD $0XF80062AC
+ WORD $0xFFFFFFFF
+ WORD $0x00000000
+ WORD $0XF80062B0
+ WORD $0x003FFFFF
+ WORD $0x00005125
+ WORD $0xF80062B4
+ WORD $0x003FFFFF
+ WORD $0x000012A8
+ WORD $0
diff --git a/sys/src/boot/zynq/fns.h b/sys/src/boot/zynq/fns.h
new file mode 100644
index 000000000..48f0a3ff9
--- /dev/null
+++ b/sys/src/boot/zynq/fns.h
@@ -0,0 +1,13 @@
+void putc(int);
+void puts(char *);
+int netboot(void);
+void puthex(u32int);
+void memset(void *, char, int);
+void memcpy(void *, void *, int);
+void print(char *, ...);
+u32int u32get(void *);
+void jump(void *);
+void sleep(int);
+void timeren(int);
+int timertrig(void);
+void flash(void);
diff --git a/sys/src/boot/zynq/fsbl.s b/sys/src/boot/zynq/fsbl.s
new file mode 100644
index 000000000..4923cdf52
--- /dev/null
+++ b/sys/src/boot/zynq/fsbl.s
@@ -0,0 +1,310 @@
+#include "mem.h"
+
+#define Rb R10
+#define SET(R, V) MOVW $(V), R0 ; MOVW R0, (R)(Rb)
+#define RMW(r, m, v) MOVW (r)(Rb), R0; BIC $(m), R0; ORR $(v), R0; MOVW R0, (r)(Rb)
+
+TEXT _start(SB), $-4
+ WORD $0xea000006
+ MOVW $abort(SB), R15
+ MOVW $abort(SB), R15
+ MOVW $abort(SB), R15
+ MOVW $abort(SB), R15
+ MOVW $abort(SB), R15
+ MOVW $abort(SB), R15
+ MOVW $abort(SB), R15
+
+TEXT reloc(SB), $-4
+ MOVW $(1<<7|1<<6|0x13), R0
+ MOVW R0, CPSR
+ MOVW $STACKTOP, R13
+ MOVW $_start(SB), R0
+ MCR CpMMU, 0, R0, C(12), C(0)
+ MOVW $SLCR_BASE, Rb
+ SET(SLCR_UNLOCK, UNLOCK_KEY)
+ MOVW $0, R0
+ MCR 15, 0, R0, C(8), C(7), 0
+ MCR 15, 0, R0, C(7), C(5), 0
+ MCR 15, 0, R0, C(7), C(5), 6
+ MOVW $0xc5047a, R1
+ MCR 15, 0, R1, C(1), C(0), 0
+ DSB
+ ISB
+ CMP.S $0, R15
+ BL.LT reset(SB)
+
+ MOVW $0xf, R1
+ MOVW $0xffff0000, R3
+ MOVW $0xe58a1910, R0
+ MOVW R0, (R3)
+ MOVW $0xf57ff04f, R0
+ MOVW R0, 4(R3)
+ MOVW $0xf57ff06f, R0
+ MOVW R0, 8(R3)
+ MOVW $0xe28ef000, R0
+ MOVW R0, 12(R3)
+ MOVW $reset(SB), R14
+ DSB
+ ISB
+ MOVW R3, R15
+
+TEXT reset(SB), $-4
+ BL pllsetup(SB)
+ BL miosetup(SB)
+ BL ddrsetup(SB)
+ BL uartsetup(SB)
+ MOVW $SLCR_BASE, Rb
+ SET(SLCR_LOCK, LOCK_KEY)
+// BL memtest(SB)
+ MOVW $setR12(SB), R12
+ BL main(SB)
+ B abort(SB)
+
+TEXT pllsetup(SB), $0
+ MOVW $SLCR_BASE, Rb
+
+ SET(ARM_PLL_CFG, ARM_PLL_CFG_VAL)
+ SET(DDR_PLL_CFG, DDR_PLL_CFG_VAL)
+ SET(IO_PLL_CFG, IO_PLL_CFG_VAL)
+
+ MOVW $(ARM_FDIV | PLL_BYPASS_FORCE), R0
+ MOVW R0, ARM_PLL_CTRL(Rb)
+ ORR $(PLL_RESET), R4
+ MOVW R4, ARM_PLL_CTRL(Rb)
+ MOVW R0, ARM_PLL_CTRL(Rb)
+
+ MOVW $(DDR_FDIV | PLL_BYPASS_FORCE), R0
+ MOVW R0, DDR_PLL_CTRL(Rb)
+ ORR $(PLL_RESET), R4
+ MOVW R4, DDR_PLL_CTRL(Rb)
+ MOVW R0, DDR_PLL_CTRL(Rb)
+
+ MOVW $(IO_FDIV | PLL_BYPASS_FORCE), R0
+ MOVW R0, IO_PLL_CTRL(Rb)
+ ORR $(PLL_RESET), R4
+ MOVW R4, IO_PLL_CTRL(Rb)
+ MOVW R0, IO_PLL_CTRL(Rb)
+
+_pllsetupl:
+ MOVW PLL_STATUS(Rb), R0
+ AND $7, R0
+ CMP.S $7, R0
+ BNE _pllsetupl
+
+ SET(ARM_PLL_CTRL, ARM_FDIV)
+ SET(DDR_PLL_CTRL, DDR_FDIV)
+ SET(IO_PLL_CTRL, IO_FDIV)
+
+ SET(ARM_CLK_CTRL, 0x1f << 24 | CPU_DIV << 8)
+ SET(UART_CLK_CTRL, UART_DIV << 8 | 3)
+ SET(DDR_CLK_CTRL, DDR_DIV3 << 20 | DDR_DIV2 << 26 | 3)
+ SET(DCI_CLK_CTRL, DCI_DIV0 << 8 | DCI_DIV1 << 20 | 1)
+ SET(GEM0_RCLK_CTRL, 1)
+ SET(GEM1_RCLK_CTRL, 0)
+ SET(GEM0_CLK_CTRL, ETH_DIV0 << 8 | ETH_DIV1 << 20 | 1)
+ SET(GEM1_CLK_CTRL, 0)
+ SET(GPIOB_CTRL, VREF_SW_EN)
+ SET(APER_CLK_CTRL, LQSPI_CLK_EN | GPIO_CLK_EN | UART0_CLK_EN | UART1_CLK_EN | I2C0_CLK_EN | SDIO1_CLK_EN | GEM0_CLK_EN | USB0_CLK_EN | USB1_CLK_EN | DMA_CLK_EN)
+ SET(SMC_CLK_CTRL, 0x3C20)
+ SET(LQSPI_CLK_CTRL, QSPI_DIV << 8 | 1)
+ SET(SDIO_CLK_CTRL, SDIO_DIV << 8 | 2)
+ SET(SPI_CLK_CTRL, 0x3F00)
+ SET(CAN_CLK_CTRL, 0x501900)
+ SET(PCAP_CLK_CTRL, PCAP_DIV << 8 | 1)
+ RET
+
+TEXT miosetup(SB), $0
+ MOVW $SLCR_BASE, Rb
+ SET(UART_RST_CTRL, 0xf)
+ SET(UART_RST_CTRL, 0)
+
+ MOVW $miodata(SB), R1
+ ADD $MIO_PIN_0, Rb, R2
+ MOVW $54, R3
+ BL copy(SB)
+
+ MOVW $0, R0
+ MOVW R0, MIO_MST_TRI0(Rb)
+ MOVW R0, MIO_MST_TRI1(Rb)
+ RET
+
+TEXT copy(SB), $0
+_copyl:
+ MOVW.P 4(R1), R0
+ MOVW.P R0, 4(R2)
+ SUB.S $1, R3
+ BNE _copyl
+ RET
+
+TEXT ddrsetup(SB), $0
+ MOVW $SLCR_BASE, Rb
+ RMW(DDRIOB_DCI_CTRL, DCI_RESET, DCI_RESET)
+ RMW(DDRIOB_DCI_CTRL, DCI_RESET, 0)
+ RMW(DDRIOB_DCI_CTRL, DDRIOB_DCI_CTRL_MASK, DCI_NREF | DCI_ENABLE | DCI_RESET)
+
+ MOVW $ddriob(SB), R1
+ ADD $DDRIOB_ADDR0, Rb, R2
+ MOVW $12, R3
+ BL copy(SB)
+
+ MOVW $ddrdata(SB), R1
+_ddrl1:
+ MOVW.P 4(R1), R2
+ ORR.S $0, R2
+ BEQ _ddrl2
+ MOVW.P 4(R1), R3
+ MOVW.P 4(R1), R4
+ AND R3, R4
+ MOVW (R2), R0
+ BIC R3, R0
+ ORR R4, R0
+ MOVW R0, (R2)
+ B _ddrl1
+_ddrl2:
+ MOVW DDRIOB_DCI_STATUS(Rb), R0
+ AND.S $(1<<13), R0
+ BEQ _ddrl2
+ MOVW $DDR_BASE, Rb
+ RMW(DDRC_CTRL, 0x1ffff, 0x81)
+_ddrl4:
+ MOVW DDR_MODE_STS(Rb), R0
+ AND.S $7, R0
+ BEQ _ddrl4
+
+ MOVW $MP_BASE, Rb
+ SET(FILTER_START, 0)
+ RET
+
+TEXT memtest(SB), $0
+ MOVW $0, R0
+ ADD $(1024 * 1024 * 10), R0, R1
+_testl:
+ MOVW R0, (R0)
+ ADD $4, R0
+ CMP.S R0, R1
+ BNE _testl
+ MOVW $0, R0
+_testl2:
+ MOVW (R0), R2
+ CMP.S R0, R2
+ BNE _no
+ ADD $4, R0
+ CMP.S R0, R1
+ BNE _testl2
+ MOVW $'.', R0
+ BL putc(SB)
+ RET
+_no:
+ MOVW $'!', R0
+ BL putc(SB)
+ RET
+
+TEXT uartsetup(SB), $0
+ MOVW $UART1_BASE, Rb
+ SET(UART_CTRL, 0x17)
+ SET(UART_MODE, 0x20)
+ SET(UART_SAMP, 15)
+ SET(UART_BAUD, 14)
+ RET
+
+TEXT putc(SB), $0
+ MOVW $UART1_BASE, Rb
+ CMP.S $10, R0
+ BNE _putcl
+ MOVW R0, R2
+ MOVW $13, R0
+ BL putc(SB)
+ MOVW R2, R0
+_putcl:
+ MOVW UART_STAT(Rb), R1
+ AND.S $0x10, R1
+ BNE _putcl
+ AND $0xFF, R0
+ MOVW R0, UART_DATA(Rb)
+ RET
+
+TEXT jump(SB), $-4
+ MOVW R0, R15
+
+TEXT abort(SB), $0
+ MOVW $'?', R0
+ BL putc(SB)
+_loop:
+ WFE
+ B _loop
+
+#define TRI 1
+#define LVCMOS18 (1<<9)
+#define LVCMOS25 (2<<9)
+#define LVCMOS33 (3<<9)
+#define HSTL (4<<9)
+#define PULLUP (1<<12)
+#define NORECV (1<<13)
+#define FAST (1<<8)
+#define MUX(a, b, c, d) ((a)<<1 | (b)<<2 | (c)<<3 | (d)<<5)
+
+#define NO (TRI | LVCMOS33)
+#define SPI (MUX(1, 0, 0, 0) | LVCMOS33)
+#define UART (MUX(0, 0, 0, 7) | LVCMOS33)
+#define SD (MUX(0, 0, 0, 4) | LVCMOS33)
+#define ETX (MUX(1, 0, 0, 0) | HSTL | NORECV | PULLUP)
+#define ERX (MUX(1, 0, 0, 0) | HSTL | TRI | PULLUP)
+#define USB (MUX(0, 1, 0, 0) | LVCMOS18)
+#define MDCLK (MUX(0, 0, 0, 4) | HSTL)
+#define MDDATA (MUX(0, 0, 0, 4) | HSTL)
+
+TEXT miodata(SB), $-4
+ WORD $NO // 0
+ WORD $SPI // 1
+ WORD $SPI // 2
+ WORD $SPI // 3
+ WORD $SPI // 4
+ WORD $SPI // 5
+ WORD $SPI // 6
+ WORD $NO // 7
+ WORD $UART // 8
+ WORD $(UART|TRI) // 9
+ WORD $SD // 10
+ WORD $SD // 11
+ WORD $SD // 12
+ WORD $SD // 13
+ WORD $SD // 14
+ WORD $SD // 15
+ WORD $ETX // 16
+ WORD $ETX // 17
+ WORD $ETX // 18
+ WORD $ETX // 19
+ WORD $ETX // 20
+ WORD $ETX // 21
+ WORD $ERX // 22
+ WORD $ERX // 23
+ WORD $ERX // 24
+ WORD $ERX // 25
+ WORD $ERX // 26
+ WORD $ERX // 27
+ WORD $USB // 28
+ WORD $USB // 29
+ WORD $USB // 30
+ WORD $USB // 31
+ WORD $USB // 32
+ WORD $USB // 33
+ WORD $USB // 34
+ WORD $USB // 35
+ WORD $USB // 36
+ WORD $USB // 37
+ WORD $USB // 38
+ WORD $USB // 39
+ WORD $USB // 40
+ WORD $USB // 41
+ WORD $USB // 42
+ WORD $USB // 43
+ WORD $USB // 44
+ WORD $USB // 45
+ WORD $USB // 46
+ WORD $USB // 47
+ WORD $USB // 48
+ WORD $USB // 49
+ WORD $USB // 50
+ WORD $USB // 51
+ WORD $MDCLK // 52
+ WORD $MDDATA // 53
diff --git a/sys/src/boot/zynq/main.c b/sys/src/boot/zynq/main.c
new file mode 100644
index 000000000..ffe2daddd
--- /dev/null
+++ b/sys/src/boot/zynq/main.c
@@ -0,0 +1,184 @@
+#include <u.h>
+#include <a.out.h>
+#include "dat.h"
+#include "fns.h"
+
+void
+puts(char *s)
+{
+ while(*s)
+ putc(*s++);
+}
+
+void
+puthex(u32int u)
+{
+ static char *dig = "0123456789abcdef";
+ int i;
+
+ for(i = 0; i < 8; i++){
+ putc(dig[u >> 28]);
+ u <<= 4;
+ }
+}
+
+void
+putdec(int n)
+{
+ if(n / 10 != 0)
+ putdec(n / 10);
+ putc(n % 10 + '0');
+}
+
+void
+print(char *s, ...)
+{
+ va_list va;
+ int n;
+ u32int u;
+
+ va_start(va, s);
+ while(*s)
+ if(*s == '%'){
+ switch(*++s){
+ case 's':
+ puts(va_arg(va, char *));
+ break;
+ case 'x':
+ puthex(va_arg(va, u32int));
+ break;
+ case 'd':
+ n = va_arg(va, int);
+ if(n < 0){
+ putc('-');
+ putdec(-n);
+ }else
+ putdec(n);
+ break;
+ case 'I':
+ u = va_arg(va, u32int);
+ putdec(u >> 24);
+ putc('.');
+ putdec((uchar)(u >> 16));
+ putc('.');
+ putdec((uchar)(u >> 8));
+ putc('.');
+ putdec((uchar)u);
+ break;
+ case 0:
+ va_end(va);
+ return;
+ }
+ s++;
+ }else
+ putc(*s++);
+ va_end(va);
+}
+
+void
+memset(void *v, char c, int n)
+{
+ char *vc;
+
+ vc = v;
+ while(n--)
+ *vc++ = c;
+}
+
+void
+memcpy(void *d, void *s, int n)
+{
+ char *cd, *cs;
+
+ cd = d;
+ cs = s;
+ while(n--)
+ *cd++ = *cs++;
+}
+
+void
+run(void)
+{
+ ulong t, tr;
+ char *p, *d;
+ int n;
+ ulong *h;
+
+ h = (ulong *) TZERO;
+ if(u32get(&h[0]) != E_MAGIC){
+ print("invalid magic: %x != %x\n", u32get(&h[0]), E_MAGIC);
+ return;
+ }
+ t = u32get(&h[1]) + 0x20;
+ tr = t + 0xfff & ~0xfff;
+ if(t != tr){
+ n = u32get(&h[2]);
+ p = (char *) (TZERO + t + n);
+ d = (char *) (TZERO + tr + n);
+ while(n--)
+ *--d = *--p;
+ }
+ p = (char *) (TZERO + tr + u32get(&h[2]));
+ memset(p, 0, u32get(&h[3]));
+ jump((void *) (u32get(&h[5]) & 0xfffffff));
+}
+
+enum {
+ TIMERVALL,
+ TIMERVALH,
+ TIMERCTL,
+ TIMERSTAT,
+ TIMERCOMPL,
+ TIMERCOMPH,
+};
+
+void
+timeren(int n)
+{
+ ulong *r;
+
+ r = (ulong *) 0xf8f00200;
+ if(n < 0){
+ r[TIMERSTAT] |= 1;
+ r[TIMERCTL] = 0;
+ return;
+ }
+ r[TIMERCTL] = 0;
+ r[TIMERVALL] = 0;
+ r[TIMERVALH] = 0;
+ r[TIMERCOMPL] = 1000 * n;
+ r[TIMERCOMPH] = 0;
+ r[TIMERSTAT] |= 1;
+ r[TIMERCTL] = 100 << 8 | 3;
+}
+
+int
+timertrig(void)
+{
+ ulong *r;
+
+ r = (ulong *) 0xf8f00200;
+ if((r[TIMERSTAT] & 1) != 0){
+ r[TIMERCTL] = 0;
+ r[TIMERSTAT] |= 1;
+ return 1;
+ }
+ return 0;
+}
+
+void
+sleep(int n)
+{
+ timeren(n);
+ while(!timertrig())
+ ;
+}
+
+void
+main(void)
+{
+ puts("Booting ...\n");
+ if(netboot() > 0)
+ run();
+ print("hjboot: ending\n");
+}
diff --git a/sys/src/boot/zynq/mem.h b/sys/src/boot/zynq/mem.h
new file mode 100644
index 000000000..9753fc346
--- /dev/null
+++ b/sys/src/boot/zynq/mem.h
@@ -0,0 +1,107 @@
+#define STACKTOP 0xFFFFFE00
+
+#define ARM_FDIV (40 << PLL_FDIV_SH)
+#define DDR_FDIV (32 << PLL_FDIV_SH)
+#define IO_FDIV (30 << PLL_FDIV_SH)
+#define PLL_CFG_VAL(CP, RES, CNT) ((CP)<<8 | (RES)<<4 | (CNT)<<12)
+#define ARM_PLL_CFG_VAL PLL_CFG_VAL(2, 2, 250)
+#define DDR_PLL_CFG_VAL PLL_CFG_VAL(2, 2, 300)
+#define IO_PLL_CFG_VAL PLL_CFG_VAL(2, 12, 325)
+#define PLL_FDIV_SH 12
+#define PLL_BYPASS_FORCE 0x10
+#define PLL_RESET 0x01
+
+#define CPU_DIV 2
+#define DDR_DIV3 2
+#define DDR_DIV2 3
+#define UART_DIV 40
+#define DCI_DIV0 20
+#define DCI_DIV1 5
+#define ETH_DIV0 8
+#define ETH_DIV1 1
+#define QSPI_DIV 5
+#define SDIO_DIV 10
+#define PCAP_DIV 5
+#define MDC_DIV 6 /* this value depends on CPU_1xCLK, see TRM GEM.net_cfg description */
+
+#define SLCR_BASE 0xF8000000
+#define SLCR_LOCK 0x004
+#define LOCK_KEY 0x767B
+#define SLCR_UNLOCK 0x008
+#define UNLOCK_KEY 0xDF0D
+
+#define ARM_PLL_CTRL 0x100
+#define DDR_PLL_CTRL 0x104
+#define IO_PLL_CTRL 0x108
+#define PLL_STATUS 0x10C
+#define ARM_PLL_CFG 0x110
+#define DDR_PLL_CFG 0x114
+#define IO_PLL_CFG 0x118
+#define ARM_CLK_CTRL 0x120
+#define DDR_CLK_CTRL 0x124
+#define DCI_CLK_CTRL 0x128
+#define APER_CLK_CTRL 0x12C
+#define GEM0_RCLK_CTRL 0x138
+#define GEM1_RCLK_CTRL 0x13C
+#define GEM0_CLK_CTRL 0x140
+#define GEM1_CLK_CTRL 0x144
+#define SMC_CLK_CTRL 0x148
+#define LQSPI_CLK_CTRL 0x14C
+#define SDIO_CLK_CTRL 0x150
+#define UART_CLK_CTRL 0x154
+#define SPI_CLK_CTRL 0x158
+#define CAN_CLK_CTRL 0x15C
+#define PCAP_CLK_CTRL 0x168
+#define UART_RST_CTRL 0x228
+#define A9_CPU_RST_CTRL 0x244
+
+#define LQSPI_CLK_EN (1<<23)
+#define GPIO_CLK_EN (1<<22)
+#define UART0_CLK_EN (1<<20)
+#define UART1_CLK_EN (1<<21)
+#define I2C0_CLK_EN (1<<18)
+#define SDIO1_CLK_EN (1<<11)
+#define GEM0_CLK_EN (1<<6)
+#define USB1_CLK_EN (1<<3)
+#define USB0_CLK_EN (1<<2)
+#define DMA_CLK_EN (1<<0)
+
+#define MIO_PIN_0 0x00000700
+#define MIO_MST_TRI0 0x80C
+#define MIO_MST_TRI1 0x810
+#define OCM_CFG 0x910
+#define GPIOB_CTRL 0xB00
+#define VREF_SW_EN (1<<11)
+#define DDRIOB_ADDR0 0xB40
+#define DDRIOB_DCI_CTRL 0xB70
+#define DDRIOB_DCI_CTRL_MASK 0x1ffc3
+#define DDRIOB_DCI_STATUS 0xB74
+#define DCI_RESET 1
+#define DCI_NREF (1<<11)
+#define DCI_ENABLE 2
+
+#define DDR_BASE 0xF8006000
+#define DDRC_CTRL 0x0
+#define DDR_MODE_STS 0x54
+
+#define UART1_BASE 0xE0001000
+#define UART_CTRL 0x0
+#define UART_MODE 0x4
+#define UART_BAUD 0x18
+#define UART_STAT 0x2C
+#define UART_DATA 0x30
+#define UART_SAMP 0x34
+
+#define QSPI_BASE 0xE000D000
+#define QSPI_CFG 0x0
+#define SPI_EN 0x4
+#define QSPI_TX 0x1c
+
+#define MP_BASE 0xF8F00000
+#define FILTER_START 0x40
+
+#define CpMMU 15
+
+#define DSB WORD $0xf57ff04f
+#define ISB WORD $0xf57ff06f
+#define WFE WORD $0xe320f002
diff --git a/sys/src/boot/zynq/mkfile b/sys/src/boot/zynq/mkfile
new file mode 100644
index 000000000..fc8e84a63
--- /dev/null
+++ b/sys/src/boot/zynq/mkfile
@@ -0,0 +1,34 @@
+objtype=arm
+</$objtype/mkfile
+BIN=/arm
+TARG=fsbl fsbl.img
+CLEANFILES=boothead.$cputype
+FSBLFILES=fsbl.$O ddr.$O main.$O net.$O div.$O qspi.$O
+
+all:V: $TARG
+
+clean:V:
+ rm -rf $TARG *.$O
+
+fsbl: $FSBLFILES
+ $LD -o $target -T0xfffc0000 -H6 -R4096 -l -s $prereq
+
+9fsbl: $FSBLFILES
+ $LD -o $target -T0xfffc0000 -l $prereq
+
+fsbl.img:D: fsbl boothead.$cputype
+ boothead.$cputype fsbl >fsbl.img
+
+boothead.$cputype:V: mkfile.boothead
+ @{objtype=$cputype mk -f $prereq all}
+
+div.$O: /sys/src/libc/arm/div.s
+ $AS /sys/src/libc/arm/div.s
+
+%.$O: dat.h fns.h mem.h
+
+%.$O: %.s
+ $AS $stem.s
+
+%.$O: %.c
+ $CC $CFLAGS $stem.c
diff --git a/sys/src/boot/zynq/mkfile.boothead b/sys/src/boot/zynq/mkfile.boothead
new file mode 100644
index 000000000..cadebcb78
--- /dev/null
+++ b/sys/src/boot/zynq/mkfile.boothead
@@ -0,0 +1,9 @@
+</$objtype/mkfile
+
+all:V: boothead.$objtype
+
+boothead.$objtype: boothead.$O
+ $LD $LDFLAGS -o $target $prereq
+
+%.$O: %.c
+ $CC $CFLAGS $stem.c
diff --git a/sys/src/boot/zynq/net.c b/sys/src/boot/zynq/net.c
new file mode 100644
index 000000000..f783958a0
--- /dev/null
+++ b/sys/src/boot/zynq/net.c
@@ -0,0 +1,591 @@
+#include <u.h>
+#include "dat.h"
+#include "fns.h"
+#include "mem.h"
+
+enum {
+ ETHLEN = 1600,
+ UDPLEN = 576,
+ NRX = 64,
+ RXBASE = 128 * 1024 * 1024,
+
+ ETHHEAD = 14,
+ IPHEAD = 20,
+ UDPHEAD = 8,
+
+ BOOTREQ = 1,
+ DHCPDISCOVER = 1,
+ DHCPOFFER,
+ DHCPREQUEST,
+ DHCPDECLINE,
+};
+
+enum {
+ NET_CTRL,
+ NET_CFG,
+ NET_STATUS,
+ DMA_CFG = 4,
+ TX_STATUS,
+ RX_QBAR,
+ TX_QBAR,
+ RX_STATUS,
+ INTR_STATUS,
+ INTR_EN,
+ INTR_DIS,
+ INTR_MASK,
+ PHY_MAINT,
+ RX_PAUSEQ,
+ TX_PAUSEQ,
+ HASH_BOT = 32,
+ HASH_TOP,
+ SPEC_ADDR1_BOT,
+ SPEC_ADDR1_TOP,
+};
+
+enum {
+ MDCTRL,
+ MDSTATUS,
+ MDID1,
+ MDID2,
+ MDAUTOADV,
+ MDAUTOPART,
+ MDAUTOEX,
+ MDAUTONEXT,
+ MDAUTOLINK,
+ MDGCTRL,
+ MDGSTATUS,
+ MDPHYCTRL = 0x1f,
+};
+
+enum {
+ /* NET_CTRL */
+ RXEN = 1<<2,
+ TXEN = 1<<3,
+ MDEN = 1<<4,
+ STARTTX = 1<<9,
+ /* NET_CFG */
+ SPEED = 1<<0,
+ FDEN = 1<<1,
+ RX1536EN = 1<<8,
+ GIGE_EN = 1<<10,
+ RXCHKSUMEN = 1<<24,
+ /* NET_STATUS */
+ PHY_IDLE = 1<<2,
+ /* DMA_CFG */
+ TXCHKSUMEN = 1<<11,
+ /* TX_STATUS */
+ TXCOMPL = 1<<5,
+ /* MDCTRL */
+ MDRESET = 1<<15,
+ AUTONEG = 1<<12,
+ FULLDUP = 1<<8,
+ /* MDSTATUS */
+ LINK = 1<<2,
+ /* MDGSTATUS */
+ RECVOK = 3<<12,
+};
+
+typedef struct {
+ uchar edest[6];
+ uchar esrc[6];
+ ulong idest;
+ ulong isrc;
+ ushort dport, sport;
+ ushort len;
+ uchar data[UDPLEN];
+} udp;
+
+static ulong *eth0 = (ulong *) 0xe000b000;
+static int phyaddr = 7;
+
+static u32int myip, dhcpip, tftpip, xid;
+static uchar mac[6] = {0x0E, 0xA7, 0xDE, 0xAD, 0xBE, 0xEF};
+static uchar tmac[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+static char file[128];
+
+static udp ubuf, urbuf;
+static uchar txbuf[ETHLEN];
+static ulong txdesc[4], *txact, *rxact;
+static ulong rxdesc[NRX*2];
+
+void
+mdwrite(ulong *r, int reg, u16int val)
+{
+ while((r[NET_STATUS] & PHY_IDLE) == 0)
+ ;
+ r[PHY_MAINT] = 1<<30 | 1<<28 | 1<<17 | phyaddr << 23 | reg << 18 | val;
+ while((r[NET_STATUS] & PHY_IDLE) == 0)
+ ;
+}
+
+u16int
+mdread(ulong *r, int reg)
+{
+ while((r[NET_STATUS] & PHY_IDLE) == 0)
+ ;
+ r[PHY_MAINT] = 1<<30 | 1<<29 | 1<<17 | phyaddr << 23 | reg << 18;
+ while((r[NET_STATUS] & PHY_IDLE) == 0)
+ ;
+ return r[PHY_MAINT];
+}
+
+void
+ethinit(ulong *r)
+{
+ int v;
+ ulong *p;
+ ulong d;
+
+ r[NET_CTRL] = 0;
+ r[RX_STATUS] = 0xf;
+ r[TX_STATUS] = 0xff;
+ r[INTR_DIS] = 0x7FFFEFF;
+ r[RX_QBAR] = r[TX_QBAR] = 0;
+ r[NET_CFG] = MDC_DIV << 18 | FDEN | SPEED | RX1536EN | GIGE_EN | RXCHKSUMEN;
+ r[SPEC_ADDR1_BOT] = mac[0] | mac[1] << 8 | mac[2] << 16 | mac[3] << 24;
+ r[SPEC_ADDR1_TOP] = mac[4] | mac[5] << 8;
+ r[DMA_CFG] = TXCHKSUMEN | 0x18 << 16 | 1 << 10 | 3 << 8 | 0x10;
+
+ txdesc[0] = 0;
+ txdesc[1] = 1<<31;
+ txdesc[2] = 0;
+ txdesc[3] = 1<<31 | 1<<30;
+ txact = txdesc;
+ r[TX_QBAR] = (ulong) txdesc;
+ for(p = rxdesc, d = RXBASE; p < rxdesc + nelem(rxdesc); d += ETHLEN){
+ *p++ = d;
+ *p++ = 0;
+ }
+ p[-2] |= 2;
+ rxact = rxdesc;
+ r[RX_QBAR] = (ulong) rxdesc;
+
+ r[NET_CTRL] = MDEN;
+// mdwrite(r, MDCTRL, MDRESET);
+ mdwrite(r, MDCTRL, AUTONEG);
+ if((mdread(r, MDSTATUS) & LINK) == 0){
+ puts("Waiting for Link ...\n");
+ while((mdread(r, MDSTATUS) & LINK) == 0)
+ ;
+ }
+ v = mdread(r, MDPHYCTRL);
+ if((v & 0x40) != 0){
+ puts("1000BASE-T");
+ while((mdread(r, MDGSTATUS) & RECVOK) != RECVOK)
+ ;
+ r[NET_CFG] |= GIGE_EN;
+ }else if((v & 0x20) != 0){
+ puts("100BASE-TX");
+ r[NET_CFG] = NET_CFG & ~GIGE_EN | SPEED;
+ }else if((v & 0x10) != 0){
+ puts("10BASE-T");
+ r[NET_CFG] = NET_CFG & ~(GIGE_EN | SPEED);
+ }else
+ puts("???");
+ if((v & 0x08) != 0)
+ puts(" Full Duplex\n");
+ else{
+ puts(" Half Duplex\n");
+ r[NET_CFG] &= ~FDEN;
+ }
+ r[NET_CTRL] |= TXEN | RXEN;
+}
+
+u32int
+u32get(void *pp)
+{
+ uchar *p;
+
+ p = pp;
+ return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
+}
+
+uchar *
+u32put(uchar *p, u32int v)
+{
+ p[0] = v >> 24;
+ p[1] = v >> 16;
+ p[2] = v >> 8;
+ p[3] = v;
+ return p + 4;
+}
+
+void
+ethtx(ulong *r, uchar *buf, int len)
+{
+ txact[0] = (ulong) buf;
+ txact[1] = 1<<15 | len;
+ if(txact == txdesc + nelem(txdesc) - 2){
+ txact[1] |= 1<<30;
+ txact = txdesc;
+ }else
+ txact += 2;
+ r[TX_STATUS] = -1;
+ r[NET_CTRL] |= STARTTX;
+ while((r[TX_STATUS] & TXCOMPL) == 0)
+ ;
+}
+
+void
+udptx(ulong *r, udp *u)
+{
+ uchar *p, *q;
+ int n;
+
+ p = q = txbuf;
+ memcpy(p, u->edest, 6);
+ memcpy(p + 6, u->esrc, 6);
+ q += 12;
+ *q++ = 8;
+ *q++ = 0;
+
+ *q++ = 5 | 4 << 4;
+ *q++ = 0;
+ n = IPHEAD + UDPHEAD + u->len;
+ *q++ = n >> 8;
+ *q++ = n;
+
+ *q++ = 0x13;
+ *q++ = 0x37;
+ *q++ = 1<<6;
+ *q++ = 0;
+
+ *q++ = 1;
+ *q++ = 0x11;
+ *q++ = 0;
+ *q++ = 0;
+ q = u32put(q, u->isrc);
+ q = u32put(q, u->idest);
+
+ *q++ = u->sport >> 8;
+ *q++ = u->sport;
+ *q++ = u->dport >> 8;
+ *q++ = u->dport;
+ n = UDPHEAD + u->len;
+ *q++ = n >> 8;
+ *q++ = n;
+ *q++ = 0;
+ *q++ = 0;
+
+ memcpy(q, u->data, u->len);
+ ethtx(r, p, ETHHEAD + IPHEAD + UDPHEAD + u->len);
+}
+
+void
+dhcppkg(ulong *r, int t)
+{
+ uchar *p;
+ udp *u;
+
+ u = &ubuf;
+ p = u->data;
+ *p++ = BOOTREQ;
+ *p++ = 1;
+ *p++ = 6;
+ *p++ = 0;
+ p = u32put(p, xid);
+ p = u32put(p, 0x8000);
+ memset(p, 0, 16);
+ u32put(p + 8, dhcpip);
+ p += 16;
+ memcpy(p, mac, 6);
+ p += 6;
+ memset(p, 0, 202);
+ p += 202;
+ *p++ = 99;
+ *p++ = 130;
+ *p++ = 83;
+ *p++ = 99;
+
+ *p++ = 53;
+ *p++ = 1;
+ *p++ = t;
+ if(t == DHCPREQUEST){
+ *p++ = 50;
+ *p++ = 4;
+ p = u32put(p, myip);
+ *p++ = 54;
+ *p++ = 4;
+ p = u32put(p, dhcpip);
+ }
+
+ *p++ = 0xff;
+
+ memset(u->edest, 0xff, 6);
+ memcpy(u->esrc, mac, 6);
+ u->sport = 68;
+ u->dport = 67;
+ u->idest = -1;
+ u->isrc = 0;
+ u->len = p - u->data;
+ udptx(r, u);
+}
+
+uchar *
+ethrx(void)
+{
+ while((*rxact & 1) == 0)
+ if(timertrig())
+ return nil;
+ return (uchar *) (*rxact & ~3);
+}
+
+void
+ethnext(void)
+{
+ *rxact &= ~1;
+ if((*rxact & 2) != 0)
+ rxact = rxdesc;
+ else
+ rxact += 2;
+}
+
+void
+arp(int op, uchar *edest, ulong idest)
+{
+ uchar *p;
+ static uchar broad[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+ p = txbuf;
+ if(edest == nil)
+ edest = broad;
+ memcpy(p, edest, 6);
+ memcpy(p + 6, mac, 6);
+ p[12] = 8;
+ p[13] = 6;
+ p += 14;
+ p = u32put(p, 0x00010800);
+ p = u32put(p, 0x06040000 | op);
+ memcpy(p, mac, 6);
+ p = u32put(p + 6, myip);
+ memcpy(p, edest, 6);
+ p = u32put(p + 6, idest);
+ ethtx(eth0, txbuf, p - txbuf);
+}
+
+void
+arpc(uchar *p)
+{
+ p += 14;
+ if(u32get(p) != 0x00010800 || p[4] != 6 || p[5] != 4 || p[6] != 0)
+ return;
+ switch(p[7]){
+ case 1:
+ if(myip != 0 && u32get(p + 24) == myip)
+ arp(2, p + 8, u32get(p + 14));
+ break;
+ case 2:
+ if(tftpip != 0 && u32get(p + 14) == tftpip)
+ memcpy(tmac, p + 8, 6);
+ break;
+ }
+}
+
+udp *
+udprx(void)
+{
+ uchar *p;
+ ulong v;
+ udp *u;
+
+ u = &urbuf;
+ for(;; ethnext()){
+ p = ethrx();
+ if(p == nil)
+ return nil;
+ if(p[12] != 8)
+ continue;
+ if(p[13] == 6){
+ arpc(p);
+ continue;
+ }
+ if(p[13] != 0)
+ continue;
+ p += ETHHEAD;
+ if((p[0] >> 4) != 4 || p[9] != 0x11)
+ continue;
+ v = u32get(p + 16);
+ if(v != (ulong) -1 && v != myip)
+ continue;
+ u->idest = v;
+ u->isrc = u32get(p + 12);
+ p += (p[0] & 0xf) << 2;
+ u->sport = p[0] << 8 | p[1];
+ u->dport = p[2] << 8 | p[3];
+ u->len = p[4] << 8 | p[5];
+ if(u->len < 8)
+ continue;
+ u->len -= 8;
+ if(u->len >= sizeof(u->data))
+ u->len = sizeof(u->data);
+ memcpy(u->data, p + 8, u->len);
+ ethnext();
+ return u;
+ }
+}
+
+void
+arpreq(void)
+{
+ uchar *p;
+
+ arp(1, nil, tftpip);
+ timeren(ARPTIMEOUT);
+ for(;; ethnext()){
+ p = ethrx();
+ if(p == nil){
+ print("ARP timeout\n");
+ timeren(ARPTIMEOUT);
+ arp(1, nil, tftpip);
+ }
+ if(p[12] != 8 || p[13] != 6)
+ continue;
+ arpc(p);
+ if(tmac[0] != 0xff)
+ break;
+ }
+ timeren(-1);
+}
+
+void
+dhcp(ulong *r)
+{
+ udp *u;
+ uchar *p;
+ uchar type;
+
+ xid = 0xdeadbeef;
+ tftpip = 0;
+ dhcppkg(r, DHCPDISCOVER);
+ timeren(DHCPTIMEOUT);
+ for(;;){
+ u = udprx();
+ if(u == nil){
+ timeren(DHCPTIMEOUT);
+ dhcppkg(r, DHCPDISCOVER);
+ print("DHCP timeout\n");
+ }
+ p = u->data;
+ if(u->dport != 68 || p[0] != 2 || u32get(p + 4) != xid || u32get(p + 236) != 0x63825363)
+ continue;
+ p += 240;
+ type = 0;
+ dhcpip = 0;
+ for(; p < u->data + u->len && *p != 0xff; p += 2 + p[1])
+ switch(*p){
+ case 53:
+ type = p[2];
+ break;
+ case 54:
+ dhcpip = u32get(p + 2);
+ break;
+ }
+ if(type != DHCPOFFER)
+ continue;
+ p = u->data;
+ if(p[108] == 0){
+ print("Offer from %I for %I with no boot file\n", dhcpip, u32get(p + 16));
+ continue;
+ }
+ myip = u32get(p + 16);
+ tftpip = u32get(p + 20);
+ memcpy(file, p + 108, 128);
+ print("Offer from %I for %I with boot file '%s' at %I\n", dhcpip, myip, file, tftpip);
+ break;
+ }
+ timeren(-1);
+ dhcppkg(r, DHCPREQUEST);
+}
+
+udp *
+tftppkg(void)
+{
+ udp *u;
+
+ u = &ubuf;
+ memcpy(u->edest, tmac, 6);
+ memcpy(u->esrc, mac, 6);
+ u->idest = tftpip;
+ u->isrc = myip;
+ u->sport = 69;
+ u->dport = 69;
+ return u;
+}
+
+void
+tftp(ulong *r, char *q, uintptr base)
+{
+ udp *u, *v;
+ uchar *p;
+ int bn, len;
+
+restart:
+ u = tftppkg();
+ p = u->data;
+ *p++ = 0;
+ *p++ = 1;
+ do
+ *p++ = *q;
+ while(*q++ != 0);
+ memcpy(p, "octet", 6);
+ p += 6;
+ u->len = p - u->data;
+ udptx(r, u);
+ timeren(TFTPTIMEOUT);
+
+ for(;;){
+ v = udprx();
+ if(v == nil){
+ print("TFTP timeout");
+ goto restart;
+ }
+ if(v->dport != 69 || v->isrc != tftpip || v->idest != myip)
+ continue;
+ if(v->data[0] != 0)
+ continue;
+ switch(v->data[1]){
+ case 3:
+ bn = v->data[2] << 8 | v->data[3];
+ len = v->len - 4;
+ if(len < 0)
+ continue;
+ if(len > 512)
+ len = 512;
+ memcpy((char*)base + ((bn - 1) << 9), v->data + 4, len);
+ if((bn & 127) == 0)
+ putc('.');
+ p = u->data;
+ *p++ = 0;
+ *p++ = 4;
+ *p++ = bn >> 8;
+ *p = bn;
+ u->len = 4;
+ udptx(r, u);
+ if(len < 512){
+ putc(10);
+ timeren(-1);
+ return;
+ }
+ timeren(TFTPTIMEOUT);
+ break;
+ case 5:
+ v->data[v->len - 1] = 0;
+ print("TFTP error: %s\n", v->data + 4);
+ timeren(-1);
+ return;
+ }
+ }
+}
+
+int
+netboot(void)
+{
+ ethinit(eth0);
+ myip = 0;
+ dhcp(eth0);
+ arpreq();
+ tftp(eth0, file, TZERO);
+ memset((void *) CONF, 0, CONFSIZE);
+ tftp(eth0, "/cfg/pxe/0ea7deadbeef", CONF);
+ return 1;
+}
diff --git a/sys/src/boot/zynq/qspi.c b/sys/src/boot/zynq/qspi.c
new file mode 100644
index 000000000..6134592ce
--- /dev/null
+++ b/sys/src/boot/zynq/qspi.c
@@ -0,0 +1,45 @@
+#include <u.h>
+#include "dat.h"
+#include "fns.h"
+
+enum {
+ QSPI_CFG,
+ QSPI_STATUS,
+ QSPI_EN = 5,
+ QSPI_TXD4 = 7,
+ QSPI_RXD,
+ QSPI_TXD1 = 32,
+ QSPI_TXD2,
+ QSPI_TXD3
+};
+
+#define QSPI0 ((void *) 0xE000D000)
+
+static u32int
+cmd(ulong *r, int sz, u32int c)
+{
+ if(sz == 4)
+ r[QSPI_TXD4] = c;
+ else
+ r[QSPI_TXD1 + sz - 1] = c;
+ r[QSPI_CFG] |= 1<<16;
+ while((r[QSPI_STATUS] & (1<<2|1<<4)) != (1<<2|1<<4))
+ ;
+ return r[QSPI_RXD];
+}
+
+void
+flash(void)
+{
+ ulong *r;
+
+ r = QSPI0;
+ r[QSPI_CFG] = 1<<31 | 1<<19 | 3<<6 | 1<<15 | 1<<14 | 1<<10 | 1<<3 | 1;
+ r[QSPI_CFG] &= ~(1<<10);
+ r[QSPI_EN] = 1;
+ cmd(r, 1, 0x06);
+// cmd(r, 3, 0xD8);
+ for(;;)
+ print("%x\n", cmd(r, 2, 0x05));
+
+}