source: trunk/src/os2ahci/libc.c@ 66

Last change on this file since 66 was 66, checked in by chris, 15 years ago
  • Added code to dprintf TF_DATA register in restart_ctxhook
  • Cleaned up aws_free() calls; in a nutshell, aws_free() will always be called when IORBs complete (successfully or with an error) and this is now verified; redundant invocations of aws_free() have been removed.
  • atapi_req_sense_pp() now sets the iorb_workspace(iorb)->complete = 1
File size: 21.8 KB
Line 
1/******************************************************************************
2 * libc.c - minimal subset of C runtime library for os2ahci
3 *
4 * Copyright (c) 2010 Christian Mueller. Parts copied from/inspired by the
5 * Linux AHCI driver; those parts are (c) Linux AHCI/ATA maintainers
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include "os2ahci.h"
23
24/* -------------------------- macros and constants ------------------------- */
25
26#define MSG_REPLACEMENT_STRING 1178 /* empty message with a single %; used
27 * for printing custom messages via
28 * DevHelp_Save_Message() */
29
30/* heap management constants */
31#define HEAP_SIZE 8192
32#define HEAP_UNIT 128
33#define HEAP_UNIT_CNT (HEAP_SIZE / HEAP_UNIT)
34
35/* ------------------------ typedefs and structures ------------------------ */
36
37/* mdelay() calibration status */
38typedef enum {
39 MD_NOT_CALIBRATED, /* delay loop not calibrated */
40 MD_CALIBRATION_START, /* calibration run started */
41 MD_CALIBRATION_END, /* calibration run ended */
42 MD_CALIBRATION_DONE /* calibration complete */
43} MDCAL;
44
45/* -------------------------- function prototypes -------------------------- */
46
47static void long_to_asc (long val,
48 char _far *buf,
49 int base,
50 int zero, int flen);
51static void _cdecl _far mdelay_timer_callback (ULONG timer_handle,
52 ULONG parm1,
53 ULONG parm2);
54static int mdelay_cal_end (void);
55
56/* ------------------------ global/static variables ------------------------ */
57
58/* debug COM port base address */
59u16 com_base = 0x03f8;
60
61static char hex_digits[] = "0123456789abcdef";
62static ULONG com_lock;
63
64/* message table for DosHelp_Save_Message() which prints the first string */
65static MSGTABLE init_msgtbl = {
66 MSG_REPLACEMENT_STRING,
67 1,
68 0
69};
70
71/* COM port initialization sequence */
72static struct {
73 int reg;
74 u8 data;
75} com1_init_sequence[] = {
76 3, 0x80, /* overlay divisor latch register at 0x3f8 and 0x3f9 */
77 0, 0x01, /* set low byte of divisor to 1 (115200 baud) */
78 1, 0x00, /* set high byte of divisor to 0 */
79 3, 0x03, /* reset divisor latch register overlay and set 8,n,1 */
80 1, 0x00, /* disable interrupts */
81 4, 0x0f, /* modem control register */
82 -1, 0x00
83};
84
85/* delay loop calibration data */
86volatile MDCAL mdelay_cal_status = 0; /* delay loop calibration status */
87volatile u32 mdelay_loops_per_ms = 0; /* delay loop counter */
88
89/* very small heap for dynamic memory management */
90static u8 heap_buf[HEAP_SIZE];
91static u8 heap_units[HEAP_UNIT_CNT];
92
93/* ----------------------------- start of code ----------------------------- */
94
95/******************************************************************************
96 * Initialize COM1 to 115200,n,8,1
97 *
98 * NOTE: Something is wrong with this code, or the init sequence, but we never
99 * got around to fixing it because it works fine on Virtualbox, and on
100 * physical machines we tend to have the kernel debugger running on the
101 * same port simply because serial ports are not that plenty on PCs
102 * these days, thus KDB will set port parameters for us. This is going
103 * to be fixed eventually...
104 */
105void init_com1(void)
106{
107 int i;
108
109 DevHelp_CreateSpinLock(&com_lock);
110
111 for (i = 0; com1_init_sequence[i].reg != -1; i++) {
112 u16 port = com_base + com1_init_sequence[i].reg;
113 u8 data = com1_init_sequence[i].data;
114 _asm {
115 mov dx, port;
116 mov al, data;
117 out dx, al;
118 }
119 }
120}
121
122/******************************************************************************
123 * Print a formatted message into a string buffer. This is very basic,
124 * supporting only strings and integers (16 and 32 bits (l), decimal (d)
125 * and hex (x)). Formatting length modifiers are only supported with a single
126 * digit -- 32-bit numbers don't need more than 9 characters -- and an
127 * optional '0' in front.
128 */
129int vsprintf(char _far *buf, const char *fmt, va_list va)
130{
131 char _far *orig = buf;
132 char _far *s;
133 int lmod;
134 int fptr;
135 int zero;
136 int flen;
137
138 for (; *fmt != '\0'; fmt++) {
139 switch (*fmt) {
140
141 case '%':
142 fmt++;
143 zero = flen = 0;
144 if (*fmt >= '0' && *fmt <= '9') {
145 /* formatting length modifiers */
146 zero = (*fmt == '0') ? 1 : 0;
147 fmt += zero;
148 if ((flen = *fmt - '0') >= 1 && flen <= 9) {
149 fmt++;
150 }
151 }
152
153 /* data type modifiers */
154 lmod = (*fmt == 'l') ? 1 : 0;
155 fptr = (*fmt == 'F') ? 1 : 0;
156 fmt += lmod + fptr;
157
158 switch (*fmt) {
159
160 case 's':
161 if (fptr) {
162 char _far *p = va_arg(va, char _far *);
163 s = (p == 0) ? "[null]" : p;
164 } else {
165 char *p = va_arg(va, char *);
166 s = (p == 0) ? "[null]" : p;
167 }
168 while ((*buf = *(s++)) != '\0')
169 buf++;
170 break;
171
172 case 'c':
173 *(buf++) = (char) va_arg(va, int);
174 break;
175
176 case 'd':
177 long_to_asc((lmod) ? va_arg(va, long)
178 : va_arg(va, int), buf, 10, zero, flen);
179 buf += strlen(buf);
180 break;
181
182 case 'x':
183 long_to_asc((lmod) ? va_arg(va, u32)
184 : va_arg(va, u16), buf, 16, zero, flen);
185 buf += strlen(buf);
186 break;
187
188 case 'p':
189 if (fptr || lmod) {
190 u16 off = va_arg(va, u16);
191 u16 seg = va_arg(va, u16);
192 long_to_asc(seg, buf, 16, 1, 4);
193 buf += strlen(buf);
194 *(buf++) = ':';
195 long_to_asc(off, buf, 16, 1, 4);
196 buf += strlen(buf);
197 } else {
198 long_to_asc(va_arg(va, u16), buf, 16, 1, 4);
199 buf += strlen(buf);
200 }
201 break;
202
203 default:
204 *(buf++) = *fmt;
205 break;
206 }
207 break;
208
209 case '\n':
210 *(buf++) = '\r';
211 *(buf++) = '\n';
212 break;
213
214 default:
215 *(buf++) = *fmt;
216 break;
217
218 }
219 }
220
221 *buf = '\0';
222 return((int) (buf - orig));
223}
224
225/*******************************************************************************
226 * Print a formatted message into a string buffer. Relies on vsprintf()
227 */
228int sprintf(char _far *buf, const char *fmt, ...)
229{
230 va_list va;
231
232 va_start(va, fmt);
233 return(vsprintf(buf, fmt, va));
234}
235
236/******************************************************************************
237 * Print messages to COM
238 *
239 * NOTES: This function uses a 1K buffer for the resulting message. Thus,
240 * messages should never exceed 1024 bytes.
241 */
242void vprintf(const char *fmt, va_list va)
243{
244 static char buf[1024];
245 char *s;
246
247 spin_lock(com_lock);
248
249 vsprintf(buf, fmt, va);
250
251 /* write debug message to COM1 */
252 for (s = buf; *s != '\0'; s++) {
253
254 /* inp() and outp() are redefined by the DDK in an incompatible
255 * way (only words). Instead of messing around with those
256 * definitions, it's safer and easier to put the whole thing
257 * into an _asm block.
258 *
259 * The C equivalent would look like this:
260 *
261 * while (!(inp(com_base + 5) & 0x20));
262 * outp(com_base, *s);
263 */
264
265 _asm {
266 /* wait until COM transmitter is idle */
267 mov dx, com_base;
268 add dx, 5;
269 transmitter_not_idle:
270 in al, dx;
271 and al, 0x20;
272 jz transmitter_not_idle;
273
274 /* output character to be sent */
275 mov dx, com_base;
276 mov bx, s;
277 mov al, [bx];
278 out dx, al;
279 };
280 }
281
282 spin_unlock(com_lock);
283}
284
285/******************************************************************************
286 * Print messages to COM port
287 */
288void printf(const char *fmt, ...)
289{
290 va_list va;
291
292 va_start(va, fmt);
293 vprintf(fmt, va);
294}
295
296/******************************************************************************
297 * Print a message to the system console. This works only during device driver
298 * initialization.
299 *
300 * NOTE: This function uses a 1K buffer for the resulting message. Thus,
301 * messages should never exceed 1024 bytes...
302 */
303void cprintf(const char *fmt, ...)
304{
305 static char buf[1024];
306 va_list va;
307 size_t len;
308
309 va_start(va, fmt);
310 vsprintf(buf, fmt, va);
311
312 if (debug) {
313 /* print the same message to COM1 as well */
314 printf("%s", buf);
315 }
316
317 /* remove trailing CR/LF (DevHelp_Save_Message() will add it again) */
318 if ((len = strlen(buf)) >= 2 && buf[len-1] == '\n' && buf[len-2] == '\r') {
319 buf[len-2] = '\0';
320 }
321
322 init_msgtbl.MsgStrings[0] = buf;
323 DevHelp_Save_Message((NPBYTE) &init_msgtbl);
324}
325
326/******************************************************************************
327 * Print hex buffer to COM port.
328 */
329void phex(const void _far *p, int len, const char *fmt, ...)
330{
331 va_list va;
332 const unsigned char _far *buf = p;
333 long pos = 0;
334 int i;
335
336 if (!debug) {
337 return;
338 }
339
340 /* print header */
341 va_start(va, fmt);
342 vprintf(fmt, va);
343
344 /* print hex block */
345 while (len > 0) {
346 printf("%Fp ", buf);
347
348 /* print hex block */
349 for (i = 0; i < 16; i++) {
350 if (i < len) {
351 printf("%c%02x", ((i == 8) ? '-' : ' '), buf[i]);
352 } else {
353 printf(" ");
354 }
355 }
356
357 /* print ASCII block */
358 printf(" ");
359 for (i = 0; i < ((len > 16) ? 16 : len); i++) {
360 printf("%c", (buf[i] >= 32 && buf[i] < 128) ? buf[i] : '.');
361 }
362 printf("\n");
363
364 pos += 16;
365 buf += 16;
366 len -= 16;
367 }
368}
369
370/******************************************************************************
371 * Return length of zero-terminated string
372 */
373size_t strlen(const char _far *s)
374{
375 int len = 0;
376
377 while (*(s++) != '\0') {
378 len++;
379 }
380 return(len);
381}
382
383/******************************************************************************
384 * Copy zero-terminated string
385 */
386char _far *strcpy(char _far *dst, const char _far *src)
387{
388 char _far *orig = dst;
389
390 while ((*(dst++) = *(src++)) != '\0');
391 return(orig);
392}
393
394/******************************************************************************
395 * Compare blocks of memory
396 */
397int memcmp(void _far *p1, void _far *p2, size_t len)
398{
399 register char _far *s1 = p1;
400 register char _far *s2 = p2;
401 int n = 0;
402
403 while (len > 0) {
404 if ((n = *(s1++) - *(s2++)) != 0) {
405 return(n);
406 }
407 len--;
408 }
409 return(0);
410}
411
412/******************************************************************************
413 * Convert a string to a long value using the specified base
414 */
415long strtol(const char _far *buf, const char _far * _far *ep, int base)
416{
417 register const char _far *s = buf;
418 long val = 0;
419 int negative = 0;
420
421 /* skip leading whitespace */
422 while (*s == ' ' || *s == '\t') {
423 s++;
424 }
425
426 /* positive or negative */
427 if (*s == '-') {
428 negative = 1;
429 s++;
430 } else if (*s == '+') {
431 s++;
432 }
433
434 /* convert string to long integer */
435 for (;; s++) {
436 int digit = (*s <= '9') ? (*s - '0') : (tolower(*s) - 'a' + 10);
437 if (digit < 0 || digit >= base) {
438 break;
439 }
440 val *= base;
441 val += digit;
442 }
443
444 if (ep != NULL) {
445 *ep = s;
446 }
447 if (negative) {
448 val = -val;
449 }
450 return(val);
451}
452
453/******************************************************************************
454 * Extremely simple and stupid implementation of malloc(). The heap is very
455 * small, only 8K at the moment, and the memory blocks are managed using a
456 * simple array of "number of heap units allocated", zero meaning this unit is
457 * available. Each heap unit is currently 128 bytes.
458 *
459 * Dynamic memory is primarily used for things like ATA identify, ATAPI
460 * sense buffers, etc. and should be freed as soon as possible, otherwise
461 * we'll quickly run out of memory.
462 *
463 * NOTE: This function is not reentrant, thus must be called with the driver-
464 * level spinlock held. The main reason for this design is that most
465 * functions that need dynamic memory are already holding the spinlock.
466 */
467void *malloc(size_t len)
468{
469 u16 units = (len + HEAP_UNIT - 1) / HEAP_UNIT;
470 u16 i;
471 u16 n;
472
473 /* find a sequence of free heap units big enough for the requested length */
474 for (i = 0; i < HEAP_UNIT_CNT; i++) {
475 if (heap_units[i] == 0) {
476 for (n = i; n < i + units && n < HEAP_UNIT_CNT; n++) {
477 if (heap_units[n] != 0) {
478 break;
479 }
480 }
481 if (n == i + units) {
482 /* found a chunk large enough; update 'heap_units[]' */
483 for (; i < n; i++) {
484 heap_units[i] = (u8) (n - i);
485 }
486 return(heap_buf + (n - units) * HEAP_UNIT);
487 }
488
489 /* keep searching... */
490 i = n;
491 } else {
492 /* skip occupied heap units */
493 i += heap_units[i] - 1;
494 }
495 }
496
497 /* out of memory */
498 dprintf("malloc(%d): out of memory\n", len);
499 return(NULL);
500}
501
502/******************************************************************************
503 * Free block of memory allocted by malloc().
504 *
505 * NOTE: This function is not reentrant, thus must be called with the driver-
506 * level spinlock held. The main reason for this design is that most
507 * functions that need dynamic memory are already holding the spinlock.
508 */
509void free(void *ptr)
510{
511 u8 *p = (u8 *) ptr;
512 u16 first_unit;
513 u16 units;
514 u16 i;
515
516 if (p < heap_buf || p >= heap_buf + sizeof(heap_buf) ||
517 (u16) (p - heap_buf) % HEAP_UNIT != 0) {
518 dprintf("free(0x%p): invalid pointer (heap_buf = 0x%p)\n",
519 (u16) p, (u16) heap_buf);
520 return;
521 }
522
523 /* clear unit allocation counters in heap_units[] */
524 first_unit = (u16) (p - heap_buf) / HEAP_UNIT;
525 units = heap_units[first_unit];
526 for (i = first_unit; i < first_unit + units; i++) {
527 heap_units[i] = 0;
528 }
529}
530
531/******************************************************************************
532 * Calibrate 'mdelay()' loop. This is done by setting up a 1 second timer
533 * with a callback that sets 'mdelay_done' to MD_CALIBRATION_END. Then it
534 * calls mdelay() with a large milliseond value an initial delay loop counter
535 * of 1,000,000. When the timer triggers, 'mdelay()' will stop and update
536 * the delay loop counter.
537 *
538 * This function needs to be called at device driver init time. Since it uses
539 * ADD timers, it must be called with interrupts enabled. All this is not very
540 * precise (we should wait for a clock tick before starting, ...) but we don't
541 * really need precise timers.
542 */
543void mdelay_cal(void)
544{
545 ULONG timer_handle;
546
547 dprintf("calibrating delay loop... ");
548
549 mdelay_loops_per_ms = 100000;
550 mdelay_cal_status = MD_CALIBRATION_START;
551
552 ADD_StartTimerMS(&timer_handle, 1000, (PFN) mdelay_timer_callback, 0, 0);
553 mdelay(999999999);
554 ADD_CancelTimer(timer_handle);
555
556 dprintf("done (loops per ms = %ld)\n", mdelay_loops_per_ms);
557}
558
559/******************************************************************************
560 * Wait specified number of milliseconds. This is implemented using a busy
561 * loop and is only good for delays in the millisecond range but never for more
562 * than a few milliseconds and only in situations where a proper timer won't do.
563 * As a rule of thumb, don't call this function and use ADD timers, instead.
564 *
565 * NOTES:
566 *
567 * - Timers are problematic on x86 platforms because there's no reliable
568 * hardware timer on all architectures and the CPU clock speed may change
569 * while executing delay loops (AMD Cool&Quiet and Intel SpeedStep), thus
570 * calibration routines won't really be sufficient. But this usually only
571 * extends the delay and we don't really need a high precision timer. The
572 * exception are things like notebooks that are clocked slower when on
573 * battery and which got booted while on battery. Should still be OK,
574 * though, because our requirements are not that strict.
575 *
576 * - The code in this function is inefficient by design to make sure it
577 * will work with future CPUs which might otherwise be too fast for
578 * our loop counters. Part of this design is using volatile variables to
579 * force memory operations.
580 *
581 * - Before using this function, call mdelay_calibrate() to determine the
582 * number of inner loops required per millisecond.
583 */
584void mdelay(u32 millies)
585{
586 volatile u32 i;
587 volatile u32 n;
588
589 for (i = 0; i < millies; i++) {
590 for (n = 0; n < mdelay_loops_per_ms; n++) {
591 if (mdelay_cal_end()) {
592 /* this is a calibration run that just ended */
593 goto complete_calibration;
594 }
595 }
596 }
597 return;
598
599complete_calibration:
600 /* complete calibration cycle */
601 if (i < 1000) {
602 /* Initial value for delay loop was too high; interpolate results for
603 * an assumed initial delay loop divided by 1000.
604 */
605 i = i * 1000 + mdelay_loops_per_ms % 1000;
606 mdelay_loops_per_ms /= 1000;
607 }
608 mdelay_loops_per_ms = (mdelay_loops_per_ms * i) / 1000;
609 mdelay_cal_status = MD_CALIBRATION_DONE;
610}
611
612/******************************************************************************
613 * Sleep specified number of milliseonds. This is implemented by yielding the
614 * CPU until the system timer value indicates we're done. This function can
615 * only be called at task time, or from a context hook.
616 *
617 * NOTE: The accuracy is limited by the OS/2 timer interrupt frequency which
618 * can lead to intervals up to 55ms (18.2 timer interrupts per second).
619 */
620void msleep(u32 millies)
621{
622 volatile PGINFOSEG gis;
623 ULONG start;
624 ULONG end;
625 PSEL p;
626
627 if (DevHelp_GetDOSVar(DHGETDOSV_SYSINFOSEG, 0, (PPVOID) &p)) {
628 /* no global info segment; use mdelay() */
629 mdelay(millies);
630 return;
631 }
632 gis = (PGINFOSEG) ((u32) *p << 16);
633 start = gis->msecs;
634 end = start + millies;
635
636 if (end < start) {
637 /* wrap-around; wait until 'msecs' has wrapped, too */
638 while (gis->msecs >= start) {
639 DevHelp_Yield();
640 }
641 }
642
643 while (gis->msecs <= end) {
644 DevHelp_Yield();
645 }
646}
647
648/******************************************************************************
649 * Halt processing by submitting an internal error. This is a last resort and
650 * should only be called when the system state is corrupt.
651 */
652void panic(char *msg)
653{
654 DevHelp_InternalError(msg, strlen(msg));
655}
656
657/******************************************************************************
658 * Disable interrupts. The reason for using a separate function for this is
659 * that the presence of _asm statements will disable compiler optimizations.
660 * In order to support nested calls, this function will return 0 if the
661 * interrupts were already disabled or != 0, if not.
662 *
663 * NOTE: SMP systems must use spinlocks, thus this function will only be
664 * compiled on non-SMP builds.
665 */
666#ifndef OS2AHCI_SMP
667int disable(void)
668{
669 int rc = 0;
670
671 _asm {
672 pushf
673 pop ax
674 and ax, 0x0200; /* "interrupts enabled" bit */
675 mov rc, ax;
676 cli
677 }
678
679 return(rc);
680}
681#endif
682
683/******************************************************************************
684 * Enable interrupts. The reason for using a separate function for this is
685 * that the presence of _asm statements will disable compiler optimizations.
686 *
687 * NOTE: SMP systems must use spinlocks, thus this function will only be
688 * compiled on non-SMP builds.
689 */
690#ifndef OS2AHCI_SMP
691void enable(void)
692{
693 _asm sti;
694}
695#endif
696
697/******************************************************************************
698 * Convert 'long' to ASCII with the specified base
699 */
700static void long_to_asc(long val, char _far *buf, int base, int zero, int flen)
701{
702 register unsigned long abs_val = (unsigned long) val;
703 char tmp[80];
704 char _far *ptmp = tmp;
705 char _far *s;
706
707 if (base > 16) {
708 sprintf(buf, "[EVAL]");
709 return;
710 }
711
712 tmp[sizeof(tmp) - 1] = '\0';
713 for (s = ptmp + sizeof(tmp) - 2; s > ptmp; s--) {
714 *s = hex_digits[abs_val % base];
715 flen--;
716 if ((abs_val /= base) == 0) {
717 break;
718 }
719 }
720
721 /* prepend minus sign if val was negative and base is decimal or less */
722 if (val < 0 && base <= 0) {
723 *(--s) = '-';
724 flen--;
725 }
726
727 /* left-pad the resulting number with zeros or spaces up to 'flen' */
728 while (flen > 0) {
729 *(--s) = (zero) ? '0' : ' ';
730 flen--;
731 }
732
733 strcpy(buf, s);
734}
735
736/******************************************************************************
737 * Timer callback handler for 'mdelay_calibrate()'
738 */
739static void _cdecl _far mdelay_timer_callback(ULONG timer_handle,
740 ULONG parm1,
741 ULONG parm2)
742{
743 mdelay_cal_status = MD_CALIBRATION_END;
744}
745
746/******************************************************************************
747 * Determine whether an mdelay calibration run has just ended. This is in a
748 * function to prevent overzealous optimizers from removing the whole delay
749 * loop in mdelay().
750 */
751static int mdelay_cal_end(void)
752{
753 return(mdelay_cal_status == MD_CALIBRATION_END);
754}
Note: See TracBrowser for help on using the repository browser.