]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. The rights granted to you under the License | |
10 | * may not be used to create, or enable the creation or redistribution of, | |
11 | * unlawful or unlicensed copies of an Apple operating system, or to | |
12 | * circumvent, violate, or enable the circumvention or violation of, any | |
13 | * terms of an Apple operating system software license agreement. | |
14 | * | |
15 | * Please obtain a copy of the License at | |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
17 | * | |
18 | * The Original Code and all software distributed under the License are | |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
23 | * Please see the License for the specific language governing rights and | |
24 | * limitations under the License. | |
25 | * | |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ | |
27 | */ | |
28 | /* | |
29 | * @OSF_COPYRIGHT@ | |
30 | */ | |
31 | /* | |
32 | * @APPLE_FREE_COPYRIGHT@ | |
33 | */ | |
34 | ||
35 | #include <mach_kdb.h> | |
36 | #include <platforms.h> | |
37 | #include <serial_console_default.h> | |
38 | ||
39 | #include <kern/spl.h> | |
40 | #include <machine/machparam.h> /* spl definitions */ | |
41 | #include <types.h> | |
42 | #include <console/video_console.h> | |
43 | #include <kern/kalloc.h> | |
44 | #include <kern/thread.h> | |
45 | #include <ppc/misc_protos.h> | |
46 | #include <ppc/serial_io.h> | |
47 | #include <kern/cpu_number.h> | |
48 | #include <ppc/Firmware.h> | |
49 | #include <ppc/proc_reg.h> | |
50 | #include <ppc/cpu_internal.h> | |
51 | #include <ppc/exception.h> | |
52 | #include <pexpert/pexpert.h> | |
53 | ||
54 | /* | |
55 | * A machine MUST have a console. In our case | |
56 | * things are a little complicated by the graphic | |
57 | * display: people expect it to be their "console", | |
58 | * but we'd like to be able to live without it. | |
59 | * This is not to be confused with the "rconsole" thing: | |
60 | * that just duplicates the console I/O to | |
61 | * another place (for debugging/logging purposes). | |
62 | */ | |
63 | ||
64 | const int console_unit = 0; | |
65 | const int console_chan_default = CONSOLE_PORT; | |
66 | #define console_chan (console_chan_default) /* ^ cpu_number()) */ | |
67 | ||
68 | #define OPS(putc, getc, nosplputc, nosplgetc) putc, getc | |
69 | ||
70 | const struct console_ops { | |
71 | int (*putc)(int, int, int); | |
72 | int (*getc)(int, int, boolean_t, boolean_t); | |
73 | } cons_ops[] = { | |
74 | #define SCC_CONS_OPS 0 | |
75 | {OPS(scc_putc, scc_getc, no_spl_scputc, no_spl_scgetc)}, | |
76 | #define VC_CONS_OPS 1 | |
77 | {OPS(vcputc, vcgetc, no_spl_vcputc, no_spl_vcgetc)}, | |
78 | }; | |
79 | #define NCONSOPS (sizeof cons_ops / sizeof cons_ops[0]) | |
80 | ||
81 | #if SERIAL_CONSOLE_DEFAULT | |
82 | #define CONS_OPS SCC_CONS_OPS | |
83 | #define CONS_NAME "com" | |
84 | #else | |
85 | #define CONS_OPS VC_CONS_OPS | |
86 | #define CONS_NAME "vc" | |
87 | #endif | |
88 | ||
89 | #define MP_SAFE_CONSOLE 1 /* Set this to 1 to allow more than 1 processor to print at once */ | |
90 | #if MP_SAFE_CONSOLE | |
91 | ||
92 | struct ppcbfr { /* Controls multiple processor output */ | |
93 | unsigned int pos; /* Current position in buffer */ | |
94 | unsigned int noprompt; /* Set if we skip the prompt */ | |
95 | unsigned int echo; /* Control character echoing */ | |
96 | char buffer[256]; /* Fairly big buffer */ | |
97 | }; | |
98 | typedef struct ppcbfr ppcbfr_t; | |
99 | ||
100 | ppcbfr_t cbfr_boot_cpu; /* Get one for boot cpu */ | |
101 | volatile unsigned int cbfpend; /* A buffer is pending output */ | |
102 | volatile unsigned int sconowner=-1; /* Mark who's actually writing */ | |
103 | ||
104 | #endif | |
105 | ||
106 | ||
107 | unsigned int cons_ops_index = CONS_OPS; | |
108 | unsigned int killprint = 0; | |
109 | unsigned int debcnputc = 0; | |
110 | extern unsigned int mappingdeb0; | |
111 | extern int debugger_cpu; | |
112 | ||
113 | void *console_per_proc_alloc(boolean_t boot_processor) | |
114 | { | |
115 | ppcbfr_t *cbfr_cpu; | |
116 | ||
117 | if (boot_processor) | |
118 | cbfr_cpu = &cbfr_boot_cpu; | |
119 | else { | |
120 | cbfr_cpu = (ppcbfr_t *)kalloc(sizeof(ppcbfr_t)); | |
121 | if (cbfr_cpu == (ppcbfr_t *)NULL) | |
122 | return (void *)NULL; | |
123 | } | |
124 | bzero((char *)cbfr_cpu, sizeof(ppcbfr_t)); | |
125 | return (void *)cbfr_cpu; | |
126 | } | |
127 | ||
128 | void console_per_proc_free(void *per_proc_cbfr) | |
129 | { | |
130 | if (per_proc_cbfr == (void *)&cbfr_boot_cpu) | |
131 | return; | |
132 | else | |
133 | kfree(per_proc_cbfr, sizeof(ppcbfr_t)); | |
134 | } | |
135 | ||
136 | ||
137 | static void _cnputc(char c) | |
138 | { | |
139 | cons_ops[cons_ops_index].putc(console_unit, console_chan, c); | |
140 | } | |
141 | ||
142 | void cnputcusr(char c) { /* Echo input character directly */ | |
143 | struct per_proc_info *procinfo; | |
144 | spl_t s; | |
145 | ||
146 | s=splhigh(); | |
147 | procinfo = getPerProc(); | |
148 | ||
149 | hw_atomic_add(&(procinfo->debugger_holdoff), 1); /* Don't allow debugger entry just now (this is a HACK) */ | |
150 | ||
151 | _cnputc( c); /* Echo the character */ | |
152 | if(c=='\n') _cnputc( '\r'); /* Add a return if we had a new line */ | |
153 | ||
154 | hw_atomic_sub(&(procinfo->debugger_holdoff), 1); /* Don't allow debugger entry just now (this is a HACK) */ | |
155 | splx(s); | |
156 | return; | |
157 | } | |
158 | ||
159 | void | |
160 | cnputc(char c) | |
161 | { | |
162 | unsigned int oldpend, i, cpu, ourbit, sccpu; | |
163 | struct per_proc_info *procinfo; | |
164 | ppcbfr_t *cbfr, *cbfr_cpu; | |
165 | spl_t s; | |
166 | ||
167 | #if MP_SAFE_CONSOLE | |
168 | ||
169 | /* | |
170 | * Handle multiple CPU console output. | |
171 | * Note: this thing has gotten god-awful complicated. We need a better way. | |
172 | */ | |
173 | ||
174 | ||
175 | if(killprint) { | |
176 | return; /* If printing is disabled, bail... */ | |
177 | } | |
178 | ||
179 | s=splhigh(); /* Don't bother me */ | |
180 | procinfo = getPerProc(); | |
181 | cpu = procinfo->cpu_number; | |
182 | cbfr = procinfo->pp_cbfr; | |
183 | ||
184 | hw_atomic_add(&(procinfo->debugger_holdoff), 1); /* Don't allow debugger entry just now (this is a HACK) */ | |
185 | ||
186 | ourbit = 1 << cpu; /* Make a mask for just us */ | |
187 | if(debugger_cpu != -1) { /* Are we in the debugger with empty buffers? */ | |
188 | ||
189 | while(sconowner != cpu) { /* Anyone but us? */ | |
190 | hw_compare_and_store(-1, cpu, (unsigned int *)&sconowner); /* Try to mark it for us if idle */ | |
191 | } | |
192 | ||
193 | _cnputc( c); /* Yeah, just write it */ | |
194 | if(c=='\n') /* Did we just write a new line? */ | |
195 | _cnputc( '\r'); /* Yeah, just add a return */ | |
196 | ||
197 | sconowner=-1; /* Mark it idle */ | |
198 | hw_atomic_sub(&(procinfo->debugger_holdoff), 1); /* Don't allow debugger entry just now (this is a HACK) */ | |
199 | ||
200 | splx(s); | |
201 | return; /* Leave... */ | |
202 | } | |
203 | ||
204 | ||
205 | while(ourbit&cbfpend); /* We aren't "double buffered," so we'll just wait until the buffers are written */ | |
206 | isync(); /* Just in case we had to wait */ | |
207 | ||
208 | if(c) { /* If the character is not null */ | |
209 | cbfr->buffer[cbfr->pos]=c; /* Fill in the buffer for our CPU */ | |
210 | cbfr->pos++; /* Up the count */ | |
211 | if(cbfr->pos > 253) { /* Is the buffer full? */ | |
212 | cbfr->buffer[254]='\n'; /* Yeah, set the second to last as a LF */ | |
213 | cbfr->buffer[255]='\r'; /* And the last to a CR */ | |
214 | cbfr->pos=256; /* Push the buffer to the end */ | |
215 | c='\r'; /* Set character to a CR */ | |
216 | } | |
217 | } | |
218 | ||
219 | if(c == '\n') { /* Are we finishing a line? */ | |
220 | cbfr->buffer[cbfr->pos]='\r'; /* And the last to a CR */ | |
221 | cbfr->pos++; /* Up the count */ | |
222 | c='\r'; /* Set character to a CR */ | |
223 | } | |
224 | ||
225 | #if 1 | |
226 | if(cbfr->echo == 1) { /* Did we hit an escape last time? */ | |
227 | if(c == 'K') { /* Is it a partial clear? */ | |
228 | cbfr->echo = 2; /* Yes, enter echo mode */ | |
229 | } | |
230 | else cbfr->echo = 0; /* Otherwise reset escape */ | |
231 | } | |
232 | else if(cbfr->echo == 0) { /* Not in escape sequence, see if we should enter */ | |
233 | cbfr->echo = 1; /* Set that we are in escape sequence */ | |
234 | } | |
235 | #endif | |
236 | ||
237 | if((c == 0x00) || (c == '\r') || (cbfr->echo == 2)) { /* Try to push out all buffers if we see CR or null */ | |
238 | ||
239 | while(1) { /* Loop until we see who's doing this */ | |
240 | oldpend=cbfpend; /* Get the currentest pending buffer flags */ | |
241 | if(hw_compare_and_store(oldpend, oldpend|ourbit, (unsigned int *)&cbfpend)) /* Swap ours on if no change */ | |
242 | break; /* Bail the loop if it worked */ | |
243 | } | |
244 | ||
245 | if(!hw_compare_and_store(-1, cpu, (unsigned int *)&sconowner)) { /* See if someone else has this, and take it if not */ | |
246 | procinfo->debugger_holdoff = 0; /* Allow debugger entry (this is a HACK) */ | |
247 | splx(s); /* Let's take some 'rupts now */ | |
248 | return; /* We leave here, 'cause another processor is already writing the buffers */ | |
249 | } | |
250 | ||
251 | while(1) { /* Loop to dump out all of the finished buffers */ | |
252 | oldpend=cbfpend; /* Get the most current finished buffers */ | |
253 | for(sccpu=0; sccpu<real_ncpus; sccpu++) { /* Cycle through all CPUs buffers */ | |
254 | if ((PerProcTable[sccpu].ppe_vaddr == 0) | |
255 | || (PerProcTable[sccpu].ppe_vaddr->pp_cbfr == 0)) | |
256 | continue; | |
257 | ||
258 | cbfr_cpu = PerProcTable[sccpu].ppe_vaddr->pp_cbfr; | |
259 | ||
260 | if(oldpend&(1<<sccpu)) { /* Does this guy have a buffer to do? */ | |
261 | ||
262 | #if 0 | |
263 | if(!cbfr_cpu->noprompt) { /* Don't prompt if there was not CR before */ | |
264 | _cnputc( '{'); /* Mark CPU number */ | |
265 | _cnputc( '0'+sccpu); /* Mark CPU number */ | |
266 | _cnputc( '.'); /* (TEST/DEBUG) */ | |
267 | _cnputc( '0'+cpu); /* (TEST/DEBUG) */ | |
268 | _cnputc( '}'); /* Mark CPU number */ | |
269 | _cnputc( ' '); /* Mark CPU number */ | |
270 | } | |
271 | #endif | |
272 | ||
273 | for(i=0; i<cbfr_cpu->pos; i++) { /* Do the whole buffer */ | |
274 | _cnputc(cbfr_cpu->buffer[i]); /* Write it */ | |
275 | } | |
276 | ||
277 | if(cbfr_cpu->buffer[cbfr_cpu->pos-1]!='\r') { /* Was the last character a return? */ | |
278 | cbfr_cpu->noprompt = 1; /* Remember not to prompt */ | |
279 | } | |
280 | else { /* Last was a return */ | |
281 | cbfr_cpu->noprompt = 0; /* Otherwise remember to prompt */ | |
282 | cbfr_cpu->echo = 0; /* And clear echo */ | |
283 | } | |
284 | ||
285 | cbfr_cpu->pos=0; /* Reset the buffer pointer */ | |
286 | ||
287 | while(!hw_compare_and_store(cbfpend, cbfpend&~(1<<sccpu), (unsigned int *)&cbfpend)); /* Swap it off */ | |
288 | } | |
289 | } | |
290 | sconowner=-1; /* Set the writer to idle */ | |
291 | sync(); /* Insure that everything's done */ | |
292 | if(hw_compare_and_store(0, 0, (unsigned int *)&cbfpend)) break; /* If there are no new buffers, we are done... */ | |
293 | if(!hw_compare_and_store(-1, cpu, (unsigned int *)&sconowner)) break; /* If this isn't idle anymore, we're done */ | |
294 | ||
295 | } | |
296 | } | |
297 | hw_atomic_sub(&(procinfo->debugger_holdoff), 1); /* Don't allow debugger entry just now (this is a HACK) */ | |
298 | splx(s); /* Let's take some 'rupts now */ | |
299 | ||
300 | #else /* MP_SAFE_CONSOLE */ | |
301 | _cnputc( c); | |
302 | if (c == '\n') | |
303 | _cnputc('\r'); | |
304 | #endif /* MP_SAFE_CONSOLE */ | |
305 | ||
306 | } | |
307 | ||
308 | int | |
309 | cngetc() | |
310 | { | |
311 | return cons_ops[cons_ops_index].getc(console_unit, console_chan, | |
312 | TRUE, FALSE); | |
313 | } | |
314 | ||
315 | int | |
316 | cnmaygetc() | |
317 | { | |
318 | return cons_ops[cons_ops_index].getc(console_unit, console_chan, | |
319 | FALSE, FALSE); | |
320 | } | |
321 | ||
322 | boolean_t console_is_serial() | |
323 | { | |
324 | return cons_ops_index == SCC_CONS_OPS; | |
325 | } | |
326 | ||
327 | int | |
328 | switch_to_video_console() | |
329 | { | |
330 | int old_cons_ops = cons_ops_index; | |
331 | cons_ops_index = VC_CONS_OPS; | |
332 | return old_cons_ops; | |
333 | } | |
334 | ||
335 | int | |
336 | switch_to_serial_console() | |
337 | { | |
338 | int old_cons_ops = cons_ops_index; | |
339 | cons_ops_index = SCC_CONS_OPS; | |
340 | return old_cons_ops; | |
341 | } | |
342 | ||
343 | /* The switch_to_{video,serial,kgdb}_console functions return a cookie that | |
344 | can be used to restore the console to whatever it was before, in the | |
345 | same way that splwhatever() and splx() work. */ | |
346 | void | |
347 | switch_to_old_console(int old_console) | |
348 | { | |
349 | static boolean_t squawked; | |
350 | unsigned int ops = old_console; | |
351 | ||
352 | if (ops >= NCONSOPS && !squawked) { | |
353 | squawked = TRUE; | |
354 | printf("switch_to_old_console: unknown ops %d\n", ops); | |
355 | } else | |
356 | cons_ops_index = ops; | |
357 | } | |
358 | ||
359 | ||
360 | int | |
361 | vcgetc(__unused int l, | |
362 | __unused int u, | |
363 | __unused boolean_t wait, | |
364 | __unused boolean_t raw) | |
365 | { | |
366 | char c; | |
367 | ||
368 | if( 0 == (*PE_poll_input)( 0, &c)) | |
369 | return( c); | |
370 | else | |
371 | return( 0); | |
372 | } |