Another Cyrix patch.

Byron Faber (byron@morticia.PHYSICS.ColoState.EDU)
Wed, 15 Jan 1997 15:06:09 -0700


Here's an updated Cyrix patch that will apply to 2.1.20 or 21.

It fixes the VSPM option for odd memory sizes (i.e. not powers of 2).

It should get more people working with VSPM.

Byron

--------------------------------------------------------------
diff -u --recursive --new-file linux-2.1.20/Documentation/Configure.help mylinux-2.1.20/Documentation/Configure.help
--- linux-2.1.20/Documentation/Configure.help Thu Jan 2 18:36:00 1997
+++ mylinux-2.1.20/Documentation/Configure.help Thu Jan 9 19:14:50 1997
@@ -4136,6 +4136,48 @@
Documentation/isdn/README and Documentation/isdn/README.pcbit for
more information.

+Support for Cyrix processors
+CONFIG_CYRIX
+ This enables recognition of Cyrix processors. Without it
+ /proc/cpuinfo will list your processor as an unknown model
+ of Cyrix. With it it will list the correct details. It should
+ be safe to say Y here regardless of what processor you are
+ actually using.
+
+5x86 performance features
+CONFIG_CYRIX_5X86
+ The Cyrix 5x86 has several performance feature which are enabled
+ using on-chip registers. Most are normally enabled by the BIOS
+ however this code ensures that all the useful ones are set to
+ suit Linux. This option is normally selected.
+
+6x86 performance features
+CONFIG_CYRIX_6X86
+ The Cyrix 6x86 has several performance feature which are enabled
+ using on-chip registers. Most are normally enabled by the BIOS
+ however this code ensures that all the useful ones are set to
+ suit Linux. This option is normally selected.
+
+6x86 variable sized paging mechanism (VSPM)
+CONFIG_CYRIX_6X86_VSPM
+ Variable sized paging mechanism (VSPM) is a feature of the Cyrix
+ 6x86 family of processors that allows large regions of memory
+ to be mapped in one go. This significantly reduces the amount
+ of work the MMU has to do compared with traditional paging hence
+ this option is normally on. However VSPM appears to have some
+ problems. Work arounds are in place for step 1 rev 5 and 6 chips.
+ If you have a 6x86 and your kernel will not boot try disabling
+ this option.
+
+6x86 disable L1 cache on step 1 rev 6 or less
+CONFIG_CYRIX_6X86_L1_BROKEN
+ Microsoft reports finding a bug in the L1 cache of the 6x86 during
+ development of NT4.0. Cyrix is reported as saying that this bug
+ is fixed in step 1 revision 7 and later chips. I am unable to
+ obtain any information as to what the bug actually is. Nor am
+ I able to detect any anomalies running Linux on a 1 rev 6 6x86.
+ If you do have problems you can try enabling this option.
+
Support for AP1000 multicomputer
CONFIG_AP1000
This enables support for a sparc based parallel multi-computer
@@ -4645,7 +4687,7 @@
# LocalWords: mgetty sendfax gert greenie muc lowlevel Lasermate LanManager io
# LocalWords: OOPSes trackball binghamton mobileip ncr IOMAPPED settags ns ser
# LocalWords: setsync NEGO MPARITY autotuning prefetch PIIX cdwrite utils rc
-# LocalWords: PCWATCHDOG berkprod bitgate boldt ucsb jf kyoto jp euc Tetsuyasu
+# LocalWords: PCWATCHDOG berkprod bitgate Cyrix
# LocalWords: YAMADA tetsu cauchy nslab ntt nevod perm su doc kaf kheops wsc
# LocalWords: traduc Bourgin dbourgin helptext menuconfig kfill READMEs HOWTOs
# LocalWords: IDEDISK IDEFLOPPY EIDE firewalls QMAGIC ZMAGIC LocalWords opti
diff -u --recursive --new-file linux-2.1.20/arch/i386/config.in mylinux-2.1.20/arch/i386/config.in
--- linux-2.1.20/arch/i386/config.in Fri Dec 13 09:24:07 1996
+++ mylinux-2.1.20/arch/i386/config.in Thu Jan 9 19:14:50 1997
@@ -46,6 +46,14 @@
bool 'Video mode selection support' CONFIG_VIDEO_SELECT
endmenu

