diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c index db0228bce00e..e88ff3a93b77 100644 --- a/drivers/video/console/newport_con.c +++ b/drivers/video/console/newport_con.c @@ -501,31 +501,17 @@ static int newport_set_font(int unit, const struct console_font *op, { int w = op->width; int h = op->height; - int size = h * op->charcount; int i; font_data_t *new_data; - unsigned char *data = op->data, *p; /* ladis: when I grow up, there will be a day... and more sizes will * be supported ;-) */ - if ((w != 8) || (h != 16) || (vpitch != 32) - || (op->charcount != 256 && op->charcount != 512)) + if (w != 8 || h != 16 || (op->charcount != 256 && op->charcount != 512)) return -EINVAL; - if (!(new_data = kmalloc(FONT_EXTRA_WORDS * sizeof(int) + size, - GFP_USER))) return -ENOMEM; - - new_data += FONT_EXTRA_WORDS * sizeof(int); - FNTSIZE(new_data) = size; - REFCOUNT(new_data) = 1; /* usage counter */ - FNTSUM(new_data) = 0; - - p = (unsigned char *)font_data_buf(new_data); - for (i = 0; i < op->charcount; i++) { - memcpy(p, data, h); - data += 32; - p += h; - } + new_data = font_data_import(op, vpitch, NULL); + if (IS_ERR(new_data)) + return PTR_ERR(new_data); /* check if font is already used by other console */ for (i = 0; i < MAX_NR_CONSOLES; i++) { diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index 00255ac92e42..53677c09a0ec 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -2039,8 +2039,6 @@ static void updatescrollmode(struct fbcon_display *p, updatescrollmode_accel(p, info, vc); } -#define PITCH(w) (((w) + 7) >> 3) - static int fbcon_resize(struct vc_data *vc, unsigned int width, unsigned int height, bool from_user) { @@ -2424,7 +2422,6 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, int charcount, resize = (w != vc->vc_font.width) || (h != vc->vc_font.height); p->fontdata = data; vc->vc_font.data = font_data_buf(p->fontdata); - old_width = vc->vc_font.width; old_height = vc->vc_font.height; old_charcount = vc->vc_font.charcount; @@ -2482,11 +2479,8 @@ static int fbcon_set_font(struct vc_data *vc, const struct console_font *font, unsigned charcount = font->charcount; int w = font->width; int h = font->height; - int size, alloc_size; - int i, csum, ret; + int i, ret; font_data_t *new_data; - const u8 *data = font->data; - int pitch = PITCH(font->width); /* Is there a reason why fbconsole couldn't handle any charcount >256? * If not this check should be changed to charcount < 256 */ @@ -2510,34 +2504,10 @@ static int fbcon_set_font(struct vc_data *vc, const struct console_font *font, if (fbcon_invalid_charcount(info, charcount)) return -EINVAL; - /* Check for integer overflow in font size calculation */ - if (check_mul_overflow(h, pitch, &size) || - check_mul_overflow(size, charcount, &size)) - return -EINVAL; + new_data = font_data_import(font, vpitch, crc32); + if (IS_ERR(new_data)) + return PTR_ERR(new_data); - /* Check for overflow in allocation size calculation */ - if (check_add_overflow(FONT_EXTRA_WORDS * sizeof(int), size, &alloc_size)) - return -EINVAL; - - new_data = kmalloc(alloc_size, GFP_USER); - - if (!new_data) - return -ENOMEM; - - memset((u8 *)new_data, 0, FONT_EXTRA_WORDS * sizeof(int)); - - new_data += FONT_EXTRA_WORDS * sizeof(int); - FNTSIZE(new_data) = size; - REFCOUNT(new_data) = 1; /* usage counter */ - for (i=0; i< charcount; i++) { - memcpy((u8 *)new_data + i * h * pitch, data + i * vpitch * pitch, h * pitch); - } - - /* Since linux has a nice crc32 function use it for counting font - * checksums. */ - csum = crc32(0, new_data, size); - - FNTSUM(new_data) = csum; /* Check if the same font is on some other console already */ for (i = first_fb_vc; i <= last_fb_vc; i++) { if (fb_display[i].fontdata && diff --git a/include/linux/font.h b/include/linux/font.h index 58bf3c64cabb..3eb4818402c5 100644 --- a/include/linux/font.h +++ b/include/linux/font.h @@ -13,6 +13,8 @@ #include +struct console_font; + /* * font_data_t and helpers */ @@ -54,6 +56,8 @@ static inline const unsigned char *font_data_buf(font_data_t *fd) return (const unsigned char *)fd; } +font_data_t *font_data_import(const struct console_font *font, unsigned int vpitch, + u32 (*calc_csum)(u32, const void *, size_t)); void font_data_get(font_data_t *fd); bool font_data_put(font_data_t *fd); unsigned int font_data_size(font_data_t *fd); @@ -124,7 +128,7 @@ extern const struct font_desc *get_default_font(int xres, int yres, struct font_data { unsigned int extra[FONT_EXTRA_WORDS]; - const unsigned char data[]; + unsigned char data[]; } __packed; #endif /* _VIDEO_FONT_H */ diff --git a/lib/fonts/fonts.c b/lib/fonts/fonts.c index 3fb76d185647..16e75c3d2a0f 100644 --- a/lib/fonts/fonts.c +++ b/lib/fonts/fonts.c @@ -14,7 +14,9 @@ #include #include +#include #include +#include #include #include #include @@ -23,6 +25,8 @@ #include #endif +#define console_font_pitch(font) DIV_ROUND_UP((font)->width, 8) + /* * Helpers for font_data_t */ @@ -42,6 +46,64 @@ static void font_data_free(font_data_t *fd) kfree(to_font_data_struct(fd)); } +/** + * font_data_import - Allocates and initializes font data from user space + * @font: A font from user space + * @vpitch: The size of a single glyph in @font in bytes + * @calc_csum: An optional helper to calculate a chechsum + * + * Font data from user space must be translated to the kernel's format. The + * font's glyph geometry and data is provided in @font. The parameter @vpitch + * gives the number of bytes per glyph, including trailing bytes. + * + * The parameter @calc_csum is optional. Fbcon passes crc32() to calculate the + * font data's checksum. + * + * Returns: + * Newly initialized font data on success, or a pointer-encoded errno value otherwise. + */ +font_data_t *font_data_import(const struct console_font *font, unsigned int vpitch, + u32 (*calc_csum)(u32, const void *, size_t)) +{ + unsigned int pitch = console_font_pitch(font); + unsigned int h = font->height; + unsigned int charcount = font->charcount; + const unsigned char *data = font->data; + u32 csum = 0; + struct font_data *font_data; + int size, alloc_size; + unsigned int i; + font_data_t *fd; + + /* Check for integer overflow in font-size calculation */ + if (check_mul_overflow(h, pitch, &size) || + check_mul_overflow(size, charcount, &size)) + return ERR_PTR(-EINVAL); + + /* Check for overflow in allocation size calculation */ + if (check_add_overflow(sizeof(*font_data), size, &alloc_size)) + return ERR_PTR(-EINVAL); + + font_data = kmalloc(alloc_size, GFP_USER); + if (!font_data) + return ERR_PTR(-ENOMEM); + memset(font_data->extra, 0, sizeof(font_data->extra)); + + for (i = 0; i < charcount; ++i) + memcpy(font_data->data + i * h * pitch, data + i * vpitch * pitch, h * pitch); + + if (calc_csum) + csum = calc_csum(0, font_data->data, size); + + fd = font_data->data; + REFCOUNT(fd) = 1; /* start with reference acquired */ + FNTSIZE(fd) = size; + FNTSUM(fd) = csum; + + return fd; +} +EXPORT_SYMBOL_GPL(font_data_import); + /** * font_data_get - Acquires a reference on font data * @fd: Font data