Merge branch 'upstream'
[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_thread(NvU64 *threadId)
152 {
153     if (__NV_ITHREAD())
154         *threadId = 0;
155     else
156         *threadId = (NvU64)(uintptr_t) CURTHREAD;
157
158     return RM_OK;
159 }
160
161 RM_STATUS NV_API_CALL os_get_current_time(
162     U032 *sec,
163     U032 *usec
164 )
165 {
166     struct timeval tv;
167
168     getmicrotime(&tv);
169
170     *sec  = tv.tv_sec;
171     *usec = tv.tv_usec;
172
173     return RM_OK;
174 }
175
176 BOOL NV_API_CALL os_is_administrator(PHWINFO pDev)
177 {
178 #if __DragonFly_version >= 200204
179     return priv_check(CURTHREAD, PRIV_ROOT) ? FALSE : TRUE;
180 #else
181     return suser(CURTHREAD) ? FALSE : TRUE;
182 #endif
183 }
184
185 U008 NV_API_CALL os_io_read_byte(
186     U032 address
187 )
188 {
189     /* XXX Fix me? (bus_space access) */
190     return inb(address);
191 }
192
193 void NV_API_CALL os_io_write_byte(
194     U032 address,
195     U008 value
196 )
197 {
198     /* XXX Fix me? (bus_space access) */
199     outb(address, value);
200 }
201
202 U016 NV_API_CALL os_io_read_word(
203     U032 address
204 )
205 {
206     /* XXX Fix me? (bus_space access) */
207     return inw(address);
208 }
209
210 void NV_API_CALL os_io_write_word(
211     U032 address,
212     U016 value
213 )
214 {
215     /* XXX Fix me? (bus_space access) */
216     return outw(address, value);
217 }
218
219 U032 NV_API_CALL os_io_read_dword(
220     U032 address
221 )
222 {
223     /* XXX Fix me? (bus_space access) */
224     return inl(address);
225 }
226
227 void NV_API_CALL os_io_write_dword(
228     U032 address,
229     U032 value
230 )
231 {
232     /* XXX Fix me? (bus_space access) */
233     outl(address, value);
234 }
235
236 void* NV_API_CALL os_map_kernel_space(
237     NvU64 start,
238     NvU64 size,
239     U032 mode
240 )
241 {
242     int map_mode;
243
244 #if defined(NVCPU_X86) && !defined(PAE)
245     if (start > 0xffffffff)
246         return NULL;
247 #endif
248
249     if (start & PAGE_MASK)
250         return NULL;
251
252     size = NV_ALIGN_UP(size, PAGE_SIZE);
253
254     switch (mode) {
255         case NV_MEMORY_CACHED:
256             map_mode = PAT_WRITE_BACK;
257             break;
258         case NV_MEMORY_WRITECOMBINED:
259             map_mode = PAT_WRITE_COMBINING;
260             break;
261         case NV_MEMORY_UNCACHED:
262         case NV_MEMORY_DEFAULT:
263             map_mode = PAT_UNCACHEABLE;
264             break;
265         default:
266             nv_printf(NV_DBG_ERRORS,
267                       "NVRM: unknown mode in os_map_kernel_space()\n");
268             return NULL;
269     }
270
271     return pmap_mapdev_attr(start, size, map_mode);
272 }
273
274 void NV_API_CALL os_unmap_kernel_space(
275     void *address,
276     NvU64 size
277 )
278 {
279     pmap_unmapdev_attr((vm_offset_t)address, size);
280 }
281
282 RM_STATUS NV_API_CALL os_set_mem_range(
283     NvU64 start,
284     NvU64 size,
285     U032 mode
286 )
287 {
288     int arg;
289     struct mem_range_desc mrd;
290
291     mrd.mr_base  = start;
292     mrd.mr_len   = size;
293     mrd.mr_flags = MDF_WRITECOMBINE;
294
295     strcpy(mrd.mr_owner, "NVIDIA");
296     arg = MEMRANGE_SET_UPDATE;
297
298     if (mem_range_attr_set(&mrd, &arg))
299         return RM_ERROR;
300
301     return RM_OK;
302 }
303
304 RM_STATUS NV_API_CALL os_unset_mem_range(
305     NvU64 start,
306     NvU64 size
307 )
308 {
309     int arg;
310     struct mem_range_desc mrd;
311
312     mrd.mr_base = start;
313     mrd.mr_len  = size;
314     arg = MEMRANGE_SET_REMOVE;
315
316     if (mem_range_attr_set(&mrd, &arg))
317         return RM_ERROR;
318
319     return RM_OK;
320 }
321
322
323 /*
324  * The current debug level is used to determine if certain debug messages
325  * are printed to the system console/log files or not. It defaults to the
326  * highest debug level, i.e. the lowest debug output.
327  */
328
329 U032 cur_debuglevel = 0xffffffff;
330
331 void NV_API_CALL os_dbg_init(void)
332 {
333     U032 new_debuglevel;
334     nv_stack_t *sp;
335
336     NV_UMA_ZONE_ALLOC_STACK(sp);
337     if (sp == NULL)
338         return;
339
340     if (rm_read_registry_dword(sp, NULL, "NVreg", "ResmanDebugLevel",
341             &new_debuglevel) == RM_OK) {
342         if (new_debuglevel != 0xffffffff)
343             cur_debuglevel = new_debuglevel;
344     }
345
346     NV_UMA_ZONE_FREE_STACK(sp);
347 }
348
349 RM_STATUS NV_API_CALL os_schedule(void)
350 {
351     return RM_ERR_NOT_SUPPORTED;
352 }
353
354 void NV_API_CALL os_dbg_set_level(U032 new_debuglevel)
355 {
356     cur_debuglevel = new_debuglevel;
357 }
358
359 void NV_API_CALL os_dbg_breakpoint(void)
360 {
361 #ifdef DEBUG
362     Debugger("nv");
363 #endif
364 }
365
366 void NV_API_CALL out_string(const char *message)
367 {
368 #if defined(DEBUG) || defined(QA_BUILD)
369     kprintf("%s", message);
370 #endif
371 }
372
373 static char nv_error_string[MAX_ERROR_STRING];
374
375 int NV_API_CALL nv_printf(
376     U032 debuglevel,
377     const char *format,
378     ...
379 )
380 {
381     char *message = nv_error_string;
382     __va_list arglist;
383     int chars_written = 0;
384
385     if (debuglevel >= ((cur_debuglevel >> 4) & 3)) {
386         __va_start(arglist, format);
387         chars_written = kvsprintf(message, format, arglist);
388         __va_end(arglist);
389         kprintf("%s", message);
390     }
391
392     return chars_written;
393 }
394
395 int NV_API_CALL nv_snprintf(
396     char *buf,
397     unsigned int size,
398     const char *fmt,
399     ...
400 )
401 {
402     __va_list arglist;
403     int chars_written;
404
405     __va_start(arglist, fmt);
406     chars_written = kvsnprintf(buf, size, fmt, arglist);
407     __va_end(arglist);
408
409     return chars_written;
410 }
411
412 void NV_API_CALL nv_os_log(
413     int loglevel,
414     const char *fmt,
415     void *ap
416 )
417 {
418     int l;
419     ksprintf(nv_error_string, "NVRM: ");
420     l = strlen(nv_error_string);
421     kvsnprintf(nv_error_string + l, MAX_ERROR_STRING - l, fmt, ap);
422     kprintf("%s", nv_error_string);
423 }
424
425 S032 NV_API_CALL os_mem_cmp(
426     const U008 *buf0,
427     const U008 *buf1,
428     U032 length
429 )
430 {
431     return memcmp(buf0, buf1, length);
432 }
433
434 U008* NV_API_CALL os_mem_copy(
435     U008 *dst,
436     const U008 *src,
437     U032 length
438 )
439 {
440 #if defined(NVCPU_X86_64)
441     uint32_t i;
442     for (i = 0; i < length; i++) dst[i] = src[i];
443     return dst;
444 #else
445     return memcpy(dst, src, length);
446 #endif
447 }
448
449 RM_STATUS NV_API_CALL os_memcpy_from_user(
450     void *dst,
451     const void *src,
452     U032 length
453 )
454 {
455     if (src < (void *) VM_MAX_USER_ADDRESS)
456         return copyin(src, dst, length)  ? RM_ERR_INVALID_POINTER : RM_OK;
457
458     return os_mem_copy(dst, src, length) ? RM_ERR_INVALID_POINTER : RM_OK;
459 }
460
461 RM_STATUS NV_API_CALL os_memcpy_to_user(
462     void *dst,
463     const void *src,
464     U032 length
465 )
466 {
467     if (dst < (void *) VM_MAX_USER_ADDRESS)
468         return copyout(src, dst, length) ? RM_ERR_INVALID_POINTER : RM_OK;
469
470     return os_mem_copy(dst, src, length) ? RM_ERR_INVALID_POINTER : RM_OK;
471 }
472
473 void* NV_API_CALL os_mem_set(
474     void *b,
475     U008 c,
476     U032 length
477 )
478 {
479     return memset(b, c, length);
480 }
481
482 S032 NV_API_CALL os_string_compare(
483     const char *s1,
484     const char *s2
485 )
486 {
487     return strcmp(s1, s2);
488 }
489
490 char* NV_API_CALL os_string_copy(
491     char *dst,
492     const char *src
493 )
494 {
495     return strcpy(dst, src);
496 }
497
498 U032 NV_API_CALL os_string_length(const char* s)
499 {
500     return strlen(s);
501 }
502
503 RM_STATUS NV_API_CALL os_strncpy_from_user(
504     char *dst,
505     const char *src,
506     U032 n
507 )
508 {
509     return copyinstr(src, dst, n, NULL) ? RM_ERR_INVALID_POINTER : RM_OK;
510 }
511
512 U032 NV_API_CALL os_get_page_size(void)
513 {
514     return PAGE_SIZE;
515 }
516
517 NvU64 NV_API_CALL os_get_page_mask(void)
518 {
519     /*
520      * On FreeBSD, PAGE_MASK means (PAGE_SIZE - 1); on Linux it means the
521      * opposite, ~(PAGE_SIZE - 1); that is what this function is expected
522      * to return.
523      */
524     return ~PAGE_MASK;
525 }
526
527 NvU64 NV_API_CALL os_get_system_memory_size(void)
528 {
529     return ((NvU64)physmem * PAGE_SIZE) / RM_PAGE_SIZE;
530 }
531
532 U032 NV_API_CALL os_get_cpu_count(void)
533 {
534     return ncpus;
535 }
536
537 RM_STATUS NV_API_CALL os_flush_cpu_cache(void)
538 {
539     /*
540      * XXX This will do for now, but this may need to be extended
541      * to make IPI calls (flushing all caches).
542      */
543     __asm__ __volatile__("wbinvd": : :"memory");
544     return RM_OK;
545 }
546
547 void NV_API_CALL os_flush_cpu_write_combine_buffer(void)
548 {
549     __asm__ __volatile__("sfence": : :"memory");
550 }
551
552 RM_STATUS NV_API_CALL os_raise_smp_barrier(void)
553 {
554     return RM_ERR_NOT_SUPPORTED;
555 }
556
557 RM_STATUS NV_API_CALL os_clear_smp_barrier(void)
558 {
559     return RM_ERR_NOT_SUPPORTED;
560 }
561
562 struct os_mutex {
563     nv_stack_t *sp;
564     struct spinlock lock;
565     int refcnt;
566 };
567
568 RM_STATUS NV_API_CALL os_alloc_sema(void **semaphore)
569 {
570     nv_stack_t *sp;
571     struct os_mutex *mtx;
572     RM_STATUS status;
573
574     NV_UMA_ZONE_ALLOC_STACK(sp);
575     if (sp == NULL)
576         return RM_ERR_NO_FREE_MEM;
577
578     status = os_alloc_mem((void **)&mtx, sizeof(struct os_mutex));
579     if (status != RM_OK) {
580         NV_UMA_ZONE_FREE_STACK(sp);
581         return status;
582     }
583
584     spin_init(&mtx->lock);
585
586     mtx->sp = sp;
587     mtx->refcnt = 1;
588
589     *semaphore = (void *) mtx;
590
591     return RM_OK;
592 }
593
594 RM_STATUS NV_API_CALL os_free_sema(void *semaphore)
595 {
596     struct os_mutex *mtx = semaphore;
597     nv_stack_t *sp;
598
599     sp = mtx->sp;
600     NV_UMA_ZONE_FREE_STACK(sp);
601
602     spin_uninit(&mtx->lock);
603
604     os_free_mem(semaphore);
605
606     return RM_OK;
607 }
608
609 RM_STATUS NV_API_CALL os_acquire_sema(void *semaphore)
610 {
611     struct os_mutex *mtx = semaphore;
612
613     spin_lock_wr(&mtx->lock);
614     if (mtx->refcnt > 0)
615         rm_disable_interrupts(mtx->sp);
616     mtx->refcnt--;
617     if (mtx->refcnt < 0)
618 #if __DragonFly_version < 200206
619         msleep(mtx, &mtx->lock, 0, "nvsemaq", 0);
620 #else
621         ssleep(mtx, &mtx->lock, 0, "nvsemaq", 0);
622 #endif
623     spin_unlock_wr(&mtx->lock);
624
625     return RM_OK;
626 }
627
628 RM_STATUS NV_API_CALL os_cond_acquire_sema(void *semaphore)
629 {
630     struct os_mutex *mtx = semaphore;
631
632     spin_lock_wr(&mtx->lock);
633     if (mtx->refcnt < 1) {
634         spin_unlock_wr(&mtx->lock);
635         return RM_ERROR;
636     } else {
637         rm_disable_interrupts(mtx->sp);
638         mtx->refcnt--;
639         spin_unlock_wr(&mtx->lock);
640     }
641
642     return RM_OK;
643 }
644
645 RM_STATUS NV_API_CALL os_release_sema(void *semaphore)
646 {
647     struct os_mutex *mtx = semaphore;
648
649     spin_lock_wr(&mtx->lock);
650     if (mtx->refcnt < 0)
651         wakeup_one(mtx);
652     else
653         rm_enable_interrupts(mtx->sp);
654     mtx->refcnt++;
655     spin_unlock_wr(&mtx->lock);
656
657     return RM_OK;
658 }
659
660 BOOL NV_API_CALL os_is_acquired_sema(void *semaphore)
661 {
662     struct os_mutex *mtx = semaphore;
663     return (mtx->refcnt < 1);
664 }
665
666 BOOL NV_API_CALL os_pat_supported(void)
667 {
668     /*
669      * FreeBSD has no native PAT support and there's no good
670      * way to implement it privately as we do on Linux.
671      */
672     return FALSE;
673 }
674
675 void NV_API_CALL os_register_compatible_ioctl(
676     U032 cmd,
677     U032 size
678 )
679 {
680 }
681
682 void NV_API_CALL os_unregister_compatible_ioctl(
683     U032 cmd,
684     U032 size
685 )
686 {
687 }
688
689 RM_STATUS NV_API_CALL os_disable_console_access(void)
690 {
691     return RM_OK;
692 }
693
694 RM_STATUS NV_API_CALL os_enable_console_access(void)
695 {
696     return RM_OK;
697 }
698 NvU64 NV_API_CALL os_acquire_spinlock(void *pSema)
699 {
700     struct os_mutex *mtx = pSema;
701
702     spin_lock_wr(&mtx->lock);
703
704     return 0;
705 }
706
707 void NV_API_CALL os_release_spinlock(void *pSema, NvU64 oldIrql)
708 {
709     struct os_mutex *mtx = pSema;
710
711     spin_unlock_wr(&mtx->lock);
712 }
713
714 RM_STATUS NV_API_CALL os_get_address_space_info(
715     NvU64 *userStartAddress,
716     NvU64 *userEndAddress,
717     NvU64 *kernelStartAddress,
718     NvU64 *kernelEndAddress
719 )
720 {
721     return RM_ERR_NOT_SUPPORTED;
722 }
723