This is for testing/developing purpose, not for merging. SerialICE in coreboot would be great for developing at least CPUs cache init code. This strategy makes use of early serial functions directly from coreboot tree. This is with example for one board (copy from romstage early serial code for your board).
Comments are welcome!
Patches (2): SerialICE files, receive function for romcc_console, bootblock_simple example SerialICE patch
src/arch/x86/SerialICE/io.h | 195 ++++++++++++++++++++++++ src/arch/x86/SerialICE/serial.c | 190 ++++++++++++++++++++++++ src/arch/x86/SerialICE/serialice.c | 272 ++++++++++++++++++++++++++++++++++ src/arch/x86/init/bootblock_simple.c | 2 + src/arch/x86/lib/romcc_console.c | 7 + 5 files changed, 666 insertions(+), 0 deletions(-) create mode 100644 src/arch/x86/SerialICE/io.h create mode 100644 src/arch/x86/SerialICE/serial.c create mode 100644 src/arch/x86/SerialICE/serialice.c
Copy required SerialICE files (not patched yet), add receive byte function to romcc_console, add SerialICE to bootblock_simple
SerialICE revision: 107
Signed-off-by: Tadas Slotkus devtadas@gmail.com --- src/arch/x86/SerialICE/io.h | 195 ++++++++++++++++++++++++++ src/arch/x86/SerialICE/serial.c | 187 ++++++++++++++++++++++++ src/arch/x86/SerialICE/serialice.c | 257 ++++++++++++++++++++++++++++++++++ src/arch/x86/init/bootblock_simple.c | 2 + src/arch/x86/lib/romcc_console.c | 7 + 5 files changed, 648 insertions(+), 0 deletions(-) create mode 100644 src/arch/x86/SerialICE/io.h create mode 100644 src/arch/x86/SerialICE/serial.c create mode 100644 src/arch/x86/SerialICE/serialice.c
diff --git a/src/arch/x86/SerialICE/io.h b/src/arch/x86/SerialICE/io.h new file mode 100644 index 0000000..a47feef --- /dev/null +++ b/src/arch/x86/SerialICE/io.h @@ -0,0 +1,195 @@ +/* + * SerialICE + * + * Copyright (C) 2009 coresystems GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef IO_H +#define IO_H + +/* Memory functions */ + +static inline u8 read8(unsigned long addr) +{ + return *((volatile u8 *)(addr)); +} + +static inline u16 read16(unsigned long addr) +{ + return *((volatile u16 *)(addr)); +} + +static inline u32 read32(unsigned long addr) +{ + return *((volatile u32 *)(addr)); +} + +static inline void write8(unsigned long addr, u8 value) +{ + *((volatile u8 *)(addr)) = value; +} + +static inline void write16(unsigned long addr, u16 value) +{ + *((volatile u16 *)(addr)) = value; +} + +static inline void write32(unsigned long addr, u32 value) +{ + *((volatile u32 *)(addr)) = value; +} + +/* IO functions */ + +#if defined( __ROMCC__ ) && !defined (__GNUC__) +static inline void outb(u8 value, u16 port) +{ + __builtin_outb(value, port); +} + +static inline void outw(u16 value, u16 port) +{ + __builtin_outw(value, port); +} + +static inline void outl(u32 value, u16 port) +{ + __builtin_outl(value, port); +} + +static inline u8 inb(u16 port) +{ + return __builtin_inb(port); +} + +static inline u16 inw(u16 port) +{ + return __builtin_inw(port); +} + +static inline u32 inl(u16 port) +{ + return __builtin_inl(port); +} +#else +static inline void outb(u8 value, u16 port) +{ + __asm__ __volatile__("outb %b0, %w1"::"a"(value), "Nd"(port)); +} + +static inline void outw(u16 value, u16 port) +{ + __asm__ __volatile__("outw %w0, %w1"::"a"(value), "Nd"(port)); +} + +static inline void outl(u32 value, u16 port) +{ + __asm__ __volatile__("outl %0, %w1"::"a"(value), "Nd"(port)); +} + +static inline u8 inb(u16 port) +{ + u8 value; + __asm__ __volatile__("inb %w1, %b0":"=a"(value): "Nd"(port)); + return value; +} + +static inline u16 inw(u16 port) +{ + u16 value; + __asm__ __volatile__("inw %w1, %w0":"=a"(value): "Nd"(port)); + return value; +} + +static inline u32 inl(u16 port) +{ + u32 value; + __asm__ __volatile__("inl %w1, %0":"=a"(value): "Nd"(port)); + return value; +} +#endif /* __ROMCC__ && !__GNUC__ */ + +/* MSR functions */ + +typedef struct { u32 lo, hi; } msr_t; + +static inline msr_t rdmsr(u32 index, u32 key) +{ + msr_t result; + __asm__ __volatile__ ( + "rdmsr" + : "=a" (result.lo), "=d" (result.hi) + : "c" (index), "D" (key) + ); + return result; +} + +static inline void wrmsr(u32 index, msr_t msr, u32 key) +{ + __asm__ __volatile__ ( + "wrmsr" + : /* No outputs */ + : "c" (index), "a" (msr.lo), "d" (msr.hi), "D" (key) + ); +} + +/* CPUID functions */ + +static inline u32 cpuid_eax(u32 op, u32 op2) +{ + u32 eax; + + __asm__("cpuid" + : "=a" (eax) + : "a" (op), "c" (op2) + : "ebx", "edx" ); + return eax; +} + +static inline u32 cpuid_ebx(u32 op, u32 op2) +{ + u32 eax, ebx; + + __asm__("cpuid" + : "=b" (ebx) + : "a" (op), "c" (op2) + : "edx"); + return ebx; +} + +static inline u32 cpuid_ecx(u32 op, u32 op2) +{ + u32 eax, ecx; + + __asm__("cpuid" + : "=c" (ecx) + : "a" (op), "c" (op2) + : "ebx", "edx" ); + return ecx; +} + +static inline u32 cpuid_edx(u32 op, u32 op2) +{ + u32 eax, edx; + + __asm__("cpuid" + : "=d" (edx) + : "a" (op), "c" (op2) + : "ebx"); + return edx; +} + +#endif diff --git a/src/arch/x86/SerialICE/serial.c b/src/arch/x86/SerialICE/serial.c new file mode 100644 index 0000000..9aaecb6 --- /dev/null +++ b/src/arch/x86/SerialICE/serial.c @@ -0,0 +1,187 @@ +/* + * SerialICE + * + * Copyright (C) 2009 coresystems GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* Data */ +#define UART_RBR 0x00 +#define UART_TBR 0x00 + +/* Control */ +#define UART_IER 0x01 +#define UART_IIR 0x02 +#define UART_FCR 0x02 +#define UART_LCR 0x03 +#define UART_MCR 0x04 +#define UART_DLL 0x00 +#define UART_DLM 0x01 + +/* Status */ +#define UART_LSR 0x05 +#define UART_MSR 0x06 +#define UART_SCR 0x07 + +/* SIO functions */ + +static void sio_init(void) +{ + int divisor = 115200 / SIO_SPEED; + int lcs = 3; + outb(0x00, SIO_PORT + UART_IER); + outb(0x01, SIO_PORT + UART_FCR); + outb(0x03, SIO_PORT + UART_MCR); + outb(0x80 | lcs, SIO_PORT + UART_LCR); + outb(divisor & 0xff, SIO_PORT + UART_DLL); + outb((divisor >> 8) & 0xff, SIO_PORT + UART_DLM); + outb(lcs, SIO_PORT + UART_LCR); +} + +static void sio_putc(u8 data) +{ + while (!(inb(SIO_PORT + UART_LSR) & 0x20)) ; + outb(data, SIO_PORT + UART_TBR); + while (!(inb(SIO_PORT + UART_LSR) & 0x40)) ; +} + +static u8 sio_getc(void) +{ + u8 val; + while (!(inb(SIO_PORT + UART_LSR) & 0x01)) ; + + val = inb(SIO_PORT + UART_RBR); + +#if ECHO_MODE + sio_putc(val); +#endif + return val; +} + +/* SIO helpers */ + +static void sio_putstring(char *string) +{ + /* Very simple, no %d, %x etc. */ + while (*string) { + if (*string == '\n') + sio_putc('\r'); + sio_putc(*string); + string++; + } +} + +#define sio_put_nibble(nibble) \ + if (nibble > 9) \ + nibble += ('a' - 10); \ + else \ + nibble += '0'; \ + sio_putc(nibble) + +static void sio_put8(u8 data) +{ + int i; + u8 c; + + c = (data >> 4) & 0xf; + sio_put_nibble(c); + + c = data & 0xf; + sio_put_nibble(c); +} + +static void sio_put16(u16 data) +{ + int i; + for (i=12; i >= 0; i -= 4) { + u8 c = (data >> i) & 0xf; + sio_put_nibble(c); + } +} + +static void sio_put32(u32 data) +{ + int i; + for (i=28; i >= 0; i -= 4) { + u8 c = (data >> i) & 0xf; + sio_put_nibble(c); + } +} + +static u8 sio_get_nibble(void) +{ + u8 ret = 0; + u8 nibble = sio_getc(); + + if (nibble >= '0' && nibble <= '9') { + ret = (nibble - '0'); + } else if (nibble >= 'a' && nibble <= 'f') { + ret = (nibble - 'a') + 0xa; + } else if (nibble >= 'A' && nibble <= 'F') { + ret = (nibble - 'A') + 0xa; + } else { + sio_putstring("ERROR: parsing number\n"); + } + return ret; +} + +static u8 sio_get8(void) +{ + u8 data; + data = sio_get_nibble(); + data = data << 4; + data |= sio_get_nibble(); + return data; +} + +static u16 sio_get16(void) +{ + u16 data; + + data = sio_get_nibble(); + data = data << 4; + data |= sio_get_nibble(); + data = data << 4; + data |= sio_get_nibble(); + data = data << 4; + data |= sio_get_nibble(); + + return data; +} + +static u32 sio_get32(void) +{ + u32 data; + + data = sio_get_nibble(); + data = data << 4; + data |= sio_get_nibble(); + data = data << 4; + data |= sio_get_nibble(); + data = data << 4; + data |= sio_get_nibble(); + data = data << 4; + data |= sio_get_nibble(); + data = data << 4; + data |= sio_get_nibble(); + data = data << 4; + data |= sio_get_nibble(); + data = data << 4; + data |= sio_get_nibble(); + + return data; +} + + diff --git a/src/arch/x86/SerialICE/serialice.c b/src/arch/x86/SerialICE/serialice.c new file mode 100644 index 0000000..35531dc --- /dev/null +++ b/src/arch/x86/SerialICE/serialice.c @@ -0,0 +1,257 @@ +/* + * SerialICE + * + * Copyright (C) 2009 coresystems GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <types.h> +#include <serialice.h> +#include <io.h> + +/* SIO functions */ +#include "serial.c" + +/* Hardware specific functions */ + +#include "chipset.c" + +/* Accessor functions */ + +static void serialice_read_memory(void) +{ + u8 width; + u32 addr; + + // Format: + // *rm00000000.w + addr = sio_get32(); + sio_getc(); // skip . + width = sio_getc(); + + sio_putc('\r'); sio_putc('\n'); + + switch (width) { + case 'b': sio_put8(read8(addr)); break; + case 'w': sio_put16(read16(addr)); break; + case 'l': sio_put32(read32(addr)); break; + } +} + +static void serialice_write_memory(void) +{ + u8 width; + u32 addr; + u32 data; + + // Format: + // *wm00000000.w=0000 + addr = sio_get32(); + sio_getc(); // skip . + width = sio_getc(); + sio_getc(); // skip = + + switch (width) { + case 'b': data = sio_get8(); write8(addr, (u8)data); break; + case 'w': data = sio_get16(); write16(addr, (u16)data); break; + case 'l': data = sio_get32(); write32(addr, (u32)data); break; + } +} + +static void serialice_read_io(void) +{ + u8 width; + u16 port; + + // Format: + // *ri0000.w + port = sio_get16(); + sio_getc(); // skip . + width = sio_getc(); + + sio_putc('\r'); sio_putc('\n'); + + switch (width) { + case 'b': sio_put8(inb(port)); break; + case 'w': sio_put16(inw(port)); break; + case 'l': sio_put32(inl(port)); break; + } +} + +static void serialice_write_io(void) +{ + u8 width; + u16 port; + u32 data; + + // Format: + // *wi0000.w=0000 + port = sio_get16(); + sio_getc(); // skip . + width = sio_getc(); + sio_getc(); // skip = + + switch (width) { + case 'b': data = sio_get8(); outb((u8)data, port); break; + case 'w': data = sio_get16(); outw((u16)data, port); break; + case 'l': data = sio_get32(); outl((u32)data, port); break; + } +} + +static void serialice_read_msr(void) +{ + u32 addr, key; + msr_t msr; + + // Format: + // *rc00000000.9c5a203a + addr = sio_get32(); + sio_getc(); // skip . + key = sio_get32(); // key in %edi + + sio_putc('\r'); sio_putc('\n'); + + msr = rdmsr(addr, key); + sio_put32(msr.hi); + sio_putc('.'); + sio_put32(msr.lo); +} + +static void serialice_write_msr(void) +{ + u32 addr, key; + msr_t msr; + + // Format: + // *wc00000000.9c5a203a=00000000.00000000 + addr = sio_get32(); + sio_getc(); // skip . + key = sio_get32(); // read key in %edi + sio_getc(); // skip = + msr.hi = sio_get32(); + sio_getc(); // skip . + msr.lo = sio_get32(); + +#ifdef __ROMCC__ + /* Cheat to avoid register outage */ + wrmsr(addr, msr, 0x9c5a203a); +#else + wrmsr(addr, msr, key); +#endif +} + +static void serialice_cpuinfo(void) +{ + u32 eax, ecx; + u32 reg32; + + // Format: + // --EAX--- --ECX--- + // *ci00000000.00000000 + eax = sio_get32(); + sio_getc(); // skip . + ecx = sio_get32(); + + sio_putc('\r'); sio_putc('\n'); + + /* This code looks quite crappy but this way we don't + * have to worry about running out of registers if we + * occupy eax, ebx, ecx, edx at the same time + */ + reg32 = cpuid_eax(eax, ecx); + sio_put32(reg32); + sio_putc('.'); + + reg32 = cpuid_ebx(eax, ecx); + sio_put32(reg32); + sio_putc('.'); + + reg32 = cpuid_ecx(eax, ecx); + sio_put32(reg32); + sio_putc('.'); + + reg32 = cpuid_edx(eax, ecx); + sio_put32(reg32); +} + +static void serialice_mainboard(void) +{ + sio_putc('\r'); sio_putc('\n'); + + /* must be defined in mainboard/<boardname>.c */ + sio_putstring(boardname); +} + +static void serialice_version(void) +{ + sio_putstring("\nSerialICE v" VERSION " (" __DATE__ ")\n"); +} + +int main(void) +{ + chipset_init(); + + sio_init(); + + serialice_version(); + + while(1) { + u16 c; + sio_putstring("\n> "); + + c = sio_getc(); + if (c != '*') + continue; + + c = sio_getc() << 8; + c |= sio_getc(); + + switch(c) { + case (('r' << 8)|'m'): // Read Memory *rm + serialice_read_memory(); + break; + case (('w' << 8)|'m'): // Write Memory *wm + serialice_write_memory(); + break; + case (('r' << 8)|'i'): // Read IO *ri + serialice_read_io(); + break; + case (('w' << 8)|'i'): // Write IO *wi + serialice_write_io(); + break; + case (('r' << 8)|'c'): // Read CPU MSR *rc + serialice_read_msr(); + break; + case (('w' << 8)|'c'): // Write CPU MSR *wc + serialice_write_msr(); + break; + case (('c' << 8)|'i'): // Read CPUID *ci + serialice_cpuinfo(); + break; + case (('m' << 8)|'b'): // Read mainboard type *mb + serialice_mainboard(); + break; + case (('v' << 8)|'i'): // Read version info *vi + serialice_version(); + break; + default: + sio_putstring("ERROR\n"); + break; + } + } + + // Never get here: + return 0; +} diff --git a/src/arch/x86/init/bootblock_simple.c b/src/arch/x86/init/bootblock_simple.c index 5d7c611..8386868 100644 --- a/src/arch/x86/init/bootblock_simple.c +++ b/src/arch/x86/init/bootblock_simple.c @@ -1,4 +1,5 @@ #include <bootblock_common.h> +#include "../SerialICE/serialice.c"
static void main(unsigned long bist) { @@ -12,6 +13,7 @@ static void main(unsigned long bist) sanitize_cmos(); #endif
+ serialice_main(); const char* target1 = "fallback/romstage"; unsigned long entry; entry = findstage(target1); diff --git a/src/arch/x86/lib/romcc_console.c b/src/arch/x86/lib/romcc_console.c index 13ee1f0..3747136 100644 --- a/src/arch/x86/lib/romcc_console.c +++ b/src/arch/x86/lib/romcc_console.c @@ -48,6 +48,13 @@ static void __console_tx_byte(unsigned char byte) #endif }
+static unsigned char __console_rx_byte(void) +{ +#if CONFIG_CONSOLE_SERIAL8250 + return uart8250_rx_byte(CONFIG_TTYS0_BASE); +#endif +} + static void __console_tx_nibble(unsigned nibble) { unsigned char digit;
Add serial init function example to use coreboot early serial funcions Add selection which cpuid or msr functions to use (coreboot's or serialice's) Addapt for coreboot
Signed-off-by: Tadas Slotkus devtadas@gmail.com --- src/arch/x86/SerialICE/io.h | 8 +++--- src/arch/x86/SerialICE/serial.c | 23 ++++++++++++--------- src/arch/x86/SerialICE/serialice.c | 39 ++++++++++++++++++++++++----------- 3 files changed, 44 insertions(+), 26 deletions(-)
diff --git a/src/arch/x86/SerialICE/io.h b/src/arch/x86/SerialICE/io.h index a47feef..efc0fe6 100644 --- a/src/arch/x86/SerialICE/io.h +++ b/src/arch/x86/SerialICE/io.h @@ -21,7 +21,7 @@ #define IO_H
/* Memory functions */ - +#if 0 static inline u8 read8(unsigned long addr) { return *((volatile u8 *)(addr)); @@ -125,8 +125,8 @@ static inline u32 inl(u16 port) /* MSR functions */
typedef struct { u32 lo, hi; } msr_t; - -static inline msr_t rdmsr(u32 index, u32 key) +#endif +static inline msr_t _rdmsr(u32 index, u32 key) { msr_t result; __asm__ __volatile__ ( @@ -137,7 +137,7 @@ static inline msr_t rdmsr(u32 index, u32 key) return result; }
-static inline void wrmsr(u32 index, msr_t msr, u32 key) +static inline void _wrmsr(u32 index, msr_t msr, u32 key) { __asm__ __volatile__ ( "wrmsr" diff --git a/src/arch/x86/SerialICE/serial.c b/src/arch/x86/SerialICE/serial.c index 9aaecb6..1181aff 100644 --- a/src/arch/x86/SerialICE/serial.c +++ b/src/arch/x86/SerialICE/serial.c @@ -17,6 +17,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#if 0 /* Data */ #define UART_RBR 0x00 #define UART_TBR 0x00 @@ -34,22 +35,23 @@ #define UART_LSR 0x05 #define UART_MSR 0x06 #define UART_SCR 0x07 - +#endif /* SIO functions */
+/* Early serial includes and defines */ +#include <device/pnp_def.h> +#include <superio/winbond/w83977tf/early_serial.c> +#define SERIAL_DEV PNP_DEV(0x3f0, W83977TF_SP1) +#include <console/console.h> + +#define sio_putc __console_tx_byte +#define sio_getc __console_rx_byte static void sio_init(void) { - int divisor = 115200 / SIO_SPEED; - int lcs = 3; - outb(0x00, SIO_PORT + UART_IER); - outb(0x01, SIO_PORT + UART_FCR); - outb(0x03, SIO_PORT + UART_MCR); - outb(0x80 | lcs, SIO_PORT + UART_LCR); - outb(divisor & 0xff, SIO_PORT + UART_DLL); - outb((divisor >> 8) & 0xff, SIO_PORT + UART_DLM); - outb(lcs, SIO_PORT + UART_LCR); + w83977tf_enable_serial(SERIAL_DEV, CONFIG_TTYS0_BASE); }
+#if 0 static void sio_putc(u8 data) { while (!(inb(SIO_PORT + UART_LSR) & 0x20)) ; @@ -69,6 +71,7 @@ static u8 sio_getc(void) #endif return val; } +#endif
/* SIO helpers */
diff --git a/src/arch/x86/SerialICE/serialice.c b/src/arch/x86/SerialICE/serialice.c index 35531dc..a503cb9 100644 --- a/src/arch/x86/SerialICE/serialice.c +++ b/src/arch/x86/SerialICE/serialice.c @@ -18,15 +18,27 @@ */
#include <types.h> -#include <serialice.h> -#include <io.h> + +//#define use_coreboot_stuff 1 +#ifdef use_coreboot_stuff +#include <arch/cpu.h> +#include <cpu/x86/msr.h> +#define _rdmsr(addr, key) rdmsr(addr) +#define _wrmsr(addr, msr, key) wrmsr(addr, msr) +#define cpuid_eax(eax, ecx) cpuid_eax(eax) +#define cpuid_ebx(eax, ecx) cpuid_ebx(eax) +#define cpuid_ecx(eax, ecx) cpuid_ecx(eax) +#define cpuid_edx(eax, ecx) cpuid_edx(eax) +#else +#include "io.h" +#endif
/* SIO functions */ #include "serial.c"
/* Hardware specific functions */
-#include "chipset.c" +//#include "chipset.c"
/* Accessor functions */
@@ -123,7 +135,7 @@ static void serialice_read_msr(void)
sio_putc('\r'); sio_putc('\n');
- msr = rdmsr(addr, key); + msr = _rdmsr(addr, key); sio_put32(msr.hi); sio_putc('.'); sio_put32(msr.lo); @@ -146,7 +158,7 @@ static void serialice_write_msr(void)
#ifdef __ROMCC__ /* Cheat to avoid register outage */ - wrmsr(addr, msr, 0x9c5a203a); + _wrmsr(addr, msr, 0x9c5a203a); #else wrmsr(addr, msr, key); #endif @@ -191,21 +203,22 @@ static void serialice_mainboard(void) sio_putc('\r'); sio_putc('\n');
/* must be defined in mainboard/<boardname>.c */ - sio_putstring(boardname); +// sio_putstring(boardname); }
static void serialice_version(void) { - sio_putstring("\nSerialICE v" VERSION " (" __DATE__ ")\n"); +// sio_putstring("\nSerialICE v" VERSION " (" __DATE__ ")\n"); }
-int main(void) +int serialice_main(void) { - chipset_init(); +// chipset_init();
sio_init(); + console_init(); +// serialice_version();
- serialice_version();
while(1) { u16 c; @@ -246,12 +259,14 @@ int main(void) case (('v' << 8)|'i'): // Read version info *vi serialice_version(); break; + case (('q' << 8)|'i'): // quit *qi + goto quits; + break; default: sio_putstring("ERROR\n"); break; } } - - // Never get here: +quits: return 0; }