+bool 'Cyrix processor recognition' CONFIG_CYRIX
+if [ "$CONFIG_CYRIX" = "y" ]; then
+ bool '5x86 performance features' CONFIG_CYRIX_5X86
+ bool '6x86 performance features' CONFIG_CYRIX_6X86
+ bool '6x86 variable sized paging mechanism (VSPM)' CONFIG_CYRIX_6X86_VSPM
+ bool '6x86 disable L1 cache on step 1 rev 6 or less' CONFIG_CYRIX_6X86_L1_BROKEN
+fi
+
source drivers/block/Config.in

if [ "$CONFIG_NET" = "y" ]; then
diff -u --recursive --new-file linux-2.1.20/arch/i386/defconfig mylinux-2.1.20/arch/i386/defconfig
--- linux-2.1.20/arch/i386/defconfig Thu Jan 2 18:36:00 1997
+++ mylinux-2.1.20/arch/i386/defconfig Thu Jan 9 19:16:09 1997
@@ -29,6 +29,12 @@
# CONFIG_M486 is not set
CONFIG_M586=y
# CONFIG_M686 is not set
+CONFIG_CYRIX=y
+CONFIG_CYRIX_5X86=y
+CONFIG_CYRIX_6X86=y
+CONFIG_CYRIX_6X86_VSPM=y
+CONFIG_CYRIX_6X86_L1_BROKEN=n
+
# CONFIG_VIDEO_SELECT is not set

#
diff -u --recursive --new-file linux-2.1.20/arch/i386/kernel/head.S mylinux-2.1.20/arch/i386/kernel/head.S
--- linux-2.1.20/arch/i386/kernel/head.S Fri Dec 13 09:24:07 1996
+++ mylinux-2.1.20/arch/i386/kernel/head.S Thu Jan 9 19:14:50 1997
@@ -146,7 +146,68 @@
* apply at our cpl of 0 and the stack ought to be aligned already, and
* we don't need to preserve eflags.
*/
- movl $3, SYMBOL_NAME(x86)
+ /*
+ * A Cyrix preserves flags in cases where other CPUs change
+ * them in undefined ways. We need to know this since we may
+ * need to enable the CPUID instruction at least.
+ */
+ xor %ax,%ax
+ sahf
+ movb $5,%ax
+ movb $2,%bx
+ div %bl
+ lahf
+ cmpb $2,%ah
+ jne ncyrix
+
+ /*
+ * It behaves like a Cyrix so put "Cyrix" in the vendor id
+ * field. It may be overwritten later with the real thing
+ * if CPUID works.
+ */
+ movl $0x69727943,SYMBOL_NAME(x86_vendor_id) # low 4 chars
+ movl $0x00000078,SYMBOL_NAME(x86_vendor_id)+4 # next 4 chars
+
+#ifdef CONFIG_CYRIX
+ /*
+ * N.B. The pattern of accesses to 0x22 and 0x23 is *important*
+ * so do not try and "optimise" it! For the same reason we
+ * do all this with interrupts off just to be sure.
+ */
+#define setCx86(reg, val) \
+ movb reg,%ax; \
+ outb %ax,$0x22; \
+ movb val,%ax; \
+ outb %ax,$0x23
+
+#define getCx86(reg) \
+ movb reg,%ax; \
+ outb %ax,$0x22; \
+ inb $0x23,%ax
+
+ cli
+ getCx86($0xc3) # get CCR3
+ movb %ax,%cx # Save old value
+ movb %ax,%bx
+ andb $0x0f,%bx # Enable all config registers (for CCR4 access)
+ orb $0x10,%bx
+ setCx86($0xc3,%bx)
+
+ getCx86($0xc2) # CCR2 |= SUSP_HLT
+ orb $8,%ax
+ movb %ax,%bx
+ setCx86($0xc2,%bx)
+
+ getCx86($0xe8) # CCR4 |= CPUID | DTE_EN
+ orb $0x90,%ax
+ movb %ax,%bx
+ setCx86($0xe8,%bx)
+
+ setCx86($0xc3,%cx) # Restore old CCR3
+ sti
+#endif /* CONFIG_CYRIX */
+
+ncyrix: movl $3, SYMBOL_NAME(x86)
pushfl # push EFLAGS
popl %eax # get EFLAGS
movl %eax,%ecx # save original EFLAGS
@@ -158,6 +219,7 @@
xorl %ecx,%eax # change in flags
andl $0x40000,%eax # check if AC bit changed
je is386
+
movl $4,SYMBOL_NAME(x86)
movl %ecx,%eax
xorl $0x200000,%eax # check ID flag
@@ -166,11 +228,23 @@
pushfl # 487SX we can't change it
popl %eax
xorl %ecx,%eax
- andl $0x200000,%eax
- je is486
-isnew: pushl %ecx # restore original EFLAGS
+ pushl %ecx # restore original EFLAGS
popfl
+ andl $0x200000,%eax
+ je nocpuid
+
incl SYMBOL_NAME(have_cpuid) # we have CPUID
+
+ /* get vendor info */
+ xorl %eax, %eax # call CPUID with 0 -> return vendor ID
+ .byte 0x0f, 0xa2 # CPUID
+ movl %ebx,SYMBOL_NAME(x86_vendor_id) # lo 4 chars
+ movl %edx,SYMBOL_NAME(x86_vendor_id)+4 # next 4 chars
+ movl %ecx,SYMBOL_NAME(x86_vendor_id)+8 # last 4 chars
+
+ cmpl $0,%eax # do we have processor info as well?
+ je nocpuid
+
/* get processor type */
# LINUS WE HAVE A BUG HERE - MUST CHECK WITH
# CPUID#0 THAT CPUID#1 IS SUPPORTED...
@@ -185,23 +259,122 @@
andb $0x0f, %cl # mask mask revision
movb %cl,SYMBOL_NAME(x86_mask)
movl %edx,SYMBOL_NAME(x86_capability)
- /* get vendor info */
- xorl %eax, %eax # call CPUID with 0 -> return vendor ID
- .byte 0x0f, 0xa2 # CPUID
- movl %ebx,SYMBOL_NAME(x86_vendor_id) # lo 4 chars
- movl %edx,SYMBOL_NAME(x86_vendor_id)+4 # next 4 chars
- movl %ecx,SYMBOL_NAME(x86_vendor_id)+8 # last 4 chars

