| 1 | #include <compiler.h> |
| 2 | #include <console/printf.h> |
| 3 | #include <drivers/e1000.h> |
| 4 | #include <drivers/mmio.h> |
| 5 | #include <drivers/pci.h> |
| 6 | #include <mem/alloc.h> |
| 7 | #include <mem/alloc_or_die.h> |
| 8 | #include <mem/page.h> |
| 9 | #include <mem/pmm.h> |
| 10 | #include <mem/vmm.h> |
| 11 | #include <stdbool.h> |
| 12 | #include <stdint.h> |
| 13 | #include <string.h> |
| 14 | #include <time/spin_sleep.h> |
| 15 | |
| 16 | LOG_HANDLE_DECLARE_DEFAULT(e1000); |
| 17 | LOG_SITE_DECLARE_DEFAULT(e1000); |
| 18 | |
| 19 | #define E1000_MAX_TX_PACKET_SIZE 1518 |
| 20 | #define REG32(dev, offset) (&(dev->regs[(offset) / 4U])) |
| 21 | |
| 22 | static void e1000_reset(struct e1000_device *dev) { |
| 23 | mmio_write_32(REG32(dev, E1000_REG_CTRL), E1000_CTRL_RST); |
| 24 | sleep_spin_ms(msec: 1); |
| 25 | } |
| 26 | |
| 27 | static void e1000_setup_tx_ring(struct e1000_device *dev) { |
| 28 | uint64_t space = sizeof(struct e1000_tx_desc) * E1000_NUM_TX_DESC; |
| 29 | dev->tx_descs_phys = pmm_alloc_page(); |
| 30 | dev->tx_descs = mmio_map(phys: dev->tx_descs_phys, size: space); |
| 31 | memset(dev->tx_descs, 0, space); |
| 32 | |
| 33 | for (int i = 0; i < E1000_NUM_TX_DESC; i++) { |
| 34 | dev->tx_buffers[i] = kmalloc_or_die(2048); |
| 35 | |
| 36 | dev->tx_descs[i].addr = |
| 37 | vmm_get_phys((uintptr_t) dev->tx_buffers[i], VMM_FLAG_NONE); |
| 38 | dev->tx_descs[i].status = E1000_TXD_STAT_DD; |
| 39 | } |
| 40 | |
| 41 | mmio_write_32(REG32(dev, E1000_REG_TDBAL), |
| 42 | value: (uint32_t) (dev->tx_descs_phys & 0xFFFFFFFF)); |
| 43 | mmio_write_32(REG32(dev, E1000_REG_TDBAH), |
| 44 | value: (uint32_t) (dev->tx_descs_phys >> 32)); |
| 45 | mmio_write_32(REG32(dev, E1000_REG_TDLEN), |
| 46 | E1000_NUM_TX_DESC * sizeof(struct e1000_tx_desc)); |
| 47 | mmio_write_32(REG32(dev, E1000_REG_TDH), value: 0); |
| 48 | mmio_write_32(REG32(dev, E1000_REG_TDT), value: 0); |
| 49 | dev->tx_tail = 0; |
| 50 | |
| 51 | mmio_write_32(REG32(dev, E1000_REG_TCTL), |
| 52 | E1000_TCTL_EN | E1000_TCTL_PSP | |
| 53 | (0x10 << E1000_TCTL_CT_SHIFT) | |
| 54 | (0x40 << E1000_TCTL_COLD_SHIFT)); |
| 55 | } |
| 56 | |
| 57 | static void e1000_setup_rx_ring(struct e1000_device *dev) { |
| 58 | uint64_t space = sizeof(struct e1000_rx_desc) * E1000_NUM_RX_DESC; |
| 59 | dev->rx_descs_phys = pmm_alloc_page(); |
| 60 | dev->rx_descs = mmio_map(phys: dev->rx_descs_phys, size: space); |
| 61 | memset(dev->rx_descs, 0, space); |
| 62 | |
| 63 | for (int i = 0; i < E1000_NUM_RX_DESC; i++) { |
| 64 | dev->rx_buffers[i] = kmalloc_or_die(E1000_RX_BUF_SIZE); |
| 65 | |
| 66 | dev->rx_descs[i].addr = |
| 67 | vmm_get_phys((uintptr_t) dev->rx_buffers[i], VMM_FLAG_NONE); |
| 68 | dev->rx_descs[i].status = 0; |
| 69 | } |
| 70 | |
| 71 | mmio_write_32(REG32(dev, E1000_REG_RDBAL), |
| 72 | value: (uint32_t) (dev->rx_descs_phys & 0xFFFFFFFF)); |
| 73 | mmio_write_32(REG32(dev, E1000_REG_RDBAH), |
| 74 | value: (uint32_t) (dev->rx_descs_phys >> 32)); |
| 75 | mmio_write_32(REG32(dev, E1000_REG_RDLEN), |
| 76 | E1000_NUM_RX_DESC * sizeof(struct e1000_rx_desc)); |
| 77 | mmio_write_32(REG32(dev, E1000_REG_RDH), value: 0); |
| 78 | mmio_write_32(REG32(dev, E1000_REG_RDT), E1000_NUM_RX_DESC - 1); |
| 79 | dev->rx_tail = E1000_NUM_RX_DESC - 1; |
| 80 | |
| 81 | mmio_write_32(REG32(dev, E1000_REG_RCTL), |
| 82 | E1000_RCTL_EN | E1000_RCTL_BAM | E1000_RCTL_SECRC); |
| 83 | } |
| 84 | |
| 85 | int e1000_send_packet(struct e1000_device *dev, const void *data, |
| 86 | uint64_t len) { |
| 87 | if (len > E1000_MAX_TX_PACKET_SIZE) { |
| 88 | return -1; |
| 89 | } |
| 90 | |
| 91 | uint32_t next = dev->tx_tail; |
| 92 | struct e1000_tx_desc *desc = &dev->tx_descs[next]; |
| 93 | |
| 94 | if (!(desc->status & E1000_TXD_STAT_DD)) { |
| 95 | return -1; |
| 96 | } |
| 97 | |
| 98 | memcpy(dev->tx_buffers[next], data, len); |
| 99 | |
| 100 | desc->length = (uint16_t) len; |
| 101 | desc->cmd = E1000_TXD_CMD_EOP | E1000_TXD_CMD_IFCS | E1000_TXD_CMD_RS; |
| 102 | desc->status = 0; |
| 103 | |
| 104 | dev->tx_tail = (next + 1) % E1000_NUM_TX_DESC; |
| 105 | mmio_write_32(REG32(dev, E1000_REG_TDT), value: dev->tx_tail); |
| 106 | |
| 107 | return 0; |
| 108 | } |
| 109 | |
| 110 | static inline uint16_t htons(uint16_t hostshort) { |
| 111 | return (hostshort << 8) | (hostshort >> 8); |
| 112 | } |
| 113 | |
| 114 | static inline uint32_t htonl(uint32_t hostlong) { |
| 115 | return ((hostlong << 24) & 0xFF000000) | ((hostlong << 8) & 0x00FF0000) | |
| 116 | ((hostlong >> 8) & 0x0000FF00) | ((hostlong >> 24) & 0x000000FF); |
| 117 | } |
| 118 | |
| 119 | static uint16_t checksum(void *data, int len) { |
| 120 | uint32_t sum = 0; |
| 121 | uint16_t *ptr = (uint16_t *) data; |
| 122 | while (len > 1) { |
| 123 | sum += *ptr++; |
| 124 | len -= 2; |
| 125 | } |
| 126 | if (len > 0) |
| 127 | sum += *((uint8_t *) ptr); |
| 128 | while (sum >> 16) |
| 129 | sum = (sum & 0xFFFF) + (sum >> 16); |
| 130 | return (uint16_t) (~sum); |
| 131 | } |
| 132 | |
| 133 | void send_hardcoded_ping(struct e1000_device *dev) { |
| 134 | uint8_t packet[14 + 20 + 8 + 32]; |
| 135 | memset(packet, 0, sizeof(packet)); |
| 136 | |
| 137 | struct eth_hdr *eth = (void *) packet; |
| 138 | struct ipv4_hdr *ip = (void *) (packet + 14); |
| 139 | struct icmp_hdr *icmp = (void *) (packet + 14 + 20); |
| 140 | uint8_t *payload = packet + 14 + 20 + 8; |
| 141 | |
| 142 | uint8_t src_mac[6] = {0x52, 0x54, 0x00, 0x12, 0x34, 0x56}; |
| 143 | uint8_t dest_mac[6] = {0x52, 0x54, 0x00, 0x8e, 0x61, 0xf4}; |
| 144 | ip->dest_ip = htonl(hostlong: 0xC0A87A01); // 192.168.122.1 |
| 145 | ip->src_ip = htonl(hostlong: 0xC0A87A64); // 192.168.122.100 |
| 146 | |
| 147 | memcpy(eth->dest, dest_mac, 6); |
| 148 | memcpy(eth->src, src_mac, 6); |
| 149 | eth->ethertype = htons(hostshort: 0x0800); |
| 150 | |
| 151 | ip->version_ihl = (4 << 4) | 5; |
| 152 | ip->tos = 0; |
| 153 | ip->total_length = htons(hostshort: 20 + 8 + 32); |
| 154 | ip->id = htons(hostshort: 0x1234); |
| 155 | ip->flags_fragment = 0; |
| 156 | ip->ttl = 64; |
| 157 | ip->protocol = 1; |
| 158 | ip->checksum = 0; |
| 159 | ip->checksum = checksum(data: ip, len: 20); |
| 160 | |
| 161 | icmp->type = 8; // Echo request |
| 162 | icmp->code = 0; |
| 163 | icmp->identifier = htons(hostshort: 0x1); |
| 164 | icmp->sequence = htons(hostshort: 0x1); |
| 165 | memset(payload, 0xAA, 32); // dummy payload |
| 166 | |
| 167 | icmp->checksum = 0; |
| 168 | icmp->checksum = checksum(data: icmp, len: 8 + 32); |
| 169 | |
| 170 | e1000_send_packet(dev, data: packet, len: sizeof(packet)); |
| 171 | } |
| 172 | |
| 173 | bool e1000_init(struct pci_device *pci, struct e1000_device *dev) { |
| 174 | memset(dev, 0, sizeof(*dev)); |
| 175 | |
| 176 | dev->bus = pci->bus; |
| 177 | dev->device = pci->dev; |
| 178 | dev->function = pci->function; |
| 179 | |
| 180 | e1000_log(LOG_INFO, "Found device at %02x:%02x.%02x" , pci->bus, pci->dev, |
| 181 | pci->function); |
| 182 | |
| 183 | uint32_t bar = pci_read(bus: dev->bus, slot: dev->device, func: dev->function, PCI_BAR0); |
| 184 | if (bar & 0x1) |
| 185 | return false; // Not MMIO |
| 186 | |
| 187 | uint32_t phys_addr = bar & ~0xF; |
| 188 | |
| 189 | pci_write(bus: dev->bus, slot: dev->device, func: dev->function, PCI_BAR0, value: 0xFFFFFFFF); |
| 190 | uint32_t bar_mask = |
| 191 | pci_read(bus: dev->bus, slot: dev->device, func: dev->function, PCI_BAR0); |
| 192 | pci_write(bus: dev->bus, slot: dev->device, func: dev->function, PCI_BAR0, value: bar); |
| 193 | |
| 194 | uint64_t mmio_size = ~(bar_mask & ~0xF) + 1; |
| 195 | if (mmio_size == 0 || mmio_size > (1 << 24)) |
| 196 | return false; |
| 197 | |
| 198 | dev->regs = mmio_map(phys: phys_addr, size: mmio_size); |
| 199 | if (!dev->regs) |
| 200 | return false; |
| 201 | |
| 202 | e1000_reset(dev); |
| 203 | |
| 204 | e1000_setup_tx_ring(dev); |
| 205 | e1000_setup_rx_ring(dev); |
| 206 | send_hardcoded_ping(dev); |
| 207 | e1000_log(LOG_INFO, "Device initialized successfully" ); |
| 208 | return true; |
| 209 | } |
| 210 | |
| 211 | static enum errno e1000_pci_init(struct device *dev) { |
| 212 | struct pci_device *db = dev->driver_data; |
| 213 | uint8_t bus = db->bus, d = db->dev, func = db->function; |
| 214 | uint16_t did = db->device_id; |
| 215 | if (did == 0x1000 || did == 0x100E || did == 0x1010 || did == 0x1026 || |
| 216 | did == 0x10D3 || did == 0x10F5) { |
| 217 | struct pci_device dev = {.bus = bus, .dev = d, .function = func}; |
| 218 | struct e1000_device *device = |
| 219 | kmalloc_or_die(sizeof(struct e1000_device)); |
| 220 | |
| 221 | e1000_init(pci: &dev, dev: device); |
| 222 | } |
| 223 | |
| 224 | return ERR_OK; |
| 225 | } |
| 226 | |
| 227 | PCI_DEV_REGISTER(e1000, 2, 0, 0xff, 0x8086, e1000_pci_init); |
| 228 | |