Subject: [PATCH/RFC 4/5] s390 zlive service processor calls From: Carsten Otte <[EMAIL PROTECTED]> From: Martin Peschke <[EMAIL PROTECTED]>
This patch adds support for calls to our service processor o infrastructure that handles sclp calls o infrastructure for sclp events o sclp handlers This is not a complete set of service calls, it implements only the minimal set needed to run a Linux kernel in the virtual machine. Signed-off-by: Carsten Otte <[EMAIL PROTECTED]> --- include/zlsclp.h | 157 ++++++++++++++++++++++++++++++++ sclp/Makefile | 25 +++++ sclp/event_facility.c | 239 ++++++++++++++++++++++++++++++++++++++++++++++++++ sclp/sclp.c | 92 +++++++++++++++++++ sclp/scp_read_info.c | 46 +++++++++ sclp/signal_quiesce.c | 45 +++++++++ 6 files changed, 604 insertions(+) Index: zlive/include/zlsclp.h =================================================================== --- /dev/null +++ zlive/include/zlsclp.h @@ -0,0 +1,157 @@ +/* + * z/Live service call header file + * Copyright IBM Corp. 2007 + * Author: Carsten Otte <[EMAIL PROTECTED]> + * + * This file is licensed under the terms of the GNU General Public License(GPL) + */ + +#ifndef __ZLSCLP_H +#define __ZLSCLP_H +#include <zllist.h> +#include <zlcpu.h> +#include <stdint.h> + +#define SCLP_SCCB_ADDRESS_BITS 0x7ffffff8 + +typedef int (*sclp_handler_t)(struct zlcpu *cpu, uint32_t _sccb); + +// SCLP command codes +#define SCLP_CMD_SCP_READ_INFO 0x00020001 +#define SCLP_CMD_SCP_READ_INFO_FORCE 0x00120001 +#define SCLP_CMD_WRITE_EVENT_DATA 0x00760005 +#define SCLP_CMD_READ_EVENT_DATA 0x00770005 +#define SCLP_CMD_WRITE_EVENT_MASK 0x00780005 + +// SCLP response codes +// FIXME: xxx means: do we ever need/want to check for this condition? +#define SCLP_RC_NORMAL_COMPLETION 0x0020 +#define SCLP_RC_SCLP_EQUIPMENT_CHECK 0x0040 +#define SCLP_RC_SCCB_BOUNDARY_VIOLATION 0x0100 // xxx +#define SCLP_RC_INVALID_COMMAND 0x01f0 +#define SCLP_RC_SOME_EVENTS_SUPPRESSED 0x0220 // naming? +#define SCLP_RC_INSUFFICIENT_SCCB_LENGTH 0x0300 +#define SCLP_RC_CONTAINED_SCLP_EQUIPMENT_CHECK 0X0340 +#define SCLP_RC_TARGET_IN_IMPROPER_STATE 0x05f0 +#define SCLP_RC_INVALID_FUNCTION 0x40f0 +#define SCLP_RC_NO_EVENT_BUFFERS_STORED 0x60f0 // naming? +#define SCLP_RC_EVENT_BUFFERS_SUPPRESSED 0x62f0 // naming? +#define SCLP_RC_INVALID_SELECTION_MASK 0x70f0 +#define SCLP_RC_OUT_OF_MEMORY 0X71f0 // xxx +#define SCLP_RC_INCONSISTENT_LENGTHS 0x72f0 +#define SCLP_RC_EVENT_BUFFER_SYNTAX_ERROR 0x73f0 +#define SCLP_RC_INVALID_MASK_LENGTH 0x74f0 +#define SCLP_RC_INVALID_EVENT_BUFFER_NUMBER 0x76f0 // xxx + +// SCLP event types (event facilities) +#define SCLP_EVENT_STATE_CHANGE 0x08 +#define SCLP_EVENT_ASCII_CONSOLE 0x1a +#define SCLP_EVENT_SIGNAL_QUIESCE 0x1d + +// SCLP event masks (event facilities) +#define SCLP_EVENT_MASK_STATE_CHANGE 0x01000000 +#define SCLP_EVENT_MASK_ASCII_CONSOLE 0x00000040 +#define SCLP_EVENT_MASK_SIGNAL_QUIESCE 0x00000008 + +#define SCLP_UNCONDITIONAL_READ 0x00 +#define SCLP_SELECTIVE_READ 0x01 + +#define SCLP_VARIABLE_LENGTH_RESPONSE 0x80 + +#define SCCB_SIZE 4096 + +#define SCLP_EVENT_DATA_PENDING_INDICATOR 0x00000001 + +// Service Call Control Block (SCCB) and its elements +struct sccb_header { + uint16_t length; + uint8_t function_code; + uint8_t control_mask[3]; + uint16_t response_code; +} __attribute__((packed)); + +struct sccb_scp_read_info { + struct sccb_header header; + uint16_t mem_code; + uint8_t increment; +} __attribute__((packed)); + +typedef uint32_t sccb_control_mask_t; + +struct sccb_write_event_mask { + struct sccb_header header; + uint16_t _reserved; + uint16_t mask_length; + sccb_control_mask_t cp_receive_mask; + sccb_control_mask_t cp_send_mask; + sccb_control_mask_t sclp_send_mask; + sccb_control_mask_t sclp_receive_mask; +} __attribute__((packed)); + +#define SCCB_UNCONDITIONAL_READ_BUFSIZE \ + (SCCB_SIZE - sizeof(struct sccb_header)) +#define SCCB_SELECTIVE_READ_BUFSIZE \ + (SCCB_SIZE - (sizeof(struct sccb_header) + sizeof(sccb_control_mask_t))) + +struct sccb_read_event_data{ + struct sccb_header header; + union { + struct { + char buf[SCCB_UNCONDITIONAL_READ_BUFSIZE]; + } unconditional; + struct { + sccb_control_mask_t mask; + char buf[SCCB_SELECTIVE_READ_BUFSIZE]; + } selective; + } function; +} __attribute__((packed)); + +struct sclp_call_register { + struct zllist head; + uint64_t sclp_code; + sclp_handler_t handler; +}; + +void sclp_register (struct sclp_call_register *scr); +void sclp_interrupt (uint32_t extint_param); + +// starting at byte 8 of SCCB +struct event_buffer_header { + uint16_t length; + uint8_t type; +#define SCLP_EVENT_BUFFER_ACCEPTED 0x80 + uint8_t flags; + uint16_t _reserved; +} __attribute__((packed)); + +struct sclp_event_types { + struct zllist head; + sccb_control_mask_t send_mask; + sccb_control_mask_t receive_mask; + uint16_t (*send) (void *__event, uint16_t length); + uint16_t (*receive) (struct event_buffer_header *event); +}; + +void sclp_register_event(struct sclp_event_types *types); +int sclp_send_event(struct zlcpu *cpu, struct event_buffer_header *event); + +struct event_buffer_ascii_console_read { + struct event_buffer_header header; +#define SCLP_ASCII_CONSOLE_DATA 0x00 +#define SCLP_ASCII_CONSOLE_END 0x01 +#define SCLP_ASCII_CONSOLE_START 0x80 + uint8_t type; + char data[0]; +} __attribute__((packed)); + +struct event_buffer_signal_quiesce { + struct event_buffer_header header; + uint16_t timeout; +#define SCLP_SIGNAL_QUIESCE_SECONDS 0 +#define SCLP_SIGNAL_QUIESCE_MINUTES 1 +#define SCLP_SIGNAL_QUIESCE_HOURS 2 + uint8_t unit; +} __attribute__((packed)); + +void init_sclp(void); +#endif Index: zlive/sclp/Makefile =================================================================== --- /dev/null +++ zlive/sclp/Makefile @@ -0,0 +1,25 @@ +# z/Live sclp calls makefile +# Copyright IBM Corp. 2007 +# Author: Carsten Otte <[EMAIL PROTECTED]> +# This file is licensed under the terms of the GNU General Public License(GPL) + +%.o : %.c ; echo " Compiling " $<; $(CC) $(CFLAGS) -c $< + +OBJS := sclp.o sclp_autoinit.o scp_read_info.o event_facility.o signal_quiesce.o + +all: $(OBJS) +clean: + rm -f $(OBJS) sclp_autoinit.c + +sclp_autoinit.c: scp_read_info.c event_facility.c signal_quiesce.c + echo " Creating " $@ + echo "//Makefile generated" >$@ + echo "#define __ZLAUTOINIT(f) void f(void);" >>$@ + cat $^ |grep __ZLAUTOINIT >>$@ + echo "#undef __ZLAUTOINIT" >>$@ + echo "#define __ZLAUTOINIT(f) { f(); };" >>$@ + echo "void autoinit_sclp(void) {" >>$@ + cat $^ |grep __ZLAUTOINIT >>$@ + echo "}" >>$@ + +.PHONY: clean all Index: zlive/sclp/event_facility.c =================================================================== --- /dev/null +++ zlive/sclp/event_facility.c @@ -0,0 +1,239 @@ +/* + * z/Live sclp event facility + * Copyright IBM Corp. 2007 + * Author: Martin Peschke <[EMAIL PROTECTED]> + * + * This file is licensed under the terms of the GNU General Public License(GPL) + */ + +#include <string.h> + +#include <zlintercept.h> +#include <zlglobals.h> +#include <zlautoinit.h> +#include <zlsclp.h> +#include <zlcpu.h> +#include <zlcpu_lib.h> + +// SCLP is capable of providing event buffers of these types to program +sccb_control_mask_t sclp_send_mask; +// SCLP accepts event buffers of these types from program +sccb_control_mask_t sclp_receive_mask; +// program is capable of providing event buffers of these types to SCLP +sccb_control_mask_t sclp_cp_send_mask; +// program accepts event buffers of these types from SCLP +sccb_control_mask_t sclp_cp_receive_mask; +// active_selection_mask ?? +sccb_control_mask_t sclp_active_selection_mask; + +struct zllist sclp_event_types_list; + +void sclp_register_event(struct sclp_event_types *types) +{ + zllist_add((struct zllist *)types, &sclp_event_types_list); + sclp_send_mask |= types->send_mask; + sclp_receive_mask |= types->receive_mask; + debug("sclp: registered event types (send 0x%x, receive 0x%x)", types->send_mask, types->receive_mask); + /* FIXME: requires state change event??? */ + return; +} + +static int sclp_write_event(struct event_buffer_header *event) +{ + struct zllist *entry; + struct sclp_event_types *types; + sccb_control_mask_t mask; + + debug("got event, buffer 0x%p length 0x%d", event, event->length); + //FIXME: check event buffer + if (event->length < sizeof(struct event_buffer_header)) + return SCLP_RC_EVENT_BUFFER_SYNTAX_ERROR; + + mask = 0x80000000 >> (event->type - 1); + if (!(sclp_receive_mask & mask)) { + log("sclp: skipping event disabled in sclp receive mask " + "(event mask 0x%x, receive mask 0x%x)", + mask, sclp_receive_mask); + return SCLP_RC_INVALID_FUNCTION; + } + + for (entry = sclp_event_types_list.next; + entry != &sclp_event_types_list; + entry = entry->next) { + types = (struct sclp_event_types *)entry; + if (types->receive_mask & mask) { + log("sclp: receiving event (event mask 0x%x)", mask); + return types->receive(event); + } + } + return SCLP_RC_INVALID_FUNCTION; /* keeping compiler happy, + case covered by check above */ +} + +static int sclp_write_event_data(struct zlcpu *cpu, uint32_t _sccb) +{ + struct sccb_header *sccb = (void *)(_sccb + glo_origin); + struct event_buffer_header *event; + signed int size; + + /* just checking */ + for (size = sccb->length - sizeof(struct sccb_header), + event = (void *)sccb + sizeof(struct sccb_header); + size > 0 && size >= event->length; + size -= event->length, event = (void *)event + event->length); + if (size < 0) + sccb->response_code = SCLP_RC_INCONSISTENT_LENGTHS; + + for (size = sccb->length - sizeof(struct sccb_header), + event = (void *)sccb + sizeof(struct sccb_header); + size > 0 && size >= event->length; + size -= event->length, event = (void *)event + event->length) { + sccb->response_code = sclp_write_event(event); + if (!(event->flags & SCLP_EVENT_BUFFER_ACCEPTED)) + break; + } + debug("response code 0x%02x", sccb->response_code); + setcc(cpu, 0); + sclp_interrupt(_sccb); + return may_do_cpu_work(cpu); +} + +static int sclp_read_event_data(struct zlcpu *cpu, uint32_t _sccb) +{ + struct sccb_read_event_data *sccb = (void *)(_sccb + glo_origin); + struct zllist *entry; + struct sclp_event_types *types; + struct event_buffer_header *event; + signed int retval; + uint16_t space; + + if (sccb->header.length != SCCB_SIZE) { + sccb->header.response_code = SCLP_RC_INSUFFICIENT_SCCB_LENGTH; + goto out; + } + + switch (sccb->header.function_code) { + case SCLP_UNCONDITIONAL_READ: + sclp_active_selection_mask = sclp_cp_receive_mask; + event = (struct event_buffer_header *)sccb->function.unconditional.buf; + space = SCCB_UNCONDITIONAL_READ_BUFSIZE; + break; + case SCLP_SELECTIVE_READ: + if (!(sclp_cp_receive_mask & sccb->function.selective.mask)) { + sccb->header.response_code = SCLP_RC_INVALID_SELECTION_MASK; + goto out; + } + sclp_active_selection_mask = sccb->function.selective.mask; + event = (struct event_buffer_header *)sccb->function.selective.buf; + space = SCCB_SELECTIVE_READ_BUFSIZE; + break; + default: + sccb->header.response_code = SCLP_RC_INVALID_FUNCTION; + goto out; + } + + sccb->header.response_code = SCLP_RC_NO_EVENT_BUFFERS_STORED; + + for (entry = sclp_event_types_list.next; + entry != &sclp_event_types_list; + entry = entry->next) { + types = (struct sclp_event_types *)entry; + if (sccb->header.function_code == SCLP_SELECTIVE_READ && + // requires a single event bit set per type + types->send_mask & sclp_active_selection_mask) + continue; + retval = types->send(event, space); + if (retval == SCLP_RC_NORMAL_COMPLETION) { + sccb->header.response_code = SCLP_RC_NORMAL_COMPLETION; + // FIXME: assumption: not more than a single buffer stored + event = (void *)event + event->length; + space -= event->length; + // FIXME: indicating more buffers though this is just an assumption + if (!space) { + _sccb |= SCLP_EVENT_DATA_PENDING_INDICATOR; + break; + } + } else if (retval == SCLP_RC_NO_EVENT_BUFFERS_STORED) { + log("no buffers available (mask 0x%x)", types->send_mask); + } else { + + } + } + + // FIXME: suppressed buffers + + if (sccb->header.control_mask[2] & SCLP_VARIABLE_LENGTH_RESPONSE) { + sccb->header.control_mask[2] &= ~SCLP_VARIABLE_LENGTH_RESPONSE; + sccb->header.length = ((char *)event + event->length) - (char *)sccb; + } +out: + setcc(cpu, 0); + sclp_interrupt(_sccb); + return may_do_cpu_work(cpu); +} + +static int sclp_write_event_mask(struct zlcpu *cpu, uint32_t _sccb) +{ + struct sccb_write_event_mask *sccb = (void *)(_sccb + glo_origin); + + /* Attention: We assume that Linux uses 4-byte masks, what it actually + does. Architecture allows for masks of variable size, though */ + if (sccb->mask_length != sizeof(sccb_control_mask_t)) { + sccb->header.response_code = SCLP_RC_INVALID_MASK_LENGTH; + goto out; + } + + /* keep track of the guest's capability masks */ + sclp_cp_send_mask = sccb->cp_send_mask; + sclp_cp_receive_mask = sccb->cp_receive_mask; + /* CP receive mask becomes active selection mask */ + sclp_active_selection_mask = sclp_cp_receive_mask; + /* return the SCLP's capability masks to the guest */ + sccb->sclp_send_mask = sclp_send_mask; + sccb->sclp_receive_mask = sclp_receive_mask; + sccb->header.response_code = SCLP_RC_NORMAL_COMPLETION; + + debug("sclp, write event mask: control-program send mask 0x%08x", + sclp_cp_send_mask); + debug("sclp, write event mask: control-program receive mask 0x%08x", + sclp_cp_receive_mask); + debug("sclp, write event mask: sclp send mask 0x%08x", + sclp_send_mask); + debug("sclp, write event mask: sclp receive mask 0x%08x", + sclp_receive_mask); + +out: + debug("sclp, write event mask: response code 0x%02x", + sccb->header.response_code); + + setcc(cpu, 0); + sclp_interrupt(_sccb); + + return may_do_cpu_work(cpu); +} + +static struct sclp_call_register sclp_write_event_data_register = { + .sclp_code = SCLP_CMD_WRITE_EVENT_DATA, + .handler = sclp_write_event_data +}; + +static struct sclp_call_register sclp_read_event_data_register = { + .sclp_code = SCLP_CMD_READ_EVENT_DATA, + .handler = sclp_read_event_data +}; + +static struct sclp_call_register sclp_write_event_mask_register = { + .sclp_code = SCLP_CMD_WRITE_EVENT_MASK, + .handler = sclp_write_event_mask +}; + +void sclp_event_facility_init(void) +{ + zllist_init(&sclp_event_types_list); + + sclp_register(&sclp_write_event_mask_register); + sclp_register(&sclp_write_event_data_register); + sclp_register(&sclp_read_event_data_register); +} + +__ZLAUTOINIT(sclp_event_facility_init) Index: zlive/sclp/sclp.c =================================================================== --- /dev/null +++ zlive/sclp/sclp.c @@ -0,0 +1,92 @@ +/* + * z/Live service call instruction interceptions + * Copyright IBM Corp. 2007 + * Author: Carsten Otte <[EMAIL PROTECTED]> + * + * This file is licensed under the terms of the GNU General Public License(GPL) + */ + +#include <zlautoinit.h> +#include <zlmessage.h> +#include <zlintercept.h> +#include <zlsclp.h> +#include <zlpriv.h> +#include <zlcpu_lib.h> +#include <zlinterrupt.h> + +static struct zllist sclp_handlers; + +int sclp_service_call(struct zlcpu *cpu) +{ + uint64_t sccb = cpu->gpr->regs[cl_get_ipbh0(cpu) & 0xf]; + uint64_t code = cpu->gpr->regs[(cl_get_ipbh0(cpu) & 0xf0) >> 4]; + struct zllist *entry; + struct sclp_call_register *scr; + + // FIXME: check_region(cpu, addr, addr + length - 1); + check_addr(cpu, sccb); + if (sccb & ~SCLP_SCCB_ADDRESS_BITS) { + log("cpu %d: invalid sccb address 0x%lx", cpu->cpuno, sccb); + goto out; + } + for (entry = sclp_handlers.next; entry != &sclp_handlers; + entry = entry->next) { + scr = (struct sclp_call_register *) entry; + if (scr->sclp_code == code) { + debug("cpu %d: sclp service call 0x%lx, sccb 0x%lx, " + "addr 0x%lx", + cpu->cpuno, code, sccb, cl_get_psw(cpu).addr); + return scr->handler(cpu, sccb); + } + } + log("cpu %d: unknown sclp service call 0x%lx, sccb 0x%lx, addr 0x%lx", + cpu->cpuno, code, sccb, cl_get_psw(cpu).addr); +out: + setcc(cpu, 3); + return may_do_cpu_work(cpu); +} + +static int _sclp_interrupt(struct zlcpu *cpu, void *__param, int cleanup) +{ + struct lowcore *lowcore; + uint32_t param = (unsigned long)__param; + + if (cleanup) { + log ("cpu %d: dropping sclp interrupt, sccb 0x%x", + cpu->cpuno, param); + return 0; + } + if (!(cl_get_gcr(cpu, 0) && 0x00000200) || ext_int_masked(cpu)) + return -EAGAIN; + lowcore = (struct lowcore *) (glo_origin + cl_get_prefix(cpu)); + lowcore->ext_params = param; + return enter_extint(cpu, EXTINT_SERVICE_SIGNAL); +} + +/* delivers asynchronous service-signal interruption for finished sccb */ +void sclp_interrupt(uint32_t extint_param) +{ + struct zlcpu *cpu = get_interrupt_cpu(); + + if (!cpu) + report_it("cannot get interrupt cpu, loosing interrupt"); + debug("extint_param=0x%x", extint_param); + add_cpu_work_kick(cpu, _sclp_interrupt, (void *)(unsigned long)extint_param); + put_cpu(cpu); +} + +/* registers a handler for a particular SCLP command code */ +void sclp_register(struct sclp_call_register *scr) +{ + zllist_add ((struct zllist *)scr, &sclp_handlers); + debug("added sclp handler for commmand 0x%04lx", scr->sclp_code) +} + +void init_sclp(void) +{ + // initialize sclp_handlers list + sclp_handlers.next = &sclp_handlers; + sclp_handlers.prev = &sclp_handlers; + autoinit_sclp(); // see makefile magic + register_privileged_handler(PRIV_SCLP_CALL, sclp_service_call); +} Index: zlive/sclp/scp_read_info.c =================================================================== --- /dev/null +++ zlive/sclp/scp_read_info.c @@ -0,0 +1,46 @@ +/* + * z/Live scp read info sclp call + * Copyright IBM Corp. 2007 + * Author: Carsten Otte <[EMAIL PROTECTED]> + * + * This file is licensed under the terms of the GNU General Public License(GPL) + */ + +#include <string.h> + +#include <zlintercept.h> +#include <zlglobals.h> +#include <zlautoinit.h> +#include <zlsclp.h> +#include <zlcpu.h> +#include <zlcpu_lib.h> + +static int scp_read_info(struct zlcpu *cpu, uint32_t _sccb) +{ + struct sccb_scp_read_info *sccb = (void *)(_sccb + glo_origin); + + sccb->mem_code = glo_memsize>>20; + sccb->increment = 1; + sccb->header.response_code = 0x10; + setcc(cpu, 3); + return may_do_cpu_work(cpu); +} + +static struct sclp_call_register read_scp_info_register = { + .sclp_code = SCLP_CMD_SCP_READ_INFO, + .handler = scp_read_info +}; + +static struct sclp_call_register read_scp_info_force_register = { + .sclp_code = SCLP_CMD_SCP_READ_INFO_FORCE, + .handler = scp_read_info +}; + + +void read_scp_info_init(void) +{ + sclp_register (&read_scp_info_register); + sclp_register (&read_scp_info_force_register); +} + +__ZLAUTOINIT(read_scp_info_init) Index: zlive/sclp/signal_quiesce.c =================================================================== --- /dev/null +++ zlive/sclp/signal_quiesce.c @@ -0,0 +1,45 @@ +/* + * z/Live console device driver + * Copyright IBM Corp. 2007 + * Author: Martin Peschke <[EMAIL PROTECTED]> + * + * This file is licensed under the terms of the GNU General Public License(GPL) + */ + +#include <unistd.h> +#include <signal.h> +#include <linux/errno.h> + +#include <zlautoinit.h> +#include <zlmessage.h> +#include <zlio_signal.h> +#include <zlcpu.h> +#include <zlcpu_lib.h> +#include <zlsclp.h> + +static uint16_t signal_quiesce_send(void *__event, uint16_t length) +{ + struct event_buffer_signal_quiesce *event = __event; + + if (length < sizeof(struct event_buffer_signal_quiesce)) + return SCLP_RC_SOME_EVENTS_SUPPRESSED; + + event->header.length = sizeof(struct event_buffer_signal_quiesce); + event->header.type = SCLP_EVENT_SIGNAL_QUIESCE; + event->header.flags &= ~SCLP_EVENT_BUFFER_ACCEPTED; + // FIXME: timeout (curently ignored by Linux, anyway) + + return SCLP_RC_NORMAL_COMPLETION; +} + +static struct sclp_event_types signal_quiesce = { + .send_mask = SCLP_EVENT_MASK_SIGNAL_QUIESCE, + .send = signal_quiesce_send, +}; + +void signal_quiesce_init(void) +{ + sclp_register_event(&signal_quiesce); +} + +__ZLAUTOINIT(signal_quiesce_init) ------------------------------------------------------------------------- This SF.net email is sponsored by DB2 Express Download DB2 Express C - the FREE version of DB2 express and take control of your XML. No limits. Just data. Click to get it now. http://sourceforge.net/powerbar/db2/ _______________________________________________ kvm-devel mailing list kvm-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/kvm-devel