- movl %cr0,%eax # 486+
- andl $0x80000011,%eax # Save PG,PE,ET
- orl $0x50022,%eax # set AM, WP, NE and MP
- jmp 2f
-is486: pushl %ecx # restore original EFLAGS
- popfl
- movl %cr0,%eax # 486
+nocpuid:
+ /*
+ * Even if we had CPUID Cyrix tries to look compatible with
+ * Intel so we have to go elsewhere for the nitty gritty.
+ */
+ cmpl $0x69727943,SYMBOL_NAME(x86_vendor_id) # "Cyri[x.*]"?
+ jne chkdevid # maybe not...
+
+ orb $0x10,SYMBOL_NAME(x86) # Flag as Cyrix
+ movb $0xfe,SYMBOL_NAME(x86_model) # Generic Cx486?
+ movb $0,SYMBOL_NAME(x86_mask)
+
+chkdevid:
+#ifdef CONFIG_CYRIX
+ cli # Test for DEVID
+ getCx86($0xc3) # by writing CCR3
+ movb %ax,%cx
+ movb %ax,%bx
+ orb $0x80,%bx
+ setCx86($0xc3,%bx)
+ getCx86($0xc0) # dummy to change bus
+ getCx86($0xc3)
+ sti
+ cmp %ax,%cx
+ je is486 # not writable == no DEVID
+
+ cli
+ setCx86($0xc3,%cx) # restore CCR3
+
+ getCx86($0xff) # get DEVID in preference to any CPUID
+ movb %al,SYMBOL_NAME(x86_mask)
+ getCx86($0xfe)
+ movb %al,SYMBOL_NAME(x86_model)
+ sti
+
+#if defined(CONFIG_CYRIX_5X86) || defined(CONFIG_CYRIX_6X86)
+ andb $0xf0,%al
+# ifdef CONFIG_CYRIX_6X86
+ cmpb $0x30,%al
+ je is6x86
+# endif
+# ifdef CONFIG_CYRIX_5X86
+ cmpb $0x20,%al
+ je is5x86
+# endif
+ jmp is486
+#endif
+
+#ifdef CONFIG_CYRIX_5X86
+ /* Special set up for 5x86 */
+is5x86:
+ cli
+ movb %cx,%bx
+ andb $0x0f,%bx # Enable all config registers
+ orb $0x10,%bx
+ setCx86($0xc3,%bx)
+
+ getCx86($0x20) # PCR0 |= RSTK_EN | BTB_EN | LOOP_EN
+ orb $0x07,%ax # (LSSER could break memory mapped
+ movb %ax,%bx # devices - we have no way of knowing)
+ setCx86($0x20,%bx)
+
+ setCx86($0xc3,%cx) # restore CCR3
+ sti
+ jmp is486
+#endif
+
+#ifdef CONFIG_CYRIX_6X86
+ /* Special set up for 6x86 */
+is6x86:
+ cli
+ movb %cx,%bx
+ andb $0x0f,%bx # Enable all config registers
+ orb $0x10,%bx
+ setCx86($0xc3,%bx)
+
+ getCx86($0xe9) # CCR5 = (CCR5 & ~SLOP) | WT_ALLOC
+ andb $0xfd,%ax
+ orb $0x01,%ax
+ movb %ax,%bx
+ setCx86($0xe9,%bx)
+
+ getCx86($0x30) # Enable test register opcodes
+ movb %ax,%dx
+ orb $0x40,%ax
+ movb %ax,%bx
+ setCx86($0x30,%bx)
+
+ mov $0x28,%ebx # Enable FAR COFs in BTB
+ .byte 0x0f,0x26,0xcb # mov %ebx,%tr1
+ .byte 0x0f,0x24,0xd0 # mov %tr2,%eax
+ and $0xfffffffd,%eax
+ .byte 0x0f,0x26,0xd0 # mov %eax,%tr2
+
+ setCx86($0x30,%dx) # Disable test register opcodes
+
+ setCx86($0xc3,%cx) # restore CCR3
+ sti
+#endif /* CONFIG_CYRIX_6X86 */
+#endif /* CONFIG_CYRIX */
+
+is486: movl %cr0,%eax # 486 or better
andl $0x80000011,%eax # Save PG,PE,ET
orl $0x50022,%eax # set AM, WP, NE and MP
+#ifdef CONFIG_CYRIX_6X86_L1_BROKEN
+ cmpb $0x16,SYMBOL_NAME(x86) # Cyrix 6x86
+ jne 2f
+ cmpb $0x17,SYMBOL_NAME(x86_model) # Step 1 rev 7 or greater
+ jge 2f
+ orl $0x60000000,%eax # set CD and NW
+ movl %eax,%cr0
+ wbinvd
+#endif
jmp 2f
+
is386: pushl %ecx # restore original EFLAGS
popfl
movl %cr0,%eax # 386
diff -u --recursive --new-file linux-2.1.20/arch/i386/kernel/setup.c mylinux-2.1.20/arch/i386/kernel/setup.c
--- linux-2.1.20/arch/i386/kernel/setup.c Fri Dec 13 09:24:07 1996
+++ mylinux-2.1.20/arch/i386/kernel/setup.c Thu Jan 9 19:39:29 1997
@@ -252,11 +252,54 @@
return NULL;
}

