MIT6.828 Lab6

Lab 6: Network Driver

Part A: Initialization and transmitting packets

time

在之前的时钟中断调度前加一个对time_tick函数的调用,注意时钟中断每个CPU都会收到,而我们的目的是计时,所以选择固定选择一个CPU来处理就好,bootcpu当然是最合适的.

1
2
3
4
5
6
case IRQ_OFFSET+IRQ_TIMER:
lapic_eoi();
if(thiscpu==bootcpu)
time_tick();
sched_yield();
return;

The Network Interface Card

PCI Interface

现在要完成E1000作为一个PCI设备的识别和初始化

The E1000 is a PCI device, which means it plugs into the PCI bus on the motherboard. The PCI bus has address, data, and interrupt lines, and allows the CPU to communicate with PCI devices and PCI devices to read and write memory. A PCI device needs to be discovered and initialized before it can be used. Discovery is the process of walking the PCI bus looking for attached devices. Initialization is the process of allocating I/O and memory space as well as negotiating the IRQ line for the device to use.

To perform PCI initialization during boot, the PCI code walks the PCI bus looking for devices. When it finds a device, it reads its vendor ID and device ID and uses these two values as a key to search the pci_attach_vendor array.

If the discovered device’s vendor ID and device ID match an entry in the array, the PCI code calls that entry’s attachfn to perform device initialization.

在Software Developer’s Manual中找到E1000的vendor ID和device ID,为E1000声明一个用来attach的函数

1
2
3
4
#define PCI_E1000_VENDOR_ID 0x8086
#define PCI_E1000_DEVICE_ID 0x100E

int e1000_attach(struct pci_func *pcif);

填入pci_attach_vendor数组中,这样E1000可以被识别.

1
2
3
4
struct pci_driver pci_attach_vendor[] = {
{PCI_E1000_VENDOR_ID,PCI_E1000_DEVICE_ID,e1000_attach},
{ 0, 0, 0 },
};
Memory-mapped

Software communicates with the E1000 via memory-mapped I/O (MMIO).

mmio_map_region的实现在lab4中.

1
2
3
4
5
6
7
8
9
10
11
12

volatile void* e1000;


int e1000_attach(struct pci_func *pcif)
{
pci_func_enable(pcif);

e1000 = mmio_map_region(pcif->reg_base[0],pcif->reg_size[0]);
cprintf("Device e1000 attached, status:%p\n",*(uint32_t*)(e1000+8));
return 0;
}

Transmitting Packets

C Structures

加上e1000设备的初始化操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct e1000_tx_desc tds[NTRANS_DESC];


