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