+static const char * cyrixmodel(unsigned int nr)
+{
+ static char nbuf[32];
+
+ if (nr < 0x20 || nr == 0xfe) {
+ /* An abridged list. The values for the Cx486 series do
+ * not seem to follow much of a pattern.
+ */
+ switch (nr) {
+ case 0x00:
+ return "Cx486 SLC";
+ case 0x01:
+ return "Cx486 DLC";
+ case 0x02:
+ return "Cx486 SLC2";
+ case 0x03:
+ return "Cx486 DLC2";
+ case 0x1a:
+ return "Cx486DX";
+ case 0x1b:
+ return "Cx486DX2";
+ case 0x1f:
+ return "Cx486DX4";
+ case 0xfe:
+ return ("Unknown");
+ default:
+ return "Cx486";
+ }
+ } else if ((nr >= 0x28 && nr <= 0x2f)
+ || (nr >= 0x30 && nr <= 0x36)) {
+ sprintf(nbuf, "%cx86 %cx Core/Bus Clock",
+ nr >= 0x30 ? '6' : '5',
+ "12??43"[nr & 0x05]);
+ return nbuf;
+ }
+ return NULL;
+}
+
static const char * getmodel(int x86, int model)
{
const char *p = NULL;
static char nbuf[12];
- switch (x86) {
+ if ((x86 & 0xf0) == 0x10) {
+ p = cyrixmodel(model);
+ } else switch (x86) {
+ case 0:
+ p = "unknown";
+ break;
case 4:
p = i486model(model);
break;
@@ -306,20 +349,24 @@
"model\t\t: %s\n"
"vendor_id\t: %s\n",
CPUN,
- CD(x86)+'0',
- CD(have_cpuid) ?
- getmodel(CD(x86), CD(x86_model)) :
- "unknown",
+ (CD(x86) & 0x0f)+'0',
+ getmodel(CD(x86), CD(x86_model)),
CD(x86_vendor_id));

- if (CD(x86_mask))
- len += sprintf(buffer+len,
- "stepping\t: %d\n",
- CD(x86_mask));
- else
+ if (CD(x86_mask)) {
+ if ((CD(x86) & 0xf0) == 0x10)
+ len += sprintf(buffer+len,
+ "stepping\t: %d rev %d\n",
+ CD(x86_mask) >> 4,
+ CD(x86_mask) & 0x0f);
+ else
+ len += sprintf(buffer+len,
+ "stepping\t: %d\n",
+ CD(x86_mask));
+ } else
len += sprintf(buffer+len,
"stepping\t: unknown\n");
-
+
len += sprintf(buffer+len,
"fdiv_bug\t: %s\n"
"hlt_bug\t\t: %s\n"
diff -u --recursive --new-file linux-2.1.20/arch/i386/mm/init.c mylinux-2.1.20/arch/i386/mm/init.c
--- linux-2.1.20/arch/i386/mm/init.c Mon Dec 30 13:49:08 1996
+++ mylinux-2.1.20/arch/i386/mm/init.c Thu Jan 9 19:19:48 1997
@@ -110,6 +110,82 @@
unsigned long tmp;
unsigned long address;

+#if defined(CONFIG_CYRIX) && defined(CONFIG_CYRIX_6X86_VSPM) && !defined(__SMP__)
+ unsigned long vspm_max = __va(0);
+
+ /* If this is a Cyrix 6x86 we use the variable size paging
+ * mechanism (VSPM) to map physical memory at 0xc0000000.
+ * Note that VSPM pages are stored on the CPU only so this
+ * needs to be done for each processor in a multi-processor
+ * system. If we have a mixture of processors we would also
+ * need to set up the traditional page tables for them.
+ * Note also that VSPM pages will be global to all memory
+ * spaces since they are not stored in the normal page
+ * directories.
+ *
+ * By experiment:
+ * VSPM pages must be power of two sizes. A single 24MB
+ * page fails.
+ * Documentation suggests there are 8 VSPM slots (3 bit
+ * index) but tests show the upper four slots mirror the
+ * lower four.
+ * With a 16MB page followed by an 8MB page I always get
+ * a write fault on the last 4k of the 8MB page. With 8MB
+ * plus 4MB I can't even boot. If we have such a memory
+ * size we map the first power of two with VSPM and use
+ * traditional paging for the rest.
+ * VSPM pages override traditional pages so we cannot
+ * overlap the start of the vmalloc region.
+ * Do not try and create a mapping with dirty and accessed
+ * flags clear - a step 1 rev 5 chip will crash and burn.
+ */
+ if ((x86 & 0xf0) == 0x10 || (x86_model & 0xf0) == 0x30) {
+ int vspm_index = 0;
+
+ do {
+ unsigned long mem_size;
+
+ mem_size = 4096;
+ while (vspm_max+mem_size < end_mem)
+ mem_size <<= 1;
+ if (vspm_max+mem_size > end_mem)
+ if ((mem_size >>= 1) < 4096)
+ break;
+
+ asm( "movl %0,%%eax\n"
+ "movl %%eax,%%tr7\n"
+ "movl $0x00000004,%%eax\n"
+ "movl %%eax,%%tr6\n"
+ "movl %1,%%eax\n"
+ "movl %%eax,%%tr7\n"
+ "movl %2,%%eax\n"
+ "movl %%eax,%%tr6\n"
+ : /* no outputs */
+ : "g" ((((mem_size-1) & 0xfffff000)) | (vspm_index<<7)),
+ "g" ((__pa(vspm_max) & 0xfffff000) | (vspm_index<<7)),
+ "g" ((vspm_max & 0xfffff000) | 0x00000cd6)
+ : "eax", "cc"
+ );
+
+ vspm_max += mem_size;
+
+#if 1
+ /* Just use one VSPM page for now, anything
+ * over will be traditionally paged.
+ */
+ break;
+#endif
+ } while (vspm_max < end_mem && vspm_index++ < 4);
+
+ /* Write protect does work correctly but the test
+ * will fail because we can't map just page 0 read
+ * only from under the VSPM big page. If we didn't
+ * know before we do now.
+ */
+ wp_works_ok = 1;
+ }
+#endif
+
/*
* Physical page 0 is special; it's not touched by Linux since BIOS
* and SMM (for laptops with [34]86/SL chips) may need it. It is read
@@ -176,6 +252,16 @@
address += 4*1024*1024;
continue;
}
+
+#if defined(CONFIG_CYRIX) && defined(CONFIG_CYRIX_6X86_VSPM) && !defined(__SMP__)
+ if ((x86 & 0xf0) == 0x10 /* Cyrix */
+ && (x86_model & 0xf0) == 0x30 /* 6x86 */
+ && (address + PAGE_SIZE*PTRS_PER_PTE) < vspm_max) { /* within VSPM mappings */
+ address += PAGE_SIZE * PTRS_PER_PTE;
+ pg_dir++
+ continue;
+ }
+#endif
+
/* map the memory at virtual addr 0xC0000000 */
/* pg_table is physical at this point */
pg_table = (pte_t *) (PAGE_MASK & pgd_val(pg_dir[768]));
@@ -186,12 +272,18 @@

pgd_val(pg_dir[768]) = _PAGE_TABLE | (unsigned long) pg_table;
pg_dir++;
+
/* now change pg_table to kernel virtual addresses */
pg_table = (pte_t *) __va(pg_table);
for (tmp = 0 ; tmp < PTRS_PER_PTE ; tmp++,pg_table++) {
pte_t pte = mk_pte(address, PAGE_KERNEL);
+#if defined(CONFIG_CYRIX) && defined(CONFIG_CYRIX_6X86_VSPM) && !defined(__SMP__)
+ if (address < vspm_max || address >= end_mem)
+ pte_val(pte) = 0;
+#else
if (address >= end_mem)
pte_val(pte) = 0;
+#endif
set_pte(pg_table, pte);
address += PAGE_SIZE;
}
diff -u --recursive --new-file linux-2.1.20/include/asm-i386/bugs.h mylinux-2.1.20/include/asm-i386/bugs.h
--- linux-2.1.20/include/asm-i386/bugs.h Tue Oct 15 21:01:32 1996
+++ mylinux-2.1.20/include/asm-i386/bugs.h Thu Jan 9 19:25:54 1997
@@ -130,5 +130,5 @@
check_tlb();
check_fpu();
check_hlt();
- system_utsname.machine[1] = '0' + x86;
+ system_utsname.machine[1] = '0' + (x86 & 0x0f);
}

-- 
#!/usr/local/bin/perl -- -pwcracker-in-3-lines-of-perl-plus-disclaimer
$u{$p[1]}=$p[0] while(@p=getpwent);while(<>){chop;foreach $c (keys %u)
{printf "u=%s p=%s\n",$u{$c},$_ if(crypt($_,$c) eq $c);}} # Use: pwc dict ...