1203288Srnoland/*-
2203288Srnoland * Copyright 2004 The Unichrome Project. All Rights Reserved.
3203288Srnoland * Copyright 2005 Thomas Hellstrom. All Rights Reserved.
4203288Srnoland *
5203288Srnoland * Permission is hereby granted, free of charge, to any person obtaining a
6203288Srnoland * copy of this software and associated documentation files (the "Software"),
7203288Srnoland * to deal in the Software without restriction, including without limitation
8203288Srnoland * the rights to use, copy, modify, merge, publish, distribute, sub license,
9203288Srnoland * and/or sell copies of the Software, and to permit persons to whom the
10203288Srnoland * Software is furnished to do so, subject to the following conditions:
11203288Srnoland *
12203288Srnoland * The above copyright notice and this permission notice (including the
13203288Srnoland * next paragraph) shall be included in all copies or substantial portions
14203288Srnoland * of the Software.
15203288Srnoland *
16203288Srnoland * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17203288Srnoland * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18203288Srnoland * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
19203288Srnoland * THE AUTHOR(S), AND/OR THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
20203288Srnoland * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21203288Srnoland * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22203288Srnoland * DEALINGS IN THE SOFTWARE.
23203288Srnoland *
24203288Srnoland * Author: Thomas Hellstrom 2004, 2005.
25203288Srnoland * This code was written using docs obtained under NDA from VIA Inc.
26203288Srnoland *
27203288Srnoland * Don't run this code directly on an AGP buffer. Due to cache problems it will
28203288Srnoland * be very slow.
29203288Srnoland */
30203288Srnoland
31203288Srnoland#include <sys/cdefs.h>
32203288Srnoland__FBSDID("$FreeBSD$");
33203288Srnoland
34203288Srnoland#include "dev/drm/via_3d_reg.h"
35203288Srnoland#include "dev/drm/drmP.h"
36203288Srnoland#include "dev/drm/drm.h"
37203288Srnoland#include "dev/drm/via_drm.h"
38203288Srnoland#include "dev/drm/via_verifier.h"
39203288Srnoland#include "dev/drm/via_drv.h"
40203288Srnoland
41203288Srnolandtypedef enum {
42203288Srnoland	state_command,
43203288Srnoland	state_header2,
44203288Srnoland	state_header1,
45203288Srnoland	state_vheader5,
46203288Srnoland	state_vheader6,
47203288Srnoland	state_error
48203288Srnoland} verifier_state_t;
49203288Srnoland
50203288Srnolandtypedef enum {
51203288Srnoland	no_check = 0,
52203288Srnoland	check_for_header2,
53203288Srnoland	check_for_header1,
54203288Srnoland	check_for_header2_err,
55203288Srnoland	check_for_header1_err,
56203288Srnoland	check_for_fire,
57203288Srnoland	check_z_buffer_addr0,
58203288Srnoland	check_z_buffer_addr1,
59203288Srnoland	check_z_buffer_addr_mode,
60203288Srnoland	check_destination_addr0,
61203288Srnoland	check_destination_addr1,
62203288Srnoland	check_destination_addr_mode,
63203288Srnoland	check_for_dummy,
64203288Srnoland	check_for_dd,
65203288Srnoland	check_texture_addr0,
66203288Srnoland	check_texture_addr1,
67203288Srnoland	check_texture_addr2,
68203288Srnoland	check_texture_addr3,
69203288Srnoland	check_texture_addr4,
70203288Srnoland	check_texture_addr5,
71203288Srnoland	check_texture_addr6,
72203288Srnoland	check_texture_addr7,
73203288Srnoland	check_texture_addr8,
74203288Srnoland	check_texture_addr_mode,
75203288Srnoland	check_for_vertex_count,
76203288Srnoland	check_number_texunits,
77203288Srnoland	forbidden_command
78203288Srnoland} hazard_t;
79203288Srnoland
80203288Srnoland/*
81203288Srnoland * Associates each hazard above with a possible multi-command
82203288Srnoland * sequence. For example an address that is split over multiple
83203288Srnoland * commands and that needs to be checked at the first command
84203288Srnoland * that does not include any part of the address.
85203288Srnoland */
86203288Srnoland
87203288Srnolandstatic drm_via_sequence_t seqs[] = {
88203288Srnoland	no_sequence,
89203288Srnoland	no_sequence,
90203288Srnoland	no_sequence,
91203288Srnoland	no_sequence,
92203288Srnoland	no_sequence,
93203288Srnoland	no_sequence,
94203288Srnoland	z_address,
95203288Srnoland	z_address,
96203288Srnoland	z_address,
97203288Srnoland	dest_address,
98203288Srnoland	dest_address,
99203288Srnoland	dest_address,
100203288Srnoland	no_sequence,
101203288Srnoland	no_sequence,
102203288Srnoland	tex_address,
103203288Srnoland	tex_address,
104203288Srnoland	tex_address,
105203288Srnoland	tex_address,
106203288Srnoland	tex_address,
107203288Srnoland	tex_address,
108203288Srnoland	tex_address,
109203288Srnoland	tex_address,
110203288Srnoland	tex_address,
111203288Srnoland	tex_address,
112203288Srnoland	no_sequence
113203288Srnoland};
114203288Srnoland
115203288Srnolandtypedef struct {
116203288Srnoland	unsigned int code;
117203288Srnoland	hazard_t hz;
118203288Srnoland} hz_init_t;
119203288Srnoland
120203288Srnolandstatic hz_init_t init_table1[] = {
121203288Srnoland	{0xf2, check_for_header2_err},
122203288Srnoland	{0xf0, check_for_header1_err},
123203288Srnoland	{0xee, check_for_fire},
124203288Srnoland	{0xcc, check_for_dummy},
125203288Srnoland	{0xdd, check_for_dd},
126203288Srnoland	{0x00, no_check},
127203288Srnoland	{0x10, check_z_buffer_addr0},
128203288Srnoland	{0x11, check_z_buffer_addr1},
129203288Srnoland	{0x12, check_z_buffer_addr_mode},
130203288Srnoland	{0x13, no_check},
131203288Srnoland	{0x14, no_check},
132203288Srnoland	{0x15, no_check},
133203288Srnoland	{0x23, no_check},
134203288Srnoland	{0x24, no_check},
135203288Srnoland	{0x33, no_check},
136203288Srnoland	{0x34, no_check},
137203288Srnoland	{0x35, no_check},
138203288Srnoland	{0x36, no_check},
139203288Srnoland	{0x37, no_check},
140203288Srnoland	{0x38, no_check},
141203288Srnoland	{0x39, no_check},
142203288Srnoland	{0x3A, no_check},
143203288Srnoland	{0x3B, no_check},
144203288Srnoland	{0x3C, no_check},
145203288Srnoland	{0x3D, no_check},
146203288Srnoland	{0x3E, no_check},
147203288Srnoland	{0x40, check_destination_addr0},
148203288Srnoland	{0x41, check_destination_addr1},
149203288Srnoland	{0x42, check_destination_addr_mode},
150203288Srnoland	{0x43, no_check},
151203288Srnoland	{0x44, no_check},
152203288Srnoland	{0x50, no_check},
153203288Srnoland	{0x51, no_check},
154203288Srnoland	{0x52, no_check},
155203288Srnoland	{0x53, no_check},
156203288Srnoland	{0x54, no_check},
157203288Srnoland	{0x55, no_check},
158203288Srnoland	{0x56, no_check},
159203288Srnoland	{0x57, no_check},
160203288Srnoland	{0x58, no_check},
161203288Srnoland	{0x70, no_check},
162203288Srnoland	{0x71, no_check},
163203288Srnoland	{0x78, no_check},
164203288Srnoland	{0x79, no_check},
165203288Srnoland	{0x7A, no_check},
166203288Srnoland	{0x7B, no_check},
167203288Srnoland	{0x7C, no_check},
168203288Srnoland	{0x7D, check_for_vertex_count}
169203288Srnoland};
170203288Srnoland
171203288Srnolandstatic hz_init_t init_table2[] = {
172203288Srnoland	{0xf2, check_for_header2_err},
173203288Srnoland	{0xf0, check_for_header1_err},
174203288Srnoland	{0xee, check_for_fire},
175203288Srnoland	{0xcc, check_for_dummy},
176203288Srnoland	{0x00, check_texture_addr0},
177203288Srnoland	{0x01, check_texture_addr0},
178203288Srnoland	{0x02, check_texture_addr0},
179203288Srnoland	{0x03, check_texture_addr0},
180203288Srnoland	{0x04, check_texture_addr0},
181203288Srnoland	{0x05, check_texture_addr0},
182203288Srnoland	{0x06, check_texture_addr0},
183203288Srnoland	{0x07, check_texture_addr0},
184203288Srnoland	{0x08, check_texture_addr0},
185203288Srnoland	{0x09, check_texture_addr0},
186203288Srnoland	{0x20, check_texture_addr1},
187203288Srnoland	{0x21, check_texture_addr1},
188203288Srnoland	{0x22, check_texture_addr1},
189203288Srnoland	{0x23, check_texture_addr4},
190203288Srnoland	{0x2B, check_texture_addr3},
191203288Srnoland	{0x2C, check_texture_addr3},
192203288Srnoland	{0x2D, check_texture_addr3},
193203288Srnoland	{0x2E, check_texture_addr3},
194203288Srnoland	{0x2F, check_texture_addr3},
195203288Srnoland	{0x30, check_texture_addr3},
196203288Srnoland	{0x31, check_texture_addr3},
197203288Srnoland	{0x32, check_texture_addr3},
198203288Srnoland	{0x33, check_texture_addr3},
199203288Srnoland	{0x34, check_texture_addr3},
200203288Srnoland	{0x4B, check_texture_addr5},
201203288Srnoland	{0x4C, check_texture_addr6},
202203288Srnoland	{0x51, check_texture_addr7},
203203288Srnoland	{0x52, check_texture_addr8},
204203288Srnoland	{0x77, check_texture_addr2},
205203288Srnoland	{0x78, no_check},
206203288Srnoland	{0x79, no_check},
207203288Srnoland	{0x7A, no_check},
208203288Srnoland	{0x7B, check_texture_addr_mode},
209203288Srnoland	{0x7C, no_check},
210203288Srnoland	{0x7D, no_check},
211203288Srnoland	{0x7E, no_check},
212203288Srnoland	{0x7F, no_check},
213203288Srnoland	{0x80, no_check},
214203288Srnoland	{0x81, no_check},
215203288Srnoland	{0x82, no_check},
216203288Srnoland	{0x83, no_check},
217203288Srnoland	{0x85, no_check},
218203288Srnoland	{0x86, no_check},
219203288Srnoland	{0x87, no_check},
220203288Srnoland	{0x88, no_check},
221203288Srnoland	{0x89, no_check},
222203288Srnoland	{0x8A, no_check},
223203288Srnoland	{0x90, no_check},
224203288Srnoland	{0x91, no_check},
225203288Srnoland	{0x92, no_check},
226203288Srnoland	{0x93, no_check}
227203288Srnoland};
228203288Srnoland
229203288Srnolandstatic hz_init_t init_table3[] = {
230203288Srnoland	{0xf2, check_for_header2_err},
231203288Srnoland	{0xf0, check_for_header1_err},
232203288Srnoland	{0xcc, check_for_dummy},
233203288Srnoland	{0x00, check_number_texunits}
234203288Srnoland};
235203288Srnoland
236203288Srnolandstatic hazard_t table1[256];
237203288Srnolandstatic hazard_t table2[256];
238203288Srnolandstatic hazard_t table3[256];
239203288Srnoland
240203288Srnolandstatic __inline__ int
241203288Srnolandeat_words(const uint32_t ** buf, const uint32_t * buf_end, unsigned num_words)
242203288Srnoland{
243203288Srnoland	if ((buf_end - *buf) >= num_words) {
244203288Srnoland		*buf += num_words;
245203288Srnoland		return 0;
246203288Srnoland	}
247203288Srnoland	DRM_ERROR("Illegal termination of DMA command buffer\n");
248203288Srnoland	return 1;
249203288Srnoland}
250203288Srnoland
251203288Srnoland/*
252203288Srnoland * Partially stolen from drm_memory.h
253203288Srnoland */
254203288Srnoland
255203288Srnolandstatic __inline__ drm_local_map_t *via_drm_lookup_agp_map(drm_via_state_t *seq,
256203288Srnoland						    unsigned long offset,
257203288Srnoland						    unsigned long size,
258203288Srnoland						    struct drm_device * dev)
259203288Srnoland{
260203288Srnoland	drm_local_map_t *map = seq->map_cache;
261203288Srnoland
262203288Srnoland	if (map && map->offset <= offset
263203288Srnoland	    && (offset + size) <= (map->offset + map->size)) {
264203288Srnoland		return map;
265203288Srnoland	}
266203288Srnoland
267203288Srnoland	TAILQ_FOREACH(map, &dev->maplist, link) {
268203288Srnoland		if (map->offset <= offset
269203288Srnoland		    && (offset + size) <= (map->offset + map->size)
270203288Srnoland		    && !(map->flags & _DRM_RESTRICTED)
271203288Srnoland		    && (map->type == _DRM_AGP)) {
272203288Srnoland			seq->map_cache = map;
273203288Srnoland			return map;
274203288Srnoland		}
275203288Srnoland	}
276203288Srnoland	return NULL;
277203288Srnoland}
278203288Srnoland
279203288Srnoland/*
280203288Srnoland * Require that all AGP texture levels reside in the same AGP map which should
281203288Srnoland * be mappable by the client. This is not a big restriction.
282203288Srnoland * FIXME: To actually enforce this security policy strictly, drm_rmmap
283203288Srnoland * would have to wait for dma quiescent before removing an AGP map.
284203288Srnoland * The via_drm_lookup_agp_map call in reality seems to take
285203288Srnoland * very little CPU time.
286203288Srnoland */
287203288Srnoland
288203288Srnolandstatic __inline__ int finish_current_sequence(drm_via_state_t * cur_seq)
289203288Srnoland{
290203288Srnoland	switch (cur_seq->unfinished) {
291203288Srnoland	case z_address:
292203288Srnoland		DRM_DEBUG("Z Buffer start address is 0x%x\n", cur_seq->z_addr);
293203288Srnoland		break;
294203288Srnoland	case dest_address:
295203288Srnoland		DRM_DEBUG("Destination start address is 0x%x\n",
296203288Srnoland			  cur_seq->d_addr);
297203288Srnoland		break;
298203288Srnoland	case tex_address:
299203288Srnoland		if (cur_seq->agp_texture) {
300203288Srnoland			unsigned start =
301203288Srnoland			    cur_seq->tex_level_lo[cur_seq->texture];
302203288Srnoland			unsigned end = cur_seq->tex_level_hi[cur_seq->texture];
303203288Srnoland			unsigned long lo = ~0, hi = 0, tmp;
304203288Srnoland			uint32_t *addr, *pitch, *height, tex;
305203288Srnoland			unsigned i;
306203288Srnoland			int npot;
307203288Srnoland
308203288Srnoland			if (end > 9)
309203288Srnoland				end = 9;
310203288Srnoland			if (start > 9)
311203288Srnoland				start = 9;
312203288Srnoland
313203288Srnoland			addr =
314203288Srnoland			    &(cur_seq->t_addr[tex = cur_seq->texture][start]);
315203288Srnoland			pitch = &(cur_seq->pitch[tex][start]);
316203288Srnoland			height = &(cur_seq->height[tex][start]);
317203288Srnoland			npot = cur_seq->tex_npot[tex];
318203288Srnoland			for (i = start; i <= end; ++i) {
319203288Srnoland				tmp = *addr++;
320203288Srnoland				if (tmp < lo)
321203288Srnoland					lo = tmp;
322203288Srnoland				if (i == 0 && npot)
323203288Srnoland					tmp += (*height++ * *pitch++);
324203288Srnoland				else
325203288Srnoland					tmp += (*height++ << *pitch++);
326203288Srnoland				if (tmp > hi)
327203288Srnoland					hi = tmp;
328203288Srnoland			}
329203288Srnoland
330203288Srnoland			if (!via_drm_lookup_agp_map
331203288Srnoland			    (cur_seq, lo, hi - lo, cur_seq->dev)) {
332203288Srnoland				DRM_ERROR
333203288Srnoland				    ("AGP texture is not in allowed map\n");
334203288Srnoland				return 2;
335203288Srnoland			}
336203288Srnoland		}
337203288Srnoland		break;
338203288Srnoland	default:
339203288Srnoland		break;
340203288Srnoland	}
341203288Srnoland	cur_seq->unfinished = no_sequence;
342203288Srnoland	return 0;
343203288Srnoland}
344203288Srnoland
345203288Srnolandstatic __inline__ int
346203288Srnolandinvestigate_hazard(uint32_t cmd, hazard_t hz, drm_via_state_t * cur_seq)
347203288Srnoland{
348203288Srnoland	register uint32_t tmp, *tmp_addr;
349203288Srnoland
350203288Srnoland	if (cur_seq->unfinished && (cur_seq->unfinished != seqs[hz])) {
351203288Srnoland		int ret;
352203288Srnoland		if ((ret = finish_current_sequence(cur_seq)))
353203288Srnoland			return ret;
354203288Srnoland	}
355203288Srnoland
356203288Srnoland	switch (hz) {
357203288Srnoland	case check_for_header2:
358203288Srnoland		if (cmd == HALCYON_HEADER2)
359203288Srnoland			return 1;
360203288Srnoland		return 0;
361203288Srnoland	case check_for_header1:
362203288Srnoland		if ((cmd & HALCYON_HEADER1MASK) == HALCYON_HEADER1)
363203288Srnoland			return 1;
364203288Srnoland		return 0;
365203288Srnoland	case check_for_header2_err:
366203288Srnoland		if (cmd == HALCYON_HEADER2)
367203288Srnoland			return 1;
368203288Srnoland		DRM_ERROR("Illegal DMA HALCYON_HEADER2 command\n");
369203288Srnoland		break;
370203288Srnoland	case check_for_header1_err:
371203288Srnoland		if ((cmd & HALCYON_HEADER1MASK) == HALCYON_HEADER1)
372203288Srnoland			return 1;
373203288Srnoland		DRM_ERROR("Illegal DMA HALCYON_HEADER1 command\n");
374203288Srnoland		break;
375203288Srnoland	case check_for_fire:
376203288Srnoland		if ((cmd & HALCYON_FIREMASK) == HALCYON_FIRECMD)
377203288Srnoland			return 1;
378203288Srnoland		DRM_ERROR("Illegal DMA HALCYON_FIRECMD command\n");
379203288Srnoland		break;
380203288Srnoland	case check_for_dummy:
381203288Srnoland		if (HC_DUMMY == cmd)
382203288Srnoland			return 0;
383203288Srnoland		DRM_ERROR("Illegal DMA HC_DUMMY command\n");
384203288Srnoland		break;
385203288Srnoland	case check_for_dd:
386203288Srnoland		if (0xdddddddd == cmd)
387203288Srnoland			return 0;
388203288Srnoland		DRM_ERROR("Illegal DMA 0xdddddddd command\n");
389203288Srnoland		break;
390203288Srnoland	case check_z_buffer_addr0:
391203288Srnoland		cur_seq->unfinished = z_address;
392203288Srnoland		cur_seq->z_addr = (cur_seq->z_addr & 0xFF000000) |
393203288Srnoland		    (cmd & 0x00FFFFFF);
394203288Srnoland		return 0;
395203288Srnoland	case check_z_buffer_addr1:
396203288Srnoland		cur_seq->unfinished = z_address;
397203288Srnoland		cur_seq->z_addr = (cur_seq->z_addr & 0x00FFFFFF) |
398203288Srnoland		    ((cmd & 0xFF) << 24);
399203288Srnoland		return 0;
400203288Srnoland	case check_z_buffer_addr_mode:
401203288Srnoland		cur_seq->unfinished = z_address;
402203288Srnoland		if ((cmd & 0x0000C000) == 0)
403203288Srnoland			return 0;
404203288Srnoland		DRM_ERROR("Attempt to place Z buffer in system memory\n");
405203288Srnoland		return 2;
406203288Srnoland	case check_destination_addr0:
407203288Srnoland		cur_seq->unfinished = dest_address;
408203288Srnoland		cur_seq->d_addr = (cur_seq->d_addr & 0xFF000000) |
409203288Srnoland		    (cmd & 0x00FFFFFF);
410203288Srnoland		return 0;
411203288Srnoland	case check_destination_addr1:
412203288Srnoland		cur_seq->unfinished = dest_address;
413203288Srnoland		cur_seq->d_addr = (cur_seq->d_addr & 0x00FFFFFF) |
414203288Srnoland		    ((cmd & 0xFF) << 24);
415203288Srnoland		return 0;
416203288Srnoland	case check_destination_addr_mode:
417203288Srnoland		cur_seq->unfinished = dest_address;
418203288Srnoland		if ((cmd & 0x0000C000) == 0)
419203288Srnoland			return 0;
420203288Srnoland		DRM_ERROR
421203288Srnoland		    ("Attempt to place 3D drawing buffer in system memory\n");
422203288Srnoland		return 2;
423203288Srnoland	case check_texture_addr0:
424203288Srnoland		cur_seq->unfinished = tex_address;
425203288Srnoland		tmp = (cmd >> 24);
426203288Srnoland		tmp_addr = &cur_seq->t_addr[cur_seq->texture][tmp];
427203288Srnoland		*tmp_addr = (*tmp_addr & 0xFF000000) | (cmd & 0x00FFFFFF);
428203288Srnoland		return 0;
429203288Srnoland	case check_texture_addr1:
430203288Srnoland		cur_seq->unfinished = tex_address;
431203288Srnoland		tmp = ((cmd >> 24) - 0x20);
432203288Srnoland		tmp += tmp << 1;
433203288Srnoland		tmp_addr = &cur_seq->t_addr[cur_seq->texture][tmp];
434203288Srnoland		*tmp_addr = (*tmp_addr & 0x00FFFFFF) | ((cmd & 0xFF) << 24);
435203288Srnoland		tmp_addr++;
436203288Srnoland		*tmp_addr = (*tmp_addr & 0x00FFFFFF) | ((cmd & 0xFF00) << 16);
437203288Srnoland		tmp_addr++;
438203288Srnoland		*tmp_addr = (*tmp_addr & 0x00FFFFFF) | ((cmd & 0xFF0000) << 8);
439203288Srnoland		return 0;
440203288Srnoland	case check_texture_addr2:
441203288Srnoland		cur_seq->unfinished = tex_address;
442203288Srnoland		cur_seq->tex_level_lo[tmp = cur_seq->texture] = cmd & 0x3F;
443203288Srnoland		cur_seq->tex_level_hi[tmp] = (cmd & 0xFC0) >> 6;
444203288Srnoland		return 0;
445203288Srnoland	case check_texture_addr3:
446203288Srnoland		cur_seq->unfinished = tex_address;
447203288Srnoland		tmp = ((cmd >> 24) - HC_SubA_HTXnL0Pit);
448203288Srnoland		if (tmp == 0 &&
449203288Srnoland		    (cmd & HC_HTXnEnPit_MASK)) {
450203288Srnoland			cur_seq->pitch[cur_seq->texture][tmp] =
451203288Srnoland				(cmd & HC_HTXnLnPit_MASK);
452203288Srnoland			cur_seq->tex_npot[cur_seq->texture] = 1;
453203288Srnoland		} else {
454203288Srnoland			cur_seq->pitch[cur_seq->texture][tmp] =
455203288Srnoland				(cmd & HC_HTXnLnPitE_MASK) >> HC_HTXnLnPitE_SHIFT;
456203288Srnoland			cur_seq->tex_npot[cur_seq->texture] = 0;
457203288Srnoland			if (cmd & 0x000FFFFF) {
458203288Srnoland				DRM_ERROR
459203288Srnoland					("Unimplemented texture level 0 pitch mode.\n");
460203288Srnoland				return 2;
461203288Srnoland			}
462203288Srnoland		}
463203288Srnoland		return 0;
464203288Srnoland	case check_texture_addr4:
465203288Srnoland		cur_seq->unfinished = tex_address;
466203288Srnoland		tmp_addr = &cur_seq->t_addr[cur_seq->texture][9];
467203288Srnoland		*tmp_addr = (*tmp_addr & 0x00FFFFFF) | ((cmd & 0xFF) << 24);
468203288Srnoland		return 0;
469203288Srnoland	case check_texture_addr5:
470203288Srnoland	case check_texture_addr6:
471203288Srnoland		cur_seq->unfinished = tex_address;
472203288Srnoland		/*
473203288Srnoland		 * Texture width. We don't care since we have the pitch.
474203288Srnoland		 */
475203288Srnoland		return 0;
476203288Srnoland	case check_texture_addr7:
477203288Srnoland		cur_seq->unfinished = tex_address;
478203288Srnoland		tmp_addr = &(cur_seq->height[cur_seq->texture][0]);
479203288Srnoland		tmp_addr[5] = 1 << ((cmd & 0x00F00000) >> 20);
480203288Srnoland		tmp_addr[4] = 1 << ((cmd & 0x000F0000) >> 16);
481203288Srnoland		tmp_addr[3] = 1 << ((cmd & 0x0000F000) >> 12);
482203288Srnoland		tmp_addr[2] = 1 << ((cmd & 0x00000F00) >> 8);
483203288Srnoland		tmp_addr[1] = 1 << ((cmd & 0x000000F0) >> 4);
484203288Srnoland		tmp_addr[0] = 1 << (cmd & 0x0000000F);
485203288Srnoland		return 0;
486203288Srnoland	case check_texture_addr8:
487203288Srnoland		cur_seq->unfinished = tex_address;
488203288Srnoland		tmp_addr = &(cur_seq->height[cur_seq->texture][0]);
489203288Srnoland		tmp_addr[9] = 1 << ((cmd & 0x0000F000) >> 12);
490203288Srnoland		tmp_addr[8] = 1 << ((cmd & 0x00000F00) >> 8);
491203288Srnoland		tmp_addr[7] = 1 << ((cmd & 0x000000F0) >> 4);
492203288Srnoland		tmp_addr[6] = 1 << (cmd & 0x0000000F);
493203288Srnoland		return 0;
494203288Srnoland	case check_texture_addr_mode:
495203288Srnoland		cur_seq->unfinished = tex_address;
496203288Srnoland		if (2 == (tmp = cmd & 0x00000003)) {
497203288Srnoland			DRM_ERROR
498203288Srnoland			    ("Attempt to fetch texture from system memory.\n");
499203288Srnoland			return 2;
500203288Srnoland		}
501203288Srnoland		cur_seq->agp_texture = (tmp == 3);
502203288Srnoland		cur_seq->tex_palette_size[cur_seq->texture] =
503203288Srnoland		    (cmd >> 16) & 0x000000007;
504203288Srnoland		return 0;
505203288Srnoland	case check_for_vertex_count:
506203288Srnoland		cur_seq->vertex_count = cmd & 0x0000FFFF;
507203288Srnoland		return 0;
508203288Srnoland	case check_number_texunits:
509203288Srnoland		cur_seq->multitex = (cmd >> 3) & 1;
510203288Srnoland		return 0;
511203288Srnoland	default:
512203288Srnoland		DRM_ERROR("Illegal DMA data: 0x%x\n", cmd);
513203288Srnoland		return 2;
514203288Srnoland	}
515203288Srnoland	return 2;
516203288Srnoland}
517203288Srnoland
518203288Srnolandstatic __inline__ int
519203288Srnolandvia_check_prim_list(uint32_t const **buffer, const uint32_t * buf_end,
520203288Srnoland		    drm_via_state_t * cur_seq)
521203288Srnoland{
522203288Srnoland	drm_via_private_t *dev_priv =
523203288Srnoland	    (drm_via_private_t *) cur_seq->dev->dev_private;
524203288Srnoland	uint32_t a_fire, bcmd, dw_count;
525203288Srnoland	int ret = 0;
526203288Srnoland	int have_fire;
527203288Srnoland	const uint32_t *buf = *buffer;
528203288Srnoland
529203288Srnoland	while (buf < buf_end) {
530203288Srnoland		have_fire = 0;
531203288Srnoland		if ((buf_end - buf) < 2) {
532203288Srnoland			DRM_ERROR
533203288Srnoland			    ("Unexpected termination of primitive list.\n");
534203288Srnoland			ret = 1;
535203288Srnoland			break;
536203288Srnoland		}
537203288Srnoland		if ((*buf & HC_ACMD_MASK) != HC_ACMD_HCmdB)
538203288Srnoland			break;
539203288Srnoland		bcmd = *buf++;
540203288Srnoland		if ((*buf & HC_ACMD_MASK) != HC_ACMD_HCmdA) {
541203288Srnoland			DRM_ERROR("Expected Vertex List A command, got 0x%x\n",
542203288Srnoland				  *buf);
543203288Srnoland			ret = 1;
544203288Srnoland			break;
545203288Srnoland		}
546203288Srnoland		a_fire =
547203288Srnoland		    *buf++ | HC_HPLEND_MASK | HC_HPMValidN_MASK |
548203288Srnoland		    HC_HE3Fire_MASK;
549203288Srnoland
550203288Srnoland		/*
551203288Srnoland		 * How many dwords per vertex ?
552203288Srnoland		 */
553203288Srnoland
554203288Srnoland		if (cur_seq->agp && ((bcmd & (0xF << 11)) == 0)) {
555203288Srnoland			DRM_ERROR("Illegal B command vertex data for AGP.\n");
556203288Srnoland			ret = 1;
557203288Srnoland			break;
558203288Srnoland		}
559203288Srnoland
560203288Srnoland		dw_count = 0;
561203288Srnoland		if (bcmd & (1 << 7))
562203288Srnoland			dw_count += (cur_seq->multitex) ? 2 : 1;
563203288Srnoland		if (bcmd & (1 << 8))
564203288Srnoland			dw_count += (cur_seq->multitex) ? 2 : 1;
565203288Srnoland		if (bcmd & (1 << 9))
566203288Srnoland			dw_count++;
567203288Srnoland		if (bcmd & (1 << 10))
568203288Srnoland			dw_count++;
569203288Srnoland		if (bcmd & (1 << 11))
570203288Srnoland			dw_count++;
571203288Srnoland		if (bcmd & (1 << 12))
572203288Srnoland			dw_count++;
573203288Srnoland		if (bcmd & (1 << 13))
574203288Srnoland			dw_count++;
575203288Srnoland		if (bcmd & (1 << 14))
576203288Srnoland			dw_count++;
577203288Srnoland
578203288Srnoland		while (buf < buf_end) {
579203288Srnoland			if (*buf == a_fire) {
580203288Srnoland				if (dev_priv->num_fire_offsets >=
581203288Srnoland				    VIA_FIRE_BUF_SIZE) {
582203288Srnoland					DRM_ERROR("Fire offset buffer full.\n");
583203288Srnoland					ret = 1;
584203288Srnoland					break;
585203288Srnoland				}
586203288Srnoland				dev_priv->fire_offsets[dev_priv->
587203288Srnoland						       num_fire_offsets++] =
588203288Srnoland				    buf;
589203288Srnoland				have_fire = 1;
590203288Srnoland				buf++;
591203288Srnoland				if (buf < buf_end && *buf == a_fire)
592203288Srnoland					buf++;
593203288Srnoland				break;
594203288Srnoland			}
595203288Srnoland			if ((*buf == HALCYON_HEADER2) ||
596203288Srnoland			    ((*buf & HALCYON_FIREMASK) == HALCYON_FIRECMD)) {
597203288Srnoland				DRM_ERROR("Missing Vertex Fire command, "
598203288Srnoland					  "Stray Vertex Fire command  or verifier "
599203288Srnoland					  "lost sync.\n");
600203288Srnoland				ret = 1;
601203288Srnoland				break;
602203288Srnoland			}
603203288Srnoland			if ((ret = eat_words(&buf, buf_end, dw_count)))
604203288Srnoland				break;
605203288Srnoland		}
606203288Srnoland		if (buf >= buf_end && !have_fire) {
607203288Srnoland			DRM_ERROR("Missing Vertex Fire command or verifier "
608203288Srnoland				  "lost sync.\n");
609203288Srnoland			ret = 1;
610203288Srnoland			break;
611203288Srnoland		}
612203288Srnoland		if (cur_seq->agp && ((buf - cur_seq->buf_start) & 0x01)) {
613203288Srnoland			DRM_ERROR("AGP Primitive list end misaligned.\n");
614203288Srnoland			ret = 1;
615203288Srnoland			break;
616203288Srnoland		}
617203288Srnoland	}
618203288Srnoland	*buffer = buf;
619203288Srnoland	return ret;
620203288Srnoland}
621203288Srnoland
622203288Srnolandstatic __inline__ verifier_state_t
623203288Srnolandvia_check_header2(uint32_t const **buffer, const uint32_t * buf_end,
624203288Srnoland		  drm_via_state_t * hc_state)
625203288Srnoland{
626203288Srnoland	uint32_t cmd;
627203288Srnoland	int hz_mode;
628203288Srnoland	hazard_t hz;
629203288Srnoland	const uint32_t *buf = *buffer;
630203288Srnoland	const hazard_t *hz_table;
631203288Srnoland
632203288Srnoland	if ((buf_end - buf) < 2) {
633203288Srnoland		DRM_ERROR
634203288Srnoland		    ("Illegal termination of DMA HALCYON_HEADER2 sequence.\n");
635203288Srnoland		return state_error;
636203288Srnoland	}
637203288Srnoland	buf++;
638203288Srnoland	cmd = (*buf++ & 0xFFFF0000) >> 16;
639203288Srnoland
640203288Srnoland	switch (cmd) {
641203288Srnoland	case HC_ParaType_CmdVdata:
642203288Srnoland		if (via_check_prim_list(&buf, buf_end, hc_state))
643203288Srnoland			return state_error;
644203288Srnoland		*buffer = buf;
645203288Srnoland		return state_command;
646203288Srnoland	case HC_ParaType_NotTex:
647203288Srnoland		hz_table = table1;
648203288Srnoland		break;
649203288Srnoland	case HC_ParaType_Tex:
650203288Srnoland		hc_state->texture = 0;
651203288Srnoland		hz_table = table2;
652203288Srnoland		break;
653203288Srnoland	case (HC_ParaType_Tex | (HC_SubType_Tex1 << 8)):
654203288Srnoland		hc_state->texture = 1;
655203288Srnoland		hz_table = table2;
656203288Srnoland		break;
657203288Srnoland	case (HC_ParaType_Tex | (HC_SubType_TexGeneral << 8)):
658203288Srnoland		hz_table = table3;
659203288Srnoland		break;
660203288Srnoland	case HC_ParaType_Auto:
661203288Srnoland		if (eat_words(&buf, buf_end, 2))
662203288Srnoland			return state_error;
663203288Srnoland		*buffer = buf;
664203288Srnoland		return state_command;
665203288Srnoland	case (HC_ParaType_Palette | (HC_SubType_Stipple << 8)):
666203288Srnoland		if (eat_words(&buf, buf_end, 32))
667203288Srnoland			return state_error;
668203288Srnoland		*buffer = buf;
669203288Srnoland		return state_command;
670203288Srnoland	case (HC_ParaType_Palette | (HC_SubType_TexPalette0 << 8)):
671203288Srnoland	case (HC_ParaType_Palette | (HC_SubType_TexPalette1 << 8)):
672203288Srnoland		DRM_ERROR("Texture palettes are rejected because of "
673203288Srnoland			  "lack of info how to determine their size.\n");
674203288Srnoland		return state_error;
675203288Srnoland	case (HC_ParaType_Palette | (HC_SubType_FogTable << 8)):
676203288Srnoland		DRM_ERROR("Fog factor palettes are rejected because of "
677203288Srnoland			  "lack of info how to determine their size.\n");
678203288Srnoland		return state_error;
679203288Srnoland	default:
680203288Srnoland
681203288Srnoland		/*
682203288Srnoland		 * There are some unimplemented HC_ParaTypes here, that
683203288Srnoland		 * need to be implemented if the Mesa driver is extended.
684203288Srnoland		 */
685203288Srnoland
686203288Srnoland		DRM_ERROR("Invalid or unimplemented HALCYON_HEADER2 "
687203288Srnoland			  "DMA subcommand: 0x%x. Previous dword: 0x%x\n",
688203288Srnoland			  cmd, *(buf - 2));
689203288Srnoland		*buffer = buf;
690203288Srnoland		return state_error;
691203288Srnoland	}
692203288Srnoland
693203288Srnoland	while (buf < buf_end) {
694203288Srnoland		cmd = *buf++;
695203288Srnoland		if ((hz = hz_table[cmd >> 24])) {
696203288Srnoland			if ((hz_mode = investigate_hazard(cmd, hz, hc_state))) {
697203288Srnoland				if (hz_mode == 1) {
698203288Srnoland					buf--;
699203288Srnoland					break;
700203288Srnoland				}
701203288Srnoland				return state_error;
702203288Srnoland			}
703203288Srnoland		} else if (hc_state->unfinished &&
704203288Srnoland			   finish_current_sequence(hc_state)) {
705203288Srnoland			return state_error;
706203288Srnoland		}
707203288Srnoland	}
708203288Srnoland	if (hc_state->unfinished && finish_current_sequence(hc_state)) {
709203288Srnoland		return state_error;
710203288Srnoland	}
711203288Srnoland	*buffer = buf;
712203288Srnoland	return state_command;
713203288Srnoland}
714203288Srnoland
715203288Srnolandstatic __inline__ verifier_state_t
716203288Srnolandvia_parse_header2(drm_via_private_t * dev_priv, uint32_t const **buffer,
717203288Srnoland		  const uint32_t * buf_end, int *fire_count)
718203288Srnoland{
719203288Srnoland	uint32_t cmd;
720203288Srnoland	const uint32_t *buf = *buffer;
721203288Srnoland	const uint32_t *next_fire;
722203288Srnoland	int burst = 0;
723203288Srnoland
724203288Srnoland	next_fire = dev_priv->fire_offsets[*fire_count];
725203288Srnoland	buf++;
726203288Srnoland	cmd = (*buf & 0xFFFF0000) >> 16;
727203288Srnoland	VIA_WRITE(HC_REG_TRANS_SET + HC_REG_BASE, *buf++);
728203288Srnoland	switch (cmd) {
729203288Srnoland	case HC_ParaType_CmdVdata:
730203288Srnoland		while ((buf < buf_end) &&
731203288Srnoland		       (*fire_count < dev_priv->num_fire_offsets) &&
732203288Srnoland		       (*buf & HC_ACMD_MASK) == HC_ACMD_HCmdB) {
733203288Srnoland			while (buf <= next_fire) {
734203288Srnoland				VIA_WRITE(HC_REG_TRANS_SPACE + HC_REG_BASE +
735203288Srnoland					  (burst & 63), *buf++);
736203288Srnoland				burst += 4;
737203288Srnoland			}
738203288Srnoland			if ((buf < buf_end)
739203288Srnoland			    && ((*buf & HALCYON_FIREMASK) == HALCYON_FIRECMD))
740203288Srnoland				buf++;
741203288Srnoland
742203288Srnoland			if (++(*fire_count) < dev_priv->num_fire_offsets)
743203288Srnoland				next_fire = dev_priv->fire_offsets[*fire_count];
744203288Srnoland		}
745203288Srnoland		break;
746203288Srnoland	default:
747203288Srnoland		while (buf < buf_end) {
748203288Srnoland
749203288Srnoland			if (*buf == HC_HEADER2 ||
750203288Srnoland			    (*buf & HALCYON_HEADER1MASK) == HALCYON_HEADER1 ||
751203288Srnoland			    (*buf & VIA_VIDEOMASK) == VIA_VIDEO_HEADER5 ||
752203288Srnoland			    (*buf & VIA_VIDEOMASK) == VIA_VIDEO_HEADER6)
753203288Srnoland				break;
754203288Srnoland
755203288Srnoland			VIA_WRITE(HC_REG_TRANS_SPACE + HC_REG_BASE +
756203288Srnoland				  (burst & 63), *buf++);
757203288Srnoland			burst += 4;
758203288Srnoland		}
759203288Srnoland	}
760203288Srnoland	*buffer = buf;
761203288Srnoland	return state_command;
762203288Srnoland}
763203288Srnoland
764203288Srnolandstatic __inline__ int verify_mmio_address(uint32_t address)
765203288Srnoland{
766203288Srnoland	if ((address > 0x3FF) && (address < 0xC00)) {
767203288Srnoland		DRM_ERROR("Invalid VIDEO DMA command. "
768203288Srnoland			  "Attempt to access 3D- or command burst area.\n");
769203288Srnoland		return 1;
770203288Srnoland	} else if ((address > 0xCFF) && (address < 0x1300)) {
771203288Srnoland		DRM_ERROR("Invalid VIDEO DMA command. "
772203288Srnoland			  "Attempt to access PCI DMA area.\n");
773203288Srnoland		return 1;
774203288Srnoland	} else if (address > 0x13FF) {
775203288Srnoland		DRM_ERROR("Invalid VIDEO DMA command. "
776203288Srnoland			  "Attempt to access VGA registers.\n");
777203288Srnoland		return 1;
778203288Srnoland	}
779203288Srnoland	return 0;
780203288Srnoland}
781203288Srnoland
782203288Srnolandstatic __inline__ int
783203288Srnolandverify_video_tail(uint32_t const **buffer, const uint32_t * buf_end,
784203288Srnoland		  uint32_t dwords)
785203288Srnoland{
786203288Srnoland	const uint32_t *buf = *buffer;
787203288Srnoland
788203288Srnoland	if (buf_end - buf < dwords) {
789203288Srnoland		DRM_ERROR("Illegal termination of video command.\n");
790203288Srnoland		return 1;
791203288Srnoland	}
792203288Srnoland	while (dwords--) {
793203288Srnoland		if (*buf++) {
794203288Srnoland			DRM_ERROR("Illegal video command tail.\n");
795203288Srnoland			return 1;
796203288Srnoland		}
797203288Srnoland	}
798203288Srnoland	*buffer = buf;
799203288Srnoland	return 0;
800203288Srnoland}
801203288Srnoland
802203288Srnolandstatic __inline__ verifier_state_t
803203288Srnolandvia_check_header1(uint32_t const **buffer, const uint32_t * buf_end)
804203288Srnoland{
805203288Srnoland	uint32_t cmd;
806203288Srnoland	const uint32_t *buf = *buffer;
807203288Srnoland	verifier_state_t ret = state_command;
808203288Srnoland
809203288Srnoland	while (buf < buf_end) {
810203288Srnoland		cmd = *buf;
811203288Srnoland		if ((cmd > ((0x3FF >> 2) | HALCYON_HEADER1)) &&
812203288Srnoland		    (cmd < ((0xC00 >> 2) | HALCYON_HEADER1))) {
813203288Srnoland			if ((cmd & HALCYON_HEADER1MASK) != HALCYON_HEADER1)
814203288Srnoland				break;
815203288Srnoland			DRM_ERROR("Invalid HALCYON_HEADER1 command. "
816203288Srnoland				  "Attempt to access 3D- or command burst area.\n");
817203288Srnoland			ret = state_error;
818203288Srnoland			break;
819203288Srnoland		} else if (cmd > ((0xCFF >> 2) | HALCYON_HEADER1)) {
820203288Srnoland			if ((cmd & HALCYON_HEADER1MASK) != HALCYON_HEADER1)
821203288Srnoland				break;
822203288Srnoland			DRM_ERROR("Invalid HALCYON_HEADER1 command. "
823203288Srnoland				  "Attempt to access VGA registers.\n");
824203288Srnoland			ret = state_error;
825203288Srnoland			break;
826203288Srnoland		} else {
827203288Srnoland			buf += 2;
828203288Srnoland		}
829203288Srnoland	}
830203288Srnoland	*buffer = buf;
831203288Srnoland	return ret;
832203288Srnoland}
833203288Srnoland
834203288Srnolandstatic __inline__ verifier_state_t
835203288Srnolandvia_parse_header1(drm_via_private_t * dev_priv, uint32_t const **buffer,
836203288Srnoland		  const uint32_t * buf_end)
837203288Srnoland{
838203288Srnoland	register uint32_t cmd;
839203288Srnoland	const uint32_t *buf = *buffer;
840203288Srnoland
841203288Srnoland	while (buf < buf_end) {
842203288Srnoland		cmd = *buf;
843203288Srnoland		if ((cmd & HALCYON_HEADER1MASK) != HALCYON_HEADER1)
844203288Srnoland			break;
845203288Srnoland		VIA_WRITE((cmd & ~HALCYON_HEADER1MASK) << 2, *++buf);
846203288Srnoland		buf++;
847203288Srnoland	}
848203288Srnoland	*buffer = buf;
849203288Srnoland	return state_command;
850203288Srnoland}
851203288Srnoland
852203288Srnolandstatic __inline__ verifier_state_t
853203288Srnolandvia_check_vheader5(uint32_t const **buffer, const uint32_t * buf_end)
854203288Srnoland{
855203288Srnoland	uint32_t data;
856203288Srnoland	const uint32_t *buf = *buffer;
857203288Srnoland
858203288Srnoland	if (buf_end - buf < 4) {
859203288Srnoland		DRM_ERROR("Illegal termination of video header5 command\n");
860203288Srnoland		return state_error;
861203288Srnoland	}
862203288Srnoland
863203288Srnoland	data = *buf++ & ~VIA_VIDEOMASK;
864203288Srnoland	if (verify_mmio_address(data))
865203288Srnoland		return state_error;
866203288Srnoland
867203288Srnoland	data = *buf++;
868203288Srnoland	if (*buf++ != 0x00F50000) {
869203288Srnoland		DRM_ERROR("Illegal header5 header data\n");
870203288Srnoland		return state_error;
871203288Srnoland	}
872203288Srnoland	if (*buf++ != 0x00000000) {
873203288Srnoland		DRM_ERROR("Illegal header5 header data\n");
874203288Srnoland		return state_error;
875203288Srnoland	}
876203288Srnoland	if (eat_words(&buf, buf_end, data))
877203288Srnoland		return state_error;
878203288Srnoland	if ((data & 3) && verify_video_tail(&buf, buf_end, 4 - (data & 3)))
879203288Srnoland		return state_error;
880203288Srnoland	*buffer = buf;
881203288Srnoland	return state_command;
882203288Srnoland
883203288Srnoland}
884203288Srnoland
885203288Srnolandstatic __inline__ verifier_state_t
886203288Srnolandvia_parse_vheader5(drm_via_private_t * dev_priv, uint32_t const **buffer,
887203288Srnoland		   const uint32_t * buf_end)
888203288Srnoland{
889203288Srnoland	uint32_t addr, count, i;
890203288Srnoland	const uint32_t *buf = *buffer;
891203288Srnoland
892203288Srnoland	addr = *buf++ & ~VIA_VIDEOMASK;
893203288Srnoland	i = count = *buf;
894203288Srnoland	buf += 3;
895203288Srnoland	while (i--) {
896203288Srnoland		VIA_WRITE(addr, *buf++);
897203288Srnoland	}
898203288Srnoland	if (count & 3)
899203288Srnoland		buf += 4 - (count & 3);
900203288Srnoland	*buffer = buf;
901203288Srnoland	return state_command;
902203288Srnoland}
903203288Srnoland
904203288Srnolandstatic __inline__ verifier_state_t
905203288Srnolandvia_check_vheader6(uint32_t const **buffer, const uint32_t * buf_end)
906203288Srnoland{
907203288Srnoland	uint32_t data;
908203288Srnoland	const uint32_t *buf = *buffer;
909203288Srnoland	uint32_t i;
910203288Srnoland
911203288Srnoland	if (buf_end - buf < 4) {
912203288Srnoland		DRM_ERROR("Illegal termination of video header6 command\n");
913203288Srnoland		return state_error;
914203288Srnoland	}
915203288Srnoland	buf++;
916203288Srnoland	data = *buf++;
917203288Srnoland	if (*buf++ != 0x00F60000) {
918203288Srnoland		DRM_ERROR("Illegal header6 header data\n");
919203288Srnoland		return state_error;
920203288Srnoland	}
921203288Srnoland	if (*buf++ != 0x00000000) {
922203288Srnoland		DRM_ERROR("Illegal header6 header data\n");
923203288Srnoland		return state_error;
924203288Srnoland	}
925203288Srnoland	if ((buf_end - buf) < (data << 1)) {
926203288Srnoland		DRM_ERROR("Illegal termination of video header6 command\n");
927203288Srnoland		return state_error;
928203288Srnoland	}
929203288Srnoland	for (i = 0; i < data; ++i) {
930203288Srnoland		if (verify_mmio_address(*buf++))
931203288Srnoland			return state_error;
932203288Srnoland		buf++;
933203288Srnoland	}
934203288Srnoland	data <<= 1;
935203288Srnoland	if ((data & 3) && verify_video_tail(&buf, buf_end, 4 - (data & 3)))
936203288Srnoland		return state_error;
937203288Srnoland	*buffer = buf;
938203288Srnoland	return state_command;
939203288Srnoland}
940203288Srnoland
941203288Srnolandstatic __inline__ verifier_state_t
942203288Srnolandvia_parse_vheader6(drm_via_private_t * dev_priv, uint32_t const **buffer,
943203288Srnoland		   const uint32_t * buf_end)
944203288Srnoland{
945203288Srnoland
946203288Srnoland	uint32_t addr, count, i;
947203288Srnoland	const uint32_t *buf = *buffer;
948203288Srnoland
949203288Srnoland	i = count = *++buf;
950203288Srnoland	buf += 3;
951203288Srnoland	while (i--) {
952203288Srnoland		addr = *buf++;
953203288Srnoland		VIA_WRITE(addr, *buf++);
954203288Srnoland	}
955203288Srnoland	count <<= 1;
956203288Srnoland	if (count & 3)
957203288Srnoland		buf += 4 - (count & 3);
958203288Srnoland	*buffer = buf;
959203288Srnoland	return state_command;
960203288Srnoland}
961203288Srnoland
962203288Srnolandint
963203288Srnolandvia_verify_command_stream(const uint32_t * buf, unsigned int size,
964203288Srnoland			  struct drm_device * dev, int agp)
965203288Srnoland{
966203288Srnoland
967203288Srnoland	drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
968203288Srnoland	drm_via_state_t *hc_state = &dev_priv->hc_state;
969203288Srnoland	drm_via_state_t saved_state = *hc_state;
970203288Srnoland	uint32_t cmd;
971203288Srnoland	const uint32_t *buf_end = buf + (size >> 2);
972203288Srnoland	verifier_state_t state = state_command;
973203288Srnoland	int cme_video;
974203288Srnoland	int supported_3d;
975203288Srnoland
976203288Srnoland	cme_video = (dev_priv->chipset == VIA_PRO_GROUP_A ||
977203288Srnoland		     dev_priv->chipset == VIA_DX9_0);
978203288Srnoland
979203288Srnoland	supported_3d = dev_priv->chipset != VIA_DX9_0;
980203288Srnoland
981203288Srnoland	hc_state->dev = dev;
982203288Srnoland	hc_state->unfinished = no_sequence;
983203288Srnoland	hc_state->map_cache = NULL;
984203288Srnoland	hc_state->agp = agp;
985203288Srnoland	hc_state->buf_start = buf;
986203288Srnoland	dev_priv->num_fire_offsets = 0;
987203288Srnoland
988203288Srnoland	while (buf < buf_end) {
989203288Srnoland
990203288Srnoland		switch (state) {
991203288Srnoland		case state_header2:
992203288Srnoland			state = via_check_header2(&buf, buf_end, hc_state);
993203288Srnoland			break;
994203288Srnoland		case state_header1:
995203288Srnoland			state = via_check_header1(&buf, buf_end);
996203288Srnoland			break;
997203288Srnoland		case state_vheader5:
998203288Srnoland			state = via_check_vheader5(&buf, buf_end);
999203288Srnoland			break;
1000203288Srnoland		case state_vheader6:
1001203288Srnoland			state = via_check_vheader6(&buf, buf_end);
1002203288Srnoland			break;
1003203288Srnoland		case state_command:
1004203288Srnoland			if ((HALCYON_HEADER2 == (cmd = *buf)) &&
1005203288Srnoland			    supported_3d)
1006203288Srnoland				state = state_header2;
1007203288Srnoland			else if ((cmd & HALCYON_HEADER1MASK) == HALCYON_HEADER1)
1008203288Srnoland				state = state_header1;
1009203288Srnoland			else if (cme_video
1010203288Srnoland				 && (cmd & VIA_VIDEOMASK) == VIA_VIDEO_HEADER5)
1011203288Srnoland				state = state_vheader5;
1012203288Srnoland			else if (cme_video
1013203288Srnoland				 && (cmd & VIA_VIDEOMASK) == VIA_VIDEO_HEADER6)
1014203288Srnoland				state = state_vheader6;
1015203288Srnoland			else if ((cmd == HALCYON_HEADER2) && !supported_3d) {
1016203288Srnoland				DRM_ERROR("Accelerated 3D is not supported on this chipset yet.\n");
1017203288Srnoland				state = state_error;
1018203288Srnoland			} else {
1019203288Srnoland				DRM_ERROR
1020203288Srnoland				    ("Invalid / Unimplemented DMA HEADER command. 0x%x\n",
1021203288Srnoland				     cmd);
1022203288Srnoland				state = state_error;
1023203288Srnoland			}
1024203288Srnoland			break;
1025203288Srnoland		case state_error:
1026203288Srnoland		default:
1027203288Srnoland			*hc_state = saved_state;
1028203288Srnoland			return -EINVAL;
1029203288Srnoland		}
1030203288Srnoland	}
1031203288Srnoland	if (state == state_error) {
1032203288Srnoland		*hc_state = saved_state;
1033203288Srnoland		return -EINVAL;
1034203288Srnoland	}
1035203288Srnoland	return 0;
1036203288Srnoland}
1037203288Srnoland
1038203288Srnolandint
1039203288Srnolandvia_parse_command_stream(struct drm_device * dev, const uint32_t * buf,
1040203288Srnoland			 unsigned int size)
1041203288Srnoland{
1042203288Srnoland
1043203288Srnoland	drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
1044203288Srnoland	uint32_t cmd;
1045203288Srnoland	const uint32_t *buf_end = buf + (size >> 2);
1046203288Srnoland	verifier_state_t state = state_command;
1047203288Srnoland	int fire_count = 0;
1048203288Srnoland
1049203288Srnoland	while (buf < buf_end) {
1050203288Srnoland
1051203288Srnoland		switch (state) {
1052203288Srnoland		case state_header2:
1053203288Srnoland			state =
1054203288Srnoland			    via_parse_header2(dev_priv, &buf, buf_end,
1055203288Srnoland					      &fire_count);
1056203288Srnoland			break;
1057203288Srnoland		case state_header1:
1058203288Srnoland			state = via_parse_header1(dev_priv, &buf, buf_end);
1059203288Srnoland			break;
1060203288Srnoland		case state_vheader5:
1061203288Srnoland			state = via_parse_vheader5(dev_priv, &buf, buf_end);
1062203288Srnoland			break;
1063203288Srnoland		case state_vheader6:
1064203288Srnoland			state = via_parse_vheader6(dev_priv, &buf, buf_end);
1065203288Srnoland			break;
1066203288Srnoland		case state_command:
1067203288Srnoland			if (HALCYON_HEADER2 == (cmd = *buf))
1068203288Srnoland				state = state_header2;
1069203288Srnoland			else if ((cmd & HALCYON_HEADER1MASK) == HALCYON_HEADER1)
1070203288Srnoland				state = state_header1;
1071203288Srnoland			else if ((cmd & VIA_VIDEOMASK) == VIA_VIDEO_HEADER5)
1072203288Srnoland				state = state_vheader5;
1073203288Srnoland			else if ((cmd & VIA_VIDEOMASK) == VIA_VIDEO_HEADER6)
1074203288Srnoland				state = state_vheader6;
1075203288Srnoland			else {
1076203288Srnoland				DRM_ERROR
1077203288Srnoland				    ("Invalid / Unimplemented DMA HEADER command. 0x%x\n",
1078203288Srnoland				     cmd);
1079203288Srnoland				state = state_error;
1080203288Srnoland			}
1081203288Srnoland			break;
1082203288Srnoland		case state_error:
1083203288Srnoland		default:
1084203288Srnoland			return -EINVAL;
1085203288Srnoland		}
1086203288Srnoland	}
1087203288Srnoland	if (state == state_error) {
1088203288Srnoland		return -EINVAL;
1089203288Srnoland	}
1090203288Srnoland	return 0;
1091203288Srnoland}
1092203288Srnoland
1093203288Srnolandstatic void
1094203288Srnolandsetup_hazard_table(hz_init_t init_table[], hazard_t table[], int size)
1095203288Srnoland{
1096203288Srnoland	int i;
1097203288Srnoland
1098203288Srnoland	for (i = 0; i < 256; ++i) {
1099203288Srnoland		table[i] = forbidden_command;
1100203288Srnoland	}
1101203288Srnoland
1102203288Srnoland	for (i = 0; i < size; ++i) {
1103203288Srnoland		table[init_table[i].code] = init_table[i].hz;
1104203288Srnoland	}
1105203288Srnoland}
1106203288Srnoland
1107203288Srnolandvoid via_init_command_verifier(void)
1108203288Srnoland{
1109203288Srnoland	setup_hazard_table(init_table1, table1,
1110203288Srnoland			   sizeof(init_table1) / sizeof(hz_init_t));
1111203288Srnoland	setup_hazard_table(init_table2, table2,
1112203288Srnoland			   sizeof(init_table2) / sizeof(hz_init_t));
1113203288Srnoland	setup_hazard_table(init_table3, table3,
1114203288Srnoland			   sizeof(init_table3) / sizeof(hz_init_t));
1115203288Srnoland}
1116