diff --git a/part14-ethernet/Makefile b/part14-ethernet/Makefile index 1e3608f..ea9f2be 100644 --- a/part14-ethernet/Makefile +++ b/part14-ethernet/Makefile @@ -2,7 +2,7 @@ CFILES = $(wildcard *.c lib/*.c kernel/*.c net/*.c) SFILES = $(wildcard boot/*.S lib/*.S kernel/*.S) OFILES = $(CFILES:.c=.o) $(SFILES:.S=.o) LLVMPATH = /opt/homebrew/opt/llvm/bin -CLANGFLAGS = -Wall -O2 -ffreestanding -nostdlib -mcpu=cortex-a72+nosimd +CLANGFLAGS = -Wall -O0 -ffreestanding -nostdlib -mcpu=cortex-a72+nosimd all: clean kernel8.img diff --git a/part14-ethernet/Makefile.gcc b/part14-ethernet/Makefile.gcc index 674751a..d8316fd 100644 --- a/part14-ethernet/Makefile.gcc +++ b/part14-ethernet/Makefile.gcc @@ -1,7 +1,7 @@ CFILES = $(wildcard *.c lib/*.c kernel/*.c net/*.c) SFILES = $(wildcard boot/*.S lib/*.S kernel/*.S) OFILES = $(CFILES:.c=.o) $(SFILES:.S=.o) -GCCFLAGS = -Wall -O2 -ffreestanding -nostdlib -nostartfiles +GCCFLAGS = -Wall -O0 -ffreestanding -nostdlib -nostartfiles GCCPATH = ../../gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf/bin all: clean kernel8.img diff --git a/part14-ethernet/include/spi.h b/part14-ethernet/include/spi.h index c5fca8b..6454a72 100644 --- a/part14-ethernet/include/spi.h +++ b/part14-ethernet/include/spi.h @@ -1,5 +1,5 @@ void spi_init(); -void spi_send_recv(unsigned char *sbuffer, unsigned char *rbuffer, unsigned int size); +unsigned int spi_send_recv(unsigned char *sbuffer, unsigned char *rbuffer, unsigned int size); void spi_send(unsigned char *data, unsigned int size); void spi_recv(unsigned char *data, unsigned int size); void spi_send_no_selection(unsigned char command); diff --git a/part14-ethernet/kernel/arp.c b/part14-ethernet/kernel/arp.c new file mode 100644 index 0000000..8cb2a94 --- /dev/null +++ b/part14-ethernet/kernel/arp.c @@ -0,0 +1,201 @@ +#include "../net/enc28j60.h" +#include "../include/fb.h" + +// Structure for Ethernet header + +typedef struct { + uint8_t DestAddrs[6]; + uint8_t SrcAddrs[6]; + uint16_t type; +} EtherNetII; + +// Ethernet packet types + +#define ARPPACKET 0x0806 +#define IPPACKET 0x0800 + +// Structure for an ARP Packet + +typedef struct { + EtherNetII eth; + uint16_t hardware; + uint16_t protocol; + uint8_t hardwareSize; + uint8_t protocolSize; + uint16_t opCode; + uint8_t senderMAC[6]; + uint8_t senderIP[4]; + uint8_t targetMAC[6]; + uint8_t targetIP[4]; +} ARP; + +// ARP OpCodes + +#define ARPREPLY 0x0002 +#define ARPREQUEST 0x0001 + +// ARP hardware types + +#define ETHERNET 0x0001 + +// MAC address to be assigned to the ENC28J60 + +uint8_t myMAC[6] = { 0xc0, 0xff, 0xee, 0xc0, 0xff, 0xee }; + +// Router MAC is not known to start with, and requires an ARP reply to find out + +uint8_t routerMAC[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +// IP address to be assigned to the ENC28J60 + +uint8_t deviceIP[4] = { 192, 168, 0, 66 }; + +// IP Address of the router, whose hardware address we will find using the ARP request + +uint8_t routerIP[4] = { 192, 168, 0, 1 }; + +// HELPER FUNCTIONS + +void *memset(void *dest, uint8_t val, uint16_t len) +{ + uint8_t *ptr = dest; + while (len-- > 0) + *ptr++ = val; + return dest; +} + +void *memcpy(void *dest, const void *src, uint16_t len) +{ + uint8_t *d = dest; + const uint8_t *s = src; + while (len--) + *d++ = *s++; + return dest; +} + +uint8_t memcmp(void *str1, void *str2, unsigned count) +{ + uint8_t *s1 = str1; + uint8_t *s2 = str2; + + while (count-- > 0) + { + if (*s1++ != *s2++) + return s1[-1] < s2[-1] ? -1 : 1; + } + + return 0; +} + +// MAIN FUNCTIONS + +void SendArpPacket(uint8_t *targetIP, uint8_t *deviceMAC) +{ + /* Parameters: + * targetIP - The target IP Address for the ARP request (the one whose hardware + * address we want) + * deviceMAC - The MAC address of the ENC28J60, i.e. the source MAC for the ARP + * request + */ + + ARP arpPacket; + + // The source of the packet will be the ENC28J60 MAC address + memcpy(arpPacket.eth.SrcAddrs, deviceMAC, 6); + + // The destination is broadcast - a MAC address of FF:FF:FF:FF:FF:FF */ + memset(arpPacket.eth.DestAddrs, 0xFF, 6); + + arpPacket.eth.type = ARPPACKET; + arpPacket.hardware = ETHERNET; + + // We want an IP address resolved + + arpPacket.protocol = IPPACKET; + arpPacket.hardwareSize = 0x06; // sizeof(deviceMAC); + arpPacket.protocolSize = 0x04; // sizeof(deviceIP); + arpPacket.opCode = ARPREQUEST; + + // Target MAC is set to 0 as it is unknown + memset(arpPacket.targetMAC, 0, 6); + + // Sender MAC is the ENC28J60's MAC address + memcpy(arpPacket.senderMAC, deviceMAC, 6); + + // The target IP is the IP address we want resolved + memcpy(arpPacket.targetIP, targetIP, 4); + + // Check if the last reply has come from an IP address that we want i.e. someone else is already using it + if (!memcmp(targetIP, deviceIP, 4)) { + // Yes, someone is using our IP so set the sender IP to 0.0.0.0 + memset(arpPacket.senderIP, 0, 4); + } else { + // No, nobody is using our IP so we can use it confidently + memcpy(arpPacket.senderIP, deviceIP, 4); + } + + // Send the packet + ENC_WriteBuffer((unsigned char*)&arpPacket, sizeof(ARP)); +} + +void arp_test(void) +{ + unsigned int tries = 0; + ARP checkPacket; + + debugstr("Sending ARP request... "); + + SendArpPacket(routerIP, myMAC); + + debugstr("done."); + debugcrlf(); + + debugstr("Waiting for ARP response... "); + debugcrlf(); + + while (tries < 1000) { + if (ENC_ReadBuffer((unsigned char *)&checkPacket, sizeof(ARP)) == 0) { + continue; + } + if (!memcmp(checkPacket.senderIP, routerIP, 4)) { + // Success! We have found our router's MAC address + + memcpy(routerMAC, checkPacket.senderMAC, 6); + debugstr("Router MAC is "); + debughex(routerMAC[0]); + debughex(routerMAC[1]); + debughex(routerMAC[2]); + debughex(routerMAC[3]); + debughex(routerMAC[4]); + debughex(routerMAC[5]); + debugcrlf(); + } + tries++; + } + + debugstr("Timed out."); + debugcrlf(); +} + +void init_network(void) +{ + ENC_HandleTypeDef handle; + + debugstr("Setting MAC address to C0:FF:EE:C0:FF:EE."); + debugcrlf(); + + handle.Init.DuplexMode = ETH_MODE_FULLDUPLEX; + handle.Init.MACAddr = myMAC; + handle.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE; + handle.Init.InterruptEnableBits = 0; + + debugstr("Starting network up."); + debugcrlf(); + + if (!ENC_Start(&handle)) { + debugstr("Could not initialise network card."); + } else { + debugstr("Network card successfully initialised."); + } + debugcrlf(); +} diff --git a/part14-ethernet/kernel/irq.c b/part14-ethernet/kernel/irq.c index 3ab82d1..bc2276a 100644 --- a/part14-ethernet/kernel/irq.c +++ b/part14-ethernet/kernel/irq.c @@ -1,4 +1,27 @@ #include "kernel.h" +#include "../include/fb.h" + +char *entry_error_messages[] = { + "SYNC_INVALID_EL1t", + "IRQ_INVALID_EL1t", + "FIQ_INVALID_EL1t", + "ERROR_INVALID_EL1T", + + "SYNC_INVALID_EL1h", + "IRQ_INVALID_EL1h", + "FIQ_INVALID_EL1h", + "ERROR_INVALID_EL1h", + + "SYNC_INVALID_EL0_64", + "IRQ_INVALID_EL0_64", + "FIQ_INVALID_EL0_64", + "ERROR_INVALID_EL0_64", + + "SYNC_INVALID_EL0_32", + "IRQ_INVALID_EL0_32", + "FIQ_INVALID_EL0_32", + "ERROR_INVALID_EL0_32" +}; void enable_interrupt_controller() { REGS_IRQ->irq0_enable_0 = SYS_TIMER_IRQ_1 | SYS_TIMER_IRQ_3; @@ -8,6 +31,14 @@ void disable_interrupt_controller() { REGS_IRQ->irq0_enable_0 = 0; } +void show_invalid_entry_message(int type, unsigned long esr, unsigned long address) { + debugstr(entry_error_messages[type]); + debugstr(" ESR: "); + debughex(esr); + debugstr("Addr: "); + debughex(address); +} + void handle_irq() { unsigned int irq = REGS_IRQ->irq0_pending_0; diff --git a/part14-ethernet/kernel/irqentry.S b/part14-ethernet/kernel/irqentry.S index d24ae00..28d0b94 100644 --- a/part14-ethernet/kernel/irqentry.S +++ b/part14-ethernet/kernel/irqentry.S @@ -67,11 +67,7 @@ mov x0, #\type mrs x1, esr_el1 mrs x2, elr_el1 - - // We could pass this to a function to print an error here - // e.g. bl show_invalid_entry_message - // - // For now we'll just hang + bl show_invalid_entry_message b err_hang .endm diff --git a/part14-ethernet/kernel/kernel.c b/part14-ethernet/kernel/kernel.c index 139c7a0..e0b4a0d 100644 --- a/part14-ethernet/kernel/kernel.c +++ b/part14-ethernet/kernel/kernel.c @@ -8,10 +8,10 @@ void initProgress(void) { drawRect(0, 0, 301, 50, 0x0f, 0); - drawString(309, 21, "Core 0", 0x0f, 1); + drawString(309, 21, "Core 1", 0x0f, 1); drawRect(0, 60, 301, 110, 0x0f, 0); - drawString(309, 81, "Core 1", 0x0f, 1); + drawString(309, 81, "Core 2", 0x0f, 1); drawRect(0, 120, 301, 170, 0x0f, 0); drawString(309, 141, "Timer 1", 0x0f, 1); @@ -28,21 +28,24 @@ void drawProgress(unsigned int core, unsigned int val) { if (val > 0) drawRect(1, (60 * core) + 1, (val * 3), (60 * core) + 49, col, 1); } -void core0_main(void) -{ - unsigned int core0_val = 0; +unsigned int c2_done = 0; - while (core0_val <= 100) { +void core2_main(void) +{ + unsigned int core2_val = 0; + + clear_core2(); // Only run once + + while (core2_val <= 100) { wait_msec(0x100000); - drawProgress(0, core0_val); - core0_val++; + drawProgress(1, core2_val); + core2_val++; } - debugstr("Core 0 done."); + debugstr("Core 2 done."); debugcrlf(); - irq_disable(); - disable_interrupt_controller(); + c2_done = 1; while(1); } @@ -55,7 +58,7 @@ void core1_main(void) while (core1_val <= 100) { wait_msec(0x3FFFF); - drawProgress(1, core1_val); + drawProgress(0, core1_val); core1_val++; } @@ -132,37 +135,6 @@ unsigned int HAL_GetTick(void) { return timer_get_ticks(); } -void init_network(void) -{ - ENC_HandleTypeDef handle; - - unsigned char macaddr[6]; - macaddr[0] = 0xc0; - macaddr[1] = 0xff; - macaddr[2] = 0xee; - macaddr[3] = 0xc0; - macaddr[4] = 0xff; - macaddr[5] = 0xee; - - debugstr("Setting MAC address to C0:FF:EE:C0:FF:EE."); - debugcrlf(); - - handle.Init.DuplexMode = ETH_MODE_FULLDUPLEX; - handle.Init.MACAddr = macaddr; - handle.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE; - handle.Init.InterruptEnableBits = 0; - - debugstr("Starting network up."); - debugcrlf(); - - if (!ENC_Start(&handle)) { - debugstr("Could not initialise network card."); - } else { - debugstr("Network card successfully initialised."); - } - debugcrlf(); -} - void main(void) { fb_init(); @@ -172,20 +144,35 @@ void main(void) initProgress(); + // Kick it off on core 1&2 + + start_core1(core1_main); + start_core2(core2_main); + // Kick off the timers irq_init_vectors(); enable_interrupt_controller(); irq_enable(); timer_init(); + + // Test the network card + spi_init(); init_network(); + arp_test(); - // Kick it off on core 1 + // The work is done - wait for timers to get done - start_core1(core1_main); + debugstr("Core 0 done."); + debugcrlf(); - // Loop endlessly + while (!c2_done); - core0_main(); + // Disable IRQs and loop endlessly + + irq_disable(); + disable_interrupt_controller(); + + while(1); } diff --git a/part14-ethernet/kernel/kernel.h b/part14-ethernet/kernel/kernel.h index 5ff14d5..acaa894 100644 --- a/part14-ethernet/kernel/kernel.h +++ b/part14-ethernet/kernel/kernel.h @@ -49,3 +49,6 @@ unsigned long timer_get_ticks(); void timer_sleep(unsigned int ms); void HAL_Delay(volatile unsigned int Delay); unsigned int HAL_GetTick(void); + +void init_network(void); +void arp_test(void); diff --git a/part14-ethernet/lib/spi.c b/part14-ethernet/lib/spi.c index c6b0bd5..d71fc22 100644 --- a/part14-ethernet/lib/spi.c +++ b/part14-ethernet/lib/spi.c @@ -52,7 +52,7 @@ void spi_chip_select(unsigned char chip_select) { REGS_SPI0->cs = (REGS_SPI0->cs & ~CS_CS) | (chip_select << CS_CS__SHIFT); } -void spi_send_recv(unsigned char *sbuffer, unsigned char *rbuffer, unsigned int size) { +unsigned int spi_send_recv(unsigned char *sbuffer, unsigned char *rbuffer, unsigned int size) { REGS_SPI0->data_length = size; REGS_SPI0->cs = REGS_SPI0->cs | CS_CLEAR_RX | CS_CLEAR_TX | CS_TA; @@ -89,6 +89,7 @@ void spi_send_recv(unsigned char *sbuffer, unsigned char *rbuffer, unsigned int } REGS_SPI0->cs = (REGS_SPI0->cs & ~CS_TA); + return read_count; } void spi_send(unsigned char *data, unsigned int size) { diff --git a/part14-ethernet/net/enc28j60.c b/part14-ethernet/net/enc28j60.c index 52ba6a5..57189d3 100644 --- a/part14-ethernet/net/enc28j60.c +++ b/part14-ethernet/net/enc28j60.c @@ -993,7 +993,7 @@ void ENC_WriteBuffer(void *buffer, uint16_t buflen) } /**************************************************************************** - * Function: enc_rdbuffer + * Function: ENC_ReadBuffer * * Description: * Read a buffer of data. @@ -1003,15 +1003,17 @@ void ENC_WriteBuffer(void *buffer, uint16_t buflen) * buflen - The number of bytes to read * * Returned Value: - * None + * read_count - Number of bytes received * * Assumptions: * Read pointer is set to the correct address * ****************************************************************************/ -static void enc_rdbuffer(void *buffer, int16_t buflen) +uint8_t ENC_ReadBuffer(void *buffer, uint16_t buflen) { + uint8_t read_count = 0; + /* Select ENC28J60 chip */ ENC_SPI_Select(true); @@ -1022,9 +1024,11 @@ static void enc_rdbuffer(void *buffer, int16_t buflen) /* Then read the buffer data */ - ENC_SPI_SendBuf(NULL, buffer, buflen); + read_count = ENC_SPI_SendBuf(NULL, buffer, buflen); /* De-select ENC28J60 chip: done in ENC_SPI_SendBuf callback */ + + return read_count; } /**************************************************************************** @@ -1187,7 +1191,7 @@ void ENC_Transmit(ENC_HandleTypeDef *handle) enc_wrbreg(handle, ENC_ERDPTL, addtTsv4 & 0xff); enc_wrbreg(handle, ENC_ERDPTH, addtTsv4 >> 8); - enc_rdbuffer(&tsv4, 1); + ENC_ReadBuffer(&tsv4, 1); regval = enc_rdgreg(ENC_EIR); if (!(regval & EIR_TXERIF) || !(tsv4 & TSV_LATECOL)) { break; @@ -1242,7 +1246,7 @@ bool ENC_GetReceivedFrame(ENC_HandleTypeDef *handle) * and wrap to the beginning of the read buffer as necessary) */ - enc_rdbuffer(rsv, 6); + ENC_ReadBuffer(rsv, 6); /* Decode the new next packet pointer, and the RSV. The * RSV is encoded as: @@ -1281,7 +1285,7 @@ bool ENC_GetReceivedFrame(ENC_HandleTypeDef *handle) * end_rdbuffer (above). */ - enc_rdbuffer(handle->RxFrameInfos.buffer, handle->RxFrameInfos.length); + ENC_ReadBuffer(handle->RxFrameInfos.buffer, handle->RxFrameInfos.length); } } diff --git a/part14-ethernet/net/enc28j60.h b/part14-ethernet/net/enc28j60.h index 7c378c0..c34152a 100644 --- a/part14-ethernet/net/enc28j60.h +++ b/part14-ethernet/net/enc28j60.h @@ -98,10 +98,10 @@ void ENC_SPI_Send(uint8_t command); * Implement SPI buffer send and receive. Must be provided by user code * param master2slave: data to be sent from host to ENC28J60, can be NULL if we only want to receive data from slave * param slave2master: answer from ENC28J60 to host, can be NULL if we only want to send data to slave - * retval none + * retval read_count: number of bytes read (if applicable) */ -void ENC_SPI_SendBuf(uint8_t *master2slave, uint8_t *slave2master, uint16_t bufferSize); +unsigned int ENC_SPI_SendBuf(uint8_t *master2slave, uint8_t *slave2master, uint16_t bufferSize); /* Exported types ------------------------------------------------------------*/ /** @defgroup ETH_Exported_Types ETH Exported Types @@ -686,6 +686,7 @@ int8_t ENC_RestoreTXBuffer(ENC_HandleTypeDef *handle, uint16_t len); ****************************************************************************/ void ENC_WriteBuffer(void *buffer, uint16_t buflen); +uint8_t ENC_ReadBuffer(void *buffer, uint16_t buflen); /**************************************************************************** * Function: ENC_Transmit diff --git a/part14-ethernet/net/encspi.c b/part14-ethernet/net/encspi.c index 524f73f..91c41b1 100644 --- a/part14-ethernet/net/encspi.c +++ b/part14-ethernet/net/encspi.c @@ -4,10 +4,14 @@ void ENC_SPI_Select(unsigned char truefalse) { spi_chip_select(!truefalse); // If it's true, select 0 (the ENC), if false, select 1 (i.e. deselect the ENC) } -void ENC_SPI_SendBuf(unsigned char *master2slave, unsigned char *slave2master, unsigned short bufferSize) { +unsigned int ENC_SPI_SendBuf(unsigned char *master2slave, unsigned char *slave2master, unsigned short bufferSize) { + unsigned int read_count = 0; + spi_chip_select(0); - spi_send_recv(master2slave, slave2master, bufferSize); + read_count = spi_send_recv(master2slave, slave2master, bufferSize); spi_chip_select(1); // De-select the ENC + + return read_count; } void ENC_SPI_Send(unsigned char command) {