mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 03:35:32 -04:00
Merge tag 'fbdev-for-7.2-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/deller/linux-fbdev
Pull fbdev fixes from Helge Deller: "A few patches for the core fbdev layer which stabilize or fix potential issues with text font rendering after screen rotation or after user initiated font changes and locking fixes for sysfb during modifications of the graphics mode database" * tag 'fbdev-for-7.2-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/deller/linux-fbdev: fbdev: bitblit: bound-check glyph index in bit_cursor() fbdev: Fix out-of-bounds access when rotating console after font resize fbdev: core: Fix pointer desynchronization in fb_io_read() fbdev: serialize mode sysfs access with lock_fb_info() fbdev: clear fb_info->mode before deleting a videomode fbdev: bound mode sysfs output to the sysfs buffer
This commit is contained in:
@@ -273,9 +273,14 @@ static void bit_cursor(struct vc_data *vc, struct fb_info *info, bool enable,
|
||||
if (!vc->vc_font.data)
|
||||
return;
|
||||
|
||||
c = scr_readw((u16 *) vc->vc_pos);
|
||||
c = scr_readw((u16 *) vc->vc_pos);
|
||||
attribute = get_attribute(info, c);
|
||||
src = vc->vc_font.data + ((c & charmask) * (w * vc->vc_font.height));
|
||||
c &= charmask;
|
||||
|
||||
/* Clamp to font size, same as bit_putcs_aligned() */
|
||||
if (c >= vc->vc_font.charcount)
|
||||
c = 0;
|
||||
src = vc->vc_font.data + (c * (w * vc->vc_font.height));
|
||||
|
||||
if (par->cursor_state.image.data != (const char *)src ||
|
||||
par->cursor_reset) {
|
||||
|
||||
@@ -61,6 +61,14 @@ ssize_t fb_io_read(struct fb_info *info, char __user *buf, size_t count, loff_t
|
||||
buf += c;
|
||||
cnt += c;
|
||||
count -= c;
|
||||
|
||||
/*
|
||||
* If there was a partial copy, the user buffer is faulty.
|
||||
* Break out to avoid over-advancing the src pointer and
|
||||
* reading out of bounds in the next iteration.
|
||||
*/
|
||||
if (trailing)
|
||||
break;
|
||||
}
|
||||
|
||||
kfree(buffer);
|
||||
|
||||
@@ -2641,9 +2641,31 @@ static void fbcon_modechanged(struct fb_info *info)
|
||||
fbcon_info_from_console(par->currcon) != info)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Clear the selection before switching bitops. Without this, the
|
||||
* clear_selection() inside vc_resize() below repaints the highlighted
|
||||
* cells through the new bitops while the console geometry(vc_rows/vc_cols)
|
||||
* has not been updated to match, so the repaint is computed from a
|
||||
* half-switched geometry and overflows the framebuffer address.
|
||||
* Pre-clearing makes that repaint a no-op.
|
||||
*/
|
||||
clear_selection();
|
||||
|
||||
p = &fb_display[vc->vc_num];
|
||||
set_blitting_type(vc, info);
|
||||
|
||||
/*
|
||||
* Rebuild par->rotated.buf for the new rotation now that bitops have
|
||||
* switched. The new putcs/cursor ops read this buffer; if it is still
|
||||
* sized for the old rotation, fbcon_putcs() and the cursor path reached
|
||||
* via update_screen() below overflow it. Mirrors fbcon_switch(); fall
|
||||
* back to unrotated rendering on allocation failure.
|
||||
*/
|
||||
if (par->bitops->rotate_font && par->bitops->rotate_font(info, vc)) {
|
||||
par->rotate = FB_ROTATE_UR;
|
||||
set_blitting_type(vc, info);
|
||||
}
|
||||
|
||||
if (con_is_visible(vc)) {
|
||||
var_to_display(p, &info->var, info);
|
||||
cols = FBCON_SWAP(par->rotate, info->var.xres, info->var.yres);
|
||||
@@ -2675,6 +2697,9 @@ static void fbcon_set_all_vcs(struct fb_info *info)
|
||||
if (!par || par->currcon < 0)
|
||||
return;
|
||||
|
||||
/* See the comment in fbcon_modechanged(). */
|
||||
clear_selection();
|
||||
|
||||
for (i = first_fb_vc; i <= last_fb_vc; i++) {
|
||||
vc = vc_cons[i].d;
|
||||
if (!vc || vc->vc_mode != KD_TEXT ||
|
||||
|
||||
@@ -246,8 +246,11 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var)
|
||||
ret = fb_mode_is_equal(&mode1, &mode2);
|
||||
if (!ret) {
|
||||
ret = fbcon_mode_deleted(info, &mode1);
|
||||
if (!ret)
|
||||
if (!ret) {
|
||||
if (info->mode && fb_mode_is_equal(info->mode, &mode1))
|
||||
info->mode = NULL;
|
||||
fb_delete_videomode(&mode1, &info->modelist);
|
||||
}
|
||||
}
|
||||
|
||||
return ret ? -EINVAL : 0;
|
||||
|
||||
@@ -12,27 +12,35 @@
|
||||
#include "fb_internal.h"
|
||||
#include "fbcon.h"
|
||||
|
||||
static int activate_locked(struct fb_info *fb_info,
|
||||
struct fb_var_screeninfo *var)
|
||||
{
|
||||
var->activate |= FB_ACTIVATE_FORCE;
|
||||
return fb_set_var_from_user(fb_info, var);
|
||||
}
|
||||
|
||||
static int activate(struct fb_info *fb_info, struct fb_var_screeninfo *var)
|
||||
{
|
||||
int err;
|
||||
|
||||
var->activate |= FB_ACTIVATE_FORCE;
|
||||
console_lock();
|
||||
lock_fb_info(fb_info);
|
||||
err = fb_set_var_from_user(fb_info, var);
|
||||
err = activate_locked(fb_info, var);
|
||||
unlock_fb_info(fb_info);
|
||||
console_unlock();
|
||||
if (err)
|
||||
return err;
|
||||
return 0;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mode_string(char *buf, unsigned int offset,
|
||||
static int mode_string(char *buf, size_t size, unsigned int offset,
|
||||
const struct fb_videomode *mode)
|
||||
{
|
||||
char m = 'U';
|
||||
char v = 'p';
|
||||
|
||||
if (offset >= size)
|
||||
return 0;
|
||||
|
||||
if (mode->flag & FB_MODE_IS_DETAILED)
|
||||
m = 'D';
|
||||
if (mode->flag & FB_MODE_IS_VESA)
|
||||
@@ -45,7 +53,7 @@ static int mode_string(char *buf, unsigned int offset,
|
||||
if (mode->vmode & FB_VMODE_DOUBLE)
|
||||
v = 'd';
|
||||
|
||||
return snprintf(&buf[offset], PAGE_SIZE - offset, "%c:%dx%d%c-%d\n",
|
||||
return scnprintf(&buf[offset], size - offset, "%c:%dx%d%c-%d\n",
|
||||
m, mode->xres, mode->yres, v, mode->refresh);
|
||||
}
|
||||
|
||||
@@ -62,19 +70,32 @@ static ssize_t store_mode(struct device *device, struct device_attribute *attr,
|
||||
|
||||
memset(&var, 0, sizeof(var));
|
||||
|
||||
console_lock();
|
||||
lock_fb_info(fb_info);
|
||||
|
||||
list_for_each_entry(modelist, &fb_info->modelist, list) {
|
||||
mode = &modelist->mode;
|
||||
i = mode_string(mstr, 0, mode);
|
||||
i = mode_string(mstr, sizeof(mstr), 0, mode);
|
||||
if (strncmp(mstr, buf, max(count, i)) == 0) {
|
||||
|
||||
var = fb_info->var;
|
||||
fb_videomode_to_var(&var, mode);
|
||||
if ((err = activate(fb_info, &var)))
|
||||
err = activate_locked(fb_info, &var);
|
||||
if (err) {
|
||||
unlock_fb_info(fb_info);
|
||||
console_unlock();
|
||||
return err;
|
||||
}
|
||||
fb_info->mode = mode;
|
||||
unlock_fb_info(fb_info);
|
||||
console_unlock();
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
unlock_fb_info(fb_info);
|
||||
console_unlock();
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -82,11 +103,20 @@ static ssize_t show_mode(struct device *device, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct fb_info *fb_info = dev_get_drvdata(device);
|
||||
struct fb_videomode mode;
|
||||
bool have_mode = false;
|
||||
|
||||
if (!fb_info->mode)
|
||||
lock_fb_info(fb_info);
|
||||
if (fb_info->mode) {
|
||||
mode = *fb_info->mode;
|
||||
have_mode = true;
|
||||
}
|
||||
unlock_fb_info(fb_info);
|
||||
|
||||
if (!have_mode)
|
||||
return 0;
|
||||
|
||||
return mode_string(buf, 0, fb_info->mode);
|
||||
return mode_string(buf, PAGE_SIZE, 0, &mode);
|
||||
}
|
||||
|
||||
static ssize_t store_modes(struct device *device,
|
||||
@@ -134,10 +164,15 @@ static ssize_t show_modes(struct device *device, struct device_attribute *attr,
|
||||
const struct fb_videomode *mode;
|
||||
|
||||
i = 0;
|
||||
lock_fb_info(fb_info);
|
||||
list_for_each_entry(modelist, &fb_info->modelist, list) {
|
||||
mode = &modelist->mode;
|
||||
i += mode_string(buf, i, mode);
|
||||
i += mode_string(buf, PAGE_SIZE, i, mode);
|
||||
if (i >= PAGE_SIZE - 1)
|
||||
break;
|
||||
}
|
||||
unlock_fb_info(fb_info);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user