-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathe1000.c~
527 lines (415 loc) · 16.2 KB
/
e1000.c~
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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
#include <kern/e1000.h>
#include <kern/sched.h>
// LAB 6: Your driver code here
volatile uint32_t *e1000;
//**************************************************************************************
// STRUCTURES:
//**************************************************************************************
//Transfer srtcutres:
struct tx_desc tx_desc_list[NUM_TRANS_DESC] __attribute__ ((aligned (16)));
struct tx_buf tx_buffer[NUM_TRANS_DESC] __attribute__ ((aligned (BUFFER_SIZE)));
//Receive structures:
struct rx_desc rx_desc_list[NUM_REC_DESC] __attribute__ ((aligned (16)));
struct rx_buf rx_buffer[NUM_REC_DESC] __attribute__ ((aligned (BUFFER_SIZE)));
struct rx_buf* zero_buf;
// this sturcture is for our own challange. PACKET FILTERER.
struct rule rules[] = {
{"block rule", VALUE_ANY, 0x0202000a, 0xf02000a, RANGE_FOUR, TCP, ACTION_DEN_WARN},
{"default", VALUE_ANY, VALUE_ANY, VALUE_ANY, VALUE_ANY, VALUE_ANY, ACTION_APPROVE},
};
uint16_t MAC_eeprom[3];
//**************************************************************************************
int
pci_vendor_attach(struct pci_func *pcif)
{
pci_func_enable(pcif);
uint32_t bar0 = pcif->reg_base[0];
e1000 = mmio_map_region(bar0, pcif->reg_size[0]);
assert(e1000[E1000_STATUS] == 0x80080783);
// EEPROM Challenge:
read_epprom();
set_mac();
//setting up the hard wired mac address
//e1000[E1000_RAL] = 0x12005452;
//e1000[E1000_RAH] = 0x00005634 | E1000_RAH_AV;
init_transmit();
init_receive();
// Enable Interrupts:
e1000[E1000_IMS] |= E1000_ICR_TXQE;//transmit queue emptry.
e1000[E1000_IMS] |= E1000_ICR_RXT0;//Receive time - for receive queue full
return 0;
}
//********************************************************************
// Init registers for trnasmit (ex.5): (chapter 14.5 in e1000 manual)
//********************************************************************
static void init_transmit(){
//Program the Transmit Descriptor Base Address (TDBAL/TDBAH) register(s) :
e1000[E1000_TDBAL] = PADDR(&tx_desc_list);
e1000[E1000_TDBAH] = 0x0; //We're only using 32bit so register isn't requried.
//Allocate a region of memory for the transmit descriptor list:
memset(tx_desc_list, 0, sizeof(struct tx_desc) * NUM_TRANS_DESC);
int i;
//Set the Transmit Descriptor Length
e1000[E1000_TDLEN] = TRANS_DESC_SIZE;
//init The Transmit Descriptor Head and Tail:
e1000[E1000_TDT] = 0x0;
e1000[E1000_TDH] = 0x0;
//Initialize the Transmit Control Register:
e1000[E1000_TCTL] |= E1000_TCTL_EN;
e1000[E1000_TCTL] |= E1000_TCTL_COLD;
e1000[E1000_TCTL] |= E1000_TCTL_CT;
e1000[E1000_TCTL] |= E1000_TCTL_PSP;
//Program the Transmit TIPG:
// Inside reg settings in bit range: {Table 13-77. TIPG Register Bit Description}
// IPGT (9:0) = 10 , IPGR1 (19:10) = 8 , IPGR2 (29:20) = 12 ,reserved (31:30) = 0b.
e1000[E1000_TIPG] = 0x0; // clear (instead of setting reserved to 0).
e1000[E1000_TIPG] |= GET_MASKED_VALUE(0xa, E1000_IPGT);
e1000[E1000_TIPG] |= GET_MASKED_VALUE(0x8, E1000_IPGR1);
e1000[E1000_TIPG] |= GET_MASKED_VALUE(0xc, E1000_IPGR2);
for (i=0; i < NUM_TRANS_DESC; i++){
tx_desc_list[i].addr = PADDR(&tx_buffer[i]);
//We used modified flags to fit our struct derived from the orignial flags:
tx_desc_list[i].cmd &= ~ E1000_TXD_DEXT; //set legacy mode.
tx_desc_list[i].cmd |= E1000_TXD_RS; //enable report status
tx_desc_list[i].status |= E1000_TXD_DD;// set desc done -> 'ready'.
}
}
//*********************************************************************
// Init registers for recieving (ex.10): (chapter 3.2 in e1000 manual)
//*********************************************************************
static void init_receive(){
//Allocate a region of memory for the receive descriptor list:
memset(rx_desc_list, 0, sizeof(struct rx_desc) * NUM_REC_DESC);
int i;
struct PageInfo* page;
if (ZERO_COPY_ENABLE){
for (i = 0; i < NUM_REC_DESC; i++){
page = page_alloc(ALLOC_ZERO);
//we add sizeof(int) to reserve place for the packet size.
// this is done to match packet struct for the ipc_send;
rx_desc_list[i].addr = page2pa(page) + sizeof(int);
}
}else{
for (i=0; i < NUM_REC_DESC; i++){
rx_desc_list[i].addr = PADDR(&rx_buffer[i]);
}
}
//Set the Receive Descriptor Length
e1000[E1000_RDLEN] = TRANS_REC_SIZE;
//Program the Receive Descriptor Base Address register(s) :
e1000[E1000_RDBAL] = PADDR(&rx_desc_list);
e1000[E1000_RDBAH] = 0x0; //We're only using 32bit so register isn't requried.
//init The Receive Descriptor Head and Tail:
e1000[E1000_RDT] = NUM_REC_DESC - 1;
e1000[E1000_RDH] = 0x0;
//RCTL flags intializtion
e1000[E1000_RCTL] = 0;
e1000[E1000_RCTL] &= ~E1000_RCTL_LPE;
e1000[E1000_RCTL] |= E1000_RCTL_LBM_NO;
e1000[E1000_RCTL] |= E1000_RCTL_SZ_2048;
e1000[E1000_RCTL] |= E1000_RCTL_SECRC;
e1000[E1000_RCTL] |= E1000_RCTL_EN;
}
//**************** ZERO COPY TRANSMIT ********************//
//ZERO COPY - will use walkpgdir to get the user envs pte for the
//buffer. then it will use it to get to the physcall address of
// the pacakge and use it as the buffer for transmit. without copying.
// we need to make sure we don't resume the user env until copy is
// done otherwise it might change the buffer while transmit is working!
//**********************************************************//
// Returns 0 on success.
// Returns -1 when sender needs to try again.
int transmit_packet (void* package, int size){
if( size > BUFFER_SIZE )
panic("Transmit packet received size bigger than BUFFER");
if(!package)
panic("Transmit Packet got a null package");
int cur_desc = e1000[E1000_TDT];
if( !(tx_desc_list[cur_desc].status & E1000_TXD_STAT_DD) ){
return -1;//the queue is full - let sender know to try again.
}
//normal copying code:
//--------------------
//uint32_t addr = tx_desc_list[cur_desc].addr;
//memcpy((void*)(KADDR(addr)) ,package, size);
//zero-copy:
//-------------
//get physical addr of package:
physaddr_t addr = (PTE_ADDR(*pgdir_walk(curenv->env_pgdir,package,0)) + PGOFF(package));
//point descriptor to the pacakge phys addr
tx_desc_list[cur_desc].addr = addr;
tx_desc_list[cur_desc].status &= ~E1000_TXD_STAT_DD;//mark used.
tx_desc_list[cur_desc].length = size;
tx_desc_list[cur_desc].cmd |= E1000_TXD_EOP;
e1000[E1000_TDT] = ( cur_desc + 1) % NUM_TRANS_DESC; // advance iterator TDT.
//make u-env sleep(yet runnnable) until the package is sent to it won't touch package mid transmit:
return 0;
}
// Returns the 'received packe size' on success.
// container must be of at least BUFFER_SIZE.
// return -E_NO_PACKAGE when packet not found.
int receive_packet (void* container, int* size){
if (!container)
panic("Receive Packet got a null container");
int idx = ( e1000[E1000_RDT] + 1) % NUM_REC_DESC;
if( !(rx_desc_list[idx].status & E1000_RXD_STAT_DD) ){
//send to sleep on packet - let the system call handle putting us to sleep:
return -E_NO_PACKAGE;
}
if ( !(rx_desc_list[idx].status & E1000_RXD_STAT_EOP) )
panic("Receive: Expected only a single pacakge message, not a stream!");
memcpy(container ,&rx_buffer[idx] ,rx_desc_list[idx].length);
rx_desc_list[idx].status = 0;
e1000[E1000_RDT] = idx; // advance iterator TDT.
*size = rx_desc_list[idx].length;
return 0;
}
//**************** ZERO COPY RECEIVE ********************//
// Unlike the zero copy trans this is a seperated function which we can choose to enable or not
// depends on the ZERO_COPY_ENABLE flag in the input.c file which starts the receive flow.
//We map the package pointer to point to pre allocated (shared) page instead of copying, the u-env will
// use this page via the package pointer later on.
// we have to take care of mapping the buffer into the curenv (receiving env) addrss space and make sure
// the buffers are aligned for NIC to use them properly.
//**********************************************************//
int zero_receive(char** package){
int size;
if (!package){
init_zero_copy_receive();
return 0;
}
int idx = ( e1000[E1000_RDT] + 1) % NUM_REC_DESC;
if( !(rx_desc_list[idx].status & E1000_RXD_STAT_DD) ){
//send to sleep on packet - let the system call handle putting us to sleep:
return -E_NO_PACKAGE;
}
if ( !(rx_desc_list[idx].status & E1000_RXD_STAT_EOP) )
panic("Receive: Expected only a single pacakge message, not a stream!");
size = rx_desc_list[idx].length;
*package = (char*)(ZEROCOPY_BASE + idx * PGSIZE);
//we put the size in the previously reserved space so it will look like jif_pkt struct!
// jif->size, jif->data:
*(*package) = size;
rx_desc_list[idx].status = 0;
e1000[E1000_RDT] = idx; // advance iterator TDT.
return size;
}
void init_zero_copy_receive(){
int i;
void* va;
for (i = 0; i < NUM_REC_DESC; i++){
va = (void*)(ZEROCOPY_BASE + i * PGSIZE);
if (page_insert(curenv->env_pgdir, pa2page(rx_desc_list[i].addr - sizeof(int)), va , PTE_P|PTE_U|PTE_W) < 0)
panic("[e1000] init zerocpy");
}
}
void e1000_interrupt_handler(){
//calls the corresponding interrupt handlers.
int reg = e1000[E1000_ICR];
if (reg & E1000_ICR_RXT0){
e1000_rec_handler();
}
if(reg & E1000_ICR_TXQE){
e1000_trans_handler();
}
}
//Handle receive interrupt by waking up sleeping receivers:
void e1000_rec_handler(){
int i = 0;
struct Env* target = NULL;
//Look for a receiving env in all of the envs:
for (i =0; i < NENV; i++){
if ( envs[i].env_e1000_rec == false)
continue;
target = &envs[i];
break;
}
//found one:
if (target){
assert(target->env_status == ENV_NOT_RUNNABLE);
target->env_e1000_rec = false;
target->env_status = ENV_RUNNABLE;
}
//clear interrupt:
e1000[E1000_ICR] |= E1000_ICR_RXT0;
lapic_eoi();
irq_eoi();
}
void e1000_trans_handler(){
int i = 0;
struct Env* target = NULL;
//Look for a receiving env in all of the envs:
for (i =0; i < NENV; i++){
if ( envs[i].env_e1000_trans == false)
continue;
target = &envs[i];
break;
}
//found one:
if (target){
assert(target->env_status == ENV_NOT_RUNNABLE);
target->env_e1000_trans = false;
target->env_status = ENV_RUNNABLE;
}
//clear interrupt:
e1000[E1000_ICR] |= E1000_ICR_TXQE;
lapic_eoi();
irq_eoi();
}
/** Challenge EEPROM **/
//************************************************************************************************
// MANUAL GUIDE TO USE EEPROM:
//************************************************************************************************
/*Software can use the EEPROM Read register (EERD) to cause the Ethernet controller to read a
word from the EEPROM that the software can then use. To do this, software writes the address to
read the Read Address (EERD.ADDR) field and then simultaneously writes a 1b to the Start Read
bit (EERD.START). The Ethernet controller then reads the word from the EEPROM, sets the Read
Done bit (EERD.DONE), and puts the data in the Read Data field (EERD.DATA). Software can
poll the EEPROM Read register until it sees the EERD.DONE bit set, then use the data from the
EERD.DATA field. Any words read this way are not written to hardware’s internal registers.
Ethernet Address (Words 00h-02h):
----------------------------------
The Ethernet Individual Address (IA) is a six-byte field that must be unique for each Ethernet port
(and unique for each copy of the EEPROM image). The first three bytes are vendor specific. The
value from this field is loaded into the Receive Address Register 0 (RAL0/RAH0). For a MAC
address of 12-34-56-78-90-AB, words 2:0 load as follows (note that these words are byteswapped):
Word 0 = 3412
Word 1 = 7856
Word 2 - AB90 */
void read_epprom(){
int i;
//reading MAC words from eeprom to read_epprom[]
for(i =0; i < 3; i++){
//tell eeprom to read from address i:
e1000[E1000_EERD] |= ( E1000_EEPROM_RW_REG_START|(i << E1000_EEPROM_RW_ADDR_SHIFT) );
while ( !(e1000[E1000_EERD] & E1000_EEPROM_RW_REG_DONE) ){
//wait till eeprom finishes reading:
}
//write the word to our MAC_eeprom array[] from the data part of register:
MAC_eeprom[i] = (e1000[E1000_EERD] >> E1000_EEPROM_RW_REG_DATA);
//clear the EERD so we can read the next word:
e1000[E1000_EERD] = 0;
}
//the MAC_eeprom[] should contain the MAC address now.
//cprintf("mac addr: [%x] , [%x] , [%x]\n", MAC_eeprom[0], MAC_eeprom[1], MAC_eeprom[2]);
return;
}
void set_mac(){
// Set MAC address in (RAH/RAL).Set valid bit in E1000_RAH.
e1000[E1000_RAL] = MAC_eeprom[0] |( MAC_eeprom[1] << E1000_EEPROM_RW_REG_DATA);
e1000[E1000_RAH] = MAC_eeprom[2] | E1000_RAH_AV;
}
//used for system call:
void send_mac(void* mac_addr){
if (mac_addr){
*((uint32_t*) mac_addr) = (uint32_t) e1000[E1000_RAL];
*((uint16_t*)(mac_addr+4)) = (uint16_t) e1000[E1000_RAH];
}
}
//*************************************************************************************
// FILTER CODE !
//*************************************************************************************
/**
** This are the rules to filter our requst. the first rule that match the packet will be activated. Keep the in mind
** also, when adding aditional rules keep in mind that anything that come after "default" wont take place.
** rule struct - struct rule name, direction, src_ip, dst_ip, range, protocol, action
************************************
** important - DONT add any rule name "default". if you do, then all the rules after won't be checked :(
************************************
**/
//**************************************************************************
// function implementions
//**************************************************************************
static char *get_protocol_name(int num)
{
switch (num){
case ICMP :
return "ICMP";
case IGMP :
return "IGMP";
case TCP :
return "TCP";
case UDP :
return "UDP";
case ENCAP:
return "ENCAP";
case OSPF :
return "OSPP";
case SCTP :
return "SCTP";
}
return "";
}
/*
in ip_cmp we can decide how much sepcific we want to be with the ip.
*/
static int ip_cmp(int rule_num, uint32_t head_ip,uint32_t rule_ip)
{
switch (rules[rule_num].range){
case RANGE_ALL :
return 1;
case RANGE_ONE :
if ( ip4_addr1(head_ip) == ip4_addr1(rule_ip) )
return 1;
break;
case RANGE_TWO :
if ( ip4_addr1(head_ip) == ip4_addr1(rule_ip) && ip4_addr2(head_ip) == ip4_addr2(rule_ip) )
return 1;
break;
case RANGE_THREE :
if ( ip4_addr1(head_ip) == ip4_addr1(rule_ip) && ip4_addr2(head_ip) == ip4_addr2(rule_ip) && ip4_addr3(head_ip) == ip4_addr3(rule_ip) )
return 1;
break;
case RANGE_FOUR :
if ( ip4_addr1(head_ip) == ip4_addr1(rule_ip) && ip4_addr2(head_ip) == ip4_addr2(rule_ip) &&
ip4_addr3(head_ip) == ip4_addr3(rule_ip) && ip4_addr4(head_ip) == ip4_addr4(rule_ip) )
return 1;
break;
}
return 0;
}
/*
* take a rule, and check if it fits the given ipheader. if so- return the action
*/
static int check_rule(int rule_num, uint32_t src_ip, uint32_t dst_ip, int protocol)
{
//sanity check - see if the rule have good range - if not, say it doesnt match.
if (rules[rule_num].range > RANGE_FOUR || rules[rule_num].range < 0)
return -1;
if (!ip_cmp(rule_num, src_ip, rules[rule_num].src_ip))
return -1;
if ( rules[rule_num].protocol != protocol && rules[rule_num].protocol != VALUE_ANY )
return -1;
if (!ip_cmp(rule_num, dst_ip, rules[rule_num].dst_ip))
return -1;
return rules[rule_num].action;
}
int check_packet(uint32_t src_ip, uint32_t dst_ip, int protocol)
{
int i = 0;
int rule_num = 0;
int res;
while (strcmp(rules[i].name,"default")){
rule_num = i;
if ( (res = check_rule(i, src_ip, dst_ip, protocol)) >= 0 ){ // rule match !
if (res == ACTION_DEN_WARN){
cprintf("FILTER: WARNING, unapproved packet src ip - %d.%d.%d.%d dst ip - %d.%d.%d.%d request for protocol %s !\n",
ip4_addr1(src_ip),ip4_addr2(src_ip),ip4_addr3(src_ip),ip4_addr4(src_ip),
ip4_addr1(dst_ip),ip4_addr2(dst_ip),ip4_addr3(dst_ip),ip4_addr4(dst_ip),
!strcmp(get_protocol_name(protocol),"") ? "UNKNOWN PROTOCOL" : get_protocol_name(protocol));
return 0;
}
return res;
}
i++;rule_num++;
}
res = rules[i].action;
if (res == ACTION_DEN_WARN){
cprintf("FILTER: WARNING, unapproved packet src ip - %d.%d.%d.%d dst ip - %d.%d.%d.%d request for protocol %s !\n",
ip4_addr1(src_ip),ip4_addr2(src_ip),ip4_addr3(src_ip),ip4_addr4(src_ip),
ip4_addr1(dst_ip),ip4_addr2(dst_ip),ip4_addr3(dst_ip),ip4_addr4(dst_ip),
!strcmp(get_protocol_name(protocol),"") ? "UNKNOWN PROTOCOL" : get_protocol_name(protocol));
return 0;
}
return res;
}