disable install lib libwfb
[nvidia.git] / src / nvidia_os.c
1 /* _NVRM_COPYRIGHT_BEGIN_
2  *
3  * Copyright 2001-2002 by NVIDIA Corporation.  All rights reserved.  All
4  * information contained herein is proprietary and confidential to NVIDIA
5  * Corporation.  Any use, reproduction, or disclosure without the written
6  * permission of NVIDIA Corporation is prohibited.
7  *
8  * _NVRM_COPYRIGHT_END_
9  */
10
11 #include "nv-misc.h"
12 #include "os-interface.h"
13 #include "nv.h"
14 #include "nv-freebsd.h"
15
16
17 /* DragonFly compat */
18 #include <machine/pmap_inval.h>
19 #if __DragonFly_version >= 200204
20 #include <sys/priv.h>
21 #endif
22
23 void
24 pmap_invalidate_range(struct pmap *pmap, vm_offset_t sva, vm_offset_t eva)
25 {
26     struct pmap_inval_info info;
27     vm_offset_t va;
28
29     pmap_inval_init(&info);
30     for (va = sva; va < eva; va += PAGE_SIZE)
31         pmap_inval_add(&info, pmap, va);
32     pmap_inval_flush(&info);
33 }
34
35
36 /*
37  * The NVIDIA kernel module's malloc identifier, needed for both tracking
38  * and actual allocation/freeing purposes. M_NVIDIA is declared elsewhere
39  * to make it known to other parts of the kernel module (nv-freebsd.h).
40  */
41
42 MALLOC_DEFINE(M_NVIDIA, "nvidia", "NVIDIA memory allocations");
43
44
45 #define MAX_ERROR_STRING    256
46
47 RM_STATUS NV_API_CALL os_alloc_contig_pages(
48     void **address,
49     U032 size
50 )
51 {
52     *address = contigmalloc(size, M_NVIDIA, 0, 0, ~0, PAGE_SIZE, 0);
53     return *address ? RM_OK : RM_ERROR;
54 }
55
56 void NV_API_CALL os_free_contig_pages(
57     void *address,
58     U032 size
59 )
60 {
61     contigfree(address, size, M_NVIDIA);
62 }
63
64 /*
65  * The core resource manager's favorite source of memory, this routine is
66  * called from different contexts, including ISRs. This means that it can
67  * not be allowed to sleep when memory is low.
68  */
69
70 RM_STATUS NV_API_CALL os_alloc_mem(
71     void **address,
72     U032 size
73 )
74 {
75     /* XXX Fix me? (malloc flags) */
76     *address = malloc(size, M_NVIDIA, M_INTWAIT | M_ZERO | M_NULLOK);
77     return *address ? RM_OK : RM_ERROR;
78 }
79
80 void NV_API_CALL os_free_mem(void *address)
81 {
82     free(address, M_NVIDIA);
83 }
84
85 #define NV_MSECS_PER_TICK       (1000 / hz)
86 #define NV_MSECS_TO_TICKS(ms)   ((ms) * hz / 1000)
87 #define NV_USECS_PER_TICK       (1000000 / hz)
88 #define NV_USECS_TO_TICKS(us)   ((us) * hz / 1000000)
89
90 RM_STATUS NV_API_CALL os_delay(U032 MilliSeconds)
91 {
92     unsigned long MicroSeconds;
93     unsigned long ticks;
94     struct timeval tv_end, tv_aux;
95
96     getmicrotime(&tv_aux);
97
98     if (__NV_ITHREAD() && (MilliSeconds > NV_MAX_ISR_DELAY_MS))
99         return RM_ERROR;
100
101     if (__NV_ITHREAD()) {
102         DELAY(MilliSeconds * 1000);
103         return RM_OK;
104     }
105
106     MicroSeconds = MilliSeconds * 1000;
107     tv_end.tv_usec = MicroSeconds;
108     tv_end.tv_sec = 0;
109     /* tv_end = tv_aux + tv_end */
110     NV_TIMERADD(&tv_aux, &tv_end, &tv_end);
111
112     ticks = NV_USECS_TO_TICKS(MicroSeconds);
113
114     if (ticks > 0) {
115         do {
116             tsleep((void *)os_delay, PCATCH, "delay", ticks);
117             getmicrotime(&tv_aux);
118             if (NV_TIMERCMP(&tv_aux, &tv_end, <)) {
119                 /* tv_aux = tv_end - tv_aux */
120                 NV_TIMERSUB(&tv_end, &tv_aux, &tv_aux);
121                 MicroSeconds = tv_aux.tv_usec + (tv_aux.tv_sec * 1000000);
122             } else
123                 MicroSeconds = 0;
124         } while ((ticks = NV_USECS_TO_TICKS(MicroSeconds)) > 0);
125     }
126
127     if (MicroSeconds > 0)
128         DELAY(MicroSeconds);
129
130     return RM_OK;
131 }
132
133 RM_STATUS NV_API_CALL os_delay_us(U032 MicroSeconds)
134 {
135     if (__NV_ITHREAD() && (MicroSeconds > NV_MAX_ISR_DELAY_US))
136         return RM_ERROR;
137     DELAY(MicroSeconds);
138     return RM_OK;
139 }
140
141 U032 NV_API_CALL os_get_cpu_frequency(void)
142 {
143     return ((tsc_frequency + 4999) / 1000000);
144 }
145
146 U032 NV_API_CALL os_get_current_process(void)
147 {
148     return curproc->p_pid;
149 }
150
151 RM_STATUS NV_API_CALL os_get_current_time(
152     U032 *sec,
153     U032 *usec
154 )
155 {
156     struct timeval tv;
157
158     getmicrotime(&tv);
159
160     *sec  = tv.tv_sec;
161     *usec = tv.tv_usec;
162
163     return RM_OK;
164 }
165
166 BOOL NV_API_CALL os_is_administrator(PHWINFO pDev)
167 {
168 #if __DragonFly_version >= 200204
169     return priv_check(CURTHREAD, PRIV_ROOT) ? FALSE : TRUE;
170 #else
171     return suser(CURTHREAD) ? FALSE : TRUE;
172 #endif
173 }
174
175 U008 NV_API_CALL os_io_read_byte(
176     PHWINFO pDev,
177     U032 address
178 )
179 {
180     /* XXX Fix me? (bus_space access) */
181     return inb(address);
182 }
183
184 void NV_API_CALL os_io_write_byte(
185     PHWINFO pDev,
186     U032 address,
187     U008 value
188 )
189 {
190     /* XXX Fix me? (bus_space access) */
191     outb(address, value);
192 }
193
194 U016 NV_API_CALL os_io_read_word(
195     PHWINFO pDev,
196     U032 address
197 )
198 {
199     /* XXX Fix me? (bus_space access) */
200     return inw(address);
201 }
202
203 void NV_API_CALL os_io_write_word(
204     PHWINFO pDev,
205     U032 address,
206     U016 value
207 )
208 {
209     /* XXX Fix me? (bus_space access) */
210     return outw(address, value);
211 }
212
213 U032 NV_API_CALL os_io_read_dword(
214     PHWINFO pDev,
215     U032 address
216 )
217 {
218     /* XXX Fix me? (bus_space access) */
219     return inl(address);
220 }
221
222 void NV_API_CALL os_io_write_dword(
223     PHWINFO pDev,
224     U032 address,
225     U032 value
226 )
227 {
228     /* XXX Fix me? (bus_space access) */
229     outl(address, value);
230 }
231
232 void* NV_API_CALL os_map_kernel_space(
233     NvU64 start,
234     NvU64 size,
235     U032 mode
236 )
237 {
238     int map_mode;
239
240 #if defined(NVCPU_X86) && !defined(PAE)
241     if (start > 0xffffffff)
242         return NULL;
243 #endif
244
245     if (start & PAGE_MASK)
246         return NULL;
247
248     size = NV_ALIGN_UP(size, PAGE_SIZE);
249
250     switch (mode) {
251         case NV_MEMORY_CACHED:
252             map_mode = PAT_WRITE_BACK;
253             break;
254         case NV_MEMORY_WRITECOMBINED:
255             map_mode = PAT_WRITE_COMBINING;
256             break;
257         case NV_MEMORY_UNCACHED:
258         case NV_MEMORY_DEFAULT:
259             map_mode = PAT_UNCACHEABLE;
260             break;
261         default:
262             nv_printf(NV_DBG_ERRORS,
263                       "NVRM: unknown mode in os_map_kernel_space()\n");
264             return NULL;
265     }
266
267     return pmap_mapdev_attr(start, size, map_mode);
268 }
269
270 void NV_API_CALL os_unmap_kernel_space(
271     void *address,
272     NvU64 size
273 )
274 {
275     pmap_unmapdev_attr((vm_offset_t)address, size);
276 }
277
278 RM_STATUS NV_API_CALL os_set_mem_range(
279     NvU64 start,
280     NvU64 size,
281     U032 mode
282 )
283 {
284     int arg;
285     struct mem_range_desc mrd;
286
287     mrd.mr_base  = start;
288     mrd.mr_len   = size;
289     mrd.mr_flags = MDF_WRITECOMBINE;
290
291     strcpy(mrd.mr_owner, "NVIDIA");
292     arg = MEMRANGE_SET_UPDATE;
293
294     if (mem_range_attr_set(&mrd, &arg))
295         return RM_ERROR;
296
297     return RM_OK;
298 }
299
300 RM_STATUS NV_API_CALL os_unset_mem_range(
301     NvU64 start,
302     NvU64 size
303 )
304 {
305     int arg;
306     struct mem_range_desc mrd;
307
308     mrd.mr_base = start;
309     mrd.mr_len  = size;
310     arg = MEMRANGE_SET_REMOVE;
311
312     if (mem_range_attr_set(&mrd, &arg))
313         return RM_ERROR;
314
315     return RM_OK;
316 }
317
318
319 /*
320  * The current debug level is used to determine if certain debug messages
321  * are printed to the system console/log files or not. It defaults to the
322  * highest debug level, i.e. the lowest debug output.
323  */
324
325 U032 cur_debuglevel = 0xffffffff;
326
327 void NV_API_CALL os_dbg_init(void)
328 {
329     U032 new_debuglevel;
330     nv_stack_t *sp;
331
332     NV_UMA_ZONE_ALLOC_STACK(sp);
333     if (sp == NULL)
334         return;
335
336     if (rm_read_registry_dword(sp, NULL, "NVreg", "ResmanDebugLevel",
337             &new_debuglevel) == RM_OK) {
338         if (new_debuglevel != 0xffffffff)
339             cur_debuglevel = new_debuglevel;
340     }
341
342     NV_UMA_ZONE_FREE_STACK(sp);
343 }
344
345 RM_STATUS NV_API_CALL os_schedule(void)
346 {
347     return RM_ERR_NOT_SUPPORTED;
348 }
349
350 void NV_API_CALL os_dbg_set_level(U032 new_debuglevel)
351 {
352     cur_debuglevel = new_debuglevel;
353 }
354
355 void NV_API_CALL os_dbg_breakpoint(void)
356 {
357 #ifdef DEBUG
358     Debugger("nv");
359 #endif
360 }
361
362 void NV_API_CALL out_string(const char *message)
363 {
364 #if defined(DEBUG) || defined(QA_BUILD)
365     kprintf("%s", message);
366 #endif
367 }
368
369 static char nv_error_string[MAX_ERROR_STRING];
370
371 int NV_API_CALL nv_printf(
372     U032 debuglevel,
373     const char *format,
374     ...
375 )
376 {
377     char *message = nv_error_string;
378     __va_list arglist;
379     int chars_written = 0;
380
381     if (debuglevel >= ((cur_debuglevel >> 4) & 3)) {
382         __va_start(arglist, format);
383         chars_written = kvsprintf(message, format, arglist);
384         __va_end(arglist);
385         kprintf("%s", message);
386     }
387
388     return chars_written;
389 }
390
391 int NV_API_CALL nv_snprintf(
392     char *buf,
393     unsigned int size,
394     const char *fmt,
395     ...
396 )
397 {
398     __va_list arglist;
399     int chars_written;
400
401     __va_start(arglist, fmt);
402     chars_written = kvsnprintf(buf, size, fmt, arglist);
403     __va_end(arglist);
404
405     return chars_written;
406 }
407
408 void NV_API_CALL nv_os_log(
409     int loglevel,
410     const char *fmt,
411     void *ap
412 )
413 {
414     int l;
415     ksprintf(nv_error_string, "NVRM: ");
416     l = strlen(nv_error_string);
417     kvsnprintf(nv_error_string + l, MAX_ERROR_STRING - l, fmt, ap);
418     kprintf("%s", nv_error_string);
419 }
420
421 S032 NV_API_CALL os_mem_cmp(
422     const U008 *buf0,
423     const U008 *buf1,
424     U032 length
425 )
426 {
427     return memcmp(buf0, buf1, length);
428 }
429
430 U008* NV_API_CALL os_mem_copy(
431     U008 *dst,
432     const U008 *src,
433     U032 length
434 )
435 {
436 #if defined(NVCPU_X86_64)
437     uint32_t i;
438     for (i = 0; i < length; i++) dst[i] = src[i];
439     return dst;
440 #else
441     return memcpy(dst, src, length);
442 #endif
443 }
444
445 RM_STATUS NV_API_CALL os_memcpy_from_user(
446     void *dst,
447     const void *src,
448     U032 length
449 )
450 {
451     if (src < (void *) VM_MAX_USER_ADDRESS)
452         return copyin(src, dst, length)  ? RM_ERR_INVALID_POINTER : RM_OK;
453
454     return os_mem_copy(dst, src, length) ? RM_ERR_INVALID_POINTER : RM_OK;
455 }
456
457 RM_STATUS NV_API_CALL os_memcpy_to_user(
458     void *dst,
459     const void *src,
460     U032 length
461 )
462 {
463     if (dst < (void *) VM_MAX_USER_ADDRESS)
464         return copyout(src, dst, length) ? RM_ERR_INVALID_POINTER : RM_OK;
465
466     return os_mem_copy(dst, src, length) ? RM_ERR_INVALID_POINTER : RM_OK;
467 }
468
469 void* NV_API_CALL os_mem_set(
470     void *b,
471     U008 c,
472     U032 length
473 )
474 {
475     return memset(b, c, length);
476 }
477
478 S032 NV_API_CALL os_string_compare(
479     const char *s1,
480     const char *s2
481 )
482 {
483     return strcmp(s1, s2);
484 }
485
486 char* NV_API_CALL os_string_copy(
487     char *dst,
488     const char *src
489 )
490 {
491     return strcpy(dst, src);
492 }
493
494 U032 NV_API_CALL os_string_length(const char* s)
495 {
496     return strlen(s);
497 }
498
499 RM_STATUS NV_API_CALL os_strncpy_from_user(
500     char *dst,
501     const char *src,
502     U032 n
503 )
504 {
505     return copyinstr(src, dst, n, NULL) ? RM_ERR_INVALID_POINTER : RM_OK;
506 }
507
508 U032 NV_API_CALL os_get_page_size(void)
509 {
510     return PAGE_SIZE;
511 }
512
513 NvU64 NV_API_CALL os_get_page_mask(void)
514 {
515     /*
516      * On FreeBSD, PAGE_MASK means (PAGE_SIZE - 1); on Linux it means the
517      * opposite, ~(PAGE_SIZE - 1); that is what this function is expected
518      * to return.
519      */
520     return ~PAGE_MASK;
521 }
522
523 NvU64 NV_API_CALL os_get_system_memory_size(void)
524 {
525     return ((NvU64)physmem * PAGE_SIZE) / RM_PAGE_SIZE;
526 }
527
528 U032 NV_API_CALL os_get_cpu_count(void)
529 {
530     return ncpus;
531 }
532
533 RM_STATUS NV_API_CALL os_flush_cpu_cache(void)
534 {
535     /*
536      * XXX This will do for now, but this may need to be extended
537      * to make IPI calls (flushing all caches).
538      */
539     __asm__ __volatile__("wbinvd": : :"memory");
540     return RM_OK;
541 }
542
543 void NV_API_CALL os_flush_cpu_write_combine_buffer(void)
544 {
545     __asm__ __volatile__("sfence": : :"memory");
546 }
547
548 RM_STATUS NV_API_CALL os_raise_smp_barrier(void)
549 {
550     return RM_ERR_NOT_SUPPORTED;
551 }
552
553 RM_STATUS NV_API_CALL os_clear_smp_barrier(void)
554 {
555     return RM_ERR_NOT_SUPPORTED;
556 }
557
558 struct os_mutex {
559     nv_stack_t *sp;
560     struct spinlock lock;
561     int refcnt;
562 };
563
564 RM_STATUS NV_API_CALL os_alloc_sema(void **semaphore)
565 {
566     nv_stack_t *sp;
567     struct os_mutex *mtx;
568     RM_STATUS status;
569
570     NV_UMA_ZONE_ALLOC_STACK(sp);
571     if (sp == NULL)
572         return RM_ERR_NO_FREE_MEM;
573
574     status = os_alloc_mem((void **)&mtx, sizeof(struct os_mutex));
575     if (status != RM_OK) {
576         NV_UMA_ZONE_FREE_STACK(sp);
577         return status;
578     }
579
580     spin_init(&mtx->lock);
581
582     mtx->sp = sp;
583     mtx->refcnt = 1;
584
585     *semaphore = (void *) mtx;
586
587     return RM_OK;
588 }
589
590 RM_STATUS NV_API_CALL os_free_sema(void *semaphore)
591 {
592     struct os_mutex *mtx = semaphore;
593     nv_stack_t *sp;
594
595     sp = mtx->sp;
596     NV_UMA_ZONE_FREE_STACK(sp);
597
598     spin_uninit(&mtx->lock);
599
600     os_free_mem(semaphore);
601
602     return RM_OK;
603 }
604
605 RM_STATUS NV_API_CALL os_acquire_sema(void *semaphore)
606 {
607     struct os_mutex *mtx = semaphore;
608
609     spin_lock_wr(&mtx->lock);
610     if (mtx->refcnt > 0)
611         rm_disable_interrupts(mtx->sp);
612     mtx->refcnt--;
613     if (mtx->refcnt < 0)
614 #if __DragonFly_version < 200206
615         msleep(mtx, &mtx->lock, 0, "nvsemaq", 0);
616 #else
617         ssleep(mtx, &mtx->lock, 0, "nvsemaq", 0);
618 #endif
619     spin_unlock_wr(&mtx->lock);
620
621     return RM_OK;
622 }
623
624 BOOL NV_API_CALL os_cond_acquire_sema(void *semaphore)
625 {
626     struct os_mutex *mtx = semaphore;
627
628     spin_lock_wr(&mtx->lock);
629     if (mtx->refcnt < 1) {
630         spin_unlock_wr(&mtx->lock);
631         return FALSE;
632     } else {
633         rm_disable_interrupts(mtx->sp);
634         mtx->refcnt--;
635         spin_unlock_wr(&mtx->lock);
636     }
637
638     return TRUE;
639 }
640
641 RM_STATUS NV_API_CALL os_release_sema(void *semaphore)
642 {
643     struct os_mutex *mtx = semaphore;
644
645     spin_lock_wr(&mtx->lock);
646     if (mtx->refcnt < 0)
647         wakeup_one(mtx);
648     else
649         rm_enable_interrupts(mtx->sp);
650     mtx->refcnt++;
651     spin_unlock_wr(&mtx->lock);
652
653     return RM_OK;
654 }
655
656 BOOL NV_API_CALL os_is_acquired_sema(void *semaphore)
657 {
658     struct os_mutex *mtx = semaphore;
659     return (mtx->refcnt < 1);
660 }
661
662 BOOL NV_API_CALL os_pat_supported(void)
663 {
664     /*
665      * FreeBSD has no native PAT support and there's no good
666      * way to implement it privately as we do on Linux.
667      */
668     return FALSE;
669 }
670
671 void NV_API_CALL os_register_compatible_ioctl(
672     U032 cmd,
673     U032 size
674 )
675 {
676 }
677
678 void NV_API_CALL os_unregister_compatible_ioctl(
679     U032 cmd,
680     U032 size
681 )
682 {
683 }
684
685 RM_STATUS NV_API_CALL os_disable_console_access(void)
686 {
687     return RM_OK;
688 }
689
690 RM_STATUS NV_API_CALL os_enable_console_access(void)
691 {
692     return RM_OK;
693 }
694
695 NvU64 NV_API_CALL os_acquire_spinlock(void *pSema)
696 {
697     struct os_mutex *mtx = pSema;
698
699     spin_lock_wr(&mtx->lock);
700
701     return 0;
702 }
703
704 void NV_API_CALL os_release_spinlock(void *pSema, NvU64 oldIrql)
705 {
706     struct os_mutex *mtx = pSema;
707
708     spin_unlock_wr(&mtx->lock);
709 }
710
711