int e1000_attach(struct pci_func *pcif)
{
int r;
pci_func_enable(pcif);

e1000 = mmio_map_region(pcif->reg_base[0],pcif->reg_size[0]);
cprintf("Device e1000 attached, status:%p\n",*(uint32_t*)(e1000+E1000_STATUS));


*(uint32_t*)(e1000+E1000_TDBAL) = PADDR(tds);
*(uint32_t*)(e1000+E1000_TDLEN) = TDLEN;
*(uint32_t*)(e1000+E1000_TDH) = 0;
*(uint32_t*)(e1000+E1000_TDT) = 0;
*(uint32_t*)(e1000+E1000_TCTL) = E1000_TCTL_EN|
E1000_TCTL_PSP|
(E1000_TCTL_COLD&(0x40<<E1000_TCTL_COLD_SHIFT));

*(uint32_t*)(e1000+E1000_TIPG) = 10 | (8 << 10) | (12 << 20);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
inline uint32_t tdh()
{
return *(uint32_t*)(e1000+E1000_TDH);
}

inline uint32_t tdt()
{
return *(uint32_t*)(e1000+E1000_TDT);
}

int tds_init()
{
for(int i = 0;i<NTRANS_DESC;++i)
{
tds[i].addr = PADDR(tdbufs[i]);
}

*(uint32_t*)(e1000+E1000_TDBAL) = PADDR(tds);
*(uint32_t*)(e1000+E1000_TDBAH) = 0;
*(uint32_t*)(e1000+E1000_TDLEN) = TDLEN;
*(uint32_t*)(e1000+E1000_TDH) = 0;
*(uint32_t*)(e1000+E1000_TDT) = 0;
*(uint32_t*)(e1000+E1000_TCTL) = E1000_TCTL_EN|
E1000_TCTL_PSP|
(E1000_TCTL_COLD&(0x40<<E1000_TCTL_COLD_SHIFT));

*(uint32_t*)(e1000+E1000_TIPG) = 10 | (8 << 10) | (12 << 20);

return 0;
}

int tx_pkt(char* buf,size_t nbytes)
{
if(nbytes>DESC_BUF_SZ)
panic("tx_pkt: invalid packet size\n");

if((tds[tdt()].cmd&E1000_TXD_CMD_RS))
{
if(!(tds[tdt()].status&E1000_TXD_STAT_DD))
{
cprintf("tx_pkt: the transmit queue is full,please retry\n");
return -E_TDQ_FULL;
}
}
memcpy(tdbufs[tdt()],buf,nbytes);
tds[tdt()].length = (uint16_t)nbytes;
tds[tdt()].cmd |= (E1000_TXD_CMD_RS|E1000_TXD_CMD_EOP);
tds[tdt()].status &= (~E1000_TXD_STAT_DD);
*(uint32_t*)(e1000+E1000_TDT) = (tdt()+1)%NTRANS_DESC;
return 0;
}

Part B: Receiving packets and the web server

和Transmit packets类似,查手册写就行了.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
int rx_init()
{
for(int i = 0;i<NRECV_DESC;++i)
{
rds[i].addr = PADDR(rdbufs[i]);
rds[i].status &= ~E1000_RXD_STAT_DD;
}


*(uint32_t*)(e1000+E1000_RAL) = MAC_IPL;
*(uint32_t*)(e1000+E1000_RAH) = MAC_IPH|E1000_RAH_AV;

for(int i = 0;i<128;++i)
((uint32_t*)(e1000+E1000_MTA))[i] = 0;

*(uint32_t*)(e1000+E1000_RDBAL) = PADDR(rds);
*(uint32_t*)(e1000+E1000_RDBAH) = 0;

*(uint32_t*)(e1000+E1000_RDLEN) = NRECV_DESC*DESC_BUF_SZ;
*(uint32_t*)(e1000+E1000_RDH) = 0;
*(uint32_t*)(e1000+E1000_RDT) = NRECV_DESC-1;

*(uint32_t*)(e1000+E1000_RCTL) = (E1000_RCTL_EN | E1000_RCTL_SECRC | E1000_RCTL_BAM | 0<<RCTL_BSIZE_SHIFT) & (~E1000_RCTL_LPE);

return 0;
}

int rx_pkt(char* buf,size_t max_bytes)
{
uint32_t next = (rdt()+1)%NRECV_DESC;
if((rds[next].status&E1000_RXD_STAT_DD)==0)
return -E_RDQ_EMPTY;
uint16_t lenth = rds[next].length;
if(lenth>max_bytes)
return -E_INVAL;
memcpy(buf,rdbufs[next],lenth);
rds[next].status &= (~E1000_RXD_STAT_DD);
*(uint32_t*)(e1000+E1000_RDT) = next;
return lenth;
}

效果

其他代码详见github.
通过所有测试.

现在启动httpd server,在浏览器中访问html页面(正好桌面上有这个html及css等文件,就拿来实验了,这是我曾经的一次作业,你可以通过这个页面猜到,我最终没有通过考试),可以看到这样的界面.
你可以看到有张只有一小半的BUPT校徽,

我尝试输出字节数的方式来探究其只有一小半的原因.发现确实未发送完全.

我启动e1000的调试功能,多次实验发现在第125次发送时稳定触发,与TX描述符个数无关,更奇怪的时,触发之后125,126,127还能够继续传输,并且再次触发.

我在课程提供的qemu的源码中找到了这一报错,它意味着e1000正在发送的描述符的buffer_addr为空,但我找不出原因,我的初始化工作和其他工作看起来没有问题,至少不太可能出现buffer_addr为空的问题.我不确定是否是课程提供的qemu存在的问题,该问题会在以后的开发中再来解决,毕竟计算机网络方面的知识我还所知甚少,近期的开发会先从JOS一些实现的优化开始.

如果您知道问题出在哪里,请联系我,感激不尽.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
if (desc.buffer_addr) {
if (desc_offset < size) {
size_t iov_copy;
hwaddr ba = le64_to_cpu(desc.buffer_addr);
size_t copy_size = size - desc_offset;
if (copy_size > s->rxbuf_size) {
copy_size = s->rxbuf_size;
}
do {
iov_copy = MIN(copy_size, iov->iov_len - iov_ofs);
pci_dma_write(d, ba, iov->iov_base + iov_ofs, iov_copy);
copy_size -= iov_copy;
ba += iov_copy;
iov_ofs += iov_copy;
if (iov_ofs == iov->iov_len) {
iov++;
iov_ofs = 0;
}
} while (copy_size);
}
desc_offset += desc_size;
desc.length = cpu_to_le16(desc_size);
if (desc_offset >= total_size) {
desc.status |= E1000_RXD_STAT_EOP | E1000_RXD_STAT_IXSM;
} else {
/* Guest zeroing out status is not a hardware requirement.
Clear EOP in case guest didn't do it. */
desc.status &= ~E1000_RXD_STAT_EOP;
}
} else { // as per intel docs; skip descriptors with null buf addr
DBGOUT(RX, "Null RX descriptor!!\n");
}
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2022-2024 翰青HanQi

请我喝杯咖啡吧~

支付宝
微信