parisc: sticon - unbreak on 64bit kernel

STI text console (sticon) was broken on 64bit machines with more than
4GB RAM and this lead in some cases to a kernel crash.

Since sticon uses the 32bit STI API it needs to keep pointers to memory
below 4GB. But on a 64bit kernel some memory regions (e.g. the kernel
stack) might be above 4GB which then may crash the kernel in the STI
functions.

Additionally sticon didn't selected the built-in framebuffer fonts by
default. This is now fixed.

On a side-note: Theoretically we could enhance the sticon driver to
use the 64bit STI API. But - beside the fact that some machines don't
provide a 64bit STI ROM - this would just add complexity.

Signed-off-by: Helge Deller <deller@gmx.de>
Cc: stable@vger.kernel.org # 3.8+
This commit is contained in:
Helge Deller 2013-11-06 23:38:59 +01:00
parent 1f2048fd8b
commit 0219132fe7
3 changed files with 158 additions and 80 deletions

View file

@ -3,7 +3,7 @@
* core code for console driver using HP's STI firmware
*
* Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
* Copyright (C) 2001-2003 Helge Deller <deller@gmx.de>
* Copyright (C) 2001-2013 Helge Deller <deller@gmx.de>
* Copyright (C) 2001-2002 Thomas Bogendoerfer <tsbogend@alpha.franken.de>
*
* TODO:
@ -30,7 +30,7 @@
#include "../sticore.h"
#define STI_DRIVERVERSION "Version 0.9a"
#define STI_DRIVERVERSION "Version 0.9b"
static struct sti_struct *default_sti __read_mostly;
@ -73,28 +73,34 @@ static const struct sti_init_flags default_init_flags = {
static int sti_init_graph(struct sti_struct *sti)
{
struct sti_init_inptr_ext inptr_ext = { 0, };
struct sti_init_inptr inptr = {
.text_planes = 3, /* # of text planes (max 3 for STI) */
.ext_ptr = STI_PTR(&inptr_ext)
};
struct sti_init_outptr outptr = { 0, };
struct sti_init_inptr *inptr = &sti->sti_data->init_inptr;
struct sti_init_inptr_ext *inptr_ext = &sti->sti_data->init_inptr_ext;
struct sti_init_outptr *outptr = &sti->sti_data->init_outptr;
unsigned long flags;
int ret;
int ret, err;
spin_lock_irqsave(&sti->lock, flags);
ret = STI_CALL(sti->init_graph, &default_init_flags, &inptr,
&outptr, sti->glob_cfg);
memset(inptr, 0, sizeof(*inptr));
inptr->text_planes = 3; /* # of text planes (max 3 for STI) */
memset(inptr_ext, 0, sizeof(*inptr_ext));
inptr->ext_ptr = STI_PTR(inptr_ext);
outptr->errno = 0;
ret = sti_call(sti, sti->init_graph, &default_init_flags, inptr,
outptr, sti->glob_cfg);
if (ret >= 0)
sti->text_planes = outptr->text_planes;
err = outptr->errno;
spin_unlock_irqrestore(&sti->lock, flags);
if (ret < 0) {
printk(KERN_ERR "STI init_graph failed (ret %d, errno %d)\n",ret,outptr.errno);
pr_err("STI init_graph failed (ret %d, errno %d)\n", ret, err);
return -1;
}
sti->text_planes = outptr.text_planes;
return 0;
}
@ -104,16 +110,18 @@ static const struct sti_conf_flags default_conf_flags = {
static void sti_inq_conf(struct sti_struct *sti)
{
struct sti_conf_inptr inptr = { 0, };
struct sti_conf_inptr *inptr = &sti->sti_data->inq_inptr;
struct sti_conf_outptr *outptr = &sti->sti_data->inq_outptr;
unsigned long flags;
s32 ret;
sti->outptr.ext_ptr = STI_PTR(&sti->outptr_ext);
outptr->ext_ptr = STI_PTR(&sti->sti_data->inq_outptr_ext);
do {
spin_lock_irqsave(&sti->lock, flags);
ret = STI_CALL(sti->inq_conf, &default_conf_flags,
&inptr, &sti->outptr, sti->glob_cfg);
memset(inptr, 0, sizeof(*inptr));
ret = sti_call(sti, sti->inq_conf, &default_conf_flags,
inptr, outptr, sti->glob_cfg);
spin_unlock_irqrestore(&sti->lock, flags);
} while (ret == 1);
}
@ -126,7 +134,8 @@ static const struct sti_font_flags default_font_flags = {
void
sti_putc(struct sti_struct *sti, int c, int y, int x)
{
struct sti_font_inptr inptr = {
struct sti_font_inptr *inptr = &sti->sti_data->font_inptr;
struct sti_font_inptr inptr_default = {
.font_start_addr= STI_PTR(sti->font->raw),
.index = c_index(sti, c),
.fg_color = c_fg(sti, c),
@ -134,14 +143,15 @@ sti_putc(struct sti_struct *sti, int c, int y, int x)
.dest_x = x * sti->font_width,
.dest_y = y * sti->font_height,
};
struct sti_font_outptr outptr = { 0, };
struct sti_font_outptr *outptr = &sti->sti_data->font_outptr;
s32 ret;
unsigned long flags;
do {
spin_lock_irqsave(&sti->lock, flags);
ret = STI_CALL(sti->font_unpmv, &default_font_flags,
&inptr, &outptr, sti->glob_cfg);
*inptr = inptr_default;
ret = sti_call(sti, sti->font_unpmv, &default_font_flags,
inptr, outptr, sti->glob_cfg);
spin_unlock_irqrestore(&sti->lock, flags);
} while (ret == 1);
}
@ -156,7 +166,8 @@ void
sti_set(struct sti_struct *sti, int src_y, int src_x,
int height, int width, u8 color)
{
struct sti_blkmv_inptr inptr = {
struct sti_blkmv_inptr *inptr = &sti->sti_data->blkmv_inptr;
struct sti_blkmv_inptr inptr_default = {
.fg_color = color,
.bg_color = color,
.src_x = src_x,
@ -166,14 +177,15 @@ sti_set(struct sti_struct *sti, int src_y, int src_x,
.width = width,
.height = height,
};
struct sti_blkmv_outptr outptr = { 0, };
struct sti_blkmv_outptr *outptr = &sti->sti_data->blkmv_outptr;
s32 ret;
unsigned long flags;
do {
spin_lock_irqsave(&sti->lock, flags);
ret = STI_CALL(sti->block_move, &clear_blkmv_flags,
&inptr, &outptr, sti->glob_cfg);
*inptr = inptr_default;
ret = sti_call(sti, sti->block_move, &clear_blkmv_flags,
inptr, outptr, sti->glob_cfg);
spin_unlock_irqrestore(&sti->lock, flags);
} while (ret == 1);
}
@ -182,7 +194,8 @@ void
sti_clear(struct sti_struct *sti, int src_y, int src_x,
int height, int width, int c)
{
struct sti_blkmv_inptr inptr = {
struct sti_blkmv_inptr *inptr = &sti->sti_data->blkmv_inptr;
struct sti_blkmv_inptr inptr_default = {
.fg_color = c_fg(sti, c),
.bg_color = c_bg(sti, c),
.src_x = src_x * sti->font_width,
@ -192,14 +205,15 @@ sti_clear(struct sti_struct *sti, int src_y, int src_x,
.width = width * sti->font_width,
.height = height* sti->font_height,
};
struct sti_blkmv_outptr outptr = { 0, };
struct sti_blkmv_outptr *outptr = &sti->sti_data->blkmv_outptr;
s32 ret;
unsigned long flags;
do {
spin_lock_irqsave(&sti->lock, flags);
ret = STI_CALL(sti->block_move, &clear_blkmv_flags,
&inptr, &outptr, sti->glob_cfg);
*inptr = inptr_default;
ret = sti_call(sti, sti->block_move, &clear_blkmv_flags,
inptr, outptr, sti->glob_cfg);
spin_unlock_irqrestore(&sti->lock, flags);
} while (ret == 1);
}
@ -212,7 +226,8 @@ void
sti_bmove(struct sti_struct *sti, int src_y, int src_x,
int dst_y, int dst_x, int height, int width)
{
struct sti_blkmv_inptr inptr = {
struct sti_blkmv_inptr *inptr = &sti->sti_data->blkmv_inptr;
struct sti_blkmv_inptr inptr_default = {
.src_x = src_x * sti->font_width,
.src_y = src_y * sti->font_height,
.dest_x = dst_x * sti->font_width,
@ -220,14 +235,15 @@ sti_bmove(struct sti_struct *sti, int src_y, int src_x,
.width = width * sti->font_width,
.height = height* sti->font_height,
};
struct sti_blkmv_outptr outptr = { 0, };
struct sti_blkmv_outptr *outptr = &sti->sti_data->blkmv_outptr;
s32 ret;
unsigned long flags;
do {
spin_lock_irqsave(&sti->lock, flags);
ret = STI_CALL(sti->block_move, &default_blkmv_flags,
&inptr, &outptr, sti->glob_cfg);
*inptr = inptr_default;
ret = sti_call(sti, sti->block_move, &default_blkmv_flags,
inptr, outptr, sti->glob_cfg);
spin_unlock_irqrestore(&sti->lock, flags);
} while (ret == 1);
}
@ -284,7 +300,7 @@ __setup("sti=", sti_setup);
static char *font_name[MAX_STI_ROMS] = { "VGA8x16", };
static char *font_name[MAX_STI_ROMS];
static int font_index[MAX_STI_ROMS],
font_height[MAX_STI_ROMS],
font_width[MAX_STI_ROMS];
@ -389,10 +405,10 @@ static void sti_dump_outptr(struct sti_struct *sti)
"%d used bits\n"
"%d planes\n"
"attributes %08x\n",
sti->outptr.bits_per_pixel,
sti->outptr.bits_used,
sti->outptr.planes,
sti->outptr.attributes));
sti->sti_data->inq_outptr.bits_per_pixel,
sti->sti_data->inq_outptr.bits_used,
sti->sti_data->inq_outptr.planes,
sti->sti_data->inq_outptr.attributes));
}
static int sti_init_glob_cfg(struct sti_struct *sti, unsigned long rom_address,
@ -402,24 +418,21 @@ static int sti_init_glob_cfg(struct sti_struct *sti, unsigned long rom_address,
struct sti_glob_cfg_ext *glob_cfg_ext;
void *save_addr;
void *sti_mem_addr;
const int save_addr_size = 1024; /* XXX */
int i;
int i, size;
if (!sti->sti_mem_request)
if (sti->sti_mem_request < 256)
sti->sti_mem_request = 256; /* STI default */
glob_cfg = kzalloc(sizeof(*sti->glob_cfg), GFP_KERNEL);
glob_cfg_ext = kzalloc(sizeof(*glob_cfg_ext), GFP_KERNEL);
save_addr = kzalloc(save_addr_size, GFP_KERNEL);
sti_mem_addr = kzalloc(sti->sti_mem_request, GFP_KERNEL);
size = sizeof(struct sti_all_data) + sti->sti_mem_request - 256;
if (!(glob_cfg && glob_cfg_ext && save_addr && sti_mem_addr)) {
kfree(glob_cfg);
kfree(glob_cfg_ext);
kfree(save_addr);
kfree(sti_mem_addr);
sti->sti_data = kzalloc(size, STI_LOWMEM);
if (!sti->sti_data)
return -ENOMEM;
}
glob_cfg = &sti->sti_data->glob_cfg;
glob_cfg_ext = &sti->sti_data->glob_cfg_ext;
save_addr = &sti->sti_data->save_addr;
sti_mem_addr = &sti->sti_data->sti_mem_addr;
glob_cfg->ext_ptr = STI_PTR(glob_cfg_ext);
glob_cfg->save_addr = STI_PTR(save_addr);
@ -475,32 +488,31 @@ static int sti_init_glob_cfg(struct sti_struct *sti, unsigned long rom_address,
return 0;
}
#ifdef CONFIG_FB
#ifdef CONFIG_FONTS
static struct sti_cooked_font *
sti_select_fbfont(struct sti_cooked_rom *cooked_rom, const char *fbfont_name)
{
const struct font_desc *fbfont;
const struct font_desc *fbfont = NULL;
unsigned int size, bpc;
void *dest;
struct sti_rom_font *nf;
struct sti_cooked_font *cooked_font;
if (!fbfont_name || !strlen(fbfont_name))
return NULL;
fbfont = find_font(fbfont_name);
if (fbfont_name && strlen(fbfont_name))
fbfont = find_font(fbfont_name);
if (!fbfont)
fbfont = get_default_font(1024,768, ~(u32)0, ~(u32)0);
if (!fbfont)
return NULL;
DPRINTK((KERN_DEBUG "selected %dx%d fb-font %s\n",
fbfont->width, fbfont->height, fbfont->name));
pr_info("STI selected %dx%d framebuffer font %s for sticon\n",
fbfont->width, fbfont->height, fbfont->name);
bpc = ((fbfont->width+7)/8) * fbfont->height;
size = bpc * 256;
size += sizeof(struct sti_rom_font);
nf = kzalloc(size, GFP_KERNEL);
nf = kzalloc(size, STI_LOWMEM);
if (!nf)
return NULL;
@ -637,7 +649,7 @@ static void *sti_bmode_font_raw(struct sti_cooked_font *f)
unsigned char *n, *p, *q;
int size = f->raw->bytes_per_char*256+sizeof(struct sti_rom_font);
n = kzalloc (4*size, GFP_KERNEL);
n = kzalloc(4*size, STI_LOWMEM);
if (!n)
return NULL;
p = n + 3;
@ -673,7 +685,7 @@ static struct sti_rom *sti_get_bmode_rom (unsigned long address)
sti_bmode_rom_copy(address + BMODE_LAST_ADDR_OFFS, sizeof(size), &size);
size = (size+3) / 4;
raw = kmalloc(size, GFP_KERNEL);
raw = kmalloc(size, STI_LOWMEM);
if (raw) {
sti_bmode_rom_copy(address, size, raw);
memmove (&raw->res004, &raw->type[0], 0x3c);
@ -707,7 +719,7 @@ static struct sti_rom *sti_get_wmode_rom(unsigned long address)
/* read the ROM size directly from the struct in ROM */
size = gsc_readl(address + offsetof(struct sti_rom,last_addr));
raw = kmalloc(size, GFP_KERNEL);
raw = kmalloc(size, STI_LOWMEM);
if (raw)
sti_rom_copy(address, size, raw);
@ -743,6 +755,10 @@ static int sti_read_rom(int wordmode, struct sti_struct *sti,
address = (unsigned long) STI_PTR(raw);
pr_info("STI ROM supports 32 %sbit firmware functions.\n",
raw->alt_code_type == ALT_CODE_TYPE_PA_RISC_64
? "and 64 " : "");
sti->font_unpmv = address + (raw->font_unpmv & 0x03ffffff);
sti->block_move = address + (raw->block_move & 0x03ffffff);
sti->init_graph = address + (raw->init_graph & 0x03ffffff);
@ -901,7 +917,8 @@ test_rom:
sti_dump_globcfg(sti->glob_cfg, sti->sti_mem_request);
sti_dump_outptr(sti);
printk(KERN_INFO " graphics card name: %s\n", sti->outptr.dev_name );
pr_info(" graphics card name: %s\n",
sti->sti_data->inq_outptr.dev_name);
sti_roms[num_sti_roms] = sti;
num_sti_roms++;
@ -1073,6 +1090,29 @@ struct sti_struct * sti_get_rom(unsigned int index)
}
EXPORT_SYMBOL(sti_get_rom);
int sti_call(const struct sti_struct *sti, unsigned long func,
const void *flags, void *inptr, void *outptr,
struct sti_glob_cfg *glob_cfg)
{
unsigned long _flags = STI_PTR(flags);
unsigned long _inptr = STI_PTR(inptr);
unsigned long _outptr = STI_PTR(outptr);
unsigned long _glob_cfg = STI_PTR(glob_cfg);
int ret;
#ifdef CONFIG_64BIT
/* Check for overflow when using 32bit STI on 64bit kernel. */
if (WARN_ONCE(_flags>>32 || _inptr>>32 || _outptr>>32 || _glob_cfg>>32,
"Out of 32bit-range pointers!"))
return -1;
#endif
ret = pdc_sti_call(func, _flags, _inptr, _outptr, _glob_cfg);
return ret;
}
MODULE_AUTHOR("Philipp Rumpf, Helge Deller, Thomas Bogendoerfer");
MODULE_DESCRIPTION("Core STI driver for HP's NGLE series graphics cards in HP PARISC machines");
MODULE_LICENSE("GPL v2");

View file

@ -18,6 +18,9 @@
#define STI_FONT_HPROMAN8 1
#define STI_FONT_KANA8 2
#define ALT_CODE_TYPE_UNKNOWN 0x00 /* alt code type values */
#define ALT_CODE_TYPE_PA_RISC_64 0x01
/* The latency of the STI functions cannot really be reduced by setting
* this to 0; STI doesn't seem to be designed to allow calling a different
* function (or the same function with different arguments) after a
@ -40,14 +43,6 @@
#define STI_PTR(p) ( virt_to_phys(p) )
#define PTR_STI(p) ( phys_to_virt((unsigned long)p) )
#define STI_CALL(func, flags, inptr, outptr, glob_cfg) \
({ \
pdc_sti_call( func, STI_PTR(flags), \
STI_PTR(inptr), \
STI_PTR(outptr), \
STI_PTR(glob_cfg)); \
})
#define sti_onscreen_x(sti) (sti->glob_cfg->onscreen_x)
#define sti_onscreen_y(sti) (sti->glob_cfg->onscreen_y)
@ -56,6 +51,12 @@
#define sti_font_x(sti) (PTR_STI(sti->font)->width)
#define sti_font_y(sti) (PTR_STI(sti->font)->height)
#ifdef CONFIG_64BIT
#define STI_LOWMEM (GFP_KERNEL | GFP_DMA)
#else
#define STI_LOWMEM (GFP_KERNEL)
#endif
/* STI function configuration structs */
@ -306,6 +307,34 @@ struct sti_blkmv_outptr {
};
/* sti_all_data is an internal struct which needs to be allocated in
* low memory (< 4GB) if STI is used with 32bit STI on a 64bit kernel */
struct sti_all_data {
struct sti_glob_cfg glob_cfg;
struct sti_glob_cfg_ext glob_cfg_ext;
struct sti_conf_inptr inq_inptr;
struct sti_conf_outptr inq_outptr; /* configuration */
struct sti_conf_outptr_ext inq_outptr_ext;
struct sti_init_inptr_ext init_inptr_ext;
struct sti_init_inptr init_inptr;
struct sti_init_outptr init_outptr;
struct sti_blkmv_inptr blkmv_inptr;
struct sti_blkmv_outptr blkmv_outptr;
struct sti_font_inptr font_inptr;
struct sti_font_outptr font_outptr;
/* leave as last entries */
unsigned long save_addr[1024 / sizeof(unsigned long)];
/* min 256 bytes which is STI default, max sti->sti_mem_request */
unsigned long sti_mem_addr[256 / sizeof(unsigned long)];
/* do not add something below here ! */
};
/* internal generic STI struct */
struct sti_struct {
@ -330,11 +359,9 @@ struct sti_struct {
region_t regions[STI_REGION_MAX];
unsigned long regions_phys[STI_REGION_MAX];
struct sti_glob_cfg *glob_cfg;
struct sti_cooked_font *font; /* ptr to selected font (cooked) */
struct sti_glob_cfg *glob_cfg; /* points into sti_all_data */
struct sti_conf_outptr outptr; /* configuration */
struct sti_conf_outptr_ext outptr_ext;
struct sti_cooked_font *font; /* ptr to selected font (cooked) */
struct pci_dev *pd;
@ -343,6 +370,9 @@ struct sti_struct {
/* pointer to the fb_info where this STI device is used */
struct fb_info *info;
/* pointer to all internal data */
struct sti_all_data *sti_data;
};
@ -350,6 +380,14 @@ struct sti_struct {
struct sti_struct *sti_get_rom(unsigned int index); /* 0: default sti */
/* sticore main function to call STI firmware */
int sti_call(const struct sti_struct *sti, unsigned long func,
const void *flags, void *inptr, void *outptr,
struct sti_glob_cfg *glob_cfg);
/* functions to call the STI ROM directly */
void sti_putc(struct sti_struct *sti, int c, int y, int x);

View file

@ -1101,6 +1101,7 @@ static int __init stifb_init_fb(struct sti_struct *sti, int bpp_pref)
var = &info->var;
fb->sti = sti;
dev_name = sti->sti_data->inq_outptr.dev_name;
/* store upper 32bits of the graphics id */
fb->id = fb->sti->graphics_id[0];
@ -1114,11 +1115,11 @@ static int __init stifb_init_fb(struct sti_struct *sti, int bpp_pref)
Since this driver only supports standard mode, we check
if the device name contains the string "DX" and tell the
user how to reconfigure the card. */
if (strstr(sti->outptr.dev_name, "DX")) {
if (strstr(dev_name, "DX")) {
printk(KERN_WARNING
"WARNING: stifb framebuffer driver does not support '%s' in double-buffer mode.\n"
"WARNING: Please disable the double-buffer mode in IPL menu (the PARISC-BIOS).\n",
sti->outptr.dev_name);
dev_name);
goto out_err0;
}
/* fall though */
@ -1130,7 +1131,7 @@ static int __init stifb_init_fb(struct sti_struct *sti, int bpp_pref)
break;
default:
printk(KERN_WARNING "stifb: '%s' (id: 0x%08x) not supported.\n",
sti->outptr.dev_name, fb->id);
dev_name, fb->id);
goto out_err0;
}
@ -1154,7 +1155,6 @@ static int __init stifb_init_fb(struct sti_struct *sti, int bpp_pref)
fb->id = S9000_ID_A1659A;
break;
case S9000_ID_TIMBER: /* HP9000/710 Any (may be a grayscale device) */
dev_name = fb->sti->outptr.dev_name;
if (strstr(dev_name, "GRAYSCALE") ||
strstr(dev_name, "Grayscale") ||
strstr(dev_name, "grayscale"))
@ -1290,7 +1290,7 @@ static int __init stifb_init_fb(struct sti_struct *sti, int bpp_pref)
var->xres,
var->yres,
var->bits_per_pixel,
sti->outptr.dev_name,
dev_name,
fb->id,
fix->mmio_start);