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

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