xref: /freebsd/sys/dev/vt/vt_font.c (revision 13de33a5dc2304b13d595d75d48c51793958474f)
1 /*-
2  * Copyright (c) 2009 The FreeBSD Foundation
3  * All rights reserved.
4  *
5  * This software was developed by Ed Schouten under sponsorship from the
6  * FreeBSD Foundation.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #include <sys/param.h>
34 #include <sys/kernel.h>
35 #include <sys/malloc.h>
36 #include <sys/refcount.h>
37 #include <sys/systm.h>
38 
39 #include <dev/vt/vt.h>
40 
41 static MALLOC_DEFINE(M_VTFONT, "vtfont", "vt font");
42 
43 /* Some limits to prevent abnormal fonts from being loaded. */
44 #define	VTFONT_MAXMAPPINGS	1024
45 #define	VTFONT_MAXGLYPHSIZE	262144
46 #define	VTFONT_MAXDIMENSION	128
47 
48 static uint16_t
49 vtfont_bisearch(const struct vt_font_map *map, unsigned int len, uint32_t src)
50 {
51 	int min, mid, max;
52 
53 	min = 0;
54 	max = len - 1;
55 
56 	/* Empty font map. */
57 	if (len == 0)
58 		return (0);
59 	/* Character below minimal entry. */
60 	if (src < map[0].vfm_src)
61 		return (0);
62 	/* Optimization: ASCII characters occur very often. */
63 	if (src <= map[0].vfm_src + map[0].vfm_len)
64 		return (src - map[0].vfm_src + map[0].vfm_dst);
65 	/* Character above maximum entry. */
66 	if (src > map[max].vfm_src + map[max].vfm_len)
67 		return (0);
68 
69 	/* Binary search. */
70 	while (max >= min) {
71 		mid = (min + max) / 2;
72 		if (src < map[mid].vfm_src)
73 			max = mid - 1;
74 		else if (src > map[mid].vfm_src + map[mid].vfm_len)
75 			min = mid + 1;
76 		else
77 			return (src - map[mid].vfm_src + map[mid].vfm_dst);
78 	}
79 
80 	return (0);
81 }
82 
83 const uint8_t *
84 vtfont_lookup(const struct vt_font *vf, term_char_t c)
85 {
86 	uint32_t src;
87 	uint16_t dst;
88 	size_t stride;
89 
90 	src = TCHAR_CHARACTER(c);
91 	if (TCHAR_FORMAT(c) & TF_BOLD) {
92 		dst = vtfont_bisearch(vf->vf_bold, vf->vf_bold_length, src);
93 		if (dst != 0)
94 			goto found;
95 	}
96 	dst = vtfont_bisearch(vf->vf_normal, vf->vf_normal_length, src);
97 
98 found:
99 	stride = howmany(vf->vf_width, 8) * vf->vf_height;
100 	return (&vf->vf_bytes[dst * stride]);
101 }
102 
103 struct vt_font *
104 vtfont_ref(struct vt_font *vf)
105 {
106 
107 	refcount_acquire(&vf->vf_refcount);
108 	return (vf);
109 }
110 
111 void
112 vtfont_unref(struct vt_font *vf)
113 {
114 
115 	if (refcount_release(&vf->vf_refcount)) {
116 		free(vf->vf_normal, M_VTFONT);
117 		free(vf->vf_bold, M_VTFONT);
118 		free(vf->vf_bytes, M_VTFONT);
119 		free(vf, M_VTFONT);
120 	}
121 }
122 
123 static int
124 vtfont_validate_map(struct vt_font_map *vfm, unsigned int length,
125     unsigned int nglyphs)
126 {
127 	unsigned int i, last = 0;
128 
129 	for (i = 0; i < length; i++) {
130 		/* Not ordered. */
131 		if (i > 0 && vfm[i].vfm_src <= last)
132 			return (EINVAL);
133 		/*
134 		 * Destination extends amount of glyphs.
135 		 */
136 		if (vfm[i].vfm_dst >= nglyphs ||
137 		    vfm[i].vfm_dst + vfm[i].vfm_len >= nglyphs)
138 			return (EINVAL);
139 		last = vfm[i].vfm_src + vfm[i].vfm_len;
140 	}
141 
142 	return (0);
143 }
144 
145 int
146 vtfont_load(vfnt_t *f, struct vt_font **ret)
147 {
148 	size_t glyphsize;
149 	struct vt_font *vf;
150 	int error;
151 
152 	/* Make sure the dimensions are valid. */
153 	if (f->width < 1 || f->height < 1)
154 		return (EINVAL);
155 	if (f->width > VTFONT_MAXDIMENSION || f->height > VTFONT_MAXDIMENSION)
156 		return (E2BIG);
157 
158 	/* Not too many mappings. */
159 	if (f->nnormal > VTFONT_MAXMAPPINGS || f->nbold > VTFONT_MAXMAPPINGS)
160 		return (E2BIG);
161 
162 	/* Character 0 must always be present. */
163 	if (f->nglyphs < 1)
164 		return (EINVAL);
165 
166 	glyphsize = howmany(f->width, 8) * f->height * f->nglyphs;
167 	if (glyphsize > VTFONT_MAXGLYPHSIZE)
168 		return (E2BIG);
169 
170 	/* Allocate new font structure. */
171 	vf = malloc(sizeof *vf, M_VTFONT, M_WAITOK);
172 	vf->vf_normal = malloc(f->nnormal * sizeof(struct vt_font_map),
173 	    M_VTFONT, M_WAITOK);
174 	vf->vf_bold = malloc(f->nbold * sizeof(struct vt_font_map),
175 	    M_VTFONT, M_WAITOK);
176 	vf->vf_bytes = malloc(glyphsize, M_VTFONT, M_WAITOK);
177 	vf->vf_height = f->height;
178 	vf->vf_width = f->width;
179 	vf->vf_normal_length = f->nnormal;
180 	vf->vf_bold_length = f->nbold;
181 	vf->vf_refcount = 1;
182 
183 	/* Copy in data. */
184 	error = copyin(f->normal, vf->vf_normal,
185 	    vf->vf_normal_length * sizeof(struct vt_font_map));
186 	if (error)
187 		goto bad;
188 	error = copyin(f->bold, vf->vf_bold,
189 	    vf->vf_bold_length * sizeof(struct vt_font_map));
190 	if (error)
191 		goto bad;
192 	error = copyin(f->glyphs, vf->vf_bytes, glyphsize);
193 	if (error)
194 		goto bad;
195 
196 	/* Validate mappings. */
197 	error = vtfont_validate_map(vf->vf_normal, vf->vf_normal_length,
198 	    f->nglyphs);
199 	if (error)
200 		goto bad;
201 	error = vtfont_validate_map(vf->vf_bold, vf->vf_bold_length,
202 	    f->nglyphs);
203 	if (error)
204 		goto bad;
205 
206 	/* Success. */
207 	*ret = vf;
208 	return (0);
209 
210 bad:	vtfont_unref(vf);
211 	return (error);
212 }
213