Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | // ========== Copyright Header Begin ========================================== |
2 | // | |
3 | // OpenSPARC T2 Processor File: dumbserial.cc | |
4 | // Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. | |
5 | // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES. | |
6 | // | |
7 | // The above named program is free software; you can redistribute it and/or | |
8 | // modify it under the terms of the GNU General Public | |
9 | // License version 2 as published by the Free Software Foundation. | |
10 | // | |
11 | // The above named program is distributed in the hope that it will be | |
12 | // useful, but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | // General Public License for more details. | |
15 | // | |
16 | // You should have received a copy of the GNU General Public | |
17 | // License along with this work; if not, write to the Free Software | |
18 | // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. | |
19 | // | |
20 | // ========== Copyright Header End ============================================ | |
21 | /* dumb serial device for NIOBP */ | |
22 | ||
23 | /* "Leveraged" from Legion dumbserial devices :) */ | |
24 | ||
25 | /* | |
26 | * A dumb serial device - briefly modeled on the NS 16550 device | |
27 | * ... i.e. register bit fields are similar, but that's about it. | |
28 | * | |
29 | * We support input and output character buffering. | |
30 | * Eventually these sizes will be settable from the configfile | |
31 | * Also eventually we will understand baud rates etc. and fake | |
32 | * these appropriately. | |
33 | * For the moment, we support only a simple status poll, byte | |
34 | * get and byte put. | |
35 | * Status simply returns, byte-available, underrun, and overrun | |
36 | * for input info. Nothing for output (yet). | |
37 | */ | |
38 | ||
39 | ||
40 | /* | |
41 | * Registers (8-bits only - 8byte addressing, upper bits read 0 write ignored) | |
42 | * num RW Function: | |
43 | * 0 R Input data register | |
44 | * 0 W Output data register | |
45 | * 1 RW Interrupt enable register | |
46 | * 2 R Interrupt indentification register | |
47 | * 2 W FIFO control register | |
48 | * 3 RW Line control register | |
49 | * 4 RW Modem control register | |
50 | * 5 RW Line status register | |
51 | * 6 RW Modem status register | |
52 | * 7 RW Scratch register | |
53 | * 8 RW Divisor lo | |
54 | * 9 RW Divisor hi | |
55 | * | |
56 | * See 16550 data sheet p 14 - table M | |
57 | */ | |
58 | ||
59 | #include <stdio.h> | |
60 | #include <stdlib.h> | |
61 | #include <string.h> | |
62 | #include <errno.h> | |
63 | #include <unistd.h> | |
64 | #include <assert.h> | |
65 | #include <fcntl.h> | |
66 | #include <sys/types.h> | |
67 | #include <sys/stat.h> | |
68 | #include <sys/mman.h> | |
69 | #include <sys/dkio.h> | |
70 | #include <sys/dklabel.h> | |
71 | #include <sys/vtoc.h> | |
72 | #include <strings.h> | |
73 | ||
74 | #include <pthread.h> | |
75 | #include <errno.h> | |
76 | #include <signal.h> | |
77 | #include <sys/socket.h> | |
78 | #include <netinet/in.h> | |
79 | #include <sys/un.h> | |
80 | #include <arpa/inet.h> | |
81 | #include <netdb.h> | |
82 | #include <pthread.h> | |
83 | ||
84 | #include "mmi.h" | |
85 | #include "ui.h" | |
86 | ||
87 | // #include "createthr.h" | |
88 | ||
89 | static int dumbserial_ld_operation (void *cd, uint64_t paddr, uint64_t *buf, int size, uint32_t v9cpuid); | |
90 | static int dumbserial_st_operation (void *cd, uint64_t paddr, uint64_t *buf, int size, uint32_t v9cpuid); | |
91 | ||
92 | #define MAXCPU 32 | |
93 | ||
94 | #define DBGX(s) do {} while (0) | |
95 | ||
96 | #define MAXNAMELEN 256 | |
97 | ||
98 | typedef enum { | |
99 | DS_Input = 0x0, /* RO */ | |
100 | DS_Output = 0x0, /* WO */ | |
101 | DS_IntEnable = 0x1, | |
102 | DS_IntIdent = 0x2, /* RO */ | |
103 | DS_FIFOCtrl = 0x2, /* WO */ | |
104 | DS_LineCtrl = 0x3, | |
105 | DS_ModemCtrl = 0x4, | |
106 | DS_LineStatus = 0x5, | |
107 | DS_ModemStatus = 0x6, | |
108 | DS_Scratch = 0x7, | |
109 | DS_DivLo = 0x8, | |
110 | DS_DivHi = 0x9 | |
111 | } ds_reg_t; | |
112 | ||
113 | typedef enum { | |
114 | TE_Normal, | |
115 | TE_SawCR, | |
116 | TE_SawTilde | |
117 | } ds_tildestate_t; | |
118 | ||
119 | #define ptest(_s) if (fds.revents & _s) printf(","#_s) | |
120 | ||
121 | typedef struct { | |
122 | ||
123 | bool uses_term; | |
124 | ||
125 | /* device registers .. */ | |
126 | uint8_t scratch; /* ! */ | |
127 | uint8_t line_status; | |
128 | #define DS_LSTAT_DATA_READY 0x1 | |
129 | #define DS_LSTAT_OVERRUN 0x2 | |
130 | #define DS_LSTAT_PARTIY_ERR 0x4 | |
131 | #define DS_LSTAT_FRAMING_ERR 0x8 | |
132 | #define DS_LSTAT_BREAK 0x10 | |
133 | #define DS_LSTAT_TX_HOLD 0x20 | |
134 | #define DS_LSTAT_TX_EMPTY 0x40 | |
135 | #define DS_LSTAT_RCV_ERR 0x80 | |
136 | ||
137 | #define DEFAULT_RXFIFOSZ 1024 | |
138 | ||
139 | struct { | |
140 | uint8_t *bufp; | |
141 | int head; | |
142 | int tail; | |
143 | int count; | |
144 | int size; | |
145 | } in; | |
146 | ||
147 | int reg_shift; /* register alignment 0 .. 3 (default = 0 = byte align) */ | |
148 | #define DS_REG_SHIFT_DEF 0 | |
149 | ||
150 | /* the following if uses_term is set */ | |
151 | #define DEFAULT_XTERM_STR "/usr/openwin/bin/xterm -T TTY -e /bin/telnet %s %d &" | |
152 | char * term_strp; | |
153 | int tty_skt; /* socket to send / receive to client on */ | |
154 | bool tty_attached; | |
155 | pthread_t pthread_id; | |
156 | pthread_mutex_t tty_lock; | |
157 | pthread_cond_t tty_cv; | |
158 | ds_tildestate_t tilde_state; | |
159 | ||
160 | uint64_t startpa; | |
161 | uint64_t endpa; | |
162 | const char * type; // GUEST or HYPERVISOR | |
163 | } ds_state_t; | |
164 | ||
165 | /* | |
166 | * Internal functions | |
167 | */ | |
168 | ||
169 | #define NUM_DEVICES 2 // hypervisor and guest consoles | |
170 | #define HV_DEVICE 0 | |
171 | #define G_DEVICE 1 | |
172 | static ds_state_t *dumbserials[NUM_DEVICES]; | |
173 | ||
174 | int temp_ds_counter = 0; | |
175 | #define HV_SERIAL 0 // calls xterm | |
176 | #define G_SERIAL 1 // nothing | |
177 | ||
178 | /* | |
179 | #define HV_START 0x9f00000000 | |
180 | #define HV_END 0x9f0000004f | |
181 | */ | |
182 | #define HV_START 0xfff0c2c000 | |
183 | #define HV_END 0xfff0c2c04f | |
184 | #define G_START 0x9f10002000 | |
185 | #define G_END 0x9f1000204f | |
186 | ||
187 | static void create_term(ds_state_t * dsp); | |
188 | extern "C" static void * ds_input_thr(void * ptr); | |
189 | static void ds_insert_bytes(ds_state_t * dsp, uint8_t * bufp, int len); | |
190 | static void dump_buf(char * strp, uint8_t * bufp, int len); | |
191 | ||
192 | #if NDEBUG /* { */ | |
193 | /* hack for TLB debugging - to go away FIXME */ | |
194 | static void ds_dump_tlb(ds_state_t * dsp, bool is_itlb); | |
195 | #endif /* } */ | |
196 | ||
197 | ds_state_t *get_ds_instance(uint64_t addr) { | |
198 | ||
199 | assert(0); // should not be called | |
200 | #if 0 | |
201 | if ( addr >= HV_START && addr < HV_END ) { | |
202 | return dumbserials[HV_DEVICE]; | |
203 | } else if ( addr >= G_START && addr < G_END ) { | |
204 | return dumbserials[G_DEVICE]; | |
205 | } else { | |
206 | fprintf(stderr, "Unknown dumbconsole address range!\n"); | |
207 | exit(1); | |
208 | } | |
209 | #endif | |
210 | return NULL; | |
211 | } | |
212 | ||
213 | extern "C" static int dumbserial_physio_access(uint32_t cpuid, void* obj, uint64_t paddr, mmi_bool_t wr, uint32_t size, uint64_t* buf, uint8_t bytemask) | |
214 | { | |
215 | if (wr) { | |
216 | return dumbserial_st_operation(obj, paddr, buf, size, cpuid); | |
217 | } else { | |
218 | return dumbserial_ld_operation(obj, paddr, buf, size, cpuid); | |
219 | } | |
220 | } | |
221 | ||
222 | int dumbserial_ld_operation (void *obj, uint64_t paddr, uint64_t *buf, int size, uint32_t v9cpuid) | |
223 | { | |
224 | // Load accessor ... | |
225 | ds_reg_t reg; | |
226 | uint64_t val; | |
227 | ds_state_t *dsp = (ds_state_t *) obj; | |
228 | DBGX (printf("CAUGHT LD to %016llx with size %d\n", paddr, size); ); | |
229 | if ((paddr & (((uint64_t)1<<dsp->reg_shift)-1))!=0) { | |
230 | *buf = 0; | |
231 | return false; | |
232 | } | |
233 | // use the offset of the address, not the entire address | |
234 | reg = (ds_reg_t) ((paddr & 0xfff) >> dsp->reg_shift); | |
235 | val = 0; | |
236 | DBGX(printf("\treg: %d\n", reg); ); | |
237 | switch (reg) { | |
238 | case DS_Input: | |
239 | pthread_mutex_lock(&dsp->tty_lock); | |
240 | if (dsp->in.count == 0) { | |
241 | val = 0; | |
242 | dsp->line_status &= ~DS_LSTAT_DATA_READY; | |
243 | } else { | |
244 | val = dsp->in.bufp[dsp->in.head]; | |
245 | dsp->in.head ++; | |
246 | if (dsp->in.head >= dsp->in.size) dsp->in.head = 0; | |
247 | dsp->in.count --; | |
248 | if (dsp->in.count == 0) { | |
249 | dsp->line_status &= ~DS_LSTAT_DATA_READY; | |
250 | } | |
251 | } | |
252 | pthread_mutex_unlock(&dsp->tty_lock); | |
253 | break; | |
254 | ||
255 | case DS_IntEnable: | |
256 | break; | |
257 | ||
258 | case DS_IntIdent: | |
259 | break; | |
260 | ||
261 | case DS_LineCtrl: | |
262 | break; | |
263 | ||
264 | case DS_ModemCtrl: | |
265 | break; | |
266 | ||
267 | case DS_LineStatus: | |
268 | /* FIXME - more to do here */ | |
269 | /* TX always empty for the moment */ | |
270 | pthread_mutex_lock(&dsp->tty_lock); | |
271 | val = dsp->line_status; | |
272 | pthread_mutex_unlock(&dsp->tty_lock); | |
273 | break; | |
274 | ||
275 | case DS_ModemStatus: | |
276 | break; | |
277 | ||
278 | case DS_Scratch: | |
279 | dsp->scratch = val & 0xff; | |
280 | break; | |
281 | ||
282 | case DS_DivLo: | |
283 | break; | |
284 | ||
285 | case DS_DivHi: | |
286 | break; | |
287 | ||
288 | default: | |
289 | return true; | |
290 | } | |
291 | ||
292 | if (1 == size) | |
293 | val = (uint64_t)(int64_t)(int8_t)val; | |
294 | DBGX(printf("\tLD from %016llx returning %016llx\n", paddr, val);); | |
295 | *buf = val; | |
296 | return 0; | |
297 | } | |
298 | ||
299 | int dumbserial_st_operation (void *obj, uint64_t pa, uint64_t *buf, int size, uint32_t v9cpuid) | |
300 | { | |
301 | // Store accessor ... | |
302 | ds_reg_t reg; | |
303 | uint64_t val; | |
304 | ds_state_t *dsp = (ds_state_t *) obj; | |
305 | DBGX(printf("caught st to %016llx with data %c, size %d\n", pa, (char)*buf, size);); | |
306 | if ((pa & (((uint64_t)1<<dsp->reg_shift)-1))!=0) { | |
307 | return false; | |
308 | } | |
309 | // use the offset of the address, not the entire address | |
310 | reg = (ds_reg_t) ((pa & 0xfff) >> dsp->reg_shift); | |
311 | val = *buf; | |
312 | ||
313 | if (val>0xff) { | |
314 | return false; | |
315 | } | |
316 | ||
317 | val &= 0xff; | |
318 | ||
319 | switch (reg) { | |
320 | case DS_Output: | |
321 | if (dsp->uses_term) { | |
322 | if (dsp->tty_attached) { | |
323 | uint8_t buf[1]; | |
324 | buf[0] = val; | |
325 | write(dsp->tty_skt, buf, 1); | |
326 | } | |
327 | } else { | |
328 | putchar(val); | |
329 | fflush(stdout); | |
330 | } | |
331 | break; | |
332 | ||
333 | case DS_IntEnable: | |
334 | break; | |
335 | ||
336 | case DS_FIFOCtrl: | |
337 | break; | |
338 | ||
339 | case DS_LineCtrl: | |
340 | break; | |
341 | ||
342 | case DS_ModemCtrl: | |
343 | break; | |
344 | ||
345 | case DS_LineStatus: | |
346 | /* FIXME - more to do here */ | |
347 | /* TX always empty for the moment */ | |
348 | pthread_mutex_lock(&dsp->tty_lock); | |
349 | dsp->line_status = (val & 0xff) | DS_LSTAT_TX_EMPTY | DS_LSTAT_TX_HOLD; | |
350 | pthread_mutex_unlock(&dsp->tty_lock); | |
351 | break; | |
352 | ||
353 | case DS_ModemStatus: | |
354 | break; | |
355 | ||
356 | case DS_Scratch: | |
357 | dsp->scratch = val & 0xff; | |
358 | break; | |
359 | ||
360 | case DS_DivLo: | |
361 | break; | |
362 | ||
363 | case DS_DivHi: | |
364 | break; | |
365 | ||
366 | default: | |
367 | return false; | |
368 | } | |
369 | return 0; | |
370 | } | |
371 | ||
372 | void ds_parse(void) { | |
373 | ||
374 | ds_state_t *dsp = (ds_state_t *)calloc(1, sizeof(ds_state_t)); | |
375 | ||
376 | dsp->uses_term = false; /* default: output but no input */ | |
377 | dsp->in.size = DEFAULT_RXFIFOSZ; | |
378 | dsp->reg_shift = DS_REG_SHIFT_DEF; | |
379 | ||
380 | dsp->term_strp = (char *)malloc(256); | |
381 | dsp->uses_term = true; | |
382 | ||
383 | switch(temp_ds_counter) { | |
384 | case HV_SERIAL: | |
385 | strcpy(dsp->term_strp, "xterm -l -lf guest1.log -bg black -fg green -geometry 80x20+170+300 -T 'Hypervisor Console' -e ./netcons %s %d &"); | |
386 | dumbserials[HV_DEVICE] = dsp; | |
387 | break; | |
388 | case G_SERIAL: | |
389 | strcpy(dsp->term_strp, "xterm -l -lf guest1.log -bg black -fg green -geometry 80x35+200+30 -T 'Guest Console' -e ./netcons %s %d &"); | |
390 | dumbserials[G_DEVICE] = dsp; | |
391 | break; | |
392 | default: | |
393 | break; | |
394 | } | |
395 | ||
396 | } | |
397 | ||
398 | void ds_init(ds_state_t * dsp) { | |
399 | #if 1 | |
400 | dsp->line_status = DS_LSTAT_TX_EMPTY | DS_LSTAT_TX_HOLD; | |
401 | #else | |
402 | dsp->line_status = 0; | |
403 | #endif | |
404 | dsp->scratch = 0; | |
405 | dsp->in.count = 0; | |
406 | dsp->in.head = 0; | |
407 | dsp->in.tail = 0; | |
408 | dsp->in.bufp = (uint8_t *)calloc(1, dsp->in.size); | |
409 | ||
410 | /* if TTY to be created then do so ... */ | |
411 | if (dsp->uses_term) create_term(dsp); | |
412 | } | |
413 | ||
414 | /* | |
415 | * Create term ... during initialisation, create the thread to run the | |
416 | * terminal. | |
417 | */ | |
418 | ||
419 | void create_term(ds_state_t * dsp) | |
420 | { | |
421 | dsp->tty_skt = -1; | |
422 | dsp->tty_attached = false; | |
423 | /* dsp->tilde_state = TE_Normal; */ | |
424 | dsp->tilde_state = TE_SawCR; /* shouldn't have to CR to use ~ as first input char */ | |
425 | pthread_mutex_init(&dsp->tty_lock, NULL); | |
426 | pthread_cond_init(&dsp->tty_cv, NULL); | |
427 | ||
428 | /* | |
429 | * OK first we create the management thread for this console's input | |
430 | * - then wait for it to tell us that it is happily up and running | |
431 | * before we return to the simulator to continue initialisation. | |
432 | */ | |
433 | ||
434 | pthread_mutex_lock(&dsp->tty_lock); | |
435 | ||
436 | pthread_create(&(dsp->pthread_id), NULL, ds_input_thr, (void *) dsp); | |
437 | ||
438 | /* should probably timeout here incase child doesn't start */ | |
439 | while (!dsp->tty_attached) pthread_cond_wait(&dsp->tty_cv, &dsp->tty_lock); | |
440 | pthread_mutex_unlock(&dsp->tty_lock); | |
441 | ||
442 | } | |
443 | ||
444 | ||
445 | ||
446 | ||
447 | ||
448 | /* | |
449 | * Thread to manage external connections to this serial port. | |
450 | * | |
451 | * By default it creates an xterm with a telnet connection to | |
452 | * the appropriate i/o port. | |
453 | */ | |
454 | ||
455 | void * ds_input_thr(void * ptr) | |
456 | { | |
457 | ds_state_t * dsp; | |
458 | uint8_t buf[1024]; | |
459 | #define MAXHOSTNAME 256 | |
460 | char myhostname[MAXHOSTNAME]; | |
461 | int ds_sv_skt; | |
462 | struct hostent * hp; | |
463 | int on, length; | |
464 | struct sockaddr_in ds_server, from; | |
465 | char * froms; | |
466 | ||
467 | dsp = (ds_state_t*)ptr; | |
468 | ||
469 | /* | |
470 | * Create our socket to listen to | |
471 | */ | |
472 | ||
473 | ds_sv_skt = socket(AF_INET, SOCK_STREAM, 0); | |
474 | if (ds_sv_skt < 0) { | |
475 | fprintf(stderr, "opening stream socket"); | |
476 | exit(1); | |
477 | } | |
478 | ||
479 | /* enable the reuse of this socket if this process dies */ | |
480 | if (setsockopt(ds_sv_skt, SOL_SOCKET, SO_REUSEADDR, (uint8_t*)&on, sizeof(on))<0) { | |
481 | fprintf(stderr,"turning on REUSEADDR"); | |
482 | exit(1); | |
483 | } | |
484 | ||
485 | /* bind it */ | |
486 | retry:; | |
487 | ds_server.sin_family = AF_INET; | |
488 | ds_server.sin_addr.s_addr = INADDR_ANY; | |
489 | ds_server.sin_port = htons(0); /* bind to an OS selected local port */ | |
490 | ||
491 | if (bind(ds_sv_skt, (struct sockaddr *)&ds_server, sizeof(ds_server)) < 0) { | |
492 | switch (errno) { | |
493 | case EAGAIN: | |
494 | goto retry; | |
495 | ||
496 | case EADDRINUSE: | |
497 | fprintf(stderr, "Port is already in use\n"); | |
498 | exit(1); | |
499 | default: | |
500 | fprintf(stderr, "binding tcp stream socket"); | |
501 | exit(1); | |
502 | } | |
503 | } | |
504 | ||
505 | length = sizeof(ds_server); | |
506 | if (getsockname(ds_sv_skt, (struct sockaddr *) &ds_server, &length)==-1) { | |
507 | fprintf(stderr, "getting socket name"); | |
508 | exit(1); | |
509 | } | |
510 | ||
511 | listen(ds_sv_skt, 1); | |
512 | ||
513 | ||
514 | /* | |
515 | * Create the client xterm etc | |
516 | */ | |
517 | ||
518 | gethostname(myhostname, MAXHOSTNAME); | |
519 | sprintf((char*)buf, dsp->term_strp, myhostname, ntohs(ds_server.sin_port)); | |
520 | ||
521 | system((char*)buf); | |
522 | ||
523 | /* | |
524 | * OK main loop for processing connections and data traffic | |
525 | */ | |
526 | ||
527 | ||
528 | do { | |
529 | if (!dsp->tty_attached) { | |
530 | ui->output("\nWaiting for connection to : %s:%d\n", myhostname, ntohs(ds_server.sin_port)); | |
531 | ||
532 | length = sizeof(from); | |
533 | dsp->tty_skt = accept(ds_sv_skt, (struct sockaddr *)&from, (int*)&length); | |
534 | ||
535 | hp = gethostbyaddr((char *)&from.sin_addr, 4, AF_INET); | |
536 | if (hp == (struct hostent *)0) { | |
537 | froms = inet_ntoa(from.sin_addr); | |
538 | fprintf(stderr,"cant resolve hostname for %s\n", froms);; | |
539 | } else { | |
540 | froms = hp->h_name; | |
541 | } | |
542 | ui->output("connection from %s:%d\n", froms, ntohs(from.sin_port)); | |
543 | ||
544 | pthread_mutex_lock(&dsp->tty_lock); | |
545 | dsp->tty_attached = true; | |
546 | pthread_mutex_unlock(&dsp->tty_lock); | |
547 | pthread_cond_signal(&dsp->tty_cv); | |
548 | } else { | |
549 | int res; | |
550 | struct pollfd fds; | |
551 | ||
552 | #define POLL_TIMEOUT -1 /* wait forever ? FIXME ? */ | |
553 | fds.fd = dsp->tty_skt; | |
554 | fds.events = POLLIN|POLLPRI; | |
555 | #if HOST_OS_SOLARIS9 /* { */ | |
556 | fds.events |= POLLRDNORM|POLLRDBAND; | |
557 | #endif /* } */ | |
558 | fds.revents = 0; | |
559 | ||
560 | res = poll(&fds, 1, POLL_TIMEOUT); | |
561 | ||
562 | DBGX( printf("tty: "); | |
563 | ptest(POLLIN); | |
564 | ptest(POLLOUT); | |
565 | ptest(POLLERR); | |
566 | ptest(POLLHUP); | |
567 | ptest(POLLNVAL); | |
568 | printf("\n"); | |
569 | fflush(stdout); ); | |
570 | ||
571 | #if HOST_OS_SOLARIS9 /* { */ | |
572 | DBGX( printf("\t"); | |
573 | ptest(POLLRDNORM); | |
574 | ptest(POLLRDBAND); | |
575 | ptest(POLLWRNORM); | |
576 | ptest(POLLWRBAND); | |
577 | printf("\n"); | |
578 | fflush(stdout); ); | |
579 | #endif /* } */ | |
580 | ||
581 | if (fds.revents & POLLIN) { | |
582 | res = read(dsp->tty_skt, buf, sizeof (buf)); | |
583 | if (res == 0) { | |
584 | /* a read of 0 bytes is an EOF */ | |
585 | pthread_mutex_lock(&dsp->tty_lock); | |
586 | dsp->tty_attached = false; | |
587 | pthread_mutex_unlock(&dsp->tty_lock); | |
588 | pthread_cond_signal(&dsp->tty_cv); | |
589 | close(dsp->tty_skt); | |
590 | // assert(dsp->tty_skt = -1); | |
591 | } else if (res<0) { | |
592 | perror("read"); | |
593 | } else { | |
594 | DBGX( dump_buf("input bytes:", buf, res); ); | |
595 | ||
596 | ds_insert_bytes(dsp, buf, res); | |
597 | } | |
598 | } | |
599 | } | |
600 | } while (1); | |
601 | ||
602 | return (void*)0; /* compiler joy */ | |
603 | } | |
604 | ||
605 | ||
606 | void dump_buf(char * strp, uint8_t * bufp, int len) | |
607 | { | |
608 | int i; | |
609 | ||
610 | printf("%s read %d bytes: ", strp, len); | |
611 | for(i=0; i<len; i++) printf("0x%02x [%c]",bufp[i],bufp[i]>=32 && bufp[i]<127 ? bufp[i] : '.'); | |
612 | printf("\n"); | |
613 | } | |
614 | ||
615 | ||
616 | /* emits string and newline */ | |
617 | void | |
618 | ds_output(ds_state_t *dsp, const char *str) | |
619 | { | |
620 | if (dsp->uses_term && dsp->tty_attached) { | |
621 | (void) write(dsp->tty_skt, str, strlen(str)); | |
622 | (void) write(dsp->tty_skt, "\n", 1); | |
623 | } else { | |
624 | puts(str); | |
625 | fflush(stdout); | |
626 | } | |
627 | } | |
628 | ||
629 | ||
630 | ||
631 | ||
632 | ||
633 | ||
634 | ||
635 | /* FIXME: This compile option | |
636 | * enables the console to force an external chip reset of any procs | |
637 | * attached to the same domain. | |
638 | * This is somewhat of a hack, and should be handled else where ... | |
639 | * ... which is why it is not a device option in the config file | |
640 | */ | |
641 | ||
642 | #if CONSOLE_RESET /* { */ | |
643 | void | |
644 | ds_domain_reset(ds_state_t * dsp) | |
645 | { | |
646 | // domain_t * dp; | |
647 | int i; | |
648 | ||
649 | /* | |
650 | * FIXME: this is a hack ... | |
651 | * walk the procs in my domain, and reset them all | |
652 | */ | |
653 | ||
654 | // dp = dsp->config_devp->domainp; | |
655 | ||
656 | for (i=0; i<dp->procs.count; i++) { | |
657 | config_proc_t * cp; | |
658 | ||
659 | cp = LIST_ENTRY(dp->procs, i); | |
660 | ||
661 | cp->proc_typep->ext_signal(cp, ES_Reset); | |
662 | } | |
663 | } | |
664 | #endif /* } */ | |
665 | ||
666 | void | |
667 | ds_insert_bytes(ds_state_t *dsp, uint8_t *bufp, int len) | |
668 | { | |
669 | int i; | |
670 | ||
671 | pthread_mutex_lock(&dsp->tty_lock); | |
672 | ||
673 | /* | |
674 | * process all characters in buffer, don't check for overrun | |
675 | * right away | |
676 | */ | |
677 | for (i = 0; i < len; i++) { | |
678 | int c = bufp[i]; | |
679 | ||
680 | DBGX( printf("State %d : Char typed 0x%x [%c]\n", (int)dsp->tilde_state, c, c>=' ' && c<127 ? c : '.'); fflush(stdout); ); | |
681 | ||
682 | switch (dsp->tilde_state) { | |
683 | case TE_Normal: /* waiting for CR */ | |
684 | if (c == '\n') { | |
685 | dsp->tilde_state = TE_SawCR; | |
686 | break; | |
687 | } | |
688 | case TE_SawCR: /* waiting for tilde */ | |
689 | if (c == '~') { | |
690 | dsp->tilde_state = TE_SawTilde; | |
691 | goto skip; | |
692 | } | |
693 | if (c != '\n') dsp->tilde_state = TE_Normal; | |
694 | break; | |
695 | ||
696 | case TE_SawTilde: /* tilde command */ | |
697 | dsp->tilde_state = TE_SawCR; | |
698 | switch (c) { | |
699 | case '~': /* emit single tilde */ | |
700 | dsp->tilde_state = TE_Normal; | |
701 | break; | |
702 | ||
703 | case '#': /* BREAK */ | |
704 | dsp->line_status |= DS_LSTAT_BREAK; | |
705 | ds_output(dsp, "BREAK"); | |
706 | /* allow another ~ cmd without CR */ | |
707 | goto skip; | |
708 | ||
709 | case '?': /* help */ | |
710 | ds_output(dsp, "dumb_serial tilde escapes:"); | |
711 | ds_output(dsp, "\t# BREAK"); | |
712 | ds_output(dsp, "\t~ generate tilde"); | |
713 | #if CONSOLE_RESET /* { */ | |
714 | ds_output(dsp, "\tx external reset for CPUs in same domain"); | |
715 | #endif /* } */ | |
716 | #if !NDEBUG /* { */ | |
717 | ds_output(dsp, "\ti dump I-TLB contents for CPUs in same domain"); | |
718 | ds_output(dsp, "\td dump D-TLB contents for CPUs in same domain"); | |
719 | ds_output(dsp, "\tb toggle the debug output enable bits"); | |
720 | #endif /* } */ | |
721 | ds_output(dsp, "\t? this message"); | |
722 | /* allow another ~ cmd without CR */ | |
723 | goto skip; | |
724 | ||
725 | #if CONSOLE_RESET /* { */ | |
726 | case 'x': /* Hack to enable extern chip reset easily */ | |
727 | ds_output(dsp, "Resetting cpu(s) in domain"); | |
728 | ds_domain_reset(dsp); | |
729 | goto skip; /* swallow 'x' character after sending reset */ | |
730 | #endif /* } */ | |
731 | ||
732 | #if !NDEBUG /* { */ | |
733 | case 'i': | |
734 | // ds_dump_tlb(dsp, false); | |
735 | goto skip; | |
736 | case 'd': | |
737 | // ds_dump_tlb(dsp, true); | |
738 | goto skip; | |
739 | case 'b': | |
740 | // debug_bits ^= -1; | |
741 | goto skip; | |
742 | #endif /* } */ | |
743 | ||
744 | default: /* eat current char */ | |
745 | /* | |
746 | * FIXME could emit ~ and current char | |
747 | * but it's harder | |
748 | */ | |
749 | ds_output(dsp, "~? for tilde help"); | |
750 | goto skip; | |
751 | } | |
752 | break; | |
753 | } | |
754 | if (dsp->in.count < dsp->in.size) { | |
755 | dsp->in.bufp[dsp->in.tail] = bufp[i]; | |
756 | dsp->in.tail++; | |
757 | if (dsp->in.tail >= dsp->in.size) | |
758 | dsp->in.tail = 0; | |
759 | dsp->in.count++; | |
760 | dsp->line_status |= DS_LSTAT_DATA_READY; | |
761 | } else { | |
762 | if (!(dsp->line_status & DS_LSTAT_OVERRUN)) | |
763 | fprintf(stderr, "dumbserial: buffer overrun"); | |
764 | dsp->line_status |= DS_LSTAT_OVERRUN; | |
765 | } | |
766 | skip:; | |
767 | } | |
768 | pthread_mutex_unlock(&dsp->tty_lock); | |
769 | } | |
770 | ||
771 | #if NDEBUG /* { */ | |
772 | /* hack for TLB debugging - to go away FIXME */ | |
773 | ||
774 | /* First find all the cpus, then dump their TLBs ... */ | |
775 | static void ds_dump_tlb(ds_state_t * dsp, bool is_dtlb) | |
776 | { | |
777 | extern void niagara_dump_tlbs(config_proc_t * cp, bool is_dtlb); | |
778 | domain_t * dp; | |
779 | int i; | |
780 | ||
781 | /* | |
782 | * FIXME: this is a hack ... | |
783 | * walk the procs in my domain, and dump the tlb contents | |
784 | */ | |
785 | ||
786 | dp = dsp->config_devp->domainp; | |
787 | ||
788 | for (i=0; i<dp->procs.count; i++) { | |
789 | config_proc_t * cp; | |
790 | ||
791 | cp = LIST_ENTRY(dp->procs, i); | |
792 | ||
793 | niagara_dump_tlbs(cp, is_dtlb); /* FIXME */ | |
794 | } | |
795 | } | |
796 | #endif /* } */ | |
797 | ||
798 | extern "C" void dumbserial_create_instance (const char *modname, const char *instance_name) | |
799 | { | |
800 | ds_state_t * dsp = new ds_state_t; | |
801 | ||
802 | dsp->uses_term = true; | |
803 | dsp->in.size = DEFAULT_RXFIFOSZ; | |
804 | dsp->reg_shift = DS_REG_SHIFT_DEF; | |
805 | ||
806 | dsp->term_strp = (char *)malloc(256); | |
807 | ||
808 | mmi_instance_t instance = mmi_register_instance(modname, instance_name, (void *) dsp, "dumbserial model"); | |
809 | ||
810 | // parse startpa, endpa, type values for this instance | |
811 | // syntax: sysconf dumbserial <name> type=<type> startpa=<pa> endpa=<pa> | |
812 | // where <type> is either HYPERVISOR or GUEST | |
813 | // syntax example: sysconf dumbserial ds0 type=HYPERVISOR startpa=0xfff0c2c000 endpa=0xfff0c2cfff | |
814 | ||
815 | int argc = mmi_argc(instance); | |
816 | int i; | |
817 | for (i=0; i<argc; i++) { | |
818 | char * arg = strdup(mmi_argv(instance, i)); | |
819 | char * marker; | |
820 | char * lv = strtok_r(arg, "=", &marker); | |
821 | if (strcmp(lv, "startpa") == 0) { | |
822 | errno = 0; | |
823 | char * rv = strtok_r(NULL, "=", &marker); | |
824 | dsp->startpa = strtoull(rv, NULL, 0); | |
825 | if (errno) { | |
826 | perror("dumbserial: error parsing startpa"); | |
827 | exit(1); | |
828 | } | |
829 | } else if (strcmp(lv, "endpa") == 0) { | |
830 | errno = 0; | |
831 | char * rv = strtok_r(NULL, "=", &marker); | |
832 | dsp->endpa = strtoull(rv, NULL, 0); | |
833 | if (errno) { | |
834 | perror("dumbserial: error parsing startpa"); | |
835 | exit(1); | |
836 | } | |
837 | } else if (strcmp(lv, "type") == 0) { | |
838 | char * rv = strtok_r(NULL, "=", &marker); | |
839 | dsp->type = strdup(rv); | |
840 | } // else - do nothing | |
841 | ||
842 | free(arg); | |
843 | } | |
844 | ||
845 | if (strcmp(dsp->type, "HYPERVISOR") == 0) { | |
846 | strcpy(dsp->term_strp, "xterm -l -lf hypervisor1.log -bg black -fg green -geometry 80x20+170+300 -T 'Hypervisor Console' -e ./netcons %s %d &"); | |
847 | // dumbserials[HV_DEVICE] = dsp; | |
848 | } else if (strcmp(dsp->type, "GUEST") == 0) { | |
849 | strcpy(dsp->term_strp, "xterm -l -lf guest1.log -bg black -fg green -geometry 80x35+200+30 -T 'Guest Console' -e ./netcons %s %d &"); | |
850 | } else { | |
851 | fprintf(stderr, "DUMBSERIAL: invalid type in sysconf directive (%s) - must be HYPERVISOR or GUEST\n", dsp->type); | |
852 | } | |
853 | ||
854 | // Parses memory information | |
855 | // ds_parse(); | |
856 | // loads file | |
857 | ds_init(dsp); | |
858 | ||
859 | // register IO operations | |
860 | uint64_t sz = dsp->endpa - dsp->startpa + 1; | |
861 | ui->output ("dumbserial (%s): register LD/ST access @PA=0x%llx size=0x%llx\n", instance_name, dsp->startpa, sz); | |
862 | if (mmi_map_physio(dsp->startpa, sz, (void *) dsp, dumbserial_physio_access)) { | |
863 | fprintf (stderr, "dumbserial (%s): unable to register IO interceptor @0x%llx size 0x%llx\n", instance_name, dsp->startpa, sz); | |
864 | return; | |
865 | } | |
866 | } | |
867 | ||
868 | extern "C" void _init () { | |
869 | ||
870 | char buff[128]; | |
871 | int ncpu; | |
872 | int argc; | |
873 | char *blaze_ver_s; | |
874 | float blaze_ver; | |
875 | ||
876 | if (! mmi_register_instance_creator ("dumbserial", dumbserial_create_instance )) { | |
877 | fprintf (stderr, "Cannot register instance creator for <%s> \n", "dumbserial"); | |
878 | return; | |
879 | } | |
880 | ||
881 | /* Assign interface */ | |
882 | ||
883 | } | |
884 | ||
885 | ///////////////////////////////////////////////// | |
886 | ||
887 | extern "C" void _fini () | |
888 | { | |
889 | } |