#ifndef lint static char sccsid[] = "@(#)gt.c 1.1 94/10/31 SMI"; #endif /* * Copyright (c) 1990, 1991, 1992 by Sun Microsystems, Inc. * * GT graphics accelerator driver */ #include "gt.h" #include "win.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef sun4m #include #endif sun4m #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include addr_t map_regs(); void unmap_regs(); void report_dev(); struct proc *pfind(); char *strcpy(); u_long rmalloc(); u_int hat_getkpfnum(); caddr_t getpages(); int gt_identify(), gt_attach(), gtpoll(), gtopen(); int gtclose(), gtioctl(), gtsegmap(); static void gtintr(); static void gt_shared_init(); static gt_server_check(), gt_diag_check(); static gt_fbioputcmap(), gt_fbiogetcmap(); static gt_fbiogtype(); #if NWIN > 0 static gt_fbiogpixrect(); #endif NWIN static gt_fbiogvideo(); static gt_fbiosvideo(); static gt_fbiovertical(); static gt_widalloc(); static gt_calc_wid(); static gt_calc_power(); static gt_widfree(); static gt_widread(); static gt_widpost(); static gt_direct_wlut_update(); static gt_clutalloc(); static gt_clutfree(); static gt_clutread(); static gt_clutpost(); static gt_gamma_clutpost(), gt_load_degamma(); static gt_cursor_clutpost(); static gt_post_command(); static gt_fcsalloc(); static gt_fcsfree(); static gt_setserver(); static void gt_server_free(); static gt_setdiagmode(); static void gt_diag_free(); static gt_setwpart(); static gt_getwpart(); static gt_setclutpart(); static gt_getclutpart(); static gt_lightpenenable(); static gt_setlightpenparam(); static gt_getlightpenparam(); static gt_setmonitor(); static gt_getmonitor(); static gt_setgamma(); static gt_getgamma(); static gt_sgversion(); static gt_fbiosattr(); static gt_fbiogattr(); static void gt_set_gattr_fbtype(); static gt_loadkmcb(); static void gt_init_kmcb(); static gt_connect(); static gt_disconnect(); static void gta_exit(); static void gt_freept(); static void gt_freemap(); static gt_vmctl(); static gta_suspend(); static gta_resume(); static gt_vmback(); static gt_vmunback(); static gt_grabhw(); static gt_ungrabhw(); static void gta_reset_accelerator(); static void gt_clean_kmcb(); static void gt_setnocache(); static void gt_link_detach(); static void gt_link_attach(); static struct proc_ll_type *gt_find_proc_link(); static void gt_insert_link(); static void gt_alloc_sr(); static void gt_init_sr(); static void gt_decrement_refcount(); static void gt_aslock(); static void gt_asunlock(); static void gta_unload(); static void gt_cntxsave(); static void gt_cntxrestore(); static struct at_proc *gta_findproc(); static struct at_proc *gta_findas(); static gt_validthread(); static void gt_pagedaemon(); static gt_badfault(); static gt_faultcheck(); static void gt_lvl1fault(); static void gt_pagefault(); static void gta_abortfault(); static void gt_mmuload(); static u_int gta_locksetup(); static struct gt_pte *gta_getmmudata(); static void gt_scheduler(); static void gt_schedkmcb(); static void gta_idle(); static void gta_run(); static void gta_stop(); static void gta_start(); static void gta_kmcbwait(); static void gta_loadroot(); static void gta_init(); static void gta_hash_insert(); static void gta_hash_delete(); static struct gta_lockmap *gta_hash_find(); static void gt_daemon_init(); static void gt_listinsert_next(); static void gt_listinsert_prev(); static void gt_listremove(); static void gt_enable_intr(); static void gt_freeze_atu(); static void gt_thaw_atu(); static void gt_wait_for_thaw(); static void gt_set_flags(); static void gt_clear_flags(); static gt_check_pf_busy(); static void gt_status_dump(); #ifdef sun4m extern u_int gt_ldsbusreg(); extern void gt_stsbusreg(); #endif sun4m #ifdef GT_DEBUG int gt_debug; #define DEBUGF(level, args) _STMT(if (gt_debug >= (level)) printf args;) #else #define DEBUGF(level, args) /* nothing */ #endif GT_DEBUG #define DUMP_MIA_REGS 0x1 #define DUMP_RP_REGS 0x2 #define DUMP_GT_REGS (DUMP_MIA_REGS | DUMP_RP_REGS) #define GT_PF_BUSY 0x00000040 #define GT_SPIN_LUT (200 * 1000) /* lut copy spin: 200 msec */ #define GT_SPIN_PFBUSY (500 * 1000) /* pf busy spin: 500 msec */ /* FBO defines */ #define FBO_ENABLE_ALL_INTERLEAVE 0x00000050 #define FBO_ENABLE_COPY_REQUEST 0x00000001 /* * Window Lookup Table masks */ #define HK_WLUT_MASK_IMAGE 0x01 #define HK_WLUT_MASK_OVERLAY 0x02 #define HK_WLUT_MASK_PLANE_GROUP 0x04 #define HK_WLUT_MASK_CLUT 0x08 #define HK_WLUT_MASK_FCS 0x10 #define HKFBO_WLUT_ENTRY_OFFSET 4 /* offset to next entry */ /* * Window Lookup Table entry definitions */ #define HKFBO_WLUT_MASK 0x00007FFF #define HKFBO_WLUT_ALPHA_MODE 0x00006000 /* Alpha mode */ #define HKFBO_WLUT_INDEX_STEREO 0x00001000 /* Indexed stereo */ #define HKFBO_WLUT_FCS_SELECT 0x00000E00 /* Fast clear set select */ #define HKFBO_WLUT_FCS_SEL_SHIFT 9 #define HKFBO_WLUT_CLUT_SELECT 0x000001E0 /* CLUT select */ #define HKFBO_WLUT_CLUT_SEL_SHIFT 5 #define HKFBO_WLUT_OV_BUFFER 0x00000010 /* Overlay buffer (1 = B) */ #define HKFBO_WLUT_IMAGE_BUFFER 0x00000008 /* Image buffer (1 = B) */ #define HKFBO_WLUT_COLOR_MODE 0x00000007 /* Color mode field */ #define HKFBO_WLUT_CM_8OVERLAY 0x00000000 /* Color mode values */ #define HKFBO_WLUT_CM_8BLUE 0x00000001 #define HKFBO_WLUT_CM_8GREEN 0x00000002 #define HKFBO_WLUT_CM_8RED 0x00000003 #define HKFBO_WLUT_CM_12LOW 0x00000004 #define HKFBO_WLUT_CM_12HIGH 0x00000005 #define HKFBO_WLUT_CM_24BIT 0x00000006 #define HK_8BIT_RED 0 #define HK_8BIT_GREEN 1 #define HK_8BIT_BLUE 2 #define HK_8BIT_OVERLAY 3 #define HK_12BIT_LOWER 4 #define HK_12BIT_UPPER 5 #define HK_24BIT 6 #define GT_ADDR(a) (softc->baseaddr + btop(a)) #if defined(sun4c) #define GT_KVTOP(x) (x) #elif defined(sun4m) #define GT_KVTOP(x) \ ((hat_getkpfnum((addr_t)x)<member) #define GT_LIST_INIT(node, type, list) \ ((struct type *) (node))->list.l_next = \ (struct gt_list *) ((struct type *) (node))->list.l_prev = \ (struct gt_list *) &(((struct type *) (node))->list) #define GT_LIST_NEXT(node, type, list) \ ((struct type *) \ ((char *) (((struct type *) (node))->list.l_next) - \ OFFSETOF(type, list))) #define GT_LIST_PREV(node, type, list) \ ((struct type *) \ ((char *) (((struct type *) (node))->list.l_prev) - \ OFFSETOF(type, list))) #define GT_LIST_INSERT_NEXT(node, newnode, type, list) \ gt_listinsert_next( \ (struct gt_list *) &(((struct type *) (node))->list), \ (struct gt_list *) &(((struct type *) (newnode))->list) ) #define GT_LIST_INSERT_PREV(node, newnode, type, list) \ gt_listinsert_prev( \ (struct gt_list *) &(((struct type *) (node))->list), \ (struct gt_list *) &(((struct type *) (newnode))->list) ) #define GT_LIST_REMOVE(node, type, list) \ gt_listremove((struct gt_list *) &(((struct type *) (node))->list)) /* * gt mapping structure */ #define GTM_K 0x0001 /* provide kernel mapping */ #define GTM_P 0x0002 /* privileged mapping */ #define GTM_KP (GTM_K | GTM_P) struct gt_map { u_int vaddr; /* virtual (mmap offset) address */ u_int poffset; /* physical offset */ u_int size; /* size in bytes */ u_short flags; /* superuser only protection flag */ } gt_map[] = { {RP_BOOT_PROM_VA, RP_BOOT_PROM_OF, RP_BOOT_PROM_SZ, GTM_P}, #ifndef GT_DEBUG {RP_FE_CTRL_VA, RP_FE_CTRL_OF, RP_FE_CTRL_SZ, 0}, #else GT_DEBUG {RP_FE_CTRL_VA, RP_FE_CTRL_OF, RP_FE_CTRL_SZ, GTM_K}, #endif GT_DEBUG {RP_SU_RAM_VA, RP_SU_RAM_OF, RP_SU_RAM_SZ, 0}, {RP_HOST_CTRL_VA, RP_HOST_CTRL_OF, RP_HOST_CTRL_SZ, GTM_K}, {FBI_PFGO_VA, FBI_PFGO_OF, FBI_PFGO_SZ, GTM_K}, {FBI_REG_VA, FBI_REG_OF, FBI_REG_SZ, GTM_K}, {FBO_CLUT_CTRL_VA, FBO_CLUT_CTRL_OF, FBO_CLUT_CTRL_SZ, GTM_KP}, {FBO_CLUT_MAP_VA, FBO_CLUT_MAP_OF, FBO_CLUT_MAP_SZ, GTM_KP}, {FBO_WLUT_CTRL_VA, FBO_WLUT_CTRL_OF, FBO_WLUT_CTRL_SZ, GTM_KP}, {FBO_WLUT_MAP_VA, FBO_WLUT_MAP_OF, FBO_WLUT_MAP_SZ, GTM_KP}, {FBO_SCREEN_CTRL_VA,FBO_SCREEN_CTRL_OF, FBO_SCREEN_CTRL_SZ, GTM_KP}, {FBO_RAMDAC_CTRL_VA,FBO_RAMDAC_CTRL_OF, FBO_RAMDAC_CTRL_SZ, GTM_KP}, {FE_CTRL_VA, FE_CTRL_OF, FE_CTRL_SZ, GTM_KP}, {FE_HFLAG1_VA, FE_HFLAG1_OF, FE_HFLAG1_SZ, GTM_K}, {FE_HFLAG2_VA, FE_HFLAG2_OF, FE_HFLAG2_SZ, GTM_K}, {FE_HFLAG3_VA, FE_HFLAG3_OF, FE_HFLAG3_SZ, GTM_KP}, {FE_I860_VA, FE_I860_OF, FE_I860_SZ, GTM_K}, {FB_VA, FB_OF, FB_SZ, 0}, {FB_CD_VA, FB_CD_OF, FB_CD_SZ, GTM_K}, {FB_CE_VA, FB_CE_OF, FB_CE_SZ, GTM_K}, }; /* * kernel mapping indexes * * Note: these must be kept in agreement with the gt_map array of structs. */ #define MAP_RP_BOOT_PROM 0 #define MAP_RP_FE_CTRL 1 #define MAP_RP_SU_RAM 2 #define MAP_RP_HOST_CTRL 3 #define MAP_FBI_PFGO 4 #define MAP_FBI_REG 5 #define MAP_FBO_CLUT_CTRL 6 #define MAP_FBO_CLUT_MAP 7 #define MAP_FBO_WLUT_CTRL 8 #define MAP_FBO_WLUT_MAP 9 #define MAP_FBO_SCREEN_CTRL 10 #define MAP_FBO_RAMDAC_CTRL 11 #define MAP_FE_CTRL 12 #define MAP_FE_HFLAG1 13 #define MAP_FE_HFLAG2 14 #define MAP_FE_HFLAG3 15 #define MAP_FE_I860 16 #define MAP_FB 17 #define MAP_FB_CD 18 #define MAP_FB_CE 19 #define MAP_NMAP (sizeof (gt_map) / sizeof (gt_map[0])) /* * graphics context */ typedef struct gt_cntxt { unsigned gc_fe_hold; unsigned gc_rp_host_csr; unsigned gc_rp_host_as; unsigned gc_fbi_vwclp_x; unsigned gc_fbi_vwclp_y; unsigned gc_fbi_fb_width; unsigned gc_fbi_buf_sel; unsigned gc_fbi_stereo_cl; unsigned gc_fbi_cur_wid; unsigned gc_fbi_wid_ctrl; unsigned gc_fbi_wbc; unsigned gc_fbi_con_z; unsigned gc_fbi_z_ctrl; unsigned gc_fbi_i_wmask; unsigned gc_fbi_w_wmask; unsigned gc_fbi_b_mode; unsigned gc_fbi_b_wmask; unsigned gc_fbi_rop; unsigned gc_fbi_mpg; unsigned gc_fbi_s_mask; unsigned gc_fbi_fg_col; unsigned gc_fbi_bg_col; unsigned gc_fbi_s_trans; unsigned gc_fbi_dir_size; unsigned gc_fbi_copy_src; } gt_cntxt; /* * shared resource types */ enum res_type { RT_CLUT8, /* 8-bit CLUT */ RT_OWID, /* overlay WID */ RT_IWID, /* image WID */ RT_FCS, /* fast-clear set */ }; struct shar_res_type { enum res_type srt_type; int srt_base; u_int srt_count; struct shar_res_type *srt_next; }; typedef struct table_type { u_int tt_flags; u_int tt_refcount; } table_type; #define TT_USED 0x1 struct proc_ll_type { short plt_pid; struct shar_res_type *plt_sr; struct proc_ll_type *plt_next; struct proc_ll_type *plt_prev; }; #define NWID 1024 #define NCLUT 16 #define NCLUT_8 14 #define NFCS 4 #define GT_CMAP_SIZE 256 #define CLUT_REDMASK 0x000000ff #define CLUT_GREENMASK 0x0000ff00 #define CLUT_BLUEMASK 0x00ff0000 #define NWIDPOST 16 /* max nbr of WID entries per post */ #define MAX_DIAG_PROC 8 #define NWID_BITS 10 int gt_power_table[NWID_BITS+1] = {1,2,4,8,16,32,64,128,256,512,1024}; /* * wait channels */ #define WCHAN_VRT 0 /* waiting for vertical retrace */ #define WCHAN_CLUT 1 /* waiting for CLUT post */ #define WCHAN_WLUT 2 /* waiting for WLUT post */ #define WCHAN_VCR 3 /* segfault waiting to access a VCR */ #define WCHAN_SCHED 4 /* scheduler sleeps here */ #define WCHAN_SUSPEND 5 /* waiting for accelerator to suspend */ #define WCHAN_THAW 6 /* waiting for ATU to thaw */ #define WCHAN_ASLOCK 7 /* waiting for an as_fault to complete */ #define TOTAL_WCHANS 8 /* * driver per-unit data */ static struct gt_softc { unsigned flags; /* internal flags */ u_int w, h; /* resolution */ int monitor; /* type of monitor */ u_long baseaddr; /* physical base address */ struct at_proc *at_head; /* accel threads list head */ struct at_proc *at_curr; /* current accel thread */ struct seg *uvcrseg; /* current uvcr seg ptr */ struct seg *svcrseg; /* current svcr seg ptr */ addr_t va[MAP_NMAP]; /* mapped in devices */ fe_ctrl_sp *fe_ctrl_sp_ptr; /* pointer to FE ctrl space */ Hkvc_kmcb kmcb; /* host to GT communication */ short gt_grabber; /* pid of grabbing process */ short server_proc; /* server process id */ short diag_proc; /* diag process count */ short diag_proc_table[MAX_DIAG_PROC]; /* diag process id */ u_int wlut_wid_part; /* sunview overlay wid index */ u_int wid_ocount; /* nbr of overlay wids */ u_int wid_icount; /* nbr of image wids */ short clut_part_total; /* nbr of server cluts */ short clut_part_used; /* nbr of server cluts in use */ struct proc_ll_type *proc_list; /* process linked list that */ /* own a GT resource */ u_int light_pen_enable; /* lightpen enable/disable */ u_int light_pen_distance; /* X+Y delta before returning */ /* lightpen info */ u_int light_pen_time; /* nbr of frames before */ /* next light pen info */ table_type wid_otable[NWID]; /* overlay wid table */ table_type wid_itable[NWID]; /* image wid table */ table_type clut_table[NCLUT_8]; /* CLUT table */ table_type fcs_table[NFCS]; /* FCS table */ u_int shadow_owlut[NWID]; /* shadow of overlay WLUT */ u_int shadow_iwlut[NWID]; /* shadow of image WLUT */ u_char shadow_cursor[3][2]; /* shadow of CLUTs */ u_int *printbuf; /* gt print buffer */ char wchan[TOTAL_WCHANS]; /* wait channels */ unsigned gt_user_colors[GT_CMAP_SIZE]; /* kmcb cmap (user) */ unsigned gt_sys_colors[GT_CMAP_SIZE]; /* kmcb cmap (kernel) */ u_char gamma[GT_CMAP_SIZE]; /* gamma table */ u_char degamma[GT_CMAP_SIZE]; /* degamma table */ float gammavalue; /* gamma value */ Gt_clut_update kernel_clut_post; /* CLUT post info (kernel) */ Gt_clut_update user_clut_post; /* CLUT post info (user) */ Gt_kernel_wlut_update wlut_post; /* WLUT POST info */ Gt_wlut_update gt_wlut_update[NWIDPOST]; /* wlut entries */ gt_cntxt cntxt; /* initial gfx context */ struct fbgattr *gattr; /* bw2 emulation: attributes */ struct proc *owner; /* owner */ struct gt_version gt_version; /* ucode version numbers */ #if NWIN > 0 Pixrect pr; struct gt_data gtd; #endif NWIN > 0 } gt_softc[NGT]; /* * softc flags */ #define F_VRT_RETRACE 0x00000001 #define F_DIRECTOPEN 0x00000002 #define F_ACCELOPEN 0x00000004 #define F_GAMMALOADED 0x00000010 #define F_DEGAMMALOADED 0x00000020 #define F_GAMMASET 0x00000040 #define F_VASCO 0x00000080 #define F_WLUT_PEND 0x00000100 #define F_CLUT_PEND 0x00000200 #define F_CLUT_HOLD 0x00001000 #define F_WLUT_HOLD 0x00002000 #define F_GRAB_HOLD 0x00004000 #define F_FREEZE_LDROOT 0x00010000 #define F_FREEZE_UNLOAD 0x00020000 #define F_FREEZE_TLB 0x00040000 #define F_FREEZE_LVL1 0x00080000 #define F_FREEZE_SIG 0x00100000 #define F_PULSED 0x02000000 #define F_LVL1_MISS 0x04000000 #define F_ATU_FAULTING 0x08000000 #define F_SUSPEND 0x10000000 #define F_THREAD_RUNNING 0x20000000 #define F_UCODE_STOPPED 0x40000000 #define F_UCODE_RUNNING 0x80000000 #define F_DONTSCHEDULE ( F_CLUT_HOLD | \ F_WLUT_HOLD | \ F_GRAB_HOLD \ ) #define F_ATU_FROZEN ( F_FREEZE_LVL1 | \ F_FREEZE_TLB | \ F_FREEZE_UNLOAD | \ F_FREEZE_LDROOT | \ F_FREEZE_SIG \ ) #define F_GTOPEN (F_DIRECTOPEN | F_ACCELOPEN) #if NWIN > 0 struct pixrectops ops_v = { gt_rop, gt_putcolormap, gt_putattributes, #ifdef _PR_IOCTL_KERNEL_DEFINED gt_ioctl #endif }; #endif NWIN > 0 #define GT_DEFAULT_OWID_COUNT 32 /* 5 bits */ #define GT_DEFAULT_IWID_COUNT 32 /* 5 bits */ #define GT_DEFAULT_SUNVIEW_CLUT8 0 #define GT_DEFAULT_SUNVIEW_OWID_INDEX 1 #define GT_DEFAULT_SUNVIEW_IWID_INDEX 0 #define GT_DEFAULT_SERVER_CLUT_PART 8 #define GT_QUANTUM (hz/10) /* scheduler wakeup frequency */ #define GT_KMCBTIMEOUT (hz*120) /* kmcb timeout: 2 minutes */ #define GT_PULSERATE (30) /* frontend pulse: 30 seconds */ #define GT_PROBESIZE (NBPG) int gt_quantum = 0; /* to allow tunability */ long gt_pulse; u_int gt_sync; /* used to drain write buffers */ /* * kmcb request queue */ static struct gt_kmcblist { unsigned q_flags; struct gt_list q_list; struct gt_softc *q_softc; struct Hkvc_kmcb q_mcb; } gt_kmcbhead; /* * flags */ #define KQ_POSTED 0x00000001 #define KQ_BLOCK 0x00000002 #define MEG (1024*1024) #define GTA_PREMAP 3 /* default premap count */ #define GT_HASHTBLSIZE 256 #define GT_HASHMASK ((GT_HASHTBLSIZE-1)*PAGESIZE) #define GT_HASH(a) (((unsigned)a & GT_HASHMASK)>>PAGESHIFT) #define GTDVMASZ (4*MEG + MEG/2) /* default gt dvmasize */ #define GTLOCKSIZE (8*MEG) /* default pagelock size */ #ifdef sun4c caddr_t gta_dvmaspace; /* GT DVMA: base address*/ struct seg gtdvmaseg; #endif sun4c u_int gta_dvmasize = GTDVMASZ; /* size */ u_int gta_locksize = GTLOCKSIZE; struct gta_lockmap { struct gt_list gtm_list; /* active/free links */ struct gta_lockmap *gtm_hashnext; /* hash link */ struct as *gtm_as; /* mapped: addr space */ caddr_t gtm_addr; /* virt addr */ struct page *gtm_pp; }; int gta_lockents; /* nbr of lockpage entries */ int gta_premap = GTA_PREMAP; /* nbr premapped pages */ static struct gta_lockmap *gta_lockmapbase; /* ptr to GT lock maps */ static struct gta_lockmap gta_lockfree; /* the map's free list, */ static struct gta_lockmap gta_lockbusy; /* the in-use list, */ static struct gta_lockmap **gta_lockhash; /* and the hash table */ static struct gt_lvl1 *gta_dummylvl1; /* to abort GT faults */ /* * accelerator thread data */ struct at_proc { struct gt_list ap_list; /* links */ struct gt_softc *ap_softc; /* softc pointer */ struct proc *ap_proc; /* proc pointer */ struct as *ap_as; /* address space pointer */ struct gt_lvl1 *ap_lvl1; /* level 1 page table */ struct Hkvc_umcb *ap_umcb; /* user mcb pointer */ unsigned *ap_uvcr; /* user vcr address */ unsigned *ap_svcr; /* user signal vcr address */ unsigned ap_flags; /* thread control */ addr_t ap_lastfault; /* address last faulted by GT */ int ap_lockcount; /* gt aslock reference count */ short ap_pid; /* process's pid */ }; /* * ap_flags */ #define GTA_CONNECT 0x00000001 /* thread is connected */ #define GTA_SWLOCK 0x00000002 /* swlock on process */ #define GTA_VIRGIN 0x00000010 /* thread hasn't run yet */ #define GTA_SUSPEND 0x00000020 /* thread is suspended */ #define GTA_EXIT 0x00000040 /* thread is exiting */ #define GTA_DONTRUN 0x00000080 /* thread is not runnable */ #define GTA_GTLOCK 0x00000100 /* thread is GT as_locked */ #define GTA_HOSTLOCK 0x00000200 /* thread is HOST as_locked */ #define GTA_UVCR 0x00001000 /* thread's user vcr bit */ #define GTA_SVCR 0x00002000 /* thread's signal vcr bit */ #define GTA_SIGNAL 0x00010000 /* signal the host process */ #define GTA_ABORT 0x10000000 /* thread being aborted */ #define GTA_BLOCKED (GTA_VIRGIN | \ GTA_SUSPEND | \ GTA_EXIT | \ GTA_DONTRUN | \ GTA_ABORT \ ) /* * Addresses and indexes associated with a GT mapping */ struct gta_mmudata { struct gt_pte *gtu_pte; struct gt_lvl1 *gtu_lvl1; struct gt_lvl2 *gtu_lvl2; int gtu_index1; int gtu_index2; }; enum mia_ops { MIA_LOADROOT, /* load root pointer */ MIA_CLEARROOT, /* clear root pointer */ }; /* * Reasons for aborting a page fault */ enum abort_types { GT_ABORT_LVL1, /* level 1 unexpected */ GT_ABORT_TLB, /* tlb miss: exit or unexpected */ GT_ABORT_BADADDR, /* tlb miss: bad address */ GT_ABORT_FAULTERR, /* error from as_fault */ }; /* * Mainbus device data */ int ngt = 0; struct dev_ops gt_ops = { 0, /* revision */ gt_identify, gt_attach, gtopen, gtclose, 0, 0, 0, 0, 0, gtioctl, 0, gtsegmap, }; addr_t gt_promrev; /* OBP rev level */ /* * Monitor-specific data. This array must be kept in the order * specified by the gtreg.h define constants GT_1280_76, ... * * 1: type h w depth cms size * 2: fbwidth */ static struct gt_monitor { struct fbtype gtm_fbtype; int gtm_fbwidth; } gt_monitor[] = { { {FBTYPE_SUNGT, 1024, 1280, 8, 256, 0xa00000}, /* 1280_76 */ GT_MONITOR_TYPE_STANDARD}, { {FBTYPE_SUNGT, 1024, 1280, 8, 256, 0xa00000}, /* 1280_67 */ GT_MONITOR_TYPE_STANDARD}, { {FBTYPE_SUNGT, 900, 1152, 8, 256, 0xa00000}, /* 1152_66 */ GT_MONITOR_TYPE_STANDARD}, { {FBTYPE_SUNGT, 680, 960, 8, 256, 0xa0000}, /* stereo */ GT_MONITOR_TYPE_STEREO}, { {FBTYPE_SUNGT, 1035, 1920, 8, 256, 0xf2a000}, /* hdtv */ GT_MONITOR_TYPE_HDTV}, { {FBTYPE_SUNGT, 480, 640, 8, 256, 0xa0000}, /* ntsc */ GT_MONITOR_TYPE_STANDARD}, { {FBTYPE_SUNGT, 575, 765, 8, 256, 0xa0000}, /* pal */ GT_MONITOR_TYPE_STANDARD}, }; #define NGT_MONITORS (sizeof (gt_monitor) / sizeof (gt_monitor[0])) struct gt_fault { u_int gf_type; /* common data */ struct gt_softc *gf_softc; struct at_proc *gf_at; short gf_pid; addr_t gf_addr; /* data used for invalid pte */ struct gt_lvl1 *gf_lvl1; /* data used for invalid ptp */ struct gt_ptp *gf_ptp; int gf_index; } gt_fault; /* * gt_fault flags */ #define GF_LVL1_FAULT 0x00000001 #define GF_LVL2_FAULT 0x00000002 #define GF_SENDSIGNAL 0x00000004 #define GF_BOGUS 0x00000010 /* * The pagein daemon and the scheduler flags are not in softc. * These processes serve all GTs. */ static u_char gta_initflag; static u_char gt_daemonflag; static short gt_pagedaemonpid; static int gt_priority; /* device priority */ static int seggta_create (/* seg, argsp */); static int seggta_dup (/* seg, newsegp */); static int seggta_unmap (/* seg, addr, len */); static int seggta_free (/* seg */); static faultcode_t seggta_fault (/* seg, addr, len, type, rw */); static faultcode_t seggta_faulta(/* seg, addr */); static int seggta_hatsync (/* seg, addr, ref, mod, flags */); static int seggta_setprot (/* seg, addr, len, prot */); static int seggta_checkprot (/* seg, addr, len, prot */); static int seggta_kluster (/* seg, addr, delta */); static u_int seggta_swapout (/* seg */); static int seggta_sync (/* seg, addr, len, flags */); static int seggta_incore (/* seg, addr, len, vec */); static int seggta_lockop (/* seg, addr, len, op */); static int seggta_advise (/* seg, addr, len, function */); struct seg_ops seggta_ops = { seggta_dup, /* dup */ seggta_unmap, /* unmap */ seggta_free, /* free */ seggta_fault, /* fault */ seggta_faulta, /* faulta */ seggta_hatsync, /* hatsync */ seggta_setprot, /* setprot */ seggta_checkprot, /* checkprot */ seggta_kluster, /* kluster */ seggta_swapout, /* swapout */ seggta_sync, /* sync */ seggta_incore, /* incore */ seggta_lockop, /* lockop */ seggta_advise, /* advise */ }; static int seggtx_dup (/* seg, newsegp */); static int seggtx_unmap (/* seg, addr, len */); static int seggtx_free (/* seg */); static faultcode_t seggtx_fault (/* seg, addr, len, type, rw */); static int seggtx_hatsync (/* seg, addr, ref, mod, flags */); static int seggtx_setprot (/* seg, addr, len, prot */); static int seggtx_checkprot (/* seg, addr, len, prot */); static int seggtx_incore (/* seg, addr, len, vec */); static int seggtx_badop(); static int seggtx_create(); struct seg_ops seggtx_ops = { seggtx_dup, /* dup */ seggtx_unmap, /* unmap */ seggtx_free, /* free */ seggtx_fault, /* fault */ seggtx_badop, /* faulta */ seggtx_hatsync, /* hatsync */ seggtx_setprot, /* setprot */ seggtx_checkprot, /* checkprot */ seggtx_badop, /* kluster */ (u_int (*)()) NULL, /* swapout */ seggtx_badop, /* sync */ seggtx_incore, /* incore */ seggtx_badop, /* lockop */ seggtx_badop, /* advise */ }; extern int cpu; /* CPU type */ extern u_int gt_printbuffer[]; /* GT printbuffer */ extern u_int gt_printbuffer_size; #ifdef GT_DEBUG /* * GT statistics */ struct gtstat { u_int gs_ioctl; /* ioctl count */ u_int gs_pollerrs; /* errors reading hisr */ u_int gs_intr; /* interrupt count */ u_int gs_intr_kvcr; /* kvcr interrupt count */ u_int gs_intr_uvcr; /* uvcr interrupt count */ u_int gs_intr_lvl1; /* lvl1 interrupt count */ u_int gs_intr_tlb; /* tlb interrupt count */ u_int gs_intr_lightpen; /* lightpen interrupts */ u_int gs_widpost; /* WID posts */ u_int gs_clutpost; /* CLUT posts */ u_int gs_freemap; /* dvma freemap operations */ u_int gs_suspend; /* suspend count */ u_int gs_grabhw; /* grab accelerator count */ u_int gs_seggt_fault; /* seggt faults */ u_int gs_seggtx_fault; /* seggtx faults */ u_int gs_seggta_fault; /* seggta faults */ u_int gs_cntxsave; /* graphics context saves */ u_int gs_cntxrestore; /* graphics context restores */ u_int gs_mapsteal; /* dvma map steals */ u_int gs_scheduler; /* scheduler wakeups */ u_int gs_threadswitch; /* scheduler thread switches */ u_int gs_kmcb_cmd; /* kernel kmcb cmd count */ u_int gs_freeze_atu; /* freeze atu count */ } gtstat; #endif GT_DEBUG static struct fbgattr gt_attr; static struct fbgattr gt_attrdefault = { FBTYPE_SUNGT, /* real type */ 0, /* owner */ /* fbtype struct init */ { FBTYPE_SUNGT, 1024, 1280, 8, 256, 0xa00000 }, /* fbsattr struct init */ { 0, /* flags */ -1, /*emu type */ /* dev_specific */ { 0, 0, 0, 0, 0, 0, 0, 0 } }, /* emu_types */ { FBTYPE_SUNGT, FBTYPE_SUN2BW, -1, -1 } }; /* * Determine whether a GT exists at the given address. */ gt_identify(name) char *name; { if (strcmp(name, "gt") == 0) return (++ngt); else return (0); } gt_attach(devi) struct dev_info *devi; { struct gt_softc *softc; addr_t reg, regs; int gt_bustype; static int unit; addr_t *vp; struct gt_map *mp; struct gt_map *lastmap = >_map[MAP_NMAP]; /* * We currently support only one GT on a workstation */ if (unit > 0) return (-1); #ifdef sun4c /* * If the system is a sparcstation 2, set the MICRO_TLB bit * in the system enable register. This is a workaround for * bug 1045662. */ if (cpu == CPU_SUN4C_75) on_enablereg(1); #endif softc = >_softc[unit]; /* * Grab properties from the GT OpenBoot PROM */ softc->w = getprop(devi->devi_nodeid, "width", 1280); softc->h = getprop(devi->devi_nodeid, "height", 1024); gt_promrev = getlongprop(devi->devi_nodeid, "promrev"); /* * Map in the PROM section (1 page) to calculate the * baseaddr of the device and unmap it when done. */ regs = devi->devi_reg[0].reg_addr; gt_bustype = devi->devi_reg[0].reg_bustype; if (!(reg = map_regs(regs, (u_int) NBPG, gt_bustype))) return (-1); softc->baseaddr = (u_long) fbgetpage(reg); unmap_regs((addr_t) reg, (u_int) NBPG); /* * Establish kernel mappings to the various GT sections. * Verify that the hardware responds to being probed. */ for (mp=gt_map, vp=softc->va; mp < lastmap; mp++, vp++) { if (mp->flags & GTM_K) { if (!(*vp = map_regs(regs + mp->poffset, mp->size, gt_bustype))) { --vp; --mp; goto err; } } } devi->devi_unit = unit; gt_priority = ipltospl(devi->devi_intr[0].int_pri); addintr(devi->devi_intr[0].int_pri, gtpoll, devi->devi_name, unit); /* save back pointer to softc */ devi->devi_data = (addr_t) softc; /* Initialize all of the software data structures */ softc->fe_ctrl_sp_ptr = (fe_ctrl_sp *)softc->va[MAP_FE_CTRL]; gt_shared_init(softc, (u_int)0); /* * Save context for reuse when window system shuts down. * This is pretty ugly, but we need to turn off the fe_hold * bit in the saved context. */ gt_cntxsave(softc, &softc->cntxt); softc->cntxt.gc_fe_hold = 0; /* yuck */ #ifdef sun4m /* * sun4m: * * Set the IOMMU Bypass bit in the SBus Slot Configuration * register. This is more properly done in the device's OpenBoot * PROM but the GT predated sun4m. If/when the GT OpenBoot PROM * is revved, this function should migrate to there. * * Set 32-byte burst transfer in, of all places, the HSA's * diagnostic register. This seems *essential* for healthy * running on Viking platforms. */ { u_int slot, regp, val, configbits; extern struct modinfo mod_info[]; #define BY 0x00000001 /* IOMMU Bypass */ #define CP 0x00008000 /* Cacheable */ #define HSA_OF 0x0084f000 /* HSA Diagnostic register */ #define HSA_32B 0x30002 /* Enable 32-byte burst read/write */ if ((mod_info[0].mod_type == CPU_VIKING) || (mod_info[0].mod_type == CPU_VIKING_MXCC)) { configbits = CP|BY; } else { configbits = BY; } slot = (u_int)(regs) >> 28; regp = 0xe0001010 + 4*slot; val = gt_ldsbusreg(regp); val |= configbits; (void) gt_stsbusreg(regp, val); if (!(reg = map_regs(regs + HSA_OF, (u_int)NBPG, gt_bustype))) { printf("gt_attach: cannot map HSA diag register\n"); return (-1); } *reg |= HSA_32B; unmap_regs((addr_t) reg, (u_int) NBPG); } #endif /* sun4m */ /* prepare for next attach */ unit++; report_dev(devi); return (0); /* * probe error: unmap anything already mapped. */ err: while (vp >= softc->va) { if (vp) unmap_regs(*vp, mp->size); vp--; mp--; } return (-1); } gtopen(dev, flag) dev_t dev; int flag; { struct gt_softc *softc = >_softc[minor(dev)>>1]; dev_t device; unsigned port; int rc; /* * Make a pseudo-device number for fbopen(). * Each physical GT device occupies two * minor-device slots. */ device = makedev(major(dev), minor(dev)>>1); rc = fbopen(device, flag, ngt); if (rc == 0) { if (minor(dev) & GT_ACCEL_PORT) port = (unsigned) F_ACCELOPEN; else port = (unsigned) F_DIRECTOPEN; gt_set_flags(&softc->flags, port); if (gta_initflag == (u_char)0) { gta_init(); gta_initflag = 1; } } return (rc); } /*ARGSUSED*/ gtclose(dev, flag) dev_t dev; int flag; { struct gt_softc *softc = >_softc[minor(dev)>>1]; unsigned port; if (minor(dev) & GT_ACCEL_PORT) port = (unsigned) F_ACCELOPEN; else port = (unsigned) F_DIRECTOPEN; gt_clear_flags(&softc->flags, port); if (!(softc->flags & F_GTOPEN)) { gt_shared_init(softc, (u_int)(F_UCODE_RUNNING | F_VRT_RETRACE | F_GAMMALOADED | F_DEGAMMALOADED | F_GAMMASET | F_VASCO)); /* * Restore the original graphics context */ gt_cntxrestore(softc, &softc->cntxt); /* * XXX: Kill the GT daemons * if (gta_initflag) { * gta_initflag = (u_char)0; * } */ } } /*ARGSUSED*/ gtmmap(dev, off, prot) dev_t dev; off_t off; int prot; { struct gt_softc *softc = >_softc[minor(dev)>>1]; struct gt_map *mp; /* * Mapping requests for a diagnostic process don't * require protection checking or offset translation. */ if (gt_diag_check(softc)) return (GT_ADDR(off)); /* * bwtwo emulation mode? */ if (softc->gattr->sattr.emu_type == FBTYPE_SUN2BW) { /* * resolve only for cursor data plane */ return (GT_ADDR(off+FB_CD_OF)); } /* * All other requests are validated and translated. */ for (mp=gt_map; mp < >_map[MAP_NMAP]; mp++) { if (off >= (off_t)mp->vaddr && off < (off_t)(mp->vaddr+mp->size)) { if ((mp->flags & GTM_P) && !(suser() || gt_server_check(softc))) { return (-1); } return (GT_ADDR(mp->poffset + (off - mp->vaddr))); } } return (-1); } /*ARGSUSED*/ gtioctl(dev, cmd, data, flag) dev_t dev; int cmd; caddr_t data; int flag; { struct gt_softc *softc = >_softc[minor(dev)>>1]; #ifdef GT_DEBUG gtstat.gs_ioctl++; #endif GT_DEBUG switch (cmd) { case FBIOPUTCMAP: return (gt_fbioputcmap(softc, data)); case FBIOGETCMAP: return (gt_fbiogetcmap(softc, data)); case FBIOGTYPE: return (gt_fbiogtype(softc, data)); #if NWIN > 0 case FBIOGPIXRECT: return (gt_fbiogpixrect(dev, softc, data)); #endif NWIN > 0 case FBIOSVIDEO: return (gt_fbiosvideo(softc, data)); case FBIOGVIDEO: return (gt_fbiogvideo(softc, data)); case FBIOVERTICAL: return (gt_fbiovertical(softc, data)); case FBIO_WID_ALLOC: return (gt_widalloc(softc, data)); case FBIO_WID_FREE: return (gt_widfree(softc, data)); case FBIO_WID_GET: return (gt_widread(softc, data)); case FBIO_WID_PUT: return (gt_widpost(softc, data)); case FB_CLUTALLOC: return (gt_clutalloc(softc, data)); case FB_CLUTFREE: return (gt_clutfree(softc, data)); case FB_CLUTREAD: return (gt_clutread(softc, data)); case FB_CLUTPOST: return (gt_clutpost(softc, data)); case FB_FCSALLOC: return (gt_fcsalloc(softc, data)); case FB_FCSFREE: return (gt_fcsfree(softc, data)); case FB_SETSERVER: return (gt_setserver(softc)); case FB_SETDIAGMODE: return (gt_setdiagmode(softc)); case FB_SETWPART: return (gt_setwpart(softc, data)); case FB_GETWPART: return (gt_getwpart(softc, data)); case FB_SETMONITOR: return (gt_setmonitor(softc, data)); case FB_GETMONITOR: return (gt_getmonitor(softc, data)); case FB_LOADKMCB: return (gt_loadkmcb(softc)); case FB_CONNECT: return (gt_connect(softc, data, dev)); case FB_DISCONNECT: return (gt_disconnect(softc)); case FB_GRABHW: return (gt_grabhw(softc, data)); case FB_UNGRABHW: return (gt_ungrabhw(softc)); case FB_VMBACK: return (gt_vmback(data)); case FB_VMUNBACK: return (gt_vmunback(data)); case FB_SETCLUTPART: return (gt_setclutpart(softc, data)); case FB_GETCLUTPART: return (gt_getclutpart(softc, data)); case FB_LIGHTPENENABLE: return (gt_lightpenenable(softc, data)); case FB_SETLIGHTPENPARAM: return (gt_setlightpenparam(softc, data)); case FB_GETLIGHTPENPARAM: return (gt_getlightpenparam(softc, data)); case FB_VMCTL: return (gt_vmctl(softc, data)); case FB_SETGAMMA: return (gt_setgamma(softc, data)); case FB_GETGAMMA: return (gt_getgamma(softc, data)); case FBIOSATTR: return (gt_fbiosattr(softc, data)); case FBIOGATTR: return (gt_fbiogattr(softc, data)); case FB_GT_SETVERSION: return (gt_sgversion(softc, data, 0)); case FB_GT_GETVERSION: return (gt_sgversion(softc, data, 1)); default: return (ENOTTY); } } /* * Called from the level 1 interrupt handler. * * The stuff about peeking at the HISR rather than simply reading * it outright is because the GT may be unresponsive to bus requests * and we want to avoid kernel bus timeouts. * XXX: Remove the peek kludgery if/when it is outdated. */ #define MAXPEEKS 5 /* try peeking at HISR this many times */ #define PEEKINTERVAL 1000 /* spin loop 1 msec between peeks */ gtpoll() { struct gt_softc *softc; fe_ctrl_sp *fep; fe_i860_sp *fe; unsigned intr_type, lcount; int serviced = 0; for (softc = gt_softc; softc < >_softc[NGT]; softc++) { fep = softc->fe_ctrl_sp_ptr; fe = (fe_i860_sp *)softc->va[MAP_FE_I860]; if (fep) { /* * Is this device interrupting? */ for (lcount=0;;) { if (peekl((long *) &fep->fe_hisr, (long *) &intr_type) == 0) break; #ifdef GT_DEBUG gtstat.gs_pollerrs++; #endif GT_DEBUG if (++lcount == MAXPEEKS) { panic("gtpoll: can't rd hisr"); } DELAY(PEEKINTERVAL); } intr_type = intr_type & HISR_INTMASK; if (intr_type || (fe->hoint_stat & FE_LBTO)) { gtintr(softc, intr_type); serviced++; } } } return (serviced); } static void gtintr(softc, intr_type) struct gt_softc *softc; u_int intr_type; { fe_ctrl_sp *fep = softc->fe_ctrl_sp_ptr; fe_i860_sp *fe = (fe_i860_sp *)softc->va[MAP_FE_I860]; fe_page_1 *fp1 = (fe_page_1 *) softc->va[MAP_FE_HFLAG1]; struct gt_fault *gfp = >_fault; #ifdef GT_DEBUG gtstat.gs_intr++; #endif GT_DEBUG /* * Bus-related error? */ if (intr_type & (HISR_MSTO|HISR_BERR|HISR_SIZE)) { if (intr_type & HISR_MSTO) { /* XXX: needs work */ gt_status_dump(softc, DUMP_GT_REGS); printf("gtintr: master timeout, par = 0x%X\n", fep->fe_par); } if (intr_type & HISR_BERR) { /* XXX: needs work */ printf("gtintr: master bus error, par = 0x%X\n", fep->fe_par); gt_status_dump(softc, DUMP_GT_REGS); panic("gt master bus error"); } if (intr_type & HISR_SIZE) { /* XXX: needs work */ printf("gt interrupt: slave size error\n"); } fep->fe_hisr &= ~(HISR_MSTO|HISR_BERR|HISR_SIZE); gt_sync = fep->fe_hisr; } /* * How about a local-bus error? * XXX: find a better way to handle this */ if (fe->hoint_stat & FE_LBTO) { printf("gtintr: local bus timeout error, lb_err_addr: 0x%X\n", fe->lb_err_addr); gt_status_dump(softc, DUMP_GT_REGS); panic("gtintr: local bus timeout error"); } /* * ATU interrupt */ if (intr_type & HISR_ATU) { if (intr_type & HISR_ROOT_INV) { gt_status_dump(softc, DUMP_MIA_REGS); panic("gtintr invalid root ptr"); } else if (intr_type & HISR_LVL1_INV) { gtintr_lvl1(softc); } else if (intr_type & HISR_TLB_MISS) { gtintr_tlb(softc); } } /* * kernel VCR interrupt: handle the interrupt and * clear the GT's kernel VCR and interrupt condition. */ if (intr_type & HISR_KVCR) { u_int status; if (fep->fe_dflag_0 == 0) { #ifdef GT_DEBUG printf("gtintr: spurious kvcr interrupt\n"); #endif GT_DEBUG fep->fe_hisr &= ~HISR_KVCR; gt_sync = fep->fe_hisr; } else { #ifdef GT_DEBUG gtstat.gs_intr_kvcr++; #endif GT_DEBUG gtintr_kmcb(softc); status = softc->kmcb.status; fep->fe_dflag_0 = 0; gt_sync = fep->fe_dflag_0; fep->fe_hisr &= ~HISR_KVCR; gt_sync = fep->fe_hisr; if (status & HKKVS_DONE_WITH_REQUEST) wakeup(&softc->wchan[WCHAN_SCHED]); } } /* * User VCR interrupt: Clear GT's user VCR and the interrupt * condition, and ask the pagein daemon to signal the user. */ if (intr_type & HISR_UVCR) { if (fp1->fe_dflag_1 == 0) { #ifdef GT_DEBUG printf("gtintr: spurious uvcr interrupt\n"); #endif GT_DEBUG fep->fe_hisr &= ~HISR_UVCR; gt_sync = fep->fe_hisr; } else if (softc->at_curr != softc->at_head) { #ifdef GT_DEBUG gtstat.gs_intr_uvcr++; #endif GT_DEBUG gfp->gf_type |= GF_SENDSIGNAL; gfp->gf_softc = softc; gfp->gf_at = softc->at_curr; gfp->gf_pid = softc->at_curr->ap_proc->p_pid; softc->at_curr->ap_flags |= GTA_SIGNAL; gt_freeze_atu(softc, F_FREEZE_SIG); fp1->fe_dflag_1 = 0; gt_sync = fp1->fe_dflag_1; fep->fe_hisr &= ~HISR_UVCR; gt_sync = fep->fe_hisr; wakeup((caddr_t) >_daemonflag); } else { fp1->fe_dflag_1 = 0; gt_sync = fp1->fe_dflag_1; fep->fe_hisr &= ~HISR_UVCR; gt_sync = fep->fe_hisr; } } } /* * A level 1 page table invalid condition can only legitimately * occur if the appropriate level 2 page table hasn't yet been * been allocated. */ static gtintr_lvl1(softc) struct gt_softc *softc; { fe_ctrl_sp *fep = softc->fe_ctrl_sp_ptr; fe_page_1 *fp1 = (fe_page_1 *)softc->va[MAP_FE_HFLAG1]; struct gt_fault *gfp = >_fault; #ifdef GT_DEBUG gtstat.gs_intr_lvl1++; #endif GT_DEBUG /* * Freeze the GT's ATU */ gt_freeze_atu(softc, F_FREEZE_LVL1); /* * Form the index into the level 1 page table and the * pointers to the PTP and its virtual address. */ if (!(softc->flags & F_THREAD_RUNNING)) { gfp->gf_type |= (GF_LVL1_FAULT | GF_BOGUS); } else { gfp->gf_type |= GF_LVL1_FAULT; gfp->gf_softc = softc; gfp->gf_at = softc->at_curr; gfp->gf_pid = softc->at_curr->ap_proc->p_pid; gfp->gf_lvl1 = (struct gt_lvl1 *)softc->at_curr->ap_lvl1; gfp->gf_index = (fp1->fe_rootindex & INDX1_MASK) >> 2; gfp->gf_ptp = &(gfp->gf_lvl1->gt_ptp[gfp->gf_index]); } /* * Clear the interrupt condition and wake the pagein daemon. */ softc->flags |= F_LVL1_MISS; fep->fe_hisr &= ~(HISR_ATU|HISR_LVL1_INV|HISR_TLB_MISS); gt_sync = fep->fe_hisr; wakeup((caddr_t) >_daemonflag); } static gtintr_tlb(softc) struct gt_softc *softc; { fe_ctrl_sp *fep = softc->fe_ctrl_sp_ptr; fe_page_1 *fp1 = (fe_page_1 *)softc->va[MAP_FE_HFLAG1]; struct gt_fault *gfp = >_fault; addr_t vaddr; #ifdef GT_DEBUG gtstat.gs_intr_tlb++; #endif GT_DEBUG /* * Freeze the GT's ATU. The thaw will be done by the pagein daemon. */ gt_freeze_atu(softc, F_FREEZE_TLB); /* * Reconstruct the virtual address. */ vaddr = (addr_t) ((fp1->fe_rootindex & INDX1_MASK) << INDX1_SHIFT | (fep->fe_lvl1index & INDX2_MASK) << INDX2_SHIFT | (fep->fe_par & PGOFF_MASK)); if (!(softc->flags & F_THREAD_RUNNING)) { gfp->gf_type |= (GF_LVL2_FAULT | GF_BOGUS); } else { gfp->gf_type |= GF_LVL2_FAULT; gfp->gf_softc = softc; gfp->gf_at = softc->at_curr; gfp->gf_pid = softc->at_curr->ap_proc->p_pid; gfp->gf_addr = vaddr; } /* * Clear the interrupt condition and wake the pagein daemon. * XXX: Here we could use the access type, currently unavailable */ softc->flags |= F_ATU_FAULTING; fep->fe_hisr &= ~(HISR_ATU|HISR_TLB_MISS); gt_sync = fep->fe_hisr; wakeup((caddr_t) >_daemonflag); } u_int prev_button_state = 0xffffffff; int prev_x_loc = -1; int prev_y_loc = -1; static gtintr_kmcb(softc) struct gt_softc *softc; { Hkvc_kmcb *kmcb = &softc->kmcb; if (kmcb->status & HKKVS_INSTRUCTION_TRAP) { /* XXX: */ printf("gtintr: unexpected trap to kernel\n"); } if (kmcb->status & HKKVS_ERROR_CONDITION) { /* XXX: */ printf("gtintr: gt error %d\n", kmcb->errorcode); } if (kmcb->status & HKKVS_PRINT_STRING) { static int partial_line = 0; char *printbuf = (char *)softc->printbuf; if (partial_line == 0) printf("gt%d: ", softc-gt_softc); partial_line = (printbuf[strlen(printbuf)-1] == '\n') ? 0:1; printf("%s", softc->printbuf); } if (kmcb->status & (HKKVS_LIGHT_PEN_NO_HIT | HKKVS_LIGHT_PEN_BUTTON_UP | HKKVS_LIGHT_PEN_BUTTON_DOWN | HKKVS_LIGHT_PEN_HIT )) { u_int lightpen_value = 0; u_int current_button_state = prev_button_state; int current_x_loc = prev_x_loc; int current_y_loc = prev_y_loc; #ifdef GT_DEBUG gtstat.gs_intr_lightpen++; #endif GT_DEBUG if (kmcb->status & HKKVS_LIGHT_PEN_BUTTON_DOWN) current_button_state = LIGHTPEN_BUTTON_DOWN; if (kmcb->status & HKKVS_LIGHT_PEN_BUTTON_UP) current_button_state = LIGHTPEN_BUTTON_UP; if (kmcb->status & HKKVS_LIGHT_PEN_HIT) { current_x_loc = kmcb->light_pen_hit_x; current_y_loc = kmcb->light_pen_hit_y; } if (current_button_state == prev_button_state) { /* Button State is same */ if (current_button_state == LIGHTPEN_BUTTON_UP){ if ((prev_x_loc != current_x_loc) || (prev_y_loc != current_y_loc)) { lightpen_value = (LIGHTPEN_BUTTON_UP << LIGHTPEN_SHIFT_BUTTON) | (current_y_loc << LIGHTPEN_SHIFT_Y) | current_x_loc; (void) store_lightpen_events( LIGHTPEN_MOVE, lightpen_value); } } else { if ((prev_x_loc != current_x_loc) || (prev_y_loc != current_y_loc)) { lightpen_value = (LIGHTPEN_BUTTON_DOWN << LIGHTPEN_SHIFT_BUTTON) | (current_y_loc << LIGHTPEN_SHIFT_Y) | current_x_loc; (void) store_lightpen_events( LIGHTPEN_DRAG, lightpen_value); } } } else { /* Button State is not equal */ (void) store_lightpen_events(LIGHTPEN_BUTTON, current_button_state); if ((prev_x_loc != current_x_loc) || (prev_y_loc != current_y_loc)) { if (current_button_state == LIGHTPEN_BUTTON_UP){ lightpen_value = (LIGHTPEN_BUTTON_UP << LIGHTPEN_SHIFT_BUTTON) | (current_y_loc << LIGHTPEN_SHIFT_Y) | current_x_loc; (void) store_lightpen_events( LIGHTPEN_MOVE, lightpen_value); } else { lightpen_value = (LIGHTPEN_BUTTON_DOWN << LIGHTPEN_SHIFT_BUTTON) | (current_y_loc << LIGHTPEN_SHIFT_Y) | current_x_loc; (void) store_lightpen_events( LIGHTPEN_DRAG, lightpen_value); } } } prev_button_state = current_button_state; prev_x_loc = current_x_loc; prev_y_loc = current_y_loc; /* Check for lightpen NO HIT for a few frames */ if (kmcb->status & HKKVS_LIGHT_PEN_NO_HIT) (void) store_lightpen_events(LIGHTPEN_UNDETECT, 0); } if (kmcb->status & HKKVS_VERTICAL_RETRACE) { if (softc->flags & F_VRT_RETRACE) { softc->flags &= ~F_VRT_RETRACE; wakeup(&softc->wchan[WCHAN_VRT]); } } } /* * Initialize the global data structures that * are associated with shared resource management */ static void gt_shared_init(softc, keepmask) struct gt_softc *softc; u_int keepmask; { struct proc_ll_type *rptr; struct shar_res_type *tsr_ptr; u_int i, j; u_char *printbuf_start, *printbuf_end, *cp; /* Initialize the WID tables */ for (i=0; iwid_otable[i].tt_flags = 0; softc->wid_otable[i].tt_refcount = 0; softc->wid_itable[i].tt_flags = 0; softc->wid_itable[i].tt_refcount = 0; } /* Initialize the 8 Bit CLUT table */ for (i=0; iclut_table[i].tt_flags = 0; softc->clut_table[i].tt_refcount = 0; } /* Initialize the FCS CLUT table */ for (i=0; ifcs_table[i].tt_flags = 0; softc->fcs_table[i].tt_refcount = 0; } /* Free up any proc list elements left*/ if (softc->proc_list != NULL) { while (softc->proc_list->plt_next != softc->proc_list) { rptr = softc->proc_list->plt_next; gt_link_detach(softc->proc_list->plt_next); /* * Now deallocate if any shared resource struct * is attached to this proc struct */ while (rptr->plt_sr != NULL) { tsr_ptr = rptr->plt_sr; rptr->plt_sr = tsr_ptr->srt_next; kmem_free((caddr_t)tsr_ptr, sizeof (*tsr_ptr)); } kmem_free((caddr_t)rptr, sizeof (*rptr)); } /* * Now deallocate if any shared resource struct * is attached to this proc struct */ while (softc->proc_list->plt_sr != NULL) { tsr_ptr = softc->proc_list->plt_sr; softc->proc_list->plt_sr = tsr_ptr->srt_next; kmem_free((caddr_t)tsr_ptr, sizeof (*tsr_ptr)); } /* Now deallocate the first element also */ kmem_free((caddr_t)softc->proc_list, sizeof (struct proc_ll_type)); softc->proc_list = NULL; } /* * Set up all the default values in the softc struct */ softc->flags &= keepmask; softc->server_proc = (short) 0; softc->diag_proc = (short) 0; for (i=0; i < MAX_DIAG_PROC; i++) softc->diag_proc_table[i] = 0; softc->wid_ocount = GT_DEFAULT_OWID_COUNT; softc->wid_icount = GT_DEFAULT_IWID_COUNT; softc->clut_part_total = GT_DEFAULT_SERVER_CLUT_PART; softc->clut_part_used = 0; softc->light_pen_enable = 0; softc->light_pen_distance = 0; softc->light_pen_time = 0; /* Initialize all the shadow tables */ for (i=0; i < NWID; i++) { softc->shadow_owlut[i] = 0; softc->shadow_iwlut[i] = 0; } for (i=0; i < 2; i++) { for (j=0; j < 3; j++) softc->shadow_cursor[i][j] = 0; } /* * initialize the kmcb queue and the accelerator-thread list */ GT_LIST_INIT(>_kmcbhead, gt_kmcblist, q_list); if (softc->at_head == (struct at_proc *)NULL) { softc->at_head = (struct at_proc *) new_kmem_zalloc(sizeof (struct at_proc), KMEM_SLEEP); GT_LIST_INIT(softc->at_head, at_proc, ap_list); softc->at_curr = softc->at_head; softc->at_head->ap_flags |= GTA_VIRGIN; } /* * Initialize the kmcb printbuf pointer. This is only done * when keepmask is zero, indicating that we are starting * up the system, rather than cleaning up after close. This * distinction is necessary because at close time the process * in which this code is being run may have already lost its * address space and the gt_setnocache() call would then panic * the kernel. */ if (keepmask == (u_int)0) { softc->printbuf = gt_printbuffer; /* * Disable caching for the GT print buffer */ printbuf_start = (u_char *)softc->printbuf; printbuf_end = (u_char *) (printbuf_start + gt_printbuffer_size); for (cp=printbuf_start; cpowner = NULL; } /* * Check for server privilege. */ static gt_server_check(softc) struct gt_softc *softc; { if (u.u_procp->p_pid == softc->server_proc) return (1); else return (0); } /* * Check for diagnostic privilege. */ static gt_diag_check(softc) struct gt_softc *softc; { int i; if (softc->diag_proc) { for (i = 0; i < MAX_DIAG_PROC; i++) { if (softc->diag_proc_table[i] == u.u_procp->p_pid) return (1); } return (0); } return (0); } /* * Copy the user data into a fb_clut structure and * call gt_clutpost() to do the real work. * * Provided for backwards compatibility. */ static gt_fbioputcmap(softc, data) struct gt_softc *softc; caddr_t data; { struct fbcmap *cmap = (struct fbcmap *) data; struct fb_clut mcmap; mcmap.index = 0; /* For backward compatibility */ mcmap.offset = cmap->index; mcmap.count = cmap->count; mcmap.red = cmap->red; mcmap.green = cmap->green; mcmap.blue = cmap->blue; return (gt_clutpost(softc, (caddr_t) &mcmap)); } /* * Copy the user data into a fb_clut structure and * call gt_clutread() to do the job. * * Provided for backwards compatibility. */ static gt_fbiogetcmap(softc, data) struct gt_softc *softc; caddr_t data; { struct fbcmap *cmap = (struct fbcmap *) data; struct fb_clut mcmap; mcmap.index = 0; /* For backward compatibility */ mcmap.offset = cmap->index; mcmap.count = cmap->count; mcmap.red = cmap->red; mcmap.green = cmap->green; mcmap.blue = cmap->blue; return (gt_clutread(softc, (caddr_t) &mcmap)); } static gt_fbiogtype(softc, data) struct gt_softc *softc; caddr_t data; { struct fbtype *fb = (struct fbtype *) data; /* set owner if not owned or if prev, owner is dead */ if (softc->owner == NULL || softc->owner->p_stat == NULL || (int) softc->owner->p_pid != softc->gattr->owner) { gt_set_gattr_fbtype(softc); } if (softc->gattr->sattr.emu_type == FBTYPE_SUN2BW){ fb->fb_type = FBTYPE_SUN2BW; fb->fb_height = softc->h; fb->fb_width = 2048; fb->fb_depth = 1; fb->fb_size = (softc->h * 2048) / 8; fb->fb_cmsize = 2; } else *fb = gt_monitor[softc->monitor].gtm_fbtype; return (0); } #if NWIN > 0 static gt_fbiogpixrect(dev, softc, data) dev_t dev; struct gt_softc *softc; caddr_t data; { struct fbpixrect *fbpix = (struct fbpixrect *) data; struct gt_fb *fbp; Pixrect *kerpr; static int i; static u_int pmask; static u_char red[2], green[2], blue[2]; struct fb_clut fbclut; static int group[] = { PIXPG_24BIT_COLOR, PIXPG_WID, PIXPG_CURSOR, PIXPG_CURSOR_ENABLE, PIXPG_8BIT_COLOR, PIXPG_ZBUF }; static int fdepth[] = { GT_DEPTH_24, GT_DEPTH_WID, GT_DEPTH_1, GT_DEPTH_1, GT_DEPTH_8, GT_DEPTH_24 }; /* * Populate the pixrect */ fbpix->fbpr_pixrect = &softc->pr; softc->pr.pr_size.x = softc->w; softc->pr.pr_size.y = softc->h; softc->pr.pr_ops = &ops_v; softc->pr.pr_data = (caddr_t) & softc->gtd; for (fbp = softc->gtd.fb, i = 0; i < GT_NFBS; i++, fbp++) { fbp->group = group[i]; fbp->depth = fdepth[i]; fbp->mprp.mpr.md_linebytes = mpr_linebytes (2048, fdepth[i]); fbp->mprp.mpr.md_offset.x = 0; fbp->mprp.mpr.md_offset.y = 0; fbp->mprp.mpr.md_primary = 0; if (fdepth[i] == GT_DEPTH_24) fbp->mprp.planes = 0x00ffffff; else fbp->mprp.planes = (1 << fbp->depth) - 1; fbp->mprp.mpr.md_flags = fbp->mprp.planes != 0 ? MP_DISPLAY | MP_PLANEMASK : MP_DISPLAY; } softc->gtd.fd = minor(dev) >> 1; softc->gtd.flag = 0; softc->gtd.gt_rp = (H_rp*)softc->va[MAP_RP_HOST_CTRL]; softc->gtd.gt_fbi = (H_fbi *)softc->va[MAP_FBI_REG]; softc->gtd.gt_fbi_go = (H_fbi_go *)softc->va[MAP_FBI_PFGO]; softc->gtd.gt_fe_h = (H_fe_hold *)softc->va[MAP_FE_I860]; /* * NULL all pointers to fb except cursor planes .RIGHT??? ROP?? */ kerpr = &softc->pr; /* * XXX - Even though this is a *pixrect* call, it seems to be * invoked by OpenWindows, hence this test. * * Set the WID partition if we aren't running the OW server. */ if (softc->server_proc == (short)0) { pmask = 5; /* set the wid partition to 5-5 */ if (gt_setwpart(softc, (caddr_t) &pmask)) printf("gt_fbiogpixrect: Error in set wid partition\n"); } gt_d(kerpr)->wid_part = 5; /* * Set up the cursor colormap - only done once */ red[0] = 255; green[0] = 255; blue[0] = 255; red[1] = 0; green[1] = 0; blue[1] = 0; fbclut.flags |= FB_KERNEL; fbclut.count = 2; fbclut.offset = 0; fbclut.red = red; fbclut.green = green; fbclut.blue = blue; fbclut.index = GT_CLUT_INDEX_CURSOR; if (gt_clutpost(softc, (caddr_t) &fbclut)) printf("gt_fbiogpixrect: Error in cursor clut post\n"); /* * Set up frame buffer pointers */ pmask = PIX_GROUP(PIXPG_24BIT_COLOR) | 0x00ffffff; (void) gt_putattributes(kerpr, &pmask); gt_d(kerpr)->fb[0].mprp.mpr.md_image = (short *)0; pmask = PIX_GROUP(PIXPG_WID); (void) gt_putattributes(kerpr, &pmask); gt_d(kerpr)->fb[1].mprp.mpr.md_image = (short *)0; pmask = PIX_GROUP(PIXPG_8BIT_COLOR); (void) gt_putattributes(kerpr, &pmask); gt_d(kerpr)->fb[4].mprp.mpr.md_image = (short *)0; pmask = PIX_GROUP(PIXPG_ZBUF) | 0x00ffffff; (void) gt_putattributes(kerpr, &pmask); gt_d(kerpr)->fb[5].mprp.mpr.md_image = (short *)0; pmask = PIX_GROUP(PIXPG_CURSOR_ENABLE); (void) gt_putattributes(kerpr, &pmask); gt_d(kerpr)->fb[3].mprp.mpr.md_image = gt_d(kerpr)->mprp.mpr.md_image = (short *)softc->va[MAP_FB_CE]; if (softc->gattr->sattr.emu_type != FBTYPE_SUN2BW){ (void) gt_rop(kerpr, 0, 0, softc->pr.pr_size.x, softc->pr.pr_size.y, PIX_CLR, (Pixrect *)0, 0, 0); } pmask = PIX_GROUP(PIXPG_CURSOR); (void) gt_putattributes(kerpr, &pmask); gt_d(kerpr)->fb[2].mprp.mpr.md_image = gt_d(kerpr)->mprp.mpr.md_image = (short *)softc->va[MAP_FB_CD]; if (softc->gattr->sattr.emu_type != FBTYPE_SUN2BW){ (void) gt_rop(kerpr, 0, 0 ,softc->pr.pr_size.x, softc->pr.pr_size.y, PIX_CLR, (Pixrect *)0, 0, 0); } return (0); } #endif NWIN > 0 static gt_fbiogvideo(softc, data) struct gt_softc *softc; caddr_t data; { u_int *user_ptr; fbo_screen_ctrl *fbo_regs; int rc; user_ptr = (u_int *) data; fbo_regs = (fbo_screen_ctrl *) softc->va[MAP_FBO_SCREEN_CTRL]; #ifdef MULTIPROCESSOR xc_attention(); /* Prevent users from busying the PF */ #endif MULTIPROCESSOR if (!gt_check_pf_busy(softc)) { if (fbo_regs->fbo_videomode_0 & VIDEO_ENABLE) *user_ptr = FBVIDEO_ON; else *user_ptr = FBVIDEO_OFF; rc = 0; } else rc = EFAULT; #ifdef MULTIPROCESSOR xc_dismissed(); #endif MULTIPROCESSOR return (rc); } static gt_fbiosvideo(softc, data) struct gt_softc *softc; caddr_t data; { fbo_screen_ctrl *fbo_regs = (fbo_screen_ctrl *) softc->va[MAP_FBO_SCREEN_CTRL]; int rc = 0; #ifdef MULTIPROCESSOR xc_attention(); /* Prevent users from busying the PF */ #endif MULTIPROCESSOR if (!gt_check_pf_busy(softc)) { switch (*(u_int *) data) { case FBVIDEO_ON: fbo_regs->fbo_videomode_0 |= VIDEO_ENABLE; break; case FBVIDEO_OFF: fbo_regs->fbo_videomode_0 &= ~VIDEO_ENABLE; break; default: rc = EINVAL; break; } } else rc = EFAULT; #ifdef MULTIPROCESSOR xc_dismissed(); #endif MULTIPROCESSOR return (rc); } /* * If this is a sleep-until request, check to see that vertical * retrace interrupts are enabled, enabling them if necessary, * and go to sleep. Otherwise, disable vertical retrace interrupts. */ static gt_fbiovertical(softc, data) struct gt_softc *softc; caddr_t data; { u_int cmd; if (*(int *)data == 1) { if ((softc->flags&F_VRT_RETRACE) == 0) { gt_set_flags(&softc->flags, F_VRT_RETRACE); cmd = HKKVC_ENABLE_VRT_INTERRUPT; if (gt_post_command(softc, cmd, (Hkvc_kmcb *)0)) return (EINVAL); } (void) sleep(&softc->wchan[WCHAN_VRT], PZERO+1); return (0); } else if (*(int *)data == 0) { gt_clear_flags(&softc->flags, F_VRT_RETRACE); cmd = HKKVC_DISABLE_VRT_INTERRUPT; if (gt_post_command(softc, cmd, (Hkvc_kmcb *)0)) return (EINVAL); else return (0); } else return (EINVAL); } /* * allocate wids */ static gt_widalloc(softc, data) struct gt_softc *softc; caddr_t data; { struct fb_wid_alloc *wptr = (struct fb_wid_alloc *) data; u_int type = wptr->wa_type; u_int count = wptr->wa_count; int base, i, j; switch (type) { case FB_WID_SHARED_8: base = GT_DEFAULT_SUNVIEW_OWID_INDEX; softc->wid_otable[base].tt_flags |= TT_USED; softc->wid_otable[base].tt_refcount++; gt_alloc_sr(softc, u.u_procp->p_pid, RT_OWID, base, 1); break; case FB_WID_SHARED_24: base = GT_DEFAULT_SUNVIEW_IWID_INDEX; softc->wid_itable[base].tt_flags |= TT_USED; softc->wid_itable[base].tt_refcount++; gt_alloc_sr(softc, u.u_procp->p_pid, RT_IWID, base, 1); break; case FB_WID_DBL_8: base = gt_calc_wid(softc, FB_WID_DBL_8, count); if (base == -1) return (EINVAL); for (j=0,i=base; jwid_otable[i].tt_flags |= TT_USED; softc->wid_otable[i].tt_refcount++; } gt_alloc_sr(softc, u.u_procp->p_pid, RT_OWID, base, count); break; case FB_WID_DBL_24: base = gt_calc_wid(softc, FB_WID_DBL_24, count); if (base == -1) return (EINVAL); for (j=0,i=base; jwid_itable[i].tt_flags |= TT_USED; softc->wid_itable[i].tt_refcount++; } gt_alloc_sr(softc, u.u_procp->p_pid, RT_IWID, base, count); break; default: return (EINVAL); } wptr->wa_index = base; return (0); } static gt_calc_wid(softc, type, count) struct gt_softc *softc; u_int type; u_int count; { int i, j, k, found, power_val; switch (type) { case FB_WID_DBL_8: if (count > softc->wid_ocount) return (-1); if ((power_val = gt_calc_power(count)) == -1) { return (-1); } else { for (k=1; k < softc->wid_ocount; k += power_val) { found = 1; for (j=0,i=k; jwid_otable[i].tt_flags & TT_USED) { found = 0; break; } } if (found) { return (k); } } } return (-1); case FB_WID_DBL_24: if (count > softc->wid_icount) return (-1); if ((power_val = gt_calc_power(count)) == -1) { return (-1); } else { for (k=0; k < softc->wid_icount; k += power_val) { found = 1; for (j=0,i=k; jwid_itable[i].tt_flags & TT_USED) { found = 0; break; } } if (found) { return (k); } } } return (-1); default: return (-1); } } static gt_calc_power(count) u_int count; { int i; for (i=0; i= count) return (gt_power_table[i]); return (-1); } /* * Free specified wids */ static gt_widfree(softc, data) struct gt_softc *softc; caddr_t data; { struct fb_wid_alloc *wlutp = (struct fb_wid_alloc *) data; int index = wlutp->wa_index; u_int type = wlutp->wa_type; u_int count = wlutp->wa_count; u_int sr_found = 0; struct shar_res_type *srptr, *prev; struct proc_ll_type *found; struct proc_ll_type *gt_find_proc_link(); enum res_type rtype; switch (type) { case FB_WID_SHARED_8: case FB_WID_DBL_8: if (index < 0 || index > (int)softc->wid_ocount) return (EINVAL); rtype = RT_OWID; break; case FB_WID_SHARED_24: case FB_WID_DBL_24: if (index < 0 || index > (int)softc->wid_icount) return (EINVAL); rtype = RT_IWID; break; default: return (EINVAL); } found = gt_find_proc_link(softc->proc_list, u.u_procp->p_pid); if (found == NULL) return (EINVAL); prev = srptr = found->plt_sr; while (srptr != NULL && !(sr_found)) { if (srptr->srt_type == rtype && srptr->srt_base == index) sr_found = 1; else { prev = srptr; srptr = srptr->srt_next; } } if (!sr_found) return (EINVAL); if (count == srptr->srt_count) { gt_decrement_refcount(softc, rtype, index, count); if (srptr == found->plt_sr) found->plt_sr = srptr->srt_next; else prev->srt_next = srptr->srt_next; kmem_free((caddr_t)srptr, sizeof (*srptr)); } else { /* XXX: Handle this case later * For now we should not come here * We cannot delete the sr struct now because there * is more in that struct */ printf("Inside gt_widfree wrong number of counts \n"); } return (0); } /* * Read a wid */ /*ARGSUSED*/ static gt_widread(softc, data) struct gt_softc *softc; caddr_t data; { /* * GT Front-end Microcode does not provide a mechanism to read * the WLUT's, therefore this ioctl is not implemented. So far * all the application programs that need this ioctl have a * workaround for this ioctl. * * XXX: When the Front-end Microcode provides the mechanism then * this ioctl has to be implemented. */ return (ENOTTY); } /* * Wake processes sleeping on one of the GT wchans */ gt_timeout(wchan) caddr_t wchan; { wakeup(wchan); } /* * A kmcb post hasn't completed. We interpret this as indicating * that the GT frontend has stopped responding. We'll dequeue all * outstanding kmcb requests and reset the frontend. */ gt_kmcbtimeout(arg) caddr_t arg; { struct gt_softc *softc = (struct gt_softc *)arg; int s; printf("the GT's accelerator port is unresponsive\n"); printf("and is being shut down.\n"); s = splr(gt_priority); softc->fe_ctrl_sp_ptr->fe_hcr = FE_RESET; softc->fe_ctrl_sp_ptr->fe_hcr &= ~FE_RESET; gt_sync = softc->fe_ctrl_sp_ptr->fe_hcr; gt_set_flags(&softc->flags, F_UCODE_STOPPED); gt_clear_flags(&softc->flags, F_ATU_FROZEN); wakeup(&softc->wchan[WCHAN_SCHED]); wakeup(&softc->wchan[WCHAN_THAW]); (void) splx(s); } /* * Posts wids */ static gt_widpost(softc, data) struct gt_softc *softc; caddr_t data; { struct fb_wid_list *pwl = (struct fb_wid_list *) data; struct Gt_kernel_wlut_update *wlut_post = &(softc->wlut_post); struct Gt_wlut_update *pwu; struct fb_wid_item *pwi, *tmp; struct Hkvc_kmcb proto_kmcb; u_int nbytes; int i, count, ret_code; unsigned widtype; u_int cmd; #ifdef sun4m u_int xc_attflag = 0; #endif sun4m #ifdef GT_DEBUG gtstat.gs_widpost++; #endif GT_DEBUG /* * Validate the request. Kernel WLUT updates are not supported. */ if (pwl->wl_count==0 || !pwl->wl_list || pwl->wl_count>NWIDPOST || (pwl->wl_flags&FB_KERNEL)) return (EINVAL); nbytes = pwl->wl_count * sizeof (struct fb_wid_item); ret_code = 0; /* * Block if there is a WLUT post in progress */ while (softc->flags & F_WLUT_PEND) (void) sleep(&softc->wchan[WCHAN_WLUT], PZERO+1); gt_set_flags(&softc->flags, F_WLUT_PEND); tmp = (struct fb_wid_item *) new_kmem_zalloc(nbytes, KMEM_SLEEP); if (copyin((caddr_t) pwl->wl_list, (caddr_t) tmp, nbytes)) { kmem_free((caddr_t)tmp, nbytes); ret_code = EFAULT; goto out; } /* * Now construct a table */ widtype = tmp->wi_type; pwu = softc->gt_wlut_update; pwi = tmp; for (count = 0; count < pwl->wl_count; count++) { if (widtype != pwi->wi_type) { kmem_free((caddr_t)tmp, nbytes); ret_code = EINVAL; goto out; } pwu->entry = (int) pwi->wi_index; pwu->mask = (int) pwi->wi_attrs; pwu->image_buffer = (Gt_buffer) pwi->wi_values[0]; pwu->overlay_buffer = (Gt_buffer) pwi->wi_values[1]; pwu->plane_group = (Gt_plane_group) pwi->wi_values[2]; pwu->clut = (int) pwi->wi_values[3]; pwu->fast_clear_set = (int) pwi->wi_values[4]; } /* Done with the temporary buffer */ kmem_free((caddr_t)tmp, nbytes); if (widtype==FB_WID_SHARED_8 || widtype==FB_WID_DBL_8) wlut_post->table = 1; else wlut_post->table = 0; wlut_post->num_entries = pwl->wl_count; wlut_post->upd = (Gt_wlut_update *)GT_KVTOP((int)softc->gt_wlut_update); /* * Use the RP to post WLUT entries if we can. Otherwise * we'll have to post the entries directly into the FB, * taking a performance hit along the way. */ if (softc->flags & F_UCODE_RUNNING) { cmd = HKKVC_UPDATE_WLUT | HKKVC_PAUSE_WITH_INTERRUPT; proto_kmcb.wlut_info = (Gt_kernel_wlut_update *)GT_KVTOP((int) wlut_post); if (gt_post_command(softc, cmd, &proto_kmcb)) { ret_code = EINVAL; goto out; } /* * Does the caller want to block until the post completes? * We can synchronize by flushing the RP, via gta_idle(), * and when we get the interrupt indicating completion of * the RP flush, we can poll for the COPY_REQUEST bit to * be reset. */ if (pwl->wl_flags & FB_BLOCK) { fbo_wlut_ctrl *fbo_csr = (fbo_wlut_ctrl *) softc->va[MAP_FBO_WLUT_CTRL]; /* * Idle the accelerator */ gta_idle(softc, F_WLUT_HOLD); #ifdef MULTIPROCESSOR xc_attention(); /* Keeps users from the PF */ xc_attflag = 1; #endif MULTIPROCESSOR /* * Check for PF not busy */ if (gt_check_pf_busy(softc)) { ret_code = EFAULT; goto out; } /* wait for the previous wlut post to finish */ CDELAY(!((fbo_csr->wlut_csr0 & FBO_ENABLE_COPY_REQUEST) || (fbo_csr->wlut_csr1 & FBO_ENABLE_COPY_REQUEST)), GT_SPIN_LUT); if ((fbo_csr->wlut_csr0 & FBO_ENABLE_COPY_REQUEST) || (fbo_csr->wlut_csr1 & FBO_ENABLE_COPY_REQUEST)) { printf("WLUT_CSR is BUSY reset hardware\n"); ret_code = EFAULT; goto out; } gt_clear_flags(&softc->flags, F_WLUT_HOLD); wakeup(&softc->wchan[WCHAN_SCHED]); } } else { /* * Can't use the RP for the post. Do it directly. */ struct Gt_wlut_update *wu; fbo_wlut_ctrl *fbo_csr = (fbo_wlut_ctrl *) softc->va[MAP_FBO_WLUT_CTRL]; #ifdef MULTIPROCESSOR /* Prevent user processes from busying the PF. */ xc_attention(); /* Keeps users from the PF */ xc_attflag = 1; #endif MULTIPROCESSOR /* * Check for PF not busy */ if (gt_check_pf_busy(softc)) { ret_code = EFAULT; goto out; } /* wait for the previous wlut post to finish */ CDELAY(!((fbo_csr->wlut_csr0 & FBO_ENABLE_COPY_REQUEST) || (fbo_csr->wlut_csr1 & FBO_ENABLE_COPY_REQUEST)), GT_SPIN_LUT); if ((fbo_csr->wlut_csr0 & FBO_ENABLE_COPY_REQUEST) || (fbo_csr->wlut_csr1 & FBO_ENABLE_COPY_REQUEST)) { printf("WLUT_CSR is BUSY reset hardware\n"); ret_code = EINVAL; goto out; } /* First copy the entries to shadow */ wu = softc->gt_wlut_update; for (i=0; iwl_count; i++, wu++) { ret_code = gt_direct_wlut_update(softc, wu, wlut_post->table); if (ret_code) { goto out; } } /* Now post it to hardware */ fbo_csr->wlut_csr0 |= FBO_ENABLE_COPY_REQUEST | FBO_ENABLE_ALL_INTERLEAVE; gt_sync = fbo_csr->wlut_csr0; if (pwl->wl_flags & FB_BLOCK) { /* process wants to sleep until this happens */ /* wait for the previous wlut post to finish */ DELAY(100); /* * Check for PF not busy */ if (gt_check_pf_busy(softc)) { ret_code = EFAULT; goto out; } CDELAY(!((fbo_csr->wlut_csr0 & FBO_ENABLE_COPY_REQUEST) || (fbo_csr->wlut_csr1 & FBO_ENABLE_COPY_REQUEST)), GT_SPIN_LUT); if ((fbo_csr->wlut_csr0 & FBO_ENABLE_COPY_REQUEST) || (fbo_csr->wlut_csr1 & FBO_ENABLE_COPY_REQUEST)) { printf("WLUT_CSR is BUSY reset hardware\n"); ret_code = EINVAL; } } } out: #ifdef MULTIPROCESSOR if (xc_attflag) { xc_dismissed(); } #endif MULTIPROCESSOR /* * Wakeup processes waiting for posting WLUTs */ gta_run(softc, F_WLUT_HOLD); gt_clear_flags(&softc->flags, F_WLUT_PEND); wakeup(&softc->wchan[WCHAN_WLUT]); return (ret_code); } /* * Post a WLUT entry directly to the FB. * This is only used if the GT FE has died. */ static gt_direct_wlut_update(softc, wu, flag) struct gt_softc *softc; Gt_wlut_update *wu; int flag; { u_int table_entry, *wlut_addr, i; u_char *temp_wlut_addr; if (!(wu->mask & (HK_WLUT_MASK_IMAGE | HK_WLUT_MASK_OVERLAY | HK_WLUT_MASK_PLANE_GROUP | HK_WLUT_MASK_CLUT | HK_WLUT_MASK_FCS))) { gt_clear_flags(&softc->flags, F_WLUT_PEND); wakeup(&softc->wchan[WCHAN_WLUT]); return (EINVAL); } if (flag) table_entry = softc->shadow_owlut[wu->entry]; else table_entry = softc->shadow_iwlut[wu->entry]; if (wu->mask & HK_WLUT_MASK_IMAGE) { table_entry &= ~HKFBO_WLUT_IMAGE_BUFFER; if (wu->image_buffer !=0) table_entry |= HKFBO_WLUT_IMAGE_BUFFER; } if (wu->mask & HK_WLUT_MASK_OVERLAY) { table_entry &= ~HKFBO_WLUT_OV_BUFFER; if (wu->overlay_buffer != 0) table_entry |= HKFBO_WLUT_OV_BUFFER; } if (wu->mask & HK_WLUT_MASK_PLANE_GROUP) { table_entry &= ~HKFBO_WLUT_COLOR_MODE; switch (wu->plane_group) { case HK_8BIT_RED: table_entry |= HKFBO_WLUT_CM_8RED; break; case HK_8BIT_GREEN: table_entry |= HKFBO_WLUT_CM_8GREEN; break; case HK_8BIT_BLUE: table_entry |= HKFBO_WLUT_CM_8BLUE; break; case HK_8BIT_OVERLAY: table_entry |= HKFBO_WLUT_CM_8OVERLAY; break; case HK_12BIT_LOWER: table_entry |= HKFBO_WLUT_CM_12LOW; break; case HK_12BIT_UPPER: table_entry |= HKFBO_WLUT_CM_12HIGH; break; case HK_24BIT: table_entry |= HKFBO_WLUT_CM_24BIT; break; default: break; } } if (wu->mask & HK_WLUT_MASK_CLUT) { table_entry &= ~HKFBO_WLUT_CLUT_SELECT; table_entry |= (wu->clut << HKFBO_WLUT_CLUT_SEL_SHIFT) & HKFBO_WLUT_CLUT_SELECT; } if (wu->mask & HK_WLUT_MASK_FCS) { table_entry &= ~HKFBO_WLUT_FCS_SELECT; table_entry |= (wu->fast_clear_set << HKFBO_WLUT_FCS_SEL_SHIFT) & HKFBO_WLUT_FCS_SELECT; } if (flag) { /* overlay */ /* Note: No HDTV alternate WLUT yet XXX */ softc->shadow_owlut[wu->entry] = table_entry; temp_wlut_addr = (u_char *) softc->va[MAP_FBO_WLUT_MAP] + (wu->entry * HKFBO_WLUT_ENTRY_OFFSET * softc->wid_icount); wlut_addr = (u_int *) temp_wlut_addr; for (i=0; iwid_icount; i++, wlut_addr++) *wlut_addr = table_entry; } else { /* image */ softc->shadow_iwlut[wu->entry] = table_entry; temp_wlut_addr = (u_char *) softc->va[MAP_FBO_WLUT_MAP] + (wu->entry * HKFBO_WLUT_ENTRY_OFFSET); wlut_addr = (u_int *)temp_wlut_addr; *wlut_addr = table_entry; } gt_sync = (u_int) *wlut_addr; return (0); } /* * Allocate CLUTs */ static gt_clutalloc(softc, data) struct gt_softc *softc; caddr_t data; { struct fb_clutalloc *clutp = (struct fb_clutalloc *) data; int flags = clutp->flags; int clutindex = clutp->clutindex; int index = -1; int i; switch (flags) { case CLUT_8BIT: if (gt_server_check(softc)) { if (softc->clut_part_used == softc->clut_part_total) return (ENOMEM); softc->clut_part_used++; i = GT_DEFAULT_SUNVIEW_CLUT8; } else i = GT_DEFAULT_SUNVIEW_CLUT8 + 1; for (; i< NCLUT_8; i++) { if (softc->clut_table[i].tt_flags & TT_USED) continue; else { index = i; break; } } if (index == -1) return (ENOMEM); /* * Found a CLUT that we can use. Mark it used * and return its index to the user. */ softc->clut_table[index].tt_flags |= TT_USED; softc->clut_table[index].tt_refcount++; gt_alloc_sr(softc, u.u_procp->p_pid, RT_CLUT8, index, 1); clutp->index = index; break; case CLUT_SHARED: if (clutindex < 0 || clutindex > NCLUT_8 -1) return (EINVAL); if (softc->clut_table[clutindex].tt_flags & TT_USED) { softc->clut_table[clutindex].tt_refcount++; gt_alloc_sr(softc, u.u_procp->p_pid, RT_CLUT8, clutindex, 1); } else { softc->clut_table[clutindex].tt_flags |= TT_USED; softc->clut_table[clutindex].tt_refcount++; gt_alloc_sr(softc, u.u_procp->p_pid, RT_CLUT8, clutindex, 1); } /* * return the index to the user */ clutp->index = clutindex; break; default: return (EINVAL); } return (0); } /* * Free CLUTs */ static gt_clutfree(softc, data) struct gt_softc *softc; caddr_t data; { struct fb_clutalloc *clutp = (struct fb_clutalloc *) data; int index = (int) clutp->index; u_int sr_found = 0; struct shar_res_type *srptr, *prev; struct proc_ll_type *found; struct proc_ll_type *gt_find_proc_link(); found = gt_find_proc_link(softc->proc_list, u.u_procp->p_pid); if (found == NULL) return (EINVAL); prev = srptr = found->plt_sr; while (srptr != NULL && !(sr_found)) { if (srptr->srt_type == RT_CLUT8 && srptr->srt_base == index) { sr_found = 1; } else { prev = srptr; srptr = srptr->srt_next; } } if (!sr_found) return (EINVAL); gt_decrement_refcount(softc, RT_CLUT8, index, 1); if (gt_server_check(softc)) softc->clut_part_used--; if (srptr == found->plt_sr) found->plt_sr = srptr->srt_next; else prev->srt_next = srptr->srt_next; kmem_free((caddr_t)srptr, sizeof (*srptr)); return (0); } /* * Read CLUTs */ static gt_clutread(softc, data) struct gt_softc *softc; caddr_t data; { fbo_clut_ctrl *fbo_csr = (fbo_clut_ctrl *)softc->va[MAP_FBO_CLUT_CTRL]; fbo_clut_map *fbo_map = (fbo_clut_map *)softc->va[MAP_FBO_CLUT_MAP]; struct fb_clut *clutp = (struct fb_clut *) data; u_int count = (u_int)clutp->count; u_int offset = (u_int)clutp->offset; u_int flags = (u_int)clutp->flags; int clut_id = (u_int)clutp->index; u_int regamma = 0; u_int ret_code = 0; u_char red, green, blue; u_int i, limit, color, *colorp; u_char tmp[3][GT_CMAP_SIZE]; if (!clutp->red || !clutp->green || !clutp->blue) return (EINVAL); if (clut_id < 0 || clut_id > NCLUT) return (EINVAL); limit = count + offset; if (clut_id <= GT_CLUT_INDEX_24BIT && (limit==0) || (limit>CLUT24_SIZE)) return (EINVAL); if (softc->flags & F_UCODE_RUNNING) { /* * Idle the accelerator. * Flush the RP then read the shadow CLUT directly */ gta_idle(softc, F_CLUT_HOLD); } #ifdef MULTIPROCESSOR xc_attention(); /* Prevent users from busying the PF */ #endif MULTIPROCESSOR /* * Check for PF not busy */ if (gt_check_pf_busy(softc)) { ret_code = EFAULT; goto out; } /* wait for the previous clut post to finish */ CDELAY(!((fbo_csr->clut_csr0 & FBO_ENABLE_COPY_REQUEST) || (fbo_csr->clut_csr1 & FBO_ENABLE_COPY_REQUEST)), GT_SPIN_LUT); if ((fbo_csr->clut_csr0 & FBO_ENABLE_COPY_REQUEST) || (fbo_csr->clut_csr1 & FBO_ENABLE_COPY_REQUEST)) { printf("CLUT_CSR is BUSY reset hardware\n"); ret_code = EFAULT; goto out; } /* * Copy the current info from the shadow CLUTs. 8-bit indexed * colormaps are conditionally regammified, depending upon the * requestor's preference or the global state. */ if (clut_id <= GT_CLUT_INDEX_24BIT) { if (clut_id == GT_CLUT_INDEX_24BIT) colorp = &fbo_map->fbo_clut24.clut24_map[offset]; else { colorp = &fbo_map->fbo_clut8[clut_id].clut8_map[offset]; if (softc->flags&F_DEGAMMALOADED) { if (flags & FB_DEGAMMA) regamma = 1; else if (flags & FB_NO_DEGAMMA) regamma = 0; else regamma = (softc->flags&F_VASCO) ? 1:0; } else regamma = 0; } for (i=0; i> 8; blue = (color & CLUT_BLUEMASK) >> 16; if (regamma) { red = softc->gamma[red]; green = softc->gamma[green]; blue = softc->gamma[blue]; } tmp[0][i] = red; tmp[1][i] = green; tmp[2][i] = blue; } } else { /* * XXX: If any user wants read back Cursor or Gamma * lookup tables, that code has to go here */ ret_code = EINVAL; goto out; } /* Now copy it to the user space */ if (copyout((caddr_t)tmp[0], (caddr_t)clutp->red, count) || copyout((caddr_t)tmp[1], (caddr_t)clutp->green, count) || copyout((caddr_t)tmp[2], (caddr_t)clutp->blue, count)) { ret_code = EFAULT; goto out; } out: #ifdef MULTIPROCESSOR xc_dismissed(); #endif MULTIPROCESSOR gta_run(softc, F_CLUT_HOLD); return (ret_code); } /* * Post CLUTs */ static gt_clutpost(softc, data) struct gt_softc *softc; caddr_t data; { struct fb_clut *clutp = (struct fb_clut *) data; u_int count = (u_int)clutp->count; u_int offset = (u_int)clutp->offset; u_int flags = (u_int)clutp->flags; int clut_id = (u_int)clutp->index; struct Gt_clut_update *clut_post; Hkvc_kmcb proto_kmcb; unsigned *cmap, *color; u_int cmd, limit, i, j; int degamma = 0; int ret_code = 0; #ifdef sun4m int xc_attflag = 0; #endif sun4m u_char tmp[3][GT_CMAP_SIZE]; #ifdef GT_DEBUG gtstat.gs_clutpost++; #endif GT_DEBUG /* * if kernel is posting a CLUT then we don't have to check for lock. */ switch (clut_id) { case GT_CLUT_INDEX_FCS: /* disallow FCS CLUT posts */ case GT_CLUT_INDEX_12BIT: /* disallow 12bit CLUT posts */ return (EINVAL); case GT_CLUT_INDEX_GAMMA: return (gt_gamma_clutpost(softc, data)); case GT_CLUT_INDEX_CURSOR: return (gt_cursor_clutpost(softc, data)); case GT_CLUT_INDEX_DEGAMMA: return (gt_load_degamma(softc, data)); default: /* 8-bit or truecolor CLUT */ break; } if (!clutp->red || !clutp->green || !clutp->blue || clut_id < 0 || clut_id > NCLUT) return (EINVAL); /* * By the time we reach here, the clut post is to one of the * 256-entry CLUTs. Time for another sanity check. */ limit = offset + count; if (limit == 0 || limit > CLUT8_SIZE) return (EINVAL); /* * Block until there are no in-progress CLUT posts */ if (!(flags & FB_KERNEL)) { while (softc->flags & F_CLUT_PEND) (void) sleep(&softc->wchan[WCHAN_CLUT], PZERO+1); gt_set_flags(&softc->flags, F_CLUT_PEND); } /* * Copy the cmap from userspace or kernelspace to local buffer, * and then to the appropriate softc color map table. */ if (flags & FB_KERNEL) { cmap = softc->gt_sys_colors; clut_post = &softc->kernel_clut_post; bcopy((caddr_t) clutp->red, (caddr_t) tmp[0], count); bcopy((caddr_t) clutp->green, (caddr_t) tmp[1], count); bcopy((caddr_t) clutp->blue, (caddr_t) tmp[2], count); } else { cmap = softc->gt_user_colors; clut_post = &softc->user_clut_post; if (copyin((caddr_t) clutp->red, (caddr_t) tmp[0], count) || copyin((caddr_t) clutp->green, (caddr_t) tmp[1], count) || copyin((caddr_t) clutp->blue, (caddr_t) tmp[2], count)) { ret_code = EFAULT; goto out; } } /* * Is this an 8-bit indexed color map requesting degammifcation? */ if ((clut_idflags&F_DEGAMMALOADED)) { if (flags & FB_DEGAMMA) degamma = 1; else if (flags & FB_NO_DEGAMMA) degamma = 0; else degamma = (softc->flags&F_VASCO) ? 1:0; } else degamma = 0; color = cmap; for (i=0; idegamma[tmp[2][i]] << 16 | softc->degamma[tmp[1][i]] << 8 | softc->degamma[tmp[0][i]]; } } if (softc->flags & F_UCODE_RUNNING) { clut_post->start_entry = offset; clut_post->num_entries = count; clut_post->colors = (u_int *) GT_KVTOP((int) cmap); clut_post->table = clut_id; if (flags & FB_KERNEL) cmd = HKKVC_UPDATE_CLUT; else cmd = HKKVC_UPDATE_CLUT | HKKVC_PAUSE_WITH_INTERRUPT; proto_kmcb.clut_info = (Gt_clut_update *)GT_KVTOP((int) clut_post); if (gt_post_command(softc, cmd, &proto_kmcb)) { ret_code = EINVAL; goto out; } if ((!(flags & FB_KERNEL)) && (flags & FB_BLOCK)) { fbo_clut_ctrl *fbo_csr = (fbo_clut_ctrl *)softc->va[MAP_FBO_CLUT_CTRL]; /* * The process wants to block until the update * completes. */ gta_idle(softc, F_CLUT_HOLD); #ifdef MULTIPROCESSOR xc_attention(); /* Keeps users from the PF */ xc_attflag = 1; #endif MULTIPROCESSOR /* * Check for PF not busy */ if (gt_check_pf_busy(softc)) { ret_code = EFAULT; goto out; } /* wait for the previous clut post to finish */ CDELAY(!((fbo_csr->clut_csr0 & FBO_ENABLE_COPY_REQUEST) || (fbo_csr->clut_csr1 & FBO_ENABLE_COPY_REQUEST)), GT_SPIN_LUT); if ((fbo_csr->clut_csr0 & FBO_ENABLE_COPY_REQUEST) || (fbo_csr->clut_csr1 & FBO_ENABLE_COPY_REQUEST)) { printf("CLUT_CSR is BUSY reset hardware\n"); ret_code = EFAULT; goto out; } gt_clear_flags(&softc->flags, F_CLUT_HOLD); wakeup(&softc->wchan[WCHAN_SCHED]); } } else { /* * FE microcode is not running so * kernel has to post this CLUT */ fbo_clut_ctrl *fbo_csr = (fbo_clut_ctrl *)softc->va[MAP_FBO_CLUT_CTRL]; fbo_clut_map *fbo_map = (fbo_clut_map *)softc->va[MAP_FBO_CLUT_MAP]; #ifdef MULTIPROCESSOR xc_attention(); /* Prevent users from busying the PF */ xc_attflag = 1; #endif MULTIPROCESSOR /* * Check for PF not busy */ if (gt_check_pf_busy(softc)) { ret_code = EFAULT; goto out; } /* wait for the previous clut post to finish */ CDELAY(!((fbo_csr->clut_csr0 & FBO_ENABLE_COPY_REQUEST) || (fbo_csr->clut_csr1 & FBO_ENABLE_COPY_REQUEST)), GT_SPIN_LUT); if ((fbo_csr->clut_csr0 & FBO_ENABLE_COPY_REQUEST) || (fbo_csr->clut_csr1 & FBO_ENABLE_COPY_REQUEST)) { printf("CLUT_CSR is BUSY reset hardware\n"); ret_code = EINVAL; goto out; } /* copy all the info to the shadow table */ if (clut_id < 14) { /* overlay clut */ for (i=0,j=offset; i< count; i++,j++) if (flags & FB_KERNEL) fbo_map->fbo_clut8[clut_id].clut8_map[j] = softc->gt_sys_colors[i]; else fbo_map->fbo_clut8[clut_id].clut8_map[j] = softc->gt_user_colors[i]; } else { for (i=0,j=offset; i < count; i++,j++) if (flags & FB_KERNEL) fbo_map->fbo_clut24.clut24_map[j] = softc->gt_sys_colors[i]; else fbo_map->fbo_clut24.clut24_map[j] = softc->gt_user_colors[i]; } fbo_csr->clut_csr0 |= (FBO_ENABLE_COPY_REQUEST | FBO_ENABLE_ALL_INTERLEAVE); gt_sync = fbo_csr->clut_csr0; if (flags & FB_BLOCK) { /* wait for the previous clut post to finish */ DELAY(100); /* * Check for PF not busy */ if (gt_check_pf_busy(softc)) { ret_code = EFAULT; goto out; } CDELAY(!((fbo_csr->clut_csr0 & FBO_ENABLE_COPY_REQUEST) || (fbo_csr->clut_csr1 & FBO_ENABLE_COPY_REQUEST)), GT_SPIN_LUT); if ((fbo_csr->clut_csr0 & FBO_ENABLE_COPY_REQUEST) || (fbo_csr->clut_csr1 & FBO_ENABLE_COPY_REQUEST)) { printf("CLUT_CSR is BUSY reset hardware\n"); ret_code = EINVAL; goto out; } } } out: #ifdef MULTIPROCESSOR if (xc_attflag) { xc_dismissed(); } #endif MULTIPROCESSOR /* * Wake processes that are waiting for posting CLUTs */ if (!(flags & FB_KERNEL)) { gta_run(softc, F_CLUT_HOLD); gt_clear_flags(&softc->flags, F_CLUT_PEND); wakeup(&softc->wchan[WCHAN_CLUT]); } return (ret_code); } /* * Post the gamma palette */ static gt_gamma_clutpost(softc, data) struct gt_softc *softc; caddr_t data; { struct fb_clut *clutp = (struct fb_clut *) data; u_int count = (u_int)clutp->count; u_int offset = (u_int)clutp->offset; u_int flags = (u_int)clutp->flags; u_char tmp[3][GT_CMAP_SIZE]; u_int i, j; fbo_ramdac_map *ramdac = (fbo_ramdac_map *)softc->va[MAP_FBO_RAMDAC_CTRL]; if (count < 1 || count > 256) return (EINVAL); if (flags & FB_KERNEL) { bcopy((caddr_t) clutp->red, (caddr_t) tmp[0], count); bcopy((caddr_t) clutp->green, (caddr_t) tmp[1], count); bcopy((caddr_t) clutp->blue, (caddr_t) tmp[2], count); } else { if (copyin((caddr_t) clutp->red, (caddr_t) tmp[0], count) || copyin((caddr_t) clutp->green, (caddr_t) tmp[1], count) || copyin((caddr_t) clutp->blue, (caddr_t) tmp[2], count)) return (EFAULT); } #ifdef MULTIPROCESSOR xc_attention(); /* Prevent users from busying the PF */ #endif MULTIPROCESSOR if (gt_check_pf_busy(softc)) { #ifdef MULTIPROCESSOR xc_dismissed(); #endif MULTIPROCESSOR return (EINVAL); } /* Change addr ptr to the starting offset */ ramdac->ramdac_addr_reg = offset; for (i=0; i < count; i++) { for (j=0; j < 3; j++) ramdac->ramdac_gamma_data_reg = tmp[j][i]; } #ifdef MULTIPROCESSOR xc_dismissed(); #endif MULTIPROCESSOR /* * Keep a (sigh) shadow of the gamma table so that we can * regamma degamma'd tables. Here we just collapse the * three channels into one, assuming them all to be identical. */ if ((count == GT_CMAP_SIZE) && gt_server_check(softc) || gt_diag_check(softc)) { bcopy((caddr_t)tmp[0], (caddr_t)softc->gamma, (u_int)GT_CMAP_SIZE); gt_set_flags(&softc->flags, F_GAMMALOADED); } return (0); } /* * Load a de-gamma table from the red colormap specified. We just * as easily could have taken the green or the blue; they should * all be identical. */ static gt_load_degamma(softc, data) struct gt_softc *softc; caddr_t data; { struct fb_clut *vasco = (struct fb_clut *) data; if (!gt_server_check(softc) && !gt_diag_check(softc)) return (EACCES); if (copyin((caddr_t)vasco->red, (caddr_t)softc->degamma, GT_CMAP_SIZE)) return (EFAULT); else { gt_set_flags(&softc->flags, F_DEGAMMALOADED); return (0); } } /* * Post the cursor palette */ static gt_cursor_clutpost(softc, data) struct gt_softc *softc; caddr_t data; { struct fb_clut *clutp=(struct fb_clut *) data; u_int count = (u_int)clutp->count; u_int offset = (u_int)clutp->offset; u_int flags = (u_int)clutp->flags; u_char tmp[3][GT_CMAP_SIZE]; u_int i, j; fbo_ramdac_map *ramdac = (fbo_ramdac_map *) softc->va[MAP_FBO_RAMDAC_CTRL]; if (offset != 0 && offset != 1) return (EINVAL); if (count < 1 || count > 2) return (EINVAL); if (flags & FB_KERNEL) { bcopy((caddr_t) clutp->red, (caddr_t) tmp[0], count); bcopy((caddr_t) clutp->green, (caddr_t) tmp[1], count); bcopy((caddr_t) clutp->blue, (caddr_t) tmp[2], count); } else { if (copyin((caddr_t) clutp->red, (caddr_t) tmp[0], count) || copyin((caddr_t) clutp->green, (caddr_t) tmp[1], count) || copyin((caddr_t) clutp->blue, (caddr_t) tmp[2], count)) return (EFAULT); } /* copy into shadow */ if (count == 2) { /* Posting both bg/fg */ for (i=0; i < 2; i++) { for (j=0; j < 3; j++) softc->shadow_cursor[j][i] = tmp[j][i]; } } else { if (offset) { /* Only fg color */ for (j=0; j < 3; j++) softc->shadow_cursor[j][1] = tmp[j][0]; } else { /* Only bg color */ for (j=0; j < 3; j++) softc->shadow_cursor[j][0] = tmp[j][0]; } } #ifdef MULTIPROCESSOR xc_attention(); /* Prevent users from busying the PF */ #endif MULTIPROCESSOR if (gt_check_pf_busy(softc)) return (EINVAL); /* * zero the addr ptr offset. always write all of the * cursor palette registers */ ramdac->ramdac_addr_reg = 0; /* zero out the transparent regs */ for (i=0; i < 6; i++) ramdac->ramdac_cursor_data_reg = 0; /* Load the cursor bg/fg color */ for (i=0; i < 2; i++) { for (j=0; j < 3; j++) { ramdac->ramdac_cursor_data_reg = softc->shadow_cursor[j][i]; } } #ifdef MULTIPROCESSOR xc_dismissed(); #endif MULTIPROCESSOR return (0); } /* * Post commands to the GT */ static gt_post_command(softc, cmd, proto) struct gt_softc *softc; u_int cmd; Hkvc_kmcb *proto; { struct gt_kmcblist *qptr; struct Hkvc_kmcb *kptr; /* * Allocate a kmcb request element */ qptr = (struct gt_kmcblist *)new_kmem_zalloc(sizeof (*qptr), KMEM_SLEEP); kptr = &qptr->q_mcb; /* * Transfer data from the prototype kmcb to the request element. */ kptr->command = cmd; if (cmd & (HKKVC_SET_IMAGE_WLUT_BITS | HKKVC_UPDATE_WLUT | HKKVC_UPDATE_CLUT | HKKVC_LOAD_USER_MCB | HKKVC_SET_FB_SIZE | HKKVC_SET_LIGHT_PEN_PARAMETERS | HKKVC_LOAD_CONTEXT)) { if (proto == (Hkvc_kmcb *) NULL) { printf("gt post kmcb: missing data\n"); kmem_free((caddr_t)qptr, sizeof (*qptr)); return (1); } kptr->wlut_image_bits = proto->wlut_image_bits; kptr->wlut_info = proto->wlut_info; kptr->clut_info = proto->clut_info; kptr->fb_width = proto->fb_width; kptr->screen_width = proto->screen_width; kptr->screen_height = proto->screen_height; kptr->light_pen_enable = proto->light_pen_enable; kptr->light_pen_distance = proto->light_pen_distance; kptr->light_pen_time = proto->light_pen_time; } /* * Enqueue the new request at the *end* of the queue * and wakeup the GT command daemon. */ GT_LIST_INSERT_PREV(>_kmcbhead, qptr, gt_kmcblist, q_list); wakeup(&softc->wchan[WCHAN_SCHED]); if (cmd & HKKVC_PAUSE_WITH_INTERRUPT) { qptr->q_flags |= KQ_BLOCK; (void) sleep((caddr_t)qptr, PZERO-1); } return (0); } /* * Allocate the FCS */ static gt_fcsalloc(softc, data) struct gt_softc *softc; caddr_t data; { struct fb_fcsalloc *fcsptr = (struct fb_fcsalloc *) data; struct table_type *tp = softc->fcs_table; int index; for (index=0; indextt_flags & TT_USED)) break; } if (index == NFCS) return (ENOMEM); tp->tt_flags |= TT_USED; tp->tt_refcount++; gt_alloc_sr(softc, u.u_procp->p_pid, RT_FCS, index, 1); fcsptr->index = index; return (0); } /* * Deallocate the FCS */ static gt_fcsfree(softc, data) struct gt_softc *softc; caddr_t data; { struct fb_fcsalloc *fcsptr = (struct fb_fcsalloc *) data; int index = (int) fcsptr->index; u_int sr_found = 0; struct proc_ll_type *found; struct shar_res_type *srptr, *prev; struct proc_ll_type *gt_find_proc_link(); found = gt_find_proc_link(softc->proc_list, u.u_procp->p_pid); if (found == NULL) return (EINVAL); prev = srptr = found->plt_sr; while (srptr != NULL && !(sr_found)) { if ((srptr->srt_type==RT_FCS) && (srptr->srt_base==index)) { sr_found = 1; } else { prev = srptr; srptr = srptr->srt_next; } } if (!sr_found) return (EINVAL); gt_decrement_refcount(softc, RT_FCS, index, 1); if (srptr == found->plt_sr) found->plt_sr = srptr->srt_next; else prev->srt_next = srptr->srt_next; kmem_free((caddr_t)srptr, sizeof (*srptr)); return (0); } /* * Give server-process privilege to the current process * * XXX: see security hole comment with gt_setdiagmode. */ static gt_setserver(softc) struct gt_softc *softc; { if (softc->server_proc != (short) 0) return (EINVAL); softc->server_proc = u.u_procp->p_pid; return (0); } static void gt_server_free(softc) struct gt_softc *softc; { softc->server_proc = (short) 0; } /* * Give the current process the GT diagnostic privilege. * * XXX: There is a bit of a security hole here: we identify the current * process as having diagnostic privilege by entering its pid in a table. * We clear that table entry when any address space segments that have been * set up by the GT segment driver are torn down. If the current process * exits without ever having set up any GT memory type 1 segments, its * pid won't be cleared from the table. The next process that gets assigned * that pid gets diagnostic privilege without even asking. * * The same type of hole exists for server privilege. */ static gt_setdiagmode(softc) struct gt_softc *softc; { int got_avail = 0; int i, avail_index; short pid; for (i=0; i < MAX_DIAG_PROC; i++) { pid = softc->diag_proc_table[i]; if (pid == (short)0) { got_avail = 1; avail_index = i; } else if (pid == u.u_procp->p_pid) { return (0); } } if (got_avail) { softc->diag_proc_table[avail_index] = u.u_procp->p_pid; softc->diag_proc++; return (0); } else return (EINVAL); } static void gt_diag_free(softc) struct gt_softc *softc; { int i; for (i=0; i < MAX_DIAG_PROC; i++) { if (softc->diag_proc_table[i] == u.u_procp->p_pid) { softc->diag_proc_table[i] = (short)0; softc->diag_proc--; } } } /* * Initialize the WID partition register */ static gt_setwpart(softc, data) struct gt_softc *softc; caddr_t data; { u_int no_of_bits = *(u_int *)data; Hkvc_kmcb proto_kmcb; u_int cmd; if (softc->server_proc == (short) 0 || gt_server_check(softc)) { if (no_of_bits > NWID_BITS) return (EINVAL); softc->wlut_wid_part = no_of_bits; softc->wid_ocount = gt_power_table[NWID_BITS - no_of_bits]; softc->wid_icount = gt_power_table[no_of_bits]; /* * Tell the FE about the new WID partition */ if (softc->flags & F_UCODE_RUNNING) { cmd = HKKVC_SET_IMAGE_WLUT_BITS | HKKVC_PAUSE_WITH_INTERRUPT; proto_kmcb.wlut_image_bits = no_of_bits; if (gt_post_command(softc, cmd, &proto_kmcb)) return (EINVAL); } } else { return (EINVAL); } return (0); } /* * Returns the value in the WID partition register */ static gt_getwpart(softc, data) struct gt_softc *softc; caddr_t data; { *(u_int *)data = softc->wlut_wid_part; return (0); } /* * Initialize the overlay clut partition */ static gt_setclutpart(softc, data) struct gt_softc *softc; caddr_t data; { u_int no_of_cluts = *(u_int *)data; if (!(gt_diag_check(softc))) return (EACCES); if (no_of_cluts>NCLUT_8) return (EINVAL); softc->clut_part_total = no_of_cluts; return (0); } /* * Read back the overlay clut partition */ static gt_getclutpart(softc, data) struct gt_softc *softc; caddr_t data; { if (!(gt_server_check(softc))) return (EACCES); *(u_int *)data = softc->clut_part_total; return (0); } /* * Enable/Disable lightpen */ static gt_lightpenenable(softc, data) struct gt_softc *softc; caddr_t data; { int enable = *(int *) data; u_int cmd; Hkvc_kmcb proto_kmcb; if (enable) softc->light_pen_enable = 1; else softc->light_pen_enable = 0; proto_kmcb.light_pen_enable = softc->light_pen_enable; proto_kmcb.light_pen_distance = softc->light_pen_distance; proto_kmcb.light_pen_time = softc->light_pen_time; cmd = HKKVC_SET_LIGHT_PEN_PARAMETERS | HKKVC_PAUSE_WITH_INTERRUPT; if (gt_post_command(softc, cmd, &proto_kmcb)) return (EFAULT); return (0); } /* * Initialize the lightpen parameters */ static gt_setlightpenparam(softc, data) struct gt_softc *softc; caddr_t data; { struct fb_lightpenparam *param = (struct fb_lightpenparam *) data; Hkvc_kmcb proto_kmcb; u_int cmd; if (param->pen_distance) softc->light_pen_distance = param->pen_distance; if (param->pen_time) softc->light_pen_time = param->pen_time; proto_kmcb.light_pen_enable = softc->light_pen_enable; proto_kmcb.light_pen_distance = softc->light_pen_distance; proto_kmcb.light_pen_time = softc->light_pen_time; cmd = HKKVC_SET_LIGHT_PEN_PARAMETERS | HKKVC_PAUSE_WITH_INTERRUPT; if (gt_post_command(softc, cmd, &proto_kmcb)) return (EFAULT); return (0); } /* * Readback the lightpen parameters */ static gt_getlightpenparam(softc, data) struct gt_softc *softc; caddr_t data; { struct fb_lightpenparam *param = (struct fb_lightpenparam *) data; param->pen_distance = softc->light_pen_distance; param->pen_time = softc->light_pen_time; return (0); } /* * Register the monitor-specific width, height and frame-buffer * width register value with the GT frontend. */ static gt_setmonitor(softc, data) struct gt_softc *softc; caddr_t data; { fbi_reg *fbi_regs = (fbi_reg *)softc->va[MAP_FBI_REG]; rp_host *rp_host_ptr = (rp_host *)softc->va[MAP_RP_HOST_CTRL]; int type = (int) *(int *)data; u_int cmd; Hkvc_kmcb proto_kmcb; if (!gt_diag_check(softc)) return (EACCES); if (type >= NGT_MONITORS) return (EINVAL); /* * ioctl overrides OpenBoot PROM's values */ if (type != -1) { struct gt_monitor *mp = >_monitor[type]; u_int host_stateset; softc->w = mp->gtm_fbtype.fb_width; softc->h = mp->gtm_fbtype.fb_height; softc->monitor = type; /* * Store override value into the FB width register */ host_stateset = rp_host_ptr->rp_host_csr_reg & 0x1; rp_host_ptr->rp_host_csr_reg |= 1; #ifdef MULTIPROCESSOR xc_attention(); /* Prevent users from busying the PF */ #endif MULTIPROCESSOR if (gt_check_pf_busy(softc)) { #ifdef MULTIPROCESSOR xc_dismissed(); #endif MULTIPROCESSOR return (EFAULT); } fbi_regs->fbi_reg_fb_width = mp->gtm_fbwidth; #ifdef MULTIPROCESSOR xc_dismissed(); #endif MULTIPROCESSOR if (host_stateset == 0) rp_host_ptr->rp_host_csr_reg &= ~0x1; } proto_kmcb.screen_width = softc->w; proto_kmcb.screen_height = softc->h; proto_kmcb.fb_width = gt_monitor[softc->monitor].gtm_fbwidth; /* * Now inform the FE microcode. */ cmd = HKKVC_SET_FB_SIZE | HKKVC_PAUSE_WITH_INTERRUPT; if (gt_post_command(softc, cmd, &proto_kmcb)) { return (EINVAL); } return (0); } /* * Fetch the monitor-type from the FB_WIDTH register in PP */ static gt_getmonitor(softc, data) struct gt_softc *softc; caddr_t data; { if (softc->monitor >= NGT_MONITORS) return (EINVAL); *(u_int *) data = gt_monitor[softc->monitor].gtm_fbwidth; return (0); } /* * Fetch or store the gamma value */ static gt_setgamma(softc, data) struct gt_softc *softc; caddr_t data; { struct fb_setgamma *fbs = (struct fb_setgamma *) data; if (gt_server_check(softc) || gt_diag_check(softc)) { softc->gammavalue = fbs->fbs_gamma; gt_set_flags(&softc->flags, F_GAMMASET); if (fbs->fbs_flag & FB_DEGAMMA) gt_set_flags(&softc->flags, F_VASCO); else gt_clear_flags(&softc->flags, F_VASCO); return (0); } else return (EACCES); } static gt_getgamma(softc, data) struct gt_softc *softc; caddr_t data; { struct fb_setgamma *fbs = (struct fb_setgamma *) data; if (softc->flags & F_GAMMASET) { fbs->fbs_gamma = softc->gammavalue; fbs->fbs_flag = (softc->flags&F_VASCO) ? FB_DEGAMMA:FB_NO_DEGAMMA; return (0); } else { fbs->fbs_gamma = (float) 0.0; fbs->fbs_flag = 0; return (EINVAL); } } static gt_sgversion(softc, data, flag) struct gt_softc *softc; caddr_t data; int flag; { struct gt_version *gtv = (struct gt_version *) data; if (flag == 0) { if (gt_diag_check(softc) == 0) return (EINVAL); else softc->gt_version = *gtv; } else { *gtv = softc->gt_version; } return (0); } /* * Set up bwtwo mode */ static gt_fbiosattr(softc, data) struct gt_softc *softc; caddr_t data; { struct fbsattr *sattr = (struct fbsattr *) data; softc->gattr->sattr.emu_type = sattr->emu_type; softc->gattr->sattr.flags = sattr->flags; if (softc->gattr->sattr.emu_type == FBTYPE_SUN2BW) { softc->gattr->fbtype.fb_type = FBTYPE_SUN2BW; softc->gattr->fbtype.fb_height = softc->h; softc->gattr->fbtype.fb_width = 2048; softc->gattr->fbtype.fb_depth = 1; softc->gattr->fbtype.fb_cmsize = 2; softc->gattr->fbtype.fb_size = (softc->h * 2048) / 8; } else { gt_set_gattr_fbtype(softc); } if (sattr->flags & FB_ATTR_DEVSPECIFIC) bcopy((char *) sattr->dev_specific, (char *) softc->gattr->sattr.dev_specific, sizeof(sattr->dev_specific)); /* under new ownership */ softc->owner = u.u_procp; softc->gattr->owner = (int) u.u_procp->p_pid; return (0); } /* * Return the frame buffer mode */ static gt_fbiogattr(softc, data) struct gt_softc *softc; caddr_t data; { struct fbgattr *gattr = (struct fbgattr *) data; /* * set owner if not owned or if previous owner is dead */ if (softc->owner == NULL || softc->owner->p_stat == NULL || (int) softc->owner->p_pid != softc->gattr->owner) { softc->owner = u.u_procp; softc->gattr->owner = (int) u.u_procp->p_pid; gt_set_gattr_fbtype(softc); } *gattr = *(softc->gattr); return (0); } static void gt_set_gattr_fbtype(softc) struct gt_softc *softc; { gt_attr = gt_attrdefault; gt_attr.fbtype = gt_monitor[softc->monitor].gtm_fbtype; softc->gattr = >_attr; } /* * Load the KMCB pointer into FE memory and initialize the FE. * Expected to be used only by gtconfig. */ static gt_loadkmcb(softc) struct gt_softc *softc; { fe_ctrl_sp *fep = softc->fe_ctrl_sp_ptr; Hkvc_kmcb *kmcbp = (Hkvc_kmcb *) &softc->kmcb; fe_i860_sp *fe_i860_regs = (fe_i860_sp *) softc->va[MAP_FE_I860]; /* * restrict this to a diagnostic-mode process */ if (gt_diag_check(softc) == 0) return (EINVAL); /* * We may be reloading code on a running system, so we need * to clean up accelerator resources here. */ gta_reset_accelerator(softc); gt_init_kmcb(softc); /* XXX: move this to general initialization */ /* * Store the kmcb`s physical address into the test register, * release the FE hold, and assert the FE go bit to start * the frontend executing. */ fep->fe_test_reg = (u_int)GT_KVTOP((int)kmcbp); fe_i860_regs->fe_hold &= ~FE_HOLD; if (fe_i860_regs->fe_hold & FE_HLDA) { printf("gt_loadkmcb error: ~FE_HOLD not acknowledged\n"); return (EINVAL); } fep->fe_hcr |= FE_GO; /* * Enable interrupts */ gt_enable_intr(softc, HISR_ENABMASK); /* * Solicit an interrupt from the frontend microcode. Having * gotten it, we shall assume that the frontend is initialized * and running. */ gt_set_flags(&softc->flags, F_UCODE_RUNNING); if (gt_post_command(softc, (u_int)HKKVC_PAUSE_WITH_INTERRUPT, (Hkvc_kmcb *)0)) { gt_clear_flags(&softc->flags, F_UCODE_RUNNING); return (EINVAL); } return (0); } /* * Initialize the kernel MCB structure. * Make the softc struct non-cacheable. * XXX: separate these two functions */ static void gt_init_kmcb(softc) struct gt_softc *softc; { Hkvc_kmcb *kmcb = (Hkvc_kmcb *) &softc->kmcb; u_char *cp, *lp; cp = (u_char *) ((u_int)softc & MMU_PAGEMASK); lp = (u_char *) softc + sizeof (struct gt_softc); for (; cp < lp; cp += MMU_PAGESIZE) gt_setnocache((addr_t)cp); cp = (u_char *) ((u_int)gt_printbuffer & MMU_PAGEMASK); lp = (u_char *) gt_printbuffer + gt_printbuffer_size; for (; cp < lp; cp += MMU_PAGESIZE) gt_setnocache((addr_t)cp); kmcb->magic = HK_KMCB_MAGIC; kmcb->version = HK_KMCB_VERSION; kmcb->print_buffer = (u_int *) GT_KVTOP((int) softc->printbuf); } /* * Register this process as an accelerator user. * * Create an at_proc struct for this process. Record the address * of the process's umcb and return the addresses of his uvcr and svcr. */ static gt_connect(softc, data, dev) struct gt_softc *softc; caddr_t data; dev_t dev; { struct gt_connect *iop = (struct gt_connect *) data; struct at_proc *ap; dev_t device; u_int vaddr; /* * Make a portless device number */ device = makedev(major(dev), minor(dev)>>1); if (!(softc->flags & F_UCODE_RUNNING)) return (EINVAL); /* * Ensure that this process hasn't already registered * itself as an accelerator user. */ if (gta_findproc(softc) != (struct at_proc *)NULL) return (EINVAL); /* * Create the at_proc struct and hang it on the list * of accelerator processes. */ ap = (struct at_proc *) new_kmem_zalloc(sizeof (*ap), KMEM_SLEEP); ap->ap_softc = softc; ap->ap_proc = u.u_procp; ap->ap_pid = u.u_procp->p_pid; ap->ap_as = u.u_procp->p_as; ap->ap_umcb = iop->gt_pmsgblk; GT_LIST_INIT(ap, at_proc, ap_list); ap->ap_lvl1 = (struct gt_lvl1 *) getpages(btopr(sizeof (struct gt_lvl1)), 0); gt_setnocache((addr_t)ap->ap_lvl1); bzero((caddr_t)ap->ap_lvl1, sizeof (struct gt_lvl1)); /* * A little paranoia here. Make sure the user's mcb * shows the GT as user-stopped. */ (void) suword((caddr_t) &ap->ap_umcb->gt_stopped, HK_USER_STOPPED); /* * Give the caller the virtual addresses of his user and signal * VCRs. Make sure that he doesn't have loaded translations to * them so that we can detect when he references them. */ if (gtsegmap(device, gt_map[MAP_FE_HFLAG2].vaddr, u.u_procp->p_as, (addr_t *) &vaddr, sizeof (struct fe_page_2), (u_int) 0, (u_int) 0, (u_int) MAP_PRIVATE, (struct ucred *) NULL) == -1) { return (EINVAL); } iop->gt_puvcr = ap->ap_uvcr = &((struct fe_page_2 *)vaddr)->fe_hflag_2; if (gtsegmap(device, gt_map[MAP_FE_HFLAG1].vaddr, u.u_procp->p_as, (addr_t *) &vaddr, sizeof (struct fe_page_1), (u_int) 0, (u_int) 0, (u_int) MAP_PRIVATE, (struct ucred *) NULL) == -1) { return (EINVAL); } iop->gt_psvcr = ap->ap_svcr = &((struct fe_page_1 *)vaddr)->fe_hflag_1; gt_set_flags(&ap->ap_flags, (GTA_CONNECT | GTA_VIRGIN)); GT_LIST_INSERT_NEXT(softc->at_head, ap, at_proc, ap_list); return (0); } /* * Unregister this process as an accelerator user */ static gt_disconnect(softc) struct gt_softc *softc; { struct at_proc *ap; ap = gta_findproc(softc); if (ap != (struct at_proc *)NULL) gta_exit(ap); return (0); } /* * Terminate the accelerator thread: * * - remove the at_proc struct from the accelerator process list. * - unmap the user VCR pages. * - free the page tables. * - free the GT lockmap elements * - free the at_struct element. */ static void gta_exit(ap) struct at_proc *ap; { struct gt_softc *softc; struct seg *seg; struct gta_lockmap *map, *maphead, *nxtmap; softc = ap->ap_softc; if (ap->ap_flags & GTA_EXIT) return; gt_set_flags(&ap->ap_flags, GTA_EXIT); /* * Ensure that the accelerator isn't running * display lists for this process. */ while (softc->at_curr == ap) { wakeup(&softc->wchan[WCHAN_SCHED]); (void) sleep(&softc->wchan[WCHAN_SUSPEND], PZERO+1); } GT_LIST_REMOVE(ap, at_proc, ap_list); seg = as_segat(ap->ap_as, (addr_t)ap->ap_svcr); if (seg != (struct seg *)NULL) { if (seg == softc->svcrseg) softc->svcrseg = (struct seg *)NULL; if (gt_validthread(softc, ap) && (!(ap->ap_proc->p_flag & SWEXIT))) (void) seggt_unmap(seg, seg->s_base, seg->s_size); } seg = as_segat(ap->ap_as, (addr_t)ap->ap_uvcr); if (seg != (struct seg *)NULL) { if (seg == softc->uvcrseg) softc->uvcrseg = (struct seg *)NULL; if (gt_validthread(softc, ap) && (!(ap->ap_proc->p_flag & SWEXIT))) (void) seggt_unmap(seg, seg->s_base, seg->s_size); } gt_freept(ap); maphead = >a_lockbusy; nxtmap = GT_LIST_NEXT(maphead, gta_lockmap, gtm_list); while (nxtmap != maphead) { map = nxtmap; nxtmap = GT_LIST_NEXT(nxtmap, gta_lockmap, gtm_list); if (map->gtm_as == ap->ap_as) gt_freemap(map); } if (ap->ap_flags & GTA_SWLOCK) ap->ap_proc->p_swlocks--; ap->ap_as->a_hatcallback = 0; kmem_free((caddr_t)ap, sizeof (*ap)); } /* * Free page tables */ static void gt_freept(ap) struct at_proc *ap; { struct gt_lvl1 *lvl1; struct gt_lvl2 **lvl2; int i; lvl1 = ap->ap_lvl1; lvl2 = lvl1->gt_lvl2; /* * Return any level 2 tables */ for (i=0; igtm_pp; s = splvm(); /* * Release the mapping */ #ifdef sun4c addr = gta_dvmaspace + ((int)(map-gta_lockmapbase) << MMU_PAGESHIFT); seg = &kseg; mmu_getpte(addr, &pte); pp = page_numtookpp(pte.pg_pfnum); hat_unlock(seg, addr); hat_unload(seg, addr, MMU_PAGESIZE); #endif sun4c page_pp_unlock(pp, 0); (void) splx(s); /* * Add the GT lockmap element to the free list. */ GT_LIST_INSERT_PREV(>a_lockfree, map, gta_lockmap, gtm_list); } static gt_vmctl(softc, data) struct gt_softc *softc; caddr_t data; { struct fb_vmctl *fbv = (struct fb_vmctl *)data; int rc; switch (fbv->vc_function) { case FBV_SUSPEND: rc = gta_suspend(softc); break; case FBV_RESUME: rc = gta_resume(softc); break; default: rc = EINVAL; break; } return (rc); } static gta_suspend(softc) struct gt_softc *softc; { struct at_proc *ap; #ifdef GT_DEBUG gtstat.gs_suspend++; #endif GT_DEBUG ap = gta_findproc(softc); if (ap == (struct at_proc *)NULL) return (EINVAL); gt_set_flags(&ap->ap_flags, GTA_SUSPEND); /* * If this thread is currently running, we need to deschedule it. */ while (ap == (struct at_proc *)softc->at_curr) { gt_set_flags(&softc->flags, F_SUSPEND); wakeup(&softc->wchan[WCHAN_SCHED]); (void) sleep(&softc->wchan[WCHAN_SUSPEND], PZERO+1); } return (0); } static gta_resume(softc) struct gt_softc *softc; { struct at_proc *ap; ap = gta_findproc(softc); if (ap == (struct at_proc *)NULL) return (EINVAL); gt_clear_flags(&ap->ap_flags, GTA_SUSPEND); return (0); } /* * Explicitly instantiate/uninstantiate GT virtual memory */ static gt_vmback(data) caddr_t data; { struct fb_vmback *fbv = (struct fb_vmback *)data; struct segvn_data *svd; struct segvn_crargs crargs_gta; struct as *as; struct seg *seg; caddr_t addr, eaddr, seglimit; int len, rc; u_int nsize; addr = fbv->vm_addr; len = fbv->vm_len; /* * Normalize addr and len to be page-aligned */ addr = (caddr_t) ((u_int)addr & PAGEMASK); len = (len + PAGESIZE-1) & PAGEMASK; eaddr = addr + len; if (len <= 0) return (EINVAL); as = u.u_procp->p_as; gt_aslock(as); /* * Convert seggtx segments into seggta segments */ while (len > 0) { seg = as_segat(as, addr); if (seg == (struct seg *)NULL) { gt_asunlock(as); return (EINVAL); } if (seg->s_ops != &seggtx_ops) { addr = seg->s_base + seg->s_size; len = eaddr - addr; continue; } /* * Got a seggtx segment to instantiate. If we * are doing just a part of it, break the seggtx * into multiple segments. */ svd = (struct segvn_data *)seg->s_data; crargs_gta.vp = (struct vnode *)NULL; crargs_gta.offset = svd->offset + addr - seg->s_base; crargs_gta.cred = svd->cred; crargs_gta.type = svd->type; crargs_gta.maxprot = svd->maxprot; crargs_gta.prot = svd->prot; crargs_gta.amp = (struct anon_map *)NULL; seglimit = seg->s_base + seg->s_size; nsize = (seglimit >= eaddr) ? eaddr-addr : seglimit-addr; len = eaddr - (addr + nsize); (void) seggtx_unmap(seg, addr, nsize); rc = as_map(as, addr, nsize, seggta_create, (caddr_t)&crargs_gta); if (rc) { gt_asunlock(as); return (rc); } seg = as_segat(as, addr); if (seg == (struct seg *)NULL) { gt_asunlock(as); return (EINVAL); } rc = (seggta_ops.fault)(seg, addr, len, F_INVAL, S_WRITE); if (rc) { gt_asunlock(as); return (rc); } } gt_asunlock(as); return (0); } static gt_vmunback(data) caddr_t data; { struct fb_vmback *fbv = (struct fb_vmback *)data; struct segvn_data *svd; struct segvn_crargs crargs_gta; struct as *as; struct seg *seg; caddr_t addr, eaddr, seglimit; int len, rc; u_int nsize; addr = fbv->vm_addr; len = fbv->vm_len; /* * Normalize addr and len in case the user hasn't already * done so. */ addr = (caddr_t) ((u_int)addr & PAGEMASK); len = (len + PAGESIZE-1) & PAGEMASK; eaddr = addr + len; if (len <= 0) return (EINVAL); as = u.u_procp->p_as; gt_aslock(as); /* * Convert seggta segments into seggtx segments */ while (len > 0) { seg = as_segat(as, addr); if (seg == (struct seg *)NULL) { gt_asunlock(as); return (EINVAL); } if (seg->s_ops != &seggta_ops) { addr = seg->s_base + seg->s_size; len = eaddr - addr; continue; } /* * Got a seggta segment to uninstantiate. */ svd = (struct segvn_data *)seg->s_data; crargs_gta.offset = svd->offset + addr - seg->s_base; crargs_gta.cred = svd->cred; crargs_gta.type = svd->type; crargs_gta.maxprot = svd->maxprot; seglimit = seg->s_base + seg->s_size; nsize = (seglimit >= eaddr) ? eaddr-addr : seglimit-addr; len = eaddr - (addr + nsize); (void) seggta_unmap(seg, addr, nsize); rc = as_map(as, addr, nsize, seggtx_create, (caddr_t)&crargs_gta); if (rc) { gt_asunlock(as); return (EINVAL); } } gt_asunlock(as); return (0); } /* * Allow processes to grab the accelerator hardware. * * Inform the scheduler not schedule any accelerator threads until * the lock is released and stop the current accelerator thread. * * gt_grabtype specifies variations on the grab/ungrab theme * * gt_grabtype action * * 0 business as usual * 1 lazy accelerator scheduling following ungrab */ int gt_grabtype = 0; static gt_grabhw(softc, data) struct gt_softc *softc; caddr_t data; { #ifdef GT_DEBUG gtstat.gs_grabhw++; #endif GT_DEBUG if (softc->flags & F_UCODE_RUNNING) { softc->gt_grabber = u.u_procp->p_pid; gt_set_flags(&softc->flags, F_GRAB_HOLD); /* * Deschedule the current thread */ while (softc->flags & F_THREAD_RUNNING) { gt_set_flags(&softc->flags, F_SUSPEND); wakeup(&softc->wchan[WCHAN_SCHED]); (void) sleep(&softc->wchan[WCHAN_SUSPEND], PZERO+1); } *(u_int *)data = 1; } else { *(u_int *)data = 0; } return (0); } /* * Allow processes to release the accelerator hardware. */ static gt_ungrabhw(softc) struct gt_softc *softc; { if (gt_grabtype == 1) { gt_clear_flags(&softc->flags, F_GRAB_HOLD); } else { gta_run(softc, F_GRAB_HOLD); } softc->gt_grabber = (short)0; return (0); } static void gta_reset_accelerator(softc) struct gt_softc *softc; { struct at_proc *ap, *ap_head, *ap_next; int s; s = splr(gt_priority); /* * Signal any processes using the accelerator */ ap_head = softc->at_head; ap = GT_LIST_NEXT(ap_head, at_proc, ap_list); while (ap != ap_head) { ap_next = GT_LIST_NEXT(ap, at_proc, ap_list); if (gt_validthread(softc, ap)) { if (!(ap->ap_proc->p_flag & SWEXIT)) psignal(ap->ap_proc, SIGTERM); gta_exit(ap); } ap = ap_next; } /* * Clean up the kmcb queue */ gt_clean_kmcb(); /* * Reset most of the softc flags */ softc->flags &= (F_VRT_RETRACE | F_GAMMALOADED | F_DEGAMMALOADED | F_GAMMASET | F_VASCO); /* * Wakeup processes that may be waiting for accelerator events */ wakeup(&softc->wchan[WCHAN_VRT]); wakeup(&softc->wchan[WCHAN_VCR]); wakeup(&softc->wchan[WCHAN_SUSPEND]); wakeup(&softc->wchan[WCHAN_THAW]); (void) splx(s); } static void gt_clean_kmcb() { struct gt_kmcblist *kptr, *next; next = GT_LIST_NEXT(>_kmcbhead, gt_kmcblist, q_list); while (next != >_kmcbhead) { kptr = next; next = GT_LIST_NEXT(next, gt_kmcblist, q_list); GT_LIST_REMOVE(kptr, gt_kmcblist, q_list); if (kptr->q_flags & KQ_BLOCK) wakeup((caddr_t)kptr); kmem_free((caddr_t)kptr, sizeof (*kptr)); } } /* * Make a page non-cached. */ static void gt_setnocache(base) addr_t base; { int s; struct pte pte; #ifdef sun4m /* * Viking? Great!!! */ extern struct modinfo mod_info[]; if ((mod_info[0].mod_type == CPU_VIKING) || (mod_info[0].mod_type == CPU_VIKING_MXCC)) { return; } else { panic("gt_setnocache: sun4m/ross not supported"); } #endif sun4m base = (addr_t)((int)base & MMU_PAGEMASK); s = splvm(); mmu_getpte(base, &pte); if (pte_valid(&pte)) { #ifdef sun4c pte.pg_nc = 1; mmu_setpte(base, pte); #endif sun4c } (void) splx(s); } /* * Shared Resource Management */ /* * Detach a proc list element */ static void gt_link_detach(ptr) struct proc_ll_type *ptr; { ptr->plt_prev->plt_next = ptr->plt_next; ptr->plt_next->plt_prev = ptr->plt_prev; } /* * Attach a proc list element between ptr1 and ptr2 */ static void gt_link_attach(ptr1, ptr2, newptr) struct proc_ll_type *ptr1, *ptr2, *newptr; { newptr->plt_next = ptr1->plt_next; ptr1->plt_next = newptr; newptr->plt_prev = ptr2->plt_prev; ptr2->plt_prev = newptr; } /* * Find a proc list element for the specified pid. */ static struct proc_ll_type * gt_find_proc_link(startptr, pid) struct proc_ll_type *startptr; short pid; { struct proc_ll_type *tptr = startptr->plt_next; struct proc_ll_type *found_ptr = NULL; u_int found = 0; if (startptr->plt_pid == pid) return (startptr); while (!found && tptr != startptr) { if (tptr->plt_pid == pid) { found_ptr = tptr; found = 1; } else { tptr = tptr->plt_next; } } if (found) return (found_ptr); else return (NULL); } /* * Insert a proc list element into the list, in ascending order. */ static void gt_insert_link(softc, startptr, cptr) struct gt_softc *softc; struct proc_ll_type *startptr; struct proc_ll_type *cptr; { struct proc_ll_type *tptr = startptr; if (startptr->plt_pid <= cptr->plt_pid) { tptr = startptr->plt_next; while ((tptr != startptr) && (cptr->plt_pid > tptr->plt_pid)) tptr = tptr->plt_next; gt_link_attach(tptr->plt_prev, tptr, cptr); } else { /* Insert in the front */ /* Therefore change startptr */ gt_link_attach(tptr->plt_prev, tptr, cptr); softc->proc_list = cptr; } } static void gt_alloc_sr(softc, pid, type, base, count) struct gt_softc *softc; short pid; enum res_type type; int base; u_int count; { struct proc_ll_type *found = NULL; struct proc_ll_type *gt_find_proc_link(); if (softc->proc_list != NULL) found = gt_find_proc_link(softc->proc_list, pid); if (found == NULL) { found = (struct proc_ll_type *) new_kmem_zalloc(sizeof (*found), KMEM_SLEEP); found->plt_pid = pid; found->plt_sr = NULL; if (softc->proc_list != NULL) gt_insert_link(softc, softc->proc_list, found); else { softc->proc_list = found; found->plt_next = found; found->plt_prev = found; } } gt_init_sr(found, type, base, count); } static void gt_init_sr(found, type, base, count) struct proc_ll_type *found; enum res_type type; int base; u_int count; { struct shar_res_type *srptr, *newptr, *prevptr; newptr = (struct shar_res_type *) new_kmem_zalloc(sizeof (*newptr), KMEM_SLEEP); newptr->srt_next = NULL; newptr->srt_type = type; newptr->srt_base = base; newptr->srt_count = count; if (found->plt_sr != NULL) { srptr=found->plt_sr; prevptr = srptr; while (srptr != NULL) { prevptr = srptr; srptr = srptr->srt_next; } prevptr->srt_next = newptr; } else { found->plt_sr = newptr; } } /* * Decrement the ref count on different types of shared resource tables */ static void gt_decrement_refcount(softc, type, base, count) struct gt_softc *softc; enum res_type type; int base; u_int count; { u_int i; switch (type) { case RT_CLUT8: for (i=0; iclut_table[base].tt_refcount--; if (softc->clut_table[base].tt_refcount == 0) softc->clut_table[base].tt_flags &= ~TT_USED; } break; case RT_OWID: for (i=0; iwid_otable[base].tt_refcount--; if (softc->wid_otable[base].tt_refcount == 0) softc->wid_otable[base].tt_flags &= ~TT_USED; } break; case RT_IWID: for (i=0; iwid_itable[base].tt_refcount--; if (softc->wid_itable[base].tt_refcount == 0) softc->wid_itable[base].tt_flags &= ~TT_USED; } break; case RT_FCS: for (i=0; ifcs_table[base].tt_refcount--; if (softc->fcs_table[base].tt_refcount == 0) softc->fcs_table[base].tt_flags &= ~TT_USED; } break; default: panic("gt bad data type"); } } /* * SEGMENT DRIVER ROUTINES * * This implements a graphical context switch that is transparent to the user. * * This virtualizes the register file by associating a register * save area with each mapping of the device (each segment). * * Only one of these mappings is valid at any time; a page fault * on one of the invalid mappings: * * - saves the current context, * - invalidates the current valid mapping, * - restores the former register contents appropriate * to the faulting mapping, * - validates the faulting mapping */ struct seggt_data *shared_list; struct gt_cntxt shared_context; /* * This routine is called via the cdevsw[] table to handle mmap() calls */ gtsegmap(dev, offset, as, addr, len, prot, maxprot, flags, cred) dev_t dev; u_int offset; struct as *as; addr_t *addr; u_int len; u_int prot; u_int maxprot; u_int flags; struct ucred *cred; { struct seggt_crargs crargs; struct segvn_crargs crargs_gtx; int (*crfp)(); caddr_t argsp; int rc; int i; struct gt_softc *softc = >_softc[minor(dev)>>1]; /* * If bwtwo emulation mode, change len to map in * the entire frame buffer */ if (softc->gattr->sattr.emu_type == FBTYPE_SUN2BW) len = 0x2000000; /* * If this is a direct-port map ensure that the entire range * is legal and we are not trying to map more than the device * will let us. */ if ((minor(dev) & GT_ACCEL_PORT) == 0) { for (i=0; is_ops = &seggt_ops; seg->s_data = (char *)dp; return (0); } /* * Duplicate seg and return new segment in newsegp. Copy original context. */ static seggt_dup(seg, newseg) struct seg *seg, *newseg; { struct seggt_data *sdp = (struct seggt_data *)seg->s_data; struct seggt_data *ndp; (void) seggt_create(newseg, (caddr_t)sdp); ndp = (struct seggt_data *)newseg->s_data; if (sdp->flag & SEG_SHARED) { struct seggt_crargs *a; a = shared_list; shared_list = ndp; ndp->link = a; } else if (sdp->flag & SEG_VCR) { ndp->cntxtp = NULL; } else { ndp->cntxtp = (struct gt_cntxt *) new_kmem_zalloc(sizeof (struct gt_cntxt), KMEM_SLEEP); *ndp->cntxtp = *sdp->cntxtp; } return (0); } static seggt_unmap(seg, addr, len) struct seg *seg; addr_t addr; u_int len; { struct seggt_data *sdp = (struct seggt_data *)seg->s_data; struct seg *nseg; addr_t nbase; u_int nsize; if (seg == (struct seg *)NULL) return (-1); /* * Check for bad sizes */ if (addr < seg->s_base || addr + len > seg->s_base + seg->s_size || (len & PAGEOFFSET) || ((u_int)addr & PAGEOFFSET)) panic("segdev_unmap"); /* * Unload any hardware translations in the range to be taken out. */ hat_unload(seg,addr,len); /* * Check for entire segment */ if (addr == seg->s_base && len == seg->s_size) { seg_free(seg); return (0); } /* * Check for beginning of segment */ if (addr == seg->s_base) { seg->s_base += len; seg->s_size -= len; return (0); } /* * Check for end of segment */ if (addr + len == seg->s_base + seg->s_size) { seg->s_size -= len; return (0); } /* * The section to go is in the middle of the segment, * have to make it into two segments. nseg is made for * the high end while seg is cut down at the low end. */ nbase = addr + len; /* new seg base */ nsize = (seg->s_base + seg->s_size) - nbase; /* new seg size */ seg->s_size = addr - seg->s_base; /* shrink old seg */ nseg = seg_alloc(seg->s_as, nbase, nsize); if (nseg == NULL) panic("seggt_unmap seg_alloc"); nseg->s_ops = seg->s_ops; /* * Figure out what to do about the new context */ nseg->s_data = (caddr_t)new_kmem_alloc(sizeof (struct seggt_data), KMEM_SLEEP); if (sdp->flag & SEG_SHARED) { struct seggt_crargs *a; a = shared_list; shared_list = (struct seggt_data *)nseg->s_data; ((struct seggt_data *)nseg->s_data)->link = a; } else { if (sdp->cntxtp == NULL) { ((struct seggt_data *)nseg->s_data)->cntxtp = NULL; } else { ((struct seggt_data *)nseg->s_data)->cntxtp = (struct gt_cntxt *) new_kmem_zalloc(sizeof (struct gt_cntxt), KMEM_SLEEP); *((struct seggt_data *)nseg->s_data)->cntxtp = *sdp->cntxtp; } } /* * Now we do something so that all the translations which used * to be associated with seg but are now associated with nseg. */ hat_newseg(seg, nseg->s_base, nseg->s_size, nseg); return (0); } /* * free a segment and clean up its context. */ static seggt_free(seg) struct seg *seg; { struct seggt_data *sdp = (struct seggt_data *)seg->s_data; seggt_freeres(seg); if (seg == gt_curcntxt) gt_curcntxt = NULL; if ((sdp->flag & SEG_SHARED) == 0 && (sdp->cntxtp != NULL)) kmem_free((caddr_t)sdp->cntxtp, sizeof (struct gt_cntxt)); kmem_free((caddr_t)sdp, sizeof (*sdp)); } /* * Reclaim driver resources used by this process */ seggt_freeres(seg) struct seg *seg; { struct seggt_data *sdp = (struct seggt_data *)seg->s_data; struct gt_softc *softc = >_softc[minor(sdp->dev)>>1]; struct proc_ll_type *found = NULL; struct shar_res_type *srptr = NULL; struct at_proc *ap; if (u.u_procp->p_flag & SWEXIT) { ap = gta_findas(seg->s_as); if (ap != (struct at_proc *)NULL) gta_exit(ap); } else if (sdp->flag & SEG_VCR) { if (seg == softc->uvcrseg) softc->uvcrseg = (struct seg *)NULL; if (seg == softc->svcrseg) softc->svcrseg = (struct seg *)NULL; return; } /* * Free the shared resources that this process was using. */ if ((softc->proc_list != NULL) && (!(sdp->flag & SEG_LOCK) || (u.u_procp->p_pid != softc->server_proc))) { found = gt_find_proc_link(softc->proc_list, u.u_procp->p_pid); if (found != NULL) { /* * This process was using some shared resources */ while (found->plt_sr != NULL) { srptr = found->plt_sr; found->plt_sr = srptr->srt_next; gt_decrement_refcount(softc, srptr->srt_type, srptr->srt_base, srptr->srt_count); kmem_free((caddr_t)srptr, sizeof (*srptr)); } gt_link_detach(found); if (found == softc->proc_list) { /* first element is being deleted */ if (found == found->plt_next && found == found->plt_prev) { softc->proc_list = NULL; } else { softc->proc_list = found->plt_next; } } kmem_free((caddr_t)found, sizeof (*found)); } } /* * Release diagnostic and/or server privilege if appropriate. */ if (gt_diag_check(softc)) gt_diag_free(softc); if (gt_server_check(softc)) { if (!(sdp->flag & SEG_LOCK)) gt_server_free(softc); softc->clut_part_used = 0; } } #define GTMAP(sp, addr, page) \ hat_devload( \ sp, \ addr, \ fbgetpage((caddr_t)page), \ PROT_READ|PROT_WRITE|PROT_USER, \ 0) #define GTUNMAP(segp) \ hat_unload( \ segp, \ (segp)->s_base, \ (segp)->s_size); /* * Handle a fault on a segment. The only tricky part is that only one * valid mapping at a time is allowed. Whenever a new mapping is called * for, the current values of the state set 0 registers in the old context * are saved away, and the old values in the new context are restored. */ /*ARGSUSED*/ static faultcode_t seggt_fault(seg, addr, len, type, rw) struct seg *seg; addr_t addr; u_int len; enum fault_type type; enum seg_rw rw; { struct seggt_data *old; struct seggt_data *current = (struct seggt_data *)seg->s_data; struct gt_softc *softc = >_softc[minor(current->dev)]; addr_t adr; int pf, s; #ifdef GT_DEBUG gtstat.gs_seggt_fault++; #endif GT_DEBUG if (type != F_INVAL) return (FC_MAKE_ERR(EFAULT)); /* * VCR access? */ if (current->flag & SEG_VCR) { struct at_proc *ap; caddr_t wchan = (caddr_t) &softc->wchan[WCHAN_VCR]; ap = gta_findproc(softc); if (ap == (struct at_proc *)NULL) return (FC_MAKE_ERR(EFAULT)); gt_clear_flags(&ap->ap_flags, GTA_VIRGIN); while ((softc->at_curr != ap) && gt_validthread(softc, ap)) (void) sleep(wchan, PZERO-1); if (!gt_validthread(softc, ap)) return (FC_MAKE_ERR(EFAULT)); /* * Either this process has the accelerator or no * one does. Record and grant the translation. */ if (current->flag & SEG_UVCR) softc->uvcrseg = seg; else softc->svcrseg = seg; #ifndef MULTIPROCESSOR s = splvm(); #else s = splvm(); xc_attention(); #endif MULTIPROCESSOR } else { #ifndef MULTIPROCESSOR s = splvm(); #else s = splvm(); xc_attention(); #endif MULTIPROCESSOR /* * Need to switch contexts? */ if (current->cntxtp && gt_curcntxt != seg) { if (gt_curcntxt != NULL) { old = (struct seggt_data *)gt_curcntxt->s_data; /* * wipe out old valid mapping. * * TODO: Figure out the PF busy is cleared & * if rp_stalled then put the current * process to sleep with timeout */ /* * Check for PF not busy */ if (gt_check_pf_busy(softc)) { #ifdef MULTIPROCESSOR xc_dismissed(); #endif MULTIPROCESSOR (void) splx(s); return (FC_HWERR); } hat_unload(gt_curcntxt, gt_curcntxt->s_base, gt_curcntxt->s_size); /* * switch hardware contexts * * If the old and new contexts are shared * then no need to save and restore contexts */ if (!(current->flag == SEG_SHARED && old->flag == SEG_SHARED)) { gt_cntxsave(softc, old->cntxtp); gt_cntxrestore(softc, current->cntxtp); } } else { if (current->flag & SEG_PRIVATE) { /* * Check for PF not busy */ if (gt_check_pf_busy(softc)) { #ifdef MULTIPROCESSOR xc_dismissed(); #endif MULTIPROCESSOR (void) splx(s); return (FC_HWERR); } gt_cntxrestore(softc, current->cntxtp); } } /* * switch software context */ gt_curcntxt = seg; } } for (adr=addr; adr < addr+len; adr += PAGESIZE) { pf = gtmmap(current->dev, (off_t)current->offset+(adr-seg->s_base), PROT_READ|PROT_WRITE); if (pf == -1) { #ifdef MULTIPROCESSOR xc_dismissed(); #endif MULTIPROCESSOR (void) splx(s); return (FC_MAKE_ERR(EFAULT)); } if (softc->gattr->sattr.emu_type == FBTYPE_SUN2BW) { if (softc->gattr->sattr.flags & FB_ATTR_AUTOINIT) { if (gt_setup_bwtwo(softc)) { #ifdef MULTIPROCESSOR xc_dismissed(); #endif MULTIPROCESSOR (void) splx(s); return (FC_MAKE_ERR(EFAULT)); } softc->gattr->sattr.flags &= ~FB_ATTR_AUTOINIT; } } hat_devload(seg, adr, pf, PROT_READ|PROT_WRITE|PROT_USER, 0); } #ifdef MULTIPROCESSOR xc_dismissed(); #endif MULTIPROCESSOR (void) splx(s); return (0); } /*ARGSUSED*/ static seggt_checkprot(seg, addr, len, prot) struct seg *seg; addr_t addr; u_int len; u_int prot; { return (PROT_READ|PROT_WRITE); } /* * segdev pages are always "in core" */ /*ARGSUSED*/ static seggt_incore(seg, addr, len, vec) struct seg *seg; addr_t addr; u_int len; char *vec; { u_int v = 0; for (len = (len + PAGEOFFSET) & PAGEMASK; len; len -= PAGESIZE, v += PAGESIZE) *vec++ = 1; return (v); } static seggt_badop() { return (0); /* silently fail */ } /* * Create a GT segment */ static int seggtx_create(seg, argsp) struct seg *seg; caddr_t argsp; { struct segvn_data *svd; struct segvn_crargs *a = (struct segvn_crargs *)argsp; svd = (struct segvn_data *) new_kmem_zalloc(sizeof (*svd), KMEM_SLEEP); seg->s_ops = &seggtx_ops; seg->s_data = (char *)svd; svd->offset = a->offset; svd->cred = a->cred; svd->type = a->type; svd->maxprot = a->maxprot; svd->prot = a->prot; return (0); } static int seggtx_dup(seg, newseg) struct seg *seg; struct seg *newseg; { struct segvn_data *svd; struct segvn_data *ndp; struct as *as; as = seg->s_as; gt_aslock(as); svd = (struct segvn_data *) seg->s_data; ndp = (struct segvn_data *) new_kmem_zalloc(sizeof (*ndp), KMEM_SLEEP); newseg->s_ops = &seggtx_ops; newseg->s_data = (char *)ndp; ndp->offset = svd->offset; ndp->cred = svd->cred; ndp->type = svd->type; ndp->maxprot = svd->maxprot; ndp->prot = svd->prot; gt_asunlock(as); return (0); } static int seggtx_unmap(seg, addr, len) struct seg *seg; addr_t addr; u_int len; { struct segvn_data *svd; struct segvn_data *nsvd; struct seg *nseg; struct as *as; addr_t nbase; u_int nsize; if (seg == (struct seg *)NULL) return (-1); /* * Check for bad sizes */ if (addr < seg->s_base || addr + len > seg->s_base + seg->s_size || (len & PAGEOFFSET) || ((u_int)addr & PAGEOFFSET)) panic("seggtx_unmap"); as = seg->s_as; gt_aslock(as); svd = (struct segvn_data *)seg->s_data; /* * We never load translations for these segments, so there are * none to unload here. * * Check for entire segment. */ if (addr == seg->s_base && len == seg->s_size) { seg_free(seg); gt_asunlock(as); return (0); } /* * Check for beginning of segment */ if (addr == seg->s_base) { seg->s_base += len; seg->s_size -= len; svd->offset += len; gt_asunlock(as); return (0); } /* * Check for end of segment */ if (addr + len == seg->s_base + seg->s_size) { seg->s_size -= len; gt_asunlock(as); return (0); } /* * The section to go is in the middle of the segment, so we've * got to split the segment into two. nseg is made for the * high end and seg is cut down at the low end. */ nbase = addr + len; /* new seg base */ nsize = (seg->s_base + seg->s_size) - nbase; /* new seg size */ seg->s_size = addr - seg->s_base; /* shrink old seg */ nseg = seg_alloc(seg->s_as, nbase, nsize); if (nseg == NULL) panic("seggtx_unmap seg_alloc"); nsvd = (struct segvn_data *) new_kmem_zalloc(sizeof (*nsvd), KMEM_SLEEP); nseg->s_ops = seg->s_ops; nseg->s_data = (char *)nsvd; nsvd->offset = svd->offset + nseg->s_base - seg->s_base; nsvd->cred = svd->cred; nsvd->type = svd->type; nsvd->maxprot = svd->maxprot; nsvd->prot = svd->prot; gt_asunlock(as); return (0); } static int seggtx_free(seg) struct seg *seg; { struct at_proc *ap; struct as *as; struct segvn_data *svd; as = seg->s_as; gt_aslock(as); svd = (struct segvn_data *)seg->s_data; ap = gta_findas(as); if (ap != (struct at_proc *)NULL) { if (ap->ap_proc->p_flag & SWEXIT) gta_exit(ap); } kmem_free((caddr_t)svd, sizeof (*svd)); gt_asunlock(as); return (0); } /* * Faults in the seggtx segment are resolved by converting the page * containing the faulting address to a seggta_segment and resolving * the fault there. */ static faultcode_t seggtx_fault(seg, addr, len, type, rw) struct seg *seg; addr_t addr; u_int len; enum fault_type type; enum seg_rw rw; { struct segvn_data *svd; struct segvn_crargs crargs_gta; struct as *as; struct seg *nseg; addr_t nbase; u_int nsize; int rc; #ifdef GT_DEBUG gtstat.gs_seggtx_fault++; #endif GT_DEBUG /* * If there is an in-progress fault on a GT portion of the * same address space, block this one until that one completes. */ as = seg->s_as; gt_aslock(as); if (gt_faultcheck(as) == -1) { gt_asunlock(as); return (FC_MAKE_ERR(EFAULT)); } svd = (struct segvn_data *)seg->s_data; /* * Make a copy of the segment private data and then unmap * the page containing the faulting address */ nbase = (addr_t)((u_int)addr & PAGEMASK); nsize = (len + PAGESIZE-1) & PAGEMASK; crargs_gta.vp = (struct vnode *)NULL; crargs_gta.offset = svd->offset + nbase - seg->s_base; crargs_gta.cred = svd->cred; crargs_gta.type = svd->type; crargs_gta.maxprot = svd->maxprot; crargs_gta.prot = svd->prot; crargs_gta.amp = (struct anon_map *)NULL; (void) seggtx_unmap(seg, nbase, nsize); rc = as_map(as, nbase, nsize, seggta_create, (caddr_t)&crargs_gta); if (rc) return (FC_MAKE_ERR(EFAULT)); nseg = as_segat(as, addr); if (nseg == 0) return (FC_MAKE_ERR(EFAULT)); rc = (seggta_ops.fault)(nseg, addr, len, type, rw); /* * Wakeup anyone blocked for this fault to complete */ gt_asunlock(as); return (rc); } static gt_faultcheck(as) struct as *as; { struct at_proc *ap; if (u.u_procp->p_pid == gt_pagedaemonpid) { ap = gta_findas(as); if (ap != (struct at_proc *)NULL) { if (ap->ap_proc->p_flag & SWEXIT) { gta_exit(ap); return (-1); } } } return (0); } /* * When a translation is unloaded, we see whether the process * is exiting. I wish there were another way to stop GT from * using resources of an exiting process. */ /*ARGSUSED*/ static int seggtx_hatsync(seg, addr, ref, mod, flags) struct seg *seg; addr_t addr; u_int ref; u_int mod; u_int flags; { struct at_proc *ap; if (u.u_procp->p_flag & SWEXIT) { ap = gta_findas(seg->s_as); if (ap != (struct at_proc *)NULL) gta_exit(ap); } return (0); } static int seggtx_badop() { printf("seggtx_badop\n"); return (0); /* silently fail */ } /*ARGSUSED*/ static int seggtx_setprot(seg, addr, len, prot) struct seg *seg; addr_t addr; u_int len; u_int prot; { struct segvn_data *svd = (struct segvn_data *)seg->s_data; if ((svd->maxprot & prot) != prot) return (-1); svd->prot = prot; return (0); } /*ARGSUSED*/ static int seggtx_checkprot (seg, addr, len, prot) struct seg *seg; addr_t addr; u_int len; u_int prot; { struct segvn_data *svd = (struct segvn_data *)seg->s_data; return (((svd->prot & prot) != prot) ? -1 : 0); } /*ARGSUSED*/ static int seggtx_incore(seg, addr, len, vec) { return (0); } static int seggta_create(seg, argsp) struct seg *seg; caddr_t argsp; { struct seg *pseg; struct seg *nseg; struct as *as; addr_t pbase = 0; addr_t nbase = 0; int rc; /* * Check the previous and next segments. If they are seggta * segments, temporarily convert them to segvn segs to allow * segvn_create the opportunity to coalesce them. When done, * convert them back to seggta segments if they still exist. */ as = seg->s_as; gt_aslock(as); pseg = seg->s_prev; nseg = seg->s_next; if ((pseg != seg) && (pseg->s_ops == &seggta_ops)) { pbase = pseg->s_base; pseg->s_ops = &segvn_ops; } if ((nseg != seg) && (nseg->s_ops == &seggta_ops)) { nbase = nseg->s_base; nseg->s_ops = &segvn_ops; } rc = segvn_create(seg, argsp); if (rc == 0) seg->s_ops = &seggta_ops; /* * Revert the previous/next segs, if necessary. */ pseg = seg->s_prev; nseg = seg->s_next; if ((pseg != seg) && (pseg->s_base == pbase)) pseg->s_ops = &seggta_ops; if ((nseg != seg) && (nseg->s_base == nbase)) nseg->s_ops = &seggta_ops; gt_asunlock(as); return (rc); } static int seggta_dup(seg, newsegp) struct seg *seg, *newsegp; { struct as *as; int rc; as = seg->s_as; gt_aslock(as); rc = (segvn_ops.dup)(seg, newsegp); newsegp->s_ops = &seggta_ops; gt_asunlock(as); return (rc); } static int seggta_unmap(seg, addr, len) struct seg *seg; addr_t addr; u_int len; { struct as *as; int rc; as = seg->s_as; gt_aslock(as); gta_unload(as, addr, len); rc = (segvn_ops.unmap)(seg, addr, len); gt_asunlock(as); return (rc); } static int seggta_free(seg) struct seg *seg; { struct at_proc *ap; struct as *as; int rc; /* * If the process is exiting, shut down the corresponding * GT accelerator thread. Otherwise, release any mappings * to addresses in the segment being freed. */ as = seg->s_as; gt_aslock(as); ap = gta_findas(as); if (ap != (struct at_proc *)NULL) { if (ap->ap_proc->p_flag & SWEXIT) { gta_exit(ap); } else { gta_unload(as, seg->s_base, seg->s_size); } } rc = (segvn_ops.free)(seg); gt_asunlock(as); return (rc); } static faultcode_t seggta_fault(seg, addr, len, type, rw) struct seg *seg; addr_t addr; u_int len; enum fault_type type; enum seg_rw rw; { struct as *as; int rc; #ifdef GT_DEBUG gtstat.gs_seggta_fault++; #endif GT_DEBUG /* * If there is an in-progress fault on a GT portion of the * same address space, block this one until that one completes. */ as = seg->s_as; gt_aslock(as); if (gt_faultcheck(as) == -1) { gt_asunlock(as); return (FC_MAKE_ERR(EFAULT)); } rc = (segvn_ops.fault)(seg, addr, len, type, rw); /* * Wakeup anyone blocked for this fault to complete */ gt_asunlock(as); return (rc); } static faultcode_t seggta_faulta(seg, addr) struct seg *seg; addr_t addr; { struct as *as; int rc; as = seg->s_as; gt_aslock(as); if (gt_faultcheck(as) == -1) { gt_asunlock(as); return (FC_MAKE_ERR(EFAULT)); } rc = (segvn_ops.faulta)(seg, addr); gt_asunlock(as); return (rc); } static int seggta_hatsync(seg, addr, ref, mod, flags) struct seg *seg; addr_t addr; u_int ref; u_int mod; u_int flags; { struct at_proc *ap; struct as *as; int rc; if (u.u_procp->p_flag & SWEXIT) { as = seg->s_as; gt_aslock(as); ap = gta_findas(as); if (ap != (struct at_proc *)NULL) gta_exit(ap); gt_asunlock(as); } rc = (segvn_ops.hatsync)(seg, addr, ref, mod, flags); return (rc); } static int seggta_setprot(seg, addr, len, prot) struct seg *seg; addr_t addr; u_int len; u_int prot; { int rc; rc = (segvn_ops.setprot)(seg, addr, len, prot); return (rc); } static int seggta_checkprot(seg, addr, len, prot) struct seg *seg; addr_t addr; u_int len; u_int prot; { int rc; rc = (segvn_ops.checkprot)(seg, addr, len, prot); return (rc); } static int seggta_kluster(seg, addr, delta) struct seg *seg; addr_t addr; int delta; { struct as *as; int rc; as = seg->s_as; gt_aslock(as); rc = (segvn_ops.kluster)(seg, addr, delta); gt_asunlock(as); return (rc); } static u_int seggta_swapout(seg) struct seg *seg; { struct as *as; int rc; as = seg->s_as; gt_aslock(as); rc = (segvn_ops.swapout)(seg); gt_asunlock(as); return (rc); } static int seggta_sync(seg, addr, len, flags) struct seg *seg; addr_t addr; u_int len; u_int flags; { struct as *as; int rc; as = seg->s_as; gt_aslock(as); rc = (segvn_ops.sync)(seg, addr, len, flags); gt_asunlock(as); return (rc); } static int seggta_incore(seg, addr, len, vec) struct seg *seg; addr_t addr; u_int len; char *vec; { struct as *as; int rc; as = seg->s_as; gt_aslock(as); rc = (segvn_ops.incore)(seg, addr, len, vec); gt_asunlock(as); return (rc); } static int seggta_lockop(seg, addr, len, op) struct seg *seg; addr_t addr; u_int len; int op; { struct as *as; int rc; as = seg->s_as; gt_aslock(as); rc = (segvn_ops.lockop)(seg, addr, len, op); gt_asunlock(as); return (rc); } static int seggta_advise(seg, addr, len, behav) struct seg *seg; addr_t addr; u_int len; int behav; { struct as *as; int rc; as = seg->s_as; gt_aslock(as); rc = (segvn_ops.advise)(seg, addr, len, behav); gt_asunlock(as); return (rc); } /* * Lock the address space against simultaneous GT/host faults */ static void gt_aslock(as) struct as *as; { struct gt_softc *softc; struct at_proc *ap; unsigned mylock, otherlock; caddr_t wchan; if ((ap=gta_findas(as)) == (struct at_proc *)NULL) return; softc = ap->ap_softc; wchan = &softc->wchan[WCHAN_ASLOCK]; if (u.u_procp->p_pid == gt_pagedaemonpid) { mylock = GTA_GTLOCK; otherlock = GTA_HOSTLOCK; } else { mylock = GTA_HOSTLOCK; otherlock = GTA_GTLOCK; } /* * Block until there is no (other) GT as lock on this address space. */ while (ap->ap_flags & otherlock) { (void) sleep(wchan, PZERO-1); ap = gta_findas(as); if (ap == (struct at_proc *)NULL) { wakeup(&softc->wchan[WCHAN_ASLOCK]); return; } } ap->ap_lockcount++; gt_set_flags(&ap->ap_flags, mylock); } /* * Remove the GT aslock if it is present, and wake any blocked processes. */ static void gt_asunlock(as) struct as *as; { struct at_proc *ap; unsigned mylock; if (u.u_procp->p_pid == gt_pagedaemonpid) mylock = GTA_GTLOCK; else mylock = GTA_HOSTLOCK; ap = gta_findas(as); if (ap != (struct at_proc *)NULL) { if (--ap->ap_lockcount <= 0) { gt_clear_flags(&ap->ap_flags, mylock); wakeup(&ap->ap_softc->wchan[WCHAN_ASLOCK]); } } } /* * Unload all the GT mappings in the range [addr..addr+len). * * addr and len must be GT_PAGESIZE aligned. */ static void gta_unload(as, addr, len) struct as *as; addr_t addr; u_int len; { struct gta_mmudata mmudata; struct gt_lvl2 **pp2; struct gt_lvl2 *p2; struct gt_ptp *ptp; struct gt_pte *pte = (struct gt_pte *)NULL; struct gt_pte *ptelast; struct at_proc *ap; addr_t a, lastaddr; int index2; struct gta_lockmap *map; ap = gta_findas(as); /* * If the user has already issued a gt_disconnect, there * won't be an at_proc struct for this address space. */ if (ap == (struct at_proc *) NULL) return; addr = (addr_t) ((u_int)addr & PAGEMASK); lastaddr = addr + len; (void) gta_getmmudata(ap, addr, &mmudata); /* * pp2: &(level 2 pointer in the level 1 table) * ptp: &(page table pointer in the level 1 table) */ pp2 = &((mmudata.gtu_lvl1)->gt_lvl2[mmudata.gtu_index1]); ptp = &((mmudata.gtu_lvl1)->gt_ptp[mmudata.gtu_index1]); index2 = mmudata.gtu_index2; /* * Before invalidating the GT PTEs, freeze the ATU and clear * its TLB. After invalidating the PTEs, thaw the ATU. * * XXX: synchronize the ref/mod bits */ gt_freeze_atu(ap->ap_softc, F_FREEZE_UNLOAD); gt_set_flags(&ap->ap_softc->fe_ctrl_sp_ptr->fe_atu_syncr, ATU_CLEAR); for (a=addr; agt_pte[index2]; ptelast = &p2->gt_pte[GT_ENTRIES_PER_LVL2]; } /* * If this is a valid PTE, invalidate it and free * the GT lock map entry. */ if (pte->pte_v == 1) { pte->pte_v = 0; map = gta_hash_find(a, as); if (map != (struct gta_lockmap *) NULL) gt_freemap(map); ptp->ptp_cnt--; if (ptp->ptp_cnt == 0) { ptp->ptp_v = 0; ptp->ptp_base = 0; freepages((caddr_t) p2->gt_pte, btopr(sizeof (struct gt_lvl2))); *pp2 = (struct gt_lvl2 *)NULL; a = (addr_t) ((u_int)a & ~(GT_LVL2SIZE-1)) + GT_LVL2SIZE - GT_PAGESIZE; pte = (struct gt_pte *)NULL; index2 = 0; pp2++; ptp++; continue; } } /* * If this is the last page in a level 2 table, clear * the pme pointer to cause it to be recalculated. */ if (++pte == ptelast) { pte = (struct gt_pte *)NULL; index2 = 0; pp2++; ptp++; } } gt_thaw_atu(ap->ap_softc, F_FREEZE_UNLOAD); } /* * Save the state set 0 registers. */ static void gt_cntxsave(softc, save) struct gt_softc *softc; struct gt_cntxt *save; { struct rp_host *rp; struct fbi_reg *fb; struct fe_i860_sp *fe; #ifdef GT_DEBUG gtstat.gs_cntxsave++; #endif GT_DEBUG rp = (struct rp_host *)softc->va[MAP_RP_HOST_CTRL]; fb = (struct fbi_reg *)softc->va[MAP_FBI_REG]; fe = (struct fe_i860_sp *)softc->va[MAP_FE_I860]; #ifdef MULTIPROCESSOR /* * Idle all other processors during this critical section. * In most cases, xc_attention() is already held by the caller * at this point. If so, this call to xc_attention() is very * lightweight, and only increments the nesting count. */ xc_attention(); #endif MULTIPROCESSOR /* * We need to preserve the notion of host-chokes-frontend * before touching the PF. */ if (fe->fe_hold & FE_HOLD) { while (!(fe->fe_hold & FE_HLDA)) DELAY(2); } save->gc_fe_hold = fe->fe_hold & FE_HOLD; /* * First check if current host FB state set is 1. If so then * save it and change the host FB state to 0. */ save->gc_rp_host_csr = rp->rp_host_csr_reg; if (rp->rp_host_csr_reg & 0x1) rp->rp_host_csr_reg &= 0xfffffffe; save->gc_rp_host_as = rp->rp_host_as_reg; save->gc_fbi_vwclp_x = fb->fbi_reg_vwclp_x; save->gc_fbi_vwclp_y = fb->fbi_reg_vwclp_y; save->gc_fbi_fb_width = fb->fbi_reg_fb_width; save->gc_fbi_buf_sel = fb->fbi_reg_buf_sel; save->gc_fbi_stereo_cl = fb->fbi_reg_stereo_cl; save->gc_fbi_cur_wid = fb->fbi_reg_cur_wid; save->gc_fbi_wid_ctrl = fb->fbi_reg_wid_ctrl; save->gc_fbi_wbc = fb->fbi_reg_wbc; save->gc_fbi_con_z = fb->fbi_reg_con_z; save->gc_fbi_z_ctrl = fb->fbi_reg_z_ctrl; save->gc_fbi_i_wmask = fb->fbi_reg_i_wmask; save->gc_fbi_w_wmask = fb->fbi_reg_w_wmask; save->gc_fbi_b_mode = fb->fbi_reg_b_mode; save->gc_fbi_b_wmask = fb->fbi_reg_b_wmask; save->gc_fbi_rop = fb->fbi_reg_rop; save->gc_fbi_mpg = fb->fbi_reg_mpg; save->gc_fbi_s_mask = fb->fbi_reg_s_mask; save->gc_fbi_fg_col = fb->fbi_reg_fg_col; save->gc_fbi_bg_col = fb->fbi_reg_bg_col; save->gc_fbi_s_trans = fb->fbi_reg_s_trans; save->gc_fbi_dir_size = fb->fbi_reg_dir_size; save->gc_fbi_copy_src = fb->fbi_reg_copy_src; #ifdef MULTIPROCESSOR xc_dismissed(); #endif MULTIPROCESSOR } /* * Restore the state set 0 registers. */ static void gt_cntxrestore(softc, save) struct gt_softc *softc; struct gt_cntxt *save; { struct rp_host *rp; struct fbi_reg *fb; struct fe_i860_sp *fe; unsigned dummy; #ifdef GT_DEBUG gtstat.gs_cntxrestore++; #endif GT_DEBUG rp = (struct rp_host *) softc->va[MAP_RP_HOST_CTRL]; fb = (struct fbi_reg *) softc->va[MAP_FBI_REG]; fe = (struct fe_i860_sp *)softc->va[MAP_FE_I860]; #ifdef MULTIPROCESSOR /* * Idle all other processors during this critical section. * In most cases, xc_attention() is already held by the caller * at this point. If so, this call to xc_attention() is very * lightweight, and only increments the nesting count. */ xc_attention(); #endif MULTIPROCESSOR /* * If the new context had placed a hold on the i860 frontend, * we need to restore that hold and block until it is * acknowledged. */ fe->fe_hold |= save->gc_fe_hold; if (fe->fe_hold & FE_HOLD) { while (!(fe->fe_hold & FE_HLDA)) DELAY(2); } /* * After restoring the RP HOST CSR, force the state set * to 0 before restoring the remaining registers. After * restoring these, we re-restore the RP HOST CSR to set * the correct state set. */ rp->rp_host_csr_reg = save->gc_rp_host_csr & 0xfffffffe; /* * To workaround a bug in the PBM, we do a dummy read of * the PP Image Write Mask register. If we just restored * the Host Stalling RP bit in the RP CSR, the dummy read * will block until the stall completes. */ dummy = fb->fbi_reg_i_wmask; dummy = dummy+1; /* makes lint happier with dummy */ rp->rp_host_as_reg = save->gc_rp_host_as; fb->fbi_reg_vwclp_x = save->gc_fbi_vwclp_x; fb->fbi_reg_vwclp_y = save->gc_fbi_vwclp_y; fb->fbi_reg_fb_width = save->gc_fbi_fb_width; fb->fbi_reg_buf_sel = save->gc_fbi_buf_sel; fb->fbi_reg_stereo_cl = save->gc_fbi_stereo_cl; fb->fbi_reg_cur_wid = save->gc_fbi_cur_wid; fb->fbi_reg_wid_ctrl = save->gc_fbi_wid_ctrl; fb->fbi_reg_wbc = save->gc_fbi_wbc; fb->fbi_reg_con_z = save->gc_fbi_con_z; fb->fbi_reg_z_ctrl = save->gc_fbi_z_ctrl; fb->fbi_reg_i_wmask = save->gc_fbi_i_wmask; fb->fbi_reg_w_wmask = save->gc_fbi_w_wmask; fb->fbi_reg_b_mode = save->gc_fbi_b_mode; fb->fbi_reg_b_wmask = save->gc_fbi_b_wmask; fb->fbi_reg_rop = save->gc_fbi_rop; fb->fbi_reg_mpg = save->gc_fbi_mpg; fb->fbi_reg_s_mask = save->gc_fbi_s_mask; fb->fbi_reg_fg_col = save->gc_fbi_fg_col; fb->fbi_reg_bg_col = save->gc_fbi_bg_col; fb->fbi_reg_s_trans = save->gc_fbi_s_trans; fb->fbi_reg_dir_size = save->gc_fbi_dir_size; fb->fbi_reg_copy_src = save->gc_fbi_copy_src; rp->rp_host_csr_reg = save->gc_rp_host_csr; #ifdef MULTIPROCESSOR xc_dismissed(); #endif MULTIPROCESSOR } static struct at_proc * gta_findproc(softc) struct gt_softc *softc; { struct proc *p; struct at_proc *ap, *ap_head; p = u.u_procp; ap_head = softc->at_head; ap = GT_LIST_NEXT(ap_head, at_proc, ap_list); while (ap != ap_head) { if ((ap->ap_proc == p) && (ap->ap_pid == p->p_pid)) return (ap); ap = GT_LIST_NEXT(ap, at_proc, ap_list); } return ((struct at_proc *) NULL); } static struct at_proc * gta_findas(as) struct as *as; { struct gt_softc *softc; struct at_proc *ap, *ap_head; for (softc=gt_softc; softc < >_softc[NGT]; softc++) { ap_head = softc->at_head; ap = GT_LIST_NEXT(ap_head, at_proc, ap_list); while (ap != ap_head) { if ((ap->ap_as == as) && (ap->ap_proc == pfind(ap->ap_pid))) return (ap); ap = GT_LIST_NEXT(ap, at_proc, ap_list); } } return ((struct at_proc *) NULL); } static gt_validthread(softc, thread) struct gt_softc *softc; struct at_proc *thread; { struct at_proc *ap, *ap_head; ap_head = softc->at_head; ap = GT_LIST_NEXT(ap_head, at_proc, ap_list); while (ap != ap_head) { if ((ap == thread) && (ap->ap_proc == pfind(ap->ap_pid))) return (1); ap = GT_LIST_NEXT(ap, at_proc, ap_list); } return (0); } /* * The GT pagein daemon, which runs as a kernel process. */ static void gt_pagedaemon() { struct gt_fault *gfp = >_fault; struct gt_softc *softc; fe_ctrl_sp *fep; caddr_t wchan; gt_daemon_init("gtdaemon"); gt_pagedaemonpid = u.u_procp->p_pid; wchan = (caddr_t) >_daemonflag; (void) splvm(); (void) splr(gt_priority); /* * Sleep until woken to resolve a page fault or to allocate * a new level one page table. We do the page table allocation * in this thread so that getpages() can sleep, if necessary, * and return when the page has been rmalloc'ed. * * Until the request is satisfied the GT MMU is kept frozen. */ for (;;) { while (gfp->gf_type == 0) (void) sleep(wchan, PZERO+1); softc = gfp->gf_softc; fep = softc->fe_ctrl_sp_ptr; if (gfp->gf_type & GF_LVL1_FAULT) { if (gt_badfault()) gta_abortfault(gfp->gf_at, GT_ABORT_LVL1); else gt_lvl1fault(); } if (gfp->gf_type & GF_LVL2_FAULT) { if (gt_badfault()) { gta_abortfault(gfp->gf_at, GT_ABORT_TLB); } else { gt_pagefault(); } } if (gfp->gf_type & GF_SENDSIGNAL) { struct at_proc *ap_head, *ap; ap_head = softc->at_head; ap = GT_LIST_NEXT(ap_head, at_proc, ap_list); while (ap != ap_head) { if (ap->ap_flags & GTA_SIGNAL) { if (!gt_badfault()) psignal(ap->ap_proc, SIGXCPU); ap->ap_flags &= ~GTA_SIGNAL; } ap = GT_LIST_NEXT(ap, at_proc, ap_list); } } /* * Unfreeze the ATU and clear the interrupt condition * XXX: Do we really need to clear the TLB? */ bzero((caddr_t)gfp, sizeof (*gfp)); softc->flags &= ~(F_ATU_FAULTING | F_LVL1_MISS); fep->fe_atu_syncr |= ATU_CLEAR; gt_sync = fep->fe_atu_syncr; gt_thaw_atu(softc, (F_FREEZE_TLB | F_FREEZE_LVL1 | F_FREEZE_SIG)); } } static gt_badfault() { struct proc *p; struct at_proc *ap; struct gt_fault *gfp = >_fault; if ((ap=gfp->gf_at) == (struct at_proc *)NULL) return (1); p = ap->ap_proc; if ((gfp->gf_type & GF_BOGUS) || (p != pfind(gfp->gf_pid)) || (p->p_flag & SWEXIT) || (ap->ap_flags & GTA_EXIT)) return (1); else return (0); } static void gt_lvl1fault() { struct gt_fault *gfp = >_fault; struct gt_lvl2 *lvl2; /* * Allocate a lvl 2 page table. Make the page table uncached, * and construct its entry in the level 1 table. */ lvl2 = (struct gt_lvl2 *) getpages(btopr(sizeof (struct gt_lvl2)), 0); gt_setnocache((addr_t)lvl2); bzero((caddr_t)lvl2, sizeof (struct gt_lvl2)); gfp->gf_lvl1->gt_lvl2[gfp->gf_index] = lvl2; gfp->gf_ptp->ptp_base = GT_KVTOP((int)lvl2) >> GT_PAGESHIFT; gfp->gf_ptp->ptp_v = 1; } static void gt_pagefault() { struct gt_fault *gfp = >_fault; struct at_proc *ap; struct as *as; struct seg *seg, *sega; struct segvn_data *svd; struct page *pp; struct anon **app; struct gta_lockmap *map; #ifndef sun4m struct ctx *octx; #else u_int octx; #endif sun4m addr_t a_addr, x_addr; faultcode_t fault_err; int i, page, err; u_int off, dummy, lookahead, pagecnt, faultsize; u_int lvl1, lvla; int prefault; int s; extern struct modinfo mod_info[]; ap = gfp->gf_at; as = ap->ap_as; gt_aslock(as); octx = mmu_getctx(); hat_setup(as); a_addr = (addr_t) ((int)(gfp->gf_addr) & PAGEMASK); /* * We'll consider prefaulting if the currently-faulted page * is one page higher than the previously-faulted page. */ if (gta_premap) { if (a_addr == ap->ap_lastfault+PAGESIZE) prefault = 1; else prefault = 0; } else prefault = 0; seg = as_segat(as, a_addr); /* * Transmogrify a vnode segment for use by the GT accelerator? */ if (seg && (seg->s_ops == &segvn_ops)) seg->s_ops = &seggta_ops; /* * Is this an unresolvable fault? */ if (seg == NULL || ((seg->s_ops != &seggta_ops) && (seg->s_ops != &seggtx_ops))) { gta_abortfault(ap, GT_ABORT_BADADDR); goto out; } /* * Shall we prefault? We constrain the prefault to not * extend beyond the mappings of a GT MMU level 2 table. */ if (prefault) { lookahead = gta_premap * PAGESIZE; pagecnt = gta_premap+1; lvl1 = (u_int)a_addr & (INDX1_MASK<ap_lastfault = a_addr + faultsize - PAGESIZE; /* * Let the vnode segfault handler do its thing. */ fault_err = as_fault(as, a_addr, faultsize, F_SOFTLOCK, S_OTHER); if (fault_err) { gta_abortfault(ap, GT_ABORT_FAULTERR); goto out; } x_addr = a_addr; for (i=0; is_data; off = svd->offset + ((x_addr-seg->s_base) & PAGEMASK); if (svd->amp == NULL) { pp = page_find(svd->vp, off); } else { AMAP_LOCK(svd->amp) app = &svd->amp->anon[svd->anon_index + page]; err = anon_getpage(app, &dummy, (struct page **) NULL, PAGESIZE, seg, x_addr, S_WRITE, svd->cred); if (err) { printf("gt_pagedaemon: err = 0x%X\n", FC_MAKE_ERR(err)); panic("gt_pagedaeon: anon page"); } pp = (*app)->un.an_page; AMAP_UNLOCK(svd->amp) } if (pp == (struct page *)NULL) panic("gt_pagedaemon: cannot find page"); (void) page_pp_lock(pp, 0, 0); /* * If our system has a VAC, make the page uncached, * but if we have a PAC, leave it cached. */ s = splvm(); #ifdef sun4c gt_setnocache(x_addr); vac_pageflush(x_addr); #elif defined(sun4m) if ((mod_info[0].mod_type != CPU_VIKING) && (mod_info[0].mod_type != CPU_VIKING_MXCC)) { panic("gt_pagefault: sun4m/ross not supported"); } #endif sun4c (void) splx(s); /* * Now load the translation into the GT MMU */ gt_mmuload(ap, x_addr, pp, seg, i); } } #ifndef sun4m /* * Make sure that the process's context isn't clean */ { struct as *a; struct ctx *c; a = ap->ap_proc->p_as; if (a != (struct as *)NULL) { c = a->a_hat.hat_ctx; if (c != (struct ctx *)NULL) { c->c_clean = 0; } } } #endif sun4m /* * If this is the first time a page has been mapped for this * thread, lock the process against swapping and start the * hat callbacks. We need the callbacks so that we can tell * if the process is exiting before hitting some nasty * assertion checks at hat_free time. */ if (!(ap->ap_flags & GTA_SWLOCK)) { gt_set_flags(&ap->ap_flags, GTA_SWLOCK); ap->ap_proc->p_swlocks++; as->a_hatcallback = 1; } (void) as_fault(as, a_addr, faultsize, F_SOFTUNLOCK, S_OTHER); out: mmu_setctx(octx); gt_asunlock(as); } static void gta_abortfault(ap, reason) struct at_proc *ap; enum abort_types reason; { struct gt_softc *softc = ap->ap_softc; fe_page_1 *fp1 = (fe_page_1 *)softc->va[MAP_FE_HFLAG1]; fe_page_3 *fp3 = (fe_page_3 *)softc->va[MAP_FE_HFLAG3]; switch (reason) { #ifdef GT_DEBUG case GT_ABORT_LVL1: printf("gt_abortfault: level 1 miss\n"); break; case GT_ABORT_TLB: printf("gt_abortfault: tlb miss\n"); break; case GT_ABORT_FAULTERR: printf("gt_abortfault: fault error\n"); break; #endif GT_DEBUG case GT_ABORT_BADADDR: printf("gt_abortfault: invalid address: 0x%X\n", gt_fault.gf_addr); break; } if (gt_validthread(softc, ap)) { gt_set_flags(&ap->ap_flags, (GTA_ABORT | GTA_DONTRUN)); if (!(ap->ap_proc->p_flag & SWEXIT)) psignal(ap->ap_proc, SIGTERM); gta_exit(ap); } fp1->fe_rootptp = (u_int) gta_dummylvl1 | ROOTVALID; gt_clear_flags(&softc->flags, F_THREAD_RUNNING); softc->at_curr = softc->at_head; fp3->fe_hflag_3 = 1; gt_sync = fp3->fe_hflag_3; } static void gt_mmuload(ap, addr, pp, seg, premapflag) struct at_proc *ap; addr_t addr; struct page *pp; struct seg *seg; int premapflag; { struct gt_pte *pte; struct gt_ptp *ptp; struct gta_mmudata mmudata; u_int ppn, gtppn; int i; /* * Sanity checks */ if ((pp == NULL) || pp->p_free) panic("gt mmuload"); /* * Find the physical page number, relative to the gt's pagesize. */ ppn = page_pptonum(pp); gtppn = ppn * GT_PAGE_RATIO; /* * If we are running on a sun4c, we must shunt GT VM accesses * through a context zero window in kernel space. Set up the * context zero translation, and use the virtual address of the * kernel window in the GT page table in lieu of the physical * address of the page. */ pte = gta_getmmudata(ap, addr, &mmudata); ptp = &((mmudata.gtu_lvl1)->gt_ptp[mmudata.gtu_index1]); /* * Increment ptp_cnt before calling gta_locksetup() so * that the target level 2 page table won't be freed * if gta_locksetup() unloads the only current * translation in it. */ ptp->ptp_cnt += GT_PAGE_RATIO; #ifdef sun4c ppn = gta_locksetup(seg, addr, pp, premapflag); gtppn = ppn * GT_PAGE_RATIO; #elif defined(sun4m) (void) gta_locksetup(seg, addr, pp, premapflag); #endif sun4c /* * Load gt translations for all gt pages spanned by the * corresponding host page. */ for (i=0; ipte_ppn = gtppn; pte->pte_u = 1; pte->pte_r = 0; pte->pte_m = 0; pte->pte_v = 1; pte++; gtppn++; } } /* * Allocate a lockmap element. * * If we are on a sun4c, set up a translation to the specified * page through the GT DVMA window. */ static u_int gta_locksetup(seg, addr, pp, premapflag) struct seg *seg; caddr_t addr; struct page *pp; int premapflag; { struct gta_lockmap *map; struct as *as; caddr_t xaddr; int i; #ifdef sun4c int index; #endif sun4c extern struct seg kseg; xaddr = (caddr_t) ((u_int)addr & PAGEMASK); /* * Allocate a window. Take one from the free list if possible. * Otherwise reclaim one from the end of the active list to * the free list. */ for (;;) { map = GT_LIST_NEXT(>a_lockfree, gta_lockmap, gtm_list); if (map != >a_lockfree) { break; } else { map = GT_LIST_PREV(>a_lockbusy, gta_lockmap, gtm_list); for (i = 0; i < (premapflag-1); i++) map = GT_LIST_PREV(map, gta_lockmap, gtm_list); gta_unload(map->gtm_as, map->gtm_addr, MMU_PAGESIZE); #ifdef GT_DEBUG gtstat.gs_mapsteal++; #endif GT_DEBUG } } /* * At this time, we are guaranteed that this map element * is on the free list. */ GT_LIST_REMOVE(map, gta_lockmap, gtm_list); /* * Map the chosen window to the specified physical page. */ as = seg->s_as; map->gtm_as = as; map->gtm_addr = xaddr; map->gtm_pp = pp; pg_setref(pp, 1); #ifdef sun4c index = (int)(map-gta_lockmapbase); xaddr = gta_dvmaspace + (index << MMU_PAGESHIFT); hat_memload(&kseg, xaddr, pp, PROT_READ|PROT_WRITE, 1); gt_setnocache(xaddr); vac_pageflush(xaddr); #endif /* sun4c */ /* * Insert the element into the hash list and into the * the active list. If this is a pre-mapped page, we * put it at the end of the active list; otherwise we * put it at the beginning. */ gta_hash_insert(map); if (premapflag == 0) { GT_LIST_INSERT_NEXT(>a_lockbusy, map, gta_lockmap, gtm_list); } else { GT_LIST_INSERT_PREV(>a_lockbusy, map, gta_lockmap, gtm_list); } return ((u_int)xaddr >> MMU_PAGESHIFT); } static struct gt_pte * gta_getmmudata(ap, addr, gtu) struct at_proc *ap; addr_t addr; struct gta_mmudata *gtu; { /* * Generate the level 1 and level 2 page table indices * and the level 1 and 2 page table addresses. */ gtu->gtu_index1 = ((u_int)addr >> (INDX1_SHIFT+2)) & 0x3ff; gtu->gtu_index2 = ((u_int)addr >> (INDX2_SHIFT+2)) & 0x3ff; gtu->gtu_lvl1 = (struct gt_lvl1 *)ap->ap_lvl1; gtu->gtu_lvl2 = gtu->gtu_lvl1->gt_lvl2[gtu->gtu_index1]; gtu->gtu_pte = >u->gtu_lvl2->gt_pte[gtu->gtu_index2]; if (((u_int)addr % MMU_PAGESIZE) >= GT_PAGESIZE) gtu->gtu_pte--; return (gtu->gtu_pte); } /* * The GT accelerator's scheduler, which runs as a kernel process */ static void gt_scheduler() { struct gt_softc *softc; struct at_proc *ap; struct at_proc *cp; caddr_t wchan; gt_daemon_init("gtscheduler"); /* * The scheduler uses the first softc's wchan */ wchan = (caddr_t) >_softc[0].wchan[WCHAN_SCHED]; (void) splr(gt_priority); for (;;) { #ifdef GT_DEBUG gtstat.gs_scheduler++; #endif GT_DEBUG for (softc=gt_softc; softc < >_softc[NGT]; softc++) { /* * If the GT microcode isn't healthy we may as well * go back to sleep. If it has just stopped, we * need to reset the accelerator and kill any * accelerator threads. */ if ((softc->flags & F_UCODE_STOPPED) || !(softc->flags & F_UCODE_RUNNING)) { if (softc->flags & F_UCODE_STOPPED) { softc->at_curr = softc->at_head; gta_reset_accelerator(softc); gt_clear_flags(&softc->flags, (F_UCODE_STOPPED|F_UCODE_RUNNING)); } continue; } /* * Run through the kmcb request queue. When we * return from gt_schedkmcb() the kmcb channel * may be either active or idle. */ gt_schedkmcb(softc); /* * Suspend the current thread? */ if ((softc->flags & F_SUSPEND) || (softc->at_curr->ap_flags & GTA_BLOCKED)) { gta_stop(softc); } if (softc->flags & (F_DONTSCHEDULE|F_ATU_FROZEN)) continue; /* * If the current process is runnable, but * stopped, let it proceed. */ if (!(softc->at_curr->ap_flags & GTA_BLOCKED) && (softc->kmcb.gt_stopped & HK_KERNEL_STOPPED)) { if (softc->flags & F_THREAD_RUNNING) { softc->kmcb.command = 0; softc->fe_ctrl_sp_ptr->fe_hflag_0 = 1; gt_sync = softc->fe_ctrl_sp_ptr->fe_hflag_0; } else gta_start(softc, softc->at_curr); continue; } /* * Look for the next roundrobin candidate. If the * next runnable process is not the current process, * stop the current process and start the next one. * If the next runnable process is the current one, * just let it keep running. */ cp = softc->at_curr; ap = GT_LIST_NEXT(cp, at_proc, ap_list); for (; ap!=cp;) { if (!(ap->ap_flags & GTA_BLOCKED)) { #ifdef GT_DEBUG gtstat.gs_threadswitch++; #endif GT_DEBUG gta_stop(softc); if (gt_validthread(softc, ap)) { gta_start(softc, ap); break; } else ap = softc->at_head; } ap = GT_LIST_NEXT(ap, at_proc, ap_list); } } /* * Until next time... */ timeout(gt_timeout, wchan, gt_quantum ? gt_quantum:GT_QUANTUM); (void) sleep(wchan, PZERO+1); /* * The scheduler may have been woken by some other means * than by timeout. The untimeout ensures that we don't * accrue multiple timeouts for the scheduler. */ untimeout(gt_timeout, wchan); } } static void gt_schedkmcb(softc) struct gt_softc *softc; { struct fe_ctrl_sp *fep = softc->fe_ctrl_sp_ptr; struct Hkvc_kmcb *kmcb = &softc->kmcb; struct Hkvc_kmcb *qmcb; struct gt_kmcblist *kptr, *next; int setpulse; /* * Is it time to check the frontend's pulse? We do this * if its been GT_PULSERATE seconds beyond the last time * we've conducted a kmcb transaction with it. */ if ((time.tv_sec >= gt_pulse+GT_PULSERATE) && !(softc->flags & F_PULSED)) { kptr = (struct gt_kmcblist *) new_kmem_zalloc(sizeof (*kptr), KMEM_SLEEP); qmcb = &kptr->q_mcb; qmcb->command = HKKVC_PAUSE_WITH_INTERRUPT; GT_LIST_INSERT_PREV(>_kmcbhead, kptr, gt_kmcblist, q_list); gt_pulse = time.tv_sec; setpulse = 1; } else setpulse = 0; /* * Run through the kmcb request queue */ next = GT_LIST_NEXT(>_kmcbhead, gt_kmcblist, q_list); while (next != >_kmcbhead) { kptr = next; next = GT_LIST_NEXT(next, gt_kmcblist, q_list); /* * If the kernel-to-hawk VCR is not clear, the GT isn't * finished with the last command, so we try again later. */ if (fep->fe_hflag_0) break; /* * Has this kmcb request already been posted? * If so, its already been acknowledged so we * can free the request element. */ if (kptr->q_flags & KQ_POSTED) { untimeout(gt_kmcbtimeout, (caddr_t)softc); gt_clear_flags(&softc->flags, F_PULSED); GT_LIST_REMOVE(kptr, gt_kmcblist, q_list); if (kptr->q_flags & KQ_BLOCK) wakeup((caddr_t)kptr); kmem_free((caddr_t)kptr, sizeof (*kptr)); next = GT_LIST_NEXT(>_kmcbhead, gt_kmcblist, q_list); continue; } /* * Copy the enqueued request to the kmcb */ qmcb = &kptr->q_mcb; kmcb->command = qmcb->command; kmcb->wlut_image_bits = qmcb->wlut_image_bits; kmcb->wlut_info = qmcb->wlut_info; kmcb->clut_info = qmcb->clut_info; kmcb->fb_width = qmcb->fb_width; kmcb->screen_width = qmcb->screen_width; kmcb->screen_height = qmcb->screen_height; kmcb->light_pen_enable = qmcb->light_pen_enable; kmcb->light_pen_distance = qmcb->light_pen_distance; kmcb->light_pen_time = qmcb->light_pen_time; /* * Post the request to the GT */ if ((softc->flags & F_DONTSCHEDULE) || !(softc->flags & F_THREAD_RUNNING)) kmcb->command |= HKKVC_PAUSE_WITH_INTERRUPT; kptr->q_flags |= KQ_POSTED; fep->fe_hflag_0 = 1; gt_sync = fep->fe_hflag_0; gt_pulse = time.tv_sec; #ifdef GT_DEBUG gtstat.gs_kmcb_cmd++; #endif GT_DEBUG } if (setpulse) { timeout(gt_kmcbtimeout, (caddr_t)softc, GT_KMCBTIMEOUT); gt_set_flags(&softc->flags, F_PULSED); } } /* * Idle the accelerator, leaving the current thread, if any, intact. */ static void gta_idle(softc, flag) struct gt_softc *softc; unsigned flag; { u_int cmd; if (softc->flags & F_UCODE_RUNNING) { gt_set_flags(&softc->flags, flag); cmd = HKKVC_FLUSH_RENDERING_PIPE | HKKVC_PAUSE_WITH_INTERRUPT; (void) gt_post_command(softc, cmd, (Hkvc_kmcb *)0); } } /* * Let the accelerator continue running. */ static void gta_run(softc, flag) struct gt_softc *softc; unsigned flag; { gt_clear_flags(&softc->flags, flag); wakeup(&softc->wchan[WCHAN_SCHED]); } /* * Stop the current accelerator thread */ static void gta_stop(softc) struct gt_softc *softc; { fe_ctrl_sp *fep = softc->fe_ctrl_sp_ptr; fe_page_1 *fp1 = (fe_page_1 *)softc->va[MAP_FE_HFLAG1]; fe_page_2 *fp2 = (fe_page_2 *)softc->va[MAP_FE_HFLAG2]; struct at_proc *ap = softc->at_curr; struct seg *ssp; struct seg *usp; int exitflag = 0; if (softc->flags & F_THREAD_RUNNING) { if (gt_validthread(softc, ap)) { if ((ap->ap_proc->p_flag & SWEXIT) || (ap->ap_flags & GTA_EXIT)) exitflag = 1; } usp = softc->uvcrseg; ssp = softc->uvcrseg; if (!exitflag) { if (usp) hat_unload(usp, usp->s_base, usp->s_size); if (ssp) hat_unload(ssp, ssp->s_base, ssp->s_size); } softc->uvcrseg = (struct seg *)NULL; softc->svcrseg = (struct seg *) NULL; if (!(ap->ap_flags & GTA_ABORT)) { /* * Wait for the kmcb channel to be clear */ gta_kmcbwait(softc); /* * Flush the graphics pipeline, the context if * so requested, and halt. */ gt_wait_for_thaw(softc); softc->kmcb.command = HKKVC_FLUSH_RENDERING_PIPE | HKKVC_PAUSE_WITH_INTERRUPT; exitflag = 0; if (gt_validthread(softc, ap)) { if ((ap->ap_proc->p_flag & SWEXIT) || (ap->ap_flags & GTA_EXIT)) exitflag = 1; } if (!exitflag) softc->kmcb.command |= HKKVC_FLUSH_FULL_CONTEXT; if (!(softc->flags & F_UCODE_STOPPED)) fep->fe_hflag_0 = 1; gta_kmcbwait(softc); /* * Save the user and signal vcr states */ if (fp2->fe_hflag_2 & 0x1) ap->ap_flags |= GTA_UVCR; else ap->ap_flags &= ~GTA_UVCR; if (fp1->fe_hflag_1 & 0x1) ap->ap_flags |= GTA_SVCR; else ap->ap_flags &= ~GTA_SVCR; gt_wait_for_thaw(softc); gta_loadroot(softc, (struct gt_lvl1 *)NULL, MIA_CLEARROOT); } softc->at_curr = softc->at_head; gt_clear_flags(&softc->flags, (F_THREAD_RUNNING | F_SUSPEND)); wakeup(&softc->wchan[WCHAN_SUSPEND]); } } /* * Start an accelerator thread */ static void gta_start(softc, ap) struct gt_softc *softc; struct at_proc *ap; { fe_ctrl_sp *fep = softc->fe_ctrl_sp_ptr; fe_page_1 *fp1 = (fe_page_1 *)softc->va[MAP_FE_HFLAG1]; fe_page_2 *fp2 = (fe_page_2 *)softc->va[MAP_FE_HFLAG2]; gta_kmcbwait(softc); gt_wait_for_thaw(softc); if (softc->flags & F_UCODE_STOPPED) return; /* * Make sure that this is still valid. It may have disconnected * while we were asleep in kmcbwait(). */ if (gt_validthread(softc, ap) && !(ap->ap_flags & GTA_BLOCKED)) { /* * Load the root pointer into the GT, and load the * user's mcb pointer and the user's context. */ gta_loadroot(softc, ap->ap_lvl1, MIA_LOADROOT); softc->kmcb.command = HKKVC_LOAD_USER_MCB | HKKVC_LOAD_CONTEXT; softc->kmcb.user_mcb = ap->ap_umcb; softc->at_curr = ap; fp2->fe_hflag_2 = (ap->ap_flags & GTA_UVCR) ? 1:0; fp1->fe_hflag_1 = (ap->ap_flags & GTA_SVCR) ? 1:0; fep->fe_hflag_0 = 1; gt_sync = fep->fe_hflag_0; gt_set_flags(&softc->flags, F_THREAD_RUNNING); } /* * A process might be sleeping while processing an address * space fault caused by referencing the user VCRs. */ wakeup(&softc->wchan[WCHAN_VCR]); } /* * Wait for the kmcb channel to be clear */ static void gta_kmcbwait(softc) struct gt_softc *softc; { fe_ctrl_sp *fep = softc->fe_ctrl_sp_ptr; caddr_t wchan = &softc->wchan[WCHAN_SCHED]; int notfirst = 0; u_int pauseflag; pauseflag = softc->kmcb.command & HKKVC_PAUSE_WITH_INTERRUPT; while (fep->fe_hflag_0) { if (!pauseflag || notfirst) timeout(gt_timeout, wchan, gt_quantum ? gt_quantum:GT_QUANTUM); (void) sleep(wchan, PZERO+1); notfirst = 1; } } /* * Freeze the GT's ATU, invalidate its TLBs, * load a new root pointer, and release the ATU. */ static void gta_loadroot(softc, root, fcn) struct gt_softc *softc; struct gt_lvl1 *root; enum mia_ops fcn; { fe_ctrl_sp *fep = softc->fe_ctrl_sp_ptr; fe_page_1 *fp1 = (fe_page_1 *)softc->va[MAP_FE_HFLAG1]; u_int rootptp; gt_freeze_atu(softc, F_FREEZE_LDROOT); gt_set_flags(&fep->fe_atu_syncr, ATU_CLEAR); switch (fcn) { case MIA_LOADROOT: rootptp = (u_int)GT_KVTOP((int)root) | ROOTVALID; break; case MIA_CLEARROOT: rootptp = (u_int)NULL; break; default: panic("gta_loadroot: bad argument"); break; } fp1->fe_rootptp = rootptp; gt_sync = fp1->fe_rootptp; gt_thaw_atu(softc, F_FREEZE_LDROOT); } /* * Initialize GT accelerator structures */ static void gta_init() { struct gta_lockmap *nextmap; struct gt_lvl1 *lvl1; struct gt_lvl2 *lvl2; caddr_t pp; u_int nbytes; int i; #ifdef sun4c u_long kmx; #endif sun4c /* * Start the daemons that service the accelerator */ kern_proc(gt_pagedaemon, 0); kern_proc(gt_scheduler, 0); #ifdef sun4c /* * XXX: pretty crufty allocation of kernel address space */ retry: kmx = rmalloc(kernelmap, (long)btoc(gta_dvmasize)); if (kmx == (u_long) 0) { gta_dvmasize -= 1*MEG; if (gta_dvmasize <= 1*MEG) panic("gta_init: cannot allocate space for GT DVMA"); goto retry; } gta_dvmaspace = (caddr_t) kmxtob(kmx); (void) seg_attach(&kas, gta_dvmaspace, gta_dvmasize, >dvmaseg); (void) segkmem_create(>dvmaseg, (caddr_t)NULL); hat_reserve(>dvmaseg, gta_dvmaspace, gta_dvmasize); gta_lockents = btoc(gta_dvmasize); #else if (gta_dvmasize > gta_locksize) gta_locksize = gta_dvmasize; gta_lockents = btoc(gta_locksize); #endif /* sun4c */ /* * Allocate and initialize the GT DVMA map */ GT_LIST_INIT(>a_lockbusy, gta_lockmap, gtm_list); GT_LIST_INIT(>a_lockfree, gta_lockmap, gtm_list); nbytes = sizeof (struct gta_lockmap) * gta_lockents; gta_lockmapbase = (struct gta_lockmap *) new_kmem_zalloc(nbytes, KMEM_NOSLEEP); nextmap = gta_lockmapbase; for (i=0; igt_ptp[i].ptp_base = (unsigned)lvl2>>GT_PAGESHIFT; lvl1->gt_ptp[i].ptp_v = 1; lvl1->gt_ptp[i].ptp_cnt = GT_ENTRIES_PER_LVL2; } for (i = 0; i < GT_ENTRIES_PER_LVL2; i++) { lvl2->gt_pte[i].pte_ppn = (unsigned)pp>>GT_PAGESHIFT; lvl2->gt_pte[i].pte_m = 0; lvl2->gt_pte[i].pte_r = 0; lvl2->gt_pte[i].pte_v = 1; } } /* * GT DVMA hash routines */ static void gta_hash_insert(map) struct gta_lockmap *map; { unsigned hashindex; hashindex = GT_HASH(map->gtm_addr); map->gtm_hashnext = gta_lockhash[hashindex]; gta_lockhash[hashindex] = map; } static void gta_hash_delete(map) struct gta_lockmap *map; { struct gta_lockmap *node, *trailnode; unsigned hashindex; hashindex = GT_HASH(map->gtm_addr); node = gta_lockhash[hashindex]; trailnode = (struct gta_lockmap *)NULL; while (node) { if (node == map) { if (trailnode) trailnode->gtm_hashnext = node->gtm_hashnext; else gta_lockhash[hashindex] = node->gtm_hashnext; return; } trailnode = node; node = node->gtm_hashnext; } } static struct gta_lockmap * gta_hash_find(addr, as) addr_t addr; struct as *as; { unsigned hashindex; struct gta_lockmap *node; hashindex = GT_HASH(addr); node = gta_lockhash[hashindex]; while (node) { if ((node->gtm_addr == addr) && (node->gtm_as == as)) return (node); node = node->gtm_hashnext; } return (NULL); } /* * Initialize a GT daemon */ static void gt_daemon_init(name) char *name; { struct proc *p = u.u_procp; int i; extern struct vnode *rootdir; (void) strcpy(u.u_comm, name); relvm(u.u_procp); VN_RELE(u.u_cdir); u.u_cdir = rootdir; VN_HOLD(u.u_cdir); if (u.u_rdir) VN_RELE(u.u_rdir); u.u_rdir = NULL; /* * Close all open files */ for (i=0; ip_sig = 0; p->p_cursig = 0; p->p_sigmask = ~(sigmask(SIGKILL)|sigmask(SIGTERM)); p->p_sigignore = ~(sigmask(SIGKILL)|sigmask(SIGTERM)); p->p_sigcatch = 0; u.u_cred = crcopy(u.u_cred); u.u_groups[0] = NOGROUP; if (setjmp(&u.u_qsave)) exit(0); } /* * Insert a list element to right of node */ static void gt_listinsert_next(node, newnode) struct gt_list *node; struct gt_list *newnode; { newnode->l_prev = node; newnode->l_next = node->l_next; (node->l_next)->l_prev = newnode; node->l_next = newnode; } /* * Insert a list element to left of node */ static void gt_listinsert_prev(node, newnode) struct gt_list *node; struct gt_list *newnode; { newnode->l_next = node; newnode->l_prev = node->l_prev; (node->l_prev)->l_next = newnode; node->l_prev = newnode; } /* * Remove node from its list */ static void gt_listremove(node) struct gt_list *node; { (node->l_prev)->l_next = node->l_next; (node->l_next)->l_prev = node->l_prev; } /* * GT hardware manipulations * * - enable/disable interrupts * - freeze/thaw the ATU * - wait for RP CSR PFBUSY to be reset */ /* * Enable GT interrupts specified x. */ static void gt_enable_intr(softc, x) struct gt_softc *softc; unsigned x; { struct fe_i860_sp *fe = (struct fe_i860_sp *)softc->va[MAP_FE_I860]; u_int intr_reg; int s; /* * Enable most of the interrupts */ s = splr(gt_priority); intr_reg = softc->fe_ctrl_sp_ptr->fe_hisr; softc->fe_ctrl_sp_ptr->fe_hisr = intr_reg | HISR_INTMASK | (x); /* * spooky - This is needed, but we don't know why. * * Without the delay, the localbus timeout enable bit does * not set, and the GT starts stuttering -- sending lots * and lots of partial and repeated bits of messages to the * host. */ DELAY(1000); fe->hoint_msk = FE_LBTO; gt_sync = fe->hoint_msk; (void) splx(s); } /* * Freeze the GT ATU */ #define FREEZE_DELAY 1000 /* 1 millisecond */ static void gt_freeze_atu(softc, cond) struct gt_softc *softc; unsigned cond; { fe_ctrl_sp *fep = softc->fe_ctrl_sp_ptr; int s; s = splr(gt_priority); if ((softc->flags & F_ATU_FROZEN) == 0) { int cnt = FREEZE_DELAY; /* * We need to re-assert ATU_FREEZE when we don't see * FREEZE_ACK to avoid a race in the frontend. If * we don't do so, we might assert ATU_FREEZE whilst * the frontend is searching its TLB for a translation * that it doesn't have. In this event, FREEZE_ACK * will never get asserted. Re-asserting ATU_FREEZE * clears the condition. */ #ifdef GT_DEBUG gtstat.gs_freeze_atu++; #endif GT_DEBUG while (--cnt > 0) { fep->fe_atu_syncr |= ATU_FREEZE; if (fep->fe_hcr & FREEZE_ACK) break; usec_delay(1); } if (!(fep->fe_hcr & FREEZE_ACK)) { gt_status_dump(softc, DUMP_GT_REGS); panic("GT ATU won't freeze"); } /* * Having frozen the ATU, clear any pending ATU * interrupts. The condition can re-assert the * interrupt after we thaw. */ fep->fe_hisr &= ~(HISR_ATU|HISR_ROOT_INV|HISR_LVL1_INV|HISR_TLB_MISS); gt_sync = fep->fe_hisr; } softc->flags |= cond; (void) splx(s); } /* * Thaw the GT ATU */ static void gt_thaw_atu(softc, cond) struct gt_softc *softc; unsigned cond; { fe_ctrl_sp *fep = softc->fe_ctrl_sp_ptr; int s; s = splr(gt_priority); softc->flags &= ~cond; if (!(softc->flags&F_ATU_FROZEN)) { fep->fe_atu_syncr &= ~(ATU_FREEZE|ATU_CLEAR); gt_sync = fep->fe_atu_syncr; wakeup(&softc->wchan[WCHAN_THAW]); } (void) splx(s); } static void gt_wait_for_thaw(softc) struct gt_softc *softc; { while (softc->flags & F_ATU_FROZEN) (void) sleep(&softc->wchan[WCHAN_THAW], PZERO+1); } static void gt_set_flags(ptr, flags) unsigned *ptr; unsigned flags; { int s; s = splr(gt_priority); *ptr |= flags; (void) splx(s); } static void gt_clear_flags(ptr, flags) unsigned *ptr; unsigned flags; { int s; s = splr(gt_priority); *ptr &= ~flags; (void) splx(s); } /* * Check whether the PF (pixel formatter) is busy. * If so, spin loop a short while for nonbusy. * Returns 1 if busy, 0 otherwise. * * NB: In the mp environment, we can check that the PF is not busy, * and a moment later a user process running on another processor can * busy it. For this reason calls to gt_check_pf_busy() from kernel * mode should make the check and the subsequent actions depending * on the check an exclusive access operation by using the exclusion * primitives xc_attention() and xc_dismissed(). * * XXX - We need to do something better than printing the * persistent busy message */ static gt_check_pf_busy(softc) struct gt_softc *softc; { rp_host *rp_host_ptr = (rp_host *)softc->va[MAP_RP_HOST_CTRL]; CDELAY(!(rp_host_ptr->rp_host_csr_reg & GT_PF_BUSY), GT_SPIN_PFBUSY); if (rp_host_ptr->rp_host_csr_reg & GT_PF_BUSY) { printf("gt%d: PF persistent busy\n", softc-gt_softc); gt_status_dump(softc, DUMP_GT_REGS); return (1); } else return (0); } /* * Print useful registers for the diagnosticians. */ /*ARGSUSED*/ static void gt_status_dump(softc, flags) struct gt_softc *softc; unsigned flags; { #ifdef GT_DEBUG fe_ctrl_sp *fep = softc->fe_ctrl_sp_ptr; fe_page_1 *fp1 = (fe_page_1 *)softc->va[MAP_FE_HFLAG1]; fe_i860_sp *fe = (fe_i860_sp *)softc->va[MAP_FE_I860]; rp_host *rh = (rp_host *)softc->va[MAP_RP_HOST_CTRL]; long peekval; unsigned int hcr, hisr, atusync, par, rootindex, lvl1index; unsigned int hold, lbdes, lbseg, intstat; unsigned int su_csr, su_idesc, ew_csr, si_csr1; unsigned int si_csr2, fe_asr, fe_csr, host_asr, host_csr; struct rp_regs { u_int su_csr, pad0, su_input_data, pad1, ew_csr, si_csr1, si_csr2, pbm_fe_asr, pbm_fe_csr; } *rp = (struct rp_regs *)softc->va[MAP_RP_FE_CTRL]; if (flags & DUMP_MIA_REGS) { /* * Peek at the first register to be sure that we can read * GT registers. If we can, we can just read the others. */ if (peekl((long *) &fep->fe_hcr, &peekval) != 0) { printf("gt_status: cannot read FE registers\n"); return; } rootindex = fp1->fe_rootindex; lvl1index = fep->fe_lvl1index; par = fep->fe_par; hcr = peekval & 0xff00075b; hisr = fep->fe_hisr & 0xef0fa80f; atusync = fep->fe_atu_syncr & 0x00000003; hold = fe->fe_hold & 0x00000003; lbdes = fe->lbdes & 0x0000000f; lbseg = fe->lbseg & 0x00000030; intstat = fe->hoint_stat & 0x00000001; printf("gt fe hcr: 0x%X\n", hcr); printf(" hisr: 0x%X\n", hisr); printf(" atu sync: 0x%X\n", atusync); printf(" test reg: 0x%X\n", fep->fe_test_reg); printf(" par: 0x%X\n", par); printf(" root ptp: 0x%X\n", fp1->fe_rootptp); printf(" rootindx: 0x%X\n", rootindex); printf(" lvl1: 0x%X\n", fep->fe_lvl1); printf(" lvl1indx: 0x%X\n", lvl1index); printf(" hold: 0x%X\n", hold); printf(" lbdes: 0x%X\n", lbdes); printf(" lbseg: 0x%X\n", lbseg); printf(" intstat: 0x%X\n", intstat); printf(" lbto_err: 0x%X\n\n", fe->lb_err_addr); printf("partially decoding these, we find:\n"); if (hcr & 0xaa000000) { printf(" host flags:"); if (hcr & 0x80000000) printf(" 0"); if (hcr & 0x20000000) printf(" 1"); if (hcr & 0x08000000) printf(" 2"); if (hcr & 0x02000000) printf(" 3"); printf("\n"); } if (hcr & 0x55000000) { printf(" gt flags:"); if (hcr & 0x40000000) printf(" 0"); if (hcr & 0x10000000) printf(" 1"); if (hcr & 0x04000000) printf(" 2"); if (hcr & 0x01000000) printf(" 3"); printf("\n"); } if ((hcr & 0x0000025b) || (atusync & 0x00000003)) { printf(" atu state:"); if (hcr & 0x00000002) printf(" go"); if (hcr & 0x00000008) printf(" ss"); if (hcr & 0x00000010) printf(" jam"); if (hcr & 0x00000040) printf(" gt reset"); if (hcr & 0x00000001) printf(" fe reset"); if (atusync & 0x00000001) printf(" atu clear"); if (atusync & 0x00000002) printf(" atu freeze"); if (hcr & 0x00000200) printf(" freeze ack"); printf("\n"); } if (hold & 0x00000003) { printf(" i860 hold:"); if (hold & 0x00000001) printf(" hold"); if (hold & 0x00000002) printf(" hlda"); printf("\n"); } if (hisr & 0xe0000000) { printf(" bus-related interrupts:"); if (hisr & 0x80000000) printf(" mst0"); if (hisr & 0x40000000) printf(" berr"); if (hisr & 0x20000000) printf(" size"); printf("\n"); } if (hisr & 0x0e000000) { addr_t vaddr; vaddr = (addr_t) ((rootindex & INDX1_MASK) << INDX1_SHIFT | (lvl1index & INDX2_MASK) << INDX2_SHIFT | (par & PGOFF_MASK)); printf(" atu-related interrupts:"); if (hisr & 0x08000000) printf(" atu"); if (hisr & 0x04000000) printf(" root invalid"); if (hisr & 0x02000000) printf(" level 1 invalid"); if (hisr & 0x01000000) printf(" tlb miss"); printf("\n virtual address: 0x%X\n", vaddr); } } if (flags & DUMP_RP_REGS) { if (peekl((long *) &rp->su_csr, &peekval) != 0) { printf("gt_status: cannot read RP registers\n"); return; } su_csr = peekval & 0xb0ff7edb; su_idesc = rp->su_input_data & 0x0fff000f; ew_csr = rp->ew_csr & 0x40000fff; si_csr1 = rp->si_csr1 & 0x7fffffef; si_csr2 = rp->si_csr2 & 0x8000ffff; fe_asr = rp->pbm_fe_asr & 0x00000007; fe_csr = rp->pbm_fe_csr & 0x0000ffff; host_asr = rh->rp_host_as_reg & 0x00000007; host_csr = rh->rp_host_csr_reg & 0x0000007b; printf("gt su csr: 0x%X\n", su_csr); printf(" inp desc: 0x%X\n", su_idesc); printf(" ew csr: 0x%X\n", ew_csr); printf(" si csr 1: 0x%X\n", si_csr1); printf(" csr 2: 0x%X\n", si_csr2); printf(" fb fe asr: 0x%X\n", fe_asr); printf(" fe csr: 0x%X\n", fe_csr); printf(" host asr: 0x%X\n", host_asr); printf(" host csr: 0x%X\n", host_csr); printf("Nestled in these registers, we find:\n"); if ((su_csr & 0x00004200) || (ew_csr & 0x40000000) || (si_csr1 & 0x40000000) || (fe_csr & 0x00000804)) { printf(" interrupts enabled:"); if (su_csr&0x00004000) printf(" su input full"); if (su_csr&0x00000200) printf(" dsp input sync error"); if (ew_csr&0x40000000) printf(" ew input seq err"); if (si_csr1&0x40000000) printf(" si input seq err"); if (fe_csr & 0x00002000) printf(" rp semaphore"); if (fe_csr & 0x00000800) printf(" pick/hit"); if (fe_csr & 0x00000004) printf(" vrt"); printf("\n"); } if (su_csr & 0x0000b800) { printf(" su:"); if (su_csr & 0x00002000) printf(" input not full"); if (su_csr & 0x00000800) printf(" input data rdy"); if (su_csr & 0x00001000) printf(" output data rdy"); printf("\n"); } if (su_csr & 0x80000000) printf(" su p wait\n"); if (!(su_idesc & 0x08000000)) printf(" input fifo stage 1 full\n"); if (!(su_idesc & 0x04000000)) printf(" input fifo stage 2 empty\n"); printf(" dsp 0:"); if (su_csr & 0x00000001) printf(" run"); if (su_csr & 0x00000008) printf(" disable"); if (su_csr & 0x10000000) printf(" wait"); if (su_csr & 0x00000040) printf(" input sync err"); if (!(su_idesc & 0x00800000)) printf(" input fifo stage 2 almost full"); if (!(su_idesc & 0x00400000)) printf(" input fifo stage 2 empty"); if (!(su_idesc & 0x00040000)) printf(" output fifo full"); else if (!(su_idesc & 0x00010000)) printf(" output fifo empty"); else if (!(su_idesc & 0x00020000)) printf(" output fifo almost empty"); printf("\n"); printf(" dsp 1:"); if (su_csr & 0x00000002) printf(" run"); if (su_csr & 0x00000010) printf(" disable"); if (su_csr & 0x20000000) printf(" wait"); if (su_csr & 0x00000080) printf(" input sync err"); if (!(su_idesc & 0x02000000)) printf(" input fifo stage 2 almost full"); if (!(su_idesc & 0x01000000)) printf(" input fifo stage 2 empty"); if (!(su_idesc & 0x00200000)) printf(" output fifo full"); else if (!(su_idesc & 0x00100000)) printf(" output fifo almost empty"); else if (!(su_idesc & 0x00080000)) printf(" output fifo empty"); printf("\n"); if (ew_csr & 0x0000007f) { printf(" ew:"); if (ew_csr & 0x00000040) printf(" inp seq err"); if (ew_csr & 0x00000020) printf(" accepting data"); if (ew_csr & 0x00000010) printf(" output data rdy"); if (ew_csr & 0x00000008) printf(" held\n"); if (ew_csr & 0x00000004) printf(" reset\n"); if (ew_csr & 0x00000002) printf(" semaphore flag"); if (ew_csr & 0x00000001) printf(" antialiasing"); printf("\n"); } if (si_csr1 & 0x3000006b) { printf(" si:"); if (si_csr1 & 0x20000000) printf(" x msip almost full"); if (si_csr1 & 0x10000000) printf(" z msip almost full"); if (si_csr1 & 0x00000040) printf(" input seq err"); if (si_csr1 & 0x00000020) printf(" accepting data"); if (si_csr1 & 0x00000008) printf(" held"); if (si_csr1 & 0x00000004) printf(" reset"); if (si_csr1 & 0x00000001) printf(" semaphore flag"); printf("\n"); } if (fe_csr & 0x000017fa) { printf(" fe pbm csr:"); if (fe_csr&0x00001000) printf(" rp semaphore"); if (fe_csr&0x00000400) printf(" pick/hit"); if (fe_csr&0x00000200) printf(" pick pending"); if (fe_csr&0x00000100) printf(" si loopback rdy"); if (fe_csr&0x00000080) printf(" si loopback"); if (fe_csr&0x00000040) printf(" pf busy"); if (fe_csr&0x00000020) printf(" pb not busy"); if (fe_csr&0x00000010) printf(" fe stalling rp"); if (fe_csr&0x00000008) printf(" rp stalled"); if (fe_csr&0x00000002) printf(" vert retrace"); printf("\n"); } if (fe_csr & 0x00000001) printf(" fe fb stateset 1\n"); else printf(" fe fb stateset 0\n"); if (host_csr & 0x0000007a) { printf(" host pbm csr:"); if (host_csr&0x00000040) printf(" pf busy"); if (host_csr&0x00000020) printf(" pb not busy"); if (host_csr&0x00000010) printf(" host stalling rp"); if (host_csr&0x00000008) printf(" rp stalled"); if (host_csr&0x00000002) printf(" vert retrace"); printf("\n"); } if (host_csr & 0x00000001) printf(" host stateset 1\n"); else printf(" host stateset 0\n"); if (host_csr & 0x00000008) { printf(" rp stalled by:"); if (fe_csr & 0x00000010) printf(" fe"); if (host_csr & 0x00000010) printf(" host"); if (fe_csr & 0x00001000) printf(" rp semaphore"); if (fe_csr & 0x00000400) printf(" pick/hit"); if (fe_csr & 0x00000200) printf(" pick pending"); if (!(fe_csr & 0x00000010) && !(host_csr & 0x00000010) && !(fe_csr & 0x00001000) && !(fe_csr & 0x00000400) && !(fe_csr & 0x00000200)) printf(" wait for vertical"); printf("\n"); } } #endif GT_DEBUG } /* * lightpen routines */ struct lightpen_events *lightpen_readbuf = NULL; struct lightpen_events *lightpen_writebuf = NULL; struct lightpen_events lightpen_readbuf_A = {NULL, 0}; struct lightpen_events lightpen_readbuf_B = {NULL, 0}; int lightpen_opened = 0; store_lightpen_events(type, val) unsigned type; unsigned val; { struct lightpen_events_data *datap, *newptr, *pdatap; int s; if (!lightpen_opened) return (0); /* * Figure out which buffer to write to */ s = splr(gt_priority); lightpen_writebuf = &lightpen_readbuf_A; /* * switch buffers? */ if (lightpen_writebuf->flags & LP_READING) lightpen_writebuf = &lightpen_readbuf_B; /* yes */ (void) splx(s); /* * Traverse the list */ if (lightpen_writebuf->event_list == NULL) { /* * found the free pointer * * XXX: check 2nd arg in new_kmem_alloc(). this is called * from an interrupt thread. */ newptr = (struct lightpen_events_data *) new_kmem_alloc(sizeof (*newptr), 0); if (newptr == NULL) { printf(" Error in store_lightpen_events no memory\n"); return (1); } else { newptr->next_event = NULL; newptr->event_type = type; newptr->event_value = val; lightpen_writebuf->event_list = newptr; lightpen_writebuf->event_count++; if ((lightpen_readbuf == NULL) || (!(lightpen_readbuf->flags & LP_READING))) lightpen_readbuf = lightpen_writebuf; return (0); } } datap = lightpen_writebuf->event_list; pdatap = datap; while (datap != NULL) { pdatap = datap; datap = datap->next_event; } /* * Found a null pointer */ newptr = (struct lightpen_events_data *) new_kmem_alloc(sizeof(*newptr), 0); if (newptr == NULL) { printf(" Error in store_lightpen_events no memory\n"); return (1); } else { newptr->next_event = NULL; newptr->event_type = type; newptr->event_value = val; pdatap->next_event = newptr; lightpen_writebuf->event_count++; if ((lightpen_readbuf == NULL) || (!(lightpen_readbuf->flags & LP_READING))) lightpen_readbuf = lightpen_writebuf; return (0); } } gt_setup_bwtwo(softc) struct gt_softc *softc; { struct rp_host *rp; struct fbi_reg *fb; struct fbi_pfgo *fb_pfgo; rp = (struct rp_host *) softc->va[MAP_RP_HOST_CTRL]; fb = (struct fbi_reg *) softc->va[MAP_FBI_REG]; fb_pfgo = (struct fbi_pfgo *) softc->va[MAP_FBI_PFGO]; #ifdef MULTIPROCESSOR xc_attention(); /* Prevent users from busying the PF */ #endif MULTIPROCESSOR if (gt_check_pf_busy(softc)) { #ifdef MULTIPROCESSOR xc_dismissed(); #endif MULTIPROCESSOR return (1); } else { gt_cntxrestore(softc, &softc->cntxt); /* * Use the PF to initialize the cursor planes, as follows: * * window write mask: cd, ce only * foreground color: cd = black (0), ce = transparent (0) * direction/size: LtoR, 1280 x 1024 * fill dest addr: window, upper left corner of screen */ fb->fbi_reg_vwclp_x = softc->w - 1; fb->fbi_reg_vwclp_y = softc->h - 1; fb->fbi_reg_fb_width = 0x0; fb->fbi_reg_buf_sel = 0x1; fb->fbi_reg_stereo_cl = 0x0; fb->fbi_reg_cur_wid = 0x0; fb->fbi_reg_wid_ctrl = 0x0; fb->fbi_reg_con_z = 0x0; fb->fbi_reg_z_ctrl = 0x0; fb->fbi_reg_i_wmask = 0xffffffff; fb->fbi_reg_w_wmask = 0x0; fb->fbi_reg_b_mode = 0x3; fb->fbi_reg_b_wmask = 0xff; fb->fbi_reg_rop = 0x0c; fb->fbi_reg_mpg = 0xfc; fb->fbi_reg_w_wmask = 0xc00; fb->fbi_reg_fg_col = 0xc00; fb->fbi_reg_dir_size = 0x00200500; fb_pfgo->fbi_pfgo_fill_dest_addr = 0x00800000; if (gt_check_pf_busy(softc)) { #ifdef MULTIPROCESSOR xc_dismissed(); #endif MULTIPROCESSOR return (1); } rp->rp_host_csr_reg &= 0xfffffffe; rp->rp_host_as_reg = 0x3; fb->fbi_reg_buf_sel = 0; gt_sync = fb->fbi_reg_buf_sel; #ifdef MULTIPROCESSOR xc_dismissed(); #endif MULTIPROCESSOR return (0); } }