xref: /freebsd/sys/dev/vt/vt_font.c (revision 718cf2ccb9956613756ab15d7a0e28f2c8e91cab)
127cf7d04SAleksandr Rybalko /*-
2*718cf2ccSPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3*718cf2ccSPedro F. Giffuni  *
427cf7d04SAleksandr Rybalko  * Copyright (c) 2009 The FreeBSD Foundation
527cf7d04SAleksandr Rybalko  * All rights reserved.
627cf7d04SAleksandr Rybalko  *
727cf7d04SAleksandr Rybalko  * This software was developed by Ed Schouten under sponsorship from the
827cf7d04SAleksandr Rybalko  * FreeBSD Foundation.
927cf7d04SAleksandr Rybalko  *
1027cf7d04SAleksandr Rybalko  * Redistribution and use in source and binary forms, with or without
1127cf7d04SAleksandr Rybalko  * modification, are permitted provided that the following conditions
1227cf7d04SAleksandr Rybalko  * are met:
1327cf7d04SAleksandr Rybalko  * 1. Redistributions of source code must retain the above copyright
1427cf7d04SAleksandr Rybalko  *    notice, this list of conditions and the following disclaimer.
1527cf7d04SAleksandr Rybalko  * 2. Redistributions in binary form must reproduce the above copyright
1627cf7d04SAleksandr Rybalko  *    notice, this list of conditions and the following disclaimer in the
1727cf7d04SAleksandr Rybalko  *    documentation and/or other materials provided with the distribution.
1827cf7d04SAleksandr Rybalko  *
1927cf7d04SAleksandr Rybalko  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2027cf7d04SAleksandr Rybalko  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2127cf7d04SAleksandr Rybalko  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2227cf7d04SAleksandr Rybalko  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2327cf7d04SAleksandr Rybalko  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2427cf7d04SAleksandr Rybalko  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2527cf7d04SAleksandr Rybalko  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2627cf7d04SAleksandr Rybalko  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2727cf7d04SAleksandr Rybalko  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2827cf7d04SAleksandr Rybalko  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2927cf7d04SAleksandr Rybalko  * SUCH DAMAGE.
3027cf7d04SAleksandr Rybalko  */
3127cf7d04SAleksandr Rybalko 
3227cf7d04SAleksandr Rybalko #include <sys/cdefs.h>
3327cf7d04SAleksandr Rybalko __FBSDID("$FreeBSD$");
3427cf7d04SAleksandr Rybalko 
3527cf7d04SAleksandr Rybalko #include <sys/param.h>
3627cf7d04SAleksandr Rybalko #include <sys/kernel.h>
3727cf7d04SAleksandr Rybalko #include <sys/malloc.h>
3827cf7d04SAleksandr Rybalko #include <sys/refcount.h>
3927cf7d04SAleksandr Rybalko #include <sys/systm.h>
4027cf7d04SAleksandr Rybalko 
4127cf7d04SAleksandr Rybalko #include <dev/vt/vt.h>
4227cf7d04SAleksandr Rybalko 
4327cf7d04SAleksandr Rybalko static MALLOC_DEFINE(M_VTFONT, "vtfont", "vt font");
4427cf7d04SAleksandr Rybalko 
4527cf7d04SAleksandr Rybalko /* Some limits to prevent abnormal fonts from being loaded. */
46a04eaf90SEd Maste #define	VTFONT_MAXMAPPINGS	65536
47a04eaf90SEd Maste #define	VTFONT_MAXGLYPHSIZE	2097152
4827cf7d04SAleksandr Rybalko #define	VTFONT_MAXDIMENSION	128
4927cf7d04SAleksandr Rybalko 
5027cf7d04SAleksandr Rybalko static uint16_t
5127cf7d04SAleksandr Rybalko vtfont_bisearch(const struct vt_font_map *map, unsigned int len, uint32_t src)
5227cf7d04SAleksandr Rybalko {
5327cf7d04SAleksandr Rybalko 	int min, mid, max;
5427cf7d04SAleksandr Rybalko 
5527cf7d04SAleksandr Rybalko 	min = 0;
5627cf7d04SAleksandr Rybalko 	max = len - 1;
5727cf7d04SAleksandr Rybalko 
5827cf7d04SAleksandr Rybalko 	/* Empty font map. */
5927cf7d04SAleksandr Rybalko 	if (len == 0)
6027cf7d04SAleksandr Rybalko 		return (0);
6127cf7d04SAleksandr Rybalko 	/* Character below minimal entry. */
6227cf7d04SAleksandr Rybalko 	if (src < map[0].vfm_src)
6327cf7d04SAleksandr Rybalko 		return (0);
6427cf7d04SAleksandr Rybalko 	/* Optimization: ASCII characters occur very often. */
6527cf7d04SAleksandr Rybalko 	if (src <= map[0].vfm_src + map[0].vfm_len)
6627cf7d04SAleksandr Rybalko 		return (src - map[0].vfm_src + map[0].vfm_dst);
6727cf7d04SAleksandr Rybalko 	/* Character above maximum entry. */
6827cf7d04SAleksandr Rybalko 	if (src > map[max].vfm_src + map[max].vfm_len)
6927cf7d04SAleksandr Rybalko 		return (0);
7027cf7d04SAleksandr Rybalko 
7127cf7d04SAleksandr Rybalko 	/* Binary search. */
7227cf7d04SAleksandr Rybalko 	while (max >= min) {
7327cf7d04SAleksandr Rybalko 		mid = (min + max) / 2;
7427cf7d04SAleksandr Rybalko 		if (src < map[mid].vfm_src)
7527cf7d04SAleksandr Rybalko 			max = mid - 1;
7627cf7d04SAleksandr Rybalko 		else if (src > map[mid].vfm_src + map[mid].vfm_len)
7727cf7d04SAleksandr Rybalko 			min = mid + 1;
7827cf7d04SAleksandr Rybalko 		else
7927cf7d04SAleksandr Rybalko 			return (src - map[mid].vfm_src + map[mid].vfm_dst);
8027cf7d04SAleksandr Rybalko 	}
8127cf7d04SAleksandr Rybalko 
8227cf7d04SAleksandr Rybalko 	return (0);
8327cf7d04SAleksandr Rybalko }
8427cf7d04SAleksandr Rybalko 
8527cf7d04SAleksandr Rybalko const uint8_t *
8627cf7d04SAleksandr Rybalko vtfont_lookup(const struct vt_font *vf, term_char_t c)
8727cf7d04SAleksandr Rybalko {
8827cf7d04SAleksandr Rybalko 	uint32_t src;
8927cf7d04SAleksandr Rybalko 	uint16_t dst;
9027cf7d04SAleksandr Rybalko 	size_t stride;
9141fb0665SEd Maste 	unsigned int normal_map;
9241fb0665SEd Maste 	unsigned int bold_map;
9327cf7d04SAleksandr Rybalko 
94a6c26592SEd Schouten 	/*
95a6c26592SEd Schouten 	 * No support for printing right hand sides for CJK fullwidth
96a6c26592SEd Schouten 	 * characters. Simply print a space and assume that the left
97a6c26592SEd Schouten 	 * hand side describes the entire character.
98a6c26592SEd Schouten 	 */
9927cf7d04SAleksandr Rybalko 	src = TCHAR_CHARACTER(c);
10041fb0665SEd Maste 	if (TCHAR_FORMAT(c) & TF_CJK_RIGHT) {
10141fb0665SEd Maste 		normal_map = VFNT_MAP_NORMAL_RIGHT;
10241fb0665SEd Maste 		bold_map = VFNT_MAP_BOLD_RIGHT;
10341fb0665SEd Maste 	} else {
10441fb0665SEd Maste 		normal_map = VFNT_MAP_NORMAL;
10541fb0665SEd Maste 		bold_map = VFNT_MAP_BOLD;
10641fb0665SEd Maste 	}
107a6c26592SEd Schouten 
10827cf7d04SAleksandr Rybalko 	if (TCHAR_FORMAT(c) & TF_BOLD) {
10941fb0665SEd Maste 		dst = vtfont_bisearch(vf->vf_map[bold_map],
11041fb0665SEd Maste 		    vf->vf_map_count[bold_map], src);
11127cf7d04SAleksandr Rybalko 		if (dst != 0)
11227cf7d04SAleksandr Rybalko 			goto found;
11327cf7d04SAleksandr Rybalko 	}
11441fb0665SEd Maste 	dst = vtfont_bisearch(vf->vf_map[normal_map],
11541fb0665SEd Maste 	    vf->vf_map_count[normal_map], src);
11627cf7d04SAleksandr Rybalko 
11727cf7d04SAleksandr Rybalko found:
11827cf7d04SAleksandr Rybalko 	stride = howmany(vf->vf_width, 8) * vf->vf_height;
11927cf7d04SAleksandr Rybalko 	return (&vf->vf_bytes[dst * stride]);
12027cf7d04SAleksandr Rybalko }
12127cf7d04SAleksandr Rybalko 
12227cf7d04SAleksandr Rybalko struct vt_font *
12327cf7d04SAleksandr Rybalko vtfont_ref(struct vt_font *vf)
12427cf7d04SAleksandr Rybalko {
12527cf7d04SAleksandr Rybalko 
12627cf7d04SAleksandr Rybalko 	refcount_acquire(&vf->vf_refcount);
12727cf7d04SAleksandr Rybalko 	return (vf);
12827cf7d04SAleksandr Rybalko }
12927cf7d04SAleksandr Rybalko 
13027cf7d04SAleksandr Rybalko void
13127cf7d04SAleksandr Rybalko vtfont_unref(struct vt_font *vf)
13227cf7d04SAleksandr Rybalko {
13341fb0665SEd Maste 	unsigned int i;
13427cf7d04SAleksandr Rybalko 
13527cf7d04SAleksandr Rybalko 	if (refcount_release(&vf->vf_refcount)) {
13641fb0665SEd Maste 		for (i = 0; i < VFNT_MAPS; i++)
13741fb0665SEd Maste 			free(vf->vf_map[i], M_VTFONT);
13827cf7d04SAleksandr Rybalko 		free(vf->vf_bytes, M_VTFONT);
13927cf7d04SAleksandr Rybalko 		free(vf, M_VTFONT);
14027cf7d04SAleksandr Rybalko 	}
14127cf7d04SAleksandr Rybalko }
14227cf7d04SAleksandr Rybalko 
14327cf7d04SAleksandr Rybalko static int
14427cf7d04SAleksandr Rybalko vtfont_validate_map(struct vt_font_map *vfm, unsigned int length,
14541fb0665SEd Maste     unsigned int glyph_count)
14627cf7d04SAleksandr Rybalko {
14727cf7d04SAleksandr Rybalko 	unsigned int i, last = 0;
14827cf7d04SAleksandr Rybalko 
14927cf7d04SAleksandr Rybalko 	for (i = 0; i < length; i++) {
15027cf7d04SAleksandr Rybalko 		/* Not ordered. */
15127cf7d04SAleksandr Rybalko 		if (i > 0 && vfm[i].vfm_src <= last)
15227cf7d04SAleksandr Rybalko 			return (EINVAL);
15327cf7d04SAleksandr Rybalko 		/*
15427cf7d04SAleksandr Rybalko 		 * Destination extends amount of glyphs.
15527cf7d04SAleksandr Rybalko 		 */
15641fb0665SEd Maste 		if (vfm[i].vfm_dst >= glyph_count ||
15741fb0665SEd Maste 		    vfm[i].vfm_dst + vfm[i].vfm_len >= glyph_count)
15827cf7d04SAleksandr Rybalko 			return (EINVAL);
15927cf7d04SAleksandr Rybalko 		last = vfm[i].vfm_src + vfm[i].vfm_len;
16027cf7d04SAleksandr Rybalko 	}
16127cf7d04SAleksandr Rybalko 
16227cf7d04SAleksandr Rybalko 	return (0);
16327cf7d04SAleksandr Rybalko }
16427cf7d04SAleksandr Rybalko 
16527cf7d04SAleksandr Rybalko int
16627cf7d04SAleksandr Rybalko vtfont_load(vfnt_t *f, struct vt_font **ret)
16727cf7d04SAleksandr Rybalko {
16841fb0665SEd Maste 	size_t glyphsize, mapsize;
16927cf7d04SAleksandr Rybalko 	struct vt_font *vf;
17027cf7d04SAleksandr Rybalko 	int error;
17141fb0665SEd Maste 	unsigned int i;
17227cf7d04SAleksandr Rybalko 
17327cf7d04SAleksandr Rybalko 	/* Make sure the dimensions are valid. */
17427cf7d04SAleksandr Rybalko 	if (f->width < 1 || f->height < 1)
17527cf7d04SAleksandr Rybalko 		return (EINVAL);
17627cf7d04SAleksandr Rybalko 	if (f->width > VTFONT_MAXDIMENSION || f->height > VTFONT_MAXDIMENSION)
17727cf7d04SAleksandr Rybalko 		return (E2BIG);
17827cf7d04SAleksandr Rybalko 
17927cf7d04SAleksandr Rybalko 	/* Not too many mappings. */
18041fb0665SEd Maste 	for (i = 0; i < VFNT_MAPS; i++)
18141fb0665SEd Maste 		if (f->map_count[i] > VTFONT_MAXMAPPINGS)
18227cf7d04SAleksandr Rybalko 			return (E2BIG);
18327cf7d04SAleksandr Rybalko 
18427cf7d04SAleksandr Rybalko 	/* Character 0 must always be present. */
18541fb0665SEd Maste 	if (f->glyph_count < 1)
18627cf7d04SAleksandr Rybalko 		return (EINVAL);
18727cf7d04SAleksandr Rybalko 
18841fb0665SEd Maste 	glyphsize = howmany(f->width, 8) * f->height * f->glyph_count;
18927cf7d04SAleksandr Rybalko 	if (glyphsize > VTFONT_MAXGLYPHSIZE)
19027cf7d04SAleksandr Rybalko 		return (E2BIG);
19127cf7d04SAleksandr Rybalko 
19227cf7d04SAleksandr Rybalko 	/* Allocate new font structure. */
19341fb0665SEd Maste 	vf = malloc(sizeof *vf, M_VTFONT, M_WAITOK | M_ZERO);
19427cf7d04SAleksandr Rybalko 	vf->vf_bytes = malloc(glyphsize, M_VTFONT, M_WAITOK);
19527cf7d04SAleksandr Rybalko 	vf->vf_height = f->height;
19627cf7d04SAleksandr Rybalko 	vf->vf_width = f->width;
19727cf7d04SAleksandr Rybalko 	vf->vf_refcount = 1;
19827cf7d04SAleksandr Rybalko 
19941fb0665SEd Maste 	/* Allocate, copy in, and validate mappings. */
20041fb0665SEd Maste 	for (i = 0; i < VFNT_MAPS; i++) {
20141fb0665SEd Maste 		vf->vf_map_count[i] = f->map_count[i];
20241fb0665SEd Maste 		if (f->map_count[i] == 0)
20341fb0665SEd Maste 			continue;
20441fb0665SEd Maste 		mapsize = f->map_count[i] * sizeof(struct vt_font_map);
20541fb0665SEd Maste 		vf->vf_map[i] = malloc(mapsize, M_VTFONT, M_WAITOK);
20641fb0665SEd Maste 		error = copyin(f->map[i], vf->vf_map[i], mapsize);
20727cf7d04SAleksandr Rybalko 		if (error)
20827cf7d04SAleksandr Rybalko 			goto bad;
20941fb0665SEd Maste 		error = vtfont_validate_map(vf->vf_map[i], vf->vf_map_count[i],
21041fb0665SEd Maste 		    f->glyph_count);
21127cf7d04SAleksandr Rybalko 		if (error)
21227cf7d04SAleksandr Rybalko 			goto bad;
21341fb0665SEd Maste 	}
21427cf7d04SAleksandr Rybalko 
21541fb0665SEd Maste 	/* Copy in glyph data. */
21641fb0665SEd Maste 	error = copyin(f->glyphs, vf->vf_bytes, glyphsize);
21727cf7d04SAleksandr Rybalko 	if (error)
21827cf7d04SAleksandr Rybalko 		goto bad;
21927cf7d04SAleksandr Rybalko 
22027cf7d04SAleksandr Rybalko 	/* Success. */
22127cf7d04SAleksandr Rybalko 	*ret = vf;
22227cf7d04SAleksandr Rybalko 	return (0);
22327cf7d04SAleksandr Rybalko 
22427cf7d04SAleksandr Rybalko bad:	vtfont_unref(vf);
22527cf7d04SAleksandr Rybalko 	return (error);
22627cf7d04SAleksandr Rybalko }
227