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

Last change on this file since 165 was 165, checked in by David Azarewicz, 12 years ago

code cleanup - debug messages
fixed defect in smart ioctl

File size: 21.0 KB
Line 
1/******************************************************************************
2 * libc.c - minimal subset of C runtime library for os2ahci
3 *
4 * Copyright (c) 2011 thi.guten Software Development
5 * Copyright (c) 2011 Mensys B.V.
6 * Portions copyright (c) 2013 David Azarewicz
7 *
8 * Authors: Christian Mueller, Markus Thielen
9 *
10 * Parts copied from/inspired by the Linux AHCI driver;
11 * those parts are (c) Linux AHCI/ATA maintainers
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 */
27
28#include "os2ahci.h"
29
30/* -------------------------- macros and constants ------------------------- */
31
32#define MSG_REPLACEMENT_STRING 1178 /* empty message with a single %; used
33 * for printing custom messages via
34 * DevHelp_Save_Message() */
35
36/* heap management constants */
37#define HEAP_SIZE 8192
38#define HEAP_UNIT 128
39#define HEAP_UNIT_CNT (HEAP_SIZE / HEAP_UNIT)
40
41/* -------------------------- function prototypes -------------------------- */
42
43static void long_to_asc(long val, char _far *buf, int base, int zero, int flen);
44
45/* ------------------------ global/static variables ------------------------ */
46
47/* debug COM port base address */
48u16 com_base = 0;
49
50static char hex_digits[] = "0123456789abcdef";
51static ULONG mem_lock;
52ULONG com_lock;
53
54/* message table for DosHelp_Save_Message() which prints the first string */
55static MSGTABLE init_msgtbl = {
56 MSG_REPLACEMENT_STRING,
57 1,
58 0
59};
60
61static struct {
62 long Baud;
63 u16 Data;
64} BaudCodes[] = {
65 115200, 0x0001,
66 57600, 0x0002,
67 38400, 0x0003,
68 19200, 0x0006,
69 9600, 0x000C,
70 4800, 24,
71 2400, 48,
72 1200, 96,
73 600, 192,
74 300, 384,
75 0, 0 /* end of list */
76};
77
78/* very small heap for dynamic memory management */
79static u8 heap_buf[HEAP_SIZE];
80static u8 heap_units[HEAP_UNIT_CNT];
81static ULONG heap_phys_addr;
82
83/* global info segment */
84volatile PGINFOSEG gis;
85
86
87/* ----------------------------- start of code ----------------------------- */
88
89/******************************************************************************
90 * Initialize libc components
91 */
92void init_libc(void)
93{
94 PSEL p;
95
96 DevHelp_CreateSpinLock(&mem_lock);
97 DevHelp_CreateSpinLock(&com_lock);
98
99 DevHelp_VirtToPhys(heap_buf, &heap_phys_addr);
100
101 /* get global info segment */
102 if (DevHelp_GetDOSVar(DHGETDOSV_SYSINFOSEG, 0, (PPVOID) &p) == 0) {
103 gis = (PGINFOSEG) ((u32) *p << 16);
104 }
105
106}
107
108/******************************************************************************
109 * Initialize COM port to 115200,n,8,1
110 *
111 * NOTE: Something is wrong with this code, or the init sequence, but we never
112 * got around to fixing it because it works fine on Virtualbox, and on
113 * physical machines we tend to have the kernel debugger running on the
114 * same port, thus KDB will set port parameters for us. This is going
115 * to be fixed eventually...
116 */
117void init_com(long BaudRate)
118{
119 int i;
120 u16 RegData;
121
122 if (com_base == 0) return; /* no com port in use */
123
124 /* Find the baud code for the given baud rate */
125 for (i = 0; BaudCodes[i].Baud; i++) if (BaudCodes[i].Baud == BaudRate) break;
126 if (BaudCodes[i].Baud == 0) i = 0; /* default to 115200 */
127 RegData = BaudCodes[i].Data;
128
129 spin_lock(com_lock);
130
131 __asm {
132 mov bx,RegData
133 mov dx,com_base ; Base address
134 cli
135 add dx,3 ; Line Control (+3)
136 mov al,10000000b ; Set baud flag
137 out dx,al ; for speed setting
138
139 ; Set Baud
140 dec dx ; High divisor address
141 dec dx ; (+1)
142 mov al,bh ; High divisor value
143 out dx,al ; set it
144 dec dx ; Low divisor address (+0)
145 mov al,bl ; Low divisor value
146 out dx,al ; set baud rate
147
148 ; Set frame
149 mov al,00000011b ; Set 8 bit, none, none
150 add dx,3 ; Line Control (+3)
151 out dx,al
152
153 inc dx
154 mov al,3
155 out dx,al ; DTR & RTS to High
156 sti
157 }
158
159 spin_unlock(com_lock);
160}
161
162/******************************************************************************
163 * Print a formatted message into a string buffer. This is very basic,
164 * supporting only strings and integers (16 and 32 bits (l), decimal (d)
165 * and hex (x)). Formatting length modifiers are only supported with a single
166 * digit -- 32-bit numbers don't need more than 9 characters -- and an
167 * optional '0' in front.
168 */
169int vsprintf(char _far *buf, const char *fmt, va_list va)
170{
171 char _far *orig = buf;
172 char _far *s;
173 int lmod;
174 int fptr;
175 int zero;
176 int flen;
177
178 for (; *fmt != '\0'; fmt++) {
179 switch (*fmt) {
180
181 case '%':
182 fmt++;
183 zero = flen = 0;
184 if (*fmt >= '0' && *fmt <= '9') {
185 /* formatting length modifiers */
186 zero = (*fmt == '0') ? 1 : 0;
187 fmt += zero;
188 if ((flen = *fmt - '0') >= 1 && flen <= 9) {
189 fmt++;
190 }
191 }
192
193 /* data type modifiers */
194 lmod = (*fmt == 'l') ? 1 : 0;
195 fptr = (*fmt == 'F') ? 1 : 0;
196 fmt += lmod + fptr;
197
198 switch (*fmt) {
199
200 case 's':
201 if (fptr) {
202 char _far *p = va_arg(va, char _far *);
203 s = (p == 0) ? "[null]" : p;
204 } else {
205 char *p = va_arg(va, char *);
206 s = (p == 0) ? "[null]" : p;
207 }
208 while ((*buf = *(s++)) != '\0')
209 buf++;
210 break;
211
212 case 'c':
213 *(buf++) = (char) va_arg(va, int);
214 break;
215
216 case 'd':
217 long_to_asc((lmod) ? va_arg(va, long)
218 : va_arg(va, int), buf, 10, zero, flen);
219 buf += strlen(buf);
220 break;
221
222 case 'x':
223 long_to_asc((lmod) ? va_arg(va, u32)
224 : va_arg(va, u16), buf, 16, zero, flen);
225 buf += strlen(buf);
226 break;
227
228 case 'p':
229 if (fptr || lmod) {
230 u16 off = va_arg(va, u16);
231 u16 seg = va_arg(va, u16);
232 long_to_asc(seg, buf, 16, 1, 4);
233 buf += strlen(buf);
234 *(buf++) = ':';
235 long_to_asc(off, buf, 16, 1, 4);
236 buf += strlen(buf);
237 } else {
238 long_to_asc(va_arg(va, u16), buf, 16, 1, 4);
239 buf += strlen(buf);
240 }
241 break;
242
243 default:
244 *(buf++) = *fmt;
245 break;
246 }
247 break;
248
249 case '\n':
250 *(buf++) = '\r';
251 *(buf++) = '\n';
252 break;
253
254 default:
255 *(buf++) = *fmt;
256 break;
257
258 }
259 }
260
261 *buf = '\0';
262 return((int) (buf - orig));
263}
264
265/*******************************************************************************
266 * Print a formatted message into a string buffer. Relies on vsprintf()
267 */
268int sprintf(char _far *buf, const char *fmt, ...)
269{
270 va_list va;
271
272 va_start(va, fmt);
273 return(vsprintf(buf, fmt, va));
274}
275
276/******************************************************************************
277 * Print messages to serial port
278 *
279 * NOTES: This function uses a 1K buffer for the resulting message. Thus,
280 * messages should never exceed 1024 bytes.
281 */
282void vprintf(int ts, const char *fmt, va_list va)
283{
284 static char buf[1024];
285 char *s;
286 int len = 0;
287
288 spin_lock(com_lock);
289
290 if (ts) {
291 /* include timestamp */
292 if (gis) {
293 len = sprintf(buf, "[%ld] ", gis->msecs);
294 } else {
295 len = sprintf(buf, "[gis=0] ");
296 }
297 }
298
299 len += vsprintf(buf + len, fmt, va);
300
301 if (com_base == 0) {
302 /* write debug message to trace buffer, not COM port */
303 trace_write(buf, len);
304 spin_unlock(com_lock);
305 return;
306 }
307
308 /* write debug message to serial port */
309 for (s = buf; *s != '\0'; s++) {
310
311 /* inp() and outp() are redefined by the DDK in an incompatible
312 * way (only words). Instead of messing around with those
313 * definitions, it's safer and easier to put the whole thing
314 * into an _asm block.
315 *
316 * The C equivalent would look like this:
317 *
318 * while (!(inp(com_base + 5) & 0x20));
319 * outp(com_base, *s);
320 */
321
322 _asm {
323 /* wait until COM transmitter is idle */
324 mov dx, com_base;
325 add dx, 5;
326 transmitter_not_idle:
327 in al, dx;
328 and al, 0x20;
329 jz transmitter_not_idle;
330
331 /* output character to be sent */
332 mov dx, com_base;
333 mov bx, s;
334 mov al, [bx];
335 out dx, al;
336 };
337 }
338
339 spin_unlock(com_lock);
340}
341
342/******************************************************************************
343 * Print messages to COM port
344 */
345void printf(const char *fmt, ...)
346{
347 va_list va;
348
349 va_start(va, fmt);
350 vprintf(1, fmt, va);
351}
352
353/******************************************************************************
354 * Print messages to COM port with no time stamp
355 */
356void printf_nts(const char *fmt, ...)
357{
358 va_list va;
359
360 va_start(va, fmt);
361 vprintf(0, fmt, va);
362}
363
364
365/******************************************************************************
366 * Print a message to the system console. This works only during device driver
367 * initialization.
368 *
369 * NOTE: This function uses a 1K buffer for the resulting message. Thus,
370 * messages should never exceed 1024 bytes...
371 */
372void cprintf(const char *fmt, ...)
373{
374 static char buf[1024];
375 va_list va;
376 size_t len;
377
378 va_start(va, fmt);
379 vsprintf(buf, fmt, va);
380
381 if (debug) {
382 /* print the same message to COM1/trace as well */
383 aprintf("%s", buf);
384 }
385
386 /* remove trailing CR/LF (DevHelp_Save_Message() will add it again) */
387 if ((len = strlen(buf)) >= 2 && buf[len-1] == '\n' && buf[len-2] == '\r') {
388 buf[len-2] = '\0';
389 }
390
391 init_msgtbl.MsgStrings[0] = buf;
392 DevHelp_Save_Message((NPBYTE) &init_msgtbl);
393}
394
395/******************************************************************************
396 * Print hex buffer to COM port.
397 */
398void phex(const void _far *p, int len, const char *fmt, ...)
399{
400 va_list va;
401 const unsigned char _far *buf = p;
402 int i;
403
404 if (!debug) {
405 return;
406 }
407
408 /* print header */
409 va_start(va, fmt);
410 vprintf(1, fmt, va);
411
412 /* print hex block */
413 while (len > 0) {
414 ntprintf("%Fp ", buf);
415
416 /* print hex block */
417 for (i = 0; i < 16; i++) {
418 if (i < len) {
419 ntprintf("%c%02x", ((i == 8) ? '-' : ' '), buf[i]);
420 } else {
421 ntprintf(" ");
422 }
423 }
424
425 /* print ASCII block */
426 ntprintf(" ");
427 for (i = 0; i < ((len > 16) ? 16 : len); i++) {
428 ntprintf("%c", (buf[i] >= 32 && buf[i] < 128) ? buf[i] : '.');
429 }
430 ntprintf("\n");
431
432 buf += 16;
433 len -= 16;
434 }
435}
436
437/******************************************************************************
438 * Return length of zero-terminated string
439 */
440size_t strlen(const char _far *s)
441{
442 int len = 0;
443
444 while (*(s++) != '\0') {
445 len++;
446 }
447 return(len);
448}
449
450/******************************************************************************
451 * Copy zero-terminated string
452 */
453char _far *strcpy(char _far *dst, const char _far *src)
454{
455 char _far *orig = dst;
456
457 while ((*(dst++) = *(src++)) != '\0');
458 return(orig);
459}
460
461/******************************************************************************
462 * Compare blocks of memory
463 */
464int memcmp(void _far *p1, void _far *p2, size_t len)
465{
466 register char _far *s1 = p1;
467 register char _far *s2 = p2;
468 int n = 0;
469
470 while (len > 0) {
471 if ((n = *(s1++) - *(s2++)) != 0) {
472 return(n);
473 }
474 len--;
475 }
476 return(0);
477}
478
479/******************************************************************************
480 * Copy block from S/G list to virtual address or vice versa.
481 */
482void sg_memcpy(SCATGATENTRY _far *sg_list, USHORT sg_cnt, ULONG sg_off,
483 void _far *buf, USHORT len, SG_MEMCPY_DIRECTION dir)
484{
485 USHORT mode_flag;
486 USHORT i;
487 USHORT l;
488 ULONG phys_addr;
489 ULONG pos = 0;
490 char _far *p;
491
492 /* walk through S/G list to find the elements involved in the operation */
493 for (i = 0; i < sg_cnt && len > 0; i++) {
494 if (pos <= sg_off && pos + sg_list[i].XferBufLen > sg_off) {
495
496 /* this S/G element intersects with the block to be copied */
497 phys_addr = sg_list[i].ppXferBuf + (sg_off - pos);
498 if ((l = sg_list[i].XferBufLen - (sg_off - pos)) > len) {
499 l = len;
500 }
501
502 if (DevHelp_PhysToVirt(phys_addr, l, (PVOID) &p, &mode_flag)) {
503 panic("sg_memcpy(): DevHelp_PhysToVirt() failed");
504 }
505 if (dir == SG_TO_BUF) {
506 memcpy(buf, p, l);
507 } else {
508 memcpy(p, buf, l);
509 }
510 sg_off += l;
511 buf = (char _far *) buf + l;
512 len -= l;
513 }
514
515 pos += sg_list[i].XferBufLen;
516 }
517}
518
519/******************************************************************************
520 * Convert a string to a long value using the specified base
521 */
522long strtol(const char _far *buf, const char _far * _far *ep, int base)
523{
524 register const char _far *s = buf;
525 long val = 0;
526 int negative = 0;
527
528 /* skip leading whitespace */
529 while (*s == ' ' || *s == '\t') {
530 s++;
531 }
532
533 /* positive or negative */
534 if (*s == '-') {
535 negative = 1;
536 s++;
537 } else if (*s == '+') {
538 s++;
539 }
540
541 /* convert string to long integer */
542 for (;; s++) {
543 int digit = (*s <= '9') ? (*s - '0') : (tolower(*s) - 'a' + 10);
544 if (digit < 0 || digit >= base) {
545 break;
546 }
547 val *= base;
548 val += digit;
549 }
550
551 if (ep != NULL) {
552 *ep = s;
553 }
554 if (negative) {
555 val = -val;
556 }
557 return(val);
558}
559
560/******************************************************************************
561 * Extremely simple and stupid implementation of malloc(). The heap is very
562 * small, only 8K at the moment, and the memory blocks are managed using a
563 * simple array of "number of heap units allocated", zero meaning this unit is
564 * available. Each heap unit is currently 128 bytes.
565 *
566 * Dynamic memory is primarily used for things like ATA identify, ATAPI
567 * sense buffers, etc. and should be freed as soon as possible, otherwise
568 * we'll quickly run out of memory.
569 */
570void *malloc(size_t len)
571{
572 u16 units = (len + HEAP_UNIT - 1) / HEAP_UNIT;
573 u16 i;
574 u16 n;
575
576 spin_lock(mem_lock);
577
578 /* find a sequence of free heap units big enough for the requested length */
579 for (i = 0; i < HEAP_UNIT_CNT; i++) {
580 if (heap_units[i] == 0) {
581 for (n = i; n < i + units && n < HEAP_UNIT_CNT; n++) {
582 if (heap_units[n] != 0) {
583 break;
584 }
585 }
586 if (n == i + units) {
587 /* found a chunk large enough; update 'heap_units[]' */
588 for (; i < n; i++) {
589 heap_units[i] = (u8) (n - i);
590 }
591 spin_unlock(mem_lock);
592 return(heap_buf + (n - units) * HEAP_UNIT);
593 }
594
595 /* keep searching... */
596 i = n;
597 } else {
598 /* skip occupied heap units */
599 i += heap_units[i] - 1;
600 }
601 }
602
603 /* out of memory */
604 spin_unlock(mem_lock);
605 dprintf("malloc(%d): out of memory\n", len);
606 return(NULL);
607}
608
609/******************************************************************************
610 * Free block of memory allocted by malloc().
611 *
612 * NOTE: This function is not reentrant, thus must be called with the driver-
613 * level spinlock held. The main reason for this design is that most
614 * functions that need dynamic memory are already holding the spinlock.
615 */
616void free(void *ptr)
617{
618 u8 *p = (u8 *) ptr;
619 u16 first_unit;
620 u16 units;
621 u16 i;
622
623 if (p < heap_buf || p >= heap_buf + sizeof(heap_buf) ||
624 (u16) (p - heap_buf) % HEAP_UNIT != 0) {
625 dprintf("free(0x%p): invalid pointer (heap_buf = 0x%p)\n",
626 (u16) p, (u16) heap_buf);
627 return;
628 }
629
630 /* clear unit allocation counters in heap_units[] */
631 spin_lock(mem_lock);
632
633 first_unit = (u16) (p - heap_buf) / HEAP_UNIT;
634 units = heap_units[first_unit];
635 for (i = first_unit; i < first_unit + units; i++) {
636 heap_units[i] = 0;
637 }
638
639 spin_unlock(mem_lock);
640}
641
642/******************************************************************************
643 * Return the physical address of a pointer inside the heap buffer. This is
644 * necessary because DevHelp_VirtToPhys() can't be called at interrupt time
645 * and we need physical addresses for heap objects when requeueing unaligned
646 * IORBs inside ahci_intr -> trigger_engine.
647 *
648 * If the pointer is not a heap pointer, this function falls back to calling
649 * DevHelp_VirtToPhys with all consequences (i.e. a trap when this is done
650 * at interrupt time).
651 */
652ULONG virt_to_phys(void _far *ptr)
653{
654 if (ptr < heap_buf || ptr > heap_buf + sizeof(heap_buf)) {
655 ULONG addr;
656
657 if (DevHelp_VirtToPhys(ptr, &addr) != 0) {
658 panic("virt_to_phys(): invalid pointer or execution mode");
659 }
660 return(addr);
661 }
662
663 return(heap_phys_addr + ((char _far *) ptr - (char _far *) heap_buf));
664}
665
666/******************************************************************************
667 * Setup the millisecond timer. This is implemented by blocking (yielding the
668 * CPU) until the system timer value indicates we're done. This function can
669 * only be called at task time, or from a context hook.
670 *
671 * NOTE: The accuracy is limited by the OS/2 timer interrupt frequency which
672 * can lead to intervals up to 55ms (18.2 timer interrupts per second).
673 */
674void timer_init(TIMER far *pTimer, u32 Milliseconds)
675{
676 pTimer->Start = gis->msecs;
677 pTimer->End = pTimer->Start + Milliseconds;
678}
679
680/******************************************************************************
681 * Check the millisecond timer. Block if not done.
682 */
683int timer_check_and_block(TIMER far *pTimer)
684{
685 u32 current;
686
687 current = gis->msecs;
688 if (pTimer->Start <= pTimer->End) {
689 if ((current >= pTimer->End) || (current < pTimer->Start)) return 1;
690 } else {
691 if ((current >= pTimer->End) && (current < pTimer->Start)) return 1;
692 }
693 DevHelp_ProcBlock((ULONG)&timer_check_and_block, 1, WAIT_IS_INTERRUPTABLE);
694 return 0;
695}
696
697/******************************************************************************
698 * Sleep specified number of milliseonds.
699 */
700void msleep(u32 millies)
701{
702 TIMER Timer;
703
704 if (millies == 0) return;
705 timer_init(&Timer, millies);
706 while (!timer_check_and_block(&Timer));
707}
708
709/******************************************************************************
710 * Halt processing by submitting an internal error. This is a last resort and
711 * should only be called when the system state is corrupt.
712 */
713void panic(char *msg)
714{
715 DevHelp_InternalError(msg, strlen(msg));
716}
717
718/******************************************************************************
719 * Disable interrupts. The reason for using a separate function for this is
720 * that the presence of _asm statements will disable compiler optimizations.
721 * In order to support nested calls, this function will return 0 if the
722 * interrupts were already disabled or != 0, if not.
723 *
724 * NOTE: SMP systems should use spinlocks.
725 */
726int disable(void)
727{
728 int rc = 0;
729
730 _asm {
731 pushf
732 pop ax
733 and ax, 0x0200; /* "interrupts enabled" bit */
734 mov rc, ax;
735 cli
736 }
737
738 return(rc);
739}
740
741/******************************************************************************
742 * Enable interrupts. The reason for using a separate function for this is
743 * that the presence of _asm statements will disable compiler optimizations.
744 *
745 * NOTE: SMP systems should use spinlocks.
746 */
747void enable(void)
748{
749 _asm sti;
750}
751
752/******************************************************************************
753 * Convert 'long' to ASCII with the specified base
754 */
755static void long_to_asc(long val, char _far *buf, int base, int zero, int flen)
756{
757 register unsigned long abs_val;
758 char tmp[80];
759 char _far *ptmp = tmp;
760 char _far *s;
761
762 if (base > 16) {
763 sprintf(buf, "[EVAL]");
764 return;
765 }
766
767 abs_val = (unsigned long) ((val < 0 && base <= 10) ? -val : val);
768 tmp[sizeof(tmp) - 1] = '\0';
769
770 for (s = ptmp + sizeof(tmp) - 2; s > ptmp; s--) {
771 *s = hex_digits[abs_val % base];
772 flen--;
773 if ((abs_val /= base) == 0) {
774 break;
775 }
776 }
777
778 /* left-pad the resulting number with zeros or spaces up to 'flen' */
779 while (flen > 0) {
780 *(--s) = (zero) ? '0' : ' ';
781 flen--;
782 }
783
784 /* prepend minus sign if val was negative and base is decimal or less */
785 if (val < 0 && base <= 0) {
786 *(--s) = '-';
787 flen--;
788 }
789
790 strcpy(buf, s);
791}
792
Note: See TracBrowser for help on using the repository browser.