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

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

Added a check for bad geometries reported by the BIOS and fix them.

File size: 20.9 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) : va_arg(va, int), buf, 10, zero, flen);
218 buf += strlen(buf);
219 break;
220
221 case 'x':
222 long_to_asc((lmod) ? va_arg(va, u32) : va_arg(va, u16), buf, 16, zero, flen);
223 buf += strlen(buf);
224 break;
225
226 case 'p':
227 if (fptr || lmod) {
228 u16 off = va_arg(va, u16);
229 u16 seg = va_arg(va, u16);
230 long_to_asc(seg, buf, 16, 1, 4);
231 buf += strlen(buf);
232 *(buf++) = ':';
233 long_to_asc(off, buf, 16, 1, 4);
234 buf += strlen(buf);
235 } else {
236 long_to_asc(va_arg(va, u16), buf, 16, 1, 4);
237 buf += strlen(buf);
238 }
239 break;
240
241 default:
242 *(buf++) = *fmt;
243 break;
244 }
245 break;
246
247 case '\n':
248 *(buf++) = '\r';
249 *(buf++) = '\n';
250 break;
251
252 default:
253 *(buf++) = *fmt;
254 break;
255
256 }
257 }
258
259 *buf = '\0';
260 return((int) (buf - orig));
261}
262
263/*******************************************************************************
264 * Print a formatted message into a string buffer. Relies on vsprintf()
265 */
266int sprintf(char _far *buf, const char *fmt, ...)
267{
268 va_list va;
269
270 va_start(va, fmt);
271 return(vsprintf(buf, fmt, va));
272}
273
274/******************************************************************************
275 * Print messages to serial port
276 *
277 * NOTES: This function uses a 1K buffer for the resulting message. Thus,
278 * messages should never exceed 1024 bytes.
279 */
280void vprintf(int ts, const char *fmt, va_list va)
281{
282 static char buf[1024];
283 char *s;
284 int len = 0;
285
286 spin_lock(com_lock);
287
288 if (ts) {
289 /* include timestamp */
290 if (gis) {
291 len = sprintf(buf, "[%ld] ", gis->msecs);
292 } else {
293 len = sprintf(buf, "[gis=0] ");
294 }
295 }
296
297 len += vsprintf(buf + len, fmt, va);
298
299 if (com_base == 0) {
300 /* write debug message to trace buffer, not COM port */
301 trace_write(buf, len);
302 spin_unlock(com_lock);
303 return;
304 }
305
306 /* write debug message to serial port */
307 for (s = buf; *s != '\0'; s++) {
308
309 /* inp() and outp() are redefined by the DDK in an incompatible
310 * way (only words). Instead of messing around with those
311 * definitions, it's safer and easier to put the whole thing
312 * into an _asm block.
313 *
314 * The C equivalent would look like this:
315 *
316 * while (!(inp(com_base + 5) & 0x20));
317 * outp(com_base, *s);
318 */
319
320 _asm {
321 /* wait until COM transmitter is idle */
322 mov dx, com_base;
323 add dx, 5;
324 transmitter_not_idle:
325 in al, dx;
326 and al, 0x20;
327 jz transmitter_not_idle;
328
329 /* output character to be sent */
330 mov dx, com_base;
331 mov bx, s;
332 mov al, [bx];
333 out dx, al;
334 };
335 }
336
337 spin_unlock(com_lock);
338}
339
340/******************************************************************************
341 * Print messages to COM port
342 */
343void printf(const char *fmt, ...)
344{
345 va_list va;
346
347 va_start(va, fmt);
348 vprintf(1, fmt, va);
349}
350
351/******************************************************************************
352 * Print messages to COM port with no time stamp
353 */
354void printf_nts(const char *fmt, ...)
355{
356 va_list va;
357
358 va_start(va, fmt);
359 vprintf(0, fmt, va);
360}
361
362
363/******************************************************************************
364 * Print a message to the system console. This works only during device driver
365 * initialization.
366 *
367 * NOTE: This function uses a 1K buffer for the resulting message. Thus,
368 * messages should never exceed 1024 bytes...
369 */
370void cprintf(const char *fmt, ...)
371{
372 static char buf[1024];
373 va_list va;
374 size_t len;
375
376 va_start(va, fmt);
377 vsprintf(buf, fmt, va);
378
379 if (debug) {
380 /* print the same message to COM1/trace as well */
381 aprintf("%s", buf);
382 }
383
384 /* remove trailing CR/LF (DevHelp_Save_Message() will add it again) */
385 if ((len = strlen(buf)) >= 2 && buf[len-1] == '\n' && buf[len-2] == '\r') {
386 buf[len-2] = '\0';
387 }
388
389 init_msgtbl.MsgStrings[0] = buf;
390 DevHelp_Save_Message((NPBYTE) &init_msgtbl);
391}
392
393/******************************************************************************
394 * Print hex buffer to COM port.
395 */
396void phex(const void _far *p, int len, const char *fmt, ...)
397{
398 va_list va;
399 const unsigned char _far *buf = p;
400 int i;
401
402 if (!debug) {
403 return;
404 }
405
406 /* print header */
407 va_start(va, fmt);
408 vprintf(1, fmt, va);
409
410 /* print hex block */
411 while (len > 0) {
412 ntprintf("%Fp ", buf);
413
414 /* print hex block */
415 for (i = 0; i < 16; i++) {
416 if (i < len) {
417 ntprintf("%c%02x", ((i == 8) ? '-' : ' '), buf[i]);
418 } else {
419 ntprintf(" ");
420 }
421 }
422
423 /* print ASCII block */
424 ntprintf(" ");
425 for (i = 0; i < ((len > 16) ? 16 : len); i++) {
426 ntprintf("%c", (buf[i] >= 32 && buf[i] < 128) ? buf[i] : '.');
427 }
428 ntprintf("\n");
429
430 buf += 16;
431 len -= 16;
432 }
433}
434
435/******************************************************************************
436 * Return length of zero-terminated string
437 */
438size_t strlen(const char _far *s)
439{
440 int len = 0;
441
442 while (*(s++) != '\0') {
443 len++;
444 }
445 return(len);
446}
447
448/******************************************************************************
449 * Copy zero-terminated string
450 */
451char _far *strcpy(char _far *dst, const char _far *src)
452{
453 char _far *orig = dst;
454
455 while ((*(dst++) = *(src++)) != '\0');
456 return(orig);
457}
458
459/******************************************************************************
460 * Compare blocks of memory
461 */
462int memcmp(void _far *p1, void _far *p2, size_t len)
463{
464 register char _far *s1 = p1;
465 register char _far *s2 = p2;
466 int n = 0;
467
468 while (len > 0) {
469 if ((n = *(s1++) - *(s2++)) != 0) {
470 return(n);
471 }
472 len--;
473 }
474 return(0);
475}
476
477/******************************************************************************
478 * Copy block from S/G list to virtual address or vice versa.
479 */
480void sg_memcpy(SCATGATENTRY _far *sg_list, USHORT sg_cnt, ULONG sg_off,
481 void _far *buf, USHORT len, SG_MEMCPY_DIRECTION dir)
482{
483 USHORT mode_flag;
484 USHORT i;
485 USHORT l;
486 ULONG phys_addr;
487 ULONG pos = 0;
488 char _far *p;
489
490 /* walk through S/G list to find the elements involved in the operation */
491 for (i = 0; i < sg_cnt && len > 0; i++) {
492 if (pos <= sg_off && pos + sg_list[i].XferBufLen > sg_off) {
493
494 /* this S/G element intersects with the block to be copied */
495 phys_addr = sg_list[i].ppXferBuf + (sg_off - pos);
496 if ((l = sg_list[i].XferBufLen - (sg_off - pos)) > len) {
497 l = len;
498 }
499
500 if (DevHelp_PhysToVirt(phys_addr, l, (PVOID) &p, &mode_flag)) {
501 panic("sg_memcpy(): DevHelp_PhysToVirt() failed");
502 }
503 if (dir == SG_TO_BUF) {
504 memcpy(buf, p, l);
505 } else {
506 memcpy(p, buf, l);
507 }
508 sg_off += l;
509 buf = (char _far *) buf + l;
510 len -= l;
511 }
512
513 pos += sg_list[i].XferBufLen;
514 }
515}
516
517/******************************************************************************
518 * Convert a string to a long value using the specified base
519 */
520long strtol(const char _far *buf, const char _far * _far *ep, int base)
521{
522 register const char _far *s = buf;
523 long val = 0;
524 int negative = 0;
525
526 /* skip leading whitespace */
527 while (*s == ' ' || *s == '\t') {
528 s++;
529 }
530
531 /* positive or negative */
532 if (*s == '-') {
533 negative = 1;
534 s++;
535 } else if (*s == '+') {
536 s++;
537 }
538
539 /* convert string to long integer */
540 for (;; s++) {
541 int digit = (*s <= '9') ? (*s - '0') : (tolower(*s) - 'a' + 10);
542 if (digit < 0 || digit >= base) {
543 break;
544 }
545 val *= base;
546 val += digit;
547 }
548
549 if (ep != NULL) {
550 *ep = s;
551 }
552 if (negative) {
553 val = -val;
554 }
555 return(val);
556}
557
558/******************************************************************************
559 * Extremely simple and stupid implementation of malloc(). The heap is very
560 * small, only 8K at the moment, and the memory blocks are managed using a
561 * simple array of "number of heap units allocated", zero meaning this unit is
562 * available. Each heap unit is currently 128 bytes.
563 *
564 * Dynamic memory is primarily used for things like ATA identify, ATAPI
565 * sense buffers, etc. and should be freed as soon as possible, otherwise
566 * we'll quickly run out of memory.
567 */
568void *malloc(size_t len)
569{
570 u16 units = (len + HEAP_UNIT - 1) / HEAP_UNIT;
571 u16 i;
572 u16 n;
573
574 spin_lock(mem_lock);
575
576 /* find a sequence of free heap units big enough for the requested length */
577 for (i = 0; i < HEAP_UNIT_CNT; i++) {
578 if (heap_units[i] == 0) {
579 for (n = i; n < i + units && n < HEAP_UNIT_CNT; n++) {
580 if (heap_units[n] != 0) {
581 break;
582 }
583 }
584 if (n == i + units) {
585 /* found a chunk large enough; update 'heap_units[]' */
586 for (; i < n; i++) {
587 heap_units[i] = (u8) (n - i);
588 }
589 spin_unlock(mem_lock);
590 return(heap_buf + (n - units) * HEAP_UNIT);
591 }
592
593 /* keep searching... */
594 i = n;
595 } else {
596 /* skip occupied heap units */
597 i += heap_units[i] - 1;
598 }
599 }
600
601 /* out of memory */
602 spin_unlock(mem_lock);
603 dprintf("malloc(%d): out of memory\n", len);
604 return(NULL);
605}
606
607/******************************************************************************
608 * Free block of memory allocted by malloc().
609 *
610 * NOTE: This function is not reentrant, thus must be called with the driver-
611 * level spinlock held. The main reason for this design is that most
612 * functions that need dynamic memory are already holding the spinlock.
613 */
614void free(void *ptr)
615{
616 u8 *p = (u8 *) ptr;
617 u16 first_unit;
618 u16 units;
619 u16 i;
620
621 if (p < heap_buf || p >= heap_buf + sizeof(heap_buf) ||
622 (u16) (p - heap_buf) % HEAP_UNIT != 0) {
623 dprintf("free(0x%p): invalid pointer (heap_buf = 0x%p)\n",
624 (u16) p, (u16) heap_buf);
625 return;
626 }
627
628 /* clear unit allocation counters in heap_units[] */
629 spin_lock(mem_lock);
630
631 first_unit = (u16) (p - heap_buf) / HEAP_UNIT;
632 units = heap_units[first_unit];
633 for (i = first_unit; i < first_unit + units; i++) {
634 heap_units[i] = 0;
635 }
636
637 spin_unlock(mem_lock);
638}
639
640/******************************************************************************
641 * Return the physical address of a pointer inside the heap buffer. This is
642 * necessary because DevHelp_VirtToPhys() can't be called at interrupt time
643 * and we need physical addresses for heap objects when requeueing unaligned
644 * IORBs inside ahci_intr -> trigger_engine.
645 *
646 * If the pointer is not a heap pointer, this function falls back to calling
647 * DevHelp_VirtToPhys with all consequences (i.e. a trap when this is done
648 * at interrupt time).
649 */
650ULONG virt_to_phys(void _far *ptr)
651{
652 if (ptr < heap_buf || ptr > heap_buf + sizeof(heap_buf)) {
653 ULONG addr;
654
655 if (DevHelp_VirtToPhys(ptr, &addr) != 0) {
656 panic("virt_to_phys(): invalid pointer or execution mode");
657 }
658 return(addr);
659 }
660
661 return(heap_phys_addr + ((char _far *) ptr - (char _far *) heap_buf));
662}
663
664/******************************************************************************
665 * Setup the millisecond timer. This is implemented by blocking (yielding the
666 * CPU) until the system timer value indicates we're done. This function can
667 * only be called at task time, or from a context hook.
668 *
669 * NOTE: The accuracy is limited by the OS/2 timer interrupt frequency which
670 * can lead to intervals up to 55ms (18.2 timer interrupts per second).
671 */
672void timer_init(TIMER far *pTimer, u32 Milliseconds)
673{
674 pTimer->Start = gis->msecs;
675 pTimer->End = pTimer->Start + Milliseconds;
676}
677
678/******************************************************************************
679 * Check the millisecond timer. Block if not done.
680 */
681int timer_check_and_block(TIMER far *pTimer)
682{
683 u32 current;
684
685 current = gis->msecs;
686 if (pTimer->Start <= pTimer->End) {
687 if ((current >= pTimer->End) || (current < pTimer->Start)) return 1;
688 } else {
689 if ((current >= pTimer->End) && (current < pTimer->Start)) return 1;
690 }
691 DevHelp_ProcBlock((ULONG)&timer_check_and_block, 1, WAIT_IS_INTERRUPTABLE);
692 return 0;
693}
694
695/******************************************************************************
696 * Sleep specified number of milliseonds.
697 */
698void msleep(u32 millies)
699{
700 TIMER Timer;
701
702 if (millies == 0) return;
703 timer_init(&Timer, millies);
704 while (!timer_check_and_block(&Timer));
705}
706
707/******************************************************************************
708 * Halt processing by submitting an internal error. This is a last resort and
709 * should only be called when the system state is corrupt.
710 */
711void panic(char *msg)
712{
713 DevHelp_InternalError(msg, strlen(msg));
714}
715
716/******************************************************************************
717 * Disable interrupts. The reason for using a separate function for this is
718 * that the presence of _asm statements will disable compiler optimizations.
719 * In order to support nested calls, this function will return 0 if the
720 * interrupts were already disabled or != 0, if not.
721 *
722 * NOTE: SMP systems should use spinlocks.
723 */
724int disable(void)
725{
726 int rc = 0;
727
728 _asm {
729 pushf
730 pop ax
731 and ax, 0x0200; /* "interrupts enabled" bit */
732 mov rc, ax;
733 cli
734 }
735
736 return(rc);
737}
738
739/******************************************************************************
740 * Enable interrupts. The reason for using a separate function for this is
741 * that the presence of _asm statements will disable compiler optimizations.
742 *
743 * NOTE: SMP systems should use spinlocks.
744 */
745void enable(void)
746{
747 _asm sti;
748}
749
750/******************************************************************************
751 * Convert 'long' to ASCII with the specified base
752 */
753static void long_to_asc(long val, char _far *buf, int base, int zero, int flen)
754{
755 register unsigned long abs_val;
756 char tmp[80];
757 char _far *ptmp = tmp;
758 char _far *s;
759
760 if (base > 16) {
761 sprintf(buf, "[EVAL]");
762 return;
763 }
764
765 abs_val = (unsigned long) ((val < 0 && base <= 10) ? -val : val);
766 tmp[sizeof(tmp) - 1] = '\0';
767
768 for (s = ptmp + sizeof(tmp) - 2; s > ptmp; s--) {
769 *s = hex_digits[abs_val % base];
770 flen--;
771 if ((abs_val /= base) == 0) {
772 break;
773 }
774 }
775
776 /* left-pad the resulting number with zeros or spaces up to 'flen' */
777 while (flen > 0) {
778 *(--s) = (zero) ? '0' : ' ';
779 flen--;
780 }
781
782 /* prepend minus sign if val was negative and base is decimal or less */
783 if (val < 0 && base <= 0) {
784 *(--s) = '-';
785 flen--;
786 }
787
788 strcpy(buf, s);
789}
790
Note: See TracBrowser for help on using the repository browser.