netboot.c (4567B)
1 // Copyright 2022, Brian Swetland <swetland@frotz.net> 2 // Licensed under the Apache License, Version 2.0 3 4 #include <string.h> 5 #include <stdio.h> 6 7 #include <hw/riscv.h> 8 #include <hw/context.h> 9 #include <hw/intrinsics.h> 10 #include <hw/debug.h> 11 12 #include <hw/platform.h> 13 #include <hw/litex.h> 14 15 #include <net/ipv6.h> 16 #include <netboot.h> 17 18 #include "boot.h" 19 20 // we expect the supervisor program to be in memory after the end of the bootloader 21 #define SVC_ENTRY (DRAM_BASE + BOOTLOADER_SIZE) 22 23 #define uart_rd(a) io_rd32(UART0_BASE + LX_UART_ ## a) 24 #define uart_wr(a,v) io_wr32(UART0_BASE + LX_UART_ ## a, v) 25 #define eth_rd(a) io_rd32(ETHMAC_BASE + LX_ETHMAC_ ## a) 26 #define eth_wr(a,v) io_wr32(ETHMAC_BASE + LX_ETHMAC_ ## a, v) 27 28 void halt_peripherals(void) { 29 eth_wr(WR_EV_ENABLE, 0); 30 eth_wr(RD_EV_ENABLE, 0); 31 } 32 33 const char *nodename = "device"; 34 35 void query_reply(void *replyto) { 36 netboot_msg_t *msg = replyto; 37 unsigned len = snprintf((void*)msg->db, NB_DATA_MAX, "name=%s", nodename); 38 msg->cmd = NB_CMD_STATUS; 39 msg->arg = NB_OK; 40 net_tx_udp_reply(replyto, msg, NB_MSG_MIN + len + 1, NB_PORT_CTRL); 41 } 42 43 void pkt_rx_udp(void *data, unsigned len, unsigned port, int mcast) { 44 netboot_msg_t* msg = data; 45 if ((len < NB_MSG_MIN) || (len > NB_MSG_MAX) || (msg->magic != NB_MAGIC)) { 46 return; 47 } 48 49 if (port == NB_PORT_QUERY) { 50 unsigned nlen = strlen(nodename); 51 // query port only replies to valid queries that match 52 if ((msg->cmd != NB_CMD_QUERY) || 53 (len != (nlen + NB_MSG_MIN)) || 54 (memcmp(msg->db, nodename, nlen))) { 55 return; 56 } 57 query_reply(data); 58 return; 59 } 60 if (port != NB_PORT_CTRL) { 61 return; 62 } 63 64 switch (msg->cmd) { 65 case NB_CMD_QUERY: 66 xputc('Q'); 67 query_reply(data); 68 return; 69 case NB_CMD_WRITE: 70 xputc('.'); 71 // TODO: apply range limits? 72 memcpy((void*) msg->arg, msg->db, len - NB_MSG_MIN); 73 msg->cmd = NB_CMD_STATUS; 74 msg->arg = NB_OK; 75 net_tx_udp_reply(data, msg, NB_MSG_MIN, NB_PORT_CTRL); 76 return; 77 case NB_CMD_EXEC: { 78 xputc('!'); 79 uint32_t entry = msg->arg; 80 msg->cmd = NB_CMD_STATUS; 81 msg->arg = NB_OK; 82 net_tx_udp_reply(data, msg, NB_MSG_MIN, NB_PORT_CTRL); 83 84 csr_write(CSR_MSTATUS, PRIV_S << MSTATUS_MPP_SHIFT); 85 xprintf("\n\n[ execution starts at 0x%08x ]\n\n", entry); 86 exit_mode_m(0, 0, entry, 0); 87 return; 88 } 89 default: 90 xputc('E'); 91 msg->cmd = NB_CMD_STATUS; 92 msg->arg = NB_ERR_BADCMD; 93 net_tx_udp_reply(data, msg, NB_MSG_MIN, NB_PORT_CTRL); 94 break; 95 } 96 } 97 98 uint8_t rxbuf[1536] = { 0, }; 99 100 void eth_rx(uint8_t *rxb, unsigned rxlen) { 101 if (rxlen > 1534) return; 102 memcpy(rxbuf + 2, rxb, rxlen); 103 net_rx_eth(rxbuf, rxlen + 2); 104 } 105 106 107 static unsigned txslot; 108 109 void eth_tx(void *txb, unsigned txlen) { 110 if (txlen > 1534) return; 111 112 while (eth_rd(RD_READY) != 1) ; 113 114 memcpy((void*)(ETHMAC_SRAM_BASE + ETHMAC_SLOT_SIZE * (ETHMAC_RX_SLOTS + txslot)), txb, txlen); 115 116 eth_wr(RD_SLOT, txslot); 117 eth_wr(RD_LEN, txlen); 118 eth_wr(RD_START, 1); 119 120 txslot ^= 1; 121 } 122 123 void eth_init(void) { 124 eth_wr(WR_EV_ENABLE, 0); 125 eth_wr(RD_EV_ENABLE, 0); 126 127 eth_wr(WR_EV_PENDING, 1); 128 eth_wr(RD_EV_PENDING, 1); 129 130 txslot = 0; 131 eth_wr(RD_SLOT, txslot); 132 133 eth_wr(WR_EV_ENABLE, 1); 134 eth_wr(RD_EV_ENABLE, 1); 135 } 136 137 void eth_handle_rx(void) { 138 if (eth_rd(WR_EV_PENDING) & 1) { 139 uint32_t slot = eth_rd(WR_SLOT); 140 uint8_t *rxb = (void*) (ETHMAC_SRAM_BASE + ETHMAC_SLOT_SIZE * slot); 141 eth_rx(rxb, eth_rd(WR_LEN)); 142 eth_wr(WR_EV_PENDING, 1); 143 } 144 } 145 146 void netboot(void) { 147 uint8_t mac[6] = { 0x42,0x42,0x10,0x20,0x30,0x40 }; 148 net_init(mac); 149 eth_init(); 150 for (;;) { 151 eth_handle_rx(); 152 } 153 } 154 155 void mach_exception_handler(eframe_t *ef) { 156 xprintf("\n** MACHINE EXCEPTION **\n"); 157 xprint_m_exception(ef); 158 xprintf("\nHALT\n"); 159 for (;;) ; 160 } 161 162 // interrupts and exceptions to delegate to supervisor mode 163 #define INT_LIST (INTb_SVC_SW|INTb_SVC_TIMER|INTb_SVC_EXTERN) 164 //#define EXC_LIST (EXCb_ECALL_UMODE) 165 #define EXC_LIST (0xFFFF) 166 167 void start(uint32_t hartid, uint32_t fdt) { 168 xprintf("\n** Frobozz Magic (Network) Bootloader v0.2 **\n\n"); 169 170 int qemu = (csr_read(CSR_MVENDORID) == 0); 171 172 // set mach exception vector and stack pointer 173 csr_write(CSR_MTVEC, ((uintptr_t) mach_exception_entry) ); 174 175 // use the free ram below the supervisor entry as our exception stack 176 csr_write(CSR_MSCRATCH, SVC_ENTRY); 177 178 // QEMU currently emulates memory protection, which we must appease 179 if (qemu) { 180 // U/S allow access to all memory 181 csr_write(CSR_PMPCFG(0), PMP_CFG_A_TOR | PMP_CFG_X | PMP_CFG_W | PMP_CFG_R); 182 csr_write(CSR_PMPADDR(0), 0xFFFFFFFF); 183 } 184 185 // delegate interrupts and exceptions 186 csr_set(CSR_MIDELEG, INT_LIST); 187 csr_set(CSR_MEDELEG, EXC_LIST); 188 189 netboot(); 190 } 191