1 /*
   2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
   3  * Use is subject to license terms.
   4  */
   5 
   6 /*
   7  * drm_context.h -- IOCTLs for generic contexts -*- linux-c -*-
   8  * Created: Fri Nov 24 18:31:37 2000 by gareth@valinux.com
   9  */
  10 /*
  11  * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
  12  * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
  13  * All Rights Reserved.
  14  *
  15  * Permission is hereby granted, free of charge, to any person obtaining a
  16  * copy of this software and associated documentation files (the "Software"),
  17  * to deal in the Software without restriction, including without limitation
  18  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  19  * and/or sell copies of the Software, and to permit persons to whom the
  20  * Software is furnished to do so, subject to the following conditions:
  21  *
  22  * The above copyright notice and this permission notice (including the next
  23  * paragraph) shall be included in all copies or substantial portions of the
  24  * Software.
  25  *
  26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  27  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  28  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  29  * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
  30  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  31  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  32  * OTHER DEALINGS IN THE SOFTWARE.
  33  *
  34  * Authors:
  35  *    Rickard E. (Rik) Faith <faith@valinux.com>
  36  *    Gareth Hughes <gareth@valinux.com>
  37  *
  38  */
  39 
  40 #pragma ident   "%Z%%M% %I%     %E% SMI"
  41 
  42 #include "drmP.h"
  43 #include "drm_io32.h"
  44 
  45 static inline int
  46 find_first_zero_bit(volatile void *p, int max)
  47 {
  48         int b;
  49         volatile int *ptr = (volatile int *)p;
  50 
  51         for (b = 0; b < max; b += 32) {
  52                 if (ptr[b >> 5] != ~0) {
  53                         for (;;) {
  54                                 if ((ptr[b >> 5] & (1 << (b & 0x1f))) == 0)
  55                                         return (b);
  56                                 b++;
  57                         }
  58                 }
  59         }
  60         return (max);
  61 }
  62 
  63 /*
  64  * Context bitmap support
  65  */
  66 void
  67 drm_ctxbitmap_free(drm_device_t *dev, int ctx_handle)
  68 {
  69         if (ctx_handle < 0 || ctx_handle >= DRM_MAX_CTXBITMAP ||
  70             dev->ctx_bitmap == NULL) {
  71                 DRM_ERROR("drm_ctxbitmap_free: Attempt to free\
  72                     invalid context handle: %d\n",
  73                     ctx_handle);
  74                 return;
  75         }
  76 
  77         DRM_LOCK();
  78         clear_bit(ctx_handle, dev->ctx_bitmap);
  79         dev->context_sareas[ctx_handle] = NULL;
  80         DRM_UNLOCK();
  81 }
  82 
  83 /* Is supposed to return -1 if  any error by calling functions */
  84 int
  85 drm_ctxbitmap_next(drm_device_t *dev)
  86 {
  87         int bit;
  88 
  89         if (dev->ctx_bitmap == NULL)
  90                 return (-1);
  91 
  92         DRM_LOCK();
  93         bit = find_first_zero_bit(dev->ctx_bitmap, DRM_MAX_CTXBITMAP);
  94         if (bit >= DRM_MAX_CTXBITMAP) {
  95                 DRM_UNLOCK();
  96                 return (-1);
  97         }
  98 
  99         set_bit(bit, dev->ctx_bitmap);
 100         DRM_DEBUG("drm_ctxbitmap_next: bit : %d", bit);
 101         if ((bit+1) > dev->max_context) {
 102                 dev->max_context = (bit+1);
 103                 if (dev->context_sareas != NULL) {
 104                         drm_local_map_t **ctx_sareas;
 105                         ctx_sareas = drm_realloc(dev->context_sareas,
 106                             (dev->max_context - 1) *
 107                             sizeof (*dev->context_sareas),
 108                             dev->max_context *
 109                             sizeof (*dev->context_sareas),
 110                             DRM_MEM_MAPS);
 111                         if (ctx_sareas == NULL) {
 112                                 clear_bit(bit, dev->ctx_bitmap);
 113                                 DRM_UNLOCK();
 114                                 return (-1);
 115                         }
 116                         dev->context_sareas = ctx_sareas;
 117                         dev->context_sareas[bit] = NULL;
 118                 } else {
 119                         /* max_context == 1 at this point */
 120                         dev->context_sareas = drm_alloc(dev->max_context *
 121                             sizeof (*dev->context_sareas), KM_NOSLEEP);
 122                         if (dev->context_sareas == NULL) {
 123                                 clear_bit(bit, dev->ctx_bitmap);
 124                                 DRM_UNLOCK();
 125                                 return (-1);
 126                         }
 127                         dev->context_sareas[bit] = NULL;
 128                 }
 129         }
 130         DRM_UNLOCK();
 131         DRM_DEBUG("drm_ctxbitmap_next: return %d", bit);
 132         return (bit);
 133 }
 134 
 135 int
 136 drm_ctxbitmap_init(drm_device_t *dev)
 137 {
 138         int i;
 139         int temp;
 140 
 141         DRM_LOCK();
 142         dev->ctx_bitmap = drm_calloc(1, DRM_PAGE_SIZE, DRM_MEM_CTXBITMAP);
 143         if (dev->ctx_bitmap == NULL) {
 144                 DRM_UNLOCK();
 145                 return (ENOMEM);
 146         }
 147         dev->context_sareas = NULL;
 148         dev->max_context = -1;
 149         DRM_UNLOCK();
 150 
 151         for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) {
 152                 temp = drm_ctxbitmap_next(dev);
 153                 DRM_DEBUG("drm_ctxbitmap_init : %d", temp);
 154         }
 155         return (0);
 156 }
 157 
 158 void
 159 drm_ctxbitmap_cleanup(drm_device_t *dev)
 160 {
 161         DRM_LOCK();
 162         if (dev->context_sareas != NULL)
 163                 drm_free(dev->context_sareas,
 164                     sizeof (*dev->context_sareas) *
 165                     dev->max_context,
 166                     DRM_MEM_MAPS);
 167         drm_free(dev->ctx_bitmap, DRM_PAGE_SIZE, DRM_MEM_CTXBITMAP);
 168         DRM_UNLOCK();
 169 }
 170 
 171 /*
 172  * Per Context SAREA Support
 173  */
 174 /*ARGSUSED*/
 175 int
 176 drm_getsareactx(DRM_IOCTL_ARGS)
 177 {
 178         DRM_DEVICE;
 179         drm_ctx_priv_map_t request;
 180         drm_local_map_t *map;
 181 
 182 #ifdef  _MULTI_DATAMODEL
 183         if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
 184                 drm_ctx_priv_map_32_t request32;
 185                 DRM_COPYFROM_WITH_RETURN(&request32, (void *)data,
 186                     sizeof (drm_ctx_priv_map_32_t));
 187                 request.ctx_id = request32.ctx_id;
 188                 request.handle = (void *)(uintptr_t)request32.handle;
 189         } else
 190 #endif
 191                 DRM_COPYFROM_WITH_RETURN(&request, (void *)data,
 192                     sizeof (request));
 193 
 194         DRM_LOCK();
 195         if (dev->max_context < 0 || request.ctx_id >= (unsigned)
 196             dev->max_context) {
 197                 DRM_UNLOCK();
 198                 return (EINVAL);
 199         }
 200 
 201         map = dev->context_sareas[request.ctx_id];
 202         DRM_UNLOCK();
 203 
 204         if (!map)
 205                 return (EINVAL);
 206 
 207         request.handle = map->handle;
 208 
 209 #ifdef  _MULTI_DATAMODEL
 210         if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
 211                 drm_ctx_priv_map_32_t request32;
 212                 request32.ctx_id = request.ctx_id;
 213                 request32.handle = (caddr32_t)(uintptr_t)request.handle;
 214                 DRM_COPYTO_WITH_RETURN((void *)data, &request32,
 215                     sizeof (drm_ctx_priv_map_32_t));
 216         } else
 217 #endif
 218                 DRM_COPYTO_WITH_RETURN((void *)data,
 219                     &request, sizeof (request));
 220 
 221         return (0);
 222 }
 223 
 224 /*ARGSUSED*/
 225 int
 226 drm_setsareactx(DRM_IOCTL_ARGS)
 227 {
 228         DRM_DEVICE;
 229         drm_ctx_priv_map_t request;
 230         drm_local_map_t *map = NULL;
 231 
 232 #ifdef  _MULTI_DATAMODEL
 233         if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
 234                 drm_ctx_priv_map_32_t request32;
 235 
 236                 DRM_COPYFROM_WITH_RETURN(&request32, (void *)data,
 237                     sizeof (drm_ctx_priv_map_32_t));
 238                 request.ctx_id = request32.ctx_id;
 239                 request.handle = (void *)(uintptr_t)request32.handle;
 240         } else
 241 #endif
 242                 DRM_COPYFROM_WITH_RETURN(&request,
 243                     (void *)data, sizeof (request));
 244 
 245         DRM_LOCK();
 246         TAILQ_FOREACH(map, &dev->maplist, link) {
 247                 if (map->handle == request.handle) {
 248                         if (dev->max_context < 0)
 249                                 goto bad;
 250                         if (request.ctx_id >= (unsigned)dev->max_context)
 251                                 goto bad;
 252                         dev->context_sareas[request.ctx_id] = map;
 253                         DRM_UNLOCK();
 254                         return (0);
 255                 }
 256         }
 257 
 258 bad:
 259         DRM_UNLOCK();
 260         return (EINVAL);
 261 }
 262 
 263 /*
 264  * The actual DRM context handling routines
 265  */
 266 int
 267 drm_context_switch(drm_device_t *dev, int old, int new)
 268 {
 269         if (test_and_set_bit(0, &dev->context_flag)) {
 270                 DRM_ERROR("drm_context_switch: Reentering -- FIXME");
 271                 return (EBUSY);
 272         }
 273 
 274         DRM_DEBUG("drm_context_switch: Context switch from %d to %d",
 275             old, new);
 276 
 277         if (new == dev->last_context) {
 278                 clear_bit(0, &dev->context_flag);
 279                 return (0);
 280         }
 281 
 282         return (0);
 283 }
 284 
 285 int
 286 drm_context_switch_complete(drm_device_t *dev, int new)
 287 {
 288         dev->last_context = new;  /* PRE/POST: This is the _only_ writer. */
 289 
 290         if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
 291                 DRM_ERROR(
 292                     "drm_context_switch_complete: Lock not held");
 293         }
 294         /*
 295          * If a context switch is ever initiated
 296          * when the kernel holds the lock, release
 297          * that lock here.
 298          */
 299         clear_bit(0, &dev->context_flag);
 300 
 301         return (0);
 302 }
 303 
 304 /*ARGSUSED*/
 305 int
 306 drm_resctx(DRM_IOCTL_ARGS)
 307 {
 308         drm_ctx_res_t res;
 309         drm_ctx_t ctx;
 310         int i;
 311 
 312 #ifdef  _MULTI_DATAMODEL
 313         if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
 314                 drm_ctx_res_32_t res32;
 315                 DRM_COPYFROM_WITH_RETURN(&res32, (void *)data, sizeof (res32));
 316                 res.count = res32.count;
 317                 res.contexts = (drm_ctx_t *)(uintptr_t)res32.contexts;
 318         } else
 319 #endif
 320                 DRM_COPYFROM_WITH_RETURN(&res, (void *)data, sizeof (res));
 321 
 322         if (res.count >= DRM_RESERVED_CONTEXTS) {
 323                 bzero(&ctx, sizeof (ctx));
 324                 for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) {
 325                         ctx.handle = i;
 326                         DRM_COPYTO_WITH_RETURN(&res.contexts[i],
 327                             &ctx, sizeof (ctx));
 328                 }
 329         }
 330         res.count = DRM_RESERVED_CONTEXTS;
 331 
 332 #ifdef  _MULTI_DATAMODEL
 333         if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
 334                 drm_ctx_res_32_t res32;
 335                 res32.count = res.count;
 336                 res32.contexts = (caddr32_t)(uintptr_t)res.contexts;
 337 
 338                 DRM_COPYTO_WITH_RETURN((void *)data, &res32,
 339                     sizeof (drm_ctx_res_32_t));
 340         } else
 341 #endif
 342                 DRM_COPYTO_WITH_RETURN((void *)data, &res, sizeof (res));
 343 
 344         return (0);
 345 }
 346 
 347 /*ARGSUSED*/
 348 int
 349 drm_addctx(DRM_IOCTL_ARGS)
 350 {
 351         DRM_DEVICE;
 352         drm_ctx_t ctx;
 353 
 354         DRM_COPYFROM_WITH_RETURN(&ctx, (void *)data, sizeof (ctx));
 355 
 356         ctx.handle = drm_ctxbitmap_next(dev);
 357         if (ctx.handle == DRM_KERNEL_CONTEXT) {
 358                 /* Skip kernel's context and get a new one. */
 359                 ctx.handle = drm_ctxbitmap_next(dev);
 360         }
 361         if (ctx.handle == (drm_context_t)-1) {
 362                 return (ENOMEM);
 363         }
 364 
 365         if (dev->driver->context_ctor && ctx.handle != DRM_KERNEL_CONTEXT) {
 366                 dev->driver->context_ctor(dev, ctx.handle);
 367         }
 368 
 369         DRM_COPYTO_WITH_RETURN((void *)data, &ctx, sizeof (ctx));
 370 
 371         return (0);
 372 }
 373 
 374 /*ARGSUSED*/
 375 int
 376 drm_modctx(DRM_IOCTL_ARGS)
 377 {
 378         /* This does nothing */
 379         return (0);
 380 }
 381 
 382 /*ARGSUSED*/
 383 int
 384 drm_getctx(DRM_IOCTL_ARGS)
 385 {
 386         drm_ctx_t ctx;
 387 
 388         DRM_COPYFROM_WITH_RETURN(&ctx, (void *)data, sizeof (ctx));
 389 
 390         /* This is 0, because we don't handle any context flags */
 391         ctx.flags = 0;
 392 
 393         DRM_COPYTO_WITH_RETURN((void *)data, &ctx, sizeof (ctx));
 394 
 395         return (0);
 396 }
 397 
 398 /*ARGSUSED*/
 399 int
 400 drm_switchctx(DRM_IOCTL_ARGS)
 401 {
 402         DRM_DEVICE;
 403         drm_ctx_t ctx;
 404 
 405         DRM_COPYFROM_WITH_RETURN(&ctx, (void *)data, sizeof (ctx));
 406 
 407         DRM_DEBUG("drm_switchctx: %d", ctx.handle);
 408         return (drm_context_switch(dev, dev->last_context, ctx.handle));
 409 }
 410 
 411 /*ARGSUSED*/
 412 int
 413 drm_newctx(DRM_IOCTL_ARGS)
 414 {
 415         DRM_DEVICE;
 416         drm_ctx_t ctx;
 417 
 418         DRM_COPYFROM_WITH_RETURN(&ctx, (void *)data, sizeof (ctx));
 419 
 420         DRM_DEBUG("drm_newctx: %d", ctx.handle);
 421         (void) drm_context_switch_complete(dev, ctx.handle);
 422 
 423         return (0);
 424 }
 425 
 426 /*ARGSUSED*/
 427 int
 428 drm_rmctx(DRM_IOCTL_ARGS)
 429 {
 430         DRM_DEVICE;
 431         drm_ctx_t ctx;
 432 
 433         DRM_COPYFROM_WITH_RETURN(&ctx, (void *)data, sizeof (ctx));
 434 
 435         DRM_DEBUG("drm_rmctx : %d", ctx.handle);
 436         if (ctx.handle != DRM_KERNEL_CONTEXT) {
 437                 if (dev->driver->context_dtor) {
 438                         DRM_LOCK();
 439                         dev->driver->context_dtor(dev, ctx.handle);
 440                         DRM_UNLOCK();
 441                 }
 442 
 443                 drm_ctxbitmap_free(dev, ctx.handle);
 444         }
 445 
 446         return (0);
 447 }