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

Last change on this file since 82 was 82, checked in by chris, 14 years ago

Version 1.09
============

  • Added new command line parameter, "/4", which will force a track size of 56 sectors to be reported in geometry requests. This will improve performance for newer HDs with 4K sectors and file systems which use block sizes larger than 512 bytes (FAT, JFS, ...) because all partitions will be aligned on 4K (8 sector) boundaries.
  • Changed AP_DEVICE parameter to ata_cmd() from 16 bits to 8 bits -- the corresponding field in ATA_CMD has always been 8 bits and there's no point discarding the lower 8 bits of the AP_DEVICE parameter as a general rule. Besides, it confused me in the end...
  • Always return some kind of error in ata_req_sense() because this function will only be called when we received an error interrupt; atapi_req_sense() already does this.
  • Cosmetic changes
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 serial port
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 serial port */
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 as initial delay loop counter.
550 * When the timer triggers, 'mdelay()' will stop and update the delay loop
551 * 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.